## CHANGES

- Removed tab title tooltips for a cleaner, distraction-free TabBar UX 🧼
- Added demo-mode and dev-mode isolated data directories to avoid conflicts 🗂️
- Defaulted sync storage path to configured userData when unset 🔄
- Ensured userData path is set before Store initialization for correctness 🧭
- Improved TTS IPC reliability with utf8 writes, callbacks, and stdin checks 🗣️
- Added richer TTS debug logging in main process for easier troubleshooting 🔍
- Logged ToastContext TTS triggers and explicit skip reasons for clarity 📣
- Extended toast test coverage to include audioNotification disabled state 
- Fixed FilePreview search match index reset to prevent unnecessary jumps 🎯
This commit is contained in:
Pedram Amini
2025-12-28 09:31:01 -06:00
parent d65b8d2c6b
commit 07f5f2c4ec
5 changed files with 48 additions and 71 deletions

View File

@@ -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(
<TabBar
tabs={tabs}
activeTabId="tab-1"
theme={mockTheme}
onTabSelect={mockOnTabSelect}
onTabClose={mockOnTabClose}
onNewTab={mockOnNewTab}
/>
);
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(
<TabBar
tabs={tabs}
activeTabId="tab-1"
theme={mockTheme}
onTabSelect={mockOnTabSelect}
onTabClose={mockOnTabClose}
onNewTab={mockOnNewTab}
/>
);
const tab = screen.getByText('New Session').closest('[data-tab-id]')!;
expect(tab).toHaveAttribute('title', 'New tab');
expect(tab).not.toHaveAttribute('title');
});
});

View File

@@ -233,6 +233,7 @@ describe('ToastContext', () => {
taskDuration: 5000,
agentSessionId: 'test-session-id',
tabName: 'TestTab',
audioNotification: { enabled: false },
});
});

View File

@@ -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<BootstrapSettings>({
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) => {

View File

@@ -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);
}

View File

@@ -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