@praxisui/core 1.0.0-beta.3 → 1.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, InjectionToken, Injectable, inject, APP_INITIALIZER, Inject, makeEnvironmentProviders, ViewContainerRef, EventEmitter, Output, Input, Directive, signal, computed, HostListener, Optional } from '@angular/core';
2
+ import { Component, InjectionToken, Injectable, inject, APP_INITIALIZER, Inject, makeEnvironmentProviders, ViewContainerRef, EventEmitter, Output, Input, Directive, signal, computed, HostListener, Optional, ChangeDetectionStrategy } from '@angular/core';
3
3
  import * as i1 from '@angular/common/http';
4
4
  import { HttpHeaders, HttpClient, HttpParams } from '@angular/common/http';
5
5
  import { BehaviorSubject, from, of, EMPTY, throwError, firstValueFrom } from 'rxjs';
@@ -8,20 +8,22 @@ import * as i1$1 from '@angular/forms';
8
8
  import { Validators, FormGroup, FormControl, FormsModule } from '@angular/forms';
9
9
  import * as i2 from '@angular/common';
10
10
  import { CommonModule } from '@angular/common';
11
- import * as i2$1 from '@angular/material/card';
11
+ import * as i3 from '@angular/material/card';
12
12
  import { MatCardModule } from '@angular/material/card';
13
- import * as i3 from '@angular/material/button';
13
+ import * as i3$1 from '@angular/material/button';
14
14
  import { MatButtonModule } from '@angular/material/button';
15
15
  import * as i4 from '@angular/material/icon';
16
16
  import { MatIconModule } from '@angular/material/icon';
17
- import * as i2$2 from '@angular/material/form-field';
17
+ import * as i2$1 from '@angular/material/form-field';
18
18
  import { MatFormFieldModule } from '@angular/material/form-field';
19
- import * as i3$1 from '@angular/material/input';
19
+ import * as i3$2 from '@angular/material/input';
20
20
  import { MatInputModule } from '@angular/material/input';
21
21
  import * as i8 from '@angular/material/chips';
22
22
  import { MatChipsModule } from '@angular/material/chips';
23
23
  import * as i1$2 from '@angular/material/dialog';
24
24
  import { MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
25
+ import * as i2$2 from '@angular/material/tabs';
26
+ import { MatTabsModule } from '@angular/material/tabs';
25
27
 
26
28
  class PraxisCore {
27
29
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisCore, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -849,6 +851,16 @@ class SchemaNormalizerService {
849
851
  // -------------------------------------------------------------------
850
852
  // Layout and presentation
851
853
  // -------------------------------------------------------------------
854
+ // Visibility flags (x-ui)
855
+ if (ui.hidden !== undefined) {
856
+ field.hidden = this.parseBoolean(ui.hidden);
857
+ }
858
+ if (ui.tableHidden !== undefined) {
859
+ field.tableHidden = this.parseBoolean(ui.tableHidden);
860
+ }
861
+ if (ui.formHidden !== undefined) {
862
+ field.formHidden = this.parseBoolean(ui.formHidden);
863
+ }
852
864
  if (ui.width !== undefined) {
853
865
  field.width = ui.width;
854
866
  }
@@ -1127,20 +1139,14 @@ class GenericCrudService {
1127
1139
  const schemaId = buildSchemaId({ path, operation, schemaType, includeInternalSchemas: false, tenant, locale, apiOrigin });
1128
1140
  const headersBase = composeHeadersWithVersion(entry);
1129
1141
  return from(this._schemaCache.get(schemaId)).pipe(concatMap((cached) => {
1130
- let headers = headersBase;
1142
+ let headers = (headersBase instanceof HttpHeaders ? headersBase : new HttpHeaders(headersBase));
1131
1143
  if (cached?.schemaHash) {
1132
- headers = (headersBase instanceof HttpHeaders ? headersBase : new HttpHeaders(headersBase))
1133
- .set('If-None-Match', `"${cached.schemaHash}"`)
1134
- .set('Accept-Language', locale || '')
1135
- .set('X-Tenant', tenant || '');
1136
- }
1137
- else {
1138
- headers = (headersBase instanceof HttpHeaders ? headersBase : new HttpHeaders(headersBase));
1139
- if (locale)
1140
- headers = headers.set('Accept-Language', locale);
1141
- if (tenant)
1142
- headers = headers.set('X-Tenant', tenant);
1144
+ headers = headers.set('If-None-Match', `"${cached.schemaHash}"`);
1143
1145
  }
1146
+ if (locale)
1147
+ headers = headers.set('Accept-Language', locale);
1148
+ if (tenant)
1149
+ headers = headers.set('X-Tenant', tenant);
1144
1150
  console.debug('[CRUD:Service] getSchema (filtered fallback):request', { filteredUrl, path, hasCached: !!cached });
1145
1151
  return this.http.get(filteredUrl, { params: httpParams, headers, observe: 'response' });
1146
1152
  }), concatMap((resp) => {
@@ -1217,9 +1223,9 @@ class GenericCrudService {
1217
1223
  catch { }
1218
1224
  return of(this.schemaNormalizer.normalizeSchema(body));
1219
1225
  }),
1220
- // Angular HttpClient treats 304 as error; handle cache reuse here
1226
+ // Angular HttpClient treats 304 as error; also handle status 0 (CORS/network) by reusing cache
1221
1227
  catchError((err) => {
1222
- if (err?.status === 304) {
1228
+ if (err?.status === 304 || err?.status === 0) {
1223
1229
  return from(this._schemaCache.get(schemaId)).pipe(concatMap((cached) => {
1224
1230
  if (cached?.schema) {
1225
1231
  try {
@@ -1437,9 +1443,9 @@ class GenericCrudService {
1437
1443
  this._schemaCache.set(schemaId, entryToCache);
1438
1444
  return of(this.schemaNormalizer.normalizeSchema(body));
1439
1445
  }),
1440
- // Angular HttpClient may surface 304 as error; handle reuse and refetch
1446
+ // Angular HttpClient may surface 304 as error; also handle status 0 (CORS/network) by reusing cache
1441
1447
  catchError((err) => {
1442
- if (err?.status === 304) {
1448
+ if (err?.status === 304 || err?.status === 0) {
1443
1449
  return from(this._schemaCache.get(schemaId)).pipe(concatMap((cached) => {
1444
1450
  if (cached?.schema) {
1445
1451
  try {
@@ -1958,10 +1964,19 @@ class GenericCrudService {
1958
1964
  const baseUrl = buildApiUrl(entry);
1959
1965
  try {
1960
1966
  const origin = new URL(baseUrl).origin;
1961
- return `${origin}/schemas/filtered`;
1967
+ return `${origin.replace(/\/+$/, '')}/schemas/filtered`;
1962
1968
  }
1963
1969
  catch {
1964
- return '/schemas/filtered';
1970
+ // Tolerate relative baseUrl (e.g., '/api') by resolving against app origin when available
1971
+ try {
1972
+ const appOrigin = globalThis?.location?.origin;
1973
+ if (appOrigin && typeof appOrigin === 'string' && appOrigin.startsWith('http')) {
1974
+ return `${appOrigin.replace(/\/+$/, '')}/schemas/filtered`;
1975
+ }
1976
+ }
1977
+ catch { }
1978
+ // Last resort: localhost origin to build an absolute URL (document SSR guidance in docs)
1979
+ return 'http://localhost/schemas/filtered';
1965
1980
  }
1966
1981
  }
1967
1982
  /**
@@ -2134,27 +2149,12 @@ function createDefaultTableConfig() {
2134
2149
  },
2135
2150
  actions: {
2136
2151
  row: {
2137
- enabled: true,
2152
+ enabled: false,
2138
2153
  position: 'end',
2139
2154
  width: '120px',
2140
2155
  display: 'icons',
2141
2156
  trigger: 'hover',
2142
- actions: [
2143
- {
2144
- id: 'view',
2145
- label: 'Visualizar',
2146
- icon: 'visibility',
2147
- action: 'view',
2148
- },
2149
- { id: 'edit', label: 'Editar', icon: 'edit', action: 'edit' },
2150
- {
2151
- id: 'delete',
2152
- label: 'Excluir',
2153
- icon: 'delete',
2154
- action: 'delete',
2155
- autoDelete: false,
2156
- },
2157
- ],
2157
+ actions: [],
2158
2158
  },
2159
2159
  bulk: {
2160
2160
  enabled: false,
@@ -4126,6 +4126,13 @@ function normalizeFormConfig(config) {
4126
4126
  return config;
4127
4127
  const normalized = { ...config };
4128
4128
  normalized.fieldMetadata = normalizeFormMetadata(config.fieldMetadata);
4129
+ // Ensure hints exist and fill missing values with defaults
4130
+ try {
4131
+ const defaults = getDefaultFormHints();
4132
+ const current = normalized.hints;
4133
+ normalized.hints = fillUndefined(current || {}, defaults);
4134
+ }
4135
+ catch { }
4129
4136
  return normalized;
4130
4137
  }
4131
4138
  /**
@@ -4257,6 +4264,8 @@ function createDefaultFormConfig() {
4257
4264
  ],
4258
4265
  },
4259
4266
  ],
4267
+ // Default mode hints (didactic tooltips)
4268
+ hints: getDefaultFormHints(),
4260
4269
  };
4261
4270
  return ensureIds(config);
4262
4271
  }
@@ -4269,6 +4278,24 @@ function isValidFormConfig(config) {
4269
4278
  function createEmptyFormConfig() {
4270
4279
  return { sections: [] };
4271
4280
  }
4281
+ /**
4282
+ * Default hint texts for data and UI modes.
4283
+ */
4284
+ function getDefaultFormHints() {
4285
+ return {
4286
+ dataModes: {
4287
+ create: 'Criar novo registro. Campos editáveis e ações de salvar habilitadas.',
4288
+ edit: 'Editar registro existente. Campos editáveis e ações de salvar habilitadas.',
4289
+ view: 'Visualizar registro. Sem edição por padrão; combine com Modo Leitura ou Apresentação.',
4290
+ },
4291
+ uiModes: {
4292
+ presentation: 'Modo apresentação: exibe rótulo + valor formatado e oculta inputs/ações. Efetivo apenas em Visualizar.',
4293
+ readonly: 'Modo leitura: mantém os inputs visíveis, porém bloqueados (sem interação). Continua participando da validação e do envio.',
4294
+ disabled: 'Desabilitado: aparência inativa; ideal para bloquear interação visualmente. Evite desativar campos essenciais.',
4295
+ visible: 'Visibilidade: controla exibição sem destruir controles (útil para regras condicionais).',
4296
+ },
4297
+ };
4298
+ }
4272
4299
  /**
4273
4300
  * Merges field metadata into a FormConfig
4274
4301
  * Useful when combining layout with server-loaded metadata
@@ -4711,6 +4738,8 @@ function mapFieldDefinitionToMetadata(field) {
4711
4738
  'disabled',
4712
4739
  'readOnly',
4713
4740
  'hidden',
4741
+ 'formHidden',
4742
+ 'tableHidden',
4714
4743
  'unique',
4715
4744
  'mask',
4716
4745
  'inlineEditing',
@@ -5750,7 +5779,7 @@ class EmptyStateCardComponent {
5750
5779
  </div>
5751
5780
  </mat-card-content>
5752
5781
  </mat-card>
5753
- `, isInline: true, styles: [".empty-card{display:block;margin:12px}.empty-card.empty-inline{margin:8px 0}.content{display:flex;align-items:center;gap:12px}.icon{font-size:32px;width:32px;height:32px;opacity:.9}.title{margin:0;font-size:16px;font-weight:600}.desc{margin:4px 0 0;opacity:.76}.actions{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i2$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i2$1.MatCardContent, selector: "mat-card-content" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.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: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
5782
+ `, isInline: true, styles: [".empty-card{display:block;margin:12px}.empty-card.empty-inline{margin:8px 0}.content{display:flex;align-items:center;gap:12px}.icon{font-size:32px;width:32px;height:32px;opacity:.9}.title{margin:0;font-size:16px;font-weight:600}.desc{margin:4px 0 0;opacity:.76}.actions{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3.MatCardContent, selector: "mat-card-content" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
5754
5783
  }
5755
5784
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: EmptyStateCardComponent, decorators: [{
5756
5785
  type: Component,
@@ -5904,7 +5933,7 @@ class ResourceQuickConnectComponent {
5904
5933
  </mat-form-field>
5905
5934
  <small style="opacity:.75">Conecte o componente à fonte de dados (ex.: /api/&lt;rota-do-recurso&gt;)</small>
5906
5935
  </div>
5907
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$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: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
5936
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
5908
5937
  }
5909
5938
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ResourceQuickConnectComponent, decorators: [{
5910
5939
  type: Component,
@@ -6093,7 +6122,7 @@ class PraxisIconPickerComponent {
6093
6122
  <span class="pip-typed" *ngIf="query.trim()">{{ previewValue() }}</span>
6094
6123
  </div>
6095
6124
  </div>
6096
- `, isInline: true, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px}.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 rgba(0,0,0,.08);border-radius:10px;background:var(--md-sys-color-surface);cursor:pointer}.pip-item:hover{background:#0000000a}.pip-name{font-size:12px;color:#0009;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:#0009;margin:-6px 0 8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:monospace;font-size:12px;color:#000000b3}.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}.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: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.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: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$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: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.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.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i8.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 }] });
6125
+ `, isInline: true, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px}.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, rgba(0,0,0,.14));border-radius:10px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer}.pip-item:hover{background:color-mix(in oklab,var(--md-sys-color-on-surface) 8%,transparent)}.pip-name{font-size:12px;color:var(--md-sys-color-on-surface-variant, rgba(0,0,0,.6));white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:var(--md-sys-color-on-surface-variant, rgba(0,0,0,.6));margin:-6px 0 8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:monospace;font-size:12px;color:var(--md-sys-color-on-surface, rgba(0,0,0,.7))}.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: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.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: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i8.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i8.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 }] });
6097
6126
  }
6098
6127
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisIconPickerComponent, decorators: [{
6099
6128
  type: Component,
@@ -6137,7 +6166,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
6137
6166
  <span class="pip-typed" *ngIf="query.trim()">{{ previewValue() }}</span>
6138
6167
  </div>
6139
6168
  </div>
6140
- `, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px}.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 rgba(0,0,0,.08);border-radius:10px;background:var(--md-sys-color-surface);cursor:pointer}.pip-item:hover{background:#0000000a}.pip-name{font-size:12px;color:#0009;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:#0009;margin:-6px 0 8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:monospace;font-size:12px;color:#000000b3}.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}.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"] }]
6169
+ `, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px}.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, rgba(0,0,0,.14));border-radius:10px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer}.pip-item:hover{background:color-mix(in oklab,var(--md-sys-color-on-surface) 8%,transparent)}.pip-name{font-size:12px;color:var(--md-sys-color-on-surface-variant, rgba(0,0,0,.6));white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:var(--md-sys-color-on-surface-variant, rgba(0,0,0,.6));margin:-6px 0 8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:monospace;font-size:12px;color:var(--md-sys-color-on-surface, rgba(0,0,0,.7))}.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"] }]
6141
6170
  }], ctorParameters: () => [{ type: undefined, decorators: [{
6142
6171
  type: Optional
6143
6172
  }, {
@@ -6196,6 +6225,246 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
6196
6225
  args: [{ providedIn: 'root' }]
6197
6226
  }] });
6198
6227
 
6228
+ /** Optional DI token used by SchemaViewer when input is not provided. */
6229
+ const SCHEMA_VIEWER_CONTEXT = new InjectionToken('SCHEMA_VIEWER_CONTEXT');
6230
+
6231
+ class SchemaViewerComponent {
6232
+ registry = inject(ComponentMetadataRegistry);
6233
+ normalizer = inject(SchemaNormalizerService);
6234
+ injected = inject(SCHEMA_VIEWER_CONTEXT, { optional: true });
6235
+ context;
6236
+ // Internal reactive state
6237
+ _ctx = signal(undefined, ...(ngDevMode ? [{ debugName: "_ctx" }] : []));
6238
+ ctx = computed(() => this._ctx(), ...(ngDevMode ? [{ debugName: "ctx" }] : []));
6239
+ _componentMeta = signal(undefined, ...(ngDevMode ? [{ debugName: "_componentMeta" }] : []));
6240
+ componentMeta = computed(() => this._componentMeta(), ...(ngDevMode ? [{ debugName: "componentMeta" }] : []));
6241
+ _normalizedFields = signal([], ...(ngDevMode ? [{ debugName: "_normalizedFields" }] : []));
6242
+ normalizedFields = computed(() => this._normalizedFields(), ...(ngDevMode ? [{ debugName: "normalizedFields" }] : []));
6243
+ ngOnChanges(changes) {
6244
+ if ('context' in changes) {
6245
+ this.refresh();
6246
+ }
6247
+ }
6248
+ refresh() {
6249
+ const ctx = this.context || this.injected || undefined;
6250
+ this._ctx.set(ctx);
6251
+ if (!ctx) {
6252
+ this._componentMeta.set(undefined);
6253
+ this._normalizedFields.set([]);
6254
+ return;
6255
+ }
6256
+ const meta = ctx.componentId ? this.registry.get(ctx.componentId) : undefined;
6257
+ this._componentMeta.set(meta);
6258
+ // Compute normalized fields if needed
6259
+ if (ctx.normalizedFields && ctx.normalizedFields.length) {
6260
+ this._normalizedFields.set(ctx.normalizedFields);
6261
+ }
6262
+ else if (ctx.backendSchema) {
6263
+ try {
6264
+ const defs = this.normalizer.normalizeSchema(ctx.backendSchema);
6265
+ this._normalizedFields.set(defs || []);
6266
+ }
6267
+ catch {
6268
+ this._normalizedFields.set([]);
6269
+ }
6270
+ }
6271
+ else {
6272
+ this._normalizedFields.set([]);
6273
+ }
6274
+ }
6275
+ hasAnyData() {
6276
+ const c = this._ctx();
6277
+ return !!(c?.rawConfig || c?.effectiveConfig || c?.backendSchema || (this._normalizedFields()?.length));
6278
+ }
6279
+ copyAll() {
6280
+ const c = this._ctx();
6281
+ const payload = {
6282
+ component: this._componentMeta(),
6283
+ config: { raw: c?.rawConfig, effective: c?.effectiveConfig },
6284
+ backendSchema: { meta: c?.schemaMeta, body: c?.backendSchema },
6285
+ fields: this._normalizedFields(),
6286
+ };
6287
+ try {
6288
+ const text = JSON.stringify(payload, null, 2);
6289
+ if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
6290
+ navigator.clipboard.writeText(text);
6291
+ }
6292
+ else {
6293
+ // Fallback: create a temp textarea
6294
+ const ta = document.createElement('textarea');
6295
+ ta.value = text;
6296
+ document.body.appendChild(ta);
6297
+ ta.select();
6298
+ document.execCommand('copy');
6299
+ document.body.removeChild(ta);
6300
+ }
6301
+ }
6302
+ catch { }
6303
+ }
6304
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SchemaViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6305
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: SchemaViewerComponent, isStandalone: true, selector: "praxis-schema-viewer", inputs: { context: "context" }, usesOnChanges: true, ngImport: i0, template: `
6306
+ <mat-card class="schema-viewer" appearance="outlined">
6307
+ <mat-card-header>
6308
+ <div class="header">
6309
+ <div class="title">
6310
+ <mat-icon fontIcon="schema"></mat-icon>
6311
+ <h3>{{ ctx()?.title || 'Schema & Metadata' }}</h3>
6312
+ </div>
6313
+ <div class="actions">
6314
+ <button mat-stroked-button color="primary" (click)="copyAll()" [disabled]="!hasAnyData()">
6315
+ <mat-icon>content_copy</mat-icon>
6316
+ Copiar tudo
6317
+ </button>
6318
+ </div>
6319
+ </div>
6320
+ <div class="notes" *ngIf="ctx()?.notes">{{ ctx()?.notes }}</div>
6321
+ </mat-card-header>
6322
+ <mat-card-content>
6323
+ <mat-tab-group>
6324
+ <mat-tab label="Component">
6325
+ <div class="section">
6326
+ <div class="kv"><span>Id</span><code>{{ componentMeta()?.id }}</code></div>
6327
+ <div class="kv"><span>Selector</span><code>{{ componentMeta()?.selector }}</code></div>
6328
+ <div class="kv"><span>Lib</span><code>{{ componentMeta()?.lib || '—' }}</code></div>
6329
+ <div class="kv"><span>Friendly</span><code>{{ componentMeta()?.friendlyName }}</code></div>
6330
+ <div class="kv" *ngIf="componentMeta()?.description"><span>Descrição</span><code>{{ componentMeta()?.description }}</code></div>
6331
+
6332
+ <div class="sub">Inputs</div>
6333
+ <pre class="pretty" *ngIf="componentMeta()?.inputs?.length; else noInputs">{{ componentMeta()?.inputs | json }}</pre>
6334
+ <ng-template #noInputs><div class="muted">Sem inputs documentados.</div></ng-template>
6335
+
6336
+ <div class="sub">Outputs</div>
6337
+ <pre class="pretty" *ngIf="componentMeta()?.outputs?.length; else noOutputs">{{ componentMeta()?.outputs | json }}</pre>
6338
+ <ng-template #noOutputs><div class="muted">Sem outputs documentados.</div></ng-template>
6339
+ </div>
6340
+ </mat-tab>
6341
+
6342
+ <mat-tab label="Config">
6343
+ <div class="section">
6344
+ <div class="sub">Raw</div>
6345
+ <pre class="pretty" *ngIf="ctx()?.rawConfig as rc; else noRaw">{{ rc | json }}</pre>
6346
+ <ng-template #noRaw><div class="muted">Sem configuração fornecida.</div></ng-template>
6347
+
6348
+ <div class="sub">Effective</div>
6349
+ <pre class="pretty" *ngIf="ctx()?.effectiveConfig as ec; else noEff">{{ ec | json }}</pre>
6350
+ <ng-template #noEff><div class="muted">Sem configuração efetiva (usando raw/defaults do componente).</div></ng-template>
6351
+ </div>
6352
+ </mat-tab>
6353
+
6354
+ <mat-tab label="Backend Schema">
6355
+ <div class="section">
6356
+ <div class="kv" *ngIf="ctx()?.schemaMeta as m">
6357
+ <span>Path</span><code>{{ m.path }}</code>
6358
+ </div>
6359
+ <div class="kv" *ngIf="ctx()?.schemaMeta as m">
6360
+ <span>Operation</span><code>{{ m.operation }}</code>
6361
+ </div>
6362
+ <div class="kv" *ngIf="ctx()?.schemaMeta as m">
6363
+ <span>Schema Type</span><code>{{ m.schemaType }}</code>
6364
+ </div>
6365
+ <div class="kv" *ngIf="ctx()?.schemaMeta?.schemaHash">
6366
+ <span>Server Hash</span><code>{{ ctx()?.schemaMeta?.schemaHash }}</code>
6367
+ </div>
6368
+ <pre class="pretty" *ngIf="ctx()?.backendSchema as bs; else noSchema">{{ bs | json }}</pre>
6369
+ <ng-template #noSchema><div class="muted">Sem schema de backend.</div></ng-template>
6370
+ </div>
6371
+ </mat-tab>
6372
+
6373
+ <mat-tab label="Fields">
6374
+ <div class="section">
6375
+ <div class="muted" *ngIf="!normalizedFields()?.length">Sem campos normalizados.</div>
6376
+ <pre class="pretty" *ngIf="normalizedFields()?.length">{{ normalizedFields() | json }}</pre>
6377
+ </div>
6378
+ </mat-tab>
6379
+ </mat-tab-group>
6380
+ </mat-card-content>
6381
+ </mat-card>
6382
+ `, isInline: true, styles: [".schema-viewer{display:block}.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}.notes{margin-top:6px;opacity:.78;font-size:12px}.section{padding:12px}.sub{font-weight:600;margin:12px 0 4px;opacity:.9}.pretty{background:#0b0b0b0d;padding:8px;border-radius:4px;overflow:auto}.kv{display:grid;grid-template-columns:140px 1fr;align-items:baseline;gap:8px;margin:2px 0}.kv>span{opacity:.75}.kv>code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.muted{opacity:.6}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2$2.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.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i3.MatCardHeader, selector: "mat-card-header" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i2.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6383
+ }
6384
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SchemaViewerComponent, decorators: [{
6385
+ type: Component,
6386
+ args: [{ selector: 'praxis-schema-viewer', standalone: true, imports: [CommonModule, MatTabsModule, MatCardModule, MatButtonModule, MatIconModule], template: `
6387
+ <mat-card class="schema-viewer" appearance="outlined">
6388
+ <mat-card-header>
6389
+ <div class="header">
6390
+ <div class="title">
6391
+ <mat-icon fontIcon="schema"></mat-icon>
6392
+ <h3>{{ ctx()?.title || 'Schema & Metadata' }}</h3>
6393
+ </div>
6394
+ <div class="actions">
6395
+ <button mat-stroked-button color="primary" (click)="copyAll()" [disabled]="!hasAnyData()">
6396
+ <mat-icon>content_copy</mat-icon>
6397
+ Copiar tudo
6398
+ </button>
6399
+ </div>
6400
+ </div>
6401
+ <div class="notes" *ngIf="ctx()?.notes">{{ ctx()?.notes }}</div>
6402
+ </mat-card-header>
6403
+ <mat-card-content>
6404
+ <mat-tab-group>
6405
+ <mat-tab label="Component">
6406
+ <div class="section">
6407
+ <div class="kv"><span>Id</span><code>{{ componentMeta()?.id }}</code></div>
6408
+ <div class="kv"><span>Selector</span><code>{{ componentMeta()?.selector }}</code></div>
6409
+ <div class="kv"><span>Lib</span><code>{{ componentMeta()?.lib || '—' }}</code></div>
6410
+ <div class="kv"><span>Friendly</span><code>{{ componentMeta()?.friendlyName }}</code></div>
6411
+ <div class="kv" *ngIf="componentMeta()?.description"><span>Descrição</span><code>{{ componentMeta()?.description }}</code></div>
6412
+
6413
+ <div class="sub">Inputs</div>
6414
+ <pre class="pretty" *ngIf="componentMeta()?.inputs?.length; else noInputs">{{ componentMeta()?.inputs | json }}</pre>
6415
+ <ng-template #noInputs><div class="muted">Sem inputs documentados.</div></ng-template>
6416
+
6417
+ <div class="sub">Outputs</div>
6418
+ <pre class="pretty" *ngIf="componentMeta()?.outputs?.length; else noOutputs">{{ componentMeta()?.outputs | json }}</pre>
6419
+ <ng-template #noOutputs><div class="muted">Sem outputs documentados.</div></ng-template>
6420
+ </div>
6421
+ </mat-tab>
6422
+
6423
+ <mat-tab label="Config">
6424
+ <div class="section">
6425
+ <div class="sub">Raw</div>
6426
+ <pre class="pretty" *ngIf="ctx()?.rawConfig as rc; else noRaw">{{ rc | json }}</pre>
6427
+ <ng-template #noRaw><div class="muted">Sem configuração fornecida.</div></ng-template>
6428
+
6429
+ <div class="sub">Effective</div>
6430
+ <pre class="pretty" *ngIf="ctx()?.effectiveConfig as ec; else noEff">{{ ec | json }}</pre>
6431
+ <ng-template #noEff><div class="muted">Sem configuração efetiva (usando raw/defaults do componente).</div></ng-template>
6432
+ </div>
6433
+ </mat-tab>
6434
+
6435
+ <mat-tab label="Backend Schema">
6436
+ <div class="section">
6437
+ <div class="kv" *ngIf="ctx()?.schemaMeta as m">
6438
+ <span>Path</span><code>{{ m.path }}</code>
6439
+ </div>
6440
+ <div class="kv" *ngIf="ctx()?.schemaMeta as m">
6441
+ <span>Operation</span><code>{{ m.operation }}</code>
6442
+ </div>
6443
+ <div class="kv" *ngIf="ctx()?.schemaMeta as m">
6444
+ <span>Schema Type</span><code>{{ m.schemaType }}</code>
6445
+ </div>
6446
+ <div class="kv" *ngIf="ctx()?.schemaMeta?.schemaHash">
6447
+ <span>Server Hash</span><code>{{ ctx()?.schemaMeta?.schemaHash }}</code>
6448
+ </div>
6449
+ <pre class="pretty" *ngIf="ctx()?.backendSchema as bs; else noSchema">{{ bs | json }}</pre>
6450
+ <ng-template #noSchema><div class="muted">Sem schema de backend.</div></ng-template>
6451
+ </div>
6452
+ </mat-tab>
6453
+
6454
+ <mat-tab label="Fields">
6455
+ <div class="section">
6456
+ <div class="muted" *ngIf="!normalizedFields()?.length">Sem campos normalizados.</div>
6457
+ <pre class="pretty" *ngIf="normalizedFields()?.length">{{ normalizedFields() | json }}</pre>
6458
+ </div>
6459
+ </mat-tab>
6460
+ </mat-tab-group>
6461
+ </mat-card-content>
6462
+ </mat-card>
6463
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".schema-viewer{display:block}.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}.notes{margin-top:6px;opacity:.78;font-size:12px}.section{padding:12px}.sub{font-weight:600;margin:12px 0 4px;opacity:.9}.pretty{background:#0b0b0b0d;padding:8px;border-radius:4px;overflow:auto}.kv{display:grid;grid-template-columns:140px 1fr;align-items:baseline;gap:8px;margin:2px 0}.kv>span{opacity:.75}.kv>code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.muted{opacity:.6}\n"] }]
6464
+ }], propDecorators: { context: [{
6465
+ type: Input
6466
+ }] } });
6467
+
6199
6468
  async function fetchWithETag(params) {
6200
6469
  const headers = { Accept: 'application/json' };
6201
6470
  if (params.schemaHash)
@@ -6247,11 +6516,41 @@ class SchemaMetadataClient {
6247
6516
  try {
6248
6517
  apiOrigin = new URL(params.baseUrl).origin;
6249
6518
  }
6250
- catch { }
6519
+ catch {
6520
+ try {
6521
+ apiOrigin = globalThis?.location?.origin || '';
6522
+ }
6523
+ catch { }
6524
+ }
6251
6525
  const schemaId = buildSchemaId({ ...params, apiOrigin });
6252
6526
  const cached = await this.cache.get(schemaId);
6253
6527
  // Build URL with query params
6254
- const u = new URL(params.baseUrl);
6528
+ let u;
6529
+ try {
6530
+ u = new URL(params.baseUrl);
6531
+ }
6532
+ catch (err) {
6533
+ // Accept relative baseUrl by resolving against the app origin when available
6534
+ const origin = (() => {
6535
+ try {
6536
+ return globalThis?.location?.origin;
6537
+ }
6538
+ catch {
6539
+ return undefined;
6540
+ }
6541
+ })();
6542
+ if (origin && origin.startsWith('http')) {
6543
+ u = new URL(params.baseUrl, origin);
6544
+ }
6545
+ else {
6546
+ // Friendly error for hosts: instruct to set absolute baseUrl or provide runtime origin
6547
+ const e = new Error(`Failed to construct schema URL. API_URL.baseUrl appears relative and no runtime origin is available. ` +
6548
+ `Set API_URL.baseUrl to an absolute URL (e.g., http://localhost:4200/api) or run in a browser context. ` +
6549
+ `Received baseUrl="${params.baseUrl}".`);
6550
+ e.cause = err;
6551
+ throw e;
6552
+ }
6553
+ }
6255
6554
  u.searchParams.set('path', params.path);
6256
6555
  u.searchParams.set('operation', (params.operation || 'get').toLowerCase());
6257
6556
  u.searchParams.set('schemaType', (params.schemaType || 'response').toLowerCase());
@@ -6794,5 +7093,5 @@ function provideHookWhitelist(allowed) {
6794
7093
  * Generated bundle index. Do not edit.
6795
7094
  */
6796
7095
 
6797
- export { API_URL, AllowedFileTypes, ApiEndpoint, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentMetadataRegistry, ConnectionManagerService, DEFAULT_TABLE_CONFIG, DynamicFormService, DynamicGridPageComponent, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EmptyStateCardComponent, ErrorMessageService, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FormHooksRegistry, GLOBAL_CONFIG, GenericCrudService, GlobalConfigService, IconPickerService, IconPosition, IconSize, LocalConnectionStorage, LocalStorageCacheAdapter, LocalStorageConfigService, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, OverlayDeciderService, PraxisCore, PraxisIconDirective, PraxisIconPickerComponent, ResourceQuickConnectComponent, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SchemaMetadataClient, SchemaNormalizerService, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryService, ValidationPattern, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildHeaders, buildPageKey, buildSchemaId, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getEssentialConfig, getReferencedFieldMetadata, getTextTransformer, isCssTextTransform, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, matchFieldValidator, maxFileSizeValidator, mergeFieldMetadata, mergeTableConfigs, minWordsValidator, normalizeFieldConstraints, normalizeFormConfig, normalizeFormMetadata, normalizePath, notifySuccessHook, prefillFromContextHook, provideDefaultFormHooks, provideFormHookPresets, provideFormHooks, provideGlobalConfig, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, provideRemoteGlobalConfig, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, removeDiacritics, reportTelemetryHookFactory, requiredCheckedValidator, resolveHidden, resolveOffset, resolveOrder, resolveSpan, slugify, stripMasksHook, syncWithServerMetadata, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, trim, uniqueAsyncValidator, urlValidator, withMessage };
7096
+ export { API_URL, AllowedFileTypes, ApiEndpoint, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentMetadataRegistry, ConnectionManagerService, DEFAULT_TABLE_CONFIG, DynamicFormService, DynamicGridPageComponent, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EmptyStateCardComponent, ErrorMessageService, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FormHooksRegistry, GLOBAL_CONFIG, GenericCrudService, GlobalConfigService, IconPickerService, IconPosition, IconSize, LocalConnectionStorage, LocalStorageCacheAdapter, LocalStorageConfigService, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, OverlayDeciderService, PraxisCore, PraxisIconDirective, PraxisIconPickerComponent, ResourceQuickConnectComponent, SCHEMA_VIEWER_CONTEXT, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SchemaMetadataClient, SchemaNormalizerService, SchemaViewerComponent, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryService, ValidationPattern, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildHeaders, buildPageKey, buildSchemaId, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getDefaultFormHints, getEssentialConfig, getReferencedFieldMetadata, getTextTransformer, isCssTextTransform, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, matchFieldValidator, maxFileSizeValidator, mergeFieldMetadata, mergeTableConfigs, minWordsValidator, normalizeFieldConstraints, normalizeFormConfig, normalizeFormMetadata, normalizePath, notifySuccessHook, prefillFromContextHook, provideDefaultFormHooks, provideFormHookPresets, provideFormHooks, provideGlobalConfig, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, provideRemoteGlobalConfig, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, removeDiacritics, reportTelemetryHookFactory, requiredCheckedValidator, resolveHidden, resolveOffset, resolveOrder, resolveSpan, slugify, stripMasksHook, syncWithServerMetadata, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, trim, uniqueAsyncValidator, urlValidator, withMessage };
6798
7097
  //# sourceMappingURL=praxisui-core.mjs.map