UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

209 lines 10.5 kB
import { jsx as _jsx } from "react/jsx-runtime"; import React from 'react'; import { createClearMessagesHandler, handleMessageSubmission, } from '../app/utils/app-util.js'; import { ErrorMessage, SuccessMessage, WarningMessage, } from '../components/message-box.js'; import Status from '../components/status.js'; import { getAppConfig } from '../config/index.js'; import { setCurrentMode as setCurrentModeContext } from '../context/mode-context.js'; import { getModelContextLimit } from '../models/index.js'; import { CheckpointManager } from '../services/checkpoint-manager.js'; import { createTokenizer } from '../tokenization/index.js'; import { calculateTokenBreakdown } from '../usage/calculator.js'; import { autoCompactSessionOverrides } from '../utils/auto-compact.js'; import { getLogger } from '../utils/logging/index.js'; import { addToMessageQueue } from '../utils/message-queue.js'; import { processPromptTemplate } from '../utils/prompt-processor.js'; /** * Consolidates all app handler setup into a single hook */ export function useAppHandlers(props) { const logger = getLogger(); // Clear messages handler const clearMessages = React.useMemo(() => createClearMessagesHandler(props.updateMessages, props.client), [props.updateMessages, props.client]); // Cancel handler const handleCancel = React.useCallback(() => { if (props.abortController) { logger.info('Cancelling current operation', { operation: 'user_cancellation', hasAbortController: !!props.abortController, }); props.setIsCancelling(true); props.abortController.abort(); } else { logger.debug('Cancel requested but no active operation to cancel'); } }, [props.abortController, props.setIsCancelling, logger, props]); // Toggle development mode handler const handleToggleDevelopmentMode = React.useCallback(() => { props.setDevelopmentMode(currentMode => { // Don't allow toggling out of scheduler mode via Shift+Tab if (currentMode === 'scheduler') return currentMode; const modes = [ 'normal', 'auto-accept', 'plan', ]; const currentIndex = modes.indexOf(currentMode); const nextIndex = (currentIndex + 1) % modes.length; const nextMode = modes[nextIndex]; logger.info('Development mode toggled', { previousMode: currentMode, nextMode, modeIndex: nextIndex, totalModes: modes.length, }); // Sync global mode context for tool needsApproval logic setCurrentModeContext(nextMode); return nextMode; }); }, [props.setDevelopmentMode, logger, props]); // Show status handler const handleShowStatus = React.useCallback(async () => { logger.debug('Status display requested', { currentProvider: props.currentProvider, currentModel: props.currentModel, currentTheme: props.currentTheme, }); // Calculate context usage and auto-compact info let contextUsage; let autoCompactInfo; try { // Calculate context usage const contextLimit = await getModelContextLimit(props.currentModel); if (contextLimit && props.messages.length > 0) { const tokenizer = createTokenizer(props.currentProvider, props.currentModel); try { const systemPrompt = processPromptTemplate(); const systemMessage = { role: 'system', content: systemPrompt, }; const breakdown = calculateTokenBreakdown([systemMessage, ...props.messages], tokenizer, props.getMessageTokens); const percentUsed = (breakdown.total / contextLimit) * 100; contextUsage = { currentTokens: breakdown.total, contextLimit, percentUsed, }; } finally { if (tokenizer.free) { tokenizer.free(); } } } // Get auto-compact info const config = getAppConfig(); const autoCompactConfig = config.autoCompact; if (autoCompactConfig) { const enabled = autoCompactSessionOverrides.enabled !== null ? autoCompactSessionOverrides.enabled : autoCompactConfig.enabled; const threshold = autoCompactSessionOverrides.threshold !== null ? autoCompactSessionOverrides.threshold : autoCompactConfig.threshold; const mode = autoCompactSessionOverrides.mode !== null ? autoCompactSessionOverrides.mode : autoCompactConfig.mode; const hasOverrides = autoCompactSessionOverrides.enabled !== null || autoCompactSessionOverrides.threshold !== null || autoCompactSessionOverrides.mode !== null; autoCompactInfo = { enabled, threshold, mode, hasOverrides, }; } } catch (error) { logger.debug('Failed to calculate status info', { error }); // Continue without context usage/auto-compact info } props.addToChatQueue(_jsx(Status, { provider: props.currentProvider, model: props.currentModel, theme: props.currentTheme, updateInfo: props.updateInfo, mcpServersStatus: props.mcpServersStatus, lspServersStatus: props.lspServersStatus, preferencesLoaded: props.preferencesLoaded, customCommandsCount: props.customCommandsCount, contextUsage: contextUsage, autoCompactInfo: autoCompactInfo }, `status-${props.getNextComponentKey()}`)); }, [props, logger]); // Checkpoint select handler const handleCheckpointSelect = React.useCallback(async (checkpointName, createBackup) => { try { const manager = new CheckpointManager(); if (createBackup) { try { await manager.saveCheckpoint(`backup-${new Date().toISOString().replace(/[:.]/g, '-')}`, props.messages, props.currentProvider, props.currentModel); } catch (error) { addToMessageQueue(_jsx(WarningMessage, { message: `Warning: Failed to create backup: ${error instanceof Error ? error.message : 'Unknown error'}`, hideBox: true }, `backup-warning-${Date.now()}`)); } } const checkpointData = await manager.loadCheckpoint(checkpointName, { validateIntegrity: true, }); await manager.restoreFiles(checkpointData); addToMessageQueue(_jsx(SuccessMessage, { message: `✓ Checkpoint '${checkpointName}' restored successfully`, hideBox: true }, `restore-success-${Date.now()}`)); } catch (error) { addToMessageQueue(_jsx(ErrorMessage, { message: `Failed to restore checkpoint: ${error instanceof Error ? error.message : 'Unknown error'}`, hideBox: true }, `restore-error-${Date.now()}`)); } finally { props.setIsCheckpointLoadMode(false); props.setCheckpointLoadData(null); } }, [props]); // Checkpoint cancel handler const handleCheckpointCancel = React.useCallback(() => { props.setIsCheckpointLoadMode(false); props.setCheckpointLoadData(null); }, [props.setIsCheckpointLoadMode, props.setCheckpointLoadData, props]); // Enter checkpoint load mode handler const enterCheckpointLoadMode = React.useCallback((checkpoints, currentMessageCount) => { props.setCheckpointLoadData({ checkpoints, currentMessageCount }); props.setIsCheckpointLoadMode(true); }, [props.setCheckpointLoadData, props.setIsCheckpointLoadMode, props]); // Message submit handler const handleMessageSubmit = React.useCallback(async (message) => { // Reset conversation completion flag when starting a new message props.setIsConversationComplete(false); await handleMessageSubmission(message, { customCommandCache: props.customCommandCache, customCommandLoader: props.customCommandLoader, customCommandExecutor: props.customCommandExecutor, onClearMessages: clearMessages, onEnterModelSelectionMode: props.enterModelSelectionMode, onEnterProviderSelectionMode: props.enterProviderSelectionMode, onEnterModelDatabaseMode: props.enterModelDatabaseMode, onEnterConfigWizardMode: props.enterConfigWizardMode, onEnterSettingsMode: props.enterSettingsMode, onEnterMcpWizardMode: props.enterMcpWizardMode, onEnterExplorerMode: props.enterExplorerMode, onEnterIdeSelectionMode: props.enterIdeSelectionMode, onEnterSchedulerMode: props.enterSchedulerMode, onEnterCheckpointLoadMode: enterCheckpointLoadMode, onShowStatus: handleShowStatus, onHandleChatMessage: props.handleChatMessage, onAddToChatQueue: props.addToChatQueue, setLiveComponent: props.setLiveComponent, setIsToolExecuting: props.setIsToolExecuting, onCommandComplete: () => props.setIsConversationComplete(true), getNextComponentKey: props.getNextComponentKey, setMessages: props.updateMessages, messages: props.messages, provider: props.currentProvider, model: props.currentModel, theme: props.currentTheme, updateInfo: props.updateInfo, getMessageTokens: props.getMessageTokens, }); }, [props, clearMessages, enterCheckpointLoadMode, handleShowStatus]); return { clearMessages, handleCancel, handleToggleDevelopmentMode, handleShowStatus, handleCheckpointSelect, handleCheckpointCancel, enterCheckpointLoadMode, handleMessageSubmit, }; } //# sourceMappingURL=useAppHandlers.js.map