Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,15 @@
"ink-gradient": "^3.0.0",
"ink-select-input": "^6.2.0",
"ink-spinner": "^5.0.0",
"ink-text-input": "^6.0.0",
"meow": "^11.0.0",
"nanostores": "^1.0.1",
"ollama": "^0.5.17",
"react": "^19.0.0"
},
"devDependencies": {
"@sindresorhus/tsconfig": "^3.0.1",
"@types/ink-text-input": "^2.0.5",
"@types/node": "^24.3.0",
"@types/react": "^19.0.0",
"@vdemedes/prettier-config": "^2.0.1",
Expand Down
169 changes: 89 additions & 80 deletions source/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ import {useModeHandlers} from './app/hooks/useModeHandlers.js';
import {useAppInitialization} from './app/hooks/useAppInitialization.js';
import {useDirectoryTrust} from './app/hooks/useDirectoryTrust.js';
import {
handleMessageSubmission,
createClearMessagesHandler,
handleMessageSubmission,
} from './app/utils/appUtils.js';

// Provide shared UI state to components
import {UIStateProvider} from './hooks/useUIState.js';

export default function App() {
// Use extracted hooks
const appState = useAppState();
Expand Down Expand Up @@ -233,88 +236,94 @@ export default function App() {

return (
<ThemeContext.Provider value={themeContextValue}>
<Box flexDirection="column" padding={1} width="100%">
<Box flexGrow={1} flexDirection="column" minHeight={0}>
<WelcomeMessage />
{appState.startChat && (
<ChatQueue
staticComponents={staticComponents}
queuedComponents={appState.chatComponents}
/>
)}
</Box>
{appState.startChat && (
<Box flexDirection="column">
{appState.isCancelling ? (
<CancellingIndicator />
) : appState.isThinking ? (
<ThinkingIndicator
contextSize={appState.thinkingStats.contextSize}
totalTokensUsed={appState.thinkingStats.totalTokensUsed}
tokensPerSecond={appState.thinkingStats.tokensPerSecond}
/>
) : null}
{appState.isModelSelectionMode ? (
<ModelSelector
client={appState.client}
currentModel={appState.currentModel}
onModelSelect={modeHandlers.handleModelSelect}
onCancel={modeHandlers.handleModelSelectionCancel}
/>
) : appState.isProviderSelectionMode ? (
<ProviderSelector
currentProvider={appState.currentProvider}
onProviderSelect={modeHandlers.handleProviderSelect}
onCancel={modeHandlers.handleProviderSelectionCancel}
<UIStateProvider>
<Box flexDirection="column" padding={1} width="100%">
<Box flexGrow={1} flexDirection="column" minHeight={0}>
<WelcomeMessage />
{appState.startChat && (
<ChatQueue
staticComponents={staticComponents}
queuedComponents={appState.chatComponents}
/>
) : appState.isThemeSelectionMode ? (
<ThemeSelector
onThemeSelect={modeHandlers.handleThemeSelect}
onCancel={modeHandlers.handleThemeSelectionCancel}
/>
) : appState.isToolConfirmationMode &&
appState.pendingToolCalls[appState.currentToolIndex] ? (
<ToolConfirmation
toolCall={appState.pendingToolCalls[appState.currentToolIndex]}
onConfirm={toolHandler.handleToolConfirmation}
onCancel={toolHandler.handleToolConfirmationCancel}
/>
) : appState.isToolExecuting &&
appState.pendingToolCalls[appState.currentToolIndex] ? (
<ToolExecutionIndicator
toolName={
appState.pendingToolCalls[appState.currentToolIndex].function
.name
}
currentIndex={appState.currentToolIndex}
totalTools={appState.pendingToolCalls.length}
/>
) : appState.isBashExecuting ? (
<BashExecutionIndicator command={appState.currentBashCommand} />
) : appState.mcpInitialized && appState.client ? (
<UserInput
customCommands={Array.from(appState.customCommandCache.keys())}
onSubmit={handleMessageSubmit}
disabled={
appState.isThinking ||
appState.isToolExecuting ||
appState.isBashExecuting
}
onCancel={handleCancel}
/>
) : appState.mcpInitialized && !appState.client ? (
<Text color={themeContextValue.colors.secondary}>
⚠️ No LLM provider available. Chat is disabled. Please fix your
provider configuration and restart.
</Text>
) : (
<Text color={themeContextValue.colors.secondary}>
<Spinner type="dots2" /> Loading...
</Text>
)}
</Box>
)}
</Box>
{appState.startChat && (
<Box flexDirection="column">
{appState.isCancelling ? (
<CancellingIndicator />
) : appState.isThinking ? (
<ThinkingIndicator
contextSize={appState.thinkingStats.contextSize}
totalTokensUsed={appState.thinkingStats.totalTokensUsed}
tokensPerSecond={appState.thinkingStats.tokensPerSecond}
/>
) : null}
{appState.isModelSelectionMode ? (
<ModelSelector
client={appState.client}
currentModel={appState.currentModel}
onModelSelect={modeHandlers.handleModelSelect}
onCancel={modeHandlers.handleModelSelectionCancel}
/>
) : appState.isProviderSelectionMode ? (
<ProviderSelector
currentProvider={appState.currentProvider}
onProviderSelect={modeHandlers.handleProviderSelect}
onCancel={modeHandlers.handleProviderSelectionCancel}
/>
) : appState.isThemeSelectionMode ? (
<ThemeSelector
onThemeSelect={modeHandlers.handleThemeSelect}
onCancel={modeHandlers.handleThemeSelectionCancel}
/>
) : appState.isToolConfirmationMode &&
appState.pendingToolCalls[appState.currentToolIndex] ? (
<ToolConfirmation
toolCall={
appState.pendingToolCalls[appState.currentToolIndex]
}
onConfirm={toolHandler.handleToolConfirmation}
onCancel={toolHandler.handleToolConfirmationCancel}
/>
) : appState.isToolExecuting &&
appState.pendingToolCalls[appState.currentToolIndex] ? (
<ToolExecutionIndicator
toolName={
appState.pendingToolCalls[appState.currentToolIndex]
.function.name
}
currentIndex={appState.currentToolIndex}
totalTools={appState.pendingToolCalls.length}
/>
) : appState.isBashExecuting ? (
<BashExecutionIndicator command={appState.currentBashCommand} />
) : appState.mcpInitialized && appState.client ? (
<UserInput
customCommands={Array.from(
appState.customCommandCache.keys(),
)}
onSubmit={handleMessageSubmit}
disabled={
appState.isThinking ||
appState.isToolExecuting ||
appState.isBashExecuting
}
onCancel={handleCancel}
/>
) : appState.mcpInitialized && !appState.client ? (
<Text color={themeContextValue.colors.secondary}>
⚠️ No LLM provider available. Chat is disabled. Please fix
your provider configuration and restart.
</Text>
) : (
<Text color={themeContextValue.colors.secondary}>
<Spinner type="dots2" /> Loading...
</Text>
)}
</Box>
)}
</Box>
</UIStateProvider>
</ThemeContext.Provider>
);
}
Loading