Files
waggle-os/docs/design_handoff_waggle_app/design-files/screens/workspaces.html
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

173 lines
13 KiB
HTML

<!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>