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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions build-macos-apps/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
}
21 changes: 21 additions & 0 deletions build-macos-apps/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"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"
}
}
}
65 changes: 65 additions & 0 deletions build-macos-apps/README.md
Original file line number Diff line number Diff line change
@@ -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

```text
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
6 changes: 6 additions & 0 deletions build-macos-apps/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -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"
Comment on lines +4 to +5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include the icon assets referenced by agent metadata

This metadata points at two plugin-local icon files, but the commit does not add an assets/ directory or either referenced file. Any surface that reads this metadata will show broken icons or fail asset resolution for the new plugin, so either include the assets or remove these references until they exist.

Useful? React with 👍 / 👎.

default_prompt: "Build/refactor native macOS UI, inspect AppKit interop, add telemetry, or debug a macOS app."
30 changes: 30 additions & 0 deletions build-macos-apps/commands/build-and-run-macos-app.md
Original file line number Diff line number Diff line change
@@ -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.
22 changes: 22 additions & 0 deletions build-macos-apps/commands/fix-codesign-error.md
Original file line number Diff line number Diff line change
@@ -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.
23 changes: 23 additions & 0 deletions build-macos-apps/commands/test-macos-app.md
Original file line number Diff line number Diff line change
@@ -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.
69 changes: 69 additions & 0 deletions build-macos-apps/skills/appkit-interop/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
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
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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
}
Comment on lines +22 to +28

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Programmatically setting up an NSTextView inside an NSScrollView without configuring resizing masks and container tracking properties often leads to layout issues where the text view does not wrap text or fill the scroll view properly. It is highly recommended to configure the text view's vertical resizability, autoresizing mask, and text container width tracking.

Suggested change
func makeNSView(context: Context) -> NSScrollView {
let scrollView = NSScrollView()
let textView = NSTextView()
textView.delegate = context.coordinator
scrollView.documentView = textView
return scrollView
}
func makeNSView(context: Context) -> NSScrollView {
let scrollView = NSScrollView()
scrollView.hasVerticalScroller = true
let textView = NSTextView()
textView.isVerticallyResizable = true
textView.autoresizingMask = [.width]
textView.textContainer?.containerSize = NSSize(width: scrollView.contentSize.width, height: CGFloat.greatestMagnitude)
textView.textContainer?.widthTracksTextView = true
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<String>) {
_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.
Original file line number Diff line number Diff line change
@@ -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.
37 changes: 37 additions & 0 deletions build-macos-apps/skills/appkit-interop/references/window-panels.md
Original file line number Diff line number Diff line change
@@ -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`.
Loading
Loading