diff --git a/src/__tests__/renderer/components/TabBar.test.tsx b/src/__tests__/renderer/components/TabBar.test.tsx index 6ecce586..497a12e9 100644 --- a/src/__tests__/renderer/components/TabBar.test.tsx +++ b/src/__tests__/renderer/components/TabBar.test.tsx @@ -1512,7 +1512,8 @@ describe('TabBar', () => { expect(inactiveTab.style.backgroundColor).not.toBe('rgba(255, 255, 255, 0.08)'); }); - it('sets tab title attribute', () => { + it('does not set title attribute on tabs (removed for cleaner UX)', () => { + // Tab title tooltips were intentionally removed to streamline the tab interaction feel const tabs = [createTab({ id: 'tab-1', name: 'My Tab', @@ -1531,51 +1532,7 @@ describe('TabBar', () => { ); const tab = screen.getByText('My Tab').closest('[data-tab-id]')!; - expect(tab).toHaveAttribute('title', 'My Tab'); - }); - - it('uses agentSessionId for title when no name', () => { - const tabs = [createTab({ - id: 'tab-1', - name: '', - agentSessionId: 'session-123-abc' - })]; - - render( - - ); - - const tab = screen.getByText('SESSION').closest('[data-tab-id]')!; - expect(tab).toHaveAttribute('title', 'session-123-abc'); - }); - - it('uses "New tab" for title when no name or agentSessionId', () => { - const tabs = [createTab({ - id: 'tab-1', - name: '', - agentSessionId: undefined - })]; - - render( - - ); - - const tab = screen.getByText('New Session').closest('[data-tab-id]')!; - expect(tab).toHaveAttribute('title', 'New tab'); + expect(tab).not.toHaveAttribute('title'); }); }); diff --git a/src/__tests__/renderer/contexts/ToastContext.test.tsx b/src/__tests__/renderer/contexts/ToastContext.test.tsx index d7987377..6dbdb81c 100644 --- a/src/__tests__/renderer/contexts/ToastContext.test.tsx +++ b/src/__tests__/renderer/contexts/ToastContext.test.tsx @@ -233,6 +233,7 @@ describe('ToastContext', () => { taskDuration: 5000, agentSessionId: 'test-session-id', tabName: 'TestTab', + audioNotification: { enabled: false }, }); }); diff --git a/src/main/index.ts b/src/main/index.ts index 224d0c04..50fe18c8 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -32,6 +32,31 @@ interface BootstrapSettings { iCloudSyncEnabled?: boolean; // Legacy - kept for backwards compatibility during migration } +// ============================================================================ +// Data Directory Configuration (MUST happen before any Store initialization) +// ============================================================================ +const isDevelopment = process.env.NODE_ENV === 'development'; + +// Demo mode: use a separate data directory for fresh demos +if (DEMO_MODE) { + app.setPath('userData', DEMO_DATA_PATH); + console.log(`[DEMO MODE] Using data directory: ${DEMO_DATA_PATH}`); +} + +// Development mode: use a separate data directory to allow running alongside production +// This prevents database lock conflicts (e.g., Service Worker storage) +// Set USE_PROD_DATA=1 to use the production data directory instead (requires closing production app) +if (isDevelopment && !DEMO_MODE && !process.env.USE_PROD_DATA) { + const devDataPath = path.join(app.getPath('userData'), '..', 'maestro-dev'); + app.setPath('userData', devDataPath); + console.log(`[DEV MODE] Using data directory: ${devDataPath}`); +} else if (isDevelopment && process.env.USE_PROD_DATA) { + console.log(`[DEV MODE] Using production data directory: ${app.getPath('userData')}`); +} + +// ============================================================================ +// Store Initialization (after userData path is configured) +// ============================================================================ const bootstrapStore = new Store({ name: 'maestro-bootstrap', defaults: {}, @@ -62,12 +87,11 @@ function getSyncPath(): string | undefined { } // Get the sync path once at startup -const syncPath = getSyncPath(); +// If no custom sync path, use the (potentially modified) userData path +const syncPath = getSyncPath() || app.getPath('userData'); -// Initialize Sentry for crash reporting (before app.ready) +// Initialize Sentry for crash reporting // Only enable in production - skip during development to avoid noise from hot-reload artifacts -const isDevelopment = process.env.NODE_ENV === 'development'; - // Check if crash reporting is enabled (default: true for opt-out behavior) const crashReportingStore = new Store<{ crashReportingEnabled: boolean }>({ name: 'maestro-settings', @@ -93,23 +117,6 @@ if (crashReportingEnabled && !isDevelopment) { }); } -// Demo mode: use a separate data directory for fresh demos -if (DEMO_MODE) { - app.setPath('userData', DEMO_DATA_PATH); - console.log(`[DEMO MODE] Using data directory: ${DEMO_DATA_PATH}`); -} - -// Development mode: use a separate data directory to allow running alongside production -// This prevents database lock conflicts (e.g., Service Worker storage) -// Set USE_PROD_DATA=1 to use the production data directory instead (requires closing production app) -if (isDevelopment && !DEMO_MODE && !process.env.USE_PROD_DATA) { - const devDataPath = path.join(app.getPath('userData'), '..', 'maestro-dev'); - app.setPath('userData', devDataPath); - console.log(`[DEV MODE] Using data directory: ${devDataPath}`); -} else if (isDevelopment && process.env.USE_PROD_DATA) { - console.log(`[DEV MODE] Using production data directory: ${app.getPath('userData')}`); -} - // Type definitions interface MaestroSettings { activeThemeId: string; @@ -1405,8 +1412,17 @@ function setupIpcHandlers() { logger.error('TTS stdin error', 'TTS', { error: String(err), code: errorCode }); } }); - child.stdin.write(text); - child.stdin.end(); + console.log('[TTS Main] Writing to stdin:', text); + child.stdin.write(text, 'utf8', (err) => { + if (err) { + console.error('[TTS Main] stdin write error:', err); + } else { + console.log('[TTS Main] stdin write completed, ending stream'); + } + child.stdin!.end(); + }); + } else { + console.error('[TTS Main] No stdin available on child process'); } child.on('error', (err) => { diff --git a/src/renderer/components/FilePreview.tsx b/src/renderer/components/FilePreview.tsx index 18952628..2ae03802 100644 --- a/src/renderer/components/FilePreview.tsx +++ b/src/renderer/components/FilePreview.tsx @@ -756,9 +756,9 @@ export function FilePreview({ file, onClose, theme, markdownEditMode, setMarkdow } } - // Update match count + // Update match count - only reset index if it's truly out of bounds and not already 0 setTotalMatches(allRanges.length); - if (allRanges.length > 0 && currentMatchIndex >= allRanges.length) { + if (allRanges.length > 0 && currentMatchIndex >= allRanges.length && currentMatchIndex !== 0) { setCurrentMatchIndex(0); } diff --git a/src/renderer/contexts/ToastContext.tsx b/src/renderer/contexts/ToastContext.tsx index 800bbd50..450f466e 100644 --- a/src/renderer/contexts/ToastContext.tsx +++ b/src/renderer/contexts/ToastContext.tsx @@ -110,9 +110,12 @@ export function ToastProvider({ children, defaultDuration: initialDuration = 20 // Speak toast via TTS if audio feedback is enabled and command is configured if (audioEnabled && audioCommand) { + console.log('[ToastContext] Triggering TTS with message:', toast.message, 'command:', audioCommand); window.maestro.notification.speak(toast.message, audioCommand).catch(err => { console.error('[ToastContext] Failed to speak toast:', err); }); + } else { + console.log('[ToastContext] TTS skipped - enabled:', audioEnabled, 'command:', audioCommand); } // Show OS notification if enabled