Enh: CodeMirror 6 code editor (HTML/JS/CSS, lazy-loaded) for HtmlBlock + Head Code #16
Reference in New Issue
Block a user
Delete Branch "enh-code"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Notepad/code editor with syntax highlighting + tag/attr autocomplete for HTML/JS/CSS via CodeMirror 6, lazy-loaded (main bundle +5.5KB; library in split chunks) with a textarea fallback. HtmlBlock gets an Edit HTML button; HeadCodeModal swaps its textarea for the editor. HtmlBlock purify unchanged. 665 tests.
🤖 Generated with Claude Code
Replaces the plain single-line input (HtmlBlock's `code` prop, via GenericPropsEditor) and the plain <textarea> (HeadCodeModal's site-wide head code) with a proper syntax-highlighted, tab-completing code editor. - New src/ui/CodeEditor.tsx: reusable CodeMirror 6 editor (state/view/ commands/autocomplete/language/lang-html/lang-css/lang-javascript/ theme-one-dark). All @codemirror/* packages are pulled in via a single dynamic import() inside the component so they land in separate lazy chunks instead of the main bundle -- confirmed via `npm run build`: main editor.js grew by only ~5.5KB (657KB -> 663KB raw) while ~570KB of CodeMirror source split into index*.js chunks that only load when a code editor modal is actually opened. While that import is in flight, or if it ever fails, the component renders a plain <textarea> so typing never breaks. - GenericPropsEditor.tsx: special-cases the `code` prop (used only by HtmlBlock today) into an "Edit HTML" button that opens the CodeEditor in a modal (language="html"), instead of rendering it as a single-line text input alongside the component's other string props. - HeadCodeModal.tsx: swaps its <textarea> for CodeEditor (language="html" -- head code is HTML with embedded <script>/<style>), keeping the existing SiteDesignContext.updateDesign({ headCode }) wiring. GuidedStyles.tsx untouched: HtmlBlock's displayName ("HTML") already matches GuidedStyles' `isUtility` regex and routes to GenericPropsEditor, so no dispatcher change was needed. HtmlBlock.tsx untouched: purifyHtml/toHtml sanitization is unchanged, as specified. Skipped the optional AnimationControl/VisibilityControl addition -- HtmlBlock has no dedicated StylePanel (it shares GenericPropsEditor with Divider/Spacer/every unmatched type), and doing it well would mean adding real Animation/Visibility widgets to that shared editor for every consumer, which is a bigger change than "trivial" for this package's scope. Tests: CodeEditor.test.tsx and HeadCodeModal.test.tsx both exploit the fact that dynamic import() always resolves on a later microtask, so asserting against the DOM immediately after the initial synchronous render deterministically exercises the <textarea> fallback path (value display, onChange wiring, language prop plumbing) without needing to mock @codemirror/*. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>