Skip to content

fix(client): resolve hostContext.theme stuck on "light" when dark mode toggled#1983

Open
mdodell wants to merge 2 commits intoMCPJam:mainfrom
mdodell:mdodell/fix-dark-mode-host-context-theme
Open

fix(client): resolve hostContext.theme stuck on "light" when dark mode toggled#1983
mdodell wants to merge 2 commits intoMCPJam:mainfrom
mdodell:mdodell/fix-dark-mode-host-context-theme

Conversation

@mdodell
Copy link
Copy Markdown

@mdodell mdodell commented Apr 30, 2026

Fixes #1982

hostContext.theme and style variables always resolved to "light" even after toggling dark mode in Settings. The global themeMode preference was baked into buildDefaultWorkspaceHostContext, but loadWorkspaceHostContext silently skipped updates when the store was dirty or had a saved config — so the stale value always won.

Removes theme from the default workspace host context so the resolution chain falls through to themeMode directly. The playground theme toggle now updates the global preference instead of patching draftHostContext.

…dark mode is toggled

Theme had two competing sources of truth — `themeMode` in the preferences
store (set by the Settings toggle) and `draftHostContext.theme` baked into
the default host context by `buildDefaultWorkspaceHostContext`. When the
global theme changed, `loadWorkspaceHostContext` silently skipped the update
whenever the store was dirty or a saved config existed, so the stale "light"
value always won the resolution chain.

Remove `theme` from the default workspace host context so it is resolved at
render time from the global preference instead. The playground theme toggle
now updates the global preference directly.

Closes MCPJam#1982

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Apr 30, 2026
@chelojimenez
Copy link
Copy Markdown
Contributor

chelojimenez commented Apr 30, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

Walkthrough

Theme is removed from workspace host-context and client-config builders; buildDefaultWorkspaceHostContext/buildDefaultWorkspaceClientConfig no longer accept or set a theme field. WorkspaceClientConfigSync stops reading theme from the preferences store. HostContextHeader refactors toggle logic to compute a local newTheme and pass it to patchHostContext. Chat/App renderer logic now resolves theme from the host-theme provider (chatboxHostTheme ?? themeMode) instead of draft host context. Tests were updated to reflect that host-context drafts no longer contain theme and theme resolution uses the host-theme provider.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@Genmin
Copy link
Copy Markdown

Genmin commented May 1, 2026

Nice fix. I think there is still one saved-config path worth covering before merge: if a workspace already has a saved/draft host context with theme: "light", removing theme from the default host context does not clear that saved value. In the playground, both MCPAppsRenderer and PlaygroundMain still prefer extractHostTheme(draftHostContext) over the shared themeMode, so Settings dark mode can still send/render light for that saved workspace case.

A focused regression would be: set themeMode = "dark", isPlaygroundActive = true, and draftHostContext = { theme: "light" }; assert the MCP Apps bridge hostContext has theme: "dark" and dark SEP variables. The corresponding runtime fix is to resolve MCP Apps/playground rendering theme from the active chat/global preference (chatboxHostTheme ?? themeMode) rather than from draftHostContext.theme, while keeping the other host-context fields unchanged.

… global prefs

The theme toggle in HostContextHeader was calling updateThemeMode/setThemeMode
which changed the global app shell preference. The intent is for the app-builder
theme to be independent — persisting with the workspace host context (like locale
and timezone) so MCP apps receive the correct theme via hostContext.theme.

MCPAppsRenderer no longer reads configuredHostTheme from draftHostContext directly;
theme reaches it through ChatboxHostThemeProvider -> chatboxHostTheme, which
PlaygroundMain feeds from extractHostTheme(draftHostContext) ?? globalThemeMode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/__tests__/mcp-apps-renderer.test.tsx (1)

384-413: ⚡ Quick win

Strengthen this regression test with dark-token verification.

Lines 399 and 409 assert theme: "dark", but the bug contract also requires dark SEP variables. Please assert hostContext.styles.variables reflects dark-mode tokens in both initialize and update paths.

Proposed test augmentation
+import { getHostStyleOrDefault } from "@/lib/host-styles";
...
   it("resolves theme from host context provider, ignoring draftHostContext.theme", async () => {
     Object.assign(mockPlaygroundStoreState, {
       isPlaygroundActive: true,
     });
     mockPreferencesState.themeMode = "dark";
+    const expectedDarkVariables =
+      getHostStyleOrDefault(mockPreferencesState.hostStyle).resolveStyleVariables(
+        "dark",
+      );
     mockHostContextStoreState.draftHostContext = {
       theme: "light",
     };
...
     expect(appBridgeArgsRef.current?.options?.hostContext?.theme).toBe("dark");
+    expect(
+      appBridgeArgsRef.current?.options?.hostContext?.styles?.variables,
+    ).toMatchObject(expectedDarkVariables);
...
       expect(mockBridge.setHostContext).toHaveBeenLastCalledWith(
         expect.objectContaining({
           theme: "dark",
+          styles: expect.objectContaining({
+            variables: expect.objectContaining(expectedDarkVariables),
+          }),
         }),
       );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/__tests__/mcp-apps-renderer.test.tsx`
around lines 384 - 413, The test currently asserts the theme is "dark" but must
also verify dark-mode SEP variables are present; update the MCPAppsRenderer test
to additionally assert that
appBridgeArgsRef.current?.options?.hostContext?.styles?.variables contains the
expected dark-mode tokens right after render/initialization, and also that
mockBridge.setHostContext was last called with an object whose
hostContext.styles.variables reflect the same dark tokens after triggerReady();
reference the MCPAppsRenderer component, appBridgeArgsRef, mockBridge.connect,
mockBridge.setHostContext, and triggerReady to locate where to add these two
assertions (one for the initial appBridgeArgsRef snapshot and one inside the
expect(mockBridge.setHostContext).toHaveBeenLastCalledWith(...)).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/__tests__/mcp-apps-renderer.test.tsx`:
- Around line 384-413: The test currently asserts the theme is "dark" but must
also verify dark-mode SEP variables are present; update the MCPAppsRenderer test
to additionally assert that
appBridgeArgsRef.current?.options?.hostContext?.styles?.variables contains the
expected dark-mode tokens right after render/initialization, and also that
mockBridge.setHostContext was last called with an object whose
hostContext.styles.variables reflect the same dark tokens after triggerReady();
reference the MCPAppsRenderer component, appBridgeArgsRef, mockBridge.connect,
mockBridge.setHostContext, and triggerReady to locate where to add these two
assertions (one for the initial appBridgeArgsRef snapshot and one inside the
expect(mockBridge.setHostContext).toHaveBeenLastCalledWith(...)).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3911cc3f-f32a-45ac-aba8-465514c7b7e9

📥 Commits

Reviewing files that changed from the base of the PR and between 419b473 and 09fa9e4.

📒 Files selected for processing (5)
  • mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/__tests__/mcp-apps-renderer.test.tsx
  • mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/mcp-apps-renderer.tsx
  • mcpjam-inspector/client/src/components/shared/HostContextHeader.tsx
  • mcpjam-inspector/client/src/components/shared/__tests__/HostContextHeader.test.tsx
  • mcpjam-inspector/client/src/components/ui-playground/PlaygroundMain.tsx
✅ Files skipped from review due to trivial changes (3)
  • mcpjam-inspector/client/src/components/shared/tests/HostContextHeader.test.tsx
  • mcpjam-inspector/client/src/components/ui-playground/PlaygroundMain.tsx
  • mcpjam-inspector/client/src/components/shared/HostContextHeader.tsx

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

Labels

size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] dark/light mode switch has no effect, sending incorrect variables to MCP Apps

3 participants