Skip to content

2026-10-26 studio-redo: Napple Studio rebuilt from zero (Liquid Glass), iOS install and IPA export, simulators in the in-game overlay

Branch claude/studio-redo-2026-10-26, Mac session (macOS 27.0.1, Xcode 27.1, Swift 6.4). Orchestration orchestrations/studio-redo.md. The old Studio stays at tag archive/studio-swiftui-2026-10; its sources under studio/Sources/NappleStudio were deleted and rewritten.

What changed

Studio (studio/, rewritten)

  • NappleStudio (app): StudioApp (WindowGroup, menu commands, standard Settings scene), RootView (NavigationSplitView: sidebar, detail, log inspector, whole-window drop, toast, first-run setup sheet, legal sheet), LibraryView (adaptive grid, cover art from the built app's icon, compatibility dot, glass action bar, context menu, search, Delete key), AddGameView (drop zone -> PlanView -> BuildingView with real stage names + checklist -> DoneView / FailedView; LogInspector), DevicesView (devices + SigningCard + memory-limit card + command lines), SettingsView (General, Keys & Firmware, Engine), Components (GlassCard, Banner, KeysetForm). Model: AppModel (+Build, +Library, +iOS). Liquid Glass through glassEffect, GlassEffectContainer, .glass / .glassProminent buttons, ToolbarSpacer, system ContentUnavailableView empty states; no custom blur anywhere. Strings go through LocalizedStringKey / String(localized:). Accessibility identifiers and labels on the main controls; keyboard: ⌘O add, ⌘B build, ⌘. cancel, ⌘1-3 panes, ⌘R refresh devices, ⌥⌘L log, Return = Play in the library.
  • NappleStudioCore (kept and extended): SigningAccount.swift (ProfileDetails from security cms -D, XcodeTeam from Xcode's IDEProvisioningTeamByIdentifier, AccountDetector, IOSEntitlementPlan), Provisioner.swift (SigningMode; Automatic mode = stub Xcode project with the bundle id, team and entitlements built with xcodebuild -allowProvisioningUpdates -allowProvisioningDeviceRegistration, then napple build signs with the profile Xcode made; xcodebuild -exportArchive was tried for real first and refused: certificateType DEVELOPMENT_MANAGED has an invalid value), DeviceService.swift (devicectl + simctl discovery, install, launch, .ipa unpack, exact command lines), ToolRunner.swift (injectable process runner), BundleIDs (one id per title), BuildTarget.iosSim.
  • Bundle ids: <prefix>.<titleid> per game (prefix editable, default com.napple.game) so several games can be installed on one device; a non-wildcard profile pins its id and the UI says so (user request during the session).
  • Memory limit: paid account -> com.apple.developer.kernel.increased-memory-limit (+ increased-debuggable-memory-limit for debug builds) in the archive entitlements, capability turned on by automatic signing; free/unknown -> nothing added, plain explanation in the UI. Detection order: Xcode team list, profile grants the capability, profile lifetime (7 d free / >= 60 d paid), "(Personal Team)" name. No JIT, no private API.
  • Packaging: build_app.sh also bundles runtime/build-ios-sim/napple-runtime as napple-runtime-ios-sim, registers the NSP/XCI/NSZ/XCZ document types (drop on the Dock icon / File > Open), signs with packaging/entitlements.plist.
  • Debug/scripting flags: --pane, --debug-tour <dir> (every pane -> <name>.rect for an external screencapture -R), --debug-tour-build.

Simulators moved to the in-game overlay (user request: "that is per game, not Studio")

  • frontend/src/simulators.m (new): amiibo tab (search, list, Place / Place new copy / Remove, Reload list with cache-first then NSURLSession fetch, Import dump…) over the runtime's AmiiboPicker; Camera tab (authorization status, Allow / Privacy Settings, Mirror) and the webcam IrFrameSource (AVCaptureSession 640x480 BGRA -> irs_adapter_rgb_to_ir), installed at window creation; the permission prompt fires only from request_access (a game starting an IR processor). Runtime symbols are weak references (__attribute__((weak))): the frontend's own test binaries link with -undefined dynamic_lookup.
  • frontend/src/overlay.m: tabs amiibo and Camera (7 segments, small control size); fe_internal.h; frontend/Makefile: -I../runtime/src/services, -fmodules (AVFoundation auto-linked, the runtime link line is untouched), AVFoundation/CoreMedia/UniformTypeIdentifiers for the test binaries.
  • crates/napple-build/src/bundle.rs: NSCameraUsageDescription in every Mac game app's Info.plist (+ unit test). Amiibo default source changed in the Studio-side loader (now deleted); the runtime's AMIIBO_DEFAULT_SOURCE (amiiboapi.com, offline since the archive) is noted under "Needs from other areas".
  • Docs: docs/guides/studio.md (new), docs/guides/hardware-simulators.md (wiring list -> what was wired), guides README, index.

Parity checklist (old Studio -> new)

Old feature New
Welcome / first-run keys + firmware form, folder scan Setup sheet on first launch (SetupSheet + KeysetForm, KeyFinder.scan)
Key sets: add, replace, delete, select Settings > Keys & Firmware
Drop NSP/XCI/NSZ/XCZ anywhere, folders, picker Whole-window drop, Dock/File > Open (document types), ⌘O picker, folder expansion with skipped-file note
Plan: base, updates, DLC toggles, sizes, warnings, compat note PlanView
Destination: Mac, device, iPhone .ipa, iPad .ipa DestinationCard + iOS Simulator destination (new)
Build with live progress, cancel, quit guard, sleep inhibitor BuildingView, ⌘., AppDelegate.applicationShouldTerminate, beginActivity
Done: play, reveal, copy to Applications, install on device, notes DoneView
Failure with humanised reason FailedView (+ Show Log)
Library: grid, status (present/missing/replaced), locate, trash, remove, adopt output folder, directory watcher LibraryView + AppModel+Library (same LibraryStore)
Compat matrix badge dot on the card, pill on the plan
Engine installer / Move to Applications Engine setup kept (EngineInstaller), engine status in Settings > Engine; Move-to-Applications prompt dropped (the DMG installer script handles it)
iOS signing (identity + profile), unsigned, .ipa, install + launch via devicectl SigningCard (3 modes), IPAExporter, DeviceService
Build log in Application Support/Napple/logs same, plus the live inspector
Settings screen (output folder, legal) standard Settings scene (⌘,)

Verified / tested here

See the integrator file for the table. Highlights: swift test 56 tests / 17 suites pass; make -C frontend test (settings 36 checks, window 13 checks) pass; runtime links with the overlay (nm shows fe_amiibo_page, amiibo_service_picker, irs_service_set_source); cargo test -p napple-build --lib bundle 5 pass; Studio built with build_app.sh; scripted tour screenshots of every pane; LAS built for the Mac through Studio (1 min 2 s, 6.5 GB) and listed in the library with cover art; LAS built for the iOS Simulator through Studio (51 s) and installed/launched with simctl (simctl listapps booted lists com.napple.game.01006bb00c6f0000, Done screen captured); the first Simulator run crashed Studio with an AppKit "more Update Constraints in Window passes than there are views" exception, bisected with --debug-fake-done to the Done screen's ContentUnavailableView followed by a wrapping Banner, now a plain ScrollView; the real iPhone 16 Pro Max is paired but not connected (shown as "Not connected"), so the devicectl install is exercised through the recorded command lines and the unit tests only; .ipa export needs a team signed in to Xcode with the -allowProvisioningUpdates path: the code path is unit-tested with a fake tool runner, the real xcodebuild -exportArchive was not run here (blocker: Xcode has only free Personal Teams on this Mac and no device was connected to register).

Screenshots: docs/archive/changes/img/studio-redo/ (library empty/selected, add, plan, building, done-mac, done-simulator, devices, log, overlay amiibo/camera, simulator home screen with the installed game).

Decisions

  • Amiibo and camera are not in Studio at all (user). The Studio-side Swift amiibo library/store and camera panels written earlier in the session were removed again.
  • Automatic signing lets Xcode make the profile through a stub project (the only supported way to create App IDs/capabilities/profiles without the portal) and keeps the signing in napple build's tested path; the real provisioning run was not executed in this session because it writes to the user's Apple account.
  • Weak runtime references in the frontend instead of new callbacks in napple_frontend_callbacks, so runtime/src stays untouched.

Not done / open

  • Overlay settings for NAPPLE_AMIIBO_DB / NAPPLE_AMIIBO_KEY (environment only).
  • Real-device install and .ipa export were not demonstrated on hardware (device not connected, no paid team); exact commands are in docs/guides/studio.md.
  • Localisation catalogue: strings are LocalizedStringKey/String(localized:), no .xcstrings file is generated yet.

Addendum 2026-10-27: overlay rebuilt (user request)

Floating glass panel with four tabs (Audio/Video, FPS/Speed, Controls, Game/System), game input kept while it is open, controller navigation, Home/Guide toggle, render target presets, statistics options and the native Metal HUD, per-player controller profiles (input/: napple_input_*_player_bindings, napple_input_get_pad_state, tests in test_input), mods/cheats toggles, Mii note. iOS Automatic signing now goes through a stub Xcode project (Provisioner). Screenshots: img/studio-redo/panel-tab0..3.png. Verified: make -C input test, make -C frontend test (settings 37, window 13), runtime links, panel rendered on a LAS data build. Not verified: controller navigation with a real pad, Home button toggle, panel drag, the Metal HUD on screen, mods/cheats loading (runtime side missing). LAS flakes at boot (guest Break 0x35f202) in about 1 of 4 runs with or without the panel (known guest-handle flake).