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