refactor: move nested debugmessage and slashcommand hooks outside of useGeminiStream (#341)
This commit is contained in:
parent
c4c11f1d65
commit
d3303fd3a0
|
@ -10,6 +10,7 @@ import { StreamingState, type HistoryItem } from './types.js';
|
||||||
import { useGeminiStream } from './hooks/useGeminiStream.js';
|
import { useGeminiStream } from './hooks/useGeminiStream.js';
|
||||||
import { useLoadingIndicator } from './hooks/useLoadingIndicator.js';
|
import { useLoadingIndicator } from './hooks/useLoadingIndicator.js';
|
||||||
import { useThemeCommand } from './hooks/useThemeCommand.js';
|
import { useThemeCommand } from './hooks/useThemeCommand.js';
|
||||||
|
import { useSlashCommandProcessor } from './hooks/slashCommandProcessor.js';
|
||||||
import { Header } from './components/Header.js';
|
import { Header } from './components/Header.js';
|
||||||
import { LoadingIndicator } from './components/LoadingIndicator.js';
|
import { LoadingIndicator } from './components/LoadingIndicator.js';
|
||||||
import { EditorState, InputPrompt } from './components/InputPrompt.js';
|
import { EditorState, InputPrompt } from './components/InputPrompt.js';
|
||||||
|
@ -36,36 +37,40 @@ interface AppProps {
|
||||||
|
|
||||||
export const App = ({ config, settings, cliVersion }: AppProps) => {
|
export const App = ({ config, settings, cliVersion }: AppProps) => {
|
||||||
const { history, addItem, clearItems } = useHistory();
|
const { history, addItem, clearItems } = useHistory();
|
||||||
|
const [staticKey, setStaticKey] = useState(0);
|
||||||
|
const refreshStatic = useCallback(() => {
|
||||||
|
setStaticKey((prev) => prev + 1);
|
||||||
|
}, [setStaticKey]);
|
||||||
|
|
||||||
const [startupWarnings, setStartupWarnings] = useState<string[]>([]);
|
const [startupWarnings, setStartupWarnings] = useState<string[]>([]);
|
||||||
|
const [debugMessage, setDebugMessage] = useState<string>('');
|
||||||
const [showHelp, setShowHelp] = useState<boolean>(false);
|
const [showHelp, setShowHelp] = useState<boolean>(false);
|
||||||
const [themeError, setThemeError] = useState<string | null>(null);
|
const [themeError, setThemeError] = useState<string | null>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isThemeDialogOpen,
|
isThemeDialogOpen,
|
||||||
openThemeDialog,
|
openThemeDialog,
|
||||||
handleThemeSelect,
|
handleThemeSelect,
|
||||||
handleThemeHighlight,
|
handleThemeHighlight,
|
||||||
} = useThemeCommand(settings, setThemeError);
|
} = useThemeCommand(settings, setThemeError);
|
||||||
|
const { handleSlashCommand, slashCommands } = useSlashCommandProcessor(
|
||||||
const [staticKey, setStaticKey] = useState(0);
|
|
||||||
const refreshStatic = useCallback(() => {
|
|
||||||
setStaticKey((prev) => prev + 1);
|
|
||||||
}, [setStaticKey]);
|
|
||||||
|
|
||||||
const {
|
|
||||||
streamingState,
|
|
||||||
submitQuery,
|
|
||||||
initError,
|
|
||||||
debugMessage,
|
|
||||||
slashCommands,
|
|
||||||
pendingHistoryItem,
|
|
||||||
} = useGeminiStream(
|
|
||||||
addItem,
|
addItem,
|
||||||
clearItems,
|
clearItems,
|
||||||
refreshStatic,
|
refreshStatic,
|
||||||
setShowHelp,
|
setShowHelp,
|
||||||
config,
|
setDebugMessage,
|
||||||
openThemeDialog,
|
openThemeDialog,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { streamingState, submitQuery, initError, pendingHistoryItem } =
|
||||||
|
useGeminiStream(
|
||||||
|
addItem,
|
||||||
|
refreshStatic,
|
||||||
|
setShowHelp,
|
||||||
|
config,
|
||||||
|
setDebugMessage,
|
||||||
|
handleSlashCommand,
|
||||||
|
);
|
||||||
const { elapsedTime, currentLoadingPhrase } =
|
const { elapsedTime, currentLoadingPhrase } =
|
||||||
useLoadingIndicator(streamingState);
|
useLoadingIndicator(streamingState);
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ interface HandleAtCommandParams {
|
||||||
query: string;
|
query: string;
|
||||||
config: Config;
|
config: Config;
|
||||||
addItem: UseHistoryManagerReturn['addItem'];
|
addItem: UseHistoryManagerReturn['addItem'];
|
||||||
setDebugMessage: React.Dispatch<React.SetStateAction<string>>;
|
onDebugMessage: (message: string) => void;
|
||||||
messageId: number;
|
messageId: number;
|
||||||
signal: AbortSignal;
|
signal: AbortSignal;
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ export async function handleAtCommand({
|
||||||
query,
|
query,
|
||||||
config,
|
config,
|
||||||
addItem,
|
addItem,
|
||||||
setDebugMessage,
|
onDebugMessage,
|
||||||
messageId: userMessageTimestamp,
|
messageId: userMessageTimestamp,
|
||||||
signal,
|
signal,
|
||||||
}: HandleAtCommandParams): Promise<HandleAtCommandResult> {
|
}: HandleAtCommandParams): Promise<HandleAtCommandResult> {
|
||||||
|
@ -109,7 +109,7 @@ export async function handleAtCommand({
|
||||||
|
|
||||||
// If the atPath is just "@", pass the original query to the LLM
|
// If the atPath is just "@", pass the original query to the LLM
|
||||||
if (atPath === '@') {
|
if (atPath === '@') {
|
||||||
setDebugMessage('Lone @ detected, passing directly to LLM.');
|
onDebugMessage('Lone @ detected, passing directly to LLM.');
|
||||||
return { processedQuery: [{ text: query }], shouldProceed: true };
|
return { processedQuery: [{ text: query }], shouldProceed: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,18 +144,18 @@ export async function handleAtCommand({
|
||||||
const stats = await fs.stat(absolutePath);
|
const stats = await fs.stat(absolutePath);
|
||||||
if (stats.isDirectory()) {
|
if (stats.isDirectory()) {
|
||||||
pathSpec = pathPart.endsWith('/') ? `${pathPart}**` : `${pathPart}/**`;
|
pathSpec = pathPart.endsWith('/') ? `${pathPart}**` : `${pathPart}/**`;
|
||||||
setDebugMessage(`Path resolved to directory, using glob: ${pathSpec}`);
|
onDebugMessage(`Path resolved to directory, using glob: ${pathSpec}`);
|
||||||
} else {
|
} else {
|
||||||
setDebugMessage(`Path resolved to file: ${pathSpec}`);
|
onDebugMessage(`Path resolved to file: ${pathSpec}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If stat fails (e.g., not found), proceed with original path.
|
// If stat fails (e.g., not found), proceed with original path.
|
||||||
// The tool itself will handle the error during execution.
|
// The tool itself will handle the error during execution.
|
||||||
if (isNodeError(error) && error.code === 'ENOENT') {
|
if (isNodeError(error) && error.code === 'ENOENT') {
|
||||||
setDebugMessage(`Path not found, proceeding with original: ${pathSpec}`);
|
onDebugMessage(`Path not found, proceeding with original: ${pathSpec}`);
|
||||||
} else {
|
} else {
|
||||||
console.error(`Error stating path ${pathPart}:`, error);
|
console.error(`Error stating path ${pathPart}:`, error);
|
||||||
setDebugMessage(
|
onDebugMessage(
|
||||||
`Error stating path, proceeding with original: ${pathSpec}`,
|
`Error stating path, proceeding with original: ${pathSpec}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||||
export const useShellCommandProcessor = (
|
export const useShellCommandProcessor = (
|
||||||
addItemToHistory: UseHistoryManagerReturn['addItem'],
|
addItemToHistory: UseHistoryManagerReturn['addItem'],
|
||||||
setStreamingState: React.Dispatch<React.SetStateAction<StreamingState>>,
|
setStreamingState: React.Dispatch<React.SetStateAction<StreamingState>>,
|
||||||
setDebugMessage: React.Dispatch<React.SetStateAction<string>>,
|
onDebugMessage: (message: string) => void,
|
||||||
config: Config,
|
config: Config,
|
||||||
) => {
|
) => {
|
||||||
/**
|
/**
|
||||||
|
@ -50,7 +50,7 @@ export const useShellCommandProcessor = (
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetDir = config.getTargetDir();
|
const targetDir = config.getTargetDir();
|
||||||
setDebugMessage(
|
onDebugMessage(
|
||||||
`Executing shell command in ${targetDir}: ${commandToExecute}`,
|
`Executing shell command in ${targetDir}: ${commandToExecute}`,
|
||||||
);
|
);
|
||||||
const execOptions = {
|
const execOptions = {
|
||||||
|
@ -80,7 +80,7 @@ export const useShellCommandProcessor = (
|
||||||
|
|
||||||
return true; // Command was initiated
|
return true; // Command was initiated
|
||||||
},
|
},
|
||||||
[config, setDebugMessage, addItemToHistory, setStreamingState],
|
[config, onDebugMessage, addItemToHistory, setStreamingState],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { handleShellCommand };
|
return { handleShellCommand };
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const useSlashCommandProcessor = (
|
||||||
clearItems: UseHistoryManagerReturn['clearItems'],
|
clearItems: UseHistoryManagerReturn['clearItems'],
|
||||||
refreshStatic: () => void,
|
refreshStatic: () => void,
|
||||||
setShowHelp: React.Dispatch<React.SetStateAction<boolean>>,
|
setShowHelp: React.Dispatch<React.SetStateAction<boolean>>,
|
||||||
setDebugMessage: React.Dispatch<React.SetStateAction<string>>,
|
onDebugMessage: (message: string) => void,
|
||||||
openThemeDialog: () => void,
|
openThemeDialog: () => void,
|
||||||
) => {
|
) => {
|
||||||
const slashCommands: SlashCommand[] = useMemo(
|
const slashCommands: SlashCommand[] = useMemo(
|
||||||
|
@ -34,7 +34,7 @@ export const useSlashCommandProcessor = (
|
||||||
altName: '?',
|
altName: '?',
|
||||||
description: 'for help on gemini-code',
|
description: 'for help on gemini-code',
|
||||||
action: (_value: PartListUnion) => {
|
action: (_value: PartListUnion) => {
|
||||||
setDebugMessage('Opening help.');
|
onDebugMessage('Opening help.');
|
||||||
setShowHelp(true);
|
setShowHelp(true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -42,7 +42,7 @@ export const useSlashCommandProcessor = (
|
||||||
name: 'clear',
|
name: 'clear',
|
||||||
description: 'clear the screen',
|
description: 'clear the screen',
|
||||||
action: (_value: PartListUnion) => {
|
action: (_value: PartListUnion) => {
|
||||||
setDebugMessage('Clearing terminal.');
|
onDebugMessage('Clearing terminal.');
|
||||||
clearItems();
|
clearItems();
|
||||||
refreshStatic();
|
refreshStatic();
|
||||||
},
|
},
|
||||||
|
@ -59,12 +59,12 @@ export const useSlashCommandProcessor = (
|
||||||
altName: 'exit',
|
altName: 'exit',
|
||||||
description: '',
|
description: '',
|
||||||
action: (_value: PartListUnion) => {
|
action: (_value: PartListUnion) => {
|
||||||
setDebugMessage('Quitting. Good-bye.');
|
onDebugMessage('Quitting. Good-bye.');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[setDebugMessage, setShowHelp, refreshStatic, openThemeDialog, clearItems],
|
[onDebugMessage, setShowHelp, refreshStatic, openThemeDialog, clearItems],
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,7 +28,6 @@ import {
|
||||||
HistoryItemWithoutId,
|
HistoryItemWithoutId,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { isAtCommand } from '../utils/commandUtils.js';
|
import { isAtCommand } from '../utils/commandUtils.js';
|
||||||
import { useSlashCommandProcessor } from './slashCommandProcessor.js';
|
|
||||||
import { useShellCommandProcessor } from './shellCommandProcessor.js';
|
import { useShellCommandProcessor } from './shellCommandProcessor.js';
|
||||||
import { handleAtCommand } from './atCommandProcessor.js';
|
import { handleAtCommand } from './atCommandProcessor.js';
|
||||||
import { findLastSafeSplitPoint } from '../utils/markdownUtilities.js';
|
import { findLastSafeSplitPoint } from '../utils/markdownUtilities.js';
|
||||||
|
@ -41,17 +40,16 @@ import { UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||||
*/
|
*/
|
||||||
export const useGeminiStream = (
|
export const useGeminiStream = (
|
||||||
addItem: UseHistoryManagerReturn['addItem'],
|
addItem: UseHistoryManagerReturn['addItem'],
|
||||||
clearItems: UseHistoryManagerReturn['clearItems'],
|
|
||||||
refreshStatic: () => void,
|
refreshStatic: () => void,
|
||||||
setShowHelp: React.Dispatch<React.SetStateAction<boolean>>,
|
setShowHelp: React.Dispatch<React.SetStateAction<boolean>>,
|
||||||
config: Config,
|
config: Config,
|
||||||
openThemeDialog: () => void,
|
onDebugMessage: (message: string) => void,
|
||||||
|
handleSlashCommand: (cmd: PartListUnion) => boolean,
|
||||||
) => {
|
) => {
|
||||||
const toolRegistry = config.getToolRegistry();
|
const toolRegistry = config.getToolRegistry();
|
||||||
const [streamingState, setStreamingState] = useState<StreamingState>(
|
const [streamingState, setStreamingState] = useState<StreamingState>(
|
||||||
StreamingState.Idle,
|
StreamingState.Idle,
|
||||||
);
|
);
|
||||||
const [debugMessage, setDebugMessage] = useState<string>('');
|
|
||||||
const [initError, setInitError] = useState<string | null>(null);
|
const [initError, setInitError] = useState<string | null>(null);
|
||||||
const abortControllerRef = useRef<AbortController | null>(null);
|
const abortControllerRef = useRef<AbortController | null>(null);
|
||||||
const chatSessionRef = useRef<Chat | null>(null);
|
const chatSessionRef = useRef<Chat | null>(null);
|
||||||
|
@ -59,19 +57,10 @@ export const useGeminiStream = (
|
||||||
const [pendingHistoryItemRef, setPendingHistoryItem] =
|
const [pendingHistoryItemRef, setPendingHistoryItem] =
|
||||||
useStateAndRef<HistoryItemWithoutId | null>(null);
|
useStateAndRef<HistoryItemWithoutId | null>(null);
|
||||||
|
|
||||||
const { handleSlashCommand, slashCommands } = useSlashCommandProcessor(
|
|
||||||
addItem,
|
|
||||||
clearItems,
|
|
||||||
refreshStatic,
|
|
||||||
setShowHelp,
|
|
||||||
setDebugMessage,
|
|
||||||
openThemeDialog,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { handleShellCommand } = useShellCommandProcessor(
|
const { handleShellCommand } = useShellCommandProcessor(
|
||||||
addItem,
|
addItem,
|
||||||
setStreamingState,
|
setStreamingState,
|
||||||
setDebugMessage,
|
onDebugMessage,
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -109,7 +98,7 @@ export const useGeminiStream = (
|
||||||
|
|
||||||
if (typeof query === 'string') {
|
if (typeof query === 'string') {
|
||||||
const trimmedQuery = query.trim();
|
const trimmedQuery = query.trim();
|
||||||
setDebugMessage(`User query: '${trimmedQuery}'`);
|
onDebugMessage(`User query: '${trimmedQuery}'`);
|
||||||
|
|
||||||
// Handle UI-only commands first
|
// Handle UI-only commands first
|
||||||
if (handleSlashCommand(trimmedQuery)) return;
|
if (handleSlashCommand(trimmedQuery)) return;
|
||||||
|
@ -121,7 +110,7 @@ export const useGeminiStream = (
|
||||||
query: trimmedQuery,
|
query: trimmedQuery,
|
||||||
config,
|
config,
|
||||||
addItem,
|
addItem,
|
||||||
setDebugMessage,
|
onDebugMessage,
|
||||||
messageId: userMessageTimestamp,
|
messageId: userMessageTimestamp,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
|
@ -138,7 +127,7 @@ export const useGeminiStream = (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryToSendToGemini === null) {
|
if (queryToSendToGemini === null) {
|
||||||
setDebugMessage(
|
onDebugMessage(
|
||||||
'Query processing resulted in null, not sending to Gemini.',
|
'Query processing resulted in null, not sending to Gemini.',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -558,6 +547,7 @@ export const useGeminiStream = (
|
||||||
setPendingHistoryItem,
|
setPendingHistoryItem,
|
||||||
toolRegistry,
|
toolRegistry,
|
||||||
refreshStatic,
|
refreshStatic,
|
||||||
|
onDebugMessage,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -565,8 +555,6 @@ export const useGeminiStream = (
|
||||||
streamingState,
|
streamingState,
|
||||||
submitQuery,
|
submitQuery,
|
||||||
initError,
|
initError,
|
||||||
debugMessage,
|
|
||||||
slashCommands,
|
|
||||||
// Normally we would be concerned that the ref would not be up-to-date, but
|
// Normally we would be concerned that the ref would not be up-to-date, but
|
||||||
// this isn't a concern as the ref is updated whenever the corresponding
|
// this isn't a concern as the ref is updated whenever the corresponding
|
||||||
// state is updated.
|
// state is updated.
|
||||||
|
|
Loading…
Reference in New Issue