@zhijiewang/openharness 2.0.0 → 2.3.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/README.md +4 -4
- package/dist/DeferredTool.js +3 -1
- package/dist/Tool.d.ts +1 -1
- package/dist/agents/roles.js +58 -62
- package/dist/commands/cybergotchi.d.ts +1 -1
- package/dist/commands/cybergotchi.js +30 -30
- package/dist/commands/index.js +360 -122
- package/dist/components/App.d.ts +1 -1
- package/dist/components/App.js +6 -6
- package/dist/components/CompanionFooter.d.ts +1 -1
- package/dist/components/CompanionFooter.js +6 -8
- package/dist/components/CybergotchiBubble.js +5 -5
- package/dist/components/CybergotchiPanel.d.ts +1 -1
- package/dist/components/CybergotchiPanel.js +7 -7
- package/dist/components/CybergotchiPanelConnected.js +2 -2
- package/dist/components/CybergotchiSetup.js +26 -24
- package/dist/components/CybergotchiSprite.d.ts +1 -1
- package/dist/components/CybergotchiSprite.js +8 -12
- package/dist/components/DiffView.d.ts +1 -1
- package/dist/components/DiffView.js +10 -10
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/InitWizard.js +65 -33
- package/dist/components/Markdown.js +2 -4
- package/dist/components/Messages.js +4 -4
- package/dist/components/PermissionPrompt.d.ts +1 -1
- package/dist/components/PermissionPrompt.js +15 -17
- package/dist/components/REPL.d.ts +1 -1
- package/dist/components/REPL.js +74 -49
- package/dist/components/Spinner.js +2 -2
- package/dist/components/TextInput.js +35 -29
- package/dist/components/ToolCallDisplay.js +3 -5
- package/dist/cybergotchi/bones.d.ts +1 -1
- package/dist/cybergotchi/bones.js +8 -8
- package/dist/cybergotchi/config.d.ts +2 -2
- package/dist/cybergotchi/config.js +13 -13
- package/dist/cybergotchi/events.d.ts +5 -5
- package/dist/cybergotchi/events.js +7 -7
- package/dist/cybergotchi/needs.d.ts +2 -2
- package/dist/cybergotchi/needs.js +7 -9
- package/dist/cybergotchi/personality.d.ts +2 -2
- package/dist/cybergotchi/personality.js +2 -2
- package/dist/cybergotchi/species.d.ts +1 -1
- package/dist/cybergotchi/species.js +145 -217
- package/dist/cybergotchi/speech.d.ts +2 -2
- package/dist/cybergotchi/speech.js +43 -43
- package/dist/cybergotchi/types.d.ts +4 -4
- package/dist/cybergotchi/types.js +26 -26
- package/dist/cybergotchi/useCybergotchi.d.ts +1 -1
- package/dist/cybergotchi/useCybergotchi.js +29 -25
- package/dist/git/index.js +11 -9
- package/dist/harness/checkpoints.js +29 -21
- package/dist/harness/config.d.ts +12 -2
- package/dist/harness/config.js +15 -9
- package/dist/harness/context-warning.d.ts +1 -1
- package/dist/harness/context-warning.js +1 -1
- package/dist/harness/cost.js +1 -1
- package/dist/harness/credentials.js +13 -13
- package/dist/harness/hooks.js +7 -5
- package/dist/harness/keybindings.js +20 -18
- package/dist/harness/marketplace.d.ts +3 -3
- package/dist/harness/marketplace.js +55 -42
- package/dist/harness/memory.d.ts +23 -5
- package/dist/harness/memory.js +142 -41
- package/dist/harness/onboarding.js +30 -10
- package/dist/harness/plugins.d.ts +9 -1
- package/dist/harness/plugins.js +54 -30
- package/dist/harness/rules.js +12 -7
- package/dist/harness/sandbox.d.ts +34 -0
- package/dist/harness/sandbox.js +104 -0
- package/dist/harness/session-db.d.ts +55 -0
- package/dist/harness/session-db.js +165 -0
- package/dist/harness/session.d.ts +1 -1
- package/dist/harness/session.js +34 -15
- package/dist/harness/store.d.ts +3 -3
- package/dist/harness/store.js +6 -4
- package/dist/harness/submit-handler.d.ts +4 -4
- package/dist/harness/submit-handler.js +57 -21
- package/dist/harness/telemetry.d.ts +1 -1
- package/dist/harness/telemetry.js +23 -19
- package/dist/harness/traces.d.ts +2 -2
- package/dist/harness/traces.js +44 -33
- package/dist/harness/verification.d.ts +1 -1
- package/dist/harness/verification.js +50 -44
- package/dist/lsp/client.js +44 -40
- package/dist/main.js +100 -59
- package/dist/mcp/DeferredMcpTool.d.ts +4 -4
- package/dist/mcp/DeferredMcpTool.js +9 -5
- package/dist/mcp/McpTool.d.ts +4 -4
- package/dist/mcp/McpTool.js +8 -4
- package/dist/mcp/client.d.ts +2 -2
- package/dist/mcp/client.js +21 -21
- package/dist/mcp/loader.d.ts +1 -1
- package/dist/mcp/loader.js +17 -12
- package/dist/mcp/registry.d.ts +3 -3
- package/dist/mcp/registry.js +97 -97
- package/dist/mcp/schema.d.ts +1 -1
- package/dist/mcp/schema.js +16 -16
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +21 -21
- package/dist/mcp/types.d.ts +3 -3
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +10 -9
- package/dist/providers/base.d.ts +1 -1
- package/dist/providers/index.js +10 -3
- package/dist/providers/llamacpp.d.ts +2 -2
- package/dist/providers/llamacpp.js +1 -3
- package/dist/providers/ollama.d.ts +2 -2
- package/dist/providers/ollama.js +3 -4
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +3 -5
- package/dist/providers/openrouter.d.ts +2 -2
- package/dist/providers/router.d.ts +1 -1
- package/dist/providers/router.js +7 -7
- package/dist/query/compress.d.ts +2 -2
- package/dist/query/compress.js +22 -21
- package/dist/query/context-manager.d.ts +2 -2
- package/dist/query/context-manager.js +8 -11
- package/dist/query/errors.js +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +30 -22
- package/dist/query/tools.js +15 -12
- package/dist/query/types.d.ts +1 -1
- package/dist/query.d.ts +1 -1
- package/dist/query.js +1 -1
- package/dist/remote/auth.d.ts +2 -2
- package/dist/remote/auth.js +8 -8
- package/dist/remote/server.d.ts +3 -3
- package/dist/remote/server.js +60 -60
- package/dist/renderer/cells.js +9 -9
- package/dist/renderer/colors.js +24 -6
- package/dist/renderer/diff.d.ts +2 -2
- package/dist/renderer/diff.js +27 -19
- package/dist/renderer/differ.d.ts +1 -1
- package/dist/renderer/differ.js +9 -9
- package/dist/renderer/image.js +19 -19
- package/dist/renderer/index.d.ts +6 -6
- package/dist/renderer/index.js +163 -93
- package/dist/renderer/input.js +66 -48
- package/dist/renderer/layout.d.ts +6 -6
- package/dist/renderer/layout.js +163 -124
- package/dist/renderer/markdown.d.ts +2 -2
- package/dist/renderer/markdown.js +173 -54
- package/dist/renderer/session-browser.d.ts +2 -2
- package/dist/renderer/session-browser.js +19 -21
- package/dist/repl.d.ts +5 -5
- package/dist/repl.js +300 -198
- package/dist/sdk/index.d.ts +8 -7
- package/dist/sdk/index.js +59 -42
- package/dist/services/AgentDispatcher.d.ts +3 -3
- package/dist/services/AgentDispatcher.js +33 -29
- package/dist/services/CronExecutor.d.ts +4 -4
- package/dist/services/CronExecutor.js +12 -8
- package/dist/services/EvaluatorLoop.d.ts +3 -3
- package/dist/services/EvaluatorLoop.js +29 -21
- package/dist/services/MetaHarness.d.ts +1 -1
- package/dist/services/MetaHarness.js +41 -33
- package/dist/services/PipelineExecutor.d.ts +1 -1
- package/dist/services/PipelineExecutor.js +23 -25
- package/dist/services/SkillExtractor.d.ts +43 -0
- package/dist/services/SkillExtractor.js +143 -0
- package/dist/services/StreamingToolExecutor.d.ts +2 -2
- package/dist/services/StreamingToolExecutor.js +11 -7
- package/dist/services/a2a.d.ts +8 -8
- package/dist/services/a2a.js +44 -34
- package/dist/services/agent-messaging.d.ts +33 -15
- package/dist/services/agent-messaging.js +65 -13
- package/dist/services/cron.js +16 -16
- package/dist/tools/AgentTool/index.d.ts +5 -2
- package/dist/tools/AgentTool/index.js +35 -15
- package/dist/tools/AskUserTool/index.js +1 -1
- package/dist/tools/BashTool/index.d.ts +2 -2
- package/dist/tools/BashTool/index.js +18 -10
- package/dist/tools/CronTool/index.d.ts +2 -2
- package/dist/tools/CronTool/index.js +30 -12
- package/dist/tools/DiagnosticsTool/index.js +28 -22
- package/dist/tools/EnterPlanModeTool/index.js +93 -14
- package/dist/tools/EnterWorktreeTool/index.js +7 -3
- package/dist/tools/ExitPlanModeTool/index.d.ts +22 -1
- package/dist/tools/ExitPlanModeTool/index.js +20 -5
- package/dist/tools/ExitWorktreeTool/index.js +11 -4
- package/dist/tools/FileEditTool/index.js +3 -5
- package/dist/tools/FileReadTool/index.js +16 -10
- package/dist/tools/FileWriteTool/index.js +2 -2
- package/dist/tools/GlobTool/index.js +5 -9
- package/dist/tools/GrepTool/index.d.ts +2 -2
- package/dist/tools/GrepTool/index.js +14 -9
- package/dist/tools/ImageReadTool/index.js +2 -2
- package/dist/tools/KillProcessTool/index.js +11 -7
- package/dist/tools/LSTool/index.js +3 -3
- package/dist/tools/MemoryTool/index.d.ts +11 -11
- package/dist/tools/MemoryTool/index.js +28 -14
- package/dist/tools/MonitorTool/index.d.ts +2 -2
- package/dist/tools/MonitorTool/index.js +24 -19
- package/dist/tools/MultiEditTool/index.js +9 -5
- package/dist/tools/NotebookEditTool/index.js +3 -3
- package/dist/tools/ParallelAgentTool/index.d.ts +4 -4
- package/dist/tools/ParallelAgentTool/index.js +12 -6
- package/dist/tools/PipelineTool/index.d.ts +4 -4
- package/dist/tools/PipelineTool/index.js +3 -3
- package/dist/tools/PowerShellTool/index.js +10 -6
- package/dist/tools/RemoteTriggerTool/index.js +8 -4
- package/dist/tools/ScheduleWakeupTool/index.d.ts +42 -0
- package/dist/tools/ScheduleWakeupTool/index.js +115 -0
- package/dist/tools/SendMessageTool/index.js +25 -7
- package/dist/tools/SessionSearchTool/index.d.ts +15 -0
- package/dist/tools/SessionSearchTool/index.js +36 -0
- package/dist/tools/SkillTool/index.d.ts +3 -0
- package/dist/tools/SkillTool/index.js +39 -9
- package/dist/tools/TaskCreateTool/index.d.ts +2 -2
- package/dist/tools/TaskCreateTool/index.js +2 -2
- package/dist/tools/TaskGetTool/index.js +2 -2
- package/dist/tools/TaskListTool/index.js +3 -5
- package/dist/tools/TaskOutputTool/index.js +2 -2
- package/dist/tools/TaskStopTool/index.js +3 -3
- package/dist/tools/TaskUpdateTool/index.d.ts +4 -4
- package/dist/tools/TaskUpdateTool/index.js +2 -2
- package/dist/tools/ToolSearchTool/index.js +9 -6
- package/dist/tools/WebFetchTool/index.js +1 -1
- package/dist/tools/WebSearchTool/index.js +2 -6
- package/dist/tools.js +31 -30
- package/dist/types/permissions.js +15 -9
- package/dist/utils/bash-safety.d.ts +1 -1
- package/dist/utils/bash-safety.js +64 -54
- package/dist/utils/diff-algorithm.d.ts +3 -3
- package/dist/utils/diff-algorithm.js +7 -7
- package/dist/utils/fs.js +3 -3
- package/dist/utils/safe-env.js +1 -1
- package/dist/utils/theme-data.d.ts +1 -1
- package/dist/utils/theme-data.js +1 -1
- package/dist/utils/theme.d.ts +1 -1
- package/dist/utils/theme.js +1 -1
- package/dist/utils/tool-summary.d.ts +1 -1
- package/dist/utils/tool-summary.js +27 -9
- package/package.json +10 -3
package/dist/renderer/layout.js
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
* Layout engine — rasterizes application state into a CellGrid.
|
|
3
3
|
* Split screen: messages area (top) + footer (bottom).
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { renderDiff } from
|
|
7
|
-
import { isImageOutput, renderImageInline } from
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { getTheme } from "../utils/theme-data.js";
|
|
6
|
+
import { renderDiff } from "./diff.js";
|
|
7
|
+
import { isImageOutput, renderImageInline } from "./image.js";
|
|
8
|
+
import { measureMarkdown, renderMarkdown } from "./markdown.js";
|
|
9
|
+
import { renderSessionBrowser } from "./session-browser.js";
|
|
10
10
|
// ── Style constants ──
|
|
11
11
|
const s = (fg, bold = false, dim = false) => ({ fg, bg: null, bold, dim, underline: false });
|
|
12
12
|
const S_TEXT = s(null);
|
|
13
13
|
const S_DIM = s(null, false, true);
|
|
14
14
|
const S_BORDER = s(null, false, true);
|
|
15
15
|
const S_BRIGHT = s(null);
|
|
16
|
-
const S_BANNER = s(
|
|
16
|
+
const S_BANNER = s("cyan");
|
|
17
17
|
const S_BANNER_DIM = s(null, false, true);
|
|
18
|
-
const S_AGENT = s(
|
|
19
|
-
const S_KEY_GREEN = s(
|
|
20
|
-
const S_KEY_RED = s(
|
|
21
|
-
const S_KEY_CYAN = s(
|
|
18
|
+
const S_AGENT = s("cyan", true);
|
|
19
|
+
const S_KEY_GREEN = s("green", true);
|
|
20
|
+
const S_KEY_RED = s("red", true);
|
|
21
|
+
const S_KEY_CYAN = s("cyan", true);
|
|
22
22
|
// Theme-dependent styles — lazily initialized on first rasterize() call
|
|
23
23
|
let S_USER;
|
|
24
24
|
let S_ASSISTANT;
|
|
@@ -41,7 +41,7 @@ function ensureStyles() {
|
|
|
41
41
|
S_YELLOW = s(t.tool);
|
|
42
42
|
S_GREEN = s(t.success);
|
|
43
43
|
}
|
|
44
|
-
const SPINNER_CHARS = [
|
|
44
|
+
const SPINNER_CHARS = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
45
45
|
// ── Shared rendering helpers ──
|
|
46
46
|
// Each takes (state, grid, row, limit, ...options) and returns next row.
|
|
47
47
|
function renderBannerSection(state, grid, r, limit, opts) {
|
|
@@ -65,12 +65,12 @@ function renderThinkingSection(state, grid, r, limit) {
|
|
|
65
65
|
return r;
|
|
66
66
|
const w = grid.width;
|
|
67
67
|
if (state.thinkingExpanded) {
|
|
68
|
-
const thinkLines = state.thinkingText.split(
|
|
68
|
+
const thinkLines = state.thinkingText.split("\n").slice(-10);
|
|
69
69
|
const shimmerPos = state.spinnerFrame % 20;
|
|
70
70
|
for (const tLine of thinkLines) {
|
|
71
71
|
if (r >= limit)
|
|
72
72
|
break;
|
|
73
|
-
grid.writeText(r, 0,
|
|
73
|
+
grid.writeText(r, 0, "💭 ", S_DIM);
|
|
74
74
|
const chars = [...tLine];
|
|
75
75
|
for (let ci = 0; ci < chars.length && ci + 3 < w; ci++) {
|
|
76
76
|
grid.setCell(r, 3 + ci, chars[ci], Math.abs(ci - shimmerPos) <= 2 ? S_BRIGHT : S_DIM);
|
|
@@ -79,9 +79,9 @@ function renderThinkingSection(state, grid, r, limit) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
else {
|
|
82
|
-
const lineCount = state.thinkingText.split(
|
|
82
|
+
const lineCount = state.thinkingText.split("\n").length;
|
|
83
83
|
const elapsed = state.thinkingStartedAt ? Math.floor((Date.now() - state.thinkingStartedAt) / 1000) : 0;
|
|
84
|
-
const summary = `∴ Thinking${elapsed > 0 ? ` (${elapsed}s)` :
|
|
84
|
+
const summary = `∴ Thinking${elapsed > 0 ? ` (${elapsed}s)` : ""} — ${lineCount} lines [Ctrl+O expand]`;
|
|
85
85
|
grid.writeText(r, 0, summary, S_DIM);
|
|
86
86
|
r++;
|
|
87
87
|
}
|
|
@@ -96,27 +96,27 @@ function renderThinkingSummarySection(state, grid, r, limit) {
|
|
|
96
96
|
function renderSpinnerSection(state, grid, r, limit) {
|
|
97
97
|
if (!state.loading || state.streamingText || state.thinkingText || r >= limit)
|
|
98
98
|
return r;
|
|
99
|
-
const
|
|
100
|
-
const thinkText =
|
|
99
|
+
const _w = grid.width;
|
|
100
|
+
const thinkText = "Thinking";
|
|
101
101
|
const elapsed = state.thinkingStartedAt ? Math.floor((Date.now() - state.thinkingStartedAt) / 1000) : 0;
|
|
102
102
|
const t = getTheme();
|
|
103
103
|
const baseColor = elapsed > 60 ? t.error : elapsed > 30 ? t.stall : t.primary;
|
|
104
104
|
const shimmerColor = elapsed > 60 ? t.stallShimmer : elapsed > 30 ? t.warning : t.primaryShimmer;
|
|
105
105
|
const baseStyle = { fg: baseColor, bg: null, bold: false, dim: false, underline: false };
|
|
106
|
-
grid.writeText(r, 0,
|
|
106
|
+
grid.writeText(r, 0, "◆ ", { ...baseStyle, bold: true });
|
|
107
107
|
const shimmerPos = state.spinnerFrame % (thinkText.length + 6);
|
|
108
108
|
const shimmerStyle = { fg: shimmerColor, bg: null, bold: true, dim: false, underline: false };
|
|
109
109
|
for (let ci = 0; ci < thinkText.length; ci++) {
|
|
110
110
|
grid.setCell(r, 2 + ci, thinkText[ci], Math.abs(ci - shimmerPos) <= 1 ? shimmerStyle : baseStyle);
|
|
111
111
|
}
|
|
112
|
-
let suffix =
|
|
112
|
+
let suffix = "";
|
|
113
113
|
if (elapsed > 0)
|
|
114
114
|
suffix += ` ${elapsed}s`;
|
|
115
115
|
if (state.tokenCount > 0) {
|
|
116
116
|
const tokStr = state.tokenCount >= 1000 ? `${(state.tokenCount / 1000).toFixed(1)}K` : `${state.tokenCount}`;
|
|
117
117
|
suffix += ` | ${tokStr} tokens`;
|
|
118
118
|
}
|
|
119
|
-
suffix +=
|
|
119
|
+
suffix += "...";
|
|
120
120
|
grid.writeText(r, 2 + thinkText.length, suffix, S_DIM);
|
|
121
121
|
return r + 1;
|
|
122
122
|
}
|
|
@@ -124,7 +124,7 @@ function renderErrorSection(state, grid, r, limit) {
|
|
|
124
124
|
if (!state.errorText || r >= limit)
|
|
125
125
|
return r;
|
|
126
126
|
const w = grid.width;
|
|
127
|
-
grid.writeText(r, 0,
|
|
127
|
+
grid.writeText(r, 0, "✗ ", S_ERROR);
|
|
128
128
|
grid.writeText(r, 2, state.errorText.slice(0, w - 4), S_ERROR);
|
|
129
129
|
return r + 1;
|
|
130
130
|
}
|
|
@@ -133,17 +133,25 @@ function renderToolCallsSection(state, grid, r, limit, opts) {
|
|
|
133
133
|
for (const [callId, tc] of state.toolCalls) {
|
|
134
134
|
if (r >= limit)
|
|
135
135
|
break;
|
|
136
|
-
const isAgent = tc.isAgent || tc.toolName ===
|
|
136
|
+
const isAgent = tc.isAgent || tc.toolName === "Agent" || tc.toolName === "ParallelAgents";
|
|
137
137
|
const icon = isAgent
|
|
138
|
-
?
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
? tc.status === "running"
|
|
139
|
+
? "⊕"
|
|
140
|
+
: tc.status === "done"
|
|
141
|
+
? "◈"
|
|
142
|
+
: "◇"
|
|
143
|
+
: tc.status === "running"
|
|
144
|
+
? SPINNER_CHARS[state.spinnerFrame % SPINNER_CHARS.length]
|
|
145
|
+
: tc.status === "done"
|
|
146
|
+
? "✓"
|
|
147
|
+
: "✗";
|
|
148
|
+
const statusStyle = tc.status === "error" ? S_ERROR : tc.status === "done" ? S_GREEN : isAgent ? S_AGENT : S_YELLOW;
|
|
141
149
|
const nameStyle = isAgent ? S_AGENT : { ...S_YELLOW, bold: true };
|
|
142
150
|
const isExpanded = state.expandedToolCalls.has(callId);
|
|
143
|
-
const canExpand = tc.status !==
|
|
151
|
+
const canExpand = tc.status !== "running" && tc.output;
|
|
144
152
|
// Collapse/expand indicator
|
|
145
153
|
if (canExpand) {
|
|
146
|
-
grid.writeText(r, 0, isExpanded ?
|
|
154
|
+
grid.writeText(r, 0, isExpanded ? "▼" : "▶", S_DIM);
|
|
147
155
|
}
|
|
148
156
|
grid.writeText(r, 2, `${icon} `, statusStyle);
|
|
149
157
|
grid.writeText(r, 4, tc.toolName, nameStyle);
|
|
@@ -151,13 +159,13 @@ function renderToolCallsSection(state, grid, r, limit, opts) {
|
|
|
151
159
|
if (tc.args) {
|
|
152
160
|
const maxArgs = w - afterName - 15;
|
|
153
161
|
if (maxArgs > 5) {
|
|
154
|
-
const argsText = tc.args.slice(0, maxArgs) + (tc.args.length > maxArgs ?
|
|
162
|
+
const argsText = tc.args.slice(0, maxArgs) + (tc.args.length > maxArgs ? "…" : "");
|
|
155
163
|
grid.writeText(r, afterName, argsText, S_DIM);
|
|
156
164
|
afterName += argsText.length + 1;
|
|
157
165
|
}
|
|
158
166
|
}
|
|
159
167
|
// Elapsed time for running tools
|
|
160
|
-
if (tc.status ===
|
|
168
|
+
if (tc.status === "running" && tc.startedAt) {
|
|
161
169
|
const elapsed = Math.floor((Date.now() - tc.startedAt) / 1000);
|
|
162
170
|
if (elapsed > 0) {
|
|
163
171
|
const lineCount = tc.liveOutput?.length ?? 0;
|
|
@@ -166,7 +174,7 @@ function renderToolCallsSection(state, grid, r, limit, opts) {
|
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
176
|
// Result summary for completed tools
|
|
169
|
-
if (tc.status !==
|
|
177
|
+
if (tc.status !== "running" && tc.resultSummary) {
|
|
170
178
|
const elapsed = tc.startedAt ? Math.floor((Date.now() - tc.startedAt) / 1000) : 0;
|
|
171
179
|
const suffix = elapsed > 0 ? `${tc.resultSummary} · ${elapsed}s` : tc.resultSummary;
|
|
172
180
|
grid.writeText(r, Math.min(afterName, w - suffix.length - 2), suffix, S_DIM);
|
|
@@ -178,7 +186,7 @@ function renderToolCallsSection(state, grid, r, limit, opts) {
|
|
|
178
186
|
r++;
|
|
179
187
|
}
|
|
180
188
|
// Live streaming output while running
|
|
181
|
-
if (tc.status ===
|
|
189
|
+
if (tc.status === "running" && tc.liveOutput && tc.liveOutput.length > 0) {
|
|
182
190
|
const overflow = tc.liveOutput.length > opts.maxLiveLines ? tc.liveOutput.length - opts.maxLiveLines : 0;
|
|
183
191
|
if (opts.showOverflow && overflow > 0 && r < limit) {
|
|
184
192
|
grid.writeText(r, 6, `… (${overflow} earlier lines)`, S_DIM);
|
|
@@ -193,7 +201,7 @@ function renderToolCallsSection(state, grid, r, limit, opts) {
|
|
|
193
201
|
}
|
|
194
202
|
}
|
|
195
203
|
// Final output — collapsed by default (only show when expanded via Tab)
|
|
196
|
-
if (tc.output && tc.status !==
|
|
204
|
+
if (tc.output && tc.status !== "running" && isExpanded && r < limit) {
|
|
197
205
|
// Image results: show inline placeholder
|
|
198
206
|
if (isImageOutput(tc.output)) {
|
|
199
207
|
const label = renderImageInline(tc.output);
|
|
@@ -201,13 +209,13 @@ function renderToolCallsSection(state, grid, r, limit, opts) {
|
|
|
201
209
|
r++;
|
|
202
210
|
continue;
|
|
203
211
|
}
|
|
204
|
-
const outLines = tc.output.split(
|
|
212
|
+
const outLines = tc.output.split("\n");
|
|
205
213
|
const maxOut = 20;
|
|
206
214
|
const showLines = outLines.slice(0, maxOut);
|
|
207
215
|
for (const line of showLines) {
|
|
208
216
|
if (r >= limit)
|
|
209
217
|
break;
|
|
210
|
-
const lineStyle = tc.status ===
|
|
218
|
+
const lineStyle = tc.status === "error" ? S_ERROR : S_DIM;
|
|
211
219
|
grid.writeText(r, 6, line.slice(0, w - 8), lineStyle);
|
|
212
220
|
r++;
|
|
213
221
|
}
|
|
@@ -222,7 +230,13 @@ function renderToolCallsSection(state, grid, r, limit, opts) {
|
|
|
222
230
|
function renderContextWarningSection(state, grid, r, limit) {
|
|
223
231
|
if (!state.contextWarning || r >= limit)
|
|
224
232
|
return r;
|
|
225
|
-
const warnStyle = {
|
|
233
|
+
const warnStyle = {
|
|
234
|
+
fg: "yellow",
|
|
235
|
+
bg: null,
|
|
236
|
+
bold: state.contextWarning.critical,
|
|
237
|
+
dim: false,
|
|
238
|
+
underline: false,
|
|
239
|
+
};
|
|
226
240
|
grid.writeText(r, 0, state.contextWarning.text, warnStyle);
|
|
227
241
|
return r + 1;
|
|
228
242
|
}
|
|
@@ -231,84 +245,84 @@ function renderPermissionBoxSection(state, grid, nextRow, h, opts) {
|
|
|
231
245
|
return nextRow;
|
|
232
246
|
const w = grid.width;
|
|
233
247
|
const { toolName, description, riskLevel } = state.permissionBox;
|
|
234
|
-
const riskColor = riskLevel ===
|
|
248
|
+
const riskColor = riskLevel === "high" ? "red" : riskLevel === "medium" ? "yellow" : "green";
|
|
235
249
|
const riskStyle = { fg: riskColor, bg: null, bold: true, dim: false, underline: false };
|
|
236
250
|
if (opts.boxed) {
|
|
237
|
-
if (
|
|
251
|
+
if (h - nextRow < 6)
|
|
238
252
|
return nextRow;
|
|
239
253
|
const riskDim = { fg: riskColor, bg: null, bold: false, dim: true, underline: false };
|
|
240
254
|
const boxWidth = Math.max(15, Math.min(w - 2, 70));
|
|
241
255
|
// Top border
|
|
242
|
-
grid.writeText(nextRow, 1,
|
|
256
|
+
grid.writeText(nextRow, 1, `╭${"─".repeat(boxWidth - 2)}╮`, riskDim);
|
|
243
257
|
nextRow++;
|
|
244
258
|
// Tool name + risk
|
|
245
|
-
grid.writeText(nextRow, 1,
|
|
246
|
-
grid.writeText(nextRow, 3,
|
|
259
|
+
grid.writeText(nextRow, 1, "│ ", riskDim);
|
|
260
|
+
grid.writeText(nextRow, 3, "⚠ ", riskStyle);
|
|
247
261
|
grid.writeText(nextRow, 5, toolName, { ...riskStyle });
|
|
248
262
|
grid.writeText(nextRow, 5 + toolName.length, ` ${riskLevel} risk`, S_DIM);
|
|
249
|
-
grid.writeText(nextRow, boxWidth,
|
|
263
|
+
grid.writeText(nextRow, boxWidth, "│", riskDim);
|
|
250
264
|
nextRow++;
|
|
251
265
|
// Description (truncated)
|
|
252
266
|
const rawDesc = state.permissionBox.suggestion || description.slice(0, boxWidth - 6);
|
|
253
|
-
const descText = rawDesc.replace(/\|/g,
|
|
254
|
-
grid.writeText(nextRow, 1,
|
|
267
|
+
const descText = rawDesc.replace(/\|/g, " ").replace(/\\/g, "/");
|
|
268
|
+
grid.writeText(nextRow, 1, "│ ", riskDim);
|
|
255
269
|
grid.writeText(nextRow, 3, descText.slice(0, boxWidth - 4), S_DIM);
|
|
256
|
-
grid.writeText(nextRow, boxWidth,
|
|
270
|
+
grid.writeText(nextRow, boxWidth, "│", riskDim);
|
|
257
271
|
nextRow++;
|
|
258
272
|
// Inline diff
|
|
259
273
|
if (state.permissionDiffVisible && state.permissionDiffInfo && nextRow + 3 < h) {
|
|
260
|
-
grid.writeText(nextRow, 1,
|
|
274
|
+
grid.writeText(nextRow, 1, "│", riskDim);
|
|
261
275
|
nextRow++;
|
|
262
276
|
const availDiffRows = Math.min(opts.maxDiffHeight, h - nextRow - 3);
|
|
263
277
|
const diffRows = renderDiff(grid, nextRow, 3, state.permissionDiffInfo, boxWidth - 2, availDiffRows);
|
|
264
278
|
for (let dr = 0; dr < diffRows; dr++) {
|
|
265
279
|
if (nextRow + dr < grid.height) {
|
|
266
|
-
grid.setCell(nextRow + dr, 1,
|
|
267
|
-
grid.setCell(nextRow + dr, boxWidth,
|
|
280
|
+
grid.setCell(nextRow + dr, 1, "│", riskDim);
|
|
281
|
+
grid.setCell(nextRow + dr, boxWidth, "│", riskDim);
|
|
268
282
|
}
|
|
269
283
|
}
|
|
270
284
|
nextRow += diffRows;
|
|
271
285
|
}
|
|
272
286
|
// Action keys
|
|
273
|
-
grid.writeText(nextRow, 1,
|
|
287
|
+
grid.writeText(nextRow, 1, "│ ", riskDim);
|
|
274
288
|
let kc = 3;
|
|
275
|
-
grid.writeText(nextRow, kc,
|
|
289
|
+
grid.writeText(nextRow, kc, "Y", S_KEY_GREEN);
|
|
276
290
|
kc += 1;
|
|
277
|
-
grid.writeText(nextRow, kc,
|
|
291
|
+
grid.writeText(nextRow, kc, "es", S_DIM);
|
|
278
292
|
kc += 2;
|
|
279
|
-
grid.writeText(nextRow, kc,
|
|
293
|
+
grid.writeText(nextRow, kc, " ", S_DIM);
|
|
280
294
|
kc += 2;
|
|
281
|
-
grid.writeText(nextRow, kc,
|
|
295
|
+
grid.writeText(nextRow, kc, "N", S_KEY_RED);
|
|
282
296
|
kc += 1;
|
|
283
|
-
grid.writeText(nextRow, kc,
|
|
297
|
+
grid.writeText(nextRow, kc, "o", S_DIM);
|
|
284
298
|
kc += 1;
|
|
285
299
|
if (state.permissionDiffInfo) {
|
|
286
|
-
grid.writeText(nextRow, kc,
|
|
300
|
+
grid.writeText(nextRow, kc, " ", S_DIM);
|
|
287
301
|
kc += 2;
|
|
288
|
-
grid.writeText(nextRow, kc,
|
|
302
|
+
grid.writeText(nextRow, kc, "D", S_KEY_CYAN);
|
|
289
303
|
kc += 1;
|
|
290
|
-
grid.writeText(nextRow, kc,
|
|
304
|
+
grid.writeText(nextRow, kc, "iff", S_DIM);
|
|
291
305
|
kc += 3;
|
|
292
306
|
}
|
|
293
|
-
grid.writeText(nextRow, boxWidth,
|
|
307
|
+
grid.writeText(nextRow, boxWidth, "│", riskDim);
|
|
294
308
|
nextRow++;
|
|
295
309
|
// Bottom border
|
|
296
|
-
grid.writeText(nextRow, 1,
|
|
310
|
+
grid.writeText(nextRow, 1, `╰${"─".repeat(boxWidth - 2)}╯`, riskDim);
|
|
297
311
|
nextRow++;
|
|
298
312
|
}
|
|
299
313
|
else {
|
|
300
314
|
// Compact mode (rasterizeLive)
|
|
301
|
-
if (
|
|
315
|
+
if (h - nextRow < 4)
|
|
302
316
|
return nextRow;
|
|
303
317
|
grid.writeText(nextRow, 1, `⚠ ${toolName} (${riskLevel} risk)`, riskStyle);
|
|
304
318
|
nextRow++;
|
|
305
|
-
grid.writeText(nextRow, 1,
|
|
306
|
-
grid.writeText(nextRow, 2,
|
|
307
|
-
grid.writeText(nextRow, 6,
|
|
308
|
-
grid.writeText(nextRow, 7,
|
|
319
|
+
grid.writeText(nextRow, 1, "Y", S_KEY_GREEN);
|
|
320
|
+
grid.writeText(nextRow, 2, "es ", S_DIM);
|
|
321
|
+
grid.writeText(nextRow, 6, "N", S_KEY_RED);
|
|
322
|
+
grid.writeText(nextRow, 7, "o", S_DIM);
|
|
309
323
|
if (state.permissionDiffInfo) {
|
|
310
|
-
grid.writeText(nextRow, 10,
|
|
311
|
-
grid.writeText(nextRow, 11,
|
|
324
|
+
grid.writeText(nextRow, 10, "D", S_KEY_CYAN);
|
|
325
|
+
grid.writeText(nextRow, 11, "iff", S_DIM);
|
|
312
326
|
}
|
|
313
327
|
nextRow++;
|
|
314
328
|
// Inline diff (when toggled)
|
|
@@ -325,37 +339,37 @@ function renderQuestionPromptSection(state, grid, nextRow, h, opts) {
|
|
|
325
339
|
return { nextRow, questionInputRow: -1 };
|
|
326
340
|
const w = grid.width;
|
|
327
341
|
const { question, options, input, cursor } = state.questionPrompt;
|
|
328
|
-
const qStyle = { fg:
|
|
342
|
+
const qStyle = { fg: "yellow", bg: null, bold: false, dim: false, underline: false };
|
|
329
343
|
if (opts.boxed) {
|
|
330
|
-
const qBorder = { fg:
|
|
344
|
+
const qBorder = { fg: "yellow", bg: null, bold: false, dim: true, underline: false };
|
|
331
345
|
const qBoxWidth = Math.max(15, Math.min(w - 2, 70));
|
|
332
|
-
grid.writeText(nextRow, 1,
|
|
346
|
+
grid.writeText(nextRow, 1, `╭${"─".repeat(qBoxWidth - 2)}╮`, qBorder);
|
|
333
347
|
nextRow++;
|
|
334
|
-
grid.writeText(nextRow, 1,
|
|
348
|
+
grid.writeText(nextRow, 1, "│ ", qBorder);
|
|
335
349
|
grid.writeText(nextRow, 3, `❓ ${question}`, qStyle);
|
|
336
|
-
grid.writeText(nextRow, qBoxWidth,
|
|
350
|
+
grid.writeText(nextRow, qBoxWidth, "│", qBorder);
|
|
337
351
|
nextRow++;
|
|
338
352
|
if (options && options.length > 0) {
|
|
339
353
|
for (let oi = 0; oi < options.length; oi++) {
|
|
340
|
-
grid.writeText(nextRow, 1,
|
|
354
|
+
grid.writeText(nextRow, 1, "│ ", qBorder);
|
|
341
355
|
grid.writeText(nextRow, 5, `${oi + 1}. ${options[oi]}`, S_DIM);
|
|
342
|
-
grid.writeText(nextRow, qBoxWidth,
|
|
356
|
+
grid.writeText(nextRow, qBoxWidth, "│", qBorder);
|
|
343
357
|
nextRow++;
|
|
344
358
|
}
|
|
345
359
|
}
|
|
346
360
|
const questionInputRow = nextRow;
|
|
347
|
-
grid.writeText(nextRow, 1,
|
|
348
|
-
grid.writeText(nextRow, 3,
|
|
361
|
+
grid.writeText(nextRow, 1, "│ ", qBorder);
|
|
362
|
+
grid.writeText(nextRow, 3, "❯ ", qStyle);
|
|
349
363
|
grid.writeText(nextRow, 5, input, S_TEXT);
|
|
350
|
-
grid.writeText(nextRow, qBoxWidth,
|
|
364
|
+
grid.writeText(nextRow, qBoxWidth, "│", qBorder);
|
|
351
365
|
nextRow++;
|
|
352
|
-
grid.writeText(nextRow, 1,
|
|
366
|
+
grid.writeText(nextRow, 1, `╰${"─".repeat(qBoxWidth - 2)}╯`, qBorder);
|
|
353
367
|
nextRow++;
|
|
354
368
|
return { nextRow, questionInputRow };
|
|
355
369
|
}
|
|
356
370
|
else {
|
|
357
371
|
// Compact mode (rasterizeLive)
|
|
358
|
-
if (
|
|
372
|
+
if (h - nextRow < 3)
|
|
359
373
|
return { nextRow, questionInputRow: -1 };
|
|
360
374
|
grid.writeText(nextRow, 1, `❓ ${question}`, S_TEXT);
|
|
361
375
|
nextRow++;
|
|
@@ -368,7 +382,7 @@ function renderQuestionPromptSection(state, grid, nextRow, h, opts) {
|
|
|
368
382
|
}
|
|
369
383
|
}
|
|
370
384
|
const questionInputRow = nextRow;
|
|
371
|
-
grid.writeText(nextRow, 1,
|
|
385
|
+
grid.writeText(nextRow, 1, "❯ ", S_USER);
|
|
372
386
|
grid.writeText(nextRow, 3, input, S_TEXT);
|
|
373
387
|
nextRow++;
|
|
374
388
|
return { nextRow, questionInputRow };
|
|
@@ -388,7 +402,7 @@ function renderAutocompleteSection(state, grid, nextRow, limit, promptWidth) {
|
|
|
388
402
|
if (nextRow >= limit)
|
|
389
403
|
break;
|
|
390
404
|
const cmd = state.autocomplete[ai];
|
|
391
|
-
const desc = state.autocompleteDescriptions[ai] ??
|
|
405
|
+
const desc = state.autocompleteDescriptions[ai] ?? "";
|
|
392
406
|
const selected = ai === state.autocompleteIndex;
|
|
393
407
|
const acStyle = selected ? s(getTheme().user, true) : s(null, false, true);
|
|
394
408
|
grid.writeText(nextRow, promptWidth, `/${cmd.padEnd(12)}`, acStyle);
|
|
@@ -412,7 +426,7 @@ function renderNotificationsSection(state, grid, nextRow, limit) {
|
|
|
412
426
|
function renderInputSection(state, grid, inputRow, limit, promptText, promptWidth) {
|
|
413
427
|
grid.writeText(inputRow, 0, promptText, S_USER);
|
|
414
428
|
const inputStart = promptWidth;
|
|
415
|
-
const inputLines = state.inputText.split(
|
|
429
|
+
const inputLines = state.inputText.split("\n");
|
|
416
430
|
const maxInputLines = Math.min(inputLines.length, 5);
|
|
417
431
|
for (let li = 0; li < maxInputLines; li++) {
|
|
418
432
|
if (inputRow + li >= limit)
|
|
@@ -433,9 +447,7 @@ function renderInputSection(state, grid, inputRow, limit, promptText, promptWidt
|
|
|
433
447
|
}
|
|
434
448
|
const hintsRow = inputRow + maxInputLines;
|
|
435
449
|
if (hintsRow < limit) {
|
|
436
|
-
const hintsText = inputLines.length > 1
|
|
437
|
-
? `${state.statusHints} | Alt+Enter newline`
|
|
438
|
-
: state.statusHints;
|
|
450
|
+
const hintsText = inputLines.length > 1 ? `${state.statusHints} | Alt+Enter newline` : state.statusHints;
|
|
439
451
|
grid.writeText(hintsRow, 0, hintsText, S_DIM);
|
|
440
452
|
}
|
|
441
453
|
return inputRow + maxInputLines + 1;
|
|
@@ -444,11 +456,11 @@ function renderCompanionSection(state, grid, anchorRow, limit, promptWidth) {
|
|
|
444
456
|
if (!state.companionLines || grid.width < 50)
|
|
445
457
|
return;
|
|
446
458
|
const w = grid.width;
|
|
447
|
-
const compWidth = Math.max(...state.companionLines.map(l => l.length), 0);
|
|
459
|
+
const compWidth = Math.max(...state.companionLines.map((l) => l.length), 0);
|
|
448
460
|
const compStartCol = Math.max(0, w - compWidth - 1);
|
|
449
461
|
if (compStartCol <= promptWidth + 20)
|
|
450
462
|
return;
|
|
451
|
-
const compStyle = { fg: state.companionColor ||
|
|
463
|
+
const compStyle = { fg: state.companionColor || "cyan", bg: null, bold: false, dim: false, underline: false };
|
|
452
464
|
for (let i = 0; i < state.companionLines.length; i++) {
|
|
453
465
|
const compRow = anchorRow + i;
|
|
454
466
|
if (compRow >= limit)
|
|
@@ -462,14 +474,14 @@ function computeCursorPosition(state, inputRow, inputStart, questionInputRow) {
|
|
|
462
474
|
return { cursorRow: questionInputRow, cursorCol: 5 + state.questionPrompt.cursor };
|
|
463
475
|
}
|
|
464
476
|
const textBeforeCursor = state.inputText.slice(0, state.inputCursor);
|
|
465
|
-
const cursorLines = textBeforeCursor.split(
|
|
477
|
+
const cursorLines = textBeforeCursor.split("\n");
|
|
466
478
|
const cursorLineIdx = Math.min(cursorLines.length - 1, 4);
|
|
467
479
|
const cursorColInLine = cursorLines[cursorLines.length - 1].length;
|
|
468
480
|
return { cursorRow: inputRow + cursorLineIdx, cursorCol: inputStart + cursorColInLine };
|
|
469
481
|
}
|
|
470
482
|
function getPromptText(state) {
|
|
471
|
-
const vimIndicator = state.vimMode ? (state.vimMode ===
|
|
472
|
-
const promptText = vimIndicator
|
|
483
|
+
const vimIndicator = state.vimMode ? (state.vimMode === "normal" ? "[N] " : "[I] ") : "";
|
|
484
|
+
const promptText = `${vimIndicator}❯ `;
|
|
473
485
|
return { promptText, promptWidth: promptText.length };
|
|
474
486
|
}
|
|
475
487
|
// ── Main rasterization functions ──
|
|
@@ -485,14 +497,17 @@ export function rasterize(state, grid) {
|
|
|
485
497
|
// Footer height — capped at 50% of terminal to preserve message area
|
|
486
498
|
const companionHeight = state.companionLines ? Math.min(state.companionLines.length + 1, 8) : 0;
|
|
487
499
|
const maxDiffHeight = Math.min(15, Math.floor(h / 3));
|
|
488
|
-
const diffHeight =
|
|
500
|
+
const diffHeight = state.permissionDiffVisible && state.permissionDiffInfo ? maxDiffHeight : 0;
|
|
489
501
|
const permissionHeight = state.permissionBox ? 6 + diffHeight : 0;
|
|
490
502
|
const questionHeight = state.questionPrompt ? 4 + (state.questionPrompt.options?.length ?? 0) : 0;
|
|
491
503
|
const statusLineHeight = state.statusLine ? 1 : 0;
|
|
492
504
|
const contextWarningHeight = state.contextWarning ? 1 : 0;
|
|
493
505
|
const autocompleteHeight = state.autocomplete.length;
|
|
494
506
|
const inputLineCount = Math.min(5, (state.inputText.match(/\n/g)?.length ?? 0) + 1);
|
|
495
|
-
const rawFooterHeight = Math.max(2 + inputLineCount + statusLineHeight + autocompleteHeight, companionHeight + 1) +
|
|
507
|
+
const rawFooterHeight = Math.max(2 + inputLineCount + statusLineHeight + autocompleteHeight, companionHeight + 1) +
|
|
508
|
+
permissionHeight +
|
|
509
|
+
questionHeight +
|
|
510
|
+
contextWarningHeight;
|
|
496
511
|
const footerHeight = Math.min(rawFooterHeight, Math.floor(h / 2));
|
|
497
512
|
const msgAreaHeight = Math.max(1, h - footerHeight);
|
|
498
513
|
// ── Session browser overlay ──
|
|
@@ -500,30 +515,48 @@ export function rasterize(state, grid) {
|
|
|
500
515
|
const browserRows = renderSessionBrowser(grid, 0, 0, state.sessionBrowser, w, msgAreaHeight);
|
|
501
516
|
const footerStart = Math.min(browserRows, msgAreaHeight);
|
|
502
517
|
for (let c = 0; c < w; c++)
|
|
503
|
-
grid.setCell(footerStart, c,
|
|
518
|
+
grid.setCell(footerStart, c, "─", S_BORDER);
|
|
504
519
|
const inputRow = footerStart + 1;
|
|
505
|
-
grid.writeText(inputRow, 0,
|
|
506
|
-
grid.writeText(inputRow + 1, 0,
|
|
520
|
+
grid.writeText(inputRow, 0, "❯ ", S_USER);
|
|
521
|
+
grid.writeText(inputRow + 1, 0, "↑/↓ navigate | Enter resume | Esc cancel", S_DIM);
|
|
507
522
|
return { cursorRow: inputRow, cursorCol: 2 };
|
|
508
523
|
}
|
|
509
524
|
// ── Messages area (top) ──
|
|
510
525
|
const allContent = [];
|
|
511
526
|
for (const msg of state.messages) {
|
|
512
|
-
if (msg.role ===
|
|
513
|
-
allContent.push({
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
527
|
+
if (msg.role === "user") {
|
|
528
|
+
allContent.push({
|
|
529
|
+
role: "user",
|
|
530
|
+
content: msg.content,
|
|
531
|
+
style: { ...S_TEXT, bold: true },
|
|
532
|
+
prefixStyle: S_USER,
|
|
533
|
+
prefix: "❯ ",
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
else if (msg.role === "assistant") {
|
|
537
|
+
allContent.push({
|
|
538
|
+
role: "assistant",
|
|
539
|
+
content: msg.content,
|
|
540
|
+
style: S_TEXT,
|
|
541
|
+
prefixStyle: S_ASSISTANT,
|
|
542
|
+
prefix: "◆ ",
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
else if (msg.role === "system") {
|
|
546
|
+
allContent.push({ role: "system", content: msg.content, style: S_DIM, prefixStyle: S_DIM, prefix: " " });
|
|
520
547
|
}
|
|
521
548
|
}
|
|
522
549
|
if (state.loading && state.streamingText) {
|
|
523
|
-
allContent.push({
|
|
550
|
+
allContent.push({
|
|
551
|
+
role: "streaming",
|
|
552
|
+
content: state.streamingText,
|
|
553
|
+
style: S_TEXT,
|
|
554
|
+
prefixStyle: S_ASSISTANT,
|
|
555
|
+
prefix: "◆ ",
|
|
556
|
+
});
|
|
524
557
|
}
|
|
525
558
|
if (state.errorText) {
|
|
526
|
-
allContent.push({ role:
|
|
559
|
+
allContent.push({ role: "error", content: state.errorText, style: S_ERROR, prefixStyle: S_ERROR, prefix: "✗ " });
|
|
527
560
|
}
|
|
528
561
|
const prefixLen = 2;
|
|
529
562
|
const contentWidth = w - 1; // reserve rightmost column for scrollbar
|
|
@@ -536,20 +569,20 @@ export function rasterize(state, grid) {
|
|
|
536
569
|
totalRows += visibleLines + 1;
|
|
537
570
|
}
|
|
538
571
|
for (const item of allContent) {
|
|
539
|
-
if (item.role ===
|
|
572
|
+
if (item.role === "user" && totalRows > 0)
|
|
540
573
|
totalRows++;
|
|
541
|
-
if (item.role ===
|
|
574
|
+
if (item.role === "assistant" || item.role === "streaming") {
|
|
542
575
|
totalRows += measureMarkdown(item.content, contentWidth);
|
|
543
576
|
}
|
|
544
577
|
else {
|
|
545
|
-
const lines = item.content.split(
|
|
578
|
+
const lines = item.content.split("\n");
|
|
546
579
|
for (const line of lines) {
|
|
547
580
|
totalRows += Math.max(1, Math.ceil((line.length || 1) / textWidth));
|
|
548
581
|
}
|
|
549
582
|
}
|
|
550
583
|
}
|
|
551
584
|
if (state.thinkingText) {
|
|
552
|
-
totalRows += state.thinkingExpanded ? Math.min(state.thinkingText.split(
|
|
585
|
+
totalRows += state.thinkingExpanded ? Math.min(state.thinkingText.split("\n").length, 10) : 1;
|
|
553
586
|
}
|
|
554
587
|
if (!state.loading && state.lastThinkingSummary)
|
|
555
588
|
totalRows += 1;
|
|
@@ -559,10 +592,10 @@ export function rasterize(state, grid) {
|
|
|
559
592
|
totalRows += 1;
|
|
560
593
|
if (tc.isAgent && tc.agentDescription)
|
|
561
594
|
totalRows += 1;
|
|
562
|
-
if (tc.status ===
|
|
595
|
+
if (tc.status === "running" && tc.liveOutput)
|
|
563
596
|
totalRows += Math.min(tc.liveOutput.length, 5);
|
|
564
|
-
if (tc.output && tc.status !==
|
|
565
|
-
totalRows += Math.min(tc.output.split(
|
|
597
|
+
if (tc.output && tc.status !== "running" && state.expandedToolCalls.has(callId)) {
|
|
598
|
+
totalRows += Math.min(tc.output.split("\n").length, 20);
|
|
566
599
|
}
|
|
567
600
|
}
|
|
568
601
|
if (state.contextWarning)
|
|
@@ -602,21 +635,21 @@ export function rasterize(state, grid) {
|
|
|
602
635
|
for (const item of allContent) {
|
|
603
636
|
if (r >= msgAreaHeight)
|
|
604
637
|
break;
|
|
605
|
-
if (item.role ===
|
|
638
|
+
if (item.role === "user" && contentIdx > 0) {
|
|
606
639
|
if (virtualR >= scrollOffset) {
|
|
607
640
|
for (let c = 0; c < w; c++) {
|
|
608
|
-
grid.setCell(r, c,
|
|
641
|
+
grid.setCell(r, c, "─", S_BORDER);
|
|
609
642
|
}
|
|
610
643
|
r++;
|
|
611
644
|
}
|
|
612
645
|
virtualR++;
|
|
613
646
|
}
|
|
614
647
|
let itemRows;
|
|
615
|
-
if (item.role ===
|
|
648
|
+
if (item.role === "assistant" || item.role === "streaming") {
|
|
616
649
|
itemRows = measureMarkdown(item.content, contentWidth);
|
|
617
650
|
}
|
|
618
651
|
else {
|
|
619
|
-
const lines = item.content.split(
|
|
652
|
+
const lines = item.content.split("\n");
|
|
620
653
|
itemRows = 0;
|
|
621
654
|
for (const line of lines) {
|
|
622
655
|
itemRows += Math.max(1, Math.ceil((line.length || 1) / textWidth));
|
|
@@ -629,7 +662,7 @@ export function rasterize(state, grid) {
|
|
|
629
662
|
}
|
|
630
663
|
grid.writeText(r, 0, item.prefix, item.prefixStyle);
|
|
631
664
|
let rows;
|
|
632
|
-
if (item.role ===
|
|
665
|
+
if (item.role === "assistant" || item.role === "streaming") {
|
|
633
666
|
rows = renderMarkdown(grid, r, prefixLen, item.content, contentWidth, state.codeBlocksExpanded, msgAreaHeight);
|
|
634
667
|
}
|
|
635
668
|
else {
|
|
@@ -651,16 +684,16 @@ export function rasterize(state, grid) {
|
|
|
651
684
|
const S_THUMB = { fg: null, bg: null, bold: false, dim: false, underline: false };
|
|
652
685
|
for (let sr = 0; sr < msgAreaHeight; sr++) {
|
|
653
686
|
const isThumb = sr >= thumbStart && sr < thumbStart + thumbSize;
|
|
654
|
-
grid.setCell(sr, w - 1, isThumb ?
|
|
687
|
+
grid.setCell(sr, w - 1, isThumb ? "█" : "░", isThumb ? S_THUMB : S_TRACK);
|
|
655
688
|
}
|
|
656
689
|
}
|
|
657
690
|
// ── Footer ──
|
|
658
691
|
const footerStart = Math.min(r, msgAreaHeight);
|
|
659
692
|
for (let c = 0; c < w; c++) {
|
|
660
|
-
grid.setCell(footerStart, c,
|
|
693
|
+
grid.setCell(footerStart, c, "─", S_BORDER);
|
|
661
694
|
}
|
|
662
695
|
if (hasScrollbar) {
|
|
663
|
-
grid.setCell(footerStart, w - 1,
|
|
696
|
+
grid.setCell(footerStart, w - 1, "┤", S_BORDER);
|
|
664
697
|
}
|
|
665
698
|
if (state.manualScroll > 0 && totalRows > msgAreaHeight) {
|
|
666
699
|
const hiddenBelow = state.manualScroll;
|
|
@@ -687,11 +720,17 @@ export function rasterize(state, grid) {
|
|
|
687
720
|
renderInputSection(state, grid, inputRow, h, promptText, promptWidth);
|
|
688
721
|
// Companion (right-aligned in footer, skipped if it would overlap input)
|
|
689
722
|
if (state.companionLines && w >= 50) {
|
|
690
|
-
const compWidth = Math.max(...state.companionLines.map(l => l.length), 0);
|
|
723
|
+
const compWidth = Math.max(...state.companionLines.map((l) => l.length), 0);
|
|
691
724
|
const compStartCol = Math.max(0, w - compWidth - 1);
|
|
692
|
-
const inputEndCol = promptWidth + (state.inputText.split(
|
|
725
|
+
const inputEndCol = promptWidth + (state.inputText.split("\n")[0]?.length ?? 0);
|
|
693
726
|
if (compStartCol > inputEndCol + 3) {
|
|
694
|
-
const compStyle = {
|
|
727
|
+
const compStyle = {
|
|
728
|
+
fg: state.companionColor || "cyan",
|
|
729
|
+
bg: null,
|
|
730
|
+
bold: false,
|
|
731
|
+
dim: false,
|
|
732
|
+
underline: false,
|
|
733
|
+
};
|
|
695
734
|
for (let i = 0; i < state.companionLines.length; i++) {
|
|
696
735
|
const compRow = footerStart + i;
|
|
697
736
|
if (compRow >= inputRow)
|
|
@@ -721,7 +760,7 @@ export function rasterizeLive(state, grid) {
|
|
|
721
760
|
}
|
|
722
761
|
// ── Streaming text ──
|
|
723
762
|
if (state.loading && state.streamingText) {
|
|
724
|
-
grid.writeText(r, 0,
|
|
763
|
+
grid.writeText(r, 0, "◆ ", S_ASSISTANT);
|
|
725
764
|
const rows = renderMarkdown(grid, r, 2, state.streamingText, w, state.codeBlocksExpanded, h);
|
|
726
765
|
r += rows;
|
|
727
766
|
}
|
|
@@ -735,7 +774,7 @@ export function rasterizeLive(state, grid) {
|
|
|
735
774
|
// ── Footer border ──
|
|
736
775
|
if (r < h) {
|
|
737
776
|
for (let c = 0; c < w; c++)
|
|
738
|
-
grid.setCell(r, c,
|
|
777
|
+
grid.setCell(r, c, "─", S_BORDER);
|
|
739
778
|
r++;
|
|
740
779
|
}
|
|
741
780
|
let nextRow = r;
|
|
@@ -758,7 +797,7 @@ export function rasterizeLive(state, grid) {
|
|
|
758
797
|
return { cursorRow: questionInputRow, cursorCol: 3 + state.questionPrompt.cursor };
|
|
759
798
|
}
|
|
760
799
|
const textBeforeCursor = state.inputText.slice(0, state.inputCursor);
|
|
761
|
-
const cursorLines = textBeforeCursor.split(
|
|
800
|
+
const cursorLines = textBeforeCursor.split("\n");
|
|
762
801
|
const cursorLineIdx = Math.min(cursorLines.length - 1, 4);
|
|
763
802
|
const cursorColInLine = cursorLines[cursorLines.length - 1].length;
|
|
764
803
|
return { cursorRow: inputRow + cursorLineIdx, cursorCol: promptWidth + cursorColInLine };
|