Land remaining unmerged branch work: single-instance follow-ups and browser extensions - #323
Merged
Merged
Conversation
…nce-arbitration-2026-08-26
# Conflicts: # CHANGELOG.md
…concept) The browser can now run Chrome-style Web Extensions (manifest v2/v3) through WebKit's WKWebExtension API (macOS 15.4+; on older macOS the browser simply has no extensions). Extensions are read from ~/.config/programa/extensions/ — one unpacked directory or .zip per extension — and their content scripts inject into main panels and popups alike via the shared configuration path. Deliberately narrow scope for this slice: - Every requested permission and host pattern is granted up front; the extensions directory is trusted input until a consent UI exists. Grant expiration dates must be distantFuture — the dictionary values are expirations, and a "now" timestamp silently expires the grant before first use. - Extension storage persists under a fixed controller identifier. - No toolbar/popup/options UI and no WKWebExtensionTab/Window adapters yet, so extension APIs that enumerate tabs see none. Those are the next slices toward running 1Password. Verified: a manifest v3 test extension's content script injects into https pages in both a browser panel and a window.open popup. (cherry picked from commit ead7d4f)
Extensions inject dynamically through tabs.query + scripting.executeScript (1Password's autofill works exactly this way), and both APIs resolve against WKWebExtensionTab/Window adapters. Without them an extension sees a browser with zero tabs and can inject into nothing. - BrowserExtensionTabAdapter wraps a BrowserPanel; a single BrowserExtensionWindowAdapter presents the app as one flat window whose tabs are all live browser panels across workspaces. Popup windows are not represented yet (their content scripts still run). - Lifecycle: register on bindWebView, unregister in BrowserPanel.close, activate via the applyTabSelection funnel (didFocusPane lands there too, so one hook covers pane focus and tab selection). - The controller delegate's openWindowsFor answer is what initially populates each context's openWindows/openTabs — without a delegate the world starts empty and tabs.query returns [] forever, regardless of didOpenTab notifications. Cost a debug cycle; documented. - Both WebKit protocols are fully @optional and a near-miss Swift signature is silently ignored; the "nearly matches" compiler warning and the tabs-poc runtime probe (PR #284) are the safety nets. The warning already caught one (indexInWindow wants Int, not UInt). Verified: a manifest v3 test extension's background worker enumerates all tabs and stamps every page via scripting.executeScript. 1Password still needs its action popup UI (extension sign-in) before it decorates fields — next slice. (cherry picked from commit 94f9f58)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Collects the work that was still sitting on old branches so those branches can be deleted.
feat/browser-extensions): load unpacked extensions in the built-in browser and expose browser panels to them as tabs and windows.Not included, on purpose
reland/ghostty-occluded-render(Aug 5): pins a staleghosttysubmodule SHA; needs a fresh reland against the current fork, not a cherry-pick.fix/provider-usage-handshake-sizing: draft that fix: make provider usage controls compact and current #320 rewrote; every pick conflicts with the shipped version.browser-red-proof-prereqs,fix/escrow-orphan-reconciliation-307,fix/safe-area-loop,followup/sidebar-drag-snapshots-2026-08-26: already on main by content.tmp/rendering-fixes-integration: a single Aug 4 merge commit with nothing discrete to pick.Review order
Sources/AppDelegate.swiftsingle-instance section andprogramaTests/AppDelegateShortcutRoutingTests.swift.Sources/Panels/.Test plan