Every composer edits markdown source, on nldd-text-editor #279
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "nldd-text-editor-composer"
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?
Closes #168.
One commit on top of main.
Two changes, and the second does not need the first
1. NLDD is imported per component instead of through the barrel (WDR-0005).
import '@nldd/design-system'resolves to one pre-bundled chunk of all ~90 components; nothing tree-shakes out of it and Waggle renders 36.src/nlddImports.test.tsguards it by resolving each rendered tag through the package's own exports map - it caught a missing import fornldd-keyboard-shortcutwhen main's keyboard work merged in.2. Every composer edits markdown source, on
nldd-text-editor(WDR-0006). Tiptap is WYSIWYG over a node tree, and the markdown we send was reconstructed on the way out bytiptap-markdown. That reconstruction was where the bugs were:~town-squarecame out\~town-square, task lists gained a blank line, hard breaks needed a custom node. The new editor's document is the markdown, so there is nothing to reconstruct. The syntax would stay on screen; it is hidden here except on the construct the caret is in.Reply, new message and notes all use it. Notes stop being a second implementation of the same composer: they differ by one flag and one absent handler. Tiptap, ProseMirror and
tiptap-markdownleave the tree.What the subclass is for
<waggle-text-editor>extends the element over theview/buildExtensions()/reconfigure()seam it documents as protected, because four things are not in the public API:value =replaces the document and drops the caret to 0@and:typeaheads@-only and writes[@Label](user:id)where Mattermost wants@username; a secondautocompletion({override})throws a config merge conflict<u>/<mark>Plus task-list checkboxes (GFM parses
- [ ], nothing styles it) and a quote bar (the editor styles a quote italic with no bar, while NLDD's rendered blockquote has one).A rename of any protected member fails the build rather than failing silently - that is the reason to sit on those rather than on internals. The element is marked beta; that and
hideMarkers.test.tsare what to watch on an NLDD bump.Defects found and fixed
Each has a test that was first run against the broken behaviour.
aria-*to a prop, so a call site that omits it type-checks like one that passes it. The prop isaccessibleLabelnow, and required.defaultPreventedalready true, so the dialog's cancel never ran; CodeMirror does not. It now closes the link bar first, then leaves the field for the toolbar, then closes the window - the middle step is the way out now that Tab indents. Closing the link bar or emoji picker also used to drop focus on<body>.Mod-]. Tab now indents by two spaces on a fence content line and Shift+Tab outdents, never on the ``` line itself (four spaces there stop it being a fence). Both directions are written out rather than taken from CodeMirror, whoseindentLessrewrites a line's whole leading whitespace run and so converts pasted tabs to spaces - inside the one region shipped byte for byte.was the last marker still on screen** when the caret was elsewhere. Hiding it was blocked by the surface: NLDD tints a block's content lines and deliberately leaves the fence lines clean, so hiding the backticks left two blank rows around the tint, and an empty block - whose only lines are fence lines - vanished. The surface now covers the fence rows, in NLDD's own classes rather than a copy of its private tokens, with a test that fails if those class names go. Language name kept as a label; an unclosed fence keeps its.FileReaderand inserts the text, andrunHandlersonly callspreventDefault, so the event still reached the dropzone and uploaded it too. Measured: dropping anotes.txton a composer holdinghallogaveGEHEIM BESTANDSINHOUDhalloplus an attachment.doPaste(view, '')when the clipboard holds only files, which replaces the selection with nothing. Both are guarded now by adomEventHandlersextension that claims the event before the built-in and lets it bubble on to the uploader.:or@inside code opened the emoji or mention list, and Tab would have committed a pick into text Mattermost renders literally.>markers were hidden on the interior lines of a quoted code block, so the code read as unquoted while the source still carried the quote.defaultKeymapand indent any line by two spaces; two presses give four, which renders as<code>. macOS gives both keys to browser history, so they are swallowed rather than rebound. Tab nests a list item and Shift+Tab un-nests it, with toolbar buttons for both. On a line where indentation has no markdown meaning Tab keeps moving focus - one to three leading spaces are dropped by the renderer and the fourth makes a code block, so there is nothing useful for it to do.notesMarkswas applied once and never watched, andtoggleUnderline/toggleHighlightwrote a literal<u>whether or not the extension was loaded - only av-ifstood between that and a Mattermost message.rem, a unit that tracks the root font rather than the editor's line, at a size that ate the room the toolbar needed on a short window.Prop cleanup
A design round asked whether the options belong in a config object. They do not: after deleting what is dead there are three real feature switches, and two of them are dependencies whose presence is the switch. But getting to that count fixed things - five switches had no caller, and three features needed two props each to stay coherent (a paperclip and its handler, a mention fetcher and its account, marks and the buttons that toggle them). Those are one value each now. 27 declared props across the three layers become 21.
Tests the work exposed as missing
Bundle
Same tree, three builds,
gzip -9over every emitted.js:main(barrel + Tiptap)The 39% is change 1. The editor swap costs 27 kB: CodeMirror and
@lezer/markdownare larger than what they replace, and they share one copy with the element, which imports@codemirror/*as bare specifiers.Verification
typecheck,vitest1153,playwright113 (3 skipped),build,eslint0 errors,pre-commit. Driven by hand in the browser at each step.Two things worth knowing for the next person: a typo in an optional prop name produces no diagnostic (
strictTemplatesis off, and turning it on is 142 errors of its own), and HMR does not rebuild the adopted stylesheet inside a shadow root - a CSS change there needs a full reload before it means anything.e32d135d24f7d46c4ab6A second composer engine on nldd-text-editor, and NLDD imported per componentto Every composer edits markdown source, on nldd-text-editor763737f49d5b9f9bccff5b9f9bccffc5ea3cc7e0c5ea3cc7e0d52c7e4cb2d52c7e4cb26b88a4774d6b88a4774d2c33ac17bf2c33ac17bfd437b0f576d437b0f57621b3a8a84221b3a8a842ce0b36c254ce0b36c254f2ec9eec39f2ec9eec39e74c2169cfe74c2169cf2d4c3c4f842d4c3c4f84be3f5d94adbe3f5d94adde93ed721ede93ed721e5adca295a25adca295a2766a7f9f3d766a7f9f3d9557e63ea49557e63ea4bf91e11643bf91e116432451259a01