场景 011 · Codex Use Case

构建原生 Mac 应用框架

使用 Codex 和 Build macOS Apps 插件,把应用想法转成桌面原生的 NavigationSplitView 应用,保持侧栏选择稳定,加入菜单、工具栏和快捷键,并将偏好设置放入独立的 Settings 场景。

macOSCode
Build a Mac app shellmacOSCode

原文核对:2026-10-13 · 中文正文 · 英文提示词与演示

场景定位

使用 Codex 和 Build macOS Apps 插件,把应用想法转成桌面原生的 NavigationSplitView 应用,保持侧栏选择稳定,加入菜单、工具栏和快捷键,并将偏好设置放入独立的 Settings 场景。

难度
高级
时间跨度
1 小时

适合用于

  • 需要持久导航、菜单、工具栏和快捷键的新 Mac 应用,或从 iPad、网页概念转向桌面的产品。
  • 由侧栏选择驱动详情区、通过检查器展示次要元数据或操作的编辑器、资料库、管理或审阅工具。
  • 希望把设置放在独立偏好窗口,而不是在主内容栈中再推入一个页面的 Mac 应用。

相关工具

Build macOS Apps

使用 macOS SwiftUI 模式、窗口管理、AppKit 互操作和构建运行技能,创建侧栏—详情—检查器布局,连接菜单和设置,并优先通过命令行构建运行循环验证应用。

Build macOS Apps ↗

起步提示词

英文原文 · 可复制后按实际材料修改

Use the Build macOS Apps plugin to turn [describe your app idea] into a Mac-native SwiftUI app shell with a sidebar, detail pane, inspector, commands, and Settings.

Constraints:
- Choose the scene model first. Prefer `WindowGroup` for the main window and add a dedicated `Settings` scene for preferences.
- Build the main UI around `NavigationSplitView` with explicit selection state, a native `.sidebar` list, a detail surface, and an `inspector(isPresented:)` panel for secondary metadata or controls.
- Keep sidebar rows lightweight and native: one icon, one title line, and at most one short secondary line. Do not wrap every row in large custom cards unless there is a strong product reason.
- Expose important actions through scene-level `commands`, `CommandMenu`, toolbar buttons, and keyboard shortcuts. Do not hide the only path to a critical action behind gestures.
- Use `@SceneStorage` for window-scoped UI state, `@AppStorage` for preferences, and explicit parent-owned selection bindings for sidebar/detail coordination.
- Prefer system materials, semantic colors, and standard sidebar backgrounds. Add custom styling only to detail or inspector content cards when needed.
- Use a narrow AppKit bridge only if SwiftUI cannot express one specific desktop behavior cleanly.
- Create or update `script/build_and_run.sh`, run the smallest useful build/run check, and tell me the exact commands you used.

Deliver:
- the scene structure and main sidebar/detail/inspector views
- the menu, toolbar, and keyboard shortcut wiring
- the Settings scene and preference state model
- any AppKit bridge you added and why it was necessary
- the build/run validation steps and any desktop UX follow-up you recommend

从 Mac 场景模型开始

本场景用于把想法转成适合桌面的 Mac 应用框架。先让 Codex 选择场景模型,再围绕稳定的侧栏选择、详情区域和用于次要控制或元数据的检查器设计主窗口。

A Mac-native sidebar and detail app shell with a selected item in the sidebar and content in the detail pane

希望 Codex 应用这种桌面结构,并优先在命令行中构建和运行时,可以使用 Build macOS Apps 插件。其中的 macOS SwiftUI patterns 技能适用于场景、侧栏、检查器、命令和设置设计;当 SwiftUI 无法完全满足某项 Mac 专属行为时,还能添加小范围 AppKit 桥接。

构建侧栏、详情区和检查器

当功能需要持久导航和稳定的选中项时,优先使用 NavigationSplitView。侧栏行保持原生和轻量,使用系统背景;自定义卡片和密集元数据放在详情区或检查器。

官方代码示例
struct LibraryRootView: View {
  @SceneStorage("LibraryRootView.selection") private var selection: Item.ID?
  @SceneStorage("LibraryRootView.showInspector") private var showInspector = true

  var body: some View {
    NavigationSplitView {
      List(selection: $selection) {
        ForEach(items) { item in
          Label(item.title, systemImage: item.systemImage)
            .tag(item.id)
        }
      }
      .listStyle(.sidebar)
      .navigationTitle("Library")
    } detail: {
      ItemDetailView(selection: selection)
        .inspector(isPresented: $showInspector) {
          ItemInspectorView(selection: selection)
        }
    }
  }
}

如果需要特殊的分栏尺寸、底层窗口协调或自定义响应链行为,让 Codex 保留 SwiftUI 框架,只为这一缺口添加必要的最小 AppKit 桥接。

在桌面层设置命令、工具栏和快捷键

Mac 用户应能从菜单栏、工具栏和快捷键发现重要操作。让 Codex 围绕相同的应用操作连接场景级命令、随上下文变化的菜单项和工具栏按钮,避免只能通过手势寻找控制项。

官方代码示例
@main
struct LibraryApp: App {
  var body: some Scene {
    WindowGroup {
      LibraryRootView()
    }
    .commands {
      CommandMenu("Library") {
        Button("New Item") {
          // Create a new item.
        }
        .keyboardShortcut("n")

        Button("Toggle Inspector") {
          // Route this command to the focused window or selected item state.
        }
        .keyboardShortcut("i", modifiers: [.command, .option])
      }
    }

    Settings {
      LibrarySettingsView()
    }
  }
}

命令需要作用于当前详情项时,使用 FocusedValue、场景状态或显式选择状态。如果多个位置注册了同一快捷键,让 Codex 统一归属,确保命令路径清晰。

将偏好设置放在 Settings 中

应用偏好使用独立的 Settings 场景,通过 @AppStorage 持久保存用户选择。这通常比在主内容窗口内推入设置页面更符合 Mac 使用方式。

官方代码示例
struct LibrarySettingsView: View {
  @AppStorage("showItemMetadata") private var showItemMetadata = true

  var body: some View {
    TabView {
      Form {
        Toggle("Show Item Metadata", isOn: $showItemMetadata)
      }
      .tabItem { Label("General", systemImage: "gearshape") }
    }
    .frame(width: 460, height: 260)
    .scenePadding()
  }
}

先描述应用概念,再验证框架

提示词应说明应用概念、主要内容对象和核心操作,让 Codex 先围绕该流程构建桌面框架。要求执行一次小规模构建运行检查,并总结场景结构、命令连接、状态归属和所用的 AppKit 桥接。

实用建议

保持侧栏原生

侧栏行使用一个图标、一行标题,最多再加一行简短辅助文字。更丰富的卡片、计数和元数据移到详情区或检查器,让来源列表易于浏览。

避免把设置藏在主内容栈中

如果偏好影响整个应用,让 Codex 将控制项放入 Settings,通过 @AppStorage 保存,并从应用菜单提供入口,而不是再推入一个设置页面。

仅用 AppKit 补充具体桌面功能

需要打开或保存面板、第一响应者控制或自定义 NSView 时,在 SwiftUI 管理的状态模型外围添加小范围 AppKit 功能,不必用 AppKit 重写整个窗口。

技术栈

需要默认选项用途
分栏应用框架NavigationSplitView、.sidebar 列表和 inspector(isPresented:)持久侧栏、详情面板和检查器更符合常见 Mac 应用布局。
桌面操作与设置commands、CommandMenu、键盘快捷键和 Settings 场景菜单栏操作、快捷键和独立设置窗口,让功能符合原生 Mac 使用方式。
状态归属@State、@SceneStorage、@AppStorage 和显式选择绑定让侧栏选择、检查器可见性和偏好设置可预测,避免习惯性增加视图模型。
原生能力补充通过小范围 NSViewRepresentable 或 NSWindow 桥接使用 AppKitAppKit ↗仅在 SwiftUI 无法清晰表达平台行为时使用 AppKit;场景和选择状态仍以 SwiftUI 为准。
展开英文原文对照

Use Codex and the Build macOS Apps plugin to turn an app idea into a desktop-native NavigationSplitView app, keep sidebar selection stable, add menus, toolbars, and keyboard shortcuts, and move preferences into a dedicated Settings scene.

Start from the Mac scene model

This use case is for turning an app idea into a Mac app shell that feels built for desktop, not stretched from a touch-first stack. Ask Codex to choose the scene model first, then design the main window around stable sidebar selection, a detail surface, and an inspector for secondary controls or metadata.

A Mac-native sidebar and detail app shell with a selected item in the sidebar and content in the detail pane

Use the Build macOS Apps plugin when you want Codex to apply that desktop structure and keep the build/run loop shell-first. Its macOS SwiftUI patterns skill is a good fit for scene design, sidebars, inspectors, commands, settings, and small AppKit bridges when SwiftUI stops just short of one Mac-specific behavior.

Build a sidebar, detail pane, and inspector

Prefer NavigationSplitView when the feature benefits from persistent navigation and a stable selected item. Keep sidebar rows native and lightweight, let the sidebar use system backgrounds, and reserve custom cards or dense metadata for the detail pane or inspector.

官方代码示例
struct LibraryRootView: View {
  @SceneStorage("LibraryRootView.selection") private var selection: Item.ID?
  @SceneStorage("LibraryRootView.showInspector") private var showInspector = true

  var body: some View {
    NavigationSplitView {
      List(selection: $selection) {
        ForEach(items) { item in
          Label(item.title, systemImage: item.systemImage)
            .tag(item.id)
        }
      }
      .listStyle(.sidebar)
      .navigationTitle("Library")
    } detail: {
      ItemDetailView(selection: selection)
        .inspector(isPresented: $showInspector) {
          ItemInspectorView(selection: selection)
        }
    }
  }
}

If the app needs unusual split sizing, low-level window coordination, or custom responder-chain behavior, ask Codex to keep the SwiftUI shell intact and add only the smallest AppKit bridge required for that one gap.

Put commands, toolbars, and shortcuts in the desktop layer

Mac users should be able to discover important actions in the menu bar, the toolbar, and keyboard shortcuts. Ask Codex to wire scene-level commands, context-sensitive menu items, and toolbar buttons around the same app actions so desktop users do not have to hunt for gesture-only controls.

官方代码示例
@main
struct LibraryApp: App {
  var body: some Scene {
    WindowGroup {
      LibraryRootView()
    }
    .commands {
      CommandMenu("Library") {
        Button("New Item") {
          // Create a new item.
        }
        .keyboardShortcut("n")

        Button("Toggle Inspector") {
          // Route this command to the focused window or selected item state.
        }
        .keyboardShortcut("i", modifiers: [.command, .option])
      }
    }

    Settings {
      LibrarySettingsView()
    }
  }
}

Use FocusedValue, scene state, or explicit selection state when a command should apply to the current detail item. If a shortcut would be registered in multiple places, ask Codex to consolidate ownership so the app has one clear command route.

Keep preferences in Settings

For app preferences, use a dedicated Settings scene and persist durable user choices with @AppStorage. This is usually a better Mac fit than pushing a settings screen inside the main content window.

官方代码示例
struct LibrarySettingsView: View {
  @AppStorage("showItemMetadata") private var showItemMetadata = true

  var body: some View {
    TabView {
      Form {
        Toggle("Show Item Metadata", isOn: $showItemMetadata)
      }
      .tabItem { Label("General", systemImage: "gearshape") }
    }
    .frame(width: 460, height: 260)
    .scenePadding()
  }
}

Prompt the app concept, then validate the shell

This page works best when your prompt names the app concept, the main content objects, and the primary actions, then asks Codex to build the desktop shell around that workflow first. Have the agent run a small build/run check and summarize the scene structure, command wiring, state ownership, and any AppKit edge it had to bridge.

Practical tips

Keep the sidebar native

Use one icon, one title line, and at most one short secondary line in sidebar rows. Move richer cards, counters, and metadata into the detail pane or inspector so the source list stays easy to scan.

Avoid hiding settings in the main stack

If a user preference affects the whole app, ask Codex to put that control in Settings with @AppStorage and expose an entry point through the app menu instead of building another pushed settings screen.

Save AppKit for narrow desktop gaps

If the feature needs open/save panels, first-responder control, or a custom NSView, use AppKit as a small edge around a SwiftUI-owned state model rather than rewriting the whole window in AppKit.