Skip to content

[pull] develop from marktext:develop - #48

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

[pull] develop from marktext:develop#48
pull[bot] merged 5 commits into
code:developfrom
marktext:develop

Conversation

@pull

@pull pull Bot commented Jun 8, 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 5 commits June 8, 2026 17:52
…4401)

* fix(muya): treat CJK as punctuation for strong/em flanking (#4307)

Strong/em delimited with `**` directly against a CJK character whose inner
content is punctuation-bounded did not bold in muya, e.g.
`例子例子**"加粗"**例子例子`, `日本語**(強調)**日本語`, `한국어**[강조]**한국어`,
and the non-BMP `𠀀𠀁**"加粗"**𠀀𠀁`. The legacy muyajs engine bolded these.

CommonMark §6.2 classifies CJK ideographs / Hangul / Kana as "other" (Lo),
neither whitespace nor punctuation, so a `**` run wrapped by CJK with
punctuation-bounded content is not left/right-flanking and stays literal.
muya has TWO inline-tokenization paths and both carry the same CJK-as-
punctuation widening the legacy engine shipped:

- Static / export path (marked@16): a `cjkEmStrong` tokenizer override that
  rebuilds marked's emStrong flanking regexes with CJK folded into the
  punctuation class and removed from the alphanumeric class, registered in
  getHighlightHtml and getClipboardHtml. Faithful copy of marked's emStrong
  body; rules are swapped in/out per-call so the shared tokenizer rules are
  never left mutated.
- Live editor path (inlineRenderer): CJK widening added to
  canOpen/canCloseEmphasis in inlineRenderer/utils.ts, plus full code-point
  reading so the non-BMP CJK Ext-B surrogate-pair branch is live.

CJK ranges (matching legacy CJK_REG): Hiragana+Katakana U+3040–U+30FF, CJK
Ext-A U+3400–U+4DBF, CJK Unified U+4E00–U+9FFF, CJK Compatibility
U+F900–U+FAFF, Hangul Syllables U+AC00–U+D7AF, Halfwidth Katakana
U+FF66–U+FF9D, and CJK Ext-B U+20000–U+2A6DF (non-BMP).

The widening is additive — it never bolds anything CommonMark accepts as
non-emphasis — so the CommonMark 0.31 + GFM conformance suites are unchanged
(1347/1347, no unexpected passes). Promotes the #4399 `it.fails` placeholders
to passing `it` cases and adds live-editor-path coverage plus negative cases.

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

* test(muya): assert negative CJK flanking cases emit neither strong nor em

The static/export-path negative cases in strongCjkFlanking.spec.ts only
checked for <strong> via rendersStrong. Since #4307 widens the
emphasis/strong flanking logic, a regression could surface as unexpected
<em> output while still passing a <strong>-only assertion. Add a rendersEm
helper mirroring rendersStrong and assert NEITHER tag is produced for the
negative cases.

The live editor path already covers this: tokenizesEmphasis returns true
for either a strong or em token, so its negative .toBe(false) already
rejects both. Only the static path needed strengthening.

Addresses Copilot review on #4401.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/dompurify/printService/sourceCode/icon) (#4402)

* feat(desktop): wire @muyajs/core into the desktop package

Begin migrating the desktop renderer off the legacy `@marktext/muyajs`
engine and onto the TypeScript rewrite published as `@muyajs/core`
(packages/muya). This first step only adds the dependency and the type
plumbing; the `muya` alias (→ packages/muyajs) is left intact so
editor.vue keeps working while the migration proceeds file by file.

- Add `@muyajs/core: workspace:*` to packages/desktop dependencies.
- `@muyajs/core` ships no built `lib/types`; its package `exports` map
  points `.` at `./src/index.ts`, so vue-tsc would otherwise descend
  into the entire muya source tree and report errors that only exist
  because muya's own `src/types/global.d.ts` globals aren't loaded under
  the desktop program. Add a hand-written `src/types/muya-core.d.ts`
  declaring just the surface the desktop consumes, and a `paths` entry
  redirecting type resolution to it. Runtime resolution still goes
  through the package `exports` map via Vite/electron-vite (verified by
  build:unpack and a vitest import smoke test).

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

* feat(desktop): consume @muyajs/core in markdownToHtml/pdf/sourceCode

Swap the directly-equivalent muyajs imports for their `@muyajs/core`
counterparts:

- util/markdownToHtml.ts: `new ExportHtml(md).renderHtml()` →
  `new MarkdownToHtml(md).renderHtml()`. The new `renderHtml()` already
  wraps its output in `<article class="markdown-body">…</article>`
  (muyajs did not), so the desktop no longer adds the wrapper — output
  is byte-identical.
- util/pdf.ts: `escapeHTML`/`unescapeHTML` now come from `@muyajs/core`
  (identical implementations). The `Slugger` import is intentionally
  left on muyajs: the TOC anchors built here must match the heading
  `id`s emitted by the muyajs export renderer (`Muya#exportStyledHTML`,
  still used by editor.vue), which use the SAME Slugger. Swapping to
  @muyajs/core's `generateGithubSlug` (a different algorithm, no
  dedup/unicode downcoding) would break in-document TOC links; it
  migrates together with the editor.vue swap.
- components/editorWithTabs/sourceCode.vue: `wordCount` now comes from
  `@muyajs/core`. Same `{ word, paragraph, character, all }` return
  shape; the token-split regex differs only cosmetically (`/\s+/` vs
  `/[\s\n]+/`, equivalent since `\s` covers `\n`).

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

* refactor(desktop): vendor dompurify directly in util/dompurify

Replace the `muya/lib/utils/dompurify` import (whose default export was
simply `DOMPurify.sanitize`) with `dompurify` directly — already a
direct desktop dependency. `@muyajs/core`'s exported `sanitize(html,
opts, disableHtml)` has a different signature (it escapes HTML
internally), whereas the desktop needs the RAW sanitizer that does not
escape (pdf.ts escapes/unescapes around it). Behavior is identical to
the legacy path. Cast through `unknown` to bridge DOMPurify's
`string | TrustedHTML` overload union; both configs set
`RETURN_TRUSTED_TYPE: false`, so the result is always a string.

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

* refactor(desktop): use @marktext/file-icons directly in sideBar/icon

Drop the `muya/lib/ui/fileIcons` import in favor of the underlying
`@marktext/file-icons` package (already a direct desktop dependency).
The muyajs wrapper only added a `getClassByName(name)` helper around the
package's `matchName(name)?.getClass(0, false)`; inline that helper in
the component and import the package's CSS (`build/index.css`) directly,
as the wrapper did. Add an ambient module declaration for
`@marktext/file-icons` (it ships no types) to shims.d.ts.

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

* docs(desktop): document printService getImageInfo migration blocker

services/printService.ts calls `getImageInfo(rawSrc: string).src` to
normalise an <img>'s src attribute into a displayable URL before
printing/PDF export (GH#678). `@muyajs/core` has no behaviour-equivalent
string helper for this:
  - its `getImageInfo(image: HTMLElement)` takes a DOM element, not a
    string;
  - its `getImageSrc(src: string)` double-prefixes already-resolved
    `file://` URLs (`file://file://…`) and blanks `data:` URLs — and the
    muyajs export renderer already emits absolute `file://` srcs into the
    HTML this consumes, so it would regress every image.

Keep the muyajs import for now and document why; it migrates once the
export render path (editor.vue / Muya#exportStyledHTML) moves to
@muyajs/core. No behavioral change.

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

---------

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

Additive prep for the muyajs -> @muyajs/core editor engine swap. The new
@muyajs/core engine reads kebab-case CSS custom properties (e.g.
--editor-color, --theme-color, --h1-color), whereas legacy muyajs reads the
themes' existing camelCase vars (--editorColor, --themeColor, --h1Color).

This change is ADDITIVE and NON-BREAKING: each of the 32 *.theme.css files
keeps all existing camelCase vars and gains a kebab-case block in its :root
that mirrors the muya target set. The current editor ignores the kebab vars;
once editor.vue switches engines they take effect. Authoritative target var
set was taken from packages/muya/src/assets/styles/index.css.

Mapping: each kebab var is set to var(--camelCaseSource) so it tracks the
theme's value with no drift (--editorColor80 -> --editor-color-80,
--floatBgColor -> --float-bg-color, --h1Color -> --h1-color, etc.). Two vars
use muya's own defaults instead of a theme source: --button-border-focus
(var(--button-border), matching muya) and --float-shadow (muya's literal
multi-layer box-shadow, since the desktop --floatShadow is a single color
wrapped at call sites and not a valid standalone box-shadow value).

Verified: postcss parses all 32 files cleanly, all 41 muya kebab vars are
declared in every theme with no dangling var() sources, build:unpack and
desktop typecheck pass, and the diff is purely additive (0 deletions).
Visual verification follows the separate editor.vue engine swap.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* build(muya): export TableChessboard from @muyajs/core entrypoint

Export the TableChessboard UI plugin from the muya core entrypoint so the
desktop editor host can register it (the quick-insert table picker). The
@muyajs/core workspace dependency and the tsconfig.base.json @muyajs/core
paths entry it consumes already landed on develop via #4402.

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

* feat(desktop): exportStyledHTML wrapper + @muyajs/core types shim

@muyajs/core exposes MarkdownToHtml(md, muya).generate() but has no
equivalent of the legacy muyajs exportStyledHTML (which injects a TOC at
the [TOC] marker and wraps the article in a header/footer page table for
PDF/print). Add a desktop-side exportStyledHTML wrapper that reproduces
that behaviour on top of the engine output so export stays equivalent.

Add a hand-written @muyajs/core declaration shim (referenced by the
tsconfig paths entry) so vue-tsc resolves the import at the package
boundary instead of type-checking the entire muya source tree under the
desktop program. Delete it once @muyajs/core ships built type
declarations.

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

* feat(desktop): migrate editor.vue to the @muyajs/core engine

Rewire the editor host onto @muyajs/core:

- Construction: build the engine with markRaw(new Muya(el, options)) +
  explicit init(); register the new UI plugin set (TableChessboard,
  ParagraphQuickInsertMenu, CodeBlockLanguageSelector, EmojiSelector,
  ImageEditTool, ImageResizeBar/ToolBar, InlineFormatToolbar,
  ParagraphFront*, PreviewToolBar, LinkTools, FootnoteTool, Table*).
- Options/i18n: map the desktop language preference to the engine's
  bundled locale objects and pass locale on construct + on
  language-changed (editor.locale(...)).
- Events: derive the content-change payload (markdown/wordCount/cursor/
  history/TOC/blocks) from the engine's low-level json-change; adapt the
  new selection-change shape for the application-menu state builder and
  drive format state off the formats it now carries; listen for scroll
  on the container directly (engine has no scroll event).
- Methods: setContent (replacing setMarkdown), getScrollContainer via
  domNode, viewport-relative caret coords for typewriter/keep-visible
  scrolling, copy/paste method remap (copyAsRich -> copyAsHtml), per-tab
  engine-history stash for in-session tab switches with a synthetic
  desktop-shaped history for save/dirty tracking.
- Search circular-ref fix: muya's search/replace/find return the live
  Search instance (circular: Search -> muya -> ... -> ScrollPage) and
  each match carries a live block ref. The store deep-clones (JSON) its
  payload, which crashed; toSearchMatches() strips it to the plain
  { index, matches, value } the search UI needs before dispatching.
- Blur the editor when the command palette opens to avoid a focus-trap
  vs engine-selection deadlock that froze the renderer.

Documented gaps (engine does not yet surface these; editor stays fully
functional): heading-copy-link hover affordance, selection affiliation
chain for block-context menu toggles, and selectedTableCells in
select-all.

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

* test(desktop): update e2e selectors + recipes for @muyajs/core mu- DOM

Migrate the desktop e2e suite to the @muyajs/core renderer:

- Swap ag-* DOM selectors for the engine's mu-* equivalents across the
  fixture-render, crash, issue-regression, paragraph-blocks and
  strong-cjk specs, and update helpers.ts (paragraph content lives in
  span.mu-paragraph-content; selection is committed via a synthetic
  keyup so the engine updates its active block).
- crash-update-paragraph #2099: the quick-insert menu now triggers on
  '/' (not the legacy '@'); type '/', wait for the float to be shown
  (it is always attached but parked off-screen until positioned), then
  pick Header 1.
- plantuml: the engine encodes via plantuml-encoder (no '~1' deflate
  prefix), so assert the bare plantuml-alphabet base64 src.
- strong-cjk: mark the CJK + **"x"** flanking case as fixme — a
  documented @muyajs/core parser gap (#4307, tracked as it.fails in
  packages/muya/src/state/__tests__/strongCjkFlanking.spec.ts); the
  CJK + **plain** regression still passes.

Full desktop e2e: 87 passed, 3 skipped (all documented gaps), 0 failed.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The @muyajs/core engine opens the quick-insert menu with "/" (its
in-editor ghost text already reads "Type / to insert"), replacing the
legacy muyajs "@" trigger. Update the desktop quick-insert hint strings
(type-at-to-insert / typeAtToInsert) across all nine locales and the
end-user EDITING doc so nothing still references the old "@" trigger.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pull pull Bot locked and limited conversation to collaborators Jun 8, 2026
@pull pull Bot added the ⤵️ pull label Jun 8, 2026
@pull
pull Bot merged commit 8285732 into code:develop Jun 8, 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.

1 participant