Restore Terminal Session History on App Restart (#1515)
* auto-claude: subtask-1-1 - Add debug logging to main process session restoration flow Add debug logging to trace outputBuffer handling in terminal session restoration to help diagnose session history restoration issues: - terminal-lifecycle.ts: Log outputBuffer lengths for passed vs stored sessions, and log buffer preview when returning for replay - terminal-session-store.ts: Log outputBuffer info when getting sessions, updating sessions in memory, migrating from previous dates, and updating output buffer (throttled to avoid spam) Uses debugLog from shared debug-logger utility - only outputs when DEBUG=true. Co-Authored-By: Claude Opus 4.5 <[email protected]> * auto-claude: subtask-1-2 - Add debug logging to renderer restoration flow Add comprehensive debug logging to trace terminal session restoration: - terminal-store.ts: Log restored terminal additions, buffer restoration, session fetching from disk, and restoration completion - usePtyProcess.ts: Log PTY creation/restoration flow including skips, success, and error cases with retry logic - useXterm.ts: Log xterm initialization, buffer replay, output callback registration, dimension ready events, and serialization All logging uses debugLog/debugError from shared utils (only logs when DEBUG=true environment variable is set). * fix(terminal): ensure output buffer is restored before existence check Move terminalBufferManager.set() BEFORE the early return in addRestoredTerminal(). This fixes a bug where terminal chat history was not restored on app restart because: 1. If terminal already existed in store, function returned early 2. Buffer was never stored in terminalBufferManager 3. useXterm read empty buffer and displayed nothing Now the buffer is always restored first, regardless of whether the terminal already exists, ensuring chat history is visible after app restart. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(terminal): initialize pendingClaudeResume during session restoration Fix Claude resume timing race condition. The TERMINAL_PENDING_RESUME IPC event was sent before the renderer's Terminal component mounted its listener, causing the event to be lost. Now addRestoredTerminal() initializes pendingClaudeResume from session.isClaudeMode, so the renderer knows to trigger 'claude --continue' when the terminal becomes active without relying on IPC timing. Co-Authored-By: Claude Opus 4.5 <[email protected]> * auto-claude: subtask-3-3 - Add visual indicator for terminals with pending Claude resume - Added pendingClaudeResume prop to TerminalHeader component - Visual indicator shows cyan pulsing badge with RotateCcw icon - Badge displays "Resume Available" text (collapses to icon on narrow terminals) - Tooltip explains user can click to resume previous Claude session - Added i18n translations for English and French - Terminal.tsx passes pendingClaudeResume from terminal store to header Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(terminal): address PR review findings - Update pendingClaudeResume for existing terminals during re-restore to ensure deferred Claude resume works in project switch scenarios - Remove sensitive terminal output preview from debug logs - Add atomic getAndClear() method to prevent theoretical buffer data loss between get() and clear() operations Co-Authored-By: Claude Sonnet 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
This commit is contained in:
committed by
StillKnotKnown
co-authored by
Claude Opus 4.5
parent
305d37dfe7
commit
feb8304541
@@ -124,7 +124,7 @@ export function usePtyProcess({
|
||||
|
||||
// Normal skip (not during recreation) - just return
|
||||
if (skipCreation) {
|
||||
debugLog(`[usePtyProcess] Skipping PTY creation for terminal: ${terminalId} - dimensions not ready (skipCreation=true)`);
|
||||
debugLog(`[usePtyProcess] Skipping PTY creation for terminal: ${terminalId} - dimensions not ready`);
|
||||
return;
|
||||
}
|
||||
if (isCreatingRef.current || isCreatedRef.current) {
|
||||
@@ -140,9 +140,7 @@ export function usePtyProcess({
|
||||
const alreadyRunning = terminalState?.status === 'running' || terminalState?.status === 'claude-active';
|
||||
const isRestored = terminalState?.isRestored;
|
||||
|
||||
debugLog(`[usePtyProcess] Starting PTY creation for terminal: ${terminalId}`);
|
||||
debugLog(`[usePtyProcess] Terminal ${terminalId} state: isRestored=${isRestored}, status=${terminalState?.status}`);
|
||||
debugLog(`[usePtyProcess] Terminal ${terminalId} dimensions for PTY: cols=${cols}, rows=${rows}`);
|
||||
debugLog(`[usePtyProcess] Starting PTY creation for terminal: ${terminalId}, isRestored: ${isRestored}, status: ${terminalState?.status}, cols: ${cols}, rows: ${rows}`);
|
||||
|
||||
// When recreating (e.g., worktree switching), reset status from 'exited' to 'idle'
|
||||
// This allows proper recreation after deliberate terminal destruction
|
||||
|
||||
@@ -5,12 +5,18 @@ import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
import { SerializeAddon } from '@xterm/addon-serialize';
|
||||
import { terminalBufferManager } from '../../lib/terminal-buffer-manager';
|
||||
import { registerOutputCallback, unregisterOutputCallback } from '../../stores/terminal-store';
|
||||
import { useTerminalFontSettingsStore } from '../../stores/terminal-font-settings-store';
|
||||
import { isWindows as checkIsWindows, isLinux as checkIsLinux } from '../../lib/os-detection';
|
||||
import { debounce } from '../../lib/debounce';
|
||||
import { DEFAULT_TERMINAL_THEME } from '../../lib/terminal-theme';
|
||||
import { debugLog, debugError } from '../../../shared/utils/debug-logger';
|
||||
|
||||
// Type augmentation for navigator.userAgentData (modern User-Agent Client Hints API)
|
||||
interface NavigatorUAData {
|
||||
platform: string;
|
||||
}
|
||||
declare global {
|
||||
interface Navigator {
|
||||
userAgentData?: NavigatorUAData;
|
||||
}
|
||||
}
|
||||
|
||||
interface UseXtermOptions {
|
||||
terminalId: string;
|
||||
onCommandEnter?: (command: string) => void;
|
||||
@@ -72,13 +78,6 @@ export function useXterm({ terminalId, onCommandEnter, onResize, onDimensionsRea
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset refs when (re)initializing xterm
|
||||
// This is critical for React StrictMode which unmounts/remounts components,
|
||||
// causing dispose() to set isDisposedRef.current = true on the first unmount.
|
||||
// Without this reset, the remounted component would still have isDisposed = true.
|
||||
isDisposedRef.current = false;
|
||||
dimensionsReadyCalledRef.current = false;
|
||||
|
||||
debugLog(`[useXterm] Initializing xterm for terminal: ${terminalId}`);
|
||||
|
||||
const xterm = new XTerm({
|
||||
@@ -249,7 +248,7 @@ export function useXterm({ terminalId, onCommandEnter, onResize, onDimensionsRea
|
||||
// Call onDimensionsReady once when we have valid dimensions
|
||||
if (!dimensionsReadyCalledRef.current && cols > 0 && rows > 0) {
|
||||
dimensionsReadyCalledRef.current = true;
|
||||
debugLog(`[useXterm] Dimensions ready for terminal: ${terminalId}, cols: ${cols}, rows: ${rows}, containerWidth: ${rect.width}, containerHeight: ${rect.height}`);
|
||||
debugLog(`[useXterm] Dimensions ready for terminal: ${terminalId}, cols: ${cols}, rows: ${rows}`);
|
||||
onDimensionsReady?.(cols, rows);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
},
|
||||
"resume": {
|
||||
"pending": "Resume Available",
|
||||
"pendingTooltip": "Click to resume previous Claude session",
|
||||
"resumeAllSessions": "Resume All"
|
||||
"pendingTooltip": "Click to resume previous Claude session"
|
||||
},
|
||||
"auth": {
|
||||
"terminalTitle": "Auth: {{profileName}}",
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
},
|
||||
"resume": {
|
||||
"pending": "Reprise disponible",
|
||||
"pendingTooltip": "Cliquez pour reprendre la session Claude précédente",
|
||||
"resumeAllSessions": "Reprendre Tout"
|
||||
"pendingTooltip": "Cliquez pour reprendre la session Claude précédente"
|
||||
},
|
||||
"auth": {
|
||||
"terminalTitle": "Auth: {{profileName}}",
|
||||
|
||||
Reference in New Issue
Block a user