v2.20.0
Added
-
ASCII-art favicon — The dashboard browser tab icon is now a stacked
[---] / [M0] / [---]mark in the design system's cream/mono style (SVG, crisp at any size). -
Dashboard redesign — Complete rebuild of the web UI with the OpenCode terminal-native manpage design system (DESIGN.md). New state-driven vanilla-JS SPA with 8 views: Dashboard (hero TUI mockup + stats + recent memories + profile snapshot), Memories, Semantic Search, Timeline (transcript history), Profile (preferences, patterns, workflows, changelog), Maintenance (cleanup, dedup, migration), Conflicts (resolve with keep-newer/keep-older/merge), and Settings. Warm cream canvas, near-black ink, Berkeley/JetBrains Mono, 4px radius on interactive elements, 0px on containers, hairline borders, no shadows or gradients, ASCII bracket markers as bullets, dark theme + system preference detection.
Changed
- i18n.js rewritten — Reduced from ~210 keys (with 80+ orphaned legacy keys and 3 silent duplicate-key overrides per locale) to 107 keys actually used by the new SPA, in both
enandzh. Thet()interpolation still usessplit().join()(no RegExp allocation). - styles.css rewritten — Full DESIGN.md token implementation as CSS custom properties. Net -700 lines. All hex values confined to
:root/[data-theme]blocks; component styles use tokens only. - index.html simplified — From ~400 lines of inline HTML to a ~55-line SPA shell with
#app, modal, and toast containers. CSP meta tag added matching the server's CSP header.
Fixed
- Profile view read wrong API shape —
renderProfileaccessedprofile.preferencesdirectly, but/api/user-profilenests them underprofileData. Now readsresp.profileDataand branches onexists === falseto show the server's "no profile" message. - Duplicate i18n keys silently overridden —
confirm-bulk-delete,confirm-cleanup,confirm-dedupappeared twice per locale; the legacy definitions (last-wins in JS object literals) silently overrode the new wording. Removed all duplicates. - Non-string ID slicing —
memory.id.slice()andconflict.id.slice()assumed string IDs; now guarded withString(...)to avoid throwing on numeric IDs.
Full Changelog: v2.19.4...v2.20.0