From adfa609b6aa60385f5fd50bf58b42139da2b33a5 Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 11:12:19 -0300 Subject: [PATCH 01/23] =?UTF-8?q?docs:=20rebrand=20cmux=20=E2=86=92=20Prog?= =?UTF-8?q?rama=20across=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename cmux → Programa in prose, manaflow-ai/cmux → darkroomengineering/programa, cmux-macos.dmg → programa-macos.dmg. Remove prior-owner promo content from README (manaflow star history, Founder's Edition Stripe link, personal contacts). --- CLAUDE.md | 60 ++++++++-------- CONTRIBUTING.md | 20 +++--- PROJECTS.md | 30 ++++---- README.md | 98 ++++++++++----------------- THIRD_PARTY_LICENSES.md | 2 +- TODO.md | 24 +++---- daemon/remote/README.md | 38 +++++------ docs/agent-browser-port-spec.md | 46 ++++++------- docs/ghostty-fork.md | 50 +++++++------- docs/notifications.md | 66 +++++++++--------- docs/remote-daemon-spec.md | 58 ++++++++-------- docs/socket-focus-steal-audit.todo.md | 4 +- docs/v2-api-migration.md | 6 +- plans/joyful-brewing-riddle.md | 90 ++++++++++++++++++++++++ plans/rebrand-cmux-to-programa.md | 57 ++++++++++++++++ 15 files changed, 385 insertions(+), 264 deletions(-) create mode 100644 plans/joyful-brewing-riddle.md create mode 100644 plans/rebrand-cmux-to-programa.md diff --git a/CLAUDE.md b/CLAUDE.md index 7da0cc108c0..9ca6cdf464d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# cmux agent notes +# Programa agent notes ## Initial setup @@ -31,26 +31,26 @@ By default, `reload.sh` builds but does **not** launch the app. The script print Example. If `reload.sh` output contains: ``` App path: - /Users/someone/Library/Developer/Xcode/DerivedData/cmux-my-tag/Build/Products/Debug/cmux DEV my-tag.app + /Users/someone/Library/Developer/Xcode/DerivedData/programa-my-tag/Build/Products/Debug/Programa DEV my-tag.app ``` **Claude Code** outputs: ```markdown ======================================================= -[cmux DEV my-tag.app](file:///Users/someone/Library/Developer/Xcode/DerivedData/cmux-my-tag/Build/Products/Debug/cmux%20DEV%20my-tag.app) +[Programa DEV my-tag.app](file:///Users/someone/Library/Developer/Xcode/DerivedData/programa-my-tag/Build/Products/Debug/Programa%20DEV%20my-tag.app) ======================================================= ``` **Codex** outputs: ``` ======================================================= -[my-tag: file:///Users/someone/Library/Developer/Xcode/DerivedData/cmux-my-tag/Build/Products/Debug/cmux%20DEV%20my-tag.app](file:///Users/someone/Library/Developer/Xcode/DerivedData/cmux-my-tag/Build/Products/Debug/cmux%20DEV%20my-tag.app) +[my-tag: file:///Users/someone/Library/Developer/Xcode/DerivedData/programa-my-tag/Build/Products/Debug/Programa%20DEV%20my-tag.app](file:///Users/someone/Library/Developer/Xcode/DerivedData/programa-my-tag/Build/Products/Debug/Programa%20DEV%20my-tag.app) ======================================================= ``` -Never use `/tmp/cmux-/...` app links in chat output. +Never use `/tmp/programa-/...` app links in chat output. -After making code changes, always use `reload.sh --tag` to build. **Never run bare `xcodebuild` or `open` an untagged `cmux DEV.app`.** Untagged builds share the default debug socket and bundle ID with other agents, causing conflicts and stealing focus. +After making code changes, always use `reload.sh --tag` to build. **Never run bare `xcodebuild` or `open` an untagged `Programa DEV.app`.** Untagged builds share the default debug socket and bundle ID with other agents, causing conflicts and stealing focus. ```bash ./scripts/reload.sh --tag @@ -59,7 +59,7 @@ After making code changes, always use `reload.sh --tag` to build. **Never run ba If you only need to verify the build compiles (no launch), use a tagged derivedDataPath: ```bash -xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination 'platform=macOS' -derivedDataPath /tmp/cmux- build +xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug -destination 'platform=macOS' -derivedDataPath /tmp/programa- build ``` When rebuilding GhosttyKit.xcframework, always use Release optimizations: @@ -68,10 +68,10 @@ When rebuilding GhosttyKit.xcframework, always use Release optimizations: cd ghostty && zig build -Demit-xcframework=true -Dxcframework-target=universal -Doptimize=ReleaseFast ``` -When rebuilding cmuxd for release/bundling, always use ReleaseFast: +When rebuilding programad for release/bundling, always use ReleaseFast: ```bash -cd cmuxd && zig build -Doptimize=ReleaseFast +cd programad && zig build -Doptimize=ReleaseFast ``` `reload` = build the Debug app (tag required). Pass `--launch` to also kill existing and open: @@ -87,7 +87,7 @@ cd cmuxd && zig build -Doptimize=ReleaseFast ./scripts/reloadp.sh ``` -`reloads` = kill and launch the Release app as "cmux STAGING" (isolated from production cmux): +`reloads` = kill and launch the Release app as "Programa STAGING" (isolated from production Programa): ```bash ./scripts/reloads.sh @@ -114,14 +114,14 @@ Before launching a new tagged run, clean up any older tags you started in this s All debug events (keys, mouse, focus, splits, tabs) go to a unified log in DEBUG builds: ```bash -tail -f "$(cat /tmp/cmux-last-debug-log-path 2>/dev/null || echo /tmp/cmux-debug.log)" +tail -f "$(cat /tmp/programa-last-debug-log-path 2>/dev/null || echo /tmp/programa-debug.log)" ``` -- Untagged Debug app: `/tmp/cmux-debug.log` -- Tagged Debug app (`./scripts/reload.sh --tag `): `/tmp/cmux-debug-.log` -- `reload.sh` writes the current path to `/tmp/cmux-last-debug-log-path` -- `reload.sh` writes the selected dev CLI path to `/tmp/cmux-last-cli-path` -- `reload.sh` updates `/tmp/cmux-cli` and `$HOME/.local/bin/cmux-dev` to that CLI +- Untagged Debug app: `/tmp/programa-debug.log` +- Tagged Debug app (`./scripts/reload.sh --tag `): `/tmp/programa-debug-.log` +- `reload.sh` writes the current path to `/tmp/programa-last-debug-log-path` +- `reload.sh` writes the selected dev CLI path to `/tmp/programa-last-cli-path` +- `reload.sh` updates `/tmp/programa-cli` and `$HOME/.local/bin/programa-dev` to that CLI - Implementation: `vendor/bonsplit/Sources/Bonsplit/Public/DebugEventLog.swift` - Free function `dlog("message")` — logs with timestamp and appends to file in real time @@ -146,12 +146,12 @@ This makes it visible in the GitHub PR UI (Commits tab, check statuses) that the The app has a **Debug** menu in the macOS menu bar (only in DEBUG builds). Use it for visual iteration: - **Debug > Debug Windows** contains panels for tuning layout, colors, and behavior. Entries are alphabetical with no dividers. -- To add a debug toggle or visual option: create an `NSWindowController` subclass with a `shared` singleton, add it to the "Debug Windows" menu in `Sources/cmuxApp.swift`, and add a SwiftUI view with `@AppStorage` bindings for live changes. +- To add a debug toggle or visual option: create an `NSWindowController` subclass with a `shared` singleton, add it to the "Debug Windows" menu in `Sources/programaApp.swift`, and add a SwiftUI view with `@AppStorage` bindings for live changes. - When the user says "debug menu" or "debug window", they mean this menu, not `defaults write`. ## Pitfalls -- **Custom UTTypes** for drag-and-drop must be declared in `Resources/Info.plist` under `UTExportedTypeDeclarations` (e.g. `com.splittabbar.tabtransfer`, `com.cmux.sidebar-tab-reorder`). +- **Custom UTTypes** for drag-and-drop must be declared in `Resources/Info.plist` under `UTExportedTypeDeclarations` (e.g. `com.splittabbar.tabtransfer`, `com.darkroom.programa.sidebar-tab-reorder`). - Do not add an app-level display link or manual `ghostty_surface_draw` loop; rely on Ghostty wakeups/renderer to avoid typing lag. - **Typing-latency-sensitive paths** (read carefully before touching these areas): - `WindowTerminalHostView.hitTest()` in `TerminalWindowPortal.swift`: called on every event including keyboard. All divider/sidebar/drag routing is gated to pointer events only. Do not add work outside the `isPointerEvent` guard. @@ -160,7 +160,7 @@ The app has a **Debug** menu in the macOS menu bar (only in DEBUG builds). Use i - **Terminal find layering contract:** `SurfaceSearchOverlay` must be mounted from `GhosttySurfaceScrollView` in `Sources/GhosttyTerminalView.swift` (AppKit portal layer), not from SwiftUI panel containers such as `Sources/Panels/TerminalPanelView.swift`. Portal-hosted terminal views can sit above SwiftUI during split/workspace churn. - **Submodule safety:** When modifying a submodule (ghostty, vendor/bonsplit, etc.), always push the submodule commit to its remote `main` branch BEFORE committing the updated pointer in the parent repo. Never commit on a detached HEAD or temporary branch — the commit will be orphaned and lost. Verify with: `cd && git merge-base --is-ancestor HEAD origin/main`. - **All user-facing strings must be localized.** Use `String(localized: "key.name", defaultValue: "English text")` for every string shown in the UI (labels, buttons, menus, dialogs, tooltips, error messages). Keys go in `Resources/Localizable.xcstrings` with translations for all supported languages (currently English and Japanese). Never use bare string literals in SwiftUI `Text()`, `Button()`, alert titles, etc. -- **Shortcut policy:** Every new cmux-owned keyboard shortcut must be added to `KeyboardShortcutSettings`, visible/editable in Settings, supported in `~/.config/cmux/settings.json`, and documented in the keyboard shortcut and configuration docs. +- **Shortcut policy:** Every new Programa-owned keyboard shortcut must be added to `KeyboardShortcutSettings`, visible/editable in Settings, supported in `~/.config/programa/settings.json`, and documented in the keyboard shortcut and configuration docs. ## Test quality policy @@ -191,23 +191,23 @@ The app has a **Debug** menu in the macOS menu bar (only in DEBUG builds). Use i **Never run tests locally.** All tests (E2E, UI, python socket tests) run via GitHub Actions or on the VM. -- **E2E / UI tests:** trigger via `gh workflow run test-e2e.yml` (see cmuxterm-hq CLAUDE.md for details) -- **Unit tests:** `xcodebuild -scheme cmux-unit` is safe (no app launch), but prefer CI -- **Python socket tests (tests_v2/):** these connect to a running cmux instance's socket. Never launch an untagged `cmux DEV.app` to run them. If you must test locally, use a tagged build's socket (`/tmp/cmux-debug-.sock`) with `CMUX_SOCKET=/tmp/cmux-debug-.sock` -- **Never `open` an untagged `cmux DEV.app`** from DerivedData. It conflicts with the user's running debug instance. +- **E2E / UI tests:** trigger via `gh workflow run test-e2e.yml` (see programa-hq CLAUDE.md for details) +- **Unit tests:** `xcodebuild -scheme programa-unit` is safe (no app launch), but prefer CI +- **Python socket tests (tests_v2/):** these connect to a running Programa instance's socket. Never launch an untagged `Programa DEV.app` to run them. If you must test locally, use a tagged build's socket (`/tmp/programa-debug-.sock`) with `PROGRAMA_SOCKET=/tmp/programa-debug-.sock` +- **Never `open` an untagged `Programa DEV.app`** from DerivedData. It conflicts with the user's running debug instance. ## Ghostty submodule workflow -Ghostty changes must be committed in the `ghostty` submodule and pushed to the `manaflow-ai/ghostty` fork. +Ghostty changes must be committed in the `ghostty` submodule and pushed to the Darkroom Engineering ghostty fork. Keep `docs/ghostty-fork.md` up to date with any fork changes and conflict notes. ```bash cd ghostty -git remote -v # origin = upstream, manaflow = fork +git remote -v # origin = upstream, darkroom = fork git checkout -b git add git commit -m "..." -git push manaflow +git push darkroom ``` To keep the fork up to date with upstream: @@ -217,7 +217,7 @@ cd ghostty git fetch origin git checkout main git merge origin/main -git push manaflow main +git push darkroom main ``` Then update the parent repo with the new submodule SHA: @@ -253,13 +253,13 @@ Manual release steps (if not using the command): ```bash git tag vX.Y.Z git push origin vX.Y.Z -gh run watch --repo manaflow-ai/cmux +gh run watch --repo darkroomengineering/programa ``` Notes: - Requires GitHub secrets: `APPLE_CERTIFICATE_BASE64`, `APPLE_CERTIFICATE_PASSWORD`, `APPLE_SIGNING_IDENTITY`, `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`. -- The release asset is `cmux-macos.dmg` attached to the tag. -- README download button points to `releases/latest/download/cmux-macos.dmg`. +- The release asset is `programa-macos.dmg` attached to the tag. +- README download button points to `releases/latest/download/programa-macos.dmg`. - Versioning: bump the minor version for updates unless explicitly asked otherwise. - Changelog: update `CHANGELOG.md`; docs changelog is rendered from it. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a0d7327cb4..4727b33d297 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing to cmux +# Contributing to Programa ## Prerequisites @@ -10,8 +10,8 @@ 1. Clone the repository with submodules: ```bash - git clone --recursive https://github.com/manaflow-ai/cmux.git - cd cmux + git clone --recursive https://github.com/darkroomengineering/programa.git + cd programa ``` 2. Run the setup script: @@ -20,7 +20,7 @@ ``` This will: - - Initialize git submodules (ghostty, homebrew-cmux) + - Initialize git submodules (ghostty, homebrew-programa) - Build the GhosttyKit.xcframework from source - Create the necessary symlinks @@ -54,18 +54,18 @@ zig build -Demit-xcframework=true -Doptimize=ReleaseFast ### Basic tests (run on VM) ```bash -ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" build && pkill -x "cmux DEV" || true && APP=$(find /Users/cmux/Library/Developer/Xcode/DerivedData -path "*/Build/Products/Debug/cmux DEV.app" -print -quit) && open "$APP" && for i in {1..20}; do [ -S /tmp/cmux.sock ] && break; sleep 0.5; done && python3 tests/test_update_timing.py && python3 tests/test_signals_auto.py && python3 tests/test_ctrl_socket.py && python3 tests/test_notifications.py' +ssh programa-vm 'cd /Users/programa/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug -destination "platform=macOS" build && pkill -x "programa DEV" || true && APP=$(find /Users/programa/Library/Developer/Xcode/DerivedData -path "*/Build/Products/Debug/programa DEV.app" -print -quit) && open "$APP" && for i in {1..20}; do [ -S /tmp/programa.sock ] && break; sleep 0.5; done && python3 tests/test_update_timing.py && python3 tests/test_signals_auto.py && python3 tests/test_ctrl_socket.py && python3 tests/test_notifications.py' ``` ### UI tests (run on VM) ```bash -ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" -only-testing:cmuxUITests test' +ssh programa-vm 'cd /Users/programa/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug -destination "platform=macOS" -only-testing:programaUITests test' ``` ## Ghostty Submodule -The `ghostty` submodule points to [manaflow-ai/ghostty](https://github.com/manaflow-ai/ghostty), a fork of the upstream Ghostty project. +The `ghostty` submodule points to a fork of the upstream Ghostty project maintained by Darkroom Engineering. ### Making changes to ghostty @@ -75,7 +75,7 @@ git checkout -b my-feature # make changes git add . git commit -m "Description of changes" -git push manaflow my-feature +git push darkroom my-feature ``` ### Keeping the fork updated @@ -85,7 +85,7 @@ cd ghostty git fetch origin git checkout main git merge origin/main -git push manaflow main +git push darkroom main ``` Then update the parent repo: @@ -103,4 +103,4 @@ See `docs/ghostty-fork.md` for details on fork changes and conflict notes. By contributing to this repository, you agree that: 1. Your contributions are licensed under the project's GNU General Public License v3.0 or later (`GPL-3.0-or-later`). -2. You grant Manaflow, Inc. a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license to use, reproduce, modify, sublicense, and distribute your contributions under any license, including a commercial license offered to third parties. +2. You grant Darkroom Engineering a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license to use, reproduce, modify, sublicense, and distribute your contributions under any license, including a commercial license offered to third parties. diff --git a/PROJECTS.md b/PROJECTS.md index 6bf2237a5ba..72b9eefc12d 100644 --- a/PROJECTS.md +++ b/PROJECTS.md @@ -1,15 +1,15 @@ # PROJECTS -Cross-project tracking (features, bugs, backlog) for cmux. +Cross-project tracking (features, bugs, backlog) for Programa. ## Done - 2026-02-14: Fixed updater release regression path: made `.github/workflows/release.yml` Sparkle Info.plist key injection idempotent (re-running tags no longer fails with "Entry Already Exists"), and hardened `scripts/bump-version.sh` to keep `CURRENT_PROJECT_VERSION` above the latest published Sparkle appcast build number so upgrades from `0.27.0` can be detected. - 2026-02-14: Relicensed the repository to strong copyleft (`AGPL-3.0-or-later`), added canonical `LICENSE` text, and updated project/package metadata to advertise AGPL consistently. - 2026-02-14: Added an opt-in nightly update channel in Settings (`Receive nightly builds`) that routes Sparkle feed selection between stable and nightly appcasts, plus a scheduled GitHub Actions nightly pipeline (`.github/workflows/nightly.yml`) that only rebuilds when `main` has new commits since `nightly` (or when manually forced). - 2026-02-13: Added `demos/wkwebview-ssh-proxy-cookie-demo/` with a standalone macOS Swift app (two WKWebViews), two Docker backends (`:8080`) running behind separate SSH SOCKS tunnels, and scripts/docs to demonstrate same URL (`shared.test:8080`) routing to different backends plus app-level cookie sync between separate proxy-scoped data stores. -- 2026-02-13: Expanded skill docs for end users: added deep-linkable `cmux-browser` references (`authentication.md`, `session-management.md`, `snapshot-refs.md`, `video-recording.md`, `proxy-support.md`) + templates, and added a new `skills/cmux/` core skill for windows/workspaces/panes/surfaces workflows. +- 2026-02-13: Expanded skill docs for end users: added deep-linkable `programa-browser` references (`authentication.md`, `session-management.md`, `snapshot-refs.md`, `video-recording.md`, `proxy-support.md`) + templates, and added a new `skills/programa/` core skill for windows/workspaces/panes/surfaces workflows. - 2026-02-13: Changed CLI ID formatting defaults to refs-first for `--json` output (UUID output now opt-in via `--id-format uuids|both`) and added regression test `tests_v2/test_cli_id_format_defaults.py`. -- 2026-02-13: Added new repo skill `skills/cmux-browser/` adapted from `vercel-labs/agent-browser` with cmux CLI syntax, wait/snapshot/ref workflow guidance, common automation flows, and command mapping references. +- 2026-02-13: Added new repo skill `skills/programa-browser/` adapted from `vercel-labs/agent-browser` with programa CLI syntax, wait/snapshot/ref workflow guidance, common automation flows, and command mapping references. - 2026-02-13: Kept browser favicons in color when a pane/tab bar is unfocused by excluding raster tab icons from inactive saturation in Bonsplit tab rendering. Added regression coverage in `vendor/bonsplit/Tests/BonsplitTests/BonsplitTests.swift`. - 2026-02-13: Browser agent-UX follow-up: `browser fill` now accepts empty text for clear operations, legacy `new-pane`/`new-surface` output now prefers short `surface:N` refs, and mutating browser actions gained optional post-action verification snapshots via `snapshot_after` (`--snapshot-after` in CLI). Added regression coverage in `tests_v2/test_browser_api_comprehensive.py` and `tests_v2/test_browser_cli_agent_port.py`. - 2026-02-13: Final titlebar hint vertical micro-adjustment: moved `Cmd+B`/`Cmd+I`/`Cmd+N` pills up by 1px for pixel-level alignment. @@ -29,7 +29,7 @@ Cross-project tracking (features, bugs, backlog) for cmux. - 2026-02-12: Fixed titlebar shortcut-hint layout shift by keeping all titlebar hint pills on one Y row and resolving collisions horizontally, added accessibility identifiers for titlebar controls/hints, and added VM UI regression coverage (`TitlebarShortcutHintsUITests`) asserting aligned Y + no control-frame shift when hints are always shown. - 2026-02-12: Added a top-level macOS menu bar `Notifications` menu (main app menu, not the status-item extra) with parity actions/inline recent notification rows (show, jump latest unread, mark all read, clear all, open row). Extracted shared notification menu snapshot logic so both the main menu and menu bar extra reuse the same unread counting/state text/recent-item selection behavior, and added unit regression coverage. - 2026-02-12: Shortcut-hint visibility pass: added collision-aware lane layout for titlebar shortcut pills to prevent overlap, unified stronger blur-backed pill styling across sidebar/titlebar/pane hints, and moved pane `Ctrl/Cmd+1..9` hints into a centered trailing slot aligned with tab close affordances/text. Added unit regression coverage for lane assignment logic. -- 2026-02-12: Fixed terminal file/image drop regression in cmux by restoring AppKit drag destination handling on `GhosttyNSView` (register drop types + perform drop insertion), and added `tests/test_file_drop_paths.py` e2e regression coverage via debug socket `simulate_file_drop` to verify dropped paths are inserted shell-escaped. +- 2026-02-12: Fixed terminal file/image drop regression in Programa by restoring AppKit drag destination handling on `GhosttyNSView` (register drop types + perform drop insertion), and added `tests/test_file_drop_paths.py` e2e regression coverage via debug socket `simulate_file_drop` to verify dropped paths are inserted shell-escaped. - 2026-02-12: Sidebar drag behavior polish: edge auto-scroll now continues when pointer leaves the scroll area/window vertically during drag, and added a top sidebar blur scrim under traffic lights/titlebar controls to prevent scrolled workspace rows from visually bleeding behind the controls. - 2026-02-12: Sidebar drag auto-scroll follow-up: improved edge scrolling when cursor leaves row drop targets by keeping drag-scroll active outside rows, allowing bounded out-of-viewport scrolling, and consulting AppKit `autoscroll(with:)` behavior for drag events. - 2026-02-12: Moved titlebar shortcut hints (sidebar/bell/plus) downward so pills render below the icon buttons, with a small style-aware base Y offset plus existing debug offset tuning. @@ -41,7 +41,7 @@ Cross-project tracking (features, bugs, backlog) for cmux. - 2026-02-12: Sidebar workspace drag/drop indicator now suppresses no-op edge placements (no blue border for first-before-first, last-after-last, self-drop, or single-workspace cases). Added unit regression tests for drop-indicator planning edge cases. - 2026-02-12: Added debug-tunable X/Y offsets for keyboard shortcut hint badges across sidebar workspace hints (`Cmd+1..9`), titlebar control hints (sidebar/bell/plus shortcut labels), and Bonsplit pane tab hints (`Ctrl/Cmd+1..9`). Exposed these in `Sidebar Debug` with reset/copy support, and included the new fields in combined debug snapshot output. - 2026-02-12: Added sidebar workspace reordering via drag-and-drop, including accessibility reorder actions (`Move Up` / `Move Down`) and context-menu move actions for keyboard/AX users. Added unit regression coverage for `TabManager.reorderWorkspace`, and tuned sidebar workspace title typography to semibold `12.5`pt. -- 2026-02-12: Added a grouped `Debug > Debug Windows` menu in the app (Sidebar, Background, Menu Bar Extra, plus `Open All Debug Windows`) and created a repo-local `cmux-debug-windows` skill with a helper snapshot script (`skills/cmux-debug-windows/scripts/debug_windows_snapshot.sh`) for combined copyable debug settings payloads. +- 2026-02-12: Added a grouped `Debug > Debug Windows` menu in the app (Sidebar, Background, Menu Bar Extra, plus `Open All Debug Windows`) and created a repo-local `programa-debug-windows` skill with a helper snapshot script (`skills/programa-debug-windows/scripts/debug_windows_snapshot.sh`) for combined copyable debug settings payloads. - 2026-02-12: Expanded shortcut hint overlays: Command-hold hints now wait longer before showing and include titlebar controls using current user-configured shortcuts (Toggle Sidebar, Show Notifications, New Workspace). Added focused-pane Bonsplit tab hints for `Ctrl+1..9` (with `9` mapped to the last tab) using intentional Control-only hold behavior. - 2026-02-12: Refined sidebar `Cmd+digit` hint UX: hint badge now renders on the close-button Y level without row layout shift, and hints only appear after an intentional Command-only hold (short delay + canceled when any non-modifier key is pressed). Added unit regression coverage for the Command-only hint policy. - 2026-02-12: Added a `New workspace placement` preference (`Top`, `After current`, `End`) in Settings, defaulting to `After current` for fresh installs. Workspace creation now follows this setting across app/menu/shortcut/service/socket entry points, and unit regression tests cover default preference + insertion-index behavior. @@ -49,12 +49,12 @@ Cross-project tracking (features, bugs, backlog) for cmux. - 2026-02-12: Sidebar now shows `Cmd+digit` badges on workspace rows while the Command key is held, and workspace shortcut routing is unified so `Cmd+9` always targets the last workspace (with unit regression coverage for the mapping). - 2026-02-12: Moved the update pill/button and `THIS IS A DEV BUILD` indicator from the titlebar into a bottom-left footer inside the sidebar (debug builds), so update/dev status lives with sidebar UI instead of titlebar accessories. - 2026-02-12: Sidebar update control now uses the update pill in debug builds (non-idle update states only), and the checking spinner matches the browser tab loading spinner style. -- 2026-02-12: Added macOS Finder/Services “here” integrations for cmux: `New … Workspace Here` (maps to `openTab`) and `New … Window Here` (maps to `openWindow`), with path dedupe/normalization and explicit working-directory routing for both new windows and new workspaces. Added unit regression tests for service path resolution. -- 2026-02-12: Menu bar extra now includes a `Quit cmux` action, and menu bar badge tuning defaults were updated to the latest shared payload values (with backward-compatible support for legacy `menubarDebugTextRectXAdjust`). +- 2026-02-12: Added macOS Finder/Services "here" integrations for Programa: `New … Workspace Here` (maps to `openTab`) and `New … Window Here` (maps to `openWindow`), with path dedupe/normalization and explicit working-directory routing for both new windows and new workspaces. Added unit regression tests for service path resolution. +- 2026-02-12: Menu bar extra now includes a `Quit Programa` action, and menu bar badge tuning defaults were updated to the latest shared payload values (with backward-compatible support for legacy `menubarDebugTextRectXAdjust`). - 2026-02-12: Added a Debug menu window for menu bar extra tuning (preview unread count override, live badge position/size controls, and one-click copy payload for sharing exact values). - 2026-02-12: Expanded menu bar extra debug controls with separate 2-digit X/Y positioning so single-digit and two-digit badge text can be tuned independently. -- 2026-02-12: Refined menu bar extra UX: inserted a separator between inline notification rows and action items, and adjusted the unread count glyph in the status icon (bigger, higher, slightly left) while keeping the white cmux icon and fixed-width layout. -- 2026-02-12: Menu bar extra now shows a dev-only build hint line (`Build Tag: ` for tagged reloads, `Build: DEV (untagged)` otherwise) so parallel `cmux DEV` instances are distinguishable from the menu. +- 2026-02-12: Refined menu bar extra UX: inserted a separator between inline notification rows and action items, and adjusted the unread count glyph in the status icon (bigger, higher, slightly left) while keeping the white Programa icon and fixed-width layout. +- 2026-02-12: Menu bar extra now shows a dev-only build hint line (`Build Tag: ` for tagged reloads, `Build: DEV (untagged)` otherwise) so parallel `Programa DEV` instances are distinguishable from the menu. - 2026-02-12: Added a right-side menu bar extra with a dynamic unread badge on the icon plus quick actions for notifications (show/jump/mark-read/clear), check for updates, and preferences. - 2026-02-12: Added unread notification count badges on the app icon (Dock/Cmd+Tab) with a new Settings toggle to disable it; added unit regression coverage for badge labeling and default preference behavior. - 2026-02-12: Fixed browser-pane split shortcuts so `Cmd+D` and `Cmd+Shift+D` work from both `WKWebView` and the browser omnibar by adding menu-backed split commands wired to effective shortcut settings and shared split handling in `AppDelegate`. Added VM UI regression coverage in `BrowserPaneNavigationKeybindUITests` for both focus states. @@ -87,19 +87,19 @@ Cross-project tracking (features, bugs, backlog) for cmux. - 2026-02-10: Browser address bar search now uses a configurable default search engine (Google, DuckDuckGo, Bing) and shows an omnibar dropdown (history + optional remote suggestions); added unit + UI tests (alignment, Ctrl+N/P). Also added Cmd+R reload and default Safari UA to avoid Google fallback/bot checks. - 2026-02-10: Browser loading UI: removed omnibar progress indicator and replaced it with a spinning tab icon while the page is loading. - 2026-02-10: Browser omnibar: added an explicit state machine (focus/editing/popup) so Escape and click-outside behaviors match Chrome; added regression tests. -- 2026-02-10: Added a customizable “Flash focused panel” keyboard shortcut (default Cmd+Shift+L) that visually highlights the currently focused terminal or browser panel. -- 2026-02-10: Added PostHog Swift SDK integration and a stable DAU signal (`cmux_daily_active`, once per UTC day per install). +- 2026-02-10: Added a customizable "Flash focused panel" keyboard shortcut (default Cmd+Shift+L) that visually highlights the currently focused terminal or browser panel. +- 2026-02-10: Added PostHog Swift SDK integration and a stable DAU signal (`programa_daily_active`, once per UTC day per install). - 2026-02-10: Added a v2 JSON socket API (handle-based) and migrated the automated test suite to v2 while keeping v1 compatibility. Verified v1 + v2 suites passing on the VM (see `docs/v2-api-migration.md`). - 2026-02-11: Extended the socket/CLI to handle multi-window automation: added v2 `window.list/current/focus/create/close`, v2 `workspace.move_to_window`, and included `window_id` in `system.identify` (plus caller validation). Added v1 window commands for the CLI (`list_windows`, etc). Added VM test coverage (`tests_v2/test_windows_api.py`) and verified v1 + v2 suites passing on the VM. - 2026-02-11: Fixed split child-exit close semantics and focus indicator drift: exiting (`Ctrl+D`) one side of a split now only closes that pane (never the whole workspace due to transient panel-count state), and terminal first-responder focus now always re-syncs active bonsplit focus so blue focus indicators match actual keyboard focus. Added VM UI regression coverage for child-exit-in-split behavior. -- 2026-02-11: Exposed v2 agent discovery from the CLI: added `cmux identify` (maps to `system.identify`, supports caller via env/flags) and `cmux capabilities` (maps to `system.capabilities`). +- 2026-02-11: Exposed v2 agent discovery from the CLI: added `programa identify` (maps to `system.identify`, supports caller via env/flags) and `programa capabilities` (maps to `system.capabilities`). - 2026-02-11: Expanded CLI split/pane coverage for agent workflows: added `list-panes`, `list-pane-surfaces`, `focus-pane`, `new-pane`, `new-surface`, `close-surface`, `drag-surface-to-split`, `refresh-surfaces`, `surface-health`, `focus-webview`, `is-webview-focused`, and `trigger-flash` (`surface.trigger_flash`). ## Backlog - Browser panels: investigate intermittent crash/relaunch around WKWebView lifecycle and focus notifications. - Keyboard shortcuts: expand VM XCUITest coverage for focus + shortcuts (once Automation Mode is reliably enabled in the VM). - Socket API: tighten/standardize semantics around split insertion side (left/right/up/down) and pane selection (UUID vs index) across CLI/docs/server. -- CLI: add an `it2`-compatible CLI shim (same subcommands/flags where feasible) that maps to cmux's socket API and ships in `Contents/Resources/bin`. -- Browser automation parity: implement `docs/agent-browser-port-spec.md` (agent-browser command mapping, `cmux browser` surface targeting, move/reorder invariants, and v1 shim strategy). +- CLI: add an `it2`-compatible CLI shim (same subcommands/flags where feasible) that maps to Programa's socket API and ships in `Contents/Resources/bin`. +- Browser automation parity: implement `docs/agent-browser-port-spec.md` (agent-browser command mapping, `programa browser` surface targeting, move/reorder invariants, and v1 shim strategy). - Tests: port the agent-browser coverage matrix into `tests_v2/` while keeping both v1 and v2 suites passing. -- Planning: agent-browser port spec decisions locked (ID refs, caller-relative placement, cmux-native output, refs-first output defaults). +- Planning: agent-browser port spec decisions locked (ID refs, caller-relative placement, programa-native output, refs-first output defaults). diff --git a/README.md b/README.md index 577a7dc07a8..f1a1c5e2600 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -

cmux

+

Programa

A Ghostty-based macOS terminal with vertical tabs and notifications for AI coding agents

- - Download cmux for macOS + + Download Programa for macOS

@@ -12,17 +12,17 @@

- X / Twitter + X / Twitter Discord - GitHub stars + GitHub stars

- cmux screenshot + Programa screenshot

- ▶ Demo video · The Zen of cmux + ▶ Demo video

## Features @@ -67,16 +67,16 @@ Sidebar shows git branch, linked PR status/number, working directory, listening

SSH

-cmux ssh user@remote creates a workspace for a remote machine. Browser panes route through the remote network so localhost just works. Drag an image into a remote session to upload via scp. +programa ssh user@remote creates a workspace for a remote machine. Browser panes route through the remote network so localhost just works. Drag an image into a remote session to upload via scp. -cmux SSH +Programa SSH

Claude Code Teams

-cmux claude-teams runs Claude Code's teammate mode with one command. Teammates spawn as native splits with sidebar metadata and notifications. No tmux required. +programa claude-teams runs Claude Code's teammate mode with one command. Teammates spawn as native splits with sidebar metadata and notifications. No tmux required. Claude Code Teams @@ -85,7 +85,7 @@ Sidebar shows git branch, linked PR status/number, working directory, listening - **Browser import** — Import cookies, history, and sessions from Chrome, Firefox, Arc, and 20+ browsers so browser panes start authenticated -- **Custom commands** — Define project-specific actions in [`cmux.json`](https://cmux.com/docs/custom-commands) that launch from the command palette +- **Custom commands** — Define project-specific actions in `programa.json` that launch from the command palette - **Scriptable** — CLI and socket API to create workspaces, split panes, send keystrokes, and automate the browser - **Native macOS app** — Built with Swift and AppKit, not Electron. Fast startup, low memory. - **Ghostty compatible** — Reads your existing `~/.config/ghostty/config` for themes, fonts, and colors @@ -95,44 +95,44 @@ Sidebar shows git branch, linked PR status/number, working directory, listening ### DMG (recommended) - - Download cmux for macOS + + Download Programa for macOS -Open the `.dmg` and drag cmux to your Applications folder. cmux auto-updates via Sparkle, so you only need to download once. +Open the `.dmg` and drag Programa to your Applications folder. Programa auto-updates via Sparkle, so you only need to download once. ### Homebrew ```bash -brew tap manaflow-ai/cmux -brew install --cask cmux +brew tap darkroomengineering/programa +brew install --cask programa ``` To update later: ```bash -brew upgrade --cask cmux +brew upgrade --cask programa ``` On first launch, macOS may ask you to confirm opening an app from an identified developer. Click **Open** to proceed. -## Why cmux? +## Why Programa? I run a lot of Claude Code and Codex sessions in parallel. I was using Ghostty with a bunch of split panes, and relying on native macOS notifications to know when an agent needed me. But Claude Code's notification body is always just "Claude is waiting for your input" with no context, and with enough tabs open I couldn't even read the titles anymore. -I tried a few coding orchestrators but most of them were Electron/Tauri apps and the performance bugged me. I also just prefer the terminal since GUI orchestrators lock you into their workflow. So I built cmux as a native macOS app in Swift/AppKit. It uses libghostty for terminal rendering and reads your existing Ghostty config for themes, fonts, and colors. +I tried a few coding orchestrators but most of them were Electron/Tauri apps and the performance bugged me. I also just prefer the terminal since GUI orchestrators lock you into their workflow. So I built Programa as a native macOS app in Swift/AppKit. It uses libghostty for terminal rendering and reads your existing Ghostty config for themes, fonts, and colors. -The main additions are the sidebar and notification system. The sidebar has vertical tabs that show git branch, linked PR status/number, working directory, listening ports, and the latest notification text for each workspace. The notification system picks up terminal sequences (OSC 9/99/777) and has a CLI (`cmux notify`) you can wire into agent hooks for Claude Code, OpenCode, etc. When an agent is waiting, its pane gets a blue ring and the tab lights up in the sidebar, so I can tell which one needs me across splits and tabs. Cmd+Shift+U jumps to the most recent unread. +The main additions are the sidebar and notification system. The sidebar has vertical tabs that show git branch, linked PR status/number, working directory, listening ports, and the latest notification text for each workspace. The notification system picks up terminal sequences (OSC 9/99/777) and has a CLI (`programa notify`) you can wire into agent hooks for Claude Code, OpenCode, etc. When an agent is waiting, its pane gets a blue ring and the tab lights up in the sidebar, so I can tell which one needs me across splits and tabs. Cmd+Shift+U jumps to the most recent unread. The in-app browser has a scriptable API ported from [agent-browser](https://github.com/vercel-labs/agent-browser). Agents can snapshot the accessibility tree, get element refs, click, fill forms, and evaluate JS. You can split a browser pane next to your terminal and have Claude Code interact with your dev server directly. Everything is scriptable through the CLI and socket API — create workspaces/tabs, split panes, send keystrokes, open URLs in the browser. -## The Zen of cmux +## The Zen of Programa -cmux is not prescriptive about how developers hold their tools. It's a terminal and browser with a CLI, and the rest is up to you. +Programa is not prescriptive about how developers hold their tools. It's a terminal and browser with a CLI, and the rest is up to you. -cmux is a primitive, not a solution. It gives you a terminal, a browser, notifications, workspaces, splits, tabs, and a CLI to control all of it. cmux doesn't force you into an opinionated way to use coding agents. What you build with the primitives is yours. +Programa is a primitive, not a solution. It gives you a terminal, a browser, notifications, workspaces, splits, tabs, and a CLI to control all of it. Programa doesn't force you into an opinionated way to use coding agents. What you build with the primitives is yours. The best developers have always built their own tools. Nobody has figured out the best way to work with agents yet, and the teams building closed products definitely haven't either. The developers closest to their own codebases will figure it out first. @@ -140,7 +140,7 @@ Give a million developers composable primitives and they'll collectively find th ## Documentation -For more info on how to configure cmux, [head over to our docs](https://cmux.com/docs/getting-started?utm_source=readme). +For more info on how to configure Programa, head over to our docs. ## Keyboard Shortcuts @@ -230,65 +230,39 @@ Browser developer-tool shortcuts follow Safari defaults and are customizable in ## Nightly Builds -[Download cmux NIGHTLY](https://github.com/darkroomengineering/programa/releases/download/nightly/cmux-nightly-macos.dmg) +[Download Programa NIGHTLY](https://github.com/darkroomengineering/programa/releases/download/nightly/programa-nightly-macos.dmg) -cmux NIGHTLY is a separate app with its own bundle ID, so it runs alongside the stable version. Built automatically from the latest `main` commit and auto-updates via its own Sparkle feed. +Programa NIGHTLY is a separate app with its own bundle ID, so it runs alongside the stable version. Built automatically from the latest `main` commit and auto-updates via its own Sparkle feed. -Report nightly bugs on [GitHub Issues](https://github.com/manaflow-ai/cmux/issues) or in [#nightly-bugs on Discord](https://discord.gg/xsgFEVrWCZ). +Report nightly bugs on [GitHub Issues](https://github.com/darkroomengineering/programa/issues) or in [#nightly-bugs on Discord](https://discord.gg/xsgFEVrWCZ). ## Session restore (current behavior) -On relaunch, cmux currently restores app layout and metadata only: +On relaunch, Programa currently restores app layout and metadata only: - Window/workspace/pane layout - Working directories - Terminal scrollback (best effort) - Browser URL and navigation history -cmux does **not** restore live process state inside terminal apps. For example, active Claude Code/tmux/vim sessions are not resumed after restart yet. - -## Star History - - - - - - Star History Chart - - +Programa does **not** restore live process state inside terminal apps. For example, active Claude Code/tmux/vim sessions are not resumed after restart yet. ## Contributing Ways to get involved: -- Follow us on X for updates [@manaflowai](https://x.com/manaflowai), [@lawrencecchen](https://x.com/lawrencecchen), and [@austinywang](https://x.com/austinywang) +- Follow us on X for updates [@darkroomengineering](https://x.com/darkroomengineering) - Join the conversation on [Discord](https://discord.gg/xsgFEVrWCZ) -- Create and participate in [GitHub issues](https://github.com/manaflow-ai/cmux/issues) and [discussions](https://github.com/manaflow-ai/cmux/discussions) -- Let us know what you're building with cmux +- Create and participate in [GitHub issues](https://github.com/darkroomengineering/programa/issues) and [discussions](https://github.com/darkroomengineering/programa/discussions) +- Let us know what you're building with Programa ## Community - [Discord](https://discord.gg/xsgFEVrWCZ) -- [GitHub](https://github.com/manaflow-ai/cmux) -- [X / Twitter](https://twitter.com/manaflowai) -- [YouTube](https://www.youtube.com/channel/UCAa89_j-TWkrXfk9A3CbASw) -- [LinkedIn](https://www.linkedin.com/company/manaflow-ai/) -- [Reddit](https://www.reddit.com/r/cmux/) - -## Founder's Edition - -cmux is free, open source, and always will be. If you'd like to support development and get early access to what's coming next: - -**[Get Founder's Edition](https://buy.stripe.com/3cI00j2Ld0it5OU33r5EY0q)** - -- **Prioritized feature requests/bug fixes** -- **Early access: cmux AI that gives you context on every workspace, tab and panel** -- **Early access: iOS app with terminals synced between desktop and phone** -- **Early access: Cloud VMs** -- **Early access: Voice mode** -- **My personal iMessage/WhatsApp** +- [GitHub](https://github.com/darkroomengineering/programa) +- [X / Twitter](https://twitter.com/darkroomengineering) ## License -cmux is open source under [GPL-3.0-or-later](LICENSE). +Programa is open source under [GPL-3.0-or-later](LICENSE). -If your organization cannot comply with GPL, a commercial license is available. Contact [founders@manaflow.com](mailto:founders@manaflow.com) for details. +Programa is a GPL-3.0 fork of [cmux](https://github.com/manaflow-ai/cmux) by Manaflow, Inc. Modifications © Darkroom Engineering. diff --git a/THIRD_PARTY_LICENSES.md b/THIRD_PARTY_LICENSES.md index fd1d9e2f70d..d25e839cb62 100644 --- a/THIRD_PARTY_LICENSES.md +++ b/THIRD_PARTY_LICENSES.md @@ -1,6 +1,6 @@ # Third-Party Licenses -cmux includes the following third-party software: +Programa includes the following third-party software: --- diff --git a/TODO.md b/TODO.md index 5453b8f5960..ad76b27721a 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,13 @@ # TODO ## Issue 151: Remote SSH (Living Execution) -- [x] `cmux ssh` creates remote workspace metadata and does not require `--name` -- [x] Remote daemon bootstrap/upload/start path with `cmuxd-remote serve --stdio` +- [x] `programa ssh` creates remote workspace metadata and does not require `--name` +- [x] Remote daemon bootstrap/upload/start path with `programad-remote serve --stdio` - [x] Reconnect/disconnect controls (CLI/API/context menu) + improved error surfacing - [x] Retry count/time surfaced in remote daemon/probe error details - [ ] Remove automatic remote service port mirroring (`ssh -L` from detected remote listening ports) - [ ] Add transport-scoped proxy broker (SOCKS5 + HTTP CONNECT) for remote traffic -- [ ] Extend `cmuxd-remote` RPC beyond `hello/ping` with proxy stream methods (`proxy.open|close`) +- [ ] Extend `programad-remote` RPC beyond `hello/ping` with proxy stream methods (`proxy.open|close`) - [ ] Auto-wire WKWebView in remote workspaces to proxy via `WKWebsiteDataStore.proxyConfigurations` - [ ] Add browser proxy e2e tests (remote egress IP, websocket, reconnect continuity) - [ ] Implement PTY resize coordinator with tmux semantics (`smallest screen wins`) @@ -16,15 +16,15 @@ ## Socket API / Agent - [x] Add window handles + `window.list/current/focus/create/close` for multi-window socket control (v2) + v1 equivalents (`list_windows`, etc) + CLI support. - [x] Add surface move/reorder commands (move between panes, reorder within pane, move across workspaces/windows). -- [x] Add browser automation API inspired by `vercel-labs/agent-browser`, but backed by cmux's WKWebView (wait, click, type, eval, screenshot, etc.). +- [x] Add browser automation API inspired by `vercel-labs/agent-browser`, but backed by Programa's WKWebView (wait, click, type, eval, screenshot, etc.). - [x] Finalize browser parity contract and command mapping decisions in `docs/agent-browser-port-spec.md`. -- [x] Add `cmux browser` command surface that mirrors agent-browser semantics and targets explicit `surface_id` handles. +- [x] Add `programa browser` command surface that mirrors agent-browser semantics and targets explicit `surface_id` handles. - [x] Add short handle refs (`surface:N`, `pane:N`, `workspace:N`, `window:N`) and CLI `--id-format refs|uuids|both` output control. - [x] Add v1->v2 compatibility shim for migrated browser/topology commands while v1 remains supported. - [x] Port browser automation coverage to `tests_v2/` per `docs/agent-browser-port-spec.md` and keep v1 + v2 suites green. - Added `tests_v2/test_browser_api_comprehensive.py`, `tests_v2/test_browser_api_p0.py`, `tests_v2/test_browser_api_extended_families.py`, `tests_v2/test_browser_api_unsupported_matrix.py`, and `tests_v2/test_browser_cli_agent_port.py`. - Full VM runs: `./scripts/run-tests-v1.sh` and `./scripts/run-tests-v2.sh` passing (v2 visual D12 remains reported as a known non-blocking VM failure, matching v1 policy). -- [x] Fix `cmux browser open|open-split|new` URL parsing so routing flags (`--workspace`, `--window`) are removed before URL construction. +- [x] Fix `programa browser open|open-split|new` URL parsing so routing flags (`--workspace`, `--window`) are removed before URL construction. - [x] Fix `identify --workspace/--surface` caller parsing to honor ref handles (`workspace:N`, `surface:N`) instead of falling back to current/focused IDs. - [x] Update `browser.open_split` placement policy: reuse nearest right sibling pane first (nested-aware), only create a new split when caller has no right sibling. - [x] Upgrade `browser.snapshot` to agent-browser-style output (`snapshot` tree text + `refs`) and make non-JSON CLI output print snapshot content instead of `OK`. @@ -34,7 +34,7 @@ - [x] Make legacy `new-pane`/`new-surface` CLI output prefer short `surface:N` refs by default. - [x] Add optional `--snapshot-after` / `snapshot_after` action feedback to include a fresh post-action browser snapshot. - [x] Switch CLI `--json` default ID output to refs-first (UUIDs only via `--id-format uuids|both`) and add regression coverage. -- [x] Expand end-user skill docs with deep-linkable cmux-browser references/templates plus a new core `skills/cmux/` topology skill. +- [x] Expand end-user skill docs with deep-linkable programa-browser references/templates plus a new core `skills/programa/` topology skill. ## Command Palette - [ ] Add cmd+shift+p palette with all commands @@ -47,7 +47,7 @@ - Opens a new terminal - Shows user the diff to their config file (claude.json, opencode config, codex config, etc.) - Prompts user to type 'y' to confirm - - Implement as part of `cmux` CLI, menubar just triggers the CLI command + - Implement as part of `programa` CLI, menubar just triggers the CLI command ## Additional Integrations - [ ] Codex integration @@ -66,7 +66,7 @@ ## Refactoring - [ ] **P0** Remove all index-based APIs in favor of short ID refs (surface:N, pane:N, workspace:N, window:N) -- [ ] **P0** CLI commands should be workspace-relative using CMUX_WORKSPACE_ID env var (not focused workspace) so agents in background workspaces don't affect the user's active workspace. Affected: send, send-key, send-panel, send-key-panel, new-split, new-pane, new-surface, close-surface, list-panes, list-pane-surfaces, list-panels, focus-pane, focus-panel, surface-health +- [ ] **P0** CLI commands should be workspace-relative using PROGRAMA_WORKSPACE_ID env var (not focused workspace) so agents in background workspaces don't affect the user's active workspace. Affected: send, send-key, send-panel, send-key-panel, new-split, new-pane, new-surface, close-surface, list-panes, list-pane-surfaces, list-panels, focus-pane, focus-panel, surface-health - [ ] **P0** Remove `close-workspace` with no args — require explicit workspace short ID or UUID, with clear error message if missing ## UI/UX Improvements @@ -75,7 +75,7 @@ - [ ] Notification popover: each button item should show outline outside when focused/hovered - [ ] Notification popover: add right-click context menu to mark as read/unread - [ ] Right-click tab should allow renaming that workspace -- [ ] Cmd+click should open links in cmux (browser panel) instead of external browser +- [ ] Cmd+click should open links in Programa (browser panel) instead of external browser - [ ] "Waiting for input" notification should include custom terminal title if set - [ ] Close button for current/active tab should always be visible (not just on hover) - [ ] Add browser icon to the left of the plus button in the tab bar @@ -121,7 +121,7 @@ - `browser.network.route|unroute|requests` - `browser.screencast.start|stop` - `browser.input_mouse|input_keyboard|input_touch` -- [x] Extend `cmux browser ...` CLI grammar for the new families (including aliases). +- [x] Extend `programa browser ...` CLI grammar for the new families (including aliases). - [x] Port/add v2 tests for all newly implemented families. - [x] Update unsupported matrix tests to assert `not_supported` for hard platform gaps (instead of `method_not_found`). -- [x] Re-run full `run-tests-v1.sh` and `run-tests-v2.sh` on `cmux-vm`. +- [x] Re-run full `run-tests-v1.sh` and `run-tests-v2.sh` on `programa-vm`. diff --git a/daemon/remote/README.md b/daemon/remote/README.md index 9bf4c758eaa..de077c9526f 100644 --- a/daemon/remote/README.md +++ b/daemon/remote/README.md @@ -1,14 +1,14 @@ -# cmuxd-remote (Go) +# programad-remote (Go) -Go remote daemon for `cmux ssh` bootstrap, capability negotiation, and remote proxy RPC. It is not in the terminal keystroke hot path. +Go remote daemon for `programa ssh` bootstrap, capability negotiation, and remote proxy RPC. It is not in the terminal keystroke hot path. ## Commands -1. `cmuxd-remote version` -2. `cmuxd-remote serve --stdio` -3. `cmuxd-remote cli [args...]` — relay cmux commands to the local app over the reverse SSH forward +1. `programad-remote version` +2. `programad-remote serve --stdio` +3. `programad-remote cli [args...]` — relay programa commands to the local app over the reverse SSH forward -When invoked as `cmux` (via wrapper/symlink installed during bootstrap), the binary auto-dispatches to the `cli` subcommand. This is busybox-style argv[0] detection. +When invoked as `programa` (via wrapper/symlink installed during bootstrap), the binary auto-dispatches to the `cli` subcommand. This is busybox-style argv[0] detection. ## RPC methods (newline-delimited JSON over stdio) @@ -26,7 +26,7 @@ When invoked as `cmux` (via wrapper/symlink installed during bootstrap), the bin 12. `session.detach` 13. `session.status` -Current integration in cmux: +Current integration in programa: 1. `workspace.remote.configure` now bootstraps this binary over SSH when missing. 2. Client sends `hello` before enabling remote proxy transport. 3. Local workspace proxy broker serves SOCKS5 + HTTP CONNECT and tunnels stream traffic through `proxy.*` RPC over `serve --stdio`, using daemon-pushed stream events instead of polling reads. @@ -40,7 +40,7 @@ Current integration in cmux: ## Distribution -Release and nightly builds publish prebuilt `cmuxd-remote` binaries on GitHub Releases for: +Release and nightly builds publish prebuilt `programad-remote` binaries on GitHub Releases for: 1. `darwin/arm64` 2. `darwin/amd64` 3. `linux/arm64` @@ -51,33 +51,33 @@ The app embeds a compact manifest in `Info.plist` with: 2. pinned SHA-256 digests 3. release tag and checksums asset URL -Release and nightly apps download and cache the matching binary locally, verify its SHA-256, then upload it to the remote host if needed. Dev builds can opt into a local `go build` fallback with `CMUX_REMOTE_DAEMON_ALLOW_LOCAL_BUILD=1`. +Release and nightly apps download and cache the matching binary locally, verify its SHA-256, then upload it to the remote host if needed. Dev builds can opt into a local `go build` fallback with `PROGRAMA_REMOTE_DAEMON_ALLOW_LOCAL_BUILD=1`. To inspect what a given app build trusts, run: -1. `cmux remote-daemon-status` +1. `programa remote-daemon-status` 2. `cmux remote-daemon-status --os linux --arch amd64` The command prints the exact release asset URL, expected SHA-256, local cache status, and a copy-pasteable `gh attestation verify` command for the selected platform. ## CLI relay -The `cli` subcommand (or `cmux` wrapper/symlink) connects to the local cmux app through an SSH reverse forward and relays commands. It supports both v1 text protocol and v2 JSON-RPC commands. +The `cli` subcommand (or `programa` wrapper/symlink) connects to the local programa app through an SSH reverse forward and relays commands. It supports both v1 text protocol and v2 JSON-RPC commands. Socket discovery order: 1. `--socket ` flag -2. `CMUX_SOCKET_PATH` environment variable -3. `~/.cmux/socket_addr` file (written by the app after the reverse relay establishes) +2. `PROGRAMA_SOCKET_PATH` environment variable +3. `~/.programa/socket_addr` file (written by the app after the reverse relay establishes) -For TCP addresses, the CLI dials once and only refreshes `~/.cmux/socket_addr` a single time if the first address was stale. Relay metadata is published only after the reverse forward is ready, so steady-state use does not rely on polling. +For TCP addresses, the CLI dials once and only refreshes `~/.programa/socket_addr` a single time if the first address was stale. Relay metadata is published only after the reverse forward is ready, so steady-state use does not rely on polling. Authenticated relay details: 1. Each SSH workspace gets its own relay ID and relay token. 2. The app runs a local loopback relay server that requires an HMAC-SHA256 challenge-response before forwarding a command to the real local Unix socket. -3. The remote shell never gets direct access to the local app socket. It only gets the reverse-forwarded relay port plus `~/.cmux/relay/.auth`, which is written with `0600` permissions and removed when the relay stops. +3. The remote shell never gets direct access to the local app socket. It only gets the reverse-forwarded relay port plus `~/.programa/relay/.auth`, which is written with `0600` permissions and removed when the relay stops. Integration additions for the relay path: -1. Bootstrap installs `~/.cmux/bin/cmux` wrapper and keeps a default daemon target (`~/.cmux/bin/cmuxd-remote-current`). -2. A background `ssh -N -R` process reverse-forwards a TCP port to the authenticated local relay server. The relay address is written to `~/.cmux/socket_addr` on the remote. -3. Relay startup writes `~/.cmux/relay/.daemon_path` so the wrapper can route each shell to the correct daemon binary when multiple local cmux instances or versions coexist. -4. Relay startup writes `~/.cmux/relay/.auth` with the relay ID and token needed for HMAC authentication. +1. Bootstrap installs `~/.programa/bin/programa` wrapper and keeps a default daemon target (`~/.programa/bin/programad-remote-current`). +2. A background `ssh -N -R` process reverse-forwards a TCP port to the authenticated local relay server. The relay address is written to `~/.programa/socket_addr` on the remote. +3. Relay startup writes `~/.programa/relay/.daemon_path` so the wrapper can route each shell to the correct daemon binary when multiple local programa instances or versions coexist. +4. Relay startup writes `~/.programa/relay/.auth` with the relay ID and token needed for HMAC authentication. diff --git a/docs/agent-browser-port-spec.md b/docs/agent-browser-port-spec.md index de89a69d7c7..e81eb58e28a 100644 --- a/docs/agent-browser-port-spec.md +++ b/docs/agent-browser-port-spec.md @@ -3,11 +3,11 @@ Last updated: February 13, 2026 Source inventory snapshot: `vercel-labs/agent-browser` @ `03a8cb9` -This document tracks implemented behavior and remaining parity gaps for the cmux browser port. +This document tracks implemented behavior and remaining parity gaps for the Programa browser port. ## Goals -1. Provide an LLM-friendly browser automation API in cmux with stable handles. +1. Provide an LLM-friendly browser automation API in Programa with stable handles. 2. Keep v1 CLI/socket behavior working while v2 reaches full parity. 3. Port `agent-browser` command surface (where meaningful for `WKWebView`). 4. Ensure move/reorder operations preserve `surface_id` identity. @@ -16,8 +16,8 @@ This document tracks implemented behavior and remaining parity gaps for the cmux ## Validation Status As of February 12, 2026: -1. `./scripts/run-tests-v1.sh` passes on `cmux-vm`. -2. `./scripts/run-tests-v2.sh` passes on `cmux-vm`. +1. `./scripts/run-tests-v1.sh` passes on `programa-vm`. +2. `./scripts/run-tests-v2.sh` passes on `programa-vm`. 3. Browser parity suites passing in v2: `test_browser_api_comprehensive.py`, `test_browser_api_p0.py`, `test_browser_api_extended_families.py`, `test_browser_api_unsupported_matrix.py`, and `test_browser_cli_agent_port.py`. 4. Visual suite note: `tests/test_visual_screenshots.py` and `tests_v2/test_visual_screenshots.py` both report D12 (`Nested: Close Top of T-shape`) as a known non-blocking VM failure when it reproduces (`VIEW_DETACHED`). @@ -186,9 +186,9 @@ Protocol-only action names: 31. `video_start` 32. `video_stop` -## cmux Target API (v2) +## Programa Target API (v2) -### Already Present in cmux +### Already Present in Programa 1. `system.ping` 2. `system.capabilities` @@ -226,11 +226,11 @@ P1 (important but not blocking initial parity): 3. `browser.frame.main` 4. `browser.dialog.respond` 5. `browser.download.wait` -6. `browser.tab.*` compatibility aliases mapped to cmux surfaces +6. `browser.tab.*` compatibility aliases mapped to Programa surfaces 7. `browser.console.list` 8. `browser.errors.list` 9. `browser.highlight` -10. `browser.state.save|load` (browser state in cmux context) +10. `browser.state.save|load` (browser state in Programa context) P2 (advanced parity / optional): 1. network interception/mocking equivalents (`route|unroute|requests|responsebody`) @@ -250,24 +250,24 @@ P2 (advanced parity / optional): Primary form: ```bash -cmux browser --surface +programa browser --surface ``` Shorthand: ```bash -cmux browser +programa browser ``` Agent discovery: ```bash -cmux identify -cmux capabilities -cmux browser identify --surface # wrapper over system.identify + browser fields +programa identify +programa capabilities +programa browser identify --surface # wrapper over system.identify + browser fields ``` Flash: ```bash -cmux trigger-flash [--workspace ] [--surface ] +programa trigger-flash [--workspace ] [--surface ] ``` Compatibility: @@ -298,7 +298,7 @@ Hard invariant: - [x] Lock method names/payload schemas for all new `browser.*` methods. - [x] Add schema validation for each new method with strict error codes (`invalid_params`, `not_found`, `invalid_state`). -- [x] Add `browser` command group in `CLI/cmux.swift` that accepts agent-browser-style command grammar. +- [x] Add `browser` command group in `CLI/programa.swift` that accepts agent-browser-style command grammar. - [x] Add `--surface` mandatory targeting (with fallback from `system.identify` when explicitly desired). - [x] Add consistent JSON output mode for all browser commands. - [x] Implement short-ref allocator and resolver for `window/pane/workspace/surface` (`window:N`, `workspace:N`, `pane:N`, `surface:N`). @@ -377,11 +377,11 @@ Hard invariant: - `tests_v2/test_browser_api_comprehensive.py` - `tests_v2/test_browser_api_unsupported_matrix.py` 2. `src/actions.test.ts` -> adapted negative coverage in `tests_v2/test_browser_api_comprehensive.py` (`invalid_params`, `not_found`, `timeout`). -3. `src/protocol.test.ts` -> adapted browser command/shape validation in `tests_v2/test_browser_api_unsupported_matrix.py` and existing `CLI/cmux.swift` command grammar checks. +3. `src/protocol.test.ts` -> adapted browser command/shape validation in `tests_v2/test_browser_api_unsupported_matrix.py` and existing `CLI/programa.swift` command grammar checks. 4. `test/file-access.test.ts` and `test/launch-options.test.ts` -> partially applicable to `WKWebView`; currently tracked as follow-up parity work (not blocking current browser method coverage). -5. `src/daemon.test.ts`, `src/stream-server.test.ts`, `test/serverless.test.ts`, `src/ios-manager.test.ts` -> out-of-scope for cmux browser parity (different transport/runtime). +5. `src/daemon.test.ts`, `src/stream-server.test.ts`, `test/serverless.test.ts`, `src/ios-manager.test.ts` -> out-of-scope for Programa browser parity (different transport/runtime). -### Implemented cmux Browser Suites +### Implemented Programa Browser Suites 1. `tests_v2/test_browser_api_p0.py` 2. `tests_v2/test_browser_api_comprehensive.py` @@ -407,16 +407,16 @@ Hard invariant: 4. No regressions in existing window/workspace/surface workflows. Planned verification commands at implementation completion: -1. `ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && ./scripts/run-tests-v2.sh'` -2. `ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && ./scripts/run-tests-v1.sh'` +1. `ssh programa-vm 'cd /Users/programa/GhosttyTabs && ./scripts/run-tests-v2.sh'` +2. `ssh programa-vm 'cd /Users/programa/GhosttyTabs && ./scripts/run-tests-v1.sh'` ## Decision Log (Locked - February 12, 2026) -1. `cmux browser tab ...` maps to browser `surface` tabs only (no separate workspace-level tab meaning inside `browser` namespace). +1. `programa browser tab ...` maps to browser `surface` tabs only (no separate workspace-level tab meaning inside `browser` namespace). 2. Default browser placement without explicit target is caller-relative: reuse the nearest right sibling pane; if none exists, split right from the caller pane. 3. Deeply nested layouts use local split ancestry: choose the nearest right sibling leaf in the caller's subtree path and avoid reshuffling unrelated panes. 4. Network parity target is full parity (not block-only phase). -5. Output shape is cmux-native overall, but `browser.snapshot` and selector `not_found` diagnostics intentionally mirror agent-browser semantics for agent usability. +5. Output shape is Programa-native overall, but `browser.snapshot` and selector `not_found` diagnostics intentionally mirror agent-browser semantics for agent usability. 6. ID model accepts UUIDs and short refs. 7. Short ref format uses full words and colon: `surface:N`, `pane:N`, `workspace:N`, `window:N`. 8. Short refs are global per daemon, monotonic, and never reused until daemon restart. @@ -432,4 +432,4 @@ Planned verification commands at implementation completion: ## Remaining Open Decisions 1. Unsupported command policy: strict `not_supported` errors vs best-effort fallback for commands that cannot be implemented on `WKWebView` with correct semantics. -2. Whether to expose protocol-only agent-browser actions in first public release of `cmux browser` or gate them behind a second rollout phase. +2. Whether to expose protocol-only agent-browser actions in first public release of `programa browser` or gate them behind a second rollout phase. diff --git a/docs/ghostty-fork.md b/docs/ghostty-fork.md index 7b9a7f3d58a..d5bbb763d5b 100644 --- a/docs/ghostty-fork.md +++ b/docs/ghostty-fork.md @@ -1,4 +1,4 @@ -# Ghostty Fork Changes (manaflow-ai/ghostty) +# Ghostty Fork Changes (Darkroom Engineering/ghostty) This repo uses a fork of Ghostty for local patches that aren't upstream yet. When we change the fork, update this document and the parent submodule SHA. @@ -6,14 +6,14 @@ When we change the fork, update this document and the parent submodule SHA. ## Fork update checklist 1) Make changes in `ghostty/`. -2) Commit and push to `manaflow-ai/ghostty`. +2) Commit and push to the Darkroom Engineering ghostty fork. 3) Update this file with the new change summary + conflict notes. 4) In the parent repo: `git add ghostty` and commit the submodule SHA. ## Current fork changes Fork rebased onto upstream `main` at `3509ccf78` (`v1.3.1-457-g3509ccf78`) on March 30, 2026. -Current cmux pinned fork head: `ae3cc5d29` (`v1.3.1-473-gae3cc5d29`). +Current Programa pinned fork head: `ae3cc5d29` (`v1.3.1-473-gae3cc5d29`). Fork `main` keeps this pin reachable via merge commit `5c781d710` (`Retain layer-background pin ancestry on main`). @@ -56,26 +56,26 @@ tend to conflict together during rebases. - `src/terminal/osc/parsers/kitty_notification.zig` - Summary: - Adds a parser for kitty OSC 99 notifications and wires it into the OSC dispatcher. - - Adapts the parser to upstream's newer capture API so the cmux OSC 99 hook survives the March 30 upstream sync. + - Adapts the parser to upstream's newer capture API so the Programa OSC 99 hook survives the March 30 upstream sync. -### 4) cmux theme picker helper hooks +### 4) Programa theme picker helper hooks - Commits: - - `1da7281fd` (Add cmux theme picker helper hooks) - - `ea482b73e` (Fix cmux theme picker preview writes) - - `c7ab66056` (Improve cmux theme picker footer contrast) - - `c49f69f7b` (Respect system theme in cmux picker) - - `599b0ff43` (Skip theme detection in cmux picker) + - `1da7281fd` (Add Programa theme picker helper hooks) + - `ea482b73e` (Fix Programa theme picker preview writes) + - `c7ab66056` (Improve Programa theme picker footer contrast) + - `c49f69f7b` (Respect system theme in Programa picker) + - `599b0ff43` (Skip theme detection in Programa picker) - `b75388d95` (Match Ghostty theme picker startup) - - `f985d2d04` (Harden cmux theme override writes) + - `f985d2d04` (Harden Programa theme override writes) - Files: - `build.zig` - `src/cli/list_themes.zig` - `src/main_ghostty.zig` - Summary: - - Adds a `zig build cli-helper` step so cmux can bundle Ghostty's CLI helper binary on macOS. - - Lets `+list-themes` switch into a cmux-managed mode via env vars, writing the cmux theme override file and posting the existing cmux reload notification for live app-wide preview. - - Keeps the preview UI readable in light mode, matches upstream picker startup behavior, and hardens writes to the cmux-managed theme override file. + - Adds a `zig build cli-helper` step so Programa can bundle Ghostty's CLI helper binary on macOS. + - Lets `+list-themes` switch into a Programa-managed mode via env vars, writing the Programa theme override file and posting the existing Programa reload notification for live app-wide preview. + - Keeps the preview UI readable in light mode, matches upstream picker startup behavior, and hardens writes to the Programa-managed theme override file. ### 5) Color scheme mode 2031 reporting @@ -87,18 +87,18 @@ tend to conflict together during rebases. - `src/termio/stream_handler.zig` - Summary: - Keeps Ghostty's mode 2031 color-scheme response aligned with the surface's actual conditional state after config reloads. - - Sends the initial DSR 997 report as soon as mode 2031 is enabled, which cmux relies on for immediate color-scheme awareness. + - Sends the initial DSR 997 report as soon as mode 2031 is enabled, which Programa relies on for immediate color-scheme awareness. ### 6) Keyboard copy mode selection C API -- Commit: `0b231db94` (Re-export cmux selection APIs removed from upstream) +- Commit: `0b231db94` (Re-export Programa selection APIs removed from upstream) - Files: - `include/ghostty.h` - `src/Surface.zig` - `src/apprt/embedded.zig` - Summary: - Restores `ghostty_surface_select_cursor_cell` and `ghostty_surface_clear_selection`. - - Keeps cmux keyboard copy mode working against the refreshed Ghostty base after upstream removed those exports. + - Keeps Programa keyboard copy mode working against the refreshed Ghostty base after upstream removed those exports. ### 7) macos-background-from-layer config flag @@ -110,7 +110,7 @@ tend to conflict together during rebases. - Adds a `macos-background-from-layer` bool config (default false). - When true, sets `bg_color[3] = 0` in the per-frame uniform update so the Metal renderer skips the full-screen background fill. - Allows the host app to provide the terminal background via `CALayer.backgroundColor` for instant coverage during view resizes, avoiding alpha double-stacking. - - Replays the layer-background restore on top of the refreshed Ghostty base so cmux keeps the resize-coverage fix after the upstream sync. + - Replays the layer-background restore on top of the refreshed Ghostty base so Programa keeps the resize-coverage fix after the upstream sync. The fork branch HEAD is now the section 7 layer-background restore commit. @@ -124,12 +124,12 @@ The fork branch HEAD is now the section 7 layer-background restore commit. ### zsh prompt redraw follow-ups - Were local in the fork as `8ade43ce5`, `0cf559581`, `312c7b23a`, and `404a3f175`. -- Dropped during the March 30, 2026 rebase because newer Ghostty prompt-marking changes on the refreshed base superseded these fork-only zsh redraw patches, so cmux no longer carries them separately. +- Dropped during the March 30, 2026 rebase because newer Ghostty prompt-marking changes on the refreshed base superseded these fork-only zsh redraw patches, so Programa no longer carries them separately. ### initial focus seeding and DECSET 1004 startup behavior - Was local in the fork as `c19c82bfd`. -- Dropped from the current pinned fork head when cmux removed the corresponding +- Dropped from the current pinned fork head when Programa removed the corresponding app-side initial focus seed and went back to post-create focus sync. ## Merge conflict notes @@ -144,16 +144,16 @@ These files change frequently upstream; be careful when rebasing the fork: - Ensure `kitty_notification` stays imported after upstream parser reorganizations. - `src/cli/list_themes.zig` - - cmux now relies on the upstream picker UI plus local env-driven hooks for live preview and restore. - If upstream reorganizes the preview loop or key handling, re-check the cmux mode path and keep the - stock Ghostty behavior unchanged when the cmux env vars are absent. + - Programa now relies on the upstream picker UI plus local env-driven hooks for live preview and restore. + If upstream reorganizes the preview loop or key handling, re-check the Programa mode path and keep the + stock Ghostty behavior unchanged when the Programa env vars are absent. - `build.zig` - - Upstream's new wasm/libghostty work touched the same build graph. Keep the cmux-only `cli-helper` + - Upstream's new wasm/libghostty work touched the same build graph. Keep the Programa-only `cli-helper` step wired in without regressing the upstream `lib-vt` or wasm build paths. - `include/ghostty.h`, `src/Surface.zig`, `src/apprt/embedded.zig` - - Upstream removed cmux-used selection exports. Preserve the re-exported + - Upstream removed Programa-used selection exports. Preserve the re-exported `ghostty_surface_select_cursor_cell` and `ghostty_surface_clear_selection` functions. - `src/renderer/generic.zig` diff --git a/docs/notifications.md b/docs/notifications.md index 250e11dd9b6..7e4b8011c90 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -1,29 +1,29 @@ # Notifications -cmux provides a notification panel for AI agents like Claude Code, Codex, and OpenCode. Notifications appear in a dedicated panel and trigger macOS system notifications. +Programa provides a notification panel for AI agents like Claude Code, Codex, and OpenCode. Notifications appear in a dedicated panel and trigger macOS system notifications. ## Quick Start ```bash -# Send a notification (if cmux is available) -command -v cmux &>/dev/null && cmux notify --title "Done" --body "Task complete" +# Send a notification (if programa is available) +command -v programa &>/dev/null && programa notify --title "Done" --body "Task complete" # With fallback to macOS notifications -command -v cmux &>/dev/null && cmux notify --title "Done" --body "Task complete" || osascript -e 'display notification "Task complete" with title "Done"' +command -v programa &>/dev/null && programa notify --title "Done" --body "Task complete" || osascript -e 'display notification "Task complete" with title "Done"' ``` ## Detection -Check if `cmux` CLI is available before using it: +Check if `programa` CLI is available before using it: ```bash # Shell -if command -v cmux &>/dev/null; then - cmux notify --title "Hello" +if command -v programa &>/dev/null; then + programa notify --title "Hello" fi # One-liner with fallback -command -v cmux &>/dev/null && cmux notify --title "Hello" || osascript -e 'display notification "" with title "Hello"' +command -v programa &>/dev/null && programa notify --title "Hello" || osascript -e 'display notification "" with title "Hello"' ``` ```python @@ -32,8 +32,8 @@ import shutil import subprocess def notify(title: str, body: str = ""): - if shutil.which("cmux"): - subprocess.run(["cmux", "notify", "--title", title, "--body", body]) + if shutil.which("programa"): + subprocess.run(["programa", "notify", "--title", title, "--body", body]) else: # Fallback to macOS subprocess.run(["osascript", "-e", f'display notification "{body}" with title "{title}"']) @@ -43,13 +43,13 @@ def notify(title: str, body: str = ""): ```bash # Simple notification -cmux notify --title "Build Complete" +programa notify --title "Build Complete" # With subtitle and body -cmux notify --title "Claude Code" --subtitle "Permission" --body "Approval needed" +programa notify --title "Claude Code" --subtitle "Permission" --body "Approval needed" # Notify specific tab/panel -cmux notify --title "Done" --tab 0 --panel 1 +programa notify --title "Done" --tab 0 --panel 1 ``` ## Integration Examples @@ -68,28 +68,28 @@ Copilot CLI supports [hooks](https://docs.github.com/en/copilot/how-tos/use-copi "userPromptSubmitted": [ { "type": "command", - "bash": "if command -v cmux &>/dev/null; then cmux set-status copilot_cli Running; fi", + "bash": "if command -v programa &>/dev/null; then programa set-status copilot_cli Running; fi", "timeoutSec": 3 } ], "agentStop": [ { "type": "command", - "bash": "if command -v cmux &>/dev/null; then cmux notify --title 'Copilot CLI' --body 'Done'; cmux set-status copilot_cli Idle; else osascript -e 'display notification \"Done\" with title \"Copilot CLI\"'; fi", + "bash": "if command -v programa &>/dev/null; then programa notify --title 'Copilot CLI' --body 'Done'; programa set-status copilot_cli Idle; else osascript -e 'display notification \"Done\" with title \"Copilot CLI\"'; fi", "timeoutSec": 5 } ], "errorOccurred": [ { "type": "command", - "bash": "if command -v cmux &>/dev/null; then cmux notify --title 'Copilot CLI' --subtitle 'Error' --body \"$(cat | jq -r '.errorMessage // \"An error occurred\"' 2>/dev/null | head -c 100)\"; cmux set-status copilot_cli Error; else osascript -e 'display notification \"An error occurred\" with title \"Copilot CLI\"'; fi", + "bash": "if command -v programa &>/dev/null; then programa notify --title 'Copilot CLI' --subtitle 'Error' --body \"$(cat | jq -r '.errorMessage // \"An error occurred\"' 2>/dev/null | head -c 100)\"; programa set-status copilot_cli Error; else osascript -e 'display notification \"An error occurred\" with title \"Copilot CLI\"'; fi", "timeoutSec": 5 } ], "sessionEnd": [ { "type": "command", - "bash": "if command -v cmux &>/dev/null; then cmux clear-status copilot_cli; fi", + "bash": "if command -v programa &>/dev/null; then programa clear-status copilot_cli; fi", "timeoutSec": 3 } ] @@ -114,7 +114,7 @@ Or for repo-level hooks, create `.github/hooks/notify.json`: Add to `~/.codex/config.toml`: ```toml -notify = ["bash", "-c", "command -v cmux &>/dev/null && cmux notify --title Codex --body \"$(echo $1 | jq -r '.\"last-assistant-message\" // \"Turn complete\"' 2>/dev/null | head -c 100)\" || osascript -e 'display notification \"Turn complete\" with title \"Codex\"'", "--"] +notify = ["bash", "-c", "command -v programa &>/dev/null && programa notify --title Codex --body \"$(echo $1 | jq -r '.\"last-assistant-message\" // \"Turn complete\"' 2>/dev/null | head -c 100)\" || osascript -e 'display notification \"Turn complete\" with title \"Codex\"'", "--"] ``` Or create a simple script `~/.local/bin/codex-notify.sh`: @@ -122,7 +122,7 @@ Or create a simple script `~/.local/bin/codex-notify.sh`: ```bash #!/bin/bash MSG=$(echo "$1" | jq -r '."last-assistant-message" // "Turn complete"' 2>/dev/null | head -c 100) -command -v cmux &>/dev/null && cmux notify --title "Codex" --body "$MSG" || osascript -e "display notification \"$MSG\" with title \"Codex\"" +command -v programa &>/dev/null && programa notify --title "Codex" --body "$MSG" || osascript -e "display notification \"$MSG\" with title \"Codex\"" ``` Then use: @@ -132,13 +132,13 @@ notify = ["bash", "~/.local/bin/codex-notify.sh"] ### OpenCode Plugin -Create `.opencode/plugins/cmux-notify.js`: +Create `.opencode/plugins/programa-notify.js`: ```javascript -export const CmuxNotificationPlugin = async ({ $, }) => { +export const ProgramaNotificationPlugin = async ({ $, }) => { const notify = async (title, body) => { try { - await $`command -v cmux && cmux notify --title ${title} --body ${body}`; + await $`command -v programa && programa notify --title ${title} --body ${body}`; } catch { await $`osascript -e ${"display notification \"" + body + "\" with title \"" + title + "\""}`; } @@ -156,27 +156,27 @@ export const CmuxNotificationPlugin = async ({ $, }) => { ## Environment Variables -cmux sets these in child shells: +Programa sets these in child shells: | Variable | Description | |----------|-------------| -| `CMUX_SOCKET_PATH` | Path to control socket | -| `CMUX_TAB_ID` | UUID of the current tab | -| `CMUX_PANEL_ID` | UUID of the current panel | +| `PROGRAMA_SOCKET_PATH` | Path to control socket | +| `PROGRAMA_TAB_ID` | UUID of the current tab | +| `PROGRAMA_PANEL_ID` | UUID of the current panel | ## CLI Commands ``` -cmux notify --title [--subtitle ] [--body ] [--tab ] [--panel ] -cmux list-notifications -cmux clear-notifications -cmux set-status -cmux clear-status -cmux ping +programa notify --title [--subtitle ] [--body ] [--tab ] [--panel ] +programa list-notifications +programa clear-notifications +programa set-status +programa clear-status +programa ping ``` ## Best Practices -1. **Always check availability first** - Use `command -v cmux` before calling +1. **Always check availability first** - Use `command -v programa` before calling 2. **Provide fallbacks** - Use `|| osascript` for macOS fallback 3. **Keep notifications concise** - Title should be brief, use body for details diff --git a/docs/remote-daemon-spec.md b/docs/remote-daemon-spec.md index 3c8bb0c8a81..99245a62a16 100644 --- a/docs/remote-daemon-spec.md +++ b/docs/remote-daemon-spec.md @@ -1,9 +1,9 @@ # Remote SSH Living Spec Last updated: March 12, 2026 -Tracking issue: https://github.com/manaflow-ai/cmux/issues/151 -Primary PR: https://github.com/manaflow-ai/cmux/pull/1296 -CLI relay PR: https://github.com/manaflow-ai/cmux/pull/374 +Tracking issue: https://github.com/darkroomengineering/programa/issues/151 +Primary PR: https://github.com/darkroomengineering/programa/pull/1296 +CLI relay PR: https://github.com/darkroomengineering/programa/pull/374 This document is the working source of truth for: 1. what is implemented now @@ -16,7 +16,7 @@ This is a **living implementation spec** (also called an **execution spec**): a ## 2. Objective -`cmux ssh` should provide: +`programa ssh` should provide: 1. durable remote terminals with reconnect/reuse 2. browser traffic that egresses from the remote host via proxying 3. tmux-style PTY resize semantics (`smallest screen wins`) @@ -24,13 +24,13 @@ This is a **living implementation spec** (also called an **execution spec**): a ## 3. Current State (Implemented) ### 3.1 Remote Workspace + Reconnect UX -- `DONE` `cmux ssh` creates remote-tagged workspaces and does not require `--name`. -- `DONE` scoped shell niceties are applied only for `cmux ssh` launches. +- `DONE` `programa ssh` creates remote-tagged workspaces and does not require `--name`. +- `DONE` scoped shell niceties are applied only for `programa ssh` launches. - `DONE` context menu actions exist for remote workspaces (`Reconnect Workspace(s)`, `Disconnect Workspace(s)`). - `DONE` socket API includes `workspace.remote.reconnect`. ### 3.2 Bootstrap + Daemon -- `DONE` local app probes remote platform, verifies a release-pinned `cmuxd-remote` artifact by embedded manifest SHA-256, uploads it when missing, and runs `serve --stdio`. +- `DONE` local app probes remote platform, verifies a release-pinned `programad-remote` artifact by embedded manifest SHA-256, uploads it when missing, and runs `serve --stdio`. - `DONE` daemon `hello` handshake is enforced. - `DONE` daemon now exposes proxy stream RPC (`proxy.open`, `proxy.close`, `proxy.write`, `proxy.stream.subscribe`) plus pushed `proxy.stream.*` events. - `DONE` local proxy broker now tunnels SOCKS5/CONNECT traffic over daemon stream RPC instead of `ssh -D`. @@ -39,26 +39,26 @@ This is a **living implementation spec** (also called an **execution spec**): a - `DONE` SOCKS handshake parsing now preserves pipelined post-connect payload bytes instead of dropping request-prefix bytes. - `DONE` `workspace.remote.configure.local_proxy_port` exists as an internal deterministic test hook for bind-conflict regression coverage. - `DONE` bootstrap/probe failures surface actionable details. -- `DONE` bootstrap installs `~/.cmux/bin/cmux` wrapper (also tries `/usr/local/bin/cmux`) so `cmux` is available in PATH on the remote. +- `DONE` bootstrap installs `~/.programa/bin/programa` wrapper (also tries `/usr/local/bin/programa`) so `programa` is available in PATH on the remote. -### 3.5 CLI Relay (Running cmux Commands From Remote) -- `DONE` `cmuxd-remote` includes a table-driven CLI relay (`cli` subcommand) that maps CLI args to v1 text or v2 JSON-RPC messages. -- `DONE` busybox-style argv[0] detection: when invoked as `cmux` via wrapper/symlink, auto-dispatches to CLI relay. +### 3.5 CLI Relay (Running Programa Commands From Remote) +- `DONE` `programad-remote` includes a table-driven CLI relay (`cli` subcommand) that maps CLI args to v1 text or v2 JSON-RPC messages. +- `DONE` busybox-style argv[0] detection: when invoked as `programa` via wrapper/symlink, auto-dispatches to CLI relay. - `DONE` background `ssh -N -R 127.0.0.1:PORT:127.0.0.1:LOCAL_RELAY_PORT` process reverse-forwards a TCP port to a dedicated authenticated local relay server. Uses TCP instead of Unix socket forwarding because many servers have `AllowStreamLocalForwarding` disabled. - `DONE` relay process uses `-S none` / standalone SSH transport (avoids ControlMaster multiplexing and inherited `RemoteForward` directives) and `ExitOnForwardFailure=yes` so dead reverse binds fail fast instead of publishing bad relay metadata. -- `DONE` relay address written to `~/.cmux/socket_addr` on the remote only after the reverse forward survives startup validation. -- `DONE` Go CLI no longer polls for relay readiness. It dials the published relay once and only refreshes `~/.cmux/socket_addr` a single time to recover from a stale shared address rewrite. -- `DONE` `cmux ssh` startup exports session-local `CMUX_SOCKET_PATH=127.0.0.1:` so parallel sessions pin to their own relay instead of racing on shared socket_addr. -- `DONE` relay startup writes `~/.cmux/relay/.daemon_path`; remote `cmux` wrapper uses this to select the right daemon binary per session, including mixed local cmux versions. -- `DONE` relay startup writes `~/.cmux/relay/.auth` with a relay ID and token; the local relay requires HMAC-SHA256 challenge-response before forwarding any command to the real local socket. +- `DONE` relay address written to `~/.programa/socket_addr` on the remote only after the reverse forward survives startup validation. +- `DONE` Go CLI no longer polls for relay readiness. It dials the published relay once and only refreshes `~/.programa/socket_addr` a single time to recover from a stale shared address rewrite. +- `DONE` `programa ssh` startup exports session-local `PROGRAMA_SOCKET_PATH=127.0.0.1:` so parallel sessions pin to their own relay instead of racing on shared socket_addr. +- `DONE` relay startup writes `~/.programa/relay/.daemon_path`; remote `programa` wrapper uses this to select the right daemon binary per session, including mixed local Programa versions. +- `DONE` relay startup writes `~/.programa/relay/.auth` with a relay ID and token; the local relay requires HMAC-SHA256 challenge-response before forwarding any command to the real local socket. - `DONE` ephemeral port range (49152-65535) filtered from probe results to exclude relay ports from other workspaces. - `DONE` multi-workspace port conflict detection uses TCP connect check (`isLoopbackPortReachable`) so ports already forwarded by another workspace are silently skipped instead of flagged as conflicts. - `DONE` orphaned relay SSH processes from previous app sessions are cleaned up before starting a new relay. ### 3.6 Artifact Trust -- `DONE` release and nightly workflows publish `cmuxd-remote` assets for `darwin/linux × arm64/amd64`. -- `DONE` release and nightly apps embed a compact `CMUXRemoteDaemonManifestJSON` in `Info.plist` with exact asset URLs and SHA-256 digests. -- `DONE` `cmux remote-daemon-status` exposes the current manifest entry, local cache verification state, release download command, and GitHub attestation verification command. +- `DONE` release and nightly workflows publish `programad-remote` assets for `darwin/linux × arm64/amd64`. +- `DONE` release and nightly apps embed a compact `PROGRAMARemoteDaemonManifestJSON` in `Info.plist` with exact asset URLs and SHA-256 digests. +- `DONE` `programa remote-daemon-status` exposes the current manifest entry, local cache verification state, release download command, and GitHub attestation verification command. ### 3.3 Error Surfacing - `DONE` remote errors are surfaced in sidebar status + logs + notifications. @@ -86,7 +86,7 @@ This is a **living implementation spec** (also called an **execution spec**): a 5. `DONE` re-apply proxy config on reconnect/state updates. ### 4.3 Remote Daemon + Transport -1. `DONE` `cmuxd-remote` now supports proxy stream RPC (`proxy.open`, `proxy.close`, `proxy.write`, `proxy.stream.subscribe`) with pushed `proxy.stream.data/eof/error` events. +1. `DONE` `programad-remote` now supports proxy stream RPC (`proxy.open`, `proxy.close`, `proxy.write`, `proxy.stream.subscribe`) with pushed `proxy.stream.data/eof/error` events. 2. `DONE` local side now runs a shared local broker that serves SOCKS5/CONNECT and tunnels each stream over persistent daemon stdio RPC without polling reads. 3. `DONE` removed remote service-port discovery/probing from browser routing path. @@ -124,14 +124,14 @@ Recompute effective size on: | ID | Milestone | Status | Notes | |---|---|---|---| -| M-001 | `cmux ssh` workspace creation + metadata + optional `--name` | DONE | Covered by `tests_v2/test_ssh_remote_cli_metadata.py` | +| M-001 | `programa ssh` workspace creation + metadata + optional `--name` | DONE | Covered by `tests_v2/test_ssh_remote_cli_metadata.py` | | M-002 | Remote bootstrap/upload/start + hello handshake | DONE | Includes daemon capability handshake + status surfacing | | M-003 | Reconnect/disconnect UX + API + improved error surfacing | DONE | Includes retry count in surfaced errors | | M-004 | Docker e2e for bootstrap/reconnect shell niceties | DONE | Docker suites validate proxy-path bootstrap and reconnect behavior | -| M-004b | CLI relay: run cmux commands from within SSH sessions | DONE | Reverse TCP forward + Go CLI relay + bootstrap wrapper | +| M-004b | CLI relay: run programa commands from within SSH sessions | DONE | Reverse TCP forward + Go CLI relay + bootstrap wrapper | | M-005 | Remove automatic remote port mirroring path | DONE | `WorkspaceRemoteSessionController` now uses one shared daemon-backed proxy endpoint | | M-006 | Transport-scoped local proxy broker (SOCKS5 + CONNECT) | DONE | Identical SSH transports now reuse one local proxy endpoint | -| M-007 | Remote proxy stream RPC in `cmuxd-remote` | DONE | `proxy.open/close/write/proxy.stream.subscribe` plus pushed stream events implemented | +| M-007 | Remote proxy stream RPC in `programad-remote` | DONE | `proxy.open/close/write/proxy.stream.subscribe` plus pushed stream events implemented | | M-008 | WebView proxy auto-wiring for remote workspaces | DONE | Workspace-scoped `WKWebsiteDataStore.proxyConfigurations` wiring is active | | M-009 | PTY resize coordinator (`smallest screen wins`) | DONE | Daemon session RPC now tracks attachments and applies min cols/rows semantics with unit tests | | M-010 | Resize + proxy reconnect e2e test suites | DONE | `tests_v2/test_ssh_remote_docker_forwarding.py` validates HTTP/websocket egress plus SOCKS pipelined-payload handling; `tests_v2/test_ssh_remote_docker_reconnect.py` verifies reconnect recovery and repeats SOCKS pipelined-payload checks after host restart; `tests_v2/test_ssh_remote_proxy_bind_conflict.py` validates structured `proxy_unavailable` bind-conflict surfacing and `local_proxy_port` status retention under bind conflict; `tests_v2/test_ssh_remote_daemon_resize_stdio.py` validates session resize semantics over real stdio RPC process boundaries; `tests_v2/test_ssh_remote_cli_metadata.py` validates `workspace.remote.configure` numeric-string compatibility, explicit `null` clear semantics (including `workspace.remote.status` reflection), strict `port`/`local_proxy_port` validation (bounds/type), case-insensitive SSH option override precedence for StrictHostKeyChecking/control-socket keys, and `local_proxy_port` payload echo for deterministic bind-conflict test hook behavior | @@ -152,10 +152,10 @@ Recompute effective size on: | ID | Scenario | Status | |---|---|---| -| C-001 | `cmux ping` from remote session | DONE | -| C-002 | `cmux list-workspaces --json` from remote | DONE | -| C-003 | `cmux new-workspace` from remote | DONE | -| C-004 | `cmux rpc system.capabilities` passthrough | DONE | +| C-001 | `programa ping` from remote session | DONE | +| C-002 | `programa list-workspaces --json` from remote | DONE | +| C-003 | `programa new-workspace` from remote | DONE | +| C-004 | `programa rpc system.capabilities` passthrough | DONE | | C-005 | TCP retry handles relay not yet established | DONE | | C-006 | multi-workspace port conflict silent skip | DONE | | C-007 | ephemeral port filtering excludes relay ports | DONE | @@ -209,6 +209,6 @@ Before declaring browser proxying complete: 3. SSH option key matching is case-insensitive for precedence checks in both CLI-built commands and remote configure payloads. ### 10.3 SSH Docker E2E Harness Knobs -1. `CMUX_SSH_TEST_DOCKER_HOST` sets the SSH destination host/IP used by docker-backed SSH fixtures (default `127.0.0.1`). -2. `CMUX_SSH_TEST_DOCKER_BIND_ADDR` sets the bind address used in fixture container publish mappings (default `127.0.0.1`). +1. `PROGRAMA_SSH_TEST_DOCKER_HOST` sets the SSH destination host/IP used by docker-backed SSH fixtures (default `127.0.0.1`). +2. `PROGRAMA_SSH_TEST_DOCKER_BIND_ADDR` sets the bind address used in fixture container publish mappings (default `127.0.0.1`). 3. Defaults preserve loopback behavior on a single host; override both when docker runs on a different host (for example VM -> host OrbStack). diff --git a/docs/socket-focus-steal-audit.todo.md b/docs/socket-focus-steal-audit.todo.md index dd49450af97..a088e271cff 100644 --- a/docs/socket-focus-steal-audit.todo.md +++ b/docs/socket-focus-steal-audit.todo.md @@ -1,7 +1,7 @@ # Socket/CLI No-Focus-Steal Todo ## Goal -Ensure commands run through the cmux Unix socket/CLI do not steal user focus from the current UI workflow. +Ensure commands run through the Programa Unix socket/CLI do not steal user focus from the current UI workflow. Policy target: - App activation/window raising from socket commands: **never**. @@ -73,4 +73,4 @@ All other commands should preserve current user focus context. ## CLI Coverage - [x] Ensure every top-level CLI command routes to non-focus-stealing socket behavior. - [x] Add/verify `rename-workspace` + `rename-window` behavior remains intact. -- [x] Add explicit `rename-tab` command (defaults to `CMUX_TAB_ID` / `CMUX_SURFACE_ID` / `CMUX_WORKSPACE_ID` when flags omitted). +- [x] Add explicit `rename-tab` command (defaults to `PROGRAMA_TAB_ID` / `PROGRAMA_SURFACE_ID` / `PROGRAMA_WORKSPACE_ID` when flags omitted). diff --git a/docs/v2-api-migration.md b/docs/v2-api-migration.md index 35a27402e65..509a147e696 100644 --- a/docs/v2-api-migration.md +++ b/docs/v2-api-migration.md @@ -134,12 +134,12 @@ Debug / Test-only: v1 suite stays in `tests/`. v2 suite lives in `tests_v2/` and should: -- use a v2 JSON client (`tests_v2/cmux.py`) +- use a v2 JSON client (`tests_v2/programa.py`) - avoid depending on v1 text output formats VM runners: -- v1: `ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && ./scripts/run-tests-v1.sh'` -- v2: `ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && ./scripts/run-tests-v2.sh'` +- v1: `ssh programa-vm 'cd /Users/programa/GhosttyTabs && ./scripts/run-tests-v1.sh'` +- v2: `ssh programa-vm 'cd /Users/programa/GhosttyTabs && ./scripts/run-tests-v2.sh'` ## Open Questions diff --git a/plans/joyful-brewing-riddle.md b/plans/joyful-brewing-riddle.md new file mode 100644 index 00000000000..22a3ab73ffc --- /dev/null +++ b/plans/joyful-brewing-riddle.md @@ -0,0 +1,90 @@ +# Board sweep — fix or clean every open issue + +## Context + +The `darkroomengineering/programa` board has **13 open issues**. The user asked to "ultracode all the issues missing or clean the board" — i.e. for each open issue, either implement the missing fix or remove it from the board. A 13-agent read-only triage (one explore agent per issue, evidence-grounded against current code) produced a disposition for each. User decisions on scope: + +- **Execute the full sweep** — all 9 actionable fixes **plus** #27 (browser proxy), including the typing-latency hot-path #20 done carefully. +- **Close #25 and #24** as deferred/wontfix-for-now (clean the board). +- **Delete the broken Homebrew workflow** and close #7. +- **Implement #27 now** — I own the SSH-relay merge policy (decided below). + +Delivery follows the established pattern: **one PR per issue**, branch → fix → push → PR → CI. Each fix is implemented by an `implementer` in an **isolated git worktree** (`isolation: worktree`) so file overlaps between issues don't collide; I review every diff before it is committed/pushed. + +## Disposition summary + +| # | Title | Disposition | Risk | Primary files | +|---|---|---|---|---| +| 9 | iframe/subframe downloads silently dropped | FIX | low | `BrowserPanel.swift`, `BrowserPopupWindowController.swift` | +| 26 | split dividers near-invisible | FIX | low | `vendor/bonsplit/.../TabBarColors.swift` (submodule) | +| 15 | SSH relay `~/.cmux`→`~/.programa` split-brain | FIX | low | `CLI/cmux.swift` | +| 23 | VS Code popup shows `about:blank` first | FIX | low | `BrowserPopupWindowController.swift` | +| 32 | regression test for #6618 dedup race | FIX (2-commit) | low | `TerminalController.swift`, new test | +| 28 | no scrollback-persistence privacy toggle | FIX | low | `cmuxApp.swift`, `TerminalPanel.swift`, `Localizable.xcstrings` | +| 19 | Cmd+F ignored in MarkdownPanel | FIX | low | new overlay + `TabManager.swift`, `MarkdownPanel*.swift` | +| 21 | VS Code auth lost on restart | FIX | medium | `AppDelegate.swift`, test | +| 20 | sidebar pays full snapshot cost per keystroke | FIX | medium ⚠️ hot path | `ContentView.swift`, `Workspace.swift` | +| 27 | no `browser.proxy` config | FIX | low | `settings.schema.json`, `KeyboardShortcutSettingsFileStore.swift`, `BrowserPanel.swift`, new settings file | +| 7 | broken Homebrew workflow | CLOSE (delete) | — | `.github/workflows/update-homebrew.yml` | +| 25 | scp fails on sftp-chroot | CLOSE (defer) | — | none | +| 24 | main-thread scrollback readback | CLOSE (defer) | — | none | + +## Per-issue fix specs + +Full evidence + exact diffs live in the triage output (`tasks/wuyvdp58w.output`). Condensed here: + +### Wave A — trivial/low-risk (parallel worktrees) + +**#9 — iframe downloads.** In both `BrowserPanel.swift` (~6257) and `BrowserPopupWindowController.swift` (~588), the `!navigationResponse.isForMainFrame` early-`return .allow` fires *before* the `Content-Disposition` check. Guard it: if the subframe response is an `HTTPURLResponse` whose `Content-Disposition` starts with `attachment`, return `.download` instead of `.allow`. Existing `BrowserDownloadDelegate` handles the rest. + +**#26 — split divider contrast.** In `vendor/bonsplit/Sources/Bonsplit/Internal/Styling/TabBarColors.swift:162–165`, raise alpha `0.26/0.36 → 0.50/0.65` and darken/lighten `0.12/0.16 → 0.20/0.28`. **Submodule flow:** commit in `vendor/bonsplit`, `git push darkroom main`, then `git add vendor/bonsplit && git commit` in parent. Verify ancestry before pointer commit. + +**#15 — SSH relay split-brain.** In `CLI/cmux.swift`, replace ~12 stale remote-host `~/.cmux/...` literals with `~/.programa/...` and remote `cmux` binary refs with `programa` (lines ~1065, 4431, 4439–4440, 4473–4474, 4480–4481, 4524–4525, 4558, 4635, 4757–4758, 4763–4764). Mechanical; matches `Workspace.swift` + `cli.go` which already use `~/.programa`. Verify via `tests_v2/test_ssh_remote_*.py` on CI. + +**#23 — VS Code popup `about:blank`.** In `BrowserPopupWindowController.swift`: remove `panel.makeKeyAndOrderFront` from `init()` (~242); add `hasShownPanel` flag + `showPanelIfNeeded()`; reveal from a new `PopupNavigationDelegate.webView(_:didCommit:)` once `url != about:blank`, with a 300 ms `asyncAfter` fallback so the panel never stays hidden. **Overlaps #9 in this file** — sequence #23 after #9 (rebase) or hand both to one implementer. + +**#32 — regression test for #6618.** Two-commit per CLAUDE.md policy. Commit 1: add `cmuxTests/SocketFastPathStateDedupTests.swift` instantiating `TerminalController.SocketFastPathState()` — fails to compile (type is `private`) → red. Commit 2: widen `SocketSurfaceKey` (`TerminalController.swift:435`) and `SocketFastPathState` (`:440`) from `private` → `internal` → compiles, asserts absent-panel does not suppress the next identical report. Verify with `xcodebuild -scheme programa-unit` (safe) or CI. + +### Wave B — multi-file, low-risk (parallel worktrees) + +**#28 — scrollback privacy toggle.** Add `ScrollbackPersistenceSettings` enum in `cmuxApp.swift` (mirror `QuitWarningSettings`, key `sessionPersistScrollback`, default `true`); `@AppStorage` binding + Settings toggle row + reset entry. Gate the single chokepoint `TerminalPanel.shouldPersistScrollbackForSessionSnapshot()` (`:210`) with `guard ScrollbackPersistenceSettings.isEnabled() else { return false }`. Add 3 localized keys (EN+JA) to `Localizable.xcstrings`. + +**#19 — MarkdownPanel find.** New `Sources/Find/MarkdownSearchOverlay.swift` mirroring `BrowserSearchOverlay`; add `MarkdownSearchState` + find methods to `MarkdownPanel.swift`; mount overlay in `MarkdownPanelView.swift`; add `focusedMarkdownPanel` to `TabManager.swift` and a markdown branch in `isFindVisible`/`startSearch`/`findNext`/`findPrevious`/`hideFind`. Also delete the unreachable duplicate terminal block in `startSearch()` (`TabManager.swift:1230–1243`). + +**#27 — browser proxy config.** Add `browser.proxy` object (`host`, `port` 1–65535, `type` enum `socks5|httpConnect`) to `settings.schema.json`; parse it in `KeyboardShortcutSettingsFileStore.parseBrowserSection()` into a new `BrowserUserProxySettings` (new file, mirror `BrowserThemeSettings`); consume in `BrowserPanel.applyRemoteProxyConfigurationIfAvailable()`. **Merge policy (decided):** SSH-relay proxy wins whenever a relay endpoint is active (remote panels depend on it); the user proxy applies only when `remoteProxyEndpoint == nil`. The existing `proxyConfigurations = []` clear (`:2807–2809`) must be replaced by "apply user proxy if set, else clear", so a user proxy is no longer clobbered. Config-file only (no Settings UI) this pass. + +### Wave C — sensitive (own worktree, careful review) + +**#21 — VS Code auth persistence.** In `AppDelegate.swift`: add stable `vscodeServerDataDir` under Application Support; make `makeConnectionTokenFile()` reuse a persistent `connection-token` file (only generate when absent); pass `--server-data-dir`; add `--port` stability or document the trade-off; set `VSCODE_CLI_USE_FILE_KEYRING=1`; guard cleanup in `stop()`/`terminationHandler` so only temp-dir tokens are deleted (`hasPrefix(NSTemporaryDirectory())`). Keep `testStopRemovesOrphanedConnectionTokenFiles` green; add a complementary test that the persistent token survives. + +**#20 — sidebar keystroke cost ⚠️ typing-latency hot path.** `TabItemView` is `Equatable`/`.equatable()` and is on the typing path (CLAUDE.md). Add three `@State` caches (`cachedOrderedPanelIds`, `cachedBranchDirectoryLines`, `cachedPullRequestRows`); read them in `body` instead of recomputing `sidebarOrderedPanelIds()` / `verticalBranchDirectoryLines()` / `pullRequestDisplays()` each generation; populate the caches **only** from the debounced `sidebarObservationPublisher` handler plus `onAppear` + `onChange(of: settings)`. `sidebarImmediateObservationPublisher` keeps incrementing the generation for title/pin/color but no longer triggers the bonsplit tree walk. **Do not** add stored props that break the `==` (caches are `@State`, excluded from equality — keep it that way). Verify typing latency unchanged via debug event log + the tests-build-and-lag CI job (known flaky — re-run before investigating). + +### Board cleanup + +- **#7** — delete `.github/workflows/update-homebrew.yml`; close #7 with reason "no Homebrew tap maintained; workflow targeted a non-existent repo and failed every release." (Bundle the deletion into the close, or its own tiny PR.) +- **#25** — close as deferred/wontfix: the whole SSH bootstrap chain (probe + mkdir + scp + chmod/mv) is incompatible with strict sftp-chroot, not just scp; no milestone planned. +- **#24** — close as deferred tech-debt: main-thread `surface.read_text` readback; revisit only if it demonstrably starves the UI. + +## Execution order + +1. **Wave A** (5 issues) — fan out 5 `implementer` agents in isolated worktrees in one message. Each: apply the fix, build with `./scripts/reload.sh --tag issue-` (compile-only `xcodebuild -derivedDataPath /tmp/programa-` acceptable), leave uncommitted. #26 follows the submodule push flow; #23 sequences after #9 (shared file). +2. **Review + ship Wave A** — review each diff, commit per-issue, push branch, open PR, let CI run. Close each issue via its PR ("Fixes #n"). +3. **Wave B** (3 issues) — same pattern. +4. **Wave C** (2 issues, #21 then #20) — individually, with extra review on the hot path. For #20, confirm no `==`/`.equatable()` regression and typing-latency parity. +5. **Cleanup** — delete Homebrew workflow + close #7; close #25 and #24 with the reasons above. + +## Verification + +- **Build:** every fix must build via `./scripts/reload.sh --tag issue-` (never bare `xcodebuild`/untagged `open`). Provide the `file://` App path link per CLAUDE.md when a runtime check is warranted (#26 divider contrast, #23/#9 browser, #19 Cmd+F, #28 toggle, #20 sidebar). +- **Tests:** never run locally. #32 unit test → `xcodebuild -scheme programa-unit` or CI; #15 → `tests_v2/test_ssh_remote_*.py` via `gh workflow run test-e2e.yml`; #21 → unit test on CI. Other UI behavior verified by tagged build + debug event log. +- **Submodule (#26):** confirm `cd vendor/bonsplit && git merge-base --is-ancestor HEAD origin/main` (or `darkroom/main`) before committing the parent pointer. +- **Per-PR CI:** watch each PR to green (`gh run watch`) before merge, matching #29/#30/#31. +- **Localization (#28):** all new strings in `Localizable.xcstrings` with EN + JA. + +## Risks / notes + +- **#20** is the only fix on a documented typing-latency hot path — highest regression risk; isolate it, review the `==` contract, and verify latency parity before merge. +- **#9 + #23** edit `BrowserPopupWindowController.swift` near the same region — sequence them. +- **#21** changes VS Code launch flags; verify the inline VS Code panel still launches and that Settings Sync auth survives a restart. +- **#27** changes proxy application order; verify SSH-remote browser panels (relay proxy) still work and a user proxy applies only when no relay is active. +- Closing #24/#25 and deleting the Homebrew workflow are reversible (reopen / revert). diff --git a/plans/rebrand-cmux-to-programa.md b/plans/rebrand-cmux-to-programa.md new file mode 100644 index 00000000000..3a83caba988 --- /dev/null +++ b/plans/rebrand-cmux-to-programa.md @@ -0,0 +1,57 @@ +# Rebrand: cmux → Programa (execution plan) + +Status: **not started** (an automated attempt was reverted — it produced an unverified, +duplicated, inconsistent 96-file diff and did risky out-of-scope churn). Tree is clean. +This is a large, multi-subsystem migration best done in a fresh session with full context, +in build-gated phases. Branch to use: off latest `main`. + +## Why it's hard: 5 runtime-coupled contracts (NOT locally verifiable — CI/manual only) + +1. **AppleScript** — `Resources/programa.sdef` references `@objc(CmuxScriptWindow/Tab/Terminal/InputTextCommand)` + by string (`Sources/AppleScriptSupport.swift`). Rename the `@objc` name → must update the `.sdef` in lockstep, + or keep the `@objc(Cmux…)` name and only rename the Swift symbol. +2. **Dock Tile plugin** — `CmuxDockTilePlugin` (`Sources/AppIconDockTilePlugin.swift`) is a separate Xcode target + loaded by principal-class name (`NSDockTilePlugIn`). Rename class + target + `.plugin` product + principal class together. +3. **Browser bridge** — `__cmux*` JS injection tokens in `Sources/Panels/BrowserPanel.swift` + `ReactGrab.swift` + must match the injected JS and `WKScriptMessageHandler` names exactly, both sides. +4. **Shell-integration protocol** — `$cmux_port`, `$cmux_tty`, `$cmux_pid`, `__cmux_t/_v`, etc. are a contract between + Swift and `Resources/shell-integration/cmux-{bash,zsh}-integration.{bash,zsh}`. Rename both sides in lockstep. +5. **CLI command + SSH** — `CLI/cmux.swift` (~6700 lines): `cmux` is the command name in hundreds of user strings AND + runtime SSH remote-bootstrap (`cmux_remote_bootstrap`, remote `cmux` invocation, socket paths). Also stale + `manaflow-ai/cmux` release-repo refs (lines ~4998-5004) that should already be `darkroomengineering/programa`. + +## Safe bucket (compile-verifiable) vs deferred bucket (runtime-coupled) + +- **Safe now:** all `Cmux*` PascalCase types → `Programa*` EXCEPT the two runtime-coupled files above + (`AppleScriptSupport.swift`, `AppIconDockTilePlugin.swift` — each references its Cmux types only within itself, + so excluding them is clean). Source-file renames (`cmuxApp.swift`, `CmuxConfig.swift`, `CmuxConfigExecutor.swift`, + `CmuxDirectoryTrust.swift`, `CmuxWebView.swift`, `CLI/cmux.swift`) + pbxproj refs. `cmuxTests/`→`programaTests/`. + Xcode schemes (`cmux{,-unit,-ci}.xcscheme` → `programa*`) + `cmux-cli`→`programa-cli` target + `PRODUCT_MODULE_NAME cmux_cli`→`programa_cli`. + Scripts (`reload*.sh`, `test-unit.sh`, `run-tests-v{1,2}.sh`, `build-sign-upload.sh`) + `.github/workflows/*` scheme refs. + Log-prefix strings (`[CmuxConfig]` etc.). Config path migration `~/.config/cmux`→`~/.config/programa` (+ `cmux.json`) + WITH a legacy-fallback shim in `ProgramaConfig.swift` + `KeyboardShortcutSettingsFileStore.swift`. +- **Deferred (needs CI/manual verification):** AppleScript `.sdef` + `@objc` names; DockTile target/principal class; + `__cmux*` JS tokens; `$cmux_*` shell-integration vars + script filenames; the `cmux` CLI command name + SSH bootstrap; + runtime `UserDefaults` keys with `cmux` prefixes (renaming orphans stored prefs). + +## Phased order (build-gate each phase; commit buildable milestones) + +- **Phase 1 — types:** `perl -pi -e 's/Cmux/Programa/g'` across `Sources/** CLI/** cmuxTests/**` EXCLUDING + `AppleScriptSupport.swift` + `AppIconDockTilePlugin.swift`. Build with the EXISTING `cmux` scheme (no structural change yet). Commit. +- **Phase 2 — file renames:** `git mv` the Cmux-named sources + update every `project.pbxproj` ref. Build (cmux scheme). Commit. +- **Phase 3 — test dir:** `git mv cmuxTests programaTests` + pbxproj group/paths/target name. Build + run unit tests. Commit. +- **Phase 4 — scheme/targets/scripts/CI:** rename schemes (files + `BlueprintName`/`BuildableName`/`BlueprintIdentifier`), + `cmux-cli`→`programa-cli`, module name; update `reload*.sh` (`-scheme cmux`→`programa`) + CI. Build with NEW `programa` scheme. Commit. +- **Phase 5 — config migration:** paths + legacy shim. Build. Commit. +- **Phase 6+ (deferred, CI-verified):** welcome banner + CLI command rename (couple together), shell-integration lockstep, + browser JS tokens, AppleScript, DockTile, UserDefaults-key migration. + +## Build recipe (local zig is 0.16.0; ghostty pins 0.15.2) + +``` +rm -rf ghostty/zig-pkg +PROGRAMA_SKIP_ZIG_BUILD=1 xcodebuild -project GhosttyTabs.xcodeproj -scheme \ + -configuration Debug -destination 'platform=macOS' -derivedDataPath /tmp/programa-rebrand \ + PROGRAMA_SKIP_ZIG_BUILD=1 build 2>&1 | grep -iE "error:|BUILD (FAILED|SUCCEEDED)" +``` +Never blanket-sed lowercase `cmux` (hits URLs, shell vars, JS tokens, command name, UserDefaults keys). Keep `com.darkroom.programa` bundle IDs. Don't touch the `ghostty/` submodule or root `*.md` files. From f869dc6b05ab8071170a2f4014d35556d34b7e7b Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 11:19:00 -0300 Subject: [PATCH 02/23] =?UTF-8?q?refactor:=20rename=20Cmux*=20symbols=20?= =?UTF-8?q?=E2=86=92=20Programa*=20(Phase=201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical s/Cmux/Programa/g across Sources/**, CLI/**, cmuxTests/**, excluding AppleScriptSupport.swift (@objc .sdef contract) and AppIconDockTilePlugin.swift (principal-class target) — both self-contained. Filenames, pbxproj, and scheme unchanged; builds green with the cmux scheme. --- CLI/cmux.swift | 12 +- Sources/AppDelegate.swift | 166 +++++++++--------- Sources/CmuxConfig.swift | 88 +++++----- Sources/CmuxConfigExecutor.swift | 14 +- Sources/CmuxDirectoryTrust.swift | 4 +- Sources/ContentView.swift | 42 ++--- Sources/GhosttyTerminalView.swift | 68 +++---- Sources/KeyboardShortcutSettings.swift | 2 +- .../KeyboardShortcutSettingsFileStore.swift | 70 ++++---- Sources/Panels/BrowserPanel.swift | 84 ++++----- Sources/Panels/BrowserPanelView.swift | 30 ++-- .../Panels/BrowserPopupWindowController.swift | 10 +- Sources/Panels/CmuxWebView.swift | 16 +- Sources/Panels/TerminalPanel.swift | 2 +- Sources/SurfacePool.swift | 2 +- Sources/TabManager.swift | 12 +- Sources/Workspace.swift | 42 ++--- Sources/cmuxApp.swift | 62 +++---- cmuxTests/BrowserConfigTests.swift | 128 +++++++------- cmuxTests/BrowserPanelTests.swift | 26 +-- cmuxTests/CmuxConfigTests.swift | 70 ++++---- cmuxTests/GhosttyConfigTests.swift | 16 +- .../InactivePaneFirstClickFocusTests.swift | 4 +- cmuxTests/NotificationAndMenuBarTests.swift | 2 +- .../SocketControlPasswordStoreTests.swift | 8 +- cmuxTests/WindowAndDragTests.swift | 2 +- cmuxTests/WorkspaceUnitTests.swift | 12 +- 27 files changed, 497 insertions(+), 497 deletions(-) diff --git a/CLI/cmux.swift b/CLI/cmux.swift index bf80214cb0a..ae6510227be 100644 --- a/CLI/cmux.swift +++ b/CLI/cmux.swift @@ -8087,7 +8087,7 @@ struct CMUXCLI { let selection = currentThemeSelection() var environment = ProcessInfo.processInfo.environment environment["PROGRAMA_THEME_PICKER_CONFIG"] = try cmuxThemeOverrideConfigURL().path - environment["PROGRAMA_THEME_PICKER_BUNDLE_ID"] = currentCmuxAppBundleIdentifier() ?? Self.cmuxThemeOverrideBundleIdentifier + environment["PROGRAMA_THEME_PICKER_BUNDLE_ID"] = currentProgramaAppBundleIdentifier() ?? Self.cmuxThemeOverrideBundleIdentifier environment["PROGRAMA_THEME_PICKER_TARGET"] = defaultThemePickerTargetMode(current: selection).rawValue environment["PROGRAMA_THEME_PICKER_COLOR_SCHEME"] = defaultAppearancePrefersDarkThemes() ? "dark" : "light" if let light = selection.light { @@ -8717,7 +8717,7 @@ struct CMUXCLI { } private func reloadThemesIfPossible() -> ThemeReloadStatus { - let bundleIdentifier = currentCmuxAppBundleIdentifier() ?? Self.cmuxThemeOverrideBundleIdentifier + let bundleIdentifier = currentProgramaAppBundleIdentifier() ?? Self.cmuxThemeOverrideBundleIdentifier DistributedNotificationCenter.default().post( name: Notification.Name(Self.cmuxThemesReloadNotificationName), object: nil, @@ -8726,7 +8726,7 @@ struct CMUXCLI { return ThemeReloadStatus(requested: true, targetBundleIdentifier: bundleIdentifier) } - private func currentCmuxAppBundleIdentifier() -> String? { + private func currentProgramaAppBundleIdentifier() -> String? { if let bundleIdentifier = ProcessInfo.processInfo.environment["PROGRAMA_BUNDLE_ID"]?.trimmingCharacters(in: .whitespacesAndNewlines), !bundleIdentifier.isEmpty { return bundleIdentifier @@ -10244,7 +10244,7 @@ struct CMUXCLI { } } - private func isCmuxClaudeWrapper(at path: String) -> Bool { + private func isProgramaClaudeWrapper(at path: String) -> Bool { guard let data = FileManager.default.contents(atPath: path) else { return false } let prefixData = data.prefix(512) guard let prefix = String(data: prefixData, encoding: .utf8) else { return false } @@ -10272,7 +10272,7 @@ struct CMUXCLI { resolveExecutableInSearchPath( "claude", searchPath: searchPath, - skip: { self.isCmuxClaudeWrapper(at: $0) } + skip: { self.isProgramaClaudeWrapper(at: $0) } ) } @@ -10467,7 +10467,7 @@ struct CMUXCLI { guard FileManager.default.fileExists(atPath: trimmed, isDirectory: &isDir), !isDir.boolValue, FileManager.default.isExecutableFile(atPath: trimmed), - !isCmuxClaudeWrapper(at: trimmed) else { continue } + !isProgramaClaudeWrapper(at: trimmed) else { continue } return trimmed } return resolveClaudeExecutable(searchPath: launcherEnvironment["PATH"]) diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index ff92a9ee6bb..46cf67a3e8e 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -33,7 +33,7 @@ final class MainWindowHostingView: NSHostingView { } } -private enum CmuxThemeNotifications { +private enum ProgramaThemeNotifications { static let reloadConfig = Notification.Name("com.darkroom.programa.themes.reload-config") } @@ -72,7 +72,7 @@ func isCommandPaletteFocusStealingTerminalOrBrowserView(_ view: NSView) -> Bool } #if DEBUG -enum CmuxTypingTiming { +enum ProgramaTypingTiming { static let isEnabled: Bool = { let environment = ProcessInfo.processInfo.environment if environment["PROGRAMA_TYPING_TIMING_LOGS"] == "1" || environment["PROGRAMA_KEY_LATENCY_PROBE"] == "1" { @@ -108,7 +108,7 @@ enum CmuxTypingTiming { @inline(__always) static func logDuration(path: String, startedAt: TimeInterval?, event: NSEvent? = nil, extra: String? = nil) { - CmuxMainThreadTurnProfiler.endMeasure(path, startedAt: startedAt) + ProgramaMainThreadTurnProfiler.endMeasure(path, startedAt: startedAt) guard let startedAt else { return } let elapsedMs = max(0, (ProcessInfo.processInfo.systemUptime - startedAt) * 1000.0) let delayMs: Double? = { @@ -188,8 +188,8 @@ enum CmuxTypingTiming { } } -final class CmuxMainRunLoopStallMonitor { - static let shared = CmuxMainRunLoopStallMonitor() +final class ProgramaMainRunLoopStallMonitor { + static let shared = ProgramaMainRunLoopStallMonitor() private let thresholdMs: Double = 8.0 private var observer: CFRunLoopObserver? @@ -200,7 +200,7 @@ final class CmuxMainRunLoopStallMonitor { private init() {} func installIfNeeded() { - guard CmuxTypingTiming.isEnabled else { return } + guard ProgramaTypingTiming.isEnabled else { return } guard !installed else { return } var context = CFRunLoopObserverContext( @@ -218,7 +218,7 @@ final class CmuxMainRunLoopStallMonitor { CFIndex.max, { _, activity, info in guard let info else { return } - let monitor = Unmanaged.fromOpaque(info).takeUnretainedValue() + let monitor = Unmanaged.fromOpaque(info).takeUnretainedValue() monitor.handle(activity: activity) }, &context @@ -274,8 +274,8 @@ final class CmuxMainRunLoopStallMonitor { } } -final class CmuxMainThreadTurnProfiler { - static let shared = CmuxMainThreadTurnProfiler() +final class ProgramaMainThreadTurnProfiler { + static let shared = ProgramaMainThreadTurnProfiler() private struct BucketStats { var count: Int = 0 @@ -294,13 +294,13 @@ final class CmuxMainThreadTurnProfiler { @inline(__always) static func endMeasure(_ bucket: String, startedAt: TimeInterval?) { - guard let startedAt, CmuxTypingTiming.isEnabled, Thread.isMainThread else { return } + guard let startedAt, ProgramaTypingTiming.isEnabled, Thread.isMainThread else { return } let elapsedMs = max(0, (ProcessInfo.processInfo.systemUptime - startedAt) * 1000.0) shared.record(bucket: bucket, elapsedMs: elapsedMs, count: 1) } func installIfNeeded() { - guard CmuxTypingTiming.isEnabled else { return } + guard ProgramaTypingTiming.isEnabled else { return } guard !installed else { return } var context = CFRunLoopObserverContext( @@ -318,7 +318,7 @@ final class CmuxMainThreadTurnProfiler { CFIndex.max, { _, activity, info in guard let info else { return } - let profiler = Unmanaged.fromOpaque(info).takeUnretainedValue() + let profiler = Unmanaged.fromOpaque(info).takeUnretainedValue() profiler.handle(activity: activity) }, &context @@ -1316,7 +1316,7 @@ enum WorkspaceShortcutMapper { } } -struct CmuxCLIPathInstaller { +struct ProgramaCLIPathInstaller { struct InstallOutcome { let usedAdministratorPrivileges: Bool let destinationURL: URL @@ -1369,9 +1369,9 @@ struct CmuxCLIPathInstaller { fileManager: FileManager = .default, destinationURL: URL = URL(fileURLWithPath: "/usr/local/bin/cmux"), bundledCLIURLProvider: @escaping () -> URL? = { - CmuxCLIPathInstaller.defaultBundledCLIURL() + ProgramaCLIPathInstaller.defaultBundledCLIURL() }, - expectedBundledCLIPath: String = CmuxCLIPathInstaller.defaultBundledCLIExpectedPath(), + expectedBundledCLIPath: String = ProgramaCLIPathInstaller.defaultBundledCLIExpectedPath(), privilegedInstaller: PrivilegedInstallHandler? = nil, privilegedUninstaller: PrivilegedUninstallHandler? = nil ) { @@ -1697,7 +1697,7 @@ func browserResponderHasMarkedText(_ responder: NSResponder?) -> Bool { // synchronous hasMarkedText() check above can return false even though an IME // composition just ended on the same Enter keystroke. Check the JS bridge's // composition timestamp to detect this race condition (#2626). - if let webView = responder.cmuxEnclosingCmuxWebView { + if let webView = responder.cmuxEnclosingProgramaWebView { if webView.webViewIsComposing { return true } let age = ProcessInfo.processInfo.systemUptime - webView.recentCompositionEndTimestamp if age >= 0 && age < 0.15 { return true } @@ -1707,11 +1707,11 @@ func browserResponderHasMarkedText(_ responder: NSResponder?) -> Bool { } private extension NSResponder { - /// Walk the responder chain to find the enclosing CmuxWebView. - var cmuxEnclosingCmuxWebView: CmuxWebView? { + /// Walk the responder chain to find the enclosing ProgramaWebView. + var cmuxEnclosingProgramaWebView: ProgramaWebView? { var current: NSResponder? = self while let responder = current { - if let webView = responder as? CmuxWebView { return webView } + if let webView = responder as? ProgramaWebView { return webView } current = responder.nextResponder } return nil @@ -2040,7 +2040,7 @@ private enum BrowserFindCommandEquivalent { case hideFind case useSelection - var keepsCmuxBrowserFindBarOwnershipWhenVisible: Bool { + var keepsProgramaBrowserFindBarOwnershipWhenVisible: Bool { switch self { case .find, .findNext, .findPrevious, .hideFind: return true @@ -2119,7 +2119,7 @@ private func browserFindCommandEquivalent(for event: NSEvent) -> BrowserFindComm func shouldRouteBrowserFindCommandEquivalentThroughWebContentFirst( _ event: NSEvent, responder: NSResponder? = nil, - owningWebView: CmuxWebView? = nil + owningWebView: ProgramaWebView? = nil ) -> Bool { guard let shortcut = browserFindCommandEquivalent(for: event) else { return false @@ -2129,7 +2129,7 @@ func shouldRouteBrowserFindCommandEquivalentThroughWebContentFirst( return false } - if shortcut.keepsCmuxBrowserFindBarOwnershipWhenVisible, + if shortcut.keepsProgramaBrowserFindBarOwnershipWhenVisible, let owningWebView { let browserFindBarIsVisible = MainActor.assumeIsolated { AppDelegate.shared?.browserFindBarIsVisible(for: owningWebView) == true @@ -2634,7 +2634,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent DistributedNotificationCenter.default().addObserver( self, selector: #selector(handleThemesReloadNotification(_:)), - name: CmuxThemeNotifications.reloadConfig, + name: ProgramaThemeNotifications.reloadConfig, object: nil, suspensionBehavior: .deliverImmediately ) @@ -2655,8 +2655,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent #if DEBUG writeUITestDiagnosticsIfNeeded(stage: "didFinishLaunching") - CmuxMainRunLoopStallMonitor.shared.installIfNeeded() - CmuxMainThreadTurnProfiler.shared.installIfNeeded() + ProgramaMainRunLoopStallMonitor.shared.installIfNeeded() + ProgramaMainThreadTurnProfiler.shared.installIfNeeded() DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in self?.writeUITestDiagnosticsIfNeeded(stage: "after1s") } @@ -3062,9 +3062,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent DispatchQueue.main.async { let alert = NSAlert() alert.alertStyle = .warning - alert.messageText = String(localized: "dialog.quitCmux.title", defaultValue: "Quit cmux?") - alert.informativeText = String(localized: "dialog.quitCmux.message", defaultValue: "This will close all windows and workspaces.") - alert.addButton(withTitle: String(localized: "dialog.quitCmux.quit", defaultValue: "Quit")) + alert.messageText = String(localized: "dialog.quitPrograma.title", defaultValue: "Quit cmux?") + alert.informativeText = String(localized: "dialog.quitPrograma.message", defaultValue: "This will close all windows and workspaces.") + alert.addButton(withTitle: String(localized: "dialog.quitPrograma.quit", defaultValue: "Quit")) alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel")) alert.showsSuppressionButton = true alert.suppressionButton?.title = String(localized: "dialog.dontWarnCmdQ", defaultValue: "Don't warn again for Cmd+Q") @@ -4559,9 +4559,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent includeScrollback: includeScrollback ) #if DEBUG - let timingStart = CmuxTypingTiming.start() + let timingStart = ProgramaTypingTiming.start() defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "session.saveSnapshot", startedAt: timingStart, extra: "includeScrollback=\(includeScrollback ? 1 : 0) removeWhenEmpty=\(removeWhenEmpty ? 1 : 0) sync=\(writeSynchronously ? 1 : 0)" @@ -4657,14 +4657,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent sessionAutosaveTickInFlight = true #if DEBUG - let timingStart = CmuxTypingTiming.start() + let timingStart = ProgramaTypingTiming.start() let phaseStart = ProcessInfo.processInfo.systemUptime var fingerprintMs: Double = 0 var saveMs: Double = 0 defer { sessionAutosaveTickInFlight = false let totalMs = (ProcessInfo.processInfo.systemUptime - phaseStart) * 1000.0 - CmuxTypingTiming.logBreakdown( + ProgramaTypingTiming.logBreakdown( path: "session.autosaveTick.phase", totalMs: totalMs, thresholdMs: 2.0, @@ -4674,7 +4674,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent ], extra: "source=\(source)" ) - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "session.autosaveTick", startedAt: timingStart, extra: "source=\(source)" @@ -7194,7 +7194,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent ) let notificationStore = TerminalNotificationStore.shared - let cmuxConfigStore = CmuxConfigStore() + let cmuxConfigStore = ProgramaConfigStore() cmuxConfigStore.wireDirectoryTracking(tabManager: tabManager) cmuxConfigStore.loadAll() @@ -7353,12 +7353,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent updateController.attemptUpdate() } - func isCmuxCLIInstalledInPATH() -> Bool { - CmuxCLIPathInstaller().isInstalled() + func isProgramaCLIInstalledInPATH() -> Bool { + ProgramaCLIPathInstaller().isInstalled() } - @objc func installCmuxCLIInPath(_ sender: Any?) { - let installer = CmuxCLIPathInstaller() + @objc func installProgramaCLIInPath(_ sender: Any?) { + let installer = ProgramaCLIPathInstaller() do { let outcome = try installer.install() var informativeText = String(localized: "cli.install.symlinkCreated", defaultValue: "Created symlink:\n\n\(outcome.destinationURL.path) -> \(outcome.sourceURL.path)") @@ -7379,8 +7379,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent } } - @objc func uninstallCmuxCLIInPath(_ sender: Any?) { - let installer = CmuxCLIPathInstaller() + @objc func uninstallProgramaCLIInPath(_ sender: Any?) { + let installer = ProgramaCLIPathInstaller() do { let outcome = try installer.uninstall() let prefix = outcome.removedExistingEntry @@ -10356,7 +10356,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent let preludeStart = ProcessInfo.processInfo.systemUptime var preludeMs: Double = 0 var shortcutMs: Double = 0 - CmuxTypingTiming.logEventDelay(path: "appMonitor", event: event) + ProgramaTypingTiming.logEventDelay(path: "appMonitor", event: event) let shortcutMonitorTraceEnabled = ProcessInfo.processInfo.environment["PROGRAMA_SHORTCUT_MONITOR_TRACE"] == "1" || UserDefaults.standard.bool(forKey: "cmuxShortcutMonitorTrace") @@ -10370,13 +10370,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent self.logDeveloperToolsShortcutSnapshot(phase: "monitor.pre.\(probeKind)", event: event) } preludeMs = (ProcessInfo.processInfo.systemUptime - preludeStart) * 1000.0 - let shortcutTimingStart = CmuxTypingTiming.start() + let shortcutTimingStart = ProgramaTypingTiming.start() #endif let shortcutStart = ProcessInfo.processInfo.systemUptime let handledByShortcut = self.handleCustomShortcut(event: event) #if DEBUG shortcutMs = (ProcessInfo.processInfo.systemUptime - shortcutStart) * 1000.0 - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "appMonitor.handleCustomShortcut", startedAt: shortcutTimingStart, event: event, @@ -10389,7 +10389,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent elapsedMs: shortcutElapsedMs ) let totalMs = (ProcessInfo.processInfo.systemUptime - phaseTotalStart) * 1000.0 - CmuxTypingTiming.logBreakdown( + ProgramaTypingTiming.logBreakdown( path: "appMonitor.phase", totalMs: totalMs, event: event, @@ -10602,9 +10602,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent let alert = NSAlert() alert.alertStyle = .warning - alert.messageText = String(localized: "dialog.quitCmux.title", defaultValue: "Quit cmux?") - alert.informativeText = String(localized: "dialog.quitCmux.message", defaultValue: "This will close all windows and workspaces.") - alert.addButton(withTitle: String(localized: "dialog.quitCmux.quit", defaultValue: "Quit")) + alert.messageText = String(localized: "dialog.quitPrograma.title", defaultValue: "Quit cmux?") + alert.informativeText = String(localized: "dialog.quitPrograma.message", defaultValue: "This will close all windows and workspaces.") + alert.addButton(withTitle: String(localized: "dialog.quitPrograma.quit", defaultValue: "Quit")) alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel")) alert.showsSuppressionButton = true alert.suppressionButton?.title = String(localized: "dialog.dontWarnCmdQ", defaultValue: "Don't warn again for Cmd+Q") @@ -13006,14 +13006,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent return tabManager?.selectedWorkspace?.browserPanel(for: panelId) } - fileprivate func browserFindBarIsVisible(for webView: CmuxWebView) -> Bool { + fileprivate func browserFindBarIsVisible(for webView: ProgramaWebView) -> Bool { browserPanelOwning(webView)?.searchState != nil } private func shouldLetFocusedBrowserOwnFindShortcut(_ event: NSEvent) -> Bool { let shortcutWindow = resolvedShortcutEventWindow(event) ?? NSApp.keyWindow ?? NSApp.mainWindow let shortcutResponder = shortcutWindow?.firstResponder - let owningWebView = tabManager?.focusedBrowserPanel?.webView as? CmuxWebView + let owningWebView = tabManager?.focusedBrowserPanel?.webView as? ProgramaWebView guard let owningWebView else { return false } return shouldRouteBrowserFindCommandEquivalentThroughWebContentFirst( event, @@ -13022,7 +13022,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent ) } - private func browserPanelOwning(_ webView: CmuxWebView) -> BrowserPanel? { + private func browserPanelOwning(_ webView: ProgramaWebView) -> BrowserPanel? { var candidateManagers: [TabManager] = [] var seenManagers = Set() @@ -13050,7 +13050,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent return nil } - private func browserPanelOwning(_ webView: CmuxWebView, in manager: TabManager) -> BrowserPanel? { + private func browserPanelOwning(_ webView: ProgramaWebView, in manager: TabManager) -> BrowserPanel? { for workspace in manager.tabs { if let panel = workspace.panels.values .compactMap({ $0 as? BrowserPanel }) @@ -13512,7 +13512,7 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate { private let clearAllItem = NSMenuItem(title: String(localized: "statusMenu.clearAll", defaultValue: "Clear All"), action: nil, keyEquivalent: "") private let checkForUpdatesItem = NSMenuItem(title: String(localized: "menu.checkForUpdates", defaultValue: "Check for Updates…"), action: nil, keyEquivalent: "") private let preferencesItem = NSMenuItem(title: String(localized: "menu.preferences", defaultValue: "Preferences…"), action: nil, keyEquivalent: "") - private let quitItem = NSMenuItem(title: String(localized: "menu.quitCmux", defaultValue: "Quit cmux"), action: nil, keyEquivalent: "") + private let quitItem = NSMenuItem(title: String(localized: "menu.quitPrograma", defaultValue: "Quit cmux"), action: nil, keyEquivalent: "") private var notificationItems: [NSMenuItem] = [] private let maxInlineNotificationItems = 6 @@ -14173,10 +14173,10 @@ func cmuxIsWindowFirstResponderBypassActive() -> Bool { cmuxWindowFirstResponderBypassDepth > 0 } -private final class CmuxFieldEditorOwningWebViewBox: NSObject { - weak var webView: CmuxWebView? +private final class ProgramaFieldEditorOwningWebViewBox: NSObject { + weak var webView: ProgramaWebView? - init(webView: CmuxWebView?) { + init(webView: ProgramaWebView?) { self.webView = webView } } @@ -14184,22 +14184,22 @@ private final class CmuxFieldEditorOwningWebViewBox: NSObject { private extension NSApplication { @objc func cmux_applicationSendEvent(_ event: NSEvent) { #if DEBUG - let typingTimingStart = event.type == .keyDown ? CmuxTypingTiming.start() : nil + let typingTimingStart = event.type == .keyDown ? ProgramaTypingTiming.start() : nil let phaseTotalStart = event.type == .keyDown ? ProcessInfo.processInfo.systemUptime : 0 if event.type == .keyDown { - CmuxTypingTiming.logEventDelay(path: "app.sendEvent", event: event) + ProgramaTypingTiming.logEventDelay(path: "app.sendEvent", event: event) } defer { if event.type == .keyDown { let totalMs = (ProcessInfo.processInfo.systemUptime - phaseTotalStart) * 1000.0 - CmuxTypingTiming.logBreakdown( + ProgramaTypingTiming.logBreakdown( path: "app.sendEvent.phase", totalMs: totalMs, event: event, thresholdMs: 1.0, parts: [("dispatchMs", totalMs)] ) - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "app.sendEvent", startedAt: typingTimingStart, event: event @@ -14298,7 +14298,7 @@ private extension NSWindow { #endif let result: Bool if pointerInitiatedWebFocus, let webView = responderWebView { - // `NSWindow.makeFirstResponder` may run before `CmuxWebView.mouseDown(with:)`. + // `NSWindow.makeFirstResponder` may run before `ProgramaWebView.mouseDown(with:)`. // Preserve pointer intent during this synchronous responder change. result = webView.withPointerFocusAllowance { cmux_makeFirstResponder(responder) @@ -14318,7 +14318,7 @@ private extension NSWindow { @objc func cmux_sendEvent(_ event: NSEvent) { #if DEBUG - let typingTimingStart = event.type == .keyDown ? CmuxTypingTiming.start() : nil + let typingTimingStart = event.type == .keyDown ? ProgramaTypingTiming.start() : nil let phaseTotalStart = event.type == .keyDown ? ProcessInfo.processInfo.systemUptime : 0 var contextSetupMs: Double = 0 var focusRepairMs: Double = 0 @@ -14336,7 +14336,7 @@ private extension NSWindow { return "browser=\((responderWebView != nil || hitWebView != nil) ? 1 : 0) firstResponder=\(firstResponderType)" }() if event.type == .keyDown { - CmuxTypingTiming.logEventDelay(path: "window.sendEvent", event: event) + ProgramaTypingTiming.logEventDelay(path: "window.sendEvent", event: event) } #endif // recordTypingActivity must run in all builds so runSessionAutosaveTick @@ -14348,7 +14348,7 @@ private extension NSWindow { defer { if event.type == .keyDown { let totalMs = (ProcessInfo.processInfo.systemUptime - phaseTotalStart) * 1000.0 - CmuxTypingTiming.logBreakdown( + ProgramaTypingTiming.logBreakdown( path: "window.sendEvent.phase", totalMs: totalMs, event: event, @@ -14361,7 +14361,7 @@ private extension NSWindow { ], extra: typingTimingExtra ) - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "window.sendEvent", startedAt: typingTimingStart, event: event, @@ -14452,9 +14452,9 @@ private extension NSWindow { @objc func cmux_performKeyEquivalent(with event: NSEvent) -> Bool { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "window.performKeyEquivalent", startedAt: typingTimingStart, event: event @@ -14624,8 +14624,8 @@ private extension NSWindow { return parts.joined(separator: "+") } - private static func cmuxOwningWebView(for responder: NSResponder) -> CmuxWebView? { - if let webView = responder as? CmuxWebView { + private static func cmuxOwningWebView(for responder: NSResponder) -> ProgramaWebView? { + if let webView = responder as? ProgramaWebView { return webView } @@ -14638,7 +14638,7 @@ private extension NSWindow { // a responder chain is tearing down can trap with "unowned reference". var current = responder.nextResponder while let next = current { - if let webView = next as? CmuxWebView { + if let webView = next as? ProgramaWebView { return webView } if let view = next as? NSView, @@ -14655,7 +14655,7 @@ private extension NSWindow { for responder: NSResponder, in window: NSWindow, event: NSEvent? - ) -> CmuxWebView? { + ) -> ProgramaWebView? { // Browser find runs in the portal slot alongside the hosted WKWebView. // Treat its native field editor chain as browser chrome, not as web content, // so Cmd+F can move first responder into the find field while web focus is suppressed. @@ -14680,14 +14680,14 @@ private extension NSWindow { return cmuxTrackedOwningWebView(for: textView) } - private static func cmuxOwningWebView(for view: NSView) -> CmuxWebView? { - if let webView = view as? CmuxWebView { + private static func cmuxOwningWebView(for view: NSView) -> ProgramaWebView? { + if let webView = view as? ProgramaWebView { return webView } var current: NSView? = view.superview while let candidate = current { - if let webView = candidate as? CmuxWebView { + if let webView = candidate as? ProgramaWebView { return webView } if String(describing: type(of: candidate)).contains("WindowBrowserSlotView"), @@ -14726,11 +14726,11 @@ private extension NSWindow { return false } - private static func cmuxUniqueBrowserWebView(in root: NSView) -> CmuxWebView? { + private static func cmuxUniqueBrowserWebView(in root: NSView) -> ProgramaWebView? { var stack: [NSView] = [root] - var found: CmuxWebView? + var found: ProgramaWebView? while let current = stack.popLast() { - if let webView = current as? CmuxWebView { + if let webView = current as? ProgramaWebView { if found == nil { found = webView } else if found !== webView { @@ -14801,12 +14801,12 @@ private extension NSWindow { return cmuxTopHitViewForEvent(in: window, event: event) } - private static func cmuxTrackFieldEditor(_ fieldEditor: NSTextView, owningWebView webView: CmuxWebView?) { + private static func cmuxTrackFieldEditor(_ fieldEditor: NSTextView, owningWebView webView: ProgramaWebView?) { if let webView { objc_setAssociatedObject( fieldEditor, &cmuxFieldEditorOwningWebViewAssociationKey, - CmuxFieldEditorOwningWebViewBox(webView: webView), + ProgramaFieldEditorOwningWebViewBox(webView: webView), .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) } else { @@ -14819,11 +14819,11 @@ private extension NSWindow { } } - private static func cmuxTrackedOwningWebView(for fieldEditor: NSTextView) -> CmuxWebView? { + private static func cmuxTrackedOwningWebView(for fieldEditor: NSTextView) -> ProgramaWebView? { guard let box = objc_getAssociatedObject( fieldEditor, &cmuxFieldEditorOwningWebViewAssociationKey - ) as? CmuxFieldEditorOwningWebViewBox else { + ) as? ProgramaFieldEditorOwningWebViewBox else { return nil } guard let webView = box.webView else { @@ -14842,7 +14842,7 @@ private extension NSWindow { } } - private static func cmuxPointerHitWebView(in window: NSWindow, event: NSEvent) -> CmuxWebView? { + private static func cmuxPointerHitWebView(in window: NSWindow, event: NSEvent) -> ProgramaWebView? { guard cmuxIsPointerDownEvent(event) else { return nil } if event.windowNumber != 0, event.windowNumber != window.windowNumber { return nil @@ -14853,7 +14853,7 @@ private extension NSWindow { if let portalWebView = BrowserWindowPortalRegistry.webViewAtWindowPoint( event.locationInWindow, in: window - ) as? CmuxWebView { + ) as? ProgramaWebView { return portalWebView } guard let hitView = cmuxHitViewForCurrentEvent(in: window, event: event) else { @@ -14864,7 +14864,7 @@ private extension NSWindow { private static func cmuxShouldAllowPointerInitiatedWebViewFocus( window: NSWindow, - webView: CmuxWebView, + webView: ProgramaWebView, event: NSEvent? ) -> Bool { guard let event, diff --git a/Sources/CmuxConfig.swift b/Sources/CmuxConfig.swift index 4fe7f8ec0b7..3fe982c5568 100644 --- a/Sources/CmuxConfig.swift +++ b/Sources/CmuxConfig.swift @@ -2,16 +2,16 @@ import Bonsplit import Combine import Foundation -struct CmuxConfigFile: Codable, Sendable { - var commands: [CmuxCommandDefinition] +struct ProgramaConfigFile: Codable, Sendable { + var commands: [ProgramaCommandDefinition] } -struct CmuxCommandDefinition: Codable, Sendable, Identifiable { +struct ProgramaCommandDefinition: Codable, Sendable, Identifiable { var name: String var description: String? var keywords: [String]? - var restart: CmuxRestartBehavior? - var workspace: CmuxWorkspaceDefinition? + var restart: ProgramaRestartBehavior? + var workspace: ProgramaWorkspaceDefinition? var command: String? var confirm: Bool? @@ -23,8 +23,8 @@ struct CmuxCommandDefinition: Codable, Sendable, Identifiable { name: String, description: String? = nil, keywords: [String]? = nil, - restart: CmuxRestartBehavior? = nil, - workspace: CmuxWorkspaceDefinition? = nil, + restart: ProgramaRestartBehavior? = nil, + workspace: ProgramaWorkspaceDefinition? = nil, command: String? = nil, confirm: Bool? = nil ) { @@ -42,8 +42,8 @@ struct CmuxCommandDefinition: Codable, Sendable, Identifiable { name = try container.decode(String.self, forKey: .name) description = try container.decodeIfPresent(String.self, forKey: .description) keywords = try container.decodeIfPresent([String].self, forKey: .keywords) - restart = try container.decodeIfPresent(CmuxRestartBehavior.self, forKey: .restart) - workspace = try container.decodeIfPresent(CmuxWorkspaceDefinition.self, forKey: .workspace) + restart = try container.decodeIfPresent(ProgramaRestartBehavior.self, forKey: .restart) + workspace = try container.decodeIfPresent(ProgramaWorkspaceDefinition.self, forKey: .workspace) command = try container.decodeIfPresent(String.self, forKey: .command) confirm = try container.decodeIfPresent(Bool.self, forKey: .confirm) @@ -84,19 +84,19 @@ struct CmuxCommandDefinition: Codable, Sendable, Identifiable { } } -enum CmuxRestartBehavior: String, Codable, Sendable { +enum ProgramaRestartBehavior: String, Codable, Sendable { case recreate case ignore case confirm } -struct CmuxWorkspaceDefinition: Codable, Sendable { +struct ProgramaWorkspaceDefinition: Codable, Sendable { var name: String? var cwd: String? var color: String? - var layout: CmuxLayoutNode? + var layout: ProgramaLayoutNode? - init(name: String? = nil, cwd: String? = nil, color: String? = nil, layout: CmuxLayoutNode? = nil) { + init(name: String? = nil, cwd: String? = nil, color: String? = nil, layout: ProgramaLayoutNode? = nil) { self.name = name self.cwd = cwd self.color = color @@ -107,7 +107,7 @@ struct CmuxWorkspaceDefinition: Codable, Sendable { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decodeIfPresent(String.self, forKey: .name) cwd = try container.decodeIfPresent(String.self, forKey: .cwd) - layout = try container.decodeIfPresent(CmuxLayoutNode.self, forKey: .layout) + layout = try container.decodeIfPresent(ProgramaLayoutNode.self, forKey: .layout) if let rawColor = try container.decodeIfPresent(String.self, forKey: .color) { guard let normalized = WorkspaceTabColorSettings.normalizedHex(rawColor) else { @@ -124,9 +124,9 @@ struct CmuxWorkspaceDefinition: Codable, Sendable { } } -indirect enum CmuxLayoutNode: Codable, Sendable { - case pane(CmuxPaneDefinition) - case split(CmuxSplitDefinition) +indirect enum ProgramaLayoutNode: Codable, Sendable { + case pane(ProgramaPaneDefinition) + case split(ProgramaSplitDefinition) private enum CodingKeys: String, CodingKey { case pane @@ -144,22 +144,22 @@ indirect enum CmuxLayoutNode: Codable, Sendable { throw DecodingError.dataCorrupted( DecodingError.Context( codingPath: decoder.codingPath, - debugDescription: "CmuxLayoutNode must not contain both 'pane' and 'direction' keys" + debugDescription: "ProgramaLayoutNode must not contain both 'pane' and 'direction' keys" ) ) } if hasPane { - let pane = try container.decode(CmuxPaneDefinition.self, forKey: .pane) + let pane = try container.decode(ProgramaPaneDefinition.self, forKey: .pane) self = .pane(pane) } else if hasDirection { - let splitDef = try CmuxSplitDefinition(from: decoder) + let splitDef = try ProgramaSplitDefinition(from: decoder) self = .split(splitDef) } else { throw DecodingError.dataCorrupted( DecodingError.Context( codingPath: decoder.codingPath, - debugDescription: "CmuxLayoutNode must contain either a 'pane' key or a 'direction' key" + debugDescription: "ProgramaLayoutNode must contain either a 'pane' key or a 'direction' key" ) ) } @@ -176,12 +176,12 @@ indirect enum CmuxLayoutNode: Codable, Sendable { } } -struct CmuxSplitDefinition: Codable, Sendable { - var direction: CmuxSplitDirection +struct ProgramaSplitDefinition: Codable, Sendable { + var direction: ProgramaSplitDirection var split: Double? - var children: [CmuxLayoutNode] + var children: [ProgramaLayoutNode] - init(direction: CmuxSplitDirection, split: Double? = nil, children: [CmuxLayoutNode]) { + init(direction: ProgramaSplitDirection, split: Double? = nil, children: [ProgramaLayoutNode]) { self.direction = direction self.split = split self.children = children @@ -189,9 +189,9 @@ struct CmuxSplitDefinition: Codable, Sendable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - direction = try container.decode(CmuxSplitDirection.self, forKey: .direction) + direction = try container.decode(ProgramaSplitDirection.self, forKey: .direction) split = try container.decodeIfPresent(Double.self, forKey: .split) - children = try container.decode([CmuxLayoutNode].self, forKey: .children) + children = try container.decode([ProgramaLayoutNode].self, forKey: .children) if children.count != 2 { throw DecodingError.dataCorrupted( DecodingError.Context( @@ -215,21 +215,21 @@ struct CmuxSplitDefinition: Codable, Sendable { } } -enum CmuxSplitDirection: String, Codable, Sendable { +enum ProgramaSplitDirection: String, Codable, Sendable { case horizontal case vertical } -struct CmuxPaneDefinition: Codable, Sendable { - var surfaces: [CmuxSurfaceDefinition] +struct ProgramaPaneDefinition: Codable, Sendable { + var surfaces: [ProgramaSurfaceDefinition] - init(surfaces: [CmuxSurfaceDefinition]) { + init(surfaces: [ProgramaSurfaceDefinition]) { self.surfaces = surfaces } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - surfaces = try container.decode([CmuxSurfaceDefinition].self, forKey: .surfaces) + surfaces = try container.decode([ProgramaSurfaceDefinition].self, forKey: .surfaces) if surfaces.isEmpty { throw DecodingError.dataCorrupted( DecodingError.Context( @@ -241,8 +241,8 @@ struct CmuxPaneDefinition: Codable, Sendable { } } -struct CmuxSurfaceDefinition: Codable, Sendable { - var type: CmuxSurfaceType +struct ProgramaSurfaceDefinition: Codable, Sendable { + var type: ProgramaSurfaceType var name: String? var command: String? var cwd: String? @@ -251,14 +251,14 @@ struct CmuxSurfaceDefinition: Codable, Sendable { var focus: Bool? } -enum CmuxSurfaceType: String, Codable, Sendable { +enum ProgramaSurfaceType: String, Codable, Sendable { case terminal case browser } @MainActor -final class CmuxConfigStore: ObservableObject { - @Published private(set) var loadedCommands: [CmuxCommandDefinition] = [] +final class ProgramaConfigStore: ObservableObject { + @Published private(set) var loadedCommands: [ProgramaCommandDefinition] = [] @Published private(set) var configRevision: UInt64 = 0 /// Which config file each command came from, keyed by command id. @@ -319,7 +319,7 @@ final class CmuxConfigStore: ObservableObject { private func updateLocalConfigPath(_ directory: String?) { let newPath: String? if let directory, !directory.isEmpty { - newPath = findCmuxConfig(startingFrom: directory) + newPath = findProgramaConfig(startingFrom: directory) ?? (directory as NSString).appendingPathComponent("cmux.json") } else { newPath = nil @@ -334,7 +334,7 @@ final class CmuxConfigStore: ObservableObject { loadAll() } - private func findCmuxConfig(startingFrom directory: String) -> String? { + private func findProgramaConfig(startingFrom directory: String) -> String? { var current = directory let fs = FileManager.default while true { @@ -350,7 +350,7 @@ final class CmuxConfigStore: ObservableObject { } func loadAll() { - var commands: [CmuxCommandDefinition] = [] + var commands: [ProgramaCommandDefinition] = [] var seenNames = Set() var sourcePaths: [String: String] = [:] @@ -385,16 +385,16 @@ final class CmuxConfigStore: ObservableObject { // MARK: - Parsing - private func parseConfig(at path: String) -> CmuxConfigFile? { + private func parseConfig(at path: String) -> ProgramaConfigFile? { guard FileManager.default.fileExists(atPath: path), let data = FileManager.default.contents(atPath: path), !data.isEmpty else { return nil } do { - return try JSONDecoder().decode(CmuxConfigFile.self, from: data) + return try JSONDecoder().decode(ProgramaConfigFile.self, from: data) } catch { - NSLog("[CmuxConfig] parse error at %@: %@", path, String(describing: error)) + NSLog("[ProgramaConfig] parse error at %@: %@", path, String(describing: error)) return nil } } @@ -599,7 +599,7 @@ final class CmuxConfigStore: ObservableObject { } } -extension CmuxConfigStore { +extension ProgramaConfigStore { static func resolveCwd(_ cwd: String?, relativeTo baseCwd: String) -> String { guard let cwd, !cwd.isEmpty, cwd != "." else { return baseCwd diff --git a/Sources/CmuxConfigExecutor.swift b/Sources/CmuxConfigExecutor.swift index df7276155c2..380c351a0d1 100644 --- a/Sources/CmuxConfigExecutor.swift +++ b/Sources/CmuxConfigExecutor.swift @@ -2,10 +2,10 @@ import AppKit import Foundation @MainActor -struct CmuxConfigExecutor { +struct ProgramaConfigExecutor { static func execute( - command: CmuxCommandDefinition, + command: ProgramaCommandDefinition, tabManager: TabManager, baseCwd: String, configSourcePath: String?, @@ -17,7 +17,7 @@ struct CmuxConfigExecutor { let shellCommand = sanitizeForDisplay(rawCommand) let needsConfirm = command.confirm ?? false if needsConfirm, let sourcePath = configSourcePath { - let trusted = CmuxDirectoryTrust.shared.isTrusted( + let trusted = ProgramaDirectoryTrust.shared.isTrusted( configPath: sourcePath, globalConfigPath: globalConfigPath ) @@ -64,7 +64,7 @@ struct CmuxConfigExecutor { guard response == .alertFirstButtonReturn else { return false } if checkbox.state == .on { - CmuxDirectoryTrust.shared.trust(configPath: configPath) + ProgramaDirectoryTrust.shared.trust(configPath: configPath) } return true @@ -82,8 +82,8 @@ struct CmuxConfigExecutor { } private static func executeWorkspaceCommand( - command: CmuxCommandDefinition, - workspace wsDef: CmuxWorkspaceDefinition, + command: ProgramaCommandDefinition, + workspace wsDef: ProgramaWorkspaceDefinition, tabManager: TabManager, baseCwd: String ) { @@ -118,7 +118,7 @@ struct CmuxConfigExecutor { } } - let resolvedCwd = CmuxConfigStore.resolveCwd(wsDef.cwd, relativeTo: baseCwd) + let resolvedCwd = ProgramaConfigStore.resolveCwd(wsDef.cwd, relativeTo: baseCwd) let newWorkspace = tabManager.addWorkspace(workingDirectory: resolvedCwd) newWorkspace.setCustomTitle(workspaceName) if let color = wsDef.color { diff --git a/Sources/CmuxDirectoryTrust.swift b/Sources/CmuxDirectoryTrust.swift index 2f73234425f..a9a42906434 100644 --- a/Sources/CmuxDirectoryTrust.swift +++ b/Sources/CmuxDirectoryTrust.swift @@ -4,8 +4,8 @@ import Foundation /// When a directory (or its git repo root) is trusted, `confirm: true` commands /// from that directory's cmux.json skip the confirmation dialog. /// Global config (~/.config/cmux/cmux.json) is always trusted. -final class CmuxDirectoryTrust { - static let shared = CmuxDirectoryTrust() +final class ProgramaDirectoryTrust { + static let shared = ProgramaDirectoryTrust() static let didChangeNotification = Notification.Name("cmux.directoryTrustDidChange") private let storePath: String diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 50cb6d3f516..7c1c19b160c 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -1798,7 +1798,7 @@ struct ContentView: View { @EnvironmentObject var notificationStore: TerminalNotificationStore @EnvironmentObject var sidebarState: SidebarState @EnvironmentObject var sidebarSelectionState: SidebarSelectionState - @EnvironmentObject var cmuxConfigStore: CmuxConfigStore + @EnvironmentObject var cmuxConfigStore: ProgramaConfigStore @State private var sidebarWidth: CGFloat = 200 @State private var hoveredResizerHandles: Set = [] @State private var isResizerDragging = false @@ -1863,8 +1863,8 @@ struct ContentView: View { private var commandPaletteRenameSelectAllOnFocus = CommandPaletteRenameSelectionSettings.defaultSelectAllOnFocus @AppStorage(CommandPaletteSwitcherSearchSettings.searchAllSurfacesKey) private var commandPaletteSearchAllSurfaces = CommandPaletteSwitcherSearchSettings.defaultSearchAllSurfaces - @AppStorage(BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowserKey) - private var openSidebarPullRequestLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInCmuxBrowser + @AppStorage(BrowserLinkOpenSettings.openSidebarPullRequestLinksInProgramaBrowserKey) + private var openSidebarPullRequestLinksInProgramaBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInProgramaBrowser @State private var commandPaletteShouldFocusWorkspaceDescriptionEditor = false @FocusState private var isCommandPaletteSearchFocused: Bool @FocusState private var isCommandPaletteRenameFocused: Bool @@ -6077,7 +6077,7 @@ struct ContentView: View { private func commandPaletteCommandsContext( terminalOpenTargets: Set ) -> CommandPaletteCommandsContext { - let cliInstalledInPATH = AppDelegate.shared?.isCmuxCLIInstalledInPATH() ?? false + let cliInstalledInPATH = AppDelegate.shared?.isProgramaCLIInstalledInPATH() ?? false var snapshot = commandPaletteContextSnapshot(terminalOpenTargets: terminalOpenTargets) snapshot.setBool(CommandPaletteContextKeys.cliInstalledInPATH, cliInstalledInPATH) return CommandPaletteCommandsContext( @@ -7075,9 +7075,9 @@ struct ContentView: View { let cmuxConfigDefaultSubtitle = constant(String(localized: "command.cmuxConfig.subtitle", defaultValue: "cmux.json")) for command in cmuxConfigStore.loadedCommands { - let commandName = sanitizeCmuxConfigPaletteText(command.name) + let commandName = sanitizeProgramaConfigPaletteText(command.name) let subtitle = command.description - .map { sanitizeCmuxConfigPaletteText($0) } + .map { sanitizeProgramaConfigPaletteText($0) } .flatMap { $0.isEmpty ? nil : constant($0) } ?? cmuxConfigDefaultSubtitle contributions.append( @@ -7093,7 +7093,7 @@ struct ContentView: View { return contributions } - private func sanitizeCmuxConfigPaletteText(_ text: String) -> String { + private func sanitizeProgramaConfigPaletteText(_ text: String) -> String { let dangerous: Set = [ "\u{200B}", "\u{200C}", "\u{200D}", "\u{200E}", "\u{200F}", "\u{202A}", "\u{202B}", "\u{202C}", "\u{202D}", "\u{202E}", @@ -7131,10 +7131,10 @@ struct ContentView: View { AppDelegate.shared?.openNewMainWindow(nil) } registry.register(commandId: "palette.installCLI") { - AppDelegate.shared?.installCmuxCLIInPath(nil) + AppDelegate.shared?.installProgramaCLIInPath(nil) } registry.register(commandId: "palette.uninstallCLI") { - AppDelegate.shared?.uninstallCmuxCLIInPath(nil) + AppDelegate.shared?.uninstallProgramaCLIInPath(nil) } registry.register(commandId: "palette.newTerminalTab") { tabManager.newSurface() @@ -7461,7 +7461,7 @@ struct ContentView: View { let rawCwd = tabManager.selectedWorkspace?.currentDirectory let baseCwd = (rawCwd?.isEmpty == false) ? rawCwd! : FileManager.default.homeDirectoryForCurrentUser.path - CmuxConfigExecutor.execute( + ProgramaConfigExecutor.execute( command: captured, tabManager: tabManager, baseCwd: baseCwd, @@ -8679,7 +8679,7 @@ struct ContentView: View { guard !pullRequests.isEmpty else { return false } var openedCount = 0 - if openSidebarPullRequestLinksInCmuxBrowser { + if openSidebarPullRequestLinksInProgramaBrowser { for pullRequest in pullRequests { if tabManager.openBrowser(url: pullRequest.url, insertAtEnd: true) != nil { openedCount += 1 @@ -9778,8 +9778,8 @@ private struct SidebarTabItemSettingsSnapshot: Equatable { let usesVerticalBranchLayout: Bool let showsGitBranchIcon: Bool let showsSSH: Bool - let openPullRequestLinksInCmuxBrowser: Bool - let openPortLinksInCmuxBrowser: Bool + let openPullRequestLinksInProgramaBrowser: Bool + let openPortLinksInProgramaBrowser: Bool let showsNotificationMessage: Bool let activeTabIndicatorStyle: SidebarActiveTabIndicatorStyle let selectionColorHex: String? @@ -9806,10 +9806,10 @@ private struct SidebarTabItemSettingsSnapshot: Equatable { usesVerticalBranchLayout = SidebarBranchLayoutSettings.usesVerticalLayout(defaults: defaults) showsGitBranchIcon = Self.bool(defaults: defaults, key: "sidebarShowGitBranchIcon", defaultValue: false) showsSSH = Self.bool(defaults: defaults, key: "sidebarShowSSH", defaultValue: true) - openPullRequestLinksInCmuxBrowser = BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowser( + openPullRequestLinksInProgramaBrowser = BrowserLinkOpenSettings.openSidebarPullRequestLinksInProgramaBrowser( defaults: defaults ) - openPortLinksInCmuxBrowser = BrowserLinkOpenSettings.openSidebarPortLinksInCmuxBrowser( + openPortLinksInProgramaBrowser = BrowserLinkOpenSettings.openSidebarPortLinksInProgramaBrowser( defaults: defaults ) @@ -12497,12 +12497,12 @@ private struct TabItemView: View, Equatable { settings.notificationBadgeColorHex } - private var openSidebarPullRequestLinksInCmuxBrowser: Bool { - settings.openPullRequestLinksInCmuxBrowser + private var openSidebarPullRequestLinksInProgramaBrowser: Bool { + settings.openPullRequestLinksInProgramaBrowser } - private var openSidebarPortLinksInCmuxBrowser: Bool { - settings.openPortLinksInCmuxBrowser + private var openSidebarPortLinksInProgramaBrowser: Bool { + settings.openPortLinksInProgramaBrowser } private var titleFontWeight: Font.Weight { @@ -13763,7 +13763,7 @@ private struct TabItemView: View, Equatable { private func openPullRequestLink(_ url: URL) { updateSelection() - if openSidebarPullRequestLinksInCmuxBrowser { + if openSidebarPullRequestLinksInProgramaBrowser { if tabManager.openBrowser( inWorkspace: tab.id, url: url, @@ -13780,7 +13780,7 @@ private struct TabItemView: View, Equatable { private func openPortLink(_ port: Int) { guard let url = URL(string: "http://localhost:\(port)") else { return } updateSelection() - if openSidebarPortLinksInCmuxBrowser { + if openSidebarPortLinksInProgramaBrowser { if tabManager.openBrowser( inWorkspace: tab.id, url: url, diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index 9f228a4c94b..e248493ed72 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -1712,7 +1712,7 @@ class GhosttyApp { ghostty_config_load_default_files(config) loadLegacyGhosttyConfigIfNeeded(config) ghostty_config_load_recursive_files(config) - loadCmuxAppSupportGhosttyConfigIfNeeded(config) + loadProgramaAppSupportGhosttyConfigIfNeeded(config) loadCJKFontFallbackIfNeeded(config) let useHostLayerBackground = !hasConfiguredBackgroundImage(config) usesHostLayerBackground = useHostLayerBackground @@ -2230,7 +2230,7 @@ class GhosttyApp { return true } - private func loadCmuxAppSupportGhosttyConfigIfNeeded(_ config: ghostty_config_t) { + private func loadProgramaAppSupportGhosttyConfigIfNeeded(_ config: ghostty_config_t) { #if os(macOS) let fm = FileManager.default guard let appSupport = fm.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { return } @@ -3079,7 +3079,7 @@ class GhosttyApp { #endif return false } - if !BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowser() { + if !BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowser() { #if DEBUG dlog("link.openURL cmuxBrowser=disabled, opening externally url=\(target.url)") #endif @@ -3387,7 +3387,7 @@ final class TerminalSurface: Identifiable, ObservableObject { return val > 0 ? val : 10 }() private let surfaceContext: ghostty_surface_context_e - private let configTemplate: CmuxSurfaceConfigTemplate? + private let configTemplate: ProgramaSurfaceConfigTemplate? private let workingDirectory: String? private let initialCommand: String? private let initialEnvironmentOverrides: [String: String] @@ -3479,7 +3479,7 @@ final class TerminalSurface: Identifiable, ObservableObject { init( tabId: UUID, context: ghostty_surface_context_e, - configTemplate: CmuxSurfaceConfigTemplate?, + configTemplate: ProgramaSurfaceConfigTemplate?, workingDirectory: String? = nil, initialCommand: String? = nil, initialEnvironmentOverrides: [String: String] = [:], @@ -4048,7 +4048,7 @@ final class TerminalSurface: Identifiable, ObservableObject { let scaleFactors = scaleFactors(for: view) - let baseConfig = configTemplate ?? CmuxSurfaceConfigTemplate() + let baseConfig = configTemplate ?? ProgramaSurfaceConfigTemplate() var surfaceConfig = ghostty_surface_config_new() surfaceConfig.font_size = baseConfig.fontSize surfaceConfig.wait_after_command = baseConfig.waitAfterCommand @@ -6178,7 +6178,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { #if DEBUG private func recordKeyLatency(path: String, event: NSEvent) { guard Self.keyLatencyProbeEnabled else { return } - CmuxTypingTiming.logEventDelay(path: path, event: event) + ProgramaTypingTiming.logEventDelay(path: path, event: event) } #endif @@ -6198,9 +6198,9 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { override func performKeyEquivalent(with event: NSEvent) -> Bool { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.performKeyEquivalent", startedAt: typingTimingStart, event: event @@ -6345,7 +6345,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { override func keyDown(with event: NSEvent) { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() let phaseTotalStart = ProcessInfo.processInfo.systemUptime var ensureSurfaceMs: Double = 0 var dismissNotificationMs: Double = 0 @@ -6356,7 +6356,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { var refreshMs: Double = 0 defer { let totalMs = (ProcessInfo.processInfo.systemUptime - phaseTotalStart) * 1000.0 - CmuxTypingTiming.logBreakdown( + ProgramaTypingTiming.logBreakdown( path: "terminal.keyDown.phase", totalMs: totalMs, event: event, @@ -6372,7 +6372,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { ], extra: "marked=\(hasMarkedText() ? 1 : 0)" ) - CmuxTypingTiming.logDuration(path: "terminal.keyDown", startedAt: typingTimingStart, event: event) + ProgramaTypingTiming.logDuration(path: "terminal.keyDown", startedAt: typingTimingStart, event: event) } let ensureSurfaceStart = ProcessInfo.processInfo.systemUptime #endif @@ -6470,7 +6470,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { #endif } else { #if DEBUG - let sendTimingStart = CmuxTypingTiming.start() + let sendTimingStart = ProgramaTypingTiming.start() let ghosttySendStart = ProcessInfo.processInfo.systemUptime #endif handled = text.withCString { ptr in @@ -6479,7 +6479,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { } #if DEBUG ghosttySendMs = (ProcessInfo.processInfo.systemUptime - ghosttySendStart) * 1000.0 - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.keyDown.ctrlGhosttySend", startedAt: sendTimingStart, event: event, @@ -6563,13 +6563,13 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { // Let the input system handle the event (for IME, dead keys, etc.) #if DEBUG - let interpretTimingStart = CmuxTypingTiming.start() + let interpretTimingStart = ProgramaTypingTiming.start() let interpretPhaseStart = ProcessInfo.processInfo.systemUptime #endif interpretKeyEvents([translationEvent]) #if DEBUG interpretMs = (ProcessInfo.processInfo.systemUptime - interpretPhaseStart) * 1000.0 - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.keyDown.interpretKeyEvents", startedAt: interpretTimingStart, event: event @@ -6628,7 +6628,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { if shouldSendText(text) { shouldRefreshAfterTextInput = true #if DEBUG - let sendTimingStart = CmuxTypingTiming.start() + let sendTimingStart = ProgramaTypingTiming.start() let ghosttySendStart = ProcessInfo.processInfo.systemUptime #endif text.withCString { ptr in @@ -6647,7 +6647,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { } #if DEBUG ghosttySendMs += (ProcessInfo.processInfo.systemUptime - ghosttySendStart) * 1000.0 - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.keyDown.accumulatedGhosttySend.total", startedAt: sendTimingStart, event: event, @@ -6707,7 +6707,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { !suppressComposingFallbackText { shouldRefreshAfterTextInput = true #if DEBUG - let sendTimingStart = CmuxTypingTiming.start() + let sendTimingStart = ProgramaTypingTiming.start() let ghosttySendStart = ProcessInfo.processInfo.systemUptime #endif text.withCString { ptr in @@ -6726,7 +6726,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { } #if DEBUG ghosttySendMs += (ProcessInfo.processInfo.systemUptime - ghosttySendStart) * 1000.0 - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.keyDown.ghosttySend.total", startedAt: sendTimingStart, event: event, @@ -6797,7 +6797,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { event: NSEvent? = nil, extra: String? = nil ) -> Bool { - let timingStart = CmuxTypingTiming.start() + let timingStart = ProgramaTypingTiming.start() let handled = sendGhosttyKey(surface, keyEvent) let baseExtra = "handled=\(handled ? 1 : 0)" let mergedExtra: String @@ -6806,7 +6806,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { } else { mergedExtra = baseExtra } - CmuxTypingTiming.logDuration(path: path, startedAt: timingStart, event: event, extra: mergedExtra) + ProgramaTypingTiming.logDuration(path: path, startedAt: timingStart, event: event, extra: mergedExtra) return handled } #endif @@ -6909,7 +6909,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { let effectiveFlags = suppressCommandPathHover ? flags.subtracting(.command) : flags #if DEBUG if suppressCommandPathHover, flags.contains(.command) { - _ = CmuxUITestCapture.mutateJSONObjectIfConfigured( + _ = ProgramaUITestCapture.mutateJSONObjectIfConfigured( envKey: "PROGRAMA_UI_TEST_CMD_HOVER_DIAGNOSTICS_PATH" ) { payload in payload["suppressed_command_hover_count"] = (payload["suppressed_command_hover_count"] as? Int ?? 0) + 1 @@ -7131,7 +7131,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { var payload = data payload["surface_id"] = terminalSurface?.id.uuidString ?? "nil" payload["word_path_hover_active"] = wordPathHoverActive - CmuxRuntimeDebugCapture.logIfConfigured( + ProgramaRuntimeDebugCapture.logIfConfigured( hypothesisID: hypothesisID, source: "GhosttyNSView.\(name)", name: name, @@ -11274,7 +11274,7 @@ extension GhosttyNSView: NSTextInputClient { fileprivate func sendTextToSurface(_ chars: String, preserveLiteralEscape: Bool) { guard let surface = surface else { return } #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() #endif #if DEBUG cmuxWriteChildExitProbe( @@ -11348,7 +11348,7 @@ extension GhosttyNSView: NSTextInputClient { } flushBufferedText() #if DEBUG - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.sendTextToSurface", startedAt: typingTimingStart, extra: "textBytes=\(chars.utf8.count)" @@ -11480,9 +11480,9 @@ extension GhosttyNSView: NSTextInputClient { func setMarkedText(_ string: Any, selectedRange: NSRange, replacementRange: NSRange) { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.setMarkedText", startedAt: typingTimingStart, extra: "markedLength=\(markedText.length)" @@ -11510,9 +11510,9 @@ extension GhosttyNSView: NSTextInputClient { func unmarkText() { #if DEBUG let hadMarkedText = markedText.length > 0 - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.unmarkText", startedAt: typingTimingStart, extra: "hadMarkedText=\(hadMarkedText ? 1 : 0)" @@ -11531,9 +11531,9 @@ extension GhosttyNSView: NSTextInputClient { /// preedit overlay (e.g. for Korean, Japanese, Chinese input). private func syncPreedit(clearIfNeeded: Bool = true) { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.syncPreedit", startedAt: typingTimingStart, extra: "markedLength=\(markedText.length) clearIfNeeded=\(clearIfNeeded ? 1 : 0)" @@ -11650,9 +11650,9 @@ extension GhosttyNSView: NSTextInputClient { func insertText(_ string: Any, replacementRange: NSRange) { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "terminal.insertText", startedAt: typingTimingStart, event: NSApp.currentEvent, diff --git a/Sources/KeyboardShortcutSettings.swift b/Sources/KeyboardShortcutSettings.swift index 4b6dbbe286c..3d820a9e52d 100644 --- a/Sources/KeyboardShortcutSettings.swift +++ b/Sources/KeyboardShortcutSettings.swift @@ -87,7 +87,7 @@ enum KeyboardShortcutSettings { case .newWindow: return String(localized: "shortcut.newWindow.label", defaultValue: "New Window") case .closeWindow: return String(localized: "shortcut.closeWindow.label", defaultValue: "Close Window") case .toggleFullScreen: return String(localized: "command.toggleFullScreen.title", defaultValue: "Toggle Full Screen") - case .quit: return String(localized: "menu.quitCmux", defaultValue: "Quit cmux") + case .quit: return String(localized: "menu.quitPrograma", defaultValue: "Quit cmux") case .toggleSidebar: return String(localized: "shortcut.toggleSidebar.label", defaultValue: "Toggle Sidebar") case .newTab: return String(localized: "shortcut.newWorkspace.label", defaultValue: "New Workspace") case .openFolder: return String(localized: "shortcut.openFolder.label", defaultValue: "Open Folder") diff --git a/Sources/KeyboardShortcutSettingsFileStore.swift b/Sources/KeyboardShortcutSettingsFileStore.swift index 554dea9cc59..b4d33af9164 100644 --- a/Sources/KeyboardShortcutSettingsFileStore.swift +++ b/Sources/KeyboardShortcutSettingsFileStore.swift @@ -17,8 +17,8 @@ final class KeyboardShortcutSettingsObserver: ObservableObject { } } -final class CmuxSettingsFileStore { - static let shared = CmuxSettingsFileStore() +final class ProgramaSettingsFileStore { + static let shared = ProgramaSettingsFileStore() static let currentSchemaVersion = 1 static let schemaURLString = "https://raw.githubusercontent.com/darkroomengineering/programa/main/Resources/settings.schema.json" @@ -65,8 +65,8 @@ final class CmuxSettingsFileStore { private(set) var activeSourcePath: String? init( - primaryPath: String = CmuxSettingsFileStore.defaultPrimaryPath, - fallbackPath: String? = CmuxSettingsFileStore.defaultFallbackPath, + primaryPath: String = ProgramaSettingsFileStore.defaultPrimaryPath, + fallbackPath: String? = ProgramaSettingsFileStore.defaultFallbackPath, fileManager: FileManager = .default, notificationCenter: NotificationCenter = .default, startWatching: Bool = true @@ -98,7 +98,7 @@ final class CmuxSettingsFileStore { self?.reapplyManagedSettingsIfNeeded() } trustObserver = notificationCenter.addObserver( - forName: CmuxDirectoryTrust.didChangeNotification, + forName: ProgramaDirectoryTrust.didChangeNotification, object: nil, queue: nil ) { [weak self] _ in @@ -189,7 +189,7 @@ final class CmuxSettingsFileStore { try template.write(to: fileURL, atomically: true, encoding: .utf8) try fileManager.setAttributes([.posixPermissions: 0o600], ofItemAtPath: fileURL.path) } catch { - NSLog("[CmuxSettingsFileStore] failed to bootstrap %@: %@", primaryPath, String(describing: error)) + NSLog("[ProgramaSettingsFileStore] failed to bootstrap %@: %@", primaryPath, String(describing: error)) } } @@ -262,7 +262,7 @@ final class CmuxSettingsFileStore { } return .parsed(parseSettingsFile(root: root, sourcePath: path)) } catch { - NSLog("[CmuxSettingsFileStore] parse error at %@: %@", path, String(describing: error)) + NSLog("[ProgramaSettingsFileStore] parse error at %@: %@", path, String(describing: error)) return .invalid } } @@ -271,7 +271,7 @@ final class CmuxSettingsFileStore { let schemaVersion = jsonInt(root["schemaVersion"]) ?? 1 if schemaVersion > Self.currentSchemaVersion { NSLog( - "[CmuxSettingsFileStore] %@ uses future schemaVersion %d; parsing known fields only", + "[ProgramaSettingsFileStore] %@ uses future schemaVersion %d; parsing known fields only", sourcePath, schemaVersion ) @@ -435,11 +435,11 @@ final class CmuxSettingsFileStore { if let value = jsonBool(section["showPullRequests"]) { snapshot.managedUserDefaults["sidebarShowPullRequest"] = .bool(value) } - if let value = jsonBool(section["openPullRequestLinksInCmuxBrowser"]) { - snapshot.managedUserDefaults[BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowserKey] = .bool(value) + if let value = jsonBool(section["openPullRequestLinksInProgramaBrowser"]) { + snapshot.managedUserDefaults[BrowserLinkOpenSettings.openSidebarPullRequestLinksInProgramaBrowserKey] = .bool(value) } - if let value = jsonBool(section["openPortLinksInCmuxBrowser"]) { - snapshot.managedUserDefaults[BrowserLinkOpenSettings.openSidebarPortLinksInCmuxBrowserKey] = .bool(value) + if let value = jsonBool(section["openPortLinksInProgramaBrowser"]) { + snapshot.managedUserDefaults[BrowserLinkOpenSettings.openSidebarPortLinksInProgramaBrowserKey] = .bool(value) } if let value = jsonBool(section["showSSH"]) { snapshot.managedUserDefaults["sidebarShowSSH"] = .bool(value) @@ -500,12 +500,12 @@ final class CmuxSettingsFileStore { for (rawName, rawValue) in rawColors { let name = rawName.trimmingCharacters(in: .whitespacesAndNewlines) guard !name.isEmpty else { - NSLog("[CmuxSettingsFileStore] ignoring empty workspace color name in %@", sourcePath) + NSLog("[ProgramaSettingsFileStore] ignoring empty workspace color name in %@", sourcePath) continue } guard let hex = jsonString(rawValue), let normalizedHex = WorkspaceTabColorSettings.normalizedHex(hex) else { - NSLog("[CmuxSettingsFileStore] ignoring invalid workspace color '%@' in %@", name, sourcePath) + NSLog("[ProgramaSettingsFileStore] ignoring invalid workspace color '%@' in %@", name, sourcePath) continue } normalizedPalette[name] = normalizedHex @@ -522,12 +522,12 @@ final class CmuxSettingsFileStore { ) for (name, rawValue) in rawOverrides { guard validNames.contains(name) else { - NSLog("[CmuxSettingsFileStore] ignoring unknown workspace color '%@' in %@", name, sourcePath) + NSLog("[ProgramaSettingsFileStore] ignoring unknown workspace color '%@' in %@", name, sourcePath) continue } guard let hex = jsonString(rawValue), let normalizedHex = WorkspaceTabColorSettings.normalizedHex(hex) else { - NSLog("[CmuxSettingsFileStore] ignoring invalid workspace color override '%@' in %@", name, sourcePath) + NSLog("[ProgramaSettingsFileStore] ignoring invalid workspace color override '%@' in %@", name, sourcePath) continue } palette[name] = normalizedHex @@ -683,11 +683,11 @@ final class CmuxSettingsFileStore { } snapshot.managedUserDefaults[BrowserThemeSettings.modeKey] = .string(mode.rawValue) } - if let value = jsonBool(section["openTerminalLinksInCmuxBrowser"]) { - snapshot.managedUserDefaults[BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey] = .bool(value) + if let value = jsonBool(section["openTerminalLinksInProgramaBrowser"]) { + snapshot.managedUserDefaults[BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowserKey] = .bool(value) } - if let value = jsonBool(section["interceptTerminalOpenCommandInCmuxBrowser"]) { - snapshot.managedUserDefaults[BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowserKey] = .bool(value) + if let value = jsonBool(section["interceptTerminalOpenCommandInProgramaBrowser"]) { + snapshot.managedUserDefaults[BrowserLinkOpenSettings.interceptTerminalOpenCommandInProgramaBrowserKey] = .bool(value) } if let values = jsonStringArray(section["hostsToOpenInEmbeddedBrowser"]) { let normalized = values @@ -781,12 +781,12 @@ final class CmuxSettingsFileStore { for (rawAction, rawBinding) in bindings { guard let action = KeyboardShortcutSettings.Action(rawValue: rawAction) else { - NSLog("[CmuxSettingsFileStore] ignoring unknown shortcut action '%@' in %@", rawAction, sourcePath) + NSLog("[ProgramaSettingsFileStore] ignoring unknown shortcut action '%@' in %@", rawAction, sourcePath) continue } guard let shortcut = parseShortcutBindingValue(rawBinding, action: action) else { NSLog( - "[CmuxSettingsFileStore] ignoring invalid shortcut binding for '%@' in %@", + "[ProgramaSettingsFileStore] ignoring invalid shortcut binding for '%@' in %@", rawAction, sourcePath ) @@ -952,7 +952,7 @@ final class CmuxSettingsFileStore { } if snapshot.managedCustomSettings.trustedDirectories != nil, backups[Self.trustedDirectoriesBackupIdentifier] == nil { - backups[Self.trustedDirectoriesBackupIdentifier] = .stringArray(CmuxDirectoryTrust.shared.allTrustedPaths) + backups[Self.trustedDirectoriesBackupIdentifier] = .stringArray(ProgramaDirectoryTrust.shared.allTrustedPaths) } if snapshot.managedCustomSettings.socketPassword != nil, backups[Self.socketPasswordBackupIdentifier] == nil { @@ -978,8 +978,8 @@ final class CmuxSettingsFileStore { private func applyManagedCustomSettings(_ settings: ManagedCustomSettings) { if let trustedDirectories = settings.trustedDirectories, - CmuxDirectoryTrust.shared.allTrustedPaths != trustedDirectories { - CmuxDirectoryTrust.shared.replaceAll(with: trustedDirectories) + ProgramaDirectoryTrust.shared.allTrustedPaths != trustedDirectories { + ProgramaDirectoryTrust.shared.replaceAll(with: trustedDirectories) } if let socketPassword = settings.socketPassword { @@ -1002,9 +1002,9 @@ final class CmuxSettingsFileStore { switch identifier { case Self.trustedDirectoriesBackupIdentifier: if case .stringArray(let values) = backup { - CmuxDirectoryTrust.shared.replaceAll(with: values) + ProgramaDirectoryTrust.shared.replaceAll(with: values) } else { - CmuxDirectoryTrust.shared.replaceAll(with: []) + ProgramaDirectoryTrust.shared.replaceAll(with: []) } case Self.socketPasswordBackupIdentifier: switch backup { @@ -1185,7 +1185,7 @@ final class CmuxSettingsFileStore { } private func logInvalid(_ path: String, sourcePath: String) { - NSLog("[CmuxSettingsFileStore] ignoring invalid setting '%@' in %@", path, sourcePath) + NSLog("[ProgramaSettingsFileStore] ignoring invalid setting '%@' in %@", path, sourcePath) } private func jsonString(_ rawValue: Any?) -> String? { @@ -1311,8 +1311,8 @@ final class CmuxSettingsFileStore { "showNotificationMessage": SidebarWorkspaceDetailSettings.defaultShowNotificationMessage, "showBranchDirectory": true, "showPullRequests": true, - "openPullRequestLinksInCmuxBrowser": BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInCmuxBrowser, - "openPortLinksInCmuxBrowser": BrowserLinkOpenSettings.defaultOpenSidebarPortLinksInCmuxBrowser, + "openPullRequestLinksInProgramaBrowser": BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInProgramaBrowser, + "openPortLinksInProgramaBrowser": BrowserLinkOpenSettings.defaultOpenSidebarPortLinksInProgramaBrowser, "showSSH": true, "showPorts": true, "showLog": true, @@ -1359,8 +1359,8 @@ final class CmuxSettingsFileStore { "defaultSearchEngine": BrowserSearchSettings.defaultSearchEngine.rawValue, "showSearchSuggestions": BrowserSearchSettings.defaultSearchSuggestionsEnabled, "theme": BrowserThemeSettings.defaultMode.rawValue, - "openTerminalLinksInCmuxBrowser": BrowserLinkOpenSettings.defaultOpenTerminalLinksInCmuxBrowser, - "interceptTerminalOpenCommandInCmuxBrowser": BrowserLinkOpenSettings.defaultInterceptTerminalOpenCommandInCmuxBrowser, + "openTerminalLinksInProgramaBrowser": BrowserLinkOpenSettings.defaultOpenTerminalLinksInProgramaBrowser, + "interceptTerminalOpenCommandInProgramaBrowser": BrowserLinkOpenSettings.defaultInterceptTerminalOpenCommandInProgramaBrowser, "hostsToOpenInEmbeddedBrowser": [String](), "urlsToAlwaysOpenExternally": [String](), "insecureHttpHostsAllowedInEmbeddedBrowser": BrowserInsecureHTTPSettings.defaultAllowlistPatterns, @@ -1418,7 +1418,7 @@ final class CmuxSettingsFileStore { } } -typealias KeyboardShortcutSettingsFileStore = CmuxSettingsFileStore +typealias KeyboardShortcutSettingsFileStore = ProgramaSettingsFileStore private struct ResolvedSettingsSnapshot { var path: String? @@ -1443,10 +1443,10 @@ private struct ManagedCustomSettings: Equatable { var managedIdentifiers: Set { var identifiers: Set = [] if trustedDirectories != nil { - identifiers.insert(CmuxSettingsFileStore.trustedDirectoriesBackupIdentifier) + identifiers.insert(ProgramaSettingsFileStore.trustedDirectoriesBackupIdentifier) } if socketPassword != nil { - identifiers.insert(CmuxSettingsFileStore.socketPasswordBackupIdentifier) + identifiers.insert(ProgramaSettingsFileStore.socketPasswordBackupIdentifier) } return identifiers } diff --git a/Sources/Panels/BrowserPanel.swift b/Sources/Panels/BrowserPanel.swift index 2565822fd5e..656ddad358b 100644 --- a/Sources/Panels/BrowserPanel.swift +++ b/Sources/Panels/BrowserPanel.swift @@ -532,59 +532,59 @@ final class BrowserProfileStore: ObservableObject { } enum BrowserLinkOpenSettings { - static let openTerminalLinksInCmuxBrowserKey = "browserOpenTerminalLinksInCmuxBrowser" - static let defaultOpenTerminalLinksInCmuxBrowser: Bool = true + static let openTerminalLinksInProgramaBrowserKey = "browserOpenTerminalLinksInProgramaBrowser" + static let defaultOpenTerminalLinksInProgramaBrowser: Bool = true - static let openSidebarPullRequestLinksInCmuxBrowserKey = "browserOpenSidebarPullRequestLinksInCmuxBrowser" - static let defaultOpenSidebarPullRequestLinksInCmuxBrowser: Bool = true + static let openSidebarPullRequestLinksInProgramaBrowserKey = "browserOpenSidebarPullRequestLinksInProgramaBrowser" + static let defaultOpenSidebarPullRequestLinksInProgramaBrowser: Bool = true - static let openSidebarPortLinksInCmuxBrowserKey = "browserOpenSidebarPortLinksInCmuxBrowser" - static let defaultOpenSidebarPortLinksInCmuxBrowser: Bool = true + static let openSidebarPortLinksInProgramaBrowserKey = "browserOpenSidebarPortLinksInProgramaBrowser" + static let defaultOpenSidebarPortLinksInProgramaBrowser: Bool = true - static let interceptTerminalOpenCommandInCmuxBrowserKey = "browserInterceptTerminalOpenCommandInCmuxBrowser" - static let defaultInterceptTerminalOpenCommandInCmuxBrowser: Bool = true + static let interceptTerminalOpenCommandInProgramaBrowserKey = "browserInterceptTerminalOpenCommandInProgramaBrowser" + static let defaultInterceptTerminalOpenCommandInProgramaBrowser: Bool = true static let browserHostWhitelistKey = "browserHostWhitelist" static let defaultBrowserHostWhitelist: String = "" static let browserExternalOpenPatternsKey = "browserExternalOpenPatterns" static let defaultBrowserExternalOpenPatterns: String = "" - static func openTerminalLinksInCmuxBrowser(defaults: UserDefaults = .standard) -> Bool { - if defaults.object(forKey: openTerminalLinksInCmuxBrowserKey) == nil { - return defaultOpenTerminalLinksInCmuxBrowser + static func openTerminalLinksInProgramaBrowser(defaults: UserDefaults = .standard) -> Bool { + if defaults.object(forKey: openTerminalLinksInProgramaBrowserKey) == nil { + return defaultOpenTerminalLinksInProgramaBrowser } - return defaults.bool(forKey: openTerminalLinksInCmuxBrowserKey) + return defaults.bool(forKey: openTerminalLinksInProgramaBrowserKey) } - static func openSidebarPullRequestLinksInCmuxBrowser(defaults: UserDefaults = .standard) -> Bool { - if defaults.object(forKey: openSidebarPullRequestLinksInCmuxBrowserKey) == nil { - return defaultOpenSidebarPullRequestLinksInCmuxBrowser + static func openSidebarPullRequestLinksInProgramaBrowser(defaults: UserDefaults = .standard) -> Bool { + if defaults.object(forKey: openSidebarPullRequestLinksInProgramaBrowserKey) == nil { + return defaultOpenSidebarPullRequestLinksInProgramaBrowser } - return defaults.bool(forKey: openSidebarPullRequestLinksInCmuxBrowserKey) + return defaults.bool(forKey: openSidebarPullRequestLinksInProgramaBrowserKey) } - static func openSidebarPortLinksInCmuxBrowser(defaults: UserDefaults = .standard) -> Bool { - if defaults.object(forKey: openSidebarPortLinksInCmuxBrowserKey) == nil { - return defaultOpenSidebarPortLinksInCmuxBrowser + static func openSidebarPortLinksInProgramaBrowser(defaults: UserDefaults = .standard) -> Bool { + if defaults.object(forKey: openSidebarPortLinksInProgramaBrowserKey) == nil { + return defaultOpenSidebarPortLinksInProgramaBrowser } - return defaults.bool(forKey: openSidebarPortLinksInCmuxBrowserKey) + return defaults.bool(forKey: openSidebarPortLinksInProgramaBrowserKey) } - static func interceptTerminalOpenCommandInCmuxBrowser(defaults: UserDefaults = .standard) -> Bool { - if defaults.object(forKey: interceptTerminalOpenCommandInCmuxBrowserKey) != nil { - return defaults.bool(forKey: interceptTerminalOpenCommandInCmuxBrowserKey) + static func interceptTerminalOpenCommandInProgramaBrowser(defaults: UserDefaults = .standard) -> Bool { + if defaults.object(forKey: interceptTerminalOpenCommandInProgramaBrowserKey) != nil { + return defaults.bool(forKey: interceptTerminalOpenCommandInProgramaBrowserKey) } // Migrate existing behavior for users who only had the link-click toggle. - if defaults.object(forKey: openTerminalLinksInCmuxBrowserKey) != nil { - return defaults.bool(forKey: openTerminalLinksInCmuxBrowserKey) + if defaults.object(forKey: openTerminalLinksInProgramaBrowserKey) != nil { + return defaults.bool(forKey: openTerminalLinksInProgramaBrowserKey) } - return defaultInterceptTerminalOpenCommandInCmuxBrowser + return defaultInterceptTerminalOpenCommandInProgramaBrowser } - static func initialInterceptTerminalOpenCommandInCmuxBrowserValue(defaults: UserDefaults = .standard) -> Bool { - interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults) + static func initialInterceptTerminalOpenCommandInProgramaBrowserValue(defaults: UserDefaults = .standard) -> Bool { + interceptTerminalOpenCommandInProgramaBrowser(defaults: defaults) } static func hostWhitelist(defaults: UserDefaults = .standard) -> [String] { @@ -2064,7 +2064,7 @@ final class BrowserPanel: Panel, ObservableObject { private static let imeCompositionHandlerName = "cmuxIMEState" - private func setupIMECompositionTracking(for webView: CmuxWebView) { + private func setupIMECompositionTracking(for webView: ProgramaWebView) { let handler = IMECompositionMessageHandler { [weak webView] composing in guard let webView else { return } if composing { @@ -2530,14 +2530,14 @@ final class BrowserPanel: Panel, ObservableObject { private static func makeWebView( profileID: UUID, websiteDataStore: WKWebsiteDataStore? = nil - ) -> CmuxWebView { + ) -> ProgramaWebView { let config = WKWebViewConfiguration() configureWebViewConfiguration( config, websiteDataStore: websiteDataStore ?? BrowserProfileStore.shared.websiteDataStore(for: profileID) ) - let webView = CmuxWebView(frame: .zero, configuration: config) + let webView = ProgramaWebView(frame: .zero, configuration: config) webView.allowsBackForwardNavigationGestures = true if #available(macOS 13.3, *) { webView.isInspectable = true @@ -2602,7 +2602,7 @@ final class BrowserPanel: Panel, ObservableObject { ) } - private func bindWebView(_ webView: CmuxWebView) { + private func bindWebView(_ webView: ProgramaWebView) { webView.onContextMenuDownloadStateChanged = { [weak self] downloading in if downloading { self?.beginDownloadActivity() @@ -2933,8 +2933,8 @@ final class BrowserPanel: Panel, ObservableObject { previousWebView.stopLoading() previousWebView.navigationDelegate = nil previousWebView.uiDelegate = nil - if let previousCmuxWebView = previousWebView as? CmuxWebView { - previousCmuxWebView.onContextMenuDownloadStateChanged = nil + if let previousProgramaWebView = previousWebView as? ProgramaWebView { + previousProgramaWebView.onContextMenuDownloadStateChanged = nil } profileID = resolvedProfileID @@ -3282,8 +3282,8 @@ final class BrowserPanel: Panel, ObservableObject { oldWebView.stopLoading() oldWebView.navigationDelegate = nil oldWebView.uiDelegate = nil - if let oldCmuxWebView = oldWebView as? CmuxWebView { - oldCmuxWebView.onContextMenuDownloadStateChanged = nil + if let oldProgramaWebView = oldWebView as? ProgramaWebView { + oldProgramaWebView.onContextMenuDownloadStateChanged = nil } let replacement = Self.makeWebView( @@ -3976,7 +3976,7 @@ final class BrowserPanel: Panel, ObservableObject { alert.messageText = String(localized: "browser.error.insecure.title", defaultValue: "Connection isn\u{2019}t secure") alert.informativeText = String(localized: "browser.error.insecure.message", defaultValue: "\(host) uses plain HTTP, so traffic can be read or modified on the network.\n\nOpen this URL in your default browser, or proceed in cmux.") alert.addButton(withTitle: String(localized: "browser.openInDefaultBrowser", defaultValue: "Open in Default Browser")) - alert.addButton(withTitle: String(localized: "browser.proceedInCmux", defaultValue: "Proceed in cmux")) + alert.addButton(withTitle: String(localized: "browser.proceedInPrograma", defaultValue: "Proceed in cmux")) alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel")) alert.showsSuppressionButton = true alert.suppressionButton?.title = String(localized: "browser.alwaysAllowHost", defaultValue: "Always allow this host in cmux") @@ -4139,8 +4139,8 @@ extension BrowserPanel { oldWebView.stopLoading() oldWebView.navigationDelegate = nil oldWebView.uiDelegate = nil - if let oldCmuxWebView = oldWebView as? CmuxWebView { - oldCmuxWebView.onContextMenuDownloadStateChanged = nil + if let oldProgramaWebView = oldWebView as? ProgramaWebView { + oldProgramaWebView.onContextMenuDownloadStateChanged = nil } let replacement = Self.makeWebView( @@ -6189,7 +6189,7 @@ private class BrowserNavigationDelegate: NSObject, WKNavigationDelegate { decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void ) { - let hasRecentMiddleClickIntent = CmuxWebView.hasRecentMiddleClickIntent(for: webView) + let hasRecentMiddleClickIntent = ProgramaWebView.hasRecentMiddleClickIntent(for: webView) let shouldOpenInNewTab = browserNavigationShouldOpenInNewTab( navigationType: navigationAction.navigationType, modifierFlags: navigationAction.modifierFlags, @@ -6429,7 +6429,7 @@ private class BrowserUIDelegate: NSObject, WKUIDelegate { navigationType: navigationAction.navigationType, modifierFlags: navigationAction.modifierFlags, buttonNumber: navigationAction.buttonNumber, - hasRecentMiddleClickIntent: CmuxWebView.hasRecentMiddleClickIntent(for: webView) + hasRecentMiddleClickIntent: ProgramaWebView.hasRecentMiddleClickIntent(for: webView) ) if isScriptedPopup, let popupWebView = openPopup?(configuration, windowFeatures) { @@ -10384,7 +10384,7 @@ enum BrowserUserProxySettings { } /// Receives `compositionstart`/`compositionend` bridge messages from the web view's -/// JS IME tracking script and forwards them to the native `CmuxWebView`. +/// JS IME tracking script and forwards them to the native `ProgramaWebView`. private final class IMECompositionMessageHandler: NSObject, WKScriptMessageHandler { private let onCompositionStateChanged: (Bool) -> Void diff --git a/Sources/Panels/BrowserPanelView.swift b/Sources/Panels/BrowserPanelView.swift index aabf80e3fa5..5b6c1b7d9a4 100644 --- a/Sources/Panels/BrowserPanelView.swift +++ b/Sources/Panels/BrowserPanelView.swift @@ -614,7 +614,7 @@ struct BrowserPanelView: View { } .onReceive(NotificationCenter.default.publisher(for: .webViewDidReceiveClick).filter { [weak panel] note in // Only handle clicks from our own webview. - guard let webView = note.object as? CmuxWebView else { return false } + guard let webView = note.object as? ProgramaWebView else { return false } return webView === panel?.webView }) { _ in #if DEBUG @@ -1357,7 +1357,7 @@ struct BrowserPanelView: View { reason: String, isPanelFocusedOverride: Bool? = nil ) { - guard let cmuxWebView = panel.webView as? CmuxWebView else { return } + guard let cmuxWebView = panel.webView as? ProgramaWebView else { return } let isPanelFocused = isPanelFocusedOverride ?? isFocused let next = isPanelFocused && !panel.shouldSuppressWebViewFocus() if cmuxWebView.allowsFirstResponderAcquisition != next { @@ -1982,10 +1982,10 @@ struct BrowserPanelView: View { private func refreshSuggestions() { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() defer { let trimmedQuery = omnibarState.buffer.trimmingCharacters(in: .whitespacesAndNewlines) - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "browser.omnibar.refreshSuggestions", startedAt: typingTimingStart, extra: "focused=\(addressBarFocused ? 1 : 0) queryLen=\(trimmedQuery.utf8.count) suggestionCount=\(omnibarState.suggestions.count)" @@ -3407,10 +3407,10 @@ private final class OmnibarNativeTextField: NSTextField { override func keyDown(with event: NSEvent) { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() var route = "super" defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "browser.omnibar.keyDown", startedAt: typingTimingStart, event: event, @@ -3437,10 +3437,10 @@ private final class OmnibarNativeTextField: NSTextField { override func performKeyEquivalent(with event: NSEvent) -> Bool { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() var handled = false defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "browser.omnibar.performKeyEquivalent", startedAt: typingTimingStart, event: event, @@ -3690,9 +3690,9 @@ private struct OmnibarTextFieldRepresentable: NSViewRepresentable { func controlTextDidChange(_ obj: Notification) { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "browser.omnibar.controlTextDidChange", startedAt: typingTimingStart, event: NSApp.currentEvent, @@ -3714,10 +3714,10 @@ private struct OmnibarTextFieldRepresentable: NSViewRepresentable { func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() var handled = false defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "browser.omnibar.doCommandBy", startedAt: typingTimingStart, event: NSApp.currentEvent, @@ -3844,10 +3844,10 @@ private struct OmnibarTextFieldRepresentable: NSViewRepresentable { func handleKeyEvent(_ event: NSEvent, editor: NSTextView?) -> Bool { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() var handled = false defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "browser.omnibar.handleKeyEvent", startedAt: typingTimingStart, event: event, @@ -6714,7 +6714,7 @@ struct WebViewRepresentable: NSViewRepresentable { webView: WKWebView, isPanelFocused: Bool ) { - guard let cmuxWebView = webView as? CmuxWebView else { return } + guard let cmuxWebView = webView as? ProgramaWebView else { return } let next = isPanelFocused && !panel.shouldSuppressWebViewFocus() if cmuxWebView.allowsFirstResponderAcquisition != next { #if DEBUG diff --git a/Sources/Panels/BrowserPopupWindowController.swift b/Sources/Panels/BrowserPopupWindowController.swift index 2f9657fbf35..4a35824ce24 100644 --- a/Sources/Panels/BrowserPopupWindowController.swift +++ b/Sources/Panels/BrowserPopupWindowController.swift @@ -34,7 +34,7 @@ func browserPopupContentRect( return NSRect(x: x, y: y, width: clampedWidth, height: clampedHeight) } -/// Hosts a popup `CmuxWebView` in a standalone `NSPanel`, created when a page +/// Hosts a popup `ProgramaWebView` in a standalone `NSPanel`, created when a page /// calls `window.open()` (scripted new-window requests). /// /// Lifecycle: @@ -66,7 +66,7 @@ final class BrowserPopupWindowController: NSObject, NSWindowDelegate { static let maxNestingDepth = 3 - let webView: CmuxWebView + let webView: ProgramaWebView private let panel: NSPanel private let urlLabel: NSTextField private weak var openerPanel: BrowserPanel? @@ -105,7 +105,7 @@ final class BrowserPopupWindowController: NSObject, NSWindowDelegate { // Create popup web view with WebKit's supplied configuration after // overlaying the opener's browser context so OAuth popups keep cmux's // shared cookie/storage scope and opener linkage. - let webView = CmuxWebView(frame: .zero, configuration: configuration) + let webView = ProgramaWebView(frame: .zero, configuration: configuration) webView.allowsBackForwardNavigationGestures = true if #available(macOS 13.3, *) { webView.isInspectable = true @@ -365,7 +365,7 @@ final class BrowserPopupWindowController: NSObject, NSWindowDelegate { alert.messageText = String(localized: "browser.error.insecure.title", defaultValue: "Connection isn\u{2019}t secure") alert.informativeText = String(localized: "browser.error.insecure.message", defaultValue: "\(host) uses plain HTTP, so traffic can be read or modified on the network.\n\nOpen this URL in your default browser, or proceed in cmux.") alert.addButton(withTitle: String(localized: "browser.openInDefaultBrowser", defaultValue: "Open in Default Browser")) - alert.addButton(withTitle: String(localized: "browser.proceedInCmux", defaultValue: "Proceed in cmux")) + alert.addButton(withTitle: String(localized: "browser.proceedInPrograma", defaultValue: "Proceed in cmux")) alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel")) alert.showsSuppressionButton = true alert.suppressionButton?.title = String(localized: "browser.alwaysAllowHost", defaultValue: "Always allow this host in cmux") @@ -427,7 +427,7 @@ private class PopupUIDelegate: NSObject, WKUIDelegate { navigationType: navigationAction.navigationType, modifierFlags: navigationAction.modifierFlags, buttonNumber: navigationAction.buttonNumber, - hasRecentMiddleClickIntent: CmuxWebView.hasRecentMiddleClickIntent(for: webView) + hasRecentMiddleClickIntent: ProgramaWebView.hasRecentMiddleClickIntent(for: webView) ) if isScriptedPopup { diff --git a/Sources/Panels/CmuxWebView.swift b/Sources/Panels/CmuxWebView.swift index 06d88b73882..84a7f655ebc 100644 --- a/Sources/Panels/CmuxWebView.swift +++ b/Sources/Panels/CmuxWebView.swift @@ -106,7 +106,7 @@ enum BrowserImageCopyPasteboardBuilder { /// preventing the app menu/SwiftUI Commands from receiving them. Route app/menu /// shortcuts first by default, but allow browser content to try the Find command /// family before cmux falls back to its own browser find overlay. -final class CmuxWebView: WKWebView { +final class ProgramaWebView: WKWebView { /// Timestamp of the most recent `compositionend` event received via the JS bridge. /// Used to detect when WKWebView has already cleared marked text before /// `performKeyEquivalent` fires, causing `hasMarkedText()` to return `false` @@ -129,7 +129,7 @@ final class CmuxWebView: WKWebView { private static let middleClickIntentMaxAge: TimeInterval = 0.8 static func hasRecentMiddleClickIntent(for webView: WKWebView) -> Bool { - guard let webView = webView as? CmuxWebView else { return false } + guard let webView = webView as? ProgramaWebView else { return false } guard let intent = lastMiddleClickIntent else { return false } let age = ProcessInfo.processInfo.systemUptime - intent.uptime @@ -141,7 +141,7 @@ final class CmuxWebView: WKWebView { return intent.webViewID == ObjectIdentifier(webView) } - private static func recordMiddleClickIntent(for webView: CmuxWebView) { + private static func recordMiddleClickIntent(for webView: ProgramaWebView) { lastMiddleClickIntent = MiddleClickIntent( webViewID: ObjectIdentifier(webView), uptime: ProcessInfo.processInfo.systemUptime @@ -164,7 +164,7 @@ final class CmuxWebView: WKWebView { /// Called when "Open Link in New Tab" context menu is selected. /// Bypasses createWebViewWith so the link opens as a tab, not a popup. var onContextMenuOpenLinkInNewTab: ((URL) -> Void)? - var contextMenuLinkURLProvider: ((CmuxWebView, NSPoint, @escaping (URL?) -> Void) -> Void)? + var contextMenuLinkURLProvider: ((ProgramaWebView, NSPoint, @escaping (URL?) -> Void) -> Void)? var contextMenuDefaultBrowserOpener: ((URL) -> Bool)? /// Guard against background panes stealing first responder (e.g. page autofocus). /// BrowserPanelView updates this as pane focus state changes. @@ -230,10 +230,10 @@ final class CmuxWebView: WKWebView { override func performKeyEquivalent(with event: NSEvent) -> Bool { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() var handled = false defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "browser.web.performKeyEquivalent", startedAt: typingTimingStart, event: event, @@ -315,10 +315,10 @@ final class CmuxWebView: WKWebView { override func keyDown(with event: NSEvent) { #if DEBUG - let typingTimingStart = CmuxTypingTiming.start() + let typingTimingStart = ProgramaTypingTiming.start() var route = "super" defer { - CmuxTypingTiming.logDuration( + ProgramaTypingTiming.logDuration( path: "browser.web.keyDown", startedAt: typingTimingStart, event: event, diff --git a/Sources/Panels/TerminalPanel.swift b/Sources/Panels/TerminalPanel.swift index 8fef333c42a..f625d12043b 100644 --- a/Sources/Panels/TerminalPanel.swift +++ b/Sources/Panels/TerminalPanel.swift @@ -90,7 +90,7 @@ final class TerminalPanel: Panel, ObservableObject { convenience init( workspaceId: UUID, context: ghostty_surface_context_e = GHOSTTY_SURFACE_CONTEXT_SPLIT, - configTemplate: CmuxSurfaceConfigTemplate? = nil, + configTemplate: ProgramaSurfaceConfigTemplate? = nil, workingDirectory: String? = nil, portOrdinal: Int = 0, initialCommand: String? = nil, diff --git a/Sources/SurfacePool.swift b/Sources/SurfacePool.swift index 500bddc9e93..02dd896a34f 100644 --- a/Sources/SurfacePool.swift +++ b/Sources/SurfacePool.swift @@ -72,7 +72,7 @@ final class SurfacePool { workspaceId: UUID, portOrdinal: Int, workingDirectory: String?, - configTemplate: CmuxSurfaceConfigTemplate? + configTemplate: ProgramaSurfaceConfigTemplate? ) -> ClaimedSurface? { guard isEnabled else { return nil } diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index 4f95128724d..23a8b264f58 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -1300,7 +1300,7 @@ class TabManager: ObservableObject { title: String, workingDirectory: String?, portOrdinal: Int, - configTemplate: CmuxSurfaceConfigTemplate?, + configTemplate: ProgramaSurfaceConfigTemplate?, initialTerminalCommand: String?, initialTerminalEnvironment: [String: String] ) -> Workspace { @@ -2472,7 +2472,7 @@ class TabManager: ObservableObject { return candidates.first } - private func inheritedTerminalConfigForNewWorkspace() -> CmuxSurfaceConfigTemplate? { + private func inheritedTerminalConfigForNewWorkspace() -> ProgramaSurfaceConfigTemplate? { inheritedTerminalConfigForNewWorkspace(workspace: selectedWorkspace) } @@ -2495,11 +2495,11 @@ class TabManager: ObservableObject { func inheritedTerminalConfigForNewWorkspace( workspace: Workspace? - ) -> CmuxSurfaceConfigTemplate? { + ) -> ProgramaSurfaceConfigTemplate? { guard let fontPoints = cachedInheritedTerminalFontPointsForNewWorkspace(workspace: workspace) else { return nil } - var config = CmuxSurfaceConfigTemplate() + var config = ProgramaSurfaceConfigTemplate() config.fontSize = fontPoints return config } @@ -2516,13 +2516,13 @@ class TabManager: ObservableObject { private func workspaceCreationConfigTemplate( inheritedTerminalFontPoints: Float? - ) -> CmuxSurfaceConfigTemplate? { + ) -> ProgramaSurfaceConfigTemplate? { guard let inheritedTerminalFontPoints, inheritedTerminalFontPoints > 0 else { return nil } // Rebuild a clean Swift-owned template instead of carrying over any pointer-backed // inherited config state from the source workspace. - var config = CmuxSurfaceConfigTemplate() + var config = ProgramaSurfaceConfigTemplate() config.fontSize = inheritedTerminalFontPoints return config } diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 1766400563b..6706009d99a 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -23,7 +23,7 @@ private func debugWorkspaceDescriptionPreview(_ text: String?, limit: Int = 120) } #endif -struct CmuxSurfaceConfigTemplate { +struct ProgramaSurfaceConfigTemplate { var fontSize: Float32 = 0 var workingDirectory: String? var command: String? @@ -103,9 +103,9 @@ func cmuxCurrentSurfaceFontSizePoints(_ surface: ghostty_surface_t) -> Float? { func cmuxInheritedSurfaceConfig( sourceSurface: ghostty_surface_t, context: ghostty_surface_context_e -) -> CmuxSurfaceConfigTemplate { +) -> ProgramaSurfaceConfigTemplate { let inherited = ghostty_surface_inherited_config(sourceSurface, context) - var config = CmuxSurfaceConfigTemplate(cConfig: inherited) + var config = ProgramaSurfaceConfigTemplate(cConfig: inherited) // Make runtime zoom inheritance explicit, even when Ghostty's // inherit-font-size config is disabled. @@ -760,10 +760,10 @@ extension Workspace { extension Workspace { - func applyCustomLayout(_ layout: CmuxLayoutNode, baseCwd: String) { + func applyCustomLayout(_ layout: ProgramaLayoutNode, baseCwd: String) { guard let rootPaneId = bonsplitController.allPaneIds.first else { return } - var leaves: [(paneId: PaneID, surfaces: [CmuxSurfaceDefinition])] = [] + var leaves: [(paneId: PaneID, surfaces: [ProgramaSurfaceDefinition])] = [] buildCustomLayoutTree(layout, inPane: rootPaneId, leaves: &leaves) // First leaf reuses the initial terminal created by addWorkspace; @@ -783,9 +783,9 @@ extension Workspace { } private func buildCustomLayoutTree( - _ node: CmuxLayoutNode, + _ node: ProgramaLayoutNode, inPane paneId: PaneID, - leaves: inout [(paneId: PaneID, surfaces: [CmuxSurfaceDefinition])] + leaves: inout [(paneId: PaneID, surfaces: [ProgramaSurfaceDefinition])] ) { switch node { case .pane(let pane): @@ -793,7 +793,7 @@ extension Workspace { case .split(let split): guard split.children.count == 2 else { - NSLog("[CmuxConfig] split node requires exactly 2 children, got %d", split.children.count) + NSLog("[ProgramaConfig] split node requires exactly 2 children, got %d", split.children.count) leaves.append((paneId: paneId, surfaces: [])) return } @@ -826,7 +826,7 @@ extension Workspace { private func populateCustomPane( _ paneId: PaneID, - surfaces: [CmuxSurfaceDefinition], + surfaces: [ProgramaSurfaceDefinition], baseCwd: String, focusPanelId: inout UUID? ) { @@ -860,14 +860,14 @@ extension Workspace { private func configureExistingSurface( panelId: UUID, inPane paneId: PaneID, - surface: CmuxSurfaceDefinition, + surface: ProgramaSurfaceDefinition, baseCwd: String, focusPanelId: inout UUID? ) { switch surface.type { case .terminal where surface.cwd != nil || surface.env != nil: // Placeholder can't change cwd/env — replace it - let resolvedCwd = CmuxConfigStore.resolveCwd(surface.cwd, relativeTo: baseCwd) + let resolvedCwd = ProgramaConfigStore.resolveCwd(surface.cwd, relativeTo: baseCwd) if let panel = newTerminalSurface( inPane: paneId, focus: false, @@ -899,13 +899,13 @@ extension Workspace { private func createNewSurface( inPane paneId: PaneID, - surface: CmuxSurfaceDefinition, + surface: ProgramaSurfaceDefinition, baseCwd: String, focusPanelId: inout UUID? ) { switch surface.type { case .terminal: - let resolvedCwd = CmuxConfigStore.resolveCwd(surface.cwd, relativeTo: baseCwd) + let resolvedCwd = ProgramaConfigStore.resolveCwd(surface.cwd, relativeTo: baseCwd) if let panel = newTerminalSurface( inPane: paneId, focus: false, @@ -927,7 +927,7 @@ extension Workspace { } private func applyCustomDividerPositions( - configNode: CmuxLayoutNode, + configNode: ProgramaLayoutNode, liveNode: ExternalTreeNode ) { switch (configNode, liveNode) { @@ -972,7 +972,7 @@ extension Workspace { guard !resolved else { return } resolved = true if let observer { NotificationCenter.default.removeObserver(observer) } - NSLog("[CmuxConfig] surface not ready after 3s, dropping command (%d chars)", text.count) + NSLog("[ProgramaConfig] surface not ready after 3s, dropping command (%d chars)", text.count) } } } @@ -6872,7 +6872,7 @@ final class Workspace: Identifiable, ObservableObject { title: String = "Terminal", workingDirectory: String? = nil, portOrdinal: Int = 0, - configTemplate: CmuxSurfaceConfigTemplate? = nil, + configTemplate: ProgramaSurfaceConfigTemplate? = nil, initialTerminalCommand: String? = nil, initialTerminalEnvironment: [String: String] = [:] ) { @@ -6985,7 +6985,7 @@ final class Workspace: Identifiable, ObservableObject { title: String = "Terminal", workingDirectory: String? = nil, portOrdinal: Int = 0, - configTemplate: CmuxSurfaceConfigTemplate? = nil + configTemplate: ProgramaSurfaceConfigTemplate? = nil ) { self.id = UUID() self.portOrdinal = portOrdinal @@ -8752,7 +8752,7 @@ final class Workspace: Identifiable, ObservableObject { private func seedTerminalInheritanceFontPoints( panelId: UUID, - configTemplate: CmuxSurfaceConfigTemplate? + configTemplate: ProgramaSurfaceConfigTemplate? ) { guard let fontPoints = configTemplate?.fontSize, fontPoints > 0 else { return } terminalInheritanceFontPointsByPanelId[panelId] = fontPoints @@ -8762,7 +8762,7 @@ final class Workspace: Identifiable, ObservableObject { private func resolvedTerminalInheritanceFontPoints( for terminalPanel: TerminalPanel, sourceSurface: ghostty_surface_t, - inheritedConfig: CmuxSurfaceConfigTemplate + inheritedConfig: ProgramaSurfaceConfigTemplate ) -> Float? { let runtimePoints = cmuxCurrentSurfaceFontSizePoints(sourceSurface) if let rooted = terminalInheritanceFontPointsByPanelId[terminalPanel.id], rooted > 0 { @@ -8873,7 +8873,7 @@ final class Workspace: Identifiable, ObservableObject { private func inheritedTerminalConfig( preferredPanelId: UUID? = nil, inPane preferredPaneId: PaneID? = nil - ) -> CmuxSurfaceConfigTemplate? { + ) -> ProgramaSurfaceConfigTemplate? { // Walk candidates in priority order and use the first panel that still exposes // a runtime surface pointer. for terminalPanel in terminalPanelConfigInheritanceCandidates( @@ -8909,7 +8909,7 @@ final class Workspace: Identifiable, ObservableObject { } if let fallbackFontPoints = lastTerminalConfigInheritanceFontPoints { - var config = CmuxSurfaceConfigTemplate() + var config = ProgramaSurfaceConfigTemplate() config.fontSize = fallbackFontPoints #if DEBUG dlog( diff --git a/Sources/cmuxApp.swift b/Sources/cmuxApp.swift index 33c1d3d9e65..dd351d33f0a 100644 --- a/Sources/cmuxApp.swift +++ b/Sources/cmuxApp.swift @@ -139,7 +139,7 @@ struct cmuxApp: App { @StateObject private var notificationStore = TerminalNotificationStore.shared @StateObject private var sidebarState = SidebarState() @StateObject private var sidebarSelectionState = SidebarSelectionState() - @StateObject private var cmuxConfigStore = CmuxConfigStore() + @StateObject private var cmuxConfigStore = ProgramaConfigStore() @StateObject private var keyboardShortcutSettingsObserver = KeyboardShortcutSettingsObserver.shared private let primaryWindowId = UUID() @AppStorage(AppearanceSettings.appearanceModeKey) private var appearanceMode = AppearanceSettings.defaultMode.rawValue @@ -355,8 +355,8 @@ struct cmuxApp: App { splitCommandButton(title: String(localized: "menu.app.settings", defaultValue: "Settings…"), shortcut: menuShortcut(for: .openSettings)) { appDelegate.openPreferencesWindow(debugSource: "menu.cmdComma") } - Button(String(localized: "menu.app.openCmuxSettingsFile", defaultValue: "Open settings.json")) { - openCmuxSettingsFileInEditor() + Button(String(localized: "menu.app.openProgramaSettingsFile", defaultValue: "Open settings.json")) { + openProgramaSettingsFileInEditor() } Button(String(localized: "menu.app.ghosttySettings", defaultValue: "Ghostty Settings…")) { GhosttyApp.shared.openConfigurationInTextEdit() @@ -378,7 +378,7 @@ struct cmuxApp: App { } CommandGroup(replacing: .appTermination) { - splitCommandButton(title: String(localized: "menu.quitCmux", defaultValue: "Quit cmux"), shortcut: menuShortcut(for: .quit)) { + splitCommandButton(title: String(localized: "menu.quitPrograma", defaultValue: "Quit cmux"), shortcut: menuShortcut(for: .quit)) { NSApp.terminate(nil) } } @@ -3860,7 +3860,7 @@ enum PreferredEditorSettings { /// Open a file path with the user's preferred editor, falling back to system default. static func open(_ url: URL) { - if CmuxUITestCapture.appendLineIfConfigured( + if ProgramaUITestCapture.appendLineIfConfigured( envKey: "PROGRAMA_UI_TEST_CAPTURE_OPEN_PATH", line: url.path ) { @@ -3897,7 +3897,7 @@ enum PreferredEditorSettings { } } -enum CmuxUITestCapture { +enum ProgramaUITestCapture { static func appendLineIfConfigured(envKey: String, line: String) -> Bool { guard let url = configuredURL(for: envKey) else { return false } appendLine(line, to: url) @@ -3971,7 +3971,7 @@ enum CmuxUITestCapture { } } -enum CmuxRuntimeDebugCapture { +enum ProgramaRuntimeDebugCapture { private struct Configuration { let baseURL: URL let token: String @@ -4044,12 +4044,12 @@ enum CmuxRuntimeDebugCapture { } } -private func openCmuxSettingsFileInEditor() { +private func openProgramaSettingsFileInEditor() { let url = KeyboardShortcutSettings.settingsFileStore.settingsFileURLForEditing() PreferredEditorSettings.open(url) } -private func openCmuxSettingsFileInTextEdit() { +private func openProgramaSettingsFileInTextEdit() { #if os(macOS) let fileURL = KeyboardShortcutSettings.settingsFileStore.settingsFileURLForEditing() let editorURL = URL(fileURLWithPath: "/System/Applications/TextEdit.app") @@ -4086,9 +4086,9 @@ struct SettingsView: View { @AppStorage(BrowserImportHintSettings.showOnBlankTabsKey) private var showBrowserImportHintOnBlankTabs = BrowserImportHintSettings.defaultShowOnBlankTabs @AppStorage(BrowserImportHintSettings.dismissedKey) private var isBrowserImportHintDismissed = BrowserImportHintSettings.defaultDismissed @AppStorage(ReactGrabSettings.versionKey) private var reactGrabVersion = ReactGrabSettings.defaultVersion - @AppStorage(BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey) private var openTerminalLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenTerminalLinksInCmuxBrowser - @AppStorage(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowserKey) - private var interceptTerminalOpenCommandInCmuxBrowser = BrowserLinkOpenSettings.initialInterceptTerminalOpenCommandInCmuxBrowserValue() + @AppStorage(BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowserKey) private var openTerminalLinksInProgramaBrowser = BrowserLinkOpenSettings.defaultOpenTerminalLinksInProgramaBrowser + @AppStorage(BrowserLinkOpenSettings.interceptTerminalOpenCommandInProgramaBrowserKey) + private var interceptTerminalOpenCommandInProgramaBrowser = BrowserLinkOpenSettings.initialInterceptTerminalOpenCommandInProgramaBrowserValue() @AppStorage(BrowserLinkOpenSettings.browserHostWhitelistKey) private var browserHostWhitelist = BrowserLinkOpenSettings.defaultBrowserHostWhitelist @AppStorage(BrowserLinkOpenSettings.browserExternalOpenPatternsKey) private var browserExternalOpenPatterns = BrowserLinkOpenSettings.defaultBrowserExternalOpenPatterns @@ -4126,10 +4126,10 @@ struct SettingsView: View { @AppStorage("sidebarNotificationBadgeColorHex") private var sidebarNotificationBadgeColorHex: String? @AppStorage("sidebarShowBranchDirectory") private var sidebarShowBranchDirectory = true @AppStorage("sidebarShowPullRequest") private var sidebarShowPullRequest = true - @AppStorage(BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowserKey) - private var openSidebarPullRequestLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInCmuxBrowser - @AppStorage(BrowserLinkOpenSettings.openSidebarPortLinksInCmuxBrowserKey) - private var openSidebarPortLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPortLinksInCmuxBrowser + @AppStorage(BrowserLinkOpenSettings.openSidebarPullRequestLinksInProgramaBrowserKey) + private var openSidebarPullRequestLinksInProgramaBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInProgramaBrowser + @AppStorage(BrowserLinkOpenSettings.openSidebarPortLinksInProgramaBrowserKey) + private var openSidebarPortLinksInProgramaBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPortLinksInProgramaBrowser @AppStorage(ShortcutHintDebugSettings.showHintsOnCommandHoldKey) private var showShortcutHintsOnCommandHold = ShortcutHintDebugSettings.defaultShowHintsOnCommandHold @AppStorage("sidebarShowSSH") private var sidebarShowSSH = true @@ -4166,7 +4166,7 @@ struct SettingsView: View { @State private var showLanguageRestartAlert = false @State private var isResettingSettings = false @State private var workspaceTabPaletteEntries = WorkspaceTabColorSettings.palette() - @State private var trustedDirectoriesDraft: String = CmuxDirectoryTrust.shared.allTrustedPaths.joined(separator: "\n") + @State private var trustedDirectoriesDraft: String = ProgramaDirectoryTrust.shared.allTrustedPaths.joined(separator: "\n") private var selectedWorkspacePlacement: NewWorkspacePlacement { NewWorkspacePlacement(rawValue: newWorkspacePlacement) ?? WorkspacePlacementSettings.defaultPlacement @@ -4348,7 +4348,7 @@ struct SettingsView: View { .split(separator: "\n", omittingEmptySubsequences: true) .map { $0.trimmingCharacters(in: .whitespaces) } .filter { !$0.isEmpty } - CmuxDirectoryTrust.shared.replaceAll(with: paths) + ProgramaDirectoryTrust.shared.replaceAll(with: paths) } private var hasCustomNotificationSoundFilePath: Bool { @@ -5010,11 +5010,11 @@ struct SettingsView: View { SettingsCardRow( String(localized: "settings.app.openSidebarPRLinks", defaultValue: "Open Sidebar PR Links in cmux Browser"), - subtitle: openSidebarPullRequestLinksInCmuxBrowser + subtitle: openSidebarPullRequestLinksInProgramaBrowser ? String(localized: "settings.app.openSidebarPRLinks.subtitleOn", defaultValue: "Clicks open inside cmux browser.") : String(localized: "settings.app.openSidebarPRLinks.subtitleOff", defaultValue: "Clicks open in your default browser.") ) { - Toggle("", isOn: $openSidebarPullRequestLinksInCmuxBrowser) + Toggle("", isOn: $openSidebarPullRequestLinksInProgramaBrowser) .labelsHidden() .controlSize(.small) } @@ -5024,11 +5024,11 @@ struct SettingsView: View { SettingsCardRow( String(localized: "settings.app.openSidebarPortLinks", defaultValue: "Open Sidebar Port Links in cmux Browser"), - subtitle: openSidebarPortLinksInCmuxBrowser + subtitle: openSidebarPortLinksInProgramaBrowser ? String(localized: "settings.app.openSidebarPortLinks.subtitleOn", defaultValue: "Port clicks open inside cmux browser.") : String(localized: "settings.app.openSidebarPortLinks.subtitleOff", defaultValue: "Port clicks open in your default browser.") ) { - Toggle("", isOn: $openSidebarPortLinksInCmuxBrowser) + Toggle("", isOn: $openSidebarPortLinksInProgramaBrowser) .labelsHidden() .controlSize(.small) } @@ -5515,7 +5515,7 @@ struct SettingsView: View { String(localized: "settings.browser.openTerminalLinks", defaultValue: "Open Terminal Links in cmux Browser"), subtitle: String(localized: "settings.browser.openTerminalLinks.subtitle", defaultValue: "When off, links clicked in terminal output open in your default browser.") ) { - Toggle("", isOn: $openTerminalLinksInCmuxBrowser) + Toggle("", isOn: $openTerminalLinksInProgramaBrowser) .labelsHidden() .controlSize(.small) } @@ -5526,12 +5526,12 @@ struct SettingsView: View { String(localized: "settings.browser.interceptOpen", defaultValue: "Intercept open http(s) in Terminal"), subtitle: String(localized: "settings.browser.interceptOpen.subtitle", defaultValue: "When off, `open https://...` and `open http://...` always use your default browser.") ) { - Toggle("", isOn: $interceptTerminalOpenCommandInCmuxBrowser) + Toggle("", isOn: $interceptTerminalOpenCommandInProgramaBrowser) .labelsHidden() .controlSize(.small) } - if openTerminalLinksInCmuxBrowser || interceptTerminalOpenCommandInCmuxBrowser { + if openTerminalLinksInProgramaBrowser || interceptTerminalOpenCommandInProgramaBrowser { SettingsCardDivider() VStack(alignment: .leading, spacing: 6) { @@ -5755,7 +5755,7 @@ struct SettingsView: View { .accessibilityIdentifier("SettingsKeyboardShortcutsChordDocsLink") Button(String(localized: "settings.app.settingsFile.openButton", defaultValue: "Open settings.json")) { - openCmuxSettingsFileInTextEdit() + openProgramaSettingsFileInTextEdit() } .buttonStyle(.bordered) .controlSize(.small) @@ -5875,7 +5875,7 @@ struct SettingsView: View { title: String(localized: "settings.app.settingsFile.openButton", defaultValue: "Open settings.json"), helpText: KeyboardShortcutSettings.settingsFileStore.settingsFileDisplayPath(), accessibilityIdentifier: "SettingsFileOpenButton", - action: openCmuxSettingsFileInTextEdit + action: openProgramaSettingsFileInTextEdit ) } } @@ -6018,8 +6018,8 @@ struct SettingsView: View { browserImportHintVariantRaw = BrowserImportHintSettings.defaultVariant.rawValue showBrowserImportHintOnBlankTabs = BrowserImportHintSettings.defaultShowOnBlankTabs isBrowserImportHintDismissed = BrowserImportHintSettings.defaultDismissed - openTerminalLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenTerminalLinksInCmuxBrowser - interceptTerminalOpenCommandInCmuxBrowser = BrowserLinkOpenSettings.defaultInterceptTerminalOpenCommandInCmuxBrowser + openTerminalLinksInProgramaBrowser = BrowserLinkOpenSettings.defaultOpenTerminalLinksInProgramaBrowser + interceptTerminalOpenCommandInProgramaBrowser = BrowserLinkOpenSettings.defaultInterceptTerminalOpenCommandInProgramaBrowser browserHostWhitelist = BrowserLinkOpenSettings.defaultBrowserHostWhitelist browserExternalOpenPatterns = BrowserLinkOpenSettings.defaultBrowserExternalOpenPatterns browserInsecureHTTPAllowlist = BrowserInsecureHTTPSettings.defaultAllowlistText @@ -6059,8 +6059,8 @@ struct SettingsView: View { sidebarNotificationBadgeColorHex = nil sidebarShowBranchDirectory = true sidebarShowPullRequest = true - openSidebarPullRequestLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInCmuxBrowser - openSidebarPortLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPortLinksInCmuxBrowser + openSidebarPullRequestLinksInProgramaBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInProgramaBrowser + openSidebarPortLinksInProgramaBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPortLinksInProgramaBrowser showShortcutHintsOnCommandHold = ShortcutHintDebugSettings.defaultShowHintsOnCommandHold sidebarShowSSH = true sidebarShowPorts = true diff --git a/cmuxTests/BrowserConfigTests.swift b/cmuxTests/BrowserConfigTests.swift index 9172d7bfc60..d22224a6e61 100644 --- a/cmuxTests/BrowserConfigTests.swift +++ b/cmuxTests/BrowserConfigTests.swift @@ -19,7 +19,7 @@ var cmuxUnitTestInspectorOverrideInstalled = false var cmuxUnitTestWKWebViewPerformKeyEquivalentOverrideInstalled = false var cmuxUnitTestWKWebViewPerformKeyEquivalentHook: ((WKWebView, NSEvent) -> Bool?)? -extension CmuxWebView { +extension ProgramaWebView { @objc func cmuxUnitTestInspector() -> NSObject? { objc_getAssociatedObject(self, &cmuxUnitTestInspectorAssociationKey) as? NSObject } @@ -44,30 +44,30 @@ extension WKWebView { } } -func installCmuxUnitTestInspectorOverride() { +func installProgramaUnitTestInspectorOverride() { guard !cmuxUnitTestInspectorOverrideInstalled else { return } guard let replacementMethod = class_getInstanceMethod( - CmuxWebView.self, - #selector(CmuxWebView.cmuxUnitTestInspector) + ProgramaWebView.self, + #selector(ProgramaWebView.cmuxUnitTestInspector) ) else { fatalError("Unable to locate test inspector replacement method") } let added = class_addMethod( - CmuxWebView.self, + ProgramaWebView.self, NSSelectorFromString("_inspector"), method_getImplementation(replacementMethod), method_getTypeEncoding(replacementMethod) ) guard added else { - fatalError("Unable to install CmuxWebView _inspector test override") + fatalError("Unable to install ProgramaWebView _inspector test override") } cmuxUnitTestInspectorOverrideInstalled = true } -func installCmuxUnitTestWKWebViewPerformKeyEquivalentOverride() { +func installProgramaUnitTestWKWebViewPerformKeyEquivalentOverride() { guard !cmuxUnitTestWKWebViewPerformKeyEquivalentOverrideInstalled else { return } let originalSelector = #selector(NSResponder.performKeyEquivalent(with:)) @@ -114,7 +114,7 @@ private final class BrowserMarkedTextProbeTextView: NSTextView { } } -final class CmuxWebViewKeyEquivalentTests: XCTestCase { +final class ProgramaWebViewKeyEquivalentTests: XCTestCase { private final class ActionSpy: NSObject { private(set) var invoked: Bool = false @@ -184,7 +184,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let spy = ActionSpy() installMenu(spy: spy, key: "n", modifiers: [.command]) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) let event = makeKeyDownEvent(key: "n", modifiers: [.command], keyCode: 45) // kVK_ANSI_N XCTAssertNotNil(event) @@ -196,7 +196,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let spy = ActionSpy() installMenu(spy: spy, key: "w", modifiers: [.command]) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) let event = makeKeyDownEvent(key: "w", modifiers: [.command], keyCode: 13) // kVK_ANSI_W XCTAssertNotNil(event) @@ -208,7 +208,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let spy = ActionSpy() installMenu(spy: spy, key: "r", modifiers: [.command]) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) let event = makeKeyDownEvent(key: "r", modifiers: [.command], keyCode: 15) // kVK_ANSI_R XCTAssertNotNil(event) @@ -220,7 +220,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let spy = ActionSpy() installMenu(spy: spy, key: "\r", modifiers: []) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) let event = makeKeyDownEvent(key: "\r", modifiers: [], keyCode: 36) // kVK_Return XCTAssertNotNil(event) @@ -232,7 +232,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let spy = ActionSpy() installMenu(spy: spy, key: "\r", modifiers: [.command]) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) let event = makeKeyDownEvent(key: "\r", modifiers: [.command], keyCode: 36) // kVK_Return XCTAssertNotNil(event) @@ -244,7 +244,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let spy = ActionSpy() installMenu(spy: spy, key: "\r", modifiers: []) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) let event = makeKeyDownEvent(key: "\r", modifiers: [], keyCode: 76) // kVK_ANSI_KeypadEnter XCTAssertNotNil(event) @@ -265,7 +265,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -298,7 +298,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -331,7 +331,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -367,7 +367,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -404,7 +404,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -478,7 +478,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { slot.autoresizingMask = [.width, .height] host.addSubview(slot) - let webView = CmuxWebView(frame: slot.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: slot.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] slot.addSubview(webView) @@ -533,7 +533,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 80, y: 60, width: 480, height: 260)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) window.makeKeyAndOrderFront(nil) contentView.layoutSubtreeIfNeeded() @@ -635,7 +635,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -790,7 +790,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -822,14 +822,14 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { _ = webView.performKeyEquivalent(with: event) XCTAssertFalse( spy.invoked, - "CmuxWebView should not route Cmd+` directly to the menu when WebKit is first responder" + "ProgramaWebView should not route Cmd+` directly to the menu when WebKit is first responder" ) } @MainActor func testCmdFDoesNotPreflightIntoPageWhenWebInspectorResponderIsFocused() { _ = NSApplication.shared - installCmuxUnitTestWKWebViewPerformKeyEquivalentOverride() + installProgramaUnitTestWKWebViewPerformKeyEquivalentOverride() let spy = ActionSpy() installMenu(spy: spy, key: "f", modifiers: [.command]) @@ -843,7 +843,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -881,7 +881,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { XCTAssertEqual( forwardedEvents.count, 0, - "Did not expect CmuxWebView to preflight Cmd+F into page content while Web Inspector is focused" + "Did not expect ProgramaWebView to preflight Cmd+F into page content while Web Inspector is focused" ) } @@ -941,7 +941,7 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { @MainActor -final class CmuxWebViewContextMenuTests: XCTestCase { +final class ProgramaWebViewContextMenuTests: XCTestCase { private func makeRightMouseDownEvent() -> NSEvent { guard let event = NSEvent.mouseEvent( with: .rightMouseDown, @@ -961,7 +961,7 @@ final class CmuxWebViewContextMenuTests: XCTestCase { func testWillOpenMenuAddsOpenLinkInDefaultBrowserAndRoutesSelectionToDefaultBrowserOpener() { _ = NSApplication.shared - let webView = CmuxWebView(frame: NSRect(x: 0, y: 0, width: 800, height: 600), configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: NSRect(x: 0, y: 0, width: 800, height: 600), configuration: WKWebViewConfiguration()) let menu = NSMenu() let openLinkItem = NSMenuItem(title: "Open Link", action: nil, keyEquivalent: "") openLinkItem.identifier = NSUserInterfaceItemIdentifier("WKMenuItemIdentifierOpenLink") @@ -1003,7 +1003,7 @@ final class CmuxWebViewContextMenuTests: XCTestCase { } func testWillOpenMenuSkipsDefaultBrowserItemWhenContextHasNoOpenLinkEntry() { - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) let menu = NSMenu() menu.addItem(NSMenuItem(title: "Back", action: nil, keyEquivalent: "")) menu.addItem(NSMenuItem(title: "Forward", action: nil, keyEquivalent: "")) @@ -1014,7 +1014,7 @@ final class CmuxWebViewContextMenuTests: XCTestCase { } func testWillOpenMenuHooksDownloadImageToDiskMenuVariant() { - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) let menu = NSMenu() let originalTarget = NSObject() let originalAction = NSSelectorFromString("downloadImageToDisk:") @@ -1031,7 +1031,7 @@ final class CmuxWebViewContextMenuTests: XCTestCase { } func testWillOpenMenuHooksDownloadLinkedFileToDiskMenuVariant() { - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) let menu = NSMenu() let originalTarget = NSObject() let originalAction = NSSelectorFromString("downloadLinkToDisk:") @@ -1969,7 +1969,7 @@ final class BrowserDeveloperToolsVisibilityPersistenceTests: XCTestCase { override class func setUp() { super.setUp() - installCmuxUnitTestInspectorOverride() + installProgramaUnitTestInspectorOverride() } private func makePanelWithInspector( @@ -2628,7 +2628,7 @@ final class BrowserIMEKeyDownRoutingTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -2677,7 +2677,7 @@ final class BrowserIMEKeyDownRoutingTests: XCTestCase { let container = NSView(frame: window.contentRect(forFrameRect: window.frame)) window.contentView = container - let webView = CmuxWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: container.bounds, configuration: WKWebViewConfiguration()) webView.autoresizingMask = [.width, .height] container.addSubview(webView) @@ -3087,10 +3087,10 @@ final class BrowserHistoryStoreTests: XCTestCase { @MainActor -final class CmuxWebViewDragRoutingTests: XCTestCase { +final class ProgramaWebViewDragRoutingTests: XCTestCase { func testRejectsInternalPaneDragEvenWhenFilePromiseTypesArePresent() { XCTAssertTrue( - CmuxWebView.shouldRejectInternalPaneDrag([ + ProgramaWebView.shouldRejectInternalPaneDrag([ DragOverlayRoutingPolicy.bonsplitTabTransferType, NSPasteboard.PasteboardType("com.apple.pasteboard.promised-file-url"), ]) @@ -3098,7 +3098,7 @@ final class CmuxWebViewDragRoutingTests: XCTestCase { } func testAllowsRegularExternalFileDrops() { - XCTAssertFalse(CmuxWebView.shouldRejectInternalPaneDrag([.fileURL])) + XCTAssertFalse(ProgramaWebView.shouldRejectInternalPaneDrag([.fileURL])) } } @@ -3120,56 +3120,56 @@ final class BrowserLinkOpenSettingsTests: XCTestCase { super.tearDown() } - func testTerminalLinksDefaultToCmuxBrowser() { - XCTAssertTrue(BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowser(defaults: defaults)) + func testTerminalLinksDefaultToProgramaBrowser() { + XCTAssertTrue(BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowser(defaults: defaults)) } func testTerminalLinksPreferenceUsesStoredValue() { - defaults.set(false, forKey: BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey) - XCTAssertFalse(BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowser(defaults: defaults)) + defaults.set(false, forKey: BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowserKey) + XCTAssertFalse(BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowser(defaults: defaults)) - defaults.set(true, forKey: BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey) - XCTAssertTrue(BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowser(defaults: defaults)) + defaults.set(true, forKey: BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowserKey) + XCTAssertTrue(BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowser(defaults: defaults)) } - func testSidebarPullRequestLinksDefaultToCmuxBrowser() { - XCTAssertTrue(BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowser(defaults: defaults)) + func testSidebarPullRequestLinksDefaultToProgramaBrowser() { + XCTAssertTrue(BrowserLinkOpenSettings.openSidebarPullRequestLinksInProgramaBrowser(defaults: defaults)) } func testSidebarPullRequestLinksPreferenceUsesStoredValue() { - defaults.set(false, forKey: BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowserKey) - XCTAssertFalse(BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowser(defaults: defaults)) + defaults.set(false, forKey: BrowserLinkOpenSettings.openSidebarPullRequestLinksInProgramaBrowserKey) + XCTAssertFalse(BrowserLinkOpenSettings.openSidebarPullRequestLinksInProgramaBrowser(defaults: defaults)) - defaults.set(true, forKey: BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowserKey) - XCTAssertTrue(BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowser(defaults: defaults)) + defaults.set(true, forKey: BrowserLinkOpenSettings.openSidebarPullRequestLinksInProgramaBrowserKey) + XCTAssertTrue(BrowserLinkOpenSettings.openSidebarPullRequestLinksInProgramaBrowser(defaults: defaults)) } - func testOpenCommandInterceptionDefaultsToCmuxBrowser() { - XCTAssertTrue(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults)) + func testOpenCommandInterceptionDefaultsToProgramaBrowser() { + XCTAssertTrue(BrowserLinkOpenSettings.interceptTerminalOpenCommandInProgramaBrowser(defaults: defaults)) } func testOpenCommandInterceptionUsesStoredValue() { - defaults.set(false, forKey: BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowserKey) - XCTAssertFalse(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults)) + defaults.set(false, forKey: BrowserLinkOpenSettings.interceptTerminalOpenCommandInProgramaBrowserKey) + XCTAssertFalse(BrowserLinkOpenSettings.interceptTerminalOpenCommandInProgramaBrowser(defaults: defaults)) - defaults.set(true, forKey: BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowserKey) - XCTAssertTrue(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults)) + defaults.set(true, forKey: BrowserLinkOpenSettings.interceptTerminalOpenCommandInProgramaBrowserKey) + XCTAssertTrue(BrowserLinkOpenSettings.interceptTerminalOpenCommandInProgramaBrowser(defaults: defaults)) } func testOpenCommandInterceptionFallsBackToLegacyLinkToggleWhenUnset() { - defaults.set(false, forKey: BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey) - XCTAssertFalse(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults)) + defaults.set(false, forKey: BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowserKey) + XCTAssertFalse(BrowserLinkOpenSettings.interceptTerminalOpenCommandInProgramaBrowser(defaults: defaults)) - defaults.set(true, forKey: BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey) - XCTAssertTrue(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults)) + defaults.set(true, forKey: BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowserKey) + XCTAssertTrue(BrowserLinkOpenSettings.interceptTerminalOpenCommandInProgramaBrowser(defaults: defaults)) } func testSettingsInitialOpenCommandInterceptionValueFallsBackToLegacyLinkToggleWhenUnset() { - defaults.set(false, forKey: BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey) - XCTAssertFalse(BrowserLinkOpenSettings.initialInterceptTerminalOpenCommandInCmuxBrowserValue(defaults: defaults)) + defaults.set(false, forKey: BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowserKey) + XCTAssertFalse(BrowserLinkOpenSettings.initialInterceptTerminalOpenCommandInProgramaBrowserValue(defaults: defaults)) - defaults.set(true, forKey: BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey) - XCTAssertTrue(BrowserLinkOpenSettings.initialInterceptTerminalOpenCommandInCmuxBrowserValue(defaults: defaults)) + defaults.set(true, forKey: BrowserLinkOpenSettings.openTerminalLinksInProgramaBrowserKey) + XCTAssertTrue(BrowserLinkOpenSettings.initialInterceptTerminalOpenCommandInProgramaBrowserValue(defaults: defaults)) } func testExternalOpenPatternsDefaultToEmpty() { diff --git a/cmuxTests/BrowserPanelTests.swift b/cmuxTests/BrowserPanelTests.swift index 1aa2bbb9e06..ab194fba3fe 100644 --- a/cmuxTests/BrowserPanelTests.swift +++ b/cmuxTests/BrowserPanelTests.swift @@ -2176,7 +2176,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let browserAnchor = NSView(frame: NSRect(x: 240, y: 20, width: 220, height: 140)) contentView.addSubview(browserAnchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) browserPortal.bind(webView: webView, to: browserAnchor, visibleInUI: true) browserPortal.synchronizeWebViewForAnchor(browserAnchor) assertHostOrder("Browser portal sync should keep browser panes above portal-hosted terminals") @@ -2202,7 +2202,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { contentView.addSubview(anchor1) contentView.addSubview(anchor2) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) portal.bind(webView: webView, to: anchor1, visibleInUI: true) let firstSuperview = webView.superview @@ -2245,7 +2245,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 120, y: 20, width: 260, height: 150)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) portal.bind(webView: webView, to: anchor, visibleInUI: true) contentView.layoutSubtreeIfNeeded() portal.synchronizeWebViewForAnchor(anchor) @@ -2284,7 +2284,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: -30, y: 0, width: 220, height: 120)) clipView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) portal.bind(webView: webView, to: anchor, visibleInUI: true) contentView.layoutSubtreeIfNeeded() clipView.layoutSubtreeIfNeeded() @@ -2320,7 +2320,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 40, y: 20, width: 220, height: 160)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) portal.bind(webView: webView, to: anchor, visibleInUI: true) contentView.layoutSubtreeIfNeeded() portal.synchronizeWebViewForAnchor(anchor) @@ -2343,7 +2343,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { func testPortalSlotPinPreservesSideDockedInspectorManagedWebViewFrameOnRehost() { let slot = WindowBrowserSlotView(frame: NSRect(x: 0, y: 0, width: 240, height: 160)) - let webView = CmuxWebView(frame: NSRect(x: 0, y: 0, width: 132, height: 160), configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: NSRect(x: 0, y: 0, width: 132, height: 160), configuration: WKWebViewConfiguration()) let inspectorContainer = NSView(frame: NSRect(x: 132, y: 0, width: 108, height: 160)) let inspectorView = WKInspectorProbeView(frame: inspectorContainer.bounds) inspectorView.autoresizingMask = [.width, .height] @@ -2386,7 +2386,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 40, y: 24, width: 260, height: 180)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) portal.bind(webView: webView, to: anchor, visibleInUI: true) contentView.layoutSubtreeIfNeeded() portal.synchronizeWebViewForAnchor(anchor) @@ -2588,7 +2588,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 40, y: 24, width: 260, height: 180)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) portal.bind(webView: webView, to: anchor, visibleInUI: true) contentView.layoutSubtreeIfNeeded() portal.synchronizeWebViewForAnchor(anchor) @@ -2703,7 +2703,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 40, y: 24, width: 260, height: 180)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) portal.bind(webView: webView, to: anchor, visibleInUI: true) contentView.layoutSubtreeIfNeeded() portal.synchronizeWebViewForAnchor(anchor) @@ -2772,7 +2772,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 40, y: 24, width: 220, height: 160)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) portal.bind(webView: webView, to: anchor, visibleInUI: true) portal.synchronizeWebViewForAnchor(anchor) @@ -2803,7 +2803,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 40, y: 24, width: 220, height: 160)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) portal.bind(webView: webView, to: anchor, visibleInUI: true) portal.synchronizeWebViewForAnchor(anchor) @@ -3016,7 +3016,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 20, y: 20, width: 180, height: 120)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) BrowserWindowPortalRegistry.bind(webView: webView, to: anchor, visibleInUI: true) XCTAssertNotNil(webView.superview) @@ -3041,7 +3041,7 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 20, y: 20, width: 180, height: 120)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) BrowserWindowPortalRegistry.bind(webView: webView, to: anchor, visibleInUI: true) BrowserWindowPortalRegistry.synchronizeForAnchor(anchor) diff --git a/cmuxTests/CmuxConfigTests.swift b/cmuxTests/CmuxConfigTests.swift index d56332e67d7..173594813df 100644 --- a/cmuxTests/CmuxConfigTests.swift +++ b/cmuxTests/CmuxConfigTests.swift @@ -8,11 +8,11 @@ import XCTest // MARK: - JSON Decoding -final class CmuxConfigDecodingTests: XCTestCase { +final class ProgramaConfigDecodingTests: XCTestCase { - private func decode(_ json: String) throws -> CmuxConfigFile { + private func decode(_ json: String) throws -> ProgramaConfigFile { let data = json.data(using: .utf8)! - return try JSONDecoder().decode(CmuxConfigFile.self, from: data) + return try JSONDecoder().decode(ProgramaConfigFile.self, from: data) } // MARK: Simple commands @@ -512,28 +512,28 @@ final class CmuxConfigDecodingTests: XCTestCase { // MARK: - Command identity -final class CmuxCommandIdentityTests: XCTestCase { +final class ProgramaCommandIdentityTests: XCTestCase { func testCommandIdIsDeterministic() { - let cmd = CmuxCommandDefinition(name: "Run tests", command: "test") + let cmd = ProgramaCommandDefinition(name: "Run tests", command: "test") XCTAssertEqual(cmd.id, "cmux.config.command.Run%20tests") } func testCommandIdEncodesSpecialCharacters() { - let cmd = CmuxCommandDefinition(name: "build & deploy", command: "make") + let cmd = ProgramaCommandDefinition(name: "build & deploy", command: "make") XCTAssertTrue(cmd.id.hasPrefix("cmux.config.command.")) XCTAssertFalse(cmd.id.contains("&")) XCTAssertFalse(cmd.id.contains(" ")) } func testCommandIdIsUniqueForDifferentNames() { - let cmd1 = CmuxCommandDefinition(name: "build", command: "make build") - let cmd2 = CmuxCommandDefinition(name: "test", command: "make test") + let cmd1 = ProgramaCommandDefinition(name: "build", command: "make build") + let cmd2 = ProgramaCommandDefinition(name: "test", command: "make test") XCTAssertNotEqual(cmd1.id, cmd2.id) } func testCommandIdDoesNotCollideWithBuiltinPrefix() { - let cmd = CmuxCommandDefinition(name: "palette.newWorkspace", command: "echo") + let cmd = ProgramaCommandDefinition(name: "palette.newWorkspace", command: "echo") XCTAssertTrue(cmd.id.hasPrefix("cmux.config.command.")) XCTAssertNotEqual(cmd.id, "palette.newWorkspace") } @@ -541,45 +541,45 @@ final class CmuxCommandIdentityTests: XCTestCase { // MARK: - Split clamping -final class CmuxSplitDefinitionTests: XCTestCase { +final class ProgramaSplitDefinitionTests: XCTestCase { func testClampedSplitPositionDefaultsToHalf() { - let split = CmuxSplitDefinition(direction: .horizontal, split: nil, children: []) + let split = ProgramaSplitDefinition(direction: .horizontal, split: nil, children: []) XCTAssertEqual(split.clampedSplitPosition, 0.5) } func testClampedSplitPositionPassesThroughValidValue() { - let split = CmuxSplitDefinition(direction: .vertical, split: 0.3, children: []) + let split = ProgramaSplitDefinition(direction: .vertical, split: 0.3, children: []) XCTAssertEqual(split.clampedSplitPosition, 0.3, accuracy: 0.001) } func testClampedSplitPositionClampsLow() { - let split = CmuxSplitDefinition(direction: .horizontal, split: 0.01, children: []) + let split = ProgramaSplitDefinition(direction: .horizontal, split: 0.01, children: []) XCTAssertEqual(split.clampedSplitPosition, 0.1, accuracy: 0.001) } func testClampedSplitPositionClampsHigh() { - let split = CmuxSplitDefinition(direction: .horizontal, split: 0.99, children: []) + let split = ProgramaSplitDefinition(direction: .horizontal, split: 0.99, children: []) XCTAssertEqual(split.clampedSplitPosition, 0.9, accuracy: 0.001) } func testClampedSplitPositionClampsNegative() { - let split = CmuxSplitDefinition(direction: .horizontal, split: -1.0, children: []) + let split = ProgramaSplitDefinition(direction: .horizontal, split: -1.0, children: []) XCTAssertEqual(split.clampedSplitPosition, 0.1, accuracy: 0.001) } func testClampedSplitPositionClampsAboveOne() { - let split = CmuxSplitDefinition(direction: .horizontal, split: 2.0, children: []) + let split = ProgramaSplitDefinition(direction: .horizontal, split: 2.0, children: []) XCTAssertEqual(split.clampedSplitPosition, 0.9, accuracy: 0.001) } func testSplitOrientationHorizontal() { - let split = CmuxSplitDefinition(direction: .horizontal, split: nil, children: []) + let split = ProgramaSplitDefinition(direction: .horizontal, split: nil, children: []) XCTAssertEqual(split.splitOrientation, .horizontal) } func testSplitOrientationVertical() { - let split = CmuxSplitDefinition(direction: .vertical, split: nil, children: []) + let split = ProgramaSplitDefinition(direction: .vertical, split: nil, children: []) XCTAssertEqual(split.splitOrientation, .vertical) } } @@ -587,41 +587,41 @@ final class CmuxSplitDefinitionTests: XCTestCase { // MARK: - CWD resolution @MainActor -final class CmuxConfigCwdResolutionTests: XCTestCase { +final class ProgramaConfigCwdResolutionTests: XCTestCase { private let baseCwd = "/Users/test/project" func testNilCwdReturnsBase() { XCTAssertEqual( - CmuxConfigStore.resolveCwd(nil, relativeTo: baseCwd), + ProgramaConfigStore.resolveCwd(nil, relativeTo: baseCwd), baseCwd ) } func testEmptyCwdReturnsBase() { XCTAssertEqual( - CmuxConfigStore.resolveCwd("", relativeTo: baseCwd), + ProgramaConfigStore.resolveCwd("", relativeTo: baseCwd), baseCwd ) } func testDotCwdReturnsBase() { XCTAssertEqual( - CmuxConfigStore.resolveCwd(".", relativeTo: baseCwd), + ProgramaConfigStore.resolveCwd(".", relativeTo: baseCwd), baseCwd ) } func testAbsolutePathReturnedAsIs() { XCTAssertEqual( - CmuxConfigStore.resolveCwd("/tmp/other", relativeTo: baseCwd), + ProgramaConfigStore.resolveCwd("/tmp/other", relativeTo: baseCwd), "/tmp/other" ) } func testRelativePathJoinedToBase() { XCTAssertEqual( - CmuxConfigStore.resolveCwd("backend/src", relativeTo: baseCwd), + ProgramaConfigStore.resolveCwd("backend/src", relativeTo: baseCwd), "/Users/test/project/backend/src" ) } @@ -629,7 +629,7 @@ final class CmuxConfigCwdResolutionTests: XCTestCase { func testTildeExpandsToHome() { let home = FileManager.default.homeDirectoryForCurrentUser.path XCTAssertEqual( - CmuxConfigStore.resolveCwd("~", relativeTo: baseCwd), + ProgramaConfigStore.resolveCwd("~", relativeTo: baseCwd), home ) } @@ -637,14 +637,14 @@ final class CmuxConfigCwdResolutionTests: XCTestCase { func testTildeSlashExpandsToHomePlusPath() { let home = FileManager.default.homeDirectoryForCurrentUser.path XCTAssertEqual( - CmuxConfigStore.resolveCwd("~/Documents/work", relativeTo: baseCwd), + ProgramaConfigStore.resolveCwd("~/Documents/work", relativeTo: baseCwd), (home as NSString).appendingPathComponent("Documents/work") ) } func testSingleSubdirectory() { XCTAssertEqual( - CmuxConfigStore.resolveCwd("src", relativeTo: baseCwd), + ProgramaConfigStore.resolveCwd("src", relativeTo: baseCwd), "/Users/test/project/src" ) } @@ -652,14 +652,14 @@ final class CmuxConfigCwdResolutionTests: XCTestCase { // MARK: - Layout encoding round-trip -final class CmuxLayoutEncodingTests: XCTestCase { +final class ProgramaLayoutEncodingTests: XCTestCase { func testPaneNodeRoundTrips() throws { - let original = CmuxLayoutNode.pane(CmuxPaneDefinition(surfaces: [ - CmuxSurfaceDefinition(type: .terminal, name: "shell") + let original = ProgramaLayoutNode.pane(ProgramaPaneDefinition(surfaces: [ + ProgramaSurfaceDefinition(type: .terminal, name: "shell") ])) let data = try JSONEncoder().encode(original) - let decoded = try JSONDecoder().decode(CmuxLayoutNode.self, from: data) + let decoded = try JSONDecoder().decode(ProgramaLayoutNode.self, from: data) if case .pane(let pane) = decoded { XCTAssertEqual(pane.surfaces.count, 1) @@ -670,16 +670,16 @@ final class CmuxLayoutEncodingTests: XCTestCase { } func testSplitNodeRoundTrips() throws { - let original = CmuxLayoutNode.split(CmuxSplitDefinition( + let original = ProgramaLayoutNode.split(ProgramaSplitDefinition( direction: .vertical, split: 0.7, children: [ - .pane(CmuxPaneDefinition(surfaces: [CmuxSurfaceDefinition(type: .terminal)])), - .pane(CmuxPaneDefinition(surfaces: [CmuxSurfaceDefinition(type: .browser, url: "http://localhost")])) + .pane(ProgramaPaneDefinition(surfaces: [ProgramaSurfaceDefinition(type: .terminal)])), + .pane(ProgramaPaneDefinition(surfaces: [ProgramaSurfaceDefinition(type: .browser, url: "http://localhost")])) ] )) let data = try JSONEncoder().encode(original) - let decoded = try JSONDecoder().decode(CmuxLayoutNode.self, from: data) + let decoded = try JSONDecoder().decode(ProgramaLayoutNode.self, from: data) if case .split(let split) = decoded { XCTAssertEqual(split.direction, .vertical) diff --git a/cmuxTests/GhosttyConfigTests.swift b/cmuxTests/GhosttyConfigTests.swift index f7fbf34f729..2bcb3fe5781 100644 --- a/cmuxTests/GhosttyConfigTests.swift +++ b/cmuxTests/GhosttyConfigTests.swift @@ -349,7 +349,7 @@ final class GhosttyConfigTests: XCTestCase { ) } - func testCmuxAppSupportConfigURLsUseReleaseConfigForDebugBundleWithoutCurrentConfig() throws { + func testProgramaAppSupportConfigURLsUseReleaseConfigForDebugBundleWithoutCurrentConfig() throws { try withTemporaryAppSupportDirectory { appSupportDirectory in let releaseConfigURL = try writeAppSupportConfig( appSupportDirectory: appSupportDirectory, @@ -368,7 +368,7 @@ final class GhosttyConfigTests: XCTestCase { } } - func testCmuxAppSupportConfigURLsPreferCurrentBundleConfigWhenPresent() throws { + func testProgramaAppSupportConfigURLsPreferCurrentBundleConfigWhenPresent() throws { try withTemporaryAppSupportDirectory { appSupportDirectory in _ = try writeAppSupportConfig( appSupportDirectory: appSupportDirectory, @@ -393,7 +393,7 @@ final class GhosttyConfigTests: XCTestCase { } } - func testCmuxAppSupportConfigURLsSkipReleaseFallbackForNonDebugBundle() throws { + func testProgramaAppSupportConfigURLsSkipReleaseFallbackForNonDebugBundle() throws { try withTemporaryAppSupportDirectory { appSupportDirectory in _ = try writeAppSupportConfig( appSupportDirectory: appSupportDirectory, @@ -411,7 +411,7 @@ final class GhosttyConfigTests: XCTestCase { } } - func testCmuxAppSupportConfigURLsIgnoreMissingOrEmptyFiles() throws { + func testProgramaAppSupportConfigURLsIgnoreMissingOrEmptyFiles() throws { try withTemporaryAppSupportDirectory { appSupportDirectory in _ = try writeAppSupportConfig( appSupportDirectory: appSupportDirectory, @@ -2799,7 +2799,7 @@ final class SidebarBackgroundConfigTests: XCTestCase { } final class ZshShellIntegrationHandoffTests: XCTestCase { - func testGhosttyPromptHooksLoadWhenCmuxRequestsZshIntegration() throws { + func testGhosttyPromptHooksLoadWhenProgramaRequestsZshIntegration() throws { let output = try runInteractiveZsh(cmuxLoadGhosttyIntegration: true) XCTAssertTrue(output.contains("PRECMD=1"), output) @@ -2807,7 +2807,7 @@ final class ZshShellIntegrationHandoffTests: XCTestCase { XCTAssertTrue(output.contains("PRECMDS=_ghostty_precmd"), output) } - func testGhosttyPromptHooksDoNotLoadWithoutCmuxHandoffFlag() throws { + func testGhosttyPromptHooksDoNotLoadWithoutProgramaHandoffFlag() throws { let output = try runInteractiveZsh(cmuxLoadGhosttyIntegration: false) XCTAssertTrue(output.contains("PRECMD=0"), output) @@ -2961,7 +2961,7 @@ final class ZshShellIntegrationHandoffTests: XCTestCase { ) } - func testShellIntegrationPublishesOnlyWorkspaceScopedCmuxEnvironmentToTmuxServerAutomatically() throws { + func testShellIntegrationPublishesOnlyWorkspaceScopedProgramaEnvironmentToTmuxServerAutomatically() throws { let fileManager = FileManager.default let root = fileManager.temporaryDirectory .appendingPathComponent("cmux-zsh-tmux-publish-\(UUID().uuidString)") @@ -3050,7 +3050,7 @@ final class ZshShellIntegrationHandoffTests: XCTestCase { XCTAssertTrue(log.contains("set-environment -gu PROGRAMA_PANEL_ID"), log) } - func testShellIntegrationRefreshesWorkspaceScopedCmuxEnvironmentFromTmuxWithoutOverwritingSurfaceScope() throws { + func testShellIntegrationRefreshesWorkspaceScopedProgramaEnvironmentFromTmuxWithoutOverwritingSurfaceScope() throws { let fileManager = FileManager.default let root = fileManager.temporaryDirectory .appendingPathComponent("cmux-zsh-tmux-refresh-\(UUID().uuidString)") diff --git a/cmuxTests/InactivePaneFirstClickFocusTests.swift b/cmuxTests/InactivePaneFirstClickFocusTests.swift index 34905341739..db275ae3a6e 100644 --- a/cmuxTests/InactivePaneFirstClickFocusTests.swift +++ b/cmuxTests/InactivePaneFirstClickFocusTests.swift @@ -41,7 +41,7 @@ final class InactivePaneFirstClickFocusTests: XCTestCase { func testBrowserViewAcceptsFirstMouseWhenSettingEnabled() { UserDefaults.standard.set(true, forKey: settingsKey) - let view = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let view = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) XCTAssertTrue(view.acceptsFirstMouse(for: nil)) } @@ -49,7 +49,7 @@ final class InactivePaneFirstClickFocusTests: XCTestCase { func testBrowserViewRejectsFirstMouseWhenSettingDisabled() { UserDefaults.standard.set(false, forKey: settingsKey) - let view = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let view = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) XCTAssertFalse(view.acceptsFirstMouse(for: nil)) } diff --git a/cmuxTests/NotificationAndMenuBarTests.swift b/cmuxTests/NotificationAndMenuBarTests.swift index b652189a4c4..e6bd43116f1 100644 --- a/cmuxTests/NotificationAndMenuBarTests.swift +++ b/cmuxTests/NotificationAndMenuBarTests.swift @@ -270,7 +270,7 @@ final class NotificationDockBadgeTests: XCTestCase { XCTAssertTrue(NotificationSoundSettings.isCustomFileSelected(defaults: defaults)) } - func testNotificationCustomStagingPreservesSourceFileWithCmuxPrefix() { + func testNotificationCustomStagingPreservesSourceFileWithProgramaPrefix() { let suiteName = "NotificationDockBadgeTests.\(UUID().uuidString)" guard let defaults = UserDefaults(suiteName: suiteName) else { XCTFail("Failed to create isolated UserDefaults suite") diff --git a/cmuxTests/SocketControlPasswordStoreTests.swift b/cmuxTests/SocketControlPasswordStoreTests.swift index 224eaa35a3a..fd8eba33a53 100644 --- a/cmuxTests/SocketControlPasswordStoreTests.swift +++ b/cmuxTests/SocketControlPasswordStoreTests.swift @@ -174,7 +174,7 @@ final class SocketControlPasswordStoreTests: XCTestCase { XCTAssertEqual(readCount, 1) } - func testDefaultPasswordFileURLUsesCmuxAppSupportPath() throws { + func testDefaultPasswordFileURLUsesProgramaAppSupportPath() throws { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent("cmux-socket-password-tests-\(UUID().uuidString)", isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) @@ -241,7 +241,7 @@ final class SocketControlPasswordStoreTests: XCTestCase { } } -final class CmuxCLIPathInstallerTests: XCTestCase { +final class ProgramaCLIPathInstallerTests: XCTestCase { func testInstallAndUninstallRoundTripWithoutAdministratorPrivileges() throws { let fileManager = FileManager.default let root = fileManager.temporaryDirectory @@ -261,7 +261,7 @@ final class CmuxCLIPathInstallerTests: XCTestCase { var privilegedInstallCallCount = 0 var privilegedUninstallCallCount = 0 - let installer = CmuxCLIPathInstaller( + let installer = ProgramaCLIPathInstaller( fileManager: fileManager, destinationURL: destinationURL, bundledCLIURLProvider: { bundledCLIURL }, @@ -311,7 +311,7 @@ final class CmuxCLIPathInstallerTests: XCTestCase { } var privilegedInstallCallCount = 0 - let installer = CmuxCLIPathInstaller( + let installer = ProgramaCLIPathInstaller( fileManager: fileManager, destinationURL: destinationURL, bundledCLIURLProvider: { bundledCLIURL }, diff --git a/cmuxTests/WindowAndDragTests.swift b/cmuxTests/WindowAndDragTests.swift index 84564a77b3a..fec3014af3b 100644 --- a/cmuxTests/WindowAndDragTests.swift +++ b/cmuxTests/WindowAndDragTests.swift @@ -1129,7 +1129,7 @@ final class FileDropOverlayViewTests: XCTestCase { let anchor = NSView(frame: NSRect(x: 40, y: 36, width: 220, height: 150)) contentView.addSubview(anchor) - let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration()) + let webView = ProgramaWebView(frame: .zero, configuration: WKWebViewConfiguration()) BrowserWindowPortalRegistry.bind(webView: webView, to: anchor, visibleInUI: true) BrowserWindowPortalRegistry.synchronizeForAnchor(anchor) diff --git a/cmuxTests/WorkspaceUnitTests.swift b/cmuxTests/WorkspaceUnitTests.swift index 3f62087ae8b..ccfbfc2dfb3 100644 --- a/cmuxTests/WorkspaceUnitTests.swift +++ b/cmuxTests/WorkspaceUnitTests.swift @@ -1209,7 +1209,7 @@ final class WorkspaceCreationPlacementTests: XCTestCase { title: String, workingDirectory: String?, portOrdinal: Int, - configTemplate: CmuxSurfaceConfigTemplate?, + configTemplate: ProgramaSurfaceConfigTemplate?, initialTerminalCommand: String?, initialTerminalEnvironment: [String: String] ) -> Workspace { @@ -1446,11 +1446,11 @@ final class WorkspaceCreationPlacementTests: XCTestCase { @MainActor final class WorkspaceCreationConfigSanitizationTests: XCTestCase { private final class UnsafeConfigSnapshotTabManager: TabManager { - private var injectedConfig: CmuxSurfaceConfigTemplate? - var capturedConfigTemplate: CmuxSurfaceConfigTemplate? + private var injectedConfig: ProgramaSurfaceConfigTemplate? + var capturedConfigTemplate: ProgramaSurfaceConfigTemplate? func installInjectedConfig(fontSize: Float) { - var config = CmuxSurfaceConfigTemplate() + var config = ProgramaSurfaceConfigTemplate() config.fontSize = fontSize config.workingDirectory = "/tmp/programa-workspace-snapshot" config.command = "echo snapshot" @@ -1460,7 +1460,7 @@ final class WorkspaceCreationConfigSanitizationTests: XCTestCase { override func inheritedTerminalConfigForNewWorkspace( workspace: Workspace? - ) -> CmuxSurfaceConfigTemplate? { + ) -> ProgramaSurfaceConfigTemplate? { injectedConfig ?? super.inheritedTerminalConfigForNewWorkspace(workspace: workspace) } @@ -1468,7 +1468,7 @@ final class WorkspaceCreationConfigSanitizationTests: XCTestCase { title: String, workingDirectory: String?, portOrdinal: Int, - configTemplate: CmuxSurfaceConfigTemplate?, + configTemplate: ProgramaSurfaceConfigTemplate?, initialTerminalCommand: String?, initialTerminalEnvironment: [String: String] ) -> Workspace { From 137a5df5e00c0c67138e2792070c1f7b1f54df49 Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 11:22:20 -0300 Subject: [PATCH 03/23] =?UTF-8?q?refactor:=20rename=20Cmux*/cmux=20source?= =?UTF-8?q?=20files=20=E2=86=92=20Programa*=20(Phase=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git mv the 6 Cmux/cmux-named sources (cmuxApp, CmuxConfig, CmuxConfigExecutor, CmuxDirectoryTrust, Panels/CmuxWebView, CLI/cmux) → Programa*/programa and update every project.pbxproj ref (build file, file reference path, group, sources phase). Builds green with the cmux scheme. --- CLI/{cmux.swift => programa.swift} | 0 GhosttyTabs.xcodeproj/project.pbxproj | 48 +++++++++---------- ...muxWebView.swift => ProgramaWebView.swift} | 0 Sources/{cmuxApp.swift => ProgramaApp.swift} | 0 ...{CmuxConfig.swift => ProgramaConfig.swift} | 0 ...tor.swift => ProgramaConfigExecutor.swift} | 0 ...ust.swift => ProgramaDirectoryTrust.swift} | 0 7 files changed, 24 insertions(+), 24 deletions(-) rename CLI/{cmux.swift => programa.swift} (100%) rename Sources/Panels/{CmuxWebView.swift => ProgramaWebView.swift} (100%) rename Sources/{cmuxApp.swift => ProgramaApp.swift} (100%) rename Sources/{CmuxConfig.swift => ProgramaConfig.swift} (100%) rename Sources/{CmuxConfigExecutor.swift => ProgramaConfigExecutor.swift} (100%) rename Sources/{CmuxDirectoryTrust.swift => ProgramaDirectoryTrust.swift} (100%) diff --git a/CLI/cmux.swift b/CLI/programa.swift similarity index 100% rename from CLI/cmux.swift rename to CLI/programa.swift diff --git a/GhosttyTabs.xcodeproj/project.pbxproj b/GhosttyTabs.xcodeproj/project.pbxproj index 0659f1ba572..7338ab32831 100644 --- a/GhosttyTabs.xcodeproj/project.pbxproj +++ b/GhosttyTabs.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - A5001001 /* cmuxApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001011 /* cmuxApp.swift */; }; + A5001001 /* ProgramaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001011 /* ProgramaApp.swift */; }; A5001002 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001012 /* ContentView.swift */; }; E62155868BB29FEB5DAAAF25 /* SidebarSelectionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD52285508B1D6A9875E7B3 /* SidebarSelectionState.swift */; }; B9000018A1B2C3D4E5F60719 /* WindowDragHandleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000017A1B2C3D4E5F60719 /* WindowDragHandleView.swift */; }; @@ -21,7 +21,7 @@ A5001543 /* TerminalSSHSessionDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001545 /* TerminalSSHSessionDetector.swift */; }; A5001006 /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5001016 /* GhosttyKit.xcframework */; }; A5001007 /* TerminalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001019 /* TerminalController.swift */; }; - A5001500 /* CmuxWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001510 /* CmuxWebView.swift */; }; + A5001500 /* ProgramaWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001510 /* ProgramaWebView.swift */; }; A5001501 /* UITestRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001511 /* UITestRecorder.swift */; }; A5001226 /* SocketControlSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001225 /* SocketControlSettings.swift */; }; A5001601 /* SentryHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001600 /* SentryHelper.swift */; }; @@ -72,13 +72,13 @@ A5001240 /* WindowDecorationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001241 /* WindowDecorationsController.swift */; }; A5001610 /* SessionPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001611 /* SessionPersistence.swift */; }; A5001640 /* RemoteRelayZshBootstrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001641 /* RemoteRelayZshBootstrap.swift */; }; - A5001650 /* CmuxConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001651 /* CmuxConfig.swift */; }; - A5001652 /* CmuxConfigExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001653 /* CmuxConfigExecutor.swift */; }; - A5001654 /* CmuxDirectoryTrust.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001655 /* CmuxDirectoryTrust.swift */; }; + A5001650 /* ProgramaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001651 /* ProgramaConfig.swift */; }; + A5001652 /* ProgramaConfigExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001653 /* ProgramaConfigExecutor.swift */; }; + A5001654 /* ProgramaDirectoryTrust.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001655 /* ProgramaDirectoryTrust.swift */; }; A5001660 /* SurfacePool.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001661 /* SurfacePool.swift */; }; A5001100 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5001101 /* Assets.xcassets */; }; A5001230 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = A5001231 /* Sparkle */; }; - B9000002A1B2C3D4E5F60719 /* cmux.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000001A1B2C3D4E5F60719 /* cmux.swift */; }; + B9000002A1B2C3D4E5F60719 /* programa.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000001A1B2C3D4E5F60719 /* programa.swift */; }; B9000027A1B2C3D4E5F60719 /* RemoteRelayZshBootstrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001641 /* RemoteRelayZshBootstrap.swift */; }; B900000BA1B2C3D4E5F60719 /* cmux in Copy CLI */ = {isa = PBXBuildFile; fileRef = B9000004A1B2C3D4E5F60719 /* cmux */; }; C1ADE00002A1B2C3D4E5F719 /* claude in Copy CLI */ = {isa = PBXBuildFile; fileRef = C1ADE00001A1B2C3D4E5F719 /* claude */; }; @@ -211,7 +211,7 @@ A5001000 /* cmux.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cmux.app; sourceTree = BUILT_PRODUCTS_DIR; }; F1000002A1B2C3D4E5F60718 /* cmuxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = cmuxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 7E7E6EF344A568AC7FEE3715 /* cmuxUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = cmuxUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - A5001011 /* cmuxApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = cmuxApp.swift; sourceTree = ""; }; + A5001011 /* ProgramaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgramaApp.swift; sourceTree = ""; }; A5001012 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 9AD52285508B1D6A9875E7B3 /* SidebarSelectionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSelectionState.swift; sourceTree = ""; }; B9000017A1B2C3D4E5F60719 /* WindowDragHandleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowDragHandleView.swift; sourceTree = ""; }; @@ -230,7 +230,7 @@ A5001600 /* SentryHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryHelper.swift; sourceTree = ""; }; A5001620 /* AppleScriptSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptSupport.swift; sourceTree = ""; }; D1320AA0D1320AA0D1320AA4 /* AppIconDockTilePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconDockTilePlugin.swift; sourceTree = ""; }; - A5001510 /* CmuxWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Panels/CmuxWebView.swift; sourceTree = ""; }; + A5001510 /* ProgramaWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Panels/ProgramaWebView.swift; sourceTree = ""; }; A5001511 /* UITestRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestRecorder.swift; sourceTree = ""; }; A5001520 /* PostHogAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogAnalytics.swift; sourceTree = ""; }; A5001225 /* SocketControlSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketControlSettings.swift; sourceTree = ""; }; @@ -272,9 +272,9 @@ A5001223 /* UpdateLogStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update/UpdateLogStore.swift; sourceTree = ""; }; A5001241 /* WindowDecorationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowDecorationsController.swift; sourceTree = ""; }; A5001611 /* SessionPersistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionPersistence.swift; sourceTree = ""; }; - A5001651 /* CmuxConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmuxConfig.swift; sourceTree = ""; }; - A5001653 /* CmuxConfigExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmuxConfigExecutor.swift; sourceTree = ""; }; - A5001655 /* CmuxDirectoryTrust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmuxDirectoryTrust.swift; sourceTree = ""; }; + A5001651 /* ProgramaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgramaConfig.swift; sourceTree = ""; }; + A5001653 /* ProgramaConfigExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgramaConfigExecutor.swift; sourceTree = ""; }; + A5001655 /* ProgramaDirectoryTrust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgramaDirectoryTrust.swift; sourceTree = ""; }; A5001661 /* SurfacePool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfacePool.swift; sourceTree = ""; }; A5001641 /* RemoteRelayZshBootstrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteRelayZshBootstrap.swift; sourceTree = ""; }; 818DBCD4AB69EB72573E8138 /* SidebarResizeUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarResizeUITests.swift; sourceTree = ""; }; @@ -288,7 +288,7 @@ C1ADE00001A1B2C3D4E5F719 /* claude */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "Resources/bin/claude"; sourceTree = SOURCE_ROOT; }; D1BEF00001A1B2C3D4E5F719 /* open */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "Resources/bin/open"; sourceTree = SOURCE_ROOT; }; A5002001 /* THIRD_PARTY_LICENSES.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = THIRD_PARTY_LICENSES.md; sourceTree = SOURCE_ROOT; }; - B9000001A1B2C3D4E5F60719 /* cmux.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = cmux.swift; sourceTree = ""; }; + B9000001A1B2C3D4E5F60719 /* programa.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = programa.swift; sourceTree = ""; }; B9000004A1B2C3D4E5F60719 /* cmux */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cmux; sourceTree = BUILT_PRODUCTS_DIR; }; D1320AA0D1320AA0D1320AA5 /* CmuxDockTilePlugin.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CmuxDockTilePlugin.plugin; sourceTree = BUILT_PRODUCTS_DIR; }; B9000011A1B2C3D4E5F60719 /* AutomationSocketUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomationSocketUITests.swift; sourceTree = ""; }; @@ -462,7 +462,7 @@ A5001041 /* Sources */ = { isa = PBXGroup; children = ( - A5001011 /* cmuxApp.swift */, + A5001011 /* ProgramaApp.swift */, A5001012 /* ContentView.swift */, 9AD52285508B1D6A9875E7B3 /* SidebarSelectionState.swift */, B9000017A1B2C3D4E5F60719 /* WindowDragHandleView.swift */, @@ -502,7 +502,7 @@ A5007421 /* BrowserPopupWindowController.swift */, A5001418 /* MarkdownPanel.swift */, A5001419 /* MarkdownPanelView.swift */, - A5001510 /* CmuxWebView.swift */, + A5001510 /* ProgramaWebView.swift */, A5001415 /* PanelContentView.swift */, A5001211 /* UpdateController.swift */, A5001212 /* UpdateDelegate.swift */, @@ -521,9 +521,9 @@ A5001222 /* WindowAccessor.swift */, A5001611 /* SessionPersistence.swift */, A5001641 /* RemoteRelayZshBootstrap.swift */, - A5001651 /* CmuxConfig.swift */, - A5001653 /* CmuxConfigExecutor.swift */, - A5001655 /* CmuxDirectoryTrust.swift */, + A5001651 /* ProgramaConfig.swift */, + A5001653 /* ProgramaConfigExecutor.swift */, + A5001655 /* ProgramaDirectoryTrust.swift */, A5001661 /* SurfacePool.swift */, ); path = Sources; @@ -532,7 +532,7 @@ B9000003A1B2C3D4E5F60719 /* CLI */ = { isa = PBXGroup; children = ( - B9000001A1B2C3D4E5F60719 /* cmux.swift */, + B9000001A1B2C3D4E5F60719 /* programa.swift */, ); path = CLI; sourceTree = ""; @@ -774,7 +774,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A5001001 /* cmuxApp.swift in Sources */, + A5001001 /* ProgramaApp.swift in Sources */, A5001002 /* ContentView.swift in Sources */, E62155868BB29FEB5DAAAF25 /* SidebarSelectionState.swift in Sources */, B9000018A1B2C3D4E5F60719 /* WindowDragHandleView.swift in Sources */, @@ -813,7 +813,7 @@ A5007420 /* BrowserPopupWindowController.swift in Sources */, A5001420 /* MarkdownPanel.swift in Sources */, A5001421 /* MarkdownPanelView.swift in Sources */, - A5001500 /* CmuxWebView.swift in Sources */, + A5001500 /* ProgramaWebView.swift in Sources */, A5001405 /* PanelContentView.swift in Sources */, A5001201 /* UpdateController.swift in Sources */, A5001202 /* UpdateDelegate.swift in Sources */, @@ -832,9 +832,9 @@ A500120C /* WindowAccessor.swift in Sources */, A5001610 /* SessionPersistence.swift in Sources */, A5001640 /* RemoteRelayZshBootstrap.swift in Sources */, - A5001650 /* CmuxConfig.swift in Sources */, - A5001652 /* CmuxConfigExecutor.swift in Sources */, - A5001654 /* CmuxDirectoryTrust.swift in Sources */, + A5001650 /* ProgramaConfig.swift in Sources */, + A5001652 /* ProgramaConfigExecutor.swift in Sources */, + A5001654 /* ProgramaDirectoryTrust.swift in Sources */, A5001660 /* SurfacePool.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -914,7 +914,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - B9000002A1B2C3D4E5F60719 /* cmux.swift in Sources */, + B9000002A1B2C3D4E5F60719 /* programa.swift in Sources */, B9000027A1B2C3D4E5F60719 /* RemoteRelayZshBootstrap.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Sources/Panels/CmuxWebView.swift b/Sources/Panels/ProgramaWebView.swift similarity index 100% rename from Sources/Panels/CmuxWebView.swift rename to Sources/Panels/ProgramaWebView.swift diff --git a/Sources/cmuxApp.swift b/Sources/ProgramaApp.swift similarity index 100% rename from Sources/cmuxApp.swift rename to Sources/ProgramaApp.swift diff --git a/Sources/CmuxConfig.swift b/Sources/ProgramaConfig.swift similarity index 100% rename from Sources/CmuxConfig.swift rename to Sources/ProgramaConfig.swift diff --git a/Sources/CmuxConfigExecutor.swift b/Sources/ProgramaConfigExecutor.swift similarity index 100% rename from Sources/CmuxConfigExecutor.swift rename to Sources/ProgramaConfigExecutor.swift diff --git a/Sources/CmuxDirectoryTrust.swift b/Sources/ProgramaDirectoryTrust.swift similarity index 100% rename from Sources/CmuxDirectoryTrust.swift rename to Sources/ProgramaDirectoryTrust.swift From e64ac3041c0fdc1e6d5100dd82a68234db991aaa Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 11:34:58 -0300 Subject: [PATCH 04/23] =?UTF-8?q?refactor:=20rename=20test=20targets=20cmu?= =?UTF-8?q?xTests/cmuxUITests=20=E2=86=92=20programaTests/programaUITests?= =?UTF-8?q?=20(Phase=203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git mv both test dirs + CmuxConfigTests.swift → ProgramaConfigTests.swift; update project.pbxproj (targets, products, group paths, config-list names) and the three schemes' test-target labels. App product label (cmux.app) and scheme filenames remain for Phase 4. build-for-testing (cmux-ci) green; tests not run locally per policy. --- GhosttyTabs.xcodeproj/project.pbxproj | 56 +++++++++---------- .../xcshareddata/xcschemes/cmux-ci.xcscheme | 6 +- .../xcshareddata/xcschemes/cmux-unit.xcscheme | 4 +- .../xcshareddata/xcschemes/cmux.xcscheme | 2 +- .../AppDelegateShortcutRoutingTests.swift | 0 .../BrowserConfigTests.swift | 0 .../BrowserFindJavaScriptTests.swift | 0 .../BrowserImportMappingTests.swift | 0 .../BrowserPanelTests.swift | 0 .../CJKIMEInputTests.swift | 0 .../CommandPaletteSearchEngineTests.swift | 0 .../GhosttyConfigTests.swift | 0 ...sttyEnsureFocusWindowActivationTests.swift | 0 .../InactivePaneFirstClickFocusTests.swift | 0 .../NotificationAndMenuBarTests.swift | 0 .../OmnibarAndToolsTests.swift | 0 .../ProgramaConfigTests.swift | 0 .../ServeWebPortStoreTests.swift | 0 .../SessionPersistenceTests.swift | 0 .../ShortcutAndCommandPaletteTests.swift | 0 .../SidebarMarkdownRendererTests.swift | 0 .../SidebarOrderingTests.swift | 0 .../SidebarWidthPolicyTests.swift | 0 .../SocketControlPasswordStoreTests.swift | 0 .../TabManagerSessionSnapshotTests.swift | 0 .../TabManagerUnitTests.swift | 0 .../TerminalAndGhosttyTests.swift | 0 ...rminalControllerShellStateDedupTests.swift | 0 ...erminalControllerSocketSecurityTests.swift | 0 .../UpdatePillReleaseVisibilityTests.swift | 0 .../WindowAndDragTests.swift | 0 .../WorkspaceContentViewVisibilityTests.swift | 0 .../WorkspaceManualUnreadTests.swift | 0 .../WorkspacePullRequestSidebarTests.swift | 0 .../WorkspaceRemoteConnectionTests.swift | 0 .../WorkspaceStressProfileTests.swift | 0 .../WorkspaceUnitTests.swift | 0 .../AutomationSocketUITests.swift | 0 .../BonsplitTabDragUITests.swift | 0 .../BrowserImportProfilesUITests.swift | 0 .../BrowserOmnibarSuggestionsUITests.swift | 0 .../BrowserPaneNavigationKeybindUITests.swift | 0 .../CloseWindowConfirmDialogUITests.swift | 0 .../CloseWorkspaceCmdDUITests.swift | 0 .../CloseWorkspaceConfirmDialogUITests.swift | 0 .../CloseWorkspacesConfirmDialogUITests.swift | 0 .../DisplayResolutionRegressionUITests.swift | 0 .../JumpToUnreadUITests.swift | 0 .../MenuKeyEquivalentRoutingUITests.swift | 0 .../MultiWindowNotificationsUITests.swift | 0 .../SidebarHelpMenuUITests.swift | 0 .../SidebarResizeUITests.swift | 0 .../TerminalCmdClickUITests.swift | 0 .../UpdatePillUITests.swift | 0 .../WorkspaceDescriptionUITests.swift | 0 55 files changed, 34 insertions(+), 34 deletions(-) rename {cmuxTests => programaTests}/AppDelegateShortcutRoutingTests.swift (100%) rename {cmuxTests => programaTests}/BrowserConfigTests.swift (100%) rename {cmuxTests => programaTests}/BrowserFindJavaScriptTests.swift (100%) rename {cmuxTests => programaTests}/BrowserImportMappingTests.swift (100%) rename {cmuxTests => programaTests}/BrowserPanelTests.swift (100%) rename {cmuxTests => programaTests}/CJKIMEInputTests.swift (100%) rename {cmuxTests => programaTests}/CommandPaletteSearchEngineTests.swift (100%) rename {cmuxTests => programaTests}/GhosttyConfigTests.swift (100%) rename {cmuxTests => programaTests}/GhosttyEnsureFocusWindowActivationTests.swift (100%) rename {cmuxTests => programaTests}/InactivePaneFirstClickFocusTests.swift (100%) rename {cmuxTests => programaTests}/NotificationAndMenuBarTests.swift (100%) rename {cmuxTests => programaTests}/OmnibarAndToolsTests.swift (100%) rename cmuxTests/CmuxConfigTests.swift => programaTests/ProgramaConfigTests.swift (100%) rename {cmuxTests => programaTests}/ServeWebPortStoreTests.swift (100%) rename {cmuxTests => programaTests}/SessionPersistenceTests.swift (100%) rename {cmuxTests => programaTests}/ShortcutAndCommandPaletteTests.swift (100%) rename {cmuxTests => programaTests}/SidebarMarkdownRendererTests.swift (100%) rename {cmuxTests => programaTests}/SidebarOrderingTests.swift (100%) rename {cmuxTests => programaTests}/SidebarWidthPolicyTests.swift (100%) rename {cmuxTests => programaTests}/SocketControlPasswordStoreTests.swift (100%) rename {cmuxTests => programaTests}/TabManagerSessionSnapshotTests.swift (100%) rename {cmuxTests => programaTests}/TabManagerUnitTests.swift (100%) rename {cmuxTests => programaTests}/TerminalAndGhosttyTests.swift (100%) rename {cmuxTests => programaTests}/TerminalControllerShellStateDedupTests.swift (100%) rename {cmuxTests => programaTests}/TerminalControllerSocketSecurityTests.swift (100%) rename {cmuxTests => programaTests}/UpdatePillReleaseVisibilityTests.swift (100%) rename {cmuxTests => programaTests}/WindowAndDragTests.swift (100%) rename {cmuxTests => programaTests}/WorkspaceContentViewVisibilityTests.swift (100%) rename {cmuxTests => programaTests}/WorkspaceManualUnreadTests.swift (100%) rename {cmuxTests => programaTests}/WorkspacePullRequestSidebarTests.swift (100%) rename {cmuxTests => programaTests}/WorkspaceRemoteConnectionTests.swift (100%) rename {cmuxTests => programaTests}/WorkspaceStressProfileTests.swift (100%) rename {cmuxTests => programaTests}/WorkspaceUnitTests.swift (100%) rename {cmuxUITests => programaUITests}/AutomationSocketUITests.swift (100%) rename {cmuxUITests => programaUITests}/BonsplitTabDragUITests.swift (100%) rename {cmuxUITests => programaUITests}/BrowserImportProfilesUITests.swift (100%) rename {cmuxUITests => programaUITests}/BrowserOmnibarSuggestionsUITests.swift (100%) rename {cmuxUITests => programaUITests}/BrowserPaneNavigationKeybindUITests.swift (100%) rename {cmuxUITests => programaUITests}/CloseWindowConfirmDialogUITests.swift (100%) rename {cmuxUITests => programaUITests}/CloseWorkspaceCmdDUITests.swift (100%) rename {cmuxUITests => programaUITests}/CloseWorkspaceConfirmDialogUITests.swift (100%) rename {cmuxUITests => programaUITests}/CloseWorkspacesConfirmDialogUITests.swift (100%) rename {cmuxUITests => programaUITests}/DisplayResolutionRegressionUITests.swift (100%) rename {cmuxUITests => programaUITests}/JumpToUnreadUITests.swift (100%) rename {cmuxUITests => programaUITests}/MenuKeyEquivalentRoutingUITests.swift (100%) rename {cmuxUITests => programaUITests}/MultiWindowNotificationsUITests.swift (100%) rename {cmuxUITests => programaUITests}/SidebarHelpMenuUITests.swift (100%) rename {cmuxUITests => programaUITests}/SidebarResizeUITests.swift (100%) rename {cmuxUITests => programaUITests}/TerminalCmdClickUITests.swift (100%) rename {cmuxUITests => programaUITests}/UpdatePillUITests.swift (100%) rename {cmuxUITests => programaUITests}/WorkspaceDescriptionUITests.swift (100%) diff --git a/GhosttyTabs.xcodeproj/project.pbxproj b/GhosttyTabs.xcodeproj/project.pbxproj index 7338ab32831..841b13d3a0f 100644 --- a/GhosttyTabs.xcodeproj/project.pbxproj +++ b/GhosttyTabs.xcodeproj/project.pbxproj @@ -135,7 +135,7 @@ 8C4BBF2DEF6DF93F395A9EE7 /* TerminalControllerSocketSecurityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491751CE2321474474F27DCF /* TerminalControllerSocketSecurityTests.swift */; }; C1A2B3C4D5E6F70800000004 /* TerminalControllerShellStateDedupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A2B3C4D5E6F70800000003 /* TerminalControllerShellStateDedupTests.swift */; }; 2BB56A710BB1FC50367E5BCF /* TabManagerSessionSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10D684CFFB8CDEF89CE2D9E1 /* TabManagerSessionSnapshotTests.swift */; }; - C1A2B3C4D5E6F70800000001 /* CmuxConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A2B3C4D5E6F70800000002 /* CmuxConfigTests.swift */; }; + C1A2B3C4D5E6F70800000001 /* ProgramaConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A2B3C4D5E6F70800000002 /* ProgramaConfigTests.swift */; }; C1A2B3C4D5E6F70800000006 /* ServeWebPortStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A2B3C4D5E6F70800000005 /* ServeWebPortStoreTests.swift */; }; /* End PBXBuildFile section */ @@ -209,8 +209,8 @@ /* Begin PBXFileReference section */ A5001000 /* cmux.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cmux.app; sourceTree = BUILT_PRODUCTS_DIR; }; - F1000002A1B2C3D4E5F60718 /* cmuxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = cmuxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 7E7E6EF344A568AC7FEE3715 /* cmuxUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = cmuxUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F1000002A1B2C3D4E5F60718 /* programaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = programaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 7E7E6EF344A568AC7FEE3715 /* programaUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = programaUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A5001011 /* ProgramaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgramaApp.swift; sourceTree = ""; }; A5001012 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 9AD52285508B1D6A9875E7B3 /* SidebarSelectionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSelectionState.swift; sourceTree = ""; }; @@ -336,7 +336,7 @@ 491751CE2321474474F27DCF /* TerminalControllerSocketSecurityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalControllerSocketSecurityTests.swift; sourceTree = ""; }; C1A2B3C4D5E6F70800000003 /* TerminalControllerShellStateDedupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalControllerShellStateDedupTests.swift; sourceTree = ""; }; 10D684CFFB8CDEF89CE2D9E1 /* TabManagerSessionSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabManagerSessionSnapshotTests.swift; sourceTree = ""; }; - C1A2B3C4D5E6F70800000002 /* CmuxConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmuxConfigTests.swift; sourceTree = ""; }; + C1A2B3C4D5E6F70800000002 /* ProgramaConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgramaConfigTests.swift; sourceTree = ""; }; C1A2B3C4D5E6F70800000005 /* ServeWebPortStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServeWebPortStoreTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -453,8 +453,8 @@ A5001016 /* GhosttyKit.xcframework */, A5001017 /* ghostty.h */, A5001018 /* cmux-Bridging-Header.h */, - 3196C9C2D01F054C1D3385DD /* cmuxUITests */, - F1000003A1B2C3D4E5F60718 /* cmuxTests */, + 3196C9C2D01F054C1D3385DD /* programaUITests */, + F1000003A1B2C3D4E5F60718 /* programaTests */, A5001042 /* Products */, ); sourceTree = ""; @@ -556,13 +556,13 @@ A5001000 /* cmux.app */, B9000004A1B2C3D4E5F60719 /* cmux */, D1320AA0D1320AA0D1320AA5 /* CmuxDockTilePlugin.plugin */, - 7E7E6EF344A568AC7FEE3715 /* cmuxUITests.xctest */, - F1000002A1B2C3D4E5F60718 /* cmuxTests.xctest */, + 7E7E6EF344A568AC7FEE3715 /* programaUITests.xctest */, + F1000002A1B2C3D4E5F60718 /* programaTests.xctest */, ); name = Products; sourceTree = ""; }; - 3196C9C2D01F054C1D3385DD /* cmuxUITests */ = { + 3196C9C2D01F054C1D3385DD /* programaUITests */ = { isa = PBXGroup; children = ( B9000011A1B2C3D4E5F60719 /* AutomationSocketUITests.swift */, @@ -583,10 +583,10 @@ E1000001A1B2C3D4E5F60718 /* MenuKeyEquivalentRoutingUITests.swift */, AA1B2C3D4E5F60719 /* BonsplitTabDragUITests.swift */, ); - path = cmuxUITests; + path = programaUITests; sourceTree = ""; }; - F1000003A1B2C3D4E5F60718 /* cmuxTests */ = { + F1000003A1B2C3D4E5F60718 /* programaTests */ = { isa = PBXGroup; children = ( F2000001A1B2C3D4E5F60718 /* UpdatePillReleaseVisibilityTests.swift */, @@ -619,10 +619,10 @@ 491751CE2321474474F27DCF /* TerminalControllerSocketSecurityTests.swift */, C1A2B3C4D5E6F70800000003 /* TerminalControllerShellStateDedupTests.swift */, 10D684CFFB8CDEF89CE2D9E1 /* TabManagerSessionSnapshotTests.swift */, - C1A2B3C4D5E6F70800000002 /* CmuxConfigTests.swift */, + C1A2B3C4D5E6F70800000002 /* ProgramaConfigTests.swift */, C1A2B3C4D5E6F70800000005 /* ServeWebPortStoreTests.swift */, ); - path = cmuxTests; + path = programaTests; sourceTree = ""; }; /* End PBXGroup section */ @@ -694,9 +694,9 @@ productReference = B9000004A1B2C3D4E5F60719 /* cmux */; productType = "com.apple.product-type.tool"; }; - CB450DF0F0B3839599082C4D /* cmuxUITests */ = { + CB450DF0F0B3839599082C4D /* programaUITests */ = { isa = PBXNativeTarget; - buildConfigurationList = AD2C7ED08993D3CD4910A1FF /* Build configuration list for PBXNativeTarget "cmuxUITests" */; + buildConfigurationList = AD2C7ED08993D3CD4910A1FF /* Build configuration list for PBXNativeTarget "programaUITests" */; buildPhases = ( E436EF0BA8EC9E6721A42F79 /* Sources */, AB408954939A11B8A87BB5DE /* Frameworks */, @@ -707,14 +707,14 @@ dependencies = ( 32568B0DCFC8656BA952468E /* PBXTargetDependency */, ); - name = cmuxUITests; - productName = cmuxUITests; - productReference = 7E7E6EF344A568AC7FEE3715 /* cmuxUITests.xctest */; + name = programaUITests; + productName = programaUITests; + productReference = 7E7E6EF344A568AC7FEE3715 /* programaUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; - F1000004A1B2C3D4E5F60718 /* cmuxTests */ = { + F1000004A1B2C3D4E5F60718 /* programaTests */ = { isa = PBXNativeTarget; - buildConfigurationList = F1000010A1B2C3D4E5F60718 /* Build configuration list for PBXNativeTarget "cmuxTests" */; + buildConfigurationList = F1000010A1B2C3D4E5F60718 /* Build configuration list for PBXNativeTarget "programaTests" */; buildPhases = ( F1000005A1B2C3D4E5F60718 /* Sources */, F1000006A1B2C3D4E5F60718 /* Frameworks */, @@ -725,9 +725,9 @@ dependencies = ( F1000009A1B2C3D4E5F60718 /* PBXTargetDependency */, ); - name = cmuxTests; - productName = cmuxTests; - productReference = F1000002A1B2C3D4E5F60718 /* cmuxTests.xctest */; + name = programaTests; + productName = programaTests; + productReference = F1000002A1B2C3D4E5F60718 /* programaTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -763,8 +763,8 @@ A5001050 /* GhosttyTabs */, D1320AA0D1320AA0D1320AA8 /* CmuxDockTilePlugin */, B9000005A1B2C3D4E5F60719 /* cmux-cli */, - CB450DF0F0B3839599082C4D /* cmuxUITests */, - F1000004A1B2C3D4E5F60718 /* cmuxTests */, + CB450DF0F0B3839599082C4D /* programaUITests */, + F1000004A1B2C3D4E5F60718 /* programaTests */, ); }; /* End PBXProject section */ @@ -905,7 +905,7 @@ 8C4BBF2DEF6DF93F395A9EE7 /* TerminalControllerSocketSecurityTests.swift in Sources */, C1A2B3C4D5E6F70800000004 /* TerminalControllerShellStateDedupTests.swift in Sources */, 2BB56A710BB1FC50367E5BCF /* TabManagerSessionSnapshotTests.swift in Sources */, - C1A2B3C4D5E6F70800000001 /* CmuxConfigTests.swift in Sources */, + C1A2B3C4D5E6F70800000001 /* ProgramaConfigTests.swift in Sources */, C1A2B3C4D5E6F70800000006 /* ServeWebPortStoreTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1332,7 +1332,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - AD2C7ED08993D3CD4910A1FF /* Build configuration list for PBXNativeTarget "cmuxUITests" */ = { + AD2C7ED08993D3CD4910A1FF /* Build configuration list for PBXNativeTarget "programaUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( C117776A77E71D1432F570D7 /* Debug */, @@ -1341,7 +1341,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F1000010A1B2C3D4E5F60718 /* Build configuration list for PBXNativeTarget "cmuxTests" */ = { + F1000010A1B2C3D4E5F60718 /* Build configuration list for PBXNativeTarget "programaTests" */ = { isa = XCConfigurationList; buildConfigurations = ( F1000011A1B2C3D4E5F60718 /* Debug */, diff --git a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-ci.xcscheme b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-ci.xcscheme index 415b3867a54..a697e241fb5 100644 --- a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-ci.xcscheme +++ b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-ci.xcscheme @@ -6,17 +6,17 @@ - + - + - + diff --git a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-unit.xcscheme b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-unit.xcscheme index 965b79c1e33..b952ce48c33 100644 --- a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-unit.xcscheme +++ b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-unit.xcscheme @@ -6,14 +6,14 @@ - + - + diff --git a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux.xcscheme b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux.xcscheme index c8f698bcb82..97917a3282d 100644 --- a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux.xcscheme +++ b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux.xcscheme @@ -10,7 +10,7 @@ - + diff --git a/cmuxTests/AppDelegateShortcutRoutingTests.swift b/programaTests/AppDelegateShortcutRoutingTests.swift similarity index 100% rename from cmuxTests/AppDelegateShortcutRoutingTests.swift rename to programaTests/AppDelegateShortcutRoutingTests.swift diff --git a/cmuxTests/BrowserConfigTests.swift b/programaTests/BrowserConfigTests.swift similarity index 100% rename from cmuxTests/BrowserConfigTests.swift rename to programaTests/BrowserConfigTests.swift diff --git a/cmuxTests/BrowserFindJavaScriptTests.swift b/programaTests/BrowserFindJavaScriptTests.swift similarity index 100% rename from cmuxTests/BrowserFindJavaScriptTests.swift rename to programaTests/BrowserFindJavaScriptTests.swift diff --git a/cmuxTests/BrowserImportMappingTests.swift b/programaTests/BrowserImportMappingTests.swift similarity index 100% rename from cmuxTests/BrowserImportMappingTests.swift rename to programaTests/BrowserImportMappingTests.swift diff --git a/cmuxTests/BrowserPanelTests.swift b/programaTests/BrowserPanelTests.swift similarity index 100% rename from cmuxTests/BrowserPanelTests.swift rename to programaTests/BrowserPanelTests.swift diff --git a/cmuxTests/CJKIMEInputTests.swift b/programaTests/CJKIMEInputTests.swift similarity index 100% rename from cmuxTests/CJKIMEInputTests.swift rename to programaTests/CJKIMEInputTests.swift diff --git a/cmuxTests/CommandPaletteSearchEngineTests.swift b/programaTests/CommandPaletteSearchEngineTests.swift similarity index 100% rename from cmuxTests/CommandPaletteSearchEngineTests.swift rename to programaTests/CommandPaletteSearchEngineTests.swift diff --git a/cmuxTests/GhosttyConfigTests.swift b/programaTests/GhosttyConfigTests.swift similarity index 100% rename from cmuxTests/GhosttyConfigTests.swift rename to programaTests/GhosttyConfigTests.swift diff --git a/cmuxTests/GhosttyEnsureFocusWindowActivationTests.swift b/programaTests/GhosttyEnsureFocusWindowActivationTests.swift similarity index 100% rename from cmuxTests/GhosttyEnsureFocusWindowActivationTests.swift rename to programaTests/GhosttyEnsureFocusWindowActivationTests.swift diff --git a/cmuxTests/InactivePaneFirstClickFocusTests.swift b/programaTests/InactivePaneFirstClickFocusTests.swift similarity index 100% rename from cmuxTests/InactivePaneFirstClickFocusTests.swift rename to programaTests/InactivePaneFirstClickFocusTests.swift diff --git a/cmuxTests/NotificationAndMenuBarTests.swift b/programaTests/NotificationAndMenuBarTests.swift similarity index 100% rename from cmuxTests/NotificationAndMenuBarTests.swift rename to programaTests/NotificationAndMenuBarTests.swift diff --git a/cmuxTests/OmnibarAndToolsTests.swift b/programaTests/OmnibarAndToolsTests.swift similarity index 100% rename from cmuxTests/OmnibarAndToolsTests.swift rename to programaTests/OmnibarAndToolsTests.swift diff --git a/cmuxTests/CmuxConfigTests.swift b/programaTests/ProgramaConfigTests.swift similarity index 100% rename from cmuxTests/CmuxConfigTests.swift rename to programaTests/ProgramaConfigTests.swift diff --git a/cmuxTests/ServeWebPortStoreTests.swift b/programaTests/ServeWebPortStoreTests.swift similarity index 100% rename from cmuxTests/ServeWebPortStoreTests.swift rename to programaTests/ServeWebPortStoreTests.swift diff --git a/cmuxTests/SessionPersistenceTests.swift b/programaTests/SessionPersistenceTests.swift similarity index 100% rename from cmuxTests/SessionPersistenceTests.swift rename to programaTests/SessionPersistenceTests.swift diff --git a/cmuxTests/ShortcutAndCommandPaletteTests.swift b/programaTests/ShortcutAndCommandPaletteTests.swift similarity index 100% rename from cmuxTests/ShortcutAndCommandPaletteTests.swift rename to programaTests/ShortcutAndCommandPaletteTests.swift diff --git a/cmuxTests/SidebarMarkdownRendererTests.swift b/programaTests/SidebarMarkdownRendererTests.swift similarity index 100% rename from cmuxTests/SidebarMarkdownRendererTests.swift rename to programaTests/SidebarMarkdownRendererTests.swift diff --git a/cmuxTests/SidebarOrderingTests.swift b/programaTests/SidebarOrderingTests.swift similarity index 100% rename from cmuxTests/SidebarOrderingTests.swift rename to programaTests/SidebarOrderingTests.swift diff --git a/cmuxTests/SidebarWidthPolicyTests.swift b/programaTests/SidebarWidthPolicyTests.swift similarity index 100% rename from cmuxTests/SidebarWidthPolicyTests.swift rename to programaTests/SidebarWidthPolicyTests.swift diff --git a/cmuxTests/SocketControlPasswordStoreTests.swift b/programaTests/SocketControlPasswordStoreTests.swift similarity index 100% rename from cmuxTests/SocketControlPasswordStoreTests.swift rename to programaTests/SocketControlPasswordStoreTests.swift diff --git a/cmuxTests/TabManagerSessionSnapshotTests.swift b/programaTests/TabManagerSessionSnapshotTests.swift similarity index 100% rename from cmuxTests/TabManagerSessionSnapshotTests.swift rename to programaTests/TabManagerSessionSnapshotTests.swift diff --git a/cmuxTests/TabManagerUnitTests.swift b/programaTests/TabManagerUnitTests.swift similarity index 100% rename from cmuxTests/TabManagerUnitTests.swift rename to programaTests/TabManagerUnitTests.swift diff --git a/cmuxTests/TerminalAndGhosttyTests.swift b/programaTests/TerminalAndGhosttyTests.swift similarity index 100% rename from cmuxTests/TerminalAndGhosttyTests.swift rename to programaTests/TerminalAndGhosttyTests.swift diff --git a/cmuxTests/TerminalControllerShellStateDedupTests.swift b/programaTests/TerminalControllerShellStateDedupTests.swift similarity index 100% rename from cmuxTests/TerminalControllerShellStateDedupTests.swift rename to programaTests/TerminalControllerShellStateDedupTests.swift diff --git a/cmuxTests/TerminalControllerSocketSecurityTests.swift b/programaTests/TerminalControllerSocketSecurityTests.swift similarity index 100% rename from cmuxTests/TerminalControllerSocketSecurityTests.swift rename to programaTests/TerminalControllerSocketSecurityTests.swift diff --git a/cmuxTests/UpdatePillReleaseVisibilityTests.swift b/programaTests/UpdatePillReleaseVisibilityTests.swift similarity index 100% rename from cmuxTests/UpdatePillReleaseVisibilityTests.swift rename to programaTests/UpdatePillReleaseVisibilityTests.swift diff --git a/cmuxTests/WindowAndDragTests.swift b/programaTests/WindowAndDragTests.swift similarity index 100% rename from cmuxTests/WindowAndDragTests.swift rename to programaTests/WindowAndDragTests.swift diff --git a/cmuxTests/WorkspaceContentViewVisibilityTests.swift b/programaTests/WorkspaceContentViewVisibilityTests.swift similarity index 100% rename from cmuxTests/WorkspaceContentViewVisibilityTests.swift rename to programaTests/WorkspaceContentViewVisibilityTests.swift diff --git a/cmuxTests/WorkspaceManualUnreadTests.swift b/programaTests/WorkspaceManualUnreadTests.swift similarity index 100% rename from cmuxTests/WorkspaceManualUnreadTests.swift rename to programaTests/WorkspaceManualUnreadTests.swift diff --git a/cmuxTests/WorkspacePullRequestSidebarTests.swift b/programaTests/WorkspacePullRequestSidebarTests.swift similarity index 100% rename from cmuxTests/WorkspacePullRequestSidebarTests.swift rename to programaTests/WorkspacePullRequestSidebarTests.swift diff --git a/cmuxTests/WorkspaceRemoteConnectionTests.swift b/programaTests/WorkspaceRemoteConnectionTests.swift similarity index 100% rename from cmuxTests/WorkspaceRemoteConnectionTests.swift rename to programaTests/WorkspaceRemoteConnectionTests.swift diff --git a/cmuxTests/WorkspaceStressProfileTests.swift b/programaTests/WorkspaceStressProfileTests.swift similarity index 100% rename from cmuxTests/WorkspaceStressProfileTests.swift rename to programaTests/WorkspaceStressProfileTests.swift diff --git a/cmuxTests/WorkspaceUnitTests.swift b/programaTests/WorkspaceUnitTests.swift similarity index 100% rename from cmuxTests/WorkspaceUnitTests.swift rename to programaTests/WorkspaceUnitTests.swift diff --git a/cmuxUITests/AutomationSocketUITests.swift b/programaUITests/AutomationSocketUITests.swift similarity index 100% rename from cmuxUITests/AutomationSocketUITests.swift rename to programaUITests/AutomationSocketUITests.swift diff --git a/cmuxUITests/BonsplitTabDragUITests.swift b/programaUITests/BonsplitTabDragUITests.swift similarity index 100% rename from cmuxUITests/BonsplitTabDragUITests.swift rename to programaUITests/BonsplitTabDragUITests.swift diff --git a/cmuxUITests/BrowserImportProfilesUITests.swift b/programaUITests/BrowserImportProfilesUITests.swift similarity index 100% rename from cmuxUITests/BrowserImportProfilesUITests.swift rename to programaUITests/BrowserImportProfilesUITests.swift diff --git a/cmuxUITests/BrowserOmnibarSuggestionsUITests.swift b/programaUITests/BrowserOmnibarSuggestionsUITests.swift similarity index 100% rename from cmuxUITests/BrowserOmnibarSuggestionsUITests.swift rename to programaUITests/BrowserOmnibarSuggestionsUITests.swift diff --git a/cmuxUITests/BrowserPaneNavigationKeybindUITests.swift b/programaUITests/BrowserPaneNavigationKeybindUITests.swift similarity index 100% rename from cmuxUITests/BrowserPaneNavigationKeybindUITests.swift rename to programaUITests/BrowserPaneNavigationKeybindUITests.swift diff --git a/cmuxUITests/CloseWindowConfirmDialogUITests.swift b/programaUITests/CloseWindowConfirmDialogUITests.swift similarity index 100% rename from cmuxUITests/CloseWindowConfirmDialogUITests.swift rename to programaUITests/CloseWindowConfirmDialogUITests.swift diff --git a/cmuxUITests/CloseWorkspaceCmdDUITests.swift b/programaUITests/CloseWorkspaceCmdDUITests.swift similarity index 100% rename from cmuxUITests/CloseWorkspaceCmdDUITests.swift rename to programaUITests/CloseWorkspaceCmdDUITests.swift diff --git a/cmuxUITests/CloseWorkspaceConfirmDialogUITests.swift b/programaUITests/CloseWorkspaceConfirmDialogUITests.swift similarity index 100% rename from cmuxUITests/CloseWorkspaceConfirmDialogUITests.swift rename to programaUITests/CloseWorkspaceConfirmDialogUITests.swift diff --git a/cmuxUITests/CloseWorkspacesConfirmDialogUITests.swift b/programaUITests/CloseWorkspacesConfirmDialogUITests.swift similarity index 100% rename from cmuxUITests/CloseWorkspacesConfirmDialogUITests.swift rename to programaUITests/CloseWorkspacesConfirmDialogUITests.swift diff --git a/cmuxUITests/DisplayResolutionRegressionUITests.swift b/programaUITests/DisplayResolutionRegressionUITests.swift similarity index 100% rename from cmuxUITests/DisplayResolutionRegressionUITests.swift rename to programaUITests/DisplayResolutionRegressionUITests.swift diff --git a/cmuxUITests/JumpToUnreadUITests.swift b/programaUITests/JumpToUnreadUITests.swift similarity index 100% rename from cmuxUITests/JumpToUnreadUITests.swift rename to programaUITests/JumpToUnreadUITests.swift diff --git a/cmuxUITests/MenuKeyEquivalentRoutingUITests.swift b/programaUITests/MenuKeyEquivalentRoutingUITests.swift similarity index 100% rename from cmuxUITests/MenuKeyEquivalentRoutingUITests.swift rename to programaUITests/MenuKeyEquivalentRoutingUITests.swift diff --git a/cmuxUITests/MultiWindowNotificationsUITests.swift b/programaUITests/MultiWindowNotificationsUITests.swift similarity index 100% rename from cmuxUITests/MultiWindowNotificationsUITests.swift rename to programaUITests/MultiWindowNotificationsUITests.swift diff --git a/cmuxUITests/SidebarHelpMenuUITests.swift b/programaUITests/SidebarHelpMenuUITests.swift similarity index 100% rename from cmuxUITests/SidebarHelpMenuUITests.swift rename to programaUITests/SidebarHelpMenuUITests.swift diff --git a/cmuxUITests/SidebarResizeUITests.swift b/programaUITests/SidebarResizeUITests.swift similarity index 100% rename from cmuxUITests/SidebarResizeUITests.swift rename to programaUITests/SidebarResizeUITests.swift diff --git a/cmuxUITests/TerminalCmdClickUITests.swift b/programaUITests/TerminalCmdClickUITests.swift similarity index 100% rename from cmuxUITests/TerminalCmdClickUITests.swift rename to programaUITests/TerminalCmdClickUITests.swift diff --git a/cmuxUITests/UpdatePillUITests.swift b/programaUITests/UpdatePillUITests.swift similarity index 100% rename from cmuxUITests/UpdatePillUITests.swift rename to programaUITests/UpdatePillUITests.swift diff --git a/cmuxUITests/WorkspaceDescriptionUITests.swift b/programaUITests/WorkspaceDescriptionUITests.swift similarity index 100% rename from cmuxUITests/WorkspaceDescriptionUITests.swift rename to programaUITests/WorkspaceDescriptionUITests.swift From 97b358c1f6434db54f65b0a0fe6f36178708548c Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 11:42:55 -0300 Subject: [PATCH 05/23] =?UTF-8?q?refactor:=20rename=20schemes/CLI=20target?= =?UTF-8?q?/scripts/CI=20cmux=20=E2=86=92=20programa=20(Phase=204)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Schemes cmux{,-unit,-ci}.xcscheme → programa*; app label cmux.app → programa.app - CLI target cmux-cli → programa-cli, PRODUCT_MODULE_NAME cmux_cli → programa_cli, stale product refs cmux/cmux.app → programa/programa.app (PRODUCT_NAME already Programa/programa — unchanged) - Bridging header cmux-Bridging-Header.h → programa-Bridging-Header.h - scripts: -scheme cmux → programa, DerivedData/cmux- tag prefix → programa- - CI: -scheme + -only-testing/-skip-testing test-target refs → programa* Deferred (Phase 6): the Copy-Resources build phase (CMUXCommit plist key + shell-integration copy — byte-verified untouched), the cmux CLI command name, release-artifact/entitlements naming. Builds green with the new programa scheme. --- .github/workflows/ci-macos-compat.yml | 8 ++-- .github/workflows/ci.yml | 22 ++++----- .github/workflows/nightly.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/test-depot.yml | 10 ++-- .github/workflows/test-e2e.yml | 6 +-- GhosttyTabs.xcodeproj/project.pbxproj | 46 +++++++++---------- ...{cmux-ci.xcscheme => programa-ci.xcscheme} | 8 ++-- ...x-unit.xcscheme => programa-unit.xcscheme} | 8 ++-- .../{cmux.xcscheme => programa.xcscheme} | 8 ++-- ...ing-Header.h => programa-Bridging-Header.h | 0 scripts/build-sign-upload.sh | 2 +- scripts/launch-tagged-automation.sh | 2 +- scripts/reload.sh | 10 ++-- scripts/reloadp.sh | 2 +- scripts/reloads.sh | 2 +- scripts/run-tests-v1.sh | 4 +- scripts/run-tests-v2.sh | 4 +- scripts/test-unit.sh | 2 +- 19 files changed, 74 insertions(+), 74 deletions(-) rename GhosttyTabs.xcodeproj/xcshareddata/xcschemes/{cmux-ci.xcscheme => programa-ci.xcscheme} (83%) rename GhosttyTabs.xcodeproj/xcshareddata/xcschemes/{cmux-unit.xcscheme => programa-unit.xcscheme} (81%) rename GhosttyTabs.xcodeproj/xcshareddata/xcschemes/{cmux.xcscheme => programa.xcscheme} (79%) rename cmux-Bridging-Header.h => programa-Bridging-Header.h (100%) diff --git a/.github/workflows/ci-macos-compat.yml b/.github/workflows/ci-macos-compat.yml index 9f7cfc9a191..b10c460862c 100644 --- a/.github/workflows/ci-macos-compat.yml +++ b/.github/workflows/ci-macos-compat.yml @@ -105,7 +105,7 @@ jobs: mkdir -p "$SOURCE_PACKAGES_DIR" for attempt in 1 2 3; do - if xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ + if xcodebuild -project GhosttyTabs.xcodeproj -scheme programa-unit -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -resolvePackageDependencies; then exit 0 @@ -125,11 +125,11 @@ jobs: set -euo pipefail SOURCE_PACKAGES_DIR="$PWD/.ci-source-packages" run_unit_tests() { - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ + xcodebuild -project GhosttyTabs.xcodeproj -scheme programa-unit -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ -destination "platform=macOS" \ - -skip-testing:cmuxTests/AppDelegateShortcutRoutingTests/testCmdWClosesWindowWhenClosingLastSurfaceInLastWorkspace \ + -skip-testing:programaTests/AppDelegateShortcutRoutingTests/testCmdWClosesWindowWhenClosingLastSurfaceInLastWorkspace \ test 2>&1 } @@ -184,7 +184,7 @@ jobs: run: | set -euo pipefail SOURCE_PACKAGES_DIR="$PWD/.ci-source-packages" - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ + xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ -destination "platform=macOS" build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15621d39d5c..d82668b1579 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,7 +144,7 @@ jobs: mkdir -p "$SOURCE_PACKAGES_DIR" for attempt in 1 2 3; do - if xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ + if xcodebuild -project GhosttyTabs.xcodeproj -scheme programa-unit -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -resolvePackageDependencies; then exit 0 @@ -162,11 +162,11 @@ jobs: set -euo pipefail SOURCE_PACKAGES_DIR="$PWD/.ci-source-packages" run_unit_tests() { - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ + xcodebuild -project GhosttyTabs.xcodeproj -scheme programa-unit -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ -destination "platform=macOS" \ - -skip-testing:cmuxTests/AppDelegateShortcutRoutingTests/testCmdWClosesWindowWhenClosingLastSurfaceInLastWorkspace \ + -skip-testing:programaTests/AppDelegateShortcutRoutingTests/testCmdWClosesWindowWhenClosingLastSurfaceInLastWorkspace \ test 2>&1 } @@ -315,7 +315,7 @@ jobs: mkdir -p "$SOURCE_PACKAGES_DIR" for attempt in 1 2 3; do - if xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ + if xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -resolvePackageDependencies; then exit 0 @@ -332,7 +332,7 @@ jobs: run: | set -euo pipefail SOURCE_PACKAGES_DIR="$PWD/.ci-source-packages" - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ + xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ -destination "platform=macOS" build @@ -467,7 +467,7 @@ jobs: mkdir -p "$SOURCE_PACKAGES_DIR" for attempt in 1 2 3; do - if xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ + if xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -resolvePackageDependencies; then exit 0 @@ -484,7 +484,7 @@ jobs: run: | set -euo pipefail SOURCE_PACKAGES_DIR="$PWD/.ci-source-packages" - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ + xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ -destination "platform=macOS" \ @@ -663,11 +663,11 @@ jobs: PRELAUNCH_EOF # Run test — app is already launched from shell - if xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ + if xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ -destination "platform=macOS" \ - -only-testing:cmuxUITests/DisplayResolutionRegressionUITests \ + -only-testing:programaUITests/DisplayResolutionRegressionUITests \ test-without-building; then exit 0 fi @@ -687,12 +687,12 @@ jobs: run: | set -euo pipefail SOURCE_PACKAGES_DIR="$PWD/.ci-source-packages" - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ + xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ -destination "platform=macOS" \ -maximum-test-execution-time-allowance 180 \ - -only-testing:cmuxUITests/BrowserPaneNavigationKeybindUITests/testCmdFFocusesBrowserFindFieldAfterCmdDCmdLNavigation \ + -only-testing:programaUITests/BrowserPaneNavigationKeybindUITests/testCmdFFocusesBrowserFindFieldAfterCmdDCmdLNavigation \ test-without-building - name: Cleanup persistent virtual display diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 261dff3e6dd..6758c770ac4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -202,7 +202,7 @@ jobs: - name: Build universal nightly app (Release) if: needs.decide.outputs.should_publish != 'true' || steps.current_head_prebuild.outputs.still_current == 'true' run: | - xcodebuild -scheme cmux -configuration Release -derivedDataPath build-universal \ + xcodebuild -scheme programa -configuration Release -derivedDataPath build-universal \ -destination 'generic/platform=macOS' \ -clonedSourcePackagesDirPath .spm-cache \ ARCHS="arm64 x86_64" \ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7af35ee69f..2ef61148af4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -176,7 +176,7 @@ jobs: - name: Build universal app (Release) if: steps.guard_release_assets.outputs.skip_all != 'true' run: | - xcodebuild -scheme cmux -configuration Release -derivedDataPath build-universal \ + xcodebuild -scheme programa -configuration Release -derivedDataPath build-universal \ -destination 'generic/platform=macOS' \ -clonedSourcePackagesDirPath .spm-cache \ ARCHS="arm64 x86_64" \ diff --git a/.github/workflows/test-depot.yml b/.github/workflows/test-depot.yml index 981920ce138..5b9f7180b2c 100644 --- a/.github/workflows/test-depot.yml +++ b/.github/workflows/test-depot.yml @@ -126,7 +126,7 @@ jobs: mkdir -p "$SOURCE_PACKAGES_DIR" for attempt in 1 2 3; do - if xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ + if xcodebuild -project GhosttyTabs.xcodeproj -scheme programa-unit -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -resolvePackageDependencies; then exit 0 @@ -145,7 +145,7 @@ jobs: set -euo pipefail SOURCE_PACKAGES_DIR="$PWD/.ci-source-packages" run_unit_tests() { - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ + xcodebuild -project GhosttyTabs.xcodeproj -scheme programa-unit -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ -destination "platform=macOS" test 2>&1 @@ -191,12 +191,12 @@ jobs: # Build the -only-testing argument if [ -n "$TEST_FILTER" ]; then - ONLY_TESTING="-only-testing:cmuxUITests/$TEST_FILTER" + ONLY_TESTING="-only-testing:programaUITests/$TEST_FILTER" else - ONLY_TESTING="-only-testing:cmuxUITests" + ONLY_TESTING="-only-testing:programaUITests" fi - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ + xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ -destination "platform=macOS" \ diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 401308f92e6..583e67683ab 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -182,7 +182,7 @@ jobs: SOURCE_PACKAGES_DIR="$PWD/.ci-source-packages" mkdir -p "$SOURCE_PACKAGES_DIR" for attempt in 1 2 3; do - if xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug \ + if xcodebuild -project GhosttyTabs.xcodeproj -scheme programa-unit -configuration Debug \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -resolvePackageDependencies; then exit 0 @@ -204,7 +204,7 @@ jobs: run: | set -euo pipefail SOURCE_PACKAGES_DIR="$PWD/.ci-source-packages" - ONLY_TESTING="-only-testing:cmuxUITests/$TEST_FILTER" + ONLY_TESTING="-only-testing:programaUITests/$TEST_FILTER" DISPLAY_ENV_PREFIX=() if [ "$TEST_FILTER" = "DisplayResolutionRegressionUITests" ]; then @@ -250,7 +250,7 @@ jobs: fi XCODEBUILD_CMD=( - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug + xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Debug -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" -disableAutomaticPackageResolution -destination "platform=macOS" diff --git a/GhosttyTabs.xcodeproj/project.pbxproj b/GhosttyTabs.xcodeproj/project.pbxproj index 841b13d3a0f..e9b30d56905 100644 --- a/GhosttyTabs.xcodeproj/project.pbxproj +++ b/GhosttyTabs.xcodeproj/project.pbxproj @@ -80,7 +80,7 @@ A5001230 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = A5001231 /* Sparkle */; }; B9000002A1B2C3D4E5F60719 /* programa.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000001A1B2C3D4E5F60719 /* programa.swift */; }; B9000027A1B2C3D4E5F60719 /* RemoteRelayZshBootstrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001641 /* RemoteRelayZshBootstrap.swift */; }; - B900000BA1B2C3D4E5F60719 /* cmux in Copy CLI */ = {isa = PBXBuildFile; fileRef = B9000004A1B2C3D4E5F60719 /* cmux */; }; + B900000BA1B2C3D4E5F60719 /* programa in Copy CLI */ = {isa = PBXBuildFile; fileRef = B9000004A1B2C3D4E5F60719 /* programa */; }; C1ADE00002A1B2C3D4E5F719 /* claude in Copy CLI */ = {isa = PBXBuildFile; fileRef = C1ADE00001A1B2C3D4E5F719 /* claude */; }; D1BEF00002A1B2C3D4E5F719 /* open in Copy CLI */ = {isa = PBXBuildFile; fileRef = D1BEF00001A1B2C3D4E5F719 /* open */; }; 84E00D47E4584162AE53BC8D /* xterm-ghostty in Resources */ = {isa = PBXBuildFile; fileRef = B2E7294509CC42FE9191870E /* xterm-ghostty */; }; @@ -156,7 +156,7 @@ dstPath = "bin"; dstSubfolderSpec = 7; files = ( - B900000BA1B2C3D4E5F60719 /* cmux in Copy CLI */, + B900000BA1B2C3D4E5F60719 /* programa in Copy CLI */, C1ADE00002A1B2C3D4E5F719 /* claude in Copy CLI */, D1BEF00002A1B2C3D4E5F719 /* open in Copy CLI */, ); @@ -188,8 +188,8 @@ isa = PBXContainerItemProxy; containerPortal = A5001070 /* Project object */; proxyType = 1; - remoteGlobalIDString = B9000005A1B2C3D4E5F60719 /* cmux-cli */; - remoteInfo = "cmux-cli"; + remoteGlobalIDString = B9000005A1B2C3D4E5F60719 /* programa-cli */; + remoteInfo = "programa-cli"; }; F1000008A1B2C3D4E5F60718 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -208,7 +208,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - A5001000 /* cmux.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cmux.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A5001000 /* programa.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = programa.app; sourceTree = BUILT_PRODUCTS_DIR; }; F1000002A1B2C3D4E5F60718 /* programaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = programaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 7E7E6EF344A568AC7FEE3715 /* programaUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = programaUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A5001011 /* ProgramaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgramaApp.swift; sourceTree = ""; }; @@ -225,7 +225,7 @@ A5001545 /* TerminalSSHSessionDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSSHSessionDetector.swift; sourceTree = ""; }; A5001016 /* GhosttyKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = GhosttyKit.xcframework; sourceTree = ""; }; A5001017 /* ghostty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ghostty.h; sourceTree = ""; }; - A5001018 /* cmux-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "cmux-Bridging-Header.h"; sourceTree = ""; }; + A5001018 /* programa-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "programa-Bridging-Header.h"; sourceTree = ""; }; A5001019 /* TerminalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalController.swift; sourceTree = ""; }; A5001600 /* SentryHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryHelper.swift; sourceTree = ""; }; A5001620 /* AppleScriptSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptSupport.swift; sourceTree = ""; }; @@ -289,7 +289,7 @@ D1BEF00001A1B2C3D4E5F719 /* open */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "Resources/bin/open"; sourceTree = SOURCE_ROOT; }; A5002001 /* THIRD_PARTY_LICENSES.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = THIRD_PARTY_LICENSES.md; sourceTree = SOURCE_ROOT; }; B9000001A1B2C3D4E5F60719 /* programa.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = programa.swift; sourceTree = ""; }; - B9000004A1B2C3D4E5F60719 /* cmux */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cmux; sourceTree = BUILT_PRODUCTS_DIR; }; + B9000004A1B2C3D4E5F60719 /* programa */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = programa; sourceTree = BUILT_PRODUCTS_DIR; }; D1320AA0D1320AA0D1320AA5 /* CmuxDockTilePlugin.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CmuxDockTilePlugin.plugin; sourceTree = BUILT_PRODUCTS_DIR; }; B9000011A1B2C3D4E5F60719 /* AutomationSocketUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomationSocketUITests.swift; sourceTree = ""; }; B9000013A1B2C3D4E5F60719 /* JumpToUnreadUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpToUnreadUITests.swift; sourceTree = ""; }; @@ -452,7 +452,7 @@ IC000002 /* AppIcon.icon */, A5001016 /* GhosttyKit.xcframework */, A5001017 /* ghostty.h */, - A5001018 /* cmux-Bridging-Header.h */, + A5001018 /* programa-Bridging-Header.h */, 3196C9C2D01F054C1D3385DD /* programaUITests */, F1000003A1B2C3D4E5F60718 /* programaTests */, A5001042 /* Products */, @@ -553,8 +553,8 @@ A5001042 /* Products */ = { isa = PBXGroup; children = ( - A5001000 /* cmux.app */, - B9000004A1B2C3D4E5F60719 /* cmux */, + A5001000 /* programa.app */, + B9000004A1B2C3D4E5F60719 /* programa */, D1320AA0D1320AA0D1320AA5 /* CmuxDockTilePlugin.plugin */, 7E7E6EF344A568AC7FEE3715 /* programaUITests.xctest */, F1000002A1B2C3D4E5F60718 /* programaTests.xctest */, @@ -655,7 +655,7 @@ ); name = GhosttyTabs; productName = GhosttyTabs; - productReference = A5001000 /* cmux.app */; + productReference = A5001000 /* programa.app */; productType = "com.apple.product-type.application"; }; D1320AA0D1320AA0D1320AA8 /* CmuxDockTilePlugin */ = { @@ -675,9 +675,9 @@ productReference = D1320AA0D1320AA0D1320AA5 /* CmuxDockTilePlugin.plugin */; productType = "com.apple.product-type.bundle"; }; - B9000005A1B2C3D4E5F60719 /* cmux-cli */ = { + B9000005A1B2C3D4E5F60719 /* programa-cli */ = { isa = PBXNativeTarget; - buildConfigurationList = B9000007A1B2C3D4E5F60719 /* Build configuration list for PBXNativeTarget "cmux-cli" */; + buildConfigurationList = B9000007A1B2C3D4E5F60719 /* Build configuration list for PBXNativeTarget "programa-cli" */; buildPhases = ( B9000006A1B2C3D4E5F60719 /* Sources */, B900000CA1B2C3D4E5F60719 /* Frameworks */, @@ -689,9 +689,9 @@ packageProductDependencies = ( A5001251 /* Sentry */, ); - name = "cmux-cli"; - productName = cmux; - productReference = B9000004A1B2C3D4E5F60719 /* cmux */; + name = "programa-cli"; + productName = programa; + productReference = B9000004A1B2C3D4E5F60719 /* programa */; productType = "com.apple.product-type.tool"; }; CB450DF0F0B3839599082C4D /* programaUITests */ = { @@ -762,7 +762,7 @@ targets = ( A5001050 /* GhosttyTabs */, D1320AA0D1320AA0D1320AA8 /* CmuxDockTilePlugin */, - B9000005A1B2C3D4E5F60719 /* cmux-cli */, + B9000005A1B2C3D4E5F60719 /* programa-cli */, CB450DF0F0B3839599082C4D /* programaUITests */, F1000004A1B2C3D4E5F60718 /* programaTests */, ); @@ -934,7 +934,7 @@ }; B900000EA1B2C3D4E5F60719 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = B9000005A1B2C3D4E5F60719 /* cmux-cli */; + target = B9000005A1B2C3D4E5F60719 /* programa-cli */; targetProxy = B900000DA1B2C3D4E5F60719 /* PBXContainerItemProxy */; }; D1320AA0D1320AA0D1320AB1 /* PBXTargetDependency */ = { @@ -1041,7 +1041,7 @@ PRODUCT_NAME = "Programa DEV"; SPARKLE_PUBLIC_KEY = "avjcgKibf1FTvhIjLBxhd+0HSpsXU4D0IGlVk8cgqRc="; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OBJC_BRIDGING_HEADER = "cmux-Bridging-Header.h"; + SWIFT_OBJC_BRIDGING_HEADER = "programa-Bridging-Header.h"; SWIFT_VERSION = 5.0; }; name = Debug; @@ -1081,7 +1081,7 @@ PRODUCT_NAME = Programa; SPARKLE_PUBLIC_KEY = "avjcgKibf1FTvhIjLBxhd+0HSpsXU4D0IGlVk8cgqRc="; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OBJC_BRIDGING_HEADER = "cmux-Bridging-Header.h"; + SWIFT_OBJC_BRIDGING_HEADER = "programa-Bridging-Header.h"; SWIFT_VERSION = 5.0; }; name = Release; @@ -1147,7 +1147,7 @@ ); MACOSX_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = programa; - PRODUCT_MODULE_NAME = cmux_cli; + PRODUCT_MODULE_NAME = programa_cli; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -1166,7 +1166,7 @@ ); MACOSX_DEPLOYMENT_TARGET = 14.0; PRODUCT_NAME = programa; - PRODUCT_MODULE_NAME = cmux_cli; + PRODUCT_MODULE_NAME = programa_cli; ONLY_ACTIVE_ARCH = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; @@ -1350,7 +1350,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - B9000007A1B2C3D4E5F60719 /* Build configuration list for PBXNativeTarget "cmux-cli" */ = { + B9000007A1B2C3D4E5F60719 /* Build configuration list for PBXNativeTarget "programa-cli" */ = { isa = XCConfigurationList; buildConfigurations = ( B9000008A1B2C3D4E5F60719 /* Debug */, diff --git a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-ci.xcscheme b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa-ci.xcscheme similarity index 83% rename from GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-ci.xcscheme rename to GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa-ci.xcscheme index a697e241fb5..78989096be1 100644 --- a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-ci.xcscheme +++ b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa-ci.xcscheme @@ -3,7 +3,7 @@ - + @@ -20,17 +20,17 @@ - + - + - + diff --git a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-unit.xcscheme b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa-unit.xcscheme similarity index 81% rename from GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-unit.xcscheme rename to GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa-unit.xcscheme index b952ce48c33..fb5d5aca807 100644 --- a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux-unit.xcscheme +++ b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa-unit.xcscheme @@ -3,7 +3,7 @@ - + @@ -17,17 +17,17 @@ - + - + - + diff --git a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux.xcscheme b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa.xcscheme similarity index 79% rename from GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux.xcscheme rename to GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa.xcscheme index 97917a3282d..cd6a5c43211 100644 --- a/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux.xcscheme +++ b/GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa.xcscheme @@ -3,7 +3,7 @@ - + @@ -14,17 +14,17 @@ - + - + - + diff --git a/cmux-Bridging-Header.h b/programa-Bridging-Header.h similarity index 100% rename from cmux-Bridging-Header.h rename to programa-Bridging-Header.h diff --git a/scripts/build-sign-upload.sh b/scripts/build-sign-upload.sh index 95790676936..a3af23307a2 100755 --- a/scripts/build-sign-upload.sh +++ b/scripts/build-sign-upload.sh @@ -71,7 +71,7 @@ fi # --- Build app (Release, unsigned) --- echo "Building app..." rm -rf build/ -xcodebuild -scheme cmux -configuration Release -derivedDataPath build CODE_SIGNING_ALLOWED=NO build 2>&1 | tail -5 +xcodebuild -scheme programa -configuration Release -derivedDataPath build CODE_SIGNING_ALLOWED=NO build 2>&1 | tail -5 echo "Build succeeded" HELPER_PATH="$APP_PATH/Contents/Resources/bin/ghostty" diff --git a/scripts/launch-tagged-automation.sh b/scripts/launch-tagged-automation.sh index b3304484d91..ae5bc5127db 100755 --- a/scripts/launch-tagged-automation.sh +++ b/scripts/launch-tagged-automation.sh @@ -104,7 +104,7 @@ fi TAG_ID="$(sanitize_bundle "$TAG")" TAG_SLUG="$(sanitize_path "$TAG")" -APP="$HOME/Library/Developer/Xcode/DerivedData/cmux-${TAG_SLUG}/Build/Products/Debug/Programa DEV ${TAG}.app" +APP="$HOME/Library/Developer/Xcode/DerivedData/programa-${TAG_SLUG}/Build/Products/Debug/Programa DEV ${TAG}.app" BID="com.darkroom.programa.debug.${TAG_ID}" SOCK="/tmp/programa-debug-${TAG_SLUG}.sock" DSOCK="$HOME/Library/Application Support/programa/programad-dev-${TAG_SLUG}.sock" diff --git a/scripts/reload.sh b/scripts/reload.sh index bb8466441f1..bba872ece26 100755 --- a/scripts/reload.sh +++ b/scripts/reload.sh @@ -159,7 +159,7 @@ sanitize_path() { tagged_derived_data_path() { local slug="$1" - echo "$HOME/Library/Developer/Xcode/DerivedData/cmux-${slug}" + echo "$HOME/Library/Developer/Xcode/DerivedData/programa-${slug}" } print_tag_cleanup_reminder() { @@ -172,8 +172,8 @@ print_tag_cleanup_reminder() { while IFS= read -r -d '' path; do if [[ "$path" == /tmp/programa-* ]]; then tag="${path#/tmp/programa-}" - elif [[ "$path" == "$HOME/Library/Developer/Xcode/DerivedData/cmux-"* ]]; then - tag="${path#$HOME/Library/Developer/Xcode/DerivedData/cmux-}" + elif [[ "$path" == "$HOME/Library/Developer/Xcode/DerivedData/programa-"* ]]; then + tag="${path#$HOME/Library/Developer/Xcode/DerivedData/programa-}" else continue fi @@ -191,7 +191,7 @@ print_tag_cleanup_reminder() { stale_tags+=("$tag") done < <( find /tmp -maxdepth 1 -name 'programa-*' -print0 2>/dev/null - find "$HOME/Library/Developer/Xcode/DerivedData" -maxdepth 1 -type d -name 'cmux-*' -print0 2>/dev/null + find "$HOME/Library/Developer/Xcode/DerivedData" -maxdepth 1 -type d -name 'programa-*' -print0 2>/dev/null ) echo @@ -304,7 +304,7 @@ fi XCODEBUILD_ARGS=( -project GhosttyTabs.xcodeproj - -scheme cmux + -scheme programa -configuration Debug -destination 'platform=macOS' ) diff --git a/scripts/reloadp.sh b/scripts/reloadp.sh index 943c07454b1..0a93539e221 100755 --- a/scripts/reloadp.sh +++ b/scripts/reloadp.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Release -destination 'platform=macOS' build +xcodebuild -project GhosttyTabs.xcodeproj -scheme programa -configuration Release -destination 'platform=macOS' build pkill -x cmux || true sleep 0.2 APP_PATH="$( diff --git a/scripts/reloads.sh b/scripts/reloads.sh index c8a1a8bc139..bfb1fa50993 100755 --- a/scripts/reloads.sh +++ b/scripts/reloads.sh @@ -121,7 +121,7 @@ fi XCODEBUILD_ARGS=( -project GhosttyTabs.xcodeproj - -scheme cmux + -scheme programa -configuration Release -destination 'platform=macOS' ) diff --git a/scripts/run-tests-v1.sh b/scripts/run-tests-v1.sh index 1a59e2c8049..4b9f124bf97 100755 --- a/scripts/run-tests-v1.sh +++ b/scripts/run-tests-v1.sh @@ -11,7 +11,7 @@ fi cd "$(dirname "$0")/.." -DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData/cmux-tests-v1" +DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData/programa-tests-v1" APP="$DERIVED_DATA_PATH/Build/Products/Debug/Programa DEV.app" RUN_TAG="tests-v1" @@ -22,7 +22,7 @@ echo "== build ==" rm -rf "$DERIVED_DATA_PATH/Build/Intermediates.noindex/SwiftExplicitPrecompiledModules" || true xcodebuild \ -project GhosttyTabs.xcodeproj \ - -scheme cmux \ + -scheme programa \ -configuration Debug \ -destination "platform=macOS" \ -derivedDataPath "$DERIVED_DATA_PATH" \ diff --git a/scripts/run-tests-v2.sh b/scripts/run-tests-v2.sh index cd98422c6e9..737ace95d8d 100755 --- a/scripts/run-tests-v2.sh +++ b/scripts/run-tests-v2.sh @@ -11,7 +11,7 @@ fi cd "$(dirname "$0")/.." -DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData/cmux-tests-v2" +DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData/programa-tests-v2" APP="$DERIVED_DATA_PATH/Build/Products/Debug/Programa DEV.app" RUN_TAG="tests-v2" @@ -22,7 +22,7 @@ echo "== build ==" rm -rf "$DERIVED_DATA_PATH/Build/Intermediates.noindex/SwiftExplicitPrecompiledModules" || true xcodebuild \ -project GhosttyTabs.xcodeproj \ - -scheme cmux \ + -scheme programa \ -configuration Debug \ -destination "platform=macOS" \ -derivedDataPath "$DERIVED_DATA_PATH" \ diff --git a/scripts/test-unit.sh b/scripts/test-unit.sh index 80c6c724a02..8922aa6ea73 100755 --- a/scripts/test-unit.sh +++ b/scripts/test-unit.sh @@ -4,7 +4,7 @@ set -euo pipefail cd "$(dirname "$0")/.." PROJECT="GhosttyTabs.xcodeproj" -SCHEME="cmux-unit" +SCHEME="programa-unit" CONFIGURATION="${PROGRAMA_TEST_CONFIGURATION:-Debug}" DESTINATION="${PROGRAMA_TEST_DESTINATION:-platform=macOS}" From f4790e1260eea02dc7bc09feae0ca633e8d0deaf Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 11:53:43 -0300 Subject: [PATCH 06/23] =?UTF-8?q?feat:=20migrate=20config=20paths=20cmux?= =?UTF-8?q?=20=E2=86=92=20programa=20with=20legacy=20fallback=20(Phase=205?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Global config, settings.json, and project-root config now prefer the new programa paths (~/.config/programa/, programa.json) and transparently fall back to the legacy cmux paths when only those exist, so existing users keep working: - ProgramaConfigStore.globalConfigPath: effective(new, legacy) - ProgramaSettingsFileStore.defaultPrimaryPath: effective(new, legacy) - findProgramaConfig: accepts programa.json then cmux.json; new configs default to programa.json - display/help strings + command.cmuxConfig.subtitle → programa.json UserDefaults key cmux.settingsFile.backups.v1 intentionally left (renaming would orphan stored backups — deferred to Phase 6 key migration). Builds green. --- CLI/programa.swift | 2 +- Resources/Localizable.xcstrings | 2 +- Sources/ContentView.swift | 2 +- Sources/KeyboardShortcutSettings.swift | 2 +- .../KeyboardShortcutSettingsFileStore.swift | 11 ++++++++-- Sources/ProgramaApp.swift | 4 ++-- Sources/ProgramaConfig.swift | 20 ++++++++++++++----- Sources/ProgramaDirectoryTrust.swift | 16 +++++++-------- Sources/Workspace.swift | 2 +- 9 files changed, 39 insertions(+), 22 deletions(-) diff --git a/CLI/programa.swift b/CLI/programa.swift index ae6510227be..916ffb1df7c 100644 --- a/CLI/programa.swift +++ b/CLI/programa.swift @@ -7347,7 +7347,7 @@ struct CMUXCLI { Usage: cmux reload-config Run the same configuration reload as the Reload Configuration shortcut. - This reloads Ghostty config, re-reads ~/.config/cmux/settings.json, and refreshes terminals. + This reloads Ghostty config, re-reads ~/.config/programa/settings.json, and refreshes terminals. Example: cmux reload-config diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index a552e89397a..9dc420631a5 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -2438,7 +2438,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux.json" + "value": "programa.json" } } } diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 7c1c19b160c..7798dc2219e 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -7073,7 +7073,7 @@ struct ContentView: View { ) ) - let cmuxConfigDefaultSubtitle = constant(String(localized: "command.cmuxConfig.subtitle", defaultValue: "cmux.json")) + let cmuxConfigDefaultSubtitle = constant(String(localized: "command.cmuxConfig.subtitle", defaultValue: "programa.json")) for command in cmuxConfigStore.loadedCommands { let commandName = sanitizeProgramaConfigPaletteText(command.name) let subtitle = command.description diff --git a/Sources/KeyboardShortcutSettings.swift b/Sources/KeyboardShortcutSettings.swift index 3d820a9e52d..ef91390f823 100644 --- a/Sources/KeyboardShortcutSettings.swift +++ b/Sources/KeyboardShortcutSettings.swift @@ -5,7 +5,7 @@ import SwiftUI enum KeyboardShortcutSettings { static let didChangeNotification = Notification.Name("cmux.keyboardShortcutSettingsDidChange") static let actionUserInfoKey = "action" - static let settingsFileDisplayPath = "~/.config/cmux/settings.json" + static let settingsFileDisplayPath = "~/.config/programa/settings.json" static var settingsFileStore: KeyboardShortcutSettingsFileStore = .shared { didSet { notifySettingsFileDidChange() diff --git a/Sources/KeyboardShortcutSettingsFileStore.swift b/Sources/KeyboardShortcutSettingsFileStore.swift index b4d33af9164..d9b1a33f417 100644 --- a/Sources/KeyboardShortcutSettingsFileStore.swift +++ b/Sources/KeyboardShortcutSettingsFileStore.swift @@ -30,7 +30,14 @@ final class ProgramaSettingsFileStore { static var defaultPrimaryPath: String { let home = FileManager.default.homeDirectoryForCurrentUser.path - return (home as NSString).appendingPathComponent(".config/cmux/settings.json") + let newPath = (home as NSString).appendingPathComponent(".config/programa/settings.json") + let legacyPath = (home as NSString).appendingPathComponent(".config/cmux/settings.json") + let fm = FileManager.default + // Prefer the new path; fall back to the legacy cmux path so existing users' + // ~/.config/programa/settings.json keeps working after the rebrand. + if fm.fileExists(atPath: newPath) { return newPath } + if fm.fileExists(atPath: legacyPath) { return legacyPath } + return newPath } static var defaultFallbackPath: String? { @@ -1233,7 +1240,7 @@ final class ProgramaSettingsFileStore { " // Uncomment and edit any setting to make it file-managed.", " // Remove a setting to fall back to the value saved in Settings.", " // cmux creates this template on launch when both settings file locations are missing.", - " // ~/.config/cmux/settings.json takes precedence over the Application Support fallback.", + " // ~/.config/programa/settings.json takes precedence over the Application Support fallback.", "", ] diff --git a/Sources/ProgramaApp.swift b/Sources/ProgramaApp.swift index dd351d33f0a..57d959b0580 100644 --- a/Sources/ProgramaApp.swift +++ b/Sources/ProgramaApp.swift @@ -5444,7 +5444,7 @@ struct SettingsView: View { VStack(alignment: .leading, spacing: 6) { SettingsCardRow( String(localized: "settings.customCommands.trustedDirectories", defaultValue: "Trusted Directories"), - subtitle: String(localized: "settings.customCommands.trustedDirectories.subtitle", defaultValue: "Commands from cmux.json in these directories run without confirmation. One path per line.") + subtitle: String(localized: "settings.customCommands.trustedDirectories.subtitle", defaultValue: "Commands from programa.json in these directories run without confirmation. One path per line.") ) { EmptyView() } @@ -5468,7 +5468,7 @@ struct SettingsView: View { } SettingsCardDivider() - SettingsCardNote(String(localized: "settings.customCommands.trustedDirectories.note", defaultValue: "Place a cmux.json in your project root to define custom commands. Trust a directory from the confirmation dialog, or add paths here. For git repos, trusting the root covers all subdirectories.")) + SettingsCardNote(String(localized: "settings.customCommands.trustedDirectories.note", defaultValue: "Place a programa.json in your project root to define custom commands. Trust a directory from the confirmation dialog, or add paths here. For git repos, trusting the root covers all subdirectories.")) } SettingsSectionHeader(title: String(localized: "settings.section.browser", defaultValue: "Browser")) diff --git a/Sources/ProgramaConfig.swift b/Sources/ProgramaConfig.swift index 3fe982c5568..0046a8339a5 100644 --- a/Sources/ProgramaConfig.swift +++ b/Sources/ProgramaConfig.swift @@ -267,7 +267,14 @@ final class ProgramaConfigStore: ObservableObject { private(set) var localConfigPath: String? let globalConfigPath: String = { let home = FileManager.default.homeDirectoryForCurrentUser.path - return (home as NSString).appendingPathComponent(".config/cmux/cmux.json") + let newPath = (home as NSString).appendingPathComponent(".config/programa/programa.json") + let legacyPath = (home as NSString).appendingPathComponent(".config/cmux/cmux.json") + let fm = FileManager.default + // Prefer the new path; transparently fall back to the legacy cmux path so + // existing users' ~/.config/cmux/cmux.json keeps working after the rebrand. + if fm.fileExists(atPath: newPath) { return newPath } + if fm.fileExists(atPath: legacyPath) { return legacyPath } + return newPath }() private var cancellables = Set() @@ -320,7 +327,7 @@ final class ProgramaConfigStore: ObservableObject { let newPath: String? if let directory, !directory.isEmpty { newPath = findProgramaConfig(startingFrom: directory) - ?? (directory as NSString).appendingPathComponent("cmux.json") + ?? (directory as NSString).appendingPathComponent("programa.json") } else { newPath = nil } @@ -338,9 +345,12 @@ final class ProgramaConfigStore: ObservableObject { var current = directory let fs = FileManager.default while true { - let candidate = (current as NSString).appendingPathComponent("cmux.json") - if fs.fileExists(atPath: candidate) { - return candidate + // Prefer programa.json; accept legacy cmux.json so existing project roots keep working. + for name in ["programa.json", "cmux.json"] { + let candidate = (current as NSString).appendingPathComponent(name) + if fs.fileExists(atPath: candidate) { + return candidate + } } let parent = (current as NSString).deletingLastPathComponent if parent == current { break } diff --git a/Sources/ProgramaDirectoryTrust.swift b/Sources/ProgramaDirectoryTrust.swift index a9a42906434..c3656639f6f 100644 --- a/Sources/ProgramaDirectoryTrust.swift +++ b/Sources/ProgramaDirectoryTrust.swift @@ -1,9 +1,9 @@ import Foundation -/// Manages trusted directories for cmux.json command execution. +/// Manages trusted directories for programa.json command execution. /// When a directory (or its git repo root) is trusted, `confirm: true` commands -/// from that directory's cmux.json skip the confirmation dialog. -/// Global config (~/.config/cmux/cmux.json) is always trusted. +/// from that directory's programa.json skip the confirmation dialog. +/// Global config (~/.config/programa/programa.json) is always trusted. final class ProgramaDirectoryTrust { static let shared = ProgramaDirectoryTrust() static let didChangeNotification = Notification.Name("cmux.directoryTrustDidChange") @@ -30,16 +30,16 @@ final class ProgramaDirectoryTrust { } } - /// Check if a cmux.json path is trusted. + /// Check if a programa.json path is trusted. /// Global config is always trusted. For local configs, check the git repo root - /// (or the cmux.json parent directory if not in a git repo). + /// (or the programa.json parent directory if not in a git repo). func isTrusted(configPath: String, globalConfigPath: String) -> Bool { if configPath == globalConfigPath { return true } let trustKey = Self.trustKey(for: configPath) return trustedPaths.contains(trustKey) } - /// Trust the directory containing a cmux.json. If the cmux.json is inside a git + /// Trust the directory containing a programa.json. If the programa.json is inside a git /// repo, trusts the repo root (covering all subdirectories). func trust(configPath: String) { let trustKey = Self.trustKey(for: configPath) @@ -79,8 +79,8 @@ final class ProgramaDirectoryTrust { // MARK: - Private - /// Resolve the trust key for a cmux.json path: git repo root if inside a repo, - /// otherwise the cmux.json's parent directory. + /// Resolve the trust key for a programa.json path: git repo root if inside a repo, + /// otherwise the programa.json's parent directory. static func trustKey(for configPath: String) -> String { let configDir = (configPath as NSString).deletingLastPathComponent if let gitRoot = findGitRoot(from: configDir) { diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 6706009d99a..71dc981e642 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -756,7 +756,7 @@ extension Workspace { } } -// MARK: - cmux.json custom layout +// MARK: - programa.json custom layout extension Workspace { From 7752d02e6b4d8e4d80704a56f35e76669b16796a Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 12:05:33 -0300 Subject: [PATCH 07/23] =?UTF-8?q?refactor:=20rename=20DockTile=20plugin=20?= =?UTF-8?q?CmuxDockTilePlugin=20=E2=86=92=20ProgramaDockTilePlugin=20(Phas?= =?UTF-8?q?e=206a)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Class + target + product (CmuxDockTilePlugin.plugin → ProgramaDockTilePlugin.plugin) + app Info.plist NSDockTilePlugIn value, in lockstep. Notification value was already com.darkroom.programa.*. Compiles; dock-icon switching to be manually verified. --- GhosttyTabs.xcodeproj/project.pbxproj | 28 +++++++++++++-------------- Resources/Info.plist | 2 +- Sources/AppIconDockTilePlugin.swift | 12 ++++++------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/GhosttyTabs.xcodeproj/project.pbxproj b/GhosttyTabs.xcodeproj/project.pbxproj index e9b30d56905..8389eb3ad1f 100644 --- a/GhosttyTabs.xcodeproj/project.pbxproj +++ b/GhosttyTabs.xcodeproj/project.pbxproj @@ -27,7 +27,7 @@ A5001601 /* SentryHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001600 /* SentryHelper.swift */; }; A5001621 /* AppleScriptSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001620 /* AppleScriptSupport.swift */; }; D1320AA0D1320AA0D1320AA1 /* AppIconDockTilePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1320AA0D1320AA0D1320AA4 /* AppIconDockTilePlugin.swift */; }; - D1320AA0D1320AA0D1320AA2 /* CmuxDockTilePlugin.plugin in Copy Dock Tile Plugin */ = {isa = PBXBuildFile; fileRef = D1320AA0D1320AA0D1320AA5 /* CmuxDockTilePlugin.plugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D1320AA0D1320AA0D1320AA2 /* ProgramaDockTilePlugin.plugin in Copy Dock Tile Plugin */ = {isa = PBXBuildFile; fileRef = D1320AA0D1320AA0D1320AA5 /* ProgramaDockTilePlugin.plugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A5001400 /* Panel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001410 /* Panel.swift */; }; A5001401 /* TerminalPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001411 /* TerminalPanel.swift */; }; A5001402 /* BrowserPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001412 /* BrowserPanel.swift */; }; @@ -169,7 +169,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( - D1320AA0D1320AA0D1320AA2 /* CmuxDockTilePlugin.plugin in Copy Dock Tile Plugin */, + D1320AA0D1320AA0D1320AA2 /* ProgramaDockTilePlugin.plugin in Copy Dock Tile Plugin */, ); name = "Copy Dock Tile Plugin"; runOnlyForDeploymentPostprocessing = 0; @@ -202,8 +202,8 @@ isa = PBXContainerItemProxy; containerPortal = A5001070 /* Project object */; proxyType = 1; - remoteGlobalIDString = D1320AA0D1320AA0D1320AA8 /* CmuxDockTilePlugin */; - remoteInfo = CmuxDockTilePlugin; + remoteGlobalIDString = D1320AA0D1320AA0D1320AA8 /* ProgramaDockTilePlugin */; + remoteInfo = ProgramaDockTilePlugin; }; /* End PBXContainerItemProxy section */ @@ -290,7 +290,7 @@ A5002001 /* THIRD_PARTY_LICENSES.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = THIRD_PARTY_LICENSES.md; sourceTree = SOURCE_ROOT; }; B9000001A1B2C3D4E5F60719 /* programa.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = programa.swift; sourceTree = ""; }; B9000004A1B2C3D4E5F60719 /* programa */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = programa; sourceTree = BUILT_PRODUCTS_DIR; }; - D1320AA0D1320AA0D1320AA5 /* CmuxDockTilePlugin.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CmuxDockTilePlugin.plugin; sourceTree = BUILT_PRODUCTS_DIR; }; + D1320AA0D1320AA0D1320AA5 /* ProgramaDockTilePlugin.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ProgramaDockTilePlugin.plugin; sourceTree = BUILT_PRODUCTS_DIR; }; B9000011A1B2C3D4E5F60719 /* AutomationSocketUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomationSocketUITests.swift; sourceTree = ""; }; B9000013A1B2C3D4E5F60719 /* JumpToUnreadUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpToUnreadUITests.swift; sourceTree = ""; }; B9000016A1B2C3D4E5F60719 /* MultiWindowNotificationsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiWindowNotificationsUITests.swift; sourceTree = ""; }; @@ -555,7 +555,7 @@ children = ( A5001000 /* programa.app */, B9000004A1B2C3D4E5F60719 /* programa */, - D1320AA0D1320AA0D1320AA5 /* CmuxDockTilePlugin.plugin */, + D1320AA0D1320AA0D1320AA5 /* ProgramaDockTilePlugin.plugin */, 7E7E6EF344A568AC7FEE3715 /* programaUITests.xctest */, F1000002A1B2C3D4E5F60718 /* programaTests.xctest */, ); @@ -658,9 +658,9 @@ productReference = A5001000 /* programa.app */; productType = "com.apple.product-type.application"; }; - D1320AA0D1320AA0D1320AA8 /* CmuxDockTilePlugin */ = { + D1320AA0D1320AA0D1320AA8 /* ProgramaDockTilePlugin */ = { isa = PBXNativeTarget; - buildConfigurationList = D1320AA0D1320AA0D1320AB4 /* Build configuration list for PBXNativeTarget "CmuxDockTilePlugin" */; + buildConfigurationList = D1320AA0D1320AA0D1320AB4 /* Build configuration list for PBXNativeTarget "ProgramaDockTilePlugin" */; buildPhases = ( D1320AA0D1320AA0D1320AB0 /* Sources */, D1320AA0D1320AA0D1320AA7 /* Frameworks */, @@ -670,9 +670,9 @@ ); dependencies = ( ); - name = CmuxDockTilePlugin; - productName = CmuxDockTilePlugin; - productReference = D1320AA0D1320AA0D1320AA5 /* CmuxDockTilePlugin.plugin */; + name = ProgramaDockTilePlugin; + productName = ProgramaDockTilePlugin; + productReference = D1320AA0D1320AA0D1320AA5 /* ProgramaDockTilePlugin.plugin */; productType = "com.apple.product-type.bundle"; }; B9000005A1B2C3D4E5F60719 /* programa-cli */ = { @@ -761,7 +761,7 @@ projectRoot = ""; targets = ( A5001050 /* GhosttyTabs */, - D1320AA0D1320AA0D1320AA8 /* CmuxDockTilePlugin */, + D1320AA0D1320AA0D1320AA8 /* ProgramaDockTilePlugin */, B9000005A1B2C3D4E5F60719 /* programa-cli */, CB450DF0F0B3839599082C4D /* programaUITests */, F1000004A1B2C3D4E5F60718 /* programaTests */, @@ -939,7 +939,7 @@ }; D1320AA0D1320AA0D1320AB1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D1320AA0D1320AA0D1320AA8 /* CmuxDockTilePlugin */; + target = D1320AA0D1320AA0D1320AA8 /* ProgramaDockTilePlugin */; targetProxy = D1320AA0D1320AA0D1320AA3 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -1323,7 +1323,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D1320AA0D1320AA0D1320AB4 /* Build configuration list for PBXNativeTarget "CmuxDockTilePlugin" */ = { + D1320AA0D1320AA0D1320AB4 /* Build configuration list for PBXNativeTarget "ProgramaDockTilePlugin" */ = { isa = XCConfigurationList; buildConfigurations = ( D1320AA0D1320AA0D1320AB2 /* Debug */, diff --git a/Resources/Info.plist b/Resources/Info.plist index f336e9db68a..0765fb74ffe 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -36,7 +36,7 @@ CFBundleVersion $(CURRENT_PROJECT_VERSION) NSDockTilePlugIn - CmuxDockTilePlugin.plugin + ProgramaDockTilePlugin.plugin LSApplicationCategoryType public.app-category.developer-tools NSHumanReadableCopyright diff --git a/Sources/AppIconDockTilePlugin.swift b/Sources/AppIconDockTilePlugin.swift index 4ee6aca7c2d..21097625ee5 100644 --- a/Sources/AppIconDockTilePlugin.swift +++ b/Sources/AppIconDockTilePlugin.swift @@ -1,7 +1,7 @@ import AppKit -private let cmuxAppIconDidChangeNotification = Notification.Name("com.darkroom.programa.appIconDidChange") -private let cmuxAppIconModeKey = "appIconMode" +private let programaAppIconDidChangeNotification = Notification.Name("com.darkroom.programa.appIconDidChange") +private let programaAppIconModeKey = "appIconMode" private enum DockTileAppIconMode: String { case automatic @@ -24,10 +24,10 @@ private enum DockTileAppIconMode: String { } } -final class CmuxDockTilePlugin: NSObject, NSDockTilePlugIn { +final class ProgramaDockTilePlugin: NSObject, NSDockTilePlugIn { // The plugin can stay alive while the app remains in the Dock, even after quit. // Keep the state minimal and derive everything from the enclosing app bundle. - private let pluginBundle = Bundle(for: CmuxDockTilePlugin.self) + private let pluginBundle = Bundle(for: ProgramaDockTilePlugin.self) private var iconChangeObserver: NSObjectProtocol? deinit { @@ -46,7 +46,7 @@ final class CmuxDockTilePlugin: NSObject, NSDockTilePlugIn { updateDockTile(dockTile) iconChangeObserver = DistributedNotificationCenter.default().addObserver( - forName: cmuxAppIconDidChangeNotification, + forName: programaAppIconDidChangeNotification, object: nil, queue: nil ) { [weak self] _ in @@ -70,7 +70,7 @@ final class CmuxDockTilePlugin: NSObject, NSDockTilePlugIn { } private func updateDockTile(_ dockTile: NSDockTile) { - let mode = DockTileAppIconMode(defaultsValue: appDefaults?.string(forKey: cmuxAppIconModeKey)) + let mode = DockTileAppIconMode(defaultsValue: appDefaults?.string(forKey: programaAppIconModeKey)) guard let imageName = mode.imageName, let icon = appBundle?.image(forResource: imageName) else { dockTile.showDefaultAppIcon() From e9fccd9a81f48bd7eb9536d3a631d75c1b81a4c9 Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 12:05:34 -0300 Subject: [PATCH 08/23] =?UTF-8?q?refactor:=20rename=20AppleScript=20CmuxSc?= =?UTF-8?q?ript*=20=E2=86=92=20ProgramaScript*=20+=20rebrand=20sdef=20text?= =?UTF-8?q?=20(Phase=206b)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @objc(CmuxScript{Window,Tab,Terminal,InputTextCommand}) → Programa* in lockstep with the .sdef cocoa class refs; sdef dictionary/suite/description text cmux → Programa. AppleEvent codes (code="Cmux…") preserved (internal dispatch, invisible to users). Compiles; AppleScript dispatch to be manually verified. --- Resources/programa.sdef | 24 ++++++++++++------------ Sources/AppleScriptSupport.swift | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Resources/programa.sdef b/Resources/programa.sdef index b55edd4b72d..8c54adb7ab2 100644 --- a/Resources/programa.sdef +++ b/Resources/programa.sdef @@ -1,15 +1,15 @@ - - - + + + - + @@ -35,8 +35,8 @@ - - + + @@ -58,8 +58,8 @@ - - + + @@ -81,7 +81,7 @@ - + @@ -115,7 +115,7 @@ - + @@ -142,7 +142,7 @@ - + @@ -159,7 +159,7 @@ - + diff --git a/Sources/AppleScriptSupport.swift b/Sources/AppleScriptSupport.swift index b90db00aa41..5144d89e35f 100644 --- a/Sources/AppleScriptSupport.swift +++ b/Sources/AppleScriptSupport.swift @@ -229,7 +229,7 @@ extension NSApplication { } @MainActor -@objc(CmuxScriptWindow) +@objc(ProgramaScriptWindow) final class ScriptWindow: NSObject { let windowId: UUID @@ -362,7 +362,7 @@ final class ScriptWindow: NSObject { } @MainActor -@objc(CmuxScriptTab) +@objc(ProgramaScriptTab) final class ScriptTab: NSObject { let windowId: UUID let tabId: UUID @@ -501,7 +501,7 @@ final class ScriptTab: NSObject { } @MainActor -@objc(CmuxScriptTerminal) +@objc(ProgramaScriptTerminal) final class ScriptTerminal: NSObject { let workspaceId: UUID let terminalId: UUID @@ -661,7 +661,7 @@ final class ScriptTerminal: NSObject { } @MainActor -@objc(CmuxScriptInputTextCommand) +@objc(ProgramaScriptInputTextCommand) final class ScriptInputTextCommand: NSScriptCommand { override func performDefaultImplementation() -> Any? { guard NSApp.validateScript(command: self) else { return nil } From ac5fa129a5be4a60c9b3c0c01b6238f5472bf5f9 Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 12:12:29 -0300 Subject: [PATCH 09/23] =?UTF-8?q?refactor:=20rename=20browser=20JS=20bridg?= =?UTF-8?q?e=20=5F=5Fcmux*/cmux*=20handlers=20=E2=86=92=20=5F=5Fprograma*/?= =?UTF-8?q?programa*=20(Phase=206c)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lockstep rename across all 7 files that inject or handle the tokens: __cmux* page globals → __programa*, and the JS↔Swift message handlers cmuxIMEState, cmuxAddressBarFocusState, cmuxReactGrab → programa*. Internal app-owned bridge (both sides ship together). Compiles; browser IME/address-bar/ReactGrab to be runtime-verified. --- CLI/programa.swift | 22 +++--- Sources/AppDelegate.swift | 36 ++++----- Sources/Find/BrowserFindJavaScript.swift | 60 +++++++-------- Sources/Panels/BrowserPanel.swift | 94 ++++++++++++------------ Sources/Panels/BrowserPanelView.swift | 38 +++++----- Sources/Panels/ReactGrab.swift | 2 +- Sources/TerminalController.swift | 68 ++++++++--------- 7 files changed, 160 insertions(+), 160 deletions(-) diff --git a/CLI/programa.swift b/CLI/programa.swift index 916ffb1df7c..dc8ee3f7d77 100644 --- a/CLI/programa.swift +++ b/CLI/programa.swift @@ -5006,7 +5006,7 @@ struct CMUXCLI { let payload: [String: Any] = [ "app_version": remoteDaemonVersionString(from: info), "build": info["CFBundleVersion"] ?? NSNull(), - "commit": info["CMUXCommit"] ?? NSNull(), + "commit": info["ProgramaCommit"] ?? NSNull(), "manifest_present": manifest != nil, "release_tag": releaseTag, "release_url": manifest?.releaseURL ?? NSNull(), @@ -5406,7 +5406,7 @@ struct CMUXCLI { func displayBrowserValue(_ value: Any) -> String { if let dict = value as? [String: Any], - let type = dict["__cmux_t"] as? String, + let type = dict["__programa_t"] as? String, type == "undefined" { return "undefined" } @@ -13984,7 +13984,7 @@ struct CMUXCLI { private func versionSummary() -> String { let info = resolvedVersionInfo() - let commit = info["CMUXCommit"].flatMap { normalizedCommitHash($0) } + let commit = info["ProgramaCommit"].flatMap { normalizedCommitHash($0) } let baseSummary: String if let version = info["CFBundleShortVersionString"], let build = info["CFBundleVersion"] { baseSummary = "cmux \(version) (\(build))" @@ -14076,7 +14076,7 @@ struct CMUXCLI { let needsPlistFallback = info["CFBundleShortVersionString"] == nil || info["CFBundleVersion"] == nil || - info["CMUXCommit"] == nil + info["ProgramaCommit"] == nil if needsPlistFallback { for plistURL in candidateInfoPlistURLs() { guard let data = try? Data(contentsOf: plistURL), @@ -14089,7 +14089,7 @@ struct CMUXCLI { info.merge(parsed, uniquingKeysWith: { current, _ in current }) if info["CFBundleShortVersionString"] != nil, info["CFBundleVersion"] != nil, - info["CMUXCommit"] != nil { + info["ProgramaCommit"] != nil { break } } @@ -14098,14 +14098,14 @@ struct CMUXCLI { let needsProjectFallback = info["CFBundleShortVersionString"] == nil || info["CFBundleVersion"] == nil || - info["CMUXCommit"] == nil + info["ProgramaCommit"] == nil if needsProjectFallback, let fromProject = versionInfoFromProjectFile() { info.merge(fromProject, uniquingKeysWith: { current, _ in current }) } - if info["CMUXCommit"] == nil, + if info["ProgramaCommit"] == nil, let commit = normalizedCommitHash(ProcessInfo.processInfo.environment["PROGRAMA_COMMIT"]) { - info["CMUXCommit"] = commit + info["ProgramaCommit"] = commit } return info @@ -14127,9 +14127,9 @@ struct CMUXCLI { info["CFBundleVersion"] = trimmed } } - if let commit = dictionary["CMUXCommit"] as? String, + if let commit = dictionary["ProgramaCommit"] as? String, let normalizedCommit = normalizedCommitHash(commit) { - info["CMUXCommit"] = normalizedCommit + info["ProgramaCommit"] = normalizedCommit } return info.isEmpty ? nil : info } @@ -14154,7 +14154,7 @@ struct CMUXCLI { info["CFBundleVersion"] = build } if let commit = gitCommitHash(at: current) { - info["CMUXCommit"] = commit + info["ProgramaCommit"] = commit } if !info.isEmpty { return info diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 46cf67a3e8e..4bd87b85636 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -9223,11 +9223,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent secondaryCenterY: -1, activeId: active && typeof active.id === "string" ? active.id : "", activeTag: active && active.tagName ? active.tagName.toLowerCase() : "", - trackerInstalled: window.__cmuxAddressBarFocusTrackerInstalled === true, + trackerInstalled: window.__programaAddressBarFocusTrackerInstalled === true, trackedStateId: - window.__cmuxAddressBarFocusState && - typeof window.__cmuxAddressBarFocusState.id === "string" - ? window.__cmuxAddressBarFocusState.id + window.__programaAddressBarFocusState && + typeof window.__programaAddressBarFocusState.id === "string" + ? window.__programaAddressBarFocusState.id : "", readyState: String(document.readyState || "") }; @@ -9303,11 +9303,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent const selectionStart = typeof input.selectionStart === "number" ? input.selectionStart : null; const selectionEnd = typeof input.selectionEnd === "number" ? input.selectionEnd : null; if ( - !window.__cmuxAddressBarFocusState || - typeof window.__cmuxAddressBarFocusState.id !== "string" || - window.__cmuxAddressBarFocusState.id !== trackedFocusId + !window.__programaAddressBarFocusState || + typeof window.__programaAddressBarFocusState.id !== "string" || + window.__programaAddressBarFocusState.id !== trackedFocusId ) { - window.__cmuxAddressBarFocusState = { id: trackedFocusId, selectionStart, selectionEnd }; + window.__programaAddressBarFocusState = { id: trackedFocusId, selectionStart, selectionEnd }; } const secondaryRect = secondaryInput.getBoundingClientRect(); @@ -9330,17 +9330,17 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent secondaryCenterY, activeId: active && typeof active.id === "string" ? active.id : "", activeTag: active && active.tagName ? active.tagName.toLowerCase() : "", - trackerInstalled: window.__cmuxAddressBarFocusTrackerInstalled === true, + trackerInstalled: window.__programaAddressBarFocusTrackerInstalled === true, trackedStateId: - window.__cmuxAddressBarFocusState && - typeof window.__cmuxAddressBarFocusState.id === "string" - ? window.__cmuxAddressBarFocusState.id + window.__programaAddressBarFocusState && + typeof window.__programaAddressBarFocusState.id === "string" + ? window.__programaAddressBarFocusState.id : "", readyState: String(document.readyState || "") }; }; const ready = () => - window.__cmuxAddressBarFocusTrackerInstalled === true && + window.__programaAddressBarFocusTrackerInstalled === true && String(document.readyState || "") === "complete"; if (ready()) { @@ -9517,7 +9517,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent type: "", editable: "false", trackedFocusStateId: "", - focusTrackerInstalled: window.__cmuxAddressBarFocusTrackerInstalled === true ? "true" : "false" + focusTrackerInstalled: window.__programaAddressBarFocusTrackerInstalled === true ? "true" : "false" }; } const tag = (active.tagName || "").toLowerCase(); @@ -9532,12 +9532,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent type, editable: editable ? "true" : "false", trackedFocusStateId: - window.__cmuxAddressBarFocusState && - typeof window.__cmuxAddressBarFocusState.id === "string" - ? window.__cmuxAddressBarFocusState.id + window.__programaAddressBarFocusState && + typeof window.__programaAddressBarFocusState.id === "string" + ? window.__programaAddressBarFocusState.id : "", focusTrackerInstalled: - window.__cmuxAddressBarFocusTrackerInstalled === true ? "true" : "false" + window.__programaAddressBarFocusTrackerInstalled === true ? "true" : "false" }; } catch (_) { return { diff --git a/Sources/Find/BrowserFindJavaScript.swift b/Sources/Find/BrowserFindJavaScript.swift index c664bdc6969..69c73f378fb 100644 --- a/Sources/Find/BrowserFindJavaScript.swift +++ b/Sources/Find/BrowserFindJavaScript.swift @@ -14,8 +14,8 @@ enum BrowserFindJavaScript { let escaped = jsStringEscape(query) return """ (() => { - const MARK_CLASS = '__cmux-find'; - const CURRENT_CLASS = '__cmux-find-current'; + const MARK_CLASS = '__programa-find'; + const CURRENT_CLASS = '__programa-find-current'; // Remove previous highlights first. \(clearBody) @@ -79,8 +79,8 @@ enum BrowserFindJavaScript { parent.replaceChild(frag, node); } - window.__cmuxFindMatches = matches; - window.__cmuxFindIndex = 0; + window.__programaFindMatches = matches; + window.__programaFindIndex = 0; if (matches.length > 0) { matches[0].classList.add(CURRENT_CLASS); @@ -88,12 +88,12 @@ enum BrowserFindJavaScript { } // Inject highlight styles if not already present. - if (!document.getElementById('__cmux-find-style')) { + if (!document.getElementById('__programa-find-style')) { const style = document.createElement('style'); - style.id = '__cmux-find-style'; + style.id = '__programa-find-style'; style.textContent = ` - mark.__cmux-find { background: #facc15; color: #000; border-radius: 2px; } - mark.__cmux-find.__cmux-find-current { background: #f97316; color: #fff; } + mark.__programa-find { background: #facc15; color: #000; border-radius: 2px; } + mark.__programa-find.__programa-find-current { background: #f97316; color: #fff; } `; document.head.appendChild(style); } @@ -107,24 +107,24 @@ enum BrowserFindJavaScript { static func nextScript() -> String { """ (() => { - const matches = window.__cmuxFindMatches || []; + const matches = window.__programaFindMatches || []; if (matches.length === 0) return JSON.stringify({ total: 0, current: 0 }); - let idx = window.__cmuxFindIndex || 0; + let idx = window.__programaFindIndex || 0; if (!matches[idx] || !matches[idx].isConnected) { - window.__cmuxFindMatches = []; - window.__cmuxFindIndex = 0; + window.__programaFindMatches = []; + window.__programaFindIndex = 0; return JSON.stringify({ total: 0, current: 0 }); } - matches[idx].classList.remove('__cmux-find-current'); + matches[idx].classList.remove('__programa-find-current'); idx = (idx + 1) % matches.length; if (!matches[idx] || !matches[idx].isConnected) { - window.__cmuxFindMatches = []; - window.__cmuxFindIndex = 0; + window.__programaFindMatches = []; + window.__programaFindIndex = 0; return JSON.stringify({ total: 0, current: 0 }); } - matches[idx].classList.add('__cmux-find-current'); + matches[idx].classList.add('__programa-find-current'); matches[idx].scrollIntoView({ block: 'center', behavior: 'smooth' }); - window.__cmuxFindIndex = idx; + window.__programaFindIndex = idx; return JSON.stringify({ total: matches.length, current: idx }); })() """ @@ -134,24 +134,24 @@ enum BrowserFindJavaScript { static func previousScript() -> String { """ (() => { - const matches = window.__cmuxFindMatches || []; + const matches = window.__programaFindMatches || []; if (matches.length === 0) return JSON.stringify({ total: 0, current: 0 }); - let idx = window.__cmuxFindIndex || 0; + let idx = window.__programaFindIndex || 0; if (!matches[idx] || !matches[idx].isConnected) { - window.__cmuxFindMatches = []; - window.__cmuxFindIndex = 0; + window.__programaFindMatches = []; + window.__programaFindIndex = 0; return JSON.stringify({ total: 0, current: 0 }); } - matches[idx].classList.remove('__cmux-find-current'); + matches[idx].classList.remove('__programa-find-current'); idx = (idx - 1 + matches.length) % matches.length; if (!matches[idx] || !matches[idx].isConnected) { - window.__cmuxFindMatches = []; - window.__cmuxFindIndex = 0; + window.__programaFindMatches = []; + window.__programaFindIndex = 0; return JSON.stringify({ total: 0, current: 0 }); } - matches[idx].classList.add('__cmux-find-current'); + matches[idx].classList.add('__programa-find-current'); matches[idx].scrollIntoView({ block: 'center', behavior: 'smooth' }); - window.__cmuxFindIndex = idx; + window.__programaFindIndex = idx; return JSON.stringify({ total: matches.length, current: idx }); })() """ @@ -162,9 +162,9 @@ enum BrowserFindJavaScript { """ (() => { \(clearBody) - window.__cmuxFindMatches = []; - window.__cmuxFindIndex = 0; - const style = document.getElementById('__cmux-find-style'); + window.__programaFindMatches = []; + window.__programaFindIndex = 0; + const style = document.getElementById('__programa-find-style'); if (style) style.remove(); return 'ok'; })() @@ -175,7 +175,7 @@ enum BrowserFindJavaScript { /// JS snippet (no wrapping IIFE) that removes existing mark highlights. private static let clearBody = """ - document.querySelectorAll('mark.__cmux-find').forEach(mark => { + document.querySelectorAll('mark.__programa-find').forEach(mark => { const parent = mark.parentNode; if (!parent) return; const text = document.createTextNode(mark.textContent || ''); diff --git a/Sources/Panels/BrowserPanel.swift b/Sources/Panels/BrowserPanel.swift index 656ddad358b..c78fc755429 100644 --- a/Sources/Panels/BrowserPanel.swift +++ b/Sources/Panels/BrowserPanel.swift @@ -1746,19 +1746,19 @@ final class BrowserPanel: Panel, ObservableObject { static let telemetryHookBootstrapScriptSource = """ (() => { - if (window.__cmuxHooksInstalled) return true; - window.__cmuxHooksInstalled = true; + if (window.__programaHooksInstalled) return true; + window.__programaHooksInstalled = true; - window.__cmuxConsoleLog = window.__cmuxConsoleLog || []; + window.__programaConsoleLog = window.__programaConsoleLog || []; const __pushConsole = (level, args) => { try { const text = Array.from(args || []).map((x) => { if (typeof x === 'string') return x; try { return JSON.stringify(x); } catch (_) { return String(x); } }).join(' '); - window.__cmuxConsoleLog.push({ level, text, timestamp_ms: Date.now() }); - if (window.__cmuxConsoleLog.length > 512) { - window.__cmuxConsoleLog.splice(0, window.__cmuxConsoleLog.length - 512); + window.__programaConsoleLog.push({ level, text, timestamp_ms: Date.now() }); + if (window.__programaConsoleLog.length > 512) { + window.__programaConsoleLog.splice(0, window.__programaConsoleLog.length - 512); } } catch (_) {} }; @@ -1772,16 +1772,16 @@ final class BrowserPanel: Panel, ObservableObject { }; } - window.__cmuxErrorLog = window.__cmuxErrorLog || []; + window.__programaErrorLog = window.__programaErrorLog || []; window.addEventListener('error', (ev) => { try { const message = String((ev && ev.message) || ''); const source = String((ev && ev.filename) || ''); const line = Number((ev && ev.lineno) || 0); const col = Number((ev && ev.colno) || 0); - window.__cmuxErrorLog.push({ message, source, line, column: col, timestamp_ms: Date.now() }); - if (window.__cmuxErrorLog.length > 512) { - window.__cmuxErrorLog.splice(0, window.__cmuxErrorLog.length - 512); + window.__programaErrorLog.push({ message, source, line, column: col, timestamp_ms: Date.now() }); + if (window.__programaErrorLog.length > 512) { + window.__programaErrorLog.splice(0, window.__programaErrorLog.length - 512); } } catch (_) {} }); @@ -1789,9 +1789,9 @@ final class BrowserPanel: Panel, ObservableObject { try { const reason = ev && ev.reason; const message = typeof reason === 'string' ? reason : (reason && reason.message ? String(reason.message) : String(reason)); - window.__cmuxErrorLog.push({ message, source: 'unhandledrejection', line: 0, column: 0, timestamp_ms: Date.now() }); - if (window.__cmuxErrorLog.length > 512) { - window.__cmuxErrorLog.splice(0, window.__cmuxErrorLog.length - 512); + window.__programaErrorLog.push({ message, source: 'unhandledrejection', line: 0, column: 0, timestamp_ms: Date.now() }); + if (window.__programaErrorLog.length > 512) { + window.__programaErrorLog.splice(0, window.__programaErrorLog.length - 512); } } catch (_) {} }); @@ -1802,20 +1802,20 @@ final class BrowserPanel: Panel, ObservableObject { static let dialogTelemetryHookBootstrapScriptSource = """ (() => { - if (window.__cmuxDialogHooksInstalled) return true; - window.__cmuxDialogHooksInstalled = true; + if (window.__programaDialogHooksInstalled) return true; + window.__programaDialogHooksInstalled = true; - window.__cmuxDialogQueue = window.__cmuxDialogQueue || []; - window.__cmuxDialogDefaults = window.__cmuxDialogDefaults || { confirm: false, prompt: null }; + window.__programaDialogQueue = window.__programaDialogQueue || []; + window.__programaDialogDefaults = window.__programaDialogDefaults || { confirm: false, prompt: null }; const __pushDialog = (type, message, defaultText) => { - window.__cmuxDialogQueue.push({ + window.__programaDialogQueue.push({ type, message: String(message || ''), default_text: defaultText == null ? null : String(defaultText), timestamp_ms: Date.now() }); - if (window.__cmuxDialogQueue.length > 128) { - window.__cmuxDialogQueue.splice(0, window.__cmuxDialogQueue.length - 128); + if (window.__programaDialogQueue.length > 128) { + window.__programaDialogQueue.splice(0, window.__programaDialogQueue.length - 128); } }; @@ -1824,11 +1824,11 @@ final class BrowserPanel: Panel, ObservableObject { }; window.confirm = function(message) { __pushDialog('confirm', message, null); - return !!window.__cmuxDialogDefaults.confirm; + return !!window.__programaDialogDefaults.confirm; }; window.prompt = function(message, defaultValue) { __pushDialog('prompt', message, defaultValue == null ? null : defaultValue); - const v = window.__cmuxDialogDefaults.prompt; + const v = window.__programaDialogDefaults.prompt; if (v === null || v === undefined) { return defaultValue == null ? '' : String(defaultValue); } @@ -1908,12 +1908,12 @@ final class BrowserPanel: Panel, ObservableObject { (() => { try { const syncState = (state) => { - window.__cmuxAddressBarFocusState = state; + window.__programaAddressBarFocusState = state; try { if (window.top && window.top !== window) { - window.top.postMessage({ cmuxAddressBarFocusState: state }, "*"); + window.top.postMessage({ programaAddressBarFocusState: state }, "*"); } else if (window.top) { - window.top.__cmuxAddressBarFocusState = state; + window.top.__programaAddressBarFocusState = state; } } catch (_) {} }; @@ -1956,27 +1956,27 @@ final class BrowserPanel: Panel, ObservableObject { private static let addressBarFocusTrackingBootstrapScript = """ (() => { try { - if (window.__cmuxAddressBarFocusTrackerInstalled) return true; - window.__cmuxAddressBarFocusTrackerInstalled = true; + if (window.__programaAddressBarFocusTrackerInstalled) return true; + window.__programaAddressBarFocusTrackerInstalled = true; const syncState = (state) => { - window.__cmuxAddressBarFocusState = state; + window.__programaAddressBarFocusState = state; try { if (window.top && window.top !== window) { - window.top.postMessage({ cmuxAddressBarFocusState: state }, "*"); + window.top.postMessage({ programaAddressBarFocusState: state }, "*"); } else if (window.top) { - window.top.__cmuxAddressBarFocusState = state; + window.top.__programaAddressBarFocusState = state; } } catch (_) {} }; - if (window.top === window && !window.__cmuxAddressBarFocusMessageBridgeInstalled) { - window.__cmuxAddressBarFocusMessageBridgeInstalled = true; + if (window.top === window && !window.__programaAddressBarFocusMessageBridgeInstalled) { + window.__programaAddressBarFocusMessageBridgeInstalled = true; window.addEventListener("message", (ev) => { try { const data = ev ? ev.data : null; - if (!data || !Object.prototype.hasOwnProperty.call(data, "cmuxAddressBarFocusState")) return; - window.__cmuxAddressBarFocusState = data.cmuxAddressBarFocusState || null; + if (!data || !Object.prototype.hasOwnProperty.call(data, "programaAddressBarFocusState")) return; + window.__programaAddressBarFocusState = data.programaAddressBarFocusState || null; } catch (_) {} }, true); } @@ -2042,15 +2042,15 @@ final class BrowserPanel: Panel, ObservableObject { """ /// JS bridge that posts `compositionstart`/`compositionend` events to the native layer - /// via `webkit.messageHandlers.cmuxIMEState`. This lets the native `performKeyEquivalent` + /// via `webkit.messageHandlers.programaIMEState`. This lets the native `performKeyEquivalent` /// detect when an Enter key is committing a CJK composition rather than submitting a form, /// even though WKWebView clears marked text before the key event fires (#2626). private static let imeCompositionTrackingScript = """ (() => { try { - if (window.__cmuxIMETrackerInstalled) return; - window.__cmuxIMETrackerInstalled = true; - const handler = window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cmuxIMEState; + if (window.__programaIMETrackerInstalled) return; + window.__programaIMETrackerInstalled = true; + const handler = window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.programaIMEState; if (!handler) return; document.addEventListener('compositionstart', () => { handler.postMessage({ composing: true }); @@ -2062,7 +2062,7 @@ final class BrowserPanel: Panel, ObservableObject { })(); """ - private static let imeCompositionHandlerName = "cmuxIMEState" + private static let imeCompositionHandlerName = "programaIMEState" private func setupIMECompositionTracking(for webView: ProgramaWebView) { let handler = IMECompositionMessageHandler { [weak webView] composing in @@ -2085,23 +2085,23 @@ final class BrowserPanel: Panel, ObservableObject { (() => { try { const readState = () => { - let state = window.__cmuxAddressBarFocusState; + let state = window.__programaAddressBarFocusState; try { if ((!state || typeof state.id !== "string" || !state.id) && - window.top && window.top.__cmuxAddressBarFocusState) { - state = window.top.__cmuxAddressBarFocusState; + window.top && window.top.__programaAddressBarFocusState) { + state = window.top.__programaAddressBarFocusState; } } catch (_) {} return state; }; const clearState = () => { - window.__cmuxAddressBarFocusState = null; + window.__programaAddressBarFocusState = null; try { if (window.top && window.top !== window) { - window.top.postMessage({ cmuxAddressBarFocusState: null }, "*"); + window.top.postMessage({ programaAddressBarFocusState: null }, "*"); } else if (window.top) { - window.top.__cmuxAddressBarFocusState = null; + window.top.__programaAddressBarFocusState = null; } } catch (_) {} }; @@ -2303,7 +2303,7 @@ final class BrowserPanel: Panel, ObservableObject { var reactGrabMessageHandler: ReactGrabMessageHandler? var pendingReactGrabReturnTargetPanelId: UUID? var pendingReactGrabRoundTripToken: String? - let reactGrabBridgeSessionUpdaterName = "__cmuxReactGrabBridgeSync_\(UUID().uuidString.replacingOccurrences(of: "-", with: ""))" + let reactGrabBridgeSessionUpdaterName = "__programaReactGrabBridgeSync_\(UUID().uuidString.replacingOccurrences(of: "-", with: ""))" private var preferredDeveloperToolsPresentation: DeveloperToolsPresentation = .unknown private var forceDeveloperToolsRefreshOnNextAttach: Bool = false private var developerToolsRestoreRetryWorkItem: DispatchWorkItem? @@ -2571,7 +2571,7 @@ final class BrowserPanel: Panel, ObservableObject { // Keep browser console/error/dialog telemetry active from document start on every navigation. // Main frame only — injecting into cross-origin iframes causes CAPTCHA providers // (reCAPTCHA, hCaptcha, Cloudflare Turnstile) to detect the overridden console.* - // methods and __cmux* globals as environment tampering, failing the challenge. + // methods and __programa* globals as environment tampering, failing the challenge. configuration.userContentController.addUserScript( WKUserScript( source: Self.telemetryHookBootstrapScriptSource, diff --git a/Sources/Panels/BrowserPanelView.swift b/Sources/Panels/BrowserPanelView.swift index 5b6c1b7d9a4..8e64f5f2f46 100644 --- a/Sources/Panels/BrowserPanelView.swift +++ b/Sources/Panels/BrowserPanelView.swift @@ -4573,14 +4573,14 @@ struct WebViewRepresentable: NSViewRepresentable { if (typeof WI === "undefined") return null; const allowSideDock = \(sideDockAllowedLiteral); - if (!WI.__cmuxOriginalUpdateDockNavigationItems && typeof WI._updateDockNavigationItems === "function") - WI.__cmuxOriginalUpdateDockNavigationItems = WI._updateDockNavigationItems; - if (!WI.__cmuxOriginalDockLeft && typeof WI._dockLeft === "function") - WI.__cmuxOriginalDockLeft = WI._dockLeft; - if (!WI.__cmuxOriginalDockRight && typeof WI._dockRight === "function") - WI.__cmuxOriginalDockRight = WI._dockRight; - if (!WI.__cmuxOriginalTogglePreviousDockConfiguration && typeof WI._togglePreviousDockConfiguration === "function") - WI.__cmuxOriginalTogglePreviousDockConfiguration = WI._togglePreviousDockConfiguration; + if (!WI.__programaOriginalUpdateDockNavigationItems && typeof WI._updateDockNavigationItems === "function") + WI.__programaOriginalUpdateDockNavigationItems = WI._updateDockNavigationItems; + if (!WI.__programaOriginalDockLeft && typeof WI._dockLeft === "function") + WI.__programaOriginalDockLeft = WI._dockLeft; + if (!WI.__programaOriginalDockRight && typeof WI._dockRight === "function") + WI.__programaOriginalDockRight = WI._dockRight; + if (!WI.__programaOriginalTogglePreviousDockConfiguration && typeof WI._togglePreviousDockConfiguration === "function") + WI.__programaOriginalTogglePreviousDockConfiguration = WI._togglePreviousDockConfiguration; function callOriginal(fn, event) { return typeof fn === "function" ? fn.call(WI, event) : null; } @@ -4594,34 +4594,34 @@ struct WebViewRepresentable: NSViewRepresentable { } } function enforceDockControls() { - const disallowSideDock = !WI.__cmuxAllowSideDock; + const disallowSideDock = !WI.__programaAllowSideDock; updateButton(WI._dockLeftTabBarButton, disallowSideDock || WI.dockConfiguration === WI.DockConfiguration.Left); updateButton(WI._dockRightTabBarButton, disallowSideDock || WI.dockConfiguration === WI.DockConfiguration.Right); } - WI.__cmuxAllowSideDock = allowSideDock; + WI.__programaAllowSideDock = allowSideDock; WI._dockLeft = function(event) { - if (!WI.__cmuxAllowSideDock) + if (!WI.__programaAllowSideDock) return callOriginal(WI._dockBottom, event); - return callOriginal(WI.__cmuxOriginalDockLeft, event); + return callOriginal(WI.__programaOriginalDockLeft, event); }; WI._dockRight = function(event) { - if (!WI.__cmuxAllowSideDock) + if (!WI.__programaAllowSideDock) return callOriginal(WI._dockBottom, event); - return callOriginal(WI.__cmuxOriginalDockRight, event); + return callOriginal(WI.__programaOriginalDockRight, event); }; WI._togglePreviousDockConfiguration = function(event) { const previousSideDock = WI._previousDockConfiguration === WI.DockConfiguration.Left || WI._previousDockConfiguration === WI.DockConfiguration.Right; - if (!WI.__cmuxAllowSideDock && previousSideDock) + if (!WI.__programaAllowSideDock && previousSideDock) return callOriginal(WI._dockBottom, event); - return callOriginal(WI.__cmuxOriginalTogglePreviousDockConfiguration, event); + return callOriginal(WI.__programaOriginalTogglePreviousDockConfiguration, event); }; WI._updateDockNavigationItems = function(...args) { - if (typeof WI.__cmuxOriginalUpdateDockNavigationItems === "function") - WI.__cmuxOriginalUpdateDockNavigationItems.apply(WI, args); + if (typeof WI.__programaOriginalUpdateDockNavigationItems === "function") + WI.__programaOriginalUpdateDockNavigationItems.apply(WI, args); enforceDockControls(); }; WI._updateDockNavigationItems(); - return WI.__cmuxAllowSideDock; + return WI.__programaAllowSideDock; })(); """, completionHandler: nil diff --git a/Sources/Panels/ReactGrab.swift b/Sources/Panels/ReactGrab.swift index 46327abb824..9e2385ba6a7 100644 --- a/Sources/Panels/ReactGrab.swift +++ b/Sources/Panels/ReactGrab.swift @@ -143,7 +143,7 @@ enum ReactGrabScriptLoader { // MARK: - WKScriptMessageHandler -private let reactGrabMessageHandlerName = "cmuxReactGrab" +private let reactGrabMessageHandlerName = "programaReactGrab" enum ReactGrabBridgeMessage { case stateChange(isActive: Bool) diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index d5f143c5600..f76502e14d2 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -184,8 +184,8 @@ class TerminalController { private final class V2BrowserUndefinedSentinel {} - private static let v2BrowserEvalEnvelopeTypeKey = "__cmux_t" - private static let v2BrowserEvalEnvelopeValueKey = "__cmux_v" + private static let v2BrowserEvalEnvelopeTypeKey = "__programa_t" + private static let v2BrowserEvalEnvelopeValueKey = "__programa_v" private static let v2BrowserEvalEnvelopeTypeUndefined = "undefined" private static let v2BrowserEvalEnvelopeTypeValue = "value" @@ -7265,7 +7265,7 @@ class TerminalController { let timeout = Double(timeoutMs) / 1000.0 let waitScript = """ (() => { - const __cmuxEvaluate = () => { + const __programaEvaluate = () => { try { return !!(\(conditionScript)); } catch (_) { @@ -7273,7 +7273,7 @@ class TerminalController { } }; - if (__cmuxEvaluate()) { + if (__programaEvaluate()) { return true; } @@ -7291,7 +7291,7 @@ class TerminalController { resolve(value); }; const recheck = () => { - if (__cmuxEvaluate()) { + if (__programaEvaluate()) { finish(true); } }; @@ -7392,16 +7392,16 @@ class TerminalController { if let frameSelector = v2BrowserCurrentFrameSelector(surfaceId: surfaceId) { let selectorLiteral = v2JSONLiteral(frameSelector) framePrelude = """ - let __cmuxDoc = document; + let __programaDoc = document; try { - const __cmuxFrame = document.querySelector(\(selectorLiteral)); - if (__cmuxFrame && __cmuxFrame.contentDocument) { - __cmuxDoc = __cmuxFrame.contentDocument; + const __programaFrame = document.querySelector(\(selectorLiteral)); + if (__programaFrame && __programaFrame.contentDocument) { + __programaDoc = __programaFrame.contentDocument; } } catch (_) {} """ } else { - framePrelude = "const __cmuxDoc = document;" + framePrelude = "const __programaDoc = document;" } let executionBlock: String @@ -7414,24 +7414,24 @@ class TerminalController { let asyncFunctionBody = """ \(framePrelude) - const __cmuxMaybeAwait = async (__r) => { + const __programaMaybeAwait = async (__r) => { if (__r !== null && (typeof __r === 'object' || typeof __r === 'function') && typeof __r.then === 'function') { return await __r; } return __r; }; - const __cmuxEvalInFrame = async function() { - const document = __cmuxDoc; + const __programaEvalInFrame = async function() { + const document = __programaDoc; \(executionBlock) - const __value = await __cmuxMaybeAwait(__r); + const __value = await __programaMaybeAwait(__r); return { - __cmux_t: (typeof __value === 'undefined') ? 'undefined' : 'value', - __cmux_v: __value + __programa_t: (typeof __value === 'undefined') ? 'undefined' : 'value', + __programa_v: __value }; }; - return await __cmuxEvalInFrame(); + return await __programaEvalInFrame(); """ var rawResult: V2JavaScriptResult @@ -7542,7 +7542,7 @@ class TerminalController { let injector = """ (() => { - window.__cmuxInitScriptsApplied = window.__cmuxInitScriptsApplied || { scripts: [], styles: [] }; + window.__programaInitScriptsApplied = window.__programaInitScriptsApplied || { scripts: [], styles: [] }; return true; })() """ @@ -9188,7 +9188,7 @@ class TerminalController { return v2BrowserWithPanel(params: params) { _, ws, surfaceId, browserPanel in let script = """ (() => { - const __cmuxCssPath = (el) => { + const __programaCssPath = (el) => { if (!el || el.nodeType !== 1) return null; if (el.id) return '#' + CSS.escape(el.id); const parts = []; @@ -9213,17 +9213,17 @@ class TerminalController { return parts.join(' > '); }; - const __cmuxFound = (() => { + const __programaFound = (() => { \(finderBody) })(); - if (!__cmuxFound) return { ok: false, error: 'not_found' }; - const selector = __cmuxCssPath(__cmuxFound); + if (!__programaFound) return { ok: false, error: 'not_found' }; + const selector = __programaCssPath(__programaFound); if (!selector) return { ok: false, error: 'not_found' }; return { ok: true, selector, - tag: String(__cmuxFound.tagName || '').toLowerCase(), - text: String(__cmuxFound.textContent || '').trim() + tag: String(__programaFound.tagName || '').toLowerCase(), + text: String(__programaFound.textContent || '').trim() }; })() """ @@ -9730,19 +9730,19 @@ class TerminalController { let textLiteral = text.map(v2JSONLiteral) ?? "null" let script = """ (() => { - const q = window.__cmuxDialogQueue || []; + const q = window.__programaDialogQueue || []; if (!q.length) return { ok: false, error: 'not_found' }; const entry = q.shift(); if (entry.type === 'confirm') { - window.__cmuxDialogDefaults = window.__cmuxDialogDefaults || { confirm: false, prompt: null }; - window.__cmuxDialogDefaults.confirm = \(acceptLiteral); + window.__programaDialogDefaults = window.__programaDialogDefaults || { confirm: false, prompt: null }; + window.__programaDialogDefaults.confirm = \(acceptLiteral); } if (entry.type === 'prompt') { - window.__cmuxDialogDefaults = window.__cmuxDialogDefaults || { confirm: false, prompt: null }; + window.__programaDialogDefaults = window.__programaDialogDefaults || { confirm: false, prompt: null }; if (\(acceptLiteral)) { - window.__cmuxDialogDefaults.prompt = \(textLiteral); + window.__programaDialogDefaults.prompt = \(textLiteral); } else { - window.__cmuxDialogDefaults.prompt = null; + window.__programaDialogDefaults.prompt = null; } } return { ok: true, dialog: entry, remaining: q.length }; @@ -10400,9 +10400,9 @@ class TerminalController { let clearLiteral = clear ? "true" : "false" let script = """ (() => { - const items = Array.isArray(window.__cmuxConsoleLog) ? window.__cmuxConsoleLog.slice() : []; + const items = Array.isArray(window.__programaConsoleLog) ? window.__programaConsoleLog.slice() : []; if (\(clearLiteral)) { - window.__cmuxConsoleLog = []; + window.__programaConsoleLog = []; } return { ok: true, items }; })() @@ -10438,9 +10438,9 @@ class TerminalController { let clearLiteral = clear ? "true" : "false" let script = """ (() => { - const items = Array.isArray(window.__cmuxErrorLog) ? window.__cmuxErrorLog.slice() : []; + const items = Array.isArray(window.__programaErrorLog) ? window.__programaErrorLog.slice() : []; if (\(clearLiteral)) { - window.__cmuxErrorLog = []; + window.__programaErrorLog = []; } return { ok: true, items }; })() From 6d2b03bfbfd480701ffdaa702b25b9fd38eece13 Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 12:12:29 -0300 Subject: [PATCH 10/23] =?UTF-8?q?refactor:=20rename=20Info.plist=20commit?= =?UTF-8?q?=20key=20CMUXCommit=20=E2=86=92=20ProgramaCommit=20(Phase=206e)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build-phase writer (Copy Resources PlistBuddy) + all reader sites (ProgramaApp, ContentView, CLI) in lockstep. Regenerated every build (no cross-version persistence), so no migration shim needed. Builds green. --- GhosttyTabs.xcodeproj/project.pbxproj | 2 +- Sources/ContentView.swift | 2 +- Sources/ProgramaApp.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/GhosttyTabs.xcodeproj/project.pbxproj b/GhosttyTabs.xcodeproj/project.pbxproj index 8389eb3ad1f..915903ed917 100644 --- a/GhosttyTabs.xcodeproj/project.pbxproj +++ b/GhosttyTabs.xcodeproj/project.pbxproj @@ -437,7 +437,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -euo pipefail\nDEST=\"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\nGHOSTTY_DEST=\"${DEST}/ghostty\"\nTERMINFO_DEST=\"${DEST}/terminfo\"\nCMUX_SHELL_DEST=\"${DEST}/shell-integration\"\nBIN_DEST=\"${DEST}/bin\"\nSRC_SHARE=\"${SRCROOT}/ghostty/zig-out/share\"\nGHOSTTY_SRC=\"${SRC_SHARE}/ghostty\"\nTERMINFO_SRC=\"${SRC_SHARE}/terminfo\"\nFALLBACK_GHOSTTY=\"${SRCROOT}/Resources/ghostty\"\nFALLBACK_TERMINFO=\"${SRCROOT}/Resources/ghostty/terminfo\"\nTERMINFO_OVERLAY=\"${SRCROOT}/Resources/terminfo-overlay\"\nCMUX_SHELL_SRC=\"${SRCROOT}/Resources/shell-integration\"\nCMUX_GHOSTTY_ZSH_SRC=\"${SRCROOT}/ghostty/src/shell-integration/zsh/ghostty-integration\"\nBUILD_GHOSTTY_HELPER=\"${SRCROOT}/scripts/build-ghostty-cli-helper.sh\"\nGHOSTTY_HELPER_DEST=\"${BIN_DEST}/ghostty\"\nif [ -d \"$GHOSTTY_SRC\" ]; then\n mkdir -p \"$GHOSTTY_DEST\"\n rsync -a --delete \"$GHOSTTY_SRC/\" \"$GHOSTTY_DEST/\"\nelif [ -d \"$FALLBACK_GHOSTTY\" ]; then\n mkdir -p \"$GHOSTTY_DEST\"\n rsync -a --delete \"$FALLBACK_GHOSTTY/\" \"$GHOSTTY_DEST/\"\nfi\nif [ -d \"$TERMINFO_SRC\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a --delete \"$TERMINFO_SRC/\" \"$TERMINFO_DEST/\"\nelif [ -d \"$FALLBACK_TERMINFO\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a --delete \"$FALLBACK_TERMINFO/\" \"$TERMINFO_DEST/\"\nfi\n# Overlay any cmux-specific terminfo adjustments.\n# This intentionally does not use --delete so we only patch specific entries.\nif [ -d \"$TERMINFO_OVERLAY\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a \"$TERMINFO_OVERLAY/\" \"$TERMINFO_DEST/\"\nfi\nif [ -d \"$CMUX_SHELL_SRC\" ]; then\n mkdir -p \"$CMUX_SHELL_DEST\"\n # Use '/.' so dotfiles like .zshenv/.zprofile are copied too.\n rsync -a \"$CMUX_SHELL_SRC/.\" \"$CMUX_SHELL_DEST/\"\nfi\nif [ -f \"$CMUX_GHOSTTY_ZSH_SRC\" ]; then\n mkdir -p \"$CMUX_SHELL_DEST\"\n rsync -a \"$CMUX_GHOSTTY_ZSH_SRC\" \"$CMUX_SHELL_DEST/ghostty-integration.zsh\"\nfi\nif [ ! -x \"$BUILD_GHOSTTY_HELPER\" ]; then\n echo \"error: missing Ghostty CLI helper build script at $BUILD_GHOSTTY_HELPER\" >&2\n exit 1\nfi\nARCHS_LIST=\" ${ARCHS:-} \"\nHAS_ARM64=0\nHAS_X86_64=0\nGHOSTTY_HELPER_TARGET=\"\"\ncase \"$ARCHS_LIST\" in\n *\" arm64 \"*) HAS_ARM64=1 ;;\nesac\ncase \"$ARCHS_LIST\" in\n *\" x86_64 \"*) HAS_X86_64=1 ;;\nesac\nif [ \"$HAS_ARM64\" -eq 1 ] && [ \"$HAS_X86_64\" -eq 1 ]; then\n \"$BUILD_GHOSTTY_HELPER\" --universal --output \"$GHOSTTY_HELPER_DEST\"\nelif [ \"$HAS_ARM64\" -eq 1 ]; then\n GHOSTTY_HELPER_TARGET=\"aarch64-macos\"\nelif [ \"$HAS_X86_64\" -eq 1 ]; then\n GHOSTTY_HELPER_TARGET=\"x86_64-macos\"\nfi\nif [ -n \"$GHOSTTY_HELPER_TARGET\" ]; then\n \"$BUILD_GHOSTTY_HELPER\" --target \"$GHOSTTY_HELPER_TARGET\" --output \"$GHOSTTY_HELPER_DEST\"\nelif [ \"$HAS_ARM64\" -eq 0 ] || [ \"$HAS_X86_64\" -eq 0 ]; then\n \"$BUILD_GHOSTTY_HELPER\" --output \"$GHOSTTY_HELPER_DEST\"\nfi\nif [ ! -x \"$GHOSTTY_HELPER_DEST\" ]; then\n echo \"error: Ghostty CLI helper was not created at $GHOSTTY_HELPER_DEST\" >&2\n exit 1\nfi\nINFO_PLIST=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\nCOMMIT=\"$(git -C \"${SRCROOT}\" rev-parse --short=9 HEAD 2>/dev/null || true)\"\nif [ -n \"$COMMIT\" ] && [ -f \"$INFO_PLIST\" ]; then\n /usr/libexec/PlistBuddy -c \"Set :CMUXCommit $COMMIT\" \"$INFO_PLIST\" >/dev/null 2>&1 || /usr/libexec/PlistBuddy -c \"Add :CMUXCommit string $COMMIT\" \"$INFO_PLIST\" >/dev/null 2>&1 || true\nfi\n"; + shellScript = "set -euo pipefail\nDEST=\"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\nGHOSTTY_DEST=\"${DEST}/ghostty\"\nTERMINFO_DEST=\"${DEST}/terminfo\"\nCMUX_SHELL_DEST=\"${DEST}/shell-integration\"\nBIN_DEST=\"${DEST}/bin\"\nSRC_SHARE=\"${SRCROOT}/ghostty/zig-out/share\"\nGHOSTTY_SRC=\"${SRC_SHARE}/ghostty\"\nTERMINFO_SRC=\"${SRC_SHARE}/terminfo\"\nFALLBACK_GHOSTTY=\"${SRCROOT}/Resources/ghostty\"\nFALLBACK_TERMINFO=\"${SRCROOT}/Resources/ghostty/terminfo\"\nTERMINFO_OVERLAY=\"${SRCROOT}/Resources/terminfo-overlay\"\nCMUX_SHELL_SRC=\"${SRCROOT}/Resources/shell-integration\"\nCMUX_GHOSTTY_ZSH_SRC=\"${SRCROOT}/ghostty/src/shell-integration/zsh/ghostty-integration\"\nBUILD_GHOSTTY_HELPER=\"${SRCROOT}/scripts/build-ghostty-cli-helper.sh\"\nGHOSTTY_HELPER_DEST=\"${BIN_DEST}/ghostty\"\nif [ -d \"$GHOSTTY_SRC\" ]; then\n mkdir -p \"$GHOSTTY_DEST\"\n rsync -a --delete \"$GHOSTTY_SRC/\" \"$GHOSTTY_DEST/\"\nelif [ -d \"$FALLBACK_GHOSTTY\" ]; then\n mkdir -p \"$GHOSTTY_DEST\"\n rsync -a --delete \"$FALLBACK_GHOSTTY/\" \"$GHOSTTY_DEST/\"\nfi\nif [ -d \"$TERMINFO_SRC\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a --delete \"$TERMINFO_SRC/\" \"$TERMINFO_DEST/\"\nelif [ -d \"$FALLBACK_TERMINFO\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a --delete \"$FALLBACK_TERMINFO/\" \"$TERMINFO_DEST/\"\nfi\n# Overlay any cmux-specific terminfo adjustments.\n# This intentionally does not use --delete so we only patch specific entries.\nif [ -d \"$TERMINFO_OVERLAY\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a \"$TERMINFO_OVERLAY/\" \"$TERMINFO_DEST/\"\nfi\nif [ -d \"$CMUX_SHELL_SRC\" ]; then\n mkdir -p \"$CMUX_SHELL_DEST\"\n # Use '/.' so dotfiles like .zshenv/.zprofile are copied too.\n rsync -a \"$CMUX_SHELL_SRC/.\" \"$CMUX_SHELL_DEST/\"\nfi\nif [ -f \"$CMUX_GHOSTTY_ZSH_SRC\" ]; then\n mkdir -p \"$CMUX_SHELL_DEST\"\n rsync -a \"$CMUX_GHOSTTY_ZSH_SRC\" \"$CMUX_SHELL_DEST/ghostty-integration.zsh\"\nfi\nif [ ! -x \"$BUILD_GHOSTTY_HELPER\" ]; then\n echo \"error: missing Ghostty CLI helper build script at $BUILD_GHOSTTY_HELPER\" >&2\n exit 1\nfi\nARCHS_LIST=\" ${ARCHS:-} \"\nHAS_ARM64=0\nHAS_X86_64=0\nGHOSTTY_HELPER_TARGET=\"\"\ncase \"$ARCHS_LIST\" in\n *\" arm64 \"*) HAS_ARM64=1 ;;\nesac\ncase \"$ARCHS_LIST\" in\n *\" x86_64 \"*) HAS_X86_64=1 ;;\nesac\nif [ \"$HAS_ARM64\" -eq 1 ] && [ \"$HAS_X86_64\" -eq 1 ]; then\n \"$BUILD_GHOSTTY_HELPER\" --universal --output \"$GHOSTTY_HELPER_DEST\"\nelif [ \"$HAS_ARM64\" -eq 1 ]; then\n GHOSTTY_HELPER_TARGET=\"aarch64-macos\"\nelif [ \"$HAS_X86_64\" -eq 1 ]; then\n GHOSTTY_HELPER_TARGET=\"x86_64-macos\"\nfi\nif [ -n \"$GHOSTTY_HELPER_TARGET\" ]; then\n \"$BUILD_GHOSTTY_HELPER\" --target \"$GHOSTTY_HELPER_TARGET\" --output \"$GHOSTTY_HELPER_DEST\"\nelif [ \"$HAS_ARM64\" -eq 0 ] || [ \"$HAS_X86_64\" -eq 0 ]; then\n \"$BUILD_GHOSTTY_HELPER\" --output \"$GHOSTTY_HELPER_DEST\"\nfi\nif [ ! -x \"$GHOSTTY_HELPER_DEST\" ]; then\n echo \"error: Ghostty CLI helper was not created at $GHOSTTY_HELPER_DEST\" >&2\n exit 1\nfi\nINFO_PLIST=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\nCOMMIT=\"$(git -C \"${SRCROOT}\" rev-parse --short=9 HEAD 2>/dev/null || true)\"\nif [ -n \"$COMMIT\" ] && [ -f \"$INFO_PLIST\" ]; then\n /usr/libexec/PlistBuddy -c \"Set :ProgramaCommit $COMMIT\" \"$INFO_PLIST\" >/dev/null 2>&1 || /usr/libexec/PlistBuddy -c \"Add :ProgramaCommit string $COMMIT\" \"$INFO_PLIST\" >/dev/null 2>&1 || true\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 7798dc2219e..8c7b61f4678 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -10303,7 +10303,7 @@ private struct FeedbackComposerAppMetadata { static var current: FeedbackComposerAppMetadata { let infoDictionary = Bundle.main.infoDictionary ?? [:] let env = ProcessInfo.processInfo.environment - let commit = (infoDictionary["CMUXCommit"] as? String).flatMap { value in + let commit = (infoDictionary["ProgramaCommit"] as? String).flatMap { value in value.isEmpty ? nil : value } ?? env["PROGRAMA_COMMIT"] diff --git a/Sources/ProgramaApp.swift b/Sources/ProgramaApp.swift index 57d959b0580..01583903ad8 100644 --- a/Sources/ProgramaApp.swift +++ b/Sources/ProgramaApp.swift @@ -2679,7 +2679,7 @@ private struct AboutPanelView: View { private var version: String? { Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String } private var build: String? { Bundle.main.infoDictionary?["CFBundleVersion"] as? String } private var commit: String? { - if let value = Bundle.main.infoDictionary?["CMUXCommit"] as? String, !value.isEmpty { + if let value = Bundle.main.infoDictionary?["ProgramaCommit"] as? String, !value.isEmpty { return value } let env = ProcessInfo.processInfo.environment["PROGRAMA_COMMIT"] ?? "" From 596394eae488b3db623cc584e7ba0deb6d8e8c57 Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 12:18:24 -0300 Subject: [PATCH 11/23] =?UTF-8?q?feat:=20migrate=20cmux=20UserDefaults=20k?= =?UTF-8?q?eys=20=E2=86=92=20programa=20with=20one-time=20shim=20(Phase=20?= =?UTF-8?q?6,=20UserDefaults)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a version-gated startup migration that copies every cmux-prefixed default to its programa-prefixed key (never deletes the legacy key), so no user preference is lost by the rebrand. Rename the standalone persisted keys (welcomeShown, surfacePoolEnabled, devMutate…, debugBG, focusDebug, keyLatencyProbe, shortcutMonitorTrace, typingTimingLogs, settingsFile.backups). Port keys (cmuxPortBase/Range) and socket-mode value (cmuxonly) deferred to their own contracts (shell-integration / socket-mode). Builds green. --- Sources/AppDelegate.swift | 6 ++--- Sources/GhosttyTerminalView.swift | 6 ++--- .../KeyboardShortcutSettingsFileStore.swift | 2 +- Sources/ProgramaApp.swift | 23 +++++++++++++++++-- Sources/SurfacePool.swift | 2 +- Sources/TabManager.swift | 2 +- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 4bd87b85636..b493e3878ed 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -79,14 +79,14 @@ enum ProgramaTypingTiming { return true } let defaults = UserDefaults.standard - return defaults.bool(forKey: "cmuxTypingTimingLogs") || defaults.bool(forKey: "cmuxKeyLatencyProbe") + return defaults.bool(forKey: "programaTypingTimingLogs") || defaults.bool(forKey: "programaKeyLatencyProbe") }() static let isVerboseProbeEnabled: Bool = { let environment = ProcessInfo.processInfo.environment if environment["PROGRAMA_KEY_LATENCY_PROBE"] == "1" { return true } - return UserDefaults.standard.bool(forKey: "cmuxKeyLatencyProbe") + return UserDefaults.standard.bool(forKey: "programaKeyLatencyProbe") }() private static let delayLogThresholdMs: Double = 6.0 private static let durationLogThresholdMs: Double = 1.0 @@ -10359,7 +10359,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent ProgramaTypingTiming.logEventDelay(path: "appMonitor", event: event) let shortcutMonitorTraceEnabled = ProcessInfo.processInfo.environment["PROGRAMA_SHORTCUT_MONITOR_TRACE"] == "1" - || UserDefaults.standard.bool(forKey: "cmuxShortcutMonitorTrace") + || UserDefaults.standard.bool(forKey: "programaShortcutMonitorTrace") if shortcutMonitorTraceEnabled { let frType = NSApp.keyWindow?.firstResponder.map { String(describing: type(of: $0)) } ?? "nil" dlog( diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index e248493ed72..4d067741b97 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -1343,7 +1343,7 @@ class GhosttyApp { if ProcessInfo.processInfo.environment["GHOSTTYTABS_DEBUG_BG"] == "1" { return true } - if UserDefaults.standard.bool(forKey: "cmuxDebugBG") { + if UserDefaults.standard.bool(forKey: "programaDebugBG") { return true } return UserDefaults.standard.bool(forKey: "GhosttyTabsDebugBG") @@ -4967,7 +4967,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { if ProcessInfo.processInfo.environment["PROGRAMA_FOCUS_DEBUG"] == "1" { return true } - return UserDefaults.standard.bool(forKey: "cmuxFocusDebug") + return UserDefaults.standard.bool(forKey: "programaFocusDebug") }() internal enum DropPlan: Equatable { case insertText(String) @@ -5104,7 +5104,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { if ProcessInfo.processInfo.environment["PROGRAMA_KEY_LATENCY_PROBE"] == "1" { return true } - return UserDefaults.standard.bool(forKey: "cmuxKeyLatencyProbe") + return UserDefaults.standard.bool(forKey: "programaKeyLatencyProbe") }() static var debugGhosttySurfaceKeyEventObserver: ((ghostty_input_key_s) -> Void)? #endif diff --git a/Sources/KeyboardShortcutSettingsFileStore.swift b/Sources/KeyboardShortcutSettingsFileStore.swift index d9b1a33f417..9cc09c46506 100644 --- a/Sources/KeyboardShortcutSettingsFileStore.swift +++ b/Sources/KeyboardShortcutSettingsFileStore.swift @@ -24,7 +24,7 @@ final class ProgramaSettingsFileStore { static let schemaURLString = "https://raw.githubusercontent.com/darkroomengineering/programa/main/Resources/settings.schema.json" private static let releaseBundleIdentifier = "com.darkroom.programa" - private static let backupsDefaultsKey = "cmux.settingsFile.backups.v1" + private static let backupsDefaultsKey = "programa.settingsFile.backups.v1" fileprivate static let trustedDirectoriesBackupIdentifier = "customCommands.trustedDirectories" fileprivate static let socketPasswordBackupIdentifier = "automation.socketPassword" diff --git a/Sources/ProgramaApp.swift b/Sources/ProgramaApp.swift index 01583903ad8..2593da64258 100644 --- a/Sources/ProgramaApp.swift +++ b/Sources/ProgramaApp.swift @@ -171,8 +171,11 @@ struct cmuxApp: App { let startupAppearance = AppearanceSettings.resolvedMode() Self.applyAppearance(startupAppearance) _tabManager = StateObject(wrappedValue: TabManager()) - // Migrate legacy and old-format socket mode values to the new enum. let defaults = UserDefaults.standard + // Rebrand: forward every legacy cmux-prefixed default to its programa key + // before anything reads the new keys, so existing users keep their prefs. + Self.migrateCmuxDefaultsToProgramaIfNeeded(defaults: defaults) + // Migrate legacy and old-format socket mode values to the new enum. if let stored = defaults.string(forKey: SocketControlSettings.appStorageKey) { let migrated = SocketControlSettings.migrateMode(stored) if migrated.rawValue != stored { @@ -268,6 +271,22 @@ struct cmuxApp: App { setenv(key, updated, 1) } + /// One-time rebrand migration: copy every `cmux`-prefixed UserDefaults value to + /// the corresponding `programa`-prefixed key. Version-gated so it runs once, and + /// never deletes the legacy keys (a downgrade still finds its old values). + private static func migrateCmuxDefaultsToProgramaIfNeeded(defaults: UserDefaults) { + let migrationKey = "programaDefaultsRebrandMigrationVersion" + let targetVersion = 1 + guard defaults.integer(forKey: migrationKey) < targetVersion else { return } + for (key, value) in defaults.dictionaryRepresentation() where key.hasPrefix("cmux") { + let newKey = "programa" + key.dropFirst("cmux".count) + if defaults.object(forKey: newKey) == nil { + defaults.set(value, forKey: newKey) + } + } + defaults.set(targetVersion, forKey: migrationKey) + } + private func migrateSidebarAppearanceDefaultsIfNeeded(defaults: UserDefaults) { let migrationKey = "sidebarAppearanceDefaultsVersion" let targetVersion = 1 @@ -3828,7 +3847,7 @@ enum ClaudeCodeIntegrationSettings { } enum WelcomeSettings { - static let shownKey = "cmuxWelcomeShown" + static let shownKey = "programaWelcomeShown" } enum TelemetrySettings { diff --git a/Sources/SurfacePool.swift b/Sources/SurfacePool.swift index 02dd896a34f..46e9514c1d4 100644 --- a/Sources/SurfacePool.swift +++ b/Sources/SurfacePool.swift @@ -34,7 +34,7 @@ final class SurfacePool { /// Whether the pool is enabled. Reads user default, defaulting to true. var isEnabled: Bool { - UserDefaults.standard.object(forKey: "cmuxSurfacePoolEnabled") as? Bool ?? true + UserDefaults.standard.object(forKey: "programaSurfacePoolEnabled") as? Bool ?? true } #if DEBUG diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index 23a8b264f58..4274e015931 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -1326,7 +1326,7 @@ class TabManager: ObservableObject { if let raw = env["PROGRAMA_DEV_MUTATE_WORKSPACE_SELECTION_DURING_CREATION"] { return raw == "1" || raw.caseInsensitiveCompare("true") == .orderedSame } - return UserDefaults.standard.bool(forKey: "cmuxDevMutateWorkspaceSelectionDuringCreation") + return UserDefaults.standard.bool(forKey: "programaDevMutateWorkspaceSelectionDuringCreation") }() guard isEnabled, let selectedTabId = snapshot.selectedTabId, From 34dea3ab32939cfde957e071e288c8783e416240 Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 12:19:23 -0300 Subject: [PATCH 12/23] docs: capture rebrand progress + precise remaining-contract map --- plans/rebrand-progress.md | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 plans/rebrand-progress.md diff --git a/plans/rebrand-progress.md b/plans/rebrand-progress.md new file mode 100644 index 00000000000..5990d37fc33 --- /dev/null +++ b/plans/rebrand-progress.md @@ -0,0 +1,74 @@ +# Rebrand cmux → Programa — progress & remaining map + +Branch: `feat/rebrand-cmux-to-programa`. Every commit below is **build-green** (compile-only +gate via `-scheme programa ... build`, or `build-for-testing` for the test phase). Runtime +contracts compile but need CI/manual verification before merge. + +## Done (11 commits, off origin/main) + +0. **docs** — cmux→Programa in docs, manaflow-ai→darkroomengineering, README promo cleanup. +1. **symbols** — `Cmux*`→`Programa*` (excl. AppleScript + DockTile files). +2. **source files** — 6 files `git mv` + pbxproj. +3. **test targets** — `cmuxTests`/`cmuxUITests`→`programaTests`/`programaUITests` + schemes' test labels. +4. **schemes/CLI target/scripts/CI** — schemes renamed, `programa-cli` target, bridging header, + scripts `-scheme`/DerivedData prefix, CI scheme+test-target refs. (`PRODUCT_NAME` was already `programa`.) +5. **config paths** — `~/.config/programa/` + `programa.json` with dual-read legacy fallback. +6a. **DockTile** — `CmuxDockTilePlugin`→`ProgramaDockTilePlugin` (class+target+product+Info.plist NSDockTilePlugIn). +6b. **AppleScript** — `@objc(CmuxScript*)`→`ProgramaScript*` + `.sdef` cocoa-class + user-facing text; AE `code=` preserved. +6c. **browser JS bridge** — `__cmux*`→`__programa*` + handlers (`cmuxIMEState`/`cmuxAddressBarFocusState`/`cmuxReactGrab`) across 7 files. +6e. **CMUXCommit** — Info.plist commit key → `ProgramaCommit` (build writer + readers; no shim, regenerated per build). +6-UserDefaults. **migration shim** — version-gated startup copy of every `cmux`-prefixed default → + `programa`-prefixed (never deletes legacy). Renamed standalone keys (welcomeShown, surfacePoolEnabled, + devMutate…, debugBG, focusDebug, keyLatencyProbe, shortcutMonitorTrace, typingTimingLogs, settingsFile.backups). + +## Remaining (each = its own build-gated commit; runtime = CI/manual) + +### 6d — shell-integration lockstep (LARGE) +- Files: `Resources/shell-integration/cmux-{bash,zsh}-integration.{bash,zsh}` (~100+ `cmux_*` shell + functions/vars, mostly internal to the scripts) + Swift/CLI refs in `GhosttyTerminalView.swift`, + `KeyboardShortcutSettingsFileStore.swift`, `ProgramaApp.swift`, `Workspace.swift`, `CLI/programa.swift`. +- Cross-boundary protocol tokens (rename BOTH sides in lockstep): `$cmux_port`, `$cmux_tty`, `$cmux_pid`, + `cmux_shell_dir`, `cmuxPortBase`/`cmuxPortRange` (injected window globals + `@AppStorage`), the + `cmux-{bash,zsh}-integration` **filenames** (+ pbxproj Copy-Resources phase `CMUX_SHELL_*` vars, line 440), + and any `cmux-integration.zsh` dest name. +- Approach: rename script filenames + `s/cmux_/programa_/g` inside the scripts, then rename every matching + token on the Swift/CLI side. `cmuxPortBase/Range` covered by the UserDefaults migration already shipped. +- Verify: `tests_v2` shell/port tests on CI + manual (open terminal, PR sidebar, port detection). + +### 6f — CLI command name + SSH (LARGE, 386 `cmux` refs in `CLI/programa.swift`) +- Binary is ALREADY `programa` (PRODUCT_NAME). Categorize the 386: + - user-facing help/usage strings `cmux ` → `programa` (command is already programa). + - remote SSH bootstrap: `~/.cmux/`→`~/.programa/` (issue #15 partially done — verify), `cmux_remote_bootstrap` + fn + remote `cmux` invocation → programa; socket paths (check already-programa). + - internal fn/var names → cosmetic. + - DO NOT blanket-sed (hits socket paths, env vars, remote tokens). Categorize per-occurrence. +- Verify: `tests_v2/test_ssh_remote_*.py` on CI. + +### 6g — user-facing brand strings (`Resources/Localizable.xcstrings` + a few Swift defaults) +- Brand noun `cmux`→`Programa` ("Quit cmux", "About cmux", "cmux CLI", notifications, error text). +- CLI usage strings inside xcstrings ("Usage: cmux claude-teams", "Install 'cmux' in PATH") → `programa` (lowercase command). +- EN + JA. Distinguish brand (Programa) vs command (programa) per string — NOT a blanket sed. + +### 6h — release artifacts +- `cmux.entitlements` → `programa.entitlements` (git mv + pbxproj `CODE_SIGN_ENTITLEMENTS` + `build-sign-upload.sh` + release/nightly yml). +- DMG names `cmux-nightly-macos*.dmg`/`cmux-release-*` → `programa-*` (release.yml, nightly.yml) so README's `programa-macos.dmg` link resolves. +- homebrew cask refs in `build-sign-upload.sh` (or delete per the board-sweep plan #7). + +### Analytics (keep + note) +- PostHog `"platform": "cmuxterm"` (`PostHogAnalytics.swift:219`) and `com.cmuxterm.*` queue labels — KEEP the + `cmuxterm` analytics value for historical continuity; add a comment. Queue labels are cosmetic (optional rename). + +### Bulk internal identifiers (SAFE but voluminous — do last) +- Remaining lowercase `cmux*` camelCase Swift symbols (`cmuxAccentColor`, `cmuxOwning*`, `cmuxConfigStore`, …) + and `cmux.*` os_log subsystems / dispatch-queue labels / in-process notification names. Pure internal, no + contract → compile-verified. Big mechanical pass; exclude URLs, the analytics value, and anything already + handled above. Consider delegating with a build gate. + +## Build recipe +``` +PROGRAMA_SKIP_ZIG_BUILD=1 xcodebuild -project GhosttyTabs.xcodeproj -scheme programa \ + -configuration Debug -destination 'platform=macOS' -derivedDataPath /tmp/programa-rebrand \ + PROGRAMA_SKIP_ZIG_BUILD=1 build 2>&1 | grep -iE "error:|BUILD (FAILED|SUCCEEDED)" +``` +Never blanket-sed lowercase `cmux` (hits URLs, shell vars, command name, socket paths, analytics). Keep +`com.darkroom.programa` bundle IDs. Don't touch `ghostty/`. From 75a23baf0c3ba713f0e3d32cbe09912a00096efb Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 12:26:50 -0300 Subject: [PATCH 13/23] fix(ci): update tests/ guard scripts to programa scheme (Phase 4 follow-up) test_ci_scheme_testaction_debug.sh referenced the old cmux.xcscheme path and test_bundled_ghostty_theme_picker_helper.sh used -scheme cmux; both broke after the Phase 4 scheme rename. Points them at programa.xcscheme / -scheme programa. --- tests/test_bundled_ghostty_theme_picker_helper.sh | 2 +- tests/test_ci_scheme_testaction_debug.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_bundled_ghostty_theme_picker_helper.sh b/tests/test_bundled_ghostty_theme_picker_helper.sh index f897302f6b1..b81c2c9a59c 100755 --- a/tests/test_bundled_ghostty_theme_picker_helper.sh +++ b/tests/test_bundled_ghostty_theme_picker_helper.sh @@ -23,7 +23,7 @@ rm -rf "$DERIVED_DATA_PATH" xcodebuild \ -project GhosttyTabs.xcodeproj \ - -scheme cmux \ + -scheme programa \ -configuration "$CONFIGURATION" \ -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ -disableAutomaticPackageResolution \ diff --git a/tests/test_ci_scheme_testaction_debug.sh b/tests/test_ci_scheme_testaction_debug.sh index 12347f31929..48809ede982 100755 --- a/tests/test_ci_scheme_testaction_debug.sh +++ b/tests/test_ci_scheme_testaction_debug.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -SCHEME_FILE="GhosttyTabs.xcodeproj/xcshareddata/xcschemes/cmux.xcscheme" +SCHEME_FILE="GhosttyTabs.xcodeproj/xcshareddata/xcschemes/programa.xcscheme" if [ ! -f "$SCHEME_FILE" ]; then echo "FAIL: Missing scheme file at $SCHEME_FILE" >&2 @@ -9,8 +9,8 @@ if [ ! -f "$SCHEME_FILE" ]; then fi if ! grep -q '&2 + echo "FAIL: programa scheme TestAction must use Debug build configuration for UI test setup hooks" >&2 exit 1 fi -echo "PASS: cmux scheme TestAction uses Debug" +echo "PASS: programa scheme TestAction uses Debug" From bf1523585be62375c7a9b95c2b5ff070781605ee Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 13:59:16 -0300 Subject: [PATCH 14/23] =?UTF-8?q?chore:=20rebrand=20release=20artifacts=20?= =?UTF-8?q?cmux=20=E2=86=92=20programa=20(Phase=206h)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename cmux.entitlements → programa.entitlements (git mv + the 3 signing refs in build-sign-upload.sh, release.yml, nightly.yml — pbxproj CODE_SIGN_ENTITLEMENTS is empty, so the Xcode build is unaffected). Rename nightly DMG/artifact names cmux-nightly-* → programa-nightly-* and the release dry-run artifact. Homebrew cask refs left as-is (external repo, deprecated per board cleanup). --- .github/workflows/nightly.yml | 16 ++++++++-------- .github/workflows/release.yml | 4 ++-- cmux.entitlements => programa.entitlements | 0 scripts/build-sign-upload.sh | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) rename cmux.entitlements => programa.entitlements (100%) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 6758c770ac4..d19fb246159 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -278,7 +278,7 @@ jobs: echo "NIGHTLY_MARKETING_VERSION=${NIGHTLY_MARKETING_VERSION}" >> "$GITHUB_ENV" echo "NIGHTLY_REMOTE_DAEMON_VERSION=${NIGHTLY_MARKETING_VERSION}" >> "$GITHUB_ENV" - NIGHTLY_DMG_IMMUTABLE="cmux-nightly-macos-${NIGHTLY_BUILD}.dmg" + NIGHTLY_DMG_IMMUTABLE="programa-nightly-macos-${NIGHTLY_BUILD}.dmg" echo "NIGHTLY_DMG_IMMUTABLE=${NIGHTLY_DMG_IMMUTABLE}" >> "$GITHUB_ENV" prepare_variant() { @@ -393,7 +393,7 @@ jobs: echo "Missing APPLE_SIGNING_IDENTITY secret" >&2 exit 1 fi - ENTITLEMENTS="cmux.entitlements" + ENTITLEMENTS="programa.entitlements" for APP_PATH in \ "build-universal/Build/Products/Release/Programa NIGHTLY.app" do @@ -471,7 +471,7 @@ jobs: notarize_and_package \ "build-universal/Build/Products/Release/Programa NIGHTLY.app" \ - "cmux-nightly-macos.dmg" \ + "programa-nightly-macos.dmg" \ "$NIGHTLY_DMG_IMMUTABLE" - name: Upload dSYMs to Sentry @@ -519,9 +519,9 @@ jobs: if: needs.decide.outputs.should_publish != 'true' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: - name: cmux-nightly-${{ needs.decide.outputs.short_sha }} + name: programa-nightly-${{ needs.decide.outputs.short_sha }} path: | - cmux-nightly-macos*.dmg + programa-nightly-macos*.dmg appcast.xml remote-daemon-assets/programad-remote-* appcast-universal.xml @@ -552,10 +552,10 @@ jobs: - feed `appcast.xml` - compatibility feed `appcast-universal.xml` for older universal nightlies - [Download cmux-nightly-macos.dmg](https://github.com/darkroomengineering/programa/releases/download/nightly/cmux-nightly-macos.dmg) + [Download programa-nightly-macos.dmg](https://github.com/darkroomengineering/programa/releases/download/nightly/programa-nightly-macos.dmg) files: | - cmux-nightly-macos-${{ github.run_id }}*.dmg - cmux-nightly-macos.dmg + programa-nightly-macos-${{ github.run_id }}*.dmg + programa-nightly-macos.dmg appcast.xml remote-daemon-assets/programad-remote-* appcast-universal.xml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ef61148af4..14c37e1680e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -278,7 +278,7 @@ jobs: exit 1 fi APP_PATH="build-universal/Build/Products/Release/Programa.app" - ENTITLEMENTS="cmux.entitlements" + ENTITLEMENTS="programa.entitlements" CLI_PATH="$APP_PATH/Contents/Resources/bin/programa" HELPER_PATH="$APP_PATH/Contents/Resources/bin/ghostty" if [ -f "$CLI_PATH" ]; then @@ -379,7 +379,7 @@ jobs: if: steps.guard_release_assets.outputs.skip_upload != 'true' && github.event_name == 'workflow_dispatch' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: - name: cmux-release-dry-run + name: programa-release-dry-run path: | programa-macos.dmg appcast.xml diff --git a/cmux.entitlements b/programa.entitlements similarity index 100% rename from cmux.entitlements rename to programa.entitlements diff --git a/scripts/build-sign-upload.sh b/scripts/build-sign-upload.sh index a3af23307a2..aed7ce27630 100755 --- a/scripts/build-sign-upload.sh +++ b/scripts/build-sign-upload.sh @@ -47,7 +47,7 @@ fi TAG="$1" SIGN_HASH="A050CC7E193C8221BDBA204E731B046CDCCC1B30" -ENTITLEMENTS="cmux.entitlements" +ENTITLEMENTS="programa.entitlements" APP_PATH="build/Build/Products/Release/Programa.app" # --- Pre-flight --- From 6f1026f22ebcf9a7b3e7ad5cdb5dcbedaab9fe48 Mon Sep 17 00:00:00 2001 From: arzafran Date: Wed, 1 Jul 2026 14:02:18 -0300 Subject: [PATCH 15/23] docs: update rebrand progress (6h release artifacts + CI-guard fix done) --- plans/rebrand-progress.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plans/rebrand-progress.md b/plans/rebrand-progress.md index 5990d37fc33..8a9d5addd25 100644 --- a/plans/rebrand-progress.md +++ b/plans/rebrand-progress.md @@ -21,6 +21,11 @@ contracts compile but need CI/manual verification before merge. `programa`-prefixed (never deletes legacy). Renamed standalone keys (welcomeShown, surfacePoolEnabled, devMutate…, debugBG, focusDebug, keyLatencyProbe, shortcutMonitorTrace, typingTimingLogs, settingsFile.backups). +6h. **release artifacts** — `cmux.entitlements`→`programa.entitlements` (git mv + 3 signing refs), nightly DMG/artifact names `cmux-nightly-*`→`programa-nightly-*`. Homebrew left (external/deprecated). +CI-fix. **tests/ guard scripts** — `test_ci_scheme_testaction_debug.sh` + `test_bundled_ghostty_theme_picker_helper.sh` pointed at the old `cmux.xcscheme`/`-scheme cmux` (Phase 4 miss). Fixed. + +**PR: #48** — CI green except `tests-build-and-lag` (known runner flake; re-running). + ## Remaining (each = its own build-gated commit; runtime = CI/manual) ### 6d — shell-integration lockstep (LARGE) From 0e67dd1cc769bb25e6172f88d61fd667a8b36a39 Mon Sep 17 00:00:00 2001 From: arzafran Date: Thu, 2 Jul 2026 10:30:46 -0300 Subject: [PATCH 16/23] =?UTF-8?q?ci:=20raise=20macos-26=20compat-tests=20t?= =?UTF-8?q?imeout=2045=E2=86=9260m?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The macos-26 runner is ~2x slower than macos-15 (same suite: 23m on macos-15 vs >45m on macos-26). The rebrand changed project.pbxproj, which is part of the DerivedData cache key, so every run on this branch rebuilds from a cold cache and macos-26 consistently hit the 45m cap. Not a code issue — macos-15 passes the same tests. Give macos-26 headroom. --- .github/workflows/ci-macos-compat.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-macos-compat.yml b/.github/workflows/ci-macos-compat.yml index b10c460862c..b3ab0253578 100644 --- a/.github/workflows/ci-macos-compat.yml +++ b/.github/workflows/ci-macos-compat.yml @@ -17,7 +17,7 @@ jobs: smoke: true skip_zig: false - os: macos-26 - timeout: 45 + timeout: 60 # macos-26 runner is ~2x slower than macos-15; 45m was too tight on a cold DerivedData cache (see cache key on project.pbxproj) smoke: false skip_zig: true # zig 0.15.2 MachO linker can't resolve libSystem on macOS 26 runs-on: ${{ matrix.os }} From e05126b9ef05b36ce120298a68a790e4fc12376c Mon Sep 17 00:00:00 2001 From: arzafran Date: Thu, 2 Jul 2026 10:46:53 -0300 Subject: [PATCH 17/23] =?UTF-8?q?feat:=20rebrand=20user-facing=20UI=20stri?= =?UTF-8?q?ngs=20cmux=20=E2=86=92=20Programa=20(Phase=206g)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Localizable.xcstrings (50 EN strings; JA had none) + matching Swift defaultValue/Text fallbacks. Brand nouns → "Programa" (Quit/About/CLI/settings/dialogs/updates); command/binary references → lowercase "programa" (Install 'programa' in PATH, Usage: programa claude-teams/omo, and the sendText("programa welcome") terminal command). Localization KEYS with lowercase cmux (cmuxOnly, cmuxConfig) preserved via value-only edits + a (?![A-Z]) guard. Builds green. --- Resources/Localizable.xcstrings | 100 +++++++++--------- Sources/AppDelegate.swift | 20 ++-- Sources/ContentView.swift | 6 +- Sources/KeyboardShortcutSettings.swift | 2 +- Sources/Panels/BrowserPanel.swift | 24 ++--- .../Panels/BrowserPopupWindowController.swift | 6 +- Sources/ProgramaApp.swift | 36 +++---- Sources/SocketControlSettings.swift | 4 +- Sources/TabManager.swift | 4 +- Sources/TerminalNotificationStore.swift | 4 +- Sources/Update/UpdatePopoverView.swift | 2 +- Sources/Update/UpdateViewModel.swift | 8 +- Sources/Workspace.swift | 2 +- 13 files changed, 109 insertions(+), 109 deletions(-) diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index 9dc420631a5..92de2611f62 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -7,7 +7,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux" + "value": "Programa" } } } @@ -227,7 +227,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux could not move this tab to the selected destination." + "value": "Programa could not move this tab to the selected destination." } } } @@ -645,7 +645,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Always allow this host in cmux" + "value": "Always allow this host in Programa" } } } @@ -788,7 +788,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "%@ uses plain HTTP, so traffic can be read or modified on the network.\n\nOpen this URL in your default browser, or proceed in cmux." + "value": "%@ uses plain HTTP, so traffic can be read or modified on the network.\n\nOpen this URL in your default browser, or proceed in Programa." } } } @@ -920,7 +920,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Created cmux profiles: %@" + "value": "Created Programa profiles: %@" } } } @@ -1118,7 +1118,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Imported data goes into the selected cmux profile." + "value": "Imported data goes into the selected Programa profile." } } } @@ -1129,7 +1129,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "All selected source profiles go into one cmux profile." + "value": "All selected source profiles go into one Programa profile." } } } @@ -1140,7 +1140,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Missing cmux profiles are created on import." + "value": "Missing Programa profiles are created on import." } } } @@ -1217,7 +1217,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux could not create the destination profile \"%@\"." + "value": "Programa could not create the destination profile \"%@\"." } } } @@ -1228,7 +1228,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "The selected cmux browser profile no longer exists. Pick a destination profile again." + "value": "The selected Programa browser profile no longer exists. Pick a destination profile again." } } } @@ -1349,7 +1349,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux could not find browser profiles to import from on this Mac." + "value": "Programa could not find browser profiles to import from on this Mac." } } } @@ -1723,7 +1723,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Proceed in cmux" + "value": "Proceed in Programa" } } } @@ -1921,7 +1921,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Usage: cmux claude-teams [claude-args...]\n\nLaunch Claude Code with agent teams enabled.\n\nThis command:\n - defaults Claude teammate mode to auto\n - sets a tmux-like environment so Claude auto mode uses cmux splits\n - sets CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1\n - prepends a private tmux shim to PATH\n - forwards all remaining arguments to claude\n\nThe tmux shim translates supported tmux window/pane commands into cmux\nworkspace and split operations in the current cmux session.\n\nExamples:\n cmux claude-teams\n cmux claude-teams --continue\n cmux claude-teams --model sonnet" + "value": "Usage: programa claude-teams [claude-args...]\n\nLaunch Claude Code with agent teams enabled.\n\nThis command:\n - defaults Claude teammate mode to auto\n - sets a tmux-like environment so Claude auto mode uses Programa splits\n - sets CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1\n - prepends a private tmux shim to PATH\n - forwards all remaining arguments to claude\n\nThe tmux shim translates supported tmux window/pane commands into Programa\nworkspace and split operations in the current Programa session.\n\nExamples:\n programa claude-teams\n programa claude-teams --continue\n programa claude-teams --model sonnet" } } } @@ -1954,7 +1954,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Couldn't Install cmux CLI" + "value": "Couldn't Install Programa CLI" } } } @@ -1965,7 +1965,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux CLI Installed" + "value": "Programa CLI Installed" } } } @@ -1976,7 +1976,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Usage: cmux omo [opencode-args...]\n\nLaunch OpenCode with oh-my-openagent in a cmux-aware environment.\n\noh-my-openagent orchestrates multiple AI models as specialized agents in\nparallel. This command sets up a tmux shim so agent panes become native\ncmux splits with sidebar metadata and notifications.\n\nThis command:\n - sets a tmux-like environment so oh-my-openagent uses cmux splits\n - prepends a private tmux shim to PATH\n - forwards all remaining arguments to opencode\n\nThe tmux shim translates tmux window/pane commands into cmux workspace\nand split operations in the current cmux session.\n\nExamples:\n cmux omo\n cmux omo --continue\n cmux omo --model claude-sonnet-4-6" + "value": "Usage: programa omo [opencode-args...]\n\nLaunch OpenCode with oh-my-openagent in a Programa-aware environment.\n\noh-my-openagent orchestrates multiple AI models as specialized agents in\nparallel. This command sets up a tmux shim so agent panes become native\nPrograma splits with sidebar metadata and notifications.\n\nThis command:\n - sets a tmux-like environment so oh-my-openagent uses Programa splits\n - prepends a private tmux shim to PATH\n - forwards all remaining arguments to opencode\n\nThe tmux shim translates tmux window/pane commands into Programa workspace\nand split operations in the current Programa session.\n\nExamples:\n programa omo\n programa omo --continue\n programa omo --model claude-sonnet-4-6" } } } @@ -1998,7 +1998,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "No cmux CLI symlink was found at %@." + "value": "No Programa CLI symlink was found at %@." } } } @@ -2020,7 +2020,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Couldn't Uninstall cmux CLI" + "value": "Couldn't Uninstall Programa CLI" } } } @@ -2031,7 +2031,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux CLI Uninstalled" + "value": "Programa CLI Uninstalled" } } } @@ -2504,7 +2504,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Shell Command: Install 'cmux' in PATH" + "value": "Shell Command: Install 'programa' in PATH" } } } @@ -3164,7 +3164,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Shell Command: Uninstall 'cmux' from PATH" + "value": "Shell Command: Uninstall 'programa' from PATH" } } } @@ -4528,7 +4528,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Notifications are disabled for cmux. Enable them in System Settings to see alerts." + "value": "Notifications are disabled for Programa. Enable them in System Settings to see alerts." } } } @@ -4561,7 +4561,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Enable Notifications for cmux" + "value": "Enable Notifications for Programa" } } } @@ -4572,7 +4572,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux could not move this tab to the selected destination." + "value": "Programa could not move this tab to the selected destination." } } } @@ -4671,7 +4671,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Quit cmux?" + "value": "Quit Programa?" } } } @@ -4825,7 +4825,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "About cmux" + "value": "About Programa" } } } @@ -5364,7 +5364,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Quit cmux" + "value": "Quit Programa" } } } @@ -6112,7 +6112,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Restart cmux to apply" + "value": "Restart Programa to apply" } } } @@ -6167,7 +6167,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Open Sidebar PR Links in cmux Browser" + "value": "Open Sidebar PR Links in Programa Browser" } } } @@ -6189,7 +6189,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Clicks open inside cmux browser." + "value": "Clicks open inside Programa browser." } } } @@ -6200,7 +6200,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Open Sidebar Port Links in cmux Browser" + "value": "Open Sidebar Port Links in Programa Browser" } } } @@ -6222,7 +6222,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Port clicks open inside cmux browser." + "value": "Port clicks open inside Programa browser." } } } @@ -6244,7 +6244,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "When cmux is inactive, the first click only activates the window. Click again to focus the pane." + "value": "When Programa is inactive, the first click only activates the window. Click again to focus the pane." } } } @@ -6255,7 +6255,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "When cmux is inactive, clicking a pane activates the window and focuses that pane in one click." + "value": "When Programa is inactive, clicking a pane activates the window and focuses that pane in one click." } } } @@ -6416,7 +6416,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Keep cmux in the menu bar for unread notifications and quick actions." + "value": "Keep Programa in the menu bar for unread notifications and quick actions." } } } @@ -6680,7 +6680,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Share anonymized crash and usage data to help improve cmux." + "value": "Share anonymized crash and usage data to help improve Programa." } } } @@ -6790,7 +6790,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "When enabled, cmux wraps the claude command to inject session tracking and notification hooks. Disable if you prefer to manage Claude Code hooks yourself." + "value": "When enabled, Programa wraps the claude command to inject session tracking and notification hooks. Disable if you prefer to manage Claude Code hooks yourself." } } } @@ -6801,7 +6801,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Claude Code runs without cmux integration." + "value": "Claude Code runs without Programa integration." } } } @@ -7274,7 +7274,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Applies to terminal link clicks and intercepted `open https://...` calls. Only these hosts open in cmux. Others open in your default browser. One host or wildcard per line (for example: example.com, *.internal.example). Leave empty to open all hosts in cmux." + "value": "Applies to terminal link clicks and intercepted `open https://...` calls. Only these hosts open in Programa. Others open in your default browser. One host or wildcard per line (for example: example.com, *.internal.example). Leave empty to open all hosts in Programa." } } } @@ -7296,7 +7296,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Controls which HTTP (non-HTTPS) hosts can open in cmux without a warning prompt. Defaults include localhost, 127.0.0.1, ::1, 0.0.0.0, and *.localtest.me." + "value": "Controls which HTTP (non-HTTPS) hosts can open in Programa without a warning prompt. Defaults include localhost, 127.0.0.1, ::1, 0.0.0.0, and *.localtest.me." } } } @@ -7428,7 +7428,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Open Terminal Links in cmux Browser" + "value": "Open Terminal Links in Programa Browser" } } } @@ -7681,7 +7681,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Briefly flash a blue outline when cmux highlights a pane." + "value": "Briefly flash a blue outline when Programa highlights a pane." } } } @@ -9265,7 +9265,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Welcome to cmux!" + "value": "Welcome to Programa!" } } } @@ -9606,7 +9606,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Only processes started inside cmux terminals can send commands." + "value": "Only processes started inside Programa terminals can send commands." } } } @@ -9617,7 +9617,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux processes only" + "value": "Programa processes only" } } } @@ -10101,7 +10101,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux couldn't download the update feed. Check your connection and try again." + "value": "Programa couldn't download the update feed. Check your connection and try again." } } } @@ -10178,7 +10178,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux can't reach the update server. Check your internet connection and try again." + "value": "Programa can't reach the update server. Check your internet connection and try again." } } } @@ -10200,7 +10200,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "Move cmux into Applications and relaunch to enable updates." + "value": "Move Programa into Applications and relaunch to enable updates." } } } @@ -10266,7 +10266,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux couldn't connect to the update server. Check your connection or try again later." + "value": "Programa couldn't connect to the update server. Check your connection or try again later." } } } @@ -10420,7 +10420,7 @@ "en": { "stringUnit": { "state": "translated", - "value": "cmux can automatically check for updates in the background." + "value": "Programa can automatically check for updates in the background." } } } diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index b493e3878ed..6c301e8b428 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -3062,7 +3062,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent DispatchQueue.main.async { let alert = NSAlert() alert.alertStyle = .warning - alert.messageText = String(localized: "dialog.quitPrograma.title", defaultValue: "Quit cmux?") + alert.messageText = String(localized: "dialog.quitPrograma.title", defaultValue: "Quit Programa?") alert.informativeText = String(localized: "dialog.quitPrograma.message", defaultValue: "This will close all windows and workspaces.") alert.addButton(withTitle: String(localized: "dialog.quitPrograma.quit", defaultValue: "Quit")) alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel")) @@ -7366,13 +7366,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent informativeText += "\n\n" + String(localized: "cli.install.adminRequired", defaultValue: "Administrator privileges were required to write to /usr/local/bin.") } presentCLIPathAlert( - title: String(localized: "cli.installed", defaultValue: "cmux CLI Installed"), + title: String(localized: "cli.installed", defaultValue: "Programa CLI Installed"), informativeText: informativeText, style: .informational ) } catch { presentCLIPathAlert( - title: String(localized: "cli.installFailed", defaultValue: "Couldn't Install cmux CLI"), + title: String(localized: "cli.installFailed", defaultValue: "Couldn't Install Programa CLI"), informativeText: error.localizedDescription, style: .warning ) @@ -7385,19 +7385,19 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent let outcome = try installer.uninstall() let prefix = outcome.removedExistingEntry ? String(localized: "cli.uninstall.removed", defaultValue: "Removed \(outcome.destinationURL.path).") - : String(localized: "cli.uninstall.notFound", defaultValue: "No cmux CLI symlink was found at \(outcome.destinationURL.path).") + : String(localized: "cli.uninstall.notFound", defaultValue: "No Programa CLI symlink was found at \(outcome.destinationURL.path).") var informativeText = prefix if outcome.usedAdministratorPrivileges { informativeText += "\n\n" + String(localized: "cli.uninstall.adminRequired", defaultValue: "Administrator privileges were required to modify /usr/local/bin.") } presentCLIPathAlert( - title: String(localized: "cli.uninstalled", defaultValue: "cmux CLI Uninstalled"), + title: String(localized: "cli.uninstalled", defaultValue: "Programa CLI Uninstalled"), informativeText: informativeText, style: .informational ) } catch { presentCLIPathAlert( - title: String(localized: "cli.uninstallFailed", defaultValue: "Couldn't Uninstall cmux CLI"), + title: String(localized: "cli.uninstallFailed", defaultValue: "Couldn't Uninstall Programa CLI"), informativeText: error.localizedDescription, style: .warning ) @@ -10602,7 +10602,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent let alert = NSAlert() alert.alertStyle = .warning - alert.messageText = String(localized: "dialog.quitPrograma.title", defaultValue: "Quit cmux?") + alert.messageText = String(localized: "dialog.quitPrograma.title", defaultValue: "Quit Programa?") alert.informativeText = String(localized: "dialog.quitPrograma.message", defaultValue: "This will close all windows and workspaces.") alert.addButton(withTitle: String(localized: "dialog.quitPrograma.quit", defaultValue: "Quit")) alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel")) @@ -13512,7 +13512,7 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate { private let clearAllItem = NSMenuItem(title: String(localized: "statusMenu.clearAll", defaultValue: "Clear All"), action: nil, keyEquivalent: "") private let checkForUpdatesItem = NSMenuItem(title: String(localized: "menu.checkForUpdates", defaultValue: "Check for Updates…"), action: nil, keyEquivalent: "") private let preferencesItem = NSMenuItem(title: String(localized: "menu.preferences", defaultValue: "Preferences…"), action: nil, keyEquivalent: "") - private let quitItem = NSMenuItem(title: String(localized: "menu.quitPrograma", defaultValue: "Quit cmux"), action: nil, keyEquivalent: "") + private let quitItem = NSMenuItem(title: String(localized: "menu.quitPrograma", defaultValue: "Quit Programa"), action: nil, keyEquivalent: "") private var notificationItems: [NSMenuItem] = [] private let maxInlineNotificationItems = 6 @@ -13649,8 +13649,8 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate { button.toolTip = displayedUnreadCount == 0 ? "cmux" : displayedUnreadCount == 1 - ? "cmux: " + String(localized: "statusMenu.tooltip.unread.one", defaultValue: "1 unread notification") - : "cmux: " + String(localized: "statusMenu.tooltip.unread.other", defaultValue: "\(displayedUnreadCount) unread notifications") + ? "Programa: " + String(localized: "statusMenu.tooltip.unread.one", defaultValue: "1 unread notification") + : "Programa: " + String(localized: "statusMenu.tooltip.unread.other", defaultValue: "\(displayedUnreadCount) unread notifications") } } diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 8c7b61f4678..b7308a35ab8 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -6364,7 +6364,7 @@ struct ContentView: View { contributions.append( CommandPaletteCommandContribution( commandId: "palette.installCLI", - title: constant(String(localized: "command.installCLI.title", defaultValue: "Shell Command: Install 'cmux' in PATH")), + title: constant(String(localized: "command.installCLI.title", defaultValue: "Shell Command: Install 'programa' in PATH")), subtitle: constant(String(localized: "command.installCLI.subtitle", defaultValue: "CLI")), keywords: ["install", "cli", "path", "shell", "command", "symlink"], when: { !$0.bool(CommandPaletteContextKeys.cliInstalledInPATH) } @@ -6373,7 +6373,7 @@ struct ContentView: View { contributions.append( CommandPaletteCommandContribution( commandId: "palette.uninstallCLI", - title: constant(String(localized: "command.uninstallCLI.title", defaultValue: "Shell Command: Uninstall 'cmux' from PATH")), + title: constant(String(localized: "command.uninstallCLI.title", defaultValue: "Shell Command: Uninstall 'programa' from PATH")), subtitle: constant(String(localized: "command.uninstallCLI.subtitle", defaultValue: "CLI")), keywords: ["uninstall", "remove", "cli", "path", "shell", "command", "symlink"], when: { $0.bool(CommandPaletteContextKeys.cliInstalledInPATH) } @@ -11776,7 +11776,7 @@ private struct SidebarHelpMenuButton: View { private var helpPopover: some View { VStack(alignment: .leading, spacing: 2) { helpOptionButton( - title: String(localized: "sidebar.help.welcome", defaultValue: "Welcome to cmux!"), + title: String(localized: "sidebar.help.welcome", defaultValue: "Welcome to Programa!"), action: .welcome, accessibilityIdentifier: "SidebarHelpMenuOptionWelcome", isExternalLink: false diff --git a/Sources/KeyboardShortcutSettings.swift b/Sources/KeyboardShortcutSettings.swift index ef91390f823..ef8153ada1c 100644 --- a/Sources/KeyboardShortcutSettings.swift +++ b/Sources/KeyboardShortcutSettings.swift @@ -87,7 +87,7 @@ enum KeyboardShortcutSettings { case .newWindow: return String(localized: "shortcut.newWindow.label", defaultValue: "New Window") case .closeWindow: return String(localized: "shortcut.closeWindow.label", defaultValue: "Close Window") case .toggleFullScreen: return String(localized: "command.toggleFullScreen.title", defaultValue: "Toggle Full Screen") - case .quit: return String(localized: "menu.quitPrograma", defaultValue: "Quit cmux") + case .quit: return String(localized: "menu.quitPrograma", defaultValue: "Quit Programa") case .toggleSidebar: return String(localized: "shortcut.toggleSidebar.label", defaultValue: "Toggle Sidebar") case .newTab: return String(localized: "shortcut.newWorkspace.label", defaultValue: "New Workspace") case .openFolder: return String(localized: "shortcut.openFolder.label", defaultValue: "Open Folder") diff --git a/Sources/Panels/BrowserPanel.swift b/Sources/Panels/BrowserPanel.swift index c78fc755429..955643c0718 100644 --- a/Sources/Panels/BrowserPanel.swift +++ b/Sources/Panels/BrowserPanel.swift @@ -3974,12 +3974,12 @@ final class BrowserPanel: Panel, ObservableObject { let alert = insecureHTTPAlertFactory() alert.alertStyle = .warning alert.messageText = String(localized: "browser.error.insecure.title", defaultValue: "Connection isn\u{2019}t secure") - alert.informativeText = String(localized: "browser.error.insecure.message", defaultValue: "\(host) uses plain HTTP, so traffic can be read or modified on the network.\n\nOpen this URL in your default browser, or proceed in cmux.") + alert.informativeText = String(localized: "browser.error.insecure.message", defaultValue: "\(host) uses plain HTTP, so traffic can be read or modified on the network.\n\nOpen this URL in your default browser, or proceed in Programa.") alert.addButton(withTitle: String(localized: "browser.openInDefaultBrowser", defaultValue: "Open in Default Browser")) - alert.addButton(withTitle: String(localized: "browser.proceedInPrograma", defaultValue: "Proceed in cmux")) + alert.addButton(withTitle: String(localized: "browser.proceedInPrograma", defaultValue: "Proceed in Programa")) alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel")) alert.showsSuppressionButton = true - alert.suppressionButton?.title = String(localized: "browser.alwaysAllowHost", defaultValue: "Always allow this host in cmux") + alert.suppressionButton?.title = String(localized: "browser.alwaysAllowHost", defaultValue: "Always allow this host in Programa") let handleResponse: (NSApplication.ModalResponse) -> Void = { [weak self, weak alert] response in self?.handleInsecureHTTPAlertResponse( @@ -7589,13 +7589,13 @@ enum BrowserImportPlanRealizationError: LocalizedError { case .missingDestinationProfile: return String( localized: "browser.import.error.destinationMissing", - defaultValue: "The selected cmux browser profile no longer exists. Pick a destination profile again." + defaultValue: "The selected Programa browser profile no longer exists. Pick a destination profile again." ) case .profileCreationFailed(let name): return String( format: String( localized: "browser.import.error.destinationCreateFailed", - defaultValue: "cmux could not create the destination profile \"%@\"." + defaultValue: "Programa could not create the destination profile \"%@\"." ), name ) @@ -7715,7 +7715,7 @@ enum BrowserImportOutcomeFormatter { String( format: String( localized: "browser.import.complete.createdProfiles", - defaultValue: "Created cmux profiles: %@" + defaultValue: "Created Programa profiles: %@" ), outcome.createdDestinationProfileNames.joined(separator: ", ") ) @@ -9133,7 +9133,7 @@ final class BrowserDataImportCoordinator { ) alert.informativeText = String( localized: "browser.import.noBrowsers.message", - defaultValue: "cmux could not find browser profiles to import from on this Mac." + defaultValue: "Programa could not find browser profiles to import from on this Mac." ) alert.addButton(withTitle: String(localized: "common.ok", defaultValue: "OK")) alert.runModal() @@ -9723,7 +9723,7 @@ final class BrowserDataImportCoordinator { sourceProfilesHelpLabel.preferredMaxLayoutWidth = 500 sourceProfilesHelpLabel.stringValue = String( localized: "browser.import.sourceProfiles.help", - defaultValue: "Choose one or more source profiles. Step 3 lets you keep them separate or merge them into one cmux profile." + defaultValue: "Choose one or more source profiles. Step 3 lets you keep them separate or merge them into one Programa profile." ) sourceProfilesContainer.orientation = .vertical @@ -9768,7 +9768,7 @@ final class BrowserDataImportCoordinator { ) mergeProfilesRadio.title = String( localized: "browser.import.destinationMode.merge", - defaultValue: "Merge all into one cmux profile" + defaultValue: "Merge all into one Programa profile" ) separateProfilesRadio.target = self separateProfilesRadio.action = #selector(handleDestinationModeChanged(_:)) @@ -9810,7 +9810,7 @@ final class BrowserDataImportCoordinator { let destinationTitleLabel = NSTextField( labelWithString: String( localized: "browser.import.destination.cmux", - defaultValue: "cmux destination" + defaultValue: "Programa destination" ) ) destinationTitleLabel.font = NSFont.systemFont(ofSize: 12, weight: .semibold) @@ -10057,13 +10057,13 @@ final class BrowserDataImportCoordinator { if presentation.showsSeparateRows { destinationHelpLabel.stringValue = String( localized: "browser.import.destinationProfile.separateHelp", - defaultValue: "Missing cmux profiles are created when import starts." + defaultValue: "Missing Programa profiles are created when import starts." ) destinationHelpLabel.isHidden = false } else if plan.entries.count > 1 { destinationHelpLabel.stringValue = String( localized: "browser.import.destinationProfile.mergeHelp", - defaultValue: "All selected source profiles will be merged into the chosen cmux browser profile." + defaultValue: "All selected source profiles will be merged into the chosen Programa browser profile." ) destinationHelpLabel.isHidden = false } else { diff --git a/Sources/Panels/BrowserPopupWindowController.swift b/Sources/Panels/BrowserPopupWindowController.swift index 4a35824ce24..536935eaed8 100644 --- a/Sources/Panels/BrowserPopupWindowController.swift +++ b/Sources/Panels/BrowserPopupWindowController.swift @@ -363,12 +363,12 @@ final class BrowserPopupWindowController: NSObject, NSWindowDelegate { let alert = NSAlert() alert.alertStyle = .warning alert.messageText = String(localized: "browser.error.insecure.title", defaultValue: "Connection isn\u{2019}t secure") - alert.informativeText = String(localized: "browser.error.insecure.message", defaultValue: "\(host) uses plain HTTP, so traffic can be read or modified on the network.\n\nOpen this URL in your default browser, or proceed in cmux.") + alert.informativeText = String(localized: "browser.error.insecure.message", defaultValue: "\(host) uses plain HTTP, so traffic can be read or modified on the network.\n\nOpen this URL in your default browser, or proceed in Programa.") alert.addButton(withTitle: String(localized: "browser.openInDefaultBrowser", defaultValue: "Open in Default Browser")) - alert.addButton(withTitle: String(localized: "browser.proceedInPrograma", defaultValue: "Proceed in cmux")) + alert.addButton(withTitle: String(localized: "browser.proceedInPrograma", defaultValue: "Proceed in Programa")) alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel")) alert.showsSuppressionButton = true - alert.suppressionButton?.title = String(localized: "browser.alwaysAllowHost", defaultValue: "Always allow this host in cmux") + alert.suppressionButton?.title = String(localized: "browser.alwaysAllowHost", defaultValue: "Always allow this host in Programa") let handleResponse: (NSApplication.ModalResponse) -> Void = { [weak alert] response in if browserShouldPersistInsecureHTTPAllowlistSelection( diff --git a/Sources/ProgramaApp.swift b/Sources/ProgramaApp.swift index 2593da64258..389ebede5ed 100644 --- a/Sources/ProgramaApp.swift +++ b/Sources/ProgramaApp.swift @@ -386,7 +386,7 @@ struct cmuxApp: App { } CommandGroup(replacing: .appInfo) { - Button(String(localized: "menu.app.about", defaultValue: "About cmux")) { + Button(String(localized: "menu.app.about", defaultValue: "About Programa")) { showAboutPanel() } @@ -397,7 +397,7 @@ struct cmuxApp: App { } CommandGroup(replacing: .appTermination) { - splitCommandButton(title: String(localized: "menu.quitPrograma", defaultValue: "Quit cmux"), shortcut: menuShortcut(for: .quit)) { + splitCommandButton(title: String(localized: "menu.quitPrograma", defaultValue: "Quit Programa"), shortcut: menuShortcut(for: .quit)) { NSApp.terminate(nil) } } @@ -1555,7 +1555,7 @@ private struct SettingsAboutTitlebarDebugView: View { VStack(alignment: .leading, spacing: 10) { Toggle("Enable Debug Overrides", isOn: overridesEnabled) - Text("When disabled, cmux uses normal default titlebar behavior for this window.") + Text("When disabled, Programa uses normal default titlebar behavior for this window.") .font(.caption) .foregroundColor(.secondary) @@ -4236,12 +4236,12 @@ struct SettingsView: View { if paneFirstClickFocusEnabled { return String( localized: "settings.app.paneFirstClickFocus.subtitleOn", - defaultValue: "When cmux is inactive, clicking a pane activates the window and focuses that pane in one click." + defaultValue: "When Programa is inactive, clicking a pane activates the window and focuses that pane in one click." ) } return String( localized: "settings.app.paneFirstClickFocus.subtitleOff", - defaultValue: "When cmux is inactive, the first click only activates the window. Click again to focus the pane." + defaultValue: "When Programa is inactive, the first click only activates the window. Click again to focus the pane." ) } @@ -4613,7 +4613,7 @@ struct SettingsView: View { SettingsCardRow( String(localized: "settings.app.language", defaultValue: "Language"), subtitle: appLanguage != LanguageSettings.languageAtLaunch.rawValue - ? String(localized: "settings.app.language.restartSubtitle", defaultValue: "Restart cmux to apply") + ? String(localized: "settings.app.language.restartSubtitle", defaultValue: "Restart Programa to apply") : nil, controlWidth: pickerColumnWidth ) { @@ -4751,7 +4751,7 @@ struct SettingsView: View { SettingsCardRow( String(localized: "settings.app.showInMenuBar", defaultValue: "Show in Menu Bar"), - subtitle: String(localized: "settings.app.showInMenuBar.subtitle", defaultValue: "Keep cmux in the menu bar for unread notifications and quick actions.") + subtitle: String(localized: "settings.app.showInMenuBar.subtitle", defaultValue: "Keep Programa in the menu bar for unread notifications and quick actions.") ) { Toggle("", isOn: $showMenuBarExtra) .labelsHidden() @@ -4779,7 +4779,7 @@ struct SettingsView: View { SettingsCardRow( String(localized: "settings.notifications.paneFlash.title", defaultValue: "Pane Flash"), - subtitle: String(localized: "settings.notifications.paneFlash.subtitle", defaultValue: "Briefly flash a blue outline when cmux highlights a pane.") + subtitle: String(localized: "settings.notifications.paneFlash.subtitle", defaultValue: "Briefly flash a blue outline when Programa highlights a pane.") ) { Toggle("", isOn: $notificationPaneFlashEnabled) .labelsHidden() @@ -4898,7 +4898,7 @@ struct SettingsView: View { String(localized: "settings.app.telemetry", defaultValue: "Send anonymous telemetry"), subtitle: sendAnonymousTelemetry != telemetryValueAtLaunch ? String(localized: "settings.app.telemetry.subtitleChanged", defaultValue: "Change takes effect on next launch.") - : String(localized: "settings.app.telemetry.subtitle", defaultValue: "Share anonymized crash and usage data to help improve cmux.") + : String(localized: "settings.app.telemetry.subtitle", defaultValue: "Share anonymized crash and usage data to help improve Programa.") ) { Toggle("", isOn: $sendAnonymousTelemetry) .labelsHidden() @@ -5028,9 +5028,9 @@ struct SettingsView: View { SettingsCardDivider() SettingsCardRow( - String(localized: "settings.app.openSidebarPRLinks", defaultValue: "Open Sidebar PR Links in cmux Browser"), + String(localized: "settings.app.openSidebarPRLinks", defaultValue: "Open Sidebar PR Links in Programa Browser"), subtitle: openSidebarPullRequestLinksInProgramaBrowser - ? String(localized: "settings.app.openSidebarPRLinks.subtitleOn", defaultValue: "Clicks open inside cmux browser.") + ? String(localized: "settings.app.openSidebarPRLinks.subtitleOn", defaultValue: "Clicks open inside Programa browser.") : String(localized: "settings.app.openSidebarPRLinks.subtitleOff", defaultValue: "Clicks open in your default browser.") ) { Toggle("", isOn: $openSidebarPullRequestLinksInProgramaBrowser) @@ -5042,9 +5042,9 @@ struct SettingsView: View { SettingsCardDivider() SettingsCardRow( - String(localized: "settings.app.openSidebarPortLinks", defaultValue: "Open Sidebar Port Links in cmux Browser"), + String(localized: "settings.app.openSidebarPortLinks", defaultValue: "Open Sidebar Port Links in Programa Browser"), subtitle: openSidebarPortLinksInProgramaBrowser - ? String(localized: "settings.app.openSidebarPortLinks.subtitleOn", defaultValue: "Port clicks open inside cmux browser.") + ? String(localized: "settings.app.openSidebarPortLinks.subtitleOn", defaultValue: "Port clicks open inside Programa browser.") : String(localized: "settings.app.openSidebarPortLinks.subtitleOff", defaultValue: "Port clicks open in your default browser.") ) { Toggle("", isOn: $openSidebarPortLinksInProgramaBrowser) @@ -5411,7 +5411,7 @@ struct SettingsView: View { String(localized: "settings.automation.claudeCode", defaultValue: "Claude Code Integration"), subtitle: claudeCodeHooksEnabled ? String(localized: "settings.automation.claudeCode.subtitleOn", defaultValue: "Sidebar shows Claude session status and notifications.") - : String(localized: "settings.automation.claudeCode.subtitleOff", defaultValue: "Claude Code runs without cmux integration.") + : String(localized: "settings.automation.claudeCode.subtitleOff", defaultValue: "Claude Code runs without Programa integration.") ) { Toggle("", isOn: $claudeCodeHooksEnabled) .labelsHidden() @@ -5421,7 +5421,7 @@ struct SettingsView: View { SettingsCardDivider() - SettingsCardNote(String(localized: "settings.automation.claudeCode.note", defaultValue: "When enabled, cmux wraps the claude command to inject session tracking and notification hooks. Disable if you prefer to manage Claude Code hooks yourself.")) + SettingsCardNote(String(localized: "settings.automation.claudeCode.note", defaultValue: "When enabled, Programa wraps the claude command to inject session tracking and notification hooks. Disable if you prefer to manage Claude Code hooks yourself.")) } SettingsCard { @@ -5531,7 +5531,7 @@ struct SettingsView: View { SettingsCardDivider() SettingsCardRow( - String(localized: "settings.browser.openTerminalLinks", defaultValue: "Open Terminal Links in cmux Browser"), + String(localized: "settings.browser.openTerminalLinks", defaultValue: "Open Terminal Links in Programa Browser"), subtitle: String(localized: "settings.browser.openTerminalLinks.subtitle", defaultValue: "When off, links clicked in terminal output open in your default browser.") ) { Toggle("", isOn: $openTerminalLinksInProgramaBrowser) @@ -5556,7 +5556,7 @@ struct SettingsView: View { VStack(alignment: .leading, spacing: 6) { SettingsCardRow( String(localized: "settings.browser.hostWhitelist", defaultValue: "Hosts to Open in Embedded Browser"), - subtitle: String(localized: "settings.browser.hostWhitelist.subtitle", defaultValue: "Applies to terminal link clicks and intercepted `open https://...` calls. Only these hosts open in cmux. Others open in your default browser. One host or wildcard per line (for example: example.com, *.internal.example). Leave empty to open all hosts in cmux.") + subtitle: String(localized: "settings.browser.hostWhitelist.subtitle", defaultValue: "Applies to terminal link clicks and intercepted `open https://...` calls. Only these hosts open in Programa. Others open in your default browser. One host or wildcard per line (for example: example.com, *.internal.example). Leave empty to open all hosts in Programa.") ) { EmptyView() } @@ -5608,7 +5608,7 @@ struct SettingsView: View { Text(String(localized: "settings.browser.httpAllowlist", defaultValue: "HTTP Hosts Allowed in Embedded Browser")) .font(.system(size: 13, weight: .semibold)) - Text(String(localized: "settings.browser.httpAllowlist.description", defaultValue: "Controls which HTTP (non-HTTPS) hosts can open in cmux without a warning prompt. Defaults include localhost, 127.0.0.1, ::1, 0.0.0.0, and *.localtest.me.")) + Text(String(localized: "settings.browser.httpAllowlist.description", defaultValue: "Controls which HTTP (non-HTTPS) hosts can open in Programa without a warning prompt. Defaults include localhost, 127.0.0.1, ::1, 0.0.0.0, and *.localtest.me.")) .font(.caption) .foregroundStyle(.secondary) diff --git a/Sources/SocketControlSettings.swift b/Sources/SocketControlSettings.swift index ae28c3a56c0..e2a7bd9d60d 100644 --- a/Sources/SocketControlSettings.swift +++ b/Sources/SocketControlSettings.swift @@ -21,7 +21,7 @@ enum SocketControlMode: String, CaseIterable, Identifiable { case .off: return String(localized: "socketControl.off.name", defaultValue: "Off") case .cmuxOnly: - return String(localized: "socketControl.cmuxOnly.name", defaultValue: "cmux processes only") + return String(localized: "socketControl.cmuxOnly.name", defaultValue: "Programa processes only") case .automation: return String(localized: "socketControl.automation.name", defaultValue: "Automation mode") case .password: @@ -36,7 +36,7 @@ enum SocketControlMode: String, CaseIterable, Identifiable { case .off: return String(localized: "socketControl.off.description", defaultValue: "Disable the local control socket.") case .cmuxOnly: - return String(localized: "socketControl.cmuxOnly.description", defaultValue: "Only processes started inside cmux terminals can send commands.") + return String(localized: "socketControl.cmuxOnly.description", defaultValue: "Only processes started inside Programa terminals can send commands.") case .automation: return String(localized: "socketControl.automation.description", defaultValue: "Allow external local automation clients from this macOS user (no ancestry check).") case .password: diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index 4274e015931..38eddeb156b 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -1482,7 +1482,7 @@ class TabManager: ObservableObject { terminalPanel.surface.surface != nil { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { UserDefaults.standard.set(true, forKey: WelcomeSettings.shownKey) - terminalPanel.sendText("cmux welcome\n") + terminalPanel.sendText("programa welcome\n") } return } @@ -1502,7 +1502,7 @@ class TabManager: ObservableObject { panelsCancellable?.cancel() DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { UserDefaults.standard.set(true, forKey: WelcomeSettings.shownKey) - terminalPanel.sendText("cmux welcome\n") + terminalPanel.sendText("programa welcome\n") } } diff --git a/Sources/TerminalNotificationStore.swift b/Sources/TerminalNotificationStore.swift index 65e39462b1e..4eee4361012 100644 --- a/Sources/TerminalNotificationStore.swift +++ b/Sources/TerminalNotificationStore.swift @@ -1278,8 +1278,8 @@ final class TerminalNotificationStore: ObservableObject { } let alert = notificationSettingsAlertFactory() - alert.messageText = String(localized: "dialog.enableNotifications.title", defaultValue: "Enable Notifications for cmux") - alert.informativeText = String(localized: "dialog.enableNotifications.message", defaultValue: "Notifications are disabled for cmux. Enable them in System Settings to see alerts.") + alert.messageText = String(localized: "dialog.enableNotifications.title", defaultValue: "Enable Notifications for Programa") + alert.informativeText = String(localized: "dialog.enableNotifications.message", defaultValue: "Notifications are disabled for Programa. Enable them in System Settings to see alerts.") alert.addButton(withTitle: String(localized: "dialog.enableNotifications.openSettings", defaultValue: "Open Settings")) alert.addButton(withTitle: String(localized: "dialog.enableNotifications.notNow", defaultValue: "Not Now")) alert.beginSheetModal(for: window) { [weak self] response in diff --git a/Sources/Update/UpdatePopoverView.swift b/Sources/Update/UpdatePopoverView.swift index 5642bf913cc..1bc4ef7bfa0 100644 --- a/Sources/Update/UpdatePopoverView.swift +++ b/Sources/Update/UpdatePopoverView.swift @@ -200,7 +200,7 @@ fileprivate struct PermissionRequestView: View { Text(String(localized: "update.popover.enableAutoUpdates", defaultValue: "Enable automatic updates?")) .font(.system(size: 13, weight: .semibold)) - Text(String(localized: "update.popover.autoUpdatesDescription", defaultValue: "cmux can automatically check for updates in the background.")) + Text(String(localized: "update.popover.autoUpdatesDescription", defaultValue: "Programa can automatically check for updates in the background.")) .font(.system(size: 11)) .foregroundColor(.secondary) .fixedSize(horizontal: false, vertical: true) diff --git a/Sources/Update/UpdateViewModel.swift b/Sources/Update/UpdateViewModel.swift index 2789cc05ba8..ec41d83ea72 100644 --- a/Sources/Update/UpdateViewModel.swift +++ b/Sources/Update/UpdateViewModel.swift @@ -269,13 +269,13 @@ class UpdateViewModel: ObservableObject { if let networkError = networkError(from: nsError) { switch networkError.code { case NSURLErrorNotConnectedToInternet: - return String(localized: "update.error.noInternet.message", defaultValue: "cmux can’t reach the update server. Check your internet connection and try again.") + return String(localized: "update.error.noInternet.message", defaultValue: "Programa can’t reach the update server. Check your internet connection and try again.") case NSURLErrorTimedOut: return String(localized: "update.error.timedOut.message", defaultValue: "The update server took too long to respond. Try again in a moment.") case NSURLErrorCannotFindHost: return String(localized: "update.error.serverNotFound.message", defaultValue: "The update server can’t be found. Check your connection or try again later.") case NSURLErrorCannotConnectToHost: - return String(localized: "update.error.serverUnreachable.message", defaultValue: "cmux couldn’t connect to the update server. Check your connection or try again later.") + return String(localized: "update.error.serverUnreachable.message", defaultValue: "Programa couldn’t connect to the update server. Check your connection or try again later.") case NSURLErrorNetworkConnectionLost: return String(localized: "update.error.connectionLost.message", defaultValue: "The network connection was lost while checking for updates. Try again.") case NSURLErrorSecureConnectionFailed, @@ -291,7 +291,7 @@ class UpdateViewModel: ObservableObject { if nsError.domain == SUSparkleErrorDomain { switch nsError.code { case 2001: - return String(localized: "update.error.feedDownload.message", defaultValue: "cmux couldn't download the update feed. Check your connection and try again.") + return String(localized: "update.error.feedDownload.message", defaultValue: "Programa couldn't download the update feed. Check your connection and try again.") case 1000, 1002: return String(localized: "update.error.feedRead.message", defaultValue: "The update feed could not be read. Please try again later.") case 4: @@ -301,7 +301,7 @@ class UpdateViewModel: ObservableObject { case 1, 2, 3001, 3002: return String(localized: "update.error.signatureError.message", defaultValue: "The update's signature could not be verified. Please try again later.") case 1003, 1005, 4005: - return String(localized: "update.error.permissionError.message", defaultValue: "Move cmux into Applications and relaunch to enable updates.") + return String(localized: "update.error.permissionError.message", defaultValue: "Move Programa into Applications and relaunch to enable updates.") default: break } diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 71dc981e642..8060ffab476 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -11235,7 +11235,7 @@ final class Workspace: Identifiable, ObservableObject { let failure = NSAlert() failure.alertStyle = .warning failure.messageText = String(localized: "alert.moveTab.failed.title", defaultValue: "Move Failed") - failure.informativeText = String(localized: "alert.moveTab.failed.message", defaultValue: "cmux could not move this tab to the selected destination.") + failure.informativeText = String(localized: "alert.moveTab.failed.message", defaultValue: "Programa could not move this tab to the selected destination.") failure.addButton(withTitle: String(localized: "alert.ok", defaultValue: "OK")) _ = failure.runModal() } From 56d67ff4eff61f7a1e6721fe67a21485a5601a6a Mon Sep 17 00:00:00 2001 From: arzafran Date: Thu, 2 Jul 2026 10:47:29 -0300 Subject: [PATCH 18/23] docs: mark 6g brand strings + 6h release artifacts done in progress map --- plans/rebrand-progress.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plans/rebrand-progress.md b/plans/rebrand-progress.md index 8a9d5addd25..579a9b2ec82 100644 --- a/plans/rebrand-progress.md +++ b/plans/rebrand-progress.md @@ -24,7 +24,10 @@ contracts compile but need CI/manual verification before merge. 6h. **release artifacts** — `cmux.entitlements`→`programa.entitlements` (git mv + 3 signing refs), nightly DMG/artifact names `cmux-nightly-*`→`programa-nightly-*`. Homebrew left (external/deprecated). CI-fix. **tests/ guard scripts** — `test_ci_scheme_testaction_debug.sh` + `test_bundled_ghostty_theme_picker_helper.sh` pointed at the old `cmux.xcscheme`/`-scheme cmux` (Phase 4 miss). Fixed. -**PR: #48** — CI green except `tests-build-and-lag` (known runner flake; re-running). +6g. **UI brand strings** — `Localizable.xcstrings` (50 EN; JA had none) + Swift `defaultValue`/`Text` fallbacks. Brand→`Programa`, command→lowercase `programa`; keys with lowercase cmux preserved. +ci-fix. **macos-26 compat timeout** 45→60m (cold DerivedData cache on slow runner). + +**PR: #48** — CI green modulo `tests-build-and-lag` (known runner flake) + `compat-tests(macos-26)` (now 60m). ## Remaining (each = its own build-gated commit; runtime = CI/manual) @@ -49,12 +52,9 @@ CI-fix. **tests/ guard scripts** — `test_ci_scheme_testaction_debug.sh` + `tes - DO NOT blanket-sed (hits socket paths, env vars, remote tokens). Categorize per-occurrence. - Verify: `tests_v2/test_ssh_remote_*.py` on CI. -### 6g — user-facing brand strings (`Resources/Localizable.xcstrings` + a few Swift defaults) -- Brand noun `cmux`→`Programa` ("Quit cmux", "About cmux", "cmux CLI", notifications, error text). -- CLI usage strings inside xcstrings ("Usage: cmux claude-teams", "Install 'cmux' in PATH") → `programa` (lowercase command). -- EN + JA. Distinguish brand (Programa) vs command (programa) per string — NOT a blanket sed. +### ~~6g — user-facing brand strings~~ ✅ DONE (commit e05126b9) -### 6h — release artifacts +### ~~6h — release artifacts~~ ✅ DONE - `cmux.entitlements` → `programa.entitlements` (git mv + pbxproj `CODE_SIGN_ENTITLEMENTS` + `build-sign-upload.sh` + release/nightly yml). - DMG names `cmux-nightly-macos*.dmg`/`cmux-release-*` → `programa-*` (release.yml, nightly.yml) so README's `programa-macos.dmg` link resolves. - homebrew cask refs in `build-sign-upload.sh` (or delete per the board-sweep plan #7). From d8127defd1e37d0c6fa504f1ba45010d3ed9e3db Mon Sep 17 00:00:00 2001 From: arzafran Date: Thu, 2 Jul 2026 10:59:30 -0300 Subject: [PATCH 19/23] fix: resolve/install the bundled CLI as programa, not cmux (Phase 6f functional) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CLI product is PRODUCT_NAME=programa and is bundled at Contents/Resources/bin/programa, but the app still looked for bin/cmux and installed /usr/local/bin/cmux — a pre-existing mismatch (main is mid-rebrand) that left the 'Install CLI' + CLI resolution broken. Point them at bin/programa and /usr/local/bin/programa. Also fix the remaining 'programa welcome' terminal command send, the menu-bar title/tooltip, and brand fallbacks (window title, app name for notifications) cmux → Programa. Socket contract already agrees (shared last-socket-path hint). Builds green. --- Sources/AppDelegate.swift | 18 +++++++++--------- Sources/GhosttyTerminalView.swift | 2 +- Sources/TabManager.swift | 4 ++-- Sources/TerminalNotificationStore.swift | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 6c301e8b428..d9f00b1f9f3 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -1346,7 +1346,7 @@ struct ProgramaCLIPathInstaller { case .destinationIsDirectory(let path): return "\(path) is a directory. Remove or rename it and try again." case .installVerificationFailed(let path): - return "Installed symlink at \(path) did not point to the bundled cmux CLI." + return "Installed symlink at \(path) did not point to the bundled programa CLI." case .uninstallVerificationFailed(let path): return "Failed to remove \(path)." case .privilegedCommandFailed(let message): @@ -1367,7 +1367,7 @@ struct ProgramaCLIPathInstaller { init( fileManager: FileManager = .default, - destinationURL: URL = URL(fileURLWithPath: "/usr/local/bin/cmux"), + destinationURL: URL = URL(fileURLWithPath: "/usr/local/bin/programa"), bundledCLIURLProvider: @escaping () -> URL? = { ProgramaCLIPathInstaller.defaultBundledCLIURL() }, @@ -1543,12 +1543,12 @@ struct ProgramaCLIPathInstaller { } private static func defaultBundledCLIURL(bundle: Bundle = .main) -> URL? { - bundle.resourceURL?.appendingPathComponent("bin/cmux", isDirectory: false) + bundle.resourceURL?.appendingPathComponent("bin/programa", isDirectory: false) } private static func defaultBundledCLIExpectedPath(bundle: Bundle = .main) -> String { bundle.bundleURL - .appendingPathComponent("Contents/Resources/bin/cmux", isDirectory: false) + .appendingPathComponent("Contents/Resources/bin/programa", isDirectory: false) .path } @@ -7336,7 +7336,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent } func sendWelcomeCommandWhenReady(to workspace: Workspace, markShownOnSend: Bool = false) { - sendTextWhenReady("cmux welcome\n", to: workspace) { + sendTextWhenReady("programa welcome\n", to: workspace) { if markShownOnSend { UserDefaults.standard.set(true, forKey: WelcomeSettings.shownKey) } @@ -12864,7 +12864,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent private func observeDuplicateLaunches() { guard let bundleId = Bundle.main.bundleIdentifier else { return } let embeddedCLIURL = Bundle.main.bundleURL - .appendingPathComponent("Contents/Resources/bin/cmux", isDirectory: false) + .appendingPathComponent("Contents/Resources/bin/programa", isDirectory: false) .standardizedFileURL .resolvingSymlinksInPath() let currentPid = ProcessInfo.processInfo.processIdentifier @@ -13491,7 +13491,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent @MainActor final class MenuBarExtraController: NSObject, NSMenuDelegate { private let statusItem: NSStatusItem - private let menu = NSMenu(title: "cmux") + private let menu = NSMenu(title: "Programa") private let notificationStore: TerminalNotificationStore private let onShowNotifications: () -> Void private let onOpenNotification: (TerminalNotification) -> Void @@ -13543,7 +13543,7 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate { button.imagePosition = .imageOnly button.imageScaling = .scaleProportionallyDown button.image = MenuBarIconRenderer.makeImage(unreadCount: 0) - button.toolTip = "cmux" + button.toolTip = "Programa" } notificationsCancellable = notificationStore.$notifications @@ -13647,7 +13647,7 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate { if let button = statusItem.button { button.image = MenuBarIconRenderer.makeImage(unreadCount: displayedUnreadCount) button.toolTip = displayedUnreadCount == 0 - ? "cmux" + ? "Programa" : displayedUnreadCount == 1 ? "Programa: " + String(localized: "statusMenu.tooltip.unread.one", defaultValue: "1 unread notification") : "Programa: " + String(localized: "statusMenu.tooltip.unread.other", defaultValue: "\(displayedUnreadCount) unread notifications") diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index 4d067741b97..d53cc04e791 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -4099,7 +4099,7 @@ final class TerminalSurface: Identifiable, ObservableObject { setManagedEnvironmentValue("PROGRAMA_SOCKET_PATH", socketPath) // Backward-compatible alias expected by older scripts and third-party integrations. setManagedEnvironmentValue("PROGRAMA_SOCKET", socketPath) - if let bundledCLIURL = Bundle.main.resourceURL?.appendingPathComponent("bin/cmux"), + if let bundledCLIURL = Bundle.main.resourceURL?.appendingPathComponent("bin/programa"), FileManager.default.isExecutableFile(atPath: bundledCLIURL.path) { setManagedEnvironmentValue("PROGRAMA_BUNDLED_CLI_PATH", bundledCLIURL.path) } diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index 38eddeb156b..336088ef1e5 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -3642,13 +3642,13 @@ class TabManager: ObservableObject { } private func windowTitle(for tab: Workspace?) -> String { - guard let tab else { return "cmux" } + guard let tab else { return "Programa" } let trimmedTitle = tab.title.trimmingCharacters(in: .whitespacesAndNewlines) if !trimmedTitle.isEmpty { return trimmedTitle } let trimmedDirectory = tab.currentDirectory.trimmingCharacters(in: .whitespacesAndNewlines) - return trimmedDirectory.isEmpty ? "cmux" : trimmedDirectory + return trimmedDirectory.isEmpty ? "Programa" : trimmedDirectory } func focusTab(_ tabId: UUID, surfaceId: UUID? = nil, suppressFlash: Bool = false) { diff --git a/Sources/TerminalNotificationStore.swift b/Sources/TerminalNotificationStore.swift index 4eee4361012..f63d8373ac3 100644 --- a/Sources/TerminalNotificationStore.swift +++ b/Sources/TerminalNotificationStore.swift @@ -1123,7 +1123,7 @@ final class TerminalNotificationStore: ObservableObject { private func resolvedNotificationTitle(for notification: TerminalNotification) -> String { let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String - ?? "cmux" + ?? "Programa" return notification.title.isEmpty ? appName : notification.title } From 5b0a7c910765ad0cd8b351b544893d6294a95af6 Mon Sep 17 00:00:00 2001 From: arzafran Date: Thu, 2 Jul 2026 11:41:01 -0300 Subject: [PATCH 20/23] =?UTF-8?q?refactor:=20rename=20shell-integration=20?= =?UTF-8?q?cmux=5F*=20=E2=86=92=20programa=5F*=20in=20lockstep=20(Phase=20?= =?UTF-8?q?6d)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the two integration scripts (cmux-{bash,zsh}-integration → programa-*) and every cmux_ shell-protocol token in lockstep across the scripts, the embedded-shell Swift string literals (Workspace, AppDelegate, BrowserPopupWindowController), and CLI/programa.swift (SSH bootstrap + loaders). The cross-boundary env vars were already PROGRAMA_*. PostHog analytics events (cmux_daily_active/hourly_active) intentionally preserved for continuity. Compiles; shell integration behavior verified by E2E CI (port detection, PR sidebar). --- CLI/programa.swift | 184 +++++++++--------- ...on.bash => programa-bash-integration.bash} | 14 +- ...ation.zsh => programa-zsh-integration.zsh} | 8 +- Sources/AppDelegate.swift | 32 +-- Sources/GhosttyTerminalView.swift | 2 +- .../Panels/BrowserPopupWindowController.swift | 2 +- Sources/Workspace.swift | 142 +++++++------- 7 files changed, 192 insertions(+), 192 deletions(-) rename Resources/shell-integration/{cmux-bash-integration.bash => programa-bash-integration.bash} (98%) rename Resources/shell-integration/{cmux-zsh-integration.zsh => programa-zsh-integration.zsh} (99%) diff --git a/CLI/programa.swift b/CLI/programa.swift index dc8ee3f7d77..5251eb2c0b5 100644 --- a/CLI/programa.swift +++ b/CLI/programa.swift @@ -4403,19 +4403,19 @@ struct CMUXCLI { remoteBootstrapInstallShell(remoteRelayPort: options.remoteRelayPort) ) var lines: [String] = [ - "cmux_workspace_id=\"${PROGRAMA_WORKSPACE_ID:-}\"", - "cmux_surface_id=\"${PROGRAMA_SURFACE_ID:-}\"", - "cmux_remote_bootstrap_b64=\(shellQuote(encodedBootstrapScript))", - "cmux_remote_bootstrap=\"$(printf %s \"$cmux_remote_bootstrap_b64\" | base64 -d 2>/dev/null || printf %s \"$cmux_remote_bootstrap_b64\" | base64 -D 2>/dev/null)\"", - "cmux_remote_bootstrap=\"$(printf '%s' \"$cmux_remote_bootstrap\" | sed \"s/__PROGRAMA_WORKSPACE_ID__/$cmux_workspace_id/g; s/__PROGRAMA_SURFACE_ID__/$cmux_surface_id/g\")\"", - "if ! printf '%s' \"$cmux_remote_bootstrap\" | command \(installSSHPrefix) -T \(shellQuote(options.destination)) \(shellQuote(remoteBootstrapInstallCommand)); then", + "programa_workspace_id=\"${PROGRAMA_WORKSPACE_ID:-}\"", + "programa_surface_id=\"${PROGRAMA_SURFACE_ID:-}\"", + "programa_remote_bootstrap_b64=\(shellQuote(encodedBootstrapScript))", + "programa_remote_bootstrap=\"$(printf %s \"$programa_remote_bootstrap_b64\" | base64 -d 2>/dev/null || printf %s \"$programa_remote_bootstrap_b64\" | base64 -D 2>/dev/null)\"", + "programa_remote_bootstrap=\"$(printf '%s' \"$programa_remote_bootstrap\" | sed \"s/__PROGRAMA_WORKSPACE_ID__/$programa_workspace_id/g; s/__PROGRAMA_SURFACE_ID__/$programa_surface_id/g\")\"", + "if ! printf '%s' \"$programa_remote_bootstrap\" | command \(installSSHPrefix) -T \(shellQuote(options.destination)) \(shellQuote(remoteBootstrapInstallCommand)); then", " exit 1", "fi", - "cmux_remote_command_template=\(shellQuote(remoteCommandTemplate))", - "cmux_remote_command=\"$(printf '%s' \"$cmux_remote_command_template\" | sed \"s/__PROGRAMA_WORKSPACE_ID__/$cmux_workspace_id/g; s/__PROGRAMA_SURFACE_ID__/$cmux_surface_id/g\")\"", + "programa_remote_command_template=\(shellQuote(remoteCommandTemplate))", + "programa_remote_command=\"$(printf '%s' \"$programa_remote_command_template\" | sed \"s/__PROGRAMA_WORKSPACE_ID__/$programa_workspace_id/g; s/__PROGRAMA_SURFACE_ID__/$programa_surface_id/g\")\"", ] - var sshInvocation = "command \(sessionSSHPrefix) -o \"RemoteCommand=$cmux_remote_command\"" + var sshInvocation = "command \(sessionSSHPrefix) -o \"RemoteCommand=$programa_remote_command\"" if !hasSSHOptionKey(options.sshOptions, key: "RequestTTY") { sshInvocation += " -tt" } @@ -4436,10 +4436,10 @@ struct CMUXCLI { [ "set -eu", "umask 077", - "cmux_bootstrap_path=\"$HOME/.programa/relay/\(remoteRelayPort).bootstrap.sh\"", + "programa_bootstrap_path=\"$HOME/.programa/relay/\(remoteRelayPort).bootstrap.sh\"", "mkdir -p \"$HOME/.programa/relay\"", - "cat > \"$cmux_bootstrap_path\"", - "chmod 700 \"$cmux_bootstrap_path\" >/dev/null 2>&1 || true", + "cat > \"$programa_bootstrap_path\"", + "chmod 700 \"$programa_bootstrap_path\" >/dev/null 2>&1 || true", ].joined(separator: "\n") } @@ -4449,13 +4449,13 @@ struct CMUXCLI { ) -> String { var lines = remoteBootstrapTTYCaptureLines(remoteRelayPort: remoteRelayPort, includeRelayRPC: false) lines += [ - "cmux_tmp=$(mktemp \"${TMPDIR:-/tmp}/cmux-ssh-bootstrap.XXXXXX\") || exit 1", - "(printf %s '\(base64Placeholder)' | base64 -d 2>/dev/null || printf %s '\(base64Placeholder)' | base64 -D 2>/dev/null) > \"$cmux_tmp\" || { rm -f \"$cmux_tmp\"; exit 1; }", - "chmod 700 \"$cmux_tmp\" >/dev/null 2>&1 || true", - "/bin/sh \"$cmux_tmp\"", - "cmux_status=$?", - "rm -f \"$cmux_tmp\"", - "exit $cmux_status", + "programa_tmp=$(mktemp \"${TMPDIR:-/tmp}/cmux-ssh-bootstrap.XXXXXX\") || exit 1", + "(printf %s '\(base64Placeholder)' | base64 -d 2>/dev/null || printf %s '\(base64Placeholder)' | base64 -D 2>/dev/null) > \"$programa_tmp\" || { rm -f \"$programa_tmp\"; exit 1; }", + "chmod 700 \"$programa_tmp\" >/dev/null 2>&1 || true", + "/bin/sh \"$programa_tmp\"", + "programa_status=$?", + "rm -f \"$programa_tmp\"", + "exit $programa_status", ] return lines.joined(separator: "\n") } @@ -4467,28 +4467,28 @@ struct CMUXCLI { guard remoteRelayPort > 0 else { return [] } var lines: [String] = [ - "cmux_bootstrap_tty=\"$(tty 2>/dev/null || true)\"", - "cmux_bootstrap_tty=\"${cmux_bootstrap_tty##*/}\"", - "if [ -n \"$cmux_bootstrap_tty\" ] && [ \"$cmux_bootstrap_tty\" != \"not a tty\" ]; then", + "programa_bootstrap_tty=\"$(tty 2>/dev/null || true)\"", + "programa_bootstrap_tty=\"${programa_bootstrap_tty##*/}\"", + "if [ -n \"$programa_bootstrap_tty\" ] && [ \"$programa_bootstrap_tty\" != \"not a tty\" ]; then", " mkdir -p \"$HOME/.programa/relay\" >/dev/null 2>&1 || true", - " printf '%s' \"$cmux_bootstrap_tty\" > \"$HOME/.programa/relay/\(remoteRelayPort).tty\" 2>/dev/null || true", - " export PROGRAMA_BOOTSTRAP_TTY=\"$cmux_bootstrap_tty\"", + " printf '%s' \"$programa_bootstrap_tty\" > \"$HOME/.programa/relay/\(remoteRelayPort).tty\" 2>/dev/null || true", + " export PROGRAMA_BOOTSTRAP_TTY=\"$programa_bootstrap_tty\"", ] if includeRelayRPC { lines += [ - " cmux_relay_cli=\"$HOME/.programa/bin/programa\"", - " if [ ! -x \"$cmux_relay_cli\" ]; then cmux_relay_cli=\"$(command -v programa 2>/dev/null || true)\"; fi", - " if [ -n \"$cmux_relay_cli\" ]; then", - " cmux_relay_report_tty='{\"workspace_id\":\"__PROGRAMA_WORKSPACE_ID__\",\"tty_name\":\"'$cmux_bootstrap_tty'\"}'", - " cmux_relay_ports_kick='{\"workspace_id\":\"__PROGRAMA_WORKSPACE_ID__\",\"reason\":\"command\"}'", + " programa_relay_cli=\"$HOME/.programa/bin/programa\"", + " if [ ! -x \"$programa_relay_cli\" ]; then programa_relay_cli=\"$(command -v programa 2>/dev/null || true)\"; fi", + " if [ -n \"$programa_relay_cli\" ]; then", + " programa_relay_report_tty='{\"workspace_id\":\"__PROGRAMA_WORKSPACE_ID__\",\"tty_name\":\"'$programa_bootstrap_tty'\"}'", + " programa_relay_ports_kick='{\"workspace_id\":\"__PROGRAMA_WORKSPACE_ID__\",\"reason\":\"command\"}'", " if [ -n \"__PROGRAMA_SURFACE_ID__\" ]; then", - " cmux_relay_report_tty='{\"workspace_id\":\"__PROGRAMA_WORKSPACE_ID__\",\"surface_id\":\"__PROGRAMA_SURFACE_ID__\",\"tty_name\":\"'$cmux_bootstrap_tty'\"}'", - " cmux_relay_ports_kick='{\"workspace_id\":\"__PROGRAMA_WORKSPACE_ID__\",\"surface_id\":\"__PROGRAMA_SURFACE_ID__\",\"reason\":\"command\"}'", + " programa_relay_report_tty='{\"workspace_id\":\"__PROGRAMA_WORKSPACE_ID__\",\"surface_id\":\"__PROGRAMA_SURFACE_ID__\",\"tty_name\":\"'$programa_bootstrap_tty'\"}'", + " programa_relay_ports_kick='{\"workspace_id\":\"__PROGRAMA_WORKSPACE_ID__\",\"surface_id\":\"__PROGRAMA_SURFACE_ID__\",\"reason\":\"command\"}'", " fi", - " PROGRAMA_SOCKET_PATH=\"127.0.0.1:\(remoteRelayPort)\" PROGRAMA_SOCKET=\"127.0.0.1:\(remoteRelayPort)\" \"$cmux_relay_cli\" rpc surface.report_tty \"$cmux_relay_report_tty\" >/dev/null 2>&1 || true", - " PROGRAMA_SOCKET_PATH=\"127.0.0.1:\(remoteRelayPort)\" PROGRAMA_SOCKET=\"127.0.0.1:\(remoteRelayPort)\" \"$cmux_relay_cli\" rpc surface.ports_kick \"$cmux_relay_ports_kick\" >/dev/null 2>&1 || true", - " unset cmux_relay_cli cmux_relay_report_tty cmux_relay_ports_kick", + " PROGRAMA_SOCKET_PATH=\"127.0.0.1:\(remoteRelayPort)\" PROGRAMA_SOCKET=\"127.0.0.1:\(remoteRelayPort)\" \"$programa_relay_cli\" rpc surface.report_tty \"$programa_relay_report_tty\" >/dev/null 2>&1 || true", + " PROGRAMA_SOCKET_PATH=\"127.0.0.1:\(remoteRelayPort)\" PROGRAMA_SOCKET=\"127.0.0.1:\(remoteRelayPort)\" \"$programa_relay_cli\" rpc surface.ports_kick \"$programa_relay_ports_kick\" >/dev/null 2>&1 || true", + " unset programa_relay_cli programa_relay_report_tty programa_relay_ports_kick", " fi", ] } @@ -4535,19 +4535,19 @@ struct CMUXCLI { ]) var zshShellLines = commonShellExportLines zshShellLines.append( - #"if [ "${PROGRAMA_SHELL_INTEGRATION:-1}" != "0" ] && [ -r "${PROGRAMA_SHELL_INTEGRATION_DIR}/cmux-zsh-integration.zsh" ]; then . "${PROGRAMA_SHELL_INTEGRATION_DIR}/cmux-zsh-integration.zsh"; fi"# + #"if [ "${PROGRAMA_SHELL_INTEGRATION:-1}" != "0" ] && [ -r "${PROGRAMA_SHELL_INTEGRATION_DIR}/programa-zsh-integration.zsh" ]; then . "${PROGRAMA_SHELL_INTEGRATION_DIR}/programa-zsh-integration.zsh"; fi"# ) var bashShellLines = commonShellExportLines bashShellLines.append( - #"if [ "${PROGRAMA_SHELL_INTEGRATION:-1}" != "0" ] && [ -r "${PROGRAMA_SHELL_INTEGRATION_DIR}/cmux-bash-integration.bash" ]; then . "${PROGRAMA_SHELL_INTEGRATION_DIR}/cmux-bash-integration.bash"; fi"# + #"if [ "${PROGRAMA_SHELL_INTEGRATION:-1}" != "0" ] && [ -r "${PROGRAMA_SHELL_INTEGRATION_DIR}/programa-bash-integration.bash" ]; then . "${PROGRAMA_SHELL_INTEGRATION_DIR}/programa-bash-integration.bash"; fi"# ) let zshBootstrap = RemoteRelayZshBootstrap(shellStateDir: shellStateDir) let zshEnvLines = zshBootstrap.zshEnvLines let zshProfileLines = zshBootstrap.zshProfileLines let zshRCLines = zshBootstrap.zshRCLines(commonShellLines: zshShellLines) let zshLoginLines = zshBootstrap.zshLoginLines - let bundledZshIntegration = bundledShellIntegrationScript(named: "cmux-zsh-integration.zsh") - let bundledBashIntegration = bundledShellIntegrationScript(named: "cmux-bash-integration.bash") + let bundledZshIntegration = bundledShellIntegrationScript(named: "programa-zsh-integration.zsh") + let bundledBashIntegration = bundledShellIntegrationScript(named: "programa-bash-integration.bash") let bashRCLines = [ "if [ -f \"$HOME/.bash_profile\" ]; then . \"$HOME/.bash_profile\"; elif [ -f \"$HOME/.bash_login\" ]; then . \"$HOME/.bash_login\"; elif [ -f \"$HOME/.profile\" ]; then . \"$HOME/.profile\"; fi", "[ -f \"$HOME/.bashrc\" ] && . \"$HOME/.bashrc\"", @@ -4556,19 +4556,19 @@ struct CMUXCLI { var outerLines: [String] = [ "mkdir -p \"$HOME/.programa/relay\"", - "cmux_shell_dir=\"\(shellStateDir)\"", - "mkdir -p \"$cmux_shell_dir\"", + "programa_shell_dir=\"\(shellStateDir)\"", + "mkdir -p \"$programa_shell_dir\"", ] if let bundledZshIntegration { outerLines += [ - "cat > \"$cmux_shell_dir/cmux-zsh-integration.zsh\" <<'CMUXCMUXZSH'", + "cat > \"$programa_shell_dir/programa-zsh-integration.zsh\" <<'CMUXCMUXZSH'", bundledZshIntegration, "CMUXCMUXZSH", ] } if let bundledBashIntegration { outerLines += [ - "cat > \"$cmux_shell_dir/cmux-bash-integration.bash\" <<'CMUXCMUXBASH'", + "cat > \"$programa_shell_dir/programa-bash-integration.bash\" <<'CMUXCMUXBASH'", bundledBashIntegration, "CMUXCMUXBASH", ] @@ -4578,45 +4578,45 @@ struct CMUXCLI { "PROGRAMA_LOGIN_SHELL=\"${SHELL:-/bin/zsh}\"", "case \"${PROGRAMA_LOGIN_SHELL##*/}\" in", " zsh)", - " cat > \"$cmux_shell_dir/.zshenv\" <<'CMUXZSHENV'", + " cat > \"$programa_shell_dir/.zshenv\" <<'CMUXZSHENV'", ] outerLines.append(contentsOf: zshEnvLines) outerLines += [ "CMUXZSHENV", - " cat > \"$cmux_shell_dir/.zprofile\" <<'CMUXZSHPROFILE'", + " cat > \"$programa_shell_dir/.zprofile\" <<'CMUXZSHPROFILE'", ] outerLines.append(contentsOf: zshProfileLines) outerLines += [ "CMUXZSHPROFILE", - " cat > \"$cmux_shell_dir/.zshrc\" <<'CMUXZSHRC'", + " cat > \"$programa_shell_dir/.zshrc\" <<'CMUXZSHRC'", ] outerLines.append(contentsOf: zshRCLines) outerLines += [ "CMUXZSHRC", - " cat > \"$cmux_shell_dir/.zlogin\" <<'CMUXZSHLOGIN'", + " cat > \"$programa_shell_dir/.zlogin\" <<'CMUXZSHLOGIN'", ] outerLines.append(contentsOf: zshLoginLines) outerLines += [ "CMUXZSHLOGIN", - " chmod 600 \"$cmux_shell_dir/.zshenv\" \"$cmux_shell_dir/.zprofile\" \"$cmux_shell_dir/.zshrc\" \"$cmux_shell_dir/.zlogin\" >/dev/null 2>&1 || true", + " chmod 600 \"$programa_shell_dir/.zshenv\" \"$programa_shell_dir/.zprofile\" \"$programa_shell_dir/.zshrc\" \"$programa_shell_dir/.zlogin\" >/dev/null 2>&1 || true", ] outerLines.append(contentsOf: relayWarmupLines.map { " " + $0 }) outerLines += [ " export PROGRAMA_REAL_ZDOTDIR=\"${ZDOTDIR:-$HOME}\"", - " export ZDOTDIR=\"$cmux_shell_dir\"", + " export ZDOTDIR=\"$programa_shell_dir\"", " exec \"$PROGRAMA_LOGIN_SHELL\" -il", " ;;", " bash)", - " cat > \"$cmux_shell_dir/.bashrc\" <<'CMUXBASHRC'", + " cat > \"$programa_shell_dir/.bashrc\" <<'CMUXBASHRC'", ] outerLines.append(contentsOf: bashRCLines) outerLines += [ "CMUXBASHRC", - " chmod 600 \"$cmux_shell_dir/.bashrc\" >/dev/null 2>&1 || true", + " chmod 600 \"$programa_shell_dir/.bashrc\" >/dev/null 2>&1 || true", ] outerLines.append(contentsOf: relayWarmupLines.map { " " + $0 }) outerLines += [ - " exec \"$PROGRAMA_LOGIN_SHELL\" --rcfile \"$cmux_shell_dir/.bashrc\" -i", + " exec \"$PROGRAMA_LOGIN_SHELL\" --rcfile \"$programa_shell_dir/.bashrc\" -i", " ;;", " *)", ] @@ -4704,17 +4704,17 @@ struct CMUXCLI { private func interactiveRemoteTerminalSetupLines(terminfoSource: String?) -> [String] { var lines: [String] = [ - "cmux_term='xterm-256color'", + "programa_term='xterm-256color'", "if command -v infocmp >/dev/null 2>&1 && infocmp xterm-ghostty >/dev/null 2>&1; then", - " cmux_term='xterm-ghostty'", + " programa_term='xterm-ghostty'", "fi", - "export TERM=\"$cmux_term\"", + "export TERM=\"$programa_term\"", ] guard let terminfoSource else { return lines } let trimmedTerminfoSource = terminfoSource.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmedTerminfoSource.isEmpty else { return lines } lines += [ - "if [ \"$cmux_term\" != 'xterm-ghostty' ]; then", + "if [ \"$programa_term\" != 'xterm-ghostty' ]; then", " (", " command -v tic >/dev/null 2>&1 || exit 0", " mkdir -p \"$HOME/.terminfo\" 2>/dev/null || exit 0", @@ -4754,26 +4754,26 @@ struct CMUXCLI { return [] } return [ - "cmux_relay_cli=\"${PROGRAMA_BUNDLED_CLI_PATH:-$HOME/.programa/bin/programa}\"", - "if [ ! -x \"$cmux_relay_cli\" ]; then cmux_relay_cli=\"$(command -v programa 2>/dev/null || true)\"; fi", - "cmux_relay_tty=\"${PROGRAMA_BOOTSTRAP_TTY:-}\"", - "if [ -z \"$cmux_relay_tty\" ]; then cmux_relay_tty=\"$(tty 2>/dev/null || true)\"; fi", - "cmux_relay_tty=\"${cmux_relay_tty##*/}\"", - "if [ -n \"$cmux_relay_tty\" ] && [ \"$cmux_relay_tty\" != \"not a tty\" ]; then", + "programa_relay_cli=\"${PROGRAMA_BUNDLED_CLI_PATH:-$HOME/.programa/bin/programa}\"", + "if [ ! -x \"$programa_relay_cli\" ]; then programa_relay_cli=\"$(command -v programa 2>/dev/null || true)\"; fi", + "programa_relay_tty=\"${PROGRAMA_BOOTSTRAP_TTY:-}\"", + "if [ -z \"$programa_relay_tty\" ]; then programa_relay_tty=\"$(tty 2>/dev/null || true)\"; fi", + "programa_relay_tty=\"${programa_relay_tty##*/}\"", + "if [ -n \"$programa_relay_tty\" ] && [ \"$programa_relay_tty\" != \"not a tty\" ]; then", " mkdir -p \"$HOME/.programa/relay\" >/dev/null 2>&1 || true", - " printf '%s' \"$cmux_relay_tty\" > \"$HOME/.programa/relay/\(remoteRelayPort).tty\" 2>/dev/null || true", + " printf '%s' \"$programa_relay_tty\" > \"$HOME/.programa/relay/\(remoteRelayPort).tty\" 2>/dev/null || true", "fi", - "if [ -n \"$cmux_relay_cli\" ] && [ -n \"$PROGRAMA_WORKSPACE_ID\" ] && [ -n \"$cmux_relay_tty\" ] && [ \"$cmux_relay_tty\" != \"not a tty\" ]; then", - " cmux_relay_report_tty=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"tty_name\\\":\\\"$cmux_relay_tty\\\"}\"", - " cmux_relay_ports_kick=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"reason\\\":\\\"command\\\"}\"", + "if [ -n \"$programa_relay_cli\" ] && [ -n \"$PROGRAMA_WORKSPACE_ID\" ] && [ -n \"$programa_relay_tty\" ] && [ \"$programa_relay_tty\" != \"not a tty\" ]; then", + " programa_relay_report_tty=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"tty_name\\\":\\\"$programa_relay_tty\\\"}\"", + " programa_relay_ports_kick=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"reason\\\":\\\"command\\\"}\"", " if [ -n \"$PROGRAMA_SURFACE_ID\" ]; then", - " cmux_relay_report_tty=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"surface_id\\\":\\\"$PROGRAMA_SURFACE_ID\\\",\\\"tty_name\\\":\\\"$cmux_relay_tty\\\"}\"", - " cmux_relay_ports_kick=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"surface_id\\\":\\\"$PROGRAMA_SURFACE_ID\\\",\\\"reason\\\":\\\"command\\\"}\"", + " programa_relay_report_tty=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"surface_id\\\":\\\"$PROGRAMA_SURFACE_ID\\\",\\\"tty_name\\\":\\\"$programa_relay_tty\\\"}\"", + " programa_relay_ports_kick=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"surface_id\\\":\\\"$PROGRAMA_SURFACE_ID\\\",\\\"reason\\\":\\\"command\\\"}\"", " fi", - " \"$cmux_relay_cli\" rpc surface.report_tty \"$cmux_relay_report_tty\" >/dev/null 2>&1 || true", - " \"$cmux_relay_cli\" rpc surface.ports_kick \"$cmux_relay_ports_kick\" >/dev/null 2>&1 || true", + " \"$programa_relay_cli\" rpc surface.report_tty \"$programa_relay_report_tty\" >/dev/null 2>&1 || true", + " \"$programa_relay_cli\" rpc surface.ports_kick \"$programa_relay_ports_kick\" >/dev/null 2>&1 || true", "fi", - "unset PROGRAMA_BOOTSTRAP_TTY cmux_relay_cli cmux_relay_tty cmux_relay_report_tty cmux_relay_ports_kick", + "unset PROGRAMA_BOOTSTRAP_TTY programa_relay_cli programa_relay_tty programa_relay_report_tty programa_relay_ports_kick", ] } @@ -4877,13 +4877,13 @@ struct CMUXCLI { let encodedLiteral = shellQuote(encodedScript) var lines = remoteBootstrapTTYCaptureLines(remoteRelayPort: remoteRelayPort, includeRelayRPC: false) lines += [ - "cmux_tmp=$(mktemp \"${TMPDIR:-/tmp}/cmux-ssh-bootstrap.XXXXXX\") || exit 1", - "(printf %s \(encodedLiteral) | base64 -d 2>/dev/null || printf %s \(encodedLiteral) | base64 -D 2>/dev/null) > \"$cmux_tmp\" || { rm -f \"$cmux_tmp\"; exit 1; }", - "chmod 700 \"$cmux_tmp\" >/dev/null 2>&1 || true", - "/bin/sh \"$cmux_tmp\"", - "cmux_status=$?", - "rm -f \"$cmux_tmp\"", - "exit $cmux_status", + "programa_tmp=$(mktemp \"${TMPDIR:-/tmp}/cmux-ssh-bootstrap.XXXXXX\") || exit 1", + "(printf %s \(encodedLiteral) | base64 -d 2>/dev/null || printf %s \(encodedLiteral) | base64 -D 2>/dev/null) > \"$programa_tmp\" || { rm -f \"$programa_tmp\"; exit 1; }", + "chmod 700 \"$programa_tmp\" >/dev/null 2>&1 || true", + "/bin/sh \"$programa_tmp\"", + "programa_status=$?", + "rm -f \"$programa_tmp\"", + "exit $programa_status", ] return lines.joined(separator: "\n") } @@ -4909,8 +4909,8 @@ struct CMUXCLI { } scriptLines += [ "PROGRAMA_SSH_SESSION_ENDED=0", - "cmux_ssh_session_end() { if [ \"${PROGRAMA_SSH_SESSION_ENDED:-0}\" = 1 ]; then return; fi; PROGRAMA_SSH_SESSION_ENDED=1; \(lifecycleCleanup); }", - "trap 'cmux_ssh_session_end' EXIT HUP INT TERM", + "programa_ssh_session_end() { if [ \"${PROGRAMA_SSH_SESSION_ENDED:-0}\" = 1 ]; then return; fi; PROGRAMA_SSH_SESSION_ENDED=1; \(lifecycleCleanup); }", + "trap 'programa_ssh_session_end' EXIT HUP INT TERM", ] if isShellSnippet { scriptLines.append(sshCommand) @@ -4918,10 +4918,10 @@ struct CMUXCLI { scriptLines.append("command \(sshCommand)") } scriptLines += [ - "cmux_ssh_status=$?", + "programa_ssh_status=$?", "trap - EXIT HUP INT TERM", - "cmux_ssh_session_end", - "exit $cmux_ssh_status", + "programa_ssh_session_end", + "exit $programa_ssh_status", ] let script = scriptLines.joined(separator: "\n") return try writeSSHStartupScript(script, remoteRelayPort: remoteRelayPort) @@ -5168,20 +5168,20 @@ struct CMUXCLI { .replacingOccurrences(of: "\\", with: "\\\\") .replacingOccurrences(of: "\"", with: "\\\"") return [ - preferredCLIPath.map { "cmux_reconnect_cli=\(shellQuote($0));" } ?? "cmux_reconnect_cli=\"\";", - "cmux_reconnect_socket=\"${PROGRAMA_SOCKET_PATH:-${PROGRAMA_SOCKET:-}}\";", - "if [ -z \"$cmux_reconnect_cli\" ] && [ -n \"${PROGRAMA_BUNDLED_CLI_PATH:-}\" ]; then cmux_reconnect_cli=\"$PROGRAMA_BUNDLED_CLI_PATH\"; fi;", - "if [ ! -x \"$cmux_reconnect_cli\" ]; then cmux_reconnect_cli=\"$(command -v cmux 2>/dev/null || true)\"; fi;", + preferredCLIPath.map { "programa_reconnect_cli=\(shellQuote($0));" } ?? "programa_reconnect_cli=\"\";", + "programa_reconnect_socket=\"${PROGRAMA_SOCKET_PATH:-${PROGRAMA_SOCKET:-}}\";", + "if [ -z \"$programa_reconnect_cli\" ] && [ -n \"${PROGRAMA_BUNDLED_CLI_PATH:-}\" ]; then programa_reconnect_cli=\"$PROGRAMA_BUNDLED_CLI_PATH\"; fi;", + "if [ ! -x \"$programa_reconnect_cli\" ]; then programa_reconnect_cli=\"$(command -v cmux 2>/dev/null || true)\"; fi;", "if [ -n \"${PROGRAMA_WORKSPACE_ID:-}\" ]; then", - "if [ -z \"$cmux_reconnect_socket\" ]; then printf '%s\\n' 'cmux: deferred SSH reconnect skipped, local cmux socket not found' >&2;", - "elif [ -z \"$cmux_reconnect_cli\" ] || [ ! -x \"$cmux_reconnect_cli\" ]; then printf '%s\\n' 'cmux: deferred SSH reconnect skipped, local cmux CLI not found' >&2;", + "if [ -z \"$programa_reconnect_socket\" ]; then printf '%s\\n' 'cmux: deferred SSH reconnect skipped, local cmux socket not found' >&2;", + "elif [ -z \"$programa_reconnect_cli\" ] || [ ! -x \"$programa_reconnect_cli\" ]; then printf '%s\\n' 'cmux: deferred SSH reconnect skipped, local cmux CLI not found' >&2;", "else", - "cmux_reconnect_payload=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"foreground_auth_token\\\":\\\"\(escapedForegroundAuthToken)\\\"}\";", - "\"$cmux_reconnect_cli\" --socket \"$cmux_reconnect_socket\" rpc workspace.remote.foreground_auth_ready \"$cmux_reconnect_payload\" >/dev/null 2>&1 || true;", - "unset cmux_reconnect_payload;", + "programa_reconnect_payload=\"{\\\"workspace_id\\\":\\\"$PROGRAMA_WORKSPACE_ID\\\",\\\"foreground_auth_token\\\":\\\"\(escapedForegroundAuthToken)\\\"}\";", + "\"$programa_reconnect_cli\" --socket \"$programa_reconnect_socket\" rpc workspace.remote.foreground_auth_ready \"$programa_reconnect_payload\" >/dev/null 2>&1 || true;", + "unset programa_reconnect_payload;", "fi;", "fi;", - "unset cmux_reconnect_socket cmux_reconnect_cli;", + "unset programa_reconnect_socket programa_reconnect_cli;", ].joined(separator: " ") } diff --git a/Resources/shell-integration/cmux-bash-integration.bash b/Resources/shell-integration/programa-bash-integration.bash similarity index 98% rename from Resources/shell-integration/cmux-bash-integration.bash rename to Resources/shell-integration/programa-bash-integration.bash index c9cbda92b51..624e9974ed0 100644 --- a/Resources/shell-integration/cmux-bash-integration.bash +++ b/Resources/shell-integration/programa-bash-integration.bash @@ -830,9 +830,9 @@ _cmux_preexec_command() { local cmd="${1:-${BASH_COMMAND:-}}" _cmux_tmux_sync_cmux_environment - local cmux_has_unix_socket=0 - _cmux_socket_is_unix && cmux_has_unix_socket=1 - (( cmux_has_unix_socket )) || _cmux_has_port_scan_transport || return 0 + local programa_has_unix_socket=0 + _cmux_socket_is_unix && programa_has_unix_socket=1 + (( programa_has_unix_socket )) || _cmux_has_port_scan_transport || return 0 [[ -n "$PROGRAMA_TAB_ID" ]] || return 0 if [[ -z "$_PROGRAMA_TTY_NAME" ]]; then @@ -858,9 +858,9 @@ _cmux_bash_preexec_hook() { _cmux_prompt_command() { _cmux_tmux_sync_cmux_environment - local cmux_has_unix_socket=0 - _cmux_socket_is_unix && cmux_has_unix_socket=1 - (( cmux_has_unix_socket )) || _cmux_has_port_scan_transport || return 0 + local programa_has_unix_socket=0 + _cmux_socket_is_unix && programa_has_unix_socket=1 + (( programa_has_unix_socket )) || _cmux_has_port_scan_transport || return 0 [[ -n "$PROGRAMA_TAB_ID" ]] || return 0 if [[ -z "$_PROGRAMA_TTY_NAME" ]]; then @@ -877,7 +877,7 @@ _cmux_prompt_command() { local now now="$(_cmux_now)" - if (( ! cmux_has_unix_socket )); then + if (( ! programa_has_unix_socket )); then if (( now - _PROGRAMA_PORTS_LAST_RUN >= 10 )); then _cmux_ports_kick refresh fi diff --git a/Resources/shell-integration/cmux-zsh-integration.zsh b/Resources/shell-integration/programa-zsh-integration.zsh similarity index 99% rename from Resources/shell-integration/cmux-zsh-integration.zsh rename to Resources/shell-integration/programa-zsh-integration.zsh index b4b4ff402ea..25b42c80c25 100644 --- a/Resources/shell-integration/cmux-zsh-integration.zsh +++ b/Resources/shell-integration/programa-zsh-integration.zsh @@ -1017,9 +1017,9 @@ _cmux_precmd() { _cmux_stop_git_head_watch _cmux_tmux_sync_cmux_environment - local cmux_has_unix_socket=0 - _cmux_socket_is_unix && cmux_has_unix_socket=1 - (( cmux_has_unix_socket )) || _cmux_has_port_scan_transport || return 0 + local programa_has_unix_socket=0 + _cmux_socket_is_unix && programa_has_unix_socket=1 + (( programa_has_unix_socket )) || _cmux_has_port_scan_transport || return 0 [[ -n "$PROGRAMA_TAB_ID" ]] || return 0 if [[ -n "$PROGRAMA_PANEL_ID" ]]; then _cmux_report_shell_activity_state prompt @@ -1045,7 +1045,7 @@ _cmux_precmd() { cmd_dur=$(( now - cmd_start )) fi - if (( ! cmux_has_unix_socket )); then + if (( ! programa_has_unix_socket )); then if (( cmd_dur >= 2 || now - _PROGRAMA_PORTS_LAST_RUN >= 10 )); then _cmux_ports_kick refresh fi diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index d9f00b1f9f3..4fb15b306f8 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -2407,7 +2407,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent private static let didInstallWindowKeyEquivalentSwizzle: Void = { let targetClass: AnyClass = NSWindow.self let originalSelector = #selector(NSWindow.performKeyEquivalent(with:)) - let swizzledSelector = #selector(NSWindow.cmux_performKeyEquivalent(with:)) + let swizzledSelector = #selector(NSWindow.programa_performKeyEquivalent(with:)) guard let originalMethod = class_getInstanceMethod(targetClass, originalSelector), let swizzledMethod = class_getInstanceMethod(targetClass, swizzledSelector) else { return @@ -2417,7 +2417,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent private static let didInstallWindowFirstResponderSwizzle: Void = { let targetClass: AnyClass = NSWindow.self let originalSelector = #selector(NSWindow.makeFirstResponder(_:)) - let swizzledSelector = #selector(NSWindow.cmux_makeFirstResponder(_:)) + let swizzledSelector = #selector(NSWindow.programa_makeFirstResponder(_:)) guard let originalMethod = class_getInstanceMethod(targetClass, originalSelector), let swizzledMethod = class_getInstanceMethod(targetClass, swizzledSelector) else { return @@ -2427,7 +2427,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent private static let didInstallWindowSendEventSwizzle: Void = { let targetClass: AnyClass = NSWindow.self let originalSelector = #selector(NSWindow.sendEvent(_:)) - let swizzledSelector = #selector(NSWindow.cmux_sendEvent(_:)) + let swizzledSelector = #selector(NSWindow.programa_sendEvent(_:)) guard let originalMethod = class_getInstanceMethod(targetClass, originalSelector), let swizzledMethod = class_getInstanceMethod(targetClass, swizzledSelector) else { return @@ -2437,7 +2437,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent private static let didInstallApplicationSendEventSwizzle: Void = { let targetClass: AnyClass = NSApplication.self let originalSelector = #selector(NSApplication.sendEvent(_:)) - let swizzledSelector = #selector(NSApplication.cmux_applicationSendEvent(_:)) + let swizzledSelector = #selector(NSApplication.programa_applicationSendEvent(_:)) guard let originalMethod = class_getInstanceMethod(targetClass, originalSelector), let swizzledMethod = class_getInstanceMethod(targetClass, swizzledSelector) else { return @@ -14182,7 +14182,7 @@ private final class ProgramaFieldEditorOwningWebViewBox: NSObject { } private extension NSApplication { - @objc func cmux_applicationSendEvent(_ event: NSEvent) { + @objc func programa_applicationSendEvent(_ event: NSEvent) { #if DEBUG let typingTimingStart = event.type == .keyDown ? ProgramaTypingTiming.start() : nil let phaseTotalStart = event.type == .keyDown ? ProcessInfo.processInfo.systemUptime : 0 @@ -14207,7 +14207,7 @@ private extension NSApplication { } } #endif - cmux_applicationSendEvent(event) + programa_applicationSendEvent(event) } } @@ -14220,7 +14220,7 @@ private extension AppDelegate { } private extension NSWindow { - @objc func cmux_makeFirstResponder(_ responder: NSResponder?) -> Bool { + @objc func programa_makeFirstResponder(_ responder: NSResponder?) -> Bool { if cmuxIsWindowFirstResponderBypassActive() { #if DEBUG dlog( @@ -14301,10 +14301,10 @@ private extension NSWindow { // `NSWindow.makeFirstResponder` may run before `ProgramaWebView.mouseDown(with:)`. // Preserve pointer intent during this synchronous responder change. result = webView.withPointerFocusAllowance { - cmux_makeFirstResponder(responder) + programa_makeFirstResponder(responder) } } else { - result = cmux_makeFirstResponder(responder) + result = programa_makeFirstResponder(responder) } if result { if let fieldEditor = responder as? NSTextView, fieldEditor.isFieldEditor { @@ -14316,7 +14316,7 @@ private extension NSWindow { return result } - @objc func cmux_sendEvent(_ event: NSEvent) { + @objc func programa_sendEvent(_ event: NSEvent) { #if DEBUG let typingTimingStart = event.type == .keyDown ? ProgramaTypingTiming.start() : nil let phaseTotalStart = event.type == .keyDown ? ProcessInfo.processInfo.systemUptime : 0 @@ -14407,12 +14407,12 @@ private extension NSWindow { if event.type == .keyDown { folderGuardMs = (ProcessInfo.processInfo.systemUptime - folderGuardStart) * 1000.0 let originalDispatchStart = ProcessInfo.processInfo.systemUptime - cmux_sendEvent(event) + programa_sendEvent(event) originalDispatchMs = (ProcessInfo.processInfo.systemUptime - originalDispatchStart) * 1000.0 return } #endif - cmux_sendEvent(event) + programa_sendEvent(event) return } #if DEBUG @@ -14434,7 +14434,7 @@ private extension NSWindow { dlog("window.sendEvent.folderDown suppress=1 hit=\(hitDesc) wasMovable=\(previousMovableState)") #endif - cmux_sendEvent(event) + programa_sendEvent(event) #if DEBUG if event.type == .keyDown { originalDispatchMs = (ProcessInfo.processInfo.systemUptime - originalDispatchStart) * 1000.0 @@ -14450,7 +14450,7 @@ private extension NSWindow { #endif } - @objc func cmux_performKeyEquivalent(with event: NSEvent) -> Bool { + @objc func programa_performKeyEquivalent(with event: NSEvent) -> Bool { #if DEBUG let typingTimingStart = ProgramaTypingTiming.start() defer { @@ -14490,7 +14490,7 @@ private extension NSWindow { // process it. Cmd-based shortcuts should still work during composition since // Cmd is never part of IME input sequences. if ghosttyView.hasMarkedText(), !event.modifierFlags.intersection(.deviceIndependentFlagsMask).contains(.command) { - return cmux_performKeyEquivalent(with: event) + return programa_performKeyEquivalent(with: event) } let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask) @@ -14605,7 +14605,7 @@ private extension NSWindow { } } - let result = cmux_performKeyEquivalent(with: event) + let result = programa_performKeyEquivalent(with: event) #if DEBUG if result { dlog(" → consumed by original performKeyEquivalent") } #endif diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index d53cc04e791..0b64ada103b 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -4185,7 +4185,7 @@ final class TerminalSurface: Identifiable, ObservableObject { [[ -r "$_programa_ghostty_bash" ]] && source "$_programa_ghostty_bash"; \ fi; \ if [[ "${PROGRAMA_SHELL_INTEGRATION:-1}" != "0" && -n "${PROGRAMA_SHELL_INTEGRATION_DIR:-}" ]]; then \ - _programa_bash_integration="$PROGRAMA_SHELL_INTEGRATION_DIR/cmux-bash-integration.bash"; \ + _programa_bash_integration="$PROGRAMA_SHELL_INTEGRATION_DIR/programa-bash-integration.bash"; \ [[ -r "$_programa_bash_integration" ]] && source "$_programa_bash_integration"; \ fi; \ unset _programa_ghostty_bash _programa_bash_integration; \ diff --git a/Sources/Panels/BrowserPopupWindowController.swift b/Sources/Panels/BrowserPopupWindowController.swift index 536935eaed8..743144ac2d9 100644 --- a/Sources/Panels/BrowserPopupWindowController.swift +++ b/Sources/Panels/BrowserPopupWindowController.swift @@ -43,7 +43,7 @@ func browserPopupContentRect( /// - The opener `BrowserPanel` also keeps a strong reference for deterministic /// cleanup when the opener tab or workspace is closed. /// NSPanel subclass that intercepts Cmd+W before the swizzled -/// `cmux_performKeyEquivalent` can dispatch it to the main menu's +/// `programa_performKeyEquivalent` can dispatch it to the main menu's /// "Close Tab" action (which would close the parent browser tab). private class BrowserPopupPanel: NSPanel { override func performKeyEquivalent(with event: NSEvent) -> Bool { diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 8060ffab476..8f58b4a6a08 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -4405,22 +4405,22 @@ final class WorkspaceRemoteSessionController { private func probeRemoteBootstrapStateLocked(version: String) throws -> RemoteBootstrapState { let script = """ - cmux_uname_os="$(uname -s)" - cmux_uname_arch="$(uname -m)" - printf '%s%s\\n' '\(Self.remotePlatformProbeOSMarker)' "$cmux_uname_os" - printf '%s%s\\n' '\(Self.remotePlatformProbeArchMarker)' "$cmux_uname_arch" - case "$(printf '%s' "$cmux_uname_os" | tr '[:upper:]' '[:lower:]')" in - linux|darwin|freebsd) cmux_go_os="$(printf '%s' "$cmux_uname_os" | tr '[:upper:]' '[:lower:]')" ;; + programa_uname_os="$(uname -s)" + programa_uname_arch="$(uname -m)" + printf '%s%s\\n' '\(Self.remotePlatformProbeOSMarker)' "$programa_uname_os" + printf '%s%s\\n' '\(Self.remotePlatformProbeArchMarker)' "$programa_uname_arch" + case "$(printf '%s' "$programa_uname_os" | tr '[:upper:]' '[:lower:]')" in + linux|darwin|freebsd) programa_go_os="$(printf '%s' "$programa_uname_os" | tr '[:upper:]' '[:lower:]')" ;; *) exit 70 ;; esac - case "$(printf '%s' "$cmux_uname_arch" | tr '[:upper:]' '[:lower:]')" in - x86_64|amd64) cmux_go_arch=amd64 ;; - aarch64|arm64) cmux_go_arch=arm64 ;; - armv7l) cmux_go_arch=arm ;; + case "$(printf '%s' "$programa_uname_arch" | tr '[:upper:]' '[:lower:]')" in + x86_64|amd64) programa_go_arch=amd64 ;; + aarch64|arm64) programa_go_arch=arm64 ;; + armv7l) programa_go_arch=arm ;; *) exit 71 ;; esac - cmux_remote_path="$HOME/.programa/bin/programad-remote/\(version)/${cmux_go_os}-${cmux_go_arch}/programad-remote" - if [ -x "$cmux_remote_path" ]; then + programa_remote_path="$HOME/.programa/bin/programad-remote/\(version)/${programa_go_os}-${programa_go_arch}/programad-remote" + if [ -x "$programa_remote_path" ]; then printf '%syes\\n' '\(Self.remotePlatformProbeExistsMarker)' else printf '%sno\\n' '\(Self.remotePlatformProbeExistsMarker)' @@ -5761,35 +5761,35 @@ final class WorkspaceRemoteSessionController { return """ set -eu - cmux_tracked_ttys=" \(ttySet) " - cmux_tty_csv='\(ttyCSV)' - cmux_excluded_ports=" \(excludedPorts) " - - cmux_emit_port() { - cmux_tty="$1" - cmux_port="$2" - case "$cmux_tracked_ttys" in - *" $cmux_tty "*) ;; + programa_tracked_ttys=" \(ttySet) " + programa_tty_csv='\(ttyCSV)' + programa_excluded_ports=" \(excludedPorts) " + + programa_emit_port() { + programa_tty="$1" + programa_port="$2" + case "$programa_tracked_ttys" in + *" $programa_tty "*) ;; *) return 0 ;; esac - case "$cmux_excluded_ports" in - *" $cmux_port "*) return 0 ;; + case "$programa_excluded_ports" in + *" $programa_port "*) return 0 ;; esac - [ "$cmux_port" -ge 1024 ] && [ "$cmux_port" -le 65535 ] || return 0 - printf '%s\\t%s\\n' "$cmux_tty" "$cmux_port" + [ "$programa_port" -ge 1024 ] && [ "$programa_port" -le 65535 ] || return 0 + printf '%s\\t%s\\n' "$programa_tty" "$programa_port" } - cmux_used_ss=0 + programa_used_ss=0 if [ -d /proc ] && command -v ss >/dev/null 2>&1; then - cmux_ss_output="$(ss -ltnpH 2>/dev/null || true)" - case "$cmux_ss_output" in + programa_ss_output="$(ss -ltnpH 2>/dev/null || true)" + case "$programa_ss_output" in *pid=*) - cmux_used_ss=1 - printf '%s\\n' "$cmux_ss_output" | while IFS= read -r cmux_line; do - [ -n "$cmux_line" ] || continue - cmux_port="$(printf '%s\\n' "$cmux_line" | awk '{print $4}' | sed -E 's/.*:([0-9]+)$/\\1/' | awk '/^[0-9]+$/ { print $1; exit }')" - [ -n "$cmux_port" ] || continue - printf '%s\\n' "$cmux_line" | awk ' + programa_used_ss=1 + printf '%s\\n' "$programa_ss_output" | while IFS= read -r programa_line; do + [ -n "$programa_line" ] || continue + programa_port="$(printf '%s\\n' "$programa_line" | awk '{print $4}' | sed -E 's/.*:([0-9]+)$/\\1/' | awk '/^[0-9]+$/ { print $1; exit }')" + [ -n "$programa_port" ] || continue + printf '%s\\n' "$programa_line" | awk ' { line = $0 while (match(line, /pid=[0-9]+/)) { @@ -5797,34 +5797,34 @@ final class WorkspaceRemoteSessionController { line = substr(line, RSTART + RLENGTH) } } - ' | while IFS= read -r cmux_pid; do - [ -n "$cmux_pid" ] || continue - cmux_tty_path="$(readlink "/proc/$cmux_pid/fd/0" 2>/dev/null || true)" - [ -n "$cmux_tty_path" ] || continue - cmux_tty="${cmux_tty_path##*/}" - [ -n "$cmux_tty" ] || continue - cmux_emit_port "$cmux_tty" "$cmux_port" + ' | while IFS= read -r programa_pid; do + [ -n "$programa_pid" ] || continue + programa_tty_path="$(readlink "/proc/$programa_pid/fd/0" 2>/dev/null || true)" + [ -n "$programa_tty_path" ] || continue + programa_tty="${programa_tty_path##*/}" + [ -n "$programa_tty" ] || continue + programa_emit_port "$programa_tty" "$programa_port" done done ;; esac fi - if [ "$cmux_used_ss" -eq 0 ] && command -v lsof >/dev/null 2>&1 && [ -n "$cmux_tty_csv" ]; then - cmux_tmpdir="$(mktemp -d 2>/dev/null || mktemp -d -t programa-ports)" - trap 'rm -rf "$cmux_tmpdir"' EXIT INT TERM - cmux_pid_tty_map="$cmux_tmpdir/pid_tty" - ps -t "$cmux_tty_csv" -o pid=,tty= 2>/dev/null | awk ' + if [ "$programa_used_ss" -eq 0 ] && command -v lsof >/dev/null 2>&1 && [ -n "$programa_tty_csv" ]; then + programa_tmpdir="$(mktemp -d 2>/dev/null || mktemp -d -t programa-ports)" + trap 'rm -rf "$programa_tmpdir"' EXIT INT TERM + programa_pid_tty_map="$programa_tmpdir/pid_tty" + ps -t "$programa_tty_csv" -o pid=,tty= 2>/dev/null | awk ' NF >= 2 { tty = $2 sub(/^.*\\//, "", tty) print $1 "\\t" tty } - ' > "$cmux_pid_tty_map" - [ -s "$cmux_pid_tty_map" ] || exit 0 - cmux_pid_csv="$(awk '{print $1}' "$cmux_pid_tty_map" | paste -sd, -)" - [ -n "$cmux_pid_csv" ] || exit 0 - lsof -nP -a -p "$cmux_pid_csv" -iTCP -sTCP:LISTEN -Fpn 2>/dev/null | awk -v map="$cmux_pid_tty_map" ' + ' > "$programa_pid_tty_map" + [ -s "$programa_pid_tty_map" ] || exit 0 + programa_pid_csv="$(awk '{print $1}' "$programa_pid_tty_map" | paste -sd, -)" + [ -n "$programa_pid_csv" ] || exit 0 + lsof -nP -a -p "$programa_pid_csv" -iTCP -sTCP:LISTEN -Fpn 2>/dev/null | awk -v map="$programa_pid_tty_map" ' BEGIN { while ((getline < map) > 0) { pid_to_tty[$1] = $2 @@ -5845,10 +5845,10 @@ final class WorkspaceRemoteSessionController { print tty "\\t" name } } - ' | while IFS=$'\\t' read -r cmux_tty cmux_port; do - [ -n "$cmux_tty" ] || continue - [ -n "$cmux_port" ] || continue - cmux_emit_port "$cmux_tty" "$cmux_port" + ' | while IFS=$'\\t' read -r programa_tty programa_port; do + [ -n "$programa_tty" ] || continue + [ -n "$programa_port" ] || continue + programa_emit_port "$programa_tty" "$programa_port" done fi """ @@ -5859,31 +5859,31 @@ final class WorkspaceRemoteSessionController { return """ set -eu - cmux_excluded_ports=" \(excludedPorts) " + programa_excluded_ports=" \(excludedPorts) " - cmux_emit_port() { - cmux_port="$1" - case "$cmux_excluded_ports" in - *" $cmux_port "*) return 0 ;; + programa_emit_port() { + programa_port="$1" + case "$programa_excluded_ports" in + *" $programa_port "*) return 0 ;; esac - [ "$cmux_port" -ge 1024 ] && [ "$cmux_port" -le 65535 ] || return 0 - printf '%s\\n' "$cmux_port" + [ "$programa_port" -ge 1024 ] && [ "$programa_port" -le 65535 ] || return 0 + printf '%s\\n' "$programa_port" } if command -v ss >/dev/null 2>&1; then - ss -ltnH 2>/dev/null | awk '{print $4}' | sed -E 's/.*:([0-9]+)$/\\1/' | awk '/^[0-9]+$/ {print $1}' | while IFS= read -r cmux_port; do - [ -n "$cmux_port" ] || continue - cmux_emit_port "$cmux_port" + ss -ltnH 2>/dev/null | awk '{print $4}' | sed -E 's/.*:([0-9]+)$/\\1/' | awk '/^[0-9]+$/ {print $1}' | while IFS= read -r programa_port; do + [ -n "$programa_port" ] || continue + programa_emit_port "$programa_port" done elif command -v netstat >/dev/null 2>&1; then - netstat -lnt 2>/dev/null | awk 'NR > 2 {print $4}' | sed -E 's/.*:([0-9]+)$/\\1/' | awk '/^[0-9]+$/ {print $1}' | while IFS= read -r cmux_port; do - [ -n "$cmux_port" ] || continue - cmux_emit_port "$cmux_port" + netstat -lnt 2>/dev/null | awk 'NR > 2 {print $4}' | sed -E 's/.*:([0-9]+)$/\\1/' | awk '/^[0-9]+$/ {print $1}' | while IFS= read -r programa_port; do + [ -n "$programa_port" ] || continue + programa_emit_port "$programa_port" done elif command -v lsof >/dev/null 2>&1; then - lsof -nP -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR > 1 {print $9}' | sed -E 's/.*:([0-9]+)$/\\1/' | awk '/^[0-9]+$/ {print $1}' | while IFS= read -r cmux_port; do - [ -n "$cmux_port" ] || continue - cmux_emit_port "$cmux_port" + lsof -nP -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR > 1 {print $9}' | sed -E 's/.*:([0-9]+)$/\\1/' | awk '/^[0-9]+$/ {print $1}' | while IFS= read -r programa_port; do + [ -n "$programa_port" ] || continue + programa_emit_port "$programa_port" done fi """ From cc361de7be14142763fefc959b5e389804d851b5 Mon Sep 17 00:00:00 2001 From: arzafran Date: Thu, 2 Jul 2026 11:43:39 -0300 Subject: [PATCH 21/23] fix: align CLI binary fallback, repo URLs, socket paths to programa (Phase 6f functional) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ${..._BIN:-cmux} tmux-shim fallbacks → :-programa (were invoking the old binary name) - manaflow-ai/cmux nightly/release workflow URLs → darkroomengineering/programa - /tmp/cmux* socket + hint paths → /tmp/programa* (fixes latent mismatch with the app's SocketControlSettings + reload.sh, which already use /tmp/programa*) - sentry tag + dispatch-queue labels cmux → programa Left cmux-relay-auth (JSON handshake protocol shared with the Go remote daemon — needs a coordinated both-sides change). Builds green. --- CLI/programa.swift | 58 +++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/CLI/programa.swift b/CLI/programa.swift index 5251eb2c0b5..e04b891e047 100644 --- a/CLI/programa.swift +++ b/CLI/programa.swift @@ -169,7 +169,7 @@ private final class CLISocketSentryTelemetry { let command = self.command _ = SentrySDK.capture(error: error) { scope in scope.setLevel(.error) - scope.setTag(value: "cmux-cli", key: "component") + scope.setTag(value: "programa-cli", key: "component") scope.setTag(value: command, key: "cli_command") scope.setTag(value: subcommand, key: "cli_subcommand") scope.setContext(value: context, key: "cli_socket") @@ -670,10 +670,10 @@ private enum CLISocketPathResolver { private static let appSupportDirectoryName = "programa" private static let stableSocketFileName = "programa.sock" private static let lastSocketPathFileName = "last-socket-path" - static let legacyDefaultSocketPath = "/tmp/cmux.sock" - private static let fallbackSocketPath = "/tmp/cmux-debug.sock" - private static let stagingSocketPath = "/tmp/cmux-staging.sock" - private static let legacyLastSocketPathFile = "/tmp/cmux-last-socket-path" + static let legacyDefaultSocketPath = "/tmp/programa.sock" + private static let fallbackSocketPath = "/tmp/programa-debug.sock" + private static let stagingSocketPath = "/tmp/programa-staging.sock" + private static let legacyLastSocketPathFile = "/tmp/programa-last-socket-path" static var defaultSocketPath: String { let stablePath: String? = stableSocketDirectoryURL()? @@ -715,8 +715,8 @@ private enum CLISocketPathResolver { if let tag = normalized(environment["PROGRAMA_TAG"]) { let slug = sanitizeTagSlug(tag) - candidates.append("/tmp/cmux-debug-\(slug).sock") - candidates.append("/tmp/cmux-\(slug).sock") + candidates.append("/tmp/programa-debug-\(slug).sock") + candidates.append("/tmp/programa-\(slug).sock") } candidates.append(requestedPath) @@ -1317,7 +1317,7 @@ final class SocketClient { throw CLIError(message: "cmux app did not start in time (socket not found at \(path))") } - let queue = DispatchQueue(label: "com.cmux.cli.socket-watch.\(UUID().uuidString)") + let queue = DispatchQueue(label: "com.programa.cli.socket-watch.\(UUID().uuidString)") let semaphore = DispatchSemaphore(value: 0) var connected = false let source = DispatchSource.makeFileSystemObjectSource( @@ -1368,7 +1368,7 @@ final class SocketClient { throw CLIError(message: "Timed out waiting for \(path)") } - let queue = DispatchQueue(label: "com.cmux.cli.path-watch.\(UUID().uuidString)") + let queue = DispatchQueue(label: "com.programa.cli.path-watch.\(UUID().uuidString)") let semaphore = DispatchSemaphore(value: 0) var found = false let source = DispatchSource.makeFileSystemObjectSource( @@ -1566,7 +1566,7 @@ enum CLIProcessRunner { struct CMUXCLI { let args: [String] - private static let debugLastSocketHintPath = "/tmp/cmux-last-socket-path" + private static let debugLastSocketHintPath = "/tmp/programa-last-socket-path" private static func normalizedEnvValue(_ value: String?) -> String? { guard let trimmed = value?.trimmingCharacters(in: .whitespacesAndNewlines), @@ -1588,7 +1588,7 @@ struct CMUXCLI { return nil } guard let hinted = normalizedEnvValue(raw), - hinted.hasPrefix("/tmp/cmux-debug"), + hinted.hasPrefix("/tmp/programa-debug"), hinted.hasSuffix(".sock"), pathIsSocket(hinted) else { return nil @@ -1607,9 +1607,9 @@ struct CMUXCLI { if let hinted = debugSocketPathFromHintFile() { return hinted } - return "/tmp/cmux-debug.sock" + return "/tmp/programa-debug.sock" #else - return "/tmp/cmux.sock" + return "/tmp/programa.sock" #endif } @@ -4995,13 +4995,13 @@ struct CMUXCLI { let downloadURL = entry?.downloadURL ?? "unknown" let checksumsAssetName = manifest?.checksumsAssetName ?? "unknown" let checksumsURL = manifest?.checksumsURL ?? "unknown" - let downloadCommand = "gh release download \(releaseTag) --repo manaflow-ai/cmux --pattern \(assetName)" - let downloadChecksumsCommand = "gh release download \(releaseTag) --repo manaflow-ai/cmux --pattern \(checksumsAssetName)" + let downloadCommand = "gh release download \(releaseTag) --repo darkroomengineering/programa --pattern \(assetName)" + let downloadChecksumsCommand = "gh release download \(releaseTag) --repo darkroomengineering/programa --pattern \(checksumsAssetName)" let checksumVerifyCommand = "shasum -a 256 -c \(checksumsAssetName) --ignore-missing" let signerWorkflow = releaseTag == "nightly" - ? "manaflow-ai/cmux/.github/workflows/nightly.yml" - : "manaflow-ai/cmux/.github/workflows/release.yml" - let verifyCommand = "gh attestation verify ./\(assetName) --repo manaflow-ai/cmux --signer-workflow \(signerWorkflow)" + ? "darkroomengineering/programa/.github/workflows/nightly.yml" + : "darkroomengineering/programa/.github/workflows/release.yml" + let verifyCommand = "gh attestation verify ./\(assetName) --repo darkroomengineering/programa --signer-workflow \(signerWorkflow)" let payload: [String: Any] = [ "app_version": remoteDaemonVersionString(from: info), @@ -5211,9 +5211,9 @@ struct CMUXCLI { private func defaultSSHControlPathTemplate(remoteRelayPort: Int? = nil) -> String { if let remoteRelayPort, remoteRelayPort > 0 { - return "/tmp/cmux-ssh-\(getuid())-\(remoteRelayPort)-%C" + return "/tmp/programa-ssh-\(getuid())-\(remoteRelayPort)-%C" } - return "/tmp/cmux-ssh-\(getuid())-%C" + return "/tmp/programa-ssh-\(getuid())-%C" } private func normalizedSSHIdentityPath(_ rawPath: String?) -> String? { @@ -5265,7 +5265,7 @@ struct CMUXCLI { if let trimmedExplicit, !trimmedExplicit.isEmpty { return trimmedExplicit } - guard let marker = try? String(contentsOfFile: "/tmp/cmux-last-debug-log-path", encoding: .utf8) else { + guard let marker = try? String(contentsOfFile: "/tmp/programa-last-debug-log-path", encoding: .utf8) else { return nil } let trimmedMarker = marker.trimmingCharacters(in: .whitespacesAndNewlines) @@ -5273,7 +5273,7 @@ struct CMUXCLI { }() guard let path else { return } let timestamp = ISO8601DateFormatter().string(from: Date()) - let line = "\(timestamp) [cmux-cli] \(message())\n" + let line = "\(timestamp) [programa-cli] \(message())\n" guard let data = line.data(using: .utf8) else { return } if !FileManager.default.fileExists(atPath: path) { FileManager.default.createFile(atPath: path, contents: nil) @@ -10413,7 +10413,7 @@ struct CMUXCLI { let script = """ #!/usr/bin/env bash set -euo pipefail - exec "${PROGRAMA_CLAUDE_TEAMS_PROGRAMA_BIN:-cmux}" __tmux-compat "$@" + exec "${PROGRAMA_CLAUDE_TEAMS_PROGRAMA_BIN:-programa}" __tmux-compat "$@" """ return try createTmuxCompatShimDirectory( directoryName: "claude-teams-bin", @@ -10522,7 +10522,7 @@ struct CMUXCLI { case "${1:-}" in -V|-v) echo "tmux 3.4"; exit 0 ;; esac - exec "${PROGRAMA_OMO_PROGRAMA_BIN:-cmux}" __tmux-compat "$@" + exec "${PROGRAMA_OMO_PROGRAMA_BIN:-programa}" __tmux-compat "$@" """ let root = try createTmuxCompatShimDirectory( directoryName: "omo-bin", @@ -10543,7 +10543,7 @@ struct CMUXCLI { *) shift ;; esac done - exec "${PROGRAMA_OMO_PROGRAMA_BIN:-cmux}" notify --title "${TITLE:-OpenCode}" --body "${BODY:-}" + exec "${PROGRAMA_OMO_PROGRAMA_BIN:-programa}" notify --title "${TITLE:-OpenCode}" --body "${BODY:-}" """ try writeShimIfChanged(notifierScript, to: notifierURL) @@ -11043,7 +11043,7 @@ struct CMUXCLI { case "${1:-}" in -V|-v) echo "tmux 3.4"; exit 0 ;; esac - exec "${PROGRAMA_OMX_PROGRAMA_BIN:-cmux}" __tmux-compat "$@" + exec "${PROGRAMA_OMX_PROGRAMA_BIN:-programa}" __tmux-compat "$@" """ return try createTmuxCompatShimDirectory( directoryName: "omx-bin", @@ -11146,7 +11146,7 @@ struct CMUXCLI { case "${1:-}" in -V|-v) echo "tmux 3.4"; exit 0 ;; esac - exec "${PROGRAMA_OMC_PROGRAMA_BIN:-cmux}" __tmux-compat "$@" + exec "${PROGRAMA_OMC_PROGRAMA_BIN:-programa}" __tmux-compat "$@" """ return try createTmuxCompatShimDirectory( directoryName: "omc-bin", @@ -11948,7 +11948,7 @@ struct CMUXCLI { private func tmuxWaitForSignalURL(name: String) -> URL { let allowed = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "._-")) let sanitized = name.unicodeScalars.map { allowed.contains($0) ? Character($0) : "_" } - return URL(fileURLWithPath: "/tmp/cmux-wait-for-\(String(sanitized)).sig") + return URL(fileURLWithPath: "/tmp/programa-wait-for-\(String(sanitized)).sig") } private func runTmuxCompatCommand( @@ -14058,7 +14058,7 @@ struct CMUXCLI { print() print(" \(bold)Docs\(reset)\(subdued) https://cmux.com/docs\(reset)") print(" \(bold)Discord\(reset)\(subdued) https://discord.gg/xsgFEVrWCZ\(reset)") - print(" \(bold)GitHub\(reset)\(subdued) https://github.com/manaflow-ai/cmux (please leave a star ⭐)\(reset)") + print(" \(bold)GitHub\(reset)\(subdued) https://github.com/darkroomengineering/programa (please leave a star ⭐)\(reset)") print(" \(bold)Email\(reset)\(subdued) founders@manaflow.com\(reset)") print() print(" \(subdued)Run \(reset)\(bold)cmux --help\(reset)\(subdued) for all commands.\(reset)") From 1da37ee5467c1097e96945e19c0d3004246ae819 Mon Sep 17 00:00:00 2001 From: arzafran Date: Thu, 2 Jul 2026 12:17:10 -0300 Subject: [PATCH 22/23] =?UTF-8?q?refactor:=20rename=20remaining=20internal?= =?UTF-8?q?=20cmux=20identifiers=20=E2=86=92=20programa=20(Phase=206,=20co?= =?UTF-8?q?smetic)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ~90 cmuxXxx Swift identifiers + ~60 dotted cmux.* Notification/queue/error-domain literals across 30 Sources/CLI files → programa*. Verified with build-for-testing (app + programaTests + programaUITests compile). Deliberately preserved identifiers/strings hard-coded by the test targets and tests_v2 (e.g. SocketControlMode.cmuxOnly rawValue "cmuxonly", cmux*ForTesting helpers, "cmux.main"/"cmux.settings"/"cmux.about" test-asserted keys, cmux-ssh-startup-* asserted in test_ssh_remote_cli_metadata) — those need a coordinated app+test rename (follow-up). Also fixed the 'Bundled Programa CLI' error string. Analytics events + cmux-relay-auth untouched. --- CLI/programa.swift | 56 ++--- Sources/AppDelegate.swift | 218 +++++++++--------- Sources/BrowserWindowPortal.swift | 52 ++--- Sources/ContentView.swift | 58 ++--- Sources/Find/SurfaceSearchOverlay.swift | 4 +- Sources/GhosttyConfig.swift | 10 +- Sources/GhosttyTerminalView.swift | 136 +++++------ Sources/NotificationsPage.swift | 4 +- Sources/Panels/BrowserPanel.swift | 58 ++--- Sources/Panels/BrowserPanelView.swift | 80 +++---- .../Panels/BrowserPopupWindowController.swift | 2 +- Sources/Panels/MarkdownPanelView.swift | 8 +- Sources/Panels/Panel.swift | 6 +- Sources/Panels/ProgramaWebView.swift | 6 +- Sources/ProgramaApp.swift | 72 +++--- Sources/ProgramaDirectoryTrust.swift | 2 +- Sources/SocketControlSettings.swift | 2 +- Sources/TabManager.swift | 32 +-- Sources/TerminalController.swift | 12 +- Sources/TerminalImageTransfer.swift | 2 +- Sources/TerminalNotificationStore.swift | 2 +- Sources/TerminalSSHSessionDetector.swift | 6 +- Sources/TerminalWindowPortal.swift | 20 +- Sources/Update/UpdateController.swift | 4 +- Sources/Update/UpdateLogStore.swift | 8 +- Sources/Update/UpdateTestURLProtocol.swift | 2 +- Sources/Update/UpdateTitlebarAccessory.swift | 12 +- Sources/Update/UpdateViewModel.swift | 8 +- Sources/WindowToolbarController.swift | 4 +- Sources/Workspace.swift | 130 +++++------ 30 files changed, 508 insertions(+), 508 deletions(-) diff --git a/CLI/programa.swift b/CLI/programa.swift index e04b891e047..1d4ff43274d 100644 --- a/CLI/programa.swift +++ b/CLI/programa.swift @@ -148,7 +148,7 @@ private final class CLISocketSentryTelemetry { for (key, value) in data { payload[key] = value } - let crumb = Breadcrumb(level: .info, category: "cmux.cli") + let crumb = Breadcrumb(level: .info, category: "programa.cli") crumb.message = message crumb.data = payload SentrySDK.addBreadcrumb(crumb) @@ -8051,10 +8051,10 @@ struct CMUXCLI { return true } - private static let cmuxThemeOverrideBundleIdentifier = "com.darkroom.programa" - private static let cmuxThemesBlockStart = "# cmux themes start" - private static let cmuxThemesBlockEnd = "# cmux themes end" - private static let cmuxThemesReloadNotificationName = "com.darkroom.programa.themes.reload-config" + private static let programaThemeOverrideBundleIdentifier = "com.darkroom.programa" + private static let programaThemesBlockStart = "# cmux themes start" + private static let programaThemesBlockEnd = "# cmux themes end" + private static let programaThemesReloadNotificationName = "com.darkroom.programa.themes.reload-config" private struct ThemeSelection { let rawValue: String? @@ -8086,8 +8086,8 @@ struct CMUXCLI { let selection = currentThemeSelection() var environment = ProcessInfo.processInfo.environment - environment["PROGRAMA_THEME_PICKER_CONFIG"] = try cmuxThemeOverrideConfigURL().path - environment["PROGRAMA_THEME_PICKER_BUNDLE_ID"] = currentProgramaAppBundleIdentifier() ?? Self.cmuxThemeOverrideBundleIdentifier + environment["PROGRAMA_THEME_PICKER_CONFIG"] = try programaThemeOverrideConfigURL().path + environment["PROGRAMA_THEME_PICKER_BUNDLE_ID"] = currentProgramaAppBundleIdentifier() ?? Self.programaThemeOverrideBundleIdentifier environment["PROGRAMA_THEME_PICKER_TARGET"] = defaultThemePickerTargetMode(current: selection).rawValue environment["PROGRAMA_THEME_PICKER_COLOR_SCHEME"] = defaultAppearancePrefersDarkThemes() ? "dark" : "light" if let light = selection.light { @@ -8264,7 +8264,7 @@ struct CMUXCLI { private func printThemesList(jsonOutput: Bool) throws { let themes = availableThemeNames() let current = currentThemeSelection() - let configPath = try cmuxThemeOverrideConfigURL().path + let configPath = try programaThemeOverrideConfigURL().path if jsonOutput { let currentPayload: [String: Any] = [ @@ -8597,8 +8597,8 @@ struct CMUXCLI { "~/.config/ghostty/config.ghostty", "~/Library/Application Support/com.mitchellh.ghostty/config", "~/Library/Application Support/com.mitchellh.ghostty/config.ghostty", - "~/Library/Application Support/\(Self.cmuxThemeOverrideBundleIdentifier)/config", - "~/Library/Application Support/\(Self.cmuxThemeOverrideBundleIdentifier)/config.ghostty", + "~/Library/Application Support/\(Self.programaThemeOverrideBundleIdentifier)/config", + "~/Library/Application Support/\(Self.programaThemeOverrideBundleIdentifier)/config.ghostty", ] return rawPaths.map { @@ -8630,18 +8630,18 @@ struct CMUXCLI { return lastValue } - private func cmuxThemeOverrideConfigURL() throws -> URL { + private func programaThemeOverrideConfigURL() throws -> URL { guard let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { throw CLIError(message: "Unable to resolve Application Support directory") } return appSupport - .appendingPathComponent(Self.cmuxThemeOverrideBundleIdentifier, isDirectory: true) + .appendingPathComponent(Self.programaThemeOverrideBundleIdentifier, isDirectory: true) .appendingPathComponent("config.ghostty", isDirectory: false) } private func writeManagedThemeOverride(rawThemeValue: String) throws -> URL { let fileManager = FileManager.default - let configURL = try cmuxThemeOverrideConfigURL() + let configURL = try programaThemeOverrideConfigURL() let directoryURL = configURL.deletingLastPathComponent() try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) @@ -8649,9 +8649,9 @@ struct CMUXCLI { let strippedContents = removingManagedThemeOverride(from: existingContents) .trimmingCharacters(in: .whitespacesAndNewlines) let block = """ - \(Self.cmuxThemesBlockStart) + \(Self.programaThemesBlockStart) theme = \(rawThemeValue) - \(Self.cmuxThemesBlockEnd) + \(Self.programaThemesBlockEnd) """ let nextContents = strippedContents.isEmpty ? "\(block)\n" : "\(strippedContents)\n\n\(block)\n" @@ -8661,7 +8661,7 @@ struct CMUXCLI { private func clearManagedThemeOverride() throws -> URL { let fileManager = FileManager.default - let configURL = try cmuxThemeOverrideConfigURL() + let configURL = try programaThemeOverrideConfigURL() guard let existingContents = try readOptionalThemeOverrideContents(at: configURL) else { return configURL } @@ -8717,9 +8717,9 @@ struct CMUXCLI { } private func reloadThemesIfPossible() -> ThemeReloadStatus { - let bundleIdentifier = currentProgramaAppBundleIdentifier() ?? Self.cmuxThemeOverrideBundleIdentifier + let bundleIdentifier = currentProgramaAppBundleIdentifier() ?? Self.programaThemeOverrideBundleIdentifier DistributedNotificationCenter.default().post( - name: Notification.Name(Self.cmuxThemesReloadNotificationName), + name: Notification.Name(Self.programaThemesReloadNotificationName), object: nil, userInfo: ["bundleIdentifier": bundleIdentifier] ) @@ -10297,7 +10297,7 @@ struct CMUXCLI { explicitPassword: String?, focusedContext: TmuxCompatFocusedContext?, tmuxPathPrefix: String, - cmuxBinEnvVar: String, + programaBinEnvVar: String, termOverrideEnvVar: String, extraEnvVars: [(key: String, value: String)] = [] ) { @@ -10317,7 +10317,7 @@ struct CMUXCLI { ?? "%1" let fakeTerm = processEnvironment[termOverrideEnvVar] ?? "screen-256color" - setenv(cmuxBinEnvVar, executablePath, 1) + setenv(programaBinEnvVar, executablePath, 1) setenv("PATH", updatedPath, 1) setenv("TMUX", fakeTmuxValue, 1) setenv("TMUX_PANE", fakeTmuxPane, 1) @@ -10367,7 +10367,7 @@ struct CMUXCLI { explicitPassword: explicitPassword, focusedContext: focusedContext, tmuxPathPrefix: "cmux-claude-teams", - cmuxBinEnvVar: "PROGRAMA_CLAUDE_TEAMS_PROGRAMA_BIN", + programaBinEnvVar: "PROGRAMA_CLAUDE_TEAMS_PROGRAMA_BIN", termOverrideEnvVar: "PROGRAMA_CLAUDE_TEAMS_TERM", extraEnvVars: [ (key: "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS", value: "1"), @@ -10945,7 +10945,7 @@ struct CMUXCLI { explicitPassword: explicitPassword, focusedContext: focusedContext, tmuxPathPrefix: "cmux-omo", - cmuxBinEnvVar: "PROGRAMA_OMO_PROGRAMA_BIN", + programaBinEnvVar: "PROGRAMA_OMO_PROGRAMA_BIN", termOverrideEnvVar: "PROGRAMA_OMO_TERM", extraEnvVars: [(key: "OPENCODE_PORT", value: openCodePort)] ) @@ -11067,7 +11067,7 @@ struct CMUXCLI { explicitPassword: explicitPassword, focusedContext: focusedContext, tmuxPathPrefix: "cmux-omx", - cmuxBinEnvVar: "PROGRAMA_OMX_PROGRAMA_BIN", + programaBinEnvVar: "PROGRAMA_OMX_PROGRAMA_BIN", termOverrideEnvVar: "PROGRAMA_OMX_TERM" ) } @@ -11170,7 +11170,7 @@ struct CMUXCLI { explicitPassword: explicitPassword, focusedContext: focusedContext, tmuxPathPrefix: "cmux-omc", - cmuxBinEnvVar: "PROGRAMA_OMC_PROGRAMA_BIN", + programaBinEnvVar: "PROGRAMA_OMC_PROGRAMA_BIN", termOverrideEnvVar: "PROGRAMA_OMC_TERM" ) // omc wraps Claude Code, so it needs the same NODE_OPTIONS restore module @@ -13406,9 +13406,9 @@ struct CMUXCLI { } var hooks = existing["hooks"] as? [String: Any] ?? [:] - let cmuxHooks = Self.codexHooksJSON["hooks"] as! [String: Any] - for (eventName, cmuxGroups) in cmuxHooks { - guard let cmuxGroupArray = cmuxGroups as? [[String: Any]] else { continue } + let programaHooks = Self.codexHooksJSON["hooks"] as! [String: Any] + for (eventName, programaGroups) in programaHooks { + guard let programaGroupArray = programaGroups as? [[String: Any]] else { continue } var eventGroups = hooks[eventName] as? [[String: Any]] ?? [] eventGroups.removeAll { group in guard let groupHooks = group["hooks"] as? [[String: Any]] else { return false } @@ -13416,7 +13416,7 @@ struct CMUXCLI { (hook["command"] as? String)?.contains(Self.codexHookCommandMarker) == true } } - eventGroups.append(contentsOf: cmuxGroupArray) + eventGroups.append(contentsOf: programaGroupArray) hooks[eventName] = eventGroups } existing["hooks"] = hooks diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 4fb15b306f8..aacdb16cf4a 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -768,8 +768,8 @@ final class VSCodeServeWebController { static let shared = VSCodeServeWebController() private static let serveWebStartupTimeoutSeconds: TimeInterval = 60 - private let queue = DispatchQueue(label: "cmux.vscode.serveWeb") - private let launchQueue = DispatchQueue(label: "cmux.vscode.serveWeb.launch") + private let queue = DispatchQueue(label: "programa.vscode.serveWeb") + private let launchQueue = DispatchQueue(label: "programa.vscode.serveWeb.launch") private let launchProcessOverride: ((URL, UInt64) -> (process: Process, url: URL)?)? private var serveWebProcess: Process? private var launchingProcess: Process? @@ -1340,7 +1340,7 @@ struct ProgramaCLIPathInstaller { var errorDescription: String? { switch self { case .bundledCLIMissing(let expectedPath): - return "Bundled cmux CLI was not found at \(expectedPath)." + return "Bundled Programa CLI was not found at \(expectedPath)." case .destinationParentNotDirectory(let path): return "Expected \(path) to be a directory." case .destinationIsDirectory(let path): @@ -1632,7 +1632,7 @@ struct ProgramaCLIPathInstaller { } private extension NSScreen { - var cmuxDisplayID: UInt32? { + var programaDisplayID: UInt32? { let key = NSDeviceDescriptionKey("NSScreenNumber") guard let value = deviceDescription[key] as? NSNumber else { return nil } return value.uint32Value @@ -1697,7 +1697,7 @@ func browserResponderHasMarkedText(_ responder: NSResponder?) -> Bool { // synchronous hasMarkedText() check above can return false even though an IME // composition just ended on the same Enter keystroke. Check the JS bridge's // composition timestamp to detect this race condition (#2626). - if let webView = responder.cmuxEnclosingProgramaWebView { + if let webView = responder.programaEnclosingProgramaWebView { if webView.webViewIsComposing { return true } let age = ProcessInfo.processInfo.systemUptime - webView.recentCompositionEndTimestamp if age >= 0 && age < 0.15 { return true } @@ -1708,7 +1708,7 @@ func browserResponderHasMarkedText(_ responder: NSResponder?) -> Bool { private extension NSResponder { /// Walk the responder chain to find the enclosing ProgramaWebView. - var cmuxEnclosingProgramaWebView: ProgramaWebView? { + var programaEnclosingProgramaWebView: ProgramaWebView? { var current: NSResponder? = self while let responder = current { if let webView = responder as? ProgramaWebView { return webView } @@ -2050,7 +2050,7 @@ private enum BrowserFindCommandEquivalent { } } -private func cmuxIsLikelyWebInspectorResponder(_ responder: NSResponder?) -> Bool { +private func programaIsLikelyWebInspectorResponder(_ responder: NSResponder?) -> Bool { guard let responder else { return false } let responderType = String(describing: type(of: responder)) if responderType.contains("WKInspector") { @@ -2125,7 +2125,7 @@ func shouldRouteBrowserFindCommandEquivalentThroughWebContentFirst( return false } - if cmuxIsLikelyWebInspectorResponder(responder) { + if programaIsLikelyWebInspectorResponder(responder) { return false } @@ -2155,7 +2155,7 @@ func cmuxOwningGhosttyView(for responder: NSResponder?) -> GhosttyNSView? { if let textView = responder as? NSTextView { if textView.isFieldEditor, - let ownerView = cmuxFieldEditorOwnerView(textView), + let ownerView = programaFieldEditorOwnerView(textView), let ghosttyView = cmuxOwningGhosttyView(for: ownerView) { return ghosttyView } @@ -2182,7 +2182,7 @@ func cmuxOwningGhosttyView(for responder: NSResponder?) -> GhosttyNSView? { return nil } -private func cmuxFieldEditorOwnerView(_ editor: NSTextView) -> NSView? { +private func programaFieldEditorOwnerView(_ editor: NSTextView) -> NSView? { guard editor.isFieldEditor else { return nil } var current = editor.nextResponder @@ -2362,9 +2362,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent } nonisolated static let persistedWindowGeometrySchemaVersion = 2 - private nonisolated static let persistedWindowGeometryDefaultsKey = "cmux.session.lastWindowGeometry.v2" + private nonisolated static let persistedWindowGeometryDefaultsKey = "programa.session.lastWindowGeometry.v2" private nonisolated static let legacyPersistedWindowGeometryDefaultsKeys = [ - "cmux.session.lastWindowGeometry.v1" + "programa.session.lastWindowGeometry.v1" ] weak var tabManager: TabManager? @@ -2826,7 +2826,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent let windows = NSApp.windows let ids = windows.map { $0.identifier?.rawValue ?? "" }.joined(separator: ",") let vis = windows.map { $0.isVisible ? "1" : "0" }.joined(separator: ",") - let screenIDs = windows.map { $0.screen?.cmuxDisplayID.map(String.init) ?? "" }.joined(separator: ",") + let screenIDs = windows.map { $0.screen?.programaDisplayID.map(String.init) ?? "" }.joined(separator: ",") let targetDisplayID = env["PROGRAMA_UI_TEST_TARGET_DISPLAY_ID"] ?? "" payload["stage"] = stage @@ -2839,8 +2839,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent payload["windowScreenDisplayIDs"] = screenIDs payload["uiTestTargetDisplayID"] = targetDisplayID if let rawDisplayID = UInt32(targetDisplayID) { - let screenPresent = NSScreen.screens.contains(where: { $0.cmuxDisplayID == rawDisplayID }) - let movedWindow = windows.contains(where: { $0.screen?.cmuxDisplayID == rawDisplayID }) + let screenPresent = NSScreen.screens.contains(where: { $0.programaDisplayID == rawDisplayID }) + let movedWindow = windows.contains(where: { $0.screen?.programaDisplayID == rawDisplayID }) payload["targetDisplayPresent"] = screenPresent ? "1" : "0" payload["targetDisplayMoveSucceeded"] = movedWindow ? "1" : "0" } @@ -2971,7 +2971,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent return } - guard let screen = NSScreen.screens.first(where: { $0.cmuxDisplayID == targetDisplayID }) else { + guard let screen = NSScreen.screens.first(where: { $0.programaDisplayID == targetDisplayID }) else { if attempt < 20 { DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { [weak self] in self?.moveUITestWindowToTargetDisplayIfNeeded(attempt: attempt + 1) @@ -3004,7 +3004,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent window.setFrame(frame, display: true, animate: false) window.makeKeyAndOrderFront(nil) window.orderFrontRegardless() - if window.screen?.cmuxDisplayID != targetDisplayID, attempt < 20 { + if window.screen?.programaDisplayID != targetDisplayID, attempt < 20 { DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { [weak self] in self?.moveUITestWindowToTargetDisplayIfNeeded(attempt: attempt + 1) } @@ -3938,14 +3938,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent ) { let available = NSScreen.screens.map { screen in SessionDisplayGeometry( - displayID: screen.cmuxDisplayID, + displayID: screen.programaDisplayID, frame: screen.frame, visibleFrame: screen.visibleFrame ) } let fallback = (NSScreen.main ?? NSScreen.screens.first).map { screen in SessionDisplayGeometry( - displayID: screen.cmuxDisplayID, + displayID: screen.programaDisplayID, frame: screen.frame, visibleFrame: screen.visibleFrame ) @@ -4396,7 +4396,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent guard let screen else { return nil } return SessionDisplaySnapshot( - displayID: screen.cmuxDisplayID, + displayID: screen.programaDisplayID, frame: SessionRectSnapshot(screen.frame), visibleFrame: SessionRectSnapshot(screen.visibleFrame) ) @@ -5790,7 +5790,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent guard let responder else { return nil } if let editor = responder as? NSTextView, editor.isFieldEditor { - return cmuxFieldEditorOwnerView(editor) ?? editor + return programaFieldEditorOwnerView(editor) ?? editor } return responder as? NSView } @@ -7102,7 +7102,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent if shortcutEventHasAddressableWindow(event) { if let eventWindow = resolvedShortcutEventWindow(event), - cmuxWindowShouldOwnCloseShortcut(eventWindow) { + programaWindowShouldOwnCloseShortcut(eventWindow) { // Auxiliary cmux windows do not own a terminal tab manager. Let them fall back // to the active main terminal window so app shortcuts like Cmd+W still route. } else { @@ -7194,16 +7194,16 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent ) let notificationStore = TerminalNotificationStore.shared - let cmuxConfigStore = ProgramaConfigStore() - cmuxConfigStore.wireDirectoryTracking(tabManager: tabManager) - cmuxConfigStore.loadAll() + let programaConfigStore = ProgramaConfigStore() + programaConfigStore.wireDirectoryTracking(tabManager: tabManager) + programaConfigStore.loadAll() let root = ContentView(updateViewModel: updateViewModel, windowId: windowId) .environmentObject(tabManager) .environmentObject(notificationStore) .environmentObject(sidebarState) .environmentObject(sidebarSelectionState) - .environmentObject(cmuxConfigStore) + .environmentObject(programaConfigStore) // Use the current key window's size for new windows so Cmd+Shift+N // creates a window matching the previous one's dimensions. @@ -10329,13 +10329,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent #if DEBUG static func setWindowFirstResponderGuardTesting(currentEvent: NSEvent?, hitView: NSView?) { - cmuxFirstResponderGuardCurrentEventOverride = currentEvent - cmuxFirstResponderGuardHitViewOverride = hitView + programaFirstResponderGuardCurrentEventOverride = currentEvent + programaFirstResponderGuardHitViewOverride = hitView } static func clearWindowFirstResponderGuardTesting() { - cmuxFirstResponderGuardCurrentEventOverride = nil - cmuxFirstResponderGuardHitViewOverride = nil + programaFirstResponderGuardCurrentEventOverride = nil + programaFirstResponderGuardHitViewOverride = nil } #endif @@ -11278,14 +11278,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent // event through the global shortcut handler first. if let targetWindow = [targetWindow, NSApp.keyWindow] .compactMap({ $0 }) - .first(where: { $0.identifier?.rawValue == "cmux.browser-popup" }) { + .first(where: { $0.identifier?.rawValue == "programa.browser-popup" }) { #if DEBUG dlog("shortcut.cmdW route=browserPopup") #endif targetWindow.performClose(nil) return true } else if let targetWindow, - cmuxWindowShouldOwnCloseShortcut(targetWindow) { + programaWindowShouldOwnCloseShortcut(targetWindow) { targetWindow.performClose(nil) } else { if let routedManager { @@ -12032,7 +12032,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent } private func isLikelyWebInspectorResponder(_ responder: NSResponder?) -> Bool { - cmuxIsLikelyWebInspectorResponder(responder) + programaIsLikelyWebInspectorResponder(responder) } #if DEBUG @@ -14150,27 +14150,27 @@ enum MenuBarIconRenderer { #if DEBUG -private var cmuxFirstResponderGuardCurrentEventOverride: NSEvent? -private var cmuxFirstResponderGuardHitViewOverride: NSView? +private var programaFirstResponderGuardCurrentEventOverride: NSEvent? +private var programaFirstResponderGuardHitViewOverride: NSView? #endif -private var cmuxFirstResponderGuardCurrentEventContext: NSEvent? -private var cmuxFirstResponderGuardHitViewContext: NSView? -private var cmuxFirstResponderGuardContextWindowNumber: Int? -private var cmuxBrowserReturnForwardingDepth = 0 -private var cmuxWindowFirstResponderBypassDepth = 0 -private var cmuxFieldEditorOwningWebViewAssociationKey: UInt8 = 0 +private var programaFirstResponderGuardCurrentEventContext: NSEvent? +private var programaFirstResponderGuardHitViewContext: NSView? +private var programaFirstResponderGuardContextWindowNumber: Int? +private var programaBrowserReturnForwardingDepth = 0 +private var programaWindowFirstResponderBypassDepth = 0 +private var programaFieldEditorOwningWebViewAssociationKey: UInt8 = 0 @discardableResult func cmuxWithWindowFirstResponderBypass(_ body: () -> T) -> T { - cmuxWindowFirstResponderBypassDepth += 1 + programaWindowFirstResponderBypassDepth += 1 defer { - cmuxWindowFirstResponderBypassDepth = max(0, cmuxWindowFirstResponderBypassDepth - 1) + programaWindowFirstResponderBypassDepth = max(0, programaWindowFirstResponderBypassDepth - 1) } return body() } -func cmuxIsWindowFirstResponderBypassActive() -> Bool { - cmuxWindowFirstResponderBypassDepth > 0 +func programaIsWindowFirstResponderBypassActive() -> Bool { + programaWindowFirstResponderBypassDepth > 0 } private final class ProgramaFieldEditorOwningWebViewBox: NSObject { @@ -14221,7 +14221,7 @@ private extension AppDelegate { private extension NSWindow { @objc func programa_makeFirstResponder(_ responder: NSResponder?) -> Bool { - if cmuxIsWindowFirstResponderBypassActive() { + if programaIsWindowFirstResponderBypassActive() { #if DEBUG dlog( "focus.guard bypassFirstResponder responder=\(String(describing: responder.map { type(of: $0) })) " + @@ -14231,9 +14231,9 @@ private extension NSWindow { return false } - let currentEvent = Self.cmuxCurrentEvent(for: self) + let currentEvent = Self.programaCurrentEvent(for: self) let responderWebView = responder.flatMap { - Self.cmuxOwningWebView(for: $0, in: self, event: currentEvent) + Self.programaOwningWebView(for: $0, in: self, event: currentEvent) } var pointerInitiatedWebFocus = false @@ -14253,7 +14253,7 @@ private extension NSWindow { if let responder, let webView = responderWebView, !webView.allowsFirstResponderAcquisitionEffective { - let pointerInitiatedFocus = Self.cmuxShouldAllowPointerInitiatedWebViewFocus( + let pointerInitiatedFocus = Self.programaShouldAllowPointerInitiatedWebViewFocus( window: self, webView: webView, event: currentEvent @@ -14308,9 +14308,9 @@ private extension NSWindow { } if result { if let fieldEditor = responder as? NSTextView, fieldEditor.isFieldEditor { - Self.cmuxTrackFieldEditor(fieldEditor, owningWebView: responderWebView) + Self.programaTrackFieldEditor(fieldEditor, owningWebView: responderWebView) } else if let fieldEditor = self.firstResponder as? NSTextView, fieldEditor.isFieldEditor { - Self.cmuxTrackFieldEditor(fieldEditor, owningWebView: responderWebView) + Self.programaTrackFieldEditor(fieldEditor, owningWebView: responderWebView) } } return result @@ -14327,10 +14327,10 @@ private extension NSWindow { let typingTimingExtra: String? = { guard event.type == .keyDown else { return nil } let responderWebView = self.firstResponder.flatMap { - Self.cmuxOwningWebView(for: $0, in: self, event: event) + Self.programaOwningWebView(for: $0, in: self, event: event) } - let hitWebView = Self.cmuxHitViewForEventDispatch(in: self, event: event).flatMap { - Self.cmuxOwningWebView(for: $0) + let hitWebView = Self.programaHitViewForEventDispatch(in: self, event: event).flatMap { + Self.programaOwningWebView(for: $0) } let firstResponderType = self.firstResponder.map { String(describing: type(of: $0)) } ?? "nil" return "browser=\((responderWebView != nil || hitWebView != nil) ? 1 : 0) firstResponder=\(firstResponderType)" @@ -14371,12 +14371,12 @@ private extension NSWindow { } let contextSetupStart = event.type == .keyDown ? ProcessInfo.processInfo.systemUptime : 0 #endif - let previousContextEvent = cmuxFirstResponderGuardCurrentEventContext - let previousContextHitView = cmuxFirstResponderGuardHitViewContext - let previousContextWindowNumber = cmuxFirstResponderGuardContextWindowNumber - cmuxFirstResponderGuardCurrentEventContext = event - cmuxFirstResponderGuardHitViewContext = Self.cmuxHitViewForEventDispatch(in: self, event: event) - cmuxFirstResponderGuardContextWindowNumber = self.windowNumber + let previousContextEvent = programaFirstResponderGuardCurrentEventContext + let previousContextHitView = programaFirstResponderGuardHitViewContext + let previousContextWindowNumber = programaFirstResponderGuardContextWindowNumber + programaFirstResponderGuardCurrentEventContext = event + programaFirstResponderGuardHitViewContext = Self.programaHitViewForEventDispatch(in: self, event: event) + programaFirstResponderGuardContextWindowNumber = self.windowNumber #if DEBUG if event.type == .keyDown { contextSetupMs = (ProcessInfo.processInfo.systemUptime - contextSetupStart) * 1000.0 @@ -14396,9 +14396,9 @@ private extension NSWindow { let folderGuardStart = event.type == .keyDown ? ProcessInfo.processInfo.systemUptime : 0 #endif defer { - cmuxFirstResponderGuardCurrentEventContext = previousContextEvent - cmuxFirstResponderGuardHitViewContext = previousContextHitView - cmuxFirstResponderGuardContextWindowNumber = previousContextWindowNumber + programaFirstResponderGuardCurrentEventContext = previousContextEvent + programaFirstResponderGuardHitViewContext = previousContextHitView + programaFirstResponderGuardContextWindowNumber = previousContextWindowNumber } guard shouldSuppressWindowMoveForFolderDrag(window: self, event: event), @@ -14481,7 +14481,7 @@ private extension NSWindow { // remaining should be menu items. let firstResponderGhosttyView = cmuxOwningGhosttyView(for: self.firstResponder) let firstResponderWebView = self.firstResponder.flatMap { - Self.cmuxOwningWebView(for: $0, in: self, event: event) + Self.programaOwningWebView(for: $0, in: self, event: event) } let firstResponderHasMarkedText = browserResponderHasMarkedText(self.firstResponder) if let ghosttyView = firstResponderGhosttyView { @@ -14532,14 +14532,14 @@ private extension NSWindow { ) { // Forwarding keyDown can re-enter performKeyEquivalent in WebKit/AppKit internals. // On re-entry, fall back to normal dispatch to avoid an infinite loop. - if cmuxBrowserReturnForwardingDepth > 0 { + if programaBrowserReturnForwardingDepth > 0 { #if DEBUG dlog(" → browser Return/Enter reentry; using normal dispatch") #endif return false } - cmuxBrowserReturnForwardingDepth += 1 - defer { cmuxBrowserReturnForwardingDepth = max(0, cmuxBrowserReturnForwardingDepth - 1) } + programaBrowserReturnForwardingDepth += 1 + defer { programaBrowserReturnForwardingDepth = max(0, programaBrowserReturnForwardingDepth - 1) } #if DEBUG dlog(" → browser Return/Enter routed to firstResponder.keyDown") #endif @@ -14624,13 +14624,13 @@ private extension NSWindow { return parts.joined(separator: "+") } - private static func cmuxOwningWebView(for responder: NSResponder) -> ProgramaWebView? { + private static func programaOwningWebView(for responder: NSResponder) -> ProgramaWebView? { if let webView = responder as? ProgramaWebView { return webView } if let view = responder as? NSView, - let webView = cmuxOwningWebView(for: view) { + let webView = programaOwningWebView(for: view) { return webView } @@ -14642,7 +14642,7 @@ private extension NSWindow { return webView } if let view = next as? NSView, - let webView = cmuxOwningWebView(for: view) { + let webView = programaOwningWebView(for: view) { return webView } current = next.nextResponder @@ -14651,7 +14651,7 @@ private extension NSWindow { return nil } - private static func cmuxOwningWebView( + private static func programaOwningWebView( for responder: NSResponder, in window: NSWindow, event: NSEvent? @@ -14663,7 +14663,7 @@ private extension NSWindow { return nil } - if let webView = cmuxOwningWebView(for: responder) { + if let webView = programaOwningWebView(for: responder) { return webView } @@ -14672,15 +14672,15 @@ private extension NSWindow { } if let event, - let hitWebView = cmuxPointerHitWebView(in: window, event: event) { - cmuxTrackFieldEditor(textView, owningWebView: hitWebView) + let hitWebView = programaPointerHitWebView(in: window, event: event) { + programaTrackFieldEditor(textView, owningWebView: hitWebView) return hitWebView } - return cmuxTrackedOwningWebView(for: textView) + return programaTrackedOwningWebView(for: textView) } - private static func cmuxOwningWebView(for view: NSView) -> ProgramaWebView? { + private static func programaOwningWebView(for view: NSView) -> ProgramaWebView? { if let webView = view as? ProgramaWebView { return webView } @@ -14691,7 +14691,7 @@ private extension NSWindow { return webView } if String(describing: type(of: candidate)).contains("WindowBrowserSlotView"), - let portalWebView = cmuxUniqueBrowserWebView(in: candidate) { + let portalWebView = programaUniqueBrowserWebView(in: candidate) { // Portal-hosted browser chrome (for example the Cmd+F overlay) is a // sibling of the hosted WKWebView inside WindowBrowserSlotView, not a // descendant of it. Allow native text-entry controls in that slot to @@ -14701,7 +14701,7 @@ private extension NSWindow { if view === portalWebView || view.isDescendant(of: portalWebView) { return portalWebView } - if cmuxAllowsPortalSlotTextEntryFocus(view) { + if programaAllowsPortalSlotTextEntryFocus(view) { return nil } return portalWebView @@ -14712,7 +14712,7 @@ private extension NSWindow { return nil } - private static func cmuxAllowsPortalSlotTextEntryFocus(_ view: NSView) -> Bool { + private static func programaAllowsPortalSlotTextEntryFocus(_ view: NSView) -> Bool { var current: NSView? = view while let candidate = current { if let textField = candidate as? NSTextField { @@ -14726,7 +14726,7 @@ private extension NSWindow { return false } - private static func cmuxUniqueBrowserWebView(in root: NSView) -> ProgramaWebView? { + private static func programaUniqueBrowserWebView(in root: NSView) -> ProgramaWebView? { var stack: [NSView] = [root] var found: ProgramaWebView? while let current = stack.popLast() { @@ -14742,19 +14742,19 @@ private extension NSWindow { return found } - private static func cmuxCurrentEvent(for window: NSWindow) -> NSEvent? { + private static func programaCurrentEvent(for window: NSWindow) -> NSEvent? { #if DEBUG - if let override = cmuxFirstResponderGuardCurrentEventOverride { + if let override = programaFirstResponderGuardCurrentEventOverride { return override } #endif - if cmuxFirstResponderGuardContextWindowNumber == window.windowNumber { - return cmuxFirstResponderGuardCurrentEventContext + if programaFirstResponderGuardContextWindowNumber == window.windowNumber { + return programaFirstResponderGuardCurrentEventContext } return NSApp.currentEvent } - private static func cmuxHitViewInThemeFrame(in window: NSWindow, event: NSEvent) -> NSView? { + private static func programaHitViewInThemeFrame(in window: NSWindow, event: NSEvent) -> NSView? { guard let contentView = window.contentView, let themeFrame = contentView.superview else { return nil @@ -14763,7 +14763,7 @@ private extension NSWindow { return themeFrame.hitTest(pointInTheme) } - private static func cmuxHitViewInContentView(in window: NSWindow, event: NSEvent) -> NSView? { + private static func programaHitViewInContentView(in window: NSWindow, event: NSEvent) -> NSView? { guard let contentView = window.contentView else { return nil } @@ -14771,69 +14771,69 @@ private extension NSWindow { return contentView.hitTest(pointInContent) } - private static func cmuxTopHitViewForEvent(in window: NSWindow, event: NSEvent) -> NSView? { - if let hitInThemeFrame = cmuxHitViewInThemeFrame(in: window, event: event) { + private static func programaTopHitViewForEvent(in window: NSWindow, event: NSEvent) -> NSView? { + if let hitInThemeFrame = programaHitViewInThemeFrame(in: window, event: event) { return hitInThemeFrame } - return cmuxHitViewInContentView(in: window, event: event) + return programaHitViewInContentView(in: window, event: event) } - private static func cmuxHitViewForEventDispatch(in window: NSWindow, event: NSEvent) -> NSView? { + private static func programaHitViewForEventDispatch(in window: NSWindow, event: NSEvent) -> NSView? { if event.windowNumber != 0, event.windowNumber != window.windowNumber { return nil } if let eventWindow = event.window, eventWindow !== window { return nil } - return cmuxTopHitViewForEvent(in: window, event: event) + return programaTopHitViewForEvent(in: window, event: event) } - private static func cmuxHitViewForCurrentEvent(in window: NSWindow, event: NSEvent) -> NSView? { + private static func programaHitViewForCurrentEvent(in window: NSWindow, event: NSEvent) -> NSView? { #if DEBUG - if let override = cmuxFirstResponderGuardHitViewOverride { + if let override = programaFirstResponderGuardHitViewOverride { return override } #endif - if cmuxFirstResponderGuardContextWindowNumber == window.windowNumber, - let contextHitView = cmuxFirstResponderGuardHitViewContext { + if programaFirstResponderGuardContextWindowNumber == window.windowNumber, + let contextHitView = programaFirstResponderGuardHitViewContext { return contextHitView } - return cmuxTopHitViewForEvent(in: window, event: event) + return programaTopHitViewForEvent(in: window, event: event) } - private static func cmuxTrackFieldEditor(_ fieldEditor: NSTextView, owningWebView webView: ProgramaWebView?) { + private static func programaTrackFieldEditor(_ fieldEditor: NSTextView, owningWebView webView: ProgramaWebView?) { if let webView { objc_setAssociatedObject( fieldEditor, - &cmuxFieldEditorOwningWebViewAssociationKey, + &programaFieldEditorOwningWebViewAssociationKey, ProgramaFieldEditorOwningWebViewBox(webView: webView), .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) } else { objc_setAssociatedObject( fieldEditor, - &cmuxFieldEditorOwningWebViewAssociationKey, + &programaFieldEditorOwningWebViewAssociationKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) } } - private static func cmuxTrackedOwningWebView(for fieldEditor: NSTextView) -> ProgramaWebView? { + private static func programaTrackedOwningWebView(for fieldEditor: NSTextView) -> ProgramaWebView? { guard let box = objc_getAssociatedObject( fieldEditor, - &cmuxFieldEditorOwningWebViewAssociationKey + &programaFieldEditorOwningWebViewAssociationKey ) as? ProgramaFieldEditorOwningWebViewBox else { return nil } guard let webView = box.webView else { - cmuxTrackFieldEditor(fieldEditor, owningWebView: nil) + programaTrackFieldEditor(fieldEditor, owningWebView: nil) return nil } return webView } - private static func cmuxIsPointerDownEvent(_ event: NSEvent) -> Bool { + private static func programaIsPointerDownEvent(_ event: NSEvent) -> Bool { switch event.type { case .leftMouseDown, .rightMouseDown, .otherMouseDown: return true @@ -14842,8 +14842,8 @@ private extension NSWindow { } } - private static func cmuxPointerHitWebView(in window: NSWindow, event: NSEvent) -> ProgramaWebView? { - guard cmuxIsPointerDownEvent(event) else { return nil } + private static func programaPointerHitWebView(in window: NSWindow, event: NSEvent) -> ProgramaWebView? { + guard programaIsPointerDownEvent(event) else { return nil } if event.windowNumber != 0, event.windowNumber != window.windowNumber { return nil } @@ -14856,19 +14856,19 @@ private extension NSWindow { ) as? ProgramaWebView { return portalWebView } - guard let hitView = cmuxHitViewForCurrentEvent(in: window, event: event) else { + guard let hitView = programaHitViewForCurrentEvent(in: window, event: event) else { return nil } - return cmuxOwningWebView(for: hitView) + return programaOwningWebView(for: hitView) } - private static func cmuxShouldAllowPointerInitiatedWebViewFocus( + private static func programaShouldAllowPointerInitiatedWebViewFocus( window: NSWindow, webView: ProgramaWebView, event: NSEvent? ) -> Bool { guard let event, - let hitWebView = cmuxPointerHitWebView(in: window, event: event) else { + let hitWebView = programaPointerHitWebView(in: window, event: event) else { return false } return hitWebView === webView diff --git a/Sources/BrowserWindowPortal.swift b/Sources/BrowserWindowPortal.swift index ad3c378af44..2b2e6d61c76 100644 --- a/Sources/BrowserWindowPortal.swift +++ b/Sources/BrowserWindowPortal.swift @@ -4,11 +4,11 @@ import ObjectiveC import SwiftUI import WebKit -private var cmuxWindowBrowserPortalKey: UInt8 = 0 -private var cmuxWindowBrowserPortalCloseObserverKey: UInt8 = 0 -private var cmuxBrowserSearchOverlayPanelIdAssociationKey: UInt8 = 0 -private var cmuxBrowserPortalNeedsRenderingStateReattachKey: UInt8 = 0 -private var cmuxWindowInteractiveSplitDividerDragKey: UInt8 = 0 +private var programaWindowBrowserPortalKey: UInt8 = 0 +private var programaWindowBrowserPortalCloseObserverKey: UInt8 = 0 +private var programaBrowserSearchOverlayPanelIdAssociationKey: UInt8 = 0 +private var programaBrowserPortalNeedsRenderingStateReattachKey: UInt8 = 0 +private var programaWindowInteractiveSplitDividerDragKey: UInt8 = 0 #if DEBUG private func browserPortalDebugToken(_ view: NSView?) -> String { @@ -49,13 +49,13 @@ private extension NSWindow { var browserPortalHasInteractiveSplitDividerDrag: Bool { get { let isActive = - (objc_getAssociatedObject(self, &cmuxWindowInteractiveSplitDividerDragKey) as? NSNumber)? + (objc_getAssociatedObject(self, &programaWindowInteractiveSplitDividerDragKey) as? NSNumber)? .boolValue ?? false guard isActive else { return false } guard (NSEvent.pressedMouseButtons & 1) != 0 else { objc_setAssociatedObject( self, - &cmuxWindowInteractiveSplitDividerDragKey, + &programaWindowInteractiveSplitDividerDragKey, NSNumber(value: false), .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) @@ -66,7 +66,7 @@ private extension NSWindow { set { objc_setAssociatedObject( self, - &cmuxWindowInteractiveSplitDividerDragKey, + &programaWindowInteractiveSplitDividerDragKey, NSNumber(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) @@ -77,13 +77,13 @@ private extension NSWindow { private extension WKWebView { private var browserPortalNeedsRenderingStateReattach: Bool { get { - (objc_getAssociatedObject(self, &cmuxBrowserPortalNeedsRenderingStateReattachKey) as? NSNumber)? + (objc_getAssociatedObject(self, &programaBrowserPortalNeedsRenderingStateReattachKey) as? NSNumber)? .boolValue ?? false } set { objc_setAssociatedObject( self, - &cmuxBrowserPortalNeedsRenderingStateReattachKey, + &programaBrowserPortalNeedsRenderingStateReattachKey, NSNumber(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) @@ -1647,8 +1647,8 @@ final class WindowBrowserSlotView: NSView { paneDropTargetView.slotView = self dropZoneOverlayView.wantsLayer = true - dropZoneOverlayView.layer?.backgroundColor = cmuxAccentNSColor().withAlphaComponent(0.25).cgColor - dropZoneOverlayView.layer?.borderColor = cmuxAccentNSColor().cgColor + dropZoneOverlayView.layer?.backgroundColor = programaAccentNSColor().withAlphaComponent(0.25).cgColor + dropZoneOverlayView.layer?.borderColor = programaAccentNSColor().cgColor dropZoneOverlayView.layer?.borderWidth = 2 dropZoneOverlayView.layer?.cornerRadius = 8 dropZoneOverlayView.isHidden = true @@ -1754,7 +1754,7 @@ final class WindowBrowserSlotView: NSView { if let overlay = searchOverlayHostingView { objc_setAssociatedObject( overlay, - &cmuxBrowserSearchOverlayPanelIdAssociationKey, + &programaBrowserSearchOverlayPanelIdAssociationKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) @@ -1781,7 +1781,7 @@ final class WindowBrowserSlotView: NSView { overlay.rootView = rootView objc_setAssociatedObject( overlay, - &cmuxBrowserSearchOverlayPanelIdAssociationKey, + &programaBrowserSearchOverlayPanelIdAssociationKey, configuration.panelId, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) @@ -1802,7 +1802,7 @@ final class WindowBrowserSlotView: NSView { overlay.translatesAutoresizingMaskIntoConstraints = false objc_setAssociatedObject( overlay, - &cmuxBrowserSearchOverlayPanelIdAssociationKey, + &programaBrowserSearchOverlayPanelIdAssociationKey, configuration.panelId, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) @@ -1836,7 +1836,7 @@ final class WindowBrowserSlotView: NSView { func searchOverlayPanelId(for responder: NSResponder) -> UUID? { guard let overlay = searchOverlayHostingView else { return nil } - let panelId = objc_getAssociatedObject(overlay, &cmuxBrowserSearchOverlayPanelIdAssociationKey) as? UUID + let panelId = objc_getAssociatedObject(overlay, &programaBrowserSearchOverlayPanelIdAssociationKey) as? UUID if let view = responder as? NSView, view === overlay || view.isDescendant(of: overlay) { @@ -2609,7 +2609,7 @@ final class WindowBrowserPortal: NSObject { append(directTransferChild(of: sourceSuperview, containing: primaryWebView) ?? primaryWebView) - if let inspectorFrontend = primaryWebView.cmuxInspectorFrontendWebView() { + if let inspectorFrontend = primaryWebView.programaInspectorFrontendWebView() { append(directTransferChild(of: sourceSuperview, containing: inspectorFrontend) ?? inspectorFrontend) } @@ -3114,7 +3114,7 @@ final class WindowBrowserPortal: NSObject { let anchorId = ObjectIdentifier(anchorView) let previousEntry = entriesByWebViewId[webViewId] let shouldPreserveExternalFullscreenHost = - webView.cmuxIsManagedByExternalFullscreenWindow(relativeTo: window) + webView.programaIsManagedByExternalFullscreenWindow(relativeTo: window) let containerView = ensureContainerView( for: previousEntry ?? Entry( webView: nil, @@ -3472,7 +3472,7 @@ final class WindowBrowserPortal: NSObject { refreshReasons.append("syncAttachContainer") } let shouldPreserveExternalFullscreenHost = - webView.cmuxIsManagedByExternalFullscreenWindow(relativeTo: window) + webView.programaIsManagedByExternalFullscreenWindow(relativeTo: window) let shouldPreserveExternalHostForHiddenEntry = !shouldPreserveExternalFullscreenHost && !entry.visibleInUI && @@ -3969,7 +3969,7 @@ enum BrowserWindowPortalRegistry { } private static func installWindowCloseObserverIfNeeded(for window: NSWindow) { - guard objc_getAssociatedObject(window, &cmuxWindowBrowserPortalCloseObserverKey) == nil else { return } + guard objc_getAssociatedObject(window, &programaWindowBrowserPortalCloseObserverKey) == nil else { return } let windowId = ObjectIdentifier(window) let observer = NotificationCenter.default.addObserver( forName: NSWindow.willCloseNotification, @@ -3986,7 +3986,7 @@ enum BrowserWindowPortalRegistry { } objc_setAssociatedObject( window, - &cmuxWindowBrowserPortalCloseObserverKey, + &programaWindowBrowserPortalCloseObserverKey, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) @@ -4003,11 +4003,11 @@ enum BrowserWindowPortalRegistry { webViewToWindowId = webViewToWindowId.filter { $0.value != windowId } guard let window else { return } - if let observer = objc_getAssociatedObject(window, &cmuxWindowBrowserPortalCloseObserverKey) { + if let observer = objc_getAssociatedObject(window, &programaWindowBrowserPortalCloseObserverKey) { NotificationCenter.default.removeObserver(observer) } - objc_setAssociatedObject(window, &cmuxWindowBrowserPortalCloseObserverKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(window, &cmuxWindowBrowserPortalKey, nil, .OBJC_ASSOCIATION_RETAIN) + objc_setAssociatedObject(window, &programaWindowBrowserPortalCloseObserverKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(window, &programaWindowBrowserPortalKey, nil, .OBJC_ASSOCIATION_RETAIN) } private static func pruneWebViewMappings(for windowId: ObjectIdentifier, validWebViewIds: Set) { @@ -4017,14 +4017,14 @@ enum BrowserWindowPortalRegistry { } private static func portal(for window: NSWindow) -> WindowBrowserPortal { - if let existing = objc_getAssociatedObject(window, &cmuxWindowBrowserPortalKey) as? WindowBrowserPortal { + if let existing = objc_getAssociatedObject(window, &programaWindowBrowserPortalKey) as? WindowBrowserPortal { portalsByWindowId[ObjectIdentifier(window)] = existing installWindowCloseObserverIfNeeded(for: window) return existing } let portal = WindowBrowserPortal(window: window) - objc_setAssociatedObject(window, &cmuxWindowBrowserPortalKey, portal, .OBJC_ASSOCIATION_RETAIN) + objc_setAssociatedObject(window, &programaWindowBrowserPortalKey, portal, .OBJC_ASSOCIATION_RETAIN) portalsByWindowId[ObjectIdentifier(window)] = portal installWindowCloseObserverIfNeeded(for: window) return portal diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index b7308a35ab8..0c456ba7d6c 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -40,7 +40,7 @@ func sidebarActiveForegroundNSColor( return baseColor.withAlphaComponent(clampedOpacity) } -func cmuxAccentNSColor(for colorScheme: ColorScheme) -> NSColor { +func programaAccentNSColor(for colorScheme: ColorScheme) -> NSColor { switch colorScheme { case .dark: return NSColor( @@ -59,20 +59,20 @@ func cmuxAccentNSColor(for colorScheme: ColorScheme) -> NSColor { } } -func cmuxAccentNSColor(for appAppearance: NSAppearance?) -> NSColor { +func programaAccentNSColor(for appAppearance: NSAppearance?) -> NSColor { let bestMatch = appAppearance?.bestMatch(from: [.darkAqua, .aqua]) let scheme: ColorScheme = (bestMatch == .darkAqua) ? .dark : .light - return cmuxAccentNSColor(for: scheme) + return programaAccentNSColor(for: scheme) } -func cmuxAccentNSColor() -> NSColor { +func programaAccentNSColor() -> NSColor { NSColor(name: nil) { appearance in - cmuxAccentNSColor(for: appearance) + programaAccentNSColor(for: appearance) } } -func cmuxAccentColor() -> Color { - Color(nsColor: cmuxAccentNSColor()) +func programaAccentColor() -> Color { + Color(nsColor: programaAccentNSColor()) } struct SidebarRemoteErrorCopyEntry: Equatable { @@ -117,7 +117,7 @@ func sidebarSelectedWorkspaceBackgroundNSColor(for colorScheme: ColorScheme) -> let parsed = NSColor(hex: hex) { return parsed } - return cmuxAccentNSColor(for: colorScheme) + return programaAccentNSColor(for: colorScheme) } func sidebarSelectedWorkspaceForegroundNSColor(opacity: CGFloat) -> NSColor { @@ -956,8 +956,8 @@ final class FileDropOverlayView: NSView { var fileDropOverlayKey: UInt8 = 0 private var commandPaletteWindowOverlayKey: UInt8 = 0 private var tmuxWorkspacePaneWindowOverlayKey: UInt8 = 0 -let commandPaletteOverlayContainerIdentifier = NSUserInterfaceItemIdentifier("cmux.commandPalette.overlay.container") -let tmuxWorkspacePaneOverlayContainerIdentifier = NSUserInterfaceItemIdentifier("cmux.tmuxWorkspacePane.overlay.container") +let commandPaletteOverlayContainerIdentifier = NSUserInterfaceItemIdentifier("programa.commandPalette.overlay.container") +let tmuxWorkspacePaneOverlayContainerIdentifier = NSUserInterfaceItemIdentifier("programa.tmuxWorkspacePane.overlay.container") enum CommandPaletteOverlayPromotionPolicy { static func shouldPromote(previouslyVisible: Bool, isVisible: Bool) -> Bool { @@ -1798,7 +1798,7 @@ struct ContentView: View { @EnvironmentObject var notificationStore: TerminalNotificationStore @EnvironmentObject var sidebarState: SidebarState @EnvironmentObject var sidebarSelectionState: SidebarSelectionState - @EnvironmentObject var cmuxConfigStore: ProgramaConfigStore + @EnvironmentObject var programaConfigStore: ProgramaConfigStore @State private var sidebarWidth: CGFloat = 200 @State private var hoveredResizerHandles: Set = [] @State private var isResizerDragging = false @@ -3988,7 +3988,7 @@ struct ContentView: View { let isHovered = commandPaletteHoveredResultIndex == index let trailingLabel = commandPaletteTrailingLabel(for: result.command) let rowBackground: Color = isSelected - ? cmuxAccentColor().opacity(0.12) + ? programaAccentColor().opacity(0.12) : (isHovered ? Color.primary.opacity(0.08) : .clear) Button { @@ -5664,7 +5664,7 @@ struct ContentView: View { private func commandPaletteCommandsFingerprint(commandsContext: CommandPaletteCommandsContext) -> Int { var hasher = Hasher() hasher.combine(commandsContext.snapshot.fingerprint()) - hasher.combine(cmuxConfigStore.configRevision) + hasher.combine(programaConfigStore.configRevision) return hasher.finalize() } @@ -7073,13 +7073,13 @@ struct ContentView: View { ) ) - let cmuxConfigDefaultSubtitle = constant(String(localized: "command.cmuxConfig.subtitle", defaultValue: "programa.json")) - for command in cmuxConfigStore.loadedCommands { + let programaConfigDefaultSubtitle = constant(String(localized: "command.cmuxConfig.subtitle", defaultValue: "programa.json")) + for command in programaConfigStore.loadedCommands { let commandName = sanitizeProgramaConfigPaletteText(command.name) let subtitle = command.description .map { sanitizeProgramaConfigPaletteText($0) } .flatMap { $0.isEmpty ? nil : constant($0) } - ?? cmuxConfigDefaultSubtitle + ?? programaConfigDefaultSubtitle contributions.append( CommandPaletteCommandContribution( commandId: command.id, @@ -7453,10 +7453,10 @@ struct ContentView: View { } } - for command in cmuxConfigStore.loadedCommands { + for command in programaConfigStore.loadedCommands { let captured = command - let sourcePath = cmuxConfigStore.commandSourcePaths[command.id] - let globalPath = cmuxConfigStore.globalConfigPath + let sourcePath = programaConfigStore.commandSourcePaths[command.id] + let globalPath = programaConfigStore.globalConfigPath registry.register(commandId: command.id) { let rawCwd = tabManager.selectedWorkspace?.currentDirectory let baseCwd = (rawCwd?.isEmpty == false) ? rawCwd! @@ -10604,8 +10604,8 @@ private enum FeedbackComposerClient { } enum SidebarDragLifecycleNotification { - static let stateDidChange = Notification.Name("cmux.sidebarDragStateDidChange") - static let requestClear = Notification.Name("cmux.sidebarDragRequestClear") + static let stateDidChange = Notification.Name("programa.sidebarDragStateDidChange") + static let requestClear = Notification.Name("programa.sidebarDragRequestClear") static let tabIdKey = "tabId" static let reasonKey = "reason" @@ -12266,7 +12266,7 @@ private struct SidebarEmptyArea: View { .overlay(alignment: .top) { if shouldShowTopDropIndicator { Rectangle() - .fill(cmuxAccentColor()) + .fill(programaAccentColor()) .frame(height: 2) .padding(.horizontal, 8) .offset(y: -(rowSpacing / 2)) @@ -12552,7 +12552,7 @@ private struct TabItemView: View, Equatable { if let hex = sidebarNotificationBadgeColorHex, let nsColor = NSColor(hex: hex) { return Color(nsColor: nsColor) } - return usesInvertedActiveForeground ? Color.white.opacity(0.25) : cmuxAccentColor() + return usesInvertedActiveForeground ? Color.white.opacity(0.25) : programaAccentColor() } private var activeProgressTrackColor: Color { @@ -12560,7 +12560,7 @@ private struct TabItemView: View, Equatable { } private var activeProgressFillColor: Color { - usesInvertedActiveForeground ? Color.white.opacity(0.8) : cmuxAccentColor() + usesInvertedActiveForeground ? Color.white.opacity(0.8) : programaAccentColor() } private var shortcutHintEmphasis: Double { @@ -13013,7 +13013,7 @@ private struct TabItemView: View, Equatable { .overlay(alignment: .top) { if showsCenteredTopDropIndicator { Rectangle() - .fill(cmuxAccentColor()) + .fill(programaAccentColor()) .frame(height: 2) .padding(.horizontal, 8) .offset(y: index == 0 ? 0 : -(rowSpacing / 2)) @@ -13359,14 +13359,14 @@ private struct TabItemView: View, Equatable { if let hex = sidebarSelectionColorHex, let parsed = NSColor(hex: hex) { return parsed } - return cmuxAccentNSColor(for: colorScheme) + return programaAccentNSColor(for: colorScheme) } private var backgroundColor: Color { switch activeTabIndicatorStyle { case .leftRail: if isActive { return Color(nsColor: selectionBackgroundColor) } - if isMultiSelected { return cmuxAccentColor().opacity(0.25) } + if isMultiSelected { return programaAccentColor().opacity(0.25) } return Color.clear case .solidFill: if isActive { return Color(nsColor: selectionBackgroundColor) } @@ -13374,7 +13374,7 @@ private struct TabItemView: View, Equatable { if isMultiSelected { return custom.opacity(0.35) } return custom.opacity(0.7) } - if isMultiSelected { return cmuxAccentColor().opacity(0.25) } + if isMultiSelected { return programaAccentColor().opacity(0.25) } return Color.clear } } @@ -14639,7 +14639,7 @@ private enum SidebarTabDragPayload { static let typeIdentifier = "com.darkroom.programa.sidebar-tab-reorder" static let dropContentType = UTType(exportedAs: typeIdentifier) static let dropContentTypes: [UTType] = [dropContentType] - private static let prefix = "cmux.sidebar-tab." + private static let prefix = "programa.sidebar-tab." static func provider(for tabId: UUID) -> NSItemProvider { let provider = NSItemProvider() diff --git a/Sources/Find/SurfaceSearchOverlay.swift b/Sources/Find/SurfaceSearchOverlay.swift index 49ea8f52d98..93606a2483d 100644 --- a/Sources/Find/SurfaceSearchOverlay.swift +++ b/Sources/Find/SurfaceSearchOverlay.swift @@ -3,7 +3,7 @@ import Bonsplit import SwiftUI private extension NSView { - func cmuxAncestor(of type: T.Type) -> T? { + func programaAncestor(of type: T.Type) -> T? { var current: NSView? = self while let view = current { if let target = view as? T { @@ -285,7 +285,7 @@ private struct SearchTextFieldRepresentable: NSViewRepresentable { case #selector(NSResponder.cancelOperation(_:)): // Don't intercept Escape during CJK IME composition (issue #118) if textView.hasMarkedText() { return false } - control.cmuxAncestor(of: GhosttySurfaceScrollView.self)?.beginFindEscapeSuppression() + control.programaAncestor(of: GhosttySurfaceScrollView.self)?.beginFindEscapeSuppression() parent.onEscape() return true case #selector(NSResponder.insertNewline(_:)): diff --git a/Sources/GhosttyConfig.swift b/Sources/GhosttyConfig.swift index bb45b2a7701..ac03572d5ec 100644 --- a/Sources/GhosttyConfig.swift +++ b/Sources/GhosttyConfig.swift @@ -7,7 +7,7 @@ struct GhosttyConfig { case dark } - private static let cmuxReleaseBundleIdentifier = "com.darkroom.programa" + private static let programaReleaseBundleIdentifier = "com.darkroom.programa" private static let loadCacheLock = NSLock() private static var cachedConfigsByColorScheme: [ColorSchemePreference: GhosttyConfig] = [:] @@ -101,7 +101,7 @@ struct GhosttyConfig { loadCacheLock.unlock() } - private static func cmuxConfigPaths( + private static func programaConfigPaths( fileManager: FileManager = .default, currentBundleIdentifier: String? = Bundle.main.bundleIdentifier ) -> [String] { @@ -129,11 +129,11 @@ struct GhosttyConfig { } } - let releasePaths = paths(for: cmuxReleaseBundleIdentifier) + let releasePaths = paths(for: programaReleaseBundleIdentifier) guard let currentBundleIdentifier, !currentBundleIdentifier.isEmpty else { return releasePaths } - if currentBundleIdentifier == cmuxReleaseBundleIdentifier { + if currentBundleIdentifier == programaReleaseBundleIdentifier { return releasePaths } @@ -204,7 +204,7 @@ struct GhosttyConfig { "~/.config/ghostty/config.ghostty", "~/Library/Application Support/com.mitchellh.ghostty/config", "~/Library/Application Support/com.mitchellh.ghostty/config.ghostty", - ].map { NSString(string: $0).expandingTildeInPath } + cmuxConfigPaths() + ].map { NSString(string: $0).expandingTildeInPath } + programaConfigPaths() for path in configPaths { if let contents = readConfigFile(at: path) { diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index 0b64ada103b..dd003d2dfb9 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -44,14 +44,14 @@ func cmuxShouldUseClearWindowBackground(for opacity: Double) -> Bool { cmuxShouldUseTransparentBackgroundWindow() || opacity < 0.999 } -private func cmuxTransparentWindowBaseColor() -> NSColor { +private func programaTransparentWindowBaseColor() -> NSColor { // A tiny non-zero alpha matches Ghostty's window compositing behavior on macOS and // avoids visual artifacts that can happen with a fully clear window background. NSColor.white.withAlphaComponent(0.001) } #endif -private func cmuxRuntimeReadClipboardCallback( +private func programaRuntimeReadClipboardCallback( _ userdata: UnsafeMutableRawPointer?, _ location: ghostty_clipboard_e, _ state: UnsafeMutableRawPointer? @@ -60,7 +60,7 @@ private func cmuxRuntimeReadClipboardCallback( } #if DEBUG -private func cmuxChildExitProbePath() -> String? { +private func programaChildExitProbePath() -> String? { let env = ProcessInfo.processInfo.environment guard env["PROGRAMA_UI_TEST_CHILD_EXIT_KEYBOARD_SETUP"] == "1", let path = env["PROGRAMA_UI_TEST_CHILD_EXIT_KEYBOARD_PATH"], @@ -70,7 +70,7 @@ private func cmuxChildExitProbePath() -> String? { return path } -private func cmuxLoadChildExitProbe(at path: String) -> [String: String] { +private func programaLoadChildExitProbe(at path: String) -> [String: String] { guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let object = try? JSONSerialization.jsonObject(with: data) as? [String: String] else { return [:] @@ -78,9 +78,9 @@ private func cmuxLoadChildExitProbe(at path: String) -> [String: String] { return object } -private func cmuxWriteChildExitProbe(_ updates: [String: String], increments: [String: Int] = [:]) { - guard let path = cmuxChildExitProbePath() else { return } - var payload = cmuxLoadChildExitProbe(at: path) +private func programaWriteChildExitProbe(_ updates: [String: String], increments: [String: Int] = [:]) { + guard let path = programaChildExitProbePath() else { return } + var payload = programaLoadChildExitProbe(at: path) for (key, by) in increments { let current = Int(payload[key] ?? "") ?? 0 payload[key] = String(current + by) @@ -92,7 +92,7 @@ private func cmuxWriteChildExitProbe(_ updates: [String: String], increments: [S try? out.write(to: URL(fileURLWithPath: path), options: .atomic) } -private func cmuxScalarHex(_ value: String?) -> String { +private func programaScalarHex(_ value: String?) -> String { guard let value else { return "" } return value.unicodeScalars .map { String(format: "%04X", $0.value) } @@ -470,12 +470,12 @@ func cmuxPasteboardImagePathForTesting(_ pasteboard: NSPasteboard) -> String? { GhosttyPasteboardHelper.saveClipboardImageIfNeeded(from: pasteboard) } -func cmuxResolveQuicklookPathForTesting( +func programaResolveQuicklookPathForTesting( _ rawText: String, cwd: String, existingPaths: Set ) -> String? { - cmuxResolveQuicklookPath( + programaResolveQuicklookPath( rawText, cwd: cwd, fileExists: { path in @@ -485,7 +485,7 @@ func cmuxResolveQuicklookPathForTesting( } #endif -private func cmuxResolveQuicklookPath( +private func programaResolveQuicklookPath( _ rawText: String, cwd: String?, fileExists: (String) -> Bool = { FileManager.default.fileExists(atPath: $0) } @@ -494,7 +494,7 @@ private func cmuxResolveQuicklookPath( guard !trimmed.isEmpty else { return nil } var seenPaths: Set = [] - for token in cmuxQuicklookPathCandidates(from: trimmed) { + for token in programaQuicklookPathCandidates(from: trimmed) { let normalizedToken = token.trimmingCharacters(in: .whitespacesAndNewlines) guard !normalizedToken.isEmpty else { continue } @@ -517,7 +517,7 @@ private func cmuxResolveQuicklookPath( return nil } -private func cmuxQuicklookPathCandidates(from rawText: String) -> [String] { +private func programaQuicklookPathCandidates(from rawText: String) -> [String] { var candidates: [String] = [] func append(_ candidate: String?) { @@ -529,14 +529,14 @@ private func cmuxQuicklookPathCandidates(from rawText: String) -> [String] { append(rawText) - let unescaped = cmuxUnescapeShellToken(rawText) + let unescaped = programaUnescapeShellToken(rawText) if unescaped != rawText { append(unescaped) } - if let unquoted = cmuxUnquoteShellToken(rawText) { + if let unquoted = programaUnquoteShellToken(rawText) { append(unquoted) - let unescapedUnquoted = cmuxUnescapeShellToken(unquoted) + let unescapedUnquoted = programaUnescapeShellToken(unquoted) if unescapedUnquoted != unquoted { append(unescapedUnquoted) } @@ -545,7 +545,7 @@ private func cmuxQuicklookPathCandidates(from rawText: String) -> [String] { return candidates } -private func cmuxUnquoteShellToken(_ token: String) -> String? { +private func programaUnquoteShellToken(_ token: String) -> String? { guard token.count >= 2, let first = token.first, let last = token.last, @@ -556,7 +556,7 @@ private func cmuxUnquoteShellToken(_ token: String) -> String? { return String(token.dropFirst().dropLast()) } -private func cmuxUnescapeShellToken(_ token: String) -> String { +private func programaUnescapeShellToken(_ token: String) -> String { var output = String.UnicodeScalarView() output.reserveCapacity(token.unicodeScalars.count) var escaping = false @@ -583,7 +583,7 @@ private func cmuxUnescapeShellToken(_ token: String) -> String { return String(output) } -private func cmuxVisibleTerminalLines(from text: String, rows: Int) -> [String] { +private func programaVisibleTerminalLines(from text: String, rows: Int) -> [String] { let lines = text.split(separator: "\n", omittingEmptySubsequences: false).map(String.init) if lines.count > rows { return Array(lines.suffix(rows)) @@ -591,7 +591,7 @@ private func cmuxVisibleTerminalLines(from text: String, rows: Int) -> [String] return lines } -private func cmuxShellEscapedTokenContainingColumn( +private func programaShellEscapedTokenContainingColumn( in line: String, column: Int ) -> String? { @@ -635,7 +635,7 @@ private func cmuxShellEscapedTokenContainingColumn( return nil } -private func cmuxIsHardPathDelimiter( +private func programaIsHardPathDelimiter( in characters: [Character], at index: Int ) -> Bool { @@ -650,21 +650,21 @@ private func cmuxIsHardPathDelimiter( return previousIsWhitespace || nextIsWhitespace } -private func cmuxRawPathSegmentContainingColumn( +private func programaRawPathSegmentContainingColumn( in line: String, column: Int ) -> String? { let characters = Array(line) guard !characters.isEmpty, column >= 0, column < characters.count else { return nil } - guard !cmuxIsHardPathDelimiter(in: characters, at: column) else { return nil } + guard !programaIsHardPathDelimiter(in: characters, at: column) else { return nil } var start = column - while start > 0, !cmuxIsHardPathDelimiter(in: characters, at: start - 1) { + while start > 0, !programaIsHardPathDelimiter(in: characters, at: start - 1) { start -= 1 } var end = column - while (end + 1) < characters.count, !cmuxIsHardPathDelimiter(in: characters, at: end + 1) { + while (end + 1) < characters.count, !programaIsHardPathDelimiter(in: characters, at: end + 1) { end += 1 } @@ -672,7 +672,7 @@ private func cmuxRawPathSegmentContainingColumn( return candidate.isEmpty ? nil : candidate } -private func cmuxPathCandidatesContainingColumn( +private func programaPathCandidatesContainingColumn( in line: String, column: Int ) -> [String] { @@ -685,20 +685,20 @@ private func cmuxPathCandidatesContainingColumn( candidates.append(trimmed) } - append(cmuxRawPathSegmentContainingColumn(in: line, column: column)) - append(cmuxShellEscapedTokenContainingColumn(in: line, column: column)) + append(programaRawPathSegmentContainingColumn(in: line, column: column)) + append(programaShellEscapedTokenContainingColumn(in: line, column: column)) return candidates } -private func cmuxResolveVisibleLinePath( +private func programaResolveVisibleLinePath( _ line: String, column: Int, cwd: String, fileExists: (String) -> Bool = { FileManager.default.fileExists(atPath: $0) } ) -> (rawToken: String, path: String)? { - for rawToken in cmuxPathCandidatesContainingColumn(in: line, column: column) { - if let resolvedPath = cmuxResolveQuicklookPath(rawToken, cwd: cwd, fileExists: fileExists) { + for rawToken in programaPathCandidatesContainingColumn(in: line, column: column) { + if let resolvedPath = programaResolveQuicklookPath(rawToken, cwd: cwd, fileExists: fileExists) { return (rawToken, resolvedPath) } } @@ -1283,7 +1283,7 @@ class GhosttyApp { guard let workspace = MainActor.assumeIsolated({ callbackContext.terminalSurface?.owningWorkspace() }) else { - finish(.failure(NSError(domain: "cmux.remote.paste", code: 3))) + finish(.failure(NSError(domain: "programa.remote.paste", code: 3))) GhosttyPasteboardHelper.cleanupTransferredTemporaryImageFiles(fileURLs) return } @@ -1514,7 +1514,7 @@ class GhosttyApp { // though the C ABI returns `bool`. Store the C-compatible shim explicitly so the // project compiles against both importer variants. runtimeConfig.read_clipboard_cb = unsafeBitCast( - cmuxRuntimeReadClipboardCallback as @convention(c) ( + programaRuntimeReadClipboardCallback as @convention(c) ( UnsafeMutableRawPointer?, ghostty_clipboard_e, UnsafeMutableRawPointer? @@ -1561,7 +1561,7 @@ class GhosttyApp { let callbackTabId = callbackContext.tabId #if DEBUG - cmuxWriteChildExitProbe( + programaWriteChildExitProbe( [ "probeCloseSurfaceNeedsConfirm": needsConfirmClose ? "1" : "0", "probeCloseSurfaceTabId": callbackTabId?.uuidString ?? "", @@ -2784,7 +2784,7 @@ class GhosttyApp { ) #endif #if DEBUG - cmuxWriteChildExitProbe( + programaWriteChildExitProbe( [ "probeShowChildExitedTabId": callbackTabId?.uuidString ?? "", "probeShowChildExitedSurfaceId": callbackSurfaceId?.uuidString ?? "", @@ -3181,7 +3181,7 @@ class GhosttyApp { private func applyBackgroundToKeyWindow() { guard let window = activeMainWindow() else { return } if cmuxShouldUseClearWindowBackground(for: defaultBackgroundOpacity) { - window.backgroundColor = cmuxTransparentWindowBaseColor() + window.backgroundColor = programaTransparentWindowBaseColor() window.isOpaque = false applyWindowBlurIfNeeded(window) if backgroundLogEnabled { @@ -3598,7 +3598,7 @@ final class TerminalSurface: Identifiable, ObservableObject { } func debugSurfaceContextLabel() -> String { - cmuxSurfaceContextName(surfaceContext) + programaSurfaceContextName(surfaceContext) } func debugInitialCommand() -> String? { @@ -3634,7 +3634,7 @@ final class TerminalSurface: Identifiable, ObservableObject { let registry = TerminalSurfaceRegistry.shared let registeredOwnerId = registry.runtimeSurfaceOwnerId(surface) guard registeredOwnerId == id, - cmuxSurfacePointerAppearsLive(surface) else { + programaSurfacePointerAppearsLive(surface) else { let callbackContext = surfaceCallbackContext surfaceCallbackContext = nil registry.unregisterRuntimeSurface(surface, ownerId: id) @@ -4065,7 +4065,7 @@ final class TerminalSurface: Identifiable, ObservableObject { #if DEBUG let templateFontText = String(format: "%.2f", surfaceConfig.font_size) dlog( - "zoom.create surface=\(id.uuidString.prefix(5)) context=\(cmuxSurfaceContextName(surfaceContext)) " + + "zoom.create surface=\(id.uuidString.prefix(5)) context=\(programaSurfaceContextName(surfaceContext)) " + "templateFont=\(templateFontText)" ) #endif @@ -4317,7 +4317,7 @@ final class TerminalSurface: Identifiable, ObservableObject { // (new surface, split, new workspace) preserve zoom from the source terminal. if let inheritedFontPoints = configTemplate?.fontSize, inheritedFontPoints > 0 { - let currentFontPoints = cmuxCurrentSurfaceFontSizePoints(createdSurface) + let currentFontPoints = programaCurrentSurfaceFontSizePoints(createdSurface) let shouldReapply = { guard let currentFontPoints else { return true } return abs(currentFontPoints - inheritedFontPoints) > 0.05 @@ -4351,11 +4351,11 @@ final class TerminalSurface: Identifiable, ObservableObject { ) #if DEBUG - let runtimeFontText = cmuxCurrentSurfaceFontSizePoints(createdSurface).map { + let runtimeFontText = programaCurrentSurfaceFontSizePoints(createdSurface).map { String(format: "%.2f", $0) } ?? "nil" dlog( - "zoom.create.done surface=\(id.uuidString.prefix(5)) context=\(cmuxSurfaceContextName(surfaceContext)) " + + "zoom.create.done surface=\(id.uuidString.prefix(5)) context=\(programaSurfaceContextName(surfaceContext)) " + "runtimeFont=\(runtimeFontText)" ) #endif @@ -5254,7 +5254,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { applySurfaceBackground() let color = effectiveBackgroundColor() if cmuxShouldUseClearWindowBackground(for: color.alphaComponent) { - window.backgroundColor = cmuxTransparentWindowBaseColor() + window.backgroundColor = programaTransparentWindowBaseColor() window.isOpaque = false GhosttyApp.shared.applyWindowBlurIfNeeded(window) } else { @@ -6241,10 +6241,10 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { #endif #if DEBUG - cmuxWriteChildExitProbe( + programaWriteChildExitProbe( [ - "probePerformCharsHex": cmuxScalarHex(event.characters), - "probePerformCharsIgnoringHex": cmuxScalarHex(event.charactersIgnoringModifiers), + "probePerformCharsHex": programaScalarHex(event.characters), + "probePerformCharsIgnoringHex": programaScalarHex(event.charactersIgnoringModifiers), "probePerformKeyCode": String(event.keyCode), "probePerformModsRaw": String(event.modifierFlags.rawValue), "probePerformSurfaceId": terminalSurface?.id.uuidString ?? "", @@ -6423,10 +6423,10 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { #endif #if DEBUG - cmuxWriteChildExitProbe( + programaWriteChildExitProbe( [ - "probeKeyDownCharsHex": cmuxScalarHex(event.characters), - "probeKeyDownCharsIgnoringHex": cmuxScalarHex(event.charactersIgnoringModifiers), + "probeKeyDownCharsHex": programaScalarHex(event.characters), + "probeKeyDownCharsIgnoringHex": programaScalarHex(event.charactersIgnoringModifiers), "probeKeyDownKeyCode": String(event.keyCode), "probeKeyDownModsRaw": String(event.modifierFlags.rawValue), "probeKeyDownSurfaceId": terminalSurface?.id.uuidString ?? "", @@ -6490,8 +6490,8 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { #if DEBUG dlog( "key.ctrl path=ghostty surface=\(terminalSurface?.id.uuidString.prefix(5) ?? "nil") " + - "handled=\(handled ? 1 : 0) keyCode=\(event.keyCode) chars=\(cmuxScalarHex(event.characters)) " + - "ign=\(cmuxScalarHex(event.charactersIgnoringModifiers)) mods=\(event.modifierFlags.rawValue)" + "handled=\(handled ? 1 : 0) keyCode=\(event.keyCode) chars=\(programaScalarHex(event.characters)) " + + "ign=\(programaScalarHex(event.charactersIgnoringModifiers)) mods=\(event.modifierFlags.rawValue)" ) #endif // If Ghostty handled the key (action/encoding), we're done. @@ -7250,11 +7250,11 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { let wordData = Data(bytes: ptr, count: Int(text.text_len)) if let decodedWord = String(bytes: wordData, encoding: .utf8) { #if DEBUG - let resolvedQuicklookWord = cmuxTerminalCmdClickQuicklookOverride(decodedWord) + let resolvedQuicklookWord = programaTerminalCmdClickQuicklookOverride(decodedWord) #else let resolvedQuicklookWord = decodedWord #endif - if let resolvedPath = cmuxResolveQuicklookPath(resolvedQuicklookWord, cwd: cwd) { + if let resolvedPath = programaResolveQuicklookPath(resolvedQuicklookWord, cwd: cwd) { quicklookResolution = makeWordPathResolution( path: resolvedPath, source: .quicklook, @@ -7267,7 +7267,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { var viewportResolution: WordPathResolution? if text.offset_len > 0 { #if DEBUG - let viewportOffsetStart = cmuxTerminalCmdClickViewportOffsetDelta(Int(text.offset_start)) + let viewportOffsetStart = programaTerminalCmdClickViewportOffsetDelta(Int(text.offset_start)) #else let viewportOffsetStart = Int(text.offset_start) #endif @@ -7302,7 +7302,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { } #if DEBUG - private func cmuxTerminalCmdClickQuicklookOverride(_ decodedWord: String) -> String { + private func programaTerminalCmdClickQuicklookOverride(_ decodedWord: String) -> String { let env = ProcessInfo.processInfo.environment guard let override = env["PROGRAMA_UI_TEST_TERMINAL_CMD_CLICK_QUICKLOOK_OVERRIDE"]? .trimmingCharacters(in: .whitespacesAndNewlines), @@ -7312,7 +7312,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { return override } - private func cmuxTerminalCmdClickViewportOffsetDelta(_ viewportOffsetStart: Int) -> Int { + private func programaTerminalCmdClickViewportOffsetDelta(_ viewportOffsetStart: Int) -> Int { let env = ProcessInfo.processInfo.environment guard let delta = env["PROGRAMA_UI_TEST_TERMINAL_CMD_CLICK_VIEWPORT_OFFSET_DELTA"]? .trimmingCharacters(in: .whitespacesAndNewlines), @@ -7453,14 +7453,14 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { terminalPanel: panel, lineLimit: max(200, rows * 4) ) ?? "" - let visibleLines = cmuxVisibleTerminalLines(from: visibleText, rows: rows) + let visibleLines = programaVisibleTerminalLines(from: visibleText, rows: rows) let rowOffset = max(0, rows - visibleLines.count) let rowFromTop = max(0, min(rows - 1, viewportOffsetStart / cols)) let visibleRow = rowFromTop - rowOffset guard visibleRow >= 0, visibleRow < visibleLines.count else { return nil } let column = max(0, min(cols - 1, viewportOffsetStart % cols)) - guard let resolution = cmuxResolveVisibleLinePath( + guard let resolution = programaResolveVisibleLinePath( visibleLines[visibleRow], column: column, cwd: cwd @@ -7497,7 +7497,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { terminalPanel: panel, lineLimit: max(200, rows * 4) ) ?? "" - let visibleLines = cmuxVisibleTerminalLines(from: visibleText, rows: rows) + let visibleLines = programaVisibleTerminalLines(from: visibleText, rows: rows) let rowOffset = max(0, rows - visibleLines.count) let xInset = max(0, (bounds.width - (CGFloat(cols) * resolvedCellWidth)) / 2) let yInset = max(0, (bounds.height - (CGFloat(rows) * resolvedCellHeight)) / 2) @@ -7508,7 +7508,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { guard visibleRow >= 0, visibleRow < visibleLines.count else { return nil } let column = max(0, min(cols - 1, Int((point.x - xInset) / resolvedCellWidth))) - guard let resolution = cmuxResolveVisibleLinePath( + guard let resolution = programaResolveVisibleLinePath( visibleLines[visibleRow], column: column, cwd: cwd @@ -8194,7 +8194,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { } }, uploadDetectedSSH: { _, _, _, finish in - finish(.failure(NSError(domain: "cmux.remote.drop", code: 4))) + finish(.failure(NSError(domain: "programa.remote.drop", code: 4))) }, insertText: sendText, onFailure: { _ in onFailure() } @@ -8230,7 +8230,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { guard let workspace = MainActor.assumeIsolated({ self?.terminalSurface?.owningWorkspace() }) else { - finish(.failure(NSError(domain: "cmux.remote.drop", code: 3))) + finish(.failure(NSError(domain: "programa.remote.drop", code: 3))) GhosttyPasteboardHelper.cleanupTransferredTemporaryImageFiles(fileURLs) return } @@ -8333,7 +8333,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { fileprivate func debugSimulateFileDrop(paths: [String]) -> Bool { guard !paths.isEmpty else { return false } let urls = paths.map { URL(fileURLWithPath: $0) as NSURL } - let pbName = NSPasteboard.Name("cmux.debug.drop.\(UUID().uuidString)") + let pbName = NSPasteboard.Name("programa.debug.drop.\(UUID().uuidString)") let pasteboard = NSPasteboard(name: pbName) pasteboard.clearContents() pasteboard.writeObjects(urls) @@ -8794,8 +8794,8 @@ final class GhosttySurfaceScrollView: NSView { inactiveOverlayView.isHidden = true addSubview(inactiveOverlayView) dropZoneOverlayView.wantsLayer = true - dropZoneOverlayView.layer?.backgroundColor = cmuxAccentNSColor().withAlphaComponent(0.25).cgColor - dropZoneOverlayView.layer?.borderColor = cmuxAccentNSColor().cgColor + dropZoneOverlayView.layer?.backgroundColor = programaAccentNSColor().withAlphaComponent(0.25).cgColor + dropZoneOverlayView.layer?.borderColor = programaAccentNSColor().cgColor dropZoneOverlayView.layer?.borderWidth = 2 dropZoneOverlayView.layer?.cornerRadius = 8 dropZoneOverlayView.isHidden = true @@ -9983,7 +9983,7 @@ final class GhosttySurfaceScrollView: NSView { return CAMediaTimingFunction(name: .easeOut) } } - self.flashLayer.add(animation, forKey: "cmux.flash") + self.flashLayer.add(animation, forKey: "programa.flash") } } @@ -11277,9 +11277,9 @@ extension GhosttyNSView: NSTextInputClient { let typingTimingStart = ProgramaTypingTiming.start() #endif #if DEBUG - cmuxWriteChildExitProbe( + programaWriteChildExitProbe( [ - "probeInsertTextCharsHex": cmuxScalarHex(chars), + "probeInsertTextCharsHex": programaScalarHex(chars), "probeInsertTextSurfaceId": terminalSurface?.id.uuidString ?? "", ], increments: ["probeInsertTextCount": 1] diff --git a/Sources/NotificationsPage.swift b/Sources/NotificationsPage.swift index 4ec4cc8843b..fe6d1db8d7d 100644 --- a/Sources/NotificationsPage.swift +++ b/Sources/NotificationsPage.swift @@ -183,11 +183,11 @@ private struct NotificationRow: View { Button(action: onOpen) { HStack(alignment: .top, spacing: 12) { Circle() - .fill(notification.isRead ? Color.clear : cmuxAccentColor()) + .fill(notification.isRead ? Color.clear : programaAccentColor()) .frame(width: 8, height: 8) .overlay( Circle() - .stroke(cmuxAccentColor().opacity(notification.isRead ? 0.2 : 1), lineWidth: 1) + .stroke(programaAccentColor().opacity(notification.isRead ? 0.2 : 1), lineWidth: 1) ) .padding(.top, 6) diff --git a/Sources/Panels/BrowserPanel.swift b/Sources/Panels/BrowserPanel.swift index 955643c0718..9b4353d7d64 100644 --- a/Sources/Panels/BrowserPanel.swift +++ b/Sources/Panels/BrowserPanel.swift @@ -3211,7 +3211,7 @@ final class BrowserPanel: Panel, ObservableObject { webViewObservers.append(progressObserver) let fullscreenObserver = webView.observe(\.fullscreenState, options: [.initial, .new]) { [weak self] webView, _ in - let isElementFullscreenActive = webView.cmuxIsElementFullscreenActiveOrTransitioning + let isElementFullscreenActive = webView.programaIsElementFullscreenActiveOrTransitioning let fullscreenState = webView.fullscreenState Task { @MainActor in guard let self, self.isCurrentWebView(webView, instanceID: observedWebViewInstanceID) else { return } @@ -4449,13 +4449,13 @@ extension BrowserPanel { guard preferredDeveloperToolsPresentation == .unknown else { return } let attachSelector = NSSelectorFromString("attach") guard inspector.responds(to: attachSelector) else { return } - inspector.cmuxCallVoid(selector: attachSelector) + inspector.programaCallVoid(selector: attachSelector) } @discardableResult private func revealDeveloperTools(_ inspector: NSObject) -> Bool { let isVisibleSelector = NSSelectorFromString("isVisible") - if inspector.cmuxCallBool(selector: isVisibleSelector) ?? false { + if inspector.programaCallBool(selector: isVisibleSelector) ?? false { developerToolsDetachedOpenGraceDeadline = nil developerToolsLastKnownVisibleAt = Date() return true @@ -4465,8 +4465,8 @@ extension BrowserPanel { let showSelector = NSSelectorFromString("show") guard inspector.responds(to: showSelector) else { return false } - inspector.cmuxCallVoid(selector: showSelector) - let visibleAfterShow = inspector.cmuxCallBool(selector: isVisibleSelector) ?? false + inspector.programaCallVoid(selector: showSelector) + let visibleAfterShow = inspector.programaCallBool(selector: isVisibleSelector) ?? false if visibleAfterShow { developerToolsLastKnownVisibleAt = Date() } @@ -4483,21 +4483,21 @@ extension BrowserPanel { @discardableResult private func concealDeveloperTools(_ inspector: NSObject) -> Bool { let isVisibleSelector = NSSelectorFromString("isVisible") - guard inspector.cmuxCallBool(selector: isVisibleSelector) ?? false else { return true } + guard inspector.programaCallBool(selector: isVisibleSelector) ?? false else { return true } var invokedSelector = false for rawSelector in ["hide", "close"] { let selector = NSSelectorFromString(rawSelector) guard inspector.responds(to: selector) else { continue } invokedSelector = true - inspector.cmuxCallVoid(selector: selector) - if !(inspector.cmuxCallBool(selector: isVisibleSelector) ?? false) { + inspector.programaCallVoid(selector: selector) + if !(inspector.programaCallBool(selector: isVisibleSelector) ?? false) { return true } } guard invokedSelector else { return false } - return !(inspector.cmuxCallBool(selector: isVisibleSelector) ?? false) + return !(inspector.programaCallBool(selector: isVisibleSelector) ?? false) } private var isDeveloperToolsTransitionInFlight: Bool { @@ -4564,10 +4564,10 @@ extension BrowserPanel { to targetVisible: Bool, source: String ) -> Bool { - guard let inspector = webView.cmuxInspectorObject() else { return false } + guard let inspector = webView.programaInspectorObject() else { return false } let isVisibleSelector = NSSelectorFromString("isVisible") - let visible = inspector.cmuxCallBool(selector: isVisibleSelector) ?? false + let visible = inspector.programaCallBool(selector: isVisibleSelector) ?? false setPreferredDeveloperToolsVisible(targetVisible) developerToolsTransitionTargetVisible = targetVisible @@ -4589,7 +4589,7 @@ extension BrowserPanel { } if targetVisible { - let visibleAfterTransition = inspector.cmuxCallBool(selector: isVisibleSelector) ?? false + let visibleAfterTransition = inspector.programaCallBool(selector: isVisibleSelector) ?? false if visibleAfterTransition { syncDeveloperToolsPresentationPreferenceFromUI() cancelDeveloperToolsRestoreRetry() @@ -4647,7 +4647,7 @@ extension BrowserPanel { func showDeveloperToolsConsole() -> Bool { guard showDeveloperTools() else { return false } guard !isDeveloperToolsTransitionInFlight else { return true } - guard let inspector = webView.cmuxInspectorObject() else { return true } + guard let inspector = webView.programaInspectorObject() else { return true } // WebKit private inspector API differs by OS; try known console selectors. let consoleSelectors = [ "showConsole", @@ -4657,7 +4657,7 @@ extension BrowserPanel { for raw in consoleSelectors { let selector = NSSelectorFromString(raw) if inspector.responds(to: selector) { - inspector.cmuxCallVoid(selector: selector) + inspector.programaCallVoid(selector: selector) break } } @@ -4666,8 +4666,8 @@ extension BrowserPanel { /// Called before WKWebView detaches so manual inspector closes are respected. func syncDeveloperToolsPreferenceFromInspector(preserveVisibleIntent: Bool = false) { - guard let inspector = webView.cmuxInspectorObject() else { return } - guard let visible = inspector.cmuxCallBool(selector: NSSelectorFromString("isVisible")) else { return } + guard let inspector = webView.programaInspectorObject() else { return } + guard let visible = inspector.programaCallBool(selector: NSSelectorFromString("isVisible")) else { return } if isDeveloperToolsTransitionInFlight { let targetVisible = pendingDeveloperToolsTransitionTargetVisible ?? developerToolsTransitionTargetVisible ?? visible setPreferredDeveloperToolsVisible(targetVisible) @@ -4741,8 +4741,8 @@ extension BrowserPanel { return false } guard developerToolsLastKnownVisibleAt != nil else { return false } - guard let inspector = inspector ?? webView.cmuxInspectorObject() else { return false } - guard let visible = inspector.cmuxCallBool(selector: NSSelectorFromString("isVisible")) else { return false } + guard let inspector = inspector ?? webView.programaInspectorObject() else { return false } + guard let visible = inspector.programaCallBool(selector: NSSelectorFromString("isVisible")) else { return false } guard !visible else { developerToolsLastKnownVisibleAt = Date() return false @@ -4770,7 +4770,7 @@ extension BrowserPanel { return } guard !isDeveloperToolsTransitionInFlight else { return } - guard let inspector = webView.cmuxInspectorObject() else { + guard let inspector = webView.programaInspectorObject() else { scheduleDeveloperToolsRestoreRetry() return } @@ -4778,7 +4778,7 @@ extension BrowserPanel { let shouldForceRefresh = forceDeveloperToolsRefreshOnNextAttach forceDeveloperToolsRefreshOnNextAttach = false - let visible = inspector.cmuxCallBool(selector: NSSelectorFromString("isVisible")) ?? false + let visible = inspector.programaCallBool(selector: NSSelectorFromString("isVisible")) ?? false if visible { developerToolsDetachedOpenGraceDeadline = nil syncDeveloperToolsPresentationPreferenceFromUI() @@ -4822,7 +4822,7 @@ extension BrowserPanel { _ = revealDeveloperTools(inspector) } setPreferredDeveloperToolsVisible(true) - let visibleAfterShow = inspector.cmuxCallBool(selector: NSSelectorFromString("isVisible")) ?? false + let visibleAfterShow = inspector.programaCallBool(selector: NSSelectorFromString("isVisible")) ?? false if visibleAfterShow { syncDeveloperToolsPresentationPreferenceFromUI() developerToolsLastKnownVisibleAt = Date() @@ -4835,8 +4835,8 @@ extension BrowserPanel { @discardableResult func isDeveloperToolsVisible() -> Bool { - guard let inspector = webView.cmuxInspectorObject() else { return false } - return inspector.cmuxCallBool(selector: NSSelectorFromString("isVisible")) ?? false + guard let inspector = webView.programaInspectorObject() else { return false } + return inspector.programaCallBool(selector: NSSelectorFromString("isVisible")) ?? false } @discardableResult @@ -5662,7 +5662,7 @@ extension BrowserPanel { func debugDeveloperToolsStateSummary() -> String { let preferred = preferredDeveloperToolsVisible ? 1 : 0 let visible = isDeveloperToolsVisible() ? 1 : 0 - let inspector = webView.cmuxInspectorObject() == nil ? 0 : 1 + let inspector = webView.programaInspectorObject() == nil ? 0 : 1 let attached = webView.superview == nil ? 0 : 1 let inWindow = webView.window == nil ? 0 : 1 let forceRefresh = forceDeveloperToolsRefreshOnNextAttach ? 1 : 0 @@ -5784,7 +5784,7 @@ extension BrowserPanel { } extension WKWebView { - func cmuxInspectorObject() -> NSObject? { + func programaInspectorObject() -> NSObject? { let selector = NSSelectorFromString("_inspector") guard responds(to: selector), let inspector = perform(selector)?.takeUnretainedValue() as? NSObject else { @@ -5793,8 +5793,8 @@ extension WKWebView { return inspector } - func cmuxInspectorFrontendWebView() -> WKWebView? { - guard let inspector = cmuxInspectorObject() else { return nil } + func programaInspectorFrontendWebView() -> WKWebView? { + guard let inspector = programaInspectorObject() else { return nil } let selector = NSSelectorFromString("inspectorWebView") guard inspector.responds(to: selector), let inspectorWebView = inspector.perform(selector)?.takeUnretainedValue() as? WKWebView else { @@ -5805,14 +5805,14 @@ extension WKWebView { } private extension NSObject { - func cmuxCallBool(selector: Selector) -> Bool? { + func programaCallBool(selector: Selector) -> Bool? { guard responds(to: selector) else { return nil } typealias Fn = @convention(c) (AnyObject, Selector) -> Bool let fn = unsafeBitCast(method(for: selector), to: Fn.self) return fn(self, selector) } - func cmuxCallVoid(selector: Selector) { + func programaCallVoid(selector: Selector) { guard responds(to: selector) else { return } typealias Fn = @convention(c) (AnyObject, Selector) -> Void let fn = unsafeBitCast(method(for: selector), to: Fn.self) diff --git a/Sources/Panels/BrowserPanelView.swift b/Sources/Panels/BrowserPanelView.swift index 8e64f5f2f46..82b6ae264cc 100644 --- a/Sources/Panels/BrowserPanelView.swift +++ b/Sources/Panels/BrowserPanelView.swift @@ -4,7 +4,7 @@ import WebKit import AppKit import ObjectiveC -private var cmuxBrowserPanelNeedsRenderingStateReattachKey: UInt8 = 0 +private var programaBrowserPanelNeedsRenderingStateReattachKey: UInt8 = 0 private func browserPanelViewObjectID(_ object: AnyObject?) -> String { guard let object else { return "nil" } @@ -28,32 +28,32 @@ private extension NSObject { } private extension WKWebView { - private var cmuxBrowserPanelNeedsRenderingStateReattach: Bool { + private var programaBrowserPanelNeedsRenderingStateReattach: Bool { get { - (objc_getAssociatedObject(self, &cmuxBrowserPanelNeedsRenderingStateReattachKey) as? NSNumber)? + (objc_getAssociatedObject(self, &programaBrowserPanelNeedsRenderingStateReattachKey) as? NSNumber)? .boolValue ?? false } set { objc_setAssociatedObject( self, - &cmuxBrowserPanelNeedsRenderingStateReattachKey, + &programaBrowserPanelNeedsRenderingStateReattachKey, NSNumber(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) } } - var cmuxBrowserPanelRequiresRenderingStateReattach: Bool { - cmuxBrowserPanelNeedsRenderingStateReattach + var programaBrowserPanelRequiresRenderingStateReattach: Bool { + programaBrowserPanelNeedsRenderingStateReattach } - private func cmuxBrowserPanelApplyRenderingStateRefresh( + private func programaBrowserPanelApplyRenderingStateRefresh( reason: String, force: Bool ) { - guard force || cmuxBrowserPanelNeedsRenderingStateReattach else { return } + guard force || programaBrowserPanelNeedsRenderingStateReattach else { return } guard window != nil else { return } - cmuxBrowserPanelNeedsRenderingStateReattach = false + programaBrowserPanelNeedsRenderingStateReattach = false let firedSelectors = [ "viewDidUnhide", @@ -87,8 +87,8 @@ private extension WKWebView { #endif } - func cmuxBrowserPanelNotifyHidden(reason: String) { - cmuxBrowserPanelNeedsRenderingStateReattach = true + func programaBrowserPanelNotifyHidden(reason: String) { + programaBrowserPanelNeedsRenderingStateReattach = true let firedSelectors = ["viewDidHide", "_exitInWindow"].filter { browserPanelCallVoidIfAvailable($0) } @@ -102,12 +102,12 @@ private extension WKWebView { #endif } - func cmuxBrowserPanelReattachRenderingState(reason: String) { - cmuxBrowserPanelApplyRenderingStateRefresh(reason: reason, force: false) + func programaBrowserPanelReattachRenderingState(reason: String) { + programaBrowserPanelApplyRenderingStateRefresh(reason: reason, force: false) } - func cmuxBrowserPanelForceRenderingStateRefresh(reason: String) { - cmuxBrowserPanelApplyRenderingStateRefresh(reason: reason, force: true) + func programaBrowserPanelForceRenderingStateRefresh(reason: String) { + programaBrowserPanelApplyRenderingStateRefresh(reason: reason, force: true) } } @@ -179,7 +179,7 @@ enum BrowserDevToolsIconColorOption: String, CaseIterable, Identifiable { // Matches Bonsplit tab icon tint for active tabs. return Color(nsColor: .labelColor) case .accent: - return cmuxAccentColor() + return programaAccentColor() case .tertiary: return Color(nsColor: .tertiaryLabelColor) } @@ -303,7 +303,7 @@ private struct OmnibarAddressButtonStyleBody: View { } private extension View { - func cmuxFlatSymbolColorRendering() -> some View { + func programaFlatSymbolColorRendering() -> some View { // `symbolColorRenderingMode(.flat)` is not available in the current SDK // used by CI/local builds. Keep this modifier as a compatibility no-op. self @@ -578,8 +578,8 @@ struct BrowserPanelView: View { } .overlay { RoundedRectangle(cornerRadius: FocusFlashPattern.ringCornerRadius) - .stroke(cmuxAccentColor().opacity(focusFlashOpacity), lineWidth: 3) - .shadow(color: cmuxAccentColor().opacity(focusFlashOpacity * 0.35), radius: 10) + .stroke(programaAccentColor().opacity(focusFlashOpacity), lineWidth: 3) + .shadow(color: programaAccentColor().opacity(focusFlashOpacity * 0.35), radius: 10) .padding(FocusFlashPattern.ringInset) .allowsHitTesting(false) } @@ -946,7 +946,7 @@ struct BrowserPanelView: View { }) { Image(systemName: "cursorarrow.click.2") .symbolRenderingMode(.monochrome) - .cmuxFlatSymbolColorRendering() + .programaFlatSymbolColorRendering() .font(.system(size: devToolsButtonIconSize, weight: .medium)) .foregroundStyle(panel.isReactGrabActive ? Color.accentColor : Color.secondary) .frame(width: addressBarButtonSize, height: addressBarButtonSize, alignment: .center) @@ -963,7 +963,7 @@ struct BrowserPanelView: View { }) { Image(systemName: devToolsIconOption.rawValue) .symbolRenderingMode(.monochrome) - .cmuxFlatSymbolColorRendering() + .programaFlatSymbolColorRendering() .font(.system(size: devToolsButtonIconSize, weight: .medium)) .foregroundStyle(devToolsColorOption.color) .frame(width: addressBarButtonSize, height: addressBarButtonSize, alignment: .center) @@ -980,7 +980,7 @@ struct BrowserPanelView: View { }) { Image(systemName: "person.crop.circle") .symbolRenderingMode(.monochrome) - .cmuxFlatSymbolColorRendering() + .programaFlatSymbolColorRendering() .font(.system(size: devToolsButtonIconSize, weight: .medium)) .foregroundStyle(devToolsColorOption.color) .frame(width: addressBarButtonSize, height: addressBarButtonSize, alignment: .center) @@ -1008,7 +1008,7 @@ struct BrowserPanelView: View { }) { Image(systemName: browserThemeMode.iconName) .symbolRenderingMode(.monochrome) - .cmuxFlatSymbolColorRendering() + .programaFlatSymbolColorRendering() .font(.system(size: devToolsButtonIconSize, weight: .medium)) .foregroundStyle(browserThemeModeIconColor) .frame(width: addressBarButtonSize, height: addressBarButtonSize, alignment: .center) @@ -1230,7 +1230,7 @@ struct BrowserPanelView: View { ) .overlay( RoundedRectangle(cornerRadius: omnibarPillCornerRadius, style: .continuous) - .stroke(addressBarFocused ? cmuxAccentColor() : Color.clear, lineWidth: 1) + .stroke(addressBarFocused ? programaAccentColor() : Color.clear, lineWidth: 1) ) .accessibilityElement(children: .contain) .background { @@ -1357,20 +1357,20 @@ struct BrowserPanelView: View { reason: String, isPanelFocusedOverride: Bool? = nil ) { - guard let cmuxWebView = panel.webView as? ProgramaWebView else { return } + guard let programaWebView = panel.webView as? ProgramaWebView else { return } let isPanelFocused = isPanelFocusedOverride ?? isFocused let next = isPanelFocused && !panel.shouldSuppressWebViewFocus() - if cmuxWebView.allowsFirstResponderAcquisition != next { + if programaWebView.allowsFirstResponderAcquisition != next { #if DEBUG dlog( "browser.focus.policy.resync panel=\(panel.id.uuidString.prefix(5)) " + - "web=\(ObjectIdentifier(cmuxWebView)) old=\(cmuxWebView.allowsFirstResponderAcquisition ? 1 : 0) " + + "web=\(ObjectIdentifier(programaWebView)) old=\(programaWebView.allowsFirstResponderAcquisition ? 1 : 0) " + "new=\(next ? 1 : 0) reason=\(reason) " + "panelFocusedUsed=\(isPanelFocused ? 1 : 0)" ) #endif } - cmuxWebView.allowsFirstResponderAcquisition = next + programaWebView.allowsFirstResponderAcquisition = next } private func setAddressBarFocused(_ focused: Bool, reason: String) { @@ -4868,7 +4868,7 @@ struct WebViewRepresentable: NSViewRepresentable { private func notifyHostedWebKitHidden(reason: String) { for webView in hostedWebKitSubviews { - webView.cmuxBrowserPanelNotifyHidden(reason: reason) + webView.programaBrowserPanelNotifyHidden(reason: reason) } } @@ -4913,9 +4913,9 @@ struct WebViewRepresentable: NSViewRepresentable { } webView.layoutSubtreeIfNeeded() if forceLifecycleRefresh { - webView.cmuxBrowserPanelForceRenderingStateRefresh(reason: reason) + webView.programaBrowserPanelForceRenderingStateRefresh(reason: reason) } else { - webView.cmuxBrowserPanelReattachRenderingState(reason: reason) + webView.programaBrowserPanelReattachRenderingState(reason: reason) } webView.displayIfNeeded() } @@ -6038,7 +6038,7 @@ struct WebViewRepresentable: NSViewRepresentable { for webView: WKWebView, relativeTo expectedWindow: NSWindow? ) -> Bool { - webView.cmuxIsManagedByExternalFullscreenWindow(relativeTo: expectedWindow) + webView.programaIsManagedByExternalFullscreenWindow(relativeTo: expectedWindow) } private static func localInlineTransferRoot(for webView: WKWebView) -> NSView? { @@ -6084,7 +6084,7 @@ struct WebViewRepresentable: NSViewRepresentable { append(directTransferChild(of: sourceSuperview, containing: primaryWebView) ?? primaryWebView) - if let inspectorFrontend = primaryWebView.cmuxInspectorFrontendWebView() { + if let inspectorFrontend = primaryWebView.programaInspectorFrontendWebView() { append(directTransferChild(of: sourceSuperview, containing: inspectorFrontend) ?? inspectorFrontend) } @@ -6256,7 +6256,7 @@ struct WebViewRepresentable: NSViewRepresentable { width: preferredAttachedWidthState.width, widthFraction: preferredAttachedWidthState.widthFraction ) - host.setHostedInspectorFrontendWebView(webView.cmuxInspectorFrontendWebView()) + host.setHostedInspectorFrontendWebView(webView.programaInspectorFrontendWebView()) host.onPreferredHostedInspectorWidthChanged = { [weak browserPanel = panel] width, _ in guard let browserPanel else { return } browserPanel.recordPreferredAttachedDeveloperToolsWidth( @@ -6313,7 +6313,7 @@ struct WebViewRepresentable: NSViewRepresentable { : "localInline.reconcile.existingHost" ) } - host.setHostedInspectorFrontendWebView(webView.cmuxInspectorFrontendWebView()) + host.setHostedInspectorFrontendWebView(webView.programaInspectorFrontendWebView()) let didRevealDeveloperToolsAfterAttach = !wasDeveloperToolsVisible && panel.isDeveloperToolsVisible() webView.needsLayout = true @@ -6347,7 +6347,7 @@ struct WebViewRepresentable: NSViewRepresentable { reason: "localInline.reconcile.async" ) } - host.setHostedInspectorFrontendWebView(webView.cmuxInspectorFrontendWebView()) + host.setHostedInspectorFrontendWebView(webView.programaInspectorFrontendWebView()) host.refreshHostedWebKitPresentation( reason: didAttachWebViewToLocalHost ? "localInline.update.async" @@ -6714,19 +6714,19 @@ struct WebViewRepresentable: NSViewRepresentable { webView: WKWebView, isPanelFocused: Bool ) { - guard let cmuxWebView = webView as? ProgramaWebView else { return } + guard let programaWebView = webView as? ProgramaWebView else { return } let next = isPanelFocused && !panel.shouldSuppressWebViewFocus() - if cmuxWebView.allowsFirstResponderAcquisition != next { + if programaWebView.allowsFirstResponderAcquisition != next { #if DEBUG dlog( "browser.focus.policy panel=\(panel.id.uuidString.prefix(5)) " + - "web=\(ObjectIdentifier(cmuxWebView)) old=\(cmuxWebView.allowsFirstResponderAcquisition ? 1 : 0) " + + "web=\(ObjectIdentifier(programaWebView)) old=\(programaWebView.allowsFirstResponderAcquisition ? 1 : 0) " + "new=\(next ? 1 : 0) isPanelFocused=\(isPanelFocused ? 1 : 0) " + "suppress=\(panel.shouldSuppressWebViewFocus() ? 1 : 0)" ) #endif } - cmuxWebView.allowsFirstResponderAcquisition = next + programaWebView.allowsFirstResponderAcquisition = next } static func dismantleNSView(_ nsView: NSView, coordinator: Coordinator) { diff --git a/Sources/Panels/BrowserPopupWindowController.swift b/Sources/Panels/BrowserPopupWindowController.swift index 743144ac2d9..e3e4e0a7f6c 100644 --- a/Sources/Panels/BrowserPopupWindowController.swift +++ b/Sources/Panels/BrowserPopupWindowController.swift @@ -153,7 +153,7 @@ final class BrowserPopupWindowController: NSObject, NSWindowDelegate { backing: .buffered, defer: false ) - panel.identifier = NSUserInterfaceItemIdentifier("cmux.browser-popup") + panel.identifier = NSUserInterfaceItemIdentifier("programa.browser-popup") panel.level = NSWindow.Level.normal panel.hidesOnDeactivate = false panel.isReleasedWhenClosed = false diff --git a/Sources/Panels/MarkdownPanelView.swift b/Sources/Panels/MarkdownPanelView.swift index d9a051d78c7..40ef120a570 100644 --- a/Sources/Panels/MarkdownPanelView.swift +++ b/Sources/Panels/MarkdownPanelView.swift @@ -27,8 +27,8 @@ struct MarkdownPanelView: View { .background(backgroundColor) .overlay { RoundedRectangle(cornerRadius: FocusFlashPattern.ringCornerRadius) - .stroke(cmuxAccentColor().opacity(focusFlashOpacity), lineWidth: 3) - .shadow(color: cmuxAccentColor().opacity(focusFlashOpacity * 0.35), radius: 10) + .stroke(programaAccentColor().opacity(focusFlashOpacity), lineWidth: 3) + .shadow(color: programaAccentColor().opacity(focusFlashOpacity * 0.35), radius: 10) .padding(FocusFlashPattern.ringInset) .allowsHitTesting(false) } @@ -71,7 +71,7 @@ struct MarkdownPanelView: View { // Rendered markdown Markdown(panel.content) - .markdownTheme(cmuxMarkdownTheme) + .markdownTheme(programaMarkdownTheme) .textSelection(.enabled) .padding(.horizontal, 24) .padding(.vertical, 16) @@ -123,7 +123,7 @@ struct MarkdownPanelView: View { : Color(nsColor: NSColor(white: 0.98, alpha: 1.0)) } - private var cmuxMarkdownTheme: Theme { + private var programaMarkdownTheme: Theme { let isDark = colorScheme == .dark return Theme() diff --git a/Sources/Panels/Panel.swift b/Sources/Panels/Panel.swift index fc0554dd942..35a92ce01e6 100644 --- a/Sources/Panels/Panel.swift +++ b/Sources/Panels/Panel.swift @@ -133,12 +133,12 @@ enum PanelOverlayRingMetrics { } #if DEBUG -func cmuxFlashDebugID(_ id: UUID?) -> String { +func programaFlashDebugID(_ id: UUID?) -> String { guard let id else { return "nil" } return String(id.uuidString.prefix(6)) } -func cmuxFlashDebugRect(_ rect: CGRect?) -> String { +func programaFlashDebugRect(_ rect: CGRect?) -> String { guard let rect else { return "nil" } return String( format: "%.1f,%.1f %.1fx%.1f", @@ -149,7 +149,7 @@ func cmuxFlashDebugRect(_ rect: CGRect?) -> String { ) } -func cmuxFlashDebugBool(_ value: Bool) -> Int { +func programaFlashDebugBool(_ value: Bool) -> Int { value ? 1 : 0 } #endif diff --git a/Sources/Panels/ProgramaWebView.swift b/Sources/Panels/ProgramaWebView.swift index 84a7f655ebc..86595925967 100644 --- a/Sources/Panels/ProgramaWebView.swift +++ b/Sources/Panels/ProgramaWebView.swift @@ -5,7 +5,7 @@ import UniformTypeIdentifiers import WebKit extension WKWebView { - var cmuxIsElementFullscreenActiveOrTransitioning: Bool { + var programaIsElementFullscreenActiveOrTransitioning: Bool { switch fullscreenState { case .notInFullscreen: return false @@ -16,8 +16,8 @@ extension WKWebView { } } - func cmuxIsManagedByExternalFullscreenWindow(relativeTo expectedWindow: NSWindow?) -> Bool { - guard cmuxIsElementFullscreenActiveOrTransitioning else { return false } + func programaIsManagedByExternalFullscreenWindow(relativeTo expectedWindow: NSWindow?) -> Bool { + guard programaIsElementFullscreenActiveOrTransitioning else { return false } guard let expectedWindow else { return true } return window !== expectedWindow } diff --git a/Sources/ProgramaApp.swift b/Sources/ProgramaApp.swift index 389ebede5ed..07979feb233 100644 --- a/Sources/ProgramaApp.swift +++ b/Sources/ProgramaApp.swift @@ -134,12 +134,12 @@ enum UITestLaunchManifest { } @main -struct cmuxApp: App { +struct programaApp: App { @StateObject private var tabManager: TabManager @StateObject private var notificationStore = TerminalNotificationStore.shared @StateObject private var sidebarState = SidebarState() @StateObject private var sidebarSelectionState = SidebarSelectionState() - @StateObject private var cmuxConfigStore = ProgramaConfigStore() + @StateObject private var programaConfigStore = ProgramaConfigStore() @StateObject private var keyboardShortcutSettingsObserver = KeyboardShortcutSettingsObserver.shared private let primaryWindowId = UUID() @AppStorage(AppearanceSettings.appearanceModeKey) private var appearanceMode = AppearanceSettings.defaultMode.rawValue @@ -342,18 +342,18 @@ struct cmuxApp: App { .environmentObject(notificationStore) .environmentObject(sidebarState) .environmentObject(sidebarSelectionState) - .environmentObject(cmuxConfigStore) + .environmentObject(programaConfigStore) .onAppear { #if DEBUG if ProcessInfo.processInfo.environment["PROGRAMA_UI_TEST_MODE"] == "1" { - UpdateLogStore.shared.append("ui test: cmuxApp onAppear") + UpdateLogStore.shared.append("ui test: programaApp onAppear") } #endif // Start the Unix socket controller for programmatic access updateSocketController() appDelegate.configure(tabManager: tabManager, notificationStore: notificationStore, sidebarState: sidebarState) - cmuxConfigStore.wireDirectoryTracking(tabManager: tabManager) - cmuxConfigStore.loadAll() + programaConfigStore.wireDirectoryTracking(tabManager: tabManager) + programaConfigStore.loadAll() applyAppearance() if ProcessInfo.processInfo.environment["PROGRAMA_UI_TEST_SHOW_SETTINGS"] == "1" { DispatchQueue.main.async { @@ -1127,7 +1127,7 @@ struct cmuxApp: App { private func closePanelOrWindow() { if let window = NSApp.keyWindow ?? NSApp.mainWindow, - cmuxWindowShouldOwnCloseShortcut(window) { + programaWindowShouldOwnCloseShortcut(window) { window.performClose(nil) return } @@ -1156,25 +1156,25 @@ struct cmuxApp: App { } } -private let cmuxAuxiliaryWindowIdentifiers: Set = [ +private let programaAuxiliaryWindowIdentifiers: Set = [ "cmux.settings", "cmux.about", - "cmux.licenses", - "cmux.browser-popup", - "cmux.settingsAboutTitlebarDebug", - "cmux.debugWindowControls", - "cmux.browserImportHintDebug", - "cmux.sidebarDebug", - "cmux.menubarDebug", - "cmux.backgroundDebug", + "programa.licenses", + "programa.browser-popup", + "programa.settingsAboutTitlebarDebug", + "programa.debugWindowControls", + "programa.browserImportHintDebug", + "programa.sidebarDebug", + "programa.menubarDebug", + "programa.backgroundDebug", ] /// Returns whether the given window should handle the standard close shortcut /// as a standalone auxiliary window instead of routing it through workspace or /// panel-close behavior. -func cmuxWindowShouldOwnCloseShortcut(_ window: NSWindow?) -> Bool { +func programaWindowShouldOwnCloseShortcut(_ window: NSWindow?) -> Bool { guard let identifier = window?.identifier?.rawValue else { return false } - return cmuxAuxiliaryWindowIdentifiers.contains(identifier) + return programaAuxiliaryWindowIdentifiers.contains(identifier) } private enum SettingsAboutWindowKind: String, CaseIterable, Identifiable { @@ -1454,7 +1454,7 @@ private final class SettingsAboutTitlebarDebugStore: ObservableObject { private func ensureToolbar(on window: NSWindow, kind: SettingsAboutWindowKind) { guard window.toolbar == nil else { return } - let identifier = NSToolbar.Identifier("cmux.debug.titlebar.\(kind.rawValue)") + let identifier = NSToolbar.Identifier("programa.debug.titlebar.\(kind.rawValue)") let toolbar = NSToolbar(identifier: identifier) toolbar.allowsUserCustomization = false toolbar.autosavesConfiguration = false @@ -1491,7 +1491,7 @@ private final class SettingsAboutTitlebarDebugWindowController: NSWindowControll window.titlebarAppearsTransparent = false window.isMovableByWindowBackground = true window.isReleasedWhenClosed = false - window.identifier = NSUserInterfaceItemIdentifier("cmux.settingsAboutTitlebarDebug") + window.identifier = NSUserInterfaceItemIdentifier("programa.settingsAboutTitlebarDebug") window.center() window.contentView = NSHostingView(rootView: SettingsAboutTitlebarDebugView()) AppDelegate.shared?.applyWindowDecorations(to: window) @@ -1719,7 +1719,7 @@ private final class DebugWindowControlsWindowController: NSWindowController, NSW window.titlebarAppearsTransparent = false window.isMovableByWindowBackground = true window.isReleasedWhenClosed = false - window.identifier = NSUserInterfaceItemIdentifier("cmux.debugWindowControls") + window.identifier = NSUserInterfaceItemIdentifier("programa.debugWindowControls") window.center() window.contentView = NSHostingView(rootView: DebugWindowControlsView()) AppDelegate.shared?.applyWindowDecorations(to: window) @@ -2023,7 +2023,7 @@ private final class BrowserImportHintDebugWindowController: NSWindowController, window.titlebarAppearsTransparent = false window.isMovableByWindowBackground = true window.isReleasedWhenClosed = false - window.identifier = NSUserInterfaceItemIdentifier("cmux.browserImportHintDebug") + window.identifier = NSUserInterfaceItemIdentifier("programa.browserImportHintDebug") window.center() window.contentView = NSHostingView(rootView: BrowserImportHintDebugView()) AppDelegate.shared?.applyWindowDecorations(to: window) @@ -2060,7 +2060,7 @@ private final class BrowserProfilePopoverDebugWindowController: NSWindowControll window.titlebarAppearsTransparent = false window.isMovableByWindowBackground = true window.isReleasedWhenClosed = false - window.identifier = NSUserInterfaceItemIdentifier("cmux.browserProfilePopoverDebug") + window.identifier = NSUserInterfaceItemIdentifier("programa.browserProfilePopoverDebug") window.center() window.contentView = NSHostingView(rootView: BrowserProfilePopoverDebugView()) AppDelegate.shared?.applyWindowDecorations(to: window) @@ -2458,7 +2458,7 @@ private final class AcknowledgmentsWindowController: NSWindowController, NSWindo ) window.isReleasedWhenClosed = false window.title = String(localized: "about.licenses.windowTitle", defaultValue: "Third-Party Licenses") - window.identifier = NSUserInterfaceItemIdentifier("cmux.licenses") + window.identifier = NSUserInterfaceItemIdentifier("programa.licenses") window.center() window.contentView = NSHostingView(rootView: AcknowledgmentsView()) super.init(window: window) @@ -2638,7 +2638,7 @@ enum SettingsNavigationTarget: String { } enum SettingsNavigationRequest { - static let notificationName = Notification.Name("cmux.settings.navigate") + static let notificationName = Notification.Name("programa.settings.navigate") private static let targetKey = "target" static func post(_ target: SettingsNavigationTarget) { @@ -2670,7 +2670,7 @@ private final class SidebarDebugWindowController: NSWindowController, NSWindowDe window.titlebarAppearsTransparent = false window.isMovableByWindowBackground = true window.isReleasedWhenClosed = false - window.identifier = NSUserInterfaceItemIdentifier("cmux.sidebarDebug") + window.identifier = NSUserInterfaceItemIdentifier("programa.sidebarDebug") window.center() window.contentView = NSHostingView(rootView: SidebarDebugView()) AppDelegate.shared?.applyWindowDecorations(to: window) @@ -2821,7 +2821,7 @@ private struct SidebarDebugView: View { if let hex = sidebarSelectionColorHex, let nsColor = NSColor(hex: hex) { return Color(nsColor: nsColor) } - return cmuxAccentColor() + return programaAccentColor() }, set: { newColor in let nsColor = NSColor(newColor) @@ -3100,7 +3100,7 @@ private final class MenuBarExtraDebugWindowController: NSWindowController, NSWin window.titlebarAppearsTransparent = false window.isMovableByWindowBackground = true window.isReleasedWhenClosed = false - window.identifier = NSUserInterfaceItemIdentifier("cmux.menubarDebug") + window.identifier = NSUserInterfaceItemIdentifier("programa.menubarDebug") window.center() window.contentView = NSHostingView(rootView: MenuBarExtraDebugView()) AppDelegate.shared?.applyWindowDecorations(to: window) @@ -3270,7 +3270,7 @@ private final class SplitButtonLayoutDebugWindowController: NSWindowController, window.titlebarAppearsTransparent = false window.isMovableByWindowBackground = true window.isReleasedWhenClosed = false - window.identifier = NSUserInterfaceItemIdentifier("cmux.splitButtonLayoutDebug") + window.identifier = NSUserInterfaceItemIdentifier("programa.splitButtonLayoutDebug") window.center() window.contentView = NSHostingView(rootView: SplitButtonLayoutDebugView()) AppDelegate.shared?.applyWindowDecorations(to: window) @@ -3340,7 +3340,7 @@ private final class BackgroundDebugWindowController: NSWindowController, NSWindo window.titlebarAppearsTransparent = false window.isMovableByWindowBackground = true window.isReleasedWhenClosed = false - window.identifier = NSUserInterfaceItemIdentifier("cmux.backgroundDebug") + window.identifier = NSUserInterfaceItemIdentifier("programa.backgroundDebug") window.center() window.contentView = NSHostingView(rootView: BackgroundDebugView()) AppDelegate.shared?.applyWindowDecorations(to: window) @@ -4096,8 +4096,8 @@ struct SettingsView: View { @AppStorage(TelemetrySettings.sendAnonymousTelemetryKey) private var sendAnonymousTelemetry = TelemetrySettings.defaultSendAnonymousTelemetry @AppStorage(PreferredEditorSettings.key) private var preferredEditorCommand = "" - @AppStorage("cmuxPortBase") private var cmuxPortBase = 9100 - @AppStorage("cmuxPortRange") private var cmuxPortRange = 10 + @AppStorage("cmuxPortBase") private var programaPortBase = 9100 + @AppStorage("cmuxPortRange") private var programaPortRange = 10 @AppStorage(BrowserSearchSettings.searchEngineKey) private var browserSearchEngine = BrowserSearchSettings.defaultSearchEngine.rawValue @AppStorage(BrowserSearchSettings.searchSuggestionsEnabledKey) private var browserSearchSuggestionsEnabled = BrowserSearchSettings.defaultSearchSuggestionsEnabled @AppStorage(BrowserThemeSettings.modeKey) private var browserThemeMode = BrowserThemeSettings.defaultMode.rawValue @@ -5142,7 +5142,7 @@ struct SettingsView: View { HexColorPicker( hex: sidebarSelectionColorHex, - fallback: cmuxAccentColor() + fallback: programaAccentColor() ) { newHex in sidebarSelectionColorHex = newHex } @@ -5171,7 +5171,7 @@ struct SettingsView: View { HexColorPicker( hex: sidebarNotificationBadgeColorHex, - fallback: cmuxAccentColor() + fallback: programaAccentColor() ) { newHex in sidebarNotificationBadgeColorHex = newHex } @@ -5440,7 +5440,7 @@ struct SettingsView: View { SettingsCard { SettingsCardRow(String(localized: "settings.automation.portBase", defaultValue: "Port Base"), subtitle: String(localized: "settings.automation.portBase.subtitle", defaultValue: "Starting port for PROGRAMA_PORT env var."), controlWidth: pickerColumnWidth) { - TextField("", value: $cmuxPortBase, format: .number) + TextField("", value: $programaPortBase, format: .number) .textFieldStyle(.roundedBorder) .multilineTextAlignment(.trailing) } @@ -5448,7 +5448,7 @@ struct SettingsView: View { SettingsCardDivider() SettingsCardRow(String(localized: "settings.automation.portRange", defaultValue: "Port Range Size"), subtitle: String(localized: "settings.automation.portRange.subtitle", defaultValue: "Number of ports per workspace."), controlWidth: pickerColumnWidth) { - TextField("", value: $cmuxPortRange, format: .number) + TextField("", value: $programaPortRange, format: .number) .textFieldStyle(.roundedBorder) .multilineTextAlignment(.trailing) } diff --git a/Sources/ProgramaDirectoryTrust.swift b/Sources/ProgramaDirectoryTrust.swift index c3656639f6f..797b242fa0a 100644 --- a/Sources/ProgramaDirectoryTrust.swift +++ b/Sources/ProgramaDirectoryTrust.swift @@ -6,7 +6,7 @@ import Foundation /// Global config (~/.config/programa/programa.json) is always trusted. final class ProgramaDirectoryTrust { static let shared = ProgramaDirectoryTrust() - static let didChangeNotification = Notification.Name("cmux.directoryTrustDidChange") + static let didChangeNotification = Notification.Name("programa.directoryTrustDidChange") private let storePath: String private var trustedPaths: Set diff --git a/Sources/SocketControlSettings.swift b/Sources/SocketControlSettings.swift index e2a7bd9d60d..2ae8626e49c 100644 --- a/Sources/SocketControlSettings.swift +++ b/Sources/SocketControlSettings.swift @@ -63,7 +63,7 @@ enum SocketControlMode: String, CaseIterable, Identifiable { enum SocketControlPasswordStore { static let directoryName = "programa" static let fileName = "socket-control-password" - static let didChangeNotification = Notification.Name("cmux.socketControlPasswordDidChange") + static let didChangeNotification = Notification.Name("programa.socketControlPasswordDidChange") private static let keychainMigrationDefaultsKey = "socketControlPasswordMigrationVersion" private static let keychainMigrationVersion = 1 private static let legacyKeychainService = "com.darkroom.programa.socket-control" diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index 336088ef1e5..bcb70766c0e 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -637,7 +637,7 @@ fileprivate final class VsyncIOSurfaceTimelineState { } } -fileprivate func cmuxVsyncIOSurfaceTimelineCallback( +fileprivate func programaVsyncIOSurfaceTimelineCallback( _ displayLink: CVDisplayLink, _ inNow: UnsafePointer, _ inOutputTime: UnsafePointer, @@ -5191,7 +5191,7 @@ class TabManager: ObservableObject { } st.link = link - CVDisplayLinkSetOutputCallback(link, cmuxVsyncIOSurfaceTimelineCallback, ctx) + CVDisplayLinkSetOutputCallback(link, programaVsyncIOSurfaceTimelineCallback, ctx) CVDisplayLinkStart(link) } @@ -5946,18 +5946,18 @@ enum ResizeDirection { } extension Notification.Name { - static let commandPaletteToggleRequested = Notification.Name("cmux.commandPaletteToggleRequested") - static let commandPaletteRequested = Notification.Name("cmux.commandPaletteRequested") - static let commandPaletteSwitcherRequested = Notification.Name("cmux.commandPaletteSwitcherRequested") - static let commandPaletteSubmitRequested = Notification.Name("cmux.commandPaletteSubmitRequested") - static let commandPaletteDismissRequested = Notification.Name("cmux.commandPaletteDismissRequested") - static let commandPaletteRenameTabRequested = Notification.Name("cmux.commandPaletteRenameTabRequested") - static let commandPaletteRenameWorkspaceRequested = Notification.Name("cmux.commandPaletteRenameWorkspaceRequested") - static let commandPaletteEditWorkspaceDescriptionRequested = Notification.Name("cmux.commandPaletteEditWorkspaceDescriptionRequested") - static let commandPaletteMoveSelection = Notification.Name("cmux.commandPaletteMoveSelection") - static let commandPaletteRenameInputInteractionRequested = Notification.Name("cmux.commandPaletteRenameInputInteractionRequested") - static let commandPaletteRenameInputDeleteBackwardRequested = Notification.Name("cmux.commandPaletteRenameInputDeleteBackwardRequested") - static let feedbackComposerRequested = Notification.Name("cmux.feedbackComposerRequested") + static let commandPaletteToggleRequested = Notification.Name("programa.commandPaletteToggleRequested") + static let commandPaletteRequested = Notification.Name("programa.commandPaletteRequested") + static let commandPaletteSwitcherRequested = Notification.Name("programa.commandPaletteSwitcherRequested") + static let commandPaletteSubmitRequested = Notification.Name("programa.commandPaletteSubmitRequested") + static let commandPaletteDismissRequested = Notification.Name("programa.commandPaletteDismissRequested") + static let commandPaletteRenameTabRequested = Notification.Name("programa.commandPaletteRenameTabRequested") + static let commandPaletteRenameWorkspaceRequested = Notification.Name("programa.commandPaletteRenameWorkspaceRequested") + static let commandPaletteEditWorkspaceDescriptionRequested = Notification.Name("programa.commandPaletteEditWorkspaceDescriptionRequested") + static let commandPaletteMoveSelection = Notification.Name("programa.commandPaletteMoveSelection") + static let commandPaletteRenameInputInteractionRequested = Notification.Name("programa.commandPaletteRenameInputInteractionRequested") + static let commandPaletteRenameInputDeleteBackwardRequested = Notification.Name("programa.commandPaletteRenameInputDeleteBackwardRequested") + static let feedbackComposerRequested = Notification.Name("programa.feedbackComposerRequested") static let ghosttyDidSetTitle = Notification.Name("ghosttyDidSetTitle") static let ghosttyDidFocusTab = Notification.Name("ghosttyDidFocusTab") static let ghosttyDidFocusSurface = Notification.Name("ghosttyDidFocusSurface") @@ -5969,6 +5969,6 @@ extension Notification.Name { static let browserDidFocusAddressBar = Notification.Name("browserDidFocusAddressBar") static let browserDidBlurAddressBar = Notification.Name("browserDidBlurAddressBar") static let webViewDidReceiveClick = Notification.Name("webViewDidReceiveClick") - static let terminalPortalVisibilityDidChange = Notification.Name("cmux.terminalPortalVisibilityDidChange") - static let browserPortalRegistryDidChange = Notification.Name("cmux.browserPortalRegistryDidChange") + static let terminalPortalVisibilityDidChange = Notification.Name("programa.terminalPortalVisibilityDidChange") + static let browserPortalRegistryDidChange = Notification.Name("programa.browserPortalRegistryDidChange") } diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index f76502e14d2..ae69a981703 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -5,12 +5,12 @@ import Bonsplit import WebKit extension Notification.Name { - static let socketListenerDidStart = Notification.Name("cmux.socketListenerDidStart") - static let terminalSurfaceDidBecomeReady = Notification.Name("cmux.terminalSurfaceDidBecomeReady") - static let terminalSurfaceHostedViewDidMoveToWindow = Notification.Name("cmux.terminalSurfaceHostedViewDidMoveToWindow") - static let mainWindowContextsDidChange = Notification.Name("cmux.mainWindowContextsDidChange") - static let browserDownloadEventDidArrive = Notification.Name("cmux.browserDownloadEventDidArrive") - static let reactGrabDidCopySelection = Notification.Name("cmux.reactGrabDidCopySelection") + static let socketListenerDidStart = Notification.Name("programa.socketListenerDidStart") + static let terminalSurfaceDidBecomeReady = Notification.Name("programa.terminalSurfaceDidBecomeReady") + static let terminalSurfaceHostedViewDidMoveToWindow = Notification.Name("programa.terminalSurfaceHostedViewDidMoveToWindow") + static let mainWindowContextsDidChange = Notification.Name("programa.mainWindowContextsDidChange") + static let browserDownloadEventDidArrive = Notification.Name("programa.browserDownloadEventDidArrive") + static let reactGrabDidCopySelection = Notification.Name("programa.reactGrabDidCopySelection") } /// Unix socket-based controller for programmatic terminal control diff --git a/Sources/TerminalImageTransfer.swift b/Sources/TerminalImageTransfer.swift index ce8473046e2..bcef09e1985 100644 --- a/Sources/TerminalImageTransfer.swift +++ b/Sources/TerminalImageTransfer.swift @@ -320,7 +320,7 @@ enum TerminalImageTransferPlanner { .map(escapeForShell) .joined(separator: " ") guard !content.isEmpty else { - onFailure(NSError(domain: "cmux.remote.drop", code: 5)) + onFailure(NSError(domain: "programa.remote.drop", code: 5)) return } insertText(content) diff --git a/Sources/TerminalNotificationStore.swift b/Sources/TerminalNotificationStore.swift index f63d8373ac3..22e5166c420 100644 --- a/Sources/TerminalNotificationStore.swift +++ b/Sources/TerminalNotificationStore.swift @@ -839,7 +839,7 @@ final class TerminalNotificationStore: ObservableObject { content.categoryIdentifier = Self.categoryIdentifier let request = UNNotificationRequest( - identifier: "cmux.settings.test.\(UUID().uuidString)", + identifier: "programa.settings.test.\(UUID().uuidString)", content: content, trigger: nil ) diff --git a/Sources/TerminalSSHSessionDetector.swift b/Sources/TerminalSSHSessionDetector.swift index ea111a35a3a..6bd75803fa1 100644 --- a/Sources/TerminalSSHSessionDetector.swift +++ b/Sources/TerminalSSHSessionDetector.swift @@ -92,7 +92,7 @@ struct DetectedSSHSession: Equatable { try operation.throwIfCancelled() let normalizedLocalURL = localURL.standardizedFileURL guard normalizedLocalURL.isFileURL else { - throw NSError(domain: "cmux.detected-ssh.drop", code: 1, userInfo: [ + throw NSError(domain: "programa.detected-ssh.drop", code: 1, userInfo: [ NSLocalizedDescriptionKey: "dropped item is not a file URL", ]) } @@ -107,7 +107,7 @@ struct DetectedSSHSession: Equatable { guard result.status == 0 else { let detail = Self.bestErrorLine(stderr: result.stderr, stdout: result.stdout) ?? "scp exited \(result.status)" - throw NSError(domain: "cmux.detected-ssh.drop", code: 2, userInfo: [ + throw NSError(domain: "programa.detected-ssh.drop", code: 2, userInfo: [ NSLocalizedDescriptionKey: "failed to upload dropped file: \(detail)", ]) } @@ -297,7 +297,7 @@ struct DetectedSSHSession: Equatable { throw TerminalImageTransferExecutionError.cancelled } terminateProcessAndWait() - throw NSError(domain: "cmux.detected-ssh.drop", code: 3, userInfo: [ + throw NSError(domain: "programa.detected-ssh.drop", code: 3, userInfo: [ NSLocalizedDescriptionKey: "scp timed out", ]) } diff --git a/Sources/TerminalWindowPortal.swift b/Sources/TerminalWindowPortal.swift index f491d340226..35cfa87d2b7 100644 --- a/Sources/TerminalWindowPortal.swift +++ b/Sources/TerminalWindowPortal.swift @@ -4,8 +4,8 @@ import ObjectiveC import Bonsplit #endif -private var cmuxWindowTerminalPortalKey: UInt8 = 0 -private var cmuxWindowTerminalPortalCloseObserverKey: UInt8 = 0 +private var programaWindowTerminalPortalKey: UInt8 = 0 +private var programaWindowTerminalPortalCloseObserverKey: UInt8 = 0 #if DEBUG private func portalDebugToken(_ view: NSView?) -> String { @@ -1864,7 +1864,7 @@ enum TerminalWindowPortalRegistry { } private static func installWindowCloseObserverIfNeeded(for window: NSWindow) { - guard objc_getAssociatedObject(window, &cmuxWindowTerminalPortalCloseObserverKey) == nil else { return } + guard objc_getAssociatedObject(window, &programaWindowTerminalPortalCloseObserverKey) == nil else { return } let windowId = ObjectIdentifier(window) let observer = NotificationCenter.default.addObserver( forName: NSWindow.willCloseNotification, @@ -1881,7 +1881,7 @@ enum TerminalWindowPortalRegistry { } objc_setAssociatedObject( window, - &cmuxWindowTerminalPortalCloseObserverKey, + &programaWindowTerminalPortalCloseObserverKey, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) @@ -1898,11 +1898,11 @@ enum TerminalWindowPortalRegistry { hostedToWindowId = hostedToWindowId.filter { $0.value != windowId } guard let window else { return } - if let observer = objc_getAssociatedObject(window, &cmuxWindowTerminalPortalCloseObserverKey) { + if let observer = objc_getAssociatedObject(window, &programaWindowTerminalPortalCloseObserverKey) { NotificationCenter.default.removeObserver(observer) } - objc_setAssociatedObject(window, &cmuxWindowTerminalPortalCloseObserverKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(window, &cmuxWindowTerminalPortalKey, nil, .OBJC_ASSOCIATION_RETAIN) + objc_setAssociatedObject(window, &programaWindowTerminalPortalCloseObserverKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(window, &programaWindowTerminalPortalKey, nil, .OBJC_ASSOCIATION_RETAIN) } private static func pruneHostedMappings(for windowId: ObjectIdentifier, validHostedIds: Set) { @@ -1912,21 +1912,21 @@ enum TerminalWindowPortalRegistry { } private static func portal(for window: NSWindow) -> WindowTerminalPortal { - if let existing = objc_getAssociatedObject(window, &cmuxWindowTerminalPortalKey) as? WindowTerminalPortal { + if let existing = objc_getAssociatedObject(window, &programaWindowTerminalPortalKey) as? WindowTerminalPortal { portalsByWindowId[ObjectIdentifier(window)] = existing installWindowCloseObserverIfNeeded(for: window) return existing } let portal = WindowTerminalPortal(window: window) - objc_setAssociatedObject(window, &cmuxWindowTerminalPortalKey, portal, .OBJC_ASSOCIATION_RETAIN) + objc_setAssociatedObject(window, &programaWindowTerminalPortalKey, portal, .OBJC_ASSOCIATION_RETAIN) portalsByWindowId[ObjectIdentifier(window)] = portal installWindowCloseObserverIfNeeded(for: window) return portal } private static func existingPortal(for window: NSWindow) -> WindowTerminalPortal? { - if let existing = objc_getAssociatedObject(window, &cmuxWindowTerminalPortalKey) as? WindowTerminalPortal { + if let existing = objc_getAssociatedObject(window, &programaWindowTerminalPortalKey) as? WindowTerminalPortal { portalsByWindowId[ObjectIdentifier(window)] = existing installWindowCloseObserverIfNeeded(for: window) return existing diff --git a/Sources/Update/UpdateController.swift b/Sources/Update/UpdateController.swift index e17404d7f15..1c251a2fc11 100644 --- a/Sources/Update/UpdateController.swift +++ b/Sources/Update/UpdateController.swift @@ -18,7 +18,7 @@ enum UpdateSettings { static let automaticallyUpdateKey = "SUAutomaticallyUpdate" static let scheduledCheckIntervalKey = "SUScheduledCheckInterval" static let sendProfileInfoKey = "SUSendProfileInfo" - static let migrationKey = "cmux.sparkle.automaticChecksMigration.v2" + static let migrationKey = "programa.sparkle.automaticChecksMigration.v2" static let previousDefaultScheduledCheckInterval: TimeInterval = 60 * 60 * 24 static let scheduledCheckInterval: TimeInterval = 60 * 60 @@ -263,7 +263,7 @@ class UpdateController { if case .checking = viewModel.state { viewModel.state = .error(.init( error: NSError( - domain: "cmux.update", + domain: "programa.update", code: 1, userInfo: [NSLocalizedDescriptionKey: "Updater is still starting. Try again in a moment."] ), diff --git a/Sources/Update/UpdateLogStore.swift b/Sources/Update/UpdateLogStore.swift index a7b7365fbbd..ef57459b16c 100644 --- a/Sources/Update/UpdateLogStore.swift +++ b/Sources/Update/UpdateLogStore.swift @@ -4,7 +4,7 @@ import AppKit final class UpdateLogStore { static let shared = UpdateLogStore() - private let queue = DispatchQueue(label: "cmux.update.log") + private let queue = DispatchQueue(label: "programa.update.log") private var entries: [String] = [] private let maxEntries = 200 private let logURL: URL @@ -15,7 +15,7 @@ final class UpdateLogStore { formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] let logsDir = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first ?? FileManager.default.temporaryDirectory - logURL = logsDir.appendingPathComponent("Logs/cmux-update.log") + logURL = logsDir.appendingPathComponent("Logs/programa-update.log") ensureLogFile() } @@ -67,7 +67,7 @@ final class UpdateLogStore { final class FocusLogStore { static let shared = FocusLogStore() - private let queue = DispatchQueue(label: "cmux.focus.log") + private let queue = DispatchQueue(label: "programa.focus.log") private var entries: [String] = [] private let maxEntries = 400 private let logURL: URL @@ -78,7 +78,7 @@ final class FocusLogStore { formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] let logsDir = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first ?? FileManager.default.temporaryDirectory - logURL = logsDir.appendingPathComponent("Logs/cmux-focus.log") + logURL = logsDir.appendingPathComponent("Logs/programa-focus.log") ensureLogFile() } diff --git a/Sources/Update/UpdateTestURLProtocol.swift b/Sources/Update/UpdateTestURLProtocol.swift index beb0779bb18..5bf3a25ecb1 100644 --- a/Sources/Update/UpdateTestURLProtocol.swift +++ b/Sources/Update/UpdateTestURLProtocol.swift @@ -4,7 +4,7 @@ import Foundation final class UpdateTestURLProtocol: URLProtocol { static let host = "cmux.test" static let appcastPath = "/appcast.xml" - static let updatePath = "/cmux-test.zip" + static let updatePath = "/programa-test.zip" private static var isRegistered = false private static let registrationLock = NSLock() diff --git a/Sources/Update/UpdateTitlebarAccessory.swift b/Sources/Update/UpdateTitlebarAccessory.swift index 5dac41a01c8..9b8f6c4f6c5 100644 --- a/Sources/Update/UpdateTitlebarAccessory.swift +++ b/Sources/Update/UpdateTitlebarAccessory.swift @@ -120,7 +120,7 @@ final class TitlebarControlsViewModel: ObservableObject { } extension Notification.Name { - static let cmuxNotificationsPopoverVisibilityDidChange = Notification.Name("cmux.notificationsPopoverVisibilityDidChange") + static let programaNotificationsPopoverVisibilityDidChange = Notification.Name("programa.notificationsPopoverVisibilityDidChange") } private enum NotificationsPopoverVisibilityUserInfoKey { @@ -129,7 +129,7 @@ private enum NotificationsPopoverVisibilityUserInfoKey { private func postNotificationsPopoverVisibilityDidChange(isShown: Bool) { NotificationCenter.default.post( - name: .cmuxNotificationsPopoverVisibilityDidChange, + name: .programaNotificationsPopoverVisibilityDidChange, object: nil, userInfo: [NotificationsPopoverVisibilityUserInfoKey.isShown: isShown] ) @@ -333,7 +333,7 @@ struct TitlebarControlsView: View { .onAppear { isNotificationsPopoverShown = AppDelegate.shared?.isNotificationsPopoverShown() ?? false } - .onReceive(NotificationCenter.default.publisher(for: .cmuxNotificationsPopoverVisibilityDidChange)) { notification in + .onReceive(NotificationCenter.default.publisher(for: .programaNotificationsPopoverVisibilityDidChange)) { notification in isNotificationsPopoverShown = (notification.userInfo?[NotificationsPopoverVisibilityUserInfoKey.isShown] as? Bool) ?? false } .onAppear { @@ -384,7 +384,7 @@ struct TitlebarControlsView: View { .foregroundColor(.white) .frame(width: config.badgeSize, height: config.badgeSize) .background( - Circle().fill(cmuxAccentColor()) + Circle().fill(programaAccentColor()) ) .offset(x: config.badgeOffset.width, y: config.badgeOffset.height) } @@ -1155,11 +1155,11 @@ private struct NotificationPopoverRow: View { Button(action: onOpen) { HStack(alignment: .top, spacing: 10) { Circle() - .fill(notification.isRead ? Color.clear : cmuxAccentColor()) + .fill(notification.isRead ? Color.clear : programaAccentColor()) .frame(width: 8, height: 8) .overlay( Circle() - .stroke(cmuxAccentColor().opacity(notification.isRead ? 0.2 : 1), lineWidth: 1) + .stroke(programaAccentColor().opacity(notification.isRead ? 0.2 : 1), lineWidth: 1) ) .padding(.top, 6) diff --git a/Sources/Update/UpdateViewModel.swift b/Sources/Update/UpdateViewModel.swift index ec41d83ea72..514763b755e 100644 --- a/Sources/Update/UpdateViewModel.swift +++ b/Sources/Update/UpdateViewModel.swift @@ -161,7 +161,7 @@ class UpdateViewModel: ObservableObject { var iconColor: Color { if showsDetectedBackgroundUpdate { - return cmuxAccentColor() + return programaAccentColor() } switch effectiveState { case .idle: @@ -171,7 +171,7 @@ class UpdateViewModel: ObservableObject { case .checking: return .secondary case .updateAvailable: - return cmuxAccentColor() + return programaAccentColor() case .downloading, .extracting, .installing: return .secondary case .notFound: @@ -183,13 +183,13 @@ class UpdateViewModel: ObservableObject { var backgroundColor: Color { if showsDetectedBackgroundUpdate { - return cmuxAccentColor() + return programaAccentColor() } switch effectiveState { case .permissionRequest: return Color(nsColor: NSColor.systemBlue.blended(withFraction: 0.3, of: .black) ?? .systemBlue) case .updateAvailable: - return cmuxAccentColor() + return programaAccentColor() case .notFound: return Color(nsColor: NSColor.systemBlue.blended(withFraction: 0.5, of: .black) ?? .systemBlue) case .error: diff --git a/Sources/WindowToolbarController.swift b/Sources/WindowToolbarController.swift index 9db9198c23a..7bee2d1a8cc 100644 --- a/Sources/WindowToolbarController.swift +++ b/Sources/WindowToolbarController.swift @@ -4,7 +4,7 @@ import SwiftUI @MainActor final class WindowToolbarController: NSObject, NSToolbarDelegate { - private let commandItemIdentifier = NSToolbarItem.Identifier("cmux.focusedCommand") + private let commandItemIdentifier = NSToolbarItem.Identifier("programa.focusedCommand") private weak var tabManager: TabManager? @@ -120,7 +120,7 @@ final class WindowToolbarController: NSObject, NSToolbarDelegate { private func attach(to window: NSWindow) { guard window.toolbar == nil else { return } guard !WorkspacePresentationModeSettings.isMinimal() else { return } - let toolbar = NSToolbar(identifier: NSToolbar.Identifier("cmux.toolbar")) + let toolbar = NSToolbar(identifier: NSToolbar.Identifier("programa.toolbar")) toolbar.delegate = self toolbar.displayMode = .iconOnly toolbar.sizeMode = .small diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 8f58b4a6a08..6c9c73d1932 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -57,7 +57,7 @@ struct ProgramaSurfaceConfigTemplate { } } -func cmuxSurfaceContextName(_ context: ghostty_surface_context_e) -> String { +func programaSurfaceContextName(_ context: ghostty_surface_context_e) -> String { switch context { case GHOSTTY_SURFACE_CONTEXT_WINDOW: return "window" @@ -70,7 +70,7 @@ func cmuxSurfaceContextName(_ context: ghostty_surface_context_e) -> String { } } -private func cmuxPointerAppearsLive(_ pointer: UnsafeMutableRawPointer?) -> Bool { +private func programaPointerAppearsLive(_ pointer: UnsafeMutableRawPointer?) -> Bool { guard let pointer, malloc_zone_from_ptr(pointer) != nil else { return false @@ -78,15 +78,15 @@ private func cmuxPointerAppearsLive(_ pointer: UnsafeMutableRawPointer?) -> Bool return malloc_size(pointer) > 0 } -func cmuxSurfacePointerAppearsLive(_ surface: ghostty_surface_t) -> Bool { +func programaSurfacePointerAppearsLive(_ surface: ghostty_surface_t) -> Bool { // Best-effort check: reject pointers that no longer belong to an active // malloc zone allocation. A Swift wrapper around `ghostty_surface_t` can // remain non-nil after the backing native surface has already been freed. - cmuxPointerAppearsLive(surface) + programaPointerAppearsLive(surface) } -func cmuxCurrentSurfaceFontSizePoints(_ surface: ghostty_surface_t) -> Float? { - guard cmuxSurfacePointerAppearsLive(surface) else { +func programaCurrentSurfaceFontSizePoints(_ surface: ghostty_surface_t) -> Float? { + guard programaSurfacePointerAppearsLive(surface) else { return nil } @@ -100,7 +100,7 @@ func cmuxCurrentSurfaceFontSizePoints(_ surface: ghostty_surface_t) -> Float? { return points } -func cmuxInheritedSurfaceConfig( +func programaInheritedSurfaceConfig( sourceSurface: ghostty_surface_t, context: ghostty_surface_context_e ) -> ProgramaSurfaceConfigTemplate { @@ -109,7 +109,7 @@ func cmuxInheritedSurfaceConfig( // Make runtime zoom inheritance explicit, even when Ghostty's // inherit-font-size config is disabled. - let runtimePoints = cmuxCurrentSurfaceFontSizePoints(sourceSurface) + let runtimePoints = programaCurrentSurfaceFontSizePoints(sourceSurface) if let points = runtimePoints { config.fontSize = points } @@ -119,7 +119,7 @@ func cmuxInheritedSurfaceConfig( let runtimeText = runtimePoints.map { String(format: "%.2f", $0) } ?? "nil" let finalText = String(format: "%.2f", config.fontSize) dlog( - "zoom.inherit context=\(cmuxSurfaceContextName(context)) " + + "zoom.inherit context=\(programaSurfaceContextName(context)) " + "inherited=\(inheritedText) runtime=\(runtimeText) final=\(finalText)" ) #endif @@ -1273,7 +1273,7 @@ private final class WorkspaceRemoteDaemonRPCClient { do { try process.run() } catch { - throw NSError(domain: "cmux.remote.daemon.rpc", code: 1, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 1, userInfo: [ NSLocalizedDescriptionKey: "Failed to launch SSH daemon transport: \(error.localizedDescription)", ]) } @@ -1295,7 +1295,7 @@ private final class WorkspaceRemoteDaemonRPCClient { let hello = try call(method: "hello", params: [:], timeout: 8.0) let capabilities = (hello["capabilities"] as? [String]) ?? [] guard capabilities.contains(Self.requiredProxyStreamCapability) else { - throw NSError(domain: "cmux.remote.daemon.rpc", code: 2, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 2, userInfo: [ NSLocalizedDescriptionKey: "remote daemon missing required capability \(Self.requiredProxyStreamCapability)", ]) } @@ -1321,7 +1321,7 @@ private final class WorkspaceRemoteDaemonRPCClient { ) let streamID = (result["stream_id"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" guard !streamID.isEmpty else { - throw NSError(domain: "cmux.remote.daemon.rpc", code: 3, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 3, userInfo: [ NSLocalizedDescriptionKey: "proxy.open missing stream_id", ]) } @@ -1346,7 +1346,7 @@ private final class WorkspaceRemoteDaemonRPCClient { ) throws { let trimmedStreamID = streamID.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmedStreamID.isEmpty else { - throw NSError(domain: "cmux.remote.daemon.rpc", code: 17, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 17, userInfo: [ NSLocalizedDescriptionKey: "proxy.stream.subscribe requires stream_id", ]) } @@ -1397,7 +1397,7 @@ private final class WorkspaceRemoteDaemonRPCClient { ]) } catch { pendingCalls.remove(pendingCall) - throw NSError(domain: "cmux.remote.daemon.rpc", code: 10, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 10, userInfo: [ NSLocalizedDescriptionKey: "failed to encode daemon RPC request \(method): \(error.localizedDescription)", ]) } @@ -1415,15 +1415,15 @@ private final class WorkspaceRemoteDaemonRPCClient { switch pendingCalls.wait(for: pendingCall, timeout: timeout) { case .timedOut: stop(suppressTerminationCallback: false) - throw NSError(domain: "cmux.remote.daemon.rpc", code: 11, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 11, userInfo: [ NSLocalizedDescriptionKey: "daemon RPC timeout waiting for \(method) response", ]) case .failure(let failure): - throw NSError(domain: "cmux.remote.daemon.rpc", code: 12, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 12, userInfo: [ NSLocalizedDescriptionKey: failure, ]) case .missing: - throw NSError(domain: "cmux.remote.daemon.rpc", code: 13, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 13, userInfo: [ NSLocalizedDescriptionKey: "daemon RPC \(method) returned empty response", ]) case .response(let pendingResponse): @@ -1438,7 +1438,7 @@ private final class WorkspaceRemoteDaemonRPCClient { let errorObject = (response["error"] as? [String: Any]) ?? [:] let code = (errorObject["code"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "rpc_error" let message = (errorObject["message"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "daemon RPC call failed" - throw NSError(domain: "cmux.remote.daemon.rpc", code: 14, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 14, userInfo: [ NSLocalizedDescriptionKey: "\(method) failed (\(code)): \(message)", ]) } @@ -1448,7 +1448,7 @@ private final class WorkspaceRemoteDaemonRPCClient { self.stdinHandle ?? FileHandle.nullDevice } if stdinHandle === FileHandle.nullDevice { - throw NSError(domain: "cmux.remote.daemon.rpc", code: 15, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 15, userInfo: [ NSLocalizedDescriptionKey: "daemon transport is not connected", ]) } @@ -1457,7 +1457,7 @@ private final class WorkspaceRemoteDaemonRPCClient { try stdinHandle.write(contentsOf: Data([0x0A])) } catch { stop(suppressTerminationCallback: false) - throw NSError(domain: "cmux.remote.daemon.rpc", code: 16, userInfo: [ + throw NSError(domain: "programa.remote.daemon.rpc", code: 16, userInfo: [ NSLocalizedDescriptionKey: "failed writing daemon RPC request: \(error.localizedDescription)", ]) } @@ -2098,7 +2098,7 @@ private final class WorkspaceRemoteDaemonProxyTunnel { let bytes = [UInt8](data) guard bytes.count >= 4 else { return nil } guard bytes[0] == 0x05 else { - throw NSError(domain: "cmux.remote.proxy", code: 1, userInfo: [NSLocalizedDescriptionKey: "invalid SOCKS version"]) + throw NSError(domain: "programa.remote.proxy", code: 1, userInfo: [NSLocalizedDescriptionKey: "invalid SOCKS version"]) } let command = bytes[1] @@ -2138,18 +2138,18 @@ private final class WorkspaceRemoteDaemonProxyTunnel { cursor += 16 default: - throw NSError(domain: "cmux.remote.proxy", code: 2, userInfo: [NSLocalizedDescriptionKey: "invalid SOCKS address type"]) + throw NSError(domain: "programa.remote.proxy", code: 2, userInfo: [NSLocalizedDescriptionKey: "invalid SOCKS address type"]) } guard !host.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { - throw NSError(domain: "cmux.remote.proxy", code: 3, userInfo: [NSLocalizedDescriptionKey: "empty SOCKS host"]) + throw NSError(domain: "programa.remote.proxy", code: 3, userInfo: [NSLocalizedDescriptionKey: "empty SOCKS host"]) } guard bytes.count >= cursor + 2 else { return nil } let port = Int(UInt16(bytes[cursor]) << 8 | UInt16(bytes[cursor + 1])) cursor += 2 guard port > 0 && port <= 65535 else { - throw NSError(domain: "cmux.remote.proxy", code: 4, userInfo: [NSLocalizedDescriptionKey: "invalid SOCKS port"]) + throw NSError(domain: "programa.remote.proxy", code: 4, userInfo: [NSLocalizedDescriptionKey: "invalid SOCKS port"]) } return SocksRequest(host: host, port: port, command: command, consumedBytes: cursor) @@ -2414,7 +2414,7 @@ private final class WorkspaceRemoteDaemonProxyTunnel { var capturedError: Error? queue.sync { guard !isStopped else { - capturedError = NSError(domain: "cmux.remote.proxy", code: 20, userInfo: [ + capturedError = NSError(domain: "programa.remote.proxy", code: 20, userInfo: [ NSLocalizedDescriptionKey: "proxy tunnel already stopped", ]) return @@ -2521,7 +2521,7 @@ private final class WorkspaceRemoteDaemonProxyTunnel { private static func makeLoopbackListener(port: Int) throws -> NWListener { guard let localPort = NWEndpoint.Port(rawValue: UInt16(port)) else { - throw NSError(domain: "cmux.remote.proxy", code: 21, userInfo: [ + throw NSError(domain: "programa.remote.proxy", code: 21, userInfo: [ NSLocalizedDescriptionKey: "invalid local proxy port \(port)", ]) } @@ -3035,7 +3035,7 @@ private final class WorkspaceRemoteCLIRelayServer { private static func roundTripUnixSocket(socketPath: String, request: Data) throws -> Data { let fd = socket(AF_UNIX, SOCK_STREAM, 0) guard fd >= 0 else { - throw NSError(domain: "cmux.remote.relay", code: 1, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 1, userInfo: [ NSLocalizedDescriptionKey: "failed to create local relay socket", ]) } @@ -3051,7 +3051,7 @@ private final class WorkspaceRemoteCLIRelayServer { address.sun_family = sa_family_t(AF_UNIX) let pathBytes = Array(socketPath.utf8CString) guard pathBytes.count <= MemoryLayout.size(ofValue: address.sun_path) else { - throw NSError(domain: "cmux.remote.relay", code: 2, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 2, userInfo: [ NSLocalizedDescriptionKey: "local relay socket path is too long", ]) } @@ -3070,7 +3070,7 @@ private final class WorkspaceRemoteCLIRelayServer { } } guard connectResult == 0 else { - throw NSError(domain: "cmux.remote.relay", code: 3, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 3, userInfo: [ NSLocalizedDescriptionKey: "failed to connect to local cmux socket", ]) } @@ -3082,7 +3082,7 @@ private final class WorkspaceRemoteCLIRelayServer { while bytesRemaining > 0 { let written = Darwin.write(fd, pointer, bytesRemaining) if written <= 0 { - throw NSError(domain: "cmux.remote.relay", code: 4, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 4, userInfo: [ NSLocalizedDescriptionKey: "failed to write relay request", ]) } @@ -3108,11 +3108,11 @@ private final class WorkspaceRemoteCLIRelayServer { if !response.isEmpty { break } - throw NSError(domain: "cmux.remote.relay", code: 5, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 5, userInfo: [ NSLocalizedDescriptionKey: "timed out waiting for local cmux response", ]) } - throw NSError(domain: "cmux.remote.relay", code: 6, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 6, userInfo: [ NSLocalizedDescriptionKey: "failed to read local cmux response", ]) } @@ -3132,7 +3132,7 @@ private final class WorkspaceRemoteCLIRelayServer { init(localSocketPath: String, relayID: String, relayTokenHex: String) throws { guard let relayToken = Session.hexData(from: relayTokenHex), !relayToken.isEmpty else { - throw NSError(domain: "cmux.remote.relay", code: 7, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 7, userInfo: [ NSLocalizedDescriptionKey: "invalid relay token", ]) } @@ -3185,7 +3185,7 @@ private final class WorkspaceRemoteCLIRelayServer { listener.newConnectionHandler = nil listener.stateUpdateHandler = nil listener.cancel() - throw NSError(domain: "cmux.remote.relay", code: 8, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 8, userInfo: [ NSLocalizedDescriptionKey: "timed out waiting for local relay listener", ]) } @@ -3199,7 +3199,7 @@ private final class WorkspaceRemoteCLIRelayServer { listener.newConnectionHandler = nil listener.stateUpdateHandler = nil listener.cancel() - throw NSError(domain: "cmux.remote.relay", code: 8, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 8, userInfo: [ NSLocalizedDescriptionKey: "failed to bind local relay listener", ]) } @@ -3530,7 +3530,7 @@ final class WorkspaceRemoteSessionController { do { let hello = try bootstrapDaemonLocked() guard hello.capabilities.contains(WorkspaceRemoteDaemonRPCClient.requiredProxyStreamCapability) else { - throw NSError(domain: "cmux.remote.daemon", code: 43, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 43, userInfo: [ NSLocalizedDescriptionKey: "remote daemon missing required capability \(WorkspaceRemoteDaemonRPCClient.requiredProxyStreamCapability)", ]) } @@ -4195,7 +4195,7 @@ final class WorkspaceRemoteSessionController { let stdoutHandle = stdoutPipe.fileHandleForReading let stderrHandle = stderrPipe.fileHandleForReading - let captureQueue = DispatchQueue(label: "cmux.remote.process.capture") + let captureQueue = DispatchQueue(label: "programa.remote.process.capture") let exitSemaphore = DispatchSemaphore(value: 0) var stdoutData = Data() var stderrData = Data() @@ -4230,7 +4230,7 @@ final class WorkspaceRemoteSessionController { "remote.proc.launchFailed exec=\(URL(fileURLWithPath: executable).lastPathComponent) " + "error=\(error.localizedDescription)" ) - throw NSError(domain: "cmux.remote.process", code: 1, userInfo: [ + throw NSError(domain: "programa.remote.process", code: 1, userInfo: [ NSLocalizedDescriptionKey: "Failed to launch \(URL(fileURLWithPath: executable).lastPathComponent): \(error.localizedDescription)", ]) } @@ -4268,7 +4268,7 @@ final class WorkspaceRemoteSessionController { "remote.proc.timeout exec=\(URL(fileURLWithPath: executable).lastPathComponent) " + "timeout=\(Int(timeout)) args=\(debugShellCommand(executable: executable, arguments: arguments))" ) - throw NSError(domain: "cmux.remote.process", code: 2, userInfo: [ + throw NSError(domain: "programa.remote.process", code: 2, userInfo: [ NSLocalizedDescriptionKey: "\(URL(fileURLWithPath: executable).lastPathComponent) timed out after \(Int(timeout))s", ]) } @@ -4375,7 +4375,7 @@ final class WorkspaceRemoteSessionController { let result = try sshExec(arguments: sshCommonArguments(batchMode: true) + [configuration.destination, command], timeout: 8) guard result.status == 0 else { let detail = Self.bestErrorLine(stderr: result.stderr, stdout: result.stdout) ?? "ssh exited \(result.status)" - throw NSError(domain: "cmux.remote.relay", code: 70, userInfo: [ + throw NSError(domain: "programa.remote.relay", code: 70, userInfo: [ NSLocalizedDescriptionKey: "failed to install remote relay metadata: \(detail)", ]) } @@ -4439,14 +4439,14 @@ final class WorkspaceRemoteSessionController { .map { String($0.dropFirst(Self.remotePlatformProbeArchMarker.count)) } guard let unameOS, let unameArch else { let detail = Self.bestErrorLine(stderr: result.stderr, stdout: result.stdout) ?? "ssh exited \(result.status)" - throw NSError(domain: "cmux.remote.daemon", code: 11, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 11, userInfo: [ NSLocalizedDescriptionKey: "failed to query remote platform: \(detail)", ]) } guard let goOS = Self.mapUnameOS(unameOS), let goArch = Self.mapUnameArch(unameArch) else { - throw NSError(domain: "cmux.remote.daemon", code: 12, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 12, userInfo: [ NSLocalizedDescriptionKey: "unsupported remote platform \(unameOS)/\(unameArch)", ]) } @@ -4455,7 +4455,7 @@ final class WorkspaceRemoteSessionController { .map { String($0.dropFirst(Self.remotePlatformProbeExistsMarker.count)) == "yes" } if result.status != 0, binaryExists == nil { let detail = Self.bestErrorLine(stderr: result.stderr, stdout: result.stdout) ?? "ssh exited \(result.status)" - throw NSError(domain: "cmux.remote.daemon", code: 13, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 13, userInfo: [ NSLocalizedDescriptionKey: "failed to query remote daemon state: \(detail)", ]) } @@ -4557,7 +4557,7 @@ final class WorkspaceRemoteSessionController { private func downloadRemoteDaemonBinaryLocked(entry: WorkspaceRemoteDaemonManifest.Entry, version: String, releaseURL: String? = nil) throws -> URL { guard let url = URL(string: entry.downloadURL) else { - throw NSError(domain: "cmux.remote.daemon", code: 25, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 25, userInfo: [ NSLocalizedDescriptionKey: "remote daemon manifest has an invalid download URL", ]) } @@ -4582,7 +4582,7 @@ final class WorkspaceRemoteSessionController { } if let httpResponse = response as? HTTPURLResponse, !(200...299).contains(httpResponse.statusCode) { - downloadError = NSError(domain: "cmux.remote.daemon", code: 26, userInfo: [ + downloadError = NSError(domain: "programa.remote.daemon", code: 26, userInfo: [ NSLocalizedDescriptionKey: "remote daemon download failed with HTTP \(httpResponse.statusCode)", ]) return @@ -4596,7 +4596,7 @@ final class WorkspaceRemoteSessionController { throw downloadError } guard let downloadedURL else { - throw NSError(domain: "cmux.remote.daemon", code: 27, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 27, userInfo: [ NSLocalizedDescriptionKey: "remote daemon download did not produce a file", ]) } @@ -4613,7 +4613,7 @@ final class WorkspaceRemoteSessionController { downloadedSHA == liveEntry.sha256.lowercased() { debugLog("remote.download.checksum-fallback: embedded manifest checksum stale, live manifest matched for \(entry.assetName)") } else { - throw NSError(domain: "cmux.remote.daemon", code: 28, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 28, userInfo: [ NSLocalizedDescriptionKey: "remote daemon checksum mismatch for \(entry.assetName)", ]) } @@ -4655,25 +4655,25 @@ final class WorkspaceRemoteSessionController { } guard Self.allowLocalDaemonBuildFallback() else { - throw NSError(domain: "cmux.remote.daemon", code: 20, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 20, userInfo: [ NSLocalizedDescriptionKey: "this build does not include a verified programad-remote manifest for \(goOS)-\(goArch). Use a release/nightly build, or set PROGRAMA_REMOTE_DAEMON_ALLOW_LOCAL_BUILD=1 for a dev-only fallback.", ]) } guard let repoRoot = Self.findRepoRoot() else { - throw NSError(domain: "cmux.remote.daemon", code: 20, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 20, userInfo: [ NSLocalizedDescriptionKey: "cannot locate cmux repo root for dev-only programad-remote build fallback", ]) } let daemonRoot = repoRoot.appendingPathComponent("daemon/remote", isDirectory: true) let goModPath = daemonRoot.appendingPathComponent("go.mod").path guard FileManager.default.fileExists(atPath: goModPath) else { - throw NSError(domain: "cmux.remote.daemon", code: 21, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 21, userInfo: [ NSLocalizedDescriptionKey: "missing daemon module at \(goModPath)", ]) } guard let goBinary = Self.which("go") else { - throw NSError(domain: "cmux.remote.daemon", code: 22, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 22, userInfo: [ NSLocalizedDescriptionKey: "go is required for the dev-only programad-remote build fallback", ]) } @@ -4696,12 +4696,12 @@ final class WorkspaceRemoteSessionController { ) guard result.status == 0 else { let detail = Self.bestErrorLine(stderr: result.stderr, stdout: result.stdout) ?? "go build failed with status \(result.status)" - throw NSError(domain: "cmux.remote.daemon", code: 23, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 23, userInfo: [ NSLocalizedDescriptionKey: "failed to build programad-remote: \(detail)", ]) } guard FileManager.default.isExecutableFile(atPath: output.path) else { - throw NSError(domain: "cmux.remote.daemon", code: 24, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 24, userInfo: [ NSLocalizedDescriptionKey: "programad-remote build output is not executable", ]) } @@ -4721,7 +4721,7 @@ final class WorkspaceRemoteSessionController { let mkdirResult = try sshExec(arguments: sshCommonArguments(batchMode: true) + [configuration.destination, mkdirCommand], timeout: 12) guard mkdirResult.status == 0 else { let detail = Self.bestErrorLine(stderr: mkdirResult.stderr, stdout: mkdirResult.stdout) ?? "ssh exited \(mkdirResult.status)" - throw NSError(domain: "cmux.remote.daemon", code: 30, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 30, userInfo: [ NSLocalizedDescriptionKey: "failed to create remote daemon directory: \(detail)", ]) } @@ -4746,7 +4746,7 @@ final class WorkspaceRemoteSessionController { let scpResult = try scpExec(arguments: scpArgs, timeout: 45) guard scpResult.status == 0 else { let detail = Self.bestErrorLine(stderr: scpResult.stderr, stdout: scpResult.stdout) ?? "scp exited \(scpResult.status)" - throw NSError(domain: "cmux.remote.daemon", code: 31, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 31, userInfo: [ NSLocalizedDescriptionKey: "failed to upload programad-remote: \(detail)", ]) } @@ -4759,7 +4759,7 @@ final class WorkspaceRemoteSessionController { let finalizeResult = try sshExec(arguments: sshCommonArguments(batchMode: true) + [configuration.destination, finalizeCommand], timeout: 12) guard finalizeResult.status == 0 else { let detail = Self.bestErrorLine(stderr: finalizeResult.stderr, stdout: finalizeResult.stdout) ?? "ssh exited \(finalizeResult.status)" - throw NSError(domain: "cmux.remote.daemon", code: 32, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 32, userInfo: [ NSLocalizedDescriptionKey: "failed to install remote daemon binary: \(detail)", ]) } @@ -4836,7 +4836,7 @@ final class WorkspaceRemoteSessionController { let result = try sshExec(arguments: sshCommonArguments(batchMode: true) + [configuration.destination, command], timeout: 12) guard result.status == 0 else { let detail = Self.bestErrorLine(stderr: result.stderr, stdout: result.stdout) ?? "ssh exited \(result.status)" - throw NSError(domain: "cmux.remote.daemon", code: 40, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 40, userInfo: [ NSLocalizedDescriptionKey: "failed to start remote daemon: \(detail)", ]) } @@ -4848,7 +4848,7 @@ final class WorkspaceRemoteSessionController { guard !responseLine.isEmpty, let data = responseLine.data(using: .utf8), let payload = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { - throw NSError(domain: "cmux.remote.daemon", code: 41, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 41, userInfo: [ NSLocalizedDescriptionKey: "remote daemon hello returned invalid JSON", ]) } @@ -4862,7 +4862,7 @@ final class WorkspaceRemoteSessionController { } return "hello call failed" }() - throw NSError(domain: "cmux.remote.daemon", code: 42, userInfo: [ + throw NSError(domain: "programa.remote.daemon", code: 42, userInfo: [ NSLocalizedDescriptionKey: "remote daemon hello failed: \(errorMessage)", ]) } @@ -5551,7 +5551,7 @@ final class WorkspaceRemoteSessionController { ) guard result.status == 0 else { let detail = Self.bestErrorLine(stderr: result.stderr, stdout: result.stdout) ?? "ssh exited \(result.status)" - throw NSError(domain: "cmux.remote.ports", code: 90, userInfo: [ + throw NSError(domain: "programa.remote.ports", code: 90, userInfo: [ NSLocalizedDescriptionKey: "remote port scan failed: \(detail)", ]) } @@ -5646,7 +5646,7 @@ final class WorkspaceRemoteSessionController { ) guard result.status == 0 else { let detail = Self.bestErrorLine(stderr: result.stderr, stdout: result.stdout) ?? "ssh exited \(result.status)" - throw NSError(domain: "cmux.remote.ports", code: 90, userInfo: [ + throw NSError(domain: "programa.remote.ports", code: 90, userInfo: [ NSLocalizedDescriptionKey: "remote port scan failed: \(detail)", ]) } @@ -8764,7 +8764,7 @@ final class Workspace: Identifiable, ObservableObject { sourceSurface: ghostty_surface_t, inheritedConfig: ProgramaSurfaceConfigTemplate ) -> Float? { - let runtimePoints = cmuxCurrentSurfaceFontSizePoints(sourceSurface) + let runtimePoints = programaCurrentSurfaceFontSizePoints(sourceSurface) if let rooted = terminalInheritanceFontPointsByPanelId[terminalPanel.id], rooted > 0 { if let runtimePoints, abs(runtimePoints - rooted) > 0.05 { // Runtime zoom changed after lineage was seeded (manual zoom on descendant); @@ -8783,7 +8783,7 @@ final class Workspace: Identifiable, ObservableObject { lastTerminalConfigInheritancePanelId = terminalPanel.id if terminalPanel.surface.isSurfaceLive, let sourceSurface = terminalPanel.surface.surface, - let runtimePoints = cmuxCurrentSurfaceFontSizePoints(sourceSurface) { + let runtimePoints = programaCurrentSurfaceFontSizePoints(sourceSurface) { let existing = terminalInheritanceFontPointsByPanelId[terminalPanel.id] if existing == nil || abs((existing ?? runtimePoints) - runtimePoints) > 0.05 { terminalInheritanceFontPointsByPanelId[terminalPanel.id] = runtimePoints @@ -8883,11 +8883,11 @@ final class Workspace: Identifiable, ObservableObject { // Pin the panel and its TerminalSurface wrapper for the duration of // this iteration. The raw ghostty_surface_t extracted below is owned // by `surface` (the TerminalSurface) — ARC must not release it while - // ghostty_surface_inherited_config or cmuxCurrentSurfaceFontSizePoints + // ghostty_surface_inherited_config or programaCurrentSurfaceFontSizePoints // is still reading through the pointer. let surface = terminalPanel.surface guard let sourceSurface = surface.surface else { continue } - var config = cmuxInheritedSurfaceConfig( + var config = programaInheritedSurfaceConfig( sourceSurface: sourceSurface, context: GHOSTTY_SURFACE_CONTEXT_SPLIT ) From a44fc31bea3caebafb5f4f95fdfb6ced86c897af Mon Sep 17 00:00:00 2001 From: arzafran Date: Thu, 2 Jul 2026 12:17:48 -0300 Subject: [PATCH 23/23] docs: mark 6d/6f-functional/cosmetic done; rebrand functionally complete --- plans/rebrand-progress.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/plans/rebrand-progress.md b/plans/rebrand-progress.md index 579a9b2ec82..a9b9c4b5f06 100644 --- a/plans/rebrand-progress.md +++ b/plans/rebrand-progress.md @@ -29,7 +29,23 @@ ci-fix. **macos-26 compat timeout** 45→60m (cold DerivedData cache on slow run **PR: #48** — CI green modulo `tests-build-and-lag` (known runner flake) + `compat-tests(macos-26)` (now 60m). -## Remaining (each = its own build-gated commit; runtime = CI/manual) +6d. **shell-integration** ✅ — scripts renamed + `cmux_*`→`programa_*` protocol lockstep (scripts + embedded-shell Swift + CLI). Env vars were already `PROGRAMA_*`. Analytics events preserved. +6f-func. **CLI functional** ✅ — bundled CLI resolves/installs as `programa` (was a pre-existing bin/cmux mismatch), `${_BIN:-cmux}`→`programa`, repo URLs, `/tmp/cmux*`→`/tmp/programa*` socket/hint paths. +6-cosmetic. **internal identifiers** ✅ — ~90 `cmuxXxx` symbols + ~60 dotted `cmux.*` literals across 30 files; verified via build-for-testing (app + test targets). + +**Rebrand is functionally + user-visibly COMPLETE.** App/CLI run as programa, all UI + config + prefs migrated. + +## Remaining follow-ups (non-blocking; app works as-is) +- **Test-coupled identifiers** — `SocketControlMode.cmuxOnly` (rawValue `"cmuxonly"`), `cmux*ForTesting` helpers, + `"cmux.main"`/`"cmux.settings"`/`"cmux.about"`/`"cmux.titlebarControls"` keys, `-cmuxUITestLaunchManifest`, + `"cmux DEV"` prefix, `cmux.test` feed host — all hard-coded in `programaTests`/`programaUITests`/`tests_v2`. + Need a coordinated app+test rename in lockstep. +- **CLI temp/session strings** — `cmux-ssh-startup-*` (asserted in `test_ssh_remote_cli_metadata.py`), + `cmux-claude-teams`/`cmux-omo` shim session names, etc. — `tests_v2`-coupled. +- **`cmux-relay-auth`** — JSON handshake shared with the Go remote daemon (`daemon/remote/`); needs both sides. +- **`com.cmux.*` dispatch-queue labels** — optional (invisible). + +## Original remaining (superseded — see above) ### 6d — shell-integration lockstep (LARGE) - Files: `Resources/shell-integration/cmux-{bash,zsh}-integration.{bash,zsh}` (~100+ `cmux_*` shell