diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx
index bd9dd25d2..2d5ad9909 100644
--- a/packages/app/src/app/_layout.tsx
+++ b/packages/app/src/app/_layout.tsx
@@ -35,6 +35,7 @@ import * as Linking from "expo-linking";
import * as Notifications from "expo-notifications";
import { LeftSidebar } from "@/components/left-sidebar";
import { DownloadToast } from "@/components/download-toast";
+import { UpdateBanner } from "@/desktop/updates/update-banner";
import { ToastProvider } from "@/contexts/toast-context";
import { usePanelStore } from "@/stores/panel-store";
import { runOnJS, interpolate, Extrapolation, useSharedValue } from "react-native-reanimated";
@@ -379,6 +380,7 @@ function AppContainer({
{isMobile && chromeEnabled && }
+
@@ -578,7 +580,12 @@ export default function RootLayout() {
>
-
+
+ `${params?.serverId}:${params?.workspaceId}`
+ }
+ />
{})}
isDragging={input?.isDragging ?? false}
@@ -1147,7 +1148,7 @@ function ProjectBlock({
return
}
onWorkspacePress?.()
- router.replace(buildHostWorkspaceRoute(serverId, flattenedWorkspace.workspaceId) as any)
+ navigateToWorkspace(serverId, flattenedWorkspace.workspaceId)
}}
drag={drag}
isDragging={isDragging}
diff --git a/packages/app/src/components/terminal-emulator.tsx b/packages/app/src/components/terminal-emulator.tsx
index 9e41c7e52..b29ba0e8b 100644
--- a/packages/app/src/components/terminal-emulator.tsx
+++ b/packages/app/src/components/terminal-emulator.tsx
@@ -7,10 +7,6 @@ import type { ITheme } from "@xterm/xterm";
import type { PendingTerminalModifiers } from "../utils/terminal-keys";
import { TerminalEmulatorRuntime } from "../terminal/runtime/terminal-emulator-runtime";
import { focusWithRetries } from "../utils/web-focus";
-import {
- summarizeTerminalText,
- terminalDebugLog,
-} from "../terminal/runtime/terminal-debug";
function buildXtermThemeKey(theme: ITheme): string {
const values: Array = [
@@ -287,14 +283,6 @@ export default function TerminalEmulator({
}
if (outputChunkSequence <= appliedChunkSequenceRef.current) {
- terminalDebugLog({
- scope: "emulator-component",
- event: "output:chunk:skip-duplicate",
- details: {
- sequence: outputChunkSequence,
- lastAppliedSequence: appliedChunkSequenceRef.current,
- },
- });
onOutputChunkConsumed?.(outputChunkSequence);
return;
}
@@ -307,13 +295,6 @@ export default function TerminalEmulator({
appliedChunkSequenceRef.current = outputChunkSequence;
if (outputChunkText.length === 0) {
- terminalDebugLog({
- scope: "emulator-component",
- event: "output:chunk:clear",
- details: {
- sequence: outputChunkSequence,
- },
- });
runtime.clear({
onCommitted: () => {
onOutputChunkConsumed?.(outputChunkSequence);
@@ -321,16 +302,6 @@ export default function TerminalEmulator({
});
return;
}
- terminalDebugLog({
- scope: "emulator-component",
- event: "output:chunk:write",
- details: {
- sequence: outputChunkSequence,
- replay: outputChunkReplay,
- length: outputChunkText.length,
- preview: summarizeTerminalText({ text: outputChunkText, maxChars: 80 }),
- },
- });
runtime.write({
text: outputChunkText,
suppressInput: outputChunkReplay,
@@ -383,10 +354,6 @@ export default function TerminalEmulator({
touchAction: "pan-y",
}}
onPointerDown={() => {
- terminalDebugLog({
- scope: "emulator-component",
- event: "surface:pointer-down-focus",
- });
runtimeRef.current?.focus();
}}
>
diff --git a/packages/app/src/components/terminal-pane.tsx b/packages/app/src/components/terminal-pane.tsx
index ddec4fa75..f106b17e5 100644
--- a/packages/app/src/components/terminal-pane.tsx
+++ b/packages/app/src/components/terminal-pane.tsx
@@ -35,10 +35,6 @@ import {
TerminalStreamController,
type TerminalStreamControllerStatus,
} from "@/terminal/runtime/terminal-stream-controller";
-import {
- summarizeTerminalText,
- terminalDebugLog,
-} from "@/terminal/runtime/terminal-debug";
import { usePanelStore } from "@/stores/panel-store";
import { toXtermTheme } from "@/utils/to-xterm-theme";
import TerminalEmulator from "./terminal-emulator";
@@ -366,7 +362,7 @@ export function TerminalPane({
const terminals = terminalsQuery.data?.terminals ?? [];
useEffect(() => {
- if (!client || !isConnected) {
+ if (!client || !isConnected || !isScreenFocused) {
return;
}
@@ -716,17 +712,6 @@ export function TerminalPane({
meta: input.meta,
},
};
- terminalDebugLog({
- scope: "terminal-pane",
- event: "input:key:send",
- details: {
- key: normalizedKey,
- ctrl: input.ctrl,
- shift: input.shift,
- alt: input.alt,
- activeStreamId: getCurrentActiveStreamId(),
- },
- });
if (!dispatchTerminalInputEntry(pendingEntry)) {
enqueuePendingTerminalInput(pendingEntry);
}
@@ -745,16 +730,6 @@ export function TerminalPane({
if (data.length === 0) {
return;
}
- const currentStreamId = getCurrentActiveStreamId();
- terminalDebugLog({
- scope: "terminal-pane",
- event: "input:data:received",
- details: {
- length: data.length,
- preview: summarizeTerminalText({ text: data, maxChars: 80 }),
- activeStreamId: currentStreamId,
- },
- });
if (hasPendingTerminalModifiers(modifiers)) {
const pendingResolution = resolvePendingModifierDataInput({
@@ -788,15 +763,6 @@ export function TerminalPane({
});
return;
}
- terminalDebugLog({
- scope: "terminal-pane",
- event: "input:data:send",
- details: {
- length: data.length,
- preview: summarizeTerminalText({ text: data, maxChars: 80 }),
- activeStreamId: currentStreamId,
- },
- });
const pendingEntry: PendingTerminalInput = {
type: "data",
data,
@@ -835,15 +801,6 @@ export function TerminalPane({
return;
}
lastReportedSizeRef.current = { rows: normalizedRows, cols: normalizedCols };
- terminalDebugLog({
- scope: "terminal-pane",
- event: "display:resize:send",
- details: {
- terminalId: selectedTerminalId,
- rows: normalizedRows,
- cols: normalizedCols,
- },
- });
client.sendTerminalInput(selectedTerminalId, {
type: "resize",
rows: normalizedRows,
@@ -871,13 +828,6 @@ export function TerminalPane({
}, [clearPendingModifiers]);
const handleOutputChunkConsumed = useCallback((sequence: number) => {
- terminalDebugLog({
- scope: "terminal-pane",
- event: "output:chunk:consumed",
- details: {
- sequence,
- },
- });
outputSession.consume({ sequence });
}, [outputSession]);
@@ -1040,7 +990,7 @@ export function TerminalPane({
) : null}
- {selectedTerminal ? (
+ {selectedTerminal && isScreenFocused ? (
+ ) : selectedTerminal ? (
+
) : (
No terminal selected
)}
- {isAttaching ? (
+ {isAttaching && isScreenFocused ? (
| null>(null);
+
+ useEffect(() => {
+ if (!isDesktop) return;
+
+ void checkForUpdates({ silent: true });
+
+ intervalRef.current = setInterval(() => {
+ void checkForUpdates({ silent: true });
+ }, CHECK_INTERVAL_MS);
+
+ return () => {
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ }
+ };
+ }, [isDesktop, checkForUpdates]);
+
+ if (!isDesktop) return null;
+ if (dismissed) return null;
+ if (status !== "available" && status !== "installed") return null;
+
+ const isInstalled = status === "installed";
+
+ return (
+
+
+ setDismissed(true)}
+ hitSlop={8}
+ style={styles.closeButton}
+ >
+
+
+
+
+
+ {isInstalled ? "Update installed" : "Update available"}
+
+
+ {isInstalled
+ ? "Restart to use the new version."
+ : `${availableUpdate?.latestVersion ? `v${availableUpdate.latestVersion.replace(/^v/i, "")} is ready` : "A new version is ready"} to install.`}
+
+
+
+
+ void openExternalUrl(CHANGELOG_URL)}
+ style={({ pressed }) => [
+ styles.outlineButton,
+ pressed && styles.buttonPressed,
+ ]}
+ >
+ What's new
+
+
+ {!isInstalled && (
+ void installUpdate()}
+ disabled={isInstalling}
+ style={({ pressed }) => [
+ styles.primaryButton,
+ pressed && styles.buttonPressed,
+ isInstalling && styles.buttonDisabled,
+ ]}
+ >
+
+ {isInstalling ? "Installing..." : "Install & restart"}
+
+
+ )}
+
+
+
+ );
+}
+
+const styles = StyleSheet.create((theme) => ({
+ container: {
+ position: "absolute",
+ bottom: theme.spacing[4],
+ right: theme.spacing[4],
+ zIndex: 1000,
+ },
+ banner: {
+ flexDirection: "row",
+ alignItems: "center",
+ gap: theme.spacing[4],
+ backgroundColor: theme.colors.surface2,
+ borderRadius: theme.borderRadius.xl,
+ borderWidth: theme.borderWidth[1],
+ borderColor: theme.colors.border,
+ paddingVertical: theme.spacing[3],
+ paddingLeft: theme.spacing[4],
+ paddingRight: theme.spacing[3],
+ shadowColor: "#000",
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 0.2,
+ shadowRadius: 12,
+ elevation: 8,
+ maxWidth: 480,
+ },
+ closeButton: {
+ position: "absolute",
+ top: theme.spacing[2],
+ left: theme.spacing[2],
+ padding: theme.spacing[1],
+ zIndex: 1,
+ },
+ textSection: {
+ flex: 1,
+ gap: 2,
+ paddingTop: theme.spacing[1],
+ },
+ title: {
+ color: theme.colors.foreground,
+ fontSize: theme.fontSize.sm,
+ fontWeight: theme.fontWeight.semibold,
+ },
+ subtitle: {
+ color: theme.colors.foregroundMuted,
+ fontSize: theme.fontSize.xs,
+ },
+ actions: {
+ flexDirection: "row",
+ gap: theme.spacing[2],
+ alignItems: "center",
+ },
+ outlineButton: {
+ paddingVertical: theme.spacing[2],
+ paddingHorizontal: theme.spacing[3],
+ borderRadius: theme.borderRadius.md,
+ borderWidth: theme.borderWidth[1],
+ borderColor: theme.colors.border,
+ },
+ outlineButtonText: {
+ color: theme.colors.foreground,
+ fontSize: theme.fontSize.xs,
+ fontWeight: theme.fontWeight.medium,
+ },
+ primaryButton: {
+ paddingVertical: theme.spacing[2],
+ paddingHorizontal: theme.spacing[3],
+ borderRadius: theme.borderRadius.md,
+ backgroundColor: theme.colors.foreground,
+ },
+ primaryButtonText: {
+ color: theme.colors.surface0,
+ fontSize: theme.fontSize.xs,
+ fontWeight: theme.fontWeight.medium,
+ },
+ buttonPressed: {
+ opacity: 0.8,
+ },
+ buttonDisabled: {
+ opacity: 0.5,
+ },
+}));
diff --git a/packages/app/src/hooks/use-keyboard-shortcuts.ts b/packages/app/src/hooks/use-keyboard-shortcuts.ts
index 675d30fbf..c566cb86d 100644
--- a/packages/app/src/hooks/use-keyboard-shortcuts.ts
+++ b/packages/app/src/hooks/use-keyboard-shortcuts.ts
@@ -1,14 +1,14 @@
import { useEffect } from "react";
import { Platform } from "react-native";
-import { usePathname, useRouter } from "expo-router";
+import { usePathname } from "expo-router";
import { getIsTauri } from "@/constants/layout";
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
import { setCommandCenterFocusRestoreElement } from "@/utils/command-center-focus-restore";
import {
- buildHostWorkspaceRoute,
parseHostAgentRouteFromPathname,
parseHostWorkspaceRouteFromPathname,
} from "@/utils/host-routes";
+import { navigateToWorkspace } from "@/hooks/use-workspace-navigation";
import {
type MessageInputKeyboardActionKind,
type KeyboardShortcutPayload,
@@ -32,7 +32,6 @@ export function useKeyboardShortcuts({
selectedAgentId?: string;
toggleFileExplorer?: () => void;
}) {
- const router = useRouter();
const pathname = usePathname();
const resetModifiers = useKeyboardShortcutsStore((s) => s.resetModifiers);
@@ -57,11 +56,7 @@ export function useKeyboardShortcuts({
return false;
}
- const shouldReplace =
- Boolean(parseHostWorkspaceRouteFromPathname(pathname)) ||
- Boolean(parseHostAgentRouteFromPathname(pathname));
- const navigate = shouldReplace ? router.replace : router.push;
- navigate(buildHostWorkspaceRoute(target.serverId, target.workspaceId) as any);
+ navigateToWorkspace(target.serverId, target.workspaceId);
return true;
};
const navigateRelativeWorkspace = (delta: 1 | -1): boolean => {
@@ -77,7 +72,7 @@ export function useKeyboardShortcuts({
if (!fallback) {
return false;
}
- router.push(buildHostWorkspaceRoute(fallback.serverId, fallback.workspaceId) as any);
+ navigateToWorkspace(fallback.serverId, fallback.workspaceId);
return true;
}
@@ -92,7 +87,7 @@ export function useKeyboardShortcuts({
if (!target) {
return false;
}
- router.replace(buildHostWorkspaceRoute(target.serverId, target.workspaceId) as any);
+ navigateToWorkspace(target.serverId, target.workspaceId);
return true;
};
@@ -134,22 +129,6 @@ export function useKeyboardShortcuts({
return false;
}
};
- const requestWorkspaceTabAction = (input:
- | { kind: "new" | "close-current" }
- | { kind: "navigate-index"; index: number }
- | { kind: "navigate-relative"; delta: 1 | -1 }): boolean => {
- const route = parseHostWorkspaceRouteFromPathname(pathname);
- if (!route) {
- return false;
- }
- useKeyboardShortcutsStore.getState().requestWorkspaceTabAction({
- serverId: route.serverId,
- workspaceId: route.workspaceId,
- ...input,
- });
- return true;
- };
-
const handleAction = (input: {
action: string;
payload: KeyboardShortcutPayload;
@@ -159,23 +138,31 @@ export function useKeyboardShortcuts({
case "agent.new":
return openProjectPicker();
case "workspace.tab.new":
- return requestWorkspaceTabAction({ kind: "new" });
+ return keyboardActionDispatcher.dispatch({
+ id: "workspace.tab.new",
+ scope: "workspace",
+ });
case "workspace.tab.close.current":
- return requestWorkspaceTabAction({ kind: "close-current" });
+ return keyboardActionDispatcher.dispatch({
+ id: "workspace.tab.close-current",
+ scope: "workspace",
+ });
case "workspace.tab.navigate.index":
if (!input.payload || typeof input.payload !== "object" || !("index" in input.payload)) {
return false;
}
- return requestWorkspaceTabAction({
- kind: "navigate-index",
+ return keyboardActionDispatcher.dispatch({
+ id: "workspace.tab.navigate-index",
+ scope: "workspace",
index: input.payload.index,
});
case "workspace.tab.navigate.relative":
if (!input.payload || typeof input.payload !== "object" || !("delta" in input.payload)) {
return false;
}
- return requestWorkspaceTabAction({
- kind: "navigate-relative",
+ return keyboardActionDispatcher.dispatch({
+ id: "workspace.tab.navigate-relative",
+ scope: "workspace",
delta: input.payload.delta,
});
case "workspace.navigate.index":
@@ -326,7 +313,6 @@ export function useKeyboardShortcuts({
isMobile,
pathname,
resetModifiers,
- router,
selectedAgentId,
toggleAgentList,
toggleFileExplorer,
diff --git a/packages/app/src/hooks/use-workspace-navigation.ts b/packages/app/src/hooks/use-workspace-navigation.ts
new file mode 100644
index 000000000..09c46c54d
--- /dev/null
+++ b/packages/app/src/hooks/use-workspace-navigation.ts
@@ -0,0 +1,26 @@
+import { useCallback } from "react";
+import { router } from "expo-router";
+import { buildHostWorkspaceRoute } from "@/utils/host-routes";
+
+/**
+ * Navigate to a workspace screen. Uses router.navigate() which,
+ * combined with getId on the workspace Stack.Screen, ensures:
+ * - Only one instance of each workspace exists in the stack
+ * - History is preserved (back button works)
+ * - No duplicate workspace screens
+ */
+export function navigateToWorkspace(serverId: string, workspaceId: string) {
+ const href = buildHostWorkspaceRoute(serverId, workspaceId);
+ router.navigate(href as any);
+}
+
+export function useWorkspaceNavigation() {
+ return {
+ navigateToWorkspace: useCallback(
+ (serverId: string, workspaceId: string) => {
+ navigateToWorkspace(serverId, workspaceId);
+ },
+ []
+ ),
+ };
+}
diff --git a/packages/app/src/keyboard/keyboard-action-dispatcher.ts b/packages/app/src/keyboard/keyboard-action-dispatcher.ts
index 6a8d5b2a1..708c2a03e 100644
--- a/packages/app/src/keyboard/keyboard-action-dispatcher.ts
+++ b/packages/app/src/keyboard/keyboard-action-dispatcher.ts
@@ -9,12 +9,22 @@ export type KeyboardActionId =
| "message-input.dictation-toggle"
| "message-input.dictation-cancel"
| "message-input.voice-toggle"
- | "message-input.voice-mute-toggle";
+ | "message-input.voice-mute-toggle"
+ | "workspace.tab.new"
+ | "workspace.tab.close-current"
+ | "workspace.tab.navigate-index"
+ | "workspace.tab.navigate-relative";
-export type KeyboardActionDefinition = {
- id: KeyboardActionId;
- scope: KeyboardActionScope;
-};
+export type KeyboardActionDefinition =
+ | { id: "message-input.focus"; scope: KeyboardActionScope }
+ | { id: "message-input.dictation-toggle"; scope: KeyboardActionScope }
+ | { id: "message-input.dictation-cancel"; scope: KeyboardActionScope }
+ | { id: "message-input.voice-toggle"; scope: KeyboardActionScope }
+ | { id: "message-input.voice-mute-toggle"; scope: KeyboardActionScope }
+ | { id: "workspace.tab.new"; scope: KeyboardActionScope }
+ | { id: "workspace.tab.close-current"; scope: KeyboardActionScope }
+ | { id: "workspace.tab.navigate-index"; scope: KeyboardActionScope; index: number }
+ | { id: "workspace.tab.navigate-relative"; scope: KeyboardActionScope; delta: 1 | -1 };
export type KeyboardActionHandler = {
handlerId: string;
diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx
index 1ab584f1b..5228c97c3 100644
--- a/packages/app/src/screens/workspace/workspace-screen.tsx
+++ b/packages/app/src/screens/workspace/workspace-screen.tsx
@@ -1,4 +1,5 @@
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { useIsFocused } from "@react-navigation/native";
import {
ActivityIndicator,
BackHandler,
@@ -54,7 +55,8 @@ import {
buildWorkspaceTabPersistenceKey,
useWorkspaceTabsStore,
} from "@/stores/workspace-tabs-store";
-import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
+import { useKeyboardActionHandler } from "@/hooks/use-keyboard-action-handler";
+import type { KeyboardActionDefinition } from "@/keyboard/keyboard-action-dispatcher";
import { useCreateFlowStore } from "@/stores/create-flow-store";
import {
buildWorkspaceOpenIntentParam,
@@ -363,6 +365,7 @@ function WorkspaceScreenContent({
const isDarkMode = useColorScheme() === "dark";
const mainBackgroundColor = isDarkMode ? theme.colors.surface1 : theme.colors.surface0;
const toast = useToast();
+ const isScreenFocused = useIsFocused();
const isMobile =
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
@@ -651,12 +654,6 @@ function WorkspaceScreenContent({
const retargetWorkspaceTab = useWorkspaceTabsStore((state) => state.retargetTab);
const reorderWorkspaceTabs = useWorkspaceTabsStore((state) => state.reorderTabs);
const pendingByDraftId = useCreateFlowStore((state) => state.pendingByDraftId);
- const workspaceTabActionRequest = useKeyboardShortcutsStore(
- (state) => state.workspaceTabActionRequest
- );
- const clearWorkspaceTabActionRequest = useKeyboardShortcutsStore(
- (state) => state.clearWorkspaceTabActionRequest
- );
const consumedOpenIntentsRef = useRef(new Set());
const pendingCloseTabIdsRef = useRef(new Set());
const [resolvedOpenIntentKey, setResolvedOpenIntentKey] = useState(null);
@@ -1386,59 +1383,56 @@ function WorkspaceScreenContent({
[handleBulkCloseTabs, tabs]
);
- useEffect(() => {
- if (!workspaceTabActionRequest) {
- return;
- }
- if (
- workspaceTabActionRequest.serverId !== normalizedServerId ||
- workspaceTabActionRequest.workspaceId !== normalizedWorkspaceId
- ) {
- return;
- }
-
- const request = workspaceTabActionRequest;
- clearWorkspaceTabActionRequest(request.id);
-
- if (request.kind === "new") {
- handleCreateDraftTab();
- return;
- }
- if (request.kind === "close-current") {
- if (activeTabId) {
- void handleCloseTabById(activeTabId);
- }
- return;
- }
- if (request.kind === "navigate-index") {
- const next = tabs[request.index - 1] ?? null;
- if (next?.tabId) {
- navigateToTabId(next.tabId);
- }
- return;
- }
- if (request.kind === "navigate-relative") {
- if (tabs.length > 0) {
- const currentIndex = tabs.findIndex((tab) => tab.tabId === activeTabId);
- const fromIndex = currentIndex >= 0 ? currentIndex : 0;
- const nextIndex = (fromIndex + request.delta + tabs.length) % tabs.length;
- const next = tabs[nextIndex] ?? null;
- if (next?.tabId) {
- navigateToTabId(next.tabId);
+ const handleWorkspaceTabAction = useCallback(
+ (action: KeyboardActionDefinition): boolean => {
+ switch (action.id) {
+ case "workspace.tab.new":
+ handleCreateDraftTab();
+ return true;
+ case "workspace.tab.close-current":
+ if (activeTabId) {
+ void handleCloseTabById(activeTabId);
+ }
+ return true;
+ case "workspace.tab.navigate-index": {
+ const next = tabs[action.index - 1] ?? null;
+ if (next?.tabId) {
+ navigateToTabId(next.tabId);
+ }
+ return true;
}
+ case "workspace.tab.navigate-relative": {
+ if (tabs.length > 0) {
+ const currentIndex = tabs.findIndex((tab) => tab.tabId === activeTabId);
+ const fromIndex = currentIndex >= 0 ? currentIndex : 0;
+ const nextIndex = (fromIndex + action.delta + tabs.length) % tabs.length;
+ const next = tabs[nextIndex] ?? null;
+ if (next?.tabId) {
+ navigateToTabId(next.tabId);
+ }
+ }
+ return true;
+ }
+ default:
+ return false;
}
- }
- }, [
- activeTabId,
- clearWorkspaceTabActionRequest,
- handleCloseTabById,
- handleCreateDraftTab,
- navigateToTabId,
- normalizedServerId,
- normalizedWorkspaceId,
- tabs,
- workspaceTabActionRequest,
- ]);
+ },
+ [activeTabId, handleCloseTabById, handleCreateDraftTab, navigateToTabId, tabs]
+ );
+
+ useKeyboardActionHandler({
+ handlerId: `workspace-tab-actions:${normalizedServerId}:${normalizedWorkspaceId}`,
+ actions: [
+ "workspace.tab.new",
+ "workspace.tab.close-current",
+ "workspace.tab.navigate-index",
+ "workspace.tab.navigate-relative",
+ ] as const,
+ enabled: Boolean(normalizedServerId && normalizedWorkspaceId),
+ priority: 100,
+ isActive: () => isScreenFocused,
+ handle: handleWorkspaceTabAction,
+ });
const renderContent = () => {
if (
@@ -1523,7 +1517,7 @@ function WorkspaceScreenContent({
);
}
- return (
+ return isScreenFocused ? (
+ ) : (
+
);
};
@@ -2069,6 +2065,11 @@ const styles = StyleSheet.create((theme) => ({
minHeight: 0,
backgroundColor: theme.colors.surface0,
},
+ contentPlaceholder: {
+ flex: 1,
+ minHeight: 0,
+ backgroundColor: theme.colors.surface0,
+ },
emptyState: {
flex: 1,
alignItems: "center",
diff --git a/packages/app/src/stores/keyboard-shortcuts-store.test.ts b/packages/app/src/stores/keyboard-shortcuts-store.test.ts
index 097a8d408..f9680fde9 100644
--- a/packages/app/src/stores/keyboard-shortcuts-store.test.ts
+++ b/packages/app/src/stores/keyboard-shortcuts-store.test.ts
@@ -9,33 +9,13 @@ beforeEach(() => {
cmdOrCtrlDown: false,
sidebarShortcutWorkspaceTargets: [],
visibleWorkspaceTargets: [],
- workspaceTabActionRequest: null,
- nextWorkspaceTabActionRequestId: 1,
});
});
describe("keyboard-shortcuts-store", () => {
- it("queues and clears workspace tab action requests", () => {
- useKeyboardShortcutsStore.getState().requestWorkspaceTabAction({
- serverId: "srv-1",
- workspaceId: "/repo/main",
- kind: "navigate-index",
- index: 3,
- });
-
- const request = useKeyboardShortcutsStore.getState().workspaceTabActionRequest;
- expect(request).toEqual({
- id: 1,
- serverId: "srv-1",
- workspaceId: "/repo/main",
- kind: "navigate-index",
- index: 3,
- });
-
- useKeyboardShortcutsStore.getState().clearWorkspaceTabActionRequest(999);
- expect(useKeyboardShortcutsStore.getState().workspaceTabActionRequest).not.toBeNull();
-
- useKeyboardShortcutsStore.getState().clearWorkspaceTabActionRequest(1);
- expect(useKeyboardShortcutsStore.getState().workspaceTabActionRequest).toBeNull();
+ it("toggles command center open state", () => {
+ expect(useKeyboardShortcutsStore.getState().commandCenterOpen).toBe(false);
+ useKeyboardShortcutsStore.getState().setCommandCenterOpen(true);
+ expect(useKeyboardShortcutsStore.getState().commandCenterOpen).toBe(true);
});
});
diff --git a/packages/app/src/stores/keyboard-shortcuts-store.ts b/packages/app/src/stores/keyboard-shortcuts-store.ts
index bddc4f583..d1bd3515e 100644
--- a/packages/app/src/stores/keyboard-shortcuts-store.ts
+++ b/packages/app/src/stores/keyboard-shortcuts-store.ts
@@ -1,28 +1,6 @@
import { create } from "zustand";
import type { SidebarShortcutWorkspaceTarget } from "@/utils/sidebar-shortcuts";
-export type WorkspaceTabActionRequest =
- | {
- id: number;
- serverId: string;
- workspaceId: string;
- kind: "new" | "close-current";
- }
- | {
- id: number;
- serverId: string;
- workspaceId: string;
- kind: "navigate-index";
- index: number;
- }
- | {
- id: number;
- serverId: string;
- workspaceId: string;
- kind: "navigate-relative";
- delta: 1 | -1;
- };
-
interface KeyboardShortcutsState {
commandCenterOpen: boolean;
projectPickerOpen: boolean;
@@ -33,8 +11,6 @@ interface KeyboardShortcutsState {
sidebarShortcutWorkspaceTargets: SidebarShortcutWorkspaceTarget[];
/** All visible workspace targets in top-to-bottom visual order. */
visibleWorkspaceTargets: SidebarShortcutWorkspaceTarget[];
- workspaceTabActionRequest: WorkspaceTabActionRequest | null;
- nextWorkspaceTabActionRequestId: number;
setCommandCenterOpen: (open: boolean) => void;
setProjectPickerOpen: (open: boolean) => void;
@@ -44,30 +20,10 @@ interface KeyboardShortcutsState {
setSidebarShortcutWorkspaceTargets: (targets: SidebarShortcutWorkspaceTarget[]) => void;
setVisibleWorkspaceTargets: (targets: SidebarShortcutWorkspaceTarget[]) => void;
resetModifiers: () => void;
-
- requestWorkspaceTabAction: (input:
- | {
- serverId: string;
- workspaceId: string;
- kind: "new" | "close-current";
- }
- | {
- serverId: string;
- workspaceId: string;
- kind: "navigate-index";
- index: number;
- }
- | {
- serverId: string;
- workspaceId: string;
- kind: "navigate-relative";
- delta: 1 | -1;
- }) => void;
- clearWorkspaceTabActionRequest: (id: number) => void;
}
export const useKeyboardShortcutsStore = create(
- (set, get) => ({
+ (set) => ({
commandCenterOpen: false,
projectPickerOpen: false,
shortcutsDialogOpen: false,
@@ -75,8 +31,6 @@ export const useKeyboardShortcutsStore = create(
cmdOrCtrlDown: false,
sidebarShortcutWorkspaceTargets: [],
visibleWorkspaceTargets: [],
- workspaceTabActionRequest: null,
- nextWorkspaceTabActionRequestId: 1,
setCommandCenterOpen: (open) => set({ commandCenterOpen: open }),
setProjectPickerOpen: (open) => set({ projectPickerOpen: open }),
@@ -87,23 +41,5 @@ export const useKeyboardShortcutsStore = create(
set({ sidebarShortcutWorkspaceTargets: targets }),
setVisibleWorkspaceTargets: (targets) => set({ visibleWorkspaceTargets: targets }),
resetModifiers: () => set({ altDown: false, cmdOrCtrlDown: false }),
-
- requestWorkspaceTabAction: (input) => {
- const id = get().nextWorkspaceTabActionRequestId;
- set({
- workspaceTabActionRequest: {
- ...input,
- id,
- } as WorkspaceTabActionRequest,
- nextWorkspaceTabActionRequestId: id + 1,
- });
- },
- clearWorkspaceTabActionRequest: (id) => {
- const current = get().workspaceTabActionRequest;
- if (!current || current.id !== id) {
- return;
- }
- set({ workspaceTabActionRequest: null });
- },
})
);
diff --git a/packages/app/src/terminal/runtime/terminal-debug.ts b/packages/app/src/terminal/runtime/terminal-debug.ts
deleted file mode 100644
index 8a7db84ad..000000000
--- a/packages/app/src/terminal/runtime/terminal-debug.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-type TerminalDebugGlobal = {
- __PASEO_TERMINAL_DEBUG?: boolean;
-};
-
-type TerminalDebugLogInput = {
- scope: string;
- event: string;
- details?: Record;
-};
-
-function resolveGlobalDebugFlag(): boolean | null {
- if (typeof globalThis === "undefined") {
- return null;
- }
-
- const value = (globalThis as TerminalDebugGlobal).__PASEO_TERMINAL_DEBUG;
- if (typeof value === "boolean") {
- return value;
- }
- return null;
-}
-
-export function isTerminalDebugEnabled(): boolean {
- const globalFlag = resolveGlobalDebugFlag();
- if (globalFlag !== null) {
- return globalFlag;
- }
- return process.env.NODE_ENV === "development";
-}
-
-export function terminalDebugLog(input: TerminalDebugLogInput): void {
- if (!isTerminalDebugEnabled()) {
- return;
- }
-
- const payload = input.details
- ? { ...input.details, ts: Date.now() }
- : { ts: Date.now() };
- console.log(`[terminal][${input.scope}] ${input.event}`, payload);
-}
-
-function escapeControlBytes(input: { text: string }): string {
- let output = "";
- for (const char of input.text) {
- const code = char.charCodeAt(0);
- if (code === 10) {
- output += "\\n";
- continue;
- }
- if (code === 13) {
- output += "\\r";
- continue;
- }
- if (code === 9) {
- output += "\\t";
- continue;
- }
- if (code < 32 || code === 127) {
- output += `\\x${code.toString(16).padStart(2, "0")}`;
- continue;
- }
- output += char;
- }
- return output;
-}
-
-export function summarizeTerminalText(input: {
- text: string;
- maxChars?: number;
-}): string {
- const maxChars = input.maxChars ?? 80;
- const escaped = escapeControlBytes({ text: input.text });
- if (escaped.length <= maxChars) {
- return escaped;
- }
- return `${escaped.slice(0, maxChars)}…`;
-}
diff --git a/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts b/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts
index 2d84d0b4a..3f161d5ca 100644
--- a/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts
+++ b/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts
@@ -10,7 +10,6 @@ import {
normalizeTerminalTransportKey,
shouldInterceptDomTerminalKey,
} from "@/utils/terminal-keys";
-import { summarizeTerminalText, terminalDebugLog } from "./terminal-debug";
export type TerminalEmulatorRuntimeMountInput = {
root: HTMLDivElement;
@@ -126,13 +125,6 @@ export class TerminalEmulatorRuntime {
}
mount(input: TerminalEmulatorRuntimeMountInput): void {
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "mount:start",
- details: {
- initialOutputLength: input.initialOutputText.length,
- },
- });
this.unmount();
input.host.innerHTML = "";
@@ -219,15 +211,6 @@ export class TerminalEmulatorRuntime {
}
this.lastSize = { rows: nextRows, cols: nextCols };
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "resize:emit",
- details: {
- rows: nextRows,
- cols: nextCols,
- force,
- },
- });
this.callbacks.onResize?.({
rows: nextRows,
cols: nextCols,
@@ -239,24 +222,8 @@ export class TerminalEmulatorRuntime {
const inputDisposable = terminal.onData((data) => {
if (this.suppressInput) {
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "input:suppressed",
- details: {
- length: data.length,
- preview: summarizeTerminalText({ text: data, maxChars: 64 }),
- },
- });
return;
}
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "input:onData",
- details: {
- length: data.length,
- preview: summarizeTerminalText({ text: data, maxChars: 64 }),
- },
- });
this.callbacks.onInput?.(data);
});
@@ -292,17 +259,6 @@ export class TerminalEmulatorRuntime {
key: normalizeTerminalTransportKey(normalizedKey),
...modifiers,
});
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "key:intercepted",
- details: {
- key: normalizedKey,
- ctrl: modifiers.ctrl,
- shift: modifiers.shift,
- alt: modifiers.alt,
- meta: modifiers.meta,
- },
- });
if (this.pendingModifiers.ctrl || this.pendingModifiers.shift || this.pendingModifiers.alt) {
this.callbacks.onPendingModifiersConsumed?.();
@@ -368,17 +324,6 @@ export class TerminalEmulatorRuntime {
if (input.initialOutputText.length > 0) {
this.write({ text: input.initialOutputText, suppressInput: true });
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "output:initial-write",
- details: {
- length: input.initialOutputText.length,
- preview: summarizeTerminalText({
- text: input.initialOutputText,
- maxChars: 96,
- }),
- },
- });
}
this.processOutputQueue();
@@ -466,15 +411,6 @@ export class TerminalEmulatorRuntime {
suppressInput: input.suppressInput ?? false,
...(input.onCommitted ? { onCommitted: input.onCommitted } : {}),
});
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "output:enqueue",
- details: {
- chunkLength: input.text.length,
- queueLength: this.outputOperations.length,
- preview: summarizeTerminalText({ text: input.text, maxChars: 64 }),
- },
- });
this.processOutputQueue();
}
@@ -485,10 +421,6 @@ export class TerminalEmulatorRuntime {
suppressInput: false,
...(input?.onCommitted ? { onCommitted: input.onCommitted } : {}),
});
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "output:clear",
- });
this.processOutputQueue();
}
@@ -504,10 +436,6 @@ export class TerminalEmulatorRuntime {
try {
terminal.options.theme = input.theme;
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "theme:update",
- });
} catch {
// ignore
return;
@@ -525,13 +453,6 @@ export class TerminalEmulatorRuntime {
}
unmount(): void {
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "mount:unmount",
- details: {
- pendingWriteOperations: this.outputOperations.length,
- },
- });
this.clearInFlightOutputTimeout();
const inFlightOperation = this.inFlightOutputOperation;
this.inFlightOutputOperation = null;
@@ -593,14 +514,6 @@ export class TerminalEmulatorRuntime {
}
const text = operation.text;
- terminalDebugLog({
- scope: "emulator-runtime",
- event: "output:flush",
- details: {
- length: text.length,
- preview: summarizeTerminalText({ text, maxChars: 96 }),
- },
- });
this.inFlightOutputOperationTimeout = setTimeout(() => {
finalizeOperation(operation);
}, OUTPUT_OPERATION_TIMEOUT_MS);
diff --git a/packages/app/src/terminal/runtime/terminal-output-delivery-queue.ts b/packages/app/src/terminal/runtime/terminal-output-delivery-queue.ts
index 140188934..51b0c290c 100644
--- a/packages/app/src/terminal/runtime/terminal-output-delivery-queue.ts
+++ b/packages/app/src/terminal/runtime/terminal-output-delivery-queue.ts
@@ -1,5 +1,3 @@
-import { summarizeTerminalText, terminalDebugLog } from "./terminal-debug";
-
export type TerminalOutputDeliveryChunk = {
sequence: number;
text: string;
@@ -37,13 +35,6 @@ export class TerminalOutputDeliveryQueue {
if (chunk.text.length === 0) {
this.pendingChunks.length = 0;
this.pendingChunks.push(chunk);
- terminalDebugLog({
- scope: "output-delivery-queue",
- event: "enqueue:clear",
- details: {
- sequence: chunk.sequence,
- },
- });
this.tryDeliver();
return;
}
@@ -59,39 +50,14 @@ export class TerminalOutputDeliveryQueue {
} else {
this.pendingChunks.push(chunk);
}
- terminalDebugLog({
- scope: "output-delivery-queue",
- event: "enqueue:text",
- details: {
- sequence: chunk.sequence,
- pendingCount: this.pendingChunks.length,
- textLength: chunk.text.length,
- preview: summarizeTerminalText({ text: chunk.text, maxChars: 80 }),
- },
- });
this.tryDeliver();
}
consume(input: { sequence: number }): void {
if (this.inFlightChunk?.sequence !== input.sequence) {
- terminalDebugLog({
- scope: "output-delivery-queue",
- event: "consume:ignored",
- details: {
- sequence: input.sequence,
- inFlightSequence: this.inFlightChunk?.sequence ?? null,
- },
- });
return;
}
- terminalDebugLog({
- scope: "output-delivery-queue",
- event: "consume",
- details: {
- sequence: input.sequence,
- },
- });
this.clearInFlightTimeout();
this.inFlightChunk = null;
this.tryDeliver();
@@ -114,14 +80,6 @@ export class TerminalOutputDeliveryQueue {
}
this.inFlightChunk = nextChunk;
- terminalDebugLog({
- scope: "output-delivery-queue",
- event: "deliver:start",
- details: {
- sequence: nextChunk.sequence,
- pendingCount: this.pendingChunks.length,
- },
- });
this.deliverInFlightChunk();
}
@@ -136,15 +94,6 @@ export class TerminalOutputDeliveryQueue {
if (!this.inFlightChunk) {
return;
}
- terminalDebugLog({
- scope: "output-delivery-queue",
- event: "deliver:timeout-retry",
- details: {
- sequence: this.inFlightChunk.sequence,
- pendingCount: this.pendingChunks.length,
- timeoutMs: this.deliveryTimeoutMs,
- },
- });
this.deliverInFlightChunk();
}, this.deliveryTimeoutMs);
diff --git a/packages/app/src/terminal/runtime/terminal-output-pump.ts b/packages/app/src/terminal/runtime/terminal-output-pump.ts
index f1834005c..22b80abfe 100644
--- a/packages/app/src/terminal/runtime/terminal-output-pump.ts
+++ b/packages/app/src/terminal/runtime/terminal-output-pump.ts
@@ -4,7 +4,6 @@ import {
readTerminalOutputBuffer,
type TerminalOutputBuffer,
} from "@/utils/terminal-output-buffer";
-import { summarizeTerminalText, terminalDebugLog } from "./terminal-debug";
export type TerminalOutputChunk = {
sequence: number;
@@ -54,14 +53,6 @@ export class TerminalOutputPump {
return;
}
- terminalDebugLog({
- scope: "output-pump",
- event: "selected-terminal:set",
- details: {
- previousTerminalId: this.selectedTerminalId,
- nextTerminalId: input.terminalId,
- },
- });
this.clearSelectedChunkFlushTimer();
this.selectedChunkAccumulator = "";
this.selectedChunkAccumulatorReplay = null;
@@ -102,17 +93,6 @@ export class TerminalOutputPump {
}
this.selectedChunkAccumulator += input.text;
- terminalDebugLog({
- scope: "output-pump",
- event: "selected-terminal:accumulate",
- details: {
- terminalId: input.terminalId,
- appendedLength: input.text.length,
- accumulatorLength: this.selectedChunkAccumulator.length,
- replay: input.replay,
- preview: summarizeTerminalText({ text: input.text, maxChars: 80 }),
- },
- });
this.scheduleSelectedChunkFlush();
}
@@ -173,16 +153,6 @@ export class TerminalOutputPump {
const replay = this.selectedChunkAccumulatorReplay ?? false;
this.selectedChunkAccumulator = "";
this.selectedChunkAccumulatorReplay = null;
- terminalDebugLog({
- scope: "output-pump",
- event: "selected-terminal:flush",
- details: {
- terminalId: this.selectedTerminalId,
- textLength: text.length,
- replay,
- preview: summarizeTerminalText({ text, maxChars: 96 }),
- },
- });
this.emitSelectedChunk({ text, replay });
}
diff --git a/packages/app/src/terminal/runtime/terminal-output-session.ts b/packages/app/src/terminal/runtime/terminal-output-session.ts
index 5fa31148a..fc09e3d52 100644
--- a/packages/app/src/terminal/runtime/terminal-output-session.ts
+++ b/packages/app/src/terminal/runtime/terminal-output-session.ts
@@ -6,7 +6,6 @@ import {
TerminalOutputDeliveryQueue,
type TerminalOutputDeliveryChunk,
} from "./terminal-output-delivery-queue";
-import { summarizeTerminalText, terminalDebugLog } from "./terminal-debug";
type TerminalOutputState = {
selectedTerminalId: string | null;
@@ -75,18 +74,6 @@ export class TerminalOutputSession {
chunkSequence: chunk.sequence,
chunkReplay: chunk.replay,
};
- terminalDebugLog({
- scope: "output-session",
- event: "deliver",
- details: {
- selectedTerminalId: this.state.selectedTerminalId,
- sequence: chunk.sequence,
- replay: chunk.replay,
- chunkLength: chunk.text.length,
- snapshotLength: snapshotText.length,
- preview: summarizeTerminalText({ text: chunk.text, maxChars: 80 }),
- },
- });
this.emit();
},
});
@@ -121,14 +108,6 @@ export class TerminalOutputSession {
chunkSequence: 0,
chunkReplay: false,
};
- terminalDebugLog({
- scope: "output-session",
- event: "selected-terminal:set",
- details: {
- selectedTerminalId: input.terminalId,
- snapshotLength: snapshotText.length,
- },
- });
this.emit();
}
@@ -154,13 +133,6 @@ export class TerminalOutputSession {
chunkSequence: 0,
chunkReplay: false,
};
- terminalDebugLog({
- scope: "output-session",
- event: "selected-terminal:clear",
- details: {
- terminalId: input.terminalId,
- },
- });
this.emit();
}
@@ -208,14 +180,6 @@ export function releaseTerminalOutputSession(input: { scopeKey: string }): void
if (current <= 1) {
sessionRefCountByScopeKey.delete(input.scopeKey);
sessionsByScopeKey.delete(input.scopeKey);
- terminalDebugLog({
- scope: "output-session",
- event: "scope:release",
- details: {
- scopeKey: input.scopeKey,
- released: true,
- },
- });
return;
}
diff --git a/packages/app/src/terminal/runtime/terminal-stream-controller.ts b/packages/app/src/terminal/runtime/terminal-stream-controller.ts
index 7920863e8..92de68525 100644
--- a/packages/app/src/terminal/runtime/terminal-stream-controller.ts
+++ b/packages/app/src/terminal/runtime/terminal-stream-controller.ts
@@ -7,7 +7,6 @@ import {
waitForDuration,
withPromiseTimeout,
} from "@/utils/terminal-attach";
-import { summarizeTerminalText, terminalDebugLog } from "./terminal-debug";
export type TerminalStreamControllerAttachPayload = {
streamId: number | null;
@@ -114,14 +113,6 @@ export class TerminalStreamController {
if (this.isDisposed) {
return;
}
- terminalDebugLog({
- scope: "stream-controller",
- event: "terminal:set",
- details: {
- previousTerminalId: this.selectedTerminalId,
- nextTerminalId: input.terminalId,
- },
- });
const nextTerminalId = input.terminalId;
const previousTerminalId = this.selectedTerminalId;
@@ -166,14 +157,6 @@ export class TerminalStreamController {
if (this.isDisposed) {
return;
}
- terminalDebugLog({
- scope: "stream-controller",
- event: "stream:exit",
- details: {
- terminalId: input.terminalId,
- streamId: input.streamId,
- },
- });
if (this.selectedTerminalId !== input.terminalId) {
return;
@@ -256,16 +239,6 @@ export class TerminalStreamController {
if (!this.isAttachGenerationCurrent({ generation: input.generation, terminalId: input.terminalId })) {
return;
}
- terminalDebugLog({
- scope: "stream-controller",
- event: "attach:attempt",
- details: {
- terminalId: input.terminalId,
- generation: input.generation,
- attempt,
- maxAttachAttempts,
- },
- });
try {
const preferredSize = this.options.getPreferredSize();
@@ -299,15 +272,6 @@ export class TerminalStreamController {
if (attachPayload.error || typeof attachPayload.streamId !== "number") {
lastErrorMessage = attachPayload.error ?? "Unable to attach terminal stream";
- terminalDebugLog({
- scope: "stream-controller",
- event: "attach:response-error",
- details: {
- terminalId: input.terminalId,
- attempt,
- error: lastErrorMessage,
- },
- });
const hasRemainingAttempts = attempt < maxAttachAttempts - 1;
if (hasRemainingAttempts && isRetryableError({ message: lastErrorMessage })) {
await waitForDelay({ durationMs: getRetryDelayMs({ attempt }) });
@@ -385,19 +349,6 @@ export class TerminalStreamController {
} else {
unsubscribe();
}
- terminalDebugLog({
- scope: "stream-controller",
- event: "attach:success",
- details: {
- terminalId: input.terminalId,
- streamId,
- replayedFrom: attachPayload.replayedFrom ?? null,
- requestedResumeOffset: resumeOffset ?? null,
- currentOffset: attachPayload.currentOffset,
- reset: attachPayload.reset,
- bootstrapReset: shouldResetForReplayBootstrap,
- },
- });
this.updateStatus({
terminalId: input.terminalId,
streamId,
@@ -408,15 +359,6 @@ export class TerminalStreamController {
} catch (error) {
lastErrorMessage =
error instanceof Error ? error.message : "Unable to attach terminal stream";
- terminalDebugLog({
- scope: "stream-controller",
- event: "attach:exception",
- details: {
- terminalId: input.terminalId,
- attempt,
- error: lastErrorMessage,
- },
- });
const hasRemainingAttempts = attempt < maxAttachAttempts - 1;
if (hasRemainingAttempts && isRetryableError({ message: lastErrorMessage })) {
await waitForDelay({ durationMs: getRetryDelayMs({ attempt }) });
@@ -510,21 +452,6 @@ export class TerminalStreamController {
if (text.length === 0) {
return;
}
- terminalDebugLog({
- scope: "stream-controller",
- event: "stream:chunk",
- details: {
- terminalId: input.terminalId,
- streamId: input.streamId,
- offset: chunkOffset,
- endOffset: chunkEndOffset,
- replay: Boolean(input.chunk.replay),
- byteLength: input.chunk.data.byteLength,
- textLength: text.length,
- preview: summarizeTerminalText({ text, maxChars: 96 }),
- },
- });
-
this.options.onChunk({
terminalId: input.terminalId,
text,
@@ -577,15 +504,6 @@ export class TerminalStreamController {
if (!activeStream) {
return;
}
- terminalDebugLog({
- scope: "stream-controller",
- event: "stream:detach",
- details: {
- terminalId: activeStream.terminalId,
- streamId: activeStream.streamId,
- shouldDetach: input.shouldDetach,
- },
- });
this.activeStream = null;
try {
@@ -633,16 +551,6 @@ export class TerminalStreamController {
private updateStatus(status: TerminalStreamControllerStatus): void {
this.status = status;
- terminalDebugLog({
- scope: "stream-controller",
- event: "status:update",
- details: {
- terminalId: status.terminalId,
- streamId: status.streamId,
- isAttaching: status.isAttaching,
- error: status.error,
- },
- });
this.options.onStatusChange?.(status);
}
diff --git a/packages/desktop/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock
index b47154956..469c03702 100644
--- a/packages/desktop/src-tauri/Cargo.lock
+++ b/packages/desktop/src-tauri/Cargo.lock
@@ -2629,7 +2629,7 @@ dependencies = [
[[package]]
name = "paseo"
-version = "0.1.27"
+version = "0.1.28"
dependencies = [
"base64 0.22.1",
"dirs",