nova-terminal-assistant 0.1.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.
Potentially problematic release.
This version of nova-terminal-assistant might be problematic. Click here for more details.
- package/README.md +358 -0
- package/bin/nova +38 -0
- package/bin/nova.js +12 -0
- package/package.json +67 -0
- package/src/cli/commands/SmartCompletion.ts +458 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/startup/IFlowRepl.ts +212 -0
- package/src/cli/startup/InkBasedRepl.ts +1056 -0
- package/src/cli/startup/InteractiveRepl.ts +2833 -0
- package/src/cli/startup/NovaApp.ts +1861 -0
- package/src/cli/startup/index.ts +4 -0
- package/src/cli/startup/parseArgs.ts +293 -0
- package/src/cli/test-modules.ts +27 -0
- package/src/cli/ui/IFlowDropdown.ts +425 -0
- package/src/cli/ui/ModernReplUI.ts +276 -0
- package/src/cli/ui/SimpleSelector2.ts +215 -0
- package/src/cli/ui/components/ConfirmDialog.ts +176 -0
- package/src/cli/ui/components/ErrorPanel.ts +364 -0
- package/src/cli/ui/components/InkAppRunner.tsx +67 -0
- package/src/cli/ui/components/InkComponents.tsx +613 -0
- package/src/cli/ui/components/NovaInkApp.tsx +312 -0
- package/src/cli/ui/components/ProgressBar.ts +177 -0
- package/src/cli/ui/components/ProgressIndicator.ts +298 -0
- package/src/cli/ui/components/QuickActions.ts +396 -0
- package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
- package/src/cli/ui/components/StatusBar.ts +194 -0
- package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
- package/src/cli/ui/components/index.ts +27 -0
- package/src/cli/ui/ink-prototype.tsx +347 -0
- package/src/cli/utils/CliUI.ts +336 -0
- package/src/cli/utils/CompletionHelper.ts +388 -0
- package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
- package/src/cli/utils/EnhancedCompleter.ts +513 -0
- package/src/cli/utils/ErrorEnhancer.ts +429 -0
- package/src/cli/utils/OutputFormatter.ts +193 -0
- package/src/cli/utils/index.ts +9 -0
- package/src/core/agents/AgentOrchestrator.ts +515 -0
- package/src/core/agents/index.ts +17 -0
- package/src/core/audit/AuditLogger.ts +509 -0
- package/src/core/audit/index.ts +11 -0
- package/src/core/auth/AuthManager.d.ts.map +1 -0
- package/src/core/auth/AuthManager.ts +138 -0
- package/src/core/auth/index.d.ts.map +1 -0
- package/src/core/auth/index.ts +2 -0
- package/src/core/config/ConfigManager.d.ts.map +1 -0
- package/src/core/config/ConfigManager.test.ts +183 -0
- package/src/core/config/ConfigManager.ts +1219 -0
- package/src/core/config/index.d.ts.map +1 -0
- package/src/core/config/index.ts +1 -0
- package/src/core/context/ContextBuilder.d.ts.map +1 -0
- package/src/core/context/ContextBuilder.ts +171 -0
- package/src/core/context/ContextCompressor.d.ts.map +1 -0
- package/src/core/context/ContextCompressor.ts +642 -0
- package/src/core/context/LayeredMemoryManager.ts +657 -0
- package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
- package/src/core/context/MemoryDiscovery.ts +175 -0
- package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
- package/src/core/context/defaultSystemPrompt.ts +35 -0
- package/src/core/context/index.d.ts.map +1 -0
- package/src/core/context/index.ts +22 -0
- package/src/core/extensions/SkillGenerator.ts +421 -0
- package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
- package/src/core/extensions/SkillInstaller.ts +257 -0
- package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
- package/src/core/extensions/SkillRegistry.ts +361 -0
- package/src/core/extensions/SkillValidator.ts +525 -0
- package/src/core/extensions/index.ts +15 -0
- package/src/core/index.d.ts.map +1 -0
- package/src/core/index.ts +42 -0
- package/src/core/mcp/McpManager.d.ts.map +1 -0
- package/src/core/mcp/McpManager.ts +632 -0
- package/src/core/mcp/index.d.ts.map +1 -0
- package/src/core/mcp/index.ts +2 -0
- package/src/core/model/ModelClient.d.ts.map +1 -0
- package/src/core/model/ModelClient.ts +217 -0
- package/src/core/model/ModelConnectionTester.ts +363 -0
- package/src/core/model/ModelValidator.ts +348 -0
- package/src/core/model/index.d.ts.map +1 -0
- package/src/core/model/index.ts +6 -0
- package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
- package/src/core/model/providers/AnthropicProvider.ts +279 -0
- package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
- package/src/core/model/providers/CodingPlanProvider.ts +210 -0
- package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
- package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
- package/src/core/model/providers/OllamaManager.ts +201 -0
- package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaProvider.ts +73 -0
- package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
- package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAIProvider.ts +29 -0
- package/src/core/model/providers/index.d.ts.map +1 -0
- package/src/core/model/providers/index.ts +12 -0
- package/src/core/model/types.d.ts.map +1 -0
- package/src/core/model/types.ts +77 -0
- package/src/core/security/ApprovalManager.d.ts.map +1 -0
- package/src/core/security/ApprovalManager.ts +174 -0
- package/src/core/security/FileFilter.d.ts.map +1 -0
- package/src/core/security/FileFilter.ts +141 -0
- package/src/core/security/HookExecutor.d.ts.map +1 -0
- package/src/core/security/HookExecutor.ts +178 -0
- package/src/core/security/SandboxExecutor.ts +447 -0
- package/src/core/security/index.d.ts.map +1 -0
- package/src/core/security/index.ts +8 -0
- package/src/core/session/AgentLoop.d.ts.map +1 -0
- package/src/core/session/AgentLoop.ts +501 -0
- package/src/core/session/SessionManager.d.ts.map +1 -0
- package/src/core/session/SessionManager.test.ts +183 -0
- package/src/core/session/SessionManager.ts +460 -0
- package/src/core/session/index.d.ts.map +1 -0
- package/src/core/session/index.ts +3 -0
- package/src/core/telemetry/Telemetry.d.ts.map +1 -0
- package/src/core/telemetry/Telemetry.ts +90 -0
- package/src/core/telemetry/TelemetryService.ts +531 -0
- package/src/core/telemetry/index.d.ts.map +1 -0
- package/src/core/telemetry/index.ts +12 -0
- package/src/core/testing/AutoFixer.ts +385 -0
- package/src/core/testing/ErrorAnalyzer.ts +499 -0
- package/src/core/testing/TestRunner.ts +265 -0
- package/src/core/testing/agent-cli-tests.ts +538 -0
- package/src/core/testing/index.ts +11 -0
- package/src/core/tools/ToolRegistry.d.ts.map +1 -0
- package/src/core/tools/ToolRegistry.test.ts +206 -0
- package/src/core/tools/ToolRegistry.ts +260 -0
- package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/EditFileTool.ts +97 -0
- package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
- package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/MemoryTool.ts +102 -0
- package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/ReadFileTool.ts +58 -0
- package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchContentTool.ts +94 -0
- package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchFileTool.ts +61 -0
- package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
- package/src/core/tools/impl/ShellTool.ts +118 -0
- package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
- package/src/core/tools/impl/TaskTool.ts +207 -0
- package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
- package/src/core/tools/impl/TodoTool.ts +122 -0
- package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebFetchTool.ts +103 -0
- package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebSearchTool.ts +89 -0
- package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/WriteFileTool.ts +49 -0
- package/src/core/tools/impl/index.d.ts.map +1 -0
- package/src/core/tools/impl/index.ts +16 -0
- package/src/core/tools/index.d.ts.map +1 -0
- package/src/core/tools/index.ts +7 -0
- package/src/core/tools/schemas/execution.d.ts.map +1 -0
- package/src/core/tools/schemas/execution.ts +42 -0
- package/src/core/tools/schemas/file.d.ts.map +1 -0
- package/src/core/tools/schemas/file.ts +119 -0
- package/src/core/tools/schemas/index.d.ts.map +1 -0
- package/src/core/tools/schemas/index.ts +11 -0
- package/src/core/tools/schemas/memory.d.ts.map +1 -0
- package/src/core/tools/schemas/memory.ts +52 -0
- package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
- package/src/core/tools/schemas/orchestration.ts +44 -0
- package/src/core/tools/schemas/search.d.ts.map +1 -0
- package/src/core/tools/schemas/search.ts +112 -0
- package/src/core/tools/schemas/todo.d.ts.map +1 -0
- package/src/core/tools/schemas/todo.ts +32 -0
- package/src/core/tools/schemas/web.d.ts.map +1 -0
- package/src/core/tools/schemas/web.ts +86 -0
- package/src/core/types/config.d.ts.map +1 -0
- package/src/core/types/config.ts +200 -0
- package/src/core/types/errors.d.ts.map +1 -0
- package/src/core/types/errors.ts +204 -0
- package/src/core/types/index.d.ts.map +1 -0
- package/src/core/types/index.ts +8 -0
- package/src/core/types/session.d.ts.map +1 -0
- package/src/core/types/session.ts +216 -0
- package/src/core/types/tools.d.ts.map +1 -0
- package/src/core/types/tools.ts +157 -0
- package/src/core/utils/CheckpointManager.d.ts.map +1 -0
- package/src/core/utils/CheckpointManager.ts +327 -0
- package/src/core/utils/Logger.d.ts.map +1 -0
- package/src/core/utils/Logger.ts +98 -0
- package/src/core/utils/RetryManager.ts +471 -0
- package/src/core/utils/TokenCounter.d.ts.map +1 -0
- package/src/core/utils/TokenCounter.ts +414 -0
- package/src/core/utils/VectorMemoryStore.ts +440 -0
- package/src/core/utils/helpers.d.ts.map +1 -0
- package/src/core/utils/helpers.ts +89 -0
- package/src/core/utils/index.d.ts.map +1 -0
- package/src/core/utils/index.ts +19 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// ThinkingBlockRenderer - Collapsible thinking block display for REPL
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Types
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
export interface ThinkingBlockOptions {
|
|
12
|
+
/** Whether to show expanded thinking blocks (default: false = compact) */
|
|
13
|
+
expanded?: boolean;
|
|
14
|
+
/** Maximum lines to show in streaming preview (default: 4) */
|
|
15
|
+
maxPreviewLines?: number;
|
|
16
|
+
/** Maximum characters per line in preview (default: 80) */
|
|
17
|
+
maxLineLength?: number;
|
|
18
|
+
/** Show elapsed time (default: true) */
|
|
19
|
+
showElapsedTime?: boolean;
|
|
20
|
+
/** Custom icon for thinking block (default: '💭') */
|
|
21
|
+
icon?: string;
|
|
22
|
+
/** Show streaming preview (default: false - only show summary when complete) */
|
|
23
|
+
showStreamingPreview?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ThinkingBlockState {
|
|
27
|
+
text: string;
|
|
28
|
+
startTime: number;
|
|
29
|
+
isComplete: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// ANSI helpers for terminal control
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
const ANSI = {
|
|
37
|
+
cursorUp: (n: number): string => `\x1b[${n}A`,
|
|
38
|
+
cursorDown: (n: number): string => `\x1b[${n}B`,
|
|
39
|
+
cursorLineStart: (): string => '\x1b[0G',
|
|
40
|
+
clearDown: (): string => '\x1b[0J',
|
|
41
|
+
clearLine: (): string => '\x1b[2K',
|
|
42
|
+
hideCursor: (): string => '\x1b[?25l',
|
|
43
|
+
showCursor: (): string => '\x1b[?25h',
|
|
44
|
+
saveCursor: (): string => '\x1b[s',
|
|
45
|
+
restoreCursor: (): string => '\x1b[u',
|
|
46
|
+
dim: (): string => '\x1b[2m',
|
|
47
|
+
reset: (): string => '\x1b[0m',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// ThinkingBlockRenderer
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Renders a collapsible thinking block in the terminal.
|
|
56
|
+
*
|
|
57
|
+
* Features:
|
|
58
|
+
* - Gray/dim color for thinking content (distinguishable from normal output)
|
|
59
|
+
* - Collapsed mode: single summary line with char/line count
|
|
60
|
+
* - Expanded mode: shows preview of thinking content
|
|
61
|
+
* - Optional streaming preview (disabled by default for cleaner UI)
|
|
62
|
+
*/
|
|
63
|
+
export class ThinkingBlockRenderer {
|
|
64
|
+
private options: Required<ThinkingBlockOptions>;
|
|
65
|
+
private state: ThinkingBlockState | null = null;
|
|
66
|
+
private renderedLineCount = 0;
|
|
67
|
+
private isActive = false;
|
|
68
|
+
|
|
69
|
+
constructor(options: ThinkingBlockOptions = {}) {
|
|
70
|
+
this.options = {
|
|
71
|
+
expanded: options.expanded ?? false,
|
|
72
|
+
maxPreviewLines: options.maxPreviewLines ?? 4,
|
|
73
|
+
maxLineLength: options.maxLineLength ?? 80,
|
|
74
|
+
showElapsedTime: options.showElapsedTime ?? true,
|
|
75
|
+
icon: options.icon ?? '💭',
|
|
76
|
+
showStreamingPreview: options.showStreamingPreview ?? false,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ========================================================================
|
|
81
|
+
// Public API
|
|
82
|
+
// ========================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Set whether to show expanded thinking blocks
|
|
86
|
+
*/
|
|
87
|
+
setExpanded(expanded: boolean): void {
|
|
88
|
+
this.options.expanded = expanded;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check if currently rendering a thinking block
|
|
93
|
+
*/
|
|
94
|
+
isRendering(): boolean {
|
|
95
|
+
return this.isActive;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Start a new thinking block.
|
|
100
|
+
* Call this when thinking content begins streaming.
|
|
101
|
+
*/
|
|
102
|
+
start(): void {
|
|
103
|
+
this.state = {
|
|
104
|
+
text: '',
|
|
105
|
+
startTime: Date.now(),
|
|
106
|
+
isComplete: false,
|
|
107
|
+
};
|
|
108
|
+
this.renderedLineCount = 0;
|
|
109
|
+
this.isActive = true;
|
|
110
|
+
|
|
111
|
+
// Show minimal indicator during thinking
|
|
112
|
+
if (this.options.showStreamingPreview) {
|
|
113
|
+
process.stdout.write(ANSI.hideCursor());
|
|
114
|
+
this.renderStreamingIndicator();
|
|
115
|
+
} else {
|
|
116
|
+
// Just show a subtle indicator
|
|
117
|
+
this.renderMinimalIndicator();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Append text to the thinking block.
|
|
123
|
+
* Updates the display in real-time only if showStreamingPreview is true.
|
|
124
|
+
*/
|
|
125
|
+
append(delta: string): void {
|
|
126
|
+
if (!this.state || !this.isActive) return;
|
|
127
|
+
this.state.text += delta;
|
|
128
|
+
|
|
129
|
+
// Only update display if streaming preview is enabled
|
|
130
|
+
if (this.options.showStreamingPreview) {
|
|
131
|
+
this.renderStreaming();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Complete the thinking block.
|
|
137
|
+
* Clears any streaming display and shows final collapsed/expanded view.
|
|
138
|
+
*/
|
|
139
|
+
complete(): void {
|
|
140
|
+
if (!this.state || !this.isActive) return;
|
|
141
|
+
this.state.isComplete = true;
|
|
142
|
+
|
|
143
|
+
// Clear any streaming display
|
|
144
|
+
if (this.options.showStreamingPreview) {
|
|
145
|
+
this.clearRender();
|
|
146
|
+
process.stdout.write(ANSI.showCursor());
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Always show final summary (collapsed or expanded)
|
|
150
|
+
this.renderFinal();
|
|
151
|
+
this.isActive = false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Cancel and clear the thinking block display.
|
|
156
|
+
*/
|
|
157
|
+
cancel(): void {
|
|
158
|
+
if (!this.isActive) return;
|
|
159
|
+
this.clearRender();
|
|
160
|
+
this.state = null;
|
|
161
|
+
this.isActive = false;
|
|
162
|
+
process.stdout.write(ANSI.showCursor());
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get the current thinking text (if any)
|
|
167
|
+
*/
|
|
168
|
+
getText(): string | null {
|
|
169
|
+
return this.state?.text ?? null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get elapsed time since thinking started
|
|
174
|
+
*/
|
|
175
|
+
getElapsed(): string {
|
|
176
|
+
if (!this.state) return '';
|
|
177
|
+
return this.formatDuration(this.state.startTime);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ========================================================================
|
|
181
|
+
// Internal rendering
|
|
182
|
+
// ========================================================================
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Render minimal indicator during thinking (default behavior)
|
|
186
|
+
*/
|
|
187
|
+
private renderMinimalIndicator(): void {
|
|
188
|
+
const elapsed = this.formatDuration(this.state?.startTime ?? Date.now());
|
|
189
|
+
// Use gray/dim styling for thinking indicator
|
|
190
|
+
process.stdout.write(
|
|
191
|
+
chalk.gray.dim(' ') +
|
|
192
|
+
chalk.gray.dim(this.options.icon + ' ') +
|
|
193
|
+
chalk.gray.dim.italic('thinking') +
|
|
194
|
+
chalk.gray.dim(` (${elapsed})...`) +
|
|
195
|
+
'\r'
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Render streaming indicator (when showStreamingPreview is true)
|
|
201
|
+
*/
|
|
202
|
+
private renderStreamingIndicator(): void {
|
|
203
|
+
const elapsed = this.formatDuration(this.state?.startTime ?? Date.now());
|
|
204
|
+
process.stdout.write(
|
|
205
|
+
ANSI.clearLine() +
|
|
206
|
+
chalk.gray.dim(' ') +
|
|
207
|
+
chalk.gray.dim(this.options.icon + ' ') +
|
|
208
|
+
chalk.gray.dim.italic('thinking') +
|
|
209
|
+
chalk.gray.dim(` (${elapsed})...`) +
|
|
210
|
+
'\n'
|
|
211
|
+
);
|
|
212
|
+
this.renderedLineCount = 1;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Render the streaming thinking block (when showStreamingPreview is true).
|
|
217
|
+
*/
|
|
218
|
+
private renderStreaming(): void {
|
|
219
|
+
if (!this.state) return;
|
|
220
|
+
|
|
221
|
+
const text = this.state.text;
|
|
222
|
+
const termWidth = process.stdout.columns || 80;
|
|
223
|
+
const usableWidth = termWidth - 4;
|
|
224
|
+
|
|
225
|
+
// Calculate text dimensions
|
|
226
|
+
const textLines = this.calculateTextLines(text, usableWidth);
|
|
227
|
+
const maxLines = this.options.maxPreviewLines;
|
|
228
|
+
|
|
229
|
+
// Clear previous render
|
|
230
|
+
if (this.renderedLineCount > 0) {
|
|
231
|
+
this.clearLines(this.renderedLineCount);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Render header with elapsed time
|
|
235
|
+
const elapsed = this.formatDuration(this.state.startTime);
|
|
236
|
+
const header = chalk.gray.dim(` ${this.options.icon} `) +
|
|
237
|
+
chalk.gray.dim.italic('thinking') +
|
|
238
|
+
chalk.gray.dim(` (${elapsed})`);
|
|
239
|
+
process.stdout.write(header + '\n');
|
|
240
|
+
|
|
241
|
+
// Render text preview in gray/dim (truncated)
|
|
242
|
+
const lines = this.wrapTextToLines(text, usableWidth);
|
|
243
|
+
const visibleLines = lines.slice(-maxLines);
|
|
244
|
+
|
|
245
|
+
if (lines.length > maxLines) {
|
|
246
|
+
process.stdout.write(chalk.gray.dim(' ...') + '\n');
|
|
247
|
+
this.renderedLineCount = 2;
|
|
248
|
+
} else {
|
|
249
|
+
this.renderedLineCount = 1;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for (const line of visibleLines) {
|
|
253
|
+
const truncated = line.slice(0, this.options.maxLineLength);
|
|
254
|
+
// Use gray.dim for thinking content - clearly distinguishable
|
|
255
|
+
process.stdout.write(chalk.gray.dim(' ') + chalk.gray.dim(truncated) + '\n');
|
|
256
|
+
this.renderedLineCount++;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Move cursor back up so next delta overwrites
|
|
260
|
+
if (this.renderedLineCount > 0) {
|
|
261
|
+
process.stdout.write(ANSI.cursorUp(this.renderedLineCount));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Clear the current streaming render.
|
|
267
|
+
*/
|
|
268
|
+
private clearRender(): void {
|
|
269
|
+
if (this.renderedLineCount > 0) {
|
|
270
|
+
this.clearLines(this.renderedLineCount);
|
|
271
|
+
this.renderedLineCount = 0;
|
|
272
|
+
} else {
|
|
273
|
+
// Clear the minimal indicator line
|
|
274
|
+
process.stdout.write(ANSI.clearLine() + ANSI.cursorLineStart());
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Render the final collapsed/expanded thinking block.
|
|
280
|
+
* Uses gray/dim color to distinguish from normal output.
|
|
281
|
+
*/
|
|
282
|
+
private renderFinal(): void {
|
|
283
|
+
if (!this.state || !this.state.text.trim()) return;
|
|
284
|
+
|
|
285
|
+
const text = this.state.text.trim();
|
|
286
|
+
const elapsed = this.formatDuration(this.state.startTime);
|
|
287
|
+
const charCount = text.length;
|
|
288
|
+
const lineCount = text.split('\n').length;
|
|
289
|
+
|
|
290
|
+
if (!this.options.expanded) {
|
|
291
|
+
// Collapsed: show single summary line in gray
|
|
292
|
+
// This is the default - minimal visual impact
|
|
293
|
+
console.log(
|
|
294
|
+
chalk.gray.dim(' └─ ') +
|
|
295
|
+
chalk.gray.dim(this.options.icon + ' ') +
|
|
296
|
+
chalk.gray.dim.italic('thinking') +
|
|
297
|
+
chalk.gray.dim(` (${charCount} chars, ${elapsed})`)
|
|
298
|
+
);
|
|
299
|
+
} else {
|
|
300
|
+
// Expanded: show preview lines in gray
|
|
301
|
+
const lines = text.split('\n');
|
|
302
|
+
const previewLines = lines.slice(0, this.options.maxPreviewLines);
|
|
303
|
+
const truncated = lines.length > this.options.maxPreviewLines;
|
|
304
|
+
|
|
305
|
+
console.log(
|
|
306
|
+
chalk.gray.dim(' ┌─ ') +
|
|
307
|
+
chalk.gray.dim(this.options.icon + ' ') +
|
|
308
|
+
chalk.gray.dim.italic(`thinking (${elapsed})`)
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
for (const line of previewLines) {
|
|
312
|
+
const truncatedLine = line.slice(0, this.options.maxLineLength);
|
|
313
|
+
// Gray/dim styling for thinking content
|
|
314
|
+
console.log(chalk.gray.dim(' │ ') + chalk.gray.dim(truncatedLine));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (truncated) {
|
|
318
|
+
console.log(chalk.gray.dim(` └─ ... (${lines.length - this.options.maxPreviewLines} more lines)`));
|
|
319
|
+
} else {
|
|
320
|
+
console.log(chalk.gray.dim(' └─ end'));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ========================================================================
|
|
326
|
+
// Helper methods
|
|
327
|
+
// ========================================================================
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Clear N lines from current cursor position upward.
|
|
331
|
+
*/
|
|
332
|
+
private clearLines(count: number): void {
|
|
333
|
+
if (count <= 0) return;
|
|
334
|
+
|
|
335
|
+
process.stdout.write(
|
|
336
|
+
ANSI.cursorUp(count) +
|
|
337
|
+
ANSI.clearLine() +
|
|
338
|
+
'\n'.repeat(count - 1) +
|
|
339
|
+
ANSI.cursorUp(count - 1) +
|
|
340
|
+
ANSI.clearLine() +
|
|
341
|
+
ANSI.cursorLineStart()
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Calculate how many terminal lines a text will occupy.
|
|
347
|
+
*/
|
|
348
|
+
private calculateTextLines(text: string, width: number): number {
|
|
349
|
+
if (!text) return 0;
|
|
350
|
+
const lines = text.split('\n');
|
|
351
|
+
let totalLines = 0;
|
|
352
|
+
for (const line of lines) {
|
|
353
|
+
if (line.length === 0) {
|
|
354
|
+
totalLines += 1;
|
|
355
|
+
} else {
|
|
356
|
+
totalLines += Math.ceil(line.length / width);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return Math.max(1, totalLines);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Wrap text to lines respecting terminal width.
|
|
364
|
+
*/
|
|
365
|
+
private wrapTextToLines(text: string, width: number): string[] {
|
|
366
|
+
if (!text) return [];
|
|
367
|
+
|
|
368
|
+
const result: string[] = [];
|
|
369
|
+
const paragraphs = text.split('\n');
|
|
370
|
+
|
|
371
|
+
for (const paragraph of paragraphs) {
|
|
372
|
+
if (paragraph.length === 0) {
|
|
373
|
+
result.push('');
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
for (let i = 0; i < paragraph.length; i += width) {
|
|
377
|
+
result.push(paragraph.slice(i, i + width));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Format duration in human-readable form.
|
|
386
|
+
*/
|
|
387
|
+
private formatDuration(startTime: number): string {
|
|
388
|
+
const ms = Date.now() - startTime;
|
|
389
|
+
if (ms < 1000) return `${ms}ms`;
|
|
390
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
391
|
+
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ============================================================================
|
|
396
|
+
// Factory function
|
|
397
|
+
// ============================================================================
|
|
398
|
+
|
|
399
|
+
export function createThinkingBlockRenderer(options?: ThinkingBlockOptions): ThinkingBlockRenderer {
|
|
400
|
+
return new ThinkingBlockRenderer(options);
|
|
401
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// UI Components Export
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
// Ink-based components
|
|
6
|
+
export {
|
|
7
|
+
Spinner,
|
|
8
|
+
StatusBar,
|
|
9
|
+
InputBox,
|
|
10
|
+
MessageList,
|
|
11
|
+
ToolCallPanel,
|
|
12
|
+
ThinkingBlock,
|
|
13
|
+
ProgressBar,
|
|
14
|
+
ConfirmDialog,
|
|
15
|
+
SelectList,
|
|
16
|
+
Toast,
|
|
17
|
+
Colors,
|
|
18
|
+
} from './InkComponents.js';
|
|
19
|
+
|
|
20
|
+
export { NovaInkApp } from './NovaInkApp.js';
|
|
21
|
+
export { InkAppRunner, createInkApp } from './InkAppRunner.js';
|
|
22
|
+
export type { InkAppOptions } from './InkAppRunner.js';
|
|
23
|
+
|
|
24
|
+
// Legacy components
|
|
25
|
+
export { ThinkingBlockRenderer } from './ThinkingBlockRenderer.js';
|
|
26
|
+
export { ProgressBar as ProgressBarLegacy } from './ProgressBar.js';
|
|
27
|
+
export { ConfirmDialog as ConfirmDialogLegacy } from './ConfirmDialog.js';
|