Build Phast v0 home and widget editor shell
This commit is contained in:
1 parent
a84b96f1dc
commit
137e89d384
1 file changed
+13
-160
+13
-160
@@ -1,160 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta name="theme-color" content="#f5f3ee">
|
||||
<meta name="description" content="Compose something useful, then make it real.">
|
||||
<title>my phast</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Manrope:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root{--paper:#f5f3ee;--ink:#121211;--muted:#77746c;--line:rgba(18,18,17,.15);--signal:#ff4d00;--soft:#ebe8e1;--pad:clamp(22px,3vw,48px)}
|
||||
*{box-sizing:border-box}html,body{min-height:100%}body{margin:0;background:var(--paper);color:var(--ink);font-family:"Manrope",system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;-webkit-font-smoothing:antialiased}
|
||||
button,textarea,input{font:inherit}button{cursor:pointer}a{color:inherit;text-decoration:none}
|
||||
.shell{min-height:100svh;padding:var(--pad);display:grid;grid-template-rows:auto 1fr auto;gap:40px}
|
||||
.masthead,.footer{display:flex;align-items:center;justify-content:space-between;gap:24px}
|
||||
.brand{font-size:clamp(27px,2.2vw,38px);font-weight:700;letter-spacing:-.075em;line-height:1}.brand-dot{color:var(--signal)}
|
||||
.wordmark{font-family:"DM Mono",ui-monospace,monospace;font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);text-align:right}.wordmark strong{display:block;color:var(--ink);font-weight:500;margin-top:3px}
|
||||
.canvas{align-self:center;width:min(1080px,100%);margin:0 auto;display:grid;grid-template-columns:minmax(0,1.05fr) minmax(330px,.95fr);gap:clamp(36px,6vw,88px);align-items:start}
|
||||
.eyebrow,.kicker,.footer,.micro{font-family:"DM Mono",ui-monospace,monospace;text-transform:uppercase;letter-spacing:.07em}
|
||||
.eyebrow{margin:0 0 14px;font-size:11px;color:var(--muted)}h1{margin:0;font-size:clamp(58px,8vw,112px);line-height:.84;letter-spacing:-.078em;font-weight:500;max-width:7ch}
|
||||
.lede{margin:22px 0 0;max-width:570px;color:var(--muted);font-size:15px;line-height:1.65}
|
||||
.composer{margin-top:38px;border-top:1px solid var(--ink);padding-top:18px}
|
||||
.kicker{font-size:10px;color:var(--muted);margin:0 0 10px}
|
||||
.prompt{width:100%;min-height:128px;resize:vertical;border:1px solid var(--line);background:transparent;padding:16px;color:var(--ink);font-size:17px;line-height:1.5;outline:none;transition:border-color .15s ease}
|
||||
.prompt:focus{border-color:var(--ink)}.prompt::placeholder{color:#99958d}
|
||||
.actions{display:flex;align-items:center;justify-content:space-between;gap:14px;margin-top:12px}.micro{font-size:9px;color:var(--muted)}
|
||||
.primary{border:1px solid var(--ink);background:var(--ink);color:var(--paper);padding:12px 16px;font-size:13px;font-weight:650;transition:transform .15s ease,background .15s ease,border-color .15s ease}
|
||||
.primary:hover{background:var(--signal);border-color:var(--signal);transform:translateY(-2px)}
|
||||
.recipe{border-top:1px solid var(--ink)}.recipe-head{padding:18px 0 16px;display:flex;justify-content:space-between;align-items:end;gap:20px}.recipe-head h2{margin:2px 0 0;font-size:27px;letter-spacing:-.045em}.state{font-family:"DM Mono",ui-monospace,monospace;font-size:9px;text-transform:uppercase;letter-spacing:.06em;color:var(--signal)}
|
||||
.rows{border-bottom:1px solid var(--ink)}.row{display:grid;grid-template-columns:110px 1fr;gap:18px;padding:14px 0;border-top:1px solid var(--line)}.row:first-child{border-top:0}.label{font-family:"DM Mono",ui-monospace,monospace;font-size:9px;text-transform:uppercase;letter-spacing:.07em;color:var(--muted)}.value{font-size:13px;line-height:1.45}
|
||||
.chips{display:flex;flex-wrap:wrap;gap:7px}.chip{border:1px solid var(--line);background:transparent;padding:7px 9px;font-family:"DM Mono",ui-monospace,monospace;font-size:9px;color:var(--muted)}button.chip:hover,button.chip.active{border-color:var(--signal);color:var(--ink);background:rgba(255,77,0,.055)}
|
||||
.recipe-actions{display:flex;flex-wrap:wrap;gap:8px;padding-top:14px}.secondary{border:1px solid var(--line);background:transparent;padding:10px 12px;font-size:11px}.secondary:hover{border-color:var(--ink)}
|
||||
.note{margin:14px 0 0;color:var(--muted);font-size:11px;line-height:1.55}.note strong{color:var(--ink)}
|
||||
.footer{font-size:9px;color:var(--muted)}.footer span:last-child{color:var(--ink)}
|
||||
.toast{position:fixed;left:50%;bottom:28px;transform:translate(-50%,10px);opacity:0;pointer-events:none;background:var(--ink);color:var(--paper);padding:9px 12px;font-family:"DM Mono",ui-monospace,monospace;font-size:10px;text-transform:uppercase;letter-spacing:.06em;transition:.15s ease}.toast.show{opacity:1;transform:translate(-50%,0)}
|
||||
@media(max-width:850px){.canvas{grid-template-columns:1fr;gap:54px}h1{max-width:8ch}.recipe{margin-top:4px}}
|
||||
@media(max-width:520px){.wordmark span{display:none}.actions{align-items:flex-end}.row{grid-template-columns:82px 1fr}.footer{align-items:flex-end;text-align:right}}
|
||||
@media(prefers-reduced-motion:reduce){*{transition:none!important}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="shell">
|
||||
<header class="masthead">
|
||||
<a class="brand" href="https://phast.app" aria-label="Phast home">phast<span class="brand-dot">.</span></a>
|
||||
<div class="wordmark"><span>personal composer</span><strong>my.phast.app</strong></div>
|
||||
</header>
|
||||
|
||||
<section class="canvas">
|
||||
<div>
|
||||
<p class="eyebrow">connect → compose → launch</p>
|
||||
<h1>Make the idea executable.</h1>
|
||||
<p class="lede">Describe what you want. Phast turns intent into a small app recipe: intelligence, capabilities, runtime, and surfaces. Oracle can discover what exists; Crystal can find a route; Factory can do the heavy building. Phast keeps the composition.</p>
|
||||
|
||||
<div class="composer">
|
||||
<p class="kicker">What do you want to make?</p>
|
||||
<textarea class="prompt" id="prompt" placeholder="A tiny app that watches a mailbox, extracts receipts, and gives me a weekly total."></textarea>
|
||||
<div class="actions">
|
||||
<span class="micro">draft first · authority later</span>
|
||||
<button class="primary" id="compose">Compose draft ↗</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="recipe" aria-live="polite">
|
||||
<div class="recipe-head">
|
||||
<div><p class="kicker">Draft composition</p><h2 id="recipeName">blank canvas</h2></div>
|
||||
<span class="state" id="state">uncomposed</span>
|
||||
</div>
|
||||
<div class="rows">
|
||||
<div class="row"><div class="label">intent</div><div class="value" id="intent">Describe something useful on the left.</div></div>
|
||||
<div class="row"><div class="label">intelligence</div><div class="value"><div class="chips" id="intelligence">
|
||||
<button class="chip active" data-kind="intelligence" data-value="connected agent">connected agent</button>
|
||||
<button class="chip" data-kind="intelligence" data-value="Codex">Codex</button>
|
||||
<button class="chip" data-kind="intelligence" data-value="ChatGPT">ChatGPT</button>
|
||||
<button class="chip" data-kind="intelligence" data-value="other MCP client">other MCP</button>
|
||||
</div></div></div>
|
||||
<div class="row"><div class="label">capabilities</div><div class="value"><div class="chips" id="capabilities">
|
||||
<button class="chip active" data-kind="capability" data-value="discover via Oracle">discover via Oracle</button>
|
||||
<button class="chip" data-kind="capability" data-value="GitHub">GitHub</button>
|
||||
<button class="chip" data-kind="capability" data-value="Gmail">Gmail</button>
|
||||
<button class="chip" data-kind="capability" data-value="Cloudflare">Cloudflare</button>
|
||||
</div></div></div>
|
||||
<div class="row"><div class="label">shape</div><div class="value">Python + Prefab + MCP · human <code>/</code> · agent <code>/mcp</code></div></div>
|
||||
<div class="row"><div class="label">route</div><div class="value">Oracle indexes → Crystal routes → Factory builds → <strong>Phast composes</strong></div></div>
|
||||
</div>
|
||||
<div class="recipe-actions">
|
||||
<button class="secondary" id="copy">Copy recipe</button>
|
||||
<a class="secondary" href="https://xn--sj8h.teahub.xyz/oracle/">Explore Oracle</a>
|
||||
</div>
|
||||
<p class="note"><strong>This is intentionally a composer, not a fake deployment button.</strong> A later iteration can make “launch” real once the authority + runtime path is worth keeping.</p>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<footer class="footer"><span>idea → composition → app</span><span>small enough to remake</span></footer>
|
||||
</main>
|
||||
<div class="toast" id="toast">recipe copied</div>
|
||||
<script>
|
||||
const promptEl=document.querySelector('#prompt');
|
||||
const nameEl=document.querySelector('#recipeName');
|
||||
const intentEl=document.querySelector('#intent');
|
||||
const stateEl=document.querySelector('#state');
|
||||
const toast=document.querySelector('#toast');
|
||||
let intelligence='connected agent';
|
||||
let capabilities=new Set(['discover via Oracle']);
|
||||
|
||||
function slugWords(text){
|
||||
const stop=new Set(['a','an','the','that','this','to','for','and','or','of','my','me','with','from','into','app','tiny']);
|
||||
const words=(text.toLowerCase().match(/[a-z0-9]+/g)||[]).filter(x=>!stop.has(x)).slice(0,3);
|
||||
return words.length?words.join(' '):'new composition';
|
||||
}
|
||||
function compose(){
|
||||
const text=promptEl.value.trim();
|
||||
if(!text){promptEl.focus(); return;}
|
||||
nameEl.textContent=slugWords(text);
|
||||
intentEl.textContent=text;
|
||||
stateEl.textContent='draft';
|
||||
}
|
||||
function recipe(){
|
||||
const text=promptEl.value.trim()||'unwritten intent';
|
||||
return {
|
||||
kind:'phast.app.draft',
|
||||
name:nameEl.textContent,
|
||||
intent:text,
|
||||
intelligence,
|
||||
capabilities:[...capabilities],
|
||||
composition:{
|
||||
runtime:'python',
|
||||
human:'/',
|
||||
agent:'/mcp',
|
||||
ui:'prefab',
|
||||
discovery:'oracle',
|
||||
routing:'crystal',
|
||||
build:'factory'
|
||||
},
|
||||
authority:'not granted',
|
||||
status:'draft'
|
||||
};
|
||||
}
|
||||
document.querySelector('#compose').addEventListener('click',compose);
|
||||
promptEl.addEventListener('keydown',e=>{if((e.metaKey||e.ctrlKey)&&e.key==='Enter')compose()});
|
||||
document.querySelectorAll('[data-kind="intelligence"]').forEach(btn=>btn.addEventListener('click',()=>{
|
||||
document.querySelectorAll('[data-kind="intelligence"]').forEach(x=>x.classList.remove('active'));
|
||||
btn.classList.add('active'); intelligence=btn.dataset.value;
|
||||
}));
|
||||
document.querySelectorAll('[data-kind="capability"]').forEach(btn=>btn.addEventListener('click',()=>{
|
||||
const v=btn.dataset.value;
|
||||
if(capabilities.has(v)){capabilities.delete(v);btn.classList.remove('active')}
|
||||
else{capabilities.add(v);btn.classList.add('active')}
|
||||
}));
|
||||
document.querySelector('#copy').addEventListener('click',async()=>{
|
||||
const text=JSON.stringify(recipe(),null,2);
|
||||
try{await navigator.clipboard.writeText(text);toast.textContent='recipe copied'}
|
||||
catch{toast.textContent='copy unavailable'}
|
||||
toast.classList.add('show');setTimeout(()=>toast.classList.remove('show'),1200);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>My Phast</title><style>
|
||||
:root{--b:#f5f3ee;--i:#121211;--m:#77746c;--l:#d5d1c8;--o:#ff4d00}*{box-sizing:border-box}body{margin:0;background:var(--b);color:var(--i);font:14px system-ui,sans-serif}button,input{font:inherit}button{cursor:pointer}main{max-width:1120px;margin:auto;padding:30px}.top,.bar{display:flex;justify-content:space-between;align-items:center;gap:16px}.brand{font-size:30px;font-weight:800;letter-spacing:-2px}.brand b,.tag{color:var(--o)}.mcp,.tag{font:10px ui-monospace,monospace}.mcp{border:0;border-bottom:1px solid var(--l);background:0;color:var(--m)}.hero{margin:75px 0 48px;display:grid;grid-template-columns:1fr 1fr;gap:50px;align-items:end}.hero h1{font-size:clamp(54px,8vw,96px);line-height:.85;letter-spacing:-7px;margin:0}.hero p{color:var(--m);line-height:1.6;max-width:430px}.hero strong{color:var(--i)}.bar{border-top:1px solid;padding:14px 0}.bar h2{font-size:16px}.btn{border:1px solid var(--l);background:transparent;padding:9px 12px}.primary{background:var(--i);color:var(--b);border-color:var(--i)}.primary:hover{background:var(--o);border-color:var(--o)}.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.widget{min-height:180px;border:1px solid var(--l);padding:18px;background:#faf9f5;display:flex;flex-direction:column;justify-content:space-between;position:relative}.wide{grid-column:span 2}.widget h3{font-size:24px;letter-spacing:-1px;margin:8px 0}.widget p{color:var(--m);font-size:12px;line-height:1.5}.empty{border-style:dashed;background:transparent;text-align:center;justify-content:center}.edit .widget:not(.empty){outline:1px dashed var(--o);outline-offset:-5px}.edit .widget:not(.empty):after{content:"⋮";position:absolute;right:12px;top:8px;color:var(--o)}.drawer{position:fixed;right:0;top:0;height:100%;width:min(450px,100%);background:#faf9f5;border-left:1px solid var(--l);padding:25px;transform:translateX(100%);transition:.2s;z-index:2}.drawer.open{transform:none}.search{width:100%;padding:12px;border:1px solid var(--l);background:0}.provider{display:flex;justify-content:space-between;gap:16px;padding:16px 0;border-top:1px solid var(--l)}.provider small{display:block;color:var(--m);margin-top:5px}.foot{margin-top:50px;border-top:1px solid var(--l);padding-top:14px;display:flex;justify-content:space-between;color:var(--m);font:10px ui-monospace,monospace}@media(max-width:700px){.hero{grid-template-columns:1fr}.grid{grid-template-columns:1fr}.wide{grid-column:auto}}
|
||||
</style></head><body><main id="home"><header class="top"><div class="brand">phast<b>.</b></div><button class="mcp" id="copy">my.phast.app/mcp ↗</button></header>
|
||||
<section class="hero"><h1>Your tool belt.</h1><p><strong>Connect your tools once. Bring them anywhere.</strong><br>One MCP for ChatGPT, Claude, Codex, or whatever comes next. Your home is the human side of the same composition.</p></section>
|
||||
<div class="bar"><h2>My Home</h2><div><button class="btn" id="edit">Edit Home</button> <button class="btn primary add">+ Add Widget</button></div></div>
|
||||
<section class="grid" id="grid"><article class="widget wide"><div><span class="tag">PHAST · TOOL BELT</span><h3>Ready to connect</h3><p>Add a provider and its tools become available through this Phast MCP. Credentials stay delegated to Persimmons.</p></div><span class="tag">0 CONNECTED PROVIDERS</span></article><article class="widget"><div><span class="tag">ORACLE</span><h3>Provider search</h3><p>Discover MCP providers, OAuth routes and capabilities from the machine graph.</p></div><span class="tag">DISCOVERY</span></article><button class="widget empty add"><strong>+<br>Add widget</strong><p>Provider tools + Prefab surfaces</p></button></section>
|
||||
<footer class="foot"><span>human: my.phast.app</span><span>agent: /mcp</span></footer></main>
|
||||
<aside class="drawer" id="drawer"><div class="top"><div><span class="tag">ADD WIDGET</span><h2>Find a provider</h2></div><button class="btn" id="close">Close</button></div><input class="search" id="search" placeholder="Search MCP, OAuth, tools…"><div id="providers">
|
||||
<div class="provider" data-q="oracle mcp provider search"><div><strong>Search with Oracle</strong><small>Discover providers from the capability graph.</small></div><button class="btn">Search</button></div>
|
||||
<div class="provider" data-q="tea teahub status prefab"><div><strong>Tea</strong><small>Existing Prefab status surface.</small></div><button class="btn install" data-name="Tea status">Add</button></div>
|
||||
<div class="provider" data-q="persimmons secrets connections"><div><strong>Connections</strong><small>Phast view over Persimmons-backed connections.</small></div><button class="btn install" data-name="Connections">Add</button></div></div>
|
||||
<p style="color:var(--m);font-size:11px;line-height:1.5">V0 gallery is tiny on purpose. Next: query Oracle and derive widget choices from provider tools and Prefab resources.</p></aside>
|
||||
<script>const h=document.querySelector('#home'),d=document.querySelector('#drawer');document.querySelectorAll('.add').forEach(x=>x.onclick=()=>d.classList.add('open'));close.onclick=()=>d.classList.remove('open');edit.onclick=e=>{h.classList.toggle('edit');e.target.textContent=h.classList.contains('edit')?'Done':'Edit Home'};copy.onclick=()=>navigator.clipboard?.writeText('https://my.phast.app/mcp');search.oninput=e=>document.querySelectorAll('.provider').forEach(x=>x.hidden=!x.dataset.q.includes(e.target.value.toLowerCase()));document.querySelectorAll('.install').forEach(b=>b.onclick=()=>{let w=document.createElement('article');w.className='widget';w.innerHTML='<div><span class="tag">PREFAB WIDGET</span><h3>'+b.dataset.name+'</h3><p>Draft widget. Chat and Home will edit the same phast.json.</p></div><span class="tag">DRAFT</span>';grid.insertBefore(w,grid.lastElementChild);d.classList.remove('open')});</script></body></html>
|
||||
Reference in new issue
Block a user