native macOS codings agent orchestrator
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(debug): DEBUG-only Debug Window with Icon Catalog

Adds a separate window dedicated to in-app debug surfaces, reachable
from a `Debug` menu that only ships in DEBUG builds. First surface is
an Icon Catalog that lists every `CommandIconMap` entry (token, icon
asset / SF Symbol fallback) rendered through the same `TabIconImage`
the tab UI uses, so the auto-detected set can be eyeballed in one
place when adding new branded artwork.

- `DebugWindowManager` mirrors `SettingsWindowManager`: cached
NSWindow + NSHostingController, configured once during app
bootstrap with the root store so the window can mirror the user's
appearance setting via `WindowAppearanceSetter` (standalone
NSWindows don't pick up `.preferredColorScheme`).
- `DebugView` is a NavigationSplitView so future debug surfaces can
be added by extending `DebugSection` and the sidebar list / detail
switch.
- `CommandIconMap.debugAllEntries` exposes the first-token mapping
for the catalog (DEBUG only).
- The Debug menu in `supacodeApp.commands` is wrapped behind
`#if DEBUG`; the existing commands are grouped under `Group { ... }`
to stay under SwiftUI's CommandsBuilder tuple-arity limit once the
Debug menu is added.

Also swaps the Codex asset for a tighter mark sourced from LobeHub
(viewBox 1 1 18 18 to match the visual weight of the other 24x24
brand SVGs).

onevcat a372091d f26adde4

+209 -8
+21 -7
supacode/App/supacodeApp.swift
··· 245 245 ghosttyShortcuts: shortcuts, 246 246 commandKeyObserver: keyObserver 247 247 ) 248 + #if DEBUG 249 + DebugWindowManager.shared.configure(store: appStore) 250 + #endif 248 251 } 249 252 250 253 private static func makeTargetResolver( ··· 622 625 .environment(ghosttyShortcuts) 623 626 .environment(commandKeyObserver) 624 627 .commands { 625 - WorktreeCommands(store: store) 626 - SidebarCommands(store: store) 627 - TerminalCommands(ghosttyShortcuts: ghosttyShortcuts) 628 - WindowCommands( 629 - ghosttyShortcuts: ghosttyShortcuts, 630 - resolvedKeybindings: store.resolvedKeybindings 631 - ) 628 + // Grouped to keep `commands` under SwiftUI's CommandsBuilder 629 + // tuple-arity limit when `#if DEBUG` adds the Debug menu below. 630 + Group { 631 + WorktreeCommands(store: store) 632 + SidebarCommands(store: store) 633 + TerminalCommands(ghosttyShortcuts: ghosttyShortcuts) 634 + WindowCommands( 635 + ghosttyShortcuts: ghosttyShortcuts, 636 + resolvedKeybindings: store.resolvedKeybindings 637 + ) 638 + } 632 639 CommandGroup(after: .textEditing) { 633 640 Button("Command Palette") { 634 641 store.send(.commandPalette(.togglePresented)) ··· 662 669 } 663 670 .help("Install the prowl command line tool to /usr/local/bin") 664 671 } 672 + #if DEBUG 673 + CommandMenu("Debug") { 674 + Button("Icon Catalog") { 675 + DebugWindowManager.shared.show() 676 + } 677 + } 678 + #endif 665 679 CommandGroup(replacing: .help) { 666 680 Button("Homepage", systemImage: "house") { 667 681 if let url = URL(string: "https://prowl.onev.cat/") {
+1 -1
supacode/Assets.xcassets/CommandIcons/Codex.imageset/Codex.svg
··· 1 - <svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Codex</title><path clip-rule="evenodd" d="M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z"></path></svg> 1 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="1 1 18 18" fill="currentColor"><path d="M13.333 11.418a.665.665 0 0 1 0 1.33h-2.5a.665.665 0 1 1 0-1.33zM6.741 7.347a.665.665 0 0 1 .912.228l1.25 2.083a.67.67 0 0 1 0 .685l-1.25 2.083a.666.666 0 0 1-1.14-.685L7.557 10 6.513 8.259a.665.665 0 0 1 .228-.912"/><path fill-rule="evenodd" d="M9.002 1.75c1.114 0 2.134.405 2.922 1.072q.392-.072.806-.073a4.523 4.523 0 0 1 4.447 5.326 4.524 4.524 0 0 1-1.922 7.18 4.52 4.52 0 0 1-4.256 2.995 4.5 4.5 0 0 1-2.923-1.073q-.391.072-.805.074a4.523 4.523 0 0 1-4.448-5.327 4.5 4.5 0 0 1-1.066-2.69l-.007-.233a4.53 4.53 0 0 1 2.994-4.258A4.525 4.525 0 0 1 9.002 1.75m0 1.328a3.2 3.2 0 0 0-3.087 2.367.67.67 0 0 1-.47.47 3.197 3.197 0 0 0-1.43 5.346.67.67 0 0 1 .17.64q-.106.398-.108.828A3.195 3.195 0 0 0 8.1 15.815l.087-.017a.66.66 0 0 1 .552.188 3.197 3.197 0 0 0 5.346-1.432l.028-.083a.66.66 0 0 1 .442-.386 3.2 3.2 0 0 0 2.368-3.086c0-.882-.358-1.681-.937-2.26a.66.66 0 0 1-.17-.64q.107-.397.108-.828A3.196 3.196 0 0 0 11.9 4.185a.66.66 0 0 1-.64-.171 3.19 3.19 0 0 0-2.093-.933z" clip-rule="evenodd"/></svg>
+55
supacode/Features/Debug/BusinessLogic/DebugWindowManager.swift
··· 1 + import AppKit 2 + import ComposableArchitecture 3 + import SwiftUI 4 + 5 + #if DEBUG 6 + 7 + /// Manages the singleton Debug Window. Lifecycle mirrors 8 + /// `SettingsWindowManager`: cache the `NSWindow`, deminiaturise + 9 + /// front it on subsequent shows, never release on close so opening 10 + /// is cheap. Configured once during app bootstrap with the root 11 + /// store so the window can mirror the user's appearance setting. 12 + @MainActor 13 + final class DebugWindowManager { 14 + static let shared = DebugWindowManager() 15 + 16 + private var window: NSWindow? 17 + private var store: StoreOf<AppFeature>? 18 + 19 + private init() {} 20 + 21 + func configure(store: StoreOf<AppFeature>) { 22 + self.store = store 23 + } 24 + 25 + func show() { 26 + if let existing = window { 27 + if existing.isMiniaturized { 28 + existing.deminiaturize(nil) 29 + } 30 + existing.makeKeyAndOrderFront(nil) 31 + return 32 + } 33 + 34 + guard let store else { return } 35 + let host = NSHostingController(rootView: DebugView(store: store)) 36 + let new = NSWindow(contentViewController: host) 37 + new.title = "Prowl Debug" 38 + new.identifier = NSUserInterfaceItemIdentifier("debug") 39 + new.styleMask = [.titled, .closable, .miniaturizable, .resizable] 40 + new.tabbingMode = .disallowed 41 + new.toolbarStyle = .unified 42 + new.toolbar = NSToolbar(identifier: "DebugToolbar") 43 + if #unavailable(macOS 15.0) { 44 + new.toolbar?.showsBaselineSeparator = false 45 + } 46 + new.isReleasedWhenClosed = false 47 + new.setContentSize(NSSize(width: 800, height: 600)) 48 + new.minSize = NSSize(width: 700, height: 500) 49 + new.center() 50 + new.makeKeyAndOrderFront(nil) 51 + window = new 52 + } 53 + } 54 + 55 + #endif
+16
supacode/Features/Debug/Views/DebugSection.swift
··· 1 + import Foundation 2 + 3 + #if DEBUG 4 + 5 + /// Sidebar entries for the Debug Window. Add a case here, an entry 6 + /// in `DebugView`'s sidebar list, and a switch arm in the detail 7 + /// area to register a new debug surface. 8 + enum DebugSection: Hashable { 9 + /// Catalogue of every `CommandIconMap` entry alongside its rendered 10 + /// icon. Lets us eyeball the auto-detected tab-icon set after 11 + /// adding new branded artwork or sanity-checking that an asset 12 + /// actually paints in the SwiftUI runtime. 13 + case iconCatalog 14 + } 15 + 16 + #endif
+47
supacode/Features/Debug/Views/DebugView.swift
··· 1 + import ComposableArchitecture 2 + import SwiftUI 3 + 4 + #if DEBUG 5 + 6 + /// Root of the Debug Window. NavigationSplitView with a sidebar so 7 + /// future debug surfaces (detector state, analytics events, 8 + /// ghostty internals…) can be added by extending `DebugSection` 9 + /// and the sidebar list / detail switch below. The store is held 10 + /// only to mirror the app-wide appearance setting on this window; 11 + /// individual debug surfaces don't have to thread it. 12 + struct DebugView: View { 13 + let store: StoreOf<AppFeature> 14 + @State private var selection: DebugSection = .iconCatalog 15 + 16 + var body: some View { 17 + NavigationSplitView(columnVisibility: .constant(.all)) { 18 + List(selection: $selection) { 19 + Label("Icon Catalog", systemImage: "square.grid.2x2") 20 + .tag(DebugSection.iconCatalog) 21 + } 22 + .listStyle(.sidebar) 23 + .frame(minWidth: 200, maxHeight: .infinity) 24 + .navigationSplitViewColumnWidth(200) 25 + } detail: { 26 + Group { 27 + switch selection { 28 + case .iconCatalog: 29 + IconCatalogView() 30 + .navigationTitle("Icon Catalog") 31 + .navigationSubtitle("CommandIconMap entries (DEBUG)") 32 + } 33 + } 34 + .frame(maxWidth: .infinity, maxHeight: .infinity) 35 + } 36 + .navigationSplitViewStyle(.balanced) 37 + .frame(minWidth: 700, minHeight: 500) 38 + .background { 39 + // Standalone NSWindow doesn't pick up `.preferredColorScheme` 40 + // (only WindowGroup scenes do), so route through the same 41 + // bridge SettingsView uses to honour the user's appearance. 42 + WindowAppearanceSetter(colorScheme: store.settings.appearanceMode.colorScheme) 43 + } 44 + } 45 + } 46 + 47 + #endif
+54
supacode/Features/Debug/Views/IconCatalogView.swift
··· 1 + import SwiftUI 2 + 3 + #if DEBUG 4 + 5 + /// DEBUG-only catalogue of the auto-detected tab icons. Each row 6 + /// renders a `CommandIconMap` entry through the same `TabIconImage` 7 + /// the actual tab UI uses, so the asset / SF Symbol fallback / size 8 + /// behaviour shown here matches what users see on a real tab. 9 + struct IconCatalogView: View { 10 + var body: some View { 11 + let entries = CommandIconMap.debugAllEntries 12 + ScrollView { 13 + LazyVStack(spacing: 0) { 14 + ForEach(entries, id: \.token) { entry in 15 + IconCatalogRow(token: entry.token, icon: entry.icon) 16 + Divider().padding(.leading, 60) 17 + } 18 + } 19 + .padding(.horizontal) 20 + } 21 + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) 22 + } 23 + } 24 + 25 + private struct IconCatalogRow: View { 26 + let token: String 27 + let icon: TabIconSource 28 + 29 + var body: some View { 30 + HStack(spacing: 16) { 31 + TabIconImage(rawName: icon.storageString, pointSize: 24) 32 + .foregroundStyle(.primary) 33 + .frame(width: 32, height: 32, alignment: .center) 34 + VStack(alignment: .leading, spacing: 2) { 35 + Text(token) 36 + .font(.body.monospaced().weight(.semibold)) 37 + Text(detailLine) 38 + .font(.caption.monospaced()) 39 + .foregroundStyle(.secondary) 40 + } 41 + Spacer(minLength: 0) 42 + } 43 + .padding(.vertical, 10) 44 + } 45 + 46 + private var detailLine: String { 47 + if let asset = icon.assetName { 48 + return "asset:\(asset) · fallback sf:\(icon.systemSymbol)" 49 + } 50 + return "sf:\(icon.systemSymbol)" 51 + } 52 + } 53 + 54 + #endif
+15
supacode/Features/Terminal/Models/CommandIconMap.swift
··· 82 82 "journalctl": TabIconSource(systemSymbol: "text.justifyleft"), 83 83 ] 84 84 } 85 + 86 + #if DEBUG 87 + 88 + extension CommandIconMap { 89 + /// All first-token mapping entries, sorted alphabetically by 90 + /// token. Surfaced for the Debug Window's Icon Catalog so the 91 + /// auto-detected set can be eyeballed in one place. 92 + static var debugAllEntries: [(token: String, icon: TabIconSource)] { 93 + firstTokenMapping 94 + .map { (token: $0.key, icon: $0.value) } 95 + .sorted { $0.token < $1.token } 96 + } 97 + } 98 + 99 + #endif