spexcode 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/spec-cli/src/anchors.ts +53 -12
- package/spec-cli/src/cli.ts +9 -5
- package/spec-cli/src/gateway.ts +8 -1
- package/spec-cli/src/graph.ts +13 -7
- package/spec-cli/src/guide.ts +52 -21
- package/spec-cli/src/harness.ts +85 -34
- package/spec-cli/src/help.ts +20 -14
- package/spec-cli/src/index.ts +25 -7
- package/spec-cli/src/lint.ts +91 -49
- package/spec-cli/src/migrate-table.ts +16 -6
- package/spec-cli/src/session-timeline.ts +148 -0
- package/spec-cli/src/sessions.ts +29 -4
- package/spec-cli/src/specs.ts +35 -17
- package/spec-cli/templates/hooks/prepare-commit-msg +6 -1
- package/spec-cli/templates/spec/project/.plugins/commands/spec.md +16 -0
- package/spec-cli/templates/spec/project/.plugins/skills/spec.md +17 -0
- package/spec-cli/templates/spec/project/.plugins/spec.md +3 -3
- package/spec-dashboard/dist/assets/{Dashboard-CTcH2eW9.js → Dashboard-C_fGmOKK.js} +3 -3
- package/spec-dashboard/dist/assets/EvalsPage-Cnr1s3bq.js +2 -0
- package/spec-dashboard/dist/assets/{FoldToggle-CVFbBpyW.js → FoldToggle-x9gtO1OQ.js} +1 -1
- package/spec-dashboard/dist/assets/{IssuesPage-kULjonqj.js → IssuesPage-5f_vL-JV.js} +1 -1
- package/spec-dashboard/dist/assets/MobileApp-DEO1jgGM.js +1 -0
- package/spec-dashboard/dist/assets/SessionInterface-CAlbMOFR.js +66 -0
- package/spec-dashboard/dist/assets/SessionWindow-JYbpPwNB.js +13 -0
- package/spec-dashboard/dist/assets/{Settings-BL6FV_8S.js → Settings-DKb5Ji_X.js} +1 -1
- package/spec-dashboard/dist/assets/index-BQu-oJ8J.js +41 -0
- package/spec-dashboard/dist/assets/index-BbMkwuix.css +1 -0
- package/spec-dashboard/dist/assets/launch-BM9GgvkX.js +6 -0
- package/spec-dashboard/dist/index.html +2 -2
- package/spec-eval/src/cli.ts +32 -18
- package/spec-eval/src/evaltab.ts +4 -3
- package/spec-eval/src/scenarios.ts +116 -4
- package/spec-dashboard/dist/assets/EvalsPage-CJNKwHLN.js +0 -2
- package/spec-dashboard/dist/assets/MobileApp-B0ZJju8K.js +0 -1
- package/spec-dashboard/dist/assets/SessionInterface-BRKJqU2U.js +0 -71
- package/spec-dashboard/dist/assets/SessionWindow-CDhEL7wO.js +0 -9
- package/spec-dashboard/dist/assets/index-DmQsNYKK.css +0 -1
- package/spec-dashboard/dist/assets/index-DulGPk6A.js +0 -41
- /package/spec-cli/templates/spec/project/.plugins/{extract → commands/extract}/spec.md +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{regroup → commands/regroup}/spec.md +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{supervisor → commands/supervisor}/spec.md +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{tidy → commands/tidy}/spec.md +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{distill → skills/distill}/digest.mjs +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{distill → skills/distill}/spec.md +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--paper: #fdf6e3;--panel: #f4eeda;--panel2:#eee8d5;--line: #ded7bf;--ink: #586e75;--ink2: #073642;--muted: #93a1a1;--blue: #268bd2;--green: #859900;--orange:#cb4b16;--red: #dc322f;--yellow:#b58900;--magenta:#d33682;--cyan: #2aa198;--term-bg: #0d1117;--mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace}:root[data-theme=dark]{--paper: #0d1117;--panel: #161b22;--panel2:#21262d;--line: #30363d;--ink: #c9d1d9;--ink2: #f0f6fc;--muted: #8b949e;--blue: #58a6ff;--green: #3fb950;--orange:#f0883e;--red: #f85149;--yellow:#d29922;--magenta:#bc8cff;--cyan: #39c5cf;--term-bg: #0d1117}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--paper);color:var(--ink);font-family:var(--mono);font-size:13px;-webkit-font-smoothing:antialiased}*{scrollbar-color:var(--line) transparent;scrollbar-width:thin}::-webkit-scrollbar{width:9px;height:9px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--line);border-radius:6px;border:2px solid var(--panel)}::-webkit-scrollbar-thumb:hover{background:var(--muted)}::-webkit-scrollbar-corner{background:transparent}.app{display:flex;height:100vh}.app-main{flex:1;min-width:0;display:flex;position:relative}.page-graph{flex:1;min-width:0;display:grid;grid-template-columns:1fr var(--fp-w, 250px);position:relative}.side-rail{flex:0 0 52px;display:flex;flex-direction:column;align-items:center;gap:6px;padding:10px 0;background:var(--panel);border-right:1px solid var(--line)}.rail-spacer{flex:1}.rail-btn{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:none;border:none;border-radius:9px;cursor:pointer;color:var(--muted)}.rail-btn:hover{background:var(--panel2);color:var(--ink2)}.rail-btn.on{background:color-mix(in srgb,var(--blue) 14%,transparent);color:var(--blue)}.page-pane{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--paper)}.page-evals,.page-issues{overflow:hidden;padding:0 14px 10px 0}.page-settings{overflow:auto;flex-direction:row;justify-content:center}.settings-body{width:min(720px,94%);padding:26px 0 60px}.page-title{font-size:17px;font-weight:700;color:var(--ink2);margin:6px 2px 14px}.pane-resizer{cursor:col-resize;z-index:6;position:relative}.pane-resizer:after{content:"";position:absolute;top:0;bottom:0;left:50%;width:3px;margin-left:-1.5px;background:transparent;border-radius:2px}.pane-resizer:hover:after,body.is-resizing .pane-resizer:after{background:color-mix(in srgb,var(--blue) 55%,transparent)}.page-graph>.pane-resizer{position:absolute;top:0;bottom:0;width:9px;right:calc(var(--fp-w, 250px) - 4px)}.si-resizer{flex:0 0 7px;margin:0 -3.5px}body.is-resizing,body.is-resizing *{cursor:col-resize!important;-webkit-user-select:none!important;user-select:none!important}.app.kbd-mode,.app.kbd-mode *{cursor:none!important}.app.kbd-mode .react-flow,.app.kbd-mode .react-flow *{pointer-events:none!important}.graph{position:relative;background:var(--paper);border-right:1px solid var(--line)}.react-flow__attribution{display:none}.react-flow__edge-path{stroke-dasharray:0}.move-edge .react-flow__edge-path{stroke-linecap:round}.hud{position:absolute;top:16px;left:18px;z-index:5;display:flex;flex-direction:row;align-items:center;gap:8px;pointer-events:none}.brand{color:var(--ink2);font-weight:700;letter-spacing:.5px}.hud .hud-help{align-self:center}.hud-help{pointer-events:auto;align-self:flex-start;cursor:pointer;width:22px;height:22px;display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:13px;line-height:1;color:var(--blue);background:var(--paper);border:1px solid var(--line);border-radius:4px}.hud-help:hover{border-color:var(--blue);color:var(--orange)}.lock-hint{position:absolute;top:16px;left:50%;transform:translate(-50%);z-index:6;display:flex;align-items:center;gap:10px;max-width:min(48vw,520px);padding:5px 8px 5px 11px;background:var(--paper);border:1px solid var(--line);border-left:4px solid var(--ov, var(--blue));border-radius:6px;box-shadow:0 6px 22px #657b8329;font-family:var(--mono);font-size:12px;color:var(--ink)}.lock-hint-lead{font-weight:700;flex:none;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lock-hint-body{color:var(--ink2);flex:1;min-width:0}.lock-hint kbd{border:1px solid var(--line);border-radius:3px;padding:0 5px;margin:0 1px;color:var(--blue);font-family:var(--mono);font-size:11px}.lock-hint-sep{color:var(--muted)}.lock-hint-release{flex:none;cursor:pointer;padding:1px 8px;background:var(--paper);border:1px solid var(--line);border-radius:4px;color:var(--muted);font-family:var(--mono);font-size:11px}.lock-hint-release:hover{border-color:var(--blue);color:var(--orange)}.graph-stats{position:absolute;bottom:14px;left:18px;z-index:5;display:flex;align-items:center;gap:5px;flex-wrap:wrap;max-width:calc(100% - 36px);padding:4px 9px;background:var(--paper);border:1px solid var(--line);border-radius:6px;box-shadow:0 4px 18px #657b831f;font-family:var(--mono);font-size:12px;color:var(--ink2)}.bstat-total{font-weight:700;color:var(--ink);padding-right:1px}.bstat{display:inline-flex;align-items:center;gap:3px;padding:1px 4px;border:0;border-radius:4px;background:none;font-family:var(--mono);font-size:12px;line-height:1.4;color:inherit;cursor:pointer}.bstat:hover:not(:disabled){background:var(--panel2);color:var(--ink)}.bstat:disabled{opacity:.38;cursor:default}.bstat-dot{width:7px;height:7px;border-radius:2px;flex:none}.bstat-drift{color:var(--yellow)}.bstat-issue{color:var(--magenta)}.bstat-sep{width:1px;align-self:stretch;margin:2px 3px;background:var(--line)}.graph-stats .score-badge{width:13px;height:13px;font-size:8px}.graph-stats .score-badge.empty{opacity:.6}.react-flow__node{transition:opacity .18s ease,transform .2s ease}.react-flow__node.is-far{opacity:.35}.react-flow__controls{box-shadow:none;border:1px solid var(--line);border-radius:4px;overflow:hidden}.react-flow__controls-button{background:var(--panel2);border-bottom:1px solid var(--line);color:var(--ink2)}.react-flow__controls-button:hover{background:var(--panel)}.react-flow__controls-button svg{fill:var(--ink)}.spec-node{position:relative;display:flex;flex-direction:column;justify-content:center;gap:3px;width:176px;min-height:46px;padding:5px 9px;background:var(--panel);border:1px solid var(--line);border-radius:4px;transition:border-color .12s}.spec-node.focused{border-color:var(--blue);outline:2px solid var(--blue);outline-offset:-1px}.spec-node.active.focused{border-color:var(--orange);outline-color:var(--orange)}.spec-node.drift.focused{border-color:var(--yellow);outline-color:var(--yellow)}.node-row1{display:flex;align-items:center;gap:7px}.node-row2{display:flex;align-items:center;gap:7px;min-height:18px}.node-dot{position:relative;width:7px;height:7px;border-radius:2px;flex:none}.pulse{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:2px;animation:pulse 1.6s ease-out infinite}@keyframes pulse{0%{transform:scale(1);opacity:.6}to{transform:scale(2.6);opacity:0}}.node-title{font-size:12px;font-weight:600;color:var(--ink2);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node-ago{font-size:10px;color:var(--muted);flex:none;white-space:nowrap}.node-ver{font-size:10px;color:var(--muted);flex:none}.node-expand{position:absolute;right:-7px;top:50%;transform:translateY(-50%);display:flex;align-items:center;gap:1px;font-size:9px;font-weight:700;line-height:1;color:var(--muted);background:var(--paper);border:1px solid var(--line);border-radius:7px;padding:2px 4px;pointer-events:none}.spec-node.focused .node-expand{color:var(--blue);border-color:var(--blue)}.node-editors{display:flex;align-items:center;gap:3px}.av-more{font-size:9px;font-weight:700;color:var(--muted);margin-left:1px}.avatar{display:inline-flex;border-radius:50%;box-shadow:0 0 0 1.5px var(--panel)}.av-face{display:inline-flex;align-items:center;justify-content:center;line-height:1;font-weight:700;overflow:hidden}.av-circle{border-radius:50%}.av-rounded{border-radius:5px}.av-square{border-radius:1px}.av-hex{border-radius:50%/18%;clip-path:polygon(25% 5%,75% 5%,100% 50%,75% 95%,25% 95%,0 50%)}.av-st-working{box-shadow:0 0 0 1.5px var(--panel),0 0 0 3px var(--green)}.av-st-idle{box-shadow:0 0 0 1.5px var(--panel),0 0 0 3px var(--muted)}.av-st-offline{box-shadow:0 0 0 1.5px var(--panel),0 0 0 3px var(--line);opacity:.55}.react-flow .react-flow__handle{opacity:0;pointer-events:none;background:transparent;border:none;width:5px;height:5px;min-width:0;min-height:0;border-radius:1px}.side{padding:18px 18px 24px;overflow-y:auto;background:var(--panel);border-left:1px solid var(--line)}.trail{display:flex;flex-wrap:wrap;align-items:center;gap:2px;margin-bottom:16px;font-size:11px}.trail .sep{color:var(--muted);margin:0 3px}.crumb{background:none;border:none;padding:1px 3px;cursor:pointer;color:var(--muted);font-family:var(--mono);font-size:11px}.crumb:hover{color:var(--blue)}.crumb.here{color:var(--ink2);font-weight:700}.side h1{font-size:17px;color:var(--ink2);margin:0 0 8px}.side h2{font-size:11px;color:var(--muted);margin:24px 0 10px;font-weight:500}.side h2:first-child{margin-top:0}.stats{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:4px;overflow:hidden}.stats div{display:flex;justify-content:space-between;font-size:12px;padding:7px 10px}.stats div:nth-child(odd){background:var(--panel2)}.stats span{color:var(--muted)}.stats b{color:var(--ink2);font-weight:600}.c-merged{color:var(--green)}.c-active{color:var(--orange)}.c-drift{color:var(--yellow)}.c-pending{color:var(--muted)}.badge{display:inline-block;font-size:11px;padding:2px 0;border:none;background:none}.badge.merged{color:var(--green)}.badge.active{color:var(--orange)}.badge.drift{color:var(--yellow)}.badge.pending{color:var(--muted)}.desc{color:var(--ink);line-height:1.55;font-size:12.5px;margin:12px 0 4px}.meta{margin-top:14px;display:flex;flex-direction:column;gap:0;border:1px solid var(--line);border-radius:4px;overflow:hidden}.meta div{display:flex;justify-content:space-between;font-size:12px;padding:7px 10px}.meta div:nth-child(odd){background:var(--panel2)}.meta span{color:var(--muted)}.meta b{color:var(--ink2);font-weight:600}.peek-btn{margin-top:22px;width:100%;padding:9px;background:var(--paper);color:var(--blue);border:1px solid var(--blue);border-radius:4px;font-family:var(--mono);font-size:13px;font-weight:600;cursor:pointer}.peek-btn:hover:not(:disabled){background:var(--blue);color:var(--paper)}.peek-btn:disabled{color:var(--muted);border-color:var(--line);cursor:default}.ov-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:20;background:#657b8340;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center}.ov-panel{width:min(900px,90vw);height:min(600px,84vh);background:var(--paper);border:1px solid var(--blue);border-radius:6px;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 12px 40px #657b8340}.ov-head{display:flex;align-items:center;gap:14px;padding:7px 12px;background:var(--panel2);border-bottom:1px solid var(--line)}.ov-title{font-size:13px;font-weight:700;color:var(--ink2)}.ov-tabs{display:flex;gap:4px}.ov-tab{background:none;border:1px solid transparent;border-radius:4px;padding:3px 8px;cursor:pointer;color:var(--muted);font-family:var(--mono);font-size:12px}.ov-tab:hover{color:var(--ink2)}.ov-tab.on{color:var(--ink2);border-color:var(--blue);background:var(--paper)}.ov-tab kbd{font-family:var(--mono);font-size:10px;color:var(--blue);border:1px solid var(--line);border-radius:2px;padding:0 3px;margin-right:3px;background:var(--paper)}.ov-tab-counts{display:inline-flex;gap:3px;margin-left:5px;vertical-align:baseline}.ovc{font-family:var(--mono);font-size:9px;font-weight:700;line-height:1.5;padding:0 4px;border-radius:3px;border:1px solid var(--line)}.ovc.st-open{color:var(--green);border-color:color-mix(in srgb,var(--green) 45%,transparent)}.ovc.st-closed{color:var(--magenta);border-color:color-mix(in srgb,var(--magenta) 45%,transparent)}.ovc.st-edit{color:var(--blue);border-color:color-mix(in srgb,var(--blue) 45%,transparent)}.ov-hint{margin-left:auto;font-size:11px;color:var(--muted)}.ov-body{flex:1;min-width:0;overflow:hidden;display:flex}.pane-doc{flex:1;padding:22px 26px;overflow:auto}.pane-doc h1{font-size:18px;color:var(--ink2);margin:0 0 14px}.pane-doc blockquote{margin:0 0 16px;padding:8px 14px;border-left:3px solid var(--blue);background:var(--panel2);color:var(--ink);line-height:1.6;font-size:13px}.pane-doc .doc-stat{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:0 0 16px;padding:6px 10px;background:var(--panel2);border:1px solid var(--line);border-radius:5px}.stat-status{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em}.stat-dot{width:7px;height:7px;border-radius:2px;flex:none;background:var(--muted)}.stat-status.st-merged{color:var(--green)}.stat-status.st-merged .stat-dot{background:var(--green)}.stat-status.st-active{color:var(--orange)}.stat-status.st-active .stat-dot{background:var(--orange)}.stat-status.st-drift{color:var(--yellow)}.stat-status.st-drift .stat-dot{background:var(--yellow)}.stat-status.st-pending{color:var(--muted)}.stat-status.st-pending .stat-dot{background:var(--muted)}.stat-chip{display:inline-flex;align-items:center;gap:4px;font-size:10.5px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:3px;padding:1px 6px}.stat-chip.stat-drift{color:var(--yellow);font-weight:700;border-color:color-mix(in srgb,var(--yellow) 45%,transparent)}.stat-sess{margin-left:auto;display:inline-flex;align-items:center;gap:5px;font-size:10.5px;color:var(--muted);min-width:0}.stat-sess b{color:var(--ink2);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pane-doc .doc-gov{margin:0 0 16px}.doc-gov-h{display:block;font-size:10.5px;color:var(--muted);margin-bottom:6px}.doc-gov-h b{color:var(--ink2);font-weight:700}.doc-gov-files{display:flex;flex-wrap:wrap;gap:5px}.gov-f{font-size:11px;color:var(--blue);background:var(--panel2);border:1px solid color-mix(in srgb,var(--blue) 22%,var(--line));padding:1px 7px;border-radius:3px}.doc-gov.prose .doc-gov-h{font-style:italic}.doc-body{border-top:1px solid var(--line);padding-top:14px;font-size:13px;line-height:1.65;color:var(--ink)}.doc-body p{margin:0 0 10px}.pane-loading{display:flex;align-items:center;justify-content:center;padding:48px 0}.spinner{width:22px;height:22px;border-radius:50%;border:2px solid var(--line);border-top-color:var(--ink2);animation:si-attach-spin .7s linear infinite}.doc-body ul{margin:0 0 10px;padding-left:18px}.doc-body li{margin:2px 0}.doc-body code{font-size:12px;background:var(--panel2);color:var(--ink2);padding:0 4px;border-radius:3px}.doc-body strong{color:var(--ink2)}.doc-h{font-size:12px;color:var(--ink2);font-weight:700;margin:14px 0 6px}.doc-pre{margin:0 0 10px;padding:10px 12px;background:var(--panel2);border-radius:4px;overflow-x:auto;font-size:12px;line-height:1.5}.doc-pre code{background:none;padding:0;color:var(--ink)}.doc-link{color:var(--blue)}.doc-table{width:100%;margin:0 0 12px;border-collapse:collapse;font-size:12px;display:block;overflow-x:auto}.doc-table th,.doc-table td{border:1px solid var(--line);padding:5px 9px;text-align:left;vertical-align:top}.doc-table th{background:var(--panel2);color:var(--ink2);font-weight:700}.doc-table code{white-space:nowrap}.spec-parts{display:flex;flex-direction:column;gap:14px;margin-top:14px}.spec-part{border:1px solid var(--line);border-radius:6px;border-left-width:3px;overflow:hidden}.spec-part.part-raw{border-left-color:var(--green)}.spec-part.part-expanded{border-left-color:var(--blue)}.spec-part.part-state{border-left-color:var(--orange)}.part-head{display:flex;align-items:baseline;gap:10px;padding:8px 12px;background:var(--panel2);flex-wrap:wrap}.part-title{font-size:12px;font-weight:700;color:var(--ink2);text-transform:uppercase;letter-spacing:.04em}.part-owner{font-size:10px;font-weight:700;padding:1px 7px;border-radius:9px;text-transform:uppercase;letter-spacing:.03em}.part-owner.owner-human{background:#8599002e;color:var(--green)}.part-owner.owner-agent{background:#268bd22e;color:var(--blue)}.part-note{font-size:11px;color:var(--muted);margin-left:auto}.part-body{padding:4px 14px 12px}.part-body .doc-body{border-top:none;padding-top:8px}.cs-sub+.cs-sub{margin-top:6px}.cs-label{font-size:11px;font-weight:700;color:var(--orange);text-transform:uppercase;letter-spacing:.04em;margin:12px 0 0}.ver-row{padding:10px 0;border-bottom:1px solid var(--line)}.ver-row.latest{border-left:2px solid var(--blue);padding-left:12px;margin-left:-14px}.rec-toggle{display:block;width:100%;text-align:left;background:none;border:0;padding:0;margin:0;cursor:pointer;font:inherit;color:inherit}.rec-caret{color:var(--muted);font-size:9px;width:9px;flex:none}.rec-head{display:flex;align-items:baseline;gap:10px;font-size:12px}.rec-v{color:var(--ink2);font-weight:700}.rec-hash{color:var(--muted);font-size:11px}.rec-date{color:var(--muted)}.rec-diff{margin-left:auto;display:flex;gap:8px;font-size:12px}.rec-add{color:var(--green)}.rec-del{color:var(--orange)}.rec-msg{color:var(--ink2);font-size:13px;margin-top:5px;word-break:break-word}.rec-sub{color:var(--muted);font-size:11px;margin-top:3px}.rec-msg.muted{color:var(--muted)}.rec-evidence{margin:12px 0 2px;padding:14px 0 0;border-top:1px solid var(--line)}.ev-note{font-size:11px;color:var(--muted);text-align:center;margin-top:12px}.ev-difflabel{font-size:11px;color:var(--muted);margin-bottom:8px}.ev-diff{margin:0;font-family:var(--mono);font-size:11px;line-height:1.55;background:var(--panel2);border:1px solid var(--line);border-radius:5px;padding:8px 0;overflow-x:auto}.ev-diff .dl{padding:0 12px;white-space:pre}.dl-hunk{color:var(--blue);opacity:.75}.dl-add{color:var(--green);background:#8599001a}.dl-del{color:var(--orange);background:#cb4b161a}.dl-ctx{color:var(--muted)}.pane-hist{flex:1;min-width:0;padding:16px 20px;overflow:auto}.pane-hist.empty{color:var(--muted);font-size:13px}.pane-solo{flex:1;min-width:0;display:flex}.spec-node.has-overlay{border-color:var(--ov);box-shadow:0 0 0 2px color-mix(in srgb,var(--ov) 22%,transparent)}.spec-node.has-overlay.ov-dirty{border-style:dashed}.spec-node.has-overlay.focused{outline-color:var(--ov)}.spec-node.ghost{background:transparent;border-style:dashed;opacity:.72}.spec-node.deleted .node-title{text-decoration:line-through;color:var(--muted)}.drift-badge{flex:none;font-size:10px;font-weight:700;color:var(--yellow);border:1px solid var(--yellow);border-radius:3px;padding:0 3px;line-height:1.4}.issue-badge{flex:none;font-size:10px;font-weight:700;color:var(--magenta);border:1px solid var(--magenta);border-radius:3px;padding:0 3px;line-height:1.4;cursor:default}.score-badge{flex:none;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%;border:1.5px solid currentColor;font-size:9px;font-weight:900;line-height:1;cursor:default}.score-badge.pass{color:var(--green)}.score-badge.fail{color:var(--red)}.score-badge.stalePass,.score-badge.staleFail{color:var(--muted)}.score-badge.empty{color:var(--muted);opacity:.45}.scenario-count{flex:none;font-family:var(--mono);font-size:10px;font-weight:700;line-height:1.4;border:1px solid currentColor;border-radius:3px;padding:0 3px;cursor:default}.scenario-count.pass{color:var(--green)}.scenario-count.fail{color:var(--red)}.scenario-count.stalePass,.scenario-count.staleFail{color:var(--muted)}.scenario-count.empty{color:var(--muted);opacity:.7}.focus-panel{overflow:auto;background:var(--paper);display:flex;flex-direction:column;gap:18px;padding:16px 14px}.fp-head{display:flex;flex-direction:column;gap:3px}.fp-title{font-size:15px;font-weight:700;color:var(--ink2);word-break:break-word;line-height:1.25}.fp-desc{color:var(--ink);font-size:11px;line-height:1.4;margin-top:3px;display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}.fp-sec{display:flex;flex-direction:column;gap:5px}.fp-sec-head{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);border-bottom:1px solid var(--line);padding-bottom:5px}.fp-count{font-family:var(--mono);font-size:10px}.fp-count.pass{color:var(--green)}.fp-count.fail{color:var(--red)}.fp-count.stalePass,.fp-count.staleFail,.fp-count.empty{color:var(--muted)}.fp-empty{color:var(--muted);font-size:11px;font-style:italic;padding:3px 2px}.fp-scenario{position:relative;display:block;width:100%;padding:6px;text-align:left;background:none;border:none;border-radius:5px;font:inherit;color:inherit;cursor:pointer}.fp-scenario:hover{background:var(--panel2)}.fp-sc-mark{position:absolute;left:6px;top:6px;width:12px;text-align:center;font-weight:900;font-size:11px;line-height:1.5}.fp-scenario.pass .fp-sc-mark{color:var(--green)}.fp-scenario.fail .fp-sc-mark{color:var(--red)}.fp-scenario.stalePass .fp-sc-mark,.fp-scenario.staleFail .fp-sc-mark{color:var(--muted)}.fp-scenario.empty .fp-sc-mark,.fp-scenario.missing .fp-sc-mark{color:var(--muted);opacity:.55}.fp-sc-body{display:flex;flex-direction:column;gap:3px;min-width:0}.fp-sc-name{padding-left:20px;font-family:var(--mono);font-size:11px;font-weight:700;color:var(--ink2)}.fp-sc-expected{color:var(--ink);font-size:11px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.fp-sc-code{color:var(--muted);font-family:var(--mono);font-size:9px;opacity:.85}.tag-chips{display:inline-flex;flex-wrap:wrap;gap:3px;min-width:0}.tag-chip{font-family:var(--mono);font-size:9px;line-height:1.5;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:3px;padding:0 4px;white-space:nowrap}.search-item .tag-chips{flex:none}.fp-issue-counts{display:flex;gap:6px;font-size:9px}.focus-panel .issue-group-head{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin:8px 2px 3px}.focus-panel .issue-card{padding:6px;border-radius:5px}.issue-card{display:block;max-width:100%;min-width:0;box-sizing:border-box;overflow:hidden;padding:5px 6px;border-radius:5px;text-decoration:none}.issue-card+.issue-card{margin-top:2px}.issue-card:hover{background:var(--panel2)}.issue-card-top{display:flex;align-items:center;gap:6px;min-width:0;margin-bottom:2px}.issue-num{font-size:10px;font-weight:700;color:var(--magenta);flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--mono)}.issue-state{font-size:8px;color:var(--muted);flex:none;text-transform:uppercase;letter-spacing:.04em;border:1px solid var(--line);border-radius:3px;padding:0 3px;line-height:1.5}.issue-state.st-open{color:var(--green);border-color:color-mix(in srgb,var(--green) 45%,transparent)}.issue-state.st-closed{color:var(--magenta);border-color:color-mix(in srgb,var(--magenta) 45%,transparent)}.issue-card-title{display:block;font-size:11px;line-height:1.35;color:var(--ink2);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.pane-issues{flex:1;min-width:0;padding:16px 20px;overflow:auto}.pane-issues.empty{color:var(--muted);font-size:13px}.pane-issues .issue-group-head{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin:0 2px 6px}.pane-issues .issue-group-head.closed{margin-top:16px;border-top:1px solid var(--line);padding-top:14px}.pane-issues .issue-card{padding:7px 8px}.pane-issues .issue-card+.issue-card{margin-top:1px}.pane-issues .issue-card-title{font-size:12px}.pane-edit{flex:1;min-width:0;padding:16px 20px;overflow:auto}.pane-edit.empty{color:var(--muted);font-size:13px}.edit-rev{margin:0 0 18px}.edit-by{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--muted);margin-bottom:8px}.edit-by-label{font-weight:600}.edit-state{margin-left:auto;font-family:var(--mono);font-size:10px}.ov-marks{display:flex;gap:3px;flex:none;margin-left:2px}.ov-mark{font-size:11px;font-weight:700;line-height:1;color:var(--ov, var(--blue))}.ov-mark.ov-deleted{color:var(--orange)}.react-flow__node.ov-hot{opacity:1}.react-flow__node.ov-dim{opacity:.22}.sesswin{position:absolute;top:48px;left:16px;z-index:6;width:232px;max-height:min(80vh,calc(100vh - 112px));background:var(--paper);border:1px solid var(--line);border-radius:6px;box-shadow:0 6px 22px #657b8329;overflow-y:auto;overflow-x:hidden;font-size:12px}.sesswin-empty{padding:12px 10px;color:var(--muted);font-size:11px;line-height:1.5}.sesswin-empty kbd{border:1px solid var(--line);border-radius:3px;padding:0 4px;color:var(--blue)}.sess-row{display:flex;flex-wrap:wrap;align-items:center;column-gap:7px;row-gap:2px;width:100%;padding:7px 9px;background:none;border:none;border-bottom:1px solid var(--line);border-left:2px solid var(--ov, transparent);cursor:pointer;text-align:left;font-family:var(--mono);font-size:12px;color:var(--ink2);-webkit-user-select:none;user-select:none;transition:background .12s,border-left-width .12s}.sess-row:last-child{border-bottom:none}.sess-row:hover{background:var(--panel)}.sess-row.locked{background:color-mix(in srgb,var(--ov) 12%,var(--panel2));border-left-width:4px;color:var(--ink);font-weight:600}.sess-id{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sess-lock{font-size:10px;flex:none;line-height:1;filter:saturate(0)}.sess-meta{order:1;flex:0 0 100%;width:100%;min-width:0;margin-top:1px;display:flex;align-items:center;column-gap:7px;font-size:10px;line-height:1.3;opacity:.9;overflow:hidden}.sess-ops{color:var(--ov, var(--blue));font-weight:700;flex:none}.sess-glyph{flex:none;width:12px;text-align:center;font-size:11px;line-height:1}.sess-lead{flex:none;align-self:stretch;display:inline-flex;align-items:stretch;--rail: color-mix(in srgb, var(--muted) 42%, transparent)}.sess-rail{flex:none;width:14px;position:relative;min-height:1.7em}.sess-rail.rail:before,.sess-rail.tee:before,.sess-rail.elbow:before{content:"";position:absolute;left:50%;top:0;bottom:0;border-left:1px solid var(--rail)}.sess-rail.elbow:before{bottom:50%}.sess-rail.tee:after,.sess-rail.elbow:after{content:"";position:absolute;left:50%;right:1px;top:50%;border-top:1px solid var(--rail)}.sess-fold{flex:none;display:inline-flex;align-items:center;justify-content:center;align-self:center;cursor:pointer;-webkit-user-select:none;user-select:none}.sess-fold.pod{min-width:16px;width:auto;height:14px;padding:0 4px;border-radius:999px;border:1px solid;font-size:9.5px;font-weight:700;line-height:1;color:var(--paper)}.sess-fold.pod.open{background:transparent}.sess-fold.pod:hover{filter:brightness(1.25)}.sesswin .sess-row{flex-wrap:nowrap}.sesswin .sess-meta{flex:none;width:auto;min-width:0;margin-top:0;margin-left:auto}.sesswin-zone{--zh: var(--muted);display:flex;align-items:center;gap:7px;padding:8px 9px 2px;font-size:9.5px;font-weight:700;letter-spacing:.9px;text-transform:uppercase;-webkit-user-select:none;user-select:none;color:var(--zh)}.sesswin-zone:after{content:"";flex:1;height:1px;background:var(--zh);opacity:.25}.sesswin-zone-need{--zh: var(--yellow)}.sesswin-zone-run{--zh: var(--green)}.sesswin-zone-offline{--zh: var(--muted)}.sess-menu{position:fixed;z-index:60;min-width:116px;background:var(--paper);border:1px solid var(--line);border-radius:5px;box-shadow:0 6px 22px #657b8338;overflow:hidden;padding:3px}.sess-menu-item{display:block;width:100%;text-align:left;background:none;border:none;border-radius:3px;padding:5px 9px;cursor:pointer;font-family:var(--mono);font-size:12px;color:var(--ink2)}.sess-menu-item:hover{background:var(--blue);color:var(--paper)}.sess-menu-item.danger{color:var(--orange)}.sess-menu-item.danger:hover{background:var(--orange);color:var(--paper)}.sess-menu-sep{height:1px;margin:3px 4px;background:var(--line)}.sess-menu-item.sess-menu-sess{display:flex;align-items:center;gap:6px}.sess-menu-item.sess-menu-sess .sess-glyph{width:11px}.sess-rename-modal{width:min(360px,92vw)}.sess-rename{display:flex;flex-direction:column;gap:10px;padding:4px 2px 2px}.sess-rename-input{font-family:var(--mono);font-size:13px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:4px;padding:7px 9px}.sess-rename-input:focus{outline:none;border-color:var(--blue)}.sess-rename-actions{display:flex;justify-content:flex-end;gap:8px}.sess-rename-btn{background:var(--paper);border:1px solid var(--line);color:var(--ink);border-radius:4px;padding:4px 12px;cursor:pointer;font-family:var(--mono);font-size:12px}.sess-rename-btn:hover{background:var(--panel)}.sess-rename-save{border-color:var(--blue);color:var(--blue)}.sess-rename-save:hover{background:var(--blue);color:var(--paper)}.sess-rename-save:disabled{opacity:.5;cursor:default}.sess-confirm{display:flex;flex-direction:column;gap:12px;padding:4px 2px 2px}.sess-confirm-msg{margin:0;color:var(--ink);font-size:12px;line-height:1.4}.sess-rename-btn.danger{border-color:var(--orange);color:var(--orange)}.sess-rename-btn.danger:hover{background:var(--orange);color:var(--paper)}.sess-rename-btn.danger:disabled{opacity:.5;cursor:default}.legend-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:60;background:#657b8340;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center}.legend{position:relative;width:min(660px,92vw);max-height:86vh;overflow-y:auto;background:var(--paper);border:1px solid var(--blue);border-radius:6px;box-shadow:0 12px 40px #657b8340;font-size:12px}.legend-head{position:sticky;top:0;display:flex;align-items:center;justify-content:space-between;padding:6px 9px;background:var(--panel2);border-bottom:1px solid var(--line)}.legend-title{color:var(--muted);font-size:11px;letter-spacing:.5px}.legend-close{background:none;border:none;color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;line-height:1;padding:2px}.legend-close:hover{color:var(--orange)}.legend-body{padding:4px 10px 10px}.legend-sec{padding:8px 0;border-bottom:1px solid var(--line)}.legend-sec:last-child{border-bottom:none}.legend-h{font-size:11px;color:var(--ink2);font-weight:700;margin-bottom:6px}.legend-sub{color:var(--muted);font-weight:400}.keymap-keys{flex:none;width:132px;display:flex;flex-wrap:wrap;gap:4px}.legend kbd,.settings-body kbd{font-family:var(--mono);font-size:11px;color:var(--blue);line-height:1.4;border:1px solid var(--line);border-radius:3px;padding:0 4px;background:var(--paper)}.legend-row{display:flex;align-items:baseline;gap:8px;padding:3px 0}.legend-row .node-dot{position:relative;top:1px}.legend-name{color:var(--ink2);font-weight:600;flex:none;width:52px}.legend-desc{color:var(--ink);flex:1;line-height:1.4}.legend-glyph{color:var(--blue);font-weight:700;flex:none;width:18px;text-align:center}.legend-glyph.legend-ver{font-size:11px;color:var(--muted)}.legend .ov-mark{flex:none;width:18px;text-align:center}.legend .drift-badge{flex:none}.legend-ring{flex:none;width:22px;height:14px;border-radius:3px;background:var(--panel)}.legend-ring.ring-dashed{border:1px dashed var(--blue)}.legend-ring.ring-solid{border:1px solid var(--blue)}.legend-ring.ring-ghost{border:1px dashed var(--line);background:transparent;opacity:.72}.set-langs{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}.set-lang{font-family:var(--mono);font-size:12px;color:var(--ink);cursor:pointer;border:1px solid var(--line);border-radius:4px;padding:4px 12px;background:var(--paper)}.set-lang:hover{border-color:var(--blue);color:var(--blue)}.set-lang.on{border-color:var(--blue);color:var(--blue);background:var(--panel2);font-weight:600}.set-hint{display:block;color:var(--muted);font-size:11px;line-height:1.4}.set-keys{display:flex;flex-direction:column;gap:2px;margin:2px 0 8px}.set-key-row{display:flex;align-items:center;gap:8px;padding:2px 0}.set-key-row .legend-desc{flex:1;font-size:12px}.bind-cell{flex:none;min-width:92px;min-height:24px;display:flex;flex-wrap:wrap;align-items:center;gap:4px;justify-content:center;cursor:pointer;background:var(--paper);border:1px solid var(--line);border-radius:4px;padding:2px 6px}.bind-cell:hover:not(.fixed){border-color:var(--blue)}.bind-cell.capturing{border-color:var(--blue);border-style:dashed;background:var(--panel2)}.bind-cell.custom{border-color:var(--blue)}.bind-cell.fixed{cursor:default;opacity:.6}.bind-cell kbd{border:none;padding:0 2px;background:transparent}.bind-hint{font-size:11px;color:var(--blue)}.set-foot{display:flex;align-items:center;justify-content:space-between;gap:12px}.set-reset{flex:none;font-family:var(--mono);font-size:11px;color:var(--muted);cursor:pointer;border:1px solid var(--line);border-radius:4px;padding:3px 10px;background:var(--paper)}.set-reset:hover{border-color:var(--red);color:var(--red)}.search-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:60;background:#657b8340;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:flex-start;justify-content:center;padding-top:12vh}.search-panel{width:min(92vw,clamp(640px,50vw,1100px));max-height:72vh;display:flex;flex-direction:column;overflow:hidden;background:var(--paper);border:1px solid var(--blue);border-radius:6px;box-shadow:0 14px 44px #657b8347;font-size:12px}.search-bar{display:flex;align-items:center;gap:8px;padding:9px 12px;border-bottom:1px solid var(--line);background:var(--panel2)}.search-icon{color:var(--muted);font-size:14px;flex:none}.search-input{flex:1;min-width:0;background:transparent;border:none;outline:none;color:var(--ink2);font-family:var(--mono);font-size:14px;line-height:1.4}.search-input::placeholder{color:var(--muted)}.search-results{margin:0;padding:4px;list-style:none;overflow-y:auto;overflow-x:hidden}.search-empty{padding:8px 10px;color:var(--muted)}.search-item{display:flex;align-items:baseline;gap:8px;padding:5px 8px;border-radius:3px;cursor:pointer;white-space:nowrap}.search-item.on{background:var(--panel2)}.search-item .node-dot{position:relative;top:1px}.search-kind{flex:none;font-size:9px;line-height:1;letter-spacing:.4px;text-transform:uppercase;padding:2px 4px;border-radius:3px;border:1px solid var(--line);color:var(--muted);position:relative;top:-1px}.search-kind.k-spec{color:var(--blue);border-color:color-mix(in srgb,var(--blue) 40%,var(--line))}.search-kind.k-session{color:var(--orange);border-color:color-mix(in srgb,var(--orange) 40%,var(--line))}.search-kind.k-issue{color:var(--green);border-color:color-mix(in srgb,var(--green) 40%,var(--line))}.search-kind.k-scenario{color:var(--cyan);border-color:color-mix(in srgb,var(--cyan) 40%,var(--line))}.search-title{color:var(--ink2);font-weight:600;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.search-path{color:var(--muted);font-size:11px;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.search-foot{padding:6px 12px;border-top:1px solid var(--line);background:var(--panel);color:var(--muted);font-size:11px}.si-page{flex:1;min-width:0;display:flex}.si-panel{flex:1;min-width:0;background:var(--paper);border:none;border-radius:0;overflow:hidden;display:flex}.si-list{flex:0 0 240px;border-right:1px solid var(--line);background:var(--panel);display:flex;flex-direction:column;overflow-y:auto}.si-list-unfold{flex:0 0 22px;display:flex;align-items:flex-start;justify-content:center;padding:9px 0 0;color:var(--muted);background:var(--panel);border:none;border-right:1px solid var(--line);cursor:pointer;min-height:0}.si-list-unfold:hover{color:var(--blue);background:var(--panel2)}.si-item{display:flex;flex-wrap:wrap;align-items:center;column-gap:7px;row-gap:2px;width:100%;padding:9px 12px;background:none;border:none;border-bottom:1px solid var(--line);border-left:2px solid var(--ov, transparent);cursor:pointer;text-align:left;font-family:var(--mono);font-size:12px;color:var(--ink2)}.si-item:hover{background:var(--panel2)}.si-item.on{background:color-mix(in srgb,var(--blue) 15%,var(--panel2));color:var(--ink2);font-weight:600;border-left-width:3px}.si-item.on:hover{background:color-mix(in srgb,var(--blue) 24%,var(--panel2))}.si-item.on:before{content:"❯";flex:none;color:var(--green);font-weight:700}.si-item{flex-wrap:nowrap;padding:5px 12px;font-size:11px}.si-item.on:before{content:none}.si-item .sess-meta{flex:none;width:auto;min-width:0;margin-top:0;margin-left:auto;opacity:1;overflow:visible}.si-item .sess-lock{color:var(--muted)}.si-item.on{display:block}.si-item.on .sess-meta{float:right;margin:0 0 0 8px}.si-item.on .sess-id{white-space:normal;overflow:visible}.si-zone{--zh: var(--muted);display:flex;align-items:center;gap:8px;padding:10px 12px 4px;font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;-webkit-user-select:none;user-select:none;color:var(--zh)}.si-zone:after{content:"";flex:1;height:1px;background:var(--zh);opacity:.25}.si-zone-need{--zh: var(--yellow)}.si-zone-run{--zh: var(--green)}.si-zone-offline{--zh: var(--muted)}.si-toprow{display:flex;gap:6px;padding:8px 10px;border-bottom:1px solid var(--line)}.si-selbar{display:flex;align-items:center;gap:6px;padding:8px 10px;border-bottom:1px solid var(--line)}.si-selcount{flex:1;font-family:var(--mono);font-size:11px;color:var(--ink2)}.si-selbar .sess-rename-btn{padding:4px 10px;font-size:11px}.si-check{flex:none;width:13px;height:13px;border:1.5px solid var(--muted);border-radius:3px;box-sizing:border-box}.si-check.on{border-color:var(--blue);background:var(--blue);position:relative}.si-check.on:after{content:"";position:absolute;left:3.5px;top:1px;width:3px;height:6px;border:solid var(--paper);border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}.si-item.picked{background:color-mix(in srgb,var(--blue) 12%,var(--panel2))}.si-item.picked:hover{background:color-mix(in srgb,var(--blue) 20%,var(--panel2))}.si-pill{flex:1;display:flex;align-items:center;justify-content:center;padding:7px 0;background:none;border:1px solid var(--line);border-radius:5px;cursor:pointer;font-family:var(--mono);line-height:1}.si-pill-glyph{font-size:15px;display:flex}.si-pill.new{color:var(--blue)}.si-pill:hover{background:var(--panel2);border-color:currentColor}.si-pill.new.on{background:color-mix(in srgb,var(--blue) 16%,var(--panel2));border-color:var(--blue)}.si-pill.search{color:var(--ink2)}.si-pill.search:hover{color:var(--blue)}.si-pill.forum{color:var(--ink2)}.si-pill.forum:hover{color:var(--blue)}.si-pill.forum.on{color:var(--blue);background:color-mix(in srgb,var(--blue) 16%,var(--panel2));border-color:var(--blue)}.fv-master{flex:1;min-height:0;display:grid;grid-template-columns:minmax(232px,280px) 1fr}.fv-master.folded{grid-template-columns:22px 1fr}.fv-list-col{position:relative;min-height:0;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--line);background:var(--paper)}.fv-list-col>.fv-notice{flex:none}.fv-list-col>.fv-group{flex:1 1 auto;min-height:0;overflow-y:auto;border-top:0}.fv-list-col>.fv-scroll{flex:1 1 auto;min-height:0;overflow-y:auto}.fv-list-col>.fv-scroll .fv-group{border-top:0}.fv-list-col>.fv-scroll .fv-group-head{padding-right:34px}.fv-fold{position:absolute;top:8px;right:6px;z-index:3;display:grid;place-items:center;padding:0;color:var(--muted);background:var(--panel);border:1px solid var(--line);border-radius:6px;width:22px;height:22px;cursor:pointer}.fv-fold:hover{color:var(--blue);border-color:var(--blue)}.fv-fold-inline{display:inline-flex;align-items:center;justify-content:center;flex:none;padding:0;color:var(--muted);background:var(--panel);border:1px solid var(--line);border-radius:6px;width:22px;height:22px;cursor:pointer}.fv-fold-inline:hover{color:var(--blue);border-color:var(--blue)}.fv-unfold{display:flex;align-items:flex-start;justify-content:center;padding:9px 0 0;color:var(--muted);background:var(--panel);border:none;border-right:1px solid var(--line);cursor:pointer;min-height:0}.fv-unfold:hover{color:var(--blue);background:var(--panel2)}.fold-toggle-icon{width:15px;height:15px;display:block;flex:none}.fv-detail{min-height:0;min-width:0;overflow-y:auto;background:var(--paper)}.fv-group{display:flex;flex-direction:column}.fv-group+.fv-group{border-top:1px solid var(--line)}.fv-group-head{position:sticky;top:0;z-index:2;display:flex;align-items:center;gap:6px;flex-wrap:wrap;row-gap:5px;padding:9px 12px;background:var(--panel2);border-bottom:1px solid var(--line)}.fv-group-title{font-size:9px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--ink2)}.fv-head-row{flex-basis:100%;display:flex;align-items:center;gap:6px}.fv-row,.ef-row{display:flex;align-items:center;gap:8px;width:100%;min-width:0;text-align:left;font:inherit;font-size:12px;color:var(--ink2);background:none;border:0;border-bottom:1px solid color-mix(in srgb,var(--line) 55%,transparent);padding:7px 12px;cursor:pointer}.fv-row:hover,.ef-row:hover{background:var(--panel2)}.fv-row.sel,.ef-row.sel{background:color-mix(in srgb,var(--blue) 12%,var(--paper));box-shadow:inset 2px 0 0 var(--blue)}.fv-row .fv-concern{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fv-note{margin:auto;padding:24px;color:var(--muted);font-family:var(--mono);font-size:12.5px;text-align:center}.fv-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--muted)}.fv-dot.st-open{background:var(--blue)}.fv-dot.st-landed{background:var(--green)}.fv-dot.st-closed{background:color-mix(in srgb,var(--muted) 55%,transparent)}.fv-replies{flex:none;font-family:var(--mono);font-size:10px;color:var(--muted);background:var(--panel2);border:1px solid var(--line);border-radius:9px;padding:0 6px;line-height:1.4}.fv-store{flex:none;font-family:var(--mono);font-size:9px;letter-spacing:.8px;text-transform:uppercase;color:var(--muted)}.fv-store-local{color:color-mix(in srgb,var(--blue) 65%,var(--muted))}.fv-concern{flex:1 1 auto;min-width:80px;color:var(--ink);font-weight:600;font-size:12px}.fv-status{flex:none;font-family:var(--mono);font-size:9px;border:1px solid var(--line);border-radius:3px;padding:1px 6px;color:var(--muted)}.fv-st-open{color:var(--blue);border-color:color-mix(in srgb,var(--blue) 40%,var(--line))}.fv-st-landed{color:var(--green);border-color:color-mix(in srgb,var(--green) 40%,var(--line))}.fv-by{flex:none;color:var(--muted);font-size:11px}.fv-originator{flex:none;display:inline-flex;align-items:center;gap:5px;font:inherit;font-size:11px;color:var(--muted);background:transparent;border:0;border-radius:4px;padding:1px 3px}.fv-originator-dot{width:7px;height:7px;border-radius:50%;flex:none}.fv-originator-who{color:var(--ink2);font-family:var(--mono);font-size:10.5px}.fv-originator.openable{cursor:pointer;background:color-mix(in srgb,var(--green) 8%,transparent)}.fv-originator.openable:hover{background:color-mix(in srgb,var(--green) 16%,transparent)}.fv-originator.openable:focus-visible{outline:2px solid var(--green);outline-offset:2px}.fv-originator.offline{opacity:.7}.fv-chips{flex:none;display:flex;flex-wrap:wrap;gap:5px}.fvd{height:100%;min-height:0;display:flex;flex-direction:column}.fvd-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;padding:16px 22px 24px}.fvd-scroll>*{max-width:860px}.fvd-compose{flex:none;border-top:1px solid var(--line);padding:8px 16px 12px;background:var(--panel)}.fvd-compose .fv-compose{margin-top:0;padding-top:0;border-top:none;max-width:860px}.fvd-head{margin-bottom:4px}.fvd-concern{font-size:16px;font-weight:700;color:var(--ink);line-height:1.35}.fv-close-issue{flex:none;font-family:var(--mono);font-size:11px;color:var(--orange);background:var(--paper);border:1px solid color-mix(in srgb,var(--orange) 35%,var(--line));padding:4px 12px;border-radius:4px;cursor:pointer}.fv-close-issue:hover{background:var(--orange);color:var(--paper);border-color:var(--orange)}.fv-close-issue:disabled{opacity:.5;cursor:default}.fv-close-issue.fv-life-promote{color:var(--blue);border-color:color-mix(in srgb,var(--blue) 35%,var(--line))}.fv-close-issue.fv-life-promote:hover{background:var(--blue);color:var(--paper);border-color:var(--blue)}.fvd-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:12px}.fvd-body{font-size:13px}.fv-chip{font-family:var(--mono);font-size:10.5px;color:var(--blue);background:var(--panel2);border:1px solid color-mix(in srgb,var(--blue) 22%,var(--line));padding:1px 7px;border-radius:3px;cursor:pointer;line-height:1.5}.fv-chip:hover{background:var(--blue);color:var(--paper);border-color:var(--blue)}.fv-count{color:var(--muted);font-size:10.5px}.fv-link{color:var(--blue);font-size:10.5px;text-decoration:none}.fv-link:hover{text-decoration:underline}.fv-text{font-family:var(--mono);font-size:12px;line-height:1.6;color:var(--ink2);white-space:pre-wrap}.fv-reply{margin-top:10px;padding-left:12px;border-left:2px solid var(--line);border-radius:0 4px 4px 0;transition:background .15s,border-color .15s}.fv-reply.active{border-left-color:var(--green);background:color-mix(in srgb,var(--green) 7%,transparent)}.fv-reply.sel{border-left-color:var(--orange);background:color-mix(in srgb,var(--orange) 9%,transparent)}.fv-reply.remark.open{border-left-color:var(--orange);border-left-width:3px}.fv-reply.remark.resolved{opacity:.62;border-left-color:color-mix(in srgb,var(--green) 45%,var(--line))}.fv-remark-state{font-family:var(--mono);font-size:10px;padding:0 5px;border-radius:3px;margin-left:auto}.fv-remark-state.open{color:var(--orange);background:color-mix(in srgb,var(--orange) 12%,transparent);font-weight:600}.fv-remark-state.resolved{color:var(--green);background:color-mix(in srgb,var(--green) 12%,transparent)}.fv-remark-act{font-family:var(--mono);font-size:10px;padding:0 6px;border-radius:3px;cursor:pointer;line-height:1.6;background:var(--paper)}.fv-remark-act.resolve{color:var(--green);border:1px solid color-mix(in srgb,var(--green) 40%,var(--line))}.fv-remark-act.resolve:hover{background:var(--green);color:var(--paper);border-color:var(--green)}.fv-remark-act.retract{color:var(--muted);border:1px solid var(--line)}.fv-remark-act.retract:hover{background:var(--orange);color:var(--paper);border-color:var(--orange)}.fv-remark-act:disabled{opacity:.5;cursor:default}.fv-reply-meta{display:flex;align-items:center;gap:8px;margin-bottom:3px;font-family:var(--mono);font-size:10.5px}.fv-reply-by{color:var(--blue);font-weight:600}.fv-reply-at{color:var(--muted)}.fv-anchor{font-family:var(--mono);font-size:10.5px;color:var(--orange);background:color-mix(in srgb,var(--orange) 12%,transparent);border:1px solid color-mix(in srgb,var(--orange) 35%,var(--line));border-radius:3px;padding:1px 6px;cursor:pointer}.fv-anchor:hover{background:var(--orange);color:var(--paper)}.fv-anchor.static{cursor:default}.fv-anchor.static:hover{background:color-mix(in srgb,var(--orange) 12%,transparent);color:var(--orange)}.fv-anchor.static.degraded{color:var(--muted);background:transparent;border-style:dashed;border-color:color-mix(in srgb,var(--muted) 45%,var(--line))}.fv-anchor.static.degraded:hover{color:var(--muted);background:transparent}.fv-reply-media{display:flex;flex-direction:column;gap:6px;margin-top:6px}.fv-frames{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:6px}.fv-frames .an-image,.fv-frames .eval-video{height:48px;width:auto;max-width:none}.fv-anchor-btn{display:inline-flex;align-items:center;gap:4px;font-family:var(--mono);font-size:10.5px;color:var(--orange);background:var(--panel2);border:1px solid color-mix(in srgb,var(--orange) 35%,var(--line));border-radius:4px;padding:3px 10px;cursor:pointer}.fv-anchor-btn:hover{background:var(--orange);color:var(--paper)}.fv-new-btn{display:inline-flex;align-items:center;justify-content:center;color:var(--blue);background:color-mix(in srgb,var(--blue) 10%,var(--panel2));border:1px solid color-mix(in srgb,var(--blue) 32%,var(--line));padding:3px 12px;border-radius:6px;cursor:pointer;transition:color .12s,background .12s,border-color .12s}.fv-new-btn:hover{background:var(--blue);color:var(--paper);border-color:var(--blue)}.fv-new-modal{width:min(520px,92vw);overflow:visible}.fv-new-modal .legend-body{padding:0}.fv-hint{font-family:var(--mono);font-size:10.5px;color:var(--muted)}.fv-notice{padding:6px 16px;font-family:var(--mono);font-size:11px;color:var(--green);background:color-mix(in srgb,var(--green) 8%,var(--paper));border-bottom:1px solid var(--line)}.fv-error{margin:4px 0 8px;font-family:var(--mono);font-size:11px;color:var(--red)}.fv-actions .fv-error{margin:0}.fv-compose{margin-top:12px;padding-top:10px;border-top:1px dashed var(--line)}.fv-compose .fv-textarea{resize:none;min-height:56px;max-height:38vh}.fv-new-form{display:flex;flex-direction:column;gap:8px;padding:12px 16px;border-bottom:1px solid var(--line);background:var(--panel2)}.fv-store-pick{display:flex;align-items:center;gap:8px;align-self:flex-start;font-family:var(--mono);font-size:10.5px;color:var(--muted)}.fv-store-pick select{font-family:var(--mono);font-size:11px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:4px;padding:3px 7px}.fv-input,.fv-textarea{width:100%;box-sizing:border-box;font-family:var(--mono);font-size:12px;color:var(--ink);background:var(--paper);border:1px solid var(--line);border-radius:4px;padding:6px 8px;resize:vertical}.fv-input:focus,.fv-textarea:focus{outline:none;border-color:var(--blue)}.fv-tawrap{position:relative}.fv-tawrap .fv-textarea{display:block}.fv-actions{display:flex;align-items:center;gap:10px;margin-top:6px}.fv-cancel{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--muted);background:var(--paper);border:1px solid var(--line);padding:4px 12px;border-radius:4px;cursor:pointer}.fv-cancel:hover{color:var(--orange);border-color:var(--orange)}.fv-cancel:disabled{opacity:.5;cursor:default}.fv-send{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--paper);background:var(--blue);border:1px solid var(--blue);padding:4px 14px;border-radius:4px;cursor:pointer}.fv-cancel+.fv-send{margin-left:0}.fv-send:disabled{opacity:.5;cursor:default}.si-content{flex:1;min-width:0;display:flex;flex-direction:column;background:var(--paper)}.si-content.is-new{align-items:center;justify-content:center}.si-new-center{width:min(560px,86%);display:flex;flex-direction:column;align-items:center;gap:12px;margin-bottom:12vh;animation:si-rise .18s ease}.si-avatar{font-size:30px;color:var(--blue);letter-spacing:2px}.si-ask{font-size:15px;color:var(--ink2)}.si-inputwrap{position:relative;width:100%;display:flex;border:1px solid var(--blue);border-radius:6px;background:var(--panel);padding:10px 12px;transition:height .12s ease}.mention-menu{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:5;margin:0;padding:4px;list-style:none;max-height:248px;overflow-y:auto;background:var(--paper);border:1px solid var(--blue);border-radius:4px;box-shadow:0 6px 18px #07364229;animation:si-rise .12s ease}.mention-menu.up{top:auto;bottom:calc(100% + 4px)}.mention-menu.fixed{position:fixed;top:auto;z-index:70}.mention-head{padding:3px 8px 5px;font-size:10.5px;color:var(--muted);letter-spacing:.02em;border-bottom:1px solid var(--line);margin-bottom:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mention-item{display:flex;align-items:baseline;gap:8px;padding:4px 8px;border-radius:3px;cursor:pointer;font-size:12px;white-space:nowrap}.mention-item.on{background:var(--panel2)}.mention-item.new .mention-id{color:var(--green);font-style:italic}.mention-dot{width:6px;height:6px;border-radius:50%;flex:none;align-self:center}.mention-id{color:var(--blue);flex:none}.mention-id .mention-hit{font-weight:700}.mention-path{color:var(--muted);font-size:11px;min-width:0;overflow:hidden;text-overflow:ellipsis}.slash-name{color:var(--blue);flex:none}.slash-name .mention-hit{font-weight:700}.slash-desc{color:var(--muted);font-size:11px;min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis}.slash-src{color:var(--muted);font-size:10.5px;flex:none;opacity:.7}.slash-src.src-user{color:var(--green);opacity:.85}.slash-src.src-project{color:var(--cyan);opacity:.85}.slash-src.src-skill{color:var(--magenta);opacity:.85}.slash-name.ui{color:var(--sc);font-weight:700}.slash-src.src-ui{color:var(--sc);opacity:.9}.slash-src.src-mutating{color:var(--yellow);opacity:.85}.slash-src.src-report{color:var(--cyan);opacity:.85}.si-input{flex:1;min-width:0;resize:none;background:transparent;border:none;outline:none;color:var(--ink2);font-family:var(--mono);font-size:13px;line-height:1.5;max-height:180px;overflow-y:hidden;transition:height .12s ease}.si-input::placeholder{color:var(--muted)}.si-hint{font-size:11px;color:var(--muted)}.si-hint code{color:var(--blue);background:var(--panel2);padding:0 4px;border-radius:3px}.si-agent-glyph{width:16px;height:16px;display:block;fill:currentColor}.si-launcher-picker{display:inline-flex;align-items:center;gap:6px}.si-launcher-label{font-size:11px;color:var(--ink2);text-transform:lowercase}.si-launcher-harness{display:inline-flex;align-items:center;color:var(--ink2)}.si-launcher-select{height:28px;padding:0 8px;cursor:pointer;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:999px;font-size:12px;transition:border-color .12s ease}.si-launcher-select:hover{border-color:var(--blue)}.si-content.is-session .si-term-body{animation:si-expand .22s ease}.si-content.is-session{--si-dock-h: 44px}.si-tabbar{flex:none;display:flex;align-items:stretch;gap:8px;padding:0 10px;background:var(--panel);border-bottom:1px solid var(--line)}.si-tabs{flex:none;display:flex;gap:2px}.si-tab{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:none;cursor:pointer;padding:8px 14px;font-family:var(--mono);font-size:12px;color:var(--muted);border-bottom:2px solid transparent;margin-bottom:-1px}.si-tab:hover{color:var(--ink2)}.si-tab.on{color:var(--ink2);border-bottom-color:var(--blue)}.si-th-name{flex:1;min-width:0;align-self:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--mono);font-size:12px;color:var(--ink2)}.si-term-body{flex:1;min-height:0;overflow:hidden;padding:6px 10px 0;margin-bottom:var(--si-dock-h);background:var(--term-bg)}.si-line{font-size:12px;line-height:1.6;color:var(--ink2);white-space:pre-wrap;word-break:break-word}.si-line.muted{color:var(--muted)}.si-op{color:var(--blue);font-weight:700}.si-bottom{position:absolute;left:0;right:0;bottom:0;z-index:2;display:flex;align-items:flex-end;gap:8px;min-height:44px;box-sizing:border-box;padding:11px 14px 12px;border-top:1px solid var(--line);background:var(--panel)}.si-prompt{color:var(--blue);flex:none;line-height:20px}.si-bottom .si-input:disabled{color:var(--muted)}.si-bottom.err{border-color:var(--orange)}.si-send-err{flex:none;color:var(--orange);font-size:11px;white-space:nowrap}.si-attach{flex:none;align-self:flex-end;display:inline-flex;align-items:center;background:transparent;border:none;cursor:pointer;color:var(--muted);line-height:1.5;padding:0 2px}.si-attach:hover:not(:disabled){color:var(--blue)}.si-attach:disabled{opacity:.5;cursor:default}.si-attach-busy{animation:si-attach-spin .8s linear infinite}@keyframes si-attach-spin{to{transform:rotate(360deg)}}.si-inputwrap.dragover,.si-bottom.dragover{border-color:var(--green)}.si-attach-err{flex:none;align-self:center;color:var(--orange);font-size:11px;white-space:nowrap}@keyframes si-rise{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}@keyframes si-expand{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.st-wrap{position:relative;width:100%;height:100%}.st-host{width:100%;height:100%;overflow:hidden}.st-copyhint{position:absolute;right:8px;bottom:7px;pointer-events:none;z-index:2;font-family:var(--mono);font-size:11px;line-height:1.25;letter-spacing:.2px;color:#cfe8e8;opacity:.85;background:#073642eb;border:1px solid rgba(131,148,150,.35);padding:3px 8px;border-radius:5px;transition:opacity .15s ease,color .15s ease,background .15s ease,border-color .15s ease}.st-wrap:hover .st-copyhint{opacity:1}.st-copyhint.copied{opacity:1;color:#fdf6e3;background:#859900f2;border-color:#859900f2}.st-copyhint.reconnecting{opacity:1;color:#fdf6e3;background:#cb4b16f2;border-color:#cb4b16f2;animation:st-recon-pulse 1.1s ease-in-out infinite}@keyframes st-recon-pulse{0%,to{opacity:1}50%{opacity:.55}}.st-host .xterm{width:100%!important;height:100%!important;background:var(--term-bg)!important}.st-host .xterm-screen{background:var(--term-bg)!important}.st-host .xterm-viewport{overflow-y:auto!important;overflow-x:hidden!important;background:var(--term-bg)!important}.si-bottom .si-input{color:var(--ink2);min-height:20px;line-height:20px;padding:0}.si-bottom .si-attach{height:20px;line-height:20px}.si-actions{margin-left:auto;display:flex;align-items:center;gap:6px}.si-act{background:var(--paper);border:1px solid var(--line);color:var(--ink2);border-radius:4px;padding:1px 9px;cursor:pointer;font-family:var(--mono);font-size:11px}.si-act:hover,.si-act.go{border-color:var(--blue);color:var(--blue)}.si-act.go:hover{background:var(--blue);color:var(--paper)}.si-act.big{padding:8px 18px;font-size:13px;margin-top:8px}.sc-red{--sc: var(--red)}.sc-green{--sc: var(--green)}.sc-yellow{--sc: var(--yellow)}.sc-cyan{--sc: var(--cyan)}.sc-muted{--sc: var(--muted)}.si-act.ui{border-color:var(--sc);color:var(--sc)}.si-act.ui:hover{background:var(--sc);color:var(--paper);border-color:var(--sc)}.si-act.ui.type.on{background:var(--sc);color:var(--paper)}.si-act.ui.type.suggest{animation:si-type-pulse 1.1s ease-in-out infinite}@keyframes si-type-pulse{0%,to{box-shadow:0 0 #b5890080}50%{box-shadow:0 0 0 3px #b5890000}}.si-bottom.type{border-color:var(--yellow);background:var(--panel2);cursor:pointer;gap:10px}.si-type-ind{color:var(--yellow);font-weight:700;flex:none}.si-type-help{color:var(--muted);font-size:11px}.si-offline{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;text-align:center;padding:24px}.si-offline-msg{font-size:15px;color:var(--ink2)}.si-offline-sub{font-size:12px;color:var(--muted);max-width:460px;line-height:1.6}.si-offline-sub code{color:var(--blue);background:var(--panel2);padding:0 4px;border-radius:3px}.si-offline-err{margin-top:10px;font-size:12px;color:var(--yellow);max-width:460px;line-height:1.55;border:1px solid var(--yellow);border-radius:4px;padding:8px 10px;background:var(--panel2)}.m-app{position:fixed;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;flex-direction:column;background:var(--paper);color:var(--ink);font-family:var(--mono);-webkit-tap-highlight-color:transparent}.m-main{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;overscroll-behavior:contain;padding-top:env(safe-area-inset-top,0)}.m-app .m-timeline,.m-app .m-children,.m-app .m-sesslist,.m-app .m-sessdetail,.m-app .m-pane-host .pane-doc,.m-app .m-pane-host .pane-hist,.m-app .m-pane-host .pane-issues,.m-app .m-pane-host .pane-eval,.m-app .m-pane-host .pane-edit{overflow-x:hidden}.m-app pre,.m-app code,.m-app .diff-line,.m-app .hist-diff pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;max-width:100%}.m-app .ev-diff{overflow-x:hidden}.m-app .ev-diff .dl{white-space:pre-wrap;overflow-wrap:anywhere}.m-app .m-ev-from{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.m-app img,.m-app video{max-width:100%;height:auto}.m-specs{flex:1;min-height:0;display:flex;flex-direction:column}.m-crumbs{flex:none;display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:7px 10px;border-bottom:1px solid var(--line);background:var(--paper)}.m-crumb{display:inline-flex;align-items:center;gap:2px}.m-crumb-sep{color:var(--muted)}.m-crumb-btn{background:none;border:none;padding:2px 3px;font-family:var(--mono);font-size:12px;color:var(--muted);cursor:pointer}.m-crumb-btn.cur{color:var(--ink2);font-weight:700}.m-node{flex:1;min-height:0;display:flex;flex-direction:column}.m-node-head{flex:none;display:flex;align-items:center;flex-wrap:wrap;gap:6px 8px;padding:12px 14px 8px}.m-dot{flex:none;width:9px;height:9px;border-radius:50%}.m-node-title{font-size:16px;font-weight:700;color:var(--ink2)}.m-node-ver{font-size:12px;color:var(--muted)}.m-node-status{font-size:11px;color:var(--ink);padding:1px 6px;border:1px solid var(--line);border-radius:999px}.m-node-faces{display:inline-flex;gap:2px}.m-node-desc{flex:none;padding:0 14px 10px;color:var(--ink);font-size:13px;line-height:1.5}.m-tabs{flex:none;display:flex;flex-wrap:wrap;gap:6px;padding:0 10px 8px;border-bottom:1px solid var(--line)}.m-tab{flex:none;padding:6px 12px;min-height:32px;cursor:pointer;font-family:var(--mono);font-size:12px;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:999px}.m-tab.on{color:var(--paper);background:var(--blue);border-color:var(--blue)}.m-pane-host{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.m-pane-host>*{flex:1;min-height:0}.m-pane-host.fixed .pane-hist{height:100%}.m-pane-host .pane-doc{overflow:auto;-webkit-overflow-scrolling:touch;padding:14px}.m-pane-host .pane-doc>h1,.m-pane-host .pane-doc>blockquote{display:none}.m-pane-host .pane-issues,.m-pane-host .pane-edit{-webkit-overflow-scrolling:touch}.m-children{overflow:auto;-webkit-overflow-scrolling:touch}.m-row{display:flex;align-items:center;gap:9px;width:100%;min-height:52px;padding:10px 14px;text-align:left;cursor:pointer;background:var(--paper);border:none;border-bottom:1px solid var(--line);font-family:var(--mono);color:var(--ink)}.m-row:active{background:var(--panel)}.m-row[disabled]{opacity:.5}.m-row-title{flex:1;min-width:0;font-size:14px;color:var(--ink2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.m-row-faces{flex:none;display:inline-flex;gap:2px}.m-row-ver{flex:none;font-size:11px;color:var(--muted)}.m-row-kids{flex:none;font-size:11px;color:var(--blue)}.m-row-chev{flex:none;color:var(--muted);font-size:18px}.m-tabbar{flex:none;display:flex;border-top:1px solid var(--line);background:var(--panel);padding-bottom:env(safe-area-inset-bottom,0)}.m-tabbar-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;height:52px;cursor:pointer;background:none;border:none;font-family:var(--mono);font-size:13px;color:var(--muted)}.m-tabbar-btn.on{color:var(--blue);font-weight:700;box-shadow:inset 0 2px 0 var(--blue)}.m-tabbar-ico{font-size:13px}.m-tabbar-badge{min-width:16px;padding:0 4px;border-radius:999px;background:var(--orange);color:var(--paper);font-size:10px;line-height:16px;text-align:center}.m-sesslist{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch}.m-sess-row{display:flex;flex-wrap:nowrap;align-items:center;gap:8px;width:100%;padding:12px 14px;text-align:left;cursor:pointer;font-size:14px;color:var(--ink2);background:var(--paper);border:none;border-bottom:1px solid var(--line);font-family:var(--mono)}.m-sess-row:active{background:var(--panel)}.m-sess-row .sess-id{flex:1;min-width:0;font-weight:700;color:var(--ink2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.m-sess-row .sess-meta{flex:none;width:auto;min-width:0;margin-top:0;margin-left:auto;font-size:12px}.m-sess-row .sess-glyph{font-size:13px;width:14px}.m-sess-row .sess-ops{color:var(--muted)}.m-zone{padding:10px 14px 4px;font-size:10px;font-weight:700;letter-spacing:.5px;color:var(--muted);text-transform:uppercase}.m-sessdetail{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch}.m-sessdetail.chat{display:flex;flex-direction:column;overflow:hidden}.m-sess-card{flex:none;display:flex;align-items:center;gap:10px;padding:12px 14px 12px 6px;border-bottom:1px solid var(--line)}.m-sess-back{flex:none;width:36px;height:36px;display:flex;align-items:center;justify-content:center;font-size:26px;line-height:1;color:var(--blue);background:none;border:none;cursor:pointer}.m-sess-meta{display:flex;flex-direction:column;gap:2px;min-width:0}.m-sess-name{font-size:16px;font-weight:700;color:var(--ink2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.m-sess-status{font-size:12px;color:var(--muted)}.m-sess-id8{color:var(--muted)}.m-timeline{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:10px 14px}.m-day{text-align:center;margin:10px 0 6px;font-size:10px;letter-spacing:.5px;color:var(--muted);text-transform:uppercase}.m-ev{margin:8px 0}.m-ev-head{display:flex;align-items:baseline;gap:7px;font-family:var(--mono);font-size:12px}.m-ev-glyph{flex:none}.m-ev-word{font-weight:700}.m-ev-time{margin-left:auto;flex:none;font-size:10px;color:var(--muted)}.m-ev-note{margin:4px 0 0 14px;padding:8px 10px;font-size:13px;line-height:1.55;color:var(--ink2);white-space:pre-wrap;overflow-wrap:anywhere;background:var(--panel);border-left:2px solid var(--line);border-radius:0 6px 6px 0}.m-ev-sent .m-ev-from{font-weight:700;color:var(--blue)}.m-ev-text{margin:4px 0 0 14px;padding:8px 10px;font-size:13px;line-height:1.55;color:var(--ink);white-space:pre-wrap;overflow-wrap:anywhere;background:color-mix(in srgb,var(--blue) 7%,var(--paper));border-left:2px solid color-mix(in srgb,var(--blue) 45%,var(--line));border-radius:0 6px 6px 0}.m-ev-prompt summary{font-family:var(--mono);font-size:12px;color:var(--muted);cursor:pointer}.m-offline,.m-senderr{flex:none;padding:6px 14px;font-size:12px}.m-offline{color:var(--muted);background:var(--panel);border-top:1px solid var(--line)}.m-senderr{color:var(--red)}.m-composer{flex:none;padding:8px 14px 10px;border-top:1px solid var(--line);background:var(--panel)}.m-composer-line{display:flex;align-items:flex-end;gap:8px}.m-input{flex:1;min-width:0;resize:none;padding:9px 10px;font-family:var(--mono);font-size:14px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:8px;outline:none}.m-input:focus{border-color:var(--blue)}.m-send{flex:none;height:38px;padding:0 16px;font-family:var(--mono);font-size:13px;font-weight:700;color:var(--paper);background:var(--blue);border:none;border-radius:8px;cursor:pointer}.m-send:disabled{opacity:.45;cursor:default}.m-empty{padding:18px 14px;color:var(--muted);font-size:13px}.m-empty.big{flex:1;display:flex;align-items:center;justify-content:center;text-align:center}.m-new-btn{display:flex;align-items:center;gap:8px;width:100%;padding:12px 14px;font-family:var(--mono);font-size:14px;font-weight:700;color:var(--blue);text-align:left;background:var(--paper);border:none;border-bottom:1px solid var(--line);cursor:pointer}.m-new-btn:active{background:var(--panel)}.m-new-btn-plus{flex:none;font-size:16px;line-height:1}.m-new-body{display:flex;flex-direction:column;gap:12px;padding:14px}.m-new-input{min-height:120px}.m-new-launcher{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--muted)}.m-new-launcher-select{flex:1;min-width:0;padding:8px 10px;font-family:var(--mono);font-size:14px;color:var(--ink2);background:var(--paper);border:1px solid var(--line);border-radius:8px}.m-new .m-senderr{padding:0}.m-new-go{height:42px}.pane-eval{flex:1;min-width:0;padding:16px 20px;overflow:auto}.pane-eval.empty{color:var(--muted);font-size:13px}.eval-todo-note{color:var(--muted);font-size:13px;margin:0 0 14px}.eval-declared-row .eval-expected{padding-left:23px;margin:4px 0 0}.eval-tracks{color:var(--muted);font-family:var(--mono);font-size:10px;opacity:.85}.eval-dangling-badge{color:var(--orange);margin-right:6px}.eval-dangling-name{text-decoration:line-through;color:var(--muted)}.eval-dangling-tag{font-family:var(--mono);font-size:10px;color:var(--orange);margin-left:8px;border:1px dashed color-mix(in srgb,var(--orange) 45%,var(--line));border-radius:3px;padding:0 5px}.eval-dangling-remarks{padding-left:23px;margin:6px 0 0}.eval-row{border-bottom:1px solid var(--line);padding:10px 0}.eval-row:last-child{border-bottom:0}.eval-head{display:flex;flex-direction:column;gap:4px;width:100%;text-align:left;background:none;border:0;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer}.eval-top{display:flex;align-items:center;gap:8px}.eval-caret{flex:none;color:var(--muted);font-size:11px}.eval-scenario{font-size:13px;font-weight:600;color:var(--ink2)}.eval-meta{display:flex;align-items:center;gap:10px;font-size:11px;color:var(--muted)}.eval-sha{color:var(--blue);background:var(--panel2);padding:0 4px;border-radius:3px}.eval-verdict{flex:none;font-size:10px;font-weight:700;border-radius:3px;padding:0 4px;line-height:1.5}.eval-verdict.pass{color:var(--green);border:1px solid var(--green)}.eval-verdict.fail{color:var(--orange);border:1px solid var(--orange)}.eval-verdict.note{color:var(--blue);border:1px solid var(--blue);cursor:help}.eval-verdict.legacy{color:var(--muted);border:1px solid var(--line)}.eval-shot{margin:8px 0 2px;padding-left:19px}.eval-shot img,.eval-video{display:block;max-width:100%;background:var(--panel2);border:1px solid var(--line);border-radius:4px}.eval-noimg{font-size:11px;color:var(--muted)}.eval-expected,.eval-note{font-size:12px;color:var(--ink2);margin:0 0 6px}.eval-expected-label{color:var(--muted);font-weight:600}.eval-transcript{font-family:var(--mono);font-size:11px;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:8px 10px;margin:0;max-height:360px;overflow:auto;white-space:pre-wrap;word-break:break-word}.eval-transcript.loading{color:var(--muted)}.eval-datawrap{border:1px solid var(--line);border-left:3px solid var(--acc, #6b46c1);border-radius:4px;overflow:hidden}.eval-datahead{font-family:var(--mono);font-size:10px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);background:var(--panel);padding:4px 10px;border-bottom:1px solid var(--line)}.eval-data{font-family:var(--mono);font-size:11px;color:var(--ink2);background:var(--panel2);padding:8px 10px;margin:0;max-height:360px;overflow:auto;white-space:pre-wrap;word-break:break-word}.eval-data.loading{color:var(--muted)}.ef-chipbar{display:flex;gap:6px;flex-wrap:wrap}.ef-chip{font:inherit;font-size:11px;color:var(--muted);background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:3px 10px;cursor:pointer;line-height:1.35;transition:color .12s,border-color .12s,background .12s}.ef-chip:hover{color:var(--ink2);border-color:color-mix(in srgb,var(--blue) 45%,var(--line))}.ef-chip.on{color:var(--paper);background:var(--blue);border-color:var(--blue)}.ef-close{font:inherit;color:var(--muted);background:none;border:0;cursor:pointer;padding:2px 6px}.ef-empty,.ef-miss{color:var(--muted);font-size:12px;padding:18px 8px}.ef-scenario{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;font-size:12px}.ef-node{flex:0 1 auto;max-width:58px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:10.5px;opacity:.9}.ef-kind{flex:none;margin-left:auto;font-size:11px;color:var(--muted)}.ef-time{flex:none;color:var(--muted);font-size:10.5px;min-width:26px;text-align:right}.ef-okd{flex:none;color:var(--green);font-size:11px}.ef-okbtn{flex:none;font-size:10.5px;color:var(--muted);border:1px solid var(--line);border-radius:4px;padding:0 5px;line-height:1.5;opacity:0;transition:opacity .12s,color .12s,border-color .12s}.ef-row:hover .ef-okbtn,.ef-row.sel .ef-okbtn{opacity:1}.ef-okbtn:hover{color:var(--green);border-color:color-mix(in srgb,var(--green) 55%,var(--line))}.an-detail{height:100%;min-height:0;display:flex;flex-direction:column;padding:0}.an-head{flex:none;display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 16px;border-bottom:1px solid var(--line);background:var(--panel2)}.an-work{flex:1 1 auto;min-height:0;display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,340px)}.an-stage-col{min-width:0;min-height:0;overflow-y:auto;padding:12px 16px 20px}.an-rail{min-width:0;min-height:0;display:flex;flex-direction:column;border-left:1px solid var(--line);background:var(--panel)}.an-rail .an-comments-head{flex:none;padding:10px 12px 8px;border-bottom:1px solid var(--line)}.an-rail-list{flex:1 1 auto;min-height:0;overflow-y:auto;padding:4px 12px 8px}.an-rail-list .fv-reply{margin-top:8px}.an-rail-compose{flex:none;border-top:1px solid var(--line);padding:0 12px 10px;background:var(--panel)}.an-rail-compose .fv-compose{margin-top:8px;padding-top:0;border-top:none}@media (max-width: 900px){.an-detail{height:auto}.an-work{display:block}.an-stage-col{overflow:visible}.an-rail{border-left:none;border-top:1px solid var(--line)}.an-rail-list{overflow:visible}.an-rail-compose{position:sticky;bottom:0;z-index:2}}.an-meta{font-size:10.5px;color:var(--muted)}.an-image{display:block;max-width:100%;border:1px solid var(--line);border-radius:6px;background:var(--panel2);cursor:zoom-in}.lightbox{position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;display:flex;align-items:center;justify-content:center;background:#000000d1;cursor:zoom-out}.lightbox img{max-width:96vw;max-height:96vh;object-fit:contain;border-radius:4px;box-shadow:0 8px 48px #0009}.an-prior-note{color:var(--muted)}.an-stale{color:var(--muted);font-family:var(--mono);font-size:11px}.an-stale b{color:var(--ink2);font-weight:700}.an-stale-files{opacity:.85}.an-title{font-weight:700;font-size:14px;color:var(--ink)}.an-node{font-size:11px;color:var(--muted)}.an-expected{font-size:12px;color:var(--ink2);padding:8px 0 0}.an-comments-head{font-size:10px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--ink2)}.an-stage-col>*{max-width:1100px}.an-gallery{display:flex;flex-direction:column;gap:10px;margin:12px 0 0}.an-detail>.an-expected,.an-detail>.an-hint{padding:8px 16px 0}.an-stage{position:relative;margin:12px 0 0;cursor:crosshair}.an-video{display:block;width:100%;background:#000;border:1px solid var(--line);border-radius:6px 6px 0 0}.an-rect{position:absolute;border:2px solid var(--orange);border-radius:3px;background:#ff963c1f;pointer-events:none}.an-rect.live{border-style:dashed}.an-bigplay{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:54px;height:54px;border-radius:50%;background:#080c1280;border:1px solid rgba(255,255,255,.22);display:flex;align-items:center;justify-content:center;font-size:22px;color:#fff;pointer-events:none;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.an-bar{display:flex;align-items:center;gap:10px;padding:6px 8px;background:var(--panel2);border:1px solid var(--line);border-top:none;border-radius:0 0 6px 6px}.an-play{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:30px;height:26px;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:5px;cursor:pointer}.an-play:hover{border-color:var(--blue)}.an-fs{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:30px;height:26px;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:5px;cursor:pointer}.an-fs:hover{border-color:var(--blue)}.an-player:fullscreen{display:flex;flex-direction:column;align-items:center;justify-content:center;background:#000}.an-player:fullscreen .an-stage{margin:0;max-width:100vw;max-height:calc(100vh - 38px)}.an-player:fullscreen .an-video{width:auto;max-width:100vw;max-height:calc(100vh - 38px);border:none;border-radius:0}.an-player:fullscreen .an-bar{width:min(100vw,1100px);border-radius:0}.an-player:-webkit-full-screen{display:flex;flex-direction:column;align-items:center;justify-content:center;background:#000}.an-player:-webkit-full-screen .an-stage{margin:0;max-width:100vw;max-height:calc(100vh - 38px)}.an-player:-webkit-full-screen .an-video{width:auto;max-width:100vw;max-height:calc(100vh - 38px);border:none;border-radius:0}.an-player:-webkit-full-screen .an-bar{width:min(100vw,1100px);border-radius:0}.an-time{flex:0 0 auto;font-family:var(--mono);font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums}.an-curstep{flex:0 0 auto;font-size:10.5px;color:var(--green);background:color-mix(in srgb,var(--green) 12%,transparent);border:1px solid color-mix(in srgb,var(--green) 35%,var(--line));border-radius:3px;padding:1px 7px;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.an-seek{flex:1 1 auto;position:relative;height:18px;display:flex;align-items:center;cursor:pointer}.an-seek-trk{position:absolute;left:0;right:0;height:5px;background:var(--line);border-radius:5px}.an-seek-play{position:absolute;left:0;height:5px;background:var(--blue);border-radius:5px;transition:width .12s linear}.an-knob{position:absolute;width:12px;height:12px;background:var(--ink);border:2px solid var(--paper);border-radius:50%;transform:translate(-50%);box-shadow:0 1px 4px #0006;transition:left .12s linear}.an-band{position:absolute;top:2px;width:2px;height:14px;background:color-mix(in srgb,var(--green) 45%,transparent);transform:translate(-50%);pointer-events:none}.an-mk{position:absolute;top:-1px;width:10px;height:20px;padding:0;transform:translate(-50%);background:none;border:none;cursor:pointer}.an-mk:after{content:"";display:block;margin:0 auto;width:3px;height:20px;border-radius:2px;background:var(--orange);box-shadow:0 0 0 1px var(--paper);transition:transform .12s,background .12s}.an-mk:hover:after{transform:scaleX(1.8)}.an-mk.active:after{background:var(--green)}.an-mk.on:after{transform:scaleX(2);background:var(--orange);box-shadow:0 0 0 1px var(--paper),0 0 8px color-mix(in srgb,var(--orange) 60%,transparent)}.an-seek-hov{position:absolute;bottom:22px;transform:translate(-50%);background:var(--panel);border:1px solid var(--line);color:var(--ink);font-family:var(--mono);font-size:10.5px;padding:1px 6px;border-radius:4px;pointer-events:none;white-space:nowrap}.an-keys{font-family:var(--mono);font-size:10px;color:var(--muted);padding:4px 0 0;letter-spacing:.2px}.an-ruler{display:flex;flex-wrap:wrap;gap:4px;padding:8px 0 0}.an-step{font:inherit;font-size:11px;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:2px 8px;cursor:pointer}.an-step.on{border-color:var(--green);color:var(--ink)}.an-hint{color:var(--muted);font-size:11px;padding:8px 0 0}.an-flash{font-size:12px;color:var(--green);padding:4px 0 0}.an-verdict-badge{font-size:12px;font-weight:700;line-height:1}.an-verdict-badge.pass{color:var(--green)}.an-verdict-badge.fail{color:var(--orange)}.an-verdict-badge.legacy{color:var(--muted)}.an-okd{font-size:11px;color:var(--green);white-space:nowrap}.an-okbtn{font:inherit;font-size:11px;color:var(--ink2);background:var(--panel);border:1px solid var(--line);border-radius:5px;padding:2px 8px;cursor:pointer;white-space:nowrap;transition:color .12s,border-color .12s}.an-okbtn:hover{color:var(--green);border-color:color-mix(in srgb,var(--green) 55%,var(--line))}.an-ab{display:flex;align-items:center;gap:8px;margin-left:auto}.an-ab-nav{display:inline-flex;align-items:center;justify-content:center;line-height:1;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:2px 6px;cursor:pointer}.an-ab-nav:disabled{opacity:.35;cursor:default}.an-ab-track{display:flex;flex-wrap:wrap;gap:3px;align-items:center}.an-ab-pip{font:inherit;font-size:11px;font-weight:700;width:20px;height:20px;display:flex;align-items:center;justify-content:center;background:var(--panel2);border:1px solid var(--line);border-radius:4px;cursor:pointer;color:var(--muted)}.an-ab-pip.pass{color:var(--green)}.an-ab-pip.fail{color:var(--orange)}.an-ab-pip.on{outline:2px solid var(--blue);outline-offset:0;border-color:var(--blue)}.an-ab-pos{font-size:10.5px;color:var(--muted);margin-left:2px}.se-pane{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--paper)}.se-gates{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 14px;border-bottom:1px solid var(--line);background:var(--panel2)}.se-gate{font-family:var(--mono);font-size:10.5px;border:1px solid var(--line);border-radius:3px;padding:1px 7px;color:var(--muted)}.se-gate.ok{color:var(--green);border-color:color-mix(in srgb,var(--green) 40%,var(--line))}.se-gate.bad{color:var(--red);border-color:color-mix(in srgb,var(--red) 40%,var(--line))}.se-export{margin-left:auto;display:inline-flex;align-items:center;color:var(--blue);text-decoration:none}.se-export:hover{color:color-mix(in srgb,var(--blue) 72%,var(--ink))}.se-blind .ef-scenario{color:var(--muted)}.se-warn{font-family:var(--mono);font-size:10px;color:var(--orange)}.ef-insession{color:var(--blue);font-size:10px;flex:none}.se-divider{display:flex;align-items:center;gap:8px;padding:5px 10px 3px;font-family:var(--mono);font-size:9px;letter-spacing:.8px;text-transform:uppercase;color:var(--muted)}.se-divider:before,.se-divider:after{content:"";flex:1;border-top:1px dashed var(--line)}.fv-filter{font-family:var(--mono);font-size:11px;line-height:1.35;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:3px 8px;cursor:pointer;transition:border-color .12s}.fv-filter:hover{border-color:color-mix(in srgb,var(--blue) 45%,var(--line))}.loading{display:flex;align-items:center;justify-content:center;gap:10px;height:100%;flex:1;color:var(--muted);font-family:var(--mono);font-size:13px}.loading.load-error{color:var(--red)}.load-retry{font-family:var(--mono);font-size:12px;color:var(--ink2);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:3px 10px;cursor:pointer}.load-retry:hover{border-color:var(--muted)}.chrono-tools{display:flex;justify-content:flex-end;margin:0 0 6px}.chrono-all{font-family:var(--mono);font-size:10px;color:var(--muted);background:none;border:1px solid var(--line);border-radius:4px;padding:2px 8px;cursor:pointer}.chrono-all:hover{color:var(--ink2);border-color:var(--muted)}.pane-filter{position:sticky;top:-16px;z-index:2;margin:-8px 0 8px;padding:6px 0;background:var(--panel)}.pane-filter-input{width:100%;box-sizing:border-box;background:var(--panel2);border:1px solid var(--line);border-radius:4px;outline:none;color:var(--ink2);font-family:var(--mono);font-size:11px;padding:4px 8px}.pane-filter-input:focus{border-color:var(--muted)}.pane-filter-input::placeholder{color:var(--muted)}.pane-filter-none{color:var(--muted);font-family:var(--mono);font-size:11px;padding:6px 2px}.ui-tip{position:fixed;z-index:100;pointer-events:none;background:var(--panel2);color:var(--ink2);border:1px solid var(--line);border-radius:6px;padding:4px 9px;font-family:var(--mono);font-size:11px;line-height:1.5;max-width:320px;white-space:pre-line;overflow-wrap:break-word;box-shadow:0 4px 16px #00000038;opacity:0;transition:opacity .14s ease,transform .14s ease}.ui-tip[data-place=top]{transform:translateY(3px)}.ui-tip[data-place=bottom]{transform:translateY(-3px)}.ui-tip.show{opacity:1;transform:translateY(0)}.ui-tip-arrow{position:absolute;width:8px;height:8px;background:var(--panel2);border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.ui-tip[data-place=top] .ui-tip-arrow{bottom:-4.5px;transform:rotate(45deg)}.ui-tip[data-place=bottom] .ui-tip-arrow{top:-4.5px;transform:rotate(225deg)}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{r as f,f as p,h as m}from"./index-BQu-oJ8J.js";import{V as d,W as y}from"./SessionWindow-JYbpPwNB.js";const $=(t,a,n)=>{const s=t.match(/^\/(\S+)\s*([\s\S]*)$/);if(!s)return t;const o=a.find(i=>i.name===s[1]);if(!o)return t;const e=[],r=s[2].replace(d,(i,h)=>(e.push(h),"")).trim(),u=e.length?e.map(i=>{const h=n.find(l=>l.id===i);return h?`- [[${h.id}]] — ${y(h.path)}`:`- [[${i}]]`}).join(`
|
|
2
|
+
`):"(No target was mentioned. If the prompt names the scope, use it; otherwise ask the human to define the scope before proceeding — unless this task needs no scope, in which case proceed.)",c=o.body.includes("{{targets}}")?o.body.replace("{{targets}}",u):`${o.body}
|
|
3
|
+
|
|
4
|
+
${u}`;return r?`${c}
|
|
5
|
+
|
|
6
|
+
${r}`:c};async function b(t,a){try{const n=await fetch("/api/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a?{prompt:t,launcher:a}:{prompt:t})}),s=await n.json().catch(()=>null);return{ok:n.ok,error:s==null?void 0:s.error}}catch{return{ok:!1}}}function k(){const[t,a]=f.useState([]),[n,s]=f.useState(()=>{try{return localStorage.getItem("si.launcher")||""}catch{return""}}),o=e=>{s(e);try{localStorage.setItem("si.launcher",e)}catch{}};return f.useEffect(()=>{p().then(e=>{const r=e==null?void 0:e.launchers;!Array.isArray(r)||!r.length||(a(r),s(u=>r.some(c=>c.name===u)?u:e.default&&r.some(c=>c.name===e.default)?e.default:r[0].name))}).catch(()=>{})},[]),{launchers:t,launcher:n,pickLauncher:o}}function E(){const[t,a]=f.useState([]);return f.useEffect(()=>{m().then(n=>{Array.isArray(n)&&a(n)}).catch(()=>{})},[]),t}export{E as a,$ as b,b as c,k as u};
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
} catch (e) { /* localStorage/matchMedia unavailable — default (light) stands */ }
|
|
20
20
|
})()
|
|
21
21
|
</script>
|
|
22
|
-
<script type="module" crossorigin src="/assets/index-
|
|
23
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
22
|
+
<script type="module" crossorigin src="/assets/index-BQu-oJ8J.js"></script>
|
|
23
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BbMkwuix.css">
|
|
24
24
|
</head>
|
|
25
25
|
<body>
|
|
26
26
|
<div id="root"></div>
|
package/spec-eval/src/cli.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { repoRoot, headSha, driftIndex, stagedFiles, git } from '../../spec-cli/
|
|
|
4
4
|
import { loadSpecs } from '../../spec-cli/src/specs.js'
|
|
5
5
|
import { loadConfig, sourceExtRe } from '../../spec-cli/src/lint.js'
|
|
6
6
|
import { mainBranch, envSessionId, readRawRecord } from '../../spec-cli/src/layout.js'
|
|
7
|
-
import { evalNodes, validateScenarios, resolveEvalNode, scenarioHash, EVAL_FILE, type EvalNode } from './scenarios.js'
|
|
7
|
+
import { evalNodes, validateScenarios, resolveEvalNode, scenarioHash, EVAL_FILE, type EvalNode, type ScenarioTestReference } from './scenarios.js'
|
|
8
8
|
import { readReadings, readSidecar, appendReading, appendRetraction, latestPerScenario, evidenceOf, isJsonBlob, type Reading, type Verdict, type Evidence, type EvidenceKind, type Retraction } from './sidecar.js'
|
|
9
9
|
import { staleAxes, contentProbeFor } from './freshness.js'
|
|
10
10
|
import { scenarioIndex } from './scenariofresh.js'
|
|
@@ -115,7 +115,7 @@ async function scan(args: string[] = []): Promise<number> {
|
|
|
115
115
|
if (y) {
|
|
116
116
|
// schema first: a malformed eval.md is the loudest gap — report each violation, then still scan its
|
|
117
117
|
// (leniently-parsed) scenarios for stale/missing so a typo doesn't mask a real freshness gap.
|
|
118
|
-
for (const e of validateScenarios(readFileSync(join(y.dir, EVAL_FILE), 'utf8'), cfg.scenarioTags)) {
|
|
118
|
+
for (const e of validateScenarios(readFileSync(join(y.dir, EVAL_FILE), 'utf8'), cfg.scenarioTags, root)) {
|
|
119
119
|
malformed++
|
|
120
120
|
findings.push(` • eval-schema: '${s.id}' ${e} — fix ${y.evalPath}`)
|
|
121
121
|
}
|
|
@@ -512,7 +512,7 @@ function checkStaged(): number {
|
|
|
512
512
|
for (const rel of staged.filter((p) => p === EVAL_FILE || p.endsWith('/' + EVAL_FILE))) {
|
|
513
513
|
const abs = join(root, rel)
|
|
514
514
|
if (!existsSync(abs)) continue // staged deletion — nothing to validate
|
|
515
|
-
const errs = validateScenarios(readFileSync(abs, 'utf8'), tagLibrary)
|
|
515
|
+
const errs = validateScenarios(readFileSync(abs, 'utf8'), tagLibrary, root)
|
|
516
516
|
if (!errs.length) continue
|
|
517
517
|
bad = true
|
|
518
518
|
console.error(`✗ SpexCode eval: ${rel} — invalid scenario schema:`)
|
|
@@ -578,6 +578,34 @@ export function formatTimeline(tl: EvalTimeline): string {
|
|
|
578
578
|
// (the collection view); a <node>/`.` scopes to one node. --unmeasured keeps only scenarios with NO
|
|
579
579
|
// effective reading — never measured, or every filing retracted — the blind-spot worklist a measuring
|
|
580
580
|
// hand picks from. Flag set closed, like every eval verb.
|
|
581
|
+
export type ScenarioListRow = {
|
|
582
|
+
node: string
|
|
583
|
+
scenario: string
|
|
584
|
+
tags?: string[]
|
|
585
|
+
test?: ScenarioTestReference
|
|
586
|
+
measured: boolean
|
|
587
|
+
latest?: { verdict?: Verdict; ts: string }
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export function scenarioListRows(nodes: EvalNode[], unmeasuredOnly = false): ScenarioListRow[] {
|
|
591
|
+
const rows: ScenarioListRow[] = []
|
|
592
|
+
for (const n of nodes) {
|
|
593
|
+
const latest = latestPerScenario(readReadings(n.sidecarPath))
|
|
594
|
+
for (const sc of n.scenarios) {
|
|
595
|
+
const r = latest.get(sc.name)
|
|
596
|
+
if (unmeasuredOnly && r) continue
|
|
597
|
+
rows.push({
|
|
598
|
+
node: n.id, scenario: sc.name,
|
|
599
|
+
...(sc.tags?.length ? { tags: sc.tags } : {}),
|
|
600
|
+
...(sc.test ? { test: sc.test } : {}),
|
|
601
|
+
measured: !!r,
|
|
602
|
+
...(r ? { latest: { ...(r.verdict ? { verdict: r.verdict } : {}), ts: r.ts } } : {}),
|
|
603
|
+
})
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return rows
|
|
607
|
+
}
|
|
608
|
+
|
|
581
609
|
async function scenarioLs(args: string[]): Promise<number> {
|
|
582
610
|
for (const a of args) {
|
|
583
611
|
if (!a.startsWith('--')) continue
|
|
@@ -598,21 +626,7 @@ async function scenarioLs(args: string[]): Promise<number> {
|
|
|
598
626
|
nodes = [res.node]
|
|
599
627
|
}
|
|
600
628
|
const unmeasuredOnly = has(args, 'unmeasured')
|
|
601
|
-
|
|
602
|
-
const rows: Row[] = []
|
|
603
|
-
for (const n of nodes) {
|
|
604
|
-
const latest = latestPerScenario(readReadings(n.sidecarPath))
|
|
605
|
-
for (const sc of n.scenarios) {
|
|
606
|
-
const r = latest.get(sc.name)
|
|
607
|
-
if (unmeasuredOnly && r) continue
|
|
608
|
-
rows.push({
|
|
609
|
-
node: n.id, scenario: sc.name,
|
|
610
|
-
...(sc.tags?.length ? { tags: sc.tags } : {}),
|
|
611
|
-
measured: !!r,
|
|
612
|
-
...(r ? { latest: { ...(r.verdict ? { verdict: r.verdict } : {}), ts: r.ts } } : {}),
|
|
613
|
-
})
|
|
614
|
-
}
|
|
615
|
-
}
|
|
629
|
+
const rows = scenarioListRows(nodes, unmeasuredOnly)
|
|
616
630
|
if (has(args, 'json')) { console.log(JSON.stringify(rows, null, 2)); return 0 }
|
|
617
631
|
if (!rows.length) {
|
|
618
632
|
console.log(`spex eval scenario ls: ${unmeasuredOnly ? 'no unmeasured scenarios' : 'no scenarios declared'}${sel ? ` on '${nodes[0]?.id ?? sel}'` : ''}`)
|
package/spec-eval/src/evaltab.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { relative, dirname } from 'node:path'
|
|
|
2
2
|
import { repoRoot, driftIndex, historyIndex, type DriftIndex, type HistoryIndex } from '../../spec-cli/src/git.js'
|
|
3
3
|
import { loadSpecs } from '../../spec-cli/src/specs.js'
|
|
4
4
|
import { loadEvalRemarkTracks, trackKey, type RemarkTrack, type Issue, type Reply } from '../../spec-cli/src/issues.js'
|
|
5
|
-
import { evalNodes, type EvalNode } from './scenarios.js'
|
|
5
|
+
import { evalNodes, type EvalNode, type ScenarioTestReference } from './scenarios.js'
|
|
6
6
|
import { readSidecar, applyRetractions, evidenceOf, isJsonBlob, humanOkFor, type Verdict, type EvidenceKind, type Retraction } from './sidecar.js'
|
|
7
7
|
import { staleAxes, codeDrift, contentProbeFor, type StaleAxis } from './freshness.js'
|
|
8
8
|
import { scenarioIndex, type ScenarioIndex } from './scenariofresh.js'
|
|
@@ -91,7 +91,7 @@ function toRemarkView(rm: Reply, threadId: string, dangling: boolean): RemarkVie
|
|
|
91
91
|
// at NODE level. `scenario` is the orphaned name (rendered struck-through / gone); `remarks` are all dangling.
|
|
92
92
|
export type DanglingTrack = { scenario: string; threadId: string; thread: Issue; remarks: RemarkView[] }
|
|
93
93
|
|
|
94
|
-
export type ScenarioInfo = { name: string; expected: string; tags?: string[]; code?: string[] }
|
|
94
|
+
export type ScenarioInfo = { name: string; expected: string; tags?: string[]; test?: ScenarioTestReference; code?: string[] }
|
|
95
95
|
|
|
96
96
|
// `hasEvalFile` distinguishes a node that declares no scenarios (no eval.md) from one that declares some but
|
|
97
97
|
// has no readings yet — the tab says different things for each. `scenarios` is the declared set; `readings`
|
|
@@ -164,7 +164,8 @@ export async function evalTimeline(id: string, ctx?: EvalContext): Promise<EvalT
|
|
|
164
164
|
const threadFor = (scenario: string): Issue | undefined => tracks.get(trackKey(id, scenario))?.thread
|
|
165
165
|
const scenarios: ScenarioInfo[] = ynode.scenarios.map((s) => ({
|
|
166
166
|
name: s.name, expected: s.expected,
|
|
167
|
-
...(s.tags?.length ? { tags: s.tags } : {}), ...(s.
|
|
167
|
+
...(s.tags?.length ? { tags: s.tags } : {}), ...(s.test ? { test: s.test } : {}),
|
|
168
|
+
...(s.code?.length ? { code: s.code } : {}),
|
|
168
169
|
}))
|
|
169
170
|
// one raw sidecar read: the effective readings feed the scoreboard rows below; the retraction events ride
|
|
170
171
|
// along as the undo trace (newest-first, like the readings), the human-ok events as the sign-off overlay.
|
|
@@ -7,12 +7,17 @@ import { mintIds } from '../../spec-cli/src/specs.js'
|
|
|
7
7
|
export const EVAL_FILE = 'eval.md'
|
|
8
8
|
export const SIDECAR_FILE = 'evals.ndjson'
|
|
9
9
|
|
|
10
|
+
export type ScenarioTestReference = {
|
|
11
|
+
path: string
|
|
12
|
+
name?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
export type Scenario = {
|
|
11
16
|
name: string
|
|
12
17
|
description: string
|
|
13
18
|
expected: string
|
|
14
19
|
tags?: string[]
|
|
15
|
-
test?:
|
|
20
|
+
test?: ScenarioTestReference
|
|
16
21
|
code?: string[]
|
|
17
22
|
related?: string[]
|
|
18
23
|
}
|
|
@@ -28,10 +33,19 @@ export type EvalNode = {
|
|
|
28
33
|
const SCENARIO_KEYS = ['name', 'description', 'expected', 'tags', 'test', 'code', 'related'] as const
|
|
29
34
|
type ScenarioKey = (typeof SCENARIO_KEYS)[number]
|
|
30
35
|
const LIST_KEYS: readonly ScenarioKey[] = ['tags', 'code', 'related']
|
|
36
|
+
const TEST_KEYS = ['path', 'name'] as const
|
|
37
|
+
type TestKey = (typeof TEST_KEYS)[number]
|
|
38
|
+
|
|
39
|
+
type RawTestObject = {
|
|
40
|
+
fields: Partial<Record<TestKey, string>>
|
|
41
|
+
unknownKeys: string[]
|
|
42
|
+
duplicateKeys: string[]
|
|
43
|
+
malformed: string[]
|
|
44
|
+
}
|
|
31
45
|
|
|
32
46
|
// a raw scenario item straight off the frontmatter walk: the known fields it set, plus any UNKNOWN keys it
|
|
33
47
|
// carried — kept (not dropped) so the validator can name a typo'd field instead of silently swallowing it.
|
|
34
|
-
type RawItem = { fields: Partial<Record<ScenarioKey, string>>; unknownKeys: string[] }
|
|
48
|
+
type RawItem = { fields: Partial<Record<ScenarioKey, string>>; testObject?: RawTestObject; unknownKeys: string[] }
|
|
35
49
|
|
|
36
50
|
// tiny indentation parser for eval.md's frontmatter `scenarios:` block (no YAML dep), shared by parseScenarios and validateScenarios so they can't disagree; reports hasFrontmatter/hasKey so the validator can tell "none declared" from "malformed"
|
|
37
51
|
function walkScenarios(src: string): { hasFrontmatter: boolean; hasKey: boolean; items: RawItem[] } {
|
|
@@ -75,6 +89,32 @@ function assignField(cur: RawItem, kv: string, lines: string[], idx: number, key
|
|
|
75
89
|
const f = kv.match(/^([A-Za-z_][\w-]*):\s*(.*)$/)
|
|
76
90
|
if (!f) return idx
|
|
77
91
|
const key = f[1]
|
|
92
|
+
if (key === 'test') {
|
|
93
|
+
const raw = f[2].trim()
|
|
94
|
+
if (!raw) {
|
|
95
|
+
const parsed = emptyTestObject()
|
|
96
|
+
let childIndent = -1
|
|
97
|
+
let j = idx + 1
|
|
98
|
+
for (; j < lines.length; j++) {
|
|
99
|
+
const line = lines[j]
|
|
100
|
+
if (!line.trim()) continue
|
|
101
|
+
const indent = line.length - line.replace(/^\s+/, '').length
|
|
102
|
+
if (indent <= keyIndent) break
|
|
103
|
+
if (childIndent < 0) childIndent = indent
|
|
104
|
+
if (indent !== childIndent) {
|
|
105
|
+
parsed.malformed.push(`invalid nested test object entry \`${line.trim()}\``)
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
assignTestField(parsed, line.trim())
|
|
109
|
+
}
|
|
110
|
+
cur.testObject = parsed
|
|
111
|
+
return j - 1
|
|
112
|
+
}
|
|
113
|
+
if (raw.startsWith('{') || raw.endsWith('}')) {
|
|
114
|
+
cur.testObject = parseFlowTestObject(raw)
|
|
115
|
+
return idx
|
|
116
|
+
}
|
|
117
|
+
}
|
|
78
118
|
// a list field (`code:`/`related:`) may be a YAML block sequence (`- item` lines); the scalar reader can't see those, so collect them here into the comma form parseCodeList expects
|
|
79
119
|
if ((LIST_KEYS as readonly string[]).includes(key) && f[2].trim() === '') {
|
|
80
120
|
const items: string[] = []
|
|
@@ -118,6 +158,65 @@ function assignField(cur: RawItem, kv: string, lines: string[], idx: number, key
|
|
|
118
158
|
|
|
119
159
|
const unquote = (s: string) => s.replace(/^["'](.*)["']$/, '$1').trim()
|
|
120
160
|
|
|
161
|
+
const emptyTestObject = (): RawTestObject => ({ fields: {}, unknownKeys: [], duplicateKeys: [], malformed: [] })
|
|
162
|
+
|
|
163
|
+
function testValue(raw: string, opaque = false): string {
|
|
164
|
+
const value = raw.trim()
|
|
165
|
+
const quoted = value.match(/^(["'])([\s\S]*)\1$/)
|
|
166
|
+
return quoted ? quoted[2] : opaque ? value : unquote(value)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function assignTestField(obj: RawTestObject, entry: string): void {
|
|
170
|
+
const f = entry.match(/^([A-Za-z_][\w-]*):\s*(.*)$/)
|
|
171
|
+
if (!f) { obj.malformed.push(`invalid test object entry \`${entry}\``); return }
|
|
172
|
+
const key = f[1]
|
|
173
|
+
if (!(TEST_KEYS as readonly string[]).includes(key)) { obj.unknownKeys.push(key); return }
|
|
174
|
+
if (key in obj.fields) obj.duplicateKeys.push(key)
|
|
175
|
+
obj.fields[key as TestKey] = testValue(f[2], key === 'name')
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// A flow mapping is accepted beside the more readable block form. Split only on commas outside quotes so
|
|
179
|
+
// an opaque case name such as "allows admin, user" survives byte-for-byte after YAML quote removal.
|
|
180
|
+
function parseFlowTestObject(raw: string): RawTestObject {
|
|
181
|
+
const obj = emptyTestObject()
|
|
182
|
+
if (!raw.startsWith('{') || !raw.endsWith('}')) {
|
|
183
|
+
obj.malformed.push('`test` object must be a mapping with exactly `path` and `name`')
|
|
184
|
+
return obj
|
|
185
|
+
}
|
|
186
|
+
const body = raw.slice(1, -1).trim()
|
|
187
|
+
if (!body) return obj
|
|
188
|
+
const entries: string[] = []
|
|
189
|
+
let start = 0
|
|
190
|
+
let quote = ''
|
|
191
|
+
let escaped = false
|
|
192
|
+
for (let i = 0; i < body.length; i++) {
|
|
193
|
+
const ch = body[i]
|
|
194
|
+
if (quote) {
|
|
195
|
+
if (escaped) { escaped = false; continue }
|
|
196
|
+
if (ch === '\\') { escaped = true; continue }
|
|
197
|
+
if (ch === quote) quote = ''
|
|
198
|
+
continue
|
|
199
|
+
}
|
|
200
|
+
if (ch === '"' || ch === "'") { quote = ch; continue }
|
|
201
|
+
if (ch === ',') { entries.push(body.slice(start, i).trim()); start = i + 1 }
|
|
202
|
+
}
|
|
203
|
+
if (quote) obj.malformed.push('`test` object has an unterminated quoted value')
|
|
204
|
+
entries.push(body.slice(start).trim())
|
|
205
|
+
for (const entry of entries) assignTestField(obj, entry)
|
|
206
|
+
return obj
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function normalizedTest(it: RawItem): ScenarioTestReference | undefined {
|
|
210
|
+
if (it.testObject) {
|
|
211
|
+
const path = it.testObject.fields.path
|
|
212
|
+
if (!path) return undefined
|
|
213
|
+
const name = it.testObject.fields.name
|
|
214
|
+
return { path, ...(name ? { name } : {}) }
|
|
215
|
+
}
|
|
216
|
+
const path = it.fields.test
|
|
217
|
+
return path ? { path } : undefined
|
|
218
|
+
}
|
|
219
|
+
|
|
121
220
|
// @@@scenario contract hash - the deterministic content hash of a scenario's SEMANTIC text, stamped on each
|
|
122
221
|
// reading at filing time ([[eval-core]]'s scenario freshness axis). Hashes ONLY the measurement contract —
|
|
123
222
|
// description (what to measure) + expected (what zero loss looks like) — never name/tags/test/code/related.
|
|
@@ -144,12 +243,13 @@ export function parseScenarios(src: string): Scenario[] {
|
|
|
144
243
|
const tags = it.fields.tags ? parseCodeList(it.fields.tags) : []
|
|
145
244
|
const code = it.fields.code ? parseCodeList(it.fields.code) : []
|
|
146
245
|
const related = it.fields.related ? parseCodeList(it.fields.related) : []
|
|
246
|
+
const test = normalizedTest(it)
|
|
147
247
|
return {
|
|
148
248
|
name: it.fields.name ?? '',
|
|
149
249
|
description: it.fields.description ?? '',
|
|
150
250
|
expected: it.fields.expected ?? '',
|
|
151
251
|
...(tags.length ? { tags } : {}),
|
|
152
|
-
...(
|
|
252
|
+
...(test ? { test } : {}),
|
|
153
253
|
...(code.length ? { code } : {}),
|
|
154
254
|
...(related.length ? { related } : {}),
|
|
155
255
|
}
|
|
@@ -161,7 +261,7 @@ export function parseScenarios(src: string): Scenario[] {
|
|
|
161
261
|
// Every scenario needs ≥1 tag; each tag must be IN the library — an out-of-library tag is rejected LOUD with
|
|
162
262
|
// the repair the user owns: pick an existing tag, or extend the library. An empty library (none configured)
|
|
163
263
|
// disables only the membership check, never the ≥1-tag requirement.
|
|
164
|
-
export function validateScenarios(src: string, tagLibrary: string[] = []): string[] {
|
|
264
|
+
export function validateScenarios(src: string, tagLibrary: string[] = [], pathRoot?: string): string[] {
|
|
165
265
|
const { hasFrontmatter, hasKey, items } = walkScenarios(src)
|
|
166
266
|
if (!hasFrontmatter) return ['no frontmatter block — an eval.md must declare a `scenarios:` list']
|
|
167
267
|
if (!hasKey) return ['frontmatter has no `scenarios:` key — declare at least one scenario']
|
|
@@ -183,6 +283,18 @@ export function validateScenarios(src: string, tagLibrary: string[] = []): strin
|
|
|
183
283
|
}
|
|
184
284
|
}
|
|
185
285
|
for (const u of it.unknownKeys) errs.push(`${label}: unknown field \`${u}\` (allowed: ${SCENARIO_KEYS.join(', ')})`)
|
|
286
|
+
if (it.testObject) {
|
|
287
|
+
for (const u of it.testObject.unknownKeys) errs.push(`${label}: unknown \`test\` field \`${u}\` (allowed: ${TEST_KEYS.join(', ')})`)
|
|
288
|
+
for (const d of it.testObject.duplicateKeys) errs.push(`${label}: duplicate \`test.${d}\` field`)
|
|
289
|
+
for (const e of it.testObject.malformed) errs.push(`${label}: ${e}`)
|
|
290
|
+
for (const k of TEST_KEYS) if (!it.testObject.fields[k]?.length) errs.push(`${label}: \`test\` object missing required field \`${k}\``)
|
|
291
|
+
} else if ('test' in it.fields && !it.fields.test?.length) {
|
|
292
|
+
errs.push(`${label}: \`test\` scalar path must not be empty`)
|
|
293
|
+
}
|
|
294
|
+
const test = normalizedTest(it)
|
|
295
|
+
if (test && pathRoot && !existsSync(join(pathRoot, test.path))) {
|
|
296
|
+
errs.push(`${label}: \`test.path\` not found: ${test.path}`)
|
|
297
|
+
}
|
|
186
298
|
if (it.fields.name) counts.set(it.fields.name, (counts.get(it.fields.name) ?? 0) + 1)
|
|
187
299
|
})
|
|
188
300
|
for (const [n, c] of counts) if (c > 1) errs.push(`duplicate scenario name '${n}' (${c}×) — names must be unique within an eval.md`)
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{u as xe,r as a,s as Ze,e as Je,j as n,f as Ve,h as Qe}from"./index-DulGPk6A.js";import{V as We,W as et,X as tt,Y as nt,Z as st,g as he,n as at,_ as ee,$ as lt,a0 as Pe,a1 as ot,a2 as it,u as rt,z as me,a3 as ct,o as ut,a4 as dt}from"./SessionWindow-CDhEL7wO.js";import{F as ve}from"./FoldToggle-CVFbBpyW.js";const Oe=(l,d)=>{let c=null;for(const v of l)if(v.at<=d)c=v;else break;return c},ft=l=>{if(!l||!Array.isArray(l.events))return null;const d=l.v===1?"time":typeof l.axis=="string"?l.axis:"time",c=l.v===1?v=>v.tMs:v=>v.at;return{axis:d,events:l.events.map(v=>({at:c(v),step:v.step,...v.node?{node:v.node}:{}}))}},ht=(l,d,c)=>l==="time"?ee(d):l==="frame"?`#${d}`:l==="line"?`L${d}`:l==="index"?c?`${d}/${c}`:`${d}`:String(d);function Fe({events:l,axis:d,extent:c,activeStepIdx:v,onSeek:S}){return n.jsx("div",{className:"an-ruler",children:l.map((i,h)=>n.jsxs("button",{className:`an-step ${v===h?"on":""}`,onClick:()=>S(i.at),"data-tip":i.node?`→ ${i.node}`:void 0,children:[ht(d,i.at,c)," ",i.step]},h))})}const pe=l=>{var d,c;return((d=l.verdict)==null?void 0:d.status)==="pass"?"✓":((c=l.verdict)==null?void 0:c.status)==="fail"?"✗":"·"},Ke=l=>{var d,c;return((d=l.verdict)==null?void 0:d.status)==="pass"?"pass":((c=l.verdict)==null?void 0:c.status)==="fail"?"fail":"legacy"};function mt({entry:l,specs:d=[],sessions:c=[],onWrite:v,onOpenSession:S}){var Le,Te,Ce,De,Ae,Ie;const i=xe(),h=a.useRef(null),x=a.useRef(null),w=a.useRef(null),j=a.useRef(null),g=a.useRef(0),[f,M]=a.useState([]),[b,k]=a.useState("time"),[r,y]=a.useState(null),[$,D]=a.useState(""),[A,U]=a.useState(!1),[te,K]=a.useState(null),P=a.useRef(null),I=a.useRef(null),u=a.useRef(null),L=a.useRef(0),[ne,ge]=a.useState(0),[se,ke]=a.useState(-1),[ye,be]=a.useState(null),[_,ae]=a.useState(!1),[we,le]=a.useState(!1),[oe,ie]=a.useState(null),[q,re]=a.useState(null),[N,je]=a.useState(null),[T,Y]=a.useState(0),p=N&&N[T]||l;a.useEffect(()=>{y(null),D(""),M([]),K(null),Y(0),je(null);let e=!0;return fetch(Ze(l.node,"evals")).then(t=>t.ok?t.json():null).then(t=>{e&&Array.isArray(t==null?void 0:t.readings)&&je(t.readings.filter(s=>s.scenario===l.scenario))}).catch(()=>{}),()=>{e=!1}},[l.node,l.scenario,l.ts,l.blob,(Le=l.humanOk)==null?void 0:Le.ts]),a.useEffect(()=>{y(null),K(null)},[T]);const B=We(p),O=B.find(e=>e.kind==="video"&&e.state==="present"),ce=B.filter(e=>e.kind==="image"),ze=B.filter(e=>e.kind!=="video"&&e.kind!=="image"),V=!!O,X=l.thread??null,ue=a.useMemo(()=>X?[{by:X.by,at:X.created,body:X.body},...X.replies||[]]:[],[X]),Ue=N&&((Te=N[0])==null?void 0:Te.by)||l.by||null,R=a.useMemo(()=>ue.map((e,t)=>{const s=et(tt(e.body),f);return s&&s.seekable?{i:t,tMs:s.tMs,step:s.step,label:s.label}:null}).filter(Boolean).sort((e,t)=>e.tMs-t.tMs),[ue,f]),Me=a.useRef(f);Me.current=f;const Ne=a.useRef(R);Ne.current=R;const G=a.useCallback(()=>{const e=L.current,t=Me.current;let s=-1;for(let m=0;m<t.length&&t[m].at<=e;m++)s=m;ke(m=>m===s?m:s);let o=null;for(const m of Ne.current)if(m.tMs<=e)o=m.i;else break;be(m=>m===o?m:o)},[]);a.useEffect(()=>{G()},[R,f,G]),a.useEffect(()=>{L.current=0,P.current&&(P.current.style.width="0%"),I.current&&(I.current.style.left="0%"),u.current&&(u.current.textContent=""),ke(-1),be(null),ge(0),ae(!1),le(!1),ie(null),re(null)},[l.blob,l.scenario,l.node]),a.useEffect(()=>{if(M([]),k("time"),!p.timelineBlob)return;let e=!0;return fetch(`/api/evidence/${p.timelineBlob}`).then(t=>t.ok?t.json():null).then(t=>{const s=e&&ft(t);s&&(M(s.events),k(s.axis))}).catch(()=>{}),()=>{e=!1}},[p.timelineBlob]),a.useEffect(()=>{const e=h.current;if(!e)return;const t=()=>{const E=Math.round((e.currentTime||0)*1e3),C=Math.round((e.duration||0)*1e3);L.current=E;const W=C?E/C*100:0;P.current&&(P.current.style.width=`${W}%`),I.current&&(I.current.style.left=`${W}%`),u.current&&(u.current.textContent=`${ee(E)} / ${ee(C)}`),G()},s=()=>{ge(e.duration||0),t()},o=()=>ae(!0),m=()=>ae(!1);return e.addEventListener("timeupdate",t),e.addEventListener("seeked",t),e.addEventListener("loadedmetadata",s),e.addEventListener("durationchange",s),e.addEventListener("play",o),e.addEventListener("pause",m),s(),()=>{e.removeEventListener("timeupdate",t),e.removeEventListener("seeked",t),e.removeEventListener("loadedmetadata",s),e.removeEventListener("durationchange",s),e.removeEventListener("play",o),e.removeEventListener("pause",m)}},[O==null?void 0:O.hash,l.node,l.scenario,T,G]);const F=Math.round(ne*1e3),Z=f[se]||null,Ee=b==="time"?F:b==="frame"?ce.length:b==="index"?f.length:0,z=a.useCallback(e=>{const t=h.current;t&&(t.currentTime=e/1e3)},[]),J=a.useCallback(()=>{const e=h.current;e&&(e.paused?e.play():e.pause())},[]),Se=(e,t)=>{re(e),t!=null&&z(t)},Q=a.useCallback(async e=>{const t=h.current;if(!(t!=null&&t.videoWidth))return null;U(!0),D(i("annotator.capturing"));try{t.seeking&&await new Promise(C=>t.addEventListener("seeked",C,{once:!0}));const s=document.createElement("canvas");s.width=t.videoWidth,s.height=t.videoHeight;const o=s.getContext("2d");o.drawImage(t,0,0,s.width,s.height),e&&(o.strokeStyle="#ff9a3c",o.lineWidth=Math.max(2,s.width/300),o.strokeRect(e.x/100*s.width,e.y/100*s.height,e.w/100*s.width,e.h/100*s.height));const m=await new Promise(C=>s.toBlob(C,"image/png")),{hash:E}=await Je(m);if(!E)throw new Error("no hash");return D(""),E}catch{return D(i("annotator.failed")),null}finally{U(!1)}},[i]),Be=a.useCallback(async()=>{var s;const e=h.current;if(!e)return null;e.pause();const t=Math.round((e.currentTime??0)*1e3);return{tMs:t,step:((s=Oe(f,t))==null?void 0:s.step)??null,frame:await Q(null)}},[f,Q]),de=a.useCallback(async e=>{const t=h.current;if(!t)return;t.pause();const s=Math.round((t.currentTime||0)*1e3),o=Oe(f,s),m=await Q(e),E=[nt(s,o==null?void 0:o.step)];m&&E.push(``),o!=null&&o.node&&o.node!==l.node&&E.push(`re: [[${o.node}]]`),E.push(""),K({seq:++g.current,body:E.join(`
|
|
2
|
-
`)})},[f,l.node,Q]),fe=a.useCallback(e=>{var o;if(!R.length)return;let t=R.findIndex(m=>m.i===q);if(t<0){const m=Math.round((((o=h.current)==null?void 0:o.currentTime)||0)*1e3);t=R.reduce((E,C,W)=>C.tMs<=m?W:E,-1)}t=Math.min(R.length-1,Math.max(0,t+e));const s=R[t];re(s.i),z(s.tMs)},[R,q,z]);a.useEffect(()=>{if(!V)return;const e=t=>{const s=document.activeElement;if(s&&(s.tagName==="INPUT"||s.tagName==="TEXTAREA"||s.tagName==="SELECT"||s.isContentEditable))return;const o=h.current;o&&(t.key===" "?(t.preventDefault(),J()):t.key==="ArrowRight"?(t.preventDefault(),o.currentTime=Math.min(o.duration||o.currentTime,o.currentTime+(t.shiftKey?1:5))):t.key==="ArrowLeft"?(t.preventDefault(),o.currentTime=Math.max(0,o.currentTime-(t.shiftKey?1:5))):t.key===","?(t.preventDefault(),o.currentTime=Math.max(0,o.currentTime-1/30)):t.key==="."?(t.preventDefault(),o.duration&&(o.currentTime=Math.min(o.duration,o.currentTime+1/30))):t.key==="ArrowDown"?(t.preventDefault(),fe(1)):t.key==="ArrowUp"?(t.preventDefault(),fe(-1)):(t.key==="a"||t.key==="A")&&(t.preventDefault(),de(null)))};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[V,J,fe,de]);const $e=e=>{var o;const t=(o=w.current)==null?void 0:o.getBoundingClientRect(),s=h.current;!t||!t.width||!s||!s.duration||(s.currentTime=Math.min(1,Math.max(0,(e-t.left)/t.width))*s.duration)},He=e=>{e.preventDefault(),le(!0),$e(e.clientX)},_e=e=>{var s;const t=(s=w.current)==null?void 0:s.getBoundingClientRect();t!=null&&t.width&&ie(Math.min(100,Math.max(0,(e.clientX-t.left)/t.width*100)))};a.useEffect(()=>{if(!we)return;const e=s=>$e(s.clientX),t=()=>le(!1);return window.addEventListener("mousemove",e),window.addEventListener("mouseup",t),()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",t)}},[we]);const Re=e=>{const t=x.current.getBoundingClientRect();return{x:(e.clientX-t.left)/t.width*100,y:(e.clientY-t.top)/t.height*100}},qe=e=>{if(e.button!==0||A)return;const t=Re(e);y({x0:t.x,y0:t.y,x:t.x,y:t.y})},Ye=e=>{var s,o;if(!r)return;const t=Re(e);!((s=h.current)!=null&&s.paused)&&(Math.abs(t.x-r.x0)>1||Math.abs(t.y-r.y0)>1)&&((o=h.current)==null||o.pause()),y({...r,x:t.x,y:t.y})},Ge=()=>{if(!r)return;const e={x:Math.min(r.x0,r.x),y:Math.min(r.y0,r.y),w:Math.abs(r.x-r.x0),h:Math.abs(r.y-r.y0)};if(y(null),e.w<1&&e.h<1){J();return}de(e)},H=r&&{x:Math.min(r.x0,r.x),y:Math.min(r.y0,r.y),w:Math.abs(r.x-r.x0),h:Math.abs(r.y-r.y0)};return n.jsxs("div",{className:"an-detail",children:[n.jsxs("header",{className:"an-head",children:[n.jsx("span",{className:"an-title",children:l.scenario}),n.jsx("span",{className:"an-node",children:l.node}),n.jsx("span",{className:`an-verdict-badge ${Ke(p)}`,children:pe(p)}),p.evaluator&&n.jsx("span",{className:"an-meta",children:p.evaluator}),n.jsx("span",{className:"an-meta",children:new Date(p.ts).toLocaleString()}),n.jsx(st,{originator:Ue,sessions:c,kind:"eval",onOpenSession:S}),p.humanOk?n.jsxs("span",{className:"an-okd","data-tip":i("annotator.okBy",{by:p.humanOk.by,at:new Date(p.humanOk.ts).toLocaleString()}),children:["☑ ",i("annotator.okd")]}):(!N||T===0)&&n.jsxs("button",{type:"button",className:"an-okbtn","data-tip":i("annotator.okTitle"),onClick:async()=>{const e=await Ve(l.node,l.scenario).catch(t=>({error:String(t)}));v==null||v((e==null?void 0:e.error)||"")},children:["☑ ",i("annotator.ok")]}),N&&N.length>1&&n.jsxs("div",{className:"an-ab",children:[n.jsx(he,{icon:"chevron-left",size:13,className:"an-ab-nav",disabled:T>=N.length-1,onClick:()=>Y(e=>Math.min(N.length-1,e+1)),label:i("annotator.abOlder")}),n.jsx("div",{className:"an-ab-track",children:N.slice().reverse().map((e,t)=>{const s=N.length-1-t;return n.jsx("button",{type:"button",className:`an-ab-pip ${Ke(e)} ${s===T?"on":""}`,onClick:()=>Y(s),"data-tip":`${pe(e)} ${new Date(e.ts).toLocaleString()}`,children:pe(e)},`${e.ts}-${s}`)})}),n.jsx(he,{icon:"chevron-right",size:13,className:"an-ab-nav",disabled:T<=0,onClick:()=>Y(e=>Math.max(0,e-1)),label:i("annotator.abNewer")}),n.jsx("span",{className:"an-ab-pos",children:T===0?i("annotator.abLatest"):i("annotator.abPos",{i:N.length-T,n:N.length})})]})]}),n.jsxs("div",{className:"an-work",children:[n.jsxs("div",{className:"an-stage-col",children:[p.expected&&n.jsxs("div",{className:"an-expected",children:[n.jsx("b",{children:i("nodeView.eval.expected")})," ",p.expected]}),B.length>0&&((Ce=p.verdict)==null?void 0:Ce.note)&&n.jsxs("div",{className:"an-expected an-prior-note",children:[n.jsx("b",{children:i("nodeView.eval.noteLabel")})," ",p.verdict.note]}),!p.fresh&&(((De=p.staleAxes)==null?void 0:De.length)??0)>0&&n.jsxs("div",{className:"an-expected an-stale","data-tip":i("nodeView.eval.staleReadoutTitle"),children:[n.jsx("b",{children:i("nodeView.eval.staleLabel")})," ",p.staleAxes.join(" · "),(((Ae=p.codeDrift)==null?void 0:Ae.length)??0)>0&&n.jsxs("span",{className:"an-stale-files",children:[" — ",p.codeDrift.map(e=>`${e.file.split("/").pop()} +${e.behind}`).join(", ")]})]}),O&&n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"an-player",ref:j,children:[n.jsxs("div",{className:`an-stage ${_?"playing":"paused"}`,ref:x,onMouseDown:qe,onMouseMove:Ye,onMouseUp:Ge,children:[n.jsx("video",{className:"an-video",ref:h,src:`/api/evidence/${O.hash}`,preload:"metadata",playsInline:!0}),H&&n.jsx("div",{className:"an-rect live",style:{left:`${H.x}%`,top:`${H.y}%`,width:`${H.w}%`,height:`${H.h}%`}}),!_&&!r&&n.jsx("div",{className:"an-bigplay","aria-hidden":!0,children:n.jsx(at,{name:"play",size:22})})]}),n.jsxs("div",{className:"an-bar",children:[n.jsx(he,{icon:_?"pause":"play",size:14,className:"an-play",label:i(_?"annotator.pause":"annotator.play"),onClick:J}),n.jsxs("div",{className:"an-seek",ref:w,onMouseDown:He,onMouseMove:_e,onMouseLeave:()=>ie(null),children:[n.jsx("div",{className:"an-seek-trk"}),F>0&&b==="time"&&f.map((e,t)=>n.jsx("div",{className:"an-band",style:{left:`${e.at/F*100}%`},"data-tip":e.step},`band-${t}`)),n.jsx("div",{className:"an-seek-play",ref:P}),F>0&&R.map(e=>n.jsx("button",{type:"button",className:`an-mk ${q===e.i?"on":""} ${ye===e.i?"active":""}`,style:{left:`${e.tMs/F*100}%`},"data-tip":e.label,onMouseDown:t=>t.stopPropagation(),onClick:t=>{t.stopPropagation(),Se(e.i,e.tMs)}},`mk-${e.i}`)),n.jsx("div",{className:"an-knob",ref:I}),oe!=null&&F>0&&n.jsx("div",{className:"an-seek-hov",style:{left:`${oe}%`},children:ee(oe/100*F)})]}),n.jsx("span",{className:"an-time",ref:u}),Z&&n.jsx("span",{className:"an-curstep","data-tip":Z.node?`→ ${Z.node}`:void 0,children:Z.step}),n.jsx(lt,{target:j})]})]}),f.length>0&&n.jsx(Fe,{events:f,axis:b,extent:Ee,activeStepIdx:se,onSeek:z}),n.jsx("div",{className:"an-hint",children:i("annotator.hint")}),n.jsx("div",{className:"an-keys",children:i("annotator.keys")}),$&&n.jsx("div",{className:"an-flash",children:$})]}),!O&&f.length>0&&n.jsx(Fe,{events:f,axis:b,extent:Ee,activeStepIdx:se,onSeek:z}),ce.length>0&&n.jsx("div",{className:"an-gallery",children:ce.map((e,t)=>n.jsx(Pe,{e,alt:l.scenario},`${e.hash}-${t}`))}),ze.map((e,t)=>n.jsx(Pe,{e,alt:l.scenario},`${e.hash}-${t}`)),B.length===0&&((Ie=p.verdict)!=null&&Ie.note?n.jsx("pre",{className:"eval-transcript",children:p.verdict.note}):n.jsx("div",{className:"an-hint",children:i("nodeView.eval.noImage")}))]}),n.jsx(vt,{entry:l,comments:ue,specs:d,sessions:c,onWrite:v,codeSha:p.codeSha,seekMs:V?z:null,anchorNow:V?Be:null,draft:te,selIdx:q,activeIdx:ye,onSelect:V?Se:null,events:V?f:null})]})]})}function vt({entry:l,comments:d,codeSha:c,specs:v,sessions:S,onWrite:i,seekMs:h,anchorNow:x,draft:w,selIdx:j,activeIdx:g,onSelect:f,events:M}){var r;const b=xe(),k=(y,$)=>Qe({node:l.node,scenario:l.scenario,body:y,codeSha:c,evidence:$});return n.jsxs("aside",{className:"an-rail",children:[n.jsx("div",{className:"an-comments-head",children:b("annotator.comments",{n:d.length})}),n.jsx("div",{className:"an-rail-list",children:n.jsx(ot,{replies:d,onSeek:h,selIdx:j,activeIdx:g,onSelect:f,events:M,threadId:((r=l.thread)==null?void 0:r.id)??null,onRemarkChange:()=>i==null?void 0:i("")})}),n.jsx("div",{className:"an-rail-compose",children:n.jsx(it,{onSend:k,specs:v,sessions:S,focusId:l.node,onDone:i,anchorNow:x,draft:w},`${l.node}·${l.scenario}`)})]})}function Xe({rowKeys:l,sel:d,onSel:c,detail:v,children:S}){const[i,h]=a.useState(!1),x=a.useRef({});x.current={rowKeys:l,sel:d},a.useEffect(()=>{const j=g=>{var y;const f=(y=g.target)==null?void 0:y.tagName;if(f==="INPUT"||f==="TEXTAREA"||g.metaKey||g.ctrlKey||g.altKey||g.key!=="j"&&g.key!=="k")return;g.preventDefault(),g.stopPropagation();const{rowKeys:M,sel:b}=x.current;if(!M.length)return;const k=M.indexOf(b),r=k<0?g.key==="j"?0:M.length-1:Math.max(0,Math.min(M.length-1,k+(g.key==="j"?1:-1)));c(M[r])};return window.addEventListener("keydown",j,!0),()=>window.removeEventListener("keydown",j,!0)},[c]),a.useEffect(()=>{var j;(j=document.querySelector(".fv-list-col .sel"))==null||j.scrollIntoView({block:"nearest"})},[d]);const w=n.jsx(ve,{className:"fv-fold-inline",onToggle:()=>h(!0)});return n.jsxs("div",{className:`fv-master ${i?"folded":""}`,children:[i&&n.jsx(ve,{className:"fv-unfold",folded:!0,onToggle:()=>h(!1)}),n.jsx("div",{className:"fv-list-col",style:i?{display:"none"}:void 0,children:typeof S=="function"?S(w):n.jsxs(n.Fragment,{children:[n.jsx(ve,{className:"fv-fold",onToggle:()=>h(!0)}),S]})}),n.jsx("div",{className:"fv-detail",children:v})]})}function pt({specs:l=[],sessions:d=[],reloadBoard:c,onOpenSession:v}){const S=xe(),{page:i,param:h}=rt(),[x,w]=a.useState(null),[j,g]=a.useState(""),[f,M]=a.useState([]),b=a.useRef([]),k=a.useMemo(()=>new Map(f.map(u=>[me(u),u])),[f]);b.current=f.map(me);const r=x&&k.has(x)?x:b.current[0]??null,y=a.useMemo(()=>{if(!h)return null;const u=h.indexOf("/");return u>0?`eval:${h.slice(0,u)}·${h.slice(u+1)}`:null},[h]),[$,D]=a.useState(null);a.useEffect(()=>{i==="evals"&&y&&(w(y),D(y))},[i,y]),a.useEffect(()=>{$&&k.has($)&&D(null)},[$,k]);const A=x&&!k.has(x),U=a.useMemo(()=>!A||ct(l).some(u=>me(u)===x),[A,l,x]);a.useEffect(()=>{A&&!U&&(w(null),D(null))},[A,U]),a.useEffect(()=>{x&&!$&&f.length&&!k.has(x)&&w(null)},[x,$,f,k]),a.useEffect(()=>{if(i!=="evals"||!r||A)return;const u=/^eval:([^·]+)·(.+)$/.exec(r);u&&ut("evals",`${u[1]}/${u[2]}`,{replace:!0})},[i,r,A]);const te=a.useCallback(u=>M(u),[]),K=u=>{u&&(g(u),setTimeout(()=>g(""),6e3))},P=a.useCallback(async u=>{const L=await Ve(u.node,u.scenario).catch(ne=>({error:String(ne)}));K((L==null?void 0:L.error)||""),await(c==null?void 0:c())},[c]),I=r?k.get(r):null;return n.jsx(Xe,{rowKeys:b.current,sel:r,onSel:w,detail:I?n.jsx(mt,{entry:I,specs:l,sessions:d,onOpenSession:v,onWrite:async u=>{K(u),await(c==null?void 0:c())}}):n.jsx("div",{className:"fv-note",children:S("evalsFeed.empty")}),children:u=>n.jsxs(n.Fragment,{children:[j&&n.jsx("div",{className:"fv-notice",children:j}),n.jsx(dt,{nodes:l,sessions:d,sel:r,onSel:L=>w(L),onRows:te,onOk:P,mustShow:$,lead:u})]})})}const yt=Object.freeze(Object.defineProperty({__proto__:null,EvalMasterDetail:Xe,default:pt},Symbol.toStringTag,{value:"Module"}));export{mt as E,Xe as a,yt as b};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{u as k,r as b,j as s}from"./index-DulGPk6A.js";import{q as M,t as A,A as y,j as g,v as H,H as V,w as R,E as _,c as $,G as B,x as G,S}from"./SessionWindow-CDhEL7wO.js";const L={spec:"nodeView.paneSpec",history:"nodeView.paneHistory",issues:"nodeView.paneIssues",eval:"nodeView.paneEval",edit:"nodeView.paneEdit"},C=(e,l)=>l.filter(u=>{var h;return(h=u.ops)==null?void 0:h.some(n=>n.nodeId===e.id)});function T({status:e}){const l=S[e]||S.pending;return s.jsx("span",{className:"m-dot",style:{background:l.color}})}function U({node:e,kids:l,editors:u,onTap:h}){return s.jsxs("button",{className:"m-row",onClick:h,children:[s.jsx(T,{status:e.status}),s.jsx("span",{className:"m-row-title",children:e.title}),u.length>0&&s.jsx("span",{className:"m-row-faces",children:u.slice(0,3).map(n=>s.jsx(y,{seed:n.id,status:n.status,title:g(n)},n.id))}),e.version?s.jsxs("span",{className:"m-row-ver",children:["v",e.version]}):null,l>0&&s.jsxs("span",{className:"m-row-kids",children:["▸",l]}),s.jsx("span",{className:"m-row-chev",children:"›"})]})}function q({node:e,childrenOf:l,sessions:u,onOpenChild:h}){const n=k(),m=l(e.id),d=M(e),i=[...m.length?[{key:"children",label:n("mobile.childrenTab",{n:m.length})}]:[],...d.map(a=>({key:a.key,label:n(L[a.key])}))],[r,x]=b.useState(null);b.useEffect(()=>{x(null)},[e.id]);const c=r&&i.some(a=>a.key===r)?r:i[0].key,j=A(e.id,c==="history"),N=C(e,u);return s.jsxs("div",{className:"m-node",children:[s.jsxs("div",{className:"m-node-head",children:[s.jsx(T,{status:e.status}),s.jsx("span",{className:"m-node-title",children:e.title}),e.version?s.jsxs("span",{className:"m-node-ver",children:["v",e.version]}):null,s.jsx("span",{className:"m-node-status",children:n(`status.${e.status}`)}),N.length>0&&s.jsx("span",{className:"m-node-faces",title:n("mobile.liveEditors",{n:N.length}),children:N.map(a=>s.jsx(y,{seed:a.id,status:a.status,title:g(a)},a.id))})]}),e.desc&&s.jsx("div",{className:"m-node-desc",children:e.desc}),s.jsx("div",{className:"m-tabs",children:i.map(a=>s.jsx("button",{className:a.key===c?"m-tab on":"m-tab",onClick:()=>x(a.key),children:a.label},a.key))}),s.jsxs("div",{className:c==="history"?"m-pane-host fixed":"m-pane-host",children:[c==="children"&&s.jsx("div",{className:"m-children",children:m.map(a=>s.jsx(U,{node:a,kids:l(a.id).length,editors:C(a,u),onTap:()=>h(a.id)},a.id))}),c==="spec"&&s.jsx(H,{node:e}),c==="history"&&s.jsx(V,{node:e,rows:j}),c==="issues"&&s.jsx(R,{node:e}),c==="edit"&&s.jsx(_,{node:e})]})]})}function D({sessions:e,openId:l,setOpenId:u,byId:h,goToNode:n}){const m=k(),d=l?e.find(i=>i.id===l):null;if(d){const i=d.ops||[];return s.jsxs("div",{className:"m-sessdetail",children:[s.jsxs("div",{className:"m-sess-card",children:[s.jsx(y,{seed:d.id,status:d.status}),s.jsxs("div",{className:"m-sess-meta",children:[s.jsx("span",{className:"m-sess-name",children:g(d)}),s.jsx("span",{className:"m-sess-status",style:{color:$[d.status]},children:m(`status.${d.status}`)})]})]}),d.activity&&s.jsx("div",{className:"m-sess-activity",children:d.activity}),s.jsx("div",{className:"m-sess-section",children:m("mobile.changing",{n:i.length})}),i.length===0?s.jsx("div",{className:"m-empty",children:m("mobile.noChanges")}):i.map((r,x)=>{const c=h[r.nodeId];return s.jsxs("button",{className:"m-row",disabled:!c,onClick:()=>c&&n(r.nodeId),children:[s.jsx("span",{className:`ov-mark ov-${r.op}`,children:B[r.op]||"•"}),s.jsx("span",{className:"m-row-title",children:c?c.title:r.nodeId}),c&&s.jsx("span",{className:"m-row-chev",children:"›"})]},x)})]})}return e.length?s.jsx("div",{className:"m-sesslist",children:e.map(i=>s.jsx("button",{className:"m-sess-row",onClick:()=>u(i.id),children:s.jsx(G,{s:i,locked:!1})},i.id))}):s.jsx("div",{className:"m-empty big",children:m("mobile.noSessions")})}function z({specs:e,sessions:l,project:u}){const h=k(),n=b.useMemo(()=>Object.fromEntries(e.map(t=>[t.id,t])),[e]),m=b.useMemo(()=>e.find(t=>!t.parent)||e[0],[e]),d=b.useMemo(()=>{const t={};return e.forEach(o=>{var p;o.parent&&(t[p=o.parent]??(t[p]=[])).push(o)}),o=>t[o]||[]},[e]),i=b.useMemo(()=>t=>{const o=[];for(let p=n[t];p;p=p.parent?n[p.parent]:null)o.unshift(p.id);return o},[n]),[r,x]=b.useState("specs"),[c,j]=b.useState(()=>m?[m.id]:[]),[N,a]=b.useState(null),v=c.filter(t=>n[t]),w=v[v.length-1]||(m==null?void 0:m.id),f=w?n[w]:null,E=t=>j(o=>[...o.filter(p=>n[p]),t]),I=t=>{j(i(t)),a(null),x("specs")},P=r==="specs"?v.length>1:!!N,O=()=>{r==="specs"?j(t=>t.slice(0,-1)):a(null)};return s.jsxs("div",{className:"m-app",children:[s.jsxs("header",{className:"m-topbar",children:[P?s.jsx("button",{className:"m-back",onClick:O,"aria-label":h("mobile.back"),children:"‹"}):s.jsx("span",{className:"m-back ghost","aria-hidden":"true"}),s.jsx("span",{className:"m-brand",children:u||"SpexCode"})]}),s.jsx("main",{className:"m-main",children:r==="specs"?s.jsxs("div",{className:"m-specs",children:[s.jsx("nav",{className:"m-crumbs",children:v.map((t,o)=>s.jsxs("span",{className:"m-crumb",children:[o>0&&s.jsx("span",{className:"m-crumb-sep",children:"›"}),s.jsx("button",{className:o===v.length-1?"m-crumb-btn cur":"m-crumb-btn",onClick:()=>j(v.slice(0,o+1)),children:n[t].title})]},t))}),f&&s.jsx(q,{node:f,childrenOf:d,sessions:l,onOpenChild:E},f.id)]}):s.jsx(D,{sessions:l,openId:N,setOpenId:a,byId:n,goToNode:I})}),s.jsxs("nav",{className:"m-tabbar",children:[s.jsxs("button",{className:r==="specs"?"m-tabbar-btn on":"m-tabbar-btn",onClick:()=>x("specs"),children:[s.jsx("span",{className:"m-tabbar-ico",children:"❯_"}),h("mobile.specsTab")]}),s.jsxs("button",{className:r==="sessions"?"m-tabbar-btn on":"m-tabbar-btn",onClick:()=>x("sessions"),children:[s.jsx("span",{className:"m-tabbar-ico",children:"◐"}),h("mobile.sessionsTab"),l.length>0&&s.jsx("span",{className:"m-tabbar-badge",children:l.length})]})]})]})}export{z as default};
|