Skip to content

[pull] develop from marktext:develop#93

Merged
pull[bot] merged 8 commits into
code:developfrom
marktext:develop
Jun 23, 2026
Merged

[pull] develop from marktext:develop#93
pull[bot] merged 8 commits into
code:developfrom
marktext:develop

Conversation

@pull

@pull pull Bot commented Jun 23, 2026

Copy link
Copy Markdown

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 : )

Jocs and others added 8 commits June 23, 2026 17:18
…) (#4655)

* fix(muya): ArrowUp at the start of the document moves the caret to offset 0 (#3193)

Pressing ArrowUp on the first visual line of the first block (no previous
block) called preventDefault/stopPropagation and returned without moving the
caret, so the caret stayed put instead of jumping to the start of the line.

When there is no previous block, ArrowUp now moves the caret to offset 0 of
the current block (a boundary ArrowLeft still has nowhere to go and is left
to do nothing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(muya): don't re-set the selection on a no-op ArrowUp at offset 0 (#3193)

When the caret is already at offset 0 of the first block, ArrowUp has
nowhere to go, but the handler still called setCursor(0, 0, true) — which
unconditionally re-sets the selection (TextSelection.setSelection always
emits selection-change, with no equality check) and re-renders the block's
innerHTML. Guard the re-set with `start.offset !== 0` so a no-op ArrowUp
stays silent. preventDefault is still issued, so the native no-op scroll is
suppressed as before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nodes (#3315) (#4659)

`Muya.destroy()` only hid currently-visible float tools; it never invoked each
registered UI plugin's `destroy()`. So nodes appended to `document.body` in
plugin constructors/init (every BaseFloat's floatBox, the ImageResizeBar
`.mu-transformer` container) leaked permanently across destroy/rebuild — e.g.
when toggling to browser/source mode.

Iterate `_uiPlugins` in `destroy()` and call each plugin's `destroy()` (guarding
plugins that don't define one). Also add a `destroy()` to `ImageResizeBar`,
which appends its container at construction but had none.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…4656)

On a cross-block selection, the clipboard keydown handler cuts (replaces) the
selection for an editing keystroke, bailing out for modifier combos. The guard
only excluded macOS `metaKey`, never `ctrlKey` — so on Windows/Linux, pressing
Ctrl+C to copy fired the handler and deleted the selected text before the
browser's copy event ran.

Extract the modifier/key decision into a pure `shouldCrossBlockCut` helper and
exclude `ctrlKey` as well (mirroring the metaKey guard).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uote (#3462) (#4661)

`format()` dispatched to the cross-block path via `_selectionInSameBlock()`,
which compares the selection endpoints at OUTMOST-block granularity. Two
paragraphs nested in the same blockquote share an outmost block (the
blockquote), so the check reported "same block" and fell through to the
single-leaf path — which then early-returns because the anchor and focus
leaves differ, applying no formatting at all.

Dispatch inline formatting on LEAF equality instead via a new
`_selectionInSameLeaf()` helper (mirroring `_selectionEndpoints` but at leaf
granularity). The paragraph-menu path keeps its outmost-block check.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Theme menu listed all 33 themes as a single flat native submenu, which
on Windows expands to the full window height. Group them into nested
"Light Themes" / "Dark Themes" submenus so the top-level Theme menu stays
short (the alternative the issue suggests; CSS max-height can't constrain a
native Electron menu).

The same theme ids/labels/order are preserved — verified the new template
produces the identical 33 (label, id) set — so theme selection, the radio
checked state (recomputed on every menu rebuild via broadcast-preferences-
changed), and getMenuItemById lookups (recursive) are unaffected. The
repetitive per-theme blocks are replaced by two data tables + a small
builder.
…ent corruption (#2938) (#4658)

* fix(muya): drop the pending op batch on setContent to prevent cross-document corruption (#2938)

`JSONState.setContent` replaced `_state` but left any deferred operation batch
in `_operationCache` (with its scheduled requestAnimationFrame). Switching
files within the same frame as a pending edit then flushed the outgoing
document's op against the new document's state — corrupting it, or throwing
and leaving `_isGoing` stuck so all further edits froze. Either way the
switched-to file could no longer be saved correctly.

Track the scheduled flush's rAF handle; on `setContent` cancel it, clear
`_operationCache`, and reset `_isGoing` before swapping the state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(muya): fold the _isGoing flush guard into _rafId (#2938)

`_isGoing` (boolean "a flush is scheduled") and `_rafId` (the rAF handle)
were always set and cleared together, so they tracked the same state
redundantly. Drop `_isGoing` and use `_rafId !== null` as the batching
guard directly. No behavior change — the existing setContent/op-batching
tests stay green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(muya): add muya.flush() to apply queued edits synchronously (#2938)

The op cache flushes on a requestAnimationFrame, so a same-frame document
swap (a tab switch calls setContent right after the last keystroke) drops
the pending edit. Add a public flush() that runs the queued batch now and
emits json-change, so a caller can make the outgoing edit durable before
swapping. Extracted the rAF body into _flushOperationCache, shared by the
frame callback and flush().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(desktop): flush the outgoing editor before a tab switch (#2938)

On tab switch the store sets currentFile then synchronously reloads the
engine via setContent, so the previous tab's last same-frame keystroke
(still queued in the engine) was either lost or applied to the incoming
document. Flush the engine while currentFile still points at the outgoing
tab (via a new flush-active-editor bus event), so the edit is applied and
persisted under the right tab before the swap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) (#4657)

When a file's folder is moved/deleted while it is open, saving recreates
the directory tree and writes the file (fs-extra `outputFile`), matching
VS Code so an (auto)save never silently fails. This is intentional but was
untested — add unit coverage and a comment documenting the rationale so it
isn't mistaken for a bug and "fixed" by switching to a plain write.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#4644) (#4660)

Pressing Enter on the empty first paragraph of a loose list item (one that
holds multiple paragraphs) moved every paragraph from the caret's index into
a new sibling list item. When the caret was on the first paragraph (index 0),
this emptied the original list item, leaving a `list-item` with zero children.

A childless list item has no content descendant, so
`previousContentInContext`/`nextContentInContext` return null when arrow
navigation tries to cross it — Up arrow then stopped moving the caret up a
line, the symptom reported in #4644.

Split from the next paragraph when the caret is on the first one, mirroring
the plain-paragraph "Enter at offset 0" behaviour, so the original list item
keeps its empty first paragraph and stays valid.

Adds a unit test for the engine invariant and an e2e regression test driving
the exact reported keystroke sequence.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pull pull Bot locked and limited conversation to collaborators Jun 23, 2026
@pull pull Bot added the ⤵️ pull label Jun 23, 2026
@pull pull Bot merged commit 3e71b36 into code:develop Jun 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants