From 8e5b407fbce822b02c9b22c3f1134599b04c4e11 Mon Sep 17 00:00:00 2001 From: Vigneswaran Rajkumar <118706051+IAmVigneswaran@users.noreply.github.com> Date: Wed, 9 Sep 2026 08:54:41 +0800 Subject: [PATCH] Fix launch without Final Cut Pro and File menu Close order Resolve FCPXML UTTypes when Final Cut Pro is not installed so Extract layout no longer traps. Custom File items replace New so Close stays last, and the Databases delete confirmation uses the app icon. Agent docs match. --- .cursorrules | 8 +- AGENT.md | 25 +++- ARCHITECTURE.md | 119 +++++++++++++++--- CHANGELOG.md | 11 ++ GUARDRAILS.md | 10 +- .../Marker Data/Marker_DataApp.swift | 3 - .../Extensions/UTTypeExtension.swift | 20 ++- .../Database/DatabaseSettingsView.swift | 1 + .../Menu Bar Commands/FileCommands.swift | 12 +- Source/Marker Data/Marker-Data-Info.plist | 42 +++++++ 10 files changed, 220 insertions(+), 31 deletions(-) diff --git a/.cursorrules b/.cursorrules index 4a348525..66f74523 100644 --- a/.cursorrules +++ b/.cursorrules @@ -11,7 +11,7 @@ Keep these four files consistent when architecture or agent guidance changes. ## Project layout - Main app: `Source/Marker Data/Marker Data/` -- Workflow Extension: `Source/Marker Data/Workflow Extension/` (`WorkflowExtensionView.swift` — Extract + Roles tabs) +- Workflow Extension: `Source/Marker Data/Workflow Extension/` (`WorkflowExtensionView.swift` — Extract + Roles tabs; two Compile Sources rows including unused main-app membership — do not delete). `WorkflowExtensionViewController.swift` is appex-only. - Uninstaller: `Source/Marker Data/Marker Data Uninstaller/` (target **Uninstall Marker Data**; product `Uninstall Marker Data.app`; bundle ID `co.theacharya.MarkerData.Uninstaller`) - FCP Share Destination (Swift + Obj‑C): `Source/Marker Data/Marker Data/FCP Share Destination/` - Bundled binaries (do not modify internals): `Source/Marker Data/Marker Data/Resources/airlift`, `csv2notion_neo` @@ -46,6 +46,8 @@ Keep these four files consistent when architecture or agent guidance changes. - **Queue:** scans for `extract_info.json` (Notion/Airtable + JSON manifest only) → upload via `QueueInstance.manifestURL` (local JSON beside folder preferred) → parallel re-upload; folder drop clears queue and disables auto-scan - **Workflow Extension:** Extract tab drops `.fcpxml` → Movies-cache handoff (does **not** mkdir the cache folder) → open `/Applications/Marker Data.app` → DistributedNotification `.workflowExtensionFileReceived`; both Extract and Roles show `DropTargetOverlay`. Shared sources have **two** `pbxproj` Compile Sources rows (one file, two targets) — never “dedupe”. Membership list: **ARCHITECTURE.md → Workflow Extension target**. Header uses `MarkerDataAppIcon.image()` at 100×100 from the **containing** `Marker Data.app` — not the appex `AppIcon.appiconset`. TabView keeps `.padding(.bottom, 40)` so the drop zone stays above the overlaid help “?”. - **Share Destination:** `OSAScriptingDefinition.sdef` + Obj‑C `MakeCommand` (cache `/` then location `…//`) → local `FCPShareStart` → Apple Event open → `.openFile`. `OpenEventHandler.setupHandler()` on init, `.FCPShareStart`, and `Marker_DataApp` `.task` (main queue). +- **FCPXML UTTypes:** public `UTType.fcpxml` / `.fcpxmld` (`UTTypeExtension`; private `finalCutProType` = `UTType(identifier) ?? UTType(importedAs:identifier, conformingTo:)`). First Extract layout evaluates these types — never `!`. Keep `UTImportedTypeDeclarations` in `Marker-Data-Info.plist`. Do not split the Share Destination **Asset Description File** document type. +- **File menu:** `FileCommands` replaces `.newItem` (Open Pagemaker ⌘P, Install Share Destination, Show/Clean Cache ⌘K). Do not empty `.newItem` in `Marker_DataApp` and do not add a second Close. Extract **Choose File** stays on `ExtractView` (`FilePicker`, ⌘O). ## Pitfalls to avoid - Do not remove or rename persisted settings keys without a migration. @@ -61,10 +63,14 @@ Keep these four files consistent when architecture or agent guidance changes. - FCP timeline drops onto the Dock icon are often pasteboard-only; Extract panel drop is the supported timeline path. - Do not use `Color.accentColor` / `.accent` for shared chrome hosted in Final Cut Pro — use `Color.markerAccent`. - Do not delete a second `DialogIcon.swift in Sources` (or other shared files) from `project.pbxproj` — that is two-target membership, not a duplicate file. +- Do not force-unwrap `UTType("com.apple.finalcutpro.xml")` / `.xmld` — resolve with `UTType(identifier) ?? UTType(importedAs:identifier, conformingTo:)` and keep `UTImportedTypeDeclarations` in `Marker-Data-Info.plist`. First Extract layout evaluates these types; FCP may be absent (App Preview). Do not split the Share Destination **Asset Description File** document type. +- File menu custom items replace `.newItem` so system Close stays last — do not empty `.newItem` and do not add a second Close. ## Definition of done for code changes - App builds **unsigned** Debug and Release for arm64 (`CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO`; Workflow Extension still embeds). - Settings load and migrate; extraction works for `.fcpxml`/`.fcpxmld` (incl. FCP pasteboard / textClipping via `FCPXMLIntake`). +- FCPXML `UTType`s still use `importedAs` fallback (no `!`); Info.plist still has `UTImportedTypeDeclarations`; Share Destination **Asset Description File** type is unchanged. +- File menu still replaces `.newItem` with app actions; system Close stays last (no second Close; do not empty `.newItem`). - Drop overlays still work on main-app Extract / Roles / Queue **and** Workflow Extension Extract + Roles; queue still finds/uploads `extract_info.json` folders via `manifestURL`. - Skipped / no-media swatch finishes as “Extract done” (not “Analysing swatch done”). - New alerts **and** confirmation dialogs use `.appDialogIcon()`; Workflow Extension header still shows the main-app Icon Composer icon (not the appex `AppIcon.appiconset`); settings changes include version + migration + wiring as required above. diff --git a/AGENT.md b/AGENT.md index 9a00b68a..76d762d1 100644 --- a/AGENT.md +++ b/AGENT.md @@ -19,6 +19,7 @@ - [New export fields / overlays](#new-export-fields--overlays) - [New database platform](#new-database-platform) - [FCP integrations](#fcp-integrations) + - [File menu](#file-menu) - [Drop overlay / shared Workflow Extension UI](#drop-overlay--shared-workflow-extension-ui) - [App icon / dialogs](#app-icon--dialogs) - [Uninstaller cleanup paths](#uninstaller-cleanup-paths) @@ -58,7 +59,7 @@ For deeper module/data-flow detail, see **`ARCHITECTURE.md`**. For hard always/n | Concern | File | |---------|------| -| `@main` app | `Source/Marker Data/Marker Data/Marker_DataApp.swift` — constructs `SettingsContainer`, `DatabaseManager`, `ExtractionModel`, `QueueModel`; menu commands; Failed Tasks + Pagemaker windows | +| `@main` app | `Source/Marker Data/Marker Data/Marker_DataApp.swift` — constructs `SettingsContainer`, `DatabaseManager`, `ExtractionModel`, `QueueModel`; menu commands (`FileCommands` replaces `.newItem`); Failed Tasks + Pagemaker windows | | AppKit / Sparkle delegate | `Source/Marker Data/Marker Data/ApplicationDelegate.swift` | | Sidebar navigation | `Source/Marker Data/Marker Data/Views/Main/ContentView.swift` (`MainViews` enum) | | Extract UI | `Source/Marker Data/Marker Data/Views/Main/ExtractView.swift` | @@ -66,8 +67,11 @@ For deeper module/data-flow detail, see **`ARCHITECTURE.md`**. For hard always/n | External handoffs (open / Workflow Extension) | `.../ExtractionModel_EventHandlers.swift` | | FCPXML intake (files / pasteboard / textClipping) | `Utilities/Other/FCPXMLIntake.swift`, `TextClippingReader.swift` | | Extract drop modifier | `Views/Components/FCPXMLDropModifier.swift` (`.fcpxmlDropDestination`) | +| FCPXML UTTypes | `Utilities/Extensions/UTTypeExtension.swift` — public `UTType.fcpxml` / `.fcpxmld` via private `finalCutProType` (`UTType(identifier) ?? UTType(importedAs:identifier, conformingTo:)` — never `!`); `Source/Marker Data/Marker-Data-Info.plist` `UTImportedTypeDeclarations` + Asset Description File `LSItemContentTypes` | +| File menu | `Views/Menu Bar Commands/FileCommands.swift` — replaces `.newItem` (Open Pagemaker ⌘P, Install Share Destination, Show/Clean Cache ⌘K); system Close stays last | +| Info.plist | `Source/Marker Data/Marker-Data-Info.plist` — Sparkle, Share Destination document types (**Asset Description File** name is a contract), imported FCPXML UTIs | | Drop overlay (Extract / Roles / Queue + WE) | `Views/Components/DropTargetOverlay.swift` (also Workflow Extension Compile Sources) | -| Workflow Extension UI | `Source/Marker Data/Workflow Extension/WorkflowExtensionView.swift` | +| Workflow Extension UI | `Source/Marker Data/Workflow Extension/WorkflowExtensionView.swift` — Extract + Roles tabs; **two** Compile Sources rows (main app + appex — do not delete the main-app membership). `WorkflowExtensionViewController.swift` is appex-only. | | Color helpers (`markerAccent`, `heroGradient`) | `Utilities/Extensions/ColorExtension.swift` | | Queue UI | `Views/Detail Views/QueueView.swift` | | Queue scan/upload | `Models/Queue/QueueModel.swift` | @@ -225,14 +229,21 @@ Also: getter for `colorSwatchSettings` **forces `enableSwatch = false` when extr - Note: existing property spelling is `plaform` on `DatabaseProfileModel` — keep consistent when extending. ### FCP integrations -- Share Destination: `Resources/OSAScriptingDefinition.sdef` + Obj‑C under `FCP Share Destination/Objective-C Code/` + Swift `OpenEventHandler`. -- Workflow Extension: DistributedNotificationCenter names + fixed Movies-cache FCPXML path; roles via `preferences.json`. +- Share Destination: `Resources/OSAScriptingDefinition.sdef` + Obj‑C under `FCP Share Destination/Objective-C Code/` + Swift `OpenEventHandler`. Keep the **Asset Description File** document type name (`DocumentController` matches it). +- Workflow Extension: DistributedNotificationCenter names + fixed Movies-cache FCPXML path; roles via `preferences.json`. Appex `Info.plist` `ProExtensionAttributes` are 700×550 (FCP host min); SwiftUI frame is 600×400. `WorkflowExtensionView.swift` has two Compile Sources rows. - Extract panel intake: `FCPXMLIntake` / `FCPXMLDropModifier` — keep Dock Open With (`OpenEventHandler`) and Share Destination paths distinct. +- FCPXML UTTypes: public `UTType.fcpxml` / `.fcpxmld` (private `finalCutProType`, never `!`) + `Marker-Data-Info.plist` `UTImportedTypeDeclarations` / Asset Description File `LSItemContentTypes`. + +### File menu +- Custom File items live in `FileCommands` as `CommandGroup(replacing: .newItem)`. +- Do **not** also empty `.newItem` in `Marker_DataApp` (that puts system Close first). Emptying `.toolbar` in `Marker_DataApp` is unrelated (removes the default View-menu toolbar group). +- Do **not** add a Close item — system Close / Close All remain last. +- Current items (in order): **Open Pagemaker** (⌘P) → **Install FCP Share Destination…** → **Show Cache** / **Clean Cache** (⌘K). Extract **Choose File** still uses ⌘O on the Extract panel (`FilePicker`), not the File menu. Extract completion also shows **Open Pagemaker** when the profile is extract-only Notion or Airtable. ### Drop overlay / shared Workflow Extension UI - Update copy in **`GUARDRAILS.md`** (Drop overlay copy table) when changing messages. - Workflow Extension **Extract** (`WorkflowExtensionView`) and **Roles** (shared `RolesSettingsView`) both show overlays. -- Shared types must stay in **both** targets’ Compile Sources (two `PBXBuildFile` rows, one `PBXFileReference`). Minimum shared UI: `DropTargetOverlay`, `ColorExtension`, `DialogIcon.swift`, `HelpButton`, `OverlayHelpButton`. Roles/settings files listed in **ARCHITECTURE.md**. Shared chrome uses `Color.markerAccent`. +- Shared types must stay in **both** targets’ Compile Sources (two `PBXBuildFile` rows, one `PBXFileReference`). Minimum shared UI: `DropTargetOverlay`, `ColorExtension`, `DialogIcon.swift`, `HelpButton`, `OverlayHelpButton`. `WorkflowExtensionView.swift` is also dual-membership (appex UI; do not delete the main-app Compile Sources row). `WorkflowExtensionViewController.swift` is appex-only. Roles/settings files listed in **ARCHITECTURE.md**. Shared chrome uses `Color.markerAccent`. ### App icon / dialogs - Icon Composer **`Marker-Data.icon`** (`ASSETCATALOG_COMPILER_APPICON_NAME` = Marker-Data) lives beside the catalog — **not** inside `Assets.xcassets`. Main-app `AppIcon.appiconset` is an empty placeholder. Never flatten the layer PNG into `AppIconSingle` (imageset removed). @@ -304,11 +315,15 @@ Exact overlay strings and always/never rules: **`GUARDRAILS.md`**. - **Shared `pbxproj` rows:** do not remove a second `DialogIcon.swift in Sources` (or other shared files) — the file is compiled into two targets. - **Persisted typos:** do not rename JSON key `plaform` without a migration. - **WE cache folder:** the extension does not `mkdir` Movies cache; a first-time drop can fail if the main app has never created `~/Movies/Marker Data Cache/`. +- **FCPXML `UTType` force-unwrap:** `UTType("com.apple.finalcutpro.xml")!` traps on first Extract layout when Final Cut Pro is not installed (App Preview / machines without FCP). Resolve with `UTType(identifier) ?? UTType(importedAs:identifier, conformingTo:)` and keep `UTImportedTypeDeclarations` in `Marker-Data-Info.plist`. Do not split the Share Destination **Asset Description File** document type. +- **File menu Close last:** custom File items replace `.newItem` (`FileCommands`). Do not empty `.newItem` (that puts system Close first) and do not add a second Close. ## Definition of done for most changes - App builds **unsigned** Debug **and** Release for **arm64** (`CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO`; Workflow Extension still embeds). - Settings still load and migrate cleanly (`preferences.json` + `Configurations/*.json`). - Extraction works for `.fcpxml` and `.fcpxmld`, including FCP pasteboard / textClipping intake via `FCPXMLIntake`. +- FCPXML `UTType`s still use `importedAs` fallback (no `!`); Info.plist still has `UTImportedTypeDeclarations`; Share Destination **Asset Description File** type is unchanged. +- File menu still replaces `.newItem` with app actions; system Close stays last (no second Close; do not empty `.newItem`). - Drop overlays still work on main-app Extract / Roles / Queue **and** Workflow Extension Extract + Roles. - Queue scan/upload still works for folders containing `extract_info.json`, including **moved/copied** folders (`manifestURL`). - Skipped / no-media swatch finishes as **“Extract done”** (not **“Analysing swatch done”**). diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 15aa54c4..1b6b48c1 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -30,6 +30,8 @@ - [Color swatch pipeline](#color-swatch-pipeline) - [Database profiles](#database-profiles) - [UI architecture](#ui-architecture) +- [File menu](#file-menu) +- [FCPXML UTTypes without Final Cut Pro](#fcpxml-uttypes-without-final-cut-pro) - [App icon and dialogs](#app-icon-and-dialogs) - [Notifications (full list)](#notifications-full-list) - [Entitlements / security model](#entitlements--security-model) @@ -53,7 +55,7 @@ It also ships two Final Cut Pro integrations: - **Share Destination** — Media Asset Protocol / AppleScript so FCP exports media + FCPXML and opens them into Marker Data - **Workflow Extension** — ProExtensions-hosted UI: Extract tab handoff (overlay + Movies-cache FCPXML → open app) and Roles management (shared prefs + overlay) -**Runtime requirements (product):** Apple silicon; macOS Sequoia **15.7+** (from 2.0.0; see README / CHANGELOG). Xcode `MACOSX_DEPLOYMENT_TARGET` is **15.0** — the 15.7 floor is a support policy, not the build setting. Final Cut Pro 12+ recommended. The app is expected to run from **`/Applications/Marker Data.app`**. +**Runtime requirements (product):** Apple silicon; macOS Sequoia **15.7+** (from 2.0.0; see README / CHANGELOG). Xcode `MACOSX_DEPLOYMENT_TARGET` is **15.0** — the 15.7 floor is a support policy, not the build setting. Final Cut Pro 12+ recommended for extraction workflows, but the **app must launch without FCP installed** (App Preview / review Macs) — FCPXML `UTType`s use `importedAs` fallback. The app is expected to run from **`/Applications/Marker Data.app`**. For agent-oriented change checklists, see **`AGENT.md`**. For hard always/never constraints and learned pitfalls, see **`GUARDRAILS.md`**. For short Cursor enforcement, see **`.cursorrules`**. @@ -72,7 +74,8 @@ Primary responsibilities: - Persistence of configurations, DB profiles, logs - Queue scanning/uploading previously extracted Notion/Airtable jobs (local-first `manifestURL`) - Installing FCP Share Destination templates -- Hosting Pagemaker (WebView) for PDF generation +- Hosting Pagemaker (WebView) for PDF generation (File menu **Open Pagemaker**, plus Extract footer after Notion/Airtable extract-only) +- FCPXML UTTypes that resolve when Final Cut Pro is absent (`UTTypeExtension` + Info.plist imported types) - Embedding the Workflow Extension as `Contents/PlugIns/Workflow Extension.appex` ### Workflow Extension target @@ -85,6 +88,7 @@ Primary responsibilities: Responsibilities: - SwiftUI UI inside `WorkflowExtensionViewController` (`NSHostingView`) - Extract tab: drag/drop `.fcpxml` + `DropTargetOverlay` (“Drop to Open Marker Data”) → write Movies-cache handoff file → open main app → DistributedNotification +- SwiftUI frame is **600×400**; FCP host minimum comes from the **appex** `Info.plist` `ProExtensionAttributes` (**700×550**). Do not edit `ProExtensionAttributes` on `Marker-Data-Info.plist` (main app; leftover 400×600) expecting to size the extension. - Header: `MarkerDataAppIcon.image()` at 100×100 from the containing `Marker Data.app` (`DialogIcon.swift`); bundle/plugin icon remains `Assets.xcassets/AppIcon.appiconset` - Roles tab sharing `RolesSettingsView` / `RolesManager` / `DropTargetOverlay` / `ColorExtension` with the main app (same prefs file) - Extract/Roles `TabView` uses `.padding(.bottom, 40)` so the drop zone stays above the help “?” @@ -94,7 +98,8 @@ Responsibilities: | Kind | Files | |------|--------| -| Extension-only | `WorkflowExtensionView.swift`, `WorkflowExtensionViewController.swift` | +| Extension-only | `WorkflowExtensionViewController.swift` | +| Dual membership (appex UI; also in main-app Compile Sources — do not delete either row) | `WorkflowExtensionView.swift` | | Shared UI | `DropTargetOverlay.swift`, `ColorExtension.swift`, `DialogIcon.swift`, `HelpButton.swift`, `OverlayHelpButton.swift`, `RolesSettingsView.swift` | | Shared roles / settings (no `SettingsContainer` in the appex) | `RolesManager.swift`, `RolesManager+DropDelegate.swift`, `RoleModel.swift`, `SettingsStore.swift`, `SettingsModels.swift`, `ColorSwatchSettingsModel.swift`, `UnifiedExportProfile.swift`, `NotificationFrequency.swift`, `ExtractError.swift` | | Shared helpers | `URLExtension.swift`, `UTTypeExtension.swift`, `RoleExtension.swift`, `NotificationNameExtension.swift`, `MarkersExtractorModelExtensions.swift`, `ExportProfileFormatExtrension.swift`, `DeltaEFormulaExtension.swift` | @@ -317,18 +322,19 @@ Changing the JSON shape of `roles` / `RoleModel` (or any `SettingsStore` key) mu **UI:** `DropTargetOverlay` while targeted and not extracting; hero caption under title describes FCP timeline / file drop. Overlay copy: see **`GUARDRAILS.md`**. -Supported types: `UTType.fcpxml`, `UTType.fcpxmld` (`ExtractionModel.supportedContentTypes`). +Supported types: `UTType.fcpxml`, `UTType.fcpxmld` (`ExtractionModel.supportedContentTypes`) — defined in `UTTypeExtension` so first Extract layout does not trap if Final Cut Pro is not installed. ```mermaid flowchart LR Drop[Drop / Open / Pasteboard] + UT["UTType.fcpxml / .fcpxmld"] Intake[FCPXMLIntake] EM[ExtractionModel] ME[MarkersExtractor] Swatch["ColorPaletteRenderer.render -> Bool"] Up[DatabaseUploader] - Drop --> Intake --> EM --> ME + Drop --> UT --> Intake --> EM --> ME ME --> Swatch ME --> Up Swatch -->|true: markAllProcessesFinished| Up @@ -437,7 +443,7 @@ Roles tab: same `RolesSettingsView` + `DropTargetOverlay` as the main app (full - Also invoked from `OpenEventHandler.init` and `Marker_DataApp` `.task` - `ExtractionModel.handleOpenDocument` validates type and starts extract / gate -Info.plist advertises Media Asset Protocol and document types for asset media/description collections. +Info.plist (`Source/Marker Data/Marker-Data-Info.plist`) advertises Media Asset Protocol, Sparkle, and document types for asset media/description collections. Keep **Asset Description File** as the FCPXML/`fcpxmld` `CFBundleTypeName` (`DocumentController` matches that string). Also declares `UTImportedTypeDeclarations` for `com.apple.finalcutpro.xml` / `.xmld` so Extract layout does not trap when FCP is missing. **Note:** FCP **timeline** drops onto the Dock icon are often pasteboard-only (no file URL). Prefer Extract panel drop, file Open With, Workflow Extension, or Share Destination for media+XML. @@ -491,7 +497,7 @@ Notable modules under `Views/`: |------|--------| | Main | `ContentView`, `ExtractView` (drop overlay + `.fcpxmlDropDestination`) | | Detail | General (File/Roles/Notifications/Updates), Image, Label, Configurations, Databases, Queue (folder drop overlay), About | -| Menu commands | App / File / Edit / Sidebar / Configuration / Help | +| Menu commands | App / File (`FileCommands` replaces `.newItem` so system Close stays last) / Edit / SwiftUI `SidebarCommands` / Configuration / Help | | Onboarding | `@AppStorage("showOnboarding")` sheet | | Components | `DropTargetOverlay` (main app + WE), `FCPXMLDropModifier`, `HelpButton` / `OverlayHelpButton`, shared controls | | Extensions | **`MarkerDataAppIcon` / `.appDialogIcon()`** (`DialogIcon.swift`, `@MainActor`) — About, Workflow Extension header, alerts and confirmation dialogs; see **App icon and dialogs**. Also `ApplyPickerSizing`, `OptionalKeyboardShortcut`. | @@ -508,6 +514,7 @@ flowchart TB EX[ExtractView] RO[RolesSettingsView] QU[QueueView] + FILE[FileCommands replacing newItem] end subgraph weUI [Workflow Extension] @@ -523,7 +530,8 @@ flowchart TB WRO --> OV EX --> FM[FCPXMLDropModifier] - FM --> IN[FCPXMLIntake] + FM --> UT[UTType.fcpxml / .fcpxmld] + UT --> IN[FCPXMLIntake] IN --> EM[ExtractionModel] RO --> RMD[RolesManager DropDelegate] @@ -543,6 +551,8 @@ flowchart TB subgraph disk [One file on disk] DI[DialogIcon.swift] DTO[DropTargetOverlay.swift] + WEV[WorkflowExtensionView.swift] + UT[UTTypeExtension.swift] RM[RolesManager.swift] SS[SettingsStore.swift] end @@ -559,6 +569,10 @@ flowchart TB DI --> WSRC DTO --> MSRC DTO --> WSRC + WEV --> MSRC + WEV --> WSRC + UT --> MSRC + UT --> WSRC RM --> MSRC RM --> WSRC SS --> MSRC @@ -582,6 +596,59 @@ flowchart LR --- +## File menu + +`Marker_DataApp` registers `FileCommands()` and does **not** empty `.newItem`. `FileCommands` **replaces** `.newItem` so custom items sit at the top of File and system **Close / Close All** stay last. Do not add a Close button. (`Marker_DataApp` does empty `.toolbar` — that only removes the default View-menu toolbar group, not File → Close.) + +| Order | Item | Notes | +|-------|------|--------| +| 1 | Open Pagemaker | `openWindow(id: "pagemaker")`, ⌘P | +| 2 | Install FCP Share Destination… | `ShareDestinationInstaller.install()` | +| 3 | Show Cache / Clean Cache | Movies cache folder; Clean is ⌘K | +| last | Close / Close All | System — do not duplicate | + +Extract **Choose File** remains on `ExtractView` (`FilePicker`, ⌘O), not the File menu. SwiftUI `SidebarCommands` is the system sidebar group (no custom `SidebarCommands.swift`). A second **Open Pagemaker** control appears on the Extract completion footer when the profile is extract-only Notion or Airtable (`showPagemakerOpenButton`). + +```mermaid +flowchart TB + FileMenu[File menu] + Custom["FileCommands replacing .newItem"] + SysClose[System Close / Close All] + FileMenu --> Custom + FileMenu --> SysClose + Custom --> PM[Open Pagemaker] + Custom --> SD[Install FCP Share Destination] + Custom --> Cache[Show Cache / Clean Cache] +``` + +--- + +## FCPXML UTTypes without Final Cut Pro + +`com.apple.finalcutpro.xml` / `.xmld` are not in the UTI database unless Final Cut Pro is installed. `UTType("…")!` traps on first Extract layout (`FilePicker` / drop evaluate `.fcpxml` / `.fcpxmld`). + +**Public API:** `UTType.fcpxml` and `UTType.fcpxmld` (`UTTypeExtension.swift`). The helper `finalCutProType` is **private**: `UTType(identifier) ?? UTType(importedAs:identifier, conformingTo:)` with `.xml` for `.fcpxml` and `.package` for `.fcpxmld`. **Never** force-unwrap. Callers use `UTType.fcpxml` / `.fcpxmld` (e.g. `ExtractionModel.supportedContentTypes`, `FilePicker`, `FCPXMLDropModifier`, Workflow Extension `.onDrop`). + +**Declare** in `Source/Marker Data/Marker-Data-Info.plist`: `UTImportedTypeDeclarations` for both identifiers, plus `LSItemContentTypes` on the existing **Asset Description File** document type. Do **not** split or rename that type (Share Destination `DocumentController` matches `"Asset Description File"`). Do not add versioned pasteboard UTIs (`…xml.v1`–`v14`) or iPad `.fcpproj`. + +The Workflow Extension also compiles `UTTypeExtension.swift`; FCP is present in that host, but the same helper must stay `importedAs`-safe. + +```mermaid +flowchart LR + UI["Extract FilePicker / drop / WE"] + UT["UTType.fcpxml / .fcpxmld"] + Lookup["UTType identifier"] + Import["UTType importedAs conformingTo"] + Plist["Marker-Data-Info.plist UTImportedTypeDeclarations"] + + UI --> UT --> Lookup + Lookup -->|FCP installed| OK[Resolved] + Lookup -->|FCP absent nil| Import --> OK + Plist --> Lookup +``` + +--- + ## App icon and dialogs Shared resolution in `Views/Extensions/DialogIcon.swift` (`@MainActor` `MarkerDataAppIcon` + `.appDialogIcon()`). Marker Data does **not** enable `SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor` (unlike Production Data), so the enum and `.appDialogIcon()` must be isolated or Swift 6 warns on `NSApplication.shared.applicationIconImage`. @@ -664,7 +731,22 @@ Changing automation, sandbox, or extension behavior usually requires entitlement | Binary refresh workflows | `update_airlift_binary.yml`, `update_csv2notion_neo_binary.yml`, `update_pagemaker.yml` | ### Notable SPM dependencies (main app) -MarkersExtractor, DominantColors, DockProgress, Sparkle, FilePicker, ColorWellKit, PasswordField, ButtonKit, WebViewKit, swift-collections, swift-log-oslog (and related logging). + +Minimum versions from `project.pbxproj` (`XCRemoteSwiftPackageReference`): + +| Package | Minimum | +|---------|---------| +| MarkersExtractor | **0.4.8** | +| DominantColors | 1.2.2 | +| DockProgress | 5.1.0 | +| Sparkle | 2.9.0 | +| FilePicker | 1.0.1 | +| ColorWellKit | 1.1.2 | +| PasswordField | 1.0.0 | +| ButtonKit | 0.7.1 | +| WebViewKit | 1.0.0 | +| swift-collections | 1.4.0 | +| swift-log-oslog | 0.2.2 | --- @@ -705,7 +787,7 @@ Source/Marker Data/Marker Data/ Views/ Main/ # ContentView, ExtractView Detail Views/ # General, Image, Label, Configurations, Databases, QueueView, About - Menu Bar Commands/ + Menu Bar Commands/ # FileCommands (replaces .newItem), AppCommands, EditCommands, ConfigurationCommands, HelpCommands Components/ # DropTargetOverlay, FCPXMLDropModifier, HelpButton, OverlayHelpButton, … Extensions/ # DialogIcon (@MainActor MarkerDataAppIcon + .appDialogIcon), ApplyPickerSizing Onboarding/, Other/ # FailedExtractionsView, ExportProfilePicker, … @@ -713,7 +795,7 @@ Source/Marker Data/Marker Data/ Install View/, Objective-C Code/, OpenEventHandler.swift Pagemaker/ # PagemakerView, PDF export handler, UIDelegate, WebViewStateManager Utilities/ - Extensions/ # URL, Color (markerAccent, heroGradient), UTType, NotificationName, … + Extensions/ # URL, Color (markerAccent, heroGradient), UTType (`UTType.fcpxml` / `.fcpxmld`, never `!`), NotificationName, … Shell/, Notifications/, Other/ # FCPXMLIntake, TextClippingReader, LibraryFolders, FileWatcher, … Resources/ @@ -722,10 +804,15 @@ Source/Marker Data/Marker Data/ Marker-Data.icon # Icon Composer (ASSETCATALOG_COMPILER_APPICON_NAME = Marker-Data) Assets.xcassets/ # empty AppIcon.appiconset placeholder; no AppIconSingle +Source/Marker Data/Marker-Data-Info.plist + # Sparkle, Media Asset Protocol, Share Destination document types (keep Asset Description File), + # UTImportedTypeDeclarations for com.apple.finalcutpro.xml / .xmld + Source/Marker Data/Workflow Extension/ - WorkflowExtensionView.swift # Extract overlay + handoff; MarkerDataAppIcon header 100×100; hosts RolesSettingsView; TabView .padding(.bottom, 40) - WorkflowExtensionViewController.swift - Assets.xcassets/, Info.plist, entitlements, bridging header + WorkflowExtensionView.swift # Extract overlay + handoff; MarkerDataAppIcon header 100×100; hosts RolesSettingsView; TabView .padding(.bottom, 40); SwiftUI frame 600×400; **two** Compile Sources rows (main app + appex) + WorkflowExtensionViewController.swift # Appex-only + Info.plist # NSExtension WorkflowExtension; ProExtensionAttributes 700×550 + Assets.xcassets/, entitlements, bridging header # Bundle/plugin icon remains AppIcon.appiconset (do not replace with Marker-Data.icon) # Header UI loads the containing Marker Data.app icon via DialogIcon.swift # Compile Sources: see Workflow Extension target table above (shared files = two pbxproj memberships) @@ -753,4 +840,6 @@ Source/Marker Data/Marker Data Uninstaller/ 13. FCPXML pasteboard/clipping temps live under `~/Movies/Marker Data Cache/` (not App Support). 14. Shared Swift sources appear twice in `project.pbxproj` Compile Sources (one file, two targets). Never “dedupe” those rows. 15. Workflow Extension decodes `SettingsStore` without running migrations — main app must migrate on launch first. -16. Definition of done: unsigned arm64 Debug+Release build; settings migrate; `.fcpxml`/`.fcpxmld` + pasteboard intake; WE Extract + Roles overlays; queue finds/uploads via `manifestURL`; agent docs (`AGENT.md` / `ARCHITECTURE.md` / `GUARDRAILS.md` / `.cursorrules`) stay aligned. +16. FCPXML UTTypes must resolve without Final Cut Pro installed: public `UTType.fcpxml` / `.fcpxmld` (private `finalCutProType`: `UTType(identifier)` then `importedAs:conformingTo:`) plus `UTImportedTypeDeclarations` in `Marker-Data-Info.plist`. Never `UTType("com.apple.finalcutpro.xml")!`. Keep the Share Destination **Asset Description File** document type (do not rename/split it). +17. File menu custom items replace `.newItem` (`FileCommands`). Do not empty `.newItem` in `Marker_DataApp` (puts system Close first) and do not add a second Close. +18. Definition of done: unsigned arm64 Debug+Release build; settings migrate; `.fcpxml`/`.fcpxmld` + pasteboard intake; FCPXML UTTypes use `importedAs` fallback; File menu Close last; WE Extract + Roles overlays; queue finds/uploads via `manifestURL`; agent docs (`AGENT.md` / `ARCHITECTURE.md` / `GUARDRAILS.md` / `.cursorrules`) stay aligned. diff --git a/CHANGELOG.md b/CHANGELOG.md index a2fb48f6..94edb7a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +### 2.1.1 (25) + +**🎉 Released:** +- 9th September 2026 + +**🐞 Bug Fix:** +- Launch no longer crashes on Macs that do not have Final Cut Pro installed. +- File menu now lists app actions first, with Close last. + +--- + ### 2.1.0 (24) **🎉 Released:** diff --git a/GUARDRAILS.md b/GUARDRAILS.md index b2cc4503..16977868 100644 --- a/GUARDRAILS.md +++ b/GUARDRAILS.md @@ -31,6 +31,8 @@ Read with **`AGENT.md`** (how to change things), **`ARCHITECTURE.md`** (how it w | **Architecture** | Prefer Apple Silicon (`arm64`) only; CI uses `macos-26` + **Xcode 26.6.0**. | | **Opaque CLIs** | Treat `airlift` / `csv2notion_neo` as black boxes; only the args in `DatabaseUploader` / `DropboxSetupModel` are the contract. | | **App icon** | Main app: Icon Composer **`Marker-Data.icon`** (name matches `ASSETCATALOG_COMPILER_APPICON_NAME`). Keep the empty main-app `AppIcon.appiconset` placeholder. Do not flatten the layer PNG into `AppIconSingle`. Workflow Extension **header** loads the icon from the containing `Marker Data.app`. Do **not** replace the Workflow Extension bundle/plugin icon (`AppIcon.appiconset`). | +| **FCPXML UTTypes** | Resolve `.fcpxml` / `.fcpxmld` with `UTType(identifier) ?? UTType(importedAs:identifier, conformingTo:)` (`.xml` / `.package`). Keep `UTImportedTypeDeclarations` and Asset Description File `LSItemContentTypes` in `Marker-Data-Info.plist`. | +| **File menu** | Custom File items replace `.newItem` (`FileCommands`) so system Close / Close All stay last. | --- @@ -51,6 +53,8 @@ Read with **`AGENT.md`** (how to change things), **`ARCHITECTURE.md`** (how it w | **Silent config overwrite** | Never replace an existing configuration file because the name already exists. | | **AppIconSingle flatten** | Never copy the Icon Composer layer PNG into `AppIconSingle` (skips glass / fill). | | **WE header icon** | Never use the appex `AppIcon.appiconset` / `applicationIconImage` for the Workflow Extension window header — that is the bundle/plugin icon (update separately). | +| **FCPXML `UTType` unwrap** | Never force-unwrap `com.apple.finalcutpro.xml` / `.xmld`. Never split or rename the Share Destination **Asset Description File** document type. | +| **File menu Close** | Never empty `.newItem` in `Marker_DataApp` (that puts system Close first). Never add a second Close item. | --- @@ -82,8 +86,10 @@ Shared component: `Views/Components/DropTargetOverlay.swift` (main app + Workflo 11. **`OpenEventHandler`** must re-register on `.FCPShareStart`; keep registration on the main queue. `setupHandler()` also runs from `init` and `Marker_DataApp` `.task`. 12. **Failed Tasks table** (`FailedExtractionsView`): one-line truncate + `.help()` tooltips; min frame ~640×240. 13. **Workflow Extension help vs drop:** `overlayHelpButton` is bottom-trailing. Keep Extract/Roles `TabView` `.padding(.bottom, 40)` so the drop overlay does not crowd the “?”. -14. **Shared Compile Sources look duplicated in `pbxproj`.** One `DialogIcon.swift` (and other shared files) correctly appears twice as `PBXBuildFile` — main app + Workflow Extension. Deleting one membership breaks the extension. +14. **Shared Compile Sources look duplicated in `pbxproj`.** One `DialogIcon.swift` (and other shared files, including `WorkflowExtensionView.swift`) correctly appears twice as `PBXBuildFile` — main app + Workflow Extension. Deleting one membership breaks the extension (or, for `WorkflowExtensionView`, still violates the two-row contract — leave the unused main-app row). `WorkflowExtensionViewController.swift` is the only appex-only Swift file. 15. **Workflow Extension does not migrate settings.** `RolesManager` JSON-decodes `SettingsStore` with no `SettingsVersioningManager`. Open the main app after a schema bump. +16. **FCPXML `UTType` force-unwrap** — `UTType("com.apple.finalcutpro.xml")!` traps on Extract first layout when Final Cut Pro is absent (App Preview / review Macs). Use `importedAs` fallback and Info.plist imported types. Do not copy the old Marker Data force-unwrap. +17. **File menu Close last:** custom File items replace `.newItem`. Emptying `.newItem` makes system Close the first File item; do not duplicate Close. --- @@ -92,6 +98,8 @@ Shared component: `Views/Components/DropTargetOverlay.swift` (main app + Workflo - [ ] Unsigned arm64 Debug + Release build succeeds (`CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO`; main app embeds Workflow Extension). - [ ] Settings load/migrate (`preferences.json` + `Configurations/*.json`). - [ ] Extract accepts `.fcpxml` / `.fcpxmld`, FCP pasteboard drop, and text clippings via `FCPXMLIntake`. +- [ ] FCPXML `UTType`s still use `importedAs` fallback (no `!` on `com.apple.finalcutpro.xml` / `.xmld`); Info.plist still has `UTImportedTypeDeclarations`; Share Destination **Asset Description File** type is unchanged. +- [ ] File menu still replaces `.newItem` with app actions; system Close stays last (no second Close; do not empty `.newItem`). - [ ] Drop overlays appear on main-app Extract / Roles / Queue **and** Workflow Extension Extract + Roles with the copy above. - [ ] Queue finds `extract_info.json` folders and uploads via `manifestURL` (relocated folders work). - [ ] No-media / skipped swatch finishes as **“Extract done”** (not **“Analysing swatch done”**); `ColorPaletteRenderer.render` → `Bool` drives finish path. diff --git a/Source/Marker Data/Marker Data/Marker_DataApp.swift b/Source/Marker Data/Marker Data/Marker_DataApp.swift index 87f0a411..8f05d772 100644 --- a/Source/Marker Data/Marker Data/Marker_DataApp.swift +++ b/Source/Marker Data/Marker Data/Marker_DataApp.swift @@ -110,9 +110,6 @@ struct Marker_DataApp: App { .windowResizability(.contentSize) // Customise Menu Bar Commands .commands { - // Removes New Window Menu Item - CommandGroup(replacing: .newItem) {} - // Removes Toolbar Menu Items CommandGroup(replacing: .toolbar) {} diff --git a/Source/Marker Data/Marker Data/Utilities/Extensions/UTTypeExtension.swift b/Source/Marker Data/Marker Data/Utilities/Extensions/UTTypeExtension.swift index 5a86cd77..75937045 100644 --- a/Source/Marker Data/Marker Data/Utilities/Extensions/UTTypeExtension.swift +++ b/Source/Marker Data/Marker Data/Utilities/Extensions/UTTypeExtension.swift @@ -4,11 +4,27 @@ // // Created by Milán Várady on 07/10/2023. // +// +// FCPXML/FCPXMLD UTTypes that resolve without Final Cut Pro installed. +// import Foundation import UniformTypeIdentifiers extension UTType { - public static let fcpxml = UTType("com.apple.finalcutpro.xml")! - public static let fcpxmld = UTType("com.apple.finalcutpro.xmld")! + /// Final Cut Pro XML document (`.fcpxml`). + public static let fcpxml = Self.finalCutProType( + identifier: "com.apple.finalcutpro.xml", + conformingTo: .xml + ) + + /// Final Cut Pro XML bundle (`.fcpxmld`). + public static let fcpxmld = Self.finalCutProType( + identifier: "com.apple.finalcutpro.xmld", + conformingTo: .package + ) + + private static func finalCutProType(identifier: String, conformingTo supertype: UTType) -> UTType { + UTType(identifier) ?? UTType(importedAs: identifier, conformingTo: supertype) + } } diff --git a/Source/Marker Data/Marker Data/Views/Detail Views/Database/DatabaseSettingsView.swift b/Source/Marker Data/Marker Data/Views/Detail Views/Database/DatabaseSettingsView.swift index cc1fa8fe..376e4f4d 100644 --- a/Source/Marker Data/Marker Data/Views/Detail Views/Database/DatabaseSettingsView.swift +++ b/Source/Marker Data/Marker Data/Views/Detail Views/Database/DatabaseSettingsView.swift @@ -61,6 +61,7 @@ struct DatabaseSettingsView: View { Button("Cancel", role: .cancel) {} } + .appDialogIcon() } var tableView: some View { diff --git a/Source/Marker Data/Marker Data/Views/Menu Bar Commands/FileCommands.swift b/Source/Marker Data/Marker Data/Views/Menu Bar Commands/FileCommands.swift index a0af30d8..946f89c9 100644 --- a/Source/Marker Data/Marker Data/Views/Menu Bar Commands/FileCommands.swift +++ b/Source/Marker Data/Marker Data/Views/Menu Bar Commands/FileCommands.swift @@ -4,6 +4,10 @@ // // Created by Milán Várady on 20/01/2024. // +// +// File menu items replace the New group so system Close / Close All stay last. +// Do not duplicate Close here. +// import Foundation import SwiftUI @@ -12,7 +16,7 @@ struct FileCommands: Commands { @Environment(\.openWindow) var openWindow var body: some Commands { - CommandGroup(after: .importExport) { + CommandGroup(replacing: .newItem) { Button("Open Pagemaker") { openWindow(id: "pagemaker") } @@ -29,13 +33,13 @@ struct FileCommands: Commands { } } } - + Divider() - + Button("Show Cache") { NSWorkspace.shared.open(URL.FCPExportCacheFolder) } - + Button("Clean Cache") { LibraryFolders.deleteCache() } diff --git a/Source/Marker Data/Marker-Data-Info.plist b/Source/Marker Data/Marker-Data-Info.plist index 7094495c..2b35ec05 100644 --- a/Source/Marker Data/Marker-Data-Info.plist +++ b/Source/Marker Data/Marker-Data-Info.plist @@ -71,10 +71,52 @@ Viewer LSHandlerRank Default + LSItemContentTypes + + com.apple.finalcutpro.xml + com.apple.finalcutpro.xmld + NSDocumentClass + UTImportedTypeDeclarations + + + UTTypeConformsTo + + public.xml + + UTTypeDescription + Final Cut Pro XML + UTTypeIdentifier + com.apple.finalcutpro.xml + UTTypeTagSpecification + + public.filename-extension + + fcpxml + + + + + UTTypeConformsTo + + com.apple.package + + UTTypeDescription + Final Cut Pro XML Bundle + UTTypeIdentifier + com.apple.finalcutpro.xmld + UTTypeTagSpecification + + public.filename-extension + + fcpxmld + + + + NSAppleScriptEnabled NSServices