@pellux/goodvibes-agent 1.0.37 → 1.0.39
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/CHANGELOG.md +10 -0
- package/dist/package/main.js +337 -463
- package/package.json +1 -1
- package/src/main.ts +9 -1
- package/src/renderer/compositor.ts +7 -1
- package/src/renderer/conversation-overlays.ts +1 -4
- package/src/renderer/onboarding/onboarding-wizard.ts +237 -499
- package/src/shell/onboarding-fullscreen.ts +40 -0
- package/src/version.ts +1 -1
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import type { Line } from '../../types/grid.ts';
|
|
2
|
-
import {
|
|
2
|
+
import { getDisplayWidth, truncateDisplay, wrapText } from '../../utils/terminal-width.ts';
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from '../
|
|
11
|
-
import { clamp, drawVerticalRule, fillWidth } from '../fullscreen-primitives.ts';
|
|
12
|
-
import { UI_TONES } from '../ui-primitives.ts';
|
|
4
|
+
getFullscreenWorkspaceMetrics,
|
|
5
|
+
renderFullscreenWorkspace,
|
|
6
|
+
stableWindow,
|
|
7
|
+
WORKSPACE_PALETTE as PALETTE,
|
|
8
|
+
type WorkspaceRow,
|
|
9
|
+
} from '../fullscreen-workspace.ts';
|
|
10
|
+
import { GLYPHS } from '../ui-primitives.ts';
|
|
13
11
|
import {
|
|
14
|
-
getOnboardingWizardBodyRows,
|
|
15
|
-
getOnboardingWizardVisibleFieldCount,
|
|
16
12
|
type OnboardingWizardController,
|
|
17
13
|
type OnboardingWizardFieldDefinition,
|
|
18
14
|
type OnboardingWizardStepDefinition,
|
|
@@ -45,106 +41,49 @@ function stepGlyph(
|
|
|
45
41
|
step: OnboardingWizardStepDefinition,
|
|
46
42
|
stepIndex: number,
|
|
47
43
|
): { readonly glyph: string; readonly fg: string } {
|
|
48
|
-
if (stepIndex === wizard.stepIndex) {
|
|
49
|
-
return { glyph: OVERLAY_GLYPHS.selected, fg: UI_TONES.state.active };
|
|
50
|
-
}
|
|
44
|
+
if (stepIndex === wizard.stepIndex) return { glyph: GLYPHS.navigation.selected, fg: PALETTE.info };
|
|
51
45
|
|
|
52
46
|
const total = wizard.getStepFieldCount(stepIndex);
|
|
53
47
|
const completed = wizard.getCompletedFieldCount(stepIndex);
|
|
54
|
-
if (wizard.isStepDirty(stepIndex)) {
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
if (total > 0 && completed === total) {
|
|
58
|
-
return { glyph: '✓', fg: UI_TONES.state.good };
|
|
59
|
-
}
|
|
60
|
-
return { glyph: '•', fg: UI_TONES.fg.muted };
|
|
48
|
+
if (wizard.isStepDirty(stepIndex)) return { glyph: GLYPHS.status.review, fg: PALETTE.warn };
|
|
49
|
+
if (total > 0 && completed === total) return { glyph: GLYPHS.status.success, fg: PALETTE.good };
|
|
50
|
+
return { glyph: GLYPHS.status.pending, fg: PALETTE.muted };
|
|
61
51
|
}
|
|
62
52
|
|
|
63
|
-
function
|
|
53
|
+
function fieldRowPrefix(
|
|
64
54
|
wizard: OnboardingWizardController,
|
|
65
55
|
field: OnboardingWizardFieldDefinition,
|
|
56
|
+
selected: boolean,
|
|
66
57
|
): string {
|
|
67
|
-
if (
|
|
68
|
-
if (field.
|
|
69
|
-
if (field.kind === '
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return wizard.getFieldValue(field) ? UI_TONES.state.good : UI_TONES.fg.muted;
|
|
75
|
-
}
|
|
76
|
-
if (field.kind === 'radio') return UI_TONES.state.active;
|
|
77
|
-
if (field.kind === 'text' || field.kind === 'masked') {
|
|
78
|
-
const missingRequired = wizard.getFieldValueLabel(field) === 'Missing';
|
|
79
|
-
if (missingRequired) return UI_TONES.state.warn;
|
|
80
|
-
if (field.kind === 'masked') return UI_TONES.state.warn;
|
|
81
|
-
}
|
|
82
|
-
return UI_TONES.fg.secondary;
|
|
58
|
+
if (selected) return `${GLYPHS.navigation.selected} `;
|
|
59
|
+
if (wizard.isFieldDirty(field.id)) return `${GLYPHS.status.skipped} `;
|
|
60
|
+
if (field.kind === 'checklist') return (wizard.getFieldValue(field) as boolean) ? `${GLYPHS.status.success} ` : '□ ';
|
|
61
|
+
if (field.kind === 'acknowledgement') return (wizard.getFieldValue(field) as boolean) ? `${GLYPHS.status.success} ` : '□ ';
|
|
62
|
+
if (field.kind === 'action') return `${GLYPHS.navigation.next} `;
|
|
63
|
+
if (field.kind === 'radio') return `${GLYPHS.status.active} `;
|
|
64
|
+
return ' ';
|
|
83
65
|
}
|
|
84
66
|
|
|
85
|
-
function
|
|
67
|
+
function fieldColor(
|
|
86
68
|
wizard: OnboardingWizardController,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
):
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
for (let index = 0; index < spacerRows; index += 1) {
|
|
99
|
-
allRows.push({ kind: 'empty' });
|
|
100
|
-
}
|
|
101
|
-
allRows.push({ kind: 'field', field, absoluteIndex });
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const selectedFieldIndex = wizard.getSelectedFieldIndex();
|
|
105
|
-
const selectedRowIndex = Math.max(0, allRows.findIndex((row) => row.kind === 'field' && row.absoluteIndex === selectedFieldIndex));
|
|
106
|
-
const scrollFieldIndex = wizard.scrollOffsets[wizard.stepIndex] ?? 0;
|
|
107
|
-
const scrollRowIndex = allRows.findIndex((row) => row.kind === 'field' && row.absoluteIndex === scrollFieldIndex);
|
|
108
|
-
const maxStart = Math.max(0, allRows.length - capacity);
|
|
109
|
-
let start = clamp(scrollRowIndex >= 0 ? scrollRowIndex : 0, 0, maxStart);
|
|
110
|
-
|
|
111
|
-
if (selectedRowIndex < start) start = selectedRowIndex;
|
|
112
|
-
if (selectedRowIndex >= start + capacity) start = selectedRowIndex - capacity + 1;
|
|
113
|
-
start = clamp(start, 0, maxStart);
|
|
114
|
-
|
|
115
|
-
if (capacity > 1 && start > 0 && selectedRowIndex === start) start = Math.max(0, start - 1);
|
|
116
|
-
if (capacity > 1 && start + capacity < allRows.length && selectedRowIndex === start + capacity - 1) {
|
|
117
|
-
start = Math.min(maxStart, start + 1);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
rows.push(...allRows.slice(start, start + capacity));
|
|
121
|
-
const firstVisibleRow = rows[0];
|
|
122
|
-
if (
|
|
123
|
-
start > 0
|
|
124
|
-
&& rows.length > 0
|
|
125
|
-
&& !(firstVisibleRow?.kind === 'field' && firstVisibleRow.absoluteIndex === selectedFieldIndex)
|
|
126
|
-
) {
|
|
127
|
-
rows[0] = {
|
|
128
|
-
kind: 'moreAbove',
|
|
129
|
-
text: `${OVERLAY_GLYPHS.moreAbove} ${start} more above`,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const hiddenBelow = Math.max(0, allRows.length - (start + capacity));
|
|
134
|
-
const lastVisibleRow = rows[rows.length - 1];
|
|
135
|
-
if (
|
|
136
|
-
hiddenBelow > 0
|
|
137
|
-
&& rows.length > 0
|
|
138
|
-
&& !(lastVisibleRow?.kind === 'field' && lastVisibleRow.absoluteIndex === selectedFieldIndex)
|
|
139
|
-
) {
|
|
140
|
-
rows[rows.length - 1] = {
|
|
141
|
-
kind: 'moreBelow',
|
|
142
|
-
text: `${OVERLAY_GLYPHS.moreBelow} ${hiddenBelow} more below`,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
69
|
+
field: OnboardingWizardFieldDefinition,
|
|
70
|
+
selected: boolean,
|
|
71
|
+
): string {
|
|
72
|
+
if (selected) return PALETTE.text;
|
|
73
|
+
if (field.kind === 'status' || field.kind === 'modelPicker') return PALETTE.info;
|
|
74
|
+
if (field.kind === 'masked') return PALETTE.warn;
|
|
75
|
+
if (field.kind === 'acknowledgement') return (wizard.getFieldValue(field) as boolean) ? PALETTE.good : PALETTE.warn;
|
|
76
|
+
if (field.kind === 'checklist') return (wizard.getFieldValue(field) as boolean) ? PALETTE.good : PALETTE.muted;
|
|
77
|
+
if (wizard.getFieldValueLabel(field) === 'Missing') return PALETTE.warn;
|
|
78
|
+
return PALETTE.text;
|
|
79
|
+
}
|
|
145
80
|
|
|
146
|
-
|
|
147
|
-
|
|
81
|
+
function formatEditingValue(value: string, multiline: boolean): string {
|
|
82
|
+
if (!multiline) return value;
|
|
83
|
+
const lines = value.split(/\r?\n/);
|
|
84
|
+
if (lines.length <= 1) return value;
|
|
85
|
+
const preview = lines[lines.length - 1] ?? '';
|
|
86
|
+
return `${preview} (${lines.length} lines)`;
|
|
148
87
|
}
|
|
149
88
|
|
|
150
89
|
function fieldHint(
|
|
@@ -162,7 +101,7 @@ function fieldHint(
|
|
|
162
101
|
const editingValue = field.kind === 'masked' && wizard.editBuffer.length > 0
|
|
163
102
|
? '•'.repeat(Math.min(12, Math.max(4, wizard.editBuffer.length)))
|
|
164
103
|
: formatEditingValue(rawValue, field.kind === 'text' && field.multiline === true);
|
|
165
|
-
return `Editing: ${editingValue}
|
|
104
|
+
return `Editing: ${editingValue}${GLYPHS.surface.cursor}`;
|
|
166
105
|
}
|
|
167
106
|
|
|
168
107
|
if (selected && field.kind === 'modelPicker') return `${field.hint} Press Enter to open picker.`;
|
|
@@ -175,28 +114,6 @@ function fieldHint(
|
|
|
175
114
|
return field.hint;
|
|
176
115
|
}
|
|
177
116
|
|
|
178
|
-
function formatEditingValue(value: string, multiline: boolean): string {
|
|
179
|
-
if (!multiline) return value;
|
|
180
|
-
const lines = value.split(/\r?\n/);
|
|
181
|
-
if (lines.length <= 1) return value;
|
|
182
|
-
const preview = lines[lines.length - 1] ?? '';
|
|
183
|
-
return `${preview} (${lines.length} lines)`;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function fieldRowPrefix(
|
|
187
|
-
wizard: OnboardingWizardController,
|
|
188
|
-
field: OnboardingWizardFieldDefinition,
|
|
189
|
-
selected: boolean,
|
|
190
|
-
): string {
|
|
191
|
-
if (selected) return `${OVERLAY_GLYPHS.selected} `;
|
|
192
|
-
if (wizard.isFieldDirty(field.id)) return '◇ ';
|
|
193
|
-
if (field.kind === 'checklist') return (wizard.getFieldValue(field) as boolean) ? '✓ ' : '□ ';
|
|
194
|
-
if (field.kind === 'acknowledgement') return (wizard.getFieldValue(field) as boolean) ? '✓ ' : '□ ';
|
|
195
|
-
if (field.kind === 'action') return '▶ ';
|
|
196
|
-
if (field.kind === 'radio') return '◉ ';
|
|
197
|
-
return ' ';
|
|
198
|
-
}
|
|
199
|
-
|
|
200
117
|
function selectedFieldText(wizard: OnboardingWizardController): {
|
|
201
118
|
readonly title: string;
|
|
202
119
|
readonly hint: string;
|
|
@@ -212,12 +129,7 @@ function selectedFieldText(wizard: OnboardingWizardController): {
|
|
|
212
129
|
}
|
|
213
130
|
|
|
214
131
|
const field = wizard.getSelectedField();
|
|
215
|
-
if (!field) {
|
|
216
|
-
return {
|
|
217
|
-
title: 'Selected: none',
|
|
218
|
-
hint: 'No selectable row is active on this screen.',
|
|
219
|
-
};
|
|
220
|
-
}
|
|
132
|
+
if (!field) return { title: 'Selected: none', hint: 'No selectable row is active on this screen.' };
|
|
221
133
|
|
|
222
134
|
return {
|
|
223
135
|
title: `Selected: ${field.label} [${wizard.getFieldValueLabel(field)}]`,
|
|
@@ -225,54 +137,14 @@ function selectedFieldText(wizard: OnboardingWizardController): {
|
|
|
225
137
|
};
|
|
226
138
|
}
|
|
227
139
|
|
|
228
|
-
function renderFieldRow(
|
|
229
|
-
line: Line,
|
|
230
|
-
wizard: OnboardingWizardController,
|
|
231
|
-
fieldRow: RenderedFieldRow,
|
|
232
|
-
startX: number,
|
|
233
|
-
width: number,
|
|
234
|
-
): void {
|
|
235
|
-
if (fieldRow.kind === 'empty') return;
|
|
236
|
-
|
|
237
|
-
if (fieldRow.kind === 'moreAbove' || fieldRow.kind === 'moreBelow') {
|
|
238
|
-
putOverlayText(line, startX + 1, width - 2, truncateDisplay(fieldRow.text, width - 2), {
|
|
239
|
-
fg: UI_TONES.fg.muted,
|
|
240
|
-
bg: UI_TONES.bg.base,
|
|
241
|
-
dim: true,
|
|
242
|
-
});
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const selected = fieldRow.absoluteIndex === wizard.getSelectedFieldIndex();
|
|
247
|
-
const field = fieldRow.field;
|
|
248
|
-
const fieldBg = selected ? DEFAULT_OVERLAY_PALETTE.selectedBg : UI_TONES.bg.base;
|
|
249
|
-
fillWidth(line, startX, width, fieldBg);
|
|
250
|
-
|
|
251
|
-
const badge = truncateDisplay(`[${wizard.getFieldValueLabel(field)}]`, Math.max(8, Math.floor(width * 0.34)));
|
|
252
|
-
const badgeWidth = getDisplayWidth(badge);
|
|
253
|
-
const labelWidth = Math.max(0, width - badgeWidth - 4);
|
|
254
|
-
const prefix = fieldRowPrefix(wizard, field, selected);
|
|
255
|
-
|
|
256
|
-
putOverlayText(line, startX + 1, labelWidth, truncateDisplay(`${prefix}${field.label}`, labelWidth), {
|
|
257
|
-
fg: UI_TONES.fg.primary,
|
|
258
|
-
bg: fieldBg,
|
|
259
|
-
bold: selected,
|
|
260
|
-
});
|
|
261
|
-
putOverlayText(line, startX + width - badgeWidth - 1, badgeWidth, badge, {
|
|
262
|
-
fg: fieldBadgeTone(wizard, field),
|
|
263
|
-
bg: fieldBg,
|
|
264
|
-
bold: selected,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
|
|
268
140
|
function footerText(wizard: OnboardingWizardController): string {
|
|
269
141
|
if (wizard.isEditingTextField()) {
|
|
270
142
|
return wizard.isEditingMultilineTextField()
|
|
271
|
-
? '[Enter]
|
|
272
|
-
: '[Enter]
|
|
143
|
+
? 'Controls: [Enter] save · [Ctrl-J] new line · [Esc] cancel · [Backspace] delete · [Del/Ctrl+U] clear'
|
|
144
|
+
: 'Controls: [Enter] save · [Esc] cancel · [Backspace] delete · [Del/Ctrl+U] clear';
|
|
273
145
|
}
|
|
274
146
|
|
|
275
|
-
return '[Enter]
|
|
147
|
+
return 'Controls: [Enter] toggle/open · [Esc] close · [Tab/Shift+Tab] screen · [Up/Down] move · [Del/Ctrl+U] clear';
|
|
276
148
|
}
|
|
277
149
|
|
|
278
150
|
function controlsText(wizard: OnboardingWizardController): string {
|
|
@@ -281,352 +153,199 @@ function controlsText(wizard: OnboardingWizardController): string {
|
|
|
281
153
|
? 'Controls: Enter saves, Ctrl-J inserts a line, Esc cancels, Backspace deletes, Del clears.'
|
|
282
154
|
: 'Controls: Enter saves, Esc cancels, Backspace deletes, Del clears.';
|
|
283
155
|
}
|
|
156
|
+
|
|
284
157
|
return 'Controls: Enter selects, Del clears, Tab moves.';
|
|
285
158
|
}
|
|
286
159
|
|
|
287
|
-
function
|
|
160
|
+
function buildFieldRows(
|
|
288
161
|
wizard: OnboardingWizardController,
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
)
|
|
293
|
-
const
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
const currentStep = wizard.currentStep;
|
|
297
|
-
const borderFg = DEFAULT_OVERLAY_PALETTE.borderFg;
|
|
298
|
-
const headerBg = UI_TONES.bg.title;
|
|
299
|
-
const railBg = UI_TONES.bg.section;
|
|
300
|
-
const bodyBg = UI_TONES.bg.base;
|
|
301
|
-
const summaryBg = UI_TONES.bg.summary;
|
|
302
|
-
const innerLeft = layout.margin + 1;
|
|
303
|
-
const availableInner = layout.innerWidth - 2;
|
|
304
|
-
const leftWidthBase = layout.innerWidth >= 150 ? 32 : layout.innerWidth >= 108 ? 28 : 24;
|
|
305
|
-
const rightWidthBase = layout.innerWidth >= 150 ? 34 : layout.innerWidth >= 108 ? 32 : 24;
|
|
306
|
-
const minCenterWidth = layout.innerWidth >= 120 ? 48 : 40;
|
|
307
|
-
let leftWidth = Math.min(leftWidthBase, Math.max(20, availableInner - minCenterWidth - 12));
|
|
308
|
-
let rightWidth = Math.min(rightWidthBase, Math.max(22, availableInner - minCenterWidth - leftWidth));
|
|
309
|
-
let centerWidth = layout.innerWidth - leftWidth - rightWidth - 2;
|
|
310
|
-
|
|
311
|
-
if (centerWidth < minCenterWidth) {
|
|
312
|
-
const deficit = minCenterWidth - centerWidth;
|
|
313
|
-
const leftCut = Math.min(Math.max(0, leftWidth - 20), Math.ceil(deficit / 2));
|
|
314
|
-
leftWidth -= leftCut;
|
|
315
|
-
rightWidth -= Math.min(Math.max(0, rightWidth - 22), deficit - leftCut);
|
|
316
|
-
centerWidth = layout.innerWidth - leftWidth - rightWidth - 2;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const leftStart = innerLeft;
|
|
320
|
-
const leftSeparatorX = leftStart + leftWidth;
|
|
321
|
-
const centerStart = leftSeparatorX + 1;
|
|
322
|
-
const rightSeparatorX = centerStart + centerWidth;
|
|
323
|
-
const rightStart = rightSeparatorX + 1;
|
|
324
|
-
const descriptionLines = wrapText(currentStep.description, Math.max(18, centerWidth - 2)).slice(0, 2);
|
|
325
|
-
const summaryLines = [
|
|
326
|
-
currentStep.summaryTitle,
|
|
327
|
-
...currentStep.summaryLines.slice(0, 2),
|
|
328
|
-
`Fields ${wizard.getCompletedFieldCount(wizard.stepIndex)}/${wizard.getStepFieldCount(wizard.stepIndex)} complete`,
|
|
329
|
-
changedScreensLabel(wizard),
|
|
330
|
-
];
|
|
331
|
-
const selectedText = selectedFieldText(wizard);
|
|
332
|
-
const selectedHintLines = wrapText(selectedText.hint, Math.max(18, centerWidth - 2)).slice(0, 3);
|
|
333
|
-
const fieldStartRow = 8;
|
|
334
|
-
const fieldRows = buildFieldRows(wizard, visibleFields, Math.max(0, bodyRows - fieldStartRow));
|
|
162
|
+
visibleFields: number,
|
|
163
|
+
capacity: number,
|
|
164
|
+
): readonly RenderedFieldRow[] {
|
|
165
|
+
wizard.ensureSelectionVisible(visibleFields);
|
|
166
|
+
const fields = wizard.currentStep.fields;
|
|
167
|
+
const rows: RenderedFieldRow[] = [];
|
|
168
|
+
if (fields.length === 0 || capacity <= 0) return rows;
|
|
335
169
|
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
OVERLAY_GLYPHS.topRight,
|
|
342
|
-
borderFg,
|
|
343
|
-
headerBg,
|
|
344
|
-
);
|
|
345
|
-
putOverlayText(topLine, layout.margin + 2, layout.width - 4, 'Onboarding Wizard', {
|
|
346
|
-
fg: UI_TONES.fg.primary,
|
|
347
|
-
bg: headerBg,
|
|
348
|
-
bold: true,
|
|
349
|
-
});
|
|
350
|
-
const meta = `${modeLabel(wizard.mode)} ${wizard.stepIndex + 1}/${wizard.steps.length} ${changedScreensLabel(wizard)}`;
|
|
351
|
-
putOverlayText(topLine, Math.max(layout.margin + 2, layout.margin + layout.width - getDisplayWidth(meta) - 3), layout.width - 4, meta, {
|
|
352
|
-
fg: UI_TONES.fg.secondary,
|
|
353
|
-
bg: headerBg,
|
|
354
|
-
});
|
|
355
|
-
lines.push(topLine);
|
|
356
|
-
|
|
357
|
-
const headerLine = createOverlayContentLine(width, layout, borderFg, headerBg);
|
|
358
|
-
fillWidth(headerLine, leftStart, leftWidth, railBg);
|
|
359
|
-
fillWidth(headerLine, centerStart, centerWidth, headerBg);
|
|
360
|
-
fillWidth(headerLine, rightStart, rightWidth, summaryBg);
|
|
361
|
-
drawVerticalRule(headerLine, leftSeparatorX, borderFg, headerBg);
|
|
362
|
-
drawVerticalRule(headerLine, rightSeparatorX, borderFg, headerBg);
|
|
363
|
-
putOverlayText(headerLine, leftStart + 1, leftWidth - 2, 'Steps', {
|
|
364
|
-
fg: UI_TONES.fg.secondary,
|
|
365
|
-
bg: railBg,
|
|
366
|
-
bold: true,
|
|
367
|
-
});
|
|
368
|
-
putOverlayText(headerLine, centerStart + 1, centerWidth - 2, truncateDisplay(currentStep.title, centerWidth - 2), {
|
|
369
|
-
fg: UI_TONES.state.active,
|
|
370
|
-
bg: headerBg,
|
|
371
|
-
bold: true,
|
|
372
|
-
});
|
|
373
|
-
putOverlayText(headerLine, rightStart + 1, rightWidth - 2, 'Summary', {
|
|
374
|
-
fg: UI_TONES.fg.secondary,
|
|
375
|
-
bg: summaryBg,
|
|
376
|
-
bold: true,
|
|
170
|
+
const allRows: RenderedFieldRow[] = [];
|
|
171
|
+
fields.forEach((field, absoluteIndex) => {
|
|
172
|
+
const spacerRows = Math.max(0, field.spacerBeforeRows ?? 0);
|
|
173
|
+
for (let index = 0; index < spacerRows; index += 1) allRows.push({ kind: 'empty' });
|
|
174
|
+
allRows.push({ kind: 'field', field, absoluteIndex });
|
|
377
175
|
});
|
|
378
|
-
lines.push(headerLine);
|
|
379
176
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
borderFg,
|
|
387
|
-
bodyBg,
|
|
388
|
-
));
|
|
389
|
-
|
|
390
|
-
for (let row = 0; row < bodyRows; row += 1) {
|
|
391
|
-
const line = createOverlayContentLine(width, layout, borderFg, bodyBg);
|
|
392
|
-
fillWidth(line, leftStart, leftWidth, railBg);
|
|
393
|
-
fillWidth(line, centerStart, centerWidth, bodyBg);
|
|
394
|
-
fillWidth(line, rightStart, rightWidth, summaryBg);
|
|
395
|
-
drawVerticalRule(line, leftSeparatorX, borderFg);
|
|
396
|
-
drawVerticalRule(line, rightSeparatorX, borderFg);
|
|
397
|
-
|
|
398
|
-
if (row === 0) {
|
|
399
|
-
putOverlayText(line, centerStart + 1, centerWidth - 2, truncateDisplay(currentStep.title, centerWidth - 2), {
|
|
400
|
-
fg: UI_TONES.fg.primary,
|
|
401
|
-
bg: bodyBg,
|
|
402
|
-
bold: true,
|
|
403
|
-
});
|
|
404
|
-
} else if (row === 1) {
|
|
405
|
-
putOverlayText(line, centerStart + 1, centerWidth - 2, descriptionLines[0] ?? '', {
|
|
406
|
-
fg: UI_TONES.fg.secondary,
|
|
407
|
-
bg: bodyBg,
|
|
408
|
-
});
|
|
409
|
-
} else if (row === 2) {
|
|
410
|
-
putOverlayText(line, centerStart + 1, centerWidth - 2, descriptionLines[1] ?? '', {
|
|
411
|
-
fg: UI_TONES.fg.secondary,
|
|
412
|
-
bg: bodyBg,
|
|
413
|
-
});
|
|
414
|
-
} else if (row === 3) {
|
|
415
|
-
fillWidth(line, centerStart, centerWidth, railBg);
|
|
416
|
-
putOverlayText(line, centerStart + 1, centerWidth - 2, truncateDisplay(controlsText(wizard), centerWidth - 2), {
|
|
417
|
-
fg: UI_TONES.state.info,
|
|
418
|
-
bg: railBg,
|
|
419
|
-
});
|
|
420
|
-
} else if (row === 4) {
|
|
421
|
-
fillWidth(line, centerStart, centerWidth, DEFAULT_OVERLAY_PALETTE.selectedBg);
|
|
422
|
-
putOverlayText(line, centerStart + 1, centerWidth - 2, truncateDisplay(`Focus: ${selectedText.title.replace(/^Selected: /, '')}`, centerWidth - 2), {
|
|
423
|
-
fg: UI_TONES.fg.primary,
|
|
424
|
-
bg: DEFAULT_OVERLAY_PALETTE.selectedBg,
|
|
425
|
-
bold: true,
|
|
426
|
-
});
|
|
427
|
-
} else if (row >= 5 && row <= 7) {
|
|
428
|
-
fillWidth(line, centerStart, centerWidth, DEFAULT_OVERLAY_PALETTE.selectedBg);
|
|
429
|
-
putOverlayText(line, centerStart + 1, centerWidth - 2, selectedHintLines[row - 5] ?? '', {
|
|
430
|
-
fg: UI_TONES.fg.secondary,
|
|
431
|
-
bg: DEFAULT_OVERLAY_PALETTE.selectedBg,
|
|
432
|
-
});
|
|
433
|
-
} else {
|
|
434
|
-
renderFieldRow(line, wizard, fieldRows[row - fieldStartRow] ?? { kind: 'empty' }, centerStart, centerWidth);
|
|
435
|
-
}
|
|
177
|
+
const selectedFieldIndex = wizard.getSelectedFieldIndex();
|
|
178
|
+
const selectedRowIndex = Math.max(0, allRows.findIndex((row) => row.kind === 'field' && row.absoluteIndex === selectedFieldIndex));
|
|
179
|
+
const scrollFieldIndex = wizard.scrollOffsets[wizard.stepIndex] ?? 0;
|
|
180
|
+
const scrollRowIndex = allRows.findIndex((row) => row.kind === 'field' && row.absoluteIndex === scrollFieldIndex);
|
|
181
|
+
const maxStart = Math.max(0, allRows.length - capacity);
|
|
182
|
+
let start = Math.max(0, Math.min(scrollRowIndex >= 0 ? scrollRowIndex : 0, maxStart));
|
|
436
183
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
const completion = `${wizard.getCompletedFieldCount(row)}/${wizard.getStepFieldCount(row)}`;
|
|
441
|
-
const completionWidth = getDisplayWidth(completion);
|
|
442
|
-
const stepLabelWidth = Math.max(0, leftWidth - completionWidth - 4);
|
|
443
|
-
putOverlayText(line, leftStart + 1, stepLabelWidth, truncateDisplay(`${stepState.glyph} ${row + 1}. ${step.shortLabel}`, stepLabelWidth), {
|
|
444
|
-
fg: stepState.fg,
|
|
445
|
-
bg: railBg,
|
|
446
|
-
bold: row === wizard.stepIndex,
|
|
447
|
-
});
|
|
448
|
-
putOverlayText(line, Math.max(leftStart + 1, leftStart + leftWidth - completionWidth - 2), completionWidth, completion, {
|
|
449
|
-
fg: wizard.isStepDirty(row) ? UI_TONES.state.warn : UI_TONES.fg.muted,
|
|
450
|
-
bg: railBg,
|
|
451
|
-
});
|
|
452
|
-
}
|
|
184
|
+
if (selectedRowIndex < start) start = selectedRowIndex;
|
|
185
|
+
if (selectedRowIndex >= start + capacity) start = selectedRowIndex - capacity + 1;
|
|
186
|
+
start = Math.max(0, Math.min(start, maxStart));
|
|
453
187
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
bg: summaryBg,
|
|
459
|
-
bold: row === 0,
|
|
460
|
-
dim: row > 0,
|
|
461
|
-
});
|
|
462
|
-
}
|
|
188
|
+
if (capacity > 1 && start > 0 && selectedRowIndex === start) start = Math.max(0, start - 1);
|
|
189
|
+
if (capacity > 1 && start + capacity < allRows.length && selectedRowIndex === start + capacity - 1) {
|
|
190
|
+
start = Math.min(maxStart, start + 1);
|
|
191
|
+
}
|
|
463
192
|
|
|
464
|
-
|
|
193
|
+
rows.push(...allRows.slice(start, start + capacity));
|
|
194
|
+
const firstVisibleRow = rows[0];
|
|
195
|
+
if (
|
|
196
|
+
start > 0
|
|
197
|
+
&& rows.length > 0
|
|
198
|
+
&& !(firstVisibleRow?.kind === 'field' && firstVisibleRow.absoluteIndex === selectedFieldIndex)
|
|
199
|
+
) {
|
|
200
|
+
rows[0] = { kind: 'moreAbove', text: `${GLYPHS.navigation.moreAbove} ${start} more above` };
|
|
465
201
|
}
|
|
466
202
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const footer = createOverlayFilledBorderLine(
|
|
478
|
-
width,
|
|
479
|
-
layout,
|
|
480
|
-
OVERLAY_GLYPHS.bottomLeft,
|
|
481
|
-
OVERLAY_GLYPHS.horizontal,
|
|
482
|
-
OVERLAY_GLYPHS.bottomRight,
|
|
483
|
-
borderFg,
|
|
484
|
-
headerBg,
|
|
485
|
-
);
|
|
486
|
-
putOverlayText(footer, layout.margin + 2, layout.width - 4, truncateDisplay(footerText(wizard), layout.width - 4), {
|
|
487
|
-
fg: UI_TONES.fg.muted,
|
|
488
|
-
bg: headerBg,
|
|
489
|
-
dim: true,
|
|
490
|
-
});
|
|
491
|
-
lines.push(footer);
|
|
203
|
+
const hiddenBelow = Math.max(0, allRows.length - (start + capacity));
|
|
204
|
+
const lastVisibleRow = rows[rows.length - 1];
|
|
205
|
+
if (
|
|
206
|
+
hiddenBelow > 0
|
|
207
|
+
&& rows.length > 0
|
|
208
|
+
&& !(lastVisibleRow?.kind === 'field' && lastVisibleRow.absoluteIndex === selectedFieldIndex)
|
|
209
|
+
) {
|
|
210
|
+
rows[rows.length - 1] = { kind: 'moreBelow', text: `${GLYPHS.navigation.moreBelow} ${hiddenBelow} more below` };
|
|
211
|
+
}
|
|
492
212
|
|
|
493
|
-
|
|
213
|
+
while (rows.length < capacity) rows.push({ kind: 'empty' });
|
|
214
|
+
return rows.slice(0, capacity);
|
|
494
215
|
}
|
|
495
216
|
|
|
496
|
-
function
|
|
497
|
-
|
|
217
|
+
function pushWrapped(
|
|
218
|
+
rows: WorkspaceRow[],
|
|
219
|
+
text: string,
|
|
498
220
|
width: number,
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
OVERLAY_GLYPHS.topRight,
|
|
523
|
-
borderFg,
|
|
524
|
-
headerBg,
|
|
525
|
-
);
|
|
526
|
-
putOverlayText(topLine, layout.margin + 2, layout.width - 4, 'Onboarding Wizard', {
|
|
527
|
-
fg: UI_TONES.fg.primary,
|
|
528
|
-
bg: headerBg,
|
|
529
|
-
bold: true,
|
|
530
|
-
});
|
|
531
|
-
const meta = `${wizard.stepIndex + 1}/${wizard.steps.length} • ${changedScreensLabel(wizard)}`;
|
|
532
|
-
putOverlayText(topLine, Math.max(layout.margin + 2, layout.margin + layout.width - getDisplayWidth(meta) - 3), layout.width - 4, meta, {
|
|
533
|
-
fg: UI_TONES.fg.secondary,
|
|
534
|
-
bg: headerBg,
|
|
535
|
-
});
|
|
536
|
-
lines.push(topLine);
|
|
537
|
-
|
|
538
|
-
const headerLine = createOverlayContentLine(width, layout, borderFg, headerBg);
|
|
539
|
-
fillWidth(headerLine, innerStart, innerWidth, headerBg);
|
|
540
|
-
putOverlayText(headerLine, innerStart + 1, innerWidth - 2, fitDisplay(`${modeLabel(wizard.mode)} • ${currentStep.shortLabel}`, innerWidth - 2), {
|
|
541
|
-
fg: UI_TONES.state.active,
|
|
542
|
-
bg: headerBg,
|
|
543
|
-
bold: true,
|
|
221
|
+
options: Partial<WorkspaceRow> = {},
|
|
222
|
+
): void {
|
|
223
|
+
if (text.length === 0) {
|
|
224
|
+
rows.push({ text: '', kind: 'empty' });
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
for (const line of wrapText(text, Math.max(1, width))) {
|
|
228
|
+
rows.push({ text: line, ...options });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function buildStepRows(wizard: OnboardingWizardController, height: number): WorkspaceRow[] {
|
|
233
|
+
const rendered = wizard.steps.map((step, stepIndex): WorkspaceRow => {
|
|
234
|
+
const selected = stepIndex === wizard.stepIndex;
|
|
235
|
+
const state = stepGlyph(wizard, step, stepIndex);
|
|
236
|
+
const completion = `${wizard.getCompletedFieldCount(stepIndex)}/${wizard.getStepFieldCount(stepIndex)}`;
|
|
237
|
+
return {
|
|
238
|
+
text: `${state.glyph} ${stepIndex + 1}. ${step.shortLabel} ${completion}`,
|
|
239
|
+
selected,
|
|
240
|
+
kind: 'item',
|
|
241
|
+
fg: state.fg,
|
|
242
|
+
bold: selected,
|
|
243
|
+
};
|
|
544
244
|
});
|
|
545
|
-
lines.push(headerLine);
|
|
546
245
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
});
|
|
572
|
-
} else if (row === 2) {
|
|
573
|
-
putOverlayText(line, innerStart + 1, innerWidth - 2, descriptionLines[1] ?? '', {
|
|
574
|
-
fg: UI_TONES.fg.secondary,
|
|
575
|
-
bg: bodyBg,
|
|
576
|
-
});
|
|
577
|
-
} else if (row === 3) {
|
|
578
|
-
fillWidth(line, innerStart, innerWidth, UI_TONES.bg.section);
|
|
579
|
-
putOverlayText(line, innerStart + 1, innerWidth - 2, truncateDisplay(controlsText(wizard), innerWidth - 2), {
|
|
580
|
-
fg: UI_TONES.state.info,
|
|
581
|
-
bg: UI_TONES.bg.section,
|
|
582
|
-
});
|
|
583
|
-
} else if (row === 4) {
|
|
584
|
-
fillWidth(line, innerStart, innerWidth, DEFAULT_OVERLAY_PALETTE.selectedBg);
|
|
585
|
-
putOverlayText(line, innerStart + 1, innerWidth - 2, truncateDisplay(`Focus: ${selectedText.title.replace(/^Selected: /, '')}`, innerWidth - 2), {
|
|
586
|
-
fg: UI_TONES.fg.primary,
|
|
587
|
-
bg: DEFAULT_OVERLAY_PALETTE.selectedBg,
|
|
588
|
-
bold: true,
|
|
589
|
-
});
|
|
590
|
-
} else if (row >= 5 && row <= 7) {
|
|
591
|
-
fillWidth(line, innerStart, innerWidth, DEFAULT_OVERLAY_PALETTE.selectedBg);
|
|
592
|
-
putOverlayText(line, innerStart + 1, innerWidth - 2, selectedHintLines[row - 5] ?? '', {
|
|
593
|
-
fg: UI_TONES.fg.secondary,
|
|
594
|
-
bg: DEFAULT_OVERLAY_PALETTE.selectedBg,
|
|
595
|
-
});
|
|
596
|
-
} else {
|
|
597
|
-
renderFieldRow(line, wizard, fieldRows[row - fieldStartRow] ?? { kind: 'empty' }, innerStart, innerWidth);
|
|
246
|
+
const visible = Math.max(1, height);
|
|
247
|
+
const window = stableWindow(rendered.length, wizard.stepIndex, visible);
|
|
248
|
+
const rows = rendered.slice(window.start, window.end);
|
|
249
|
+
if (window.start > 0 && rows.length > 0) {
|
|
250
|
+
rows[0] = { text: `${GLYPHS.navigation.moreAbove} ${window.start} more step(s) above`, kind: 'more', fg: PALETTE.dim, dim: true };
|
|
251
|
+
}
|
|
252
|
+
if (window.end < rendered.length && rows.length > 0) {
|
|
253
|
+
rows[rows.length - 1] = { text: `${GLYPHS.navigation.moreBelow} ${rendered.length - window.end} more step(s) below`, kind: 'more', fg: PALETTE.dim, dim: true };
|
|
254
|
+
}
|
|
255
|
+
while (rows.length < height) rows.push({ text: '', kind: 'empty' });
|
|
256
|
+
return rows.slice(0, height);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function buildContextRows(wizard: OnboardingWizardController, width: number): WorkspaceRow[] {
|
|
260
|
+
const step = wizard.currentStep;
|
|
261
|
+
const selected = selectedFieldText(wizard);
|
|
262
|
+
const rows: WorkspaceRow[] = [
|
|
263
|
+
{ text: step.title, fg: PALETTE.title, bold: true },
|
|
264
|
+
];
|
|
265
|
+
const pushSummaryRows = () => {
|
|
266
|
+
rows.push({ text: 'Summary', fg: PALETTE.subtitle, bold: true });
|
|
267
|
+
rows.push({ text: step.summaryTitle, fg: PALETTE.info, bold: true });
|
|
268
|
+
for (const line of step.summaryLines.slice(0, 4)) {
|
|
269
|
+
pushWrapped(rows, line, width, { fg: PALETTE.text });
|
|
598
270
|
}
|
|
271
|
+
rows.push({
|
|
272
|
+
text: `Fields ${wizard.getCompletedFieldCount(wizard.stepIndex)}/${wizard.getStepFieldCount(wizard.stepIndex)} complete · ${changedScreensLabel(wizard)}`,
|
|
273
|
+
fg: wizard.dirtyStepCount > 0 ? PALETTE.warn : PALETTE.muted,
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
const pushSelectedRows = () => {
|
|
277
|
+
rows.push({ text: selected.title, fg: PALETTE.info, bold: true });
|
|
278
|
+
pushWrapped(rows, selected.hint, width, { fg: PALETTE.text });
|
|
279
|
+
pushWrapped(rows, controlsText(wizard), width, { fg: PALETTE.info });
|
|
280
|
+
};
|
|
599
281
|
|
|
600
|
-
|
|
282
|
+
pushWrapped(rows, step.description, width, { fg: PALETTE.text });
|
|
283
|
+
rows.push({ text: '', kind: 'empty' });
|
|
284
|
+
|
|
285
|
+
if (wizard.isEditingTextField()) {
|
|
286
|
+
pushSelectedRows();
|
|
287
|
+
rows.push({ text: '', kind: 'empty' });
|
|
288
|
+
pushSummaryRows();
|
|
289
|
+
} else {
|
|
290
|
+
pushSummaryRows();
|
|
291
|
+
rows.push({ text: '', kind: 'empty' });
|
|
292
|
+
pushSelectedRows();
|
|
601
293
|
}
|
|
602
294
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
295
|
+
if (wizard.hydrationPending || wizard.hydrationError !== null || wizard.applyFeedback !== null) {
|
|
296
|
+
rows.push({ text: '', kind: 'empty' });
|
|
297
|
+
}
|
|
298
|
+
if (wizard.hydrationPending) {
|
|
299
|
+
rows.push({ text: 'Loading current settings...', fg: PALETTE.info, bold: true });
|
|
300
|
+
}
|
|
301
|
+
if (wizard.hydrationError !== null) {
|
|
302
|
+
pushWrapped(rows, `Current settings could not load: ${wizard.hydrationError}`, width, { fg: PALETTE.bad, bold: true });
|
|
303
|
+
}
|
|
304
|
+
if (wizard.applyFeedback !== null) {
|
|
305
|
+
const feedbackColor = wizard.applyFeedback.severity === 'error'
|
|
306
|
+
? PALETTE.bad
|
|
307
|
+
: wizard.applyFeedback.severity === 'warning'
|
|
308
|
+
? PALETTE.warn
|
|
309
|
+
: PALETTE.info;
|
|
310
|
+
rows.push({ text: wizard.applyFeedback.title, fg: feedbackColor, bold: true });
|
|
311
|
+
pushWrapped(rows, wizard.applyFeedback.summary, width, { fg: PALETTE.text });
|
|
312
|
+
for (const message of wizard.applyFeedback.messages) {
|
|
313
|
+
pushWrapped(rows, message, width, { fg: PALETTE.muted });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return rows;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function formatFieldRowText(wizard: OnboardingWizardController, field: OnboardingWizardFieldDefinition, selected: boolean, width: number): string {
|
|
321
|
+
const badge = `[${wizard.getFieldValueLabel(field)}]`;
|
|
322
|
+
const badgeWidth = getDisplayWidth(badge);
|
|
323
|
+
const labelWidth = Math.max(1, width - badgeWidth - 2);
|
|
324
|
+
const label = truncateDisplay(`${fieldRowPrefix(wizard, field, selected)}${field.label}`, labelWidth);
|
|
325
|
+
return `${label}${' '.repeat(Math.max(1, width - getDisplayWidth(label) - badgeWidth))}${badge}`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function buildControlRows(wizard: OnboardingWizardController, width: number, height: number): WorkspaceRow[] {
|
|
329
|
+
const visibleFields = Math.max(1, height);
|
|
330
|
+
const fieldRows = buildFieldRows(wizard, visibleFields, height);
|
|
331
|
+
const rows = fieldRows.map((row): WorkspaceRow => {
|
|
332
|
+
if (row.kind === 'empty') return { text: '', kind: 'empty' };
|
|
333
|
+
if (row.kind === 'moreAbove' || row.kind === 'moreBelow') {
|
|
334
|
+
return { text: row.text, kind: 'more', fg: PALETTE.dim, dim: true };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const selected = row.absoluteIndex === wizard.getSelectedFieldIndex();
|
|
338
|
+
return {
|
|
339
|
+
text: formatFieldRowText(wizard, row.field, selected, width),
|
|
340
|
+
selected,
|
|
341
|
+
kind: 'item',
|
|
342
|
+
fg: fieldColor(wizard, row.field, selected),
|
|
343
|
+
bold: selected || row.field.kind === 'action',
|
|
344
|
+
};
|
|
626
345
|
});
|
|
627
|
-
lines.push(footer);
|
|
628
346
|
|
|
629
|
-
|
|
347
|
+
while (rows.length < height) rows.push({ text: '', kind: 'empty' });
|
|
348
|
+
return rows.slice(0, height);
|
|
630
349
|
}
|
|
631
350
|
|
|
632
351
|
export function renderOnboardingWizard(
|
|
@@ -634,9 +353,28 @@ export function renderOnboardingWizard(
|
|
|
634
353
|
width: number,
|
|
635
354
|
viewportHeight: number,
|
|
636
355
|
): Line[] {
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
?
|
|
641
|
-
:
|
|
356
|
+
const layoutOptions = {
|
|
357
|
+
width,
|
|
358
|
+
height: viewportHeight,
|
|
359
|
+
leftWidth: width < 90 ? undefined : 30,
|
|
360
|
+
contextRatio: 0.42,
|
|
361
|
+
minContextRows: 8,
|
|
362
|
+
};
|
|
363
|
+
const metrics = getFullscreenWorkspaceMetrics(layoutOptions);
|
|
364
|
+
|
|
365
|
+
return renderFullscreenWorkspace({
|
|
366
|
+
width,
|
|
367
|
+
height: viewportHeight,
|
|
368
|
+
title: 'GoodVibes Agent / Onboarding Wizard',
|
|
369
|
+
stateLabel: `${modeLabel(wizard.mode)} · ${wizard.stepIndex + 1}/${wizard.steps.length} · ${changedScreensLabel(wizard)}`,
|
|
370
|
+
leftHeader: 'Steps',
|
|
371
|
+
mainHeader: wizard.currentStep.title,
|
|
372
|
+
leftRows: buildStepRows(wizard, metrics.bodyRows),
|
|
373
|
+
contextRows: buildContextRows(wizard, metrics.contextWidth),
|
|
374
|
+
controlRows: buildControlRows(wizard, metrics.contextWidth, metrics.controlRows),
|
|
375
|
+
footer: footerText(wizard),
|
|
376
|
+
leftWidth: layoutOptions.leftWidth,
|
|
377
|
+
contextRatio: layoutOptions.contextRatio,
|
|
378
|
+
minContextRows: layoutOptions.minContextRows,
|
|
379
|
+
});
|
|
642
380
|
}
|