This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · App surfaces</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height:100vh; overflow:hidden; display:flex; flex-direction:column; }
|
||||
.controls { flex:none; display:flex; align-items:center; gap:14px; padding:11px 20px; border-bottom:1px solid var(--line-soft); background:color-mix(in srgb,var(--bg) 88%,transparent); backdrop-filter:blur(10px); z-index:30; }
|
||||
.controls .lab { font-family:var(--mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); }
|
||||
.controls .vlabel { font-size:12.5px; color:var(--text-muted); } .controls .vlabel b { color:var(--text); font-weight:600; }
|
||||
.controls .right { margin-left:auto; display:flex; align-items:center; gap:10px; }
|
||||
.tbtn { width:34px; height:34px; border-radius:9px; border:1px solid var(--line); background:var(--surface); color:var(--text-2); cursor:pointer; }
|
||||
.tbtn:hover { border-color:var(--honey-line); color:var(--honey); }
|
||||
.app { flex:1; min-height:0; display:flex; }
|
||||
.rail { flex:none; width:208px; border-right:1px solid var(--line-soft); background:var(--bg-2); padding:16px 10px; overflow:auto; }
|
||||
.rail .rh { font-family:var(--mono); font-size:10px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); padding:6px 11px 12px; }
|
||||
.ritem { display:flex; align-items:center; gap:11px; padding:9px 11px; border-radius:10px; cursor:pointer; color:var(--text-2); transition:.13s; font-size:13.5px; font-weight:550; position:relative; }
|
||||
.ritem:hover { background:var(--surface-2); color:var(--text); }
|
||||
.ritem.on { background:var(--honey-wash); color:var(--text); }
|
||||
.ritem.on::before { content:''; position:absolute; left:-10px; top:50%; transform:translateY(-50%); width:3px; height:17px; border-radius:3px; background:var(--honey); }
|
||||
.ritem svg { width:17px; height:17px; stroke:currentColor; fill:none; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; flex:none; }
|
||||
.ritem.on svg { stroke:var(--honey); }
|
||||
.ritem .ct { margin-left:auto; font-family:var(--mono); font-size:10px; color:var(--text-dim); }
|
||||
.rail .rz { font-family:var(--mono); font-size:9.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); padding:16px 11px 6px; }
|
||||
|
||||
.main { flex:1; min-width:0; display:flex; flex-direction:column; }
|
||||
.shead { flex:none; display:flex; align-items:flex-start; gap:16px; padding:24px 30px 16px; border-bottom:1px solid var(--line-soft); }
|
||||
.shead h1 { font-size:21px; font-weight:650; letter-spacing:-0.02em; margin:0 0 4px; }
|
||||
.shead p { font-size:13px; color:var(--text-muted); margin:0; max-width:62ch; line-height:1.5; }
|
||||
.shead .pa { margin-left:auto; font-size:12.5px; font-weight:650; padding:9px 15px; border-radius:9px; background:var(--honey); color:#1a1407; cursor:pointer; white-space:nowrap; border:0; display:inline-flex; align-items:center; gap:7px; }
|
||||
.shead .pa:hover { background:var(--honey-bright); }
|
||||
.body { flex:1; min-height:0; overflow:auto; padding:22px 30px 50px; }
|
||||
.panel { display:none; } .panel.on { display:block; }
|
||||
|
||||
.grp { font-family:var(--mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); margin:4px 0 12px; display:flex; align-items:center; gap:10px; } .grp:not(:first-child){ margin-top:26px; } .grp::after { content:''; flex:1; height:1px; background:var(--line-soft); }
|
||||
.rows { display:grid; gap:9px; }
|
||||
.rc { display:flex; align-items:center; gap:14px; padding:14px 16px; border-radius:var(--r); border:1px solid var(--line-soft); background:var(--surface); transition:.14s; }
|
||||
.rc:hover { border-color:var(--line-strong); }
|
||||
.rc .ri { width:34px; height:38px; flex:none; display:grid; place-items:center; font-family:var(--mono); font-size:11px; font-weight:700; color:#1a1407; background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.rc .ri.soft { color:var(--text-2); background:var(--surface-2); border:1px solid var(--line); }
|
||||
.rc .rt { flex:1; min-width:0; } .rc .rt b { font-size:14.5px; font-weight:600; } .rc .rt .rs { font-size:12.5px; color:var(--text-muted); margin-top:2px; } .rc .rt .rs .mono { font-family:var(--mono); font-size:11.5px; color:var(--text-dim); } .rc .rt .rs .prov { color:var(--intel); }
|
||||
.rc .meta { font-family:var(--mono); font-size:11px; color:var(--text-dim); white-space:nowrap; }
|
||||
.bdg { font-size:11px; font-weight:600; padding:3px 9px; border-radius:999px; display:inline-flex; align-items:center; gap:5px; white-space:nowrap; }
|
||||
.bdg.live { color:var(--healthy); background:var(--healthy-wash); } .bdg.idle { color:var(--text-dim); background:var(--surface-2); } .bdg.warn { color:var(--attention); background:var(--honey-wash); } .bdg.work { color:var(--work); background:var(--work-wash); }
|
||||
.lnk { font-size:12px; font-weight:600; color:var(--text-2); cursor:pointer; } .lnk:hover { color:var(--honey); }
|
||||
|
||||
/* grid cards (artifacts) */
|
||||
.cards { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; }
|
||||
.acard { border:1px solid var(--line-soft); background:var(--surface); border-radius:var(--r-lg); padding:16px; cursor:pointer; transition:.15s; }
|
||||
.acard:hover { border-color:var(--honey-line); transform:translateY(-2px); box-shadow:var(--shadow); }
|
||||
.acard .ext { width:38px; height:42px; display:grid; place-items:center; font-family:var(--mono); font-size:11px; font-weight:700; border-radius:8px; margin-bottom:12px; }
|
||||
.acard h4 { font-size:14px; font-weight:600; margin:0 0 4px; } .acard .am { font-size:11.5px; color:var(--text-dim); font-family:var(--mono); } .acard .prov { font-size:11px; color:var(--intel); margin-top:9px; }
|
||||
|
||||
/* profile */
|
||||
.profhead { display:flex; align-items:center; gap:16px; margin-bottom:22px; }
|
||||
.profhead .pa2 { width:60px; height:66px; flex:none; display:grid; place-items:center; font-weight:800; font-size:24px; color:#1a1407; background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.profhead h2 { font-size:22px; font-weight:650; margin:0; letter-spacing:-0.02em; } .profhead .ph2 { font-size:13px; color:var(--text-muted); margin-top:3px; }
|
||||
.chips { display:flex; flex-wrap:wrap; gap:8px; }
|
||||
.chip { font-size:12.5px; font-weight:550; padding:6px 13px; border-radius:999px; border:1px solid var(--line-soft); background:var(--surface); color:var(--text-2); }
|
||||
.chip.on { background:var(--honey-wash); border-color:var(--honey-line); color:var(--honey); }
|
||||
.knows { display:grid; gap:9px; }
|
||||
.kfact { display:grid; grid-template-columns:18px 1fr; gap:11px; padding:12px 14px; border-radius:var(--r); border:1px solid var(--line-soft); background:var(--surface); font-size:13.5px; color:var(--text); }
|
||||
.kfact .kd { color:var(--honey); }
|
||||
|
||||
/* mission control */
|
||||
.mc-stat { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:22px; }
|
||||
.mcs { padding:16px; border-radius:var(--r-lg); border:1px solid var(--line-soft); background:var(--surface); }
|
||||
.mcs .v { font-size:24px; font-weight:750; letter-spacing:-0.02em; } .mcs .l { font-size:11.5px; color:var(--text-muted); margin-top:5px; }
|
||||
.svc { display:flex; align-items:center; gap:12px; padding:12px 0; border-bottom:1px solid var(--line-soft); font-size:13.5px; } .svc:last-child { border-bottom:0; } .svc .sd { width:8px; height:8px; border-radius:999px; flex:none; } .svc .sn { flex:1; } .svc .sm { font-family:var(--mono); font-size:11px; color:var(--text-dim); }
|
||||
|
||||
/* timeline */
|
||||
.tl { display:grid; grid-template-columns:74px 1fr; gap:16px; padding:13px 0; border-top:1px solid var(--line-soft); } .tl:first-child { border-top:0; }
|
||||
.tl .when { font-family:var(--mono); font-size:11px; color:var(--text-dim); padding-top:2px; }
|
||||
.tl .ev { font-size:13.5px; color:var(--text-2); line-height:1.5; } .tl .ev b { color:var(--text); font-weight:600; } .tl .ev .src { font-family:var(--mono); font-size:10.5px; margin-left:6px; }
|
||||
.tl .ev .src.mem { color:var(--intel); } .tl .ev .src.web { color:var(--work); } .tl .ev .src.sys { color:var(--text-dim); } .tl .ev .src.ok { color:var(--healthy); }
|
||||
|
||||
/* room */
|
||||
.room { display:grid; grid-template-columns:1fr 260px; gap:16px; }
|
||||
.roomstage { border:1px solid var(--line-soft); border-radius:var(--r-xl); background:var(--bg-2); padding:22px; min-height:280px; position:relative; overflow:hidden; }
|
||||
.roomstage .comb { position:absolute; inset:0; opacity:.4; }
|
||||
.live-act { position:relative; display:grid; gap:10px; }
|
||||
.la { display:flex; gap:11px; align-items:flex-start; padding:12px 14px; border-radius:var(--r); background:var(--surface); border:1px solid var(--line-soft); }
|
||||
.la .av { width:28px; height:32px; flex:none; display:grid; place-items:center; font-size:11px; font-weight:700; }
|
||||
.la .av.you { background:var(--surface-3); color:var(--text-2); border-radius:8px; } .la .av.bot { color:#1a1407; background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.la .lc b { font-size:13px; font-weight:650; } .la .lc p { margin:3px 0 0; font-size:12.5px; color:var(--text-muted); line-height:1.45; }
|
||||
.participants { border:1px solid var(--line-soft); border-radius:var(--r-lg); background:var(--surface); padding:16px; }
|
||||
.participants h4 { font-family:var(--mono); font-size:10.5px; letter-spacing:.1em; text-transform:uppercase; color:var(--text-dim); margin:0 0 12px; }
|
||||
.pp { display:flex; align-items:center; gap:10px; padding:7px 0; font-size:13px; } .pp .pd { width:7px; height:7px; border-radius:999px; } .pp .pn { flex:1; } .pp .pr { font-size:11px; color:var(--text-dim); }
|
||||
|
||||
.hidden { display:none !important; }
|
||||
@media (max-width:820px){ .rail { width:62px; } .rail .rh, .rail .rz, .ritem span.txt, .ritem .ct { display:none; } .cards, .mc-stat { grid-template-columns:1fr 1fr; } .room { grid-template-columns:1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">App surfaces · queued screens, now drawn</span>
|
||||
<span class="vlabel" id="vlabel"><b>Agents</b> — your team of specialists</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="app">
|
||||
<nav class="rail" id="rail">
|
||||
<div class="rz">Intelligence</div>
|
||||
<div class="ritem on" data-s="agents"><svg viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M9 9h6v6H9z"/></svg><span class="txt">Agents</span><span class="ct">4</span></div>
|
||||
<div class="ritem" data-s="room"><svg viewBox="0 0 24 24"><circle cx="9" cy="8" r="3"/><path d="M3.5 19a5.5 5.5 0 0 1 11 0M16 6a3 3 0 0 1 0 6"/></svg><span class="txt">Room</span><span class="ct live" style="color:var(--healthy)">●</span></div>
|
||||
<div class="rz">Work</div>
|
||||
<div class="ritem" data-s="artifacts"><svg viewBox="0 0 24 24"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6"/></svg><span class="txt">Artifacts</span><span class="ct">7</span></div>
|
||||
<div class="rz">System</div>
|
||||
<div class="ritem" data-s="mission"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg><span class="txt">Mission Control</span></div>
|
||||
<div class="ritem" data-s="timeline"><svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h10"/></svg><span class="txt">Timeline</span></div>
|
||||
<div class="ritem" data-s="profile"><svg viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M5.5 21a7 7 0 0 1 13 0"/></svg><span class="txt">Profile</span></div>
|
||||
</nav>
|
||||
|
||||
<div class="main">
|
||||
<div class="shead">
|
||||
<div><h1 id="sTitle">Agents</h1><p id="sSub">Your team of specialists — each with a persona, a model, and a memory of its work.</p></div>
|
||||
<button class="pa" id="sAction"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg><span id="sActionT">New agent</span></button>
|
||||
</div>
|
||||
<div class="body" id="body"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const head = {
|
||||
agents:['Agents','Your team of specialists — each with a persona, a model, and a memory of its work.','New agent'],
|
||||
room:['Room','Work alongside your agents live — shared context, real-time.','Invite'],
|
||||
artifacts:['Artifacts','Everything you and your agents have made — with provenance and versions.','New'],
|
||||
mission:['Mission Control','The health of your hive — services, agents, memory, sync.','Diagnostics'],
|
||||
timeline:['Timeline','One stream of everything that happened, across every workspace.','Filter'],
|
||||
profile:['Profile','What Waggle knows about you — so every agent works the way you do.','Edit'],
|
||||
};
|
||||
function rc(icon, title, sub, right, soft){ return `<div class="rc"><div class="ri ${soft?'soft':''}">${icon}</div><div class="rt"><b>${title}</b><div class="rs">${sub}</div></div>${right||''}</div>`; }
|
||||
|
||||
const panels = {
|
||||
agents: () => `
|
||||
<div class="grp">Working now</div>
|
||||
<div class="rows">
|
||||
${rc('R','Research-synth','<span class="rs">Analyst persona · Claude Sonnet · <span class="prov">Competitive Intelligence</span></span>','<div style="display:flex;gap:10px;align-items:center"><span class="bdg live"><span class="dot dot-live" style="background:var(--healthy)"></span>theming · 78%</span><span class="lnk">Open</span></div>')}
|
||||
${rc('D','Deck-builder','<span class="rs">Designer persona · GPT-4o · <span class="prov">Q2 Board Deck</span></span>','<div style="display:flex;gap:10px;align-items:center"><span class="bdg work">waiting on you</span><span class="lnk">Open</span></div>')}
|
||||
</div>
|
||||
<div class="grp">Idle · ready</div>
|
||||
<div class="rows">
|
||||
${rc('W','Writer','<span class="rs">Writing persona · auto · last ran 2d ago</span>','<span class="bdg idle">idle</span>')}
|
||||
${rc('A','Analyst','<span class="rs">Finance persona · local model · last ran 3d ago</span>','<span class="bdg idle">idle</span>')}
|
||||
</div>
|
||||
<div class="grp">Want more hands?</div>
|
||||
${rc('<svg width=16 height=16 viewBox="0 0 24 24" fill=none stroke="var(--honey)" stroke-width=1.8><circle cx=6 cy=7 r=2.4/><circle cx=18 cy=7 r=2.4/><circle cx=12 cy=17 r=2.4/><path d="M8 8l3 7M16 8l-3 7"/></svg>','Run a team of agents','<span class="rs">waggle-dance · let several specialists coordinate on one goal</span>','<span class="lnk">Start a swarm →</span>', true)}`,
|
||||
|
||||
room: () => `
|
||||
<div class="room">
|
||||
<div class="roomstage">
|
||||
<div class="comb"></div>
|
||||
<div class="live-act">
|
||||
<div class="la"><div class="av you">You</div><div class="lc"><b>You</b><p>Let's get the board narrative tight — focus on the regulated-industries angle.</p></div></div>
|
||||
<div class="la"><div class="av bot hex">R</div><div class="lc"><b>Research-synth</b><p>Pulling the 3 strongest proof points from memory and the teardown… two cite customer quotes.</p></div></div>
|
||||
<div class="la"><div class="av bot hex">D</div><div class="lc"><b>Deck-builder</b><p>Drafting slide 6 around that angle now — I'll show you a version in ~30s.</p></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="participants">
|
||||
<h4>In the room</h4>
|
||||
<div class="pp"><span class="pd" style="background:var(--honey)"></span><span class="pn">You</span><span class="pr">host</span></div>
|
||||
<div class="pp"><span class="pd dot-live" style="background:var(--healthy)"></span><span class="pn">Research-synth</span><span class="pr">live</span></div>
|
||||
<div class="pp"><span class="pd dot-live" style="background:var(--healthy)"></span><span class="pn">Deck-builder</span><span class="pr">live</span></div>
|
||||
<div class="pp"><span class="pd" style="background:var(--text-dim)"></span><span class="pn">Jonas P.</span><span class="pr">invited</span></div>
|
||||
<div style="margin-top:14px;font-size:12px;color:var(--text-muted);line-height:1.5">Everyone shares this workspace's memory. Voice is one tap away.</div>
|
||||
</div>
|
||||
</div>`,
|
||||
|
||||
artifacts: () => `
|
||||
<div class="grp">Recent · Competitive Intelligence</div>
|
||||
<div class="cards">
|
||||
<div class="acard"><div class="ext" style="background:var(--work-wash);color:var(--work)">MD</div><h4>teardown.md</h4><div class="am">48 KB · v3</div><div class="prov">⬡ made by Waggle</div></div>
|
||||
<div class="acard"><div class="ext" style="background:var(--healthy-wash);color:var(--healthy)">XLS</div><h4>pricing-landscape.xlsx</h4><div class="am">126 KB · v2</div><div class="prov">⬡ made by Waggle</div></div>
|
||||
<div class="acard"><div class="ext" style="background:var(--intel-wash);color:var(--intel)">MD</div><h4>board-brief.md</h4><div class="am">22 KB · v1</div><div class="prov">⬡ made by Claude Code</div></div>
|
||||
<div class="acard"><div class="ext" style="background:var(--risk-wash);color:var(--risk)">PDF</div><h4>mem0-teardown.pdf</h4><div class="am">1.4 MB</div><div class="prov" style="color:var(--text-dim)">you uploaded</div></div>
|
||||
<div class="acard"><div class="ext" style="background:var(--work-wash);color:var(--work)">PNG</div><h4>landscape-chart.png</h4><div class="am">340 KB</div><div class="prov">⬡ made by Waggle</div></div>
|
||||
<div class="acard"><div class="ext" style="background:var(--honey-wash);color:var(--honey)">PPT</div><h4>q2-board.pptx</h4><div class="am">2.1 MB · v4</div><div class="prov">⬡ made by Waggle</div></div>
|
||||
</div>`,
|
||||
|
||||
mission: () => `
|
||||
<div class="mc-stat">
|
||||
<div class="mcs"><div class="v" style="color:var(--healthy)">●</div><div class="l">All systems healthy</div></div>
|
||||
<div class="mcs"><div class="v">2</div><div class="l">Agents running</div></div>
|
||||
<div class="mcs"><div class="v" style="color:var(--intel)">581</div><div class="l">Memories (all hives)</div></div>
|
||||
<div class="mcs"><div class="v">58ms</div><div class="l">Avg recall</div></div>
|
||||
</div>
|
||||
<div class="grp">Local services</div>
|
||||
<div class="rows" style="padding:4px 0">
|
||||
<div class="svc"><span class="sd dot-live" style="background:var(--healthy)"></span><span class="sn">Memory engine <span style="color:var(--text-dim)">· hive-mind</span></span><span class="sm">SQLite · 581 rows · ok</span></div>
|
||||
<div class="svc"><span class="sd dot-live" style="background:var(--healthy)"></span><span class="sn">Agent runtime</span><span class="sm">2 active · ok</span></div>
|
||||
<div class="svc"><span class="sd dot-live" style="background:var(--healthy)"></span><span class="sn">Local model <span style="color:var(--text-dim)">· Qwen 2.5</span></span><span class="sm">Ollama · ok</span></div>
|
||||
<div class="svc"><span class="sd" style="background:var(--attention)"></span><span class="sn">Salesforce sync</span><span class="sm" style="color:var(--attention)">auth expired · fix</span></div>
|
||||
<div class="svc"><span class="sd" style="background:var(--text-dim)"></span><span class="sn">Team sync</span><span class="sm">not connected</span></div>
|
||||
</div>`,
|
||||
|
||||
timeline: () => `
|
||||
<div class="grp">Today</div>
|
||||
<div>
|
||||
<div class="tl"><div class="when">08:42</div><div class="ev">You opened <b>Competitive Intelligence</b><span class="src sys">⬡ session</span></div></div>
|
||||
<div class="tl"><div class="when">02:40</div><div class="ev"><b>Teardown</b> finished overnight · 9 competitors<span class="src ok">⬡ agent</span></div></div>
|
||||
<div class="tl"><div class="when">02:14</div><div class="ev">Salesforce export <b>failed</b> · auth expired<span class="src" style="color:var(--risk)">⬡ automation</span></div></div>
|
||||
<div class="tl"><div class="when">02:05</div><div class="ev">Folded <b>14 memories</b> into the hive<span class="src mem">⬡ mem</span></div></div>
|
||||
</div>
|
||||
<div class="grp">Yesterday</div>
|
||||
<div>
|
||||
<div class="tl"><div class="when">16:30</div><div class="ev">Learned <b>Mem0 changed pricing</b><span class="src web">⬡ web</span></div></div>
|
||||
<div class="tl"><div class="when">11:10</div><div class="ev"><b>Deck-builder</b> drafted 18 slides<span class="src ok">⬡ agent</span></div></div>
|
||||
</div>`,
|
||||
|
||||
profile: () => `
|
||||
<div class="profhead"><div class="pa2 hex">M</div><div><h2>Mara Kovač</h2><div class="ph2">Strategy · solo · Pro trial · mara@egzakta.com</div></div></div>
|
||||
<div class="grp">How I like to work <span style="text-transform:none;letter-spacing:0;color:var(--text-dim)">— every agent reads this</span></div>
|
||||
<div class="chips" style="margin-bottom:6px">
|
||||
<span class="chip on">Direct, no fluff</span><span class="chip on">Lead with the recommendation</span><span class="chip on">Always cite sources</span><span class="chip">Metric over narrative</span><span class="chip">+ add</span>
|
||||
</div>
|
||||
<div class="grp">What Waggle knows about me</div>
|
||||
<div class="knows">
|
||||
<div class="kfact"><span class="kd">⬡</span><div>Works in <b>competitive strategy</b>; reports up to a board quarterly.</div></div>
|
||||
<div class="kfact"><span class="kd">⬡</span><div>Prefers <b>regulated-industries framing</b> in market work.</div></div>
|
||||
<div class="kfact"><span class="kd">⬡</span><div>Trusts <b>local-first</b>; sensitive about data leaving the machine.</div></div>
|
||||
</div>
|
||||
<div class="grp">Expertise · personas you can speak as</div>
|
||||
<div class="chips"><span class="chip">Strategist</span><span class="chip">Analyst</span><span class="chip">Writer</span><span class="chip">Editor</span></div>`,
|
||||
};
|
||||
|
||||
function show(s){
|
||||
document.querySelectorAll('.ritem').forEach(r=>r.classList.toggle('on', r.dataset.s===s));
|
||||
document.getElementById('sTitle').textContent = head[s][0];
|
||||
document.getElementById('sSub').textContent = head[s][1];
|
||||
document.getElementById('sActionT').textContent = head[s][2];
|
||||
document.getElementById('vlabel').innerHTML = '<b>'+head[s][0]+'</b> — '+({agents:'your team of specialists',room:'work with agents live',artifacts:'everything you\u2019ve made',mission:'the health of your hive',timeline:'everything that happened',profile:'what Waggle knows about you'})[s];
|
||||
document.getElementById('body').innerHTML = panels[s]();
|
||||
document.getElementById('body').scrollTop = 0;
|
||||
}
|
||||
document.getElementById('rail').addEventListener('click', e=>{ const r=e.target.closest('.ritem'); if(r) show(r.dataset.s); });
|
||||
show('agents');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
180
docs/design_handoff_waggle_app/design-files/screens/auth.html
Normal file
180
docs/design_handoff_waggle_app/design-files/screens/auth.html
Normal file
@@ -0,0 +1,180 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Sign in</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 11px; border-radius: 7px; transition: .14s; white-space:nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
.split { flex:1; min-height:0; display:grid; grid-template-columns: 1.05fr 1fr; }
|
||||
/* brand panel */
|
||||
.brandside { position:relative; overflow:hidden; padding: 56px; display:flex; flex-direction:column; justify-content:space-between; background: linear-gradient(160deg, var(--bg-2), var(--bg)); border-right:1px solid var(--line-soft); }
|
||||
.brandside .comb { position:absolute; inset:0; opacity:.6; pointer-events:none; mask-image: radial-gradient(90% 80% at 30% 30%, black, transparent 75%); }
|
||||
.brandside .bk { position:relative; display:flex; align-items:center; gap:12px; }
|
||||
.brandside .bk .mk { width:34px; height:38px; display:grid; place-items:center; font-weight:800; font-size:16px; color:#1a1407; background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); box-shadow:var(--honey-glow); }
|
||||
.brandside .bk b { font-size:19px; font-weight:700; letter-spacing:-0.01em; }
|
||||
.brandside .pitch { position:relative; }
|
||||
.brandside .pitch h2 { font-size: clamp(26px,3vw,38px); font-weight:600; letter-spacing:-0.025em; line-height:1.08; margin:0 0 16px; max-width:16ch; }
|
||||
.brandside .pitch h2 em { font-style:normal; color:var(--honey); }
|
||||
.brandside .pitch p { font-size:15px; color:var(--text-2); line-height:1.55; max-width:42ch; margin:0; }
|
||||
.brandside .trust { position:relative; display:flex; flex-direction:column; gap:11px; }
|
||||
.brandside .trust .t { display:flex; align-items:center; gap:11px; font-size:13.5px; color:var(--text-2); }
|
||||
.brandside .trust .t svg { width:17px; height:17px; stroke:var(--honey); fill:none; stroke-width:1.8; flex:none; }
|
||||
|
||||
/* form panel */
|
||||
.formside { display:flex; align-items:center; justify-content:center; padding: 40px 32px; overflow:auto; }
|
||||
.formcard { width:100%; max-width: 380px; }
|
||||
.fhead { text-align:center; margin-bottom: 26px; }
|
||||
.fhead h1 { font-size: 24px; font-weight:650; letter-spacing:-0.02em; margin:0 0 6px; }
|
||||
.fhead p { font-size:13.5px; color:var(--text-muted); margin:0; }
|
||||
.fhead p a { color:var(--honey); text-decoration:none; font-weight:600; cursor:pointer; }
|
||||
|
||||
.sso { display:grid; gap:9px; margin-bottom:18px; }
|
||||
.ssobtn { display:flex; align-items:center; justify-content:center; gap:10px; padding:11px; border-radius:11px; border:1px solid var(--line-strong); background:var(--surface); color:var(--text); font-family:var(--sans); font-size:14px; font-weight:600; cursor:pointer; transition:.14s; }
|
||||
.ssobtn:hover { border-color:var(--honey-line); background:var(--surface-2); }
|
||||
.ssobtn .g { width:18px; height:18px; border-radius:4px; display:grid; place-items:center; font-family:var(--mono); font-size:11px; font-weight:700; background:var(--surface-3); flex:none; }
|
||||
.divider { display:flex; align-items:center; gap:12px; margin:18px 0; color:var(--text-dim); font-size:12px; font-family:var(--mono); }
|
||||
.divider::before, .divider::after { content:''; flex:1; height:1px; background:var(--line-soft); }
|
||||
|
||||
.fld { margin-bottom:13px; }
|
||||
.fld label { display:block; font-size:12.5px; font-weight:600; color:var(--text-2); margin-bottom:6px; }
|
||||
.fld input { width:100%; padding:11px 14px; border-radius:11px; border:1px solid var(--line); background:var(--surface); color:var(--text); font-family:var(--sans); font-size:14.5px; outline:none; transition:.14s; }
|
||||
.fld input:focus { border-color:var(--honey-line); box-shadow:var(--honey-glow); }
|
||||
.fld .lrow { display:flex; align-items:center; justify-content:space-between; }
|
||||
.fld .lrow a { font-size:11.5px; color:var(--honey); text-decoration:none; cursor:pointer; }
|
||||
.submit { width:100%; padding:12px; border-radius:11px; border:0; background:var(--honey); color:#1a1407; font-family:var(--sans); font-size:15px; font-weight:650; cursor:pointer; transition:.14s; margin-top:6px; }
|
||||
.submit:hover { background:var(--honey-bright); }
|
||||
.fineprint { text-align:center; font-size:11.5px; color:var(--text-dim); margin-top:16px; line-height:1.5; } .fineprint a { color:var(--text-2); }
|
||||
|
||||
/* OTP */
|
||||
.otp { display:flex; gap:10px; justify-content:center; margin:8px 0 4px; }
|
||||
.otp input { width:48px; height:56px; text-align:center; font-family:var(--mono); font-size:22px; border-radius:12px; border:1px solid var(--line); background:var(--surface); color:var(--text); outline:none; }
|
||||
.otp input:focus { border-color:var(--honey-line); box-shadow:var(--honey-glow); }
|
||||
.otp input.filled { border-color:var(--honey-line); color:var(--honey); }
|
||||
.resend { text-align:center; font-size:12.5px; color:var(--text-muted); margin-top:14px; } .resend a { color:var(--honey); cursor:pointer; }
|
||||
|
||||
/* local-first banner */
|
||||
.localnote { display:flex; gap:10px; align-items:flex-start; padding:12px 14px; border-radius:11px; background:var(--honey-wash); border:1px solid var(--honey-line); margin-bottom:20px; }
|
||||
.localnote svg { width:16px; height:16px; stroke:var(--honey); fill:none; stroke-width:1.8; flex:none; margin-top:1px; }
|
||||
.localnote p { margin:0; font-size:12px; color:var(--text-2); line-height:1.5; } .localnote b { color:var(--text); }
|
||||
|
||||
.view { display:none; } .view.on { display:block; }
|
||||
@media (max-width: 820px){ .split { grid-template-columns:1fr; } .brandside { display:none; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Auth · Clerk · state</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="signin">Sign in</button>
|
||||
<button data-v="signup">Sign up</button>
|
||||
<button data-v="verify">Verify</button>
|
||||
<button data-v="sso">SSO</button>
|
||||
</div>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="split">
|
||||
<div class="brandside">
|
||||
<div class="comb"></div>
|
||||
<div class="bk"><div class="mk hex">W</div><b>Waggle</b></div>
|
||||
<div class="pitch">
|
||||
<h2>Your work follows you, <em>everywhere.</em></h2>
|
||||
<p>Sign in to sync your hive across devices, collaborate with a team, and pick up any project exactly where you left off — on any machine.</p>
|
||||
</div>
|
||||
<div class="trust">
|
||||
<div class="t"><svg viewBox="0 0 24 24"><path d="M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/></svg>An account is optional — Waggle runs fully local without one</div>
|
||||
<div class="t"><svg viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></svg>Your memory stays yours; sign-in only adds sync</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="formside">
|
||||
<div class="formcard">
|
||||
<!-- SIGN IN -->
|
||||
<div class="view on" data-view="signin">
|
||||
<div class="fhead"><h1>Welcome back</h1><p>New to Waggle? <a data-go="signup">Create an account</a></p></div>
|
||||
<div class="sso">
|
||||
<button class="ssobtn"><span class="g">G</span>Continue with Google</button>
|
||||
<button class="ssobtn"><span class="g">⌥</span>Continue with Apple</button>
|
||||
</div>
|
||||
<div class="divider">or</div>
|
||||
<div class="fld"><label>Email</label><input type="email" value="mara@egzakta.com"></div>
|
||||
<div class="fld"><div class="lrow"><label>Password</label><a data-go="verify">Forgot?</a></div><input type="password" value="••••••••••"></div>
|
||||
<button class="submit" data-go="home">Sign in</button>
|
||||
<div class="fineprint">By continuing you agree to the <a href="#">Terms</a> & <a href="#">Privacy Policy</a>.</div>
|
||||
</div>
|
||||
|
||||
<!-- SIGN UP -->
|
||||
<div class="view" data-view="signup">
|
||||
<div class="fhead"><h1>Create your hive</h1><p>Already have an account? <a data-go="signin">Sign in</a></p></div>
|
||||
<div class="localnote"><svg viewBox="0 0 24 24"><path d="M3 11l9-8 9 8M5 10v10h14V10"/></svg><p><b>You don't need this to start.</b> Waggle works locally right away — create an account only when you want sync or a team.</p></div>
|
||||
<div class="sso">
|
||||
<button class="ssobtn"><span class="g">G</span>Sign up with Google</button>
|
||||
</div>
|
||||
<div class="divider">or</div>
|
||||
<div class="fld"><label>Name</label><input value="Mara Kovač"></div>
|
||||
<div class="fld"><label>Email</label><input type="email" placeholder="you@company.com"></div>
|
||||
<div class="fld"><label>Password</label><input type="password" placeholder="At least 10 characters"></div>
|
||||
<button class="submit" data-go="verify">Create account</button>
|
||||
<div class="fineprint">We'll send a code to verify your email.</div>
|
||||
</div>
|
||||
|
||||
<!-- VERIFY (OTP) -->
|
||||
<div class="view" data-view="verify">
|
||||
<div class="fhead"><h1>Check your email</h1><p>We sent a 6-digit code to <b style="color:var(--text-2)">mara@egzakta.com</b></p></div>
|
||||
<div class="otp" id="otp"></div>
|
||||
<button class="submit" data-go="home" style="margin-top:18px">Verify & continue</button>
|
||||
<div class="resend">Didn't get it? <a>Resend code</a> · <a data-go="signin">Use a different email</a></div>
|
||||
</div>
|
||||
|
||||
<!-- SSO / ORG -->
|
||||
<div class="view" data-view="sso">
|
||||
<div class="fhead"><h1>Single sign-on</h1><p>Use your organization's identity provider.</p></div>
|
||||
<div class="fld"><label>Work email or organization</label><input placeholder="you@company.com"></div>
|
||||
<button class="submit" data-go="home">Continue with SSO</button>
|
||||
<div class="divider">enterprise</div>
|
||||
<div class="localnote" style="background:var(--bg-2);border-color:var(--line-soft)"><svg viewBox="0 0 24 24" style="stroke:var(--text-muted)"><path d="M9 12l2 2 4-4M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/></svg><p>SAML, SCIM provisioning, and audit logs are available on <b>Teams</b> and <b>KVARK</b>. <a style="color:var(--honey)" href="#">Talk to sales →</a></p></div>
|
||||
<div class="fineprint"><a data-go="signin">← Back to sign in</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
// OTP boxes
|
||||
document.getElementById('otp').innerHTML = Array.from({length:6},(_, i)=>`<input maxlength="1" inputmode="numeric" data-i="${i}" value="${i<3?[2,4,9][i]:''}">`).join('');
|
||||
document.querySelectorAll('#otp input').forEach((inp,i,arr)=>{
|
||||
if(inp.value) inp.classList.add('filled');
|
||||
inp.addEventListener('input', e=>{ inp.classList.toggle('filled', !!inp.value); if(inp.value && arr[i+1]) arr[i+1].focus(); });
|
||||
inp.addEventListener('keydown', e=>{ if(e.key==='Backspace' && !inp.value && arr[i-1]) arr[i-1].focus(); });
|
||||
});
|
||||
|
||||
function go(v){
|
||||
if(v==='home'){ document.body.style.transition='opacity .25s'; document.body.style.opacity='.4'; setTimeout(()=>{ try{ location.href='../Waggle Reimagined.html#home'; }catch(e){} }, 260); return; }
|
||||
document.querySelectorAll('.view').forEach(el=>el.classList.toggle('on', el.dataset.view===v));
|
||||
document.querySelectorAll('#seg button').forEach(b=>b.classList.toggle('on', b.dataset.v===v));
|
||||
}
|
||||
document.getElementById('seg').addEventListener('click', e=>{ const b=e.target.closest('button'); if(b) go(b.dataset.v); });
|
||||
document.addEventListener('click', e=>{ const a=e.target.closest('[data-go]'); if(a){ e.preventDefault(); go(a.dataset.go); } });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,195 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Benchmarks</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height:100vh; overflow:hidden; display:flex; flex-direction:column; }
|
||||
.controls { flex:none; display:flex; align-items:center; gap:14px; padding:11px 20px; border-bottom:1px solid var(--line-soft); background:color-mix(in srgb,var(--bg) 88%,transparent); backdrop-filter:blur(10px); z-index:30; }
|
||||
.controls .lab { font-family:var(--mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); }
|
||||
.seg { display:inline-flex; padding:3px; gap:3px; border-radius:10px; background:var(--surface-2); border:1px solid var(--line-soft); }
|
||||
.seg button { font-family:var(--sans); font-size:12.5px; font-weight:600; cursor:pointer; border:0; background:transparent; color:var(--text-muted); padding:6px 13px; border-radius:7px; transition:.14s; white-space:nowrap; }
|
||||
.seg button.on { background:var(--honey); color:#1a1407; }
|
||||
.controls .vlabel { font-size:12.5px; color:var(--text-muted); } .controls .vlabel b { color:var(--text); font-weight:600; }
|
||||
.controls .right { margin-left:auto; display:flex; align-items:center; gap:10px; }
|
||||
.tbtn { width:34px; height:34px; border-radius:9px; border:1px solid var(--line); background:var(--surface); color:var(--text-2); cursor:pointer; }
|
||||
.tbtn:hover { border-color:var(--honey-line); color:var(--honey); }
|
||||
.stage { flex:1; min-height:0; overflow:auto; }
|
||||
.wrap { max-width:1000px; margin:0 auto; padding:30px 32px 70px; }
|
||||
.view { display:none; } .view.on { display:block; }
|
||||
|
||||
.head { margin-bottom:24px; }
|
||||
.head .eyebrow { font-family:var(--mono); font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--honey); margin-bottom:12px; display:flex; align-items:center; gap:9px; } .head .eyebrow::before { content:''; width:20px; height:1px; background:var(--honey-line); }
|
||||
.head h1 { font-size:28px; font-weight:650; letter-spacing:-0.02em; margin:0 0 10px; } .head h1 em { font-style:normal; color:var(--honey); }
|
||||
.head p { font-size:15px; color:var(--text-muted); line-height:1.55; margin:0; max-width:64ch; } .head p b { color:var(--text-2); }
|
||||
|
||||
/* category framing */
|
||||
.frame-cats { display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-bottom:30px; }
|
||||
.fcat { padding:22px; border-radius:var(--r-lg); border:1px solid var(--line-soft); background:var(--surface); }
|
||||
.fcat.us { border-color:var(--honey-line); background:linear-gradient(155deg,var(--surface-2),var(--surface)); }
|
||||
.fcat .tag { font-family:var(--mono); font-size:10px; letter-spacing:.1em; text-transform:uppercase; color:var(--text-dim); margin-bottom:10px; } .fcat.us .tag { color:var(--honey); }
|
||||
.fcat h3 { font-size:18px; font-weight:650; letter-spacing:-0.01em; margin:0 0 8px; }
|
||||
.fcat p { margin:0; font-size:13px; color:var(--text-muted); line-height:1.55; }
|
||||
.fcat .who { margin-top:12px; font-size:11.5px; color:var(--text-dim); } .fcat .who b { color:var(--text-2); }
|
||||
|
||||
/* matrix */
|
||||
.matrix { border:1px solid var(--line-soft); border-radius:var(--r-lg); overflow:hidden; background:var(--surface); }
|
||||
.mtable { width:100%; border-collapse:collapse; }
|
||||
.mtable th, .mtable td { padding:13px 10px; text-align:center; border-bottom:1px solid var(--line-soft); }
|
||||
.mtable thead th { font-size:11.5px; font-weight:650; color:var(--text-2); position:sticky; top:0; background:var(--surface-2); }
|
||||
.mtable thead th.usCol { color:var(--honey); }
|
||||
.mtable th.cap { text-align:left; font-size:13px; font-weight:600; color:var(--text); width:32%; }
|
||||
.mtable td.cap { text-align:left; font-size:13px; color:var(--text-2); width:32%; }
|
||||
.mtable .usCol { background:color-mix(in srgb,var(--honey) 7%,transparent); }
|
||||
.mtable td.usCol { border-left:1px solid var(--honey-line); border-right:1px solid var(--honey-line); }
|
||||
.mtable tbody tr:last-child td.usCol { border-bottom:1px solid var(--honey-line); }
|
||||
.mk { display:inline-grid; place-items:center; width:22px; height:22px; border-radius:999px; font-size:13px; }
|
||||
.mk.yes { color:var(--healthy); } .mk.us { color:var(--honey); font-weight:700; }
|
||||
.mk.part { color:var(--attention); } .mk.no { color:var(--text-dim); }
|
||||
.colsub { font-size:9.5px; font-weight:400; color:var(--text-dim); display:block; margin-top:2px; font-family:var(--mono); }
|
||||
.legend { display:flex; gap:18px; flex-wrap:wrap; margin-top:16px; font-size:12.5px; color:var(--text-muted); }
|
||||
.legend span { display:inline-flex; align-items:center; gap:7px; }
|
||||
.disclaimer { margin-top:18px; display:flex; gap:11px; align-items:flex-start; padding:14px 16px; border-radius:var(--r); background:var(--bg-2); border:1px solid var(--line-soft); }
|
||||
.disclaimer svg { width:16px; height:16px; stroke:var(--text-muted); fill:none; stroke-width:1.8; flex:none; margin-top:1px; }
|
||||
.disclaimer p { margin:0; font-size:12px; color:var(--text-dim); line-height:1.55; } .disclaimer b { color:var(--text-2); }
|
||||
|
||||
/* memory bench */
|
||||
.bench { display:grid; gap:12px; margin-top:8px; }
|
||||
.barrow { display:grid; grid-template-columns:180px 1fr 64px; gap:16px; align-items:center; }
|
||||
.barrow .blab { font-size:13.5px; font-weight:600; text-align:right; line-height:1.15; } .barrow .blab small { display:block; font-weight:400; color:var(--text-dim); font-size:10.5px; font-family:var(--mono); margin-top:2px; }
|
||||
.barrow .track { height:34px; border-radius:9px; background:var(--surface); border:1px solid var(--line-soft); overflow:hidden; }
|
||||
.barrow .fill { height:100%; border-radius:8px 0 0 8px; background:var(--surface-3); }
|
||||
.barrow.us .fill { background:linear-gradient(90deg,var(--honey-deep),var(--honey-bright)); box-shadow:var(--honey-glow); }
|
||||
.barrow .bval { font-family:var(--mono); font-size:14px; font-weight:600; color:var(--text-2); } .barrow.us .bval { color:var(--honey); }
|
||||
.benchstats { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin:26px 0; }
|
||||
.bs { padding:20px; border-radius:var(--r-lg); border:1px solid var(--line-soft); background:var(--surface); } .bs .v { font-size:32px; font-weight:750; letter-spacing:-0.03em; color:var(--honey); } .bs .l { font-size:13px; color:var(--text-muted); margin-top:6px; } .bs .l b { color:var(--text); }
|
||||
|
||||
@media (max-width:820px){ .frame-cats { grid-template-columns:1fr; } .mtable thead th, .mtable td { padding:10px 5px; font-size:10px; } .barrow { grid-template-columns:110px 1fr 50px; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Benchmarks · view</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="caps">vs competitors</button>
|
||||
<button data-v="memory">Memory SOTA</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Capabilities</b> — where Waggle sits vs the field</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<!-- CAPS -->
|
||||
<div class="view on wrap" data-view="caps">
|
||||
<div class="head">
|
||||
<div class="eyebrow">How we're different</div>
|
||||
<h1>They automate tasks. <em>Waggle remembers you.</em></h1>
|
||||
<p>The strong agents today are <b>terminal coding tools</b> for engineers. Waggle plays a different game: a <b>persistent, local-first memory layer</b> for knowledge workers that even <b>runs those agents inside it</b>. Here's the honest lay of the land.</p>
|
||||
</div>
|
||||
|
||||
<div class="frame-cats">
|
||||
<div class="fcat">
|
||||
<div class="tag">The field</div>
|
||||
<h3>Task agents</h3>
|
||||
<p>Powerful, mostly terminal-based, model-locked, and built for developers. Each session starts fresh; the intelligence lives in the model, not in a memory of you.</p>
|
||||
<div class="who"><b>Claude Code · Codex · Claude Cowork · Hermes · Odysseus</b></div>
|
||||
</div>
|
||||
<div class="fcat us">
|
||||
<div class="tag">Our category</div>
|
||||
<h3>A memory layer + workspace</h3>
|
||||
<p>Knows you and your work across every session, runs on any model (even local), is built for non-technical experts — and can launch the task agents into its shared memory.</p>
|
||||
<div class="who"><b>Waggle</b> — complements them, doesn't compete head-on</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="matrix">
|
||||
<table class="mtable">
|
||||
<thead><tr>
|
||||
<th class="cap">Capability</th>
|
||||
<th class="usCol">Waggle</th>
|
||||
<th>Claude Code<span class="colsub">CLI</span></th>
|
||||
<th>Codex<span class="colsub">CLI</span></th>
|
||||
<th>Cowork<span class="colsub">Anthropic</span></th>
|
||||
<th>Hermes<span class="colsub">agent</span></th>
|
||||
<th>Odysseus<span class="colsub">agent</span></th>
|
||||
</tr></thead>
|
||||
<tbody id="mbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="legend">
|
||||
<span><span class="mk yes">●</span>Yes</span>
|
||||
<span><span class="mk part">◐</span>Partial / limited</span>
|
||||
<span><span class="mk no">○</span>No / not the focus</span>
|
||||
</div>
|
||||
<div class="disclaimer">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 8h.01M11 12h1v4h1"/></svg>
|
||||
<p><b>This is a positioning view, not a lab benchmark</b> — capabilities as understood June 2026, focused on the dimensions Waggle is built around. Coding agents are genuinely excellent at deep code work (the last row), which is exactly why Waggle <b>launches them</b> rather than replacing them. The one head-to-head lab result is the memory benchmark → see "Memory SOTA".</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MEMORY -->
|
||||
<div class="view wrap" data-view="memory">
|
||||
<div class="head">
|
||||
<div class="eyebrow">State of the art · LoCoMo · June 2026</div>
|
||||
<h1>The best long-term memory <em>on record.</em></h1>
|
||||
<p>On <b>LoCoMo</b> — the standard test for long-term conversational memory — Waggle's open-source substrate scores <b>87.66%</b>, a new state of the art, measured under the prior leader's own protocol and judge.</p>
|
||||
</div>
|
||||
<div class="bench">
|
||||
<div class="barrow us"><div class="blab">Waggle · Hive Mind<small>ours · local</small></div><div class="track"><div class="fill" style="width:87.7%"></div></div><div class="bval">87.66</div></div>
|
||||
<div class="barrow"><div class="blab">Memori<small>prev. SOTA</small></div><div class="track"><div class="fill" style="width:81.95%"></div></div><div class="bval">81.95</div></div>
|
||||
<div class="barrow"><div class="blab">LangMem<small>corrected</small></div><div class="track"><div class="fill" style="width:78.05%"></div></div><div class="bval">78.05</div></div>
|
||||
<div class="barrow"><div class="blab">Mem0<small>baseline</small></div><div class="track"><div class="fill" style="width:62.47%"></div></div><div class="bval">62.47</div></div>
|
||||
</div>
|
||||
<div class="benchstats">
|
||||
<div class="bs"><div class="v">+5.71</div><div class="l">points over the prior best · <b>z = 4.42, p < 10⁻⁵</b></div></div>
|
||||
<div class="bs"><div class="v">92.75%</div><div class="l">single-hop recall — <b>~1pt off the full-context ceiling</b></div></div>
|
||||
<div class="bs"><div class="v">100%</div><div class="l">local — warm recalls in <b>58–83 ms</b>, on-device</div></div>
|
||||
</div>
|
||||
<div class="disclaimer">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 8h.01M11 12h1v4h1"/></svg>
|
||||
<p><b>LoCoMo</b>, N = 1,540, GPT-4.1-mini as answerer & judge — the prior SOTA's exact published protocol, reproduced in-harness to 0.03 points before comparison. The intelligence lives in the <b>memory layer, not the model</b>, so it travels onto a small local model too. Honest caveat: higher token use per question than the leanest systems; efficiency work underway. Reproducible offline: <span style="font-family:var(--mono);color:var(--text-2)">github.com/marolinik/hive-mind</span>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
// matrix data — Y=yes, P=partial, N=no. Order: Waggle, ClaudeCode, Codex, Cowork, Hermes, Odysseus
|
||||
const CAPS = [
|
||||
['Persistent memory across sessions', ['Y','P','N','P','P','P']],
|
||||
['Local-first & private by default', ['Y','N','N','N','P','P']],
|
||||
['Any model — incl. small local models', ['Y','N','N','N','P','P']],
|
||||
['Built for non-technical experts', ['Y','N','N','P','N','N']],
|
||||
['Runs your existing coding agents', ['Y','N','N','N','N','N']],
|
||||
['Self-evolving skills', ['Y','N','N','N','P','N']],
|
||||
['Skills shared across agents/workspaces', ['Y','P','P','N','P','N']],
|
||||
['Multi-agent swarm', ['Y','P','N','P','P','Y']],
|
||||
['Native desktop app · Win + Mac', ['Y','N','N','P','N','N']],
|
||||
['Audit-ready / EU AI Act', ['Y','P','P','P','N','N']],
|
||||
['Deep coding in the terminal', ['P','Y','Y','P','P','Y']],
|
||||
];
|
||||
const MK = { Y:'<span class="mk yes">●</span>', P:'<span class="mk part">◐</span>', N:'<span class="mk no">○</span>' };
|
||||
const USMK = '<span class="mk us">●</span>';
|
||||
document.getElementById('mbody').innerHTML = CAPS.map(([cap, marks]) => `
|
||||
<tr>
|
||||
<td class="cap">${cap}</td>
|
||||
<td class="usCol">${marks[0]==='Y'?USMK:(marks[0]==='P'?'<span class="mk part">◐</span>':'<span class="mk no">○</span>')}</td>
|
||||
${marks.slice(1).map(m=>`<td>${MK[m]}</td>`).join('')}
|
||||
</tr>`).join('');
|
||||
|
||||
const labels = { caps:'<b>Capabilities</b> — where Waggle sits vs the field', memory:'<b>Memory SOTA</b> — the one head-to-head lab result' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e=>{ const b=e.target.closest('button'); if(!b) return; [...seg.children].forEach(x=>x.classList.toggle('on',x===b)); document.querySelectorAll('.view').forEach(v=>v.classList.toggle('on', v.dataset.view===b.dataset.v)); document.getElementById('vlabel').innerHTML = labels[b.dataset.v]; document.querySelector('.stage').scrollTop=0; });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
247
docs/design_handoff_waggle_app/design-files/screens/billing.html
Normal file
247
docs/design_handoff_waggle_app/design-files/screens/billing.html
Normal file
@@ -0,0 +1,247 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Billing</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 11px; border-radius: 7px; transition: .14s; white-space:nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
.stage { flex:1; min-height:0; overflow:auto; }
|
||||
.view { display:none; } .view.on { display:block; }
|
||||
.view[data-view="checkout"].on { display:grid; }
|
||||
|
||||
/* PLANS */
|
||||
.wrap { max-width: 980px; margin: 0 auto; padding: 34px 32px 60px; }
|
||||
.phead { text-align:center; margin-bottom: 22px; }
|
||||
.phead h1 { font-size:28px; font-weight:650; letter-spacing:-0.02em; margin:0 0 8px; } .phead h1 em { font-style:normal; color:var(--honey); }
|
||||
.phead p { font-size:14.5px; color:var(--text-muted); margin:0 0 18px; }
|
||||
.cycle { display:inline-flex; padding:4px; gap:3px; border-radius:11px; background:var(--surface-2); border:1px solid var(--line-soft); }
|
||||
.cycle button { font-family:var(--sans); font-size:13px; font-weight:600; cursor:pointer; border:0; background:transparent; color:var(--text-muted); padding:8px 16px; border-radius:8px; }
|
||||
.cycle button.on { background:var(--honey); color:#1a1407; }
|
||||
.cycle .save { font-size:10.5px; color:var(--healthy); margin-left:5px; }
|
||||
.cycle button.on .save { color:#1a1407; }
|
||||
|
||||
.plans { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-top:24px; align-items:stretch; }
|
||||
.plan { padding:26px; border-radius:var(--r-lg); border:1px solid var(--line-soft); background:var(--surface); display:flex; flex-direction:column; position:relative; }
|
||||
.plan.feat { border-color:var(--honey-line); box-shadow:var(--honey-glow); }
|
||||
.plan.cur { border-color:var(--work); }
|
||||
.plan .pop { position:absolute; top:-11px; left:26px; font-size:11px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:#1a1407; background:var(--honey); padding:4px 11px; border-radius:999px; }
|
||||
.plan .curtag { position:absolute; top:-11px; right:26px; font-size:11px; font-weight:700; text-transform:uppercase; color:#fff; background:var(--work); padding:4px 11px; border-radius:999px; }
|
||||
.plan .pn { font-size:15px; font-weight:600; color:var(--text-2); }
|
||||
.plan .pp { font-size:38px; font-weight:750; letter-spacing:-0.03em; margin:12px 0 2px; } .plan .pp small { font-size:15px; font-weight:500; color:var(--text-muted); letter-spacing:0; }
|
||||
.plan .ptag { font-size:13px; color:var(--text-muted); margin-bottom:20px; min-height:36px; }
|
||||
.plan ul { list-style:none; margin:0 0 24px; padding:0; display:grid; gap:10px; flex:1; }
|
||||
.plan li { font-size:13px; color:var(--text-2); display:grid; grid-template-columns:17px 1fr; gap:9px; line-height:1.4; }
|
||||
.plan li svg { width:14px; height:14px; stroke:var(--healthy); fill:none; stroke-width:2.4; margin-top:2px; }
|
||||
.pbtn { padding:11px; border-radius:10px; font-family:var(--sans); font-size:14px; font-weight:650; cursor:pointer; border:1px solid transparent; text-align:center; }
|
||||
.pbtn.go { background:var(--honey); color:#1a1407; } .pbtn.go:hover { background:var(--honey-bright); }
|
||||
.pbtn.ghost { background:var(--surface-2); color:var(--text-2); border-color:var(--line-strong); } .pbtn.ghost:hover { border-color:var(--honey-line); color:var(--honey); }
|
||||
.pbtn.disabled { background:transparent; color:var(--text-dim); border-color:var(--line-soft); cursor:default; }
|
||||
|
||||
/* CHECKOUT */
|
||||
.cowrap { max-width: 860px; margin:0 auto; padding: 30px 32px 60px; display:grid; grid-template-columns: 1.2fr 1fr; gap:28px; }
|
||||
.panel { border:1px solid var(--line-soft); background:var(--surface); border-radius:var(--r-lg); padding:24px; }
|
||||
.panel h2 { font-size:16px; font-weight:650; margin:0 0 18px; letter-spacing:-0.01em; }
|
||||
.fld { margin-bottom:14px; } .fld label { display:block; font-size:12.5px; font-weight:600; color:var(--text-2); margin-bottom:6px; }
|
||||
.fld input { width:100%; padding:11px 14px; border-radius:10px; border:1px solid var(--line); background:var(--bg-2); color:var(--text); font-family:var(--sans); font-size:14px; outline:none; }
|
||||
.fld input:focus { border-color:var(--honey-line); box-shadow:var(--honey-glow); }
|
||||
.cardfield { position:relative; }
|
||||
.cardfield .brand { position:absolute; right:12px; top:50%; transform:translateY(-50%); font-family:var(--mono); font-size:11px; color:var(--text-dim); }
|
||||
.frow { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
|
||||
.secure { display:flex; align-items:center; gap:8px; font-size:11.5px; color:var(--text-dim); margin-top:6px; }
|
||||
.secure svg { width:13px; height:13px; stroke:var(--healthy); fill:none; stroke-width:1.8; }
|
||||
.secure b { color:var(--text-2); font-weight:500; }
|
||||
.powered { text-align:center; font-family:var(--mono); font-size:11px; color:var(--text-dim); margin-top:16px; }
|
||||
.powered b { color:var(--text-2); }
|
||||
|
||||
/* summary */
|
||||
.summary .line { display:flex; align-items:center; justify-content:space-between; padding:11px 0; border-bottom:1px solid var(--line-soft); font-size:13.5px; }
|
||||
.summary .line .l { color:var(--text-muted); } .summary .line .v { font-weight:600; }
|
||||
.summary .line.total { border-bottom:0; padding-top:16px; font-size:16px; } .summary .line.total .v { color:var(--honey); font-weight:750; font-size:20px; letter-spacing:-0.02em; }
|
||||
.summary .planrow { display:flex; align-items:center; gap:12px; padding-bottom:16px; border-bottom:1px solid var(--line-soft); margin-bottom:6px; }
|
||||
.summary .planrow .wm { width:34px; height:38px; flex:none; display:grid; place-items:center; font-weight:800; font-size:13px; color:#1a1407; background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.summary .planrow b { font-size:15px; font-weight:650; display:block; } .summary .planrow span { font-size:12px; color:var(--text-muted); display:block; margin-top:2px; }
|
||||
.promo { display:flex; gap:8px; margin:14px 0; } .promo input { flex:1; padding:9px 12px; border-radius:9px; border:1px solid var(--line); background:var(--bg-2); color:var(--text); font-family:var(--sans); font-size:13px; outline:none; }
|
||||
.promo button { padding:9px 14px; border-radius:9px; border:1px solid var(--line-strong); background:var(--surface-2); color:var(--text-2); font-size:12.5px; font-weight:600; cursor:pointer; }
|
||||
.paybtn { width:100%; padding:13px; border-radius:11px; border:0; background:var(--honey); color:#1a1407; font-family:var(--sans); font-size:15px; font-weight:650; cursor:pointer; margin-top:14px; }
|
||||
.paybtn:hover { background:var(--honey-bright); }
|
||||
.guarantee { text-align:center; font-size:11.5px; color:var(--text-dim); margin-top:12px; }
|
||||
|
||||
/* SUCCESS */
|
||||
.succ { max-width:520px; margin:0 auto; padding:70px 32px; text-align:center; }
|
||||
.succ .ring { width:78px; height:78px; border-radius:999px; margin:0 auto 24px; display:grid; place-items:center; background:var(--healthy-wash); border:1px solid color-mix(in srgb,var(--healthy) 40%,transparent); }
|
||||
.succ .ring svg { width:36px; height:36px; stroke:var(--healthy); fill:none; stroke-width:2.2; }
|
||||
.succ h1 { font-size:28px; font-weight:650; letter-spacing:-0.02em; margin:0 0 12px; } .succ h1 em { font-style:normal; color:var(--honey); }
|
||||
.succ p { font-size:15px; color:var(--text-2); line-height:1.55; margin:0 auto 28px; max-width:42ch; } .succ p b { color:var(--text); }
|
||||
.succ .rcpt { text-align:left; border:1px solid var(--line-soft); border-radius:var(--r); background:var(--surface); padding:16px 18px; margin-bottom:24px; }
|
||||
.succ .rcpt .r { display:flex; justify-content:space-between; padding:7px 0; font-size:13px; } .succ .rcpt .r .l { color:var(--text-muted); } .succ .rcpt .r .v { font-weight:600; }
|
||||
.succ .cta { display:inline-flex; gap:10px; }
|
||||
.succ .cta a { padding:12px 22px; border-radius:11px; font-size:14.5px; font-weight:650; text-decoration:none; cursor:pointer; }
|
||||
.succ .cta .go { background:var(--honey); color:#1a1407; } .succ .cta .ghost { background:var(--surface); color:var(--text-2); border:1px solid var(--line-strong); }
|
||||
|
||||
/* MANAGE */
|
||||
.mwrap { max-width:760px; margin:0 auto; padding:30px 32px 60px; }
|
||||
.mwrap h1 { font-size:24px; font-weight:650; letter-spacing:-0.02em; margin:0 0 20px; }
|
||||
.mcard { border:1px solid var(--line-soft); background:var(--surface); border-radius:var(--r-lg); padding:22px; margin-bottom:14px; }
|
||||
.mcard.cur { border-color:var(--honey-line); }
|
||||
.mtop { display:flex; align-items:center; justify-content:space-between; gap:14px; }
|
||||
.mtop .pl b { font-size:17px; font-weight:650; } .mtop .pl span { font-size:13px; color:var(--text-muted); display:block; margin-top:3px; }
|
||||
.mtop .badge { font-size:11.5px; font-weight:650; color:var(--honey); background:var(--honey-wash); border:1px solid var(--honey-line); padding:6px 12px; border-radius:999px; }
|
||||
.mrow { display:flex; align-items:center; justify-content:space-between; padding:13px 0; border-top:1px solid var(--line-soft); font-size:13.5px; }
|
||||
.mrow .l { color:var(--text-muted); } .mrow .v { font-weight:600; display:flex; align-items:center; gap:9px; }
|
||||
.mrow .card { font-family:var(--mono); font-size:13px; }
|
||||
.mrow .chg { font-size:12px; font-weight:600; color:var(--honey); cursor:pointer; }
|
||||
.invoices .iv { display:grid; grid-template-columns:1fr auto auto auto; gap:14px; align-items:center; padding:11px 0; border-top:1px solid var(--line-soft); font-size:13px; }
|
||||
.invoices .iv:first-child { border-top:0; }
|
||||
.invoices .iv .dt { color:var(--text-2); } .invoices .iv .am { font-family:var(--mono); color:var(--text-2); } .invoices .iv .st { font-size:11px; color:var(--healthy); background:var(--healthy-wash); padding:3px 8px; border-radius:999px; } .invoices .iv .dl { font-size:12px; color:var(--honey); cursor:pointer; }
|
||||
.btn { font-family:var(--sans); font-size:13px; font-weight:650; padding:10px 16px; border-radius:10px; cursor:pointer; border:1px solid transparent; }
|
||||
.btn.ghost { background:var(--surface-2); color:var(--text-2); border-color:var(--line-strong); } .btn.danger { background:transparent; color:var(--risk); border-color:color-mix(in srgb,var(--risk) 35%,transparent); }
|
||||
|
||||
@media (max-width:820px){ .plans { grid-template-columns:1fr; } .cowrap { grid-template-columns:1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Billing · Stripe · state</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="plans">Plans</button>
|
||||
<button data-v="checkout">Checkout</button>
|
||||
<button data-v="success">Success</button>
|
||||
<button data-v="manage">Manage</button>
|
||||
</div>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<!-- PLANS -->
|
||||
<div class="view on wrap" data-view="plans">
|
||||
<div class="phead">
|
||||
<h1>Upgrade your <em>hive.</em></h1>
|
||||
<p>Memory is free forever. You only pay for scale — no feature-count games.</p>
|
||||
<div class="cycle" id="cycle"><button class="on" data-c="mo">Monthly</button><button data-c="yr">Annual<span class="save">−20%</span></button></div>
|
||||
</div>
|
||||
<div class="plans">
|
||||
<div class="plan cur">
|
||||
<span class="curtag">Current</span>
|
||||
<div class="pn">Solo</div>
|
||||
<div class="pp">$0<small> / forever</small></div>
|
||||
<div class="ptag">For individuals exploring an AI workspace.</div>
|
||||
<ul><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>Personal memory graph</li><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>All major LLMs + local</li><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>Local-first by default</li></ul>
|
||||
<div class="pbtn disabled">Your plan</div>
|
||||
</div>
|
||||
<div class="plan feat">
|
||||
<span class="pop">Most popular</span>
|
||||
<div class="pn">Pro</div>
|
||||
<div class="pp" data-mo="$19" data-yr="$15">$19<small> / month</small></div>
|
||||
<div class="ptag">For power users compounding across projects.</div>
|
||||
<ul><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>Everything in Solo</li><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>Sync across devices</li><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>Marketplace skills & connectors</li><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>Self-evolving skills</li></ul>
|
||||
<div class="pbtn go" data-go="checkout">Choose Pro</div>
|
||||
</div>
|
||||
<div class="plan">
|
||||
<div class="pn">Teams</div>
|
||||
<div class="pp" data-mo="$49" data-yr="$39">$49<small> / seat / mo</small></div>
|
||||
<div class="ptag">Shared memory without losing privacy.</div>
|
||||
<ul><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>Everything in Pro</li><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>Shared team memory</li><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>WaggleDance multi-agent</li><li><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>SSO & role-based access</li></ul>
|
||||
<div class="pbtn ghost" data-go="checkout">Choose Teams</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CHECKOUT -->
|
||||
<div class="view cowrap" data-view="checkout">
|
||||
<div class="panel">
|
||||
<h2>Payment details</h2>
|
||||
<div class="fld"><label>Email</label><input value="mara@egzakta.com"></div>
|
||||
<div class="fld"><label>Card information</label>
|
||||
<div class="cardfield"><input placeholder="1234 1234 1234 1234" value="4242 4242 4242 4242"><span class="brand">VISA</span></div>
|
||||
</div>
|
||||
<div class="frow"><div class="fld"><input placeholder="MM / YY" value="08 / 28"></div><div class="fld"><input placeholder="CVC" value="•••"></div></div>
|
||||
<div class="fld"><label>Name on card</label><input value="Mara Kovač"></div>
|
||||
<div class="fld"><label>Country</label><input value="Germany"></div>
|
||||
<div class="secure"><svg viewBox="0 0 24 24"><rect x="4" y="10" width="16" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg><b>Encrypted & secure.</b> We never store your card — Stripe does.</div>
|
||||
<button class="paybtn" data-go="success">Pay $19.00 / month</button>
|
||||
<div class="powered">Powered by <b>Stripe</b> · cancel anytime</div>
|
||||
</div>
|
||||
<div class="panel summary">
|
||||
<h2>Order summary</h2>
|
||||
<div class="planrow"><div class="wm hex">W</div><div><b>Waggle Pro</b><span>Monthly · renews Jul 14</span></div></div>
|
||||
<div class="line"><span class="l">Pro plan</span><span class="v">$19.00</span></div>
|
||||
<div class="promo"><input placeholder="Promo code"><button>Apply</button></div>
|
||||
<div class="line"><span class="l">Tax (est.)</span><span class="v">$0.00</span></div>
|
||||
<div class="line total"><span class="l">Due today</span><span class="v">$19.00</span></div>
|
||||
<div class="guarantee">14-day free trial · you won't be charged until Jun 28</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SUCCESS -->
|
||||
<div class="view succ" data-view="success">
|
||||
<div class="ring"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<h1>You're <em>Pro.</em></h1>
|
||||
<p>Your hive just leveled up — <b>sync, the marketplace, and self-evolving skills</b> are live. Your trial runs 14 days; we'll remind you before the first charge.</p>
|
||||
<div class="rcpt">
|
||||
<div class="r"><span class="l">Plan</span><span class="v">Waggle Pro · Monthly</span></div>
|
||||
<div class="r"><span class="l">Trial ends</span><span class="v">Jun 28, 2026</span></div>
|
||||
<div class="r"><span class="l">Then</span><span class="v">$19.00 / month</span></div>
|
||||
<div class="r"><span class="l">Receipt</span><span class="v" style="color:var(--honey);cursor:pointer">Emailed →</span></div>
|
||||
</div>
|
||||
<div class="cta"><a class="go" data-go="home">Start using Pro →</a><a class="ghost" data-go="manage">Manage billing</a></div>
|
||||
</div>
|
||||
|
||||
<!-- MANAGE -->
|
||||
<div class="view mwrap" data-view="manage">
|
||||
<h1>Billing</h1>
|
||||
<div class="mcard cur">
|
||||
<div class="mtop"><div class="pl"><b>Waggle Pro</b><span>$19.00 / month · renews Jul 14, 2026</span></div><span class="badge">● Active</span></div>
|
||||
<div class="mrow"><span class="l">Billing cycle</span><span class="v">Monthly <span class="chg">Switch to annual (−20%)</span></span></div>
|
||||
<div class="mrow"><span class="l">Payment method</span><span class="v"><span class="card">VISA ···· 4242</span><span class="chg">Update</span></span></div>
|
||||
<div class="mrow"><span class="l">Next charge</span><span class="v">$19.00 on Jul 14</span></div>
|
||||
</div>
|
||||
<div class="mcard invoices">
|
||||
<div class="mtop" style="margin-bottom:8px"><div class="pl"><b style="font-size:14px">Invoices</b></div></div>
|
||||
<div class="iv"><span class="dt">Jun 14, 2026</span><span class="am">$19.00</span><span class="st">Paid</span><span class="dl">PDF ↓</span></div>
|
||||
<div class="iv"><span class="dt">May 14, 2026</span><span class="am">$19.00</span><span class="st">Paid</span><span class="dl">PDF ↓</span></div>
|
||||
<div class="iv"><span class="dt">Apr 14, 2026</span><span class="am">$19.00</span><span class="st">Paid</span><span class="dl">PDF ↓</span></div>
|
||||
</div>
|
||||
<div style="display:flex;gap:10px;margin-top:4px"><button class="btn ghost" data-go="plans">Change plan</button><button class="btn danger">Cancel subscription</button></div>
|
||||
<div class="powered" style="text-align:left;margin-top:18px">Subscription managed securely via <b>Stripe</b>.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
function go(v){
|
||||
if(v==='home'){ document.body.style.transition='opacity .25s'; document.body.style.opacity='.4'; setTimeout(()=>{ try{ location.href='../Waggle Reimagined.html#home'; }catch(e){} },260); return; }
|
||||
document.querySelectorAll('.view').forEach(el=>el.classList.toggle('on', el.dataset.view===v));
|
||||
document.querySelectorAll('#seg button').forEach(b=>b.classList.toggle('on', b.dataset.v===v));
|
||||
document.querySelector('.stage').scrollTop = 0;
|
||||
}
|
||||
document.getElementById('seg').addEventListener('click', e=>{ const b=e.target.closest('button'); if(b) go(b.dataset.v); });
|
||||
document.addEventListener('click', e=>{ const a=e.target.closest('[data-go]'); if(a){ go(a.dataset.go); } });
|
||||
|
||||
// billing cycle toggle
|
||||
document.getElementById('cycle').addEventListener('click', e=>{
|
||||
const b=e.target.closest('button'); if(!b) return;
|
||||
[...e.currentTarget.children].forEach(x=>x.classList.toggle('on',x===b));
|
||||
const c=b.dataset.c;
|
||||
document.querySelectorAll('.pp[data-mo]').forEach(el=>{ const small=el.querySelector('small').outerHTML; el.innerHTML = el.dataset[c] + (c==='yr'? '<small> / mo · billed yearly</small>' : '<small> / month</small>'); if(el.closest('.plan').querySelector('.pn').textContent==='Teams') el.innerHTML = el.dataset[c] + (c==='yr'?'<small> / seat · yearly</small>':'<small> / seat / mo</small>'); });
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
271
docs/design_handoff_waggle_app/design-files/screens/chat.html
Normal file
271
docs/design_handoff_waggle_app/design-files/screens/chat.html
Normal file
@@ -0,0 +1,271 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Chat</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
|
||||
/* ── Concept control bar ─────────────────────────────────────── */
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 13px; border-radius: 7px; transition: .14s; white-space: nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); }
|
||||
.controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
/* ── App frame ───────────────────────────────────────────────── */
|
||||
.app { flex: 1; min-height: 0; display: flex; }
|
||||
.pane { display: flex; flex-direction: column; min-height: 0; min-width: 0; flex: 1; }
|
||||
|
||||
/* context header */
|
||||
.ctx { flex: none; display: flex; align-items: center; gap: 14px; padding: 14px 26px; border-bottom: 1px solid var(--line-soft); }
|
||||
.ctx .wmark { width: 30px; height: 34px; flex: none; display: grid; place-items: center; font-weight: 800; font-size: 13px; color: #1a1407; background: linear-gradient(150deg, var(--honey-bright), var(--honey-deep)); }
|
||||
.ctx .wname { font-size: 15.5px; font-weight: 650; letter-spacing: -0.01em; }
|
||||
.ctx .wsub { font-size: 12px; color: var(--text-dim); font-family: var(--mono); }
|
||||
.ctx .right { margin-left: auto; display: flex; align-items: center; gap: 9px; }
|
||||
.modelpill { display: inline-flex; align-items: center; gap: 8px; padding: 7px 12px; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); font-size: 12.5px; color: var(--text-2); cursor: pointer; transition:.15s; }
|
||||
.modelpill:hover { border-color: var(--honey-line); }
|
||||
.modelpill .dot { background: var(--healthy); }
|
||||
.modelpill b { color: var(--text); font-weight: 600; }
|
||||
.iconbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-muted); cursor: pointer; display: grid; place-items: center; transition:.15s; }
|
||||
.iconbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
.iconbtn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.8; }
|
||||
|
||||
/* thread */
|
||||
.thread { flex: 1; min-height: 0; overflow: auto; padding: 30px 0; }
|
||||
.thread-inner { max-width: 760px; margin: 0 auto; padding: 0 26px; display: flex; flex-direction: column; gap: 26px; }
|
||||
|
||||
.msg { display: flex; gap: 14px; }
|
||||
.msg .av { width: 30px; height: 34px; flex: none; display: grid; place-items: center; font-size: 12px; font-weight: 700; }
|
||||
.msg.user .av { background: var(--surface-3); color: var(--text-2); border-radius: 9px; }
|
||||
.msg.bot .av { background: linear-gradient(150deg, var(--honey-bright), var(--honey-deep)); color: #1a1407; }
|
||||
.msg .body { flex: 1; min-width: 0; }
|
||||
.msg .who { font-size: 12px; color: var(--text-dim); margin-bottom: 7px; display: flex; align-items: center; gap: 8px; }
|
||||
.msg .who b { color: var(--text-2); font-weight: 600; }
|
||||
.msg.user .bubble { font-size: 15.5px; line-height: 1.55; color: var(--text); background: var(--surface); border: 1px solid var(--line-soft); padding: 13px 17px; border-radius: 4px 14px 14px 14px; }
|
||||
.msg.bot .prose { font-size: 15.5px; line-height: 1.62; color: var(--text); }
|
||||
.msg.bot .prose p { margin: 0 0 12px; }
|
||||
.msg.bot .prose ul { margin: 0 0 12px; padding-left: 4px; list-style: none; display: grid; gap: 8px; }
|
||||
.msg.bot .prose li { display: grid; grid-template-columns: 16px 1fr; gap: 9px; }
|
||||
.msg.bot .prose li .b { color: var(--honey); }
|
||||
.msg.bot .prose b { font-weight: 650; }
|
||||
|
||||
/* activity stream (the "magic", progressively disclosed) */
|
||||
.activity { margin: 0 0 16px; border: 1px solid var(--line-soft); border-radius: var(--r); overflow: hidden; background: var(--bg-2); }
|
||||
.activity .ahead { display: flex; align-items: center; gap: 10px; padding: 11px 15px; cursor: pointer; user-select: none; }
|
||||
.activity .ahead .spark { width: 18px; height: 18px; flex:none; }
|
||||
.activity .ahead .spark svg { width: 18px; height: 18px; stroke: var(--intel); fill: none; stroke-width: 1.8; }
|
||||
.activity .ahead .t { flex: 1; font-size: 13px; color: var(--text-2); font-weight: 550; }
|
||||
.activity .ahead .t b { color: var(--text); font-weight: 650; }
|
||||
.activity .ahead .chev { color: var(--text-dim); font-size: 12px; transition:.2s; }
|
||||
.activity.open .ahead .chev { transform: rotate(90deg); }
|
||||
.activity .steps { display: none; padding: 4px 15px 14px 15px; flex-direction: column; gap: 0; }
|
||||
.activity.open .steps { display: flex; }
|
||||
.astep { display: flex; align-items: flex-start; gap: 12px; padding: 9px 0; border-top: 1px solid var(--line-soft); position: relative; }
|
||||
.astep .sd { width: 9px; height: 9px; border-radius: 999px; margin-top: 5px; flex: none; }
|
||||
.astep .stext { flex: 1; font-size: 13px; color: var(--text-2); line-height: 1.5; }
|
||||
.astep .stext b { color: var(--text); font-weight: 600; }
|
||||
.astep .stext .mono { font-family: var(--mono); font-size: 11.5px; color: var(--text-dim); }
|
||||
.astep .prov { margin-top: 6px; display: inline-flex; align-items: center; gap: 7px; font-family: var(--mono); font-size: 10.5px; color: var(--intel); background: var(--intel-wash); border: 1px solid color-mix(in srgb, var(--intel) 28%, transparent); padding: 3px 9px; border-radius: 7px; }
|
||||
|
||||
/* approval card */
|
||||
.approval { border: 1px solid color-mix(in srgb, var(--attention) 35%, transparent); background: var(--honey-wash); border-radius: var(--r); padding: 16px 18px; }
|
||||
.approval .ah { display: flex; align-items: center; gap: 9px; font-size: 13.5px; font-weight: 650; }
|
||||
.approval .ah svg { width: 17px; height: 17px; stroke: var(--attention); fill: none; stroke-width: 1.8; }
|
||||
.approval p { margin: 8px 0 14px; font-size: 13px; color: var(--text-muted); line-height: 1.55; }
|
||||
.approval p .mono { font-family: var(--mono); color: var(--text-2); font-size: 12px; }
|
||||
.approval .actions { display: flex; gap: 10px; }
|
||||
.approval .btn { font-family: var(--sans); font-size: 13px; font-weight: 650; padding: 9px 16px; border-radius: 9px; cursor: pointer; border: 1px solid transparent; }
|
||||
.approval .btn.yes { background: var(--honey); color: #1a1407; }
|
||||
.approval .btn.no { background: transparent; border-color: var(--line-strong); color: var(--text-2); }
|
||||
|
||||
/* composer */
|
||||
.composer { flex: none; padding: 16px 26px 20px; border-top: 1px solid var(--line-soft); }
|
||||
.composer-inner { max-width: 760px; margin: 0 auto; }
|
||||
.cbox { border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-lg); padding: 6px 6px 6px 18px; display: flex; align-items: flex-end; gap: 10px; transition:.15s; }
|
||||
.cbox:focus-within { border-color: var(--honey-line); box-shadow: var(--honey-glow); }
|
||||
.cbox textarea { flex: 1; border: 0; background: transparent; resize: none; color: var(--text); font-family: var(--sans); font-size: 15px; line-height: 1.5; outline: none; padding: 9px 0; max-height: 90px; }
|
||||
.cbox textarea::placeholder { color: var(--text-dim); }
|
||||
.cbox .send { width: 40px; height: 40px; flex:none; border-radius: 11px; border: 0; background: var(--honey); color: #1a1407; font-size: 17px; cursor: pointer; display:grid; place-items:center; }
|
||||
.cbox .send:hover { background: var(--honey-bright); }
|
||||
.ctools { display: flex; align-items: center; gap: 8px; margin-top: 11px; padding: 0 2px; }
|
||||
.ctool { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); padding: 6px 10px; border-radius: 8px; border: 1px solid var(--line-soft); background: var(--bg-2); cursor: pointer; transition:.14s; }
|
||||
.ctool:hover { border-color: var(--honey-line); color: var(--text); }
|
||||
.ctool svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 1.9; }
|
||||
.ctools .spacer { flex: 1; }
|
||||
.ctools .hint { font-family: var(--mono); font-size: 11px; color: var(--text-dim); }
|
||||
|
||||
/* right rail / canvas (variation B) */
|
||||
.canvas { flex: none; width: 0; overflow: hidden; border-left: 1px solid var(--line-soft); background: var(--bg-2); display: flex; flex-direction: column; transition: width .25s ease; }
|
||||
body.split .thread-inner { max-width: 620px; }
|
||||
.canvas-head { flex: none; display: flex; align-items: center; gap: 10px; padding: 14px 20px; border-bottom: 1px solid var(--line-soft); }
|
||||
.canvas-head .ct { font-size: 13.5px; font-weight: 650; flex: 1; }
|
||||
.canvas-head .ctag { font-family: var(--mono); font-size: 10.5px; color: var(--healthy); background: var(--healthy-wash); padding: 4px 9px; border-radius: 7px; }
|
||||
.canvas-body { flex: 1; min-height: 0; overflow: auto; padding: 24px 26px; }
|
||||
.doc h1 { font-size: 22px; font-weight: 650; letter-spacing: -0.02em; margin: 0 0 6px; }
|
||||
.doc .docsub { font-size: 12.5px; color: var(--text-dim); font-family: var(--mono); margin-bottom: 20px; }
|
||||
.doc h2 { font-size: 13px; font-family: var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--honey); margin: 22px 0 10px; }
|
||||
.doc p { font-size: 14px; line-height: 1.62; color: var(--text-2); margin: 0 0 12px; }
|
||||
.doc table { width: 100%; border-collapse: collapse; font-size: 12.5px; margin: 6px 0 12px; }
|
||||
.doc th, .doc td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line-soft); }
|
||||
.doc th { color: var(--text-dim); font-weight: 600; font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; }
|
||||
.doc td { color: var(--text-2); }
|
||||
.doc td b { color: var(--text); }
|
||||
.typing-cursor::after { content:'▍'; color: var(--honey); animation: bl 1s steps(2) infinite; }
|
||||
@keyframes bl { 50% { opacity: 0; } }
|
||||
|
||||
.hidden { display: none !important; }
|
||||
@media (max-width: 820px) { body.split .canvas { display: none; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="split">
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Chat · variation</span>
|
||||
<div class="seg" id="seg">
|
||||
<button data-v="a">A · Conversation-first</button>
|
||||
<button class="on" data-v="b">B · Split work canvas</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Split work canvas</b> — chat builds the artifact live</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="app">
|
||||
<!-- ── chat pane ── -->
|
||||
<div class="pane">
|
||||
<div class="ctx">
|
||||
<div class="wmark hex">C</div>
|
||||
<div>
|
||||
<div class="wname">Competitive Intelligence</div>
|
||||
<div class="wsub">workspace · 142 memories · 9 sources</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button class="modelpill" title="Waggle picked the model — click to override"><span class="dot dot-live"></span>Model: <b>auto</b> · Claude Sonnet</button>
|
||||
<button class="iconbtn" title="Memory & context"><svg viewBox="0 0 24 24"><path d="M12 5a3 3 0 0 0-3 3 3 3 0 0 0-3 3 3 3 0 0 0 1 5 3 3 0 0 0 5 1 3 3 0 0 0 5-1 3 3 0 0 0 1-5 3 3 0 0 0-3-3 3 3 0 0 0-3-3Z"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="thread"><div class="thread-inner" id="thread"></div></div>
|
||||
|
||||
<div class="composer">
|
||||
<div class="composer-inner">
|
||||
<div class="cbox">
|
||||
<textarea rows="1" placeholder="Reply, or ask Waggle to take the next step…"></textarea>
|
||||
<button class="send">↑</button>
|
||||
</div>
|
||||
<div class="ctools">
|
||||
<button class="ctool"><svg viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg>Attach</button>
|
||||
<button class="ctool"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>Persona: Analyst</button>
|
||||
<button class="ctool"><svg viewBox="0 0 24 24"><path d="M3 12h18M12 3v18"/></svg>Tools</button>
|
||||
<span class="spacer"></span>
|
||||
<span class="hint">⏎ send · ⌘K commands</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── work canvas (variation B) ── -->
|
||||
<aside class="canvas" id="canvas" style="width:42%">
|
||||
<div class="canvas-head">
|
||||
<span class="ct">teardown.md</span>
|
||||
<span class="ctag">● live draft</span>
|
||||
<button class="iconbtn" title="Open in Artifacts"><svg viewBox="0 0 24 24"><path d="M14 3h7v7M21 3l-9 9M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"/></svg></button>
|
||||
</div>
|
||||
<div class="canvas-body">
|
||||
<div class="doc">
|
||||
<h1>Q2 Competitive Teardown</h1>
|
||||
<div class="docsub">draft · 9 competitors · updated just now by Waggle</div>
|
||||
<h2>Executive summary</h2>
|
||||
<p>The category is consolidating around persistent-memory positioning, but only two rivals ship local-first. The clearest opening is <b>regulated industries</b>, where cloud-only memory is a non-starter and no incumbent has a credible audit story.</p>
|
||||
<h2>Landscape</h2>
|
||||
<table>
|
||||
<tr><th>Player</th><th>Memory</th><th>Local-first</th></tr>
|
||||
<tr><td><b>Mem0</b></td><td>Cloud</td><td>No</td></tr>
|
||||
<tr><td><b>Letta</b></td><td>Agent-centric</td><td>Partial</td></tr>
|
||||
<tr><td><b>LangMem</b></td><td>Toy-tier</td><td>No</td></tr>
|
||||
<tr><td><b>Notion AI</b></td><td>Doc-scoped</td><td>No</td></tr>
|
||||
</table>
|
||||
<p>Pricing pulled from public pages; full table in the appendix. <span class="typing-cursor"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* theme */
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const SPARK = '<svg viewBox="0 0 24 24"><path d="M12 3v3M12 18v3M5 12H2M22 12h-3M6 6 4 4M20 20l-2-2M6 18l-2 2M20 4l-2 2"/><circle cx="12" cy="12" r="3.5"/></svg>';
|
||||
const WARN = '<svg viewBox="0 0 24 24"><path d="M12 9v4m0 4h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg>';
|
||||
|
||||
const thread = [
|
||||
{ role:'user', who:'You', text:'Pull together a competitive teardown for Q2 — who else is selling persistent memory, and where\u2019s our opening?' },
|
||||
{ role:'bot', who:'Waggle', activity:{
|
||||
title:'Worked across <b>memory, web & files</b>', sub:'6 steps · 38s',
|
||||
steps:[
|
||||
{ c:'var(--intel)', t:'Recalled <b>6 memories</b> from your hive — last quarter\u2019s landscape, the Mem0 teardown, your pricing notes.', prov:'mem://hive · provenance kept' },
|
||||
{ c:'var(--step-web,#06b6d4)', t:'Searched <b>9 competitor sites</b> for pricing & positioning <span class="mono">(mem0, letta, langmem, notion…)</span>' },
|
||||
{ c:'var(--work)', t:'Compared against your <b>last teardown</b> — flagged 3 changes since March.' },
|
||||
{ c:'var(--healthy)', t:'Drafted <b>teardown.md</b> — exec summary, landscape table, opening.' },
|
||||
]
|
||||
},
|
||||
prose:`<p>Done. Here\u2019s the shape of it — full draft is in <b>teardown.md</b>.</p>
|
||||
<ul>
|
||||
<li><span class="b">◆</span><div>The field is consolidating on <b>persistent-memory</b> messaging, but only two rivals ship <b>local-first</b>.</div></li>
|
||||
<li><span class="b">◆</span><div>Your clearest opening is <b>regulated industries</b> — cloud-only memory is a non-starter there, and nobody has a credible audit story.</div></li>
|
||||
<li><span class="b">◆</span><div>Mem0 raised prices ~15% since March; I noted it against your pricing model.</div></li>
|
||||
</ul>
|
||||
<p>Want me to turn the opening into a one-page brief for the board, or export the table to the pricing sheet?</p>`
|
||||
},
|
||||
{ role:'user', who:'You', text:'Export the table to the Q2 pricing sheet in Salesforce.' },
|
||||
{ role:'bot', who:'Waggle', approval:{
|
||||
title:'Approve before I leave your machine',
|
||||
body:'This step writes to an external system — <span class="mono">Salesforce › Q2 Pricing</span>. Everything else stayed local. I\u2019ll export the 9-row table and nothing else.',
|
||||
} },
|
||||
];
|
||||
|
||||
function renderThread(){
|
||||
const el = document.getElementById('thread');
|
||||
el.innerHTML = thread.map((m,i) => {
|
||||
if(m.role==='user') return `<div class="msg user"><div class="av">You</div><div class="body"><div class="who"><b>You</b></div><div class="bubble">${m.text}</div></div></div>`;
|
||||
let inner = '';
|
||||
if(m.activity){
|
||||
inner += `<div class="activity ${i===1?'open':''}"><div class="ahead"><span class="spark">${SPARK}</span><span class="t">${m.activity.title} <span style="color:var(--text-dim)">· ${m.activity.sub}</span></span><span class="chev">▸</span></div><div class="steps">${
|
||||
m.activity.steps.map(s=>`<div class="astep"><span class="sd" style="background:${s.c}"></span><span class="stext">${s.t}${s.prov?`<div class="prov">⬡ ${s.prov}</div>`:''}</span></div>`).join('')
|
||||
}</div></div>`;
|
||||
}
|
||||
if(m.prose) inner += `<div class="prose">${m.prose}</div>`;
|
||||
if(m.approval) inner += `<div class="approval"><div class="ah">${WARN} ${m.approval.title}</div><p>${m.approval.body}</p><div class="actions"><button class="btn yes">Approve & export</button><button class="btn no">Not now</button></div></div>`;
|
||||
return `<div class="msg bot"><div class="av hex">W</div><div class="body"><div class="who"><b>Waggle</b> · Analyst · Claude Sonnet</div>${inner}</div></div>`;
|
||||
}).join('');
|
||||
el.querySelectorAll('.activity .ahead').forEach(h => h.onclick = () => h.parentElement.classList.toggle('open'));
|
||||
}
|
||||
renderThread();
|
||||
|
||||
/* variation switch */
|
||||
const labels = { a:'<b>Conversation-first</b> — the work reveals inline', b:'<b>Split work canvas</b> — chat builds the artifact live' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e => {
|
||||
const b = e.target.closest('button'); if(!b) return;
|
||||
[...seg.children].forEach(x=>x.classList.toggle('on', x===b));
|
||||
const split = b.dataset.v==='b';
|
||||
document.body.classList.toggle('split', split);
|
||||
document.getElementById('canvas').style.width = split ? '42%' : '0px';
|
||||
document.getElementById('vlabel').innerHTML = labels[b.dataset.v];
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,202 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Skill evolution</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 13px; border-radius: 7px; transition: .14s; white-space: nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); } .controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
.stage { flex: 1; min-height: 0; overflow: auto; }
|
||||
.wrap { max-width: 920px; margin: 0 auto; padding: 30px 32px 70px; }
|
||||
|
||||
.head { margin-bottom: 24px; }
|
||||
.head .eyebrow { font-family: var(--mono); font-size: 11px; letter-spacing:.14em; text-transform:uppercase; color: var(--honey); margin-bottom: 12px; display:flex; align-items:center; gap:9px; }
|
||||
.head .eyebrow::before { content:''; width:20px; height:1px; background: var(--honey-line); }
|
||||
.head h1 { font-size: 28px; font-weight: 650; letter-spacing:-0.02em; margin: 0 0 10px; }
|
||||
.head h1 em { font-style:normal; color: var(--honey); }
|
||||
.head p { font-size: 15px; color: var(--text-muted); line-height:1.55; margin:0; max-width: 64ch; } .head p b { color: var(--text-2); }
|
||||
|
||||
/* skill card + version ladder */
|
||||
.skill { border:1px solid var(--line); border-radius: var(--r-xl); background: var(--surface); overflow:hidden; }
|
||||
.skill .sh { display:flex; align-items:center; gap:13px; padding: 18px 22px; border-bottom: 1px solid var(--line-soft); }
|
||||
.skill .si { width:38px; height:42px; flex:none; display:grid; place-items:center; color:#1a1407; background: linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.skill .si svg { width:19px; height:19px; stroke:#1a1407; fill:none; stroke-width:2; }
|
||||
.skill .sh b { font-size: 16px; font-weight: 650; } .skill .sh .sub { font-size: 12px; color: var(--text-muted); }
|
||||
.skill .sh .now { margin-left:auto; text-align:right; } .skill .sh .now .v { font-size: 24px; font-weight: 750; color: var(--honey); letter-spacing:-0.02em; } .skill .sh .now .l { font-size:10.5px; color:var(--text-dim); font-family:var(--mono); }
|
||||
|
||||
.ladder { padding: 8px 22px 18px; }
|
||||
.ver { display:grid; grid-template-columns: 64px 1fr; gap: 16px; padding: 16px 0; border-bottom: 1px solid var(--line-soft); position:relative; }
|
||||
.ver:last-child { border-bottom:0; }
|
||||
.ver .vtag { font-family: var(--mono); font-size: 12px; font-weight:600; color: var(--text-2); padding-top: 2px; }
|
||||
.ver.best .vtag { color: var(--honey); }
|
||||
.ver .vbody b { font-size: 14px; font-weight:600; }
|
||||
.ver .vbody p { margin: 4px 0 10px; font-size: 13px; color: var(--text-muted); line-height:1.5; }
|
||||
.ver .scorebar { display:flex; align-items:center; gap: 12px; }
|
||||
.ver .track { flex:1; max-width: 360px; height: 10px; border-radius: 6px; background: var(--surface-2); border:1px solid var(--line-soft); overflow:hidden; }
|
||||
.ver .fill { height:100%; border-radius:6px; background: var(--surface-3); }
|
||||
.ver.best .fill { background: linear-gradient(90deg, var(--honey-deep), var(--honey-bright)); }
|
||||
.ver .sc { font-family: var(--mono); font-size: 12.5px; font-weight:600; color: var(--text-2); } .ver.best .sc { color: var(--honey); }
|
||||
.ver .prov { margin-top: 8px; display:inline-flex; align-items:center; gap:7px; font-family:var(--mono); font-size:10.5px; color: var(--intel); }
|
||||
|
||||
.howline { margin-top: 18px; display:flex; gap:12px; align-items:flex-start; padding: 15px 17px; border-radius: var(--r-lg); background: var(--bg-2); border:1px solid var(--line-soft); }
|
||||
.howline svg { width:18px;height:18px;stroke:var(--honey);fill:none;stroke-width:1.8;flex:none;margin-top:1px; }
|
||||
.howline p { margin:0; font-size:13px; color:var(--text-muted); line-height:1.6; } .howline b { color: var(--text); }
|
||||
|
||||
/* diffusion */
|
||||
.diffuse { display:grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
.dcard { border:1px solid var(--line-soft); border-radius: var(--r-xl); background: var(--surface); padding: 20px; }
|
||||
.dcard .dh { display:flex; align-items:center; gap:10px; margin-bottom:10px; flex-wrap:wrap; } .dcard .dh b { font-size: 15px; font-weight:650; white-space:nowrap; }
|
||||
.dcard .dh .tag { font-family:var(--mono); font-size:9.5px; letter-spacing:.1em; text-transform:uppercase; padding:3px 8px; border-radius:6px; }
|
||||
.dcard.skill-d .dh .tag { color: var(--intel); background: var(--intel-wash); }
|
||||
.dcard.tool-d .dh .tag { color: var(--healthy); background: var(--healthy-wash); }
|
||||
.dcard .dsub { font-size: 12.5px; color: var(--text-muted); margin-bottom: 18px; }
|
||||
.map { position: relative; height: 200px; margin-bottom: 16px; }
|
||||
.map svg { position:absolute; inset:0; width:100%; height:100%; }
|
||||
.ml { stroke: var(--line-strong); stroke-width:1.3; fill:none; } .mlf { stroke-width:2.2; fill:none; stroke-linecap:round; stroke-dasharray:4 120; animation: mf 3s linear infinite; }
|
||||
.dcard.skill-d .mlf { stroke: var(--intel); } .dcard.tool-d .mlf { stroke: var(--healthy); }
|
||||
@keyframes mf { to { stroke-dashoffset:-124; } }
|
||||
.node { position:absolute; transform:translate(-50%,-50%); text-align:center; }
|
||||
.node .nb { padding: 7px 11px; border-radius: 9px; background: var(--surface-2); border:1px solid var(--line-strong); font-size:11.5px; font-weight:600; white-space:nowrap; }
|
||||
.node.origin .nb { color:#1a1407; background: linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); border-color:transparent; }
|
||||
.node .nt { font-family:var(--mono); font-size:8.5px; letter-spacing:.08em; text-transform:uppercase; color: var(--text-dim); margin-bottom:3px; }
|
||||
.signal { display:inline-flex; align-items:center; gap:7px; font-family:var(--mono); font-size:10.5px; padding:4px 9px; border-radius:7px; margin-bottom:12px; }
|
||||
.dcard.skill-d .signal { color:var(--intel); background:var(--intel-wash); } .dcard.tool-d .signal { color:var(--healthy); background:var(--healthy-wash); }
|
||||
.adopt { font-size: 13px; color: var(--text-2); } .adopt b { color: var(--text); font-weight:650; }
|
||||
|
||||
.hidden { display:none !important; }
|
||||
@media (max-width: 800px){ .diffuse { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Moat · variation</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="a">A · Skills that improve</button>
|
||||
<button data-v="b">B · Skills & tools that spread</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Evolution</b> — skills get better on their own</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<!-- ════ A — EVOLUTION ════ -->
|
||||
<div class="wrap view" id="view-a">
|
||||
<div class="head">
|
||||
<div class="eyebrow">The moat · self-improvement</div>
|
||||
<h1>Your skills get <em>better on their own.</em></h1>
|
||||
<p>A skill isn't static. Waggle re-optimizes it against real outcomes — and keeps every version, with the score that earned it. <b>You don't tune prompts; the hive does.</b></p>
|
||||
</div>
|
||||
|
||||
<div class="skill">
|
||||
<div class="sh">
|
||||
<div class="si hex"><svg viewBox="0 0 24 24"><path d="m12 3 2.5 6 6.5.5-5 4.2 1.6 6.3L12 16.8 6 20l1.6-6.3-5-4.2 6.5-.5z"/></svg></div>
|
||||
<div><b>Competitor teardown</b><div class="sub">skill · distilled by Research-synth · used in 4 workspaces</div></div>
|
||||
<div class="now"><div class="v">91%</div><div class="l">current quality</div></div>
|
||||
</div>
|
||||
<div class="ladder" id="ladder"></div>
|
||||
</div>
|
||||
|
||||
<div class="howline">
|
||||
<svg viewBox="0 0 24 24"><path d="M3 17l5-5 4 4 8-8M14 4h6v6"/></svg>
|
||||
<p><b>How it improves:</b> when a skill runs, Waggle's optimizer (GEPA) proposes refinements and a three-model judge scores them against held-out tasks. A new version only ships if it actually wins — and the old ones stay, so you can always roll back. Nothing about your data leaves the machine.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════ B — DIFFUSION ════ -->
|
||||
<div class="wrap view hidden" id="view-b">
|
||||
<div class="head">
|
||||
<div class="eyebrow">The moat · the waggle dance</div>
|
||||
<h1>And they <em>spread across the hive.</em></h1>
|
||||
<p>When one agent learns something good, the others don't start from zero. Skills and tool setups diffuse across your agents and workspaces — the way bees share a find. <b>The whole hive compounds.</b></p>
|
||||
</div>
|
||||
|
||||
<div class="diffuse">
|
||||
<div class="dcard skill-d">
|
||||
<div class="dh"><b>Competitor teardown</b><span class="tag">skill</span></div>
|
||||
<div class="dsub">Learned in Competitive Intelligence — now adopted elsewhere.</div>
|
||||
<div class="map">
|
||||
<svg viewBox="0 0 380 200" preserveAspectRatio="none">
|
||||
<line class="ml" x1="80" y1="100" x2="300" y2="40"></line><line class="ml" x1="80" y1="100" x2="300" y2="100"></line><line class="ml" x1="80" y1="100" x2="300" y2="160"></line>
|
||||
<line class="mlf" x1="80" y1="100" x2="300" y2="40"></line><line class="mlf" x1="80" y1="100" x2="300" y2="100" style="animation-delay:1s"></line><line class="mlf" x1="80" y1="100" x2="300" y2="160" style="animation-delay:2s"></line>
|
||||
</svg>
|
||||
<div class="node origin" style="left:21%;top:50%"><div class="nt">origin</div><div class="nb">Research-synth</div></div>
|
||||
<div class="node" style="left:79%;top:20%"><div class="nt">agent</div><div class="nb">Deck-builder</div></div>
|
||||
<div class="node" style="left:79%;top:50%"><div class="nt">workspace</div><div class="nb">Customer Research</div></div>
|
||||
<div class="node" style="left:79%;top:80%"><div class="nt">teammate</div><div class="nb">Jonas (Team)</div></div>
|
||||
</div>
|
||||
<span class="signal">⬡ waggle-dance · skill_share</span>
|
||||
<div class="adopt"><b>3 adopters</b> · 2 agents, 1 teammate — each got the v3 quality instantly.</div>
|
||||
</div>
|
||||
|
||||
<div class="dcard tool-d">
|
||||
<div class="dh"><b>Salesforce export recipe</b><span class="tag">tool setup</span></div>
|
||||
<div class="dsub">A working connector config, shared so others skip the setup.</div>
|
||||
<div class="map">
|
||||
<svg viewBox="0 0 380 200" preserveAspectRatio="none">
|
||||
<line class="ml" x1="80" y1="100" x2="300" y2="55"></line><line class="ml" x1="80" y1="100" x2="300" y2="145"></line>
|
||||
<line class="mlf" x1="80" y1="100" x2="300" y2="55"></line><line class="mlf" x1="80" y1="100" x2="300" y2="145" style="animation-delay:1.3s"></line>
|
||||
</svg>
|
||||
<div class="node origin" style="left:21%;top:50%"><div class="nt">origin</div><div class="nb">Q2 Board Deck</div></div>
|
||||
<div class="node" style="left:79%;top:28%"><div class="nt">workspace</div><div class="nb">Pricing Model</div></div>
|
||||
<div class="node" style="left:79%;top:72%"><div class="nt">workspace</div><div class="nb">Marketing Site</div></div>
|
||||
</div>
|
||||
<span class="signal">⬡ waggle-dance · model_recipe</span>
|
||||
<div class="adopt"><b>2 workspaces</b> reused the auth + field mapping — no re-setup, secrets stayed in each vault.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="howline">
|
||||
<svg viewBox="0 0 24 24"><circle cx="6" cy="7" r="2.4"/><circle cx="18" cy="7" r="2.4"/><circle cx="12" cy="17" r="2.4"/><path d="M8 8l3 7M16 8l-3 7"/></svg>
|
||||
<p><b>You stay in control:</b> diffusion is suggest-then-adopt — Waggle proposes a shared skill or recipe, you accept it per workspace. Personal stays personal; only what you share moves. On Teams, this is how a whole org levels up at once.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const vers = [
|
||||
{ v:'v1', t:'First draft', p:'Distilled from a manual teardown you did in March.', score:71 },
|
||||
{ v:'v2', t:'+ pricing-pull lane', p:'Optimizer added a step to fetch live pricing pages; judges confirmed a real lift.', score:84 },
|
||||
{ v:'v3', t:'+ source citations & recency check', p:'Every claim now carries a source and a freshness flag. Current best.', score:91, best:true, prov:'GEPA optimized · judged by 3 models' },
|
||||
];
|
||||
document.getElementById('ladder').innerHTML = vers.map(v=>`
|
||||
<div class="ver ${v.best?'best':''}">
|
||||
<div class="vtag">${v.v}</div>
|
||||
<div class="vbody">
|
||||
<b>${v.t}</b><p>${v.p}</p>
|
||||
<div class="scorebar"><div class="track"><div class="fill" style="width:${v.score}%"></div></div><span class="sc">${v.score}% quality</span></div>
|
||||
${v.prov?`<div class="prov">⬡ ${v.prov}</div>`:''}
|
||||
</div>
|
||||
</div>`).join('');
|
||||
|
||||
const labels = { a:'<b>Evolution</b> — skills get better on their own', b:'<b>Diffusion</b> — skills & tools spread across the hive' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e => {
|
||||
const b = e.target.closest('button'); if(!b) return;
|
||||
[...seg.children].forEach(x=>x.classList.toggle('on', x===b));
|
||||
document.getElementById('view-a').classList.toggle('hidden', b.dataset.v!=='a');
|
||||
document.getElementById('view-b').classList.toggle('hidden', b.dataset.v!=='b');
|
||||
document.getElementById('vlabel').innerHTML = labels[b.dataset.v];
|
||||
document.querySelector('.stage').scrollTop = 0;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
146
docs/design_handoff_waggle_app/design-files/screens/habit.html
Normal file
146
docs/design_handoff_waggle_app/design-files/screens/habit.html
Normal file
@@ -0,0 +1,146 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Momentum</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display:flex; flex-direction:column; }
|
||||
.controls { flex:none; display:flex; align-items:center; gap:14px; padding:11px 20px; border-bottom:1px solid var(--line-soft); background:color-mix(in srgb,var(--bg) 88%,transparent); backdrop-filter:blur(10px); z-index:30; }
|
||||
.controls .lab { font-family:var(--mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); }
|
||||
.controls .vlabel { font-size:12.5px; color:var(--text-muted); } .controls .vlabel b { color:var(--text); font-weight:600; }
|
||||
.controls .right { margin-left:auto; display:flex; align-items:center; gap:10px; }
|
||||
.tbtn { width:34px; height:34px; border-radius:9px; border:1px solid var(--line); background:var(--surface); color:var(--text-2); cursor:pointer; }
|
||||
.tbtn:hover { border-color:var(--honey-line); color:var(--honey); }
|
||||
.stage { flex:1; min-height:0; overflow:auto; position:relative; }
|
||||
.stage .comb { position:absolute; inset:0; opacity:.4; pointer-events:none; }
|
||||
.wrap { max-width:920px; margin:0 auto; padding:30px 32px 70px; position:relative; }
|
||||
|
||||
.head { margin-bottom:26px; }
|
||||
.head .eyebrow { font-family:var(--mono); font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--honey); margin-bottom:12px; display:flex; align-items:center; gap:9px; }
|
||||
.head .eyebrow::before { content:''; width:20px; height:1px; background:var(--honey-line); }
|
||||
.head h1 { font-size:28px; font-weight:650; letter-spacing:-0.02em; margin:0 0 10px; } .head h1 em { font-style:normal; color:var(--honey); }
|
||||
.head p { font-size:15px; color:var(--text-muted); line-height:1.55; margin:0; max-width:62ch; } .head p b { color:var(--text-2); }
|
||||
|
||||
/* loop diagram */
|
||||
.loop { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:14px; }
|
||||
.lstep { padding:18px; border-radius:var(--r-lg); background:var(--surface); border:1px solid var(--line-soft); position:relative; }
|
||||
.lstep .ln { font-family:var(--mono); font-size:11px; color:var(--honey); letter-spacing:.1em; margin-bottom:10px; }
|
||||
.lstep .li { width:34px; height:38px; display:grid; place-items:center; margin-bottom:11px; }
|
||||
.lstep .li svg { width:18px; height:18px; stroke:var(--honey); fill:none; stroke-width:1.7; }
|
||||
.lstep b { font-size:14.5px; font-weight:650; display:block; margin-bottom:6px; letter-spacing:-0.01em; }
|
||||
.lstep p { margin:0; font-size:12.5px; color:var(--text-muted); line-height:1.5; }
|
||||
.lstep .arr { position:absolute; right:-9px; top:50%; transform:translateY(-50%); color:var(--honey); font-size:15px; z-index:2; }
|
||||
.lstep:last-child .arr { content:'↻'; }
|
||||
|
||||
.grp { font-family:var(--mono); font-size:11px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); margin:30px 0 16px; display:flex; align-items:center; gap:10px; } .grp::after { content:''; flex:1; height:1px; background:var(--line-soft); }
|
||||
|
||||
/* mechanics grid */
|
||||
.mechs { display:grid; grid-template-columns:repeat(2,1fr); gap:14px; }
|
||||
.mech { padding:22px; border-radius:var(--r-lg); background:var(--surface); border:1px solid var(--line-soft); }
|
||||
.mech.live { border-color:var(--honey-line); background:linear-gradient(155deg,var(--surface-2),var(--surface)); }
|
||||
.mech h3 { font-size:16px; font-weight:650; letter-spacing:-0.01em; margin:0 0 8px; display:flex; align-items:center; gap:10px; }
|
||||
.mech p { margin:0 0 14px; font-size:13px; color:var(--text-muted); line-height:1.55; } .mech p b { color:var(--text); }
|
||||
/* streak demo */
|
||||
.streak { display:flex; align-items:center; gap:7px; }
|
||||
.streak .d { width:30px; height:34px; display:grid; place-items:center; font-family:var(--mono); font-size:11px; font-weight:600; color:var(--text-dim); background:var(--bg-2); border:1px solid var(--line-soft); }
|
||||
.streak .d.on { color:#1a1407; background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); border-color:transparent; }
|
||||
.streak .d.today { box-shadow:0 0 0 2px var(--honey-line); }
|
||||
.streakcount { margin-left:6px; font-size:13px; font-weight:650; color:var(--honey); }
|
||||
/* counter demo */
|
||||
.counter { display:flex; align-items:baseline; gap:10px; }
|
||||
.counter .big { font-size:40px; font-weight:750; letter-spacing:-0.03em; color:var(--honey); }
|
||||
.counter .lab { font-size:13px; color:var(--text-muted); }
|
||||
.grow { font-size:12px; color:var(--healthy); margin-top:6px; } .grow b { color:var(--healthy); }
|
||||
/* reward demo */
|
||||
.reward { display:grid; gap:8px; }
|
||||
.rchip { display:flex; align-items:center; gap:10px; padding:9px 12px; border-radius:9px; background:var(--bg-2); border:1px solid var(--line-soft); font-size:12.5px; color:var(--text-2); }
|
||||
.rchip .dot { width:7px; height:7px; }
|
||||
.rchip b { color:var(--text); font-weight:600; }
|
||||
/* nudge demo */
|
||||
.nudge { display:flex; gap:11px; align-items:flex-start; padding:12px 14px; border-radius:10px; background:var(--honey-wash); border:1px solid var(--honey-line); }
|
||||
.nudge svg { width:16px; height:16px; stroke:var(--honey); fill:none; stroke-width:1.8; flex:none; margin-top:1px; }
|
||||
.nudge p { margin:0; font-size:12.5px; color:var(--text-2); line-height:1.5; } .nudge b { color:var(--text); }
|
||||
|
||||
/* ethics note */
|
||||
.ethics { margin-top:26px; display:flex; gap:13px; align-items:flex-start; padding:18px 20px; border-radius:var(--r-lg); background:var(--bg-2); border:1px solid var(--line-soft); }
|
||||
.ethics svg { width:20px; height:20px; stroke:var(--healthy); fill:none; stroke-width:1.8; flex:none; margin-top:1px; }
|
||||
.ethics p { margin:0; font-size:13.5px; color:var(--text-muted); line-height:1.6; } .ethics b { color:var(--text); }
|
||||
@media (max-width:820px){ .loop { grid-template-columns:1fr 1fr; } .mechs { grid-template-columns:1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Engagement · the habit loop</span>
|
||||
<span class="vlabel" id="vlabel">Momentum, not manipulation — why people come back daily</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<div class="comb"></div>
|
||||
<div class="wrap">
|
||||
<div class="head">
|
||||
<div class="eyebrow">What makes it stick</div>
|
||||
<h1>The hive pulls you <em>back.</em></h1>
|
||||
<p>Waggle gets more valuable every day you use it — and it makes that obvious. Four honest loops turn it into a daily habit: <b>a reason to open it, a reward when you do, a visible streak, and a hive you've invested in too much to leave.</b></p>
|
||||
</div>
|
||||
|
||||
<div class="loop">
|
||||
<div class="lstep"><span class="arr">→</span><div class="li hex" style="background:var(--honey-wash)"><svg viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 0 1-3.4 0"/></svg></div><div class="ln">01 · Trigger</div><b>A reason to open it</b><p>“3 things ran overnight.” A morning nudge that's always worth the tap.</p></div>
|
||||
<div class="lstep"><span class="arr">→</span><div class="li hex" style="background:var(--honey-wash)"><svg viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></svg></div><div class="ln">02 · Action</div><b>One easy move</b><p>Continue a workspace, approve a result, ask one thing. Friction near zero.</p></div>
|
||||
<div class="lstep"><span class="arr">→</span><div class="li hex" style="background:var(--honey-wash)"><svg viewBox="0 0 24 24"><path d="m12 3 2.5 6 6.5.5-5 4.2 1.6 6.3L12 16.8 6 20l1.6-6.3-5-4.2 6.5-.5z"/></svg></div><div class="ln">03 · Reward</div><b>Variable payoff</b><p>You never know exactly what Waggle finished — sometimes it's a small win, sometimes a big one.</p></div>
|
||||
<div class="lstep"><span class="arr" style="font-size:18px">↺</span><div class="li hex" style="background:var(--honey-wash)"><svg viewBox="0 0 24 24"><path d="M12 5a3 3 0 0 0-3 3 3 3 0 0 0-3 3 3 3 0 0 0 1 5 3 3 0 0 0 5 1 3 3 0 0 0 5-1 3 3 0 0 0 1-5 3 3 0 0 0-3-3 3 3 0 0 0-3-3Z"/></svg></div><div class="ln">04 · Investment</div><b>The hive grows</b><p>Every session adds memory and sharpens skills — so tomorrow is better than today.</p></div>
|
||||
</div>
|
||||
|
||||
<div class="grp">The four mechanics, made concrete</div>
|
||||
<div class="mechs">
|
||||
<div class="mech live">
|
||||
<h3>🔥 Daily streak</h3>
|
||||
<p>A gentle streak rewards showing up. Not a guilt trip — a quiet <b>“you're on a roll.”</b></p>
|
||||
<div class="streak">
|
||||
<div class="d on">M</div><div class="d on">T</div><div class="d on">W</div><div class="d on">T</div><div class="d on today">F</div><div class="d">S</div><div class="d">S</div>
|
||||
<span class="streakcount">12-day streak</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mech live">
|
||||
<h3>📈 Compounding memory</h3>
|
||||
<p>The counter that makes leaving unthinkable. The more it knows, the more <b>it's yours</b>.</p>
|
||||
<div class="counter"><span class="big">142</span><span class="lab">memories in this hive</span></div>
|
||||
<div class="grow">▲ <b>+14 this week</b> · your hive is in the top 8% by depth</div>
|
||||
</div>
|
||||
<div class="mech">
|
||||
<h3>✨ While you were away</h3>
|
||||
<p>The variable reward. Overnight work means opening Waggle is <b>always worth it</b>.</p>
|
||||
<div class="reward">
|
||||
<div class="rchip"><span class="dot" style="background:var(--healthy)"></span><b>Teardown</b> finished · 9 competitors</div>
|
||||
<div class="rchip"><span class="dot" style="background:var(--intel)"></span><b>14 memories</b> consolidated</div>
|
||||
<div class="rchip"><span class="dot" style="background:var(--work)"></span><b>2 artifacts</b> waiting for you</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mech">
|
||||
<h3>🛎️ Gentle nudges</h3>
|
||||
<p>Earned attention, never spam. A nudge only when there's <b>something real</b> to act on.</p>
|
||||
<div class="nudge"><svg viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 0 1-3.4 0"/></svg><p><b>Good morning, Mara.</b> Your competitor teardown is ready, and 3 memories need a quick confirm.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ethics">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/><path d="M9 12l2 2 4-4"/></svg>
|
||||
<p><b>Momentum, not manipulation.</b> Every loop here is anchored to <b>real value the user wants</b> — work that actually got done, memory that actually compounds. No dark patterns, no fake urgency, no infinite feed. The habit forms because the product genuinely gets better the more you use it; we just make that legible. Streaks and nudges can always be turned off.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
339
docs/design_handoff_waggle_app/design-files/screens/home.html
Normal file
339
docs/design_handoff_waggle_app/design-files/screens/home.html
Normal file
@@ -0,0 +1,339 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Home</title>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
|
||||
/* ── Screen control bar (concept affordance, not product chrome) ── */
|
||||
.controls {
|
||||
flex: none; display: flex; align-items: center; gap: 14px;
|
||||
padding: 12px 22px; border-bottom: 1px solid var(--line-soft);
|
||||
background: color-mix(in srgb, var(--bg) 85%, transparent);
|
||||
backdrop-filter: blur(10px); z-index: 20;
|
||||
}
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 13px; border-radius: 7px; transition: .14s; white-space: nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); }
|
||||
.controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
.stage { flex: 1; min-height: 0; overflow: auto; position: relative; }
|
||||
.stage .comb { position: absolute; inset: 0; opacity: .5; pointer-events: none; }
|
||||
|
||||
.wrap { max-width: 920px; margin: 0 auto; padding: 46px 32px 80px; position: relative; }
|
||||
|
||||
/* ── Greeting ───────────────────────────────────────────────── */
|
||||
.greet { margin-bottom: 6px; }
|
||||
.greet .date { font-family: var(--mono); font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--honey); margin-bottom: 12px; display:flex; align-items:center; gap:9px; }
|
||||
.greet .date .dot { background: var(--healthy); }
|
||||
.greet .date .streakchip { margin-left: auto; padding: 4px 11px; border-radius: 999px; background: var(--honey-wash); border: 1px solid var(--honey-line); color: var(--honey); font-size: 11px; letter-spacing: .02em; text-transform: none; }
|
||||
.greet h1 { font-family: var(--sans); font-weight: 600; font-size: clamp(34px, 5vw, 52px); line-height: 1.02; letter-spacing: -0.02em; margin: 0; }
|
||||
.greet h1 em { font-style: normal; color: var(--honey); }
|
||||
|
||||
/* ── Overnight story (the hero of A) ────────────────────────── */
|
||||
.overnight {
|
||||
margin-top: 30px; padding: 28px 30px; border-radius: var(--r-xl);
|
||||
background: linear-gradient(140deg, var(--surface), var(--surface-2));
|
||||
border: 1px solid var(--line); box-shadow: var(--shadow);
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.overnight::after { content:''; position:absolute; right:-50px; top:-50px; width:200px; height:200px; background: radial-gradient(circle, var(--honey-wash), transparent 65%); pointer-events:none; }
|
||||
.overnight .tag { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--intel); display:flex; align-items:center; gap:8px; }
|
||||
.overnight .story { font-family: var(--sans); font-weight: 600; font-size: clamp(21px,2.6vw,28px); line-height: 1.32; letter-spacing: -0.01em; margin: 14px 0 0; max-width: 30ch; }
|
||||
.overnight .story b { font-style: normal; color: var(--honey); font-family: var(--sans); font-weight: 700; }
|
||||
.overnight .runs { margin-top: 22px; display: flex; flex-wrap: wrap; gap: 10px; }
|
||||
.runchip { display: inline-flex; align-items: center; gap: 8px; padding: 8px 13px; border-radius: 10px; background: var(--bg-2); border: 1px solid var(--line-soft); font-size: 13px; color: var(--text-2); }
|
||||
.runchip .dot { width: 8px; height: 8px; }
|
||||
.runchip b { color: var(--text); font-weight: 650; }
|
||||
|
||||
/* ── Section heads ──────────────────────────────────────────── */
|
||||
.sec-h { font-family: var(--mono); font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); margin: 40px 0 16px; display:flex; align-items:center; gap:10px; }
|
||||
.sec-h::after { content:''; flex:1; height:1px; background: var(--line-soft); }
|
||||
|
||||
/* ── Continue cards ─────────────────────────────────────────── */
|
||||
.ws-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
|
||||
.ws-card {
|
||||
padding: 20px; border-radius: var(--r-lg); background: var(--surface);
|
||||
border: 1px solid var(--line-soft); cursor: pointer; transition: .18s; position: relative;
|
||||
}
|
||||
.ws-card:hover { border-color: var(--honey-line); transform: translateY(-3px); box-shadow: var(--shadow); }
|
||||
.ws-card .top { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; }
|
||||
.ws-card .ic { width: 34px; height: 38px; flex: none; display:grid; place-items:center; font-size: 15px; color:#1a1407; font-weight:800; }
|
||||
.ws-card h3 { margin: 0; font-size: 16px; font-weight: 650; letter-spacing: -0.01em; flex: 1; }
|
||||
.ws-card .when { font-size: 12px; color: var(--text-dim); font-family: var(--mono); }
|
||||
.ws-card p { margin: 0 0 16px; font-size: 13.5px; color: var(--text-muted); line-height: 1.5; }
|
||||
.ws-card .foot { display: flex; align-items: center; justify-content: space-between; }
|
||||
.ws-card .cont { font-size: 13px; font-weight: 650; color: var(--honey); display: inline-flex; align-items: center; gap: 5px; }
|
||||
.ws-card .badge { font-size: 11.5px; padding: 3px 9px; border-radius: 999px; display:inline-flex; align-items:center; gap:5px; }
|
||||
.ws-card .badge.pend { color: var(--attention); background: var(--honey-wash); }
|
||||
.ws-card .badge.live { color: var(--healthy); background: var(--healthy-wash); }
|
||||
|
||||
/* ── Next move / suggestions ───────────────────────────────── */
|
||||
.moves { display: grid; gap: 10px; }
|
||||
.move {
|
||||
display: flex; align-items: center; gap: 14px; padding: 15px 18px;
|
||||
border-radius: var(--r); background: var(--surface); border: 1px solid var(--line-soft);
|
||||
cursor: pointer; transition: .15s;
|
||||
}
|
||||
.move:hover { border-color: var(--honey-line); background: var(--surface-2); }
|
||||
.move .mi { width: 30px; height: 30px; border-radius: 9px; flex: none; display:grid; place-items:center; }
|
||||
.move .mtext { flex: 1; }
|
||||
.move .mtext b { display:block; font-size: 14.5px; font-weight: 600; }
|
||||
.move .mtext span { font-size: 12.5px; color: var(--text-muted); }
|
||||
.move .arrow { color: var(--text-dim); font-size: 18px; transition: .15s; }
|
||||
.move:hover .arrow { color: var(--honey); transform: translateX(3px); }
|
||||
|
||||
/* ── Ask bar (B + footer of A) ─────────────────────────────── */
|
||||
.ask {
|
||||
margin-top: 28px; display: flex; align-items: center; gap: 12px;
|
||||
padding: 7px 7px 7px 20px; border-radius: 999px;
|
||||
background: var(--surface); border: 1px solid var(--line); box-shadow: var(--shadow);
|
||||
}
|
||||
.ask:focus-within { border-color: var(--honey-line); box-shadow: var(--honey-glow); }
|
||||
.ask input { flex: 1; background: transparent; border: 0; color: var(--text); font-family: var(--sans); font-size: 15.5px; outline: none; }
|
||||
.ask input::placeholder { color: var(--text-dim); }
|
||||
.ask .go { width: 42px; height: 42px; border-radius: 999px; border: 0; background: var(--honey); color: #1a1407; font-size: 18px; cursor: pointer; flex:none; display:grid; place-items:center; transition:.15s; }
|
||||
.ask .go:hover { background: var(--honey-bright); }
|
||||
.ask .hint { font-family: var(--mono); font-size: 11px; color: var(--text-dim); }
|
||||
|
||||
/* ── Variation B — focus ───────────────────────────────────── */
|
||||
.focus-wrap { max-width: 720px; margin: 0 auto; padding: 70px 32px; text-align: center; position: relative; }
|
||||
.focus-wrap .date { justify-content: center; }
|
||||
.focus-wrap h1 { font-size: clamp(36px,5.5vw,58px); margin-bottom: 26px; }
|
||||
.focus-strip { margin-top: 30px; display: inline-flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
|
||||
.focus-resume { margin-top: 38px; display:flex; flex-direction:column; gap:10px; text-align:left; }
|
||||
|
||||
/* ── Variation C — operator dashboard ──────────────────────── */
|
||||
.dash { display: grid; grid-template-columns: 1.5fr 1fr; gap: 16px; margin-top: 26px; }
|
||||
.dash .col { display: grid; gap: 16px; align-content: start; }
|
||||
.card { padding: 20px; border-radius: var(--r-lg); background: var(--surface); border: 1px solid var(--line-soft); }
|
||||
.card > h4 { margin: 0 0 14px; font-size: 12px; font-family: var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text-dim); display:flex; align-items:center; gap:8px; }
|
||||
.stat-row { display: grid; grid-template-columns: repeat(3,1fr); gap: 12px; }
|
||||
.stat { padding: 14px; border-radius: var(--r); background: var(--bg-2); border: 1px solid var(--line-soft); }
|
||||
.stat .v { font-size: 26px; font-weight: 750; letter-spacing: -0.02em; font-family: var(--sans); line-height: 1; }
|
||||
.stat .l { font-size: 11.5px; color: var(--text-muted); margin-top: 6px; }
|
||||
.mini { display:flex; align-items:center; gap:10px; padding: 10px 0; border-bottom: 1px solid var(--line-soft); font-size: 13.5px; }
|
||||
.mini:last-child { border-bottom: 0; }
|
||||
.mini .dot { flex: none; }
|
||||
.mini .t { flex: 1; color: var(--text-2); }
|
||||
.mini .meta { font-size: 11.5px; color: var(--text-dim); font-family: var(--mono); }
|
||||
.alert { display:flex; gap:11px; padding: 13px 15px; border-radius: var(--r); background: var(--risk-wash); border: 1px solid color-mix(in srgb, var(--risk) 30%, transparent); }
|
||||
.alert .ai { color: var(--risk); flex:none; }
|
||||
.alert b { font-size: 13.5px; }
|
||||
.alert p { margin: 3px 0 0; font-size: 12.5px; color: var(--text-muted); }
|
||||
.alert .fix { margin-left:auto; align-self:center; font-size:12px; font-weight:600; color: var(--risk); white-space:nowrap; }
|
||||
|
||||
.hidden { display: none !important; }
|
||||
svg.i { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Home · variation</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="a">A · Editorial</button>
|
||||
<button data-v="b">B · Focus</button>
|
||||
<button data-v="c">C · Operator</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Editorial briefing</b> — calm, story-led</span>
|
||||
<div class="right">
|
||||
<button class="tbtn" id="tbtn" title="Toggle theme">☾</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<div class="comb"></div>
|
||||
|
||||
<!-- ══════════ VARIATION A — EDITORIAL ══════════ -->
|
||||
<div class="wrap view" id="view-a">
|
||||
<div class="greet">
|
||||
<div class="date"><span class="dot dot-live"></span>Friday · June 14 · 8:42<span class="streakchip" title="You've opened Waggle 12 days running">🔥 12-day streak</span></div>
|
||||
<h1>Good morning, Mara.<br>You're <em>ahead</em> of yesterday.</h1>
|
||||
</div>
|
||||
|
||||
<div class="overnight">
|
||||
<div class="tag"><span class="dot dot-live" style="background:var(--intel)"></span>While you slept</div>
|
||||
<p class="story">Waggle finished the <b>Q2 competitor teardown</b>, folded <b>14 new memories</b> into the hive, and ran into <b>one snag</b> worth a look.</p>
|
||||
<div class="runs">
|
||||
<span class="runchip"><span class="dot" style="background:var(--healthy)"></span><b>Teardown</b> drafted · 9 competitors</span>
|
||||
<span class="runchip"><span class="dot" style="background:var(--intel)"></span><b>14</b> memories consolidated</span>
|
||||
<span class="runchip"><span class="dot" style="background:var(--work)"></span><b>2</b> artifacts created</span>
|
||||
<span class="runchip"><span class="dot" style="background:var(--risk)"></span><b>1</b> export failed</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-h">Pick up where you left off</div>
|
||||
<div class="ws-grid" id="wsGridA"></div>
|
||||
|
||||
<div class="sec-h">Waggle suggests</div>
|
||||
<div class="moves" id="movesA"></div>
|
||||
|
||||
<div class="ask">
|
||||
<svg class="i" viewBox="0 0 24 24" style="color:var(--honey)"><path d="M12 3v18M3 12h18"/></svg>
|
||||
<input placeholder="Start something new — “draft the board update from this week’s work”…">
|
||||
<span class="hint">⌘K</span>
|
||||
<button class="go">→</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════ VARIATION B — FOCUS ══════════ -->
|
||||
<div class="wrap focus-wrap view hidden" id="view-b">
|
||||
<div class="greet">
|
||||
<div class="date"><span class="dot dot-live"></span>Friday · June 14</div>
|
||||
<h1>What should we<br>work on, <em>Mara?</em></h1>
|
||||
</div>
|
||||
<div class="ask" style="max-width:620px;margin:0 auto">
|
||||
<svg class="i" viewBox="0 0 24 24" style="color:var(--honey)"><path d="M4 12h16M13 5l7 7-7 7"/></svg>
|
||||
<input placeholder="Describe a task, or pick up a thread below…" autofocus>
|
||||
<span class="hint">⌘K</span>
|
||||
<button class="go">→</button>
|
||||
</div>
|
||||
<div class="focus-strip" id="focusStrip"></div>
|
||||
|
||||
<div class="focus-resume" id="focusResume"></div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════ VARIATION C — OPERATOR ══════════ -->
|
||||
<div class="wrap view hidden" id="view-c">
|
||||
<div class="greet">
|
||||
<div class="date"><span class="dot dot-live"></span>Friday · June 14 · 8:42</div>
|
||||
<h1 style="font-size:clamp(28px,3.6vw,38px)">Good morning, Mara.</h1>
|
||||
</div>
|
||||
|
||||
<div class="alert" style="margin-top:22px">
|
||||
<svg class="i ai" viewBox="0 0 24 24"><path d="M12 9v4m0 4h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg>
|
||||
<div>
|
||||
<b>Overnight export to Salesforce failed</b>
|
||||
<p>“Q2 pipeline sync” · auth token expired at 02:14 · 1 of 4 automations</p>
|
||||
</div>
|
||||
<span class="fix">Fix & retry →</span>
|
||||
</div>
|
||||
|
||||
<div class="dash">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<h4><span class="dot dot-live" style="background:var(--intel)"></span>Overnight</h4>
|
||||
<div class="stat-row">
|
||||
<div class="stat"><div class="v" style="color:var(--intel)">14</div><div class="l">Memories consolidated</div></div>
|
||||
<div class="stat"><div class="v" style="color:var(--work)">2</div><div class="l">Artifacts created</div></div>
|
||||
<div class="stat"><div class="v" style="color:var(--healthy)">3/4</div><div class="l">Automations done</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>Active workspaces</h4>
|
||||
<div id="dashWs"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<h4>Up next</h4>
|
||||
<div id="dashNext"></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>Agents running</h4>
|
||||
<div id="dashAgents"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ── theme: sync with parent concept doc, fall back to own ──────── */
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{ localStorage.setItem('waggle-concept-theme', t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
/* ── data ───────────────────────────────────────────────────────── */
|
||||
const HONEY = 'linear-gradient(150deg,var(--honey-bright),var(--honey-deep))';
|
||||
const workspaces = [
|
||||
{ id:'ci', name:'Competitive Intelligence', glyph:'C', sum:'Q2 landscape teardown — 9 rivals mapped, pricing pulled, draft ready for your read.', when:'2h ago', badge:'pend', badgeText:'3 to review' },
|
||||
{ id:'board', name:'Q2 Board Deck', glyph:'B', sum:'18 slides drafted from the metrics pack. Narrative needs your voice on slide 6.', when:'yesterday', badge:'live', badgeText:'agent live' },
|
||||
{ id:'research', name:'Customer Research', glyph:'R', sum:'31 interviews transcribed and themed. Top friction: onboarding drop-off.', when:'2d ago', badge:null },
|
||||
{ id:'pricing', name:'Pricing Model', glyph:'P', sum:'Scenario model rebuilt. New tier lifts ARPU ~12% with low churn risk.', when:'3d ago', badge:null },
|
||||
];
|
||||
const moves = [
|
||||
{ ic:'check', col:'var(--healthy)', bg:'var(--healthy-wash)', t:'Review the competitor teardown', s:'9 competitors · ready since 02:40 · ~6 min read' },
|
||||
{ ic:'send', col:'var(--work)', bg:'var(--work-wash)', t:'Send the board update', s:'Draft built from this week’s work in Q2 Board Deck' },
|
||||
{ ic:'brain', col:'var(--intel)', bg:'var(--intel-wash)', t:'Confirm 3 imported memories', s:'From Tuesday’s pricing call — Waggle wants your sign-off' },
|
||||
];
|
||||
const next = [
|
||||
{ dot:'var(--work)', t:'Board sync', meta:'10:00' },
|
||||
{ dot:'var(--attention)', t:'Pricing review w/ Finance', meta:'13:30' },
|
||||
{ dot:'var(--healthy)', t:'“Weekly digest” automation runs', meta:'17:00' },
|
||||
];
|
||||
const agents = [
|
||||
{ dot:'var(--healthy)', t:'Research-synth', meta:'theming · 78%' },
|
||||
{ dot:'var(--work)', t:'Deck-builder', meta:'idle · waiting on you' },
|
||||
];
|
||||
const icons = {
|
||||
check:'<path d="M20 6 9 17l-5-5"/>', send:'<path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>',
|
||||
brain:'<path d="M12 5a3 3 0 0 0-3 3 3 3 0 0 0-3 3 3 3 0 0 0 1 5 3 3 0 0 0 5 1 3 3 0 0 0 5-1 3 3 0 0 0 1-5 3 3 0 0 0-3-3 3 3 0 0 0-3-3Z"/>'
|
||||
};
|
||||
|
||||
/* ── render A ───────────────────────────────────────────────────── */
|
||||
document.getElementById('wsGridA').innerHTML = workspaces.slice(0,4).map(w => `
|
||||
<div class="ws-card">
|
||||
<div class="top">
|
||||
<div class="ic hex" style="background:${HONEY}">${w.glyph}</div>
|
||||
<h3>${w.name}</h3>
|
||||
<span class="when">${w.when}</span>
|
||||
</div>
|
||||
<p>${w.sum}</p>
|
||||
<div class="foot">
|
||||
<span class="cont">Continue →</span>
|
||||
${w.badge ? `<span class="badge ${w.badge}">${w.badge==='live'?'<span class="dot dot-live" style="background:var(--healthy)"></span>':''}${w.badgeText}</span>` : ''}
|
||||
</div>
|
||||
</div>`).join('');
|
||||
|
||||
document.getElementById('movesA').innerHTML = moves.map(m => `
|
||||
<div class="move">
|
||||
<div class="mi" style="background:${m.bg};color:${m.col}"><svg class="i" viewBox="0 0 24 24">${icons[m.ic]}</svg></div>
|
||||
<div class="mtext"><b>${m.t}</b><span>${m.s}</span></div>
|
||||
<span class="arrow">→</span>
|
||||
</div>`).join('');
|
||||
|
||||
/* ── render B ───────────────────────────────────────────────────── */
|
||||
document.getElementById('focusStrip').innerHTML =
|
||||
`<span class="runchip"><span class="dot dot-live" style="background:var(--intel)"></span>Overnight: teardown ready · 14 memories · <b style="color:var(--risk)">1 snag</b></span>`;
|
||||
document.getElementById('focusResume').innerHTML = workspaces.slice(0,3).map(w => `
|
||||
<div class="move">
|
||||
<div class="mi hex" style="background:${HONEY};color:#1a1407;font-weight:800;border-radius:0">${w.glyph}</div>
|
||||
<div class="mtext"><b>${w.name}</b><span>${w.sum}</span></div>
|
||||
<span class="arrow">→</span>
|
||||
</div>`).join('');
|
||||
|
||||
/* ── render C ───────────────────────────────────────────────────── */
|
||||
document.getElementById('dashWs').innerHTML = workspaces.map(w => `
|
||||
<div class="mini"><span class="dot" style="background:${w.badge==='live'?'var(--healthy)':'var(--text-dim)'}"></span><span class="t">${w.name}</span><span class="meta">${w.when}</span></div>`).join('');
|
||||
document.getElementById('dashNext').innerHTML = next.map(n => `
|
||||
<div class="mini"><span class="dot" style="background:${n.dot}"></span><span class="t">${n.t}</span><span class="meta">${n.meta}</span></div>`).join('');
|
||||
document.getElementById('dashAgents').innerHTML = agents.map(a => `
|
||||
<div class="mini"><span class="dot dot-live" style="background:${a.dot}"></span><span class="t">${a.t}</span><span class="meta">${a.meta}</span></div>`).join('');
|
||||
|
||||
/* ── variation switch ───────────────────────────────────────────── */
|
||||
const labels = { a:'<b>Editorial briefing</b> — calm, story-led', b:'<b>Focus</b> — one action, minimal', c:'<b>Operator dashboard</b> — dense, for power users' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e => {
|
||||
const b = e.target.closest('button'); if(!b) return;
|
||||
[...seg.children].forEach(x=>x.classList.toggle('on', x===b));
|
||||
const v = b.dataset.v;
|
||||
['a','b','c'].forEach(k => document.getElementById('view-'+k).classList.toggle('hidden', k!==v));
|
||||
document.getElementById('vlabel').innerHTML = labels[v];
|
||||
document.querySelector('.stage').scrollTop = 0;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
287
docs/design_handoff_waggle_app/design-files/screens/ia.html
Normal file
287
docs/design_handoff_waggle_app/design-files/screens/ia.html
Normal file
@@ -0,0 +1,287 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Navigation</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 40; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 13px; border-radius: 7px; transition: .14s; white-space: nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); } .controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.kbtn { display:inline-flex; align-items:center; gap:8px; font-family: var(--sans); font-size:12.5px; font-weight:600; color: var(--text-2); padding: 7px 12px; border-radius: 9px; border: 1px solid var(--line-strong); background: var(--surface); cursor: pointer; }
|
||||
.kbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
.app { flex: 1; min-height: 0; display: flex; position: relative; }
|
||||
|
||||
/* ── sidebar ── */
|
||||
.side { flex: none; width: 248px; border-right: 1px solid var(--line-soft); background: var(--bg-2); display: flex; flex-direction: column; padding: 14px 12px; gap: 4px; }
|
||||
.wspill { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 11px; border: 1px solid var(--line-soft); background: var(--surface); cursor: pointer; transition:.15s; margin-bottom: 10px; }
|
||||
.wspill:hover { border-color: var(--honey-line); }
|
||||
.wspill .wm { width: 28px; height: 32px; flex:none; display:grid; place-items:center; font-weight:800; font-size:12px; color:#1a1407; background: linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.wspill .wn { flex:1; min-width:0; } .wspill .wn b { font-size: 13px; font-weight:650; display:block; line-height:1.1; } .wspill .wn span { font-size: 11px; color: var(--text-dim); }
|
||||
.wspill .cv { color: var(--text-dim); font-size: 11px; }
|
||||
|
||||
.navitem { display: flex; align-items: center; gap: 12px; padding: 9px 11px; border-radius: 10px; cursor: pointer; color: var(--text-2); transition:.13s; position: relative; }
|
||||
.navitem:hover { background: var(--surface-2); color: var(--text); }
|
||||
.navitem.on { background: var(--honey-wash); color: var(--text); }
|
||||
.navitem.on::before { content:''; position:absolute; left:-12px; top:50%; transform:translateY(-50%); width:3px; height:18px; border-radius:3px; background: var(--honey); }
|
||||
.navitem .ni { width: 19px; height: 19px; flex:none; }
|
||||
.navitem .ni svg { width:19px; height:19px; stroke: currentColor; fill:none; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
|
||||
.navitem.on .ni svg { stroke: var(--honey); }
|
||||
.navitem .nt { flex:1; font-size: 14px; font-weight: 550; }
|
||||
.navitem .badge { font-size: 10.5px; font-family: var(--mono); color: var(--attention); background: var(--honey-wash); padding: 2px 7px; border-radius: 999px; }
|
||||
|
||||
.side .zone { font-family: var(--mono); font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--text-dim); padding: 14px 11px 6px; display: flex; align-items: center; gap: 8px; }
|
||||
.side .pro-tier { display: none; }
|
||||
body.pro .side .pro-tier { display: block; }
|
||||
|
||||
.side .more { margin-top: 6px; }
|
||||
.cmdtile { display: flex; align-items: center; gap: 11px; padding: 10px 11px; border-radius: 10px; border: 1px dashed var(--line-strong); cursor: pointer; transition:.14s; color: var(--text-muted); }
|
||||
.cmdtile:hover { border-color: var(--honey-line); color: var(--honey); background: var(--honey-wash); }
|
||||
.cmdtile .nt { flex:1; font-size: 13px; font-weight: 600; }
|
||||
.cmdtile .kbd { font-family: var(--mono); font-size: 11px; padding: 2px 7px; border-radius: 6px; border: 1px solid var(--line-strong); background: var(--surface); color: var(--text-2); }
|
||||
|
||||
.side .spacer { flex: 1; }
|
||||
.userrow { display:flex; align-items:center; gap:10px; padding: 9px 10px; border-top: 1px solid var(--line-soft); margin-top: 6px; }
|
||||
.userrow .ua { width: 28px; height:28px; border-radius:999px; background: var(--honey); color:#1a1407; display:grid; place-items:center; font-size:11px; font-weight:700; flex:none; }
|
||||
.userrow .un { flex:1; font-size:13px; font-weight:600; }
|
||||
.userrow .ut { font-size:10.5px; color: var(--text-dim); font-family: var(--mono); }
|
||||
|
||||
/* ── main ── */
|
||||
.main { flex:1; min-width:0; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding: 40px; position: relative; }
|
||||
.main .comb { position:absolute; inset:0; opacity:.45; pointer-events:none; }
|
||||
.main .micon { width:64px; height:72px; display:grid; place-items:center; background: var(--surface); border:1px solid var(--line); margin-bottom:22px; position:relative; }
|
||||
.main .micon svg { width:30px; height:30px; stroke: var(--honey); fill:none; stroke-width:1.6; }
|
||||
.main h1 { font-size: 30px; font-weight:650; letter-spacing:-0.02em; margin:0 0 8px; position:relative; }
|
||||
.main p { font-size:15px; color: var(--text-muted); margin:0 0 24px; max-width: 42ch; position:relative; line-height:1.6; }
|
||||
.main .openk { position: relative; }
|
||||
|
||||
/* ── command palette ── */
|
||||
.palette-scrim { position: absolute; inset: 0; background: rgba(8,6,3,0.55); backdrop-filter: blur(3px); z-index: 50; display: none; align-items: flex-start; justify-content: center; padding-top: 64px; }
|
||||
body.cmd .palette-scrim { display: flex; }
|
||||
.palette { width: min(620px, 92%); background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-lg); box-shadow: var(--shadow-pop); overflow: hidden; display: flex; flex-direction: column; max-height: calc(100% - 96px); }
|
||||
.palette .pin { display: flex; align-items: center; gap: 12px; padding: 16px 18px; border-bottom: 1px solid var(--line-soft); }
|
||||
.palette .pin svg { width: 18px; height: 18px; stroke: var(--text-muted); fill:none; stroke-width:1.9; flex:none; }
|
||||
.palette .pin input { flex:1; border:0; background:transparent; color: var(--text); font-family: var(--sans); font-size: 16px; outline:none; }
|
||||
.palette .pin input::placeholder { color: var(--text-dim); }
|
||||
.palette .pin .esc { font-family: var(--mono); font-size: 11px; color: var(--text-dim); border:1px solid var(--line-strong); border-radius:6px; padding:2px 7px; }
|
||||
.palette .results { overflow:auto; padding: 8px; }
|
||||
.pgroup-h { font-family: var(--mono); font-size: 10px; letter-spacing:.12em; text-transform:uppercase; color: var(--text-dim); padding: 12px 12px 6px; }
|
||||
.pgroup-h.pinned { color: var(--honey); }
|
||||
.presult .pinmark { color: var(--honey); }
|
||||
.presult { display:flex; align-items:center; gap:12px; padding: 9px 12px; border-radius:9px; cursor:pointer; }
|
||||
.presult:hover, .presult.sel { background: var(--honey-wash); }
|
||||
.presult .pi { width:18px; height:18px; flex:none; }
|
||||
.presult .pi svg { width:18px; height:18px; stroke: var(--text-2); fill:none; stroke-width:1.7; }
|
||||
.presult:hover .pi svg, .presult.sel .pi svg { stroke: var(--honey); }
|
||||
.presult .pt { flex:1; min-width:0; }
|
||||
.presult .pt b { font-size: 14px; font-weight: 550; }
|
||||
.presult .pt .sub { font-family: var(--mono); font-size: 11px; color: var(--text-dim); margin-left: 8px; }
|
||||
.presult .meta { font-family: var(--mono); font-size: 10.5px; color: var(--text-dim); }
|
||||
.pempty { padding: 30px; text-align:center; color: var(--text-dim); font-size: 13px; }
|
||||
|
||||
.hidden { display:none !important; }
|
||||
@media (max-width: 760px){ .side { width: 200px; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="cmd">
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Navigation · variation</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="a">A · Calm spine</button>
|
||||
<button data-v="b">B · Pro (pinned)</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Calm spine</b> — five places, depth in ⌘K</span>
|
||||
<div class="right">
|
||||
<button class="kbtn" id="openK">⌘K · Command bar</button>
|
||||
<button class="tbtn" id="tbtn" title="Toggle theme">☾</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="app">
|
||||
<!-- sidebar -->
|
||||
<nav class="side">
|
||||
<div class="wspill">
|
||||
<div class="wm hex">C</div>
|
||||
<div class="wn"><b>Competitive Intel</b><span>workspace</span></div>
|
||||
<span class="cv">⌄</span>
|
||||
</div>
|
||||
|
||||
<div class="navitem on" data-place="Home"><span class="ni"><svg viewBox="0 0 24 24"><path d="M3 11l9-8 9 8M5 10v10h14V10"/></svg></span><span class="nt">Home</span></div>
|
||||
<div class="navitem" data-place="Chat"><span class="ni"><svg viewBox="0 0 24 24"><path d="M21 12a8 8 0 0 1-11.6 7.1L3 21l1.9-6.4A8 8 0 1 1 21 12Z"/></svg></span><span class="nt">Chat</span></div>
|
||||
<div class="navitem" data-place="Memory"><span class="ni"><svg viewBox="0 0 24 24"><path d="M12 5a3 3 0 0 0-3 3 3 3 0 0 0-3 3 3 3 0 0 0 1 5 3 3 0 0 0 5 1 3 3 0 0 0 5-1 3 3 0 0 0 1-5 3 3 0 0 0-3-3 3 3 0 0 0-3-3Z"/></svg></span><span class="nt">Memory</span></div>
|
||||
<div class="navitem" data-place="Agents & tasks"><span class="ni"><svg viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M9 9h6v6H9z"/></svg></span><span class="nt">Agents & tasks</span><span class="badge">2</span></div>
|
||||
<div class="navitem" data-place="Library"><span class="ni"><svg viewBox="0 0 24 24"><path d="M4 5c0-1 1-1.5 2-1.5S19 4 19 4v15s-11-.5-13 .5M4 5v15"/></svg></span><span class="nt">Library</span></div>
|
||||
|
||||
<!-- pro tier (variation B) -->
|
||||
<div class="pro-tier">
|
||||
<div class="zone">Pinned · power tools</div>
|
||||
<div class="navitem" data-place="Agent swarm"><span class="ni"><svg viewBox="0 0 24 24"><circle cx="6" cy="7" r="2.4"/><circle cx="18" cy="7" r="2.4"/><circle cx="12" cy="17" r="2.4"/><path d="M8 8l3 7M16 8l-3 7"/></svg></span><span class="nt">Agent swarm</span></div>
|
||||
<div class="navitem" data-place="Connectors"><span class="ni"><svg viewBox="0 0 24 24"><path d="M7 8a4 4 0 0 0 0 8h2M17 8a4 4 0 0 1 0 8h-2M8 12h8"/></svg></span><span class="nt">Connectors</span></div>
|
||||
<div class="navitem" data-place="Approvals"><span class="ni"><svg viewBox="0 0 24 24"><path d="M9 12l2 2 4-4M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/></svg></span><span class="nt">Approvals</span><span class="badge">1</span></div>
|
||||
</div>
|
||||
|
||||
<div class="more">
|
||||
<div class="zone">Everything else</div>
|
||||
<div class="cmdtile" id="openK2"><span class="nt">Search & commands</span><span class="kbd">⌘K</span></div>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div class="userrow"><div class="ua">M</div><div class="un">Mara K.<div class="ut">Pro · trial</div></div></div>
|
||||
</nav>
|
||||
|
||||
<!-- main -->
|
||||
<div class="main">
|
||||
<div class="comb"></div>
|
||||
<div class="micon hex" id="mainIcon"><svg viewBox="0 0 24 24"><path d="M3 11l9-8 9 8M5 10v10h14V10"/></svg></div>
|
||||
<h1 id="mainTitle">Home</h1>
|
||||
<p id="mainHint">Five places cover the everyday. Everything Waggle can do — the swarm, MCP servers, the optimizer, the vault — lives one keystroke away.</p>
|
||||
<button class="kbtn openk" id="openK3">Press ⌘K to find anything</button>
|
||||
</div>
|
||||
|
||||
<!-- command palette -->
|
||||
<div class="palette-scrim" id="scrim">
|
||||
<div class="palette" role="dialog" aria-label="Command bar">
|
||||
<div class="pin">
|
||||
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<input id="pq" placeholder="Search places, run a command, reach any tool…" autocomplete="off">
|
||||
<span class="esc">esc</span>
|
||||
</div>
|
||||
<div class="results" id="results"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const I = {
|
||||
home:'<path d="M3 11l9-8 9 8M5 10v10h14V10"/>',
|
||||
chat:'<path d="M21 12a8 8 0 0 1-11.6 7.1L3 21l1.9-6.4A8 8 0 1 1 21 12Z"/>',
|
||||
mem:'<path d="M12 5a3 3 0 0 0-3 3 3 3 0 0 0-3 3 3 3 0 0 0 1 5 3 3 0 0 0 5 1 3 3 0 0 0 5-1 3 3 0 0 0 1-5 3 3 0 0 0-3-3 3 3 0 0 0-3-3Z"/>',
|
||||
task:'<rect x="4" y="4" width="16" height="16" rx="3"/><path d="M9 9h6v6H9z"/>',
|
||||
lib:'<path d="M4 5c0-1 1-1.5 2-1.5S19 4 19 4v15s-11-.5-13 .5M4 5v15"/>',
|
||||
plus:'<path d="M12 5v14M5 12h14"/>', import:'<path d="M12 3v12m0 0 4-4m-4 4-4-4M5 21h14"/>',
|
||||
swarm:'<circle cx="6" cy="7" r="2.4"/><circle cx="18" cy="7" r="2.4"/><circle cx="12" cy="17" r="2.4"/><path d="M8 8l3 7M16 8l-3 7"/>',
|
||||
mcp:'<path d="M7 8a4 4 0 0 0 0 8h2M17 8a4 4 0 0 1 0 8h-2M8 12h8"/>',
|
||||
model:'<circle cx="12" cy="12" r="3.5"/><path d="M12 3v3M12 18v3M5 12H2M22 12h-3"/>',
|
||||
vault:'<rect x="4" y="4" width="16" height="16" rx="3"/><circle cx="12" cy="12" r="3"/>',
|
||||
opt:'<path d="M3 17l5-5 4 4 8-8M14 4h6v6"/>', shield:'<path d="M9 12l2 2 4-4M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/>',
|
||||
usage:'<path d="M4 20V10M10 20V4M16 20v-7M22 20H2"/>', skills:'<path d="m12 3 2.5 6 6.5.5-5 4.2 1.6 6.3L12 16.8 6 20l1.6-6.3-5-4.2 6.5-.5z"/>',
|
||||
clock:'<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
|
||||
launch:'<path d="M6 4l14 8-14 8z"/>', drive:'<rect x="3" y="4" width="18" height="14" rx="2"/><path d="M3 9h18M8 21h8"/>'
|
||||
};
|
||||
const groups = [
|
||||
{ h:'Jump to', items:[
|
||||
{ ic:I.home, n:'Home', meta:'G H' }, { ic:I.chat, n:'Chat', meta:'G C' },
|
||||
{ ic:I.mem, n:'Memory', meta:'G M' }, { ic:I.task, n:'Agents & tasks', meta:'G A' },
|
||||
{ ic:I.lib, n:'Library', meta:'G L' },
|
||||
{ ic:I.opt, n:'Benchmarks', sub:'how Waggle compares · memory SOTA' },
|
||||
{ ic:I.drive, n:'All workspaces', sub:'every space you have', meta:'G W' },
|
||||
{ ic:I.home, n:'Competitive Intelligence', sub:'workspace' },
|
||||
{ ic:I.home, n:'Q2 Board Deck', sub:'workspace' },
|
||||
]},
|
||||
{ h:'Do', items:[
|
||||
{ ic:I.plus, n:'Start a task', sub:'new agent run', meta:'⌘N' },
|
||||
{ ic:I.launch, n:'Launch a coding agent', sub:'Claude Code · Cursor · Codex — shares this hive' },
|
||||
{ ic:I.import, n:'Import your history', sub:'ChatGPT · Claude · Cursor' },
|
||||
{ ic:I.usage, n:'Upgrade to Pro', sub:'plans · billing · invoices' },
|
||||
{ ic:I.shield, n:'Account & sign-in', sub:'sync, devices, SSO — optional' },
|
||||
{ ic:I.model, n:'Switch model', sub:'auto · Claude · GPT · local' },
|
||||
{ ic:I.opt, n:'Settings', sub:'models, failover, permissions, plan' },
|
||||
{ ic:I.plus, n:'Quick capture a note' },
|
||||
]},
|
||||
{ h:'Power tools — plain name, technical underneath', items:[
|
||||
{ ic:I.swarm, n:'Run a team of agents', sub:'waggle-dance · swarm' },
|
||||
{ ic:I.mcp, n:'Connect a tool', sub:'MCP servers · 21 tools' },
|
||||
{ ic:I.mcp, n:'Connectors', sub:'29 integrations' },
|
||||
{ ic:I.shield, n:'Approvals', sub:'pending agent actions' },
|
||||
{ ic:I.opt, n:'Tune the agent', sub:'GEPA optimizer' },
|
||||
{ ic:I.vault, n:'Secrets', sub:'encrypted vault' },
|
||||
{ ic:I.skills, n:'Add a capability', sub:'marketplace · skills, connectors, tools — ask the agent' },
|
||||
{ ic:I.opt, n:'Skill evolution & sharing', sub:'how skills improve & spread across the hive' },
|
||||
{ ic:I.clock, n:'Automations', sub:'scheduled jobs · 1 failing' },
|
||||
{ ic:I.drive, n:'Files & storage', sub:'where this workspace lives · local/virtual/team' },
|
||||
{ ic:I.shield, n:'Audit & compliance', sub:'EU AI Act report' },
|
||||
{ ic:I.usage, n:'Usage & cost', sub:'tokens · spend' },
|
||||
]},
|
||||
];
|
||||
|
||||
// Pinned group — only shown in Pro mode, floats the tools you live in to the top of ⌘K.
|
||||
const pinnedGroup = { h:'★ Pinned · Pro', items:[
|
||||
{ ic:I.swarm, n:'Run a team of agents', sub:'waggle-dance · swarm' },
|
||||
{ ic:I.mcp, n:'Connectors', sub:'29 integrations' },
|
||||
{ ic:I.shield, n:'Approvals', sub:'1 pending' },
|
||||
] };
|
||||
|
||||
const results = document.getElementById('results'), pq = document.getElementById('pq');
|
||||
function render(q){
|
||||
q = (q||'').toLowerCase().trim();
|
||||
let html = '', any = false;
|
||||
const active = (document.body.classList.contains('pro') ? [pinnedGroup, ...groups] : groups);
|
||||
active.forEach(g => {
|
||||
const items = g.items.filter(it => !q || (it.n+' '+(it.sub||'')).toLowerCase().includes(q));
|
||||
if(!items.length) return; any = true;
|
||||
const pin = g===pinnedGroup;
|
||||
html += `<div class="pgroup-h${pin?' pinned':''}">${g.h}</div>`;
|
||||
html += items.map(it => `<div class="presult"><span class="pi"><svg viewBox="0 0 24 24">${it.ic}</svg></span><span class="pt"><b>${it.n}</b>${it.sub?`<span class="sub">${it.sub}</span>`:''}</span>${pin?'<span class="meta pinmark">★</span>':(it.meta?`<span class="meta">${it.meta}</span>`:'')}</div>`).join('');
|
||||
});
|
||||
results.innerHTML = any ? html : '<div class="pempty">No matches — try “import”, “swarm”, “model”, “audit”…</div>';
|
||||
const first = results.querySelector('.presult'); if(first) first.classList.add('sel');
|
||||
}
|
||||
render('');
|
||||
|
||||
function openPalette(){ document.body.classList.add('cmd'); pq.value=''; render(''); setTimeout(()=>pq.focus(),30); }
|
||||
function closePalette(){ document.body.classList.remove('cmd'); }
|
||||
['openK','openK2','openK3'].forEach(id => document.getElementById(id).onclick = openPalette);
|
||||
document.getElementById('scrim').addEventListener('click', e => { if(e.target.id==='scrim') closePalette(); });
|
||||
pq.addEventListener('input', () => render(pq.value));
|
||||
addEventListener('keydown', e => {
|
||||
if((e.metaKey||e.ctrlKey) && e.key.toLowerCase()==='k'){ e.preventDefault(); document.body.classList.contains('cmd')?closePalette():openPalette(); }
|
||||
if(e.key==='Escape') closePalette();
|
||||
});
|
||||
results.addEventListener('click', e => { const r=e.target.closest('.presult'); if(r){ const n=r.querySelector('b').textContent; activate(n); closePalette(); } });
|
||||
|
||||
/* sidebar nav */
|
||||
function activate(place){
|
||||
const item = [...document.querySelectorAll('.navitem')].find(n => n.dataset.place===place);
|
||||
document.querySelectorAll('.navitem').forEach(n=>n.classList.toggle('on', n===item));
|
||||
const icMap = { 'Home':I.home, 'Chat':I.chat, 'Memory':I.mem, 'Agents & tasks':I.task, 'Library':I.lib, 'Agent swarm':I.swarm, 'Connectors':I.mcp, 'Approvals':I.shield };
|
||||
document.getElementById('mainTitle').textContent = place;
|
||||
document.getElementById('mainIcon').innerHTML = `<svg viewBox="0 0 24 24">${icMap[place]||I.home}</svg>`;
|
||||
const hints = { 'Home':'Your daily briefing — what ran overnight, what to pick back up, what\u2019s next.', 'Chat':'The work surface. Talk to your agent; the work reveals beside the thread.', 'Memory':'Everything Waggle knows about this project — with a source on every fact.', 'Agents & tasks':'What\u2019s running, what\u2019s scheduled, what finished while you were away.', 'Library':'Artifacts, files, and skills — the things you and your agents have made.' };
|
||||
document.getElementById('mainHint').textContent = hints[place] || 'A power tool, surfaced from \u2318K. In the calm spine it lives in the command bar; pin it in Pro mode.';
|
||||
}
|
||||
document.querySelectorAll('.navitem').forEach(n => n.onclick = () => activate(n.dataset.place));
|
||||
|
||||
/* variation */
|
||||
const labels = { a:'<b>Calm spine</b> — five places, depth in ⌘K', b:'<b>Pro</b> — power user pins the tools they live in' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e => {
|
||||
const b = e.target.closest('button'); if(!b) return;
|
||||
[...seg.children].forEach(x=>x.classList.toggle('on', x===b));
|
||||
document.body.classList.toggle('pro', b.dataset.v==='b');
|
||||
document.getElementById('vlabel').innerHTML = labels[b.dataset.v];
|
||||
render(pq.value);
|
||||
});
|
||||
|
||||
/* start closed */
|
||||
closePalette();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,245 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Launch agents</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 13px; border-radius: 7px; transition: .14s; white-space: nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); } .controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
.stage { flex: 1; min-height: 0; overflow: auto; }
|
||||
.wrap { max-width: 940px; margin: 0 auto; padding: 30px 32px 70px; }
|
||||
|
||||
.head { margin-bottom: 22px; }
|
||||
.head .crumbs { font-family: var(--mono); font-size: 11px; color: var(--text-dim); margin-bottom: 12px; }
|
||||
.head .crumbs b { color: var(--text-2); font-weight: 500; }
|
||||
.head h1 { font-size: 26px; font-weight: 650; letter-spacing: -0.02em; margin: 0 0 8px; }
|
||||
.head p { font-size: 15px; color: var(--text-muted); line-height: 1.55; margin: 0; max-width: 64ch; }
|
||||
.head p b { color: var(--text-2); font-weight: 600; }
|
||||
.platform { float: right; font-family: var(--mono); font-size: 11px; color: var(--text-2); padding: 6px 11px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); }
|
||||
|
||||
/* prompt box */
|
||||
.promptbox { margin: 20px 0; border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-lg); padding: 14px 16px; }
|
||||
.promptbox .ph { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-muted); margin-bottom: 10px; }
|
||||
.promptbox .ph svg { width: 14px; height: 14px; stroke: var(--honey); fill: none; stroke-width: 1.8; }
|
||||
.promptbox textarea { width: 100%; border: 0; background: transparent; resize: none; color: var(--text); font-family: var(--sans); font-size: 14.5px; line-height: 1.5; outline: none; min-height: 24px; }
|
||||
.promptbox textarea::placeholder { color: var(--text-dim); }
|
||||
.promptbox .pf { margin-top: 8px; font-family: var(--mono); font-size: 10.5px; color: var(--text-dim); }
|
||||
.promptbox .pf .y { color: var(--healthy); }
|
||||
|
||||
.sec-h { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); margin: 26px 0 14px; display: flex; align-items: center; gap: 10px; }
|
||||
.sec-h::after { content:''; flex:1; height:1px; background: var(--line-soft); }
|
||||
.sec-h .pin { color: var(--text-2); }
|
||||
|
||||
/* tool cards */
|
||||
.tools { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
|
||||
.tool { border: 1px solid var(--line-soft); background: var(--surface); border-radius: var(--r-lg); padding: 18px; transition: .16s; }
|
||||
.tool.live { border-color: var(--honey-line); box-shadow: var(--honey-glow); }
|
||||
.tool.off { opacity: .62; }
|
||||
.tool .top { display: flex; align-items: center; gap: 13px; }
|
||||
.tool .logo { width: 40px; height: 44px; flex: none; display: grid; place-items: center; font-family: var(--mono); font-weight: 700; font-size: 14px; color: var(--text); background: var(--surface-3); border: 1px solid var(--line); }
|
||||
.tool.live .logo { color: #1a1407; background: linear-gradient(150deg, var(--honey-bright), var(--honey-deep)); border-color: transparent; }
|
||||
.tool .nm { flex: 1; min-width: 0; }
|
||||
.tool .nm b { font-size: 15.5px; font-weight: 650; letter-spacing: -0.01em; display: block; }
|
||||
.tool .nm .path { font-family: var(--mono); font-size: 11px; color: var(--text-dim); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.badges { display: flex; flex-wrap: wrap; gap: 6px; margin: 13px 0; }
|
||||
.bdg { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 999px; display: inline-flex; align-items: center; gap: 5px; }
|
||||
.bdg.ok { color: var(--healthy); background: var(--healthy-wash); }
|
||||
.bdg.hook { color: var(--attention); background: var(--honey-wash); }
|
||||
.bdg.run { color: var(--work); background: var(--work-wash); }
|
||||
.bdg.no { color: var(--text-dim); background: var(--surface-2); }
|
||||
.actions { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.abtn { font-family: var(--sans); font-size: 12.5px; font-weight: 600; padding: 8px 14px; border-radius: 9px; cursor: pointer; border: 1px solid transparent; display: inline-flex; align-items: center; gap: 6px; transition:.14s; }
|
||||
.abtn svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2; }
|
||||
.abtn.go { background: var(--honey); color: #1a1407; }
|
||||
.abtn.go:hover { background: var(--honey-bright); }
|
||||
.abtn.ghost { background: var(--surface-2); color: var(--text-2); border-color: var(--line-strong); }
|
||||
.abtn.ghost:hover { border-color: var(--honey-line); color: var(--text); }
|
||||
.abtn.stop { background: transparent; color: var(--risk); border-color: color-mix(in srgb,var(--risk) 35%,transparent); }
|
||||
.tool .note { font-size: 12px; color: var(--text-dim); }
|
||||
|
||||
/* running panel */
|
||||
.running { margin-top: 26px; border: 1px solid var(--line-soft); border-radius: var(--r-lg); background: var(--bg-2); padding: 18px; }
|
||||
.running h3 { font-size: 13px; font-weight: 650; margin: 0 0 12px; display: flex; align-items: center; gap: 9px; }
|
||||
.running h3 .dot { background: var(--work); }
|
||||
.prow { display: grid; grid-template-columns: 1fr auto auto auto; gap: 14px; align-items: center; padding: 10px 0; border-top: 1px solid var(--line-soft); font-size: 13px; }
|
||||
.prow:first-of-type { border-top: 0; }
|
||||
.prow .pn { font-weight: 600; display:flex; align-items:center; gap:9px; }
|
||||
.prow .ws { font-size: 11.5px; color: var(--text-muted); }
|
||||
.prow .meta { font-family: var(--mono); font-size: 11px; color: var(--text-dim); }
|
||||
.prow .x { color: var(--risk); cursor: pointer; font-size: 12px; font-weight: 600; }
|
||||
|
||||
/* variation B — shared memory diagram */
|
||||
.flow-wrap { border: 1px solid var(--line); border-radius: var(--r-xl); background: radial-gradient(70% 90% at 50% 0%, var(--surface), var(--bg-2)); padding: 36px; text-align: center; position: relative; overflow: hidden; }
|
||||
.flow-wrap .comb { position: absolute; inset: 0; opacity: .4; }
|
||||
.flow-row { display: flex; align-items: center; justify-content: center; gap: 0; flex-wrap: wrap; position: relative; }
|
||||
.fnode { padding: 16px 16px; border-radius: var(--r-lg); border: 1px solid var(--line-strong); background: var(--surface); min-width: 140px; max-width: 184px; }
|
||||
.fnode .ft { font-family: var(--mono); font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 6px; }
|
||||
.fnode b { font-size: 14px; font-weight: 650; }
|
||||
.fnode small { display: block; font-size: 11.5px; color: var(--text-muted); margin-top: 4px; line-height: 1.4; }
|
||||
.fnode.hive { border-color: var(--honey-line); box-shadow: var(--honey-glow); background: linear-gradient(150deg,var(--surface-2),var(--surface)); }
|
||||
.farrow { color: var(--honey); font-size: 20px; padding: 0 12px; }
|
||||
.farrow small { display:block; font-family: var(--mono); font-size: 9px; color: var(--text-dim); text-transform:uppercase; letter-spacing:.1em; }
|
||||
.flow-note { margin-top: 26px; display: grid; grid-template-columns: repeat(3,1fr); gap: 14px; }
|
||||
.fc { text-align: left; padding: 16px; border-radius: var(--r); background: var(--surface); border: 1px solid var(--line-soft); }
|
||||
.fc .ic { width: 28px; height: 30px; display:grid; place-items:center; margin-bottom: 10px; }
|
||||
.fc .ic svg { width: 17px; height: 17px; stroke: var(--honey); fill:none; stroke-width:1.8; }
|
||||
.fc b { font-size: 13.5px; font-weight: 650; display: block; margin-bottom: 5px; }
|
||||
.fc p { margin: 0; font-size: 12.5px; color: var(--text-muted); line-height: 1.55; }
|
||||
.prov-eg { margin-top: 18px; display:inline-flex; align-items:center; gap: 9px; font-family: var(--mono); font-size: 11.5px; color: var(--intel); background: var(--intel-wash); border: 1px solid color-mix(in srgb,var(--intel) 28%,transparent); padding: 7px 13px; border-radius: 9px; }
|
||||
|
||||
.hidden { display:none !important; }
|
||||
@media (max-width: 820px){ .tools, .flow-note { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Launcher · variation</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="a">A · Launch</button>
|
||||
<button data-v="b">B · How memory is shared</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Launch</b> — detected agents, one click into this workspace</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<!-- ════ A — LAUNCH ════ -->
|
||||
<div class="wrap view" id="view-a">
|
||||
<div class="head">
|
||||
<span class="platform">⌘ macOS · 7 tools scanned</span>
|
||||
<div class="crumbs">Competitive Intelligence <span>›</span> <b>Launch an agent</b></div>
|
||||
<h1>Bring your own agent into this workspace</h1>
|
||||
<p>Open Claude Code, Cursor, or any coding agent from here and it runs <b>wired into this workspace's memory</b> — it recalls what the hive knows, and everything it decides flows back, with provenance. Reversible hooks, nothing leaves your machine.</p>
|
||||
</div>
|
||||
|
||||
<div class="promptbox">
|
||||
<div class="ph"><svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h10"/></svg>Launch with a task <span style="color:var(--text-dim)">— optional, passed to agents that accept a prompt</span></div>
|
||||
<textarea rows="2" placeholder="e.g. “Refactor the pricing module and reconcile it against the Q2 model in memory.”"></textarea>
|
||||
<div class="pf"><span class="y">Sent to:</span> Claude Code, Cursor · <span style="color:var(--text-dim)">Ignored by:</span> Claude Desktop</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-h">Detected on this machine</div>
|
||||
<div class="tools" id="toolsLive"></div>
|
||||
|
||||
<div class="sec-h">Not installed</div>
|
||||
<div class="tools" id="toolsOff"></div>
|
||||
|
||||
<div class="running">
|
||||
<h3><span class="dot dot-live"></span>Running now · sharing this hive</h3>
|
||||
<div id="procs"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════ B — HOW MEMORY IS SHARED ════ -->
|
||||
<div class="wrap view hidden" id="view-b">
|
||||
<div class="head">
|
||||
<h1>How a launched agent shares the hive</h1>
|
||||
<p>When you launch from Waggle, the workspace's memory is wired into the external tool — no copy-paste, no re-explaining. Its work comes back attributed.</p>
|
||||
</div>
|
||||
|
||||
<div class="flow-wrap">
|
||||
<div class="comb"></div>
|
||||
<div class="flow-row">
|
||||
<div class="fnode">
|
||||
<div class="ft">You launch</div>
|
||||
<b>Claude Code</b>
|
||||
<small>opened from Waggle with<br><span class="mono" style="font-family:var(--mono);font-size:10.5px;color:var(--honey)">WAGGLE_WORKSPACE_ID</span></small>
|
||||
</div>
|
||||
<div class="farrow">⇄<small>hooks</small></div>
|
||||
<div class="fnode">
|
||||
<div class="ft">5 lifecycle hooks</div>
|
||||
<b>hive-mind</b>
|
||||
<small>session · prompt · stop · compact · tool</small>
|
||||
</div>
|
||||
<div class="farrow">⇄<small>recall + commit</small></div>
|
||||
<div class="fnode hive">
|
||||
<div class="ft">Your workspace</div>
|
||||
<b>The hive</b>
|
||||
<small>local SQLite memory<br>shared with Waggle</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-note">
|
||||
<div class="fc"><div class="ic"><svg viewBox="0 0 24 24"><path d="M12 3v12m0 0 4-4m-4 4-4-4M5 21h14"/></svg></div><b>It recalls on start</b><p>The agent opens already knowing this workspace — decisions, constraints, and context, pulled from the hive.</p></div>
|
||||
<div class="fc"><div class="ic"><svg viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></svg></div><b>It commits as it works</b><p>Every meaningful step is captured back into the hive, so Waggle and your next session see it too.</p></div>
|
||||
<div class="fc"><div class="ic"><svg viewBox="0 0 24 24"><path d="M9 12l2 2 4-4M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/></svg></div><b>Reversible & local</b><p>Hooks install and uninstall cleanly. Nothing leaves your machine; you can verify or remove them anytime.</p></div>
|
||||
</div>
|
||||
<div class="prov-eg">⬡ remembered from Claude Code · 14:22 · session a1f9</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const P = { play:'<path d="M6 4l14 8-14 8z"/>', stop:'<rect x="6" y="6" width="12" height="12" rx="1.5"/>', shield:'<path d="M9 12l2 2 4-4M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/>', dl:'<path d="M12 3v12m0 0 4-4m-4 4-4-4M5 21h14"/>', trash:'<path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13"/>' };
|
||||
const live = [
|
||||
{ mono:'CC', name:'Claude Code', path:'/opt/homebrew/bin/claude', installed:true, hooks:true, running:true, v:'2.4.0' },
|
||||
{ mono:'Cu', name:'Cursor', path:'/Applications/Cursor.app', installed:true, hooks:false, running:false, v:'0.51' },
|
||||
{ mono:'Cx', name:'Codex CLI', path:'/usr/local/bin/codex', installed:true, hooks:false, running:false, v:'1.2', hooksSoon:true },
|
||||
{ mono:'CD', name:'Claude Desktop', path:'/Applications/Claude.app', installed:true, hooks:false, running:false, v:'1.9', noPrompt:true },
|
||||
];
|
||||
const off = [
|
||||
{ mono:'He', name:'Hermes', note:'Not installed — install it, then re-scan.' },
|
||||
{ mono:'Oc', name:'OpenClaw', note:'Not installed — launch & hooks arrive when detected.' },
|
||||
];
|
||||
|
||||
document.getElementById('toolsLive').innerHTML = live.map(t => `
|
||||
<div class="tool ${t.running?'live':''}">
|
||||
<div class="top">
|
||||
<div class="logo">${t.mono}</div>
|
||||
<div class="nm"><b>${t.name}</b><div class="path" title="${t.path}">${t.path} · v${t.v}</div></div>
|
||||
</div>
|
||||
<div class="badges">
|
||||
<span class="bdg ok">✓ Installed</span>
|
||||
${t.hooks?'<span class="bdg hook">⬡ Hooks active</span>':(t.hooksSoon?'<span class="bdg no">hooks soon</span>':'<span class="bdg no">hooks off</span>')}
|
||||
${t.running?'<span class="bdg run"><span class="dot dot-live" style="background:var(--work)"></span>Running</span>':''}
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="abtn go"><svg viewBox="0 0 24 24">${P.play}</svg>Launch in workspace</button>
|
||||
${t.running?`<button class="abtn stop"><svg viewBox="0 0 24 24">${P.stop}</svg>Stop</button>`:''}
|
||||
${t.hooks?`<button class="abtn ghost"><svg viewBox="0 0 24 24">${P.shield}</svg>Verify</button>`:(t.hooksSoon?'':`<button class="abtn ghost"><svg viewBox="0 0 24 24">${P.dl}</svg>Install hooks</button>`)}
|
||||
</div>
|
||||
</div>`).join('');
|
||||
|
||||
document.getElementById('toolsOff').innerHTML = off.map(t => `
|
||||
<div class="tool off">
|
||||
<div class="top"><div class="logo">${t.mono}</div><div class="nm"><b>${t.name}</b><div class="path">not detected</div></div></div>
|
||||
<div class="badges"><span class="bdg no">Not installed</span></div>
|
||||
<div class="note">${t.note}</div>
|
||||
</div>`).join('');
|
||||
|
||||
document.getElementById('procs').innerHTML = `
|
||||
<div class="prow"><div class="pn"><span class="dot dot-live" style="background:var(--work)"></span>Claude Code <span class="ws">· Competitive Intelligence</span></div><span class="meta">pid 48213</span><span class="meta">12m</span><span class="x">Stop</span></div>`;
|
||||
|
||||
const labels = { a:'<b>Launch</b> — detected agents, one click into this workspace', b:'<b>How memory is shared</b> — the wiring, made legible' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e => {
|
||||
const b = e.target.closest('button'); if(!b) return;
|
||||
[...seg.children].forEach(x=>x.classList.toggle('on', x===b));
|
||||
document.getElementById('view-a').classList.toggle('hidden', b.dataset.v!=='a');
|
||||
document.getElementById('view-b').classList.toggle('hidden', b.dataset.v!=='b');
|
||||
document.getElementById('vlabel').innerHTML = labels[b.dataset.v];
|
||||
document.querySelector('.stage').scrollTop = 0;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,319 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Marketplace</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 13px; border-radius: 7px; transition: .14s; white-space: nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); } .controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
.stage { flex: 1; min-height: 0; overflow: auto; }
|
||||
|
||||
/* ════ A — MARKETPLACE ════ */
|
||||
.wrap { max-width: 960px; margin: 0 auto; padding: 28px 32px 70px; }
|
||||
.hero { text-align: center; margin-bottom: 8px; }
|
||||
.hero h1 { font-size: 28px; font-weight: 650; letter-spacing:-0.02em; margin: 0 0 8px; }
|
||||
.hero h1 em { font-style:normal; color: var(--honey); }
|
||||
.hero p { font-size: 15px; color: var(--text-muted); margin: 0 auto 22px; max-width: 50ch; line-height:1.5; }
|
||||
|
||||
/* agent-search bar */
|
||||
.ask { display:flex; align-items:center; gap:12px; max-width: 620px; margin: 0 auto 12px; padding: 10px 10px 10px 18px; border-radius: 14px; border:1px solid var(--line); background: var(--surface); box-shadow: var(--shadow); }
|
||||
.ask:focus-within { border-color: var(--honey-line); box-shadow: var(--honey-glow); }
|
||||
.ask .sp { width:20px; height:20px; flex:none; } .ask .sp svg { width:20px; height:20px; stroke:var(--honey); fill:none; stroke-width:1.8; }
|
||||
.ask input { flex:1; border:0; background:transparent; color:var(--text); font-family:var(--sans); font-size:15.5px; outline:none; }
|
||||
.ask input::placeholder { color: var(--text-dim); }
|
||||
.ask .go { padding:10px 16px; border-radius:10px; border:0; background:var(--honey); color:#1a1407; font-size:13.5px; font-weight:650; cursor:pointer; display:inline-flex; align-items:center; gap:7px; white-space:nowrap; }
|
||||
.asexamples { display:flex; flex-wrap:wrap; gap:8px; justify-content:center; margin-bottom:8px; }
|
||||
.asx { font-size:12.5px; color:var(--text-muted); padding:6px 13px; border-radius:999px; border:1px solid var(--line-soft); background:var(--surface); cursor:pointer; transition:.14s; }
|
||||
.asx:hover { border-color:var(--honey-line); color:var(--text); }
|
||||
|
||||
/* agent result */
|
||||
.agentbox { max-width: 720px; margin: 18px auto 0; border:1px solid var(--honey-line); border-radius: var(--r-xl); background: linear-gradient(160deg,var(--surface-2),var(--surface)); overflow:hidden; box-shadow: var(--honey-glow); display:none; }
|
||||
.agentbox.show { display:block; }
|
||||
.agentbox .ah { display:flex; align-items:center; gap:11px; padding:15px 18px 11px; }
|
||||
.agentbox .ah .wm { width:28px; height:32px; flex:none; display:grid; place-items:center; font-weight:800; font-size:12px; color:#1a1407; background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.agentbox .ah b { font-size:13.5px; font-weight:650; } .agentbox .ah .sub { font-family:var(--mono); font-size:11px; color:var(--text-dim); margin-left:auto; }
|
||||
.agentbox .arec { padding: 0 18px 14px; font-size:13.5px; color:var(--text-2); line-height:1.55; } .agentbox .arec b { color:var(--text); }
|
||||
.agentbox .picks { padding: 0 12px 12px; display:grid; gap:8px; }
|
||||
.pick { display:flex; align-items:center; gap:13px; padding:12px 14px; border-radius:var(--r); background:var(--surface); border:1px solid var(--line-soft); }
|
||||
.pick .pi { width:32px; height:36px; flex:none; display:grid; place-items:center; font-family:var(--mono); font-size:10px; font-weight:700; color:var(--text-2); background:var(--surface-2); border:1px solid var(--line); }
|
||||
.pick .pt { flex:1; min-width:0; } .pick .pt b { font-size:14px; font-weight:600; } .pick .pt .ps { font-size:12px; color:var(--text-muted); }
|
||||
.pick .why { font-size:11px; color:var(--healthy); background:var(--healthy-wash); padding:3px 8px; border-radius:999px; white-space:nowrap; }
|
||||
.pick .add { font-size:12.5px; font-weight:650; padding:7px 14px; border-radius:9px; border:0; background:var(--honey); color:#1a1407; cursor:pointer; }
|
||||
.pick .add.added { background:var(--healthy-wash); color:var(--healthy); }
|
||||
|
||||
/* category filter + grid */
|
||||
.cats { display:flex; gap:7px; justify-content:center; flex-wrap:wrap; margin: 26px 0 20px; }
|
||||
.cat { font-size:12.5px; font-weight:600; color:var(--text-muted); padding:8px 14px; border-radius:999px; border:1px solid var(--line-soft); background:var(--surface); cursor:pointer; transition:.14s; }
|
||||
.cat:hover { color:var(--text); } .cat.on { color:var(--text); border-color:var(--honey-line); background:var(--honey-wash); }
|
||||
.mgrid { display:grid; grid-template-columns: repeat(3,1fr); gap:14px; }
|
||||
.mcard { padding:18px; border-radius:var(--r-lg); border:1px solid var(--line-soft); background:var(--surface); transition:.16s; }
|
||||
.mcard:hover { border-color:var(--honey-line); transform:translateY(-2px); box-shadow:var(--shadow); }
|
||||
.mcard .top { display:flex; align-items:center; gap:11px; margin-bottom:11px; }
|
||||
.mcard .ci { width:36px; height:40px; flex:none; display:grid; place-items:center; font-family:var(--mono); font-size:12px; font-weight:700; color:var(--text-2); background:var(--surface-2); border:1px solid var(--line); }
|
||||
.mcard .ck { font-family:var(--mono); font-size:9.5px; letter-spacing:.08em; text-transform:uppercase; padding:3px 8px; border-radius:6px; margin-left:auto; }
|
||||
.mcard .ck.skill { color:var(--intel); background:var(--intel-wash); } .mcard .ck.conn { color:var(--healthy); background:var(--healthy-wash); } .mcard .ck.mcp { color:var(--work); background:var(--work-wash); }
|
||||
.mcard h3 { font-size:15px; font-weight:650; letter-spacing:-0.01em; margin:0 0 6px; }
|
||||
.mcard p { font-size:12.5px; color:var(--text-muted); line-height:1.5; margin:0 0 14px; min-height:36px; }
|
||||
.mcard .mf { display:flex; align-items:center; justify-content:space-between; }
|
||||
.mcard .inst { font-family:var(--mono); font-size:11px; color:var(--text-dim); }
|
||||
.mcard .add { font-size:12px; font-weight:650; padding:7px 13px; border-radius:8px; border:1px solid var(--line-strong); background:var(--surface-2); color:var(--text-2); cursor:pointer; transition:.14s; }
|
||||
.mcard .add:hover { border-color:var(--honey-line); color:var(--honey); }
|
||||
.mcard .add.added { background:var(--healthy-wash); color:var(--healthy); border-color:transparent; }
|
||||
/* install states (shared by grid + picks) */
|
||||
.add.installing, .pick .add.installing { background:var(--honey-wash); color:var(--honey); border-color:var(--honey-line); cursor:default; }
|
||||
.add.done, .pick .add.done { background:var(--healthy-wash); color:var(--healthy); border-color:transparent; cursor:default; }
|
||||
.pick .add.installing { background:var(--surface-2); }
|
||||
.spin { width:11px; height:11px; border-radius:999px; border:2px solid currentColor; border-top-color:transparent; display:inline-block; margin-right:6px; vertical-align:-1px; animation:spin .7s linear infinite; }
|
||||
@keyframes spin { to { transform:rotate(360deg); } }
|
||||
/* installed-in-this-workspace bar */
|
||||
.installed-bar { display:flex; align-items:center; gap:14px; max-width:720px; margin:18px auto 0; padding:11px 16px; border-radius:var(--r-lg); background:var(--bg-2); border:1px solid var(--line-soft); }
|
||||
.ib-count { font-family:var(--mono); font-size:12px; font-weight:600; color:var(--honey); background:var(--honey-wash); border:1px solid var(--honey-line); padding:4px 10px; border-radius:999px; white-space:nowrap; }
|
||||
.ib-chips { flex:1; display:flex; gap:6px; flex-wrap:wrap; min-width:0; }
|
||||
.ib-chip { display:inline-flex; align-items:center; gap:7px; font-size:11.5px; color:var(--text-2); padding:4px 10px; border-radius:999px; background:var(--surface); border:1px solid var(--line-soft); }
|
||||
.ib-chip .ibd { width:6px; height:6px; border-radius:999px; }
|
||||
.ib-chip .ibd.skill { background:var(--intel); } .ib-chip .ibd.conn { background:var(--healthy); } .ib-chip .ibd.mcp { background:var(--work); }
|
||||
.ib-link { font-size:12.5px; font-weight:650; color:var(--honey); text-decoration:none; white-space:nowrap; }
|
||||
.ib-link:hover { color:var(--honey-bright); }
|
||||
/* toast */
|
||||
.toast { position:fixed; bottom:22px; left:50%; transform:translateX(-50%) translateY(18px); opacity:0; pointer-events:none; display:flex; align-items:center; gap:10px; padding:12px 18px; border-radius:12px; background:var(--surface); border:1px solid var(--healthy); box-shadow:var(--shadow-lg); font-size:13.5px; color:var(--text); z-index:80; transition:.28s cubic-bezier(.16,1,.3,1); max-width:90%; }
|
||||
.toast.show { opacity:1; transform:translateX(-50%) translateY(0); }
|
||||
.toast .td { width:8px; height:8px; border-radius:999px; background:var(--healthy); flex:none; }
|
||||
.toast b { font-weight:650; }
|
||||
|
||||
/* ════ B — INLINE IN CHAT ════ */
|
||||
.chatwrap { max-width: 720px; margin: 0 auto; padding: 28px 32px 60px; }
|
||||
.chatintro { text-align:center; margin-bottom:22px; }
|
||||
.chatintro h1 { font-size:24px; font-weight:650; letter-spacing:-0.02em; margin:0 0 8px; } .chatintro h1 em { font-style:normal; color:var(--honey); }
|
||||
.chatintro p { font-size:14px; color:var(--text-muted); margin:0; }
|
||||
.msg { display:flex; gap:13px; margin-bottom:22px; }
|
||||
.msg .av { width:30px; height:34px; flex:none; display:grid; place-items:center; font-size:12px; font-weight:700; }
|
||||
.msg.user .av { background:var(--surface-3); color:var(--text-2); border-radius:9px; }
|
||||
.msg.bot .av { background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); color:#1a1407; }
|
||||
.msg .body { flex:1; min-width:0; } .msg .who { font-size:12px; color:var(--text-dim); margin-bottom:7px; }
|
||||
.msg.user .bubble { font-size:15px; line-height:1.55; color:var(--text); background:var(--surface); border:1px solid var(--line-soft); padding:12px 16px; border-radius:4px 14px 14px 14px; }
|
||||
.msg.bot .prose { font-size:15px; line-height:1.6; color:var(--text); } .msg.bot .prose p { margin:0 0 12px; }
|
||||
.inlinecard { border:1px solid var(--line); border-radius:var(--r-lg); background:var(--bg-2); padding:14px; margin-top:4px; }
|
||||
.inlinecard .ih { font-family:var(--mono); font-size:10.5px; letter-spacing:.1em; text-transform:uppercase; color:var(--healthy); margin-bottom:11px; display:flex; align-items:center; gap:8px; }
|
||||
.inlinecard .pick { background:var(--surface); }
|
||||
.approve-inline { display:flex; align-items:center; gap:10px; margin-top:12px; padding-top:12px; border-top:1px solid var(--line-soft); }
|
||||
.approve-inline span { font-size:12px; color:var(--text-muted); flex:1; }
|
||||
.approve-inline button { font-size:12.5px; font-weight:650; padding:8px 15px; border-radius:9px; border:0; cursor:pointer; }
|
||||
.approve-inline .yes { background:var(--honey); color:#1a1407; } .approve-inline .no { background:var(--surface-2); color:var(--text-2); border:1px solid var(--line-strong); }
|
||||
|
||||
.hidden { display:none !important; }
|
||||
@media (max-width:820px){ .mgrid { grid-template-columns:1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Marketplace · variation</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="a">A · Browse & ask</button>
|
||||
<button data-v="b">B · Inline in chat</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>One place</b> — skills, connectors & tools, agent-searchable</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<!-- ════ A ════ -->
|
||||
<div class="wrap view" id="view-a">
|
||||
<div class="hero">
|
||||
<h1>Add a <em>capability.</em></h1>
|
||||
<p>Skills, connectors, and tools — one shelf. Don't know what you need? Describe the job and let the agent pick.</p>
|
||||
</div>
|
||||
|
||||
<div class="ask">
|
||||
<span class="sp"><svg viewBox="0 0 24 24"><path d="M12 3v3M12 18v3M5 12H2M22 12h-3M6 6 4 4M20 20l-2-2M6 18l-2 2M20 4l-2 2"/><circle cx="12" cy="12" r="3.5"/></svg></span>
|
||||
<input id="askInput" placeholder="Describe what you want to do…">
|
||||
<button class="go" id="askGo">Ask the agent</button>
|
||||
</div>
|
||||
<div class="asexamples" id="asex"></div>
|
||||
|
||||
<div class="agentbox" id="agentbox">
|
||||
<div class="ah"><div class="wm hex">W</div><b>Waggle suggests</b><span class="sub">3 of 412 · matched to your ask</span></div>
|
||||
<div class="arec" id="arec"></div>
|
||||
<div class="picks" id="picks"></div>
|
||||
</div>
|
||||
|
||||
<div class="installed-bar" id="instBar">
|
||||
<span class="ib-count"><span id="ibCount">0</span> in this workspace</span>
|
||||
<div class="ib-chips" id="ibChips"></div>
|
||||
<a class="ib-link" href="surfaces.html">Manage in Tools →</a>
|
||||
</div>
|
||||
|
||||
<div class="cats" id="cats"></div>
|
||||
<div class="mgrid" id="mgrid"></div>
|
||||
</div>
|
||||
|
||||
<!-- ════ B ════ -->
|
||||
<div class="chatwrap view hidden" id="view-b">
|
||||
<div class="chatintro">
|
||||
<h1>Or just <em>ask in the chat.</em></h1>
|
||||
<p>The same marketplace, surfaced inline the moment you need it — no context-switch.</p>
|
||||
</div>
|
||||
|
||||
<div class="msg user"><div class="av">You</div><div class="body"><div class="who">You</div><div class="bubble">Can you pull our open deals from Salesforce and summarize them?</div></div></div>
|
||||
|
||||
<div class="msg bot"><div class="av hex">W</div><div class="body"><div class="who">Waggle</div>
|
||||
<div class="prose"><p>I can — but I don't have a <b>Salesforce</b> connection yet. Here's the official one; it covers reading deals and contacts. Want me to add it?</p></div>
|
||||
<div class="inlinecard">
|
||||
<div class="ih">⬡ from the marketplace · 1 match</div>
|
||||
<div class="pick">
|
||||
<div class="pi">SF</div>
|
||||
<div class="pt"><b>Salesforce</b><div class="ps">CRM connector · read deals, contacts, pipeline · official</div></div>
|
||||
<span class="why">covers your ask</span>
|
||||
</div>
|
||||
<div class="approve-inline" id="inlineApprove">
|
||||
<span>Adds the connector to this workspace. You'll sign in to Salesforce; the token goes to your vault.</span>
|
||||
<button class="no">Not now</button>
|
||||
<button class="yes">Add & connect</button>
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<div class="msg bot" style="opacity:.6"><div class="av hex">W</div><div class="body"><div class="who">Waggle · after you approve</div>
|
||||
<div class="prose"><p>Connected. Pulling your open deals now — 23 found, summarizing by stage…</p></div>
|
||||
</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const items = [
|
||||
{ id:'sf', mono:'SF', k:'conn', cat:'Connectors', n:'Salesforce', d:'Read deals, contacts, and pipeline from your CRM.', inst:'12k installs' },
|
||||
{ id:'notion', mono:'No', k:'conn', cat:'Connectors', n:'Notion', d:'Search and write docs, wikis, and databases.', inst:'31k installs' },
|
||||
{ id:'slack', mono:'Sl', k:'conn', cat:'Connectors', n:'Slack', d:'Post updates and read channels your agent watches.', inst:'44k installs' },
|
||||
{ id:'teardown', mono:'★', k:'skill', cat:'Skills', n:'Competitor teardown', d:'Structured rival analysis with pricing pull and citations.', inst:'self-evolving' },
|
||||
{ id:'brief', mono:'★', k:'skill', cat:'Skills', n:'Board brief writer', d:'Turn a workspace into a tight one-page board narrative.', inst:'8k installs' },
|
||||
{ id:'interview', mono:'★', k:'skill', cat:'Skills', n:'Interview synthesizer', d:'Theme and quote-mine user interviews at scale.', inst:'6k installs' },
|
||||
{ id:'fs', mono:'fs', k:'mcp', cat:'MCP servers', n:'Filesystem', d:'Let agents read and write workspace files safely.', inst:'official' },
|
||||
{ id:'web', mono:'web', k:'mcp', cat:'MCP servers', n:'Web search', d:'Live retrieval from the open web during a task.', inst:'official' },
|
||||
{ id:'gh', mono:'Gh', k:'conn', cat:'Connectors', n:'GitHub', d:'Issues, PRs, and repo context for engineering work.', inst:'28k installs' },
|
||||
];
|
||||
const KLABEL = { skill:'skill', conn:'connector', mcp:'MCP' };
|
||||
// Per-type one-click verbs: [idle, in-progress, done] — each install does the right thing.
|
||||
const VERB = { skill:['Add','Adding…','Added'], conn:['Connect','Signing in…','Connected'], mcp:['Enable','Enabling…','Enabled'] };
|
||||
|
||||
/* ── Single source of truth: install state shared by grid, picks & chat ── */
|
||||
const installed = new Set(['fs','web','gh']); // already in this workspace (matches the Tools surface)
|
||||
const installing = new Set();
|
||||
|
||||
function btnHTML(it, cls){
|
||||
const v = VERB[it.k];
|
||||
if(installed.has(it.id)) return `<button class="${cls} done" data-id="${it.id}" disabled>✓ ${v[2]}</button>`;
|
||||
if(installing.has(it.id)) return `<button class="${cls} installing" data-id="${it.id}" disabled><span class="spin"></span>${v[1]}</button>`;
|
||||
return `<button class="${cls}" data-id="${it.id}">+ ${v[0]}</button>`;
|
||||
}
|
||||
|
||||
function toast(it){
|
||||
const t = document.getElementById('toast');
|
||||
const verb = it.k==='conn'?'connected':it.k==='mcp'?'enabled':'added';
|
||||
t.innerHTML = `<span class="td"></span><span><b>${it.n}</b> ${verb} · now available to your agents here</span>`;
|
||||
t.classList.add('show'); clearTimeout(t._t); t._t = setTimeout(()=>t.classList.remove('show'), 2700);
|
||||
}
|
||||
|
||||
/* One-click install — connector signs in (longer), MCP enables tools, skill is instant. Then SYNC everywhere. */
|
||||
function install(id){
|
||||
const it = items.find(x=>x.id===id);
|
||||
if(!it || installed.has(id) || installing.has(id)) return;
|
||||
installing.add(id); renderAll();
|
||||
const ms = it.k==='skill' ? 480 : it.k==='mcp' ? 720 : 1100;
|
||||
setTimeout(()=>{ installing.delete(id); installed.add(id); renderAll(); toast(it); }, ms);
|
||||
}
|
||||
|
||||
let cat = 'All';
|
||||
const cats = ['All','Skills','Connectors','MCP servers'];
|
||||
document.getElementById('cats').innerHTML = cats.map(c=>`<span class="cat ${c==='All'?'on':''}" data-c="${c}">${c}</span>`).join('');
|
||||
|
||||
function renderGrid(){
|
||||
const list = items.filter(it => cat==='All' || it.cat===cat);
|
||||
document.getElementById('mgrid').innerHTML = list.map(it=>`
|
||||
<div class="mcard">
|
||||
<div class="top"><div class="ci">${it.mono}</div><span class="ck ${it.k}">${KLABEL[it.k]}</span></div>
|
||||
<h3>${it.n}</h3><p>${it.d}</p>
|
||||
<div class="mf"><span class="inst">${it.inst}</span>${btnHTML(it,'add')}</div>
|
||||
</div>`).join('');
|
||||
}
|
||||
function renderPicks(){
|
||||
if(!currentPicks) return;
|
||||
document.getElementById('picks').innerHTML = currentPicks.map(id=>{ const it=items.find(x=>x.id===id); return `
|
||||
<div class="pick"><div class="pi">${it.mono}</div><div class="pt"><b>${it.n}</b><div class="ps">${KLABEL[it.k]} · ${it.d}</div></div><span class="why">why: ${it.k==='conn'?'reaches your data':it.k==='skill'?'does the work':'needed tool'}</span>${btnHTML(it,'add')}</div>`; }).join('');
|
||||
}
|
||||
function renderBar(){
|
||||
document.getElementById('ibCount').textContent = installed.size;
|
||||
document.getElementById('ibChips').innerHTML = [...installed].map(id=>{ const it=items.find(x=>x.id===id); return `<span class="ib-chip"><span class="ibd ${it.k}"></span>${it.n}</span>`; }).join('');
|
||||
}
|
||||
function renderAll(){ renderGrid(); renderPicks(); renderBar(); renderInline(); }
|
||||
|
||||
renderGrid(); renderBar();
|
||||
document.getElementById('cats').addEventListener('click', e=>{ const c=e.target.closest('.cat'); if(!c)return; cat=c.dataset.c; document.querySelectorAll('.cat').forEach(x=>x.classList.toggle('on',x===c)); renderGrid(); });
|
||||
|
||||
// one delegated handler powers every install button on the page → guaranteed in sync
|
||||
document.addEventListener('click', e=>{ const b=e.target.closest('button.add[data-id]'); if(b && !b.disabled) install(b.dataset.id); });
|
||||
|
||||
// agent search
|
||||
let currentPicks = null;
|
||||
const examples = ['Summarize our Salesforce pipeline','Analyze 3 competitors','Theme our user interviews','Post a daily digest to Slack'];
|
||||
document.getElementById('asex').innerHTML = examples.map(x=>`<span class="asx">${x}</span>`).join('');
|
||||
const recs = {
|
||||
default: { rec:'Based on “<b>__Q__</b>”, these three cover it — a connector to reach your data, a skill to do the analysis, and the tool they need.', picks:['sf','teardown','web'] },
|
||||
interview: { rec:'For interview work, you mainly need the <b>synthesizer skill</b>; it works on files already in your workspace.', picks:['interview','fs'] },
|
||||
slack: { rec:'To post to Slack you need the <b>Slack connector</b>; pair it with an automation to send on a schedule.', picks:['slack'] },
|
||||
};
|
||||
function runAsk(q){
|
||||
const ql = q.toLowerCase();
|
||||
let r = recs.default;
|
||||
if(ql.includes('interview')) r = recs.interview;
|
||||
else if(ql.includes('slack') || ql.includes('digest')) r = recs.slack;
|
||||
document.getElementById('arec').innerHTML = r.rec.replace('__Q__', q || 'your task');
|
||||
currentPicks = r.picks;
|
||||
renderPicks();
|
||||
document.getElementById('agentbox').classList.add('show');
|
||||
}
|
||||
document.getElementById('askGo').onclick = () => runAsk(document.getElementById('askInput').value);
|
||||
document.getElementById('askInput').addEventListener('keydown', e=>{ if(e.key==='Enter') runAsk(e.target.value); });
|
||||
document.getElementById('asex').addEventListener('click', e=>{ const x=e.target.closest('.asx'); if(x){ document.getElementById('askInput').value=x.textContent; runAsk(x.textContent); } });
|
||||
|
||||
// inline-in-chat install — shares the SAME state, so adding here updates the browse view too
|
||||
function renderInline(){
|
||||
const wrap = document.getElementById('inlineApprove'); if(!wrap) return;
|
||||
if(installed.has('sf')) wrap.innerHTML = '<span style="color:var(--healthy)">✓ Salesforce connected — token saved to your vault. Pulling your open deals…</span>';
|
||||
else if(installing.has('sf')) wrap.innerHTML = '<span style="color:var(--honey)"><span class="spin"></span>Signing in to Salesforce…</span>';
|
||||
}
|
||||
document.querySelector('#view-b .approve-inline .yes')?.addEventListener('click', ()=> install('sf'));
|
||||
|
||||
// variation switch
|
||||
const labels = { a:'<b>Browse & ask</b> — one shelf, the agent picks for you', b:'<b>Inline in chat</b> — surfaced the moment you need it' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e => {
|
||||
const b = e.target.closest('button'); if(!b) return;
|
||||
[...seg.children].forEach(x=>x.classList.toggle('on', x===b));
|
||||
document.getElementById('view-a').classList.toggle('hidden', b.dataset.v!=='a');
|
||||
document.getElementById('view-b').classList.toggle('hidden', b.dataset.v!=='b');
|
||||
document.getElementById('vlabel').innerHTML = labels[b.dataset.v];
|
||||
document.querySelector('.stage').scrollTop = 0;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,227 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Memory Trust</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height:100vh; overflow:hidden; display:flex; flex-direction:column; }
|
||||
.controls { flex:none; display:flex; align-items:center; gap:14px; padding:11px 20px; border-bottom:1px solid var(--line-soft); background:color-mix(in srgb,var(--bg) 88%,transparent); backdrop-filter:blur(10px); z-index:30; }
|
||||
.controls .lab { font-family:var(--mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); }
|
||||
.seg { display:inline-flex; padding:3px; gap:3px; border-radius:10px; background:var(--surface-2); border:1px solid var(--line-soft); }
|
||||
.seg button { font-family:var(--sans); font-size:12.5px; font-weight:600; cursor:pointer; border:0; background:transparent; color:var(--text-muted); padding:6px 13px; border-radius:7px; transition:.14s; white-space:nowrap; }
|
||||
.seg button.on { background:var(--honey); color:#1a1407; }
|
||||
.controls .vlabel { font-size:12.5px; color:var(--text-muted); } .controls .vlabel b { color:var(--text); font-weight:600; }
|
||||
.controls .right { margin-left:auto; display:flex; align-items:center; gap:10px; }
|
||||
.tbtn { width:34px; height:34px; border-radius:9px; border:1px solid var(--line); background:var(--surface); color:var(--text-2); cursor:pointer; }
|
||||
.tbtn:hover { border-color:var(--honey-line); color:var(--honey); }
|
||||
.stage { flex:1; min-height:0; overflow:auto; }
|
||||
.wrap { max-width:920px; margin:0 auto; padding:30px 32px 70px; }
|
||||
.view { display:none; } .view.on { display:block; }
|
||||
|
||||
.head { margin-bottom:22px; }
|
||||
.head .eyebrow { font-family:var(--mono); font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--honey); margin-bottom:12px; display:flex; align-items:center; gap:9px; } .head .eyebrow::before { content:''; width:20px; height:1px; background:var(--honey-line); }
|
||||
.head h1 { font-size:28px; font-weight:650; letter-spacing:-0.02em; margin:0 0 10px; } .head h1 em { font-style:normal; color:var(--honey); }
|
||||
.head p { font-size:15px; color:var(--text-muted); line-height:1.55; margin:0; max-width:64ch; } .head p b { color:var(--text-2); }
|
||||
|
||||
/* trust summary bar */
|
||||
.tsum { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:24px; }
|
||||
.ts { padding:16px; border-radius:var(--r-lg); border:1px solid var(--line-soft); background:var(--surface); }
|
||||
.ts .v { font-size:24px; font-weight:750; letter-spacing:-0.02em; } .ts .l { font-size:11.5px; color:var(--text-muted); margin-top:5px; }
|
||||
.ts.warn { border-color:color-mix(in srgb,var(--attention) 35%,transparent); } .ts.warn .v { color:var(--attention); }
|
||||
|
||||
.toolbar { display:flex; align-items:center; gap:10px; margin-bottom:16px; flex-wrap:wrap; }
|
||||
.search { flex:1; min-width:200px; display:flex; align-items:center; gap:10px; padding:9px 14px; border-radius:11px; border:1px solid var(--line); background:var(--surface); }
|
||||
.search svg { width:16px; height:16px; stroke:var(--text-dim); fill:none; stroke-width:1.9; }
|
||||
.search input { flex:1; border:0; background:transparent; color:var(--text); font-family:var(--sans); font-size:14px; outline:none; }
|
||||
.filt { font-size:12.5px; font-weight:600; color:var(--text-muted); padding:8px 13px; border-radius:9px; border:1px solid var(--line-soft); background:var(--surface); cursor:pointer; transition:.14s; }
|
||||
.filt:hover { color:var(--text); } .filt.on { color:var(--text); border-color:var(--honey-line); background:var(--honey-wash); }
|
||||
|
||||
/* memory rows */
|
||||
.mems { display:grid; gap:10px; }
|
||||
.mem { border:1px solid var(--line-soft); background:var(--surface); border-radius:var(--r-lg); padding:16px 18px; transition:.15s; }
|
||||
.mem.stale { border-color:color-mix(in srgb,var(--attention) 30%,var(--line-soft)); }
|
||||
.mem.disputed { border-color:color-mix(in srgb,var(--risk) 30%,var(--line-soft)); opacity:.92; }
|
||||
.mem .top { display:flex; align-items:flex-start; gap:13px; }
|
||||
.mem .conf { width:42px; flex:none; text-align:center; }
|
||||
.mem .conf .ring { width:38px; height:38px; border-radius:999px; display:grid; place-items:center; font-family:var(--mono); font-size:11px; font-weight:600; }
|
||||
.mem .conf .cl { font-size:9px; color:var(--text-dim); font-family:var(--mono); margin-top:4px; text-transform:uppercase; letter-spacing:.06em; }
|
||||
.mem .mbody { flex:1; min-width:0; }
|
||||
.mem .mtext { font-size:14.5px; line-height:1.5; color:var(--text); } .mem .mtext b { font-weight:650; }
|
||||
.mem.disputed .mtext { text-decoration:line-through; text-decoration-color:color-mix(in srgb,var(--risk) 60%,transparent); color:var(--text-muted); }
|
||||
.mem .prov { margin-top:8px; display:flex; flex-wrap:wrap; gap:7px 14px; align-items:center; font-family:var(--mono); font-size:10.5px; color:var(--text-dim); }
|
||||
.mem .prov .src { color:var(--intel); } .mem .prov .fresh.ok { color:var(--healthy); } .mem .prov .fresh.old { color:var(--attention); }
|
||||
.mem .acts { display:flex; gap:6px; align-items:center; flex:none; }
|
||||
.mact { width:30px; height:30px; border-radius:8px; border:1px solid var(--line-soft); background:var(--surface-2); color:var(--text-muted); cursor:pointer; display:grid; place-items:center; transition:.14s; }
|
||||
.mact:hover { border-color:var(--honey-line); color:var(--honey); }
|
||||
.mact.danger:hover { border-color:color-mix(in srgb,var(--risk) 45%,transparent); color:var(--risk); }
|
||||
.mact svg { width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.8; }
|
||||
.corrected { margin-top:10px; padding:9px 13px; border-radius:9px; background:var(--healthy-wash); border:1px solid color-mix(in srgb,var(--healthy) 30%,transparent); font-size:12.5px; color:var(--text-2); display:flex; gap:9px; align-items:center; }
|
||||
.corrected svg { width:14px; height:14px; stroke:var(--healthy); fill:none; stroke-width:2; flex:none; }
|
||||
.corrected b { color:var(--text); }
|
||||
.stalebanner { margin-top:10px; padding:9px 13px; border-radius:9px; background:var(--honey-wash); border:1px solid var(--honey-line); font-size:12.5px; color:var(--text-2); display:flex; gap:9px; align-items:center; }
|
||||
.stalebanner svg { width:14px; height:14px; stroke:var(--honey); fill:none; stroke-width:2; flex:none; }
|
||||
.stalebanner .sp { margin-left:auto; display:flex; gap:6px; }
|
||||
.sbtn { font-size:11.5px; font-weight:650; padding:5px 11px; border-radius:7px; cursor:pointer; border:1px solid transparent; }
|
||||
.sbtn.go { background:var(--honey); color:#1a1407; } .sbtn.ghost { background:var(--surface); color:var(--text-2); border-color:var(--line-strong); }
|
||||
|
||||
/* why-trace (variation B) */
|
||||
.trace { border:1px solid var(--line); border-radius:var(--r-xl); background:var(--bg-2); overflow:hidden; }
|
||||
.trace .th { display:flex; align-items:center; gap:12px; padding:18px 22px; border-bottom:1px solid var(--line-soft); }
|
||||
.trace .th .ti { width:34px; height:38px; flex:none; display:grid; place-items:center; color:#1a1407; background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.trace .th .ti svg { width:18px; height:18px; stroke:#1a1407; fill:none; stroke-width:2; }
|
||||
.trace .th b { font-size:15.5px; font-weight:650; } .trace .th .sub { font-size:12px; color:var(--text-muted); }
|
||||
.trace .th .when { margin-left:auto; font-family:var(--mono); font-size:11px; color:var(--text-dim); }
|
||||
.tchain { padding:8px 22px 18px; }
|
||||
.tnode { display:grid; grid-template-columns:24px 1fr; gap:14px; padding:14px 0; position:relative; }
|
||||
.tnode::before { content:''; position:absolute; left:11px; top:34px; bottom:-14px; width:1.5px; background:var(--line); }
|
||||
.tnode:last-child::before { display:none; }
|
||||
.tnode .tdot { width:24px; height:24px; border-radius:999px; display:grid; place-items:center; flex:none; z-index:1; }
|
||||
.tnode .tdot svg { width:13px; height:13px; stroke:#1a1407; fill:none; stroke-width:2.4; }
|
||||
.tnode .tc b { font-size:13.5px; font-weight:600; } .tnode .tc p { margin:4px 0 0; font-size:13px; color:var(--text-muted); line-height:1.5; } .tnode .tc p .mono { font-family:var(--mono); font-size:11.5px; color:var(--text-dim); } .tnode .tc p .src { color:var(--intel); font-family:var(--mono); font-size:11px; }
|
||||
.tnode .tc .ev { margin-top:7px; font-size:12px; color:var(--text-2); padding:8px 12px; border-radius:8px; background:var(--surface); border:1px solid var(--line-soft); }
|
||||
.traceact { display:flex; gap:9px; padding:16px 22px; border-top:1px solid var(--line-soft); background:var(--surface); }
|
||||
.tbtn2 { font-size:12.5px; font-weight:650; padding:9px 15px; border-radius:9px; cursor:pointer; border:1px solid transparent; }
|
||||
.tbtn2.go { background:var(--honey); color:#1a1407; } .tbtn2.ghost { background:var(--surface-2); color:var(--text-2); border-color:var(--line-strong); } .tbtn2.danger { background:transparent; color:var(--risk); border-color:color-mix(in srgb,var(--risk) 35%,transparent); }
|
||||
|
||||
.principle { margin-top:24px; display:flex; gap:13px; align-items:flex-start; padding:18px 20px; border-radius:var(--r-lg); background:var(--bg-2); border:1px solid var(--line-soft); }
|
||||
.principle svg { width:20px; height:20px; stroke:var(--healthy); fill:none; stroke-width:1.8; flex:none; margin-top:1px; }
|
||||
.principle p { margin:0; font-size:13.5px; color:var(--text-muted); line-height:1.6; } .principle b { color:var(--text); }
|
||||
|
||||
.toast { position:fixed; bottom:22px; left:50%; transform:translateX(-50%) translateY(18px); opacity:0; pointer-events:none; display:flex; align-items:center; gap:10px; padding:12px 18px; border-radius:12px; background:var(--surface); border:1px solid var(--healthy); box-shadow:var(--shadow-lg); font-size:13.5px; color:var(--text); z-index:80; transition:.28s cubic-bezier(.16,1,.3,1); }
|
||||
.toast.show { opacity:1; transform:translateX(-50%) translateY(0); }
|
||||
.toast .td2 { width:8px; height:8px; border-radius:999px; background:var(--healthy); flex:none; }
|
||||
@media (max-width:820px){ .tsum { grid-template-columns:1fr 1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Memory Trust · view</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="manage">Manage memory</button>
|
||||
<button data-v="why">Why did you do that?</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Manage</b> — forget, correct, confirm; see confidence & freshness</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<!-- ════ A — MANAGE ════ -->
|
||||
<div class="view on wrap" data-view="manage">
|
||||
<div class="head">
|
||||
<div class="eyebrow">Trust · the thing that makes you stay</div>
|
||||
<h1>Memory you can <em>correct, age, and forget.</em></h1>
|
||||
<p>A memory that only grows is a liability. Waggle shows you <b>how sure it is</b>, <b>how fresh it is</b>, and <b>where it came from</b> — and lets you fix or forget anything. You're always in control of what the hive believes.</p>
|
||||
</div>
|
||||
|
||||
<div class="tsum">
|
||||
<div class="ts"><div class="v">142</div><div class="l">Memories in this hive</div></div>
|
||||
<div class="ts"><div class="v" style="color:var(--healthy)">128</div><div class="l">High confidence & fresh</div></div>
|
||||
<div class="ts warn"><div class="v">9</div><div class="l">Stale · worth a review</div></div>
|
||||
<div class="ts warn"><div class="v">3</div><div class="l">Awaiting your confirm</div></div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="search"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg><input placeholder="Search what Waggle knows… or ask it to forget something"></div>
|
||||
<span class="filt on">All</span><span class="filt">Stale</span><span class="filt">Needs confirm</span><span class="filt">Forgotten</span>
|
||||
</div>
|
||||
|
||||
<div class="mems" id="mems"></div>
|
||||
|
||||
<div class="principle">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/><path d="M9 12l2 2 4-4"/></svg>
|
||||
<p><b>Nothing is remembered behind your back.</b> Every memory is inspectable, editable, and forgettable — and forgetting is real: it's removed from recall and from anything Waggle says next. Confidence and freshness are shown so the agent (and you) can discount what's old or shaky instead of acting on it blindly.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════ B — WHY TRACE ════ -->
|
||||
<div class="view wrap" data-view="why">
|
||||
<div class="head">
|
||||
<div class="eyebrow">Provenance · accountability</div>
|
||||
<h1>Ask the agent <em>“why did you do that?”</em></h1>
|
||||
<p>Any action an agent takes can be traced back to the exact memories and sources behind it — so a wrong move is <b>diagnosable, not mysterious</b>. If a bad memory caused it, fix the memory right from the trace.</p>
|
||||
</div>
|
||||
|
||||
<div class="trace">
|
||||
<div class="th">
|
||||
<div class="ti hex"><svg viewBox="0 0 24 24"><path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/></svg></div>
|
||||
<div><b>Drafted the board brief around “regulated industries”</b><div class="sub">Deck-builder · 18m ago · Q2 Board Deck</div></div>
|
||||
<span class="when">trace #a1f9</span>
|
||||
</div>
|
||||
<div class="tchain">
|
||||
<div class="tnode"><div class="tdot" style="background:var(--intel)"><svg viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></svg></div><div class="tc"><b>Goal received</b><p>You asked: <span class="mono">“tighten the board narrative.”</span></p></div></div>
|
||||
<div class="tnode"><div class="tdot" style="background:var(--honey)"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></div><div class="tc"><b>Recalled 3 memories</b><p>Strongest was: <span class="src">⬡ mem #M-204</span> — “Mara wants market work to lead with the regulated-industries angle.”</p><div class="ev">confidence 94% · source: chat · Tue · still fresh</div></div></div>
|
||||
<div class="tnode"><div class="tdot" style="background:var(--honey)"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></div><div class="tc"><b>Cross-checked the teardown</b><p>Confirmed 2 of 3 proof points cite customer quotes <span class="src">⬡ teardown.md</span></p></div></div>
|
||||
<div class="tnode"><div class="tdot" style="background:var(--healthy)"><svg viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></svg></div><div class="tc"><b>Acted</b><p>Wrote slide 6 around the regulated angle and flagged it for your review.</p></div></div>
|
||||
</div>
|
||||
<div class="traceact">
|
||||
<button class="tbtn2 go">Looks right</button>
|
||||
<button class="tbtn2 ghost">That memory is wrong → correct it</button>
|
||||
<button class="tbtn2 danger">Forget #M-204 & redo</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="principle">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 8h.01M11 12h1v4h1"/></svg>
|
||||
<p><b>Every agent action keeps its trace.</b> The chain from goal → recalled memories → checks → action is stored with the result, so "why did you do that?" always has an answer — and the fix (correct or forget the offending memory) is one click from the explanation.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast"><span class="td2"></span><span id="toastText"></span></div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
function confColor(c){ return c>=85?'var(--healthy)':c>=60?'var(--attention)':'var(--risk)'; }
|
||||
const mems = [
|
||||
{ id:'M-204', text:'Mara wants market work to <b>lead with the regulated-industries angle</b>.', conf:94, src:'chat · Tue', fresh:'fresh', state:'ok' },
|
||||
{ id:'M-198', text:'<b>Mem0</b> is cloud-only and raised prices ~15% in March.', conf:88, src:'web · mem0.ai', fresh:'fresh', state:'ok' },
|
||||
{ id:'M-141', text:'The Q3 launch date is <b>September 12</b>.', conf:61, src:'chat · 6 weeks ago', fresh:'old', state:'stale' },
|
||||
{ id:'M-088', text:'Mara prefers <b>Slack over email</b> for updates.', conf:47, src:'inferred · once', fresh:'old', state:'disputed', corrected:'Corrected by you → prefers a daily digest, not Slack pings.' },
|
||||
{ id:'M-052', text:'Primary competitor is <b>Letta</b>.', conf:90, src:'teardown.md', fresh:'fresh', state:'ok' },
|
||||
];
|
||||
const FRESH = { fresh:['ok','fresh'], old:['old','aging'] };
|
||||
function row(m){
|
||||
const stale = m.state==='stale', disp = m.state==='disputed';
|
||||
return `<div class="mem ${m.state}">
|
||||
<div class="top">
|
||||
<div class="conf"><div class="ring" style="color:${confColor(m.conf)};border:2px solid ${confColor(m.conf)}">${m.conf}</div><div class="cl">conf</div></div>
|
||||
<div class="mbody">
|
||||
<div class="mtext">${m.text}</div>
|
||||
<div class="prov"><span><span class="src">⬡ ${m.id}</span></span><span>source: ${m.src}</span><span class="fresh ${m.fresh==='fresh'?'ok':'old'}">● ${m.fresh==='fresh'?'fresh':'aging — last seen 6w ago'}</span></div>
|
||||
${disp&&m.corrected?`<div class="corrected"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg><span><b>${m.corrected.split('→')[0].trim()}</b> → ${m.corrected.split('→')[1].trim()}</span></div>`:''}
|
||||
${stale?`<div class="stalebanner"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg><span>This is 6 weeks old — still true?</span><span class="sp"><button class="sbtn go" data-act="confirm" data-id="${m.id}">Still true</button><button class="sbtn ghost" data-act="forget" data-id="${m.id}">Forget</button></span></div>`:''}
|
||||
</div>
|
||||
<div class="acts">
|
||||
<button class="mact" title="Edit / correct" data-act="edit" data-id="${m.id}"><svg viewBox="0 0 24 24"><path d="M14 7l-1.5-1.5a2 2 0 0 0-3 0l-5 5L4 14l3.5-.5 5-5a2 2 0 0 0 0-3z"/></svg></button>
|
||||
<button class="mact danger" title="Forget this" data-act="forget" data-id="${m.id}"><svg viewBox="0 0 24 24"><path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
document.getElementById('mems').innerHTML = mems.map(row).join('');
|
||||
|
||||
function toast(msg){ const t=document.getElementById('toast'); document.getElementById('toastText').textContent=msg; t.classList.add('show'); clearTimeout(t._t); t._t=setTimeout(()=>t.classList.remove('show'),2400); }
|
||||
document.getElementById('mems').addEventListener('click', e=>{
|
||||
const b=e.target.closest('[data-act]'); if(!b) return;
|
||||
const act=b.dataset.act, id=b.dataset.id;
|
||||
if(act==='forget'){ const el=b.closest('.mem'); el.style.transition='.3s'; el.style.opacity='0'; el.style.transform='translateX(-12px)'; setTimeout(()=>el.remove(),300); toast('Forgotten '+id+' — removed from recall'); }
|
||||
else if(act==='confirm'){ const sb=b.closest('.stalebanner'); if(sb){ sb.outerHTML='<div class="corrected"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg><span>Confirmed still true · freshness reset</span></div>'; } toast('Confirmed '+id+' — freshness reset'); }
|
||||
else if(act==='edit'){ toast('Correcting '+id+' — opens an inline editor'); }
|
||||
});
|
||||
|
||||
const labels = { manage:'<b>Manage</b> — forget, correct, confirm; see confidence & freshness', why:'<b>Why-trace</b> — every action explains itself' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e=>{ const b=e.target.closest('button'); if(!b) return; [...seg.children].forEach(x=>x.classList.toggle('on',x===b)); document.querySelectorAll('.view').forEach(v=>v.classList.toggle('on', v.dataset.view===b.dataset.v)); document.getElementById('vlabel').innerHTML=labels[b.dataset.v]; document.querySelector('.stage').scrollTop=0; });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,260 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Onboarding</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 11px; border-radius: 7px; transition: .14s; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); } .controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
.stage { flex: 1; min-height: 0; position: relative; overflow: hidden; }
|
||||
.stage .comb { position: absolute; inset: 0; opacity: .5; pointer-events: none; }
|
||||
|
||||
/* progress */
|
||||
.prog { position: absolute; top: 18px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 8px; z-index: 5; }
|
||||
.prog .step { display:flex; align-items:center; gap: 8px; }
|
||||
.prog .pd { width: 26px; height: 4px; border-radius: 3px; background: var(--surface-3); transition:.3s; }
|
||||
.prog .pd.done { background: var(--honey); } .prog .pd.cur { background: var(--honey-bright); }
|
||||
|
||||
.screen { position: absolute; inset: 0; display: none; align-items: flex-start; justify-content: center; padding: 92px 32px 96px; overflow: auto; }
|
||||
.screen.on { display: flex; }
|
||||
.card { width: 100%; max-width: 600px; text-align: center; position: relative; }
|
||||
.eyebrow { font-family: var(--mono); font-size: 11px; letter-spacing:.14em; text-transform:uppercase; color: var(--honey); margin-bottom: 14px; }
|
||||
.card h1 { font-size: clamp(28px, 4vw, 40px); font-weight: 650; letter-spacing:-0.02em; line-height:1.06; margin: 0 0 14px; text-wrap:balance; }
|
||||
.card h1 em { font-style: normal; color: var(--honey); }
|
||||
.card .lede { font-size: 16px; color: var(--text-2); line-height: 1.55; margin: 0 auto 30px; max-width: 46ch; }
|
||||
.mk { width: 60px; height: 66px; margin: 0 auto 24px; display:grid; place-items:center; font-weight:800; font-size:24px; color:#1a1407; background: linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); box-shadow: var(--honey-glow); }
|
||||
|
||||
.chips { display:flex; flex-wrap:wrap; gap: 10px; justify-content:center; margin-bottom: 16px; }
|
||||
.chip { font-size: 14px; font-weight: 550; padding: 10px 16px; border-radius: 999px; border:1px solid var(--line-strong); background: var(--surface); color: var(--text-2); cursor:pointer; transition:.14s; }
|
||||
.chip:hover { border-color: var(--honey-line); color: var(--text); }
|
||||
.chip.sel { background: var(--honey); color:#1a1407; border-color: var(--honey); }
|
||||
.flabel { font-size: 12.5px; color: var(--text-dim); margin: 22px 0 12px; font-family:var(--mono); letter-spacing:.08em; text-transform:uppercase; }
|
||||
.field { width:100%; max-width: 360px; margin: 0 auto 8px; padding: 12px 16px; border-radius: 11px; border:1px solid var(--line); background: var(--surface); color: var(--text); font-family:var(--sans); font-size:15px; outline:none; text-align:center; }
|
||||
.field:focus { border-color: var(--honey-line); }
|
||||
|
||||
/* import cards */
|
||||
.opts { display:grid; grid-template-columns: repeat(2,1fr); gap: 12px; max-width: 460px; margin: 0 auto; }
|
||||
.opt { padding: 18px; border-radius: var(--r-lg); border:1px solid var(--line-strong); background: var(--surface); cursor:pointer; transition:.15s; text-align:left; display:flex; align-items:center; gap:13px; }
|
||||
.opt:hover { border-color: var(--honey-line); } .opt.sel { border-color: var(--honey); background: var(--honey-wash); }
|
||||
.opt .oi { width:34px; height:38px; flex:none; display:grid; place-items:center; font-family:var(--mono); font-weight:700; font-size:12px; color:var(--text); background:var(--surface-2); border:1px solid var(--line); }
|
||||
.opt b { font-size: 14px; font-weight: 650; display:block; } .opt small { font-size: 11.5px; color: var(--text-muted); }
|
||||
.reassure { margin-top: 18px; font-size: 12.5px; color: var(--text-dim); display:inline-flex; align-items:center; gap:8px; }
|
||||
.reassure svg { width:14px; height:14px; stroke: var(--healthy); fill:none; stroke-width:1.8; }
|
||||
|
||||
/* template cards */
|
||||
.tpls { display:grid; grid-template-columns: repeat(3,1fr); gap: 11px; max-width: 540px; margin: 0 auto; }
|
||||
.tpl { padding: 16px 12px; border-radius: var(--r); border:1px solid var(--line-strong); background: var(--surface); cursor:pointer; transition:.15s; }
|
||||
.tpl:hover { border-color: var(--honey-line); } .tpl.sel { border-color: var(--honey); background: var(--honey-wash); }
|
||||
.tpl .ti { width: 32px; height: 36px; margin: 0 auto 9px; display:grid; place-items:center; }
|
||||
.tpl .ti svg { width:18px; height:18px; stroke: var(--honey); fill:none; stroke-width:1.7; }
|
||||
.tpl b { font-size: 13px; font-weight: 600; display:block; } .tpl small { font-size: 10.5px; color: var(--text-dim); }
|
||||
|
||||
/* first task */
|
||||
.ask { display:flex; align-items:center; gap:10px; max-width: 480px; margin: 0 auto 16px; padding: 6px 6px 6px 18px; border-radius: 999px; border:1px solid var(--line); background: var(--surface); }
|
||||
.ask:focus-within { border-color: var(--honey-line); box-shadow: var(--honey-glow); }
|
||||
.ask input { flex:1; border:0; background:transparent; color:var(--text); font-family:var(--sans); font-size:15px; outline:none; text-align:left; }
|
||||
.ask .go { width:40px;height:40px;border-radius:999px;border:0;background:var(--honey);color:#1a1407;font-size:17px;cursor:pointer;flex:none; }
|
||||
.suggest { display:flex; flex-direction:column; gap:8px; max-width: 480px; margin: 0 auto; }
|
||||
.sg { text-align:left; padding: 11px 15px; border-radius: 10px; border:1px solid var(--line-soft); background: var(--surface); color: var(--text-2); font-size:13.5px; cursor:pointer; transition:.14s; }
|
||||
.sg:hover { border-color: var(--honey-line); color: var(--text); }
|
||||
|
||||
/* model gate */
|
||||
.mtabs { display:inline-flex; padding:3px; gap:3px; border-radius:10px; background:var(--surface-2); border:1px solid var(--line-soft); margin-bottom:20px; }
|
||||
.mtab { font-size:13px; font-weight:600; cursor:pointer; padding:8px 16px; border-radius:7px; color:var(--text-muted); }
|
||||
.mtab.sel { background:var(--honey); color:#1a1407; }
|
||||
.keyfield { display:flex; align-items:center; gap:10px; max-width:420px; margin:0 auto 8px; padding:6px 8px 6px 16px; border-radius:11px; border:1px solid var(--healthy); background:var(--surface); }
|
||||
.keyfield input { flex:1; border:0; background:transparent; color:var(--text); font-family:var(--mono); font-size:13.5px; outline:none; text-align:left; }
|
||||
.keyfield .valid { font-size:12px; font-weight:600; color:var(--healthy); white-space:nowrap; padding-right:6px; }
|
||||
.mpane.hidden { display:none; }
|
||||
|
||||
/* nav */
|
||||
.nav { position: absolute; bottom: 0; left:0; right:0; display:flex; align-items:center; justify-content:space-between; padding: 18px 32px; border-top: 1px solid var(--line-soft); background: color-mix(in srgb,var(--bg) 80%, transparent); backdrop-filter: blur(8px); }
|
||||
.nav .skip { font-size: 13px; color: var(--text-dim); cursor:pointer; } .nav .skip:hover { color: var(--text-2); }
|
||||
.nav .rgt { display:flex; gap: 10px; align-items:center; }
|
||||
.btn { font-family:var(--sans); font-size: 14px; font-weight: 650; padding: 11px 20px; border-radius: 11px; cursor:pointer; border:1px solid transparent; }
|
||||
.btn.back { background: transparent; color: var(--text-muted); } .btn.back:hover { color: var(--text); }
|
||||
.btn.next { background: var(--honey); color:#1a1407; } .btn.next:hover { background: var(--honey-bright); }
|
||||
@media (max-width: 620px){ .opts, .tpls { grid-template-columns: 1fr 1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Onboarding · jump to step</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-s="0">1</button><button data-s="1">2</button><button data-s="2">3</button><button data-s="3">4</button><button data-s="4">5</button><button data-s="5">6</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Welcome</b> — first run, full-screen takeover</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<div class="comb"></div>
|
||||
<div class="prog" id="prog"></div>
|
||||
|
||||
<!-- 1 welcome -->
|
||||
<div class="screen on" data-step="0">
|
||||
<div class="card">
|
||||
<div class="mk hex">W</div>
|
||||
<h1>Welcome to Waggle.<br>The workspace that <em>remembers.</em></h1>
|
||||
<p class="lede">In two minutes we'll set up a workspace that knows you and your work — so you never start from a blank box again. Everything stays on your machine.</p>
|
||||
<div class="chips"><span class="chip sel" onclick="next()">Let's set it up →</span></div>
|
||||
<div class="reassure"><svg viewBox="0 0 24 24"><path d="M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/></svg>Local-first · no account needed to start</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2 about you -->
|
||||
<div class="screen" data-step="1">
|
||||
<div class="card">
|
||||
<div class="eyebrow">Step 2 · about you</div>
|
||||
<h1>A little about <em>you.</em></h1>
|
||||
<p class="lede">This shapes how Waggle works for you — your personas, suggestions, and defaults. You can change any of it later.</p>
|
||||
<input class="field" placeholder="Your name" value="Mara">
|
||||
<div class="flabel">What do you do most?</div>
|
||||
<div class="chips" data-single>
|
||||
<span class="chip">Research</span><span class="chip sel">Strategy / exec</span><span class="chip">Engineering</span>
|
||||
<span class="chip">Writing</span><span class="chip">Sales</span><span class="chip">Legal</span><span class="chip">Finance</span>
|
||||
</div>
|
||||
<div class="flabel">Working...</div>
|
||||
<div class="chips" data-single>
|
||||
<span class="chip sel">Solo</span><span class="chip">Small team</span><span class="chip">Larger org</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3 give it a brain (model gate) -->
|
||||
<div class="screen" data-step="2">
|
||||
<div class="card">
|
||||
<div class="eyebrow">Step 3 · the one requirement</div>
|
||||
<h1>Give it a <em>brain.</em></h1>
|
||||
<p class="lede">Waggle is model-agnostic — but it needs at least one to think with. Bring an API key, or run a model locally. You can add more and set up failover anytime.</p>
|
||||
<div class="mtabs" id="mtabs"><span class="mtab sel" data-m="key">Use an API key</span><span class="mtab" data-m="local">Run it locally</span></div>
|
||||
|
||||
<div class="mpane" data-m="key">
|
||||
<div class="chips" data-single style="margin-bottom:14px"><span class="chip sel">Anthropic</span><span class="chip">OpenAI</span><span class="chip">Google</span><span class="chip">Groq</span></div>
|
||||
<div class="keyfield"><input placeholder="sk-ant-… paste your key" value="sk-ant-api03-••••••••••3a9f"><span class="valid">✓ valid</span></div>
|
||||
<div class="reassure"><svg viewBox="0 0 24 24"><path d="M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/></svg>Encrypted in your local vault — never shown to a model or sent anywhere</div>
|
||||
</div>
|
||||
|
||||
<div class="mpane hidden" data-m="local">
|
||||
<div class="opts" style="max-width:460px">
|
||||
<div class="opt sel"><div class="oi">QW</div><div><b>Qwen 2.5 7B</b><small>detected · Ollama · running</small></div></div>
|
||||
<div class="opt"><div class="oi">L3</div><div><b>Llama 3.2</b><small>pull · ~2 GB</small></div></div>
|
||||
</div>
|
||||
<div class="reassure"><svg viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></svg>100% on your machine — no key, no cloud, no cost</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4 import -->
|
||||
<div class="screen" data-step="3">
|
||||
<div class="card">
|
||||
<div class="eyebrow">Step 4 · bring your history</div>
|
||||
<h1>Start with what you <em>already have.</em></h1>
|
||||
<p class="lede">Import past conversations and Waggle learns you on day one. Or start fresh — it learns fast either way.</p>
|
||||
<div class="opts">
|
||||
<div class="opt sel"><div class="oi">CG</div><div><b>ChatGPT</b><small>export → import</small></div></div>
|
||||
<div class="opt"><div class="oi">Cl</div><div><b>Claude</b><small>export → import</small></div></div>
|
||||
<div class="opt"><div class="oi">Cu</div><div><b>Cursor</b><small>history</small></div></div>
|
||||
<div class="opt"><div class="oi">+</div><div><b>Start fresh</b><small>learns as you go</small></div></div>
|
||||
</div>
|
||||
<div class="reassure"><svg viewBox="0 0 24 24"><path d="M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/></svg>Imported on your machine — it never leaves</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5 template -->
|
||||
<div class="screen" data-step="4">
|
||||
<div class="card">
|
||||
<div class="eyebrow">Step 5 · first workspace</div>
|
||||
<h1>Pick a <em>starting point.</em></h1>
|
||||
<p class="lede">A template seeds the right persona, connectors, and a few starter prompts. One click and you're in.</p>
|
||||
<div class="tpls" id="tpls"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6 first task -->
|
||||
<div class="screen" data-step="5">
|
||||
<div class="card">
|
||||
<div class="eyebrow">Step 6 · your first move</div>
|
||||
<h1>What should we <em>start with?</em></h1>
|
||||
<p class="lede">Type the first thing you'd like help with, or pick one — your workspace opens straight into it.</p>
|
||||
<div class="ask"><input placeholder="e.g. “Map our top 5 competitors and where we win”" value=""><button class="go">→</button></div>
|
||||
<div class="suggest" id="suggest"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav">
|
||||
<span class="skip" id="skip">Skip setup</span>
|
||||
<div class="rgt">
|
||||
<button class="btn back" id="back" style="visibility:hidden">← Back</button>
|
||||
<button class="btn next" id="nextb">Continue →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const STEPS = 6;
|
||||
const labels = ['<b>Welcome</b> — first run, full-screen takeover','<b>About you</b> — role & team shape your defaults','<b>Give it a brain</b> — one model required (key or local)','<b>Import</b> — start from your history, locally','<b>Template</b> — seed persona + connectors','<b>First task</b> — open straight into the work'];
|
||||
let step = 0;
|
||||
|
||||
const tplData = [
|
||||
{ ic:'<path d="M12 5a3 3 0 0 0-3 3 3 3 0 0 0-3 3 3 3 0 0 0 1 5 3 3 0 0 0 5 1 3 3 0 0 0 5-1 3 3 0 0 0 1-5 3 3 0 0 0-3-3 3 3 0 0 0-3-3Z"/>', n:'Research', s:'analyst' },
|
||||
{ ic:'<path d="m12 3 2.5 6 6.5.5-5 4.2 1.6 6.3L12 16.8 6 20l1.6-6.3-5-4.2 6.5-.5z"/>', n:'Strategy', s:'consultant', sel:true },
|
||||
{ ic:'<rect x="4" y="4" width="16" height="16" rx="3"/><path d="M9 9h6v6H9z"/>', n:'Engineering', s:'coder' },
|
||||
{ ic:'<path d="m22 2-7 20-4-9-9-4Z"/>', n:'Sales', s:'sales-rep' },
|
||||
{ ic:'<path d="M4 6h16M4 12h12M4 18h8"/>', n:'Writing', s:'writer' },
|
||||
{ ic:'<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>', n:'Custom', s:'general' },
|
||||
];
|
||||
document.getElementById('tpls').innerHTML = tplData.map(t=>`<div class="tpl ${t.sel?'sel':''}"><div class="ti"><svg viewBox="0 0 24 24">${t.ic}</svg></div><b>${t.n}</b><small>${t.s}</small></div>`).join('');
|
||||
document.getElementById('suggest').innerHTML = ['Map our top 5 competitors and where we win','Draft the Q2 board narrative from this week\u2019s work','Summarize what changed in the market this month'].map(s=>`<div class="sg">${s}</div>`).join('');
|
||||
|
||||
// single-select chips & cards
|
||||
document.querySelectorAll('[data-single]').forEach(grp => grp.addEventListener('click', e => { const c=e.target.closest('.chip'); if(!c)return; grp.querySelectorAll('.chip').forEach(x=>x.classList.remove('sel')); c.classList.add('sel'); }));
|
||||
['opts','tpls'].forEach(id => { const el=document.getElementById(id)||document.querySelector('.'+id); });
|
||||
document.querySelector('.opts').addEventListener('click', e=>{const o=e.target.closest('.opt');if(!o)return;document.querySelectorAll('.opt').forEach(x=>x.classList.remove('sel'));o.classList.add('sel');});
|
||||
document.getElementById('tpls').addEventListener('click', e=>{const t=e.target.closest('.tpl');if(!t)return;document.querySelectorAll('.tpl').forEach(x=>x.classList.remove('sel'));t.classList.add('sel');});
|
||||
document.getElementById('suggest').addEventListener('click', e=>{const s=e.target.closest('.sg');if(s)document.querySelector('[data-step="5"] .ask input').value=s.textContent;});
|
||||
|
||||
// model step: tab switch + provider/local select
|
||||
const mtabs = document.getElementById('mtabs');
|
||||
mtabs.addEventListener('click', e => { const t=e.target.closest('.mtab'); if(!t)return; mtabs.querySelectorAll('.mtab').forEach(x=>x.classList.toggle('sel',x===t)); document.querySelectorAll('.mpane').forEach(p=>p.classList.toggle('hidden', p.dataset.m!==t.dataset.m)); });
|
||||
document.querySelectorAll('.mpane .opts').forEach(grp => grp.addEventListener('click', e=>{const o=e.target.closest('.opt');if(!o)return;grp.querySelectorAll('.opt').forEach(x=>x.classList.remove('sel'));o.classList.add('sel');}));
|
||||
|
||||
function renderProg(){ document.getElementById('prog').innerHTML = Array.from({length:STEPS},(_,i)=>`<span class="pd ${i<step?'done':i===step?'cur':''}"></span>`).join(''); }
|
||||
function go(s){
|
||||
step = Math.max(0, Math.min(STEPS-1, s));
|
||||
document.querySelectorAll('.screen').forEach(el => el.classList.toggle('on', +el.dataset.step===step));
|
||||
document.querySelectorAll('#seg button').forEach(b => b.classList.toggle('on', +b.dataset.s===step));
|
||||
document.getElementById('vlabel').innerHTML = labels[step];
|
||||
document.getElementById('back').style.visibility = step===0 ? 'hidden':'visible';
|
||||
document.getElementById('nextb').textContent = step===STEPS-1 ? 'Enter Waggle →' : 'Continue →';
|
||||
renderProg();
|
||||
}
|
||||
function next(){ go(step+1); }
|
||||
document.getElementById('nextb').onclick = () => { if(step===STEPS-1){ document.getElementById('nextb').textContent='Opening your workspace…'; } else next(); };
|
||||
document.getElementById('back').onclick = () => go(step-1);
|
||||
document.getElementById('skip').onclick = () => go(STEPS-1);
|
||||
document.getElementById('seg').addEventListener('click', e => { const b=e.target.closest('button'); if(b) go(+b.dataset.s); });
|
||||
go(0);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,213 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Platform</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height:100vh; overflow:hidden; display:flex; flex-direction:column; }
|
||||
.controls { flex:none; display:flex; align-items:center; gap:14px; padding:11px 20px; border-bottom:1px solid var(--line-soft); background:color-mix(in srgb,var(--bg) 88%,transparent); backdrop-filter:blur(10px); z-index:30; }
|
||||
.controls .lab { font-family:var(--mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); }
|
||||
.seg { display:inline-flex; padding:3px; gap:3px; border-radius:10px; background:var(--surface-2); border:1px solid var(--line-soft); }
|
||||
.seg button { font-family:var(--sans); font-size:12px; font-weight:600; cursor:pointer; border:0; background:transparent; color:var(--text-muted); padding:6px 11px; border-radius:7px; transition:.14s; white-space:nowrap; }
|
||||
.seg button.on { background:var(--honey); color:#1a1407; }
|
||||
.controls .vlabel { font-size:12.5px; color:var(--text-muted); } .controls .vlabel b { color:var(--text); font-weight:600; }
|
||||
.controls .right { margin-left:auto; display:flex; align-items:center; gap:10px; }
|
||||
.tbtn { width:34px; height:34px; border-radius:9px; border:1px solid var(--line); background:var(--surface); color:var(--text-2); cursor:pointer; }
|
||||
.tbtn:hover { border-color:var(--honey-line); color:var(--honey); }
|
||||
.stage { flex:1; min-height:0; overflow:auto; position:relative; }
|
||||
.stage .comb { position:absolute; inset:0; opacity:.4; pointer-events:none; }
|
||||
.view { display:none; } .view.on { display:block; }
|
||||
.wrap { max-width:920px; margin:0 auto; padding:30px 32px 60px; position:relative; }
|
||||
.head { margin-bottom:24px; text-align:center; }
|
||||
.head .eyebrow { font-family:var(--mono); font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--honey); margin-bottom:12px; }
|
||||
.head h1 { font-size:28px; font-weight:650; letter-spacing:-0.02em; margin:0 0 10px; } .head h1 em { font-style:normal; color:var(--honey); }
|
||||
.head p { font-size:15px; color:var(--text-muted); line-height:1.55; margin:0 auto; max-width:54ch; } .head p b { color:var(--text-2); }
|
||||
|
||||
/* desktop window mock */
|
||||
.winwrap { max-width:760px; margin:0 auto; }
|
||||
.ostabs { display:flex; gap:6px; justify-content:center; margin-bottom:16px; }
|
||||
.ostab { font-size:12.5px; font-weight:600; padding:7px 15px; border-radius:9px; border:1px solid var(--line-soft); background:var(--surface); color:var(--text-muted); cursor:pointer; display:inline-flex; align-items:center; gap:7px; }
|
||||
.ostab.on { border-color:var(--honey-line); background:var(--honey-wash); color:var(--honey); }
|
||||
.window { border-radius:12px; overflow:hidden; border:1px solid var(--line-strong); box-shadow:var(--shadow-lg); background:var(--bg-2); }
|
||||
.titlebar { height:38px; display:flex; align-items:center; gap:8px; padding:0 14px; background:var(--surface); border-bottom:1px solid var(--line-soft); }
|
||||
.titlebar.mac .lights { display:flex; gap:8px; } .titlebar .lights i { width:12px; height:12px; border-radius:999px; }
|
||||
.titlebar .lights .r { background:#ec6a5e; } .titlebar .lights .y { background:#f4bf4f; } .titlebar .lights .g { background:#61c554; }
|
||||
.titlebar .tt { flex:1; text-align:center; font-size:12px; color:var(--text-dim); font-family:var(--mono); }
|
||||
.titlebar.win { flex-direction:row-reverse; }
|
||||
.titlebar.win .lights { display:flex; gap:0; } .titlebar.win .lights i { width:30px; height:38px; border-radius:0; display:grid; place-items:center; color:var(--text-muted); font-size:12px; background:transparent; }
|
||||
.titlebar.win .lights i.x:hover { background:var(--risk); color:#fff; }
|
||||
.titlebar.mac .lights.winhide, .titlebar.win .lights.machide { display:none; }
|
||||
.winbody { display:flex; height:300px; }
|
||||
.wb-side { width:120px; flex:none; background:var(--bg-2); border-right:1px solid var(--line-soft); padding:14px 8px; }
|
||||
.wb-side .wi { display:flex; align-items:center; gap:8px; padding:7px 9px; border-radius:8px; font-size:12px; color:var(--text-2); margin-bottom:2px; } .wb-side .wi.on { background:var(--honey-wash); color:var(--text); } .wb-side .wi .d { width:13px; height:13px; border-radius:4px; background:var(--surface-3); } .wb-side .wi.on .d { background:var(--honey); }
|
||||
.wb-main { flex:1; padding:22px; background:var(--bg); }
|
||||
.wb-main .g { font-family:var(--mono); font-size:10px; color:var(--honey); letter-spacing:.08em; }
|
||||
.wb-main h3 { font-family:var(--sans); font-size:20px; font-weight:600; letter-spacing:-0.02em; margin:8px 0 14px; }
|
||||
.wb-main .ln { height:9px; border-radius:5px; background:var(--surface-2); margin-bottom:9px; } .wb-main .ln.s { width:60%; }
|
||||
|
||||
.specs { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-top:22px; }
|
||||
.spec { padding:16px; border-radius:var(--r); border:1px solid var(--line-soft); background:var(--surface); text-align:center; }
|
||||
.spec .sv { font-size:16px; font-weight:700; letter-spacing:-0.01em; } .spec .sl { font-size:11px; color:var(--text-muted); margin-top:5px; }
|
||||
|
||||
/* boot */
|
||||
.boot { min-height:440px; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; }
|
||||
.boot .mk { width:72px; height:80px; display:grid; place-items:center; font-weight:800; font-size:30px; color:#1a1407; background:linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); box-shadow:var(--honey-glow); margin-bottom:24px; animation:pulse 2.2s ease-in-out infinite; }
|
||||
@keyframes pulse { 0%,100%{ box-shadow:0 0 0 1px rgba(233,165,44,.25), 0 8px 30px -10px rgba(233,165,44,.35);} 50%{ box-shadow:0 0 0 1px rgba(233,165,44,.5), 0 8px 50px -6px rgba(233,165,44,.6);} }
|
||||
.boot h1 { font-size:24px; font-weight:600; letter-spacing:-0.02em; margin:0 0 8px; white-space:nowrap; }
|
||||
.boot .sub { font-family:var(--mono); font-size:12px; color:var(--text-dim); margin-bottom:26px; }
|
||||
.bootlist { display:grid; gap:8px; width:300px; text-align:left; }
|
||||
.bl { display:flex; align-items:center; gap:11px; font-size:13px; color:var(--text-2); }
|
||||
.bl .c { width:16px; height:16px; flex:none; } .bl .c svg { width:16px; height:16px; stroke:var(--healthy); fill:none; stroke-width:2.4; }
|
||||
.bl .spin { width:13px; height:13px; border-radius:999px; border:2px solid var(--honey); border-top-color:transparent; animation:spin .7s linear infinite; }
|
||||
@keyframes spin { to { transform:rotate(360deg); } }
|
||||
.bl.pending { color:var(--text-dim); }
|
||||
|
||||
/* roadmap */
|
||||
.channels { display:grid; gap:12px; }
|
||||
.ch { display:flex; align-items:center; gap:15px; padding:18px 20px; border-radius:var(--r-lg); border:1px solid var(--line-soft); background:var(--surface); }
|
||||
.ch.now { border-color:var(--honey-line); }
|
||||
.ch .ci { width:42px; height:46px; flex:none; display:grid; place-items:center; }
|
||||
.ch .ci svg { width:22px; height:22px; stroke:var(--honey); fill:none; stroke-width:1.7; }
|
||||
.ch .ct { flex:1; } .ch .ct b { font-size:15.5px; font-weight:650; } .ch .ct p { margin:3px 0 0; font-size:13px; color:var(--text-muted); }
|
||||
.ch .platforms { display:flex; gap:6px; margin-top:9px; } .ch .platforms span { font-family:var(--mono); font-size:10.5px; color:var(--text-dim); padding:3px 8px; border-radius:6px; border:1px solid var(--line-soft); }
|
||||
.ch .status { font-size:11.5px; font-weight:650; padding:6px 13px; border-radius:999px; white-space:nowrap; }
|
||||
.ch .status.now { color:var(--healthy); background:var(--healthy-wash); }
|
||||
.ch .status.next { color:var(--attention); background:var(--honey-wash); }
|
||||
.ch .status.beta { color:var(--work); background:var(--work-wash); }
|
||||
|
||||
/* 404 */
|
||||
.nf { min-height:420px; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; }
|
||||
.nf .big { font-family:var(--sans); font-size:88px; font-weight:750; letter-spacing:-0.04em; color:var(--honey); line-height:1; }
|
||||
.nf h1 { font-size:24px; font-weight:600; margin:12px 0 8px; }
|
||||
.nf p { font-size:14.5px; color:var(--text-muted); margin:0 0 24px; max-width:40ch; }
|
||||
.nf .acts { display:flex; gap:10px; } .nf .acts a { padding:11px 20px; border-radius:11px; font-size:14px; font-weight:650; text-decoration:none; cursor:pointer; } .nf .acts .go { background:var(--honey); color:#1a1407; } .nf .acts .ghost { background:var(--surface); color:var(--text-2); border:1px solid var(--line-strong); }
|
||||
|
||||
@media (max-width:820px){ .specs { grid-template-columns:1fr 1fr; } .winbody { height:auto; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Platform & roadmap</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="desktop">Desktop (now)</button>
|
||||
<button data-v="boot">Boot</button>
|
||||
<button data-v="roadmap">Coming next</button>
|
||||
<button data-v="nf">404</button>
|
||||
</div>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<div class="comb"></div>
|
||||
|
||||
<!-- DESKTOP -->
|
||||
<div class="view on wrap" data-view="desktop">
|
||||
<div class="head">
|
||||
<div class="eyebrow">Ships now · Tauri</div>
|
||||
<h1>A real <em>desktop app.</em></h1>
|
||||
<p>Waggle ships as a native app for <b>Windows & macOS</b> — built on Tauri, so it's tiny, fast, and truly local. Your hive lives on your disk; the app just opens a window onto it.</p>
|
||||
</div>
|
||||
<div class="winwrap">
|
||||
<div class="ostabs" id="ostabs"><span class="ostab on" data-os="mac">macOS</span><span class="ostab" data-os="win">Windows</span></div>
|
||||
<div class="window">
|
||||
<div class="titlebar mac" id="titlebar">
|
||||
<div class="lights machide"><i class="r"></i><i class="y"></i><i class="g"></i></div>
|
||||
<div class="lights winhide"><i>―</i><i>▢</i><i class="x">✕</i></div>
|
||||
<div class="tt">Waggle</div>
|
||||
</div>
|
||||
<div class="winbody">
|
||||
<div class="wb-side"><div class="wi on"><span class="d"></span>Home</div><div class="wi"><span class="d"></span>Chat</div><div class="wi"><span class="d"></span>Memory</div><div class="wi"><span class="d"></span>Agents</div><div class="wi"><span class="d"></span>Library</div></div>
|
||||
<div class="wb-main"><div class="g">FRIDAY · 8:42</div><h3>Good morning, Mara.</h3><div class="ln"></div><div class="ln"></div><div class="ln s"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="specs">
|
||||
<div class="spec"><div class="sv">~12 MB</div><div class="sl">Install size</div></div>
|
||||
<div class="spec"><div class="sv">Local</div><div class="sl">Data on your disk</div></div>
|
||||
<div class="spec"><div class="sv">Auto-update</div><div class="sl">Signed & notarized</div></div>
|
||||
<div class="spec"><div class="sv">Win · Mac</div><div class="sl">Linux soon</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BOOT -->
|
||||
<div class="view wrap" data-view="boot">
|
||||
<div class="boot">
|
||||
<div class="mk hex">W</div>
|
||||
<h1>Warming the hive…</h1>
|
||||
<div class="sub">waggle · v1.0 · local</div>
|
||||
<div class="bootlist">
|
||||
<div class="bl"><span class="c"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></span>Memory engine ready · 581 memories</div>
|
||||
<div class="bl"><span class="c"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></span>Local model online · Qwen 2.5</div>
|
||||
<div class="bl"><span class="spin"></span>Loading workspaces… 8 found</div>
|
||||
<div class="bl pending"><span class="c"></span>Restoring your last session</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ROADMAP -->
|
||||
<div class="view wrap" data-view="roadmap">
|
||||
<div class="head">
|
||||
<div class="eyebrow">Where Waggle goes</div>
|
||||
<h1>One hive, <em>everywhere you work.</em></h1>
|
||||
<p>Your memory is the constant; the surfaces multiply. Desktop today — your phone and your messaging apps next.</p>
|
||||
</div>
|
||||
<div class="channels">
|
||||
<div class="ch now">
|
||||
<div class="ci hex" style="background:var(--honey-wash)"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M3 9h18M8 21h8"/></svg></div>
|
||||
<div class="ct"><b>Desktop app</b><p>The full hive — native, local, fast.</p><div class="platforms"><span>macOS</span><span>Windows</span><span>Linux soon</span></div></div>
|
||||
<span class="status now">● Available now</span>
|
||||
</div>
|
||||
<div class="ch">
|
||||
<div class="ci hex" style="background:var(--work-wash)"><svg viewBox="0 0 24 24"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7M14 3l6 6M14 3v6h6M9 13h2M9 17h6"/></svg></div>
|
||||
<div class="ct"><b>Browser extension</b><p>Capture context & recall memory anywhere on the web.</p><div class="platforms"><span>Chrome</span><span>Edge</span><span>Firefox</span></div></div>
|
||||
<span class="status beta">◐ In beta</span>
|
||||
</div>
|
||||
<div class="ch">
|
||||
<div class="ci hex" style="background:var(--honey-wash)"><svg viewBox="0 0 24 24"><path d="M21 11.5a8.4 8.4 0 0 1-9 8.4 8.6 8.6 0 0 1-3.8-.9L3 20l1.1-4.1A8.4 8.4 0 1 1 21 11.5z"/></svg></div>
|
||||
<div class="ct"><b>Messaging</b><p>Talk to your hive from where you already chat — same memory, same agents.</p><div class="platforms"><span>WhatsApp</span><span>Telegram</span><span>iMessage</span></div></div>
|
||||
<span class="status next">Coming next</span>
|
||||
</div>
|
||||
<div class="ch">
|
||||
<div class="ci hex" style="background:var(--intel-wash)"><svg viewBox="0 0 24 24"><rect x="7" y="3" width="10" height="18" rx="2"/><path d="M11 18h2"/></svg></div>
|
||||
<div class="ct"><b>Mobile</b><p>Your morning briefing and quick capture, in your pocket.</p><div class="platforms"><span>iOS</span><span>Android</span></div></div>
|
||||
<span class="status next">Coming next</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 404 -->
|
||||
<div class="view wrap" data-view="nf">
|
||||
<div class="nf">
|
||||
<div class="big">404</div>
|
||||
<h1>This cell of the hive is empty.</h1>
|
||||
<p>The page you're after doesn't exist — but your memory and workspaces are right where you left them.</p>
|
||||
<div class="acts"><a class="go" data-go="home">← Back to Home</a><a class="ghost">Search with ⌘K</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e=>{ const b=e.target.closest('button'); if(!b) return; [...seg.children].forEach(x=>x.classList.toggle('on',x===b)); document.querySelectorAll('.view').forEach(v=>v.classList.toggle('on', v.dataset.view===b.dataset.v)); document.querySelector('.stage').scrollTop=0; });
|
||||
|
||||
// os toggle
|
||||
document.getElementById('ostabs').addEventListener('click', e=>{
|
||||
const t=e.target.closest('.ostab'); if(!t) return;
|
||||
document.querySelectorAll('.ostab').forEach(x=>x.classList.toggle('on',x===t));
|
||||
const tb=document.getElementById('titlebar'); tb.className = 'titlebar '+t.dataset.os;
|
||||
});
|
||||
document.addEventListener('click', e=>{ const a=e.target.closest('[data-go=home]'); if(a){ try{ location.href='../Waggle Reimagined.html#home'; }catch(_){} } });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,280 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Settings</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); } .controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.controls .exp { display:inline-flex; padding:3px; gap:3px; border-radius:9px; background:var(--surface-2); border:1px solid var(--line-soft); }
|
||||
.controls .exp button { font-family:var(--sans); font-size:11.5px; font-weight:600; cursor:pointer; border:0; background:transparent; color:var(--text-muted); padding:5px 10px; border-radius:6px; }
|
||||
.controls .exp button.on { background: var(--honey); color:#1a1407; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
.app { flex: 1; min-height: 0; display: flex; }
|
||||
.rail { flex: none; width: 196px; border-right: 1px solid var(--line-soft); background: var(--bg-2); padding: 16px 10px; overflow:auto; }
|
||||
.rail .rh { font-family: var(--mono); font-size: 10px; letter-spacing:.12em; text-transform:uppercase; color: var(--text-dim); padding: 6px 11px 12px; }
|
||||
.ritem { display:flex; align-items:center; gap:11px; padding: 9px 11px; border-radius: 10px; cursor:pointer; color: var(--text-2); transition:.13s; font-size: 13.5px; font-weight: 550; position:relative; }
|
||||
.ritem:hover { background: var(--surface-2); color: var(--text); }
|
||||
.ritem.on { background: var(--honey-wash); color: var(--text); }
|
||||
.ritem.on::before { content:''; position:absolute; left:-10px; top:50%; transform:translateY(-50%); width:3px; height:17px; border-radius:3px; background: var(--honey); }
|
||||
.ritem svg { width:17px; height:17px; stroke: currentColor; fill:none; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; flex:none; }
|
||||
.ritem.on svg { stroke: var(--honey); }
|
||||
.ritem.adv { display:none; } body.everything .ritem.adv { display:flex; }
|
||||
.ritem .warn { margin-left:auto; width:7px; height:7px; border-radius:999px; background: var(--attention); }
|
||||
|
||||
.main { flex:1; min-width:0; display:flex; flex-direction:column; }
|
||||
.shead { flex:none; padding: 24px 30px 16px; border-bottom: 1px solid var(--line-soft); }
|
||||
.shead h1 { font-size: 21px; font-weight: 650; letter-spacing:-0.02em; margin: 0 0 4px; }
|
||||
.shead p { font-size: 13px; color: var(--text-muted); margin: 0; max-width: 64ch; line-height:1.5; }
|
||||
.bodyc { flex:1; min-height:0; overflow:auto; padding: 24px 30px 60px; }
|
||||
.panel { display:none; max-width: 680px; } .panel.on { display:block; }
|
||||
|
||||
.grp { font-family: var(--mono); font-size: 10.5px; letter-spacing:.12em; text-transform:uppercase; color: var(--text-dim); margin: 4px 0 14px; display:flex; align-items:center; gap:10px; }
|
||||
.grp:not(:first-child) { margin-top: 30px; }
|
||||
.grp::after { content:''; flex:1; height:1px; background: var(--line-soft); }
|
||||
|
||||
.card { border:1px solid var(--line-soft); background: var(--surface); border-radius: var(--r-lg); padding: 18px; margin-bottom: 12px; }
|
||||
.field-row { display:flex; align-items:center; justify-content:space-between; gap:16px; }
|
||||
.field-row .lt b { font-size: 14px; font-weight: 600; display:block; } .field-row .lt span { font-size: 12.5px; color: var(--text-muted); }
|
||||
.sw { width: 40px; height: 23px; border-radius: 999px; background: var(--surface-3); border:1px solid var(--line-strong); position:relative; cursor:pointer; transition:.16s; flex:none; }
|
||||
.sw::after { content:''; position:absolute; top:2px; left:2px; width:17px; height:17px; border-radius:999px; background: var(--text-muted); transition:.16s; }
|
||||
.sw.on { background: var(--honey); border-color: var(--honey); } .sw.on::after { left:19px; background:#1a1407; }
|
||||
|
||||
.seg2 { display:inline-flex; padding:3px; gap:3px; border-radius:10px; background:var(--surface-2); border:1px solid var(--line-soft); }
|
||||
.seg2 button { font-family:var(--sans); font-size:12.5px; font-weight:600; cursor:pointer; border:0; background:transparent; color:var(--text-muted); padding:7px 13px; border-radius:7px; }
|
||||
.seg2 button.on { background:var(--honey); color:#1a1407; }
|
||||
.theme-cards { display:flex; gap:12px; }
|
||||
.tc { flex:1; padding:16px; border-radius:var(--r); text-align:center; cursor:pointer; border:1px solid var(--line); }
|
||||
.tc.dk { background:#14110b; } .tc.lt { background:#f1e9d8; }
|
||||
.tc.sel { border:2px solid var(--honey); }
|
||||
.tc .lbl { font-size:12px; font-weight:600; margin-bottom:8px; } .tc.dk .lbl { color:#f6f1e4; } .tc.lt .lbl { color:#211b11; }
|
||||
.tc .dots { display:flex; gap:5px; justify-content:center; } .tc .dots i { width:11px; height:11px; border-radius:999px; }
|
||||
|
||||
/* ── MODELS: failover pilot chain ── */
|
||||
.pilot { border:1px solid var(--honey-line); border-radius: var(--r-xl); background: linear-gradient(160deg, var(--surface-2), var(--surface)); padding: 6px; box-shadow: var(--honey-glow); margin-bottom: 14px; }
|
||||
.pilot-h { display:flex; align-items:center; gap:10px; padding: 14px 16px 12px; }
|
||||
.pilot-h svg { width:18px; height:18px; stroke: var(--honey); fill:none; stroke-width:1.8; }
|
||||
.pilot-h b { font-size: 14px; font-weight:650; white-space:nowrap; } .pilot-h .sub { font-size:11.5px; color:var(--text-muted); margin-left:auto; font-family:var(--mono); white-space:nowrap; }
|
||||
.mslot { display:flex; align-items:center; gap:14px; padding: 14px 16px; border-radius: var(--r); background: var(--surface); border:1px solid var(--line-soft); margin: 4px; position:relative; }
|
||||
.mslot .tier { font-family:var(--mono); font-size:9.5px; letter-spacing:.1em; text-transform:uppercase; width: 64px; flex:none; }
|
||||
.mslot.primary .tier { color: var(--honey); } .mslot.fallback .tier { color: var(--work); } .mslot.budget .tier { color: var(--healthy); }
|
||||
.mslot .mlogo { width:32px; height:36px; flex:none; display:grid; place-items:center; font-family:var(--mono); font-size:11px; font-weight:700; color:var(--text-2); background:var(--surface-2); border:1px solid var(--line); }
|
||||
.mslot .mname { flex:1; min-width:0; } .mslot .mname b { font-size:14px; font-weight:600; } .mslot .mname span { font-size:12px; color:var(--text-muted); }
|
||||
.mslot .mstatus { font-size:11px; font-weight:600; padding:3px 9px; border-radius:999px; display:inline-flex; align-items:center; gap:5px; }
|
||||
.mslot .mstatus.ok { color: var(--healthy); background: var(--healthy-wash); }
|
||||
.mslot .mstatus.local { color: var(--work); background: var(--work-wash); }
|
||||
.mslot .chg { font-size:12px; font-weight:600; color:var(--text-2); cursor:pointer; padding:6px 11px; border-radius:8px; border:1px solid var(--line-strong); background:var(--surface-2); }
|
||||
.mslot .chg:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
.trigger { display:flex; align-items:center; gap:9px; padding: 5px 16px 5px 80px; font-size: 11.5px; color: var(--text-dim); font-family: var(--mono); }
|
||||
.trigger svg { width:13px; height:13px; stroke: var(--text-dim); fill:none; stroke-width:1.8; }
|
||||
.trigger b { color: var(--text-2); font-weight:500; }
|
||||
.budget-row { display:flex; align-items:center; gap:14px; padding: 14px 16px; margin-top:4px; border-top:1px dashed var(--line); }
|
||||
.budget-row .bl { flex:1; font-size:13px; } .budget-row .bl b { font-weight:600; }
|
||||
.budget-row input { width: 90px; padding:7px 11px; border-radius:8px; border:1px solid var(--line); background:var(--surface); color:var(--text); font-family:var(--mono); font-size:13px; text-align:right; outline:none; }
|
||||
.budget-row input:focus { border-color: var(--honey-line); }
|
||||
|
||||
.keyrow { display:flex; align-items:center; gap:12px; padding: 11px 14px; border-radius: var(--r); border:1px solid var(--line-soft); background: var(--surface); margin-bottom:8px; }
|
||||
.keyrow .kd { width:8px; height:8px; border-radius:999px; flex:none; } .keyrow .kd.on { background:var(--healthy); } .keyrow .kd.off { background:var(--text-dim); }
|
||||
.keyrow .kn { flex:1; font-size:13.5px; font-weight:550; } .keyrow .kn small { color:var(--text-dim); font-weight:400; margin-left:6px; }
|
||||
.keyrow .ks { font-size:11.5px; font-weight:600; } .keyrow .ks.on { color:var(--healthy); } .keyrow .ks.off { color:var(--attention); }
|
||||
.note2 { display:flex; gap:11px; padding:13px 15px; border-radius:var(--r); background: var(--honey-wash); border:1px solid var(--honey-line); margin-bottom:14px; }
|
||||
.note2 svg { width:17px; height:17px; stroke:var(--honey); fill:none; stroke-width:1.8; flex:none; margin-top:1px; }
|
||||
.note2 p { margin:0; font-size:12.5px; color:var(--text-2); line-height:1.55; } .note2 b { color:var(--text); }
|
||||
|
||||
.radio-row { display:flex; align-items:flex-start; gap:11px; padding: 12px 14px; border-radius: var(--r); border:1px solid var(--line-soft); background:var(--surface); cursor:pointer; margin-bottom:8px; }
|
||||
.radio-row.sel { border-color: var(--honey-line); background: var(--honey-wash); }
|
||||
.radio-row .rd { width:16px; height:16px; border-radius:999px; border:1px solid var(--line-strong); flex:none; margin-top:1px; } .radio-row.sel .rd { border-color: var(--honey); background: var(--honey); box-shadow: inset 0 0 0 3px var(--surface); }
|
||||
.radio-row b { font-size:13.5px; font-weight:600; } .radio-row p { margin:3px 0 0; font-size:12px; color:var(--text-muted); }
|
||||
|
||||
.plan-badge { display:inline-flex; align-items:center; gap:8px; font-size:13px; font-weight:650; padding:8px 14px; border-radius:999px; background: var(--honey-wash); color: var(--honey); border:1px solid var(--honey-line); }
|
||||
.btn { font-family:var(--sans); font-size:13px; font-weight:650; padding:9px 15px; border-radius:9px; cursor:pointer; border:1px solid transparent; display:inline-flex; align-items:center; gap:7px; }
|
||||
.btn.go { background:var(--honey); color:#1a1407; } .btn.ghost { background:var(--surface-2); color:var(--text-2); border-color:var(--line-strong); } .btn.danger { background:transparent; color:var(--risk); border-color:color-mix(in srgb,var(--risk) 35%,transparent); }
|
||||
.btn svg { width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:2; }
|
||||
@media (max-width:800px){ .rail { width:60px; } .rail .rh, .ritem span.txt { display:none; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Settings</span>
|
||||
<span class="vlabel" id="vlabel">Calm by default · depth when you ask for it</span>
|
||||
<div class="right">
|
||||
<span class="lab" style="margin-right:-4px">Show</span>
|
||||
<div class="exp" id="exp">
|
||||
<button data-e="essential" class="on">Essential</button>
|
||||
<button data-e="standard">Standard</button>
|
||||
<button data-e="everything">Everything</button>
|
||||
</div>
|
||||
<button class="tbtn" id="tbtn" title="Toggle theme">☾</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="app">
|
||||
<nav class="rail" id="rail">
|
||||
<div class="rh">Settings</div>
|
||||
<div class="ritem" data-s="general"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M5 19l2-2M17 7l2-2"/></svg><span class="txt">General</span></div>
|
||||
<div class="ritem on" data-s="models"><svg viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="3"/><circle cx="12" cy="12" r="3"/></svg><span class="txt">Models</span></div>
|
||||
<div class="ritem" data-s="perms"><svg viewBox="0 0 24 24"><path d="M9 12l2 2 4-4M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/></svg><span class="txt">Permissions</span></div>
|
||||
<div class="ritem" data-s="billing"><svg viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 10h18"/></svg><span class="txt">Plan</span></div>
|
||||
<div class="ritem" data-s="team"><svg viewBox="0 0 24 24"><circle cx="9" cy="8" r="3"/><path d="M3.5 20a5.5 5.5 0 0 1 11 0M16 6a3 3 0 0 1 0 6"/></svg><span class="txt">Team</span></div>
|
||||
<div class="ritem" data-s="backup"><svg viewBox="0 0 24 24"><ellipse cx="12" cy="6" rx="8" ry="3"/><path d="M4 6v12c0 1.7 3.6 3 8 3s8-1.3 8-3V6"/></svg><span class="txt">Backup</span></div>
|
||||
<div class="ritem adv" data-s="advanced"><svg viewBox="0 0 24 24"><path d="M14 7l-1.5-1.5a2 2 0 0 0-3 0l-1 1M4 20l5-1 9-9-4-4-9 9z"/></svg><span class="txt">Advanced</span></div>
|
||||
</nav>
|
||||
|
||||
<div class="main">
|
||||
<div class="shead"><h1 id="sTitle">Models</h1><p id="sSub">Pick your intelligence and a failover chain — Waggle routes between them automatically.</p></div>
|
||||
<div class="bodyc" id="bodyc"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} document.querySelectorAll('.tc').forEach(c=>c.classList.toggle('sel', c.classList.contains(t==='dark'?'dk':'lt'))); }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const heads = {
|
||||
general:['General','The basics — appearance, language, and your data.'],
|
||||
models:['Models','Pick your intelligence and a failover chain — Waggle routes between them automatically.'],
|
||||
perms:['Permissions','How much your agents can do before they ask you.'],
|
||||
billing:['Plan','Your tier and what it unlocks.'],
|
||||
team:['Team','Connect a shared team server to sync workspaces.'],
|
||||
backup:['Backup & export','Your data is yours — take it anywhere, anytime.'],
|
||||
advanced:['Advanced','Developer mode, telemetry, and diagnostics.'],
|
||||
};
|
||||
|
||||
const panels = {
|
||||
models: () => `
|
||||
<div class="note2"><svg viewBox="0 0 24 24"><path d="M12 9v4m0 4h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg><p><b>Waggle needs at least one working model.</b> You have a key for Claude and a local model running — you're good. Everything below is optional tuning.</p></div>
|
||||
|
||||
<div class="pilot">
|
||||
<div class="pilot-h"><svg viewBox="0 0 24 24"><path d="M12 2 4 6v6c0 5 3.5 7.5 8 9 4.5-1.5 8-4 8-9V6z"/><path d="m9 12 2 2 4-4"/></svg><b>Model pilot</b><span class="sub">automatic failover</span></div>
|
||||
|
||||
<div class="mslot primary">
|
||||
<span class="tier">Primary</span>
|
||||
<div class="mlogo">CL</div>
|
||||
<div class="mname"><b>Claude Sonnet 4</b> <span>· best quality · ~$3/Mtok</span></div>
|
||||
<span class="mstatus ok">● key set</span>
|
||||
<span class="chg">Change</span>
|
||||
</div>
|
||||
<div class="trigger"><svg viewBox="0 0 24 24"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span>if it <b>errors or is unavailable</b> →</span></div>
|
||||
<div class="mslot fallback">
|
||||
<span class="tier">Fallback</span>
|
||||
<div class="mlogo">GP</div>
|
||||
<div class="mname"><b>GPT-4o</b> <span>· reliable backup · ~$2.5/Mtok</span></div>
|
||||
<span class="mstatus ok">● key set</span>
|
||||
<span class="chg">Change</span>
|
||||
</div>
|
||||
<div class="trigger"><svg viewBox="0 0 24 24"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span>when you <b>hit your daily budget</b> →</span></div>
|
||||
<div class="mslot budget">
|
||||
<span class="tier">Budget</span>
|
||||
<div class="mlogo">QW</div>
|
||||
<div class="mname"><b>Qwen 2.5 · local</b> <span>· free · runs on this Mac</span></div>
|
||||
<span class="mstatus local">● running</span>
|
||||
<span class="chg">Change</span>
|
||||
</div>
|
||||
|
||||
<div class="budget-row">
|
||||
<div class="bl"><b>Daily budget</b> · switch to the budget model at 80%</div>
|
||||
<input value="50" type="number"><span style="color:var(--text-dim);font-family:var(--mono);font-size:13px">USD</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grp">Provider keys · managed in the Vault</div>
|
||||
<div class="note2" style="background:var(--bg-2);border-color:var(--line-soft)"><svg viewBox="0 0 24 24" style="stroke:var(--text-muted)"><rect x="4" y="10" width="16" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg><p>Keys are encrypted on this machine. This list is read-only — add or rotate keys in the <b>Vault</b>.</p></div>
|
||||
<div class="keyrow"><span class="kd on"></span><span class="kn">Anthropic <small>Claude</small></span><span class="ks on">✓ key set</span></div>
|
||||
<div class="keyrow"><span class="kd on"></span><span class="kn">OpenAI <small>GPT</small></span><span class="ks on">✓ key set</span></div>
|
||||
<div class="keyrow"><span class="kd off"></span><span class="kn">Google <small>Gemini</small></span><span class="ks off">add in Vault</span></div>
|
||||
|
||||
<div class="grp">Local models · on this machine</div>
|
||||
<div class="keyrow"><span class="kd on"></span><span class="kn">Qwen 2.5 7B <small>via Ollama</small></span><span class="ks on">● running</span></div>
|
||||
<div class="keyrow"><span class="kd off"></span><span class="kn">Llama 3.2 <small>via Ollama</small></span><span class="ks off">pull to use</span></div>`,
|
||||
|
||||
general: () => `
|
||||
<div class="grp">Appearance</div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Theme</b><span>Match the hive to your eyes.</span></div></div>
|
||||
<div class="theme-cards" style="margin-top:14px" id="themeCards">
|
||||
<div class="tc dk" data-t="dark"><div class="lbl">Dark</div><div class="dots"><i style="background:#14110b;border:1px solid #38301f"></i><i style="background:#e9a52c"></i><i style="background:#272117"></i></div></div>
|
||||
<div class="tc lt" data-t="light"><div class="lbl">Light</div><div class="dots"><i style="background:#fffdf8;border:1px solid #e4d8be"></i><i style="background:#b57d12"></i><i style="background:#efe6d2"></i></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Language</b><span>Interface language.</span></div><div class="seg2"><button class="on">English</button><button>Deutsch</button><button>+</button></div></div></div>
|
||||
|
||||
<div class="grp">Your data</div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Local-first storage</b><span>Memory & files stay on this machine unless you share a workspace.</span></div><span class="plan-badge" style="font-size:11.5px">⬡ On · always</span></div></div>
|
||||
<div class="card" style="border-color:color-mix(in srgb,var(--risk) 25%,var(--line-soft))"><div class="field-row"><div class="lt"><b>Erase all data</b><span>Schedule a complete, unrecoverable wipe at next launch.</span></div><button class="btn danger"><svg viewBox="0 0 24 24"><path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13"/></svg>Erase…</button></div></div>`,
|
||||
|
||||
perms: () => `
|
||||
<div class="grp">Default approval level <span style="color:var(--text-dim);text-transform:none;letter-spacing:0">— new chats inherit this; each window can override</span></div>
|
||||
<div class="radio-row"><div class="rd"></div><div><b>Ask every time</b><p>Approve every write, edit, and mutating tool call. Safest.</p></div></div>
|
||||
<div class="radio-row sel"><div class="rd"></div><div><b>Ask only for risky things</b><p>Auto-pass routine writes & edits; still gate git push, installs, and anything that leaves your machine. Recommended.</p></div></div>
|
||||
<div class="radio-row"><div class="rd"></div><div><b>Never ask</b><p>Auto-pass everything except a hardcoded critical blacklist. Fastest, for trusted flows.</p></div></div>
|
||||
<div class="grp">Always require approval for</div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b class="mono" style="font-family:var(--mono);font-size:12.5px">git push</b></div><span class="ks off" style="color:var(--text-dim);font-size:12px;cursor:pointer">Remove</span></div></div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b class="mono" style="font-family:var(--mono);font-size:12.5px">anything → external system</b></div><span class="ks off" style="color:var(--text-dim);font-size:12px;cursor:pointer">Remove</span></div></div>`,
|
||||
|
||||
billing: () => `
|
||||
<div class="card" style="border-color:var(--honey-line)"><div class="field-row"><div class="lt"><b>Pro · trial</b><span>11 days left · $19/mo after · unlimited workspaces, marketplace, all connectors</span></div><span class="plan-badge">PRO</span></div></div>
|
||||
<div class="grp">Switch plan</div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Teams</b><span>$49/seat · shared memory, WaggleDance, SSO & governance</span></div><button class="btn ghost">Upgrade →</button></div></div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>KVARK · sovereign</b><span>On your own infrastructure, full audit trail. By Egzakta Group.</span></div><button class="btn ghost">Talk to sales →</button></div></div>
|
||||
<div style="margin-top:12px"><button class="btn ghost">Manage subscription</button></div>`,
|
||||
|
||||
team: () => `
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Status</b><span>Not connected to a team server.</span></div><span class="ks off" style="color:var(--text-dim)">● Disconnected</span></div></div>
|
||||
<div class="note2"><svg viewBox="0 0 24 24"><path d="M12 9v4m0 4h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg><p>Connecting to a team server <b>shares the workspaces you choose</b>. Only connect to a server you trust.</p></div>
|
||||
<div class="card"><div class="lt" style="margin-bottom:10px"><b>Team server URL</b></div><input style="width:100%;padding:10px 13px;border-radius:9px;border:1px solid var(--line);background:var(--surface);color:var(--text);font-family:var(--sans);font-size:14px;outline:none" placeholder="https://team.waggle.ai"></div>
|
||||
<button class="btn go">Connect</button>`,
|
||||
|
||||
backup: () => `
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Export everything</b><span>All workspaces, sessions, and memory as a zip.</span></div><button class="btn ghost"><svg viewBox="0 0 24 24"><path d="M12 3v12m0 0 4-4m-4 4-4-4M5 21h14"/></svg>Export</button></div></div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Import</b><span>From a Waggle export, or ChatGPT / Claude / Cursor history.</span></div><button class="btn ghost"><svg viewBox="0 0 24 24"><path d="M12 21V9m0 0 4 4m-4-4-4 4M5 3h14"/></svg>Import</button></div></div>
|
||||
<div class="grp">Encrypted backup</div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>AES-256 backup</b><span>Restore on any machine with the same vault key.</span></div><div style="display:flex;gap:8px"><button class="btn go">Create</button><button class="btn ghost">Restore</button></div></div></div>`,
|
||||
|
||||
advanced: () => `
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Developer mode</b><span>Show raw events, tool I/O, and the prompt inspector.</span></div><div class="sw" onclick="this.classList.toggle('on')"></div></div></div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Anonymous telemetry</b><span>Local-only usage counters — never leaves the machine. 0 events.</span></div><div class="sw" onclick="this.classList.toggle('on')"></div></div></div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Prompt shape</b><span>GEPA-evolved prompt variant for the agent runtime.</span></div><div class="seg2"><button class="on">Auto</button><button>Concise</button><button>Thorough</button></div></div></div>
|
||||
<div class="card"><div class="field-row"><div class="lt"><b>Debug logging</b><span>Verbose logs to the local console.</span></div><div class="sw" onclick="this.classList.toggle('on')"></div></div></div>`,
|
||||
};
|
||||
|
||||
function show(s){
|
||||
document.querySelectorAll('.ritem').forEach(r => r.classList.toggle('on', r.dataset.s===s));
|
||||
document.getElementById('sTitle').textContent = heads[s][0];
|
||||
document.getElementById('sSub').textContent = heads[s][1];
|
||||
document.getElementById('bodyc').innerHTML = panels[s]();
|
||||
document.getElementById('bodyc').scrollTop = 0;
|
||||
if(s==='general'){ const cur = root.getAttribute('data-theme'); document.querySelectorAll('.tc').forEach(c=>c.classList.toggle('sel', c.dataset.t===cur)); document.getElementById('themeCards').addEventListener('click', e=>{const c=e.target.closest('.tc'); if(c) setTheme(c.dataset.t);}); }
|
||||
}
|
||||
document.getElementById('rail').addEventListener('click', e => { const r=e.target.closest('.ritem'); if(r) show(r.dataset.s); });
|
||||
|
||||
/* radio rows + key/perms interactions */
|
||||
document.getElementById('bodyc').addEventListener('click', e => {
|
||||
const r = e.target.closest('.radio-row'); if(r){ r.parentElement.querySelectorAll('.radio-row').forEach(x=>x.classList.remove('sel')); r.classList.add('sel'); }
|
||||
});
|
||||
|
||||
/* experience level (progressive disclosure) */
|
||||
const labels = { essential:'Calm by default · the essentials only', standard:'Standard · full workspace tools', everything:'Everything · all controls visible' };
|
||||
document.getElementById('exp').addEventListener('click', e => {
|
||||
const b = e.target.closest('button'); if(!b) return;
|
||||
[...e.currentTarget.children].forEach(x=>x.classList.toggle('on', x===b));
|
||||
document.body.classList.toggle('everything', b.dataset.e==='everything');
|
||||
document.getElementById('vlabel').textContent = labels[b.dataset.e];
|
||||
if(b.dataset.e!=='everything' && document.querySelector('.ritem[data-s=advanced]').classList.contains('on')) show('models');
|
||||
});
|
||||
|
||||
show('models');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
225
docs/design_handoff_waggle_app/design-files/screens/storage.html
Normal file
225
docs/design_handoff_waggle_app/design-files/screens/storage.html
Normal file
@@ -0,0 +1,225 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Storage & files</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 13px; border-radius: 7px; transition: .14s; white-space: nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); } .controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
.stage { flex: 1; min-height: 0; overflow: auto; }
|
||||
|
||||
/* ── A: storage map ── */
|
||||
.wrap { max-width: 940px; margin: 0 auto; padding: 30px 32px 70px; }
|
||||
.head h1 { font-size: 26px; font-weight: 650; letter-spacing:-0.02em; margin: 0 0 8px; }
|
||||
.head .crumbs { font-family: var(--mono); font-size: 11px; color: var(--text-dim); margin-bottom: 12px; } .head .crumbs b { color: var(--text-2); font-weight: 500; }
|
||||
.head p { font-size: 15px; color: var(--text-muted); line-height: 1.55; margin: 0; max-width: 62ch; }
|
||||
|
||||
.liveloc { margin-top: 24px; border: 1px solid var(--honey-line); border-radius: var(--r-xl); background: linear-gradient(150deg, var(--surface-2), var(--surface)); padding: 24px; box-shadow: var(--honey-glow); }
|
||||
.liveloc .top { display: flex; align-items: center; gap: 13px; margin-bottom: 18px; }
|
||||
.liveloc .wm { width: 40px; height: 44px; flex:none; display:grid; place-items:center; font-weight:800; font-size:15px; color:#1a1407; background: linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.liveloc .top b { font-size: 16px; font-weight: 650; } .liveloc .top .sub { font-size: 12px; color: var(--text-muted); }
|
||||
.liveloc .top .pillk { margin-left:auto; font-size: 12px; font-weight: 600; color: var(--honey); background: var(--honey-wash); border: 1px solid var(--honey-line); padding: 6px 12px; border-radius: 999px; }
|
||||
.tree { font-family: var(--mono); font-size: 12.5px; background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: var(--r); padding: 14px 16px; }
|
||||
.tline { display: flex; align-items: baseline; gap: 12px; line-height: 1.95; }
|
||||
.tline .nm { flex: 1; }
|
||||
.tree .dir { color: var(--honey); } .tree .f .nm { color: var(--text-2); } .tree .note { color: var(--text-dim); }
|
||||
.tree .sz { color: var(--text-dim); white-space: nowrap; }
|
||||
|
||||
.sec-h { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); margin: 30px 0 14px; display:flex; align-items:center; gap:10px; }
|
||||
.sec-h::after { content:''; flex:1; height:1px; background: var(--line-soft); }
|
||||
|
||||
.types { display: grid; grid-template-columns: repeat(3,1fr); gap: 14px; }
|
||||
.type { border: 1px solid var(--line-soft); background: var(--surface); border-radius: var(--r-lg); padding: 20px; position: relative; }
|
||||
.type.active { border-color: var(--honey-line); }
|
||||
.type .ic { width: 36px; height: 40px; display:grid; place-items:center; margin-bottom: 12px; }
|
||||
.type .ic svg { width: 19px; height: 19px; fill:none; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
|
||||
.type.virtual .ic svg { stroke: var(--intel); } .type.local .ic svg { stroke: var(--honey); } .type.team .ic svg { stroke: var(--healthy); }
|
||||
.type h3 { font-size: 16px; font-weight: 650; letter-spacing:-0.01em; margin: 0 0 4px; }
|
||||
.type .where { font-family: var(--mono); font-size: 10.5px; color: var(--text-dim); margin-bottom: 10px; }
|
||||
.type p { font-size: 13px; color: var(--text-muted); line-height: 1.55; margin: 0 0 12px; }
|
||||
.type .priv { font-size: 11.5px; font-weight: 600; display:inline-flex; align-items:center; gap:6px; padding: 4px 10px; border-radius: 999px; }
|
||||
.type.virtual .priv { color: var(--intel); background: var(--intel-wash); }
|
||||
.type.local .priv { color: var(--honey); background: var(--honey-wash); }
|
||||
.type.team .priv { color: var(--healthy); background: var(--healthy-wash); }
|
||||
.type .cur { position:absolute; top:16px; right:16px; font-family: var(--mono); font-size: 9.5px; letter-spacing:.1em; text-transform:uppercase; color: var(--honey); }
|
||||
|
||||
.reassure { margin-top: 22px; display:flex; gap: 13px; align-items: flex-start; padding: 16px 18px; border-radius: var(--r-lg); background: var(--bg-2); border: 1px solid var(--line-soft); }
|
||||
.reassure svg { width: 20px; height: 20px; stroke: var(--healthy); fill:none; stroke-width:1.8; flex:none; margin-top:1px; }
|
||||
.reassure p { margin: 0; font-size: 13.5px; color: var(--text-muted); line-height: 1.6; } .reassure b { color: var(--text); }
|
||||
|
||||
/* ── B: file browser ── */
|
||||
.files { display: flex; height: 100%; }
|
||||
.ftree { flex: none; width: 220px; border-right: 1px solid var(--line-soft); background: var(--bg-2); padding: 16px 10px; overflow:auto; }
|
||||
.ftree .fh { font-family: var(--mono); font-size: 10px; letter-spacing:.12em; text-transform:uppercase; color: var(--text-dim); padding: 6px 10px 10px; }
|
||||
.ftree .row { display:flex; align-items:center; gap:9px; padding: 7px 10px; border-radius: 8px; font-size: 13px; color: var(--text-2); cursor:pointer; }
|
||||
.ftree .row:hover { background: var(--surface-2); } .ftree .row.on { background: var(--honey-wash); color: var(--text); }
|
||||
.ftree .row.in { padding-left: 26px; }
|
||||
.ftree .row svg { width: 15px; height: 15px; stroke: var(--honey); fill: none; stroke-width:1.7; flex:none; }
|
||||
.fmain { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
||||
.fbar { flex:none; display:flex; align-items:center; gap:12px; padding: 13px 20px; border-bottom: 1px solid var(--line-soft); }
|
||||
.fbar .bc { font-size: 13px; color: var(--text-muted); } .fbar .bc b { color: var(--text); font-weight:600; }
|
||||
.fbar .acts { margin-left:auto; display:flex; gap:8px; }
|
||||
.fbar .ab { font-size: 12px; font-weight:600; color: var(--text-2); padding: 7px 12px; border-radius: 8px; border:1px solid var(--line-strong); background: var(--surface); cursor:pointer; display:inline-flex; align-items:center; gap:6px; }
|
||||
.fbar .ab:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
.flist { flex:1; min-height:0; overflow:auto; padding: 8px 12px; }
|
||||
.ftab { width:100%; border-collapse: collapse; }
|
||||
.ftab th { text-align:left; font-family: var(--mono); font-size: 10px; letter-spacing:.06em; text-transform:uppercase; color: var(--text-dim); font-weight: 600; padding: 8px 10px; border-bottom: 1px solid var(--line-soft); }
|
||||
.ftab th.r, .ftab td.r { text-align: right; }
|
||||
.ftab td { padding: 10px; border-bottom: 1px solid var(--line-soft); font-size: 13.5px; color: var(--text-2); }
|
||||
.ftab tr { cursor: pointer; } .ftab tbody tr:hover td { background: var(--surface-2); }
|
||||
.ftab tr.sel td { background: var(--honey-wash); }
|
||||
.ftab .nmc { display:flex; align-items:center; gap:11px; } .ftab .nmc b { color: var(--text); font-weight: 550; }
|
||||
.ftab .fi { width: 28px; height: 30px; flex:none; display:grid; place-items:center; font-family: var(--mono); font-size: 9px; font-weight:700; border-radius: 6px; }
|
||||
.ftab .prov { font-family: var(--mono); font-size: 10.5px; color: var(--intel); }
|
||||
.fstatus { flex:none; display:flex; align-items:center; justify-content:space-between; padding: 9px 20px; border-top: 1px solid var(--line-soft); font-size: 12px; color: var(--text-muted); }
|
||||
.fstatus .loc { display:inline-flex; align-items:center; gap:8px; font-family: var(--mono); font-size: 11px; }
|
||||
.fstatus .loc .dot { background: var(--honey); }
|
||||
|
||||
.hidden { display:none !important; }
|
||||
@media (max-width:820px){ .types { grid-template-columns: 1fr; } .ftree { width: 160px; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Storage · variation</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="a">A · Where it lives</button>
|
||||
<button data-v="b">B · Files</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Where it lives</b> — the workspace, made legible</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<!-- ════ A — STORAGE MAP ════ -->
|
||||
<div class="wrap view" id="view-a">
|
||||
<div class="head">
|
||||
<div class="crumbs">Competitive Intelligence <span>›</span> <b>Storage</b></div>
|
||||
<h1>Where this workspace lives</h1>
|
||||
<p>A workspace is one place on disk: its memory, its files, its artifacts. Waggle shows you exactly where — and keeps it local unless you choose to share.</p>
|
||||
</div>
|
||||
|
||||
<div class="liveloc">
|
||||
<div class="top">
|
||||
<div class="wm hex">C</div>
|
||||
<div><b>Competitive Intelligence</b><div class="sub">Local workspace · on this Mac</div></div>
|
||||
<span class="pillk">⬡ Local · private</span>
|
||||
</div>
|
||||
<div class="tree">
|
||||
<div class="tline note"><span class="nm">~/Waggle/workspaces/</span></div>
|
||||
<div class="tline dir"><span class="nm" style="padding-left:1.4em">competitive-intelligence/</span></div>
|
||||
<div class="tline f"><span class="nm" style="padding-left:2.8em">hive.mind</span><span class="sz">3.2 MB · 142 memories</span></div>
|
||||
<div class="tline f"><span class="nm" style="padding-left:2.8em">files/</span><span class="sz">12 files · 4.1 MB</span></div>
|
||||
<div class="tline f"><span class="nm" style="padding-left:2.8em">artifacts/</span><span class="sz">7 items</span></div>
|
||||
<div class="tline f"><span class="nm" style="padding-left:2.8em">workspace.json</span><span class="sz">config</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-h">Three ways a workspace can live</div>
|
||||
<div class="types">
|
||||
<div class="type virtual">
|
||||
<div class="ic"><svg viewBox="0 0 24 24"><path d="M4 7c0-1.5 3.6-2.5 8-2.5s8 1 8 2.5-3.6 2.5-8 2.5-8-1-8-2.5z"/><path d="M4 7v10c0 1.5 3.6 2.5 8 2.5s8-1 8-2.5V7"/></svg></div>
|
||||
<h3>Virtual</h3>
|
||||
<div class="where">managed by Waggle</div>
|
||||
<p>Files Waggle keeps for you inside the app — no folder to manage. Perfect for quick, throwaway work.</p>
|
||||
<span class="priv">⬡ Only you · session</span>
|
||||
</div>
|
||||
<div class="type local active">
|
||||
<span class="cur">this one</span>
|
||||
<div class="ic"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M3 9h18M8 21h8"/></svg></div>
|
||||
<h3>Local</h3>
|
||||
<div class="where">a folder on your machine</div>
|
||||
<p>Bound to a real folder you can open in Finder. Your agents read and write the same files you do.</p>
|
||||
<span class="priv">⬡ Only you · on disk</span>
|
||||
</div>
|
||||
<div class="type team">
|
||||
<div class="ic"><svg viewBox="0 0 24 24"><circle cx="9" cy="8" r="3"/><path d="M3.5 20a5.5 5.5 0 0 1 11 0M16 6a3 3 0 0 1 0 6M21 20a5 5 0 0 0-4-4.9"/></svg></div>
|
||||
<h3>Team</h3>
|
||||
<div class="where">shared, synced</div>
|
||||
<p>A shared memory + file space for a team — same workspace, role-based access, synced across seats.</p>
|
||||
<span class="priv">⬡ Shared with team</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="reassure">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/><path d="M9 12l2 2 4-4"/></svg>
|
||||
<p><b>Local-first, always.</b> Memory and files for Virtual and Local workspaces never leave this machine — there's nothing for anyone to leak, subpoena, or sell. Switch a workspace to Team only when you mean to share it, and you can export or delete any workspace, whole, at any time.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════ B — FILES ════ -->
|
||||
<div class="files view hidden" id="view-b">
|
||||
<aside class="ftree">
|
||||
<div class="fh">Competitive Intel</div>
|
||||
<div class="row on"><svg viewBox="0 0 24 24"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>Root</div>
|
||||
<div class="row in"><svg viewBox="0 0 24 24"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>research</div>
|
||||
<div class="row in"><svg viewBox="0 0 24 24"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>exports</div>
|
||||
</aside>
|
||||
<div class="fmain">
|
||||
<div class="fbar">
|
||||
<span class="bc">Root <span style="color:var(--text-dim)">/</span> <b>Competitive Intelligence</b></span>
|
||||
<div class="acts">
|
||||
<span class="ab"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>New folder</span>
|
||||
<span class="ab"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12m0 0 4-4m-4 4-4-4M5 21h14"/></svg>Upload</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flist">
|
||||
<table class="ftab">
|
||||
<thead><tr><th>Name</th><th>Source</th><th class="r">Size</th><th class="r">Modified</th></tr></thead>
|
||||
<tbody id="fbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="fstatus">
|
||||
<span>12 items · 4.1 MB</span>
|
||||
<span class="loc"><span class="dot"></span>Local · private · ~/Waggle/workspaces/competitive-intelligence/files</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const files = [
|
||||
{ ext:'DIR', c:'var(--honey)', n:'research', dir:true, src:'—', size:'—', when:'2d ago' },
|
||||
{ ext:'DIR', c:'var(--honey)', n:'exports', dir:true, src:'—', size:'—', when:'2h ago' },
|
||||
{ ext:'MD', c:'var(--work)', n:'teardown.md', src:'made by Waggle', size:'48 KB', when:'2h ago' },
|
||||
{ ext:'XLS', c:'var(--healthy)', n:'pricing-landscape.xlsx', src:'made by Waggle', size:'126 KB', when:'2h ago' },
|
||||
{ ext:'PDF', c:'var(--risk)', n:'mem0-teardown.pdf', src:'you uploaded', size:'1.4 MB', when:'1d ago' },
|
||||
{ ext:'PDF', c:'var(--risk)', n:'letta-pricing.pdf', src:'you uploaded', size:'880 KB', when:'1d ago' },
|
||||
{ ext:'MD', c:'var(--work)', n:'board-brief.md', src:'made by Claude Code', size:'22 KB', when:'18m ago' },
|
||||
];
|
||||
document.getElementById('fbody').innerHTML = files.map(f=>`
|
||||
<tr><td><div class="nmc"><span class="fi" style="background:color-mix(in srgb,${f.c} 18%,transparent);color:${f.c}">${f.dir?'<svg width=14 height=14 viewBox="0 0 24 24" fill=none stroke="'+f.c+'" stroke-width=1.8><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>':f.ext}</span><b>${f.n}</b></div></td><td>${f.src==='—'?'<span style="color:var(--text-dim)">—</span>':`<span class="prov">⬡ ${f.src}</span>`}</td><td class="r" style="color:var(--text-dim)">${f.size}</td><td class="r" style="color:var(--text-dim)">${f.when}</td></tr>`).join('');
|
||||
document.querySelectorAll('#fbody tr').forEach(tr => tr.onclick = () => { document.querySelectorAll('#fbody tr').forEach(x=>x.classList.remove('sel')); tr.classList.add('sel'); });
|
||||
|
||||
const labels = { a:'<b>Where it lives</b> — the workspace, made legible', b:'<b>Files</b> — a clean browser, with provenance' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e => {
|
||||
const b = e.target.closest('button'); if(!b) return;
|
||||
[...seg.children].forEach(x=>x.classList.toggle('on', x===b));
|
||||
document.getElementById('view-a').classList.toggle('hidden', b.dataset.v!=='a');
|
||||
document.getElementById('view-b').classList.toggle('hidden', b.dataset.v!=='b');
|
||||
document.getElementById('vlabel').innerHTML = labels[b.dataset.v];
|
||||
document.querySelector('.stage').scrollTop = 0;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,217 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Power surfaces</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); } .controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.controls .hint { font-family: var(--mono); font-size: 11px; color: var(--text-dim); }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
.app { flex: 1; min-height: 0; display: flex; }
|
||||
/* rail */
|
||||
.rail { flex: none; width: 200px; border-right: 1px solid var(--line-soft); background: var(--bg-2); padding: 16px 10px; }
|
||||
.rail .rh { font-family: var(--mono); font-size: 10px; letter-spacing:.12em; text-transform:uppercase; color: var(--text-dim); padding: 6px 11px 12px; }
|
||||
.ritem { display:flex; align-items:center; gap:11px; padding: 9px 11px; border-radius: 10px; cursor:pointer; color: var(--text-2); transition:.13s; font-size: 13.5px; font-weight: 550; position: relative; }
|
||||
.ritem:hover { background: var(--surface-2); color: var(--text); }
|
||||
.ritem.on { background: var(--honey-wash); color: var(--text); }
|
||||
.ritem.on::before { content:''; position:absolute; left:-10px; top:50%; transform:translateY(-50%); width:3px; height:17px; border-radius:3px; background: var(--honey); }
|
||||
.ritem svg { width:17px; height:17px; stroke: currentColor; fill:none; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; flex:none; }
|
||||
.ritem.on svg { stroke: var(--honey); }
|
||||
.ritem .ct { margin-left:auto; font-family: var(--mono); font-size: 10px; color: var(--text-dim); }
|
||||
.ritem .ct.warn { color: var(--attention); }
|
||||
|
||||
/* main */
|
||||
.main { flex:1; min-width:0; display:flex; flex-direction:column; }
|
||||
.shead { flex:none; display:flex; align-items:flex-start; gap:16px; padding: 26px 30px 18px; border-bottom: 1px solid var(--line-soft); }
|
||||
.shead h1 { font-size: 22px; font-weight: 650; letter-spacing:-0.02em; margin: 0 0 5px; }
|
||||
.shead p { font-size: 13.5px; color: var(--text-muted); margin: 0; max-width: 60ch; line-height:1.5; }
|
||||
.shead .pa { margin-left:auto; font-size: 12.5px; font-weight: 650; padding: 9px 15px; border-radius: 9px; background: var(--honey); color:#1a1407; cursor:pointer; white-space:nowrap; border:0; display:inline-flex; align-items:center; gap:7px; }
|
||||
.shead .pa:hover { background: var(--honey-bright); }
|
||||
.body { flex:1; min-height:0; overflow:auto; padding: 22px 30px 50px; }
|
||||
.panel { display:none; } .panel.on { display:block; }
|
||||
.grp { font-family: var(--mono); font-size: 10.5px; letter-spacing:.12em; text-transform:uppercase; color: var(--text-dim); margin: 4px 0 12px; display:flex; align-items:center; gap:10px; }
|
||||
.grp:not(:first-child) { margin-top: 26px; }
|
||||
.grp::after { content:''; flex:1; height:1px; background: var(--line-soft); }
|
||||
|
||||
.rows { display:grid; gap: 9px; }
|
||||
.rc { display:flex; align-items:center; gap:14px; padding: 14px 16px; border-radius: var(--r); border: 1px solid var(--line-soft); background: var(--surface); transition:.14s; }
|
||||
.rc:hover { border-color: var(--line-strong); }
|
||||
.rc .ri { width: 34px; height: 38px; flex:none; display:grid; place-items:center; font-family: var(--mono); font-size:11px; font-weight:700; color:var(--text-2); background: var(--surface-2); border:1px solid var(--line); }
|
||||
.rc .rt { flex:1; min-width:0; }
|
||||
.rc .rt b { font-size: 14.5px; font-weight: 600; }
|
||||
.rc .rt .rs { font-size: 12.5px; color: var(--text-muted); margin-top: 2px; }
|
||||
.rc .rt .rs .mono { font-family: var(--mono); font-size: 11.5px; color: var(--text-dim); }
|
||||
.rc .rt .rs .prov { color: var(--intel); }
|
||||
.rc .meta { font-family: var(--mono); font-size: 11px; color: var(--text-dim); white-space:nowrap; }
|
||||
.bdg { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 999px; display:inline-flex; align-items:center; gap:5px; white-space:nowrap; }
|
||||
.bdg.ok { color: var(--healthy); background: var(--healthy-wash); }
|
||||
.bdg.warn { color: var(--attention); background: var(--honey-wash); }
|
||||
.bdg.risk { color: var(--risk); background: var(--risk-wash); }
|
||||
.bdg.off { color: var(--text-dim); background: var(--surface-2); }
|
||||
.lnk { font-size: 12px; font-weight: 600; color: var(--text-2); cursor:pointer; } .lnk:hover { color: var(--honey); }
|
||||
.rbtn { font-size: 12px; font-weight: 650; padding: 7px 13px; border-radius: 8px; cursor:pointer; border:1px solid transparent; }
|
||||
.rbtn.go { background: var(--honey); color:#1a1407; } .rbtn.go:hover { background: var(--honey-bright); }
|
||||
.rbtn.ghost { background: var(--surface-2); color: var(--text-2); border-color: var(--line-strong); } .rbtn.ghost:hover { border-color: var(--honey-line); color: var(--text); }
|
||||
.rbtn.danger { background: transparent; color: var(--risk); border-color: color-mix(in srgb,var(--risk) 35%,transparent); }
|
||||
.acts { display:flex; gap:8px; align-items:center; }
|
||||
|
||||
/* toggle */
|
||||
.sw { width: 38px; height: 22px; border-radius: 999px; background: var(--surface-3); border:1px solid var(--line-strong); position:relative; cursor:pointer; transition:.16s; flex:none; }
|
||||
.sw::after { content:''; position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:999px; background: var(--text-muted); transition:.16s; }
|
||||
.sw.on { background: var(--honey); border-color: var(--honey); } .sw.on::after { left:18px; background:#1a1407; }
|
||||
|
||||
/* approval cards */
|
||||
.appr { border:1px solid var(--line-soft); border-radius: var(--r-lg); background: var(--surface); padding: 18px; }
|
||||
.appr.hot { border-color: color-mix(in srgb,var(--attention) 35%,transparent); background: var(--honey-wash); }
|
||||
.appr .at { display:flex; align-items:center; gap:10px; margin-bottom: 8px; }
|
||||
.appr .at b { font-size: 14.5px; font-weight: 650; }
|
||||
.appr p { margin: 0 0 14px; font-size: 13px; color: var(--text-muted); line-height:1.55; }
|
||||
.appr p .mono { font-family: var(--mono); font-size:11.5px; color: var(--text-2); }
|
||||
.appr .ar { display:flex; gap:9px; align-items:center; }
|
||||
|
||||
/* usage */
|
||||
.ustat { display:grid; grid-template-columns: repeat(3,1fr); gap:14px; margin-bottom: 22px; }
|
||||
.ust { padding: 18px; border-radius: var(--r-lg); border:1px solid var(--line-soft); background: var(--surface); }
|
||||
.ust .v { font-size: 30px; font-weight: 750; letter-spacing:-0.03em; line-height:1; }
|
||||
.ust .l { font-size: 12px; color: var(--text-muted); margin-top: 7px; }
|
||||
.ubar { display:grid; grid-template-columns: 130px 1fr 70px; gap:14px; align-items:center; margin-bottom: 10px; }
|
||||
.ubar .ul { font-size: 13px; font-weight:600; text-align:right; }
|
||||
.ubar .ut { height: 26px; border-radius: 7px; background: var(--surface-2); border:1px solid var(--line-soft); overflow:hidden; }
|
||||
.ubar .uf { height:100%; }
|
||||
.ubar .uv { font-family: var(--mono); font-size: 12.5px; color: var(--text-2); }
|
||||
|
||||
@media (max-width: 800px){ .rail { width: 64px; } .rail .rh, .ritem span.txt, .ritem .ct { display:none; } .ustat { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Power surfaces · ⌘K destinations</span>
|
||||
<span class="vlabel" id="vlabel"><b>One template</b>, every tool — clean & consistent</span>
|
||||
<div class="right"><span class="hint">these all live behind ⌘K</span><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="app">
|
||||
<nav class="rail" id="rail">
|
||||
<div class="rh">Behind ⌘K</div>
|
||||
<div class="ritem on" data-s="tools"><svg viewBox="0 0 24 24"><path d="M7 8a4 4 0 0 0 0 8h2M17 8a4 4 0 0 1 0 8h-2M8 12h8"/></svg><span class="txt">Tools</span><span class="ct">7</span></div>
|
||||
<div class="ritem" data-s="auto"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/><path d="M12 8v4l3 2"/></svg><span class="txt">Automations</span><span class="ct warn">1</span></div>
|
||||
<div class="ritem" data-s="appr"><svg viewBox="0 0 24 24"><path d="M9 12l2 2 4-4M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/></svg><span class="txt">Approvals</span><span class="ct warn">2</span></div>
|
||||
<div class="ritem" data-s="vault"><svg viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="3"/><circle cx="12" cy="12" r="3"/></svg><span class="txt">Vault</span><span class="ct">5</span></div>
|
||||
<div class="ritem" data-s="usage"><svg viewBox="0 0 24 24"><path d="M4 20V10M10 20V4M16 20v-7M22 20H2"/></svg><span class="txt">Usage</span></div>
|
||||
</nav>
|
||||
|
||||
<div class="main">
|
||||
<div class="shead">
|
||||
<div><h1 id="sTitle">Tools & connections</h1><p id="sSub">Everything your agents can reach — integrations, MCP servers, and skills.</p></div>
|
||||
<button class="pa" id="sAction"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg><span id="sActionT">Add a tool</span></button>
|
||||
</div>
|
||||
<div class="body" id="body"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const head = {
|
||||
tools: ['Tools & connections','Everything your agents can reach — integrations, MCP servers, and skills.','Add a tool'],
|
||||
auto: ['Automations','Work that runs on a schedule, while you\u2019re away.','New automation'],
|
||||
appr: ['Approvals','Actions your agents are holding until you say go.','Approval settings'],
|
||||
vault: ['Vault','Secrets, encrypted on this machine. Agents use them; they never see them.','Add secret'],
|
||||
usage: ['Usage & cost','Tokens and spend across models and workspaces.','Set budget'],
|
||||
};
|
||||
|
||||
function rc(icon, title, sub, right){ return `<div class="rc"><div class="ri">${icon}</div><div class="rt"><b>${title}</b><div class="rs">${sub}</div></div>${right}</div>`; }
|
||||
function tog(on){ return `<div class="sw ${on?'on':''}" onclick="this.classList.toggle('on')"></div>`; }
|
||||
|
||||
const panels = {
|
||||
tools: () => `
|
||||
<div class="grp">Connected integrations</div>
|
||||
<div class="rows">
|
||||
${rc('SF','Salesforce','<span class="rs">CRM · used by 2 agents · <span class="mono">Q2 Pricing</span></span>','<div class="acts"><span class="bdg ok">● Connected</span><span class="lnk">Manage</span></div>')}
|
||||
${rc('Sl','Slack','Notifications & digests','<div class="acts"><span class="bdg ok">● Connected</span><span class="lnk">Manage</span></div>')}
|
||||
${rc('Gh','GitHub','Repos & issues','<div class="acts"><span class="bdg ok">● Connected</span><span class="lnk">Manage</span></div>')}
|
||||
</div>
|
||||
<div class="grp">MCP servers · 21 tools</div>
|
||||
<div class="rows">
|
||||
${rc('fs','Filesystem','<span class="rs">read/write workspace files · <span class="mono">6 tools</span></span>','<span class="bdg ok">● Active</span>')}
|
||||
${rc('web','Web search','<span class="rs">live retrieval · <span class="mono">3 tools</span></span>','<span class="bdg ok">● Active</span>')}
|
||||
</div>
|
||||
<div class="grp">Available</div>
|
||||
<div class="rows">
|
||||
${rc('No','Notion','Docs & wikis','<button class="rbtn ghost">Connect</button>')}
|
||||
${rc('Li','Linear','Issues & projects','<button class="rbtn ghost">Connect</button>')}
|
||||
</div>`,
|
||||
auto: () => `
|
||||
<div class="rows">
|
||||
${rc('<svg width=16 height=16 viewBox="0 0 24 24" fill=none stroke="var(--healthy)" stroke-width=1.8><circle cx=12 cy=12 r=8/><path d="M12 8v4l3 2"/></svg>','Weekly digest','<span class="rs"><span class="mono">Mon 09:00</span> · last ran 3d ago · ok</span>','<div class="acts"><span class="lnk">Logs</span>'+tog(true)+'</div>')}
|
||||
${rc('<svg width=16 height=16 viewBox="0 0 24 24" fill=none stroke="var(--risk)" stroke-width=1.8><path d="M12 9v4m0 4h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg>','Q2 pipeline sync → Salesforce','<span class="rs"><span class="mono">Daily 02:00</span> · failed 02:14 · auth expired</span>','<div class="acts"><span class="bdg risk">Failed</span><button class="rbtn go">Fix & retry</button>'+tog(true)+'</div>')}
|
||||
${rc('<svg width=16 height=16 viewBox="0 0 24 24" fill=none stroke="var(--intel)" stroke-width=1.8><path d="M12 5a3 3 0 0 0-3 3 3 3 0 0 0-3 3 3 3 0 0 0 1 5 3 3 0 0 0 5 1 3 3 0 0 0 5-1 3 3 0 0 0 1-5 3 3 0 0 0-3-3 3 3 0 0 0-3-3Z"/></svg>','Nightly memory consolidation','<span class="rs"><span class="mono">Nightly 03:00</span> · last ran ok · +14 memories</span>','<div class="acts"><span class="lnk">Logs</span>'+tog(true)+'</div>')}
|
||||
${rc('<svg width=16 height=16 viewBox="0 0 24 24" fill=none stroke="var(--text-dim)" stroke-width=1.8><circle cx=12 cy=12 r=8/><path d="M12 8v4l3 2"/></svg>','Competitor price watch','<span class="rs"><span class="mono">Weekly</span> · paused</span>','<div class="acts"><span class="bdg off">Paused</span>'+tog(false)+'</div>')}
|
||||
</div>`,
|
||||
appr: () => `
|
||||
<div class="grp">Waiting on you</div>
|
||||
<div class="rows" style="gap:12px">
|
||||
<div class="appr hot"><div class="at"><span class="bdg warn">Medium risk</span><b>Export pricing table to Salesforce</b></div><p>Research-synth wants to write to an external system — <span class="mono">Salesforce › Q2 Pricing</span>. 9 rows, no deletes. From the Competitive Intelligence chat.</p><div class="ar"><button class="rbtn go">Approve</button><button class="rbtn ghost">Deny</button><span class="lnk">Always allow for this workspace</span></div></div>
|
||||
<div class="appr"><div class="at"><span class="bdg off">Low risk</span><b>Send the board update email</b></div><p>Deck-builder drafted an email to <span class="mono">board@egzakta</span>. Reads from this workspace only.</p><div class="ar"><button class="rbtn go">Approve</button><button class="rbtn ghost">Deny</button><span class="lnk">Review draft</span></div></div>
|
||||
</div>
|
||||
<div class="grp">Recently decided</div>
|
||||
<div class="rows">
|
||||
${rc('✓','Read competitor sites','<span class="rs">approved 2h ago · web fetch</span>','<span class="bdg ok">Approved</span>')}
|
||||
</div>`,
|
||||
vault: () => `
|
||||
<div class="rows">
|
||||
${rc('🔑','ANTHROPIC_API_KEY','<span class="rs"><span class="mono">sk-ant-•••••••••••3a9f</span> · used by the agent runtime</span>','<div class="acts"><span class="lnk">Rotate</span><span class="lnk">Reveal</span></div>')}
|
||||
${rc('🔑','OPENAI_API_KEY','<span class="rs"><span class="mono">sk-•••••••••••7c20</span> · LiteLLM routing</span>','<div class="acts"><span class="lnk">Rotate</span><span class="lnk">Reveal</span></div>')}
|
||||
${rc('🔑','Salesforce token','<span class="rs"><span class="mono">••••••••</span> · 1 connector · <span style="color:var(--risk)">expired</span></span>','<div class="acts"><button class="rbtn go">Renew</button></div>')}
|
||||
${rc('🔑','GitHub PAT','<span class="rs"><span class="mono">ghp_•••••••••••</span> · 1 connector</span>','<div class="acts"><span class="lnk">Rotate</span></div>')}
|
||||
</div>
|
||||
<div class="grp">How it\u2019s stored</div>
|
||||
<div class="rc"><div class="ri"><svg width=16 height=16 viewBox="0 0 24 24" fill=none stroke="var(--healthy)" stroke-width=1.8><path d="M12 3l8 4v5c0 5-3.5 7.5-8 9-4.5-1.5-8-4-8-9V7z"/><path d="M9 12l2 2 4-4"/></svg></div><div class="rt"><b>Encrypted, on this machine</b><div class="rs">Secrets live in the local vault. Agents reference them by name to call tools — the key value is never placed into a prompt or shown to a model.</div></div></div>`,
|
||||
usage: () => `
|
||||
<div class="ustat">
|
||||
<div class="ust"><div class="v">$12.40</div><div class="l">This month · est.</div></div>
|
||||
<div class="ust"><div class="v" style="color:var(--intel)">4.2M</div><div class="l">Tokens</div></div>
|
||||
<div class="ust"><div class="v" style="color:var(--healthy)">$50</div><div class="l">Budget · 25% used</div></div>
|
||||
</div>
|
||||
<div class="grp">By model</div>
|
||||
<div>
|
||||
<div class="ubar"><span class="ul">Claude Sonnet</span><div class="ut"><div class="uf" style="width:62%;background:linear-gradient(90deg,var(--honey-deep),var(--honey))"></div></div><span class="uv">$7.70</span></div>
|
||||
<div class="ubar"><span class="ul">GPT-5</span><div class="ut"><div class="uf" style="width:24%;background:var(--work)"></div></div><span class="uv">$3.00</span></div>
|
||||
<div class="ubar"><span class="ul">Qwen · local</span><div class="ut"><div class="uf" style="width:6%;background:var(--healthy)"></div></div><span class="uv">$0.00</span></div>
|
||||
</div>
|
||||
<div class="grp">By workspace</div>
|
||||
<div class="rows">
|
||||
${rc('C','Competitive Intelligence','<span class="rs">2.4M tokens</span>','<span class="meta">$6.90</span>')}
|
||||
${rc('B','Q2 Board Deck','<span class="rs">1.1M tokens</span>','<span class="meta">$3.80</span>')}
|
||||
${rc('R','Customer Research','<span class="rs">0.7M tokens</span>','<span class="meta">$1.70</span>')}
|
||||
</div>`,
|
||||
};
|
||||
|
||||
function show(s){
|
||||
document.querySelectorAll('.ritem').forEach(r => r.classList.toggle('on', r.dataset.s===s));
|
||||
document.getElementById('sTitle').innerHTML = head[s][0];
|
||||
document.getElementById('sSub').innerHTML = head[s][1];
|
||||
document.getElementById('sActionT').textContent = head[s][2];
|
||||
document.getElementById('body').innerHTML = panels[s]();
|
||||
document.getElementById('body').scrollTop = 0;
|
||||
}
|
||||
document.getElementById('rail').addEventListener('click', e => { const r = e.target.closest('.ritem'); if(r) show(r.dataset.s); });
|
||||
show('tools');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,338 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Workspace</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 13px; border-radius: 7px; transition: .14s; white-space: nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); }
|
||||
.controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
.stage { flex: 1; min-height: 0; overflow: auto; }
|
||||
|
||||
/* ── workspace header ── */
|
||||
.wshead { padding: 30px 36px 0; max-width: 1080px; margin: 0 auto; }
|
||||
.crumbs { font-family: var(--mono); font-size: 11.5px; color: var(--text-dim); margin-bottom: 16px; display: flex; align-items: center; gap: 8px; }
|
||||
.crumbs b { color: var(--text-2); font-weight: 500; }
|
||||
.wtitle-row { display: flex; align-items: center; gap: 16px; }
|
||||
.wtitle-row .wmark { width: 46px; height: 52px; flex: none; display: grid; place-items: center; font-weight: 800; font-size: 20px; color: #1a1407; background: linear-gradient(150deg, var(--honey-bright), var(--honey-deep)); box-shadow: var(--honey-glow); }
|
||||
.wtitle-row h1 { font-size: 28px; font-weight: 650; letter-spacing: -0.02em; margin: 0; }
|
||||
.wtitle-row .wmeta { font-size: 13px; color: var(--text-muted); margin-top: 3px; display: flex; gap: 14px; flex-wrap: wrap; }
|
||||
.wtitle-row .wmeta span { display: inline-flex; align-items: center; gap: 6px; }
|
||||
.wtitle-row .actions { margin-left: auto; display: flex; gap: 9px; }
|
||||
.btn { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-family: var(--sans); font-size: 13.5px; font-weight: 650; padding: 10px 17px; border-radius: 10px; border: 1px solid transparent; text-decoration:none; transition:.15s; }
|
||||
.btn-primary { background: var(--honey); color: #1a1407; }
|
||||
.btn-primary:hover { background: var(--honey-bright); }
|
||||
.btn-ghost { background: var(--surface); color: var(--text-2); border-color: var(--line-strong); }
|
||||
.btn-ghost:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
|
||||
/* ── tabs ── */
|
||||
.tabs { display: flex; gap: 2px; margin: 24px 0 0; border-bottom: 1px solid var(--line-soft); }
|
||||
.tab { font-size: 14px; font-weight: 600; color: var(--text-muted); padding: 12px 16px; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; transition:.14s; display:flex; align-items:center; gap:8px; }
|
||||
.tab:hover { color: var(--text); }
|
||||
.tab.on { color: var(--text); border-bottom-color: var(--honey); }
|
||||
.tab .cnt { font-family: var(--mono); font-size: 11px; color: var(--text-dim); }
|
||||
|
||||
/* ── content ── */
|
||||
.content { max-width: 1080px; margin: 0 auto; padding: 28px 36px 60px; }
|
||||
.grid { display: grid; grid-template-columns: 1.7fr 1fr; gap: 22px; }
|
||||
.col { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
|
||||
.sec-h { font-family: var(--mono); font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); margin: 0 0 14px; display: flex; align-items: center; gap: 10px; }
|
||||
.sec-h .ic { width: 15px; height: 15px; }
|
||||
.sec-h .ic svg { width: 15px; height: 15px; stroke: var(--honey); fill: none; stroke-width: 1.8; }
|
||||
|
||||
.card { background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-lg); padding: 22px; }
|
||||
.summary { font-size: 15.5px; line-height: 1.6; color: var(--text-2); }
|
||||
.summary b { color: var(--text); font-weight: 600; }
|
||||
|
||||
/* knows / memory cards */
|
||||
.knows { display: grid; gap: 10px; }
|
||||
.fact { display: grid; grid-template-columns: 30px 1fr; gap: 13px; padding: 13px 15px; border-radius: var(--r); background: var(--surface); border: 1px solid var(--line-soft); transition:.15s; }
|
||||
.fact:hover { border-color: var(--honey-line); }
|
||||
.fact .fi { width: 30px; height: 34px; flex:none; display:grid; place-items:center; }
|
||||
.fact .fi svg { width: 15px; height: 15px; stroke: #1a1407; fill:none; stroke-width:2; }
|
||||
.fact .ft { font-size: 14px; line-height: 1.5; color: var(--text); }
|
||||
.fact .ft b { font-weight: 650; }
|
||||
.fact .prov { margin-top: 7px; display: inline-flex; align-items: center; gap: 7px; font-family: var(--mono); font-size: 10.5px; color: var(--text-dim); }
|
||||
.fact .prov .src { color: var(--intel); }
|
||||
|
||||
/* artifacts */
|
||||
.arts { display: grid; gap: 9px; }
|
||||
.art { display: flex; align-items: center; gap: 13px; padding: 12px 15px; border-radius: var(--r); border: 1px solid var(--line-soft); background: var(--surface); cursor: pointer; transition:.15s; }
|
||||
.art:hover { border-color: var(--honey-line); background: var(--surface-2); }
|
||||
.art .ai { width: 32px; height: 32px; border-radius: 8px; flex:none; display:grid; place-items:center; font-family: var(--mono); font-size: 10px; font-weight: 700; }
|
||||
.art .at { flex: 1; min-width: 0; }
|
||||
.art .at b { font-size: 14px; font-weight: 600; display: block; }
|
||||
.art .at span { font-size: 12px; color: var(--text-dim); }
|
||||
.art .when { font-size: 11.5px; color: var(--text-dim); font-family: var(--mono); }
|
||||
|
||||
/* right rail */
|
||||
.stat-line { display: flex; align-items: center; gap: 10px; padding: 11px 0; border-bottom: 1px solid var(--line-soft); font-size: 13.5px; }
|
||||
.stat-line:last-child { border-bottom: 0; }
|
||||
.stat-line .l { color: var(--text-muted); flex: 1; }
|
||||
.stat-line .v { font-weight: 600; }
|
||||
.person { display: flex; align-items: center; gap: 11px; padding: 9px 0; }
|
||||
.person .pa { width: 30px; height: 30px; border-radius: 999px; flex:none; display:grid; place-items:center; font-size: 11px; font-weight: 700; color: #1a1407; }
|
||||
.person .pn { flex:1; font-size: 13.5px; }
|
||||
.person .pr { font-size: 11.5px; color: var(--text-dim); }
|
||||
.agentrow { display:flex; align-items:center; gap:10px; padding: 10px 0; border-bottom: 1px solid var(--line-soft); font-size:13.5px; }
|
||||
.agentrow:last-child { border-bottom:0; }
|
||||
.agentrow .an { flex:1; }
|
||||
.agentrow .am { font-family: var(--mono); font-size: 11px; color: var(--text-dim); }
|
||||
|
||||
/* ── memory-forward (variation B) ── */
|
||||
.graph-wrap { position: relative; height: 360px; border-radius: var(--r-xl); border: 1px solid var(--line); background: radial-gradient(80% 80% at 50% 45%, var(--surface), var(--bg-2)); overflow: hidden; }
|
||||
.graph-wrap .comb { position: absolute; inset: 0; opacity: .5; }
|
||||
.node { position: absolute; transform: translate(-50%,-50%); display: grid; place-items: center; text-align: center; }
|
||||
.node .nb { padding: 9px 13px; border-radius: 11px; background: var(--surface); border: 1px solid var(--line-strong); font-size: 12.5px; font-weight: 600; box-shadow: var(--shadow); white-space: nowrap; }
|
||||
.node.center .nb { background: linear-gradient(150deg, var(--surface-2), var(--surface)); border-color: var(--honey-line); box-shadow: var(--honey-glow); padding: 13px 18px; }
|
||||
.node.center .nb b { font-size: 14px; }
|
||||
.node .tag { font-family: var(--mono); font-size: 9px; letter-spacing: .1em; text-transform: uppercase; margin-bottom: 5px; }
|
||||
.node.person .tag { color: var(--work); } .node.person .nb { border-color: color-mix(in srgb, var(--work) 40%, var(--line-strong)); }
|
||||
.node.concept .tag { color: var(--intel); } .node.concept .nb { border-color: color-mix(in srgb, var(--intel) 40%, var(--line-strong)); }
|
||||
.node.org .tag { color: var(--risk); } .node.org .nb { border-color: color-mix(in srgb, var(--risk) 40%, var(--line-strong)); }
|
||||
.node.proj .tag { color: var(--healthy); } .node.proj .nb { border-color: color-mix(in srgb, var(--healthy) 40%, var(--line-strong)); }
|
||||
.graph-wrap svg { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||||
.gline { stroke: var(--line-strong); stroke-width: 1.2; }
|
||||
.glegend { position: absolute; bottom: 14px; left: 16px; display: flex; gap: 14px; font-family: var(--mono); font-size: 10px; color: var(--text-dim); }
|
||||
.glegend span { display:inline-flex; align-items:center; gap:6px; }
|
||||
|
||||
.timeline { display: grid; gap: 0; }
|
||||
.tl { display: grid; grid-template-columns: 64px 1fr; gap: 16px; padding: 14px 0; border-top: 1px solid var(--line-soft); }
|
||||
.tl:first-child { border-top: 0; }
|
||||
.tl .when { font-family: var(--mono); font-size: 11px; color: var(--text-dim); padding-top: 2px; }
|
||||
.tl .ev b { font-weight: 600; }
|
||||
.tl .ev { font-size: 14px; color: var(--text-2); line-height: 1.5; }
|
||||
.tl .ev .src { font-family: var(--mono); font-size: 10.5px; color: var(--intel); margin-left: 6px; }
|
||||
|
||||
.hidden { display: none !important; }
|
||||
.emptytab { padding: 60px 20px; text-align: center; color: var(--text-muted); }
|
||||
.emptytab .mono { font-family: var(--mono); font-size: 12px; color: var(--text-dim); letter-spacing: .08em; }
|
||||
@media (max-width: 820px){ .grid { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Workspace · variation</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="a">A · Overview + tabs</button>
|
||||
<button data-v="b">B · Memory-forward</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel"><b>Overview + tabs</b> — calm home for a project</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<div class="wshead">
|
||||
<div class="crumbs">Home <span>›</span> <b>Competitive Intelligence</b></div>
|
||||
<div class="wtitle-row">
|
||||
<div class="wmark hex">C</div>
|
||||
<div>
|
||||
<h1>Competitive Intelligence</h1>
|
||||
<div class="wmeta">
|
||||
<span><span class="dot dot-live" style="background:var(--healthy)"></span>1 agent live</span>
|
||||
<span>142 memories</span>
|
||||
<span>9 sources</span>
|
||||
<span>updated 2h ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-ghost">Memory</button>
|
||||
<button class="btn btn-primary">Continue →</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs" id="tabs">
|
||||
<div class="tab on" data-t="overview">Overview</div>
|
||||
<div class="tab" data-t="chat">Chat <span class="cnt">3</span></div>
|
||||
<div class="tab" data-t="memory">Memory <span class="cnt">142</span></div>
|
||||
<div class="tab" data-t="artifacts">Artifacts <span class="cnt">7</span></div>
|
||||
<div class="tab" data-t="files">Files <span class="cnt">12</span></div>
|
||||
<div class="tab" data-t="team">Team <span class="cnt">4</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════ VARIATION A — OVERVIEW + TABS ══════ -->
|
||||
<div class="content view" id="view-a">
|
||||
<div id="tab-overview">
|
||||
<div class="grid">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="summary">This workspace tracks the <b>persistent-memory competitor landscape</b> for the Q2 board cycle. Waggle has mapped <b>9 rivals</b>, pulled current pricing, and drafted a teardown — the live thread is mid-flight on turning the opening into a board brief.</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="sec-h"><span class="ic"><svg viewBox="0 0 24 24"><path d="M12 5a3 3 0 0 0-3 3 3 3 0 0 0-3 3 3 3 0 0 0 1 5 3 3 0 0 0 5 1 3 3 0 0 0 5-1 3 3 0 0 0 1-5 3 3 0 0 0-3-3 3 3 0 0 0-3-3Z"/></svg></span>What Waggle knows</div>
|
||||
<div class="knows" id="knowsA"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="sec-h"><span class="ic"><svg viewBox="0 0 24 24"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6"/></svg></span>Recent work</div>
|
||||
<div class="arts" id="artsA"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="sec-h" style="margin-bottom:6px">Status</div>
|
||||
<div class="stat-line"><span class="l">Agent</span><span class="v" style="color:var(--healthy)"><span class="dot dot-live" style="background:var(--healthy)"></span> Research-synth · live</span></div>
|
||||
<div class="stat-line"><span class="l">Model</span><span class="v">auto · Claude Sonnet</span></div>
|
||||
<div class="stat-line"><span class="l">Memories</span><span class="v">142 <span style="color:var(--healthy)">+6 today</span></span></div>
|
||||
<div class="stat-line"><span class="l">Needs review</span><span class="v" style="color:var(--attention)">3 memories</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="sec-h" style="margin-bottom:6px">Up next</div>
|
||||
<div class="agentrow"><span class="dot" style="background:var(--work)"></span><span class="an">Board brief from teardown</span><span class="am">draft</span></div>
|
||||
<div class="agentrow"><span class="dot" style="background:var(--attention)"></span><span class="an">Export table → Salesforce</span><span class="am">awaiting you</span></div>
|
||||
<div class="agentrow"><span class="dot" style="background:var(--healthy)"></span><span class="an">Weekly digest</span><span class="am">17:00</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="sec-h" style="margin-bottom:6px">Team</div>
|
||||
<div id="teamA"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tab-other" class="hidden">
|
||||
<div class="emptytab">
|
||||
<p style="font-size:16px;color:var(--text-2);margin-bottom:6px" id="otherTitle">Chat</p>
|
||||
<p class="mono">This tab is wired in the full prototype — Overview is the focus of this concept pass.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════ VARIATION B — MEMORY-FORWARD ══════ -->
|
||||
<div class="content view hidden" id="view-b">
|
||||
<div class="sec-h" style="margin-bottom:14px"><span class="ic"><svg viewBox="0 0 24 24"><circle cx="5" cy="6" r="2"/><circle cx="19" cy="6" r="2"/><circle cx="12" cy="18" r="2"/><path d="M7 6h10M6 8l5 8M18 8l-5 8"/></svg></span>What this workspace knows · knowledge graph</div>
|
||||
<div class="graph-wrap">
|
||||
<div class="comb"></div>
|
||||
<svg viewBox="0 0 900 360" preserveAspectRatio="none" aria-hidden="true">
|
||||
<line class="gline" x1="450" y1="180" x2="180" y2="80"></line>
|
||||
<line class="gline" x1="450" y1="180" x2="720" y2="80"></line>
|
||||
<line class="gline" x1="450" y1="180" x2="150" y2="265"></line>
|
||||
<line class="gline" x1="450" y1="180" x2="740" y2="270"></line>
|
||||
<line class="gline" x1="450" y1="180" x2="450" y2="305"></line>
|
||||
<line class="gline" x1="180" y1="80" x2="450" y2="305"></line>
|
||||
</svg>
|
||||
<div class="node center" style="left:50%;top:50%"><div class="nb"><b>Competitive Intelligence</b></div></div>
|
||||
<div class="node org" style="left:20%;top:22%"><div class="tag">Competitor</div><div class="nb">Mem0 · cloud-only</div></div>
|
||||
<div class="node org" style="left:80%;top:22%"><div class="tag">Competitor</div><div class="nb">Letta · agent-centric</div></div>
|
||||
<div class="node concept" style="left:17%;top:74%"><div class="tag">Concept</div><div class="nb">Local-first moat</div></div>
|
||||
<div class="node concept" style="left:82%;top:75%"><div class="tag">Decision</div><div class="nb">Target regulated</div></div>
|
||||
<div class="node person" style="left:50%;top:85%"><div class="tag">Person</div><div class="nb">Mara · owner</div></div>
|
||||
</div>
|
||||
<div class="glegend">
|
||||
<span><span class="dot" style="background:var(--work)"></span>People</span>
|
||||
<span><span class="dot" style="background:var(--intel)"></span>Concepts</span>
|
||||
<span><span class="dot" style="background:var(--risk)"></span>Orgs</span>
|
||||
<span><span class="dot" style="background:var(--healthy)"></span>Decisions</span>
|
||||
</div>
|
||||
|
||||
<div class="grid" style="margin-top:28px">
|
||||
<div class="col">
|
||||
<div class="sec-h">Memory timeline</div>
|
||||
<div class="timeline" id="timelineB"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="sec-h" style="margin-bottom:6px">Provenance</div>
|
||||
<div class="stat-line"><span class="l">From your chats</span><span class="v">88</span></div>
|
||||
<div class="stat-line"><span class="l">From web sources</span><span class="v">41</span></div>
|
||||
<div class="stat-line"><span class="l">Imported</span><span class="v">13</span></div>
|
||||
<div class="stat-line"><span class="l">Awaiting review</span><span class="v" style="color:var(--attention)">3</span></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="sec-h" style="margin-bottom:10px">Every memory carries a source</div>
|
||||
<p style="margin:0;font-size:13.5px;color:var(--text-muted);line-height:1.6">Click any node or fact and Waggle shows exactly where it came from, when, and which model wrote it — nothing in the hive is unattributed.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const HONEY='linear-gradient(150deg,var(--honey-bright),var(--honey-deep))';
|
||||
const CHECK='<svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>';
|
||||
|
||||
const facts = [
|
||||
{ t:'<b>Mem0</b> is cloud-only and raised prices ~15% since March.', src:'web · mem0.ai', when:'2h ago' },
|
||||
{ t:'Only <b>2 of 9</b> rivals ship local-first memory.', src:'teardown.md', when:'2h ago' },
|
||||
{ t:'Mara wants the board brief to <b>lead with the regulated-industries opening</b>.', src:'chat · Tue', when:'2d ago' },
|
||||
{ t:'Pricing model assumes a <b>new tier lifts ARPU ~12%</b>.', src:'Pricing Model', when:'3d ago' },
|
||||
];
|
||||
document.getElementById('knowsA').innerHTML = facts.map(f=>`
|
||||
<div class="fact"><div class="fi hex" style="background:${HONEY}">${CHECK}</div><div><div class="ft">${f.t}</div><div class="prov">⬡ <span class="src">${f.src}</span> · ${f.when}</div></div></div>`).join('');
|
||||
|
||||
const arts = [
|
||||
{ ext:'MD', c:'var(--work)', n:'teardown.md', s:'Q2 competitive teardown · 9 competitors', when:'2h ago' },
|
||||
{ ext:'XLS', c:'var(--healthy)', n:'pricing-landscape.xlsx', s:'Competitor pricing table', when:'2h ago' },
|
||||
{ ext:'PDF', c:'var(--risk)', n:'mem0-teardown.pdf', s:'Deep-dive on the category leader', when:'1d ago' },
|
||||
];
|
||||
document.getElementById('artsA').innerHTML = arts.map(a=>`
|
||||
<div class="art"><div class="ai" style="background:color-mix(in srgb, ${a.c} 18%, transparent);color:${a.c}">${a.ext}</div><div class="at"><b>${a.n}</b><span>${a.s}</span></div><span class="when">${a.when}</span></div>`).join('');
|
||||
|
||||
const team = [
|
||||
{ i:'M', c:'var(--honey)', n:'Mara K.', r:'Owner' },
|
||||
{ i:'R', c:'var(--work)', n:'Research-synth', r:'Agent · live' },
|
||||
{ i:'D', c:'var(--intel)', n:'Deck-builder', r:'Agent · idle' },
|
||||
{ i:'J', c:'var(--healthy)', n:'Jonas P.', r:'Editor' },
|
||||
];
|
||||
document.getElementById('teamA').innerHTML = team.map(p=>`
|
||||
<div class="person"><div class="pa" style="background:${p.c}">${p.i}</div><div class="pn">${p.n}</div><div class="pr">${p.r}</div></div>`).join('');
|
||||
|
||||
const timeline = [
|
||||
{ when:'2h ago', ev:'Folded <b>6 memories</b> from the teardown run', src:'mem://hive' },
|
||||
{ when:'2h ago', ev:'Learned <b>Mem0 pricing changed</b> — flagged vs your model', src:'web' },
|
||||
{ when:'2d ago', ev:'Captured Mara\u2019s steer: <b>lead with regulated</b>', src:'chat' },
|
||||
{ when:'1w ago', ev:'Imported <b>March landscape</b> notes', src:'import' },
|
||||
];
|
||||
document.getElementById('timelineB').innerHTML = timeline.map(t=>`
|
||||
<div class="tl"><div class="when">${t.when}</div><div class="ev">${t.ev}<span class="src">⬡ ${t.src}</span></div></div>`).join('');
|
||||
|
||||
/* tabs (variation A) */
|
||||
const tabs = document.getElementById('tabs');
|
||||
tabs.addEventListener('click', e => {
|
||||
const t = e.target.closest('.tab'); if(!t) return;
|
||||
[...tabs.children].forEach(x=>x.classList.toggle('on', x===t));
|
||||
const isOverview = t.dataset.t==='overview';
|
||||
document.getElementById('tab-overview').classList.toggle('hidden', !isOverview);
|
||||
document.getElementById('tab-other').classList.toggle('hidden', isOverview);
|
||||
if(!isOverview) document.getElementById('otherTitle').textContent = t.textContent.trim().replace(/\s*\d+$/,'');
|
||||
});
|
||||
|
||||
/* variation switch */
|
||||
const labels = { a:'<b>Overview + tabs</b> — calm home for a project', b:'<b>Memory-forward</b> — the graph leads' };
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e => {
|
||||
const b = e.target.closest('button'); if(!b) return;
|
||||
[...seg.children].forEach(x=>x.classList.toggle('on', x===b));
|
||||
document.getElementById('view-a').classList.toggle('hidden', b.dataset.v!=='a');
|
||||
document.getElementById('view-b').classList.toggle('hidden', b.dataset.v!=='b');
|
||||
document.getElementById('vlabel').innerHTML = labels[b.dataset.v];
|
||||
document.querySelector('.stage').scrollTop = 0;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,172 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Waggle · Workspaces</title>
|
||||
<script>document.documentElement.classList.add('js');</script>
|
||||
<link rel="stylesheet" href="../styles/waggle.css">
|
||||
<style>
|
||||
body { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.controls { flex: none; display: flex; align-items: center; gap: 14px; padding: 11px 20px; border-bottom: 1px solid var(--line-soft); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); z-index: 30; }
|
||||
.controls .lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
|
||||
.seg { display: inline-flex; padding: 3px; gap: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line-soft); }
|
||||
.seg button { font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; border: 0; background: transparent; color: var(--text-muted); padding: 6px 13px; border-radius: 7px; transition: .14s; white-space: nowrap; }
|
||||
.seg button.on { background: var(--honey); color: #1a1407; }
|
||||
.controls .vlabel { font-size: 12.5px; color: var(--text-muted); } .controls .vlabel b { color: var(--text); font-weight: 600; }
|
||||
.controls .right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
|
||||
.tbtn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
|
||||
.tbtn:hover { border-color: var(--honey-line); color: var(--honey); }
|
||||
.stage { flex: 1; min-height: 0; overflow: auto; }
|
||||
.wrap { max-width: 1000px; margin: 0 auto; padding: 28px 32px 60px; }
|
||||
|
||||
.head { display:flex; align-items:flex-end; gap: 16px; margin-bottom: 6px; }
|
||||
.head h1 { font-size: 28px; font-weight: 650; letter-spacing:-0.02em; margin: 0; }
|
||||
.head .new { margin-left:auto; font-size: 13px; font-weight: 650; padding: 10px 16px; border-radius: 10px; background: var(--honey); color:#1a1407; cursor:pointer; border:0; display:inline-flex; align-items:center; gap:8px; }
|
||||
.head .new:hover { background: var(--honey-bright); }
|
||||
.sub { font-size: 14px; color: var(--text-muted); margin: 0 0 22px; }
|
||||
|
||||
.toolbar { display:flex; align-items:center; gap: 12px; margin-bottom: 20px; flex-wrap: wrap; }
|
||||
.search { flex:1; min-width: 220px; display:flex; align-items:center; gap:10px; padding: 9px 14px; border-radius: 11px; border:1px solid var(--line); background: var(--surface); }
|
||||
.search:focus-within { border-color: var(--honey-line); }
|
||||
.search svg { width:16px; height:16px; stroke: var(--text-dim); fill:none; stroke-width:1.9; }
|
||||
.search input { flex:1; border:0; background:transparent; color:var(--text); font-family:var(--sans); font-size:14px; outline:none; }
|
||||
.filters { display:flex; gap:6px; }
|
||||
.filt { font-size: 12.5px; font-weight:600; color: var(--text-muted); padding: 8px 13px; border-radius: 9px; border:1px solid var(--line-soft); background: var(--surface); cursor:pointer; transition:.14s; display:inline-flex; align-items:center; gap:7px; }
|
||||
.filt:hover { color: var(--text); } .filt.on { color: var(--text); border-color: var(--honey-line); background: var(--honey-wash); }
|
||||
.filt .dot { width:7px; height:7px; }
|
||||
|
||||
/* grid */
|
||||
.grid { display:grid; grid-template-columns: repeat(2,1fr); gap: 14px; }
|
||||
.ws { padding: 18px; border-radius: var(--r-lg); border:1px solid var(--line-soft); background: var(--surface); cursor:pointer; transition:.16s; position:relative; }
|
||||
.ws:hover { border-color: var(--honey-line); transform: translateY(-2px); box-shadow: var(--shadow); }
|
||||
.ws .top { display:flex; align-items:center; gap:12px; margin-bottom: 12px; }
|
||||
.ws .wm { width:36px; height:40px; flex:none; display:grid; place-items:center; font-weight:800; font-size:14px; color:#1a1407; background: linear-gradient(150deg,var(--honey-bright),var(--honey-deep)); }
|
||||
.ws h3 { flex:1; font-size: 16px; font-weight: 650; letter-spacing:-0.01em; margin:0; }
|
||||
.ws .stbadge { font-size: 10.5px; font-weight:600; padding: 3px 9px; border-radius:999px; display:inline-flex; align-items:center; gap:5px; }
|
||||
.ws .stbadge.local { color: var(--honey); background: var(--honey-wash); }
|
||||
.ws .stbadge.virtual { color: var(--intel); background: var(--intel-wash); }
|
||||
.ws .stbadge.team { color: var(--healthy); background: var(--healthy-wash); }
|
||||
.ws p { margin: 0 0 14px; font-size: 13px; color: var(--text-muted); line-height: 1.5; min-height: 38px; }
|
||||
.ws .meta { display:flex; align-items:center; gap: 14px; font-size: 12px; color: var(--text-dim); }
|
||||
.ws .meta span { display:inline-flex; align-items:center; gap:5px; }
|
||||
.ws .meta .live { color: var(--healthy); }
|
||||
.ws .meta .pend { color: var(--attention); }
|
||||
.ws .meta .open { margin-left:auto; color: var(--honey); font-weight:650; }
|
||||
|
||||
/* table */
|
||||
.tbl { width:100%; border-collapse: collapse; }
|
||||
.tbl th { text-align:left; font-family: var(--mono); font-size: 10px; letter-spacing:.06em; text-transform:uppercase; color: var(--text-dim); font-weight:600; padding: 10px 12px; border-bottom: 1px solid var(--line-soft); }
|
||||
.tbl td { padding: 13px 12px; border-bottom: 1px solid var(--line-soft); font-size: 13.5px; color: var(--text-2); }
|
||||
.tbl tr { cursor:pointer; } .tbl tbody tr:hover td { background: var(--surface-2); }
|
||||
.tbl .nmc { display:flex; align-items:center; gap:11px; } .tbl .nmc .wm { width:26px; height:30px; font-size:11px; } .tbl .nmc b { color: var(--text); font-weight:600; }
|
||||
.tbl .r { text-align:right; color: var(--text-dim); font-family: var(--mono); font-size: 12px; }
|
||||
|
||||
.note { margin-top: 22px; display:flex; gap:12px; align-items:flex-start; padding: 14px 16px; border-radius: var(--r-lg); background: var(--bg-2); border:1px solid var(--line-soft); }
|
||||
.note svg { width:18px; height:18px; stroke: var(--honey); fill:none; stroke-width:1.8; flex:none; margin-top:1px; }
|
||||
.note p { margin:0; font-size: 13px; color: var(--text-muted); line-height:1.6; } .note b { color: var(--text); }
|
||||
|
||||
.hidden { display:none !important; }
|
||||
@media (max-width: 760px){ .grid { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="controls">
|
||||
<span class="lab">Workspaces · view</span>
|
||||
<div class="seg" id="seg">
|
||||
<button class="on" data-v="grid">Grid</button>
|
||||
<button data-v="table">Table</button>
|
||||
</div>
|
||||
<span class="vlabel" id="vlabel">Every workspace you have, in one place</span>
|
||||
<div class="right"><button class="tbtn" id="tbtn" title="Toggle theme">☾</button></div>
|
||||
</div>
|
||||
|
||||
<div class="stage">
|
||||
<div class="wrap">
|
||||
<div class="head">
|
||||
<h1>Workspaces</h1>
|
||||
<button class="new"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>New workspace</button>
|
||||
</div>
|
||||
<p class="sub">Home greets you with the day. <b style="color:var(--text-2)">This is the full shelf</b> — every workspace, where it lives, and what's happening in it.</p>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="search"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg><input id="q" placeholder="Search workspaces… or ask the agent to find one"></div>
|
||||
<div class="filters" id="filters">
|
||||
<span class="filt on" data-f="all">All <span style="color:var(--text-dim)">8</span></span>
|
||||
<span class="filt" data-f="local"><span class="dot" style="background:var(--honey)"></span>Local</span>
|
||||
<span class="filt" data-f="virtual"><span class="dot" style="background:var(--intel)"></span>Virtual</span>
|
||||
<span class="filt" data-f="team"><span class="dot" style="background:var(--healthy)"></span>Team</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="gridView" class="grid"></div>
|
||||
<div id="tableView" class="hidden"><table class="tbl"><thead><tr><th>Workspace</th><th>Storage</th><th>Memories</th><th>Agent</th><th class="r">Last active</th></tr></thead><tbody id="tbody"></tbody></table></div>
|
||||
|
||||
<div class="note">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 8h.01M11 12h1v4h1"/></svg>
|
||||
<p><b>Where this lives in the app:</b> Workspaces is one of the five places in the left rail, and it's the first thing ⌘K offers. The switcher in any workspace header opens it too — so you're never more than a click from the full shelf.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const root = document.documentElement, tbtn = document.getElementById('tbtn');
|
||||
function setTheme(t){ root.setAttribute('data-theme', t); tbtn.textContent = t==='dark'?'☾':'☀'; try{localStorage.setItem('waggle-concept-theme',t);}catch(e){} }
|
||||
setTheme((()=>{ try{ return localStorage.getItem('waggle-concept-theme')||'dark'; }catch(e){ return 'dark'; } })());
|
||||
tbtn.onclick = () => setTheme(root.getAttribute('data-theme')==='dark'?'light':'dark');
|
||||
window.addEventListener('message', e => { if(e.data && e.data.theme) setTheme(e.data.theme); });
|
||||
window.addEventListener('storage', e => { if(e.key==='waggle-concept-theme' && e.newValue) setTheme(e.newValue); });
|
||||
|
||||
const ws = [
|
||||
{ g:'C', n:'Competitive Intelligence', grp:'Research', st:'local', sum:'Q2 teardown — 9 rivals mapped, draft ready for your read.', mem:142, agent:'live', pend:3, when:'2h ago' },
|
||||
{ g:'B', n:'Q2 Board Deck', grp:'Exec', st:'local', sum:'18 slides drafted; narrative needs your voice on slide 6.', mem:88, agent:'live', pend:0, when:'yesterday' },
|
||||
{ g:'R', n:'Customer Research', grp:'Research', st:'local', sum:'31 interviews themed. Top friction: onboarding drop-off.', mem:210, agent:'idle', pend:0, when:'2d ago' },
|
||||
{ g:'P', n:'Pricing Model', grp:'Finance', st:'local', sum:'Scenario model rebuilt; new tier lifts ARPU ~12%.', mem:64, agent:'idle', pend:0, when:'3d ago' },
|
||||
{ g:'M', n:'Marketing Site Rewrite', grp:'Marketing', st:'team', sum:'Shared with 3 teammates. Hero + proof section in review.', mem:51, agent:'idle', pend:2, when:'5h ago' },
|
||||
{ g:'L', n:'Legal — Vendor Review', grp:'Legal', st:'team', sum:'Contract redlines synced across the team.', mem:38, agent:'idle', pend:0, when:'1w ago' },
|
||||
{ g:'S', n:'Scratch', grp:'Personal', st:'virtual', sum:'Quick throwaway space — notes and one-off questions.', mem:9, agent:'idle', pend:0, when:'30m ago' },
|
||||
{ g:'O', n:'Onboarding Redesign', grp:'Product', st:'local', sum:'Flow draft + 4 template options for first-run.', mem:27, agent:'idle', pend:0, when:'1h ago' },
|
||||
];
|
||||
const STL = { local:'Local · private', virtual:'Virtual · session', team:'Team · shared' };
|
||||
|
||||
let filter = 'all', query = '';
|
||||
function visible(){ return ws.filter(w => (filter==='all'||w.st===filter) && (!query || (w.n+' '+w.grp+' '+w.sum).toLowerCase().includes(query))); }
|
||||
|
||||
function renderGrid(){
|
||||
document.getElementById('gridView').innerHTML = visible().map(w => `
|
||||
<div class="ws">
|
||||
<div class="top">
|
||||
<div class="wm hex">${w.g}</div>
|
||||
<h3>${w.n}</h3>
|
||||
<span class="stbadge ${w.st}">⬡ ${STL[w.st].split(' · ')[0]}</span>
|
||||
</div>
|
||||
<p>${w.sum}</p>
|
||||
<div class="meta">
|
||||
<span>⬡ ${w.mem} memories</span>
|
||||
${w.agent==='live'?'<span class="live"><span class="dot dot-live" style="background:var(--healthy)"></span>agent live</span>':''}
|
||||
${w.pend>0?`<span class="pend">▲ ${w.pend} to review</span>`:''}
|
||||
<span class="open">Open →</span>
|
||||
</div>
|
||||
</div>`).join('') || '<p style="color:var(--text-dim);grid-column:1/-1;padding:30px;text-align:center">No workspaces match.</p>';
|
||||
}
|
||||
function renderTable(){
|
||||
document.getElementById('tbody').innerHTML = visible().map(w => `
|
||||
<tr>
|
||||
<td><div class="nmc"><div class="wm hex">${w.g}</div><div><b>${w.n}</b><div style="font-size:11.5px;color:var(--text-dim)">${w.grp}</div></div></div></td>
|
||||
<td><span class="ws"><span class="stbadge ${w.st}" style="font-size:10.5px;padding:3px 9px;border-radius:999px">⬡ ${STL[w.st]}</span></span></td>
|
||||
<td>${w.mem}</td>
|
||||
<td>${w.agent==='live'?'<span style="color:var(--healthy)">● live</span>':'<span style="color:var(--text-dim)">idle</span>'}${w.pend>0?` · <span style="color:var(--attention)">${w.pend}▲</span>`:''}</td>
|
||||
<td class="r">${w.when}</td>
|
||||
</tr>`).join('');
|
||||
}
|
||||
function render(){ renderGrid(); renderTable(); }
|
||||
render();
|
||||
|
||||
document.getElementById('q').addEventListener('input', e => { query = e.target.value.toLowerCase().trim(); render(); });
|
||||
document.getElementById('filters').addEventListener('click', e => { const f=e.target.closest('.filt'); if(!f)return; filter=f.dataset.f; document.querySelectorAll('.filt').forEach(x=>x.classList.toggle('on',x===f)); render(); });
|
||||
const seg = document.getElementById('seg');
|
||||
seg.addEventListener('click', e => { const b=e.target.closest('button'); if(!b)return; [...seg.children].forEach(x=>x.classList.toggle('on',x===b)); const grid=b.dataset.v==='grid'; document.getElementById('gridView').classList.toggle('hidden',!grid); document.getElementById('tableView').classList.toggle('hidden',grid); });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user