mistagent 0.1.18 → 0.1.20
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/dist/src/components/App.d.ts +2 -0
- package/dist/src/components/App.js +23 -5
- package/dist/src/components/Composer.js +2 -1
- package/dist/src/components/Header.js +74 -166
- package/dist/src/components/MainContent.js +3 -14
- package/dist/src/components/shared/MarkdownRenderer.js +15 -26
- package/dist/src/components/shared/TextInput.js +62 -3
- package/dist/src/contexts/ChatContext.d.ts +2 -0
- package/dist/src/contexts/ChatContext.js +1 -1
- package/dist/src/hooks/useChat.js +71 -10
- package/dist/src/main.js +62 -4
- package/dist/src/types/api.d.ts +4 -0
- package/dist/src/utils/config.d.ts +2 -0
- package/dist/src/utils/config.js +23 -0
- package/dist/src/utils/constants.d.ts +1 -1
- package/dist/src/utils/constants.js +1 -1
- package/dist/src/utils/fileTunnel.d.ts +7 -2
- package/dist/src/utils/fileTunnel.js +376 -5
- package/dist/src/utils/markdown.d.ts +10 -0
- package/dist/src/utils/markdown.js +223 -0
- package/dist/src/utils/updateChecker.js +10 -4
- package/package.json +3 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/src/api/auth.d.ts.map +0 -1
- package/dist/src/api/auth.js.map +0 -1
- package/dist/src/api/chat.d.ts.map +0 -1
- package/dist/src/api/chat.js.map +0 -1
- package/dist/src/api/client.d.ts.map +0 -1
- package/dist/src/api/client.js.map +0 -1
- package/dist/src/api/models.d.ts.map +0 -1
- package/dist/src/api/models.js.map +0 -1
- package/dist/src/api/sessions.d.ts.map +0 -1
- package/dist/src/api/sessions.js.map +0 -1
- package/dist/src/api/skills.d.ts.map +0 -1
- package/dist/src/api/skills.js.map +0 -1
- package/dist/src/api/tools.d.ts.map +0 -1
- package/dist/src/api/tools.js.map +0 -1
- package/dist/src/api/tunnel.d.ts.map +0 -1
- package/dist/src/api/tunnel.js.map +0 -1
- package/dist/src/components/App.d.ts.map +0 -1
- package/dist/src/components/App.js.map +0 -1
- package/dist/src/components/AppLayout.d.ts.map +0 -1
- package/dist/src/components/AppLayout.js.map +0 -1
- package/dist/src/components/Composer.d.ts.map +0 -1
- package/dist/src/components/Composer.js.map +0 -1
- package/dist/src/components/Footer.d.ts.map +0 -1
- package/dist/src/components/Footer.js.map +0 -1
- package/dist/src/components/Header.d.ts.map +0 -1
- package/dist/src/components/Header.js.map +0 -1
- package/dist/src/components/HistoryItemDisplay.d.ts.map +0 -1
- package/dist/src/components/HistoryItemDisplay.js.map +0 -1
- package/dist/src/components/InputPrompt.d.ts.map +0 -1
- package/dist/src/components/InputPrompt.js.map +0 -1
- package/dist/src/components/LoadingIndicator.d.ts.map +0 -1
- package/dist/src/components/LoadingIndicator.js.map +0 -1
- package/dist/src/components/LoginPrompt.d.ts.map +0 -1
- package/dist/src/components/LoginPrompt.js.map +0 -1
- package/dist/src/components/MainContent.d.ts.map +0 -1
- package/dist/src/components/MainContent.js.map +0 -1
- package/dist/src/components/ModelPicker.d.ts.map +0 -1
- package/dist/src/components/ModelPicker.js.map +0 -1
- package/dist/src/components/SessionPicker.d.ts.map +0 -1
- package/dist/src/components/SessionPicker.js.map +0 -1
- package/dist/src/components/SuggestionsDisplay.d.ts.map +0 -1
- package/dist/src/components/SuggestionsDisplay.js.map +0 -1
- package/dist/src/components/ThemePicker.d.ts.map +0 -1
- package/dist/src/components/ThemePicker.js.map +0 -1
- package/dist/src/components/messages/AssistantMessage.d.ts.map +0 -1
- package/dist/src/components/messages/AssistantMessage.js.map +0 -1
- package/dist/src/components/messages/CommandResult.d.ts.map +0 -1
- package/dist/src/components/messages/CommandResult.js.map +0 -1
- package/dist/src/components/messages/ErrorMessage.d.ts.map +0 -1
- package/dist/src/components/messages/ErrorMessage.js.map +0 -1
- package/dist/src/components/messages/InfoMessage.d.ts.map +0 -1
- package/dist/src/components/messages/InfoMessage.js.map +0 -1
- package/dist/src/components/messages/ModelMessage.d.ts.map +0 -1
- package/dist/src/components/messages/ModelMessage.js.map +0 -1
- package/dist/src/components/messages/SessionMessage.d.ts.map +0 -1
- package/dist/src/components/messages/SessionMessage.js.map +0 -1
- package/dist/src/components/messages/ToolCallMessage.d.ts.map +0 -1
- package/dist/src/components/messages/ToolCallMessage.js.map +0 -1
- package/dist/src/components/messages/UserMessage.d.ts.map +0 -1
- package/dist/src/components/messages/UserMessage.js.map +0 -1
- package/dist/src/components/shared/HorizontalLine.d.ts.map +0 -1
- package/dist/src/components/shared/HorizontalLine.js.map +0 -1
- package/dist/src/components/shared/MarkdownRenderer.d.ts.map +0 -1
- package/dist/src/components/shared/MarkdownRenderer.js.map +0 -1
- package/dist/src/components/shared/Spinner.d.ts.map +0 -1
- package/dist/src/components/shared/Spinner.js.map +0 -1
- package/dist/src/components/shared/TextInput.d.ts.map +0 -1
- package/dist/src/components/shared/TextInput.js.map +0 -1
- package/dist/src/contexts/AppContext.d.ts.map +0 -1
- package/dist/src/contexts/AppContext.js.map +0 -1
- package/dist/src/contexts/ChatContext.d.ts.map +0 -1
- package/dist/src/contexts/ChatContext.js.map +0 -1
- package/dist/src/contexts/KeypressContext.d.ts.map +0 -1
- package/dist/src/contexts/KeypressContext.js.map +0 -1
- package/dist/src/contexts/ModelContext.d.ts.map +0 -1
- package/dist/src/contexts/ModelContext.js.map +0 -1
- package/dist/src/contexts/SessionContext.d.ts.map +0 -1
- package/dist/src/contexts/SessionContext.js.map +0 -1
- package/dist/src/contexts/UIContext.d.ts.map +0 -1
- package/dist/src/contexts/UIContext.js.map +0 -1
- package/dist/src/hooks/useChat.d.ts.map +0 -1
- package/dist/src/hooks/useChat.js.map +0 -1
- package/dist/src/hooks/useFileCompletion.d.ts.map +0 -1
- package/dist/src/hooks/useFileCompletion.js.map +0 -1
- package/dist/src/hooks/useInputHistory.d.ts.map +0 -1
- package/dist/src/hooks/useInputHistory.js.map +0 -1
- package/dist/src/hooks/useKeypress.d.ts.map +0 -1
- package/dist/src/hooks/useKeypress.js.map +0 -1
- package/dist/src/hooks/useLoadingIndicator.d.ts.map +0 -1
- package/dist/src/hooks/useLoadingIndicator.js.map +0 -1
- package/dist/src/hooks/usePasteBuffer.d.ts.map +0 -1
- package/dist/src/hooks/usePasteBuffer.js.map +0 -1
- package/dist/src/hooks/useSlashCommand.d.ts.map +0 -1
- package/dist/src/hooks/useSlashCommand.js.map +0 -1
- package/dist/src/hooks/useStdinInterceptor.d.ts.map +0 -1
- package/dist/src/hooks/useStdinInterceptor.js.map +0 -1
- package/dist/src/hooks/useSymbolCompletion.d.ts.map +0 -1
- package/dist/src/hooks/useSymbolCompletion.js.map +0 -1
- package/dist/src/hooks/useTextBuffer.d.ts.map +0 -1
- package/dist/src/hooks/useTextBuffer.js.map +0 -1
- package/dist/src/main.d.ts.map +0 -1
- package/dist/src/main.js.map +0 -1
- package/dist/src/tools/code-analyzer/config/ignore-service.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/config/ignore-service.js.map +0 -1
- package/dist/src/tools/code-analyzer/config/supported-languages.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/config/supported-languages.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/graph/graph.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/graph/graph.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/graph/types.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/graph/types.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/ast-cache.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/ast-cache.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/call-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/call-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/community-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/community-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/entry-point-scoring.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/entry-point-scoring.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/filesystem-walker.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/filesystem-walker.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/framework-detection.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/framework-detection.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/heritage-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/heritage-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/import-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/import-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/parsing-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/parsing-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/pipeline.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/pipeline.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/process-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/process-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/structure-processor.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/structure-processor.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/symbol-table.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/symbol-table.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/tree-sitter-queries.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/tree-sitter-queries.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/utils.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/utils.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/workers/parse-worker.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/workers/parse-worker.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/workers/worker-pool.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/ingestion/workers/worker-pool.js.map +0 -1
- package/dist/src/tools/code-analyzer/core/tree-sitter/parser-loader.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/core/tree-sitter/parser-loader.js.map +0 -1
- package/dist/src/tools/code-analyzer/index.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/index.js.map +0 -1
- package/dist/src/tools/code-analyzer/lib/utils.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/lib/utils.js.map +0 -1
- package/dist/src/tools/code-analyzer/types/pipeline.d.ts.map +0 -1
- package/dist/src/tools/code-analyzer/types/pipeline.js.map +0 -1
- package/dist/src/types/api.d.ts.map +0 -1
- package/dist/src/types/api.js.map +0 -1
- package/dist/src/types/history.d.ts.map +0 -1
- package/dist/src/types/history.js.map +0 -1
- package/dist/src/utils/colors.d.ts.map +0 -1
- package/dist/src/utils/colors.js.map +0 -1
- package/dist/src/utils/config.d.ts.map +0 -1
- package/dist/src/utils/config.js.map +0 -1
- package/dist/src/utils/constants.d.ts.map +0 -1
- package/dist/src/utils/constants.js.map +0 -1
- package/dist/src/utils/fileRef.d.ts.map +0 -1
- package/dist/src/utils/fileRef.js.map +0 -1
- package/dist/src/utils/fileTunnel.d.ts.map +0 -1
- package/dist/src/utils/fileTunnel.js.map +0 -1
- package/dist/src/utils/formatters.d.ts.map +0 -1
- package/dist/src/utils/formatters.js.map +0 -1
- package/dist/src/utils/pasteUtils.d.ts.map +0 -1
- package/dist/src/utils/pasteUtils.js.map +0 -1
- package/dist/src/utils/skillScanner.d.ts.map +0 -1
- package/dist/src/utils/skillScanner.js.map +0 -1
- package/dist/src/utils/textUtils.d.ts.map +0 -1
- package/dist/src/utils/textUtils.js.map +0 -1
- package/dist/src/utils/updateChecker.d.ts.map +0 -1
- package/dist/src/utils/updateChecker.js.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useCallback } from 'react';
|
|
2
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
3
|
+
import { useStdout } from 'ink';
|
|
3
4
|
import { AppStateContext, AppActionsContext, } from '../contexts/AppContext.js';
|
|
4
5
|
import { ChatStateContext, ChatDispatchContext, useChatReducer, } from '../contexts/ChatContext.js';
|
|
5
6
|
import { UIStateContext, UIActionsContext, } from '../contexts/UIContext.js';
|
|
@@ -30,15 +31,32 @@ const AppInner = ({ onLogin, loginError, isAuthenticated, authEnabled }) => {
|
|
|
30
31
|
}
|
|
31
32
|
return _jsx(AppLayout, { onSubmit: handleSubmit, onCancel: cancelStream, onConfirmPlan: confirmPlan, onResolveToolApproval: resolveToolApproval });
|
|
32
33
|
};
|
|
33
|
-
export const App = ({ serverUrl, token, username, authEnabled, version, healthData, availableCommands, availableTools, initialModels, initialCurrentModel, initialSessions, onLogin, onLogout, loginError, isAuthenticated, initialTheme, }) => {
|
|
34
|
+
export const App = ({ serverUrl, token, username, authEnabled, version, healthData, availableCommands, availableTools, initialModels, initialCurrentModel, initialSessions, onLogin, onLogout, loginError, isAuthenticated, initialTheme, terminalWidth: initialTerminalWidth, terminalHeight: initialTerminalHeight, }) => {
|
|
35
|
+
const { stdout } = useStdout();
|
|
34
36
|
const [appToken, setAppToken] = useState(token);
|
|
35
37
|
const [appUsername, setAppUsername] = useState(username);
|
|
36
38
|
const [chatState, chatDispatch] = useChatReducer();
|
|
37
|
-
// Terminal size
|
|
38
|
-
const [terminalWidth] = useState(process.stdout.columns || 80);
|
|
39
|
-
const [terminalHeight] = useState(process.stdout.rows || 24);
|
|
40
39
|
const [activeDialog, setActiveDialog] = useState('none');
|
|
41
40
|
const [theme, setThemeState] = useState(initialTheme);
|
|
41
|
+
// Track terminal size — updated by both:
|
|
42
|
+
// 1. stdout 'resize' events (primary: fired by the OS → Node TTY → Ink's stdout)
|
|
43
|
+
// 2. A polling fallback every 200ms (catches any missed events)
|
|
44
|
+
const [terminalWidth, setTerminalWidth] = useState(() => stdout.columns || process.stdout.columns || initialTerminalWidth);
|
|
45
|
+
const [terminalHeight, setTerminalHeight] = useState(() => stdout.rows || process.stdout.rows || initialTerminalHeight);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const readSize = () => {
|
|
48
|
+
setTerminalWidth(stdout.columns || process.stdout.columns || 80);
|
|
49
|
+
setTerminalHeight(stdout.rows || process.stdout.rows || 24);
|
|
50
|
+
};
|
|
51
|
+
stdout.on('resize', readSize);
|
|
52
|
+
process.stdout.on('resize', readSize);
|
|
53
|
+
const poll = setInterval(readSize, 200);
|
|
54
|
+
return () => {
|
|
55
|
+
stdout.off('resize', readSize);
|
|
56
|
+
process.stdout.off('resize', readSize);
|
|
57
|
+
clearInterval(poll);
|
|
58
|
+
};
|
|
59
|
+
}, [stdout]);
|
|
42
60
|
const appState = {
|
|
43
61
|
serverUrl,
|
|
44
62
|
token: appToken,
|
|
@@ -13,6 +13,7 @@ import { useInputHistory } from '../hooks/useInputHistory.js';
|
|
|
13
13
|
import { useFileCompletion } from '../hooks/useFileCompletion.js';
|
|
14
14
|
import { useSymbolCompletion } from '../hooks/useSymbolCompletion.js';
|
|
15
15
|
import { useTextBuffer } from '../hooks/useTextBuffer.js';
|
|
16
|
+
import { MarkdownRenderer } from './shared/MarkdownRenderer.js';
|
|
16
17
|
import { useKeypress, KeypressPriority } from '../hooks/useKeypress.js';
|
|
17
18
|
import { palette } from '../utils/colors.js';
|
|
18
19
|
const EXIT_WARNING_DURATION_MS = 2000;
|
|
@@ -405,7 +406,7 @@ export const Composer = ({ onSubmit, onCancel, onConfirmPlan, onResolveToolAppro
|
|
|
405
406
|
const full = activeToolCall.name + (activeToolCall.args ? `(${activeToolCall.args})` : '');
|
|
406
407
|
const maxLen = Math.max(40, terminalWidth - 10);
|
|
407
408
|
return full.length > maxLen ? full.slice(0, maxLen) + '...' : full;
|
|
408
|
-
})() })] }))] })), hasPendingPlan && (_jsxs(Box, { flexDirection: "column", paddingX: 1, marginBottom: 0, children: [_jsxs(Box, { borderStyle: "round", borderColor: palette.warning, flexDirection: "column", paddingX: 1, paddingY: 0, children: [
|
|
409
|
+
})() })] }))] })), hasPendingPlan && (_jsxs(Box, { flexDirection: "column", paddingX: 1, marginBottom: 0, children: [_jsxs(Box, { borderStyle: "round", borderColor: palette.warning, flexDirection: "column", paddingX: 1, paddingY: 0, children: [_jsxs(Text, { color: palette.warning, bold: true, children: ["\uD83D\uDCCB Plan Review (", pendingPlan.steps.length, " steps)"] }), _jsx(Text, { color: palette.textDim, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), pendingPlan.content ? (_jsx(MarkdownRenderer, { text: pendingPlan.content })) : (pendingPlan.steps.map((step, i) => (_jsxs(Text, { color: palette.text, children: [_jsx(Text, { color: palette.accent, bold: true, children: ` ${i + 1}. ` }), step] }, i)))), _jsx(Text, { color: palette.textDim, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), planEditMode ? (_jsxs(Text, { children: [_jsx(Text, { color: palette.warning, bold: true, children: "\u270F\uFE0F Type feedback, " }), _jsx(Text, { color: palette.success, bold: true, children: "Enter" }), _jsx(Text, { color: palette.textDim, children: " to revise " }), _jsx(Text, { color: palette.error, bold: true, children: "Esc" }), _jsx(Text, { color: palette.textDim, children: " to cancel" })] })) : (_jsxs(Text, { children: [_jsx(Text, { color: palette.success, bold: true, children: "Enter" }), _jsx(Text, { color: palette.textDim, children: " approve " }), _jsx(Text, { color: palette.warning, bold: true, children: "Tab" }), _jsx(Text, { color: palette.textDim, children: " edit " }), _jsx(Text, { color: palette.error, bold: true, children: "Esc" }), _jsx(Text, { color: palette.textDim, children: " reject" })] }))] }), planEditMode && (_jsx(InputPrompt, { buffer: buffer, isActive: true, borderColor: palette.warning, width: terminalWidth - 2 }))] })), hasPendingToolApproval && pendingToolApproval && (_jsx(Box, { flexDirection: "column", paddingX: 1, marginBottom: 0, children: _jsxs(Box, { borderStyle: "round", borderColor: palette.warning, flexDirection: "column", paddingX: 1, paddingY: 0, children: [_jsx(Text, { color: palette.warning, bold: true, children: "\uD83D\uDD12 \u64CD\u4F5C\u786E\u8BA4" }), _jsx(Text, { color: palette.textDim, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsxs(Text, { color: palette.text, children: [_jsx(Text, { color: palette.accent, bold: true, children: "\u5DE5\u5177: " }), pendingToolApproval.request.tool_name] }), Object.entries(pendingToolApproval.request.args).map(([k, v]) => {
|
|
409
410
|
const str = String(v);
|
|
410
411
|
const truncated = !toolDetailExpanded && str.length > 80;
|
|
411
412
|
return (_jsxs(Text, { color: palette.text, wrap: toolDetailExpanded ? 'wrap' : 'truncate', children: [_jsxs(Text, { color: palette.textDim, children: [" ", k, ": "] }), truncated ? str.slice(0, 80) + '...' : str] }, k));
|
|
@@ -8,12 +8,7 @@ import { useSessionState } from '../contexts/SessionContext.js';
|
|
|
8
8
|
import { useChatState } from '../contexts/ChatContext.js';
|
|
9
9
|
import { useAppState } from '../contexts/AppContext.js';
|
|
10
10
|
import { MASCOT_LINES, VERSION, TIPS, getMascotGradient } from '../utils/constants.js';
|
|
11
|
-
// ── Catppuccin tokens (matching pencil variables) ──
|
|
12
11
|
const C = {
|
|
13
|
-
bg: '#1E1E2E',
|
|
14
|
-
bgDark: '#181825',
|
|
15
|
-
bgDeep: '#11111B',
|
|
16
|
-
surface1: '#313244',
|
|
17
12
|
border: '#585B70',
|
|
18
13
|
dimtext: '#6C7086',
|
|
19
14
|
subtext: '#A6ADC8',
|
|
@@ -24,185 +19,98 @@ const C = {
|
|
|
24
19
|
teal: '#94E2D5',
|
|
25
20
|
green: '#A6E3A1',
|
|
26
21
|
red: '#F38BA8',
|
|
27
|
-
peach: '#FAB387',
|
|
28
|
-
rose: '#E05A4E',
|
|
29
22
|
yellow: '#F9E2AF',
|
|
30
23
|
};
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// ── Helper functions ──
|
|
40
|
-
const pad = (s, width) => {
|
|
41
|
-
const len = stringWidth(s);
|
|
42
|
-
if (len >= width)
|
|
43
|
-
return s;
|
|
44
|
-
return s + ' '.repeat(width - len);
|
|
45
|
-
};
|
|
46
|
-
const centerPad = (s, width) => {
|
|
47
|
-
const len = stringWidth(s);
|
|
48
|
-
if (len >= width)
|
|
49
|
-
return s;
|
|
50
|
-
const left = Math.floor((width - len) / 2);
|
|
51
|
-
const right = width - len - left;
|
|
52
|
-
return ' '.repeat(left) + s + ' '.repeat(right);
|
|
53
|
-
};
|
|
54
|
-
const truncate = (s, maxLen) => {
|
|
55
|
-
if (maxLen <= 0)
|
|
24
|
+
// Three-tier breakpoints
|
|
25
|
+
// col1(mascot)=35 + div=1 + col2(stats)=26 + div=1 + col3(min~10) + border+pad~6 = ~79 minimum
|
|
26
|
+
// Use 90 as safe threshold so the 3-column layout never gets squeezed
|
|
27
|
+
const CONDENSED_MAX = 40; // <40 → single line
|
|
28
|
+
const COMPACT_MAX = 90; // <90 → stacked compact
|
|
29
|
+
// ≥90 → full 3-column
|
|
30
|
+
function truncate(s, max) {
|
|
31
|
+
if (max <= 0)
|
|
56
32
|
return '';
|
|
57
|
-
if (stringWidth(s) <=
|
|
33
|
+
if (stringWidth(s) <= max)
|
|
58
34
|
return s;
|
|
59
|
-
let
|
|
35
|
+
let out = '';
|
|
60
36
|
for (const ch of s) {
|
|
61
|
-
if (stringWidth(
|
|
37
|
+
if (stringWidth(out + ch) > max - 1)
|
|
62
38
|
break;
|
|
63
|
-
|
|
39
|
+
out += ch;
|
|
64
40
|
}
|
|
65
|
-
return
|
|
41
|
+
return out + '…';
|
|
42
|
+
}
|
|
43
|
+
// ─────────────────────────────────────────────
|
|
44
|
+
// Tier 1 — Condensed (<40 cols)
|
|
45
|
+
// Single row: ❯ MIST v0.x model ●
|
|
46
|
+
// ─────────────────────────────────────────────
|
|
47
|
+
const CondensedHeader = ({ modelName, isConnected, version }) => (_jsxs(Box, { flexDirection: "row", gap: 1, marginTop: 1, children: [_jsx(Text, { color: C.blue, bold: true, children: '❯' }), _jsx(Text, { color: C.text, bold: true, children: 'MIST' }), _jsx(Text, { color: C.dimtext, children: `v${version}` }), _jsx(Text, { color: C.sapphire, children: modelName }), _jsx(Text, { color: isConnected ? C.green : C.red, children: '●' })] }));
|
|
48
|
+
// ─────────────────────────────────────────────
|
|
49
|
+
// Tier 2 — Compact (40–69 cols)
|
|
50
|
+
// Stacked: brand + model + status + shortcuts
|
|
51
|
+
// ─────────────────────────────────────────────
|
|
52
|
+
const CompactHeader = ({ modelName, isConnected, modeLabel, theme, version }) => {
|
|
53
|
+
const themeIcon = theme === 'dark' ? '◐' : '◑';
|
|
54
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: C.border, paddingX: 1, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsx(Text, { color: C.blue, bold: true, children: `❯ MIST v${version}` }), _jsx(Text, { color: isConnected ? C.green : C.red, children: isConnected ? '● ON' : '○ OFF' })] }), _jsx(Text, { color: C.sapphire, children: `model: ${modelName}` }), _jsx(Text, { color: C.dimtext, children: `mode: ${modeLabel} ${themeIcon} ${theme}` }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: C.dimtext, children: '[/] cmd [@] file [Shift+Tab] mode' }) })] }));
|
|
55
|
+
};
|
|
56
|
+
// ─────────────────────────────────────────────
|
|
57
|
+
// Tier 3 — Horizontal (≥70 cols)
|
|
58
|
+
// Full 3-column with Ink flexbox (no manual widths)
|
|
59
|
+
// ─────────────────────────────────────────────
|
|
60
|
+
const MascotLogo = () => {
|
|
61
|
+
const gradient = getMascotGradient();
|
|
62
|
+
return (_jsxs(Box, { flexDirection: "column", alignItems: "center", children: [MASCOT_LINES.map((line, i) => (_jsx(Text, { color: gradient[i] ?? gradient[gradient.length - 1], bold: true, children: line }, i))), _jsx(Text, { color: C.dimtext, children: 'A G E N T' })] }));
|
|
66
63
|
};
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
const StatsPanel = ({ modelName, sessionCount, modeLabel, theme }) => {
|
|
65
|
+
const themeIcon = theme === 'dark' ? '◐' : '◑';
|
|
66
|
+
const rows = [
|
|
67
|
+
{ label: 'model', value: truncate(modelName, 18), color: C.sapphire },
|
|
68
|
+
{ label: 'sessions', value: String(sessionCount), color: C.teal },
|
|
69
|
+
{ label: 'mode', value: modeLabel, color: modeLabel === 'plan' ? C.mauve : C.green },
|
|
70
|
+
{ label: 'theme', value: `${themeIcon} ${theme}`, color: C.mauve },
|
|
71
|
+
];
|
|
72
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: C.subtext, children: '// stats' }), rows.map(r => (_jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(Box, { width: 10, children: _jsx(Text, { color: C.dimtext, children: r.label }) }), _jsx(Text, { color: r.color, bold: true, children: r.value })] }, r.label)))] }));
|
|
73
|
+
};
|
|
74
|
+
const ActivityPanel = ({ tip, sessions }) => {
|
|
75
|
+
const dotColors = [C.green, C.blue, C.mauve];
|
|
76
|
+
const recent = sessions.slice(0, 3);
|
|
77
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: C.subtext, children: '// tip' }), _jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(Text, { color: C.green, bold: true, children: '$ ' }), _jsx(Text, { color: C.text, children: tip })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: C.subtext, children: `// recent (${sessions.length})` }) }), recent.length === 0 ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: C.dimtext, children: 'No recent activity' }) })) : (recent.map((s, i) => {
|
|
78
|
+
const d = new Date(s.last_activity);
|
|
79
|
+
const t = d.toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
|
|
80
|
+
return (_jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(Text, { color: C.dimtext, children: `${t} ` }), _jsx(Text, { color: dotColors[i % dotColors.length], children: '● ' }), _jsx(Text, { color: C.subtext, children: truncate(s.title || 'New Chat', 25) })] }, i));
|
|
81
|
+
}))] }));
|
|
75
82
|
};
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
const SHORTCUTS = [
|
|
84
|
+
{ key: '/', desc: '命令' },
|
|
85
|
+
{ key: '@', desc: '文件' },
|
|
86
|
+
{ key: '@@', desc: '符号' },
|
|
87
|
+
{ key: 'Shift+Tab', desc: '模式' },
|
|
88
|
+
{ key: 'Shift+↵', desc: '多行' },
|
|
89
|
+
];
|
|
90
|
+
const HorizontalHeader = ({ modelName, isConnected, sessionCount, modeLabel, theme, tip, sessions, version }) => {
|
|
91
|
+
const netColor = isConnected ? C.green : C.red;
|
|
92
|
+
const netBadge = isConnected ? '● CONNECTED' : '○ OFFLINE';
|
|
93
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: C.border, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", paddingX: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: C.blue, bold: true, children: '❯ ' }), _jsx(Text, { color: C.text, bold: true, children: 'MIST AGENT ' }), _jsx(Text, { color: C.mauve, bold: true, children: `[v${version}]` })] }), _jsx(Text, { color: netColor, children: `[ ${netBadge} ]` })] }), _jsx(Box, { borderStyle: "classic", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: C.border }), _jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { flexShrink: 0, paddingX: 2, paddingY: 1, children: _jsx(MascotLogo, {}) }), _jsx(Box, { borderStyle: "classic", borderLeft: true, borderTop: false, borderBottom: false, borderRight: false, borderColor: C.border }), _jsx(Box, { flexShrink: 0, width: 26, paddingX: 1, paddingY: 1, children: _jsx(StatsPanel, { modelName: modelName, sessionCount: sessionCount, modeLabel: modeLabel, theme: theme }) }), _jsx(Box, { borderStyle: "classic", borderLeft: true, borderTop: false, borderBottom: false, borderRight: false, borderColor: C.border }), _jsx(Box, { flexGrow: 1, flexShrink: 1, paddingX: 1, paddingY: 1, overflow: "hidden", children: _jsx(ActivityPanel, { tip: tip, sessions: sessions }) })] }), _jsx(Box, { borderStyle: "classic", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: C.border }), _jsx(Box, { flexDirection: "row", justifyContent: "center", gap: 3, paddingX: 1, paddingY: 1, children: SHORTCUTS.map(s => (_jsxs(Text, { color: C.dimtext, children: [_jsx(Text, { color: C.subtext, children: `[${s.key}]` }), ` ${s.desc}`] }, s.key))) })] }));
|
|
94
|
+
};
|
|
95
|
+
// ─────────────────────────────────────────────
|
|
96
|
+
// Root export — picks tier based on terminalWidth
|
|
97
|
+
// ─────────────────────────────────────────────
|
|
80
98
|
export const Header = () => {
|
|
81
99
|
const { currentModel } = useModelState();
|
|
82
100
|
const { terminalWidth, theme } = useUIState();
|
|
83
101
|
const { sessions } = useSessionState();
|
|
84
102
|
const { forceMode } = useChatState();
|
|
85
103
|
const { healthData } = useAppState();
|
|
86
|
-
const modelName = currentModel?.model ?? 'MistAgent
|
|
104
|
+
const modelName = currentModel?.model ?? 'MistAgent';
|
|
87
105
|
const isConnected = !!healthData;
|
|
88
|
-
const innerWidth = terminalWidth - 2;
|
|
89
|
-
// ── Column widths ──
|
|
90
|
-
const mascotWidth = Math.max(...MASCOT_LINES.map(l => stringWidth(l)));
|
|
91
|
-
const logoColWidth = mascotWidth + 8;
|
|
92
|
-
const statsColWidth = 28;
|
|
93
|
-
const infoColWidth = Math.max(10, innerWidth - logoColWidth - statsColWidth - 2);
|
|
94
|
-
// ── Random tip (stable per mount) ──
|
|
95
|
-
const [tipText] = React.useState(() => TIPS[Math.floor(Math.random() * TIPS.length)]);
|
|
96
|
-
const mascotGradient = getMascotGradient();
|
|
97
|
-
// ── Stats data ──
|
|
98
106
|
const modeLabel = forceMode === 'plan' ? 'plan' : 'chat';
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
{
|
|
102
|
-
{ label: 'sessions', value: String(sessions.length), valueColor: C.teal },
|
|
103
|
-
{ label: 'mode', value: modeLabel, valueColor: forceMode === 'plan' ? C.mauve : C.green },
|
|
104
|
-
{ label: 'theme', value: `${themeIcon} ${theme}`, valueColor: C.mauve },
|
|
105
|
-
];
|
|
106
|
-
// ── Recent sessions ──
|
|
107
|
-
const recentSessions = sessions.slice(0, 3);
|
|
108
|
-
const dotColors = [C.green, C.blue, C.mauve];
|
|
109
|
-
const emptyLogo = () => _jsx(Text, { children: ' '.repeat(logoColWidth) });
|
|
110
|
-
const emptyStats = () => _jsx(Text, { children: ' '.repeat(statsColWidth) });
|
|
111
|
-
const emptyInfo = () => _jsx(Text, { children: ' '.repeat(infoColWidth) });
|
|
112
|
-
// Col 1: Logo — top padding(2) + mascot(6) + gap + subtitle + bottom padding(2)
|
|
113
|
-
const logoRows = [
|
|
114
|
-
emptyLogo, // top padding 1
|
|
115
|
-
emptyLogo, // top padding 2
|
|
116
|
-
...MASCOT_LINES.map((line, i) => () => {
|
|
117
|
-
const color = mascotGradient[i] ?? mascotGradient[mascotGradient.length - 1];
|
|
118
|
-
return _jsx(Text, { color: color, bold: true, children: centerPad(line, logoColWidth) });
|
|
119
|
-
}),
|
|
120
|
-
emptyLogo, // gap before subtitle
|
|
121
|
-
() => _jsx(Text, { color: C.dimtext, children: centerPad('A G E N T', logoColWidth) }),
|
|
122
|
-
emptyLogo, // bottom padding 1
|
|
123
|
-
emptyLogo, // bottom padding 2
|
|
124
|
-
];
|
|
125
|
-
// Col 2: Stats — top padding(2) + header + gap + 4 rows (with gaps) + bottom padding
|
|
126
|
-
const statsRows = [
|
|
127
|
-
emptyStats, // top padding 1
|
|
128
|
-
emptyStats, // top padding 2
|
|
129
|
-
() => _jsx(Text, { color: C.subtext, children: pad(' // quick stats', statsColWidth) }),
|
|
130
|
-
emptyStats, // gap after header
|
|
131
|
-
...statsData.flatMap((s, i) => {
|
|
132
|
-
const row = () => {
|
|
133
|
-
const lbl = ` ${s.label}`;
|
|
134
|
-
const val = `${s.value} `;
|
|
135
|
-
const gap = Math.max(1, statsColWidth - stringWidth(lbl) - stringWidth(val));
|
|
136
|
-
return (_jsxs(Text, { children: [_jsx(Text, { color: C.dimtext, children: lbl }), _jsx(Text, { children: ' '.repeat(gap) }), _jsx(Text, { color: s.valueColor, bold: true, children: val })] }));
|
|
137
|
-
};
|
|
138
|
-
// Add empty row between stat rows (not after the last one)
|
|
139
|
-
return i < statsData.length - 1 ? [row, emptyStats] : [row];
|
|
140
|
-
}),
|
|
141
|
-
emptyStats, // bottom padding 1
|
|
142
|
-
emptyStats, // bottom padding 2
|
|
143
|
-
];
|
|
144
|
-
// Col 3: Info — top padding(2) + tip section + gap + recent section + bottom padding
|
|
145
|
-
const infoRows = [
|
|
146
|
-
emptyInfo, // top padding
|
|
147
|
-
() => _jsx(Text, { color: C.subtext, children: pad(' // daily tip', infoColWidth) }),
|
|
148
|
-
emptyInfo, // gap after tip label
|
|
149
|
-
() => {
|
|
150
|
-
const tipPrefix = ' $ ';
|
|
151
|
-
const tipContent = truncate(tipText, infoColWidth - stringWidth(tipPrefix));
|
|
152
|
-
return (_jsxs(Text, { children: [_jsx(Text, { color: C.green, bold: true, children: tipPrefix }), _jsx(Text, { color: C.text, children: pad(tipContent, infoColWidth - stringWidth(tipPrefix)) })] }));
|
|
153
|
-
},
|
|
154
|
-
emptyInfo, // gap between sections
|
|
155
|
-
emptyInfo,
|
|
156
|
-
() => _jsx(Text, { color: C.subtext, children: pad(' // recent activity (' + sessions.length + ')', infoColWidth) }),
|
|
157
|
-
emptyInfo, // gap after recent label
|
|
158
|
-
];
|
|
159
|
-
if (recentSessions.length > 0) {
|
|
160
|
-
for (const [idx, s] of recentSessions.entries()) {
|
|
161
|
-
const dotColor = dotColors[idx % dotColors.length];
|
|
162
|
-
infoRows.push(() => {
|
|
163
|
-
const date = new Date(s.last_activity);
|
|
164
|
-
const timeStr = date.toLocaleString('zh-CN', {
|
|
165
|
-
month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit',
|
|
166
|
-
});
|
|
167
|
-
const title = s.title || 'New Chat';
|
|
168
|
-
const timePrefix = ` ${timeStr} `;
|
|
169
|
-
const dotStr = '● ';
|
|
170
|
-
const usedWidth = stringWidth(timePrefix) + stringWidth(dotStr);
|
|
171
|
-
const titleWidth = Math.max(0, infoColWidth - usedWidth);
|
|
172
|
-
const displayTitle = pad(truncate(title, titleWidth), titleWidth);
|
|
173
|
-
return (_jsxs(Text, { children: [_jsx(Text, { color: C.dimtext, children: timePrefix }), _jsx(Text, { color: dotColor, children: dotStr }), _jsx(Text, { color: C.subtext, children: displayTitle })] }));
|
|
174
|
-
});
|
|
175
|
-
// Add empty row between recent items (not after the last one)
|
|
176
|
-
if (idx < recentSessions.length - 1) {
|
|
177
|
-
infoRows.push(emptyInfo);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
107
|
+
const [tip] = React.useState(() => TIPS[Math.floor(Math.random() * TIPS.length)]);
|
|
108
|
+
if (terminalWidth < CONDENSED_MAX) {
|
|
109
|
+
return _jsx(CondensedHeader, { modelName: modelName, isConnected: isConnected, version: VERSION });
|
|
180
110
|
}
|
|
181
|
-
|
|
182
|
-
|
|
111
|
+
if (terminalWidth < COMPACT_MAX) {
|
|
112
|
+
return (_jsx(CompactHeader, { modelName: modelName, isConnected: isConnected, modeLabel: modeLabel, theme: theme, version: VERSION }));
|
|
183
113
|
}
|
|
184
|
-
|
|
185
|
-
const maxRows = Math.max(logoRows.length, statsRows.length, infoRows.length);
|
|
186
|
-
while (logoRows.length < maxRows)
|
|
187
|
-
logoRows.push(emptyLogo);
|
|
188
|
-
while (statsRows.length < maxRows)
|
|
189
|
-
statsRows.push(emptyStats);
|
|
190
|
-
while (infoRows.length < maxRows)
|
|
191
|
-
infoRows.push(emptyInfo);
|
|
192
|
-
// ── Top bar layout ──
|
|
193
|
-
const netColor = isConnected ? C.green : C.red;
|
|
194
|
-
return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, marginTop: 1, children: [_jsx(Text, { children: _jsxs(Text, { color: C.yellow, children: [B.topLeft, B.h.repeat(innerWidth), B.topRight] }) }), _jsx(EmptyRow, { width: innerWidth }), (() => {
|
|
195
|
-
const leftPad = ' ';
|
|
196
|
-
const prompt = '❯';
|
|
197
|
-
const brand = ' M I S T A G E N T ';
|
|
198
|
-
const verBadge = `[ v${VERSION} ]`;
|
|
199
|
-
const netBadge = isConnected ? '[ ● CONNECTED ]' : '[ ○ OFFLINE ]';
|
|
200
|
-
const leftStr = `${leftPad}${prompt}${brand}${verBadge}`;
|
|
201
|
-
const rightStr = `${netBadge} `;
|
|
202
|
-
const gap = Math.max(1, innerWidth - stringWidth(leftStr) - stringWidth(rightStr));
|
|
203
|
-
return (_jsxs(Text, { children: [_jsx(Text, { color: C.yellow, children: B.v }), _jsx(Text, { children: leftPad }), _jsx(Text, { color: C.blue, bold: true, children: prompt }), _jsx(Text, { color: C.text, bold: true, children: brand }), _jsx(Text, { color: C.mauve, bold: true, children: verBadge }), _jsx(Text, { children: ' '.repeat(gap) }), _jsx(Text, { color: netColor, children: netBadge }), _jsx(Text, { children: ' ' }), _jsx(Text, { color: C.yellow, children: B.v })] }));
|
|
204
|
-
})(), _jsx(EmptyRow, { width: innerWidth }), _jsxs(Text, { children: [_jsx(Text, { color: C.yellow, children: B.v }), _jsx(DashedDivider, { width: innerWidth }), _jsx(Text, { color: C.yellow, children: B.v })] }), Array.from({ length: maxRows }).map((_, i) => {
|
|
205
|
-
return (_jsxs(Text, { children: [_jsx(Text, { color: C.yellow, children: B.v }), logoRows[i](), _jsx(Text, { color: C.yellow, children: "\u2502" }), statsRows[i](), _jsx(Text, { color: C.yellow, children: "\u2502" }), infoRows[i](), _jsx(Text, { color: C.yellow, children: B.v })] }, i));
|
|
206
|
-
}), _jsxs(Text, { children: [_jsx(Text, { color: C.yellow, children: B.v }), _jsx(DashedDivider, { width: innerWidth }), _jsx(Text, { color: C.yellow, children: B.v })] }), _jsx(EmptyRow, { width: innerWidth }), _jsxs(Text, { children: [_jsx(Text, { color: C.yellow, children: B.v }), _jsx(Text, { children: centerPad(SHORTCUTS.map(s => `[${s.key}] ${s.desc}`).join(' '), innerWidth) }), _jsx(Text, { color: C.yellow, children: B.v })] }), _jsx(EmptyRow, { width: innerWidth }), _jsx(Text, { children: _jsxs(Text, { color: C.yellow, children: [B.bottomLeft, B.h.repeat(innerWidth), B.bottomRight] }) })] }));
|
|
114
|
+
return (_jsx(HorizontalHeader, { modelName: modelName, isConnected: isConnected, sessionCount: sessions.length, modeLabel: modeLabel, theme: theme, tip: tip, sessions: sessions, version: VERSION }));
|
|
207
115
|
};
|
|
208
116
|
//# sourceMappingURL=Header.js.map
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { Static, Box } from 'ink';
|
|
2
|
+
import { Box } from 'ink';
|
|
4
3
|
import { useChatState, StreamingState } from '../contexts/ChatContext.js';
|
|
5
4
|
import { HistoryItemDisplay } from './HistoryItemDisplay.js';
|
|
6
5
|
import { AssistantMessage } from './messages/AssistantMessage.js';
|
|
7
6
|
import { Header } from './Header.js';
|
|
8
|
-
// Stable sentinel object — always the first item so <Static> renders
|
|
9
|
-
// the Header once, and subsequent history items are appended after it.
|
|
10
|
-
const HEADER_SENTINEL = { __header: true, id: '__header__' };
|
|
11
7
|
export const MainContent = () => {
|
|
12
|
-
const { history, pendingContent, streamingState
|
|
13
|
-
|
|
14
|
-
const staticItems = useMemo(() => [HEADER_SENTINEL, ...history], [history]);
|
|
15
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Static, { items: staticItems, children: (item) => {
|
|
16
|
-
if ('__header' in item) {
|
|
17
|
-
return (_jsx(Box, { children: _jsx(Header, {}) }, "__header__"));
|
|
18
|
-
}
|
|
19
|
-
return (_jsx(Box, { paddingX: 1, children: _jsx(HistoryItemDisplay, { item: item }) }, item.id));
|
|
20
|
-
} }, sessionVersion), streamingState !== StreamingState.Idle && (_jsx(Box, { paddingX: 1, children: _jsx(AssistantMessage, { text: pendingContent, isPending: true }) }))] }));
|
|
8
|
+
const { history, pendingContent, streamingState } = useChatState();
|
|
9
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, {}), history.map((item) => (_jsx(Box, { paddingX: 1, children: _jsx(HistoryItemDisplay, { item: item }) }, item.id))), streamingState !== StreamingState.Idle && (_jsx(Box, { paddingX: 1, children: _jsx(AssistantMessage, { text: pendingContent, isPending: true }) }))] }));
|
|
21
10
|
};
|
|
22
11
|
//# sourceMappingURL=MainContent.js.map
|
|
@@ -1,31 +1,20 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { useMemo } from 'react';
|
|
2
3
|
import { Text } from 'ink';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
cleaned = cleaned.replace(/^```(\w+)/gm, (_match, lang) => supportsLanguage(lang) ? '```' + lang : '```');
|
|
17
|
-
return marked.parse(cleaned);
|
|
18
|
-
}
|
|
19
|
-
export const MarkdownRenderer = ({ text, }) => {
|
|
20
|
-
if (!text)
|
|
4
|
+
import { applyMarkdown } from '../../utils/markdown.js';
|
|
5
|
+
export const MarkdownRenderer = React.memo(({ text, }) => {
|
|
6
|
+
const rendered = useMemo(() => {
|
|
7
|
+
if (!text)
|
|
8
|
+
return null;
|
|
9
|
+
try {
|
|
10
|
+
return applyMarkdown(text);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return text;
|
|
14
|
+
}
|
|
15
|
+
}, [text]);
|
|
16
|
+
if (!rendered)
|
|
21
17
|
return null;
|
|
22
|
-
let rendered;
|
|
23
|
-
try {
|
|
24
|
-
rendered = silentParse(text).trimEnd();
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
rendered = text;
|
|
28
|
-
}
|
|
29
18
|
return _jsx(Text, { children: rendered });
|
|
30
|
-
};
|
|
19
|
+
});
|
|
31
20
|
//# sourceMappingURL=MarkdownRenderer.js.map
|
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { Box, Text, useCursor } from 'ink';
|
|
3
4
|
import chalk from 'chalk';
|
|
5
|
+
import stringWidth from 'string-width';
|
|
4
6
|
import { cpSlice, cpLen } from '../../utils/textUtils.js';
|
|
5
7
|
import { PASTE_PLACEHOLDER_REGEX } from '../../utils/pasteUtils.js';
|
|
6
8
|
import { palette } from '../../utils/colors.js';
|
|
9
|
+
/**
|
|
10
|
+
* Walk up the Yoga layout tree to compute absolute (x, y) of a DOM node.
|
|
11
|
+
* Same algorithm as Ink's internal `getAbsolutePosition` (layout.ts).
|
|
12
|
+
*/
|
|
13
|
+
function getAbsolutePosition(node) {
|
|
14
|
+
let x = 0;
|
|
15
|
+
let y = 0;
|
|
16
|
+
let cur = node;
|
|
17
|
+
while (cur?.parentNode) {
|
|
18
|
+
if (!cur.yogaNode)
|
|
19
|
+
return null;
|
|
20
|
+
x += cur.yogaNode.getComputedLeft();
|
|
21
|
+
y += cur.yogaNode.getComputedTop();
|
|
22
|
+
cur = cur.parentNode;
|
|
23
|
+
}
|
|
24
|
+
return { x, y };
|
|
25
|
+
}
|
|
7
26
|
/**
|
|
8
27
|
* Split a line into segments, marking paste placeholders for accent rendering.
|
|
9
28
|
* Returns array of { text, isPaste } segments.
|
|
@@ -93,10 +112,50 @@ export const TextInput = ({ buffer, placeholder = 'Ask anything...', isActive, }
|
|
|
93
112
|
const { viewportVisualLines, visualCursor } = buffer;
|
|
94
113
|
const [cursorRow, cursorCol] = visualCursor;
|
|
95
114
|
const showPlaceholder = buffer.text.length === 0;
|
|
115
|
+
// ── Terminal cursor positioning for IME ──
|
|
116
|
+
// Park the real terminal cursor at the text caret so CJK IME candidate
|
|
117
|
+
// windows appear inline instead of at the end of the line.
|
|
118
|
+
const { setCursorPosition } = useCursor();
|
|
119
|
+
const boxRef = useRef(null);
|
|
120
|
+
if (!isActive) {
|
|
121
|
+
setCursorPosition(undefined);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const node = boxRef.current;
|
|
125
|
+
if (node) {
|
|
126
|
+
const abs = getAbsolutePosition(node);
|
|
127
|
+
if (abs) {
|
|
128
|
+
const indent = cursorRow === 0 ? 0 : 2;
|
|
129
|
+
const lineText = viewportVisualLines[cursorRow] ?? '';
|
|
130
|
+
const textBeforeCursor = cpSlice(lineText, 0, cursorCol);
|
|
131
|
+
const displayCol = stringWidth(textBeforeCursor);
|
|
132
|
+
// Ink's buildCursorSuffix computes: moveUp = visibleLineCount - y.
|
|
133
|
+
// When output fits in terminal, the output string ends with '\n'
|
|
134
|
+
// and visibleLineCount = lines.length - 1 (= Yoga rootH). The
|
|
135
|
+
// cursor sits one line below the last visible line, so Yoga's
|
|
136
|
+
// absolute Y maps directly.
|
|
137
|
+
//
|
|
138
|
+
// When output overflows (rootH > termRows), Ink's incremental
|
|
139
|
+
// renderer omits the trailing '\n', so visibleLineCount = lines.length
|
|
140
|
+
// (= rootH + 1 effectively in the moveUp math). The cursor sits at
|
|
141
|
+
// the END of the last line, not a line below it. This shifts the
|
|
142
|
+
// moveUp origin by one row, requiring y + 1 to compensate.
|
|
143
|
+
let rootNode = node;
|
|
144
|
+
while (rootNode?.parentNode)
|
|
145
|
+
rootNode = rootNode.parentNode;
|
|
146
|
+
const rootH = rootNode?.yogaNode?.getComputedHeight() ?? 0;
|
|
147
|
+
const overflows = rootH > (process.stdout.rows ?? 24);
|
|
148
|
+
setCursorPosition({
|
|
149
|
+
x: abs.x + indent + displayCol,
|
|
150
|
+
y: abs.y + cursorRow + (overflows ? 1 : 0),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
96
155
|
if (showPlaceholder) {
|
|
97
|
-
return (_jsx(Box, { children: isActive ? (_jsxs(Text, { children: [chalk.inverse(placeholder[0] || ' '), _jsx(Text, { color: palette.textDim, children: placeholder.slice(1) })] })) : (_jsx(Text, { color: palette.textDim, children: placeholder })) }));
|
|
156
|
+
return (_jsx(Box, { ref: boxRef, children: isActive ? (_jsxs(Text, { children: [chalk.inverse(placeholder[0] || ' '), _jsx(Text, { color: palette.textDim, children: placeholder.slice(1) })] })) : (_jsx(Text, { color: palette.textDim, children: placeholder })) }));
|
|
98
157
|
}
|
|
99
|
-
return (_jsx(Box, { flexDirection: "column", children: viewportVisualLines.map((lineText, idx) => {
|
|
158
|
+
return (_jsx(Box, { ref: boxRef, flexDirection: "column", children: viewportVisualLines.map((lineText, idx) => {
|
|
100
159
|
const isCursorLine = isActive && idx === cursorRow;
|
|
101
160
|
// First line has no indent (follows ❯ prompt), subsequent lines indent 2 spaces
|
|
102
161
|
const indent = idx === 0 ? '' : ' ';
|
|
@@ -13,6 +13,7 @@ export interface ActiveToolCall {
|
|
|
13
13
|
export type ForceMode = 'chat' | 'plan';
|
|
14
14
|
export interface PendingPlan {
|
|
15
15
|
steps: string[];
|
|
16
|
+
content: string;
|
|
16
17
|
}
|
|
17
18
|
export interface PendingToolApproval {
|
|
18
19
|
request: TunnelRequest;
|
|
@@ -81,6 +82,7 @@ export type ChatAction = {
|
|
|
81
82
|
} | {
|
|
82
83
|
type: 'PLAN_CONFIRM';
|
|
83
84
|
steps: string[];
|
|
85
|
+
content: string;
|
|
84
86
|
} | {
|
|
85
87
|
type: 'PLAN_CLEAR';
|
|
86
88
|
} | {
|
|
@@ -179,7 +179,7 @@ function chatReducer(state, action) {
|
|
|
179
179
|
history: planHistory,
|
|
180
180
|
pendingContent: '',
|
|
181
181
|
activeToolCall: null,
|
|
182
|
-
pendingPlan: { steps: action.steps },
|
|
182
|
+
pendingPlan: { steps: action.steps, content: action.content },
|
|
183
183
|
tokenCount: state.tokenCount + Math.ceil(flushedContent.length / 4),
|
|
184
184
|
};
|
|
185
185
|
}
|