Markdowner is a Rust-first Markdown editor desktop app built with Tauri v2, React, Vite, and Tiptap. The current repository now includes a runnable macOS desktop shell, a shared Rust document core, and the first cross-platform foundation for a future Windows build.
- macOS local development run works through
pnpm tauri dev - macOS local debug build works through
pnpm tauri build --debug - macOS no-cost DMG distribution works through
pnpm build:mac:dmgwith ad-hoc signing - the app shell includes file open, folder open, save, command palette, quick open, mode switching, theme switching, drag-and-drop open, and a Rust command bridge to
markdowner-core - document outlines are shown in the side panel with jump-to-line support
- shell reliability includes atomic writes, external-change detection, and ErrorBoundary fallback
- Windows is still a follow-up target, but the app architecture is now aligned for the same Tauri app shell
As of 2026-05-05, Markdowner is best described as a macOS developer-preview app with a strong local-first foundation. The core desktop loop is usable: open a Markdown file or workspace, edit in WYSIWYG/Source/Split View, save safely, reopen recent files, switch themes, and recover from common external-change conflicts. Against the current v1 product ambition, the repository is roughly 60-65% complete: the shell, core file model, settings persistence, and common Markdown round-trip path are in place, while authoring power features, export, search, packaging polish, and Windows validation remain open.
Completed or solid:
- Tauri v2 desktop shell with React 19, Vite 7, TypeScript, Tiptap, CodeMirror, React Markdown, and shadcn-style UI components
- Rust workspace with
markdowner-core, the Tauri bridge insrc-tauri, and the oldermarkdowner-macosreference crate - File lifecycle: new document, open file, open workspace, Save, Save As, recent documents, CLI path opening, single-instance routing, drag-and-drop file/folder opening, native menu command events
- Safety model: atomic writes, read-only file protection, external disk-change detection, compare/reload/keep-local flow, dirty close confirmation, session restore
- Navigation and shell UX: Activity Bar, resizable/collapsible sidebar, workspace tree, file-name filtering, Quick Open, Command Palette, Outline panel, document stats, status bar metadata
- Markdown coverage for headings, paragraphs, quotes, bullets, checklists, images, tables, fenced code blocks, links, emphasis, inline code, and raw-preserved unsupported blocks
- Settings persistence and runtime behavior for autosave, editor font, word wrap, startup mode, focus/typewriter writing aids, system theme following, and diagnostics logging; asset folder and PDF paper size preferences are stored for follow-on export/asset workflows
- Custom CSS theme import with validation plus frontend scoping to Markdown content surfaces
Partially complete:
- Asset folder and PDF paper size are persisted in settings, but their full runtime behaviors are waiting on the image asset and export workflows
- Code highlighting exists in the Rust core model for known code fences, but frontend preview/WYSIWYG highlighting policy still needs product-level polish
- macOS DMG generation is enabled for no-cost direct distribution, but paid Developer ID signing/notarization and release metadata are not complete
- Test coverage is meaningful at the Rust core and React shell levels, but there is no full desktop E2E, screenshot regression, or automated accessibility gate yet
Not implemented yet:
- In-document Find & Replace
- Slash command menu
- KaTeX math and Mermaid diagram rendering
- HTML/PDF/Print export
- Workspace full-text search
- Image paste/drop asset copying and relative-path insertion
- Automatic backups before overwrite
- Window size/position restore
- Windows build/test/release validation
- WYSIWYG editing surface powered by Tiptap
- Source mode powered by CodeMirror 6
- Preview mode powered by React Markdown + GFM rendering
- File open and save through the desktop shell
- Command palette (
⌘⇧P) and quick open (⌘P) for rapid navigation - Workspace folder opening and file tree navigation
- Document stats dialog and outline panel
- Support for images, tables, checklists, and fenced code blocks
- Built-in light and dark themes plus user CSS theme import
- Settings panel with font, wrapping, autosave, startup mode, theme-following, asset, PDF, and diagnostics preferences
- Rust
markdowner-coreremains the canonical Markdown/document layer
crates/markdowner-core: Markdown parsing and serialization, document model, themes, workspace state, and runtime logiccrates/markdowner-macos: earlier macOS shell/reference crate kept for boundary and regression coveragesrc: React/Vite frontend shellsrc-tauri: Tauri desktop shell, Rust command bridge, and app configurationdocs/architecture/core-platform-boundary.md: notes on the core/platform split
Markdowner has been verified locally on macOS with the following toolchain available:
Node.js v22.20.0pnpm v10.33.0cargo 1.94.0rustc 1.94.0- Xcode Command Line Tools available through
xcode-select
Minimum setup checklist:
- Install a recent Rust toolchain
- Install Node.js and pnpm
- Install Xcode Command Line Tools
Example check commands:
node -v
pnpm -v
cargo -V
rustc -V
xcode-select -p
xcrun --versionpnpm installIf pnpm install warns about ignored build scripts in your environment, approve the required builds and rerun install:
pnpm approve-builds
pnpm installStart the desktop app in development mode:
pnpm tauri devWhat this does:
- starts the Vite dev server on
http://127.0.0.1:14238 - compiles the Tauri Rust shell
- launches the local debug desktop executable
This command was verified locally in this repository. During startup, Tauri runs pnpm dev first, then runs the Rust desktop app from target/debug/markdowner-desktop.
If pnpm tauri dev fails immediately, first check whether port 14238 is already in use. Markdowner binds the Vite dev server to 127.0.0.1:14238 with strictPort enabled so it does not silently attach to another project's dev server.
cargo buildOn a fresh machine, the first Rust build downloads crate dependencies from crates.io and can take noticeably longer than subsequent builds.
pnpm buildpnpm tauri build --debugVerified output path:
target/debug/markdowner-desktopUse pnpm build for the normal frontend production build. Add build subcommands when you want a Tauri bundle, local install, or launch-after-install flow.
pnpm build # type-check and build the frontend
pnpm build debug # debug Tauri build
pnpm build dmg # release DMG with ad-hoc signing
pnpm build universal dmg # universal Apple Silicon + Intel DMG
pnpm build install # release Tauri build, install to /Applications
pnpm build install open # install, then launch the installed app
pnpm build debug install open # debug build, install, then launch
pnpm build:install:open # package-script alias for install + open
pnpm build:mac:dmg # package-script alias for release DMG
pnpm build:mac:universal:dmg # package-script alias for universal DMGInstall options:
MARKDOWNER_INSTALL_PATH=~/Applications pnpm build install
pnpm build install -- --path ~/Applications
pnpm build install -- --no-build # install an already-built bundle
pnpm build install -- --open # flag form of "open"Behavior:
pnpm buildwithout subcommands runs the frontend build used by Tauri'sbeforeBuildCommandinstallis macOS only (the bundle target insrc-tauri/tauri.conf.jsonisapp)- Installs to
/Applicationsby default; override withMARKDOWNER_INSTALL_PATHor--path <DIR> - Uses
sudoautomatically only when the install path is not writable - Replaces any existing
Markdowner.appat the destination, copies withditto, and clears thecom.apple.quarantineattribute so the locally built bundle launches without a Gatekeeper prompt - Pass
openor--opento launch the installed bundle after copying scripts/build-and-install.shremains as a compatibility wrapper aroundpnpm build install
Markdowner is configured for Tauri ad-hoc macOS signing through src-tauri/tauri.conf.json:
"signingIdentity": "-"Build a DMG for direct download distribution:
pnpm build:mac:dmgThe command prints the generated .dmg path and a SHA-256 checksum. Share both with testers so they can verify the downloaded file.
For one DMG that supports both Apple Silicon and Intel Macs, install both Rust targets once and run the universal build:
rustup target add aarch64-apple-darwin x86_64-apple-darwin
pnpm build:mac:universal:dmgImportant limitation: ad-hoc signing is not Developer ID signing and does not notarize the app. A downloaded DMG can still require the recipient to allow the app manually in macOS Privacy & Security on first launch. A warning-free double-click flow for general users still requires the paid Apple Developer Program, a Developer ID certificate, and notarization.
Tester launch instructions:
1. Open the DMG and drag Markdowner.app to Applications.
2. Try opening Markdowner once.
3. If macOS blocks it, open System Settings -> Privacy & Security.
4. In the Security section, click Open Anyway for Markdowner.
5. Confirm Open. Future launches should work normally.
Builds the Tauri app and launches it immediately.
scripts/build-and-run.sh # release build, then open the .app
scripts/build-and-run.sh --debug # debug build
scripts/build-and-run.sh --no-run # build only, do not launchBehavior:
- Verifies
pnpmandcargoare onPATH - Runs
pnpm installifnode_modulesis missing - Runs
pnpm tauri build(orpnpm tauri build --debug) - On macOS opens
target/{release,debug}/bundle/macos/Markdowner.app; on other platforms runstarget/{release,debug}/markdowner-desktop
Run the frontend and Rust test suites:
pnpm test
cargo testUseful focused checks:
cargo test -p markdowner-core
pnpm build
pnpm tauri build --debug- The Tauri desktop shell is working locally on macOS and no-cost ad-hoc DMG generation is available; paid Developer ID signing/notarization remains follow-up for warning-free public distribution.
- The frontend production bundle is currently large enough to trigger Vite's chunk size warning.
- Windows support is a planned next step rather than a completed local workflow.
crates/markdowner-macosstill exists as a reference implementation and regression target while the Tauri shell becomes the main app entrypoint.
MIT. See LICENSE for details.