@praxisui/core 3.0.0-beta.9 → 4.0.0-beta.0

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.
@@ -5,7 +5,7 @@ import { HttpHeaders, HttpClient, HttpParams, HttpResponse, HttpContextToken, HT
5
5
  import { of, defer, throwError, from, EMPTY, BehaviorSubject, firstValueFrom, Subject, map as map$1 } from 'rxjs';
6
6
  import { switchMap, take, map, catchError, concatMap, tap, shareReplay, takeUntil, toArray, finalize } from 'rxjs/operators';
7
7
  import * as i1$2 from '@angular/common';
8
- import { Location, CommonModule } from '@angular/common';
8
+ import { Location, DOCUMENT, CommonModule } from '@angular/common';
9
9
  import { Router, ActivatedRoute } from '@angular/router';
10
10
  import * as i1$1 from '@angular/forms';
11
11
  import { Validators, FormGroup, FormControl, FormsModule } from '@angular/forms';
@@ -14,9 +14,9 @@ import * as i2 from '@angular/material/button';
14
14
  import { MatButtonModule } from '@angular/material/button';
15
15
  import * as i3 from '@angular/material/form-field';
16
16
  import { MatFormFieldModule } from '@angular/material/form-field';
17
- import * as i5 from '@angular/material/icon';
17
+ import * as i3$1 from '@angular/material/icon';
18
18
  import { MatIconModule } from '@angular/material/icon';
19
- import * as i5$1 from '@angular/material/input';
19
+ import * as i5 from '@angular/material/input';
20
20
  import { MatInputModule } from '@angular/material/input';
21
21
  import * as i6 from '@angular/material/select';
22
22
  import { MatSelectModule } from '@angular/material/select';
@@ -27,7 +27,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
27
27
  import { DomSanitizer } from '@angular/platform-browser';
28
28
  import * as i4 from '@angular/material/menu';
29
29
  import { MatMenuModule } from '@angular/material/menu';
30
- import * as i3$1 from '@angular/material/card';
30
+ import * as i3$2 from '@angular/material/card';
31
31
  import { MatCardModule } from '@angular/material/card';
32
32
  import * as i8$1 from '@angular/material/chips';
33
33
  import { MatChipsModule } from '@angular/material/chips';
@@ -1822,9 +1822,9 @@ function isObject(value) {
1822
1822
  */
1823
1823
  function deepMerge(left, right) {
1824
1824
  if (right === undefined || right === null)
1825
- return clone(left);
1825
+ return clone$1(left);
1826
1826
  if (Array.isArray(left)) {
1827
- return (Array.isArray(right) ? right : clone(left));
1827
+ return (Array.isArray(right) ? right : clone$1(left));
1828
1828
  }
1829
1829
  if (!isObject(left) || !isObject(right)) {
1830
1830
  // Right wins when defined, else left
@@ -1837,13 +1837,13 @@ function deepMerge(left, right) {
1837
1837
  if (r === undefined)
1838
1838
  continue; // keep left as-is
1839
1839
  if (Array.isArray(r)) {
1840
- out[key] = r.map((v) => clone(v));
1840
+ out[key] = r.map((v) => clone$1(v));
1841
1841
  }
1842
1842
  else if (isObject(r) && isObject(l)) {
1843
1843
  out[key] = deepMerge(l, r);
1844
1844
  }
1845
1845
  else {
1846
- out[key] = clone(r);
1846
+ out[key] = clone$1(r);
1847
1847
  }
1848
1848
  }
1849
1849
  return out;
@@ -1855,7 +1855,7 @@ function deepMerge(left, right) {
1855
1855
  */
1856
1856
  function fillUndefined(target, defaults) {
1857
1857
  if (defaults === undefined || defaults === null)
1858
- return clone(target);
1858
+ return clone$1(target);
1859
1859
  if (!isObject(target) || !isObject(defaults)) {
1860
1860
  return (target === undefined || target === null ? defaults : target);
1861
1861
  }
@@ -1864,7 +1864,7 @@ function fillUndefined(target, defaults) {
1864
1864
  const d = defaults[key];
1865
1865
  const t = target[key];
1866
1866
  if (t === undefined || t === null) {
1867
- out[key] = clone(d);
1867
+ out[key] = clone$1(d);
1868
1868
  }
1869
1869
  else if (isObject(t) && isObject(d)) {
1870
1870
  out[key] = fillUndefined(t, d);
@@ -1875,7 +1875,7 @@ function fillUndefined(target, defaults) {
1875
1875
  }
1876
1876
  return out;
1877
1877
  }
1878
- function clone(v) {
1878
+ function clone$1(v) {
1879
1879
  if (v == null || typeof v !== 'object')
1880
1880
  return v;
1881
1881
  try {
@@ -2183,12 +2183,15 @@ class GenericCrudService {
2183
2183
  get errorMessages() {
2184
2184
  return GenericCrudService.ERROR_MESSAGES;
2185
2185
  }
2186
- configure(resourcePath, endpointKey) {
2186
+ configure(resourcePath, endpointKeyOrOptions) {
2187
2187
  if (!resourcePath || !resourcePath.trim()) {
2188
2188
  throw new Error(this.errorMessages.emptyResourcePath);
2189
2189
  }
2190
- const nextEndpointKey = endpointKey ?? this.currentEndpointKey;
2191
- const entry = this.resolveEndpointEntry(nextEndpointKey);
2190
+ const configureOptions = typeof endpointKeyOrOptions === 'object' && endpointKeyOrOptions !== null
2191
+ ? endpointKeyOrOptions
2192
+ : { endpointKey: endpointKeyOrOptions };
2193
+ const nextEndpointKey = configureOptions.endpointKey ?? this.currentEndpointKey;
2194
+ const entry = configureOptions.apiUrlEntry ?? this.resolveEndpointEntry(nextEndpointKey);
2192
2195
  const nextBaseApiUrl = buildApiUrl(entry);
2193
2196
  const base = nextBaseApiUrl.replace(/\/+$/, '');
2194
2197
  let resource = resourcePath.trim();
@@ -2963,6 +2966,16 @@ class GenericCrudService {
2963
2966
  })));
2964
2967
  }
2965
2968
  filter(filterCriteria, pageable, opts) {
2969
+ const response$ = opts
2970
+ ? this.filterResponse(filterCriteria, pageable, opts)
2971
+ : this.filterResponse(filterCriteria, pageable);
2972
+ return response$.pipe(map((resp) => {
2973
+ const body = this.unwrapPageResponse(resp?.data);
2974
+ const version = resp?.dataVersion;
2975
+ return version ? { ...body, dataVersion: version } : body;
2976
+ }));
2977
+ }
2978
+ filterResponse(filterCriteria, pageable, opts) {
2966
2979
  let params = new HttpParams()
2967
2980
  .set('page', pageable.pageNumber)
2968
2981
  .set('size', pageable.pageSize);
@@ -2987,12 +3000,13 @@ class GenericCrudService {
2987
3000
  headers: composeHeadersWithVersion(entry),
2988
3001
  context: opts?.httpContext,
2989
3002
  });
2990
- return observeVersion ? req$.pipe(map((resp) => {
2991
- const raw = resp.body;
2992
- const body = this.unwrapPageResponse(raw);
2993
- const version = resp.headers.get('X-Data-Version') || undefined;
2994
- return version ? { ...body, dataVersion: version } : body;
2995
- }), catchError(this.handleError)) : req$.pipe(map((raw) => this.unwrapPageResponse(raw)), catchError(this.handleError));
3003
+ return observeVersion
3004
+ ? req$.pipe(map((resp) => {
3005
+ const body = this.normalizeRestApiResponse(resp.body);
3006
+ const version = resp.headers.get('X-Data-Version') || undefined;
3007
+ return version ? { ...body, dataVersion: version } : body;
3008
+ }), catchError(this.handleError))
3009
+ : req$.pipe(map((raw) => this.normalizeRestApiResponse(raw)), catchError(this.handleError));
2996
3010
  }
2997
3011
  /** GET /by-ids */
2998
3012
  getByIds(ids, options) {
@@ -3693,6 +3707,12 @@ class GenericCrudService {
3693
3707
  }
3694
3708
  return raw;
3695
3709
  }
3710
+ normalizeRestApiResponse(raw) {
3711
+ if (raw && typeof raw === 'object' && 'data' in raw) {
3712
+ return raw;
3713
+ }
3714
+ return { data: raw };
3715
+ }
3696
3716
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: GenericCrudService, deps: [{ token: i1.HttpClient }, { token: SchemaNormalizerService }, { token: GlobalConfigService }, { token: API_URL }], target: i0.ɵɵFactoryTarget.Injectable });
3697
3717
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: GenericCrudService, providedIn: null });
3698
3718
  }
@@ -7012,7 +7032,7 @@ class DefaultLoadingRenderer {
7012
7032
  .pfx-loading-root {
7013
7033
  position: fixed;
7014
7034
  inset: 0;
7015
- z-index: 10000;
7035
+ z-index: var(--praxis-layer-loading-blocking, 2000);
7016
7036
  pointer-events: none;
7017
7037
  }
7018
7038
  .pfx-loading-entry {
@@ -7090,260 +7110,1293 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
7090
7110
  type: Injectable
7091
7111
  }] });
7092
7112
 
7093
- function provideFieldSelectorRegistryRuntime(map, opts) {
7094
- return {
7095
- provide: ENVIRONMENT_INITIALIZER,
7096
- multi: true,
7097
- useFactory: () => {
7098
- const registry = inject(FieldSelectorRegistry);
7099
- return () => registry.register(map, { overwrite: opts?.overwrite ?? false });
7100
- },
7101
- };
7102
- }
7103
-
7104
- function providePraxisToastGlobalActions(opts = {}) {
7105
- return {
7106
- provide: GLOBAL_TOAST_SERVICE,
7107
- useFactory: () => {
7108
- const snack = (() => { try {
7109
- return inject(MatSnackBar);
7110
- }
7111
- catch {
7112
- return null;
7113
- } })();
7114
- const successDuration = opts.successDurationMs ?? 2500;
7115
- const errorDuration = opts.errorDurationMs ?? 3500;
7116
- const successClass = opts.successPanelClass ?? ['pdx-toast-success'];
7117
- const errorClass = opts.errorPanelClass ?? ['pdx-toast-error'];
7118
- return {
7119
- success: (message) => {
7120
- if (snack) {
7121
- snack.open(message, undefined, { duration: successDuration, panelClass: successClass });
7122
- }
7123
- else {
7124
- console.log('[Toast]', message);
7125
- }
7126
- },
7127
- error: (message) => {
7128
- if (snack) {
7129
- snack.open(message, undefined, { duration: errorDuration, panelClass: errorClass });
7130
- }
7131
- else {
7132
- console.error('[Toast]', message);
7133
- }
7134
- },
7135
- };
7136
- },
7137
- };
7113
+ const PRAXIS_LAYER_SCALE_DEFAULTS = {
7114
+ stickyLocal: 100,
7115
+ floatingLocal: 200,
7116
+ authoringInsert: 280,
7117
+ authoringHover: 300,
7118
+ authoringSelected: 320,
7119
+ authoringToolbar: 340,
7120
+ cdkOverlayContainer: 1200,
7121
+ cdkOverlayBackdrop: 1200,
7122
+ cdkOverlayWrapper: 1210,
7123
+ settingsPanel: 1220,
7124
+ widgetShellBackdrop: 1280,
7125
+ widgetShellExpanded: 1290,
7126
+ widgetShellFullscreen: 1291,
7127
+ dialog: 1300,
7128
+ popup: 1400,
7129
+ loadingBlocking: 2000,
7130
+ };
7131
+ const PRAXIS_LAYER_SCALE_VARS = {
7132
+ stickyLocal: '--praxis-layer-sticky-local',
7133
+ floatingLocal: '--praxis-layer-floating-local',
7134
+ authoringInsert: '--praxis-layer-authoring-insert',
7135
+ authoringHover: '--praxis-layer-authoring-hover',
7136
+ authoringSelected: '--praxis-layer-authoring-selected',
7137
+ authoringToolbar: '--praxis-layer-authoring-toolbar',
7138
+ cdkOverlayContainer: '--praxis-layer-cdk-overlay-container',
7139
+ cdkOverlayBackdrop: '--praxis-layer-cdk-overlay-backdrop',
7140
+ cdkOverlayWrapper: '--praxis-layer-cdk-overlay-wrapper',
7141
+ settingsPanel: '--praxis-layer-settings-panel',
7142
+ widgetShellBackdrop: '--praxis-layer-widget-shell-backdrop',
7143
+ widgetShellExpanded: '--praxis-layer-widget-shell-expanded',
7144
+ widgetShellFullscreen: '--praxis-layer-widget-shell-fullscreen',
7145
+ dialog: '--praxis-layer-dialog',
7146
+ popup: '--praxis-layer-popup',
7147
+ loadingBlocking: '--praxis-layer-loading-blocking',
7148
+ };
7149
+ function buildPraxisLayerScaleCss(scale = PRAXIS_LAYER_SCALE_DEFAULTS) {
7150
+ return `
7151
+ :root {
7152
+ ${PRAXIS_LAYER_SCALE_VARS.stickyLocal}: ${scale.stickyLocal};
7153
+ ${PRAXIS_LAYER_SCALE_VARS.floatingLocal}: ${scale.floatingLocal};
7154
+ ${PRAXIS_LAYER_SCALE_VARS.authoringInsert}: ${scale.authoringInsert};
7155
+ ${PRAXIS_LAYER_SCALE_VARS.authoringHover}: ${scale.authoringHover};
7156
+ ${PRAXIS_LAYER_SCALE_VARS.authoringSelected}: ${scale.authoringSelected};
7157
+ ${PRAXIS_LAYER_SCALE_VARS.authoringToolbar}: ${scale.authoringToolbar};
7158
+ ${PRAXIS_LAYER_SCALE_VARS.cdkOverlayContainer}: ${scale.cdkOverlayContainer};
7159
+ ${PRAXIS_LAYER_SCALE_VARS.cdkOverlayBackdrop}: ${scale.cdkOverlayBackdrop};
7160
+ ${PRAXIS_LAYER_SCALE_VARS.cdkOverlayWrapper}: ${scale.cdkOverlayWrapper};
7161
+ ${PRAXIS_LAYER_SCALE_VARS.settingsPanel}: ${scale.settingsPanel};
7162
+ ${PRAXIS_LAYER_SCALE_VARS.widgetShellBackdrop}: ${scale.widgetShellBackdrop};
7163
+ ${PRAXIS_LAYER_SCALE_VARS.widgetShellExpanded}: ${scale.widgetShellExpanded};
7164
+ ${PRAXIS_LAYER_SCALE_VARS.widgetShellFullscreen}: ${scale.widgetShellFullscreen};
7165
+ ${PRAXIS_LAYER_SCALE_VARS.dialog}: ${scale.dialog};
7166
+ ${PRAXIS_LAYER_SCALE_VARS.popup}: ${scale.popup};
7167
+ ${PRAXIS_LAYER_SCALE_VARS.loadingBlocking}: ${scale.loadingBlocking};
7138
7168
  }
7139
7169
 
7140
- function providePraxisAnalyticsGlobalActions(opts = {}) {
7141
- return {
7142
- provide: GLOBAL_ANALYTICS_SERVICE,
7143
- useFactory: () => {
7144
- const telemetry = (() => { try {
7145
- return inject(TelemetryService);
7146
- }
7147
- catch {
7148
- return null;
7149
- } })();
7150
- const prefix = opts.prefix ? `${opts.prefix}.` : '';
7151
- return {
7152
- track: (eventName, payload) => {
7153
- if (telemetry) {
7154
- telemetry.record(`${prefix}${eventName}`, payload);
7155
- }
7156
- else {
7157
- console.log('[Analytics]', `${prefix}${eventName}`, payload ?? null);
7158
- }
7159
- },
7160
- };
7161
- },
7162
- };
7170
+ .cdk-overlay-container {
7171
+ z-index: var(${PRAXIS_LAYER_SCALE_VARS.cdkOverlayContainer}, ${scale.cdkOverlayContainer}) !important;
7163
7172
  }
7164
7173
 
7165
- /**
7166
- * Registers only the default visual renderer for Praxis loading.
7167
- *
7168
- * This does not wire any HTTP interceptor by itself. Use this when the host
7169
- * wants a custom HTTP integration path but still wants the stock Praxis UI.
7170
- */
7171
- function providePraxisLoadingDefaults() {
7172
- return [
7173
- {
7174
- provide: PRAXIS_LOADING_RENDERER,
7175
- useClass: DefaultLoadingRenderer,
7176
- },
7177
- ];
7178
- }
7179
- /**
7180
- * Official DI-based entrypoint to enable Praxis HTTP loading.
7181
- *
7182
- * Recommended when the host already uses DI interceptors, e.g.:
7183
- *
7184
- * ```ts
7185
- * providers: [
7186
- * provideHttpClient(withInterceptorsFromDi()),
7187
- * ...providePraxisHttpLoading(),
7188
- * ]
7189
- * ```
7190
- *
7191
- * By default this provider does not register any renderer implementation.
7192
- * Add `...providePraxisLoadingDefaults()` or a custom `PRAXIS_LOADING_RENDERER`
7193
- * explicitly so hosts keep full control over visual composition.
7194
- *
7195
- * Requests remain opt-in and only show loading when they carry
7196
- * `PRAXIS_LOADING_CTX` in the `HttpContext`.
7197
- */
7198
- function providePraxisHttpLoading(opts = {}) {
7199
- const providers = [
7200
- {
7201
- provide: HTTP_INTERCEPTORS,
7202
- useClass: PraxisLoadingInterceptor,
7203
- multi: true,
7204
- },
7205
- ];
7206
- if (opts.includeDefaultRenderer === true) {
7207
- providers.unshift(...providePraxisLoadingDefaults());
7208
- }
7209
- return providers;
7210
- }
7211
- /**
7212
- * Angular modern helper for `provideHttpClient(...)`.
7213
- *
7214
- * Usage:
7215
- *
7216
- * ```ts
7217
- * providers: [
7218
- * provideHttpClient(
7219
- * withInterceptors([authInterceptor]),
7220
- * withPraxisHttpLoading(),
7221
- * ),
7222
- * ...providePraxisLoadingDefaults(),
7223
- * ]
7224
- * ```
7225
- *
7226
- * Use this helper or `providePraxisHttpLoading()`, never both in the same host.
7227
- */
7228
- function withPraxisHttpLoading() {
7229
- return withInterceptors([praxisLoadingInterceptorFn]);
7174
+ .cdk-overlay-backdrop {
7175
+ z-index: var(${PRAXIS_LAYER_SCALE_VARS.cdkOverlayBackdrop}, ${scale.cdkOverlayBackdrop}) !important;
7230
7176
  }
7231
7177
 
7232
- const LOGGER_LEVEL_PRIORITY = {
7233
- error: 0,
7234
- warn: 1,
7235
- info: 2,
7236
- debug: 3,
7237
- trace: 4,
7238
- };
7239
- const LOGGER_LEVEL_BY_ENV = {
7240
- prod: 'warn',
7241
- qa: 'warn',
7242
- hml: 'info',
7243
- dev: 'debug',
7244
- };
7245
- const PRAXIS_CORPORATE_SENSITIVE_KEYS = [
7246
- 'password',
7247
- 'pass',
7248
- 'token',
7249
- 'accessToken',
7250
- 'refreshToken',
7251
- 'authorization',
7252
- 'secret',
7253
- 'apiKey',
7254
- 'cpf',
7255
- 'cnpj',
7256
- 'document',
7257
- 'ssn',
7258
- 'email',
7259
- 'phone',
7260
- ];
7261
- const PRAXIS_CORPORATE_THROTTLE = {
7262
- enabled: true,
7263
- windowMs: 5000,
7264
- maxOccurrences: 3,
7265
- };
7266
- function createCorporateLoggerConfig(environment = 'prod') {
7267
- return {
7268
- environment,
7269
- minLevel: LOGGER_LEVEL_BY_ENV[environment],
7270
- warnOnceEnabled: true,
7271
- throttle: { ...PRAXIS_CORPORATE_THROTTLE },
7272
- pii: {
7273
- enabled: true,
7274
- redactionText: '[REDACTED]',
7275
- sensitiveKeys: [...PRAXIS_CORPORATE_SENSITIVE_KEYS],
7276
- },
7277
- };
7178
+ .cdk-global-overlay-wrapper {
7179
+ z-index: var(${PRAXIS_LAYER_SCALE_VARS.cdkOverlayWrapper}, ${scale.cdkOverlayWrapper}) !important;
7278
7180
  }
7279
- function resolveLoggerConfig(options = {}) {
7280
- const environment = options.environment ?? 'prod';
7281
- const corporate = createCorporateLoggerConfig(environment);
7282
- const overridePII = options.pii ?? {};
7283
- return {
7284
- ...corporate,
7285
- minLevel: options.minLevel ?? corporate.minLevel,
7286
- warnOnceEnabled: options.warnOnceEnabled ?? corporate.warnOnceEnabled,
7287
- throttle: {
7288
- ...corporate.throttle,
7289
- ...(options.throttle ?? {}),
7290
- },
7291
- pii: {
7292
- ...corporate.pii,
7293
- ...overridePII,
7294
- sensitiveKeys: overridePII.sensitiveKeys
7295
- ? [...overridePII.sensitiveKeys]
7296
- : [...corporate.pii.sensitiveKeys],
7297
- },
7298
- defaultContext: options.defaultContext
7299
- ? { ...options.defaultContext }
7300
- : corporate.defaultContext,
7301
- environment,
7302
- };
7181
+ `;
7303
7182
  }
7304
7183
 
7305
- class ConsoleLoggerSink {
7306
- log(event) {
7307
- const print = this.resolveMethod(event.level);
7308
- const prefix = `[Praxis][${event.level.toUpperCase()}]`;
7309
- const envelope = {
7310
- environment: event.environment,
7311
- timestamp: event.timestamp,
7312
- context: event.context ?? null,
7313
- data: event.data ?? null,
7314
- };
7315
- if (event.context || event.data !== undefined) {
7316
- print(`${prefix} ${event.message}`, envelope);
7317
- return;
7318
- }
7319
- print(`${prefix} ${event.message}`);
7184
+ class PraxisLayerScaleStyleService {
7185
+ doc = inject(DOCUMENT);
7186
+ styleId = 'praxis-layer-scale';
7187
+ constructor() {
7188
+ this.ensureInstalled();
7320
7189
  }
7321
- resolveMethod(level) {
7322
- if (level === 'error' && typeof console.error === 'function') {
7323
- return console.error.bind(console);
7324
- }
7325
- if (level === 'warn' && typeof console.warn === 'function') {
7326
- return console.warn.bind(console);
7327
- }
7328
- if (level === 'info' && typeof console.info === 'function') {
7329
- return console.info.bind(console);
7330
- }
7331
- if (level === 'debug' && typeof console.debug === 'function') {
7332
- return console.debug.bind(console);
7190
+ ensureInstalled() {
7191
+ const doc = this.doc;
7192
+ if (!doc?.head) {
7193
+ return;
7333
7194
  }
7334
- if (level === 'trace') {
7335
- if (typeof console.trace === 'function') {
7336
- return console.trace.bind(console);
7337
- }
7338
- if (typeof console.debug === 'function') {
7339
- return console.debug.bind(console);
7195
+ const css = buildPraxisLayerScaleCss();
7196
+ const existing = doc.getElementById(this.styleId);
7197
+ if (existing) {
7198
+ if (existing.textContent !== css) {
7199
+ existing.textContent = css;
7340
7200
  }
7201
+ return;
7341
7202
  }
7342
- return console.log.bind(console);
7203
+ const style = doc.createElement('style');
7204
+ style.id = this.styleId;
7205
+ style.textContent = css;
7206
+ doc.head.appendChild(style);
7343
7207
  }
7208
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisLayerScaleStyleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
7209
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisLayerScaleStyleService, providedIn: 'root' });
7344
7210
  }
7211
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisLayerScaleStyleService, decorators: [{
7212
+ type: Injectable,
7213
+ args: [{ providedIn: 'root' }]
7214
+ }], ctorParameters: () => [] });
7345
7215
 
7346
- const PRAXIS_LOGGER_CONFIG = new InjectionToken('PRAXIS_LOGGER_CONFIG', {
7216
+ class ResourceDiscoveryService {
7217
+ http = inject(HttpClient);
7218
+ schemaNormalizer = inject(SchemaNormalizerService);
7219
+ apiUrlConfig = inject(API_URL);
7220
+ getLinks(source, rel) {
7221
+ const candidate = this.extractLinks(source)?.[rel];
7222
+ if (!candidate) {
7223
+ return [];
7224
+ }
7225
+ return Array.isArray(candidate) ? candidate.filter(Boolean) : [candidate];
7226
+ }
7227
+ getLink(source, rel) {
7228
+ return this.getLinks(source, rel)[0] ?? null;
7229
+ }
7230
+ hasLink(source, rel) {
7231
+ return this.getLinks(source, rel).length > 0;
7232
+ }
7233
+ resolveLinkHref(source, rel, options) {
7234
+ const link = this.getLink(source, rel);
7235
+ return link?.href ? this.resolveHref(link.href, options) : null;
7236
+ }
7237
+ followLink(source, rel, options) {
7238
+ return this.fetchJson(this.requireLinkHref(source, rel, options), options);
7239
+ }
7240
+ getSurfaces(source, options) {
7241
+ return this.followLink(source, 'surfaces', options);
7242
+ }
7243
+ getActions(source, options) {
7244
+ return this.followLink(source, 'actions', options);
7245
+ }
7246
+ getCapabilities(source, options) {
7247
+ return this.followLink(source, 'capabilities', options);
7248
+ }
7249
+ fetchJson(href, options) {
7250
+ return this.http.get(this.resolveHref(href, options));
7251
+ }
7252
+ getSchemaByUrl(schemaUrl, options) {
7253
+ return this.fetchJson(schemaUrl, options);
7254
+ }
7255
+ getSchemaFieldsByUrl(schemaUrl, options) {
7256
+ return this.getSchemaByUrl(schemaUrl, options).pipe(map$1((schema) => this.schemaNormalizer.normalizeSchema(schema)));
7257
+ }
7258
+ resolveHref(href, options) {
7259
+ const normalizedHref = String(href || '').trim();
7260
+ if (!normalizedHref) {
7261
+ throw new Error('ResourceDiscoveryService cannot resolve an empty href.');
7262
+ }
7263
+ if (/^https?:\/\//i.test(normalizedHref)) {
7264
+ return normalizedHref;
7265
+ }
7266
+ const apiBaseUrl = this.resolveApiBaseHref(options);
7267
+ if (!apiBaseUrl) {
7268
+ return normalizedHref;
7269
+ }
7270
+ if (normalizedHref.startsWith('/')) {
7271
+ const apiOrigin = this.resolveApiOrigin(options);
7272
+ return apiOrigin
7273
+ ? new URL(normalizedHref, `${apiOrigin}/`).toString()
7274
+ : normalizedHref;
7275
+ }
7276
+ return new URL(normalizedHref, this.ensureTrailingSlash(apiBaseUrl)).toString();
7277
+ }
7278
+ requireLinkHref(source, rel, options) {
7279
+ const href = this.resolveLinkHref(source, rel, options);
7280
+ if (!href) {
7281
+ throw new Error(`ResourceDiscoveryService could not resolve rel "${rel}".`);
7282
+ }
7283
+ return href;
7284
+ }
7285
+ extractLinks(source) {
7286
+ if (!source) {
7287
+ return undefined;
7288
+ }
7289
+ if (this.isRestApiResponse(source)) {
7290
+ return source._links;
7291
+ }
7292
+ return source;
7293
+ }
7294
+ resolveApiEntry(options) {
7295
+ if (options?.apiUrlEntry) {
7296
+ return options.apiUrlEntry;
7297
+ }
7298
+ return this.resolveEndpointEntry(options?.endpointKey);
7299
+ }
7300
+ resolveApiOrigin(options) {
7301
+ const baseUrl = this.resolveApiBaseUrl(options);
7302
+ if (!baseUrl) {
7303
+ return '';
7304
+ }
7305
+ try {
7306
+ return new URL(baseUrl).origin.replace(/\/+$/, '');
7307
+ }
7308
+ catch {
7309
+ const runtimeOrigin = this.getRuntimeOrigin();
7310
+ return runtimeOrigin ? runtimeOrigin.replace(/\/+$/, '') : '';
7311
+ }
7312
+ }
7313
+ resolveApiBaseUrl(options) {
7314
+ const entry = this.resolveApiEntry(options);
7315
+ return String(buildApiUrl(entry) || '').trim();
7316
+ }
7317
+ resolveApiBaseHref(options) {
7318
+ const baseUrl = this.resolveApiBaseUrl(options);
7319
+ if (!baseUrl) {
7320
+ return '';
7321
+ }
7322
+ try {
7323
+ return new URL(baseUrl).toString();
7324
+ }
7325
+ catch {
7326
+ const runtimeOrigin = this.getRuntimeOrigin();
7327
+ return runtimeOrigin
7328
+ ? new URL(baseUrl.replace(/^\/+/, ''), this.ensureTrailingSlash(runtimeOrigin)).toString()
7329
+ : '';
7330
+ }
7331
+ }
7332
+ resolveEndpointEntry(endpointKey) {
7333
+ const key = endpointKey ?? ApiEndpoint.Default;
7334
+ return this.apiUrlConfig[key] || this.apiUrlConfig.default || {};
7335
+ }
7336
+ getRuntimeOrigin() {
7337
+ try {
7338
+ const origin = globalThis?.location?.origin;
7339
+ return typeof origin === 'string' && origin.startsWith('http') ? origin : undefined;
7340
+ }
7341
+ catch {
7342
+ return undefined;
7343
+ }
7344
+ }
7345
+ ensureTrailingSlash(value) {
7346
+ return /\/$/.test(value) ? value : `${value}/`;
7347
+ }
7348
+ isRestApiResponse(source) {
7349
+ return 'data' in source;
7350
+ }
7351
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceDiscoveryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
7352
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceDiscoveryService, providedIn: 'any' });
7353
+ }
7354
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceDiscoveryService, decorators: [{
7355
+ type: Injectable,
7356
+ args: [{ providedIn: 'any' }]
7357
+ }] });
7358
+
7359
+ const SURFACE_OPEN_PRESETS = [
7360
+ {
7361
+ id: 'praxis-dynamic-form',
7362
+ label: 'Formulário',
7363
+ description: 'Abre um formulário dinâmico com resourcePath, formId e resourceId.',
7364
+ payload: {
7365
+ presentation: 'drawer',
7366
+ widget: {
7367
+ id: 'praxis-dynamic-form',
7368
+ bindingOrder: ['resourcePath', 'formId', 'resourceId'],
7369
+ inputs: {
7370
+ resourcePath: '',
7371
+ formId: '',
7372
+ mode: 'view',
7373
+ },
7374
+ },
7375
+ bindings: [],
7376
+ },
7377
+ },
7378
+ {
7379
+ id: 'praxis-table',
7380
+ label: 'Tabela',
7381
+ description: 'Abre uma tabela filtrável usando inputs declarativos e queryContext.',
7382
+ payload: {
7383
+ presentation: 'drawer',
7384
+ widget: {
7385
+ id: 'praxis-table',
7386
+ bindingOrder: ['resourcePath', 'tableId', 'queryContext'],
7387
+ inputs: {
7388
+ resourcePath: '',
7389
+ tableId: '',
7390
+ queryContext: {
7391
+ filters: {},
7392
+ },
7393
+ },
7394
+ },
7395
+ bindings: [],
7396
+ },
7397
+ },
7398
+ {
7399
+ id: 'praxis-list',
7400
+ label: 'Lista',
7401
+ description: 'Abre uma lista com config/listId e bindings configuráveis.',
7402
+ payload: {
7403
+ presentation: 'drawer',
7404
+ widget: {
7405
+ id: 'praxis-list',
7406
+ bindingOrder: ['listId', 'config'],
7407
+ inputs: {
7408
+ listId: '',
7409
+ config: {},
7410
+ },
7411
+ },
7412
+ bindings: [],
7413
+ },
7414
+ },
7415
+ {
7416
+ id: 'praxis-crud',
7417
+ label: 'CRUD',
7418
+ description: 'Abre a surface de CRUD com inputs mínimos e bindings configuráveis.',
7419
+ payload: {
7420
+ presentation: 'drawer',
7421
+ widget: {
7422
+ id: 'praxis-crud',
7423
+ bindingOrder: ['resourcePath', 'formId', 'resourceId'],
7424
+ inputs: {
7425
+ resourcePath: '',
7426
+ formId: '',
7427
+ },
7428
+ },
7429
+ bindings: [],
7430
+ },
7431
+ },
7432
+ ];
7433
+
7434
+ class ResourceActionOpenAdapterService {
7435
+ discovery = inject(ResourceDiscoveryService);
7436
+ toPayload(action, options) {
7437
+ const resourcePath = this.normalizeResourcePath(options.resourcePath);
7438
+ if (!resourcePath) {
7439
+ throw new Error('ResourceActionOpenAdapterService requires resourcePath.');
7440
+ }
7441
+ if (!action.requestSchemaUrl) {
7442
+ throw new Error(`ResourceActionOpenAdapterService requires requestSchemaUrl for action "${action.id}".`);
7443
+ }
7444
+ const discoveryOptions = options.endpointKey || options.apiUrlEntry
7445
+ ? {
7446
+ ...(options.endpointKey ? { endpointKey: options.endpointKey } : {}),
7447
+ ...(options.apiUrlEntry ? { apiUrlEntry: options.apiUrlEntry } : {}),
7448
+ }
7449
+ : undefined;
7450
+ const resolvedRequestSchemaUrl = this.discovery.resolveHref(action.requestSchemaUrl, discoveryOptions);
7451
+ const resolvedResponseSchemaUrl = action.responseSchemaUrl
7452
+ ? this.discovery.resolveHref(action.responseSchemaUrl, discoveryOptions)
7453
+ : null;
7454
+ const resolvedSubmitUrl = this.discovery.resolveHref(action.path, discoveryOptions);
7455
+ const payload = this.clone(this.resolveDynamicFormPreset());
7456
+ payload.presentation = options.presentation ?? payload.presentation;
7457
+ payload.title = options.title ?? action.title ?? payload.title;
7458
+ payload.subtitle = options.subtitle ?? action.description ?? payload.subtitle;
7459
+ payload.icon = options.icon ?? payload.icon;
7460
+ payload.context = {
7461
+ resource: {
7462
+ resourceKey: action.resourceKey,
7463
+ resourcePath,
7464
+ resourceId: options.resourceId ?? null,
7465
+ group: options.group ?? null,
7466
+ },
7467
+ action: {
7468
+ id: action.id,
7469
+ scope: action.scope,
7470
+ operationId: action.operationId,
7471
+ path: action.path,
7472
+ method: action.method,
7473
+ requestSchemaId: action.requestSchemaId ?? null,
7474
+ requestSchemaUrl: action.requestSchemaUrl,
7475
+ responseSchemaId: action.responseSchemaId ?? null,
7476
+ responseSchemaUrl: action.responseSchemaUrl ?? null,
7477
+ availability: action.availability,
7478
+ successMessage: action.successMessage ?? null,
7479
+ tags: action.tags,
7480
+ },
7481
+ };
7482
+ payload.widget.inputs = {
7483
+ ...(payload.widget.inputs || {}),
7484
+ resourcePath,
7485
+ formId: this.buildStableInstanceId(action),
7486
+ mode: 'create',
7487
+ schemaUrl: resolvedRequestSchemaUrl,
7488
+ apiEndpointKey: options.endpointKey ?? null,
7489
+ apiUrlEntry: options.apiUrlEntry ?? (discoveryOptions ? this.discovery.resolveApiEntry(discoveryOptions) : null),
7490
+ submitMethod: String(action.method || '').trim().toLowerCase(),
7491
+ submitUrl: resolvedSubmitUrl,
7492
+ responseSchemaUrl: resolvedResponseSchemaUrl,
7493
+ };
7494
+ if (action.scope === 'ITEM') {
7495
+ if (options.resourceId != null) {
7496
+ payload.widget.inputs['resourceId'] = options.resourceId;
7497
+ }
7498
+ else if (options.idBindingPath) {
7499
+ payload.bindings = [
7500
+ ...(payload.bindings || []),
7501
+ this.buildIdBinding(options.idBindingPath),
7502
+ ];
7503
+ }
7504
+ else {
7505
+ throw new Error(`ResourceActionOpenAdapterService requires resourceId or idBindingPath for item action "${action.id}".`);
7506
+ }
7507
+ }
7508
+ return payload;
7509
+ }
7510
+ resolveDynamicFormPreset() {
7511
+ const preset = SURFACE_OPEN_PRESETS.find((candidate) => candidate.id === 'praxis-dynamic-form');
7512
+ if (!preset) {
7513
+ throw new Error('Missing canonical surface preset "praxis-dynamic-form".');
7514
+ }
7515
+ return preset.payload;
7516
+ }
7517
+ buildStableInstanceId(action) {
7518
+ return `${action.resourceKey}.action.${action.id}`.replace(/[^a-zA-Z0-9._-]+/g, '-');
7519
+ }
7520
+ normalizeResourcePath(resourcePath) {
7521
+ return String(resourcePath || '').trim().replace(/^\/+/, '').replace(/\/+$/, '');
7522
+ }
7523
+ buildIdBinding(idBindingPath) {
7524
+ return {
7525
+ from: idBindingPath,
7526
+ to: 'widget.inputs.resourceId',
7527
+ mode: 'path',
7528
+ };
7529
+ }
7530
+ clone(value) {
7531
+ return value == null ? value : JSON.parse(JSON.stringify(value));
7532
+ }
7533
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceActionOpenAdapterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
7534
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceActionOpenAdapterService, providedIn: 'any' });
7535
+ }
7536
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceActionOpenAdapterService, decorators: [{
7537
+ type: Injectable,
7538
+ args: [{ providedIn: 'any' }]
7539
+ }] });
7540
+
7541
+ class ResourceSurfaceOpenAdapterService {
7542
+ discovery = inject(ResourceDiscoveryService);
7543
+ toPayload(surface, options) {
7544
+ const resourcePath = this.normalizeResourcePath(options.resourcePath);
7545
+ if (!resourcePath) {
7546
+ throw new Error('ResourceSurfaceOpenAdapterService requires resourcePath.');
7547
+ }
7548
+ const discoveryOptions = options.endpointKey || options.apiUrlEntry
7549
+ ? {
7550
+ ...(options.endpointKey ? { endpointKey: options.endpointKey } : {}),
7551
+ ...(options.apiUrlEntry ? { apiUrlEntry: options.apiUrlEntry } : {}),
7552
+ }
7553
+ : undefined;
7554
+ const resolvedSchemaUrl = this.discovery.resolveHref(surface.schemaUrl, discoveryOptions);
7555
+ const resolvedSubmitUrl = this.isWritableFormSurface(surface.kind)
7556
+ ? this.discovery.resolveHref(surface.path, discoveryOptions)
7557
+ : null;
7558
+ const basePayload = this.buildBasePayload(surface);
7559
+ const payload = {
7560
+ ...basePayload,
7561
+ presentation: options.presentation ?? basePayload.presentation,
7562
+ title: options.title ?? surface.title ?? basePayload.title,
7563
+ subtitle: options.subtitle ?? surface.description ?? basePayload.subtitle,
7564
+ icon: options.icon ?? basePayload.icon,
7565
+ context: {
7566
+ resource: {
7567
+ resourceKey: surface.resourceKey,
7568
+ resourcePath,
7569
+ resourceId: options.resourceId ?? null,
7570
+ group: options.group ?? null,
7571
+ },
7572
+ surface: {
7573
+ id: surface.id,
7574
+ kind: surface.kind,
7575
+ scope: surface.scope,
7576
+ intent: surface.intent ?? null,
7577
+ operationId: surface.operationId,
7578
+ path: surface.path,
7579
+ method: surface.method,
7580
+ schemaId: surface.schemaId,
7581
+ schemaUrl: surface.schemaUrl,
7582
+ availability: surface.availability,
7583
+ tags: surface.tags,
7584
+ },
7585
+ },
7586
+ };
7587
+ if (basePayload.widget.id === 'praxis-table') {
7588
+ payload.widget.inputs = {
7589
+ ...(payload.widget.inputs || {}),
7590
+ resourcePath,
7591
+ tableId: this.buildStableInstanceId(surface),
7592
+ queryContext: options.queryContext ?? payload.widget.inputs?.['queryContext'] ?? { filters: {} },
7593
+ };
7594
+ return payload;
7595
+ }
7596
+ const mode = this.resolveFormMode(surface.kind, surface.scope);
7597
+ payload.widget.inputs = {
7598
+ ...(payload.widget.inputs || {}),
7599
+ resourcePath,
7600
+ formId: this.buildStableInstanceId(surface),
7601
+ mode,
7602
+ schemaUrl: resolvedSchemaUrl,
7603
+ apiEndpointKey: options.endpointKey ?? null,
7604
+ apiUrlEntry: options.apiUrlEntry ?? (discoveryOptions ? this.discovery.resolveApiEntry(discoveryOptions) : null),
7605
+ };
7606
+ if (this.isWritableFormSurface(surface.kind)) {
7607
+ payload.widget.inputs['submitMethod'] = surface.method.toLowerCase();
7608
+ payload.widget.inputs['submitUrl'] = resolvedSubmitUrl;
7609
+ }
7610
+ if (surface.scope === 'ITEM') {
7611
+ if (options.resourceId != null) {
7612
+ payload.widget.inputs['resourceId'] = options.resourceId;
7613
+ }
7614
+ else if (options.idBindingPath) {
7615
+ payload.bindings = [
7616
+ ...(payload.bindings || []),
7617
+ this.buildIdBinding(options.idBindingPath),
7618
+ ];
7619
+ }
7620
+ else {
7621
+ throw new Error(`ResourceSurfaceOpenAdapterService requires resourceId or idBindingPath for item surface "${surface.id}".`);
7622
+ }
7623
+ }
7624
+ return payload;
7625
+ }
7626
+ buildBasePayload(surface) {
7627
+ const presetId = surface.scope === 'COLLECTION' && this.isCollectionView(surface.kind)
7628
+ ? 'praxis-table'
7629
+ : 'praxis-dynamic-form';
7630
+ const preset = SURFACE_OPEN_PRESETS.find((candidate) => candidate.id === presetId);
7631
+ if (!preset) {
7632
+ throw new Error(`Missing canonical surface preset "${presetId}".`);
7633
+ }
7634
+ return this.clone(preset.payload);
7635
+ }
7636
+ buildStableInstanceId(surface) {
7637
+ return `${surface.resourceKey}.${surface.id}`.replace(/[^a-zA-Z0-9._-]+/g, '-');
7638
+ }
7639
+ normalizeResourcePath(resourcePath) {
7640
+ return String(resourcePath || '').trim().replace(/^\/+/, '').replace(/\/+$/, '');
7641
+ }
7642
+ resolveFormMode(kind, scope) {
7643
+ if (kind === 'VIEW' || kind === 'READ_PROJECTION') {
7644
+ return 'view';
7645
+ }
7646
+ if (scope === 'ITEM') {
7647
+ return 'edit';
7648
+ }
7649
+ return 'create';
7650
+ }
7651
+ isCollectionView(kind) {
7652
+ return kind === 'VIEW' || kind === 'READ_PROJECTION';
7653
+ }
7654
+ isWritableFormSurface(kind) {
7655
+ return kind === 'FORM' || kind === 'PARTIAL_FORM';
7656
+ }
7657
+ buildIdBinding(idBindingPath) {
7658
+ return {
7659
+ from: idBindingPath,
7660
+ to: 'widget.inputs.resourceId',
7661
+ mode: 'path',
7662
+ };
7663
+ }
7664
+ clone(value) {
7665
+ return value == null ? value : JSON.parse(JSON.stringify(value));
7666
+ }
7667
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceSurfaceOpenAdapterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
7668
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceSurfaceOpenAdapterService, providedIn: 'any' });
7669
+ }
7670
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceSurfaceOpenAdapterService, decorators: [{
7671
+ type: Injectable,
7672
+ args: [{ providedIn: 'any' }]
7673
+ }] });
7674
+
7675
+ const DEFAULT_FAMILIES = [
7676
+ 'chart',
7677
+ 'analytic-table',
7678
+ 'kpi',
7679
+ 'summary-list',
7680
+ ];
7681
+ const ANALYTIC_TABLE_OPERATIONS = new Set(['group-by', 'timeseries', 'distribution']);
7682
+ class AnalyticsPresentationResolver {
7683
+ resolve(analytics, options) {
7684
+ const projections = analytics?.projections?.filter(Boolean) ?? [];
7685
+ if (!projections.length) {
7686
+ throw new Error('AnalyticsPresentationResolver requires at least one analytics projection.');
7687
+ }
7688
+ const availableFamilies = this.normalizeFamilies(options?.availableFamilies);
7689
+ const preferredDecision = this.tryResolvePreferredProjection(projections, availableFamilies, options?.preferFamily ?? null);
7690
+ if (preferredDecision) {
7691
+ return preferredDecision;
7692
+ }
7693
+ for (const projection of projections) {
7694
+ const inferredDecision = this.inferDecision(projection, availableFamilies);
7695
+ if (inferredDecision) {
7696
+ return inferredDecision;
7697
+ }
7698
+ }
7699
+ throw new Error('AnalyticsPresentationResolver could not resolve a semantic presentation family. Expose analytic-table or publish stronger semantic hints.');
7700
+ }
7701
+ tryResolvePreferredProjection(projections, availableFamilies, preferredFamily) {
7702
+ if (preferredFamily) {
7703
+ for (const projection of projections) {
7704
+ if (this.supportsFamily(projection, preferredFamily, availableFamilies)) {
7705
+ return {
7706
+ projectionId: projection.id,
7707
+ family: preferredFamily,
7708
+ reason: `Preferencia explicita do host pelo renderer "${preferredFamily}".`,
7709
+ };
7710
+ }
7711
+ }
7712
+ }
7713
+ for (const projection of projections) {
7714
+ const compatibleFamilies = this.getCompatiblePreferredFamilies(projection, availableFamilies);
7715
+ if (!compatibleFamilies.length) {
7716
+ continue;
7717
+ }
7718
+ return {
7719
+ projectionId: projection.id,
7720
+ family: compatibleFamilies[0],
7721
+ reason: `Projection "${projection.id}" publicou a familia preferencial "${compatibleFamilies[0]}".`,
7722
+ };
7723
+ }
7724
+ return null;
7725
+ }
7726
+ inferDecision(projection, availableFamilies) {
7727
+ if (this.supportsFamily(projection, 'chart', availableFamilies)) {
7728
+ return {
7729
+ projectionId: projection.id,
7730
+ family: 'chart',
7731
+ reason: this.buildInferenceReason(projection, 'chart'),
7732
+ };
7733
+ }
7734
+ if (this.supportsFamily(projection, 'analytic-table', availableFamilies)) {
7735
+ return {
7736
+ projectionId: projection.id,
7737
+ family: 'analytic-table',
7738
+ reason: this.buildInferenceReason(projection, 'analytic-table'),
7739
+ };
7740
+ }
7741
+ return null;
7742
+ }
7743
+ buildInferenceReason(projection, family) {
7744
+ if (family === 'chart') {
7745
+ return `Projection "${projection.id}" foi inferida como chart por combinar trend + timeseries + eixo temporal.`;
7746
+ }
7747
+ return `Projection "${projection.id}" foi resolvida como analytic-table por preservar a leitura semantica declarada sem forcar um chart ambiguo.`;
7748
+ }
7749
+ getCompatiblePreferredFamilies(projection, availableFamilies) {
7750
+ const preferredFamilies = projection.presentationHints?.preferredFamilies ?? [];
7751
+ return preferredFamilies.filter((family) => this.supportsFamily(projection, family, availableFamilies));
7752
+ }
7753
+ supportsFamily(projection, family, availableFamilies) {
7754
+ if (!availableFamilies.includes(family)) {
7755
+ return false;
7756
+ }
7757
+ if (family === 'chart') {
7758
+ return this.isChartInequivocal(projection) || this.hasExplicitPreferredFamily(projection, 'chart');
7759
+ }
7760
+ if (family === 'analytic-table') {
7761
+ return this.isAnalyticTableEligible(projection) || this.hasExplicitPreferredFamily(projection, 'analytic-table');
7762
+ }
7763
+ return this.hasExplicitPreferredFamily(projection, family);
7764
+ }
7765
+ isChartInequivocal(projection) {
7766
+ return (projection.intent === 'trend' &&
7767
+ projection.source.operation === 'timeseries' &&
7768
+ projection.bindings.primaryDimension?.role === 'time');
7769
+ }
7770
+ isAnalyticTableEligible(projection) {
7771
+ return (projection.bindings.primaryMetrics.length > 0 &&
7772
+ ANALYTIC_TABLE_OPERATIONS.has(projection.source.operation) &&
7773
+ (!!projection.bindings.primaryDimension || projection.bindings.primaryMetrics.length > 0));
7774
+ }
7775
+ hasExplicitPreferredFamily(projection, family) {
7776
+ return (projection.presentationHints?.preferredFamilies ?? []).includes(family);
7777
+ }
7778
+ normalizeFamilies(families) {
7779
+ const normalized = (families ?? DEFAULT_FAMILIES).filter(Boolean);
7780
+ return normalized.length ? normalized : [...DEFAULT_FAMILIES];
7781
+ }
7782
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AnalyticsPresentationResolver, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
7783
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AnalyticsPresentationResolver, providedIn: 'root' });
7784
+ }
7785
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AnalyticsPresentationResolver, decorators: [{
7786
+ type: Injectable,
7787
+ args: [{ providedIn: 'root' }]
7788
+ }] });
7789
+
7790
+ async function parseJsonResponseOrEmpty(res, context = 'response') {
7791
+ const raw = await res.text();
7792
+ if (!raw || !raw.trim()) {
7793
+ return {};
7794
+ }
7795
+ try {
7796
+ return JSON.parse(raw);
7797
+ }
7798
+ catch (error) {
7799
+ const e = new Error(`${context}: invalid JSON payload`);
7800
+ e.cause = error;
7801
+ throw e;
7802
+ }
7803
+ }
7804
+ async function fetchWithETag(params) {
7805
+ const headers = { Accept: 'application/json' };
7806
+ if (params.schemaHash)
7807
+ headers['If-None-Match'] = `"${params.schemaHash}"`;
7808
+ if (params.tenant)
7809
+ headers['X-Tenant'] = params.tenant;
7810
+ if (params.locale)
7811
+ headers['Accept-Language'] = params.locale;
7812
+ // Allow host apps to provide additional headers (e.g., Authorization) without coupling
7813
+ try {
7814
+ const extra = globalThis?.PAX_FETCH_HEADERS?.();
7815
+ if (extra && typeof extra === 'object') {
7816
+ for (const [k, v] of Object.entries(extra)) {
7817
+ const key = String(k);
7818
+ const val = Array.isArray(v) ? String(v[0]) : String(v);
7819
+ if (val && !headers[key])
7820
+ headers[key] = val;
7821
+ }
7822
+ }
7823
+ }
7824
+ catch {
7825
+ // ignore header hook errors
7826
+ }
7827
+ const res = await fetch(params.url, {
7828
+ headers,
7829
+ cache: 'no-cache',
7830
+ credentials: params.credentials ?? 'include',
7831
+ signal: params.signal,
7832
+ });
7833
+ if (res.status === 304)
7834
+ return { status: 304 };
7835
+ if (!res.ok) {
7836
+ const raw = await res.text().catch(() => '');
7837
+ const bodyHint = raw?.trim() ? `: ${raw.trim().slice(0, 240)}` : '';
7838
+ throw new Error(`fetchWithETag failed (${res.status} ${res.statusText || 'HTTP error'})${bodyHint}`);
7839
+ }
7840
+ const etag = res.headers.get('ETag') || '';
7841
+ const newHash = etag.replace(/^W\//, '').replace(/^\"|\"$/g, '');
7842
+ const xSchemaHash = res.headers.get('X-Schema-Hash');
7843
+ const schemaHash = xSchemaHash || newHash; // prefer explicit header when available
7844
+ const schema = await parseJsonResponseOrEmpty(res, 'fetchWithETag');
7845
+ return { status: 200, schema, schemaHash };
7846
+ }
7847
+
7848
+ class SchemaMetadataClient {
7849
+ cache;
7850
+ inFlight = new Map();
7851
+ constructor(cache) {
7852
+ this.cache = cache;
7853
+ }
7854
+ async getSchema(params) {
7855
+ // Bind identity to API origin to avoid cross-origin collisions
7856
+ let apiOrigin = '';
7857
+ try {
7858
+ apiOrigin = new URL(params.baseUrl).origin;
7859
+ }
7860
+ catch {
7861
+ try {
7862
+ apiOrigin = globalThis?.location?.origin || '';
7863
+ }
7864
+ catch { }
7865
+ }
7866
+ const schemaId = buildSchemaId({ ...params, apiOrigin });
7867
+ const cached = await this.cache.get(schemaId);
7868
+ // Build URL with query params
7869
+ let u;
7870
+ try {
7871
+ u = new URL(params.baseUrl);
7872
+ }
7873
+ catch (err) {
7874
+ // Accept relative baseUrl by resolving against the app origin when available
7875
+ const origin = (() => {
7876
+ try {
7877
+ return globalThis?.location?.origin;
7878
+ }
7879
+ catch {
7880
+ return undefined;
7881
+ }
7882
+ })();
7883
+ if (origin && origin.startsWith('http')) {
7884
+ u = new URL(params.baseUrl, origin);
7885
+ }
7886
+ else {
7887
+ // Friendly error for hosts: instruct to set absolute baseUrl or provide runtime origin
7888
+ const e = new Error(`Failed to construct schema URL. API_URL.baseUrl appears relative and no runtime origin is available. ` +
7889
+ `Set API_URL.baseUrl to an absolute URL (e.g., http://localhost:4200/api) or run in a browser context. ` +
7890
+ `Received baseUrl="${params.baseUrl}".`);
7891
+ e.cause = err;
7892
+ throw e;
7893
+ }
7894
+ }
7895
+ u.searchParams.set('path', params.path);
7896
+ u.searchParams.set('operation', (params.operation || 'get').toLowerCase());
7897
+ u.searchParams.set('schemaType', (params.schemaType || 'response').toLowerCase());
7898
+ u.searchParams.set('includeInternalSchemas', String(!!params.includeInternalSchemas));
7899
+ const key = schemaId;
7900
+ if (this.inFlight.has(key))
7901
+ return this.inFlight.get(key);
7902
+ const p = (async () => {
7903
+ const res = await fetchWithETag({
7904
+ url: u.toString(),
7905
+ schemaHash: cached?.schemaHash,
7906
+ tenant: params.tenant,
7907
+ locale: params.locale,
7908
+ });
7909
+ if (res.status === 304) {
7910
+ if (cached)
7911
+ return cached;
7912
+ // No cache: refetch without If-None-Match
7913
+ const refetch = await fetch(u.toString(), { cache: 'no-cache', credentials: 'include' });
7914
+ if (!refetch.ok) {
7915
+ const raw = await refetch.text().catch(() => '');
7916
+ const bodyHint = raw?.trim() ? `: ${raw.trim().slice(0, 240)}` : '';
7917
+ throw new Error(`SchemaMetadataClient refetch failed (${refetch.status} ${refetch.statusText || 'HTTP error'})${bodyHint}`);
7918
+ }
7919
+ const etag = refetch.headers.get('ETag') || '';
7920
+ const xHash = refetch.headers.get('X-Schema-Hash') || '';
7921
+ const schemaHash = (xHash || etag).replace(/^W\//, '').replace(/^\"|\"$/g, '');
7922
+ const schema = await parseJsonResponseOrEmpty(refetch, 'SchemaMetadataClient refetch');
7923
+ const fallback = { schema, schemaHash };
7924
+ await this.cache.set(schemaId, fallback);
7925
+ return fallback;
7926
+ }
7927
+ // res.status === 200
7928
+ const entry = {
7929
+ schema: res.schema,
7930
+ schemaHash: res.schemaHash,
7931
+ };
7932
+ await this.cache.set(schemaId, entry);
7933
+ return entry;
7934
+ })();
7935
+ this.inFlight.set(key, p);
7936
+ try {
7937
+ const result = await p;
7938
+ return result;
7939
+ }
7940
+ finally {
7941
+ this.inFlight.delete(key);
7942
+ }
7943
+ }
7944
+ }
7945
+
7946
+ const DEFAULT_CACHE_NAMESPACE = 'praxis.analytics.contract';
7947
+ const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000;
7948
+ class AnalyticsSchemaContractService {
7949
+ apiUrl;
7950
+ constructor(apiUrl) {
7951
+ this.apiUrl = apiUrl;
7952
+ }
7953
+ async getAnalytics(request) {
7954
+ const client = new SchemaMetadataClient(new LocalStorageCacheAdapter({
7955
+ namespace: request.cacheNamespace || DEFAULT_CACHE_NAMESPACE,
7956
+ ttlMs: request.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS,
7957
+ }));
7958
+ const entry = await client.getSchema({
7959
+ baseUrl: this.buildSchemasBaseUrl(),
7960
+ path: request.path,
7961
+ operation: request.operation ?? 'post',
7962
+ schemaType: request.schemaType ?? 'response',
7963
+ includeInternalSchemas: request.includeInternalSchemas ?? false,
7964
+ tenant: request.tenant,
7965
+ locale: request.locale,
7966
+ });
7967
+ const schema = this.asRecord(entry.schema);
7968
+ if (!schema) {
7969
+ throw new Error(`Schema payload for ${request.path} is not a JSON object.`);
7970
+ }
7971
+ const xUi = this.asRecord(schema?.['x-ui']);
7972
+ const analytics = this.asRecord(xUi?.['analytics']);
7973
+ const projections = Array.isArray(analytics?.['projections'])
7974
+ ? analytics['projections']
7975
+ : [];
7976
+ if (!analytics || !projections.length) {
7977
+ throw new Error(`x-ui.analytics.projections[] was not published for ${request.path}.`);
7978
+ }
7979
+ return {
7980
+ ...analytics,
7981
+ projections: projections,
7982
+ };
7983
+ }
7984
+ buildSchemasBaseUrl() {
7985
+ const baseUrl = String(this.apiUrl?.default?.baseUrl || '').replace(/\/+$/, '');
7986
+ if (!baseUrl) {
7987
+ throw new Error('AnalyticsSchemaContractService requires API_URL.default.baseUrl to resolve /schemas/filtered.');
7988
+ }
7989
+ return `${baseUrl}/schemas/filtered`;
7990
+ }
7991
+ asRecord(value) {
7992
+ return value && typeof value === 'object' && !Array.isArray(value)
7993
+ ? value
7994
+ : null;
7995
+ }
7996
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AnalyticsSchemaContractService, deps: [{ token: API_URL }], target: i0.ɵɵFactoryTarget.Injectable });
7997
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AnalyticsSchemaContractService, providedIn: 'root' });
7998
+ }
7999
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AnalyticsSchemaContractService, decorators: [{
8000
+ type: Injectable,
8001
+ args: [{ providedIn: 'root' }]
8002
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
8003
+ type: Inject,
8004
+ args: [API_URL]
8005
+ }] }] });
8006
+
8007
+ class AnalyticsStatsRequestBuilderService {
8008
+ buildExecutionPlan(projection) {
8009
+ const dimension = projection.bindings.primaryDimension;
8010
+ const metrics = this.getExecutionMetrics(projection);
8011
+ if (!dimension?.field) {
8012
+ throw new Error(`AnalyticsStatsRequestBuilderService requires primaryDimension for projection "${projection.id}".`);
8013
+ }
8014
+ if (!metrics.length) {
8015
+ throw new Error(`AnalyticsStatsRequestBuilderService requires at least one metric for projection "${projection.id}".`);
8016
+ }
8017
+ return {
8018
+ resourcePath: projection.source.resource,
8019
+ statsPath: `${projection.source.resource.replace(/\/+$/, '')}/stats/${projection.source.operation}`,
8020
+ operation: projection.source.operation,
8021
+ statsRequest: this.buildStatsRequest(projection),
8022
+ dimensions: [dimension.field],
8023
+ metrics: metrics.map((metric) => ({
8024
+ field: metric.field,
8025
+ aggregation: metric.aggregation ?? undefined,
8026
+ alias: metric.field,
8027
+ })),
8028
+ sort: projection.defaults?.sort?.map((item) => `${item.field}:${item.direction}`) ?? undefined,
8029
+ limit: projection.defaults?.limit ?? undefined,
8030
+ };
8031
+ }
8032
+ buildStatsRequest(projection) {
8033
+ const field = projection.bindings.primaryDimension.field;
8034
+ const metrics = this.getExecutionMetrics(projection).map((metric) => this.buildStatsMetric(metric.field, metric.aggregation));
8035
+ const metric = metrics[0];
8036
+ const requestMetrics = metrics.length > 1 ? metrics : undefined;
8037
+ const limit = projection.defaults?.limit ?? undefined;
8038
+ const orderBy = this.mapOrderBy(projection.defaults?.sort?.[0]?.field, projection.defaults?.sort?.[0]?.direction, projection);
8039
+ switch (projection.source.operation) {
8040
+ case 'group-by':
8041
+ return {
8042
+ filter: {},
8043
+ field,
8044
+ metric,
8045
+ metrics: requestMetrics,
8046
+ limit,
8047
+ orderBy,
8048
+ };
8049
+ case 'timeseries':
8050
+ if (!projection.defaults?.granularity) {
8051
+ throw new Error(`AnalyticsStatsRequestBuilderService requires defaults.granularity for timeseries projection "${projection.id}".`);
8052
+ }
8053
+ return {
8054
+ filter: {},
8055
+ field,
8056
+ granularity: this.mapGranularityToBackend(projection.defaults.granularity),
8057
+ metric,
8058
+ metrics: requestMetrics,
8059
+ };
8060
+ case 'distribution':
8061
+ return {
8062
+ filter: {},
8063
+ field,
8064
+ mode: 'TERMS',
8065
+ metric,
8066
+ bucketCount: limit,
8067
+ limit,
8068
+ orderBy,
8069
+ };
8070
+ default:
8071
+ throw new Error(`Unsupported analytics stats operation "${projection.source.operation}".`);
8072
+ }
8073
+ }
8074
+ buildStatsMetric(field, aggregation) {
8075
+ const operation = this.mapAggregationToBackend(aggregation);
8076
+ return {
8077
+ operation,
8078
+ field: operation === 'COUNT' ? undefined : field,
8079
+ alias: field,
8080
+ };
8081
+ }
8082
+ mapAggregationToBackend(aggregation) {
8083
+ switch ((aggregation ?? '').toLowerCase()) {
8084
+ case 'avg':
8085
+ return 'AVG';
8086
+ case 'min':
8087
+ return 'MIN';
8088
+ case 'max':
8089
+ return 'MAX';
8090
+ case 'count':
8091
+ case '':
8092
+ return 'COUNT';
8093
+ case 'sum':
8094
+ return 'SUM';
8095
+ default:
8096
+ throw new Error(`Analytics aggregation "${aggregation}" is not supported in praxis.stats execution.`);
8097
+ }
8098
+ }
8099
+ mapOrderBy(field, direction, projection) {
8100
+ if (!field || !direction) {
8101
+ return undefined;
8102
+ }
8103
+ const dimensionField = projection.bindings.primaryDimension?.field;
8104
+ if (field === dimensionField) {
8105
+ return direction === 'asc' ? 'KEY_ASC' : 'KEY_DESC';
8106
+ }
8107
+ const metricFields = [
8108
+ ...(projection.bindings.primaryMetrics ?? []),
8109
+ ...(projection.bindings.secondaryMetrics ?? []),
8110
+ ].map((metric) => metric.field);
8111
+ if (metricFields.includes(field)) {
8112
+ return direction === 'asc' ? 'VALUE_ASC' : 'VALUE_DESC';
8113
+ }
8114
+ return undefined;
8115
+ }
8116
+ mapGranularityToBackend(granularity) {
8117
+ switch (granularity) {
8118
+ case 'hour':
8119
+ return 'HOUR';
8120
+ case 'day':
8121
+ return 'DAY';
8122
+ case 'week':
8123
+ return 'WEEK';
8124
+ case 'month':
8125
+ return 'MONTH';
8126
+ case 'quarter':
8127
+ return 'QUARTER';
8128
+ case 'year':
8129
+ return 'YEAR';
8130
+ }
8131
+ }
8132
+ getExecutionMetrics(projection) {
8133
+ return [
8134
+ ...(projection.bindings.primaryMetrics ?? []),
8135
+ ...(projection.bindings.secondaryMetrics ?? []),
8136
+ ];
8137
+ }
8138
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AnalyticsStatsRequestBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
8139
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AnalyticsStatsRequestBuilderService, providedIn: 'root' });
8140
+ }
8141
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AnalyticsStatsRequestBuilderService, decorators: [{
8142
+ type: Injectable,
8143
+ args: [{ providedIn: 'root' }]
8144
+ }] });
8145
+
8146
+ function provideFieldSelectorRegistryRuntime(map, opts) {
8147
+ return {
8148
+ provide: ENVIRONMENT_INITIALIZER,
8149
+ multi: true,
8150
+ useFactory: () => {
8151
+ const registry = inject(FieldSelectorRegistry);
8152
+ return () => registry.register(map, { overwrite: opts?.overwrite ?? false });
8153
+ },
8154
+ };
8155
+ }
8156
+
8157
+ function providePraxisToastGlobalActions(opts = {}) {
8158
+ return {
8159
+ provide: GLOBAL_TOAST_SERVICE,
8160
+ useFactory: () => {
8161
+ const snack = (() => { try {
8162
+ return inject(MatSnackBar);
8163
+ }
8164
+ catch {
8165
+ return null;
8166
+ } })();
8167
+ const successDuration = opts.successDurationMs ?? 2500;
8168
+ const errorDuration = opts.errorDurationMs ?? 3500;
8169
+ const successClass = opts.successPanelClass ?? ['pdx-toast-success'];
8170
+ const errorClass = opts.errorPanelClass ?? ['pdx-toast-error'];
8171
+ return {
8172
+ success: (message) => {
8173
+ if (snack) {
8174
+ snack.open(message, undefined, { duration: successDuration, panelClass: successClass });
8175
+ }
8176
+ else {
8177
+ console.log('[Toast]', message);
8178
+ }
8179
+ },
8180
+ error: (message) => {
8181
+ if (snack) {
8182
+ snack.open(message, undefined, { duration: errorDuration, panelClass: errorClass });
8183
+ }
8184
+ else {
8185
+ console.error('[Toast]', message);
8186
+ }
8187
+ },
8188
+ };
8189
+ },
8190
+ };
8191
+ }
8192
+
8193
+ function providePraxisAnalyticsGlobalActions(opts = {}) {
8194
+ return {
8195
+ provide: GLOBAL_ANALYTICS_SERVICE,
8196
+ useFactory: () => {
8197
+ const telemetry = (() => { try {
8198
+ return inject(TelemetryService);
8199
+ }
8200
+ catch {
8201
+ return null;
8202
+ } })();
8203
+ const prefix = opts.prefix ? `${opts.prefix}.` : '';
8204
+ return {
8205
+ track: (eventName, payload) => {
8206
+ if (telemetry) {
8207
+ telemetry.record(`${prefix}${eventName}`, payload);
8208
+ }
8209
+ else {
8210
+ console.log('[Analytics]', `${prefix}${eventName}`, payload ?? null);
8211
+ }
8212
+ },
8213
+ };
8214
+ },
8215
+ };
8216
+ }
8217
+
8218
+ /**
8219
+ * Registers only the default visual renderer for Praxis loading.
8220
+ *
8221
+ * This does not wire any HTTP interceptor by itself. Use this when the host
8222
+ * wants a custom HTTP integration path but still wants the stock Praxis UI.
8223
+ */
8224
+ function providePraxisLoadingDefaults() {
8225
+ return [
8226
+ {
8227
+ provide: PRAXIS_LOADING_RENDERER,
8228
+ useClass: DefaultLoadingRenderer,
8229
+ },
8230
+ ];
8231
+ }
8232
+ /**
8233
+ * Official DI-based entrypoint to enable Praxis HTTP loading.
8234
+ *
8235
+ * Recommended when the host already uses DI interceptors, e.g.:
8236
+ *
8237
+ * ```ts
8238
+ * providers: [
8239
+ * provideHttpClient(withInterceptorsFromDi()),
8240
+ * ...providePraxisHttpLoading(),
8241
+ * ]
8242
+ * ```
8243
+ *
8244
+ * By default this provider does not register any renderer implementation.
8245
+ * Add `...providePraxisLoadingDefaults()` or a custom `PRAXIS_LOADING_RENDERER`
8246
+ * explicitly so hosts keep full control over visual composition.
8247
+ *
8248
+ * Requests remain opt-in and only show loading when they carry
8249
+ * `PRAXIS_LOADING_CTX` in the `HttpContext`.
8250
+ */
8251
+ function providePraxisHttpLoading(opts = {}) {
8252
+ const providers = [
8253
+ {
8254
+ provide: HTTP_INTERCEPTORS,
8255
+ useClass: PraxisLoadingInterceptor,
8256
+ multi: true,
8257
+ },
8258
+ ];
8259
+ if (opts.includeDefaultRenderer === true) {
8260
+ providers.unshift(...providePraxisLoadingDefaults());
8261
+ }
8262
+ return providers;
8263
+ }
8264
+ /**
8265
+ * Angular modern helper for `provideHttpClient(...)`.
8266
+ *
8267
+ * Usage:
8268
+ *
8269
+ * ```ts
8270
+ * providers: [
8271
+ * provideHttpClient(
8272
+ * withInterceptors([authInterceptor]),
8273
+ * withPraxisHttpLoading(),
8274
+ * ),
8275
+ * ...providePraxisLoadingDefaults(),
8276
+ * ]
8277
+ * ```
8278
+ *
8279
+ * Use this helper or `providePraxisHttpLoading()`, never both in the same host.
8280
+ */
8281
+ function withPraxisHttpLoading() {
8282
+ return withInterceptors([praxisLoadingInterceptorFn]);
8283
+ }
8284
+
8285
+ const LOGGER_LEVEL_PRIORITY = {
8286
+ error: 0,
8287
+ warn: 1,
8288
+ info: 2,
8289
+ debug: 3,
8290
+ trace: 4,
8291
+ };
8292
+ const LOGGER_LEVEL_BY_ENV = {
8293
+ prod: 'warn',
8294
+ qa: 'warn',
8295
+ hml: 'info',
8296
+ dev: 'debug',
8297
+ };
8298
+ const PRAXIS_CORPORATE_SENSITIVE_KEYS = [
8299
+ 'password',
8300
+ 'pass',
8301
+ 'token',
8302
+ 'accessToken',
8303
+ 'refreshToken',
8304
+ 'authorization',
8305
+ 'secret',
8306
+ 'apiKey',
8307
+ 'cpf',
8308
+ 'cnpj',
8309
+ 'document',
8310
+ 'ssn',
8311
+ 'email',
8312
+ 'phone',
8313
+ ];
8314
+ const PRAXIS_CORPORATE_THROTTLE = {
8315
+ enabled: true,
8316
+ windowMs: 5000,
8317
+ maxOccurrences: 3,
8318
+ };
8319
+ function createCorporateLoggerConfig(environment = 'prod') {
8320
+ return {
8321
+ environment,
8322
+ minLevel: LOGGER_LEVEL_BY_ENV[environment],
8323
+ warnOnceEnabled: true,
8324
+ throttle: { ...PRAXIS_CORPORATE_THROTTLE },
8325
+ pii: {
8326
+ enabled: true,
8327
+ redactionText: '[REDACTED]',
8328
+ sensitiveKeys: [...PRAXIS_CORPORATE_SENSITIVE_KEYS],
8329
+ },
8330
+ };
8331
+ }
8332
+ function resolveLoggerConfig(options = {}) {
8333
+ const environment = options.environment ?? 'prod';
8334
+ const corporate = createCorporateLoggerConfig(environment);
8335
+ const overridePII = options.pii ?? {};
8336
+ return {
8337
+ ...corporate,
8338
+ minLevel: options.minLevel ?? corporate.minLevel,
8339
+ warnOnceEnabled: options.warnOnceEnabled ?? corporate.warnOnceEnabled,
8340
+ throttle: {
8341
+ ...corporate.throttle,
8342
+ ...(options.throttle ?? {}),
8343
+ },
8344
+ pii: {
8345
+ ...corporate.pii,
8346
+ ...overridePII,
8347
+ sensitiveKeys: overridePII.sensitiveKeys
8348
+ ? [...overridePII.sensitiveKeys]
8349
+ : [...corporate.pii.sensitiveKeys],
8350
+ },
8351
+ defaultContext: options.defaultContext
8352
+ ? { ...options.defaultContext }
8353
+ : corporate.defaultContext,
8354
+ environment,
8355
+ };
8356
+ }
8357
+
8358
+ class ConsoleLoggerSink {
8359
+ log(event) {
8360
+ const print = this.resolveMethod(event.level);
8361
+ const prefix = `[Praxis][${event.level.toUpperCase()}]`;
8362
+ const envelope = {
8363
+ environment: event.environment,
8364
+ timestamp: event.timestamp,
8365
+ context: event.context ?? null,
8366
+ data: event.data ?? null,
8367
+ };
8368
+ if (event.context || event.data !== undefined) {
8369
+ print(`${prefix} ${event.message}`, envelope);
8370
+ return;
8371
+ }
8372
+ print(`${prefix} ${event.message}`);
8373
+ }
8374
+ resolveMethod(level) {
8375
+ if (level === 'error' && typeof console.error === 'function') {
8376
+ return console.error.bind(console);
8377
+ }
8378
+ if (level === 'warn' && typeof console.warn === 'function') {
8379
+ return console.warn.bind(console);
8380
+ }
8381
+ if (level === 'info' && typeof console.info === 'function') {
8382
+ return console.info.bind(console);
8383
+ }
8384
+ if (level === 'debug' && typeof console.debug === 'function') {
8385
+ return console.debug.bind(console);
8386
+ }
8387
+ if (level === 'trace') {
8388
+ if (typeof console.trace === 'function') {
8389
+ return console.trace.bind(console);
8390
+ }
8391
+ if (typeof console.debug === 'function') {
8392
+ return console.debug.bind(console);
8393
+ }
8394
+ }
8395
+ return console.log.bind(console);
8396
+ }
8397
+ }
8398
+
8399
+ const PRAXIS_LOGGER_CONFIG = new InjectionToken('PRAXIS_LOGGER_CONFIG', {
7347
8400
  providedIn: 'root',
7348
8401
  factory: () => createCorporateLoggerConfig('prod'),
7349
8402
  });
@@ -9078,16 +10131,98 @@ const SURFACE_OPEN_I18N_CONFIG = {
9078
10131
  },
9079
10132
  };
9080
10133
 
10134
+ /**
10135
+ * Authoring/editor drawer bridge.
10136
+ *
10137
+ * Use this token for settings/configuration flows that depend on
10138
+ * `SettingsValueProvider` semantics (`apply/save/reset/cancel`).
10139
+ *
10140
+ * Runtime surfaces opened by `surface.open` should migrate to
10141
+ * `SURFACE_DRAWER_BRIDGE`, which carries runtime-oriented semantics instead of
10142
+ * editor semantics.
10143
+ */
9081
10144
  const SETTINGS_PANEL_BRIDGE = new InjectionToken('SETTINGS_PANEL_BRIDGE');
9082
10145
  // Optional data token for consumers that still use DI-based data passing.
9083
10146
  // Prefer using component inputs when opening via SettingsPanelBridge.
9084
10147
  const SETTINGS_PANEL_DATA = new InjectionToken('SETTINGS_PANEL_DATA');
9085
10148
 
10149
+ const SURFACE_DRAWER_BRIDGE = new InjectionToken('SURFACE_DRAWER_BRIDGE');
10150
+
9086
10151
  const TABLE_CONFIG_EDITOR = new InjectionToken('TABLE_CONFIG_EDITOR');
9087
10152
  const STEPPER_CONFIG_EDITOR = new InjectionToken('STEPPER_CONFIG_EDITOR');
9088
10153
  const DYNAMIC_PAGE_SHELL_EDITOR = new InjectionToken('DYNAMIC_PAGE_SHELL_EDITOR');
9089
10154
  const DYNAMIC_PAGE_CONFIG_EDITOR = new InjectionToken('DYNAMIC_PAGE_CONFIG_EDITOR');
9090
10155
 
10156
+ const RESOURCE_DISCOVERY_I18N_NAMESPACE = 'resourceDiscovery';
10157
+ const RESOURCE_AVAILABILITY_REASON_KEY_BY_CODE = {
10158
+ 'resource-state-blocked': 'availability.reason.resource-state-blocked',
10159
+ 'missing-authority': 'availability.reason.missing-authority',
10160
+ 'resource-context-required': 'availability.reason.resource-context-required',
10161
+ 'resource-not-found': 'availability.reason.resource-not-found',
10162
+ };
10163
+ const RESOURCE_DISCOVERY_I18N_CONFIG = {
10164
+ namespaces: {
10165
+ [RESOURCE_DISCOVERY_I18N_NAMESPACE]: {
10166
+ 'pt-BR': {
10167
+ 'workflow.unavailable.default': 'A\u00e7\u00e3o indispon\u00edvel no contexto atual.',
10168
+ 'workflow.unavailable.withReason': 'A\u00e7\u00e3o indispon\u00edvel: {{reason}}',
10169
+ 'rowActions.menu.loading': 'Carregando a\u00e7\u00f5es contextuais',
10170
+ 'rowActions.menu.more': 'Mais a\u00e7\u00f5es',
10171
+ 'availability.reason.resource-state-blocked': 'Dependente do estado atual do registro.',
10172
+ 'availability.reason.missing-authority': 'Voc\u00ea n\u00e3o tem permiss\u00e3o para executar esta a\u00e7\u00e3o.',
10173
+ 'availability.reason.resource-context-required': 'Abra um item espec\u00edfico para continuar.',
10174
+ 'availability.reason.resource-not-found': 'O registro n\u00e3o est\u00e1 dispon\u00edvel.',
10175
+ 'availability.reason.unknown': 'O contexto atual n\u00e3o permite esta a\u00e7\u00e3o.',
10176
+ },
10177
+ 'en-US': {
10178
+ 'workflow.unavailable.default': 'Action is unavailable in the current context.',
10179
+ 'workflow.unavailable.withReason': 'Action unavailable: {{reason}}',
10180
+ 'rowActions.menu.loading': 'Loading contextual actions',
10181
+ 'rowActions.menu.more': 'More actions',
10182
+ 'availability.reason.resource-state-blocked': 'Blocked by the current resource state.',
10183
+ 'availability.reason.missing-authority': 'You do not have permission to perform this action.',
10184
+ 'availability.reason.resource-context-required': 'Open a specific item to continue.',
10185
+ 'availability.reason.resource-not-found': 'The record is not available.',
10186
+ 'availability.reason.unknown': 'The current context does not allow this action.',
10187
+ },
10188
+ },
10189
+ },
10190
+ };
10191
+ function normalizeResourceAvailabilityReasonCode(reason) {
10192
+ return String(reason || '').trim().toLowerCase();
10193
+ }
10194
+ function resolveResourceAvailabilityReasonKey(reason) {
10195
+ const normalized = normalizeResourceAvailabilityReasonCode(reason);
10196
+ return (RESOURCE_AVAILABILITY_REASON_KEY_BY_CODE[normalized] ||
10197
+ 'availability.reason.unknown');
10198
+ }
10199
+ function translateResourceDiscoveryText(i18n, key, params, fallback) {
10200
+ return i18n.t(key, params, fallback, RESOURCE_DISCOVERY_I18N_NAMESPACE);
10201
+ }
10202
+ function translateResourceAvailabilityReason(i18n, reason) {
10203
+ const key = resolveResourceAvailabilityReasonKey(reason);
10204
+ const fallback = key === 'availability.reason.resource-state-blocked'
10205
+ ? 'Dependente do estado atual do registro.'
10206
+ : key === 'availability.reason.missing-authority'
10207
+ ? 'Voc\u00ea n\u00e3o tem permiss\u00e3o para executar esta a\u00e7\u00e3o.'
10208
+ : key === 'availability.reason.resource-context-required'
10209
+ ? 'Abra um item espec\u00edfico para continuar.'
10210
+ : key === 'availability.reason.resource-not-found'
10211
+ ? 'O registro n\u00e3o est\u00e1 dispon\u00edvel.'
10212
+ : 'O contexto atual n\u00e3o permite esta a\u00e7\u00e3o.';
10213
+ return translateResourceDiscoveryText(i18n, key, undefined, fallback);
10214
+ }
10215
+ function translateUnavailableWorkflowMessage(i18n, availability) {
10216
+ const reason = normalizeResourceAvailabilityReasonCode(availability?.reason);
10217
+ if (!reason) {
10218
+ return translateResourceDiscoveryText(i18n, 'workflow.unavailable.default', undefined, 'A\u00e7\u00e3o indispon\u00edvel no contexto atual.');
10219
+ }
10220
+ const translatedReason = translateResourceAvailabilityReason(i18n, reason);
10221
+ return translateResourceDiscoveryText(i18n, 'workflow.unavailable.withReason', {
10222
+ reason: translatedReason,
10223
+ }, `A\u00e7\u00e3o indispon\u00edvel: ${translatedReason}`);
10224
+ }
10225
+
9091
10226
  const DEFAULT_LOCALE = 'en-US';
9092
10227
  const DEFAULT_DATE_TIME = {
9093
10228
  dateFormat: 'shortDate',
@@ -11693,6 +12828,196 @@ function getEditorialSolutionPresetById(presetId) {
11693
12828
  return match ? structuredClone(match) : undefined;
11694
12829
  }
11695
12830
 
12831
+ const WIDGET_PAGE_COMPOSITION_SCHEMA_VERSION = '1.0.0';
12832
+ /**
12833
+ * Normalizes a CompositionLink for deterministic persistence.
12834
+ * Ensures explicit ordering and strips compatibility-only legacy residues.
12835
+ */
12836
+ function normalizeCompositionLink(link) {
12837
+ const normalized = {
12838
+ id: link.id,
12839
+ from: normalizeEndpoint$1(link.from),
12840
+ to: normalizeEndpoint$1(link.to),
12841
+ intent: link.intent,
12842
+ };
12843
+ if (link.transform) {
12844
+ normalized.transform = clone(link.transform);
12845
+ }
12846
+ const conditions = normalizeConditions(link.conditions);
12847
+ if (conditions) {
12848
+ normalized.conditions = conditions;
12849
+ }
12850
+ const policy = normalizePolicy(link.policy);
12851
+ if (policy) {
12852
+ normalized.policy = policy;
12853
+ }
12854
+ const metadata = normalizeMetadata(link.metadata);
12855
+ if (metadata) {
12856
+ normalized.metadata = metadata;
12857
+ }
12858
+ return normalized;
12859
+ }
12860
+ /**
12861
+ * Normalizes an array of CompositionLinks for deterministic persistence.
12862
+ */
12863
+ function normalizeCompositionLinks(links) {
12864
+ return links
12865
+ .map(normalizeCompositionLink)
12866
+ .sort(compareNormalizedLinks);
12867
+ }
12868
+ /**
12869
+ * Canonical persisted write surface for CompositionLink[].
12870
+ * New saved shapes must serialize links under `composition.links`.
12871
+ */
12872
+ function serializeCompositionLinks(links) {
12873
+ return {
12874
+ version: WIDGET_PAGE_COMPOSITION_SCHEMA_VERSION,
12875
+ links: normalizeCompositionLinks(links),
12876
+ };
12877
+ }
12878
+ /**
12879
+ * Writes the canonical persisted composition surface into a page shape.
12880
+ * The canonical writer omits legacy `connections` to avoid dual-write ambiguity.
12881
+ */
12882
+ function writeCompositionLinksToPersistedPage(page, links) {
12883
+ const { composition: _legacyComposition, ...rest } = clone(page);
12884
+ return {
12885
+ ...rest,
12886
+ composition: serializeCompositionLinks(links),
12887
+ };
12888
+ }
12889
+ function normalizeEndpoint$1(endpoint) {
12890
+ if (endpoint.kind === 'component-port') {
12891
+ return {
12892
+ kind: 'component-port',
12893
+ ref: {
12894
+ widget: endpoint.ref.widget,
12895
+ port: endpoint.ref.port,
12896
+ direction: endpoint.ref.direction,
12897
+ ...(endpoint.ref.componentType ? { componentType: endpoint.ref.componentType } : {}),
12898
+ ...(endpoint.ref.bindingPath ? { bindingPath: endpoint.ref.bindingPath } : {}),
12899
+ },
12900
+ };
12901
+ }
12902
+ return {
12903
+ kind: 'state',
12904
+ ref: {
12905
+ path: endpoint.ref.path,
12906
+ ...(endpoint.ref.layer ? { layer: endpoint.ref.layer } : {}),
12907
+ },
12908
+ };
12909
+ }
12910
+ function normalizeConditions(conditions) {
12911
+ if (!conditions?.length) {
12912
+ return undefined;
12913
+ }
12914
+ return conditions
12915
+ .map((condition) => ({
12916
+ kind: condition.kind,
12917
+ ...(condition.expression ? { expression: condition.expression } : {}),
12918
+ ...(condition.path ? { path: condition.path } : {}),
12919
+ ...(condition.value !== undefined ? { value: clone(condition.value) } : {}),
12920
+ ...(condition.negate !== undefined ? { negate: condition.negate } : {}),
12921
+ ...(condition.description ? { description: condition.description } : {}),
12922
+ }))
12923
+ .sort(compareConditions);
12924
+ }
12925
+ function normalizePolicy(policy) {
12926
+ if (!policy) {
12927
+ return undefined;
12928
+ }
12929
+ const normalized = {
12930
+ ...(policy.debounceMs !== undefined ? { debounceMs: policy.debounceMs } : {}),
12931
+ ...(policy.distinct !== undefined ? { distinct: policy.distinct } : {}),
12932
+ ...(policy.distinctBy ? { distinctBy: policy.distinctBy } : {}),
12933
+ ...(policy.delivery ? { delivery: policy.delivery } : {}),
12934
+ ...(policy.missingValuePolicy ? { missingValuePolicy: policy.missingValuePolicy } : {}),
12935
+ ...(policy.errorPolicy ? { errorPolicy: policy.errorPolicy } : {}),
12936
+ };
12937
+ return Object.keys(normalized).length ? normalized : undefined;
12938
+ }
12939
+ function normalizeMetadata(metadata) {
12940
+ if (!metadata) {
12941
+ return undefined;
12942
+ }
12943
+ const tags = metadata.tags?.length
12944
+ ? [...metadata.tags].sort((a, b) => a.localeCompare(b))
12945
+ : undefined;
12946
+ const source = metadata.source === 'legacy-widget-connection'
12947
+ ? undefined
12948
+ : metadata.source;
12949
+ const normalized = {
12950
+ ...(metadata.label ? { label: metadata.label } : {}),
12951
+ ...(metadata.description ? { description: metadata.description } : {}),
12952
+ ...(tags?.length ? { tags } : {}),
12953
+ ...(metadata.deprecated !== undefined ? { deprecated: metadata.deprecated } : {}),
12954
+ ...(source ? { source } : {}),
12955
+ };
12956
+ return Object.keys(normalized).length ? normalized : undefined;
12957
+ }
12958
+ function compareConditions(a, b) {
12959
+ return compareValues([
12960
+ a.kind,
12961
+ a.expression || '',
12962
+ a.path || '',
12963
+ JSON.stringify(a.value ?? null),
12964
+ String(a.negate ?? ''),
12965
+ a.description || '',
12966
+ ], [
12967
+ b.kind,
12968
+ b.expression || '',
12969
+ b.path || '',
12970
+ JSON.stringify(b.value ?? null),
12971
+ String(b.negate ?? ''),
12972
+ b.description || '',
12973
+ ]);
12974
+ }
12975
+ function compareNormalizedLinks(a, b) {
12976
+ return compareValues([
12977
+ a.id,
12978
+ a.intent,
12979
+ endpointSortKey(a.from),
12980
+ endpointSortKey(a.to),
12981
+ ], [
12982
+ b.id,
12983
+ b.intent,
12984
+ endpointSortKey(b.from),
12985
+ endpointSortKey(b.to),
12986
+ ]);
12987
+ }
12988
+ function endpointSortKey(endpoint) {
12989
+ if (endpoint.kind === 'component-port') {
12990
+ return [
12991
+ endpoint.kind,
12992
+ endpoint.ref.widget,
12993
+ endpoint.ref.port,
12994
+ endpoint.ref.direction,
12995
+ endpoint.ref.bindingPath || '',
12996
+ endpoint.ref.componentType || '',
12997
+ ].join('|');
12998
+ }
12999
+ return [
13000
+ endpoint.kind,
13001
+ endpoint.ref.path,
13002
+ endpoint.ref.layer || '',
13003
+ ].join('|');
13004
+ }
13005
+ function compareValues(left, right) {
13006
+ for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
13007
+ const comparison = (left[index] || '').localeCompare(right[index] || '');
13008
+ if (comparison !== 0) {
13009
+ return comparison;
13010
+ }
13011
+ }
13012
+ return 0;
13013
+ }
13014
+ function clone(value) {
13015
+ if (value == null || typeof value !== 'object') {
13016
+ return value;
13017
+ }
13018
+ return JSON.parse(JSON.stringify(value));
13019
+ }
13020
+
11696
13021
  /** Utility: generate a random id using crypto.randomUUID or fallback. */
11697
13022
  function generateId() {
11698
13023
  const g = globalThis.crypto?.randomUUID?.();
@@ -11732,7 +13057,8 @@ function ensurePageIds(page) {
11732
13057
  * Create a persisted page record from an in-memory page definition and identity.
11733
13058
  */
11734
13059
  function createPersistedPage(identity, page, opts) {
11735
- const { page: pageWithIds } = ensurePageIds(page);
13060
+ const persistedPage = writeCompositionLinksToPersistedPage(page, page.composition?.links || []);
13061
+ const { page: pageWithIds } = ensurePageIds(persistedPage);
11736
13062
  const now = new Date().toISOString();
11737
13063
  return {
11738
13064
  id: opts?.id || generateId(),
@@ -12977,104 +14303,29 @@ const GLOBAL_ACTION_UI_SCHEMAS = [
12977
14303
  ],
12978
14304
  },
12979
14305
  {
12980
- id: 'copyToClipboard',
12981
- label: 'Copiar para clipboard',
12982
- fields: [
12983
- { key: 'text', label: 'Texto', type: 'text' },
12984
- { key: 'useFieldValue', label: 'Usar valor do campo', type: 'toggle' },
12985
- ],
12986
- },
12987
- {
12988
- id: 'refreshOptions',
12989
- label: 'Recarregar opções',
12990
- fields: [{ key: 'field', label: 'Campo', type: 'text', required: true }],
12991
- },
12992
- {
12993
- id: 'loadDependentData',
12994
- label: 'Carregar dependentes',
12995
- fields: [{ key: 'field', label: 'Campo alvo', type: 'text', required: true }],
12996
- },
12997
- ];
12998
- function getGlobalActionUiSchema(id) {
12999
- if (!id)
13000
- return undefined;
13001
- return GLOBAL_ACTION_UI_SCHEMAS.find((schema) => schema.id === id);
13002
- }
13003
-
13004
- const SURFACE_OPEN_PRESETS = [
13005
- {
13006
- id: 'praxis-dynamic-form',
13007
- label: 'Formulário',
13008
- description: 'Abre um formulário dinâmico com resourcePath, formId e resourceId.',
13009
- payload: {
13010
- presentation: 'drawer',
13011
- widget: {
13012
- id: 'praxis-dynamic-form',
13013
- bindingOrder: ['resourcePath', 'formId', 'resourceId'],
13014
- inputs: {
13015
- resourcePath: '',
13016
- formId: '',
13017
- mode: 'view',
13018
- },
13019
- },
13020
- bindings: [],
13021
- },
13022
- },
13023
- {
13024
- id: 'praxis-table',
13025
- label: 'Tabela',
13026
- description: 'Abre uma tabela filtrável usando inputs declarativos e queryContext.',
13027
- payload: {
13028
- presentation: 'drawer',
13029
- widget: {
13030
- id: 'praxis-table',
13031
- bindingOrder: ['resourcePath', 'tableId', 'queryContext'],
13032
- inputs: {
13033
- resourcePath: '',
13034
- tableId: '',
13035
- queryContext: {
13036
- filters: {},
13037
- },
13038
- },
13039
- },
13040
- bindings: [],
13041
- },
13042
- },
13043
- {
13044
- id: 'praxis-list',
13045
- label: 'Lista',
13046
- description: 'Abre uma lista com config/listId e bindings configuráveis.',
13047
- payload: {
13048
- presentation: 'drawer',
13049
- widget: {
13050
- id: 'praxis-list',
13051
- bindingOrder: ['listId', 'config'],
13052
- inputs: {
13053
- listId: '',
13054
- config: {},
13055
- },
13056
- },
13057
- bindings: [],
13058
- },
13059
- },
13060
- {
13061
- id: 'praxis-crud',
13062
- label: 'CRUD',
13063
- description: 'Abre a surface de CRUD com inputs mínimos e bindings configuráveis.',
13064
- payload: {
13065
- presentation: 'drawer',
13066
- widget: {
13067
- id: 'praxis-crud',
13068
- bindingOrder: ['resourcePath', 'formId', 'resourceId'],
13069
- inputs: {
13070
- resourcePath: '',
13071
- formId: '',
13072
- },
13073
- },
13074
- bindings: [],
13075
- },
14306
+ id: 'copyToClipboard',
14307
+ label: 'Copiar para clipboard',
14308
+ fields: [
14309
+ { key: 'text', label: 'Texto', type: 'text' },
14310
+ { key: 'useFieldValue', label: 'Usar valor do campo', type: 'toggle' },
14311
+ ],
14312
+ },
14313
+ {
14314
+ id: 'refreshOptions',
14315
+ label: 'Recarregar opções',
14316
+ fields: [{ key: 'field', label: 'Campo', type: 'text', required: true }],
14317
+ },
14318
+ {
14319
+ id: 'loadDependentData',
14320
+ label: 'Carregar dependentes',
14321
+ fields: [{ key: 'field', label: 'Campo alvo', type: 'text', required: true }],
13076
14322
  },
13077
14323
  ];
14324
+ function getGlobalActionUiSchema(id) {
14325
+ if (!id)
14326
+ return undefined;
14327
+ return GLOBAL_ACTION_UI_SCHEMAS.find((schema) => schema.id === id);
14328
+ }
13078
14329
 
13079
14330
  class SurfaceOpenActionEditorComponent {
13080
14331
  value;
@@ -13806,7 +15057,7 @@ class SurfaceOpenActionEditorComponent {
13806
15057
  </mat-form-field>
13807
15058
  </div>
13808
15059
  </div>
13809
- `, isInline: true, styles: [".surface-editor{display:grid;gap:16px}.surface-section{padding:12px 14px;border:1px dashed var(--md-sys-color-outline-variant, rgba(0, 0, 0, .18));border-radius:10px;background:var(--md-sys-color-surface-container-lowest, #fff)}.surface-section-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.surface-section-title{font-size:12px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #5f6368);margin-bottom:10px}.surface-section-header .surface-section-title{margin-bottom:0}.surface-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px 16px}.surface-preset-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.surface-span-2{grid-column:span 2}.surface-span-all{width:100%}.surface-component-meta{display:grid;gap:4px}.surface-component-title{font-weight:600}.surface-component-description,.surface-empty{color:var(--md-sys-color-on-surface-variant, #5f6368);font-size:12px}.surface-bindings{display:grid;gap:12px}.surface-binding-row{display:grid;grid-template-columns:minmax(200px,1.2fr) minmax(220px,1.4fr) minmax(140px,.8fr) minmax(180px,1fr) auto;gap:12px;align-items:start}mat-form-field{width:100%}@media(max-width:960px){.surface-binding-row{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { 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: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5$1.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: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { 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: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
15060
+ `, isInline: true, styles: [".surface-editor{display:grid;gap:16px}.surface-section{padding:12px 14px;border:1px dashed var(--md-sys-color-outline-variant, rgba(0, 0, 0, .18));border-radius:10px;background:var(--md-sys-color-surface-container-lowest, #fff)}.surface-section-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.surface-section-title{font-size:12px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #5f6368);margin-bottom:10px}.surface-section-header .surface-section-title{margin-bottom:0}.surface-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px 16px}.surface-preset-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.surface-span-2{grid-column:span 2}.surface-span-all{width:100%}.surface-component-meta{display:grid;gap:4px}.surface-component-title{font-weight:600}.surface-component-description,.surface-empty{color:var(--md-sys-color-on-surface-variant, #5f6368);font-size:12px}.surface-bindings{display:grid;gap:12px}.surface-binding-row{display:grid;grid-template-columns:minmax(200px,1.2fr) minmax(220px,1.4fr) minmax(140px,.8fr) minmax(180px,1fr) auto;gap:12px;align-items:start}mat-form-field{width:100%}@media(max-width:960px){.surface-binding-row{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { 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: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { 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: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
13810
15061
  }
13811
15062
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SurfaceOpenActionEditorComponent, decorators: [{
13812
15063
  type: Component,
@@ -15078,11 +16329,11 @@ const ENUMS = {
15078
16329
  actionPlacement: ['header', 'window'],
15079
16330
  };
15080
16331
  const CAPS = [
15081
- { path: 'page', category: 'page', valueKind: 'object', description: 'Definição da página dinâmica.' },
16332
+ { path: 'page', category: 'page', valueKind: 'object', description: 'Definicao da pagina dinamica.' },
15082
16333
  { path: 'page.context', category: 'context', valueKind: 'object', description: 'Contexto compartilhado entre widgets.' },
15083
- { path: 'page.layout', category: 'layout', valueKind: 'object', description: 'Layout base da página.' },
15084
- { path: 'page.layout.orientation', category: 'layout', valueKind: 'enum', allowedValues: ENUMS.layoutOrientation, description: 'Orientação do grid (vertical/columns).' },
15085
- { path: 'page.layout.columns', category: 'layout', valueKind: 'number', description: 'Número de colunas (quando orientation=columns).' },
16334
+ { path: 'page.layout', category: 'layout', valueKind: 'object', description: 'Layout base da pagina.' },
16335
+ { path: 'page.layout.orientation', category: 'layout', valueKind: 'enum', allowedValues: ENUMS.layoutOrientation, description: 'Orientacao do grid (vertical/columns).' },
16336
+ { path: 'page.layout.columns', category: 'layout', valueKind: 'number', description: 'Numero de colunas (quando orientation=columns).' },
15086
16337
  { path: 'page.layout.gap', category: 'layout', valueKind: 'string', description: 'Gap entre widgets (ex: 16px).' },
15087
16338
  { path: 'page.layout.breakpoints', category: 'layout', valueKind: 'object', description: 'Colunas por breakpoint.' },
15088
16339
  { path: 'page.layout.breakpoints.sm', category: 'layout', valueKind: 'number', description: 'Colunas para breakpoint sm.' },
@@ -15090,46 +16341,49 @@ const CAPS = [
15090
16341
  { path: 'page.layout.breakpoints.lg', category: 'layout', valueKind: 'number', description: 'Colunas para breakpoint lg.' },
15091
16342
  { path: 'page.layout.breakpoints.xl', category: 'layout', valueKind: 'number', description: 'Colunas para breakpoint xl.' },
15092
16343
  { path: 'page.widgets', category: 'widgets', valueKind: 'array', description: 'Lista de widgets renderizados.' },
15093
- { path: 'page.widgets[].key', category: 'widgets', valueKind: 'string', description: 'Identificador único do widget.' },
16344
+ { path: 'page.widgets[].key', category: 'widgets', valueKind: 'string', description: 'Identificador unico do widget.' },
15094
16345
  { path: 'page.widgets[].className', category: 'widgets', valueKind: 'string', description: 'Classe CSS opcional do widget.' },
15095
16346
  { path: 'page.widgets[].definition.id', category: 'widgets', valueKind: 'string', description: 'ID do componente do widget (ex: praxis-table).' },
15096
16347
  { path: 'page.widgets[].definition.inputs', category: 'widgets', valueKind: 'object', description: 'Inputs iniciais do widget.' },
15097
16348
  { path: 'page.widgets[].definition.bindingOrder', category: 'widgets', valueKind: 'array', description: 'Ordem de binding de inputs.' },
15098
- { path: 'page.widgets[].shell', category: 'shell', valueKind: 'object', description: 'Configuração do shell do widget.' },
16349
+ { path: 'page.widgets[].shell', category: 'shell', valueKind: 'object', description: 'Configuracao do shell do widget.' },
15099
16350
  { path: 'page.widgets[].shell.kind', category: 'shell', valueKind: 'enum', allowedValues: ENUMS.shellKind, description: 'Tipo de shell.' },
15100
- { path: 'page.widgets[].shell.title', category: 'shell', valueKind: 'string', description: 'Título do shell.' },
15101
- { path: 'page.widgets[].shell.subtitle', category: 'shell', valueKind: 'string', description: 'Subtítulo do shell.' },
15102
- { path: 'page.widgets[].shell.icon', category: 'shell', valueKind: 'string', description: 'Ícone do shell.' },
16351
+ { path: 'page.widgets[].shell.title', category: 'shell', valueKind: 'string', description: 'Titulo do shell.' },
16352
+ { path: 'page.widgets[].shell.subtitle', category: 'shell', valueKind: 'string', description: 'Subtitulo do shell.' },
16353
+ { path: 'page.widgets[].shell.icon', category: 'shell', valueKind: 'string', description: 'Icone do shell.' },
15103
16354
  { path: 'page.widgets[].shell.showHeader', category: 'shell', valueKind: 'boolean', description: 'Exibe o header do shell.' },
15104
- { path: 'page.widgets[].shell.actions', category: 'shell', valueKind: 'array', description: 'Ações do shell.' },
15105
- { path: 'page.widgets[].shell.actions[].id', category: 'shell', valueKind: 'string', description: 'ID da ação.' },
15106
- { path: 'page.widgets[].shell.actions[].label', category: 'shell', valueKind: 'string', description: 'Label da ação.' },
15107
- { path: 'page.widgets[].shell.actions[].icon', category: 'shell', valueKind: 'string', description: 'Ícone da ação.' },
15108
- { path: 'page.widgets[].shell.actions[].variant', category: 'shell', valueKind: 'enum', allowedValues: ENUMS.actionVariant, description: 'Estilo visual da ação.' },
15109
- { path: 'page.widgets[].shell.actions[].placement', category: 'shell', valueKind: 'enum', allowedValues: ENUMS.actionPlacement, description: 'Posicionamento da ação.' },
15110
- { path: 'page.widgets[].shell.actions[].emit', category: 'shell', valueKind: 'string', description: 'Evento emitido ao acionar a ação.' },
15111
- { path: 'page.connections', category: 'connections', valueKind: 'array', description: 'Conexões entre widgets.' },
15112
- { path: 'page.connections[].from.widget', category: 'connections', valueKind: 'string', description: 'Widget de origem.' },
15113
- { path: 'page.connections[].from.output', category: 'connections', valueKind: 'string', description: 'Output de origem.' },
15114
- { path: 'page.connections[].to.widget', category: 'connections', valueKind: 'string', description: 'Widget de destino.' },
15115
- { path: 'page.connections[].to.input', category: 'connections', valueKind: 'string', description: 'Input de destino.' },
15116
- { path: 'page.connections[].to.bindingOrder', category: 'connections', valueKind: 'array', description: 'Ordem de binding no destino.' },
15117
- { path: 'page.connections[].map', category: 'connections', valueKind: 'string', description: 'Caminho de extração do payload.' },
15118
- { path: 'page.connections[].set', category: 'connections', valueKind: 'object', description: 'Valor constante para a conexão.' },
16355
+ { path: 'page.widgets[].shell.actions', category: 'shell', valueKind: 'array', description: 'Acoes do shell.' },
16356
+ { path: 'page.widgets[].shell.actions[].id', category: 'shell', valueKind: 'string', description: 'ID da acao.' },
16357
+ { path: 'page.widgets[].shell.actions[].label', category: 'shell', valueKind: 'string', description: 'Label da acao.' },
16358
+ { path: 'page.widgets[].shell.actions[].icon', category: 'shell', valueKind: 'string', description: 'Icone da acao.' },
16359
+ { path: 'page.widgets[].shell.actions[].variant', category: 'shell', valueKind: 'enum', allowedValues: ENUMS.actionVariant, description: 'Estilo visual da acao.' },
16360
+ { path: 'page.widgets[].shell.actions[].placement', category: 'shell', valueKind: 'enum', allowedValues: ENUMS.actionPlacement, description: 'Posicionamento da acao.' },
16361
+ { path: 'page.widgets[].shell.actions[].emit', category: 'shell', valueKind: 'string', description: 'Evento emitido ao acionar a acao.' },
16362
+ { path: 'page.composition', category: 'connections', valueKind: 'object', description: 'Envelope canonico da composicao persistida.' },
16363
+ { path: 'page.composition.version', category: 'connections', valueKind: 'string', description: 'Versao do envelope de composicao.' },
16364
+ { path: 'page.composition.links', category: 'connections', valueKind: 'array', description: 'Links canonicos entre widgets e estado.' },
16365
+ { path: 'page.composition.links[].id', category: 'connections', valueKind: 'string', description: 'Identificador estavel do link.' },
16366
+ { path: 'page.composition.links[].from', category: 'connections', valueKind: 'object', description: 'Endpoint de origem do link.' },
16367
+ { path: 'page.composition.links[].to', category: 'connections', valueKind: 'object', description: 'Endpoint de destino do link.' },
16368
+ { path: 'page.composition.links[].intent', category: 'connections', valueKind: 'string', description: 'Intencao semantica do link.' },
16369
+ { path: 'page.composition.links[].transform', category: 'connections', valueKind: 'object', description: 'Pipeline de transformacao do link.' },
16370
+ { path: 'page.composition.links[].conditions', category: 'connections', valueKind: 'array', description: 'Condicoes opcionais de entrega.' },
16371
+ { path: 'page.composition.links[].policy', category: 'connections', valueKind: 'object', description: 'Politicas opcionais de entrega.' },
16372
+ { path: 'page.composition.links[].metadata', category: 'connections', valueKind: 'object', description: 'Metadados opcionais do link.' },
15119
16373
  ];
15120
16374
  const DYNAMIC_PAGE_AI_CAPABILITIES = {
15121
16375
  version: 'v1.0',
15122
16376
  enums: ENUMS,
15123
16377
  targets: ['praxis-dynamic-page'],
15124
16378
  notes: [
15125
- 'Este catálogo é específico para o componente praxis-dynamic-page.',
15126
- 'Widgets e connections são arrays; o adapter faz merge por key.',
15127
- 'Para remoção/replace, use flags {_remove:true} ou {_replace:true} no item.',
15128
- 'Para alterar origem/destino de conexão, inclua {_beforeKey:"from|out|to|in"} no item.',
15129
- 'Inputs de widgets dependem do componente (ex: praxis-table, praxis-dynamic-form). Evite inventar campos; prefira pedir confirmação.',
15130
- 'Use keys estáveis para widgets ao criar conexões.',
15131
- 'Objetivo: compor widgets e relacionamentos (ex.: master-detail).',
15132
- 'Conexões típicas: table.rowClick -> form.resourceId (map: payload.row.id). Para master-detail, use este padrão.',
16379
+ 'Este catalogo e especifico para o componente praxis-dynamic-page.',
16380
+ 'Widgets e page.composition.links sao arrays; o adapter faz merge por key estavel.',
16381
+ 'Para remocao/replace, use flags {_remove:true} ou {_replace:true} no item.',
16382
+ 'Para renomear um link, inclua {_beforeKey:"link-id-anterior"} no item.',
16383
+ 'Inputs de widgets dependem do componente (ex: praxis-table, praxis-dynamic-form). Evite inventar campos; prefira pedir confirmacao.',
16384
+ 'Use ids estaveis para links e keys estaveis para widgets.',
16385
+ 'Objetivo: compor widgets e relacionamentos canonicos (ex.: master-detail).',
16386
+ 'Link tipico de master-detail: component-port(table.rowClick) -> component-port(form.resourceId) com transform map payload.row.id.',
15133
16387
  ],
15134
16388
  capabilities: CAPS,
15135
16389
  };
@@ -15154,7 +16408,7 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15154
16408
  'page.widgets[].shell.actions[].variant': {
15155
16409
  mode: 'enum',
15156
16410
  options: [
15157
- { value: 'icon', label: 'Ícone' },
16411
+ { value: 'icon', label: 'Icone' },
15158
16412
  { value: 'text', label: 'Texto' },
15159
16413
  { value: 'outlined', label: 'Outlined' },
15160
16414
  ],
@@ -15166,25 +16420,39 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15166
16420
  { value: 'window', label: 'Janela' },
15167
16421
  ],
15168
16422
  },
15169
- 'page.connections[].from.output': {
16423
+ 'page.composition.version': {
16424
+ mode: 'enum',
16425
+ options: [
16426
+ { value: '1.0.0', label: 'Schema canonico 1.0.0' },
16427
+ ],
16428
+ },
16429
+ 'page.composition.links[].intent': {
16430
+ mode: 'enum',
16431
+ options: [
16432
+ { value: 'event-propagation', label: 'Propagacao de evento' },
16433
+ { value: 'state-read', label: 'Leitura de estado' },
16434
+ { value: 'state-write', label: 'Escrita de estado' },
16435
+ ],
16436
+ },
16437
+ 'page.composition.links[].from.ref.port': {
15170
16438
  mode: 'suggested',
15171
16439
  options: [
15172
- { value: 'rowClick', label: 'Clique na linha (praxis-table)', example: 'Usar table.rowClick -> form.resourceId' },
15173
- { value: 'rowAction', label: 'Ação da linha (praxis-table)' },
15174
- { value: 'formSubmit', label: 'Submit do formulário (praxis-dynamic-form)' },
16440
+ { value: 'rowClick', label: 'Clique na linha (praxis-table)', example: 'Usar table.rowClick -> form.resourceId via transform pick-path payload.row.id' },
16441
+ { value: 'rowAction', label: 'Acao da linha (praxis-table)' },
16442
+ { value: 'formSubmit', label: 'Submit do formulario (praxis-dynamic-form)' },
15175
16443
  ],
15176
16444
  },
15177
- 'page.connections[].to.input': {
16445
+ 'page.composition.links[].to.ref.port': {
15178
16446
  mode: 'suggested',
15179
16447
  options: [
15180
- { value: 'resourceId', label: 'ID do registro (praxis-dynamic-form)', example: 'map: payload.row.id' },
15181
- { value: 'mode', label: 'Modo do formulário (create|edit|view)' },
16448
+ { value: 'resourceId', label: 'ID do registro (praxis-dynamic-form)', example: 'transform pick-path payload.row.id' },
16449
+ { value: 'mode', label: 'Modo do formulario (create|edit|view)' },
15182
16450
  ],
15183
16451
  },
15184
- 'page.connections[].map': {
16452
+ 'page.composition.links[].transform.steps[].config.path': {
15185
16453
  mode: 'suggested',
15186
16454
  options: [
15187
- { value: 'payload.row.id', label: 'ID padrão do registro', example: 'rowClick -> resourceId' },
16455
+ { value: 'payload.row.id', label: 'ID padrao do registro', example: 'rowClick -> resourceId' },
15188
16456
  ],
15189
16457
  },
15190
16458
  },
@@ -15208,21 +16476,20 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15208
16476
  requiresExistingTarget: true,
15209
16477
  scope: 'ROW',
15210
16478
  params: [
15211
- { name: 'fromWidget', type: 'STRING' },
15212
- { name: 'fromOutput', type: 'STRING' },
15213
- { name: 'toWidget', type: 'STRING' },
15214
- { name: 'toInput', type: 'STRING' },
16479
+ { name: 'linkId', type: 'STRING' },
15215
16480
  ],
15216
16481
  patchTemplate: {
15217
16482
  page: {
15218
- connections: [
15219
- {
15220
- _beforeKey: '{{target}}',
15221
- from: { widget: '{{params.fromWidget}}', output: '{{params.fromOutput}}' },
15222
- to: { widget: '{{params.toWidget}}', input: '{{params.toInput}}' },
15223
- _remove: true,
15224
- },
15225
- ],
16483
+ composition: {
16484
+ version: '1.0.0',
16485
+ links: [
16486
+ {
16487
+ _beforeKey: '{{target}}',
16488
+ id: '{{params.linkId}}',
16489
+ _remove: true,
16490
+ },
16491
+ ],
16492
+ },
15226
16493
  },
15227
16494
  },
15228
16495
  },
@@ -15323,14 +16590,40 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15323
16590
  ],
15324
16591
  patchTemplate: {
15325
16592
  page: {
15326
- connections: [
15327
- {
15328
- from: { widget: '{{params.fromWidget}}', output: '{{params.fromOutput}}' },
15329
- to: { widget: '{{params.toWidget}}', input: '{{params.toInput}}' },
15330
- set: '{{params.value}}',
15331
- _replace: true,
15332
- },
15333
- ],
16593
+ composition: {
16594
+ version: '1.0.0',
16595
+ links: [
16596
+ {
16597
+ id: '{{params.fromWidget}}.{{params.fromOutput}}->{{params.toWidget}}.{{params.toInput}}',
16598
+ from: {
16599
+ kind: 'component-port',
16600
+ ref: { widget: '{{params.fromWidget}}', port: '{{params.fromOutput}}', direction: 'output' },
16601
+ },
16602
+ to: {
16603
+ kind: 'component-port',
16604
+ ref: { widget: '{{params.toWidget}}', port: '{{params.toInput}}', direction: 'input' },
16605
+ },
16606
+ intent: 'event-propagation',
16607
+ transform: {
16608
+ version: '2.0',
16609
+ phase: 'link-propagation',
16610
+ mode: 'single-value',
16611
+ steps: [
16612
+ {
16613
+ id: 'constant-value',
16614
+ kind: 'constant',
16615
+ phase: 'link-propagation',
16616
+ config: { value: '{{params.value}}' },
16617
+ },
16618
+ ],
16619
+ },
16620
+ metadata: {
16621
+ source: 'persisted-composition-link',
16622
+ },
16623
+ _replace: true,
16624
+ },
16625
+ ],
16626
+ },
15334
16627
  },
15335
16628
  },
15336
16629
  },
@@ -15372,13 +16665,27 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15372
16665
  ],
15373
16666
  patchTemplate: {
15374
16667
  page: {
15375
- connections: [
15376
- {
15377
- _beforeKey: '{{params.beforeFromWidget}}|{{params.beforeFromOutput}}|{{params.beforeToWidget}}|{{params.beforeToInput}}',
15378
- from: { widget: '{{params.fromWidget}}', output: '{{params.fromOutput}}' },
15379
- to: { widget: '{{params.toWidget}}', input: '{{params.toInput}}' },
15380
- },
15381
- ],
16668
+ composition: {
16669
+ version: '1.0.0',
16670
+ links: [
16671
+ {
16672
+ _beforeKey: '{{params.beforeFromWidget}}.{{params.beforeFromOutput}}->{{params.beforeToWidget}}.{{params.beforeToInput}}',
16673
+ id: '{{params.fromWidget}}.{{params.fromOutput}}->{{params.toWidget}}.{{params.toInput}}',
16674
+ from: {
16675
+ kind: 'component-port',
16676
+ ref: { widget: '{{params.fromWidget}}', port: '{{params.fromOutput}}', direction: 'output' },
16677
+ },
16678
+ to: {
16679
+ kind: 'component-port',
16680
+ ref: { widget: '{{params.toWidget}}', port: '{{params.toInput}}', direction: 'input' },
16681
+ },
16682
+ intent: 'event-propagation',
16683
+ metadata: {
16684
+ source: 'persisted-composition-link',
16685
+ },
16686
+ },
16687
+ ],
16688
+ },
15382
16689
  },
15383
16690
  },
15384
16691
  },
@@ -15392,7 +16699,7 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15392
16699
  { name: 'widgetKey', type: 'STRING' },
15393
16700
  { name: 'resourcePath', type: 'STRING' },
15394
16701
  ],
15395
- safetyNotes: 'Use apenas resourcePath base. Evite inventar colunas; deixe config mínima.',
16702
+ safetyNotes: 'Use apenas resourcePath base. Evite inventar colunas; deixe config minima.',
15396
16703
  patchTemplate: {
15397
16704
  page: {
15398
16705
  widgets: [
@@ -15457,17 +16764,44 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15457
16764
  { name: 'toInput', type: 'STRING' },
15458
16765
  { name: 'map', type: 'STRING' },
15459
16766
  ],
15460
- safetyNotes: 'Use map payload.row.id para master-detail padrão.',
16767
+ safetyNotes: 'Use transform pick-path payload.row.id para master-detail padrao.',
15461
16768
  patchTemplate: {
15462
16769
  page: {
15463
- connections: [
15464
- {
15465
- from: { widget: '{{params.fromWidget}}', output: '{{params.fromOutput}}' },
15466
- to: { widget: '{{params.toWidget}}', input: '{{params.toInput}}' },
15467
- map: '{{params.map}}',
15468
- _replace: true,
15469
- },
15470
- ],
16770
+ composition: {
16771
+ version: '1.0.0',
16772
+ links: [
16773
+ {
16774
+ id: '{{params.fromWidget}}.{{params.fromOutput}}->{{params.toWidget}}.{{params.toInput}}',
16775
+ from: {
16776
+ kind: 'component-port',
16777
+ ref: { widget: '{{params.fromWidget}}', port: '{{params.fromOutput}}', direction: 'output' },
16778
+ },
16779
+ to: {
16780
+ kind: 'component-port',
16781
+ ref: { widget: '{{params.toWidget}}', port: '{{params.toInput}}', direction: 'input' },
16782
+ },
16783
+ intent: 'event-propagation',
16784
+ transform: {
16785
+ version: '2.0',
16786
+ phase: 'link-propagation',
16787
+ mode: 'single-value',
16788
+ steps: [
16789
+ {
16790
+ id: 'pick-master-detail-value',
16791
+ kind: 'pick-path',
16792
+ phase: 'link-propagation',
16793
+ input: { source: 'event' },
16794
+ config: { path: '{{params.map}}' },
16795
+ },
16796
+ ],
16797
+ },
16798
+ metadata: {
16799
+ source: 'persisted-composition-link',
16800
+ },
16801
+ _replace: true,
16802
+ },
16803
+ ],
16804
+ },
15471
16805
  },
15472
16806
  },
15473
16807
  },
@@ -15481,17 +16815,44 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15481
16815
  { name: 'fromWidget', type: 'STRING' },
15482
16816
  { name: 'toWidget', type: 'STRING' },
15483
16817
  ],
15484
- safetyNotes: 'Padrao master-detail: rowClick -> resourceId com map payload.row.id.',
16818
+ safetyNotes: 'Padrao master-detail: rowClick -> resourceId com transform pick-path payload.row.id.',
15485
16819
  patchTemplate: {
15486
16820
  page: {
15487
- connections: [
15488
- {
15489
- from: { widget: '{{params.fromWidget}}', output: 'rowClick' },
15490
- to: { widget: '{{params.toWidget}}', input: 'resourceId' },
15491
- map: 'payload.row.id',
15492
- _replace: true,
15493
- },
15494
- ],
16821
+ composition: {
16822
+ version: '1.0.0',
16823
+ links: [
16824
+ {
16825
+ id: '{{params.fromWidget}}.rowClick->{{params.toWidget}}.resourceId',
16826
+ from: {
16827
+ kind: 'component-port',
16828
+ ref: { widget: '{{params.fromWidget}}', port: 'rowClick', direction: 'output' },
16829
+ },
16830
+ to: {
16831
+ kind: 'component-port',
16832
+ ref: { widget: '{{params.toWidget}}', port: 'resourceId', direction: 'input' },
16833
+ },
16834
+ intent: 'event-propagation',
16835
+ transform: {
16836
+ version: '2.0',
16837
+ phase: 'link-propagation',
16838
+ mode: 'single-value',
16839
+ steps: [
16840
+ {
16841
+ id: 'pick-master-row-id',
16842
+ kind: 'pick-path',
16843
+ phase: 'link-propagation',
16844
+ input: { source: 'event' },
16845
+ config: { path: 'payload.row.id' },
16846
+ },
16847
+ ],
16848
+ },
16849
+ metadata: {
16850
+ source: 'persisted-composition-link',
16851
+ },
16852
+ _replace: true,
16853
+ },
16854
+ ],
16855
+ },
15495
16856
  },
15496
16857
  },
15497
16858
  },
@@ -15506,7 +16867,7 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15506
16867
  { name: 'formId', type: 'STRING' },
15507
16868
  { name: 'resourcePath', type: 'STRING' },
15508
16869
  ],
15509
- safetyNotes: 'Padrao master-detail: tabela (rowClick) -> formulario (resourceId) com map payload.row.id.',
16870
+ safetyNotes: 'Padrao master-detail: tabela (rowClick) -> formulario (resourceId) com transform pick-path payload.row.id.',
15510
16871
  patchTemplate: {
15511
16872
  page: {
15512
16873
  widgets: [
@@ -15537,45 +16898,72 @@ const DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK = {
15537
16898
  _replace: true,
15538
16899
  },
15539
16900
  ],
15540
- connections: [
15541
- {
15542
- from: { widget: '{{params.tableId}}', output: 'rowClick' },
15543
- to: { widget: '{{params.formId}}', input: 'resourceId' },
15544
- map: 'payload.row.id',
15545
- _replace: true,
15546
- },
15547
- ],
16901
+ composition: {
16902
+ version: '1.0.0',
16903
+ links: [
16904
+ {
16905
+ id: '{{params.tableId}}.rowClick->{{params.formId}}.resourceId',
16906
+ from: {
16907
+ kind: 'component-port',
16908
+ ref: { widget: '{{params.tableId}}', port: 'rowClick', direction: 'output' },
16909
+ },
16910
+ to: {
16911
+ kind: 'component-port',
16912
+ ref: { widget: '{{params.formId}}', port: 'resourceId', direction: 'input' },
16913
+ },
16914
+ intent: 'event-propagation',
16915
+ transform: {
16916
+ version: '2.0',
16917
+ phase: 'link-propagation',
16918
+ mode: 'single-value',
16919
+ steps: [
16920
+ {
16921
+ id: 'pick-master-row-id',
16922
+ kind: 'pick-path',
16923
+ phase: 'link-propagation',
16924
+ input: { source: 'event' },
16925
+ config: { path: 'payload.row.id' },
16926
+ },
16927
+ ],
16928
+ },
16929
+ metadata: {
16930
+ source: 'persisted-composition-link',
16931
+ },
16932
+ _replace: true,
16933
+ },
16934
+ ],
16935
+ },
15548
16936
  },
15549
16937
  },
15550
16938
  },
15551
16939
  ],
15552
16940
  fieldResolvers: {
15553
16941
  'page.widgets[]': ['key'],
15554
- 'page.connections[]': ['_label', 'id', 'name'],
16942
+ 'page.composition.links[]': ['id'],
15555
16943
  },
15556
16944
  hints: [
15557
- 'praxis-dynamic-page é um host de composição: cria widgets, conecta outputs e mantém relações.',
15558
- 'Widgets e connections são arrays; o adapter faz merge por key (widgets) e chave composta (conexões).',
15559
- 'Preferir mudanças incrementais: alterar/estender em vez de substituir toda a página.',
15560
- 'Para remover um item, envie {_remove: true} junto ao widget/connection.',
16945
+ 'praxis-dynamic-page e um host de composicao: cria widgets, conecta ports canonicos e mantem relacoes em page.composition.links.',
16946
+ 'Widgets e composition.links sao arrays; o patching deve fazer merge por key (widgets) e por id (links).',
16947
+ 'Preferir mudancas incrementais: alterar/estender em vez de substituir toda a pagina.',
16948
+ 'Para remover um item, envie {_remove: true} junto ao widget/link.',
15561
16949
  'Para substituir um item inteiro, envie {_replace: true}.',
15562
- 'Para alterar origem/destino de conexão, use {_beforeKey} com a chave antiga.',
15563
- 'Use keys estáveis para widgets ao criar conexões.',
15564
- 'resourcePath sempre é o path base do recurso (sem /filter, /all).',
16950
+ 'Para alterar origem/destino de link, use {_beforeKey} com o id antigo.',
16951
+ 'Use keys estaveis para widgets e ids estaveis para links ao criar composicoes.',
16952
+ 'resourcePath sempre e o path base do recurso (sem /filter, /all).',
15565
16953
  'Praxis Table gera colunas dinamicamente a partir do resourcePath quando columns nao forem definidas.',
15566
16954
  'Praxis Dynamic Form gera campos dinamicamente a partir do resourcePath quando config nao for definida.',
15567
16955
  'Praxis List pode filtrar via config.dataSource.query (enviado para /filter).',
15568
16956
  'Quando houver recursos secundarios (ex.: enderecos), use contextHints.addressResourcePath.',
15569
- 'Exemplo master-detail: tabela emite rowClick -> form.resourceId (map: payload.row.id).',
15570
- 'Pattern Master-Detail: { "from": {"output": "rowClick"}, "to": {"input": "resourceId"}, "map": "payload.row.id" }',
15571
- 'Ao criar widgets, use inputs mínimos e complemente depois (evite inventar campos).',
15572
- 'Quando widgets existem, interpretar outputs/inputs antes de conectar.',
15573
- 'Intenção comum: criar pagina master-detail = criar tabela + criar formulário + conectar seleção.',
15574
- 'Intenção comum: ajustar pagina existente = modificar apenas widgets/inputs necessários.',
15575
- 'Exemplo de conexão (master-detail): { from:{widget:\"masterTable\",output:\"rowClick\"}, to:{widget:\"detailForm\",input:\"resourceId\"}, map:\"payload.row.id\" }',
15576
- 'Exemplo de widget tabela (mínimo): { key:\"masterTable\", definition:{ id:\"praxis-table\", inputs:{ resourcePath:\"/api/x\" }}}',
15577
- 'Exemplo de widget formulário (mínimo): { key:\"detailForm\", definition:{ id:\"praxis-dynamic-form\", inputs:{ resourcePath:\"/api/x\", mode:\"view\" }}}',
15578
- 'Se houver idField conhecido, use map: payload.row.{idField} em conexões.',
16957
+ 'Exemplo master-detail: tabela emite rowClick -> form.resourceId via transform pick-path payload.row.id.',
16958
+ 'Pattern Master-Detail: { "id":"master.rowClick->detail.resourceId", "from":{"kind":"component-port","ref":{"port":"rowClick","direction":"output"}}, "to":{"kind":"component-port","ref":{"port":"resourceId","direction":"input"}}, "transform":{"steps":[{"kind":"pick-path","config":{"path":"payload.row.id"}}]}}',
16959
+ 'Ao criar widgets, use inputs minimos e complemente depois (evite inventar campos).',
16960
+ 'Quando widgets ja existem, interpretar ports de origem/destino antes de conectar.',
16961
+ 'Intencao comum: criar pagina master-detail = criar tabela + criar formulario + conectar selecao via composition.links.',
16962
+ 'Intencao comum: ajustar pagina existente = modificar apenas widgets/inputs necessarios.',
16963
+ 'Exemplo de link canonico (master-detail): { id:"masterTable.rowClick->detailForm.resourceId", from:{kind:"component-port",ref:{widget:"masterTable",port:"rowClick",direction:"output"}}, to:{kind:"component-port",ref:{widget:"detailForm",port:"resourceId",direction:"input"}}, intent:"event-propagation", transform:{version:"2.0",phase:"link-propagation",mode:"single-value",steps:[{kind:"pick-path",phase:"link-propagation",input:{source:"event"},config:{path:"payload.row.id"}}]}}',
16964
+ 'Exemplo de widget tabela (minimo): { key:"masterTable", definition:{ id:"praxis-table", inputs:{ resourcePath:"/api/x" }}}',
16965
+ 'Exemplo de widget formulario (minimo): { key:"detailForm", definition:{ id:"praxis-dynamic-form", inputs:{ resourcePath:"/api/x", mode:"view" }}}',
16966
+ 'Se houver idField conhecido, use transform pick-path payload.row.{idField} em links canonicos.',
15579
16967
  ],
15580
16968
  };
15581
16969
 
@@ -16072,7 +17460,7 @@ class PraxisRichTextBlockComponent {
16072
17460
 
16073
17461
  <div class="prt-content" [innerHTML]="renderedContent"></div>
16074
17462
  </section>
16075
- `, isInline: true, styles: [":host{display:block;--prt-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 72%, transparent);--prt-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-surface-container-lowest) 4%), color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, var(--md-sys-color-surface) 8%) );--prt-emphasis-border: color-mix(in srgb, var(--md-sys-color-primary) 32%, var(--md-sys-color-outline-variant));--prt-emphasis-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary-container) 36%, var(--md-sys-color-surface) 64%), color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-primary-container) 4%) );--prt-subtle-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 82%, transparent);--prt-icon-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 64%, transparent);--prt-code-bg: color-mix(in srgb, var(--md-sys-color-surface-container-highest) 82%, transparent)}:host-context(.mdc-theme-dark),:host-context(.theme-dark){--prt-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 84%, transparent);--prt-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, var(--md-sys-color-surface) 8%), color-mix(in srgb, var(--md-sys-color-surface-container) 90%, var(--md-sys-color-surface-container-high) 10%) );--prt-emphasis-border: color-mix(in srgb, var(--md-sys-color-primary) 42%, var(--md-sys-color-outline-variant));--prt-emphasis-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary-container) 28%, var(--md-sys-color-surface-container-low) 72%), color-mix(in srgb, var(--md-sys-color-surface-container) 92%, var(--md-sys-color-primary-container) 8%) );--prt-subtle-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 88%, transparent);--prt-icon-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 42%, transparent);--prt-code-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high) 88%, transparent)}.prt-block{display:grid;gap:12px;padding:16px 18px;border-radius:16px;border:1px solid var(--prt-border);background:var(--prt-bg);color:var(--md-sys-color-on-surface)}.prt-block-emphasis{border-color:var(--prt-emphasis-border);background:var(--prt-emphasis-bg)}.prt-block-subtle{background:var(--prt-subtle-bg);border-style:dashed}.prt-block-plain{padding:0;border:0;border-radius:0;background:transparent}.prt-head{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:start}.prt-icon{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:12px;background:var(--prt-icon-bg);color:var(--md-sys-color-primary);font-size:20px;line-height:1}.prt-title-wrap{display:grid;gap:4px}.prt-title{margin:0;font-size:1rem;font-weight:700;line-height:1.3}.prt-subtitle{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.86rem;line-height:1.4}.prt-content{color:var(--md-sys-color-on-surface);font-size:.94rem;line-height:1.6}.prt-content :where(p,ul){margin:0}.prt-content :where(p+p,p+ul,ul+p,ul+ul){margin-top:10px}.prt-content ul{padding-left:18px}.prt-content a{color:var(--md-sys-color-primary);text-decoration:underline;text-underline-offset:2px}.prt-content strong{font-weight:700}.prt-content em{font-style:italic}.prt-content code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.88em;padding:.1em .35em;border-radius:6px;background:var(--prt-code-bg)}.prt-block-plain .prt-icon{width:32px;height:32px;border-radius:10px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 80%,transparent)}.prt-block-plain .prt-content{font-size:.92rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
17463
+ `, isInline: true, styles: [":host{display:block;--prt-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 72%, transparent);--prt-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-surface-container-lowest) 4%), color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, var(--md-sys-color-surface) 8%) );--prt-emphasis-border: color-mix(in srgb, var(--md-sys-color-primary) 32%, var(--md-sys-color-outline-variant));--prt-emphasis-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary-container) 36%, var(--md-sys-color-surface) 64%), color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-primary-container) 4%) );--prt-subtle-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 82%, transparent);--prt-icon-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 64%, transparent);--prt-code-bg: color-mix(in srgb, var(--md-sys-color-surface-container-highest) 82%, transparent)}:host-context(.mdc-theme-dark),:host-context(.theme-dark){--prt-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 84%, transparent);--prt-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, var(--md-sys-color-surface) 8%), color-mix(in srgb, var(--md-sys-color-surface-container) 90%, var(--md-sys-color-surface-container-high) 10%) );--prt-emphasis-border: color-mix(in srgb, var(--md-sys-color-primary) 42%, var(--md-sys-color-outline-variant));--prt-emphasis-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary-container) 28%, var(--md-sys-color-surface-container-low) 72%), color-mix(in srgb, var(--md-sys-color-surface-container) 92%, var(--md-sys-color-primary-container) 8%) );--prt-subtle-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 88%, transparent);--prt-icon-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 42%, transparent);--prt-code-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high) 88%, transparent)}.prt-block{display:grid;gap:12px;padding:16px 18px;border-radius:16px;border:1px solid var(--prt-border);background:var(--prt-bg);color:var(--md-sys-color-on-surface)}.prt-block-emphasis{border-color:var(--prt-emphasis-border);background:var(--prt-emphasis-bg)}.prt-block-subtle{background:var(--prt-subtle-bg);border-style:dashed}.prt-block-plain{padding:0;border:0;border-radius:0;background:transparent}.prt-head{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:start}.prt-icon{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:12px;background:var(--prt-icon-bg);color:var(--md-sys-color-primary);font-size:20px;line-height:1}.prt-title-wrap{display:grid;gap:4px}.prt-title{margin:0;font-size:1rem;font-weight:700;line-height:1.3}.prt-subtitle{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.86rem;line-height:1.4}.prt-content{color:var(--md-sys-color-on-surface);font-size:.94rem;line-height:1.6}.prt-content :where(p,ul){margin:0}.prt-content :where(p+p,p+ul,ul+p,ul+ul){margin-top:10px}.prt-content ul{padding-left:18px}.prt-content a{color:var(--md-sys-color-primary);text-decoration:underline;text-underline-offset:2px}.prt-content strong{font-weight:700}.prt-content em{font-style:italic}.prt-content code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.88em;padding:.1em .35em;border-radius:6px;background:var(--prt-code-bg)}.prt-block-plain .prt-icon{width:32px;height:32px;border-radius:10px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 80%,transparent)}.prt-block-plain .prt-content{font-size:.92rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16076
17464
  }
16077
17465
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisRichTextBlockComponent, decorators: [{
16078
17466
  type: Component,
@@ -17313,7 +18701,7 @@ class WidgetShellComponent {
17313
18701
  @if (expanded || fullscreen) {
17314
18702
  <div class="pdx-shell-backdrop" (click)="closeOverlay()"></div>
17315
18703
  }
17316
- `, isInline: true, styles: [":host{display:block;height:100%}.pdx-shell{position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var(--pdx-shell-card-bg, var(--pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low)));border:1px solid var(--pdx-shell-card-border, var(--pdx-dashboard-card-border, var(--md-sys-color-outline-variant)));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.pdx-shell-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var(--pdx-shell-header-bg, var(--md-sys-color-surface-container))}.pdx-shell-header--drag-enabled{cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pdx-shell-header--drag-enabled:active{cursor:grabbing}.pdx-shell-header--drag-enabled:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 72%,white 28%);outline-offset:-2px}.pdx-shell-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0}.pdx-shell-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;align-items:center;gap:6px}.pdx-shell-window-actions{margin-left:auto}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell .pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed .pdx-shell-header{border-bottom-color:transparent}.pdx-shell.expanded .pdx-shell-body,.pdx-shell.fullscreen .pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.expanded .pdx-shell-body>*,.pdx-shell.fullscreen .pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:1200;box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen{position:fixed;top:50%;left:50%;width:95vw;height:95vh;transform:translate(-50%,-50%);z-index:1201;box-shadow:var(--mat-elevation-level8)}.pdx-shell-backdrop{position:fixed;inset:0;z-index:1199;background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18704
+ `, isInline: true, styles: [":host{display:block;height:100%}.pdx-shell{position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var(--pdx-shell-card-bg, var(--pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low)));border:1px solid var(--pdx-shell-card-border, var(--pdx-dashboard-card-border, var(--md-sys-color-outline-variant)));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.pdx-shell-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var(--pdx-shell-header-bg, var(--md-sys-color-surface-container))}.pdx-shell-header--drag-enabled{cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pdx-shell-header--drag-enabled:active{cursor:grabbing}.pdx-shell-header--drag-enabled:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 72%,white 28%);outline-offset:-2px}.pdx-shell-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0}.pdx-shell-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;align-items:center;gap:6px}.pdx-shell-window-actions{margin-left:auto}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell .pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed .pdx-shell-header{border-bottom-color:transparent}.pdx-shell.expanded .pdx-shell-body,.pdx-shell.fullscreen .pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.expanded .pdx-shell-body>*,.pdx-shell.fullscreen .pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:var(--praxis-layer-widget-shell-expanded, 1290);box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen{position:fixed;top:50%;left:50%;width:95vw;height:95vh;transform:translate(-50%,-50%);z-index:var(--praxis-layer-widget-shell-fullscreen, 1291);box-shadow:var(--mat-elevation-level8)}.pdx-shell-backdrop{position:fixed;inset:0;z-index:var(--praxis-layer-widget-shell-backdrop, 1280);background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
17317
18705
  }
17318
18706
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetShellComponent, decorators: [{
17319
18707
  type: Component,
@@ -17449,7 +18837,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
17449
18837
  @if (expanded || fullscreen) {
17450
18838
  <div class="pdx-shell-backdrop" (click)="closeOverlay()"></div>
17451
18839
  }
17452
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;height:100%}.pdx-shell{position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var(--pdx-shell-card-bg, var(--pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low)));border:1px solid var(--pdx-shell-card-border, var(--pdx-dashboard-card-border, var(--md-sys-color-outline-variant)));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.pdx-shell-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var(--pdx-shell-header-bg, var(--md-sys-color-surface-container))}.pdx-shell-header--drag-enabled{cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pdx-shell-header--drag-enabled:active{cursor:grabbing}.pdx-shell-header--drag-enabled:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 72%,white 28%);outline-offset:-2px}.pdx-shell-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0}.pdx-shell-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;align-items:center;gap:6px}.pdx-shell-window-actions{margin-left:auto}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell .pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed .pdx-shell-header{border-bottom-color:transparent}.pdx-shell.expanded .pdx-shell-body,.pdx-shell.fullscreen .pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.expanded .pdx-shell-body>*,.pdx-shell.fullscreen .pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:1200;box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen{position:fixed;top:50%;left:50%;width:95vw;height:95vh;transform:translate(-50%,-50%);z-index:1201;box-shadow:var(--mat-elevation-level8)}.pdx-shell-backdrop{position:fixed;inset:0;z-index:1199;background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"] }]
18840
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;height:100%}.pdx-shell{position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var(--pdx-shell-card-bg, var(--pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low)));border:1px solid var(--pdx-shell-card-border, var(--pdx-dashboard-card-border, var(--md-sys-color-outline-variant)));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.pdx-shell-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var(--pdx-shell-header-bg, var(--md-sys-color-surface-container))}.pdx-shell-header--drag-enabled{cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pdx-shell-header--drag-enabled:active{cursor:grabbing}.pdx-shell-header--drag-enabled:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 72%,white 28%);outline-offset:-2px}.pdx-shell-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0}.pdx-shell-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;align-items:center;gap:6px}.pdx-shell-window-actions{margin-left:auto}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell .pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed .pdx-shell-header{border-bottom-color:transparent}.pdx-shell.expanded .pdx-shell-body,.pdx-shell.fullscreen .pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.expanded .pdx-shell-body>*,.pdx-shell.fullscreen .pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:var(--praxis-layer-widget-shell-expanded, 1290);box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen{position:fixed;top:50%;left:50%;width:95vw;height:95vh;transform:translate(-50%,-50%);z-index:var(--praxis-layer-widget-shell-fullscreen, 1291);box-shadow:var(--mat-elevation-level8)}.pdx-shell-backdrop{position:fixed;inset:0;z-index:var(--praxis-layer-widget-shell-backdrop, 1280);background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"] }]
17453
18841
  }], propDecorators: { shell: [{
17454
18842
  type: Input
17455
18843
  }], context: [{
@@ -17824,10 +19212,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
17824
19212
  }] });
17825
19213
 
17826
19214
  class WidgetPageStateRuntimeService {
17827
- connections;
17828
- constructor(connections) {
17829
- this.connections = connections;
17830
- }
19215
+ connections = new ConnectionManagerService();
17831
19216
  normalizeState(state) {
17832
19217
  if (!state)
17833
19218
  return { values: {} };
@@ -17855,12 +19240,19 @@ class WidgetPageStateRuntimeService {
17855
19240
  const node = normalized.derived?.[key];
17856
19241
  if (!node)
17857
19242
  continue;
17858
- const value = this.computeDerivedNode(node, {
17859
- state: effectiveValues,
17860
- values: primaryValues,
17861
- derived: derivedValues,
17862
- context: this.clone(context) || {},
17863
- }, diagnostics, key);
19243
+ let value;
19244
+ try {
19245
+ value = this.computeDerivedNode(node, {
19246
+ state: effectiveValues,
19247
+ values: primaryValues,
19248
+ derived: derivedValues,
19249
+ context: this.clone(context) || {},
19250
+ }, diagnostics, key);
19251
+ }
19252
+ catch (error) {
19253
+ diagnostics.push(`Derived state '${key}' failed: ${this.describeError(error)}`);
19254
+ continue;
19255
+ }
17864
19256
  if (value === undefined)
17865
19257
  continue;
17866
19258
  effectiveValues = this.connections.setValueAtPath(effectiveValues, key, this.clone(value));
@@ -17909,7 +19301,8 @@ class WidgetPageStateRuntimeService {
17909
19301
  visiting.add(key);
17910
19302
  const deps = derived[key]?.dependsOn || [];
17911
19303
  for (const dep of deps) {
17912
- const dependencyKey = keys.find((candidate) => dep === candidate || dep.startsWith(`${candidate}.`));
19304
+ const normalizedDependency = this.normalizeDependencyPath(dep);
19305
+ const dependencyKey = keys.find((candidate) => (normalizedDependency === candidate || normalizedDependency.startsWith(`${candidate}.`)));
17913
19306
  if (!dependencyKey)
17914
19307
  continue;
17915
19308
  visit(dependencyKey, [...stack, key]);
@@ -17924,8 +19317,10 @@ class WidgetPageStateRuntimeService {
17924
19317
  }
17925
19318
  computeDerivedNode(node, env, diagnostics, key) {
17926
19319
  const compute = node.compute;
17927
- if (!compute)
19320
+ if (!compute) {
19321
+ diagnostics.push(`Derived state '${key}' is missing compute definition.`);
17928
19322
  return undefined;
19323
+ }
17929
19324
  if (compute.kind === 'template') {
17930
19325
  return this.resolveTemplate(compute.value, env);
17931
19326
  }
@@ -17936,9 +19331,16 @@ class WidgetPageStateRuntimeService {
17936
19331
  diagnostics.push(`Derived state '${key}' references transformer '${compute.transformerId}', but transformer runtime is not implemented yet.`);
17937
19332
  return undefined;
17938
19333
  }
17939
- if (compute.kind !== 'operator')
19334
+ if (compute.kind !== 'operator') {
19335
+ const computeKind = compute.kind;
19336
+ diagnostics.push(`Derived state '${key}' uses unsupported compute kind '${String(computeKind ?? '')}'.`);
17940
19337
  return undefined;
19338
+ }
17941
19339
  const operator = (compute.operator || '').trim();
19340
+ if (!operator) {
19341
+ diagnostics.push(`Derived state '${key}' uses operator compute without a valid operator.`);
19342
+ return undefined;
19343
+ }
17942
19344
  const options = compute.options || {};
17943
19345
  const sources = this.resolveSourceValues(node.dependsOn, env.state);
17944
19346
  switch (operator) {
@@ -18048,13 +19450,17 @@ class WidgetPageStateRuntimeService {
18048
19450
  }
18049
19451
  }
18050
19452
  resolveNamedSource(sourcePath, sources, effectiveState) {
18051
- if (typeof sourcePath === 'string' && sourcePath.trim()) {
18052
- return this.readPath(effectiveState, sourcePath.trim());
19453
+ const normalizedSourcePath = this.normalizeDependencyPath(sourcePath);
19454
+ if (normalizedSourcePath) {
19455
+ return this.readPath(effectiveState, normalizedSourcePath);
18053
19456
  }
18054
19457
  return sources[0];
18055
19458
  }
18056
19459
  resolveSourceValues(paths, state) {
18057
- return (paths || []).map((path) => this.readPath(state, path));
19460
+ return (paths || [])
19461
+ .map((path) => this.normalizeDependencyPath(path))
19462
+ .filter(Boolean)
19463
+ .map((path) => this.readPath(state, path));
18058
19464
  }
18059
19465
  aggregateArray(rows, metrics) {
18060
19466
  const output = {};
@@ -18217,6 +19623,22 @@ class WidgetPageStateRuntimeService {
18217
19623
  }
18218
19624
  return node;
18219
19625
  }
19626
+ normalizeDependencyPath(path) {
19627
+ if (typeof path !== 'string')
19628
+ return '';
19629
+ const trimmed = path.trim();
19630
+ if (!trimmed)
19631
+ return '';
19632
+ const exactTemplate = trimmed.match(/^\$\{([^}]+)\}$/);
19633
+ const normalized = exactTemplate ? exactTemplate[1].trim() : trimmed;
19634
+ if (normalized.startsWith('state.')) {
19635
+ return normalized.slice('state.'.length);
19636
+ }
19637
+ if (normalized.startsWith('derived.')) {
19638
+ return normalized.slice('derived.'.length);
19639
+ }
19640
+ return normalized;
19641
+ }
18220
19642
  readPath(source, path) {
18221
19643
  if (!path)
18222
19644
  return source;
@@ -18292,213 +19714,20 @@ class WidgetPageStateRuntimeService {
18292
19714
  return value;
18293
19715
  return JSON.parse(JSON.stringify(value));
18294
19716
  }
18295
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetPageStateRuntimeService, deps: [{ token: ConnectionManagerService }], target: i0.ɵɵFactoryTarget.Injectable });
19717
+ describeError(error) {
19718
+ if (error instanceof Error)
19719
+ return error.message;
19720
+ if (typeof error === 'string')
19721
+ return error;
19722
+ return 'Unknown runtime error';
19723
+ }
19724
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetPageStateRuntimeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18296
19725
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetPageStateRuntimeService, providedIn: 'root' });
18297
19726
  }
18298
19727
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetPageStateRuntimeService, decorators: [{
18299
19728
  type: Injectable,
18300
19729
  args: [{ providedIn: 'root' }]
18301
- }], ctorParameters: () => [{ type: ConnectionManagerService }] });
18302
-
18303
- function adaptWidgetConnectionToCompositionLink(connection) {
18304
- const from = adaptSource(connection);
18305
- const to = adaptTarget(connection);
18306
- return {
18307
- id: buildLinkId(connection),
18308
- from,
18309
- to,
18310
- intent: resolveIntent(from, to),
18311
- transform: adaptTransform(connection),
18312
- conditions: adaptConditions(connection),
18313
- policy: adaptPolicy(connection),
18314
- metadata: {
18315
- source: 'legacy-widget-connection',
18316
- traceKey: buildTraceKey(connection),
18317
- },
18318
- };
18319
- }
18320
- function adaptWidgetConnectionsToCompositionLinks(connections) {
18321
- return (connections || []).map((connection) => adaptWidgetConnectionToCompositionLink(connection));
18322
- }
18323
- function adaptSource(connection) {
18324
- if ('widget' in connection.from) {
18325
- return {
18326
- kind: 'component-port',
18327
- ref: {
18328
- widget: connection.from.widget,
18329
- port: connection.from.output,
18330
- direction: 'output',
18331
- },
18332
- };
18333
- }
18334
- return {
18335
- kind: 'state',
18336
- ref: {
18337
- path: connection.from.state,
18338
- writable: false,
18339
- },
18340
- };
18341
- }
18342
- function adaptTarget(connection) {
18343
- if ('widget' in connection.to) {
18344
- const binding = normalizeBindingPath(connection.to.input);
18345
- return {
18346
- kind: 'component-port',
18347
- ref: {
18348
- widget: connection.to.widget,
18349
- port: binding.port,
18350
- direction: 'input',
18351
- bindingPath: binding.bindingPath,
18352
- },
18353
- };
18354
- }
18355
- return {
18356
- kind: 'state',
18357
- ref: {
18358
- path: connection.to.state,
18359
- layer: 'values',
18360
- writable: true,
18361
- },
18362
- };
18363
- }
18364
- function adaptTransform(connection) {
18365
- if (connection.set !== undefined) {
18366
- return {
18367
- version: '2.0',
18368
- phase: 'link-propagation',
18369
- mode: inferPipelineMode(connection.set),
18370
- sourceBindings: [{ source: 'constant', value: connection.set }],
18371
- steps: [
18372
- {
18373
- id: 'constant',
18374
- kind: 'constant',
18375
- phase: 'link-propagation',
18376
- input: {
18377
- source: 'constant',
18378
- value: connection.set,
18379
- },
18380
- },
18381
- ],
18382
- };
18383
- }
18384
- if (!connection.map) {
18385
- return undefined;
18386
- }
18387
- const trimmed = connection.map.trim();
18388
- const kind = resolveMapTransformKind(trimmed);
18389
- return {
18390
- version: '2.0',
18391
- phase: 'link-propagation',
18392
- mode: kind === 'array-template'
18393
- ? 'collection'
18394
- : kind === 'object-template'
18395
- ? 'object-fragment'
18396
- : 'single-value',
18397
- sourceBindings: [{ source: 'event' }],
18398
- steps: [
18399
- {
18400
- id: 'legacy-map',
18401
- kind,
18402
- phase: 'link-propagation',
18403
- input: { source: 'event' },
18404
- config: buildMapConfig(kind, trimmed),
18405
- },
18406
- ],
18407
- };
18408
- }
18409
- function adaptConditions(connection) {
18410
- const conditions = [];
18411
- if (connection.meta?.filterExpr) {
18412
- conditions.push({
18413
- kind: 'expression',
18414
- expression: connection.meta.filterExpr,
18415
- description: 'Adaptado de WidgetConnection.meta.filterExpr',
18416
- });
18417
- }
18418
- return conditions.length ? conditions : undefined;
18419
- }
18420
- function adaptPolicy(connection) {
18421
- const policy = {};
18422
- if (typeof connection.meta?.debounceMs === 'number') {
18423
- policy.debounceMs = connection.meta.debounceMs;
18424
- }
18425
- if (typeof connection.meta?.distinct === 'boolean') {
18426
- policy.distinct = connection.meta.distinct;
18427
- }
18428
- if (typeof connection.meta?.distinctBy === 'string' && connection.meta.distinctBy.trim()) {
18429
- policy.distinctBy = connection.meta.distinctBy.trim();
18430
- }
18431
- return Object.keys(policy).length ? policy : undefined;
18432
- }
18433
- function resolveIntent(from, to) {
18434
- if (from.kind === 'component-port' && to.kind === 'state') {
18435
- return 'state-write';
18436
- }
18437
- if (from.kind === 'state' && to.kind === 'component-port') {
18438
- return 'state-read';
18439
- }
18440
- if (from.kind === 'state' && to.kind === 'state') {
18441
- return 'data-projection';
18442
- }
18443
- return 'event-propagation';
18444
- }
18445
- function normalizeBindingPath(rawInput) {
18446
- const trimmed = (rawInput || '').trim();
18447
- const dotIndex = trimmed.search(/[.[]/);
18448
- if (dotIndex <= 0) {
18449
- return { port: trimmed };
18450
- }
18451
- return {
18452
- port: trimmed.slice(0, dotIndex),
18453
- bindingPath: trimmed,
18454
- };
18455
- }
18456
- function resolveMapTransformKind(map) {
18457
- if (isQuotedLiteral(map)) {
18458
- return 'template';
18459
- }
18460
- if (map.startsWith('{')) {
18461
- return 'object-template';
18462
- }
18463
- if (map.startsWith('[')) {
18464
- return 'array-template';
18465
- }
18466
- return 'pick-path';
18467
- }
18468
- function buildMapConfig(kind, map) {
18469
- if (kind === 'pick-path') {
18470
- return { path: map };
18471
- }
18472
- if (kind === 'template') {
18473
- return { value: map };
18474
- }
18475
- return { template: map };
18476
- }
18477
- function inferPipelineMode(value) {
18478
- if (Array.isArray(value)) {
18479
- return 'collection';
18480
- }
18481
- if (value != null && typeof value === 'object') {
18482
- return 'object-fragment';
18483
- }
18484
- return 'single-value';
18485
- }
18486
- function isQuotedLiteral(value) {
18487
- return (value.startsWith('"') && value.endsWith('"'))
18488
- || (value.startsWith('\'') && value.endsWith('\''));
18489
- }
18490
- function buildLinkId(connection) {
18491
- return `legacy:${buildTraceKey(connection)}`;
18492
- }
18493
- function buildTraceKey(connection) {
18494
- const from = 'widget' in connection.from
18495
- ? `widget:${connection.from.widget}.${connection.from.output}`
18496
- : `state:${connection.from.state}`;
18497
- const to = 'widget' in connection.to
18498
- ? `widget:${connection.to.widget}.${connection.to.input}`
18499
- : `state:${connection.to.state}`;
18500
- return `${from}->${to}`;
18501
- }
19730
+ }] });
18502
19731
 
18503
19732
  class CompositionRuntimeStore {
18504
19733
  snapshot;
@@ -18606,13 +19835,18 @@ function applyLinkPatches(current, patches) {
18606
19835
  return {
18607
19836
  ...cloneLink(link),
18608
19837
  status: patch.status ?? link.status,
18609
- lastEventAt: patch.lastEventAt ?? link.lastEventAt,
18610
- lastDispatchTraceId: patch.lastDispatchTraceId ?? link.lastDispatchTraceId,
18611
- lastDeliveredValuePreview: patch.lastDeliveredValuePreview ?? link.lastDeliveredValuePreview,
19838
+ lastEventAt: pickPatchedValue(patch, 'lastEventAt', link.lastEventAt),
19839
+ lastDispatchTraceId: pickPatchedValue(patch, 'lastDispatchTraceId', link.lastDispatchTraceId),
19840
+ lastDeliveredValuePreview: pickPatchedValue(patch, 'lastDeliveredValuePreview', link.lastDeliveredValuePreview),
18612
19841
  diagnostics: cloneDiagnostics$1(patch.diagnostics ?? link.diagnostics),
18613
19842
  };
18614
19843
  });
18615
19844
  }
19845
+ function pickPatchedValue(patch, key, fallback) {
19846
+ return Object.prototype.hasOwnProperty.call(patch, key)
19847
+ ? patch[key]
19848
+ : fallback;
19849
+ }
18616
19850
  function cloneLinks(links) {
18617
19851
  return links.map((link) => cloneLink(link));
18618
19852
  }
@@ -18779,19 +20013,33 @@ class TransformRuntimeService {
18779
20013
  const env = this.buildEnvironment(runtime);
18780
20014
  let current = this.resolvePipelineSeed(pipeline, env);
18781
20015
  for (const [index, step] of pipeline.steps.entries()) {
18782
- if (!this.matchesStepCondition(step.when, env)) {
18783
- continue;
20016
+ try {
20017
+ if (!this.matchesStepCondition(step.when, env)) {
20018
+ continue;
20019
+ }
20020
+ if (!isSupportedTransformKind(step.kind)) {
20021
+ diagnostics.push(this.createDiagnostic('RUNTIME_TRANSFORM_UNSUPPORTED', `O transform ${step.kind} ainda nao e suportado pelo runtime da Wave 1.`, {
20022
+ kind: 'transform-step',
20023
+ transformIndex: index,
20024
+ label: step.id,
20025
+ }, { transformKind: step.kind, pipelineId: pipeline.id, stepId: step.id }, undefined, 'unsupported-kind'));
20026
+ continue;
20027
+ }
20028
+ current = this.executeStep(step, current, env, diagnostics, index);
20029
+ env.current = this.clone(current);
18784
20030
  }
18785
- if (!isSupportedTransformKind(step.kind)) {
18786
- diagnostics.push(this.createDiagnostic('RUNTIME_TRANSFORM_UNSUPPORTED', `O transform ${step.kind} ainda nao e suportado pelo runtime da Wave 1.`, {
20031
+ catch (error) {
20032
+ diagnostics.push(this.createDiagnostic('RUNTIME_TRANSFORM_STEP_FAILED', `O transform ${step.kind} falhou no passo '${step.id || index}'. ${this.describeError(error)}`, {
18787
20033
  kind: 'transform-step',
18788
20034
  transformIndex: index,
18789
20035
  label: step.id,
18790
- }, { transformKind: step.kind, pipelineId: pipeline.id }));
18791
- continue;
20036
+ }, {
20037
+ transformKind: step.kind,
20038
+ pipelineId: pipeline.id,
20039
+ stepId: step.id,
20040
+ errorMessage: this.describeError(error),
20041
+ }, undefined, 'step-failed'));
18792
20042
  }
18793
- current = this.executeStep(step, current, env, diagnostics, index);
18794
- env.current = this.clone(current);
18795
20043
  }
18796
20044
  if (current === undefined && pipeline.fallbackValue !== undefined) {
18797
20045
  current = this.clone(pipeline.fallbackValue);
@@ -18846,9 +20094,9 @@ class TransformRuntimeService {
18846
20094
  case 'array-template':
18847
20095
  return this.resolveStructuredTemplate(step, inputs[0], env);
18848
20096
  case 'coalesce':
18849
- return this.resolveCoalesce(step, inputs, env);
20097
+ return this.resolveCoalesce(step, inputs, env, diagnostics, index);
18850
20098
  case 'merge-objects':
18851
- return this.resolveMergeObjects(step, inputs, env);
20099
+ return this.resolveMergeObjects(step, inputs, env, diagnostics, index);
18852
20100
  case 'select-case':
18853
20101
  return this.resolveSelectCase(step, inputs[0], env);
18854
20102
  default:
@@ -18856,7 +20104,7 @@ class TransformRuntimeService {
18856
20104
  kind: 'transform-step',
18857
20105
  transformIndex: index,
18858
20106
  label: step.id,
18859
- }, { transformKind: step.kind }));
20107
+ }, { transformKind: step.kind }, undefined, 'unsupported-kind'));
18860
20108
  return current;
18861
20109
  }
18862
20110
  }
@@ -18958,14 +20206,11 @@ class TransformRuntimeService {
18958
20206
  const template = step.config?.['template'] ?? source;
18959
20207
  return this.templateResolver.resolveTemplate(template, this.buildTemplateContext(env, source));
18960
20208
  }
18961
- resolveCoalesce(step, inputs, env) {
18962
- const configuredPaths = Array.isArray(step.config?.['paths'])
18963
- ? step.config?.['paths']
18964
- : [];
18965
- const candidates = configuredPaths.map((path) => typeof path === 'string'
18966
- ? this.pathAccessor.extractByPath({ state: env['state'], payload: env['payload'], context: env['context'] }, path)
18967
- : undefined);
18968
- for (const value of [...inputs, ...candidates]) {
20209
+ resolveCoalesce(step, inputs, env, diagnostics, index) {
20210
+ const configuredValues = this.resolveConfiguredValues(step, env, diagnostics, index);
20211
+ const explicitInputs = step.inputs?.length || step.input ? inputs : [];
20212
+ const fallbackInputs = explicitInputs.length ? [] : inputs;
20213
+ for (const value of [...explicitInputs, ...configuredValues, ...fallbackInputs]) {
18969
20214
  if (value == null) {
18970
20215
  continue;
18971
20216
  }
@@ -18976,16 +20221,11 @@ class TransformRuntimeService {
18976
20221
  }
18977
20222
  return this.clone(step.config?.['fallback']);
18978
20223
  }
18979
- resolveMergeObjects(step, inputs, env) {
18980
- const configuredPaths = Array.isArray(step.config?.['paths'])
18981
- ? step.config?.['paths']
18982
- : [];
18983
- const values = [
18984
- ...inputs,
18985
- ...configuredPaths.map((path) => typeof path === 'string'
18986
- ? this.pathAccessor.extractByPath({ state: env['state'], payload: env['payload'], context: env['context'] }, path)
18987
- : undefined),
18988
- ];
20224
+ resolveMergeObjects(step, inputs, env, diagnostics, index) {
20225
+ const configuredValues = this.resolveConfiguredValues(step, env, diagnostics, index);
20226
+ const explicitInputs = step.inputs?.length || step.input ? inputs : [];
20227
+ const fallbackInputs = explicitInputs.length || configuredValues.length ? [] : inputs;
20228
+ const values = [...explicitInputs, ...configuredValues, ...fallbackInputs];
18989
20229
  return values.reduce((acc, value) => {
18990
20230
  if (!this.isPlainObject(value)) {
18991
20231
  return acc;
@@ -18993,6 +20233,59 @@ class TransformRuntimeService {
18993
20233
  return deepMerge(acc, this.clone(value));
18994
20234
  }, {});
18995
20235
  }
20236
+ resolveConfiguredValues(step, env, diagnostics, index) {
20237
+ if (!Array.isArray(step.config?.['paths'])) {
20238
+ return [];
20239
+ }
20240
+ const rawPaths = step.config?.['paths'];
20241
+ const validPaths = [];
20242
+ const invalidEntries = [];
20243
+ for (const item of rawPaths) {
20244
+ if (typeof item === 'string' && item.trim()) {
20245
+ validPaths.push(item);
20246
+ }
20247
+ else {
20248
+ invalidEntries.push(item);
20249
+ }
20250
+ }
20251
+ if (invalidEntries.length) {
20252
+ diagnostics.push(this.createDiagnostic('RUNTIME_TRANSFORM_CONFIG_INVALID', `O transform ${step.kind} recebeu entradas invalidas em config.paths no passo '${step.id || index}'.`, {
20253
+ kind: 'transform-step',
20254
+ transformIndex: index,
20255
+ label: step.id,
20256
+ }, {
20257
+ transformKind: step.kind,
20258
+ stepId: step.id,
20259
+ invalidPathEntries: this.clone(invalidEntries),
20260
+ }, 'warning', 'invalid-path-entries'));
20261
+ }
20262
+ if (!validPaths.length) {
20263
+ if (rawPaths.length) {
20264
+ diagnostics.push(this.createDiagnostic('RUNTIME_TRANSFORM_CONFIG_INVALID', `O transform ${step.kind} nao encontrou nenhum path valido em config.paths no passo '${step.id || index}'.`, {
20265
+ kind: 'transform-step',
20266
+ transformIndex: index,
20267
+ label: step.id,
20268
+ }, {
20269
+ transformKind: step.kind,
20270
+ stepId: step.id,
20271
+ }, 'warning', 'missing-valid-paths'));
20272
+ }
20273
+ return [];
20274
+ }
20275
+ const resolved = validPaths.map((path) => this.resolveConfiguredPath(path, env));
20276
+ if (resolved.every((value) => value === undefined)) {
20277
+ diagnostics.push(this.createDiagnostic('RUNTIME_TRANSFORM_PATHS_UNRESOLVED', `O transform ${step.kind} nao resolveu nenhum dos paths configurados no passo '${step.id || index}'.`, {
20278
+ kind: 'transform-step',
20279
+ transformIndex: index,
20280
+ label: step.id,
20281
+ }, {
20282
+ transformKind: step.kind,
20283
+ stepId: step.id,
20284
+ paths: validPaths,
20285
+ }, 'warning', `paths-unresolved:${this.hashIdParts(validPaths)}`));
20286
+ }
20287
+ return resolved;
20288
+ }
18996
20289
  resolveSelectCase(step, source, env) {
18997
20290
  const cases = Array.isArray(step.config?.['cases'])
18998
20291
  ? step.config?.['cases']
@@ -19018,7 +20311,7 @@ class TransformRuntimeService {
19018
20311
  ? conditionRecord['path'].trim()
19019
20312
  : '';
19020
20313
  const value = path
19021
- ? this.pathAccessor.extractByPath({ source, state: env['state'], payload: env['payload'], context: env['context'] }, path)
20314
+ ? this.resolveCasePath(path, env, source)
19022
20315
  : source;
19023
20316
  if ('equals' in conditionRecord) {
19024
20317
  return value === conditionRecord['equals'];
@@ -19036,12 +20329,38 @@ class TransformRuntimeService {
19036
20329
  }
19037
20330
  resolveCaseValue(item, env, source) {
19038
20331
  if (typeof item['path'] === 'string' && item['path'].trim()) {
19039
- return this.pathAccessor.extractByPath({ source, state: env['state'], payload: env['payload'], context: env['context'] }, item['path']);
20332
+ return this.resolveCasePath(item['path'], env, source);
19040
20333
  }
19041
20334
  if (item['template'] !== undefined) {
19042
20335
  return this.templateResolver.resolveTemplate(item['template'], this.buildTemplateContext(env, source));
19043
20336
  }
19044
- return this.clone(item['value']);
20337
+ return this.templateResolver.resolveTemplate(item['value'], this.buildTemplateContext(env, source));
20338
+ }
20339
+ resolveCasePath(path, env, source) {
20340
+ if (/^(event|payload|state|context|current|source)\b/.test(path)) {
20341
+ return this.pathAccessor.extractByPath({
20342
+ event: env['event'],
20343
+ payload: env['payload'],
20344
+ state: env['state'],
20345
+ context: env['context'],
20346
+ current: source,
20347
+ source,
20348
+ }, path);
20349
+ }
20350
+ return this.pathAccessor.extractByPath(source, path);
20351
+ }
20352
+ resolveConfiguredPath(path, env) {
20353
+ if (/^(event|payload|state|context|current|source)\b/.test(path)) {
20354
+ return this.pathAccessor.extractByPath({
20355
+ event: env['event'],
20356
+ payload: env['payload'],
20357
+ state: env['state'],
20358
+ context: env['context'],
20359
+ current: env['current'],
20360
+ source: env['current'],
20361
+ }, path);
20362
+ }
20363
+ return this.pathAccessor.extractByPath(env['current'], path);
19045
20364
  }
19046
20365
  buildEffectiveState(state, derived, transient) {
19047
20366
  return deepMerge(deepMerge(this.clone(state) || {}, this.clone(derived) || {}), this.clone(transient) || {});
@@ -19080,26 +20399,65 @@ class TransformRuntimeService {
19080
20399
  }
19081
20400
  return false;
19082
20401
  }
19083
- createDiagnostic(code, message, subject, details) {
20402
+ createDiagnostic(code, message, subject, details, severity = 'error', occurrenceKey) {
19084
20403
  return {
19085
- id: `${code}:${subject.transformIndex ?? 'root'}`,
20404
+ id: this.buildDiagnosticId(code, subject, occurrenceKey),
19086
20405
  code,
19087
- severity: 'error',
20406
+ severity,
19088
20407
  phase: 'runtime-transform',
19089
20408
  message,
19090
20409
  summary: message,
19091
20410
  subject,
19092
20411
  details,
19093
20412
  source: 'runtime-engine',
19094
- blocking: true,
20413
+ blocking: severity === 'error' || severity === 'fatal',
19095
20414
  createdAt: new Date().toISOString(),
19096
20415
  };
19097
20416
  }
19098
- clone(value) {
19099
- if (value == null || typeof value !== 'object') {
19100
- return value;
20417
+ clone(value) {
20418
+ if (value == null || typeof value !== 'object') {
20419
+ return value;
20420
+ }
20421
+ return JSON.parse(JSON.stringify(value));
20422
+ }
20423
+ describeError(error) {
20424
+ if (error instanceof Error) {
20425
+ return error.message;
20426
+ }
20427
+ if (typeof error === 'string') {
20428
+ return error;
20429
+ }
20430
+ return 'Erro desconhecido.';
20431
+ }
20432
+ buildDiagnosticId(code, subject, occurrenceKey) {
20433
+ const parts = [
20434
+ code,
20435
+ String(subject.transformIndex ?? 'root'),
20436
+ ];
20437
+ if (subject.label) {
20438
+ parts.push(this.sanitizeIdPart(subject.label));
20439
+ }
20440
+ if (occurrenceKey) {
20441
+ parts.push(this.sanitizeIdPart(occurrenceKey));
20442
+ }
20443
+ return parts.filter(Boolean).join(':');
20444
+ }
20445
+ sanitizeIdPart(value) {
20446
+ return value
20447
+ .trim()
20448
+ .toLowerCase()
20449
+ .replace(/[^a-z0-9]+/g, '-')
20450
+ .replace(/^-+|-+$/g, '')
20451
+ .slice(0, 48);
20452
+ }
20453
+ hashIdParts(values) {
20454
+ let hash = 0;
20455
+ for (const value of values) {
20456
+ for (const char of value) {
20457
+ hash = ((hash * 31) + char.charCodeAt(0)) >>> 0;
20458
+ }
19101
20459
  }
19102
- return JSON.parse(JSON.stringify(value));
20460
+ return hash.toString(36);
19103
20461
  }
19104
20462
  }
19105
20463
 
@@ -19153,7 +20511,7 @@ class LinkExecutorService {
19153
20511
  skippedReason: missingResolution.reason,
19154
20512
  };
19155
20513
  }
19156
- const policySkip = this.resolvePolicySkip(link, value, context, now);
20514
+ const policySkip = this.resolvePolicySkip(link, sourceValue, value, context, now);
19157
20515
  if (policySkip) {
19158
20516
  return {
19159
20517
  status: 'skipped',
@@ -19189,13 +20547,34 @@ class LinkExecutorService {
19189
20547
  if (endpoint.kind === 'state') {
19190
20548
  return this.readStatePath(snapshot, endpoint.ref.path, endpoint.ref.layer);
19191
20549
  }
19192
- if (context.payload !== undefined) {
19193
- return this.clone(context.payload);
20550
+ const payload = context.payload !== undefined
20551
+ ? context.payload
20552
+ : (context.event && typeof context.event === 'object' && 'payload' in context.event)
20553
+ ? context.event['payload']
20554
+ : context.event;
20555
+ if (endpoint.ref.bindingPath) {
20556
+ const extracted = this.extractComponentSourceBinding(endpoint.ref.bindingPath, payload, context);
20557
+ if (extracted !== undefined) {
20558
+ return this.clone(extracted);
20559
+ }
20560
+ }
20561
+ return this.clone(payload);
20562
+ }
20563
+ extractComponentSourceBinding(bindingPath, payload, context) {
20564
+ const normalized = String(bindingPath || '').trim();
20565
+ if (!normalized) {
20566
+ return undefined;
19194
20567
  }
19195
- if (context.event && typeof context.event === 'object' && 'payload' in context.event) {
19196
- return this.clone(context.event['payload']);
20568
+ const fromPayload = this.pathAccessor.extractByPath(payload, normalized);
20569
+ if (fromPayload !== undefined) {
20570
+ return fromPayload;
19197
20571
  }
19198
- return this.clone(context.event);
20572
+ return this.pathAccessor.extractByPath({
20573
+ source: payload,
20574
+ payload,
20575
+ event: context.event,
20576
+ context: context.context || {},
20577
+ }, normalized);
19199
20578
  }
19200
20579
  matchesConditions(link, sourceValue, context, snapshot, diagnostics) {
19201
20580
  for (const condition of link.conditions || []) {
@@ -19321,7 +20700,7 @@ class LinkExecutorService {
19321
20700
  };
19322
20701
  }
19323
20702
  resolveMissingValuePolicy(link, value) {
19324
- if (value !== undefined) {
20703
+ if (value !== undefined && value !== null) {
19325
20704
  return { value, skipped: false };
19326
20705
  }
19327
20706
  switch (link.policy?.missingValuePolicy) {
@@ -19336,13 +20715,22 @@ class LinkExecutorService {
19336
20715
  return { value, skipped: false };
19337
20716
  }
19338
20717
  }
19339
- resolvePolicySkip(link, value, context, now) {
20718
+ resolvePolicySkip(link, sourceValue, value, context, now) {
19340
20719
  if (link.policy?.distinct) {
19341
20720
  const currentValue = link.policy.distinctBy
19342
- ? this.pathAccessor.extractByPath({ payload: value, value }, link.policy.distinctBy)
20721
+ ? this.resolveDistinctValue(link.policy.distinctBy, {
20722
+ sourceValue,
20723
+ transformedValue: value,
20724
+ payload: context.payload,
20725
+ event: context.event,
20726
+ context: context.context,
20727
+ })
19343
20728
  : value;
19344
20729
  const previousValue = link.policy.distinctBy
19345
- ? this.pathAccessor.extractByPath({ payload: context.lastDeliveredValue, value: context.lastDeliveredValue }, link.policy.distinctBy)
20730
+ ? this.resolveDistinctValue(link.policy.distinctBy, {
20731
+ sourceValue: context.lastDeliveredValue,
20732
+ transformedValue: context.lastDeliveredValue,
20733
+ })
19346
20734
  : context.lastDeliveredValue;
19347
20735
  if (this.isEqual(currentValue, previousValue)) {
19348
20736
  return 'distinct-skip';
@@ -19389,6 +20777,45 @@ class LinkExecutorService {
19389
20777
  bindingPath: link.to.ref.bindingPath,
19390
20778
  };
19391
20779
  }
20780
+ resolveDistinctValue(distinctBy, options) {
20781
+ const normalized = String(distinctBy || '').trim();
20782
+ if (!normalized) {
20783
+ return options.transformedValue;
20784
+ }
20785
+ const payload = options.payload !== undefined
20786
+ ? options.payload
20787
+ : (options.event && typeof options.event === 'object'
20788
+ ? options.event['payload']
20789
+ : undefined);
20790
+ const envelopes = [
20791
+ {
20792
+ source: options.sourceValue,
20793
+ value: options.transformedValue,
20794
+ payload,
20795
+ event: options.event,
20796
+ context: options.context || {},
20797
+ original: {
20798
+ source: options.sourceValue,
20799
+ payload,
20800
+ event: options.event,
20801
+ },
20802
+ },
20803
+ {
20804
+ source: options.sourceValue,
20805
+ value: options.transformedValue,
20806
+ payload: options.transformedValue,
20807
+ event: options.event,
20808
+ context: options.context || {},
20809
+ },
20810
+ ];
20811
+ for (const envelope of envelopes) {
20812
+ const extracted = this.pathAccessor.extractByPath(envelope, normalized);
20813
+ if (extracted !== undefined) {
20814
+ return extracted;
20815
+ }
20816
+ }
20817
+ return options.transformedValue;
20818
+ }
19392
20819
  readStatePath(snapshot, path, preferredLayer) {
19393
20820
  const lookup = (layer) => {
19394
20821
  switch (layer) {
@@ -19556,8 +20983,9 @@ class CompositionRuntimeEngine {
19556
20983
  this.definition = definition;
19557
20984
  const generatedAt = this.now();
19558
20985
  const stateSnapshot = this.materializeDerivedState(definition.state.primaryValues, definition);
20986
+ const bootstrapDiagnostics = uniqueDiagnosticsById(stateSnapshot.diagnostics);
19559
20987
  const init = {
19560
- status: stateSnapshot.diagnostics.some(isBlockingDiagnostic) ? 'degraded' : 'ready',
20988
+ status: bootstrapDiagnostics.some(isBlockingDiagnostic) ? 'degraded' : 'ready',
19561
20989
  generatedAt,
19562
20990
  links: definition.links.map((link) => this.createLinkSnapshot(link)),
19563
20991
  state: {
@@ -19565,9 +20993,9 @@ class CompositionRuntimeEngine {
19565
20993
  derivedValues: stateSnapshot.derivedValues,
19566
20994
  transientValues: stateSnapshot.transientValues,
19567
20995
  changedPaths: [],
19568
- diagnostics: stateSnapshot.diagnostics,
20996
+ diagnostics: bootstrapDiagnostics,
19569
20997
  },
19570
- diagnostics: stateSnapshot.diagnostics,
20998
+ diagnostics: bootstrapDiagnostics,
19571
20999
  traceTail: [
19572
21000
  this.traceService.createEntry('bootstrap', 0, {
19573
21001
  timestamp: generatedAt,
@@ -19581,9 +21009,9 @@ class CompositionRuntimeEngine {
19581
21009
  linkCount: definition.links.length,
19582
21010
  derivedDefinitionCount: Object.keys(definition.state.derivedDefinitions || {}).length,
19583
21011
  },
19584
- diagnostics: stateSnapshot.diagnostics.map((item) => item.code),
21012
+ diagnostics: bootstrapDiagnostics.map((item) => item.code),
19585
21013
  }),
19586
- ...stateSnapshot.diagnostics.map((diagnostic, index) => this.traceService.createEntry('bootstrap', index + 1, {
21014
+ ...bootstrapDiagnostics.map((diagnostic, index) => this.traceService.createEntry('bootstrap', index + 1, {
19587
21015
  timestamp: diagnostic.createdAt,
19588
21016
  phase: 'diagnostic-emitted',
19589
21017
  subject: diagnostic.subject,
@@ -19610,6 +21038,7 @@ class CompositionRuntimeEngine {
19610
21038
  const runtimeDiagnostics = [];
19611
21039
  const linkResults = new Map();
19612
21040
  const trace = [];
21041
+ const emittedDiagnosticIds = new Set();
19613
21042
  let sequence = 0;
19614
21043
  trace.push(this.traceService.createEntry(event.eventId, sequence++, {
19615
21044
  timestamp: occurredAt,
@@ -19719,53 +21148,28 @@ class CompositionRuntimeEngine {
19719
21148
  },
19720
21149
  diagnostics: result.diagnostics.map((item) => item.code),
19721
21150
  }));
19722
- for (const diagnostic of result.diagnostics) {
19723
- trace.push(this.traceService.createEntry(event.eventId, sequence++, {
19724
- timestamp: diagnostic.createdAt,
19725
- phase: 'diagnostic-emitted',
19726
- linkId: link.id,
19727
- subject: diagnostic.subject,
19728
- details: {
19729
- code: diagnostic.code,
19730
- severity: diagnostic.severity,
19731
- phase: diagnostic.phase,
19732
- },
19733
- diagnostics: [diagnostic.code],
19734
- }));
19735
- }
21151
+ sequence = this.appendDiagnosticTraceEntries(trace, emittedDiagnosticIds, event.eventId, sequence, result.diagnostics, link.id);
19736
21152
  }
19737
21153
  const materializedState = this.materializeDerivedState(workingState.primaryValues, this.definition, workingState.transientValues);
19738
- const diagnostics = [
21154
+ const diagnostics = uniqueDiagnosticsById([
19739
21155
  ...runtimeDiagnostics,
19740
21156
  ...materializedState.diagnostics,
19741
- ];
21157
+ ]);
19742
21158
  materializedState.changedPaths = Array.from(new Set([
19743
21159
  ...(workingState.changedPaths ?? []),
19744
21160
  ...this.stateRuntime.collectChangedPaths(this.store.getSnapshot().state.derivedValues, materializedState.derivedValues, Object.keys(this.definition.state.derivedDefinitions || {})),
19745
21161
  ]));
19746
21162
  materializedState.diagnostics = cloneDiagnostics(diagnostics);
19747
- for (const diagnostic of materializedState.diagnostics.filter((item) => !runtimeDiagnostics.some((runtimeItem) => runtimeItem.id === item.id))) {
19748
- trace.push(this.traceService.createEntry(event.eventId, sequence++, {
19749
- timestamp: diagnostic.createdAt,
19750
- phase: 'diagnostic-emitted',
19751
- subject: diagnostic.subject,
19752
- details: {
19753
- code: diagnostic.code,
19754
- severity: diagnostic.severity,
19755
- phase: diagnostic.phase,
19756
- },
19757
- diagnostics: [diagnostic.code],
19758
- }));
19759
- }
21163
+ sequence = this.appendDiagnosticTraceEntries(trace, emittedDiagnosticIds, event.eventId, sequence, materializedState.diagnostics.filter((item) => !runtimeDiagnostics.some((runtimeItem) => runtimeItem.id === item.id)));
19760
21164
  const snapshot = this.store.applyCycle({
19761
21165
  generatedAt: occurredAt,
19762
21166
  status: diagnostics.some(isBlockingDiagnostic)
19763
21167
  ? 'degraded'
19764
21168
  : 'ready',
19765
21169
  stateSnapshot: materializedState,
19766
- linkPatches: this.definition.links.map((link) => ({
21170
+ linkPatches: matchedLinks.map((link) => ({
19767
21171
  linkId: link.id,
19768
- status: linkResults.get(link.id)?.status ?? 'idle',
21172
+ status: linkResults.get(link.id)?.status,
19769
21173
  lastEventAt: linkResults.has(link.id) ? occurredAt : undefined,
19770
21174
  lastDispatchTraceId: linkResults.has(link.id) ? event.eventId : undefined,
19771
21175
  lastDeliveredValuePreview: linkResults.get(link.id)?.value,
@@ -19802,7 +21206,6 @@ class CompositionRuntimeEngine {
19802
21206
  if (link.from.kind !== 'state' || link.to.kind !== 'component-port') {
19803
21207
  continue;
19804
21208
  }
19805
- matchedLinkIds.push(link.id);
19806
21209
  const result = this.linkExecutor.executeLink(link, {
19807
21210
  snapshot: { state: runtimeState },
19808
21211
  context: definition.context,
@@ -19816,16 +21219,17 @@ class CompositionRuntimeEngine {
19816
21219
  if (widgetIndex < 0) {
19817
21220
  continue;
19818
21221
  }
19819
- const bindingPath = result.delivery.bindingPath || result.delivery.portId;
19820
- if (!bindingPath) {
21222
+ const previewBindingPath = resolvePreviewBindingPath(result.delivery.portId, result.delivery.bindingPath);
21223
+ if (!previewBindingPath) {
19821
21224
  continue;
19822
21225
  }
21226
+ matchedLinkIds.push(link.id);
19823
21227
  const currentInputs = widgets[widgetIndex].definition.inputs || {};
19824
21228
  widgets[widgetIndex] = {
19825
21229
  ...widgets[widgetIndex],
19826
21230
  definition: {
19827
21231
  ...widgets[widgetIndex].definition,
19828
- inputs: this.pathAccessor.setValueAtPath(this.cloneJson(currentInputs), bindingPath, this.cloneJson(result.value)),
21232
+ inputs: this.pathAccessor.setValueAtPath(this.cloneJson(currentInputs), previewBindingPath, this.cloneJson(result.value)),
19829
21233
  },
19830
21234
  };
19831
21235
  }
@@ -19853,6 +21257,9 @@ class CompositionRuntimeEngine {
19853
21257
  const linkResults = new Map();
19854
21258
  const diagnostics = [...snapshot.diagnostics];
19855
21259
  const trace = [...snapshot.traceTail];
21260
+ const emittedDiagnosticIds = new Set(trace
21261
+ .filter((entry) => entry.phase === 'diagnostic-emitted')
21262
+ .flatMap((entry) => entry.diagnostics || []));
19856
21263
  let sequence = trace.length;
19857
21264
  for (const link of hydrationLinks) {
19858
21265
  const stateBefore = cloneStateSnapshot(workingState);
@@ -19916,24 +21323,12 @@ class CompositionRuntimeEngine {
19916
21323
  },
19917
21324
  diagnostics: result.diagnostics.map((item) => item.code),
19918
21325
  }));
19919
- for (const diagnostic of result.diagnostics) {
19920
- trace.push(this.traceService.createEntry('bootstrap', sequence++, {
19921
- timestamp: diagnostic.createdAt,
19922
- phase: 'diagnostic-emitted',
19923
- linkId: link.id,
19924
- subject: diagnostic.subject,
19925
- details: {
19926
- code: diagnostic.code,
19927
- severity: diagnostic.severity,
19928
- phase: diagnostic.phase,
19929
- },
19930
- diagnostics: [diagnostic.code],
19931
- }));
19932
- }
21326
+ sequence = this.appendDiagnosticTraceEntries(trace, emittedDiagnosticIds, 'bootstrap', sequence, result.diagnostics, link.id);
19933
21327
  }
21328
+ const dedupedDiagnostics = uniqueDiagnosticsById(diagnostics);
19934
21329
  return this.store.applyCycle({
19935
21330
  generatedAt: occurredAt,
19936
- status: diagnostics.some(isBlockingDiagnostic) ? 'degraded' : 'ready',
21331
+ status: dedupedDiagnostics.some(isBlockingDiagnostic) ? 'degraded' : 'ready',
19937
21332
  stateSnapshot: {
19938
21333
  ...workingState,
19939
21334
  changedPaths: [],
@@ -19947,7 +21342,7 @@ class CompositionRuntimeEngine {
19947
21342
  lastDeliveredValuePreview: linkResults.get(link.id)?.value,
19948
21343
  diagnostics: linkResults.get(link.id)?.diagnostics,
19949
21344
  })),
19950
- diagnostics,
21345
+ diagnostics: dedupedDiagnostics,
19951
21346
  traceTail: trace,
19952
21347
  });
19953
21348
  }
@@ -19966,26 +21361,30 @@ class CompositionRuntimeEngine {
19966
21361
  };
19967
21362
  }
19968
21363
  createDerivedDiagnostic(definition, message, index) {
21364
+ const nodeKey = this.extractDerivedNodeKey(message);
21365
+ const classification = classifyDerivedDiagnosticMessage(message);
19969
21366
  return {
19970
21367
  id: `derived:${index}:${message}`,
19971
- code: message.includes('cycle')
19972
- ? 'RUNTIME_DERIVED_STATE_CYCLE'
19973
- : message.includes('transformer')
19974
- ? 'RUNTIME_DERIVED_TRANSFORMER_UNSUPPORTED'
19975
- : 'RUNTIME_DERIVED_STATE_WARNING',
19976
- severity: message.includes('cycle') || message.includes('not implemented')
19977
- ? 'warning'
19978
- : 'info',
21368
+ code: classification.code,
21369
+ severity: classification.severity,
19979
21370
  phase: 'runtime-state',
19980
21371
  message,
19981
21372
  summary: message,
19982
- subject: {
19983
- kind: 'runtime-snapshot',
19984
- label: definition.widgetOrder.join(', '),
19985
- },
21373
+ subject: nodeKey
21374
+ ? {
21375
+ kind: 'derived-state',
21376
+ statePath: nodeKey,
21377
+ path: nodeKey,
21378
+ label: nodeKey,
21379
+ }
21380
+ : {
21381
+ kind: 'runtime-snapshot',
21382
+ label: definition.widgetOrder.join(', '),
21383
+ },
19986
21384
  source: 'runtime-engine',
19987
- blocking: false,
21385
+ blocking: classification.severity === 'error' || classification.severity === 'fatal',
19988
21386
  createdAt: this.now(),
21387
+ details: nodeKey ? { nodeKey } : undefined,
19989
21388
  };
19990
21389
  }
19991
21390
  clonePreviewWidgets(widgets) {
@@ -19997,6 +21396,31 @@ class CompositionRuntimeEngine {
19997
21396
  }
19998
21397
  return JSON.parse(JSON.stringify(value));
19999
21398
  }
21399
+ extractDerivedNodeKey(message) {
21400
+ const match = message.match(/Derived state '([^']+)'/);
21401
+ return match?.[1];
21402
+ }
21403
+ appendDiagnosticTraceEntries(trace, emittedDiagnosticIds, traceId, sequence, diagnostics, linkId) {
21404
+ for (const diagnostic of diagnostics) {
21405
+ if (emittedDiagnosticIds.has(diagnostic.id)) {
21406
+ continue;
21407
+ }
21408
+ emittedDiagnosticIds.add(diagnostic.id);
21409
+ trace.push(this.traceService.createEntry(traceId, sequence++, {
21410
+ timestamp: diagnostic.createdAt,
21411
+ phase: 'diagnostic-emitted',
21412
+ linkId,
21413
+ subject: diagnostic.subject,
21414
+ details: {
21415
+ code: diagnostic.code,
21416
+ severity: diagnostic.severity,
21417
+ phase: diagnostic.phase,
21418
+ },
21419
+ diagnostics: [diagnostic.code],
21420
+ }));
21421
+ }
21422
+ return sequence;
21423
+ }
20000
21424
  }
20001
21425
  function createDefaultLinkExecutor() {
20002
21426
  const connections = new ConnectionManagerService();
@@ -20004,7 +21428,7 @@ function createDefaultLinkExecutor() {
20004
21428
  return new LinkExecutorService(transforms, connections);
20005
21429
  }
20006
21430
  function createDefaultStateRuntime() {
20007
- return new WidgetPageStateRuntimeService(new ConnectionManagerService());
21431
+ return new WidgetPageStateRuntimeService();
20008
21432
  }
20009
21433
  function cloneStateSnapshot(snapshot) {
20010
21434
  return {
@@ -20026,9 +21450,92 @@ function cloneDiagnostics(diagnostics) {
20026
21450
  details: item.details ? { ...item.details } : undefined,
20027
21451
  }));
20028
21452
  }
21453
+ function uniqueDiagnosticsById(diagnostics) {
21454
+ const deduped = [];
21455
+ const seen = new Set();
21456
+ for (const diagnostic of diagnostics) {
21457
+ if (seen.has(diagnostic.id)) {
21458
+ continue;
21459
+ }
21460
+ seen.add(diagnostic.id);
21461
+ deduped.push(diagnostic);
21462
+ }
21463
+ return deduped;
21464
+ }
20029
21465
  function isBlockingDiagnostic(item) {
20030
21466
  return item.severity === 'error' || item.severity === 'fatal';
20031
21467
  }
21468
+ function resolvePreviewBindingPath(portId, bindingPath) {
21469
+ const normalizedPortId = String(portId || '').trim();
21470
+ const normalizedBindingPath = String(bindingPath || '').trim();
21471
+ if (!normalizedPortId && !normalizedBindingPath) {
21472
+ return undefined;
21473
+ }
21474
+ if (!normalizedBindingPath) {
21475
+ return normalizedPortId || undefined;
21476
+ }
21477
+ if (!normalizedPortId) {
21478
+ return normalizedBindingPath;
21479
+ }
21480
+ if (normalizedBindingPath === normalizedPortId
21481
+ || normalizedBindingPath.startsWith(`${normalizedPortId}.`)
21482
+ || normalizedBindingPath.startsWith(`${normalizedPortId}[`)) {
21483
+ return normalizedBindingPath;
21484
+ }
21485
+ if (normalizedBindingPath.startsWith('.')
21486
+ || normalizedBindingPath.startsWith('[')) {
21487
+ return `${normalizedPortId}${normalizedBindingPath}`;
21488
+ }
21489
+ return `${normalizedPortId}.${normalizedBindingPath}`;
21490
+ }
21491
+ function classifyDerivedDiagnosticMessage(message) {
21492
+ if (message.includes('cycle')) {
21493
+ return {
21494
+ code: 'RUNTIME_DERIVED_STATE_CYCLE',
21495
+ severity: 'warning',
21496
+ };
21497
+ }
21498
+ if (message.includes('transformer')) {
21499
+ return {
21500
+ code: 'RUNTIME_DERIVED_TRANSFORMER_UNSUPPORTED',
21501
+ severity: 'warning',
21502
+ };
21503
+ }
21504
+ if (message.includes('missing compute definition')) {
21505
+ return {
21506
+ code: 'RUNTIME_DERIVED_COMPUTE_MISSING',
21507
+ severity: 'warning',
21508
+ };
21509
+ }
21510
+ if (message.includes('unsupported compute kind')) {
21511
+ return {
21512
+ code: 'RUNTIME_DERIVED_COMPUTE_KIND_UNSUPPORTED',
21513
+ severity: 'warning',
21514
+ };
21515
+ }
21516
+ if (message.includes('without a valid operator')) {
21517
+ return {
21518
+ code: 'RUNTIME_DERIVED_OPERATOR_MISSING',
21519
+ severity: 'warning',
21520
+ };
21521
+ }
21522
+ if (message.includes('uses unsupported operator')) {
21523
+ return {
21524
+ code: 'RUNTIME_DERIVED_OPERATOR_UNSUPPORTED',
21525
+ severity: 'warning',
21526
+ };
21527
+ }
21528
+ if (message.includes(' failed: ')) {
21529
+ return {
21530
+ code: 'RUNTIME_DERIVED_STATE_FAILED',
21531
+ severity: 'error',
21532
+ };
21533
+ }
21534
+ return {
21535
+ code: 'RUNTIME_DERIVED_STATE_WARNING',
21536
+ severity: 'info',
21537
+ };
21538
+ }
20032
21539
  function matchesEndpoint(expected, actual) {
20033
21540
  if (expected.kind !== actual.kind) {
20034
21541
  return false;
@@ -20154,7 +21661,7 @@ class WidgetPageCompositionFactory {
20154
21661
  return {
20155
21662
  widgetOrder: page.widgets.map((widget) => widget.key),
20156
21663
  widgetsByKey,
20157
- links: adaptWidgetConnectionsToCompositionLinks(page.connections),
21664
+ links: this.readCanonicalLinks(page),
20158
21665
  state: {
20159
21666
  primaryValues: this.materializePrimaryValues(normalizedState),
20160
21667
  schema: this.clone(normalizedState.schema) || {},
@@ -20163,6 +21670,9 @@ class WidgetPageCompositionFactory {
20163
21670
  context: this.clone(page.context) || {},
20164
21671
  };
20165
21672
  }
21673
+ readCanonicalLinks(page) {
21674
+ return this.clone(page.composition?.links) || [];
21675
+ }
20166
21676
  indexWidgets(widgets) {
20167
21677
  const widgetsByKey = {};
20168
21678
  for (const widget of widgets) {
@@ -20252,11 +21762,6 @@ const CANVAS_RESIZE_HANDLES = [
20252
21762
  { id: 'south-west', className: 'pdx-canvas-resize--south-west' },
20253
21763
  ];
20254
21764
  class DynamicWidgetPageComponent {
20255
- conn;
20256
- stateRuntime;
20257
- settingsPanel;
20258
- defaultShellEditor;
20259
- defaultPageEditor;
20260
21765
  pageCanvasHost;
20261
21766
  page;
20262
21767
  context = null;
@@ -20310,6 +21815,7 @@ class DynamicWidgetPageComponent {
20310
21815
  isHydrating = false;
20311
21816
  persistenceReady = false;
20312
21817
  warnedMissingKey = false;
21818
+ runtimeEventSequence = 0;
20313
21819
  compositionFactory = new WidgetPageCompositionFactory();
20314
21820
  compositionRuntime = new CompositionRuntimeFacade();
20315
21821
  compositionDefinition;
@@ -20323,13 +21829,12 @@ class DynamicWidgetPageComponent {
20323
21829
  catch {
20324
21830
  return undefined;
20325
21831
  } })();
20326
- constructor(conn, stateRuntime, settingsPanel, defaultShellEditor, defaultPageEditor) {
20327
- this.conn = conn;
20328
- this.stateRuntime = stateRuntime;
20329
- this.settingsPanel = settingsPanel;
20330
- this.defaultShellEditor = defaultShellEditor;
20331
- this.defaultPageEditor = defaultPageEditor;
20332
- }
21832
+ conn = inject(ConnectionManagerService);
21833
+ stateRuntime = inject(WidgetPageStateRuntimeService);
21834
+ settingsPanel = inject(SETTINGS_PANEL_BRIDGE, { optional: true });
21835
+ defaultShellEditor = inject(DYNAMIC_PAGE_SHELL_EDITOR, { optional: true });
21836
+ defaultPageEditor = inject(DYNAMIC_PAGE_CONFIG_EDITOR, { optional: true });
21837
+ constructor() { }
20333
21838
  ngOnDestroy() {
20334
21839
  this.compositionRuntime.destroy();
20335
21840
  }
@@ -20337,6 +21842,7 @@ class DynamicWidgetPageComponent {
20337
21842
  if (changes['page'] || changes['context'] || changes['enableCustomization']) {
20338
21843
  const parsed = this.parsePage(this.page);
20339
21844
  const resolvedPage = parsed ? this.resolvePagePresets(parsed) : parsed;
21845
+ this.assertNoLegacyConnections(resolvedPage);
20340
21846
  this.widgetDiagnostics = {};
20341
21847
  this.widgetDiagnosticsChange.emit({});
20342
21848
  this.pageDefinition = resolvedPage ? { ...resolvedPage, state: this.stateRuntime.normalizeState(resolvedPage.state) } : resolvedPage;
@@ -20374,7 +21880,7 @@ class DynamicWidgetPageComponent {
20374
21880
  if (!cycle || !cycle.matchedLinkIds.length)
20375
21881
  return;
20376
21882
  const state = this.stateFromCompositionSnapshot(page.state, cycle.snapshot.state.primaryValues);
20377
- const runtimeStatePaths = Array.from(new Set(cycle.snapshot.state.changedPaths || []));
21883
+ const runtimeStatePaths = Array.from(new Set((cycle.snapshot.state.changedPaths || []).filter((path) => typeof path === 'string')));
20378
21884
  const updatedPrimaryStatePaths = runtimeStatePaths.filter((path) => (!this.areStateValuesEqual(this.conn.extractByPath(this.stateRuntime.normalizeState(page.state).values || {}, path), this.conn.extractByPath(state.values || {}, path))));
20379
21885
  let widgets = this.cloneWidgets(page.widgets || []);
20380
21886
  const directDelivery = this.applyCompositionWidgetDeliveries(widgets, cycle);
@@ -20392,6 +21898,220 @@ class DynamicWidgetPageComponent {
20392
21898
  }
20393
21899
  this.applyPageUpdate({ ...page, widgets, state }, true, nextRuntime, false);
20394
21900
  }
21901
+ buildStateRuntime(state, pageContext) {
21902
+ return this.stateRuntime.buildRuntimeSnapshot(state, this.buildStateContext(pageContext));
21903
+ }
21904
+ bootstrapCompositionAdapter(page) {
21905
+ if (!page) {
21906
+ this.compositionDefinition = undefined;
21907
+ return undefined;
21908
+ }
21909
+ this.compositionDefinition = this.compositionFactory.create(page);
21910
+ return this.compositionRuntime.bootstrap(this.compositionDefinition);
21911
+ }
21912
+ applyEditShellActions(widgets) {
21913
+ return this.cloneWidgets(widgets).map((widget) => {
21914
+ if (!this.shouldInjectWidgetSettings(widget)) {
21915
+ return widget;
21916
+ }
21917
+ const existingActions = widget.shell?.actions || [];
21918
+ if (existingActions.some((action) => action.id === 'widget-settings')) {
21919
+ return widget;
21920
+ }
21921
+ const widgetSettingsAction = {
21922
+ id: 'widget-settings',
21923
+ icon: 'settings',
21924
+ variant: 'icon',
21925
+ placement: 'header',
21926
+ label: this.t('controls.widgetSettings', 'Configurar widget'),
21927
+ tooltip: this.t('controls.widgetSettingsTooltip', 'Abrir configurações do widget'),
21928
+ };
21929
+ return {
21930
+ ...widget,
21931
+ shell: {
21932
+ ...(widget.shell || {}),
21933
+ actions: [...existingActions, widgetSettingsAction],
21934
+ },
21935
+ };
21936
+ });
21937
+ }
21938
+ applyBootstrapCompositionHydration(widgets, bootstrapSnapshot) {
21939
+ if (!this.compositionDefinition || !bootstrapSnapshot) {
21940
+ return this.cloneWidgets(widgets);
21941
+ }
21942
+ return this.compositionRuntime.projectStateWidgetInputs(this.compositionDefinition, {
21943
+ widgets,
21944
+ state: bootstrapSnapshot.state,
21945
+ now: bootstrapSnapshot.generatedAt,
21946
+ }).widgets;
21947
+ }
21948
+ reportStateDiagnostics(diagnostics) {
21949
+ if (!diagnostics?.length)
21950
+ return;
21951
+ console.warn('[DynamicWidgetPage] State runtime diagnostics', diagnostics);
21952
+ }
21953
+ dispatchWidgetEventToComposition(page, fromKey, evt) {
21954
+ const output = String(evt?.output || '').trim();
21955
+ if (!output)
21956
+ return undefined;
21957
+ const definition = this.compositionDefinition ?? this.compositionFactory.create(page);
21958
+ this.compositionDefinition = definition;
21959
+ if (!definition.links.some((link) => (link.from.kind === 'component-port'
21960
+ && link.from.ref.widget === fromKey
21961
+ && link.from.ref.port === output
21962
+ && link.from.ref.direction === 'output'))) {
21963
+ return undefined;
21964
+ }
21965
+ let payload;
21966
+ try {
21967
+ payload = this.cloneStateValues(evt?.payload);
21968
+ }
21969
+ catch (error) {
21970
+ const message = `DynamicWidgetPageComponent could not serialize widget event payload for "${fromKey}.${output}". ` +
21971
+ `Composition links only support JSON-safe payloads.`;
21972
+ const wrapped = new Error(message);
21973
+ wrapped.cause = error;
21974
+ throw wrapped;
21975
+ }
21976
+ return this.compositionRuntime.dispatch({
21977
+ eventId: this.buildRuntimeEventId(fromKey, output),
21978
+ source: {
21979
+ kind: 'component-port',
21980
+ ref: {
21981
+ widget: fromKey,
21982
+ port: output,
21983
+ direction: 'output',
21984
+ componentType: evt.sourceComponentId,
21985
+ },
21986
+ },
21987
+ payload,
21988
+ });
21989
+ }
21990
+ stateFromCompositionSnapshot(previousState, primaryValues) {
21991
+ const normalized = this.stateRuntime.normalizeState(previousState);
21992
+ return {
21993
+ ...normalized,
21994
+ values: this.cloneStateValues(primaryValues) || {},
21995
+ };
21996
+ }
21997
+ applyCompositionWidgetDeliveries(widgets, cycle) {
21998
+ if (!this.compositionDefinition) {
21999
+ return {
22000
+ widgets: this.cloneWidgets(widgets),
22001
+ changed: false,
22002
+ };
22003
+ }
22004
+ const linksById = new Map(this.compositionDefinition.links.map((link) => [link.id, link]));
22005
+ const runtimeLinks = cycle.snapshot.links.filter((link) => (link.lastDispatchTraceId === cycle.cycleId && link.status === 'delivered'));
22006
+ let nextWidgets = this.cloneWidgets(widgets);
22007
+ let changed = false;
22008
+ for (const runtimeLink of runtimeLinks) {
22009
+ const link = linksById.get(runtimeLink.linkId);
22010
+ if (!link || link.from.kind === 'state' || link.to.kind !== 'component-port') {
22011
+ continue;
22012
+ }
22013
+ const targetRef = link.to.ref;
22014
+ const bindingPath = this.resolveComponentBindingPath(targetRef.port, targetRef.bindingPath);
22015
+ if (!bindingPath) {
22016
+ continue;
22017
+ }
22018
+ const widgetIndex = nextWidgets.findIndex((widget) => widget.key === targetRef.widget);
22019
+ if (widgetIndex < 0) {
22020
+ continue;
22021
+ }
22022
+ const widget = nextWidgets[widgetIndex];
22023
+ const currentInputs = this.cloneStateValues(widget.definition.inputs || {});
22024
+ const nextInputs = this.conn.setValueAtPath(currentInputs, bindingPath, this.cloneStateValues(runtimeLink.lastDeliveredValuePreview));
22025
+ if (this.areStateValuesEqual(currentInputs, nextInputs)) {
22026
+ continue;
22027
+ }
22028
+ nextWidgets[widgetIndex] = {
22029
+ ...widget,
22030
+ definition: {
22031
+ ...widget.definition,
22032
+ inputs: nextInputs,
22033
+ },
22034
+ };
22035
+ changed = true;
22036
+ }
22037
+ return { widgets: nextWidgets, changed };
22038
+ }
22039
+ buildStateContext(pageContext) {
22040
+ return {
22041
+ ...(this.cloneStateValues(pageContext) || {}),
22042
+ ...(this.cloneStateValues(this.context) || {}),
22043
+ };
22044
+ }
22045
+ cloneStateValues(value) {
22046
+ if (value == null || typeof value !== 'object') {
22047
+ return value;
22048
+ }
22049
+ return JSON.parse(JSON.stringify(value));
22050
+ }
22051
+ cloneGrouping(grouping) {
22052
+ return this.cloneStateValues(grouping);
22053
+ }
22054
+ resolveShellTemplates(widgets, runtime) {
22055
+ const templateContext = {
22056
+ context: this.buildStateContext(this.pageDefinition?.context),
22057
+ pageContext: this.buildStateContext(this.pageDefinition?.context),
22058
+ state: this.cloneStateValues(runtime.effectiveValues),
22059
+ values: this.cloneStateValues(runtime.primaryValues),
22060
+ derived: this.cloneStateValues(runtime.derivedValues),
22061
+ pageState: this.cloneStateValues(runtime.primaryValues),
22062
+ pageStateDerived: this.cloneStateValues(runtime.derivedValues),
22063
+ pageStateEffective: this.cloneStateValues(runtime.effectiveValues),
22064
+ };
22065
+ return this.cloneWidgets(widgets).map((widget) => {
22066
+ if (!widget.shell) {
22067
+ return widget;
22068
+ }
22069
+ return {
22070
+ ...widget,
22071
+ shell: this.resolveTemplate(widget.shell, templateContext),
22072
+ };
22073
+ });
22074
+ }
22075
+ resolveComponentBindingPath(portId, bindingPath) {
22076
+ const normalizedPortId = String(portId || '').trim();
22077
+ const normalizedBindingPath = String(bindingPath || '').trim();
22078
+ if (!normalizedPortId && !normalizedBindingPath) {
22079
+ return undefined;
22080
+ }
22081
+ if (!normalizedBindingPath) {
22082
+ return normalizedPortId || undefined;
22083
+ }
22084
+ if (!normalizedPortId) {
22085
+ return normalizedBindingPath;
22086
+ }
22087
+ if (normalizedBindingPath === normalizedPortId
22088
+ || normalizedBindingPath.startsWith(`${normalizedPortId}.`)
22089
+ || normalizedBindingPath.startsWith(`${normalizedPortId}[`)) {
22090
+ return normalizedBindingPath;
22091
+ }
22092
+ if (normalizedBindingPath.startsWith('.')
22093
+ || normalizedBindingPath.startsWith('[')) {
22094
+ return `${normalizedPortId}${normalizedBindingPath}`;
22095
+ }
22096
+ return `${normalizedPortId}.${normalizedBindingPath}`;
22097
+ }
22098
+ buildRuntimeEventId(fromKey, output) {
22099
+ const sequence = ++this.runtimeEventSequence;
22100
+ const timestamp = Date.now();
22101
+ if (typeof globalThis !== 'undefined' && globalThis.crypto?.randomUUID) {
22102
+ return `widget:${fromKey}:${output}:${sequence}:${globalThis.crypto.randomUUID()}`;
22103
+ }
22104
+ return `widget:${fromKey}:${output}:${timestamp}:${sequence}`;
22105
+ }
22106
+ shouldInjectWidgetSettings(widget) {
22107
+ if (!this.enableCustomization || !this.settingsPanel) {
22108
+ return false;
22109
+ }
22110
+ if (!(this.shellEditorComponent || this.defaultShellEditor)) {
22111
+ return false;
22112
+ }
22113
+ return !!widget.shell && widget.shell.kind !== 'none';
22114
+ }
20395
22115
  areStateValuesEqual(left, right) {
20396
22116
  if (left === right)
20397
22117
  return true;
@@ -20651,6 +22371,7 @@ class DynamicWidgetPageComponent {
20651
22371
  }, persist);
20652
22372
  }
20653
22373
  applyPageUpdate(next, persist = true, runtime = this.buildStateRuntime(next.state, next.context), syncCompositionAdapter = true) {
22374
+ this.assertNoLegacyConnections(next);
20654
22375
  const normalized = { ...next, state: runtime.state };
20655
22376
  const normalizedWidgets = this.applyEditShellActions(normalized.widgets || []);
20656
22377
  const bootstrapSnapshot = syncCompositionAdapter
@@ -20828,12 +22549,15 @@ class DynamicWidgetPageComponent {
20828
22549
  return undefined;
20829
22550
  if (typeof input === 'string') {
20830
22551
  try {
20831
- return JSON.parse(input);
22552
+ const parsed = JSON.parse(input);
22553
+ this.assertNoLegacyConnections(parsed);
22554
+ return parsed;
20832
22555
  }
20833
22556
  catch {
20834
22557
  return undefined;
20835
22558
  }
20836
22559
  }
22560
+ this.assertNoLegacyConnections(input);
20837
22561
  return input;
20838
22562
  }
20839
22563
  resolvePagePresets(page) {
@@ -21518,9 +23242,9 @@ class DynamicWidgetPageComponent {
21518
23242
  if (!grouping?.length)
21519
23243
  return grouping;
21520
23244
  if (!overrides?.length)
21521
- return this.cloneGrouping(grouping);
23245
+ return this.cloneGrouping(grouping) || [];
21522
23246
  const overridesById = new Map(overrides.map((item) => [item.id, item]));
21523
- return this.cloneGrouping(grouping)
23247
+ return (this.cloneGrouping(grouping) || [])
21524
23248
  .map((group) => {
21525
23249
  const override = overridesById.get(group.id);
21526
23250
  if (!override || override.hidden)
@@ -21658,35 +23382,16 @@ class DynamicWidgetPageComponent {
21658
23382
  resolveRenderedCanvasItem(widget) {
21659
23383
  return this.transientCanvasItemsState()[widget.key] || widget.renderCanvasItem;
21660
23384
  }
21661
- applyEditShellActions(widgets) {
21662
- if (!this.enableCustomization)
21663
- return widgets;
21664
- return widgets.map((w) => {
21665
- if (!w.shell || w.shell.kind === 'none')
21666
- return w;
21667
- const shell = { ...w.shell };
21668
- const actions = [...(shell.actions || [])];
21669
- if (!actions.some((a) => a.id === 'widget-settings')) {
21670
- actions.push({
21671
- id: 'widget-settings',
21672
- icon: 'settings',
21673
- label: 'Configurar Card',
21674
- placement: 'window',
21675
- variant: 'icon',
21676
- tooltip: 'Configurar Card',
21677
- });
21678
- }
21679
- if (shell.showHeader === false)
21680
- shell.showHeader = true;
21681
- return { ...w, shell: { ...shell, actions } };
21682
- });
23385
+ toPersistedCanonicalPage(page) {
23386
+ this.assertNoLegacyConnections(page);
23387
+ return writeCompositionLinksToPersistedPage(page, page.composition?.links || []);
21683
23388
  }
21684
23389
  savePage(page) {
21685
23390
  const key = this.storageKey();
21686
23391
  if (!key)
21687
23392
  return;
21688
23393
  try {
21689
- this.storage.saveConfig(key, page).pipe(take(1)).subscribe({ error: () => { } });
23394
+ this.storage.saveConfig(key, this.toPersistedCanonicalPage(page)).pipe(take(1)).subscribe({ error: () => { } });
21690
23395
  }
21691
23396
  catch { }
21692
23397
  }
@@ -21761,135 +23466,15 @@ class DynamicWidgetPageComponent {
21761
23466
  return '';
21762
23467
  return String(value).trim().replace(/[\r\n\t|:]/g, '-');
21763
23468
  }
21764
- cloneStateValues(state) {
21765
- return state ? JSON.parse(JSON.stringify(state)) : {};
21766
- }
21767
- cloneValue(value) {
21768
- if (value == null || typeof value !== 'object') {
21769
- return value;
21770
- }
21771
- return JSON.parse(JSON.stringify(value));
21772
- }
21773
- cloneGrouping(grouping) {
21774
- return grouping ? JSON.parse(JSON.stringify(grouping)) : [];
21775
- }
21776
- buildStateRuntime(state, pageContext) {
21777
- return this.stateRuntime.buildRuntimeSnapshot(state, this.buildStateContext(pageContext));
21778
- }
21779
- buildStateContext(pageContext) {
21780
- return {
21781
- ...(pageContext || {}),
21782
- ...(this.context || {}),
21783
- };
21784
- }
21785
- resolveShellTemplates(widgets, runtime) {
21786
- const context = {
21787
- ...(this.mergedContext || {}),
21788
- state: this.cloneStateValues(runtime.effectiveValues),
21789
- pageState: this.cloneStateValues(runtime.primaryValues),
21790
- pageStateDerived: this.cloneStateValues(runtime.derivedValues),
21791
- pageStateEffective: this.cloneStateValues(runtime.effectiveValues),
21792
- };
21793
- return widgets.map((widget) => (widget.shell
21794
- ? { ...widget, shell: this.resolveTemplate(widget.shell, context) }
21795
- : widget));
21796
- }
21797
- reportStateDiagnostics(diagnostics) {
21798
- for (const diagnostic of diagnostics || []) {
21799
- console.warn(`[DynamicWidgetPage] ${diagnostic}`);
21800
- }
21801
- }
21802
- applyBootstrapCompositionHydration(widgets, snapshot) {
21803
- const matchedLinkIds = (snapshot?.links || [])
21804
- .filter((link) => link.status === 'delivered')
21805
- .map((link) => link.linkId);
21806
- if (!snapshot || !matchedLinkIds.length) {
21807
- return widgets;
21808
- }
21809
- return this.applyCompositionWidgetDeliveries(widgets, {
21810
- cycleId: 'bootstrap',
21811
- matchedLinkIds,
21812
- snapshot,
21813
- diagnostics: snapshot.diagnostics,
21814
- }).widgets;
21815
- }
21816
- bootstrapCompositionAdapter(page) {
21817
- if (!page) {
21818
- this.compositionDefinition = undefined;
21819
- return undefined;
21820
- }
21821
- this.compositionDefinition = this.compositionFactory.create({
21822
- ...page,
21823
- state: this.stateRuntime.normalizeState(page.state),
21824
- });
21825
- return this.compositionRuntime.bootstrap(this.compositionDefinition);
21826
- }
21827
- dispatchWidgetEventToComposition(page, fromKey, evt) {
21828
- if (!evt?.output) {
21829
- return null;
21830
- }
21831
- if (!this.compositionDefinition) {
21832
- this.bootstrapCompositionAdapter(page);
21833
- }
21834
- return this.compositionRuntime.dispatch({
21835
- eventId: `widget:${evt.ownerWidgetKey || fromKey}:${evt.output}:${Date.now()}`,
21836
- source: {
21837
- kind: 'component-port',
21838
- ref: {
21839
- widget: evt.ownerWidgetKey || fromKey,
21840
- port: evt.output,
21841
- direction: 'output',
21842
- componentType: evt.sourceComponentId,
21843
- },
21844
- },
21845
- payload: evt.payload,
21846
- occurredAt: new Date().toISOString(),
21847
- });
21848
- }
21849
- stateFromCompositionSnapshot(baseState, primaryValues) {
21850
- const normalized = this.stateRuntime.normalizeState(baseState);
21851
- return {
21852
- ...normalized,
21853
- values: this.cloneStateValues(primaryValues),
21854
- };
21855
- }
21856
- applyCompositionWidgetDeliveries(widgets, cycle) {
21857
- if (!this.compositionDefinition) {
21858
- return { widgets, changed: false };
21859
- }
21860
- const linkMap = new Map(this.compositionDefinition.links.map((link) => [link.id, link]));
21861
- const snapshotMap = new Map(cycle.snapshot.links.map((link) => [link.linkId, link]));
21862
- let changed = false;
21863
- for (const linkId of cycle.matchedLinkIds) {
21864
- const link = linkMap.get(linkId);
21865
- const linkSnapshot = snapshotMap.get(linkId);
21866
- if (!link || !linkSnapshot || linkSnapshot.status !== 'delivered' || link.to.kind !== 'component-port') {
21867
- continue;
21868
- }
21869
- const targetRef = link.to.ref;
21870
- const widgetIndex = widgets.findIndex((widget) => widget.key === targetRef.widget);
21871
- if (widgetIndex < 0) {
21872
- continue;
21873
- }
21874
- const bindingPath = targetRef.bindingPath || targetRef.port;
21875
- const currentInputs = widgets[widgetIndex].definition.inputs || {};
21876
- const nextValue = this.cloneValue(linkSnapshot.lastDeliveredValuePreview);
21877
- const currentValue = this.conn.extractByPath(currentInputs, bindingPath);
21878
- if (this.areStateValuesEqual(currentValue, nextValue)) {
21879
- continue;
21880
- }
21881
- widgets[widgetIndex] = {
21882
- ...widgets[widgetIndex],
21883
- definition: {
21884
- ...widgets[widgetIndex].definition,
21885
- inputs: this.conn.setValueAtPath(this.cloneStateValues(currentInputs), bindingPath, nextValue),
21886
- },
21887
- };
21888
- changed = true;
23469
+ assertNoLegacyConnections(page) {
23470
+ if (!page)
23471
+ return;
23472
+ if (!Object.prototype.hasOwnProperty.call(page, 'connections')) {
23473
+ return;
21889
23474
  }
21890
- return { widgets, changed };
23475
+ throw new Error('DynamicWidgetPageComponent no longer accepts `page.connections`. Use `composition.links` only.');
21891
23476
  }
21892
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicWidgetPageComponent, deps: [{ token: ConnectionManagerService }, { token: WidgetPageStateRuntimeService }, { token: SETTINGS_PANEL_BRIDGE, optional: true }, { token: DYNAMIC_PAGE_SHELL_EDITOR, optional: true }, { token: DYNAMIC_PAGE_CONFIG_EDITOR, optional: true }], target: i0.ɵɵFactoryTarget.Component });
23477
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicWidgetPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
21893
23478
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: DynamicWidgetPageComponent, isStandalone: true, selector: "praxis-dynamic-page", inputs: { page: "page", context: "context", strictValidation: "strictValidation", enableCustomization: "enableCustomization", showPageSettingsButton: "showPageSettingsButton", shellEditorComponent: "shellEditorComponent", pageEditorComponent: "pageEditorComponent", autoPersist: "autoPersist", pageIdentity: "pageIdentity", componentInstanceId: "componentInstanceId" }, outputs: { pageChange: "pageChange", widgetDiagnosticsChange: "widgetDiagnosticsChange" }, host: { listeners: { "window:resize": "onWindowResize()", "window:pointermove": "onCanvasPointerMove($event)", "window:pointerup": "onCanvasPointerUp($event)", "window:pointercancel": "onCanvasPointerCancel($event)" } }, providers: [providePraxisI18nConfig(DYNAMIC_WIDGET_PAGE_I18N_CONFIG)], viewQueries: [{ propertyName: "pageCanvasHost", first: true, predicate: ["pageCanvasHost"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
21894
23479
  <div class="pdx-page-wrapper" [class.editing]="enableCustomization">
21895
23480
  @if (enableCustomization && showPageSettingsButton) {
@@ -22073,7 +23658,7 @@ class DynamicWidgetPageComponent {
22073
23658
  <div class="pdx-sr-only" aria-live="polite">{{ layoutAnnouncement }}</div>
22074
23659
  }
22075
23660
  </div>
22076
- `, isInline: true, styles: [".pdx-page-wrapper{position:relative;display:block}.pdx-page{display:grid;gap:16px;grid-template-columns:minmax(0,1fr)}.pdx-page--canvas{align-items:start;grid-auto-flow:dense}.pdx-page-settings{position:sticky;top:8px;z-index:2;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);color:inherit;border-radius:999px;margin-bottom:8px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:8px;min-width:0;min-height:0}.pdx-widget--canvas{align-self:stretch;--pdx-resize-gradient-horizontal: linear-gradient( 90deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-vertical: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-se: linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-sw: linear-gradient( 225deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-widget--canvas:after{content:\"\";position:absolute;inset:0;padding:2px;border-radius:14px;background:var(--pdx-active-resize-gradient);opacity:0;pointer-events:none;transition:opacity .14s ease,filter .14s ease;filter:saturate(1.02) drop-shadow(0 0 5px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent));-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.pdx-widget--canvas-selected:before,.pdx-widget--canvas-blocked:before{content:\"\";position:absolute;inset:0;border-radius:14px;pointer-events:none;z-index:1;transition:opacity .14s ease,box-shadow .14s ease,border-color .14s ease}.pdx-widget--canvas-selected:before{border:1px dashed color-mix(in srgb,var(--md-sys-color-primary) 34%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent),0 8px 20px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent)}.pdx-widget--canvas-blocked:before{border:1px solid color-mix(in srgb,var(--md-sys-color-error) 74%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 20%,transparent),0 10px 24px color-mix(in srgb,var(--md-sys-color-error) 12%,transparent)}.pdx-widget--canvas:has(.pdx-canvas-resize:hover):after,.pdx-widget--canvas:has(.pdx-canvas-resize:focus-visible):after{opacity:.82}.pdx-widget--canvas:has(.pdx-canvas-resize--north:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-horizontal)}.pdx-widget--canvas:has(.pdx-canvas-resize--east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-vertical)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-sw)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize{position:absolute;z-index:7;border:0;padding:0;margin:0;background:transparent;touch-action:none;cursor:pointer;opacity:0;pointer-events:none;transform:scale(.94);transition:opacity .14s ease,transform .14s ease,filter .14s ease;filter:saturate(.9)}.pdx-widget--canvas:hover .pdx-canvas-resize,.pdx-widget--canvas:focus-within .pdx-canvas-resize,.pdx-widget--canvas-selected .pdx-canvas-resize{opacity:.88;pointer-events:auto;transform:scale(1);filter:saturate(1)}.pdx-canvas-resize-grip{display:block;width:9px;height:9px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 42%,var(--md-sys-color-outline-variant) 58%);background:color-mix(in srgb,var(--md-sys-color-surface) 92%,var(--md-sys-color-primary) 8%);box-shadow:0 1px 4px color-mix(in srgb,var(--md-sys-color-shadow) 10%,transparent);transition:transform .14s ease,border-color .14s ease,background .14s ease,box-shadow .14s ease}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:color-mix(in srgb,var(--md-sys-color-primary) 58%,var(--md-sys-color-outline-variant) 42%);background:color-mix(in srgb,var(--md-sys-color-surface) 84%,var(--md-sys-color-primary) 16%);box-shadow:0 2px 7px color-mix(in srgb,var(--md-sys-color-primary) 14%,transparent);transform:scale(1.04)}.pdx-canvas-resize--north,.pdx-canvas-resize--south{left:50%;width:40px;height:18px;margin-left:-20px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north{top:-8px;cursor:ns-resize}.pdx-canvas-resize--south{bottom:-8px;cursor:ns-resize}.pdx-canvas-resize--east,.pdx-canvas-resize--west{top:50%;width:18px;height:40px;margin-top:-20px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--east{right:-8px;cursor:ew-resize}.pdx-canvas-resize--west{left:-8px;cursor:ew-resize}.pdx-canvas-resize--north-east,.pdx-canvas-resize--north-west,.pdx-canvas-resize--south-east,.pdx-canvas-resize--south-west{width:18px;height:18px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north-east{top:-8px;right:-8px;cursor:nesw-resize}.pdx-canvas-resize--north-west{top:-8px;left:-8px;cursor:nwse-resize}.pdx-canvas-resize--south-east{bottom:-8px;right:-8px;cursor:nwse-resize}.pdx-canvas-resize--south-west{bottom:-8px;left:-8px;cursor:nesw-resize}.pdx-canvas-snap-preview{position:relative;align-self:stretch;border-radius:12px;pointer-events:none;z-index:0;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent),color-mix(in srgb,var(--md-sys-color-tertiary) 12%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 42%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 55%,transparent);animation:pdx-snap-preview-pulse .48s ease-out infinite alternate}.pdx-canvas-snap-preview:before,.pdx-canvas-snap-preview:after{content:\"\";position:absolute;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary) 82%,var(--md-sys-color-tertiary) 18%);opacity:.72}.pdx-canvas-snap-preview:before{inset:0 auto 0 0;width:3px}.pdx-canvas-snap-preview:after{inset:0 0 auto;height:3px}.pdx-canvas-snap-preview--invalid{background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-error) 18%,transparent),color-mix(in srgb,var(--md-sys-color-error-container) 22%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 58%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 40%,transparent)}.pdx-canvas-snap-preview--invalid:before,.pdx-canvas-snap-preview--invalid:after{background:color-mix(in srgb,var(--md-sys-color-error) 86%,var(--md-sys-color-error-container) 14%)}.pdx-canvas-resize:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:transparent;box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent),var(--mat-elevation-level3)}.pdx-canvas-resize--north:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-horizontal)}.pdx-canvas-resize--east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-vertical)}.pdx-canvas-resize--north-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-sw)}.pdx-canvas-resize--north-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-west:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{transform:scale(1.08)}@keyframes pdx-snap-preview-pulse{0%{opacity:.72;transform:scale(.996)}to{opacity:1;transform:scale(1)}}.pdx-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pdx-group{display:grid;gap:12px;grid-column:1 / -1;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 76%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.pdx-group--hero{padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-primary-container) 42%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low) 86%,transparent))}.pdx-group--rail{align-self:start}.pdx-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.pdx-group-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.pdx-group-content{display:grid;gap:12px}.pdx-group-content--stack{grid-template-columns:minmax(0,1fr)}.pdx-group-content--row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.pdx-group-content--grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.pdx-group-tabs{display:grid;gap:12px}.pdx-tabs-header{display:flex;flex-wrap:wrap;gap:8px}.pdx-tab-chip{appearance:none;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface);border-radius:999px;padding:8px 12px;font:inherit;cursor:pointer}.pdx-tab-chip.active{border-color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 78%,transparent);color:var(--md-sys-color-on-primary-container)}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: WidgetShellComponent, selector: "praxis-widget-shell", inputs: ["shell", "context", "dragSurfaceEnabled", "dragSurfaceLabel"], outputs: ["action", "dragSurfacePointerDown", "dragSurfaceKeydown"] }] });
23661
+ `, isInline: true, styles: [".pdx-page-wrapper{position:relative;display:block}.pdx-page{display:grid;gap:16px;grid-template-columns:minmax(0,1fr)}.pdx-page--canvas{align-items:start;grid-auto-flow:dense}.pdx-page-settings{position:sticky;top:8px;z-index:2;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);color:inherit;border-radius:999px;margin-bottom:8px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:8px;min-width:0;min-height:0}.pdx-widget--canvas{align-self:stretch;--pdx-resize-gradient-horizontal: linear-gradient( 90deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-vertical: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-se: linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-sw: linear-gradient( 225deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-widget--canvas:after{content:\"\";position:absolute;inset:0;padding:2px;border-radius:14px;background:var(--pdx-active-resize-gradient);opacity:0;pointer-events:none;transition:opacity .14s ease,filter .14s ease;filter:saturate(1.02) drop-shadow(0 0 5px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent));-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.pdx-widget--canvas-selected:before,.pdx-widget--canvas-blocked:before{content:\"\";position:absolute;inset:0;border-radius:14px;pointer-events:none;z-index:1;transition:opacity .14s ease,box-shadow .14s ease,border-color .14s ease}.pdx-widget--canvas-selected:before{border:1px dashed color-mix(in srgb,var(--md-sys-color-primary) 34%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent),0 8px 20px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent)}.pdx-widget--canvas-blocked:before{border:1px solid color-mix(in srgb,var(--md-sys-color-error) 74%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 20%,transparent),0 10px 24px color-mix(in srgb,var(--md-sys-color-error) 12%,transparent)}.pdx-widget--canvas:has(.pdx-canvas-resize:hover):after,.pdx-widget--canvas:has(.pdx-canvas-resize:focus-visible):after{opacity:.82}.pdx-widget--canvas:has(.pdx-canvas-resize--north:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-horizontal)}.pdx-widget--canvas:has(.pdx-canvas-resize--east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-vertical)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-sw)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize{position:absolute;z-index:7;border:0;padding:0;margin:0;background:transparent;touch-action:none;cursor:pointer;opacity:0;pointer-events:none;transform:scale(.94);transition:opacity .14s ease,transform .14s ease,filter .14s ease;filter:saturate(.9)}.pdx-widget--canvas:hover .pdx-canvas-resize,.pdx-widget--canvas:focus-within .pdx-canvas-resize,.pdx-widget--canvas-selected .pdx-canvas-resize{opacity:.88;pointer-events:auto;transform:scale(1);filter:saturate(1)}.pdx-canvas-resize-grip{display:block;width:9px;height:9px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 42%,var(--md-sys-color-outline-variant) 58%);background:color-mix(in srgb,var(--md-sys-color-surface) 92%,var(--md-sys-color-primary) 8%);box-shadow:0 1px 4px color-mix(in srgb,var(--md-sys-color-shadow) 10%,transparent);transition:transform .14s ease,border-color .14s ease,background .14s ease,box-shadow .14s ease}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:color-mix(in srgb,var(--md-sys-color-primary) 58%,var(--md-sys-color-outline-variant) 42%);background:color-mix(in srgb,var(--md-sys-color-surface) 84%,var(--md-sys-color-primary) 16%);box-shadow:0 2px 7px color-mix(in srgb,var(--md-sys-color-primary) 14%,transparent);transform:scale(1.04)}.pdx-canvas-resize--north,.pdx-canvas-resize--south{left:50%;width:40px;height:18px;margin-left:-20px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north{top:-8px;cursor:ns-resize}.pdx-canvas-resize--south{bottom:-8px;cursor:ns-resize}.pdx-canvas-resize--east,.pdx-canvas-resize--west{top:50%;width:18px;height:40px;margin-top:-20px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--east{right:-8px;cursor:ew-resize}.pdx-canvas-resize--west{left:-8px;cursor:ew-resize}.pdx-canvas-resize--north-east,.pdx-canvas-resize--north-west,.pdx-canvas-resize--south-east,.pdx-canvas-resize--south-west{width:18px;height:18px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north-east{top:-8px;right:-8px;cursor:nesw-resize}.pdx-canvas-resize--north-west{top:-8px;left:-8px;cursor:nwse-resize}.pdx-canvas-resize--south-east{bottom:-8px;right:-8px;cursor:nwse-resize}.pdx-canvas-resize--south-west{bottom:-8px;left:-8px;cursor:nesw-resize}.pdx-canvas-snap-preview{position:relative;align-self:stretch;border-radius:12px;pointer-events:none;z-index:0;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent),color-mix(in srgb,var(--md-sys-color-tertiary) 12%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 42%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 55%,transparent);animation:pdx-snap-preview-pulse .48s ease-out infinite alternate}.pdx-canvas-snap-preview:before,.pdx-canvas-snap-preview:after{content:\"\";position:absolute;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary) 82%,var(--md-sys-color-tertiary) 18%);opacity:.72}.pdx-canvas-snap-preview:before{inset:0 auto 0 0;width:3px}.pdx-canvas-snap-preview:after{inset:0 0 auto;height:3px}.pdx-canvas-snap-preview--invalid{background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-error) 18%,transparent),color-mix(in srgb,var(--md-sys-color-error-container) 22%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 58%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 40%,transparent)}.pdx-canvas-snap-preview--invalid:before,.pdx-canvas-snap-preview--invalid:after{background:color-mix(in srgb,var(--md-sys-color-error) 86%,var(--md-sys-color-error-container) 14%)}.pdx-canvas-resize:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:transparent;box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent),var(--mat-elevation-level3)}.pdx-canvas-resize--north:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-horizontal)}.pdx-canvas-resize--east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-vertical)}.pdx-canvas-resize--north-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-sw)}.pdx-canvas-resize--north-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-west:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{transform:scale(1.08)}@keyframes pdx-snap-preview-pulse{0%{opacity:.72;transform:scale(.996)}to{opacity:1;transform:scale(1)}}.pdx-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pdx-group{display:grid;gap:12px;grid-column:1 / -1;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 76%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.pdx-group--hero{padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-primary-container) 42%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low) 86%,transparent))}.pdx-group--rail{align-self:start}.pdx-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.pdx-group-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.pdx-group-content{display:grid;gap:12px}.pdx-group-content--stack{grid-template-columns:minmax(0,1fr)}.pdx-group-content--row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.pdx-group-content--grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.pdx-group-tabs{display:grid;gap:12px}.pdx-tabs-header{display:flex;flex-wrap:wrap;gap:8px}.pdx-tab-chip{appearance:none;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface);border-radius:999px;padding:8px 12px;font:inherit;cursor:pointer}.pdx-tab-chip.active{border-color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 78%,transparent);color:var(--md-sys-color-on-primary-container)}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: WidgetShellComponent, selector: "praxis-widget-shell", inputs: ["shell", "context", "dragSurfaceEnabled", "dragSurfaceLabel"], outputs: ["action", "dragSurfacePointerDown", "dragSurfaceKeydown"] }] });
22077
23662
  }
22078
23663
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicWidgetPageComponent, decorators: [{
22079
23664
  type: Component,
@@ -22261,22 +23846,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
22261
23846
  }
22262
23847
  </div>
22263
23848
  `, styles: [".pdx-page-wrapper{position:relative;display:block}.pdx-page{display:grid;gap:16px;grid-template-columns:minmax(0,1fr)}.pdx-page--canvas{align-items:start;grid-auto-flow:dense}.pdx-page-settings{position:sticky;top:8px;z-index:2;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);color:inherit;border-radius:999px;margin-bottom:8px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:8px;min-width:0;min-height:0}.pdx-widget--canvas{align-self:stretch;--pdx-resize-gradient-horizontal: linear-gradient( 90deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-vertical: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-se: linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-sw: linear-gradient( 225deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-widget--canvas:after{content:\"\";position:absolute;inset:0;padding:2px;border-radius:14px;background:var(--pdx-active-resize-gradient);opacity:0;pointer-events:none;transition:opacity .14s ease,filter .14s ease;filter:saturate(1.02) drop-shadow(0 0 5px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent));-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.pdx-widget--canvas-selected:before,.pdx-widget--canvas-blocked:before{content:\"\";position:absolute;inset:0;border-radius:14px;pointer-events:none;z-index:1;transition:opacity .14s ease,box-shadow .14s ease,border-color .14s ease}.pdx-widget--canvas-selected:before{border:1px dashed color-mix(in srgb,var(--md-sys-color-primary) 34%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent),0 8px 20px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent)}.pdx-widget--canvas-blocked:before{border:1px solid color-mix(in srgb,var(--md-sys-color-error) 74%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 20%,transparent),0 10px 24px color-mix(in srgb,var(--md-sys-color-error) 12%,transparent)}.pdx-widget--canvas:has(.pdx-canvas-resize:hover):after,.pdx-widget--canvas:has(.pdx-canvas-resize:focus-visible):after{opacity:.82}.pdx-widget--canvas:has(.pdx-canvas-resize--north:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-horizontal)}.pdx-widget--canvas:has(.pdx-canvas-resize--east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-vertical)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-sw)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize{position:absolute;z-index:7;border:0;padding:0;margin:0;background:transparent;touch-action:none;cursor:pointer;opacity:0;pointer-events:none;transform:scale(.94);transition:opacity .14s ease,transform .14s ease,filter .14s ease;filter:saturate(.9)}.pdx-widget--canvas:hover .pdx-canvas-resize,.pdx-widget--canvas:focus-within .pdx-canvas-resize,.pdx-widget--canvas-selected .pdx-canvas-resize{opacity:.88;pointer-events:auto;transform:scale(1);filter:saturate(1)}.pdx-canvas-resize-grip{display:block;width:9px;height:9px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 42%,var(--md-sys-color-outline-variant) 58%);background:color-mix(in srgb,var(--md-sys-color-surface) 92%,var(--md-sys-color-primary) 8%);box-shadow:0 1px 4px color-mix(in srgb,var(--md-sys-color-shadow) 10%,transparent);transition:transform .14s ease,border-color .14s ease,background .14s ease,box-shadow .14s ease}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:color-mix(in srgb,var(--md-sys-color-primary) 58%,var(--md-sys-color-outline-variant) 42%);background:color-mix(in srgb,var(--md-sys-color-surface) 84%,var(--md-sys-color-primary) 16%);box-shadow:0 2px 7px color-mix(in srgb,var(--md-sys-color-primary) 14%,transparent);transform:scale(1.04)}.pdx-canvas-resize--north,.pdx-canvas-resize--south{left:50%;width:40px;height:18px;margin-left:-20px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north{top:-8px;cursor:ns-resize}.pdx-canvas-resize--south{bottom:-8px;cursor:ns-resize}.pdx-canvas-resize--east,.pdx-canvas-resize--west{top:50%;width:18px;height:40px;margin-top:-20px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--east{right:-8px;cursor:ew-resize}.pdx-canvas-resize--west{left:-8px;cursor:ew-resize}.pdx-canvas-resize--north-east,.pdx-canvas-resize--north-west,.pdx-canvas-resize--south-east,.pdx-canvas-resize--south-west{width:18px;height:18px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north-east{top:-8px;right:-8px;cursor:nesw-resize}.pdx-canvas-resize--north-west{top:-8px;left:-8px;cursor:nwse-resize}.pdx-canvas-resize--south-east{bottom:-8px;right:-8px;cursor:nwse-resize}.pdx-canvas-resize--south-west{bottom:-8px;left:-8px;cursor:nesw-resize}.pdx-canvas-snap-preview{position:relative;align-self:stretch;border-radius:12px;pointer-events:none;z-index:0;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent),color-mix(in srgb,var(--md-sys-color-tertiary) 12%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 42%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 55%,transparent);animation:pdx-snap-preview-pulse .48s ease-out infinite alternate}.pdx-canvas-snap-preview:before,.pdx-canvas-snap-preview:after{content:\"\";position:absolute;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary) 82%,var(--md-sys-color-tertiary) 18%);opacity:.72}.pdx-canvas-snap-preview:before{inset:0 auto 0 0;width:3px}.pdx-canvas-snap-preview:after{inset:0 0 auto;height:3px}.pdx-canvas-snap-preview--invalid{background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-error) 18%,transparent),color-mix(in srgb,var(--md-sys-color-error-container) 22%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 58%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 40%,transparent)}.pdx-canvas-snap-preview--invalid:before,.pdx-canvas-snap-preview--invalid:after{background:color-mix(in srgb,var(--md-sys-color-error) 86%,var(--md-sys-color-error-container) 14%)}.pdx-canvas-resize:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:transparent;box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent),var(--mat-elevation-level3)}.pdx-canvas-resize--north:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-horizontal)}.pdx-canvas-resize--east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-vertical)}.pdx-canvas-resize--north-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-sw)}.pdx-canvas-resize--north-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-west:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{transform:scale(1.08)}@keyframes pdx-snap-preview-pulse{0%{opacity:.72;transform:scale(.996)}to{opacity:1;transform:scale(1)}}.pdx-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pdx-group{display:grid;gap:12px;grid-column:1 / -1;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 76%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.pdx-group--hero{padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-primary-container) 42%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low) 86%,transparent))}.pdx-group--rail{align-self:start}.pdx-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.pdx-group-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.pdx-group-content{display:grid;gap:12px}.pdx-group-content--stack{grid-template-columns:minmax(0,1fr)}.pdx-group-content--row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.pdx-group-content--grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.pdx-group-tabs{display:grid;gap:12px}.pdx-tabs-header{display:flex;flex-wrap:wrap;gap:8px}.pdx-tab-chip{appearance:none;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface);border-radius:999px;padding:8px 12px;font:inherit;cursor:pointer}.pdx-tab-chip.active{border-color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 78%,transparent);color:var(--md-sys-color-on-primary-container)}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"] }]
22264
- }], ctorParameters: () => [{ type: ConnectionManagerService }, { type: WidgetPageStateRuntimeService }, { type: undefined, decorators: [{
22265
- type: Optional
22266
- }, {
22267
- type: Inject,
22268
- args: [SETTINGS_PANEL_BRIDGE]
22269
- }] }, { type: i0.Type, decorators: [{
22270
- type: Optional
22271
- }, {
22272
- type: Inject,
22273
- args: [DYNAMIC_PAGE_SHELL_EDITOR]
22274
- }] }, { type: i0.Type, decorators: [{
22275
- type: Optional
22276
- }, {
22277
- type: Inject,
22278
- args: [DYNAMIC_PAGE_CONFIG_EDITOR]
22279
- }] }], propDecorators: { pageCanvasHost: [{
23849
+ }], ctorParameters: () => [], propDecorators: { pageCanvasHost: [{
22280
23850
  type: ViewChild,
22281
23851
  args: ['pageCanvasHost']
22282
23852
  }], page: [{
@@ -22323,23 +23893,23 @@ const PRAXIS_DYNAMIC_PAGE_COMPONENT_METADATA = {
22323
23893
  selector: 'praxis-dynamic-page',
22324
23894
  component: DynamicWidgetPageComponent,
22325
23895
  friendlyName: 'Dynamic Page',
22326
- description: 'Página dinâmica com widgets e conexões em layout de grid responsivo.',
23896
+ description: 'Pagina dinamica com widgets e composition.links em layout de grid responsivo.',
22327
23897
  icon: 'dashboard',
22328
23898
  inputs: [
22329
- { name: 'page', type: 'WidgetPageDefinition', description: 'Definição da página (widgets, layout e conexões).' },
23899
+ { name: 'page', type: 'WidgetPageDefinition', description: 'Definicao da pagina (widgets, layout e composition.links).' },
22330
23900
  { name: 'context', type: 'Record<string, any>', description: 'Contexto adicional compartilhado entre widgets.' },
22331
- { name: 'strictValidation', type: 'boolean', description: 'Habilita validação estrita de inputs.' },
22332
- { name: 'enableCustomization', type: 'boolean', description: 'Habilita affordances de edição na página.' },
22333
- { name: 'showPageSettingsButton', type: 'boolean', description: 'Exibe botão de configuração da página.' },
23901
+ { name: 'strictValidation', type: 'boolean', description: 'Habilita validacao estrita de inputs.' },
23902
+ { name: 'enableCustomization', type: 'boolean', description: 'Habilita affordances de edicao na pagina.' },
23903
+ { name: 'showPageSettingsButton', type: 'boolean', description: 'Exibe botao de configuracao da pagina.' },
22334
23904
  { name: 'shellEditorComponent', type: 'Type<any>', description: 'Override do editor de shell dos widgets.' },
22335
- { name: 'pageEditorComponent', type: 'Type<any>', description: 'Override do editor de configuração da página.' },
22336
- { name: 'autoPersist', type: 'boolean', description: 'Ativa persistência automática (load/save) da página.' },
22337
- { name: 'pageIdentity', type: 'PageIdentity', description: 'Identidade de persistência (tenant/usuário/rota/locale).' },
22338
- { name: 'componentInstanceId', type: 'string', description: 'Identificador opcional para múltiplas instâncias na mesma rota.' },
23905
+ { name: 'pageEditorComponent', type: 'Type<any>', description: 'Override do editor de configuracao da pagina.' },
23906
+ { name: 'autoPersist', type: 'boolean', description: 'Ativa persistencia automatica (load/save) da pagina.' },
23907
+ { name: 'pageIdentity', type: 'PageIdentity', description: 'Identidade de persistencia (tenant/usuario/rota/locale).' },
23908
+ { name: 'componentInstanceId', type: 'string', description: 'Identificador opcional para multiplas instancias na mesma rota.' },
22339
23909
  ],
22340
23910
  outputs: [
22341
- { name: 'pageChange', type: 'WidgetPageDefinition', description: 'Emitido ao alterar a definição da página.' },
22342
- { name: 'widgetDiagnosticsChange', type: 'Record<string, WidgetResolutionDiagnostic>', description: 'Emitido quando o runtime detecta widgets resolvidos ou falhos durante o carregamento dinâmico.' },
23911
+ { name: 'pageChange', type: 'WidgetPageDefinition', description: 'Emitido ao alterar a definicao da pagina.' },
23912
+ { name: 'widgetDiagnosticsChange', type: 'Record<string, WidgetResolutionDiagnostic>', description: 'Emitido quando o runtime detecta widgets resolvidos ou falhos durante o carregamento dinamico.' },
22343
23913
  ],
22344
23914
  tags: ['widget', 'page', 'dynamic', 'layout'],
22345
23915
  lib: '@praxisui/core',
@@ -22481,7 +24051,7 @@ class EmptyStateCardComponent {
22481
24051
  </div>
22482
24052
  </mat-card-content>
22483
24053
  </mat-card>
22484
- `, isInline: true, styles: [".empty-card{display:block;margin:12px;border-color:var(--md-sys-color-outline-variant);--empty-icon-color: var(--md-sys-color-on-surface-variant)}.empty-card.empty-inline{margin:8px 0}.content{display:flex;align-items:center;gap:12px}.icon{font-size:32px;width:32px;height:32px;color:var(--empty-icon-color)}.texts{display:grid;gap:4px}.title{margin:0;font-size:16px;font-weight:600;color:var(--md-sys-color-on-surface)}.desc{margin:0;color:var(--md-sys-color-on-surface-variant)}.actions{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}.empty-card.tone-primary{--empty-icon-color: var(--md-sys-color-primary)}.empty-card.tone-secondary{--empty-icon-color: var(--md-sys-color-secondary)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3$1.MatCardContent, selector: "mat-card-content" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
24054
+ `, isInline: true, styles: [".empty-card{display:block;margin:12px;border-color:var(--md-sys-color-outline-variant);--empty-icon-color: var(--md-sys-color-on-surface-variant)}.empty-card.empty-inline{margin:8px 0}.content{display:flex;align-items:center;gap:12px}.icon{font-size:32px;width:32px;height:32px;color:var(--empty-icon-color)}.texts{display:grid;gap:4px}.title{margin:0;font-size:16px;font-weight:600;color:var(--md-sys-color-on-surface)}.desc{margin:0;color:var(--md-sys-color-on-surface-variant)}.actions{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}.empty-card.tone-primary{--empty-icon-color: var(--md-sys-color-primary)}.empty-card.tone-secondary{--empty-icon-color: var(--md-sys-color-secondary)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$2.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3$2.MatCardContent, selector: "mat-card-content" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
22485
24055
  }
22486
24056
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EmptyStateCardComponent, decorators: [{
22487
24057
  type: Component,
@@ -22586,7 +24156,7 @@ class ResourceQuickConnectComponent {
22586
24156
  Rota normalizada: {{ normalizedPath() }}
22587
24157
  </small>
22588
24158
  </div>
22589
- `, isInline: true, styles: [".pdx-quick-connect{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.pdx-quick-connect__hint{color:var(--md-sys-color-on-surface-variant);font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.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: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5$1.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.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
24159
+ `, isInline: true, styles: [".pdx-quick-connect{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.pdx-quick-connect__hint{color:var(--md-sys-color-on-surface-variant);font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.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: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: i3$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
22590
24160
  }
22591
24161
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceQuickConnectComponent, decorators: [{
22592
24162
  type: Component,
@@ -22814,7 +24384,7 @@ class PraxisIconPickerComponent {
22814
24384
  <span class="pip-typed" *ngIf="query.trim()">{{ previewValue() }}</span>
22815
24385
  </div>
22816
24386
  </div>
22817
- `, isInline: true, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px;color:var(--md-sys-color-on-surface)}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap}.pip-search{flex:1;min-width:180px}.pip-spacer{flex:1}.pip-body{flex:1;overflow:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;padding:4px}.pip-item{display:flex;gap:10px;align-items:center;justify-content:flex-start;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);color:inherit;cursor:pointer;transition:background-color .18s ease,border-color .18s ease,box-shadow .18s ease}.pip-item:hover{background:var(--md-sys-color-surface-container)}.pip-item:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pip-item.selected{border-color:var(--md-sys-color-primary);box-shadow:0 0 0 2px var(--md-sys-color-primary)}.pip-name{font-size:12px;color:var(--md-sys-color-on-surface-variant);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin:-6px 0 8px}.pip-stats{display:flex;justify-content:space-between;gap:8px;font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-bottom:8px}.pip-shortcut{opacity:.9}.pip-empty{padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:10px;color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container);margin-top:8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;color:var(--md-sys-color-on-surface)}.pip-family{display:flex;align-items:center}.pip-family .mat-mdc-chip-listbox{min-width:260px}.pip-root .mat-icon{font-family:Material Icons,Material Symbols Outlined,Material Symbols Rounded,Material Symbols Sharp!important;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor}.material-symbols-outlined{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-rounded{font-family:Material Symbols Rounded;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-sharp{font-family:Material Symbols Sharp;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.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: i5$1.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.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: "ngmodule", type: MatChipsModule }, { kind: "component", type: i8$1.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i8$1.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
24387
+ `, isInline: true, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px;color:var(--md-sys-color-on-surface)}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap}.pip-search{flex:1;min-width:180px}.pip-spacer{flex:1}.pip-body{flex:1;overflow:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;padding:4px}.pip-item{display:flex;gap:10px;align-items:center;justify-content:flex-start;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);color:inherit;cursor:pointer;transition:background-color .18s ease,border-color .18s ease,box-shadow .18s ease}.pip-item:hover{background:var(--md-sys-color-surface-container)}.pip-item:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pip-item.selected{border-color:var(--md-sys-color-primary);box-shadow:0 0 0 2px var(--md-sys-color-primary)}.pip-name{font-size:12px;color:var(--md-sys-color-on-surface-variant);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin:-6px 0 8px}.pip-stats{display:flex;justify-content:space-between;gap:8px;font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-bottom:8px}.pip-shortcut{opacity:.9}.pip-empty{padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:10px;color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container);margin-top:8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;color:var(--md-sys-color-on-surface)}.pip-family{display:flex;align-items:center}.pip-family .mat-mdc-chip-listbox{min-width:260px}.pip-root .mat-icon{font-family:Material Icons,Material Symbols Outlined,Material Symbols Rounded,Material Symbols Sharp!important;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor}.material-symbols-outlined{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-rounded{font-family:Material Symbols Rounded;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-sharp{font-family:Material Symbols Sharp;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.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: i5.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: i3$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: "ngmodule", type: MatChipsModule }, { kind: "component", type: i8$1.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i8$1.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
22818
24388
  }
22819
24389
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisIconPickerComponent, decorators: [{
22820
24390
  type: Component,
@@ -23158,7 +24728,7 @@ class SchemaViewerComponent {
23158
24728
  </mat-tab-group>
23159
24729
  </mat-card-content>
23160
24730
  </mat-card>
23161
- `, isInline: true, styles: [".schema-viewer{display:block;border-color:var(--md-sys-color-outline-variant)}.header{display:flex;align-items:center;justify-content:space-between;gap:8px}.title{display:flex;align-items:center;gap:8px}.title h3{margin:0;font-weight:600;color:var(--md-sys-color-on-surface)}.notes{margin-top:6px;color:var(--md-sys-color-on-surface-variant);font-size:12px}.copy-status{margin-top:6px;font-size:12px;color:var(--md-sys-color-primary)}.section{padding:12px}.section-actions{display:flex;justify-content:flex-end;margin-bottom:8px}.sub{font-weight:600;margin:12px 0 4px;color:var(--md-sys-color-on-surface)}.pretty{background:var(--md-sys-color-surface-container);padding:10px;border-radius:8px;overflow:auto;border:1px solid var(--md-sys-color-outline-variant)}.kv{display:grid;grid-template-columns:140px 1fr;align-items:baseline;gap:8px;margin:2px 0}.kv>span{color:var(--md-sys-color-on-surface-variant)}.kv>code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.muted{color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2$1.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"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3$1.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i3$1.MatCardHeader, selector: "mat-card-header" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i1$2.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
24731
+ `, isInline: true, styles: [".schema-viewer{display:block;border-color:var(--md-sys-color-outline-variant)}.header{display:flex;align-items:center;justify-content:space-between;gap:8px}.title{display:flex;align-items:center;gap:8px}.title h3{margin:0;font-weight:600;color:var(--md-sys-color-on-surface)}.notes{margin-top:6px;color:var(--md-sys-color-on-surface-variant);font-size:12px}.copy-status{margin-top:6px;font-size:12px;color:var(--md-sys-color-primary)}.section{padding:12px}.section-actions{display:flex;justify-content:flex-end;margin-bottom:8px}.sub{font-weight:600;margin:12px 0 4px;color:var(--md-sys-color-on-surface)}.pretty{background:var(--md-sys-color-surface-container);padding:10px;border-radius:8px;overflow:auto;border:1px solid var(--md-sys-color-outline-variant)}.kv{display:grid;grid-template-columns:140px 1fr;align-items:baseline;gap:8px;margin:2px 0}.kv>span{color:var(--md-sys-color-on-surface-variant)}.kv>code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.muted{color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2$1.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"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$2.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3$2.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i3$2.MatCardHeader, selector: "mat-card-header" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i3$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i1$2.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
23162
24732
  }
23163
24733
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SchemaViewerComponent, decorators: [{
23164
24734
  type: Component,
@@ -23269,162 +24839,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
23269
24839
  type: Input
23270
24840
  }] } });
23271
24841
 
23272
- async function parseJsonResponseOrEmpty(res, context = 'response') {
23273
- const raw = await res.text();
23274
- if (!raw || !raw.trim()) {
23275
- return {};
23276
- }
23277
- try {
23278
- return JSON.parse(raw);
23279
- }
23280
- catch (error) {
23281
- const e = new Error(`${context}: invalid JSON payload`);
23282
- e.cause = error;
23283
- throw e;
23284
- }
23285
- }
23286
- async function fetchWithETag(params) {
23287
- const headers = { Accept: 'application/json' };
23288
- if (params.schemaHash)
23289
- headers['If-None-Match'] = `"${params.schemaHash}"`;
23290
- if (params.tenant)
23291
- headers['X-Tenant'] = params.tenant;
23292
- if (params.locale)
23293
- headers['Accept-Language'] = params.locale;
23294
- // Allow host apps to provide additional headers (e.g., Authorization) without coupling
23295
- try {
23296
- const extra = globalThis?.PAX_FETCH_HEADERS?.();
23297
- if (extra && typeof extra === 'object') {
23298
- for (const [k, v] of Object.entries(extra)) {
23299
- const key = String(k);
23300
- const val = Array.isArray(v) ? String(v[0]) : String(v);
23301
- if (val && !headers[key])
23302
- headers[key] = val;
23303
- }
23304
- }
23305
- }
23306
- catch {
23307
- // ignore header hook errors
23308
- }
23309
- const res = await fetch(params.url, {
23310
- headers,
23311
- cache: 'no-cache',
23312
- credentials: params.credentials ?? 'include',
23313
- signal: params.signal,
23314
- });
23315
- if (res.status === 304)
23316
- return { status: 304 };
23317
- if (!res.ok) {
23318
- const raw = await res.text().catch(() => '');
23319
- const bodyHint = raw?.trim() ? `: ${raw.trim().slice(0, 240)}` : '';
23320
- throw new Error(`fetchWithETag failed (${res.status} ${res.statusText || 'HTTP error'})${bodyHint}`);
23321
- }
23322
- const etag = res.headers.get('ETag') || '';
23323
- const newHash = etag.replace(/^W\//, '').replace(/^\"|\"$/g, '');
23324
- const xSchemaHash = res.headers.get('X-Schema-Hash');
23325
- const schemaHash = xSchemaHash || newHash; // prefer explicit header when available
23326
- const schema = await parseJsonResponseOrEmpty(res, 'fetchWithETag');
23327
- return { status: 200, schema, schemaHash };
23328
- }
23329
-
23330
- class SchemaMetadataClient {
23331
- cache;
23332
- inFlight = new Map();
23333
- constructor(cache) {
23334
- this.cache = cache;
23335
- }
23336
- async getSchema(params) {
23337
- // Bind identity to API origin to avoid cross-origin collisions
23338
- let apiOrigin = '';
23339
- try {
23340
- apiOrigin = new URL(params.baseUrl).origin;
23341
- }
23342
- catch {
23343
- try {
23344
- apiOrigin = globalThis?.location?.origin || '';
23345
- }
23346
- catch { }
23347
- }
23348
- const schemaId = buildSchemaId({ ...params, apiOrigin });
23349
- const cached = await this.cache.get(schemaId);
23350
- // Build URL with query params
23351
- let u;
23352
- try {
23353
- u = new URL(params.baseUrl);
23354
- }
23355
- catch (err) {
23356
- // Accept relative baseUrl by resolving against the app origin when available
23357
- const origin = (() => {
23358
- try {
23359
- return globalThis?.location?.origin;
23360
- }
23361
- catch {
23362
- return undefined;
23363
- }
23364
- })();
23365
- if (origin && origin.startsWith('http')) {
23366
- u = new URL(params.baseUrl, origin);
23367
- }
23368
- else {
23369
- // Friendly error for hosts: instruct to set absolute baseUrl or provide runtime origin
23370
- const e = new Error(`Failed to construct schema URL. API_URL.baseUrl appears relative and no runtime origin is available. ` +
23371
- `Set API_URL.baseUrl to an absolute URL (e.g., http://localhost:4200/api) or run in a browser context. ` +
23372
- `Received baseUrl="${params.baseUrl}".`);
23373
- e.cause = err;
23374
- throw e;
23375
- }
23376
- }
23377
- u.searchParams.set('path', params.path);
23378
- u.searchParams.set('operation', (params.operation || 'get').toLowerCase());
23379
- u.searchParams.set('schemaType', (params.schemaType || 'response').toLowerCase());
23380
- u.searchParams.set('includeInternalSchemas', String(!!params.includeInternalSchemas));
23381
- const key = schemaId;
23382
- if (this.inFlight.has(key))
23383
- return this.inFlight.get(key);
23384
- const p = (async () => {
23385
- const res = await fetchWithETag({
23386
- url: u.toString(),
23387
- schemaHash: cached?.schemaHash,
23388
- tenant: params.tenant,
23389
- locale: params.locale,
23390
- });
23391
- if (res.status === 304) {
23392
- if (cached)
23393
- return cached;
23394
- // No cache: refetch without If-None-Match
23395
- const refetch = await fetch(u.toString(), { cache: 'no-cache', credentials: 'include' });
23396
- if (!refetch.ok) {
23397
- const raw = await refetch.text().catch(() => '');
23398
- const bodyHint = raw?.trim() ? `: ${raw.trim().slice(0, 240)}` : '';
23399
- throw new Error(`SchemaMetadataClient refetch failed (${refetch.status} ${refetch.statusText || 'HTTP error'})${bodyHint}`);
23400
- }
23401
- const etag = refetch.headers.get('ETag') || '';
23402
- const xHash = refetch.headers.get('X-Schema-Hash') || '';
23403
- const schemaHash = (xHash || etag).replace(/^W\//, '').replace(/^\"|\"$/g, '');
23404
- const schema = await parseJsonResponseOrEmpty(refetch, 'SchemaMetadataClient refetch');
23405
- const fallback = { schema, schemaHash };
23406
- await this.cache.set(schemaId, fallback);
23407
- return fallback;
23408
- }
23409
- // res.status === 200
23410
- const entry = {
23411
- schema: res.schema,
23412
- schemaHash: res.schemaHash,
23413
- };
23414
- await this.cache.set(schemaId, entry);
23415
- return entry;
23416
- })();
23417
- this.inFlight.set(key, p);
23418
- try {
23419
- const result = await p;
23420
- return result;
23421
- }
23422
- finally {
23423
- this.inFlight.delete(key);
23424
- }
23425
- }
23426
- }
23427
-
23428
24842
  function buildBaseFormField(def) {
23429
24843
  const meta = mapFieldDefinitionsToMetadata([def])[0];
23430
24844
  return meta;
@@ -23913,9 +25327,6 @@ function provideHookWhitelist(allowed) {
23913
25327
  return [{ provide: FORM_HOOKS_WHITELIST, useValue: allowed, multi: true }];
23914
25328
  }
23915
25329
 
23916
- /*
23917
- * Public API Surface of praxis-core
23918
- */
23919
25330
  /*
23920
25331
  * Public API Surface of praxis-core
23921
25332
  */
@@ -23924,4 +25335,4 @@ function provideHookWhitelist(allowed) {
23924
25335
  * Generated bundle index. Do not edit.
23925
25336
  */
23926
25337
 
23927
- export { API_CONFIG_STORAGE_OPTIONS, API_URL, ASYNC_CONFIG_STORAGE, AllowedFileTypes, ApiConfigStorage, ApiEndpoint, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, BUILTIN_SHELL_PRESETS, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentKeyService, ComponentMetadataRegistry, CompositionRuntimeFacade, ConnectionManagerService, ConsoleLoggerSink, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, DEFAULT_TABLE_CONFIG, DYNAMIC_PAGE_AI_CAPABILITIES, DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK, DYNAMIC_PAGE_CONFIG_EDITOR, DYNAMIC_PAGE_SHELL_EDITOR, DefaultLoadingRenderer, DeferredAsyncConfigStorage, DynamicFormService, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EDITORIAL_ALLOWED_CONTENT_FORMATS, EDITORIAL_COMPLIANCE_PRESETS, EDITORIAL_EXTERNAL_LINK_REL, EDITORIAL_FORM_TEMPLATE_CATALOG, EDITORIAL_HTML_ENABLED, EDITORIAL_MARKDOWN_IMAGES_ENABLED, EDITORIAL_SOLUTION_CATALOG, EDITORIAL_SOLUTION_PRESETS, EDITORIAL_THEME_PRESETS, EDITORIAL_WIDGET_CONVENTION_INPUTS, EDITORIAL_WIDGET_TAG, EMPLOYEE_ONBOARDING_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_EDITORIAL_TEMPLATE, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_TEMPLATE, EVENT_REGISTRATION_EDITORIAL_SOLUTION, EVENT_REGISTRATION_EDITORIAL_TEMPLATE, EmptyStateCardComponent, ErrorMessageService, FIELD_METADATA_CAPABILITIES, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, FIELD_SELECTOR_REGISTRY_OVERRIDES, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FieldSelectorRegistry, FormHooksRegistry, GLOBAL_ACTION_CATALOG$1 as GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_HANDLERS, GLOBAL_ACTION_CATALOG as GLOBAL_ACTION_SPEC_CATALOG, GLOBAL_ACTION_UI_SCHEMAS, GLOBAL_ANALYTICS_SERVICE, GLOBAL_API_CLIENT, GLOBAL_CONFIG, GLOBAL_DIALOG_SERVICE, GLOBAL_ROUTE_GUARD_RESOLVER, GLOBAL_SURFACE_SERVICE, GLOBAL_TOAST_SERVICE, GenericCrudService, GlobalActionService, GlobalConfigService, INLINE_FILTER_ALIAS_TOKENS, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_SET, INLINE_FILTER_CONTROL_TYPE_VALUES, INLINE_FILTER_TOKEN_TO_BASE_CONTROL_TYPE, INLINE_FILTER_TOKEN_TO_CONTROL_TYPE, IconPickerService, IconPosition, IconSize, LOGGER_LEVEL_BY_ENV, LOGGER_LEVEL_PRIORITY, LoadingOrchestrator, LocalConnectionStorage, LocalStorageAsyncAdapter, LocalStorageCacheAdapter, LocalStorageConfigService, LoggerService, LoggerThrottleTracker, LoggerWarnOnceTracker, MemoryCacheAdapter, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, ObservabilityDashboardService, OverlayDeciderService, PRAXIS_CORPORATE_SENSITIVE_KEYS, PRAXIS_DEFAULT_OBSERVABILITY_ALERT_RULES, PRAXIS_DYNAMIC_PAGE_COMPONENT_METADATA, PRAXIS_FOOTER_LINKS_METADATA, PRAXIS_GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_OPTIONS, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_READY, PRAXIS_GLOBAL_CONFIG_TENANT_RESOLVER, PRAXIS_HERO_BANNER_METADATA, PRAXIS_I18N_CONFIG, PRAXIS_I18N_TRANSLATOR, PRAXIS_LEGAL_NOTICE_METADATA, PRAXIS_LOADING_CTX, PRAXIS_LOADING_RENDERER, PRAXIS_LOGGER_CONFIG, PRAXIS_LOGGER_SINKS, PRAXIS_OBSERVABILITY_DASHBOARD_OPTIONS, PRAXIS_RICH_TEXT_BLOCK_METADATA, PRAXIS_TELEMETRY_TRANSPORT, PRAXIS_USER_CONTEXT_SUMMARY_METADATA, PRIVACY_CONSENT_EDITORIAL_SOLUTION, PRIVACY_CONSENT_EDITORIAL_TEMPLATE, PraxisCore, PraxisFooterLinksComponent, PraxisGlobalErrorHandler, PraxisHeroBannerComponent, PraxisI18nService, PraxisIconDirective, PraxisIconPickerComponent, PraxisLegalNoticeComponent, PraxisLoadingInterceptor, PraxisRichTextBlockComponent, PraxisSurfaceHostComponent, PraxisUserContextSummaryComponent, RULE_PROPERTY_SCHEMA, RemoteConfigStorage, ResourceQuickConnectComponent, SCHEMA_VIEWER_CONTEXT, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SURFACE_OPEN_I18N_CONFIG, SURFACE_OPEN_I18N_NAMESPACE, SURFACE_OPEN_PRESETS, SchemaMetadataClient, SchemaNormalizerService, SchemaViewerComponent, SurfaceBindingRuntimeService, SurfaceOpenActionEditorComponent, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryLoggerSink, TelemetryService, ValidationPattern, WidgetPageStateRuntimeService, WidgetShellComponent, adaptWidgetConnectionToCompositionLink, adaptWidgetConnectionsToCompositionLinks, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildFormConfigFromEditorialTemplate, buildHeaders, buildPageKey, buildSchemaId, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, clampRange, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCorporateLoggerConfig, createCorporateObservabilityOptions, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, extractNormalizedError, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getDefaultFormHints, getEditorialCompliancePresetById, getEditorialFormTemplateById, getEditorialFormTemplateCatalog, getEditorialSolutionById, getEditorialSolutionCatalog, getEditorialSolutionPresetById, getEditorialThemePresetById, getEssentialConfig, getFieldMetadataCapabilities, getGlobalActionCatalog, getGlobalActionUiSchema, getReferencedFieldMetadata, getTextTransformer, interpolatePraxisTranslation, isAllowedEditorialContentFormat, isAllowedEditorialHref, isCssTextTransform, isEditorialComponentMeta, isInlineFilterControlType, isRangeValidForFilter, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, matchFieldValidator, maxFileSizeValidator, mergeFieldMetadata, mergePraxisI18nConfigs, mergeTableConfigs, migrateFormLayoutRule, minWordsValidator, normalizeControlTypeKey, normalizeControlTypeToken, normalizeEditorialLink, normalizeEnd, normalizeFieldConstraints, normalizeFormConfig, normalizeFormMetadata, normalizePath, normalizePraxisDataQueryContext, normalizeStart, normalizeUnknownError, notifySuccessHook, parseJsonResponseOrEmpty, praxisLoadingInterceptorFn, prefillFromContextHook, provideDefaultFormHooks, provideFieldSelectorRegistryBase, provideFieldSelectorRegistryOverride, provideFieldSelectorRegistryRuntime, provideFormHookPresets, provideFormHooks, provideGlobalActionCatalog, provideGlobalActionHandler, provideGlobalConfig, provideGlobalConfigReady, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, providePraxisAnalyticsGlobalActions, providePraxisDynamicPageMetadata, providePraxisFooterLinksMetadata, providePraxisGlobalActionCatalog, providePraxisGlobalActions, providePraxisGlobalConfigBootstrap, providePraxisHeroBannerMetadata, providePraxisHttpLoading, providePraxisI18n, providePraxisI18nConfig, providePraxisI18nTranslator, providePraxisLegalNoticeMetadata, providePraxisLoadingDefaults, providePraxisLogging, providePraxisRichTextBlockMetadata, providePraxisToastGlobalActions, providePraxisUserContextSummaryMetadata, provideRemoteGlobalConfig, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, removeDiacritics, reportTelemetryHookFactory, requiredCheckedValidator, resolveBuiltinPresets, resolveControlTypeAlias, resolveDefaultValuePresentationFormat, resolveHidden, resolveInlineFilterControlType, resolveInlineFilterControlTypeToBaseControlType, resolveLoggerConfig, resolveObservabilityOptions, resolveOffset, resolveOrder, resolvePraxisFilterCriteria, resolveSpan, resolveValuePresentation, resolveValuePresentationLocale, slugify, stripMasksHook, supportsImplicitValuePresentation, syncWithServerMetadata, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, trim, uniqueAsyncValidator, urlValidator, withMessage, withPraxisHttpLoading };
25338
+ export { API_CONFIG_STORAGE_OPTIONS, API_URL, ASYNC_CONFIG_STORAGE, AllowedFileTypes, AnalyticsPresentationResolver, AnalyticsSchemaContractService, AnalyticsStatsRequestBuilderService, ApiConfigStorage, ApiEndpoint, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, BUILTIN_SHELL_PRESETS, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentKeyService, ComponentMetadataRegistry, CompositionRuntimeFacade, ConsoleLoggerSink, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, DEFAULT_TABLE_CONFIG, DYNAMIC_PAGE_AI_CAPABILITIES, DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK, DYNAMIC_PAGE_CONFIG_EDITOR, DYNAMIC_PAGE_SHELL_EDITOR, DefaultLoadingRenderer, DeferredAsyncConfigStorage, DynamicFormService, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EDITORIAL_ALLOWED_CONTENT_FORMATS, EDITORIAL_COMPLIANCE_PRESETS, EDITORIAL_EXTERNAL_LINK_REL, EDITORIAL_FORM_TEMPLATE_CATALOG, EDITORIAL_HTML_ENABLED, EDITORIAL_MARKDOWN_IMAGES_ENABLED, EDITORIAL_SOLUTION_CATALOG, EDITORIAL_SOLUTION_PRESETS, EDITORIAL_THEME_PRESETS, EDITORIAL_WIDGET_CONVENTION_INPUTS, EDITORIAL_WIDGET_TAG, EMPLOYEE_ONBOARDING_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_EDITORIAL_TEMPLATE, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_TEMPLATE, EVENT_REGISTRATION_EDITORIAL_SOLUTION, EVENT_REGISTRATION_EDITORIAL_TEMPLATE, EmptyStateCardComponent, ErrorMessageService, FIELD_METADATA_CAPABILITIES, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, FIELD_SELECTOR_REGISTRY_OVERRIDES, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FieldSelectorRegistry, FormHooksRegistry, GLOBAL_ACTION_CATALOG$1 as GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_HANDLERS, GLOBAL_ACTION_CATALOG as GLOBAL_ACTION_SPEC_CATALOG, GLOBAL_ACTION_UI_SCHEMAS, GLOBAL_ANALYTICS_SERVICE, GLOBAL_API_CLIENT, GLOBAL_CONFIG, GLOBAL_DIALOG_SERVICE, GLOBAL_ROUTE_GUARD_RESOLVER, GLOBAL_SURFACE_SERVICE, GLOBAL_TOAST_SERVICE, GenericCrudService, GlobalActionService, GlobalConfigService, INLINE_FILTER_ALIAS_TOKENS, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_SET, INLINE_FILTER_CONTROL_TYPE_VALUES, INLINE_FILTER_TOKEN_TO_BASE_CONTROL_TYPE, INLINE_FILTER_TOKEN_TO_CONTROL_TYPE, IconPickerService, IconPosition, IconSize, LOGGER_LEVEL_BY_ENV, LOGGER_LEVEL_PRIORITY, LoadingOrchestrator, LocalConnectionStorage, LocalStorageAsyncAdapter, LocalStorageCacheAdapter, LocalStorageConfigService, LoggerService, LoggerThrottleTracker, LoggerWarnOnceTracker, MemoryCacheAdapter, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, ObservabilityDashboardService, OverlayDeciderService, PRAXIS_CORPORATE_SENSITIVE_KEYS, PRAXIS_DEFAULT_OBSERVABILITY_ALERT_RULES, PRAXIS_DYNAMIC_PAGE_COMPONENT_METADATA, PRAXIS_FOOTER_LINKS_METADATA, PRAXIS_GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_OPTIONS, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_READY, PRAXIS_GLOBAL_CONFIG_TENANT_RESOLVER, PRAXIS_HERO_BANNER_METADATA, PRAXIS_I18N_CONFIG, PRAXIS_I18N_TRANSLATOR, PRAXIS_LAYER_SCALE_DEFAULTS, PRAXIS_LAYER_SCALE_VARS, PRAXIS_LEGAL_NOTICE_METADATA, PRAXIS_LOADING_CTX, PRAXIS_LOADING_RENDERER, PRAXIS_LOGGER_CONFIG, PRAXIS_LOGGER_SINKS, PRAXIS_OBSERVABILITY_DASHBOARD_OPTIONS, PRAXIS_RICH_TEXT_BLOCK_METADATA, PRAXIS_TELEMETRY_TRANSPORT, PRAXIS_USER_CONTEXT_SUMMARY_METADATA, PRIVACY_CONSENT_EDITORIAL_SOLUTION, PRIVACY_CONSENT_EDITORIAL_TEMPLATE, PraxisCore, PraxisFooterLinksComponent, PraxisGlobalErrorHandler, PraxisHeroBannerComponent, PraxisI18nService, PraxisIconDirective, PraxisIconPickerComponent, PraxisLayerScaleStyleService, PraxisLegalNoticeComponent, PraxisLoadingInterceptor, PraxisRichTextBlockComponent, PraxisSurfaceHostComponent, PraxisUserContextSummaryComponent, RESOURCE_DISCOVERY_I18N_CONFIG, RESOURCE_DISCOVERY_I18N_NAMESPACE, RULE_PROPERTY_SCHEMA, RemoteConfigStorage, ResourceActionOpenAdapterService, ResourceDiscoveryService, ResourceQuickConnectComponent, ResourceSurfaceOpenAdapterService, SCHEMA_VIEWER_CONTEXT, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SURFACE_DRAWER_BRIDGE, SURFACE_OPEN_I18N_CONFIG, SURFACE_OPEN_I18N_NAMESPACE, SURFACE_OPEN_PRESETS, SchemaMetadataClient, SchemaNormalizerService, SchemaViewerComponent, SurfaceBindingRuntimeService, SurfaceOpenActionEditorComponent, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryLoggerSink, TelemetryService, ValidationPattern, WidgetPageStateRuntimeService, WidgetShellComponent, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildFormConfigFromEditorialTemplate, buildHeaders, buildPageKey, buildPraxisLayerScaleCss, buildSchemaId, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, clampRange, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCorporateLoggerConfig, createCorporateObservabilityOptions, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, extractNormalizedError, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getDefaultFormHints, getEditorialCompliancePresetById, getEditorialFormTemplateById, getEditorialFormTemplateCatalog, getEditorialSolutionById, getEditorialSolutionCatalog, getEditorialSolutionPresetById, getEditorialThemePresetById, getEssentialConfig, getFieldMetadataCapabilities, getGlobalActionCatalog, getGlobalActionUiSchema, getReferencedFieldMetadata, getTextTransformer, interpolatePraxisTranslation, isAllowedEditorialContentFormat, isAllowedEditorialHref, isCssTextTransform, isEditorialComponentMeta, isInlineFilterControlType, isRangeValidForFilter, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, matchFieldValidator, maxFileSizeValidator, mergeFieldMetadata, mergePraxisI18nConfigs, mergeTableConfigs, migrateFormLayoutRule, minWordsValidator, normalizeControlTypeKey, normalizeControlTypeToken, normalizeEditorialLink, normalizeEnd, normalizeFieldConstraints, normalizeFormConfig, normalizeFormMetadata, normalizePath, normalizePraxisDataQueryContext, normalizeResourceAvailabilityReasonCode, normalizeStart, normalizeUnknownError, notifySuccessHook, parseJsonResponseOrEmpty, praxisLoadingInterceptorFn, prefillFromContextHook, provideDefaultFormHooks, provideFieldSelectorRegistryBase, provideFieldSelectorRegistryOverride, provideFieldSelectorRegistryRuntime, provideFormHookPresets, provideFormHooks, provideGlobalActionCatalog, provideGlobalActionHandler, provideGlobalConfig, provideGlobalConfigReady, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, providePraxisAnalyticsGlobalActions, providePraxisDynamicPageMetadata, providePraxisFooterLinksMetadata, providePraxisGlobalActionCatalog, providePraxisGlobalActions, providePraxisGlobalConfigBootstrap, providePraxisHeroBannerMetadata, providePraxisHttpLoading, providePraxisI18n, providePraxisI18nConfig, providePraxisI18nTranslator, providePraxisLegalNoticeMetadata, providePraxisLoadingDefaults, providePraxisLogging, providePraxisRichTextBlockMetadata, providePraxisToastGlobalActions, providePraxisUserContextSummaryMetadata, provideRemoteGlobalConfig, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, removeDiacritics, reportTelemetryHookFactory, requiredCheckedValidator, resolveBuiltinPresets, resolveControlTypeAlias, resolveDefaultValuePresentationFormat, resolveHidden, resolveInlineFilterControlType, resolveInlineFilterControlTypeToBaseControlType, resolveLoggerConfig, resolveObservabilityOptions, resolveOffset, resolveOrder, resolvePraxisFilterCriteria, resolveResourceAvailabilityReasonKey, resolveSpan, resolveValuePresentation, resolveValuePresentationLocale, slugify, stripMasksHook, supportsImplicitValuePresentation, syncWithServerMetadata, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, translateResourceAvailabilityReason, translateResourceDiscoveryText, translateUnavailableWorkflowMessage, trim, uniqueAsyncValidator, urlValidator, withMessage, withPraxisHttpLoading };