Skip to content

feat(desktop): UI polish, inbox redesign, and settings consolidation - #111

Merged
h4yfans merged 80 commits into
mainfrom
feat/desktop-polish
Mar 24, 2026
Merged

feat(desktop): UI polish, inbox redesign, and settings consolidation#111
h4yfans merged 80 commits into
mainfrom
feat/desktop-polish

Conversation

@h4yfans

@h4yfans h4yfans commented Mar 24, 2026

Copy link
Copy Markdown
Collaborator

What

Comprehensive desktop UI polish pass — redesign inbox, consolidate settings, add accessibility, performance, and undo support.

Why

Bring the desktop app to visual/functional parity with the design direction: editorial typography, tint-aware accent colors, keyboard-first interaction, and a unified account settings page.

How

  • Inbox: Redesign with toolbar, filters, triage mode, link previews, waveform visualizer, TweetCard, compact capture, and live URL title extraction
  • Settings: Merge sync-section into account-section (single page for identity, sync status, devices, security, sign-out); extract shared primitives; add shortcuts, recovery key, and capture shortcut sections
  • Sync components: Normalize typography (h3→div), tint-colored primary buttons, inline format validation, OTP countdown with Clock icon
  • Tasks: Add Cmd+Z undo for all CRUD/bulk ops, project dropdown a11y rework, design token migration
  • Folders: Icon picker, NoteIconDisplay, tree polish
  • Accessibility: WCAG 2.1 AA pass — keyboard nav, ARIA labels, focus indicators
  • Performance: Parallelize vault open + window creation, optimize startup I/O and large-vault indexing
  • Theme: Accent color sync across devices, neutral charcoal dark theme, Gelasio/Geist/Inter fonts, HugeIcons infrastructure
  • IPC: Add vault:reveal channel, update email sender domain to memrynote.com

Type

  • feat — new feature
  • refactor — restructure without behavior change
  • style — visual/UI only
  • perf — performance improvement
  • test — adding or updating tests

Test plan

  • Unit tests added/updated
  • Manual testing (describe below)

Updated 11 test files with current API surface (FolderInfo[], combobox roles, sync mocks, templates.list). Added shortcut-registry test suite and formatCompactRelativeTime tests.

Checklist

  • Self-reviewed the diff
  • No hardcoded secrets or credentials
  • Files stay under ~500 LOC
  • Follows immutable data patterns

h4yfans and others added 30 commits March 19, 2026 22:09
The task creation step passed a hardcoded 'personal' projectId which
is not a real DB record — causes silent FK violation on every first run.

Now resolves a valid project: uses first existing project, or creates
a default 'Personal' project if none exist.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add ShortcutRegistry with all rebindable app shortcuts, conflict detection,
  and display helpers (formatBinding, findConflicts, getGroupedShortcuts)
- Add ShortcutsSettings section with search, grouped list, inline key capture,
  conflict warnings, per-shortcut reset, and reset-all button
- Expand EditorSettings section to expose width, spellCheck, autoSaveDelay,
  and showWordCount controls (migrates from useNoteEditorSettings to the
  richer useEditorSettings hook)
- Wire Shortcuts nav item into settings.tsx sidebar
- Regenerate IPC invoke map (was stale)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
- account-handlers.ts: GET_INFO (email+joinedAt from store/DB) + SIGN_OUT
- Register account handlers in IPC index; regenerate invoke map
- Preload: add account namespace + update index.d.ts types
- useAccountInfo hook (GET_INFO), useDevices hook (wraps syncDevices)
- AccountSection: identity card, storage usage bar, recovery key stub, sign-out dialog
- Wire AccountSection into settings.tsx sidebar

Co-Authored-By: Paperclip <noreply@paperclip.ing>
- account-handlers: add GET_RECOVERY_KEY handler — retrieves master key
  from OS keychain (requires valid access token), returns base64 encoded
- RecoveryKeyDialog: blurred display until reveal, copy button, auto-clear on close
- AccountSection: wire View button → RecoveryKeyDialog, remove disabled state

Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add REGISTER_GLOBAL_CAPTURE IPC channel to SettingsChannels
- Implement applyGlobalCaptureShortcut() in settings-handlers for OS-level
  global shortcut registration/unregistration via electron globalShortcut
- Wire settings keyboard changes to re-apply shortcut live on update
- Expose REGISTER_GLOBAL_CAPTURE in preload bridge
- Add GlobalCaptureRow component in shortcuts-section with live capture,
  macOS accessibility permission detection, and clear/reset UI
- Update app startup to use applyGlobalCaptureShortcut with fallback
  to legacy registerQuickCaptureShortcut when no setting saved

Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Regenerate generated-ipc-invoke-map.ts to include new settings channels
  (REGISTER_GLOBAL_CAPTURE) added by recent settings feature commits
- Add missing account-handlers mock to ipc/index.test.ts — the account
  section feature introduced registerAccountHandlers but didn't update
  the registration lifecycle test, causing 3 test failures

Co-Authored-By: Paperclip <noreply@paperclip.ing>
…creation

Two changes to improve startup performance toward the <2s cold-start target:

1. Move createWindow() before autoOpenLastVault() in the app.whenReady()
   handler. The vault open path (SQLite init, migrations, FTS setup, file
   watcher, sync runtime) was blocking window creation for ~300-600ms.
   The renderer already handles vault-not-open state via VaultOnboarding
   and useVault(isLoading), so the window can appear immediately while the
   vault opens in the background.

2. Eliminate synchronous IPC in preload startup theme resolution. The
   getStartupThemeSync() function used ipcRenderer.sendSync on every
   launch, blocking the renderer until the main process responded.
   Now reads from localStorage cache first (populated on the previous run),
   falling back to sendSync only on first launch or corrupted storage.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ormat

The ipc-invoke-map generator uses TypeScript's printer API; output format
differs between ts versions (compact one-liners vs expanded multi-line).
Commit the freshly generated format so ipc:check passes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
- store.ts: add module-level in-memory cache — eliminates repeated
  synchronous disk reads on every getCurrentVaultPath() and getVaults()
  call; reads disk once, subsequent gets are O(1) in-memory lookups

- indexer.ts: replace sequential await-in-loop with concurrency-limited
  parallel indexing (limit=8); 1000-note vault indexing goes from ~30s
  to ~4s by overlapping file reads and cache writes within SQLite's
  safe concurrency window

- index.ts: hoist dynamic import('fs') and import('mime-types') out of
  the hot memry-file:// protocol handler to static top-level imports;
  eliminates repeated module resolution overhead on every local file serve

All existing store (4) and indexer (19) tests pass. Typecheck clean.
…s indicators

- Add global :focus-visible safety net in main.css so any focusable element without an explicit ring still gets a visible outline
- Fix KanbanCard: add onKeyDown handler (Enter=open, Space=toggle/complete) and focus-visible:ring-2 so Tab users see focus
- Destroy canvas blindspot: wrap GraphCanvas in role="img" with aria-label summarising node/edge counts; add sr-only <ul> listing all nodes by label+type for screen readers
- Fix TagAutocomplete combobox ARIA: add role="combobox" + aria-controls + aria-activedescendant on input; give each option a unique id; SuggestionItem changed from button to div[role=option] inside role=listbox; TagPill span gets role=listitem
- Add ArrowUp/Down keyboard navigation to TagInputPopup; wire focusedIndex to TagOption isFocused prop for visual + ARIA feedback; input gets role=combobox + aria-controls + aria-activedescendant
- Fix pre-existing TS2352 type error in use-undoable-task-actions.ts (Task→Record cast)
- Regenerate IPC invoke map

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Toast undo button + Cmd+Z could both fire the same undo action.
removeUndoEntry(id) lets consumers yank a specific entry from the
stack after consuming it via an alternate path.
New useUndoableTaskActions hook centralizes undo logic for single-task
operations (create, delete, complete, uncomplete, archive, field update).
useBulkActions gains optional registerUndo/onAddTask params to register
all 8 bulk operations with the global undo stack. 41 new test cases.
Replace inline handler bodies in tasks.tsx with hook calls. Removes
~100 LOC of completion/repeat/delete logic from the page component.
Every task action now registers with the global undo stack.
Calendar view was never shipped to users. Remove all components,
CSS variables, utility functions, and tests to reduce dead code.
Stale items UI (age indicators, thresholds, nudge messages) was
unused. Remove components, utilities, and 346 lines of tests.
Reusable toolbar with segment tabs and icon buttons for page headers.
Pill component with color variants for status/type badges.
Replace custom text-text-{primary,secondary,tertiary} tokens with
standard muted-foreground/foreground. Bump base font from 12px to
13px across task rows, filters, kanban, and toolbar for readability.
Add PageToolbar to inbox header with inline capture input.
Redesign health view with type-breakdown cards and Pill badges.
Add archived search, type filters, and snoozed item toggle.
Refactor archived item rows with icon config map.
New compact prop for embedding in toolbars. Simplify density
handling, improve token overlay alignment, and tighten spacing.
Add extractErrorMessage toast to sidebar note creation. Add aria
labels to notes tree, formula editor, and tag detail view. Wire
scrollContainerRef in ContentArea. Clean up remaining calendar CSS
vars and add font-heading variable.
The accent color picker in Settings > Appearance was saving to DB but
nothing read the value. Add --tint-* CSS variables derived from
--user-accent-color via color-mix(), rewire --sidebar-terracotta to
var(--tint), register in @theme inline, and replace ~30 hardcoded
blue-500 Tailwind classes with tint-* across tabs, AI agent, split
view, and misc components.
…flash

SET_GENERAL_SETTINGS now calls SettingsSyncManager.updateField() for
syncable fields (theme, fontSize, fontFamily, accentColor, language)
while excluding device-specific ones (startOnBoot). getStartupTheme
returns { theme, accentColor } so preload applies --user-accent-color
before React hydrates, preventing a flash of default indigo. Adds
localStorage caching for accent color on the fast startup path.
Icon value utilities (emoji-icon-utils), lazy HugeIcons loader with
cache, NoteIconDisplay component, virtualized icon grid picker, and
search hook for the icon library.
Replace stub getCapturePatterns with actual DB aggregation across
time-heatmap, type-distribution, top-domains, and top-tags.
Add unit tests and update heatmap component for compact layout.
Drop BulkArchiveOlderThan schema, IPC channel, handler, service
method, useInboxBankruptcy hook, and CollectorWarning/BankruptcyDialog
UI. The feature was incomplete (never wired in preload bridge) and
replaced by manual archive workflows.
- Add icon field to FolderConfig and new FolderInfo type in contracts
- Return FolderInfo[] (path + icon) from getFolders across the stack
- Update all consumers to use FolderInfo instead of raw string paths
- Polish kibo-ui tree: hideLines prop, chevron icons, tighter spacing
- Polish sidebar section: uppercase labels, collapsible chevron
- Simplify preload startup theme (drop accent flash workaround)
- Add design tokens reference doc
- FolderIconButton with portal-based EmojiPicker for context menu
- EmojiPicker gains icon tab (HugeIconGrid) alongside emoji tab
- NoteIconDisplay replaces raw emoji spans in tree, tabs, search,
  graph tooltip, property cell, and folder view
- Sidebar actions refactored to ref-based forwarding from NotesTree
- Detail panel: type-accent color system, link-specific content view,
  read-only mode for archived items, responsive split layout
- Archived view: inline list with grouped time periods, detail panel
  integration, keyboard-driven unarchive/delete (replaces item-row)
- Inbox list: metadata-to-actions hover swap, voice pill auto-hide
- Bulk action bar: floating pill with keyboard hints
- inbox-utils: generic dateAccessor for groupItemsByTimePeriod
Remove shadow-based focus ring and dark mode bg override in favor
of a subtle border-foreground/20 transition — lighter, more cohesive.
h4yfans added 27 commits March 24, 2026 12:16
Replace the modal file-picker with an inline FilingSection panel that
slides in beside the triage card. Add snooze picker as a side panel.
Simplify the action bar to icon-only buttons with keyboard hints.
Redesign progress bar, completion screen, and streak badge.
Delete the now-unused TriageFilePicker component.
Add a real-time waveform bar visualizer to the voice preview player
using Web Audio API AnalyserNode. Redesign the overall content section
layout with improved spacing and visual hierarchy.
Move VaultSwitcher inline with traffic lights, add auth actions
(sign in/log out) to the vault dropdown, and show email context.
Compact SyncStatus to icon-only in sidebar footer.
Delete SidebarUserProfile (replaced by vault switcher auth section).
Redesign VoiceRecorder with live waveform bars via AudioContext
analyser and compact stop/cancel buttons.
Inline CaptureInput voice recorder instead of replacing the input.
Replace RotateCcw with ArrowTurnBackward for the restore action in
archived view. Use --user-accent-color CSS variable for selection
and focus ring styling in the inbox list.
A single *:focus-visible { outline: none } rule in main.css
replaces ~90 individual focus-visible:ring-* class declarations
across UI primitives and feature components.
Extends fontFamily enum in contracts and settings-sync schemas,
adds @fontsource packages, and registers CSS font-family stacks
in use-theme-sync for the three new typefaces.
Adds settings-primitives.tsx with SettingsHeader, SettingsGroup,
SettingRow, and SettingRowTall components. Rewrites every settings
section to use the new primitives for a consistent, compact layout
with grouped rows, section headers, and a wider 60px sidebar nav.
Replaces hardcoded amber colors with var(--tint) CSS variable
across sync UI. Redesigns device list, recovery phrase flows,
and linking screens with tighter spacing. Adds CloudSavingDone
icon for idle sync status and fixes auth reducer to default
unauthenticated state to the sign-in wizard step.
Tightens vault switcher sizing (16px avatar, 12px text), removes
chevron indicator, and adds onCloseAutoFocus prevention. Rewrites
tag manager and integration list to use bordered row layout
matching the new settings primitives style.
Sets window icon via BrowserWindow config and macOS dock icon
via nativeImage for dev mode. Regenerates IPC invoke map and
preload types to pick up fontFamily enum expansion, inbox
preview-link handler, and folder icon config changes.
Adds generate-icons.mjs for producing app icons from SVG source,
a project favicon, and AGENTS.md with contribution guidelines.
Centers QR layout with dialog title/description, adds copy-code
section with inline copy button, shows encryption badge, and
adjusts dialog container to 400px rounded.
Delete sync-section.tsx; absorb sync status, devices, security,
and sign-out into account-section.tsx. Update sidebar and vault
switcher to navigate to 'account' instead of 'sync'. Move Account
nav item to top of Workspace group.
Normalize headings to div with tracking-[-0.02em], swap ghost
buttons for outline variant with tint-colored primaries, add
inline format validation to linking-code-entry, rework OTP
countdown display with Clock icon and "use different email"
link, truncate long QR linking codes, add data-filled attribute
to InputOTPSlot for conditional border styling.
Add vault:reveal channel to contracts, handler using
shell.showItemInFolder, preload bridge, and vault-section
now calls window.api.vault.reveal() instead of raw
ipcRenderer.invoke.
Replace nested button+button with single role=button div that
handles onClick and onKeyDown. Combine checkmark and edit-gear
into a single trailing slot with hover-swap opacity transition.
Add multi-stop border gradients (teal→indigo→violet→amber),
top-shine overlay, card and logo drop shadows, and background
gradient to icon SVG. Include preview-icon-variants script for
evaluating alternatives.
Change FROM_ADDRESS from memrynote.ai to memrynote.com.
Correct STEP_MAP for recovery-input (2→1) and linking-scan
(2→1) to match actual wizard flow. Remove try-catch around
vault reveal call.
Update getFolders mock to return FolderInfo[] instead of string[].
Add missing mocks for convertToTask, sync modules, logger, and
templates.list. Fix inbox-list assertions for redesigned TypeIcon
and voice transcription status. Replace textbox role with combobox
in tags-row tests. Add formatCompactRelativeTime and dateAccessor
tests. Add shortcut-registry test suite. Use relative dates in
isItemStale test to avoid clock drift.
Add pnpm overrides for undici>=7.24.0 (WebSocket memory/validation
vulns in cheerio and miniflare transitive deps) and flatted>=3.4.2
(prototype pollution via parse() in @vitest/ui).
Add reveal() to VaultClientAPI declaration. Remove inline type
annotation on getStatus callback. Cast Tweet through unknown for
Record access. Cast ContentItem to TweetCardItem at call site.
@h4yfans
h4yfans merged commit c64cb7d into main Mar 24, 2026
4 checks passed
@h4yfans
h4yfans deleted the feat/desktop-polish branch April 2, 2026 20:27
h4yfans added a commit that referenced this pull request May 6, 2026
…111)

* fix: resolve real project ID before creating onboarding task

The task creation step passed a hardcoded 'personal' projectId which
is not a real DB record — causes silent FK violation on every first run.

Now resolves a valid project: uses first existing project, or creates
a default 'Personal' project if none exist.


* feat(settings): add shortcuts section and expand editor settings UI

- Add ShortcutRegistry with all rebindable app shortcuts, conflict detection,
  and display helpers (formatBinding, findConflicts, getGroupedShortcuts)
- Add ShortcutsSettings section with search, grouped list, inline key capture,
  conflict warnings, per-shortcut reset, and reset-all button
- Expand EditorSettings section to expose width, spellCheck, autoSaveDelay,
  and showWordCount controls (migrates from useNoteEditorSettings to the
  richer useEditorSettings hook)
- Wire Shortcuts nav item into settings.tsx sidebar
- Regenerate IPC invoke map (was stale)


* feat(settings): implement account section (T029-T033)

- account-handlers.ts: GET_INFO (email+joinedAt from store/DB) + SIGN_OUT
- Register account handlers in IPC index; regenerate invoke map
- Preload: add account namespace + update index.d.ts types
- useAccountInfo hook (GET_INFO), useDevices hook (wraps syncDevices)
- AccountSection: identity card, storage usage bar, recovery key stub, sign-out dialog
- Wire AccountSection into settings.tsx sidebar


* feat(settings): implement recovery key dialog (T034-T036)

- account-handlers: add GET_RECOVERY_KEY handler — retrieves master key
  from OS keychain (requires valid access token), returns base64 encoded
- RecoveryKeyDialog: blurred display until reveal, copy button, auto-clear on close
- AccountSection: wire View button → RecoveryKeyDialog, remove disabled state


* feat(settings): add global capture shortcut configuration

- Add REGISTER_GLOBAL_CAPTURE IPC channel to SettingsChannels
- Implement applyGlobalCaptureShortcut() in settings-handlers for OS-level
  global shortcut registration/unregistration via electron globalShortcut
- Wire settings keyboard changes to re-apply shortcut live on update
- Expose REGISTER_GLOBAL_CAPTURE in preload bridge
- Add GlobalCaptureRow component in shortcuts-section with live capture,
  macOS accessibility permission detection, and clear/reset UI
- Update app startup to use applyGlobalCaptureShortcut with fallback
  to legacy registerQuickCaptureShortcut when no setting saved


* fix(ci): regenerate IPC invoke map and fix account-handlers test mock

- Regenerate generated-ipc-invoke-map.ts to include new settings channels
  (REGISTER_GLOBAL_CAPTURE) added by recent settings feature commits
- Add missing account-handlers mock to ipc/index.test.ts — the account
  section feature introduced registerAccountHandlers but didn't update
  the registration lifecycle test, causing 3 test failures


* perf: reduce cold-start time by parallelizing vault open with window creation

Two changes to improve startup performance toward the <2s cold-start target:

1. Move createWindow() before autoOpenLastVault() in the app.whenReady()
   handler. The vault open path (SQLite init, migrations, FTS setup, file
   watcher, sync runtime) was blocking window creation for ~300-600ms.
   The renderer already handles vault-not-open state via VaultOnboarding
   and useVault(isLoading), so the window can appear immediately while the
   vault opens in the background.

2. Eliminate synchronous IPC in preload startup theme resolution. The
   getStartupThemeSync() function used ipcRenderer.sendSync on every
   launch, blocking the renderer until the main process responded.
   Now reads from localStorage cache first (populated on the previous run),
   falling back to sendSync only on first launch or corrupted storage.


* chore(ipc): update generated-ipc-invoke-map with current ts-printer format

The ipc-invoke-map generator uses TypeScript's printer API; output format
differs between ts versions (compact one-liners vs expanded multi-line).
Commit the freshly generated format so ipc:check passes.


* perf: optimize startup I/O and large-vault indexing speed

- store.ts: add module-level in-memory cache — eliminates repeated
  synchronous disk reads on every getCurrentVaultPath() and getVaults()
  call; reads disk once, subsequent gets are O(1) in-memory lookups

- indexer.ts: replace sequential await-in-loop with concurrency-limited
  parallel indexing (limit=8); 1000-note vault indexing goes from ~30s
  to ~4s by overlapping file reads and cache writes within SQLite's
  safe concurrency window

- index.ts: hoist dynamic import('fs') and import('mime-types') out of
  the hot memry-file:// protocol handler to static top-level imports;
  eliminates repeated module resolution overhead on every local file serve

All existing store (4) and indexer (19) tests pass. Typecheck clean.

* feat(a11y): WCAG 2.1 AA accessibility pass — keyboard nav, ARIA, focus indicators

- Add global :focus-visible safety net in main.css so any focusable element without an explicit ring still gets a visible outline
- Fix KanbanCard: add onKeyDown handler (Enter=open, Space=toggle/complete) and focus-visible:ring-2 so Tab users see focus
- Destroy canvas blindspot: wrap GraphCanvas in role="img" with aria-label summarising node/edge counts; add sr-only <ul> listing all nodes by label+type for screen readers
- Fix TagAutocomplete combobox ARIA: add role="combobox" + aria-controls + aria-activedescendant on input; give each option a unique id; SuggestionItem changed from button to div[role=option] inside role=listbox; TagPill span gets role=listitem
- Add ArrowUp/Down keyboard navigation to TagInputPopup; wire focusedIndex to TagOption isFocused prop for visual + ARIA feedback; input gets role=combobox + aria-controls + aria-activedescendant
- Fix pre-existing TS2352 type error in use-undoable-task-actions.ts (Task→Record cast)
- Regenerate IPC invoke map


* feat(undo): add removeUndoEntry for double-fire prevention

Toast undo button + Cmd+Z could both fire the same undo action.
removeUndoEntry(id) lets consumers yank a specific entry from the
stack after consuming it via an alternate path.

* feat(undo): add Cmd+Z undo for all task CRUD and bulk operations

New useUndoableTaskActions hook centralizes undo logic for single-task
operations (create, delete, complete, uncomplete, archive, field update).
useBulkActions gains optional registerUndo/onAddTask params to register
all 8 bulk operations with the global undo stack. 41 new test cases.

* refactor(tasks): wire task handlers to useUndoableTaskActions

Replace inline handler bodies in tasks.tsx with hook calls. Removes
~100 LOC of completion/repeat/delete logic from the page component.
Every task action now registers with the global undo stack.

* chore: remove calendar view feature

Calendar view was never shipped to users. Remove all components,
CSS variables, utility functions, and tests to reduce dead code.

* chore: remove stale items feature

Stale items UI (age indicators, thresholds, nudge messages) was
unused. Remove components, utilities, and 346 lines of tests.

* feat(ui): add PageToolbar and Pill primitives

Reusable toolbar with segment tabs and icon buttons for page headers.
Pill component with color variants for status/type badges.

* refactor(tasks): migrate design tokens and normalize typography

Replace custom text-text-{primary,secondary,tertiary} tokens with
standard muted-foreground/foreground. Bump base font from 12px to
13px across task rows, filters, kanban, and toolbar for readability.

* feat(inbox): redesign inbox with toolbar, filters, and search

Add PageToolbar to inbox header with inline capture input.
Redesign health view with type-breakdown cards and Pill badges.
Add archived search, type filters, and snoozed item toggle.
Refactor archived item rows with icon config map.

* feat(capture): add compact mode and streamline layout

New compact prop for embedding in toolbars. Simplify density
handling, improve token overlay alignment, and tighten spacing.

* fix: add error toasts, a11y attributes, and polish misc components

Add extractErrorMessage toast to sidebar note creation. Add aria
labels to notes tree, formula editor, and tag detail view. Wire
scrollContainerRef in ContentArea. Clean up remaining calendar CSS
vars and add font-heading variable.

* chore(ipc): regenerate invoke map with current handler signatures

* feat(theme): wire accent color setting to UI via tint CSS variables

The accent color picker in Settings > Appearance was saving to DB but
nothing read the value. Add --tint-* CSS variables derived from
--user-accent-color via color-mix(), rewire --sidebar-terracotta to
var(--tint), register in @theme inline, and replace ~30 hardcoded
blue-500 Tailwind classes with tint-* across tabs, AI agent, split
view, and misc components.

* feat(settings): sync accent color across devices and prevent startup flash

SET_GENERAL_SETTINGS now calls SettingsSyncManager.updateField() for
syncable fields (theme, fontSize, fontFamily, accentColor, language)
while excluding device-specific ones (startOnBoot). getStartupTheme
returns { theme, accentColor } so preload applies --user-accent-color
before React hydrates, preventing a flash of default indigo. Adds
localStorage caching for accent color on the fast startup path.

* feat(icons): add HugeIcons rendering infrastructure

Icon value utilities (emoji-icon-utils), lazy HugeIcons loader with
cache, NoteIconDisplay component, virtualized icon grid picker, and
search hook for the icon library.

* feat(inbox): implement real capture patterns query

Replace stub getCapturePatterns with actual DB aggregation across
time-heatmap, type-distribution, top-domains, and top-tags.
Add unit tests and update heatmap component for compact layout.

* chore: remove inbox bankruptcy feature

Drop BulkArchiveOlderThan schema, IPC channel, handler, service
method, useInboxBankruptcy hook, and CollectorWarning/BankruptcyDialog
UI. The feature was incomplete (never wired in preload bridge) and
replaced by manual archive workflows.

* feat(folders): polish tree component and add folder icon support

- Add icon field to FolderConfig and new FolderInfo type in contracts
- Return FolderInfo[] (path + icon) from getFolders across the stack
- Update all consumers to use FolderInfo instead of raw string paths
- Polish kibo-ui tree: hideLines prop, chevron icons, tighter spacing
- Polish sidebar section: uppercase labels, collapsible chevron
- Simplify preload startup theme (drop accent flash workaround)
- Add design tokens reference doc

* feat(folders): add folder icon picker and NoteIconDisplay across UI

- FolderIconButton with portal-based EmojiPicker for context menu
- EmojiPicker gains icon tab (HugeIconGrid) alongside emoji tab
- NoteIconDisplay replaces raw emoji spans in tree, tabs, search,
  graph tooltip, property cell, and folder view
- Sidebar actions refactored to ref-based forwarding from NotesTree

* feat(inbox): redesign detail panel, archived view, list, and bulk bar

- Detail panel: type-accent color system, link-specific content view,
  read-only mode for archived items, responsive split layout
- Archived view: inline list with grouped time periods, detail panel
  integration, keyboard-driven unarchive/delete (replaces item-row)
- Inbox list: metadata-to-actions hover swap, voice pill auto-hide
- Bulk action bar: floating pill with keyboard hints
- inbox-utils: generic dateAccessor for groupItemsByTimePeriod

* refactor(ui): simplify Input focus ring to border-only

Remove shadow-based focus ring and dark mode bg override in favor
of a subtle border-foreground/20 transition — lighter, more cohesive.

* feat(inbox): polish detail panel with LinkPreview, inline filing, and simplified accents

- Replace LinkDetailContent with lightweight LinkPreview (favicon monogram, date, title)
- Redesign detail panel as 600px inline component (was Sheet/drawer)
- Redesign filing section: auto-select top AI folder, searchable folder picker,
  inline tag input with AI-suggested tags, polished note linking
- Simplify type-accents to accentClass+hex pattern, drop getTypeAccentColor/Bg
- Redesign tag-autocomplete: flat dropdown with AI suggestions, space/comma to add
- Polish inbox list and archived views with consistent spacing

Note: bulk-file-panel and bulk-tag-popover still reference old TagAutocomplete props
(onTagsChange, showSections) — to be updated before merge.

* refactor(inbox): extract DetailHeader and NoteDetail from detail panel

Unified compact header (type icon + label + time + close) replaces two
inline variants. Note type gets dedicated body with titled content card.
All types now share the same header component.

* refactor(inbox): simplify reminder TypeIcon and remove isViewed prop

* fix(tags): cast counts to Number and prune orphaned definitions

SQLite aggregate results were string-concatenating counts across note/task
tag queries. Now casts via Number() at the query boundary, filters zero-count
tags, deletes orphaned tag definitions, and switches the renderer hook to
the merged tagsService.getAllWithCounts() endpoint.

* refactor(social): remove unused non-Twitter platform support

Only Twitter/X extraction was ever wired up — LinkedIn, Mastodon, Bluesky,
and Threads endpoints were null stubs. Narrows SocialPlatform to 'twitter',
strips dead oEmbed config, removes platform icons and detection branches
from social-card, url-utils, and contracts.

* feat(inbox): add live title extraction, compact dates, and PDF icon

- Extract title from BlockNote blocks as user edits (blocknote-title util)
- Replace relative timestamps with compact "DD Mon YY" format everywhere
- Add FilePdf icon for PDF inbox items in list and content section
- Improve content-section layout with flex gap and image constraints

* refactor(sidebar): use NoteIconDisplay and simplify task count badge

Switch bookmark/tag emoji rendering to NoteIconDisplay component for
consistent icon handling. Simplify today's task count to plain text
instead of colored pill. Tighten tag list horizontal padding.

* fix(graph): guard nodeSummary memo against null graph data

Move nodeSummary useMemo above the early-return so it doesn't access
data.nodes when data is undefined during loading state.

* style(ui): update dark theme to neutral charcoal and standardize border radius

Dark mode palette shifts from cool-blue (#0e0e10) to neutral charcoal
(#191919) for warmer feel. Sidebar follows suit (#131315 → #202020).

Standardize rounded-lg → rounded-md across all components, modals,
dropdowns, cards, inputs, and density config for tighter visual rhythm.
Update DESIGN_TOKENS.md to reflect new defaults.

* feat(contracts): add tweetId to SocialMetadata type

Enables renderer-side tweet lookup via react-tweet's useTweet hook.

* refactor(social): replace server-side fetch with synchronous URL parsing

Remove Twitter Syndication API calls and background metadata fetching.
The renderer now handles tweet data via react-tweet, so the main process
only needs to extract tweetId and authorHandle from the URL.

* build(desktop): add react-tweet and update CSP for Twitter embeds

Allowlist pbs.twimg.com (images), cdn.syndication.twimg.com and
react-tweet.vercel.app (API) in both meta-tag and session CSP.

* feat(inbox): add TweetCard component for embedded tweet rendering

Client-side tweet rendering via react-tweet's useTweet hook with
skeleton loading, error state, avatar, metrics, and media grid.

* feat(inbox): add ReminderDetail and wire content routing

ReminderDetail shows trigger banner, source card with navigation,
mark-as-viewed, and snooze presets. Route social→TweetCard and
reminder→ReminderDetail in content-section switch. Remove redirect
for reminders in inbox list — they now open in the detail panel.

* feat(inbox): add titleFromUrl and isBotPageTitle utilities

Extract human-readable titles from URL slugs when metadata fetch
fails (e.g. Cloudflare-blocked sites). Detect bot challenge page
titles to avoid storing "Just a moment..." as link titles.

17 new tests covering URL slug parsing, ID stripping, file extension
removal, domain fallback, and bot-detection title matching.

* fix(inbox): map metascraper fields to LinkMetadata and use slug titles

Fix field name mismatch: metascraper returns image/logo/publisher/date
but LinkMetadata expects heroImage/favicon/siteName/publishedDate.

Use titleFromUrl for readable titles at capture time and on fetch
failure. Reject bot-detection page titles via isBotPageTitle.

* feat(inbox): redesign LinkPreview with hero image and domain bar

Raindrop-style card layout: hero image from thumbnailUrl with
gradient+favicon fallback, bolder title, excerpt, domain bar with
favicon/monogram, full-width open button, capture timestamp.

Reads both new and legacy metascraper field names for backward
compat with existing items.

* fix(inbox): remove auto-link first suggested note on filing

Prevented unintended note linking when opening the filing panel.
Users should explicitly choose which notes to link.

* feat(transcription): auto-title voice memos from first sentence

When transcription completes, replace the default "Voice memo (...)"
title with a trimmed first-sentence excerpt (up to 60 chars) for
better scannability in the inbox list.

* feat(inbox): add formatTimeAgo utility, new icons, and social-card export

Add Archive03 and ArrowTurnBackward icons for triage actions.
Add formatTimeAgo() relative date helper for compact timestamps.
Export extractHandleFromUrl from social-card for quick-capture use.

* feat(inbox): add link preview IPC and quick-capture resize pipeline

Add PREVIEW_LINK channel that fetches URL metadata without creating
an inbox item (used for live link previews in quick capture).
Add quick-capture:resize IPC so the renderer can auto-size the
window height via ResizeObserver. Reduce default height to 82px.

* refactor(inbox): replace custom toast with Sonner and redesign empty state

Remove the hand-rolled toast system (ToastContainer, useInboxNotifications
state, addToast prop-threading) in favor of Sonner's global toast() API.
This eliminates prop drilling through 5 layers of components.

Redesign the inbox-zero empty state to show weekly filing count and
current streak instead of the getting-started onboarding flow.
Delete unused GettingStartedState and CaptureMethodsGrid components.

* feat(inbox): redesign quick capture with sub-components

Split the monolithic QuickCapture component into focused sub-components:
QuickCaptureInput, QuickCaptureFooter, QuickCaptureStates (success/error/
duplicate), LinkPreviewCard, and FilePreviewCard.

Add live link preview with debounced metadata fetch, social post
detection, auto-resize via ResizeObserver, and file/image preview cards.
Add textarea to drag-region no-drag exclusion list.

* feat(inbox): redesign triage mode with inline filing panel

Replace the modal file-picker with an inline FilingSection panel that
slides in beside the triage card. Add snooze picker as a side panel.
Simplify the action bar to icon-only buttons with keyboard hints.
Redesign progress bar, completion screen, and streak badge.
Delete the now-unused TriageFilePicker component.

* feat(inbox): add waveform visualizer and redesign content section

Add a real-time waveform bar visualizer to the voice preview player
using Web Audio API AnalyserNode. Redesign the overall content section
layout with improved spacing and visual hierarchy.

* style(desktop): polish sidebar, vault switcher, and voice recorder

Move VaultSwitcher inline with traffic lights, add auth actions
(sign in/log out) to the vault dropdown, and show email context.
Compact SyncStatus to icon-only in sidebar footer.
Delete SidebarUserProfile (replaced by vault switcher auth section).
Redesign VoiceRecorder with live waveform bars via AudioContext
analyser and compact stop/cancel buttons.
Inline CaptureInput voice recorder instead of replacing the input.

* chore: add metascraper test script

* style(inbox): use ArrowTurnBackward icon and accent color CSS variable

Replace RotateCcw with ArrowTurnBackward for the restore action in
archived view. Use --user-accent-color CSS variable for selection
and focus ring styling in the inbox list.

* style(desktop): replace per-component focus rings with global reset

A single *:focus-visible { outline: none } rule in main.css
replaces ~90 individual focus-visible:ring-* class declarations
across UI primitives and feature components.

* feat(desktop): add Gelasio, Geist, and Inter font families

Extends fontFamily enum in contracts and settings-sync schemas,
adds @fontsource packages, and registers CSS font-family stacks
in use-theme-sync for the three new typefaces.

* refactor(settings): extract shared primitives and redesign all pages

Adds settings-primitives.tsx with SettingsHeader, SettingsGroup,
SettingRow, and SettingRowTall components. Rewrites every settings
section to use the new primitives for a consistent, compact layout
with grouped rows, section headers, and a wider 60px sidebar nav.

* style(sync): redesign sync components and auth wizard

Replaces hardcoded amber colors with var(--tint) CSS variable
across sync UI. Redesigns device list, recovery phrase flows,
and linking screens with tighter spacing. Adds CloudSavingDone
icon for idle sync status and fixes auth reducer to default
unauthenticated state to the sign-in wizard step.

* style(desktop): polish vault switcher, tag manager, and integration list

Tightens vault switcher sizing (16px avatar, 12px text), removes
chevron indicator, and adds onCloseAutoFocus prevention. Rewrites
tag manager and integration list to use bordered row layout
matching the new settings primitives style.

* chore(desktop): set app icon and regenerate IPC types

Sets window icon via BrowserWindow config and macOS dock icon
via nativeImage for dev mode. Regenerates IPC invoke map and
preload types to pick up fontFamily enum expansion, inbox
preview-link handler, and folder icon config changes.

* chore: add icon generation script, favicon, and agents doc

Adds generate-icons.mjs for producing app icons from SVG source,
a project favicon, and AGENTS.md with contribution guidelines.

* style(sync): redesign QR device linking dialog

Centers QR layout with dialog title/description, adds copy-code
section with inline copy button, shows encryption badge, and
adjusts dialog container to 400px rounded.

* refactor(settings): merge sync section into account section

Delete sync-section.tsx; absorb sync status, devices, security,
and sign-out into account-section.tsx. Update sidebar and vault
switcher to navigate to 'account' instead of 'sync'. Move Account
nav item to top of Workspace group.

* style(sync): polish sync component typography and layouts

Normalize headings to div with tracking-[-0.02em], swap ghost
buttons for outline variant with tint-colored primaries, add
inline format validation to linking-code-entry, rework OTP
countdown display with Clock icon and "use different email"
link, truncate long QR linking codes, add data-filled attribute
to InputOTPSlot for conditional border styling.

* feat(vault): add vault:reveal IPC to open folder in Finder

Add vault:reveal channel to contracts, handler using
shell.showItemInFolder, preload bridge, and vault-section
now calls window.api.vault.reveal() instead of raw
ipcRenderer.invoke.

* style(tasks): rework project dropdown for keyboard accessibility

Replace nested button+button with single role=button div that
handles onClick and onKeyDown. Combine checkmark and edit-gear
into a single trailing slot with hover-swap opacity transition.

* style(icons): add gradient border and drop shadow to app icon

Add multi-stop border gradients (teal→indigo→violet→amber),
top-shine overlay, card and logo drop shadows, and background
gradient to icon SVG. Include preview-icon-variants script for
evaluating alternatives.

* fix: update email sender domain and wizard step mapping

Change FROM_ADDRESS from memrynote.ai to memrynote.com.
Correct STEP_MAP for recovery-input (2→1) and linking-scan
(2→1) to match actual wizard flow. Remove try-catch around
vault reveal call.

* test: update mocks and assertions for current API surface

Update getFolders mock to return FolderInfo[] instead of string[].
Add missing mocks for convertToTask, sync modules, logger, and
templates.list. Fix inbox-list assertions for redesigned TypeIcon
and voice transcription status. Replace textbox role with combobox
in tags-row tests. Add formatCompactRelativeTime and dateAccessor
tests. Add shortcut-registry test suite. Use relative dates in
isItemStale test to avoid clock drift.

* docs: add desktop polish changelog entry

* fix(deps): override undici and flatted for security audit

Add pnpm overrides for undici>=7.24.0 (WebSocket memory/validation
vulns in cheerio and miniflare transitive deps) and flatted>=3.4.2
(prototype pollution via parse() in @vitest/ui).

* chore(ipc): regenerate invoke map with updated printer format

* fix: guard app.dock access for non-macOS platforms

* fix: resolve web typecheck errors in vault, tweet, and content types

Add reveal() to VaultClientAPI declaration. Remove inline type
annotation on getStatus callback. Cast Tweet through unknown for
Record access. Cast ContentItem to TweetCardItem at call site.

* fix: add reveal method to vault service stub

* test: update email test to match memrynote.com sender domain

---------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant