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


Summary
t3://thread/<threadId>OS protocol handler to the Electron desktop app so thatt3://URLs can be opened from anywhere (browser, Slack, terminal) without hitting the pairing screenapps/desktop/src/main.ts): registersopen-url/second-instancelisteners, callssetAsDefaultProtocolClient('t3')inwhenReady, and queues deep links received before the window loadsapps/desktop/src/preload.ts): exposesonOpenThreadover the context bridgeapps/web/src/components/AppSidebarLayout.tsx): subscribes toonOpenThreadand navigates to/$environmentId/$threadIdusing TanStack Routerscripts/build-desktop-artifact.ts): addsCFBundleURLTypeswith schemet3to the macOS packaged build via electron-builder'sprotocolsfield, enabling OS-level registration even when the app is not runningTest plan
bun dist:desktop:dmg)t3://appears inlsregister -dumpopen "t3://thread/<valid-threadId>"— app should come to front and navigate to the threadpendingDeepLinkThreadId)bun teststill passes (existing test mocks updated inlocalApi.test.tsandSettingsPanels.browser.tsx)🤖 Generated with Claude Code
Note
Medium Risk
Adds OS-level protocol handling and IPC-driven navigation, which touches desktop app startup/single-instance behavior and could affect window lifecycle and deep-link dispatch timing across platforms.
Overview
Adds support for
t3://thread/<threadId>deep links in the Electron desktop app, including early-event handling and queuing deep links until the main window finishes loading.Registers the app as the default
t3protocol client and enforces single-instance behavior to route Windows/Linux deep links viasecond-instance, while macOS usesopen-url. Exposes a newdesktopBridge.onOpenThreadIPC subscription and updates the web renderer to navigate to the specified thread for the active environment; test stubs/mocks and the macOS electron-builder config are updated accordingly.Reviewed by Cursor Bugbot for commit c1e8393. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Register
t3://OS protocol handler for thread deep links in the desktop appt3custom URL scheme viaapp.setAsDefaultProtocolClientand, for macOS bundles, declares it in the build config protocols entry.handleDeepLinkin main.ts to parset3://thread/<threadId>URLs and send adesktop:open-threadIPC message to the renderer, buffering the threadId if the window hasn't finished loading yet.second-instanceevent.desktopBridge.onOpenThreadin preload.ts so renderer code can subscribe to open-thread events.onOpenThreadand navigates to the thread route using the active environment ID.Macroscope summarized c1e8393.