Sync diff renderer and worker theme with resolved app theme - #108

Merged
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35
Feb 27, 2026
Merged

Sync diff renderer and worker theme with resolved app theme#108
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

  • update diff panel rendering to use explicit Pierre theme names (pierre-light/pierre-dark) derived from the resolved app theme
  • include resolved theme in file diff React keys so diff views remount cleanly on theme switches
  • sync worker pool highlighter/render options with current theme via DiffWorkerThemeSync
  • improve useTheme snapshot stability by tracking both stored theme and system dark-mode state for consistent useSyncExternalStore updates

Testing

  • Not run (not provided in patch context)
  • Suggested check: toggle app theme between light, dark, and system and verify diff colors update immediately without stale highlighting
  • Suggested check: with system theme selected, change OS color scheme and verify diff panel/worker rendering follows the new resolved theme

Note

Medium Risk
Touches diff rendering and worker pool configuration; regressions could cause stale or incorrect syntax highlighting when toggling themes, but changes are UI-only and not security/data-critical.

Overview
Ensures diff rendering updates cleanly on theme changes by mapping the app’s resolved theme to explicit Pierre themes (pierre-light/pierre-dark) and passing them into FileDiff options.

Updates the diff worker pool to initialize and continuously sync highlighter/render options to the current theme via a new DiffWorkerThemeSync, and adjusts useTheme’s useSyncExternalStore snapshot to include cached system dark-mode state for more consistent updates (especially when theme=system).

Written by Cursor Bugbot for commit df323f8. This will update automatically on new commits. Configure here.

Note

Sync diff renderer and worker theme with resolved app theme in apps/web/src/components/DiffPanel.tsx and apps/web/src/components/DiffWorkerPoolProvider.tsx

Restrict DiffThemeType to "light" | "dark", add resolveDiffThemeName to map resolved themes to "pierre-light" or "pierre-dark", remount FileDiff items on theme change via keyed resolvedTheme, and propagate theme names to the worker pool render and highlighter options. Core changes are in DiffPanel.tsx, DiffWorkerPoolProvider.tsx, and useTheme.ts.

📍Where to Start

Start with resolveDiffThemeName and its usage in DiffPanel in DiffPanel.tsx, then review DiffWorkerPoolProvider and DiffWorkerThemeSync in DiffWorkerPoolProvider.tsx.

Macroscope summarized df323f8.

Summary by CodeRabbit

  • Changes

    • Removed "system" theme option from diff panel; now supports "light" and "dark" only.
  • Improvements

    • Improved theme synchronization for diff rendering to ensure consistency across the UI.
    • Enhanced performance of theme handling.

- Pass explicit Pierre light/dark theme into diff render options
- Re-key file diffs on resolved theme so views rerender on theme switch
- Stabilize `useTheme` snapshots to propagate system theme updates reliably
@coderabbitai

coderabbitaiBot commented Feb 27, 2026

Copy link
Copy Markdown

Walkthrough

These changes implement theme synchronization across the diff rendering system. DiffPanel removes the "system" theme option and uses a concrete theme mapping to "pierre-light" or "pierre-dark". DiffWorkerPoolProvider now synchronizes theme changes to the worker pool, and useTheme introduces snapshot-based caching for optimized theme resolution with system preference tracking.

Changes

Cohort / File(s)Summary
Diff Panel Theme Mapping
apps/web/src/components/DiffPanel.tsx
Removed "system" from DiffThemeType ("light" | "dark" only). Added resolveDiffThemeName helper to map theme values to "pierre-light" or "pierre-dark". Updated rendering to use themedFileKey and pass resolved theme to FileDiff component.
Worker Pool Theme Synchronization
apps/web/src/components/DiffWorkerPoolProvider.tsx
Integrated theme synchronization with worker pool. Added DiffWorkerThemeSync component to detect theme changes and update worker render options. Computes diffThemeName and passes it to highlighterOptions. Extended provider to sync theme changes dynamically.
Theme Hook Snapshot Caching
apps/web/src/hooks/useTheme.ts
Introduced ThemeSnapshot type containing theme and systemDark fields. Implemented module-level caching in getSnapshot to avoid recomputation when values unchanged. Updated getSnapshot to return ThemeSnapshot and useTheme to consume snapshots via useSyncExternalStore, eliminating redundant system preference lookups.

Sequence Diagram

sequenceDiagram
participant UI as React UI
participant useTheme as useTheme Hook
participant ExtStore as External Store
participant DiffProvider as DiffWorkerPoolProvider
participant Worker as Worker Pool
UI->>useTheme: Read current theme
useTheme->>ExtStore: getSnapshot() → ThemeSnapshot
ExtStore-->>useTheme: {theme, systemDark}
useTheme-->>UI: resolvedTheme (light/dark)
UI->>DiffProvider: Theme changed signal
DiffProvider->>useTheme: useTheme() → current theme
useTheme-->>DiffProvider: resolvedTheme
DiffProvider->>DiffProvider: Compute diffThemeName<br/>(light→pierre-light, etc.)
DiffProvider->>Worker: setRenderOptions({<br/>highlighterOptions: {theme}})
Worker-->>DiffProvider: Updated
DiffProvider->>UI: DiffPanel with themed<br/>FileDiff components
UI-->>UI: Render with active theme
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main objective of the pull request: synchronizing diff renderer and worker theme with the resolved app theme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/c44d2b35

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/web/src/components/DiffWorkerPoolProvider.tsx (1)

10-32: Consider logging errors instead of silently swallowing them.

The .catch(() => undefined) on line 28 silently discards any errors from setRenderOptions. While you may not want to crash the UI, logging the error would help with debugging theme sync issues.

♻️ Proposed improvement
 void workerPool
.setRenderOptions({
...current,
theme: themeName,
})
- .catch(() => undefined);+ .catch((err) => {+ console.error("[DiffWorkerThemeSync] Failed to sync theme:", err);+ });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx` around lines 10 - 32, In
DiffWorkerThemeSync, stop silently swallowing errors from
workerPool.setRenderOptions; replace the .catch(() => undefined) with a handler
that logs the error (e.g., console.error or your app logger) and includes
context (mention themeName and that it occurred in DiffWorkerThemeSync when
calling setRenderOptions on the workerPool returned by useWorkerPool); ensure
this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).
apps/web/src/components/DiffPanel.tsx (1)

19-23: Extract resolveDiffThemeName to a shared utility to eliminate duplication.

This function is duplicated in DiffWorkerPoolProvider.tsx (lines 6-8). Consider extracting it to a shared location, e.g., ~/lib/diffTheme.ts.

♻️ Proposed extraction

Create a new file apps/web/src/lib/diffTheme.ts:

exporttypeDiffThemeType="light"|"dark";exportfunctionresolveDiffThemeName(theme: DiffThemeType){returntheme==="dark" ? "pierre-dark" : "pierre-light";}

Then import it in both files:

-type DiffThemeType = "light" | "dark";--function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}+import { resolveDiffThemeName, type DiffThemeType } from "~/lib/diffTheme";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffPanel.tsx` around lines 19 - 23, Extract the
duplicated resolveDiffThemeName and DiffThemeType into a shared utility: create
a new module that exports `type DiffThemeType = "light" | "dark"` and `function
resolveDiffThemeName(theme: DiffThemeType)` (returning "pierre-dark" for "dark"
else "pierre-light"), then replace the local definitions in both `DiffPanel` and
`DiffWorkerPoolProvider` with imports of `DiffThemeType` and
`resolveDiffThemeName` from the new utility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/src/components/DiffPanel.tsx`:
- Around line 19-23: Extract the duplicated resolveDiffThemeName and
DiffThemeType into a shared utility: create a new module that exports `type
DiffThemeType = "light" | "dark"` and `function resolveDiffThemeName(theme:
DiffThemeType)` (returning "pierre-dark" for "dark" else "pierre-light"), then
replace the local definitions in both `DiffPanel` and `DiffWorkerPoolProvider`
with imports of `DiffThemeType` and `resolveDiffThemeName` from the new utility.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx`:
- Around line 10-32: In DiffWorkerThemeSync, stop silently swallowing errors
from workerPool.setRenderOptions; replace the .catch(() => undefined) with a
handler that logs the error (e.g., console.error or your app logger) and
includes context (mention themeName and that it occurred in DiffWorkerThemeSync
when calling setRenderOptions on the workerPool returned by useWorkerPool);
ensure this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 041acf1 and df323f8.

📒 Files selected for processing (3)
  • apps/web/src/components/DiffPanel.tsx
  • apps/web/src/components/DiffWorkerPoolProvider.tsx
  • apps/web/src/hooks/useTheme.ts

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicate helper function across two files
    • Extracted resolveDiffThemeName into the shared lib/diffRendering.ts module and updated both DiffPanel.tsx and DiffWorkerPoolProvider.tsx to import from it.

Create PR

Or push these changes by commenting:

@cursor push 48f196d911
Preview (48f196d911)
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx--- a/apps/web/src/components/DiffPanel.tsx+++ b/apps/web/src/components/DiffPanel.tsx@@ -10,7 +10,7 @@
import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
import { isElectron } from "../env";
import { useTheme } from "../hooks/useTheme";
-import { buildPatchCacheKey } from "../lib/diffRendering";+import { buildPatchCacheKey, resolveDiffThemeName } from "../lib/diffRendering";
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
import { useStore } from "../store";
import { ToggleGroup, Toggle } from "./ui/toggle-group";
@@ -18,10 +18,6 @@
type DiffRenderMode = "stacked" | "split";
type DiffThemeType = "light" | "dark";
-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
type RenderablePatch =
| {
kind: "files";
diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx--- a/apps/web/src/components/DiffWorkerPoolProvider.tsx+++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx@@ -2,11 +2,8 @@
import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
import { useEffect, useMemo, type ReactNode } from "react";
import { useTheme } from "../hooks/useTheme";
+import { resolveDiffThemeName } from "../lib/diffRendering";-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
function DiffWorkerThemeSync({ themeName }: { themeName: "pierre-light" | "pierre-dark" }) {
const workerPool = useWorkerPool();
diff --git a/apps/web/src/lib/diffRendering.ts b/apps/web/src/lib/diffRendering.ts--- a/apps/web/src/lib/diffRendering.ts+++ b/apps/web/src/lib/diffRendering.ts@@ -12,6 +12,10 @@
return hash >>> 0;
}
+export function resolveDiffThemeName(theme: "light" | "dark") {+ return theme === "dark" ? "pierre-dark" : "pierre-light";+}+
export function buildPatchCacheKey(patch: string, scope = "diff-panel"): string {
const normalizedPatch = patch.trim();
const primary = fnv1a32(normalizedPatch, FNV_OFFSET_BASIS_32, FNV_PRIME_32).toString(36);


function resolveDiffThemeName(theme: "light" | "dark") {
return theme === "dark" ? "pierre-dark" : "pierre-light";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate helper function across two files

Low Severity

resolveDiffThemeName is identically defined in both DiffPanel.tsx and DiffWorkerPoolProvider.tsx. If the theme name mapping ever changes (e.g., new theme variants), both copies need to be updated in sync, which is easy to miss. Extracting it to a shared module avoids the maintenance risk.

Additional Locations (1)

Fix in CursorFix in Web

@juliusmarminge
juliusmarminge merged commit 3a43047 into mainFeb 27, 2026
5 checks passed
Sign up for freeto 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

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Sync diff renderer and worker theme with resolved app theme - #108

Merged
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35
Feb 27, 2026
Merged

Sync diff renderer and worker theme with resolved app theme#108
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

  • update diff panel rendering to use explicit Pierre theme names (pierre-light/pierre-dark) derived from the resolved app theme
  • include resolved theme in file diff React keys so diff views remount cleanly on theme switches
  • sync worker pool highlighter/render options with current theme via DiffWorkerThemeSync
  • improve useTheme snapshot stability by tracking both stored theme and system dark-mode state for consistent useSyncExternalStore updates

Testing

  • Not run (not provided in patch context)
  • Suggested check: toggle app theme between light, dark, and system and verify diff colors update immediately without stale highlighting
  • Suggested check: with system theme selected, change OS color scheme and verify diff panel/worker rendering follows the new resolved theme

Note

Medium Risk
Touches diff rendering and worker pool configuration; regressions could cause stale or incorrect syntax highlighting when toggling themes, but changes are UI-only and not security/data-critical.

Overview
Ensures diff rendering updates cleanly on theme changes by mapping the app’s resolved theme to explicit Pierre themes (pierre-light/pierre-dark) and passing them into FileDiff options.

Updates the diff worker pool to initialize and continuously sync highlighter/render options to the current theme via a new DiffWorkerThemeSync, and adjusts useTheme’s useSyncExternalStore snapshot to include cached system dark-mode state for more consistent updates (especially when theme=system).

Written by Cursor Bugbot for commit df323f8. This will update automatically on new commits. Configure here.

Note

Sync diff renderer and worker theme with resolved app theme in apps/web/src/components/DiffPanel.tsx and apps/web/src/components/DiffWorkerPoolProvider.tsx

Restrict DiffThemeType to "light" | "dark", add resolveDiffThemeName to map resolved themes to "pierre-light" or "pierre-dark", remount FileDiff items on theme change via keyed resolvedTheme, and propagate theme names to the worker pool render and highlighter options. Core changes are in DiffPanel.tsx, DiffWorkerPoolProvider.tsx, and useTheme.ts.

📍Where to Start

Start with resolveDiffThemeName and its usage in DiffPanel in DiffPanel.tsx, then review DiffWorkerPoolProvider and DiffWorkerThemeSync in DiffWorkerPoolProvider.tsx.

Macroscope summarized df323f8.

Summary by CodeRabbit

  • Changes

    • Removed "system" theme option from diff panel; now supports "light" and "dark" only.
  • Improvements

    • Improved theme synchronization for diff rendering to ensure consistency across the UI.
    • Enhanced performance of theme handling.

- Pass explicit Pierre light/dark theme into diff render options
- Re-key file diffs on resolved theme so views rerender on theme switch
- Stabilize `useTheme` snapshots to propagate system theme updates reliably
@coderabbitai

coderabbitaiBot commented Feb 27, 2026

Copy link
Copy Markdown

Walkthrough

These changes implement theme synchronization across the diff rendering system. DiffPanel removes the "system" theme option and uses a concrete theme mapping to "pierre-light" or "pierre-dark". DiffWorkerPoolProvider now synchronizes theme changes to the worker pool, and useTheme introduces snapshot-based caching for optimized theme resolution with system preference tracking.

Changes

Cohort / File(s)Summary
Diff Panel Theme Mapping
apps/web/src/components/DiffPanel.tsx
Removed "system" from DiffThemeType ("light" | "dark" only). Added resolveDiffThemeName helper to map theme values to "pierre-light" or "pierre-dark". Updated rendering to use themedFileKey and pass resolved theme to FileDiff component.
Worker Pool Theme Synchronization
apps/web/src/components/DiffWorkerPoolProvider.tsx
Integrated theme synchronization with worker pool. Added DiffWorkerThemeSync component to detect theme changes and update worker render options. Computes diffThemeName and passes it to highlighterOptions. Extended provider to sync theme changes dynamically.
Theme Hook Snapshot Caching
apps/web/src/hooks/useTheme.ts
Introduced ThemeSnapshot type containing theme and systemDark fields. Implemented module-level caching in getSnapshot to avoid recomputation when values unchanged. Updated getSnapshot to return ThemeSnapshot and useTheme to consume snapshots via useSyncExternalStore, eliminating redundant system preference lookups.

Sequence Diagram

sequenceDiagram
participant UI as React UI
participant useTheme as useTheme Hook
participant ExtStore as External Store
participant DiffProvider as DiffWorkerPoolProvider
participant Worker as Worker Pool
UI->>useTheme: Read current theme
useTheme->>ExtStore: getSnapshot() → ThemeSnapshot
ExtStore-->>useTheme: {theme, systemDark}
useTheme-->>UI: resolvedTheme (light/dark)
UI->>DiffProvider: Theme changed signal
DiffProvider->>useTheme: useTheme() → current theme
useTheme-->>DiffProvider: resolvedTheme
DiffProvider->>DiffProvider: Compute diffThemeName<br/>(light→pierre-light, etc.)
DiffProvider->>Worker: setRenderOptions({<br/>highlighterOptions: {theme}})
Worker-->>DiffProvider: Updated
DiffProvider->>UI: DiffPanel with themed<br/>FileDiff components
UI-->>UI: Render with active theme
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main objective of the pull request: synchronizing diff renderer and worker theme with the resolved app theme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/c44d2b35

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/web/src/components/DiffWorkerPoolProvider.tsx (1)

10-32: Consider logging errors instead of silently swallowing them.

The .catch(() => undefined) on line 28 silently discards any errors from setRenderOptions. While you may not want to crash the UI, logging the error would help with debugging theme sync issues.

♻️ Proposed improvement
 void workerPool
.setRenderOptions({
...current,
theme: themeName,
})
- .catch(() => undefined);+ .catch((err) => {+ console.error("[DiffWorkerThemeSync] Failed to sync theme:", err);+ });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx` around lines 10 - 32, In
DiffWorkerThemeSync, stop silently swallowing errors from
workerPool.setRenderOptions; replace the .catch(() => undefined) with a handler
that logs the error (e.g., console.error or your app logger) and includes
context (mention themeName and that it occurred in DiffWorkerThemeSync when
calling setRenderOptions on the workerPool returned by useWorkerPool); ensure
this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).
apps/web/src/components/DiffPanel.tsx (1)

19-23: Extract resolveDiffThemeName to a shared utility to eliminate duplication.

This function is duplicated in DiffWorkerPoolProvider.tsx (lines 6-8). Consider extracting it to a shared location, e.g., ~/lib/diffTheme.ts.

♻️ Proposed extraction

Create a new file apps/web/src/lib/diffTheme.ts:

exporttypeDiffThemeType="light"|"dark";exportfunctionresolveDiffThemeName(theme: DiffThemeType){returntheme==="dark" ? "pierre-dark" : "pierre-light";}

Then import it in both files:

-type DiffThemeType = "light" | "dark";--function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}+import { resolveDiffThemeName, type DiffThemeType } from "~/lib/diffTheme";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffPanel.tsx` around lines 19 - 23, Extract the
duplicated resolveDiffThemeName and DiffThemeType into a shared utility: create
a new module that exports `type DiffThemeType = "light" | "dark"` and `function
resolveDiffThemeName(theme: DiffThemeType)` (returning "pierre-dark" for "dark"
else "pierre-light"), then replace the local definitions in both `DiffPanel` and
`DiffWorkerPoolProvider` with imports of `DiffThemeType` and
`resolveDiffThemeName` from the new utility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/src/components/DiffPanel.tsx`:
- Around line 19-23: Extract the duplicated resolveDiffThemeName and
DiffThemeType into a shared utility: create a new module that exports `type
DiffThemeType = "light" | "dark"` and `function resolveDiffThemeName(theme:
DiffThemeType)` (returning "pierre-dark" for "dark" else "pierre-light"), then
replace the local definitions in both `DiffPanel` and `DiffWorkerPoolProvider`
with imports of `DiffThemeType` and `resolveDiffThemeName` from the new utility.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx`:
- Around line 10-32: In DiffWorkerThemeSync, stop silently swallowing errors
from workerPool.setRenderOptions; replace the .catch(() => undefined) with a
handler that logs the error (e.g., console.error or your app logger) and
includes context (mention themeName and that it occurred in DiffWorkerThemeSync
when calling setRenderOptions on the workerPool returned by useWorkerPool);
ensure this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 041acf1 and df323f8.

📒 Files selected for processing (3)
  • apps/web/src/components/DiffPanel.tsx
  • apps/web/src/components/DiffWorkerPoolProvider.tsx
  • apps/web/src/hooks/useTheme.ts

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicate helper function across two files
    • Extracted resolveDiffThemeName into the shared lib/diffRendering.ts module and updated both DiffPanel.tsx and DiffWorkerPoolProvider.tsx to import from it.

Create PR

Or push these changes by commenting:

@cursor push 48f196d911
Preview (48f196d911)
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx--- a/apps/web/src/components/DiffPanel.tsx+++ b/apps/web/src/components/DiffPanel.tsx@@ -10,7 +10,7 @@
import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
import { isElectron } from "../env";
import { useTheme } from "../hooks/useTheme";
-import { buildPatchCacheKey } from "../lib/diffRendering";+import { buildPatchCacheKey, resolveDiffThemeName } from "../lib/diffRendering";
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
import { useStore } from "../store";
import { ToggleGroup, Toggle } from "./ui/toggle-group";
@@ -18,10 +18,6 @@
type DiffRenderMode = "stacked" | "split";
type DiffThemeType = "light" | "dark";
-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
type RenderablePatch =
| {
kind: "files";
diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx--- a/apps/web/src/components/DiffWorkerPoolProvider.tsx+++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx@@ -2,11 +2,8 @@
import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
import { useEffect, useMemo, type ReactNode } from "react";
import { useTheme } from "../hooks/useTheme";
+import { resolveDiffThemeName } from "../lib/diffRendering";-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
function DiffWorkerThemeSync({ themeName }: { themeName: "pierre-light" | "pierre-dark" }) {
const workerPool = useWorkerPool();
diff --git a/apps/web/src/lib/diffRendering.ts b/apps/web/src/lib/diffRendering.ts--- a/apps/web/src/lib/diffRendering.ts+++ b/apps/web/src/lib/diffRendering.ts@@ -12,6 +12,10 @@
return hash >>> 0;
}
+export function resolveDiffThemeName(theme: "light" | "dark") {+ return theme === "dark" ? "pierre-dark" : "pierre-light";+}+
export function buildPatchCacheKey(patch: string, scope = "diff-panel"): string {
const normalizedPatch = patch.trim();
const primary = fnv1a32(normalizedPatch, FNV_OFFSET_BASIS_32, FNV_PRIME_32).toString(36);


function resolveDiffThemeName(theme: "light" | "dark") {
return theme === "dark" ? "pierre-dark" : "pierre-light";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate helper function across two files

Low Severity

resolveDiffThemeName is identically defined in both DiffPanel.tsx and DiffWorkerPoolProvider.tsx. If the theme name mapping ever changes (e.g., new theme variants), both copies need to be updated in sync, which is easy to miss. Extracting it to a shared module avoids the maintenance risk.

Additional Locations (1)

Fix in CursorFix in Web

@juliusmarminge
juliusmarminge merged commit 3a43047 into mainFeb 27, 2026
5 checks passed
Sign up for freeto 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

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Sync diff renderer and worker theme with resolved app theme - #108

Merged
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35
Feb 27, 2026
Merged

Sync diff renderer and worker theme with resolved app theme#108
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

  • update diff panel rendering to use explicit Pierre theme names (pierre-light/pierre-dark) derived from the resolved app theme
  • include resolved theme in file diff React keys so diff views remount cleanly on theme switches
  • sync worker pool highlighter/render options with current theme via DiffWorkerThemeSync
  • improve useTheme snapshot stability by tracking both stored theme and system dark-mode state for consistent useSyncExternalStore updates

Testing

  • Not run (not provided in patch context)
  • Suggested check: toggle app theme between light, dark, and system and verify diff colors update immediately without stale highlighting
  • Suggested check: with system theme selected, change OS color scheme and verify diff panel/worker rendering follows the new resolved theme

Note

Medium Risk
Touches diff rendering and worker pool configuration; regressions could cause stale or incorrect syntax highlighting when toggling themes, but changes are UI-only and not security/data-critical.

Overview
Ensures diff rendering updates cleanly on theme changes by mapping the app’s resolved theme to explicit Pierre themes (pierre-light/pierre-dark) and passing them into FileDiff options.

Updates the diff worker pool to initialize and continuously sync highlighter/render options to the current theme via a new DiffWorkerThemeSync, and adjusts useTheme’s useSyncExternalStore snapshot to include cached system dark-mode state for more consistent updates (especially when theme=system).

Written by Cursor Bugbot for commit df323f8. This will update automatically on new commits. Configure here.

Note

Sync diff renderer and worker theme with resolved app theme in apps/web/src/components/DiffPanel.tsx and apps/web/src/components/DiffWorkerPoolProvider.tsx

Restrict DiffThemeType to "light" | "dark", add resolveDiffThemeName to map resolved themes to "pierre-light" or "pierre-dark", remount FileDiff items on theme change via keyed resolvedTheme, and propagate theme names to the worker pool render and highlighter options. Core changes are in DiffPanel.tsx, DiffWorkerPoolProvider.tsx, and useTheme.ts.

📍Where to Start

Start with resolveDiffThemeName and its usage in DiffPanel in DiffPanel.tsx, then review DiffWorkerPoolProvider and DiffWorkerThemeSync in DiffWorkerPoolProvider.tsx.

Macroscope summarized df323f8.

Summary by CodeRabbit

  • Changes

    • Removed "system" theme option from diff panel; now supports "light" and "dark" only.
  • Improvements

    • Improved theme synchronization for diff rendering to ensure consistency across the UI.
    • Enhanced performance of theme handling.

- Pass explicit Pierre light/dark theme into diff render options
- Re-key file diffs on resolved theme so views rerender on theme switch
- Stabilize `useTheme` snapshots to propagate system theme updates reliably
@coderabbitai

coderabbitaiBot commented Feb 27, 2026

Copy link
Copy Markdown

Walkthrough

These changes implement theme synchronization across the diff rendering system. DiffPanel removes the "system" theme option and uses a concrete theme mapping to "pierre-light" or "pierre-dark". DiffWorkerPoolProvider now synchronizes theme changes to the worker pool, and useTheme introduces snapshot-based caching for optimized theme resolution with system preference tracking.

Changes

Cohort / File(s)Summary
Diff Panel Theme Mapping
apps/web/src/components/DiffPanel.tsx
Removed "system" from DiffThemeType ("light" | "dark" only). Added resolveDiffThemeName helper to map theme values to "pierre-light" or "pierre-dark". Updated rendering to use themedFileKey and pass resolved theme to FileDiff component.
Worker Pool Theme Synchronization
apps/web/src/components/DiffWorkerPoolProvider.tsx
Integrated theme synchronization with worker pool. Added DiffWorkerThemeSync component to detect theme changes and update worker render options. Computes diffThemeName and passes it to highlighterOptions. Extended provider to sync theme changes dynamically.
Theme Hook Snapshot Caching
apps/web/src/hooks/useTheme.ts
Introduced ThemeSnapshot type containing theme and systemDark fields. Implemented module-level caching in getSnapshot to avoid recomputation when values unchanged. Updated getSnapshot to return ThemeSnapshot and useTheme to consume snapshots via useSyncExternalStore, eliminating redundant system preference lookups.

Sequence Diagram

sequenceDiagram
participant UI as React UI
participant useTheme as useTheme Hook
participant ExtStore as External Store
participant DiffProvider as DiffWorkerPoolProvider
participant Worker as Worker Pool
UI->>useTheme: Read current theme
useTheme->>ExtStore: getSnapshot() → ThemeSnapshot
ExtStore-->>useTheme: {theme, systemDark}
useTheme-->>UI: resolvedTheme (light/dark)
UI->>DiffProvider: Theme changed signal
DiffProvider->>useTheme: useTheme() → current theme
useTheme-->>DiffProvider: resolvedTheme
DiffProvider->>DiffProvider: Compute diffThemeName<br/>(light→pierre-light, etc.)
DiffProvider->>Worker: setRenderOptions({<br/>highlighterOptions: {theme}})
Worker-->>DiffProvider: Updated
DiffProvider->>UI: DiffPanel with themed<br/>FileDiff components
UI-->>UI: Render with active theme
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main objective of the pull request: synchronizing diff renderer and worker theme with the resolved app theme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/c44d2b35

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/web/src/components/DiffWorkerPoolProvider.tsx (1)

10-32: Consider logging errors instead of silently swallowing them.

The .catch(() => undefined) on line 28 silently discards any errors from setRenderOptions. While you may not want to crash the UI, logging the error would help with debugging theme sync issues.

♻️ Proposed improvement
 void workerPool
.setRenderOptions({
...current,
theme: themeName,
})
- .catch(() => undefined);+ .catch((err) => {+ console.error("[DiffWorkerThemeSync] Failed to sync theme:", err);+ });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx` around lines 10 - 32, In
DiffWorkerThemeSync, stop silently swallowing errors from
workerPool.setRenderOptions; replace the .catch(() => undefined) with a handler
that logs the error (e.g., console.error or your app logger) and includes
context (mention themeName and that it occurred in DiffWorkerThemeSync when
calling setRenderOptions on the workerPool returned by useWorkerPool); ensure
this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).
apps/web/src/components/DiffPanel.tsx (1)

19-23: Extract resolveDiffThemeName to a shared utility to eliminate duplication.

This function is duplicated in DiffWorkerPoolProvider.tsx (lines 6-8). Consider extracting it to a shared location, e.g., ~/lib/diffTheme.ts.

♻️ Proposed extraction

Create a new file apps/web/src/lib/diffTheme.ts:

exporttypeDiffThemeType="light"|"dark";exportfunctionresolveDiffThemeName(theme: DiffThemeType){returntheme==="dark" ? "pierre-dark" : "pierre-light";}

Then import it in both files:

-type DiffThemeType = "light" | "dark";--function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}+import { resolveDiffThemeName, type DiffThemeType } from "~/lib/diffTheme";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffPanel.tsx` around lines 19 - 23, Extract the
duplicated resolveDiffThemeName and DiffThemeType into a shared utility: create
a new module that exports `type DiffThemeType = "light" | "dark"` and `function
resolveDiffThemeName(theme: DiffThemeType)` (returning "pierre-dark" for "dark"
else "pierre-light"), then replace the local definitions in both `DiffPanel` and
`DiffWorkerPoolProvider` with imports of `DiffThemeType` and
`resolveDiffThemeName` from the new utility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/src/components/DiffPanel.tsx`:
- Around line 19-23: Extract the duplicated resolveDiffThemeName and
DiffThemeType into a shared utility: create a new module that exports `type
DiffThemeType = "light" | "dark"` and `function resolveDiffThemeName(theme:
DiffThemeType)` (returning "pierre-dark" for "dark" else "pierre-light"), then
replace the local definitions in both `DiffPanel` and `DiffWorkerPoolProvider`
with imports of `DiffThemeType` and `resolveDiffThemeName` from the new utility.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx`:
- Around line 10-32: In DiffWorkerThemeSync, stop silently swallowing errors
from workerPool.setRenderOptions; replace the .catch(() => undefined) with a
handler that logs the error (e.g., console.error or your app logger) and
includes context (mention themeName and that it occurred in DiffWorkerThemeSync
when calling setRenderOptions on the workerPool returned by useWorkerPool);
ensure this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 041acf1 and df323f8.

📒 Files selected for processing (3)
  • apps/web/src/components/DiffPanel.tsx
  • apps/web/src/components/DiffWorkerPoolProvider.tsx
  • apps/web/src/hooks/useTheme.ts

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicate helper function across two files
    • Extracted resolveDiffThemeName into the shared lib/diffRendering.ts module and updated both DiffPanel.tsx and DiffWorkerPoolProvider.tsx to import from it.

Create PR

Or push these changes by commenting:

@cursor push 48f196d911
Preview (48f196d911)
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx--- a/apps/web/src/components/DiffPanel.tsx+++ b/apps/web/src/components/DiffPanel.tsx@@ -10,7 +10,7 @@
import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
import { isElectron } from "../env";
import { useTheme } from "../hooks/useTheme";
-import { buildPatchCacheKey } from "../lib/diffRendering";+import { buildPatchCacheKey, resolveDiffThemeName } from "../lib/diffRendering";
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
import { useStore } from "../store";
import { ToggleGroup, Toggle } from "./ui/toggle-group";
@@ -18,10 +18,6 @@
type DiffRenderMode = "stacked" | "split";
type DiffThemeType = "light" | "dark";
-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
type RenderablePatch =
| {
kind: "files";
diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx--- a/apps/web/src/components/DiffWorkerPoolProvider.tsx+++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx@@ -2,11 +2,8 @@
import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
import { useEffect, useMemo, type ReactNode } from "react";
import { useTheme } from "../hooks/useTheme";
+import { resolveDiffThemeName } from "../lib/diffRendering";-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
function DiffWorkerThemeSync({ themeName }: { themeName: "pierre-light" | "pierre-dark" }) {
const workerPool = useWorkerPool();
diff --git a/apps/web/src/lib/diffRendering.ts b/apps/web/src/lib/diffRendering.ts--- a/apps/web/src/lib/diffRendering.ts+++ b/apps/web/src/lib/diffRendering.ts@@ -12,6 +12,10 @@
return hash >>> 0;
}
+export function resolveDiffThemeName(theme: "light" | "dark") {+ return theme === "dark" ? "pierre-dark" : "pierre-light";+}+
export function buildPatchCacheKey(patch: string, scope = "diff-panel"): string {
const normalizedPatch = patch.trim();
const primary = fnv1a32(normalizedPatch, FNV_OFFSET_BASIS_32, FNV_PRIME_32).toString(36);


function resolveDiffThemeName(theme: "light" | "dark") {
return theme === "dark" ? "pierre-dark" : "pierre-light";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate helper function across two files

Low Severity

resolveDiffThemeName is identically defined in both DiffPanel.tsx and DiffWorkerPoolProvider.tsx. If the theme name mapping ever changes (e.g., new theme variants), both copies need to be updated in sync, which is easy to miss. Extracting it to a shared module avoids the maintenance risk.

Additional Locations (1)

Fix in CursorFix in Web

@juliusmarminge
juliusmarminge merged commit 3a43047 into mainFeb 27, 2026
5 checks passed
Sign up for freeto 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

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Sync diff renderer and worker theme with resolved app theme - #108

Merged
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35
Feb 27, 2026
Merged

Sync diff renderer and worker theme with resolved app theme#108
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

  • update diff panel rendering to use explicit Pierre theme names (pierre-light/pierre-dark) derived from the resolved app theme
  • include resolved theme in file diff React keys so diff views remount cleanly on theme switches
  • sync worker pool highlighter/render options with current theme via DiffWorkerThemeSync
  • improve useTheme snapshot stability by tracking both stored theme and system dark-mode state for consistent useSyncExternalStore updates

Testing

  • Not run (not provided in patch context)
  • Suggested check: toggle app theme between light, dark, and system and verify diff colors update immediately without stale highlighting
  • Suggested check: with system theme selected, change OS color scheme and verify diff panel/worker rendering follows the new resolved theme

Note

Medium Risk
Touches diff rendering and worker pool configuration; regressions could cause stale or incorrect syntax highlighting when toggling themes, but changes are UI-only and not security/data-critical.

Overview
Ensures diff rendering updates cleanly on theme changes by mapping the app’s resolved theme to explicit Pierre themes (pierre-light/pierre-dark) and passing them into FileDiff options.

Updates the diff worker pool to initialize and continuously sync highlighter/render options to the current theme via a new DiffWorkerThemeSync, and adjusts useTheme’s useSyncExternalStore snapshot to include cached system dark-mode state for more consistent updates (especially when theme=system).

Written by Cursor Bugbot for commit df323f8. This will update automatically on new commits. Configure here.

Note

Sync diff renderer and worker theme with resolved app theme in apps/web/src/components/DiffPanel.tsx and apps/web/src/components/DiffWorkerPoolProvider.tsx

Restrict DiffThemeType to "light" | "dark", add resolveDiffThemeName to map resolved themes to "pierre-light" or "pierre-dark", remount FileDiff items on theme change via keyed resolvedTheme, and propagate theme names to the worker pool render and highlighter options. Core changes are in DiffPanel.tsx, DiffWorkerPoolProvider.tsx, and useTheme.ts.

📍Where to Start

Start with resolveDiffThemeName and its usage in DiffPanel in DiffPanel.tsx, then review DiffWorkerPoolProvider and DiffWorkerThemeSync in DiffWorkerPoolProvider.tsx.

Macroscope summarized df323f8.

Summary by CodeRabbit

  • Changes

    • Removed "system" theme option from diff panel; now supports "light" and "dark" only.
  • Improvements

    • Improved theme synchronization for diff rendering to ensure consistency across the UI.
    • Enhanced performance of theme handling.

- Pass explicit Pierre light/dark theme into diff render options
- Re-key file diffs on resolved theme so views rerender on theme switch
- Stabilize `useTheme` snapshots to propagate system theme updates reliably
@coderabbitai

coderabbitaiBot commented Feb 27, 2026

Copy link
Copy Markdown

Walkthrough

These changes implement theme synchronization across the diff rendering system. DiffPanel removes the "system" theme option and uses a concrete theme mapping to "pierre-light" or "pierre-dark". DiffWorkerPoolProvider now synchronizes theme changes to the worker pool, and useTheme introduces snapshot-based caching for optimized theme resolution with system preference tracking.

Changes

Cohort / File(s)Summary
Diff Panel Theme Mapping
apps/web/src/components/DiffPanel.tsx
Removed "system" from DiffThemeType ("light" | "dark" only). Added resolveDiffThemeName helper to map theme values to "pierre-light" or "pierre-dark". Updated rendering to use themedFileKey and pass resolved theme to FileDiff component.
Worker Pool Theme Synchronization
apps/web/src/components/DiffWorkerPoolProvider.tsx
Integrated theme synchronization with worker pool. Added DiffWorkerThemeSync component to detect theme changes and update worker render options. Computes diffThemeName and passes it to highlighterOptions. Extended provider to sync theme changes dynamically.
Theme Hook Snapshot Caching
apps/web/src/hooks/useTheme.ts
Introduced ThemeSnapshot type containing theme and systemDark fields. Implemented module-level caching in getSnapshot to avoid recomputation when values unchanged. Updated getSnapshot to return ThemeSnapshot and useTheme to consume snapshots via useSyncExternalStore, eliminating redundant system preference lookups.

Sequence Diagram

sequenceDiagram
participant UI as React UI
participant useTheme as useTheme Hook
participant ExtStore as External Store
participant DiffProvider as DiffWorkerPoolProvider
participant Worker as Worker Pool
UI->>useTheme: Read current theme
useTheme->>ExtStore: getSnapshot() → ThemeSnapshot
ExtStore-->>useTheme: {theme, systemDark}
useTheme-->>UI: resolvedTheme (light/dark)
UI->>DiffProvider: Theme changed signal
DiffProvider->>useTheme: useTheme() → current theme
useTheme-->>DiffProvider: resolvedTheme
DiffProvider->>DiffProvider: Compute diffThemeName<br/>(light→pierre-light, etc.)
DiffProvider->>Worker: setRenderOptions({<br/>highlighterOptions: {theme}})
Worker-->>DiffProvider: Updated
DiffProvider->>UI: DiffPanel with themed<br/>FileDiff components
UI-->>UI: Render with active theme
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main objective of the pull request: synchronizing diff renderer and worker theme with the resolved app theme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/c44d2b35

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/web/src/components/DiffWorkerPoolProvider.tsx (1)

10-32: Consider logging errors instead of silently swallowing them.

The .catch(() => undefined) on line 28 silently discards any errors from setRenderOptions. While you may not want to crash the UI, logging the error would help with debugging theme sync issues.

♻️ Proposed improvement
 void workerPool
.setRenderOptions({
...current,
theme: themeName,
})
- .catch(() => undefined);+ .catch((err) => {+ console.error("[DiffWorkerThemeSync] Failed to sync theme:", err);+ });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx` around lines 10 - 32, In
DiffWorkerThemeSync, stop silently swallowing errors from
workerPool.setRenderOptions; replace the .catch(() => undefined) with a handler
that logs the error (e.g., console.error or your app logger) and includes
context (mention themeName and that it occurred in DiffWorkerThemeSync when
calling setRenderOptions on the workerPool returned by useWorkerPool); ensure
this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).
apps/web/src/components/DiffPanel.tsx (1)

19-23: Extract resolveDiffThemeName to a shared utility to eliminate duplication.

This function is duplicated in DiffWorkerPoolProvider.tsx (lines 6-8). Consider extracting it to a shared location, e.g., ~/lib/diffTheme.ts.

♻️ Proposed extraction

Create a new file apps/web/src/lib/diffTheme.ts:

exporttypeDiffThemeType="light"|"dark";exportfunctionresolveDiffThemeName(theme: DiffThemeType){returntheme==="dark" ? "pierre-dark" : "pierre-light";}

Then import it in both files:

-type DiffThemeType = "light" | "dark";--function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}+import { resolveDiffThemeName, type DiffThemeType } from "~/lib/diffTheme";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffPanel.tsx` around lines 19 - 23, Extract the
duplicated resolveDiffThemeName and DiffThemeType into a shared utility: create
a new module that exports `type DiffThemeType = "light" | "dark"` and `function
resolveDiffThemeName(theme: DiffThemeType)` (returning "pierre-dark" for "dark"
else "pierre-light"), then replace the local definitions in both `DiffPanel` and
`DiffWorkerPoolProvider` with imports of `DiffThemeType` and
`resolveDiffThemeName` from the new utility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/src/components/DiffPanel.tsx`:
- Around line 19-23: Extract the duplicated resolveDiffThemeName and
DiffThemeType into a shared utility: create a new module that exports `type
DiffThemeType = "light" | "dark"` and `function resolveDiffThemeName(theme:
DiffThemeType)` (returning "pierre-dark" for "dark" else "pierre-light"), then
replace the local definitions in both `DiffPanel` and `DiffWorkerPoolProvider`
with imports of `DiffThemeType` and `resolveDiffThemeName` from the new utility.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx`:
- Around line 10-32: In DiffWorkerThemeSync, stop silently swallowing errors
from workerPool.setRenderOptions; replace the .catch(() => undefined) with a
handler that logs the error (e.g., console.error or your app logger) and
includes context (mention themeName and that it occurred in DiffWorkerThemeSync
when calling setRenderOptions on the workerPool returned by useWorkerPool);
ensure this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 041acf1 and df323f8.

📒 Files selected for processing (3)
  • apps/web/src/components/DiffPanel.tsx
  • apps/web/src/components/DiffWorkerPoolProvider.tsx
  • apps/web/src/hooks/useTheme.ts

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicate helper function across two files
    • Extracted resolveDiffThemeName into the shared lib/diffRendering.ts module and updated both DiffPanel.tsx and DiffWorkerPoolProvider.tsx to import from it.

Create PR

Or push these changes by commenting:

@cursor push 48f196d911
Preview (48f196d911)
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx--- a/apps/web/src/components/DiffPanel.tsx+++ b/apps/web/src/components/DiffPanel.tsx@@ -10,7 +10,7 @@
import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
import { isElectron } from "../env";
import { useTheme } from "../hooks/useTheme";
-import { buildPatchCacheKey } from "../lib/diffRendering";+import { buildPatchCacheKey, resolveDiffThemeName } from "../lib/diffRendering";
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
import { useStore } from "../store";
import { ToggleGroup, Toggle } from "./ui/toggle-group";
@@ -18,10 +18,6 @@
type DiffRenderMode = "stacked" | "split";
type DiffThemeType = "light" | "dark";
-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
type RenderablePatch =
| {
kind: "files";
diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx--- a/apps/web/src/components/DiffWorkerPoolProvider.tsx+++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx@@ -2,11 +2,8 @@
import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
import { useEffect, useMemo, type ReactNode } from "react";
import { useTheme } from "../hooks/useTheme";
+import { resolveDiffThemeName } from "../lib/diffRendering";-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
function DiffWorkerThemeSync({ themeName }: { themeName: "pierre-light" | "pierre-dark" }) {
const workerPool = useWorkerPool();
diff --git a/apps/web/src/lib/diffRendering.ts b/apps/web/src/lib/diffRendering.ts--- a/apps/web/src/lib/diffRendering.ts+++ b/apps/web/src/lib/diffRendering.ts@@ -12,6 +12,10 @@
return hash >>> 0;
}
+export function resolveDiffThemeName(theme: "light" | "dark") {+ return theme === "dark" ? "pierre-dark" : "pierre-light";+}+
export function buildPatchCacheKey(patch: string, scope = "diff-panel"): string {
const normalizedPatch = patch.trim();
const primary = fnv1a32(normalizedPatch, FNV_OFFSET_BASIS_32, FNV_PRIME_32).toString(36);


function resolveDiffThemeName(theme: "light" | "dark") {
return theme === "dark" ? "pierre-dark" : "pierre-light";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate helper function across two files

Low Severity

resolveDiffThemeName is identically defined in both DiffPanel.tsx and DiffWorkerPoolProvider.tsx. If the theme name mapping ever changes (e.g., new theme variants), both copies need to be updated in sync, which is easy to miss. Extracting it to a shared module avoids the maintenance risk.

Additional Locations (1)

Fix in CursorFix in Web

@juliusmarminge
juliusmarminge merged commit 3a43047 into mainFeb 27, 2026
5 checks passed
Sign up for freeto 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

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Sync diff renderer and worker theme with resolved app theme - #108

Merged
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35
Feb 27, 2026
Merged

Sync diff renderer and worker theme with resolved app theme#108
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

  • update diff panel rendering to use explicit Pierre theme names (pierre-light/pierre-dark) derived from the resolved app theme
  • include resolved theme in file diff React keys so diff views remount cleanly on theme switches
  • sync worker pool highlighter/render options with current theme via DiffWorkerThemeSync
  • improve useTheme snapshot stability by tracking both stored theme and system dark-mode state for consistent useSyncExternalStore updates

Testing

  • Not run (not provided in patch context)
  • Suggested check: toggle app theme between light, dark, and system and verify diff colors update immediately without stale highlighting
  • Suggested check: with system theme selected, change OS color scheme and verify diff panel/worker rendering follows the new resolved theme

Note

Medium Risk
Touches diff rendering and worker pool configuration; regressions could cause stale or incorrect syntax highlighting when toggling themes, but changes are UI-only and not security/data-critical.

Overview
Ensures diff rendering updates cleanly on theme changes by mapping the app’s resolved theme to explicit Pierre themes (pierre-light/pierre-dark) and passing them into FileDiff options.

Updates the diff worker pool to initialize and continuously sync highlighter/render options to the current theme via a new DiffWorkerThemeSync, and adjusts useTheme’s useSyncExternalStore snapshot to include cached system dark-mode state for more consistent updates (especially when theme=system).

Written by Cursor Bugbot for commit df323f8. This will update automatically on new commits. Configure here.

Note

Sync diff renderer and worker theme with resolved app theme in apps/web/src/components/DiffPanel.tsx and apps/web/src/components/DiffWorkerPoolProvider.tsx

Restrict DiffThemeType to "light" | "dark", add resolveDiffThemeName to map resolved themes to "pierre-light" or "pierre-dark", remount FileDiff items on theme change via keyed resolvedTheme, and propagate theme names to the worker pool render and highlighter options. Core changes are in DiffPanel.tsx, DiffWorkerPoolProvider.tsx, and useTheme.ts.

📍Where to Start

Start with resolveDiffThemeName and its usage in DiffPanel in DiffPanel.tsx, then review DiffWorkerPoolProvider and DiffWorkerThemeSync in DiffWorkerPoolProvider.tsx.

Macroscope summarized df323f8.

Summary by CodeRabbit

  • Changes

    • Removed "system" theme option from diff panel; now supports "light" and "dark" only.
  • Improvements

    • Improved theme synchronization for diff rendering to ensure consistency across the UI.
    • Enhanced performance of theme handling.

- Pass explicit Pierre light/dark theme into diff render options
- Re-key file diffs on resolved theme so views rerender on theme switch
- Stabilize `useTheme` snapshots to propagate system theme updates reliably
@coderabbitai

coderabbitaiBot commented Feb 27, 2026

Copy link
Copy Markdown

Walkthrough

These changes implement theme synchronization across the diff rendering system. DiffPanel removes the "system" theme option and uses a concrete theme mapping to "pierre-light" or "pierre-dark". DiffWorkerPoolProvider now synchronizes theme changes to the worker pool, and useTheme introduces snapshot-based caching for optimized theme resolution with system preference tracking.

Changes

Cohort / File(s)Summary
Diff Panel Theme Mapping
apps/web/src/components/DiffPanel.tsx
Removed "system" from DiffThemeType ("light" | "dark" only). Added resolveDiffThemeName helper to map theme values to "pierre-light" or "pierre-dark". Updated rendering to use themedFileKey and pass resolved theme to FileDiff component.
Worker Pool Theme Synchronization
apps/web/src/components/DiffWorkerPoolProvider.tsx
Integrated theme synchronization with worker pool. Added DiffWorkerThemeSync component to detect theme changes and update worker render options. Computes diffThemeName and passes it to highlighterOptions. Extended provider to sync theme changes dynamically.
Theme Hook Snapshot Caching
apps/web/src/hooks/useTheme.ts
Introduced ThemeSnapshot type containing theme and systemDark fields. Implemented module-level caching in getSnapshot to avoid recomputation when values unchanged. Updated getSnapshot to return ThemeSnapshot and useTheme to consume snapshots via useSyncExternalStore, eliminating redundant system preference lookups.

Sequence Diagram

sequenceDiagram
participant UI as React UI
participant useTheme as useTheme Hook
participant ExtStore as External Store
participant DiffProvider as DiffWorkerPoolProvider
participant Worker as Worker Pool
UI->>useTheme: Read current theme
useTheme->>ExtStore: getSnapshot() → ThemeSnapshot
ExtStore-->>useTheme: {theme, systemDark}
useTheme-->>UI: resolvedTheme (light/dark)
UI->>DiffProvider: Theme changed signal
DiffProvider->>useTheme: useTheme() → current theme
useTheme-->>DiffProvider: resolvedTheme
DiffProvider->>DiffProvider: Compute diffThemeName<br/>(light→pierre-light, etc.)
DiffProvider->>Worker: setRenderOptions({<br/>highlighterOptions: {theme}})
Worker-->>DiffProvider: Updated
DiffProvider->>UI: DiffPanel with themed<br/>FileDiff components
UI-->>UI: Render with active theme
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main objective of the pull request: synchronizing diff renderer and worker theme with the resolved app theme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/c44d2b35

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/web/src/components/DiffWorkerPoolProvider.tsx (1)

10-32: Consider logging errors instead of silently swallowing them.

The .catch(() => undefined) on line 28 silently discards any errors from setRenderOptions. While you may not want to crash the UI, logging the error would help with debugging theme sync issues.

♻️ Proposed improvement
 void workerPool
.setRenderOptions({
...current,
theme: themeName,
})
- .catch(() => undefined);+ .catch((err) => {+ console.error("[DiffWorkerThemeSync] Failed to sync theme:", err);+ });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx` around lines 10 - 32, In
DiffWorkerThemeSync, stop silently swallowing errors from
workerPool.setRenderOptions; replace the .catch(() => undefined) with a handler
that logs the error (e.g., console.error or your app logger) and includes
context (mention themeName and that it occurred in DiffWorkerThemeSync when
calling setRenderOptions on the workerPool returned by useWorkerPool); ensure
this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).
apps/web/src/components/DiffPanel.tsx (1)

19-23: Extract resolveDiffThemeName to a shared utility to eliminate duplication.

This function is duplicated in DiffWorkerPoolProvider.tsx (lines 6-8). Consider extracting it to a shared location, e.g., ~/lib/diffTheme.ts.

♻️ Proposed extraction

Create a new file apps/web/src/lib/diffTheme.ts:

exporttypeDiffThemeType="light"|"dark";exportfunctionresolveDiffThemeName(theme: DiffThemeType){returntheme==="dark" ? "pierre-dark" : "pierre-light";}

Then import it in both files:

-type DiffThemeType = "light" | "dark";--function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}+import { resolveDiffThemeName, type DiffThemeType } from "~/lib/diffTheme";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffPanel.tsx` around lines 19 - 23, Extract the
duplicated resolveDiffThemeName and DiffThemeType into a shared utility: create
a new module that exports `type DiffThemeType = "light" | "dark"` and `function
resolveDiffThemeName(theme: DiffThemeType)` (returning "pierre-dark" for "dark"
else "pierre-light"), then replace the local definitions in both `DiffPanel` and
`DiffWorkerPoolProvider` with imports of `DiffThemeType` and
`resolveDiffThemeName` from the new utility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/src/components/DiffPanel.tsx`:
- Around line 19-23: Extract the duplicated resolveDiffThemeName and
DiffThemeType into a shared utility: create a new module that exports `type
DiffThemeType = "light" | "dark"` and `function resolveDiffThemeName(theme:
DiffThemeType)` (returning "pierre-dark" for "dark" else "pierre-light"), then
replace the local definitions in both `DiffPanel` and `DiffWorkerPoolProvider`
with imports of `DiffThemeType` and `resolveDiffThemeName` from the new utility.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx`:
- Around line 10-32: In DiffWorkerThemeSync, stop silently swallowing errors
from workerPool.setRenderOptions; replace the .catch(() => undefined) with a
handler that logs the error (e.g., console.error or your app logger) and
includes context (mention themeName and that it occurred in DiffWorkerThemeSync
when calling setRenderOptions on the workerPool returned by useWorkerPool);
ensure this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 041acf1 and df323f8.

📒 Files selected for processing (3)
  • apps/web/src/components/DiffPanel.tsx
  • apps/web/src/components/DiffWorkerPoolProvider.tsx
  • apps/web/src/hooks/useTheme.ts

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicate helper function across two files
    • Extracted resolveDiffThemeName into the shared lib/diffRendering.ts module and updated both DiffPanel.tsx and DiffWorkerPoolProvider.tsx to import from it.

Create PR

Or push these changes by commenting:

@cursor push 48f196d911
Preview (48f196d911)
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx--- a/apps/web/src/components/DiffPanel.tsx+++ b/apps/web/src/components/DiffPanel.tsx@@ -10,7 +10,7 @@
import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
import { isElectron } from "../env";
import { useTheme } from "../hooks/useTheme";
-import { buildPatchCacheKey } from "../lib/diffRendering";+import { buildPatchCacheKey, resolveDiffThemeName } from "../lib/diffRendering";
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
import { useStore } from "../store";
import { ToggleGroup, Toggle } from "./ui/toggle-group";
@@ -18,10 +18,6 @@
type DiffRenderMode = "stacked" | "split";
type DiffThemeType = "light" | "dark";
-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
type RenderablePatch =
| {
kind: "files";
diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx--- a/apps/web/src/components/DiffWorkerPoolProvider.tsx+++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx@@ -2,11 +2,8 @@
import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
import { useEffect, useMemo, type ReactNode } from "react";
import { useTheme } from "../hooks/useTheme";
+import { resolveDiffThemeName } from "../lib/diffRendering";-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
function DiffWorkerThemeSync({ themeName }: { themeName: "pierre-light" | "pierre-dark" }) {
const workerPool = useWorkerPool();
diff --git a/apps/web/src/lib/diffRendering.ts b/apps/web/src/lib/diffRendering.ts--- a/apps/web/src/lib/diffRendering.ts+++ b/apps/web/src/lib/diffRendering.ts@@ -12,6 +12,10 @@
return hash >>> 0;
}
+export function resolveDiffThemeName(theme: "light" | "dark") {+ return theme === "dark" ? "pierre-dark" : "pierre-light";+}+
export function buildPatchCacheKey(patch: string, scope = "diff-panel"): string {
const normalizedPatch = patch.trim();
const primary = fnv1a32(normalizedPatch, FNV_OFFSET_BASIS_32, FNV_PRIME_32).toString(36);


function resolveDiffThemeName(theme: "light" | "dark") {
return theme === "dark" ? "pierre-dark" : "pierre-light";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate helper function across two files

Low Severity

resolveDiffThemeName is identically defined in both DiffPanel.tsx and DiffWorkerPoolProvider.tsx. If the theme name mapping ever changes (e.g., new theme variants), both copies need to be updated in sync, which is easy to miss. Extracting it to a shared module avoids the maintenance risk.

Additional Locations (1)

Fix in CursorFix in Web

@juliusmarminge
juliusmarminge merged commit 3a43047 into mainFeb 27, 2026
5 checks passed
Sign up for freeto 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

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Sync diff renderer and worker theme with resolved app theme - #108

Merged
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35
Feb 27, 2026
Merged

Sync diff renderer and worker theme with resolved app theme#108
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

  • update diff panel rendering to use explicit Pierre theme names (pierre-light/pierre-dark) derived from the resolved app theme
  • include resolved theme in file diff React keys so diff views remount cleanly on theme switches
  • sync worker pool highlighter/render options with current theme via DiffWorkerThemeSync
  • improve useTheme snapshot stability by tracking both stored theme and system dark-mode state for consistent useSyncExternalStore updates

Testing

  • Not run (not provided in patch context)
  • Suggested check: toggle app theme between light, dark, and system and verify diff colors update immediately without stale highlighting
  • Suggested check: with system theme selected, change OS color scheme and verify diff panel/worker rendering follows the new resolved theme

Note

Medium Risk
Touches diff rendering and worker pool configuration; regressions could cause stale or incorrect syntax highlighting when toggling themes, but changes are UI-only and not security/data-critical.

Overview
Ensures diff rendering updates cleanly on theme changes by mapping the app’s resolved theme to explicit Pierre themes (pierre-light/pierre-dark) and passing them into FileDiff options.

Updates the diff worker pool to initialize and continuously sync highlighter/render options to the current theme via a new DiffWorkerThemeSync, and adjusts useTheme’s useSyncExternalStore snapshot to include cached system dark-mode state for more consistent updates (especially when theme=system).

Written by Cursor Bugbot for commit df323f8. This will update automatically on new commits. Configure here.

Note

Sync diff renderer and worker theme with resolved app theme in apps/web/src/components/DiffPanel.tsx and apps/web/src/components/DiffWorkerPoolProvider.tsx

Restrict DiffThemeType to "light" | "dark", add resolveDiffThemeName to map resolved themes to "pierre-light" or "pierre-dark", remount FileDiff items on theme change via keyed resolvedTheme, and propagate theme names to the worker pool render and highlighter options. Core changes are in DiffPanel.tsx, DiffWorkerPoolProvider.tsx, and useTheme.ts.

📍Where to Start

Start with resolveDiffThemeName and its usage in DiffPanel in DiffPanel.tsx, then review DiffWorkerPoolProvider and DiffWorkerThemeSync in DiffWorkerPoolProvider.tsx.

Macroscope summarized df323f8.

Summary by CodeRabbit

  • Changes

    • Removed "system" theme option from diff panel; now supports "light" and "dark" only.
  • Improvements

    • Improved theme synchronization for diff rendering to ensure consistency across the UI.
    • Enhanced performance of theme handling.

- Pass explicit Pierre light/dark theme into diff render options
- Re-key file diffs on resolved theme so views rerender on theme switch
- Stabilize `useTheme` snapshots to propagate system theme updates reliably
@coderabbitai

coderabbitaiBot commented Feb 27, 2026

Copy link
Copy Markdown

Walkthrough

These changes implement theme synchronization across the diff rendering system. DiffPanel removes the "system" theme option and uses a concrete theme mapping to "pierre-light" or "pierre-dark". DiffWorkerPoolProvider now synchronizes theme changes to the worker pool, and useTheme introduces snapshot-based caching for optimized theme resolution with system preference tracking.

Changes

Cohort / File(s)Summary
Diff Panel Theme Mapping
apps/web/src/components/DiffPanel.tsx
Removed "system" from DiffThemeType ("light" | "dark" only). Added resolveDiffThemeName helper to map theme values to "pierre-light" or "pierre-dark". Updated rendering to use themedFileKey and pass resolved theme to FileDiff component.
Worker Pool Theme Synchronization
apps/web/src/components/DiffWorkerPoolProvider.tsx
Integrated theme synchronization with worker pool. Added DiffWorkerThemeSync component to detect theme changes and update worker render options. Computes diffThemeName and passes it to highlighterOptions. Extended provider to sync theme changes dynamically.
Theme Hook Snapshot Caching
apps/web/src/hooks/useTheme.ts
Introduced ThemeSnapshot type containing theme and systemDark fields. Implemented module-level caching in getSnapshot to avoid recomputation when values unchanged. Updated getSnapshot to return ThemeSnapshot and useTheme to consume snapshots via useSyncExternalStore, eliminating redundant system preference lookups.

Sequence Diagram

sequenceDiagram
participant UI as React UI
participant useTheme as useTheme Hook
participant ExtStore as External Store
participant DiffProvider as DiffWorkerPoolProvider
participant Worker as Worker Pool
UI->>useTheme: Read current theme
useTheme->>ExtStore: getSnapshot() → ThemeSnapshot
ExtStore-->>useTheme: {theme, systemDark}
useTheme-->>UI: resolvedTheme (light/dark)
UI->>DiffProvider: Theme changed signal
DiffProvider->>useTheme: useTheme() → current theme
useTheme-->>DiffProvider: resolvedTheme
DiffProvider->>DiffProvider: Compute diffThemeName<br/>(light→pierre-light, etc.)
DiffProvider->>Worker: setRenderOptions({<br/>highlighterOptions: {theme}})
Worker-->>DiffProvider: Updated
DiffProvider->>UI: DiffPanel with themed<br/>FileDiff components
UI-->>UI: Render with active theme
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main objective of the pull request: synchronizing diff renderer and worker theme with the resolved app theme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/c44d2b35

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/web/src/components/DiffWorkerPoolProvider.tsx (1)

10-32: Consider logging errors instead of silently swallowing them.

The .catch(() => undefined) on line 28 silently discards any errors from setRenderOptions. While you may not want to crash the UI, logging the error would help with debugging theme sync issues.

♻️ Proposed improvement
 void workerPool
.setRenderOptions({
...current,
theme: themeName,
})
- .catch(() => undefined);+ .catch((err) => {+ console.error("[DiffWorkerThemeSync] Failed to sync theme:", err);+ });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx` around lines 10 - 32, In
DiffWorkerThemeSync, stop silently swallowing errors from
workerPool.setRenderOptions; replace the .catch(() => undefined) with a handler
that logs the error (e.g., console.error or your app logger) and includes
context (mention themeName and that it occurred in DiffWorkerThemeSync when
calling setRenderOptions on the workerPool returned by useWorkerPool); ensure
this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).
apps/web/src/components/DiffPanel.tsx (1)

19-23: Extract resolveDiffThemeName to a shared utility to eliminate duplication.

This function is duplicated in DiffWorkerPoolProvider.tsx (lines 6-8). Consider extracting it to a shared location, e.g., ~/lib/diffTheme.ts.

♻️ Proposed extraction

Create a new file apps/web/src/lib/diffTheme.ts:

exporttypeDiffThemeType="light"|"dark";exportfunctionresolveDiffThemeName(theme: DiffThemeType){returntheme==="dark" ? "pierre-dark" : "pierre-light";}

Then import it in both files:

-type DiffThemeType = "light" | "dark";--function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}+import { resolveDiffThemeName, type DiffThemeType } from "~/lib/diffTheme";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffPanel.tsx` around lines 19 - 23, Extract the
duplicated resolveDiffThemeName and DiffThemeType into a shared utility: create
a new module that exports `type DiffThemeType = "light" | "dark"` and `function
resolveDiffThemeName(theme: DiffThemeType)` (returning "pierre-dark" for "dark"
else "pierre-light"), then replace the local definitions in both `DiffPanel` and
`DiffWorkerPoolProvider` with imports of `DiffThemeType` and
`resolveDiffThemeName` from the new utility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/src/components/DiffPanel.tsx`:
- Around line 19-23: Extract the duplicated resolveDiffThemeName and
DiffThemeType into a shared utility: create a new module that exports `type
DiffThemeType = "light" | "dark"` and `function resolveDiffThemeName(theme:
DiffThemeType)` (returning "pierre-dark" for "dark" else "pierre-light"), then
replace the local definitions in both `DiffPanel` and `DiffWorkerPoolProvider`
with imports of `DiffThemeType` and `resolveDiffThemeName` from the new utility.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx`:
- Around line 10-32: In DiffWorkerThemeSync, stop silently swallowing errors
from workerPool.setRenderOptions; replace the .catch(() => undefined) with a
handler that logs the error (e.g., console.error or your app logger) and
includes context (mention themeName and that it occurred in DiffWorkerThemeSync
when calling setRenderOptions on the workerPool returned by useWorkerPool);
ensure this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 041acf1 and df323f8.

📒 Files selected for processing (3)
  • apps/web/src/components/DiffPanel.tsx
  • apps/web/src/components/DiffWorkerPoolProvider.tsx
  • apps/web/src/hooks/useTheme.ts

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicate helper function across two files
    • Extracted resolveDiffThemeName into the shared lib/diffRendering.ts module and updated both DiffPanel.tsx and DiffWorkerPoolProvider.tsx to import from it.

Create PR

Or push these changes by commenting:

@cursor push 48f196d911
Preview (48f196d911)
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx--- a/apps/web/src/components/DiffPanel.tsx+++ b/apps/web/src/components/DiffPanel.tsx@@ -10,7 +10,7 @@
import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
import { isElectron } from "../env";
import { useTheme } from "../hooks/useTheme";
-import { buildPatchCacheKey } from "../lib/diffRendering";+import { buildPatchCacheKey, resolveDiffThemeName } from "../lib/diffRendering";
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
import { useStore } from "../store";
import { ToggleGroup, Toggle } from "./ui/toggle-group";
@@ -18,10 +18,6 @@
type DiffRenderMode = "stacked" | "split";
type DiffThemeType = "light" | "dark";
-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
type RenderablePatch =
| {
kind: "files";
diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx--- a/apps/web/src/components/DiffWorkerPoolProvider.tsx+++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx@@ -2,11 +2,8 @@
import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
import { useEffect, useMemo, type ReactNode } from "react";
import { useTheme } from "../hooks/useTheme";
+import { resolveDiffThemeName } from "../lib/diffRendering";-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
function DiffWorkerThemeSync({ themeName }: { themeName: "pierre-light" | "pierre-dark" }) {
const workerPool = useWorkerPool();
diff --git a/apps/web/src/lib/diffRendering.ts b/apps/web/src/lib/diffRendering.ts--- a/apps/web/src/lib/diffRendering.ts+++ b/apps/web/src/lib/diffRendering.ts@@ -12,6 +12,10 @@
return hash >>> 0;
}
+export function resolveDiffThemeName(theme: "light" | "dark") {+ return theme === "dark" ? "pierre-dark" : "pierre-light";+}+
export function buildPatchCacheKey(patch: string, scope = "diff-panel"): string {
const normalizedPatch = patch.trim();
const primary = fnv1a32(normalizedPatch, FNV_OFFSET_BASIS_32, FNV_PRIME_32).toString(36);


function resolveDiffThemeName(theme: "light" | "dark") {
return theme === "dark" ? "pierre-dark" : "pierre-light";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate helper function across two files

Low Severity

resolveDiffThemeName is identically defined in both DiffPanel.tsx and DiffWorkerPoolProvider.tsx. If the theme name mapping ever changes (e.g., new theme variants), both copies need to be updated in sync, which is easy to miss. Extracting it to a shared module avoids the maintenance risk.

Additional Locations (1)

Fix in CursorFix in Web

@juliusmarminge
juliusmarminge merged commit 3a43047 into mainFeb 27, 2026
5 checks passed
Sign up for freeto 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

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Sync diff renderer and worker theme with resolved app theme - #108

Merged
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35
Feb 27, 2026
Merged

Sync diff renderer and worker theme with resolved app theme#108
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

  • update diff panel rendering to use explicit Pierre theme names (pierre-light/pierre-dark) derived from the resolved app theme
  • include resolved theme in file diff React keys so diff views remount cleanly on theme switches
  • sync worker pool highlighter/render options with current theme via DiffWorkerThemeSync
  • improve useTheme snapshot stability by tracking both stored theme and system dark-mode state for consistent useSyncExternalStore updates

Testing

  • Not run (not provided in patch context)
  • Suggested check: toggle app theme between light, dark, and system and verify diff colors update immediately without stale highlighting
  • Suggested check: with system theme selected, change OS color scheme and verify diff panel/worker rendering follows the new resolved theme

Note

Medium Risk
Touches diff rendering and worker pool configuration; regressions could cause stale or incorrect syntax highlighting when toggling themes, but changes are UI-only and not security/data-critical.

Overview
Ensures diff rendering updates cleanly on theme changes by mapping the app’s resolved theme to explicit Pierre themes (pierre-light/pierre-dark) and passing them into FileDiff options.

Updates the diff worker pool to initialize and continuously sync highlighter/render options to the current theme via a new DiffWorkerThemeSync, and adjusts useTheme’s useSyncExternalStore snapshot to include cached system dark-mode state for more consistent updates (especially when theme=system).

Written by Cursor Bugbot for commit df323f8. This will update automatically on new commits. Configure here.

Note

Sync diff renderer and worker theme with resolved app theme in apps/web/src/components/DiffPanel.tsx and apps/web/src/components/DiffWorkerPoolProvider.tsx

Restrict DiffThemeType to "light" | "dark", add resolveDiffThemeName to map resolved themes to "pierre-light" or "pierre-dark", remount FileDiff items on theme change via keyed resolvedTheme, and propagate theme names to the worker pool render and highlighter options. Core changes are in DiffPanel.tsx, DiffWorkerPoolProvider.tsx, and useTheme.ts.

📍Where to Start

Start with resolveDiffThemeName and its usage in DiffPanel in DiffPanel.tsx, then review DiffWorkerPoolProvider and DiffWorkerThemeSync in DiffWorkerPoolProvider.tsx.

Macroscope summarized df323f8.

Summary by CodeRabbit

  • Changes

    • Removed "system" theme option from diff panel; now supports "light" and "dark" only.
  • Improvements

    • Improved theme synchronization for diff rendering to ensure consistency across the UI.
    • Enhanced performance of theme handling.

- Pass explicit Pierre light/dark theme into diff render options
- Re-key file diffs on resolved theme so views rerender on theme switch
- Stabilize `useTheme` snapshots to propagate system theme updates reliably
@coderabbitai

coderabbitaiBot commented Feb 27, 2026

Copy link
Copy Markdown

Walkthrough

These changes implement theme synchronization across the diff rendering system. DiffPanel removes the "system" theme option and uses a concrete theme mapping to "pierre-light" or "pierre-dark". DiffWorkerPoolProvider now synchronizes theme changes to the worker pool, and useTheme introduces snapshot-based caching for optimized theme resolution with system preference tracking.

Changes

Cohort / File(s)Summary
Diff Panel Theme Mapping
apps/web/src/components/DiffPanel.tsx
Removed "system" from DiffThemeType ("light" | "dark" only). Added resolveDiffThemeName helper to map theme values to "pierre-light" or "pierre-dark". Updated rendering to use themedFileKey and pass resolved theme to FileDiff component.
Worker Pool Theme Synchronization
apps/web/src/components/DiffWorkerPoolProvider.tsx
Integrated theme synchronization with worker pool. Added DiffWorkerThemeSync component to detect theme changes and update worker render options. Computes diffThemeName and passes it to highlighterOptions. Extended provider to sync theme changes dynamically.
Theme Hook Snapshot Caching
apps/web/src/hooks/useTheme.ts
Introduced ThemeSnapshot type containing theme and systemDark fields. Implemented module-level caching in getSnapshot to avoid recomputation when values unchanged. Updated getSnapshot to return ThemeSnapshot and useTheme to consume snapshots via useSyncExternalStore, eliminating redundant system preference lookups.

Sequence Diagram

sequenceDiagram
participant UI as React UI
participant useTheme as useTheme Hook
participant ExtStore as External Store
participant DiffProvider as DiffWorkerPoolProvider
participant Worker as Worker Pool
UI->>useTheme: Read current theme
useTheme->>ExtStore: getSnapshot() → ThemeSnapshot
ExtStore-->>useTheme: {theme, systemDark}
useTheme-->>UI: resolvedTheme (light/dark)
UI->>DiffProvider: Theme changed signal
DiffProvider->>useTheme: useTheme() → current theme
useTheme-->>DiffProvider: resolvedTheme
DiffProvider->>DiffProvider: Compute diffThemeName<br/>(light→pierre-light, etc.)
DiffProvider->>Worker: setRenderOptions({<br/>highlighterOptions: {theme}})
Worker-->>DiffProvider: Updated
DiffProvider->>UI: DiffPanel with themed<br/>FileDiff components
UI-->>UI: Render with active theme
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main objective of the pull request: synchronizing diff renderer and worker theme with the resolved app theme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/c44d2b35

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/web/src/components/DiffWorkerPoolProvider.tsx (1)

10-32: Consider logging errors instead of silently swallowing them.

The .catch(() => undefined) on line 28 silently discards any errors from setRenderOptions. While you may not want to crash the UI, logging the error would help with debugging theme sync issues.

♻️ Proposed improvement
 void workerPool
.setRenderOptions({
...current,
theme: themeName,
})
- .catch(() => undefined);+ .catch((err) => {+ console.error("[DiffWorkerThemeSync] Failed to sync theme:", err);+ });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx` around lines 10 - 32, In
DiffWorkerThemeSync, stop silently swallowing errors from
workerPool.setRenderOptions; replace the .catch(() => undefined) with a handler
that logs the error (e.g., console.error or your app logger) and includes
context (mention themeName and that it occurred in DiffWorkerThemeSync when
calling setRenderOptions on the workerPool returned by useWorkerPool); ensure
this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).
apps/web/src/components/DiffPanel.tsx (1)

19-23: Extract resolveDiffThemeName to a shared utility to eliminate duplication.

This function is duplicated in DiffWorkerPoolProvider.tsx (lines 6-8). Consider extracting it to a shared location, e.g., ~/lib/diffTheme.ts.

♻️ Proposed extraction

Create a new file apps/web/src/lib/diffTheme.ts:

exporttypeDiffThemeType="light"|"dark";exportfunctionresolveDiffThemeName(theme: DiffThemeType){returntheme==="dark" ? "pierre-dark" : "pierre-light";}

Then import it in both files:

-type DiffThemeType = "light" | "dark";--function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}+import { resolveDiffThemeName, type DiffThemeType } from "~/lib/diffTheme";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffPanel.tsx` around lines 19 - 23, Extract the
duplicated resolveDiffThemeName and DiffThemeType into a shared utility: create
a new module that exports `type DiffThemeType = "light" | "dark"` and `function
resolveDiffThemeName(theme: DiffThemeType)` (returning "pierre-dark" for "dark"
else "pierre-light"), then replace the local definitions in both `DiffPanel` and
`DiffWorkerPoolProvider` with imports of `DiffThemeType` and
`resolveDiffThemeName` from the new utility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/src/components/DiffPanel.tsx`:
- Around line 19-23: Extract the duplicated resolveDiffThemeName and
DiffThemeType into a shared utility: create a new module that exports `type
DiffThemeType = "light" | "dark"` and `function resolveDiffThemeName(theme:
DiffThemeType)` (returning "pierre-dark" for "dark" else "pierre-light"), then
replace the local definitions in both `DiffPanel` and `DiffWorkerPoolProvider`
with imports of `DiffThemeType` and `resolveDiffThemeName` from the new utility.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx`:
- Around line 10-32: In DiffWorkerThemeSync, stop silently swallowing errors
from workerPool.setRenderOptions; replace the .catch(() => undefined) with a
handler that logs the error (e.g., console.error or your app logger) and
includes context (mention themeName and that it occurred in DiffWorkerThemeSync
when calling setRenderOptions on the workerPool returned by useWorkerPool);
ensure this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 041acf1 and df323f8.

📒 Files selected for processing (3)
  • apps/web/src/components/DiffPanel.tsx
  • apps/web/src/components/DiffWorkerPoolProvider.tsx
  • apps/web/src/hooks/useTheme.ts

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicate helper function across two files
    • Extracted resolveDiffThemeName into the shared lib/diffRendering.ts module and updated both DiffPanel.tsx and DiffWorkerPoolProvider.tsx to import from it.

Create PR

Or push these changes by commenting:

@cursor push 48f196d911
Preview (48f196d911)
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx--- a/apps/web/src/components/DiffPanel.tsx+++ b/apps/web/src/components/DiffPanel.tsx@@ -10,7 +10,7 @@
import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
import { isElectron } from "../env";
import { useTheme } from "../hooks/useTheme";
-import { buildPatchCacheKey } from "../lib/diffRendering";+import { buildPatchCacheKey, resolveDiffThemeName } from "../lib/diffRendering";
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
import { useStore } from "../store";
import { ToggleGroup, Toggle } from "./ui/toggle-group";
@@ -18,10 +18,6 @@
type DiffRenderMode = "stacked" | "split";
type DiffThemeType = "light" | "dark";
-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
type RenderablePatch =
| {
kind: "files";
diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx--- a/apps/web/src/components/DiffWorkerPoolProvider.tsx+++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx@@ -2,11 +2,8 @@
import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
import { useEffect, useMemo, type ReactNode } from "react";
import { useTheme } from "../hooks/useTheme";
+import { resolveDiffThemeName } from "../lib/diffRendering";-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
function DiffWorkerThemeSync({ themeName }: { themeName: "pierre-light" | "pierre-dark" }) {
const workerPool = useWorkerPool();
diff --git a/apps/web/src/lib/diffRendering.ts b/apps/web/src/lib/diffRendering.ts--- a/apps/web/src/lib/diffRendering.ts+++ b/apps/web/src/lib/diffRendering.ts@@ -12,6 +12,10 @@
return hash >>> 0;
}
+export function resolveDiffThemeName(theme: "light" | "dark") {+ return theme === "dark" ? "pierre-dark" : "pierre-light";+}+
export function buildPatchCacheKey(patch: string, scope = "diff-panel"): string {
const normalizedPatch = patch.trim();
const primary = fnv1a32(normalizedPatch, FNV_OFFSET_BASIS_32, FNV_PRIME_32).toString(36);


function resolveDiffThemeName(theme: "light" | "dark") {
return theme === "dark" ? "pierre-dark" : "pierre-light";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate helper function across two files

Low Severity

resolveDiffThemeName is identically defined in both DiffPanel.tsx and DiffWorkerPoolProvider.tsx. If the theme name mapping ever changes (e.g., new theme variants), both copies need to be updated in sync, which is easy to miss. Extracting it to a shared module avoids the maintenance risk.

Additional Locations (1)

Fix in CursorFix in Web

@juliusmarminge
juliusmarminge merged commit 3a43047 into mainFeb 27, 2026
5 checks passed
Sign up for freeto 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

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Sync diff renderer and worker theme with resolved app theme - #108

Merged
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35
Feb 27, 2026
Merged

Sync diff renderer and worker theme with resolved app theme#108
juliusmarminge merged 1 commit into
mainfrom
codething/c44d2b35

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

  • update diff panel rendering to use explicit Pierre theme names (pierre-light/pierre-dark) derived from the resolved app theme
  • include resolved theme in file diff React keys so diff views remount cleanly on theme switches
  • sync worker pool highlighter/render options with current theme via DiffWorkerThemeSync
  • improve useTheme snapshot stability by tracking both stored theme and system dark-mode state for consistent useSyncExternalStore updates

Testing

  • Not run (not provided in patch context)
  • Suggested check: toggle app theme between light, dark, and system and verify diff colors update immediately without stale highlighting
  • Suggested check: with system theme selected, change OS color scheme and verify diff panel/worker rendering follows the new resolved theme

Note

Medium Risk
Touches diff rendering and worker pool configuration; regressions could cause stale or incorrect syntax highlighting when toggling themes, but changes are UI-only and not security/data-critical.

Overview
Ensures diff rendering updates cleanly on theme changes by mapping the app’s resolved theme to explicit Pierre themes (pierre-light/pierre-dark) and passing them into FileDiff options.

Updates the diff worker pool to initialize and continuously sync highlighter/render options to the current theme via a new DiffWorkerThemeSync, and adjusts useTheme’s useSyncExternalStore snapshot to include cached system dark-mode state for more consistent updates (especially when theme=system).

Written by Cursor Bugbot for commit df323f8. This will update automatically on new commits. Configure here.

Note

Sync diff renderer and worker theme with resolved app theme in apps/web/src/components/DiffPanel.tsx and apps/web/src/components/DiffWorkerPoolProvider.tsx

Restrict DiffThemeType to "light" | "dark", add resolveDiffThemeName to map resolved themes to "pierre-light" or "pierre-dark", remount FileDiff items on theme change via keyed resolvedTheme, and propagate theme names to the worker pool render and highlighter options. Core changes are in DiffPanel.tsx, DiffWorkerPoolProvider.tsx, and useTheme.ts.

📍Where to Start

Start with resolveDiffThemeName and its usage in DiffPanel in DiffPanel.tsx, then review DiffWorkerPoolProvider and DiffWorkerThemeSync in DiffWorkerPoolProvider.tsx.

Macroscope summarized df323f8.

Summary by CodeRabbit

  • Changes

    • Removed "system" theme option from diff panel; now supports "light" and "dark" only.
  • Improvements

    • Improved theme synchronization for diff rendering to ensure consistency across the UI.
    • Enhanced performance of theme handling.

- Pass explicit Pierre light/dark theme into diff render options
- Re-key file diffs on resolved theme so views rerender on theme switch
- Stabilize `useTheme` snapshots to propagate system theme updates reliably
@coderabbitai

coderabbitaiBot commented Feb 27, 2026

Copy link
Copy Markdown

Walkthrough

These changes implement theme synchronization across the diff rendering system. DiffPanel removes the "system" theme option and uses a concrete theme mapping to "pierre-light" or "pierre-dark". DiffWorkerPoolProvider now synchronizes theme changes to the worker pool, and useTheme introduces snapshot-based caching for optimized theme resolution with system preference tracking.

Changes

Cohort / File(s)Summary
Diff Panel Theme Mapping
apps/web/src/components/DiffPanel.tsx
Removed "system" from DiffThemeType ("light" | "dark" only). Added resolveDiffThemeName helper to map theme values to "pierre-light" or "pierre-dark". Updated rendering to use themedFileKey and pass resolved theme to FileDiff component.
Worker Pool Theme Synchronization
apps/web/src/components/DiffWorkerPoolProvider.tsx
Integrated theme synchronization with worker pool. Added DiffWorkerThemeSync component to detect theme changes and update worker render options. Computes diffThemeName and passes it to highlighterOptions. Extended provider to sync theme changes dynamically.
Theme Hook Snapshot Caching
apps/web/src/hooks/useTheme.ts
Introduced ThemeSnapshot type containing theme and systemDark fields. Implemented module-level caching in getSnapshot to avoid recomputation when values unchanged. Updated getSnapshot to return ThemeSnapshot and useTheme to consume snapshots via useSyncExternalStore, eliminating redundant system preference lookups.

Sequence Diagram

sequenceDiagram
participant UI as React UI
participant useTheme as useTheme Hook
participant ExtStore as External Store
participant DiffProvider as DiffWorkerPoolProvider
participant Worker as Worker Pool
UI->>useTheme: Read current theme
useTheme->>ExtStore: getSnapshot() → ThemeSnapshot
ExtStore-->>useTheme: {theme, systemDark}
useTheme-->>UI: resolvedTheme (light/dark)
UI->>DiffProvider: Theme changed signal
DiffProvider->>useTheme: useTheme() → current theme
useTheme-->>DiffProvider: resolvedTheme
DiffProvider->>DiffProvider: Compute diffThemeName<br/>(light→pierre-light, etc.)
DiffProvider->>Worker: setRenderOptions({<br/>highlighterOptions: {theme}})
Worker-->>DiffProvider: Updated
DiffProvider->>UI: DiffPanel with themed<br/>FileDiff components
UI-->>UI: Render with active theme
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main objective of the pull request: synchronizing diff renderer and worker theme with the resolved app theme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/c44d2b35

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/web/src/components/DiffWorkerPoolProvider.tsx (1)

10-32: Consider logging errors instead of silently swallowing them.

The .catch(() => undefined) on line 28 silently discards any errors from setRenderOptions. While you may not want to crash the UI, logging the error would help with debugging theme sync issues.

♻️ Proposed improvement
 void workerPool
.setRenderOptions({
...current,
theme: themeName,
})
- .catch(() => undefined);+ .catch((err) => {+ console.error("[DiffWorkerThemeSync] Failed to sync theme:", err);+ });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx` around lines 10 - 32, In
DiffWorkerThemeSync, stop silently swallowing errors from
workerPool.setRenderOptions; replace the .catch(() => undefined) with a handler
that logs the error (e.g., console.error or your app logger) and includes
context (mention themeName and that it occurred in DiffWorkerThemeSync when
calling setRenderOptions on the workerPool returned by useWorkerPool); ensure
this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).
apps/web/src/components/DiffPanel.tsx (1)

19-23: Extract resolveDiffThemeName to a shared utility to eliminate duplication.

This function is duplicated in DiffWorkerPoolProvider.tsx (lines 6-8). Consider extracting it to a shared location, e.g., ~/lib/diffTheme.ts.

♻️ Proposed extraction

Create a new file apps/web/src/lib/diffTheme.ts:

exporttypeDiffThemeType="light"|"dark";exportfunctionresolveDiffThemeName(theme: DiffThemeType){returntheme==="dark" ? "pierre-dark" : "pierre-light";}

Then import it in both files:

-type DiffThemeType = "light" | "dark";--function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}+import { resolveDiffThemeName, type DiffThemeType } from "~/lib/diffTheme";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/DiffPanel.tsx` around lines 19 - 23, Extract the
duplicated resolveDiffThemeName and DiffThemeType into a shared utility: create
a new module that exports `type DiffThemeType = "light" | "dark"` and `function
resolveDiffThemeName(theme: DiffThemeType)` (returning "pierre-dark" for "dark"
else "pierre-light"), then replace the local definitions in both `DiffPanel` and
`DiffWorkerPoolProvider` with imports of `DiffThemeType` and
`resolveDiffThemeName` from the new utility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/src/components/DiffPanel.tsx`:
- Around line 19-23: Extract the duplicated resolveDiffThemeName and
DiffThemeType into a shared utility: create a new module that exports `type
DiffThemeType = "light" | "dark"` and `function resolveDiffThemeName(theme:
DiffThemeType)` (returning "pierre-dark" for "dark" else "pierre-light"), then
replace the local definitions in both `DiffPanel` and `DiffWorkerPoolProvider`
with imports of `DiffThemeType` and `resolveDiffThemeName` from the new utility.
In `@apps/web/src/components/DiffWorkerPoolProvider.tsx`:
- Around line 10-32: In DiffWorkerThemeSync, stop silently swallowing errors
from workerPool.setRenderOptions; replace the .catch(() => undefined) with a
handler that logs the error (e.g., console.error or your app logger) and
includes context (mention themeName and that it occurred in DiffWorkerThemeSync
when calling setRenderOptions on the workerPool returned by useWorkerPool);
ensure this change is applied where setRenderOptions is called and keep the
non-blocking behavior (do not rethrow).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 041acf1 and df323f8.

📒 Files selected for processing (3)
  • apps/web/src/components/DiffPanel.tsx
  • apps/web/src/components/DiffWorkerPoolProvider.tsx
  • apps/web/src/hooks/useTheme.ts

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicate helper function across two files
    • Extracted resolveDiffThemeName into the shared lib/diffRendering.ts module and updated both DiffPanel.tsx and DiffWorkerPoolProvider.tsx to import from it.

Create PR

Or push these changes by commenting:

@cursor push 48f196d911
Preview (48f196d911)
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx--- a/apps/web/src/components/DiffPanel.tsx+++ b/apps/web/src/components/DiffPanel.tsx@@ -10,7 +10,7 @@
import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
import { isElectron } from "../env";
import { useTheme } from "../hooks/useTheme";
-import { buildPatchCacheKey } from "../lib/diffRendering";+import { buildPatchCacheKey, resolveDiffThemeName } from "../lib/diffRendering";
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
import { useStore } from "../store";
import { ToggleGroup, Toggle } from "./ui/toggle-group";
@@ -18,10 +18,6 @@
type DiffRenderMode = "stacked" | "split";
type DiffThemeType = "light" | "dark";
-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
type RenderablePatch =
| {
kind: "files";
diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx--- a/apps/web/src/components/DiffWorkerPoolProvider.tsx+++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx@@ -2,11 +2,8 @@
import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
import { useEffect, useMemo, type ReactNode } from "react";
import { useTheme } from "../hooks/useTheme";
+import { resolveDiffThemeName } from "../lib/diffRendering";-function resolveDiffThemeName(theme: "light" | "dark") {- return theme === "dark" ? "pierre-dark" : "pierre-light";-}-
function DiffWorkerThemeSync({ themeName }: { themeName: "pierre-light" | "pierre-dark" }) {
const workerPool = useWorkerPool();
diff --git a/apps/web/src/lib/diffRendering.ts b/apps/web/src/lib/diffRendering.ts--- a/apps/web/src/lib/diffRendering.ts+++ b/apps/web/src/lib/diffRendering.ts@@ -12,6 +12,10 @@
return hash >>> 0;
}
+export function resolveDiffThemeName(theme: "light" | "dark") {+ return theme === "dark" ? "pierre-dark" : "pierre-light";+}+
export function buildPatchCacheKey(patch: string, scope = "diff-panel"): string {
const normalizedPatch = patch.trim();
const primary = fnv1a32(normalizedPatch, FNV_OFFSET_BASIS_32, FNV_PRIME_32).toString(36);


function resolveDiffThemeName(theme: "light" | "dark") {
return theme === "dark" ? "pierre-dark" : "pierre-light";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate helper function across two files

Low Severity

resolveDiffThemeName is identically defined in both DiffPanel.tsx and DiffWorkerPoolProvider.tsx. If the theme name mapping ever changes (e.g., new theme variants), both copies need to be updated in sync, which is easy to miss. Extracting it to a shared module avoids the maintenance risk.

Additional Locations (1)

Fix in CursorFix in Web

@juliusmarminge
juliusmarminge merged commit 3a43047 into mainFeb 27, 2026
5 checks passed
Sign up for freeto 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

@juliusmarminge