ruvnet-brain 3.9.134-dev → 4.0.2
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/.claude-plugin/marketplace.json +14 -0
- package/README.md +5 -5
- package/bin/install.mjs +382 -36
- package/console/CONTRACT.md +172 -0
- package/console/activity.js +753 -0
- package/console/app.js +4189 -0
- package/console/architecture.html +1221 -0
- package/console/assets/depth-1.webp +0 -0
- package/console/assets/depth-2.webp +0 -0
- package/console/assets/depth-3.webp +0 -0
- package/console/assets/harness-vs-plain.svg +259 -0
- package/console/assets/hero.webp +0 -0
- package/console/assets/memory.webp +0 -0
- package/console/assets/metaharness.svg +247 -0
- package/console/index.html +777 -0
- package/console/install-architecture.html +162 -0
- package/console/install-mockup.html +543 -0
- package/console/style.css +2144 -0
- package/console/tips.css +926 -0
- package/console/tips.html +858 -0
- package/console/tips.js +128 -0
- package/docs/RELEASE-NOTES-4.0.md +88 -0
- package/kb/model-requirements.mjs +37 -6
- package/kb/zip-extract.mjs +53 -14
- package/keys/ruvnet-brain-signing.pub.pem +3 -0
- package/package.json +14 -22
- package/plugin/.claude-plugin/marketplace.json +14 -0
- package/plugin/.claude-plugin/plugin.json +22 -0
- package/plugin/.codex-plugin/plugin.json +21 -0
- package/plugin/.mcp.json +8 -0
- package/plugin/commands/brain-console.md +16 -0
- package/plugin/commands/configure.md +33 -0
- package/plugin/commands/rvbc.md +79 -0
- package/plugin/commands/rvcb.md +16 -0
- package/plugin/commands/whats-new.md +57 -0
- package/plugin/hooks/codex-hooks.json +160 -0
- package/plugin/hooks/hook-contracts.json +77 -0
- package/plugin/hooks/hooks.json +202 -0
- package/plugin/mcp/managed-cli-interface.mjs +47 -4
- package/plugin/mcp/server.mjs +56 -6
- package/plugin/scripts/anticipate.sh +534 -0
- package/plugin/scripts/codex-hook-adapter.mjs +96 -0
- package/plugin/scripts/continuation-gate.mjs +267 -0
- package/plugin/scripts/design-wall.sh +137 -0
- package/plugin/scripts/detach.mjs +182 -0
- package/plugin/scripts/first-session-worker.mjs +38 -0
- package/plugin/scripts/gate-receipt.sh +35 -0
- package/plugin/scripts/ground-before-write.sh +199 -0
- package/plugin/scripts/ground-ruvnet.sh +517 -0
- package/plugin/scripts/grounding-stamp.sh +113 -0
- package/plugin/scripts/grounding-substance.mjs +595 -0
- package/plugin/scripts/hijack-ruvnet.sh +81 -0
- package/plugin/scripts/hook-input.mjs +558 -0
- package/plugin/scripts/hook-shim-bash.mjs +55 -0
- package/plugin/scripts/hook-shim.mjs +303 -0
- package/plugin/scripts/host-update.mjs +58 -0
- package/plugin/scripts/kling-preflight.sh +146 -0
- package/plugin/scripts/learn-capture.sh +173 -0
- package/plugin/scripts/learn-flush.mjs +155 -0
- package/plugin/scripts/lesson-hooks.sh +213 -0
- package/plugin/scripts/md-stamp.mjs +219 -0
- package/plugin/scripts/protect-brain-state.sh +84 -0
- package/plugin/scripts/route-dispatch.sh +147 -0
- package/plugin/scripts/routing-outcome-capture.mjs +89 -0
- package/plugin/scripts/runtime-preferences.mjs +269 -0
- package/plugin/scripts/session-start-core.mjs +477 -0
- package/plugin/scripts/session-start.sh +13 -0
- package/plugin/scripts/signal-watch.mjs +193 -0
- package/plugin/scripts/unprompted-runtime.mjs +377 -0
- package/plugin/scripts/update-apply.mjs +419 -0
- package/plugin/scripts/verify-interface.sh +53 -0
- package/plugin/scripts/version-bump-gate.sh +112 -0
- package/plugin/skills/brain-build/SKILL.md +123 -0
- package/plugin/skills/brain-console/SKILL.md +22 -0
- package/plugin/skills/brain-prompt/SKILL.md +83 -0
- package/plugin/skills/brain-score/SKILL.md +101 -0
- package/plugin/skills/release-proof/SKILL.md +81 -0
- package/plugin/skills/release-proof/agents/openai.yaml +4 -0
- package/plugin/skills/release-proof/references/receipt-contract.md +38 -0
- package/plugin/skills/release-proof/scripts/release-proof.mjs +210 -0
- package/plugin/skills/ruvnet-brain/PLAYBOOK.md +121 -0
- package/plugin/skills/ruvnet-brain/SKILL.md +234 -0
- package/plugin/skills/rvbc/SKILL.md +23 -0
- package/plugin/skills/savings/SKILL.md +46 -0
- package/plugin/skills/whats-new/SKILL.md +22 -0
- package/scripts/adr-backfill.mjs +107 -0
- package/scripts/advocacy-outcomes.mjs +808 -0
- package/scripts/agentdb-context.mjs +216 -0
- package/scripts/agentdb-fleet-doctor.mjs +101 -0
- package/scripts/ascii-drift.mjs +236 -0
- package/scripts/behavioral-l1-l4.mjs +210 -0
- package/scripts/brain-capability-check.mjs +72 -0
- package/scripts/brain-grade-groundtruth.mjs +100 -0
- package/scripts/brain-latency-50.mjs +227 -0
- package/scripts/brain-novice-50.mjs +189 -0
- package/scripts/brain-stamp.mjs +94 -0
- package/scripts/brain-state.mjs +212 -0
- package/scripts/build-bundle.mjs +522 -0
- package/scripts/build-concepts.mjs +132 -0
- package/scripts/build-l2.mjs +71 -0
- package/scripts/build-primer.mjs +73 -0
- package/scripts/build-symbols.mjs +68 -0
- package/scripts/calibrate-router.mjs +97 -0
- package/scripts/capability-audit.mjs +321 -0
- package/scripts/capability-registry.mjs +876 -0
- package/scripts/check-indexation.mjs +108 -0
- package/scripts/check-legibility.mjs +189 -0
- package/scripts/ci/build-fixture-kb.mjs +67 -0
- package/scripts/ci/learning-replay-codex-adapter.mjs +62 -0
- package/scripts/ci/learning-replay-recorder.mjs +59 -0
- package/scripts/ci/mutate-hook-timeout.mjs +70 -0
- package/scripts/ci/stranger-fixture-stage.mjs +17 -0
- package/scripts/ci/stranger-scenario.mjs +228 -0
- package/scripts/ci/stranger-timeout.mjs +25 -0
- package/scripts/ci-verdict.mjs +29 -0
- package/scripts/claims-verify.mjs +710 -0
- package/scripts/clear-claude-tmp.sh +31 -0
- package/scripts/console-engine.mjs +434 -0
- package/scripts/console-engine.test.mjs +125 -0
- package/scripts/corpus-qa.mjs +250 -0
- package/scripts/correction-detect-embed.mjs +346 -0
- package/scripts/correction-detect-measure.mjs +270 -0
- package/scripts/correction-detect.mjs +686 -0
- package/scripts/count-chunks.mjs +54 -0
- package/scripts/described-questions.json +30 -0
- package/scripts/design-grade.mjs +58 -0
- package/scripts/dev-plugin-link.sh +105 -0
- package/scripts/distill-project.mjs +200 -0
- package/scripts/doc-currency.mjs +801 -0
- package/scripts/eval-brain.mjs +244 -0
- package/scripts/fix-metaharness-memretrieve.mjs +121 -0
- package/scripts/full-hints.mjs +87 -0
- package/scripts/gate.sh +39 -0
- package/scripts/gates.mjs +146 -0
- package/scripts/gen-console-images.mjs +54 -0
- package/scripts/gen-images.mjs +47 -0
- package/scripts/git-clone-refresh.mjs +52 -0
- package/scripts/git-hooks/pre-push +126 -0
- package/scripts/goal-match.mjs +398 -0
- package/scripts/goldie-research.mjs +223 -0
- package/scripts/goldie-weekly.sh +67 -0
- package/scripts/health-repair.mjs +250 -0
- package/scripts/helix-scenario-questions.json +10 -0
- package/scripts/ingest-gists.mjs +230 -0
- package/scripts/ingest-meeting.mjs +115 -0
- package/scripts/ingest-repo.mjs +79 -0
- package/scripts/install-npx-witness.sh +49 -0
- package/scripts/issue-fix.mjs +639 -0
- package/scripts/issue-watch.mjs +276 -0
- package/scripts/issue4-close-note.md +31 -0
- package/scripts/key-canary.mjs +91 -0
- package/scripts/latency-to-surface.mjs +233 -0
- package/scripts/learning-enable.mjs +380 -0
- package/scripts/learning-replay.mjs +1570 -0
- package/scripts/learnings.mjs +62 -0
- package/scripts/lesson-gate.mjs +680 -0
- package/scripts/lesson-lifecycle.mjs +449 -0
- package/scripts/lesson-promote.mjs +262 -0
- package/scripts/lesson-ratify.mjs +98 -0
- package/scripts/lesson-seed.mjs +252 -0
- package/scripts/lesson-store.mjs +447 -0
- package/scripts/loop-checkpoint.mjs +86 -0
- package/scripts/memdb-health.sh +14 -0
- package/scripts/memory-doctor.mjs +271 -0
- package/scripts/model-catalog.mjs +79 -0
- package/scripts/nightly-controller.mjs +66 -0
- package/scripts/nightly-gists.sh +72 -0
- package/scripts/nightly-wrapper.sh +180 -0
- package/scripts/notify.sh +12 -0
- package/scripts/npx-witness.sh +56 -0
- package/scripts/onboarding-console.mjs +2749 -0
- package/scripts/private-fence.mjs +69 -0
- package/scripts/proactivity-metrics.mjs +118 -0
- package/scripts/proof-questions.json +56 -0
- package/scripts/prove.mjs +95 -0
- package/scripts/proxy/claude-proxied.sh +57 -0
- package/scripts/proxy/proxy-revert.sh +59 -0
- package/scripts/proxy/proxy-up.sh +60 -0
- package/scripts/proxy/proxy-verify.mjs +142 -0
- package/scripts/published-surface-probe.mjs +241 -0
- package/scripts/qe/card-lane-gate.mjs +162 -0
- package/scripts/qe/session-start-gate.mjs +229 -0
- package/scripts/qe/ux-suite.mjs +323 -0
- package/scripts/reconcile-project.mjs +0 -0
- package/scripts/record-lesson.mjs +113 -0
- package/scripts/refresh-model-catalog.mjs +99 -0
- package/scripts/release-proof.mjs +9 -0
- package/scripts/release-vector.mjs +281 -0
- package/scripts/release.mjs +395 -0
- package/scripts/remedy-registry.mjs +247 -0
- package/scripts/rerank-cap-eval.mjs +265 -0
- package/scripts/rerank-cap-warm-ab.mjs +129 -0
- package/scripts/route-cheap.mjs +20 -15
- package/scripts/router-utilization.mjs +182 -0
- package/scripts/routing-flywheel.mjs +596 -0
- package/scripts/rvf-generation.mjs +104 -0
- package/scripts/rvf-index-audit.mjs +138 -0
- package/scripts/self-update.mjs +508 -0
- package/scripts/selfcheck.mjs +7 -1
- package/scripts/sign-bundle.mjs +69 -0
- package/scripts/signal-watch.mjs +171 -0
- package/scripts/stack-sync.mjs +469 -0
- package/scripts/stamp-existing-rvf-generations.mjs +53 -0
- package/scripts/stamp-sweep.mjs +144 -0
- package/scripts/status-honesty.mjs +102 -0
- package/scripts/sync-version.mjs +217 -0
- package/scripts/token-report.mjs +102 -0
- package/scripts/top100-benchmark.mjs +479 -0
- package/scripts/top100-corpus.mjs +112 -0
- package/scripts/top100-semantic-assertions.mjs +449 -0
- package/scripts/update-apply.mjs +9 -0
- package/scripts/upgrade-notice.mjs +14 -0
- package/scripts/verify-bundle.mjs +51 -0
- package/scripts/verify-channels.mjs +184 -0
- package/scripts/verify-model-catalog.mjs +104 -0
- package/scripts/verify-nightly-close-issue4.sh +31 -0
- package/scripts/version.mjs +40 -0
- package/scripts/wired-check.mjs +864 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
RUVNET BRAIN — ONBOARDING CONSOLE · "Brain activity" card
|
|
3
|
+
----------------------------------------------------------------------------
|
|
4
|
+
Self-contained module: renders the approved glass-brain activity panel
|
|
5
|
+
inside #body-activity, fed by GET /api/activity (live AgentDB state).
|
|
6
|
+
All CSS is injected scoped under #card-activity. Design tokens (colors,
|
|
7
|
+
fonts, radii) come from style.css — nothing is redefined here.
|
|
8
|
+
Graceful degrade: fetch failure or hasStore:false → one calm line.
|
|
9
|
+
============================================================================ */
|
|
10
|
+
(function () {
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
var CARD = document.getElementById('card-activity');
|
|
14
|
+
var BODY = document.getElementById('body-activity');
|
|
15
|
+
if (!CARD || !BODY) return;
|
|
16
|
+
|
|
17
|
+
/* ------------------------- scoped styles ------------------------- */
|
|
18
|
+
var CSS = [
|
|
19
|
+
/* stat row (tiles are doors) */
|
|
20
|
+
'#card-activity .ab-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin:16px 0 4px}',
|
|
21
|
+
'#card-activity .ab-stat{background:var(--surface-2);border:1px solid var(--ridge);border-radius:12px;padding:9px 14px 8px}',
|
|
22
|
+
'#card-activity .ab-stat .ab-n{font-family:var(--mono);font-size:25px;line-height:1.15;color:var(--ink);font-weight:500;font-variant-numeric:tabular-nums}',
|
|
23
|
+
'#card-activity .ab-stat .ab-l{font-size:13px;color:var(--ink-2);margin-top:1px}',
|
|
24
|
+
'#card-activity .ab-stat.ab-door{cursor:pointer;position:relative;transition:border-color .18s,background .18s}',
|
|
25
|
+
'#card-activity .ab-stat.ab-door:hover{border-color:color-mix(in srgb,var(--amber) 60%,var(--ridge))}',
|
|
26
|
+
'#card-activity .ab-stat.ab-door:hover .ab-l{text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--faint)}',
|
|
27
|
+
'#card-activity .ab-stat.ab-door:focus-visible{outline:2px solid var(--amber);outline-offset:2px}',
|
|
28
|
+
'#card-activity .ab-stat.ab-door .ab-tchev{position:absolute;top:7px;right:10px;font-size:12px;color:var(--muted);transition:transform .25s,color .15s}',
|
|
29
|
+
'#card-activity .ab-stat.ab-door .ab-hint{position:absolute;top:8px;right:26px;font-size:13px;color:var(--muted);opacity:0;transition:opacity .15s;white-space:nowrap;pointer-events:none}',
|
|
30
|
+
'#card-activity .ab-stat.ab-door .ab-hint::after{content:"click to open"}',
|
|
31
|
+
'#card-activity .ab-stat.ab-door.ab-on .ab-hint::after{content:"click to close"}',
|
|
32
|
+
'#card-activity .ab-stat.ab-door:hover .ab-hint{opacity:1}',
|
|
33
|
+
'#card-activity .ab-stat.ab-door:hover .ab-tchev{color:var(--amber-text)}',
|
|
34
|
+
'#card-activity .ab-stat.ab-door.ab-on{border-color:var(--amber);background:color-mix(in srgb,var(--amber) 14%,transparent)}',
|
|
35
|
+
'#card-activity .ab-stat.ab-door.ab-on .ab-tchev{transform:rotate(90deg);color:var(--amber-text)}',
|
|
36
|
+
/* doors drawer */
|
|
37
|
+
'#card-activity .ab-doors{display:grid;grid-template-rows:0fr;transition:grid-template-rows .35s ease}',
|
|
38
|
+
'#card-activity .ab-doors.ab-open{grid-template-rows:1fr}',
|
|
39
|
+
'#card-activity .ab-doors-clip{overflow:hidden}',
|
|
40
|
+
'#card-activity .ab-door-box{background:var(--surface-2);border:1px solid var(--ridge);border-radius:12px;margin:10px 0 2px}',
|
|
41
|
+
'#card-activity .ab-door-head{display:flex;align-items:baseline;gap:10px;padding:12px 16px 9px;border-bottom:1px solid color-mix(in srgb,var(--ridge) 60%,transparent)}',
|
|
42
|
+
'#card-activity .ab-door-head .ab-t{font-family:var(--display);font-size:15.5px;font-weight:600;color:var(--ink)}',
|
|
43
|
+
'#card-activity .ab-door-head .ab-s{font-size:13px;color:var(--muted)}',
|
|
44
|
+
'#card-activity .ab-door-close{margin-left:auto;background:none;border:1px solid var(--ridge);border-radius:8px;color:var(--muted);cursor:pointer;font:13px var(--sans);padding:3px 9px}',
|
|
45
|
+
'#card-activity .ab-door-close:hover{color:var(--ink);border-color:var(--amber)}',
|
|
46
|
+
'#card-activity .ab-door-view{display:none;padding:10px 16px 14px;max-height:520px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--ridge) transparent}',
|
|
47
|
+
'#card-activity .ab-door-view.ab-active{display:block}',
|
|
48
|
+
'#card-activity .ab-door-view::-webkit-scrollbar{width:8px}',
|
|
49
|
+
'#card-activity .ab-door-view::-webkit-scrollbar-thumb{background:var(--ridge);border-radius:4px}',
|
|
50
|
+
'#card-activity .ab-door-foot{font-size:13px;color:var(--muted);margin-top:10px;padding-top:8px;border-top:1px solid color-mix(in srgb,var(--ridge) 55%,transparent)}',
|
|
51
|
+
'#card-activity .ab-door-foot .mono{font-size:13px;color:var(--ink-2)}',
|
|
52
|
+
/* lessons door */
|
|
53
|
+
'#card-activity .ab-dl-title{font-size:13.5px;font-weight:600;color:var(--amber-text);margin:8px 0 0}',
|
|
54
|
+
'#card-activity .ab-dl-title .ab-g{color:var(--amber);margin-right:7px}',
|
|
55
|
+
'#card-activity .ab-door-view .ab-lesson-card{margin:6px 0 14px;background:var(--surface)}',
|
|
56
|
+
/* memories-breakdown door */
|
|
57
|
+
'#card-activity .ab-krow{display:grid;grid-template-columns:162px 210px 58px 1fr;gap:12px;align-items:center;padding:3.5px 0}',
|
|
58
|
+
'#card-activity .ab-krow .ab-kn{font-family:var(--mono);font-size:13px;color:var(--ink-2)}',
|
|
59
|
+
'#card-activity .ab-krow .ab-kbar{height:8px;border-radius:4px;background:color-mix(in srgb,var(--ridge) 50%,transparent);overflow:hidden}',
|
|
60
|
+
'#card-activity .ab-krow .ab-kbar i{display:block;height:100%;background:var(--amber);border-radius:4px}',
|
|
61
|
+
'#card-activity .ab-krow .ab-kc{font-family:var(--mono);font-size:13px;color:var(--ink);text-align:right;font-variant-numeric:tabular-nums}',
|
|
62
|
+
'#card-activity .ab-krow .ab-kg{font-size:13px;color:var(--ink-2)}',
|
|
63
|
+
/* universe door */
|
|
64
|
+
'#card-activity .ab-urow{display:grid;grid-template-columns:252px 1fr 76px;gap:12px;align-items:center;padding:3.5px 0}',
|
|
65
|
+
'#card-activity .ab-urow .ab-un{font-family:var(--mono);font-size:13px;color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
|
|
66
|
+
'#card-activity .ab-urow .ab-ubar{height:8px;border-radius:4px;background:color-mix(in srgb,var(--ridge) 50%,transparent);overflow:hidden}',
|
|
67
|
+
'#card-activity .ab-urow .ab-ubar i{display:block;height:100%;background:var(--cyan);border-radius:4px;opacity:.8}',
|
|
68
|
+
'#card-activity .ab-urow .ab-uc{font-family:var(--mono);font-size:13px;color:var(--ink);text-align:right;font-variant-numeric:tabular-nums}',
|
|
69
|
+
'#card-activity .ab-urow.ab-me .ab-un,#card-activity .ab-urow.ab-me .ab-uc{color:var(--amber-text);font-weight:600}',
|
|
70
|
+
'#card-activity .ab-urow.ab-me .ab-ubar i{background:var(--amber);opacity:1}',
|
|
71
|
+
'#card-activity .ab-umore{font-size:13px;color:var(--muted);padding:6px 0 0}',
|
|
72
|
+
/* distillation flow strip (84px animation zone + 24px narration bar) */
|
|
73
|
+
'#card-activity .ab-flow{position:relative;height:108px;margin:12px 0 0;border:1px solid var(--ridge);border-radius:12px;background:var(--surface-2);overflow:hidden}',
|
|
74
|
+
'#card-activity .ab-flow canvas{position:absolute;top:0;left:0;width:100%;height:84px;display:block}',
|
|
75
|
+
'#card-activity .ab-flow-label{position:absolute;bottom:31px;font-size:13px;color:var(--ink-2);pointer-events:none;z-index:2}',
|
|
76
|
+
'#card-activity .ab-flow-label .mono{font-size:13px}',
|
|
77
|
+
'#card-activity .ab-flow-label.ab-left{left:14px}',
|
|
78
|
+
'#card-activity .ab-flow-label.ab-gate{left:54%;transform:translateX(-50%);color:var(--amber-text);font-size:13px;letter-spacing:.1em}',
|
|
79
|
+
'#card-activity .ab-flow-label.ab-right{right:14px}',
|
|
80
|
+
'#card-activity .ab-flow-hit{position:absolute;top:0;bottom:24px;z-index:1;cursor:pointer}',
|
|
81
|
+
'#card-activity .ab-flow-hit.ab-left{left:0;width:52%}',
|
|
82
|
+
'#card-activity .ab-flow-hit.ab-right{left:52%;right:0}',
|
|
83
|
+
'#card-activity .ab-flow-hit:hover{background:color-mix(in srgb,var(--amber) 5%,transparent)}',
|
|
84
|
+
'#card-activity .ab-flow-hit:focus-visible{outline:2px solid var(--amber);outline-offset:-2px}',
|
|
85
|
+
/* rolling narration */
|
|
86
|
+
'#card-activity .ab-flow-narr{position:absolute;left:14px;right:14px;bottom:0;height:24px;z-index:2;display:flex;align-items:center;justify-content:center;gap:10px;pointer-events:none}',
|
|
87
|
+
'#card-activity .ab-fn-text{font-family:var(--display);font-style:italic;font-size:13.5px;color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:1;transition:opacity .55s ease}',
|
|
88
|
+
'#card-activity .ab-fn-steps{flex:none;font-size:11px;letter-spacing:3px;line-height:1;color:var(--faint)}',
|
|
89
|
+
'#card-activity .ab-fn-steps .ab-on{color:var(--amber-text)}',
|
|
90
|
+
/* vocabulary + receipt */
|
|
91
|
+
'#card-activity .ab-vocab{font-size:13px;color:var(--ink-2);margin:9px 2px 0}',
|
|
92
|
+
'#card-activity .ab-vocab b{color:var(--ink);font-weight:600}',
|
|
93
|
+
'#card-activity .ab-empty-note{font-size:13px;color:var(--muted);margin:8px 2px 2px;font-style:italic}',
|
|
94
|
+
/* roster strip */
|
|
95
|
+
'#card-activity .ab-roster{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:10px 0 8px}',
|
|
96
|
+
'#card-activity .ab-roster .ab-who{font-size:13px;color:var(--muted);margin-right:2px}',
|
|
97
|
+
'#card-activity .ab-rchip{font:13px var(--mono);color:var(--ink-2);border:1px solid var(--ridge);background:var(--surface-2);border-radius:999px;padding:3px 11px;cursor:default;transition:border-color .15s,color .15s}',
|
|
98
|
+
'#card-activity .ab-rchip:hover{border-color:var(--amber);color:var(--ink)}',
|
|
99
|
+
'#card-activity .ab-rchip .ab-k{color:var(--faint);margin-left:6px}',
|
|
100
|
+
'#card-activity .ab-rchip .ab-cs{color:var(--muted);margin-left:6px;font-size:13px}',
|
|
101
|
+
/* feed */
|
|
102
|
+
'#card-activity .ab-feed{border-top:1px solid var(--ridge);max-height:252px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--ridge) transparent;transition:max-height .3s ease;margin-top:8px}',
|
|
103
|
+
'#card-activity .ab-feed.ab-expanded{max-height:460px}',
|
|
104
|
+
'#card-activity .ab-feed::-webkit-scrollbar{width:8px}',
|
|
105
|
+
'#card-activity .ab-feed::-webkit-scrollbar-thumb{background:var(--ridge);border-radius:4px}',
|
|
106
|
+
'#card-activity .ab-rowwrap{border-bottom:1px solid color-mix(in srgb,var(--ridge) 55%,transparent)}',
|
|
107
|
+
'#card-activity .ab-row{display:grid;grid-template-columns:112px 20px 1fr auto;gap:10px;align-items:baseline;padding:6.5px 6px 6.5px 2px;transition:opacity .18s}',
|
|
108
|
+
'#card-activity .ab-row .ab-ts{font-family:var(--mono);font-size:13px;color:var(--muted);white-space:nowrap}',
|
|
109
|
+
'#card-activity .ab-row .ab-glyph{font-size:13px;text-align:center}',
|
|
110
|
+
'#card-activity .ab-row .ab-glyph.ab-amber{color:var(--amber-text)}',
|
|
111
|
+
'#card-activity .ab-row .ab-glyph.ab-cyan{color:var(--cyan-text)}',
|
|
112
|
+
'#card-activity .ab-row .ab-glyph.ab-green{color:var(--green-text)}',
|
|
113
|
+
'#card-activity .ab-row .ab-txt{font-size:13.5px;color:var(--ink-2)}',
|
|
114
|
+
'#card-activity .ab-badge{font:13px var(--mono);color:var(--muted);border:1px solid var(--ridge);border-radius:6px;padding:1px 7px;white-space:nowrap}',
|
|
115
|
+
'#card-activity .ab-feed.ab-spot .ab-row{opacity:.3}',
|
|
116
|
+
'#card-activity .ab-feed.ab-spot .ab-row.ab-lit{opacity:1}',
|
|
117
|
+
'#card-activity .ab-row.ab-lesson{cursor:pointer}',
|
|
118
|
+
'#card-activity .ab-row.ab-lesson .ab-txt{color:var(--amber-text);font-weight:600}',
|
|
119
|
+
'#card-activity .ab-row.ab-lesson:hover{background:color-mix(in srgb,var(--amber) 14%,transparent)}',
|
|
120
|
+
'#card-activity .ab-row.ab-lesson .ab-rchev{color:var(--faint);font-size:11px;margin-left:8px;display:inline-block;transition:transform .25s}',
|
|
121
|
+
'#card-activity .ab-rowwrap.ab-ropen .ab-rchev{transform:rotate(90deg)}',
|
|
122
|
+
/* drill-down lesson card */
|
|
123
|
+
'#card-activity .ab-exp{display:grid;grid-template-rows:0fr;transition:grid-template-rows .3s ease}',
|
|
124
|
+
'#card-activity .ab-rowwrap.ab-ropen .ab-exp{grid-template-rows:1fr}',
|
|
125
|
+
'#card-activity .ab-exp>div{overflow:hidden}',
|
|
126
|
+
'#card-activity .ab-lesson-card{margin:2px 8px 12px 132px;padding:12px 16px;background:var(--surface-2);border:1px solid var(--ridge);border-left:2px solid var(--amber);border-radius:10px}',
|
|
127
|
+
'#card-activity .ab-seg{margin:0 0 7px;max-width:none}',
|
|
128
|
+
'#card-activity .ab-seg .ab-lab{font-size:13px;font-weight:700;letter-spacing:.09em;font-variant:small-caps;text-transform:lowercase;margin-right:8px}',
|
|
129
|
+
'#card-activity .ab-seg .ab-lab.ab-task{color:var(--cyan-text)}',
|
|
130
|
+
'#card-activity .ab-seg .ab-lab.ab-tried{color:var(--red)}',
|
|
131
|
+
'#card-activity .ab-seg .ab-lab.ab-works{color:var(--green-text)}',
|
|
132
|
+
'#card-activity .ab-seg .ab-lab.ab-crit{color:var(--muted)}',
|
|
133
|
+
'#card-activity .ab-seg .ab-lab.ab-out{color:var(--green-text)}',
|
|
134
|
+
'#card-activity .ab-seg .ab-body{font-size:13.5px;color:var(--ink-2)}',
|
|
135
|
+
'#card-activity .ab-lesson-meta{font-family:var(--mono);font-size:13px;color:var(--muted);margin-top:9px}',
|
|
136
|
+
'#card-activity .ab-lesson-src{font-size:13px;color:var(--muted);margin-top:3px;font-style:italic}',
|
|
137
|
+
/* foot strip */
|
|
138
|
+
'#card-activity .ab-foot{border-top:1px solid var(--ridge);display:flex;align-items:center;gap:14px;padding:10px 2px 2px;margin-top:0}',
|
|
139
|
+
'#card-activity .ab-growth{display:flex;align-items:center;gap:10px}',
|
|
140
|
+
'#card-activity .ab-growth .ab-gl{font-size:13px;color:var(--ink-2)}',
|
|
141
|
+
'#card-activity .ab-thesis{flex:1;text-align:center;font-family:var(--display);font-style:italic;font-size:13.5px;color:var(--muted);line-height:1.45}',
|
|
142
|
+
'#card-activity .ab-thesis .ab-attr{font-style:normal;color:var(--muted);font-size:13px;display:block}',
|
|
143
|
+
'#card-activity .ab-thesis .ab-tagline{display:block;font-style:italic;color:var(--muted);font-size:13px;margin-top:3px}',
|
|
144
|
+
'#card-activity .ab-ghostlink{font-size:13px;color:var(--muted);white-space:nowrap;text-decoration:none;cursor:pointer}',
|
|
145
|
+
'#card-activity .ab-ghostlink:hover{color:var(--amber-text)}',
|
|
146
|
+
/* delight: newest glyph pulses once */
|
|
147
|
+
'@keyframes ab-pulse-once{0%{text-shadow:0 0 0 rgba(240,168,48,0);transform:scale(1)}40%{text-shadow:0 0 14px rgba(240,168,48,.9);transform:scale(1.35)}100%{text-shadow:0 0 0 rgba(240,168,48,0);transform:scale(1)}}',
|
|
148
|
+
'#card-activity .ab-glyph.ab-pulse{animation:ab-pulse-once 1.1s ease-out 1;display:inline-block}',
|
|
149
|
+
'#card-activity .ab-caret{display:inline-block;width:7px;color:var(--amber-text)}',
|
|
150
|
+
/* graceful degrade */
|
|
151
|
+
'#card-activity .ab-fallback{margin-top:16px;font-size:13.5px;color:var(--ink-2);font-style:italic}',
|
|
152
|
+
'@media (prefers-reduced-motion:reduce){',
|
|
153
|
+
' #card-activity .ab-glyph.ab-pulse{animation:none}',
|
|
154
|
+
' #card-activity .ab-exp{transition:none}',
|
|
155
|
+
' #card-activity .ab-doors{transition:none}',
|
|
156
|
+
' #card-activity .ab-fn-text{transition:none}',
|
|
157
|
+
'}',
|
|
158
|
+
'@media (max-width:760px){',
|
|
159
|
+
' #card-activity .ab-stats{grid-template-columns:repeat(2,1fr)}',
|
|
160
|
+
' #card-activity .ab-foot{flex-wrap:wrap}',
|
|
161
|
+
' #card-activity .ab-lesson-card{margin-left:8px}',
|
|
162
|
+
' #card-activity .ab-krow{grid-template-columns:120px 1fr 52px}',
|
|
163
|
+
' #card-activity .ab-krow .ab-kg{grid-column:1/-1;padding-left:2px;margin-top:-2px}',
|
|
164
|
+
' #card-activity .ab-urow{grid-template-columns:140px 1fr 64px}',
|
|
165
|
+
'}'
|
|
166
|
+
].join('\n');
|
|
167
|
+
var styleEl = document.createElement('style');
|
|
168
|
+
styleEl.setAttribute('data-owner', 'card-activity');
|
|
169
|
+
styleEl.textContent = CSS;
|
|
170
|
+
document.head.appendChild(styleEl);
|
|
171
|
+
|
|
172
|
+
/* ------------------------- helpers ------------------------- */
|
|
173
|
+
var MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
|
174
|
+
function fmt(n) { return Number(n || 0).toLocaleString('en-US'); }
|
|
175
|
+
function esc(s) {
|
|
176
|
+
return String(s == null ? '' : s)
|
|
177
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
178
|
+
}
|
|
179
|
+
function fmtTs(at) { // "2026-07-15 15:44:02" → "Jul 15 · 15:44"
|
|
180
|
+
var m = String(at || '').match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})/);
|
|
181
|
+
if (!m) return esc(at || '');
|
|
182
|
+
return MONTHS[+m[2] - 1] + ' ' + (+m[3]) + ' · ' + m[4] + ':' + m[5];
|
|
183
|
+
}
|
|
184
|
+
function fmtDay(day) { // "2026-06-30" → "Jun 30"
|
|
185
|
+
var m = String(day || '').match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
186
|
+
return m ? MONTHS[+m[2] - 1] + ' ' + (+m[3]) : '';
|
|
187
|
+
}
|
|
188
|
+
function setChips(html) {
|
|
189
|
+
var c = document.getElementById('chips-activity');
|
|
190
|
+
if (c) c.innerHTML = html;
|
|
191
|
+
}
|
|
192
|
+
function degrade() {
|
|
193
|
+
BODY.innerHTML = '<p class="ab-fallback">memory store not readable right now</p>';
|
|
194
|
+
setChips('<span class="chip tone-grey">unavailable</span>');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* ---------------- key → plain-English sentence mapper ---------------- */
|
|
198
|
+
var TOOL_LABEL = { memory: 'AgentDB', autocapture: 'Autocapture', hooks: 'Hooks intel', metaharness: 'MetaHarness' };
|
|
199
|
+
var LESSON_TITLES = {
|
|
200
|
+
'lesson-capture-lessons-not-transcripts': 'capture lessons, not transcripts',
|
|
201
|
+
'lesson-telemetry-drowns-signal': 'telemetry drowns the signal at recall time',
|
|
202
|
+
'lesson-verify-by-mechanism': 'verify by mechanism, not by found instances',
|
|
203
|
+
'lesson-never-cp-live-db': 'never cp a live database',
|
|
204
|
+
'lesson-npx-disease': 'the npx disease: fix the producer, not the copies'
|
|
205
|
+
};
|
|
206
|
+
function lessonTitle(key) {
|
|
207
|
+
return LESSON_TITLES[key] || String(key).replace(/^lesson-/, '').replace(/-/g, ' ');
|
|
208
|
+
}
|
|
209
|
+
function describe(r) {
|
|
210
|
+
var k = String(r.key || ''), ns = String(r.namespace || '');
|
|
211
|
+
if (ns === 'lessons' || /^lesson-/.test(k))
|
|
212
|
+
return { tool: 'memory', g: '✦', c: 'amber', txt: 'stored lesson — ' + lessonTitle(k), lessonKey: k };
|
|
213
|
+
if (/^project-state-current/.test(k))
|
|
214
|
+
return { tool: 'memory', g: '✦', c: 'amber', txt: 'project checkpoint written — append-only, safe under concurrent sessions' };
|
|
215
|
+
if (/^session-sessionend/.test(k) || ns === 'sessions')
|
|
216
|
+
return { tool: 'autocapture', g: '◆', c: 'cyan', txt: 'session snapshot captured — nothing lost' };
|
|
217
|
+
if (/^feedback-edit-/.test(k)) {
|
|
218
|
+
var f = k.replace(/^feedback-edit-/, '').replace(/-\d{10,}$/, '').split('/').pop();
|
|
219
|
+
return { tool: 'hooks', g: '●', c: 'green', txt: 'edit verified & feedback scored — ' + f };
|
|
220
|
+
}
|
|
221
|
+
if (ns === 'feedback')
|
|
222
|
+
return { tool: 'hooks', g: '●', c: 'green', txt: 'edit verified & feedback scored' };
|
|
223
|
+
if (/^pattern[_-]/.test(k) || ns === 'pattern')
|
|
224
|
+
return { tool: 'hooks', g: '✦', c: 'amber', txt: "pattern learned from this session's edits" };
|
|
225
|
+
if (/^trajectory-/.test(k) || ns === 'trajectories')
|
|
226
|
+
return { tool: 'hooks', g: '◆', c: 'cyan', txt: 'trajectory recorded — reasoning steps kept for replay' };
|
|
227
|
+
if (ns === 'metaharness-audit' || /^audit-/.test(k))
|
|
228
|
+
return { tool: 'metaharness', g: '●', c: 'green', txt: 'MetaHarness audit passed — composite clean' };
|
|
229
|
+
if (/^session-/.test(k))
|
|
230
|
+
return { tool: 'autocapture', g: '◆', c: 'cyan', txt: 'session observed — ' + k.replace(/^session-/, '').replace(/-\d+$/, '').replace(/-/g, ' ') };
|
|
231
|
+
return { tool: 'memory', g: '✦', c: 'amber', txt: 'stored memory — ' + k.replace(/-\d{10,}$/, '').replace(/[-_]+/g, ' ') };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* ---------------- lesson excerpt → structured segments ---------------- */
|
|
235
|
+
var LAB = { task: 'task', tried: 'tried (failed)', works: 'works', crit: 'critique', out: 'outcome' };
|
|
236
|
+
function parseExcerpt(ex) {
|
|
237
|
+
var parts = String(ex || '').split(/(TASK:|TRIED\s*\(failed\):|WORKED(?:\s*\(fix\))?:|CRITIQUE:|OUTCOME:)/);
|
|
238
|
+
var kindOf = function (tok) {
|
|
239
|
+
if (/^TASK/.test(tok)) return 'task';
|
|
240
|
+
if (/^TRIED/.test(tok)) return 'tried';
|
|
241
|
+
if (/^WORKED/.test(tok)) return 'works';
|
|
242
|
+
if (/^CRITIQUE/.test(tok)) return 'crit';
|
|
243
|
+
if (/^OUTCOME/.test(tok)) return 'out';
|
|
244
|
+
return null;
|
|
245
|
+
};
|
|
246
|
+
var segs = [];
|
|
247
|
+
for (var i = 1; i < parts.length; i += 2) {
|
|
248
|
+
var kind = kindOf(parts[i]);
|
|
249
|
+
var body = (parts[i + 1] || '').trim();
|
|
250
|
+
if (kind && body) segs.push([kind, body]);
|
|
251
|
+
}
|
|
252
|
+
if (!segs.length && String(ex || '').trim()) segs.push(['task', String(ex).trim()]);
|
|
253
|
+
return segs;
|
|
254
|
+
}
|
|
255
|
+
function lessonCardHTML(L) {
|
|
256
|
+
var inner = '<div class="ab-lesson-card">';
|
|
257
|
+
L.segs.forEach(function (kv) {
|
|
258
|
+
inner += '<p class="ab-seg"><span class="ab-lab ab-' + kv[0] + '">' + LAB[kv[0]] + '</span><span class="ab-body">' + esc(kv[1]) + '</span></p>';
|
|
259
|
+
});
|
|
260
|
+
inner += '<div class="ab-lesson-meta">learned ' + esc(L.learned) + ' · recalled ' + (L.recalled || 0) + '×</div>';
|
|
261
|
+
inner += '<div class="ab-lesson-src">a real memory from this machine — .swarm/memory.db</div>';
|
|
262
|
+
inner += '</div>';
|
|
263
|
+
return inner;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* ------------------------- fetch + render ------------------------- */
|
|
267
|
+
fetch('/api/activity', { headers: { Accept: 'application/json' } })
|
|
268
|
+
.then(function (r) { if (!r.ok) throw new Error('http ' + r.status); return r.json(); })
|
|
269
|
+
.then(function (d) {
|
|
270
|
+
if (!d || d.hasStore === false || !d.totals) { degrade(); return; }
|
|
271
|
+
render(d);
|
|
272
|
+
})
|
|
273
|
+
.catch(degrade);
|
|
274
|
+
|
|
275
|
+
function render(d) {
|
|
276
|
+
var totals = d.totals || {};
|
|
277
|
+
var lessonsRaw = Array.isArray(d.lessons) ? d.lessons : [];
|
|
278
|
+
var recent = Array.isArray(d.recent) ? d.recent : [];
|
|
279
|
+
var breakdown = Array.isArray(d.breakdown) ? d.breakdown : [];
|
|
280
|
+
var growthRaw = Array.isArray(d.growth) ? d.growth : [];
|
|
281
|
+
var machine = d.machine || {};
|
|
282
|
+
var projects = (Array.isArray(machine.projects) ? machine.projects.slice() : [])
|
|
283
|
+
.sort(function (a, b) { return (b.memories || 0) - (a.memories || 0); });
|
|
284
|
+
var projectName = d.project || 'this project';
|
|
285
|
+
var nMem = totals.memories || 0, nLes = totals.lessons || 0;
|
|
286
|
+
var nProj = projects.length, nMachine = machine.totalMemories || 0;
|
|
287
|
+
|
|
288
|
+
var LESSONS = lessonsRaw.map(function (l) {
|
|
289
|
+
return { key: l.key, learned: l.learned, recalled: l.access_count || 0, segs: parseExcerpt(l.excerpt) };
|
|
290
|
+
});
|
|
291
|
+
var lessonByKey = {};
|
|
292
|
+
LESSONS.forEach(function (L) { lessonByKey[L.key] = L; });
|
|
293
|
+
|
|
294
|
+
/* summary chips */
|
|
295
|
+
setChips(
|
|
296
|
+
'<span class="chip tone-amber">' + fmt(nMem) + ' memories</span>' +
|
|
297
|
+
'<span class="chip tone-green">' + fmt(nLes) + ' lessons</span>'
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
/* ---------------- panel skeleton ---------------- */
|
|
301
|
+
BODY.innerHTML =
|
|
302
|
+
'<div class="ab-stats">' +
|
|
303
|
+
'<div class="ab-stat ab-door" id="ab-stat-memories" role="button" tabindex="0" aria-expanded="false" aria-controls="ab-doors">' +
|
|
304
|
+
'<span class="ab-hint"></span><span class="ab-tchev">▸</span>' +
|
|
305
|
+
'<div class="ab-n">' + fmt(nMem) + '</div><div class="ab-l">memories · this project</div></div>' +
|
|
306
|
+
'<div class="ab-stat ab-door" id="ab-stat-lessons" role="button" tabindex="0" aria-expanded="false" aria-controls="ab-doors">' +
|
|
307
|
+
'<span class="ab-hint"></span><span class="ab-tchev">▸</span>' +
|
|
308
|
+
'<div class="ab-n">' + fmt(nLes) + '</div><div class="ab-l">distilled lessons</div></div>' +
|
|
309
|
+
'<div class="ab-stat ab-door" id="ab-stat-projects" role="button" tabindex="0" aria-expanded="false" aria-controls="ab-doors">' +
|
|
310
|
+
'<span class="ab-hint"></span><span class="ab-tchev">▸</span>' +
|
|
311
|
+
'<div class="ab-n">' + fmt(nProj) + '</div><div class="ab-l">projects machine-wide</div></div>' +
|
|
312
|
+
'<div class="ab-stat ab-door" id="ab-stat-machine" role="button" tabindex="0" aria-expanded="false" aria-controls="ab-doors">' +
|
|
313
|
+
'<span class="ab-hint"></span><span class="ab-tchev">▸</span>' +
|
|
314
|
+
'<div class="ab-n">' + fmt(nMachine) + '</div><div class="ab-l">memories machine-wide</div></div>' +
|
|
315
|
+
'</div>' +
|
|
316
|
+
'<div class="ab-doors" id="ab-doors">' +
|
|
317
|
+
'<div class="ab-doors-clip"><div class="ab-door-box">' +
|
|
318
|
+
'<div class="ab-door-head">' +
|
|
319
|
+
'<span class="ab-t" id="ab-doorTitle"></span><span class="ab-s" id="ab-doorSub"></span>' +
|
|
320
|
+
'<button class="ab-door-close" id="ab-doorClose" aria-label="close panel view">✕ close</button>' +
|
|
321
|
+
'</div>' +
|
|
322
|
+
'<div class="ab-door-view" id="ab-view-lessons"></div>' +
|
|
323
|
+
'<div class="ab-door-view" id="ab-view-memories"></div>' +
|
|
324
|
+
'<div class="ab-door-view" id="ab-view-universe"></div>' +
|
|
325
|
+
'</div></div>' +
|
|
326
|
+
'</div>' +
|
|
327
|
+
'<div class="ab-flow" id="ab-flow" aria-label="' + fmt(nMem) + ' captured memories distill down to ' + fmt(nLes) + ' kept lessons">' +
|
|
328
|
+
'<canvas id="ab-flowCanvas"></canvas>' +
|
|
329
|
+
'<div class="ab-flow-hit ab-left" id="ab-flowHitLeft" role="button" tabindex="0" aria-label="see the ' + fmt(nMem) + ' memories by kind" title="see the ' + fmt(nMem) + ' memories by kind"></div>' +
|
|
330
|
+
'<div class="ab-flow-hit ab-right" id="ab-flowHitRight" role="button" tabindex="0" aria-label="open the ' + fmt(nLes) + ' distilled lessons" title="open the ' + fmt(nLes) + ' distilled lessons"></div>' +
|
|
331
|
+
'<div class="ab-flow-label ab-left"><span class="mono">' + fmt(nMem) + '</span> captured</div>' +
|
|
332
|
+
'<div class="ab-flow-label ab-gate">distill</div>' +
|
|
333
|
+
'<div class="ab-flow-label ab-right"><span class="mono">' + fmt(nLes) + '</span> lessons — the keepers</div>' +
|
|
334
|
+
'<div class="ab-flow-narr"><span class="ab-fn-text" id="ab-fnText"></span><span class="ab-fn-steps" id="ab-fnSteps" aria-hidden="true"></span></div>' +
|
|
335
|
+
'</div>' +
|
|
336
|
+
'<div class="ab-vocab"><b>memories</b> = everything your AI captures automatically · <b>lessons</b> = what survives distillation (task → what failed → what works)</div>' +
|
|
337
|
+
(nLes ? '' : '<div class="ab-empty-note">0 lessons recorded yet. Lessons are captured manually: run <code>record-lesson</code> in this project to keep one. There is no automatic distillation into lessons yet.</div>') +
|
|
338
|
+
'<div class="ab-roster" id="ab-roster">' +
|
|
339
|
+
'<span class="ab-who">written by</span>' +
|
|
340
|
+
'<span class="ab-rchip" data-tool="memory">AgentDB<span class="ab-cs">via ruflo memory</span><span class="ab-k">✦</span></span>' +
|
|
341
|
+
'<span class="ab-rchip" data-tool="autocapture">Autocapture hooks<span class="ab-k">◆</span></span>' +
|
|
342
|
+
'<span class="ab-rchip" data-tool="hooks">Hooks intelligence<span class="ab-k">◆●</span></span>' +
|
|
343
|
+
'<span class="ab-rchip" data-tool="metaharness">MetaHarness<span class="ab-k">●</span></span>' +
|
|
344
|
+
'</div>' +
|
|
345
|
+
'<div class="ab-feed" id="ab-feed" role="log" aria-label="recent brain activity"></div>' +
|
|
346
|
+
'<div class="ab-foot">' +
|
|
347
|
+
'<div class="ab-growth"><svg id="ab-spark" width="140" height="28" viewBox="0 0 140 28" aria-label="memory growth sparkline"></svg>' +
|
|
348
|
+
'<span class="ab-gl" id="ab-growthLabel"></span></div>' +
|
|
349
|
+
'<div class="ab-thesis">“not just visualization — visualization + personalization is the product.”' +
|
|
350
|
+
'<span class="ab-attr">— product thesis · stored to memory Jul 15</span>' +
|
|
351
|
+
'<span class="ab-tagline">the model is the engine — this is the harness driving it.</span></div>' +
|
|
352
|
+
'<a class="ab-ghostlink" href="#" id="ab-openUniverse">open universe view →</a>' +
|
|
353
|
+
'</div>';
|
|
354
|
+
|
|
355
|
+
var feedEl = document.getElementById('ab-feed');
|
|
356
|
+
var doorsEl = document.getElementById('ab-doors');
|
|
357
|
+
|
|
358
|
+
/* ---------------- feed ---------------- */
|
|
359
|
+
recent.forEach(function (r, i) {
|
|
360
|
+
var m = describe(r);
|
|
361
|
+
var L = m.lessonKey ? lessonByKey[m.lessonKey] : null;
|
|
362
|
+
var isLesson = !!L;
|
|
363
|
+
var wrap = document.createElement('div');
|
|
364
|
+
wrap.className = 'ab-rowwrap';
|
|
365
|
+
var row = document.createElement('div');
|
|
366
|
+
row.className = 'ab-row' + (isLesson ? ' ab-lesson' : '');
|
|
367
|
+
row.dataset.tool = m.tool;
|
|
368
|
+
row.title = (r.key || '') + ' · ' + (r.namespace || '');
|
|
369
|
+
if (isLesson) { row.setAttribute('role', 'button'); row.setAttribute('tabindex', '0'); row.setAttribute('aria-expanded', 'false'); }
|
|
370
|
+
row.innerHTML =
|
|
371
|
+
'<span class="ab-ts">' + fmtTs(r.at) + '</span>' +
|
|
372
|
+
'<span class="ab-glyph ab-' + m.c + '">' + m.g + '</span>' +
|
|
373
|
+
'<span class="ab-txt">' + esc(m.txt) + (isLesson ? '<span class="ab-rchev">▸</span>' : '') + '</span>' +
|
|
374
|
+
'<span class="ab-badge">' + TOOL_LABEL[m.tool] + '</span>';
|
|
375
|
+
wrap.appendChild(row);
|
|
376
|
+
if (isLesson) {
|
|
377
|
+
var exp = document.createElement('div');
|
|
378
|
+
exp.className = 'ab-exp';
|
|
379
|
+
exp.innerHTML = '<div>' + lessonCardHTML(L) + '</div>';
|
|
380
|
+
wrap.appendChild(exp);
|
|
381
|
+
var toggle = function () {
|
|
382
|
+
wrap.classList.toggle('ab-ropen');
|
|
383
|
+
var open = wrap.classList.contains('ab-ropen');
|
|
384
|
+
row.setAttribute('aria-expanded', open);
|
|
385
|
+
feedEl.classList.toggle('ab-expanded', !!feedEl.querySelector('.ab-rowwrap.ab-ropen'));
|
|
386
|
+
if (open) setTimeout(function () { wrap.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, 320);
|
|
387
|
+
};
|
|
388
|
+
row.addEventListener('click', toggle);
|
|
389
|
+
row.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); } });
|
|
390
|
+
}
|
|
391
|
+
feedEl.appendChild(wrap);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
/* roster hover → spotlight matching rows */
|
|
395
|
+
BODY.querySelectorAll('.ab-rchip').forEach(function (chip) {
|
|
396
|
+
chip.addEventListener('mouseenter', function () {
|
|
397
|
+
feedEl.classList.add('ab-spot');
|
|
398
|
+
feedEl.querySelectorAll('.ab-row').forEach(function (r) { r.classList.toggle('ab-lit', r.dataset.tool === chip.dataset.tool); });
|
|
399
|
+
});
|
|
400
|
+
chip.addEventListener('mouseleave', function () {
|
|
401
|
+
feedEl.classList.remove('ab-spot');
|
|
402
|
+
feedEl.querySelectorAll('.ab-row').forEach(function (r) { r.classList.remove('ab-lit'); });
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
/* ---------------- doors ---------------- */
|
|
407
|
+
document.getElementById('ab-view-lessons').innerHTML = LESSONS.length
|
|
408
|
+
? LESSONS.map(function (L) {
|
|
409
|
+
return '<div class="ab-dl-title"><span class="ab-g">✦</span>' + esc(lessonTitle(L.key)) + '</div>' + lessonCardHTML(L);
|
|
410
|
+
}).join('')
|
|
411
|
+
: '<p class="ab-fallback">0 lessons recorded yet. Lessons are captured manually: run <code>record-lesson</code> in this project to keep one. There is no automatic distillation into lessons yet.</p>';
|
|
412
|
+
|
|
413
|
+
(function () {
|
|
414
|
+
var GLOSS = {
|
|
415
|
+
'ruvnet-brain': 'project knowledge & KB work',
|
|
416
|
+
sessions: 'session snapshots (compaction survivors)',
|
|
417
|
+
'default': 'checkpoints & decisions',
|
|
418
|
+
'adr-patterns': 'architecture decision patterns',
|
|
419
|
+
'adr-edges': 'links between decisions',
|
|
420
|
+
trajectories: 'reasoning paths kept for replay',
|
|
421
|
+
lessons: 'distilled lessons — the keepers',
|
|
422
|
+
'metaharness-audit': 'MetaHarness audit runs',
|
|
423
|
+
feedback: 'edit verifications, scored',
|
|
424
|
+
pattern: 'learned edit patterns',
|
|
425
|
+
session: 'observed sessions'
|
|
426
|
+
};
|
|
427
|
+
if (!breakdown.length) {
|
|
428
|
+
document.getElementById('ab-view-memories').innerHTML = '<p class="ab-fallback">no namespace breakdown available</p>';
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
var lmax = Math.log10(Math.max.apply(null, breakdown.map(function (b) { return b.n; })) + 1);
|
|
432
|
+
document.getElementById('ab-view-memories').innerHTML = breakdown.map(function (b) {
|
|
433
|
+
var w = Math.max(9, Math.round(100 * Math.log10(b.n + 1) / lmax));
|
|
434
|
+
return '<div class="ab-krow"><span class="ab-kn">' + esc(b.namespace) + '</span>' +
|
|
435
|
+
'<span class="ab-kbar"><i style="width:' + w + '%"></i></span>' +
|
|
436
|
+
'<span class="ab-kc">' + fmt(b.n) + '</span>' +
|
|
437
|
+
'<span class="ab-kg">' + esc(GLOSS[b.namespace] || '') + '</span></div>';
|
|
438
|
+
}).join('') +
|
|
439
|
+
'<div class="ab-door-foot">' + breakdown.length + ' namespaces · <span class="mono">' + fmt(nMem) + '</span> rows — live from <span class="mono">.swarm/memory.db</span> · bars log-scaled</div>';
|
|
440
|
+
})();
|
|
441
|
+
|
|
442
|
+
(function () {
|
|
443
|
+
if (!projects.length) {
|
|
444
|
+
document.getElementById('ab-view-universe').innerHTML = '<p class="ab-fallback">no machine-wide data available</p>';
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
var others = projects.filter(function (p) { return p.name !== projectName; }).slice(0, 12);
|
|
448
|
+
var meRow = projects.filter(function (p) { return p.name === projectName; })[0] || { name: projectName, memories: nMem };
|
|
449
|
+
var more = Math.max(0, projects.length - others.length - 1);
|
|
450
|
+
var top = Math.log10(Math.max(projects[0].memories || 1, 10));
|
|
451
|
+
var base = Math.min(2.7, top - 0.5);
|
|
452
|
+
var wU = function (n) { return Math.round(100 * Math.max(0.10, (Math.log10(Math.max(n, 1)) - base) / (top - base))); };
|
|
453
|
+
var rowHTML = function (name, n, me) {
|
|
454
|
+
return '<div class="ab-urow' + (me ? ' ab-me' : '') + '"><span class="ab-un">' + esc(name) + (me ? ' — you are here' : '') + '</span>' +
|
|
455
|
+
'<span class="ab-ubar"><i style="width:' + wU(n) + '%"></i></span>' +
|
|
456
|
+
'<span class="ab-uc">' + fmt(n) + '</span></div>';
|
|
457
|
+
};
|
|
458
|
+
document.getElementById('ab-view-universe').innerHTML =
|
|
459
|
+
others.map(function (p) { return rowHTML(p.name, p.memories, false); }).join('') +
|
|
460
|
+
rowHTML(meRow.name, meRow.memories, true) +
|
|
461
|
+
(more ? '<div class="ab-umore">…and ' + more + ' more projects</div>' : '') +
|
|
462
|
+
'<div class="ab-door-foot"><span class="mono">' + fmt(nMachine) + '</span> memories machine-wide — every one persists across sessions and reboots</div>';
|
|
463
|
+
})();
|
|
464
|
+
|
|
465
|
+
var DOOR_META = {
|
|
466
|
+
lessons: [fmt(nLes) + ' distilled lessons', 'what survived distillation — every card is a real memory'],
|
|
467
|
+
memories: [fmt(nMem) + ' memories in this project', 'by namespace — what your AI writes down as you work'],
|
|
468
|
+
universe: [fmt(nProj) + ' projects machine-wide', "everywhere this machine's AI remembers"]
|
|
469
|
+
};
|
|
470
|
+
var VIEW_OF = { 'ab-stat-memories': 'memories', 'ab-stat-lessons': 'lessons', 'ab-stat-projects': 'universe', 'ab-stat-machine': 'universe' };
|
|
471
|
+
var curView = null, curTile = null;
|
|
472
|
+
|
|
473
|
+
function openDoor(view, tileId) {
|
|
474
|
+
BODY.querySelectorAll('.ab-door-view').forEach(function (v) { v.classList.toggle('ab-active', v.id === 'ab-view-' + view); });
|
|
475
|
+
BODY.querySelectorAll('.ab-stat.ab-door').forEach(function (s) {
|
|
476
|
+
var on = s.id === tileId;
|
|
477
|
+
s.classList.toggle('ab-on', on);
|
|
478
|
+
s.setAttribute('aria-expanded', on ? 'true' : 'false');
|
|
479
|
+
});
|
|
480
|
+
document.getElementById('ab-doorTitle').textContent = DOOR_META[view][0];
|
|
481
|
+
document.getElementById('ab-doorSub').textContent = DOOR_META[view][1];
|
|
482
|
+
doorsEl.classList.add('ab-open');
|
|
483
|
+
curView = view; curTile = tileId;
|
|
484
|
+
setTimeout(function () { doorsEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, 380);
|
|
485
|
+
}
|
|
486
|
+
function closeDoors() {
|
|
487
|
+
doorsEl.classList.remove('ab-open');
|
|
488
|
+
BODY.querySelectorAll('.ab-stat.ab-door').forEach(function (s) { s.classList.remove('ab-on'); s.setAttribute('aria-expanded', 'false'); });
|
|
489
|
+
curView = null; curTile = null;
|
|
490
|
+
}
|
|
491
|
+
BODY.querySelectorAll('.ab-stat.ab-door').forEach(function (tile) {
|
|
492
|
+
var go = function () { (curTile === tile.id) ? closeDoors() : openDoor(VIEW_OF[tile.id], tile.id); };
|
|
493
|
+
tile.addEventListener('click', go);
|
|
494
|
+
tile.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); go(); } });
|
|
495
|
+
});
|
|
496
|
+
document.getElementById('ab-doorClose').addEventListener('click', closeDoors);
|
|
497
|
+
document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && curView) closeDoors(); });
|
|
498
|
+
document.getElementById('ab-openUniverse').addEventListener('click', function (e) {
|
|
499
|
+
e.preventDefault(); openDoor('universe', 'ab-stat-machine');
|
|
500
|
+
BODY.querySelector('.ab-stats').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
501
|
+
});
|
|
502
|
+
[['ab-flowHitLeft', 'memories', 'ab-stat-memories'], ['ab-flowHitRight', 'lessons', 'ab-stat-lessons']].forEach(function (t) {
|
|
503
|
+
var el = document.getElementById(t[0]);
|
|
504
|
+
var go = function () { (curView === t[1] && curTile === t[2]) ? closeDoors() : openDoor(t[1], t[2]); };
|
|
505
|
+
el.addEventListener('click', go);
|
|
506
|
+
el.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); go(); } });
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
/* ---------------- distillation flow strip — canvas ---------------- */
|
|
510
|
+
(function () {
|
|
511
|
+
var flowEl = document.getElementById('ab-flow');
|
|
512
|
+
var cv = document.getElementById('ab-flowCanvas');
|
|
513
|
+
var ctx = cv.getContext('2d');
|
|
514
|
+
var reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
515
|
+
var W = 0, H = 0, colors = {};
|
|
516
|
+
|
|
517
|
+
function refreshColors() {
|
|
518
|
+
var cs = getComputedStyle(document.documentElement);
|
|
519
|
+
colors = {
|
|
520
|
+
muted: cs.getPropertyValue('--muted').trim(),
|
|
521
|
+
faint: cs.getPropertyValue('--faint').trim(),
|
|
522
|
+
amber: cs.getPropertyValue('--amber').trim()
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
refreshColors();
|
|
526
|
+
/* the console theme toggle flips html[data-theme] — re-read the palette */
|
|
527
|
+
new MutationObserver(function () { refreshColors(); if (reduce) drawScene(0, true); })
|
|
528
|
+
.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
|
|
529
|
+
|
|
530
|
+
var dots = Array.from({ length: 76 }, function () {
|
|
531
|
+
return { fx: .025 + Math.random() * .375, fy: .14 + Math.random() * .56, r: .8 + Math.random() * .9, a: .3 + Math.random() * .4, ph: Math.random() * 6.283 };
|
|
532
|
+
});
|
|
533
|
+
/* honest: zero lessons means zero keeper-stars, never a decorative placeholder count */
|
|
534
|
+
var nStars = nLes > 0 ? Math.max(1, Math.min(7, nLes)) : 0;
|
|
535
|
+
var stars = Array.from({ length: nStars }, function (_, i) {
|
|
536
|
+
return { fx: .665 + i * (.31 / Math.max(nStars, 2)), ph: Math.random() * 6.283, pulse: -9999 };
|
|
537
|
+
});
|
|
538
|
+
var GX = .54, CY = .40;
|
|
539
|
+
|
|
540
|
+
function roundBar(x, y, w, h) {
|
|
541
|
+
var r = 1.5;
|
|
542
|
+
ctx.beginPath();
|
|
543
|
+
ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r);
|
|
544
|
+
ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.fill();
|
|
545
|
+
}
|
|
546
|
+
function sparkle(x, y, s) {
|
|
547
|
+
ctx.beginPath();
|
|
548
|
+
ctx.moveTo(x, y - s);
|
|
549
|
+
ctx.quadraticCurveTo(x + s * .18, y - s * .18, x + s, y);
|
|
550
|
+
ctx.quadraticCurveTo(x + s * .18, y + s * .18, x, y + s);
|
|
551
|
+
ctx.quadraticCurveTo(x - s * .18, y + s * .18, x - s, y);
|
|
552
|
+
ctx.quadraticCurveTo(x - s * .18, y - s * .18, x, y - s);
|
|
553
|
+
ctx.closePath(); ctx.fill();
|
|
554
|
+
}
|
|
555
|
+
var ease = function (p) { return p < 0 ? 0 : p > 1 ? 1 : p * p * (3 - 2 * p); };
|
|
556
|
+
|
|
557
|
+
var motes = [], lastSpawn = 0, nextGap = 1200, seq = 0, cueLine = 0;
|
|
558
|
+
function spawn(now, forcePass) {
|
|
559
|
+
var d0 = dots[(Math.random() * dots.length) | 0];
|
|
560
|
+
/* honest: with zero real lessons there are zero keepers — never let a mote pass through */
|
|
561
|
+
var pass = nStars > 0 && (forcePass !== undefined ? forcePass : (cueLine === 2 ? false : (seq++ % 4) === 2));
|
|
562
|
+
motes.push({ sfx: d0.fx, sfy: d0.fy, t0: now, dur: 2400 + Math.random() * 700, pass: pass, star: (Math.random() * nStars) | 0, phase: 0, t1: 0 });
|
|
563
|
+
}
|
|
564
|
+
/* narration sync (best-effort): line 2 → motes dissolve at the gate; line 3 → send one keeper through (only if any exist) */
|
|
565
|
+
window.__abFlowCue = function (line) {
|
|
566
|
+
cueLine = line;
|
|
567
|
+
if (line === 3 && nStars > 0 && !reduce && !document.hidden) { spawn(performance.now(), true); lastSpawn = performance.now(); }
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
function drawScene(now, still) {
|
|
571
|
+
if (!W || !H) return;
|
|
572
|
+
ctx.clearRect(0, 0, W, H);
|
|
573
|
+
var cy = H * CY, gx = W * GX;
|
|
574
|
+
|
|
575
|
+
dots.forEach(function (d0) {
|
|
576
|
+
var y = d0.fy * H + (still ? 0 : Math.sin(now * .0006 + d0.ph) * 1.4);
|
|
577
|
+
ctx.globalAlpha = d0.a;
|
|
578
|
+
ctx.fillStyle = colors.muted;
|
|
579
|
+
ctx.beginPath(); ctx.arc(d0.fx * W, y, d0.r, 0, 6.283); ctx.fill();
|
|
580
|
+
});
|
|
581
|
+
ctx.globalAlpha = 1;
|
|
582
|
+
|
|
583
|
+
ctx.globalAlpha = .05;
|
|
584
|
+
ctx.fillStyle = colors.amber;
|
|
585
|
+
ctx.beginPath(); ctx.arc(gx, cy, 15, 0, 6.283); ctx.fill();
|
|
586
|
+
ctx.globalAlpha = 1;
|
|
587
|
+
ctx.shadowColor = colors.amber; ctx.shadowBlur = 8;
|
|
588
|
+
roundBar(gx - 1.5, cy - 26, 3, 17);
|
|
589
|
+
roundBar(gx - 1.5, cy + 9, 3, 17);
|
|
590
|
+
ctx.shadowBlur = 0;
|
|
591
|
+
|
|
592
|
+
stars.forEach(function (s) {
|
|
593
|
+
var x = s.fx * W;
|
|
594
|
+
var tw = still ? 1 : .82 + .18 * Math.sin(now * .0011 + s.ph);
|
|
595
|
+
var since = now - s.pulse;
|
|
596
|
+
var pk = (!still && since < 700) ? (1 - since / 700) : 0;
|
|
597
|
+
ctx.globalAlpha = Math.min(1, tw + pk * .4);
|
|
598
|
+
ctx.fillStyle = colors.amber;
|
|
599
|
+
if (pk > 0) { ctx.shadowColor = colors.amber; ctx.shadowBlur = 14 * pk; }
|
|
600
|
+
sparkle(x, cy, 7 + 3 * pk);
|
|
601
|
+
ctx.shadowBlur = 0;
|
|
602
|
+
});
|
|
603
|
+
ctx.globalAlpha = 1;
|
|
604
|
+
|
|
605
|
+
if (still) {
|
|
606
|
+
ctx.fillStyle = colors.muted; ctx.globalAlpha = .6;
|
|
607
|
+
[.42, .48].forEach(function (f) { ctx.beginPath(); ctx.arc(f * W, cy - 3, 1.6, 0, 6.283); ctx.fill(); });
|
|
608
|
+
ctx.globalAlpha = 1;
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
for (var i = motes.length - 1; i >= 0; i--) {
|
|
613
|
+
var m = motes[i];
|
|
614
|
+
if (m.phase === 0) {
|
|
615
|
+
var p = (now - m.t0) / m.dur;
|
|
616
|
+
if (p >= 1) { m.phase = m.pass ? 2 : 1; m.t1 = now; }
|
|
617
|
+
else {
|
|
618
|
+
var e = ease(p);
|
|
619
|
+
var x = m.sfx * W + (gx - m.sfx * W) * e;
|
|
620
|
+
var y = m.sfy * H + (cy - m.sfy * H) * e - Math.sin(p * Math.PI) * 5;
|
|
621
|
+
ctx.globalAlpha = .45 + .45 * e;
|
|
622
|
+
ctx.fillStyle = e > .75 ? colors.amber : colors.muted;
|
|
623
|
+
ctx.beginPath(); ctx.arc(x, y, 1.6, 0, 6.283); ctx.fill();
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (m.phase === 1) {
|
|
628
|
+
var p1 = (now - m.t1) / 480;
|
|
629
|
+
if (p1 >= 1) { motes.splice(i, 1); continue; }
|
|
630
|
+
ctx.globalAlpha = .5 * (1 - p1);
|
|
631
|
+
ctx.fillStyle = colors.faint;
|
|
632
|
+
ctx.beginPath(); ctx.arc(gx + 5 + p1 * 7, cy, 1.6 + p1 * 2.6, 0, 6.283); ctx.fill();
|
|
633
|
+
} else if (m.phase === 2) {
|
|
634
|
+
var p2 = (now - m.t1) / 1050;
|
|
635
|
+
var st = stars[m.star], tx = st.fx * W;
|
|
636
|
+
if (p2 >= 1) { st.pulse = now; motes.splice(i, 1); continue; }
|
|
637
|
+
var e2 = ease(p2);
|
|
638
|
+
ctx.globalAlpha = .95;
|
|
639
|
+
ctx.fillStyle = colors.amber;
|
|
640
|
+
ctx.beginPath(); ctx.arc(gx + (tx - gx) * e2, cy - Math.sin(p2 * Math.PI) * 4, 1.8, 0, 6.283); ctx.fill();
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
ctx.globalAlpha = 1;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function resize() {
|
|
647
|
+
var dpr = window.devicePixelRatio || 1;
|
|
648
|
+
W = flowEl.clientWidth; H = cv.clientHeight;
|
|
649
|
+
if (!W || !H) return;
|
|
650
|
+
cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr);
|
|
651
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
652
|
+
if (reduce) drawScene(0, true);
|
|
653
|
+
}
|
|
654
|
+
resize();
|
|
655
|
+
addEventListener('resize', resize);
|
|
656
|
+
CARD.addEventListener('toggle', function () { if (CARD.open) resize(); });
|
|
657
|
+
|
|
658
|
+
if (!reduce) {
|
|
659
|
+
var started = false;
|
|
660
|
+
var loop = function (now) {
|
|
661
|
+
if (!started) { spawn(now - 1500); lastSpawn = now; nextGap = 1100; started = true; }
|
|
662
|
+
if (!document.hidden && CARD.open) {
|
|
663
|
+
if (now - lastSpawn > nextGap) { spawn(now); lastSpawn = now; nextGap = 1500 + Math.random() * 700; }
|
|
664
|
+
drawScene(now, false);
|
|
665
|
+
}
|
|
666
|
+
requestAnimationFrame(loop);
|
|
667
|
+
};
|
|
668
|
+
requestAnimationFrame(loop);
|
|
669
|
+
}
|
|
670
|
+
})();
|
|
671
|
+
|
|
672
|
+
/* ---------------- sparkline — live daily counts (zero-filled) ---------------- */
|
|
673
|
+
(function () {
|
|
674
|
+
var svg = document.getElementById('ab-spark');
|
|
675
|
+
var series = [];
|
|
676
|
+
if (growthRaw.length) {
|
|
677
|
+
var map = {};
|
|
678
|
+
growthRaw.forEach(function (g) { map[g.day] = g.n; });
|
|
679
|
+
var t0 = Date.parse(growthRaw[0].day + 'T00:00:00Z');
|
|
680
|
+
var t1 = Date.parse(growthRaw[growthRaw.length - 1].day + 'T00:00:00Z');
|
|
681
|
+
for (var t = t0; t <= t1; t += 86400000) {
|
|
682
|
+
var k = new Date(t).toISOString().slice(0, 10);
|
|
683
|
+
series.push(map[k] || 0);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (series.length < 2) series = [0, 0];
|
|
687
|
+
var W = 140, H = 28, P = 2, max = Math.max.apply(null, series.concat([1]));
|
|
688
|
+
var pts = series.map(function (n, i) {
|
|
689
|
+
var x = P + i * (W - 2 * P) / (series.length - 1);
|
|
690
|
+
var y = H - P - (n / max) * (H - 2 * P);
|
|
691
|
+
return [x.toFixed(1), y.toFixed(1)];
|
|
692
|
+
});
|
|
693
|
+
var line = pts.map(function (p) { return p.join(','); }).join(' ');
|
|
694
|
+
var area = line + ' ' + (W - P) + ',' + (H - P) + ' ' + P + ',' + (H - P);
|
|
695
|
+
svg.innerHTML =
|
|
696
|
+
'<polygon points="' + area + '" fill="var(--cyan)" opacity="0.12"/>' +
|
|
697
|
+
'<polyline points="' + line + '" fill="none" stroke="var(--cyan)" stroke-width="1.5" stroke-linejoin="round"/>' +
|
|
698
|
+
'<circle cx="' + pts[pts.length - 1][0] + '" cy="' + pts[pts.length - 1][1] + '" r="2" fill="var(--cyan)"/>';
|
|
699
|
+
document.getElementById('ab-growthLabel').textContent =
|
|
700
|
+
'memory growth · ' + (growthRaw.length ? fmtDay(growthRaw[0].day) + ' → today' : 'no data yet');
|
|
701
|
+
})();
|
|
702
|
+
|
|
703
|
+
/* ---------------- the ONE delight: newest row types itself in ---------------- */
|
|
704
|
+
(function () {
|
|
705
|
+
var newRow = feedEl.querySelector('.ab-row');
|
|
706
|
+
if (!newRow) return;
|
|
707
|
+
var reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
708
|
+
var txtEl = newRow.querySelector('.ab-txt');
|
|
709
|
+
var glyph = newRow.querySelector('.ab-glyph');
|
|
710
|
+
glyph.classList.add('ab-pulse');
|
|
711
|
+
if (reduce || newRow.classList.contains('ab-lesson')) return;
|
|
712
|
+
var full = txtEl.textContent;
|
|
713
|
+
txtEl.innerHTML = '<span class="ab-typed"></span><span class="ab-caret">▍</span>';
|
|
714
|
+
var typed = txtEl.querySelector('.ab-typed'), caret = txtEl.querySelector('.ab-caret');
|
|
715
|
+
var i = 0;
|
|
716
|
+
var t = setInterval(function () {
|
|
717
|
+
typed.textContent = full.slice(0, ++i);
|
|
718
|
+
if (i >= full.length) { clearInterval(t); setTimeout(function () { caret.remove(); }, 600); }
|
|
719
|
+
}, 16);
|
|
720
|
+
})();
|
|
721
|
+
|
|
722
|
+
/* ---------------- rolling narration — the takeaway in plain English ---------------- */
|
|
723
|
+
(function () {
|
|
724
|
+
var LINES = nLes > 0 ? [
|
|
725
|
+
'Your AI captures everything as it works — ' + fmt(nMem) + ' memories in this project so far.',
|
|
726
|
+
"Most of that stays raw. Only what's proven survives distillation.",
|
|
727
|
+
fmt(nLes) + ' lessons have earned permanence — the task, what failed, what actually works.',
|
|
728
|
+
"These survive compaction, restarts, and reboots. Your AI doesn't start over tomorrow.",
|
|
729
|
+
'Click any number above to see exactly what it knows.'
|
|
730
|
+
] : [
|
|
731
|
+
'Your AI captures everything as it works — ' + fmt(nMem) + ' memories in this project so far.',
|
|
732
|
+
"Most of that stays raw. Only what's proven survives distillation.",
|
|
733
|
+
'0 lessons distilled yet. Distillation is manual for now; there is no auto-distill.',
|
|
734
|
+
'Capture one any time: run record-lesson in this project.',
|
|
735
|
+
'Click any number above to see exactly what it knows.'
|
|
736
|
+
];
|
|
737
|
+
var txt = document.getElementById('ab-fnText');
|
|
738
|
+
var steps = document.getElementById('ab-fnSteps');
|
|
739
|
+
var idx = 0;
|
|
740
|
+
function show() {
|
|
741
|
+
txt.textContent = LINES[idx];
|
|
742
|
+
steps.innerHTML = LINES.map(function (_, i) { return '<span' + (i === idx ? ' class="ab-on"' : '') + '>·</span>'; }).join('');
|
|
743
|
+
if (window.__abFlowCue) window.__abFlowCue(idx + 1);
|
|
744
|
+
}
|
|
745
|
+
show();
|
|
746
|
+
setInterval(function () {
|
|
747
|
+
if (doorsEl.classList.contains('ab-open')) return; // hold the line while a door is open
|
|
748
|
+
txt.style.opacity = '0';
|
|
749
|
+
setTimeout(function () { idx = (idx + 1) % LINES.length; show(); txt.style.opacity = '1'; }, 560);
|
|
750
|
+
}, 6500);
|
|
751
|
+
})();
|
|
752
|
+
}
|
|
753
|
+
})();
|