macOS 版本未签名公证,首次打开需到系统设置里点「仍要打开」或用 xattr 清除隔离标记;打包需在各自平台上分别执行,不支持交叉构建;仓库无自动化测试,npm run verify 目前只跑 typecheck

Moji
alexishidaMIT活跃维护把 Markdown 文件当 PDF 一样双击打开就能读的轻量桌面应用,支持阅读、编辑和导出,基于 Electron 跨平台。
Open Markdown files like PDFs. A lightweight, clean desktop app for opening, reading, editing, and exporting Markdown files.
npm install
npm run devREADME 未说明;仅要求 Node.js ^20.19.0 || >=22.12.0 与 npm,提供 Windows、macOS(通用包)、Linux 安装包
把 Markdown 文件当 PDF 一样打开的轻量桌面阅读编辑器,支持预览、编辑与导出
把 Markdown 文件当 PDF 一样双击打开就能读的轻量桌面应用,支持阅读、编辑和导出,基于 Electron 跨平台。
核心能力
- 多标签工作区管理文档,带未保存标记与重复文件检测
- 预览支持 KaTeX 数学公式、Mermaid 图表与代码高亮复制
- CodeMirror 6 编辑器配可调分栏实时预览与滚动同步
- 超 20 MB 文档启用块虚拟化,解析与高亮放在 Web Worker
- 导出 HTML、PDF、PNG,PDF 支持 A4/Letter/Legal 与横竖版
容易踩的坑
- v1.0.7Moji v1.0.7最新正式版2026-09-10 · 2026-09-10 01:32 UTC
Added
- The bundled Markdown guide now opens in Editor mode with the read-only source beside its rendered preview, so examples can be inspected and copied while keeping the source and result aligned.
Fixed
- Split-view scrolling now follows the pane the user last interacted with, including scrollbar clicks that do not emit a scroll event. Delayed CodeMirror measurements can no longer reverse the sync direction, and stale preview-driven editor scrolls are cancelled when the user takes control.
- Split-view heading positions are recalculated after preview layout changes, keeping editor and preview aligned after fonts, images, or rendered content change size. The preview no longer uses estimated off-screen block heights that could make scrollbar dragging jump.
- Preview scroll-spy now finds the active heading with a binary search and schedules its updates per animation frame, reducing work on large documents.
- In-page anchors now resolve reliably in virtualized previews, including explicit HTML anchors outside the mounted viewport and URL-encoded fragment identifiers.
- v1.0.6Moji v1.0.62026-08-22 · 2026-08-22 22:52 UTC
查看更新说明
Added
- Font size can now be adjusted in Editor mode, from the same top-bar control and the Ctrl+Plus / Ctrl+Minus / Ctrl+0 shortcuts already available in view mode. Editor and preview keep separate sizes (12px-24px), the editor resetting to 14px and the preview to 16px.
- Settings now has a dedicated Editor tab to set the editor's default font size, alongside the existing Preview tab for the preview's font size.
- Split view that renders the live preview beside the CodeMirror editor, toggled from the top bar or with Ctrl+. A draggable divider sets the pane ratio (20%–80%), and both the toggle and the ratio persist in settings. The toggle is disabled below a 700px-wide workspace and explains why in its tooltip. In view mode the toggle switches straight to edit mode in the same action, and a view-only split mode keeps the editor mounted while showing only the preview pane.
- Ctrl+G and Ctrl+Shift+G jump to the next and previous search match; Ctrl+M leaves editor focus.
- Ctrl+Q now goes through a real
requestQuitIPC path, so the shortcut behaves like the native Quit menu and still runs the unsaved-changes guard (previously it only closed the window and did not quit on macOS).
Changed
- Preview and editor font sizes now persist across launches instead of resetting every session. The full-width reading toggle stays session-only.
- Scrolling the source editor now moves the preview to the matching part of the document, and scrolling the preview moves the editor in turn; only one pane owns the sync while it is being scrolled, so the two cannot bounce off each other.
- Shortcut handling is more robust: AltGr keyboard layouts (such as pt-BR ABNT2) no longer drop Ctrl+\ and similar primary chords, Escape now prevents default when closing a panel, and the macOS Window menu drops Miniaturize (Cmd+M) so the editor's exit-focus binding (Mod+M) works as advertised.
- A
.tmpfile left over by a failedsettings.jsonatomic write is now cleaned up instead of lingering.
Fixed
- PDF export no longer passes
marginType, dropped from Electron'sprintToPDFAPI; margins keep the same 1cm default. npm installdownloads the Electron binary again. Electron removed its ownpostinstallhook in 42.0.0, so a fresh install leftnode_modules/electronwithout adist/andnpm run devdied withError: Electron uninstall.- Switching the reading theme no longer drops the preview's imperative patches. The preview body is keyed by the theme, so changing it used to remount from pristine HTML and discard local image sources, copy buttons, search marks, and scroll-sync state; effects now track the mounted body instead of a patch counter.
- The reading position stays anchored on the visible heading across the theme remount, so toggling light/dark no longer drifts the preview scroll.
- Unsaved work can no longer be lost. Close/quit is only forced through Chromium's own
unresponsivehang detector instead of a fixed 5-second timer that could close while the confirm dialog was still open, and it only forces through a close/quit already in flight. - A crashed renderer reloads the window with a warning dialog instead of silently discarding unsaved edits; launch-failed and integrity-failure events quit with an error instead of reload-looping.
- A draft journal now records the base snapshot's SHA-256, so a crash between the snapshot rename and its journal cleanup is detected as stale on load (the old length-only check could collide on an equal-length edit) and is no longer replayed and duplicated.
- An untouched new or restored document is no longer born "dirty", eliminating a spurious close-confirmation and an empty draft written to disk before any typing. Restored untitled documents resume their sequential numbering, tombstoned draft ids can't be resurrected by a late autosave, and empty draft-queue entries are dropped.
- The close guard,
render-process-gone, and runtime errors no longer restart or force
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
Name
Moji (文字) literally means "letter", "character", or "writing" in Japanese. Short and easy to remember, it evokes characters and writing. The name fits its purpose: opening, editing, previewing, and exporting Markdown smoothly—without distractions.
Features
- Open Markdown files: supports
.mdand.markdownthrough file dialog, drag and drop, CLI/file association entry points, and single-instance forwarding. - Multi-document workspace: horizontal tabs, dirty markers, close buttons, duplicate-file detection, and unsaved-change confirmation with clear action icons.
- Tab management: close other tabs, tabs to the right, saved tabs, or all tabs from the document tab menu.
- Preview mode: sanitized Markdown rendering with heading anchors, outline navigation, tables, task lists, footnotes, definition lists, subscript/superscript, highlight/insert marks, emoji shortcodes, LaTeX math via KaTeX (
$…$and$$…$$), linkify, typographer, syntax-highlighted code, and copy buttons for code blocks. - Graphics and Mermaid diagrams: every valid fenced
mermaidblock supported by bundled Mermaid renders as a responsive diagram, including flowcharts, sequence, Gantt, class, ER, state, and journey diagrams. Click any rendered SVG or Markdown image to inspect it in a modal with zoom, drag navigation, a minimap, and individual PNG export; malformed Mermaid blocks remain readable code blocks. - Outline navigation: collapsible heading tree available in Preview and Editor modes. Preview uses scroll-spy; clicking any heading scrolls preview or moves editor cursor to its Markdown source.
- Search and replace: top-bar search finds visible Markdown text even across inline formatting, distinguishes the active match, and shows the active/total occurrence count. Preview offers previous/next navigation; Editor separates navigation from replace-one/replace-all controls.
- Editor mode: CodeMirror 6 Markdown editor with line numbers, history, wrapping, localized untitled document names, Markdown formatting shortcuts, and save/save as flows.
- Live preview: while editing, toggle a resizable split view from the top bar (or
Ctrl+\) to keep the rendered preview beside the source editor. Scrolling either pane moves the other to the matching part of the document, and the pane ratio is remembered. The toggle is disabled in view mode and when the workspace is too narrow for two readable panes. - Untitled document recovery: documents without a filesystem path are saved as internal recovery drafts and reopened after restarting Moji. Saving as a real file or closing the tab removes the recovery draft.
- Large documents: files are streamed from the main process in UTF-8 chunks and decoded incrementally, Markdown is parsed and highlighted in a Web Worker, and documents above 20 MB switch the preview to block virtualization so only the visible part stays in the DOM.
- Batch open with progress: selecting many files opens them with bounded concurrency, a progress banner, and a cancel action that keeps whatever already opened.
- Export mode: export the active document as HTML, PDF, or PNG. PDF supports A4, Letter, Legal, portrait, and landscape; long code lines wrap in PDF and PNG exports.
- Export progress: HTML, PDF, and PNG exports report their current phase and can be cancelled; a cancelled or failed export never leaves a partial file behind.
- Diagram exports: rendered Mermaid diagrams are embedded as self-contained SVG in HTML, PDF, and PNG exports.
- Local images: images referenced relative to the document are served through an authorized
moji-asset://protocol, restricted to directories of documents you actually opened, loaded lazily and cached in memory. - Settings view: centered in-workspace panel for language, untitled-document recovery, preview typography, editor and preview font sizes, reading width, and a localized shortcut reference.
- About view: in-workspace panel showing app name, version (from
package.json), author, repository link, and the story behind the name. - Markdown guide: bundled localized reference documents (
samples/markdown-guide.<locale>.md) opened from the status bar in Editor mode with split view enabled, showing the read-only Markdown source beside its rendered preview. You can select and copy examples; narrow workspaces show the source until there is room for both panes. - Recent files: Welcome screen shows recently opened Markdown files and lets you reopen or remove entries.
- Remembered app state: window size/position, recent files, last used folder, language, preview typography, editor and preview font sizes, reading width, Markdown preview theme, live preview split and pane ratio, and untitled-document recovery preference are persisted in user settings.
- Update checks: installed Windows NSIS and Linux AppImage builds check GitHub Releases and link to the release page when a newer version is available, so you can choose the correct artifact.
- Markdown themes: dark/light toggle for rendered Markdown. App chrome remains dark; exports always use the light theme.
- Internationalization: English (United States and United Kingdom), Portuguese (Brazil and Portugal), Spanish, French, German, Italian, Dutch, Arabic, Hindi, Japanese, Chinese (Simplified and Traditional, Taiwan), and Russian. Initial language follows the OS when possible and user choice is persisted.
- Security: sandboxed renderer, context isolation,
nodeIntegration: false, DOMPurify sanitization, and external links opened in the OS browser. - Keyboard shortcuts: common file, search (Ctrl+G / Ctrl+Shift+G for next and previous match), replace, tab, preview, split view, export, fullscreen, and font-size (Ctrl+Plus / Ctrl+Minus / Ctrl+0, in Editor mode too) actions, plus Ctrl+M to leave editor focus and Ctrl+Q to quit through the unsaved-changes guard; Settings lists every available shortcut.
Screenshots
Installation
Windows and Linux install from the downloads above with no extra steps.
macOS
macOS refuses to open Moji the first time, saying it is damaged or that Apple cannot check it for malicious software. The app is fine. Signing an app requires a paid Apple Developer account, which Moji does not have yet, so macOS treats it as coming from an unidentified developer.
- Drag Moji from the DMG into your Applications folder.
- Double-click it. macOS blocks it. Dismiss the dialog.
- Open System Settings > Privacy & Security, scroll to the Security section, and click Open Anyway next to the message about Moji.
- Confirm. macOS remembers the choice, so this is a one-time step per version.
Control-clicking the app and choosing Open no longer works on macOS 15 (Sequoia) and later: Apple removed that override. System Settings is the only route through the interface.
If you prefer the terminal, clearing the quarantine flag skips the prompts entirely:
xattr -dr com.apple.quarantine /Applications/Moji.app
Update from GitHub Releases by downloading a new DMG. Signing and notarizing future macOS builds removes Gatekeeper warnings.
Requirements
- Node.js
^20.19.0 || >=22.12.0(required by Vite 7 and electron-vite 5; packaging also needsrequire()of ES modules, unflagged since Node 22.12) - npm
Development
npm install
npm run dev
npm run typecheck
npm run build
Useful scripts:
npm run dev: launch Electron with hot reload.npm run dev:update: launch development mode and simulate an available99.0.0update without network access.npm run typecheck: run TypeScript checks without emitting files.npm run verify: the gate thedist*scripts run first. Currently it istypecheckalone: this repository has no automated test suite.npm run build: build main, preload, and renderer intoout/.npm run preview: run the built app preview.npm run benchmark:corpus: generate a local 1/5/20/50 MB Markdown corpus under.tmp/benchmark-corpus/.npm run benchmark:ipc: measure the cost of moving a document across the process boundary.npm run benchmark:record: build, generate the corpus, and record a run intodocs/baseline-v1.json.npm run benchmark:compare: compare a run against the recorded baseline and the budgets indocs/performance-budget.md.
Packaging
npm run dist
npm run dist:win
npm run dist:linux
npm run dist:mac
Artifacts are written to release/.
Current packaging targets:
- Windows: NSIS installer, x64, with GitHub Release checks.
- Linux: AppImage with GitHub Release checks, plus deb for manual installation.
- macOS: universal (Apple Silicon + Intel) DMG and ZIP.
File associations for .md and .markdown are declared in electron-builder.yml.
macOS builds
macOS releases are not code-signed or notarized, because that requires a paid Apple Developer account. Consequences:
- Gatekeeper blocks the app when the DMG is downloaded from the web. Users open it through System Settings > Privacy & Security > Open Anyway, or by clearing the quarantine flag with
xattr -dr com.apple.quarantine /Applications/Moji.app. Control-clicking the app and choosing Open stopped working in macOS 15 (Sequoia), where Apple removed that override. The user-facing steps live under Installation. - Update checks stay disabled on macOS. Users update by downloading a new DMG from GitHub Releases.
To sign locally, install an Apple Developer ID certificate in the keychain and drop the CSC_IDENTITY_AUTO_DISCOVERY=false override; build/entitlements.mac.plist and hardenedRuntime are already configured for notarization.
Publishing a release
Releases are built and published by hand. There is no CI workflow in this repository.
- Update
versioninpackage.jsonandpackage-lock.json. - Run
npm run verify(typecheck). Thedist*scripts already runverifyfirst, so a release cannot be produced from a failing tree. - Build each platform on that platform:
npm run dist:win,npm run dist:linux,npm run dist:mac. Cross-building is not set up, andelectron-builder.ymlpublishes to a draft GitHub Release. - Commit the version bump, then create and push the matching tag, such as
v1.0.5. - Upload the artifacts to the draft release and publish it once Windows and Linux are in place: NSIS, AppImage, DEB, and the
latest.yml/latest-linux.ymlupdate metadata thatelectron-updaterreads.
macOS is unsigned and secondary. A missing or broken DMG should not hold back a good Windows and Linux release, so publish without it rather than waiting.
electron-updater checks GitHub Releases only in packaged Windows NSIS builds and Linux AppImages. Development, deb, and macOS builds do not check for updates. When a newer version is found, Moji opens GitHub Releases for the user to choose and install the correct artifact. Windows production releases should use an Authenticode certificate through electron-builder signing environment variables; never store certificate credentials in repository.
Project Structure
electron/
main.ts Window lifecycle, persisted bounds, file opening, single-instance flow, close guard, macOS application menu, IPC registration
preload.ts Safe renderer API exposed through contextBridge, including the chunked document read
shared.ts Shared IPC names, settings, export types, languages, recent-file limits, supported extensions
ipcInput.ts Normalization of every value the renderer sends over IPC
fileCapabilities.ts Paths the user actually chose; the only paths writes and asset reads are allowed on
assetPaths.ts Authorization for the moji-asset:// protocol, resolved through realpath
assetCache.ts Bounded in-memory LRU cache for served images
documentStream.ts Chunked file reads, paired with documentDecoder.ts for streaming UTF-8 decoding
openPool.ts Bounded-concurrency map used by the multi-file open session
settings.ts User settings persistence, window bounds, recent files, preview theme, and last dialog directory
drafts.ts Recovery storage for untitled documents (draftStore/draftJournal/draftCapacity)
export.ts HTML/PDF/PNG export implementation with progress, cancellation, and remembered output directory
png.ts Streaming PNG encoder (pngWorker/pngScanlines) that keeps tall-document exports within memory
updater.ts GitHub release checks for packaged NSIS and AppImage builds
performance.ts Local numeric measurements, plus the benchmark.ts runner
src/
App.tsx Renderer state, document actions, close guard wiring, mode switching, split-view scroll sync
components/ Top bar, tabs, sidebar, outline tree, preview, split view, Mermaid viewer, editor, export/settings/about dialogs, progress banners, confirm dialog, welcome view
hooks/ Grouped app state and the observed workspace width
workers/ Markdown parse/highlight/render worker
lib/ Markdown and Mermaid rendering, outline extraction, preview scroll/search/virtualization, split-view scroll mapping, draft journaling, export HTML, metrics
locales/ en, pt-BR, es, ja, zh, ru translation files
styles/ Theme tokens, app shell CSS, Markdown preview CSS
types/ Preload API typings and Vite environment types
assets/ App logo used by the welcome and about views
samples/ Bundled Markdown documents (full Markdown guide)
scripts/ electron-vite runner, Electron binary install, benchmark corpus/run/compare
docs/ README screenshots, performance budget, recorded benchmark baseline
Documentation
.ai-framework/RULES.md: project rules for AI-assisted changes..ai-framework/DESIGN.md: visual system, tokens, layout, and component rules.openspec/specs/: current behavior specs (app-shell, appearance, automatic-updates, document-export, document-translation, internationalization, markdown-editing, markdown-viewing).docs/performance-budget.md: performance budgets the benchmark scripts compare against.
License
MIT © Alex Ishida