Skip to content

Repository files navigation

remark-desk

Desktop GUI for remark β€” Markdown ⇄ Structured JSON, plugin-driven, batch-capable.

Status Electron Tauri License: MIT Fork of remark 🌐 δΈ­ζ–‡η‰ˆ

A monorepo that ships two complete desktop applications on top of the remark markdown processor β€” built and shipped from the same source tree, sharing one React UI, one feature set, and one converter (the structured document tree).

Table of contents

Why remark-desk?

remark is the most popular markdown processor (used in Next.js, VitePress, Astro, MDX, etc.). It already has a CLI (remark-cli) for batch processing markdown files, but for day-to-day authoring, you need a GUI β€” one that can:

  • Show a live preview as you type
  • Convert markdown to a structured JSON document that downstream tools can consume without re-parsing
  • Manage your plugin chain visually
  • Batch-process an entire folder of .md files with progress, retry, and a report
  • Work offline, single-file portable, no Electron/Tauri version decision needed at install time

remark-desk does all of the above, with the structured-document-tree converter as its main differentiator β€” the right-pane shows a JSON tree that's both human-readable (collapsible, with type labels) and machine-consumable (round-trip-stable, drop into any other tool).

Lineage / 缘衷

This project is a fork + extension of remarkjs/remark, not an independent product. Understanding this lineage explains most of the design decisions in the codebase.

remark-desk started as a monorepo clone of remarkjs/remark, then added a desktop GUI layer on top. The upstream packages under packages/ are vendored unmodified:

Path Upstream Role
packages/remark/ remarkjs/remark the unified pipeline
packages/remark-parse/ remarkjs/remark-parse mdast parser
packages/remark-stringify/ remarkjs/remark-stringify mdast serializer
packages/remark-cli/ remarkjs/remark-cli CLI wrapper
packages/remark-gfm/ remarkjs/remark-gfm GFM extension
…and more (the full monorepo) (parser, rehype, lint, etc.)

The root license is the original Titus Wormer MIT license (remark's original author). The package.json workspace list still points at the upstream packages verbatim, with the new packages/remark-gui/ entry appended on top.

What's new in this fork

New code Where What
Desktop GUI (Electron) packages/remark-gui/ ~75 MB portable .exe
Desktop GUI (Tauri 2) remark_Rust/ ~16 MB portable .exe
Structured doc tree electron/ipc/processor.ts, src-tauri/src/ast.rs markdownToDocument / jsonToMarkdown (the main differentiator)
Brand mark v5 build/icon.svg, src/components/Brand.tsx { R } with 12% safe margin
Batch processing electron/ipc/batch.ts, src-tauri/src/commands/batch.rs recursive folder scan + report
Plugin manager UI src/components/PluginManager.tsx visual .remarkrc editor + marketplace
Command palette src/components/CommandPalette.tsx 19 commands via Ctrl+Shift+P

What was borrowed from other projects

remark-desk stands on the shoulders of a number of widely-used open-source projects. We didn't reinvent any of these β€” we used them, and we list them here so the lineage is clear.

Borrowed From How
Markdown engine (Tauri build) kivikakk/comrak Pure-Rust GFM parser, drop-in for unified
Tauri shell tauri-apps/tauri v2.11, the webview-based Rust desktop framework
React UI react 18, the renderer in both builds
Vite dev server vite the renderer dev server for both builds
CodeMirror 6 editor codemirror.net markdown syntax highlighting
Tailwind CSS tailwindcss.com utility-first CSS, dark mode built-in
highlight.js highlightjs.org code-block syntax highlighting (180+ languages)
.remarkrc convention remarkjs/remark inherited verbatim β€” same format, same loader
GitHub Actions CI remarkjs/.github the upstream CI scripts, lightly adapted for dual builds

What we built that has no upstream equivalent

  • The structured document tree shape (headings as keys, tables as {headers, rows}, lists as string[], etc.). The closest similar concept is pandoc's JSON AST, but our shape is application-facing (round-trip-clean, hand-editable), not a full representation of the parser state.
  • The dual-build system (Electron + Tauri from one source tree).
  • The direction toggle (MD↔JSON) with looksLikeJson auto-detect (the toggle is a hint, not a hard switch).
  • The race-condition-safe state management for rapid direction toggles (verified by 27-case test in scripts/test-direction-toggle-fix.cjs).

Two builds, one product

Electron (default) Tauri 2 (alternative)
Folder packages/remark-gui/ remark_Rust/
Runtime Node 20 + Chromium Native WebView + Rust
Portable size ~75 MB (single .exe) ~16 MB (single .exe)
Cold start ~1.5 s ~0.3 s
Markdown engine unified + remark + rehype (Node) comrak 0.31 (pure-Rust)
Plugin loading βœ… dynamic import() of npm packages (150+) ⚠️ display-only (comrak built-ins)
Config file .remarkrc (active) .remarkrc (read-only β€” same format)
Frontend React 18 + Vite + CodeMirror 6 + Tailwind same (shared via copy)
IPC ipcRenderer.invoke (Electron) invoke() (Tauri) β€” same interface
.remarkrc portability βœ… bidirectional with Tauri βœ… bidirectional with Electron

The two builds ship from the same React UI, the same feature set, and the same .remarkrc format β€” switching is a no-op (just use the other binary). They differ in size / startup / plugin loading, so:

  • Use Electron if you need user-defined remark plugins (the full 150+ plugin ecosystem) or you have a custom .remarkrc.
  • Use Tauri if you're fine with standard CommonMark + GFM and want a smaller, faster binary.

The structured document tree

The right pane shows a JSON tree that's both human-readable and machine-consumable. Headings become object keys; tables, lists, code blocks, and quotes each get a canonical shape; and the whole thing round-trips β€” md β†’ json β†’ md β†’ json' produces json == json' byte-equivalent (or data-equivalent for the array-of-objects case).

Example

Input markdown:

# Introduction

remark is a markdown processor.

## Setup

```bash
npm install remark

Plugins

Name Stars
remark-gfm 2.1k
remark-math 1.4k

> Note: all of remark's tooling is built on [unified](https://unifiedjs.com).

Output JSON:

{
  "Introduction": [
    "remark is a markdown processor."
  ],
  "Setup": [
    { "code": "bash", "content": "npm install remark" }
  ],
  "Plugins": {
    "headers": ["Name", "Stars"],
    "rows": [
      { "Name": "remark-gfm",  "Stars": "2.1k" },
      { "Name": "remark-math", "Stars": "1.4k" }
    ]
  },
  "quotes": [
    "Note: all of remark's tooling is built on [unified](https://unifiedjs.com)."
  ]
}

Shape reference

Markdown element JSON shape
Heading Object key (the heading text)
Paragraph string
Multiple paragraphs string[] (or string if there's only one)
List (one level) string[] (items)
Nested list string[][] (each top-level item is a list of its sub-items)
Table { "headers": string[], "rows": Array<{[header]: cell}> }
Code block { "code": string, "content": string } (or string if no language)
Blockquote string (collected into quotes: string[] when there are several)
Content before any heading parked under the content key so it's not lost

Multiple element types under the same heading are joined: { "heading": { "paragraphs": [...], "list": [...], "table": {...} } }.

Bonus: lenient input. Hand-rolled JSON arrays of objects are also accepted as table input β€” [{name: "Alice"}, {name: "Bob"}] is rendered back as a GFM table (header = union of keys in first-appearance order).

Why this shape?

A few design choices are worth calling out:

  • Headings as object keys, not nested children. This makes the JSON a flat namespace (one key per heading) that you can grep, index, or hand-edit without walking a tree. Downstream tools can read a heading with a simple doc["Setup"] lookup.
  • Tables as { headers, rows } with rows as object arrays, not arrays of arrays. Object rows are self-describing (row.Name, row.Stars) and survive column reordering when round-tripping. Array rows would lose column labels after one round-trip.
  • Lists as flattened string[]. A one-level list is just ["a", "b", "c"]; a nested list is string[][]. We intentionally don't model the tree of ul β†’ li nodes because the user's mental model is "list of items", not "tree of list nodes".
  • Code as { code, content } so the language tag is preserved. Without it, ```bash and ```sh would both round-trip to a language-less block.

The rejected alternative β€” a flat key-value like { "h1_0": "...", "table_1_headers": [...], "table_1_rows": [...] } β€” was simpler but lost too much structure; headings collided, tables became two separate keys, and the order was implicit.

Design system

remark-desk has its own brand mark that lives in two places, kept visually identical so the desktop icon and the in-app header always match.

The { R } mark

A rounded square (1024Γ—1024, 22.5% corner radius) with a blue β†’ indigo β†’ violet gradient, a single letter R in white, framed by a pair of curly braces β€” { R }. The braces are drawn as SVG paths (not font characters) so they don't depend on the host platform's font set, and the tips are pulled in to a 12% safe margin so the mark stays legible at 16Γ—16 (favicon) and at 256Γ—256 (high-DPI desktop).

Property Value
Canvas 1024Γ—1024, rx=230 (22.5% of 1024)
Gradient stops #3b82f6 (blue 500) β†’ #6366f1 (indigo 500) β†’ #8b5cf6 (violet 500)
R font-size 660 px, weight 900, letter-spacing -30, all-white
Brace stroke 72 px, all-white, drawn as <path> with cubic BΓ©zier
Brace tips x 130 / 894 (12% margin from each side)
Content bounds x ∈ [130, 894], y ∈ [200, 820]

The same SVG feeds both the Windows .ico (electron-builder input) and the Tauri icon set (via npx tauri icon).

In-app Brand component

The same mark renders inline in the app's top toolbar, via a shared Brand.tsx component (in both packages/remark-gui/src/components/ and remark_Rust/src/components/). It uses useId() for unique gradient IDs so multiple instances on one page don't collide.

<svg viewBox="0 0 1024 1024" aria-label="remark-desk">
  <defs>
    <linearGradient id={gradId} x1="0" y1="0" x2="1024" y2="1024">
      <stop offset="0%"  stopColor="#3b82f6" />
      <stop offset="50%" stopColor="#6366f1" />
      <stop offset="100%" stopColor="#8b5cf6" />
    </linearGradient>
  </defs>
  <rect width="1024" height="1024" rx="230" fill={`url(#${gradId})`} />
  <path d="M 290 200 C 220 200 190 240 190 320 ..." stroke="white" strokeWidth={72} fill="none" />
  <text x="512" y="750" textAnchor="middle" fontSize={660} fontWeight={900} fill="white">R</text>
</svg>

The branding was iterated through 5 versions to land on this β€” earlier attempts had the braces too close to the edge (6.8% margin β†’ 12% margin in v5) and the R letter too thin. v5 is the first version that is fully legible at 16Γ—16, the smallest size Windows uses for taskbar icons.

Features

Editor

  • Open / save .md / .markdown / .mdx via native dialog
  • CodeMirror 6 with markdown syntax highlighting
  • Live preview β€” debounced 300 ms; renders the structured tree on the right (collapsible nodes, raw JSON toggle, copy button)
  • Auto-load .remarkrc from the file's directory (walk-up to root)
  • Direction toggle in the top toolbar β€” MD β†’ JSON or JSON β†’ MD (the app auto-detects what your content looks like, so the toggle is a hint, not a hard switch)
  • Light / dark theme with smooth transition
  • Three layouts: editor-only / split / preview-only
  • Ctrl+S to save, Ctrl+K to clear content, Ctrl+Shift+P for command palette

Plugins

  • Visual editor for .remarkrc.json (enable / disable / options)
  • Curated marketplace of 10 popular remark / rehype plugins
  • Add by name for any of the 150+ plugins not in the curated list
  • Live-reload after save (next preview uses the new plugin set)

Batch processing

  • Drop a folder into the window, or pick one
  • Recursive scan for .md / .markdown / .mdx
  • Bounded concurrency (1–16, default 4)
  • Real-time progress (done / succeeded / failed / current file)
  • Cancel mid-run
  • Export report as JSON / CSV / Markdown

Polish

  • Command Palette (Ctrl+Shift+P) with 19 commands
  • Global shortcuts: Ctrl+B (plugin manager), Ctrl+J (mode), Ctrl+S (save), Ctrl+K (clear), Ctrl+1/2/3 (layout)
  • Dark mode with highlight.js dark theme
  • Friendly errors with πŸ’‘ hint for common failures (plugin not installed, parse errors, file I/O)
  • Drag & drop files / folders
  • Full Chinese (zh-CN) localization

Quick start

Install dependencies

git clone https://github.com/Dannykey/remark-desk.git
cd remark-desk
npm install

Run the Electron build (dev mode)

cd packages/remark-gui
npm start

This builds the renderer + main, then launches Electron with hot-reload.

Run the Tauri build (dev mode)

cd remark_Rust
npm install
npm run tauri:dev

This starts Vite on localhost:1420 and launches the Tauri window pointing at it. Edits in src/ hot-reload; edits in src-tauri/ trigger a Rust rebuild.

Build a portable binary

See Building below.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Renderer (React) ──────────────────────────────────┐
β”‚  App.tsx                                                                          β”‚
β”‚  β”œβ”€ Toolbar       (open / save / clear / mode / layout / direction toggle /  )   β”‚
β”‚  β”œβ”€ Editor        (CodeMirror 6, markdown)                                      β”‚
β”‚  β”œβ”€ Preview       (structured JSON tree + raw JSON toggle)                      β”‚
β”‚  β”œβ”€ PluginManager (enable / disable / marketplace)                              β”‚
β”‚  β”œβ”€ BatchView     (folder drop, progress, report export)                        β”‚
β”‚  └─ StatusBar     (line / char / config source / theme)                         β”‚
β”‚                                                                                   β”‚
β”‚  Calls: remarkApi.remark.process(md) / processJson(json) / batch.*  etc.        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                      β”‚  (Electron IPC or Tauri invoke)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Main (Electron: Node 20) ──────────────────┐
β”‚  electron/main.ts: window + tray + menu                       β”‚
β”‚  electron/preload.ts: contextBridge β†’ window.remarkApi        β”‚
β”‚  electron/ipc/remark.ts: unified pipeline (load .remarkrc)   β”‚
β”‚  electron/ipc/batch.ts: p-limit parallel scan                  β”‚
β”‚  electron/ipc/files.ts: dialog + fs                          β”‚
β”‚  electron/ipc/plugins.ts: .remarkrc visual editor             β”‚
β”‚  electron/ipc/processor.ts: the structured-doc-tree converter β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                      β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Backend (Tauri: Rust) ─────────────────────┐
β”‚  src-tauri/src/main.rs   β€” 4-line stub                       β”‚
β”‚  src-tauri/src/lib.rs    β€” tauri::Builder + invoke_handler  β”‚
β”‚  src-tauri/src/state.rs  β€” AppState (active batch jobs)     β”‚
β”‚  src-tauri/src/error.rs  β€” AppError + AppResult<T>          β”‚
β”‚  src-tauri/src/commands/                                    β”‚
β”‚  β”œβ”€ files.rs    open / save / read-dir / pick-dir            β”‚
β”‚  β”œβ”€ remark.rs   process_markdown / process_batch (comrak)   β”‚
β”‚  β”œβ”€ batch.rs    scan / start / cancel / results / export    β”‚
β”‚  └─ plugins.rs  list / read / readPath / write / pickDir    β”‚
β”‚  src-tauri/src/ast.rs     the structured-doc-tree converter β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The two backends produce byte-equivalent JSON for the same input (verified by scripts/roundtrip-verify.cjs for Electron and 31 #[test] cases in src-tauri/src/ast.rs for Rust).

Project layout

remark-desk/
β”œβ”€β”€ packages/
β”‚   β”œβ”€β”€ remark/             ← (upstream) unified markdown processor
β”‚   β”œβ”€β”€ remark-cli/         ← (upstream) CLI wrapper
β”‚   β”œβ”€β”€ remark-parse/       ← (upstream) mdast parser
β”‚   β”œβ”€β”€ remark-stringify/   ← (upstream) mdast serializer
β”‚   └── remark-gui/         ← **Electron desktop app** (the main work)
β”‚       β”œβ”€β”€ electron/           main + preload + IPC handlers
β”‚       β”œβ”€β”€ src/                React UI (App, Editor, Preview, …)
β”‚       β”œβ”€β”€ scripts/            build, test, generate-icons
β”‚       β”œβ”€β”€ locales/            zh-CN strings
β”‚       β”œβ”€β”€ build/              icon.svg β†’ .ico / .icns / .png
β”‚       β”œβ”€β”€ styles/             Tailwind + custom CSS
β”‚       └── release/            built .exe (gitignored)
β”œβ”€β”€ remark_Rust/            ← **Tauri 2 desktop port** (alternative build)
β”‚   β”œβ”€β”€ src/                    React UI (mirrors packages/remark-gui/src)
β”‚   β”œβ”€β”€ src-tauri/              Rust backend
β”‚   β”‚   β”œβ”€β”€ src/                main + lib + state + error + commands/
β”‚   β”‚   β”œβ”€β”€ icons/              generated from remark-gui's icon.svg
β”‚   β”‚   └── tauri.conf.json     productName, identifier, nsis target
β”‚   └── target/                 built .exe (gitignored)
β”œβ”€β”€ progress.md            ← session-by-session working notes
β”œβ”€β”€ package.json           ← pnpm workspace root
β”œβ”€β”€ .gitignore             ← ignores node_modules / release / target / dist
└── readme.md              ← this file

Building

Electron build

The Electron build outputs a single .exe (~75 MB) into packages/remark-gui/release/0.1.0/Remark 0.1.0.exe.

# from the monorepo root
npm install
npm run --workspace=remark-gui build      # vite + tsc
npm run --workspace=remark-gui dist:win:portable
# β†’ packages/remark-gui/release/0.1.0/Remark 0.1.0.exe

For a dev iteration loop, use npm start (or npm run --workspace=remark-gui start).

Tauri build

The Tauri build outputs a single .exe (~16 MB) into remark_Rust/src-tauri/target/release/remark-rust.exe.

Prerequisites (one-time, only on Windows):

  • Rust toolchain (rustup β†’ cargo + rustc)
  • MSVC C++ build tools (Visual Studio 2022 "Desktop development with C++" or Build Tools for Visual Studio)
# from the monorepo root
npm install
cd remark_Rust
# generate platform icons from src-tauri/icons/icon.svg (one-time)
npx tauri icon src-tauri/icons/icon.png

# dev mode (hot reload)
npm run tauri:dev

# production build
npm run tauri:build
# β†’ remark_Rust/src-tauri/target/release/remark-rust.exe

Tip β€” icon cache gotcha: Tauri 2's tauri-winres caches the icon resource by hashing resource.rc. If you regenerate icons with npx tauri icon but the .rc content (absolute path to the .ico) doesn't change, the .exe may still embed the old icon. If that happens, run cargo clean --release -p remark-rust to force re-linking, then re-build.

Development workflow

# Workspace-level commands
npm run --workspace=remark-gui start       # Electron dev (hot reload)
npm run --workspace=remark-gui build       # type-check + Vite build
npm run --workspace=remark-gui dist:win    # Windows portable

cd remark_Rust
npm run tauri:dev                          # Tauri dev (hot reload)
npm run tauri:build                        # Tauri portable

The two builds share src/ patterns: components, stores, hooks, locales. If you change a component, copy it from remark-gui/src/ to remark_Rust/src/ (or write a tiny script to do it). See remark_Rust/README.md for the file-by-file map.

Testing

# Round-trip converter tests (md β†’ json β†’ md β†’ json == json)
node scripts/roundtrip-verify.cjs          # 20 cases
node scripts/test-direction-toggle-fix.cjs  # 27 cases (rapid toggle)
node scripts/test-content-format-fix.cjs    # lenient input

# Tauri-side: cargo test (in remark_Rust/src-tauri/)
cd remark_Rust/src-tauri
cargo test --lib                           # 31 cases

All test scripts test production code paths, not parallel implementations.

Performance

Both builds target sub-second interactive feedback on typical documents. Numbers below are measured on a Windows 11 / i5-12400 machine, single-file .md around 5 KB, 30+ headings, 1 table, 1 code block, 1 list.

Metric Electron Tauri 2
Cold start (window β†’ ready) ~1.5 s ~0.3 s
Single-file conversion (md ↔ json) ~40 ms ~30 ms
100-file batch (concurrency 4) ~6 s ~5 s
Idle RAM (after window open) ~180 MB ~30 MB
Portable binary size ~75 MB ~16 MB
Renderer main bundle ~1.1 MB (gz 366 KB) ~1.1 MB (gz 366 KB)

The Tauri build wins on every dimension that depends on the runtime (the shell, the embedded webview, the IPC bridge) because the heavy lifting (parsing markdown) is already pure-Rust via comrak. The renderer bundle is the same in both builds.

Limitations

These are intentional in v0.1.0, not bugs:

  • No real-time collaboration. Single-user, single-device.
  • No remote sync. Fully offline. Your markdown never leaves the machine.
  • Tauri build: plugin loading is display-only. The Tauri backend uses comrak's built-in extensions (tables, strikethrough, task lists, autolinks, footnotes). For 150+ remark plugins, use the Electron build.
  • Tables with merged cells are not preserved. GFM tables with colspan / rowspan are rendered cell-by-cell after a single round-trip; the merge is lost.
  • .remarkrc walking is per-file-directory only. No per-folder config switch in the GUI yet (the loader walks up to the repo root on its own).

Roadmap (v0.2.0+)

Tentative plans, in priority order:

  • Mermaid diagram preview (Electron build; via remark-mermaid plugin)
  • LaTeX math rendering (via remark-math + KaTeX)
  • Outline pane (left sidebar TOC, click to jump)
  • Custom CSS injection for the preview (theme override)
  • Per-folder .remarkrc switcher in the GUI
  • Find & replace across headings
  • Word/character count per heading (reuses the markdownToDocument AST)
  • Plugin search in the marketplace (currently category-bucketed only)
  • i18n: ja-JP, en-US (currently only zh-CN)

If you have a request, please open an issue on github.com/Dannykey/remark-desk/issues.

FAQ

Q: Is this a fork of remarkjs/remark? A: It's a monorepo that contains the upstream remark packages (packages/remark/, packages/remark-cli/, etc., unmodified) plus two new directories on top: packages/remark-gui/ (Electron) and remark_Rust/ (Tauri 2). The upstream packages are vendored, not forked, and you can pull new versions of remark at any time.

Q: Can I use the converter without the GUI? A: Yes. The two functions markdownToDocument(md: string): DocTree and jsonToMarkdown(doc: DocTree): string are the public API. They live in:

  • packages/remark-gui/electron/ipc/processor.ts (TypeScript)
  • remark_Rust/src-tauri/src/ast.rs (Rust)

Both implementations are byte-equivalent on the supported input shapes (verified by 20 + 31 round-trip test cases).

Q: Why two builds? A: Electron gives you the full 150+ plugin ecosystem at the cost of a 75 MB binary. Tauri gives you a 16 MB binary with sub-second startup but only CommonMark + GFM built-ins. Pick the one that matches your use case; switching is a no-op (just use the other .exe).

Q: Why "structured document tree" and not "AST"? A: The unified/remark mdast is rich but noisy β€” it carries source positions, formatting info, and node-type-specific metadata that downstream apps don't need. The structured doc tree is the application-facing shape: stable, round-trip-clean, and easy to hand-edit.

Q: Does it phone home? A: No. Both builds are fully offline. The only network calls are optional GitHub-Actions-CI fetches when you set up CI yourself.

Changelog

See progress.md for the full session-by-session working notes. Highlights:

Version Date Highlights
v0.1.0 2026-08-02 Initial release: two builds, structured-doc-tree converter, full plugin + batch + command palette

License & acknowledgements

This project is MIT-licensed β€” see ./license.

This is a fork/derivative of remarkjs/remark, not an independent product. The upstream packages under packages/ are vendored unmodified (see the Lineage section above for the full list). The package.json workspace list in the root still references the upstream packages verbatim; the packages/remark-gui/ entry is the new addition.

For a complete list of bundled third-party software (Electron, Tauri, comrak, React, Vite, etc.) and their licenses, see THIRD_PARTY_LICENSES.md.

Maintainer: Dannykey (921203783@qq.com).

Upstream authors: Titus Wormer (original remark), the unified collective, and the 100+ contributors of the remark ecosystem.

remark-desk is a desktop GUI layer on top of remark. The core markdown processing is upstream's work; what this repo adds is the desktop shell (Electron + Tauri), the React UI, the IPC layer, the plugin manager, the batch processor, and the structured document tree converter (the markdownToDocument / jsonToMarkdown pair in electron/ipc/processor.ts and its Rust mirror in src-tauri/src/ast.rs).

If you find this useful, please consider sponsoring the unified collective β€” they maintain the markdown ecosystem this project is built on.


remark-desk is a derivative of the remark monorepo, customised with two side-by-side desktop implementations.

About

remark-desk

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages