mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
MAESTRO: fix 73+ type errors in renderer hooks and components
Major type fixes: - useSettings.ts: Add type assertions for settings.get() return values - global.d.ts: Fix playbooks API types (maxLoops, branchNameTemplate, etc.) - global.d.ts: Fix logger API to use proper log level union type - useAgentExecution.ts: Simplify ref type definitions - useExpandedSet.ts: Fix Set iteration type issues - useTemplateAutocomplete.ts: Cast refs array properly Component fixes: - Fix ringColor CSS property -> use --tw-ring-color CSS variable (11 files) - Fix Lucide icon title prop -> wrap icons in span with title - Fix confetti shapes type in FirstRunCelebration and StandingOvation - Fix nullable costUsd checks in AgentSessionsBrowser, SessionListItem - Fix isDuplicate boolean coercion in AutoRunDocumentSelector - Add lastAcknowledgedBadgeLevel to PlaygroundPanel mock stats - Make GitLogEntry additions/deletions optional to match IPC - Fix listDocuments -> listDocs API call in DirectorySelectionScreen Reduces lint errors from 102 to 29.
This commit is contained in:
@@ -729,7 +729,7 @@ export function AgentSessionsBrowser({
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-lg font-mono font-semibold" style={{ color: theme.colors.success }}>
|
||||
${viewingSession.costUsd.toFixed(2)}
|
||||
${(viewingSession.costUsd ?? 0).toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export function AutoRunDocumentSelector({
|
||||
const fullNewPath = selectedCreateFolder
|
||||
? `${selectedCreateFolder}/${normalizedNewName}`.toLowerCase()
|
||||
: normalizedNewName;
|
||||
const isDuplicate = fullNewPath && documents.some(
|
||||
const isDuplicate = !!fullNewPath && documents.some(
|
||||
doc => doc.toLowerCase() === fullNewPath
|
||||
);
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ export function FirstRunCelebration({
|
||||
scalar: 1.2,
|
||||
ticks: 300,
|
||||
flat: false,
|
||||
shapes: ['circle', 'star', 'square'] as const,
|
||||
shapes: ['circle', 'star', 'square'] as ('circle' | 'star' | 'square')[],
|
||||
colors: confettiColors,
|
||||
zIndex: CONFETTI_Z_INDEX,
|
||||
disableForReducedMotion: true,
|
||||
@@ -152,7 +152,7 @@ export function FirstRunCelebration({
|
||||
confetti({
|
||||
...defaults,
|
||||
particleCount: 100,
|
||||
shapes: ['star'] as const,
|
||||
shapes: ['star'] as ('star')[],
|
||||
colors: [goldColor, '#FFA500', '#FFD700'],
|
||||
origin: { x: 0.5, y: 0.3 },
|
||||
startVelocity: 40,
|
||||
|
||||
@@ -16,8 +16,8 @@ interface GitLogEntry {
|
||||
date: string;
|
||||
refs: string[];
|
||||
subject: string;
|
||||
additions: number;
|
||||
deletions: number;
|
||||
additions?: number;
|
||||
deletions?: number;
|
||||
}
|
||||
|
||||
interface GitLogViewerProps {
|
||||
|
||||
@@ -608,7 +608,9 @@ export const MainPanel = forwardRef<MainPanelHandle, MainPanelProps>(function Ma
|
||||
</span>
|
||||
)}
|
||||
{currentSessionBatchState?.worktreeActive && (
|
||||
<GitBranch className="w-4 h-4 ml-1" title={`Worktree: ${currentSessionBatchState.worktreeBranch || 'active'}`} />
|
||||
<span title={`Worktree: ${currentSessionBatchState.worktreeBranch || 'active'}`}>
|
||||
<GitBranch className="w-4 h-4 ml-1" />
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -326,8 +326,8 @@ export function NewInstanceModal({ isOpen, onClose, onCreate, theme, existingSes
|
||||
style={{
|
||||
borderColor: theme.colors.border,
|
||||
backgroundColor: isSelected ? theme.colors.accentDim : 'transparent',
|
||||
ringColor: theme.colors.accent,
|
||||
}}
|
||||
'--tw-ring-color': theme.colors.accent,
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{/* Collapsed header row */}
|
||||
<div
|
||||
|
||||
@@ -156,6 +156,7 @@ export function PlaygroundPanel({ theme, themeMode, onClose }: PlaygroundPanelPr
|
||||
totalRuns: mockTotalRuns,
|
||||
currentBadgeLevel: getBadgeForTime(mockCumulativeTime)?.level || 0,
|
||||
lastBadgeUnlockLevel: mockBadgeHistory.length > 0 ? mockBadgeHistory[mockBadgeHistory.length - 1].level : 0,
|
||||
lastAcknowledgedBadgeLevel: mockBadgeHistory.length > 0 ? mockBadgeHistory[mockBadgeHistory.length - 1].level : 0,
|
||||
badgeHistory: mockBadgeHistory,
|
||||
};
|
||||
|
||||
|
||||
@@ -501,7 +501,9 @@ export const RightPanel = forwardRef<RightPanelHandle, RightPanelProps>(function
|
||||
{currentSessionBatchState.isStopping ? 'Stopping...' : 'Auto Run Active'}
|
||||
</span>
|
||||
{currentSessionBatchState.worktreeActive && (
|
||||
<GitBranch className="w-4 h-4" style={{ color: theme.colors.warning }} title={`Worktree: ${currentSessionBatchState.worktreeBranch || 'active'}`} />
|
||||
<span title={`Worktree: ${currentSessionBatchState.worktreeBranch || 'active'}`}>
|
||||
<GitBranch className="w-4 h-4" style={{ color: theme.colors.warning }} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{/* Elapsed time */}
|
||||
|
||||
@@ -912,8 +912,8 @@ export function SessionList(props: SessionListProps) {
|
||||
width: leftSidebarOpen ? `${leftSidebarWidthState}px` : '64px',
|
||||
backgroundColor: theme.colors.bgSidebar,
|
||||
borderColor: theme.colors.border,
|
||||
ringColor: theme.colors.accent
|
||||
}}
|
||||
'--tw-ring-color': theme.colors.accent
|
||||
} as React.CSSProperties}
|
||||
onClick={() => setActiveFocus('sidebar')}
|
||||
onFocus={() => setActiveFocus('sidebar')}
|
||||
onKeyDown={(e) => {
|
||||
@@ -1900,7 +1900,7 @@ export function SessionList(props: SessionListProps) {
|
||||
onClick={() => setActiveSessionId(session.id)}
|
||||
onContextMenu={(e) => handleContextMenu(e, session.id)}
|
||||
className={`group relative w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-all ${activeSessionId === session.id ? 'ring-2' : 'hover:bg-white/10'}`}
|
||||
style={{ ringColor: theme.colors.accent }}
|
||||
style={{ '--tw-ring-color': theme.colors.accent } as React.CSSProperties}
|
||||
>
|
||||
<div className="relative">
|
||||
<div
|
||||
|
||||
@@ -65,7 +65,7 @@ export interface SessionListItemProps {
|
||||
/** Ref to attach to selected item */
|
||||
selectedItemRef: React.RefObject<HTMLButtonElement | HTMLDivElement | null>;
|
||||
/** Ref for rename input */
|
||||
renameInputRef: React.RefObject<HTMLInputElement | null>;
|
||||
renameInputRef: React.RefObject<HTMLInputElement>;
|
||||
/** Handler for clicking a session row */
|
||||
onSessionClick: (session: ClaudeSession) => void;
|
||||
/** Handler for toggling star status */
|
||||
@@ -270,10 +270,10 @@ export function SessionListItem({
|
||||
</span>
|
||||
|
||||
{/* Cost per session */}
|
||||
{session.costUsd > 0 && (
|
||||
{(session.costUsd ?? 0) > 0 && (
|
||||
<span className="flex items-center gap-1 font-mono" style={{ color: theme.colors.success }}>
|
||||
<DollarSign className="w-3 h-3" />
|
||||
{session.costUsd.toFixed(2)}
|
||||
{(session.costUsd ?? 0).toFixed(2)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
|
||||
@@ -636,8 +636,8 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
|
||||
style={{
|
||||
borderColor: theme.colors.border,
|
||||
backgroundColor: t.colors.bgSidebar,
|
||||
ringColor: t.colors.accent
|
||||
}}
|
||||
'--tw-ring-color': t.colors.accent
|
||||
} as React.CSSProperties}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
@@ -840,9 +840,9 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
|
||||
style={{
|
||||
borderColor: theme.colors.border,
|
||||
backgroundColor: props.defaultShell === shell.id ? theme.colors.accentDim : theme.colors.bgMain,
|
||||
ringColor: theme.colors.accent,
|
||||
'--tw-ring-color': theme.colors.accent,
|
||||
color: theme.colors.textMain,
|
||||
}}
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
@@ -884,9 +884,9 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
|
||||
style={{
|
||||
borderColor: theme.colors.border,
|
||||
backgroundColor: theme.colors.accentDim,
|
||||
ringColor: theme.colors.accent,
|
||||
'--tw-ring-color': theme.colors.accent,
|
||||
color: theme.colors.textMain,
|
||||
}}
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
@@ -1429,8 +1429,8 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
|
||||
borderColor: recordingId === sc.id ? theme.colors.accent : theme.colors.border,
|
||||
backgroundColor: recordingId === sc.id ? theme.colors.accentDim : theme.colors.bgActivity,
|
||||
color: recordingId === sc.id ? theme.colors.accent : theme.colors.textDim,
|
||||
ringColor: theme.colors.accent
|
||||
}}
|
||||
'--tw-ring-color': theme.colors.accent
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{recordingId === sc.id ? 'Press keys...' : formatShortcutKeys(sc.keys)}
|
||||
</button>
|
||||
|
||||
@@ -52,8 +52,8 @@ export function ShortcutEditor({ theme, shortcuts, setShortcuts }: ShortcutEdito
|
||||
borderColor: recordingId === sc.id ? theme.colors.accent : theme.colors.border,
|
||||
backgroundColor: recordingId === sc.id ? theme.colors.accentDim : theme.colors.bgActivity,
|
||||
color: recordingId === sc.id ? theme.colors.accent : theme.colors.textDim,
|
||||
ringColor: theme.colors.accent
|
||||
}}
|
||||
'--tw-ring-color': theme.colors.accent
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{recordingId === sc.id ? 'Press keys...' : formatShortcutKeys(sc.keys)}
|
||||
</button>
|
||||
|
||||
@@ -84,7 +84,7 @@ export function StandingOvationOverlay({
|
||||
scalar: 1.2,
|
||||
ticks: 355,
|
||||
flat: false,
|
||||
shapes: ['circle', 'star', 'square'] as const,
|
||||
shapes: ['circle', 'star', 'square'] as ('circle' | 'star' | 'square')[],
|
||||
colors: confettiColors,
|
||||
zIndex: CONFETTI_Z_INDEX,
|
||||
disableForReducedMotion: true,
|
||||
|
||||
@@ -204,8 +204,8 @@ function Tab({
|
||||
marginBottom: isActive ? '-1px' : '0',
|
||||
// Slight z-index for active tab to cover border properly
|
||||
zIndex: isActive ? 1 : 0,
|
||||
ringColor: isDragOver ? theme.colors.accent : 'transparent'
|
||||
}}
|
||||
'--tw-ring-color': isDragOver ? theme.colors.accent : 'transparent'
|
||||
} as React.CSSProperties}
|
||||
onClick={onSelect}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
@@ -244,11 +244,12 @@ function Tab({
|
||||
|
||||
{/* Draft indicator - pencil icon for tabs with unsent input or staged images */}
|
||||
{hasDraft && (
|
||||
<Pencil
|
||||
className="w-3 h-3 shrink-0"
|
||||
style={{ color: theme.colors.warning }}
|
||||
title="Has draft message"
|
||||
/>
|
||||
<span title="Has draft message">
|
||||
<Pencil
|
||||
className="w-3 h-3 shrink-0"
|
||||
style={{ color: theme.colors.warning }}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Shortcut hint badge - shows tab number for Cmd+1-9 navigation */}
|
||||
|
||||
@@ -33,8 +33,8 @@ export function ThemePicker({ theme, themes, activeThemeId, setActiveThemeId }:
|
||||
style={{
|
||||
borderColor: theme.colors.border,
|
||||
backgroundColor: t.colors.bgSidebar,
|
||||
ringColor: theme.colors.accent
|
||||
}}
|
||||
'--tw-ring-color': theme.colors.accent
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-sm font-bold" style={{ color: t.colors.textMain }}>{t.name}</span>
|
||||
|
||||
@@ -192,11 +192,12 @@ const AutoRunPill = memo(({
|
||||
|
||||
{/* Worktree indicator */}
|
||||
{autoRunState.worktreeActive && (
|
||||
<GitBranch
|
||||
className="w-3.5 h-3.5 shrink-0"
|
||||
style={{ color: theme.colors.accent }}
|
||||
title={`Worktree: ${autoRunState.worktreeBranch || 'active'}`}
|
||||
/>
|
||||
<span title={`Worktree: ${autoRunState.worktreeBranch || 'active'}`}>
|
||||
<GitBranch
|
||||
className="w-3.5 h-3.5 shrink-0"
|
||||
style={{ color: theme.colors.accent }}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Divider */}
|
||||
|
||||
@@ -66,9 +66,9 @@ function ToggleButtonGroupInner<T extends string | number>({
|
||||
style={{
|
||||
borderColor: theme.colors.border,
|
||||
backgroundColor: isActive ? activeColor : 'transparent',
|
||||
ringColor: ringColor,
|
||||
'--tw-ring-color': ringColor,
|
||||
color: isActive ? activeTextColor : theme.colors.textMain,
|
||||
}}
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{displayLabel}
|
||||
</button>
|
||||
|
||||
@@ -252,7 +252,8 @@ export function DirectorySelectionScreen({ theme }: DirectorySelectionScreenProp
|
||||
// Check if Auto Run Docs folder exists and has files
|
||||
try {
|
||||
const autoRunPath = `${state.directoryPath}/${AUTO_RUN_FOLDER_NAME}`;
|
||||
const docs = await window.maestro.autorun.listDocuments(autoRunPath);
|
||||
const result = await window.maestro.autorun.listDocs(autoRunPath);
|
||||
const docs = result.success ? result.files : [];
|
||||
|
||||
if (docs && docs.length > 0) {
|
||||
setDirectoryError(
|
||||
|
||||
34
src/renderer/global.d.ts
vendored
34
src/renderer/global.d.ts
vendored
@@ -477,10 +477,10 @@ interface MaestroAPI {
|
||||
toggle: () => Promise<void>;
|
||||
};
|
||||
logger: {
|
||||
log: (level: string, message: string, context?: string, data?: unknown) => Promise<void>;
|
||||
log: (level: 'debug' | 'info' | 'warn' | 'error' | 'toast' | 'autorun', message: string, context?: string, data?: unknown) => Promise<void>;
|
||||
getLogs: (filter?: { level?: string; context?: string; limit?: number }) => Promise<Array<{
|
||||
timestamp: number;
|
||||
level: string;
|
||||
level: 'debug' | 'info' | 'warn' | 'error' | 'toast' | 'autorun';
|
||||
message: string;
|
||||
context?: string;
|
||||
data?: unknown;
|
||||
@@ -492,7 +492,7 @@ interface MaestroAPI {
|
||||
getMaxLogBuffer: () => Promise<number>;
|
||||
toast: (title: string, data?: unknown) => Promise<void>;
|
||||
autorun: (message: string, context?: string, data?: unknown) => Promise<void>;
|
||||
onNewLog: (callback: (log: { timestamp: number; level: string; message: string; context?: string; data?: unknown }) => void) => () => void;
|
||||
onNewLog: (callback: (log: { timestamp: number; level: 'debug' | 'info' | 'warn' | 'error' | 'toast' | 'autorun'; message: string; context?: string; data?: unknown }) => void) => () => void;
|
||||
};
|
||||
claude: {
|
||||
listSessions: (projectPath: string) => Promise<Array<{
|
||||
@@ -703,39 +703,37 @@ interface MaestroAPI {
|
||||
updatedAt: number;
|
||||
documents: Array<{ filename: string; resetOnCompletion: boolean }>;
|
||||
loopEnabled: boolean;
|
||||
maxLoops?: number | null;
|
||||
prompt: string;
|
||||
worktreeSettings?: {
|
||||
enabled: boolean;
|
||||
branchPrefix: string;
|
||||
createPR: boolean;
|
||||
baseBranch?: string;
|
||||
keepWorktree: boolean;
|
||||
branchNameTemplate: string;
|
||||
createPROnCompletion: boolean;
|
||||
prTargetBranch?: string;
|
||||
};
|
||||
}>; error?: string }>;
|
||||
create: (sessionId: string, playbook: {
|
||||
name: string;
|
||||
documents: Array<{ filename: string; resetOnCompletion: boolean }>;
|
||||
loopEnabled: boolean;
|
||||
maxLoops?: number | null;
|
||||
prompt: string;
|
||||
worktreeSettings?: {
|
||||
enabled: boolean;
|
||||
branchPrefix: string;
|
||||
createPR: boolean;
|
||||
baseBranch?: string;
|
||||
keepWorktree: boolean;
|
||||
branchNameTemplate: string;
|
||||
createPROnCompletion: boolean;
|
||||
prTargetBranch?: string;
|
||||
};
|
||||
}) => Promise<{ success: boolean; playbook?: any; error?: string }>;
|
||||
update: (sessionId: string, playbookId: string, updates: Partial<{
|
||||
name: string;
|
||||
documents: Array<{ filename: string; resetOnCompletion: boolean }>;
|
||||
loopEnabled: boolean;
|
||||
maxLoops?: number | null;
|
||||
prompt: string;
|
||||
updatedAt: number;
|
||||
worktreeSettings?: {
|
||||
enabled: boolean;
|
||||
branchPrefix: string;
|
||||
createPR: boolean;
|
||||
baseBranch?: string;
|
||||
keepWorktree: boolean;
|
||||
branchNameTemplate: string;
|
||||
createPROnCompletion: boolean;
|
||||
prTargetBranch?: string;
|
||||
};
|
||||
}>) => Promise<{ success: boolean; playbook?: any; error?: string }>;
|
||||
delete: (sessionId: string, playbookId: string) => Promise<{ success: boolean; error?: string }>;
|
||||
|
||||
@@ -48,7 +48,7 @@ export interface UseAgentExecutionReturn {
|
||||
toolType?: ToolType
|
||||
) => Promise<AgentSpawnResult>;
|
||||
/** Ref to spawnBackgroundSynopsis for use in callbacks that need latest version */
|
||||
spawnBackgroundSynopsisRef: React.MutableRefObject<UseAgentExecutionReturn['spawnBackgroundSynopsis'] | null>;
|
||||
spawnBackgroundSynopsisRef: React.MutableRefObject<((sessionId: string, cwd: string, resumeAgentSessionId: string, prompt: string, toolType?: ToolType) => Promise<AgentSpawnResult>) | null>;
|
||||
/** Ref to spawnAgentWithPrompt for use in callbacks that need latest version */
|
||||
spawnAgentWithPromptRef: React.MutableRefObject<((prompt: string) => Promise<AgentSpawnResult>) | null>;
|
||||
/** Show flash notification (auto-dismisses after 2 seconds) */
|
||||
|
||||
@@ -158,7 +158,7 @@ export function useExpandedSet<T extends string | number = string>(
|
||||
// Convert initial expanded to a Set<T>
|
||||
const initialSet = useMemo(() => {
|
||||
if (!initialExpanded) return new Set<T>();
|
||||
if (initialExpanded instanceof Set) return new Set(initialExpanded) as Set<T>;
|
||||
if (initialExpanded instanceof Set) return new Set<T>([...initialExpanded] as T[]);
|
||||
return new Set(initialExpanded as T[]);
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -906,38 +906,38 @@ export function useSettings(): UseSettingsReturn {
|
||||
const savedWebInterfaceUseCustomPort = await window.maestro.settings.get('webInterfaceUseCustomPort');
|
||||
const savedWebInterfaceCustomPort = await window.maestro.settings.get('webInterfaceCustomPort');
|
||||
|
||||
if (savedEnterToSendAI !== undefined) setEnterToSendAIState(savedEnterToSendAI);
|
||||
if (savedEnterToSendTerminal !== undefined) setEnterToSendTerminalState(savedEnterToSendTerminal);
|
||||
if (savedDefaultSaveToHistory !== undefined) setDefaultSaveToHistoryState(savedDefaultSaveToHistory);
|
||||
if (savedEnterToSendAI !== undefined) setEnterToSendAIState(savedEnterToSendAI as boolean);
|
||||
if (savedEnterToSendTerminal !== undefined) setEnterToSendTerminalState(savedEnterToSendTerminal as boolean);
|
||||
if (savedDefaultSaveToHistory !== undefined) setDefaultSaveToHistoryState(savedDefaultSaveToHistory as boolean);
|
||||
|
||||
if (savedLlmProvider !== undefined) setLlmProviderState(savedLlmProvider);
|
||||
if (savedModelSlug !== undefined) setModelSlugState(savedModelSlug);
|
||||
if (savedApiKey !== undefined) setApiKeyState(savedApiKey);
|
||||
if (savedDefaultShell !== undefined) setDefaultShellState(savedDefaultShell);
|
||||
if (savedCustomShellPath !== undefined) setCustomShellPathState(savedCustomShellPath);
|
||||
if (savedShellArgs !== undefined) setShellArgsState(savedShellArgs);
|
||||
if (savedShellEnvVars !== undefined) setShellEnvVarsState(savedShellEnvVars);
|
||||
if (savedGhPath !== undefined) setGhPathState(savedGhPath);
|
||||
if (savedFontSize !== undefined) setFontSizeState(savedFontSize);
|
||||
if (savedFontFamily !== undefined) setFontFamilyState(savedFontFamily);
|
||||
if (savedLeftSidebarWidth !== undefined) setLeftSidebarWidthState(Math.max(256, Math.min(600, savedLeftSidebarWidth)));
|
||||
if (savedRightPanelWidth !== undefined) setRightPanelWidthState(savedRightPanelWidth);
|
||||
if (savedMarkdownEditMode !== undefined) setMarkdownEditModeState(savedMarkdownEditMode);
|
||||
if (savedShowHiddenFiles !== undefined) setShowHiddenFilesState(savedShowHiddenFiles);
|
||||
if (savedActiveThemeId !== undefined) setActiveThemeIdState(savedActiveThemeId);
|
||||
if (savedCustomThemeColors !== undefined) setCustomThemeColorsState(savedCustomThemeColors);
|
||||
if (savedCustomThemeBaseId !== undefined) setCustomThemeBaseIdState(savedCustomThemeBaseId);
|
||||
if (savedTerminalWidth !== undefined) setTerminalWidthState(savedTerminalWidth);
|
||||
if (savedLlmProvider !== undefined) setLlmProviderState(savedLlmProvider as LLMProvider);
|
||||
if (savedModelSlug !== undefined) setModelSlugState(savedModelSlug as string);
|
||||
if (savedApiKey !== undefined) setApiKeyState(savedApiKey as string);
|
||||
if (savedDefaultShell !== undefined) setDefaultShellState(savedDefaultShell as string);
|
||||
if (savedCustomShellPath !== undefined) setCustomShellPathState(savedCustomShellPath as string);
|
||||
if (savedShellArgs !== undefined) setShellArgsState(savedShellArgs as string);
|
||||
if (savedShellEnvVars !== undefined) setShellEnvVarsState(savedShellEnvVars as Record<string, string>);
|
||||
if (savedGhPath !== undefined) setGhPathState(savedGhPath as string);
|
||||
if (savedFontSize !== undefined) setFontSizeState(savedFontSize as number);
|
||||
if (savedFontFamily !== undefined) setFontFamilyState(savedFontFamily as string);
|
||||
if (savedLeftSidebarWidth !== undefined) setLeftSidebarWidthState(Math.max(256, Math.min(600, savedLeftSidebarWidth as number)));
|
||||
if (savedRightPanelWidth !== undefined) setRightPanelWidthState(savedRightPanelWidth as number);
|
||||
if (savedMarkdownEditMode !== undefined) setMarkdownEditModeState(savedMarkdownEditMode as boolean);
|
||||
if (savedShowHiddenFiles !== undefined) setShowHiddenFilesState(savedShowHiddenFiles as boolean);
|
||||
if (savedActiveThemeId !== undefined) setActiveThemeIdState(savedActiveThemeId as ThemeId);
|
||||
if (savedCustomThemeColors !== undefined) setCustomThemeColorsState(savedCustomThemeColors as ThemeColors);
|
||||
if (savedCustomThemeBaseId !== undefined) setCustomThemeBaseIdState(savedCustomThemeBaseId as ThemeId);
|
||||
if (savedTerminalWidth !== undefined) setTerminalWidthState(savedTerminalWidth as number);
|
||||
if (savedLogLevel !== undefined) setLogLevelState(savedLogLevel);
|
||||
if (savedMaxLogBuffer !== undefined) setMaxLogBufferState(savedMaxLogBuffer);
|
||||
if (savedMaxOutputLines !== undefined) setMaxOutputLinesState(savedMaxOutputLines);
|
||||
if (savedOsNotificationsEnabled !== undefined) setOsNotificationsEnabledState(savedOsNotificationsEnabled);
|
||||
if (savedAudioFeedbackEnabled !== undefined) setAudioFeedbackEnabledState(savedAudioFeedbackEnabled);
|
||||
if (savedAudioFeedbackCommand !== undefined) setAudioFeedbackCommandState(savedAudioFeedbackCommand);
|
||||
if (savedToastDuration !== undefined) setToastDurationState(savedToastDuration);
|
||||
if (savedCheckForUpdatesOnStartup !== undefined) setCheckForUpdatesOnStartupState(savedCheckForUpdatesOnStartup);
|
||||
if (savedCrashReportingEnabled !== undefined) setCrashReportingEnabledState(savedCrashReportingEnabled);
|
||||
if (savedLogViewerSelectedLevels !== undefined) setLogViewerSelectedLevelsState(savedLogViewerSelectedLevels);
|
||||
if (savedMaxOutputLines !== undefined) setMaxOutputLinesState(savedMaxOutputLines as number);
|
||||
if (savedOsNotificationsEnabled !== undefined) setOsNotificationsEnabledState(savedOsNotificationsEnabled as boolean);
|
||||
if (savedAudioFeedbackEnabled !== undefined) setAudioFeedbackEnabledState(savedAudioFeedbackEnabled as boolean);
|
||||
if (savedAudioFeedbackCommand !== undefined) setAudioFeedbackCommandState(savedAudioFeedbackCommand as string);
|
||||
if (savedToastDuration !== undefined) setToastDurationState(savedToastDuration as number);
|
||||
if (savedCheckForUpdatesOnStartup !== undefined) setCheckForUpdatesOnStartupState(savedCheckForUpdatesOnStartup as boolean);
|
||||
if (savedCrashReportingEnabled !== undefined) setCrashReportingEnabledState(savedCrashReportingEnabled as boolean);
|
||||
if (savedLogViewerSelectedLevels !== undefined) setLogViewerSelectedLevelsState(savedLogViewerSelectedLevels as string[]);
|
||||
|
||||
// Merge saved shortcuts with defaults (in case new shortcuts were added)
|
||||
if (savedShortcuts !== undefined) {
|
||||
@@ -1000,11 +1000,11 @@ export function useSettings(): UseSettingsReturn {
|
||||
}
|
||||
|
||||
// Merge saved AI commands with defaults (ensure built-in commands always exist)
|
||||
if (savedCustomAICommands !== undefined) {
|
||||
if (savedCustomAICommands !== undefined && Array.isArray(savedCustomAICommands)) {
|
||||
// Start with defaults, then merge saved commands (by ID to avoid duplicates)
|
||||
const commandsById = new Map<string, CustomAICommand>();
|
||||
DEFAULT_AI_COMMANDS.forEach(cmd => commandsById.set(cmd.id, cmd));
|
||||
savedCustomAICommands.forEach((cmd: CustomAICommand) => {
|
||||
(savedCustomAICommands as CustomAICommand[]).forEach((cmd: CustomAICommand) => {
|
||||
// Migration: Skip old /synopsis command - it was renamed to /history which is now
|
||||
// a built-in command handled by Maestro directly (not a custom AI command)
|
||||
if (cmd.command === '/synopsis' || cmd.id === 'synopsis') {
|
||||
@@ -1023,24 +1023,24 @@ export function useSettings(): UseSettingsReturn {
|
||||
|
||||
// Load global stats
|
||||
if (savedGlobalStats !== undefined) {
|
||||
setGlobalStatsState({ ...DEFAULT_GLOBAL_STATS, ...savedGlobalStats });
|
||||
setGlobalStatsState({ ...DEFAULT_GLOBAL_STATS, ...(savedGlobalStats as Partial<GlobalStats>) });
|
||||
}
|
||||
|
||||
// Load auto-run stats
|
||||
if (savedAutoRunStats !== undefined) {
|
||||
setAutoRunStatsState({ ...DEFAULT_AUTO_RUN_STATS, ...savedAutoRunStats });
|
||||
setAutoRunStatsState({ ...DEFAULT_AUTO_RUN_STATS, ...(savedAutoRunStats as Partial<AutoRunStats>) });
|
||||
}
|
||||
|
||||
// Load onboarding settings
|
||||
// UI collapse states
|
||||
if (savedUngroupedCollapsed !== undefined) setUngroupedCollapsedState(savedUngroupedCollapsed);
|
||||
if (savedUngroupedCollapsed !== undefined) setUngroupedCollapsedState(savedUngroupedCollapsed as boolean);
|
||||
|
||||
if (savedTourCompleted !== undefined) setTourCompletedState(savedTourCompleted);
|
||||
if (savedFirstAutoRunCompleted !== undefined) setFirstAutoRunCompletedState(savedFirstAutoRunCompleted);
|
||||
if (savedTourCompleted !== undefined) setTourCompletedState(savedTourCompleted as boolean);
|
||||
if (savedFirstAutoRunCompleted !== undefined) setFirstAutoRunCompletedState(savedFirstAutoRunCompleted as boolean);
|
||||
|
||||
// Load onboarding stats
|
||||
if (savedOnboardingStats !== undefined) {
|
||||
setOnboardingStatsState({ ...DEFAULT_ONBOARDING_STATS, ...savedOnboardingStats });
|
||||
setOnboardingStatsState({ ...DEFAULT_ONBOARDING_STATS, ...(savedOnboardingStats as Partial<OnboardingStats>) });
|
||||
}
|
||||
|
||||
// Load leaderboard registration
|
||||
@@ -1049,8 +1049,8 @@ export function useSettings(): UseSettingsReturn {
|
||||
}
|
||||
|
||||
// Load web interface settings
|
||||
if (savedWebInterfaceUseCustomPort !== undefined) setWebInterfaceUseCustomPortState(savedWebInterfaceUseCustomPort);
|
||||
if (savedWebInterfaceCustomPort !== undefined) setWebInterfaceCustomPortState(savedWebInterfaceCustomPort);
|
||||
if (savedWebInterfaceUseCustomPort !== undefined) setWebInterfaceUseCustomPortState(savedWebInterfaceUseCustomPort as boolean);
|
||||
if (savedWebInterfaceCustomPort !== undefined) setWebInterfaceCustomPortState(savedWebInterfaceCustomPort as number);
|
||||
|
||||
// Mark settings as loaded
|
||||
setSettingsLoaded(true);
|
||||
|
||||
@@ -246,7 +246,7 @@ export function useTemplateAutocomplete({
|
||||
|
||||
// Close on click outside (uses multiple refs to exclude both dropdown and textarea)
|
||||
useClickOutside(
|
||||
[autocompleteRef, textareaRef],
|
||||
[autocompleteRef, textareaRef] as React.RefObject<HTMLElement | null>[],
|
||||
closeAutocomplete,
|
||||
autocompleteState.isOpen
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user