@miphamai/cli 0.27.0 → 0.29.0

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/src/ui/chat.tsx CHANGED
@@ -28,6 +28,7 @@ function toolColor(name: string): string {
28
28
  return 'white'
29
29
  case 'Write':
30
30
  case 'Edit':
31
+ case 'Update':
31
32
  return 'red'
32
33
  case 'Agent':
33
34
  case 'Task':
@@ -59,12 +60,19 @@ const MessageRow = React.memo(
59
60
  >
60
61
  {msg.toolMeta ? (
61
62
  <Box flexDirection="column">
62
- <Text color={toolColor(msg.toolMeta.name)}>
63
- {msg.toolMeta.collapsed ? '⏺' : '⏺ ▼'} {msg.toolMeta.name}
64
- {msg.toolMeta.input ? `(${msg.toolMeta.input.slice(0, 120)})` : ''}
65
- </Text>
63
+ {/* Tool call line: ⏺ ToolName(detail) — Claude Code parity */}
64
+ {msg.toolMeta.name ? (
65
+ <Text color={toolColor(msg.toolMeta.name)}>
66
+ {msg.toolMeta.collapsed ? '⏺' : '⏺ ▼'} {msg.toolMeta.name}
67
+ {msg.toolMeta.input ? ` (${msg.toolMeta.input.slice(0, 120)})` : ''}
68
+ </Text>
69
+ ) : null}
70
+ {/* Tool result line: ⎿ summary text */}
66
71
  {msg.toolMeta.output ? (
67
- <Text dimColor> ⎿ {msg.toolMeta.output.slice(0, 300)}</Text>
72
+ <Text dimColor>
73
+ {' ⎿ '}
74
+ {msg.toolMeta.output.slice(0, 300)}
75
+ </Text>
68
76
  ) : msg.content && !msg.toolMeta.collapsed ? (
69
77
  <Text dimColor>{msg.content}</Text>
70
78
  ) : null}
package/src/ui/input.tsx CHANGED
@@ -83,6 +83,16 @@ export function InputBar({
83
83
  const [completionVerb, setCompletionVerb] = useState<string | null>(null)
84
84
  const prevLoading = useRef(isLoading)
85
85
 
86
+ // ── Message history for arrow-key navigation (Claude Code parity) ──
87
+ const [submittedHistory, setSubmittedHistory] = useState<string[]>([])
88
+ const historyIndexRef = useRef(-1) // -1 = not browsing history
89
+ const savedDraftRef = useRef('') // saved user draft before browsing history
90
+
91
+ // Stabilize t ref — prevents stale closures in intervals and avoids
92
+ // unnecessary effect re-runs when the i18n context value object changes.
93
+ const tRef = useRef(t)
94
+ tRef.current = t
95
+
86
96
  // ── Slash command hints ──
87
97
  const allCommands = useMemo(() => getCommandList(), [])
88
98
  const slashHints = useMemo(() => {
@@ -92,32 +102,33 @@ export function InputBar({
92
102
  return allCommands.filter((c) => c.name.toLowerCase().includes(filter)).slice(0, 8)
93
103
  }, [value, allCommands])
94
104
 
95
- // Rotate gerunds while loading
105
+ // Rotate gerunds while loading — use tRef to avoid re-running when i18n context re-renders
96
106
  useEffect(() => {
97
107
  if (!isLoading) return
98
108
  const interval = setInterval(() => {
99
- setVerb(t(pick(LOADING_KEYS)))
100
- }, 1200)
109
+ setVerb(tRef.current(pick(LOADING_KEYS)))
110
+ }, 2000)
101
111
  return () => clearInterval(interval)
102
- }, [isLoading, t])
112
+ }, [isLoading])
103
113
 
104
114
  // Pick a fresh gerund when loading starts
105
115
  useEffect(() => {
106
116
  if (isLoading) {
107
- setVerb(t(pick(LOADING_KEYS)))
117
+ setVerb(tRef.current(pick(LOADING_KEYS)))
108
118
  setCompletionVerb(null)
109
119
  }
110
- }, [isLoading, t])
120
+ }, [isLoading])
111
121
 
112
122
  // Flash a past participle when loading stops
113
123
  useEffect(() => {
114
124
  if (prevLoading.current === true && isLoading === false) {
115
- setCompletionVerb(t(pick(COMPLETED_KEYS)))
125
+ setCompletionVerb(tRef.current(pick(COMPLETED_KEYS)))
116
126
  const timer = setTimeout(() => setCompletionVerb(null), 1500)
127
+ prevLoading.current = isLoading
117
128
  return () => clearTimeout(timer)
118
129
  }
119
130
  prevLoading.current = isLoading
120
- }, [isLoading, t])
131
+ }, [isLoading])
121
132
 
122
133
  const [vimMode, setVimMode] = useState<VimMode>('insert')
123
134
  // NOTE: Dual mode state — React state (vimMode) drives UI re-renders (prompt color,
@@ -195,6 +206,39 @@ export function InputBar({
195
206
  return
196
207
  }
197
208
 
209
+ // ── Arrow-key history navigation (insert mode only, Claude Code parity) ──
210
+ if (vimEngine.current.mode === 'insert' && !searchMode) {
211
+ if (key.upArrow || key.downArrow) {
212
+ // Ignore if picker is active (command picker handles its own arrows)
213
+ if (value.startsWith('/')) return
214
+
215
+ if (key.upArrow) {
216
+ if (submittedHistory.length === 0) return
217
+ // Save current draft the first time we enter history browsing
218
+ if (historyIndexRef.current === -1) {
219
+ savedDraftRef.current = value
220
+ }
221
+ const newIndex = Math.min(historyIndexRef.current + 1, submittedHistory.length - 1)
222
+ historyIndexRef.current = newIndex
223
+ setValue(submittedHistory[submittedHistory.length - 1 - newIndex]!)
224
+ return
225
+ }
226
+ if (key.downArrow) {
227
+ if (historyIndexRef.current === -1) return
228
+ const newIndex = historyIndexRef.current - 1
229
+ historyIndexRef.current = newIndex
230
+ if (newIndex === -1) {
231
+ // Back to the original draft
232
+ setValue(savedDraftRef.current)
233
+ savedDraftRef.current = ''
234
+ } else {
235
+ setValue(submittedHistory[submittedHistory.length - 1 - newIndex]!)
236
+ }
237
+ return
238
+ }
239
+ }
240
+ }
241
+
198
242
  if (vimEngine.current.mode !== 'normal') return
199
243
 
200
244
  // Handle search mode — collect query characters
@@ -288,6 +332,10 @@ export function InputBar({
288
332
 
289
333
  const handleSubmit = (val: string) => {
290
334
  if (!val.trim() || isLoading) return
335
+ // Save to message history for arrow-key navigation
336
+ setSubmittedHistory((prev) => [...prev, val])
337
+ historyIndexRef.current = -1
338
+ savedDraftRef.current = ''
291
339
  onSubmit(val)
292
340
  setValue('')
293
341
  setPickerActive(false)
@@ -327,6 +375,11 @@ export function InputBar({
327
375
  onChange={(val) => {
328
376
  // Block text changes during search mode — keys go to search query
329
377
  if (searchMode) return
378
+ // Reset history browsing when user starts typing
379
+ if (historyIndexRef.current !== -1) {
380
+ historyIndexRef.current = -1
381
+ savedDraftRef.current = ''
382
+ }
330
383
  setValue(val)
331
384
  }}
332
385
  onSubmit={handleSubmit}