A board leaves as one file and comes back as one — headings are lanes, rows are cards, position is the order
File ▸ Export ▸ writes the frontmost board as Obsidian Kanban Markdown, a plain Markdown outline, or RFC 4180 CSV; File ▸ Import Board… reads any of the three back into a fresh board, format detected rather than asked. Every format encodes order as document position, so an export writes no ranks and an import mints them in parse order on the ordinary create path. Lossy exports post a warning-tone loss row naming the comments and attachments the destination cannot carry. Convert-once: nothing watches, nothing merges. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -3018,6 +3018,27 @@ public enum WriteOperation: Sendable, Equatable, CustomStringConvertible {
|
||||
/// never happened. `title` is the board's display name.
|
||||
case shareBoard(title: String?)
|
||||
|
||||
/// File ▸ Export ▸ … — the board written out as one foreign document (15-import-export.md;
|
||||
/// `BoardExporter.write`). Its own case beside the three copy-the-whole-board commands above, on
|
||||
/// their standing reasoning: a fourth command produces a fourth kind of artifact, and a banner
|
||||
/// saying the app could not "share" or "duplicate" a board nobody asked to share or duplicate would
|
||||
/// name a gesture that never happened.
|
||||
///
|
||||
/// **It is the one operation in this vocabulary that writes outside a board**, which is also why it
|
||||
/// says nothing about the board's own files: the export failed, the board is exactly as it was.
|
||||
/// `title` is the board's display name.
|
||||
case exportBoard(title: String?)
|
||||
|
||||
/// File ▸ Import Board… — a foreign document read, and the fresh board built from it
|
||||
/// (15-import-export.md; `BoardImporter`). It covers the *read* half only: once the parse succeeds,
|
||||
/// the tree is built by the ordinary create path and its failures speak as `.createBoard`,
|
||||
/// `.createLane` and the rest, which is the honest account of what went wrong.
|
||||
///
|
||||
/// **It carries a filename rather than a title**, on `.importAttachment`'s precedent and for its
|
||||
/// reason: the thing the user picked is a file, they are looking at its name in the panel they just
|
||||
/// dismissed, and the board it was going to become does not exist yet to have a title.
|
||||
case importBoard(fileName: String)
|
||||
|
||||
case importAttachment(filename: String)
|
||||
case listAttachments
|
||||
|
||||
@@ -3231,7 +3252,10 @@ public enum WriteOperation: Sendable, Equatable, CustomStringConvertible {
|
||||
// at all, and one with no `schema` is the file the walk just refused.
|
||||
// `.setBoardBackground` joins them on `.mintBoardIndex`'s reasoning: it carries no title
|
||||
// slot, and the board it writes to is the one the user is looking at.
|
||||
case .createBoard, .createLane, .createCard, .importAttachment, .listAttachments,
|
||||
// `.importBoard` joins them on `.importAttachment`'s reasoning: it carries a filename, which is
|
||||
// the name the user is looking at and the only one its banner should say — and there is no
|
||||
// document to enrich from anyway, since the failure it describes is the read of one.
|
||||
case .createBoard, .createLane, .createCard, .importAttachment, .importBoard, .listAttachments,
|
||||
.removeAttachment, .renumberChildren, .relocateLooseFile, .agentGuide, .seedGitignore,
|
||||
.mintBoardIndex, .stampSchema, .setBoardBackground,
|
||||
.displaceClaimedName, .repairDuplicateID, .saveCommentDraft, .postComment,
|
||||
@@ -3255,6 +3279,7 @@ public enum WriteOperation: Sendable, Equatable, CustomStringConvertible {
|
||||
case .duplicateBoard: .duplicateBoard(title: title)
|
||||
case .saveAsTemplate: .saveAsTemplate(title: title)
|
||||
case .shareBoard: .shareBoard(title: title)
|
||||
case .exportBoard: .exportBoard(title: title)
|
||||
case .toggleTask: .toggleTask(title: title)
|
||||
case .editBody: .editBody(title: title)
|
||||
case .rawSource: .rawSource(title: title)
|
||||
@@ -3299,6 +3324,7 @@ public enum WriteOperation: Sendable, Equatable, CustomStringConvertible {
|
||||
// plain container reason its board-level twin does.
|
||||
case .createBoard, .createLane, .createCard, .move, .copy, .delete, .purge, .migrateTombstone,
|
||||
.style, .resize, .collapse, .expand, .rename, .duplicateBoard, .saveAsTemplate, .shareBoard, .paste,
|
||||
.exportBoard, .importBoard,
|
||||
.importAttachment,
|
||||
.listAttachments, .removeAttachment, .relocateLooseFile, .agentGuide, .seedGitignore,
|
||||
.mintBoardIndex, .stampSchema, .setBoardBackground,
|
||||
@@ -3336,6 +3362,8 @@ public enum WriteOperation: Sendable, Equatable, CustomStringConvertible {
|
||||
case let .duplicateBoard(title): Self.phrase("duplicate board", title)
|
||||
case let .saveAsTemplate(title): Self.phrase("save as template", title)
|
||||
case let .shareBoard(title): Self.phrase("share board", title)
|
||||
case let .exportBoard(title): Self.phrase("export board", title)
|
||||
case let .importBoard(fileName): "import board from '\(fileName)'"
|
||||
case let .importAttachment(filename): "import attachment '\(filename)'"
|
||||
case .listAttachments: "list attachments"
|
||||
case let .removeAttachment(filename): "move attachment '\(filename)' to the Trash"
|
||||
|
||||
Reference in New Issue
Block a user