Add pinnable new-tab targets to the tab row and sidebar

Any new-tab target (new agent, terminal, browser, or a terminal profile)
can be pinned as a quick-launch button. Pins are global client app state
(persisted), not daemon config, so the same set shows on every workspace;
profile pins resolve per host and are dropped where that profile is absent.

The sidebar mirror launches via a navigate-then-launch intent store rather
than calling the create handlers directly: terminal creation only works in
the mounted/focused workspace, so the sidebar requests the launch and the
workspace screen consumes it on focus.
This commit is contained in:
Mohamed Boudra
2026-06-15 14:09:29 +07:00
parent 42969e40d5
commit 713a042dc5
22 changed files with 835 additions and 65 deletions
@@ -185,6 +185,8 @@ import {
resolveTerminalProfiles,
} from "@getpaseo/protocol/terminal-profiles";
import { getProviderIcon } from "@/components/provider-icons";
import { useLaunchIntentStore } from "@/workspace-pins/launch-intent-store";
import { runPinnedTabTarget, type TabTargetHandlers } from "@/workspace-pins/run";
import {
createWorkspaceFileTabTarget,
normalizeWorkspaceFileLocation,
@@ -1718,6 +1720,37 @@ function useWorkspaceCheckoutStatus(input: {
return { checkoutQuery, isCheckoutStatusLoading };
}
interface PinnedTabLaunchConsumerInput {
serverId: string;
persistenceKey: string | null;
isRouteFocused: boolean;
handlers: TabTargetHandlers;
}
function usePinnedTabLaunchConsumer({
serverId,
persistenceKey,
isRouteFocused,
handlers,
}: PinnedTabLaunchConsumerInput) {
const { config } = useDaemonConfig(serverId);
const profiles = useMemo(
() => resolveTerminalProfiles(config?.terminalProfiles),
[config?.terminalProfiles],
);
const pending = useLaunchIntentStore((state) => state.pending);
useEffect(() => {
if (!persistenceKey || !isRouteFocused || pending?.workspaceKey !== persistenceKey) {
return;
}
const target = useLaunchIntentStore.getState().consume(persistenceKey);
if (target) {
runPinnedTabTarget(target, profiles, handlers);
}
}, [handlers, isRouteFocused, pending, persistenceKey, profiles]);
}
function WorkspaceScreenContent({
serverId,
workspaceId,
@@ -2564,6 +2597,28 @@ function WorkspaceScreenContent({
[openWorkspaceTabFocused, persistenceKey],
);
const pinnedTabHandlers = useMemo<TabTargetHandlers>(
() => ({
createDraft: handleCreateDraftTab,
createTerminal: handleCreateTerminal,
createBrowser: handleCreateBrowserTab,
createTerminalWithProfile: handleCreateTerminalWithProfile,
}),
[
handleCreateBrowserTab,
handleCreateDraftTab,
handleCreateTerminal,
handleCreateTerminalWithProfile,
],
);
usePinnedTabLaunchConsumer({
serverId: normalizedServerId,
persistenceKey,
isRouteFocused,
handlers: pinnedTabHandlers,
});
useDesktopBrowserNewTabRequests({
enabled: Boolean(persistenceKey),
workspaceLayout,