WIP Swift block text editor app, currently focused on its reusable editor engine.
Slopad is a work-in-progress Swift app project for a block text editor. The app layer is still early; most of the current codebase is the reusable editor foundation that the app will use.
That foundation is SlopadEngine: a headless block editor engine for
Notion/Craft-style editors where the document is a tree of blocks, the engine owns editing
semantics, and platform code supplies native input, drawing, and text layout.
The engine is designed to stay platform-independent. An embedding app supplies two edge
pieces: a native UI layer that translates platform callbacks into engine inputs, and a
text layout backend that satisfies BlockTextLayoutProtocol.
The current project proves that path on macOS with AppKit UI and a TextKit2 text layout
backend. SlopadAppKitUI is the reusable AppKit adapter around the engine; it is not the
engine model itself.
swift run SlopadDebugAppSlopadEngine owns the semantic editor model:
- block document state and block identity
- caret, text selection, and block selection
- keyboard, pointer, native command, and IME/composition semantics
- command application, undo/redo, and semantic change projection
- block layout orchestration, hit testing, reveal geometry, and render snapshots
The engine does not own platform widgets. A host view receives native callbacks, translates them into engine input values, asks the engine for layout/render/hit-test facts, and draws using the platform backend it chose.
flowchart LR
subgraph EngineLayer["Engine Layer (Headless)"]
direction TB
Session["SlopadEngine<br/>(EditorSession)"]
subgraph RuntimeOwners["Semantic + Layout Owners"]
direction LR
Model["SlopadEditorModel<br/>(EditorModel)"]
Layout["SlopadBlockLayout<br/>(BlockLayout + TextLayout axis)"]
end
subgraph Foundation["Foundation & Data"]
direction LR
Core["SlopadCoreModel<br/>(Document/Block + public vocabulary)"]
DataStructure["SlopadDataStructure<br/>(PrefixSumRedBlackTree)"]
end
Session --> Model
Session --> Layout
Model --> Core
Layout --> Core
Layout --> DataStructure
end
subgraph PlatformLayer["Platform Layer"]
direction TB
AppKitUI["SlopadAppKitUI<br/>(AppKit adapter)"]
TextKit["SlopadTextKit<br/>(TextKit2 backend)"]
AppKitUI --> TextKit
end
AppKitUI --> Session
TextKit -. implements text layout seam .-> Core
| Layer | Owner | Responsibility |
|---|---|---|
| Platform Layer | SlopadAppKitUI |
Reusable AppKit callback, drawing, focus, scroll, and block chrome adapter around EditorSession. |
| Platform Layer | SlopadTextKit |
TextKit2 implementation of the text layout seam: measurement, line fragments, caret/selection rects, hit testing, and drawing helpers. |
| Engine Layer | SlopadEngine |
Host-facing EditorSession facade. It accepts native-independent input, composes semantic and layout owners, and returns render, hit-test, reveal, and redraw facts. |
| Engine Layer | SlopadEditorModel |
Canonical document, selection, command, transaction, history, and semantic change owner. |
| Engine Layer | SlopadBlockLayout |
Visible order, y/height geometry, invalidation, reveal/hit-test geometry, marker projection, text-layout cache, and block height index owner. |
| Foundation & Data | SlopadCoreModel |
Shared public vocabulary, canonical Document/Block values, and backend seam values such as BlockTextLayoutProtocol. |
| Foundation & Data | SlopadDataStructure |
Pure storage such as PrefixSumRedBlackTree, with no editor, layout, or platform vocabulary. |
SlopadEditorModel and SlopadBlockLayout do not import each other. EditorSession
combines their results and translates semantic changes into layout invalidation.
SwiftPM keeps these responsibilities in separate targets. See Package.swift for the
exact product and target list.
The repository also keeps benchmark and debug targets for development convenience. They validate the current AppKit/TextKit2 path and performance behavior, but they do not define engine semantics.
Benchmark targets:
SlopadUIBenchmarkApp: AppKit UI benchmark harness for frame loops, CSV output, and display flush checks.SlopadHeightBenchmark: block height/index benchmark executable underBenchmarks/.SlopadSessionBenchmark: engine/session benchmark executable underBenchmarks/.
Debug target:
SlopadDebugApp: macOS reference/debug host for scenarios, screenshots, and state assertions.
AGENTS.md: working conventions for agents.ADR/: accepted architecture decisions.docs/LOOP_REQUEST_TEMPLATE.md: copy-paste template for bounded loop requests.docs/ROADMAP.md: achieved milestones, current product direction, and open risks.docs/LESSONS_LEARNED.md: failure patterns from past cleanup/refactor work.
swift package dump-package
swift test --quiet
swift build --product SlopadAppKitUI --quiet
swift build --product SlopadDebugApp --quiet
swift build --product SlopadUIBenchmarkApp --quiet
git diff --check