Skip to content

Replace Monaco with CodeMirror#36764

Merged
silverwind merged 156 commits intogo-gitea:mainfrom
silverwind:cm
Mar 31, 2026
Merged

Replace Monaco with CodeMirror#36764
silverwind merged 156 commits intogo-gitea:mainfrom
silverwind:cm

Conversation

@silverwind
Copy link
Copy Markdown
Member

@silverwind silverwind commented Feb 26, 2026

  • Replace monaco-editor with CodeMirror 6
  • Add --color-syntax-* CSS variables for all syntax token types, shared by CodeMirror, Chroma and EasyMDE
  • Consolidate chroma CSS into a single theme-independent file (modules/chroma.css)
  • Syntax colors in the code editor now match the code view and light/dark themes
  • Code editor is now 12px instead of 14px font size to match code view and GitHub
  • Use a global style for kbd elements
  • When editing existing files, focus will be on codemirror instead of filename input.
  • Keyboard shortcuts are roughtly the same as VSCode
  • Add a "Find" button, useful for mobile
  • Add context menu similar to Monaco
  • Add a command palette (Ctrl/Cmd+Shift+P or F1) or via button
  • Add clickable URLs via Ctrl/Cmd+click
  • Add e2e test for the code editor
  • Remove window.codeEditors global
  • The main missing Monaco features are hover types and semantic rename but these were not fully working because monaco operated only on single files and only for JS/TS/HTML/CSS/JSON.
Monaco (main) CodeMirror (cm) Delta
Build time 7.8s 5.3s -32%
JS output 25 MB 14 MB -44%
CSS output 1.2 MB 1012 KB -17%
Total (no maps) 23.3 MB 12.1 MB -48%

Fixes: #36311
Fixes: #14776
Fixes: #12171

image

- Replace monaco-editor with @codemirror/* packages for the code editor
- Use @codemirror/language-data for automatic language detection
- Use @lezer/highlight classHighlighter for CSS class-based syntax highlighting
- Define --color-syntax-* CSS variables in both themes, shared by CodeMirror 6 and EasyMDE
- Move all editor styling to CSS (codeeditor.css), no CSS-in-JS
- Add placeholder, rectangularSelection, crosshairCursor, highlightSelectionMatches extensions
- Add translatable placeholder text for the editor textarea
- Add e2e test for the code editor
- Remove MonacoWebpackPlugin and monaco-editor dependencies

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Feb 26, 2026
@silverwind silverwind added the type/enhancement An improvement of existing functionality label Feb 26, 2026
The function was only returning false after Monaco removal and served
no purpose. Flatten the remaining error check into a single condition.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@wxiaoguang

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR replaces the existing Monaco-based repository code editor with a CodeMirror 6 implementation to improve mobile usability and reduce frontend build/bundle size, while also aligning syntax highlighting across the code editor and EasyMDE via shared CSS variables.

Changes:

  • Remove Monaco (and its webpack worker/plugin setup) and introduce a new CodeMirror 6 editor module with matching CSS.
  • Add shared --color-syntax-* theme variables and update EasyMDE highlighting to use them.
  • Add a Playwright e2e test to verify editor-to-textarea synchronization.

Reviewed changes

Copilot reviewed 20 out of 22 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
webpack.config.ts Removes Monaco webpack plugin/worker configuration.
web_src/js/modules/codeeditor.ts New CodeMirror 6 editor implementation (creation, language detection, option handling).
web_src/js/features/repo-editor.ts Switches repo editor to the new CodeMirror module and updates value-setting logic.
web_src/js/features/repo-settings.ts Switches git hook editor to the new CodeMirror module.
web_src/js/globals.d.ts Clarifies exported window.codeEditors contents for customization.
web_src/js/bootstrap.ts / web_src/js/bootstrap.test.ts Removes Monaco-specific ignored-error logic and its unit test.
web_src/css/themes/theme-gitea-{light,dark}.css Adds new --color-syntax-* variables for highlighting.
web_src/css/modules/codeeditor.css New CodeMirror-focused styling for the code editor UI.
web_src/css/index.css Switches code editor CSS import from features/ to modules/.
web_src/css/easymde.css Updates EasyMDE token colors to use shared --color-syntax-* variables.
web_src/css/features/codeeditor.css Removes Monaco-specific styling.
web_src/css/codemirror/{light,dark}.css Removes legacy CodeMirror theme overrides/imports.
templates/repo/editor/{edit,patch}.tmpl Adds a textarea placeholder used by the new CodeMirror editor.
options/locale/locale_en-US.json Adds translation for the new placeholder string.
tests/e2e/codeeditor.test.ts Adds e2e coverage for editor-to-textarea updates.
package.json / pnpm-lock.yaml Removes Monaco deps and adds CodeMirror 6 + Lezer highlight deps.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The drawSelection() extension renders the selection layer behind the
content layer, so the active line background was covering it entirely.
Override CodeMirror's transparent ::selection with the selection color
at higher specificity so native selection renders on top of everything.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@silverwind
Copy link
Copy Markdown
Member Author

One line, select all + highlight

Fixed in 3734024.

silverwind and others added 4 commits February 26, 2026 16:59
Address Copilot review feedback:
- Align default indent size to 4 to match the UI dropdown default
- Unify indent style/size change handlers so both indentUnit and tabSize
  update together when either dropdown changes
- Use synthetic .sh filename for git hooks to enable shell highlighting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The drawSelection() extension renders selection behind the content
layer, so the active line background was covering it. Disable the
active line highlight to let the selection layer show through cleanly.
Also increase ::selection specificity to properly override CodeMirror's
injected styles for text color.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use a semi-transparent active line highlight (40% opacity via
color-mix) so it's visible but doesn't dominate the selection.
Override ::selection at higher specificity to render native selection
on top of the active line, ensuring text selection is clearly visible
in both light and dark themes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace color-mix with direct hex colors for the active line highlight,
keeping the blue hue but much closer to the code background so the
selection remains clearly visible on top.

Dark: #1a2433 (was #193450), Light: #ecf2f8 (was #d9e6f3)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@silverwind silverwind marked this pull request as draft February 26, 2026 16:31
@silverwind silverwind marked this pull request as draft February 26, 2026 16:31
- Set uniform 12px font size on .cm-editor for consistent gutter/content sizing
- Use SVG octicon chevrons for fold gutter markers
- Use octicon-kebab-horizontal for fold placeholders
- Move search panel to top of editor
- Restyle search panel with GitHub-like two-row flexbox layout
- Use proportional font in search/replace panels
- Override all CodeMirror hardcoded colors with CSS variables
- Theme buttons, textfields, tooltips, and checkboxes to match Gitea

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@silverwind
Copy link
Copy Markdown
Member Author

many styling fixes done, here is the search panel now:

image

Merge chroma/base.css, chroma/light.css, and chroma/dark.css into a
single modules/chroma.css where all colors reference --color-syntax-*
CSS variables defined in each theme. This makes syntax highlighting
fully themeable without per-theme chroma files.

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

@bircni bircni left a comment

Choose a reason for hiding this comment

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

trim_trailing_whitespace is now only done in backend - previously I think it was also done in frontend?

@silverwind
Copy link
Copy Markdown
Member Author

trim_trailing_whitespace is now only done in backend - previously I think it was also done in frontend?

CodeMirror does not have such a feature currently, but maybe it can be re-implemented.

silverwind and others added 3 commits February 26, 2026 19:01
Read the trim_trailing_whitespace setting from .editorconfig (already
passed by the backend) and apply it in the CodeMirror editor:
- Visually highlight trailing whitespace via highlightTrailingWhitespace()
- Trim all trailing whitespace on commit via trimTrailingWhitespaceFromView()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@silverwind
Copy link
Copy Markdown
Member Author

CodeMirror does not have such a feature currently, but maybe it can be re-implemented.

Implemented in 8f09499.

@silverwind silverwind marked this pull request as ready for review February 26, 2026 18:08
@bircni
Copy link
Copy Markdown
Member

bircni commented Feb 26, 2026

CodeMirror does not have such a feature currently, but maybe it can be re-implemented.

Implemented in 8f09499.

Cool already saw it!! 😄

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
@silverwind
Copy link
Copy Markdown
Member Author

silverwind commented Mar 30, 2026

Color unification done. Now light and dark use the same hue for all colors, just different lightness. Color scheme is roughtly based on dark theme, but did a number of tweaks. All colors pass WCAG AA.

Screenshot 2026-03-30 at 19 55 27 Screenshot 2026-03-30 at 19 55 01 Screenshot 2026-03-30 at 19 55 19 Screenshot 2026-03-30 at 19 55 09

Another notable change is that text in diff word highlights now uses monochrome text color, which ensures there can be no contrast problems any more.

Screenshot 2026-03-30 at 19 57 59 Screenshot 2026-03-30 at 19 57 44

@bircni bircni self-requested a review March 30, 2026 18:22
@bircni
Copy link
Copy Markdown
Member

bircni commented Mar 30, 2026

When you're ready feel free to tag me

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
@silverwind
Copy link
Copy Markdown
Member Author

It is ready for review. All monaco feature besides hover tooltips are implemented. Besides a few small tweaks, I don't expect any more major changes.

@bircni
Copy link
Copy Markdown
Member

bircni commented Mar 30, 2026

Could you update the description? it still states webpack

@lunny
Copy link
Copy Markdown
Member

lunny commented Mar 30, 2026

Could we disable the collapses? I think they’re always bothering me.

image

@silverwind
Copy link
Copy Markdown
Member Author

silverwind commented Mar 31, 2026

Could you update the description? it still states webpack

I will benchmark size and speed again later.

Could we disable the collapses? I think they’re always bothering me.

Yes the fold chevrons are a bit visually distracting. I will probably make them only show when cursor hovers over that column, like in VSCode.

silverwind and others added 3 commits March 31, 2026 13:06
Show fold triangles only on gutter hover, reduce their size to 13px,
fix cut-off on first line, reduce fold placeholder to 13px, and add
--color-editor-selection variable for text selection background.

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Use replaceWith to atomically swap the loading placeholder with the
editor container, and add min-height: 90vh to the container to match
the loading placeholder height.

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
@silverwind
Copy link
Copy Markdown
Member Author

All done:

  • Fold chevrons only show on hover like in VSCode
  • Fixed a rendering bug in chevrons
  • Made editor loading sequence stable without content shifts
  • Extracted a CSS variable for the selection color and made in brighter on dark

@bircni
Copy link
Copy Markdown
Member

bircni commented Mar 31, 2026

Code review for #36764 (by Claude Sonnet 4.6)

Issues to address before merging:

  • Scoped DOM queriesdocument.querySelector('a[data-tab="preview"]') and similar selectors (already FIXMEd) should be scoped to the editor container to avoid matching unrelated elements when multiple editors exist
  • Clipboard error handlingnavigator.clipboard.readText() can throw on permission denial; wrap in try/catch
  • Non-null assertionsquerySelector('.js-code-find')! will throw if the element is absent; verify these are truly guaranteed or add guards
  • JSONC detection/(tsconfig|devcontainer|jsconfig)/i is a fragile hardcoded list; will need ongoing maintenance
  • CSS missing fallbacksvar(--color-syntax-link) etc. have no fallback values; elements become invisible if a theme doesn't define them
  • Brittle CSS selectors.cm-panel.cm-search br + * will silently break if CodeMirror changes its DOM structure upstream
  • Test coverage — the single e2e test only checks textarea sync; command palette, language detection, keybindings, and indent/wrap toggles are untested

Minor:

  • MenuItem union type: prefer {type: 'separator'} | {type: 'item', ...} over {...} | 'separator'
  • Hard-coded z-index: 301 on .cm-command-palette needs coordination with other modals
  • Config assembly split between editor_util.go and editor.go (Autofocus, Filename set separately) — consolidate in one place

silverwind and others added 3 commits March 31, 2026 13:59
Move lintGutter into getLinterExtension so it only appears when a
linter is active. Skip linter for StreamLanguage-based languages
which cannot produce Lezer error nodes. Avoid double-loading the
language by passing the loaded result directly.

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
@silverwind
Copy link
Copy Markdown
Member Author

Scoped DOM queriesdocument.querySelector('a[data-tab="preview"]') and similar selectors (already FIXMEd) should be scoped to the editor container to avoid matching unrelated elements when multiple editors exist

There's only one editor per page currently. The FIXME is already there acknowledging this needs a broader refactor.

Clipboard error handlingnavigator.clipboard.readText() can throw on permission denial; wrap in try/catch

Fixed in 5bbf069.

Non-null assertionsquerySelector('.js-code-find')! will throw if the element is absent; verify these are truly guaranteed or add guards

These are inside an if (elEditorOptions) guard. The child elements are guaranteed by the template when the parent exists. Using ! over ?. is the project convention.

JSONC detection/(tsconfig|devcontainer|jsconfig)/i is a fragile hardcoded list; will need ongoing maintenance

This is the same approach VSCode takes. These are the well-known JSONC filenames. The regex is easy to extend if needed.

CSS missing fallbacksvar(--color-syntax-link) etc. have no fallback values; elements become invisible if a theme doesn't define them

Both themes always define all variables. Custom themes inherit from the default themes. Fallback values would be dead code.

Brittle CSS selectors.cm-panel.cm-search br + * will silently break if CodeMirror changes its DOM structure upstream

Inherent to styling CodeMirror internals. These selectors are pinned to the current CodeMirror version and would be reviewed on upgrades.

Test coverage — the single e2e test only checks textarea sync; command palette, language detection, keybindings, and indent/wrap toggles are untested

Valid, but a separate effort. More e2e tests can be added incrementally.

MenuItem union type: prefer {type: 'separator'} | {type: 'item', ...} over {...} | 'separator'

The current form is more concise and the === 'separator' check reads naturally.

Hard-coded z-index: 301 on .cm-command-palette needs coordination with other modals

It's positioned above CodeMirror's internal z-indexes (which go up to 300) and below Gitea's modal overlays. Works correctly.

Config assembly split between editor_util.go and editor.go (Autofocus, Filename set separately) — consolidate in one place

Intentional: editor_util.go builds the shared config from .editorconfig, while editor.go adds route-specific overrides like Autofocus and Filename which depend on request context.


This comment was written by Claude Opus 4.6.

@silverwind
Copy link
Copy Markdown
Member Author

#36764 (comment) is updated and includes a table of build size and performance.

This rule enablement belongs in a separate PR.

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
silverwind and others added 6 commits March 31, 2026 22:49
Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Extend goToDefinitionAt to recognize identifier nodes from Go, Java,
Rust, and C++ Lezer grammars in addition to JS/TS. Extend
collectSymbols to find Go functions, methods, and type declarations.

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Listen on document instead of document.body for scroll events.

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Block all keyboard input while context menu is open except Escape
and Enter. Use capture phase to prevent CodeMirror from handling
arrow keys.

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
…verrides

Use the existing clippie library for Cut/Copy clipboard operations instead
of raw navigator.clipboard.writeText. This handles errors internally and
provides an execCommand fallback. Cut now only deletes text on successful
clipboard write.

Remove per-component kbd font-size overrides that are no longer needed
since the global kbd rule provides the correct size.

Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. modifies/dependencies modifies/frontend modifies/go Pull requests that update Go code modifies/templates This PR modifies the template files topic/code-linting type/feature Completely new functionality. Can only be merged if feature freeze is not active.

Projects

None yet

8 participants