@within-7/minto 0.3.6 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{cli.js → cli.cjs} +25 -23
- package/dist/commands/language.js +137 -0
- package/dist/commands/language.js.map +7 -0
- package/dist/commands/new.js +56 -0
- package/dist/commands/new.js.map +7 -0
- package/dist/commands/resume.js +251 -16
- package/dist/commands/resume.js.map +2 -2
- package/dist/commands/sessions.js +224 -0
- package/dist/commands/sessions.js.map +7 -0
- package/dist/commands/setup.js +3 -2
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/stats.js +235 -0
- package/dist/commands/stats.js.map +7 -0
- package/dist/commands/status.js +11 -5
- package/dist/commands/status.js.map +2 -2
- package/dist/commands/undo.js +26 -16
- package/dist/commands/undo.js.map +2 -2
- package/dist/commands.js +6 -0
- package/dist/commands.js.map +2 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
- package/dist/components/Config.js +9 -8
- package/dist/components/Config.js.map +2 -2
- package/dist/components/HeaderBar.js +2 -1
- package/dist/components/HeaderBar.js.map +2 -2
- package/dist/components/Help.js +2 -1
- package/dist/components/Help.js.map +2 -2
- package/dist/components/HotkeyHelpPanel.js +46 -44
- package/dist/components/HotkeyHelpPanel.js.map +2 -2
- package/dist/components/Logo.js +5 -2
- package/dist/components/Logo.js.map +2 -2
- package/dist/components/MCPServerApprovalDialog.js +6 -5
- package/dist/components/MCPServerApprovalDialog.js.map +2 -2
- package/dist/components/MCPServerMultiselectDialog.js +5 -4
- package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
- package/dist/components/MessageSelector.js +4 -3
- package/dist/components/MessageSelector.js.map +2 -2
- package/dist/components/ModelConfig.js +13 -12
- package/dist/components/ModelConfig.js.map +2 -2
- package/dist/components/ModelListManager.js +4 -3
- package/dist/components/ModelListManager.js.map +2 -2
- package/dist/components/PromptInput.js +72 -39
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/SensitiveFileWarning.js +12 -8
- package/dist/components/SensitiveFileWarning.js.map +2 -2
- package/dist/components/TabbedListView/ScrollableList.js +91 -0
- package/dist/components/TabbedListView/ScrollableList.js.map +7 -0
- package/dist/components/TabbedListView/SearchInput.js +23 -0
- package/dist/components/TabbedListView/SearchInput.js.map +7 -0
- package/dist/components/TabbedListView/TabBar.js +20 -0
- package/dist/components/TabbedListView/TabBar.js.map +7 -0
- package/dist/components/TabbedListView/TabbedListView.js +171 -0
- package/dist/components/TabbedListView/TabbedListView.js.map +7 -0
- package/dist/components/TabbedListView/index.js +11 -0
- package/dist/components/TabbedListView/index.js.map +7 -0
- package/dist/components/TabbedListView/types.js +1 -0
- package/dist/components/TabbedListView/types.js.map +7 -0
- package/dist/components/TodoChangeBlock.js +6 -5
- package/dist/components/TodoChangeBlock.js.map +3 -3
- package/dist/components/TodoPanel.js +6 -3
- package/dist/components/TodoPanel.js.map +3 -3
- package/dist/components/TrustDialog.js +6 -5
- package/dist/components/TrustDialog.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolCanceledMessage.js +2 -1
- package/dist/components/messages/UserToolResultMessage/UserToolCanceledMessage.js.map +2 -2
- package/dist/constants/macros.js +1 -1
- package/dist/constants/macros.js.map +1 -1
- package/dist/constants/product.js +2 -2
- package/dist/constants/product.js.map +1 -1
- package/dist/constants/prompts.js +17 -0
- package/dist/constants/prompts.js.map +2 -2
- package/dist/constants/toolInputExamples.js +5 -1
- package/dist/constants/toolInputExamples.js.map +2 -2
- package/dist/core/tokenStatsManager.js +5 -0
- package/dist/core/tokenStatsManager.js.map +2 -2
- package/dist/entrypoints/bootstrap.js +54 -0
- package/dist/entrypoints/bootstrap.js.map +7 -0
- package/dist/entrypoints/cli.js +132 -23
- package/dist/entrypoints/cli.js.map +3 -3
- package/dist/history.js +75 -15
- package/dist/history.js.map +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/index.js.map +2 -2
- package/dist/i18n/locales/en.js +283 -1
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +283 -1
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/messages.js +11 -0
- package/dist/messages.js.map +2 -2
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +9 -0
- package/dist/query.js.map +2 -2
- package/dist/screens/REPL.js +45 -7
- package/dist/screens/REPL.js.map +2 -2
- package/dist/services/customCommands.js +14 -8
- package/dist/services/customCommands.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +176 -1
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/tools/TodoWriteTool/prompt.js +21 -0
- package/dist/tools/TodoWriteTool/prompt.js.map +2 -2
- package/dist/tools/URLFetcherTool/prompt.js +14 -9
- package/dist/tools/URLFetcherTool/prompt.js.map +2 -2
- package/dist/tools/WebSearchTool/prompt.js +12 -6
- package/dist/tools/WebSearchTool/prompt.js.map +2 -2
- package/dist/types/PermissionMode.js +30 -1
- package/dist/types/PermissionMode.js.map +2 -2
- package/dist/types/plugin.js.map +2 -2
- package/dist/utils/agentHookExecutor.js +106 -0
- package/dist/utils/agentHookExecutor.js.map +7 -0
- package/dist/utils/agentLoader.js +212 -26
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/agentMemory.js +134 -0
- package/dist/utils/agentMemory.js.map +7 -0
- package/dist/utils/config.js +51 -1
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/configPaths.js +199 -0
- package/dist/utils/configPaths.js.map +7 -0
- package/dist/utils/historyManager.js +234 -0
- package/dist/utils/historyManager.js.map +7 -0
- package/dist/utils/messages.js +13 -8
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/migration/index.js +37 -0
- package/dist/utils/migration/index.js.map +7 -0
- package/dist/utils/migration/migrateHistory.js +273 -0
- package/dist/utils/migration/migrateHistory.js.map +7 -0
- package/dist/utils/migration/migrateTodos.js +323 -0
- package/dist/utils/migration/migrateTodos.js.map +7 -0
- package/dist/utils/pasteCache.js +309 -0
- package/dist/utils/pasteCache.js.map +7 -0
- package/dist/utils/pluginLoader.js +6 -3
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/sessionIndex.js +192 -0
- package/dist/utils/sessionIndex.js.map +7 -0
- package/dist/utils/sessionTracker.js +170 -0
- package/dist/utils/sessionTracker.js.map +7 -0
- package/dist/utils/skillLoader.js +91 -5
- package/dist/utils/skillLoader.js.map +2 -2
- package/dist/utils/stats.js +417 -0
- package/dist/utils/stats.js.map +7 -0
- package/dist/utils/stringSubstitution.js +107 -0
- package/dist/utils/stringSubstitution.js.map +7 -0
- package/dist/utils/teamConfig.js +3 -1
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/todoStorage.js +51 -19
- package/dist/utils/todoStorage.js.map +2 -2
- package/dist/utils/tooling/safeRender.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +71 -28
- package/scripts/{postinstall.js → postinstall.cjs} +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/commands/stats.tsx"],
|
|
4
|
+
"sourcesContent": ["/**\n * Stats Command\n *\n * Displays usage statistics including messages, sessions, tool calls,\n * and token usage with ASCII visualization charts.\n *\n * Usage:\n * /stats - Show today's statistics and overview\n * /stats --week - Show this week's statistics\n * /stats --month - Show this month's statistics\n * /stats --models - Show model usage details\n * /stats --tools - Show tool usage details\n */\n\nimport React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { Command } from '@commands'\nimport { t } from '@i18n'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\nimport {\n getStats,\n getTodayStats,\n getStatsForDays,\n getModelUsageStats,\n getToolUsageStats,\n type DailyActivity,\n type ModelUsage,\n type ToolUsage,\n type StatsData,\n} from '@utils/stats'\nimport { PressEnterToContinue } from '@components/PressEnterToContinue'\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Render an ASCII progress bar\n */\nfunction renderProgressBar(\n value: number,\n max: number,\n width: number = 40,\n): string {\n if (max === 0) return '\u2591'.repeat(width)\n const filled = Math.min(Math.round((value / max) * width), width)\n const empty = width - filled\n return '\u2588'.repeat(filled) + '\u2591'.repeat(empty)\n}\n\n/**\n * Format number with thousands separator\n */\nfunction formatNumber(n: number): string {\n return n.toLocaleString()\n}\n\n/**\n * Format number with K/M suffix for large numbers\n */\nfunction formatCompactNumber(n: number): string {\n if (n >= 1_000_000) {\n return `${(n / 1_000_000).toFixed(1)}M`\n }\n if (n >= 1_000) {\n return `${(n / 1_000).toFixed(1)}K`\n }\n return n.toString()\n}\n\n/**\n * Format cost in USD\n */\nfunction formatCost(cost: number): string {\n if (cost < 0.01) {\n return `$${cost.toFixed(4)}`\n }\n return `$${cost.toFixed(2)}`\n}\n\n/**\n * Get today's date string in YYYY-MM-DD format\n */\nfunction getTodayString(): string {\n return new Date().toISOString().split('T')[0]!\n}\n\n/**\n * Calculate percentage safely (avoid division by zero)\n */\nfunction safePercentage(value: number, total: number): number {\n if (total === 0) return 0\n return Math.round((value / total) * 100)\n}\n\n/**\n * Sum daily stats for a period\n */\nfunction sumDailyStats(days: DailyActivity[]): DailyActivity {\n return days.reduce(\n (acc, day) => ({\n date: '',\n messageCount: acc.messageCount + day.messageCount,\n sessionCount: acc.sessionCount + day.sessionCount,\n toolCallCount: acc.toolCallCount + day.toolCallCount,\n tokensUsed: acc.tokensUsed + day.tokensUsed,\n costEstimate: acc.costEstimate + day.costEstimate,\n }),\n {\n date: '',\n messageCount: 0,\n sessionCount: 0,\n toolCallCount: 0,\n tokensUsed: 0,\n costEstimate: 0,\n },\n )\n}\n\n// ============================================================================\n// UI Components\n// ============================================================================\n\ninterface StatRowProps {\n label: string\n value: number\n max: number\n barWidth?: number\n comparison?: number\n suffix?: string\n}\n\n/**\n * Render a single stat row with progress bar\n */\nfunction StatRow({\n label,\n value,\n max,\n barWidth = 20,\n comparison,\n suffix,\n}: StatRowProps) {\n const bar = renderProgressBar(value, max, barWidth)\n const formattedValue = formatNumber(value)\n const paddedLabel = label.padEnd(12)\n const paddedValue = formattedValue.padStart(8)\n\n return (\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>{paddedLabel}</Text>\n <Text color={SEMANTIC_COLORS.primary}>{paddedValue}</Text>\n <Text color={SEMANTIC_COLORS.dim}> </Text>\n <Text color={BRAND_GRADIENT.START}>{bar}</Text>\n {comparison !== undefined && comparison !== 0 && (\n <Text\n color={\n comparison > 0 ? SEMANTIC_COLORS.success : SEMANTIC_COLORS.error\n }\n >\n {' '}\n ({comparison > 0 ? '+' : ''}\n {comparison} {t('commands.stats.vsYesterday')})\n </Text>\n )}\n {suffix && <Text color={SEMANTIC_COLORS.dim}> {suffix}</Text>}\n </Box>\n )\n}\n\ninterface ModelRowProps {\n key?: string\n model: ModelUsage\n totalCalls: number\n barWidth?: number\n}\n\n/**\n * Render a model usage row\n */\nfunction ModelRow({ model, totalCalls, barWidth = 40 }: ModelRowProps) {\n const percentage = safePercentage(model.calls, totalCalls)\n const bar = renderProgressBar(model.calls, totalCalls, barWidth)\n const displayName =\n model.model.length > 14 ? model.model.slice(0, 11) + '...' : model.model\n\n return (\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>{displayName.padEnd(14)}</Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {String(percentage).padStart(3)}%\n </Text>\n <Text color={SEMANTIC_COLORS.dim}> </Text>\n <Text color={BRAND_GRADIENT.MIDDLE}>{bar}</Text>\n <Text color={SEMANTIC_COLORS.dim}>\n {' '}\n ({formatNumber(model.calls)} {t('commands.stats.calls')})\n </Text>\n </Box>\n )\n}\n\ninterface ToolRowProps {\n key?: string\n tool: ToolUsage\n rank: number\n}\n\n/**\n * Render a tool usage row\n */\nfunction ToolRow({ tool, rank }: ToolRowProps) {\n const successRate = safePercentage(tool.successCount, tool.calls)\n const displayName =\n tool.toolName.length > 16\n ? tool.toolName.slice(0, 13) + '...'\n : tool.toolName\n\n return (\n <Box>\n <Text color={SEMANTIC_COLORS.dim}>{String(rank).padStart(2)}. </Text>\n <Text color={SEMANTIC_COLORS.secondary}>{displayName.padEnd(16)}</Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(tool.calls).padStart(6)}\n </Text>\n <Text color={SEMANTIC_COLORS.dim}> {t('commands.stats.calls')}</Text>\n {tool.errorCount > 0 && (\n <Text color={SEMANTIC_COLORS.error}> ({successRate}% success)</Text>\n )}\n </Box>\n )\n}\n\ninterface StatsDisplayProps {\n stats: StatsData\n today: DailyActivity | null\n yesterday: DailyActivity | null\n weekStats: DailyActivity\n showWeek: boolean\n showMonth: boolean\n showModels: boolean\n showTools: boolean\n onClose: () => void\n}\n\n/**\n * Main stats display component\n */\nfunction StatsDisplay({\n stats,\n today,\n yesterday,\n weekStats,\n showWeek,\n showMonth,\n showModels,\n showTools,\n onClose,\n}: StatsDisplayProps) {\n useInput((_, key) => {\n if (key.return || key.escape) onClose()\n })\n\n const modelStats = getModelUsageStats()\n const toolStats = getToolUsageStats()\n const totalModelCalls = modelStats.reduce((sum, m) => sum + m.calls, 0)\n\n // Calculate comparison values\n const msgDiff =\n today && yesterday ? today.messageCount - yesterday.messageCount : undefined\n const sessionDiff =\n today && yesterday ? today.sessionCount - yesterday.sessionCount : undefined\n const toolDiff =\n today && yesterday\n ? today.toolCallCount - yesterday.toolCallCount\n : undefined\n const tokenDiff =\n today && yesterday ? today.tokensUsed - yesterday.tokensUsed : undefined\n\n // Find max values for progress bars\n const maxMsg = Math.max(\n today?.messageCount ?? 0,\n yesterday?.messageCount ?? 0,\n 1,\n )\n const maxSession = Math.max(\n today?.sessionCount ?? 0,\n yesterday?.sessionCount ?? 0,\n 1,\n )\n const maxTool = Math.max(\n today?.toolCallCount ?? 0,\n yesterday?.toolCallCount ?? 0,\n 1,\n )\n const maxToken = Math.max(\n today?.tokensUsed ?? 0,\n yesterday?.tokensUsed ?? 0,\n 1,\n )\n\n // Check if we have any data\n const hasData =\n stats.lifetime.totalMessages > 0 || stats.lifetime.totalSessions > 0\n\n if (!hasData) {\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={BRAND_GRADIENT.START}>\n {t('commands.stats.title')}\n </Text>\n </Box>\n <Text color={SEMANTIC_COLORS.dim}>{t('commands.stats.noData')}</Text>\n <Box marginTop={1}>\n <PressEnterToContinue />\n </Box>\n </Box>\n )\n }\n\n return (\n <Box flexDirection=\"column\" marginY={1}>\n {/* Header */}\n <Box marginBottom={1}>\n <Text bold color={BRAND_GRADIENT.START}>\n {t('commands.stats.title')}\n </Text>\n </Box>\n\n {/* Divider */}\n <Text color={SEMANTIC_COLORS.dim}>{'\u2500'.repeat(65)}</Text>\n\n {/* Today's Stats */}\n {!showWeek && !showMonth && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold>\n {t('commands.stats.today')} ({getTodayString()})\n </Text>\n </Box>\n <Box paddingLeft={2} flexDirection=\"column\">\n <StatRow\n label={t('commands.stats.messages')}\n value={today?.messageCount ?? 0}\n max={maxMsg * 1.2}\n comparison={msgDiff}\n />\n <StatRow\n label={t('commands.stats.sessions')}\n value={today?.sessionCount ?? 0}\n max={maxSession * 1.2}\n comparison={sessionDiff}\n />\n <StatRow\n label={t('commands.stats.toolCalls')}\n value={today?.toolCallCount ?? 0}\n max={maxTool * 1.2}\n comparison={toolDiff}\n />\n <StatRow\n label={t('commands.stats.tokens')}\n value={today?.tokensUsed ?? 0}\n max={maxToken * 1.2}\n comparison={tokenDiff}\n />\n </Box>\n </Box>\n )}\n\n {/* Week Stats */}\n {(showWeek || !showMonth) && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold>{t('commands.stats.thisWeek')}</Text>\n </Box>\n <Box paddingLeft={2} flexDirection=\"column\">\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.totalMessages').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(weekStats.messageCount)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.totalSessions').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(weekStats.sessionCount)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.totalTools').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(weekStats.toolCallCount)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.estCost').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatCost(weekStats.costEstimate)}\n </Text>\n </Box>\n </Box>\n </Box>\n )}\n\n {/* Month Stats */}\n {showMonth && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold>{t('commands.stats.thisMonth')}</Text>\n </Box>\n <Box paddingLeft={2} flexDirection=\"column\">\n {(() => {\n const monthStats = sumDailyStats(getStatsForDays(30))\n return (\n <>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.totalMessages').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(monthStats.messageCount)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.totalSessions').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(monthStats.sessionCount)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.totalTools').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(monthStats.toolCallCount)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.estCost').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatCost(monthStats.costEstimate)}\n </Text>\n </Box>\n </>\n )\n })()}\n </Box>\n </Box>\n )}\n\n {/* Model Usage */}\n {(showModels || (!showWeek && !showMonth && !showTools)) &&\n modelStats.length > 0 && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold>{t('commands.stats.modelUsage')}</Text>\n </Box>\n <Box paddingLeft={2} flexDirection=\"column\">\n {modelStats.slice(0, 5).map(model => (\n <ModelRow\n key={model.model}\n model={model}\n totalCalls={totalModelCalls}\n />\n ))}\n </Box>\n </Box>\n )}\n\n {/* Tool Usage */}\n {(showTools || (!showWeek && !showMonth && !showModels)) &&\n toolStats.length > 0 && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold>{t('commands.stats.topTools')}</Text>\n </Box>\n <Box paddingLeft={2} flexDirection=\"column\">\n {toolStats.slice(0, 5).map((tool, index) => (\n <ToolRow key={tool.toolName} tool={tool} rank={index + 1} />\n ))}\n </Box>\n </Box>\n )}\n\n {/* Lifetime Stats */}\n {!showWeek && !showMonth && !showModels && !showTools && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold>{t('commands.stats.lifetime')}</Text>\n </Box>\n <Box paddingLeft={2} flexDirection=\"column\">\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.totalMessages').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(stats.lifetime.totalMessages)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.totalSessions').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(stats.lifetime.totalSessions)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.totalTools').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatNumber(stats.lifetime.totalToolCalls)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.tokens').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatCompactNumber(stats.lifetime.totalTokens)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.estCost').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.primary}>\n {formatCost(stats.lifetime.totalCost)}\n </Text>\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.secondary}>\n {t('commands.stats.firstUsed').padEnd(16)}\n </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n {stats.lifetime.firstUsed.split('T')[0]}\n </Text>\n </Box>\n </Box>\n </Box>\n )}\n\n {/* Footer Divider */}\n <Text color={SEMANTIC_COLORS.dim}>{'\u2500'.repeat(65)}</Text>\n\n {/* Press Enter to continue */}\n <Box marginTop={1}>\n <PressEnterToContinue />\n </Box>\n </Box>\n )\n}\n\n// ============================================================================\n// Command Definition\n// ============================================================================\n\ninterface ParsedArgs {\n week: boolean\n month: boolean\n models: boolean\n tools: boolean\n}\n\n/**\n * Parse command arguments\n */\nfunction parseArgs(args: string): ParsedArgs {\n const trimmed = args.trim().toLowerCase()\n return {\n week: trimmed.includes('--week') || trimmed.includes('-w'),\n month: trimmed.includes('--month') || trimmed.includes('-m'),\n models: trimmed.includes('--models'),\n tools: trimmed.includes('--tools'),\n }\n}\n\nconst command: Command = {\n name: 'stats',\n description: t('commands.stats.description'),\n isEnabled: true,\n isHidden: false,\n type: 'local-jsx',\n\n userFacingName() {\n return this.name\n },\n\n async call(onDone, _context, args) {\n const parsedArgs = parseArgs(args ?? '')\n const stats = getStats()\n const today = getTodayStats()\n\n // Get yesterday's stats for comparison\n const recentDays = getStatsForDays(2)\n const yesterday = recentDays.length >= 2 ? (recentDays[1] ?? null) : null\n\n // Get week stats\n const weekStats = sumDailyStats(getStatsForDays(7))\n\n return (\n <StatsDisplay\n stats={stats}\n today={today}\n yesterday={yesterday}\n weekStats={weekStats}\n showWeek={parsedArgs.week}\n showMonth={parsedArgs.month}\n showModels={parsedArgs.models}\n showTools={parsedArgs.tools}\n onClose={onDone}\n />\n )\n },\n}\n\nexport default command\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/commands/status.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Box, Text } from "ink";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { getModelManager } from "../utils/model.js";
|
|
4
4
|
import { getCwd } from "../utils/state.js";
|
|
5
5
|
import { MACRO } from "../constants/macros.js";
|
|
6
6
|
import { SEMANTIC_COLORS } from "../constants/colors.js";
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { PressEnterToContinue } from "../components/PressEnterToContinue.js";
|
|
8
|
+
const StatusDisplay = ({
|
|
9
|
+
status,
|
|
10
|
+
onClose
|
|
11
|
+
}) => {
|
|
12
|
+
useInput((_, key) => {
|
|
13
|
+
if (key.return || key.escape) onClose();
|
|
14
|
+
});
|
|
15
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "cyan" }, "Minto Status")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Version Info"), /* @__PURE__ */ React.createElement(Box, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Minto: "), /* @__PURE__ */ React.createElement(Text, { color: "green" }, "v", status.version)), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Node.js: "), /* @__PURE__ */ React.createElement(Text, null, status.nodeVersion)), status.bunVersion && /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Bun: "), /* @__PURE__ */ React.createElement(Text, null, status.bunVersion)), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Platform: "), /* @__PURE__ */ React.createElement(Text, null, status.platform)))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Environment"), /* @__PURE__ */ React.createElement(Box, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "CWD: "), /* @__PURE__ */ React.createElement(Text, null, status.cwd)))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Model Configuration"), /* @__PURE__ */ React.createElement(Box, { paddingLeft: 2, flexDirection: "column" }, status.modelInfo ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Model: "), /* @__PURE__ */ React.createElement(Text, { color: "cyan" }, status.modelInfo.name)), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Provider: "), /* @__PURE__ */ React.createElement(Text, null, status.modelInfo.provider)), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Context: "), /* @__PURE__ */ React.createElement(Text, null, (status.modelInfo.contextLength / 1e3).toFixed(0), "k tokens"))) : /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "No model configured"))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "API Connection"), /* @__PURE__ */ React.createElement(Box, { paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Status: "), status.apiConnected ? /* @__PURE__ */ React.createElement(Text, { color: "green" }, "\u25CF Connected") : /* @__PURE__ */ React.createElement(Text, { color: "red" }, "\u25CB Not connected")))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(PressEnterToContinue, null)));
|
|
9
16
|
};
|
|
10
17
|
async function getStatusInfo() {
|
|
11
18
|
let modelInfo = null;
|
|
@@ -48,8 +55,7 @@ const command = {
|
|
|
48
55
|
},
|
|
49
56
|
async call(onDone) {
|
|
50
57
|
const status = await getStatusInfo();
|
|
51
|
-
|
|
52
|
-
return /* @__PURE__ */ React.createElement(StatusDisplay, { status });
|
|
58
|
+
return /* @__PURE__ */ React.createElement(StatusDisplay, { status, onClose: onDone });
|
|
53
59
|
}
|
|
54
60
|
};
|
|
55
61
|
var status_default = command;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/status.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * Status Command\n *\n * Shows version information, connection status, and system health.\n */\n\nimport React from 'react'\nimport { Box, Text } from 'ink'\nimport type { Command } from '@commands'\nimport { getModelManager } from '@utils/model'\nimport { getCwd } from '@utils/state'\nimport { MACRO } from '@constants/macros'\nimport { SEMANTIC_COLORS } from '@constants/colors'\n\ninterface StatusInfo {\n version: string\n nodeVersion: string\n bunVersion: string | null\n platform: string\n cwd: string\n modelInfo: {\n name: string\n provider: string\n contextLength: number\n } | null\n apiConnected: boolean\n}\n\nconst StatusDisplay = ({
|
|
5
|
-
"mappings": "AAMA,OAAO,WAAW;AAClB,SAAS,KAAK,
|
|
4
|
+
"sourcesContent": ["/**\n * Status Command\n *\n * Shows version information, connection status, and system health.\n */\n\nimport React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { Command } from '@commands'\nimport { getModelManager } from '@utils/model'\nimport { getCwd } from '@utils/state'\nimport { MACRO } from '@constants/macros'\nimport { SEMANTIC_COLORS } from '@constants/colors'\nimport { PressEnterToContinue } from '@components/PressEnterToContinue'\n\ninterface StatusInfo {\n version: string\n nodeVersion: string\n bunVersion: string | null\n platform: string\n cwd: string\n modelInfo: {\n name: string\n provider: string\n contextLength: number\n } | null\n apiConnected: boolean\n}\n\nconst StatusDisplay = ({\n status,\n onClose,\n}: {\n status: StatusInfo\n onClose: () => void\n}) => {\n useInput((_, key) => {\n if (key.return || key.escape) onClose()\n })\n\n return (\n <Box flexDirection=\"column\" marginY={1}>\n {/* Header */}\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n Minto Status\n </Text>\n </Box>\n\n {/* Version info */}\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text bold>Version Info</Text>\n <Box paddingLeft={2} flexDirection=\"column\">\n <Text>\n <Text color={SEMANTIC_COLORS.dim}>Minto: </Text>\n <Text color=\"green\">v{status.version}</Text>\n </Text>\n <Text>\n <Text color={SEMANTIC_COLORS.dim}>Node.js: </Text>\n <Text>{status.nodeVersion}</Text>\n </Text>\n {status.bunVersion && (\n <Text>\n <Text color={SEMANTIC_COLORS.dim}>Bun: </Text>\n <Text>{status.bunVersion}</Text>\n </Text>\n )}\n <Text>\n <Text color={SEMANTIC_COLORS.dim}>Platform: </Text>\n <Text>{status.platform}</Text>\n </Text>\n </Box>\n </Box>\n\n {/* Environment */}\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text bold>Environment</Text>\n <Box paddingLeft={2} flexDirection=\"column\">\n <Text>\n <Text color={SEMANTIC_COLORS.dim}>CWD: </Text>\n <Text>{status.cwd}</Text>\n </Text>\n </Box>\n </Box>\n\n {/* Model info */}\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text bold>Model Configuration</Text>\n <Box paddingLeft={2} flexDirection=\"column\">\n {status.modelInfo ? (\n <>\n <Text>\n <Text color={SEMANTIC_COLORS.dim}>Model: </Text>\n <Text color=\"cyan\">{status.modelInfo.name}</Text>\n </Text>\n <Text>\n <Text color={SEMANTIC_COLORS.dim}>Provider: </Text>\n <Text>{status.modelInfo.provider}</Text>\n </Text>\n <Text>\n <Text color={SEMANTIC_COLORS.dim}>Context: </Text>\n <Text>\n {(status.modelInfo.contextLength / 1000).toFixed(0)}k tokens\n </Text>\n </Text>\n </>\n ) : (\n <Text color=\"yellow\">No model configured</Text>\n )}\n </Box>\n </Box>\n\n {/* API Status */}\n <Box flexDirection=\"column\">\n <Text bold>API Connection</Text>\n <Box paddingLeft={2}>\n <Text>\n <Text color={SEMANTIC_COLORS.dim}>Status: </Text>\n {status.apiConnected ? (\n <Text color=\"green\">\u25CF Connected</Text>\n ) : (\n <Text color=\"red\">\u25CB Not connected</Text>\n )}\n </Text>\n </Box>\n </Box>\n\n {/* Press Enter to continue */}\n <Box marginTop={1}>\n <PressEnterToContinue />\n </Box>\n </Box>\n )\n}\n\nasync function getStatusInfo(): Promise<StatusInfo> {\n let modelInfo: StatusInfo['modelInfo'] = null\n let apiConnected = false\n\n try {\n const modelManager = getModelManager()\n const profile = modelManager.resolveModel('main')\n if (profile) {\n modelInfo = {\n name: profile.name || profile.modelName,\n provider: profile.provider || 'anthropic',\n contextLength: profile.contextLength || 200_000,\n }\n apiConnected = true // If we can resolve model, API is configured\n }\n } catch {\n // Model not configured\n }\n\n // Check for Bun\n let bunVersion: string | null = null\n if (typeof Bun !== 'undefined') {\n bunVersion = Bun.version\n }\n\n return {\n version: MACRO.VERSION,\n nodeVersion: process.version,\n bunVersion,\n platform: `${process.platform} (${process.arch})`,\n cwd: getCwd(),\n modelInfo,\n apiConnected,\n }\n}\n\nconst command: Command = {\n name: 'status',\n description: 'Display version and connection information',\n isEnabled: true,\n isHidden: false,\n type: 'local-jsx',\n\n userFacingName() {\n return this.name\n },\n\n async call(onDone) {\n const status = await getStatusInfo()\n\n return <StatusDisplay status={status} onClose={onDone} />\n },\n}\n\nexport default command\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/commands/undo.js
CHANGED
|
@@ -7,22 +7,32 @@ import {
|
|
|
7
7
|
getBackupVersions,
|
|
8
8
|
restoreBackup
|
|
9
9
|
} from "../core/backupManager.js";
|
|
10
|
+
import { t } from "../i18n/index.js";
|
|
10
11
|
function formatTimestamp(timestamp) {
|
|
11
12
|
const date = new Date(timestamp);
|
|
12
13
|
const now = /* @__PURE__ */ new Date();
|
|
13
14
|
const diff = now.getTime() - timestamp;
|
|
15
|
+
const timeStr = `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
|
|
14
16
|
if (diff < 60 * 60 * 1e3) {
|
|
15
17
|
const minutes = Math.floor(diff / (60 * 1e3));
|
|
16
|
-
return minutes <= 1 ? "
|
|
18
|
+
return minutes <= 1 ? t("commands.undo.justNow") : t("commands.undo.minutesAgo", { count: minutes });
|
|
17
19
|
}
|
|
18
20
|
if (date.getDate() === now.getDate() && date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear()) {
|
|
19
|
-
return
|
|
21
|
+
return `${t("commands.undo.today")} ${timeStr}`;
|
|
20
22
|
}
|
|
21
23
|
if (diff < 7 * 24 * 60 * 60 * 1e3) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
+
const weekdayKeys = [
|
|
25
|
+
"commands.undo.weekday.sun",
|
|
26
|
+
"commands.undo.weekday.mon",
|
|
27
|
+
"commands.undo.weekday.tue",
|
|
28
|
+
"commands.undo.weekday.wed",
|
|
29
|
+
"commands.undo.weekday.thu",
|
|
30
|
+
"commands.undo.weekday.fri",
|
|
31
|
+
"commands.undo.weekday.sat"
|
|
32
|
+
];
|
|
33
|
+
return `${t(weekdayKeys[date.getDay()])} ${timeStr}`;
|
|
24
34
|
}
|
|
25
|
-
return `${date.getMonth() + 1}/${date.getDate()} ${
|
|
35
|
+
return `${date.getMonth() + 1}/${date.getDate()} ${timeStr}`;
|
|
26
36
|
}
|
|
27
37
|
function getRelativePath(filePath) {
|
|
28
38
|
const cwd = getCwd();
|
|
@@ -43,9 +53,9 @@ const FileListView = ({ files, selectedIndex, onSelect, onClose }) => {
|
|
|
43
53
|
}
|
|
44
54
|
});
|
|
45
55
|
if (files.length === 0) {
|
|
46
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.brand }, "
|
|
56
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.brand }, t("commands.undo.title"))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingLeft: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, t("commands.undo.noBackups")), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, " "), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, t("commands.undo.smartModeHint").replace("--smart", ""), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.info }, "--smart")), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, t("commands.undo.smartModeCommand"))), /* @__PURE__ */ React.createElement(Box, { marginTop: 2 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, t("commands.undo.pressQOrEsc"))));
|
|
47
57
|
}
|
|
48
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.brand }, "
|
|
58
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.brand }, t("commands.undo.title")), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "- ", t("commands.undo.selectFile"))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, files.map((file, index) => {
|
|
49
59
|
const isSelected = index === selectedIndex;
|
|
50
60
|
const relativePath = getRelativePath(file.filePath);
|
|
51
61
|
return /* @__PURE__ */ React.createElement(Box, { key: file.filePath, paddingLeft: 1 }, /* @__PURE__ */ React.createElement(Text, { color: isSelected ? SEMANTIC_COLORS.info : void 0 }, isSelected ? "\u25B6 " : " ", /* @__PURE__ */ React.createElement(
|
|
@@ -54,8 +64,8 @@ const FileListView = ({ files, selectedIndex, onSelect, onClose }) => {
|
|
|
54
64
|
color: isSelected ? SEMANTIC_COLORS.primary : SEMANTIC_COLORS.secondary
|
|
55
65
|
},
|
|
56
66
|
relativePath
|
|
57
|
-
), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "(", file.versionCount, "
|
|
58
|
-
})), /* @__PURE__ */ React.createElement(Box, { marginTop: 2 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, "
|
|
67
|
+
), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "(", t("commands.undo.versions", { count: file.versionCount }), ")")));
|
|
68
|
+
})), /* @__PURE__ */ React.createElement(Box, { marginTop: 2 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, t("commands.undo.navHintsFile"))));
|
|
59
69
|
};
|
|
60
70
|
const VersionListView = ({ file, versions, selectedIndex, onSelect, onBack }) => {
|
|
61
71
|
useInput((input, key) => {
|
|
@@ -68,7 +78,7 @@ const VersionListView = ({ file, versions, selectedIndex, onSelect, onBack }) =>
|
|
|
68
78
|
}
|
|
69
79
|
});
|
|
70
80
|
const relativePath = getRelativePath(file.filePath);
|
|
71
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.brand }, "
|
|
81
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.brand }, t("commands.undo.selectVersion"))), /* @__PURE__ */ React.createElement(Box, { marginBottom: 1, paddingLeft: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, t("commands.undo.file"), ": "), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.secondary }, relativePath)), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, versions.map((version, index) => {
|
|
72
82
|
const isSelected = index === selectedIndex;
|
|
73
83
|
return /* @__PURE__ */ React.createElement(Box, { key: version.version, paddingLeft: 1 }, /* @__PURE__ */ React.createElement(Text, { color: isSelected ? SEMANTIC_COLORS.info : void 0 }, isSelected ? "\u25B6 " : " ", /* @__PURE__ */ React.createElement(
|
|
74
84
|
Text,
|
|
@@ -78,7 +88,7 @@ const VersionListView = ({ file, versions, selectedIndex, onSelect, onBack }) =>
|
|
|
78
88
|
"v",
|
|
79
89
|
version.version
|
|
80
90
|
), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, ": ", version.operation, " - ", formatTimestamp(version.timestamp))));
|
|
81
|
-
})), /* @__PURE__ */ React.createElement(Box, { marginTop: 2 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, "
|
|
91
|
+
})), /* @__PURE__ */ React.createElement(Box, { marginTop: 2 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, t("commands.undo.navHintsVersion"))));
|
|
82
92
|
};
|
|
83
93
|
const SuccessView = ({ file, version, onClose }) => {
|
|
84
94
|
useInput((input, key) => {
|
|
@@ -87,7 +97,7 @@ const SuccessView = ({ file, version, onClose }) => {
|
|
|
87
97
|
}
|
|
88
98
|
});
|
|
89
99
|
const relativePath = getRelativePath(file.filePath);
|
|
90
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.success }, "\u2713
|
|
100
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.success }, "\u2713 ", t("commands.undo.fileRestored"))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingLeft: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, t("commands.undo.file"), ": "), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.secondary }, relativePath)), /* @__PURE__ */ React.createElement(Box, { paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, t("commands.undo.restoredTo"), ": ", version.operation, " (", formatTimestamp(version.timestamp), ")")), /* @__PURE__ */ React.createElement(Box, { marginTop: 2 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, t("commands.undo.pressAnyKey"))));
|
|
91
101
|
};
|
|
92
102
|
const UndoCommand = ({ onClose }) => {
|
|
93
103
|
const [view, setView] = useState("file-list");
|
|
@@ -164,7 +174,7 @@ const UndoCommand = ({ onClose }) => {
|
|
|
164
174
|
setRestoredVersion(version);
|
|
165
175
|
setView("success");
|
|
166
176
|
} else {
|
|
167
|
-
setError("
|
|
177
|
+
setError(t("commands.undo.restoreFailed"));
|
|
168
178
|
}
|
|
169
179
|
} catch (err) {
|
|
170
180
|
setError(err instanceof Error ? err.message : String(err));
|
|
@@ -177,10 +187,10 @@ const UndoCommand = ({ onClose }) => {
|
|
|
177
187
|
setSelectedVersionIndex(0);
|
|
178
188
|
};
|
|
179
189
|
if (isLoading) {
|
|
180
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "
|
|
190
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, t("commands.undo.loading")));
|
|
181
191
|
}
|
|
182
192
|
if (error) {
|
|
183
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.error }, "
|
|
193
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.error }, t("commands.undo.error"))), /* @__PURE__ */ React.createElement(Box, { paddingLeft: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, error)), /* @__PURE__ */ React.createElement(Box, { marginTop: 2 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, t("commands.undo.pressAnyKey"))));
|
|
184
194
|
}
|
|
185
195
|
switch (view) {
|
|
186
196
|
case "file-list":
|
|
@@ -227,7 +237,7 @@ const UndoCommand = ({ onClose }) => {
|
|
|
227
237
|
};
|
|
228
238
|
const command = {
|
|
229
239
|
name: "undo",
|
|
230
|
-
description: "
|
|
240
|
+
description: t("commands.undo.description"),
|
|
231
241
|
type: "local-jsx",
|
|
232
242
|
isEnabled: true,
|
|
233
243
|
isHidden: false,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/undo.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * Undo Command\n *\n * Interactive command to restore file versions from backups.\n * Requires --smart mode to have backups available.\n */\n\nimport React, { useState, useEffect } from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { Command } from '@commands'\nimport { SEMANTIC_COLORS } from '@constants/colors'\nimport { getCwd } from '@utils/state'\nimport {\n listBackedUpFiles,\n getBackupVersions,\n restoreBackup,\n type BackupFileSummary,\n type BackupMetadata,\n} from '../core/backupManager'\n\ntype ViewState = 'file-list' | 'version-list' | 'success'\n\ninterface UndoCommandProps {\n onClose: () => void\n}\n\n/**\n * Format a timestamp as a human-readable string\n */\nfunction formatTimestamp(timestamp: number): string {\n const date = new Date(timestamp)\n const now = new Date()\n const diff = now.getTime() - timestamp\n\n // Within last hour - show minutes\n if (diff < 60 * 60 * 1000) {\n const minutes = Math.floor(diff / (60 * 1000))\n return minutes <= 1 ? '\u521A\u521A' : `${minutes} \u5206\u949F\u524D`\n }\n\n // Within today - show time\n if (\n date.getDate() === now.getDate() &&\n date.getMonth() === now.getMonth() &&\n date.getFullYear() === now.getFullYear()\n ) {\n return `\u4ECA\u5929 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`\n }\n\n // Within this week - show day and time\n if (diff < 7 * 24 * 60 * 60 * 1000) {\n const days = ['\u65E5', '\u4E00', '\u4E8C', '\u4E09', '\u56DB', '\u4E94', '\u516D']\n return `\u5468${days[date.getDay()]} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`\n }\n\n // Otherwise - show date\n return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`\n}\n\n/**\n * Get relative path from cwd\n */\nfunction getRelativePath(filePath: string): string {\n const cwd = getCwd()\n if (filePath.startsWith(cwd)) {\n const relative = filePath.slice(cwd.length)\n return relative.startsWith('/') ? relative.slice(1) : relative\n }\n return filePath\n}\n\n/**\n * File List View - shows all files with backups\n */\nconst FileListView: React.FC<{\n files: BackupFileSummary[]\n selectedIndex: number\n onSelect: (file: BackupFileSummary) => void\n onClose: () => void\n}> = ({ files, selectedIndex, onSelect, onClose }) => {\n useInput((input, key) => {\n if (key.escape || input === 'q') {\n onClose()\n } else if (key.return) {\n if (files.length > 0 && files[selectedIndex]) {\n onSelect(files[selectedIndex])\n }\n }\n })\n\n if (files.length === 0) {\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.brand}>\n \u64A4\u9500\u6587\u4EF6\u4FEE\u6539\n </Text>\n </Box>\n\n <Box flexDirection=\"column\" paddingLeft={1}>\n <Text color={SEMANTIC_COLORS.dim}>\u6682\u65E0\u53EF\u64A4\u9500\u7684\u6587\u4EF6\u4FEE\u6539\u3002</Text>\n <Text color={SEMANTIC_COLORS.muted}> </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n \u63D0\u793A\uFF1A\u4F7F\u7528 <Text color={SEMANTIC_COLORS.info}>--smart</Text>{' '}\n \u6A21\u5F0F\u542F\u52A8 Minto \u4EE5\u542F\u7528\u81EA\u52A8\u5907\u4EFD\u529F\u80FD\uFF1A\n </Text>\n <Text color={SEMANTIC_COLORS.muted}> minto --smart</Text>\n </Box>\n\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\u6309 q \u6216 Esc \u9000\u51FA</Text>\n </Box>\n </Box>\n )\n }\n\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.brand}>\n \u64A4\u9500\u6587\u4EF6\u4FEE\u6539\n </Text>\n <Text color={SEMANTIC_COLORS.dim}> - \u9009\u62E9\u8981\u6062\u590D\u7684\u6587\u4EF6</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n {files.map((file, index) => {\n const isSelected = index === selectedIndex\n const relativePath = getRelativePath(file.filePath)\n\n return (\n <Box key={file.filePath} paddingLeft={1}>\n <Text color={isSelected ? SEMANTIC_COLORS.info : undefined}>\n {isSelected ? '\u25B6 ' : ' '}\n <Text\n color={\n isSelected\n ? SEMANTIC_COLORS.primary\n : SEMANTIC_COLORS.secondary\n }\n >\n {relativePath}\n </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n {' '}\n ({file.versionCount} \u4E2A\u7248\u672C)\n </Text>\n </Text>\n </Box>\n )\n })}\n </Box>\n\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\n \u2191/\u2193: \u9009\u62E9 | Enter: \u67E5\u770B\u7248\u672C | q/Esc: \u9000\u51FA\n </Text>\n </Box>\n </Box>\n )\n}\n\n/**\n * Version List View - shows all versions for a selected file\n */\nconst VersionListView: React.FC<{\n file: BackupFileSummary\n versions: BackupMetadata[]\n selectedIndex: number\n onSelect: (version: BackupMetadata) => void\n onBack: () => void\n}> = ({ file, versions, selectedIndex, onSelect, onBack }) => {\n useInput((input, key) => {\n if (key.escape || input === 'q') {\n onBack()\n } else if (key.return) {\n if (versions.length > 0 && versions[selectedIndex]) {\n onSelect(versions[selectedIndex])\n }\n }\n })\n\n const relativePath = getRelativePath(file.filePath)\n\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.brand}>\n \u9009\u62E9\u8981\u6062\u590D\u7684\u7248\u672C\n </Text>\n </Box>\n\n <Box marginBottom={1} paddingLeft={1}>\n <Text color={SEMANTIC_COLORS.dim}>\u6587\u4EF6: </Text>\n <Text color={SEMANTIC_COLORS.secondary}>{relativePath}</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n {versions.map((version, index) => {\n const isSelected = index === selectedIndex\n\n return (\n <Box key={version.version} paddingLeft={1}>\n <Text color={isSelected ? SEMANTIC_COLORS.info : undefined}>\n {isSelected ? '\u25B6 ' : ' '}\n <Text\n color={\n isSelected\n ? SEMANTIC_COLORS.primary\n : SEMANTIC_COLORS.secondary\n }\n >\n v{version.version}\n </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n : {version.operation} - {formatTimestamp(version.timestamp)}\n </Text>\n </Text>\n </Box>\n )\n })}\n </Box>\n\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\n \u2191/\u2193: \u9009\u62E9 | Enter: \u6062\u590D\u6B64\u7248\u672C | Esc: \u8FD4\u56DE\n </Text>\n </Box>\n </Box>\n )\n}\n\n/**\n * Success View - shows restoration confirmation\n */\nconst SuccessView: React.FC<{\n file: BackupFileSummary\n version: BackupMetadata\n onClose: () => void\n}> = ({ file, version, onClose }) => {\n useInput((input, key) => {\n if (key.escape || input === 'q' || key.return) {\n onClose()\n }\n })\n\n const relativePath = getRelativePath(file.filePath)\n\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.success}>\n \u2713 \u6587\u4EF6\u5DF2\u6062\u590D\n </Text>\n </Box>\n\n <Box flexDirection=\"column\" paddingLeft={1}>\n <Text color={SEMANTIC_COLORS.dim}>\u6587\u4EF6: </Text>\n <Text color={SEMANTIC_COLORS.secondary}>{relativePath}</Text>\n </Box>\n\n <Box paddingLeft={1} marginTop={1}>\n <Text color={SEMANTIC_COLORS.dim}>\n \u5DF2\u6062\u590D\u5230: {version.operation} ({formatTimestamp(version.timestamp)})\n </Text>\n </Box>\n\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\u6309\u4EFB\u610F\u952E\u9000\u51FA</Text>\n </Box>\n </Box>\n )\n}\n\n/**\n * Main Undo Command Component\n */\nconst UndoCommand: React.FC<UndoCommandProps> = ({ onClose }) => {\n const [view, setView] = useState<ViewState>('file-list')\n const [files, setFiles] = useState<BackupFileSummary[]>([])\n const [selectedFileIndex, setSelectedFileIndex] = useState(0)\n const [selectedFile, setSelectedFile] = useState<BackupFileSummary | null>(\n null,\n )\n const [versions, setVersions] = useState<BackupMetadata[]>([])\n const [selectedVersionIndex, setSelectedVersionIndex] = useState(0)\n const [restoredVersion, setRestoredVersion] = useState<BackupMetadata | null>(\n null,\n )\n const [error, setError] = useState<string | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n\n // Load files with backups on mount\n useEffect(() => {\n const loadFiles = async () => {\n try {\n const backupFiles = await listBackedUpFiles()\n // Already sorted by most recent in listBackedUpFiles\n setFiles(backupFiles)\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setIsLoading(false)\n }\n }\n loadFiles()\n }, [])\n\n // Handle keyboard navigation for file list\n useInput(\n (input, key) => {\n if (view === 'file-list' && files.length > 0) {\n if (key.upArrow) {\n setSelectedFileIndex(prev => Math.max(0, prev - 1))\n } else if (key.downArrow) {\n setSelectedFileIndex(prev => Math.min(files.length - 1, prev + 1))\n }\n }\n },\n { isActive: view === 'file-list' },\n )\n\n // Handle keyboard navigation for version list\n useInput(\n (input, key) => {\n if (view === 'version-list' && versions.length > 0) {\n if (key.upArrow) {\n setSelectedVersionIndex(prev => Math.max(0, prev - 1))\n } else if (key.downArrow) {\n setSelectedVersionIndex(prev =>\n Math.min(versions.length - 1, prev + 1),\n )\n }\n }\n },\n { isActive: view === 'version-list' },\n )\n\n // Handle file selection\n const handleFileSelect = async (file: BackupFileSummary) => {\n setSelectedFile(file)\n try {\n const fileVersions = await getBackupVersions(file.filePath)\n // Already sorted by newest first in getBackupVersions\n setVersions(fileVersions)\n setSelectedVersionIndex(0)\n setView('version-list')\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n }\n\n // Handle version selection and restore\n const handleVersionSelect = async (version: BackupMetadata) => {\n if (!selectedFile) return\n\n try {\n const success = await restoreBackup(\n selectedFile.filePath,\n version.version,\n )\n if (success) {\n setRestoredVersion(version)\n setView('success')\n } else {\n setError('\u6062\u590D\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002')\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n }\n\n // Handle back navigation\n const handleBack = () => {\n setView('file-list')\n setSelectedFile(null)\n setVersions([])\n setSelectedVersionIndex(0)\n }\n\n // Loading state\n if (isLoading) {\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Text color={SEMANTIC_COLORS.dim}>\u52A0\u8F7D\u5907\u4EFD\u4FE1\u606F...</Text>\n </Box>\n )\n }\n\n // Error display\n if (error) {\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.error}>\n \u9519\u8BEF\n </Text>\n </Box>\n <Box paddingLeft={1}>\n <Text color={SEMANTIC_COLORS.dim}>{error}</Text>\n </Box>\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\u6309\u4EFB\u610F\u952E\u9000\u51FA</Text>\n </Box>\n </Box>\n )\n }\n\n // Render current view\n switch (view) {\n case 'file-list':\n return (\n <FileListView\n files={files}\n selectedIndex={selectedFileIndex}\n onSelect={handleFileSelect}\n onClose={onClose}\n />\n )\n\n case 'version-list':\n if (!selectedFile) {\n handleBack()\n return null\n }\n return (\n <VersionListView\n file={selectedFile}\n versions={versions}\n selectedIndex={selectedVersionIndex}\n onSelect={handleVersionSelect}\n onBack={handleBack}\n />\n )\n\n case 'success':\n if (!selectedFile || !restoredVersion) {\n onClose()\n return null\n }\n return (\n <SuccessView\n file={selectedFile}\n version={restoredVersion}\n onClose={onClose}\n />\n )\n\n default:\n return null\n }\n}\n\n/**\n * Undo Command Export\n */\nconst command: Command = {\n name: 'undo',\n description: '\u64A4\u9500\u6587\u4EF6\u4FEE\u6539\uFF08\u9700\u8981 --smart \u6A21\u5F0F\uFF09',\n type: 'local-jsx' as const,\n isEnabled: true,\n isHidden: false,\n\n userFacingName() {\n return 'undo'\n },\n\n async call(onDone: (result?: string) => void) {\n return <UndoCommand onClose={onDone} />\n },\n}\n\nexport default command\n"],
|
|
5
|
-
"mappings": "AAOA,OAAO,SAAS,UAAU,iBAAiB;AAC3C,SAAS,KAAK,MAAM,gBAAgB;AAEpC,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;
|
|
4
|
+
"sourcesContent": ["/**\n * Undo Command\n *\n * Interactive command to restore file versions from backups.\n * Requires --smart mode to have backups available.\n */\n\nimport React, { useState, useEffect } from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport type { Command } from '@commands'\nimport { SEMANTIC_COLORS } from '@constants/colors'\nimport { getCwd } from '@utils/state'\nimport {\n listBackedUpFiles,\n getBackupVersions,\n restoreBackup,\n type BackupFileSummary,\n type BackupMetadata,\n} from '../core/backupManager'\nimport { t } from '@i18n'\n\ntype ViewState = 'file-list' | 'version-list' | 'success'\n\ninterface UndoCommandProps {\n onClose: () => void\n}\n\n/**\n * Format a timestamp as a human-readable string\n */\nfunction formatTimestamp(timestamp: number): string {\n const date = new Date(timestamp)\n const now = new Date()\n const diff = now.getTime() - timestamp\n const timeStr = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`\n\n // Within last hour - show minutes\n if (diff < 60 * 60 * 1000) {\n const minutes = Math.floor(diff / (60 * 1000))\n return minutes <= 1\n ? t('commands.undo.justNow')\n : t('commands.undo.minutesAgo', { count: minutes })\n }\n\n // Within today - show time\n if (\n date.getDate() === now.getDate() &&\n date.getMonth() === now.getMonth() &&\n date.getFullYear() === now.getFullYear()\n ) {\n return `${t('commands.undo.today')} ${timeStr}`\n }\n\n // Within this week - show day and time\n if (diff < 7 * 24 * 60 * 60 * 1000) {\n const weekdayKeys = [\n 'commands.undo.weekday.sun',\n 'commands.undo.weekday.mon',\n 'commands.undo.weekday.tue',\n 'commands.undo.weekday.wed',\n 'commands.undo.weekday.thu',\n 'commands.undo.weekday.fri',\n 'commands.undo.weekday.sat',\n ] as const\n return `${t(weekdayKeys[date.getDay()])} ${timeStr}`\n }\n\n // Otherwise - show date\n return `${date.getMonth() + 1}/${date.getDate()} ${timeStr}`\n}\n\n/**\n * Get relative path from cwd\n */\nfunction getRelativePath(filePath: string): string {\n const cwd = getCwd()\n if (filePath.startsWith(cwd)) {\n const relative = filePath.slice(cwd.length)\n return relative.startsWith('/') ? relative.slice(1) : relative\n }\n return filePath\n}\n\n/**\n * File List View - shows all files with backups\n */\nconst FileListView: React.FC<{\n files: BackupFileSummary[]\n selectedIndex: number\n onSelect: (file: BackupFileSummary) => void\n onClose: () => void\n}> = ({ files, selectedIndex, onSelect, onClose }) => {\n useInput((input, key) => {\n if (key.escape || input === 'q') {\n onClose()\n } else if (key.return) {\n if (files.length > 0 && files[selectedIndex]) {\n onSelect(files[selectedIndex])\n }\n }\n })\n\n if (files.length === 0) {\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.brand}>\n {t('commands.undo.title')}\n </Text>\n </Box>\n\n <Box flexDirection=\"column\" paddingLeft={1}>\n <Text color={SEMANTIC_COLORS.dim}>\n {t('commands.undo.noBackups')}\n </Text>\n <Text color={SEMANTIC_COLORS.muted}> </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n {t('commands.undo.smartModeHint').replace('--smart', '')}\n <Text color={SEMANTIC_COLORS.info}>--smart</Text>\n </Text>\n <Text color={SEMANTIC_COLORS.muted}>\n {t('commands.undo.smartModeCommand')}\n </Text>\n </Box>\n\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\n {t('commands.undo.pressQOrEsc')}\n </Text>\n </Box>\n </Box>\n )\n }\n\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.brand}>\n {t('commands.undo.title')}\n </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n {' '}\n - {t('commands.undo.selectFile')}\n </Text>\n </Box>\n\n <Box flexDirection=\"column\">\n {files.map((file, index) => {\n const isSelected = index === selectedIndex\n const relativePath = getRelativePath(file.filePath)\n\n return (\n <Box key={file.filePath} paddingLeft={1}>\n <Text color={isSelected ? SEMANTIC_COLORS.info : undefined}>\n {isSelected ? '\u25B6 ' : ' '}\n <Text\n color={\n isSelected\n ? SEMANTIC_COLORS.primary\n : SEMANTIC_COLORS.secondary\n }\n >\n {relativePath}\n </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n {' '}\n ({t('commands.undo.versions', { count: file.versionCount })})\n </Text>\n </Text>\n </Box>\n )\n })}\n </Box>\n\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\n {t('commands.undo.navHintsFile')}\n </Text>\n </Box>\n </Box>\n )\n}\n\n/**\n * Version List View - shows all versions for a selected file\n */\nconst VersionListView: React.FC<{\n file: BackupFileSummary\n versions: BackupMetadata[]\n selectedIndex: number\n onSelect: (version: BackupMetadata) => void\n onBack: () => void\n}> = ({ file, versions, selectedIndex, onSelect, onBack }) => {\n useInput((input, key) => {\n if (key.escape || input === 'q') {\n onBack()\n } else if (key.return) {\n if (versions.length > 0 && versions[selectedIndex]) {\n onSelect(versions[selectedIndex])\n }\n }\n })\n\n const relativePath = getRelativePath(file.filePath)\n\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.brand}>\n {t('commands.undo.selectVersion')}\n </Text>\n </Box>\n\n <Box marginBottom={1} paddingLeft={1}>\n <Text color={SEMANTIC_COLORS.dim}>{t('commands.undo.file')}: </Text>\n <Text color={SEMANTIC_COLORS.secondary}>{relativePath}</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n {versions.map((version, index) => {\n const isSelected = index === selectedIndex\n\n return (\n <Box key={version.version} paddingLeft={1}>\n <Text color={isSelected ? SEMANTIC_COLORS.info : undefined}>\n {isSelected ? '\u25B6 ' : ' '}\n <Text\n color={\n isSelected\n ? SEMANTIC_COLORS.primary\n : SEMANTIC_COLORS.secondary\n }\n >\n v{version.version}\n </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n : {version.operation} - {formatTimestamp(version.timestamp)}\n </Text>\n </Text>\n </Box>\n )\n })}\n </Box>\n\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\n {t('commands.undo.navHintsVersion')}\n </Text>\n </Box>\n </Box>\n )\n}\n\n/**\n * Success View - shows restoration confirmation\n */\nconst SuccessView: React.FC<{\n file: BackupFileSummary\n version: BackupMetadata\n onClose: () => void\n}> = ({ file, version, onClose }) => {\n useInput((input, key) => {\n if (key.escape || input === 'q' || key.return) {\n onClose()\n }\n })\n\n const relativePath = getRelativePath(file.filePath)\n\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.success}>\n \u2713 {t('commands.undo.fileRestored')}\n </Text>\n </Box>\n\n <Box flexDirection=\"column\" paddingLeft={1}>\n <Text color={SEMANTIC_COLORS.dim}>{t('commands.undo.file')}: </Text>\n <Text color={SEMANTIC_COLORS.secondary}>{relativePath}</Text>\n </Box>\n\n <Box paddingLeft={1} marginTop={1}>\n <Text color={SEMANTIC_COLORS.dim}>\n {t('commands.undo.restoredTo')}: {version.operation} (\n {formatTimestamp(version.timestamp)})\n </Text>\n </Box>\n\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\n {t('commands.undo.pressAnyKey')}\n </Text>\n </Box>\n </Box>\n )\n}\n\n/**\n * Main Undo Command Component\n */\nconst UndoCommand: React.FC<UndoCommandProps> = ({ onClose }) => {\n const [view, setView] = useState<ViewState>('file-list')\n const [files, setFiles] = useState<BackupFileSummary[]>([])\n const [selectedFileIndex, setSelectedFileIndex] = useState(0)\n const [selectedFile, setSelectedFile] = useState<BackupFileSummary | null>(\n null,\n )\n const [versions, setVersions] = useState<BackupMetadata[]>([])\n const [selectedVersionIndex, setSelectedVersionIndex] = useState(0)\n const [restoredVersion, setRestoredVersion] = useState<BackupMetadata | null>(\n null,\n )\n const [error, setError] = useState<string | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n\n // Load files with backups on mount\n useEffect(() => {\n const loadFiles = async () => {\n try {\n const backupFiles = await listBackedUpFiles()\n // Already sorted by most recent in listBackedUpFiles\n setFiles(backupFiles)\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setIsLoading(false)\n }\n }\n loadFiles()\n }, [])\n\n // Handle keyboard navigation for file list\n useInput(\n (input, key) => {\n if (view === 'file-list' && files.length > 0) {\n if (key.upArrow) {\n setSelectedFileIndex(prev => Math.max(0, prev - 1))\n } else if (key.downArrow) {\n setSelectedFileIndex(prev => Math.min(files.length - 1, prev + 1))\n }\n }\n },\n { isActive: view === 'file-list' },\n )\n\n // Handle keyboard navigation for version list\n useInput(\n (input, key) => {\n if (view === 'version-list' && versions.length > 0) {\n if (key.upArrow) {\n setSelectedVersionIndex(prev => Math.max(0, prev - 1))\n } else if (key.downArrow) {\n setSelectedVersionIndex(prev =>\n Math.min(versions.length - 1, prev + 1),\n )\n }\n }\n },\n { isActive: view === 'version-list' },\n )\n\n // Handle file selection\n const handleFileSelect = async (file: BackupFileSummary) => {\n setSelectedFile(file)\n try {\n const fileVersions = await getBackupVersions(file.filePath)\n // Already sorted by newest first in getBackupVersions\n setVersions(fileVersions)\n setSelectedVersionIndex(0)\n setView('version-list')\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n }\n\n // Handle version selection and restore\n const handleVersionSelect = async (version: BackupMetadata) => {\n if (!selectedFile) return\n\n try {\n const success = await restoreBackup(\n selectedFile.filePath,\n version.version,\n )\n if (success) {\n setRestoredVersion(version)\n setView('success')\n } else {\n setError(t('commands.undo.restoreFailed'))\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n }\n\n // Handle back navigation\n const handleBack = () => {\n setView('file-list')\n setSelectedFile(null)\n setVersions([])\n setSelectedVersionIndex(0)\n }\n\n // Loading state\n if (isLoading) {\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Text color={SEMANTIC_COLORS.dim}>{t('commands.undo.loading')}</Text>\n </Box>\n )\n }\n\n // Error display\n if (error) {\n return (\n <Box flexDirection=\"column\" marginY={1}>\n <Box marginBottom={1}>\n <Text bold color={SEMANTIC_COLORS.error}>\n {t('commands.undo.error')}\n </Text>\n </Box>\n <Box paddingLeft={1}>\n <Text color={SEMANTIC_COLORS.dim}>{error}</Text>\n </Box>\n <Box marginTop={2}>\n <Text color={SEMANTIC_COLORS.muted}>\n {t('commands.undo.pressAnyKey')}\n </Text>\n </Box>\n </Box>\n )\n }\n\n // Render current view\n switch (view) {\n case 'file-list':\n return (\n <FileListView\n files={files}\n selectedIndex={selectedFileIndex}\n onSelect={handleFileSelect}\n onClose={onClose}\n />\n )\n\n case 'version-list':\n if (!selectedFile) {\n handleBack()\n return null\n }\n return (\n <VersionListView\n file={selectedFile}\n versions={versions}\n selectedIndex={selectedVersionIndex}\n onSelect={handleVersionSelect}\n onBack={handleBack}\n />\n )\n\n case 'success':\n if (!selectedFile || !restoredVersion) {\n onClose()\n return null\n }\n return (\n <SuccessView\n file={selectedFile}\n version={restoredVersion}\n onClose={onClose}\n />\n )\n\n default:\n return null\n }\n}\n\n/**\n * Undo Command Export\n */\nconst command: Command = {\n name: 'undo',\n description: t('commands.undo.description'),\n type: 'local-jsx' as const,\n isEnabled: true,\n isHidden: false,\n\n userFacingName() {\n return 'undo'\n },\n\n async call(onDone: (result?: string) => void) {\n return <UndoCommand onClose={onDone} />\n },\n}\n\nexport default command\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/commands.js
CHANGED
|
@@ -16,10 +16,13 @@ import setup from "./commands/setup.js";
|
|
|
16
16
|
import status from "./commands/status.js";
|
|
17
17
|
import terminalSetup from "./commands/terminalSetup.js";
|
|
18
18
|
import resume from "./commands/resume.js";
|
|
19
|
+
import newCmd from "./commands/new.js";
|
|
19
20
|
import agents from "./commands/agents/index.js";
|
|
20
21
|
import plugin from "./commands/plugin.js";
|
|
21
22
|
import undo from "./commands/undo.js";
|
|
23
|
+
import language from "./commands/language.js";
|
|
22
24
|
import { quit } from "./commands/quit.js";
|
|
25
|
+
import stats from "./commands/stats.js";
|
|
23
26
|
import doctor from "./commands/doctor.js";
|
|
24
27
|
import bug from "./commands/bug.js";
|
|
25
28
|
import tasks from "./commands/tasks.js";
|
|
@@ -43,14 +46,17 @@ const COMMANDS = memoize(() => [
|
|
|
43
46
|
exportCmd,
|
|
44
47
|
help,
|
|
45
48
|
init,
|
|
49
|
+
language,
|
|
46
50
|
mcp,
|
|
47
51
|
model,
|
|
52
|
+
newCmd,
|
|
48
53
|
permissions,
|
|
49
54
|
plugin,
|
|
50
55
|
quit,
|
|
51
56
|
resume,
|
|
52
57
|
sandbox,
|
|
53
58
|
setup,
|
|
59
|
+
stats,
|
|
54
60
|
status,
|
|
55
61
|
tasks,
|
|
56
62
|
todos,
|
package/dist/commands.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/commands.ts"],
|
|
4
|
-
"sourcesContent": ["import React from 'react'\nimport clear from './commands/clear'\nimport compact from './commands/compact'\nimport config from './commands/config'\nimport context from './commands/context'\nimport cost from './commands/cost'\nimport ctx_viz from './commands/ctx_viz'\nimport exportCmd from './commands/export'\nimport help from './commands/help'\nimport init from './commands/init'\nimport mcp from './commands/mcp-interactive'\nimport model from './commands/model'\nimport permissions from './commands/permissions'\nimport refreshCommands from './commands/refreshCommands'\nimport sandbox from './commands/sandbox'\nimport setup from './commands/setup'\nimport status from './commands/status'\nimport terminalSetup from './commands/terminalSetup'\nimport { Tool, ToolUseContext } from './Tool'\nimport resume from './commands/resume'\nimport agents from './commands/agents'\nimport plugin from './commands/plugin'\nimport undo from './commands/undo'\nimport { quit } from './commands/quit'\n// Phase 4: Diagnostic and task management commands\nimport doctor from './commands/doctor'\nimport bug from './commands/bug'\nimport tasks from './commands/tasks'\nimport todos from './commands/todos'\nimport { getMCPCommands } from './services/mcpClient'\nimport {\n loadCustomCommands,\n loadPluginCommands,\n} from './services/customCommands'\nimport type { MessageParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { memoize } from 'lodash-es'\nimport type { Message } from './query'\n\ntype PromptCommand = {\n type: 'prompt'\n progressMessage: string\n argNames?: string[]\n getPromptForCommand(args: string): Promise<MessageParam[]>\n}\n\ntype LocalCommand = {\n type: 'local'\n call(\n args: string,\n context: {\n options: {\n commands: Command[]\n tools: Tool[]\n slowAndCapableModel: string\n }\n abortController: AbortController\n setForkConvoWithMessagesOnTheNextRender: (\n forkConvoWithMessages: Message[],\n ) => void\n },\n ): Promise<string>\n}\n\ntype LocalJSXCommand = {\n type: 'local-jsx'\n call(\n onDone: (result?: string) => void,\n context: ToolUseContext & {\n setForkConvoWithMessagesOnTheNextRender: (\n forkConvoWithMessages: Message[],\n ) => void\n },\n ): Promise<React.ReactNode>\n}\n\nexport type Command = {\n description: string\n isEnabled: boolean\n isHidden: boolean\n name: string\n aliases?: string[]\n userFacingName(): string\n} & (PromptCommand | LocalCommand | LocalJSXCommand)\n\n// Hidden developer commands (not shown in /help but still accessible)\nconst HIDDEN_COMMANDS = [ctx_viz, refreshCommands, terminalSetup]\n\n// Declared as a function so that we don't run this until getCommands is called,\n// since underlying functions read from config, which can't be read at module initialization time\nconst COMMANDS = memoize((): Command[] => [\n agents,\n bug,\n clear,\n compact,\n config,\n context,\n cost,\n doctor,\n exportCmd,\n help,\n init,\n mcp,\n model,\n permissions,\n plugin,\n quit,\n resume,\n sandbox,\n setup,\n status,\n tasks,\n todos,\n undo,\n ...HIDDEN_COMMANDS,\n])\n\nexport const getCommands = memoize(async (): Promise<Command[]> => {\n const [mcpCommands, customCommands, pluginCommands] = await Promise.all([\n getMCPCommands(),\n loadCustomCommands(),\n loadPluginCommands(),\n ])\n\n // Get built-in commands\n const builtInCommands = COMMANDS()\n\n // Sort built-in commands alphabetically by name\n const sortedBuiltIn = [...builtInCommands].sort((a, b) =>\n a.userFacingName().localeCompare(b.userFacingName()),\n )\n\n // Sort MCP commands alphabetically by name\n const sortedMCP = [...mcpCommands].sort((a, b) =>\n a.userFacingName().localeCompare(b.userFacingName()),\n )\n\n // Sort plugin commands alphabetically by name\n const sortedPlugin = [...pluginCommands].sort((a, b) =>\n a.userFacingName().localeCompare(b.userFacingName()),\n )\n\n // Sort custom commands alphabetically by name\n const sortedCustom = [...customCommands].sort((a, b) =>\n a.userFacingName().localeCompare(b.userFacingName()),\n )\n\n // Command priority (later overrides earlier):\n // 1. MCP commands (lowest priority) - sorted alphabetically\n // 2. Plugin commands - sorted alphabetically\n // 3. Custom commands (user/project) - sorted alphabetically\n // 4. Built-in commands (highest priority) - sorted alphabetically\n // Display order: Built-in first, then custom/plugin/MCP\n return [\n ...sortedBuiltIn,\n ...sortedCustom,\n ...sortedPlugin,\n ...sortedMCP,\n ].filter(_ => _.isEnabled)\n})\n\nexport function hasCommand(commandName: string, commands: Command[]): boolean {\n return commands.some(\n _ => _.userFacingName() === commandName || _.aliases?.includes(commandName),\n )\n}\n\nexport function getCommand(commandName: string, commands: Command[]): Command {\n const command = commands.find(\n _ => _.userFacingName() === commandName || _.aliases?.includes(commandName),\n ) as Command | undefined\n if (!command) {\n throw ReferenceError(\n `Command ${commandName} not found. Available commands: ${commands\n .map(_ => {\n const name = _.userFacingName()\n return _.aliases ? `${name} (aliases: ${_.aliases.join(', ')})` : name\n })\n .join(', ')}`,\n )\n }\n\n return command\n}\n"],
|
|
5
|
-
"mappings": "AACA,OAAO,WAAW;AAClB,OAAO,aAAa;AACpB,OAAO,YAAY;AACnB,OAAO,aAAa;AACpB,OAAO,UAAU;AACjB,OAAO,aAAa;AACpB,OAAO,eAAe;AACtB,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,OAAO,iBAAiB;AACxB,OAAO,qBAAqB;AAC5B,OAAO,aAAa;AACpB,OAAO,WAAW;AAClB,OAAO,YAAY;AACnB,OAAO,mBAAmB;AAE1B,OAAO,YAAY;AACnB,OAAO,YAAY;AACnB,OAAO,YAAY;AACnB,OAAO,UAAU;AACjB,SAAS,YAAY;
|
|
4
|
+
"sourcesContent": ["import React from 'react'\nimport clear from './commands/clear'\nimport compact from './commands/compact'\nimport config from './commands/config'\nimport context from './commands/context'\nimport cost from './commands/cost'\nimport ctx_viz from './commands/ctx_viz'\nimport exportCmd from './commands/export'\nimport help from './commands/help'\nimport init from './commands/init'\nimport mcp from './commands/mcp-interactive'\nimport model from './commands/model'\nimport permissions from './commands/permissions'\nimport refreshCommands from './commands/refreshCommands'\nimport sandbox from './commands/sandbox'\nimport setup from './commands/setup'\nimport status from './commands/status'\nimport terminalSetup from './commands/terminalSetup'\nimport { Tool, ToolUseContext } from './Tool'\nimport resume from './commands/resume'\nimport newCmd from './commands/new'\nimport agents from './commands/agents'\nimport plugin from './commands/plugin'\nimport undo from './commands/undo'\nimport language from './commands/language'\nimport { quit } from './commands/quit'\nimport stats from './commands/stats'\n// Phase 4: Diagnostic and task management commands\nimport doctor from './commands/doctor'\nimport bug from './commands/bug'\nimport tasks from './commands/tasks'\nimport todos from './commands/todos'\nimport { getMCPCommands } from './services/mcpClient'\nimport {\n loadCustomCommands,\n loadPluginCommands,\n} from './services/customCommands'\nimport type { MessageParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { memoize } from 'lodash-es'\nimport type { Message } from './query'\n\ntype PromptCommand = {\n type: 'prompt'\n progressMessage: string\n argNames?: string[]\n getPromptForCommand(args: string): Promise<MessageParam[]>\n}\n\ntype LocalCommand = {\n type: 'local'\n call(\n args: string,\n context: {\n options: {\n commands: Command[]\n tools: Tool[]\n slowAndCapableModel: string\n }\n abortController: AbortController\n setForkConvoWithMessagesOnTheNextRender: (\n forkConvoWithMessages: Message[],\n ) => void\n },\n ): Promise<string>\n}\n\ntype LocalJSXCommand = {\n type: 'local-jsx'\n /** If true, the command UI will show below PromptInput instead of replacing it */\n showBelowPrompt?: boolean\n call(\n onDone: (result?: string) => void,\n context: ToolUseContext & {\n setForkConvoWithMessagesOnTheNextRender: (\n forkConvoWithMessages: Message[],\n ) => void\n /** Unmount current REPL (for commands that need to replace the entire UI) */\n unmount?: () => void\n },\n args?: string,\n ): Promise<React.ReactNode>\n}\n\nexport type Command = {\n description: string\n isEnabled: boolean\n isHidden: boolean\n name: string\n aliases?: string[]\n userFacingName(): string\n} & (PromptCommand | LocalCommand | LocalJSXCommand)\n\n// Hidden developer commands (not shown in /help but still accessible)\nconst HIDDEN_COMMANDS = [ctx_viz, refreshCommands, terminalSetup]\n\n// Declared as a function so that we don't run this until getCommands is called,\n// since underlying functions read from config, which can't be read at module initialization time\nconst COMMANDS = memoize((): Command[] => [\n agents,\n bug,\n clear,\n compact,\n config,\n context,\n cost,\n doctor,\n exportCmd,\n help,\n init,\n language,\n mcp,\n model,\n newCmd,\n permissions,\n plugin,\n quit,\n resume,\n sandbox,\n setup,\n stats,\n status,\n tasks,\n todos,\n undo,\n ...HIDDEN_COMMANDS,\n])\n\nexport const getCommands = memoize(async (): Promise<Command[]> => {\n const [mcpCommands, customCommands, pluginCommands] = await Promise.all([\n getMCPCommands(),\n loadCustomCommands(),\n loadPluginCommands(),\n ])\n\n // Get built-in commands\n const builtInCommands = COMMANDS()\n\n // Sort built-in commands alphabetically by name\n const sortedBuiltIn = [...builtInCommands].sort((a, b) =>\n a.userFacingName().localeCompare(b.userFacingName()),\n )\n\n // Sort MCP commands alphabetically by name\n const sortedMCP = [...mcpCommands].sort((a, b) =>\n a.userFacingName().localeCompare(b.userFacingName()),\n )\n\n // Sort plugin commands alphabetically by name\n const sortedPlugin = [...pluginCommands].sort((a, b) =>\n a.userFacingName().localeCompare(b.userFacingName()),\n )\n\n // Sort custom commands alphabetically by name\n const sortedCustom = [...customCommands].sort((a, b) =>\n a.userFacingName().localeCompare(b.userFacingName()),\n )\n\n // Command priority (later overrides earlier):\n // 1. MCP commands (lowest priority) - sorted alphabetically\n // 2. Plugin commands - sorted alphabetically\n // 3. Custom commands (user/project) - sorted alphabetically\n // 4. Built-in commands (highest priority) - sorted alphabetically\n // Display order: Built-in first, then custom/plugin/MCP\n return [\n ...sortedBuiltIn,\n ...sortedCustom,\n ...sortedPlugin,\n ...sortedMCP,\n ].filter(_ => _.isEnabled)\n})\n\nexport function hasCommand(commandName: string, commands: Command[]): boolean {\n return commands.some(\n _ => _.userFacingName() === commandName || _.aliases?.includes(commandName),\n )\n}\n\nexport function getCommand(commandName: string, commands: Command[]): Command {\n const command = commands.find(\n _ => _.userFacingName() === commandName || _.aliases?.includes(commandName),\n ) as Command | undefined\n if (!command) {\n throw ReferenceError(\n `Command ${commandName} not found. Available commands: ${commands\n .map(_ => {\n const name = _.userFacingName()\n return _.aliases ? `${name} (aliases: ${_.aliases.join(', ')})` : name\n })\n .join(', ')}`,\n )\n }\n\n return command\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -6,6 +6,7 @@ import { useNotifyAfterTimeout } from "../../hooks/useNotifyAfterTimeout.js";
|
|
|
6
6
|
import { PRODUCT_NAME } from "../../constants/product.js";
|
|
7
7
|
import TextInput from "ink-text-input";
|
|
8
8
|
import { BRAND_GRADIENT, SEMANTIC_COLORS } from "../../constants/colors.js";
|
|
9
|
+
import { t } from "../../i18n/index.js";
|
|
9
10
|
function AskUserQuestionDialog({
|
|
10
11
|
context,
|
|
11
12
|
onDone
|
|
@@ -114,14 +115,14 @@ function AskUserQuestionDialog({
|
|
|
114
115
|
resolve
|
|
115
116
|
]
|
|
116
117
|
);
|
|
117
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingY: 1 }, isCustomInputMode ? /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingX: 2 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "
|
|
118
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingY: 1 }, isCustomInputMode ? /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingX: 2 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, t("dialog.enterCustomResponse"))), /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: BRAND_GRADIENT.START }, "> "), /* @__PURE__ */ React.createElement(
|
|
118
119
|
TextInput,
|
|
119
120
|
{
|
|
120
121
|
value: customInput,
|
|
121
122
|
onChange: setCustomInput,
|
|
122
123
|
onSubmit: handleConfirm
|
|
123
124
|
}
|
|
124
|
-
)), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "
|
|
125
|
+
)), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, t("dialog.confirmCancelPrompt")))) : (
|
|
125
126
|
/* Normal question view */
|
|
126
127
|
/* @__PURE__ */ React.createElement(
|
|
127
128
|
QuestionView,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/AskUserQuestionDialog/AskUserQuestionDialog.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Box, Text, useInput } from 'ink'\nimport * as React from 'react'\nimport { useState, useCallback } from 'react'\nimport {\n type AskUserQuestionContext,\n type UserAnswer,\n} from '@minto-types/askUserQuestion'\nimport { QuestionView } from './QuestionView'\nimport { useNotifyAfterTimeout } from '@hooks/useNotifyAfterTimeout'\nimport { PRODUCT_NAME } from '@constants/product'\nimport TextInput from 'ink-text-input'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\n\ninterface AskUserQuestionDialogProps {\n context: AskUserQuestionContext\n onDone: () => void\n}\n\nexport function AskUserQuestionDialog({\n context,\n onDone,\n}: AskUserQuestionDialogProps): React.ReactElement {\n const { questions, resolve, reject } = context\n\n // Current question index (for multi-question scenarios)\n const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0)\n\n // Selected option indices for each question\n const [selectedOptions, setSelectedOptions] = useState<number[]>(\n questions.map(() => 0),\n )\n\n // Custom input mode for \"Other\" option\n const [isCustomInputMode, setIsCustomInputMode] = useState(false)\n const [customInput, setCustomInput] = useState('')\n\n // Notification after timeout\n useNotifyAfterTimeout(`${PRODUCT_NAME} needs your input to continue`)\n\n const currentQuestion = questions[currentQuestionIndex]!\n\n // Handle keyboard input\n useInput(\n (input, key) => {\n // Cancel with Escape or Ctrl+C\n if (key.escape || (key.ctrl && input === 'c')) {\n onDone()\n reject()\n return\n }\n\n // Don't handle other keys in custom input mode\n if (isCustomInputMode) {\n return\n }\n\n // Navigate between questions with left/right arrows\n if (key.leftArrow && currentQuestionIndex > 0) {\n setCurrentQuestionIndex(prev => prev - 1)\n return\n }\n\n if (key.rightArrow && currentQuestionIndex < questions.length - 1) {\n setCurrentQuestionIndex(prev => prev + 1)\n return\n }\n\n // Confirm with Enter\n if (key.return) {\n handleConfirm()\n return\n }\n\n // Press 'o' to enter custom input mode\n if (input === 'o' && currentQuestion.options) {\n setIsCustomInputMode(true)\n setCustomInput('')\n return\n }\n },\n { isActive: true },\n )\n\n // Handle option change\n const handleOptionChange = useCallback(\n (optionIndex: number) => {\n const newSelectedOptions = [...selectedOptions]\n newSelectedOptions[currentQuestionIndex] = optionIndex\n setSelectedOptions(newSelectedOptions)\n\n // If \"Other\" is selected (-1), enter custom input mode\n if (optionIndex === -1) {\n setIsCustomInputMode(true)\n setCustomInput('')\n }\n },\n [selectedOptions, currentQuestionIndex],\n )\n\n // Handle confirmation\n const handleConfirm = useCallback(() => {\n // If in custom input mode, save the custom input\n if (isCustomInputMode) {\n if (!customInput.trim()) {\n // Don't allow empty custom input\n return\n }\n\n // Save custom input and exit custom input mode\n setIsCustomInputMode(false)\n\n // If this is the last question, submit all answers\n if (currentQuestionIndex === questions.length - 1) {\n submitAnswers(customInput)\n } else {\n // Move to next question\n setCurrentQuestionIndex(prev => prev + 1)\n }\n return\n }\n\n // If this is the last question, submit all answers\n if (currentQuestionIndex === questions.length - 1) {\n submitAnswers()\n } else {\n // Move to next question\n setCurrentQuestionIndex(prev => prev + 1)\n }\n }, [isCustomInputMode, customInput, currentQuestionIndex, questions.length])\n\n // Submit all answers\n const submitAnswers = useCallback(\n (customInputOverride?: string) => {\n const answers: UserAnswer[] = questions.map((question, index) => {\n const selectedOptionIndex = selectedOptions[index]!\n\n // If it's the current question and we have custom input\n if (\n index === currentQuestionIndex &&\n (customInputOverride || isCustomInputMode)\n ) {\n return {\n questionIndex: index,\n customInput: customInputOverride || customInput,\n }\n }\n\n // If \"Other\" was selected (-1), use custom input\n if (selectedOptionIndex === -1) {\n return {\n questionIndex: index,\n customInput: customInput,\n }\n }\n\n // Regular option selection\n return {\n questionIndex: index,\n selectedOptions: [selectedOptionIndex],\n }\n })\n\n onDone()\n resolve(answers)\n },\n [\n questions,\n selectedOptions,\n currentQuestionIndex,\n isCustomInputMode,\n customInput,\n onDone,\n resolve,\n ],\n )\n\n return (\n <Box flexDirection=\"column\" paddingY={1}>\n {/* Custom input mode */}\n {isCustomInputMode ? (\n <Box flexDirection=\"column\" paddingX={2}>\n <Box marginBottom={1}>\n <Text bold>
|
|
5
|
-
"mappings": "AAAA,SAAS,KAAK,MAAM,gBAAgB;AACpC,YAAY,WAAW;AACvB,SAAS,UAAU,mBAAmB;AAKtC,SAAS,oBAAoB;AAC7B,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAC7B,OAAO,eAAe;AACtB,SAAS,gBAAgB,uBAAuB;
|
|
4
|
+
"sourcesContent": ["import { Box, Text, useInput } from 'ink'\nimport * as React from 'react'\nimport { useState, useCallback } from 'react'\nimport {\n type AskUserQuestionContext,\n type UserAnswer,\n} from '@minto-types/askUserQuestion'\nimport { QuestionView } from './QuestionView'\nimport { useNotifyAfterTimeout } from '@hooks/useNotifyAfterTimeout'\nimport { PRODUCT_NAME } from '@constants/product'\nimport TextInput from 'ink-text-input'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\nimport { t } from '@i18n'\n\ninterface AskUserQuestionDialogProps {\n context: AskUserQuestionContext\n onDone: () => void\n}\n\nexport function AskUserQuestionDialog({\n context,\n onDone,\n}: AskUserQuestionDialogProps): React.ReactElement {\n const { questions, resolve, reject } = context\n\n // Current question index (for multi-question scenarios)\n const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0)\n\n // Selected option indices for each question\n const [selectedOptions, setSelectedOptions] = useState<number[]>(\n questions.map(() => 0),\n )\n\n // Custom input mode for \"Other\" option\n const [isCustomInputMode, setIsCustomInputMode] = useState(false)\n const [customInput, setCustomInput] = useState('')\n\n // Notification after timeout\n useNotifyAfterTimeout(`${PRODUCT_NAME} needs your input to continue`)\n\n const currentQuestion = questions[currentQuestionIndex]!\n\n // Handle keyboard input\n useInput(\n (input, key) => {\n // Cancel with Escape or Ctrl+C\n if (key.escape || (key.ctrl && input === 'c')) {\n onDone()\n reject()\n return\n }\n\n // Don't handle other keys in custom input mode\n if (isCustomInputMode) {\n return\n }\n\n // Navigate between questions with left/right arrows\n if (key.leftArrow && currentQuestionIndex > 0) {\n setCurrentQuestionIndex(prev => prev - 1)\n return\n }\n\n if (key.rightArrow && currentQuestionIndex < questions.length - 1) {\n setCurrentQuestionIndex(prev => prev + 1)\n return\n }\n\n // Confirm with Enter\n if (key.return) {\n handleConfirm()\n return\n }\n\n // Press 'o' to enter custom input mode\n if (input === 'o' && currentQuestion.options) {\n setIsCustomInputMode(true)\n setCustomInput('')\n return\n }\n },\n { isActive: true },\n )\n\n // Handle option change\n const handleOptionChange = useCallback(\n (optionIndex: number) => {\n const newSelectedOptions = [...selectedOptions]\n newSelectedOptions[currentQuestionIndex] = optionIndex\n setSelectedOptions(newSelectedOptions)\n\n // If \"Other\" is selected (-1), enter custom input mode\n if (optionIndex === -1) {\n setIsCustomInputMode(true)\n setCustomInput('')\n }\n },\n [selectedOptions, currentQuestionIndex],\n )\n\n // Handle confirmation\n const handleConfirm = useCallback(() => {\n // If in custom input mode, save the custom input\n if (isCustomInputMode) {\n if (!customInput.trim()) {\n // Don't allow empty custom input\n return\n }\n\n // Save custom input and exit custom input mode\n setIsCustomInputMode(false)\n\n // If this is the last question, submit all answers\n if (currentQuestionIndex === questions.length - 1) {\n submitAnswers(customInput)\n } else {\n // Move to next question\n setCurrentQuestionIndex(prev => prev + 1)\n }\n return\n }\n\n // If this is the last question, submit all answers\n if (currentQuestionIndex === questions.length - 1) {\n submitAnswers()\n } else {\n // Move to next question\n setCurrentQuestionIndex(prev => prev + 1)\n }\n }, [isCustomInputMode, customInput, currentQuestionIndex, questions.length])\n\n // Submit all answers\n const submitAnswers = useCallback(\n (customInputOverride?: string) => {\n const answers: UserAnswer[] = questions.map((question, index) => {\n const selectedOptionIndex = selectedOptions[index]!\n\n // If it's the current question and we have custom input\n if (\n index === currentQuestionIndex &&\n (customInputOverride || isCustomInputMode)\n ) {\n return {\n questionIndex: index,\n customInput: customInputOverride || customInput,\n }\n }\n\n // If \"Other\" was selected (-1), use custom input\n if (selectedOptionIndex === -1) {\n return {\n questionIndex: index,\n customInput: customInput,\n }\n }\n\n // Regular option selection\n return {\n questionIndex: index,\n selectedOptions: [selectedOptionIndex],\n }\n })\n\n onDone()\n resolve(answers)\n },\n [\n questions,\n selectedOptions,\n currentQuestionIndex,\n isCustomInputMode,\n customInput,\n onDone,\n resolve,\n ],\n )\n\n return (\n <Box flexDirection=\"column\" paddingY={1}>\n {/* Custom input mode */}\n {isCustomInputMode ? (\n <Box flexDirection=\"column\" paddingX={2}>\n <Box marginBottom={1}>\n <Text bold>{t('dialog.enterCustomResponse')}</Text>\n </Box>\n <Box marginBottom={1}>\n <Text color={BRAND_GRADIENT.START}>> </Text>\n <TextInput\n value={customInput}\n onChange={setCustomInput}\n onSubmit={handleConfirm}\n />\n </Box>\n <Box>\n <Text color={SEMANTIC_COLORS.dim}>\n {t('dialog.confirmCancelPrompt')}\n </Text>\n </Box>\n </Box>\n ) : (\n /* Normal question view */\n <QuestionView\n question={currentQuestion}\n questionIndex={currentQuestionIndex}\n totalQuestions={questions.length}\n selectedOptionIndex={selectedOptions[currentQuestionIndex]!}\n onOptionChange={handleOptionChange}\n />\n )}\n </Box>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|