mirror of
https://github.com/samvallad33/vestige.git
synced 2026-05-02 12:22:37 +02:00
Closes Agent 1's audit gap #4: FSRS memory state (Active / Dormant / Silent / Unavailable) was computed server-side per query but never rendered in the 3D graph. Spheres always tinted by node type. The new colour mode adds a second channel that users can toggle between at runtime — Type (default, existing behaviour) and State (new). The toggle is a radio-pair pill in the graph page's top-right control bar next to the node-count selector + Dream button. Buckets + palette: - Active ≥ 70% emerald #10b981 easily retrievable - Dormant 40-70% amber #f59e0b retrievable with effort - Silent 10-40% violet #8b5cf6 difficult, needs cues - Unavail. < 10% slate #6b7280 needs reinforcement Thresholds match `execute_system_status` at the backend so the graph colour bands line up exactly with what the Stats page reports in its stateDistribution block. Using retention as the proxy for the full accessibility formula (retention × 0.5 + retrieval × 0.3 + storage × 0.2) is an approximation — retention is the dominant 0.5 weight and it is the only FSRS channel the current GraphNode DTO carries. Swap to the full formula in a future release if the DTO grows. Implementation: - `apps/dashboard/src/lib/graph/nodes.ts` — new `MemoryState` type, `getMemoryState(retention)`, `MEMORY_STATE_COLORS`, `MEMORY_STATE_DESCRIPTIONS`, `ColorMode`, `getNodeColor(node, mode)`. - `NodeManager.colorMode` field (default `'type'`). `createNodeMeshes` now calls `getNodeColor(node, this.colorMode)` so newly-added nodes during the session follow the toggled mode. - New `NodeManager.setColorMode(mode)` mutates every live mesh's material + glow sprite colour in place. Idempotent; cheap. Does NOT touch opacity/emissive-intensity so the v2.0.5 suppression dimming layer keeps working unchanged. - New `MemoryStateLegend.svelte` floating overlay in the bottom-right when state mode is active (hidden in type mode so the legend doesn't compete with the node-type palette). - `Graph3D.svelte` accepts a new `colorMode` prop (default `'type'`) and runs a `$effect` that calls `setColorMode` on every toggle. - Dashboard rebuild picks up the new component + wiring. Tests: 171 vitest, svelte-check 581 files / 0 errors. No backend changes; this is pure dashboard code.
1 line
3.8 KiB
JavaScript
1 line
3.8 KiB
JavaScript
import"../chunks/Bzak7iHL.js";import{o as pe}from"../chunks/DWVWfZUn.js";import{p as ce,s as b,c as me,g as e,a as _e,d as i,e as a,h as c,r as t,t as g}from"../chunks/VE8Jor13.js";import{d as ue,a as K,s as m}from"../chunks/DHnEMX8z.js";import{i as M}from"../chunks/JkhlGLjU.js";import{e as h,i as P}from"../chunks/ByItJEsC.js";import{a as l,f as v}from"../chunks/7UNxJI5L.js";import{s as Q}from"../chunks/ussr1V5_.js";import{b as xe}from"../chunks/B5Pq2mnD.js";import{a as fe}from"../chunks/DcQGRi49.js";import{N as U}from"../chunks/BNytumrp.js";var be=v('<div class="h-16 glass-subtle rounded-xl animate-pulse"></div>'),ge=v('<div class="space-y-4"></div>'),he=v('<div class="text-center py-20 text-dim"><p>No memories in the selected time range.</p></div>'),ye=v('<div class="w-2 h-2 rounded-full"></div>'),we=v('<span class="text-xs text-muted"> </span>'),ke=v('<div class="flex items-start gap-2 text-sm"><div class="w-2 h-2 mt-1.5 rounded-full flex-shrink-0"></div> <div class="flex-1 min-w-0"><span class="text-dim line-clamp-1"> </span></div> <span class="text-xs text-muted flex-shrink-0"> </span></div>'),Te=v('<div class="mt-3 pt-3 border-t border-synapse/10 space-y-2"></div>'),je=v('<div class="relative pl-14"><div class="absolute left-4 top-3 w-5 h-5 rounded-full border-2 border-synapse bg-void flex items-center justify-center"><div class="w-2 h-2 rounded-full bg-synapse"></div></div> <button class="w-full text-left p-4 glass-subtle rounded-xl hover:bg-white/[0.03] transition-all"><div class="flex items-center justify-between"><div><span class="text-sm text-bright font-medium"> </span> <span class="text-xs text-dim ml-2"> </span></div> <div class="flex gap-1"><!> <!></div></div> <!></button></div>'),Ae=v('<div class="relative"><div class="absolute left-6 top-0 bottom-0 w-px bg-synapse/15"></div> <div class="space-y-4"></div></div>'),Ne=v('<div class="p-6 max-w-4xl mx-auto space-y-6"><div class="flex items-center justify-between"><h1 class="text-xl text-bright font-semibold">Timeline</h1> <select class="px-3 py-2 bg-white/[0.03] border border-synapse/10 rounded-xl text-dim text-sm focus:outline-none backdrop-blur-sm"><option>7 days</option><option>14 days</option><option>30 days</option><option>90 days</option></select></div> <!></div>');function Re(V,W){ce(W,!0);let _=b(me([])),y=b(!0),w=b(14),k=b(null);pe(()=>R());async function R(){c(y,!0);try{const s=await fe.timeline(e(w),500);c(_,s.timeline,!0)}catch{c(_,[],!0)}finally{c(y,!1)}}var T=Ne(),j=a(T),u=i(a(j),2),A=a(u);A.value=A.__value=7;var N=i(A);N.value=N.__value=14;var O=i(N);O.value=O.__value=30;var Y=i(O);Y.value=Y.__value=90,t(u),t(j);var X=i(j,2);{var Z=s=>{var d=ge();h(d,20,()=>Array(7),P,(x,f)=>{var r=be();l(x,r)}),t(d),l(s,d)},ee=s=>{var d=he();l(s,d)},te=s=>{var d=Ae(),x=i(a(d),2);h(x,21,()=>e(_),f=>f.date,(f,r)=>{var S=je(),B=i(a(S),2),D=a(B),E=a(D),$=a(E),ae=a($,!0);t($);var q=i($,2),se=a(q);t(q),t(E);var z=i(E,2),G=a(z);h(G,17,()=>e(r).memories.slice(0,10),P,(n,o)=>{var p=ye();g(()=>Q(p,`background: ${(U[e(o).nodeType]||"#8B95A5")??""}; opacity: ${.3+e(o).retentionStrength*.7}`)),l(n,p)});var ie=i(G,2);{var re=n=>{var o=we(),p=a(o);t(o),g(()=>m(p,`+${e(r).memories.length-10}`)),l(n,o)};M(ie,n=>{e(r).memories.length>10&&n(re)})}t(z),t(D);var oe=i(D,2);{var le=n=>{var o=Te();h(o,21,()=>e(r).memories,P,(p,C)=>{var F=ke(),H=a(F),L=i(H,2),I=a(L),ve=a(I,!0);t(I),t(L);var J=i(L,2),de=a(J);t(J),t(F),g(ne=>{Q(H,`background: ${(U[e(C).nodeType]||"#8B95A5")??""}`),m(ve,e(C).content),m(de,`${ne??""}%`)},[()=>(e(C).retentionStrength*100).toFixed(0)]),l(p,F)}),t(o),l(n,o)};M(oe,n=>{e(k)===e(r).date&&n(le)})}t(B),t(S),g(()=>{m(ae,e(r).date),m(se,`${e(r).count??""} memories`)}),K("click",B,()=>c(k,e(k)===e(r).date?null:e(r).date,!0)),l(f,S)}),t(x),t(d),l(s,d)};M(X,s=>{e(y)?s(Z):e(_).length===0?s(ee,1):s(te,!1)})}t(T),K("change",u,R),xe(u,()=>e(w),s=>c(w,s)),l(V,T),_e()}ue(["change","click"]);export{Re as component};
|