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:
Pedram Amini
2025-12-20 09:24:48 -06:00
parent e2347a52d1
commit 263eebf09c
23 changed files with 110 additions and 104 deletions

View File

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

View File

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

View File

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

View File

@@ -16,8 +16,8 @@ interface GitLogEntry {
date: string;
refs: string[];
subject: string;
additions: number;
deletions: number;
additions?: number;
deletions?: number;
}
interface GitLogViewerProps {

View File

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

View File

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

View File

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

View File

@@ -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 */}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 */}

View File

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

View File

@@ -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 */}

View File

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

View File

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

View File

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

View File

@@ -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) */

View File

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

View File

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

View File

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