mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
@@ -20,6 +20,44 @@ Settings are organized into tabs:
|
||||
| **AI Commands** | View and edit slash commands, [Spec-Kit](./speckit-commands), and [OpenSpec](./openspec-commands) prompts |
|
||||
| **SSH Hosts** | Configure remote hosts for [SSH agent execution](./ssh-remote-execution) |
|
||||
|
||||
## Conductor Profile
|
||||
|
||||
The **Conductor Profile** (Settings → General → **About Me**) is a short description of yourself that gets injected into every AI agent's system prompt. This helps agents understand your background, preferences, and communication style so they can tailor responses accordingly.
|
||||
|
||||
**To configure:**
|
||||
1. Open **Settings** (`Cmd+,` / `Ctrl+,`) → **General** tab
|
||||
2. Find the **About Me** text area at the top
|
||||
3. Write a brief profile describing yourself
|
||||
|
||||
### What to Include
|
||||
|
||||
A good conductor profile is concise (a few sentences to a short paragraph) and covers:
|
||||
|
||||
- **Your role/background**: Developer, researcher, team lead, etc.
|
||||
- **Technical context**: Languages you work with, tools you prefer, platforms you use
|
||||
- **Communication preferences**: Direct vs. detailed, level of explanation needed
|
||||
- **Work style**: Preferences for how agents should approach tasks
|
||||
|
||||
### Example Profile
|
||||
|
||||
```
|
||||
Security researcher. macOS desktop. TypeScript and Python for tools.
|
||||
Direct communication, no fluff. Action over process. Push back on bad ideas.
|
||||
Generate markdown for Obsidian. CST timezone.
|
||||
```
|
||||
|
||||
### How Agents Use It
|
||||
|
||||
When you start a session, Maestro includes your conductor profile in the system prompt sent to the AI agent. This means:
|
||||
|
||||
- Agents adapt their response style to match your preferences
|
||||
- Technical context helps agents make appropriate assumptions
|
||||
- Communication preferences reduce back-and-forth clarification
|
||||
|
||||
### Using in Custom Commands
|
||||
|
||||
You can reference your conductor profile in [slash commands](./slash-commands) using the `{{CONDUCTOR_PROFILE}}` template variable. This is useful for commands that need to remind agents of your preferences mid-conversation.
|
||||
|
||||
## Checking for Updates
|
||||
|
||||
Maestro checks for updates automatically on startup (configurable in Settings → General → **Check for updates on startup**).
|
||||
@@ -112,7 +150,6 @@ In-app toast notifications appear in the corner when events occur. Configure how
|
||||
Notifications are sent when:
|
||||
- An AI task completes (OS notification + optional custom notification)
|
||||
- A long-running command finishes (OS notification)
|
||||
- The LLM analysis generates a feedback synopsis (custom notification only, if configured)
|
||||
|
||||
## Sleep Prevention
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ vi.mock('../../renderer/components/TemplateAutocompleteDropdown', () => ({
|
||||
|
||||
vi.mock('../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: vi.fn((keys) => keys?.join('+') || ''),
|
||||
isMacOS: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
// Create a mock theme for testing
|
||||
|
||||
@@ -126,6 +126,7 @@ vi.mock('../../renderer/components/TemplateAutocompleteDropdown', () => ({
|
||||
|
||||
vi.mock('../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: vi.fn((keys) => keys?.join('+') || ''),
|
||||
isMacOS: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
vi.mock('../../renderer/hooks/useGitStatusPolling', () => ({
|
||||
|
||||
@@ -96,6 +96,7 @@ vi.mock('../../../renderer/components/AutoRun', () => ({
|
||||
// Mock shortcut formatter
|
||||
vi.mock('../../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: vi.fn((keys: string[]) => keys.join('+')),
|
||||
isMacOS: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
// Create a mock theme for testing
|
||||
|
||||
@@ -38,6 +38,7 @@ vi.mock('../../../renderer/contexts/LayerStackContext', async () => {
|
||||
// Mock formatShortcutKeys to return predictable output
|
||||
vi.mock('../../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: (keys: string[]) => keys.join('+'),
|
||||
isMacOS: () => false,
|
||||
}));
|
||||
|
||||
// Sample theme for testing
|
||||
|
||||
@@ -109,6 +109,7 @@ vi.mock('../../../renderer/utils/tokenCounter', () => ({
|
||||
// Mock shortcut formatter
|
||||
vi.mock('../../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: vi.fn((keys: string) => keys),
|
||||
isMacOS: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
// Mock remarkFileLinks
|
||||
|
||||
@@ -333,9 +333,10 @@ describe('WizardInputPanel', () => {
|
||||
expect(screen.getByText('Enter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows "⌘ + Enter" when enterToSend is false', () => {
|
||||
it('shows "⌘ + Enter" (or "Ctrl + Enter" on non-Mac) when enterToSend is false', () => {
|
||||
render(<WizardInputPanel {...defaultProps} enterToSend={false} />);
|
||||
expect(screen.getByText('⌘ + Enter')).toBeInTheDocument();
|
||||
// Test environment doesn't have Mac user agent, so it shows Ctrl + Enter
|
||||
expect(screen.getByText(/⌘ \+ Enter|Ctrl \+ Enter/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls setEnterToSend when clicked', () => {
|
||||
|
||||
@@ -235,17 +235,18 @@ describe('InputArea', () => {
|
||||
const props = createDefaultProps();
|
||||
render(<InputArea {...props} />);
|
||||
|
||||
const button = screen.getByTitle('Switch to Meta+Enter to send');
|
||||
const button = screen.getByTitle(/Switch to (Cmd|Ctrl)\+Enter to send/);
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveTextContent('Enter');
|
||||
});
|
||||
|
||||
it('renders Cmd+Enter when enterToSend is false', () => {
|
||||
it('renders Cmd+Enter (or Ctrl+Enter on non-Mac) when enterToSend is false', () => {
|
||||
const props = createDefaultProps({ enterToSend: false });
|
||||
render(<InputArea {...props} />);
|
||||
|
||||
const button = screen.getByTitle('Switch to Enter to send');
|
||||
expect(button).toHaveTextContent('⌘ + Enter');
|
||||
// Test environment doesn't have Mac user agent, so it shows Ctrl + Enter
|
||||
expect(button).toHaveTextContent(/⌘ \+ Enter|Ctrl \+ Enter/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1505,7 +1506,7 @@ describe('InputArea', () => {
|
||||
const props = createDefaultProps({ enterToSend: true, setEnterToSend });
|
||||
render(<InputArea {...props} />);
|
||||
|
||||
fireEvent.click(screen.getByTitle('Switch to Meta+Enter to send'));
|
||||
fireEvent.click(screen.getByTitle(/Switch to (Cmd|Ctrl)\+Enter to send/));
|
||||
|
||||
expect(setEnterToSend).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
@@ -148,6 +148,7 @@ vi.mock('../../../renderer/utils/tabHelpers', () => ({
|
||||
// Mock shortcut formatter
|
||||
vi.mock('../../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: vi.fn((keys: string[]) => keys?.join('+') || ''),
|
||||
isMacOS: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
// Configurable git status data for tests - can be modified in individual tests
|
||||
|
||||
@@ -185,8 +185,9 @@ describe('PromptComposerModal', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
// When enterToSend is false, it shows "⌘ + Enter"
|
||||
expect(screen.getByText('⌘ + Enter')).toBeInTheDocument();
|
||||
// When enterToSend is false, it shows "⌘ + Enter" on Mac or "Ctrl + Enter" on other platforms
|
||||
// Test environment doesn't have Mac user agent, so it shows Ctrl + Enter
|
||||
expect(screen.getByText(/⌘ \+ Enter|Ctrl \+ Enter/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render close button with X icon', () => {
|
||||
|
||||
@@ -59,6 +59,7 @@ vi.mock('../../../renderer/services/git', () => ({
|
||||
|
||||
vi.mock('../../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: vi.fn((keys: string[]) => keys.join('+')),
|
||||
isMacOS: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
// Mock lucide-react
|
||||
|
||||
@@ -21,6 +21,7 @@ vi.mock('../../../renderer/components/AutoRun', () => ({
|
||||
|
||||
vi.mock('../../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: vi.fn((keys) => keys.join('+')),
|
||||
isMacOS: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
// Mock lucide-react
|
||||
|
||||
@@ -38,6 +38,7 @@ vi.mock('../../../renderer/contexts/LayerStackContext', () => ({
|
||||
// Mock formatShortcutKeys
|
||||
vi.mock('../../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: vi.fn((keys: string[]) => keys.join('+')),
|
||||
isMacOS: vi.fn(() => false), // Test environment is not Mac
|
||||
}));
|
||||
|
||||
// Mock AICommandsPanel
|
||||
@@ -808,14 +809,15 @@ describe('SettingsModal', () => {
|
||||
expect(setEnterToSendTerminal).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('should display Cmd+Enter when enter-to-send is false', async () => {
|
||||
it('should display Cmd+Enter (or Ctrl+Enter on non-Mac) when enter-to-send is false', async () => {
|
||||
render(<SettingsModal {...createDefaultProps({ enterToSendAI: false })} />);
|
||||
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(100);
|
||||
});
|
||||
|
||||
expect(screen.getByText('⌘ + Enter')).toBeInTheDocument();
|
||||
// Test environment doesn't have Mac user agent, so it shows Ctrl + Enter
|
||||
expect(screen.getByText(/⌘ \+ Enter|Ctrl \+ Enter/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import type { Theme, Shortcut } from '../../../renderer/types';
|
||||
// Mock the shortcutFormatter module
|
||||
vi.mock('../../../renderer/utils/shortcutFormatter', () => ({
|
||||
formatShortcutKeys: vi.fn((keys: string[]) => keys.join('+')),
|
||||
isMacOS: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
// Import after mock to get the mocked version
|
||||
|
||||
@@ -271,7 +271,7 @@ describe('ToastContext', () => {
|
||||
taskDuration: 5000,
|
||||
agentSessionId: 'test-session-id',
|
||||
tabName: 'TestTab',
|
||||
audioNotification: { enabled: false },
|
||||
audioNotification: { enabled: false, reason: 'disabled' },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -364,7 +364,10 @@ export function registerNotificationsHandlers(): void {
|
||||
async (_event, text: string, command?: string): Promise<NotificationCommandResponse> => {
|
||||
// Skip if there's no content to send
|
||||
if (!text || text.trim().length === 0) {
|
||||
logger.debug('Notification skipped - no content to send', 'Notification');
|
||||
logger.info('Notification skipped - empty or whitespace-only content', 'Notification', {
|
||||
textLength: text?.length ?? 0,
|
||||
hasText: !!text,
|
||||
});
|
||||
return { success: true }; // Return success since there's nothing to do
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ Rules:
|
||||
- 2-4 words max, shorter is better
|
||||
- Title Case, no punctuation
|
||||
- Be specific (mention tech/file names if relevant)
|
||||
- Include ID numbers (PR #, issue #, ticket ID) when mentioned
|
||||
- Use hyphens for compound concepts
|
||||
- If too vague, use format: YYYY-MM-DD Topic
|
||||
|
||||
@@ -13,4 +14,7 @@ Examples:
|
||||
"refactor DB queries for pooling" = DB Connection Pooling
|
||||
"write tests for checkout" = Checkout Tests
|
||||
"fix TS errors in parser.ts" = Parser TS Errors
|
||||
"review PR #256" = PR 256 Review
|
||||
"fix issue #42 memory leak" = Issue 42 Memory Leak
|
||||
"implement JIRA-1234 feature" = JIRA-1234 Feature
|
||||
"help with my code" = 2024-01-15 Code Help
|
||||
|
||||
@@ -22,7 +22,7 @@ import type {
|
||||
QueuedItem,
|
||||
Shortcut,
|
||||
} from '../types';
|
||||
import { formatShortcutKeys } from '../utils/shortcutFormatter';
|
||||
import { formatShortcutKeys, isMacOS } from '../utils/shortcutFormatter';
|
||||
import { QueuedItemsList } from './QueuedItemsList';
|
||||
import { normalizeMentionName } from '../utils/participantColors';
|
||||
|
||||
@@ -549,10 +549,10 @@ export const GroupChatInput = React.memo(function GroupChatInput({
|
||||
<button
|
||||
onClick={() => setEnterToSend(!enterToSend)}
|
||||
className="flex items-center gap-1 text-[10px] opacity-50 hover:opacity-100 px-2 py-1 rounded hover:bg-white/5"
|
||||
title={enterToSend ? 'Switch to Meta+Enter to send' : 'Switch to Enter to send'}
|
||||
title={enterToSend ? `Switch to ${isMacOS() ? 'Cmd' : 'Ctrl'}+Enter to send` : 'Switch to Enter to send'}
|
||||
>
|
||||
<Keyboard className="w-3 h-3" />
|
||||
{enterToSend ? 'Enter' : '⌘ + Enter'}
|
||||
{enterToSend ? 'Enter' : isMacOS() ? '⌘ + Enter' : 'Ctrl + Enter'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,6 +26,7 @@ import type { Session, Theme } from '../../types';
|
||||
import { WizardPill } from './WizardPill';
|
||||
import { WizardConfidenceGauge } from './WizardConfidenceGauge';
|
||||
import { WizardExitConfirmDialog } from './WizardExitConfirmDialog';
|
||||
import { isMacOS } from '../../utils/shortcutFormatter';
|
||||
|
||||
interface WizardInputPanelProps {
|
||||
/** Current session with wizard state */
|
||||
@@ -322,10 +323,10 @@ export const WizardInputPanel = React.memo(function WizardInputPanel({
|
||||
<button
|
||||
onClick={() => setEnterToSend(!enterToSend)}
|
||||
className="flex items-center gap-1 text-[10px] opacity-50 hover:opacity-100 px-2 py-1 rounded hover:bg-white/5"
|
||||
title={enterToSend ? 'Switch to Meta+Enter to send' : 'Switch to Enter to send'}
|
||||
title={enterToSend ? `Switch to ${isMacOS() ? 'Cmd' : 'Ctrl'}+Enter to send` : 'Switch to Enter to send'}
|
||||
>
|
||||
<Keyboard className="w-3 h-3" />
|
||||
{enterToSend ? 'Enter' : '⌘ + Enter'}
|
||||
{enterToSend ? 'Enter' : isMacOS() ? '⌘ + Enter' : 'Ctrl + Enter'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
Pin,
|
||||
} from 'lucide-react';
|
||||
import type { Session, Theme, BatchRunState, Shortcut, ThinkingMode } from '../types';
|
||||
import { formatShortcutKeys } from '../utils/shortcutFormatter';
|
||||
import { formatShortcutKeys, isMacOS } from '../utils/shortcutFormatter';
|
||||
import type { TabCompletionSuggestion, TabCompletionFilter } from '../hooks';
|
||||
import type {
|
||||
SummarizeProgress,
|
||||
@@ -1087,10 +1087,10 @@ export const InputArea = React.memo(function InputArea(props: InputAreaProps) {
|
||||
<button
|
||||
onClick={() => setEnterToSend(!enterToSend)}
|
||||
className="flex items-center gap-1 text-[10px] opacity-50 hover:opacity-100 px-2 py-1 rounded hover:bg-white/5"
|
||||
title={enterToSend ? 'Switch to Meta+Enter to send' : 'Switch to Enter to send'}
|
||||
title={enterToSend ? `Switch to ${isMacOS() ? 'Cmd' : 'Ctrl'}+Enter to send` : 'Switch to Enter to send'}
|
||||
>
|
||||
<Keyboard className="w-3 h-3" />
|
||||
{enterToSend ? 'Enter' : '⌘ + Enter'}
|
||||
{enterToSend ? 'Enter' : isMacOS() ? '⌘ + Enter' : 'Ctrl + Enter'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -306,10 +306,6 @@ export function NotificationsPanel({
|
||||
<ul className="text-xs opacity-70 space-y-1" style={{ color: theme.colors.textDim }}>
|
||||
<li>• When an AI task completes</li>
|
||||
<li>• When a long-running command finishes</li>
|
||||
<li>
|
||||
• When the LLM analysis generates a feedback synopsis (custom notification only, if
|
||||
configured)
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
className="text-xs opacity-60 mt-3 pt-3"
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { Theme, ThinkingMode } from '../types';
|
||||
import { useLayerStack } from '../contexts/LayerStackContext';
|
||||
import { MODAL_PRIORITIES } from '../constants/modalPriorities';
|
||||
import { estimateTokenCount } from '../../shared/formatters';
|
||||
import { isMacOS } from '../utils/shortcutFormatter';
|
||||
|
||||
interface PromptComposerModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -450,11 +451,11 @@ export function PromptComposerModal({
|
||||
<button
|
||||
onClick={onToggleEnterToSend}
|
||||
className="flex items-center gap-1 text-[10px] opacity-50 hover:opacity-100 px-2 py-1 rounded hover:bg-white/5"
|
||||
title={enterToSend ? 'Switch to Meta+Enter to send' : 'Switch to Enter to send'}
|
||||
title={enterToSend ? `Switch to ${isMacOS() ? 'Cmd' : 'Ctrl'}+Enter to send` : 'Switch to Enter to send'}
|
||||
>
|
||||
<Keyboard className="w-3 h-3" style={{ color: theme.colors.textDim }} />
|
||||
<span style={{ color: theme.colors.textDim }}>
|
||||
{enterToSend ? 'Enter' : '⌘ + Enter'}
|
||||
{enterToSend ? 'Enter' : isMacOS() ? '⌘ + Enter' : 'Ctrl + Enter'}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -49,7 +49,7 @@ import { MODAL_PRIORITIES } from '../constants/modalPriorities';
|
||||
import { AICommandsPanel } from './AICommandsPanel';
|
||||
import { SpecKitCommandsPanel } from './SpecKitCommandsPanel';
|
||||
import { OpenSpecCommandsPanel } from './OpenSpecCommandsPanel';
|
||||
import { formatShortcutKeys } from '../utils/shortcutFormatter';
|
||||
import { formatShortcutKeys, isMacOS } from '../utils/shortcutFormatter';
|
||||
import { ToggleButtonGroup } from './ToggleButtonGroup';
|
||||
import { SettingCheckbox } from './SettingCheckbox';
|
||||
import { FontConfigurationPanel } from './FontConfigurationPanel';
|
||||
@@ -1302,8 +1302,8 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
|
||||
Input Send Behavior
|
||||
</label>
|
||||
<p className="text-xs opacity-50 mb-3">
|
||||
Configure how to send messages in each mode. Choose between Enter or Command+Enter
|
||||
for each input type.
|
||||
Configure how to send messages in each mode. Choose between Enter or{' '}
|
||||
{isMacOS() ? 'Command' : 'Ctrl'}+Enter for each input type.
|
||||
</p>
|
||||
|
||||
{/* AI Mode Setting */}
|
||||
@@ -1324,13 +1324,13 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
|
||||
border: `1px solid ${theme.colors.border}`,
|
||||
}}
|
||||
>
|
||||
{props.enterToSendAI ? 'Enter' : '⌘ + Enter'}
|
||||
{props.enterToSendAI ? 'Enter' : isMacOS() ? '⌘ + Enter' : 'Ctrl + Enter'}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs opacity-50">
|
||||
{props.enterToSendAI
|
||||
? 'Press Enter to send. Use Shift+Enter for new line.'
|
||||
: 'Press Command+Enter to send. Enter creates new line.'}
|
||||
: `Press ${isMacOS() ? 'Command' : 'Ctrl'}+Enter to send. Enter creates new line.`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1352,13 +1352,13 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
|
||||
border: `1px solid ${theme.colors.border}`,
|
||||
}}
|
||||
>
|
||||
{props.enterToSendTerminal ? 'Enter' : '⌘ + Enter'}
|
||||
{props.enterToSendTerminal ? 'Enter' : isMacOS() ? '⌘ + Enter' : 'Ctrl + Enter'}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs opacity-50">
|
||||
{props.enterToSendTerminal
|
||||
? 'Press Enter to send. Use Shift+Enter for new line.'
|
||||
: 'Press Command+Enter to send. Enter creates new line.'}
|
||||
: `Press ${isMacOS() ? 'Command' : 'Ctrl'}+Enter to send. Enter creates new line.`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,6 +34,7 @@ import type { WizardError } from '../services/wizardErrorDetection';
|
||||
import { AUTO_RUN_FOLDER_NAME, wizardDebugLogger } from '../services/phaseGenerator';
|
||||
import { getNextFillerPhrase } from '../services/fillerPhrases';
|
||||
import { ScreenReaderAnnouncement } from '../ScreenReaderAnnouncement';
|
||||
import { isMacOS } from '../../../utils/shortcutFormatter';
|
||||
|
||||
interface ConversationScreenProps {
|
||||
theme: Theme;
|
||||
@@ -1613,7 +1614,7 @@ export function ConversationScreen({
|
||||
className="px-1.5 py-0.5 rounded text-xs"
|
||||
style={{ backgroundColor: theme.colors.border }}
|
||||
>
|
||||
⌘+Enter
|
||||
{isMacOS() ? '⌘' : 'Ctrl'}+Enter
|
||||
</kbd>
|
||||
Send
|
||||
</span>
|
||||
|
||||
@@ -98,7 +98,16 @@ export function ToastProvider({
|
||||
// Capture audio feedback state for logging
|
||||
const { enabled: audioEnabled, command: audioCommand } = audioFeedbackRef.current;
|
||||
|
||||
// Determine if we have content to send for custom notification
|
||||
// Also skip if there's no content to send
|
||||
const hasContent = toast.message && toast.message.trim().length > 0;
|
||||
|
||||
// Determine if custom notification will actually be triggered
|
||||
const willTriggerCustomNotification =
|
||||
audioEnabled && audioCommand && !toast.skipCustomNotification && hasContent;
|
||||
|
||||
// Log toast to system logs (include audio notification info)
|
||||
// Only log enabled: true when we will actually trigger the notification
|
||||
window.maestro.logger.toast(toast.title, {
|
||||
type: toast.type,
|
||||
message: toast.message,
|
||||
@@ -107,23 +116,29 @@ export function ToastProvider({
|
||||
taskDuration: toast.taskDuration,
|
||||
agentSessionId: toast.agentSessionId,
|
||||
tabName: toast.tabName,
|
||||
// Audio/TTS notification info
|
||||
audioNotification:
|
||||
audioEnabled && audioCommand
|
||||
// Audio/TTS notification info - reflects whether notification WILL be triggered
|
||||
audioNotification: willTriggerCustomNotification
|
||||
? {
|
||||
enabled: true,
|
||||
command: audioCommand,
|
||||
}
|
||||
: {
|
||||
enabled: false,
|
||||
reason: !audioEnabled
|
||||
? 'disabled'
|
||||
: !audioCommand
|
||||
? 'no-command'
|
||||
: toast.skipCustomNotification
|
||||
? 'opted-out'
|
||||
: !hasContent
|
||||
? 'no-content'
|
||||
: 'unknown',
|
||||
},
|
||||
});
|
||||
|
||||
// Run custom notification command if enabled and configured
|
||||
// Skip for toasts that explicitly opt out (e.g., synopsis messages)
|
||||
// Also skip if there's no content to send
|
||||
const hasContent = toast.message && toast.message.trim().length > 0;
|
||||
if (audioEnabled && audioCommand && !toast.skipCustomNotification && hasContent) {
|
||||
if (willTriggerCustomNotification) {
|
||||
console.log(
|
||||
'[ToastContext] Running custom notification with message:',
|
||||
toast.message,
|
||||
|
||||
Reference in New Issue
Block a user