mirror of
https://github.com/samvallad33/vestige.git
synced 2026-05-11 00:32:36 +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.
3 lines
9.4 KiB
JavaScript
3 lines
9.4 KiB
JavaScript
import"../chunks/Bzak7iHL.js";import{o as Ne}from"../chunks/DWVWfZUn.js";import{f as be,d as n,e as s,r as a,t as F,p as je,a as De,h as c,g as r,s as X,u as Y,O as ze}from"../chunks/VE8Jor13.js";import{s as v,d as Ge,a as k}from"../chunks/DHnEMX8z.js";import{i as M}from"../chunks/JkhlGLjU.js";import{e as Z,i as ee}from"../chunks/ByItJEsC.js";import{c as Re,a as m,f as b}from"../chunks/7UNxJI5L.js";import{s as He}from"../chunks/BZYVQ1d5.js";import{s as te,r as Oe}from"../chunks/Cu3VmnGp.js";import{s as ae}from"../chunks/BR2EHpd7.js";import{b as Ve}from"../chunks/BRHZEveZ.js";import{b as We}from"../chunks/DHakDdar.js";import{a as y,s as ge}from"../chunks/AcZBvMXu.js";import{s as Qe,g as fe}from"../chunks/00s_zK56.js";import{b as _}from"../chunks/FMdNDkar.js";import{s as he,w as xe,u as Ue,a as Be,i as Je,m as Pe,f as Xe}from"../chunks/XIUN5r_Y.js";import"../chunks/CrlWs-6R.js";const Ye=()=>{const f=Qe;return{page:{subscribe:f.page.subscribe},navigating:{subscribe:f.navigating.subscribe},updated:f.updated}},Ze={subscribe(f){return Ye().page.subscribe(f)}};var et=b('<div class="flex items-center gap-2 rounded-full border border-[#A33FFF]/40 bg-[#A33FFF]/10 px-3 py-1.5 text-xs shadow-[0_0_12px_rgba(163,63,255,0.15)]" title="Memories currently under top-down suppression (Anderson 2025 SIF)"><div class="relative flex h-2 w-2 items-center justify-center"><span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-[#A33FFF] opacity-75"></span> <span class="relative inline-flex h-2 w-2 rounded-full bg-[#A33FFF]"></span></div> <span class="font-medium text-[#E4C8FF]"> </span></div>');function tt(f){const h=()=>y(he,"$suppressedCount",S),[S,K]=ge();var L=Re(),z=be(L);{var T=C=>{var u=et(),q=n(s(u),2),p=s(q);a(q),a(u),F(()=>v(p,`Actively forgetting ${h()??""} ${h()===1?"memory":"memories"}`)),m(C,u)};M(z,C=>{h()>0&&C(T)})}m(f,L),K()}var at=b('<a><span class="text-base w-5 text-center"> </span> <span class="hidden lg:block"> </span> <span class="hidden lg:block ml-auto text-[10px] text-muted/50 font-mono"> </span></a>'),st=b('<div title="MCP server uptime"> </div>'),rt=b('<div class="hidden lg:block pt-1"><!></div>'),nt=b('<a><span class="text-lg"> </span> <span class="text-[9px]"> </span></a>'),ot=b('<button class="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-dim hover:text-text hover:bg-white/[0.04] transition"><span class="text-base w-5 text-center"> </span> <span> </span> <span class="ml-auto text-[10px] text-muted/50 font-mono hidden md:block"> </span></button>'),it=b('<div class="px-4 py-6 text-center text-sm text-muted">No matches</div>'),lt=b('<div class="fixed inset-0 z-50 flex items-start justify-center pt-[10vh] md:pt-[15vh] px-4 bg-void/60 backdrop-blur-sm"><div class="w-full max-w-lg glass-panel rounded-xl shadow-2xl shadow-synapse/10 overflow-hidden"><div class="flex items-center gap-3 px-4 py-3 border-b border-synapse/10"><span class="text-synapse text-sm">◎</span> <input type="text" placeholder="Navigate to..." class="flex-1 bg-transparent text-text text-sm placeholder:text-muted focus:outline-none"/> <span class="text-[10px] text-muted font-mono bg-white/[0.04] px-1.5 py-0.5 rounded">esc</span></div> <div class="max-h-72 overflow-y-auto py-1"><!> <!></div></div></div>'),dt=b('<div class="ambient-orb ambient-orb-1" aria-hidden="true"></div> <div class="ambient-orb ambient-orb-2" aria-hidden="true"></div> <div class="ambient-orb ambient-orb-3" aria-hidden="true"></div> <div class="flex flex-col md:flex-row h-screen overflow-hidden bg-void relative z-[1]"><nav class="hidden md:flex w-16 lg:w-56 flex-shrink-0 glass-sidebar flex-col"><a class="flex items-center gap-3 px-4 py-5 border-b border-synapse/10"><div class="w-8 h-8 rounded-lg bg-gradient-to-br from-dream to-synapse flex items-center justify-center text-bright text-sm font-bold shadow-lg shadow-synapse/20">V</div> <span class="hidden lg:block text-sm font-semibold text-bright tracking-wide">VESTIGE</span></a> <div class="flex-1 py-3 flex flex-col gap-1 px-2"></div> <div class="px-2 pb-2"><button class="w-full flex items-center gap-2 px-3 py-2 rounded-lg text-xs text-muted hover:text-dim hover:bg-white/[0.03] transition border border-subtle/15"><span class="text-[10px] font-mono bg-white/[0.04] px-1.5 py-0.5 rounded">⌘K</span> <span class="hidden lg:block">Command</span></button></div> <div class="px-3 py-4 border-t border-synapse/10 space-y-2"><div class="flex items-center gap-2 text-xs"><div></div> <span class="hidden lg:block text-dim"> </span></div> <div class="hidden lg:block text-xs text-muted space-y-0.5"><div> </div> <div> </div> <!></div> <!></div></nav> <main class="flex-1 flex flex-col min-h-0 pb-16 md:pb-0"><div class="animate-page-in flex-1 min-h-0 overflow-y-auto svelte-12qhfyh"><!></div></main> <nav class="md:hidden fixed bottom-0 inset-x-0 glass border-t border-synapse/10 z-40 safe-bottom svelte-12qhfyh"><div class="flex items-center justify-around px-2 py-1"><!> <button class="flex flex-col items-center gap-0.5 px-3 py-2 rounded-lg text-muted min-w-[3.5rem]"><span class="text-lg">⋯</span> <span class="text-[9px]">More</span></button></div></nav></div> <!>',1);function Et(f,h){je(h,!0);const S=()=>y(Ze,"$page",u),K=()=>y(Je,"$isConnected",u),L=()=>y(Pe,"$memoryCount",u),z=()=>y(Be,"$avgRetention",u),T=()=>y(Ue,"$uptimeSeconds",u),C=()=>y(he,"$suppressedCount",u),[u,q]=ge();let p=X(!1),g=X(""),E=X(void 0);Ne(()=>{xe.connect();function t(e){if((e.metaKey||e.ctrlKey)&&e.key==="k"){e.preventDefault(),c(p,!r(p)),c(g,""),r(p)&&requestAnimationFrame(()=>{var i;return(i=r(E))==null?void 0:i.focus()});return}if(e.key==="Escape"&&r(p)){c(p,!1);return}if(e.target instanceof HTMLInputElement||e.target instanceof HTMLTextAreaElement)return;if(e.key==="/"){e.preventDefault();const i=document.querySelector('input[type="text"]');i==null||i.focus();return}const o={g:"/graph",m:"/memories",t:"/timeline",f:"/feed",e:"/explore",i:"/intentions",s:"/stats"}[e.key.toLowerCase()];o&&!e.metaKey&&!e.ctrlKey&&!e.altKey&&(e.preventDefault(),fe(`${_}${o}`))}return window.addEventListener("keydown",t),()=>{xe.disconnect(),window.removeEventListener("keydown",t)}});const I=[{href:"/graph",label:"Graph",icon:"◎",shortcut:"G"},{href:"/memories",label:"Memories",icon:"◈",shortcut:"M"},{href:"/timeline",label:"Timeline",icon:"◷",shortcut:"T"},{href:"/feed",label:"Feed",icon:"◉",shortcut:"F"},{href:"/explore",label:"Explore",icon:"◬",shortcut:"E"},{href:"/intentions",label:"Intentions",icon:"◇",shortcut:"I"},{href:"/stats",label:"Stats",icon:"◫",shortcut:"S"},{href:"/settings",label:"Settings",icon:"⚙",shortcut:","}],_e=I.slice(0,5);function se(t,e){const d=e.startsWith(_)?e.slice(_.length)||"/":e;return t==="/graph"?d==="/"||d==="/graph":d.startsWith(t)}let N=Y(()=>r(g)?I.filter(t=>t.label.toLowerCase().includes(r(g).toLowerCase())):I);function re(t){c(p,!1),c(g,""),fe(`${_}${t}`)}var ne=dt(),G=n(be(ne),6),R=s(G),oe=s(R),H=n(oe,2);Z(H,21,()=>I,ee,(t,e)=>{const d=Y(()=>se(r(e).href,S().url.pathname));var o=at(),i=s(o),w=s(i,!0);a(i);var x=n(i,2),A=s(x,!0);a(x);var j=n(x,2),l=s(j,!0);a(j),a(o),F(()=>{te(o,"href",`${_??""}${r(e).href??""}`),ae(o,1,`flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all duration-200 text-sm
|
|
${r(d)?"bg-synapse/15 text-synapse-glow border border-synapse/30 shadow-[0_0_12px_rgba(99,102,241,0.15)] nav-active-border":"text-dim hover:text-text hover:bg-white/[0.03] border border-transparent"}`),v(w,r(e).icon),v(A,r(e).label),v(l,r(e).shortcut)}),m(t,o)}),a(H);var O=n(H,2),ye=s(O);a(O);var ie=n(O,2),V=s(ie),le=s(V),de=n(le,2),we=s(de,!0);a(de),a(V);var W=n(V,2),Q=s(W),$e=s(Q);a(Q);var U=n(Q,2),ke=s(U);a(U);var Fe=n(U,2);{var Ce=t=>{var e=st(),d=s(e);a(e),F(o=>v(d,`up ${o??""}`),[()=>Xe(T())]),m(t,e)};M(Fe,t=>{T()>0&&t(Ce)})}a(W);var Ee=n(W,2);{var Ae=t=>{var e=rt(),d=s(e);tt(d),a(e),m(t,e)};M(Ee,t=>{C()>0&&t(Ae)})}a(ie),a(R);var B=n(R,2),pe=s(B),Me=s(pe);He(Me,()=>h.children),a(pe),a(B);var ce=n(B,2),ve=s(ce),ue=s(ve);Z(ue,17,()=>_e,ee,(t,e)=>{const d=Y(()=>se(r(e).href,S().url.pathname));var o=nt(),i=s(o),w=s(i,!0);a(i);var x=n(i,2),A=s(x,!0);a(x),a(o),F(()=>{te(o,"href",`${_??""}${r(e).href??""}`),ae(o,1,`flex flex-col items-center gap-0.5 px-3 py-2 rounded-lg transition-all min-w-[3.5rem]
|
|
${r(d)?"text-synapse-glow":"text-muted"}`),v(w,r(e).icon),v(A,r(e).label)}),m(t,o)});var Se=n(ue,2);a(ve),a(ce),a(G);var Ke=n(G,2);{var Le=t=>{var e=lt(),d=s(e),o=s(d),i=n(s(o),2);Oe(i),We(i,l=>c(E,l),()=>r(E)),ze(2),a(o);var w=n(o,2),x=s(w);Z(x,17,()=>r(N),ee,(l,$)=>{var D=ot(),J=s(D),Te=s(J,!0);a(J);var P=n(J,2),qe=s(P,!0);a(P);var me=n(P,2),Ie=s(me,!0);a(me),a(D),F(()=>{v(Te,r($).icon),v(qe,r($).label),v(Ie,r($).shortcut)}),k("click",D,()=>re(r($).href)),m(l,D)});var A=n(x,2);{var j=l=>{var $=it();m(l,$)};M(A,l=>{r(N).length===0&&l(j)})}a(w),a(d),a(e),k("keydown",e,l=>{l.key==="Escape"&&c(p,!1)}),k("click",e,l=>{l.target===l.currentTarget&&c(p,!1)}),k("keydown",i,l=>{l.key==="Enter"&&r(N).length>0&&re(r(N)[0].href)}),Ve(i,()=>r(g),l=>c(g,l)),m(t,e)};M(Ke,t=>{r(p)&&t(Le)})}F(t=>{te(oe,"href",`${_??""}/graph`),ae(le,1,`w-2 h-2 rounded-full ${K()?"bg-recall animate-pulse-glow":"bg-decay"}`),v(we,K()?"Connected":"Offline"),v($e,`${L()??""} memories`),v(ke,`${t??""}% retention`)},[()=>(z()*100).toFixed(0)]),k("click",ye,()=>{c(p,!0),c(g,""),requestAnimationFrame(()=>{var t;return(t=r(E))==null?void 0:t.focus()})}),k("click",Se,()=>{c(p,!0),c(g,""),requestAnimationFrame(()=>{var t;return(t=r(E))==null?void 0:t.focus()})}),m(f,ne),De(),q()}Ge(["click","keydown"]);export{Et as component};
|