zerg-ztc 0.1.10 → 0.1.12

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 (151) hide show
  1. package/bin/.gitkeep +0 -0
  2. package/bin/ztc-audio-darwin-arm64 +0 -0
  3. package/dist/App.d.ts.map +1 -1
  4. package/dist/App.js +63 -2
  5. package/dist/App.js.map +1 -1
  6. package/dist/agent/commands/dictation.d.ts +3 -0
  7. package/dist/agent/commands/dictation.d.ts.map +1 -0
  8. package/dist/agent/commands/dictation.js +10 -0
  9. package/dist/agent/commands/dictation.js.map +1 -0
  10. package/dist/agent/commands/index.d.ts.map +1 -1
  11. package/dist/agent/commands/index.js +2 -1
  12. package/dist/agent/commands/index.js.map +1 -1
  13. package/dist/agent/commands/types.d.ts +7 -0
  14. package/dist/agent/commands/types.d.ts.map +1 -1
  15. package/dist/components/InputArea.d.ts +1 -0
  16. package/dist/components/InputArea.d.ts.map +1 -1
  17. package/dist/components/InputArea.js +591 -43
  18. package/dist/components/InputArea.js.map +1 -1
  19. package/dist/components/SingleMessage.d.ts.map +1 -1
  20. package/dist/components/SingleMessage.js +157 -7
  21. package/dist/components/SingleMessage.js.map +1 -1
  22. package/dist/config/types.d.ts +6 -0
  23. package/dist/config/types.d.ts.map +1 -1
  24. package/dist/ui/views/status_bar.js +2 -2
  25. package/dist/ui/views/status_bar.js.map +1 -1
  26. package/dist/utils/dictation.d.ts +46 -0
  27. package/dist/utils/dictation.d.ts.map +1 -0
  28. package/dist/utils/dictation.js +409 -0
  29. package/dist/utils/dictation.js.map +1 -0
  30. package/dist/utils/dictation_native.d.ts +51 -0
  31. package/dist/utils/dictation_native.d.ts.map +1 -0
  32. package/dist/utils/dictation_native.js +236 -0
  33. package/dist/utils/dictation_native.js.map +1 -0
  34. package/dist/utils/path_format.d.ts +20 -0
  35. package/dist/utils/path_format.d.ts.map +1 -0
  36. package/dist/utils/path_format.js +90 -0
  37. package/dist/utils/path_format.js.map +1 -0
  38. package/dist/utils/table.d.ts +38 -0
  39. package/dist/utils/table.d.ts.map +1 -0
  40. package/dist/utils/table.js +133 -0
  41. package/dist/utils/table.js.map +1 -0
  42. package/dist/utils/tool_trace.d.ts +7 -2
  43. package/dist/utils/tool_trace.d.ts.map +1 -1
  44. package/dist/utils/tool_trace.js +156 -51
  45. package/dist/utils/tool_trace.js.map +1 -1
  46. package/package.json +5 -1
  47. package/src/App.tsx +0 -813
  48. package/src/agent/agent.ts +0 -534
  49. package/src/agent/backends/anthropic.ts +0 -86
  50. package/src/agent/backends/gemini.ts +0 -119
  51. package/src/agent/backends/inception.ts +0 -23
  52. package/src/agent/backends/index.ts +0 -17
  53. package/src/agent/backends/openai.ts +0 -23
  54. package/src/agent/backends/openai_compatible.ts +0 -143
  55. package/src/agent/backends/types.ts +0 -83
  56. package/src/agent/commands/clipboard.ts +0 -77
  57. package/src/agent/commands/config.ts +0 -204
  58. package/src/agent/commands/debug.ts +0 -23
  59. package/src/agent/commands/emulation.ts +0 -80
  60. package/src/agent/commands/execution.ts +0 -9
  61. package/src/agent/commands/help.ts +0 -20
  62. package/src/agent/commands/history.ts +0 -13
  63. package/src/agent/commands/index.ts +0 -46
  64. package/src/agent/commands/input_mode.ts +0 -22
  65. package/src/agent/commands/keybindings.ts +0 -40
  66. package/src/agent/commands/model.ts +0 -11
  67. package/src/agent/commands/models.ts +0 -116
  68. package/src/agent/commands/permissions.ts +0 -64
  69. package/src/agent/commands/retry.ts +0 -9
  70. package/src/agent/commands/shell.ts +0 -68
  71. package/src/agent/commands/skills.ts +0 -54
  72. package/src/agent/commands/status.ts +0 -19
  73. package/src/agent/commands/types.ts +0 -80
  74. package/src/agent/commands/update.ts +0 -32
  75. package/src/agent/factory.ts +0 -60
  76. package/src/agent/index.ts +0 -20
  77. package/src/agent/runtime/capabilities.ts +0 -7
  78. package/src/agent/runtime/memory.ts +0 -23
  79. package/src/agent/runtime/policy.ts +0 -48
  80. package/src/agent/runtime/session.ts +0 -18
  81. package/src/agent/runtime/tracing.ts +0 -23
  82. package/src/agent/tools/file.ts +0 -178
  83. package/src/agent/tools/index.ts +0 -52
  84. package/src/agent/tools/screenshot.ts +0 -821
  85. package/src/agent/tools/search.ts +0 -138
  86. package/src/agent/tools/shell.ts +0 -69
  87. package/src/agent/tools/skills.ts +0 -28
  88. package/src/agent/tools/types.ts +0 -14
  89. package/src/agent/tools/zerg.ts +0 -50
  90. package/src/cli.tsx +0 -163
  91. package/src/components/ActivityLine.tsx +0 -23
  92. package/src/components/FullScreen.tsx +0 -79
  93. package/src/components/Header.tsx +0 -27
  94. package/src/components/InputArea.tsx +0 -1096
  95. package/src/components/MessageList.tsx +0 -71
  96. package/src/components/SingleMessage.tsx +0 -59
  97. package/src/components/StatusBar.tsx +0 -55
  98. package/src/components/index.tsx +0 -8
  99. package/src/config/types.ts +0 -12
  100. package/src/config.ts +0 -186
  101. package/src/debug/logger.ts +0 -14
  102. package/src/emulation/README.md +0 -24
  103. package/src/emulation/catalog.ts +0 -82
  104. package/src/emulation/trace_style.ts +0 -8
  105. package/src/emulation/types.ts +0 -7
  106. package/src/skills/index.ts +0 -36
  107. package/src/skills/loader.ts +0 -135
  108. package/src/skills/registry.ts +0 -6
  109. package/src/skills/types.ts +0 -10
  110. package/src/types.ts +0 -84
  111. package/src/ui/README.md +0 -44
  112. package/src/ui/core/factory.ts +0 -9
  113. package/src/ui/core/index.ts +0 -4
  114. package/src/ui/core/input.ts +0 -38
  115. package/src/ui/core/input_segments.ts +0 -410
  116. package/src/ui/core/input_state.ts +0 -17
  117. package/src/ui/core/layout_yoga.ts +0 -122
  118. package/src/ui/core/style.ts +0 -38
  119. package/src/ui/core/types.ts +0 -54
  120. package/src/ui/ink/index.tsx +0 -1
  121. package/src/ui/ink/render.tsx +0 -60
  122. package/src/ui/views/activity_line.ts +0 -33
  123. package/src/ui/views/app.ts +0 -111
  124. package/src/ui/views/header.ts +0 -44
  125. package/src/ui/views/input_area.ts +0 -255
  126. package/src/ui/views/message_list.ts +0 -443
  127. package/src/ui/views/status_bar.ts +0 -114
  128. package/src/ui/vue/index.ts +0 -53
  129. package/src/ui/web/frame_render.tsx +0 -148
  130. package/src/ui/web/index.tsx +0 -1
  131. package/src/ui/web/render.tsx +0 -41
  132. package/src/utils/clipboard.ts +0 -39
  133. package/src/utils/clipboard_image.ts +0 -40
  134. package/src/utils/diff.ts +0 -52
  135. package/src/utils/image_preview.ts +0 -36
  136. package/src/utils/models.ts +0 -98
  137. package/src/utils/path_complete.ts +0 -173
  138. package/src/utils/shell.ts +0 -72
  139. package/src/utils/spinner_frames.ts +0 -1
  140. package/src/utils/spinner_verbs.ts +0 -23
  141. package/src/utils/tool_summary.ts +0 -56
  142. package/src/utils/tool_trace.ts +0 -216
  143. package/src/utils/update.ts +0 -44
  144. package/src/utils/version.ts +0 -15
  145. package/src/web/index.html +0 -352
  146. package/src/web/mirror-favicon.svg +0 -4
  147. package/src/web/mirror.html +0 -641
  148. package/src/web/mirror_hook.ts +0 -25
  149. package/src/web/mirror_server.ts +0 -204
  150. package/tsconfig.json +0 -22
  151. package/vite.config.ts +0 -363
@@ -1,33 +0,0 @@
1
- import { AgentState } from '../../types.js';
2
- import { box, text, LayoutNode } from '../core/index.js';
3
-
4
- interface ActivityLineProps {
5
- state: AgentState;
6
- spinnerLabel?: string | null;
7
- spinnerFrame?: string | null;
8
- inputMode?: 'queue' | 'interrupt';
9
- }
10
-
11
- export function buildActivityLineView({
12
- state,
13
- spinnerLabel,
14
- spinnerFrame,
15
- inputMode
16
- }: ActivityLineProps): LayoutNode {
17
- const active = state.status === 'thinking' || state.status === 'streaming';
18
- if (!active || !spinnerLabel) {
19
- return box([], { height: 0 });
20
- }
21
- const frame = spinnerFrame || '✽';
22
- const hint = inputMode === 'interrupt' ? ' (esc to interrupt)' : '';
23
- return box([
24
- text(`${frame} ${spinnerLabel}…${hint}`, { color: 'yellow' })
25
- ], {
26
- flexDirection: 'row',
27
- paddingX: 2,
28
- paddingY: 0,
29
- marginTop: 1,
30
- marginBottom: 1,
31
- height: 1
32
- });
33
- }
@@ -1,111 +0,0 @@
1
- import { Message, AgentState } from '../../types.js';
2
- import { InputState } from '../core/input_state.js';
3
- import { box, LayoutNode } from '../core/index.js';
4
- import { buildHeaderView } from './header.js';
5
- import { buildMessageListView } from './message_list.js';
6
- import { buildInputAreaView, estimateInputLines } from './input_area.js';
7
- import { buildStatusBarView } from './status_bar.js';
8
- import { buildActivityLineView } from './activity_line.js';
9
-
10
- interface AppViewProps {
11
- messages: Message[];
12
- agentState: AgentState;
13
- inputState: InputState;
14
- sessionId: string;
15
- cols: number;
16
- rows: number;
17
- commands: Array<{ name: string; description: string; usage?: string }>;
18
- hasApiKey: boolean;
19
- version?: string;
20
- contextLength?: number;
21
- contextEstimated?: boolean;
22
- provider?: string;
23
- model?: string;
24
- emulationId?: string;
25
- toast?: string | null;
26
- spinnerLabel?: string | null;
27
- spinnerFrame?: string | null;
28
- inputMode?: 'queue' | 'interrupt';
29
- debug?: boolean;
30
- expandToolOutputs?: boolean;
31
- }
32
-
33
- function estimateBadgePreviewLines(state: InputState): number {
34
- const segment = state.segments[state.cursor.index];
35
- if (!segment || segment.type === 'text') return 0;
36
- if (segment.type === 'paste') {
37
- const lines = segment.text.split('\n');
38
- return Math.min(3, lines.length) + (lines.length > 3 ? 1 : 0);
39
- }
40
- if (segment.type === 'image') {
41
- return 16;
42
- }
43
- return 1;
44
- }
45
-
46
- export function buildAppView({
47
- messages,
48
- agentState,
49
- inputState,
50
- sessionId,
51
- cols,
52
- rows,
53
- commands,
54
- hasApiKey,
55
- version = '0.1.0',
56
- contextLength,
57
- contextEstimated,
58
- provider,
59
- model,
60
- emulationId,
61
- inputMode,
62
- toast,
63
- spinnerLabel,
64
- spinnerFrame,
65
- debug = false,
66
- expandToolOutputs = false
67
- }: AppViewProps): LayoutNode {
68
- const suggestionLines = 4;
69
- const headerHeight = 1;
70
- const statusHeight = 1;
71
- const activityHeight = (agentState.status === 'thinking' || agentState.status === 'streaming') && spinnerLabel ? 3 : 0;
72
- const inputLineCount = estimateInputLines(inputState.segments, cols);
73
- const previewLines = estimateBadgePreviewLines(inputState);
74
- const maxInputLines = Math.max(1, rows - (headerHeight + statusHeight + activityHeight + suggestionLines + 5));
75
- const inputHeight = Math.min(Math.max(1, inputLineCount + previewLines), maxInputLines) + suggestionLines;
76
- const contentHeight = Math.max(rows - (headerHeight + inputHeight + statusHeight + activityHeight), 5);
77
-
78
- const placeholder = !hasApiKey ? 'Set API key with /config key <key>' :
79
- agentState.status === 'thinking' ? 'Thinking...' :
80
- agentState.status === 'tool_use' ? `Running ${agentState.currentTool}...` :
81
- agentState.status === 'streaming' ? 'Streaming response...' :
82
- 'Type a message or /help for commands...';
83
-
84
- return box([
85
- buildHeaderView({ version, debug }),
86
- buildMessageListView({ messages, height: contentHeight, debug, expandToolOutputs }),
87
- buildActivityLineView({ state: agentState, spinnerLabel, spinnerFrame, inputMode }),
88
- buildInputAreaView({
89
- state: inputState,
90
- placeholder,
91
- disabled: agentState.status !== 'idle' && agentState.status !== 'error',
92
- commands,
93
- cols,
94
- debug
95
- }),
96
- buildStatusBarView({
97
- state: agentState,
98
- sessionId,
99
- version,
100
- connectionStatus: hasApiKey ? 'connected' : 'disconnected',
101
- contextLength,
102
- contextEstimated,
103
- provider,
104
- model,
105
- emulationId,
106
- inputMode,
107
- toast,
108
- debug
109
- })
110
- ], { flexDirection: 'column' });
111
- }
@@ -1,44 +0,0 @@
1
- import { box, text, LayoutNode } from '../core/index.js';
2
-
3
- interface HeaderProps {
4
- title?: string;
5
- version?: string;
6
- dateLabel?: string;
7
- showHelp?: boolean;
8
- debug?: boolean;
9
- }
10
-
11
- export function buildHeaderView({
12
- title = 'Zerg Terminal Client',
13
- version = '0.1.0',
14
- dateLabel,
15
- showHelp = true,
16
- debug = false
17
- }: HeaderProps): LayoutNode {
18
- return box([
19
- box([
20
- text(title, { bold: true }),
21
- text(' ', {}),
22
- text('ZTC', { color: 'gray', dimColor: true })
23
- ], { flexDirection: 'row' }),
24
- showHelp
25
- ? box([
26
- dateLabel ? text(dateLabel, { color: 'gray', dimColor: true }) : text('', {}),
27
- dateLabel ? text(' • ', { color: 'gray', dimColor: true }) : text('', {}),
28
- text(`v${version}`, { color: 'gray', dimColor: true }),
29
- text(' • ', { color: 'gray', dimColor: true }),
30
- text('Ctrl+C exit', { color: 'gray', dimColor: true }),
31
- text(' • ', { color: 'gray', dimColor: true }),
32
- text('/help', { color: 'gray', dimColor: true })
33
- ], { flexDirection: 'row' })
34
- : box([], { flexDirection: 'row' })
35
- ], {
36
- flexDirection: 'row',
37
- justifyContent: 'space-between',
38
- paddingX: 1,
39
- flexShrink: 0,
40
- borderStyle: 'single',
41
- borderColor: debug ? 'cyan' : 'gray',
42
- marginBottom: 1
43
- });
44
- }
@@ -1,255 +0,0 @@
1
- import { InputState, InputSegment } from '../core/input_state.js';
2
- import { box, text, LayoutNode } from '../core/index.js';
3
-
4
- interface InputAreaViewProps {
5
- state: InputState;
6
- placeholder: string;
7
- disabled?: boolean;
8
- commands?: Array<{
9
- name: string;
10
- description: string;
11
- usage?: string;
12
- }>;
13
- cols?: number;
14
- badgePreview?: string[] | null;
15
- showBadgePreview?: boolean;
16
- debug?: boolean;
17
- renderContent?: boolean;
18
- }
19
-
20
- const suggestionLines = 4;
21
- const promptPrefix = '❯ ';
22
- const promptWidth = promptPrefix.length;
23
-
24
- interface RenderToken {
25
- text: string;
26
- style?: { color?: string; dimColor?: boolean; bold?: boolean; inverse?: boolean; badge?: {
27
- type: 'paste' | 'file' | 'image';
28
- preview: string;
29
- full: string;
30
- path?: string;
31
- } };
32
- }
33
-
34
- function badgeLabel(segment: InputSegment): string {
35
- switch (segment.type) {
36
- case 'paste': {
37
- const lines = segment.text.split('\n').length;
38
- return `[pasted ${lines} line${lines === 1 ? '' : 's'}]`;
39
- }
40
- case 'file':
41
- return `[file ${segment.path}]`;
42
- case 'image':
43
- return `[image ${segment.path}]`;
44
- default:
45
- return '';
46
- }
47
- }
48
-
49
- function badgePreview(segment: InputSegment): { preview: string; full: string; path?: string; type: 'paste' | 'file' | 'image' } {
50
- switch (segment.type) {
51
- case 'paste': {
52
- const lines = segment.text.split('\n');
53
- const previewLines = lines.slice(0, 3).join('\n');
54
- const suffix = lines.length > 3 ? '\n…' : '';
55
- return { type: 'paste', preview: previewLines + suffix, full: segment.text };
56
- }
57
- case 'image':
58
- return { type: 'image', preview: `Image: ${segment.path}`, full: segment.path, path: segment.path };
59
- case 'file':
60
- return { type: 'file', preview: `File: ${segment.path}`, full: segment.path, path: segment.path };
61
- default:
62
- return { type: 'file', preview: 'File: (unknown)', full: '' };
63
- }
64
- }
65
-
66
- export function buildRenderTokens(segments: InputSegment[]): RenderToken[] {
67
- return segments.map(segment => {
68
- if (segment.type === 'text') {
69
- return { text: segment.text };
70
- }
71
- const badge = badgePreview(segment);
72
- return { text: badgeLabel(segment), style: { color: 'yellow', dimColor: true, badge } };
73
- });
74
- }
75
-
76
- export function wrapInputSegments(
77
- segments: InputSegment[],
78
- cursor: { index: number; offset: number },
79
- cols: number,
80
- prefixWidth = promptWidth
81
- ): { lines: RenderToken[][]; cursorLine: number; cursorCol: number } {
82
- const wrapWidth = Math.max(10, cols - prefixWidth - 2);
83
- const tokens = buildRenderTokens(segments);
84
- let cursorPos = 0;
85
- let pos = 0;
86
- for (let i = 0; i < segments.length; i += 1) {
87
- const segment = segments[i];
88
- const tokenText = tokens[i].text;
89
- if (i === cursor.index) {
90
- if (segment.type === 'text') {
91
- cursorPos = pos + cursor.offset;
92
- } else {
93
- cursorPos = pos + (cursor.offset > 0 ? tokenText.length : 0);
94
- }
95
- }
96
- pos += tokenText.length;
97
- }
98
- if (cursor.index === segments.length) {
99
- cursorPos = pos;
100
- }
101
-
102
- const lines: RenderToken[][] = [[]];
103
- let lineIndex = 0;
104
- let col = 0;
105
- let cursorLine = 0;
106
- let cursorCol = 0;
107
- let globalPos = 0;
108
-
109
- for (const token of tokens) {
110
- const textValue = token.text || '';
111
- for (let i = 0; i < textValue.length; i += 1) {
112
- if (globalPos === cursorPos) {
113
- cursorLine = lineIndex;
114
- cursorCol = col;
115
- }
116
- const ch = textValue[i];
117
- if (ch === '\n') {
118
- lineIndex += 1;
119
- lines.push([]);
120
- col = 0;
121
- globalPos += 1;
122
- continue;
123
- }
124
- if (col >= wrapWidth) {
125
- lineIndex += 1;
126
- lines.push([]);
127
- col = 0;
128
- }
129
- lines[lineIndex].push({ text: ch, style: token.style });
130
- col += 1;
131
- globalPos += 1;
132
- }
133
- }
134
-
135
- if (cursorPos === globalPos) {
136
- cursorLine = lineIndex;
137
- cursorCol = col;
138
- }
139
-
140
- return { lines, cursorLine, cursorCol };
141
- }
142
-
143
- export function estimateInputLines(segments: InputSegment[], cols: number): number {
144
- return wrapInputSegments(segments, { index: segments.length, offset: 0 }, cols).lines.length;
145
- }
146
-
147
- export function buildInputAreaView({
148
- state,
149
- placeholder,
150
- disabled = false,
151
- commands = [],
152
- cols = 80,
153
- badgePreview,
154
- showBadgePreview = false,
155
- debug = false,
156
- renderContent = true
157
- }: InputAreaViewProps): LayoutNode {
158
- if (!renderContent) {
159
- return box([text('')], { flexDirection: 'column', flexShrink: 0, height: 5 });
160
- }
161
- const truncate = (value: string, max: number) => (
162
- value.length > max ? `${value.slice(0, max - 1)}…` : value
163
- );
164
- const promptColor = disabled ? 'gray' : 'blue';
165
-
166
- const plainText = state.segments.length === 1 && state.segments[0].type === 'text'
167
- ? state.segments[0].text
168
- : '';
169
- const isCommandMode = plainText.startsWith('/');
170
- const commandQuery = isCommandMode ? plainText.slice(1).trim() : '';
171
- const commandMatches = isCommandMode
172
- ? commands.filter(c => c.name.startsWith(commandQuery)).slice(0, suggestionLines)
173
- : [];
174
-
175
- let inputLines: LayoutNode[] = [];
176
- if (disabled) {
177
- inputLines = [
178
- box([
179
- text(promptPrefix, { color: promptColor, bold: true }),
180
- text(placeholder, { color: 'gray', dimColor: true })
181
- ], { flexDirection: 'row' })
182
- ];
183
- } else if (state.segments.length === 0) {
184
- inputLines = [
185
- box([
186
- text(promptPrefix, { color: promptColor, bold: true }),
187
- box([
188
- text('|', { inverse: true }),
189
- text(placeholder, { color: 'gray', dimColor: true })
190
- ], { flexDirection: 'row' })
191
- ], { flexDirection: 'row' })
192
- ];
193
- } else {
194
- const wrapped = wrapInputSegments(state.segments, state.cursor, cols, promptWidth);
195
- inputLines = wrapped.lines.map((line, index) => {
196
- const prefix = index === 0 ? promptPrefix : ' ';
197
- const segments: LayoutNode[] = [text(prefix, { color: promptColor, bold: index === 0 })];
198
- line.forEach((token, tokenIndex) => {
199
- if (index === wrapped.cursorLine && tokenIndex === wrapped.cursorCol) {
200
- segments.push(text(token.text || ' ', { inverse: true, ...token.style }));
201
- } else {
202
- segments.push(text(token.text, token.style));
203
- }
204
- });
205
- if (index === wrapped.cursorLine && wrapped.cursorCol === line.length) {
206
- segments.push(text(' ', { inverse: true }));
207
- }
208
- return box(segments, { flexDirection: 'row' });
209
- });
210
- }
211
-
212
- const inputLine = box(inputLines, {
213
- flexDirection: 'column',
214
- paddingX: 1,
215
- borderStyle: debug ? 'round' : undefined,
216
- borderColor: debug ? (disabled ? 'gray' : 'blue') : undefined
217
- });
218
-
219
- // Only render suggestion lines if there are actual command matches
220
- const suggestions = commandMatches.length > 0
221
- ? box(
222
- commandMatches.map((cmd) => {
223
- const usage = cmd.usage ? ` ${truncate(cmd.usage, 36)}` : '';
224
- return box([
225
- text(`/${cmd.name}`, { color: 'cyan', bold: true }),
226
- text(usage, { color: 'white' }),
227
- text(` — ${truncate(cmd.description, 48)}`, { color: 'gray', dimColor: true })
228
- ], { flexDirection: 'row' });
229
- }),
230
- { flexDirection: 'column', paddingX: 2 }
231
- )
232
- : null;
233
-
234
- const previewLines = showBadgePreview && badgePreview && badgePreview.length > 0
235
- ? box(badgePreview.map(line => text(line, { color: 'gray', dimColor: true })), {
236
- flexDirection: 'column',
237
- paddingX: 2
238
- })
239
- : null;
240
-
241
- // Horizontal separator lines
242
- const separatorTop = text('─'.repeat(Math.max(10, cols - 2)), { color: 'gray', dimColor: true });
243
- const separatorBottom = text('─'.repeat(Math.max(10, cols - 2)), { color: 'gray', dimColor: true });
244
-
245
- // Build the content array, filtering out nulls
246
- const content: LayoutNode[] = [separatorTop, inputLine];
247
- if (previewLines) content.push(previewLines);
248
- if (suggestions) content.push(suggestions);
249
- content.push(separatorBottom);
250
-
251
- return box(content, {
252
- flexDirection: 'column',
253
- flexShrink: 0
254
- });
255
- }