Business Rules: Unit 2 — Document Management

BR-01: Document Lifecycle

Rule Description
BR-01.1 On app launch with no restored windows, exactly ONE blank document MUST be created
BR-01.2 Cmd+N MUST create a new blank document (new tab if tab bar visible, new window otherwise)
BR-01.3 New blank documents MUST have defaultDraftName() as “Untitled” (localized)
BR-01.4 Opening a file MUST decode data as UTF-8; if UTF-8 fails, attempt Shift_JIS and EUC-JP before reporting error
BR-01.5 Save (Cmd+S) on an untitled document MUST present Save As (NSSavePanel)
BR-01.6 Save As (Cmd+Shift+S) MUST NOT delete the original file
BR-01.7 data(ofType:) MUST encode sourceText as UTF-8
BR-01.8 Closing a document with unsaved changes MUST present the standard macOS save confirmation dialog

BR-02: Auto-Save & Versions

Rule Description
BR-02.1 MarkdownDocument MUST declare autosavesInPlace returning true
BR-02.2 Auto-save MUST NOT pause or interfere with the Unit 1 parse pipeline
BR-02.3 Auto-save MUST use the current sourceText at the time data(ofType:) is called
BR-02.4 macOS Versions (File > Revert To > Browse All Versions) MUST be supported via NSDocument
BR-02.5 If auto-save fails (disk full, permissions), the standard NSDocument error alert MUST be shown
BR-02.6 The document MUST NOT be marked as saved (isDocumentEdited = false) if auto-save fails

BR-03: Tabbed Windows

Rule Description
BR-03.1 Tabbed windows MUST be enabled via macOS native NSWindow.allowsAutomaticWindowTabbing
BR-03.2 Each tab MUST display the document’s displayName (filename or “Untitled”)
BR-03.3 Window > Merge All Windows and Window > Move Tab to New Window MUST function via native macOS behavior
BR-03.4 Tab close MUST trigger save confirmation if the document has unsaved changes
BR-03.5 Drag-and-drop tab reordering MUST be supported (macOS native)
BR-03.6 Each tab MUST have its own independent EditorViewModel instance

BR-04: Recent Files

Rule Description
BR-04.1 Opening a document MUST add its URL to the recent documents list via NSDocumentController
BR-04.2 File > Open Recent MUST display recent files managed by NSDocumentController
BR-04.3 File > Open Recent > Clear Menu MUST clear the recent documents list
BR-04.4 Selecting a recent file that no longer exists MUST display a system “file not found” error
BR-04.5 The app MUST register the .md UTType so that .md files open in Shoe Choo when double-clicked

BR-05: EditorSettings Persistence

Rule Description
BR-05.1 EditorSettings MUST be a singleton accessed via EditorSettings.shared
BR-05.2 All settings MUST be persisted to UserDefaults.standard using EditorSettingsKey keys
BR-05.3 On first launch (no UserDefaults values), factory defaults MUST be applied (see domain-entities.md)
BR-05.4 Settings changes MUST be applied immediately to all open editor windows
BR-05.5 “Reset to Defaults” MUST restore all settings to factory defaults and persist them
BR-05.6 Font family change MUST invalidate all render caches and trigger full re-render in all open editors
BR-05.7 Font size change MUST invalidate all render caches and trigger full re-render in all open editors
BR-05.8 Line spacing change MUST invalidate layout and trigger re-render in all open editors
BR-05.9 Appearance override change MUST invalidate render caches (colors differ) and update window appearance

BR-06: Font & Line Spacing

Rule Description
BR-06.1 Font family MUST be selectable from a curated list of monospaced and proportional fonts
BR-06.2 Font size MUST be adjustable in the range 10.0 … 24.0 points, in 1-point increments
BR-06.3 Line spacing MUST be adjustable in the range 1.0 … 2.0, in 0.1 increments
BR-06.4 Font and line spacing changes MUST be previewed in real-time in all open editors
BR-06.5 The selected font MUST be used for body text rendering; heading sizes are relative to the base font size
BR-06.6 Code blocks and inline code MUST always render in a monospaced font regardless of the body font setting

Curated Font List

Font Family Type Bundled with macOS
SF Mono Monospaced Yes (14+)
Menlo Monospaced Yes
Monaco Monospaced Yes
Courier New Monospaced Yes
SF Pro Proportional Yes (14+)
Helvetica Neue Proportional Yes
Georgia Proportional Yes

BR-07: Toolbar

Rule Description
BR-07.1 The toolbar MUST be displayed at the top of the EditorView, above the text editor area
BR-07.2 Toolbar buttons MUST include: Heading (1-3), Bold, Italic, Strikethrough, Inline Code, Link, Image
BR-07.3 Each toolbar button MUST invoke the corresponding EditorViewModel method
BR-07.4 Toolbar buttons MUST use SF Symbols for icons
BR-07.5 Toolbar buttons MUST show a tooltip with the keyboard shortcut hint on hover
BR-07.6 Toolbar buttons for active formatting (e.g., cursor inside bold text) SHOULD appear highlighted
BR-07.7 The toolbar MUST be implemented using SwiftUI .toolbar modifier for native macOS integration

Toolbar Button Mapping

Button SF Symbol ViewModel Method Shortcut
Heading 1 number setHeading(level: 1) Cmd+1
Heading 2 number setHeading(level: 2) Cmd+2
Heading 3 number setHeading(level: 3) Cmd+3
Bold bold toggleBold() Cmd+B
Italic italic toggleItalic() Cmd+I
Strikethrough strikethrough toggleStrikethrough()
Inline Code chevron.left.forwardslash.chevron.right toggleInlineCode() Cmd+Shift+K
Link link insertLink() Cmd+K
Image photo insertImage()