Skip to content

feat(desktop): browser profiles for the preview browser - #7254

Merged
juliusmarminge merged 35 commits into
mainfrom
browser-profiles
Sep 2, 2026
Merged

feat(desktop): browser profiles for the preview browser#7254
juliusmarminge merged 35 commits into
mainfrom
browser-profiles

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Aug 16, 2026

Copy link
Copy Markdown
Member

Adds profiles to the desktop preview browser so tabs can keep separate cookies and site data. This is the bottom of the remaining stack; the browser-access setting is already merged.

Default preserves existing login storage. Incognito uses an in-memory partition. Other profiles use a separate partition namespace with unambiguous environment/profile scoping. A tab keeps its chosen profile for its lifetime.

Settings supports creating, renaming, choosing a default, and removing profiles. Removal waits for cookie/cache cleanup across all known environments and retains the profile if cleanup fails. Existing tabs remain open after removal and are labeled “Removed profile.” The Browser menu supports opening a chosen profile, including touch input.

Validation: focused profile, preview, settings-hydration, and partition-isolation tests; web/desktop typechecks; scoped formatting and lint. Latest CI results are shown below. Earlier desktop behavior was verified by the maintainer; this audit did not run another GUI pass.

Compatibility: Default partitions are unchanged. Data created in custom profiles by an earlier unmerged version of this stack is not migrated from the collision-prone partition mapping.

Original implementation: Claude Code. Review fixes: GPT-5.6 Sol agents, coordinated through Codex.

Note

Add browser profiles for the preview browser with per-profile partition isolation

  • Adds browser profile contracts in browserProfile.ts with built-in Default and Incognito profiles, plus user-defined persistent profiles capped at a contract maximum
  • Extends BrowserSession partition derivation in BrowserSession.ts to support persistent, ephemeral, and profile-namespaced partitions, with escaping that prevents collisions from lone UTF-16 surrogates and backslash sequences
  • Adds profile management and default-profile selection UI in IntegrationsSettings.tsx; removing a user profile clears its cookies and cache across all environments first
  • Propagates profileId through preview open, navigate, and status snapshots in Manager.ts, through webview config caching keyed by environment+profile in previewWebviewConfigState.ts, and through all desktop IPC handlers in preview.ts
  • Surfaces a browser-profile submenu in RightPanelTabs.tsx where mouse clicks open the default profile directly and touch opens the submenu for selection
  • Risk: getPartition in BrowserSession.ts changes partition format for non-default profiles using a JSON tuple scope; existing sessions opened before this change remain on the legacy bare-environment persistent partition, and default-profile partitions are kept compatible with that legacy format

Macroscope summarized 1a5aa85.


Note

Medium Risk
Changes Electron partition derivation and cookie/cache clearing semantics; default profile behavior is preserved, but wrong scoping could leak or wipe the wrong profile’s storage.

Overview
Adds browser profiles so preview tabs can use separate Chromium session partitions (cookies, cache, logins). Built-in Default keeps the legacy bare-environment partition; Incognito uses a non-persistent partition; custom profiles get a namespaced partition with collision-safe scope encoding.

Contracts & server: New profile types and client settings (browserProfiles, browserDefaultProfileId). Preview open/snapshots carry optional profileId through navigate and status updates so a tab does not silently switch partitions.

Desktop: resolvePartitionScope maps environment + profile to partition scope; getPreviewConfig and clear-cookie/cache IPC take environmentId and optional profileId, with per-profile clears loading the session first so data is actually removed after restart.

Web: Settings to add/rename/remove profiles and pick a default (incognito excluded as default). New tabs and link/file opens send configured profileId; webview config is cached per environment+profile. UI: profile submenu on “Add browser,” profile badge in preview chrome, and profile-scoped clear actions in the more menu.

Note: Default partitions stay compatible; custom profiles from an earlier partition scheme are not migrated per the PR description.

Reviewed by Cursor Bugbot for commit 1a5aa85. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 904d0a95-7510-4028-b553-9dcda458780c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 16, 2026
@github-actions github-actions Bot added the size:XL 500-999 changed lines (additions + deletions). label Aug 16, 2026
Comment thread apps/desktop/src/ipc/methods/preview.ts Outdated
Comment thread apps/web/src/browser/previewWebviewConfigState.ts
Comment thread apps/web/src/components/preview/PreviewMoreMenu.tsx
@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.2 KiB 13.3 KiB +64 B (+0.5%) 15.1 KiB
Codex Thread snapshot wire 6.9 KiB 6.9 KiB +6 B (+0.1%) 7.3 KiB
Codex Live turn WebSocket wire 6.3 KiB 6.4 KiB +58 B (+0.9%) 7.8 KiB
Codex Live turn WebSocket decoded 55.5 KiB 55.6 KiB +88 B (+0.2%) 66.4 KiB
Codex Live turn messages 8 10 +2 (+25.0%) 21
Claude Total thread wire 13.3 KiB 13.3 KiB −29 B (−0.2%) 15.1 KiB
Claude Thread snapshot wire 6.9 KiB 6.9 KiB −9 B (−0.1%) 7.3 KiB
Claude Live turn WebSocket wire 6.4 KiB 6.4 KiB −20 B (−0.3%) 7.8 KiB
Claude Live turn WebSocket decoded 56.4 KiB 56.4 KiB −44 B (−0.1%) 66.4 KiB
Claude Live turn messages 10 9 −1 (−10.0%) 21

Baseline: 28ddaf7 · PR result: 1a5aa85 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the web UI changes (RightPanelTabs, PreviewChromeRow/PreviewView, PreviewMoreMenu, IntegrationsSettings, menu.tsx) against the shared primitive contracts. Two consistency findings, both inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/IntegrationsSettings.tsx Outdated
Comment thread apps/web/src/components/ui/menu.tsx Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review August 16, 2026 21:36
Comment thread apps/web/src/components/preview/PreviewView.tsx Outdated
Comment thread apps/desktop/src/ipc/methods/preview.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial cross-layer browser-profile capability with new persistent/ephemeral storage partitions, profile-aware webview configuration, settings CRUD, and destructive profile-data cleanup. It also adds a configurable default profile for new tabs, so the resulting product-default and data-isolation behavior requires human review.

No code changes detected at 1a5aa85. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@github-actions github-actions Bot added the 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. label Aug 16, 2026
Comment thread packages/contracts/src/browserProfile.ts
Comment thread packages/contracts/src/browserProfile.ts Outdated
Comment thread apps/web/src/components/settings/IntegrationsSettings.tsx
Comment thread apps/web/src/browser/openFileInPreview.ts Outdated
Comment thread apps/web/src/components/RightPanelTabs.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings in the changed web UI. The MenuSubTrigger icon treatment added in this revision fixes the alignment of the new Browser sub-trigger, but the selector it uses also matches the trailing chevron on the two existing icon-less sub-triggers and overrides their ms-auto.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ui/menu.tsx Outdated
Comment thread apps/web/src/components/preview/PreviewView.tsx Outdated
Comment thread apps/web/src/components/settings/IntegrationsSettings.tsx
Comment thread apps/web/src/components/settings/IntegrationsSettings.tsx
Comment thread apps/web/src/components/settings/IntegrationsSettings.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding on the new profile badge in the preview chrome row: the truncate cap doesn't actually ellipsize because Badge is a flex container. Everything else in scope (the MenuSubTrigger icon-column fix, the MenuGroup/MenuGroupLabel wrapping in PreviewMoreMenu, the profile list rows reusing SettingsRow/ITEM_ROW_INNER_CLASSNAME/DraftInput/Button/AlertDialog, and the Select for the default profile) follows the existing primitives and settings conventions.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/preview/PreviewView.tsx Outdated
@github-actions github-actions Bot removed the 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. label Aug 17, 2026
Comment thread packages/contracts/src/ipc.ts
Comment thread apps/web/src/components/settings/IntegrationsSettings.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One convention finding: the new browserProfile module (and its test) uses the consolidated import { Schema } from "effect" form instead of the subpath namespace import used by the rest of packages/contracts. Everything else in the diff (service interfaces on BrowserSession/PreviewManager, Schema.TaggedErrorClass errors with structural attributes, make/layer shape, dependency acquisition) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread packages/contracts/src/browserProfile.ts Outdated
Comment thread packages/contracts/src/browserProfile.test.ts Outdated
Comment thread apps/web/src/components/settings/IntegrationsSettings.tsx
juliusmarminge and others added 21 commits September 2, 2026 14:34
… picker

The default-profile popup rendered user-supplied names bare, so a 48-character
name widened it to fit. Capped and truncated to match the tab menu's profile
submenu, so the same name ellipsizes in both pickers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The earlier cap missed on two counts: `truncate` sat on an inline span
inside `ItemText`'s block, where overflow rules never apply, so long names
scrolled instead of ellipsizing; and `max-w-64` styled the list rather than
the glass shell, which still matched the trigger width and left empty chrome
beside the list when the trigger was full-width. The label is now a block,
the cap applies to the shell as well, and the trigger width is not matched —
the same shape as the keybinding pickers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Dropping the trigger-width match left the popup purely content-sized — about 80px under a 176px trigger with only "Default" listed. The list now fills the shell, so the shell stays at least the trigger's width (and at most 16rem), matching every other settings select.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The partition digest hashed `TextEncoder().encode(scope)`, which replaces a
lone UTF-16 surrogate with U+FFFD — so two supported ids differing only by
such a unit (`"p\ud800"` vs `"p�"`) landed in the same partition and
shared every cookie and site storage. Lone surrogates are now escaped to
`\uXXXX` before hashing, with a literal backslash doubled so the escape
cannot be forged. Every well-formed scope passes through byte-for-byte
unchanged, so existing partitions — and the logins in them — stay put.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
>
{addSurfaceActions.map((action) => {
const Icon = action.icon;
// Browser collapses into one row: clicking the trigger opens

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium components/RightPanelTabs.tsx:937

When the panel has no surfaces, users cannot open Incognito or a custom browser profile directly; RightPanelEmptyState only invokes onAddBrowser, which always creates the default-profile tab. The profile submenu is mounted only when props.surfaces.length > 0, so users must first create an unwanted default tab to reach it. Make the profile choices available from the empty-panel launcher as well.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/RightPanelTabs.tsx around line 937:

When the panel has no surfaces, users cannot open Incognito or a custom browser profile directly; `RightPanelEmptyState` only invokes `onAddBrowser`, which always creates the default-profile tab. The profile submenu is mounted only when `props.surfaces.length > 0`, so users must first create an unwanted default tab to reach it. Make the profile choices available from the empty-panel launcher as well.

@juliusmarminge
juliusmarminge merged commit 134d510 into main Sep 2, 2026
27 checks passed
@juliusmarminge
juliusmarminge deleted the browser-profiles branch September 2, 2026 21:56
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254
* refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272
* feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830
* feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277
* fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112
* feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808
* feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276
* fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281
* fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279
* Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284
* fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280
* fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283
* fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285
* fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188
* fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180
* fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254
* fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287
* feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292
* chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297


**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 4, 2026
…icons, file tree diff, panel animations, vite-plus 0.3.0)

Brings the fork up to upstream/main 5b8445b. Highlights: customizable project icons (pingdotgg#9137), file tree in diff/PR tab (pingdotgg#9330), opt-in panel animations (pingdotgg#8830), browser profiles for preview (pingdotgg#7254), unified file/media previews (pingdotgg#9253), vite-plus 0.3.0 (pingdotgg#9327) + tailwind/lightningcss dedupe (pingdotgg#9331), environment machine icons (pingdotgg#9299/pingdotgg#9365).

Conflict resolutions: keep-both for pnpm-workspace (upstream dedupe + fork allowBuilds); pnpm-lock regenerated via pnpm install --lockfile-only (node@24) to union upstream upgrades with fork infra/mt-teams workspace; strip stray '<' prefixes the merge left on 7 fork lines (Migrations 041, RpcAuthorization stacks, AssetAccess/CursorProvider imports, Cursor --force test, GitHub CLI detail).

Fork guard script OK.
aorwall added a commit to aorwall/t3code that referenced this pull request Sep 5, 2026
Merges 137 upstream commits (`d937e3075..36c4e9c`) into the fork,
following the `fork-upstream-merge` skill. Landed as a merge commit;
conflicts resolved by the inventory's cached verdicts.

## Merge shape

816 files landed (`git diff --stat HEAD^1 HEAD`) against 811 in the
upstream range; fork delta 650 files. The gap of five is all in landed
and not the range: two fork-owned files touched during resolution
(`sandboxControl.placement.test.tsx` test-prop fixup,
`useSandboxCommandsBanner.tsx` reformat) plus the three fork docs this
merge writes (`inventory.json`, `gaps.md`, `upstream-merge-log.md`). No
upstream work was dropped.

## Conflicts

Resolved by inventory verdict — `AGENTS.md` (kept the fork's slimmed
shape, added Antigravity to the provider list), `contracts/rpc.ts` and
`environment.ts`, the ChatView/MessagesTimeline thread-fork +
message-origin convergence, the preview cluster (`addBrowserSurface`
`profileId` beside the fork's `url`, `rightPanelStore` `openAttachment`
beside `retargetFile`), the settings gates, and the two upstream pingdotgg#9364
test deletions.

Two latent fork bugs surfaced and were fixed: `PreviewView`'s hosted
annotation handler called an unexported helper, now routed through the
exported `capturePreviewAnnotationScreenshot` wrapper (matching the
sibling native handler); and `ChatView` passed a `configuredUrls` prop
the fork's hosted `PreviewPanel` does not accept.

## Unsupported methods

`unsupported-methods.mjs` reported ADD 10; recomputed to 0 by adding
`UnsupportedMethodError` to the shared `ProviderSetupRpcError` union
(nine `provider.auth.*` / `provider.install.*` methods) and to
`server.refreshUsageRates`. `gaps.md` grown with a _Provider setup_
bullet and a `refreshUsageRates` clause on _Usage summary_. The three
DROP entries (`scripts.run`, `subtasks.list`, `threads.getShell`) are
the documented keep-anyway exceptions — `apps/server` still refuses
them.

## Feature classification

**Usable as-is** (UI only, no backend dependency): mod+w tab close
(pingdotgg#9363), PageUp/PageDown chat nav (pingdotgg#9315), diff/PR file tree (pingdotgg#9330),
diff-header copy path (pingdotgg#2403), error-report copy (pingdotgg#9166), opt-in
context-window indicator (pingdotgg#9190), opt-in panel animations (pingdotgg#8830),
proactive panels (pingdotgg#9276), button press feedback (pingdotgg#9349), provider-editor
redesign chrome (pingdotgg#8508).

**Unsupported in Moatless** (resolve to a refusal): Antigravity provider
auth/install and all `provider.*` setup (pingdotgg#9348, pingdotgg#8508),
`server.refreshUsageRates`. Desktop-only and already capability- or
desktop-gated, so no new fork work: preview browser profiles (pingdotgg#7254),
open-links-in-app (pingdotgg#9339), ssh-host suggestions (pingdotgg#9171),
environment-as-machine (pingdotgg#9299), continue-threads-across-restart (pingdotgg#9167,
rides the new `serverUpdateThreadContinuation` capability).

**Backend behavior to reproduce** if Moatless wants it: project icons
(pingdotgg#9137, migration 047), auto-pull clean default branches (pingdotgg#9277,
migration 045), inline citations (pingdotgg#9146, needs the backend to emit
them), the usage page. Migration 046
(RepairAutomaticSettlementTimestamps) is upstream-server-only.

Net-zero: context compaction (pingdotgg#8808) landed and was reverted (pingdotgg#9284).

## Verification

`fmt:check`, `lint`, `typecheck`, `tripwires` and `inventory-check`
green. Full test suite green — one web test failed initially
(`addBrowserSurface.test.ts` did not expect the converged default
`profileId`), fixed and re-run.

Two `verify.mjs` checks exit non-zero and are the caveated machine
failures: `unsupported-methods` on the three documented DROP exceptions
above, and `duplicate-adds` on three confirmed false positives
(`openPreview` in a `ChatView` object literal vs its deps array; three
distinct `it()` blocks in `addBrowserSurface.test.ts`).

Written by Claude Opus 5 in Claude Code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---
Moatless task:
https://moatless.soaplabstest.com/tasks/4e881239-73d1-4a93-9563-6a926e920b42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant