Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 21 additions & 29 deletions Microfiche/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ struct ContentView: View {
static let minimumWidth: CGFloat = 240
static let idealWidth: CGFloat = 280
static let maximumWidth: CGFloat = 360
static let autoCollapseWidth: CGFloat = 220
}

@Environment(\.accessibilityReduceMotion) private var reduceMotion
@Environment(\.toggleSidebar) private var toggleSidebar

@State private var selection: Selection?
@State private var imageFiles: [ImageFile] = []
@State private var libraryLoadGeneration = UUID()
Expand All @@ -88,7 +90,7 @@ struct ContentView: View {
@State private var scrollToID: UUID?
@State private var gridColumnCount: Int = 1
@State private var detailViewFile: ImageFile?
@State private var isMetadataInspectorPresented = true
@AppStorage("isMetadataInspectorPresented") private var isMetadataInspectorPresented = true
@State private var splitViewVisibility: NavigationSplitViewVisibility = .all
@State private var externalDriveNotice: String?
@AppStorage("lastSelectedLibraryFolderID") private var lastSelectedLibraryFolderID = ""
Expand All @@ -110,7 +112,6 @@ struct ContentView: View {
externalVolumes: libraryStorage.rememberedExternalVolumes,
contactSheets: contactSheetStorage.contactSheets,
selection: selection,
onWidthChange: handleSidebarWidthChange,
onLinkFolder: linkFolder,
onSelect: { newSelection in
selection = newSelection
Expand Down Expand Up @@ -140,7 +141,7 @@ struct ContentView: View {
max: SidebarLayout.maximumWidth
)
} detail: {
ZStack {
NavigationStack {
MainContentView(
imageFiles: imageFiles,
unavailableLocation: unavailableSelectedFolder,
Expand All @@ -157,20 +158,14 @@ struct ContentView: View {
contactSheets: contactSheetStorage.contactSheets,
onAddToContactSheet: handleAddToContactSheet
)
.opacity(detailViewFile == nil ? 1 : 0)
.allowsHitTesting(detailViewFile == nil)
.accessibilityHidden(detailViewFile != nil)

if let detailFile = detailViewFile {
.navigationDestination(item: $detailViewFile) { file in
ImageDetailView(
file: detailFile,
file: file,
isInspectorPresented: $isMetadataInspectorPresented,
onBack: closeImageDetail
)
.transition(.opacity)
}
}
.animation(MicroficheMotion.transition, value: detailViewFile?.id)
.inspector(isPresented: $isMetadataInspectorPresented) {
Group {
if let focusedImageFile {
Expand All @@ -185,13 +180,22 @@ struct ContentView: View {
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.microficheSidebarChrome()
.inspectorColumnWidth(min: 280, ideal: 320, max: 420)
}
}
.navigationTitle("")
.toolbar {
if detailViewFile == nil {
ToolbarItem {
Button(action: toggleSidebar) {
Image(systemName: "sidebar.left")
}
.help("Toggle Sidebar")
.accessibilityLabel("Toggle sidebar")
.accessibilityIdentifier("sidebar.toggle")
}
.hideSharedBackgroundIfAvailable()

ToolbarItem(placement: .principal) {
if viewMode == .grid {
HStack(spacing: 6) {
Expand Down Expand Up @@ -229,6 +233,8 @@ struct ContentView: View {
Image(systemName: "sidebar.right")
}
.help(isMetadataInspectorPresented ? "Hide Info" : "Show Info")
.accessibilityLabel(isMetadataInspectorPresented ? "Hide inspector" : "Show inspector")
.accessibilityIdentifier("inspector.toggle")
}
.hideSharedBackgroundIfAvailable()
}
Expand Down Expand Up @@ -455,16 +461,6 @@ struct ContentView: View {
}
}

private func handleSidebarWidthChange(_ width: CGFloat) {
guard splitViewVisibility != .detailOnly else { return }

if width < SidebarLayout.autoCollapseWidth {
withAnimation(MicroficheMotion.transition) {
splitViewVisibility = .detailOnly
}
}
}

// MARK: - Contact Sheets

private func handleDropToContactSheet(sheetID: UUID, urls: [URL]) {
Expand Down Expand Up @@ -554,16 +550,12 @@ struct ContentView: View {
isQuickPreviewPresented = false
selectedImageFileIDs = [fileID]
focusedImageFileID = fileID
withAnimation(MicroficheMotion.transition) {
detailViewFile = file
// Keep the metadata inspector available for editing Finder labels/tags.
isMetadataInspectorPresented = true
}
detailViewFile = file
}
}

private func closeImageDetail() {
withAnimation(MicroficheMotion.transition) {
withAnimation(MicroficheMotion.transition(reducedMotion: reduceMotion)) {
detailViewFile = nil
}
requestScrollToFocusedImage()
Expand Down
3 changes: 1 addition & 2 deletions Microfiche/Views/SidebarView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ struct SidebarView: View {
let externalVolumes: [RememberedExternalVolume]
let contactSheets: [ContactSheet]
let selection: Selection?
let onWidthChange: (CGFloat) -> Void
let onLinkFolder: () -> Void
let onSelect: (Selection) -> Void
let onRemoveFolder: (UUID) -> Void
Expand Down Expand Up @@ -115,7 +114,7 @@ struct SidebarView: View {
}
.scrollIndicators(.hidden)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.background(WidthReader(onChange: onWidthChange))
.accessibilityIdentifier("library.sidebar")
}

private var folderSectionDetail: String {
Expand Down
22 changes: 0 additions & 22 deletions Microfiche/Views/WidthReader.swift

This file was deleted.

6 changes: 3 additions & 3 deletions design.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ Rules derived from `ContentView`, `SidebarView`, and `ImageDetailView`.
1. **Two-column shell + inspector** — Use `NavigationSplitView` (sidebar + detail). Metadata lives in a detail-attached `.inspector`, not a third permanent split column.
2. **Sidebar owns library location** — Selection is a single enum: All Images, Folder, or Contact Sheet. External drive rows are status only — not navigation targets.
3. **Location change clears browsing state** — On library selection change, clear image selection, focus, quick preview, and detail view.
4. **Detail is a canvas overlay** — Double-click opens `ImageDetailView` over the library detail column (opacity / hit-testing), not a pushed navigation destination or new column.
4. **Detail pushes on the detail stack** — Double-click opens `ImageDetailView` via `NavigationStack` + `navigationDestination`, not an opacity overlay or new split column.
5. **Inspector stays with detail** — Entering detail keeps the metadata inspector available so Finder labels, tags, and comments can be edited while viewing.
6. **Sidebar is collapsible** — Width 240–360 (ideal 280). Auto-collapse to `.detailOnly` below 220 pt measured width.
6. **Sidebar is collapsible** — Width 240–360 (ideal 280). Use the system split-view divider and sidebar toggle; do not programmatically override `columnVisibility` during resize.
7. **Unified chrome** — Window uses unified toolbar with no title. Keep `.navigationTitle("")` unless a mode truly needs a title.
8. **Window scale** — Minimum about 1100×700; default launch size stays large enough for sidebar + grid + inspector.

Expand Down Expand Up @@ -190,7 +190,7 @@ Disable animations while the grid size slider is dragging. Prefer `MicroficheMot

### Sidebar
- **File:** `Microfiche/Views/SidebarView.swift`
- Custom scroll sections; width reported via `WidthReader`
- Custom scroll sections; collapsible via the system split-view divider and sidebar toggle

### Library canvas
- **File:** `Microfiche/Views/MainContentView.swift`
Expand Down