spinss

MDeX v2.2.1 (macOS · Windows · Linux · Tauri v2)

MDeX · pronounced "em-dex" (/ˌemˈdɛks/) — the letter M followed by "dex", two syllables.

A multilingual Markdown reader & editor focused on privacy and AI assistance. Every file is processed locally — no network, no uploads, no cloud sync, no ads, no telemetry, by default; atomic (crash-proof) saves prevent data loss on crash or power outage. Since v2.0, MDeX also offers optional AI-assisted editing — and it can stay fully offline: a local model (e.g., Ollama) needs no internet; only an online AI service (OpenAI / DeepSeek / Anthropic / GLM / Gemini / Kimi, etc.) makes a request, and only when you configure and trigger it.


🌐 Languages

The interface ships in 17 languages: English, 简体中文, Français, Deutsch, Español, Português, Italiano, Русский, 日本語, 한국어, العربية, हिन्दी, ਪੰਜਾਬੀ, Tiếng Việt, Bahasa Indonesia, اردو, Монгол (Кирилл).


✨ Features


⌨️ Shortcuts

Use on macOS, Ctrl on Windows / Linux.

Shortcut Action
⌘/Ctrl + N New
⌘/Ctrl + O Open file
⌘/Ctrl + S Save
⌘/Ctrl + Shift + S Save As
⌘/Ctrl + W Close tab
⌘/Ctrl + Shift + W Close window
⌘/Ctrl + F Find
⌘/Ctrl + H Replace
⌘/Ctrl + B / I / E Bold / Italic / Inline code
⌘/Ctrl + K Insert link
Tab Indent 2 spaces
Alt/Option + drag Rectangular (column) select
Alt/Option + Shift + ←↑↓→ Extend column select
Esc Cancel column select
⌘/Ctrl + =/−/0 Zoom the last-clicked pane (editor or preview) in / out / reset
⌘/Ctrl + J AI-assisted editing

With multiple windows open, shortcuts only affect the focused window. When the image viewer window is open, ⌘/Ctrl + =/−/0 zooms the image instead.


📝 Cheat sheet

Markdown: headings # / ## / ###, bold **text**, italic *text*, strikethrough ~~text~~, inline code `code`, code blocks (triple backticks, with optional language), quote > text, lists - / 1., task list - [ ] / - [x], link [text](url), image ![alt](url), divider ---, tables | A | B |.

Math: inline $E = mc^2$; block $$\int_0^1 x\,dx$$ (may span lines). Uses LaTeX syntax, rendered by KaTeX; $ inside code blocks is not treated as a math delimiter. Supports align / aligned, matrices, cases and other common environments.

Citations: write [@key] or [@a; @b] in the text (LaTeX-compatible \cite{key}), embed the library via a ```bibtex block or load a .bib with the "Refs" button. A References list is generated at the end; in-text [n] is clickable.


📤 Export (Save As)

Click "Save As" and pick a format:


🔒 Offline & security


📦 Installation

Prebuilt downloads

Download the installer for your platform from either source:

macOS (.dmg, universal arm64 + x86_64), Windows (.exe, NSIS installer), Linux (.deb / .rpm / .AppImage).

Opening the unsigned app on macOS (bypass Gatekeeper)

This app is not developer-signed / notarized (offline scenarios usually can't notarize online). On macOS 12+, especially macOS 26 (Tahoe), launching it straight from the .dmg — or a freshly copied build — fails with "MDeX.app is damaged and can't be opened." That's Gatekeeper, not real damage. Fix in Terminal:

  1. Drag MDeX.app out of the .dmg into /Applications first — never run it directly from the dmg (that triggers App Translocation and the com.apple.provenance attribute, the real cause of "damaged" on macOS 26).
  2. Clear attributes and re-sign:
    xattr -cr /Applications/MDeX.app
    codesign --force --deep --sign - /Applications/MDeX.app

    com.apple.provenance is SIP-protected and can't be removed even with sudo; re-signing resets the signature so Gatekeeper lets it run. spctl still reports rejected for ad-hoc signing — expected, and it does not block open.

  3. Launch with open /Applications/MDeX.app (or double-click). The first launch may still prompt once — confirm via System Settings → Privacy & Security → Open Anyway, or right-click the app → Open.

🛠️ Build from source

Source code: https://github.com/fwzheng/mdex. One-time setup: npm installnpm run fetch (downloads vendor with an integrity lock) → npm run build:html (inlines vendor + src/app.js into dist/index.html) → cargo tauri dev / cargo tauri build. Tests: npm test (frontend) · npx tsc --noEmit (types) · cd src-tauri && cargo test --lib (backend).


📁 Project structure

markdown/
├── app-shell.html          # frontend shell (HTML+CSS); app logic in src/app.js (+ i18n.js/help.js data)
├── src/
│   ├── app.js              # application logic (// @ts-check; inlined into dist by build-html.mjs)
│   ├── i18n.js            # 17-language UI strings (pure data; window.I18N, split from app.js)
│   ├── help.js            # help-document data (HELP_STRINGS + SK/sc/CITE_HELP_*, window.HELP_DATA)
│   └── globals.d.ts        # vendor / Window type declarations for type-checking
├── tsconfig.json           # type-check config (`tsc --noEmit`; no bundler)
├── tools/
│   ├── fetch-vendor.mjs    # one-time: download deps into vendor/ + integrity lock (online only here)
│   ├── build-html.mjs      # inline vendor + src/app.js + i18n.js + help.js into dist/index.html (KaTeX fonts → base64)
│   └── test-pure.mjs       # frontend pure-function tests (`npm test`)
├── dist/index.html         # build output: self-contained single file (Tauri frontendDist)
├── vendor/                 # download cache + integrity.json (.gitignore)
├── package.json            # @tauri-apps/cli + typescript(dev) + scripts
└── src-tauri/
    ├── Cargo.toml          # tauri 2 + dialog / single-instance + encoding_rs
    ├── build.rs            # tauri_build::build()
    ├── tauri.conf.json     # 1200×750 window, strict CSP, icons, .md association, menu hooks
    ├── capabilities/default.json
    ├── icons/              # full icon set from `cargo tauri icon`
    └── src/{main.rs, lib.rs}   # menus + file IO + multi-window routing + atomic write / file ownership

🎨 Customization

To change Where
App name / Bundle ID src-tauri/tauri.conf.jsonproductName / identifier
Window size tauri.conf.jsonapp.windows[0] (default 1200×750)
Icons replace the source image, then npm run icon
Theme colors / fonts :root CSS variables atop app-shell.html
Menu items build_menu() in src-tauri/src/lib.rs
UI strings / help doc I18N in src/i18n.js / HELP_STRINGS in src/help.js
Dependency versions VERSIONS atop tools/fetch-vendor.mjs (then npm run fetch -- --force)
Tests / type-check npm test (frontend) · tsc --noEmit (types) · cargo test --lib (backend)

📄 License

This project's own code is open-sourced under the Apache License 2.0.

Third-party components: the project uses some third-party components (including but not limited to marked, KaTeX, highlight.js, DOMPurify, jsPDF, html2canvas-pro, turndown, mermaid, @retorquere/bibtex-parser, and Tauri, etc.); their copyright and license notices are detailed in the respective source files. These components are respectively licensed under MIT, BSD-3-Clause, Apache-2.0, MPL-2.0, and other open-source licenses.

Distribution requirements: under the Apache-2.0 license, redistributing this project requires retaining the LICENSE and NOTICE files; if you modify any source file, you must clearly indicate the changes in the corresponding file.


📬 Contact

For problems or suggestions: 郑法伟 (Fawei Zheng) fwzheng@bit.edu.cn