Skip to content

CodeMirror stack 4/4: <datasette-sql-editor> element, dogfooded on core SQL pages#2837

Draft
asg017 wants to merge 2 commits into
asg017/codemirror-3-primitivesfrom
asg017/codemirror-4-element
Draft

CodeMirror stack 4/4: <datasette-sql-editor> element, dogfooded on core SQL pages#2837
asg017 wants to merge 2 commits into
asg017/codemirror-3-primitivesfrom
asg017/codemirror-4-element

Conversation

@asg017

@asg017 asg017 commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Part 4 (final) of the CodeMirror stack (integration view: #2833). The behavior-changing part: a <datasette-sql-editor> custom element, dogfooded on Datasette's own SQL pages.

What

The element

Form-associated, light-DOM custom element built on part 3's createSqlEditor (light DOM so Datasette/plugin CSS and CSS-variable theming keep working):

  • Attributes name / database / base-url / schema-url / default-table / readonly / autofocus; properties value, schema, view (raw EditorView escape hatch), readOnly, extensions; methods focus(), updateSchema(), format()
  • Events (bubbling): input {origin}, cancelable submit (default action form.requestSubmit()) on Mod/Shift-Enter, ready, editor-escape
  • Form participation via ElementInternals.setFormValue — the element sits directly in the ?sql= form, no hidden-textarea sync; participates in form reset. Degrades gracefully where attachInternals is unavailable
  • Schema fetched from {base-url}/{database}/-/editor-schema.json (or schema-url), never blocking: fetch failure → keyword-only completion + console.warn
  • Theming via CSS custom properties (--datasette-sql-editor-font-size/-bg/-font-family) with appearance-preserving fallbacks
  • Auto-registers on import (guarded); registerSqlEditorElement("other-tag") for custom tags

Dogfooding

The five SQL templates (database, query, query_create, query_edit, execute_write) now render the element wrapping a plain <textarea name="sql"> fallback — a real form field that keeps working with JavaScript disabled; the element adopts its value and removes it on mount (no duplicate field). _codemirror_foot.html shrinks to: assign the server-inlined rich schema (first-paint completions, no extra request), keep window.editor back-compat, wire Format SQL to element.format().

Two subtleties worth reviewer attention

  1. Parser-timing guard: the bundle loads from <head>, so the browser upgrades the element at its start tag — before its children (the initial SQL / fallback textarea) are parsed. Verified empirically in headless Chrome; without the guard every page's initial SQL would silently vanish. The element detects still-parsing + no children and defers mounting to DOMContentLoaded (document-level listener, so it runs before the foot script's window-level one).
  2. Submit semantics: Shift/Meta-Enter now go through the cancelable submit event → form.requestSubmit() (runs validation/submit handlers) instead of raw form.submit(). On mac Mod = Cmd as before; Ctrl-Enter now also works elsewhere.

window.editor is preserved (points at element.view). The cm IIFE global is no longer used by core templates but intentionally not deprecated here — separate decision.

Testing

Full suite green (2304 passed; markup assertions updated). Headless-Chrome verified on live pages: initial SQL preserved, window.editor live, form submits correct sql=, fallback adopted, schema completions returning table/column names. Manual QA page: demos/sql-editor-element.html (uv run datasette fixtures.db --static demos:demos/). Human browser pass still recommended: dark mode, resize, canned-query parameter refresh.

The stack

# PR Content
1/4 #2834 CM 6.x upgrade, option-name bugfix, rollup build
2/4 #2835 Rich schema autocomplete, /-/editor-schema.json, defaultTable
3/4 #2836 datasette-sql-editor ESM primitives
4/4 #2837 <datasette-sql-editor> element + core-page dogfood

Each PR is based on the previous branch; merge top-down, retargeting the next base to main as each lands. Bundle files (*.bundle.js) conflict on rebase by nature — resolve by re-running npm run build:codemirror, never by merging. Integration view of the whole branch: #2833.

🤖 Generated with Claude Code

asg017 and others added 2 commits July 10, 2026 11:15
Light-DOM element built on createSqlEditor(): form participation via
ElementInternals (name= field, reset support), schema fetched from
{base-url}/{database}/-/editor-schema.json or schema-url= without ever
blocking editing, cancelable submit event on Mod/Shift-Enter driving
form.requestSubmit(), readOnly/value/schema/view properties, format()
via the sql-formatter global, theming through CSS custom properties
with appearance-preserving fallbacks. Auto-registers the tag on import
(guarded). Manual-QA page at demos/sql-editor-element.html.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The five SQL pages now render the element wrapping a plain
<textarea name=sql> fallback that keeps working without JavaScript
(the element adopts and removes it on mount). The element also gained
a parser-timing guard: when its definition loads from <head> the
browser connects it at the start tag before its children are parsed,
so mounting defers to DOMContentLoaded in that case. window.editor
back-compat preserved; cm global deprecation deferred.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant