feature: model-owned resolvedOneDriveFolder + OneDrive Settings/menu panel snapshots

SettingsView's OneDrive row called OneDriveLocator.resolveOneDriveFolder()
directly with the real UserDefaults.standard and the real home directory,
which made that row impossible to drive from a fake/isolated environment.
Moved that resolution into AppModel as a tracked resolvedOneDriveFolder
property (nil means "no OneDrive folder found"), refreshed at init,
bootstrap, chooseTransport, chooseOneDriveFolder, and inside send()'s live
folder check. SettingsView and chooseOneDriveFolder's picker-start path now
read model.resolvedOneDriveFolder instead of calling OneDriveLocator
directly — state flows through the model like everything else in this app.

PanelSnapshot (SHOTDECK_SNAPSHOT_DIR) adds three panels on a SEPARATE
isolated model so the transport switch never bleeds into the six existing
AirDrop-mode panels:
- panel-07-settings-onedrive.png: transport=oneDrive with a resolved folder,
  built by pointing OneDriveLocator.defaultRedlineFolder at a fake home tree
  (Library/CloudStorage/OneDrive-MMDGROUP under this snapshot's own temp
  root) so the displayed path is shaped like the real default without ever
  touching the real home.
- panel-08-settings-onedrive-missing.png: a fake home with no
  Library/CloudStorage at all, resolved through a throwaway UserDefaults
  suite (never .standard) so the "no OneDrive folder found" state and its
  still-usable Choose... button are exercised for real.
- panel-09-captures-present-onedrive.png: 3 captures + transport=oneDrive,
  confirming the menu row reads "Send to OneDrive".

Extracted the 3-swatch capture seeding (panel 04) into addSampleCaptures(to:)
so panel 09 reuses it instead of duplicating the loop.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ZiTXPbPCSjzPVsfoweAbp
This commit is contained in:
2026-09-05 09:10:59 +04:00
co-authored by Claude Fable 5.1
parent 2937d2d5e0
commit 209921f084
4 changed files with 106 additions and 15 deletions
+4 -9
View File
@@ -77,7 +77,7 @@ struct SettingsView: View {
} else {
GridRow(alignment: .center) {
fieldLabel("OneDrive folder")
if let folder = resolvedOneDriveFolder {
if let folder = model.resolvedOneDriveFolder {
folderValue(path: folder.path) {
model.chooseOneDriveFolder()
}
@@ -122,13 +122,6 @@ struct SettingsView: View {
Binding(get: { model.transport }, set: { model.chooseTransport($0) })
}
/// Ground truth from OneDriveLocator, not `model.outboxURL` the model may be
/// showing an AirDrop-folder fallback when no real OneDrive folder resolves, and
/// the Settings row must say so plainly rather than repeat that fallback path.
private var resolvedOneDriveFolder: URL? {
OneDriveLocator.resolveOneDriveFolder()
}
private func armHotkeyRecorder() {
guard !isRecordingHotkey else { return }
isRecordingHotkey = true
@@ -267,6 +260,7 @@ extension AppModel: SettingsWindowPresenting {
)
}
setFolderURLs(outbox: folders.outbox, watch: folders.watch)
setResolvedOneDriveFolder(OneDriveLocator.resolveOneDriveFolder())
Task {
await watcher.setRecordUncommented(value == .airDrop)
do {
@@ -280,10 +274,11 @@ extension AppModel: SettingsWindowPresenting {
}
func chooseOneDriveFolder() {
let start = OneDriveLocator.resolveOneDriveFolder() ?? FileManager.default.homeDirectoryForCurrentUser
let start = resolvedOneDriveFolder ?? FileManager.default.homeDirectoryForCurrentUser
guard let url = chooseDirectory(startingAt: start) else { return }
TransportSettings.setOneDriveFolder(url)
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
setResolvedOneDriveFolder(OneDriveLocator.resolveOneDriveFolder())
guard transport == .oneDrive else { return }
setFolderURLs(outbox: url, watch: url)
Task {