Comments, phase 2 — the pane, the composer, and the inline session
The card window recomposes into three componentized panes (body, comments, attributes) with two mounts — beside or body-over-comments at ~3:2 — behind View ▸ Comments Beside Body. View ▸ Show Comments is one persisted app-wide bit, no content-derived auto-show; File ▸ Add Comment flips it on and focuses the composer. The thread renders author lines, edited markers, card-subset Markdown bodies, and read-only Quick Look chips under a count header with the sort- direction control. The composer edits comments/.draft/ on the slow cadence (blur, close, quit, ~30s interval), Escape only moves focus, ⌘↩ posts. Inline edit is a body-edit session in miniature: 700ms debounce, Save/⌘↩ commits, Cancel and Escape revert to session-start bytes, close flushes. File drops within either authoring surface carve out of the window-wide card default into that surface's attachments/; paperclips cover the no-drag path. Close flush runs inline flush, then draft save, then the comments/.trash purge; open sweeps crash residue. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -89,7 +89,7 @@ public final class CardAttachments {
|
||||
/// the very same store method a whole-window drop uses.
|
||||
public func add() {
|
||||
guard isEditable, cardFolder != nil else { return }
|
||||
let urls = Self.chooseFiles()
|
||||
let urls = AttachmentPanel.chooseFiles(message: "Choose files to attach to this card.")
|
||||
guard !urls.isEmpty else { return }
|
||||
|
||||
// The sandbox's half, `BoardDropContext.commitFileDrop`'s rule: `start…` answers false for a
|
||||
@@ -172,22 +172,35 @@ public final class CardAttachments {
|
||||
return [cardFolder]
|
||||
}
|
||||
|
||||
// MARK: - The panel
|
||||
}
|
||||
|
||||
/// The multi-select open panel behind Add Attachment… — **every file type**, because a card's
|
||||
/// `attachments/` takes anything (01-storage-format.md § Attachments) and a filter here would be
|
||||
/// this app deciding what the user may keep beside their card.
|
||||
///
|
||||
/// Directories are not choosable, which is the panel's own spelling of the same refusal a
|
||||
/// folder drop gets (`FinderDrop`): the attachment model is flat top-level files.
|
||||
private static func chooseFiles() -> [URL] {
|
||||
// MARK: - The panel
|
||||
|
||||
/// The multi-select open panel behind every "add a file" affordance in the card window — File ▸ Add
|
||||
/// Attachment… and the attachments header's plus (card-scoped), and the composer's and inline
|
||||
/// editor's paperclips (comment-scoped, 05-card-window.md ▸ The comments column).
|
||||
///
|
||||
/// **Every file type**, because a card's `attachments/` takes anything (01-storage-format.md §
|
||||
/// Attachments) and a filter here would be this app deciding what the user may keep beside their
|
||||
/// card. Directories are not choosable, which is the panel's own spelling of the same refusal a
|
||||
/// folder drop gets (`FinderDrop`): the attachment model is flat top-level files.
|
||||
///
|
||||
/// Shared rather than copied per surface for `BoardWriter.importFiles`' reason one layer up: the
|
||||
/// paperclip is "the section header's add-affordance pattern" and a second panel that happened to
|
||||
/// allow folders would make that sentence false.
|
||||
@MainActor
|
||||
enum AttachmentPanel {
|
||||
|
||||
/// - Parameter message: the panel's one line of guidance — the only thing that differs between
|
||||
/// the card-scoped and comment-scoped calls, because it is the only thing that *is* different.
|
||||
static func chooseFiles(message: String) -> [URL] {
|
||||
let panel = NSOpenPanel()
|
||||
panel.canChooseFiles = true
|
||||
panel.canChooseDirectories = false
|
||||
panel.allowsMultipleSelection = true
|
||||
panel.resolvesAliases = true
|
||||
panel.prompt = "Add"
|
||||
panel.message = "Choose files to attach to this card."
|
||||
panel.message = message
|
||||
guard panel.runModal() == .OK else { return [] }
|
||||
return panel.urls
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user