Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion apps/web/src/commandPaletteBus.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import type { EnvironmentId, ProjectId } from "@t3tools/contracts";

// Tiny event bus allowing components to programmatically open the command palette
// without owning its React state.
const COMMAND_PALETTE_OPEN_EVENT = "t3code:open-command-palette";

export interface CommandPaletteOpenDetail {
readonly open?: "add-project" | "new-thread-in";
readonly open?: "add-project" | "new-thread-in" | "import-sessions";
readonly environmentId?: EnvironmentId;
readonly projectId?: ProjectId;
}

export function openCommandPalette(detail?: CommandPaletteOpenDetail): void {
Expand Down
40 changes: 40 additions & 0 deletions apps/web/src/components/ChatView.logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,46 @@ describe("buildLoadingThreadFromShell", () => {
checkpoints: [],
});
});

it("preserves shell teleport presence while detail is still loading", () => {
const teleport = {
presence: "native" as const,
provider: "grok" as const,
externalSessionId: "session-1",
nativePath: "/tmp/native",
lastSyncedAt: now,
};
const shell = {
environmentId,
id: threadId,
projectId,
title: "Loading thread",
modelSelection: {
instanceId: ProviderInstanceId.make("codex"),
model: "gpt-5.4",
},
runtimeMode: "full-access" as const,
interactionMode: "default" as const,
branch: "main",
worktreePath: null,
latestTurn: null,
createdAt: now,
updatedAt: now,
archivedAt: null,
settledOverride: null,
settledAt: null,
snoozedUntil: null,
snoozedAt: null,
session: null,
latestUserMessageAt: now,
hasPendingApprovals: false,
hasPendingUserInput: false,
hasActionableProposedPlan: false,
teleport,
} satisfies ThreadShell;

expect(buildLoadingThreadFromShell(shell).teleport).toEqual(teleport);
});
});

describe("resolveThreadMetadataUpdateForNextTurn", () => {
Expand Down
20 changes: 19 additions & 1 deletion apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
ProviderDriverKind,
RuntimeMode,
TerminalOpenInput,
isTeleportedOut,
} from "@t3tools/contracts";
import {
connectionStatusTitle,
Expand Down Expand Up @@ -166,6 +167,7 @@ import {
WifiOffIcon,
} from "lucide-react";
import { cn, randomHex } from "~/lib/utils";
import { TELEPORTED_OUT_SEND_DISABLED_REASON } from "~/lib/teleport";
import { COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS } from "~/workspaceTitlebar";
import { stackedThreadToast, toastManager } from "./ui/toast";
import { decodeProjectScriptKeybindingRule } from "~/lib/projectScriptKeybindings";
Expand Down Expand Up @@ -4894,6 +4896,16 @@ function ChatViewContent(props: ChatViewProps) {
notifyDirectAnnotationAttached();
return;
}
if (isTeleportedOut(activeThread.teleport)) {
toastManager.add(
stackedThreadToast({
type: "warning",
title: "Thread is in the native CLI",
description: TELEPORTED_OUT_SEND_DISABLED_REASON,
}),
);
return;
}
if (activeEnvironmentUnavailable) {
toastManager.add(
stackedThreadToast({
Expand Down Expand Up @@ -6349,7 +6361,13 @@ function ChatViewContent(props: ChatViewProps) {
phase={phase}
isConnecting={isConnecting}
isSendBusy={isSendBusy}
sendDisabledReason={threadDetailLoading ? "Messages loading" : null}
sendDisabledReason={
threadDetailLoading
? "Messages loading"
: isTeleportedOut(activeThread?.teleport)
? TELEPORTED_OUT_SEND_DISABLED_REASON
: null
}
isPreparingWorktree={isPreparingWorktree}
environmentUnavailable={activeEnvironmentUnavailableState}
activePendingApproval={activePendingApproval}
Expand Down
20 changes: 20 additions & 0 deletions apps/web/src/components/CommandPalette.logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,26 @@ describe("reduceCommandPaletteUiState", () => {
mode: "command",
openIntent: { kind: "new-thread-in" },
});
expect(reduceCommandPaletteUiState(filesOpen, { _tag: "OpenImportSessions" })).toEqual({
open: true,
mode: "command",
openIntent: { kind: "import-sessions" },
});
expect(
reduceCommandPaletteUiState(filesOpen, {
_tag: "OpenImportSessions",
environmentId: EnvironmentId.make("environment-local"),
projectId: ProjectId.make("project-1"),
}),
).toEqual({
open: true,
mode: "command",
openIntent: {
kind: "import-sessions",
environmentId: EnvironmentId.make("environment-local"),
projectId: ProjectId.make("project-1"),
},
});
});

it("resets to command mode for dialog-driven opens and closes", () => {
Expand Down
32 changes: 29 additions & 3 deletions apps/web/src/components/CommandPalette.logic.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
type EnvironmentId,
type FilesystemBrowseEntry,
type KeybindingCommand,
type ProjectId,
THREAD_JUMP_KEYBINDING_COMMANDS,
} from "@t3tools/contracts";
import type { SidebarThreadSortOrder } from "@t3tools/contracts/settings";
Expand All @@ -23,9 +25,14 @@ export const ADDON_ICON_CLASS = "size-4";
*/
export type SearchOverlayMode = "command" | "files" | "content";

export interface CommandPaletteOpenIntent {
readonly kind: "add-project" | "new-thread-in";
}
export type CommandPaletteOpenIntent =
| { readonly kind: "add-project" }
| { readonly kind: "new-thread-in" }
| {
readonly kind: "import-sessions";
readonly environmentId?: EnvironmentId;
readonly projectId?: ProjectId;
};

export interface CommandPaletteUiState {
readonly open: boolean;
Expand All @@ -38,6 +45,11 @@ export type CommandPaletteUiAction =
| { readonly _tag: "ToggleMode"; readonly mode: SearchOverlayMode }
| { readonly _tag: "OpenAddProject" }
| { readonly _tag: "OpenNewThreadIn" }
| {
readonly _tag: "OpenImportSessions";
readonly environmentId?: EnvironmentId;
readonly projectId?: ProjectId;
}
| { readonly _tag: "ClearOpenIntent" };

export function reduceCommandPaletteUiState(
Expand All @@ -59,8 +71,22 @@ export function reduceCommandPaletteUiState(
return { open: true, mode: "command", openIntent: { kind: "add-project" } };
case "OpenNewThreadIn":
return { open: true, mode: "command", openIntent: { kind: "new-thread-in" } };
case "OpenImportSessions":
return {
open: true,
mode: "command",
openIntent: {
kind: "import-sessions",
...(action.environmentId === undefined ? {} : { environmentId: action.environmentId }),
...(action.projectId === undefined ? {} : { projectId: action.projectId }),
},
};
case "ClearOpenIntent":
return state.openIntent ? { ...state, openIntent: null } : state;
default: {
const _exhaustive: never = action;
return _exhaustive;
}
}
}

Expand Down
Loading
Loading