diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index ebb98277..06cd4e1a 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1111,6 +1111,9 @@ function MaestroConsoleInner() { }, ], activeTabId: defaultTabId, + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: defaultTabId }], }; } @@ -1235,6 +1238,12 @@ function MaestroConsoleInner() { agentError: undefined, agentErrorPaused: false, closedTabHistory: [], // Runtime-only, reset on load + // File preview tabs - initialize from persisted data or empty + filePreviewTabs: correctedSession.filePreviewTabs || [], + activeFileTabId: correctedSession.activeFileTabId ?? null, + unifiedTabOrder: + correctedSession.unifiedTabOrder || + resetAiTabs.map((tab) => ({ type: 'ai' as const, id: tab.id })), }; } else { // Process spawn failed @@ -1652,6 +1661,9 @@ function MaestroConsoleInner() { aiTabs: [initialTab], activeTabId: initialTabId, closedTabHistory: [], + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: initialTabId }], customPath: parentSession.customPath, customArgs: parentSession.customArgs, customEnvVars: parentSession.customEnvVars, @@ -7287,6 +7299,9 @@ You are taking over this conversation. Based on the context above, provide a bri aiTabs: [initialTab], activeTabId: initialTabId, closedTabHistory: [], + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: initialTabId }], customPath: parentSession.customPath, customArgs: parentSession.customArgs, customEnvVars: parentSession.customEnvVars, @@ -7468,6 +7483,9 @@ You are taking over this conversation. Based on the context above, provide a bri aiTabs: [initialTab], activeTabId: initialTabId, closedTabHistory: [], + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: initialTabId }], customPath: session.customPath, customArgs: session.customArgs, customEnvVars: session.customEnvVars, @@ -8756,6 +8774,10 @@ You are taking over this conversation. Based on the context above, provide a bri aiTabs: [initialTab], activeTabId: initialTabId, closedTabHistory: [], + // File preview tabs - start empty, unified tab order starts with initial AI tab + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: initialTabId }], // Nudge message - appended to every interactive user message nudgeMessage, // Per-agent config (path, args, env vars, model) @@ -8922,6 +8944,9 @@ You are taking over this conversation. Based on the context above, provide a bri aiTabs: [initialTab], activeTabId: initialTabId, closedTabHistory: [], + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: initialTabId }], // Auto Run configuration from wizard autoRunFolderPath, autoRunSelectedFile, @@ -10958,6 +10983,9 @@ You are taking over this conversation. Based on the context above, provide a bri aiTabs: [initialTab], activeTabId: initialTabId, closedTabHistory: [], + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: initialTabId }], customPath: activeSession.customPath, customArgs: activeSession.customArgs, customEnvVars: activeSession.customEnvVars, @@ -11133,6 +11161,9 @@ You are taking over this conversation. Based on the context above, provide a bri aiTabs: [initialTab], activeTabId: initialTabId, closedTabHistory: [], + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: initialTabId }], customPath: activeSession.customPath, customArgs: activeSession.customArgs, customEnvVars: activeSession.customEnvVars, @@ -11282,6 +11313,9 @@ You are taking over this conversation. Based on the context above, provide a bri aiTabs: [initialTab], activeTabId: initialTabId, closedTabHistory: [], + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: initialTabId }], customPath: createWorktreeSession.customPath, customArgs: createWorktreeSession.customArgs, customEnvVars: createWorktreeSession.customEnvVars, @@ -13215,6 +13249,9 @@ You are taking over this conversation. Based on the context above, provide a bri aiTabs: [initialTab], activeTabId: initialTabId, closedTabHistory: [], + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: initialTabId }], // Custom agent config customPath: data.customPath, customArgs: data.customArgs, diff --git a/src/renderer/types/index.ts b/src/renderer/types/index.ts index 3c4c3a06..2a2e2db8 100644 --- a/src/renderer/types/index.ts +++ b/src/renderer/types/index.ts @@ -442,6 +442,29 @@ export interface ClosedTab { closedAt: number; // Timestamp when closed } +/** + * File Preview Tab for in-tab file viewing. + * Designed to coexist with AITab and future terminal tabs in the unified tab system. + * File tabs persist across session switches and app restarts. + */ +export interface FilePreviewTab { + id: string; // Unique tab ID (UUID) + path: string; // Full file path + name: string; // Filename without extension (displayed as tab name) + extension: string; // File extension with dot (e.g., '.md', '.ts') - shown as badge + scrollTop: number; // Saved scroll position + searchQuery: string; // Preserved search query + editMode: boolean; // Whether tab was in edit mode + editContent: string | undefined; // Unsaved edit content (undefined if no pending changes) + createdAt: number; // Timestamp for ordering +} + +/** + * Reference to any tab in the unified tab system. + * Used for unified tab ordering across different tab types. + */ +export type UnifiedTabRef = { type: 'ai' | 'file'; id: string }; + export interface Session { id: string; groupId?: string; @@ -559,6 +582,15 @@ export interface Session { activeTabId: string; // Stack of recently closed tabs for undo (max 25, runtime-only, not persisted) closedTabHistory: ClosedTab[]; + + // File Preview Tabs - in-tab file viewing (coexists with AI tabs and future terminal tabs) + // Tabs are interspersed visually but stored separately for type safety + filePreviewTabs: FilePreviewTab[]; + // Currently active file tab ID (null if an AI tab is active) + activeFileTabId: string | null; + // Unified tab ordering - determines visual order of all tabs (AI and file) + unifiedTabOrder: UnifiedTabRef[]; + // Saved scroll position for terminal/shell output view terminalScrollTop?: number; // Draft input for terminal mode (persisted across session switches) diff --git a/src/renderer/utils/tabHelpers.ts b/src/renderer/utils/tabHelpers.ts index 0b42cbb1..8ee55ba8 100644 --- a/src/renderer/utils/tabHelpers.ts +++ b/src/renderer/utils/tabHelpers.ts @@ -893,6 +893,9 @@ export function createMergedSession( aiTabs: [mergedTab], activeTabId: tabId, closedTabHistory: [], + filePreviewTabs: [], + activeFileTabId: null, + unifiedTabOrder: [{ type: 'ai' as const, id: tabId }], }; return { session, tabId };