From 58e01bf18e47d8482057e7414c910244a18cddd4 Mon Sep 17 00:00:00 2001 From: Conner512 <51692123+Conner512@users.noreply.github.com> Date: Tue, 29 Sep 2026 05:57:16 +0000 Subject: [PATCH 01/17] ui(console): neural console redesign with memory constellation and owner-scoped overview charts - New dark-first visual system for all six palettes (contrast >= 4.5:1 kept) - Overview: memory constellation (categories/types), 30-day activity chart, type and lifecycle composition, sparkline, pipeline and timeline - Core overview adds owner-scoped count aggregates (insights), no content - Shell: glass sidebar/topbar, keyboard search (/ and Cmd/Ctrl+K) - Library and detail: translated type/lifecycle chips with colour coding - Sign-in: synapse field story panel and card form Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01WGuzodhrtwbwjzraohufpx --- server/lib/console-read.mjs | 16 +- services/oauth/test/console-layout-a.test.mjs | 13 + web/console/app.mjs | 13 +- web/console/appearance.mjs | 2 +- web/console/catalog.mjs | 16 + web/console/layout-polish.css | 36 +- web/console/render.mjs | 4 +- web/console/styles.css | 475 ++++++++++++------ web/console/visuals.mjs | 90 +++- 9 files changed, 479 insertions(+), 186 deletions(-) diff --git a/server/lib/console-read.mjs b/server/lib/console-read.mjs index 2b4d553..a61f4bb 100644 --- a/server/lib/console-read.mjs +++ b/server/lib/console-read.mjs @@ -7,6 +7,19 @@ function pagination(params,maximum=50){ } const snippet=m=>({memory_id:m.memory_id,content:[...String(m.content??'')].slice(0,160).join(''),memory_type:m.memory_type,status:m.status,created_at:m.created_at,...(m.category?{category:m.category}:{})}); const jobView=job=>({job_id:job.job_id,job_type:job.job_type,state:job.state,total:job.total,processed:job.processed,attempt_count:job.attempt_count,last_error_code:job.last_error_code,created_at:job.created_at,updated_at:job.updated_at,result_ref:job.result_ref}); +// Owner-scoped aggregates for overview charts. Counts only: no content, IDs or other accounts. +function overviewInsights(store,user){ + const db=store.db,since=new Date(Date.now()-29*86400000).toISOString().slice(0,10); + const byDay=new Map(db.prepare("SELECT substr(created_at,1,10) day,COUNT(*) n FROM memories WHERE user_id=? AND created_at>=? GROUP BY day").all(user,since).map(r=>[r.day,r.n])); + const activity=Array.from({length:30},(_,i)=>{const day=new Date(Date.parse(since)+i*86400000).toISOString().slice(0,10);return {day,count:byDay.get(day)||0};}); + const group=column=>db.prepare(`SELECT ${column} value,COUNT(*) count FROM memories WHERE user_id=?${column==='memory_type'?" AND status='active'":''} GROUP BY ${column} ORDER BY count DESC,value LIMIT 12`).all(user); + const categories=db.prepare(`SELECT COALESCE(o.category,a.category,'uncategorized') value,COUNT(*) count FROM memories m + LEFT JOIN memory_category_overrides o ON o.user_id=m.user_id AND o.memory_id=m.memory_id AND o.locked=1 + LEFT JOIN memory_annotations a ON a.user_id=m.user_id AND a.memory_id=m.memory_id AND a.taxonomy_version=? + AND a.revision=(SELECT MAX(revision) FROM memory_revisions WHERE user_id=m.user_id AND memory_id=m.memory_id) + WHERE m.user_id=? AND m.status='active' GROUP BY value ORDER BY count DESC,value LIMIT 12`).all(store.consoleService.taxonomy().version,user); + return {window_days:30,activity,types:group('memory_type'),statuses:group('status'),categories}; +} export async function consoleRead(store,auth,view,params={}) { store.requireScope(auth,'console:read'); if(!isConsoleReader(auth))throw new NotFoundError('Console route not available.'); @@ -24,7 +37,8 @@ export async function consoleRead(store,auth,view,params={}) { case 'projects':return {projects:db.prepare('SELECT project_id,name FROM projects WHERE user_id=? ORDER BY name LIMIT 200').all(user)}; case 'overview':return {read_only:true,production_ready:false,counts:{memories:count('memories'),sources:count('memory_sources'),summaries:count('memory_summaries'),jobs:count('memory_jobs')}, - recent:db.prepare('SELECT memory_id,content,memory_type,status,created_at FROM memories WHERE user_id=? ORDER BY created_at DESC,memory_id LIMIT 5').all(user).map(m=>({...m,content:[...m.content].slice(0,160).join('')}))}; + recent:db.prepare('SELECT memory_id,content,memory_type,status,created_at FROM memories WHERE user_id=? ORDER BY created_at DESC,memory_id LIMIT 5').all(user).map(m=>({...m,content:[...m.content].slice(0,160).join('')})), + insights:overviewInsights(store,user)}; case 'memories': { const {offset,limit}=pagination(params),taxonomy=store.consoleService.taxonomy(); if(params.mode!==undefined&&!['lexical','hybrid','semantic'].includes(params.mode))throw new ValidationError('Invalid retrieval mode.'); diff --git a/services/oauth/test/console-layout-a.test.mjs b/services/oauth/test/console-layout-a.test.mjs index 575f450..b50b653 100644 --- a/services/oauth/test/console-layout-a.test.mjs +++ b/services/oauth/test/console-layout-a.test.mjs @@ -90,3 +90,16 @@ test('Layout A: decorative SVG cannot incorporate caller-supplied markup',()=>{ assert.doesNotMatch(svg,/