111 lines
12 KiB
HTML
111 lines
12 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Persimmons — Passwords, PATs & Plenty of P's</title>
|
||
<meta name="description" content="Useful P's for humans + agents.">
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🍊</text></svg>">
|
||
<style>
|
||
:root{--ink:#241a14;--muted:#75675e;--line:#eadfd6;--paper:#fffdfa;--soft:#fff5ea;--orange:#f47b20;--orange2:#dc6916;--green:#2f7d4a;--red:#a23b2a;--shadow:0 20px 56px rgba(83,48,23,.075)}
|
||
*{box-sizing:border-box}[hidden]{display:none!important}html,body{margin:0;min-height:100%;background:var(--paper);color:var(--ink);font-family:ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
||
body{padding:24px}main{width:min(980px,100%);margin:auto;padding:30px 0 64px}
|
||
.row,.actions,.cardtop,.shellTop,.groupHead,.choicegrid{display:flex;align-items:center}
|
||
.hero{padding:18px 0 36px;text-align:center}.heroInner{width:min(900px,100%);margin:0 auto}.hero h1{display:grid;row-gap:.045em;margin:0;font-size:clamp(40px,6vw,68px);line-height:.9;letter-spacing:-.052em}
|
||
.hero h1 span{display:block}.heroName,.heroPermissions,.heroPromise{font-size:1em;line-height:.9}.heroName,.heroPromise{font-family:ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-weight:800;letter-spacing:-.052em}.heroFruit{display:inline!important;font-size:.62em;vertical-align:.08em;margin-right:.08em}.heroPermissions{color:var(--orange);font-family:Georgia,"Times New Roman",serif;font-style:italic;font-weight:600;letter-spacing:-.035em;margin:0}.heroPromise{color:var(--ink)}
|
||
.heroFlow{margin-top:28px;font-size:clamp(40px,6vw,68px);line-height:1.2}.pLine{margin:0 0 8px;font-size:clamp(17px,2vw,21px);font-weight:730;letter-spacing:-.018em}.lede{margin:0 auto;max-width:680px;color:var(--muted);font-size:15px;line-height:1.5}
|
||
button,.buttonLink{appearance:none;border:1px solid var(--line);border-radius:11px;background:#fff;padding:9px 13px;font:inherit;font-size:13px;font-weight:730;color:var(--ink);cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;justify-content:center}button:hover,.buttonLink:hover{border-color:#d9c7ba}.primary{background:var(--orange);border-color:var(--orange);color:#fff}.primary:hover{background:var(--orange2);border-color:var(--orange2)}.ctaOutline{border-color:var(--orange);color:var(--orange);background:#fff}.ctaOutline:hover{border-color:var(--orange2);color:var(--orange2);background:var(--soft)}.danger{color:var(--red)}button:disabled{opacity:.5;cursor:default}
|
||
.vaultShell{border:1px solid var(--line);border-radius:22px;background:#fff;box-shadow:var(--shadow);overflow:hidden}.shellTop{justify-content:space-between;gap:16px;padding:18px 20px;border-bottom:1px solid var(--line);background:linear-gradient(180deg,#fff,#fffdfa)}
|
||
.shellTop h2{margin:0;font-size:20px;letter-spacing:-.02em}.hint{color:var(--muted);font-size:12px;line-height:1.5}.shellActions{display:flex;gap:8px;flex-wrap:wrap}.vaultGroup{padding:18px 20px}.vaultGroup+.vaultGroup{border-top:1px solid var(--line)}.groupHead{justify-content:space-between;gap:16px;margin-bottom:12px}.groupRule{border-top:1px solid var(--line);margin:0 -20px 16px}.groupHead strong{font-size:15px}.groupHead .hint{margin-top:3px}
|
||
.repoControls{min-width:min(360px,100%)}select,input,textarea{width:100%;border:1px solid var(--line);border-radius:10px;background:#fff;padding:10px 11px;font:inherit;color:var(--ink);outline:none}select:focus,input:focus,textarea:focus{border-color:#f0a36c;box-shadow:0 0 0 3px rgba(244,123,32,.08)}
|
||
.state{min-height:18px;margin-bottom:8px;color:var(--muted);font-size:12px}.state.good{color:var(--green)}.state.bad{color:var(--red)}.list{display:grid;gap:10px}.empty{padding:24px 14px;border:1px dashed var(--line);border-radius:13px;color:var(--muted);text-align:center;font-size:13px;line-height:1.5}.emptyCta{display:flex;align-items:center;justify-content:center;border:0;padding:18px 14px}
|
||
.connection{border:1px solid var(--line);border-radius:14px;padding:14px;background:#fff}.cardtop{justify-content:space-between;gap:14px;align-items:flex-start}.title{font:760 14px ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere}.account{margin-top:4px;color:var(--muted);font-size:12px}.badge{font-size:12px;white-space:nowrap}.badge.active{color:var(--green)}.badge.revoked{color:var(--red)}
|
||
.where{margin-top:10px;color:var(--muted);font-size:12px}.mask{margin:9px 0 10px;padding:9px 10px;border-radius:9px;background:var(--soft);font:13px ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.04em}.scope{display:inline-block;margin:3px 4px 0 0;padding:3px 7px;border:1px solid var(--line);border-radius:999px;font-size:11px}.meta{margin-top:8px;color:var(--muted);font-size:12px;line-height:1.5}.actions{flex-wrap:wrap;gap:8px;margin-top:11px}.rotatebox{display:none;margin-top:11px;padding-top:11px;border-top:1px solid var(--line)}.rotatebox.open{display:grid;grid-template-columns:1fr auto;gap:8px}
|
||
.scrim{position:fixed;inset:0;display:grid;place-items:center;padding:18px;background:rgba(36,26,20,.32);z-index:20}.scrim[hidden]{display:none}.composer{width:min(660px,100%);max-height:calc(100vh - 36px);overflow:auto;border:1px solid var(--line);border-radius:20px;background:var(--paper);box-shadow:0 30px 90px rgba(50,30,18,.24);padding:22px}.composerTop{display:flex;justify-content:space-between;gap:16px;align-items:flex-start}.composerTop h2{margin:0;font-size:22px}.close{padding:6px 10px}.step{margin-top:22px}.step[hidden]{display:none}.stepnum{color:var(--orange);font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.08em}.question{margin:6px 0 14px;font-size:18px;font-weight:760}
|
||
.choicegrid{align-items:stretch;gap:10px;flex-wrap:wrap}.choice{flex:1 1 130px;flex-direction:column;align-items:flex-start;justify-content:center;text-align:left;padding:14px}.choice strong{display:block;width:100%}.choice small{display:block;width:100%;margin-top:4px;color:var(--muted);font-weight:500;line-height:1.25}.choice.selected{border-color:var(--orange);box-shadow:0 0 0 2px rgba(244,123,32,.1)}
|
||
.field{display:grid;gap:6px;margin-bottom:12px}.field label{font-size:12px;font-weight:720;color:var(--muted)}.fieldHelp{margin-top:-2px;color:var(--muted);font-size:11px}.checks{display:flex;gap:8px;flex-wrap:wrap}.check{display:flex;gap:6px;align-items:center;border:1px solid var(--line);border-radius:999px;padding:7px 9px;font-size:12px}.check input{width:auto}.advanced{margin-top:14px;border-top:1px solid var(--line);padding-top:13px}.advanced summary{cursor:pointer;font-size:13px;font-weight:720}.advanced .inside{margin-top:12px}.composerNav{display:flex;justify-content:space-between;gap:10px;margin-top:18px}
|
||
.footerNote{font-weight:400;font-style:normal;padding:18px 12px 0;color:var(--muted);font-size:11.5px;line-height:1.5;text-align:center}.footerNote strong{display:block;color:var(--ink);margin-bottom:3px}.apiP{font-style:italic;font-weight:inherit}
|
||
@media(max-width:720px){body{padding:16px}.hero{padding-top:22px}.shellTop,.groupHead{align-items:flex-start;flex-direction:column}.shellActions{width:100%}.shellActions button{flex:1}.repoControls{width:100%;min-width:0}.choicegrid{display:grid;grid-template-columns:1fr 1fr}.rotatebox.open{grid-template-columns:1fr}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main>
|
||
<section class="hero">
|
||
<div class="heroInner">
|
||
<h1><span class="heroName">Persimmons</span><span class="heroPermissions">permissions</span><span class="heroPromise">put plainly</span></h1>
|
||
<div class="heroFlow" aria-label="Orange to water to robot">🍊 → 💧 → 🤖</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="vaultShell">
|
||
<div class="shellTop">
|
||
<div><h2>Your Persimmons</h2><div class="hint"><span id="userIdentity">Resolving signed-in user…</span></div></div>
|
||
<div class="shellActions"><button id="refreshAll">Refresh</button><button id="signOutButton" hidden>Sign out</button><button class="primary" id="addButton">+ Add</button></div>
|
||
</div>
|
||
|
||
<div class="vaultGroup">
|
||
<div class="groupHead">
|
||
<div><strong>👤 My vault</strong><div class="hint">Available to you across Teahub.</div></div>
|
||
</div>
|
||
<div class="groupRule" aria-hidden="true"></div>
|
||
<div id="userState" class="state">Loading…</div>
|
||
<div id="userList" class="list"></div>
|
||
</div>
|
||
|
||
<div class="vaultGroup">
|
||
<div class="groupHead">
|
||
<div><strong>📦 Repository vaults</strong><div class="hint">Caller-authorized repository secrets.</div></div>
|
||
<div class="repoControls"><select id="repoSelect"><option value="">Loading repositories…</option></select></div>
|
||
</div>
|
||
<div id="repoState" class="state">Discovering writable repositories…</div>
|
||
<div id="repoList" class="list"></div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
</main>
|
||
|
||
<div class="scrim" id="composerScrim" hidden>
|
||
<section class="composer" role="dialog" aria-modal="true" aria-labelledby="composerTitle">
|
||
<div class="composerTop"><div><div class="stepnum">Add a Persimmon</div><h2 id="composerTitle">Simple now, reusable later.</h2><div class="hint" id="stepSummary">What are you connecting?</div></div><button class="close" id="closeComposer">×</button></div>
|
||
|
||
<div class="step" id="step1">
|
||
<div class="question">What are you connecting?</div>
|
||
<div class="choicegrid">
|
||
<button class="choice provider" data-provider="Gitea"><strong>Gitea</strong><small>Personal access token</small></button>
|
||
<button class="choice provider" data-provider="Cloudflare"><strong>Cloudflare</strong><small>API token</small></button>
|
||
<button class="choice provider" data-provider="Fly"><strong>Fly</strong><small>Access token</small></button>
|
||
<button class="choice provider" data-provider="Other"><strong>Other</strong><small>Password, key or token</small></button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="step" id="step2" hidden>
|
||
<div class="question">Where should it be available?</div>
|
||
<div class="choicegrid">
|
||
<button class="choice vault" data-vault="user"><strong>👤 My vault</strong><small>Reusable by you across Teahub.</small></button>
|
||
<button class="choice vault" data-vault="repository"><strong>📦 A repository</strong><small>Only in that repository context.</small></button>
|
||
</div>
|
||
<div class="field" id="composerRepoField" hidden style="margin-top:12px"><label>Repository</label><select id="composerRepo"><option value="">Choose a repository…</option></select></div>
|
||
</div>
|
||
|
||
<div class="step" id="step3" hidden>
|
||
<div class="question">Name it, then add only what it needs.</div>
|
||
<div class="field"><label>Name</label><input id="connectionName" placeholder="e.g. GITEA_TOKEN" autocomplete="off"><div class="fieldHelp">This becomes the Gitea secret name and the Persimmon title.</div></div>
|
||
<div class="field"><label id="accountLabel">Account</label><input id="accountInput" placeholder=""></div>
|
||
<div class="field" id="typeField" hidden><label>Type</label><select id="typeInput"><option>Password</option><option>PAT</option><option>API key</option><option>Token</option><option>Other</option></select></div>
|
||
<div class="field"><label id="valueLabel">Value</label><input id="valueInput" type="password" autocomplete="new-password" spellcheck="false"></div>
|
||
<div class="field" id="scopeField"><label>Intended capabilities</label><div class="checks" id="scopeChoices"></div></div>
|
||
<details class="advanced"><summary>Advanced</summary><div class="inside">
|
||
<div class="field"><label>Expires</label><input id="expiresInput" placeholder="optional"></div>
|
||
<div class="field"><label>Notes</label><textarea id="notesInput" placeholder="optional"></textarea></div>
|
||
</div></details>
|
||
</div>
|
||
|
||
<div class="state" id="composerState"></div>
|
||
<div class="composerNav"><button id="backButton" hidden>Back</button><div class="actions"><button class="primary" id="nextButton">Next</button><button class="primary" id="saveButton" hidden>Save to vault</button></div></div>
|
||
</section>
|
||
</div>
|
||
|
||
<script type="module" src="./app.js"></script>
|
||
</body>
|
||
</html> |