Skip to content

fix(admin): make CodeMirror respect dark mode in custom scripts#38599

Open
Pradeep-10x wants to merge 3 commits into
RocketChat:developfrom
Pradeep-10x:fix/codemirror-dark-mode
Open

fix(admin): make CodeMirror respect dark mode in custom scripts#38599
Pradeep-10x wants to merge 3 commits into
RocketChat:developfrom
Pradeep-10x:fix/codemirror-dark-mode

Conversation

@Pradeep-10x
Copy link
Copy Markdown

@Pradeep-10x Pradeep-10x commented Feb 10, 2026

Proposed changes (screenshots)

This PR fixes an issue where the CodeMirror editor used in Administration → Layout → Custom Scripts did not adapt to Rocket.Chat’s dark mode.

Previously, the surrounding admin UI correctly switched themes, but the CodeMirror editor always rendered using a light theme, causing visual inconsistency and reduced readability in dark mode.

With this change, the editor now:

  • Detects the current Rocket.Chat theme
  • Applies a dark-compatible CodeMirror theme when dark mode is enabled
  • Updates dynamically when the theme is toggled without requiring a reload

This ensures a consistent and comfortable editing experience across light and dark themes.


Issue #38562


Steps to test or reproduce

  1. Start Rocket.Chat
  2. Enable Dark Mode
  3. Navigate to Administration → Layout → Custom Scripts
  4. Observe the CodeMirror editor background and syntax highlighting
  5. Toggle between light and dark mode and verify the editor updates accordingly

Further comments

  • The fix is localized to the admin CodeMirror input component.
  • It uses existing Rocket.Chat hooks and CodeMirror theming APIs.
  • No functional or behavioral changes were introduced beyond theme synchronization.

Screenshot :

image

Summary by CodeRabbit

  • New Features

    • Dark mode support for the code editor in admin settings; theme now automatically follows your system preference.
  • Bug Fixes

    • Improved editor stability and reliability: better handling of external edits, fewer glitches when themes change, and cleaner teardown on exit.

@Pradeep-10x Pradeep-10x requested a review from a team as a code owner February 10, 2026 20:27
@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot Bot commented Feb 10, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Feb 10, 2026

⚠️ No Changeset found

Latest commit: 72efb2a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 10, 2026

Walkthrough

The CodeMirror component was refactored to support dark mode by detecting theme via a hook, capture the initial editor theme, load a material-darker CSS theme, synchronize prop-driven value changes to internal state and the editor, and move editor teardown to an unmount effect without changing public APIs.

Changes

Cohort / File(s) Summary
CodeMirror component
apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx
Adds dark-mode awareness via useDarkMode and isDark; captures initial theme in a ref; imports and applies material-darker CSS; initializes editor with captured theme; syncs external value prop to internal state and pushes updates to the editor; replaces inline cleanup with dedicated unmount teardown; preserves public component shape (readOnly textarea ref).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Under moonlit keys I softly prance,
I swap the theme, I take my chance,
From light to dark the editor sings,
A rabbit taps and fluffs its wings —
Code gleams in night as day’s expanse.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring 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 name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding dark mode support to CodeMirror in the admin custom scripts editor, which is the primary focus of the changeset.

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


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

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

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.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx`:
- Around line 56-58: The callback ref for the CodeMirror textarea fails to clean
up a previous editor instance when React invokes the old ref with null; update
the ref callback used in CodeMirror.tsx to check for the `node === null` case
and call the existing editor cleanup (`editorRef.current?.toTextArea()` or
equivalent) before returning, and when creating a new instance use
`editorRef.current = CodeMirror.fromTextArea(...)`; ensure `editorRef` is the
single source of truth so the old instance is properly destroyed on ref
re-invocation.
- Line 111: The textAreaRef callback currently includes isDark in its dependency
array which causes the CodeMirror editor to be destroyed/recreated on every
theme toggle; remove isDark from the dependency list of the textAreaRef callback
so the editor initialization only runs on mount/element change, and keep theme
updates handled by the separate effect that calls setOption('theme', ...) (the
effect around lines 130–140). Also change the initial theme capture used during
editor creation (the logic at ~line 86) to use a static initial value or read
isDark once into a ref so the mount-time initialization does not capture a stale
isDark.
🧹 Nitpick comments (2)
apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx (2)

115-115: Remove inline code comments.

Lines 115 and 130 contain implementation comments (// Sync external value → editor and // React to dark mode changes). As per coding guidelines, "Avoid code comments in the implementation".

Also applies to: 130-130


116-128: Consider collapsing the two value-sync effects into one.

The intermediate setValue(valueProp) on line 117 triggers a re-render, which then triggers the second effect to push into the editor. You could sync directly from valueProp to the editor in a single effect and keep setValue only for the onChange path:

Proposed simplification
-	// Sync external value → editor
-	useEffect(() => {
-		setValue(valueProp);
-	}, [valueProp]);
-
 	useEffect(() => {
 		if (!editorRef.current) {
 			return;
 		}
 
-		if (value !== editorRef.current.getValue()) {
-			editorRef.current.setValue(value ?? '');
+		if (valueProp !== editorRef.current.getValue()) {
+			editorRef.current.setValue(valueProp ?? '');
 		}
-	}, [value]);
+	}, [valueProp]);

This avoids an extra render cycle when the parent passes a new value.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c53312f and 934ecab.

📒 Files selected for processing (1)
  • apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (2)
apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx (2)

130-140: Dark mode effect looks good.

Clean reactive approach using setOption to toggle the CodeMirror theme without re-initialization.


142-150: Rendered textarea element looks correct.

The hidden textarea with readOnly and callback ref is the standard pattern for CodeMirror.fromTextArea. No issues here.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx Outdated
Comment thread apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 1 file


Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

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.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx`:
- Around line 51-55: The theme can become stale because initialThemeRef is
captured at mount; change the editor init to read isDark directly at creation
time and immediately set the editor's theme option after creating the editor
(replace use of initialThemeRef with a local isDark read inside the async init
callback used to import and call fromTextArea), and ensure the theme-sync effect
(which checks editorRef.current) still updates on subsequent isDark changes;
this closes the race where isDark flips while the async import/fromTextArea is
in flight.
- Around line 120-127: The effect that syncs external value into the editor
(useEffect that calls editorRef.current.setValue) causes CodeMirror to emit a
change event which triggers the existing change handler (handleChange/onChange)
and echoes the same value back to the parent; fix this by introducing a
temporary guard flag (e.g., isSettingValueRef) that you set true immediately
before calling editorRef.current.setValue(value ?? '') in the useEffect and set
back to false immediately after, and update the change listener (the handler
referenced around lines 90–94) to early-return when isSettingValueRef.current is
true so external setValue does not invoke onChange.
🧹 Nitpick comments (1)
apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx (1)

52-55: Remove inline code comments.

The coding guidelines for this repository require avoiding code comments in implementation files. Lines 52, 72, 87, 115, 120, and 129 all contain comments (// Capture initial theme once, // added, // Sync external value → state, etc.) that should be removed.

As per coding guidelines: **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

Also applies to: 72-72, 87-87, 115-115, 120-120, 129-129

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 934ecab and 3fa944c.

📒 Files selected for processing (1)
  • apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx
🧠 Learnings (1)
📚 Learning: 2026-01-19T18:08:13.423Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38169
File: packages/ui-client/src/hooks/useGoToDirectMessage.ts:20-27
Timestamp: 2026-01-19T18:08:13.423Z
Learning: In React hooks, including custom hooks like useUserSubscriptionByName, must always be called unconditionally and in the same order on every render, per the Rules of Hooks. Passing empty strings or fallback values to hooks is the correct approach when dealing with potentially undefined values, rather than conditionally calling the hook based on whether the value exists.

Applied to files:

  • apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (2)
apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx (2)

140-147: Good: dedicated unmount effect for editor teardown.

Separating the cleanup into its own useEffect(…, []) is clearer than embedding it in the callback ref. The toTextArea() call restores the original textarea and the ref is properly nulled.


149-157: Rendering looks correct.

The hidden textarea with the controlled value and the forwarded ref is the standard CodeMirror integration pattern. readOnly on the textarea prevents user edits on the raw element before CodeMirror takes over.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants