Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 4 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ Marcdown is built for markdown writers who want a distraction-free, keyboard-dri

- **Plain text, local files** β€” notes live in `~/marcdown/` under your control; sync via iCloud Drive, Dropbox, Syncthing, or just `git`. No lock-in.
- **Live markdown rendering** β€” as you type `# Heading` or `**bold**`, the text renders in place (WYSIWYG-ish; no formatting hotkeys needed).
- **Markdown tables** β€” full table support, not just lists and basic text.
- **Native macOS** β€” Swift 6, AppKit + SwiftUI; no Electron, no browser overhead. Fast, low-footprint.
- **Keyboard-first UX** β€” global hotkey, panel overlay, all controls accessible via chords. No mouse required.

## Status

**Working today** (released in slices):

- Live markdown styling (headings, bold, italic, code blocks, blockquotes, tables, lists)
- Live markdown styling (headings, bold, italic, code blocks, blockquotes, lists)
- Floating panel with global hotkey (Cmd+Shift+Space, customizable)
- Multi-note management (quick switcher, prev/next, 9-item recent history)
- H1-driven auto-rename (Untitled N.md becomes "My Note.md" once you type a level-1 heading)
Expand All @@ -40,48 +39,7 @@ Marcdown is built for markdown writers who want a distraction-free, keyboard-dri

## Architecture

Marcdown is built as a **macOS 15+ background-only app** in Swift 6 with strict concurrency. The UI layer is SwiftUI + AppKit; the core (model, IO, indexing) is pure Foundation, packaged as SPM libraries.

```
marcdowndev/
β”œβ”€β”€ App/ # Main app target
β”‚ β”œβ”€β”€ Sources/
β”‚ β”‚ β”œβ”€β”€ MarcdownApp.swift # Entry point (@main, Settings scene)
β”‚ β”‚ β”œβ”€β”€ AppDelegate.swift # Background mode, global hotkey registration
β”‚ β”‚ β”œβ”€β”€ PanelController.swift # NSPanel lifecycle and toggling
β”‚ β”‚ β”œβ”€β”€ PanelRootView.swift # Root SwiftUI view (editor + overlays)
β”‚ β”‚ β”œβ”€β”€ NotesStore.swift # @MainActor, @Observable facade
β”‚ β”‚ β”œβ”€β”€ NoteViewModel.swift # Per-note editor state, debounced save
β”‚ β”‚ β”œβ”€β”€ QuickSwitcher.swift # Fuzzy-search note picker overlay
β”‚ β”‚ β”œβ”€β”€ SettingsView.swift # Hotkey rebinder UI
β”‚ β”‚ └── Shortcuts.swift # Global hotkey name definition
β”‚ └── Resources/
β”‚ β”œβ”€β”€ Info.plist
β”‚ └── Marcdown.entitlements
β”œβ”€β”€ Packages/
β”‚ β”œβ”€β”€ MarcdownCore/ # Pure Foundation model & IO
β”‚ β”‚ β”œβ”€β”€ Sources/MarcdownCore/
β”‚ β”‚ β”‚ β”œβ”€β”€ Note.swift # Model (id, body, modifiedAt, title extraction)
β”‚ β”‚ β”‚ β”œβ”€β”€ NoteStore.swift # actor: load, save, loadOrCreate disk IO
β”‚ β”‚ β”‚ β”œβ”€β”€ NoteLocation.swift # Constants (~/marcdown/, scratch.md, etc.)
β”‚ β”‚ β”‚ └── NotesIndex.swift # actor: file watcher, list, create, delete, rename
β”‚ β”‚ └── Tests/MarcdownCoreTests/
β”‚ β”‚ └── NoteTitleTests.swift
β”‚ β”œβ”€β”€ MarcdownStyling/ # Markdown β†’ NSAttributedString
β”‚ β”‚ β”œβ”€β”€ Sources/MarcdownStyling/
β”‚ β”‚ β”‚ β”œβ”€β”€ MarkdownStyler.swift # @MainActor API (parse, walk, apply attributes)
β”‚ β”‚ β”‚ β”œβ”€β”€ StyleWalker.swift # struct: MarkupWalker impl (mutating)
β”‚ β”‚ β”‚ β”œβ”€β”€ LineOffsetIndex.swift # UTF-8 column β†’ UTF-16 NSRange converter
β”‚ β”‚ β”‚ └── StylingTheme.swift # Semantic NSColors (heading, bold, code, etc.)
β”‚ β”‚ └── Tests/MarcdownStylingTests/
β”‚ β”‚ β”œβ”€β”€ MarkdownStylerTests.swift
β”‚ β”‚ └── LineOffsetIndexTests.swift
β”‚ └── MarcdownEditor/ # NSTextView + SwiftUI glue
β”‚ └── Sources/MarcdownEditor/
β”‚ β”œβ”€β”€ NoteEditorView.swift # NSViewRepresentable (TextKit 1 stack)
β”‚ └── EditorTextView.swift # NSTextView subclass, IME-aware restyle
└── project.yml # xcodegen config
```
Marcdown is built as a **macOS 15+ background-only app** in Swift 6 with strict concurrency. The UI layer is SwiftUI + AppKit; the core (model, IO, indexing) is pure Foundation, packaged as four local SPM packages.

### Module breakdown

Expand Down Expand Up @@ -222,10 +180,11 @@ If any CI or release job fails, a summary message is posted to `#marcdown-ci` wi
| ---------------------- | -------------------------------------------- |
| **Cmd+N** | New note |
| **Cmd+P** | Quick switcher (fuzzy search) |
| **Cmd+Shift+⌫** | Delete current note (with confirmation) |
| **Cmd+Shift+⌫** | Delete current note |
| **Cmd+Shift+[** | Previous note |
| **Cmd+Shift+]** | Next note |
| **Cmd+1** to **Cmd+9** | Jump to recent (1 = most recent, 9 = oldest) |
| **Cmd+K** | Command Palette (Export, other actions) |
| **Cmd+,** | Settings (hotkey rebinder) |
| **Esc** | Hide panel |

Expand Down Expand Up @@ -289,16 +248,6 @@ The styler always applies a baseline set of attributes before walking the AST. A

`NoteEditorView` checks `textView.hasMarkedText` before restyling. This prevents eating in-progress IME input (e.g., Japanese composition). Restyle resumes once composition is complete.

## Project layout

| Directory | Purpose |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **App/** | Main macOS application target (SwiftUI entry, AppKit panel, UI view models). |
| **Packages/MarcdownCore/** | SPM package: Note model, disk IO (NoteStore actor), file indexing (NotesIndex actor). Pure Foundation, no UI deps. |
| **Packages/MarcdownStyling/** | SPM package: Markdown parser wrapper, syntax highlighter (StyleWalker), UTF-8↔UTF-16 converter (LineOffsetIndex). Depends on swift-markdown. |
| **Packages/MarcdownEditor/** | SPM package: TextKit 1 wrapper (NoteEditorView, EditorTextView). Bridges Styling and Core. |
| **project.yml** | xcodegen config (replaces .pbxproj). Defines targets, packages, schemes, build settings. |

## Next steps

If you want to extend Marcdown:
Expand Down
Loading