From 6aea5f91d2fe991b850bad1e3df6da4e471079e1 Mon Sep 17 00:00:00 2001 From: Duyet Le Date: Sat, 13 Jun 2026 23:36:44 +0700 Subject: [PATCH 1/2] feat(build-macos-apps): add macOS app building skills and commands New plugin for building, debugging, instrumenting, and implementing macOS apps with SwiftUI and AppKit guidance, codesigning, packaging, and telemetry workflows. 11 skills (liquid-glass, signing-entitlements, packaging-notarization, test-triage, window-management, telemetry, swiftui-patterns, view-refactor, appkit-interop, build-run-debug, swiftpm-macos) + 3 commands (build-and-run-macos-app, fix-codesign-error, test-macos-app) + agent definition. Claude + Codex manifests included; already registered in the marketplace manifests. Co-Authored-By: duyetbot --- build-macos-apps/.claude-plugin/plugin.json | 12 ++ build-macos-apps/.codex-plugin/plugin.json | 25 +++ build-macos-apps/README.md | 65 +++++++ build-macos-apps/agents/openai.yaml | 6 + .../commands/build-and-run-macos-app.md | 30 +++ .../commands/fix-codesign-error.md | 22 +++ build-macos-apps/commands/test-macos-app.md | 23 +++ .../skills/appkit-interop/SKILL.md | 68 +++++++ .../references/drag-drop-pasteboard.md | 23 +++ .../references/representables.md | 56 ++++++ .../references/responder-menus.md | 22 +++ .../references/window-panels.md | 37 ++++ .../skills/build-run-debug/SKILL.md | 129 +++++++++++++ .../references/run-button-bootstrap.md | 172 ++++++++++++++++++ build-macos-apps/skills/liquid-glass/SKILL.md | 170 +++++++++++++++++ .../skills/packaging-notarization/SKILL.md | 47 +++++ .../skills/signing-entitlements/SKILL.md | 58 ++++++ .../skills/swiftpm-macos/SKILL.md | 50 +++++ .../skills/swiftui-patterns/SKILL.md | 135 ++++++++++++++ .../references/commands-menus.md | 41 +++++ .../references/components-index.md | 16 ++ .../references/menu-bar-extra.md | 62 +++++++ .../swiftui-patterns/references/settings.md | 51 ++++++ .../references/split-inspectors.md | 111 +++++++++++ .../swiftui-patterns/references/windowing.md | 46 +++++ build-macos-apps/skills/telemetry/SKILL.md | 86 +++++++++ build-macos-apps/skills/test-triage/SKILL.md | 54 ++++++ .../skills/view-refactor/SKILL.md | 109 +++++++++++ .../skills/window-management/SKILL.md | 170 +++++++++++++++++ .../references/api-snippets.md | 60 ++++++ 30 files changed, 1956 insertions(+) create mode 100644 build-macos-apps/.claude-plugin/plugin.json create mode 100644 build-macos-apps/.codex-plugin/plugin.json create mode 100644 build-macos-apps/README.md create mode 100644 build-macos-apps/agents/openai.yaml create mode 100644 build-macos-apps/commands/build-and-run-macos-app.md create mode 100644 build-macos-apps/commands/fix-codesign-error.md create mode 100644 build-macos-apps/commands/test-macos-app.md create mode 100644 build-macos-apps/skills/appkit-interop/SKILL.md create mode 100644 build-macos-apps/skills/appkit-interop/references/drag-drop-pasteboard.md create mode 100644 build-macos-apps/skills/appkit-interop/references/representables.md create mode 100644 build-macos-apps/skills/appkit-interop/references/responder-menus.md create mode 100644 build-macos-apps/skills/appkit-interop/references/window-panels.md create mode 100644 build-macos-apps/skills/build-run-debug/SKILL.md create mode 100644 build-macos-apps/skills/build-run-debug/references/run-button-bootstrap.md create mode 100644 build-macos-apps/skills/liquid-glass/SKILL.md create mode 100644 build-macos-apps/skills/packaging-notarization/SKILL.md create mode 100644 build-macos-apps/skills/signing-entitlements/SKILL.md create mode 100644 build-macos-apps/skills/swiftpm-macos/SKILL.md create mode 100644 build-macos-apps/skills/swiftui-patterns/SKILL.md create mode 100644 build-macos-apps/skills/swiftui-patterns/references/commands-menus.md create mode 100644 build-macos-apps/skills/swiftui-patterns/references/components-index.md create mode 100644 build-macos-apps/skills/swiftui-patterns/references/menu-bar-extra.md create mode 100644 build-macos-apps/skills/swiftui-patterns/references/settings.md create mode 100644 build-macos-apps/skills/swiftui-patterns/references/split-inspectors.md create mode 100644 build-macos-apps/skills/swiftui-patterns/references/windowing.md create mode 100644 build-macos-apps/skills/telemetry/SKILL.md create mode 100644 build-macos-apps/skills/test-triage/SKILL.md create mode 100644 build-macos-apps/skills/view-refactor/SKILL.md create mode 100644 build-macos-apps/skills/window-management/SKILL.md create mode 100644 build-macos-apps/skills/window-management/references/api-snippets.md diff --git a/build-macos-apps/.claude-plugin/plugin.json b/build-macos-apps/.claude-plugin/plugin.json new file mode 100644 index 0000000..7c32a9e --- /dev/null +++ b/build-macos-apps/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "build-macos-apps", + "version": "1.0.0", + "description": "Build, debug, instrument, and implement macOS apps with SwiftUI and AppKit guidance, codesigning, packaging, and telemetry workflows.", + "author": { + "name": "duyet", + "url": "https://github.com/duyet" + }, + "homepage": "https://github.com/duyet/codex-claude-plugins", + "repository": "https://github.com/duyet/codex-claude-plugins", + "license": "MIT" +} diff --git a/build-macos-apps/.codex-plugin/plugin.json b/build-macos-apps/.codex-plugin/plugin.json new file mode 100644 index 0000000..1c85485 --- /dev/null +++ b/build-macos-apps/.codex-plugin/plugin.json @@ -0,0 +1,25 @@ +{ + "name": "build-macos-apps", + "version": "1.0.0", + "description": "Build, debug, instrument, and implement macOS apps with SwiftUI and AppKit guidance, codesigning, packaging, and telemetry workflows.", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "commands": "./commands/", + "agents": "./agents/", + "interface": { + "displayName": "Build macOS Apps", + "shortDescription": "Build, debug, instrument, and implement macOS apps with SwiftUI and AppKit guidance", + "developerName": "duyet", + "category": "development", + "capabilities": [ + "Skill", + "Command", + "Agent" + ], + "links": { + "homepage": "https://github.com/duyet/codex-claude-plugins" + } + } +} diff --git a/build-macos-apps/README.md b/build-macos-apps/README.md new file mode 100644 index 0000000..f6fea27 --- /dev/null +++ b/build-macos-apps/README.md @@ -0,0 +1,65 @@ +# Build macOS Apps Plugin + +This plugin packages macOS-first development workflows. + +It currently includes these skills: + +- `build-run-debug` +- `test-triage` +- `signing-entitlements` +- `swiftpm-macos` +- `packaging-notarization` +- `swiftui-patterns` +- `liquid-glass` +- `window-management` +- `appkit-interop` +- `view-refactor` +- `telemetry` + +## What It Covers + +- discovering local Xcode workspaces, projects, and Swift packages +- building and running macOS apps with shell-first desktop workflows +- creating one project-local `script/build_and_run.sh` entrypoint and wiring `.codex/environments/environment.toml` so the Codex app Run button works +- implementing native macOS SwiftUI scenes, menus, settings, toolbars, and multiwindow flows +- adopting modern macOS Liquid Glass and design-system guidance with standard SwiftUI structures, toolbars, search, controls, and custom glass surfaces +- tailoring SwiftUI windows with title/toolbar styling, material-backed container backgrounds, minimize/restoration behavior, default and ideal placement, borderless window style, and launch behavior +- bridging into AppKit for representables, responder-chain behavior, panels, and other desktop-only needs +- refactoring large macOS view files toward stable scene, selection, and command structure +- adding lightweight `Logger` / `os.Logger` instrumentation for windows, sidebars, menu commands, and menu bar actions +- reading and verifying runtime events with Console, `log stream`, and process logs +- triaging failing unit, integration, and UI-hosted macOS tests +- debugging launch failures, crashes, linker problems, and runtime regressions +- inspecting signing identities, entitlements, hardened runtime, and Gatekeeper issues +- preparing packaging and notarization workflows for distribution + +## Plugin Structure + +``` +build-macos-apps/ +├── .claude-plugin/ # Claude manifest +├── .codex-plugin/ # Codex manifest +├── agents/ +│ └── openai.yaml # OpenAI surface metadata +├── assets/ # Icons and SVGs +├── commands/ # Slash commands +│ ├── build-and-run-macos-app.md +│ ├── fix-codesign-error.md +│ └── test-macos-app.md +└── skills/ # Skill payloads + ├── build-run-debug/ + ├── test-triage/ + ├── signing-entitlements/ + ├── swiftpm-macos/ + ├── packaging-notarization/ + ├── swiftui-patterns/ + ├── liquid-glass/ + ├── window-management/ + ├── appkit-interop/ + ├── view-refactor/ + └── telemetry/ +``` + +## License + +MIT diff --git a/build-macos-apps/agents/openai.yaml b/build-macos-apps/agents/openai.yaml new file mode 100644 index 0000000..358d8af --- /dev/null +++ b/build-macos-apps/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Build macOS Apps" + short_description: "Build, debug, instrument, and implement macOS apps with SwiftUI and AppKit guidance" + icon_small: "./assets/build-macos-apps-small.svg" + icon_large: "./assets/app-icon.png" + default_prompt: "Build/refactor native macOS UI, inspect AppKit interop, add telemetry, or debug a macOS app." diff --git a/build-macos-apps/commands/build-and-run-macos-app.md b/build-macos-apps/commands/build-and-run-macos-app.md new file mode 100644 index 0000000..dc493ff --- /dev/null +++ b/build-macos-apps/commands/build-and-run-macos-app.md @@ -0,0 +1,30 @@ +# /build-and-run-macos-app + +Create or update the project-local macOS `build_and_run.sh` script, wire the +Codex app Run button, then use that script as the default build/run entrypoint. + +## Arguments + +- `scheme`: Xcode scheme name (optional) +- `workspace`: path to `.xcworkspace` (optional) +- `project`: path to `.xcodeproj` (optional) +- `product`: SwiftPM executable product name (optional) +- `mode`: `run`, `debug`, `logs`, `telemetry`, or `verify` (optional, default: `run`) +- `app_name`: process/app name to stop before relaunching (optional) + +## Workflow + +1. Detect whether the repo uses an Xcode workspace, Xcode project, or SwiftPM package. +2. If the workspace is not inside git yet, run `git init` at the project root so Codex app git-backed features unlock. +3. Create or update `script/build_and_run.sh` so it always stops the current app, builds the macOS target, and launches the fresh result. +4. For SwiftPM, keep raw executable launch only for true CLI tools; for AppKit/SwiftUI GUI apps, create a project-local `.app` bundle and launch it with `/usr/bin/open -n`. +5. Support optional script flags for `--debug`, `--logs`, `--telemetry`, and `--verify`. +6. Follow the canonical bootstrap contract in `../skills/build-run-debug/references/run-button-bootstrap.md` for the exact script shape and `.codex/environments/environment.toml` format. +7. Run the script in the requested mode and summarize any build, script, or launch failure. + +## Guardrails + +- Do not initialize a nested git repo inside an existing parent checkout. +- Do not leave stale `Run` actions pointing at old script paths. +- Keep the no-flag script path simple: kill, build, run. +- Use `--debug`, `--logs`, `--telemetry`, or `--verify` only when the user asks for those modes. diff --git a/build-macos-apps/commands/fix-codesign-error.md b/build-macos-apps/commands/fix-codesign-error.md new file mode 100644 index 0000000..5414e48 --- /dev/null +++ b/build-macos-apps/commands/fix-codesign-error.md @@ -0,0 +1,22 @@ +# /fix-codesign-error + +Inspect a macOS signing or entitlement failure and explain the minimum fix path. + +## Arguments + +- `app`: path to `.app` bundle or binary (optional) +- `identity`: signing identity hint (optional) +- `mode`: `inspect` or `repair-plan` (optional, default: `inspect`) + +## Workflow + +1. Inspect the app bundle, executable, signing info, and entitlements. +2. Determine whether the problem is identity, provisioning, hardened runtime, sandboxing, or trust policy. +3. Summarize the exact failure class in plain language. +4. Provide the minimal repair sequence or validation command. + +## Guardrails + +- Never invent entitlements; read them from the binary or source files. +- Distinguish local development signing problems from distribution or notarization failures. +- Prefer verifiable commands like `codesign -d`, `spctl`, and `plutil` over guesswork. diff --git a/build-macos-apps/commands/test-macos-app.md b/build-macos-apps/commands/test-macos-app.md new file mode 100644 index 0000000..595b34b --- /dev/null +++ b/build-macos-apps/commands/test-macos-app.md @@ -0,0 +1,23 @@ +# /test-macos-app + +Run the smallest meaningful macOS test scope first and explain failures by category. + +## Arguments + +- `scheme`: Xcode scheme name (optional) +- `target`: test target or product name (optional) +- `filter`: test filter expression (optional) +- `configuration`: `Debug` or `Release` (optional, default: `Debug`) + +## Workflow + +1. Detect whether the repo uses `xcodebuild test` or `swift test`. +2. Prefer focused test execution when a target or filter is provided. +3. Classify failures as compile, assertion, crash, env/setup, or flake. +4. Summarize the top blocker and the narrowest sensible next step. + +## Guardrails + +- Avoid rerunning the full suite if a focused rerun is possible. +- Distinguish build failures from actual failing tests. +- Note when host app setup or simulator-only test assumptions leak into a macOS run. diff --git a/build-macos-apps/skills/appkit-interop/SKILL.md b/build-macos-apps/skills/appkit-interop/SKILL.md new file mode 100644 index 0000000..76783ef --- /dev/null +++ b/build-macos-apps/skills/appkit-interop/SKILL.md @@ -0,0 +1,68 @@ +--- +name: appkit-interop +description: Bridge macOS SwiftUI into AppKit narrowly. Use when implementing representables, reaching NSWindow or panels, handling menus, or using the responder chain. +--- + +# AppKit Interop + +## Quick Start + +Use this skill when SwiftUI is close but not quite enough for native macOS behavior. +Keep the bridge as small and explicit as possible. SwiftUI should usually remain +the source of truth, while AppKit handles the imperative edge. + +## Choose The Smallest Bridge + +- Use pure SwiftUI when the required behavior already exists in scenes, toolbars, commands, inspectors, or standard controls. +- Use `NSViewRepresentable` when you need a specific AppKit view with lightweight lifecycle needs. +- Use `NSViewControllerRepresentable` when you need controller lifecycle, delegation, or presentation coordination. +- Use direct AppKit window or app hooks when you need `NSWindow`, responder-chain, menu validation, panels, or app-level behavior. + +## Workflow + +1. Name the capability gap precisely. + - Window behavior + - Text system behavior + - Menu validation + - Drag and drop + - File open/save panels + - First responder control + +2. Pick the smallest boundary that solves it. + - Avoid porting a whole screen to AppKit when one wrapped control or coordinator would do. + +3. Keep ownership explicit. + - SwiftUI owns value state, selection, and observable models. + - AppKit objects stay inside the representable, coordinator, or bridge object. + +4. Expose a narrow interface back to SwiftUI. + - Bindings for editable state + - Small callbacks for events + - Focused bridge services only when necessary + +5. Validate lifecycle assumptions. + - SwiftUI may recreate representables. + - Coordinators exist to hold delegate and target-action glue, not as a second app architecture. + +## References + +- `references/representables.md`: choosing between view and view-controller wrappers, plus coordinator patterns. +- `references/window-panels.md`: window access, utility windows, and open/save panels. +- `references/responder-menus.md`: first responder, command routing, and menu validation. +- `references/drag-drop-pasteboard.md`: pasteboard, file URLs, and desktop drag/drop edges. + +## Guardrails + +- Do not duplicate the source of truth between SwiftUI and AppKit. +- Do not let `Coordinator` become an unstructured dumping ground. +- Do not store long-lived `NSView` or `NSWindow` instances globally without a strong ownership reason. +- Prefer a tiny tested bridge over rewriting the feature in raw AppKit. +- If a pattern can remain entirely in `swiftui-patterns`, keep it there. + +## Output Expectations + +Provide: +- the exact SwiftUI limitation being crossed +- the smallest recommended bridge type +- the data-flow boundary between SwiftUI and AppKit +- the lifecycle or validation risks to watch diff --git a/build-macos-apps/skills/appkit-interop/references/drag-drop-pasteboard.md b/build-macos-apps/skills/appkit-interop/references/drag-drop-pasteboard.md new file mode 100644 index 0000000..d1f5e73 --- /dev/null +++ b/build-macos-apps/skills/appkit-interop/references/drag-drop-pasteboard.md @@ -0,0 +1,23 @@ +# Drag, Drop, and Pasteboard + +## Intent + +Use this when desktop drag/drop or pasteboard behavior exceeds what plain SwiftUI modifiers cover comfortably. + +## Good fits + +- File URL dragging +- Pasteboard interoperability with other macOS apps +- Rich drag previews or AppKit-specific drop validation +- Legacy AppKit views with custom drag types + +## Core patterns + +- Start with SwiftUI drag/drop APIs when they already cover the use case. +- Drop to AppKit when you need `NSPasteboard`, custom pasteboard types, or older AppKit delegate flows. +- Keep data conversion at the boundary instead of leaking AppKit types through the whole feature. + +## Pitfalls + +- Do not move your whole list or canvas into AppKit just for one drop target. +- Keep file and pasteboard types explicit and validated. diff --git a/build-macos-apps/skills/appkit-interop/references/representables.md b/build-macos-apps/skills/appkit-interop/references/representables.md new file mode 100644 index 0000000..104eea3 --- /dev/null +++ b/build-macos-apps/skills/appkit-interop/references/representables.md @@ -0,0 +1,56 @@ +# Representables + +## Intent + +Use this when wrapping an AppKit control or controller for a SwiftUI macOS app. + +## Choose the wrapper type + +- Use `NSViewRepresentable` for a view-level bridge such as `NSTextView`, `NSScrollView`, or a custom AppKit control. +- Use `NSViewControllerRepresentable` when you need controller lifecycle, delegate coordination, or AppKit presentation logic. + +## Skeleton + +```swift +struct LegacyTextView: NSViewRepresentable { + @Binding var text: String + + func makeCoordinator() -> Coordinator { + Coordinator(text: $text) + } + + func makeNSView(context: Context) -> NSScrollView { + let scrollView = NSScrollView() + let textView = NSTextView() + textView.delegate = context.coordinator + scrollView.documentView = textView + return scrollView + } + + func updateNSView(_ nsView: NSScrollView, context: Context) { + guard let textView = nsView.documentView as? NSTextView else { return } + if textView.string != text { + textView.string = text + } + } + + final class Coordinator: NSObject, NSTextViewDelegate { + @Binding var text: String + + init(text: Binding) { + _text = text + } + + func textDidChange(_ notification: Notification) { + guard let textView = notification.object as? NSTextView else { return } + text = textView.string + } + } +} +``` + +## Pitfalls + +- Avoid infinite update loops by only pushing state into AppKit when values actually changed. +- Keep delegates and target-action wiring in the coordinator. +- If the wrapper grows into a full screen, re-evaluate the boundary. diff --git a/build-macos-apps/skills/appkit-interop/references/responder-menus.md b/build-macos-apps/skills/appkit-interop/references/responder-menus.md new file mode 100644 index 0000000..9c312d7 --- /dev/null +++ b/build-macos-apps/skills/appkit-interop/references/responder-menus.md @@ -0,0 +1,22 @@ +# Responder Chain and Menus + +## Intent + +Use this when command handling depends on the active window, first responder, or AppKit menu validation. + +## Core patterns + +- Start with SwiftUI `commands`, `FocusedValue`, and focused scene state. +- Use AppKit responder-chain hooks only when command routing or validation truly depends on the underlying responder system. +- Keep menu enablement rules close to the state they depend on. + +## Good fits for AppKit + +- Validating whether a menu item should be enabled +- Routing actions through the current first responder +- Integrating with existing AppKit document or text behaviors + +## Pitfalls + +- Do not recreate AppKit-style global command handling when SwiftUI focused values would work. +- Avoid scattering command logic between SwiftUI closures and AppKit selectors without a clear boundary. diff --git a/build-macos-apps/skills/appkit-interop/references/window-panels.md b/build-macos-apps/skills/appkit-interop/references/window-panels.md new file mode 100644 index 0000000..f89e0f3 --- /dev/null +++ b/build-macos-apps/skills/appkit-interop/references/window-panels.md @@ -0,0 +1,37 @@ +# Windows and Panels + +## Intent + +Use this when SwiftUI scenes are not enough for the required macOS window or panel behavior. + +## Common cases + +- Accessing the backing `NSWindow` +- Configuring titlebar or toolbar behavior +- Presenting `NSOpenPanel` or `NSSavePanel` +- Managing utility panels or floating windows + +## Core patterns + +- Prefer SwiftUI `Window`, `WindowGroup`, and `openWindow` first. +- Use AppKit only for window features SwiftUI does not expose cleanly. +- Keep file open/save panels behind a small service or helper instead of scattering panel setup throughout the view tree. + +## Example: open panel + +```swift +@MainActor +func chooseFile() -> URL? { + let panel = NSOpenPanel() + panel.canChooseFiles = true + panel.canChooseDirectories = false + panel.allowsMultipleSelection = false + return panel.runModal() == .OK ? panel.url : nil +} +``` + +## Pitfalls + +- Do not let random views own long-lived `NSWindow` references. +- Keep floating panels and utility windows consistent with the scene model. +- If the behavior is really just settings or a secondary scene, go back to `swiftui-patterns`. diff --git a/build-macos-apps/skills/build-run-debug/SKILL.md b/build-macos-apps/skills/build-run-debug/SKILL.md new file mode 100644 index 0000000..d017921 --- /dev/null +++ b/build-macos-apps/skills/build-run-debug/SKILL.md @@ -0,0 +1,129 @@ +--- +name: build-run-debug +description: Build, run, and debug macOS apps with shell-first Xcode and Swift workflows. Use when launching apps or diagnosing build, startup, or runtime failures. +--- + +# Build / Run / Debug + +## Quick Start + +Use this skill to set up one project-local `script/build_and_run.sh` entrypoint, +wire `.codex/environments/environment.toml` so the Codex app shows a Run button, +then use that script as the default build/run path. + +Prefer shell-first workflows: + +- `./script/build_and_run.sh` as the single kill + build + run entrypoint once it exists +- `xcodebuild` for Xcode workspaces or projects +- `swift build` plus raw executable launch inside that script for true SwiftPM command-line tools +- `swift build` plus project-local `.app` bundle staging and `/usr/bin/open -n` launch for SwiftPM AppKit/SwiftUI GUI apps +- optional script flags for `lldb`, `log stream`, telemetry verification, or post-launch process checks + +Do not assume simulators, touch interaction, or mobile-specific tooling. + +If an Xcode-aware MCP surface is already available and the user explicitly wants +it, use it only where it fits. Keep that usage narrow and honest: prefer it for +Xcode-oriented discovery, logging, or debugging support, and do not force +simulator-specific workflows onto pure macOS tasks. + +## Workflow + +1. Discover the project shape. + - Check whether the workspace is already inside a git repo with `git rev-parse --is-inside-work-tree`. + - If no git repo is present, run `git init` at the project/workspace root before building so Codex app git-backed features are available. Never run `git init` inside a nested subdirectory when the current workspace already belongs to a parent repo. + - Look for `.xcworkspace`, `.xcodeproj`, and `Package.swift`. + - If more than one candidate exists, explain the default choice and the ambiguity. + +2. Resolve the runnable target and process name. + - For Xcode, list schemes and prefer the app-producing scheme unless the user names another one. + - For SwiftPM, identify executable products when possible. + - Split SwiftPM launch handling by product type: + - use raw executable launch only for true command-line tools, + - use a generated project-local `.app` bundle for AppKit/SwiftUI GUI apps. + - Determine the app/process name to kill before relaunching. + +3. Create or update `script/build_and_run.sh`. + - Make the script project-specific and executable. + - It should always: + 1. stop the existing running app/process if present, + 2. build the macOS target, + 3. launch the freshly built app or executable. + - Add optional flags for debugging/log inspection: + - `--debug` to launch under `lldb` or attach the debugger + - `--logs` to stream process logs after launch + - `--telemetry` to stream unified logs filtered to the app subsystem/category + - `--verify` to launch the app and confirm the process exists with `pgrep -x ` + - Keep the default no-flag path simple: kill, build, run. + - Prefer writing one script that owns this workflow instead of repeatedly asking the agent to manually run `swift build`, locate the artifact, then invoke an ad hoc run command. + - For SwiftPM GUI apps, make the script build the product, create `dist/.app`, copy the binary to `Contents/MacOS/`, generate a minimal `Contents/Info.plist` with `CFBundlePackageType=APPL`, `CFBundleExecutable`, `CFBundleIdentifier`, `CFBundleName`, `LSMinimumSystemVersion`, and `NSPrincipalClass=NSApplication`, then launch with `/usr/bin/open -n `. + - For SwiftPM GUI `--logs` and `--telemetry`, launch the bundle with `/usr/bin/open -n` first, then stream unified logs with `/usr/bin/log stream --info ...`. + - Do not recommend direct SwiftPM executable launch for AppKit/SwiftUI GUI apps. + - Use `references/run-button-bootstrap.md` as the canonical source for the + script shape and exact environment file format. Do not fork a second + authoritative snippet in another skill or command. + - Keep the run script outside app source. It belongs in `script/build_and_run.sh`, not in `App/`, `Views/`, `Models/`, `Stores/`, `Services/`, or `Support/`. + +4. Write `.codex/environments/environment.toml` at the project root once the script exists. + - Use this exact placement: `.codex/environments/environment.toml`. + - Use the exact action shape in `references/run-button-bootstrap.md`. + - This file is what gives the user a Codex app Run button wired to the script. + - If the project already has this file, update the `Run` action command to point at `./script/build_and_run.sh` instead of creating a duplicate action. + - Keep this Codex environment config separate from Swift app source files. + +5. Build and run through the script. + - Default to `./script/build_and_run.sh`. + - Use `./script/build_and_run.sh --debug`, `--logs`, `--telemetry`, or `--verify` when the user asks for debugger/log/telemetry/process verification support. + +6. Summarize failures correctly. + - Classify the blocker as compiler, linker, signing, build settings, missing SDK/toolchain, script bug, or runtime launch. + - Quote the smallest useful error snippet and explain what it means. + +7. Debug the right way. + - Use the script's `--logs` or `--telemetry` mode for config, entitlement, sandbox, and action-event verification. + - For SwiftPM GUI apps, if the app bundle launches but its window still does not come forward, check whether the entrypoint needs `NSApp.setActivationPolicy(.regular)` and `NSApp.activate(ignoringOtherApps: true)`. + - Use the script's `--debug` mode or direct `lldb` if symbolized crash debugging is needed. + - If the user needs to instrument and verify specific window, sidebar, menu, or menu bar actions, switch to `telemetry`. + - Keep evidence tight and user-facing. + +8. Use Xcode-aware MCP tooling only when it helps. + - If the user explicitly asks for XcodeBuildMCP and it is already available, prefer it over ad hoc setup. + - Use the MCP for Xcode-aware discovery or debug/logging workflows when the available tool surface clearly matches the task. + - Fall back to shell commands immediately when the MCP does not provide a clean macOS path. + +## Preferred Commands + +- Project discovery: + - `find . -name '*.xcworkspace' -o -name '*.xcodeproj' -o -name 'Package.swift'` +- Scheme discovery: + - `xcodebuild -list -workspace ` + - `xcodebuild -list -project ` +- Build/run: + - `./script/build_and_run.sh` + - `./script/build_and_run.sh --debug` + - `./script/build_and_run.sh --logs` + - `./script/build_and_run.sh --telemetry` + - `./script/build_and_run.sh --verify` + +## References + +- `references/run-button-bootstrap.md`: canonical `build_and_run.sh` and `.codex/environments/environment.toml` contract. + +## Guardrails + +- Prefer the narrowest command that proves or disproves the current theory. +- Do not leave the user with a one-off manual command chain once a stable `build_and_run.sh` script can own the workflow. +- Do not write `.codex/environments/environment.toml` before the run script exists, and do not point the Run action at a stale script path. +- Do not launch a SwiftUI/AppKit SwiftPM GUI app as a raw executable unless the user explicitly wants to diagnose that failure mode: it can produce no Dock icon, no foreground activation, and missing bundle identifier warnings. Keep raw executable launch only for true command-line tools. +- Do not claim UI state you cannot inspect directly. +- Do not describe mobile or simulator workflows as if they apply to macOS. +- If build output is huge, summarize the first real blocker and point to follow-up commands. + +## Output Expectations + +Provide: +- the detected project type +- the script path and Codex environment action you configured, if applicable +- the command you ran +- whether build and launch succeeded +- the top blocker if they failed +- the smallest sensible next action diff --git a/build-macos-apps/skills/build-run-debug/references/run-button-bootstrap.md b/build-macos-apps/skills/build-run-debug/references/run-button-bootstrap.md new file mode 100644 index 0000000..8796ca6 --- /dev/null +++ b/build-macos-apps/skills/build-run-debug/references/run-button-bootstrap.md @@ -0,0 +1,172 @@ +# Run Button Bootstrap + +This is the canonical bootstrap contract for the macOS Build plugin's local run +loop. + +When a project does not already have an established macOS run entrypoint: + +1. Create one project-local `script/build_and_run.sh`. +2. Make it executable. +3. Use it as the single kill + build + run entrypoint. +4. Support optional `--debug`, `--logs`, `--telemetry`, and `--verify` flags. +5. Write `.codex/environments/environment.toml` so the Codex app exposes a + `Run` action wired to that script. + +## `script/build_and_run.sh` + +Use one project-specific script with a tiny mode switch and a default no-flag +path that just kills, builds, and launches. Keep raw executable launch only for +true command-line tools. For SwiftPM AppKit/SwiftUI GUI apps, stage a +project-local `.app` bundle and launch that bundle with `/usr/bin/open -n`. + +### SwiftPM CLI executable + +Use this shape for true command-line tools: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +MODE="${1:-run}" +APP_NAME="MyTool" + +pkill -x "$APP_NAME" >/dev/null 2>&1 || true + +swift build +APP_BINARY="$(swift build --show-bin-path)/$APP_NAME" + +case "$MODE" in + run) + "$APP_BINARY" + ;; + --debug|debug) + lldb -- "$APP_BINARY" + ;; + --logs|logs) + "$APP_BINARY" & + /usr/bin/log stream --info --style compact --predicate "process == \"$APP_NAME\"" + ;; + --telemetry|telemetry) + "$APP_BINARY" & + /usr/bin/log stream --info --style compact --predicate "subsystem == \"com.example.MyTool\"" + ;; + --verify|verify) + "$APP_BINARY" & + sleep 1 + pgrep -x "$APP_NAME" >/dev/null + ;; + *) + echo "usage: $0 [run|--debug|--logs|--telemetry|--verify]" >&2 + exit 2 + ;; +esac +``` + +### SwiftPM AppKit/SwiftUI GUI app + +Use this shape for SwiftPM GUI apps so they launch as a real foreground app +bundle with Dock activation and bundle metadata: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +MODE="${1:-run}" +APP_NAME="MyApp" +BUNDLE_ID="com.example.MyApp" +MIN_SYSTEM_VERSION="14.0" + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +DIST_DIR="$ROOT_DIR/dist" +APP_BUNDLE="$DIST_DIR/$APP_NAME.app" +APP_CONTENTS="$APP_BUNDLE/Contents" +APP_MACOS="$APP_CONTENTS/MacOS" +APP_BINARY="$APP_MACOS/$APP_NAME" +INFO_PLIST="$APP_CONTENTS/Info.plist" + +pkill -x "$APP_NAME" >/dev/null 2>&1 || true + +swift build +BUILD_BINARY="$(swift build --show-bin-path)/$APP_NAME" + +rm -rf "$APP_BUNDLE" +mkdir -p "$APP_MACOS" +cp "$BUILD_BINARY" "$APP_BINARY" +chmod +x "$APP_BINARY" + +cat >"$INFO_PLIST" < + + + + CFBundleExecutable + $APP_NAME + CFBundleIdentifier + $BUNDLE_ID + CFBundleName + $APP_NAME + CFBundlePackageType + APPL + LSMinimumSystemVersion + $MIN_SYSTEM_VERSION + NSPrincipalClass + NSApplication + + +PLIST + +open_app() { + /usr/bin/open -n "$APP_BUNDLE" +} + +case "$MODE" in + run) + open_app + ;; + --debug|debug) + lldb -- "$APP_BINARY" + ;; + --logs|logs) + open_app + /usr/bin/log stream --info --style compact --predicate "process == \"$APP_NAME\"" + ;; + --telemetry|telemetry) + open_app + /usr/bin/log stream --info --style compact --predicate "subsystem == \"$BUNDLE_ID\"" + ;; + --verify|verify) + open_app + sleep 1 + pgrep -x "$APP_NAME" >/dev/null + ;; + *) + echo "usage: $0 [run|--debug|--logs|--telemetry|--verify]" >&2 + exit 2 + ;; +esac +``` + +## `.codex/environments/environment.toml` + +Write the environment file at this exact path: + +`.codex/environments/environment.toml` + +with this action shape: + +```toml +# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY +version = 1 +name = "" + +[setup] +script = "" + +[[actions]] +name = "Run" +icon = "run" +command = "./script/build_and_run.sh" +``` + +If the project already has an environment file, update the existing `Run` +action to point at `./script/build_and_run.sh` instead of adding a duplicate. diff --git a/build-macos-apps/skills/liquid-glass/SKILL.md b/build-macos-apps/skills/liquid-glass/SKILL.md new file mode 100644 index 0000000..9095a81 --- /dev/null +++ b/build-macos-apps/skills/liquid-glass/SKILL.md @@ -0,0 +1,170 @@ +--- +name: liquid-glass +description: Implement and review macOS SwiftUI Liquid Glass UI. Use when adopting system glass, removing conflicting custom chrome, or building glass surfaces. +--- + +# Liquid Glass + +## Overview + +Use this skill to bring a macOS SwiftUI app into the modern macOS design system +with the least custom chrome possible. Start with standard app structure, +toolbars, search placement, sheets, and controls, then add custom Liquid Glass +only where the app needs a distinctive surface. + +Prefer system-provided glass and adaptive materials over bespoke blur, opaque +backgrounds, or custom toolbar/sidebar skins. Audit existing UI for extra fills, +scrims, and clipping before adding more effects. + +## Workflow + +1. Read the relevant scene or root view and identify the structural pattern: + `NavigationSplitView`, `TabView`, sheet presentation, detail/inspector + layout, toolbar, or custom floating controls. +2. Remove custom backgrounds or darkening layers behind system sheets, + sidebars, and toolbars unless the product explicitly needs them. These can + obscure Liquid Glass and interfere with the automatic scroll-edge effect. +3. Update standard SwiftUI structure and controls first. +4. Add custom `glassEffect` surfaces only for app-specific UI that standard + controls do not cover. +5. Validate that glass grouping, transitions, icon treatment, and foreground + activation are visually coherent and still usable with pointer and keyboard. +6. If the UI change also affects launch behavior for a SwiftPM GUI app, use + `build-run-debug` so the app runs as a foreground `.app` bundle rather + than as a raw executable. + +## App Structure + +- Prefer `NavigationSplitView` for hierarchy-driven macOS layouts. Let the + sidebar use the system Liquid Glass material instead of painting over it. +- For hero artwork or large media adjacent to a floating sidebar, use + `backgroundExtensionEffect` so the visual can extend beyond the safe area + without clipping the subject. +- Keep inspectors visually associated with the current selection and avoid + giving them a heavier custom background than the content they inspect. +- If the app uses tabs, keep `TabView` for persistent top-level sections and + preserve each tab's local navigation state. +- Do not force iPhone-only tab bar minimize/accessory behavior onto a Mac app. + On macOS, prefer a conventional top toolbar and native tab/search placement. +- If a sheet already uses `presentationBackground` purely to imitate frosted + material, consider removing it and letting the system's new material render. +- For sheet transitions that should visually originate from a toolbar button, + make the presenting item the source of a navigation zoom transition and mark + the sheet content as the destination. + +## Toolbars + +- Assume toolbar items are rendered on a floating Liquid Glass surface and are + grouped automatically. +- Use `ToolbarSpacer` to communicate grouping: + - fixed spacing to split related actions into a distinct group, + - flexible spacing to push a leading action away from a trailing group. +- Use `sharedBackgroundVisibility` when an item should stand alone without the + shared glass background, for example a profile/avatar item. +- Add `badge` to toolbar item content for notification or status indicators. +- Expect monochrome icon rendering in more toolbar contexts. Use `tint` only to + convey semantic meaning such as a primary action or alert state, not as pure + decoration. +- If content underneath a toolbar has extra darkening, blur, or custom + background layers, remove them before judging the new automatic scroll-edge + effect. +- For dense windows with many floating elements, tune the content's scroll-edge + treatment with `scrollEdgeEffectStyle` instead of building a custom bar + background. + +## Search + +- For a search field that applies across a whole split-view hierarchy, attach + `searchable` to the `NavigationSplitView`, not to just one column. +- When search is secondary and a compact affordance is better, use + `searchToolbarBehavior` instead of hand-rolling a toolbar button and a + separate field. +- For a dedicated search page in a multi-tab app, assign the search role to one + tab and place `searchable` on the `TabView`. +- Make most of the app's content discoverable from search when the field lives + in the top-trailing toolbar location. +- On iPad and Mac, expect the dedicated search tab to show a centered field + above browsing suggestions rather than a bottom search bar. + +## Controls + +- Prefer standard SwiftUI controls before creating custom glass components. +- Expect bordered buttons to default to a capsule shape at larger sizes. On + macOS, mini/small/medium controls preserve a rounded-rectangle shape for + denser layouts. +- Use `buttonBorderShape` when a button shape needs to be explicit. +- Use `controlSize` to preserve density in inspectors and popovers, and reserve + extra-large sizing for truly prominent actions. +- Use the system glass and glass-prominent button styles for primary actions + instead of recreating a translucent button background by hand. +- For sliders with discrete values, pass `step` to get automatic tick marks or + provide specific ticks in a `ticks` closure. +- For sliders that should expand left and right around a baseline, set + `neutralValue`. +- Use `Label` or standard control initializers for menu items so icons are + consistently placed on the leading edge across platforms. +- For custom shapes that must align concentrically with a sheet, card, or + window corner, use a concentric rectangle shape with the + `containerConcentric` corner configuration instead of guessing a radius. + +## Custom Liquid Glass + +- Use `glassEffect` for custom glass surfaces. The default shape is capsule-like + and text foregrounds are automatically made vibrant and legible against + changing content underneath. +- Pass an explicit shape to `glassEffect` when a capsule is not the right fit. +- Add `tint` only when color carries meaning, such as a status or call to + action. +- Use `glassEffect(... .interactive())` for custom controls or containers with + interactive elements so they scale, bounce, and shimmer like system glass. +- Wrap nearby custom glass elements in one `GlassEffectContainer`. This is a + visual correctness rule, not just organization: separate containers cannot + sample each other's glass and can produce inconsistent refraction. +- Use `glassEffectID` with a local `@Namespace` when matching glass elements + should morph between collapsed and expanded states. + +## Review Checklist + +- Standard structures and controls were updated first before adding custom + glass. +- Opaque backgrounds, dark scrims, and custom toolbar/sheet fills that fight the + system material were removed unless intentionally required. +- `searchable` is attached at the correct container level for the intended + search scope. +- Toolbar grouping uses `ToolbarSpacer`, `sharedBackgroundVisibility`, and + `badge` instead of one-off hand-built chrome. +- Icon tint is semantic, not decorative. +- Custom glass elements that sit near each other share a + `GlassEffectContainer`. +- Morphing glass transitions use `glassEffectID` with a namespace and stable + identity. +- Any SwiftPM GUI app used to test the result is launched as a `.app` bundle, + not as a raw executable. + +## Guardrails + +- Do not rebuild system sidebars, toolbars, sheets, or controls from scratch if + standard SwiftUI APIs already provide the modern macOS behavior. +- Do not apply custom opaque backgrounds behind a `NavigationSplitView` + sidebar, system toolbar, or sheet just because an older version needed + one. +- Do not scatter related glass elements across multiple + `GlassEffectContainer`s. +- Do not tint every icon or glass surface for visual variety alone. +- Do not assume an iPhone tab/search behavior is the right answer on macOS. + Prefer desktop-native toolbar, split-view, and inspector placement. +- Do not leave a GUI SwiftPM app launching as a bare executable when reviewing + Liquid Glass behavior; missing foreground activation can make a design bug + look like a rendering bug. + +## When To Use Other Skills + +- Use `swiftui-patterns` when the main question is scene architecture, + sidebar/detail layout, commands, or settings rather than Liquid Glass-specific + treatment. +- Use `view-refactor` when the main issue is file structure, state + ownership, and extracting large views before design changes. +- Use `appkit-interop` when the design requires window, panel, responder-chain, + or AppKit-only control behavior. +- Use `build-run-debug` when you need to launch, verify, or inspect logs + for the app after the visual update. diff --git a/build-macos-apps/skills/packaging-notarization/SKILL.md b/build-macos-apps/skills/packaging-notarization/SKILL.md new file mode 100644 index 0000000..13312f1 --- /dev/null +++ b/build-macos-apps/skills/packaging-notarization/SKILL.md @@ -0,0 +1,47 @@ +--- +name: packaging-notarization +description: Prepare macOS packaging and notarization workflows. Use when archiving apps, validating bundles, or explaining distribution-only failures. +--- + +# Packaging & Notarization + +## Quick Start + +Use this skill when the work is about shipping the app rather than merely +running it locally: archives, exported app bundles, notarization readiness, +hardened runtime, or distribution validation. + +## Workflow + +1. Confirm the distribution goal. + - Local archive validation + - Signed distributable app + - Notarization troubleshooting + +2. Inspect the artifact. + - Validate app bundle structure. + - Check nested frameworks, helper tools, and entitlements. + +3. Inspect signing and runtime prerequisites. + - Hardened runtime + - Signing identity + - Nested code signatures + - Required entitlements + +4. Explain notarization readiness or failure. + - Separate packaging issues from trust-policy symptoms. + - Point to the minimum follow-up validation commands. + +## Guardrails + +- Do not present notarization as required for ordinary local debug runs. +- Call out when you lack the actual exported artifact and are inferring from project settings. +- Keep advice concrete and verifiable. + +## Output Expectations + +Provide: +- what artifact or settings were inspected +- whether the app looks distribution-ready +- the top missing prerequisite or failure mode +- the next validation or repair step diff --git a/build-macos-apps/skills/signing-entitlements/SKILL.md b/build-macos-apps/skills/signing-entitlements/SKILL.md new file mode 100644 index 0000000..02a6c66 --- /dev/null +++ b/build-macos-apps/skills/signing-entitlements/SKILL.md @@ -0,0 +1,58 @@ +--- +name: signing-entitlements +description: Inspect macOS signing, entitlements, and Gatekeeper issues. Use when diagnosing code signing, sandbox, hardened runtime, or trust failures. +--- + +# Signing & Entitlements + +## Quick Start + +Use this skill when the failure smells like codesigning rather than compilation: +launch refusal, missing entitlement, invalid signature, sandbox mismatch, +hardened runtime confusion, or trust-policy rejection. + +## Workflow + +1. Inspect the bundle or binary. + - Locate the `.app` or executable. + - Identify the main binary inside `Contents/MacOS/`. + +2. Read signing details. + - Use `codesign -dvvv --entitlements :- `. + - Use `spctl -a -vv ` when Gatekeeper behavior matters. + - Use `plutil -p` for entitlements or Info.plist inspection. + +3. Classify the failure. + - Unsigned or ad hoc signed + - Wrong identity + - Entitlement mismatch + - Hardened runtime issue + - App Sandbox issue + - Nested code signing issue + - Distribution/notarization prerequisite issue + +4. Explain the minimum fix path. + - Say exactly what is wrong. + - Show the shortest set of validation or repair commands. + - Distinguish local development problems from distribution problems. + +## Useful Commands + +- `codesign -dvvv --entitlements :- ` +- `spctl -a -vv ` +- `security find-identity -p codesigning -v` +- `plutil -p ` + +## Guardrails + +- Never invent missing entitlements. +- Do not conflate notarization with local debug signing. +- If the real issue is a build setting or provisioning profile, say so directly. + +## Output Expectations + +Provide: +- what artifact was inspected +- what signing state it is in +- the exact failure class +- the minimum fix or validation sequence diff --git a/build-macos-apps/skills/swiftpm-macos/SKILL.md b/build-macos-apps/skills/swiftpm-macos/SKILL.md new file mode 100644 index 0000000..a1e6c77 --- /dev/null +++ b/build-macos-apps/skills/swiftpm-macos/SKILL.md @@ -0,0 +1,50 @@ +--- +name: swiftpm-macos +description: Build, run, and test SwiftPM macOS packages and executables. Use when the repo is package-first or has no Xcode project. +--- + +# SwiftPM for macOS + +## Quick Start + +Use this skill when `Package.swift` is the primary entrypoint or when SwiftPM is +the fastest path to a reproducible result. + +## Workflow + +1. Inspect the package. + - Read `Package.swift`. + - Identify executable, library, and test products. + +2. Build with SwiftPM. + - Use `swift build` by default. + - Use release mode only when the user explicitly needs it. + +3. Run the right product. + - Use `swift run ` when an executable exists. + - If multiple executables exist, explain the default choice. + +4. Test narrowly. + - Use `swift test`. + - Apply filters when a specific test target or case is known. + +5. Summarize failures. + - Module/import resolution + - Package graph or dependency issue + - Linker failure + - Runtime failure + - Test regression + +## Guardrails + +- Prefer SwiftPM over Xcode when both exist and the package path is clearly simpler. +- Do not assume an app bundle exists in a pure package workflow. +- Explain when the package is library-only and therefore not directly runnable. + +## Output Expectations + +Provide: +- the package products you found +- the command you ran +- whether build, run, or test succeeded +- the top blocker if not diff --git a/build-macos-apps/skills/swiftui-patterns/SKILL.md b/build-macos-apps/skills/swiftui-patterns/SKILL.md new file mode 100644 index 0000000..1453b20 --- /dev/null +++ b/build-macos-apps/skills/swiftui-patterns/SKILL.md @@ -0,0 +1,135 @@ +--- +name: swiftui-patterns +description: Build macOS SwiftUI scenes and components with desktop patterns. Use when shaping windows, commands, toolbars, settings, split views, or inspectors. +--- + +# SwiftUI Patterns + +## Quick Start + +Choose a track based on your goal: + +### Existing project + +- Identify the feature or scene and the primary interaction model: document, editor, sidebar-detail, utility window, settings, or menu bar extra. +- Read the nearest existing scene or root view before inventing a new desktop structure. +- Choose the relevant reference from `references/components-index.md`. +- If SwiftUI cannot express the required platform behavior cleanly, use the `appkit-interop` skill rather than forcing a shaky workaround. + +### New app scaffolding + +- Choose the scene model first: `WindowGroup`, `Window`, `Settings`, `MenuBarExtra`, or `DocumentGroup`. +- If the app combines a normal main window and a `MenuBarExtra`, use `WindowGroup(..., id:)` for the primary window when it should appear at launch. Treat `Window(...)` as a better fit for auxiliary/on-demand singleton windows; in menu-bar-heavy apps, a `Window(...)` scene may not present the main window automatically at launch. +- Before creating the scaffold, check whether the workspace is already inside a git repo with `git rev-parse --is-inside-work-tree`. If not, run `git init` at the project root so Codex app git-backed features are available from the start. Do not initialize a nested repo inside an existing parent checkout. +- For a new app scaffold, also create one project-local `script/build_and_run.sh` and `.codex/environments/environment.toml` so the Codex app Run button works immediately. Use the exact bootstrap contract from `build-run-debug` and its `references/run-button-bootstrap.md` file rather than inventing a second variant here. +- Decide which state is app-wide, scene-scoped, or window-scoped before writing views. +- Sketch file and module boundaries before writing the full UI. For any non-trivial app, create the folder structure first and split files by responsibility from the start. +- Use a single Swift file only for tiny throwaway examples or snippets: roughly under 50 lines, one screen, no persistence, no networking/process client, and no reusable models. Anything beyond that should be multi-file immediately. +- Use system-adaptive colors and materials by default (`Color.primary`, `Color.secondary`, semantic foreground styles, `.regularMaterial`, etc.) so the app follows Light/Dark mode automatically. Do not hardcode white or light backgrounds unless the user explicitly asks for a fixed theme, and do not reach for opaque `windowBackgroundColor` fills for root panes by default. +- Pick the references for the first feature surface you need: windowing, commands, split layouts, or settings. + +## New App File Structure + +For any non-trivial macOS app, start with this shape instead of putting the app, +all views, models, stores, services, and helpers in one Swift file: + +- `App/App.swift`: the `@main` app type and `AppDelegate` only. +- `Views/ContentView.swift`: root layout and high-level composition only. +- `Views/SidebarView.swift`, `Views/DetailView.swift`, `Views/ComposerView.swift`, etc.: feature views named after their primary type. +- `Models/*.swift`: value models, identifiers, and selection enums. +- `Stores/*.swift`: persistence and state stores. +- `Services/*.swift`: app-server, network, process, or platform clients. +- `Support/*.swift`: small formatters, resolvers, extensions, and glue helpers. + +Keep files small and named after the primary type they contain. If a file starts +collecting unrelated views, models, stores, networking clients, and helper +extensions, split it before adding more behavior. + +## Pre-Edit Checklist For New App Scaffolds + +Before writing the full UI: + +1. Choose the scene model. +2. Choose state ownership: app-wide, scene-scoped, window-scoped, or view-local. +3. Sketch file and module boundaries. +4. Create the folder structure before filling in the UI. +5. Keep `script/build_and_run.sh` and `.codex/environments/environment.toml` separate from app source. + +## General Rules To Follow + +- Design for pointer, keyboard, menus, and multiple windows. +- Keep scenes explicit. A separate settings window, utility window, or menu bar extra should be modeled as its own scene, not hidden inside one monolithic `ContentView`. +- Prefer system desktop affordances: `commands`, toolbars, sidebars, inspectors, contextual menus, and `searchable`. +- For menu bar apps, keep `MenuBarExtra` item titles and action labels short and scannable. Cap visible menu item text at 30 characters; if source content is longer, truncate or summarize it before rendering and open the full content in a dedicated window or detail surface. +- If a `MenuBarExtra` app should still behave like a regular Dock app with a visible main window/process, install an `NSApplicationDelegate` via `@NSApplicationDelegateAdaptor`, call `NSApp.setActivationPolicy(.regular)` during launch, and activate the app with `NSApp.activate(ignoringOtherApps: true)`. If the app is intentionally menu-bar-only, document that `.accessory` / no-Dock behavior is a deliberate product choice. +- Prefer system-adaptive colors, materials, and semantic foreground styles. Avoid fixed white/light backgrounds in scaffolding and examples unless the requested design explicitly calls for a custom non-adaptive theme. +- Do not paint `NavigationSplitView` sidebars or root window panes with opaque custom `Color(...)` or `Color(nsColor: .windowBackgroundColor)` fills by default. Prefer native macOS sidebar/window materials and system-provided backgrounds unless the user explicitly asks for a custom opaque surface. In sidebar-detail-inspector layouts, let the sidebar keep the standard source-list/material appearance and reserve custom backgrounds for detail or inspector content cards where needed. +- Use `@SceneStorage` for per-window ephemeral state and `@AppStorage` for durable user preferences. +- Keep selection state explicit and stable. macOS layouts often pivot around sidebar selection rather than push navigation. +- Prefer `NavigationSplitView` or a deliberate manual split layout over iOS-style stacked flows when the app benefits from always-visible structure. +- For `List(...).listStyle(.sidebar)` and `NavigationSplitView` sidebars, prefer flat native rows with standard system selection/highlight behavior. Keep rows visually lightweight and Mail-like: at most one leading icon, one strong title line, and one optional secondary detail line in `.secondary`. Avoid stacked metadata rows, repeated inline utility icons, or dense multi-column status text in the sidebar. Reserve card-style and metadata-heavy surfaces for detail or inspector panes unless the user explicitly asks for a highly custom sidebar treatment. +- Keep primary actions discoverable from both UI chrome and keyboard shortcuts when appropriate. +- Use SwiftUI-native scenes and views first. If you need low-level window, responder-chain, text system, or panel control, switch to `appkit-interop`. + +For concrete sidebar row and split-view background examples, read +`references/split-inspectors.md`. + +## State Ownership Summary + +Use the narrowest state tool that matches the ownership model: + +| Scenario | Preferred pattern | +| --- | --- | +| Local view or control state | `@State` | +| Child mutates parent-owned value state | `@Binding` | +| Root-owned reference model on macOS 14+ | `@State` with an `@Observable` type | +| Child reads or mutates an injected `@Observable` model | Pass it explicitly as a stored property | +| Window-scoped ephemeral selection or expansion state | `@SceneStorage` when practical, otherwise scene-owned `@State` | +| Shared user preference | `@AppStorage` | +| Shared app service or configuration | `@Environment(Type.self)` | +| Legacy reference model on older targets | `@StateObject` at the owner and `@ObservedObject` when injected | + +Choose the ownership location first, then the wrapper. Do not turn simple desktop state into a view model by reflex. + +## Cross-Cutting References + +- `references/components-index.md`: entry point for scene and component guidance. +- `references/windowing.md`: choosing between `WindowGroup`, `Window`, `DocumentGroup`, and window-opening patterns. +- `references/settings.md`: dedicated settings scenes, `SettingsLink`, and preference layouts. +- `references/commands-menus.md`: command menus, keyboard shortcuts, focused values, and desktop action routing. +- `references/split-inspectors.md`: sidebars, split views, selection-driven layout, and inspectors. +- `references/menu-bar-extra.md`: menu bar extra structure and when it fits. + +## Anti-Patterns + +- One huge `ContentView` pretending the whole app is a single screen. +- A single Swift file containing the `@main` app, all views, models, stores, networking/process clients, formatters, and extensions. This is acceptable only for tiny throwaway snippets under the new-app threshold above. +- Touch-first interaction models ported directly from iOS without desktop affordances. +- Hiding core actions behind gestures with no menu, toolbar, or keyboard path. +- Building a menu-bar-plus-window app around only a `Window(...)` scene and then expecting the main window to appear at launch. Use `WindowGroup(..., id:)` for the primary launch window and reserve `Window(...)` for auxiliary/on-demand windows. +- Rendering full unbounded document titles, prompts, or message text directly inside a menu bar extra. Menu item labels should stay at or below 30 characters, with longer content moved into a dedicated window or detail view. +- Treating settings as another navigation destination in the main content window. +- Hardcoding `.background(.white)`, `Color.white`, or a fixed light palette in a brand-new scaffold without an explicit design requirement. +- Wrapping each sidebar item in large rounded custom cards inside a `.sidebar` list, which fights native source-list density, alignment, and selection behavior unless the user explicitly asked for a bespoke visual sidebar. +- Building sidebar rows with multiple repeated icons, three or more text lines, or a dense strip of inline metadata counters/timestamps/models. Keep the sidebar row to one icon and one or two text lines, then move richer metadata into the detail pane. +- Painting `NavigationSplitView` sidebars or root window panes with opaque custom color fills by default, instead of letting the sidebar use native source-list/material appearance and reserving custom backgrounds for actual content cards. +- Using push navigation for layouts that want stable sidebar selection and detail panes. +- Reaching for AppKit before the SwiftUI scene and command APIs have been used properly. + +## Workflow For A New macOS Scene Or View + +1. Define the scene type and ownership model before writing child views. +2. Decide which actions live in content, toolbars, commands, inspectors, or settings. +3. Sketch the selection model and layout: sidebar-detail, editor-inspector, document window, or utility window. +4. Create the file/folder structure for app entrypoint, root layout, feature views, models, stores, services, and support helpers. +5. Build with small, focused subviews and explicit inputs rather than giant computed fragments. +6. Add keyboard shortcuts and menu or toolbar exposure for actions that matter on desktop. +7. Validate the flow with a build and a quick usability pass: multiwindow assumptions, settings entry points, and selection stability. + +## Component References + +Use `references/components-index.md` as the entry point. Each component reference should include: +- intent and best-fit scenarios +- minimal usage pattern with desktop conventions +- pitfalls and discoverability notes +- when to fall back to `appkit-interop` diff --git a/build-macos-apps/skills/swiftui-patterns/references/commands-menus.md b/build-macos-apps/skills/swiftui-patterns/references/commands-menus.md new file mode 100644 index 0000000..47fb3b7 --- /dev/null +++ b/build-macos-apps/skills/swiftui-patterns/references/commands-menus.md @@ -0,0 +1,41 @@ +# Commands and Menus + +## Intent + +Use this when mapping desktop actions into menu items, keyboard shortcuts, and focused scene behavior. + +## Core patterns + +- Add `commands` at the scene level. +- Use `CommandMenu` for app-specific actions. +- Use `CommandGroup` to insert, replace, or remove menu sections. +- Use `FocusedValue` or scene state to make commands context-sensitive. +- Pair important commands with keyboard shortcuts and visible toolbar or content affordances when appropriate. + +## Example + +```swift +@main +struct SampleApp: App { + var body: some Scene { + WindowGroup { + EditorRootView() + } + .commands { + CommandMenu("Document") { + Button("New Note") { /* create */ } + .keyboardShortcut("n") + + Button("Toggle Inspector") { /* toggle */ } + .keyboardShortcut("i", modifiers: [.command, .option]) + } + } + } +} +``` + +## Pitfalls + +- Do not register the same shortcut in multiple places. +- Do not make commands the only discoverable path for a critical action. +- If you need responder-chain validation, custom menu item state, or AppKit-specific command behavior, use `appkit-interop`. diff --git a/build-macos-apps/skills/swiftui-patterns/references/components-index.md b/build-macos-apps/skills/swiftui-patterns/references/components-index.md new file mode 100644 index 0000000..0fe2432 --- /dev/null +++ b/build-macos-apps/skills/swiftui-patterns/references/components-index.md @@ -0,0 +1,16 @@ +# Components Index + +Use this file to find scene and component guidance. Each entry lists when to use it. + +## Available references + +- Windowing: `references/windowing.md` — Use when choosing between `WindowGroup`, `Window`, `DocumentGroup`, or window-opening APIs. +- Settings: `references/settings.md` — Use for dedicated settings scenes, preference storage, and settings entry points. +- Commands and menus: `references/commands-menus.md` — Use for menu items, keyboard shortcuts, focused actions, and command routing. +- Split views and inspectors: `references/split-inspectors.md` — Use for sidebar-detail apps, inspectors, and selection-driven desktop layouts. +- Menu bar extras: `references/menu-bar-extra.md` — Use when the app belongs primarily in the menu bar. + +## Adding entries + +- Add a new reference file when a macOS-specific pattern comes up repeatedly. +- Keep each reference short, actionable, and explicit about when SwiftUI is enough versus when AppKit interop is warranted. diff --git a/build-macos-apps/skills/swiftui-patterns/references/menu-bar-extra.md b/build-macos-apps/skills/swiftui-patterns/references/menu-bar-extra.md new file mode 100644 index 0000000..864c68a --- /dev/null +++ b/build-macos-apps/skills/swiftui-patterns/references/menu-bar-extra.md @@ -0,0 +1,62 @@ +# Menu Bar Extra + +## Intent + +Use this when the app primarily lives in the macOS menu bar instead of a traditional always-open window. + +## Core patterns + +- Use `MenuBarExtra` for lightweight utilities, status indicators, and quick actions. +- If the app also has a primary main window that should appear at launch, define that scene with `WindowGroup(..., id:)` and use `Window(...)` only for auxiliary/on-demand windows. +- If the menu bar app should still show in the Dock and activate like a normal app, install an app delegate with `@NSApplicationDelegateAdaptor`, call `NSApp.setActivationPolicy(.regular)` during launch, and then `NSApp.activate(ignoringOtherApps: true)`. +- If the app is intentionally menu-bar-only, explicitly document that `.accessory` / no-Dock behavior is expected product behavior rather than a launch bug. +- Keep the menu content concise and action-oriented. +- Keep each visible menu item label to 30 characters or fewer. If the backing content can be longer than that, derive a short display title and open the full text in a separate window or detail pane. +- If the app has deeper workflows, open a dedicated window from the menu bar extra rather than cramming everything into the menu. + +## Example + +```swift +import AppKit + +private func shortMenuTitle(_ title: String) -> String { + if title.count <= 30 { + return title + } + return String(title.prefix(27)) + "..." +} + +final class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + NSApp.setActivationPolicy(.regular) + NSApp.activate(ignoringOtherApps: true) + } +} + +@main +struct SampleApp: App { + @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate + + var body: some Scene { + WindowGroup("Sample", id: "main") { + ContentView() + } + + MenuBarExtra("Sample", systemImage: "bolt.circle") { + Button(shortMenuTitle("Open Dashboard")) { /* open window */ } + Divider() + Button("Quit") { + NSApplication.shared.terminate(nil) + } + } + } +} +``` + +## Pitfalls + +- Do not rely on a `Window(...)` scene alone for the main launch window in a menu-bar-plus-window app when the product expects a regular window at startup. +- Do not silently ship a no-Dock menu-bar-only app if the user expects a normal app process. Either install the app delegate and switch to `.regular`, or clearly document that `.accessory` behavior is intentional. +- Do not turn the menu bar extra into a tiny, overloaded substitute for a full app window. +- Do not render raw unbounded titles or message bodies as menu items. Long labels quickly blow out the menu width and should be capped to 30 characters with a short display title. +- If the extra needs advanced status item customization or AppKit menu control, use `appkit-interop`. diff --git a/build-macos-apps/skills/swiftui-patterns/references/settings.md b/build-macos-apps/skills/swiftui-patterns/references/settings.md new file mode 100644 index 0000000..89813bd --- /dev/null +++ b/build-macos-apps/skills/swiftui-patterns/references/settings.md @@ -0,0 +1,51 @@ +# Settings + +## Intent + +Use this when building a native macOS settings window with SwiftUI. + +## Core patterns + +- Declare a dedicated `Settings` scene in the app. +- Keep settings content in a separate root view. +- Use `@AppStorage` for user preferences that should persist. +- Prefer tabs, sections, or a split settings layout over deep push navigation. +- Use `SettingsLink` or `OpenSettingsAction` for in-app entry points. + +## Example + +```swift +@main +struct SampleApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + + Settings { + SettingsView() + } + } +} + +struct SettingsView: View { + @AppStorage("showSidebarIcons") private var showSidebarIcons = true + + var body: some View { + TabView { + Form { + Toggle("Show Sidebar Icons", isOn: $showSidebarIcons) + } + .tabItem { Label("General", systemImage: "gearshape") } + } + .frame(width: 460, height: 260) + .scenePadding() + } +} +``` + +## Pitfalls + +- Do not reuse an iOS full-screen settings screen unless the app really is a direct Catalyst-style port. +- Keep settings rows simple and accessible. +- If settings require custom panels, responders, or first-responder integration, use `appkit-interop`. diff --git a/build-macos-apps/skills/swiftui-patterns/references/split-inspectors.md b/build-macos-apps/skills/swiftui-patterns/references/split-inspectors.md new file mode 100644 index 0000000..98563ed --- /dev/null +++ b/build-macos-apps/skills/swiftui-patterns/references/split-inspectors.md @@ -0,0 +1,111 @@ +# Split Views and Inspectors + +## Intent + +Use this when the app benefits from a stable sidebar-detail layout, optional supplementary content, or an inspector panel. + +## Core patterns + +- Prefer explicit selection state over push-only navigation. +- Start with `NavigationSplitView` when the layout matches the system mental model. +- Use a manual split only when you need unusual sizing or an always-visible custom column. +- Use `inspector(isPresented:)` for lightweight detail controls that complement the main content. + +## Example: sidebar + detail + +```swift +struct LibraryRootView: View { + @State private var selection: Item.ID? + @State private var showInspector = false + + var body: some View { + NavigationSplitView { + SidebarList(selection: $selection) + } detail: { + DetailView(selection: selection) + .inspector(isPresented: $showInspector) { + InspectorView(selection: selection) + } + } + } +} +``` + +## Example: native sidebar row + +Prefer a native source-list row shape: + +```swift +List(selection: $selection) { + ForEach(items) { item in + HStack(spacing: 10) { + Image(systemName: item.systemImage) + .foregroundStyle(.secondary) + .frame(width: 16) + + VStack(alignment: .leading, spacing: 2) { + Text(item.title) + .lineLimit(1) + + if let detail = item.detail { + Text(detail) + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } + } + .tag(item.id) + } +} +.listStyle(.sidebar) +``` + +Keep each row to one icon and one or two text lines. Put richer metadata in the +detail or inspector content instead of every sidebar row. + +## Example: split-view backgrounds + +Let the sidebar and split container keep system backgrounds while detail content +owns custom surfaces: + +```swift +NavigationSplitView { + List(selection: $selection) { + ForEach(items) { item in + Label(item.title, systemImage: item.systemImage) + .tag(item.id) + } + } + .listStyle(.sidebar) +} detail: { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + DetailSummaryCard(item: selectedItem) + DetailMetricsCard(item: selectedItem) + } + .padding() + } +} +``` + +Avoid opaque sidebar and root split-pane fills by default: + +```swift +NavigationSplitView { + List(items) { item in + SidebarCardRow(item: item) + } + .listStyle(.sidebar) + .background(Color(nsColor: .windowBackgroundColor)) +} detail: { + DetailView(item: selectedItem) + .background(Color(.white)) +} +``` + +## Pitfalls + +- Avoid swapping the whole root layout with top-level conditionals when selection changes. +- Avoid hiding too much detail behind modal sheets when an inspector or secondary column would fit better. +- If the layout requires AppKit split view delegation or advanced window coordination, use `appkit-interop`. diff --git a/build-macos-apps/skills/swiftui-patterns/references/windowing.md b/build-macos-apps/skills/swiftui-patterns/references/windowing.md new file mode 100644 index 0000000..1b34da1 --- /dev/null +++ b/build-macos-apps/skills/swiftui-patterns/references/windowing.md @@ -0,0 +1,46 @@ +# Windowing + +## Intent + +Use this when choosing the top-level scene model for a native macOS app. + +## Choose the scene type deliberately + +- Use `WindowGroup(..., id:)` for the primary app window when it should appear at launch, especially in apps that also have a `MenuBarExtra`. +- Use `WindowGroup` for any scene that can have multiple independent instances. +- Use `Window` for singleton utility windows or focused secondary surfaces. In menu-bar-heavy apps, `Window(...)` is better for auxiliary/on-demand windows and may not present the main window automatically at launch. +- Use `Settings` for preferences. Do not bury settings inside the main content flow. +- Use `DocumentGroup` when the app is fundamentally document-driven. + +## Example: main app plus utility window + +```swift +@main +struct SampleApp: App { + var body: some Scene { + WindowGroup("Library", id: "library") { + LibraryRootView() + } + + Window("Inspector", id: "inspector") { + InspectorRootView() + } + + Settings { + SettingsView() + } + } +} +``` + +## Opening windows + +- Use `openWindow(id:)` when a command, toolbar item, or button should open another scene. +- Keep per-window state in the scene or `@SceneStorage`, not in a single global pile. + +## Pitfalls + +- Avoid modeling every feature as a pushed destination inside one window. +- Do not use only `Window(...)` for the main launch window in a menu-bar-plus-window app unless you have verified the launch behavior and intentionally want an on-demand auxiliary window. +- Avoid singleton state for window-specific selections or drafts. +- If you need lower-level titlebar, tabbing, or window lifecycle control, switch to `appkit-interop`. diff --git a/build-macos-apps/skills/telemetry/SKILL.md b/build-macos-apps/skills/telemetry/SKILL.md new file mode 100644 index 0000000..c1ba13a --- /dev/null +++ b/build-macos-apps/skills/telemetry/SKILL.md @@ -0,0 +1,86 @@ +--- +name: telemetry +description: Add and verify lightweight macOS runtime telemetry. Use when wiring Logger events or inspecting logs for windows, sidebars, menus, and actions. +--- + +# Telemetry + +## Quick Start + +Use this skill to add lightweight app instrumentation that helps debug behavior +without turning the codebase into a logging landfill. Prefer Apple's unified +logging APIs and verify the events after a build/run loop. + +## Core Guidelines + +- Prefer `Logger` from the `OSLog` framework for structured app logs. +- Give each feature a clear subsystem/category pair so runtime filtering stays easy. +- Log meaningful user and app lifecycle events: window opening, sidebar selection changes, menu commands, menu bar extra actions, sync/load milestones, and unexpected fallback paths. +- Keep info logs concise and stable. Use debug logs for noisy state details. +- Do not log secrets, auth tokens, personal data, or raw document contents. +- Add signposts only when measuring timing or performance spans; do not overinstrument by default. + +## Minimal Logger Pattern + +```swift +import OSLog + +private let logger = Logger( + subsystem: Bundle.main.bundleIdentifier ?? "SampleApp", + category: "Sidebar" +) + +@MainActor +func selectItem(_ item: SidebarItem) { + logger.info("Selected sidebar item: \(item.id, privacy: .public)") + selection = item.id +} +``` + +Use feature-specific categories like `Windowing`, `Commands`, `MenuBar`, `Sidebar`, +`Sync`, or `Import` so logs can be filtered quickly. + +## Workflow + +1. Identify the behavior that needs observability. + - Window open/close + - Sidebar or inspector selection changes + - Menu or keyboard command actions + - Menu bar extra actions + - Background load/sync/import events + - Error and recovery paths + +2. Add the smallest useful instrumentation. + - Create one `Logger` per feature area or type. + - Log action boundaries and key state transitions. + - Prefer one high-signal line per user action over noisy value dumps. + +3. Build and run the app. + - Use `build-run-debug` for the build/run loop. + - If `script/build_and_run.sh` exists, prefer `./script/build_and_run.sh --telemetry` for live telemetry checks or `./script/build_and_run.sh --logs` for broader process logs. + - Exercise the UI or command path that should emit telemetry. + +4. Read runtime logs and verify the event fired. + - Use Console.app with a process/subsystem filter when that is the fastest manual check. + - Use `log stream --style compact --predicate 'process == "AppName"'` for live terminal verification. + - Prefer tighter predicates when you know the subsystem/category: + `log stream --style compact --predicate 'subsystem == "com.example.app" && category == "Sidebar"'` + +5. Tighten or remove instrumentation. + - If the event fires, keep only the logs that remain useful for future debugging. + - If it does not fire, move the log closer to the suspected control path and rerun. + +## Verification Checklist + +- The app builds after telemetry changes. +- The relevant action emits exactly one clear log line or a small bounded sequence. +- The log can be filtered by process, subsystem, or category. +- No sensitive payloads are written to unified logs. +- Noisy temporary debug logs are removed or demoted before finishing. + +## Guardrails + +- Do not use `print` as the primary app telemetry mechanism for macOS app code. +- Do not leave a dense trail of permanent debug logs around every state mutation. +- Do not claim an event is wired correctly until you have a concrete verification path through Console, `log stream`, or captured process output. +- If the debugging task is mostly about crash/backtrace analysis rather than action telemetry, switch to `build-run-debug`. diff --git a/build-macos-apps/skills/test-triage/SKILL.md b/build-macos-apps/skills/test-triage/SKILL.md new file mode 100644 index 0000000..3b303e2 --- /dev/null +++ b/build-macos-apps/skills/test-triage/SKILL.md @@ -0,0 +1,54 @@ +--- +name: test-triage +description: Triage macOS tests across Xcode and SwiftPM. Use when narrowing failures, explaining assertions or crashes, or separating setup from regressions. +--- + +# Test Triage + +## Quick Start + +Use this skill to run the smallest meaningful test scope first, classify +failures precisely, and avoid treating every test failure like a product bug. + +## Workflow + +1. Detect the test harness. + - Use `xcodebuild test` for Xcode-based projects. + - Use `swift test` for SwiftPM packages. + +2. Narrow the scope. + - If the user gave a target, product, or test filter, use it. + - If not, prefer the smallest likely failing target before a full suite. + +3. Classify the result. + - Build failure + - Assertion failure + - Crash or signal + - Async timing or flake + - Environment or fixture setup issue + - Missing entitlement or host app issue + +4. Rerun intelligently. + - Use focused reruns when a specific case fails. + - Avoid burning time on full-suite reruns without new information. + +5. Summarize clearly. + - What command ran + - Which tests failed + - What kind of failure it was + - The best next proof step or fix path + +## Guardrails + +- Distinguish compilation failures from test execution failures. +- Call out when a test appears to assume iOS-only or simulator-only behavior. +- Mark likely flakes as such instead of overstating confidence. + +## Output Expectations + +Provide: +- the command used +- the smallest failing scope +- the top failure category +- a concise explanation of the likely cause +- the next rerun or fix step diff --git a/build-macos-apps/skills/view-refactor/SKILL.md b/build-macos-apps/skills/view-refactor/SKILL.md new file mode 100644 index 0000000..89ccaf7 --- /dev/null +++ b/build-macos-apps/skills/view-refactor/SKILL.md @@ -0,0 +1,109 @@ +--- +name: view-refactor +description: Refactor macOS SwiftUI views and scenes into stable structure. Use when splitting large views, tightening scene state, or narrowing AppKit escapes. +--- + +# View Refactor + +## Overview + +Refactor macOS views toward small, explicit, stable scene and view types. Default +to native SwiftUI for layout, selection, commands, and settings. Reach for AppKit +only at the narrow edges where desktop behavior truly requires it. + +## Core Guidelines + +### 1) Model scenes explicitly + +- Break the app into meaningful scene roots: main window, settings, utility windows, inspectors, or menu bar extras. +- Do not let one giant root view silently own every desktop surface. + +### 2) Keep a predictable file shape + +- Follow this ordering unless the file already has a stronger local convention: +- Environment +- `private`/`public` `let` +- `@State` / other stored properties +- computed `var` (non-view) +- `init` +- `body` +- computed view builders / other view helpers +- helper / async functions + +### 2b) Split files by responsibility + +- For non-trivial apps, do not keep the full app, all views, models, stores, networking clients, process clients, and helpers in one Swift file. +- Accept a single Swift file only for tiny throwaway examples or snippets: roughly under 50 lines, one screen, no persistence, no networking/process client, and no reusable models. +- Use `App/App.swift` for the `@main` app and `AppDelegate` only. +- Keep `Views/ContentView.swift` focused on root layout and composition; move feature UI into files such as `Views/SidebarView.swift`, `Views/DetailView.swift`, and `Views/ComposerView.swift`. +- Move value types and selection enums into `Models/*.swift`, stores into `Stores/*.swift`, app-server/network/process clients into `Services/*.swift`, and small formatters/resolvers/extensions into `Support/*.swift`. +- Keep files small and named after the primary type they contain. + +### 3) Prefer dedicated subview types over many computed `some View` fragments + +- Extract meaningful desktop sections like sidebar rows, detail panels, inspectors, or toolbar content into focused subviews. +- Keep computed `some View` helpers small and rare. +- Pass explicit data, bindings, and actions into subviews instead of handing down the whole scene model. + +### 4) Keep selection and layout stable + +- Prefer one stable split or window layout with local conditionals inside it. +- Avoid top-level branch swapping between radically different roots when selection changes. +- Let the layout be constant; let state drive the content inside it. + +### 5) Extract commands, toolbars, and actions out of `body` + +- Do not bury non-trivial button logic inline. +- Do not mix command routing, menu state, and layout in the same block if they can be named clearly. +- Keep `body` readable as UI, not as a desktop view controller. + +### 6) Use scene and app storage intentionally + +- Use `@SceneStorage` for per-window ephemeral state when it truly helps restore the scene. +- Use `@AppStorage` for durable preferences, not transient UI toggles that only matter in one window. +- Keep scene-owned state close to the scene root. + +### 7) Keep AppKit escape hatches narrow + +- If a representable or `NSWindow` bridge exists, isolate it behind a small wrapper or helper. +- Do not let AppKit references spread through unrelated SwiftUI views. +- If the bridge starts owning the feature, re-evaluate the architecture. + +### 8) Observation usage + +- For `@Observable` reference types on modern macOS targets, store them as `@State` in the owning view. +- Pass observables explicitly to children. +- On older deployment targets, fall back to `@StateObject` and `@ObservedObject` where needed. + +## Workflow + +1. Identify the current scene boundary and whether the file is trying to do too much. +2. Reorder the file into a predictable top-to-bottom structure. +3. Extract desktop-specific sections into dedicated subview types. +4. Stabilize the root layout around selection, scenes, and commands rather than top-level branching. +5. Move action logic, command routing, and toolbar behavior into named helpers or separate types. +6. Tighten any AppKit bridge so the imperative edge is small and explicit. +7. Keep behavior intact unless the request explicitly asks for structural and behavioral changes together. + +## Refactor Checklist + +- Split oversized view files before adding more UI. +- Move pure models, identifiers, and selection enums out of view files. +- Move `Process`, `URLSession`, app-server, and platform client code out of SwiftUI views into `Services/`. +- Keep `AppDelegate` and the `@main` app entrypoint minimal. +- Build after each major split so compile errors stay local. + +## Common Smells + +- A root view that mixes window scaffolding, settings, toolbar code, command handling, and detail layout. +- A single app file that mixes app entrypoint, root layout, feature views, models, stores, service clients, and support extensions. +- iOS-style push navigation forced into a Mac sidebar-detail problem. +- Several booleans for mutually exclusive inspectors, sheets, or utility windows. +- AppKit objects passed through many SwiftUI layers without a clear ownership reason. +- Large computed view fragments standing in for real subviews. + +## Notes + +- A good macOS refactor should make scene structure, selection flow, and command ownership obvious. +- When the problem is fundamentally a missing desktop pattern, use `swiftui-patterns`. +- When the problem is fundamentally a boundary with AppKit, use `appkit-interop`. diff --git a/build-macos-apps/skills/window-management/SKILL.md b/build-macos-apps/skills/window-management/SKILL.md new file mode 100644 index 0000000..a5a66a4 --- /dev/null +++ b/build-macos-apps/skills/window-management/SKILL.md @@ -0,0 +1,170 @@ +--- +name: window-management +description: Customize macOS SwiftUI windows and scene behavior. Use when tuning window chrome, drag regions, placement, restoration, launch behavior, or borderless windows. +--- + +# Window Management + +## Overview + +Use this skill to tailor each SwiftUI window to its job. Start by identifying +which scene owns the window (`Window`, `WindowGroup`, or a dedicated utility +scene), then customize the toolbar/title area, background material, resize and +restoration behavior, and initial or zoomed placement. + +Prefer scene and window modifiers over ad hoc AppKit bridges when SwiftUI offers +the behavior directly. Keep each window purpose-built: a main browser window, an +About window, and a media player window usually want different chrome, +resizability, restoration, and placement rules. + +These APIs are macOS 15+ SwiftUI window/scene customizations. For older +deployment targets, expect to use more AppKit bridging or availability guards. + +## Workflow + +1. Inspect the relevant scene declaration and classify the window role: + main app navigation, inspector/detail utility, About/support window, media + playback window, welcome window, or a borderless custom surface. +2. Adjust toolbar and title presentation to match the content. +3. If the toolbar background or entire toolbar is hidden, make sure the window + still has a usable drag region. +4. Refine window behavior for that role: minimize availability, restoration, + resize expectations, and whether the window should appear at launch. +5. Set default placement for newly opened windows and ideal placement for zoom + behavior when content and display size matter. +6. Build and launch the app with `build-run-debug` to verify the result in + a real foreground `.app` bundle. +7. If SwiftUI scene/window modifiers are not enough, switch to `appkit-interop` + for a narrow `NSWindow` bridge rather than spreading AppKit through the view + tree. + +## Toolbar And Title + +- Use `.toolbar(removing: .title)` when the window title should stay associated + with the window for accessibility and menus, but not be visibly drawn in the + title bar. +- Use `.toolbarBackgroundVisibility(.hidden, for: .windowToolbar)` when large + media or hero content should visually extend to the top edge of the window. +- If the window still needs close/minimize/full-screen controls, remove only the + title and toolbar background. If the toolbar should disappear entirely, use + `.toolbarVisibility(.hidden, for: .windowToolbar)` instead. +- Remove custom toolbar backgrounds and manually painted titlebar fills before + layering new SwiftUI toolbar APIs on top. +- Keep the window's logical title meaningful even if hidden; the system can + still use it for accessibility and menu items. These are visual changes only. + +## Drag Regions + +- If a toolbar background is hidden or the toolbar is removed entirely, use + `WindowDragGesture()` to extend the draggable area into your content. +- Attach the gesture to a transparent overlay or non-interactive header region + that does not steal gestures from real controls. +- For a media player with custom playback controls, insert the drag overlay + between the video content and the controls so AVKit or transport controls keep + receiving input. +- Pair the drag gesture with `.allowsWindowActivationEvents(true)` so clicking + and immediately dragging a background window still activates and moves it. + +## Background And Materials + +- Use `.containerBackground(.thickMaterial, for: .window)` when a utility window + or About window should replace the default window background with a subtle + frosted material. +- Prefer system materials for stylized windows instead of hardcoded translucent + colors. +- Use this especially for fixed-content utility windows where a softer backdrop + is part of the design. + +## Window Behavior + +- Use `.windowMinimizeBehavior(.disabled)` for always-reachable utility windows + such as a custom About window where minimizing adds little value. +- Disable the green zoom control through fixed sizing or window constraints when + the window's content has one intended size. +- Use `.restorationBehavior(.disabled)` for windows that should not reopen on + next launch, such as About panels, transient support/info windows, or + first-run welcome surfaces. +- Keep state restoration enabled for primary document or navigation windows when + reopening prior size and position is desirable. +- By default, SwiftUI respects the user's system-wide macOS state restoration + setting. Use `restorationBehavior(...)` only when a specific window should + intentionally opt into or out of that system behavior. +- Use `.defaultLaunchBehavior(.presented)` for windows that should appear first + on launch, such as a welcome window, and choose that behavior intentionally + rather than relying on side effects from scene creation order. + +## Window Placement + +- Use `.defaultWindowPlacement { content, context in ... }` to control the + initial size and optional position of newly opened windows. +- Inside the placement closure, call `content.sizeThatFits(.unspecified)` to get + the content's ideal size. +- Read `context.defaultDisplay.visibleRect` to get the display's usable region + after accounting for the menu bar and Dock. +- Return `WindowPlacement(size: size)` with a size clamped to the visible rect + when media or document content may be larger than the display. If no position + is provided, the window is centered by default. +- Use `.windowIdealPlacement { content, context in ... }` to control what + happens when the user chooses Zoom from the Window menu or Option-clicks the + green toolbar button. For media windows, preserve aspect ratio and grow to the + largest size that fits the display. +- Treat default placement and ideal placement as separate policies: + - default placement controls where a new window first appears, + - ideal placement controls how large a zoomed window should become. +- Always consider external displays and rotated/narrow screens when sizing + player windows or document windows from content dimensions. + +## Borderless And Specialized Windows + +- Use `.windowStyle(.plain)` for borderless or highly custom chrome windows, but + make sure the content still provides a clear drag/move affordance and visible + context. +- For a borderless player, HUD, or welcome window, decide upfront whether losing + standard titlebar affordances is worth the custom presentation. +- Keep one clear path back to regular window management if the plain style makes + the window feel invisible or hard to move. + +For concrete window modifier examples, read `references/api-snippets.md`. + +## Review Checklist + +- The scene type matches the window's role and lifecycle. +- Hidden titles still leave a meaningful logical title for accessibility and + menus. +- Toolbar background removal is intentional and does not hurt titlebar legibility + or window control placement. +- Windows with hidden or removed toolbars still have a reliable drag region and + support click-then-drag activation from the background. +- Utility windows have restoration/minimize behavior that matches their purpose. +- Restoration overrides are used only when a scene should intentionally differ + from the user's system-wide setting. +- Default and ideal placement use `content.sizeThatFits(.unspecified)` and + `context.defaultDisplay.visibleRect` when content/display size matters. +- Media windows preserve aspect ratio and fit on small or rotated displays. +- Borderless windows still have a usable move/drag affordance. + +## Guardrails + +- Do not use `.toolbar(removing: .title)` just to hide a title you forgot to set. + Keep the underlying window title meaningful. +- Do not hide the toolbar background or the whole toolbar without replacing the + lost drag affordance. +- Do not disable restoration on the main document/navigation window unless the + user explicitly wants a fresh-start app every launch. +- Do not hardcode one monitor size or assume a single-display setup when sizing + player windows. +- Do not reach for `NSWindow` mutation before checking whether + `.windowMinimizeBehavior`, `.restorationBehavior`, `.defaultWindowPlacement`, + `.windowIdealPlacement`, `.windowStyle`, or `.defaultLaunchBehavior` already + solve the problem. +- Do not leave a plain borderless window without any obvious drag or close path. + +## When To Use Other Skills + +- Use `swiftui-patterns` for broader scene, commands, settings, sidebar, + and inspector architecture. +- Use `liquid-glass` when the main question is modern macOS visual treatment, + Liquid Glass, or system material adoption. +- Use `appkit-interop` if a custom window behavior truly requires `NSWindow`, + `NSPanel`, or responder-chain control. +- Use `build-run-debug` to launch and verify the resulting windows. diff --git a/build-macos-apps/skills/window-management/references/api-snippets.md b/build-macos-apps/skills/window-management/references/api-snippets.md new file mode 100644 index 0000000..5b2d060 --- /dev/null +++ b/build-macos-apps/skills/window-management/references/api-snippets.md @@ -0,0 +1,60 @@ +# Window API Snippets + +Use these examples after the window role and modifier choices are clear. + +```swift +WindowGroup("Destination Video") { + CatalogView() + .toolbar(removing: .title) + .toolbarBackgroundVisibility(.hidden, for: .windowToolbar) +} +``` + +```swift +Window("About", id: "about") { + AboutView() + .toolbar(removing: .title) + .toolbarBackgroundVisibility(.hidden, for: .windowToolbar) + .containerBackground(.thickMaterial, for: .window) +} +.windowMinimizeBehavior(.disabled) +.restorationBehavior(.disabled) +``` + +```swift +WindowGroup("Player", for: Video.self) { $video in + PlayerView(video: video) +} +.defaultWindowPlacement { content, context in + let idealSize = content.sizeThatFits(.unspecified) + let displayBounds = context.defaultDisplay.visibleRect + let fittedSize = clampToDisplay(idealSize, displayBounds: displayBounds) + return WindowPlacement(size: fittedSize) +} +.windowIdealPlacement { content, context in + let idealSize = content.sizeThatFits(.unspecified) + let displayBounds = context.defaultDisplay.visibleRect + let zoomedSize = zoomToFit(idealSize, displayBounds: displayBounds) + let position = centeredPosition(for: zoomedSize, in: displayBounds) + return WindowPlacement(position, size: zoomedSize) +} +``` + +```swift +PlayerView(video: video) + .overlay(alignment: .top) { + Color.clear + .frame(height: 48) + .contentShape(Rectangle()) + .gesture(WindowDragGesture()) + .allowsWindowActivationEvents(true) + } +``` + +```swift +Window("Welcome", id: "welcome") { + WelcomeView() +} +.windowStyle(.plain) +.defaultLaunchBehavior(.presented) +``` From 4ccfd54f10b5eddabf2ffef40f8c398b31abf334 Mon Sep 17 00:00:00 2001 From: duyetbot Date: Sun, 14 Jun 2026 00:28:46 +0700 Subject: [PATCH 2/2] style(build-macos-apps): satisfy super-linter (MD040, MD013, prettier) - Tag directory-tree fences as text (MD040) - Wrap three guideline lines over 400 chars (MD013) - Collapse codex capabilities array (JSON_PRETTIER) - Apply prettier formatting to 7 SKILL.md files (MARKDOWN_PRETTIER) Co-Authored-By: duyetbot --- build-macos-apps/.codex-plugin/plugin.json | 6 +--- build-macos-apps/README.md | 2 +- .../skills/appkit-interop/SKILL.md | 1 + .../skills/build-run-debug/SKILL.md | 1 + .../skills/packaging-notarization/SKILL.md | 1 + .../skills/signing-entitlements/SKILL.md | 1 + .../skills/swiftpm-macos/SKILL.md | 1 + .../skills/swiftui-patterns/SKILL.md | 30 +++++++++++-------- build-macos-apps/skills/test-triage/SKILL.md | 1 + 9 files changed, 25 insertions(+), 19 deletions(-) diff --git a/build-macos-apps/.codex-plugin/plugin.json b/build-macos-apps/.codex-plugin/plugin.json index 1c85485..63e37a9 100644 --- a/build-macos-apps/.codex-plugin/plugin.json +++ b/build-macos-apps/.codex-plugin/plugin.json @@ -13,11 +13,7 @@ "shortDescription": "Build, debug, instrument, and implement macOS apps with SwiftUI and AppKit guidance", "developerName": "duyet", "category": "development", - "capabilities": [ - "Skill", - "Command", - "Agent" - ], + "capabilities": ["Skill", "Command", "Agent"], "links": { "homepage": "https://github.com/duyet/codex-claude-plugins" } diff --git a/build-macos-apps/README.md b/build-macos-apps/README.md index f6fea27..21f4ffd 100644 --- a/build-macos-apps/README.md +++ b/build-macos-apps/README.md @@ -35,7 +35,7 @@ It currently includes these skills: ## Plugin Structure -``` +```text build-macos-apps/ ├── .claude-plugin/ # Claude manifest ├── .codex-plugin/ # Codex manifest diff --git a/build-macos-apps/skills/appkit-interop/SKILL.md b/build-macos-apps/skills/appkit-interop/SKILL.md index 76783ef..58e4907 100644 --- a/build-macos-apps/skills/appkit-interop/SKILL.md +++ b/build-macos-apps/skills/appkit-interop/SKILL.md @@ -62,6 +62,7 @@ the source of truth, while AppKit handles the imperative edge. ## Output Expectations Provide: + - the exact SwiftUI limitation being crossed - the smallest recommended bridge type - the data-flow boundary between SwiftUI and AppKit diff --git a/build-macos-apps/skills/build-run-debug/SKILL.md b/build-macos-apps/skills/build-run-debug/SKILL.md index d017921..ec488e6 100644 --- a/build-macos-apps/skills/build-run-debug/SKILL.md +++ b/build-macos-apps/skills/build-run-debug/SKILL.md @@ -121,6 +121,7 @@ simulator-specific workflows onto pure macOS tasks. ## Output Expectations Provide: + - the detected project type - the script path and Codex environment action you configured, if applicable - the command you ran diff --git a/build-macos-apps/skills/packaging-notarization/SKILL.md b/build-macos-apps/skills/packaging-notarization/SKILL.md index 13312f1..f6c044f 100644 --- a/build-macos-apps/skills/packaging-notarization/SKILL.md +++ b/build-macos-apps/skills/packaging-notarization/SKILL.md @@ -41,6 +41,7 @@ hardened runtime, or distribution validation. ## Output Expectations Provide: + - what artifact or settings were inspected - whether the app looks distribution-ready - the top missing prerequisite or failure mode diff --git a/build-macos-apps/skills/signing-entitlements/SKILL.md b/build-macos-apps/skills/signing-entitlements/SKILL.md index 02a6c66..72eff3f 100644 --- a/build-macos-apps/skills/signing-entitlements/SKILL.md +++ b/build-macos-apps/skills/signing-entitlements/SKILL.md @@ -52,6 +52,7 @@ hardened runtime confusion, or trust-policy rejection. ## Output Expectations Provide: + - what artifact was inspected - what signing state it is in - the exact failure class diff --git a/build-macos-apps/skills/swiftpm-macos/SKILL.md b/build-macos-apps/skills/swiftpm-macos/SKILL.md index a1e6c77..c047e7c 100644 --- a/build-macos-apps/skills/swiftpm-macos/SKILL.md +++ b/build-macos-apps/skills/swiftpm-macos/SKILL.md @@ -44,6 +44,7 @@ the fastest path to a reproducible result. ## Output Expectations Provide: + - the package products you found - the command you ran - whether build, run, or test succeeded diff --git a/build-macos-apps/skills/swiftui-patterns/SKILL.md b/build-macos-apps/skills/swiftui-patterns/SKILL.md index 1453b20..7dd66b5 100644 --- a/build-macos-apps/skills/swiftui-patterns/SKILL.md +++ b/build-macos-apps/skills/swiftui-patterns/SKILL.md @@ -61,13 +61,16 @@ Before writing the full UI: - Keep scenes explicit. A separate settings window, utility window, or menu bar extra should be modeled as its own scene, not hidden inside one monolithic `ContentView`. - Prefer system desktop affordances: `commands`, toolbars, sidebars, inspectors, contextual menus, and `searchable`. - For menu bar apps, keep `MenuBarExtra` item titles and action labels short and scannable. Cap visible menu item text at 30 characters; if source content is longer, truncate or summarize it before rendering and open the full content in a dedicated window or detail surface. -- If a `MenuBarExtra` app should still behave like a regular Dock app with a visible main window/process, install an `NSApplicationDelegate` via `@NSApplicationDelegateAdaptor`, call `NSApp.setActivationPolicy(.regular)` during launch, and activate the app with `NSApp.activate(ignoringOtherApps: true)`. If the app is intentionally menu-bar-only, document that `.accessory` / no-Dock behavior is a deliberate product choice. +- If a `MenuBarExtra` app should still behave like a regular Dock app with a visible main window/process, install an `NSApplicationDelegate` via `@NSApplicationDelegateAdaptor`, call `NSApp.setActivationPolicy(.regular)` during launch, and activate the app with `NSApp.activate(ignoringOtherApps: true)`. + If the app is intentionally menu-bar-only, document that `.accessory` / no-Dock behavior is a deliberate product choice. - Prefer system-adaptive colors, materials, and semantic foreground styles. Avoid fixed white/light backgrounds in scaffolding and examples unless the requested design explicitly calls for a custom non-adaptive theme. -- Do not paint `NavigationSplitView` sidebars or root window panes with opaque custom `Color(...)` or `Color(nsColor: .windowBackgroundColor)` fills by default. Prefer native macOS sidebar/window materials and system-provided backgrounds unless the user explicitly asks for a custom opaque surface. In sidebar-detail-inspector layouts, let the sidebar keep the standard source-list/material appearance and reserve custom backgrounds for detail or inspector content cards where needed. +- Do not paint `NavigationSplitView` sidebars or root window panes with opaque custom `Color(...)` or `Color(nsColor: .windowBackgroundColor)` fills by default. Prefer native macOS sidebar/window materials and system-provided backgrounds unless the user explicitly asks for a custom opaque surface. + In sidebar-detail-inspector layouts, let the sidebar keep the standard source-list/material appearance and reserve custom backgrounds for detail or inspector content cards where needed. - Use `@SceneStorage` for per-window ephemeral state and `@AppStorage` for durable user preferences. - Keep selection state explicit and stable. macOS layouts often pivot around sidebar selection rather than push navigation. - Prefer `NavigationSplitView` or a deliberate manual split layout over iOS-style stacked flows when the app benefits from always-visible structure. -- For `List(...).listStyle(.sidebar)` and `NavigationSplitView` sidebars, prefer flat native rows with standard system selection/highlight behavior. Keep rows visually lightweight and Mail-like: at most one leading icon, one strong title line, and one optional secondary detail line in `.secondary`. Avoid stacked metadata rows, repeated inline utility icons, or dense multi-column status text in the sidebar. Reserve card-style and metadata-heavy surfaces for detail or inspector panes unless the user explicitly asks for a highly custom sidebar treatment. +- For `List(...).listStyle(.sidebar)` and `NavigationSplitView` sidebars, prefer flat native rows with standard system selection/highlight behavior. Keep rows visually lightweight and Mail-like: at most one leading icon, one strong title line, and one optional secondary detail line in `.secondary`. + Avoid stacked metadata rows, repeated inline utility icons, or dense multi-column status text in the sidebar. Reserve card-style and metadata-heavy surfaces for detail or inspector panes unless the user explicitly asks for a highly custom sidebar treatment. - Keep primary actions discoverable from both UI chrome and keyboard shortcuts when appropriate. - Use SwiftUI-native scenes and views first. If you need low-level window, responder-chain, text system, or panel control, switch to `appkit-interop`. @@ -78,16 +81,16 @@ For concrete sidebar row and split-view background examples, read Use the narrowest state tool that matches the ownership model: -| Scenario | Preferred pattern | -| --- | --- | -| Local view or control state | `@State` | -| Child mutates parent-owned value state | `@Binding` | -| Root-owned reference model on macOS 14+ | `@State` with an `@Observable` type | -| Child reads or mutates an injected `@Observable` model | Pass it explicitly as a stored property | -| Window-scoped ephemeral selection or expansion state | `@SceneStorage` when practical, otherwise scene-owned `@State` | -| Shared user preference | `@AppStorage` | -| Shared app service or configuration | `@Environment(Type.self)` | -| Legacy reference model on older targets | `@StateObject` at the owner and `@ObservedObject` when injected | +| Scenario | Preferred pattern | +| ------------------------------------------------------ | --------------------------------------------------------------- | +| Local view or control state | `@State` | +| Child mutates parent-owned value state | `@Binding` | +| Root-owned reference model on macOS 14+ | `@State` with an `@Observable` type | +| Child reads or mutates an injected `@Observable` model | Pass it explicitly as a stored property | +| Window-scoped ephemeral selection or expansion state | `@SceneStorage` when practical, otherwise scene-owned `@State` | +| Shared user preference | `@AppStorage` | +| Shared app service or configuration | `@Environment(Type.self)` | +| Legacy reference model on older targets | `@StateObject` at the owner and `@ObservedObject` when injected | Choose the ownership location first, then the wrapper. Do not turn simple desktop state into a view model by reflex. @@ -129,6 +132,7 @@ Choose the ownership location first, then the wrapper. Do not turn simple deskto ## Component References Use `references/components-index.md` as the entry point. Each component reference should include: + - intent and best-fit scenarios - minimal usage pattern with desktop conventions - pitfalls and discoverability notes diff --git a/build-macos-apps/skills/test-triage/SKILL.md b/build-macos-apps/skills/test-triage/SKILL.md index 3b303e2..7deab0c 100644 --- a/build-macos-apps/skills/test-triage/SKILL.md +++ b/build-macos-apps/skills/test-triage/SKILL.md @@ -47,6 +47,7 @@ failures precisely, and avoid treating every test failure like a product bug. ## Output Expectations Provide: + - the command used - the smallest failing scope - the top failure category