llama.cpp
ac007054 - ui: use a live-preview markdown editor for the chat form input (#31955)

Commit
3 days ago
ui: use a live-preview markdown editor for the chat form input (#31955) * ui: use a live-preview markdown editor for the chat form input Replace the contenteditable rich input with a CodeMirror editor that shows the markdown source of the block being edited and renders every other block through the MarkdownContent pipeline and stylesheet, so they match chat messages. Plain text types exactly as before, the value stays the exact markdown source, and mention links show as atomic badges. The editor lives in a shared MarkdownEditor component, and the highlight.js theme loader is shared between MarkdownContent and SyntaxHighlightedCode. The old rich input, its tokenizer, source history and their tests go away. Assisted-by: Claude (Opus 5.5 Medium) * ui: give the production build more memory, note what mdsvex is for The single-bundle build now includes CodeMirror and runs past Node's default heap limit on CI runners. Raise it in the build script, so local and CI builds behave the same on every platform. Assisted-by: Claude (Opus 5.5 Medium) * ui: keep the live preview's blocks while only the cursor moves Moving the cursor re-walked the syntax tree and rebuilt every block's decoration. Keep the blocks with the decorations and rebuild only when the text changes or the cursor enters another block. Assisted-by: Claude (Opus 5.5 Medium) * ui: point the chat form story at the editor and drop the engraved placeholder until the design system lands Assisted-by: Claude (Opus 5.5 Medium) * ui: name the markdown editor for screen readers CodeMirror's content is a textbox with no name of its own, so the chat form input was announced as an unnamed text box, and could not be found by its name as the old input could. It takes an aria-label, its placeholder by default, which makes the chat form's "Type a message..." again. Assisted-by: Claude (Opus 5.5 Medium) * ui: format the chat form input's classes Assisted-by: Claude (Opus 5.5 Medium) * ui: share the markdown styles and diagram drawing between messages and the editor The editor and MarkdownContent each @imported markdown-content.css into their own <style>, so the bundle carried two scoped copies of it. Both now render into a MarkdownSurface, the one element that carries the stylesheet; the few rules for MarkdownContent's own template elements are global under .markdown-content. Drawing mermaid and svg blocks and the actions of their header buttons move to markdown-diagrams.ts, so the editor's rendered blocks can draw diagrams the same way messages do. Assisted-by: Claude (Opus 5.5 Medium) * ui: harden the live preview editor after review - onChange skips only the editor's own syncs from `value`, marked by an annotation, instead of reporting only edits that carry a user event - Enter and the mention badges ask the syntax tree whether they are in code, so ~~~ fences and indented code count, and a closing fence ends a block - a picked mention is one undoable edit, through replaceText - rendered blocks draw mermaid and svg diagrams with their header buttons, a $$ math block with blank lines renders as one block, and a link reference definition stays raw instead of rendering to nothing - rendered blocks render again when the mention path settings change - a drag in a rendered block selects its text and copy takes it, across blocks; a click still opens the block - the block margins are the ones a chat message renders (each heading is a first child there), checked by a test; the blank lines after a raw block are its gap - the tree comes from syntaxTree, without forcing a full parse on every edit - placeholder, label, extensions, mention widgets and render key follow prop changes after mount, through compartments - the editor uses the markdown language alone, which drops the html, css and javascript parsers markdown() pulls in for html tag completion (-70 KB gzip) Assisted-by: Claude (Opus 5.5 Medium) * ui: pin the CodeMirror packages and set the build heap in .npmrc The new dependencies are pinned like the rest of package.json. The build heap moves from a node call on vite's bin path to npm's node-options, so the build script is plain `vite build` again and the setting reaches it on every platform. Assisted-by: Claude (Opus 5.5 Medium) * ui: update the live preview block by block and estimate block heights An edit used to build every block's decorations and widgets again, which the editor then compared one by one. The previous decorations, and the mention badges of raw blocks, now move through the edit, and only the blocks whose range, text, raw or rendered state, or gap changed get new ones; the texts of the blocks an edit left alone are reused instead of sliced again. A test drives random edits, cursor moves and focus changes and checks each result against a full build. Rendered blocks report the height they were last measured at, or a guess from their text, so CodeMirror's height map for blocks off screen in a long document stays close and scrolling stays steady. Assisted-by: Claude (Opus 5.5 Medium) * ui: skip the editor's redundant syncs, reconfigures and theme reloads - syncing `value` compares it with the text the editor last reported or synced, so a keystroke coming back through onChange no longer serializes the document again - the prop effects reconfigure the editor only when a prop changed, not once more right after it is created (which rebuilt the whole preview) - loadHighlightTheme leaves the theme's style element alone while the mode stays, so a message or an editor mounting no longer restyles the whole page Tests check that typing in one block leaves the other rendered blocks without a single DOM mutation, and that mounting reconfigures nothing. Assisted-by: Claude (Opus 5.5 Medium) * ui: simplify the live preview to a plain rebuild in three small modules Rebuilding the decorations of every block on an edit measures the same as patching only the changed ones (0.09 ms against 0.11 ms per keystroke at 200 blocks, 0.9 ms against 1.1 ms at 2000), and equal widgets keep their DOM either way, so the incremental update and its test go. - markdown-blocks.ts collects the document's blocks, markdown-block-widget.ts renders one, markdown-live-preview.ts decides which ones show raw text - one bounded cache holds a block's HTML and its measured height - the render key is a plain option: a new one reconfigures the preview, which keeps the editor's focus - MarkdownEditor follows its props with one helper instead of five effects Assisted-by: Claude (Opus 5.5 Medium) * ui: keep setext headings raw in the live preview editor `test` over `---` is a setext heading, so the preview rendered it as an h2 and the underline vanished once the cursor left the block. The editor now shows a setext heading's text as typed at all times, like a link reference definition. Assisted-by: Claude (Opus 5.5 Medium) * ui: add a setting to turn the live markdown preview off "Live markdown preview in the message input" (General) is on by default. Off, the input shows the plain markdown text, with file mention badges still in place; switching it takes effect without a reload. Assisted-by: Claude (Opus 5.5 Medium)
Author
Parents
Loading