@pellux/goodvibes-tui 0.19.27 → 0.19.29

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 (41) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +3 -1
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +2 -2
  5. package/src/cli/bundle-command.ts +3 -2
  6. package/src/cli/entrypoint.ts +2 -2
  7. package/src/cli/help.ts +1 -1
  8. package/src/cli/status.ts +9 -9
  9. package/src/cli/surface-command.ts +46 -11
  10. package/src/cli/tui-startup.ts +4 -4
  11. package/src/daemon/cli.ts +7 -0
  12. package/src/input/handler-interactions.ts +14 -1
  13. package/src/input/handler-onboarding.ts +161 -118
  14. package/src/input/handler.ts +1 -1
  15. package/src/input/onboarding/handler-onboarding-routes.ts +35 -15
  16. package/src/input/onboarding/onboarding-wizard-apply.ts +35 -25
  17. package/src/input/onboarding/onboarding-wizard-constants.ts +4 -5
  18. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +93 -5
  19. package/src/input/onboarding/onboarding-wizard-helpers.ts +2 -3
  20. package/src/input/onboarding/onboarding-wizard-rules.ts +40 -8
  21. package/src/input/onboarding/onboarding-wizard-state.ts +19 -8
  22. package/src/input/onboarding/onboarding-wizard-steps.ts +226 -93
  23. package/src/input/onboarding/onboarding-wizard-types.ts +15 -0
  24. package/src/input/onboarding/onboarding-wizard.ts +123 -6
  25. package/src/input/settings-modal-types.ts +2 -1
  26. package/src/input/settings-modal.ts +4 -0
  27. package/src/main.ts +35 -27
  28. package/src/renderer/compositor.ts +3 -3
  29. package/src/renderer/onboarding/onboarding-wizard.ts +141 -57
  30. package/src/renderer/settings-modal-helpers.ts +9 -0
  31. package/src/renderer/settings-modal.ts +3 -0
  32. package/src/runtime/bootstrap.ts +15 -0
  33. package/src/runtime/onboarding/apply.ts +45 -90
  34. package/src/runtime/onboarding/derivation.ts +7 -7
  35. package/src/runtime/onboarding/markers.ts +41 -55
  36. package/src/runtime/onboarding/snapshot.ts +1 -0
  37. package/src/runtime/onboarding/state.ts +6 -6
  38. package/src/runtime/onboarding/types.ts +24 -27
  39. package/src/runtime/onboarding/verify.ts +3 -65
  40. package/src/runtime/surface-feature-flags.ts +67 -0
  41. package/src/version.ts +1 -1
@@ -4,11 +4,39 @@ import { STEP_ORDER } from './onboarding-wizard-constants.ts';
4
4
  import { buildOnboardingApplyRequest } from './onboarding-wizard-apply.ts';
5
5
  import { buildOnboardingWizardSteps } from './onboarding-wizard-steps.ts';
6
6
  import { buildDefaultDerivedState, clamp, cloneSelection, getRuntimeDerivedState, maskValue, modelSelectionLabel, normalizeText } from './onboarding-wizard-helpers.ts';
7
- import { defaultReviewUserMarker as defaultReviewUserMarkerForController, getBooleanFieldValue as getBooleanFieldValueForController, getDefaultAdminUsername as getDefaultAdminUsernameForController, getNumberFieldValue as getNumberFieldValueForController, getPortFieldValue as getPortFieldValueForController, getSelectedSecretMedium as getSelectedSecretMediumForController, getSharedIpDefault as getSharedIpDefaultForController, getSharedIpHostDefault as getSharedIpHostDefaultForController, getStringFieldValue as getStringFieldValueForController, hasAdminAuthUser as hasAdminAuthUserForController, hasBootstrapCredentialPresent as hasBootstrapCredentialPresentForController, hasSelectedInboundExternalSurface as hasSelectedInboundExternalSurfaceForController, hasServerCapabilitiesSelected as hasServerCapabilitiesSelectedForController, isCapabilitySelected as isCapabilitySelectedForController, isRequiredExternalSetupField as isRequiredExternalSetupFieldForController, parseIntegerFieldValue as parseIntegerFieldValueForController, requiresAuthBootstrap as requiresAuthBootstrapForController, selectAllServerCapabilities as selectAllServerCapabilitiesForController, selectLocalTuiOnly as selectLocalTuiOnlyForController, setCapabilityValue as setCapabilityValueForController, shouldEnableBrowserSurface as shouldEnableBrowserSurfaceForController, shouldEnableHttpListener as shouldEnableHttpListenerForController, shouldExposeControlPlaneNetwork as shouldExposeControlPlaneNetworkForController, shouldExposeHttpListenerNetworkFields as shouldExposeHttpListenerNetworkFieldsForController, toggleCapability as toggleCapabilityForController } from './onboarding-wizard-rules.ts';
7
+ import {
8
+ clearExternalSurfaces as clearExternalSurfacesForController,
9
+ getBooleanFieldValue as getBooleanFieldValueForController,
10
+ getDefaultAdminUsername as getDefaultAdminUsernameForController,
11
+ getNumberFieldValue as getNumberFieldValueForController,
12
+ getPortFieldValue as getPortFieldValueForController,
13
+ getSelectedSecretMedium as getSelectedSecretMediumForController,
14
+ getSharedIpDefault as getSharedIpDefaultForController,
15
+ getSharedIpHostDefault as getSharedIpHostDefaultForController,
16
+ getStringFieldValue as getStringFieldValueForController,
17
+ hasAdminAuthUser as hasAdminAuthUserForController,
18
+ hasBootstrapCredentialPresent as hasBootstrapCredentialPresentForController,
19
+ hasLocalAuthUser as hasLocalAuthUserForController,
20
+ hasSelectedInboundExternalSurface as hasSelectedInboundExternalSurfaceForController,
21
+ hasServerCapabilitiesSelected as hasServerCapabilitiesSelectedForController,
22
+ isCapabilitySelected as isCapabilitySelectedForController,
23
+ isRequiredExternalSetupField as isRequiredExternalSetupFieldForController,
24
+ parseIntegerFieldValue as parseIntegerFieldValueForController,
25
+ requiresAuthBootstrap as requiresAuthBootstrapForController,
26
+ selectAllExternalSurfaces as selectAllExternalSurfacesForController,
27
+ selectAllServerCapabilities as selectAllServerCapabilitiesForController,
28
+ selectLocalTuiOnly as selectLocalTuiOnlyForController,
29
+ setCapabilityValue as setCapabilityValueForController,
30
+ shouldEnableBrowserSurface as shouldEnableBrowserSurfaceForController,
31
+ shouldEnableHttpListener as shouldEnableHttpListenerForController,
32
+ shouldExposeControlPlaneNetwork as shouldExposeControlPlaneNetworkForController,
33
+ shouldExposeHttpListenerNetworkFields as shouldExposeHttpListenerNetworkFieldsForController,
34
+ toggleCapability as toggleCapabilityForController,
35
+ } from './onboarding-wizard-rules.ts';
8
36
  import { ensureSelectionVisible as ensureSelectionVisibleForController, getBlockingFieldLabels as getBlockingFieldLabelsForController, getCapabilitySelectionState as getCapabilitySelectionStateForController, getCompletedFieldCount as getCompletedFieldCountForController, getCompletedToggleCount as getCompletedToggleCountForController, getCurrentCapabilities as getCurrentCapabilitiesForController, getFieldById as getFieldByIdForController, getFieldValidationError as getFieldValidationErrorForController, getStepFieldCount as getStepFieldCountForController, getToggleFieldCount as getToggleFieldCountForController, hasExistingAccessState as hasExistingAccessStateForController, isFieldDirty as isFieldDirtyForController, isFieldDirtyByDefinition as isFieldDirtyByDefinitionForController, isFieldSatisfied as isFieldSatisfiedForController, isStepDirty as isStepDirtyForController, recalculateDirtyState as recalculateDirtyStateForController, reconcileStateWithCurrentDefinitions as reconcileStateWithCurrentDefinitionsForController, reconcileStepCursor as reconcileStepCursorForController, resetValuesFromCurrentDefinitions as resetValuesFromCurrentDefinitionsForController } from './onboarding-wizard-state.ts';
9
- import type { MutableModelSelectionMap, OnboardingWizardAction, OnboardingWizardFieldDefinition, OnboardingWizardFieldWindow, OnboardingWizardMode, OnboardingWizardModelSelection, OnboardingWizardRuntimeHydration, OnboardingWizardSnapshot, OnboardingWizardStepDefinition, OnboardingWizardStepId } from './onboarding-wizard-types.ts';
37
+ import type { MutableModelSelectionMap, OnboardingWizardAction, OnboardingWizardApplyFeedback, OnboardingWizardFieldDefinition, OnboardingWizardFieldWindow, OnboardingWizardMode, OnboardingWizardModelSelection, OnboardingWizardRuntimeHydration, OnboardingWizardSnapshot, OnboardingWizardStepDefinition, OnboardingWizardStepId } from './onboarding-wizard-types.ts';
10
38
 
11
- export type { OnboardingWizardAcknowledgementFieldDefinition, OnboardingWizardAction, OnboardingWizardActionFieldDefinition, OnboardingWizardChecklistFieldDefinition, OnboardingWizardFieldDefinition, OnboardingWizardFieldKind, OnboardingWizardFieldWindow, OnboardingWizardMaskedFieldDefinition, OnboardingWizardMode, OnboardingWizardModelPickerFieldDefinition, OnboardingWizardModelSelection, OnboardingWizardRadioFieldDefinition, OnboardingWizardRadioOption, OnboardingWizardRuntimeHydration, OnboardingWizardSnapshot, OnboardingWizardStatusFieldDefinition, OnboardingWizardStepDefinition, OnboardingWizardStepId, OnboardingWizardTextFieldDefinition } from './onboarding-wizard-types.ts';
39
+ export type { OnboardingWizardAcknowledgementFieldDefinition, OnboardingWizardAction, OnboardingWizardActionFieldDefinition, OnboardingWizardApplyFeedback, OnboardingWizardApplyFeedbackSeverity, OnboardingWizardChecklistFieldDefinition, OnboardingWizardExternalSurfaceStepId, OnboardingWizardFieldDefinition, OnboardingWizardFieldKind, OnboardingWizardFieldWindow, OnboardingWizardMaskedFieldDefinition, OnboardingWizardMode, OnboardingWizardModelPickerFieldDefinition, OnboardingWizardModelSelection, OnboardingWizardRadioFieldDefinition, OnboardingWizardRadioOption, OnboardingWizardRuntimeHydration, OnboardingWizardSnapshot, OnboardingWizardStatusFieldDefinition, OnboardingWizardStepDefinition, OnboardingWizardStepId, OnboardingWizardTextFieldDefinition } from './onboarding-wizard-types.ts';
12
40
  export { getOnboardingWizardBodyRows, getOnboardingWizardVisibleFieldCount } from './onboarding-wizard-helpers.ts';
13
41
 
14
42
  export class OnboardingWizardController {
@@ -30,6 +58,7 @@ export class OnboardingWizardController {
30
58
  public editingFieldId: string | null = null;
31
59
  public editBuffer = '';
32
60
  public hydrationError: string | null = null;
61
+ public applyFeedback: OnboardingWizardApplyFeedback | null = null;
33
62
 
34
63
  public readonly baselineToggleState = new Map<string, boolean>();
35
64
  public readonly baselineRadioState = new Map<string, string>();
@@ -40,7 +69,9 @@ export class OnboardingWizardController {
40
69
  public runtimeDerived: OnboardingStepDerivationState = buildDefaultDerivedState();
41
70
 
42
71
  public get steps(): readonly OnboardingWizardStepDefinition[] {
43
- return buildOnboardingWizardSteps(this);
72
+ const steps = buildOnboardingWizardSteps(this);
73
+ this.ensureNavigationStateLength(steps.length);
74
+ return steps;
44
75
  }
45
76
 
46
77
  public get currentStep(): OnboardingWizardStepDefinition {
@@ -59,6 +90,11 @@ export class OnboardingWizardController {
59
90
  this.resetValuesFromCurrentDefinitions();
60
91
  }
61
92
 
93
+ private ensureNavigationStateLength(stepCount: number): void {
94
+ while (this.scrollOffsets.length < stepCount) this.scrollOffsets.push(0);
95
+ while (this.selectedFieldIndices.length < stepCount) this.selectedFieldIndices.push(0);
96
+ }
97
+
62
98
  public open(mode: OnboardingWizardMode = 'new'): void {
63
99
  this.mode = mode;
64
100
  this.active = true;
@@ -67,6 +103,7 @@ export class OnboardingWizardController {
67
103
  this.stepIndex = 0;
68
104
  this.pendingModelPickerTarget = null;
69
105
  this.pendingAction = null;
106
+ this.applyFeedback = null;
70
107
  this.editingFieldId = null;
71
108
  this.editBuffer = '';
72
109
  this.scrollOffsets.fill(0);
@@ -80,6 +117,7 @@ export class OnboardingWizardController {
80
117
  this.hydrationError = null;
81
118
  this.pendingModelPickerTarget = null;
82
119
  this.pendingAction = null;
120
+ this.applyFeedback = null;
83
121
  this.cancelEdit();
84
122
  }
85
123
 
@@ -143,6 +181,7 @@ export class OnboardingWizardController {
143
181
  editBuffer: this.editBuffer,
144
182
  hydrationPending: this.hydrationPending,
145
183
  hydrationError: this.hydrationError,
184
+ applyFeedback: this.applyFeedback,
146
185
  hydration: this.captureHydratedState(),
147
186
  };
148
187
  }
@@ -156,10 +195,13 @@ export class OnboardingWizardController {
156
195
  this.stepIndex = clamp(snapshot.stepIndex, 0, Math.max(0, this.steps.length - 1));
157
196
  this.pendingModelPickerTarget = snapshot.pendingModelPickerTarget;
158
197
  this.pendingAction = snapshot.pendingAction;
198
+ this.applyFeedback = snapshot.applyFeedback;
159
199
  this.dirtyStepIds.clear();
160
200
  for (const stepId of snapshot.dirtyStepIds) this.dirtyStepIds.add(stepId);
161
201
 
162
- for (let index = 0; index < this.scrollOffsets.length; index += 1) {
202
+ const navigationLength = Math.max(this.steps.length, snapshot.scrollOffsets.length, this.scrollOffsets.length);
203
+ this.ensureNavigationStateLength(navigationLength);
204
+ for (let index = 0; index < navigationLength; index += 1) {
163
205
  this.scrollOffsets[index] = snapshot.scrollOffsets[index] ?? 0;
164
206
  this.selectedFieldIndices[index] = snapshot.selectedFieldIndices[index] ?? 0;
165
207
  }
@@ -186,6 +228,7 @@ export class OnboardingWizardController {
186
228
  public beginRuntimeHydration(): void {
187
229
  this.hydrationPending = true;
188
230
  this.hydrationError = null;
231
+ this.applyFeedback = null;
189
232
  this.stepIndex = 0;
190
233
  this.pendingModelPickerTarget = null;
191
234
  this.pendingAction = null;
@@ -195,6 +238,7 @@ export class OnboardingWizardController {
195
238
  public finishRuntimeHydration(): void {
196
239
  this.hydrationPending = false;
197
240
  this.hydrationError = null;
241
+ this.applyFeedback = null;
198
242
  this.stepIndex = clamp(this.stepIndex, 0, Math.max(0, this.steps.length - 1));
199
243
  this.reconcileStepCursor(this.stepIndex);
200
244
  }
@@ -202,6 +246,12 @@ export class OnboardingWizardController {
202
246
  public failRuntimeHydration(message: string): void {
203
247
  this.hydrationPending = false;
204
248
  this.hydrationError = message;
249
+ this.applyFeedback = {
250
+ severity: 'error',
251
+ title: 'Current settings could not load',
252
+ summary: message,
253
+ messages: [message],
254
+ };
205
255
  this.stepIndex = 0;
206
256
  this.pendingModelPickerTarget = null;
207
257
  this.pendingAction = null;
@@ -307,6 +357,14 @@ export class OnboardingWizardController {
307
357
  this.pendingAction = null;
308
358
  }
309
359
 
360
+ public setApplyFeedback(feedback: OnboardingWizardApplyFeedback): void {
361
+ this.applyFeedback = feedback;
362
+ }
363
+
364
+ public clearApplyFeedback(): void {
365
+ this.applyFeedback = null;
366
+ }
367
+
310
368
  public consumePendingAction(): OnboardingWizardAction | null {
311
369
  const action = this.pendingAction;
312
370
  this.pendingAction = null;
@@ -323,6 +381,7 @@ export class OnboardingWizardController {
323
381
  this.toggleCapability(field.capabilityId);
324
382
  this.pendingModelPickerTarget = null;
325
383
  this.pendingAction = null;
384
+ this.applyFeedback = null;
326
385
  this.recalculateDirtyState();
327
386
  return;
328
387
  }
@@ -332,6 +391,7 @@ export class OnboardingWizardController {
332
391
  this.toggleState.set(field.id, !current);
333
392
  this.pendingModelPickerTarget = null;
334
393
  this.pendingAction = null;
394
+ this.applyFeedback = null;
335
395
  this.recalculateDirtyState();
336
396
  return;
337
397
  }
@@ -345,6 +405,7 @@ export class OnboardingWizardController {
345
405
  this.radioState.set(field.id, next.id);
346
406
  this.pendingModelPickerTarget = null;
347
407
  this.pendingAction = null;
408
+ this.applyFeedback = null;
348
409
  this.recalculateDirtyState();
349
410
  return;
350
411
  }
@@ -360,6 +421,7 @@ export class OnboardingWizardController {
360
421
  this.selectAllServerCapabilities();
361
422
  this.pendingAction = null;
362
423
  this.pendingModelPickerTarget = null;
424
+ this.applyFeedback = null;
363
425
  this.recalculateDirtyState();
364
426
  return;
365
427
  }
@@ -368,6 +430,25 @@ export class OnboardingWizardController {
368
430
  this.selectLocalTuiOnly();
369
431
  this.pendingAction = null;
370
432
  this.pendingModelPickerTarget = null;
433
+ this.applyFeedback = null;
434
+ this.recalculateDirtyState();
435
+ return;
436
+ }
437
+
438
+ if (field.action === 'select-all-external-surfaces') {
439
+ this.selectAllExternalSurfaces();
440
+ this.pendingAction = null;
441
+ this.pendingModelPickerTarget = null;
442
+ this.applyFeedback = null;
443
+ this.recalculateDirtyState();
444
+ return;
445
+ }
446
+
447
+ if (field.action === 'clear-external-surfaces') {
448
+ this.clearExternalSurfaces();
449
+ this.pendingAction = null;
450
+ this.pendingModelPickerTarget = null;
451
+ this.applyFeedback = null;
371
452
  this.recalculateDirtyState();
372
453
  return;
373
454
  }
@@ -380,6 +461,7 @@ export class OnboardingWizardController {
380
461
  this.pendingModelPickerTarget = field.target;
381
462
  this.pendingAction = null;
382
463
  this.touchedActionFields.add(field.id);
464
+ this.applyFeedback = null;
383
465
  }
384
466
 
385
467
  public beginEdit(fieldId: string): void {
@@ -391,12 +473,21 @@ export class OnboardingWizardController {
391
473
  this.editBuffer = this.textState.get(field.id) ?? field.defaultValue;
392
474
  }
393
475
 
476
+ public beginSelectedTextInput(initialText: string): boolean {
477
+ const field = this.getSelectedField();
478
+ if (!field || (field.kind !== 'text' && field.kind !== 'masked')) return false;
479
+ this.beginEdit(field.id);
480
+ this.editBuffer = initialText;
481
+ return true;
482
+ }
483
+
394
484
  public commitEdit(): void {
395
485
  const fieldId = this.editingFieldId;
396
486
  if (fieldId === null) return;
397
487
  const field = this.getFieldById(fieldId);
398
488
  if (field && (field.kind === 'text' || field.kind === 'masked')) {
399
489
  this.textState.set(fieldId, this.editBuffer);
490
+ this.applyFeedback = null;
400
491
  this.recalculateDirtyState();
401
492
  }
402
493
  this.editingFieldId = null;
@@ -418,6 +509,21 @@ export class OnboardingWizardController {
418
509
  this.editBuffer = this.editBuffer.slice(0, -1);
419
510
  }
420
511
 
512
+ public clearEditingValue(): void {
513
+ if (this.editingFieldId === null) return;
514
+ this.editBuffer = '';
515
+ }
516
+
517
+ public clearSelectedTextField(): boolean {
518
+ const field = this.getSelectedField();
519
+ if (!field || (field.kind !== 'text' && field.kind !== 'masked')) return false;
520
+ this.textState.set(field.id, '');
521
+ if (this.editingFieldId === field.id) this.editBuffer = '';
522
+ this.applyFeedback = null;
523
+ this.recalculateDirtyState();
524
+ return true;
525
+ }
526
+
421
527
  public setFieldValue(fieldId: string, value: boolean | string): void {
422
528
  const field = this.getFieldById(fieldId);
423
529
  if (!field) return;
@@ -426,6 +532,7 @@ export class OnboardingWizardController {
426
532
  if (typeof value === 'boolean') {
427
533
  if (field.kind === 'checklist' && field.capabilityId) this.setCapabilityValue(field.capabilityId, value);
428
534
  else this.toggleState.set(fieldId, value);
535
+ this.applyFeedback = null;
429
536
  this.recalculateDirtyState();
430
537
  }
431
538
  return;
@@ -434,6 +541,7 @@ export class OnboardingWizardController {
434
541
  if (field.kind === 'radio') {
435
542
  if (typeof value === 'string' && field.options.some((option) => option.id === value)) {
436
543
  this.radioState.set(fieldId, value);
544
+ this.applyFeedback = null;
437
545
  this.recalculateDirtyState();
438
546
  }
439
547
  return;
@@ -443,6 +551,7 @@ export class OnboardingWizardController {
443
551
  if (typeof value === 'string') {
444
552
  this.textState.set(fieldId, value);
445
553
  if (this.editingFieldId === fieldId) this.editBuffer = value;
554
+ this.applyFeedback = null;
446
555
  this.recalculateDirtyState();
447
556
  }
448
557
  }
@@ -498,11 +607,15 @@ export class OnboardingWizardController {
498
607
 
499
608
  if (field.kind === 'text') {
500
609
  const value = normalizeText(this.getFieldValue(field) as string);
610
+ if (value.length === 0 && field.required === true) return 'Missing';
611
+ if (value.length === 0 && this.isRequiredExternalSetupField(field.id)) return 'Not set';
501
612
  return value.length > 0 ? value : field.placeholder;
502
613
  }
503
614
 
504
615
  if (field.kind === 'masked') {
505
616
  const value = normalizeText(this.getFieldValue(field) as string);
617
+ if (value.length === 0 && field.required === true) return 'Missing';
618
+ if (value.length === 0 && this.isRequiredExternalSetupField(field.id)) return 'Not set';
506
619
  return value.length > 0 ? maskValue(value) : field.placeholder;
507
620
  }
508
621
 
@@ -521,6 +634,7 @@ export class OnboardingWizardController {
521
634
  enabled: selection.enabled ?? true,
522
635
  });
523
636
  this.pendingModelPickerTarget = null;
637
+ this.applyFeedback = null;
524
638
  this.recalculateDirtyState();
525
639
  }
526
640
 
@@ -538,6 +652,7 @@ export class OnboardingWizardController {
538
652
  this.baselineModelSelectionState.clear();
539
653
  for (const [key, value] of this.modelSelectionState) this.baselineModelSelectionState.set(key, cloneSelection(value));
540
654
  this.dirtyStepIds.clear();
655
+ this.applyFeedback = null;
541
656
  }
542
657
 
543
658
  public getSharedIpDefault(enabled: { readonly controlPlane: boolean; readonly httpListener: boolean; readonly web: boolean }): boolean {
@@ -548,10 +663,11 @@ export class OnboardingWizardController {
548
663
  return getSharedIpHostDefaultForController(this, enabled);
549
664
  }
550
665
 
551
- public defaultReviewUserMarker(): boolean { return defaultReviewUserMarkerForController(this); }
552
666
  public toggleCapability(capabilityId: OnboardingStep1CapabilityId): void { toggleCapabilityForController(this, capabilityId); }
553
667
  public selectAllServerCapabilities(): void { selectAllServerCapabilitiesForController(this); }
554
668
  public selectLocalTuiOnly(): void { selectLocalTuiOnlyForController(this); }
669
+ public selectAllExternalSurfaces(): void { selectAllExternalSurfacesForController(this); }
670
+ public clearExternalSurfaces(): void { clearExternalSurfacesForController(this); }
555
671
  public setCapabilityValue(capabilityId: OnboardingStep1CapabilityId, selected: boolean): void { setCapabilityValueForController(this, capabilityId, selected); }
556
672
  public isCapabilitySelected(capabilityId: OnboardingStep1CapabilityId): boolean { return isCapabilitySelectedForController(this, capabilityId); }
557
673
  public hasServerCapabilitiesSelected(): boolean { return hasServerCapabilitiesSelectedForController(this); }
@@ -564,6 +680,7 @@ export class OnboardingWizardController {
564
680
  public shouldExposeControlPlaneNetwork(): boolean { return shouldExposeControlPlaneNetworkForController(this); }
565
681
  public requiresAuthBootstrap(): boolean { return requiresAuthBootstrapForController(this); }
566
682
  public hasAdminAuthUser(): boolean { return hasAdminAuthUserForController(this); }
683
+ public hasLocalAuthUser(): boolean { return hasLocalAuthUserForController(this); }
567
684
  public hasBootstrapCredentialPresent(): boolean { return hasBootstrapCredentialPresentForController(this); }
568
685
  public getDefaultAdminUsername(): string { return getDefaultAdminUsernameForController(this); }
569
686
  public getBooleanFieldValue(fieldId: string, fallback: boolean): boolean { return getBooleanFieldValueForController(this, fieldId, fallback); }
@@ -2,7 +2,7 @@ import type { ConfigSetting } from '@pellux/goodvibes-sdk/platform/config/schema
2
2
  import type { ProviderAuthFreshness, ProviderAuthRoute } from '@pellux/goodvibes-sdk/platform/runtime/provider-accounts/registry';
3
3
  import type { FeatureFlag, FlagState } from '@pellux/goodvibes-sdk/platform/runtime/feature-flags/types';
4
4
 
5
- export type SettingsCategory = 'display' | 'ui' | 'provider' | 'subscriptions' | 'behavior' | 'storage' | 'permissions' | 'mcp' | 'sandbox' | 'danger' | 'tools' | 'flags' | 'network';
5
+ export type SettingsCategory = 'display' | 'ui' | 'provider' | 'subscriptions' | 'behavior' | 'storage' | 'permissions' | 'mcp' | 'sandbox' | 'surfaces' | 'danger' | 'tools' | 'flags' | 'network';
6
6
 
7
7
  export const SETTINGS_CATEGORIES: SettingsCategory[] = [
8
8
  'display',
@@ -14,6 +14,7 @@ export const SETTINGS_CATEGORIES: SettingsCategory[] = [
14
14
  'permissions',
15
15
  'mcp',
16
16
  'sandbox',
17
+ 'surfaces',
17
18
  'danger',
18
19
  'tools',
19
20
  'flags',
@@ -519,6 +519,8 @@ export class SettingsModal {
519
519
  cat = 'tools';
520
520
  } else if (rawCat === 'controlPlane' || rawCat === 'httpListener' || rawCat === 'web') {
521
521
  cat = 'network';
522
+ } else if (rawCat === 'surfaces') {
523
+ cat = 'surfaces';
522
524
  } else {
523
525
  cat = rawCat as SettingsCategory;
524
526
  }
@@ -746,6 +748,8 @@ export class SettingsModal {
746
748
  if (isRestartKey && previousValue !== value) {
747
749
  this.lastSaveTriggeredRestart = 'web';
748
750
  }
751
+ } else if (rawCat === 'surfaces') {
752
+ cat = 'surfaces';
749
753
  } else {
750
754
  cat = rawCat as SettingsCategory;
751
755
  }
package/src/main.ts CHANGED
@@ -177,6 +177,7 @@ async function main() {
177
177
  };
178
178
 
179
179
  const getViewportHeight = (): number => {
180
+ if (input.onboardingWizard.active) return stdout.rows || 24;
180
181
  const promptLines: number = input.getVisiblePromptLineCount(getPromptContentWidth());
181
182
  const currentModel = providerRegistry.getCurrentModel();
182
183
  return (stdout.rows || 24) - 2 - estimateShellFooterHeight(promptLines, currentModel.contextWindow);
@@ -258,7 +259,8 @@ async function main() {
258
259
  stdout.removeListener('resize', resizeHandler);
259
260
  process.removeListener('SIGINT', sigintHandler);
260
261
  process.removeListener('unhandledRejection', unhandledRejectionHandler);
261
- stdout.write(PASTE_DISABLE + KEYBOARD_EXT_DISABLE + MOUSE_DISABLE + CURSOR_SHOW + (cli.flags.noAltScreen ? '' : ALT_SCREEN_EXIT));
262
+ const exitScreen = cli.flags.noAltScreen ? CLEAR_SCREEN : CLEAR_SCREEN + ALT_SCREEN_EXIT;
263
+ stdout.write(PASTE_DISABLE + KEYBOARD_EXT_DISABLE + MOUSE_DISABLE + CURSOR_SHOW + exitScreen);
262
264
  stdin.setRawMode(false);
263
265
  process.exit(0);
264
266
  };
@@ -550,19 +552,23 @@ async function main() {
550
552
  composerPendingRisk: composerState.pendingRisk,
551
553
  }).lines;
552
554
 
555
+ const onboardingOwnsScreen = input.onboardingWizard.active;
556
+ const shellHeaderLines = onboardingOwnsScreen ? [] : headerLines;
557
+ const shellFooterLines = onboardingOwnsScreen ? [] : footerLines;
558
+ const panelWidth = !onboardingOwnsScreen && panelManager.isVisible() && panelManager.getAllOpen().length > 0
559
+ ? panelManager.getRightWidth(width)
560
+ : 0;
553
561
  const shellLayout = createShellLayout({
554
562
  width,
555
563
  height,
556
- headerHeight: headerLines.length,
557
- footerHeight: footerLines.length,
558
- panelWidth: panelManager.isVisible() && panelManager.getAllOpen().length > 0
559
- ? panelManager.getRightWidth(width)
560
- : 0,
564
+ headerHeight: shellHeaderLines.length,
565
+ footerHeight: shellFooterLines.length,
566
+ panelWidth,
561
567
  });
562
568
  const vHeight = shellLayout.body.height;
563
569
  const conversationWidth = shellLayout.conversation.width;
564
570
  activeConversationWidth = conversationWidth;
565
- const hasPanelWorkspace = panelManager.isVisible() && panelManager.getAllOpen().length > 0;
571
+ const hasPanelWorkspace = !onboardingOwnsScreen && panelManager.isVisible() && panelManager.getAllOpen().length > 0;
566
572
  conversation.setSplashSuppressed(hasPanelWorkspace);
567
573
 
568
574
  // Flush pending renders after updating the width provider and splash posture
@@ -626,27 +632,29 @@ async function main() {
626
632
  });
627
633
 
628
634
  // Panel composite data
629
- const panelComposite = buildPanelCompositeData(
630
- panelManager,
631
- input,
632
- shellLayout.panel?.width ?? 0,
633
- shellLayout.panel?.height ?? vHeight,
634
- );
635
+ const panelComposite = onboardingOwnsScreen
636
+ ? { panelData: undefined, panelWidth: 0 }
637
+ : buildPanelCompositeData(
638
+ panelManager,
639
+ input,
640
+ shellLayout.panel?.width ?? 0,
641
+ shellLayout.panel?.height ?? vHeight,
642
+ );
635
643
 
636
644
  compositor.composite({
637
645
  width, height,
638
- header: headerLines,
646
+ header: shellHeaderLines,
639
647
  viewport,
640
- footer: footerLines,
641
- selection: {
648
+ footer: shellFooterLines,
649
+ selection: onboardingOwnsScreen ? undefined : {
642
650
  isCellSelected: (col, row) => selection.isCellSelected(col, row),
643
651
  scrollTop,
644
652
  lineCount: conversation.history.getLineCount(),
645
653
  },
646
- search: input.searchManager.active ? {
654
+ search: !onboardingOwnsScreen && input.searchManager.active ? {
647
655
  manager: input.searchManager,
648
656
  scrollTop,
649
- viewportStartY: 2,
657
+ viewportStartY: shellHeaderLines.length,
650
658
  } : undefined,
651
659
  panel: panelComposite.panelData,
652
660
  panelWidth: panelComposite.panelWidth,
@@ -674,15 +682,6 @@ async function main() {
674
682
  render,
675
683
  });
676
684
 
677
- applyInitialTuiCliState({
678
- cli,
679
- input,
680
- commandRegistry,
681
- commandContext,
682
- shellPaths: ctx.services.shellPaths,
683
- render,
684
- });
685
-
686
685
  // --- Streaming speed + tool preview wiring ---
687
686
  const refreshGit = () => gitStatusProvider.refresh().then((info) => { lastGitInfoRef.value = info; render(); }).catch(() => { /* non-fatal */ });
688
687
  // Refresh git status after each turn completes or after tool results arrive
@@ -728,6 +727,15 @@ async function main() {
728
727
  stdin.setEncoding('utf8');
729
728
  stdout.write((cli.flags.noAltScreen ? '' : ALT_SCREEN_ENTER) + CLEAR_SCREEN + CURSOR_HIDE + MOUSE_ENABLE + KEYBOARD_EXT_ENABLE + PASTE_ENABLE);
730
729
 
730
+ applyInitialTuiCliState({
731
+ cli,
732
+ input,
733
+ commandRegistry,
734
+ commandContext,
735
+ shellPaths: ctx.services.shellPaths,
736
+ render,
737
+ });
738
+
731
739
  stdin.on('data', (data: string) => {
732
740
  const blocking = handleBlockingShellInput({
733
741
  data,
@@ -86,11 +86,11 @@ export class Compositor {
86
86
  const leftWidth = hasPanel ? Math.max(1, width - panelWidth - 1) : width;
87
87
  const sepX = hasPanel ? leftWidth : -1;
88
88
 
89
- // 1. Draw Header (Rows 0-1) — always full width
89
+ // 1. Draw Header — always full width
90
90
  header.forEach((line, i) => newBuffer.blitLine(i, line));
91
91
 
92
- // 2. Draw Viewport (Starting at Row 2)
93
- const viewportStartY = 2;
92
+ // 2. Draw Viewport directly after the supplied header.
93
+ const viewportStartY = header.length;
94
94
  const vHeight = Math.max(0, height - header.length - footer.length);
95
95
 
96
96
  // Calculate the offset for bottom-anchored short history