Files

Edmund architecture: developer overview

The human-readable tour of Edmund's internals. This doc is a summary: every claim here points at the section or doc that owns it. If you're an agent making a change fast, read ../ARCHITECTURE.md instead; if you're a human getting oriented, start here.

What Edmund is

A native macOS Markdown editor with live preview: AppKit + TextKit 2, built with SwiftPM, targeting macOS 14+. Two SPM targets: EdmundCore, the library that holds all editor logic (parsing, rendering, the EditorTextView) plus the test suite, and edmd, the executable, an NSDocument app shell (Settings, menus, window setup) that depends on EdmundCore. Source: ../ARCHITECTURE.md §1.

The two invariants

Everything else in the codebase is built to hold these:

  1. Text storage always equals rawSource. Rendering is attribute-only. Edmund never inserts or deletes display characters. Markdown delimiters are hidden (near-zero font, clear color), never stripped.
  2. TextKit 2 only. The editor never touches NSTextView.layoutManager and never stores NSTextBlock/NSTextTable attributes. Either one silently and permanently reverts the view to TextKit 1.

The home of these rules is ../ARCHITECTURE.md §2.

Map of this folder

  • editor-pipeline.md: rawSource → parse → style → attributed storage, the recompose paths, lazy styling.
  • text-system.md: why TextKit 2 only, the custom layout-fragment decoration mechanism, hiding, the height-estimate and image-wedge constraints.
  • extensibility.md: the themes/extensions design. Design only, not yet implemented on main.
  • sandboxing.md: the App Sandbox preparation plan for the App Store build, modeled on CotEditor. Plan only, nothing sandboxed on main.
  • Planned, not yet written: reader-and-export.md, edit-flow-and-undo.md, app-shell-and-settings.md.

Map of sibling folders

  • ../investigations/: chronicles of hard bugs, one doc per bug class, written up round by round. archives/ holds closed classes (fixed and not expected to recur); the rest are still active.
  • ../dev-guides/: method docs (how to do something), not bug chronicles. Start with live-repro-guide.md if you're reproducing a live-app bug.

Quirks you'll hit

  • A "successful" build can be stale. swift build sometimes prints Build complete! after compiling a changed file without relinking edmd, so you run old code. ../ARCHITECTURE.md §8 has the detection/cure.
  • Delimiters are hidden, not stripped. **bold** keeps its asterisks in storage; they're rendered near-invisible. This is invariant 1 above. Home: ../ARCHITECTURE.md §2.
  • TextKit 2 height estimates cause most viewport glitches. A fragment has a real frame only once laid out; everything else is an estimate that gets corrected as layout catches up. ../ARCHITECTURE.md §8 covers the mitigations; the bug history is ../investigations/viewport-glitch-investigation.md.
  • Images can't be drawn on wrapping (multi-line) layout fragments. Drawing one collapses the fragment's layout to one line. ../ARCHITECTURE.md §9; the full saga (and the stroked-path workaround) is ../investigations/archives/callout-title-wrap-investigation.md.
  • Never mutate storage while an IME is composing. Doing so can strand marked text and permanently break the storage==rawSource sync, which drifts the caret on every later edit. ../ARCHITECTURE.md §8; the full investigation is ../investigations/delete-drift-investigation.md.

Getting started

swift build   # debug build of both targets
swift test    # full suite

Tests live in Tests/EdmundTests. Before committing, run the pre-commit checklist in ../ARCHITECTURE.md §12.