Skip to content

Refactor theme management: delegate preferences to PreferencesService#8

Merged
ptheofan merged 19 commits intomainfrom
claude/fix-bidirectional-sync-EzfVF
Apr 11, 2026
Merged

Refactor theme management: delegate preferences to PreferencesService#8
ptheofan merged 19 commits intomainfrom
claude/fix-bidirectional-sync-EzfVF

Conversation

@ptheofan
Copy link
Copy Markdown
Owner

Summary

This PR refactors theme management to establish a clear separation of concerns: ThemeService now handles only OS-level system theme detection, while PreferencesService becomes the single source of truth for theme mode preferences (light/dark/system).

Key Changes

  • ThemeService simplification: Removed all preference persistence logic (getCurrentTheme(), setTheme(), preferences file I/O, getPreferencesPath()). ThemeService now focuses exclusively on detecting system theme via nativeTheme and notifying listeners of OS-level changes.

  • PreferencesService integration: Theme mode preference is now stored and managed through PreferencesService's core.theme.mode field, ensuring consistency with other user preferences.

  • ThemeHandler delegation:

    • GET_CURRENT and SET IPC handlers now delegate to PreferencesService instead of ThemeService
    • GET_SYSTEM and ON_SYSTEM_CHANGE continue to use ThemeService for OS detection
  • Renderer synchronization: Removed redundant theme sync logic in renderer.ts. Theme mode is now exclusively managed through PreferencesService, eliminating the need to separately call theme.set().

  • Typography preferences: Added fontFamily and monoFontFamily fields to CorePreferences with sensible defaults, allowing users to customize font families alongside other typography settings.

  • CSS generation: Updated theme CSS generation to respect user-configured font families from preferences.

  • Test updates: Refactored unit tests to reflect the new architecture, removing preference persistence tests from ThemeService and updating ThemeHandler tests to verify PreferencesService integration.

Implementation Details

  • ThemeService constructor no longer accepts a preferences directory parameter
  • PreferencesService is now passed to registerThemeHandlers() alongside ThemeService
  • The separation ensures PreferencesService remains the authoritative source for all user preferences, improving maintainability and reducing duplication

https://claude.ai/code/session_01JPKgqUDYb7zrib8Qi6KF6n

ptheofan and others added 2 commits March 9, 2026 17:10
One-way markdown → Google Docs sync with three-way diffing
infrastructure for surgical updates that preserve reviewer comments.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
16-task plan covering auth, converter, three-way diffing, API wrapper,
IPC handlers, UI components, mermaid support, and E2E tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ptheofan ptheofan self-assigned this Apr 10, 2026
ptheofan and others added 5 commits April 11, 2026 14:37
…lection

Theme mode was stored in two places (ThemeService's theme-preferences.json
and PreferencesService's preferences.json), causing them to drift out of
sync — toolbar toggles were lost on restart because only ThemeService was
updated while PreferencesService (the one read at startup) kept stale data.

This commit makes PreferencesService the single source of truth:
- ThemeService stripped to OS-level system theme detection only
- ThemeHandler's GET_CURRENT/SET now delegate to PreferencesService
- Removed dual-write in renderer's handlePreferencesChange
- Removed ThemeService.initialize() from main process startup

Also adds user-configurable font families (body + monospace) to the
preferences system, as requested in issue #6's last comment. Font
values are stored in preferences.json and override the theme defaults
via CSS variables.

Closes #6

https://claude.ai/code/session_01JPKgqUDYb7zrib8Qi6KF6n
The font family preferences were being generated as --font-body and
--font-mono CSS variables but the CSS used hardcoded font stacks.
Replace hardcoded values with var() references so user-configured
fonts actually take effect. Fallbacks preserve the original stacks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ptheofan ptheofan force-pushed the claude/fix-bidirectional-sync-EzfVF branch from 4f10e8b to 046dcc0 Compare April 11, 2026 11:43
ptheofan and others added 9 commits April 11, 2026 14:56
Replace raw CSS font-stack text inputs with a searchable dropdown that
enumerates installed system fonts via queryLocalFonts(). Each option
previews in its own typeface. "System Default" uses the built-in
fallback stack. Empty string = system default, so generateTypographyCSS
skips the variable and the CSS var() fallback takes over.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Font family CSS variables should only affect .markdown-body (the
rendered markdown), not the app chrome (toolbar, preferences panel,
find bar). Reverted body and color picker input back to hardcoded
font stacks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Separates document font variables from app chrome fonts. The app UI
keeps its hardcoded font stacks; only the markdown content area uses
the --font-doc-* variables from user preferences.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Click the color swatch to open the macOS native color picker
(NSColorPanel). Hex result is converted to OKLCH via existing utils.
Text inputs for hex and OKLCH direct entry remain functional.

A reset button (↺) appears when the color differs from its default
value. All core color pickers (background, link, headings) and plugin
color pickers now receive their default values for reset support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the native color input with a purpose-built OKLCH picker that
shows a 2D Lightness×Chroma plane with the sRGB gamut boundary as a
natural curve, plus a hue strip. Clicking the swatch opens the picker
as a popup. Uses inline OKLCH→sRGB math (no extra dependencies) with
ImageData rendering for the canvas. Cached image data avoids full
re-renders when only the crosshair position changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restructure ColorPicker layout from one shared swatch to per-row
swatches. Each input (hex and oklch) now has its own mini swatch
that opens the OKLCH picker popup. DOM references stored directly
during construction — no querySelector for event binding.

Layout: [swatch] #hex       [reset]
        [swatch] oklch(...)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add try-catch and console.error to swatch click handler for debugging.
Set pointer-events: none on swatch child elements so clicks reliably
reach the parent preview element.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The popup (z-index: 2000) was rendering behind the preferences
container (z-index: 9000). Raised to 10000 so it appears on top.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ptheofan ptheofan force-pushed the claude/fix-bidirectional-sync-EzfVF branch from 3936cf5 to 03bda06 Compare April 11, 2026 12:59
Reverted to one swatch per ColorPicker (not two per-row swatches
that confused background/foreground). Layout is now:
  [swatch] [hex input  ] [reset]
           [oklch input]

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ptheofan ptheofan force-pushed the claude/fix-bidirectional-sync-EzfVF branch from 03bda06 to 9ea4c73 Compare April 11, 2026 13:03
claude and others added 2 commits April 11, 2026 13:12
field.defaultValue is already narrowed by the switch on field.type,
so the `as string` and `as ColorPair` casts are redundant and flagged
by @typescript-eslint/no-unnecessary-type-assertion.

https://claude.ai/code/session_01JPKgqUDYb7zrib8Qi6KF6n
Fix duplicate System sections caused by async race condition in
renderSystemSection, eliminate flicker by updating controls in-place
instead of rebuilding DOM, and debounce expensive diagram re-renders
during color picker drag.

Rename all document CSS variables to --doc-* prefix to distinguish
from app chrome variables, fixing the background color picker which
was writing to a dead variable. Flatten heading controls inline in
Typography section with separator lines, and drive collapsible
section visibility from CSS classes instead of inline styles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ptheofan ptheofan merged commit c2a37df into main Apr 11, 2026
2 checks passed
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.

2 participants