Skip to content

v0.2.0 — Design system overhaul, universal dock, coding agents

Choose a tag to compare

@hallelx2 hallelx2 released this 07 May 03:33
· 136 commits to main since this release

A near-total UI overhaul. Same product underneath — every screen looks
different, the desktop chrome is reimagined as a side-rail dock, and a
handful of long-standing dev paper-cuts are gone.

Highlights

🎨 Swappable design system — Saigon + Render

Two distinct visual identities you can flip between at runtime via
Settings → Appearance → Design system:

Pack Vibe Defaults
Saigon (default) Atmospheric dark, organic Inter typography, sage-green accent, 75 px pill buttons, spacious 4 px rhythm
Render Crisp light, vibrant Tight Inter, vivid purple → sunset accent gradient, sharp 0 px buttons

Both packs support both light and dark tones. Saigon-dark and
Render-light are the primary designs; the inverse tones are best-effort
tonal mappings.

Mechanism: a single semantic token vocabulary (--ds-bg-canvas,
--ds-radius-button, --ds-text-h1, …) overridden per pack via
[data-ds="saigon"] / [data-ds="render"] selectors. Components
consume only tokens — no hardcoded hex anywhere — so flipping the
active pack restyles the entire app without re-rendering.

🧱 Component primitives

New token-driven primitives in @notebooklm/ui/components/primitives:
Button, Card, Heading, Text, Pill, Input, Textarea,
IconButton, Stack/Inline, Modal, Skeleton, Spinner. Each
consumes only --ds-* tokens via CVA variants — adding a new
primitive doesn't require touching either pack's stylesheet.

🧭 Universal sidebar dock (desktop only)

The desktop app's chrome is now a single floating vertical pill on the
left edge. Icon-only at rest; per-icon labels slide in on hover.
Replaces the per-page top-header pattern across /notebooks,
/notebooks/[id], and /settings/*.

The dock is context-aware — it hides the Library entry when you're
already on /notebooks, and the Settings entry on /settings/*
where the sidebar already shows the current section.

The web app keeps top headers for now.

⚙️ Settings — full redesign

  • Left-rail sidebar (lg+) with grouped sections: You / AI
    / Capabilities / Workspace. Mobile gets a horizontal
    scrollable strip.
  • Status dots per section: green when configured, amber if
    partially set up, accent when a runtime is actively routing chat,
    muted otherwise. Driven by a single shared
    useSettingsStatus() hook so both layouts share one set of tRPC
    observers.
  • New: Coding Agents pane at /settings/coding-agents. Detects
    Claude Code, OpenAI Codex, and gh copilot via
    runtimes.detectAvailable, lets you route chat through whichever
    is installed (writes preferences.chat = [<agentId>, "ai-sdk"]
    via aiConfig.update). Studio + deep-research keep falling back
    to your saved AI provider.

🚀 Onboarding — expanded

  • Chat provider step: 5 → 11 curated providers (Free: Google
    Gemini, Groq · Paid: OpenAI, Anthropic, Mistral, Cohere, xAI ·
    Aggregator: Together AI, OpenRouter · Local: Ollama · Custom:
    OpenAI-compatible for LM Studio / vLLM / gateways). 3-column grid
    on lg+, tonal badges per category.
  • Web search step: now exposes all 3 providers
    (Tavily, Exa, SearxNG) instead of just Tavily + Exa,
    with per-provider input kind (api key vs base URL) and inline test
    button.
  • Test status persists: onboarding now upserts the credential and
    immediately re-tests it by credentialId, so the saved row's
    validationStatus is "ok" straight away. No more "Saved ·
    untested" in Settings until you manually re-test.
  • Models updated to the live catalogue: retired
    gemini-2.0-flash-exp swapped to gemini-2.5-flash; Anthropic
    default moved to claude-haiku-4-5-20251001.
  • New api_key_and_base_url auth flow for OpenAI-compatible
    providers — renders API key + base URL + freeform model id
    together.

🪲 Notable fixes

  • React-19 / TanStack-Router page duplication. Switching settings
    tabs (or navigating between Notebooks / Notebook / Settings) used
    to leave the previous route's tree mounted underneath the new one
    — visible as the same page rendered twice top-to-bottom. Cause:
    inline arrow component: callbacks on every route changed
    identity each render, triggering React 19's stricter unmount
    path, which silently bailed when our DOM-mutating ThemeProvider
    had touched the document. Fixed by extracting every route's
    component into a stable named function (RootShell,
    OnboardingShell, NotebooksShell, NotebookShell,
    SettingsLayoutShell).
  • ELECTRON_RUN_AS_NODE trap. Some agent runtimes / CI configs
    export this var, which makes electron . run as plain Node and
    silently crashes on app.isPackaged. New
    apps/desktop/scripts/dev-electron.mjs wrapper scrubs the var
    before spawning Electron — bun run dev now works regardless of
    the parent shell.
  • electron-updater dev-mode crash.
    apps/desktop/electron/updater.cjs used to destructure
    autoUpdater at module top, triggering a getter that crashed on
    Electron 33 + bun. Moved the require inside setupAutoUpdater
    (only called in production) so the import no longer fires during
    dev.
  • Provider logos disappearing on dark surfaces. Many provider
    SVGs (OpenAI, Ollama, Groq) use fill="currentColor" and vanish
    on dark backgrounds. Fixed by pinning the logo wrapper to a
    forced-white tile regardless of pack/tone.
  • Pill buttons touching. The -ml-[1px] "shared border" trick
    (a leftover from sharp-cornered Render styling) made the pill
    buttons in NotebooksHeader and SettingsNav overlap. Replaced with
    gap-2.

🎯 Brand mark + app icon

New app icon — saigon-accent green plate carrying a stack-of-books
glyph in black (the spirit of Material Symbols book_2, which is
what the in-app sidebar / dock / auth chrome / onboarding header all
use). Same composition now reads consistently across:

  • Desktop installer + taskbar (apps/desktop/build/icon.{png,ico,icns})
  • Electron BrowserWindow({ icon }) at runtime
  • Web favicon (apps/web/src/app/icon.tsx)
  • Apple Touch Icon (apps/web/src/app/apple-icon.tsx)

Migration notes

  • Two new localStorage keys are written: notebooklm-theme (existing,
    unchanged) and notebooklm-ds (new, "saigon" | "render",
    default "saigon"). Existing users keep their tone preference and
    start on Saigon.
  • Inter + JetBrains Mono replace Google Sans as the default fonts.
    Loaded via next/font on web, Google Fonts CDN on desktop.
  • The token vocabulary is documented inline in
    packages/ui/src/styles/tokens.css. New screens should import
    from @notebooklm/ui/components/primitives rather than rolling
    their own <button> / <div> chrome.
  • The legacy Tailwind colour aliases (bg-background-light,
    bg-element-dark, etc.) have been removed from both apps'
    @theme blocks. All call sites were migrated as part of the
    same release — if you have an out-of-tree fork that still uses
    them, swap in the new utilities (bg-canvas, bg-elevated,
    border-border-subtle, …).

Commits

7b70ebd  feat(brand): redesign app icon — bump 0.2.0
9c66d51  fix(desktop): ELECTRON_RUN_AS_NODE + electron-updater defer
c765aa6  feat(desktop): universal AppDock + named route components
b7250f2  feat(onboarding): 11 chat providers, 3 web-search providers
9b28f72  feat(settings): coding agents pane
57a71aa  feat(settings): sidebar nav with grouped sections + status dots
5c922c7  refactor(ui): consume design tokens across every screen
053146d  feat(ui): swappable design system foundation

Full diff: v0.1.24...v0.2.0