Skip to content

v2.20.0

Choose a tag to compare

@github-actions github-actions released this 02 Aug 22:03
· 112 commits to main since this release

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 en and zh. The t() interpolation still uses split().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 shaperenderProfile accessed profile.preferences directly, but /api/user-profile nests them under profileData. Now reads resp.profileData and branches on exists === false to show the server's "no profile" message.
  • Duplicate i18n keys silently overriddenconfirm-bulk-delete, confirm-cleanup, confirm-dedup appeared twice per locale; the legacy definitions (last-wins in JS object literals) silently overrode the new wording. Removed all duplicates.
  • Non-string ID slicingmemory.id.slice() and conflict.id.slice() assumed string IDs; now guarded with String(...) to avoid throwing on numeric IDs.

Full Changelog: v2.19.4...v2.20.0