The phone joins the format — KanbanMobile MVP: shared storage verbatim over an iCloud container
A second product, not a second edition: dev.rzen.indie.KanbanMobile (iOS 26,
iPhone-only) compiles Kanban/Storage as source files, so a format change that
breaks the phone breaks this build the day it's made. Boards live in
iCloud.dev.rzen.indie.Kanban — named after the Mac bundle id so the Mac app can
adopt the container later without a migration; until then the folder is
"Lanework" in iCloud Drive and the Mac opens boards there through the open
panel.
EchoLedger grows #if os(macOS) gates around its three consumer surfaces
(verdicts/BoardDiff, harvest/HarvestedReceipt, comment retirement/CommentPath)
— the recording side BoardWriter stamps compiles on every platform, and the
gates are the seam a future phone verdict surface lands behind. AgentGuide
stays Mac-only.
The phone's watcher is NSMetadataQuery: BoardIndexStore (one query, package
UTI export makes a .kanban directory one item, equality-gated rescans,
download kicks per pass), BoardSession (materialization sweep before every
fail-fast walk, NSFileCoordinator brackets, perform{} = coordinated write then
awaited reload, ParseMemo threaded), CloudHome (off-main container resolution,
LANEWORK_LOCAL_ROOT DEBUG override for simulator work without an account).
Screens: Boards -> lanes -> cards -> card editor, value-routed by ItemID with
every screen re-reading the live snapshot; leading swipe moves a card via
confirmationDialog, trailing swipe sends it to .trash/; the editor commits
title through the Mac's canonical rename path and body through writeBody, with
drafts that survive reloads; attributes are the three typed style fields
(icon, iconColor, background) — labels is a reserved unknown-field key and
deliberately has no editor. Settings carries IndieBackup (backup root = the
container's Documents, restore rebuild = an index rescan, controller
constructed only once the home resolves).
Arbiter: KanbanMobile green for iOS Simulator, Kanban green for macOS, 3006
unit tests / 517 suites passed (PointerLatencyTests excluded — mid-rework
uncommitted in a parallel session).
Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import SwiftUI
|
||||
import IndieBackup
|
||||
|
||||
/// App version, and backup/restore (indie-backup skill).
|
||||
struct SettingsTabView: View {
|
||||
@Environment(BoardIndexStore.self) private var index
|
||||
@Environment(\.backupController) private var backupController
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section {
|
||||
LabeledContent("Version") {
|
||||
Text(Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "—")
|
||||
}
|
||||
}
|
||||
|
||||
backupSection
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
}
|
||||
// Idempotent (BoardIndexStore.start()), and harmless if BoardsTabView already called it —
|
||||
// this tab can be the first one the user opens, and `backupController` only ever appears
|
||||
// once `index.home` resolves.
|
||||
.task { index.start() }
|
||||
}
|
||||
|
||||
/// `backupController` is `nil` until `MobileApp` has a resolved `CloudHome` to build one from
|
||||
/// — which is also exactly the condition under which there is a `Documents/` folder to back
|
||||
/// up. Everything short of that gets the same quiet explanation rather than a section that
|
||||
/// looks broken or, worse, a crash on a nil root.
|
||||
@ViewBuilder
|
||||
private var backupSection: some View {
|
||||
if let backupController {
|
||||
BackupsSectionView(controller: backupController)
|
||||
} else {
|
||||
Section {
|
||||
switch index.phase {
|
||||
case .idle, .loading:
|
||||
LabeledContent("Backup") {
|
||||
ProgressView()
|
||||
}
|
||||
case .unavailable, .ready:
|
||||
// `.ready` can appear briefly here too: `backupController` lags one runloop
|
||||
// turn behind `index.home` resolving (MobileApp's `.task(id:)`).
|
||||
Label("Backup and restore need iCloud Drive.", systemImage: "icloud.slash")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
} footer: {
|
||||
Text("Sign into iCloud in Settings, then come back here to back up your boards.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user