@praxisui/stepper 8.0.0-beta.2 → 8.0.0-beta.21

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.
@@ -1,19 +1,19 @@
1
1
  import * as i1$1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Inject, Component, inject, ChangeDetectorRef, signal, ElementRef, Renderer2, EventEmitter, computed, TemplateRef, Output, Input, ContentChild, ChangeDetectionStrategy, ENVIRONMENT_INITIALIZER, isDevMode, effect, ViewChild } from '@angular/core';
4
+ import { Inject, Component, EventEmitter, inject, DestroyRef, Injector, ChangeDetectorRef, ViewContainerRef, ViewChild, Output, ChangeDetectionStrategy, Input, signal, ElementRef, Renderer2, effect, computed, TemplateRef, ContentChild, ENVIRONMENT_INITIALIZER, isDevMode } from '@angular/core';
5
5
  import { ActivatedRoute } from '@angular/router';
6
6
  import * as i2$1 from '@angular/material/stepper';
7
7
  import { MatStepperModule } from '@angular/material/stepper';
8
- import * as i6 from '@angular/material/button';
8
+ import * as i3$1 from '@angular/material/button';
9
9
  import { MatButtonModule } from '@angular/material/button';
10
- import * as i5$1 from '@angular/material/icon';
10
+ import * as i4$1 from '@angular/material/icon';
11
11
  import { MatIconModule } from '@angular/material/icon';
12
- import { providePraxisI18n, PraxisI18nService, IconPickerService, createEmptyRichContentDocument, PraxisIconDirective, deepMerge, ASYNC_CONFIG_STORAGE, ComponentKeyService, LoggerService, DynamicWidgetLoaderDirective, EmptyStateCardComponent, EDITORIAL_WIDGET_TAG, ComponentMetadataRegistry, createCorporateLoggerConfig, ConsoleLoggerSink, CONFIG_STORAGE } from '@praxisui/core';
12
+ import { PraxisIconDirective, providePraxisI18n, PraxisI18nService, IconPickerService, createEmptyRichContentDocument, deepMerge, ASYNC_CONFIG_STORAGE, ComponentKeyService, LoggerService, DynamicWidgetLoaderDirective, EmptyStateCardComponent, EDITORIAL_WIDGET_TAG, ComponentMetadataRegistry, createCorporateLoggerConfig, ConsoleLoggerSink, CONFIG_STORAGE } from '@praxisui/core';
13
13
  import * as i2 from '@angular/forms';
14
14
  import { FormsModule, ReactiveFormsModule } from '@angular/forms';
15
15
  import { PraxisDynamicFormConfigEditor, PraxisDynamicForm } from '@praxisui/dynamic-form';
16
- import { SettingsPanelService, SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
16
+ import { SETTINGS_PANEL_DATA, SettingsPanelService } from '@praxisui/settings-panel';
17
17
  import * as i3 from '@angular/material/form-field';
18
18
  import { MatFormFieldModule } from '@angular/material/form-field';
19
19
  import * as i4 from '@angular/material/input';
@@ -28,13 +28,16 @@ import * as i1 from '@angular/material/dialog';
28
28
  import { MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
29
29
  import * as i10 from '@angular/material/tabs';
30
30
  import { MatTabsModule } from '@angular/material/tabs';
31
- import { BehaviorSubject } from 'rxjs';
31
+ import { isObservable, firstValueFrom, BehaviorSubject, Subscription } from 'rxjs';
32
32
  import { PraxisListConfigEditor } from '@praxisui/list';
33
33
  import { PraxisFilesUploadConfigEditor } from '@praxisui/files-upload';
34
34
  import { ComponentPaletteDialogComponent } from '@praxisui/page-builder';
35
35
  import * as i5 from '@angular/material/checkbox';
36
36
  import { MatCheckboxModule } from '@angular/material/checkbox';
37
- import { BaseAiAdapter, PraxisAiAssistantComponent } from '@praxisui/ai';
37
+ import * as i5$1 from '@angular/material/progress-spinner';
38
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
39
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
40
+ import { BaseAiAdapter, AiBackendApiService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnOrchestratorService, createPraxisAssistantViewportLayout, PraxisAiAssistantShellComponent } from '@praxisui/ai';
38
41
  import { PraxisRichContent } from '@praxisui/rich-content';
39
42
  import { take } from 'rxjs/operators';
40
43
  import * as i1$2 from '@angular/material/card';
@@ -93,7 +96,7 @@ class SelectQuickConfigDialogComponent {
93
96
  <button mat-button (click)="cancel()">Cancelar</button>
94
97
  <button mat-flat-button color="primary" (click)="confirm()">Adicionar</button>
95
98
  </div>
96
- `, isInline: true, styles: [".g{display:grid}.gap-12{gap:12px}.w-full{width:100%}.row{display:flex}.ai-center{align-items:center}.gap-16{gap:16px}.g-auto-220{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }] });
99
+ `, isInline: true, styles: [".g{display:grid}.gap-12{gap:12px}.w-full{width:100%}.row{display:flex}.ai-center{align-items:center}.gap-16{gap:16px}.g-auto-220{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }] });
97
100
  }
98
101
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SelectQuickConfigDialogComponent, decorators: [{
99
102
  type: Component,
@@ -137,6 +140,283 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
137
140
  args: [MAT_DIALOG_DATA]
138
141
  }] }] });
139
142
 
143
+ class StepperNestedSettingsDialogComponent {
144
+ data;
145
+ dialogRef;
146
+ applied = new EventEmitter();
147
+ contentHost;
148
+ isDirty = false;
149
+ isValid = true;
150
+ isBusy = false;
151
+ contentRef;
152
+ destroyRef = inject(DestroyRef);
153
+ injector = inject(Injector);
154
+ cdr = inject(ChangeDetectorRef);
155
+ constructor(data, dialogRef) {
156
+ this.data = data;
157
+ this.dialogRef = dialogRef;
158
+ }
159
+ get canApply() {
160
+ return this.isDirty && this.isValid && !this.isBusy;
161
+ }
162
+ get canSave() {
163
+ return this.isDirty && this.isValid && !this.isBusy;
164
+ }
165
+ ngAfterViewInit() {
166
+ const childInjector = Injector.create({
167
+ parent: this.injector,
168
+ providers: [
169
+ {
170
+ provide: SETTINGS_PANEL_DATA,
171
+ useValue: {
172
+ id: this.data.id,
173
+ ...(this.data.inputs ?? {}),
174
+ },
175
+ },
176
+ ],
177
+ });
178
+ this.contentRef = this.contentHost.createComponent(this.data.component, {
179
+ injector: childInjector,
180
+ });
181
+ this.applyInputs(this.contentRef, this.data.inputs);
182
+ this.bindState(this.contentRef.instance);
183
+ this.contentRef.changeDetectorRef.detectChanges();
184
+ this.cdr.detectChanges();
185
+ }
186
+ onApply() {
187
+ if (!this.canApply) {
188
+ return;
189
+ }
190
+ this.applied.emit(this.getNestedValue());
191
+ }
192
+ onSave() {
193
+ if (!this.canSave) {
194
+ return;
195
+ }
196
+ const instance = this.contentRef?.instance;
197
+ const result = instance?.onSave?.();
198
+ if (isObservable(result)) {
199
+ firstValueFrom(result)
200
+ .then((value) => this.closeWithValue(value))
201
+ .catch(() => { });
202
+ return;
203
+ }
204
+ if (result instanceof Promise) {
205
+ result.then((value) => this.closeWithValue(value)).catch(() => { });
206
+ return;
207
+ }
208
+ this.closeWithValue(result !== undefined ? result : this.getNestedValue());
209
+ }
210
+ onReset() {
211
+ this.contentRef?.instance?.reset?.();
212
+ }
213
+ onCancel() {
214
+ this.dialogRef.close(undefined);
215
+ }
216
+ getNestedValue() {
217
+ return this.contentRef?.instance?.getSettingsValue?.();
218
+ }
219
+ closeWithValue(value) {
220
+ this.dialogRef.close({ action: 'save', value });
221
+ }
222
+ bindState(instance) {
223
+ instance.isDirty$
224
+ ?.pipe(takeUntilDestroyed(this.destroyRef))
225
+ .subscribe((dirty) => {
226
+ this.isDirty = dirty;
227
+ this.cdr.markForCheck();
228
+ });
229
+ instance.isValid$
230
+ ?.pipe(takeUntilDestroyed(this.destroyRef))
231
+ .subscribe((valid) => {
232
+ this.isValid = valid;
233
+ this.cdr.markForCheck();
234
+ });
235
+ instance.isBusy$
236
+ ?.pipe(takeUntilDestroyed(this.destroyRef))
237
+ .subscribe((busy) => {
238
+ this.isBusy = busy;
239
+ this.cdr.markForCheck();
240
+ });
241
+ const selected = instance?.selected;
242
+ if (selected && typeof selected.subscribe === 'function') {
243
+ const selected$ = typeof selected.pipe === 'function'
244
+ ? selected.pipe(takeUntilDestroyed(this.destroyRef))
245
+ : selected;
246
+ selected$.subscribe((value) => this.applied.emit(value));
247
+ }
248
+ }
249
+ applyInputs(contentRef, inputs) {
250
+ if (!inputs) {
251
+ return;
252
+ }
253
+ const supportedInputs = this.resolveSupportedInputs(contentRef);
254
+ for (const [key, value] of Object.entries(inputs)) {
255
+ if (supportedInputs && !supportedInputs.has(key)) {
256
+ continue;
257
+ }
258
+ try {
259
+ contentRef.setInput(key, value);
260
+ }
261
+ catch {
262
+ contentRef.instance[key] = value;
263
+ }
264
+ }
265
+ }
266
+ resolveSupportedInputs(contentRef) {
267
+ const declaredInputs = contentRef.componentType?.ɵcmp?.inputs;
268
+ if (!declaredInputs || typeof declaredInputs !== 'object') {
269
+ return null;
270
+ }
271
+ return new Set(Object.keys(declaredInputs));
272
+ }
273
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: StepperNestedSettingsDialogComponent, deps: [{ token: MAT_DIALOG_DATA }, { token: i1.MatDialogRef }], target: i0.ɵɵFactoryTarget.Component });
274
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: StepperNestedSettingsDialogComponent, isStandalone: true, selector: "praxis-stepper-nested-settings-dialog", outputs: { applied: "applied" }, viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
275
+ <section class="pdx-stepper-nested-dialog" data-testid="stepper-nested-settings-dialog">
276
+ <header class="pdx-stepper-nested-dialog__header">
277
+ <div>
278
+ <p class="pdx-stepper-nested-dialog__eyebrow">Nested component editor</p>
279
+ <h2>{{ data.title }}</h2>
280
+ </div>
281
+ <button
282
+ mat-icon-button
283
+ type="button"
284
+ data-testid="stepper-nested-settings-cancel"
285
+ aria-label="Close nested editor"
286
+ (click)="onCancel()">
287
+ <mat-icon [praxisIcon]="'close'"></mat-icon>
288
+ </button>
289
+ </header>
290
+
291
+ <div class="pdx-stepper-nested-dialog__status" [class.invalid]="!isValid">
292
+ <mat-progress-spinner
293
+ *ngIf="isBusy"
294
+ diameter="18"
295
+ mode="indeterminate">
296
+ </mat-progress-spinner>
297
+ <span *ngIf="isBusy">Operation in progress...</span>
298
+ <span *ngIf="!isBusy && !isValid">Review the highlighted fields before applying.</span>
299
+ <span *ngIf="!isBusy && isValid && isDirty">Ready to apply nested changes.</span>
300
+ <span *ngIf="!isBusy && isValid && !isDirty">No nested changes yet.</span>
301
+ </div>
302
+
303
+ <div class="pdx-stepper-nested-dialog__body">
304
+ <ng-container #contentHost></ng-container>
305
+ </div>
306
+
307
+ <footer class="pdx-stepper-nested-dialog__footer">
308
+ <button
309
+ mat-button
310
+ type="button"
311
+ data-testid="stepper-nested-settings-reset"
312
+ [disabled]="isBusy"
313
+ (click)="onReset()">
314
+ Reset
315
+ </button>
316
+ <span class="pdx-stepper-nested-dialog__spacer"></span>
317
+ <button
318
+ mat-stroked-button
319
+ type="button"
320
+ data-testid="stepper-nested-settings-apply"
321
+ [disabled]="!canApply"
322
+ (click)="onApply()">
323
+ Apply
324
+ </button>
325
+ <button
326
+ mat-flat-button
327
+ color="primary"
328
+ type="button"
329
+ data-testid="stepper-nested-settings-save"
330
+ [disabled]="!canSave"
331
+ (click)="onSave()">
332
+ Save
333
+ </button>
334
+ </footer>
335
+ </section>
336
+ `, isInline: true, styles: [":host{display:block}.pdx-stepper-nested-dialog{--pdx-nested-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #1f2630) 92%, black);background:var(--pdx-nested-surface);color:var(--md-sys-color-on-surface, #eef2ff);min-width:min(760px,88vw);max-width:92vw;max-height:86vh;display:grid;grid-template-rows:auto auto minmax(0,1fr) auto;overflow:hidden}.pdx-stepper-nested-dialog__header,.pdx-stepper-nested-dialog__footer{display:flex;align-items:center;gap:12px;padding:16px 20px;border-color:color-mix(in srgb,var(--md-sys-color-outline-variant, #4b5563) 48%,transparent)}.pdx-stepper-nested-dialog__header{justify-content:space-between;border-bottom:1px solid}.pdx-stepper-nested-dialog__header h2{margin:0;font-size:18px;font-weight:800;letter-spacing:-.01em}.pdx-stepper-nested-dialog__eyebrow{margin:0 0 4px;color:var(--md-sys-color-primary, #8ab4ff);font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase}.pdx-stepper-nested-dialog__status{min-height:34px;display:flex;align-items:center;gap:8px;padding:8px 20px;color:var(--md-sys-color-on-surface-variant, #c7d2fe);background:color-mix(in srgb,var(--md-sys-color-primary-container, #1d3a5f) 18%,transparent);font-size:12px}.pdx-stepper-nested-dialog__status.invalid{color:var(--md-sys-color-error, #ffb4ab);background:color-mix(in srgb,var(--md-sys-color-error-container, #5f1b1b) 18%,transparent)}.pdx-stepper-nested-dialog__body{min-height:0;overflow:auto;padding:16px 20px 22px}.pdx-stepper-nested-dialog__footer{border-top:1px solid}.pdx-stepper-nested-dialog__spacer{flex:1 1 auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i5$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
337
+ }
338
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: StepperNestedSettingsDialogComponent, decorators: [{
339
+ type: Component,
340
+ args: [{ selector: 'praxis-stepper-nested-settings-dialog', standalone: true, imports: [
341
+ CommonModule,
342
+ MatButtonModule,
343
+ MatDialogModule,
344
+ MatIconModule,
345
+ MatProgressSpinnerModule,
346
+ PraxisIconDirective,
347
+ ], template: `
348
+ <section class="pdx-stepper-nested-dialog" data-testid="stepper-nested-settings-dialog">
349
+ <header class="pdx-stepper-nested-dialog__header">
350
+ <div>
351
+ <p class="pdx-stepper-nested-dialog__eyebrow">Nested component editor</p>
352
+ <h2>{{ data.title }}</h2>
353
+ </div>
354
+ <button
355
+ mat-icon-button
356
+ type="button"
357
+ data-testid="stepper-nested-settings-cancel"
358
+ aria-label="Close nested editor"
359
+ (click)="onCancel()">
360
+ <mat-icon [praxisIcon]="'close'"></mat-icon>
361
+ </button>
362
+ </header>
363
+
364
+ <div class="pdx-stepper-nested-dialog__status" [class.invalid]="!isValid">
365
+ <mat-progress-spinner
366
+ *ngIf="isBusy"
367
+ diameter="18"
368
+ mode="indeterminate">
369
+ </mat-progress-spinner>
370
+ <span *ngIf="isBusy">Operation in progress...</span>
371
+ <span *ngIf="!isBusy && !isValid">Review the highlighted fields before applying.</span>
372
+ <span *ngIf="!isBusy && isValid && isDirty">Ready to apply nested changes.</span>
373
+ <span *ngIf="!isBusy && isValid && !isDirty">No nested changes yet.</span>
374
+ </div>
375
+
376
+ <div class="pdx-stepper-nested-dialog__body">
377
+ <ng-container #contentHost></ng-container>
378
+ </div>
379
+
380
+ <footer class="pdx-stepper-nested-dialog__footer">
381
+ <button
382
+ mat-button
383
+ type="button"
384
+ data-testid="stepper-nested-settings-reset"
385
+ [disabled]="isBusy"
386
+ (click)="onReset()">
387
+ Reset
388
+ </button>
389
+ <span class="pdx-stepper-nested-dialog__spacer"></span>
390
+ <button
391
+ mat-stroked-button
392
+ type="button"
393
+ data-testid="stepper-nested-settings-apply"
394
+ [disabled]="!canApply"
395
+ (click)="onApply()">
396
+ Apply
397
+ </button>
398
+ <button
399
+ mat-flat-button
400
+ color="primary"
401
+ type="button"
402
+ data-testid="stepper-nested-settings-save"
403
+ [disabled]="!canSave"
404
+ (click)="onSave()">
405
+ Save
406
+ </button>
407
+ </footer>
408
+ </section>
409
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.pdx-stepper-nested-dialog{--pdx-nested-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #1f2630) 92%, black);background:var(--pdx-nested-surface);color:var(--md-sys-color-on-surface, #eef2ff);min-width:min(760px,88vw);max-width:92vw;max-height:86vh;display:grid;grid-template-rows:auto auto minmax(0,1fr) auto;overflow:hidden}.pdx-stepper-nested-dialog__header,.pdx-stepper-nested-dialog__footer{display:flex;align-items:center;gap:12px;padding:16px 20px;border-color:color-mix(in srgb,var(--md-sys-color-outline-variant, #4b5563) 48%,transparent)}.pdx-stepper-nested-dialog__header{justify-content:space-between;border-bottom:1px solid}.pdx-stepper-nested-dialog__header h2{margin:0;font-size:18px;font-weight:800;letter-spacing:-.01em}.pdx-stepper-nested-dialog__eyebrow{margin:0 0 4px;color:var(--md-sys-color-primary, #8ab4ff);font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase}.pdx-stepper-nested-dialog__status{min-height:34px;display:flex;align-items:center;gap:8px;padding:8px 20px;color:var(--md-sys-color-on-surface-variant, #c7d2fe);background:color-mix(in srgb,var(--md-sys-color-primary-container, #1d3a5f) 18%,transparent);font-size:12px}.pdx-stepper-nested-dialog__status.invalid{color:var(--md-sys-color-error, #ffb4ab);background:color-mix(in srgb,var(--md-sys-color-error-container, #5f1b1b) 18%,transparent)}.pdx-stepper-nested-dialog__body{min-height:0;overflow:auto;padding:16px 20px 22px}.pdx-stepper-nested-dialog__footer{border-top:1px solid}.pdx-stepper-nested-dialog__spacer{flex:1 1 auto}\n"] }]
410
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
411
+ type: Inject,
412
+ args: [MAT_DIALOG_DATA]
413
+ }] }, { type: i1.MatDialogRef }], propDecorators: { applied: [{
414
+ type: Output
415
+ }], contentHost: [{
416
+ type: ViewChild,
417
+ args: ['contentHost', { read: ViewContainerRef, static: true }]
418
+ }] } });
419
+
140
420
  const PRAXIS_STEPPER_EN_US = {
141
421
  'praxis.stepper.editor.tabs.general': 'General',
142
422
  'praxis.stepper.editor.tabs.steps': 'Steps',
@@ -593,39 +873,53 @@ function resolvePraxisStepperText(i18n, key, fallback) {
593
873
 
594
874
  class PraxisStepperConfigEditor {
595
875
  i18n = inject(PraxisI18nService);
876
+ set stepperConfig(value) {
877
+ if (value == null) {
878
+ return;
879
+ }
880
+ this.initializeConfig(value, false);
881
+ }
596
882
  config = { steps: [], orientation: 'horizontal', headerPosition: 'top', linear: false };
597
883
  activeStepRef = null;
598
884
  // SettingsValueProvider observables
599
885
  isDirty$ = new BehaviorSubject(false);
600
886
  isValid$ = new BehaviorSubject(true);
601
887
  isBusy$ = new BehaviorSubject(false);
602
- settings = inject(SettingsPanelService);
603
888
  dialog = inject(MatDialog);
604
889
  iconPicker = inject(IconPickerService);
605
890
  cdr = inject(ChangeDetectorRef);
606
891
  constructor(data) {
607
892
  let seed = data?.config ?? data;
608
- // Normalize when seed is a JSON string
609
- if (typeof seed === 'string') {
893
+ this.initializeConfig(seed, true);
894
+ }
895
+ initializeConfig(seed, dirtyWhenEmpty) {
896
+ let normalizedSeed = seed;
897
+ if (typeof normalizedSeed === 'string') {
610
898
  try {
611
- seed = JSON.parse(seed);
899
+ normalizedSeed = JSON.parse(normalizedSeed);
612
900
  }
613
901
  catch {
614
- seed = undefined;
902
+ normalizedSeed = undefined;
615
903
  }
616
904
  }
617
- if (seed && typeof seed === 'object') {
618
- // shallow clone to detach references
619
- this.config = JSON.parse(JSON.stringify(seed));
905
+ if (normalizedSeed && typeof normalizedSeed === 'object') {
906
+ this.config = JSON.parse(JSON.stringify(normalizedSeed));
620
907
  if (!Array.isArray(this.config.steps)) {
621
908
  this.config.steps = [];
622
909
  }
910
+ this.isDirty$.next(false);
623
911
  }
624
912
  else {
625
- this.config.steps = [{ id: 'step1', label: this.tx('editor.defaults.newStep', 'New step') }];
626
- this.isDirty$.next(true);
913
+ this.config = {
914
+ steps: [{ id: 'step1', label: this.tx('editor.defaults.newStep', 'New step') }],
915
+ orientation: 'horizontal',
916
+ headerPosition: 'top',
917
+ linear: false,
918
+ };
919
+ this.isDirty$.next(dirtyWhenEmpty);
627
920
  }
628
921
  this.ensureActiveStep();
922
+ this.validate();
629
923
  }
630
924
  tx(key, fallback) {
631
925
  return resolvePraxisStepperText(this.i18n, key, fallback);
@@ -999,18 +1293,26 @@ class PraxisStepperConfigEditor {
999
1293
  const step = this.activeStep;
1000
1294
  if (!step)
1001
1295
  return;
1002
- const ref = this.settings.open({ id: `stepper:form:${step.id || this.activeIndex}`, title: this.tx('editor.dialog.configureStepForm', 'Configure Step Form'), content: { component: PraxisDynamicFormConfigEditor, inputs: { formConfig: step.form?.config || { sections: [] }, formId: step.form?.formId || step.id, mode: step.form?.mode || 'create' } } });
1003
1296
  const apply = (val) => {
1004
1297
  const v = val || {};
1005
1298
  step.form = {
1006
- config: v.formConfig || step.form?.config || { sections: [] },
1299
+ config: v.formConfig || v.config || step.form?.config || { sections: [] },
1007
1300
  formId: v?.formId || step.form?.formId || step.id,
1008
- mode: v?.inputsPatch?.mode || step.form?.mode || 'create',
1301
+ mode: v?.inputsPatch?.mode || v?.bindings?.mode || step.form?.mode || 'create',
1009
1302
  };
1010
1303
  this.markDirty();
1011
1304
  };
1012
- ref.applied$.subscribe(apply);
1013
- ref.saved$.subscribe(apply);
1305
+ this.openNestedSettingsEditor({
1306
+ id: `stepper:form:${step.id || this.activeIndex}`,
1307
+ title: this.tx('editor.dialog.configureStepForm', 'Configure Step Form'),
1308
+ component: PraxisDynamicFormConfigEditor,
1309
+ inputs: {
1310
+ formConfig: step.form?.config || { sections: [] },
1311
+ formId: step.form?.formId || step.id,
1312
+ mode: step.form?.mode || 'create',
1313
+ },
1314
+ apply,
1315
+ });
1014
1316
  }
1015
1317
  editMainForm() { this.addMainForm(); }
1016
1318
  removeMainForm() { const s = this.activeStep; if (!s)
@@ -1023,16 +1325,27 @@ class PraxisStepperConfigEditor {
1023
1325
  const current = { ...(this.ensureWidgets().find(w => w.id === 'praxis-list')?.inputs?.config || {}) };
1024
1326
  // Ensure minimal selection setup
1025
1327
  current.selection = current.selection || { mode: 'single', return: 'value' };
1026
- const ref = this.settings.open({ id: `stepper:list:${step.id || this.activeIndex}`, title: this.tx('editor.dialog.configureListSelection', 'Configure List (selection)'), content: { component: PraxisListConfigEditor, inputs: { config: current, listId: step.id || 'list' } } });
1328
+ let targetWidget;
1329
+ const listId = `${step.id || 'step'}-list-${Date.now()}`;
1027
1330
  const apply = (val) => {
1028
1331
  const cfg = (val && (val.config || val)) || current;
1029
- const wd = { id: 'praxis-list', inputs: { config: cfg, listId: `${step.id || 'step'}-list-${Date.now()}` } };
1030
1332
  const arr = this.ensureWidgets();
1031
- arr.push(wd);
1333
+ if (!targetWidget) {
1334
+ targetWidget = { id: 'praxis-list', inputs: { config: cfg, listId } };
1335
+ arr.push(targetWidget);
1336
+ }
1337
+ else {
1338
+ targetWidget.inputs = { ...(targetWidget.inputs || {}), config: cfg, listId };
1339
+ }
1032
1340
  this.markDirty();
1033
1341
  };
1034
- ref.applied$.subscribe(apply);
1035
- ref.saved$.subscribe(apply);
1342
+ this.openNestedSettingsEditor({
1343
+ id: `stepper:list:${step.id || this.activeIndex}`,
1344
+ title: this.tx('editor.dialog.configureListSelection', 'Configure List (selection)'),
1345
+ component: PraxisListConfigEditor,
1346
+ inputs: { config: current, listId: step.id || 'list' },
1347
+ apply,
1348
+ });
1036
1349
  }
1037
1350
  addSearchableSelect() {
1038
1351
  const step = this.activeStep;
@@ -1062,14 +1375,26 @@ class PraxisStepperConfigEditor {
1062
1375
  const step = this.activeStep;
1063
1376
  if (!step)
1064
1377
  return;
1065
- const ref = this.settings.open({ id: `stepper:upload:${step.id || this.activeIndex}`, title: this.tx('editor.dialog.configureFilesUpload', 'Configure File Upload'), content: { component: PraxisFilesUploadConfigEditor, inputs: {} } });
1378
+ let targetWidget;
1379
+ const filesUploadId = `${step.id || 'step'}-upload-${Date.now()}`;
1066
1380
  const apply = (cfg) => {
1067
- const wd = { id: 'praxis-files-upload', inputs: { config: cfg, filesUploadId: `${step.id || 'step'}-upload-${Date.now()}` } };
1068
- this.ensureWidgets().push(wd);
1381
+ const arr = this.ensureWidgets();
1382
+ if (!targetWidget) {
1383
+ targetWidget = { id: 'praxis-files-upload', inputs: { config: cfg, filesUploadId } };
1384
+ arr.push(targetWidget);
1385
+ }
1386
+ else {
1387
+ targetWidget.inputs = { ...(targetWidget.inputs || {}), config: cfg, filesUploadId };
1388
+ }
1069
1389
  this.markDirty();
1070
1390
  };
1071
- ref.applied$.subscribe(apply);
1072
- ref.saved$.subscribe(apply);
1391
+ this.openNestedSettingsEditor({
1392
+ id: `stepper:upload:${step.id || this.activeIndex}`,
1393
+ title: this.tx('editor.dialog.configureFilesUpload', 'Configure File Upload'),
1394
+ component: PraxisFilesUploadConfigEditor,
1395
+ inputs: {},
1396
+ apply,
1397
+ });
1073
1398
  }
1074
1399
  openMoreComponents() {
1075
1400
  const dlg = this.dialog.open(ComponentPaletteDialogComponent, { width: '560px', data: { title: this.tx('editor.dialog.addComponentToStep', 'Add component to step'), allowedWidgetIds: ['praxis-dynamic-form', 'praxis-list', 'pdx-material-searchable-select', 'praxis-files-upload'] } });
@@ -1116,14 +1441,18 @@ class PraxisStepperConfigEditor {
1116
1441
  { rows: [[{ name: 'items', label: this.tx('editor.defaults.items', 'Items'), controlType: 'TRANSFER_LIST', transferOptions: { source: [], target: [] } }]] }
1117
1442
  ]
1118
1443
  };
1119
- const ref = this.settings.open({ id: `stepper:transfer:${step.id || this.activeIndex}`, title: this.tx('editor.dialog.configureTransfer', 'Configure Item Transfer'), content: { component: PraxisDynamicFormConfigEditor, inputs: { formConfig, formId: step.id || 'transfer' } } });
1120
1444
  const apply = (val) => {
1121
1445
  const v = val || {};
1122
- step.form = { config: v.formConfig || formConfig, formId: v?.formId || step.id, mode: v?.inputsPatch?.mode || 'create' };
1446
+ step.form = { config: v.formConfig || v.config || formConfig, formId: v?.formId || step.id, mode: v?.inputsPatch?.mode || v?.bindings?.mode || 'create' };
1123
1447
  this.markDirty();
1124
1448
  };
1125
- ref.applied$.subscribe(apply);
1126
- ref.saved$.subscribe(apply);
1449
+ this.openNestedSettingsEditor({
1450
+ id: `stepper:transfer:${step.id || this.activeIndex}`,
1451
+ title: this.tx('editor.dialog.configureTransfer', 'Configure Item Transfer'),
1452
+ component: PraxisDynamicFormConfigEditor,
1453
+ inputs: { formConfig, formId: step.id || 'transfer' },
1454
+ apply,
1455
+ });
1127
1456
  }
1128
1457
  editWidget(w) {
1129
1458
  switch (w.id) {
@@ -1136,14 +1465,18 @@ class PraxisStepperConfigEditor {
1136
1465
  const step = this.activeStep;
1137
1466
  if (!step)
1138
1467
  return;
1139
- const ref = this.settings.open({ id: `stepper:list:${step.id || this.activeIndex}`, title: this.tx('editor.dialog.configureListSelection', 'Configure List (selection)'), content: { component: PraxisListConfigEditor, inputs: { config: cfg, listId: step.id || 'list' } } });
1140
1468
  const apply = (val) => {
1141
1469
  const next = (val && (val.config || val)) || cfg;
1142
1470
  w.inputs = { ...(w.inputs || {}), config: next };
1143
1471
  this.markDirty();
1144
1472
  };
1145
- ref.applied$.subscribe(apply);
1146
- ref.saved$.subscribe(apply);
1473
+ this.openNestedSettingsEditor({
1474
+ id: `stepper:list:${step.id || this.activeIndex}`,
1475
+ title: this.tx('editor.dialog.configureListSelection', 'Configure List (selection)'),
1476
+ component: PraxisListConfigEditor,
1477
+ inputs: { config: cfg, listId: step.id || 'list' },
1478
+ apply,
1479
+ });
1147
1480
  break;
1148
1481
  }
1149
1482
  case 'pdx-material-searchable-select': {
@@ -1160,10 +1493,14 @@ class PraxisStepperConfigEditor {
1160
1493
  }
1161
1494
  case 'praxis-files-upload': {
1162
1495
  const cfg = w.inputs?.config || {};
1163
- const ref = this.settings.open({ id: `stepper:upload:edit`, title: this.tx('editor.dialog.configureFilesUpload', 'Configure File Upload'), content: { component: PraxisFilesUploadConfigEditor, inputs: { ...cfg } } });
1164
1496
  const apply = (val) => { w.inputs = { ...(w.inputs || {}), config: val }; this.markDirty(); };
1165
- ref.applied$.subscribe(apply);
1166
- ref.saved$.subscribe(apply);
1497
+ this.openNestedSettingsEditor({
1498
+ id: `stepper:upload:edit`,
1499
+ title: this.tx('editor.dialog.configureFilesUpload', 'Configure File Upload'),
1500
+ component: PraxisFilesUploadConfigEditor,
1501
+ inputs: { ...cfg },
1502
+ apply,
1503
+ });
1167
1504
  break;
1168
1505
  }
1169
1506
  default:
@@ -1171,6 +1508,32 @@ class PraxisStepperConfigEditor {
1171
1508
  break;
1172
1509
  }
1173
1510
  }
1511
+ openNestedSettingsEditor(options) {
1512
+ const dialogRef = this.dialog.open(StepperNestedSettingsDialogComponent, {
1513
+ width: 'min(960px, 92vw)',
1514
+ maxWidth: '96vw',
1515
+ maxHeight: '92vh',
1516
+ autoFocus: false,
1517
+ restoreFocus: false,
1518
+ data: {
1519
+ id: options.id,
1520
+ title: options.title,
1521
+ component: options.component,
1522
+ inputs: options.inputs,
1523
+ },
1524
+ });
1525
+ dialogRef.componentInstance?.applied.subscribe((value) => {
1526
+ options.apply(value);
1527
+ this.cdr.markForCheck();
1528
+ });
1529
+ dialogRef.afterClosed().subscribe((result) => {
1530
+ if (result?.action !== 'save') {
1531
+ return;
1532
+ }
1533
+ options.apply(result.value);
1534
+ this.cdr.markForCheck();
1535
+ });
1536
+ }
1174
1537
  // SettingsValueProvider
1175
1538
  getSettingsValue() { return this.config; }
1176
1539
  onSave() { return this.getSettingsValue(); }
@@ -1186,7 +1549,7 @@ class PraxisStepperConfigEditor {
1186
1549
  }
1187
1550
  }
1188
1551
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisStepperConfigEditor, deps: [{ token: SETTINGS_PANEL_DATA }], target: i0.ɵɵFactoryTarget.Component });
1189
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisStepperConfigEditor, isStandalone: true, selector: "praxis-stepper-config-editor", providers: [providePraxisStepperI18n()], ngImport: i0, template: `
1552
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisStepperConfigEditor, isStandalone: true, selector: "praxis-stepper-config-editor", inputs: { stepperConfig: "stepperConfig" }, providers: [providePraxisStepperI18n()], ngImport: i0, template: `
1190
1553
  <div class="pdx-editor">
1191
1554
  <mat-tab-group>
1192
1555
  <mat-tab [label]="tx('editor.tabs.general', 'General')">
@@ -1780,7 +2143,7 @@ class PraxisStepperConfigEditor {
1780
2143
  </mat-tab>
1781
2144
  </mat-tab-group>
1782
2145
  </div>
1783
- `, isInline: true, styles: [".pdx-editor{display:grid;gap:16px}.pdx-editor .mat-mdc-form-field{width:100%}.pdx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.pdx-toggles{display:flex;gap:12px;flex-wrap:wrap}.tab-pad{padding:16px;display:grid;gap:16px;background:var(--md-sys-color-surface);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 78%,transparent);border-radius:16px}.help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.editor-intro{display:flex;justify-content:space-between;gap:16px;align-items:flex-start;padding:4px 2px 2px}.editor-intro-copy{display:grid;gap:6px;max-width:760px}.eyebrow{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant);font-weight:600}.editor-title{font-size:22px;line-height:1.15;font-weight:650;color:var(--md-sys-color-on-surface)}.editor-subtitle{max-width:72ch;color:var(--md-sys-color-on-surface-variant);line-height:1.45;font-size:14px}.editor-intro-meta{display:flex;gap:8px;align-items:flex-start}.meta-pill{display:grid;gap:2px;min-width:88px;padding:10px 12px;border-radius:14px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 70%,transparent);background:var(--md-sys-color-surface-container-low)}.meta-label{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--md-sys-color-on-surface-variant)}.quick-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:6px}.icons-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin:8px 0}.icon-item{display:grid;gap:6px;align-content:start}.icons-hint{margin-top:4px}.settings-group{display:grid;gap:14px;padding:2px 0 4px}.settings-group+.settings-group,.settings-group+.disclosure,.disclosure+.settings-group{padding-top:18px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 60%,transparent)}.group-head{display:grid;gap:4px;max-width:72ch}.inline-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;align-items:start}.inline-action-item{display:grid;gap:8px;align-content:start;min-width:0}.pdx-steps{display:grid;gap:12px;padding:4px 0 2px}.steps-workspace{display:grid;grid-template-columns:minmax(260px,320px) minmax(0,1fr);gap:24px;align-items:start}.pdx-steps-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.pdx-step-list{display:grid;gap:10px}.pdx-step-item{display:grid;grid-template-columns:20px 1fr auto;gap:10px;align-items:start;padding:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 68%,transparent);border-radius:14px;background:var(--md-sys-color-surface);cursor:pointer;outline:none}.pdx-step-item.active{border-color:color-mix(in srgb,var(--md-sys-color-primary) 55%,var(--md-sys-color-outline-variant));background:color-mix(in srgb,var(--md-sys-color-primary-container) 26%,var(--md-sys-color-surface));box-shadow:none}.pdx-step-item:focus-visible{border-color:var(--md-sys-color-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--md-sys-color-primary) 22%,transparent)}.drag-handle{display:flex;align-items:center;color:var(--md-sys-color-on-surface-variant)}.step-summary{display:grid;gap:8px;min-width:0}.step-summary-head{display:grid;gap:2px}.step-summary-index{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--md-sys-color-on-surface-variant);font-weight:600}.step-summary-title{font-size:15px;line-height:1.3;font-weight:600;color:var(--md-sys-color-on-surface)}.step-summary-sub{font-size:12px;color:var(--md-sys-color-on-surface-variant);font-family:var(--md-ref-typeface-plain, monospace);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.step-summary-flags{display:flex;gap:6px;flex-wrap:wrap}.summary-chip{display:inline-flex;align-items:center;min-height:22px;padding:0 8px;border-radius:999px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:600}.summary-chip-warn{background:color-mix(in srgb,var(--md-sys-color-error-container) 60%,transparent);color:var(--md-sys-color-on-error-container)}.pdx-active-step{display:grid;gap:18px;min-width:0}.pdx-active-step .hdr{display:flex;gap:12px;align-items:flex-start;justify-content:space-between}.active-step-head{display:grid;gap:4px}.pdx-content-editor{display:grid;gap:16px}.section{display:grid;gap:12px;padding:0;border:none;border-radius:0;background:transparent}.section-body{display:grid;gap:12px}.section-title{font-weight:600;font-size:15px;color:var(--md-sys-color-on-surface)}.section-subtitle{font-size:13px;color:var(--md-sys-color-on-surface-variant);line-height:1.45}.section-head{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap}.editorial-section{padding-bottom:6px;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 68%,transparent)}.detail-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.field-span-2{grid-column:span 2}.toggle-group{display:flex;gap:10px;flex-wrap:wrap;padding-top:4px}.disclosure{padding:0 0 12px;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 62%,transparent)}.disclosure summary{list-style:none;display:flex;justify-content:space-between;align-items:baseline;gap:16px;cursor:pointer;padding:0;position:relative}.disclosure summary::-webkit-details-marker{display:none}.disclosure summary:after{content:\"expand_more\";font-family:Material Icons;font-size:18px;color:var(--md-sys-color-on-surface-variant);transition:transform .16s ease}.disclosure[open] summary:after{transform:rotate(180deg)}.disclosure summary:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 58%,transparent);outline-offset:4px;border-radius:8px}.disclosure-body{padding-top:12px}.widget-list{display:grid;gap:8px}.widget-item{display:grid;grid-template-columns:20px 1fr auto;align-items:start;gap:12px;padding:10px 0;border:none;border-radius:0;background:transparent;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}.widget-item .info{display:grid;gap:2px;min-width:0}.widget-item .info .name{font-weight:600}.widget-item .info .sub{font-size:12px;color:var(--md-sys-color-on-surface-variant);line-height:1.45}.widget-item .info .meta{margin-top:2px;font-size:11px;color:var(--md-sys-color-on-surface-variant);font-family:var(--md-ref-typeface-plain, monospace);opacity:.85}.widget-item .actions{display:flex;gap:2px;flex-wrap:nowrap;justify-content:flex-end;align-items:flex-start;opacity:.92}.editorial-item{align-items:start}.editorial-item .actions{justify-content:flex-end}.cta-row{display:flex;gap:8px;align-items:center}.spacer{flex:1 1 auto}.muted{color:var(--md-sys-color-on-surface-variant)}.cta-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px;margin-bottom:4px}.cta-row-item{display:grid;gap:10px;padding:12px 0;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}.tokens-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin:8px 0}.token-item{display:contents}.pdx-appearance{margin-top:8px;display:grid;gap:8px}.pdx-appearance-actions{display:flex;gap:8px;flex-wrap:wrap}.zone-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px}.zone-column{display:grid;gap:10px;min-width:0}.zone-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.zone-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}.zone-title{font-weight:600}.zone-column+.zone-column{padding-left:16px;border-left:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}.zone-count{font-size:12px;padding:2px 8px;border-radius:999px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface)}.advanced-list{margin-top:4px}.form-row-card{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}.form-row-main{display:flex;align-items:center;gap:12px;min-width:0}.form-row-actions{display:flex;gap:4px;flex-wrap:wrap;justify-content:flex-end}.code-card{margin-top:8px}.code-head{font-weight:600;margin-bottom:4px}.code{white-space:pre;overflow:auto;background:var(--md-sys-color-surface-container-highest)}.w-full{grid-column:1 / -1}.min-180{min-width:180px}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}.cta-row-item .cta-head{display:grid;grid-template-columns:20px 1fr;align-items:start;gap:10px}.cta-row-item .cta-title{font-weight:600;color:var(--md-sys-color-on-surface)}.cta-row-item .cta-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant);line-height:1.45;margin-top:2px}.cta-row-item .cta-actions{display:flex;gap:8px;align-items:center}.widget-item:last-child,.cta-row-item:last-child,.form-row-card:last-child{border-bottom:none}@media(max-width:900px){.editor-intro{flex-direction:column}.steps-workspace{grid-template-columns:1fr;gap:20px}.pdx-step-item{grid-template-columns:20px 1fr}.pdx-actions{grid-column:1 / -1;justify-self:end}.widget-item{grid-template-columns:20px 1fr}.widget-item .actions{grid-column:1 / -1;justify-content:flex-end}.form-row-card{flex-direction:column;align-items:flex-start}.form-row-actions{justify-content:flex-start}.zone-column+.zone-column{padding-left:0;border-left:none;padding-top:12px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}}@media(max-width:640px){.tab-pad{padding:14px}.editor-title{font-size:20px}.pdx-active-step .hdr{align-items:flex-start;flex-direction:column}.detail-grid{grid-template-columns:1fr}.field-span-2{grid-column:auto}.toggle-group{flex-direction:column;align-items:flex-start}.zone-head{align-items:flex-start;flex-direction:column}.zone-actions,.widget-item .actions{justify-content:flex-start}.disclosure summary{flex-direction:column;align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i8.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i8.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i8.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i10.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i10.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }] });
2146
+ `, isInline: true, styles: [".pdx-editor{display:grid;gap:16px}.pdx-editor .mat-mdc-form-field{width:100%}.pdx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.pdx-toggles{display:flex;gap:12px;flex-wrap:wrap}.tab-pad{padding:16px;display:grid;gap:16px;background:var(--md-sys-color-surface);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 78%,transparent);border-radius:16px}.help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.editor-intro{display:flex;justify-content:space-between;gap:16px;align-items:flex-start;padding:4px 2px 2px}.editor-intro-copy{display:grid;gap:6px;max-width:760px}.eyebrow{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant);font-weight:600}.editor-title{font-size:22px;line-height:1.15;font-weight:650;color:var(--md-sys-color-on-surface)}.editor-subtitle{max-width:72ch;color:var(--md-sys-color-on-surface-variant);line-height:1.45;font-size:14px}.editor-intro-meta{display:flex;gap:8px;align-items:flex-start}.meta-pill{display:grid;gap:2px;min-width:88px;padding:10px 12px;border-radius:14px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 70%,transparent);background:var(--md-sys-color-surface-container-low)}.meta-label{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--md-sys-color-on-surface-variant)}.quick-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:6px}.icons-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin:8px 0}.icon-item{display:grid;gap:6px;align-content:start}.icons-hint{margin-top:4px}.settings-group{display:grid;gap:14px;padding:2px 0 4px}.settings-group+.settings-group,.settings-group+.disclosure,.disclosure+.settings-group{padding-top:18px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 60%,transparent)}.group-head{display:grid;gap:4px;max-width:72ch}.inline-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;align-items:start}.inline-action-item{display:grid;gap:8px;align-content:start;min-width:0}.pdx-steps{display:grid;gap:12px;padding:4px 0 2px}.steps-workspace{display:grid;grid-template-columns:minmax(260px,320px) minmax(0,1fr);gap:24px;align-items:start}.pdx-steps-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.pdx-step-list{display:grid;gap:10px}.pdx-step-item{display:grid;grid-template-columns:20px 1fr auto;gap:10px;align-items:start;padding:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 68%,transparent);border-radius:14px;background:var(--md-sys-color-surface);cursor:pointer;outline:none}.pdx-step-item.active{border-color:color-mix(in srgb,var(--md-sys-color-primary) 55%,var(--md-sys-color-outline-variant));background:color-mix(in srgb,var(--md-sys-color-primary-container) 26%,var(--md-sys-color-surface));box-shadow:none}.pdx-step-item:focus-visible{border-color:var(--md-sys-color-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--md-sys-color-primary) 22%,transparent)}.drag-handle{display:flex;align-items:center;color:var(--md-sys-color-on-surface-variant)}.step-summary{display:grid;gap:8px;min-width:0}.step-summary-head{display:grid;gap:2px}.step-summary-index{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--md-sys-color-on-surface-variant);font-weight:600}.step-summary-title{font-size:15px;line-height:1.3;font-weight:600;color:var(--md-sys-color-on-surface)}.step-summary-sub{font-size:12px;color:var(--md-sys-color-on-surface-variant);font-family:var(--md-ref-typeface-plain, monospace);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.step-summary-flags{display:flex;gap:6px;flex-wrap:wrap}.summary-chip{display:inline-flex;align-items:center;min-height:22px;padding:0 8px;border-radius:999px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:600}.summary-chip-warn{background:color-mix(in srgb,var(--md-sys-color-error-container) 60%,transparent);color:var(--md-sys-color-on-error-container)}.pdx-active-step{display:grid;gap:18px;min-width:0}.pdx-active-step .hdr{display:flex;gap:12px;align-items:flex-start;justify-content:space-between}.active-step-head{display:grid;gap:4px}.pdx-content-editor{display:grid;gap:16px}.section{display:grid;gap:12px;padding:0;border:none;border-radius:0;background:transparent}.section-body{display:grid;gap:12px}.section-title{font-weight:600;font-size:15px;color:var(--md-sys-color-on-surface)}.section-subtitle{font-size:13px;color:var(--md-sys-color-on-surface-variant);line-height:1.45}.section-head{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap}.editorial-section{padding-bottom:6px;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 68%,transparent)}.detail-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.field-span-2{grid-column:span 2}.toggle-group{display:flex;gap:10px;flex-wrap:wrap;padding-top:4px}.disclosure{padding:0 0 12px;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 62%,transparent)}.disclosure summary{list-style:none;display:flex;justify-content:space-between;align-items:baseline;gap:16px;cursor:pointer;padding:0;position:relative}.disclosure summary::-webkit-details-marker{display:none}.disclosure summary:after{content:\"expand_more\";font-family:Material Icons;font-size:18px;color:var(--md-sys-color-on-surface-variant);transition:transform .16s ease}.disclosure[open] summary:after{transform:rotate(180deg)}.disclosure summary:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 58%,transparent);outline-offset:4px;border-radius:8px}.disclosure-body{padding-top:12px}.widget-list{display:grid;gap:8px}.widget-item{display:grid;grid-template-columns:20px 1fr auto;align-items:start;gap:12px;padding:10px 0;border:none;border-radius:0;background:transparent;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}.widget-item .info{display:grid;gap:2px;min-width:0}.widget-item .info .name{font-weight:600}.widget-item .info .sub{font-size:12px;color:var(--md-sys-color-on-surface-variant);line-height:1.45}.widget-item .info .meta{margin-top:2px;font-size:11px;color:var(--md-sys-color-on-surface-variant);font-family:var(--md-ref-typeface-plain, monospace);opacity:.85}.widget-item .actions{display:flex;gap:2px;flex-wrap:nowrap;justify-content:flex-end;align-items:flex-start;opacity:.92}.editorial-item{align-items:start}.editorial-item .actions{justify-content:flex-end}.cta-row{display:flex;gap:8px;align-items:center}.spacer{flex:1 1 auto}.muted{color:var(--md-sys-color-on-surface-variant)}.cta-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px;margin-bottom:4px}.cta-row-item{display:grid;gap:10px;padding:12px 0;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}.tokens-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin:8px 0}.token-item{display:contents}.pdx-appearance{margin-top:8px;display:grid;gap:8px}.pdx-appearance-actions{display:flex;gap:8px;flex-wrap:wrap}.zone-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px}.zone-column{display:grid;gap:10px;min-width:0}.zone-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.zone-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}.zone-title{font-weight:600}.zone-column+.zone-column{padding-left:16px;border-left:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}.zone-count{font-size:12px;padding:2px 8px;border-radius:999px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface)}.advanced-list{margin-top:4px}.form-row-card{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}.form-row-main{display:flex;align-items:center;gap:12px;min-width:0}.form-row-actions{display:flex;gap:4px;flex-wrap:wrap;justify-content:flex-end}.code-card{margin-top:8px}.code-head{font-weight:600;margin-bottom:4px}.code{white-space:pre;overflow:auto;background:var(--md-sys-color-surface-container-highest)}.w-full{grid-column:1 / -1}.min-180{min-width:180px}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}.cta-row-item .cta-head{display:grid;grid-template-columns:20px 1fr;align-items:start;gap:10px}.cta-row-item .cta-title{font-weight:600;color:var(--md-sys-color-on-surface)}.cta-row-item .cta-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant);line-height:1.45;margin-top:2px}.cta-row-item .cta-actions{display:flex;gap:8px;align-items:center}.widget-item:last-child,.cta-row-item:last-child,.form-row-card:last-child{border-bottom:none}@media(max-width:900px){.editor-intro{flex-direction:column}.steps-workspace{grid-template-columns:1fr;gap:20px}.pdx-step-item{grid-template-columns:20px 1fr}.pdx-actions{grid-column:1 / -1;justify-self:end}.widget-item{grid-template-columns:20px 1fr}.widget-item .actions{grid-column:1 / -1;justify-content:flex-end}.form-row-card{flex-direction:column;align-items:flex-start}.form-row-actions{justify-content:flex-start}.zone-column+.zone-column{padding-left:0;border-left:none;padding-top:12px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 56%,transparent)}}@media(max-width:640px){.tab-pad{padding:14px}.editor-title{font-size:20px}.pdx-active-step .hdr{align-items:flex-start;flex-direction:column}.detail-grid{grid-template-columns:1fr}.field-span-2{grid-column:auto}.toggle-group{flex-direction:column;align-items:flex-start}.zone-head{align-items:flex-start;flex-direction:column}.zone-actions,.widget-item .actions{justify-content:flex-start}.disclosure summary{flex-direction:column;align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i8.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i8.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i8.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i10.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i10.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }] });
1784
2147
  }
1785
2148
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisStepperConfigEditor, decorators: [{
1786
2149
  type: Component,
@@ -2395,7 +2758,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2395
2758
  }], ctorParameters: () => [{ type: undefined, decorators: [{
2396
2759
  type: Inject,
2397
2760
  args: [SETTINGS_PANEL_DATA]
2398
- }] }] });
2761
+ }] }], propDecorators: { stepperConfig: [{
2762
+ type: Input
2763
+ }] } });
2399
2764
 
2400
2765
  /**
2401
2766
  * Capabilities catalog for Praxis Stepper (StepperMetadata).
@@ -2531,6 +2896,8 @@ const STEPPER_AI_CAPABILITIES = {
2531
2896
 
2532
2897
  class StepperAiAdapter extends BaseAiAdapter {
2533
2898
  stepper;
2899
+ componentId = 'praxis-stepper';
2900
+ componentType = 'stepper';
2534
2901
  componentName = 'Praxis Stepper';
2535
2902
  constructor(stepper) {
2536
2903
  super();
@@ -2554,6 +2921,43 @@ class StepperAiAdapter extends BaseAiAdapter {
2554
2921
  hasForms: steps.some((s) => !!s.form),
2555
2922
  };
2556
2923
  }
2924
+ getDataProfile() {
2925
+ const cfg = this.getCurrentConfig();
2926
+ const steps = cfg.steps || [];
2927
+ return {
2928
+ stepperId: this.stepper.stepperId,
2929
+ stepCount: steps.length,
2930
+ formStepCount: steps.filter((step) => !!step.form).length,
2931
+ widgetStepCount: steps.filter((step) => Array.isArray(step.widgets) && step.widgets.length > 0).length,
2932
+ richContentStepCount: steps.filter((step) => (this.hasRichContent(step.stepBlocksBeforeForm)
2933
+ || this.hasRichContent(step.stepBlocksAfterForm))).length,
2934
+ linear: !!cfg.linear,
2935
+ orientation: cfg.orientation || 'horizontal',
2936
+ };
2937
+ }
2938
+ getSchemaFields() {
2939
+ return (this.getCurrentConfig().steps || []).map((step, index) => ({
2940
+ name: step.id || step.label || `step-${index + 1}`,
2941
+ label: step.label || step.id || `Etapa ${index + 1}`,
2942
+ optional: !!step.optional,
2943
+ hasForm: !!step.form,
2944
+ hasWidgets: Array.isArray(step.widgets) && step.widgets.length > 0,
2945
+ }));
2946
+ }
2947
+ getAuthoringContext() {
2948
+ return {
2949
+ authoringManifestRef: 'PRAXIS_STEPPER_AUTHORING_MANIFEST',
2950
+ runtimeAuthoringPolicy: {
2951
+ mode: 'agentic-authoring',
2952
+ enableCustomization: !!this.stepper.enableCustomization,
2953
+ canApplyLocalPatch: false,
2954
+ reason: 'praxis-stepper exige componentEditPlan validado pelo manifesto antes de aplicar mudancas runtime.',
2955
+ },
2956
+ domainCatalog: {
2957
+ recommendedAuthoringFlow: 'component_authoring',
2958
+ },
2959
+ };
2960
+ }
2557
2961
  createSnapshot() {
2558
2962
  return this.getCurrentConfig();
2559
2963
  }
@@ -2612,6 +3016,283 @@ class StepperAiAdapter extends BaseAiAdapter {
2612
3016
  return JSON.parse(JSON.stringify(config));
2613
3017
  }
2614
3018
  }
3019
+ hasRichContent(document) {
3020
+ return Array.isArray(document?.nodes) && document.nodes.length > 0;
3021
+ }
3022
+ }
3023
+
3024
+ class StepperAgenticAuthoringTurnFlow {
3025
+ adapter;
3026
+ aiApi;
3027
+ mode = 'agentic-authoring';
3028
+ constructor(adapter, aiApi) {
3029
+ this.adapter = adapter;
3030
+ this.aiApi = aiApi;
3031
+ }
3032
+ async submit(request) {
3033
+ const prompt = (request.prompt ?? '').trim();
3034
+ if (!prompt)
3035
+ return { state: 'listening', phase: 'capture', statusText: '' };
3036
+ const componentId = this.adapter.componentId || request.componentId || 'praxis-stepper';
3037
+ const componentType = this.adapter.componentType || request.componentType || 'stepper';
3038
+ const currentState = this.toAiJsonObject(this.adapter.getCurrentConfig());
3039
+ const dataProfile = this.optionalJsonObject(this.adapter.getDataProfile?.());
3040
+ const runtimeState = this.optionalJsonObject(this.adapter.getRuntimeState?.());
3041
+ const schemaFields = this.adapter.getSchemaFields?.()
3042
+ ?.map((field) => this.toAiJsonObject(field))
3043
+ .filter((field) => Object.keys(field).length > 0);
3044
+ const contextHints = this.optionalJsonObject(this.adapter.getAuthoringContext?.());
3045
+ if (this.shouldRouteToGovernedDecision(prompt, contextHints)) {
3046
+ return this.toGovernedDecisionHandoff(prompt, request);
3047
+ }
3048
+ const response = await firstValueFrom(this.aiApi.getPatch({
3049
+ componentId,
3050
+ componentType,
3051
+ userPrompt: prompt,
3052
+ sessionId: request.sessionId,
3053
+ clientTurnId: request.clientTurnId,
3054
+ messages: this.toChatMessages(request.messages, prompt),
3055
+ currentState,
3056
+ currentStateDigest: this.buildCurrentStateDigest(dataProfile),
3057
+ uiContextRef: { componentId, componentType },
3058
+ ...(dataProfile ? { dataProfile } : {}),
3059
+ ...(runtimeState ? { runtimeState } : {}),
3060
+ ...(schemaFields?.length ? { schemaFields } : {}),
3061
+ ...(contextHints ? { contextHints } : {}),
3062
+ }));
3063
+ return this.toTurnResult(this.compileAdapterResponse(response), request);
3064
+ }
3065
+ async apply(_request) {
3066
+ return {
3067
+ state: 'error',
3068
+ phase: 'apply',
3069
+ assistantMessage: 'O stepper ainda exige componentEditPlan validado pelo manifesto antes de aplicar mudancas locais.',
3070
+ errorText: 'Aplicacao local bloqueada ate existir compilacao manifest-backed para praxis-stepper.',
3071
+ canApply: false,
3072
+ pendingPatch: null,
3073
+ };
3074
+ }
3075
+ cancel() {
3076
+ return Promise.resolve({
3077
+ state: 'listening',
3078
+ phase: 'capture',
3079
+ assistantMessage: 'Solicitacao cancelada.',
3080
+ statusText: '',
3081
+ canApply: false,
3082
+ pendingPatch: null,
3083
+ pendingClarification: null,
3084
+ });
3085
+ }
3086
+ retry(request) {
3087
+ const lastPrompt = [...(request.messages ?? [])].reverse()
3088
+ .find((message) => message.role === 'user')?.text;
3089
+ return this.submit({ ...request, prompt: lastPrompt ?? request.prompt, action: { kind: 'retry' } });
3090
+ }
3091
+ toTurnResult(response, request) {
3092
+ if (!response) {
3093
+ return { state: 'error', phase: 'capture', assistantMessage: 'Resposta vazia da IA.', errorText: 'Resposta vazia da IA.' };
3094
+ }
3095
+ if (response.type === 'clarification') {
3096
+ return {
3097
+ state: 'clarification',
3098
+ phase: 'clarify',
3099
+ sessionId: response.sessionId ?? request.sessionId,
3100
+ assistantMessage: response.message || 'Preciso de mais detalhes para continuar.',
3101
+ clarificationQuestions: this.toClarificationQuestions(response),
3102
+ quickReplies: this.toQuickReplies(response),
3103
+ canApply: false,
3104
+ };
3105
+ }
3106
+ if (response.type === 'info') {
3107
+ const message = response.message || response.explanation || 'Informacao gerada.';
3108
+ return { state: 'success', phase: 'summarize', sessionId: response.sessionId ?? request.sessionId, assistantMessage: message, statusText: message, canApply: false };
3109
+ }
3110
+ if (response.type === 'error') {
3111
+ const message = response.message || 'Falha ao gerar alteracao de stepper.';
3112
+ return {
3113
+ state: 'error',
3114
+ phase: 'capture',
3115
+ sessionId: response.sessionId ?? request.sessionId,
3116
+ assistantMessage: message,
3117
+ errorText: message,
3118
+ diagnostics: response.warnings?.length ? { warnings: response.warnings } : undefined,
3119
+ };
3120
+ }
3121
+ if (response.patch && Object.keys(response.patch).length > 0) {
3122
+ return {
3123
+ state: 'error',
3124
+ phase: 'review',
3125
+ sessionId: response.sessionId ?? request.sessionId,
3126
+ assistantMessage: 'O stepper rejeitou patch livre. Gere um componentEditPlan validado pelo PRAXIS_STEPPER_AUTHORING_MANIFEST antes de propor alteracao local.',
3127
+ errorText: 'Patch livre de stepper rejeitado.',
3128
+ canApply: false,
3129
+ pendingPatch: null,
3130
+ diagnostics: {
3131
+ warnings: [
3132
+ 'free-stepper-patch-rejected',
3133
+ 'Use componentEditPlan validado contra PRAXIS_STEPPER_AUTHORING_MANIFEST.',
3134
+ ],
3135
+ },
3136
+ };
3137
+ }
3138
+ return {
3139
+ state: 'success',
3140
+ phase: 'summarize',
3141
+ sessionId: response.sessionId ?? request.sessionId,
3142
+ assistantMessage: response.message || response.explanation || 'Nenhuma alteracao necessaria.',
3143
+ statusText: response.message || response.explanation || 'Nenhuma alteracao necessaria.',
3144
+ canApply: false,
3145
+ };
3146
+ }
3147
+ compileAdapterResponse(response) {
3148
+ const compiled = this.adapter.compileAiResponse?.(response);
3149
+ if (!compiled)
3150
+ return response;
3151
+ if (compiled.type === 'error') {
3152
+ return {
3153
+ type: 'error',
3154
+ message: compiled.message || 'O componentEditPlan do stepper nao passou na validacao de capacidades.',
3155
+ warnings: compiled.warnings,
3156
+ };
3157
+ }
3158
+ const warnings = [...(response.warnings ?? []), ...(compiled.warnings ?? [])];
3159
+ return { ...response, ...compiled, patch: compiled.patch, warnings: warnings.length ? warnings : undefined };
3160
+ }
3161
+ toChatMessages(messages, prompt) {
3162
+ const supported = (messages ?? [])
3163
+ .filter((message) => message.role === 'user' || message.role === 'assistant' || message.role === 'system')
3164
+ .map((message) => ({ role: message.role, content: message.text }))
3165
+ .filter((message) => message.content.trim().length > 0);
3166
+ return supported.length ? supported : [{ role: 'user', content: prompt }];
3167
+ }
3168
+ toClarificationQuestions(response) {
3169
+ const labels = response.questions?.length
3170
+ ? response.questions
3171
+ : response.message ? [response.message] : ['Qual ajuste voce quer aplicar no stepper?'];
3172
+ const options = this.toQuickReplies(response).map((reply) => ({ id: reply.id, label: reply.label, value: reply.prompt }));
3173
+ return labels.map((label, index) => ({
3174
+ id: `stepper-clarification-${index + 1}`,
3175
+ type: options.length ? 'single-choice' : 'text',
3176
+ label,
3177
+ allowCustom: true,
3178
+ options,
3179
+ }));
3180
+ }
3181
+ toQuickReplies(response) {
3182
+ const payloads = response.optionPayloads ?? [];
3183
+ if (payloads.length) {
3184
+ return payloads.map((option, index) => {
3185
+ const label = option.label?.trim() || option.value?.trim() || `Opcao ${index + 1}`;
3186
+ const prompt = option.example?.trim() || option.value?.trim() || label;
3187
+ return { id: `option-${index + 1}`, label, prompt, kind: 'clarification-option' };
3188
+ });
3189
+ }
3190
+ return (response.options ?? [])
3191
+ .filter((option) => !!option?.trim())
3192
+ .map((option, index) => ({ id: `option-${index + 1}`, label: option.trim(), prompt: option.trim(), kind: 'clarification-option' }));
3193
+ }
3194
+ buildCurrentStateDigest(dataProfile) {
3195
+ const stepCount = typeof dataProfile?.['stepCount'] === 'number' ? dataProfile['stepCount'] : undefined;
3196
+ return stepCount !== undefined ? { rowCount: stepCount } : {};
3197
+ }
3198
+ shouldRouteToGovernedDecision(prompt, contextHints) {
3199
+ const normalized = prompt.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
3200
+ const recommendedFlow = this.toRecord(contextHints?.['domainCatalog'])?.['recommendedAuthoringFlow'];
3201
+ if (recommendedFlow === 'shared_rule_authoring')
3202
+ return true;
3203
+ return [
3204
+ 'regra',
3205
+ 'politica',
3206
+ 'policy',
3207
+ 'compliance',
3208
+ 'lgpd',
3209
+ 'privacidade',
3210
+ 'aprovacao',
3211
+ 'aprovar',
3212
+ 'publicar',
3213
+ 'materializar',
3214
+ 'enforcement',
3215
+ 'validacao de negocio',
3216
+ 'validar negocio',
3217
+ 'elegibilidade',
3218
+ 'permissao',
3219
+ 'acesso',
3220
+ ].some((term) => normalized.includes(term));
3221
+ }
3222
+ toGovernedDecisionHandoff(prompt, request) {
3223
+ const message = 'Esse pedido parece alterar uma decisao de negocio compartilhada. O stepper pode localizar a etapa e a experiencia afetada, mas a regra deve seguir pelo fluxo governado de domain-rules antes de qualquer materializacao runtime.';
3224
+ return {
3225
+ state: 'clarification',
3226
+ phase: 'clarify',
3227
+ sessionId: request.sessionId,
3228
+ assistantMessage: message,
3229
+ statusText: 'Handoff governado necessario.',
3230
+ canApply: false,
3231
+ quickReplies: [
3232
+ {
3233
+ id: 'shared-rule-handoff',
3234
+ label: 'Continuar como regra governada',
3235
+ prompt,
3236
+ kind: 'shared-rule-handoff',
3237
+ description: 'Criar intake de domain-rules em vez de aplicar patch local no stepper.',
3238
+ icon: 'rule',
3239
+ tone: 'warning',
3240
+ contextHints: {
3241
+ flowId: 'shared_rule_authoring',
3242
+ source: 'praxis-stepper',
3243
+ recommendedAction: 'domain-rules/intake',
3244
+ },
3245
+ },
3246
+ ],
3247
+ clarificationQuestions: [
3248
+ {
3249
+ id: 'stepper-governed-rule-confirmation',
3250
+ type: 'confirm',
3251
+ label: 'Deseja continuar pelo fluxo governado de regras compartilhadas?',
3252
+ description: 'Esse caminho permite intake, simulacao, aprovacao/publicacao, materializacao e validacao de enforcement.',
3253
+ required: true,
3254
+ options: [
3255
+ {
3256
+ id: 'shared-rule-handoff',
3257
+ label: 'Sim, continuar governado',
3258
+ value: prompt,
3259
+ description: 'Nao aplicar como patch local do stepper.',
3260
+ contextHints: { flowId: 'shared_rule_authoring', source: 'praxis-stepper' },
3261
+ },
3262
+ ],
3263
+ },
3264
+ ],
3265
+ diagnostics: {
3266
+ governedDecisionHandoff: {
3267
+ flowId: 'shared_rule_authoring',
3268
+ sourcePrompt: prompt,
3269
+ sourceComponent: 'praxis-stepper',
3270
+ },
3271
+ },
3272
+ };
3273
+ }
3274
+ optionalJsonObject(value) {
3275
+ if (value === undefined || value === null)
3276
+ return undefined;
3277
+ const object = this.toAiJsonObject(value);
3278
+ return Object.keys(object).length ? object : undefined;
3279
+ }
3280
+ toAiJsonObject(value) {
3281
+ const record = this.toRecord(value);
3282
+ if (!record)
3283
+ return {};
3284
+ try {
3285
+ return JSON.parse(JSON.stringify(record));
3286
+ }
3287
+ catch {
3288
+ return {};
3289
+ }
3290
+ }
3291
+ toRecord(value) {
3292
+ return value && typeof value === 'object' && !Array.isArray(value)
3293
+ ? value
3294
+ : null;
3295
+ }
2615
3296
  }
2616
3297
 
2617
3298
  class PraxisStepper {
@@ -2685,10 +3366,37 @@ class PraxisStepper {
2685
3366
  catch {
2686
3367
  return undefined;
2687
3368
  } })();
3369
+ cdr = inject(ChangeDetectorRef);
3370
+ aiApi = inject(AiBackendApiService);
3371
+ assistantSessions = inject(PraxisAssistantSessionRegistryService);
3372
+ aiTurnOrchestrator = inject(PraxisAssistantTurnOrchestratorService);
3373
+ aiAssistantSessionEffect = effect(() => {
3374
+ const session = this.assistantSessions.activeSession();
3375
+ if (!session || session.id !== this.resolveAiAssistantSessionId())
3376
+ return;
3377
+ if (!this.aiAssistantOpen) {
3378
+ this.openAiAssistantFromSession(session);
3379
+ }
3380
+ }, ...(ngDevMode ? [{ debugName: "aiAssistantSessionEffect" }] : []));
2688
3381
  warnedMissingId = false;
2689
3382
  emptyStepRichContentDocument = createEmptyRichContentDocument();
2690
3383
  animationDone = new EventEmitter();
2691
3384
  aiAdapter = new StepperAiAdapter(this);
3385
+ aiAssistantOpen = false;
3386
+ aiAssistantPrompt = '';
3387
+ aiAssistantViewState = null;
3388
+ aiAssistantLayout = createPraxisAssistantViewportLayout();
3389
+ aiAssistantLabels = {
3390
+ title: 'Copiloto semantico Praxis',
3391
+ subtitle: 'Converse, revise e governe ajustes do stepper.',
3392
+ prompt: 'Mensagem',
3393
+ promptPlaceholder: 'Descreva o ajuste que voce precisa no fluxo.',
3394
+ emptyConversation: 'Diga o que voce quer alterar no stepper.',
3395
+ submit: 'Interpretar pedido',
3396
+ apply: 'Aplicar ajuste',
3397
+ };
3398
+ aiAssistantController = null;
3399
+ aiAssistantStateSubscription = null;
2692
3400
  // Computed getters
2693
3401
  steps = computed(() => this._config()?.steps || [], ...(ngDevMode ? [{ debugName: "steps" }] : []));
2694
3402
  orientation = computed(() => this._config()?.orientation || 'horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
@@ -2879,6 +3587,306 @@ class PraxisStepper {
2879
3587
  this.persistConfig(cloned);
2880
3588
  this.scheduleDomSync();
2881
3589
  }
3590
+ openAiAssistant() {
3591
+ this.initializeAiAssistantController();
3592
+ this.aiAssistantOpen = true;
3593
+ this.aiAssistantController?.setContextItems(this.buildAiAssistantContextItems());
3594
+ this.syncAiAssistantSession('active');
3595
+ this.cdr.markForCheck();
3596
+ }
3597
+ openAiAssistantFromSession(session) {
3598
+ if (session.id !== this.resolveAiAssistantSessionId())
3599
+ return;
3600
+ this.initializeAiAssistantController();
3601
+ this.aiAssistantOpen = true;
3602
+ this.aiAssistantController?.setContextItems(this.buildAiAssistantContextItems());
3603
+ this.syncAiAssistantSession('active');
3604
+ this.cdr.markForCheck();
3605
+ }
3606
+ closeAiAssistant() {
3607
+ this.aiAssistantOpen = false;
3608
+ this.syncAiAssistantSession('minimized');
3609
+ this.cdr.markForCheck();
3610
+ }
3611
+ onAiAssistantPromptChange(prompt) {
3612
+ this.aiAssistantPrompt = prompt;
3613
+ this.syncAiAssistantSession();
3614
+ }
3615
+ onAiAssistantSubmit(prompt) {
3616
+ this.aiAssistantController?.submitPrompt(prompt).subscribe((state) => {
3617
+ this.aiAssistantPrompt = '';
3618
+ this.aiAssistantViewState = state;
3619
+ this.syncAiAssistantSession();
3620
+ this.cdr.markForCheck();
3621
+ });
3622
+ }
3623
+ onAiAssistantApply() {
3624
+ this.aiAssistantController?.apply().subscribe((state) => {
3625
+ this.aiAssistantViewState = state;
3626
+ this.syncAiAssistantSession();
3627
+ this.cdr.markForCheck();
3628
+ });
3629
+ }
3630
+ onAiAssistantRetry() {
3631
+ this.aiAssistantController?.retry().subscribe((state) => {
3632
+ this.aiAssistantViewState = state;
3633
+ this.syncAiAssistantSession();
3634
+ this.cdr.markForCheck();
3635
+ });
3636
+ }
3637
+ onAiAssistantCancel() {
3638
+ this.aiAssistantController?.cancel().subscribe((state) => {
3639
+ this.aiAssistantPrompt = '';
3640
+ this.aiAssistantViewState = state;
3641
+ this.syncAiAssistantSession();
3642
+ this.cdr.markForCheck();
3643
+ });
3644
+ }
3645
+ onAiAssistantQuickReply(reply) {
3646
+ const controller = this.aiAssistantController;
3647
+ if (!controller)
3648
+ return;
3649
+ const state = controller.snapshot();
3650
+ const next$ = state.state === 'clarification'
3651
+ ? controller.answerClarification(reply.prompt)
3652
+ : controller.submitPrompt(reply.prompt, {
3653
+ kind: reply.kind || 'quick-reply',
3654
+ id: reply.id,
3655
+ value: reply.prompt,
3656
+ });
3657
+ next$.subscribe((nextState) => {
3658
+ this.aiAssistantPrompt = '';
3659
+ this.aiAssistantViewState = nextState;
3660
+ this.syncAiAssistantSession();
3661
+ this.cdr.markForCheck();
3662
+ });
3663
+ }
3664
+ onAiAssistantEditMessage(message) {
3665
+ this.aiAssistantPrompt = message.text;
3666
+ this.cdr.markForCheck();
3667
+ }
3668
+ onAiAssistantResendMessage(message) {
3669
+ this.aiAssistantController?.resendMessage(message.id).subscribe((state) => {
3670
+ this.aiAssistantPrompt = '';
3671
+ this.aiAssistantViewState = state;
3672
+ this.syncAiAssistantSession();
3673
+ this.cdr.markForCheck();
3674
+ });
3675
+ }
3676
+ onAiAssistantLayoutChange(layout) {
3677
+ this.aiAssistantLayout = layout;
3678
+ }
3679
+ buildAiAssistantContextItems() {
3680
+ const steps = this.steps();
3681
+ const items = [
3682
+ {
3683
+ id: 'component',
3684
+ label: 'Componente',
3685
+ value: 'Stepper',
3686
+ kind: 'component',
3687
+ icon: 'view_timeline',
3688
+ },
3689
+ {
3690
+ id: 'stepper-id',
3691
+ label: 'Stepper',
3692
+ value: this.safeAiAssistantStepperId(),
3693
+ kind: 'custom',
3694
+ icon: 'tag',
3695
+ },
3696
+ {
3697
+ id: 'steps',
3698
+ label: 'Etapas',
3699
+ value: String(steps.length),
3700
+ kind: 'custom',
3701
+ icon: 'format_list_numbered',
3702
+ },
3703
+ ];
3704
+ const selected = steps[this.selectedIndexComputed()];
3705
+ if (selected) {
3706
+ items.push({
3707
+ id: 'selected-step',
3708
+ label: 'Etapa ativa',
3709
+ value: selected.label || selected.id || String(this.selectedIndexComputed()),
3710
+ kind: 'custom',
3711
+ icon: 'ads_click',
3712
+ });
3713
+ }
3714
+ return items;
3715
+ }
3716
+ initializeAiAssistantController() {
3717
+ if (this.aiAssistantController)
3718
+ return;
3719
+ const flow = new StepperAgenticAuthoringTurnFlow(this.aiAdapter, this.aiApi);
3720
+ const controller = this.aiTurnOrchestrator.createController(flow, {
3721
+ componentId: this.aiAdapter.componentId || 'praxis-stepper',
3722
+ componentType: this.aiAdapter.componentType || 'stepper',
3723
+ contextItems: this.buildAiAssistantContextItems(),
3724
+ });
3725
+ this.aiAssistantController = controller;
3726
+ this.aiAssistantViewState = controller.snapshot();
3727
+ this.aiAssistantStateSubscription?.unsubscribe();
3728
+ this.aiAssistantStateSubscription = controller.state$.subscribe((state) => {
3729
+ this.aiAssistantViewState = state;
3730
+ this.syncAiAssistantSession();
3731
+ this.cdr.markForCheck();
3732
+ });
3733
+ this.cdr.markForCheck();
3734
+ }
3735
+ buildAiAssistantContextSnapshot() {
3736
+ const counts = this.collectAiAssistantCounts();
3737
+ const stepNames = this.collectAiAssistantStepNames();
3738
+ return {
3739
+ identity: {
3740
+ sessionId: this.resolveAiAssistantSessionId(),
3741
+ ownerId: this.resolveAiAssistantOwnerId(),
3742
+ ownerType: 'stepper',
3743
+ componentId: 'praxis-stepper',
3744
+ componentType: 'stepper',
3745
+ routeKey: this.resolveAiAssistantRouteKey(),
3746
+ },
3747
+ target: {
3748
+ kind: 'component',
3749
+ id: this.resolveAiAssistantOwnerId(),
3750
+ label: this.safeAiAssistantStepperId(),
3751
+ metadata: {
3752
+ stepperId: this.safeAiAssistantStepperId(),
3753
+ hasCustomization: !!this.enableCustomization,
3754
+ },
3755
+ },
3756
+ contextItems: this.buildAiAssistantContextItems().map((item) => ({
3757
+ id: item.id,
3758
+ label: item.label,
3759
+ value: item.value || '',
3760
+ kind: item.kind,
3761
+ })),
3762
+ mode: 'agentic-authoring',
3763
+ authoringManifestRef: {
3764
+ componentId: 'praxis-stepper',
3765
+ source: 'PRAXIS_STEPPER_AUTHORING_MANIFEST',
3766
+ },
3767
+ schemaFields: stepNames.length ? stepNames : undefined,
3768
+ dataProfileDigest: {
3769
+ summary: `${counts.stepCount} etapa(s), ${counts.formStepCount} formulario(s), ${counts.widgetStepCount} grupo(s) de widget(s)`,
3770
+ counts,
3771
+ },
3772
+ runtimeStateDigest: {
3773
+ summary: `Stepper ${this.linear() ? 'linear' : 'nao linear'}, etapa ativa ${this.selectedIndexComputed() + 1}`,
3774
+ fields: [
3775
+ 'steps',
3776
+ 'selectedIndex',
3777
+ 'linear',
3778
+ 'orientation',
3779
+ ],
3780
+ },
3781
+ capabilityRefs: [
3782
+ {
3783
+ id: 'stepper.component-edit-plan',
3784
+ label: 'Plano de edicao de stepper',
3785
+ source: 'PRAXIS_STEPPER_AUTHORING_MANIFEST',
3786
+ risk: 'medium',
3787
+ },
3788
+ ],
3789
+ governanceHints: [
3790
+ {
3791
+ kind: 'business-rule-boundary',
3792
+ label: 'Regras compartilhadas exigem governanca',
3793
+ reason: 'Politicas de acesso, validacoes reutilizaveis e compliance nao devem ser aplicadas como patch local do stepper.',
3794
+ risk: 'high',
3795
+ },
3796
+ ],
3797
+ };
3798
+ }
3799
+ syncAiAssistantSession(visibility = null) {
3800
+ if (!this.enableCustomization)
3801
+ return;
3802
+ if (!this.aiAssistantOpen && !this.hasAiAssistantSessionState() && visibility !== 'minimized')
3803
+ return;
3804
+ const state = this.aiAssistantViewState;
3805
+ this.assistantSessions.upsertContextSession(this.buildAiAssistantContextSnapshot(), {
3806
+ title: 'Copiloto semantico Praxis',
3807
+ summary: this.resolveAiAssistantSummary(),
3808
+ mode: state?.mode || 'agentic-authoring',
3809
+ state: state?.state || 'idle',
3810
+ visibility: visibility ?? (this.aiAssistantOpen ? 'active' : 'minimized'),
3811
+ badge: this.resolveAiAssistantBadge(),
3812
+ icon: this.resolveAiAssistantIcon(),
3813
+ });
3814
+ }
3815
+ hasAiAssistantSessionState() {
3816
+ return !!this.aiAssistantPrompt.trim()
3817
+ || !!this.aiAssistantViewState?.messages?.length
3818
+ || !!this.aiAssistantViewState?.quickReplies?.length
3819
+ || !!this.aiAssistantViewState?.pendingPatch
3820
+ || !!this.aiAssistantViewState?.statusText?.trim()
3821
+ || !!this.aiAssistantViewState?.errorText?.trim();
3822
+ }
3823
+ resolveAiAssistantSessionId() {
3824
+ return `stepper:${this.resolveAiAssistantRouteKey()}:${this.resolveAiAssistantOwnerId()}`;
3825
+ }
3826
+ resolveAiAssistantOwnerId() {
3827
+ return (this.componentInstanceId || this.safeAiAssistantStepperId() || 'stepper').trim() || 'stepper';
3828
+ }
3829
+ safeAiAssistantStepperId() {
3830
+ return String(this.stepperId || '').trim();
3831
+ }
3832
+ resolveAiAssistantRouteKey() {
3833
+ const routePath = this.route?.snapshot?.routeConfig?.path?.trim();
3834
+ return routePath || 'local';
3835
+ }
3836
+ resolveAiAssistantSummary() {
3837
+ const status = this.aiAssistantViewState?.statusText?.trim();
3838
+ if (status)
3839
+ return status;
3840
+ const error = this.aiAssistantViewState?.errorText?.trim();
3841
+ if (error)
3842
+ return error;
3843
+ const prompt = this.aiAssistantPrompt.trim();
3844
+ if (prompt)
3845
+ return prompt.length > 96 ? `${prompt.slice(0, 93)}...` : prompt;
3846
+ const lastMessage = [...(this.aiAssistantViewState?.messages ?? [])].reverse()
3847
+ .find((message) => message.role === 'assistant' || message.role === 'user');
3848
+ if (lastMessage?.text) {
3849
+ return lastMessage.text.length > 96 ? `${lastMessage.text.slice(0, 93)}...` : lastMessage.text;
3850
+ }
3851
+ return 'Assistente contextual do stepper.';
3852
+ }
3853
+ resolveAiAssistantBadge() {
3854
+ const state = this.aiAssistantViewState?.state;
3855
+ if (state === 'error')
3856
+ return 'erro';
3857
+ if (state === 'clarification')
3858
+ return 'revisar';
3859
+ if (state === 'review')
3860
+ return 'preview';
3861
+ if (state === 'success')
3862
+ return 'ok';
3863
+ return undefined;
3864
+ }
3865
+ resolveAiAssistantIcon() {
3866
+ const state = this.aiAssistantViewState?.state;
3867
+ if (state === 'error')
3868
+ return 'error';
3869
+ if (state === 'clarification')
3870
+ return 'rule';
3871
+ if (state === 'review')
3872
+ return 'rate_review';
3873
+ return 'auto_awesome';
3874
+ }
3875
+ collectAiAssistantStepNames() {
3876
+ return Array.from(new Set(this.steps()
3877
+ .map((step, index) => step.id || step.label || `step-${index + 1}`)
3878
+ .filter((name) => typeof name === 'string' && !!name.trim())));
3879
+ }
3880
+ collectAiAssistantCounts() {
3881
+ const steps = this.steps();
3882
+ return {
3883
+ stepCount: steps.length,
3884
+ formStepCount: steps.filter((step) => !!step.form).length,
3885
+ widgetStepCount: steps.filter((step) => Array.isArray(step.widgets) && step.widgets.length > 0).length,
3886
+ richContentStepCount: steps.filter((step) => (this.hasRichContent(step.stepBlocksBeforeForm)
3887
+ || this.hasRichContent(step.stepBlocksAfterForm))).length,
3888
+ };
3889
+ }
2882
3890
  // CTA helpers
2883
3891
  addFirstStep() {
2884
3892
  const current = this._config() || { steps: [], orientation: 'horizontal', headerPosition: 'top', linear: false };
@@ -2996,6 +4004,10 @@ class PraxisStepper {
2996
4004
  dedupeKey: 'praxis-stepper:missing-stepper-id',
2997
4005
  });
2998
4006
  }
4007
+ ngOnDestroy() {
4008
+ this.assistantSessions.removeContextSession(this.buildAiAssistantContextSnapshot().identity);
4009
+ this.aiAssistantStateSubscription?.unsubscribe();
4010
+ }
2999
4011
  ngOnInit() {
3000
4012
  const key = this.storageKey();
3001
4013
  if (!key)
@@ -3055,8 +4067,45 @@ class PraxisStepper {
3055
4067
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisStepper, deps: [], target: i0.ɵɵFactoryTarget.Component });
3056
4068
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisStepper, isStandalone: true, selector: "praxis-stepper", inputs: { stepperId: "stepperId", componentInstanceId: "componentInstanceId", config: "config", selectedIndexInput: "selectedIndexInput", selectedIndex: "selectedIndex", disableRippleInput: "disableRippleInput", enableCustomization: "enableCustomization", labelPosition: "labelPosition", color: "color", serverValidate: "serverValidate", stepperContext: "stepperContext" }, outputs: { selectedIndexChange: "selectedIndexChange", widgetEvent: "widgetEvent", stepFormReady: "stepFormReady", stepFormValueChange: "stepFormValueChange", animationDone: "animationDone", selectionChange: "selectionChange" }, host: { properties: { "class": "densityClass()" } }, queries: [{ propertyName: "stepLabelTpl", first: true, predicate: ["stepLabelTpl"], descendants: true, read: TemplateRef }], ngImport: i0, template: `
3057
4069
  <div class="stepper-ai-assistant" *ngIf="enableCustomization">
3058
- <praxis-ai-assistant [adapter]="aiAdapter"></praxis-ai-assistant>
4070
+ <button
4071
+ mat-mini-fab
4072
+ color="primary"
4073
+ type="button"
4074
+ class="stepper-ai-assistant-trigger"
4075
+ aria-label="Abrir copiloto semantico Praxis do stepper"
4076
+ data-testid="praxis-stepper-ai-assistant-trigger"
4077
+ (click)="openAiAssistant()"
4078
+ >
4079
+ <mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
4080
+ </button>
3059
4081
  </div>
4082
+ <praxis-ai-assistant-shell
4083
+ *ngIf="aiAssistantOpen && aiAssistantViewState"
4084
+ [mode]="aiAssistantViewState.mode"
4085
+ [state]="aiAssistantViewState.state"
4086
+ [contextItems]="aiAssistantViewState.contextItems"
4087
+ [attachments]="aiAssistantViewState.attachments"
4088
+ [messages]="aiAssistantViewState.messages"
4089
+ [quickReplies]="aiAssistantViewState.quickReplies"
4090
+ [prompt]="aiAssistantPrompt"
4091
+ [statusText]="aiAssistantViewState.statusText"
4092
+ [errorText]="aiAssistantViewState.errorText"
4093
+ [busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
4094
+ [canApply]="aiAssistantViewState.canApply"
4095
+ [labels]="aiAssistantLabels"
4096
+ [layout]="aiAssistantLayout"
4097
+ testIdPrefix="praxis-stepper-ai-assistant"
4098
+ (promptChange)="onAiAssistantPromptChange($event)"
4099
+ (submitPrompt)="onAiAssistantSubmit($event)"
4100
+ (apply)="onAiAssistantApply()"
4101
+ (retryTurn)="onAiAssistantRetry()"
4102
+ (cancelTurn)="onAiAssistantCancel()"
4103
+ (quickReply)="onAiAssistantQuickReply($event)"
4104
+ (editMessage)="onAiAssistantEditMessage($event)"
4105
+ (resendMessage)="onAiAssistantResendMessage($event)"
4106
+ (layoutChange)="onAiAssistantLayoutChange($event)"
4107
+ (close)="closeAiAssistant()"
4108
+ ></praxis-ai-assistant-shell>
3060
4109
  <ng-container *ngIf="steps().length > 0; else emptyState">
3061
4110
  <mat-stepper
3062
4111
  #stepperRoot
@@ -3211,16 +4260,53 @@ class PraxisStepper {
3211
4260
  >
3212
4261
  <mat-icon fontIcon="edit"></mat-icon>
3213
4262
  </button>
3214
- `, isInline: true, styles: [":host{display:block;position:relative}.praxis-stepper{width:100%;background:var(--md-sys-color-surface);border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;padding:12px;box-shadow:var(--mat-elevation-level1)}.praxis-stepper.pdx-stepper-ft-light{background:transparent;border:none;border-radius:0;padding:0;box-shadow:none}:host(.density-compact) ::ng-deep .mat-step-header{min-height:36px}:host(.density-compact) .pdx-step-actions{padding-top:4px;gap:6px}:host(.density-comfortable) ::ng-deep .mat-step-header{min-height:44px}.pdx-step-content{padding:12px 4px 8px;color:var(--md-sys-color-on-surface)}.pdx-step-actions{display:flex;gap:8px;padding-top:8px}::ng-deep .praxis-stepper .mat-step-header{border-radius:999px;margin:4px 0;color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-label{color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-label.mat-step-label-selected{color:var(--md-sys-color-on-surface);font-weight:600}::ng-deep .praxis-stepper .mat-step-header.cdk-keyboard-focused,::ng-deep .praxis-stepper .mat-step-header.cdk-program-focused{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}::ng-deep .praxis-stepper .mat-step-icon{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-icon-selected{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}::ng-deep .praxis-stepper .mat-step-icon-state-done{background:var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary)}::ng-deep .praxis-stepper .mat-step-icon-state-error{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}::ng-deep .praxis-stepper .mat-stepper-horizontal-line{border-top-color:var(--md-sys-color-outline-variant)}::ng-deep .praxis-stepper .mat-stepper-vertical-line:before{border-left-color:var(--md-sys-color-outline-variant)}::ng-deep .pdx-stepper-ft-light .mat-stepper-horizontal{background:transparent;--mat-stepper-header-hover-state-layer-color: color-mix( in srgb, var(--md-sys-color-on-surface) 8%, transparent );--mat-stepper-header-focus-state-layer-color: transparent;--mat-stepper-header-hover-state-layer-shape: 14px;--mat-stepper-header-focus-state-layer-shape: 14px}::ng-deep .pdx-stepper-ft-light .mat-step-header{margin:0}::ng-deep .pdx-stepper-ft-light .mat-step-header .mat-step-header-ripple{display:none}::ng-deep .ft-stepper{background:transparent}.ft-stepper-content{padding:0}.ft-stepper-content .form-section{border:none;padding:0;background:transparent}.nav-align-start{justify-content:flex-start}.nav-align-center{justify-content:center}.nav-align-end{justify-content:flex-end}.nav-align-space-between{justify-content:space-between}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.stepper-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatStepperModule }, { kind: "component", type: i2$1.MatStep, selector: "mat-step", inputs: ["color"], exportAs: ["matStep"] }, { kind: "directive", type: i2$1.MatStepLabel, selector: "[matStepLabel]" }, { kind: "component", type: i2$1.MatStepper, selector: "mat-stepper, mat-vertical-stepper, mat-horizontal-stepper, [matStepper]", inputs: ["disableRipple", "color", "labelPosition", "headerPosition", "animationDuration"], outputs: ["animationDone"], exportAs: ["matStepper", "matVerticalStepper", "matHorizontalStepper"] }, { kind: "directive", type: i2$1.MatStepperIcon, selector: "ng-template[matStepperIcon]", inputs: ["matStepperIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "submitUrl", "submitMethod", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "formId", "componentInstanceId", "layout", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { kind: "component", type: PraxisAiAssistantComponent, selector: "praxis-ai-assistant", inputs: ["adapter", "riskPolicy", "allowManualPatchEdit"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4263
+ `, isInline: true, styles: [":host{display:block;position:relative}.praxis-stepper{width:100%;background:var(--md-sys-color-surface);border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;padding:12px;box-shadow:var(--mat-elevation-level1)}.praxis-stepper.pdx-stepper-ft-light{background:transparent;border:none;border-radius:0;padding:0;box-shadow:none}:host(.density-compact) ::ng-deep .mat-step-header{min-height:36px}:host(.density-compact) .pdx-step-actions{padding-top:4px;gap:6px}:host(.density-comfortable) ::ng-deep .mat-step-header{min-height:44px}.pdx-step-content{padding:12px 4px 8px;color:var(--md-sys-color-on-surface)}.pdx-step-actions{display:flex;gap:8px;padding-top:8px}::ng-deep .praxis-stepper .mat-step-header{border-radius:999px;margin:4px 0;color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-label{color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-label.mat-step-label-selected{color:var(--md-sys-color-on-surface);font-weight:600}::ng-deep .praxis-stepper .mat-step-header.cdk-keyboard-focused,::ng-deep .praxis-stepper .mat-step-header.cdk-program-focused{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}::ng-deep .praxis-stepper .mat-step-icon{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-icon-selected{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}::ng-deep .praxis-stepper .mat-step-icon-state-done{background:var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary)}::ng-deep .praxis-stepper .mat-step-icon-state-error{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}::ng-deep .praxis-stepper .mat-stepper-horizontal-line{border-top-color:var(--md-sys-color-outline-variant)}::ng-deep .praxis-stepper .mat-stepper-vertical-line:before{border-left-color:var(--md-sys-color-outline-variant)}::ng-deep .pdx-stepper-ft-light .mat-stepper-horizontal{background:transparent;--mat-stepper-header-hover-state-layer-color: color-mix( in srgb, var(--md-sys-color-on-surface) 8%, transparent );--mat-stepper-header-focus-state-layer-color: transparent;--mat-stepper-header-hover-state-layer-shape: 14px;--mat-stepper-header-focus-state-layer-shape: 14px}::ng-deep .pdx-stepper-ft-light .mat-step-header{margin:0}::ng-deep .pdx-stepper-ft-light .mat-step-header .mat-step-header-ripple{display:none}::ng-deep .ft-stepper{background:transparent}.ft-stepper-content{padding:0}.ft-stepper-content .form-section{border:none;padding:0;background:transparent}.nav-align-start{justify-content:flex-start}.nav-align-center{justify-content:center}.nav-align-end{justify-content:flex-end}.nav-align-space-between{justify-content:space-between}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.stepper-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.stepper-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2, 0 4px 12px rgba(0, 0, 0, .18))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatStepperModule }, { kind: "component", type: i2$1.MatStep, selector: "mat-step", inputs: ["color"], exportAs: ["matStep"] }, { kind: "directive", type: i2$1.MatStepLabel, selector: "[matStepLabel]" }, { kind: "component", type: i2$1.MatStepper, selector: "mat-stepper, mat-vertical-stepper, mat-horizontal-stepper, [matStepper]", inputs: ["disableRipple", "color", "labelPosition", "headerPosition", "animationDuration"], outputs: ["animationDone"], exportAs: ["matStepper", "matVerticalStepper", "matHorizontalStepper"] }, { kind: "directive", type: i2$1.MatStepperIcon, selector: "ng-template[matStepperIcon]", inputs: ["matStepperIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$1.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "submitUrl", "submitMethod", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "formId", "componentInstanceId", "layout", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "domainRules", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "ruleDiagnosticsChange"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "primaryAction", "secondaryActions", "governanceActions", "busy", "canSubmit", "canApply", "submitOnEnter", "showAttachAction", "enablePastedAttachments", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "retryTurn", "cancelTurn", "shellAction", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "layoutChange"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3215
4264
  }
3216
4265
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisStepper, decorators: [{
3217
4266
  type: Component,
3218
- args: [{ selector: 'praxis-stepper', standalone: true, imports: [CommonModule, ReactiveFormsModule, MatStepperModule, MatButtonModule, MatIconModule, PraxisIconDirective, PraxisDynamicForm, DynamicWidgetLoaderDirective, EmptyStateCardComponent, PraxisAiAssistantComponent, PraxisRichContent], host: {
4267
+ args: [{ selector: 'praxis-stepper', standalone: true, imports: [CommonModule, ReactiveFormsModule, MatStepperModule, MatButtonModule, MatIconModule, PraxisIconDirective, PraxisDynamicForm, DynamicWidgetLoaderDirective, EmptyStateCardComponent, PraxisAiAssistantShellComponent, PraxisRichContent], host: {
3219
4268
  '[class]': 'densityClass()'
3220
4269
  }, template: `
3221
4270
  <div class="stepper-ai-assistant" *ngIf="enableCustomization">
3222
- <praxis-ai-assistant [adapter]="aiAdapter"></praxis-ai-assistant>
4271
+ <button
4272
+ mat-mini-fab
4273
+ color="primary"
4274
+ type="button"
4275
+ class="stepper-ai-assistant-trigger"
4276
+ aria-label="Abrir copiloto semantico Praxis do stepper"
4277
+ data-testid="praxis-stepper-ai-assistant-trigger"
4278
+ (click)="openAiAssistant()"
4279
+ >
4280
+ <mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
4281
+ </button>
3223
4282
  </div>
4283
+ <praxis-ai-assistant-shell
4284
+ *ngIf="aiAssistantOpen && aiAssistantViewState"
4285
+ [mode]="aiAssistantViewState.mode"
4286
+ [state]="aiAssistantViewState.state"
4287
+ [contextItems]="aiAssistantViewState.contextItems"
4288
+ [attachments]="aiAssistantViewState.attachments"
4289
+ [messages]="aiAssistantViewState.messages"
4290
+ [quickReplies]="aiAssistantViewState.quickReplies"
4291
+ [prompt]="aiAssistantPrompt"
4292
+ [statusText]="aiAssistantViewState.statusText"
4293
+ [errorText]="aiAssistantViewState.errorText"
4294
+ [busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
4295
+ [canApply]="aiAssistantViewState.canApply"
4296
+ [labels]="aiAssistantLabels"
4297
+ [layout]="aiAssistantLayout"
4298
+ testIdPrefix="praxis-stepper-ai-assistant"
4299
+ (promptChange)="onAiAssistantPromptChange($event)"
4300
+ (submitPrompt)="onAiAssistantSubmit($event)"
4301
+ (apply)="onAiAssistantApply()"
4302
+ (retryTurn)="onAiAssistantRetry()"
4303
+ (cancelTurn)="onAiAssistantCancel()"
4304
+ (quickReply)="onAiAssistantQuickReply($event)"
4305
+ (editMessage)="onAiAssistantEditMessage($event)"
4306
+ (resendMessage)="onAiAssistantResendMessage($event)"
4307
+ (layoutChange)="onAiAssistantLayoutChange($event)"
4308
+ (close)="closeAiAssistant()"
4309
+ ></praxis-ai-assistant-shell>
3224
4310
  <ng-container *ngIf="steps().length > 0; else emptyState">
3225
4311
  <mat-stepper
3226
4312
  #stepperRoot
@@ -3375,7 +4461,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
3375
4461
  >
3376
4462
  <mat-icon fontIcon="edit"></mat-icon>
3377
4463
  </button>
3378
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;position:relative}.praxis-stepper{width:100%;background:var(--md-sys-color-surface);border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;padding:12px;box-shadow:var(--mat-elevation-level1)}.praxis-stepper.pdx-stepper-ft-light{background:transparent;border:none;border-radius:0;padding:0;box-shadow:none}:host(.density-compact) ::ng-deep .mat-step-header{min-height:36px}:host(.density-compact) .pdx-step-actions{padding-top:4px;gap:6px}:host(.density-comfortable) ::ng-deep .mat-step-header{min-height:44px}.pdx-step-content{padding:12px 4px 8px;color:var(--md-sys-color-on-surface)}.pdx-step-actions{display:flex;gap:8px;padding-top:8px}::ng-deep .praxis-stepper .mat-step-header{border-radius:999px;margin:4px 0;color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-label{color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-label.mat-step-label-selected{color:var(--md-sys-color-on-surface);font-weight:600}::ng-deep .praxis-stepper .mat-step-header.cdk-keyboard-focused,::ng-deep .praxis-stepper .mat-step-header.cdk-program-focused{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}::ng-deep .praxis-stepper .mat-step-icon{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-icon-selected{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}::ng-deep .praxis-stepper .mat-step-icon-state-done{background:var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary)}::ng-deep .praxis-stepper .mat-step-icon-state-error{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}::ng-deep .praxis-stepper .mat-stepper-horizontal-line{border-top-color:var(--md-sys-color-outline-variant)}::ng-deep .praxis-stepper .mat-stepper-vertical-line:before{border-left-color:var(--md-sys-color-outline-variant)}::ng-deep .pdx-stepper-ft-light .mat-stepper-horizontal{background:transparent;--mat-stepper-header-hover-state-layer-color: color-mix( in srgb, var(--md-sys-color-on-surface) 8%, transparent );--mat-stepper-header-focus-state-layer-color: transparent;--mat-stepper-header-hover-state-layer-shape: 14px;--mat-stepper-header-focus-state-layer-shape: 14px}::ng-deep .pdx-stepper-ft-light .mat-step-header{margin:0}::ng-deep .pdx-stepper-ft-light .mat-step-header .mat-step-header-ripple{display:none}::ng-deep .ft-stepper{background:transparent}.ft-stepper-content{padding:0}.ft-stepper-content .form-section{border:none;padding:0;background:transparent}.nav-align-start{justify-content:flex-start}.nav-align-center{justify-content:center}.nav-align-end{justify-content:flex-end}.nav-align-space-between{justify-content:space-between}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.stepper-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}\n"] }]
4464
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;position:relative}.praxis-stepper{width:100%;background:var(--md-sys-color-surface);border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;padding:12px;box-shadow:var(--mat-elevation-level1)}.praxis-stepper.pdx-stepper-ft-light{background:transparent;border:none;border-radius:0;padding:0;box-shadow:none}:host(.density-compact) ::ng-deep .mat-step-header{min-height:36px}:host(.density-compact) .pdx-step-actions{padding-top:4px;gap:6px}:host(.density-comfortable) ::ng-deep .mat-step-header{min-height:44px}.pdx-step-content{padding:12px 4px 8px;color:var(--md-sys-color-on-surface)}.pdx-step-actions{display:flex;gap:8px;padding-top:8px}::ng-deep .praxis-stepper .mat-step-header{border-radius:999px;margin:4px 0;color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-label{color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-label.mat-step-label-selected{color:var(--md-sys-color-on-surface);font-weight:600}::ng-deep .praxis-stepper .mat-step-header.cdk-keyboard-focused,::ng-deep .praxis-stepper .mat-step-header.cdk-program-focused{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}::ng-deep .praxis-stepper .mat-step-icon{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface)}::ng-deep .praxis-stepper .mat-step-icon-selected{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}::ng-deep .praxis-stepper .mat-step-icon-state-done{background:var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary)}::ng-deep .praxis-stepper .mat-step-icon-state-error{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}::ng-deep .praxis-stepper .mat-stepper-horizontal-line{border-top-color:var(--md-sys-color-outline-variant)}::ng-deep .praxis-stepper .mat-stepper-vertical-line:before{border-left-color:var(--md-sys-color-outline-variant)}::ng-deep .pdx-stepper-ft-light .mat-stepper-horizontal{background:transparent;--mat-stepper-header-hover-state-layer-color: color-mix( in srgb, var(--md-sys-color-on-surface) 8%, transparent );--mat-stepper-header-focus-state-layer-color: transparent;--mat-stepper-header-hover-state-layer-shape: 14px;--mat-stepper-header-focus-state-layer-shape: 14px}::ng-deep .pdx-stepper-ft-light .mat-step-header{margin:0}::ng-deep .pdx-stepper-ft-light .mat-step-header .mat-step-header-ripple{display:none}::ng-deep .ft-stepper{background:transparent}.ft-stepper-content{padding:0}.ft-stepper-content .form-section{border:none;padding:0;background:transparent}.nav-align-start{justify-content:flex-start}.nav-align-center{justify-content:center}.nav-align-end{justify-content:flex-end}.nav-align-space-between{justify-content:space-between}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.stepper-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.stepper-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2, 0 4px 12px rgba(0, 0, 0, .18))}\n"] }]
3379
4465
  }], propDecorators: { stepLabelTpl: [{
3380
4466
  type: ContentChild,
3381
4467
  args: ['stepLabelTpl', { read: TemplateRef }]
@@ -3416,6 +4502,86 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
3416
4502
  type: Output
3417
4503
  }] } });
3418
4504
 
4505
+ class PraxisStepperWidgetConfigEditor {
4506
+ inputs = null;
4507
+ widgetKey;
4508
+ stepperEditor;
4509
+ isDirty$ = new BehaviorSubject(false);
4510
+ isValid$ = new BehaviorSubject(true);
4511
+ isBusy$ = new BehaviorSubject(false);
4512
+ subscription = new Subscription();
4513
+ emptyConfig = {
4514
+ steps: [],
4515
+ orientation: 'horizontal',
4516
+ headerPosition: 'top',
4517
+ linear: false,
4518
+ };
4519
+ get config() {
4520
+ return this.inputs?.config ?? this.emptyConfig;
4521
+ }
4522
+ get stepperId() {
4523
+ return this.inputs?.stepperId ?? this.widgetKey;
4524
+ }
4525
+ ngAfterViewInit() {
4526
+ if (!this.stepperEditor) {
4527
+ return;
4528
+ }
4529
+ this.subscription.add(this.stepperEditor.isDirty$.subscribe((value) => this.isDirty$.next(value)));
4530
+ this.subscription.add(this.stepperEditor.isValid$.subscribe((value) => this.isValid$.next(value)));
4531
+ this.subscription.add(this.stepperEditor.isBusy$.subscribe((value) => this.isBusy$.next(value)));
4532
+ }
4533
+ ngOnDestroy() {
4534
+ this.subscription.unsubscribe();
4535
+ }
4536
+ getSettingsValue() {
4537
+ return this.buildValue(this.stepperEditor?.getSettingsValue());
4538
+ }
4539
+ onSave() {
4540
+ return this.buildValue(this.stepperEditor?.onSave?.() ?? this.stepperEditor?.getSettingsValue());
4541
+ }
4542
+ reset() {
4543
+ this.stepperEditor?.reset();
4544
+ }
4545
+ buildValue(config) {
4546
+ const nextConfig = config ?? this.config;
4547
+ return {
4548
+ inputs: {
4549
+ ...(this.inputs ?? {}),
4550
+ config: nextConfig,
4551
+ ...(this.stepperId ? { stepperId: this.stepperId } : {}),
4552
+ ...(typeof nextConfig.selectedIndex === 'number' ? { selectedIndex: nextConfig.selectedIndex } : {}),
4553
+ },
4554
+ };
4555
+ }
4556
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisStepperWidgetConfigEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
4557
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisStepperWidgetConfigEditor, isStandalone: true, selector: "praxis-stepper-widget-config-editor", inputs: { inputs: "inputs", widgetKey: "widgetKey" }, viewQueries: [{ propertyName: "stepperEditor", first: true, predicate: ["stepperEditor"], descendants: true }], ngImport: i0, template: `
4558
+ <section data-testid="stepper-widget-config-editor">
4559
+ <praxis-stepper-config-editor #stepperEditor [stepperConfig]="config" />
4560
+ </section>
4561
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PraxisStepperConfigEditor, selector: "praxis-stepper-config-editor", inputs: ["stepperConfig"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4562
+ }
4563
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisStepperWidgetConfigEditor, decorators: [{
4564
+ type: Component,
4565
+ args: [{
4566
+ selector: 'praxis-stepper-widget-config-editor',
4567
+ standalone: true,
4568
+ imports: [CommonModule, PraxisStepperConfigEditor],
4569
+ template: `
4570
+ <section data-testid="stepper-widget-config-editor">
4571
+ <praxis-stepper-config-editor #stepperEditor [stepperConfig]="config" />
4572
+ </section>
4573
+ `,
4574
+ changeDetection: ChangeDetectionStrategy.OnPush,
4575
+ }]
4576
+ }], propDecorators: { inputs: [{
4577
+ type: Input
4578
+ }], widgetKey: [{
4579
+ type: Input
4580
+ }], stepperEditor: [{
4581
+ type: ViewChild,
4582
+ args: ['stepperEditor']
4583
+ }] } });
4584
+
3419
4585
  class PraxisWizardBenefitsGridComponent {
3420
4586
  blockId;
3421
4587
  title;
@@ -3440,7 +4606,7 @@ class PraxisWizardBenefitsGridComponent {
3440
4606
  </div>
3441
4607
  </div>
3442
4608
  </section>
3443
- `, isInline: true, styles: [":host{display:block}.pwx-benefits{display:flex;flex-direction:column;gap:10px}.pwx-benefits-title{margin:0;font-size:.9rem;font-weight:600;color:var(--md-sys-color-on-surface);letter-spacing:.02em;display:flex;align-items:center;gap:12px}.pwx-benefits-title:before,.pwx-benefits-title:after{content:\"\";height:1px;flex:1;background:var(--md-sys-color-outline-variant)}.pwx-benefits-grid{display:grid;grid-template-columns:repeat(var(--pwx-columns),minmax(0,1fr));gap:12px 16px;align-items:start}.pwx-benefits-grid.is-boxed{padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface)}.pwx-benefit{display:flex;gap:10px;align-items:flex-start;min-height:0}.pwx-benefit-icon{width:24px;min-width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;line-height:1;color:var(--praxis-wizard-accent, var(--md-sys-color-primary))}.pwx-benefit-icon mat-icon{width:24px;height:24px;font-size:24px;line-height:24px;display:block;overflow:visible}.pwx-benefit-title{font-weight:600;font-size:.88rem;color:var(--md-sys-color-on-surface)}.pwx-benefit-text{font-size:.82rem;color:var(--praxis-wizard-muted, var(--md-sys-color-on-surface-variant));line-height:1.35}@media(max-width:720px){.pwx-benefits-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:480px){.pwx-benefits-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4609
+ `, isInline: true, styles: [":host{display:block}.pwx-benefits{display:flex;flex-direction:column;gap:10px}.pwx-benefits-title{margin:0;font-size:.9rem;font-weight:600;color:var(--md-sys-color-on-surface);letter-spacing:.02em;display:flex;align-items:center;gap:12px}.pwx-benefits-title:before,.pwx-benefits-title:after{content:\"\";height:1px;flex:1;background:var(--md-sys-color-outline-variant)}.pwx-benefits-grid{display:grid;grid-template-columns:repeat(var(--pwx-columns),minmax(0,1fr));gap:12px 16px;align-items:start}.pwx-benefits-grid.is-boxed{padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface)}.pwx-benefit{display:flex;gap:10px;align-items:flex-start;min-height:0}.pwx-benefit-icon{width:24px;min-width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;line-height:1;color:var(--praxis-wizard-accent, var(--md-sys-color-primary))}.pwx-benefit-icon mat-icon{width:24px;height:24px;font-size:24px;line-height:24px;display:block;overflow:visible}.pwx-benefit-title{font-weight:600;font-size:.88rem;color:var(--md-sys-color-on-surface)}.pwx-benefit-text{font-size:.82rem;color:var(--praxis-wizard-muted, var(--md-sys-color-on-surface-variant));line-height:1.35}@media(max-width:720px){.pwx-benefits-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:480px){.pwx-benefits-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3444
4610
  }
3445
4611
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisWizardBenefitsGridComponent, decorators: [{
3446
4612
  type: Component,
@@ -3595,6 +4761,14 @@ const PRAXIS_STEPPER_COMPONENT_METADATA = {
3595
4761
  friendlyName: 'Praxis Stepper',
3596
4762
  description: 'Stepper configurável (horizontal/vertical/linear) com passos e conteúdo dinâmico.',
3597
4763
  icon: 'format_list_numbered',
4764
+ authoringManifestRef: {
4765
+ componentId: 'praxis-stepper',
4766
+ source: 'PRAXIS_STEPPER_AUTHORING_MANIFEST',
4767
+ },
4768
+ configEditor: {
4769
+ component: PraxisStepperWidgetConfigEditor,
4770
+ title: 'Configure stepper',
4771
+ },
3598
4772
  inputs: [
3599
4773
  { name: 'stepperId', type: 'string', label: 'ID do Stepper', description: 'Identificador para persistência (obrigatório)' },
3600
4774
  { name: 'componentInstanceId', type: 'string', label: 'ID da instância', description: 'Identificador opcional para múltiplas instâncias na mesma rota' },
@@ -4232,7 +5406,7 @@ class PraxisWizardCtaBarComponent {
4232
5406
  {{ primaryLabel }}
4233
5407
  </button>
4234
5408
  </div>
4235
- `, isInline: true, styles: [":host{display:block}.pwx-cta{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding-top:16px}.pwx-cta:not(.has-secondary){justify-content:center}.pwx-cta.is-sticky{position:sticky;bottom:0;background:var(--praxis-wizard-card-bg, var(--md-sys-color-surface));padding:12px 0 8px;border-top:1px solid var(--praxis-wizard-border, var(--md-sys-color-outline-variant))}.pwx-cta.has-secondary .pwx-cta-primary{min-width:200px}.pwx-cta-primary{min-width:240px;height:36px;border-radius:4px;font-weight:600;text-transform:none;padding:0 18px}.pwx-cta-secondary{color:var(--praxis-wizard-muted, var(--md-sys-color-on-surface-variant));text-transform:none;font-weight:500;padding:0 4px;min-width:auto}@media(max-width:600px){.pwx-cta{flex-direction:column;align-items:stretch}.pwx-cta-primary{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5409
+ `, isInline: true, styles: [":host{display:block}.pwx-cta{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding-top:16px}.pwx-cta:not(.has-secondary){justify-content:center}.pwx-cta.is-sticky{position:sticky;bottom:0;background:var(--praxis-wizard-card-bg, var(--md-sys-color-surface));padding:12px 0 8px;border-top:1px solid var(--praxis-wizard-border, var(--md-sys-color-outline-variant))}.pwx-cta.has-secondary .pwx-cta-primary{min-width:200px}.pwx-cta-primary{min-width:240px;height:36px;border-radius:4px;font-weight:600;text-transform:none;padding:0 18px}.pwx-cta-secondary{color:var(--praxis-wizard-muted, var(--md-sys-color-on-surface-variant));text-transform:none;font-weight:500;padding:0 4px;min-width:auto}@media(max-width:600px){.pwx-cta{flex-direction:column;align-items:stretch}.pwx-cta-primary{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4236
5410
  }
4237
5411
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisWizardCtaBarComponent, decorators: [{
4238
5412
  type: Component,
@@ -4644,6 +5818,357 @@ function applyPreferenceDefaults(cfg, values) {
4644
5818
  return { ...cfg, steps };
4645
5819
  }
4646
5820
 
5821
+ const stepItemSchema = {
5822
+ type: 'object',
5823
+ required: ['id', 'label'],
5824
+ properties: {
5825
+ id: { type: 'string' },
5826
+ label: { type: 'string' },
5827
+ description: { type: 'string' },
5828
+ optional: { type: 'boolean' },
5829
+ editable: { type: 'boolean' },
5830
+ completed: { type: 'boolean' },
5831
+ hasError: { type: 'boolean' },
5832
+ state: { type: 'string' },
5833
+ stateIcon: { type: 'string' },
5834
+ errorMessage: { type: 'string' },
5835
+ ariaLabel: { type: 'string' },
5836
+ ariaLabelledby: { type: 'string' },
5837
+ form: { type: 'object' },
5838
+ stepBlocksBeforeForm: { type: 'object' },
5839
+ stepBlocksAfterForm: { type: 'object' },
5840
+ widgets: { type: 'array', items: { type: 'object' } },
5841
+ },
5842
+ };
5843
+ const stepPatchSchema = {
5844
+ type: 'object',
5845
+ minProperties: 1,
5846
+ properties: {
5847
+ id: { type: 'string' },
5848
+ label: { type: 'string' },
5849
+ description: { type: 'string' },
5850
+ optional: { type: 'boolean' },
5851
+ editable: { type: 'boolean' },
5852
+ completed: { type: 'boolean' },
5853
+ hasError: { type: 'boolean' },
5854
+ state: { type: 'string' },
5855
+ stateIcon: { type: 'string' },
5856
+ errorMessage: { type: 'string' },
5857
+ form: { type: 'object' },
5858
+ stepBlocksBeforeForm: { type: 'object' },
5859
+ stepBlocksAfterForm: { type: 'object' },
5860
+ widgets: { type: 'array', items: { type: 'object' } },
5861
+ },
5862
+ };
5863
+ const PRAXIS_STEPPER_AUTHORING_MANIFEST = {
5864
+ schemaVersion: '1.0.0',
5865
+ componentId: 'praxis-stepper',
5866
+ ownerPackage: '@praxisui/stepper',
5867
+ configSchemaId: 'StepperMetadata',
5868
+ manifestVersion: '1.0.0',
5869
+ runtimeInputs: [
5870
+ { name: 'config', type: 'StepperMetadata', description: 'Canonical multi-step flow configuration.' },
5871
+ { name: 'stepperId', type: 'string', description: 'Stable id used to derive stepper config persistence scope.' },
5872
+ { name: 'componentInstanceId', type: 'string', description: 'Optional instance discriminator for persistence scope.' },
5873
+ { name: 'selectedIndex', type: 'number', description: 'Selected step index, compatible with two-way binding.' },
5874
+ { name: 'serverValidate', type: 'function', description: 'Optional host-owned async validation gate for step progression.' },
5875
+ { name: 'stepperContext', type: 'Record<string, any>', description: 'Context passed to nested widgets and rich content.' },
5876
+ { name: 'enableCustomization', type: 'boolean', description: 'Enables Settings Panel authoring surfaces.' },
5877
+ ],
5878
+ editableTargets: [
5879
+ { kind: 'step', resolver: 'step-by-id-or-label', description: 'A step in config.steps[].' },
5880
+ { kind: 'stepLabel', resolver: 'step-by-id-or-label', description: 'The visible label of a step.' },
5881
+ { kind: 'stepContent', resolver: 'step-content-by-id', description: 'Dynamic form, rich content or advanced widgets hosted inside a step.' },
5882
+ { kind: 'navigation', resolver: 'stepper-navigation-config', description: 'Footer navigation labels, icons, variant, alignment and visibility.' },
5883
+ { kind: 'completionRule', resolver: 'step-by-id-or-label', description: 'Optional/completed/error state metadata for a step.' },
5884
+ { kind: 'validationGate', resolver: 'step-validation-by-id', description: 'Validation gate that controls linear navigation and remote validation delegation.' },
5885
+ { kind: 'orientation', resolver: 'stepper-layout-config', description: 'Stepper orientation, header position and label position.' },
5886
+ ],
5887
+ operations: [
5888
+ {
5889
+ operationId: 'step.add',
5890
+ title: 'Add step',
5891
+ scope: 'global',
5892
+ targetKind: 'step',
5893
+ target: { kind: 'step', resolver: 'steps-array', ambiguityPolicy: 'fail', required: false },
5894
+ inputSchema: stepItemSchema,
5895
+ effects: [{ kind: 'append-unique', path: 'steps[]', key: 'id' }],
5896
+ destructive: false,
5897
+ requiresConfirmation: false,
5898
+ validators: ['step-id-unique', 'step-order-deterministic', 'step-content-valid'],
5899
+ affectedPaths: ['steps[]'],
5900
+ submissionImpact: 'config-only',
5901
+ preconditions: ['config-initialized'],
5902
+ },
5903
+ {
5904
+ operationId: 'step.remove',
5905
+ title: 'Remove step',
5906
+ scope: 'layout',
5907
+ targetKind: 'step',
5908
+ target: { kind: 'step', resolver: 'step-by-id-or-label', ambiguityPolicy: 'fail', required: true },
5909
+ inputSchema: {
5910
+ type: 'object',
5911
+ properties: {
5912
+ replacementStepId: { type: 'string' },
5913
+ },
5914
+ },
5915
+ effects: [{
5916
+ kind: 'compile-domain-patch',
5917
+ handler: 'stepper-step-remove',
5918
+ handlerContract: {
5919
+ reads: ['steps[]', 'selectedIndex'],
5920
+ writes: ['steps[]', 'selectedIndex'],
5921
+ identityKeys: ['steps[].id'],
5922
+ inputSchema: {
5923
+ type: 'object',
5924
+ properties: {
5925
+ replacementStepId: { type: 'string' },
5926
+ },
5927
+ },
5928
+ failureModes: ['step-not-found', 'replacement-step-not-found', 'content-removal-not-confirmed'],
5929
+ description: 'Removes the target step by stable id and recomputes selectedIndex to preserve deterministic navigation state.',
5930
+ },
5931
+ }],
5932
+ destructive: true,
5933
+ requiresConfirmation: true,
5934
+ validators: ['step-exists', 'selected-step-removal-safe', 'step-content-removal-confirmed'],
5935
+ affectedPaths: ['steps[]', 'selectedIndex'],
5936
+ submissionImpact: 'config-only',
5937
+ preconditions: ['config-initialized', 'target-step-exists', 'confirmation-collected'],
5938
+ },
5939
+ {
5940
+ operationId: 'step.label.set',
5941
+ title: 'Set step label',
5942
+ scope: 'layout',
5943
+ targetKind: 'stepLabel',
5944
+ target: { kind: 'stepLabel', resolver: 'step-by-id-or-label', ambiguityPolicy: 'fail', required: true },
5945
+ inputSchema: { type: 'object', required: ['label'], properties: { label: { type: 'string' } } },
5946
+ effects: [{ kind: 'merge-by-key', path: 'steps[]', key: 'id' }],
5947
+ destructive: false,
5948
+ requiresConfirmation: false,
5949
+ validators: ['step-exists', 'step-label-valid'],
5950
+ affectedPaths: ['steps[].label'],
5951
+ submissionImpact: 'config-only',
5952
+ preconditions: ['config-initialized', 'target-step-exists'],
5953
+ },
5954
+ {
5955
+ operationId: 'step.order.set',
5956
+ title: 'Reorder steps',
5957
+ scope: 'layout',
5958
+ targetKind: 'step',
5959
+ target: { kind: 'step', resolver: 'step-by-id-or-label', ambiguityPolicy: 'fail', required: true },
5960
+ inputSchema: { type: 'object', required: ['beforeStepId'], properties: { beforeStepId: { type: 'string' } } },
5961
+ effects: [{
5962
+ kind: 'compile-domain-patch',
5963
+ handler: 'stepper-step-reorder',
5964
+ handlerContract: {
5965
+ reads: ['steps[]', 'selectedIndex'],
5966
+ writes: ['steps[]', 'selectedIndex'],
5967
+ identityKeys: ['steps[].id'],
5968
+ inputSchema: { type: 'object', required: ['beforeStepId'], properties: { beforeStepId: { type: 'string' } } },
5969
+ failureModes: ['step-not-found', 'before-step-not-found', 'ambiguous-step-target'],
5970
+ description: 'Moves the target step before another stable step id and recomputes selectedIndex by preserving selected step identity.',
5971
+ },
5972
+ }],
5973
+ destructive: false,
5974
+ requiresConfirmation: false,
5975
+ validators: ['step-exists', 'step-order-deterministic', 'selected-index-preserved'],
5976
+ affectedPaths: ['steps[]', 'selectedIndex'],
5977
+ submissionImpact: 'config-only',
5978
+ preconditions: ['config-initialized', 'target-step-exists'],
5979
+ },
5980
+ {
5981
+ operationId: 'step.optional.set',
5982
+ title: 'Set optional state',
5983
+ scope: 'interaction',
5984
+ targetKind: 'completionRule',
5985
+ target: { kind: 'completionRule', resolver: 'step-by-id-or-label', ambiguityPolicy: 'fail', required: true },
5986
+ inputSchema: { type: 'object', required: ['optional'], properties: { optional: { type: 'boolean' } } },
5987
+ effects: [{ kind: 'merge-by-key', path: 'steps[]', key: 'id' }],
5988
+ destructive: false,
5989
+ requiresConfirmation: false,
5990
+ validators: ['step-exists', 'completion-rule-compatible'],
5991
+ affectedPaths: ['steps[].optional'],
5992
+ submissionImpact: 'config-only',
5993
+ preconditions: ['config-initialized', 'target-step-exists'],
5994
+ },
5995
+ {
5996
+ operationId: 'step.completed.set',
5997
+ title: 'Set completed state',
5998
+ scope: 'interaction',
5999
+ targetKind: 'completionRule',
6000
+ target: { kind: 'completionRule', resolver: 'step-by-id-or-label', ambiguityPolicy: 'fail', required: true },
6001
+ inputSchema: { type: 'object', required: ['completed'], properties: { completed: { type: 'boolean' }, hasError: { type: 'boolean' }, errorMessage: { type: 'string' } } },
6002
+ effects: [{ kind: 'merge-by-key', path: 'steps[]', key: 'id' }],
6003
+ destructive: false,
6004
+ requiresConfirmation: false,
6005
+ validators: ['step-exists', 'completion-rule-compatible', 'linear-completion-safe'],
6006
+ affectedPaths: ['steps[].completed', 'steps[].hasError', 'steps[].errorMessage'],
6007
+ submissionImpact: 'config-only',
6008
+ preconditions: ['config-initialized', 'target-step-exists'],
6009
+ },
6010
+ {
6011
+ operationId: 'navigation.mode.set',
6012
+ title: 'Set navigation mode',
6013
+ scope: 'interaction',
6014
+ targetKind: 'navigation',
6015
+ target: { kind: 'navigation', resolver: 'stepper-navigation-config', ambiguityPolicy: 'fail', required: true },
6016
+ inputSchema: {
6017
+ type: 'object',
6018
+ properties: {
6019
+ linear: { type: 'boolean' },
6020
+ visible: { type: 'boolean' },
6021
+ prevLabel: { type: 'string' },
6022
+ nextLabel: { type: 'string' },
6023
+ prevIcon: { type: 'string' },
6024
+ nextIcon: { type: 'string' },
6025
+ variant: { enum: ['basic', 'flat', 'stroked', 'raised'] },
6026
+ color: { enum: ['primary', 'accent', 'warn'] },
6027
+ align: { enum: ['start', 'center', 'end', 'space-between'] },
6028
+ },
6029
+ },
6030
+ effects: [{ kind: 'merge-object', path: 'navigation' }, { kind: 'set-value', path: 'linear' }],
6031
+ destructive: false,
6032
+ requiresConfirmation: false,
6033
+ validators: ['navigation-values-valid', 'linear-navigation-valid', 'editor-runtime-round-trip'],
6034
+ affectedPaths: ['navigation.visible', 'navigation.prevLabel', 'navigation.nextLabel', 'navigation.prevIcon', 'navigation.nextIcon', 'navigation.variant', 'navigation.color', 'navigation.align', 'linear'],
6035
+ submissionImpact: 'config-only',
6036
+ preconditions: ['config-initialized'],
6037
+ },
6038
+ {
6039
+ operationId: 'orientation.set',
6040
+ title: 'Set orientation',
6041
+ scope: 'layout',
6042
+ targetKind: 'orientation',
6043
+ target: { kind: 'orientation', resolver: 'stepper-layout-config', ambiguityPolicy: 'fail', required: true },
6044
+ inputSchema: {
6045
+ type: 'object',
6046
+ required: ['orientation'],
6047
+ properties: {
6048
+ orientation: { enum: ['horizontal', 'vertical'] },
6049
+ headerPosition: { enum: ['top', 'bottom'] },
6050
+ labelPosition: { enum: ['bottom', 'end'] },
6051
+ density: { enum: ['default', 'comfortable', 'compact'] },
6052
+ },
6053
+ },
6054
+ effects: [{ kind: 'set-value', path: 'orientation' }, { kind: 'set-value', path: 'headerPosition' }, { kind: 'set-value', path: 'labelPosition' }, { kind: 'set-value', path: 'density' }],
6055
+ destructive: false,
6056
+ requiresConfirmation: false,
6057
+ validators: ['orientation-values-valid', 'label-position-compatible', 'editor-runtime-round-trip'],
6058
+ affectedPaths: ['orientation', 'headerPosition', 'labelPosition', 'density'],
6059
+ submissionImpact: 'config-only',
6060
+ preconditions: ['config-initialized'],
6061
+ },
6062
+ {
6063
+ operationId: 'validation.rule.add',
6064
+ title: 'Add validation rule',
6065
+ scope: 'rule',
6066
+ targetKind: 'validationGate',
6067
+ target: { kind: 'validationGate', resolver: 'step-validation-by-id', ambiguityPolicy: 'fail', required: true },
6068
+ inputSchema: {
6069
+ type: 'object',
6070
+ required: ['stepId', 'rule'],
6071
+ properties: {
6072
+ stepId: { type: 'string' },
6073
+ fieldName: { type: 'string' },
6074
+ childComponentId: { type: 'string' },
6075
+ rule: { type: 'object' },
6076
+ message: { type: 'string' },
6077
+ remote: { type: 'boolean' },
6078
+ },
6079
+ },
6080
+ effects: [{
6081
+ kind: 'compile-domain-patch',
6082
+ handler: 'stepper-validation-rule-upsert',
6083
+ handlerContract: {
6084
+ reads: ['steps[]', 'steps[].form', 'steps[].form.config', 'steps[].widgets', 'linear'],
6085
+ writes: ['steps[].form.config', 'steps[].form.config.sections', 'steps[].form.config.fieldMetadata', 'steps[].hasError', 'steps[].errorMessage'],
6086
+ identityKeys: ['steps[].id', 'steps[].form.formId', 'steps[].widgets[].id'],
6087
+ inputSchema: {
6088
+ type: 'object',
6089
+ required: ['stepId', 'rule'],
6090
+ properties: {
6091
+ stepId: { type: 'string' },
6092
+ fieldName: { type: 'string' },
6093
+ childComponentId: { type: 'string' },
6094
+ rule: { type: 'object' },
6095
+ message: { type: 'string' },
6096
+ remote: { type: 'boolean' },
6097
+ },
6098
+ },
6099
+ failureModes: [
6100
+ 'step-not-found',
6101
+ 'validation-target-not-found',
6102
+ 'dynamic-form-config-missing',
6103
+ 'remote-validation-requires-host-serverValidate',
6104
+ ],
6105
+ description: 'Projects a validation rule into the step dynamic-form config or delegates it to the host serverValidate gate without redefining child component contracts.',
6106
+ },
6107
+ }],
6108
+ validators: ['step-exists', 'validation-rule-target-exists', 'validation-rule-compatible', 'server-validation-delegated'],
6109
+ destructive: false,
6110
+ requiresConfirmation: false,
6111
+ affectedPaths: ['steps[].form.config', 'steps[].form.config.sections', 'steps[].form.config.fieldMetadata', 'steps[].hasError', 'steps[].errorMessage'],
6112
+ submissionImpact: 'affects-schema-backed-data',
6113
+ preconditions: ['config-initialized', 'target-step-exists'],
6114
+ },
6115
+ {
6116
+ operationId: 'step.content.set',
6117
+ title: 'Set step content',
6118
+ scope: 'layout',
6119
+ targetKind: 'stepContent',
6120
+ target: { kind: 'stepContent', resolver: 'step-content-by-id', ambiguityPolicy: 'fail', required: true },
6121
+ inputSchema: stepPatchSchema,
6122
+ effects: [{ kind: 'merge-by-key', path: 'steps[]', key: 'id' }],
6123
+ destructive: false,
6124
+ requiresConfirmation: false,
6125
+ validators: ['step-exists', 'step-content-valid', 'nested-widget-contract-delegated'],
6126
+ affectedPaths: ['steps[].form', 'steps[].stepBlocksBeforeForm', 'steps[].stepBlocksAfterForm', 'steps[].widgets'],
6127
+ submissionImpact: 'affects-schema-backed-data',
6128
+ preconditions: ['config-initialized', 'target-step-exists'],
6129
+ },
6130
+ ],
6131
+ validators: [
6132
+ { validatorId: 'step-id-unique', level: 'error', code: 'PSTEP001', description: 'Step ids must be unique and stable within config.steps[].' },
6133
+ { validatorId: 'step-exists', level: 'error', code: 'PSTEP002', description: 'Target step must exist before applying the operation.' },
6134
+ { validatorId: 'step-order-deterministic', level: 'error', code: 'PSTEP003', description: 'Step ordering must use stable ids, not transient array index as identity.' },
6135
+ { validatorId: 'step-content-valid', level: 'error', code: 'PSTEP004', description: 'Step content must remain valid Dynamic Form config, RichContentDocument or WidgetDefinition[] metadata.' },
6136
+ { validatorId: 'step-content-removal-confirmed', level: 'error', code: 'PSTEP005', description: 'Removing a step with form, rich content or widgets is destructive and requires confirmation.' },
6137
+ { validatorId: 'selected-step-removal-safe', level: 'error', code: 'PSTEP006', description: 'Removing the selected step requires a deterministic replacement selected index or explicit confirmation.' },
6138
+ { validatorId: 'selected-index-preserved', level: 'warning', code: 'PSTEP007', description: 'Reordering steps must preserve selected step identity and only recompute selectedIndex after resolution.' },
6139
+ { validatorId: 'step-label-valid', level: 'error', code: 'PSTEP008', description: 'Step labels must be non-empty text values after localization/domain projection.' },
6140
+ { validatorId: 'completion-rule-compatible', level: 'error', code: 'PSTEP009', description: 'Optional, completed, hasError and errorMessage flags must remain compatible with Material stepper semantics.' },
6141
+ { validatorId: 'linear-completion-safe', level: 'warning', code: 'PSTEP010', description: 'Forcing completion in linear mode should not bypass invalid Dynamic Form or server validation gates.' },
6142
+ { validatorId: 'navigation-values-valid', level: 'error', code: 'PSTEP011', description: 'Navigation labels, icons, variant, color and alignment must match StepperMetadata enums and runtime bindings.' },
6143
+ { validatorId: 'linear-navigation-valid', level: 'error', code: 'PSTEP012', description: 'Linear navigation depends on form validity and must not be replaced by host-local navigation rules.' },
6144
+ { validatorId: 'orientation-values-valid', level: 'error', code: 'PSTEP013', description: 'Orientation, headerPosition, labelPosition and density values must match StepperMetadata enums.' },
6145
+ { validatorId: 'label-position-compatible', level: 'warning', code: 'PSTEP014', description: 'labelPosition only affects horizontal steppers and must round-trip without corrupting vertical layout.' },
6146
+ { validatorId: 'validation-rule-target-exists', level: 'error', code: 'PSTEP015', description: 'Validation rules must reference an existing field inside the step form or an existing child widget/component.' },
6147
+ { validatorId: 'validation-rule-compatible', level: 'error', code: 'PSTEP016', description: 'Validation rules must compile into Dynamic Form metadata or host serverValidate delegation without inventing a parallel stepper rule DSL.' },
6148
+ { validatorId: 'server-validation-delegated', level: 'info', code: 'PSTEP017', description: 'Remote validation remains delegated to the serverValidate input and is not implemented as hidden stepper metadata.' },
6149
+ { validatorId: 'nested-widget-contract-delegated', level: 'info', code: 'PSTEP018', description: 'Nested widget content remains governed by the child component contract and widgetEvent path delegation.' },
6150
+ { validatorId: 'editor-runtime-round-trip', level: 'error', code: 'PSTEP019', description: 'Settings Panel editor, runtime and registry projection must preserve step ids, order, navigation and validation intent.' },
6151
+ ],
6152
+ roundTripRequirements: [
6153
+ 'Operations must preserve stable step ids; array index may be used only as a resolver fallback, never as canonical identity.',
6154
+ 'Settings Panel editor, runtime persistence and registry projection must round-trip StepperMetadata without losing step ids, order or selectedIndex.',
6155
+ 'Validation authoring must target Dynamic Form metadata or the host-owned serverValidate gate; the stepper manifest must not define a parallel validation DSL.',
6156
+ 'Nested widgets remain delegated through WidgetDefinition and widgetEvent path enrichment instead of being redefined by the stepper contract.',
6157
+ 'Removing a step with form, rich content or widgets requires explicit confirmation because it can delete nested authoring content.',
6158
+ ],
6159
+ examples: [
6160
+ { id: 'add-review-step', request: 'Add a review step after the documents step.', operationId: 'step.add', params: { id: 'review', label: 'Review' }, isPositive: true },
6161
+ { id: 'rename-profile-step', request: 'Rename the profile step to Customer profile.', operationId: 'step.label.set', target: 'profile', params: { label: 'Customer profile' }, isPositive: true },
6162
+ { id: 'move-confirm-before-submit', request: 'Move confirm before submit.', operationId: 'step.order.set', target: 'confirm', params: { beforeStepId: 'submit' }, isPositive: true },
6163
+ { id: 'make-upload-optional', request: 'Make the upload step optional.', operationId: 'step.optional.set', target: 'upload', params: { optional: true }, isPositive: true },
6164
+ { id: 'set-linear-navigation', request: 'Use linear navigation with Back and Continue labels.', operationId: 'navigation.mode.set', params: { linear: true, prevLabel: 'Back', nextLabel: 'Continue' }, isPositive: true },
6165
+ { id: 'switch-vertical-layout', request: 'Make the stepper vertical.', operationId: 'orientation.set', params: { orientation: 'vertical', headerPosition: 'top' }, isPositive: true },
6166
+ { id: 'require-email-on-profile', request: 'Require email before continuing from profile.', operationId: 'validation.rule.add', target: 'profile', params: { stepId: 'profile', fieldName: 'email', rule: { required: true }, message: 'Email is required' }, isPositive: true },
6167
+ { id: 'reject-duplicate-step-id', request: 'Add another step with id review.', operationId: 'step.add', params: { id: 'review', label: 'Duplicate review' }, isPositive: false },
6168
+ { id: 'confirm-remove-content-step', request: 'Remove the documents step that contains an upload widget.', operationId: 'step.remove', target: 'documents', params: { replacementStepId: 'review' }, isPositive: true },
6169
+ ],
6170
+ };
6171
+
4647
6172
  /*
4648
6173
  * Public API Surface of praxis-stepper
4649
6174
  */
@@ -4652,4 +6177,4 @@ function applyPreferenceDefaults(cfg, values) {
4652
6177
  * Generated bundle index. Do not edit.
4653
6178
  */
4654
6179
 
4655
- export { FT_WIZARD_CONFIG, FT_WIZARD_JSON, PRAXIS_STEPPER_COMPONENT_METADATA, PRAXIS_WIZARD_BENEFITS_METADATA, PRAXIS_WIZARD_CONTENT_METADATA, PRAXIS_WIZARD_DIVIDER_METADATA, PRAXIS_WIZARD_NOTICE_METADATA, PraxisStepper, PraxisStepperConfigEditor, PraxisWizardFormComponent, STEPPER_AI_CAPABILITIES, providePraxisStepperMetadata };
6180
+ export { FT_WIZARD_CONFIG, FT_WIZARD_JSON, PRAXIS_STEPPER_AUTHORING_MANIFEST, PRAXIS_STEPPER_COMPONENT_METADATA, PRAXIS_WIZARD_BENEFITS_METADATA, PRAXIS_WIZARD_CONTENT_METADATA, PRAXIS_WIZARD_DIVIDER_METADATA, PRAXIS_WIZARD_NOTICE_METADATA, PraxisStepper, PraxisStepperConfigEditor, PraxisStepperWidgetConfigEditor, PraxisWizardFormComponent, STEPPER_AI_CAPABILITIES, providePraxisStepperMetadata };