refactor(surface): drop the deprecated main-tab aliases and dead diagram surface
MainTab/activeMainTab/setActiveMainTab/setMainTabGuard were deprecated mirrors of the surface names — every call site now uses activeSurface/setActiveSurface/setSurfaceGuard directly and the aliases are gone, including the persisted mirror field. The 'diagram' surface had no way to open it (navigateToDiagram had no callers except a .drawio attachment click that navigated to a surface nothing rendered); the surface, DiagramView, and its store plumbing are removed, and a .drawio attachment now opens in the file panel. ?tab= deep links map to the matching context-panel surface instead of setting a main-area surface nothing renders, and a persisted non-chat surface can no longer rehydrate into a blank main area.
This commit is contained in:
@@ -626,7 +626,7 @@ function App({ apis }: AppProps) {
|
|||||||
const directory = typeof detail?.directory === 'string' && detail.directory.trim().length > 0
|
const directory = typeof detail?.directory === 'string' && detail.directory.trim().length > 0
|
||||||
? detail.directory.trim()
|
? detail.directory.trim()
|
||||||
: null;
|
: null;
|
||||||
useUIStore.getState().setActiveMainTab('chat');
|
useUIStore.getState().setActiveSurface('chat');
|
||||||
void useSessionUIStore.getState().setCurrentSession(sessionId, directory);
|
void useSessionUIStore.getState().setCurrentSession(sessionId, directory);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -675,7 +675,7 @@ function App({ apis }: AppProps) {
|
|||||||
? detail.projectId.trim()
|
? detail.projectId.trim()
|
||||||
: null;
|
: null;
|
||||||
const hasProjectTarget = Boolean(directory || projectId);
|
const hasProjectTarget = Boolean(directory || projectId);
|
||||||
useUIStore.getState().setActiveMainTab('chat');
|
useUIStore.getState().setActiveSurface('chat');
|
||||||
useUIStore.getState().setSessionSwitcherOpen(false);
|
useUIStore.getState().setSessionSwitcherOpen(false);
|
||||||
useSessionUIStore.getState().openNewSessionDraft({
|
useSessionUIStore.getState().openNewSessionDraft({
|
||||||
target: hasProjectTarget ? 'project' : 'chat',
|
target: hasProjectTarget ? 'project' : 'chat',
|
||||||
|
|||||||
@@ -1006,8 +1006,8 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { activeMainTab } = useUIStore.getState();
|
const { activeSurface } = useUIStore.getState();
|
||||||
if (activeMainTab !== 'chat' || hasBlockingChatOverlay()) {
|
if (activeSurface !== 'chat' || hasBlockingChatOverlay()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useRef, memo } from 'react';
|
|||||||
import { useInputStore } from '@/sync/input-store';
|
import { useInputStore } from '@/sync/input-store';
|
||||||
import type { AttachedFile } from '@/sync/session-ui-store';
|
import type { AttachedFile } from '@/sync/session-ui-store';
|
||||||
import { useUIStore } from '@/stores/useUIStore';
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
|
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||||
import { toast } from '@/components/ui';
|
import { toast } from '@/components/ui';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { openExternalUrl } from '@/lib/url';
|
import { openExternalUrl } from '@/lib/url';
|
||||||
@@ -833,7 +834,10 @@ export const MessageFilesDisplay = memo(({ files, onShowPopup, compact = false }
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
useUIStore.getState().navigateToDiagram(filePath);
|
const directory = useDirectoryStore.getState().currentDirectory;
|
||||||
|
if (directory) {
|
||||||
|
useUIStore.getState().openContextFile(directory, filePath);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-2 p-2 rounded-lg border border-border/40 bg-muted/10 hover:bg-muted/20 transition-colors text-left cursor-pointer",
|
"flex items-center gap-2 p-2 rounded-lg border border-border/40 bg-muted/10 hover:bg-muted/20 transition-colors text-left cursor-pointer",
|
||||||
|
|||||||
@@ -434,7 +434,7 @@ export const Header: React.FC = () => {
|
|||||||
const openContextOverview = useUIStore((state) => state.openContextOverview);
|
const openContextOverview = useUIStore((state) => state.openContextOverview);
|
||||||
const openContextPlan = useUIStore((state) => state.openContextPlan);
|
const openContextPlan = useUIStore((state) => state.openContextPlan);
|
||||||
const closeContextPanel = useUIStore((state) => state.closeContextPanel);
|
const closeContextPanel = useUIStore((state) => state.closeContextPanel);
|
||||||
const activeMainTab = useUIStore((state) => state.activeMainTab);
|
const activeSurface = useUIStore((state) => state.activeSurface);
|
||||||
const shortcutOverrides = useUIStore((state) => state.shortcutOverrides);
|
const shortcutOverrides = useUIStore((state) => state.shortcutOverrides);
|
||||||
|
|
||||||
const getCurrentModel = useConfigStore((state) => state.getCurrentModel);
|
const getCurrentModel = useConfigStore((state) => state.getCurrentModel);
|
||||||
@@ -612,7 +612,7 @@ export const Header: React.FC = () => {
|
|||||||
}, [setWorkStatusOverlayOpen, setWorkStatusPanelEnabled, workStatusOverlayOpen, workStatusPanelEnabled, workStatusPanelFits]);
|
}, [setWorkStatusOverlayOpen, setWorkStatusPanelEnabled, workStatusOverlayOpen, workStatusPanelEnabled, workStatusPanelFits]);
|
||||||
const showDesktopHeaderContextUsage = !isVSCode
|
const showDesktopHeaderContextUsage = !isVSCode
|
||||||
&& !workStatusPanelVisible
|
&& !workStatusPanelVisible
|
||||||
&& activeMainTab === 'chat'
|
&& activeSurface === 'chat'
|
||||||
&& !!stableDesktopContextUsage
|
&& !!stableDesktopContextUsage
|
||||||
&& stableDesktopContextUsage.totalTokens > 0;
|
&& stableDesktopContextUsage.totalTokens > 0;
|
||||||
const desktopHeaderDisplayPercentage = stableDesktopContextUsage && stableDesktopContextUsage.contextLimit > 0
|
const desktopHeaderDisplayPercentage = stableDesktopContextUsage && stableDesktopContextUsage.contextLimit > 0
|
||||||
@@ -1153,8 +1153,8 @@ export const Header: React.FC = () => {
|
|||||||
// Reset plan tab availability when session changes
|
// Reset plan tab availability when session changes
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!planModeEnabled) {
|
if (!planModeEnabled) {
|
||||||
if (useUIStore.getState().activeMainTab === 'plan') {
|
if (useUIStore.getState().activeSurface === 'plan') {
|
||||||
useUIStore.getState().setActiveMainTab('chat');
|
useUIStore.getState().setActiveSurface('chat');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1167,8 +1167,8 @@ export const Header: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If plan is not available but user is on plan tab, switch them back to chat
|
// If plan is not available but user is on plan tab, switch them back to chat
|
||||||
if (!planTabAvailable && useUIStore.getState().activeMainTab === 'plan') {
|
if (!planTabAvailable && useUIStore.getState().activeSurface === 'plan') {
|
||||||
useUIStore.getState().setActiveMainTab('chat');
|
useUIStore.getState().setActiveSurface('chat');
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
planModeEnabled,
|
planModeEnabled,
|
||||||
@@ -1759,7 +1759,7 @@ export const Header: React.FC = () => {
|
|||||||
className={cn(desktopHeaderIconButtonClass, 'mr-1')}
|
className={cn(desktopHeaderIconButtonClass, 'mr-1')}
|
||||||
Icon={'picture-in-picture-2'}
|
Icon={'picture-in-picture-2'}
|
||||||
/>
|
/>
|
||||||
{activeMainTab === 'chat' && !isVSCode ? (
|
{activeSurface === 'chat' && !isVSCode ? (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
|
|||||||
const homeDirectory = useDirectoryStore((s) => s.homeDirectory);
|
const homeDirectory = useDirectoryStore((s) => s.homeDirectory);
|
||||||
const projects = useProjectsStore((s) => s.projects);
|
const projects = useProjectsStore((s) => s.projects);
|
||||||
const addProject = useProjectsStore((s) => s.addProject);
|
const addProject = useProjectsStore((s) => s.addProject);
|
||||||
const setActiveMainTab = useUIStore((s) => s.setActiveMainTab);
|
const setActiveSurface = useUIStore((s) => s.setActiveSurface);
|
||||||
const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen);
|
const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen);
|
||||||
const openNewSessionDraft = useSessionUIStore((s) => s.openNewSessionDraft);
|
const openNewSessionDraft = useSessionUIStore((s) => s.openNewSessionDraft);
|
||||||
const gitIdentityProfiles = useGitIdentitiesStore((s) => s.profiles);
|
const gitIdentityProfiles = useGitIdentitiesStore((s) => s.profiles);
|
||||||
@@ -411,11 +411,11 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
|
|||||||
}, [onOpenChange]);
|
}, [onOpenChange]);
|
||||||
|
|
||||||
const openProjectDraft = React.useCallback((projectId: string, projectPath: string) => {
|
const openProjectDraft = React.useCallback((projectId: string, projectPath: string) => {
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
if (isMobile) setSessionSwitcherOpen(false);
|
if (isMobile) setSessionSwitcherOpen(false);
|
||||||
openNewSessionDraft({ selectedProjectId: projectId, directoryOverride: projectPath });
|
openNewSessionDraft({ selectedProjectId: projectId, directoryOverride: projectPath });
|
||||||
handleClose();
|
handleClose();
|
||||||
}, [handleClose, isMobile, openNewSessionDraft, setActiveMainTab, setSessionSwitcherOpen]);
|
}, [handleClose, isMobile, openNewSessionDraft, setActiveSurface, setSessionSwitcherOpen]);
|
||||||
|
|
||||||
const handleQuickAdd = React.useCallback((event: React.MouseEvent, path: string) => {
|
const handleQuickAdd = React.useCallback((event: React.MouseEvent, path: string) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ export function ScheduledTasksDialog() {
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
useFilesViewTabsStore.getState().setSelectedPath(selectedProject.path, task.loopFile, { allowOutsideRoot: true });
|
useFilesViewTabsStore.getState().setSelectedPath(selectedProject.path, task.loopFile, { allowOutsideRoot: true });
|
||||||
useUIStore.getState().setActiveMainTab('files');
|
useUIStore.getState().setActiveSurface('files');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
useUIStore.getState().openContextFile(selectedProject.path, task.loopFile);
|
useUIStore.getState().openContextFile(selectedProject.path, task.loopFile);
|
||||||
@@ -397,7 +397,7 @@ export function ScheduledTasksDialog() {
|
|||||||
// this surface (MainLayout closes surfaces on session selection).
|
// this surface (MainLayout closes surfaces on session selection).
|
||||||
const project = projects.find((entry) => entry.id === selectedProjectID);
|
const project = projects.find((entry) => entry.id === selectedProjectID);
|
||||||
useSessionUIStore.getState().setCurrentSession(sessionId, project?.path ?? null);
|
useSessionUIStore.getState().setCurrentSession(sessionId, project?.path ?? null);
|
||||||
useUIStore.getState().setActiveMainTab('chat');
|
useUIStore.getState().setActiveSurface('chat');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error instanceof Error ? error.message : t('sessions.scheduledTasks.dialog.toast.runFailed'));
|
toast.error(error instanceof Error ? error.message : t('sessions.scheduledTasks.dialog.toast.runFailed'));
|
||||||
|
|||||||
@@ -391,7 +391,7 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
const updateProjectMeta = useProjectsStore((state) => state.updateProjectMeta);
|
const updateProjectMeta = useProjectsStore((state) => state.updateProjectMeta);
|
||||||
const reorderProjects = useProjectsStore((state) => state.reorderProjects);
|
const reorderProjects = useProjectsStore((state) => state.reorderProjects);
|
||||||
|
|
||||||
const setActiveMainTab = useUIStore((state) => state.setActiveMainTab);
|
const setActiveSurface = useUIStore((state) => state.setActiveSurface);
|
||||||
const openContextPanelTab = useUIStore((state) => state.openContextPanelTab);
|
const openContextPanelTab = useUIStore((state) => state.openContextPanelTab);
|
||||||
const setSettingsDialogOpen = useUIStore((state) => state.setSettingsDialogOpen);
|
const setSettingsDialogOpen = useUIStore((state) => state.setSettingsDialogOpen);
|
||||||
const toggleHelpDialog = useUIStore((state) => state.toggleHelpDialog);
|
const toggleHelpDialog = useUIStore((state) => state.toggleHelpDialog);
|
||||||
@@ -848,7 +848,7 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
sessionSearchQuery,
|
sessionSearchQuery,
|
||||||
setSessionSearchQuery,
|
setSessionSearchQuery,
|
||||||
setIsSessionSearchOpen,
|
setIsSessionSearchOpen,
|
||||||
setActiveMainTab,
|
setActiveSurface,
|
||||||
setSessionSwitcherOpen,
|
setSessionSwitcherOpen,
|
||||||
setCurrentSession,
|
setCurrentSession,
|
||||||
updateSessionTitle,
|
updateSessionTitle,
|
||||||
@@ -1698,7 +1698,7 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
alwaysShowActions={alwaysShowSidebarActions}
|
alwaysShowActions={alwaysShowSidebarActions}
|
||||||
activeProjectId={activeProjectId}
|
activeProjectId={activeProjectId}
|
||||||
setActiveProjectIdOnly={setActiveProjectIdOnly}
|
setActiveProjectIdOnly={setActiveProjectIdOnly}
|
||||||
setActiveMainTab={setActiveMainTab}
|
setActiveSurface={setActiveSurface}
|
||||||
setSessionSwitcherOpen={setSessionSwitcherOpen}
|
setSessionSwitcherOpen={setSessionSwitcherOpen}
|
||||||
openNewSessionDraft={openNewSessionDraftFromTree}
|
openNewSessionDraft={openNewSessionDraftFromTree}
|
||||||
addSessionToFolder={addSessionToFolder}
|
addSessionToFolder={addSessionToFolder}
|
||||||
@@ -1741,7 +1741,7 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
alwaysShowSidebarActions,
|
alwaysShowSidebarActions,
|
||||||
activeProjectId,
|
activeProjectId,
|
||||||
setActiveProjectIdOnly,
|
setActiveProjectIdOnly,
|
||||||
setActiveMainTab,
|
setActiveSurface,
|
||||||
setSessionSwitcherOpen,
|
setSessionSwitcherOpen,
|
||||||
openNewSessionDraftFromTree,
|
openNewSessionDraftFromTree,
|
||||||
addSessionToFolder,
|
addSessionToFolder,
|
||||||
@@ -1763,12 +1763,12 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
|
|
||||||
const handleOpenNewSessionDraftFromHeader = React.useCallback(() => {
|
const handleOpenNewSessionDraftFromHeader = React.useCallback(() => {
|
||||||
useUIStore.getState().closeMainSurfaces();
|
useUIStore.getState().closeMainSurfaces();
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
if (mobileVariant) {
|
if (mobileVariant) {
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
}
|
}
|
||||||
openNewSessionDraft();
|
openNewSessionDraft();
|
||||||
}, [mobileVariant, openNewSessionDraft, setActiveMainTab, setSessionSwitcherOpen]);
|
}, [mobileVariant, openNewSessionDraft, setActiveSurface, setSessionSwitcherOpen]);
|
||||||
|
|
||||||
const renderChatsSection = React.useCallback((items: ActivityItem[]) => {
|
const renderChatsSection = React.useCallback((items: ActivityItem[]) => {
|
||||||
const chatsRoot = getChatsRootForHome(homeDirectory)
|
const chatsRoot = getChatsRootForHome(homeDirectory)
|
||||||
@@ -1850,12 +1850,12 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
setBulkDeleteConfirm,
|
setBulkDeleteConfirm,
|
||||||
});
|
});
|
||||||
const handleOpenMultiRunFromHeader = React.useCallback(() => {
|
const handleOpenMultiRunFromHeader = React.useCallback(() => {
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
if (mobileVariant) {
|
if (mobileVariant) {
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
}
|
}
|
||||||
openMultiRunLauncher();
|
openMultiRunLauncher();
|
||||||
}, [mobileVariant, openMultiRunLauncher, setActiveMainTab, setSessionSwitcherOpen]);
|
}, [mobileVariant, openMultiRunLauncher, setActiveSurface, setSessionSwitcherOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// One shared tooltip provider for the whole sidebar: session tooltips open
|
// One shared tooltip provider for the whole sidebar: session tooltips open
|
||||||
@@ -1889,7 +1889,7 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
handleSessionSelect={stableHandleSessionSelect}
|
handleSessionSelect={stableHandleSessionSelect}
|
||||||
mobileVariant={mobileVariant}
|
mobileVariant={mobileVariant}
|
||||||
openNewSessionDraft={openNewSessionDraft}
|
openNewSessionDraft={openNewSessionDraft}
|
||||||
setActiveMainTab={setActiveMainTab}
|
setActiveSurface={setActiveSurface}
|
||||||
setSessionSwitcherOpen={setSessionSwitcherOpen}
|
setSessionSwitcherOpen={setSessionSwitcherOpen}
|
||||||
sessionOwnerBySessionId={sessionOwnership.bySessionId}
|
sessionOwnerBySessionId={sessionOwnership.bySessionId}
|
||||||
/>
|
/>
|
||||||
@@ -1959,7 +1959,7 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
alwaysShowActions={alwaysShowSidebarActions}
|
alwaysShowActions={alwaysShowSidebarActions}
|
||||||
toggleProject={toggleProject}
|
toggleProject={toggleProject}
|
||||||
setActiveProjectIdOnly={setActiveProjectIdOnly}
|
setActiveProjectIdOnly={setActiveProjectIdOnly}
|
||||||
setActiveMainTab={setActiveMainTab}
|
setActiveSurface={setActiveSurface}
|
||||||
setSessionSwitcherOpen={setSessionSwitcherOpen}
|
setSessionSwitcherOpen={setSessionSwitcherOpen}
|
||||||
openNewSessionDraft={openNewSessionDraftFromTree}
|
openNewSessionDraft={openNewSessionDraftFromTree}
|
||||||
openNewWorktreeDialog={openNewWorktreeDialog}
|
openNewWorktreeDialog={openNewWorktreeDialog}
|
||||||
@@ -2033,7 +2033,7 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
open={newWorktreeDialogOpen}
|
open={newWorktreeDialogOpen}
|
||||||
onOpenChange={setNewWorktreeDialogOpen}
|
onOpenChange={setNewWorktreeDialogOpen}
|
||||||
onWorktreeCreated={(worktreePath, options) => {
|
onWorktreeCreated={(worktreePath, options) => {
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
if (mobileVariant) {
|
if (mobileVariant) {
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,14 +71,14 @@ type SwitcherContentProps = {
|
|||||||
function SwitcherContent({ onSelect, variant, scopeProjectId }: SwitcherContentProps): React.ReactElement {
|
function SwitcherContent({ onSelect, variant, scopeProjectId }: SwitcherContentProps): React.ReactElement {
|
||||||
const items = useSwitcherItems(true, { scopeProjectId });
|
const items = useSwitcherItems(true, { scopeProjectId });
|
||||||
const openNewSessionDraft = useSessionUIStore((state) => state.openNewSessionDraft);
|
const openNewSessionDraft = useSessionUIStore((state) => state.openNewSessionDraft);
|
||||||
const setActiveMainTab = useUIStore((state) => state.setActiveMainTab);
|
const setActiveSurface = useUIStore((state) => state.setActiveSurface);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const handleNewSession = React.useCallback(() => {
|
const handleNewSession = React.useCallback(() => {
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
onSelect();
|
onSelect();
|
||||||
openNewSessionDraft();
|
openNewSessionDraft();
|
||||||
}, [onSelect, openNewSessionDraft, setActiveMainTab]);
|
}, [onSelect, openNewSessionDraft, setActiveSurface]);
|
||||||
|
|
||||||
const [expandedParents, setExpandedParents] = React.useState<Set<string>>(new Set());
|
const [expandedParents, setExpandedParents] = React.useState<Set<string>>(new Set());
|
||||||
const toggleParent = React.useCallback((sessionId: string) => {
|
const toggleParent = React.useCallback((sessionId: string) => {
|
||||||
|
|||||||
@@ -44,13 +44,13 @@ export const useProjectTodoSend = (options: {
|
|||||||
const sendMessage = useSessionUIStore((state) => state.sendMessage);
|
const sendMessage = useSessionUIStore((state) => state.sendMessage);
|
||||||
const setCurrentSession = useSessionUIStore((state) => state.setCurrentSession);
|
const setCurrentSession = useSessionUIStore((state) => state.setCurrentSession);
|
||||||
const setPendingInputText = useInputStore((state) => state.setPendingInputText);
|
const setPendingInputText = useInputStore((state) => state.setPendingInputText);
|
||||||
const setActiveMainTab = useUIStore((state) => state.setActiveMainTab);
|
const setActiveSurface = useUIStore((state) => state.setActiveSurface);
|
||||||
const setSessionSwitcherOpen = useUIStore((state) => state.setSessionSwitcherOpen);
|
const setSessionSwitcherOpen = useUIStore((state) => state.setSessionSwitcherOpen);
|
||||||
|
|
||||||
const routeToChat = React.useCallback(() => {
|
const routeToChat = React.useCallback(() => {
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
}, [setActiveMainTab, setSessionSwitcherOpen]);
|
}, [setActiveSurface, setSessionSwitcherOpen]);
|
||||||
|
|
||||||
const sendToCurrentSession = React.useCallback(
|
const sendToCurrentSession = React.useCallback(
|
||||||
(todoText: string) => {
|
(todoText: string) => {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Icon } from "@/components/icon/Icon";
|
import { Icon } from "@/components/icon/Icon";
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { sessionEvents } from '@/lib/sessionEvents';
|
import { sessionEvents } from '@/lib/sessionEvents';
|
||||||
import type { MainTab } from '@/stores/useUIStore';
|
import type { WorkspaceSurface } from '@/stores/useUIStore';
|
||||||
import { SessionFolderItem } from '../SessionFolderItem';
|
import { SessionFolderItem } from '../SessionFolderItem';
|
||||||
import type { SortableDragHandleProps } from './sortableItems';
|
import type { SortableDragHandleProps } from './sortableItems';
|
||||||
import { DroppableFolderWrapper, SessionFolderDndScope } from './sessionFolderDnd';
|
import { DroppableFolderWrapper, SessionFolderDndScope } from './sessionFolderDnd';
|
||||||
@@ -84,7 +84,7 @@ type Props = {
|
|||||||
alwaysShowActions: boolean;
|
alwaysShowActions: boolean;
|
||||||
activeProjectId: string | null;
|
activeProjectId: string | null;
|
||||||
setActiveProjectIdOnly: (id: string) => void;
|
setActiveProjectIdOnly: (id: string) => void;
|
||||||
setActiveMainTab: (tab: MainTab) => void;
|
setActiveSurface: (tab: WorkspaceSurface) => void;
|
||||||
setSessionSwitcherOpen: (open: boolean) => void;
|
setSessionSwitcherOpen: (open: boolean) => void;
|
||||||
openNewSessionDraft: (options?: { selectedProjectId?: string | null; directoryOverride?: string | null; targetFolderId?: string; target?: 'chat' | 'project' }) => void;
|
openNewSessionDraft: (options?: { selectedProjectId?: string | null; directoryOverride?: string | null; targetFolderId?: string; target?: 'chat' | 'project' }) => void;
|
||||||
addSessionToFolder: (scopeKey: string, folderId: string, sessionId: string) => void;
|
addSessionToFolder: (scopeKey: string, folderId: string, sessionId: string) => void;
|
||||||
@@ -265,7 +265,7 @@ const areGroupPropsEqual = (prev: Props, next: Props): boolean => {
|
|||||||
&& prev.alwaysShowActions === next.alwaysShowActions
|
&& prev.alwaysShowActions === next.alwaysShowActions
|
||||||
&& prev.activeProjectId === next.activeProjectId
|
&& prev.activeProjectId === next.activeProjectId
|
||||||
&& prev.setActiveProjectIdOnly === next.setActiveProjectIdOnly
|
&& prev.setActiveProjectIdOnly === next.setActiveProjectIdOnly
|
||||||
&& prev.setActiveMainTab === next.setActiveMainTab
|
&& prev.setActiveSurface === next.setActiveSurface
|
||||||
&& prev.setSessionSwitcherOpen === next.setSessionSwitcherOpen
|
&& prev.setSessionSwitcherOpen === next.setSessionSwitcherOpen
|
||||||
&& prev.openNewSessionDraft === next.openNewSessionDraft
|
&& prev.openNewSessionDraft === next.openNewSessionDraft
|
||||||
&& prev.addSessionToFolder === next.addSessionToFolder
|
&& prev.addSessionToFolder === next.addSessionToFolder
|
||||||
@@ -307,7 +307,7 @@ function SessionGroupSectionBase(props: Props): React.ReactNode {
|
|||||||
alwaysShowActions,
|
alwaysShowActions,
|
||||||
activeProjectId,
|
activeProjectId,
|
||||||
setActiveProjectIdOnly,
|
setActiveProjectIdOnly,
|
||||||
setActiveMainTab,
|
setActiveSurface,
|
||||||
setSessionSwitcherOpen,
|
setSessionSwitcherOpen,
|
||||||
openNewSessionDraft,
|
openNewSessionDraft,
|
||||||
addSessionToFolder,
|
addSessionToFolder,
|
||||||
@@ -880,7 +880,7 @@ function SessionGroupSectionBase(props: Props): React.ReactNode {
|
|||||||
depth={0}
|
depth={0}
|
||||||
onNewSession={() => {
|
onNewSession={() => {
|
||||||
if (projectId && projectId !== activeProjectId) setActiveProjectIdOnly(projectId);
|
if (projectId && projectId !== activeProjectId) setActiveProjectIdOnly(projectId);
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
if (mobileVariant) setSessionSwitcherOpen(false);
|
if (mobileVariant) setSessionSwitcherOpen(false);
|
||||||
openNewSessionDraft({
|
openNewSessionDraft({
|
||||||
selectedProjectId: projectId,
|
selectedProjectId: projectId,
|
||||||
@@ -1248,7 +1248,7 @@ function SessionGroupSectionBase(props: Props): React.ReactNode {
|
|||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (projectId && projectId !== activeProjectId) setActiveProjectIdOnly(projectId);
|
if (projectId && projectId !== activeProjectId) setActiveProjectIdOnly(projectId);
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
if (mobileVariant) setSessionSwitcherOpen(false);
|
if (mobileVariant) setSessionSwitcherOpen(false);
|
||||||
openNewSessionDraft({ selectedProjectId: projectId, directoryOverride: group.directory });
|
openNewSessionDraft({ selectedProjectId: projectId, directoryOverride: group.directory });
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import type { SortableDragHandleProps } from './sortableItems';
|
|||||||
import { ProjectHeaderIdentity, SortableGroupItem, SortableProjectItem } from './sortableItems';
|
import { ProjectHeaderIdentity, SortableGroupItem, SortableProjectItem } from './sortableItems';
|
||||||
import { formatProjectLabel } from './utils';
|
import { formatProjectLabel } from './utils';
|
||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
import type { MainTab } from '@/stores/useUIStore';
|
import type { WorkspaceSurface } from '@/stores/useUIStore';
|
||||||
import type { ProjectSortOrder } from '@/stores/useSessionDisplayStore';
|
import type { ProjectSortOrder } from '@/stores/useSessionDisplayStore';
|
||||||
import { streamPerfCount } from '@/stores/utils/streamDebug';
|
import { streamPerfCount } from '@/stores/utils/streamDebug';
|
||||||
import { Icon } from '@/components/icon/Icon';
|
import { Icon } from '@/components/icon/Icon';
|
||||||
@@ -91,7 +91,7 @@ type Props = {
|
|||||||
alwaysShowActions: boolean;
|
alwaysShowActions: boolean;
|
||||||
toggleProject: (id: string) => void;
|
toggleProject: (id: string) => void;
|
||||||
setActiveProjectIdOnly: (id: string) => void;
|
setActiveProjectIdOnly: (id: string) => void;
|
||||||
setActiveMainTab: (tab: MainTab) => void;
|
setActiveSurface: (tab: WorkspaceSurface) => void;
|
||||||
setSessionSwitcherOpen: (open: boolean) => void;
|
setSessionSwitcherOpen: (open: boolean) => void;
|
||||||
openNewSessionDraft: (options?: { selectedProjectId?: string | null; directoryOverride?: string | null }) => void;
|
openNewSessionDraft: (options?: { selectedProjectId?: string | null; directoryOverride?: string | null }) => void;
|
||||||
openNewWorktreeDialog: () => void;
|
openNewWorktreeDialog: () => void;
|
||||||
@@ -362,7 +362,7 @@ function SidebarProjectsListComponent(props: Props): React.ReactNode {
|
|||||||
}}
|
}}
|
||||||
onNewSession={() => {
|
onNewSession={() => {
|
||||||
if (projectKey !== props.activeProjectId) props.setActiveProjectIdOnly(projectKey);
|
if (projectKey !== props.activeProjectId) props.setActiveProjectIdOnly(projectKey);
|
||||||
props.setActiveMainTab('chat');
|
props.setActiveSurface('chat');
|
||||||
if (props.mobileVariant) props.setSessionSwitcherOpen(false);
|
if (props.mobileVariant) props.setSessionSwitcherOpen(false);
|
||||||
props.openNewSessionDraft({
|
props.openNewSessionDraft({
|
||||||
selectedProjectId: projectKey,
|
selectedProjectId: projectKey,
|
||||||
@@ -371,7 +371,7 @@ function SidebarProjectsListComponent(props: Props): React.ReactNode {
|
|||||||
}}
|
}}
|
||||||
onNewWorktreeSession={() => {
|
onNewWorktreeSession={() => {
|
||||||
if (projectKey !== props.activeProjectId) props.setActiveProjectIdOnly(projectKey);
|
if (projectKey !== props.activeProjectId) props.setActiveProjectIdOnly(projectKey);
|
||||||
props.setActiveMainTab('chat');
|
props.setActiveSurface('chat');
|
||||||
props.openNewWorktreeDialog();
|
props.openNewWorktreeDialog();
|
||||||
}}
|
}}
|
||||||
onManageWorktrees={() => props.openWorktreesPage(projectKey)}
|
onManageWorktrees={() => props.openWorktreesPage(projectKey)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import type { Session } from '@opencode-ai/sdk/v2';
|
import type { Session } from '@opencode-ai/sdk/v2';
|
||||||
import type { SessionGroup, SessionNode } from '../types';
|
import type { SessionGroup, SessionNode } from '../types';
|
||||||
import { normalizePath } from '../utils';
|
import { normalizePath } from '../utils';
|
||||||
import type { MainTab } from '@/stores/useUIStore';
|
import type { WorkspaceSurface } from '@/stores/useUIStore';
|
||||||
import { useUIStore } from '@/stores/useUIStore';
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ type Args = {
|
|||||||
newSessionDraftOpen: boolean;
|
newSessionDraftOpen: boolean;
|
||||||
mobileVariant: boolean;
|
mobileVariant: boolean;
|
||||||
openNewSessionDraft: (options?: { selectedProjectId?: string | null; directoryOverride?: string | null }) => void;
|
openNewSessionDraft: (options?: { selectedProjectId?: string | null; directoryOverride?: string | null }) => void;
|
||||||
setActiveMainTab: (tab: MainTab) => void;
|
setActiveSurface: (tab: WorkspaceSurface) => void;
|
||||||
setSessionSwitcherOpen: (open: boolean) => void;
|
setSessionSwitcherOpen: (open: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ export const useProjectSessionSelection = (args: Args): void => {
|
|||||||
newSessionDraftOpen,
|
newSessionDraftOpen,
|
||||||
mobileVariant,
|
mobileVariant,
|
||||||
openNewSessionDraft,
|
openNewSessionDraft,
|
||||||
setActiveMainTab,
|
setActiveSurface,
|
||||||
setSessionSwitcherOpen,
|
setSessionSwitcherOpen,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ export const useProjectSessionSelection = (args: Args): void => {
|
|||||||
previousActiveProjectRef.current = activeProjectId;
|
previousActiveProjectRef.current = activeProjectId;
|
||||||
|
|
||||||
if (selection.kind === 'open-draft') {
|
if (selection.kind === 'open-draft') {
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
if (mobileVariant) {
|
if (mobileVariant) {
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
}
|
}
|
||||||
@@ -232,7 +232,7 @@ export const useProjectSessionSelection = (args: Args): void => {
|
|||||||
openNewSessionDraft,
|
openNewSessionDraft,
|
||||||
projectSections,
|
projectSections,
|
||||||
projectSessionMeta,
|
projectSessionMeta,
|
||||||
setActiveMainTab,
|
setActiveSurface,
|
||||||
setSessionSwitcherOpen,
|
setSessionSwitcherOpen,
|
||||||
setActiveSessionByProject,
|
setActiveSessionByProject,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { Session } from '@opencode-ai/sdk/v2';
|
|||||||
import { toast } from '@/components/ui';
|
import { toast } from '@/components/ui';
|
||||||
import { copyTextToClipboard } from '@/lib/clipboard';
|
import { copyTextToClipboard } from '@/lib/clipboard';
|
||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
import type { MainTab } from '@/stores/useUIStore';
|
import type { WorkspaceSurface } from '@/stores/useUIStore';
|
||||||
import { useUIStore } from '@/stores/useUIStore';
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
import { streamPerfMark } from '@/stores/utils/streamDebug';
|
import { streamPerfMark } from '@/stores/utils/streamDebug';
|
||||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||||
@@ -30,7 +30,7 @@ type Args = {
|
|||||||
sessionSearchQuery: string;
|
sessionSearchQuery: string;
|
||||||
setSessionSearchQuery: (value: string) => void;
|
setSessionSearchQuery: (value: string) => void;
|
||||||
setIsSessionSearchOpen: (open: boolean) => void;
|
setIsSessionSearchOpen: (open: boolean) => void;
|
||||||
setActiveMainTab: (tab: MainTab) => void;
|
setActiveSurface: (tab: WorkspaceSurface) => void;
|
||||||
setSessionSwitcherOpen: (open: boolean) => void;
|
setSessionSwitcherOpen: (open: boolean) => void;
|
||||||
setCurrentSession: (sessionId: string | null, directoryHint?: string | null) => void;
|
setCurrentSession: (sessionId: string | null, directoryHint?: string | null) => void;
|
||||||
updateSessionTitle: (id: string, title: string) => Promise<void>;
|
updateSessionTitle: (id: string, title: string) => Promise<void>;
|
||||||
@@ -79,7 +79,7 @@ export const useSessionActions = (args: Args) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (args.mobileVariant) {
|
if (args.mobileVariant) {
|
||||||
args.setActiveMainTab('chat');
|
args.setActiveSurface('chat');
|
||||||
args.setSessionSwitcherOpen(false);
|
args.setSessionSwitcherOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export const CommandPalette: React.FC = () => {
|
|||||||
|
|
||||||
const isCommandPaletteOpen = useUIStore((s) => s.isCommandPaletteOpen);
|
const isCommandPaletteOpen = useUIStore((s) => s.isCommandPaletteOpen);
|
||||||
const setCommandPaletteOpen = useUIStore((s) => s.setCommandPaletteOpen);
|
const setCommandPaletteOpen = useUIStore((s) => s.setCommandPaletteOpen);
|
||||||
const setActiveMainTab = useUIStore((s) => s.setActiveMainTab);
|
const setActiveSurface = useUIStore((s) => s.setActiveSurface);
|
||||||
const setSettingsDialogOpen = useUIStore((s) => s.setSettingsDialogOpen);
|
const setSettingsDialogOpen = useUIStore((s) => s.setSettingsDialogOpen);
|
||||||
const setSettingsPage = useUIStore((s) => s.setSettingsPage);
|
const setSettingsPage = useUIStore((s) => s.setSettingsPage);
|
||||||
const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen);
|
const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen);
|
||||||
@@ -170,7 +170,7 @@ export const CommandPalette: React.FC = () => {
|
|||||||
shortcutId: 'new_chat',
|
shortcutId: 'new_chat',
|
||||||
searchText: t('commandPalette.item.newSession'),
|
searchText: t('commandPalette.item.newSession'),
|
||||||
onSelect: run(() => {
|
onSelect: run(() => {
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
openNewSessionDraft();
|
openNewSessionDraft();
|
||||||
}),
|
}),
|
||||||
@@ -263,7 +263,7 @@ export const CommandPalette: React.FC = () => {
|
|||||||
t,
|
t,
|
||||||
run,
|
run,
|
||||||
isMobile,
|
isMobile,
|
||||||
setActiveMainTab,
|
setActiveSurface,
|
||||||
setSessionSwitcherOpen,
|
setSessionSwitcherOpen,
|
||||||
openNewSessionDraft,
|
openNewSessionDraft,
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export function ArchiveView(): React.ReactNode {
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const open = useUIStore((state) => state.isArchivePageOpen);
|
const open = useUIStore((state) => state.isArchivePageOpen);
|
||||||
const setOpen = useUIStore((state) => state.setArchivePageOpen);
|
const setOpen = useUIStore((state) => state.setArchivePageOpen);
|
||||||
const setActiveMainTab = useUIStore((state) => state.setActiveMainTab);
|
const setActiveSurface = useUIStore((state) => state.setActiveSurface);
|
||||||
const setCurrentSession = useSessionUIStore((state) => state.setCurrentSession);
|
const setCurrentSession = useSessionUIStore((state) => state.setCurrentSession);
|
||||||
const unarchiveSession = useSessionUIStore((state) => state.unarchiveSession);
|
const unarchiveSession = useSessionUIStore((state) => state.unarchiveSession);
|
||||||
const homeDirectory = useDirectoryStore((state) => state.homeDirectory);
|
const homeDirectory = useDirectoryStore((state) => state.homeDirectory);
|
||||||
@@ -86,9 +86,9 @@ export function ArchiveView(): React.ReactNode {
|
|||||||
const openSession = React.useCallback((session: Session) => {
|
const openSession = React.useCallback((session: Session) => {
|
||||||
const directory = normalizePath(resolveGlobalSessionDirectory(session));
|
const directory = normalizePath(resolveGlobalSessionDirectory(session));
|
||||||
setCurrentSession(session.id, directory ?? undefined);
|
setCurrentSession(session.id, directory ?? undefined);
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}, [setActiveMainTab, setCurrentSession, setOpen]);
|
}, [setActiveSurface, setCurrentSession, setOpen]);
|
||||||
|
|
||||||
const restoreSession = React.useCallback((session: Session) => {
|
const restoreSession = React.useCallback((session: Session) => {
|
||||||
void unarchiveSession(session.id).then((success) => {
|
void unarchiveSession(session.id).then((success) => {
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useUIStore } from '@/stores/useUIStore';
|
|
||||||
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
|
||||||
import { useI18n } from '@/lib/i18n';
|
|
||||||
import { DiagramEditor, type DiagramEditorHandle } from '@/components/diagram';
|
|
||||||
import { Icon } from '@/components/icon/Icon';
|
|
||||||
|
|
||||||
export function DiagramView() {
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { files } = useRuntimeAPIs();
|
|
||||||
|
|
||||||
const [filePath, setFilePath] = React.useState<string | null>(null);
|
|
||||||
const [xml, setXml] = React.useState('');
|
|
||||||
const [loading, setLoading] = React.useState(true);
|
|
||||||
const editorRef = React.useRef<DiagramEditorHandle>(null);
|
|
||||||
const pendingDiagramFile = useUIStore((state) => state.pendingDiagramFile);
|
|
||||||
|
|
||||||
const loadFile = React.useCallback(async (path: string) => {
|
|
||||||
setLoading(true);
|
|
||||||
setFilePath(path);
|
|
||||||
try {
|
|
||||||
const result = await files?.readFile?.(path);
|
|
||||||
if (result) {
|
|
||||||
setXml(result.content);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
setXml('');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}, [files]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (!pendingDiagramFile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pending = useUIStore.getState().consumePendingDiagramFile();
|
|
||||||
if (pending) {
|
|
||||||
void loadFile(pending);
|
|
||||||
}
|
|
||||||
}, [loadFile, pendingDiagramFile]);
|
|
||||||
|
|
||||||
const saveDiagram = React.useCallback(async () => {
|
|
||||||
const newXml = editorRef.current?.getXml();
|
|
||||||
if (filePath && files?.writeFile && newXml && newXml !== xml) {
|
|
||||||
await files.writeFile(filePath, newXml);
|
|
||||||
setXml(newXml);
|
|
||||||
}
|
|
||||||
}, [filePath, files, xml]);
|
|
||||||
|
|
||||||
const fileName = filePath ? filePath.split('/').pop() || filePath : '';
|
|
||||||
|
|
||||||
if (!filePath) {
|
|
||||||
return (
|
|
||||||
<div className="flex h-full items-center justify-center p-3">
|
|
||||||
<div className="typography-ui text-muted-foreground">
|
|
||||||
{t('filesView.editor.pickFileFromTree')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<div className="flex h-full items-center justify-center p-3">
|
|
||||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex h-full flex-col">
|
|
||||||
<div className="flex items-center gap-2 border-b border-border/30 px-3 py-1.5">
|
|
||||||
<Icon name="file" className="size-4 shrink-0 text-muted-foreground" />
|
|
||||||
<span className="typography-ui text-muted-foreground truncate flex-1">{fileName}</span>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => void saveDiagram()}
|
|
||||||
className="size-6 flex items-center justify-center rounded-md text-foreground hover:bg-interactive-hover/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
|
||||||
title={t('filesView.diagram.saveDiagram')}
|
|
||||||
>
|
|
||||||
<Icon name="save-3" className="size-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => useUIStore.getState().setActiveMainTab('chat')}
|
|
||||||
className="size-6 flex items-center justify-center rounded-md text-muted-foreground hover:bg-interactive-hover/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
|
||||||
title={t('filesView.diagram.closeDiagramView')}
|
|
||||||
>
|
|
||||||
<Icon name="close" className="size-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 min-h-0">
|
|
||||||
<DiagramEditor
|
|
||||||
ref={editorRef}
|
|
||||||
xml={xml}
|
|
||||||
className="h-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -939,7 +939,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
|
|
||||||
const [confirmDiscardOpen, setConfirmDiscardOpen] = React.useState(false);
|
const [confirmDiscardOpen, setConfirmDiscardOpen] = React.useState(false);
|
||||||
const pendingSelectFileRef = React.useRef<FileNode | null>(null);
|
const pendingSelectFileRef = React.useRef<FileNode | null>(null);
|
||||||
const pendingTabRef = React.useRef<import('@/stores/useUIStore').MainTab | null>(null);
|
const pendingTabRef = React.useRef<import('@/stores/useUIStore').WorkspaceSurface | null>(null);
|
||||||
const pendingClosePathRef = React.useRef<string | null>(null);
|
const pendingClosePathRef = React.useRef<string | null>(null);
|
||||||
const skipDirtyOnceRef = React.useRef(false);
|
const skipDirtyOnceRef = React.useRef(false);
|
||||||
const copiedContentTimeoutRef = React.useRef<number | null>(null);
|
const copiedContentTimeoutRef = React.useRef<number | null>(null);
|
||||||
@@ -1029,7 +1029,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
const [isDragging, setIsDragging] = React.useState(false);
|
const [isDragging, setIsDragging] = React.useState(false);
|
||||||
|
|
||||||
// Session/config for sending comments
|
// Session/config for sending comments
|
||||||
const setMainTabGuard = useUIStore((state) => state.setMainTabGuard);
|
const setSurfaceGuard = useUIStore((state) => state.setSurfaceGuard);
|
||||||
const pendingFileNavigation = useUIStore((state) => state.pendingFileNavigation);
|
const pendingFileNavigation = useUIStore((state) => state.pendingFileNavigation);
|
||||||
const setPendingFileNavigation = useUIStore((state) => state.setPendingFileNavigation);
|
const setPendingFileNavigation = useUIStore((state) => state.setPendingFileNavigation);
|
||||||
const pendingFileFocusPath = useUIStore((state) => state.pendingFileFocusPath);
|
const pendingFileFocusPath = useUIStore((state) => state.pendingFileFocusPath);
|
||||||
@@ -1098,10 +1098,10 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setLineSelection(null);
|
setLineSelection(null);
|
||||||
reset();
|
reset();
|
||||||
setMainTabGuard(null);
|
setSurfaceGuard(null);
|
||||||
setDraftContent('');
|
setDraftContent('');
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}, [selectedFile?.path, reset, setMainTabGuard]);
|
}, [selectedFile?.path, reset, setSurfaceGuard]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setCommentSelection(lineSelection);
|
setCommentSelection(lineSelection);
|
||||||
@@ -1713,11 +1713,11 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!isDirty) {
|
if (!isDirty) {
|
||||||
setMainTabGuard(null);
|
setSurfaceGuard(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const guard = (_nextTab: import('@/stores/useUIStore').MainTab) => {
|
const guard = (_nextTab: import('@/stores/useUIStore').WorkspaceSurface) => {
|
||||||
if (skipDirtyOnceRef.current) {
|
if (skipDirtyOnceRef.current) {
|
||||||
skipDirtyOnceRef.current = false;
|
skipDirtyOnceRef.current = false;
|
||||||
return true;
|
return true;
|
||||||
@@ -1727,15 +1727,15 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
setMainTabGuard(guard);
|
setSurfaceGuard(guard);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const currentGuard = useUIStore.getState().mainTabGuard;
|
const currentGuard = useUIStore.getState().surfaceGuard;
|
||||||
if (currentGuard === guard) {
|
if (currentGuard === guard) {
|
||||||
setMainTabGuard(null);
|
setSurfaceGuard(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [isDirty, setMainTabGuard]);
|
}, [isDirty, setSurfaceGuard]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (autoSaveEnabled) {
|
if (autoSaveEnabled) {
|
||||||
@@ -2180,10 +2180,10 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nextTab) {
|
if (nextTab) {
|
||||||
setMainTabGuard(null);
|
setSurfaceGuard(null);
|
||||||
useUIStore.getState().setActiveMainTab(nextTab);
|
useUIStore.getState().setActiveSurface(nextTab);
|
||||||
}
|
}
|
||||||
}, [displayedContent, handleSelectFile, isMobile, removeOpenPath, root, selectedFile?.path, setMainTabGuard, setSelectedPath]);
|
}, [displayedContent, handleSelectFile, isMobile, removeOpenPath, root, selectedFile?.path, setSurfaceGuard, setSelectedPath]);
|
||||||
|
|
||||||
const saveAndContinue = React.useCallback(async () => {
|
const saveAndContinue = React.useCallback(async () => {
|
||||||
const nextFile = pendingSelectFileRef.current;
|
const nextFile = pendingSelectFileRef.current;
|
||||||
@@ -2234,10 +2234,10 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nextTab) {
|
if (nextTab) {
|
||||||
setMainTabGuard(null);
|
setSurfaceGuard(null);
|
||||||
useUIStore.getState().setActiveMainTab(nextTab);
|
useUIStore.getState().setActiveSurface(nextTab);
|
||||||
}
|
}
|
||||||
}, [handleSelectFile, isMobile, removeOpenPath, root, saveDraft, selectedFile?.path, setMainTabGuard, setSelectedPath]);
|
}, [handleSelectFile, isMobile, removeOpenPath, root, saveDraft, selectedFile?.path, setSurfaceGuard, setSelectedPath]);
|
||||||
|
|
||||||
const handleCloseFile = React.useCallback((path: string) => {
|
const handleCloseFile = React.useCallback((path: string) => {
|
||||||
const isActive = selectedFile?.path === path;
|
const isActive = selectedFile?.path === path;
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null, projectPl
|
|||||||
const activeProjectId = useProjectsStore((state) => state.activeProjectId);
|
const activeProjectId = useProjectsStore((state) => state.activeProjectId);
|
||||||
const gitDirectories = useGitStore((state) => state.directories);
|
const gitDirectories = useGitStore((state) => state.directories);
|
||||||
const effectiveDirectory = useEffectiveDirectory() ?? '';
|
const effectiveDirectory = useEffectiveDirectory() ?? '';
|
||||||
const setActiveMainTab = useUIStore((state) => state.setActiveMainTab);
|
const setActiveSurface = useUIStore((state) => state.setActiveSurface);
|
||||||
const setSessionSwitcherOpen = useUIStore((state) => state.setSessionSwitcherOpen);
|
const setSessionSwitcherOpen = useUIStore((state) => state.setSessionSwitcherOpen);
|
||||||
const runtimeApis = useRuntimeAPIs();
|
const runtimeApis = useRuntimeAPIs();
|
||||||
const { isMobile } = useDeviceInfo();
|
const { isMobile } = useDeviceInfo();
|
||||||
@@ -579,10 +579,10 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null, projectPl
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const routeToChat = React.useCallback(() => {
|
const routeToChat = React.useCallback(() => {
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
onNavigatedToChat?.();
|
onNavigatedToChat?.();
|
||||||
}, [onNavigatedToChat, setActiveMainTab, setSessionSwitcherOpen]);
|
}, [onNavigatedToChat, setActiveSurface, setSessionSwitcherOpen]);
|
||||||
|
|
||||||
const handleConfirmPlanSend = React.useCallback(
|
const handleConfirmPlanSend = React.useCallback(
|
||||||
async (execution: TodoSendExecution) => {
|
async (execution: TodoSendExecution) => {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const ConflictDialog: React.FC<ConflictDialogProps> = ({
|
|||||||
const currentSessionId = useSessionUIStore((state) => state.currentSessionId);
|
const currentSessionId = useSessionUIStore((state) => state.currentSessionId);
|
||||||
const setPendingInputText = useInputStore((state) => state.setPendingInputText);
|
const setPendingInputText = useInputStore((state) => state.setPendingInputText);
|
||||||
const setPendingSyntheticParts = useInputStore((state) => state.setPendingSyntheticParts);
|
const setPendingSyntheticParts = useInputStore((state) => state.setPendingSyntheticParts);
|
||||||
const setActiveMainTab = useUIStore((state) => state.setActiveMainTab);
|
const setActiveSurface = useUIStore((state) => state.setActiveSurface);
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = React.useState(false);
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
const [conflictDetails, setConflictDetails] = React.useState<MergeConflictDetails | null>(null);
|
const [conflictDetails, setConflictDetails] = React.useState<MergeConflictDetails | null>(null);
|
||||||
@@ -137,7 +137,7 @@ export const ConflictDialog: React.FC<ConflictDialogProps> = ({
|
|||||||
{ text: context.payloadText, synthetic: true },
|
{ text: context.payloadText, synthetic: true },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
onClearState?.();
|
onClearState?.();
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
};
|
};
|
||||||
@@ -159,7 +159,7 @@ export const ConflictDialog: React.FC<ConflictDialogProps> = ({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
// Navigate to chat tab so user sees the new session
|
// Navigate to chat tab so user sees the new session
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
onClearState?.();
|
onClearState?.();
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export const IntegrateCommitsSection: React.FC<{
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const currentSessionId = useSessionUIStore((s) => s.currentSessionId);
|
const currentSessionId = useSessionUIStore((s) => s.currentSessionId);
|
||||||
const setActiveMainTab = useUIStore((s) => s.setActiveMainTab);
|
const setActiveSurface = useUIStore((s) => s.setActiveSurface);
|
||||||
const [branchDropdownOpen, setBranchDropdownOpen] = React.useState(false);
|
const [branchDropdownOpen, setBranchDropdownOpen] = React.useState(false);
|
||||||
const [branchSearch, setBranchSearch] = React.useState('');
|
const [branchSearch, setBranchSearch] = React.useState('');
|
||||||
const searchInputRef = React.useRef<HTMLInputElement>(null);
|
const searchInputRef = React.useRef<HTMLInputElement>(null);
|
||||||
@@ -236,7 +236,7 @@ export const IntegrateCommitsSection: React.FC<{
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
// Navigate to chat tab so user sees the new session
|
// Navigate to chat tab so user sees the new session
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,8 +251,8 @@ export const IntegrateCommitsSection: React.FC<{
|
|||||||
{ text: context.instructionsText, synthetic: true },
|
{ text: context.instructionsText, synthetic: true },
|
||||||
{ text: context.payloadText, synthetic: true },
|
{ text: context.payloadText, synthetic: true },
|
||||||
]);
|
]);
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
}, [currentSessionId, setActiveMainTab, buildConflictContext, openNewSessionDraft, setPendingInputText, setPendingSyntheticParts, t]);
|
}, [currentSessionId, setActiveSurface, buildConflictContext, openNewSessionDraft, setPendingInputText, setPendingSyntheticParts, t]);
|
||||||
|
|
||||||
const handleMove = React.useCallback(async () => {
|
const handleMove = React.useCallback(async () => {
|
||||||
if (ui.kind !== 'ready') return;
|
if (ui.kind !== 'ready') return;
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ export const PullRequestSection: React.FC<{
|
|||||||
const githubAuthChecked = useGitHubAuthStore((state) => state.hasChecked);
|
const githubAuthChecked = useGitHubAuthStore((state) => state.hasChecked);
|
||||||
const setSettingsDialogOpen = useUIStore((state) => state.setSettingsDialogOpen);
|
const setSettingsDialogOpen = useUIStore((state) => state.setSettingsDialogOpen);
|
||||||
const setSettingsPage = useUIStore((state) => state.setSettingsPage);
|
const setSettingsPage = useUIStore((state) => state.setSettingsPage);
|
||||||
const setActiveMainTab = useUIStore((state) => state.setActiveMainTab);
|
const setActiveSurface = useUIStore((state) => state.setActiveSurface);
|
||||||
const currentSessionId = useSessionUIStore((state) => state.currentSessionId);
|
const currentSessionId = useSessionUIStore((state) => state.currentSessionId);
|
||||||
const newSessionDraftOpen = useSessionUIStore((state) => Boolean(state.newSessionDraft?.open));
|
const newSessionDraftOpen = useSessionUIStore((state) => Boolean(state.newSessionDraft?.open));
|
||||||
const { isMobile, hasTouchInput, screenWidth } = useDeviceInfo();
|
const { isMobile, hasTouchInput, screenWidth } = useDeviceInfo();
|
||||||
@@ -986,14 +986,14 @@ export const PullRequestSection: React.FC<{
|
|||||||
text: '',
|
text: '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = e instanceof Error ? e.message : String(e);
|
const message = e instanceof Error ? e.message : String(e);
|
||||||
toast.error(t('gitView.pr.toast.loadChecksFailed'), { description: message });
|
toast.error(t('gitView.pr.toast.loadChecksFailed'), { description: message });
|
||||||
} finally {
|
} finally {
|
||||||
setIsAttachingChecks(false);
|
setIsAttachingChecks(false);
|
||||||
}
|
}
|
||||||
}, [directory, ensurePrContext, github, pr, resolveDraftTarget, setActiveMainTab, status?.repo, t]);
|
}, [directory, ensurePrContext, github, pr, resolveDraftTarget, setActiveSurface, status?.repo, t]);
|
||||||
|
|
||||||
const sendCommentsToChat = React.useCallback(async () => {
|
const sendCommentsToChat = React.useCallback(async () => {
|
||||||
if (!github?.prContext) {
|
if (!github?.prContext) {
|
||||||
@@ -1021,14 +1021,14 @@ export const PullRequestSection: React.FC<{
|
|||||||
for (const comment of timelineComments) {
|
for (const comment of timelineComments) {
|
||||||
attachCommentDraft(target, comment);
|
attachCommentDraft(target, comment);
|
||||||
}
|
}
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = e instanceof Error ? e.message : String(e);
|
const message = e instanceof Error ? e.message : String(e);
|
||||||
toast.error(t('gitView.pr.toast.loadPrCommentsFailed'), { description: message });
|
toast.error(t('gitView.pr.toast.loadPrCommentsFailed'), { description: message });
|
||||||
} finally {
|
} finally {
|
||||||
setIsAttachingComments(false);
|
setIsAttachingComments(false);
|
||||||
}
|
}
|
||||||
}, [attachCommentDraft, directory, ensurePrContext, github, pr, resolveDraftTarget, setActiveMainTab, status?.repo, t, timelineComments]);
|
}, [attachCommentDraft, directory, ensurePrContext, github, pr, resolveDraftTarget, setActiveSurface, status?.repo, t, timelineComments]);
|
||||||
|
|
||||||
const sendSingleCommentToChat = React.useCallback(async (comment: TimelineCommentItem) => {
|
const sendSingleCommentToChat = React.useCallback(async (comment: TimelineCommentItem) => {
|
||||||
const target = resolveDraftTarget();
|
const target = resolveDraftTarget();
|
||||||
@@ -1037,8 +1037,8 @@ export const PullRequestSection: React.FC<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
attachCommentDraft(target, comment);
|
attachCommentDraft(target, comment);
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
}, [attachCommentDraft, resolveDraftTarget, setActiveMainTab]);
|
}, [attachCommentDraft, resolveDraftTarget, setActiveSurface]);
|
||||||
|
|
||||||
const refresh = React.useCallback(async (options?: { force?: boolean; onlyExistingPr?: boolean; silent?: boolean; markInitialResolved?: boolean }) => {
|
const refresh = React.useCallback(async (options?: { force?: boolean; onlyExistingPr?: boolean; silent?: boolean; markInitialResolved?: boolean }) => {
|
||||||
await refreshPrStatus(prStatusKey, options);
|
await refreshPrStatus(prStatusKey, options);
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
}, [currentShortcutDirectory]);
|
}, [currentShortcutDirectory]);
|
||||||
const isMobile = useUIStore((s) => s.isMobile);
|
const isMobile = useUIStore((s) => s.isMobile);
|
||||||
const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen);
|
const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen);
|
||||||
const setActiveMainTab = useUIStore((s) => s.setActiveMainTab);
|
const setActiveSurface = useUIStore((s) => s.setActiveSurface);
|
||||||
const setSettingsDialogOpen = useUIStore((s) => s.setSettingsDialogOpen);
|
const setSettingsDialogOpen = useUIStore((s) => s.setSettingsDialogOpen);
|
||||||
const setModelSelectorOpen = useUIStore((s) => s.setModelSelectorOpen);
|
const setModelSelectorOpen = useUIStore((s) => s.setModelSelectorOpen);
|
||||||
const setTimelineDialogOpen = useUIStore((s) => s.setTimelineDialogOpen);
|
const setTimelineDialogOpen = useUIStore((s) => s.setTimelineDialogOpen);
|
||||||
@@ -154,7 +154,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
isAboutDialogOpen,
|
isAboutDialogOpen,
|
||||||
isMultiRunLauncherOpen,
|
isMultiRunLauncherOpen,
|
||||||
isImagePreviewOpen,
|
isImagePreviewOpen,
|
||||||
activeMainTab,
|
activeSurface,
|
||||||
isPromptNavigatorPanelOpen,
|
isPromptNavigatorPanelOpen,
|
||||||
} = useUIStore.getState();
|
} = useUIStore.getState();
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasOverlay = isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen || isMultiRunLauncherOpen || isImagePreviewOpen;
|
const hasOverlay = isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen || isMultiRunLauncherOpen || isImagePreviewOpen;
|
||||||
const isChatActive = activeMainTab === 'chat';
|
const isChatActive = activeSurface === 'chat';
|
||||||
|
|
||||||
if (hasOverlay || !isChatActive) {
|
if (hasOverlay || !isChatActive) {
|
||||||
resetAbortPriming();
|
resetAbortPriming();
|
||||||
@@ -245,7 +245,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
|
|
||||||
if (eventMatchesShortcut(e, combo('toggle_prompt_navigator'))) {
|
if (eventMatchesShortcut(e, combo('toggle_prompt_navigator'))) {
|
||||||
const {
|
const {
|
||||||
activeMainTab,
|
activeSurface,
|
||||||
promptNavigatorEnabled,
|
promptNavigatorEnabled,
|
||||||
isSettingsDialogOpen,
|
isSettingsDialogOpen,
|
||||||
isCommandPaletteOpen,
|
isCommandPaletteOpen,
|
||||||
@@ -257,7 +257,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
isImagePreviewOpen,
|
isImagePreviewOpen,
|
||||||
} = useUIStore.getState();
|
} = useUIStore.getState();
|
||||||
|
|
||||||
if (!promptNavigatorEnabled || isMobile || isVSCodeRuntime() || activeMainTab !== 'chat') {
|
if (!promptNavigatorEnabled || isMobile || isVSCodeRuntime() || activeSurface !== 'chat') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
if (matchedNewSessionShortcut || matchedWorktreeShortcut) {
|
if (matchedNewSessionShortcut || matchedWorktreeShortcut) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
|
|
||||||
if (!isVSCodeRuntime() && matchedWorktreeShortcut) {
|
if (!isVSCodeRuntime() && matchedWorktreeShortcut) {
|
||||||
@@ -394,11 +394,11 @@ export const useKeyboardShortcuts = () => {
|
|||||||
isHelpDialogOpen,
|
isHelpDialogOpen,
|
||||||
isSessionSwitcherOpen,
|
isSessionSwitcherOpen,
|
||||||
isAboutDialogOpen,
|
isAboutDialogOpen,
|
||||||
activeMainTab,
|
activeSurface,
|
||||||
} = useUIStore.getState();
|
} = useUIStore.getState();
|
||||||
|
|
||||||
const hasOverlay = isSettingsDialogOpen || isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen;
|
const hasOverlay = isSettingsDialogOpen || isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen;
|
||||||
if (hasOverlay || activeMainTab !== 'chat' || !isChatInputTarget(e.target)) {
|
if (hasOverlay || activeSurface !== 'chat' || !isChatInputTarget(e.target)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -525,7 +525,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
isHelpDialogOpen,
|
isHelpDialogOpen,
|
||||||
isSessionSwitcherOpen,
|
isSessionSwitcherOpen,
|
||||||
isAboutDialogOpen,
|
isAboutDialogOpen,
|
||||||
activeMainTab,
|
activeSurface,
|
||||||
isModelSelectorOpen,
|
isModelSelectorOpen,
|
||||||
} = useUIStore.getState();
|
} = useUIStore.getState();
|
||||||
|
|
||||||
@@ -536,7 +536,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
|
|
||||||
// Skip if any overlay open or not on chat tab
|
// Skip if any overlay open or not on chat tab
|
||||||
const hasOverlay = isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen;
|
const hasOverlay = isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen;
|
||||||
const isChatActive = activeMainTab === 'chat';
|
const isChatActive = activeSurface === 'chat';
|
||||||
|
|
||||||
if (hasOverlay || !isChatActive) {
|
if (hasOverlay || !isChatActive) {
|
||||||
return;
|
return;
|
||||||
@@ -555,7 +555,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
isHelpDialogOpen,
|
isHelpDialogOpen,
|
||||||
isSessionSwitcherOpen,
|
isSessionSwitcherOpen,
|
||||||
isAboutDialogOpen,
|
isAboutDialogOpen,
|
||||||
activeMainTab,
|
activeSurface,
|
||||||
} = useUIStore.getState();
|
} = useUIStore.getState();
|
||||||
|
|
||||||
if (isSettingsDialogOpen) {
|
if (isSettingsDialogOpen) {
|
||||||
@@ -563,7 +563,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasOverlay = isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen;
|
const hasOverlay = isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen;
|
||||||
const isChatActive = activeMainTab === 'chat';
|
const isChatActive = activeSurface === 'chat';
|
||||||
|
|
||||||
if (hasOverlay || !isChatActive) {
|
if (hasOverlay || !isChatActive) {
|
||||||
return;
|
return;
|
||||||
@@ -602,7 +602,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
isHelpDialogOpen,
|
isHelpDialogOpen,
|
||||||
isSessionSwitcherOpen,
|
isSessionSwitcherOpen,
|
||||||
isAboutDialogOpen,
|
isAboutDialogOpen,
|
||||||
activeMainTab,
|
activeSurface,
|
||||||
favoriteModels,
|
favoriteModels,
|
||||||
addRecentModel,
|
addRecentModel,
|
||||||
} = useUIStore.getState();
|
} = useUIStore.getState();
|
||||||
@@ -612,7 +612,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasOverlay = isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen;
|
const hasOverlay = isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isAboutDialogOpen;
|
||||||
const isChatActive = activeMainTab === 'chat';
|
const isChatActive = activeSurface === 'chat';
|
||||||
|
|
||||||
if (hasOverlay || !isChatActive || favoriteModels.length === 0) {
|
if (hasOverlay || !isChatActive || favoriteModels.length === 0) {
|
||||||
return;
|
return;
|
||||||
@@ -644,8 +644,8 @@ export const useKeyboardShortcuts = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (eventMatchesShortcut(e, combo('toggle_dictation'))) {
|
if (eventMatchesShortcut(e, combo('toggle_dictation'))) {
|
||||||
const { activeMainTab, isCommandPaletteOpen, isHelpDialogOpen, isSessionSwitcherOpen, isSettingsDialogOpen } = useUIStore.getState();
|
const { activeSurface, isCommandPaletteOpen, isHelpDialogOpen, isSessionSwitcherOpen, isSettingsDialogOpen } = useUIStore.getState();
|
||||||
if (activeMainTab !== 'chat' || isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isSettingsDialogOpen) {
|
if (activeSurface !== 'chat' || isCommandPaletteOpen || isHelpDialogOpen || isSessionSwitcherOpen || isSettingsDialogOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -695,7 +695,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
toggleTerminalSurfaceExpanded,
|
toggleTerminalSurfaceExpanded,
|
||||||
isMobile,
|
isMobile,
|
||||||
setSessionSwitcherOpen,
|
setSessionSwitcherOpen,
|
||||||
setActiveMainTab,
|
setActiveSurface,
|
||||||
setSettingsDialogOpen,
|
setSettingsDialogOpen,
|
||||||
setModelSelectorOpen,
|
setModelSelectorOpen,
|
||||||
setTimelineDialogOpen,
|
setTimelineDialogOpen,
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export const useMenuActions = (
|
|||||||
const toggleHelpDialog = useUIStore((s) => s.toggleHelpDialog);
|
const toggleHelpDialog = useUIStore((s) => s.toggleHelpDialog);
|
||||||
const toggleSidebar = useUIStore((s) => s.toggleSidebar);
|
const toggleSidebar = useUIStore((s) => s.toggleSidebar);
|
||||||
const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen);
|
const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen);
|
||||||
const setActiveMainTab = useUIStore((s) => s.setActiveMainTab);
|
const setActiveSurface = useUIStore((s) => s.setActiveSurface);
|
||||||
const setSettingsDialogOpen = useUIStore((s) => s.setSettingsDialogOpen);
|
const setSettingsDialogOpen = useUIStore((s) => s.setSettingsDialogOpen);
|
||||||
const setAboutDialogOpen = useUIStore((s) => s.setAboutDialogOpen);
|
const setAboutDialogOpen = useUIStore((s) => s.setAboutDialogOpen);
|
||||||
const checkForUpdates = useUpdateStore((state) => state.checkForUpdates);
|
const checkForUpdates = useUpdateStore((state) => state.checkForUpdates);
|
||||||
@@ -151,10 +151,10 @@ export const useMenuActions = (
|
|||||||
const nextSession = sessions[nextIndex];
|
const nextSession = sessions[nextIndex];
|
||||||
if (!nextSession) return;
|
if (!nextSession) return;
|
||||||
|
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
useSessionUIStore.getState().setCurrentSession(nextSession.id);
|
useSessionUIStore.getState().setCurrentSession(nextSession.id);
|
||||||
}, [setActiveMainTab, setSessionSwitcherOpen]);
|
}, [setActiveSurface, setSessionSwitcherOpen]);
|
||||||
|
|
||||||
const navigateProject = React.useCallback((direction: -1 | 1) => {
|
const navigateProject = React.useCallback((direction: -1 | 1) => {
|
||||||
const { activeProjectId, projects, setActiveProject } = useProjectsStore.getState();
|
const { activeProjectId, projects, setActiveProject } = useProjectsStore.getState();
|
||||||
@@ -191,7 +191,7 @@ export const useMenuActions = (
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'new-session':
|
case 'new-session':
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
{
|
{
|
||||||
const sessionState = useSessionUIStore.getState();
|
const sessionState = useSessionUIStore.getState();
|
||||||
@@ -203,7 +203,7 @@ export const useMenuActions = (
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'new-worktree-session':
|
case 'new-worktree-session':
|
||||||
setActiveMainTab('chat');
|
setActiveSurface('chat');
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
createWorktreeSession();
|
createWorktreeSession();
|
||||||
break;
|
break;
|
||||||
@@ -341,7 +341,7 @@ export const useMenuActions = (
|
|||||||
onToggleMemoryDebug,
|
onToggleMemoryDebug,
|
||||||
openNewSessionDraft,
|
openNewSessionDraft,
|
||||||
setAboutDialogOpen,
|
setAboutDialogOpen,
|
||||||
setActiveMainTab,
|
setActiveSurface,
|
||||||
setSessionSwitcherOpen,
|
setSessionSwitcherOpen,
|
||||||
setCommandPaletteOpen,
|
setCommandPaletteOpen,
|
||||||
setSettingsDialogOpen,
|
setSettingsDialogOpen,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||||
import { useUIStore } from '@/stores/useUIStore';
|
import { useUIStore, type ContextPanelMode } from '@/stores/useUIStore';
|
||||||
import { parseRoute, updateBrowserURL, hasRouteParams } from '@/lib/router';
|
import { parseRoute, updateBrowserURL, hasRouteParams } from '@/lib/router';
|
||||||
import type { RouteState, AppRouteState } from '@/lib/router';
|
import type { RouteState, AppRouteState } from '@/lib/router';
|
||||||
import type { WorkspaceSurface } from '@/stores/useUIStore';
|
import type { WorkspaceSurface } from '@/stores/useUIStore';
|
||||||
import { resolveSettingsSlug } from '@/lib/settings/metadata';
|
import { resolveSettingsSlug } from '@/lib/settings/metadata';
|
||||||
import { isEmbeddedSessionChat } from '@/components/layout/contextPanelEmbeddedChat';
|
import { isEmbeddedSessionChat } from '@/components/layout/contextPanelEmbeddedChat';
|
||||||
|
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if running in VS Code webview context.
|
* Check if running in VS Code webview context.
|
||||||
@@ -88,9 +89,16 @@ export function useRouter(): void {
|
|||||||
setSettingsDialogOpen(false);
|
setSettingsDialogOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Apply the view selected by the legacy URL parameter.
|
// 3. Apply the view selected by the legacy URL parameter. Desktop
|
||||||
if (route.tab) {
|
// surfaces live in the context panel, so a non-chat tab deep link
|
||||||
setActiveSurface(route.tab);
|
// opens the matching panel surface; activeSurface itself stays 'chat'
|
||||||
|
// (nothing renders non-chat surfaces in the main area).
|
||||||
|
if (route.tab && route.tab !== 'chat') {
|
||||||
|
const directory = useDirectoryStore.getState().currentDirectory;
|
||||||
|
if (directory) {
|
||||||
|
const mode: ContextPanelMode = route.tab === 'files' ? 'file' : route.tab;
|
||||||
|
useUIStore.getState().openContextSurface(directory, mode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Apply diff file (only if going to diff tab)
|
// 4. Apply diff file (only if going to diff tab)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
|
|||||||
|
|
||||||
const focusChatInputCalls: number[] = [];
|
const focusChatInputCalls: number[] = [];
|
||||||
const pendingInputCalls: Array<{ text: string | null; mode?: string }> = [];
|
const pendingInputCalls: Array<{ text: string | null; mode?: string }> = [];
|
||||||
const activeMainTabCalls: string[] = [];
|
const activeSurfaceCalls: string[] = [];
|
||||||
const sessionSwitcherCalls: boolean[] = [];
|
const sessionSwitcherCalls: boolean[] = [];
|
||||||
const codeMirrorDispatches: Array<{ selection: { anchor: number } }> = [];
|
const codeMirrorDispatches: Array<{ selection: { anchor: number } }> = [];
|
||||||
|
|
||||||
@@ -41,8 +41,8 @@ mock.module('@/sync/input-store', () => ({
|
|||||||
mock.module('@/stores/useUIStore', () => ({
|
mock.module('@/stores/useUIStore', () => ({
|
||||||
useUIStore: {
|
useUIStore: {
|
||||||
getState: () => ({
|
getState: () => ({
|
||||||
setActiveMainTab: (tab: string) => {
|
setActiveSurface: (tab: string) => {
|
||||||
activeMainTabCalls.push(tab);
|
activeSurfaceCalls.push(tab);
|
||||||
},
|
},
|
||||||
setSessionSwitcherOpen: (open: boolean) => {
|
setSessionSwitcherOpen: (open: boolean) => {
|
||||||
sessionSwitcherCalls.push(open);
|
sessionSwitcherCalls.push(open);
|
||||||
@@ -86,7 +86,7 @@ const installSelectionEnvironment = (options: {
|
|||||||
const clearCalls = () => {
|
const clearCalls = () => {
|
||||||
focusChatInputCalls.length = 0;
|
focusChatInputCalls.length = 0;
|
||||||
pendingInputCalls.length = 0;
|
pendingInputCalls.length = 0;
|
||||||
activeMainTabCalls.length = 0;
|
activeSurfaceCalls.length = 0;
|
||||||
sessionSwitcherCalls.length = 0;
|
sessionSwitcherCalls.length = 0;
|
||||||
codeMirrorDispatches.length = 0;
|
codeMirrorDispatches.length = 0;
|
||||||
codeMirrorView = null;
|
codeMirrorView = null;
|
||||||
@@ -262,7 +262,7 @@ describe('addSelectionToChat', () => {
|
|||||||
installSelectionEnvironment({ activeElement: textarea });
|
installSelectionEnvironment({ activeElement: textarea });
|
||||||
|
|
||||||
expect(addSelectionToChat()).toBe(true);
|
expect(addSelectionToChat()).toBe(true);
|
||||||
expect(activeMainTabCalls).toEqual(['chat']);
|
expect(activeSurfaceCalls).toEqual(['chat']);
|
||||||
expect(sessionSwitcherCalls).toEqual([false]);
|
expect(sessionSwitcherCalls).toEqual([false]);
|
||||||
expect(pendingInputCalls).toEqual([{ text: '```md\nselected\n```', mode: 'append' }]);
|
expect(pendingInputCalls).toEqual([{ text: '```md\nselected\n```', mode: 'append' }]);
|
||||||
|
|
||||||
@@ -290,7 +290,7 @@ describe('addSelectionToChat', () => {
|
|||||||
|
|
||||||
expect(addSelectionToChat()).toBe(false);
|
expect(addSelectionToChat()).toBe(false);
|
||||||
expect(pendingInputCalls).toEqual([]);
|
expect(pendingInputCalls).toEqual([]);
|
||||||
expect(activeMainTabCalls).toEqual(['chat']);
|
expect(activeSurfaceCalls).toEqual(['chat']);
|
||||||
|
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
expect(focusChatInputCalls.length).toBe(1);
|
expect(focusChatInputCalls.length).toBe(1);
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export const captureSelectionMarkdownForChat = (): string | null => {
|
|||||||
export const addSelectionToChat = (): boolean => {
|
export const addSelectionToChat = (): boolean => {
|
||||||
const markdown = captureSelectionMarkdownForChat();
|
const markdown = captureSelectionMarkdownForChat();
|
||||||
|
|
||||||
useUIStore.getState().setActiveMainTab('chat');
|
useUIStore.getState().setActiveSurface('chat');
|
||||||
useUIStore.getState().setSessionSwitcherOpen(false);
|
useUIStore.getState().setSessionSwitcherOpen(false);
|
||||||
|
|
||||||
if (markdown) {
|
if (markdown) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export interface RouteState {
|
|||||||
/**
|
/**
|
||||||
* Valid values for the legacy `tab` URL parameter.
|
* Valid values for the legacy `tab` URL parameter.
|
||||||
*/
|
*/
|
||||||
export const VALID_TABS: readonly WorkspaceSurface[] = ['chat', 'git', 'diff', 'terminal', 'files', 'diagram'] as const;
|
export const VALID_TABS: readonly WorkspaceSurface[] = ['chat', 'git', 'diff', 'terminal', 'files'] as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Valid settings section values for URL routing.
|
* Valid settings section values for URL routing.
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ import { isVSCodeRuntime } from '@/lib/desktop';
|
|||||||
* The primary view on mobile and the desktop's promoted full-screen view.
|
* The primary view on mobile and the desktop's promoted full-screen view.
|
||||||
* Desktop context-panel content is not represented here.
|
* Desktop context-panel content is not represented here.
|
||||||
*/
|
*/
|
||||||
export type WorkspaceSurface = 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files' | 'context' | 'diagram';
|
export type WorkspaceSurface = 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files' | 'context';
|
||||||
/** @deprecated Use WorkspaceSurface. */
|
|
||||||
export type MainTab = WorkspaceSurface;
|
|
||||||
export type PendingDiffScope = 'working' | 'staged' | 'turn' | 'branch';
|
export type PendingDiffScope = 'working' | 'staged' | 'turn' | 'branch';
|
||||||
export type ContextPanelMode = 'diff' | 'walkthrough' | 'file' | 'context' | 'plan' | 'chat' | 'browser' | 'git' | 'pr' | 'notes' | 'terminal';
|
export type ContextPanelMode = 'diff' | 'walkthrough' | 'file' | 'context' | 'plan' | 'chat' | 'browser' | 'git' | 'pr' | 'notes' | 'terminal';
|
||||||
export type MermaidRenderingMode = 'svg' | 'ascii';
|
export type MermaidRenderingMode = 'svg' | 'ascii';
|
||||||
@@ -84,8 +82,6 @@ type PendingFileNavigation = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type WorkspaceSurfaceGuard = (nextSurface: WorkspaceSurface) => boolean;
|
export type WorkspaceSurfaceGuard = (nextSurface: WorkspaceSurface) => boolean;
|
||||||
/** @deprecated Use WorkspaceSurfaceGuard. */
|
|
||||||
export type MainTabGuard = WorkspaceSurfaceGuard;
|
|
||||||
export type EventStreamStatus =
|
export type EventStreamStatus =
|
||||||
| 'idle'
|
| 'idle'
|
||||||
| 'connecting'
|
| 'connecting'
|
||||||
@@ -658,15 +654,10 @@ interface UIStore {
|
|||||||
isSessionDropdownOpen: boolean;
|
isSessionDropdownOpen: boolean;
|
||||||
activeSurface: WorkspaceSurface;
|
activeSurface: WorkspaceSurface;
|
||||||
surfaceGuard: WorkspaceSurfaceGuard | null;
|
surfaceGuard: WorkspaceSurfaceGuard | null;
|
||||||
/** @deprecated Use activeSurface. */
|
|
||||||
activeMainTab: WorkspaceSurface;
|
|
||||||
/** @deprecated Use surfaceGuard. */
|
|
||||||
mainTabGuard: WorkspaceSurfaceGuard | null;
|
|
||||||
sidebarOpenBeforeFullscreenTab: boolean | null;
|
sidebarOpenBeforeFullscreenTab: boolean | null;
|
||||||
pendingDiffFile: string | null;
|
pendingDiffFile: string | null;
|
||||||
pendingDiffStaged: boolean;
|
pendingDiffStaged: boolean;
|
||||||
pendingDiffScope: PendingDiffScope | null;
|
pendingDiffScope: PendingDiffScope | null;
|
||||||
pendingDiagramFile: string | null;
|
|
||||||
pendingFileNavigation: PendingFileNavigation | null;
|
pendingFileNavigation: PendingFileNavigation | null;
|
||||||
pendingFileFocusPath: string | null;
|
pendingFileFocusPath: string | null;
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
@@ -854,21 +845,14 @@ interface UIStore {
|
|||||||
setSessionSwitcherOpen: (open: boolean) => void;
|
setSessionSwitcherOpen: (open: boolean) => void;
|
||||||
setSessionDropdownOpen: (open: boolean) => void;
|
setSessionDropdownOpen: (open: boolean) => void;
|
||||||
setActiveSurface: (surface: WorkspaceSurface) => void;
|
setActiveSurface: (surface: WorkspaceSurface) => void;
|
||||||
/** @deprecated Use setActiveSurface. */
|
|
||||||
setActiveMainTab: (surface: WorkspaceSurface) => void;
|
|
||||||
prepareForRuntimeSwitch: (runtimeKey?: string | null) => void;
|
prepareForRuntimeSwitch: (runtimeKey?: string | null) => void;
|
||||||
restoreForRuntimeSwitch: (runtimeKey?: string | null) => void;
|
restoreForRuntimeSwitch: (runtimeKey?: string | null) => void;
|
||||||
setSurfaceGuard: (guard: WorkspaceSurfaceGuard | null) => void;
|
setSurfaceGuard: (guard: WorkspaceSurfaceGuard | null) => void;
|
||||||
/** @deprecated Use setSurfaceGuard. */
|
|
||||||
setMainTabGuard: (guard: WorkspaceSurfaceGuard | null) => void;
|
|
||||||
setPendingDiffFile: (filePath: string | null, staged?: boolean, scope?: PendingDiffScope | null) => void;
|
setPendingDiffFile: (filePath: string | null, staged?: boolean, scope?: PendingDiffScope | null) => void;
|
||||||
setPendingDiagramFile: (filePath: string | null) => void;
|
|
||||||
setPendingFileNavigation: (navigation: PendingFileNavigation | null) => void;
|
setPendingFileNavigation: (navigation: PendingFileNavigation | null) => void;
|
||||||
setPendingFileFocusPath: (path: string | null) => void;
|
setPendingFileFocusPath: (path: string | null) => void;
|
||||||
navigateToDiff: (filePath: string, staged?: boolean, scope?: PendingDiffScope | null) => void;
|
navigateToDiff: (filePath: string, staged?: boolean, scope?: PendingDiffScope | null) => void;
|
||||||
consumePendingDiffFile: () => string | null;
|
consumePendingDiffFile: () => string | null;
|
||||||
navigateToDiagram: (filePath: string) => void;
|
|
||||||
consumePendingDiagramFile: () => string | null;
|
|
||||||
setIsMobile: (isMobile: boolean) => void;
|
setIsMobile: (isMobile: boolean) => void;
|
||||||
toggleCommandPalette: () => void;
|
toggleCommandPalette: () => void;
|
||||||
setCommandPaletteOpen: (open: boolean) => void;
|
setCommandPaletteOpen: (open: boolean) => void;
|
||||||
@@ -1037,13 +1021,10 @@ export const useUIStore = create<UIStore>()(
|
|||||||
isSessionDropdownOpen: false,
|
isSessionDropdownOpen: false,
|
||||||
activeSurface: 'chat',
|
activeSurface: 'chat',
|
||||||
surfaceGuard: null,
|
surfaceGuard: null,
|
||||||
activeMainTab: 'chat',
|
|
||||||
mainTabGuard: null,
|
|
||||||
sidebarOpenBeforeFullscreenTab: null,
|
sidebarOpenBeforeFullscreenTab: null,
|
||||||
pendingDiffFile: null,
|
pendingDiffFile: null,
|
||||||
pendingDiffStaged: false,
|
pendingDiffStaged: false,
|
||||||
pendingDiffScope: null,
|
pendingDiffScope: null,
|
||||||
pendingDiagramFile: null,
|
|
||||||
pendingFileNavigation: null,
|
pendingFileNavigation: null,
|
||||||
pendingFileFocusPath: null,
|
pendingFileFocusPath: null,
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
@@ -1656,29 +1637,25 @@ export const useUIStore = create<UIStore>()(
|
|||||||
if (get().surfaceGuard === guard) {
|
if (get().surfaceGuard === guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
set({ surfaceGuard: guard, mainTabGuard: guard });
|
set({ surfaceGuard: guard });
|
||||||
},
|
},
|
||||||
|
|
||||||
setMainTabGuard: (guard) => get().setSurfaceGuard(guard),
|
|
||||||
|
|
||||||
setActiveSurface: (surface) => {
|
setActiveSurface: (surface) => {
|
||||||
const guard = get().surfaceGuard;
|
const guard = get().surfaceGuard;
|
||||||
if (guard && !guard(surface)) {
|
if (guard && !guard(surface)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
activeSurfaceByRuntime.set(runtimeMemoryKey(), surface);
|
activeSurfaceByRuntime.set(runtimeMemoryKey(), surface);
|
||||||
set({ activeSurface: surface, activeMainTab: surface });
|
set({ activeSurface: surface });
|
||||||
},
|
},
|
||||||
|
|
||||||
setActiveMainTab: (surface) => get().setActiveSurface(surface),
|
|
||||||
|
|
||||||
prepareForRuntimeSwitch: (runtimeKey?: string | null) => {
|
prepareForRuntimeSwitch: (runtimeKey?: string | null) => {
|
||||||
activeSurfaceByRuntime.set(runtimeMemoryKey(runtimeKey), get().activeSurface);
|
activeSurfaceByRuntime.set(runtimeMemoryKey(runtimeKey), get().activeSurface);
|
||||||
},
|
},
|
||||||
|
|
||||||
restoreForRuntimeSwitch: (runtimeKey?: string | null) => {
|
restoreForRuntimeSwitch: (runtimeKey?: string | null) => {
|
||||||
const restored = activeSurfaceByRuntime.get(runtimeMemoryKey(runtimeKey)) ?? 'chat';
|
const restored = activeSurfaceByRuntime.get(runtimeMemoryKey(runtimeKey)) ?? 'chat';
|
||||||
set({ activeSurface: restored, activeMainTab: restored });
|
set({ activeSurface: restored });
|
||||||
},
|
},
|
||||||
|
|
||||||
setPendingDiffFile: (filePath, staged = false, scope = null) => {
|
setPendingDiffFile: (filePath, staged = false, scope = null) => {
|
||||||
@@ -1689,10 +1666,6 @@ export const useUIStore = create<UIStore>()(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setPendingDiagramFile: (filePath) => {
|
|
||||||
set({ pendingDiagramFile: filePath });
|
|
||||||
},
|
|
||||||
|
|
||||||
setPendingFileNavigation: (navigation) => {
|
setPendingFileNavigation: (navigation) => {
|
||||||
set({ pendingFileNavigation: navigation });
|
set({ pendingFileNavigation: navigation });
|
||||||
},
|
},
|
||||||
@@ -1706,7 +1679,7 @@ export const useUIStore = create<UIStore>()(
|
|||||||
if (guard && !guard('diff')) {
|
if (guard && !guard('diff')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
set({ pendingDiffFile: filePath, pendingDiffStaged: staged, pendingDiffScope: scope, activeSurface: 'diff', activeMainTab: 'diff' });
|
set({ pendingDiffFile: filePath, pendingDiffStaged: staged, pendingDiffScope: scope, activeSurface: 'diff' });
|
||||||
},
|
},
|
||||||
|
|
||||||
consumePendingDiffFile: () => {
|
consumePendingDiffFile: () => {
|
||||||
@@ -1717,22 +1690,6 @@ export const useUIStore = create<UIStore>()(
|
|||||||
return pendingDiffFile;
|
return pendingDiffFile;
|
||||||
},
|
},
|
||||||
|
|
||||||
navigateToDiagram: (filePath) => {
|
|
||||||
const guard = get().surfaceGuard;
|
|
||||||
if (guard && !guard('diagram')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
set({ pendingDiagramFile: filePath, activeSurface: 'diagram', activeMainTab: 'diagram' });
|
|
||||||
},
|
|
||||||
|
|
||||||
consumePendingDiagramFile: () => {
|
|
||||||
const { pendingDiagramFile } = get();
|
|
||||||
if (pendingDiagramFile) {
|
|
||||||
set({ pendingDiagramFile: null });
|
|
||||||
}
|
|
||||||
return pendingDiagramFile;
|
|
||||||
},
|
|
||||||
|
|
||||||
setIsMobile: (isMobile) => {
|
setIsMobile: (isMobile) => {
|
||||||
set({ isMobile });
|
set({ isMobile });
|
||||||
},
|
},
|
||||||
@@ -2498,18 +2455,20 @@ export const useUIStore = create<UIStore>()(
|
|||||||
{
|
{
|
||||||
name: 'ui-store',
|
name: 'ui-store',
|
||||||
storage: createDeferredSafeJSONStorage(),
|
storage: createDeferredSafeJSONStorage(),
|
||||||
version: 15,
|
version: 16,
|
||||||
migrate: (persistedState, version) => {
|
migrate: (persistedState, version) => {
|
||||||
if (!persistedState || typeof persistedState !== 'object') {
|
if (!persistedState || typeof persistedState !== 'object') {
|
||||||
return persistedState;
|
return persistedState;
|
||||||
}
|
}
|
||||||
const state = persistedState as Record<string, unknown>;
|
const state = persistedState as Record<string, unknown>;
|
||||||
|
|
||||||
// v14 -> v15: rename the historic main-tab field. The selected
|
// v15 -> v16: the main-area surface concept is gone from persistence
|
||||||
// mobile or promoted desktop view remains unchanged.
|
// (the chat always owns the desktop main area; panel surfaces have
|
||||||
if (version < 15) {
|
// their own state). Drop the historic fields so a stored non-chat
|
||||||
state.activeSurface = state.activeMainTab;
|
// value cannot rehydrate into a blank main area.
|
||||||
state.activeMainTab = state.activeSurface;
|
if (version < 16) {
|
||||||
|
delete state.activeMainTab;
|
||||||
|
delete state.activeSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
// v13 -> v14: the separate 'preview' surface merged into 'browser'.
|
// v13 -> v14: the separate 'preview' surface merged into 'browser'.
|
||||||
@@ -2717,9 +2676,6 @@ export const useUIStore = create<UIStore>()(
|
|||||||
workStatusPanelEnabled: state.workStatusPanelEnabled,
|
workStatusPanelEnabled: state.workStatusPanelEnabled,
|
||||||
workStatusHiddenSections: state.workStatusHiddenSections,
|
workStatusHiddenSections: state.workStatusHiddenSections,
|
||||||
isSessionSwitcherOpen: state.isSessionSwitcherOpen,
|
isSessionSwitcherOpen: state.isSessionSwitcherOpen,
|
||||||
activeSurface: state.activeSurface,
|
|
||||||
// Keep the deprecated mirror synchronized while consumers migrate.
|
|
||||||
activeMainTab: state.activeSurface,
|
|
||||||
sidebarSection: state.sidebarSection,
|
sidebarSection: state.sidebarSection,
|
||||||
settingsPage: state.settingsPage,
|
settingsPage: state.settingsPage,
|
||||||
settingsHasOpenedOnce: state.settingsHasOpenedOnce,
|
settingsHasOpenedOnce: state.settingsHasOpenedOnce,
|
||||||
|
|||||||
Reference in New Issue
Block a user