moderado 0.4.10 → 0.4.26

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.
@@ -9,11 +9,14 @@ export interface WelcomeLayoutOptions {
9
9
  mode: 'Plan' | 'Execute';
10
10
  autoApprove: boolean;
11
11
  input?: string;
12
+ inputCaret?: number;
12
13
  width?: number;
13
14
  /** When set, render the full chat window (question row 1 + answer) instead of the logo header. */
14
15
  chatQuestion?: string;
15
16
  chatAnswer?: string;
16
17
  chatThoughtTime?: number;
18
+ /** Settled turn completed successfully — render the "✔ Done" status marker. */
19
+ chatTaskDone?: boolean;
17
20
  /** Generated completion tokens per second for the preceding response. */
18
21
  outputTokenRate?: number;
19
22
  tokenUsage?: UsageEvent;
@@ -31,6 +34,18 @@ export declare function stripAnsi(str: string): string;
31
34
  export declare function getWelcomeCardWidth(width?: number): number;
32
35
  /** Left indent that centers a block of `blockWidth` visible columns on the terminal. */
33
36
  export declare function getWelcomeIndent(blockWidth: number, width?: number): number;
37
+ /**
38
+ * Geometry of the answer area: it occupies the same columns as the composer
39
+ * card, so the question row, the streamed answer, and the input box all share
40
+ * one width instead of stretching across a wide terminal.
41
+ */
42
+ export declare function getChatAnswerArea(width?: number): {
43
+ indent: number;
44
+ cardWidth: number;
45
+ textWidth: number;
46
+ startColumn: number;
47
+ wrapWidth: number;
48
+ };
34
49
  export declare function renderQueuedCommandsBox(commands: readonly string[], width: number): string[];
35
50
  export declare function renderWelcomeCard(options: WelcomeLayoutOptions): string;
36
51
  export interface SlashCommand {
@@ -40,10 +55,10 @@ export interface SlashCommand {
40
55
  export declare const SLASH_COMMANDS: SlashCommand[];
41
56
  export declare function getMatchingCommands(input: string): SlashCommand[];
42
57
  export declare function selectCommandCandidate(input: string, currentIndex: number, direction: number): SlashCommand | undefined;
43
- export declare function renderSuggestionsBox(commands: SlashCommand[], width?: number): string[];
44
- export declare function renderMentionSuggestionsBox(files: string[], selectedIndex?: number): string[];
45
- export declare function renderModeradoHeader(): string;
46
- /** Word-wrap plain text to `maxWidth` visible columns. */
58
+ export declare function renderSuggestionsBox(commands: SlashCommand[], width?: number, selectedIndex?: number): string[];
59
+ export declare function renderMentionSuggestionsBox(files: string[], selectedIndex?: number, width?: number): string[];
60
+ export declare function renderModeradoHeader(width?: number): string;
61
+ /** Word-wrap plain text to `maxWidth` visible columns, hard-breaking long words. */
47
62
  export declare function wrapText(text: string, maxWidth: number): string[];
48
63
  /**
49
64
  * Full chat window (after a question is submitted): no logo — the question is
@@ -59,13 +74,20 @@ export declare function navigateQuestionHistory(history: string[], direction: -1
59
74
  draft: string;
60
75
  };
61
76
  /** Update the fixed chat elapsed-time row without repainting the terminal. */
62
- export declare function renderChatThoughtTimeUpdate(thoughtTime: number): string;
77
+ export declare function renderChatThoughtTimeUpdate(thoughtTime: number, taskStep?: {
78
+ step: number;
79
+ maxSteps: number;
80
+ }, width?: number): string;
63
81
  export interface ChatAnswerPosition {
64
82
  row: number;
65
83
  column: number;
66
84
  }
67
- /** Write one streamed fragment into the answer area without repainting the chat frame. */
68
- export declare function renderChatAnswerDelta(delta: string, start: ChatAnswerPosition, width: number): ChatAnswerPosition & {
85
+ /**
86
+ * Write one streamed fragment into the answer area without repainting the chat
87
+ * frame. Wrapped lines return to `startColumn` so the answer keeps the same
88
+ * columns as the composer card instead of stretching across the terminal.
89
+ */
90
+ export declare function renderChatAnswerDelta(delta: string, start: ChatAnswerPosition, width: number, startColumn?: number): ChatAnswerPosition & {
69
91
  sequence: string;
70
92
  };
71
93
  export declare function renderFullWelcomeScreen(options: WelcomeLayoutOptions, height?: number): string;
@@ -107,6 +129,7 @@ export interface PromptInteractiveTurnOptions {
107
129
  chatQuestion?: string;
108
130
  chatAnswer?: string;
109
131
  chatThoughtTime?: number;
132
+ chatTaskDone?: boolean;
110
133
  outputTokenRate?: number;
111
134
  tokenUsage?: UsageEvent;
112
135
  questionHistory?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/ui/welcome.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/ui/welcome.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,11 @@ export function stripAnsi(str) {
16
16
  function visibleLen(str) {
17
17
  return stripAnsi(str).length;
18
18
  }
19
+ function composerInputWindow(input, caret, width) {
20
+ const capacity = Math.max(1, width - 3);
21
+ const start = Math.max(0, Math.min(caret, input.length - capacity));
22
+ return { text: input.slice(start, start + capacity), caretColumn: caret - start };
23
+ }
19
24
  /** Card width: nearly full terminal width, like OpenCode/Cline. */
20
25
  export function getWelcomeCardWidth(width) {
21
26
  const terminalWidth = width ?? (process.stdout.columns || 80);
@@ -26,13 +31,28 @@ export function getWelcomeIndent(blockWidth, width) {
26
31
  const terminalWidth = width ?? (process.stdout.columns || 80);
27
32
  return Math.max(0, Math.floor((terminalWidth - blockWidth) / 2));
28
33
  }
29
- /** Center each line of a block independently (used for the ASCII logo / hints). */
34
+ /** Center each line of a block inside the input card's columns (used for the ASCII logo / hints). */
30
35
  function centerBlock(lines, width) {
36
+ const terminalWidth = width ?? (process.stdout.columns || 80);
37
+ const cardWidth = getWelcomeCardWidth(terminalWidth);
38
+ const cardIndent = getWelcomeIndent(cardWidth, terminalWidth);
31
39
  return lines.map((line) => {
32
- const pad = getWelcomeIndent(visibleLen(line), width);
33
- return ' '.repeat(pad) + line;
40
+ const pad = Math.max(0, Math.floor((cardWidth - visibleLen(line)) / 2));
41
+ return ' '.repeat(cardIndent + pad) + line;
34
42
  });
35
43
  }
44
+ /**
45
+ * Geometry of the answer area: it occupies the same columns as the composer
46
+ * card, so the question row, the streamed answer, and the input box all share
47
+ * one width instead of stretching across a wide terminal.
48
+ */
49
+ export function getChatAnswerArea(width) {
50
+ const terminalWidth = width ?? (process.stdout.columns || 80);
51
+ const cardWidth = getWelcomeCardWidth(terminalWidth);
52
+ const indent = getWelcomeIndent(cardWidth, terminalWidth);
53
+ const textWidth = Math.max(20, cardWidth - 2);
54
+ return { indent, cardWidth, textWidth, startColumn: indent + 1, wrapWidth: indent + textWidth };
55
+ }
36
56
  export function renderQueuedCommandsBox(commands, width) {
37
57
  if (!commands || commands.length === 0)
38
58
  return [];
@@ -63,7 +83,7 @@ export function renderWelcomeCard(options) {
63
83
  const width = getWelcomeCardWidth(options.width);
64
84
  const indent = ' '.repeat(getWelcomeIndent(width, options.width));
65
85
  const displayInput = options.input && options.input.length > 0
66
- ? options.input
86
+ ? composerInputWindow(options.input, options.inputCaret ?? options.input.length, width).text
67
87
  : '\x1b[38;5;242mAsk anything, I am all ears...\x1b[0m';
68
88
  const promptMarker = '\x1b[1;38;5;75m' + String.fromCodePoint(0x276F) + '\x1b[0;48;5;236m';
69
89
  const textBox = `${promptMarker} ${displayInput}`;
@@ -93,18 +113,21 @@ export function renderWelcomeCard(options) {
93
113
  const spaces4Count = Math.max(1, width - visibleLen(left4) - right4Raw.length);
94
114
  const line4 = left4 + ' '.repeat(spaces4Count) + right4;
95
115
  // Line 5: Working directory (left) ... model and Auto-approve (right)
96
- const shortWs = options.workspace.length > 36
97
- ? '...' + options.workspace.slice(-33)
116
+ const approvalLabelRaw = options.autoApprove
117
+ ? 'Auto-approve enabled (Shift+Tab)'
118
+ : 'Auto-approve off (Shift+Tab)';
119
+ const modelSpace = Math.max(8, width - approvalLabelRaw.length - 10);
120
+ const shortModel = options.model.length > modelSpace ? '...' + options.model.slice(-(modelSpace - 3)) : options.model;
121
+ const workspaceSpace = Math.max(3, width - approvalLabelRaw.length - shortModel.length - 2);
122
+ const shortWs = options.workspace.length > workspaceSpace
123
+ ? '...' + options.workspace.slice(-(workspaceSpace - 3))
98
124
  : options.workspace;
99
125
  const left5 = `\x1b[38;5;245m${shortWs}\x1b[0m`;
100
126
  const approvalLabel = options.autoApprove
101
127
  ? '\x1b[38;5;114mAuto-approve enabled (Shift+Tab)\x1b[0m'
102
128
  : '\x1b[38;5;242mAuto-approve off (Shift+Tab)\x1b[0m';
103
- const approvalLabelRaw = options.autoApprove
104
- ? 'Auto-approve enabled (Shift+Tab)'
105
- : 'Auto-approve off (Shift+Tab)';
106
- const modelLabel = `\x1b[38;5;180m${options.model}\x1b[0m`;
107
- const spaces5Count = Math.max(1, width - shortWs.length - options.model.length - approvalLabelRaw.length - 2);
129
+ const modelLabel = `\x1b[38;5;180m${shortModel}\x1b[0m`;
130
+ const spaces5Count = Math.max(1, width - shortWs.length - shortModel.length - approvalLabelRaw.length - 1);
108
131
  const line5 = left5 + ' '.repeat(spaces5Count) + modelLabel + ' ' + approvalLabel;
109
132
  const cardLines = [];
110
133
  const queuedBox = renderQueuedCommandsBox(options.queuedCommands ?? [], width);
@@ -115,11 +138,11 @@ export function renderWelcomeCard(options) {
115
138
  if (!options.mentionFiles?.length && options.input?.startsWith('/')) {
116
139
  const matching = getMatchingCommands(options.input);
117
140
  if (matching.length)
118
- cardLines.push(...renderSuggestionsBox(matching, width).map(line => indent + line));
141
+ cardLines.push(...renderSuggestionsBox(matching, width, options.commandSelection ?? 0).map(line => indent + line));
119
142
  }
120
143
  cardLines.push(indent + surfaceLine(), indent + surfaceLine(textBox), indent + surfaceLine(), indent + line4, indent + line5);
121
144
  if (options.mentionFiles && options.mentionFiles.length > 0) {
122
- cardLines.push(...renderMentionSuggestionsBox(options.mentionFiles, options.mentionSelection ?? 0).map((line) => indent + line));
145
+ cardLines.push(...renderMentionSuggestionsBox(options.mentionFiles, options.mentionSelection ?? 0, width).map((line) => indent + line));
123
146
  }
124
147
  return cardLines.join('\n');
125
148
  }
@@ -153,60 +176,88 @@ export function selectCommandCandidate(input, currentIndex, direction) {
153
176
  const index = ((currentIndex + direction) % commands.length + commands.length) % commands.length;
154
177
  return commands[index];
155
178
  }
156
- export function renderSuggestionsBox(commands, width) {
179
+ export function renderSuggestionsBox(commands, width, selectedIndex = 0) {
157
180
  if (commands.length === 0)
158
181
  return [];
159
- const terminalWidth = width ?? (process.stdout?.columns || 80);
160
- const targetWidth = 82;
161
- const boxWidth = Math.max(78, Math.min(terminalWidth, targetWidth));
182
+ // Exactly the card width so the box shares the composer's columns instead of
183
+ // forcing its own (previously a 78–82 fixed width that escaped narrow cards).
184
+ const boxWidth = width ?? getWelcomeCardWidth(process.stdout?.columns || 80);
162
185
  const innerW = boxWidth - 4;
163
- const title = 'Commands (Press Tab to autocomplete)';
186
+ const title = 'Commands (↑↓ browse, Tab to insert)';
164
187
  const remainingDashes = Math.max(0, boxWidth - title.length - 5);
165
188
  const nameColWidth = 10;
166
189
  const availDescWidth = Math.max(20, innerW - nameColWidth - 2);
167
190
  const lines = [];
168
191
  lines.push('\x1b[38;5;240m╭─ \x1b[1;38;5;75m' + title + '\x1b[0;38;5;240m ' + '─'.repeat(remainingDashes) + '╮\x1b[0m');
169
- for (const cmd of commands) {
170
- const nameStr = `\x1b[1;38;5;75m${cmd.name.padEnd(nameColWidth)}\x1b[0m`;
192
+ for (const [index, cmd] of commands.entries()) {
193
+ const isSelected = index === selectedIndex;
171
194
  let desc = cmd.desc;
172
195
  if (desc.length > availDescWidth) {
173
196
  desc = desc.slice(0, availDescWidth - 1) + '…';
174
197
  }
175
- const descStr = `\x1b[38;5;244m${desc.padEnd(availDescWidth)}\x1b[0m`;
176
- lines.push(`\x1b[38;5;240m│\x1b[0m ${nameStr} ${descStr}\x1b[38;5;240m│\x1b[0m`);
198
+ const nameCell = cmd.name.padEnd(nameColWidth);
199
+ const descCell = desc.padEnd(availDescWidth);
200
+ if (isSelected) {
201
+ // Highlight bar matches the app's list popups: a ❯ cursor plus a dark
202
+ // background across the whole row, so the arrow-key selection is visible.
203
+ lines.push('\x1b[38;5;240m│\x1b[0m' +
204
+ '\x1b[48;5;236m\x1b[1;38;5;75m❯ ' + nameCell +
205
+ '\x1b[0;48;5;236;38;5;253m ' + descCell + '\x1b[0m' +
206
+ '\x1b[38;5;240m│\x1b[0m');
207
+ }
208
+ else {
209
+ lines.push('\x1b[38;5;240m│\x1b[0m' +
210
+ ' ' + `\x1b[1;38;5;75m${nameCell}\x1b[0m` + ' ' +
211
+ `\x1b[38;5;244m${descCell}\x1b[0m` +
212
+ '\x1b[38;5;240m│\x1b[0m');
213
+ }
177
214
  }
178
215
  lines.push('\x1b[38;5;240m╰' + '─'.repeat(boxWidth - 2) + '╯\x1b[0m');
179
216
  return lines;
180
217
  }
181
- export function renderMentionSuggestionsBox(files, selectedIndex = 0) {
218
+ export function renderMentionSuggestionsBox(files, selectedIndex = 0, width) {
182
219
  if (files.length === 0)
183
220
  return [];
221
+ // Sized to the composer card (like the queued/commands boxes) so it never
222
+ // breaks the card's left/right boundary.
223
+ const boxWidth = width ?? getWelcomeCardWidth(process.stdout?.columns || 80);
224
+ const title = 'Files (@ to mention, Tab to insert)';
184
225
  const lines = [];
185
- lines.push('\x1b[38;5;240m ╭─ Files (@ to mention, Tab to insert) ──────────────╮\x1b[0m');
226
+ const remainingDashes = Math.max(0, boxWidth - title.length - 5);
227
+ lines.push('\x1b[38;5;240m╭─ \x1b[1;38;5;75m' + title + '\x1b[0;38;5;240m ' + '─'.repeat(remainingDashes) + '╮\x1b[0m');
186
228
  for (let i = 0; i < files.length; i++) {
187
229
  const file = files[i];
188
230
  const isSelected = i === selectedIndex;
189
231
  const marker = isSelected ? '\x1b[1;38;5;75m❯ \x1b[0m' : ' ';
190
- const nameStr = isSelected ? `\x1b[1;38;5;75m@${file}\x1b[0m` : `\x1b[38;5;250m@${file}\x1b[0m`;
191
- const lineContent = `${marker}${nameStr}`;
192
- const plainLen = visibleLen(lineContent);
193
- const pad = Math.max(0, 52 - plainLen);
194
- lines.push(`\x1b[38;5;240m │\x1b[0m ${lineContent}${' '.repeat(pad)}\x1b[38;5;240m│\x1b[0m`);
232
+ // Visible budget: │ + space + marker(2) + label + pad + │ = boxWidth.
233
+ const labelCapacity = Math.max(4, boxWidth - 5);
234
+ let label = `@${file}`;
235
+ if (label.length > labelCapacity)
236
+ label = `@${file.slice(0, Math.max(1, labelCapacity - 4))}...`;
237
+ const nameStr = isSelected ? `\x1b[1;38;5;75m${label}\x1b[0m` : `\x1b[38;5;250m${label}\x1b[0m`;
238
+ const pad = Math.max(0, boxWidth - 5 - label.length);
239
+ lines.push(`\x1b[38;5;240m│\x1b[0m ${marker}${nameStr}${' '.repeat(pad)}\x1b[38;5;240m│\x1b[0m`);
195
240
  }
196
- lines.push('\x1b[38;5;240m ╰─────────────────────────────────────────────────────╯\x1b[0m');
241
+ lines.push('\x1b[38;5;240m╰' + '─'.repeat(Math.max(0, boxWidth - 2)) + '╯\x1b[0m');
197
242
  return lines;
198
243
  }
199
- export function renderModeradoHeader() {
244
+ export function renderModeradoHeader(width) {
200
245
  return [
201
246
  '',
202
- ...centerBlock(MODERADO_ASCII_LOGO),
247
+ ...centerBlock(MODERADO_ASCII_LOGO, width),
203
248
  '',
204
- ...centerBlock([COMMAND_HINT]),
249
+ ...centerBlock([COMMAND_HINT], width),
205
250
  ].join('\n');
206
251
  }
207
- /** Word-wrap plain text to `maxWidth` visible columns. */
252
+ /** Word-wrap plain text to `maxWidth` visible columns, hard-breaking long words. */
208
253
  export function wrapText(text, maxWidth) {
209
254
  const out = [];
255
+ const pushWrapped = (word) => {
256
+ // A single word longer than the column budget is split so no line overflows.
257
+ for (let start = 0; start < word.length; start += maxWidth) {
258
+ out.push(word.slice(start, start + maxWidth));
259
+ }
260
+ };
210
261
  for (const rawLine of text.split('\n')) {
211
262
  if (rawLine.length <= maxWidth) {
212
263
  out.push(rawLine);
@@ -214,6 +265,14 @@ export function wrapText(text, maxWidth) {
214
265
  }
215
266
  let current = '';
216
267
  for (const word of rawLine.split(' ')) {
268
+ if (word.length > maxWidth) {
269
+ if (current.length > 0) {
270
+ out.push(current);
271
+ current = '';
272
+ }
273
+ pushWrapped(word);
274
+ continue;
275
+ }
217
276
  if (current.length === 0) {
218
277
  current = word;
219
278
  }
@@ -238,27 +297,35 @@ export function wrapText(text, maxWidth) {
238
297
  export function renderChatScreen(options, height) {
239
298
  const terminalWidth = options.width ?? (process.stdout.columns || 80);
240
299
  const terminalHeight = height ?? process.stdout.rows ?? 24;
241
- const maxWidth = Math.max(40, terminalWidth - 4);
242
- const width = Math.max(40, terminalWidth - 2);
300
+ const area = getChatAnswerArea(terminalWidth);
301
+ const indent = ' '.repeat(area.indent);
243
302
  const lines = [];
244
- lines.push(...renderModeradoHeader().split('\n'));
303
+ lines.push(...renderModeradoHeader(terminalWidth).split('\n'));
245
304
  lines.push('');
246
- // Row 1: the user's question
305
+ // Row 1: the user's question, wrapped onto the same columns as the composer card.
247
306
  const questionMarker = '\x1b[1;38;5;75m' + String.fromCodePoint(0x276F) + '\x1b[0;48;5;236m';
248
- const questionText = `${questionMarker} ${options.chatQuestion ?? ''}`;
249
- lines.push(String.fromCharCode(27) + '[48;5;236m' + questionText + ' '.repeat(Math.max(0, width - visibleLen(questionText))) + String.fromCharCode(27) + '[0m');
307
+ for (const [index, questionLine] of wrapText(options.chatQuestion ?? '', area.textWidth - 2).entries()) {
308
+ const questionText = `${index === 0 ? questionMarker : ' '} ${questionLine}`;
309
+ lines.push(indent + String.fromCharCode(27) + '[48;5;236m' + questionText + ' '.repeat(Math.max(0, area.cardWidth - visibleLen(questionText))) + String.fromCharCode(27) + '[0m');
310
+ }
250
311
  const thoughtTime = options.chatThoughtTime ?? 0;
251
312
  const thoughtTimeLabel = thoughtTime > 0 && thoughtTime < 1 ? '<1s' : `${Math.round(thoughtTime)}s`;
252
- const chatStatus = !options.tokenUsage && options.chatAnswer?.trim() ? `Thought for ${thoughtTimeLabel}` : '';
253
- lines.push(chatStatus ? `\x1b[38;5;244m${chatStatus.slice(0, Math.max(0, terminalWidth - 1))}\x1b[0m` : '');
313
+ const chatStatus = !options.chatAnswer?.trim()
314
+ ? ''
315
+ : options.chatTaskDone
316
+ ? `✔ Done · Thought for ${thoughtTimeLabel}`
317
+ : options.tokenUsage
318
+ ? ''
319
+ : `Thought for ${thoughtTimeLabel}`;
320
+ lines.push(chatStatus ? indent + `\x1b[38;5;244m${chatStatus.slice(0, Math.max(0, area.cardWidth - 1))}\x1b[0m` : '');
254
321
  // Answer section: multi-line model answer
255
322
  if (options.chatAnswer && options.chatAnswer.trim().length > 0) {
256
323
  lines.push('');
257
- for (const answerLine of wrapText(options.chatAnswer.trim(), maxWidth)) {
324
+ for (const answerLine of wrapText(options.chatAnswer.trim(), area.textWidth)) {
258
325
  const heading = answerLine.match(/^\s*(?:#{1,6}\s+([^#]+?)|\*\*(.+?)\*\*)\s*$/);
259
- lines.push(heading
326
+ lines.push(indent + (heading
260
327
  ? `\x1b[1;38;5;25m${(heading[1] ?? heading[2]).trim()}\x1b[0m`
261
- : `\x1b[38;5;253m${answerLine}\x1b[0m`);
328
+ : `\x1b[38;5;253m${answerLine}\x1b[0m`));
262
329
  }
263
330
  }
264
331
  const composer = renderWelcomeCard(options).split('\n');
@@ -268,7 +335,8 @@ export function renderChatScreen(options, height) {
268
335
  }
269
336
  /** Place the terminal cursor on the first row of the bottom composer. */
270
337
  export function renderChatComposerCursor(options, inputLength) {
271
- const column = getWelcomeIndent(getWelcomeCardWidth(options.width), options.width) + 2 + inputLength;
338
+ const width = getWelcomeCardWidth(options.width);
339
+ const column = getWelcomeIndent(width, options.width) + 2 + Math.min(inputLength, width - 3);
272
340
  return `\x1b[1 q\x1b[?25h\x1b[4A\r\x1b[${column}C`;
273
341
  }
274
342
  export function navigateQuestionHistory(history, direction, index, draft) {
@@ -276,34 +344,57 @@ export function navigateQuestionHistory(history, direction, index, draft) {
276
344
  return { input: nextIndex === history.length ? draft : history[nextIndex], index: nextIndex, draft };
277
345
  }
278
346
  /** Update the fixed chat elapsed-time row without repainting the terminal. */
279
- export function renderChatThoughtTimeUpdate(thoughtTime) {
347
+ export function renderChatThoughtTimeUpdate(thoughtTime, taskStep, width) {
348
+ const area = getChatAnswerArea(width);
280
349
  const label = thoughtTime > 0 && thoughtTime < 1 ? '<1s' : `${Math.round(thoughtTime)}s`;
281
- return `\x1b7\x1b[13;1H\r\x1b[K\x1b[38;5;244mThought for ${label}\x1b[0m\x1b8`;
350
+ const status = (taskStep && taskStep.maxSteps > 0 && taskStep.step >= 0
351
+ ? `Processing task… Step ${taskStep.step}/${taskStep.maxSteps} · ${label}`
352
+ : `Thought for ${label}`).slice(0, area.cardWidth);
353
+ return `\x1b7\x1b[13;${area.startColumn}H\x1b[K\x1b[38;5;244m${status}\x1b[0m\x1b8`;
282
354
  }
283
- /** Write one streamed fragment into the answer area without repainting the chat frame. */
284
- export function renderChatAnswerDelta(delta, start, width) {
355
+ /**
356
+ * Write one streamed fragment into the answer area without repainting the chat
357
+ * frame. Wrapped lines return to `startColumn` so the answer keeps the same
358
+ * columns as the composer card instead of stretching across the terminal.
359
+ */
360
+ export function renderChatAnswerDelta(delta, start, width, startColumn = 1) {
285
361
  let row = start.row;
286
362
  let column = start.column;
363
+ let writeColumn = start.column;
364
+ let sequence = '';
365
+ let line = '';
366
+ const flush = () => {
367
+ if (line.length === 0)
368
+ return;
369
+ sequence += `\x1b[${row};${writeColumn}H\x1b[38;5;253m${line}\x1b[0m`;
370
+ line = '';
371
+ };
287
372
  for (const character of delta) {
288
373
  if (character === '\n') {
374
+ flush();
289
375
  row++;
290
- column = 1;
376
+ column = startColumn;
377
+ writeColumn = startColumn;
291
378
  continue;
292
379
  }
380
+ line += character;
293
381
  column++;
294
382
  if (column > width) {
383
+ flush();
295
384
  row++;
296
- column = 1;
385
+ column = startColumn;
386
+ writeColumn = startColumn;
297
387
  }
298
388
  }
299
- return { sequence: `\x1b[${start.row};${start.column}H\x1b[38;5;253m${delta}\x1b[0m`, row, column };
389
+ flush();
390
+ return { sequence, row, column };
300
391
  }
301
392
  export function renderFullWelcomeScreen(options, height) {
302
393
  if (options.chatQuestion !== undefined) {
303
394
  return renderChatScreen(options, height);
304
395
  }
305
396
  return [
306
- renderModeradoHeader(),
397
+ renderModeradoHeader(options.width),
307
398
  '',
308
399
  renderWelcomeCard(options),
309
400
  ].join('\n') + '\n';
@@ -368,6 +459,7 @@ export function renderHelpPopupBox(version, workspace, width) {
368
459
  '\x1b[1mCtrl+C\x1b[0m Cancel active inference or exit session',
369
460
  '\x1b[1mEsc\x1b[0m Stop the active task',
370
461
  '\x1b[1mLeft/Right\x1b[0m Move the caret inside the bottom input line',
462
+ ' \x1b[1mUp/Down\x1b[0m Browse slash commands after "/", else recall history',
371
463
  '\x1b[1mMouse click\x1b[0m Place the caret on the input line',
372
464
  '',
373
465
  `\x1b[38;5;245mWorkspace:\x1b[0m \x1b[38;5;253m${shortWs}\x1b[0m`,
@@ -485,9 +577,12 @@ export async function promptInteractiveTurn(options) {
485
577
  mode: currentMode,
486
578
  autoApprove: currentAutoApprove,
487
579
  input,
580
+ inputCaret: caret,
581
+ commandSelection,
488
582
  chatQuestion: options.chatQuestion,
489
583
  chatAnswer: options.chatAnswer,
490
584
  chatThoughtTime: options.chatThoughtTime,
585
+ chatTaskDone: options.chatTaskDone,
491
586
  outputTokenRate: options.outputTokenRate,
492
587
  tokenUsage: options.tokenUsage,
493
588
  queuedCommands: options.queuedCommands,
@@ -527,7 +622,8 @@ export async function promptInteractiveTurn(options) {
527
622
  */
528
623
  const positionCursorOnInput = () => {
529
624
  const { row, column } = getComposerAnchor();
530
- stdout.write(`\x1b[1 q\x1b[?25h\x1b[${row};${column + caret}H`);
625
+ const { caretColumn } = composerInputWindow(input, caret, getWelcomeCardWidth());
626
+ stdout.write(`\x1b[1 q\x1b[?25h\x1b[${row};${column + caretColumn}H`);
531
627
  };
532
628
  /** Full-screen redraw: clear everything, repaint welcome TUI, reposition cursor. */
533
629
  const redrawFull = () => {
@@ -1014,11 +1110,12 @@ export function formatTokenUsage(event) {
1014
1110
  }
1015
1111
  /** Update only the chat status row, preserving the answer/composer cursor. */
1016
1112
  export function renderChatUsageUpdate(event, width = process.stdout.columns || 80, budgetStatus, usageDetails, terminalHeight = process.stdout.rows || 24, mode) {
1113
+ const area = getChatAnswerArea(width);
1017
1114
  const usageText = formatTokenUsage(event);
1018
1115
  const suffix = `${usageDetails ? ` | ${usageDetails}` : ''}${budgetStatus ? ` | ${budgetStatus}` : ''}`;
1019
1116
  const modeLabel = mode === 'Plan' ? '\x1b[1;38;5;75m[Plan]\x1b[0m / Execute (Tab)' : 'Plan / \x1b[1;38;5;75m[Execute]\x1b[0m (Tab)';
1020
1117
  const modeText = mode === 'Plan' ? '[Plan] / Execute (Tab)' : 'Plan / [Execute] (Tab)';
1021
- const rowWidth = Math.max(0, width - 1);
1118
+ const rowWidth = area.cardWidth;
1022
1119
  const textLimit = Math.max(0, rowWidth - (mode ? modeText.length + 1 : 0));
1023
1120
  const usageLimit = Math.max(0, textLimit - suffix.length);
1024
1121
  const clippedUsage = mode && usageText.length > usageLimit
@@ -1026,14 +1123,15 @@ export function renderChatUsageUpdate(event, width = process.stdout.columns || 8
1026
1123
  : usageText;
1027
1124
  const text = `${clippedUsage}${suffix}`.slice(0, textLimit);
1028
1125
  const row = Math.max(1, terminalHeight - 2);
1029
- return `\x1b7\x1b[${row};1H\r\x1b[K\x1b[38;5;244m${text}\x1b[0m${mode ? `${' '.repeat(Math.max(1, rowWidth - text.length - modeText.length))}${modeLabel}` : ''}\x1b8`;
1126
+ return `\x1b7\x1b[${row};${area.startColumn}H\x1b[K\x1b[38;5;244m${text}\x1b[0m${mode ? `${' '.repeat(Math.max(1, rowWidth - text.length - modeText.length))}${modeLabel}` : ''}\x1b8`;
1030
1127
  }
1031
1128
  /** Show trusted, schema-validated Gateway route changes without provider supplied prose. */
1032
1129
  export function renderGatewayFallbackUpdate(event, width = process.stdout.columns || 80) {
1130
+ const area = getChatAnswerArea(width);
1033
1131
  const destination = event.toProvider ? `${event.toProvider}/${event.toModel}` : event.toModel;
1034
1132
  const reason = event.reason === 'rate_limited_or_unavailable' ? 'after an upstream route was rate limited or unavailable' : 'after a fallback';
1035
- const text = `Gateway route changed to ${destination} ${reason}`.slice(0, Math.max(0, width - 1));
1133
+ const text = `Gateway route changed to ${destination} ${reason}`.slice(0, area.cardWidth);
1036
1134
  const row = renderModeradoHeader().split('\n').length + 3;
1037
- return `\x1b7\x1b[${row};1H\r\x1b[K\x1b[38;5;244m${text}\x1b[0m\x1b8`;
1135
+ return `\x1b7\x1b[${row};${area.startColumn}H\x1b[K\x1b[38;5;244m${text}\x1b[0m\x1b8`;
1038
1136
  }
1039
1137
  //# sourceMappingURL=welcome.js.map