Skip to content

feat(desktop): register t3:// OS protocol handler for thread deep links - #2424

Closed
davidmashburn wants to merge 3 commits into
pingdotgg:mainfrom
davidmashburn:feat/t3-deep-link-protocol
Closed

feat(desktop): register t3:// OS protocol handler for thread deep links#2424
davidmashburn wants to merge 3 commits into
pingdotgg:mainfrom
davidmashburn:feat/t3-deep-link-protocol

Conversation

@davidmashburn

@davidmashburn davidmashburn commented Apr 30, 2026

Copy link
Copy Markdown

What Changed

Adds a t3://thread/<threadId> OS-level protocol handler to the Electron desktop app.

  • apps/desktop/src/main.ts: registers open-url (macOS) and second-instance (Win/Linux) listeners before whenReady; handleDeepLink() parses the URL and sends desktop:open-thread to the renderer via webContents.send; pendingDeepLinkThreadId queues deep links that arrive before the window finishes loading; setAsDefaultProtocolClient('t3') called in whenReady
  • apps/desktop/src/preload.ts: exposes onOpenThread over the context bridge (same pattern as onMenuAction)
  • packages/contracts/src/ipc.ts: adds onOpenThread to the DesktopBridge interface
  • apps/web/src/components/AppSidebarLayout.tsx: subscribes to onOpenThread and navigates to /$environmentId/$threadId using TanStack Router
  • scripts/build-desktop-artifact.ts: adds protocols: [{name:"T3 Code", schemes:["t3"]}] to the mac electron-builder config so CFBundleURLTypes is written into the packaged Info.plist
  • test mocks (localApi.test.ts, SettingsPanels.browser.tsx): stub onOpenThread: () => () => {} added

Why

Clicking a thread link (e.g. from Slack or the terminal) currently opens a browser tab that hits the pairing screen. With this change, t3://thread/<id> links route directly to the correct thread inside the already-running desktop app — or launch it if it's closed.

Verified end-to-end in dev: open "t3://thread/<id>" in the terminal fired the open-url event, handleDeepLink parsed it correctly, and webContents.send("desktop:open-thread", threadId) was confirmed in the Electron main-process logs.

UI Changes

N/A — no visual changes. Navigation behavior only.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

High Risk
Packaged alpha builds change bundle id from com.t3tools.t3code to com.t3tools.t3code.alpha, which can break in-place auto-updates and alter which app macOS routes t3:// links to; protocol registration and second-instance argv handling are security-sensitive entry points.

Overview
Adds t3://thread/<threadId> handling so OS links open the right thread in the desktop app instead of a browser.

A new DesktopDeepLinks service registers open-url / second-instance early, parses URLs via threadDeepLink, registers the t3 protocol (macOS pins setAsDefaultProtocolClient to the running executable), and calls DesktopWindow.openThread, which IPCs desktop:open-thread to the renderer (with pending IDs until the main window loads). The web shell listens through desktopBridge.onOpenThread and navigates to /$environmentId/$threadId, queuing the thread id until an environment id is known.

Alpha / nightly / dev builds now get distinct bundle ids via resolveDesktopAppBundleId (alpha uses com.t3tools.t3code.alpha instead of com.t3tools.t3code), macOS/electron-builder protocol lists include t3, and local artifact builds set publish: null so electron-builder does not auto-detect GitHub publish from GH_TOKEN.

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

Note

Register t3:// protocol handler for thread deep links in desktop app

  • Adds DesktopDeepLinks service that wires Electron open-url, second-instance, and argv events to parse t3://thread/<id> URLs and open the requested thread via DesktopWindow.openThread
  • Adds deep link parsing utilities in threadDeepLink.ts and a new desktop:open-thread IPC channel so the main process can tell the renderer to navigate to a thread
  • Renderer-side integration in AppSidebarLayout.tsx subscribes to onOpenThread and navigates to /$environmentId/$threadId, deferring until an environment id is available
  • Build config in build-desktop-artifact.ts now uses channel-specific app IDs (nightly vs alpha) and registers the t3 scheme on macOS; local builds set publish = null to disable auto-detection
  • Behavioral Change: production APP_BUNDLE_ID changes from com.t3tools.t3code to com.t3tools.t3code.alpha in electron-launcher.mjs; Windows appUserModelId now varies by channel via resolveDesktopAppBundleId in updateChannels.ts

Macroscope summarized edd18aa.

@coderabbitai

coderabbitai Bot commented Apr 30, 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: 2a79ffcc-4c7a-4b9e-9c70-ba42e65c36c2

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 30, 2026
Comment thread apps/desktop/src/main.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new OS protocol handler feature with cross-cutting changes to desktop lifecycle, IPC, and frontend navigation. Multiple high-severity unresolved findings identify fundamental issues including cold-start timing races, missing Linux support, and potential passkey signing breakage that require human attention.

Not approved because:

  • 7 blocking correctness issues found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@wturon

wturon commented Jun 10, 2026

Copy link
Copy Markdown

Trying to integrate with the T3 code app and deep linking (the ability to focus specific chats programmatically specifically) would be a big help.

@davidmashburn

Copy link
Copy Markdown
Author

Branch update: rebased onto current main and squashed into one commit (75a2103). The April implementation is replaced with the Effect-based DesktopDeepLinks path plus renderer-side pending-thread queueing (see reply on the Bugbot cold-start thread).

No server/orchestration changes in this revision — those are split out to #3642.

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Jul 7, 2026
Comment thread apps/web/src/components/AppSidebarLayout.tsx
void navigate({
to: "/$environmentId/$threadId",
params: { environmentId, threadId },
});

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.

Deep link ignores thread environment

Medium Severity

onOpenThread navigates using only the active or primary environment id, not the environment that owns the thread. The codebase already exposes findThreadRef(threadId) for cross-environment lookup, but the new handler never uses it, so links to threads in another environment can land on / after the thread route check fails.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 75a2103. Configure here.

Comment thread apps/desktop/src/window/DesktopWindow.ts
Comment thread apps/desktop/src/window/DesktopWindow.ts
Comment thread apps/desktop/src/app/DesktopDeepLinks.ts

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

export function resolveDesktopBuildAppId(version: string): string {

resolveDesktopBuildAppId now returns distinct appId values for the alpha and nightly channels, so both builds can be installed side-by-side. However, both channels still share the same Electron userData path (.../.t3/userdata/t3code), so they read and write the same profile, settings, and update-metadata files. Running both builds simultaneously causes them to overwrite each other's persisted state and auto-update data. Consider making the userDataDirName (or the stateDir) channel-specific — e.g. t3code-nightly for nightly builds — so each channel gets an isolated data directory.

🤖 Copy this AI Prompt to have your agent fix this:
In file @scripts/build-desktop-artifact.ts around line 1314:

`resolveDesktopBuildAppId` now returns distinct `appId` values for the `alpha` and `nightly` channels, so both builds can be installed side-by-side. However, both channels still share the same Electron `userData` path (`.../.t3/userdata/t3code`), so they read and write the same profile, settings, and update-metadata files. Running both builds simultaneously causes them to overwrite each other's persisted state and auto-update data. Consider making the `userDataDirName` (or the `stateDir`) channel-specific — e.g. `t3code-nightly` for nightly builds — so each channel gets an isolated data directory.

void runPromise(handleDeepLinkUrl(url));
});

yield* electronApp.on(

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 app/DesktopDeepLinks.ts:78

The second-instance handler is registered in registerEarly before the single-instance lock is acquired, so a t3:// link opened during startup on Windows/Linux launches a second app process instead of routing the URL to the starting instance. registerEarly attaches the second-instance listener early, but the lock (requestSingleInstanceLock) is only taken later during configure, leaving a window where the second-instance event is never emitted and the deep link is lost. Consider registering the second-instance listener only after the single-instance lock has been acquired, or document the startup ordering if this gap is intentional.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/desktop/src/app/DesktopDeepLinks.ts around line 78:

The `second-instance` handler is registered in `registerEarly` before the single-instance lock is acquired, so a `t3://` link opened during startup on Windows/Linux launches a second app process instead of routing the URL to the starting instance. `registerEarly` attaches the `second-instance` listener early, but the lock (`requestSingleInstanceLock`) is only taken later during `configure`, leaving a window where the second-instance event is never emitted and the deep link is lost. Consider registering the `second-instance` listener only after the single-instance lock has been acquired, or document the startup ordering if this gap is intentional.


yield* appIdentity.configure;
yield* lifecycle.register;
yield* deepLinks.registerEarly;

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 app/DesktopApp.ts:243

deepLinks.registerEarly is called after shellEnvironment.installIntoProcess, resolveUserDataPath, electronApp.setPath, desktopSettings.load, appIdentity.configure, and lifecycle.register. On a macOS cold start, Electron emits the open-url event very early — before this point — so a t3://... launch that triggers the initial open-url is dropped and the app opens without navigating to the deep link. Consider registering the open-url listener before these async initialization steps.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/desktop/src/app/DesktopApp.ts around line 243:

`deepLinks.registerEarly` is called after `shellEnvironment.installIntoProcess`, `resolveUserDataPath`, `electronApp.setPath`, `desktopSettings.load`, `appIdentity.configure`, and `lifecycle.register`. On a macOS cold start, Electron emits the `open-url` event very early — before this point — so a `t3://...` launch that triggers the initial `open-url` is dropped and the app opens without navigating to the deep link. Consider registering the `open-url` listener before these async initialization steps.

});
}

const launchThreadId = findThreadDeepLinkInArgv(process.argv);

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 app/DesktopDeepLinks.ts:106

On startup, configure re-parses process.argv and treats any preserved t3://... argument as a fresh deep link. The app's own relaunch path restarts packaged builds with args: process.argv.slice(1), so if the app was originally launched from a t3:// deep link, subsequent relaunches (for example WSL/server-exposure changes) carry the stale URL forward and reopen that old thread unexpectedly. Consider checking only argv that was not carried over from a prior launch (for example the OS-provided launch args from the first instance), or stripping known deep-link arguments before relaunching.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/desktop/src/app/DesktopDeepLinks.ts around line 106:

On startup, `configure` re-parses `process.argv` and treats any preserved `t3://...` argument as a fresh deep link. The app's own relaunch path restarts packaged builds with `args: process.argv.slice(1)`, so if the app was originally launched from a `t3://` deep link, subsequent relaunches (for example WSL/server-exposure changes) carry the stale URL forward and reopen that old thread unexpectedly. Consider checking only argv that was not carried over from a prior launch (for example the OS-provided launch args from the first instance), or stripping known deep-link arguments before relaunching.

ipcRenderer.removeListener(IpcChannels.MENU_ACTION_CHANNEL, wrappedListener);
};
},
onOpenThread: (listener) => {

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.

🟠 High src/preload.ts:119

The onOpenThread listener is registered during useEffect in AppSidebarLayout, which runs after the renderer has loaded. The main process emits the thread-open event on did-finish-load, so a t3://thread/... deep link opened during cold start fires before this listener exists and is permanently dropped. Consider having the main process buffer early thread-open events (or replay the last one) so a late-subscribing renderer still receives them.

Also found in 1 other location(s)

apps/web/src/components/AppSidebarLayout.tsx:101

AppSidebarLayout registers window.desktopBridge.onOpenThread inside useEffect, so the listener is attached only after the initial React commit. On a cold start from a t3://thread/... link, the main process flushes the queued deep link on did-finish-load and the preload bridge forwards it immediately, but there is no replay/buffer in preload.ts. That means the first OPEN_THREAD_CHANNEL event can fire before this effect runs, and the deep link is silently dropped instead of navigating to the thread.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/desktop/src/preload.ts around line 119:

The `onOpenThread` listener is registered during `useEffect` in `AppSidebarLayout`, which runs after the renderer has loaded. The main process emits the thread-open event on `did-finish-load`, so a `t3://thread/...` deep link opened during cold start fires before this listener exists and is permanently dropped. Consider having the main process buffer early thread-open events (or replay the last one) so a late-subscribing renderer still receives them.

Also found in 1 other location(s):
- apps/web/src/components/AppSidebarLayout.tsx:101 -- `AppSidebarLayout` registers `window.desktopBridge.onOpenThread` inside `useEffect`, so the listener is attached only after the initial React commit. On a cold start from a `t3://thread/...` link, the main process flushes the queued deep link on `did-finish-load` and the preload bridge forwards it immediately, but there is no replay/buffer in `preload.ts`. That means the first `OPEN_THREAD_CHANNEL` event can fire before this effect runs, and the deep link is silently dropped instead of navigating to the thread.

const existingWindow = yield* currentMainWindow;
if (Option.isNone(existingWindow)) {
yield* Ref.set(pendingOpenThreadIdRef, Option.some(threadId));
yield* createMainIfBackendReady;

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 window/DesktopWindow.ts:950

Concurrent deep links can create duplicate main windows during cold startup: each openThread call observes no window and createMainIfBackendReady starts a separate createWindow() before either call registers its result. Serialize these requests or guard creation with a shared in-flight effect so only one main window is created and all pending thread IDs are routed through it.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/desktop/src/window/DesktopWindow.ts around line 950:

Concurrent deep links can create duplicate main windows during cold startup: each `openThread` call observes no window and `createMainIfBackendReady` starts a separate `createWindow()` before either call registers its result. Serialize these requests or guard creation with a shared in-flight effect so only one main window is created and all pending thread IDs are routed through it.

});
const userDataDirName = isDevelopment ? "t3code-dev" : "t3code";
const legacyUserDataDirName = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)";
const legacyUserDataDirName = branding.displayName;

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.

🟠 High app/DesktopEnvironment.ts:182

Nightly upgrades now look for T3 Code (Nightly) instead of the existing T3 Code (Alpha) legacy directory, so users with that prior installation fall back to the fresh t3code directory and appear signed out with their desktop state/settings missing. Preserve the legacy Alpha name for non-development builds so the existing data can still be discovered.

Suggested change
const legacyUserDataDirName = branding.displayName;
const legacyUserDataDirName = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)";
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/desktop/src/app/DesktopEnvironment.ts around line 182:

Nightly upgrades now look for `T3 Code (Nightly)` instead of the existing `T3 Code (Alpha)` legacy directory, so users with that prior installation fall back to the fresh `t3code` directory and appear signed out with their desktop state/settings missing. Preserve the legacy Alpha name for non-development builds so the existing data can still be discovered.

}

send();
};

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.

Cold-start deep links drop IPC

High Severity

Cold-start t3://thread/... delivery sends desktop:open-thread on did-finish-load, but the renderer only listens after AppSidebarLayout mounts and its useEffect runs. Router beforeLoad can still be in flight then, and that layout is not mounted at all until auth succeeds, so the IPC event is dropped. pendingThreadIdRef only helps after a listener already ran and the environment id is missing.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9cf9ae2. Configure here.

{
name: "T3 Code",
schemes: ["t3code", "t3code-dev"],
schemes: ["t3", "t3code", "t3code-dev"],

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.

Linux never claims t3://

High Severity

Thread links use the t3 scheme, but Linux still only registers t3code in the hidden URL-handler .desktop file, xdg-mime, and electron-builder linux.protocols. Existing comments already note that setAsDefaultProtocolClient cannot claim schemes for AppImage builds, so t3://thread/... never reaches second-instance on packaged Linux.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9cf9ae2. Configure here.

return resolveWebAssetBrandForChannel(resolveDesktopUpdateChannel(version));
}

// Keep in sync with apps/desktop/src/updates/updateChannels.ts.

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.

Passkey app id left stale

High Severity

Packaged appId is now com.t3tools.t3code.alpha or .nightly, but resolveMacPasskeySigningConfiguration still writes entitlements as TEAMID.com.t3tools.t3code. Signed macOS builds then have a bundle id that does not match com.apple.application-identifier or the embedded provisioning profile, which breaks signing and passkeys.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9cf9ae2. Configure here.

displayName,
appUserModelId: Option.getOrElse(config.appUserModelIdOverride, () =>
isDevelopment ? "com.t3tools.t3code.dev" : "com.t3tools.t3code",
resolveDesktopAppBundleId({ isDevelopment, appVersion: input.appVersion }),

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.

Channels still share userData lock

Medium Severity

Bundle ids were split so Alpha and Nightly can run together and each receive t3:// links, but userDataDirName is still t3code for every non-dev build. Electron’s single-instance lock follows that directory, so a fresh Alpha and Nightly still cannot both stay running, and the second launch is swallowed by whichever instance holds the lock.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9cf9ae2. Configure here.

@davidmashburn
davidmashburn force-pushed the feat/t3-deep-link-protocol branch from 9cf9ae2 to 26cff8f Compare August 20, 2026 03:01

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

There are 8 total unresolved issues (including 5 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 26cff8f. Configure here.

yield* logDeepLinkError("failed to register default protocol client", {
scheme: DESKTOP_THREAD_DEEP_LINK_SCHEME,
});
}

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.

Linux never claims the t3 scheme

Medium Severity

t3 is added to macOS CFBundleURLTypes and registered via setAsDefaultProtocolClient, but Linux packaging still advertises only t3code / t3code-dev, and DesktopLinuxUrlHandler still xdg-mime-claims only getDesktopScheme(). On AppImage that Electron API does not bind the handler, so t3:// never launches or focuses the Linux app.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 26cff8f. Configure here.

});
const userDataDirName = isDevelopment ? "t3code-dev" : "t3code";
const legacyUserDataDirName = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)";
const legacyUserDataDirName = branding.displayName;

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.

Nightly loses legacy userData path

Medium Severity

legacyUserDataDirName is now branding.displayName. Nightly therefore looks for T3 Code (Nightly) instead of the previous non-dev legacy folder T3 Code (Alpha). Nightly installs still using that Alpha-named directory are treated as fresh (t3code) and drop existing session/settings data.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 26cff8f. Configure here.


// Alpha-channel builds use a distinct bundle id so they can run alongside
// Nightly and receive `t3://` deep links without single-instance conflicts.
return "com.t3tools.t3code.alpha";

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.

Channels still share single-instance lock

Medium Severity

Distinct bundle ids are described as letting Alpha and Nightly run together without single-instance conflicts so each can own t3://. Electron’s lock is still scoped to userData, and both non-dev channels keep userDataDirName t3code. The second channel still quits as a duplicate instance and forwards into the first.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 26cff8f. Configure here.

davidmashburn and others added 3 commits August 20, 2026 02:20
Replay of d0a2c94 onto upstream (origin/main).

Changes from original:
- Merged deep-link wiring with upstream desktop: kept linuxUrlHandler,
  fullscreen IPC, and zoomMain alongside new OPEN_THREAD channel,
  DesktopDeepLinks, and openThread window routing.
- AppSidebarLayout: kept upstream resizable sidebar/fullscreen chrome;
  added onOpenThread navigation only.
- Reverted redundant server orchestration hunks already covered by
  8209478 (assistant message ID scoping).
- build-desktop-artifact: kept upstream nightly branding test plus fork
  publish auto-detection expectations.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replay of 7317944 onto upstream (105cd5e).

- Kept upstream electron-launcher LAUNCHER_VERSION=15; applied fork alpha bundle id and t3+t3code schemes.
- Merged DesktopEnvironment: upstream DesktopStatePaths + fork resolveDesktopAppBundleId; legacyUserDataDirName uses branding.displayName.
- Merged AppSidebarLayout: kept upstream resizable sidebar, settings nav, fullscreen traffic-light inset; added fork deep-link navigation queue (pendingThreadIdRef, onOpenThread).
- Kept upstream resolveDesktopWebAssetBrand and added fork resolveDesktopBuildAppId in build-desktop-artifact.ts.
- DesktopDeepLinks, updateChannels, and tests applied cleanly from original.

Co-authored-by: Cursor <cursoragent@cursor.com>
Upstream added DesktopLifecycle.test.ts after the fork point, and its
DesktopWindow mock predates the openThread member the t3:// deep-link work adds
to that service. The replay carried the interface change without the new mock,
so apps/desktop failed to typecheck on both the integration branch and the
deep-link PR branch.

Co-authored-by: Cursor <cursoragent@cursor.com>
@davidmashburn
davidmashburn force-pushed the feat/t3-deep-link-protocol branch from 26cff8f to edd18aa Compare August 20, 2026 08:29
@t3dotgg

t3dotgg commented Aug 28, 2026

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

This branch passes only a thread ID through its deep link. #8246 carries both the environment and thread IDs and waits for the renderer, which avoids opening the wrong environment.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotgg t3dotgg closed this Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants