@miphamai/cli 0.85.5 → 0.85.6

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.
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react'
1
+ import React, { useEffect, useState } from 'react'
2
2
  import { Box, Text } from 'ink'
3
3
  import { basename } from 'node:path'
4
4
  import { findGraftStats, type GraftStats } from '../shared/graft-stats'
@@ -13,17 +13,46 @@ function freshnessSegment(stats: GraftStats): { label: string; color: string } {
13
13
  return { label: '✓ synced', color: 'blue' }
14
14
  }
15
15
 
16
+ /**
17
+ * 只有**真变了**才换 state —— 否则每秒一次 setState 会把整棵树重渲染一遍。
18
+ * 两侧都出自 `findGraftStats` 的同一个 return 字面量,键序恒等,故字符串比较是稳的;
19
+ * 用整体比较而不是逐字段比,是为了将来给 `GraftStats` 加字段时**不必回来补这一行**。
20
+ */
21
+ function sameStats(a: GraftStats | null, b: GraftStats | null): boolean {
22
+ if (a === b) return true
23
+ if (!a || !b) return false
24
+ return JSON.stringify(a) === JSON.stringify(b)
25
+ }
26
+
27
+ /** 重读间隔。与 `goal-progress` / `workflow-progress` 同法(那两处也是 1s 一拍)。 */
28
+ const STATS_REFRESH_MS = 1000
29
+
16
30
  /**
17
31
  * Bottom-of-screen graft status line — mirrors graft's own "◤ graft · …" bar:
18
32
  * ◤ graft · N nodes / E edges · ✓ synced · ~T tok saved
19
33
  * ▸ ctx X% · last: file
20
- * Reads `graft/.cache/stats.json` (walking up to the repo root) once at mount;
21
- * the session's tok-saved total is read live each render (the app re-renders on
22
- * the agent tick). Renders nothing only when there's no graft graph AND no known
23
- * ctx% — the ctx% line shows even when graft isn't built in this directory.
34
+ *
35
+ * `graft/.cache/stats.json`(向上走到仓库根)是**别人在后台写的**:graft 自己重建索引时
36
+ * 先落 `syncing: true`,建完再落回来。挂载时读一次就永远说那一拍的话 ⇒ 启动正好撞上索引
37
+ * 重建的话,页脚会一直挂着 `syncing…`,而磁盘上早已 `"syncing": false` ——「有人的读数与
38
+ * 没人读的那个对象不一致」。故这里**按拍重读**。
39
+ *
40
+ * 别把它降成「每次渲染读一遍」:那要靠别的组件把这一棵带着重渲染(敲键盘 / agent tick)
41
+ * 才生效,而索引建完的那一刻**恰恰可能一个键都没有** —— 页脚会一直撒谎到用户下次敲键。
42
+ *
43
+ * 其余同前:tok-saved 每次渲染实时读;只在有 graft 图或有已知 ctx% 时渲染 —— ctx% 行
44
+ * 在本目录没建过 graft 时也照常显示。
24
45
  */
25
46
  export function GraftStatusLine({ cwd, ctxPct }: { cwd: string; ctxPct?: number }) {
26
- const [stats] = useState(() => findGraftStats(cwd))
47
+ const [stats, setStats] = useState(() => findGraftStats(cwd))
48
+
49
+ useEffect(() => {
50
+ const timer = setInterval(() => {
51
+ const next = findGraftStats(cwd)
52
+ setStats((prev) => (sameStats(prev, next) ? prev : next))
53
+ }, STATS_REFRESH_MS)
54
+ return () => clearInterval(timer)
55
+ }, [cwd])
27
56
  const showCtx = typeof ctxPct === 'number'
28
57
  if (!stats && !showCtx) return null
29
58
  const fresh = stats ? freshnessSegment(stats) : null
package/src/ui/input.tsx CHANGED
@@ -324,7 +324,12 @@ export function InputBar({
324
324
  const suggestionReqIdRef = useRef(0)
325
325
 
326
326
  // 统一清理:清 suggestion + 使在途/待发请求失效 + 取消防抖定时器。
327
- // Escape / handleSubmit / 翻历史三处共用,避免各自手写漏掉某一环(rule of three)。
327
+ // Escape / handleSubmit / 翻历史 / **Tab 接受** 四处共用,避免各自手写漏掉某一环。
328
+ // Tab 那一处原先只手写 `setSuggestion(null)`(ROADMAP D8 缺口③):今天**不可达**
329
+ // ——有建议显示就说明上次请求已结束、定时器早烧掉了——但「接受后继续续写」一加上,
330
+ // 那个漏掉的环立刻变成洞。走同一入口,洞就不存在了。
331
+ // (`onChange` 里另有一处内联的同样三步,**不共用**:那里要的是把 reqId 推成**新**的,
332
+ // 与这里的「作废」是两件事。)
328
333
  const clearSuggestion = () => {
329
334
  setSuggestion(null)
330
335
  suggestionReqIdRef.current++
@@ -411,7 +416,7 @@ export function InputBar({
411
416
  const next = valueRef.current + suggestion
412
417
  setValue(next)
413
418
  valueRef.current = next
414
- setSuggestion(null)
419
+ clearSuggestion()
415
420
  return
416
421
  }
417
422
  // Ctrl+P → toggle model picker