Skip to content

feat(desktop): consume @muyajs/core in util files (markdownToHtml/pdf/dompurify/printService/sourceCode/icon) - #4402

Merged
Jocs merged 5 commits into
developfrom
feat/desktop-consume-muya-core-utils
Jun 8, 2026
Merged

Jocs merged 5 commits into
developfrom
feat/desktop-consume-muya-core-utils

Conversation

@Jocs

@Jocs Jocs commented Jun 8, 2026

Copy link
Copy Markdown
Member

Summary

First step of migrating the desktop renderer off the legacy @marktext/muyajs engine and onto the TypeScript rewrite published as @muyajs/core (packages/muya).

This PR only touches the non-editor.vue utility/component files. editor.vue (the large engine swap) is handled separately, so the muya alias (→ packages/muyajs) is left fully intact and the legacy engine still drives the editor during the migration.

Wiring

  • Add @muyajs/core: workspace:* to packages/desktop dependencies.
  • @muyajs/core ships no built lib/types; its exports map points . at ./src/index.ts. Letting vue-tsc resolve to that source pulls the entire muya tree into the desktop program, where muya's own src/types/global.d.ts globals (e.g. Element.__MUYA_BLOCK__) aren't loaded → spurious errors. Added a hand-written src/types/muya-core.d.ts (declaring only the surface the desktop consumes) plus a paths entry redirecting type resolution to it. Runtime resolution still flows through the package exports map via Vite/electron-vite (verified by build:unpack + a vitest import smoke test).

Per-file old → new mapping

File Old (muyajs) New Notes
util/markdownToHtml.ts new ExportHtml(md).renderHtml() new MarkdownToHtml(md).renderHtml() from @muyajs/core MarkdownToHtml#renderHtml now wraps in <article class="markdown-body">…</article> itself, so the desktop no longer adds the wrapper — output is identical (no double-wrap).
util/pdf.ts escapeHTML/unescapeHTML from muya/lib/utils same from @muyajs/core Identical implementations.
util/pdf.ts Slugger from muya/lib/parser/marked/slugger kept on muyajs (documented) See adaptation below.
util/dompurify.ts runSanitize from muya/lib/utils/dompurify DOMPurify.sanitize (vendor dompurify directly) The legacy default export was DOMPurify.sanitize. @muyajs/core's sanitize(html, opts, disableHtml) escapes HTML internally — wrong for the desktop's raw-sanitize use (pdf.ts escapes/unescapes around it). Behavior preserved.
services/printService.ts getImageInfo from muya/lib/utils kept on muyajs (documented) See blocker below.
components/editorWithTabs/sourceCode.vue wordCount from muya/lib/utils same from @muyajs/core Same { word, paragraph, character, all } shape; split regex differs only cosmetically (/\s+/ vs /[\s\n]+/).
components/sideBar/icon.vue fileIcons from muya/lib/ui/fileIcons @marktext/file-icons directly (+ build/index.css) The muyajs wrapper only added getClassByName around matchName(name)?.getClass(0, false); inlined in the component. Ambient decl added to shims.d.ts (package ships no types).

Signature adaptations / documented blockers

Two functions have no behaviour-equivalent in @muyajs/core and were deliberately left on muyajs (each with an inline comment) to avoid regressions. They migrate together with the editor.vue / Muya#exportStyledHTML swap:

  1. pdf.ts Slugger — getHtmlToc builds TOC anchors (#${slugger.slug(content)}) that must match the heading ids emitted by the muyajs export renderer (Muya#exportStyledHTML, still used by editor.vue), which use the same Slugger. @muyajs/core only offers generateGithubSlug — a different algorithm (no dedup, no unicode downcoding), which would break in-document TOC links while the export path is still muyajs.
  2. printService.ts getImageInfo(rawSrc: string) — normalises an <img> src string into a displayable URL (GH#678). @muyajs/core's getImageInfo(image: HTMLElement) takes a DOM element (different), and 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 here, so it would regress every image.

Verification

  • pnpm -C packages/desktop run typecheck — clean ✅
  • pnpm run lint — 0 errors (77 pre-existing warnings, identical to develop) ✅
  • pnpm run build:unpack — builds successfully ✅
  • pnpm -C packages/desktop exec vitest run — 560/560 pass ✅ (plus a throwaway smoke test confirming @muyajs/core resolves & returns correct values in vitest)

🤖 Generated with Claude Code

Jocs and others added 5 commits June 8, 2026 17:10
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) <[email protected]>
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) <[email protected]>
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) <[email protected]>
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) <[email protected]>
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) <[email protected]>
Copilot AI review requested due to automatic review settings June 8, 2026 09:15
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Website preview ready: https://pr-4402-marktext-website.ransixi.workers.dev

Built from 4a1a56f5de3342236a3c2614085ccad5bef6db1d · Worker: marktext-website · Version: 3c69cefa-ebf9-47ad-b8fd-be9df79c87b5 · Alias: pr-4402

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is an incremental step in migrating the desktop renderer’s non-editor utility/component code from the legacy @marktext/muyajs engine to the TypeScript rewrite published as @muyajs/core, while explicitly keeping legacy-only helpers on muyajs to avoid behavior regressions during the staged migration.

Changes:

  • Add @muyajs/core as a packages/desktop dependency and introduce a tsconfig paths redirect to a minimal hand-written muya-core.d.ts surface to keep vue-tsc from typechecking the full muya source tree.
  • Switch desktop utilities/components to consume MarkdownToHtml, escapeHTML/unescapeHTML, and wordCount from @muyajs/core, while deliberately retaining legacy Slugger and getImageInfo imports on muyajs (documented inline) to preserve export/TOC/image behavior.
  • Vendor dompurify directly (matching the legacy wrapper behavior) and consume @marktext/file-icons directly in the sidebar icon component.

Reviewed changes

Copilot reviewed 8 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds workspace link for @muyajs/core under the desktop importer.
packages/desktop/package.json Adds @muyajs/core: workspace:* dependency.
packages/desktop/tsconfig.base.json Redirects @muyajs/core type resolution to a local d.ts surface.
packages/desktop/src/types/shims.d.ts Adds an ambient module declaration for @marktext/file-icons (package ships no types).
packages/desktop/src/types/muya-core.d.ts Adds a minimal declaration surface for the subset of @muyajs/core used by desktop.
packages/desktop/src/renderer/src/util/pdf.ts Uses escapeHTML/unescapeHTML from @muyajs/core while intentionally keeping legacy Slugger for TOC anchor compatibility.
packages/desktop/src/renderer/src/util/markdownToHtml.ts Switches to @muyajs/core’s MarkdownToHtml and removes the now-redundant outer <article> wrapper.
packages/desktop/src/renderer/src/util/dompurify.ts Replaces legacy wrapper import with direct dompurify usage while preserving raw-sanitize semantics.
packages/desktop/src/renderer/src/services/printService.ts Documents why getImageInfo remains on legacy muyajs for now.
packages/desktop/src/renderer/src/components/sideBar/icon.vue Switches from the legacy wrapper to direct @marktext/file-icons usage and inlines the wrapper helper logic.
packages/desktop/src/renderer/src/components/editorWithTabs/sourceCode.vue Switches wordCount import to @muyajs/core.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Build artifacts for PR #4402:

Run: https://github.com/marktext/marktext/actions/runs/27127729045

Artifact Size Link
marktext-macos-x64 257.3 MB Download
marktext-windows-arm64 256.8 MB Download
marktext-linux 557.7 MB Download
marktext-windows-x64 258.1 MB Download
marktext-macos-arm64 247.1 MB Download

@Jocs
Jocs merged commit 0706ee5 into develop Jun 8, 2026
19 checks passed
Jocs added a commit that referenced this pull request Jun 8, 2026
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) <[email protected]>
Jocs added a commit that referenced this pull request Jun 8, 2026
* 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) <[email protected]>

* 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) <[email protected]>

* 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) <[email protected]>

* 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) <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
@Jocs
Jocs deleted the feat/desktop-consume-muya-core-utils branch June 10, 2026 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants