- normalize Kimi thinking parts and omit empty placeholders - add standalone session-sharing and UI demo artifacts - add regression coverage and refresh the Kimi index marker - bump the Electron app and CLI packages to v0.2.1 - ignore generated app build assets
259 lines
140 KiB
HTML
259 lines
140 KiB
HTML
<!doctype html>
|
||
<!-- PROTOTYPE: faithful, dependency-free desktop UI model of the current Obelisk app. Mock data only. -->
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Obelisk UI Mini</title>
|
||
<style>
|
||
:root{--bg:#0a0b14;--bg2:#11131f;--surface:rgba(255,255,255,.03);--surface2:rgba(255,255,255,.06);--surface3:rgba(255,255,255,.09);--fg:rgba(255,255,255,.92);--fg2:rgba(255,255,255,.72);--muted:rgba(255,255,255,.48);--muted2:rgba(255,255,255,.28);--line:rgba(255,255,255,.05);--line2:rgba(255,255,255,.08);--accent:#a78bfa;--accent2:#c4b5fd;--accentSoft:rgba(167,139,250,.12);--danger:#f87171;--warn:#fbbf24;--green:#34d399;--sans:-apple-system,BlinkMacSystemFont,"Inter","Segoe UI",system-ui,sans-serif;--mono:ui-monospace,"JetBrains Mono","IBM Plex Mono","SF Mono",Menlo,monospace;--sidebar:220px}
|
||
*{box-sizing:border-box;margin:0;padding:0}html,body,#app{height:100%;overflow:hidden}body{color:var(--fg);font:13px/1.4 var(--sans);-webkit-font-smoothing:antialiased;background-color:var(--bg);background-image:radial-gradient(80% 60% at 100% 0%,rgba(236,72,153,.14),transparent 55%),radial-gradient(60% 50% at 50% 40%,rgba(167,139,250,.12),transparent 60%),radial-gradient(70% 60% at 0% 100%,rgba(99,102,241,.16),transparent 60%),linear-gradient(var(--bg),var(--bg2))}
|
||
body:before{content:"";position:fixed;inset:0;pointer-events:none;opacity:.22;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .04 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");mix-blend-mode:overlay}
|
||
button,input{font:inherit;color:inherit}button{border:0;background:none;cursor:pointer}button:disabled{cursor:not-allowed;opacity:.35}button:focus-visible,input:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}::-webkit-scrollbar{width:8px}::-webkit-scrollbar-thumb{background:rgba(255,255,255,.1);border:2px solid transparent;background-clip:padding-box;border-radius:9px}
|
||
.app{position:relative;z-index:1;height:100%;display:flex;flex-direction:column}.titlebar{height:32px;flex:none;display:flex;align-items:center;justify-content:center;padding-left:72px;background:rgba(0,0,0,.15);border-bottom:1px solid var(--line);backdrop-filter:blur(20px);font-size:12px;font-weight:500;letter-spacing:-.005em;color:var(--muted);user-select:none}.titlebar b{color:var(--fg2);font-weight:600}.titlebar i{font-style:normal;color:var(--muted2);margin:0 6px}.columns{flex:1;min-height:0;display:grid;grid-template-columns:var(--sidebar) 1fr}.main{min-width:0;min-height:0;display:flex;flex-direction:column}
|
||
.sidebar{min-height:0;display:flex;flex-direction:column;overflow:hidden;background:rgba(0,0,0,.2);border-right:1px solid var(--line2)}.brand{height:36px;flex:none;padding:0 14px;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--line);position:relative}.brand-logo{width:18px;height:18px;filter:drop-shadow(0 0 8px rgba(167,139,250,.4))}.brand-name{font-weight:600;color:var(--fg2)}.health{margin-left:auto;padding:5px 6px;display:flex;gap:3px;border-radius:4px}.health:hover{background:var(--surface2)}.dot{width:5px;height:5px;border-radius:50%;display:inline-block}.dot.claude{background:#d97757;box-shadow:0 0 4px rgba(217,119,87,.7)}.dot.codex{background:#10a37f;box-shadow:0 0 4px rgba(16,163,127,.7)}
|
||
.popover{position:absolute;top:41px;left:7px;width:260px;padding:6px 0;border:1px solid var(--line2);border-radius:8px;background:rgba(20,22,38,.98);box-shadow:0 12px 40px rgba(0,0,0,.6);z-index:20}.pop-head,.pop-foot{padding:8px 14px;font-size:11.5px;color:var(--muted)}.pop-head{border-bottom:1px solid var(--line)}.pop-foot{border-top:1px solid var(--line)}.pop-foot button{color:var(--accent2)}.source-row{width:100%;padding:8px 14px;display:flex;gap:10px;text-align:left}.source-row:hover{background:var(--surface)}.source-row .dot{width:7px;height:7px;margin-top:5px}.source-name{color:var(--fg);font-weight:500}.source-meta{font:10.5px var(--mono);color:var(--muted);margin-top:2px}
|
||
.side-section{padding:8px 6px;flex:none}.side-section+.side-section{border-top:1px solid var(--line)}.side-section.projects{flex:1;min-height:0;display:flex;flex-direction:column}.side-section.bottom{margin-top:auto}.section-title{padding:4px 10px 6px;font-size:10.5px;color:var(--muted);letter-spacing:.04em;display:flex;justify-content:space-between}.side-item{width:100%;height:28px;padding:0 10px;border-radius:5px;display:flex;align-items:center;gap:8px;color:var(--fg2);text-align:left;position:relative}.side-item:hover{background:var(--surface2);color:var(--fg)}.side-item.active{background:var(--accentSoft);color:var(--fg)}.side-item.active:before{content:"";position:absolute;left:-6px;top:4px;bottom:4px;width:2px;background:var(--accent);box-shadow:0 0 8px rgba(167,139,250,.35)}.side-item svg{width:14px;height:14px;color:var(--muted);flex:none}.side-item.active svg{color:var(--accent2);filter:drop-shadow(0 0 4px rgba(167,139,250,.35))}.side-item .label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.badge{font:10.5px var(--mono);color:var(--muted);min-width:22px;text-align:right}.side-item.sub{height:26px;padding-left:30px;font-size:12px}.side-item.sub svg{width:12px}.project-search{padding:0 6px 6px}.project-search input{width:100%;height:24px;padding:0 8px;border:1px solid var(--line);border-radius:4px;background:var(--surface);font-size:12px}.project-list{overflow:auto;min-height:0;flex:1}.project-list .side-item{font-size:12px}
|
||
.toolbar{height:44px;flex:none;display:flex;align-items:center;gap:10px;padding:0 14px;border-bottom:1px solid var(--line2);background:rgba(0,0,0,.15);backdrop-filter:blur(20px);position:relative;z-index:10}.breadcrumb{display:flex;align-items:center;gap:6px;min-width:0}.crumb{font-size:14px;color:var(--muted);padding:4px 6px;border-radius:4px;white-space:nowrap}.crumb:not(.current):hover{background:var(--surface2);color:var(--fg2)}.crumb.current{font-weight:600;color:var(--fg);overflow:hidden;text-overflow:ellipsis}.crumb-sep{color:var(--muted2)}.spacer{flex:1}.search{width:220px;position:relative}.search input{width:100%;height:26px;padding:0 30px 0 27px;border:1px solid var(--line);border-radius:5px;background:var(--surface)}.search input:focus{border-color:var(--accent);background:var(--surface2);box-shadow:0 0 0 2px var(--accentSoft);outline:0}.search svg{position:absolute;left:8px;top:7px;width:12px;color:var(--muted)}.kbd{font:9.5px var(--mono);color:var(--muted2);padding:1px 4px;border:1px solid var(--line2);border-radius:3px}.search .kbd{position:absolute;right:6px;top:5px}.sort{height:26px;padding:0 7px;border-radius:5px;font:12px var(--mono);color:var(--muted)}.sort:hover{background:var(--surface2);color:var(--fg2)}.filter,.btn{height:28px;padding:0 11px;border:1px solid var(--line2);border-radius:5px;background:var(--surface);color:var(--fg2);font-size:12px}.filter:hover,.btn:hover{background:var(--surface2);color:var(--fg)}.filter.active,.btn.primary{border-color:rgba(167,139,250,.35);background:var(--accentSoft);color:var(--accent2)}.filter-menu{position:absolute;right:68px;top:36px;width:190px;padding:6px;border:1px solid var(--line2);border-radius:8px;background:rgba(20,22,38,.98);box-shadow:0 12px 40px #0009}.filter-menu button{width:100%;padding:7px 8px;text-align:left;border-radius:5px;color:var(--fg2)}.filter-menu button:hover,.filter-menu button.on{background:var(--accentSoft);color:var(--accent2)}.tabs{display:flex;height:26px;border:1px solid var(--line2);border-radius:5px;overflow:hidden}.tabs button{padding:0 12px;color:var(--muted);border-right:1px solid var(--line2)}.tabs button:last-child{border:0}.tabs button.active{background:var(--accentSoft);color:var(--accent2)}
|
||
#content{flex:1;min-height:0;overflow:auto}.list{min-height:100%}.srow,.mrow{position:relative;border-bottom:1px solid var(--line);cursor:pointer;transition:background .08s}.srow:hover,.mrow:hover{background:rgba(255,255,255,.025)}.srow{min-height:64px;padding:12px 16px;display:grid;grid-template-columns:1fr auto;gap:12px}.srow-title{font-size:14px;font-weight:500;color:var(--fg);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.srow-meta{margin-top:4px;display:flex;gap:8px;align-items:center;flex-wrap:wrap;font:11px var(--mono);color:var(--muted)}.meta-dot{width:2px;height:2px;background:var(--muted2);border-radius:50%}.source-pill{font:9.5px var(--mono);padding:2px 5px;border-radius:7px;background:var(--surface2)}.source-pill.claude{color:#e69a7d}.source-pill.codex{color:#57c7a9}.srow-right{font:11px var(--mono);color:var(--fg2);text-align:right}.srow-right small{display:block;color:var(--muted);margin-top:2px}.snippet{margin-top:7px;padding-left:14px;position:relative;color:var(--fg2);font-size:12px}.snippet:before{content:"";position:absolute;left:0;top:1px;bottom:1px;width:2px;background:var(--accentSoft)}mark{padding:0 2px;border-radius:2px;background:var(--accentSoft);color:var(--accent2)}
|
||
.mrow{min-height:88px;padding:14px 16px 14px 14px;display:grid;grid-template-columns:22px 1fr auto;gap:12px}.mrow.cursor{background:var(--surface)}.mrow.cursor:before,.mrow.selected:before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--muted2)}.mrow.selected:before{background:var(--accent);box-shadow:0 0 12px rgba(167,139,250,.35)}.mrow.cursor.selected{background:rgba(167,139,250,.16)}.check{width:18px;height:18px;margin-top:1px;border:1.5px solid var(--muted2);border-radius:4px;display:grid;place-items:center;opacity:0}.mrow:hover .check,.mrow.selected .check,.mrow.cursor .check{opacity:1}.check.on{opacity:1;background:var(--accent);border-color:var(--accent);color:var(--bg);box-shadow:0 0 8px rgba(167,139,250,.35)}.check svg{width:10px;height:10px}.mrow.selected{background:var(--accentSoft)}.mrow-body{min-width:0}.mrow-path{font:500 14px/1.4 var(--mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mrow-project{color:var(--muted);font-weight:400}.row-status{display:inline-grid;place-items:center;width:14px;height:14px;margin-right:6px;color:var(--muted2);vertical-align:-2px}.row-status svg{width:14px;height:14px}.mrow-summary{margin-top:6px;color:var(--fg2);line-height:1.5;max-width:75ch}.mrow-right{display:flex;flex-direction:column;align-items:flex-end;gap:12px;font:10.5px var(--mono);color:var(--muted)}.row-action{font:12px var(--sans);color:var(--muted);opacity:0}.mrow:hover .row-action{opacity:1}.row-action:hover{color:var(--danger)}.empty{min-height:100%;display:grid;place-content:center;text-align:center;color:var(--muted);gap:8px}.empty b{color:var(--fg2);font-size:14px}.empty small{font-size:11px}
|
||
.detail{max-width:920px;margin:auto;padding:34px 44px 90px}.eyebrow{font:10.5px var(--mono);color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.detail h1{font-size:24px;line-height:1.25;margin:10px 0;color:var(--fg);letter-spacing:-.02em}.detail-sub{color:var(--fg2);font-size:14px;max-width:70ch}.detail-meta{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:12px;font:11px var(--mono);color:var(--muted)}.detail-rule{height:1px;background:var(--line);margin:26px 0}.timeline{display:flex;flex-direction:column;gap:18px}.message{display:grid;grid-template-columns:58px 1fr;gap:14px}.role{padding-top:10px;font:10px var(--mono);text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}.bubble{padding:13px 15px;border:1px solid var(--line);border-radius:7px;background:rgba(255,255,255,.022);color:var(--fg2);line-height:1.6}.message.user .bubble{background:rgba(167,139,250,.08);border-color:rgba(167,139,250,.18);color:var(--fg)}.thinking,.tool{margin-top:8px;border:1px solid var(--line);border-radius:5px;background:rgba(0,0,0,.18)}.disclosure{width:100%;padding:8px 10px;text-align:left;font:11px var(--mono);color:var(--muted)}.disclosure:hover{color:var(--fg2);background:var(--surface)}.disclosure-content{display:none;padding:0 11px 10px;color:var(--muted);font:11px/1.55 var(--mono);white-space:pre-wrap}.open .disclosure-content{display:block}.tool-result{padding:10px;border-top:1px solid var(--line);font:11px/1.5 var(--mono);color:var(--fg2)}.msg-nav{position:sticky;bottom:14px;margin:24px auto 0;width:max-content;padding:5px 7px;display:flex;align-items:center;gap:6px;border:1px solid var(--line2);border-radius:7px;background:rgba(10,11,20,.88);backdrop-filter:blur(12px);box-shadow:0 8px 24px #0007}.msg-nav button{width:26px;height:24px;border-radius:4px;color:var(--muted)}.msg-nav button:hover{background:var(--surface2);color:var(--fg)}.msg-pos{font:11px var(--mono);color:var(--muted);min-width:45px;text-align:center}
|
||
.settings{max-width:720px;margin:auto;padding:36px 32px 80px}.settings section{margin-bottom:44px}.section-head{padding-bottom:10px;margin-bottom:16px;border-bottom:1px solid var(--line)}.section-head h2{font-size:18px;margin-bottom:2px}.section-head p{color:var(--muted)}.source-card{padding:18px;border:1px solid var(--line);border-radius:8px;background:rgba(0,0,0,.18);margin-bottom:12px}.source-card-head{display:flex;align-items:center;gap:10px;margin-bottom:14px}.source-mark{width:28px;height:28px;border:1px solid var(--line2);border-radius:6px;display:grid;place-items:center;background:#0005}.source-info{flex:1}.source-info b{font-size:14px}.vendor{font-size:11px;color:var(--muted);font-weight:400;margin-left:6px}.status{font:10.5px var(--mono);color:var(--muted);margin-top:3px}.status .ok{color:var(--green)}.path{display:flex;gap:6px}.path input{flex:1;min-width:0;height:28px;padding:0 10px;border:1px solid var(--line2);border-radius:5px;background:#0005;font:12px var(--mono)}.setting-row{display:grid;grid-template-columns:180px 1fr;gap:24px;padding:14px 0}.setting-row+.setting-row{border-top:1px solid var(--line)}.setting-label{color:var(--fg2);font-weight:500;padding-top:6px}.toggle{display:inline-flex;align-items:center;gap:8px;color:var(--fg2)}.track{width:30px;height:16px;border:1px solid var(--line2);border-radius:9px;position:relative;background:var(--surface2)}.track:after{content:"";position:absolute;width:10px;height:10px;top:2px;left:2px;border-radius:50%;background:var(--muted);transition:transform .15s}.track.on{border-color:rgba(167,139,250,.5);background:var(--accentSoft)}.track.on:after{transform:translateX(14px);background:var(--accent2);box-shadow:0 0 6px rgba(167,139,250,.5)}
|
||
.toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:100;padding:10px 16px;border:1px solid var(--line2);border-radius:8px;background:var(--surface2);box-shadow:0 4px 20px #0006;color:var(--fg2);font-size:12px}.toast button{margin-left:12px;padding:3px 10px;border:1px solid rgba(167,139,250,.3);border-radius:4px;background:var(--accentSoft);color:var(--accent2);font:12px var(--mono)}
|
||
/* Fidelity layer: mirrors the installed Electron app, while remaining standalone. */
|
||
:root{--sidebar:220px;--serif:"Iowan Old Style",Charter,"Source Serif Pro",Georgia,serif}
|
||
.titlebar{position:relative;padding:0 16px 0 78px}.traffic{position:absolute;left:13px;top:10px;display:flex;gap:8px}.traffic span{width:12px;height:12px;border-radius:50%;box-shadow:inset 0 0 0 .5px #0005}.traffic .red{background:#ff5f57}.traffic .yellow{background:#febc2e}.traffic .green{background:#28c840}
|
||
.popover{top:36px;left:0;width:260px;border-radius:0 0 7px 0;padding:0}.pop-head{padding:10px 14px}.source-row{padding:9px 14px}.pop-foot{padding:8px 14px;background:#0a0b14cc}.source-name{display:block}.source-meta{display:block}.section-title .show-all{font:10px var(--mono);color:var(--muted);padding:2px 5px;border-radius:3px}.section-title .show-all:hover{background:var(--surface2);color:var(--fg2)}.noise-fold{width:100%;height:28px;padding:0 10px;display:flex;align-items:center;gap:8px;color:var(--muted);font-size:12px}.noise-fold:hover{background:var(--surface2);color:var(--fg2)}
|
||
.source-filter-wrap{position:relative}.filter.source-filter{display:flex;align-items:center;gap:6px;height:26px;font:10.5px var(--mono)}.filter.source-filter svg{width:11px}.filter-menu{top:32px;right:0;width:220px;padding:6px}.filter-menu button{display:flex;align-items:center;gap:10px;padding:8px 10px}.filter-menu button.on{background:transparent;color:var(--fg2)}.filter-menu button:hover{background:var(--surface)}.filter-check{width:15px;height:15px;border:1.5px solid var(--muted2);border-radius:3px;display:grid;place-items:center;flex:none}.on .filter-check{background:var(--accent);border-color:var(--accent);color:#090a12}.filter-check svg{width:10px;height:10px}.toolbar .tabs{height:26px}.toolbar .btn.primary{height:26px}
|
||
.session-row{position:relative;min-height:61px;padding:12px 16px;display:grid;grid-template-columns:1fr auto;gap:12px;border-bottom:1px solid var(--line);cursor:pointer}.session-row:hover{background:rgba(255,255,255,.025)}.session-row-title{font-size:14px;font-weight:500;color:var(--fg);line-height:1.35;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.session-row-meta{margin-top:4px;font:11px var(--mono);color:var(--muted);display:flex;gap:7px}.session-row-meta .project{color:var(--fg2)}.session-row-time{padding-top:1px;font:11px var(--mono);color:var(--fg2);white-space:nowrap}.session-obelisk{position:absolute;left:0;bottom:0;width:3px;border-radius:2px 2px 0 0;background:linear-gradient(to top,#6d28d9,#a78bfa);box-shadow:0 0 7px rgba(167,139,250,.42)}.quiet-fold{height:39px;padding:0 16px;display:flex;align-items:center;gap:9px;border-bottom:1px solid var(--line);color:var(--muted);font-size:12px}.quiet-fold:hover{background:var(--surface)}.quiet-fold .chev{font-size:15px;color:var(--muted2)}.quiet-fold .quiet-count{font:11px var(--mono);color:var(--fg2)}.quiet-fold .quiet-text{flex:1}.quiet-fold .quiet-action{color:var(--accent2);font-size:11.5px}
|
||
.session-reader{width:min(660px,calc(100% - 48px));margin:0 auto;padding:28px 0 90px}.session-head-mark{height:18px;display:flex;align-items:center;gap:10px;margin-bottom:2px}.session-head-mark:before{content:"";width:36px;height:2px;background:linear-gradient(90deg,var(--accent),transparent);box-shadow:0 0 8px rgba(167,139,250,.45)}.tiny-obelisk{width:12px;height:16px;position:relative}.tiny-obelisk:before{content:"";position:absolute;left:4px;top:1px;border-left:3px solid transparent;border-right:3px solid transparent;border-bottom:5px solid #c4b5fd}.tiny-obelisk:after{content:"";position:absolute;left:5px;top:6px;width:4px;height:9px;background:linear-gradient(90deg,#a78bfa 50%,#4c1d95 50%)}.session-provenance{display:flex;align-items:center;gap:8px;font:10.5px var(--mono);color:var(--muted)}.session-provenance .folder{width:11px}.via{padding:2px 6px;border:1px solid var(--line2);border-radius:3px;color:var(--fg2)}.via.codex:before{content:"";display:inline-block;width:4px;height:4px;margin-right:5px;border-radius:50%;background:#10a37f;box-shadow:0 0 4px #10a37f}.session-reader h1{font-size:23px;line-height:1.25;letter-spacing:-.015em;margin:12px 0 7px}.session-meta-line{display:flex;gap:8px;align-items:center;font:11px var(--mono);color:var(--muted);margin-bottom:42px}.session-timeline{display:flex;flex-direction:column;gap:14px}.session-msg{padding:12px 14px;border:1px solid var(--line2);border-radius:7px;background:rgba(255,255,255,.022);box-shadow:inset 0 1px rgba(255,255,255,.025)}.session-msg.user{background:rgba(167,139,250,.08);border-color:rgba(167,139,250,.18)}.msg-head{display:flex;align-items:center;margin-bottom:9px;font:10px var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--fg2)}.msg-head time{margin-left:auto;color:var(--muted)}.msg-body{color:var(--fg2);font-size:13px;line-height:1.6;white-space:pre-wrap}.user .msg-body{color:var(--fg)}.trace-row{margin-top:8px;border:1px solid var(--line);border-radius:4px;background:rgba(0,0,0,.19);overflow:hidden}.trace-toggle{width:100%;height:29px;padding:0 10px;display:flex;align-items:center;gap:7px;text-align:left;font:10.5px var(--mono);color:var(--muted)}.trace-toggle:hover{background:var(--surface);color:var(--fg2)}.trace-toggle .trace-icon{color:var(--accent2);font-size:9px}.trace-body{display:none;padding:0 11px 10px;font:11px/1.55 var(--mono);color:var(--muted);white-space:pre-wrap}.trace-row.open .trace-body{display:block}.trace-row.open .trace-toggle{color:var(--fg2)}.session-reader .msg-nav{position:sticky;z-index:5;margin-top:-1px;bottom:14px}
|
||
.memory-reader{width:min(660px,calc(100% - 48px));margin:0 auto;padding:30px 0 80px}.memory-project{display:flex;align-items:center;gap:7px;font:11px var(--mono);color:var(--muted);margin-bottom:12px}.memory-project svg{width:12px}.memory-path{font:500 17px/1.5 var(--mono);color:var(--fg);word-break:break-all;margin-bottom:16px}.memory-summary{font-size:14px;line-height:1.6;color:var(--fg2);margin-bottom:16px}.memory-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-bottom:16px;border-bottom:1px solid var(--line);font:12px var(--mono);color:var(--muted)}.memory-meta button{color:var(--accent2);text-decoration:underline;text-decoration-color:#a78bfa40;text-underline-offset:3px}.memory-body-head{display:flex;align-items:center;margin:28px 0 12px;font-size:10.5px;color:var(--muted);letter-spacing:.04em}.memory-body-head span{flex:1}.source-toggle-v2{height:22px;padding:0 8px;border:1px solid var(--line2);border-radius:4px;background:var(--surface);font-size:12px;color:var(--muted)}.source-toggle-v2.active{background:var(--accentSoft);color:var(--accent2)}.memory-article{color:var(--fg2);font-size:13px;line-height:1.75}.memory-article h1{font-size:22px;color:var(--fg);margin:0 0 18px}.memory-article h2{font-size:17px;color:var(--fg);margin:24px 0 9px}.memory-article p{margin-bottom:14px}.memory-article ul,.memory-article ol{padding-left:24px;margin-bottom:14px}.memory-source{padding:14px 16px;border:1px solid var(--line);border-radius:6px;background:#0005;font:12px/1.55 var(--mono);white-space:pre-wrap;color:var(--fg2)}.anchor-head{display:flex;gap:8px;align-items:center;margin-top:28px;padding-bottom:8px;border-bottom:1px solid var(--line);font:10.5px var(--mono);color:var(--muted)}.anchor-v2{width:100%;display:flex;gap:8px;padding:9px 4px;border-bottom:1px solid var(--line);text-align:left;font:11px var(--mono);color:var(--accent2)}.memory-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:32px}
|
||
.session-reader{position:relative;width:100%;max-width:720px;padding:32px 32px 90px}.session-progress{position:sticky;top:0;z-index:5;height:2px;margin:-32px -32px 30px;background:var(--line)}.session-progress-fill{height:100%;background:var(--accent);box-shadow:0 0 6px rgba(167,139,250,.35)}.session-reader .session-header{margin-bottom:28px;padding-bottom:20px;border-bottom:1px solid var(--line)}.session-reader .session-eyebrow{display:flex;align-items:center;gap:6px;margin-bottom:12px;flex-wrap:wrap;font-size:11px;color:var(--muted)}.session-reader .session-eyebrow svg{width:13px;height:13px}.session-reader .session-eyebrow .project-name{color:var(--fg2);font-weight:500}.session-reader .session-eyebrow .project-path{font-family:var(--mono)}.session-reader .session-eyebrow .via{display:inline-flex;align-items:center;gap:5px;margin-left:6px;padding:1px 7px;border:1px solid var(--line);border-radius:3px;background:rgba(255,255,255,.04);font:10.5px var(--mono)}.via-dot{width:4px;height:4px;border-radius:50%}.via-dot.claude{background:#d97757;box-shadow:0 0 4px rgba(217,119,87,.5)}.via-dot.codex{background:#10a37f;box-shadow:0 0 4px rgba(16,163,127,.5)}.session-reader .session-title{margin-bottom:14px;font-size:22px;font-weight:600;line-height:1.3;letter-spacing:-.01em}.session-reader .session-meta-line{margin:0;gap:10px}.trace-chev{width:9px;color:var(--muted2);transition:transform .15s}.trace-row.open .trace-chev{transform:rotate(90deg);color:var(--accent2)}
|
||
|
||
.session-reader .msg-body{font-size:var(--reader-font,14px)}.trace-icon{width:14px;height:14px;display:inline-flex;align-items:center;flex:none}.trace-icon svg{width:14px;height:14px}.section-title .show-all svg{display:none}.section-title .show-all:before{content:"≡";font:11px/1 var(--mono);color:var(--muted2);transform:scaleX(1.15)}.sort{display:flex;align-items:center;gap:4px}.sort svg,.btn svg{width:13px;height:13px;flex:none}.memory-meta button{display:inline-flex;align-items:center;gap:5px}.memory-meta button svg{width:11px;height:11px;flex:none}.anchor-v2 svg{width:12px;height:12px;flex:none}
|
||
/* Activity 0.2.0 */
|
||
.activity-wrap{height:100%;overflow-y:auto}.activity-wide{max-width:860px;margin:0 auto;padding:28px 32px 60px}.activity-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px}.activity-title{font-size:16px;font-weight:600;letter-spacing:-.01em}.activity-tabs{display:flex}.activity-tab{padding:5px 12px;border:1px solid var(--line);color:var(--muted);font:12px var(--mono)}.activity-tab:first-child{border-radius:4px 0 0 4px}.activity-tab:last-child{border-radius:0 4px 4px 0}.activity-tab+.activity-tab{border-left:0}.activity-tab:hover{background:var(--surface2);color:var(--fg2)}.activity-tab.active{background:var(--accentSoft);border-color:var(--accentSoft);color:var(--fg)}.activity-stats{display:flex;margin-bottom:32px;border:1px solid var(--line);border-radius:8px;background:var(--surface);overflow:hidden}.activity-stat{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;padding:16px 12px;border-right:1px solid var(--line)}.activity-stat:last-child{border-right:0}.activity-stat-value{font-size:18px;font-weight:600;font-variant-numeric:tabular-nums}.activity-stat-label{font:10.5px var(--mono);color:var(--muted);text-align:center}.activity-chart{margin-top:8px}.activity-chart svg{display:block;width:100%;height:auto;max-height:164px}.heat-cell{cursor:pointer;transition:opacity .08s}.heat-cell:hover{opacity:.7}.heat-cell.level-0{fill:var(--surface2)}.heat-cell.level-1{fill:rgba(99,102,241,.3)}.heat-cell.level-2{fill:rgba(99,102,241,.5)}.heat-cell.level-3{fill:rgba(139,92,246,.7)}.heat-cell.level-4{fill:rgba(168,85,247,.9)}.heat-cell.selected{stroke:var(--fg);stroke-width:1.5}.heat-month{font:10px var(--mono);fill:var(--muted2)}.heat-legend{display:flex;justify-content:flex-end;align-items:center;gap:6px;margin-top:12px;color:var(--muted2);font:10px var(--mono)}.weekly-bar{fill:var(--accent);opacity:.8;cursor:pointer}.weekly-bar:hover{opacity:1}.cumulative-area{fill:rgba(99,102,241,.12)}.cumulative-line{fill:none;stroke:var(--accent);stroke-width:1.5}.cumulative-dot{fill:var(--accent);opacity:0;cursor:pointer}.cumulative-dot:hover{opacity:1}.chart-tooltip{position:fixed;z-index:200;padding:5px 10px;border:1px solid var(--line2);border-radius:4px;background:rgba(30,35,50,.95);color:var(--fg2);font:11px var(--mono);pointer-events:none;opacity:0;white-space:nowrap;backdrop-filter:blur(8px);transition:opacity .1s}.chart-tooltip.show{opacity:1}.activity-month{margin-top:28px;margin-bottom:44px}.activity-month-head{display:grid;grid-template-columns:max-content minmax(48px,1fr) max-content;align-items:center;gap:16px;margin-bottom:22px}.activity-month-head h2{font-size:14px;font-weight:600;letter-spacing:-.01em}.activity-month-rule{height:1px;background:var(--line)}.activity-month-count{font:10px/1 var(--mono);color:var(--muted2);letter-spacing:.04em}.activity-ledger{position:relative;margin-left:10px;padding-left:44px}.activity-ledger:before{content:"";position:absolute;top:2px;bottom:4px;left:12px;width:1px;background:var(--line)}.ledger-group{position:relative;padding-bottom:28px}.ledger-group:last-child{padding-bottom:6px}.ledger-node{position:absolute;top:-2px;left:-44px;width:26px;height:26px;display:grid;place-items:center;border:1px solid var(--line2);border-radius:50%;background:var(--surface);color:var(--muted);box-shadow:0 0 0 5px var(--bg)}.ledger-node svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2}.ledger-group.workspace .ledger-node{color:#f59e0b;border-color:rgba(245,158,11,.28);background:rgba(245,158,11,.1)}.ledger-group.started .ledger-node{color:var(--accent2);border-color:rgba(167,139,250,.28);background:var(--accentSoft)}.ledger-group-head{display:flex;align-items:baseline;justify-content:space-between;gap:20px;min-height:24px;margin-bottom:9px}.ledger-group-head h3{font-size:13px;font-weight:500}.ledger-group-head time{font:10px/1 var(--mono);color:var(--muted2);letter-spacing:.05em}.ledger-items{display:grid;gap:2px}.ledger-item{display:block;width:100%;margin-left:-10px;padding:6px 10px;border-radius:5px;text-align:left}.ledger-item:hover{background:var(--surface2)}.ledger-item-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--accent2);font-size:13px;font-weight:500;line-height:1.35}.ledger-item.continued .ledger-item-title{color:var(--fg2)}.ledger-item:hover .ledger-item-title{text-decoration:underline;text-underline-offset:2px}.ledger-item-meta{display:flex;align-items:center;gap:8px;min-height:15px;margin-top:4px;color:var(--muted2);font:11px/1.35 var(--mono)}.ledger-item-meta .source{color:var(--fg2);font-weight:500}.ledger-item-meta .project{color:var(--muted)}.ledger-noise{display:flex;align-items:center;gap:8px;width:max-content;margin:4px 0 0 -4px;padding:6px 8px;border-radius:5px;color:var(--muted2);font:11px/1.25 var(--mono)}.ledger-noise:hover{background:var(--surface2);color:var(--muted)}.ledger-noise .chev{width:9px;transition:transform .15s}.ledger-noise.expanded .chev{transform:rotate(90deg)}.show-more-activity{display:block;width:max-content;margin:-18px auto 16px;padding:8px 12px;border:1px solid var(--line);border-radius:6px;color:var(--muted);font:11px/1 var(--mono)}.show-more-activity:hover{background:var(--surface2);border-color:var(--line2);color:var(--fg2)}
|
||
|
||
.heat-legend svg{width:70px!important;height:11px!important;max-height:none!important;flex:0 0 70px}
|
||
.activity-empty{padding:26px 72px;color:var(--muted2);font-size:12px}
|
||
/* Recap list and generator */
|
||
.recap-list-v2{height:100%;overflow-y:auto;--rl-serif:var(--serif)}.rl-content{max-width:720px;margin:0 auto;padding:32px 32px 80px}.rl-head{display:flex;align-items:baseline;gap:12px;margin-bottom:20px;padding-bottom:10px;border-bottom:1px solid var(--line)}.rl-year{font:500 22px var(--serif);color:var(--fg2)}.rl-count{font:12px var(--mono);color:var(--muted)}.rl-timeline{position:relative}.rl-timeline:before{content:"";position:absolute;left:32px;top:32px;bottom:32px;width:1px;background:linear-gradient(rgba(167,139,250,.55),rgba(167,139,250,.35) 8%,rgba(255,255,255,.12) 30%,rgba(255,255,255,.06))}.rl-row{position:relative;display:grid;grid-template-columns:64px 1fr;align-items:center;gap:18px;padding:12px 0;cursor:pointer;transition:transform .12s}.rl-row:hover{transform:translateX(2px)}.rl-node{width:64px;height:64px;position:relative;z-index:2}.rl-node:before{content:"";position:absolute;inset:-2px;border-radius:50%;background:var(--bg);z-index:-1}.rl-node svg{display:block;width:100%;height:100%;filter:drop-shadow(0 0 6px var(--node-glow))}.rl-row:hover .rl-node svg{filter:drop-shadow(0 0 10px var(--node-glow))}.rl-card{display:grid;grid-template-columns:1fr auto;align-items:center;gap:16px;padding:14px 16px;border:1px solid var(--line);border-radius:8px;background:rgba(255,255,255,.02)}.rl-row:hover .rl-card{background:rgba(255,255,255,.035);border-color:var(--line2)}.rl-body{display:flex;flex-direction:column;gap:4px;min-width:0}.rl-period{display:flex;align-items:center;gap:8px;color:var(--muted);font:12px var(--mono)}.rl-period .dot{width:2px;height:2px;background:var(--muted2)}.rl-archetype{font:500 20px var(--serif);letter-spacing:-.01em}.rl-claim{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:rgba(255,255,255,.55);font:italic 14.5px/1.4 var(--serif)}.rl-stats{display:flex;gap:10px;margin-top:4px;color:var(--muted2);font:11.5px var(--mono)}.rl-arrow{color:var(--muted2);font-size:19px}
|
||
.generate-backdrop{position:fixed;inset:0;z-index:500;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(5,6,12,.65);backdrop-filter:blur(8px)}.generate-modal{width:100%;max-width:480px;border:1px solid var(--line2);border-radius:12px;background:linear-gradient(165deg,rgba(20,22,38,.95),rgba(13,15,28,.95));box-shadow:0 30px 80px #0009,0 12px 32px #0006,inset 0 1px rgba(255,255,255,.08);overflow:hidden}.generate-head{display:flex;align-items:baseline;gap:10px;padding:18px 22px 12px;border-bottom:1px solid var(--line)}.generate-head .diamond{width:6px;height:6px;align-self:center;background:var(--accent);transform:rotate(45deg);box-shadow:0 0 8px rgba(167,139,250,.35)}.generate-title{flex:1;font:500 17px var(--serif)}.generate-close{width:24px;height:24px;border-radius:4px;color:var(--muted)}.generate-close:hover{background:var(--surface);color:var(--fg2)}.generate-body{padding:18px 22px 20px}.generate-copy{margin-bottom:14px;color:var(--fg2);font:italic 13.5px/1.6 var(--serif)}.generate-options{display:flex;flex-direction:column;gap:1px;margin-bottom:14px;border:1px solid var(--line);border-radius:6px;background:var(--line);overflow:hidden}.generate-option{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;background:rgba(0,0,0,.2);text-align:left;color:var(--fg2);font:12px var(--mono)}.generate-option:hover{background:rgba(255,255,255,.025)}.generate-option.active{background:var(--accentSoft);color:var(--accent2)}.generate-radio{width:12px;height:12px;border:1.5px solid var(--muted2);border-radius:50%;position:relative}.generate-option.active .generate-radio{border-color:var(--accent)}.generate-option.active .generate-radio:after{content:"";position:absolute;inset:2px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px rgba(167,139,250,.35)}.generate-command{position:relative;margin-bottom:14px;padding:14px 50px 14px 16px;border:1px solid var(--line2);border-radius:6px;background:rgba(0,0,0,.4);font:12.5px var(--mono)}.generate-command .prompt{margin-right:4px;color:var(--accent2)}.generate-command button{position:absolute;right:8px;top:7px;width:32px;height:32px;border-radius:5px;color:var(--muted)}.generate-command button:hover,.generate-command button.copied{background:var(--surface);color:var(--accent2)}.generate-hint{color:var(--muted2);font:10.5px/1.5 var(--mono)}
|
||
|
||
/* Recap five-card reader */
|
||
.recap-app-v2{height:100%;display:grid;grid-template-rows:1fr 64px;position:relative;outline:0;--tc:#a78bfa;--tc2:#c4b5fd;--tg:rgba(167,139,250,.35);--tgmid:rgba(167,139,250,.18);--tgsoft:rgba(167,139,250,.08);--rfg3:rgba(255,255,255,.55)}.rc-stage{position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center;padding:32px 24px}.rc-deck{position:relative;width:100%;max-width:540px;height:100%}.rc-card{position:absolute;inset:0;display:flex;flex-direction:column;overflow:hidden;border:1px solid rgba(255,255,255,.1);border-radius:14px;background:linear-gradient(165deg,rgba(255,255,255,.04),rgba(255,255,255,.015));box-shadow:0 30px 80px #0008,0 12px 32px #0005,inset 0 1px rgba(255,255,255,.08)}.rc-card.themed:before{content:"";position:absolute;right:0;bottom:0;width:60%;height:50%;background:radial-gradient(ellipse at 100% 100%,var(--tgmid),transparent 70%)}.rc-cover{background:radial-gradient(120% 80% at 50% 100%,var(--tg),transparent 55%),radial-gradient(100% 70% at 50% 80%,var(--tgmid),transparent 65%),linear-gradient(180deg,rgba(10,11,20,.4),rgba(10,11,20,.85) 70%)}.rc-stars span{position:absolute;width:2px;height:2px;border-radius:50%;background:#fff;box-shadow:0 0 4px #fff9}.rc-stars span:nth-child(1){top:12%;left:18%}.rc-stars span:nth-child(2){top:8%;left:78%}.rc-stars span:nth-child(3){top:22%;left:88%;opacity:.6}.rc-stars span:nth-child(4){top:32%;left:8%;opacity:.5}.rc-stars span:nth-child(5){top:18%;left:52%;opacity:.7}.rc-eyebrow{display:flex;align-items:center;gap:10px;padding:22px 28px 0;position:relative;z-index:1;color:var(--muted);font:12px var(--mono)}.rc-eyebrow .diamond{width:6px;height:6px;background:var(--tc);transform:rotate(45deg);box-shadow:0 0 8px var(--tg)}.rc-eyebrow .slot{margin-left:auto;color:var(--muted2)}.rc-seal{position:absolute;right:24px;top:22px;width:60px;height:60px}.rc-seal svg{width:100%;height:100%;filter:drop-shadow(0 0 12px var(--tg))}.rc-cover-body{flex:1;display:flex;flex-direction:column;padding:0 36px;position:relative;z-index:1}.rc-cover-title{margin-top:auto;margin-bottom:18px;color:var(--fg);font:500 64px/1.05 var(--serif);letter-spacing:-.02em;text-shadow:0 2px 24px #0006}.rc-cover-claim{max-width:92%;margin-bottom:36px;color:var(--fg2);font:italic 19px/1.5 var(--serif)}.rc-activity{margin-bottom:28px}.rc-activity-bars{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;margin-bottom:8px}.rc-activity-bar{height:32px;border:1px solid var(--line);border-radius:3px;background:rgba(255,255,255,.04);position:relative;overflow:hidden}.rc-activity-bar i{position:absolute;left:0;right:0;bottom:0;background:linear-gradient(var(--tc2),var(--tc));box-shadow:0 0 10px var(--tg)}.rc-day-labels{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;text-align:center;color:var(--muted2);font:10.5px var(--mono)}.rc-footer{padding-bottom:28px;color:var(--muted);font:13px var(--mono)}.rc-title{padding:18px 36px 6px;font:500 30px/1.2 var(--serif);letter-spacing:-.015em;position:relative;z-index:1}.rc-content{flex:1;padding:0 36px 32px;overflow:auto;position:relative;z-index:1}.rc-path{position:relative;padding-left:28px}.rc-path:before{content:"";position:absolute;left:6px;top:14px;bottom:14px;width:1px;background:linear-gradient(var(--tg),rgba(255,255,255,.06))}.rc-path-item{position:relative;padding:8px 0 10px}.rc-path-item:before{content:"";position:absolute;left:-28px;top:16px;width:7px;height:7px;background:var(--tc);transform:rotate(45deg);box-shadow:0 0 8px var(--tg),0 0 0 3px var(--bg)}.rc-day{margin-bottom:4px;color:var(--tc2);font:600 12px var(--mono)}.rc-prompt{margin-bottom:6px;color:var(--fg);font:italic 16px/1.35 var(--serif)}.rc-outcome{display:inline-flex;padding:4px 10px;border:1px solid var(--line);border-left:2px solid var(--tc);border-radius:4px;background:rgba(255,255,255,.025);color:var(--fg2);font:12px var(--mono)}.rc-section-label{margin:8px 0 12px;color:var(--muted);font:italic 13px var(--serif)}.rc-vibe-list{display:flex;flex-direction:column;gap:10px}.rc-vibe-row{display:flex;align-items:baseline;gap:12px;padding:10px 14px;border:1px solid var(--line);border-left:2px solid var(--tgmid);border-radius:4px;background:rgba(255,255,255,.025)}.rc-vibe-text{flex:1;color:var(--fg);font:italic 18px/1.4 var(--serif)}.rc-vibe-meta{white-space:nowrap;color:var(--muted);font:12px var(--mono)}.rc-meter{margin-top:22px}.rc-meter-track{height:10px;border:1px solid var(--line);border-radius:2px;background:rgba(255,255,255,.04);overflow:hidden}.rc-meter-fill{height:100%;width:76%;background:linear-gradient(90deg,var(--tc),var(--tc2));box-shadow:0 0 12px var(--tg)}.rc-meter-row{display:flex;justify-content:space-between;margin-top:8px}.rc-meter-row span:first-child{color:var(--muted);font:italic 14px var(--serif)}.rc-meter-row span:last-child{color:var(--tc2);font:600 11.5px var(--mono)}.rc-quote{margin-top:22px;padding-top:18px;border-top:1px solid var(--line);color:var(--fg);font:500 22px/1.4 var(--serif)}.rc-deck-text{padding:0 36px 22px;color:var(--rfg3);font:italic 15px/1.55 var(--serif);position:relative;z-index:1}.rc-workflow-stat{margin-bottom:18px;color:var(--muted);font:13px var(--mono)}.rc-workflow-list{border:1px solid var(--line);border-radius:6px;overflow:hidden}.rc-workflow-row{padding:14px 16px;background:rgba(10,11,20,.4)}.rc-workflow-row+.rc-workflow-row{border-top:1px solid var(--line)}.rc-workflow-row b{display:block;margin-bottom:6px;font:500 13px var(--mono)}.rc-workflow-row span{color:var(--fg2);font:italic 16px/1.4 var(--serif)}.rc-verdict{margin-top:auto;padding:16px 18px;border:1px solid var(--line2);border-radius:6px;background:rgba(255,255,255,.025)}.rc-verdict small{display:block;margin-bottom:6px;color:var(--muted);font:italic 13px var(--serif)}.rc-verdict strong{font:500 22px var(--serif)}.rc-closing{background:radial-gradient(80% 70% at 50% 30%,var(--tgsoft),transparent 60%),radial-gradient(60% 50% at 50% 50%,var(--tgmid),transparent 70%),linear-gradient(180deg,rgba(10,11,20,.6),rgba(10,11,20,.95))}.rc-closing-body{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:32px;padding:0 40px;text-align:center}.rc-closing-title{font:500 72px/1 var(--serif);letter-spacing:-.02em;text-shadow:0 4px 24px var(--tg)}.rc-closing-stats{display:flex;flex-direction:column;gap:4px;color:var(--muted);font:13px var(--mono)}.rc-closing-quote{max-width:360px;color:var(--fg2);font:italic 19px/1.5 var(--serif)}.rc-closing-quote small{display:block;margin-top:8px;color:var(--muted);font-size:13px}.rc-signoff{color:var(--muted);font:italic 15px var(--serif)}.rc-nav{display:flex;align-items:center;justify-content:center;gap:16px;padding:0 22px;border-top:1px solid var(--line);background:rgba(0,0,0,.18);position:relative}.rc-arrow{width:36px;height:36px;border:1px solid var(--line2);border-radius:50%;background:var(--surface);color:var(--fg2)}.rc-arrow:hover:not(:disabled){background:var(--surface2);color:var(--fg)}.rc-dots{display:flex;gap:8px}.rc-dot{display:flex;flex-direction:column;align-items:center;gap:4px;padding:4px 8px;border-radius:4px}.rc-dot:hover{background:var(--surface)}.rc-glyph{width:24px;height:3px;border-radius:2px;background:rgba(255,255,255,.16)}.rc-dot.active .rc-glyph{width:28px;background:var(--tc);box-shadow:0 0 8px var(--tg)}.rc-label{color:var(--muted2);font:italic 11px var(--serif)}.rc-dot.active .rc-label{color:var(--fg2)}.rc-actions{position:absolute;right:60px;display:flex;gap:6px}.rc-action{width:32px;height:32px;display:grid;place-items:center;border:1px solid var(--line2);border-radius:6px;background:var(--surface);color:var(--fg2)}.rc-action:hover{background:var(--surface2);color:var(--fg)}.rc-action svg{width:14px;height:14px}
|
||
|
||
.recap-app-v2.p1{--tc:#fbbf24;--tc2:#fde68a;--tg:rgba(251,191,36,.4);--tgmid:rgba(251,191,36,.22);--tgsoft:rgba(251,191,36,.1)}.recap-app-v2.p2{--tc:#f472b6;--tc2:#fda4af;--tg:rgba(244,114,182,.4);--tgmid:rgba(244,114,182,.22);--tgsoft:rgba(244,114,182,.1)}.recap-app-v2.p3{--tc:#67e8f9;--tc2:#a5f3fc;--tg:rgba(103,232,249,.4);--tgmid:rgba(103,232,249,.22);--tgsoft:rgba(103,232,249,.1)}.recap-app-v2.p4{--tc:#fcd34d;--tc2:#fde68a;--tg:rgba(252,211,77,.4);--tgmid:rgba(252,211,77,.22);--tgsoft:rgba(252,211,77,.1)}.recap-app-v2.p5{--tc:#34d399;--tc2:#6ee7b7;--tg:rgba(52,211,153,.4);--tgmid:rgba(52,211,153,.22);--tgsoft:rgba(52,211,153,.1)}.recap-app-v2.p6{--tc:#64748b;--tc2:#94a3b8;--tg:rgba(100,116,139,.45);--tgmid:rgba(100,116,139,.25);--tgsoft:rgba(100,116,139,.12)}.rc-card{animation:rc-enter .22s cubic-bezier(.22,1,.36,1)}@keyframes rc-enter{from{opacity:0;transform:translateY(24px) scale(.97)}}
|
||
|
||
/* Settings exact copy */
|
||
.settings-v2{max-width:720px;margin:0 auto;padding:36px 32px 80px}.settings-v2 section{margin-bottom:44px}.settings-v2 .section-head{margin-bottom:16px}.settings-v2 .section-head h2{font-size:18px;font-weight:600;letter-spacing:-.01em}.settings-v2 .section-head p{font-size:13px}.settings-v2 .source-card{transition:border-color .15s}.settings-v2 .btn{display:inline-flex;align-items:center;gap:6px;font-weight:500;white-space:nowrap}.source-mark .dot{width:8px;height:8px}.source-card-name{display:flex;align-items:baseline;gap:8px;font-size:14px;font-weight:600}.source-card-name .vendor{font-size:11.5px}.source-status{display:flex;align-items:center;gap:8px;margin-top:3px;color:var(--muted);font:10.5px var(--mono)}.source-status .status-dot-v2{position:relative;width:6px;height:6px;border-radius:50%;background:#34d399;box-shadow:0 0 5px rgba(52,211,153,.5)}.source-status .status-dot-v2:before{content:"";position:absolute;inset:-2.5px;border:1px solid #34d399;border-radius:50%;animation:src-pulse 1.6s ease-out infinite}.source-status .connected{color:#34d399}.source-status strong{color:var(--fg2);font-weight:500}.setting-hint{margin-top:4px;color:var(--muted);font-size:11.5px}.setting-hint code{padding:1px 4px;border-radius:3px;background:#0005;color:var(--muted);font:10.5px var(--mono)}.settings-v2 .setting-row{align-items:start}.version-text{padding-top:6px;color:var(--fg2);font:12px var(--mono)}.reset-hint{margin-top:6px;color:var(--muted);font-size:11.5px}@keyframes src-pulse{0%{transform:scale(.8);opacity:.5}100%{transform:scale(1.8);opacity:0}}
|
||
/* Subagent detail: the installed app's wider, non-virtualized conversation reader. */
|
||
.subagent-wrap{height:100%;overflow-y:auto}.subagent-wide{max-width:860px;margin:0 auto;padding:28px 32px 60px}.subagent-header{margin-bottom:28px;padding-bottom:20px;border-bottom:1px solid var(--line)}.subagent-eyebrow{margin-bottom:12px;color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.05em}.subagent-title{margin-bottom:14px;color:var(--fg);font-size:22px;font-weight:600;line-height:1.3;letter-spacing:-.01em}.subagent-meta{color:var(--muted);font:12px var(--mono)}.sub-timeline{display:flex;flex-direction:column;gap:14px}.sub-msg{padding:12px 14px;border:1px solid rgba(255,255,255,.06);border-radius:8px;background:rgba(255,255,255,.025)}.sub-msg.user{border-color:rgba(167,139,250,.18);background:rgba(167,139,250,.08)}.sub-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;color:var(--muted);font:11px var(--mono)}.sub-role{color:var(--fg2);font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.06em}.sub-msg.user .sub-role{color:var(--accent2)}.sub-when{margin-left:auto;font-variant-numeric:tabular-nums}.markdown-msg{color:var(--fg);font:13px/1.75 "Helvetica Neue","Inter",-apple-system,system-ui,"PingFang SC","Hiragino Sans GB",sans-serif;letter-spacing:.005em;word-wrap:break-word}.markdown-msg p{margin:.5em 0}.markdown-msg p:first-child{margin-top:0}.markdown-msg p:last-child{margin-bottom:0}.markdown-msg h2{margin:1em 0 .4em;color:var(--fg);font-size:15px;font-weight:600;line-height:1.3;letter-spacing:-.01em}.markdown-msg ul,.markdown-msg ol{margin:.5em 0;padding-left:22px}.markdown-msg li{margin:.18em 0}.markdown-msg code{padding:1px 5px;border-radius:3px;background:rgba(255,255,255,.06);color:var(--accent2);font:12px var(--mono)}.sub-disclosure{margin-top:10px;overflow:hidden;border:1px solid var(--line);border-radius:5px;background:rgba(0,0,0,.15)}.sub-toggle{display:flex;align-items:center;gap:8px;width:100%;padding:7px 10px;text-align:left;color:inherit}.sub-toggle:hover{background:rgba(255,255,255,.03)}.sub-chevron{width:8px;height:8px;flex:none;color:var(--muted);transition:transform .15s}.sub-disclosure.open .sub-chevron{transform:rotate(90deg);color:var(--accent2)}.sub-label{flex:none;color:var(--muted);font:600 10.5px var(--mono);text-transform:uppercase;letter-spacing:.05em}.sub-preview{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--muted2);font-size:11px}.sub-body{display:none;padding:8px 14px 12px;border-top:1px solid var(--line)}.sub-disclosure.open .sub-body{display:block}.sub-meta{margin-top:0;background:rgba(0,0,0,.1)}.sub-meta .sub-toggle{padding:5px 10px}.sub-meta .sub-label{color:var(--muted2);font-size:10px}.sub-meta .sub-body{padding:6px 12px 10px}.sub-tool{margin-top:5px;background:rgba(0,0,0,.2)}.sub-tool.is-error{border-color:rgba(248,113,113,.3);background:rgba(248,113,113,.12)}.sub-tool .sub-toggle{padding:6px 10px}.sub-tool-name{flex:none;color:var(--accent2);font:600 11px var(--mono)}.sub-tool-arg{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg2);font:11px var(--mono)}.sub-tool.is-error .sub-tool-name{color:var(--danger)}.sub-error{flex:none;padding:1px 6px;border-radius:3px;background:rgba(248,113,113,.18);color:var(--danger);font-size:10px;font-weight:500;text-transform:uppercase;letter-spacing:.04em}.sub-tool .sub-body{max-height:400px;overflow:auto;padding:12px 14px;background:rgba(0,0,0,.32)}.tc-section{margin:0 0 5px;color:var(--muted);font:500 10px var(--mono);text-transform:uppercase;letter-spacing:.05em}.sub-tool pre{margin-bottom:12px;color:var(--fg2);font:11.5px/1.55 var(--mono);white-space:pre-wrap;word-wrap:break-word}.sub-tool pre:last-child{margin-bottom:0}.truncated-btn{display:block;width:100%;margin-top:8px;padding:6px 12px;border:1px solid rgba(167,139,250,.2);border-radius:4px;background:var(--accentSoft);color:var(--accent2);font:11px var(--mono);text-align:center}.truncated-btn:hover{border-color:var(--accent);background:rgba(167,139,250,.2)}.empty-text{color:var(--muted2);font-style:italic}.agent-call{border-color:rgba(245,158,11,.12)!important;border-left:2px solid #f59e0b!important}.agent-call-head{display:flex;align-items:center}.agent-call-head .trace-toggle{min-width:0;flex:1;width:auto}.agent-kind{flex:none;color:#f59e0b;font-weight:600}.agent-nav-btn{margin-right:8px;padding:2px 8px;border:1px solid rgba(245,158,11,.25);border-radius:3px;background:rgba(245,158,11,.12);color:#f59e0b;white-space:nowrap;font:10.5px var(--mono)}.agent-nav-btn:hover{border-color:#f59e0b;background:rgba(245,158,11,.25)}
|
||
/* Sessions' hidden branches: first-run/no-source state and quiet-session group. */
|
||
.empty .hint{color:var(--muted2);font-size:11px}
|
||
.empty-content{display:flex;flex:1;max-width:520px;margin:0 auto;padding:40px;flex-direction:column;justify-content:center;gap:16px}.empty-eyebrow{display:flex;align-items:center;gap:8px;color:var(--muted);font:11px var(--mono);letter-spacing:.04em}.empty-eyebrow .diamond{width:6px;height:6px;flex:none;background:var(--accent);transform:rotate(45deg);box-shadow:0 0 6px rgba(167,139,250,.4)}.empty-title{color:var(--fg);font:500 22px/1.2 var(--serif);letter-spacing:-.015em}.empty-body{max-width:460px;color:rgba(255,255,255,.55);font:italic 14px/1.6 var(--serif)}.empty-body code{padding:1px 6px;border-radius:3px;background:var(--accentSoft);color:var(--accent2);font:12.5px var(--mono);font-style:normal}.empty-body strong{color:var(--fg);font-style:normal;font-weight:600}.empty-body .inline-link{padding-bottom:1px;border-bottom:1px solid rgba(167,139,250,.4);color:var(--accent2)}.empty-actions{display:flex;gap:8px;margin-top:6px}.empty-actions .toolbar-action{display:inline-flex;height:32px;padding:0 14px;align-items:center;gap:6px;border:1px solid rgba(167,139,250,.35);border-radius:5px;background:var(--accentSoft);color:var(--accent2);font-size:12px;font-weight:500}.empty-actions svg{width:13px;height:13px}.empty-divider{width:100%;height:1px;margin:6px 0;background:var(--line)}.empty-help{display:flex;flex-direction:column;gap:6px;color:var(--muted);font:11px var(--mono)}.help-row{display:flex;align-items:baseline;gap:8px}.help-row .label{width:76px;flex:none;color:var(--muted2);letter-spacing:.04em}.empty-help code{padding:1px 6px;border-radius:3px;background:rgba(0,0,0,.3);color:var(--fg2);font-family:var(--mono)}.fold-banner{display:flex;align-items:center;gap:12px;padding:10px 22px;border-top:1px solid var(--line);border-bottom:1px solid var(--line);background:rgba(255,255,255,.015);color:var(--muted);font-size:12.5px;text-align:left}.fold-banner:hover{background:rgba(255,255,255,.03);color:var(--fg2)}.fold-banner .chev{width:10px;height:10px;flex:none;color:var(--muted2);transition:transform .15s}.fold-banner.expanded .chev{transform:rotate(90deg);color:var(--accent2)}.fold-banner .body{flex:1}.fold-banner strong{color:var(--fg2);font:500 11.5px var(--mono)}.fold-banner .reveal-link{padding-bottom:1px;border-bottom:1px solid rgba(167,139,250,.4);color:var(--accent2);font-size:11.5px}.noise-group{border-bottom:1px solid var(--line2);background:rgba(0,0,0,.15)}.noise-group-head{padding:6px 22px;border-bottom:1px solid var(--line);background:rgba(0,0,0,.1);color:var(--muted2);font:10px var(--mono);text-transform:uppercase;letter-spacing:.06em}.session-row.noise{min-height:58px;padding:8px 22px 8px 18px}.session-row.noise .session-row-title{color:var(--muted);font-size:13px;font-style:italic;font-weight:400}.session-row.noise .session-row-meta{color:var(--muted2)}.noise-fold-bottom{display:flex;width:100%;padding:8px 22px;align-items:center;gap:8px;border-top:1px solid var(--line);background:rgba(0,0,0,.2);color:var(--muted);font:11px var(--mono);text-align:left}.noise-fold-bottom:hover{background:rgba(0,0,0,.3);color:var(--fg2)}.noise-fold-bottom .chev{width:9px;height:9px;transform:rotate(-90deg);color:var(--muted2)}
|
||
@media(max-width:900px){:root{--sidebar:190px}.search{width:170px}.stats{grid-template-columns:repeat(3,1fr)}.stat:nth-child(n+4){display:none}.detail{padding-inline:28px}.usage{padding-inline:24px}}
|
||
@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}
|
||
/* SessionTimelineRow.vue fidelity: component branches and tool presenters. */
|
||
.session-reader{--text-base:var(--reader-font,14px);--text-md:var(--reader-font,14px);max-width:720px;width:100%;padding:32px 32px 90px}
|
||
.session-reader .session-progress{margin:-32px -32px 30px}.session-reader .msg-nav{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);margin:0;z-index:10}.session-reader .msg-nav button{width:28px;height:28px}.session-timeline{gap:14px}
|
||
.session-timeline .msg{position:relative;padding:12px 14px;border:1px solid;border-radius:8px;background:rgba(255,255,255,.025);transition:border-color .6s,box-shadow .6s}
|
||
.session-timeline .msg.user{background:rgba(167,139,250,.08);border-color:rgba(167,139,250,.18)}
|
||
.session-timeline .msg.assistant{border-color:rgba(255,255,255,.06)}
|
||
.session-timeline .msg-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;color:var(--muted);font:11px var(--mono);letter-spacing:0;text-transform:none}
|
||
.session-timeline .msg-head .role{color:var(--fg2);font:600 10px var(--mono);text-transform:uppercase;letter-spacing:.06em}
|
||
.session-timeline .msg.user .msg-head .role{color:var(--accent2)}
|
||
.session-timeline .msg-head .when{margin-left:auto;color:var(--muted);font-variant-numeric:tabular-nums}
|
||
.msg-text{color:var(--fg);font-size:var(--text-base);line-height:1.55;white-space:pre-wrap;overflow-wrap:anywhere}
|
||
.msg-text p{margin:.55em 0}.msg-text p:first-child{margin-top:0}.msg-text p:last-child{margin-bottom:0}.msg-text h3{margin:0 0 .55em;font-size:15px}.msg-text code{padding:1px 5px;border-radius:3px;background:rgba(255,255,255,.06);color:var(--accent2);font:calc(var(--text-base) - 1px) var(--mono)}
|
||
.msg-thinking{overflow:hidden;border:1px solid var(--line);border-radius:5px;background:rgba(0,0,0,.15)}
|
||
.thinking-toggle,.meta-toggle,.summary-toggle,.toolcall-toggle{display:flex;width:100%;align-items:center;gap:8px;border:0;background:transparent;color:inherit;text-align:left}
|
||
.thinking-toggle{padding:7px 10px}.thinking-toggle:hover,.meta-toggle:hover,.toolcall-toggle:hover{background:rgba(255,255,255,.03)}
|
||
.timeline-chevron{width:8px;height:8px;flex:none;color:var(--muted);transition:transform .15s}.trace-row.open .timeline-chevron{transform:rotate(90deg);color:var(--accent2)}
|
||
.thinking-label{flex:none;color:var(--muted);font:600 10.5px var(--mono);text-transform:uppercase;letter-spacing:.05em}
|
||
.thinking-body{display:none;padding:8px 14px 12px;border-top:1px solid var(--line)}.msg-thinking.open .thinking-body{display:block}
|
||
.msg-tools{display:flex;flex-direction:column;gap:5px;margin-top:10px}.msg-tool{overflow:hidden;border:1px solid var(--line);border-radius:5px;background:rgba(0,0,0,.2);transition:border-color .1s}
|
||
.msg-tool.is-error{border-color:rgba(248,113,113,.3);background:rgba(248,113,113,.12)}.toolcall-toggle{padding:6px 10px}
|
||
.tool-icon{display:inline-flex;width:14px;height:14px;flex:none;align-items:center;color:var(--accent2)}.tool-icon svg{width:14px;height:14px}.msg-tool.is-error .tool-icon{color:var(--danger)}
|
||
.tool-name{flex:none;color:var(--accent2);font:600 11px var(--mono)}.msg-tool.is-error .tool-name{color:var(--danger)}
|
||
.tool-arg{min-width:0;flex:1;overflow:hidden;color:var(--fg2);font:11px var(--mono);text-overflow:ellipsis;white-space:nowrap}.tool-error{flex:none;padding:1px 6px;border-radius:3px;background:rgba(248,113,113,.18);color:var(--danger);font-size:10px;font-weight:500;text-transform:uppercase;letter-spacing:.04em}
|
||
.toolcall-body{display:none;border-top:1px solid var(--line);background:rgba(0,0,0,.32)}.msg-tool.open .toolcall-body{display:block}
|
||
.toolcall-body-strip{display:flex;align-items:center;gap:8px;padding:6px 10px;border-bottom:1px solid var(--line);background:rgba(0,0,0,.18)}.strip-label{color:var(--muted);font:10px var(--mono);text-transform:uppercase;letter-spacing:.05em}.raw-toggle{padding:2px 7px;border:1px solid var(--line);border-radius:3px;color:var(--muted);font:10px var(--mono)}.raw-toggle:hover{border-color:var(--line2);background:var(--surface2);color:var(--fg2)}.raw-toggle.active{border-color:var(--accentSoft);background:var(--accentSoft);color:var(--accent2)}
|
||
.toolcall-pretty{padding:10px 12px}.toolcall-raw{display:none;max-height:400px;overflow:auto;padding:12px 14px}.msg-tool.raw .toolcall-pretty{display:none}.msg-tool.raw .toolcall-raw{display:block}.tc-section{margin:0 0 5px;color:var(--muted);font:500 10px var(--mono);text-transform:uppercase;letter-spacing:.05em}.toolcall-raw .tc-section+pre{margin-bottom:12px}.toolcall-raw pre{color:var(--fg2);font:11.5px/1.55 var(--mono);white-space:pre-wrap;overflow-wrap:anywhere}
|
||
.codeact-view{overflow:hidden;border:1px solid var(--line2);border-radius:6px;background:#11121d;color:var(--fg2);font:11.5px/1.55 var(--mono)}.codeact-section+.codeact-section{border-top:1px solid var(--line2)}.codeact-section-head{min-height:32px;display:flex;align-items:center;padding:6px 10px 6px 12px;background:#181a27;color:var(--muted)}.codeact-section-label{color:var(--fg2);font-size:9.5px;font-weight:650;letter-spacing:.09em;text-transform:uppercase}.codeact-code-frame{display:grid;grid-template-columns:max-content minmax(0,1fr);max-height:260px;overflow:auto}.codeact-gutter,.codeact-code,.codeact-result-block{margin:0;font:inherit;white-space:pre}.codeact-gutter{padding:8px 10px 8px 12px;border-right:1px solid var(--line);background:rgba(255,255,255,.015);color:var(--muted2);text-align:right;user-select:none}.codeact-code{padding:8px 12px;color:var(--fg2)}.codeact-token.keyword{color:#c4b5fd}.codeact-token.string{color:#86efac}.codeact-token.global{color:#7dd3fc}.codeact-result{max-height:280px;overflow:auto;background:#0d0e17}.codeact-result-block{padding:10px 12px;white-space:pre-wrap;overflow-wrap:anywhere}.codeact-note{padding:7px 12px;border-top:1px solid var(--line);background:rgba(251,191,36,.07);color:#d6bd82;font:10.5px var(--sans)}
|
||
.terminal-view{overflow:hidden;border:1px solid var(--line);border-radius:5px;background:#07090f;color:var(--fg2);font:11.5px/1.55 var(--mono)}.terminal-prompt-line{display:flex;gap:8px;padding:8px 12px;background:rgba(255,255,255,.03)}.prompt-marker{flex:none;color:#4ade80;font-weight:600}.prompt-cmd{color:var(--fg);white-space:pre-wrap;overflow-wrap:anywhere}.terminal-divider{height:1px;background:rgba(255,255,255,.06)}.terminal-output{max-height:300px;overflow:auto;margin-left:10px;padding:8px 12px;border-left:2px solid rgba(255,255,255,.06);color:rgba(255,255,255,.68);white-space:pre-wrap}.terminal-output.is-error{border-left-color:rgba(248,113,113,.3);color:#fca5a5}
|
||
.file-content,.diff-view{overflow:hidden;border:1px solid var(--line);border-radius:5px;background:rgba(0,0,0,.4)}.file-content-head,.diff-view-head{display:flex;align-items:center;gap:10px;padding:6px 10px;border-bottom:1px solid var(--line);background:rgba(255,255,255,.02);color:var(--muted);font:10.5px var(--mono)}.file-content-head .label,.diff-view-head .label{color:var(--fg2);font-size:10px;font-weight:500;letter-spacing:.04em;text-transform:uppercase}.file-content-head .meta,.diff-view-head .stats{margin-left:auto}.file-content-body,.diff-body{display:grid;grid-template-columns:max-content 1fr;max-height:320px;overflow:auto;color:var(--fg2);font:11.5px/1.55 var(--mono)}.file-content-body.collapsed{max-height:180px}.file-content-body .gutter,.diff-gutter{padding:6px 10px 6px 12px;border-right:1px solid var(--line);background:rgba(255,255,255,.015);color:var(--muted2);text-align:right;white-space:pre;user-select:none}.file-content-body .code,.diff-line{padding:6px 12px;white-space:pre}.file-content-expand{width:100%;padding:6px;border-top:1px solid var(--line);background:rgba(255,255,255,.02);color:var(--muted);font:10.5px var(--mono)}.file-content-expand:hover{background:var(--surface2);color:var(--fg2)}.diff-body{max-height:380px}.diff-gutter{padding-block:0}.diff-line{padding-block:0}.diff-line.add{background:rgba(99,102,241,.06);color:rgba(165,180,252,.85)}.diff-line.del{background:rgba(236,72,153,.06);color:rgba(249,168,212,.6);text-decoration:line-through}.stat-add{color:rgba(165,180,252,.85)}.stat-del{color:rgba(249,168,212,.7)}
|
||
.field-grid{display:grid;grid-template-columns:max-content 1fr;gap:4px 14px;color:var(--fg2);font:11.5px var(--mono)}.field-key{color:var(--muted);font-weight:500}.literal-string{color:var(--accent2)}.literal-bool{color:#4ade80}.literal-num{color:#fcd34d}.result-chip{display:inline-flex;margin-top:10px;padding:5px 10px;border:1px solid rgba(74,222,128,.18);border-radius:4px;background:rgba(74,222,128,.12);color:var(--fg2);font-size:11.5px}.result-chip.error{border-color:rgba(248,113,113,.25);background:rgba(248,113,113,.12)}
|
||
.msg-tool.agent-call{border-color:rgba(245,158,11,.12);border-left:2px solid #f59e0b}.msg-tool.agent-call .tool-name{color:#f59e0b}.agent-call-head{display:flex;align-items:center}.agent-call-head .toolcall-toggle{min-width:0;flex:1;width:auto}.agent-nav-btn{margin-left:auto;margin-right:8px}.agent-prompt{margin-bottom:8px;padding:8px 10px;border-radius:4px;background:rgba(0,0,0,.2);color:var(--fg2);font-size:12px;line-height:1.5;white-space:pre-wrap}.agent-result{max-height:400px;overflow:auto;color:var(--fg);font-size:13px;line-height:1.55}
|
||
.skill-card{display:flex;align-items:flex-start;gap:12px;padding:12px 14px;border:1px solid rgba(6,182,212,.15);border-left:3px solid rgba(6,182,212,.5);border-radius:8px;background:rgba(6,182,212,.04)}.skill-card-icon{width:28px;height:28px;display:grid;flex:none;place-items:center;border-radius:6px;background:rgba(6,182,212,.1);color:#67e8f9}.skill-card-icon svg{width:14px}.skill-card-body{min-width:0;flex:1}.skill-card-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.skill-card-badge{padding:1px 6px;border-radius:3px;background:rgba(6,182,212,.15);color:#67e8f9;font:600 10px var(--mono);text-transform:uppercase;letter-spacing:.04em}.skill-card-name{color:var(--fg);font-size:14px;font-weight:500}.skill-card-args{overflow:hidden;color:rgba(255,255,255,.58);font-size:12px;line-height:1.5}.skill-card-md{margin-top:8px}.skill-md-toggle{display:flex;align-items:center;gap:6px;padding:2px 0;color:var(--muted);font:10.5px var(--mono)}.skill-md-body{display:none;max-height:400px;overflow:auto;margin-top:8px;padding:12px;border:1px solid var(--line);border-radius:5px;background:rgba(0,0,0,.3)}.skill-card.open .skill-md-body{display:block}
|
||
.msg.meta{padding:4px 10px;border-color:var(--line);background:transparent}.msg-meta-collapsed{overflow:hidden;border:1px solid var(--line);border-radius:4px;background:rgba(0,0,0,.1)}.meta-toggle{padding:5px 10px}.meta-label{flex:none;color:var(--muted2);font:600 10px var(--mono);text-transform:uppercase;letter-spacing:.05em}.meta-preview{min-width:0;flex:1;overflow:hidden;color:var(--muted2);font-size:11px;text-overflow:ellipsis;white-space:nowrap}.meta-body{display:none;padding:6px 12px 10px;border-top:1px solid var(--line)}.msg-meta-collapsed.open .meta-body{display:block}
|
||
.msg-summary{overflow:hidden;margin-top:12px;border:1px solid var(--line);border-left:3px solid var(--accentSoft);border-radius:5px;background:rgba(167,139,250,.04)}.summary-toggle{padding:7px 12px}.summary-label{color:var(--accent2);font:600 10.5px var(--mono);text-transform:uppercase;letter-spacing:.05em}.summary-source{margin-left:4px;color:var(--muted);font:10.5px var(--mono)}.summary-body{display:none;padding:8px 14px 12px;border-top:1px solid var(--line)}.msg-summary.open .summary-body{display:block}
|
||
.wf-card{overflow:hidden;border:1px solid rgba(245,158,11,.12);border-left:3px solid #f59e0b;border-radius:8px;background:rgba(245,158,11,.04)}.wf-card-header{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:1px solid rgba(245,158,11,.12);background:rgba(245,158,11,.06)}.wf-card-name{color:var(--fg);font:600 14px var(--mono)}.wf-card-count{color:var(--muted);font:11px var(--mono)}.wf-card-status{margin-left:auto;padding:2px 8px;border-radius:3px;background:rgba(74,222,128,.14);color:#4ade80;font:500 10px var(--mono);text-transform:uppercase;letter-spacing:.04em}.wf-card-body{padding:12px 16px}.wf-card-phase-title{margin-bottom:4px;padding-bottom:4px;border-bottom:1px solid var(--line);color:#f59e0b;font:600 11px var(--mono);text-transform:uppercase;letter-spacing:.05em}.wf-card-agent{display:flex;width:100%;align-items:center;gap:10px;padding:7px 10px;border-radius:4px;text-align:left}.wf-card-agent:hover{background:rgba(245,158,11,.08)}.wf-card-agent-label{min-width:0;flex:1;overflow:hidden;color:var(--fg2);font-size:13px;text-overflow:ellipsis;white-space:nowrap}.wf-card-agent-arrow{color:var(--muted2)}
|
||
.archived-tag{display:inline-flex;align-items:center;gap:4px;padding:1px 6px;border-radius:3px;background:rgba(248,113,113,.1);color:var(--danger);font:9.5px var(--mono);text-transform:uppercase;letter-spacing:.04em}.archived-tag:before{content:"";width:4px;height:4px;border-radius:50%;background:currentColor}.memory-actions .btn.danger{color:var(--danger)}.memory-actions .btn.danger:hover{border-color:rgba(248,113,113,.3);background:rgba(248,113,113,.12)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="app"></div>
|
||
<div id="modalHost"></div>
|
||
<div id="toast" aria-live="polite"></div>
|
||
<script>
|
||
const sessions=[
|
||
{id:'s1',title:'Design the Obelisk session reader',project:'quiet-zero',source:'codex',when:'18 min',created:'Today, 14:32',messages:86,duration:'42m',branch:'codex/session-reader',snippet:'Implement the virtualized timeline and preserve reader position across live reloads.'},
|
||
{id:'s2',title:'Fix memory archive undo behavior',project:'quiet-zero',source:'claude',when:'2 hr',created:'Today, 11:06',messages:42,duration:'19m',branch:'main',snippet:'The undo toast should restore the selection and its original list position.'},
|
||
{id:'s3',title:'Build benchmark corpus and evaluation notes',project:'obelisk-bench',source:'claude',when:'Yesterday',created:'Yesterday, 16:48',messages:113,duration:'1h 08m',branch:'research/benchmark',snippet:'Compare evidence recall across Claude Code and Codex histories.'},
|
||
{id:'s4',title:'Refactor the indexer writer lease',project:'quiet-zero',source:'codex',when:'Jul 18',created:'Jul 18, 09:14',messages:67,duration:'36m',branch:'codex/writer-lease',snippet:'Keep a single writer while readers continue to serve session detail.'},
|
||
{id:'s5',title:'Landing page icon direction',project:'obelisk-site',source:'claude',when:'Jul 16',created:'Jul 16, 20:21',messages:29,duration:'14m',branch:'design/icons',snippet:'Use the slab obelisk with a quiet aurora, not a generic sparkle mark.'},
|
||
{id:'s6',title:'Package the Obelisk skill artifact',project:'quiet-zero',source:'codex',when:'Jul 15',created:'Jul 15, 10:17',messages:54,duration:'31m',branch:'release/skill',snippet:'Publish readable references without bundling development-only files.'}
|
||
];
|
||
const memories=[
|
||
{id:'m1',project:'quiet-zero',path:'docs/decisions/session-reader-state.md',summary:'Reader state is keyed by session and restores expanded messages, focused item, and scroll anchor after live reload.',when:'12m ago',archived:false},
|
||
{id:'m2',project:'quiet-zero',path:'docs/decisions/evidence-before-assertion.md',summary:'Every interpretation in Obelisk must keep a visible path back to the raw session event that supports it.',when:'3h ago',archived:false},
|
||
{id:'m3',project:'obelisk-bench',path:'research/benchmark/retrieval-notes.md',summary:'Exact evidence recall improves when queries include both the project boundary and a concrete implementation noun.',when:'Yesterday',archived:false},
|
||
{id:'m4',project:'quiet-zero',path:'docs/decisions/two-tier-runtime.md',summary:'The CLI consumes precompiled core output; the desktop renderer communicates through a narrow IPC contract.',when:'Jul 17',archived:true},
|
||
{id:'m5',project:'obelisk-site',path:'brand/icon-direction.md',summary:'The approved mark combines a literal stone slab with a restrained violet and magenta aurora.',when:'Jul 12',archived:true},
|
||
{id:'m6',project:'quiet-zero',path:'docs/decisions/indexer-migration.md',summary:'Provider adapters emit normalized records through the shared parsing contract while the indexer owns persistence.',when:'Jul 8',archived:true}
|
||
];
|
||
const projects=[['quiet-zero',9],['obelisk-bench',3],['obelisk-site',2],['accio',2],['codex-pets',1],['docs-lab',1]];
|
||
const recaps=[
|
||
{id:'recap-2026-W25.json',period:'Jun 15 – 18',kind:'weekly',title:'The Architect',claim:'You widened the system from schema to UI while keeping every layer inspectable.',sessions:31,tokens:'3.2K',messages:'3.2K'},
|
||
{id:'recap-2026-W24.json',period:'Jun 9 – 15',kind:'weekly',title:'The Shipper',claim:'You moved a desktop app from a small prototype to something ready to package.',sessions:11,tokens:'1.4K',messages:'1.4K'},
|
||
{id:'recap-2026-06.json',period:'June 2026',range:'Jun 1 – 15',kind:'monthly',title:'The Architect',claim:'Gave every kind of knowledge a shell you can browse.',sessions:18,tokens:'7.9M',messages:'14.7K'}
|
||
];
|
||
const icons={sessions:'<path d="M3 4h10v8a1 1 0 01-1 1H4a1 1 0 01-1-1V4z"/><path d="M5.5 7h5M5.5 9.5h3"/>',memory:'<rect x="2.5" y="2.5" width="11" height="11" rx="2"/><path d="M5 5.5h6M5 8h6M5 10.5h4"/>',activity:'<rect x="2" y="10" width="2.5" height="4"/><rect x="6" y="6" width="2.5" height="8"/><rect x="10" y="3" width="2.5" height="11"/>',recap:'<path d="M3 2h10v12H3z"/><path d="M6 5h4M6 8h4M6 11h2"/>',folder:'<path d="M2 5.5V12a1.5 1.5 0 001.5 1.5h9A1.5 1.5 0 0014 12V6.5A1.5 1.5 0 0012.5 5H8.3L7 3.5H3.5A1.5 1.5 0 002 5z"/>',settings:'<path d="M3 4h10M3 8h10M3 12h10"/><circle cx="9.5" cy="4" r="1.7"/><circle cx="5.5" cy="8" r="1.7"/><circle cx="11" cy="12" r="1.7"/>'};
|
||
const svg=k=>`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${icons[k]}</svg>`;
|
||
const fileSvg=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round" stroke-linecap="round" aria-hidden="true"><path d="M3.5 2h6l3 3v9a1 1 0 01-1 1h-8a1 1 0 01-1-1V3a1 1 0 011-1z"/><path d="M9.5 2v3h3"/></svg>`;
|
||
const logo=`<svg class="brand-logo" viewBox="0 0 40 40" aria-hidden="true"><defs><radialGradient id="a"><stop stop-color="#ec4899" stop-opacity=".8"/><stop offset=".5" stop-color="#a855f7" stop-opacity=".7"/><stop offset="1" stop-color="#6366f1" stop-opacity="0"/></radialGradient><linearGradient id="s" x2="0" y2="1"><stop stop-color="#cbd5e1"/><stop offset="1" stop-color="#475569"/></linearGradient></defs><ellipse cx="20" cy="22" rx="15" ry="11" fill="url(#a)"/><polygon points="20,7 16.5,12 23.5,12" fill="url(#s)"/><polygon points="20,12 16.5,12 17.5,33 20,33" fill="url(#s)"/><polygon points="20,12 23.5,12 22.5,33 20,33" fill="#1e293b"/><rect x="15.5" y="33" width="9" height="1.6" rx=".3" fill="#0f172a"/></svg>`;
|
||
const recapSeals={
|
||
architect:`<svg viewBox="0 0 110 110" fill="none"><defs><radialGradient id="seal-a"><stop stop-color="#a78bfa" stop-opacity=".5"/><stop offset="1" stop-color="#a78bfa" stop-opacity="0"/></radialGradient></defs><circle cx="55" cy="55" r="50" fill="url(#seal-a)"/><circle cx="55" cy="55" r="42" stroke="rgba(255,255,255,.2)"/><circle cx="55" cy="55" r="38" stroke="#a78bfa" stroke-width="1.4"/><polygon points="55,28 50,38 60,38" fill="#c4b5fd"/><polygon points="50,38 60,38 58,76 52,76" fill="#a78bfa"/><polygon points="55,38 60,38 58,76 55,76" fill="#7c3aed" opacity=".75"/></svg>`,
|
||
shipper:`<svg viewBox="0 0 110 110" fill="none"><defs><radialGradient id="seal-s"><stop stop-color="#f472b6" stop-opacity=".5"/><stop offset="1" stop-color="#f472b6" stop-opacity="0"/></radialGradient></defs><circle cx="55" cy="55" r="50" fill="url(#seal-s)"/><circle cx="55" cy="55" r="42" stroke="rgba(255,255,255,.2)"/><circle cx="55" cy="55" r="38" stroke="#f472b6" stroke-width="1.4"/><rect x="32" y="48" width="14" height="14" rx="1.5" fill="#f472b6" opacity=".35"/><rect x="48" y="48" width="14" height="14" rx="1.5" fill="#f472b6" opacity=".65"/><rect x="64" y="48" width="14" height="14" rx="1.5" fill="#fda4af"/></svg>`
|
||
};
|
||
const S={page:'sessions',project:'all',memoryView:'active',q:'',desc:true,source:'all',sourceMenu:false,health:false,selected:new Set(),detail:null,activity:'daily',day:null,recapKind:'weekly',slide:0,recapPalette:0,auto:true,toastTimer:0,undo:null};
|
||
const $=s=>document.querySelector(s), esc=s=>String(s).replace(/\\n/g,'\n').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||
function label(p){return ['sessions','sessionDetail','subagentDetail'].includes(p)?'Sessions':p==='memory'||p==='memoryDetail'?'Memory':p==='activity'?'Activity':p==='recap'||p==='recapDetail'?'Recap':'Settings'}
|
||
function scope(){if(S.page==='sessionDetail')return`Sessions · ${sessions.find(x=>x.id===S.detail)?.title||''}`;if(S.page==='subagentDetail')return`Sessions · ${sessions.find(x=>x.id===(S.parentSession||'s1'))?.title||''}`;if(S.page==='memoryDetail')return`Memory · ${memories.find(x=>x.id===S.detail)?.path.split('/').pop()||''}`;if(S.page==='recapDetail')return`Recap · ${S.detail}`;if(S.page==='memory')return`Memory · ${S.memoryView==='archived'?'Archived':'Active'}${S.project!=='all'?` · ${S.project}`:''}`;return label(S.page)+(S.project!=='all'&&S.page==='sessions'?` · ${S.project}`:'')}
|
||
function sideItem(page,icon,text,badge,sub=''){const active=page==='sessions'?['sessions','sessionDetail','subagentDetail'].includes(S.page)&&S.project==='all':page==='memory'?['memory','memoryDetail'].includes(S.page)&&S.project==='all'&&S.memoryView===(sub||'active'):S.page===page;const click=page==='memory'?`A.memoryView('${sub||'active'}')`:`A.nav('${page}')`;return`<button class="side-item ${sub?'sub ':''}${active?'active':''}" onclick="${click}">${svg(icon)}<span class="label">${text}</span>${badge!==''?`<span class="badge">${badge}</span>`:''}</button>`}
|
||
function breadcrumb(){if(S.page==='sessionDetail'){const x=sessions.find(x=>x.id===S.detail);return`<button class="crumb" onclick="A.backSessions()">Sessions</button><span class="crumb-sep">/</span><span class="crumb current">${esc(x?.title||'')}</span>`}if(S.page==='subagentDetail'){const x=sessions.find(x=>x.id===S.parentSession)||sessions[0];return`<button class="crumb" onclick="A.backSessions()">Sessions</button><span class="crumb-sep">/</span><button class="crumb" onclick="A.backSessionDetail()">${esc(x.title)}</button><span class="crumb-sep">/</span><span class="crumb current">${esc(S.detail)}</span>`}if(S.page==='memoryDetail'){const x=memories.find(x=>x.id===S.detail);return`<button class="crumb" onclick="A.backMemory()">Memory</button><span class="crumb-sep">/</span><span class="crumb current" style="font-family:var(--mono)">${esc(x?.path.split('/').pop()||'')}</span>`}if(S.page==='recapDetail')return`<button class="crumb" onclick="A.backRecap()">Recap</button><span class="crumb-sep">/</span><span class="crumb current">${S.detail}</span>`;return S.project!=='all'&&['sessions','memory'].includes(S.page)?`<button class="crumb" onclick="A.project('all')">${label(S.page)}</button><span class="crumb-sep">/</span><span class="crumb current">${esc(S.project)}</span>`:`<span class="crumb current">${label(S.page)}</span>`}
|
||
function hi(text){if(!S.q)return esc(text);const q=esc(S.q).replace(/[.*+?^${}()|[\]\\]/g,'\\$&');return esc(text).replace(new RegExp(`(${q})`,'ig'),'<mark>$1</mark>')}
|
||
function visibleMemoryRows(){let rows=memories.filter(x=>x.archived===(S.memoryView==='archived')&&(S.project==='all'||x.project===S.project)&&(`${x.path} ${x.summary} ${x.project}`.toLowerCase().includes(S.q.toLowerCase())));return S.desc?rows:[...rows].reverse()}
|
||
function memoryList(){const rows=visibleMemoryRows(),check=`<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M2.5 6.5l2.5 2.5 4.5-5"/></svg>`,archived=`<span class="row-status" title="archived"><svg viewBox="0 0 14 14" fill="currentColor"><circle cx="7" cy="7" r="2.5"/></svg></span>`;return rows.length?`<div class="list">${rows.map(x=>`<article class="mrow ${S.cursor===x.id?'cursor ':''}${S.selected.has(x.id)?'selected':''}" data-id="${x.id}" onclick="A.openMemory(event,'${x.id}')"><button class="check ${S.selected.has(x.id)?'on':''}" aria-label="Select" onclick="event.stopPropagation();A.select('${x.id}',event.shiftKey)">${S.selected.has(x.id)?check:''}</button><div class="mrow-body"><div class="mrow-path">${x.archived?archived:''}<span class="mrow-project">${x.project} / </span>${hi(x.path.split('/').pop())}</div><div class="mrow-summary">${hi(x.summary)}</div></div><div class="mrow-right"><span>${x.when}</span><button class="row-action" onclick="event.stopPropagation();A.archive('${x.id}')">${x.archived?'Restore':'Archive'} <span class="kbd">D</span></button></div></article>`).join('')}</div>`:`<div class="empty"><b>No memories${S.memoryView==='archived'?' archived':''} here.</b><small>${S.q?'Try a different search term.':'Press / to search.'}</small></div>`}
|
||
S.showNoise=false;S.quietShown=false;S.debugEmpty=false;S.projectQ='';S.showSource=false;S.generateWindow='this-week';S.msgPos=1;S.months=1;S.fontSize=14;S.cursor=null;S.noiseLedger=false;S.traceOpen={};S.rawTools={};S.traceSeq=0;
|
||
const noiseProjects=[['od-conn-test',1],['4d924b',1],['7f8e91',1]];
|
||
function projectButtonsV2(){const memoryScope=S.page.startsWith('memory');const memoryProjects=[...new Map(memories.filter(m=>m.archived===(S.memoryView==='archived')).map(m=>[m.project,0])).keys()].map(name=>[name,memories.filter(m=>m.project===name&&m.archived===(S.memoryView==='archived')).length]);const base=memoryScope?memoryProjects:projects;const all=[...base,...(!memoryScope&&S.showNoise?noiseProjects:[])];return all.filter(x=>!S.projectQ||x[0].toLowerCase().includes(S.projectQ)).map(p=>`<button class="side-item ${S.project===p[0]?'active':''} ${noiseProjects.some(n=>n[0]===p[0])?'noise':''}" onclick="A.project('${p[0]}')">${svg('folder')}<span class="label">${p[0]}</span><span class="badge">${p[1]}</span></button>`).join('')}
|
||
function sidebarV2(){const showProjects=['sessions','sessionDetail','subagentDetail','memory','memoryDetail'].includes(S.page);const sessionsScope=['sessions','sessionDetail','subagentDetail'].includes(S.page);return`<aside class="sidebar"><div class="brand">${logo}<span class="brand-name">Obelisk</span><button class="health" aria-label="Connected sources" onclick="A.health()"><span class="dot claude"></span><span class="dot codex"></span></button>${S.health?`<div class="popover"><div class="pop-head">Connected sources</div><button class="source-row" onclick="A.sourceSettings()"><span class="dot claude"></span><span><span class="source-name">Claude Code <small style="color:var(--muted);font:10px var(--mono)">76 sessions</small></span><span class="source-meta">Connected</span></span></button><button class="source-row" onclick="A.sourceSettings()"><span class="dot codex"></span><span><span class="source-name">Codex <small style="color:var(--muted);font:10px var(--mono)">244 sessions</small></span><span class="source-meta">Connected</span></span></button><div class="pop-foot"><button onclick="A.manageSettings()">Manage in Settings →</button></div></div>`:''}</div><div class="side-section"><div class="section-title">Library</div>${sideItem('sessions','sessions','Sessions',326)}${sideItem('memory','memory','Memory',memories.length)}${sideItem('memory','memory','Active',memories.filter(x=>!x.archived).length,'active')}${sideItem('memory','memory','Archived',memories.filter(x=>x.archived).length,'archived')}</div><div class="side-section"><div class="section-title">Stats</div>${sideItem('activity','activity','Activity','')}${sideItem('recap','recap','Recap','')}</div>${showProjects?`<div class="side-section projects"><div class="section-title"><span>Projects</span>${sessionsScope?`<button class="show-all" onclick="A.noise()">${svg('sessions')} ${S.showNoise?'hide noise':'show all'}</button>`:''}</div>${sessionsScope?`<div class="project-search"><input aria-label="Filter projects" placeholder="Filter projects…" value="${esc(S.projectQ)}" oninput="A.projectSearchV2(this.value)"></div>`:''}<div class="project-list">${projectButtonsV2()}${sessionsScope&&!S.showNoise?`<button class="noise-fold" onclick="A.noise()"><span>›</span><span class="label">${noiseProjects.length} test projects hidden</span><span class="badge">${noiseProjects.length}</span></button>`:''}</div></div>`:'<div style="flex:1"></div>'}<div class="side-section bottom">${sideItem('settings','settings','Settings','')}</div></aside>`}
|
||
function filterCheckV2(on){return`<span class="filter-check">${on?`<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M2.5 6l2.2 2.2L9.5 3.5"/></svg>`:''}</span>`}
|
||
function toolbarV2(){const list=['sessions','memory'].includes(S.page),isRecap=S.page==='recap';return`<div class="toolbar"><div class="breadcrumb">${breadcrumb()}</div><div class="spacer"></div>${isRecap?`<div class="tabs"><button class="${S.recapKind==='weekly'?'active':''}" onclick="A.recapKind('weekly')">Weekly</button><button class="${S.recapKind==='monthly'?'active':''}" onclick="A.recapKind('monthly')">Monthly</button></div><button class="btn primary" onclick="A.generate()"><span style="font-size:14px;line-height:1;opacity:.8">+</span><span>Generate</span></button>`:''}${S.page==='sessions'?`<div class="source-filter-wrap"><button class="filter source-filter ${S.source!=='all'?'active':''}" onclick="A.sourceMenu()"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 3h8M3.5 6h5M5 9h2"/></svg>${S.source==='all'?'All sources':S.source==='claude'?'Claude Code':'Codex'}</button>${S.sourceMenu?`<div class="filter-menu">${['claude','codex','all'].map(x=>{const on=S.source==='all'||S.source===x;return`<button class="${on?'on':''}" onclick="A.source('${x}')">${filterCheckV2(on)}<span>${x==='all'?'All sources':x==='claude'?'Claude Code':'Codex'}</span></button>`}).join('')}</div>`:''}</div>`:''}${list?`<label class="search"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="7" cy="7" r="5"/><path d="M11 11l3 3"/></svg><input id="search" aria-label="Search" placeholder="Search…" value="${esc(S.q)}" oninput="A.search(this.value)"><span class="kbd">/</span></label><button class="sort ${S.desc?'desc':'asc'}" onclick="A.sort()"><span>${S.desc?'newest':'oldest'}</span><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M5 6l3-3 3 3" opacity="${S.desc?'.25':'1'}"/><path d="M5 10l3 3 3-3" opacity="${S.desc?'1':'.25'}"/></svg></button>`:''}</div>`}
|
||
function sessionNoDataV2(){return`<div class="empty-content"><div class="empty-eyebrow"><span class="diamond"></span><span>No data source connected</span></div><div class="empty-title">Obelisk reads your Claude Code session history.</div><div class="empty-body">We didn't find <code>~/.claude</code> on this machine. If you've already used Claude Code, point Obelisk at where its data lives in <button class="inline-link" onclick="A.nav('settings')">Settings</button>. If you haven't, <strong>install Claude Code first</strong> — Obelisk has nothing to read until sessions exist.</div><div class="empty-actions"><button class="toolbar-action" onclick="A.nav('settings')">${svg('folder')}Choose folder…</button></div><div class="empty-divider"></div><div class="empty-help"><div class="help-row"><span class="label">expected</span><code>~/.claude</code></div><div class="help-row"><span class="label">searched</span><code>~</code></div></div></div>`}
|
||
function quietSessionsV2(count){const times=['07/19 04:00','07/17 04:35','07/17 04:27','07/17 04:06','07/09 12:52','06/17 01:13','06/15 01:43','1970/01/01 08:00'];return`<button class="fold-banner ${S.quietShown?'expanded':''}" onclick="A.quiet()"><svg class="chev" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M4 2.5l3 3.5-3 3.5"/></svg><span class="body"><strong>${count}</strong> quiet sessions hidden — untitled, likely tests or incomplete runs.</span>${S.quietShown?'':`<span class="reveal-link">Show all</span>`}</button>${S.quietShown?`<div class="noise-group"><div class="noise-group-head">${count} sessions · untitled</div>${Array.from({length:count},(_,i)=>`<article class="session-row noise" onclick="A.openSession('s${i%6+1}')"><div><div class="session-row-title">(untitled)</div><div class="session-row-meta">${S.project==='all'?`<span class="project">${sessions[i%6].project}</span><span>·</span>`:''}<span>${[6,25,11,19,148,6,3,0][i]||0} msg</span></div></div><time class="session-row-time">${times[i]}</time></article>`).join('')}<button class="noise-fold-bottom" onclick="event.stopPropagation();A.quiet()"><svg class="chev" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 2.5l3 3.5-3 3.5"/></svg>Collapse</button></div>`:''}`}
|
||
function sessionListV2(){let rows=sessions.filter(x=>(S.project==='all'||x.project===S.project)&&(S.source==='all'||x.source===S.source)&&(`${x.title} ${x.project}`.toLowerCase().includes(S.q.toLowerCase())));if(S.debugEmpty||(!rows.length&&!S.q))return sessionNoDataV2();if(!S.desc)rows=[...rows].reverse();const times=['05:07','05:03','04:10','02:29','02:24','07/19 20:15'];const visible=rows.map((x,i)=>`<article class="session-row" tabindex="0" onclick="A.openSession('${x.id}')" onkeydown="if(event.key==='Enter')A.openSession('${x.id}')"><span class="session-obelisk" style="height:${Math.max(5,Math.min(31,Math.round(x.messages/4)))}px"></span><div><div class="session-row-title">${hi(x.title)}</div><div class="session-row-meta">${S.project==='all'?`<span class="project">${hi(x.project)}</span><span>·</span>`:''}<span>${x.messages} msg</span></div></div><time class="session-row-time">${times[i]||x.when}</time></article>`).join('');return visible?`<div class="list">${visible}${!S.q?quietSessionsV2(8):''}</div>`:`<div class="empty">No sessions here.<span class="hint">Try a different search term.</span></div>`}
|
||
/* Mirrors SessionTimelineRow.vue and session-timeline-presentation.mjs. */
|
||
const timelineChev=`<svg class="timeline-chevron" viewBox="0 0 8 8" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M2.5 1.5l3 2.5-3 2.5"/></svg>`;
|
||
const terminalIcon=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round"><rect x="2" y="3" width="12" height="10" rx="1.2"/><path d="M5 7l2 1.5-2 1.5M8.5 10.5h2.5"/></svg>`;
|
||
const readIcon=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round"><path d="M3.5 2h6l3 3v9a1 1 0 0 1-1 1h-8a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1z"/><path d="M9.5 2v3h3"/></svg>`;
|
||
const editIcon=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round"><path d="M3.5 2h6l3 3v9a1 1 0 0 1-1 1h-8a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1z"/><path d="M9.5 2v3h3"/><path d="M6 10l4-4M6.5 10.5l-1.2 1.4 1.4-1.2"/></svg>`;
|
||
function toolIconV3(name){return ['Bash','exec'].includes(name)?terminalIcon:name==='Read'?readIcon:['Edit','Write'].includes(name)?editIcon:''}
|
||
function disclosureOpenV3(key){return key in S.traceOpen?S.traceOpen[key]:false}
|
||
function thinkingV3(text,id='thinking',open=false){const key=`thinking:${id}`;return`<div class="msg-thinking trace-row ${disclosureOpenV3(key,open)?'open':''}" data-trace="${key}"><button class="thinking-toggle" onclick="A.disclose(this)">${timelineChev}<span class="thinking-label">Thinking</span></button><div class="thinking-body msg-text">${text}</div></div>`}
|
||
function execPrettyV3(source,result){const lines=source.split('\n');return`<div class="codeact-view" role="group" aria-label="CodeAct execution"><section class="codeact-section"><div class="codeact-section-head"><span class="codeact-section-label">Source</span></div><div class="codeact-code-frame"><pre class="codeact-gutter">${lines.map((_,i)=>i+1).join('\n')}</pre><pre class="codeact-code"><code>${esc(source)}</code></pre></div></section><section class="codeact-section"><div class="codeact-section-head"><span class="codeact-section-label">Result</span></div><div class="codeact-result"><pre class="codeact-result-block">${esc(result)}</pre></div><div class="codeact-note">Indexed output truncated. Open Raw to inspect the captured envelope.</div></section></div>`}
|
||
function readPrettyV3(output){const lines=output.split('\n'),collapsed=lines.length>12;return`<div class="file-content"><div class="file-content-head"><span class="label">File contents</span><span class="meta">${lines.length} lines</span></div><div class="file-content-body ${collapsed?'collapsed':''}"><div class="gutter">${lines.map((_,i)=>i+1).join('\n')}</div><div class="code">${esc(output)}</div></div>${collapsed?`<button class="file-content-expand" onclick="A.fileExpand(this)">Show all ${lines.length} lines</button>`:''}</div>`}
|
||
function diffPrettyV3(){return`<div class="diff-view"><div class="diff-view-head"><span class="label">Diff</span><div class="stats"><span class="stat-add">+2</span><span class="stat-del">−1</span></div></div><div class="diff-body"><div class="diff-gutter"> 1 1\n 2 \n 2\n 3</div><div><div class="diff-line context"> function restoreReaderState(state) {</div><div class="diff-line del">- viewport.scrollTop = state.top;</div><div class="diff-line add">+ restoreFocusedAnchor(state.anchor);</div><div class="diff-line add">+ restoreDisclosures(state.open);</div></div></div></div><div class="result-chip">Updated session-reader-state.mjs</div>`}
|
||
function terminalPrettyV3(command,output,error=false){return`<div class="terminal-view"><div class="terminal-prompt-line"><span class="prompt-marker">$</span><span class="prompt-cmd">${esc(command)}</span></div><div class="terminal-divider"></div><div class="terminal-output ${error?'is-error':''}">${esc(output)}</div></div>`}
|
||
function genericPrettyV3(){return`<div class="field-grid"><div class="field-key">query</div><div class="field-val"><span class="literal-string">"SessionTimelineRow"</span></div><div class="field-key">limit</div><div class="field-val"><span class="literal-num">20</span></div><div class="field-key">case_sensitive</div><div class="field-val"><span class="literal-bool">false</span></div></div><div class="result-chip">20 matches</div>`}
|
||
function prettyToolV3(t){return t.name==='exec'?execPrettyV3(t.source,t.output):t.name==='Read'?readPrettyV3(t.output):t.name==='Edit'?diffPrettyV3():t.name==='Bash'?terminalPrettyV3(t.arg,t.output,t.error):genericPrettyV3()}
|
||
function toolV3(t){const key=`tool:${t.id}`,open=disclosureOpenV3(key,t.open),raw=S.rawTools?.[key];return`<div class="msg-tool trace-row ${open?'open':''} ${raw?'raw':''} ${t.error?'is-error':''}" data-trace="${key}"><button class="toolcall-toggle" onclick="A.disclose(this)">${timelineChev}${toolIconV3(t.name)?`<span class="tool-icon">${toolIconV3(t.name)}</span>`:''}<span class="tool-name">${t.name}</span><span class="tool-arg">${esc(t.arg)}</span>${t.error?'<span class="tool-error">error</span>':''}</button><div class="toolcall-body"><div class="toolcall-body-strip"><span class="strip-label">${t.name}</span><span class="spacer"></span><button class="raw-toggle ${raw?'active':''}" onclick="A.rawTool(event,this)">{ } Raw</button></div><div class="toolcall-pretty">${prettyToolV3(t)}</div><div class="toolcall-raw"><div class="tc-section">Input</div><pre>${esc(t.input)}</pre><div class="tc-section">${t.error?'Error':'Output'}</div><pre>${esc(t.output||'(empty)')}</pre></div></div></div>`}
|
||
function messageV3(role,time,body,pos){const user=role==='You';return`<section class="msg ${user?'user':'assistant'}" data-pos="${pos}"><div class="msg-head"><span class="role">${role}</span><span class="when">${time}</span></div>${body}</section>`}
|
||
function agentToolV3(){const key='tool:agent-a6205b6278fc60f04',open=disclosureOpenV3(key);return`<div class="msg-tool agent-call trace-row ${open?'open':''}" data-trace="${key}"><div class="agent-call-head"><button class="toolcall-toggle" onclick="A.disclose(this)">${timelineChev}<span class="tool-name">general-purpose</span><span class="tool-arg">Audit the mini-app against every UI state</span></button><button class="agent-nav-btn" onclick="event.stopPropagation();A.openSubagent('agent-a6205b6278fc60f04')">View conversation →</button></div><div class="toolcall-body" style="padding:10px 12px"><div class="tc-section">Prompt</div><div class="agent-prompt">Compare the existing UI page tree and report every mismatch.</div><div class="tc-section">Result</div><div class="agent-result">The subagent found a missing route and supplied interaction evidence.</div></div></div>`}
|
||
function skillV3(){const key='skill:session-ui',open=disclosureOpenV3(key);return`<section class="skill-card trace-row ${open?'open':''}" data-pos="6" data-trace="${key}"><div class="skill-card-icon">${readIcon}</div><div class="skill-card-body"><div class="skill-card-header"><span class="skill-card-badge">Skill</span><span class="skill-card-name">computer-use</span></div><div class="skill-card-args">Compare the installed Obelisk app and the mini-app state by state.</div><div class="skill-card-md"><button class="skill-md-toggle" onclick="A.disclose(this)">${timelineChev}<span>SKILL.md</span></button><div class="skill-md-body msg-text"><p>Use Computer Use for app interactions that are not exposed through a more specific interface.</p></div></div></div></section>`}
|
||
function metaV3(){const key='meta:system-demo',open=disclosureOpenV3(key);return`<section class="msg meta" data-pos="7"><div class="msg-meta-collapsed trace-row ${open?'open':''}" data-trace="${key}"><button class="meta-toggle" onclick="A.disclose(this)">${timelineChev}<span class="meta-label">System</span><span class="meta-preview">Keep evidence separate from inferred presentation state.</span></button><div class="meta-body msg-text">Keep evidence separate from inferred presentation state. Use the installed application as the visual source of truth.</div></div></section>`}
|
||
function summaryV3(){const key='summary:session-demo',open=disclosureOpenV3(key);return`<div class="msg-summary trace-row ${open?'open':''}" data-trace="${key}"><button class="summary-toggle" onclick="A.disclose(this)">${timelineChev}<span class="summary-label">Session summary</span><span class="summary-source">compaction</span></button><div class="summary-body msg-text"><p>The reader now restores its focused anchor and disclosure state after live updates.</p></div></div>`}
|
||
function workflowV3(){return`<section class="wf-card" data-pos="9"><div class="wf-card-header"><span>⚙</span><span class="wf-card-name">UI fidelity audit</span><span class="wf-card-count">2 agents</span><span class="wf-card-status">completed</span></div><div class="wf-card-body"><div class="wf-card-phase-title">Compare</div><button class="wf-card-agent" onclick="A.openSubagent('agent-a6205b6278fc60f04')"><span class="wf-card-agent-label">Session timeline component audit</span><span class="wf-card-agent-arrow">→</span></button><button class="wf-card-agent" onclick="A.openSubagent('agent-visual-check')"><span class="wf-card-agent-label">Computer Use visual verification</span><span class="wf-card-agent-arrow">→</span></button></div></section>`}
|
||
function sessionDetailV3(){const x=sessions.find(x=>x.id===S.detail)||sessions[0],total=9,readOut=`export function captureReaderState(viewport) {\n return { anchor: viewport.focusedItem, open: viewport.disclosures };\n}\n\nexport function restoreReaderState(state) {\n restoreFocusedAnchor(state.anchor);\n restoreDisclosures(state.open);\n}`,execSource=`const r = await tools.exec_command({\n cmd: "sed -n '1,240p' app/src/renderer/src/components/SessionTimelineRow.vue"\n});\ntext(r.output);`;return`<div class="session-reader" style="--reader-font:${S.fontSize}px"><div class="session-progress"><div class="session-progress-fill" style="width:${Math.round(S.msgPos/total*100)}%"></div></div><header class="session-header"><div class="session-eyebrow">${svg('folder')}<span class="project-name">${x.project}</span><span class="sep">·</span><span class="project-path">/Users/tomiya/Code/${x.project}</span><span class="via"><span class="via-dot ${x.source}"></span>via ${x.source==='codex'?'Codex':'Claude Code'}</span></div><div class="session-title">${esc(x.title)}</div><div class="session-meta-line"><span>created 2h ago</span><span class="meta-dot"></span><span>last active 55m ago</span><span class="meta-dot"></span><span>${x.messages} messages</span><span class="meta-dot"></span><span>${x.branch||'main'}</span></div></header><div class="session-timeline">${messageV3('You','02:55',`<div class="msg-text">修改 mini-app,使其忠实复刻 app 的 UI / UX 行为;尤其注意 UI 部件、字体样式和文字内容。</div>`,1)}${messageV3('Assistant','02:55',thinkingV3('<h3>Inspecting the component tree</h3><p>I need to map the real timeline branches before changing the miniature.</p>','inspect')+`<div class="msg-text"><p>我会先对照 <code>SessionTimelineRow.vue</code> 和 tool renderer,再使用 Computer Use 验收。</p></div><div class="msg-tools">`+toolV3({id:'exec-source',name:'exec',arg:execSource,input:JSON.stringify(execSource),source:execSource,output:'---\\nSessionTimelineRow\\nTool presenter branches\\n...',open:true})+`</div>`,2)}${messageV3('Assistant','02:56',`<div class="msg-text"><p>普通工具需要保留独立的 Pretty 和 Raw 展示。</p></div><div class="msg-tools">`+toolV3({id:'read-state',name:'Read',arg:'/app/src/renderer/src/session-reader-state.mjs',input:'{\\n \"file_path\": \"/app/src/renderer/src/session-reader-state.mjs\"\\n}',output:readOut})+toolV3({id:'edit-state',name:'Edit',arg:'/app/src/renderer/src/session-reader-state.mjs',input:'{\\n \"old_string\": \"viewport.scrollTop = state.top\",\\n \"new_string\": \"restoreFocusedAnchor(state.anchor)\"\\n}',output:'Updated session-reader-state.mjs'})+`</div>`,3)}${messageV3('Assistant','02:57',thinkingV3('<p>The viewport follows new events only when the reader is already at the live tail.</p>','tail')+`<div class="msg-tools">`+toolV3({id:'bash-tests',name:'Bash',arg:'npm test -- session-reader-state',input:'{\\n \"command\": \"npm test -- session-reader-state\"\\n}',output:'✓ restores focused item\\n✓ preserves expanded messages\\n✓ follows new events only at live tail'})+toolV3({id:'edit-error',name:'Edit',arg:'/app/obelisk-ui-mini.html',input:'{\\n \"old_string\": \"trace-row\",\\n \"new_string\": \"msg-tool\"\\n}',output:'File has been modified since read. Read it again before attempting to write.',error:true})+`</div>`,4)}${messageV3('Assistant','02:58',`<div class="msg-text"><p>The Agent/Task branch exposes the child conversation directly from the tool header.</p></div><div class="msg-tools">`+agentToolV3()+`</div>`,5)}${skillV3()}${metaV3()}${messageV3('Assistant','03:01',`<div class="msg-text"><p>Implemented source-shaped disclosures and preserved their open state.</p></div>`+summaryV3(),8)}${workflowV3()}</div><div class="msg-nav"><button ${S.msgPos===1?'disabled':''} onclick="A.msgNav('first')" title="First">⇤</button><button ${S.msgPos===1?'disabled':''} onclick="A.msgNav('prev')" title="Previous">‹</button><span class="msg-pos">${S.msgPos} / ${total}</span><button ${S.msgPos===total?'disabled':''} onclick="A.msgNav('next')" title="Next">›</button><button ${S.msgPos===total?'disabled':''} onclick="A.msgNav('last')" title="Last">⇥</button></div></div>`}
|
||
const subChev=`<svg class="sub-chevron" viewBox="0 0 8 8" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M2.5 1.5l3 2.5-3 2.5"/></svg>`;
|
||
function subTool(name,arg,input,output,error=false){return`<div class="sub-disclosure sub-tool ${error?'is-error ':''}"><button class="sub-toggle" onclick="A.subToggle(this)">${subChev}<span class="sub-tool-name">${esc(name)}</span><span class="sub-tool-arg">${esc(arg)}</span>${error?'<span class="sub-error">error</span>':''}</button><div class="sub-body"><div class="tc-section">Input</div><pre>${esc(input)}</pre><div class="tc-section">${error?'Error':'Output'}</div><pre>${esc(output||'(empty)')}</pre></div></div>`}
|
||
function subagentDetailV2(){const id=S.detail||'agent-a6205b6278fc60f04';return`<div class="subagent-wrap"><div class="subagent-wide"><header class="subagent-header"><div class="subagent-eyebrow">Subagent</div><div class="subagent-title">${esc(id)}</div><div class="subagent-meta">8 messages</div></header><div class="sub-timeline"><section class="sub-msg user"><div class="sub-head"><span class="sub-role">Prompt</span><time class="sub-when">11:14</time></div><div class="markdown-msg"><p>Audit the current mini-app against the installed Obelisk app. Preserve the real information hierarchy and interaction semantics.</p><h2>What to verify</h2><ul><li>Traverse every route and representative state.</li><li>Match visible copy, punctuation, icons, and typography.</li><li>Keep the result in <code>obelisk-ui-mini.html</code>.</li></ul></div></section><section class="sub-msg"><div class="sub-head"><span class="sub-role">Assistant</span><time class="sub-when">11:14</time></div><div class="markdown-msg"><p>I’ll start by reading the real renderer and opening each reachable branch in the installed app.</p></div>${subTool('Read','/app/src/renderer/src/App.vue','{"file_path":"/app/src/renderer/src/App.vue"}','<template>\n <RouterView />\n</template>')}</section><section class="sub-msg"><div class="sub-disclosure"><button class="sub-toggle" onclick="A.subToggle(this)">${subChev}<span class="sub-label">Thinking</span></button><div class="sub-body markdown-msg"><p>The visible route tree includes a child conversation that the miniature does not currently represent.</p></div></div></section><section class="sub-msg"><div class="sub-disclosure sub-meta"><button class="sub-toggle" onclick="A.subToggle(this)">${subChev}<span class="sub-label">System</span><span class="sub-preview">Keep evidence separate from inferred presentation state.</span></button><div class="sub-body markdown-msg"><p>Keep evidence separate from inferred presentation state. Use the installed application as the visual source of truth.</p></div></div></section><section class="sub-msg"><div class="sub-head"><span class="sub-role">Assistant</span><time class="sub-when">11:18</time></div><div class="markdown-msg"><p>Now I’ll compare the message components and their disclosure states.</p></div>${subTool('Bash','node --check mini-inline.js','{"command":"node --check mini-inline.js"}','Syntax OK',false,true)}</section><section class="sub-msg"><div class="sub-head"><span class="sub-role">Assistant</span><time class="sub-when">11:19</time></div><div class="markdown-msg"><p>The first edit collided with a newer file version, so I re-read before applying it again.</p></div>${subTool('Edit','/app/obelisk-ui-mini.html','{"old_string":"sessionDetail","new_string":"subagentDetail"}','File has been modified since read. Read it again before attempting to write.',true)}</section><section class="sub-msg"><div class="sub-head"><span class="sub-role">Assistant</span><time class="sub-when">11:20</time></div><div class="markdown-msg sub-message-body"><p>The comparison found the missing Subagent route, its three-part breadcrumb, the wider reader, Prompt and Assistant role labels, and simplified tool disclosures…</p></div><button class="truncated-btn" onclick="A.subFull(this)">Message truncated — click to load full text</button></section><section class="sub-msg"><div class="sub-head"><span class="sub-role">Assistant</span><time class="sub-when">11:21</time></div><div class="markdown-msg empty-text">(no text content)</div></section></div></div></div>`}
|
||
function memoryDetailV3(){const x=memories.find(x=>x.id===S.detail)||memories[0],relative=x.path.split('/').slice(-3).join('/');return`<div class="memory-reader"><div class="memory-project">${svg('folder')}<strong>${x.project}</strong>${x.archived?'<span class="archived-tag">archived</span>':''}</div><div class="memory-path">${esc(relative)}</div><div class="memory-summary">${esc(x.summary)}</div><div class="memory-meta"><button onclick="A.openSession('s1')">${svg('sessions')}<span>Design the Obelisk session reader</span></button><span>·</span><span>${x.when}</span><span>·</span><span>codex:01…→ codex:01…</span></div><div class="memory-body-head"><span>Body</span><button class="source-toggle-v2 ${S.showSource?'active':''}" onclick="A.toggleSource()">${S.showSource?'Show rendered':'Show source'}</button></div>${S.showSource?`<pre class="memory-source"># Reader state and evidence\n\n## Decision\nReader state is keyed by session and preserves the focused evidence anchor.\n\n## Constraints\n- Expanded messages remain open.\n- Follow the tail only when already live.</pre>`:`<article class="memory-article"><h1>Reader state and evidence</h1><h2>Decision</h2><p>Reader state is keyed by session and survives live reload without losing the visible evidence anchor.</p><p>The reading surface must remain stable. Incoming events can inform the reader, but they must not steal position unless the reader explicitly follows the live tail.</p><h2>Initial implementation slice</h2><ol><li>Capture the focused timeline item and its offset.</li><li>Restore expanded disclosures before restoring scroll.</li><li>Advance only while the viewport is already at the tail.</li></ol><h2>Scope constraints</h2><p>Observed session events remain distinct from inferred presentation state.</p></article>`}<div class="anchor-head"><span>Anchors</span><span>3</span></div><button class="anchor-v2">${fileSvg} session-reader-state.mjs</button><button class="anchor-v2">${fileSvg} session-live-reload.mjs</button><button class="anchor-v2">${fileSvg} session-reader-state.test.mjs</button><div class="memory-actions"><button class="btn" onclick="A.backMemory()">Back <span class="kbd">Esc</span></button><button class="btn ${x.archived?'primary':'danger'}" onclick="A.archive('${x.id}')">${x.archived?'Restore':'Archive'} <span class="kbd">D</span></button></div></div>`}
|
||
|
||
function activityDate(i){return new Date(2025,6,20+i)}
|
||
function activityLevel(i){const col=Math.floor(i/7);if(col<38)return 0;let v=Math.imul(i+1,-1640531527);v^=v>>>16;v=Math.imul(v,-2048144789);v^=v>>>13;v=(v>>>0)%17;return v<10?0:Math.min(4,1+Math.floor((v-10)/2))}
|
||
function ordinal(n){const m=n%100;return n+(m>=11&&m<=13?'th':n%10===1?'st':n%10===2?'nd':n%10===3?'rd':'th')}
|
||
function activityHeatSvg(){const cells=Array.from({length:371},(_,i)=>{const col=Math.floor(i/7),row=i%7,level=activityLevel(i),d=activityDate(i),selected=S.day===i?' selected':'',tip=`${level?level*24+'M':'0'} tokens on ${d.toLocaleString('en',{month:'long'})} ${ordinal(d.getDate())}, ${d.getFullYear()}`;return`<rect class="heat-cell level-${level}${selected}" x="${col*13}" y="${row*13}" width="11" height="11" rx="2" onclick="A.day(${i})" onmouseenter="A.chartTip(event,'${tip}')" onmousemove="A.chartTip(event,'${tip}')" onmouseleave="A.chartTipOff()"/>`}).join('');const labels=['Jul','Aug','Sep','Oct','Nov','Dec','Jan','Feb','Mar','Apr','May','Jun','Jul'].map((m,i)=>`<text class="heat-month" x="${i*56}" y="105">${m}</text>`).join('');return`<svg viewBox="0 0 709 112" role="img" aria-label="Daily token activity">${cells}${labels}</svg><div class="heat-legend"><span>Less</span><svg width="70" height="11">${[0,1,2,3,4].map((l,i)=>`<rect x="${i*14}" width="11" height="11" rx="2" class="heat-cell level-${l}"/>`).join('')}</svg><span>More</span></div>`}
|
||
function activityChartV2(){if(S.activity==='daily')return activityHeatSvg();if(S.activity==='weekly'){const bars=Array.from({length:53},(_,i)=>{const h=3+((i*17)%105),tip=`Week ${i+1}: ${h*3}M tokens`;return`<rect class="weekly-bar" x="${i*13}" y="${120-h}" width="10" height="${h}" rx="2" onmouseenter="A.chartTip(event,'${tip}')" onmousemove="A.chartTip(event,'${tip}')" onmouseleave="A.chartTipOff()"/>`}).join('');return`<svg viewBox="0 0 709 144" aria-label="Weekly token activity">${bars}<text class="heat-month" x="0" y="138">Jul</text><text class="heat-month" x="170" y="138">Oct</text><text class="heat-month" x="340" y="138">Jan</text><text class="heat-month" x="510" y="138">Apr</text><text class="heat-month" x="675" y="138">Jul</text></svg>`}const dots=[[0,140],[90,137],[210,112],[340,86],[420,72],[560,42],[700,8]].map((p,i)=>`<circle class="cumulative-dot" cx="${p[0]}" cy="${p[1]}" r="6" onmouseenter="A.chartTip(event,'${(i+1)*1.6}B cumulative tokens')" onmousemove="A.chartTip(event,'${(i+1)*1.6}B cumulative tokens')" onmouseleave="A.chartTipOff()"/>`).join('');return`<svg viewBox="0 0 700 164" aria-label="Cumulative token activity"><path class="cumulative-area" d="M0 140 C90 137 140 125 210 112 S340 86 420 72 S560 42 700 8 L700 140 L0 140Z"/><path class="cumulative-line" d="M0 140 C90 137 140 125 210 112 S340 86 420 72 S560 42 700 8"/>${dots}<text class="heat-month" x="0" y="158">Jul</text><text class="heat-month" x="175" y="158">Oct</text><text class="heat-month" x="350" y="158">Jan</text><text class="heat-month" x="525" y="158">Apr</text><text class="heat-month" x="680" y="158">Jul</text></svg>`}
|
||
const ledgerRows=[['Created 3 new workspaces','workspace',[['Prototype the evidence reader','Codex','quiet-zero','86 msg'],['Benchmark local retrieval','Claude Code','obelisk-bench','113 msg']]],['Started 5 sessions in 3 projects','started',[['Design the Obelisk session reader','Codex','quiet-zero','86 msg'],['Fix memory archive undo behavior','Claude Code','quiet-zero','42 msg'],['Landing page icon direction','Claude Code','obelisk-site','29 msg']]],['Continued 2 sessions','continued',[['Refactor the indexer writer lease','Codex','quiet-zero','67 msg'],['Package the Obelisk skill artifact','Codex','quiet-zero','54 msg']]]];
|
||
function activityLedgerV2(){const d=S.day===null?null:activityDate(S.day),eventDate=d?`${d.toLocaleString('en',{month:'short'}).toUpperCase()} ${d.getDate()}`:'';return`<div class="activity-ledger">${ledgerRows.map((g,gi)=>`<article class="ledger-group ${g[1]}"><div class="ledger-node">${g[1]==='workspace'?svg('folder'):g[1]==='started'?svg('sessions'):'↻'}</div><header class="ledger-group-head"><h3>${g[0]}</h3>${d?`<time>${eventDate}</time>`:''}</header><div class="ledger-items">${g[2].map((r,i)=>`<button class="ledger-item ${g[1]}" onclick="A.openSession('s${(i%6)+1}')"><span class="ledger-item-title">${r[0]}</span><span class="ledger-item-meta"><span class="source">${r[1]}</span><span>·</span>${g[1]!=='workspace'?`<span class="project">${r[2]}</span><span>·</span>`:''}<span>${r[3]}</span></span></button>`).join('')}${gi===1?`<button class="ledger-noise ${S.noiseLedger?'expanded':''}" onclick="A.ledgerNoise()"><svg class="chev" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 2.5l3 3.5-3 3.5"/></svg><span>3 hidden, likely test or throwaway runs</span></button>${S.noiseLedger?`<button class="ledger-item noise"><span class="ledger-item-title">Untitled test run</span><span class="ledger-item-meta">Codex · 3 msg</span></button>`:''}`:''}</div></article>`).join('')}</div>`}
|
||
function activityV2(){let months;if(S.day!==null){const d=activityDate(S.day),empty=activityLevel(S.day)===0,count=empty?0:5,eventDate=`${d.toLocaleString('en',{month:'short'}).toUpperCase()} ${d.getDate()}`;months=`<section class="activity-month"><div class="activity-month-head"><h2>${d.toLocaleString('en',{month:'long'})} ${d.getFullYear()}</h2><span class="activity-month-rule"></span><span class="activity-month-count">${count} session${count===1?'':'s'}</span></div>${empty?`<div class="activity-empty">No sessions on ${eventDate}.</div>`:activityLedgerV2()}</section>`}else months=Array.from({length:S.months},(_,i)=>`<section class="activity-month"><div class="activity-month-head"><h2>${i?'June':'July'} 2026</h2><span class="activity-month-rule"></span><span class="activity-month-count">${i?18:10} sessions</span></div>${activityLedgerV2()}</section>`).join('');return`<div class="activity-wrap"><div class="activity-wide"><div class="activity-header"><span class="activity-title">Token activity</span><div class="activity-tabs">${['daily','weekly','cumulative'].map(x=>`<button class="activity-tab ${S.activity===x?'active':''}" onclick="A.activity('${x}')">${x[0].toUpperCase()+x.slice(1)}</button>`).join('')}</div></div><div class="activity-stats"><div class="activity-stat"><span class="activity-stat-value">10.35B</span><span class="activity-stat-label">Lifetime tokens</span></div><div class="activity-stat"><span class="activity-stat-value">679.1M</span><span class="activity-stat-label">Peak tokens</span></div><div class="activity-stat"><span class="activity-stat-value">16h 5m 27s</span><span class="activity-stat-label">Longest task</span></div><div class="activity-stat"><span class="activity-stat-value">14d</span><span class="activity-stat-label">Current streak</span></div><div class="activity-stat"><span class="activity-stat-value">47d</span><span class="activity-stat-label">Longest streak</span></div></div><div class="activity-chart">${activityChartV2()}</div>${months}${S.day===null?`<button class="show-more-activity" onclick="A.moreActivity()">Show more activity</button>`:''}<div id="chartTip" class="chart-tooltip"></div></div></div>`}
|
||
|
||
function recapListV2(){const list=recaps.filter(x=>x.kind===S.recapKind);return`<div class="recap-list-v2"><div class="rl-content"><div class="rl-head"><span class="rl-year">2026</span><span class="rl-count">${list.length} ${list.length===1?'recap':'recaps'}</span></div><div class="rl-timeline">${list.map((x,i)=>{const arch=i?'shipper':'architect',glow=arch==='shipper'?'rgba(244,114,182,.45)':'rgba(167,139,250,.45)',week=x.id.match(/W(\d+)/)?.[1];return`<article class="rl-row" style="--node-glow:${glow}" onclick="A.openRecap('${x.id}')"><div class="rl-node">${recapSeals[arch]}</div><div class="rl-card"><div class="rl-body"><div class="rl-period"><span>${x.kind==='weekly'?'Week '+week:x.period}</span><span class="dot"></span><span>${x.kind==='weekly'?x.period:x.range}</span></div><div class="rl-archetype">${esc(x.title)}</div><div class="rl-claim">${esc(x.claim)}</div><div class="rl-stats"><span>${x.sessions} sessions</span><span>·</span><span>${x.tokens} tokens</span></div></div><span class="rl-arrow">›</span></div></article>`}).join('')}</div></div></div>`}
|
||
function recapCardV2(x){
|
||
const star=`<div class="rc-stars"><span></span><span></span><span></span><span></span><span></span></div>`,week=x.id.match(/W(\d+)/)?.[1],active=x.kind==='weekly'?4:14;
|
||
if(S.slide===0)return`<article class="rc-card rc-cover">${star}<div class="rc-eyebrow"><span class="diamond"></span><span>${x.kind==='weekly'?'Week '+week:x.period}</span></div><div class="rc-seal">${S.recapPalette===2?recapSeals.shipper:recapSeals.architect}</div><div class="rc-cover-body"><div class="rc-cover-title">${esc(x.title)}</div><div class="rc-cover-claim">${esc(x.claim)}</div><div class="rc-activity"><div class="rc-activity-bars">${[.15,.22,.84,.3,0,.5,.9].map(v=>`<span class="rc-activity-bar">${v?`<i style="height:${v*100}%"></i>`:''}</span>`).join('')}</div><div class="rc-day-labels"><span>M</span><span>T</span><span>W</span><span>T</span><span>F</span><span>S</span><span>S</span></div></div><div class="rc-footer">${x.sessions} sessions · ${x.messages} messages · ${active} active days</div></div></article>`;
|
||
if(S.slide===1)return`<article class="rc-card themed"><div class="rc-eyebrow"><span class="diamond"></span><span>Your thinking path</span><span class="slot">02 · 05</span></div><div class="rc-title">Five turns, one system wider.</div><div class="rc-content"><div class="rc-path">${[['Mon','“Can the watcher stay narrow?”','Yes, but only around real session roots.'],['Tue','“The old database crashes on open.”','The migration chain was missing, not the query.'],['Wed','“Will live messages steal the reader position?”','Only follow when already at the live tail.'],['Thu','“Can the evidence remain inspectable?”','Keep presentation state separate from observed events.'],['Fri','“Can the prototype stay disposable?”','Keep every interaction, remove every dependency.']].map(r=>`<div class="rc-path-item"><div class="rc-day">${r[0]}</div><div class="rc-prompt">${r[1]}</div><div class="rc-outcome">${r[2]}</div></div>`).join('')}</div></div></article>`;
|
||
if(S.slide===2)return`<article class="rc-card themed"><div class="rc-eyebrow"><span class="diamond"></span><span>Your vibe this week</span><span class="slot">03 · 05</span></div><div class="rc-title">Builder with doubts, building anyway.</div><div class="rc-content"><div class="rc-section-label">Things you kept saying</div><div class="rc-vibe-list"><div class="rc-vibe-row"><span class="rc-vibe-text">“Keep the current evidence visible.”</span><span class="rc-vibe-meta">×3 · exacting</span></div><div class="rc-vibe-row"><span class="rc-vibe-text">“Don’t invent UI that isn’t there.”</span><span class="rc-vibe-meta">pragmatist</span></div><div class="rc-vibe-row"><span class="rc-vibe-text">“Use the actual app as the reference.”</span><span class="rc-vibe-meta">questioning</span></div><div class="rc-vibe-row"><span class="rc-vibe-text">“…still comparing at 4 AM”</span><span class="rc-vibe-meta">night owl · 04:00</span></div></div><div class="rc-meter"><div class="rc-meter-track"><div class="rc-meter-fill"></div></div><div class="rc-meter-row"><span>conviction</span><span>quiet resolve</span></div></div><div class="rc-quote">The UI is evidence too.<div style="margin-top:8px;color:var(--muted);font:italic 13px var(--serif)">— the reason you kept comparing</div></div></div></article>`;
|
||
if(S.slide===3)return`<article class="rc-card themed"><div class="rc-eyebrow"><span class="diamond"></span><span>Workflows</span><span class="slot">04 · 05</span></div><div class="rc-title">Three focused implementation loops.</div><div class="rc-deck-text">You compared, corrected, and verified without replacing the source of truth.</div><div class="rc-content" style="display:flex;flex-direction:column"><div class="rc-workflow-stat">3 workflows · 9 focused checks</div><div class="rc-workflow-list"><div class="rc-workflow-row"><b>Source inspection</b><span>“Map the real component before changing the mock.”</span></div><div class="rc-workflow-row"><b>Computer Use comparison</b><span>“Verify each visible state in the installed app.”</span></div><div class="rc-workflow-row"><b>Regression pass</b><span>“Walk every branch again after the fix.”</span></div></div><div class="rc-verdict"><small>Verdict —</small><strong>Evidence-led week.</strong></div></div></article>`;
|
||
return`<article class="rc-card rc-closing"><div class="rc-eyebrow"><span class="diamond"></span><span>The week, carved.</span><span class="slot">05 · 05</span></div><div class="rc-closing-body"><div class="rc-closing-title">${active} active days</div><div class="rc-closing-stats"><span>7 projects touched</span><span>8 decisions verified</span></div><div class="rc-closing-quote">“Compare it with the actual app.”<small>— most-said phrase</small></div><div class="rc-signoff">See you next week.</div></div></article>`
|
||
}
|
||
function recapDetailV2(){const x=recaps.find(x=>x.id===S.detail)||recaps[0],labels=['Cover','Path','Vibe','Workflow','Closing'];return`<div class="recap-app-v2 p${S.recapPalette}" tabindex="0"><div class="rc-stage"><div class="rc-deck">${recapCardV2(x)}</div></div><div class="rc-nav"><button class="rc-arrow" ${S.slide===0?'disabled':''} onclick="A.slide(-1)" aria-label="Previous card">‹</button><div class="rc-dots">${labels.map((l,i)=>`<button class="rc-dot ${S.slide===i?'active':''}" onclick="A.goSlide(${i})"><span class="rc-glyph"></span><span class="rc-label">${l}</span></button>`).join('')}</div><button class="rc-arrow" ${S.slide===4?'disabled':''} onclick="A.slide(1)" aria-label="Next card">›</button><div class="rc-actions"><button class="rc-action" title="Copy image" onclick="A.copyRecap()"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="5" y="5" width="9" height="9" rx="1.5"/><path d="M5 11H3.5A1.5 1.5 0 012 9.5v-7A1.5 1.5 0 013.5 1h7A1.5 1.5 0 0112 2.5V5"/></svg></button><button class="rc-action" title="Export PNG" onclick="A.exportRecap()"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 2v8M5 7l3 3 3-3M3 12v1.5a1 1 0 001 1h8a1 1 0 001-1V12"/></svg></button></div></div></div>`}
|
||
function settingsV2(){const sourceData=[['claude','Claude Code','Anthropic','/Users/tomiya/.claude','78','3h ago'],['codex','Codex','OpenAI','/Users/tomiya/.codex','248','1h ago']];return`<div class="settings-v2"> <section><div class="section-head"><h2>Data Sources</h2><p>Where Obelisk reads your agent session history.</p></div>${sourceData.map(x=>`<div class="source-card"><div class="source-card-head"><div class="source-mark"><span class="dot ${x[0]}"></span></div><div class="source-info"><div class="source-card-name">${x[1]} <span class="vendor">by ${x[2]}</span></div><div class="source-status"><span class="status-dot-v2"></span><span class="connected">Connected</span><span>·</span><span>last read <strong>${x[5]}</strong></span><span>·</span><span><strong>${x[4]}</strong> sessions</span></div></div></div><div class="path"><input value="${x[3]}" readonly><button class="btn">${svg('folder')}Browse…</button></div></div>`).join('')}</section><section><div class="section-head"><h2>Index location</h2><p>SQLite database where Obelisk caches the unified session index.</p></div><div class="path" style="max-width:480px"><input value="/Users/tomiya/.obelisk/obelisk.sqlite" readonly><button class="btn">Reveal</button></div></section><section><div class="section-head"><h2>Auto-refresh</h2><p>Obelisk re-reads when new session files appear.</p></div><button class="toggle" onclick="A.auto()"><span class="track ${S.auto?'on':''}"></span>Watch data sources for changes</button></section><section><div class="section-head"><h2>Recap</h2><p>Where generated weekly and monthly recap files live.</p></div><div class="setting-row"><div><div class="setting-label">Recap output directory</div><div class="setting-hint">Watched by Obelisk for new <code>recap-*.json</code> files.</div></div><div class="path"><input value="/Users/tomiya/.obelisk/recap"><button class="btn">Browse…</button></div></div></section><section><div class="section-head"><h2>About</h2><p>The kind of details you don't usually need.</p></div><div class="setting-row"><span class="setting-label">Version</span><span class="version-text">Obelisk 0.1.0</span></div><div class="setting-row"><span class="setting-label">Reset</span><div><button id="rebuild" class="btn" onclick="A.rebuild()">Rebuild index</button><div class="reset-hint">Rebuilding re-reads your coding agent session data. It does not delete memories or recaps.</div></div></div></section></div>`}
|
||
function content(){return S.page==='sessions'?sessionListV2():S.page==='memory'?memoryList():S.page==='sessionDetail'?sessionDetailV3():S.page==='subagentDetail'?subagentDetailV2():S.page==='memoryDetail'?memoryDetailV3():S.page==='activity'?activityV2():S.page==='recap'?recapListV2():S.page==='recapDetail'?recapDetailV2():settingsV2()}
|
||
function render(){document.title=`Obelisk — ${scope()}`;$('#app').innerHTML=`<div class="app"><div class="titlebar"><div class="traffic" aria-hidden="true"><span class="red"></span><span class="yellow"></span><span class="green"></span></div><b>Obelisk</b><i>—</i><span>${esc(scope())}</span></div><div class="columns">${sidebarV2()}<main class="main">${toolbarV2()}<div id="content">${content()}</div></main></div></div>`;if(S.page==='sessionDetail')$('#content').onscroll=()=>A.readerScroll($('#content'))}
|
||
function renderContent(){document.title=`Obelisk — ${scope()}`;$('#content').innerHTML=content()}
|
||
function renderToolbar(){document.querySelector('.toolbar').outerHTML=toolbarV2()}
|
||
const A=window.A={nav(p){S.page=p;S.detail=null;S.q='';S.sourceMenu=false;S.health=false;S.selected.clear();render()},memoryView(v){S.memoryView=v;S.page='memory';S.project='all';S.detail=null;S.q='';S.selected.clear();render()},project(p){S.project=p;S.detail=null;S.page=S.page.startsWith('memory')?'memory':'sessions';render()},projectSearch(q){S.projectQ=q.toLowerCase();document.querySelector('.project-list').innerHTML=projects.filter(x=>!S.projectQ||x[0].includes(S.projectQ)).map(p=>`<button class="side-item ${S.project===p[0]?'active':''}" onclick="A.project('${p[0]}')">${svg('folder')}<span class="label">${p[0]}</span><span class="badge">${p[1]}</span></button>`).join('')},health(){S.health=!S.health;render()},search(q){S.q=q;renderContent()},sort(){S.desc=!S.desc;renderToolbar();renderContent()},sourceMenu(){S.sourceMenu=!S.sourceMenu;renderToolbar()},source(x){S.source=x;S.sourceMenu=false;renderToolbar();renderContent()},openSession(id){S.page='sessionDetail';S.detail=id;S.q='';render()},openMemory(e,id){if(e.metaKey||e.ctrlKey){A.select(id);return}S.page='memoryDetail';S.detail=id;S.showSource=false;render()},select(id){S.selected.has(id)?S.selected.delete(id):S.selected.add(id);renderContent()},archive(id){const x=memories.find(x=>x.id===id);S.undo={id,archived:x.archived};x.archived=!x.archived;S.selected.delete(id);S.page='memory';render();A.toast(x.archived?'Memory archived':'Memory restored','Undo')},undoAction(){if(!S.undo)return;memories.find(x=>x.id===S.undo.id).archived=S.undo.archived;S.undo=null;render();A.toast('Action undone')},toggleSource(){S.showSource=!S.showSource;renderContent()},disclose(el){el.parentElement.classList.toggle('open');el.textContent=(el.parentElement.classList.contains('open')?'▾':'▸')+el.textContent.slice(1)},activity(x){S.activity=x;renderContent()},day(i){S.day=S.day===i?null:i;renderContent()},recapKind(k){S.recapKind=k;renderToolbar();renderContent()},openRecap(id){S.page='recapDetail';S.detail=id;S.slide=0;render()},slide(d){S.slide=Math.max(0,Math.min(2,S.slide+d));renderContent()},generate(){const cmd=S.recapKind==='weekly'?'obelisk recap --week current':'obelisk recap --month current';$('#modalHost').innerHTML=`<dialog><div class="modal-head">◇ Generate a new recap<button aria-label="Close" onclick="this.closest('dialog').close()">×</button></div><div class="modal-body"><p style="color:var(--fg2)">Choose the window, then run the command in your terminal.</p><div class="modal-options"><button class="modal-option active">Current ${S.recapKind==='weekly'?'week':'month'}</button><button class="modal-option">Previous</button></div><div class="command"><b>$</b><span>${cmd}</span><button onclick="A.copy(this)">Copy</button></div><div class="modal-hint">Generation takes about 30s. New recaps appear automatically.</div></div></dialog>`;$('#modalHost dialog').showModal()},copy(el){el.textContent='Copied';A.toast('Command copied')},auto(){S.auto=!S.auto;renderContent();A.toast(S.auto?'Auto-refresh enabled':'Auto-refresh paused')},rebuild(){const b=$('#rebuild');b.disabled=true;b.textContent='Rebuilding…';setTimeout(()=>{b.disabled=false;b.textContent='Rebuild index';A.toast('Index rebuilt from 131 sessions')},1100)},toast(msg,action){clearTimeout(S.toastTimer);$('#toast').innerHTML=`<div class="toast">${msg}${action?`<button onclick="A.undoAction()">${action}</button>`:''}</div>`;S.toastTimer=setTimeout(()=>$('#toast').innerHTML='',3200)}};
|
||
A.dropUndo=()=>{clearInterval(S.undoTimer);S.undo=null;$('#toast').innerHTML=''};
|
||
A.nav=p=>{const prior=S.page;A.dropUndo();if(prior==='sessions'&&p!=='sessions')S.debugEmpty=false;if(p==='sessions'&&prior!=='sessions'){S.debugEmpty=false;S.quietShown=false}if(p==='activity'&&prior!=='activity'){S.activity='daily';S.day=null;S.months=1;S.noiseLedger=false}if(p==='recap'){S.recapKind='weekly';S.generateWindow='this-week'}S.page=p;S.detail=null;S.q='';S.sourceMenu=false;S.selected.clear();S.cursor=null;render()};
|
||
A.memoryView=v=>{S.memoryView=v;S.page='memory';S.project='all';S.detail=null;S.q='';S.sourceMenu=false;S.selected.clear();S.cursor=null;render()};
|
||
A.project=p=>{S.project=p;S.detail=null;S.page=S.page.startsWith('memory')?'memory':'sessions';S.selected.clear();S.cursor=null;render()};
|
||
A.backSessions=()=>{S.page='sessions';S.detail=null;S.debugEmpty=false;S.quietShown=false;render()};
|
||
A.backSessionDetail=()=>{S.page='sessionDetail';S.detail=S.parentSession||'s1';render();requestAnimationFrame(()=>{$('#content').scrollTop=S.parentScroll||0})};
|
||
A.backMemory=()=>{S.page='memory';S.detail=null;render()};
|
||
A.backRecap=()=>{S.page='recap';S.detail=null;S.recapKind='weekly';S.generateWindow='this-week';render()};
|
||
A.sourceSettings=()=>{A.dropUndo();S.page='settings';S.detail=null;render()};
|
||
A.manageSettings=()=>{S.health=false;A.sourceSettings()};
|
||
A.openSession=id=>{A.dropUndo();S.debugEmpty=false;S.quietShown=false;S.page='sessionDetail';S.detail=id;S.msgPos=1;S.traceOpen={};S.rawTools={};render()};
|
||
A.openSubagent=id=>{S.parentSession=S.detail;S.parentScroll=$('#content')?.scrollTop||0;S.page='subagentDetail';S.detail=id;render()};
|
||
A.subToggle=el=>el.closest('.sub-disclosure')?.classList.toggle('open');
|
||
A.subFull=el=>{const body=el.parentElement.querySelector('.sub-message-body');if(body)body.innerHTML='<p>The comparison found the missing Subagent route, its three-part breadcrumb, the wider reader, Prompt and Assistant role labels, simplified tool disclosures, explicit error styling, and full-text loading behavior.</p><p>The parent breadcrumb returns to the same Session Detail reading position, preserving the user’s evidence context.</p>';el.remove()};
|
||
A.openMemory=(e,id)=>{if(e.metaKey||e.ctrlKey||e.shiftKey){A.select(id,e.shiftKey);return}S.cursor=id;S.page='memoryDetail';S.detail=id;S.showSource=false;render()};
|
||
A.select=(id,range=false)=>{const rows=visibleMemoryRows(),before=S.cursor;if(range&&before){const ids=rows.map(x=>x.id),a=ids.indexOf(before),b=ids.indexOf(id);if(a>=0&&b>=0)for(let i=Math.min(a,b);i<=Math.max(a,b);i++)S.selected.add(ids[i])}else S.selected.has(id)?S.selected.delete(id):S.selected.add(id);S.cursor=id;renderContent()};
|
||
A.moveMemory=(direction,extend=false)=>{const rows=visibleMemoryRows();if(!rows.length)return;const before=S.cursor,i=rows.findIndex(x=>x.id===before),next=i<0?0:Math.max(0,Math.min(rows.length-1,i+direction));if(extend&&before){S.selected.add(before);S.selected.add(rows[next].id)}S.cursor=rows[next].id;renderContent();requestAnimationFrame(()=>document.querySelector(`.mrow[data-id="${S.cursor}"]`)?.scrollIntoView({block:'nearest'}))};
|
||
A.openCursor=()=>{if(!S.cursor)return;S.page='memoryDetail';S.detail=S.cursor;S.showSource=false;render()};
|
||
A.disclose=el=>{const row=el.closest('.trace-row');if(!row)return;row.classList.toggle('open');S.traceOpen[row.dataset.trace]=row.classList.contains('open')};
|
||
A.rawTool=(e,el)=>{e.stopPropagation();const row=el.closest('.msg-tool'),key=row?.dataset.trace;if(!row||!key)return;row.classList.toggle('raw');el.classList.toggle('active');S.rawTools[key]=row.classList.contains('raw')};
|
||
A.fileExpand=el=>{const body=el.previousElementSibling;body.classList.toggle('collapsed');el.textContent=body.classList.contains('collapsed')?`Show all ${body.querySelector('.gutter').textContent.split('\n').length} lines`:'Collapse'};
|
||
A.msgNav=target=>{const total=9;S.msgPos=target==='first'?1:target==='last'?total:target==='prev'?Math.max(1,S.msgPos-1):Math.min(total,S.msgPos+1);renderContent();requestAnimationFrame(()=>document.querySelector(`[data-pos="${S.msgPos}"]`)?.scrollIntoView({behavior:'smooth',block:'center'}))};
|
||
A.readerScroll=el=>{cancelAnimationFrame(S.readerFrame);S.readerFrame=requestAnimationFrame(()=>{const rows=[...el.querySelectorAll('[data-pos]')],center=el.scrollTop+el.clientHeight/2;if(!rows.length)return;const row=rows.reduce((best,n)=>Math.abs(n.offsetTop+n.offsetHeight/2-center)<Math.abs(best.offsetTop+best.offsetHeight/2-center)?n:best);S.msgPos=+row.dataset.pos;const nav=el.querySelector('.msg-nav'),pos=nav?.querySelector('.msg-pos'),buttons=nav?.querySelectorAll('button');if(pos)pos.textContent=`${S.msgPos} / 9`;if(buttons?.length===4){buttons[0].disabled=buttons[1].disabled=S.msgPos===1;buttons[2].disabled=buttons[3].disabled=S.msgPos===9}})};
|
||
A.slide=d=>{S.slide=Math.max(0,Math.min(4,S.slide+d));renderContent()};
|
||
A.goSlide=i=>{S.slide=Math.max(0,Math.min(4,i));renderContent()};
|
||
A.openRecap=id=>{S.page='recapDetail';S.detail=id;S.slide=0;S.recapPalette=0;render()};
|
||
A.health=()=>{const y=$('#content')?.scrollTop||0;S.health=!S.health;render();requestAnimationFrame(()=>{if($('#content'))$('#content').scrollTop=y})};
|
||
A.sourceSettings=()=>{A.dropUndo();const same=S.page==='settings',y=same?$('#content')?.scrollTop||0:0;S.health=false;S.page='settings';S.detail=null;render();if(same)requestAnimationFrame(()=>{if($('#content'))$('#content').scrollTop=y})};
|
||
A.manageSettings=A.sourceSettings;
|
||
A.browse=()=>{const i=document.createElement('input');i.type='file';i.webkitdirectory=true;i.click()};
|
||
A.cardBlob=async()=>{const n=$('.rc-card'),r=n.getBoundingClientRect(),css=[...document.styleSheets].flatMap(s=>{try{return[...s.cssRules].map(x=>x.cssText)}catch{return[]}}).join(''),html=`<div xmlns="http://www.w3.org/1999/xhtml" style="width:${r.width}px;height:${r.height}px;background:#0a0b14"><style>${css}</style>${n.outerHTML}</div>`,svg=`<svg xmlns="http://www.w3.org/2000/svg" width="${r.width}" height="${r.height}"><foreignObject width="100%" height="100%">${html}</foreignObject></svg>`,u=URL.createObjectURL(new Blob([svg],{type:'image/svg+xml'})),im=new Image;await new Promise((ok,no)=>{im.onload=ok;im.onerror=no;im.src=u});const c=document.createElement('canvas');c.width=r.width*2;c.height=r.height*2;c.getContext('2d').drawImage(im,0,0,c.width,c.height);URL.revokeObjectURL(u);return await new Promise(ok=>c.toBlob(ok,'image/png'))};
|
||
A.fallbackCardBlob=async()=>{const n=$('.rc-card'),c=document.createElement('canvas'),g=c.getContext('2d'),lines=n.innerText.split('\n').filter(Boolean);c.width=1080;c.height=1080;g.fillStyle='#0a0b14';g.fillRect(0,0,c.width,c.height);g.strokeStyle='#312e45';g.lineWidth=2;g.strokeRect(36,36,1008,1008);g.fillStyle='#f2effa';g.font='52px Georgia';lines.slice(0,2).forEach((s,i)=>g.fillText(s.slice(0,34),76,150+i*72));g.fillStyle='#aaa3b8';g.font='25px monospace';lines.slice(2,14).forEach((s,i)=>g.fillText(s.slice(0,62),76,340+i*48));return await new Promise(ok=>c.toBlob(ok,'image/png'))};
|
||
A.safeCardBlob=async()=>{try{return await A.cardBlob()}catch{return await A.fallbackCardBlob()}};
|
||
A.copyRecap=async()=>{try{const b=await A.safeCardBlob();await navigator.clipboard.write([new ClipboardItem({'image/png':b})])}catch{}};
|
||
A.exportRecap=async()=>{try{const b=await A.safeCardBlob(),name=`obelisk-recap-${S.slide+1}.png`;if(window.showSaveFilePicker){const h=await showSaveFilePicker({suggestedName:name,types:[{description:'PNG image',accept:{'image/png':['.png']}}]}),w=await h.createWritable();await w.write(b);await w.close()}else{const a=document.createElement('a'),u=URL.createObjectURL(b);a.href=u;a.download=name;a.click();setTimeout(()=>URL.revokeObjectURL(u),1000)}}catch{}};
|
||
A.moreActivity=()=>{S.months++;renderContent()};
|
||
A.ledgerNoise=()=>{S.noiseLedger=!S.noiseLedger;renderContent()};
|
||
A.day=i=>{S.day=i;renderContent()};
|
||
A.chartTip=(e,text)=>{const t=$('#chartTip');if(!t)return;t.textContent=text;t.style.left=e.clientX+12+'px';t.style.top=e.clientY+12+'px';t.classList.add('show')};
|
||
A.chartTipOff=()=>$('#chartTip')?.classList.remove('show');
|
||
A.generate=()=>{const opts=[['this-week','This week'],['last-week','Last week'],['this-month','This month'],['last-month','Last month']];const commands={'this-week':'/obelisk recap this week','last-week':'/obelisk recap last week','this-month':'/obelisk recap this month','last-month':'/obelisk recap last month'};$('#modalHost').innerHTML=`<div class="generate-backdrop" onclick="if(event.target===this)A.closeGenerate()"><div class="generate-modal"><div class="generate-head"><span class="diamond"></span><span class="generate-title">Generate a new recap</span><button class="generate-close" aria-label="Close" onclick="A.closeGenerate()">×</button></div><div class="generate-body"><p class="generate-copy">Recaps are generated by Claude Code. Run the command below in your terminal — Obelisk will pick it up automatically.</p><div class="generate-options">${opts.map(o=>`<button class="generate-option ${S.generateWindow===o[0]?'active':''}" onclick="A.generateSelect('${o[0]}')"><span class="generate-radio"></span><span>${o[1]}</span></button>`).join('')}</div><div class="generate-command"><span class="prompt">$</span> <span id="generateCmd">${commands[S.generateWindow]}</span><button id="copyCmd" aria-label="Copy command" onclick="A.copyCmd(this)">▣</button></div><div class="generate-hint">Generation takes ~30s. New recaps appear in this list automatically.</div></div></div></div>`};
|
||
A.closeGenerate=()=>{$('#modalHost').innerHTML=''};
|
||
A.generateSelect=key=>{S.generateWindow=key;A.generate()};
|
||
A.copyCmd=el=>{el.classList.add('copied');el.textContent='✓';setTimeout(()=>{if(el.isConnected){el.classList.remove('copied');el.textContent='▣'}},1600)};
|
||
A.undoToast=()=>{clearInterval(S.undoTimer);const paint=()=>{if(!S.undo)return;const n=S.undo.entries.length,noun=n===1?'memory':'memories';$('#toast').innerHTML=`<div class="toast" onclick="A.undoAction()">${S.undo.action==='archive'?'Archived':'Restored'} ${n} ${noun}.<button>Undo (${S.undo.countdown}s)</button></div>`};paint();S.undoTimer=setInterval(()=>{if(!S.undo)return clearInterval(S.undoTimer);S.undo.countdown--;if(S.undo.countdown<=0){clearInterval(S.undoTimer);S.undo=null;$('#toast').innerHTML=''}else paint()},1000)};
|
||
A.archive=ids=>{const targets=(Array.isArray(ids)?ids:[ids]).map(id=>memories.find(x=>x.id===id)).filter(Boolean);if(!targets.length)return;const action=targets[0].archived?'restore':'archive';S.undo={action,countdown:5,entries:targets.map(x=>({id:x.id,archived:x.archived}))};targets.forEach(x=>x.archived=action==='archive');S.selected.clear();if(targets.some(x=>x.id===S.cursor))S.cursor=null;S.page='memory';S.detail=null;const rows=visibleMemoryRows();if(rows.length)S.cursor=rows[0].id;render();A.undoToast()};
|
||
A.undoAction=()=>{if(!S.undo)return;S.undo.entries.forEach(v=>{const x=memories.find(m=>m.id===v.id);if(x)x.archived=v.archived});clearInterval(S.undoTimer);S.undo=null;$('#toast').innerHTML='';render()};
|
||
A.auto=()=>{S.auto=!S.auto;renderContent()};
|
||
A.rebuild=()=>{const b=$('#rebuild');if(!b||b.disabled)return;b.disabled=true;b.textContent='Rebuilding…';setTimeout(()=>{const next=$('#rebuild');if(next){next.disabled=false;next.textContent='Rebuild index'}},1100)};
|
||
A.noise=()=>{S.showNoise=!S.showNoise;render()};
|
||
A.quiet=()=>{S.quietShown=!S.quietShown;renderContent()};
|
||
A.projectSearchV2=q=>{S.projectQ=q.toLowerCase();const list=document.querySelector('.project-list');if(list)list.innerHTML=projectButtonsV2()+(S.page.startsWith('session')&&!S.showNoise?`<button class="noise-fold" onclick="A.noise()"><span>›</span><span class="label">${noiseProjects.length} test projects hidden</span><span class="badge">${noiseProjects.length}</span></button>`:'')};
|
||
document.addEventListener('click',e=>{const b=e.target.closest('.settings-v2 .btn');if(b?.textContent.trim().startsWith('Browse'))A.browse()});
|
||
document.addEventListener('keydown',e=>{if(S.page==='recapDetail'&&!/INPUT|TEXTAREA/.test(e.target.tagName)&&e.key.toLowerCase()==='p'){e.preventDefault();e.stopImmediatePropagation();S.recapPalette=(S.recapPalette+1)%7;renderContent()}});
|
||
document.addEventListener('keydown',e=>{const input=/INPUT|TEXTAREA/.test(e.target.tagName)||e.target.isContentEditable,key=e.key.length===1?e.key.toLowerCase():e.key,mod=e.metaKey||e.ctrlKey;if(mod&&['1','2','3'].includes(key)){e.preventDefault();key==='1'?A.nav('sessions'):A.memoryView(key==='2'?'active':'archived');return}if(S.page==='sessionDetail'&&mod){const sizes=[12,13,14,15,16,18],i=sizes.indexOf(S.fontSize);if(['=','+'].includes(key)){e.preventDefault();S.fontSize=sizes[Math.min(sizes.length-1,i+1)];renderContent();return}if(key==='-'){e.preventDefault();S.fontSize=sizes[Math.max(0,i-1)];renderContent();return}if(key==='0'){e.preventDefault();S.fontSize=14;renderContent();return}}if(mod&&key==='z'&&S.undo&&['memory','memoryDetail'].includes(S.page)){e.preventDefault();A.undoAction();return}if(S.page==='recapDetail'&&!input){if(['ArrowLeft','PageUp'].includes(key)){e.preventDefault();A.slide(-1);return}if(['ArrowRight','PageDown',' '].includes(key)){e.preventDefault();A.slide(1);return}if(key==='Home'){e.preventDefault();A.goSlide(0);return}if(key==='End'){e.preventDefault();A.goSlide(4);return}}if(input){if(key==='Escape')e.target.blur();return}if(S.page==='sessions'&&key==='m'){S.debugEmpty=!S.debugEmpty;renderContent();return}if(S.page==='memoryDetail'){if(key==='Escape'){e.preventDefault();A.backMemory();return}if(key==='d'){e.preventDefault();A.archive(S.detail);return}}if(S.page==='memory'){if(key==='j'||key==='ArrowDown'){e.preventDefault();A.moveMemory(1,e.shiftKey);return}if(key==='k'||key==='ArrowUp'){e.preventDefault();A.moveMemory(-1,e.shiftKey);return}if(key==='Enter'&&S.cursor){e.preventDefault();A.openCursor();return}if(key==='x'&&S.cursor){e.preventDefault();A.select(S.cursor);return}if(key==='d'){e.preventDefault();A.archive(S.selected.size?[...S.selected]:(S.cursor?[S.cursor]:[]));return}if(key==='u'&&S.undo){e.preventDefault();A.undoAction();return}}if(key==='/'&&['sessions','memory'].includes(S.page)){e.preventDefault();$('#search')?.focus();return}if(key==='s'&&['sessions','memory'].includes(S.page)){e.preventDefault();A.sort();return}if(key==='Escape'&&['sessions','memory'].includes(S.page)){if(S.selected.size){S.selected.clear();renderContent()}else if(S.q){S.q='';renderToolbar();renderContent()}}});
|
||
render();
|
||
</script>
|
||
</body>
|
||
</html>
|