Area
apps/web (plus a small packages/contracts client-settings addition)
Problem or use case
"Open in" remembers exactly one editor across every project. The choice lives in a single t3code:last-editor localStorage key, and picking anything from the dropdown overwrites it globally.
That breaks down the moment different projects want different apps. My iOS project wants Xcode, my web projects want Cursor, and a docs repo wants Finder. Today they fight over one slot: opening the iOS project rewrites the preference, and the next time I hit Open on a web project it launches Xcode. There is also no way to say "this is my normal editor" separately from "this is what I opened last", so a single one-off open silently becomes the new global default.
Proposed solution
Split the one sticky value into two levels, the same shape sidebarProjectGroupingMode / sidebarProjectGroupingOverrides already uses:
defaultEditor — a global default, set in Settings → General ("Open in"). null means "first editor the environment reports", so a fresh install still opens something.
projectEditorOverrides — optional per-project pin, keyed by physical project key (environmentId + normalized workspace root), so it matches the key sidebar grouping overrides already use.
Resolution: project override → global default → first available editor. An override or default the environment no longer reports falls through rather than leaving a dead Open button.
UX:
- Picking an app in the
OpenInPicker dropdown opens it and pins it to the current project. The plain Open button no longer rewrites the preference — only an explicit pick does.
- When a project has an override, the menu grows a
Use default (VS Code) entry that opens in the default and clears the pin.
- The sidebar's project settings dialog gets an "Open in" select alongside "Grouping rule", with
Use global default (X).
- The project's app also applies to file links opened from the diff panel, chat markdown, terminal links, and git actions. The diagnostics logs folder and
keybindings.json stay on the global default, since they are not project files.
Both fields go in client settings, so there is no DB migration. The existing t3code:last-editor value is read as the fallback default so nobody loses their current editor, and is dropped the first time a new default is set.
Why this matters
Anyone driving more than one kind of repo from T3 Code hits this daily — mixed-language monorepos, mobile plus web, or a docs repo you want to open in Finder. The current behavior is not just missing a feature; it actively loses your choice every time you open a different project.
Smallest useful scope
The per-project override alone would solve the problem, with the existing sticky value continuing to act as the default. The Settings row is the part that makes the default explicit rather than implicit, and could land second.
Alternatives considered
- Keep the dropdown global and put the override only in the project settings dialog. Less surprising for existing users, but it makes pinning a project a two-click detour into a dialog most people never open. Happy to go this way if maintainers prefer it — it is the main open question here.
- Store the override on the project record server-side so it syncs across clients. It does sync better (the editor launches on the environment, not the client), but it needs a SQLite migration and RPC changes for a preference that is cheap to re-set. Client settings felt like the right first cut.
- Per-environment rather than per-project. Wrong axis — one machine hosts many projects that want different apps.
Risks or tradeoffs
- Behavior change to an existing control. A dropdown pick now pins the project instead of moving the global default. That is the deliberate part of the proposal, but it is a change for existing users, which is why I am opening this before a PR.
- Client-settings storage means the preference does not follow you to another client hitting the same server.
- Picking the app that already matches the inherited default still writes an explicit override. That is intentional (it pins the project against later default changes), but it is a subtle distinction.
Examples or references
I have this implemented and running on a fork: contracts fields, a tested pure resolver, the picker, the Settings row, and the project dialog field. Web/contracts/desktop/mobile typecheck clean with focused tests on the resolution rules. Happy to open the PR against main if the shape above sounds right — or to adjust the dropdown semantics first.
Contribution
Yes, I would like to implement this — the work is already done and can be sent as a PR.
Area
apps/web (plus a small
packages/contractsclient-settings addition)Problem or use case
"Open in" remembers exactly one editor across every project. The choice lives in a single
t3code:last-editorlocalStorage key, and picking anything from the dropdown overwrites it globally.That breaks down the moment different projects want different apps. My iOS project wants Xcode, my web projects want Cursor, and a docs repo wants Finder. Today they fight over one slot: opening the iOS project rewrites the preference, and the next time I hit Open on a web project it launches Xcode. There is also no way to say "this is my normal editor" separately from "this is what I opened last", so a single one-off open silently becomes the new global default.
Proposed solution
Split the one sticky value into two levels, the same shape
sidebarProjectGroupingMode/sidebarProjectGroupingOverridesalready uses:defaultEditor— a global default, set in Settings → General ("Open in").nullmeans "first editor the environment reports", so a fresh install still opens something.projectEditorOverrides— optional per-project pin, keyed by physical project key (environmentId+ normalized workspace root), so it matches the key sidebar grouping overrides already use.Resolution: project override → global default → first available editor. An override or default the environment no longer reports falls through rather than leaving a dead Open button.
UX:
OpenInPickerdropdown opens it and pins it to the current project. The plain Open button no longer rewrites the preference — only an explicit pick does.Use default (VS Code)entry that opens in the default and clears the pin.Use global default (X).keybindings.jsonstay on the global default, since they are not project files.Both fields go in client settings, so there is no DB migration. The existing
t3code:last-editorvalue is read as the fallback default so nobody loses their current editor, and is dropped the first time a new default is set.Why this matters
Anyone driving more than one kind of repo from T3 Code hits this daily — mixed-language monorepos, mobile plus web, or a docs repo you want to open in Finder. The current behavior is not just missing a feature; it actively loses your choice every time you open a different project.
Smallest useful scope
The per-project override alone would solve the problem, with the existing sticky value continuing to act as the default. The Settings row is the part that makes the default explicit rather than implicit, and could land second.
Alternatives considered
Risks or tradeoffs
Examples or references
I have this implemented and running on a fork: contracts fields, a tested pure resolver, the picker, the Settings row, and the project dialog field. Web/contracts/desktop/mobile typecheck clean with focused tests on the resolution rules. Happy to open the PR against
mainif the shape above sounds right — or to adjust the dropdown semantics first.Contribution
Yes, I would like to implement this — the work is already done and can be sent as a PR.