The phone learns to say who it is — IndieAbout joins the Settings tab with a changelog of its own
The About section lands last in the mobile Settings form, mirroring the Mac AboutBox voice: license link, copyright, and a version label that doubles as the changelog link. The changelog is the phone app's own — a separate product with its own 1.0, so the Mac CHANGELOG.md would narrate someone else's releases. The Update Build Info stamping phase rides along so the version line can carry a build number and date. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
**August 2026**
|
||||
|
||||
Version 1.0: Lanework comes to iPhone — browse your boards from iCloud Drive, move cards between lanes, and edit them on the go.
|
||||
|
||||
Back up your boards from Settings and restore any backup later, even on a new phone.
|
||||
@@ -1,7 +1,8 @@
|
||||
import SwiftUI
|
||||
import IndieAbout
|
||||
import IndieBackup
|
||||
|
||||
/// App version, and backup/restore (indie-backup skill).
|
||||
/// Backup/restore (indie-backup skill) and the About section (indie-about skill).
|
||||
struct SettingsTabView: View {
|
||||
@Environment(BoardIndexStore.self) private var index
|
||||
@Environment(\.backupController) private var backupController
|
||||
@@ -9,13 +10,19 @@ struct SettingsTabView: View {
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section {
|
||||
LabeledContent("Version") {
|
||||
Text(Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "—")
|
||||
}
|
||||
}
|
||||
|
||||
backupSection
|
||||
|
||||
// Last section by convention. The version label doubles as the changelog link,
|
||||
// which is why the changelog sits in its dedicated slot and not in `documents`.
|
||||
// Same voice as the Mac app's `AboutBox.configuration`; the changelog itself is
|
||||
// the phone app's own (see project.yml — separate product, separate 1.0).
|
||||
Section {
|
||||
IndieAbout(configuration: .init(
|
||||
copyrightText: "© 2026 rzen",
|
||||
documents: [.license(extension: "md")],
|
||||
changelogDocument: .changelog()
|
||||
))
|
||||
}
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ Lanework is in early development. This list tracks what has actually shipped and
|
||||
- **Branches and commit identity** — the board popover's git section is where a git board's branches live: the current branch is itself the picker, and beside the local branches it offers create-and-switch, which starts the new branch at the commit you are on. **Switching is never silent.** If any card window is holding unsaved keystrokes, an open Edit session whose crash-safe saves no commit has yet, or an open raw-source buffer, the switch stops and asks — **Save All**, **Discard**, or **Cancel** — for *every* open window, not just the ones the checkout would touch, because an unapplied raw buffer would otherwise write the whole pre-switch file onto the new branch's card later on. Save All ends each session with its normal commit and applies each raw buffer; a buffer that won't validate cancels the whole switch and puts you in front of the window that refused, nothing half-switched. Discard puts both the buffers and their uncommitted on-disk saves back to the last commit. With the sessions settled the pending auto-commit flushes onto the branch you are **leaving**, so the checkout runs on a genuinely settled tree and cannot fail dirty — and the checkout itself is git's *safe* one, never a force: work the app somehow didn't settle refuses the switch rather than being overwritten. The switch is bracketed like every wholesale operation — the watcher suspended, one full reload at the end, an in-progress row saying "Switching to 'main'…" — and if that final reload fails, the board locks read-only until a reload succeeds rather than letting you edit a snapshot of the branch you just left. Undo and redo do not survive the switch: the stack is discarded and reseeded from the new branch's own history, with redo empty, because replaying a restore from the old branch onto the new one would be wrong. When another program holds the repository's index, the switch waits and retries quietly, the row changing to say it is waiting for another writer's git lock and eventually naming the lock file — never a dialog, and the lock is never removed, because it isn't the app's. **An interrupted switch cleans up after itself**: before touching the repository the app writes down what it is about to do — in its own state, never in your board and never inside `.git` — so a crash or an unplugged volume mid-switch is recognized at the next open, rolled back to the state you were in, and announced ("A branch switch was interrupted — the previous state is restored"); an unfinished merge or rebase that *isn't* the app's is still left strictly alone. Beneath the branches sit the **commit-identity** fields: what you type there is written to the repository's own `.git/config`, so the setting is the file — portable to any git client, per board, and what the next commit is signed with. Leave a field empty and it shows the derived default (your account name, and `you@yourmac`) as a placeholder rather than filling it in, because a value the app wrote there would quietly outrank your own global git config for that board. A config edited from outside while the popover is open refreshes the fields you aren't typing in and leaves the one you are alone.
|
||||
- **A card's History** — the card window's sidebar gains a read-only **History** section on git boards: every commit that touched that card, newest first, each row its own semantic subject over a relative date and the author who made it — so an agent's work and your own read as one story ("Move card 'Fix login' to Doing · 2 days ago · Claude"). It follows the card by identity rather than by path, so moving between lanes — or into the trash and back — keeps one continuous trail. Read-only in this version: restoring a single old version stays a git-client job. The section is simply absent on boards without app-managed git — no placeholder, no greyed-out promise.
|
||||
|
||||
- **Lanework for iPhone (in development)** — a companion iOS app, second product rather than second edition (`dev.rzen.indie.KanbanMobile`), whose boards are the same `.kanban` folders in an iCloud Drive container (`iCloud.dev.rzen.indie.Kanban` — a folder the Mac's Finder shows as "Lanework", so the Mac app opens the same boards through the ordinary open panel today and can adopt the container natively later with no migration). It compiles the Mac app's storage engine *verbatim* — same loader, same writer, same byte-fidelity contract, so the two apps cannot drift on the format — and drives it from an `NSMetadataQuery` observer instead of FSEvents. The MVP surface: a Boards tab (boards → lanes → cards → card editor, with per-item download/materialization states honestly rendered), swipe actions to move a card between lanes or send it to `.trash/`, a card editor for title, body, and the typed style attributes (icon, icon color, background), and a Settings tab with local backup/restore of the whole container (IndieBackup). No git on the phone — the change signal is the container itself.
|
||||
- **Lanework for iPhone (in development)** — a companion iOS app, second product rather than second edition (`dev.rzen.indie.KanbanMobile`), whose boards are the same `.kanban` folders in an iCloud Drive container (`iCloud.dev.rzen.indie.Kanban` — a folder the Mac's Finder shows as "Lanework", so the Mac app opens the same boards through the ordinary open panel today and can adopt the container natively later with no migration). It compiles the Mac app's storage engine *verbatim* — same loader, same writer, same byte-fidelity contract, so the two apps cannot drift on the format — and drives it from an `NSMetadataQuery` observer instead of FSEvents. The MVP surface: a Boards tab (boards → lanes → cards → card editor, with per-item download/materialization states honestly rendered), swipe actions to move a card between lanes or send it to `.trash/`, a card editor for title, body, and the typed style attributes (icon, icon color, background), and a Settings tab with local backup/restore of the whole container (IndieBackup) plus an About section with the app's own changelog and license (IndieAbout). No git on the phone — the change signal is the container itself.
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
+25
-1
@@ -175,7 +175,19 @@ targets:
|
||||
type: application
|
||||
platform: iOS
|
||||
sources:
|
||||
- KanbanMobile
|
||||
- path: KanbanMobile
|
||||
excludes:
|
||||
- CHANGELOG.md
|
||||
# The Settings tab's About section (indie-about skill): the shared ISC license verbatim from
|
||||
# the repo root, and a changelog of the phone app's own — it is a separate product with its
|
||||
# own 1.0, so the Mac app's CHANGELOG.md would narrate someone else's releases. Both ride in
|
||||
# Apple's inline-Markdown subset (bold headings, one physical line per paragraph).
|
||||
- path: KanbanMobile/CHANGELOG.md
|
||||
type: file
|
||||
buildPhase: resources
|
||||
- path: LICENSE.md
|
||||
type: file
|
||||
buildPhase: resources
|
||||
# The shared storage engine — frontmatter, loader, writer, ranks, integrity. Pure
|
||||
# Foundation + Yams + swift-markdown; no AppKit anywhere in the directory, which is what
|
||||
# makes this line possible. One exclusion: AgentGuide reaches up into LiveStore's
|
||||
@@ -199,6 +211,18 @@ targets:
|
||||
- package: swift-markdown
|
||||
product: Markdown
|
||||
- package: IndieBackup
|
||||
# The Settings tab's About section — same package, same pin as the Mac target above.
|
||||
- package: IndieAbout
|
||||
# Same stamping as the Mac target: CFBundleVersion from git commit count, BuildDate/BuildHash
|
||||
# into the product's Info.plist, so the About section can show "Built YYYY-MM-DD".
|
||||
postBuildScripts:
|
||||
- script: '"${SRCROOT}/../indie-skills/skills/app-versioning/scripts/update_build_info.sh"'
|
||||
name: Update Build Info
|
||||
shell: /bin/sh
|
||||
basedOnDependencyAnalysis: false
|
||||
inputFiles:
|
||||
- $(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)
|
||||
- $(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Info.plist
|
||||
settings:
|
||||
base:
|
||||
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.KanbanMobile
|
||||
|
||||
Reference in New Issue
Block a user