Build the template chooser and Save as Template
The chooser completes its three tiers: bundled by template order, then keyed user templates, then keyless boards by display name — and a malformed user template still lists, by folder name with the loader's own sentence on the row, never failing its neighbours. The store is re-scanned on every presentation and on app activation, the Reveal round trip made honest without watching a folder 09 deliberately leaves unwatched; Reveal lives in the chooser's header and mints the store on first press. Save as Template repeats Duplicate's sequence — progress row with Cancel, flush, detached cancellable copy — through the engine: mint the store, read the next user order before the copy can count itself, Finder-ladder the name, copy excluding .git and .trash/, then stamp the whole template: mapping on the landed copy through updateIndex, with no bracket because the copy lives outside every watched board. Folder attributes deliberately don't carry — the one lock the command stays live under is the read-only-DMG one, and carrying its mode bits would mint a read-only template in the user's own store; the command gates instead on the real hazard, unsaved card content. A signpost names the template only when the ladder renamed it. One name ladder now serves Duplicate and the store. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -59,6 +59,19 @@ final class CardWindowSession: CardSessionFlushing {
|
||||
/// Write-failure surfacing: "the one modal moment on the write-failure path").
|
||||
let bufferGuard: DirtyBufferGuard
|
||||
|
||||
/// The window's raw-source outlet, wired in by the host once the window exists.
|
||||
///
|
||||
/// A closure rather than a stored reference, `CardBodyEditSession.save`'s precedent: the outlet
|
||||
/// is window state living beside this object (`CardWindowHost.rawSource`) rather than inside it,
|
||||
/// and a session that reached into the view's state would be the wrong direction. `nil` — a
|
||||
/// window that has not joined its board — holds nothing, which is true.
|
||||
var rawSourceHoldsUnsavedText: (@MainActor () -> Bool)?
|
||||
|
||||
/// The Edit buffer's dirty text or a typed-in raw-source outlet — see `CardSessionFlushing`.
|
||||
var holdsUnsavedContent: Bool {
|
||||
body.isDirty || rawSourceHoldsUnsavedText?() == true
|
||||
}
|
||||
|
||||
private var hasEnded = false
|
||||
|
||||
/// Both `let`s, wired to each other through a local — the guard's two closures need the buffer,
|
||||
@@ -430,6 +443,9 @@ struct CardWindowHost: View {
|
||||
store: store,
|
||||
cardID: cardID
|
||||
)
|
||||
// The other half of the outlet's wiring: the session answers for this window's unsaved
|
||||
// content, and the outlet is the half that does not live inside it (`CardWindowSession`).
|
||||
session.rawSourceHoldsUnsavedText = { [rawSource] in rawSource.holdsUnsavedText }
|
||||
Self.configureAttachments(attachments, store: store, cardID: cardID)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user