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.
Files changed (200) hide show
  1. package/dist/src/components/App.d.ts +2 -0
  2. package/dist/src/components/App.js +23 -5
  3. package/dist/src/components/Composer.js +2 -1
  4. package/dist/src/components/Header.js +74 -166
  5. package/dist/src/components/MainContent.js +3 -14
  6. package/dist/src/components/shared/MarkdownRenderer.js +15 -26
  7. package/dist/src/components/shared/TextInput.js +62 -3
  8. package/dist/src/contexts/ChatContext.d.ts +2 -0
  9. package/dist/src/contexts/ChatContext.js +1 -1
  10. package/dist/src/hooks/useChat.js +71 -10
  11. package/dist/src/main.js +62 -4
  12. package/dist/src/types/api.d.ts +4 -0
  13. package/dist/src/utils/config.d.ts +2 -0
  14. package/dist/src/utils/config.js +23 -0
  15. package/dist/src/utils/constants.d.ts +1 -1
  16. package/dist/src/utils/constants.js +1 -1
  17. package/dist/src/utils/fileTunnel.d.ts +7 -2
  18. package/dist/src/utils/fileTunnel.js +376 -5
  19. package/dist/src/utils/markdown.d.ts +10 -0
  20. package/dist/src/utils/markdown.js +223 -0
  21. package/dist/src/utils/updateChecker.js +10 -4
  22. package/package.json +3 -2
  23. package/dist/index.d.ts.map +0 -1
  24. package/dist/index.js.map +0 -1
  25. package/dist/src/api/auth.d.ts.map +0 -1
  26. package/dist/src/api/auth.js.map +0 -1
  27. package/dist/src/api/chat.d.ts.map +0 -1
  28. package/dist/src/api/chat.js.map +0 -1
  29. package/dist/src/api/client.d.ts.map +0 -1
  30. package/dist/src/api/client.js.map +0 -1
  31. package/dist/src/api/models.d.ts.map +0 -1
  32. package/dist/src/api/models.js.map +0 -1
  33. package/dist/src/api/sessions.d.ts.map +0 -1
  34. package/dist/src/api/sessions.js.map +0 -1
  35. package/dist/src/api/skills.d.ts.map +0 -1
  36. package/dist/src/api/skills.js.map +0 -1
  37. package/dist/src/api/tools.d.ts.map +0 -1
  38. package/dist/src/api/tools.js.map +0 -1
  39. package/dist/src/api/tunnel.d.ts.map +0 -1
  40. package/dist/src/api/tunnel.js.map +0 -1
  41. package/dist/src/components/App.d.ts.map +0 -1
  42. package/dist/src/components/App.js.map +0 -1
  43. package/dist/src/components/AppLayout.d.ts.map +0 -1
  44. package/dist/src/components/AppLayout.js.map +0 -1
  45. package/dist/src/components/Composer.d.ts.map +0 -1
  46. package/dist/src/components/Composer.js.map +0 -1
  47. package/dist/src/components/Footer.d.ts.map +0 -1
  48. package/dist/src/components/Footer.js.map +0 -1
  49. package/dist/src/components/Header.d.ts.map +0 -1
  50. package/dist/src/components/Header.js.map +0 -1
  51. package/dist/src/components/HistoryItemDisplay.d.ts.map +0 -1
  52. package/dist/src/components/HistoryItemDisplay.js.map +0 -1
  53. package/dist/src/components/InputPrompt.d.ts.map +0 -1
  54. package/dist/src/components/InputPrompt.js.map +0 -1
  55. package/dist/src/components/LoadingIndicator.d.ts.map +0 -1
  56. package/dist/src/components/LoadingIndicator.js.map +0 -1
  57. package/dist/src/components/LoginPrompt.d.ts.map +0 -1
  58. package/dist/src/components/LoginPrompt.js.map +0 -1
  59. package/dist/src/components/MainContent.d.ts.map +0 -1
  60. package/dist/src/components/MainContent.js.map +0 -1
  61. package/dist/src/components/ModelPicker.d.ts.map +0 -1
  62. package/dist/src/components/ModelPicker.js.map +0 -1
  63. package/dist/src/components/SessionPicker.d.ts.map +0 -1
  64. package/dist/src/components/SessionPicker.js.map +0 -1
  65. package/dist/src/components/SuggestionsDisplay.d.ts.map +0 -1
  66. package/dist/src/components/SuggestionsDisplay.js.map +0 -1
  67. package/dist/src/components/ThemePicker.d.ts.map +0 -1
  68. package/dist/src/components/ThemePicker.js.map +0 -1
  69. package/dist/src/components/messages/AssistantMessage.d.ts.map +0 -1
  70. package/dist/src/components/messages/AssistantMessage.js.map +0 -1
  71. package/dist/src/components/messages/CommandResult.d.ts.map +0 -1
  72. package/dist/src/components/messages/CommandResult.js.map +0 -1
  73. package/dist/src/components/messages/ErrorMessage.d.ts.map +0 -1
  74. package/dist/src/components/messages/ErrorMessage.js.map +0 -1
  75. package/dist/src/components/messages/InfoMessage.d.ts.map +0 -1
  76. package/dist/src/components/messages/InfoMessage.js.map +0 -1
  77. package/dist/src/components/messages/ModelMessage.d.ts.map +0 -1
  78. package/dist/src/components/messages/ModelMessage.js.map +0 -1
  79. package/dist/src/components/messages/SessionMessage.d.ts.map +0 -1
  80. package/dist/src/components/messages/SessionMessage.js.map +0 -1
  81. package/dist/src/components/messages/ToolCallMessage.d.ts.map +0 -1
  82. package/dist/src/components/messages/ToolCallMessage.js.map +0 -1
  83. package/dist/src/components/messages/UserMessage.d.ts.map +0 -1
  84. package/dist/src/components/messages/UserMessage.js.map +0 -1
  85. package/dist/src/components/shared/HorizontalLine.d.ts.map +0 -1
  86. package/dist/src/components/shared/HorizontalLine.js.map +0 -1
  87. package/dist/src/components/shared/MarkdownRenderer.d.ts.map +0 -1
  88. package/dist/src/components/shared/MarkdownRenderer.js.map +0 -1
  89. package/dist/src/components/shared/Spinner.d.ts.map +0 -1
  90. package/dist/src/components/shared/Spinner.js.map +0 -1
  91. package/dist/src/components/shared/TextInput.d.ts.map +0 -1
  92. package/dist/src/components/shared/TextInput.js.map +0 -1
  93. package/dist/src/contexts/AppContext.d.ts.map +0 -1
  94. package/dist/src/contexts/AppContext.js.map +0 -1
  95. package/dist/src/contexts/ChatContext.d.ts.map +0 -1
  96. package/dist/src/contexts/ChatContext.js.map +0 -1
  97. package/dist/src/contexts/KeypressContext.d.ts.map +0 -1
  98. package/dist/src/contexts/KeypressContext.js.map +0 -1
  99. package/dist/src/contexts/ModelContext.d.ts.map +0 -1
  100. package/dist/src/contexts/ModelContext.js.map +0 -1
  101. package/dist/src/contexts/SessionContext.d.ts.map +0 -1
  102. package/dist/src/contexts/SessionContext.js.map +0 -1
  103. package/dist/src/contexts/UIContext.d.ts.map +0 -1
  104. package/dist/src/contexts/UIContext.js.map +0 -1
  105. package/dist/src/hooks/useChat.d.ts.map +0 -1
  106. package/dist/src/hooks/useChat.js.map +0 -1
  107. package/dist/src/hooks/useFileCompletion.d.ts.map +0 -1
  108. package/dist/src/hooks/useFileCompletion.js.map +0 -1
  109. package/dist/src/hooks/useInputHistory.d.ts.map +0 -1
  110. package/dist/src/hooks/useInputHistory.js.map +0 -1
  111. package/dist/src/hooks/useKeypress.d.ts.map +0 -1
  112. package/dist/src/hooks/useKeypress.js.map +0 -1
  113. package/dist/src/hooks/useLoadingIndicator.d.ts.map +0 -1
  114. package/dist/src/hooks/useLoadingIndicator.js.map +0 -1
  115. package/dist/src/hooks/usePasteBuffer.d.ts.map +0 -1
  116. package/dist/src/hooks/usePasteBuffer.js.map +0 -1
  117. package/dist/src/hooks/useSlashCommand.d.ts.map +0 -1
  118. package/dist/src/hooks/useSlashCommand.js.map +0 -1
  119. package/dist/src/hooks/useStdinInterceptor.d.ts.map +0 -1
  120. package/dist/src/hooks/useStdinInterceptor.js.map +0 -1
  121. package/dist/src/hooks/useSymbolCompletion.d.ts.map +0 -1
  122. package/dist/src/hooks/useSymbolCompletion.js.map +0 -1
  123. package/dist/src/hooks/useTextBuffer.d.ts.map +0 -1
  124. package/dist/src/hooks/useTextBuffer.js.map +0 -1
  125. package/dist/src/main.d.ts.map +0 -1
  126. package/dist/src/main.js.map +0 -1
  127. package/dist/src/tools/code-analyzer/config/ignore-service.d.ts.map +0 -1
  128. package/dist/src/tools/code-analyzer/config/ignore-service.js.map +0 -1
  129. package/dist/src/tools/code-analyzer/config/supported-languages.d.ts.map +0 -1
  130. package/dist/src/tools/code-analyzer/config/supported-languages.js.map +0 -1
  131. package/dist/src/tools/code-analyzer/core/graph/graph.d.ts.map +0 -1
  132. package/dist/src/tools/code-analyzer/core/graph/graph.js.map +0 -1
  133. package/dist/src/tools/code-analyzer/core/graph/types.d.ts.map +0 -1
  134. package/dist/src/tools/code-analyzer/core/graph/types.js.map +0 -1
  135. package/dist/src/tools/code-analyzer/core/ingestion/ast-cache.d.ts.map +0 -1
  136. package/dist/src/tools/code-analyzer/core/ingestion/ast-cache.js.map +0 -1
  137. package/dist/src/tools/code-analyzer/core/ingestion/call-processor.d.ts.map +0 -1
  138. package/dist/src/tools/code-analyzer/core/ingestion/call-processor.js.map +0 -1
  139. package/dist/src/tools/code-analyzer/core/ingestion/community-processor.d.ts.map +0 -1
  140. package/dist/src/tools/code-analyzer/core/ingestion/community-processor.js.map +0 -1
  141. package/dist/src/tools/code-analyzer/core/ingestion/entry-point-scoring.d.ts.map +0 -1
  142. package/dist/src/tools/code-analyzer/core/ingestion/entry-point-scoring.js.map +0 -1
  143. package/dist/src/tools/code-analyzer/core/ingestion/filesystem-walker.d.ts.map +0 -1
  144. package/dist/src/tools/code-analyzer/core/ingestion/filesystem-walker.js.map +0 -1
  145. package/dist/src/tools/code-analyzer/core/ingestion/framework-detection.d.ts.map +0 -1
  146. package/dist/src/tools/code-analyzer/core/ingestion/framework-detection.js.map +0 -1
  147. package/dist/src/tools/code-analyzer/core/ingestion/heritage-processor.d.ts.map +0 -1
  148. package/dist/src/tools/code-analyzer/core/ingestion/heritage-processor.js.map +0 -1
  149. package/dist/src/tools/code-analyzer/core/ingestion/import-processor.d.ts.map +0 -1
  150. package/dist/src/tools/code-analyzer/core/ingestion/import-processor.js.map +0 -1
  151. package/dist/src/tools/code-analyzer/core/ingestion/parsing-processor.d.ts.map +0 -1
  152. package/dist/src/tools/code-analyzer/core/ingestion/parsing-processor.js.map +0 -1
  153. package/dist/src/tools/code-analyzer/core/ingestion/pipeline.d.ts.map +0 -1
  154. package/dist/src/tools/code-analyzer/core/ingestion/pipeline.js.map +0 -1
  155. package/dist/src/tools/code-analyzer/core/ingestion/process-processor.d.ts.map +0 -1
  156. package/dist/src/tools/code-analyzer/core/ingestion/process-processor.js.map +0 -1
  157. package/dist/src/tools/code-analyzer/core/ingestion/structure-processor.d.ts.map +0 -1
  158. package/dist/src/tools/code-analyzer/core/ingestion/structure-processor.js.map +0 -1
  159. package/dist/src/tools/code-analyzer/core/ingestion/symbol-table.d.ts.map +0 -1
  160. package/dist/src/tools/code-analyzer/core/ingestion/symbol-table.js.map +0 -1
  161. package/dist/src/tools/code-analyzer/core/ingestion/tree-sitter-queries.d.ts.map +0 -1
  162. package/dist/src/tools/code-analyzer/core/ingestion/tree-sitter-queries.js.map +0 -1
  163. package/dist/src/tools/code-analyzer/core/ingestion/utils.d.ts.map +0 -1
  164. package/dist/src/tools/code-analyzer/core/ingestion/utils.js.map +0 -1
  165. package/dist/src/tools/code-analyzer/core/ingestion/workers/parse-worker.d.ts.map +0 -1
  166. package/dist/src/tools/code-analyzer/core/ingestion/workers/parse-worker.js.map +0 -1
  167. package/dist/src/tools/code-analyzer/core/ingestion/workers/worker-pool.d.ts.map +0 -1
  168. package/dist/src/tools/code-analyzer/core/ingestion/workers/worker-pool.js.map +0 -1
  169. package/dist/src/tools/code-analyzer/core/tree-sitter/parser-loader.d.ts.map +0 -1
  170. package/dist/src/tools/code-analyzer/core/tree-sitter/parser-loader.js.map +0 -1
  171. package/dist/src/tools/code-analyzer/index.d.ts.map +0 -1
  172. package/dist/src/tools/code-analyzer/index.js.map +0 -1
  173. package/dist/src/tools/code-analyzer/lib/utils.d.ts.map +0 -1
  174. package/dist/src/tools/code-analyzer/lib/utils.js.map +0 -1
  175. package/dist/src/tools/code-analyzer/types/pipeline.d.ts.map +0 -1
  176. package/dist/src/tools/code-analyzer/types/pipeline.js.map +0 -1
  177. package/dist/src/types/api.d.ts.map +0 -1
  178. package/dist/src/types/api.js.map +0 -1
  179. package/dist/src/types/history.d.ts.map +0 -1
  180. package/dist/src/types/history.js.map +0 -1
  181. package/dist/src/utils/colors.d.ts.map +0 -1
  182. package/dist/src/utils/colors.js.map +0 -1
  183. package/dist/src/utils/config.d.ts.map +0 -1
  184. package/dist/src/utils/config.js.map +0 -1
  185. package/dist/src/utils/constants.d.ts.map +0 -1
  186. package/dist/src/utils/constants.js.map +0 -1
  187. package/dist/src/utils/fileRef.d.ts.map +0 -1
  188. package/dist/src/utils/fileRef.js.map +0 -1
  189. package/dist/src/utils/fileTunnel.d.ts.map +0 -1
  190. package/dist/src/utils/fileTunnel.js.map +0 -1
  191. package/dist/src/utils/formatters.d.ts.map +0 -1
  192. package/dist/src/utils/formatters.js.map +0 -1
  193. package/dist/src/utils/pasteUtils.d.ts.map +0 -1
  194. package/dist/src/utils/pasteUtils.js.map +0 -1
  195. package/dist/src/utils/skillScanner.d.ts.map +0 -1
  196. package/dist/src/utils/skillScanner.js.map +0 -1
  197. package/dist/src/utils/textUtils.d.ts.map +0 -1
  198. package/dist/src/utils/textUtils.js.map +0 -1
  199. package/dist/src/utils/updateChecker.d.ts.map +0 -1
  200. package/dist/src/utils/updateChecker.js.map +0 -1
@@ -18,6 +18,8 @@ interface AppProps {
18
18
  loginError: string | null;
19
19
  isAuthenticated: boolean;
20
20
  initialTheme: ThemeMode;
21
+ terminalWidth: number;
22
+ terminalHeight: number;
21
23
  }
22
24
  export declare const App: React.FC<AppProps>;
23
25
  export {};
@@ -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: [_jsx(Text, { color: palette.warning, bold: true, children: "\uD83D\uDCCB Plan Review" }), _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.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
+ })() })] }))] })), 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
- // ── Shortcuts (matching pencil: full key names) ──
32
- const SHORTCUTS = [
33
- { key: '/', desc: '命令' },
34
- { key: '@', desc: '文件' },
35
- { key: '@@', desc: '符号' },
36
- { key: 'Shift+Tab', desc: '模式' },
37
- { key: 'Shift+Enter', desc: '多行' },
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) <= maxLen)
33
+ if (stringWidth(s) <= max)
58
34
  return s;
59
- let result = '';
35
+ let out = '';
60
36
  for (const ch of s) {
61
- if (stringWidth(result + ch) > maxLen - 1)
37
+ if (stringWidth(out + ch) > max - 1)
62
38
  break;
63
- result += ch;
39
+ out += ch;
64
40
  }
65
- return result + '…';
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
- // ── Border chars (dashed style) ──
68
- const B = {
69
- topLeft: '╭',
70
- topRight: '',
71
- bottomLeft: '',
72
- bottomRight: '',
73
- h: '', // dashed horizontal
74
- v: '┊', // dashed vertical
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
- // ── Dashed divider (orange, matching outer border) ──
77
- const DashedDivider = ({ width }) => (_jsx(Text, { color: C.yellow, children: B.h.repeat(width) }));
78
- // ── Empty row (full inner width, bordered) ──
79
- const EmptyRow = ({ width }) => (_jsxs(Text, { children: [_jsx(Text, { color: C.yellow, children: B.v }), _jsx(Text, { children: ' '.repeat(width) }), _jsx(Text, { color: C.yellow, children: B.v })] }));
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 0.1';
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 themeIcon = theme === 'dark' ? '◐' : '◑';
100
- const statsData = [
101
- { label: 'model', value: modelName, valueColor: C.sapphire },
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
- else {
182
- infoRows.push(() => _jsx(Text, { color: C.dimtext, children: pad(' No recent activity', infoColWidth) }));
111
+ if (terminalWidth < COMPACT_MAX) {
112
+ return (_jsx(CompactHeader, { modelName: modelName, isConnected: isConnected, modeLabel: modeLabel, theme: theme, version: VERSION }));
183
113
  }
184
- // Equalize row count
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 { useMemo } from 'react';
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, sessionVersion } = useChatState();
13
- // Always keep the sentinel at index 0 so Static.length grows correctly.
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 { Marked } from 'marked';
4
- import { markedTerminal } from 'marked-terminal';
5
- import { supportsLanguage } from 'cli-highlight';
6
- const marked = new Marked(markedTerminal({
7
- reflowText: true,
8
- width: 80,
9
- showSectionPrefix: false,
10
- tab: 2,
11
- }));
12
- // Replace ```<unsupported-lang> with ``` to avoid console.warn from highlight.js
13
- function silentParse(src) {
14
- // Strip internal heading markers (e.g. {mist_session_heading:...})
15
- let cleaned = src.replace(/\{mist_session_heading:[^}]*\}\s*/g, '');
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 { Box, Text } from 'ink';
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
  }