Build the Raw Source outlet
The escape hatch: View > Raw Source (opt-cmd-E) unmounts the whole content area for the literal on-disk index.md in a plain monospaced editor with Cancel/Apply. Raw source is window-level state, not a third body mode — entry rides setMode(.preview), which flushes the Edit session by construction, then reads the file fresh; exit reveals Preview, and an empty body after Apply does not reopen Edit (openIfNeeded already ran). Apply validates the proposed bytes through the loader's own card checks — parseDocument's strict UTF-8/BOM rejection, schema, order — deliberately skipping the uneditable-shape refusal, since a flow-mapping card is exactly what the hatch repairs; invalid bytes alert in place with the loader's own error and no bracket opens. The write is byte-for-byte with no modified stamp and no modified-by clear, per 01's explicit carve-out — the verbatim contract outranks stamping — and identical bytes write nothing. Escape cancels, cmd-Return applies, toggle-off applies too, and cmd-E disables while raw is active via a testable predicate. Tombstoned targets refuse as vanished: a foreign delete is never reverted by a stale buffer. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -652,6 +652,13 @@ public final class BannerCenter {
|
||||
// act happening on its own. The keystrokes are still in the buffer — the banner says the
|
||||
// app could not put them on disk, not that they are gone.
|
||||
if let title { "Couldn't save '\(title)'" } else { "Couldn't save the card" }
|
||||
case let .rawSource(title):
|
||||
// **Apply**, because that is the button they pressed (05-card-window.md ▸ Raw source
|
||||
// outlet), and "source changes" because what failed to land is the whole file as they
|
||||
// typed it — not a save of the card's body, which is what "Couldn't save" would claim.
|
||||
// The buffer is still on screen: the banner says the app could not put those bytes on
|
||||
// disk, not that they are gone.
|
||||
if let title { "Couldn't apply source changes to '\(title)'" } else { "Couldn't apply the source changes" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,6 +675,12 @@ public final class BannerCenter {
|
||||
message
|
||||
case let .staleTarget(message):
|
||||
message
|
||||
case let .invalidSource(error):
|
||||
// The loader's own reason, without its path: the path is always the card's own
|
||||
// `index.md`, and the banner has already named the card. In practice the raw-source
|
||||
// outlet raises this in its alert and never here — the store validates before it opens a
|
||||
// write bracket — so this line exists for a caller that reached the Writer directly.
|
||||
error.reason.description
|
||||
}
|
||||
return trimmed(text)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user