From b3d6bb9f0368a9ecfa15ff045b9a9dbaacf664d5 Mon Sep 17 00:00:00 2001 From: Philip Zeyliger Date: Mon, 5 Jan 2026 06:31:02 +0000 Subject: [PATCH] shelley: apply light/dark mode setting to Monaco diff viewer Prompt: in a new worktree, reset to origin/main. The light/dark mode setting doesn't apply yet to the monaco diff view; fix that; it should apply there too. - Add isDarkModeActive() helper to theme service - Set Monaco theme based on current dark mode state when creating editor - Use MutationObserver to watch for dark class changes on documentElement - Dynamically update Monaco theme via monaco.editor.setTheme() when mode changes The Monaco diff viewer now correctly shows 'vs-dark' theme in dark mode and 'vs' theme in light mode, and updates instantly when the user toggles themes. --- ui/src/components/DiffViewer.tsx | 26 +++++++++++++++++++++++++- ui/src/services/theme.ts | 5 +++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/ui/src/components/DiffViewer.tsx b/ui/src/components/DiffViewer.tsx index c81504c952dc92ed68f2345dc859f2453124c7b7..bf432bd31a8b8330fb89c2bb44337096432a2796 100644 --- a/ui/src/components/DiffViewer.tsx +++ b/ui/src/components/DiffViewer.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useCallback, useRef } from "react"; import type * as Monaco from "monaco-editor"; import { api } from "../services/api"; +import { isDarkModeActive } from "../services/theme"; import { GitDiffInfo, GitFileInfo, GitFileDiff } from "../types"; interface DiffViewerProps { @@ -227,7 +228,7 @@ function DiffViewer({ cwd, isOpen, onClose, onCommentTextChange, initialCommit } // Create diff editor with mobile-friendly options const diffEditor = monaco.editor.createDiffEditor(editorContainerRef.current, { - theme: "vs", + theme: isDarkModeActive() ? "vs-dark" : "vs", readOnly: true, // Always read-only in diff viewer originalEditable: false, automaticLayout: true, @@ -578,6 +579,29 @@ function DiffViewer({ cwd, isOpen, onClose, onCommentTextChange, initialCommit } saveCurrentFile(); }, [saveCurrentFile]); + // Update Monaco theme when dark mode changes + useEffect(() => { + if (!monacoRef.current) return; + + const updateMonacoTheme = () => { + const theme = isDarkModeActive() ? "vs-dark" : "vs"; + monacoRef.current?.editor.setTheme(theme); + }; + + // Watch for changes to the dark class on documentElement + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (mutation.attributeName === "class") { + updateMonacoTheme(); + } + } + }); + + observer.observe(document.documentElement, { attributes: true }); + + return () => observer.disconnect(); + }, [monacoLoaded]); + // Keyboard shortcuts useEffect(() => { if (!isOpen) return; diff --git a/ui/src/services/theme.ts b/ui/src/services/theme.ts index 95cedafdf965189aff9203ea313777d3745caa87..fb23b69ed8dfce5f5ed9adba4106cbe807259375 100644 --- a/ui/src/services/theme.ts +++ b/ui/src/services/theme.ts @@ -18,6 +18,11 @@ export function getSystemPrefersDark(): boolean { return window.matchMedia("(prefers-color-scheme: dark)").matches; } +export function isDarkModeActive(): boolean { + const theme = getStoredTheme(); + return theme === "dark" || (theme === "system" && getSystemPrefersDark()); +} + export function applyTheme(theme: ThemeMode): void { const isDark = theme === "dark" || (theme === "system" && getSystemPrefersDark()); document.documentElement.classList.toggle("dark", isDark);