native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259import ComposableArchitectureimport ProwlCLISharedimport SwiftUI
struct AppearanceSettingsView: View { @Bindable var store: StoreOf<SettingsFeature>
var body: some View { let openActionOptions = OpenWorktreeAction.availableCases let externalDiffToolOptions = ExternalDiffTool.settingsMenuCases VStack(alignment: .leading) { Form { Section { Picker( selection: Binding( get: { store.appLanguage }, set: { store.send(.setAppLanguage($0)) } ) ) { Text(AppLanguage.system.title).tag(AppLanguage.system) Divider() ForEach(AppLanguage.allCases.filter { $0 != .system }) { language in Text(language.title).tag(language) } } label: { Label("Language", systemImage: "translate") } Text( "The change applies the next time Prowl starts; quitting the app may interrupt running terminal tasks.", comment: "App language setting footer: Restart requirement and terminal task warning" ) .foregroundStyle(.secondary) if store.languageChangePending { Text("The language will switch on next launch.", comment: "App language change pending notice") .font(.footnote) .foregroundStyle(.secondary) } } .help("Choose the app language. The change applies the next time Prowl starts.") .onAppear { store.send(.refreshAppLanguage) } Section("Appearance") { HStack { let appearanceMode: Binding<AppearanceMode> = $store.appearanceMode
ForEach(AppearanceMode.allCases) { mode in AppearanceOptionCardView( mode: mode, isSelected: mode == appearanceMode.wrappedValue ) { appearanceMode.wrappedValue = mode } } } VStack(alignment: .leading, spacing: 6) { Text( """ Terminal theming follows your Ghostty configuration. \ Browse [all built-in themes](https://iterm2colorschemes.com/), \ then add a dual-theme line such as: """ ) Text("theme = light:Monokai Pro Light Sun,dark:Dimmed Monokai") .monospaced() .textSelection(.enabled) HStack(spacing: 8) { Button("Open Config") { GhosttyRuntime.openGhosttyConfig() } .help("Open your Ghostty config file in the default text editor.") Button("Reload") { GhosttyRuntime.shared?.reloadAppConfig() } .help("Re-read the Ghostty config from disk and apply it to running terminals.") } .controlSize(.small) } .font(.footnote) .foregroundStyle(.secondary) } Section("Window Tint") { Picker("Tint nav & toolbar", selection: $store.windowTintMode) { ForEach(WindowTintMode.allCases) { mode in Text(mode.title).tag(mode) } } .help("Color the navigation panel and toolbar.") if store.windowTintMode == .custom { ColorPicker( "Custom tint color", selection: $store.windowTintCustomColor, supportsOpacity: false ) .help("Tint the nav and toolbar with this color in every view, ignoring repository colors.") } Text(tintFootnote) .font(.callout) .foregroundStyle(.secondary)
Picker("Tint spines in Shelf View", selection: $store.shelfSpineTintFallback) { ForEach(ShelfSpineTintFallback.allCases) { fallback in Text(fallback.title).tag(fallback) } } .help("Spine style for repositories without a color, or for every spine when Follow Repo Color is off.") Toggle( "Follow Repo Color Setting", isOn: $store.shelfSpineTintFollowsRepositoryColor ) .help("When disabled, all Shelf spines use the selected Gray or System Tint style.") Text(shelfSpineTintFootnote) .font(.callout) .foregroundStyle(.secondary) } Section("Repository Icons") { Toggle( "Detect project icons automatically", isOn: $store.detectRepositoryIconsAutomatically ) .help("Use a project's own app icon or logo as the repository icon when adding it.") Text( """ Detection runs locally when a repository is added. It never replaces an icon \ you picked, and turning it off leaves already detected icons unchanged. """ ) .foregroundStyle(.secondary) .font(.callout) } Section("Splits") { Toggle( "Dim unfocused split panes", isOn: $store.dimUnfocusedSplits ) .help("Fade split panes that aren't focused so the active one stands out.") } Section("Default Views") { Picker("Open when launching Prowl", selection: $store.defaultViewMode) { ForEach(DefaultViewMode.allCases) { mode in Text(mode.title).tag(mode) } } .help("View Prowl starts in on launch. Shelf and Canvas require at least one worktree or folder.")
Picker("Canvas layout", selection: $store.canvasDefaultLayout) { ForEach(CanvasDefaultLayout.allCases) { layout in Text(layout.title).tag(layout) } } .help("How cards are arranged the first time you open Canvas for a set of cards.") Text(store.canvasDefaultLayout.settingsDescription) .foregroundStyle(.secondary) .font(.callout) } Section("Default Editor") { Toggle( "Show in toolbar", isOn: $store.showDefaultEditorInToolbar ) .help("Show the Open in Editor button in the worktree toolbar.") Picker( "Default editor", selection: $store.defaultEditorID ) { Text("Automatic") .tag(OpenWorktreeAction.automaticSettingsID) ForEach(openActionOptions) { action in Text(action.labelTitle) .tag(action.settingsID) } } .help( """ Applies to worktrees without repository overrides. \ Automatic prefers an app matching the project type, e.g. Xcode for Swift projects. """ ) } Section("Diff Tool") { Picker( "Open diff with", selection: $store.externalDiffToolID ) { ForEach(externalDiffToolOptions) { tool in Text(tool.title) .tag(tool.settingsID) .disabled(!tool.isInstalled) } } .help("Choose what opens when you click a diff badge or run Show Diff.") Text("Tools not installed on this Mac appear disabled.") .font(.callout) .foregroundStyle(.secondary) if store.externalDiffToolID == ExternalDiffTool.custom.settingsID { TextField( "Command", text: $store.externalDiffCustomCommand, prompt: Text("my-diff {leftPath} {rightPath}") ) .textFieldStyle(.roundedBorder) .help( """ Runs in the worktree directory. Supports {leftPath}, {rightPath}, \ {worktreePath}, {repoPath}, and {branch}. """ ) } } Section("Run") { Toggle( "Show in toolbar", isOn: $store.showRunButtonInToolbar ) .help("Show the Run button in the worktree toolbar.") } Section("Quit") { Toggle( "Confirm before quitting", isOn: $store.confirmBeforeQuit ) .help("Ask before quitting Prowl") } } .formStyle(.grouped) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) }
private var tintFootnote: String { switch store.windowTintMode { case .none: return String(localized: "No tint. The nav and toolbar use the default system look.") case .repositoryColor: return String(localized: "Uses the active repository's color. Uncolored repositories use gray.") case .custom: return String(localized: "Uses your chosen color everywhere, regardless of per-repository colors.") } }
private var shelfSpineTintFootnote: String { switch (store.shelfSpineTintFallback, store.shelfSpineTintFollowsRepositoryColor) { case (.neutral, true): return String( localized: "Uncolored repositories use a gray spine. Repositories with a custom color still use that color.") case (.neutral, false): return String( localized: "Uncolored repositories use a gray spine. Repository colors are ignored for Shelf spines.") case (.systemTint, true): return String( localized: "Uncolored repositories use the system tint color. Repositories with a custom color still use that color.") case (.systemTint, false): return String( localized: "Uncolored repositories use the system tint color. Repository colors are ignored for Shelf spines.") } }}