feat(desktop): register t3:// OS protocol handler for thread deep links#2424
feat(desktop): register t3:// OS protocol handler for thread deep links#2424davidmashburn wants to merge 2 commits intopingdotgg:mainfrom
Conversation
Clicking a t3://thread/<threadId> link from anywhere — browser, terminal,
another app — now focuses the running T3 window and navigates directly
to that thread without a pairing screen.
Changes:
- main.ts: setAsDefaultProtocolClient("t3"), open-url handler (macOS),
requestSingleInstanceLock + second-instance handler (Windows/Linux),
handleDeepLink() parses t3://thread/<threadId> and sends IPC to the
renderer; queues in pendingDeepLinkThreadId if window not yet loaded
- preload.ts: expose onOpenThread via desktopBridge (desktop:open-thread)
- AppSidebarLayout: subscribe to onOpenThread, navigate to
/$activeEnvironmentId/$threadId on receipt
- contracts/ipc.ts: add onOpenThread to DesktopBridge interface
- Update two test mocks for the new bridge method
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Include the `t3` protocol in the electron-builder mac config so the packaged app registers the t3:// URL scheme at install time via CFBundleURLTypes in Info.plist, rather than relying solely on the runtime setAsDefaultProtocolClient call. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c1e8393. Configure here.
| if (pendingDeepLinkThreadId !== null) { | ||
| window.webContents.send(OPEN_THREAD_CHANNEL, pendingDeepLinkThreadId); | ||
| pendingDeepLinkThreadId = null; | ||
| } |
There was a problem hiding this comment.
Cold-start deep link permanently lost due to timing
High Severity
The cold-start deep link path is broken due to two compounding timing issues. First, a comment in the same file notes that did-finish-load "typically fires before the first paint," but React's useEffect (which registers the onOpenThread IPC listener) runs after paint — so the IPC message is sent before any listener exists. Second, even if that race were avoided, activeEnvironmentId is initialized to null and only set once serverConfig loads from the backend; during cold start the callback will hit the if (!activeEnvironmentId) return guard and silently discard the thread ID. Since pendingDeepLinkThreadId is cleared after sending and never retried, the deep link is permanently lost.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit c1e8393. Configure here.
ApprovabilityVerdict: Needs human review This PR introduces a new OS protocol handler feature with deep link navigation capabilities, which constitutes new user-facing behavior requiring human review. Additionally, an unresolved review comment identifies a high-severity timing bug where cold-start deep links may be permanently lost. You can customize Macroscope's approvability policy. Learn more. |


What Changed
Adds a
t3://thread/<threadId>OS-level protocol handler to the Electron desktop app.apps/desktop/src/main.ts: registersopen-url(macOS) andsecond-instance(Win/Linux) listeners beforewhenReady;handleDeepLink()parses the URL and sendsdesktop:open-threadto the renderer viawebContents.send;pendingDeepLinkThreadIdqueues deep links that arrive before the window finishes loading;setAsDefaultProtocolClient('t3')called inwhenReadyapps/desktop/src/preload.ts: exposesonOpenThreadover the context bridge (same pattern asonMenuAction)packages/contracts/src/ipc.ts: addsonOpenThreadto theDesktopBridgeinterfaceapps/web/src/components/AppSidebarLayout.tsx: subscribes toonOpenThreadand navigates to/$environmentId/$threadIdusing TanStack Routerscripts/build-desktop-artifact.ts: addsprotocols: [{name:"T3 Code", schemes:["t3"]}]to the mac electron-builder config soCFBundleURLTypesis written into the packagedInfo.plistlocalApi.test.ts,SettingsPanels.browser.tsx): stubonOpenThread: () => () => {}addedWhy
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 theopen-urlevent,handleDeepLinkparsed it correctly, andwebContents.send("desktop:open-thread", threadId)was confirmed in the Electron main-process logs.UI Changes
N/A — no visual changes. Navigation behavior only.
Checklist