Skip to content

feat(editor): add experimental canvas renderer - #2988

Merged
bajrangCoder merged 15 commits into
mainfrom
feat/canvas-renderer
Oct 25, 2026
Merged

bajrangCoder merged 15 commits into
mainfrom
feat/canvas-renderer

Conversation

@bajrangCoder

@bajrangCoder bajrangCoder commented Oct 25, 2026 •

Copy link
Copy Markdown
Member
  • Adds an experimental canvas renderer (Settings → Editor → Renderer), backed by @acode.app/canvas-view, which draws the editor with WebGL2/WebGPU instead of the DOM. The default DOM renderer is unchanged.
  • With the canvas renderer, Acode's themes, fonts and display settings map to canvas-view options; the touch selection menu, read-only handling, color chips/picker, lint underlines (also on touch devices), selection matches and the active indent guide all work on canvas views, and CodeMirror's fold gutter is left out.
  • Canvas-only settings, listed only while the canvas renderer is selected: backend (WebGL2/WebGPU), sticky scroll and its max lines, gutter marker layout, cursor blink rate and scroll speed. The line height setting now applies to the editor's lines.
  • Bumps @codemirror/view, state and language to the versions canvas-view requires, so a single copy of each is installed.

bajrangCoder and others added 11 commits October 20, 2026 15:11
Resolve canvas-view's imports (and their types) from Acode's own
node_modules, so CodeMirror's state and view stay single instances.
Renderer: DOM (default) or Canvas (experimental), applied on reload.
With editorRenderer set to canvas, editors are @acode/canvas-view
CanvasViews. Settings that the DOM editor applies through extensions
(line numbers, folding, guides, rainbow brackets, whitespace, wrapping,
scroll past end, RTL) become canvas options, kept in sync on changes.
Themes map to a CanvasThemeSpec (config colors + the theme's
HighlightStyle). The selection menu is driven by the view's touch
selection hooks, reusing the existing menu UI.
…erer

The canvas renderer draws its own fold markers, but foldGutter() was
still in the state, and its markers plugin rebuilt (unused) markers for
the viewport on every update and tab switch. Canvas views now get only
the folding state (codeFolding) with indentation folding.
isViewEditable read contentDOM's contenteditable and aria-readonly
attributes. A canvas view's contentDOM is its hidden input element,
which has neither, so taps on color chips (regex and LSP document
colors) were swallowed as if the editor were read-only. Read the
state's editable and readOnly facets instead, which is what the DOM
editor's attributes reflect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Acode's read-only setup works around the DOM editor on mobile: it makes
the content non-editable, blurs on every focus so no keyboard opens,
and shows CodeMirror's cursor layer with CSS. A canvas view handles
read-only documents itself: it ignores typing, cut, paste and drops,
takes focus with inputmode="none" (so its cursor blinks and hardware
shortcuts work, without a keyboard), and drops focus when it becomes
read-only. For canvas views the extension is now only
EditorState.readOnly plus the filter for Acode's own editing commands,
and blurEditorIfReadOnly / focusEditorIfEditable leave them focused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the canvas renderer:
- highlightSelectionMatches is on (it's off by default for the DOM
  editor), drawn with the theme's selectionMatch color, or its
  selection color, as the themes' CSS does;
- indent guides highlight the block around the cursor (one rect per
  frame on the canvas);
- the base extensions leave out drawSelection, dropCursor,
  highlightActiveLine (and its gutter) and highlightSpecialChars,
  which the canvas draws itself. The canvas view only skipped their
  plugins; the active line gutter still rebuilt its markers on every
  selection change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On touch devices the LSP linter drops every in-text mark (markerFilter),
so only the lint gutter showed an error. Canvas views draw the wavy
underlines and lint points natively, without the DOM editor's layout
cost, so they keep them; hover tooltips stay filtered on touch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ColorChipWidget gets toCanvas(): the same box toDOM styles (0.9em,
2px margins, rounded, a faint border), in cells of the view's font.
Canvas views then make no element per chip; they call toDOM only when
a chip is tapped, so the color picker still opens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The font theme set `lineHeight` on the editor element, but CodeMirror's
base theme sets `line-height: 1.4` on `.cm-scroller`, so the setting
never reached the lines or gutters: the DOM editor was always 1.4 while
canvas views used the setting, and the two renderers didn't match.

Set it on `.cm-scroller`, so both renderers use it (canvas-view measures
it like CodeMirror, to the DOM's exact value). The default becomes 1.4,
what the editor always showed, and stored values of the old default (2),
which nobody ever saw, are migrated once so editors don't change size.
Two settings for the canvas renderer, next to "Renderer":
- "Canvas backend": Auto (WebGL2), WebGL2 or WebGPU (falls back to
  WebGL2 where unavailable). Views are created with it, so a change asks
  to reload when the canvas renderer is on.
- "Sticky scroll": keeps the header lines of the scopes around the top
  line pinned while scrolling. Applies live to canvas views.
@github-actions github-actions Bot added enhancement New feature or request translations Anything related to Translations Whether a Issue or PR labels Oct 25, 2026
canvas-view is now published as @acode.app/canvas-view, so depend on it
instead of a local `bun link` checkout and drop the rspack and tsconfig
workarounds that kept the linked copy on Acode's CodeMirror packages.

Raise @codemirror/view, state, language and @lezer/common to the
versions canvas-view requires, in dependencies and overrides, so a
single copy of each is installed.

The newer view flushes on every selectionchange, and happy-dom fires
that event synchronously from Selection methods, which re-entered
EditorView.update in the read-only cursor test. Queue it there like a
browser does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	bun.lock
#	package-lock.json
#	package.json
@greptile-apps

greptile-apps Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issues were established.

Summary

Adds an opt-in canvas editor while keeping DOM as the default.

  • Acode can draw editor panes with Canvas while keeping the DOM renderer as the default.
  • Canvas editors keep Acode's touch, read-only, color, and lint features.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Saved renderer setting] --> B{Canvas selected and supported?}
  B -->|No| C[Create DOM editor]
  B -->|Yes| D[Create canvas editor]
  D --> E{Construction succeeds?}
  E -->|Yes| F[Sync themes, fonts and display options]
  E -->|No| G[Rebuild DOM state]
  G --> C
Loading

Reviews (3) · Last reviewed commit: "feat(settings): more canvas renderer set..." · Reviewed by Greptile

Comment thread src/settings/editorSettings.js Outdated
Comment thread src/lib/editorManager.js
Comment thread src/lang/index.d.ts
- Flush pending cache writes and save the session before the reload that
  applies a renderer or canvas backend change, as the pause handler does,
  so recent edits aren't lost.
- Include the renderer in the editor state signature, and give the DOM
  fallback pane a fresh empty state, so states built for canvas views
  (without fold controls and the active line) aren't reused by DOM ones.
- Declare the canvas backend and sticky scroll strings in LangStrings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bajrangCoder

This comment was marked as outdated.

Comment thread src/settings/editorSettings.js
- Cursor blink rate, sticky scroll max lines and gutter marker layout
  (shared column, column per gutter, over line numbers), mapped to the
  matching canvas-view options and applied live.
- Bring back the scroll speed setting, which canvas views use.
- Canvas-only settings (backend, sticky scroll, its max lines, gutter
  markers, cursor blink rate, scroll speed) are listed only while the
  canvas renderer is selected, and follow renderer changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bajrangCoder

This comment was marked as outdated.

@bajrangCoder
bajrangCoder added this pull request to the merge queue Oct 25, 2026
Merged via the queue into main with commit 98446ef Oct 25, 2026
11 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in The Code Board - Acode Oct 25, 2026
@bajrangCoder
bajrangCoder deleted the feat/canvas-renderer branch October 25, 2026 05:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request translations Anything related to Translations Whether a Issue or PR

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant