[pull] develop from marktext:develop - #49
Merged
Merged
Conversation
…aps (#4407) * test(parity): muya engine xfail tests for #4406 gaps PG1,3,5,6,7,8,9,10,11,12,13 Encode 10 of the 15 confirmed muyajs→@muyajs/core functional-parity gaps as vitest `it.fails` regression tests in the engine. Each asserts the desired pre-migration behaviour and fails on develop today (counted as a pass by `it.fails`); when a fix lands the test passes and `it.fails` errors, forcing the fixer to drop the marker. Every test name is prefixed with its gap id (`PGn:`) so fix PRs can grep + flip it. - PG1 selection-change lacks block affiliation / ancestor type - PG3 autoCheck preference not consumed (task-list cascade lost) - PG5 binary/bitmap clipboard image paste not persisted via imageAction - PG6 pasted image FILE bypasses imageAction (insert preference ignored) - PG7 export links core CSS from CDN instead of inlining it - PG8 exported headings carry no slug id (dead TOC anchors) - PG9 copyAsRich writes HTML source not rich text (no copyAsRich path) - PG10 preview-image not emitted (Space on a selected image) - PG11 heading-copy-link not emitted (hover-to-copy affordance gone) - PG12 hideLinkPopup not consumed (link hover popover not gated) - PG13 insertParagraph anchors to outermost not immediate nested block Allow the uppercase `PG` test-title prefix through antfu's `test/prefer-lowercase-title` rule (scoped to spec files) so the greppable gap-id naming coexists with the lint rule; all other test titles still must start lowercase. Suite stays GREEN: 512 pass + 20 expected-fail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(parity): desktop e2e xfail tests for #4406 gaps PG1,2,14,15 Add Playwright `test.fail()` regression specs for the desktop-side parity gaps. Each runs headless and currently fails (the gap), which `test.fail()` counts as a pass; removing `test.fail()` flips the entry green once fixed. Test names are prefixed with the gap id (`PGn:`) for grep-and-flip. - PG1 Paragraph menu does not check the current block type (affiliation gap; caret placed directly in an H1 content span — the engine-unit half lives in paritySelectionChange.spec.ts) - PG2 WYSIWYG caret not restored after a source-mode edit (handleFileChange drops muyaIndexCursor; no index→path cursor conversion in the engine) - PG14 first undo after exiting source mode does not revert the bulk source-mode change as one step - PG15 undo back to on-disk content leaves the tab marked unsaved (synthetic history id never re-matches the saved id) Undo is driven through the same `mt::editor-edit-action` IPC the Edit › Undo menu uses. Suite stays GREEN (4 expected-fail). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(parity): scoreboard index + manual-QA checklist for #4406 gaps Add the visible "how many gaps remain" board (PARITY_SCOREBOARD.md): a table of all 15 muyajs→@muyajs/core parity gaps → severity → test location(s) → status (all xfail now), plus the flip-to-green workflow for fix PRs. Add PARITY_QA.md: precise manual-QA checklists (exact steps + expected vs current result) for the two gaps that cannot be driven headless — PG4 drag-drop image insertion and the OS-clipboard/screenshot half of PG5 binary image paste. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(parity): destroy() muya instances in afterEach to detach DOM listeners Address Copilot review: the parity engine specs' cleanup only removed the host DOM node, never calling `muya.destroy()`, so the `document`-level keydown/click listeners selection registers during init leaked across tests — a flake / order- dependence risk as the suite grows. Track booted Muya instances and `destroy()` each in afterEach (detaches all DOM events + removes the node). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (PG13) (#4408) Restore the legacy immediate-anchor capability for `insertParagraph`. The engine method always resolved `_outmostBlockAtCursor()`, so inside a nested list item / blockquote the new paragraph landed adjacent to the OUTERMOST container instead of as an inner sibling next to the immediate block. Add a third `outMost` flag (default `false`): when false, anchor to the IMMEDIATE block-level parent of the active content leaf (mirrors the legacy `getAnchor`/`getParent` path used by the context-menu "Insert Paragraph Before/After" action); when true, keep the outermost-container behaviour used by "Create Paragraph Below" (which passes `outMost=true` explicitly). Top-level paragraphs are unaffected since the immediate parent and outermost block coincide. Flips parity scoreboard PG13 (parityInsertParagraphNested.spec.ts, 2 specs `it.fails` -> `it`) and adds a blockEditing.spec.ts regression locking the `outMost=true` path. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(muya): cascade task-list checkbox state when autoCheck is on (PG3) The autoCheck option was merged into muya.options (muya.ts setOptions) but no engine code consumed it, so the legacy muyajs task-list cascade was lost (parity gap PG3 of #4406). Restore it in the task-list checkbox handler: a user toggle now sets every descendant task item to the same checked state and re-derives each ancestor (checked iff all its siblings are checked), gated on muya.options.autoCheck. Cascaded items mutate state through the TaskListItem.checked setter so each change still dispatches its OT replace op; their checkboxes sync via syncDom. Flips the parityAutoCheck scoreboard specs from it.fails to it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(muya): gate link hover popover on hideLinkPopup (PG12) The hideLinkPopup option was merged into muya.options but the link-hover emitter (linkMouseEvents.ts#overHandler) emitted muya-link-tools unconditionally, so the popover always appeared on hover even when the user set hideLinkPopup: true (parity gap PG12 of #4406). Read muya.options.hideLinkPopup live in overHandler and return early when set, mirroring legacy muyajs eventHandler/mouseEvent.js. Optional chaining keeps the existing options-less test mock working. Flips the parityHideLinkPopup scoreboard spec from it.fails to it; the positive control stays green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(muya): expose block affiliation in selection-change (PG1) The `selection-change` payload only carried flat caret/range info, so the desktop Paragraph/Format menu state builder (`createApplicationMenuState`) could not reconstruct block context: no ancestor `affiliation` chain, and the engine blocks expose `blockName` rather than muyajs's `type` / `functionType`. As a result the Paragraph-menu check marks, Loose/Task-list toggles, table / code-fence detection and the in-code Format-menu disable were all dead. Re-derive the legacy `selectionChange` block-context from the @muyajs/core block tree in a focused `selection/affiliation.ts` helper and add three fields to the `selection-change` payload (existing fields untouched): - `affiliation` — shared ancestor PARAGRAPH-type chain (outermost-first), each entry carrying the markdown `type` (`p`, `h1`…`h6`, `ul`, `ol`, `li`, `pre`, `figure`, `blockquote`) plus list context (`listType`, `listItemType`, `isLooseListItem`). - `anchorBlockInfo` / `focusBlockInfo` — per-endpoint content-leaf info: `type` (always `span`) + `functionType` (`codeContent`, `cellContent`, `languageInput`, `paragraphContent`). Flips parity scoreboard PG1: the two `it.fails` PG1 tests are now real passing tests, plus two new tests pin the per-endpoint code-content info and list affiliation context. Desktop adapter consumption is wave 2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(muya): derive list-item affiliation context from the parent list (PG1) Address Copilot review on #4410: - `isLooseListItem` was read from a list-item block's own `meta.loose`, but `list-item` / `task-list-item` carry no `meta` — the loose/tight flag lives on the parent list (`bullet-list` / `order-list` / `task-list`). It was therefore always `false` for `li` entries, so the desktop "Loose list item" state could never enable. - `listItemType` for an `li` was mapped from the item's own `blockName` (`list-item` → `bullet`), but both bullet and ordered lists use the same `list-item` block, so ordered-list items were misclassified as `bullet`. Walk from a list-item up to its enclosing list block and read both the discriminator (`bullet` | `order` | `task`) and `meta.loose` from there. Add tests pinning ordered-list-item classification and loose-list detection on both the list and item entries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…yAsRich (PG5/PG6/PG9) (#4411) * feat(muya): add copyAsRich clipboard path for rich-text copy (PG9) Expose a public Muya.copyAsRich() backed by a new 'copyAsRich' copyType branch in the clipboard copyHandler. Unlike copyAsHtml (which blanks text/html and drops markup into text/plain as literal source), copyAsRich mirrors the 'normal' branch: rendered HTML in the text/html slot so a rich-text target (Word, email, contenteditable) renders formatting, with the markdown source in text/plain. Restores the legacy @muyajs 'Copy as Rich Text' behaviour (PG-COPYRICH). The desktop COPY_PASTE_METHOD_MAP.copyAsRich remap is wave 2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(muya): route pasted images through imageAction, restore bitmap paste (PG5/PG6) On paste, both the resolved clipboard FILE path (PG06) and an in-memory bitmap (PG05, screenshots / browser 'Copy Image') now flow through the embedder's options.imageAction so the insert preference (copy-to-assets / upload / keep-path) applies and a portable src is written — instead of linking the raw on-disk path verbatim (PG06) or inserting nothing (PG05). - types.ts: add IMuyaOptions.imageAction({ src, alt, title }) => Promise<string> and the IImageActionState shape, ported from legacy @muyajs. - utils/paste.ts: add getClipboardImageFile (reads clipboardData.files / items for an image File) and readFileAsDataURL (FileReader.readAsDataURL, falling back to Blob.arrayBuffer + btoa for the chrome70 target / Node tests). - clipboard: snapshot the image File synchronously before the first await, add tryPasteImage (file path then binary) + insertImageSrc (imageAction routing); split the raw markdown splice out as insertImageText. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(parity): flip PG5/PG6/PG9 to passing, update scoreboard + QA The clipboard image-paste + copyAsRich fixes land the engine behaviour, so convert the parity specs from it.fails to it: - parityImagePaste.spec.ts: PG5 (binary paste -> imageAction) + PG6 (x2, resolved path -> imageAction). - parityCopyAsRich.spec.ts: PG9 (x2, copyAsRich sets text/html=html and text/plain=text). Mark PG5/PG6/PG9 green on PARITY_SCOREBOARD.md (gaps remaining 12/15) and update PARITY_QA.md § PG5: the engine half is implemented; only the OS-clipboard delivery (real bitmap, macOS screencapture) stays manual. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(muya): guard readFileAsDataURL fallback; sync scoreboard mechanism (review) Address Copilot review on PR #4411: - readFileAsDataURL: guard the non-FileReader fallback for missing arrayBuffer/btoa (resolve '' instead of throwing out of Promise<string>) and base64-encode the bytes in 0x8000 chunks so a large blob avoids a huge per-byte intermediate string. - PARITY_SCOREBOARD: update the Mechanism column for PG5/PG6/PG9 from it.fails to 'passing it' now that those parity specs are flipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nchors (PG7/PG8) (#4412) * fix(muya): inline export CSS for offline self-contained HTML (PG7) MarkdownToHtml.generate linked github-markdown-css, katex, and prism from external CDNs via <link> tags, so a saved standalone .html (and any offline / CSP-restricted / air-gapped viewer) rendered unstyled. The legacy muyajs ExportHtml inlined those three core stylesheets as <style> blocks via ?inline imports, producing fully self-contained output. Restore that: inline the three base stylesheets by default. Keep the CDN shell available behind generate({ inlineStyles: false }) for callers that want a lighter document. Add github-markdown-css to muya deps (katex / prismjs already present) and enable Vitest CSS processing (css: true) so the inlined ?inline imports resolve to real content under test — without it Vitest stubs every CSS import to an empty string and would silently mask this regression. Flips parityExportHtml PG7 specs (it.fails -> it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(muya): inject github slug ids onto exported headings (PG8) @muyajs/core rendered exported headings via stock marked with no id, so in-document [TOC] / getHtmlToc `<a href="#slug">` anchors pointed at nonexistent targets — dead TOC links in exported HTML/PDF. The legacy muyajs export emitted `<hN id="{slug}">` using the same slugger as the TOC. Inject a github-compatible slug id (reusing the engine's existing generateGithubSlug) onto every h1..h6 in the export DOM, deduplicating collisions with a `-N` suffix. Scoped to MarkdownToHtml's export path only — renderToStaticHTML (the CommonMark/GFM conformance renderer) is left untouched, so spec conformance output does not change. Flips parityExportHtml PG8 specs (it.fails -> it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(muya): dedup exported heading ids by full slug (review) _injectHeadingIds deduplicated per base slug only, so a heading whose text already matched an earlier `-N` slug collided (e.g. `heading`, `heading`, `heading-1` emitted `heading-1` twice), breaking the anchor it was meant to fix. Switch to a Slugger-style "seen full slug" set, incrementing the suffix until the whole candidate id is unused, and seed it with any pre-existing heading ids. Add a chained-collision regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#4414) * feat(muya): emit preview-image on Space for a selected image (PG10) Legacy muyajs dispatched `preview-image` { data: src } from keyboard.js when an image was selected and the user pressed Space; the desktop renderer opened the full-screen SimpleImageViewer. @muyajs/core never emitted it — the image-selected keydown handler only acted on Backspace/Delete/Enter, so Space fell through to native handling (inserting a literal space) and the desktop subscription was dead. Restore the emit: when an image is selected and Space is pressed, the selection keydown handler resolves the image src the same way the Cmd/Ctrl-click preview path does (token src via getImageSrc, falling back to the rendered <img>'s src) and emits `preview-image` { data: src }, calling preventDefault so no space is inserted. Extracts the image keydown handler and the preview resolution into private methods to keep _listenSelectActions under the max-lines gate. Flips the PG10 parity scoreboard specs (it.fails -> it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(muya): add heading hover-copy affordance + heading-copy-link (PG11) Legacy muyajs rendered a hover affordance (i.icon.ag-copy-header-link) on each heading and dispatched `heading-copy-link` { key } on click; the desktop renderer copied the heading's GitHub slug/anchor to the clipboard (copyGithubSlug, which looks the key up against listToc by slug). @muyajs/core rendered no such affordance and never emitted the event, leaving copyGithubSlug unreachable. Restore both: - New HeadingCopyLink attachment block (mu-copy-header-link), appended to every atx/setext heading via appendAttachment (same mechanism as the task-list checkbox). It carries no document state, so markdown/HTML round-trip is unaffected. On click it emits `heading-copy-link` { key } and stops propagation. - The emitted key is the heading's stable slug — the SAME value getTOC() exposes as ITocItem.slug — so the host can resolve it back to a TOC entry. stableSlug is exported from state/getTOC for reuse (no new circular dep: getTOC only type-imports blocks). - Engine-level hover-reveal CSS in blockSyntax.css: the affordance sits in the left gutter, hidden by default and revealed on heading hover. Flips the PG11 parity scoreboard specs (it.fails -> it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(muya): make heading copy-link affordance accessible (review) Addresses the e2e a11y scan failure (critical image-alt) and Copilot review on the heading copy-link affordance: - The decorative icon <img> now carries alt="" — resolves the axe-core `image-alt` critical violation that failed the muya e2e a11y host scan. - The affordance is now a proper button: role="button", tabindex="0", and an i18n aria-label/title, so it is discoverable and focusable by assistive tech. - Keyboard activation (Enter / Space) mirrors click, so the focusable control is operable without a pointer. - Clarify (no behavior change) that the document-level image keydown handler is gated solely by `selectedImage`, which is only ever set by an in-editor image click and cleared on any document click — matching the legacy muyajs scope. A focus-in-editor gate was rejected because a selected image intentionally blurs the contenteditable (activeElement becomes <body>), so it would defeat the Space-preview feature. Adds PG11 spec coverage for the button semantics + Enter/Space activation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(muya): restore drag-and-drop image insertion (PG4) The @muyajs/core rewrite (#4406) shipped without any DnD handler, so dropping an image into the document was a no-op — parity gap PG4. Port the legacy muyajs dragDrop/dragDropCtrl behaviour as an engine-level, embedder-agnostic handler. `attachDragDropImageHandlers(muya)` binds dragstart/dragover/drop on the editor container (and dragleave for the ghost) via `eventCenter.attachDOMEvent`, so cleanup rides on `muya.destroy() → detachAllDomEvents()`. It is wired in `Editor.init()` alongside `attachLinkMouseHandlers`. Two drop paths mirror the legacy controller: - web-link image (`text/uri-list`): verify it is an image (extension or content-type sniff) then insert ``. - local image FILE (`dataTransfer.files`): resolve the file to a path via the new embedder `getPathForFile` hook, insert a `` placeholder, persist it through the new `imageAction` option (the same `{ src, alt, title }` contract the imageEditTool plugin consumes), then swap in the returned src. Two optional `IMuyaOptions` hooks are added (mirroring the existing `clipboardFilePath`): `imageAction` and `getPathForFile`. The engine stays free of `window.electron`; the desktop wires these in wave 2. Tested in `dragDropImage.spec.ts`: happy-dom provides a fully working `DataTransfer` (items.add / getAsString / files) and fires getAsString synchronously, so a synthetic `drop` event drives the real handler end-to-end — asserting both drop paths and the no-op-off-target case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(parity): mark PG4 engine half automated, note wave-2 desktop wiring PG4 (drag-drop image insertion) was a manual-QA-only entry because real drag gestures are hard headless. The engine handler now has a synthetic- DataTransfer unit test, so update the scoreboard and QA checklist: - PARITY_QA.md § PG4: describe what is now automated (both drop paths via the live handler), keep the OS-integration steps manual, and document the desktop wave-2 wiring needed for the local-file persistence path (pass `imageAction` / `getPathForFile` into the Muya constructor). - PARITY_SCOREBOARD.md: point PG4 at the new spec, flag the engine half fixed, and drop the remaining-gaps count 15 → 14. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(muya): address PG4 drag-drop review (ghost CSS, no-imageAction, web-image gating) Copilot review on #4413: - Add the `#mu-dragover-ghost` CSS rule (position/height/background) to `assets/styles/index.css`. The new engine had no ghost style, so the drop indicator was invisible — legacy muyajs shipped this rule. - When no `imageAction` hook is configured, insert a clean `` with the raw path verbatim instead of a `` placeholder that would never be swapped (it persists only when imageAction resolves). Matches the documented `imageAction` contract and imageEditTool's direct-replacement behaviour. - Gate the web-link path on the legacy "image dragged from a browser" signature — `text/uri-list` + `text/html` and NO `text/plain` — in both `dragover` and `drop`. A plain hyperlink drag (uri-list + text/plain) is now left to the browser instead of being intercepted and swallowed by `preventDefault()`. Tests updated: web-link drags use the realistic (uri-list + html) payload, plus new cases for the no-imageAction clean insert and the plain-hyperlink pass-through. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… heading-link, source cursor, saved indicator) (#4415) * fix(desktop): map copyAsRich to the engine copyAsRich method (PG9) The legacy "Copy as Rich Text" command was remapped to copyAsHtml, which blanks text/html and puts the HTML source into text/plain, so pasting into Word/email yielded raw HTML markup as literal text. #4411 added a real Muya.copyAsRich() that writes rendered HTML to text/html and plain text to text/plain; point COPY_PASTE_METHOD_MAP.copyAsRich at it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(desktop): wire heading-copy-link to copyGithubSlug (PG11) #4414 made the engine attach a hover-to-copy affordance to every heading and emit heading-copy-link { key } (key == the heading's stable slug) on click. Re-subscribe in editor.vue and forward the key to editorStore.copyGithubSlug, which copies `#<githubSlug>` to the clipboard — restoring the heading-anchor copy affordance that was a documented gap after the @muyajs/core migration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(desktop): match exported-TOC anchors to engine heading ids (PG8) editor.vue now exports via @muyajs/core (#4406), and #4412 injects github-compatible heading ids onto exported headings (deduped in document order with a `-N` suffix). getHtmlToc still slugged via the legacy muyajs Slugger, so `href="#slug"` targets no longer matched the injected ids and TOC / [TOC] links were dead. Swap to @muyajs/core's generateGithubSlug and replicate the engine's whole-document `-N` dedup so the anchors resolve. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(desktop): consume selection-change affiliation for menu state (PG1) adaptSelectionChange hardcoded affiliation:[] and copied changes.type ('Caret'/'Range') into start.type/end.type, so createApplicationMenuState's `start.type === 'span'` and functionType guards never fired — the native Paragraph-menu check marks, loose/task-list toggles, table/code-fence detection, and Format-disable-in-code all went dead after the @muyajs/core migration. #4410 added an `affiliation` chain (outermost-first) plus per-endpoint `anchorBlockInfo`/`focusBlockInfo` (`type: 'span'` + `functionType`) to the selection-change payload. Map them onto the legacy shape: - start/end `.type` and `.block.functionType` from the leaf block info, - affiliation passed through, with a derived `functionType` surfaced on `pre`/`figure` containers so table / code-fence detection lights up. Also fix the consumer's loose-list read: the engine affiliation entry carries `isLooseListItem` on the list block directly, not via a `children` chain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(desktop): restore saved indicator on undo-to-disk (PG15) makeSyntheticHistory minted an ever-incrementing editSeq id on every json-change (including undo/redo), so after edit-then-undo-to-saved the id never matched lastSavedHistoryId and the tab stayed marked dirty even when its content matched disk. Derive the synthetic id from the engine undo-stack DEPTH instead — a stable position marker that returns to its saved value when an edit is undone back to the baseline. Seed lastSavedHistoryId to 0 (the engine's post-setContent baseline depth, since setContent clears history) so a freshly-loaded, never-saved document clears its dirty indicator when undone back to disk content, mirroring the legacy history-index behaviour. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(muya): add setCursorByOffset for source-mode cursor restore (PG2) The source-code -> WYSIWYG handoff carries only a CodeMirror {line, ch} index cursor; @muyajs/core had no index-offset -> block-path conversion, so the WYSIWYG caret was lost. Add Muya#setCursorByOffset, reproducing the legacy muyajs approach: inject sentinel strings into the current markdown at the line/ch offsets, rebuild the tree (sentinels embed as literal text), find the content blocks they landed in, then rebuild the clean document and set the cursor by the resolved block paths + offsets. Both setContent calls run synchronously so no intermediate paint occurs, and the method is a no-op for stale/unresolvable cursors. Engine helper only — desktop wiring lands in a separate commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(desktop): restore WYSIWYG caret after source-mode edit (PG2) handleFileChange dropped the saved muyaIndexCursor on the source-code -> WYSIWYG handoff, so the caret was lost. Consume the new engine Muya#setCursorByOffset: when the tab has no key-based cursor but carries a CodeMirror {line, ch} index cursor, map it onto a block-key cursor so the caret lands where the source-mode cursor was. Restore the per-tab engine history afterwards (setCursorByOffset re-runs setContent internally, which clears history). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(desktop): flip parity e2e for PG1/PG2/PG15, defer PG14 Remove the `test.fail()` markers on the PG1 (Paragraph-menu check mark), PG2 (source-mode caret restore), and PG15 (saved indicator on undo-to-disk) parity e2e tests now that the desktop wiring lands — all three genuinely pass. PG14 (first undo after source mode reverts the bulk edit in one step) stays `test.fail()`: recording the source-mode change as a single undo boundary needs a general whole-document json1 diff through Editor.updateContents' pick/drop walker, which only handles specific op shapes and would risk corrupting the document. Deferred with an explanatory note here and in handleFileChange. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(test): reconcile parity scoreboard after wave-2 (14/15 fixed) Update the Status column to the true post-merge state: all seven Wave-1 engine PRs (#4408-#4414) plus the Wave-2 desktop wiring close PG1-PG13 and PG15. Fix the "Gaps remaining" count (11 -> 1), add the PG2 setCursorByOffset engine spec, and document why PG14 is accept-deferred (single-undo-boundary across the source-mode handoff needs a whole-document json1 diff the op walker can't safely apply). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address Copilot review on parity wave-2 - editor.vue adaptSelectionChange: restore start/end block.text from the live anchorBlock/focusBlock so SELECTION_CHANGE can still slice the selected text (search prefill); the previous {functionType}-only block dropped it. - editor.vue isIndexCursor: validate both line AND ch are numbers (factored out isIndexPosition) so a missing ch no longer silently clamps to column 0. - muya setCursorByOffset: snapshot getHistory() and restore it after the internal setContent rebuild so the public API is caret-only and does not clear the undo stack; document the behaviour and cover it with a test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )