codebase-cli 2.0.0-pre.4 → 2.0.0-pre.40

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 (111) hide show
  1. package/dist/agent/agent.js +43 -15
  2. package/dist/agent/agent.js.map +1 -1
  3. package/dist/agent/config.js +61 -20
  4. package/dist/agent/config.js.map +1 -1
  5. package/dist/agent/events.bench.js +84 -0
  6. package/dist/agent/events.bench.js.map +1 -0
  7. package/dist/agent/prompt-suggestion.js +141 -0
  8. package/dist/agent/prompt-suggestion.js.map +1 -0
  9. package/dist/agent/system-prompt.js +15 -0
  10. package/dist/agent/system-prompt.js.map +1 -1
  11. package/dist/app-server/protocol.js +7 -0
  12. package/dist/app-server/protocol.js.map +1 -0
  13. package/dist/app-server/server.js +241 -0
  14. package/dist/app-server/server.js.map +1 -0
  15. package/dist/auth/cli.js +1 -1
  16. package/dist/auth/cli.js.map +1 -1
  17. package/dist/auth/credentials.js +33 -2
  18. package/dist/auth/credentials.js.map +1 -1
  19. package/dist/auth/flow.js +145 -24
  20. package/dist/auth/flow.js.map +1 -1
  21. package/dist/auth/token-manager.js +73 -0
  22. package/dist/auth/token-manager.js.map +1 -0
  23. package/dist/cli.js +56 -1
  24. package/dist/cli.js.map +1 -1
  25. package/dist/commands/builtins.js +160 -14
  26. package/dist/commands/builtins.js.map +1 -1
  27. package/dist/commands/registry.js +46 -1
  28. package/dist/commands/registry.js.map +1 -1
  29. package/dist/compaction/monitor.js +38 -0
  30. package/dist/compaction/monitor.js.map +1 -0
  31. package/dist/config/types.js.map +1 -1
  32. package/dist/glue/client.js +12 -2
  33. package/dist/glue/client.js.map +1 -1
  34. package/dist/hooks/manager.js +8 -2
  35. package/dist/hooks/manager.js.map +1 -1
  36. package/dist/memory/store.js +2 -3
  37. package/dist/memory/store.js.map +1 -1
  38. package/dist/plan/run-flow.js +89 -0
  39. package/dist/plan/run-flow.js.map +1 -0
  40. package/dist/projects/cli.js +92 -0
  41. package/dist/projects/cli.js.map +1 -0
  42. package/dist/projects/client.js +120 -0
  43. package/dist/projects/client.js.map +1 -0
  44. package/dist/projects/types.js +2 -0
  45. package/dist/projects/types.js.map +1 -0
  46. package/dist/skills/platform-loader.js +133 -38
  47. package/dist/skills/platform-loader.js.map +1 -1
  48. package/dist/tools/__test__/mock-tool-context.js +31 -0
  49. package/dist/tools/__test__/mock-tool-context.js.map +1 -0
  50. package/dist/tools/file-state-cache.js.map +1 -1
  51. package/dist/tools/read-file.js +7 -2
  52. package/dist/tools/read-file.js.map +1 -1
  53. package/dist/ui/App.js +109 -110
  54. package/dist/ui/App.js.map +1 -1
  55. package/dist/ui/CompactionBanner.js +23 -0
  56. package/dist/ui/CompactionBanner.js.map +1 -0
  57. package/dist/ui/FirstRunSetup.js +66 -14
  58. package/dist/ui/FirstRunSetup.js.map +1 -1
  59. package/dist/ui/Input.js +370 -20
  60. package/dist/ui/Input.js.map +1 -1
  61. package/dist/ui/Markdown.js +286 -0
  62. package/dist/ui/Markdown.js.map +1 -0
  63. package/dist/ui/Message.js +110 -48
  64. package/dist/ui/Message.js.map +1 -1
  65. package/dist/ui/MessageList.js +100 -3
  66. package/dist/ui/MessageList.js.map +1 -1
  67. package/dist/ui/Permission.js +43 -20
  68. package/dist/ui/Permission.js.map +1 -1
  69. package/dist/ui/PixelC.js +25 -0
  70. package/dist/ui/PixelC.js.map +1 -0
  71. package/dist/ui/Status.js +267 -7
  72. package/dist/ui/Status.js.map +1 -1
  73. package/dist/ui/Throbber.js +11 -7
  74. package/dist/ui/Throbber.js.map +1 -1
  75. package/dist/ui/Welcome.js +59 -0
  76. package/dist/ui/Welcome.js.map +1 -0
  77. package/dist/ui/attachments.js +68 -0
  78. package/dist/ui/attachments.js.map +1 -0
  79. package/dist/ui/debug-input.js +44 -0
  80. package/dist/ui/debug-input.js.map +1 -0
  81. package/dist/ui/diff-summary.js +171 -0
  82. package/dist/ui/diff-summary.js.map +1 -0
  83. package/dist/ui/highlight.js +324 -0
  84. package/dist/ui/highlight.js.map +1 -0
  85. package/dist/ui/history-store.js +60 -0
  86. package/dist/ui/history-store.js.map +1 -0
  87. package/dist/ui/input-state.js +125 -1
  88. package/dist/ui/input-state.js.map +1 -1
  89. package/dist/ui/path-complete.js +102 -0
  90. package/dist/ui/path-complete.js.map +1 -0
  91. package/dist/ui/paths.js +41 -0
  92. package/dist/ui/paths.js.map +1 -0
  93. package/dist/ui/shell-escape.js +42 -0
  94. package/dist/ui/shell-escape.js.map +1 -0
  95. package/dist/ui/terminal-restore.js +83 -0
  96. package/dist/ui/terminal-restore.js.map +1 -0
  97. package/dist/ui/terminal-title.js +21 -0
  98. package/dist/ui/terminal-title.js.map +1 -0
  99. package/dist/ui/tool-call-line.js +83 -0
  100. package/dist/ui/tool-call-line.js.map +1 -0
  101. package/dist/ui/tool-labels.js +194 -0
  102. package/dist/ui/tool-labels.js.map +1 -0
  103. package/dist/ui/truncated-output.js +42 -0
  104. package/dist/ui/truncated-output.js.map +1 -0
  105. package/dist/ui/use-coalesced-agent-events.js +70 -0
  106. package/dist/ui/use-coalesced-agent-events.js.map +1 -0
  107. package/dist/ui/use-prompt-suggestion.js +52 -0
  108. package/dist/ui/use-prompt-suggestion.js.map +1 -0
  109. package/dist/ui/wrapped-lines.js +18 -0
  110. package/dist/ui/wrapped-lines.js.map +1 -0
  111. package/package.json +4 -1
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Static, useStdout } from "ink";
3
- import { useEffect, useState } from "react";
3
+ import { useEffect, useMemo, useState } from "react";
4
4
  import { Message } from "./Message.js";
5
5
  /**
6
6
  * Static finalized history + a live streaming pane. Ink's <Static> renders
@@ -9,10 +9,93 @@ import { Message } from "./Message.js";
9
9
  *
10
10
  * Threads the live terminal width down to Message so its body pre-wraps
11
11
  * at word boundaries — see ui/wrap.ts for why we want manual wrap.
12
+ *
13
+ * Note: the static-rendered finalized messages don't see live `tools`
14
+ * updates after they're committed — that's fine because by the time a
15
+ * message is finalized its tool calls have completed. The streaming
16
+ * pane (which DOES update) is where the live spinner lives.
12
17
  */
13
- export function MessageList({ messages, streaming }) {
18
+ export function MessageList({ messages, streaming, tools }) {
14
19
  const width = useTerminalWidth();
15
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: messages.map((message, index) => ({ message, key: indexKey(message, index) })), children: ({ message, key }) => (_jsx(Box, { marginBottom: 1, children: _jsx(Message, { message: message, width: width }) }, key)) }), streaming ? (_jsx(Box, { marginBottom: 1, children: _jsx(Message, { message: streaming, streaming: true, width: width }) })) : null] }));
20
+ const rows = useTerminalRows();
21
+ // Cap how many lines the streaming pane renders. The big jank class
22
+ // users hit is: streaming message grows past the visible viewport,
23
+ // every token update writes more lines than before, and the terminal
24
+ // scrolls them down — yanking the user's manual scroll position to
25
+ // the bottom on every keystroke from the model. By trimming the
26
+ // streamed body to a bounded *tail* (the most recent N text lines),
27
+ // the rendered region is constant-height after it fills, log-update
28
+ // clears and rewrites the same number of rows every time, and the
29
+ // terminal stops fighting the scrollbar. The full message goes into
30
+ // <Static> once finalized so terminal scrollback still has the rest.
31
+ //
32
+ // Reserve a chunk for chrome (input bar, status, welcome banner).
33
+ // 12 rows is conservative — leaves enough room that the user can
34
+ // still see "the agent is doing something" without the tree ever
35
+ // trying to exceed the viewport.
36
+ const streamingTailRows = Math.max(8, rows - 12);
37
+ const visibleStreaming = useMemo(() => (streaming ? clipToTail(streaming, streamingTailRows) : undefined), [streaming, streamingTailRows]);
38
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: messages.map((message, index) => ({ message, key: indexKey(message, index) })), children: ({ message, key }) => (_jsx(Box, { marginBottom: 1, children: _jsx(Message, { message: message, width: width, tools: tools }) }, key)) }), visibleStreaming ? (_jsx(Box, { marginBottom: 1, children: _jsx(Message, { message: visibleStreaming, streaming: true, width: width, tools: tools }) })) : null] }));
39
+ }
40
+ /**
41
+ * Trim an in-flight assistant message down to ~`maxLines` of trailing
42
+ * content for live rendering. Content blocks are processed back-to-front
43
+ * until we've accumulated enough lines; earlier blocks are dropped or
44
+ * truncated. The original message object is unchanged — this returns a
45
+ * shallow copy with a new `content` array.
46
+ *
47
+ * Tool calls/results stay intact (each counts as ~1 row, replacing a
48
+ * dropped tool call would change the message's meaning to the user).
49
+ * Text blocks at the head of the kept region get a leading-line trim
50
+ * with a one-line "(earlier output trimmed)" marker so it's obvious
51
+ * we're showing a tail.
52
+ */
53
+ function clipToTail(message, maxLines) {
54
+ if (message.role !== "assistant" || !Array.isArray(message.content))
55
+ return message;
56
+ const blocks = message.content;
57
+ const kept = [];
58
+ let remaining = maxLines;
59
+ for (let i = blocks.length - 1; i >= 0; i--) {
60
+ const block = blocks[i];
61
+ if (remaining <= 0)
62
+ break;
63
+ if (block.type === "text" && typeof block.text === "string") {
64
+ const lines = block.text.split("\n");
65
+ if (lines.length <= remaining) {
66
+ kept.unshift(block);
67
+ remaining -= lines.length;
68
+ }
69
+ else {
70
+ const tail = lines.slice(lines.length - remaining).join("\n");
71
+ kept.unshift({
72
+ ...block,
73
+ text: `(earlier output trimmed — full message in scrollback once finished)\n${tail}`,
74
+ });
75
+ remaining = 0;
76
+ }
77
+ }
78
+ else if (block.type === "thinking" && typeof block.thinking === "string") {
79
+ const lines = block.thinking.split("\n");
80
+ if (lines.length <= remaining) {
81
+ kept.unshift(block);
82
+ remaining -= lines.length;
83
+ }
84
+ else {
85
+ const tail = lines.slice(lines.length - remaining).join("\n");
86
+ kept.unshift({ ...block, thinking: tail });
87
+ remaining = 0;
88
+ }
89
+ }
90
+ else {
91
+ // Tool calls / results: keep verbatim, count as 1 row each.
92
+ kept.unshift(block);
93
+ remaining -= 1;
94
+ }
95
+ }
96
+ if (kept.length === blocks.length)
97
+ return message;
98
+ return { ...message, content: kept };
16
99
  }
17
100
  function useTerminalWidth(fallback = 80) {
18
101
  const { stdout } = useStdout();
@@ -28,6 +111,20 @@ function useTerminalWidth(fallback = 80) {
28
111
  }, [stdout, fallback]);
29
112
  return width;
30
113
  }
114
+ function useTerminalRows(fallback = 24) {
115
+ const { stdout } = useStdout();
116
+ const [rows, setRows] = useState(stdout?.rows ?? fallback);
117
+ useEffect(() => {
118
+ if (!stdout)
119
+ return;
120
+ const onResize = () => setRows(stdout.rows ?? fallback);
121
+ stdout.on("resize", onResize);
122
+ return () => {
123
+ stdout.off("resize", onResize);
124
+ };
125
+ }, [stdout, fallback]);
126
+ return rows;
127
+ }
31
128
  function indexKey(message, index) {
32
129
  const ts = "timestamp" in message && typeof message.timestamp === "number" ? message.timestamp : 0;
33
130
  return `${index}:${message.role}:${ts}`;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.js","sourceRoot":"","sources":["../../src/ui/MessageList.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAOvC;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAoB;IACpE,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,OAAO,CACN,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aAC1B,KAAC,MAAM,IAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,YAC3F,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CACtB,KAAC,GAAG,IAAW,YAAY,EAAE,CAAC,YAC7B,KAAC,OAAO,IAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAI,IADlC,GAAG,CAEP,CACN,GACO,EACR,SAAS,CAAC,CAAC,CAAC,CACZ,KAAC,GAAG,IAAC,YAAY,EAAE,CAAC,YACnB,KAAC,OAAO,IAAC,OAAO,EAAE,SAAS,EAAE,SAAS,QAAC,KAAK,EAAE,KAAK,GAAI,GAClD,CACN,CAAC,CAAC,CAAC,IAAI,IACH,CACN,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAQ,GAAG,EAAE;IACtC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAC/B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,IAAI,QAAQ,CAAC,CAAC;IAChE,SAAS,CAAC,GAAG,EAAE;QACd,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,IAAI,QAAQ,CAAC,CAAC;QAC5D,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC9B,OAAO,GAAG,EAAE;YACX,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAChC,CAAC,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,OAAqB,EAAE,KAAa;IACrD,MAAM,EAAE,GAAG,WAAW,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IACnG,OAAO,GAAG,KAAK,IAAI,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"MessageList.js","sourceRoot":"","sources":["../../src/ui/MessageList.tsx"],"names":[],"mappings":"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"}
@@ -1,39 +1,62 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text, useInput } from "ink";
3
+ import { useState } from "react";
3
4
  const RISK_COLOR = {
4
5
  low: "yellow",
5
6
  medium: "yellow",
6
7
  high: "red",
7
8
  };
8
- const RISK_GLYPH = {
9
- low: "?",
10
- medium: "!",
11
- high: "",
9
+ const RISK_LABEL = {
10
+ low: "LOW RISK",
11
+ medium: "REVIEW",
12
+ high: "HIGH RISK",
12
13
  };
14
+ const CHOICES = [
15
+ { label: "Allow", key: "allow-once", hint: "this one time", color: "green", shortcut: "y" },
16
+ { label: "Trust tool", key: "trust-tool", hint: "for the rest of this session", color: "cyan", shortcut: "t" },
17
+ { label: "Trust all", key: "trust-all", hint: "any tool, this session", color: "cyan", shortcut: "a" },
18
+ { label: "Deny", key: "deny", hint: "block this call", color: "red", shortcut: "n" },
19
+ ];
13
20
  /**
14
- * Modal-ish prompt that takes over the input row while a tool call
15
- * awaits user approval. Single-keystroke responses keep it fast:
16
- * y allow once t trust this tool for the session
17
- * a trust everything n / Esc deny
21
+ * Permission prompt bordered box with risk badge, tool summary,
22
+ * collapsed detail, and four arrow-navigable choices. Single-key
23
+ * shortcuts still work (y/t/a/n) for muscle memory; Enter on the
24
+ * highlighted choice for newcomers; Esc maps to Deny.
18
25
  */
19
26
  export function Permission({ request, onRespond }) {
27
+ const [cursor, setCursor] = useState(0);
20
28
  useInput((input, key) => {
21
29
  if (key.escape) {
22
30
  onRespond("deny");
23
31
  return;
24
32
  }
33
+ if (key.return) {
34
+ onRespond(CHOICES[cursor].key);
35
+ return;
36
+ }
37
+ if (key.leftArrow || (key.shift && key.tab)) {
38
+ setCursor((c) => (c - 1 + CHOICES.length) % CHOICES.length);
39
+ return;
40
+ }
41
+ if (key.rightArrow || key.tab || key.downArrow || key.upArrow) {
42
+ setCursor((c) => (c + 1) % CHOICES.length);
43
+ return;
44
+ }
25
45
  const ch = input.toLowerCase();
26
- if (ch === "y")
27
- onRespond("allow-once");
28
- else if (ch === "n")
29
- onRespond("deny");
30
- else if (ch === "t")
31
- onRespond("trust-tool");
32
- else if (ch === "a")
33
- onRespond("trust-all");
46
+ const direct = CHOICES.find((c) => c.shortcut === ch);
47
+ if (direct)
48
+ onRespond(direct.key);
34
49
  });
35
- const color = RISK_COLOR[request.risk];
36
- const glyph = RISK_GLYPH[request.risk];
37
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, marginY: 0, children: [_jsxs(Box, { children: [_jsxs(Text, { color: color, bold: true, children: [glyph, " permission"] }), _jsx(Text, { children: " " }), _jsx(Text, { children: request.summary })] }), request.detail ? (_jsx(Box, { marginLeft: 2, marginY: 0, children: _jsx(Text, { dimColor: true, children: request.detail }) })) : null, _jsx(Box, { marginTop: 0, children: _jsxs(Text, { dimColor: true, children: ["[", _jsx(Text, { color: "green", children: "y" }), "]es \u00B7 [", _jsx(Text, { color: "cyan", children: "t" }), "]rust this tool \u00B7 [", _jsx(Text, { color: "cyan", children: "a" }), "]ll \u00B7 [", _jsx(Text, { color: "red", children: "n" }), "]o"] }) })] }));
50
+ const riskColor = RISK_COLOR[request.risk];
51
+ const riskLabel = RISK_LABEL[request.risk];
52
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: riskColor, paddingX: 1, marginY: 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: riskColor, bold: true, children: riskLabel }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u00B7 permission needed" })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { bold: true, children: request.tool }), _jsx(Text, { dimColor: true, children: " " }), _jsx(Text, { children: request.summary })] }), request.detail ? (_jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Text, { dimColor: true, children: truncate(request.detail, 600) }) })) : null, _jsx(Box, { marginTop: 1, flexDirection: "row", children: CHOICES.map((c, i) => {
53
+ const selected = i === cursor;
54
+ return (_jsxs(Box, { marginRight: 2, children: [_jsxs(Text, { color: selected ? c.color : "gray", bold: selected, children: [selected ? "▸ " : " ", c.label] }), _jsxs(Text, { dimColor: true, children: [" (", c.shortcut, ")"] })] }, c.key));
55
+ }) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [CHOICES[cursor].hint, " \u00B7 \u2190\u2192 Enter \u00B7 y/t/a/n shortcuts \u00B7 Esc to deny"] }) })] }));
56
+ }
57
+ function truncate(s, n) {
58
+ if (s.length <= n)
59
+ return s;
60
+ return `${s.slice(0, n - 1)}…`;
38
61
  }
39
62
  //# sourceMappingURL=Permission.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../src/ui/Permission.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAQ1C,MAAM,UAAU,GAA8C;IAC7D,GAAG,EAAE,QAAQ;IACb,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,KAAK;CACX,CAAC;AAEF,MAAM,UAAU,GAA8C;IAC7D,GAAG,EAAE,GAAG;IACR,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,GAAG;CACT,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,EAAE,OAAO,EAAE,SAAS,EAAmB;IACjE,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YAChB,SAAS,CAAC,MAAM,CAAC,CAAC;YAClB,OAAO;QACR,CAAC;QACD,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAC/B,IAAI,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,YAAY,CAAC,CAAC;aACnC,IAAI,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,MAAM,CAAC,CAAC;aAClC,IAAI,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,YAAY,CAAC,CAAC;aACxC,IAAI,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,WAAW,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,OAAO,CACN,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,aAClD,MAAC,GAAG,eACH,MAAC,IAAI,IAAC,KAAK,EAAE,KAAK,EAAE,IAAI,mBACtB,KAAK,mBACA,EACP,KAAC,IAAI,oBAAS,EACd,KAAC,IAAI,cAAE,OAAO,CAAC,OAAO,GAAQ,IACzB,EACL,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CACjB,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,YAC7B,KAAC,IAAI,IAAC,QAAQ,kBAAE,OAAO,CAAC,MAAM,GAAQ,GACjC,CACN,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YAChB,MAAC,IAAI,IAAC,QAAQ,wBACZ,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,kBAAS,kBAAO,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,kBAAS,8BAC/D,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,kBAAS,kBAAO,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,kBAAS,UACtD,GACF,IACD,CACN,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../src/ui/Permission.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ /**
4
+ * Static pixel-C brand mark. Mirrors `web/public/favicon.svg`:
5
+ * 5-row × 4-col grid, 9 filled pixels (3 top + 3 left + 3 bottom).
6
+ * Each SVG pixel renders as two block chars wide so the C reads
7
+ * proportionally in a 1:2 cell-ratio terminal.
8
+ */
9
+ const FILL = "██";
10
+ const GAP = " ";
11
+ // Row id doubles as the React key. Names describe the C shape so the
12
+ // keys are stable regardless of order (biome's array-index-as-key rule
13
+ // is right in general — but here the rows aren't unique by content
14
+ // (the three FILL rows repeat), so we lean on positional ids).
15
+ const ROWS = [
16
+ { id: "top", text: `${GAP}${FILL}${FILL}${FILL}` },
17
+ { id: "mid-1", text: FILL },
18
+ { id: "mid-2", text: FILL },
19
+ { id: "mid-3", text: FILL },
20
+ { id: "bot", text: `${GAP}${FILL}${FILL}${FILL}` },
21
+ ];
22
+ export function PixelC({ color = "cyan" }) {
23
+ return (_jsx(Box, { flexDirection: "column", children: ROWS.map((row) => (_jsx(Text, { bold: true, color: color, children: row.text }, row.id))) }));
24
+ }
25
+ //# sourceMappingURL=PixelC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PixelC.js","sourceRoot":"","sources":["../../src/ui/PixelC.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC;;;;;GAKG;AAEH,MAAM,IAAI,GAAG,IAAI,CAAC;AAClB,MAAM,GAAG,GAAG,IAAI,CAAC;AAMjB,qEAAqE;AACrE,uEAAuE;AACvE,mEAAmE;AACnE,+DAA+D;AAC/D,MAAM,IAAI,GAA4C;IACrD,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE;IAClD,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE;IAC3B,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE;IAC3B,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE;IAC3B,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE;CAClD,CAAC;AAEF,MAAM,UAAU,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,EAAe;IACrD,OAAO,CACN,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACzB,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAClB,KAAC,IAAI,IAAc,IAAI,QAAC,KAAK,EAAE,KAAK,YAClC,GAAG,CAAC,IAAI,IADC,GAAG,CAAC,EAAE,CAEV,CACP,CAAC,GACG,CACN,CAAC;AACH,CAAC"}
package/dist/ui/Status.js CHANGED
@@ -1,11 +1,35 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { basename } from "node:path";
2
3
  import { Box, Text } from "ink";
4
+ import { useEffect, useRef, useState } from "react";
3
5
  import { Throbber } from "./Throbber.js";
6
+ /** Average chars-per-token across the major model families. Used only as a
7
+ * fallback when the provider doesn't return usage info on message_end. */
8
+ const CHARS_PER_TOKEN = 4;
9
+ /**
10
+ * Playful verbs we cycle through while the agent is thinking. They all
11
+ * read as "the model is working." Kept ASCII-clean so they line up in
12
+ * any terminal font.
13
+ */
14
+ const THINKING_VERBS = [
15
+ "Thinking",
16
+ "Pondering",
17
+ "Synthesizing",
18
+ "Cogitating",
19
+ "Ruminating",
20
+ "Deliberating",
21
+ "Mulling",
22
+ "Marinating",
23
+ "Brewing",
24
+ "Contemplating",
25
+ "Reasoning",
26
+ "Considering",
27
+ ];
4
28
  const STATUS_LABEL = {
5
29
  idle: "ready",
6
- thinking: "thinking",
7
- streaming: "responding",
8
- tool: "tool",
30
+ thinking: "Thinking",
31
+ streaming: "Writing",
32
+ tool: "Working",
9
33
  aborted: "aborted",
10
34
  error: "error",
11
35
  };
@@ -17,12 +41,248 @@ const STATUS_COLOR = {
17
41
  aborted: "red",
18
42
  error: "red",
19
43
  };
20
- export function Status({ state }) {
44
+ /**
45
+ * Bottom status line — spinner + state on the left, model + cwd +
46
+ * context % + cost on the right. Stays on one row in normal terminal
47
+ * widths; the cwd basename is the only dynamic-length piece so we
48
+ * always show what matters.
49
+ */
50
+ export function Status({ state, cwd, contextWindow = 200_000 }) {
21
51
  const busy = state.status === "thinking" || state.status === "streaming" || state.status === "tool";
22
- const label = STATUS_LABEL[state.status];
52
+ const verb = useThinkingVerb(state.status === "thinking");
53
+ let label = state.status === "thinking" ? verb : STATUS_LABEL[state.status];
54
+ if (state.status === "tool") {
55
+ const running = findRunningTool(state);
56
+ if (running)
57
+ label = `${STATUS_LABEL.tool} · ${running}`;
58
+ }
23
59
  const color = STATUS_COLOR[state.status];
60
+ const tokRate = useTokenRate(state);
61
+ const elapsedSec = useBusyElapsed(busy);
24
62
  const u = state.usage;
25
- return (_jsxs(Box, { flexDirection: "column", children: [state.error ? (_jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: "red", children: ["! ", state.error] }) })) : null, _jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [_jsxs(Box, { children: [busy ? (_jsxs(_Fragment, { children: [_jsx(Throbber, { color: color }), _jsx(Text, { children: " " })] })) : null, _jsx(Text, { color: color, children: label })] }), _jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [state.model.provider, "/", state.model.id, " \u00B7 \u2193", u.input, " \u2191", u.output, " $", formatCost(u.cost.total)] }) })] })] }));
63
+ const usedTokens = estimateContextTokens(state);
64
+ const ctxPct = contextWindow > 0 ? Math.min(100, Math.round((usedTokens / contextWindow) * 100)) : 0;
65
+ const cwdLabel = cwd ? basename(cwd) || "/" : "";
66
+ const modelLabel = state.model.name || state.model.id;
67
+ return (_jsxs(Box, { flexDirection: "column", children: [state.error ? _jsx(ErrorCard, { message: state.error }) : null, ctxPct >= 85 ? _jsx(ContextWarning, { pct: ctxPct }) : null, _jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [_jsxs(Box, { children: [busy ? (_jsxs(_Fragment, { children: [_jsx(Throbber, { color: color }), _jsx(Text, { children: " " })] })) : null, _jsx(Text, { color: color, children: label }), elapsedSec !== undefined ? _jsxs(Text, { dimColor: true, children: [" (", elapsedSec, "s)"] }) : null] }), _jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [modelLabel, cwdLabel ? ` · ${cwdLabel}` : "", " \u00B7", " "] }), _jsxs(Text, { color: ctxColor(ctxPct), children: [ctxBar(ctxPct), " ", ctxPct, "%"] }), _jsxs(Text, { dimColor: true, children: [tokRate !== undefined ? ` · ${tokRate} tok/s` : "", " \u00B7 $", formatCost(u.cost.total)] })] })] })] }));
68
+ }
69
+ /**
70
+ * Track how long the agent has been busy. Returns undefined unless the
71
+ * elapsed time has crossed 3 seconds — short turns shouldn't carry an
72
+ * "(0s)" suffix on the status bar. Resets cleanly when the agent
73
+ * goes idle so consecutive turns each start their own timer.
74
+ */
75
+ function useBusyElapsed(busy) {
76
+ const startRef = useRef(undefined);
77
+ const [tick, setTick] = useState(0);
78
+ useEffect(() => {
79
+ if (!busy) {
80
+ startRef.current = undefined;
81
+ return;
82
+ }
83
+ startRef.current = Date.now();
84
+ const id = setInterval(() => setTick((t) => t + 1), 1000);
85
+ return () => clearInterval(id);
86
+ }, [busy]);
87
+ if (!busy || !startRef.current)
88
+ return undefined;
89
+ const elapsed = Math.floor((Date.now() - startRef.current) / 1000);
90
+ void tick;
91
+ return elapsed >= 3 ? elapsed : undefined;
92
+ }
93
+ /**
94
+ * Pluck the most-recently-started in-flight tool so the status bar can
95
+ * say "Working · shell" instead of just "Working". Falls back to no
96
+ * tool name when the map is empty — keeps the bar terse.
97
+ */
98
+ function findRunningTool(state) {
99
+ let best;
100
+ for (const tool of state.tools.values()) {
101
+ if (tool.status !== "running")
102
+ continue;
103
+ if (!best || tool.startedAt > best.startedAt) {
104
+ best = { name: tool.name, startedAt: tool.startedAt };
105
+ }
106
+ }
107
+ return best?.name;
108
+ }
109
+ /**
110
+ * Estimate the live token-output rate during streaming using a 4-second
111
+ * sliding window over recent character growth. This avoids dragging the
112
+ * rate down with the pre-text wait period — a thinking-heavy model that
113
+ * spent 60s reasoning before emitting its first token should show "120
114
+ * tok/s" once it starts streaming, not "5 tok/s averaged with the wait."
115
+ *
116
+ * Returns undefined when not streaming, or when the window doesn't yet
117
+ * have enough samples / delta for the rate to be meaningful (so the bar
118
+ * doesn't flicker noisy values in the first half-second).
119
+ */
120
+ function useTokenRate(state) {
121
+ const samplesRef = useRef([]);
122
+ const charsRef = useRef(0);
123
+ const [, setTick] = useState(0);
124
+ const streaming = state.status === "streaming";
125
+ // Keep the latest char count in a ref so the interval callback always
126
+ // reads the live value rather than the closure-captured one.
127
+ charsRef.current = streaming ? streamingChars(state) : 0;
128
+ useEffect(() => {
129
+ if (!streaming) {
130
+ samplesRef.current = [];
131
+ return;
132
+ }
133
+ const sample = () => {
134
+ const now = Date.now();
135
+ samplesRef.current.push({ t: now, c: charsRef.current });
136
+ const cutoff = now - 4000;
137
+ while (samplesRef.current.length > 0 && samplesRef.current[0].t < cutoff) {
138
+ samplesRef.current.shift();
139
+ }
140
+ setTick((n) => n + 1);
141
+ };
142
+ sample(); // seed immediately
143
+ const id = setInterval(sample, 500);
144
+ return () => clearInterval(id);
145
+ }, [streaming]);
146
+ if (!streaming || samplesRef.current.length < 2)
147
+ return undefined;
148
+ const oldest = samplesRef.current[0];
149
+ const newest = samplesRef.current[samplesRef.current.length - 1];
150
+ const dt = (newest.t - oldest.t) / 1000;
151
+ if (dt < 0.5)
152
+ return undefined;
153
+ const dc = newest.c - oldest.c;
154
+ if (dc < 10)
155
+ return undefined;
156
+ return Math.round(dc / CHARS_PER_TOKEN / dt);
157
+ }
158
+ /**
159
+ * Tokens currently in the model's context, for the status-bar fill meter.
160
+ * Prefers the last-turn's reported `input + cacheRead` from pi-ai, since
161
+ * that's literally what the model saw. Falls back to char-based estimation
162
+ * when the provider strips usage (e.g. some OAuth-fronted proxies) so the
163
+ * bar still grows as the conversation grows. Streaming content is added
164
+ * on top of the prior-turn baseline so the bar visibly fills during a turn
165
+ * instead of jumping at message_end.
166
+ */
167
+ export function estimateContextTokens(state) {
168
+ if (state.turnUsage && state.turnUsage.input + state.turnUsage.cacheRead > 0) {
169
+ const reported = state.turnUsage.input + state.turnUsage.cacheRead;
170
+ const streamingExtra = Math.round(streamingChars(state) / CHARS_PER_TOKEN);
171
+ return reported + streamingExtra;
172
+ }
173
+ let chars = 0;
174
+ for (const msg of state.messages)
175
+ chars += messageChars(msg);
176
+ if (state.streaming)
177
+ chars += messageChars(state.streaming);
178
+ return Math.round(chars / CHARS_PER_TOKEN);
179
+ }
180
+ function messageChars(message) {
181
+ if (typeof message.content === "string")
182
+ return message.content.length;
183
+ if (!Array.isArray(message.content))
184
+ return 0;
185
+ let total = 0;
186
+ for (const block of message.content) {
187
+ if (block.type === "text")
188
+ total += block.text.length;
189
+ else if (block.type === "thinking")
190
+ total += block.thinking.length;
191
+ else if (block.type === "toolCall") {
192
+ total += block.name.length;
193
+ total += JSON.stringify(block.arguments ?? {}).length;
194
+ }
195
+ }
196
+ return total;
197
+ }
198
+ /** Sum the visible text length of all text/thinking blocks in the live streaming message. */
199
+ function streamingChars(state) {
200
+ const m = state.streaming;
201
+ if (!m || m.role !== "assistant")
202
+ return 0;
203
+ let total = 0;
204
+ for (const block of m.content) {
205
+ if (block.type === "text")
206
+ total += block.text.length;
207
+ else if (block.type === "thinking")
208
+ total += block.thinking.length;
209
+ }
210
+ return total;
211
+ }
212
+ /**
213
+ * While the agent is thinking, swap the verb every 3 seconds. We pick
214
+ * the next verb at random (excluding the current one) instead of
215
+ * cycling in order so the same word doesn't reappear at predictable
216
+ * beats. When the status leaves thinking we drop back to the first
217
+ * verb so re-entry starts fresh.
218
+ */
219
+ function useThinkingVerb(active) {
220
+ const [verb, setVerb] = useState(THINKING_VERBS[0]);
221
+ useEffect(() => {
222
+ if (!active) {
223
+ setVerb(THINKING_VERBS[0]);
224
+ return;
225
+ }
226
+ const id = setInterval(() => {
227
+ setVerb((current) => {
228
+ let next = current;
229
+ while (next === current) {
230
+ next = THINKING_VERBS[Math.floor(Math.random() * THINKING_VERBS.length)];
231
+ }
232
+ return next;
233
+ });
234
+ }, 3000);
235
+ return () => clearInterval(id);
236
+ }, [active]);
237
+ return verb;
238
+ }
239
+ /**
240
+ * Banner shown when the context window is past 85%. Suggests /compact
241
+ * so the user can take action before auto-compaction kicks in, and
242
+ * shifts to red past 95% where the next turn might actually trip the
243
+ * model's hard limit.
244
+ */
245
+ function ContextWarning({ pct }) {
246
+ const urgent = pct >= 95;
247
+ return (_jsxs(Box, { paddingX: 1, children: [_jsxs(Text, { color: urgent ? "red" : "yellow", bold: true, children: [urgent ? "⚠" : "•", " ", pct, "% of context used"] }), _jsx(Text, { dimColor: true, children: " \u2014 run /compact to free space" })] }));
248
+ }
249
+ /**
250
+ * Boxed error card. Headers the error with ERROR + a one-line summary,
251
+ * then shows the rest of the message body (if multi-line) in dim text.
252
+ * Fatal errors need visual weight so the user doesn't miss them in a
253
+ * busy transcript.
254
+ */
255
+ function ErrorCard({ message }) {
256
+ const lines = message.split("\n");
257
+ const head = lines[0] ?? message;
258
+ const body = lines.slice(1).filter((l) => l.trim().length > 0);
259
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, marginY: 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: "red", bold: true, children: "ERROR" }), _jsx(Text, { children: " " }), _jsx(Text, { children: head })] }), body.length > 0 ? (_jsx(Box, { flexDirection: "column", marginTop: 1, children: body.map((line, i) => (_jsx(Text, { dimColor: true, children: line }, `err-${i}-${line.slice(0, 12)}`))) })) : null] }));
260
+ }
261
+ /**
262
+ * Render a tiny 6-cell bar for the context-window meter. Eighth-block
263
+ * glyphs give us 48 effective steps in 6 chars — enough resolution
264
+ * that 12% / 25% / 50% all look visibly different. Empty cells stay
265
+ * as a dim track so the bar always reads as a meter, not a slider.
266
+ */
267
+ function ctxBar(pct) {
268
+ const cells = 6;
269
+ const totalEighths = Math.round((pct / 100) * cells * 8);
270
+ const full = Math.floor(totalEighths / 8);
271
+ const remainder = totalEighths - full * 8;
272
+ const partials = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉"];
273
+ let out = "█".repeat(Math.min(full, cells));
274
+ if (full < cells && remainder > 0)
275
+ out += partials[remainder] ?? "";
276
+ while (out.length < cells)
277
+ out += "░";
278
+ return out;
279
+ }
280
+ function ctxColor(pct) {
281
+ if (pct >= 90)
282
+ return "red";
283
+ if (pct >= 75)
284
+ return "yellow";
285
+ return "gray";
26
286
  }
27
287
  function formatCost(value) {
28
288
  if (value === 0)
@@ -1 +1 @@
1
- {"version":3,"file":"Status.js","sourceRoot":"","sources":["../../src/ui/Status.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAMzC,MAAM,YAAY,GAAwC;IACzD,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,YAAY;IACvB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;CACd,CAAC;AAEF,MAAM,YAAY,GAAwC;IACzD,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,QAAQ;IAClB,SAAS,EAAE,MAAM;IACjB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,KAAK;CACZ,CAAC;AAEF,MAAM,UAAU,MAAM,CAAC,EAAE,KAAK,EAAe;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC;IACpG,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;IAEtB,OAAO,CACN,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CACd,KAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,YACf,MAAC,IAAI,IAAC,KAAK,EAAC,KAAK,mBAAI,KAAK,CAAC,KAAK,IAAQ,GACnC,CACN,CAAC,CAAC,CAAC,IAAI,EACR,MAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,EAAE,cAAc,EAAC,eAAe,aAC/C,MAAC,GAAG,eACF,IAAI,CAAC,CAAC,CAAC,CACP,8BACC,KAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,GAAI,EAC1B,KAAC,IAAI,oBAAS,IACZ,CACH,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,YAAG,KAAK,GAAQ,IAC7B,EACN,KAAC,GAAG,cACH,MAAC,IAAI,IAAC,QAAQ,mBACZ,KAAK,CAAC,KAAK,CAAC,QAAQ,OAAG,KAAK,CAAC,KAAK,CAAC,EAAE,oBAAM,CAAC,CAAC,KAAK,aAAI,CAAC,CAAC,MAAM,QAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IACrF,GACF,IACD,IACD,CACN,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAChC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"Status.js","sourceRoot":"","sources":["../../src/ui/Status.tsx"],"names":[],"mappings":"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"}
@@ -1,20 +1,24 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Text } from "ink";
3
3
  import { useEffect, useState } from "react";
4
- const FRAMES = ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"];
5
4
  /**
6
- * Self-throttling spinner. Owns its own interval so the parent's reducer
7
- * state isn't ticked on every frame a hot stream of message_update events
8
- * + a 100ms spinner would otherwise compound into needless full re-renders.
5
+ * 8-frame pulse cycle the codebase pixel-C "scanning" through brightness
6
+ * levels. The compact (1-char) variant cycles a single block-glyph: ░▒▓█▓▒░.
7
+ * The full pixel-C variant scans a highlight row across the C shape.
8
+ *
9
+ * Self-throttling: owns its own interval so the parent's reducer state
10
+ * doesn't tick on every frame. A hot message_update stream + a 100ms
11
+ * spinner would otherwise compound into full re-renders 10× a second.
9
12
  */
10
- export function Throbber({ color = "cyan", intervalMs = 80 }) {
13
+ const COMPACT_FRAMES = ["░", "▒", "", "█", "█", "▓", "▒", "░"];
14
+ export function Throbber({ color = "cyan", intervalMs = 90 }) {
11
15
  const [frame, setFrame] = useState(0);
12
16
  useEffect(() => {
13
17
  const id = setInterval(() => {
14
- setFrame((f) => (f + 1) % FRAMES.length);
18
+ setFrame((f) => (f + 1) % COMPACT_FRAMES.length);
15
19
  }, intervalMs);
16
20
  return () => clearInterval(id);
17
21
  }, [intervalMs]);
18
- return _jsx(Text, { color: color, children: FRAMES[frame] });
22
+ return _jsx(Text, { color: color, children: COMPACT_FRAMES[frame] });
19
23
  }
20
24
  //# sourceMappingURL=Throbber.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Throbber.js","sourceRoot":"","sources":["../../src/ui/Throbber.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAOxD;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,UAAU,GAAG,EAAE,EAAiB;IAC1E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEtC,SAAS,CAAC,GAAG,EAAE;QACd,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE;YAC3B,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC,EAAE,UAAU,CAAC,CAAC;QACf,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,OAAO,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,YAAG,MAAM,CAAC,KAAK,CAAC,GAAQ,CAAC;AACnD,CAAC"}
1
+ {"version":3,"file":"Throbber.js","sourceRoot":"","sources":["../../src/ui/Throbber.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C;;;;;;;;GAQG;AAEH,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAOhE,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,UAAU,GAAG,EAAE,EAAiB;IAC1E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEtC,SAAS,CAAC,GAAG,EAAE;QACd,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE;YAC3B,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QAClD,CAAC,EAAE,UAAU,CAAC,CAAC;QACf,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,OAAO,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,YAAG,cAAc,CAAC,KAAK,CAAC,GAAQ,CAAC;AAC3D,CAAC"}