@elle96/ng-json-schema-manager 0.1.1 → 0.1.3

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 { Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, ViewEncapsulation, Component, NgModule } from '@angular/core';
2
+ import { Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, ViewEncapsulation, Component, PLATFORM_ID, Inject, NgModule } from '@angular/core';
3
3
  import * as i4 from '@angular/forms';
4
4
  import { FormGroup, FormArray, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
5
5
  import Ajv from 'ajv';
@@ -8,7 +8,7 @@ import Ajv2020 from 'ajv/dist/2020';
8
8
  import addFormats from 'ajv-formats';
9
9
  import AjvDraft04 from 'ajv-draft-04';
10
10
  import * as i3 from '@angular/common';
11
- import { CommonModule } from '@angular/common';
11
+ import { CommonModule, isPlatformBrowser } from '@angular/common';
12
12
 
13
13
  class JsonSchemaFormService {
14
14
  buildControl(schema, value, required = false) {
@@ -582,6 +582,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
582
582
  class JsonSchemaNodeComponent {
583
583
  schemaService;
584
584
  validation;
585
+ cdr;
585
586
  schema;
586
587
  control;
587
588
  label = '';
@@ -596,34 +597,38 @@ class JsonSchemaNodeComponent {
596
597
  selectedAnyOf = new Set();
597
598
  newPropertyKey = '';
598
599
  propertyError = '';
599
- constructor(schemaService, validation) {
600
+ valueSub;
601
+ constructor(schemaService, validation, cdr) {
600
602
  this.schemaService = schemaService;
601
603
  this.validation = validation;
604
+ this.cdr = cdr;
602
605
  }
603
606
  ngOnInit() {
604
- if (this.schema.anyOf?.length) {
607
+ if (this.schema.anyOf?.length)
605
608
  this.selectedAnyOf.add(0);
606
- }
609
+ // Subscribe to value changes to trigger CD for OnPush (needed for toggle visual state)
610
+ this.valueSub = this.control.valueChanges.subscribe(() => this.cdr.markForCheck());
611
+ }
612
+ ngOnDestroy() {
613
+ this.valueSub?.unsubscribe();
607
614
  }
608
- /**
609
- * Determina se un figlio di un object è "semplice" (primitive/enum/bool),
610
- * quindi può stare in griglia 2-col, oppure "complesso" (object/array/combinator)
611
- * che deve occupare la larghezza piena.
612
- */
613
615
  isSimpleChild(key) {
614
- const childSchema = this.schemaForKey(key);
615
- if (childSchema.oneOf?.length || childSchema.anyOf?.length || childSchema.allOf?.length)
616
+ const s = this.schemaForKey(key);
617
+ if (s.oneOf?.length || s.anyOf?.length || s.allOf?.length)
616
618
  return false;
617
- const t = this.schemaService.resolveType(childSchema);
619
+ const t = this.schemaService.resolveType(s);
618
620
  return t !== 'object' && t !== 'array';
619
621
  }
620
622
  badgeClass(badge) {
621
- return {
622
- 'bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400': badge === 'Deprecated',
623
- 'bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-400': badge === 'Read-only' || badge === 'Nullable',
624
- 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400': badge === 'Write-only',
625
- 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400': badge === 'Not constraint',
626
- };
623
+ if (badge === 'Deprecated')
624
+ return 'jsm-badge--deprecated';
625
+ if (badge === 'Read-only' || badge === 'Nullable')
626
+ return 'jsm-badge--readonly';
627
+ if (badge === 'Write-only')
628
+ return 'jsm-badge--writeonly';
629
+ if (badge === 'Not constraint')
630
+ return 'jsm-badge--not';
631
+ return 'jsm-badge--nullable';
627
632
  }
628
633
  get nodeKind() {
629
634
  if (this.schema.oneOf?.length)
@@ -632,10 +637,10 @@ class JsonSchemaNodeComponent {
632
637
  return 'anyOf';
633
638
  if (this.schema.allOf?.length)
634
639
  return 'allOf';
635
- const resolved = this.resolvedType;
636
- if (resolved === 'object')
640
+ const r = this.resolvedType;
641
+ if (r === 'object')
637
642
  return 'object';
638
- if (resolved === 'array')
643
+ if (r === 'array')
639
644
  return 'array';
640
645
  return 'primitive';
641
646
  }
@@ -671,12 +676,8 @@ class JsonSchemaNodeComponent {
671
676
  get controlAsArray() { return this.control; }
672
677
  get arrayControls() { return this.controlAsArray.controls; }
673
678
  get objectKeys() { return Object.keys(this.controlAsGroup.controls); }
674
- get simpleKeys() {
675
- return this.objectKeys.filter(k => this.isSimpleChild(k));
676
- }
677
- get complexKeys() {
678
- return this.objectKeys.filter(k => !this.isSimpleChild(k));
679
- }
679
+ get simpleKeys() { return this.objectKeys.filter(k => this.isSimpleChild(k)); }
680
+ get complexKeys() { return this.objectKeys.filter(k => !this.isSimpleChild(k)); }
680
681
  get canAddProperty() {
681
682
  if (!this.allowAdditionalProperties)
682
683
  return false;
@@ -684,25 +685,25 @@ class JsonSchemaNodeComponent {
684
685
  }
685
686
  get errorsForPath() { return this.errorsMap.get(this.path) ?? []; }
686
687
  get metaBadges() {
687
- const badges = [];
688
+ const b = [];
688
689
  if (this.effectiveSchema.deprecated)
689
- badges.push('Deprecated');
690
+ b.push('Deprecated');
690
691
  if (this.effectiveSchema.readOnly)
691
- badges.push('Read-only');
692
+ b.push('Read-only');
692
693
  if (this.effectiveSchema.writeOnly)
693
- badges.push('Write-only');
694
+ b.push('Write-only');
694
695
  if (this.effectiveSchema.nullable)
695
- badges.push('Nullable');
696
+ b.push('Nullable');
696
697
  if (this.effectiveSchema.not)
697
- badges.push('Not constraint');
698
- return badges;
698
+ b.push('Not constraint');
699
+ return b;
699
700
  }
700
701
  get activeVariant() {
701
702
  if (this.schema.oneOf?.length)
702
703
  return this.schema.oneOf[this.selectedOneOf] ?? null;
703
704
  if (this.schema.anyOf?.length) {
704
- const selections = Array.from(this.selectedAnyOf).map((i) => this.schema.anyOf[i]);
705
- return selections.length ? this.mergeSchemas(selections) : this.schema.anyOf[0] ?? null;
705
+ const sel = Array.from(this.selectedAnyOf).map(i => this.schema.anyOf[i]);
706
+ return sel.length ? this.mergeSchemas(sel) : this.schema.anyOf[0] ?? null;
706
707
  }
707
708
  if (this.schema.allOf?.length)
708
709
  return this.mergeSchemas([this.schema, ...this.schema.allOf]);
@@ -713,9 +714,7 @@ class JsonSchemaNodeComponent {
713
714
  }
714
715
  isRequired(key) { return this.effectiveSchema.required?.includes(key) ?? false; }
715
716
  schemaForKey(key) {
716
- return (this.effectiveSchema.properties?.[key] ??
717
- this.schemaService.resolveDynamicSchema(this.effectiveSchema, key) ??
718
- {});
717
+ return this.effectiveSchema.properties?.[key] ?? this.schemaService.resolveDynamicSchema(this.effectiveSchema, key) ?? {};
719
718
  }
720
719
  isDynamicKey(key) { return !this.effectiveSchema.properties?.[key]; }
721
720
  pathForChild(key) {
@@ -729,8 +728,7 @@ class JsonSchemaNodeComponent {
729
728
  return;
730
729
  }
731
730
  if (this.effectiveSchema.propertyNames) {
732
- const nameErrors = this.validation.validate(this.effectiveSchema.propertyNames, key);
733
- if (nameErrors.length) {
731
+ if (this.validation.validate(this.effectiveSchema.propertyNames, key).length) {
734
732
  this.propertyError = 'Property name does not match schema constraints.';
735
733
  return;
736
734
  }
@@ -739,20 +737,19 @@ class JsonSchemaNodeComponent {
739
737
  this.propertyError = 'Property already exists.';
740
738
  return;
741
739
  }
742
- const schemaForKey = this.schemaService.resolveDynamicSchema(this.effectiveSchema, key);
743
- if (!schemaForKey) {
740
+ const s = this.schemaService.resolveDynamicSchema(this.effectiveSchema, key);
741
+ if (!s) {
744
742
  this.propertyError = 'This property is not allowed by the schema.';
745
743
  return;
746
744
  }
747
- const control = this.schemaService.buildControl(schemaForKey, undefined, false);
748
- this.controlAsGroup.addControl(key, control);
745
+ this.controlAsGroup.addControl(key, this.schemaService.buildControl(s, undefined, false));
749
746
  this.newPropertyKey = '';
750
747
  }
751
748
  removeDynamicKey(key) { this.controlAsGroup.removeControl(key); }
752
749
  schemaForIndex(index) {
753
- const prefixItems = this.effectiveSchema.prefixItems ?? [];
754
- if (prefixItems[index])
755
- return prefixItems[index];
750
+ const p = this.effectiveSchema.prefixItems ?? [];
751
+ if (p[index])
752
+ return p[index];
756
753
  if (this.effectiveSchema.items && this.effectiveSchema.items !== true)
757
754
  return this.effectiveSchema.items;
758
755
  if (this.effectiveSchema.additionalItems && this.effectiveSchema.additionalItems !== true)
@@ -762,17 +759,14 @@ class JsonSchemaNodeComponent {
762
759
  return {};
763
760
  }
764
761
  get canAddArrayItem() {
765
- return !(this.effectiveSchema.items === false ||
766
- this.effectiveSchema.additionalItems === false ||
767
- this.effectiveSchema.unevaluatedItems === false);
762
+ return !(this.effectiveSchema.items === false || this.effectiveSchema.additionalItems === false || this.effectiveSchema.unevaluatedItems === false);
768
763
  }
769
764
  addArrayItem() {
770
765
  if (!this.canAddArrayItem)
771
766
  return;
772
- const formArray = this.controlAsArray;
773
- const index = formArray.length;
774
- formArray.push(this.schemaService.buildControl(this.schemaForIndex(index), undefined, false));
775
- formArray.markAsDirty();
767
+ const fa = this.controlAsArray;
768
+ fa.push(this.schemaService.buildControl(this.schemaForIndex(fa.length), undefined, false));
769
+ fa.markAsDirty();
776
770
  }
777
771
  removeArrayItem(index) {
778
772
  this.controlAsArray.removeAt(index);
@@ -788,9 +782,9 @@ class JsonSchemaNodeComponent {
788
782
  this.selectedAnyOf.delete(index);
789
783
  else
790
784
  this.selectedAnyOf.add(index);
791
- const variant = this.activeVariant;
792
- if (variant)
793
- this.replaceControl(this.schemaService.buildControl(variant, this.control.value, this.required));
785
+ const v = this.activeVariant;
786
+ if (v)
787
+ this.replaceControl(this.schemaService.buildControl(v, this.control.value, this.required));
794
788
  }
795
789
  replaceControl(control) {
796
790
  if (this.parent) {
@@ -806,12 +800,12 @@ class JsonSchemaNodeComponent {
806
800
  }
807
801
  childControl(key) { return this.controlAsGroup.get(key); }
808
802
  get effectiveSchema() {
809
- let schema = this.schema;
810
- if (schema.allOf?.length)
811
- schema = this.mergeSchemas([schema, ...schema.allOf]);
812
- schema = this.applyConditional(schema);
813
- schema = this.applyDependencies(schema);
814
- return schema;
803
+ let s = this.schema;
804
+ if (s.allOf?.length)
805
+ s = this.mergeSchemas([s, ...s.allOf]);
806
+ s = this.applyConditional(s);
807
+ s = this.applyDependencies(s);
808
+ return s;
815
809
  }
816
810
  applyConditional(schema) {
817
811
  if (!schema.if)
@@ -827,80 +821,67 @@ class JsonSchemaNodeComponent {
827
821
  if (!value || typeof value !== 'object' || Array.isArray(value))
828
822
  return schema;
829
823
  let merged = { ...schema };
830
- const valueKeys = Object.keys(value);
824
+ const keys = Object.keys(value);
831
825
  if (schema.dependentRequired) {
832
- for (const key of valueKeys) {
833
- const required = schema.dependentRequired[key];
834
- if (required?.length)
835
- merged = this.mergeSchemas([merged, { required }]);
826
+ for (const k of keys) {
827
+ const r = schema.dependentRequired[k];
828
+ if (r?.length)
829
+ merged = this.mergeSchemas([merged, { required: r }]);
836
830
  }
837
831
  }
838
832
  if (schema.dependentSchemas) {
839
- for (const key of valueKeys) {
840
- const depSchema = schema.dependentSchemas[key];
841
- if (depSchema)
842
- merged = this.mergeSchemas([merged, depSchema]);
833
+ for (const k of keys) {
834
+ const d = schema.dependentSchemas[k];
835
+ if (d)
836
+ merged = this.mergeSchemas([merged, d]);
843
837
  }
844
838
  }
845
839
  if (schema.dependencies) {
846
- for (const key of valueKeys) {
847
- const dep = schema.dependencies[key];
848
- if (Array.isArray(dep))
849
- merged = this.mergeSchemas([merged, { required: dep }]);
850
- else if (dep)
851
- merged = this.mergeSchemas([merged, dep]);
840
+ for (const k of keys) {
841
+ const d = schema.dependencies[k];
842
+ if (Array.isArray(d))
843
+ merged = this.mergeSchemas([merged, { required: d }]);
844
+ else if (d)
845
+ merged = this.mergeSchemas([merged, d]);
852
846
  }
853
847
  }
854
848
  return merged;
855
849
  }
856
850
  mergeSchemas(schemas) {
857
- return schemas.reduce((acc, schema) => {
858
- const merged = {
859
- ...acc,
860
- ...schema,
861
- properties: { ...(acc.properties ?? {}), ...(schema.properties ?? {}) },
862
- patternProperties: { ...(acc.patternProperties ?? {}), ...(schema.patternProperties ?? {}) },
863
- required: Array.from(new Set([...(acc.required ?? []), ...(schema.required ?? [])])),
864
- };
865
- if (acc.type && schema.type) {
866
- const accTypes = Array.isArray(acc.type) ? acc.type : [acc.type];
867
- const schemaTypes = Array.isArray(schema.type) ? schema.type : [schema.type];
868
- merged.type = Array.from(new Set([...accTypes, ...schemaTypes]));
869
- }
870
- return merged;
871
- }, {});
851
+ return schemas.reduce((acc, s) => ({
852
+ ...acc, ...s,
853
+ properties: { ...(acc.properties ?? {}), ...(s.properties ?? {}) },
854
+ patternProperties: { ...(acc.patternProperties ?? {}), ...(s.patternProperties ?? {}) },
855
+ required: Array.from(new Set([...(acc.required ?? []), ...(s.required ?? [])])),
856
+ ...(acc.type && s.type ? {
857
+ type: Array.from(new Set([
858
+ ...(Array.isArray(acc.type) ? acc.type : [acc.type]),
859
+ ...(Array.isArray(s.type) ? s.type : [s.type]),
860
+ ]))
861
+ } : {}),
862
+ }), {});
872
863
  }
873
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: JsonSchemaNodeComponent, deps: [{ token: JsonSchemaFormService }, { token: JsonSchemaValidationService }], target: i0.ɵɵFactoryTarget.Component });
864
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: JsonSchemaNodeComponent, deps: [{ token: JsonSchemaFormService }, { token: JsonSchemaValidationService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
874
865
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: JsonSchemaNodeComponent, isStandalone: true, selector: "jsm-schema-node", inputs: { schema: "schema", control: "control", label: "label", required: "required", path: "path", errorsMap: "errorsMap", parent: "parent", controlKey: "controlKey", allowAdditionalProperties: "allowAdditionalProperties" }, outputs: { controlReplaced: "controlReplaced" }, ngImport: i0, template: `
875
866
  <ng-container [ngSwitch]="nodeKind">
876
867
 
877
- <!-- ═══════════════════════════════════════════════════════════
878
- oneOf — tab bar: ogni opzione è una tab, nessuna icona
879
- ═══════════════════════════════════════════════════════════ -->
880
- <div *ngSwitchCase="'oneOf'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
881
- <div class="flex items-start justify-between px-4 py-3 bg-slate-50 dark:bg-slate-800/60 border-b border-slate-200 dark:border-slate-700">
868
+ <!-- ═══ oneOf ═══ -->
869
+ <div *ngSwitchCase="'oneOf'" class="jsm-combinator">
870
+ <div class="jsm-combinator-header">
882
871
  <div>
883
- <p class="text-sm font-semibold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</p>
884
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">{{ schema.description }}</p>
872
+ <p class="jsm-combinator-title">{{ schema.title || label }}</p>
873
+ <p *ngIf="schema.description" class="jsm-combinator-hint">{{ schema.description }}</p>
885
874
  </div>
886
- <span class="ml-3 shrink-0 rounded-full bg-blue-100 dark:bg-blue-900/40 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-blue-700 dark:text-blue-400">oneOf</span>
875
+ <span class="jsm-combinator-badge jsm-badge--oneof">oneOf</span>
887
876
  </div>
888
- <div class="flex overflow-x-auto border-b border-slate-200 dark:border-slate-700">
889
- <button
890
- *ngFor="let option of schema.oneOf; let i = index"
891
- type="button"
892
- class="px-4 py-2.5 text-xs font-medium whitespace-nowrap border-b-2 transition-colors"
893
- [class.border-indigo-500]="selectedOneOf === i"
894
- [class.text-indigo-600]="selectedOneOf === i"
895
- [class.dark:text-indigo-400]="selectedOneOf === i"
896
- [class.font-semibold]="selectedOneOf === i"
897
- [class.border-transparent]="selectedOneOf !== i"
898
- [class.text-slate-500]="selectedOneOf !== i"
899
- [class.dark:text-slate-400]="selectedOneOf !== i"
900
- (click)="selectedOneOf = i; onOneOfChange()"
901
- >{{ option.title || ('Option ' + (i + 1)) }}</button>
877
+ <div class="jsm-oneof-tabs">
878
+ <button *ngFor="let option of schema.oneOf; let i = index" type="button"
879
+ class="jsm-tab" [class.jsm-tab--active]="selectedOneOf === i"
880
+ (click)="selectedOneOf = i; onOneOfChange()">
881
+ {{ option.title || ('Option ' + (i + 1)) }}
882
+ </button>
902
883
  </div>
903
- <div class="p-4" *ngIf="activeVariant">
884
+ <div class="jsm-combinator-body" *ngIf="activeVariant">
904
885
  <jsm-schema-node [schema]="activeVariant" [control]="control" [parent]="parent" [controlKey]="controlKey"
905
886
  [path]="path" [errorsMap]="errorsMap" [label]="label" [required]="required"
906
887
  [allowAdditionalProperties]="allowAdditionalProperties"
@@ -908,36 +889,23 @@ class JsonSchemaNodeComponent {
908
889
  </div>
909
890
  </div>
910
891
 
911
- <!-- ═══════════════════════════════════════════════════════════
912
- anyOf — pill toggle, nessuna icona
913
- ═══════════════════════════════════════════════════════════ -->
914
- <div *ngSwitchCase="'anyOf'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
915
- <div class="flex items-start justify-between px-4 py-3 bg-slate-50 dark:bg-slate-800/60 border-b border-slate-200 dark:border-slate-700">
892
+ <!-- ═══ anyOf ═══ -->
893
+ <div *ngSwitchCase="'anyOf'" class="jsm-combinator">
894
+ <div class="jsm-combinator-header">
916
895
  <div>
917
- <p class="text-sm font-semibold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</p>
918
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">{{ schema.description }}</p>
896
+ <p class="jsm-combinator-title">{{ schema.title || label }}</p>
897
+ <p *ngIf="schema.description" class="jsm-combinator-hint">{{ schema.description }}</p>
919
898
  </div>
920
- <span class="ml-3 shrink-0 rounded-full bg-violet-100 dark:bg-violet-900/40 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-violet-700 dark:text-violet-400">anyOf</span>
899
+ <span class="jsm-combinator-badge jsm-badge--anyof">anyOf</span>
921
900
  </div>
922
- <div class="flex flex-wrap gap-2 px-4 py-3 border-b border-slate-200 dark:border-slate-700">
923
- <label
924
- *ngFor="let option of schema.anyOf; let i = index"
925
- class="inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium cursor-pointer transition-colors select-none"
926
- [class.border-indigo-400]="selectedAnyOf.has(i)"
927
- [class.bg-indigo-50]="selectedAnyOf.has(i)"
928
- [class.dark:bg-indigo-900/30]="selectedAnyOf.has(i)"
929
- [class.text-indigo-700]="selectedAnyOf.has(i)"
930
- [class.dark:text-indigo-400]="selectedAnyOf.has(i)"
931
- [class.border-slate-200]="!selectedAnyOf.has(i)"
932
- [class.dark:border-slate-600]="!selectedAnyOf.has(i)"
933
- [class.text-slate-600]="!selectedAnyOf.has(i)"
934
- [class.dark:text-slate-400]="!selectedAnyOf.has(i)"
935
- >
936
- <input type="checkbox" class="h-3.5 w-3.5 accent-indigo-600" [checked]="selectedAnyOf.has(i)" (change)="toggleAnyOf(i)" />
901
+ <div class="jsm-anyof-options">
902
+ <label *ngFor="let option of schema.anyOf; let i = index"
903
+ class="jsm-anyof-option" [class.jsm-anyof-option--active]="selectedAnyOf.has(i)">
904
+ <input type="checkbox" class="sr-only" [checked]="selectedAnyOf.has(i)" (change)="toggleAnyOf(i)" />
937
905
  {{ option.title || ('Option ' + (i + 1)) }}
938
906
  </label>
939
907
  </div>
940
- <div class="p-4" *ngIf="activeVariant">
908
+ <div class="jsm-combinator-body" *ngIf="activeVariant">
941
909
  <jsm-schema-node [schema]="activeVariant" [control]="control" [parent]="parent" [controlKey]="controlKey"
942
910
  [path]="path" [errorsMap]="errorsMap" [label]="label" [required]="required"
943
911
  [allowAdditionalProperties]="allowAdditionalProperties"
@@ -945,19 +913,17 @@ class JsonSchemaNodeComponent {
945
913
  </div>
946
914
  </div>
947
915
 
948
- <!-- ═══════════════════════════════════════════════════════════
949
- allOf — sezioni in colonna con bordo sinistro
950
- ═══════════════════════════════════════════════════════════ -->
951
- <div *ngSwitchCase="'allOf'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
952
- <div class="flex items-start justify-between px-4 py-3 bg-slate-50 dark:bg-slate-800/60 border-b border-slate-200 dark:border-slate-700">
916
+ <!-- ═══ allOf ═══ -->
917
+ <div *ngSwitchCase="'allOf'" class="jsm-combinator">
918
+ <div class="jsm-combinator-header">
953
919
  <div>
954
- <p class="text-sm font-semibold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</p>
955
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">{{ schema.description }}</p>
920
+ <p class="jsm-combinator-title">{{ schema.title || label }}</p>
921
+ <p *ngIf="schema.description" class="jsm-combinator-hint">{{ schema.description }}</p>
956
922
  </div>
957
- <span class="ml-3 shrink-0 rounded-full bg-teal-100 dark:bg-teal-900/40 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-teal-700 dark:text-teal-400">allOf</span>
923
+ <span class="jsm-combinator-badge jsm-badge--allof">allOf</span>
958
924
  </div>
959
- <div class="p-4 space-y-4">
960
- <div *ngFor="let subSchema of mergedAllOf; let i = index" class="border-l-2 border-indigo-200 dark:border-indigo-700 pl-4">
925
+ <div class="jsm-combinator-body">
926
+ <div *ngFor="let subSchema of mergedAllOf; let i = index" class="jsm-allof-section">
961
927
  <jsm-schema-node [schema]="subSchema" [control]="control" [parent]="parent" [controlKey]="controlKey"
962
928
  [path]="path" [errorsMap]="errorsMap" [label]="subSchema.title || ('Section ' + (i + 1))" [required]="required"
963
929
  [allowAdditionalProperties]="allowAdditionalProperties"
@@ -966,145 +932,95 @@ class JsonSchemaNodeComponent {
966
932
  </div>
967
933
  </div>
968
934
 
969
- <!-- ═══════════════════════════════════════════════════════════
970
- object — fieldset con griglia SOLO per campi primitivi.
971
- Logica layout:
972
- - I figli vengono classificati come "semplici" (primitive/enum/bool)
973
- o "complessi" (object/array/oneOf/anyOf/allOf).
974
- - I semplici vanno in griglia 2 colonne (sm:grid-cols-2).
975
- - I complessi vanno sempre in colonna piena (col-span-2).
976
- ═══════════════════════════════════════════════════════════ -->
977
- <fieldset *ngSwitchCase="'object'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm p-4 transition-shadow focus-within:border-indigo-300 dark:focus-within:border-indigo-600 focus-within:ring-2 focus-within:ring-indigo-100 dark:focus-within:ring-indigo-900/40">
978
- <legend class="px-2 text-sm font-bold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</legend>
935
+ <!-- ═══ object ═══ -->
936
+ <fieldset *ngSwitchCase="'object'" class="jsm-fieldset">
937
+ <legend class="jsm-legend">{{ schema.title || label }}</legend>
938
+ <p *ngIf="schema.description" class="jsm-field-hint" style="margin-bottom:.75rem;margin-top:.25rem;">{{ schema.description }}</p>
979
939
 
980
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 mb-3 mt-1">{{ schema.description }}</p>
981
-
982
- <div *ngIf="metaBadges.length" class="flex flex-wrap gap-1.5 mb-3">
983
- <span *ngFor="let badge of metaBadges"
984
- class="rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide"
985
- [ngClass]="badgeClass(badge)">{{ badge }}</span>
940
+ <div *ngIf="metaBadges.length" class="jsm-badge-row" style="margin-bottom:.75rem;">
941
+ <span *ngFor="let badge of metaBadges" class="jsm-badge" [ngClass]="badgeClass(badge)">{{ badge }}</span>
986
942
  </div>
987
943
 
988
- <!-- campi semplici (string/number/bool/enum) → griglia 2 colonne -->
989
- <div class="grid grid-cols-1 sm:grid-cols-2 gap-4" [formGroup]="controlAsGroup">
944
+ <div class="jsm-obj-grid" [formGroup]="controlAsGroup">
990
945
  <ng-container *ngFor="let key of simpleKeys">
991
- <div class="flex flex-col gap-1">
992
- <div *ngIf="isDynamicKey(key)"
993
- class="flex items-center justify-between rounded-md border border-dashed border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800/50 px-2 py-1 mb-1">
994
- <span class="font-mono text-xs text-slate-500 dark:text-slate-400">{{ key }}</span>
995
- <button type="button"
996
- class="rounded px-1.5 py-0.5 text-xs font-semibold text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
997
- (click)="removeDynamicKey(key)">Remove</button>
946
+ <div style="display:flex;flex-direction:column;gap:.25rem;">
947
+ <div *ngIf="isDynamicKey(key)" class="jsm-dynamic-key-header">
948
+ <span class="jsm-dynamic-key-label">{{ key }}</span>
949
+ <button type="button" class="jsm-btn-danger" (click)="removeDynamicKey(key)">Remove</button>
998
950
  </div>
999
- <jsm-schema-node
1000
- [schema]="schemaForKey(key)"
1001
- [control]="childControl(key)"
1002
- [parent]="controlAsGroup"
1003
- [controlKey]="key"
1004
- [path]="pathForChild(key)"
1005
- [errorsMap]="errorsMap"
1006
- [label]="key"
1007
- [required]="isRequired(key)"
1008
- [allowAdditionalProperties]="allowAdditionalProperties"
1009
- (controlReplaced)="controlReplaced.emit($event)">
1010
- </jsm-schema-node>
951
+ <jsm-schema-node [schema]="schemaForKey(key)" [control]="childControl(key)" [parent]="controlAsGroup"
952
+ [controlKey]="key" [path]="pathForChild(key)" [errorsMap]="errorsMap" [label]="key"
953
+ [required]="isRequired(key)" [allowAdditionalProperties]="allowAdditionalProperties"
954
+ (controlReplaced)="controlReplaced.emit($event)"></jsm-schema-node>
1011
955
  </div>
1012
956
  </ng-container>
1013
957
  </div>
1014
958
 
1015
- <!-- campi complessi (object/array/combinator) → colonna piena -->
1016
- <div class="space-y-4 mt-4" *ngIf="complexKeys.length > 0" [formGroup]="controlAsGroup">
959
+ <div class="jsm-obj-complex" *ngIf="complexKeys.length > 0" [formGroup]="controlAsGroup">
1017
960
  <ng-container *ngFor="let key of complexKeys">
1018
- <div class="flex flex-col gap-1">
1019
- <div *ngIf="isDynamicKey(key)"
1020
- class="flex items-center justify-between rounded-md border border-dashed border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800/50 px-2 py-1 mb-1">
1021
- <span class="font-mono text-xs text-slate-500 dark:text-slate-400">{{ key }}</span>
1022
- <button type="button"
1023
- class="rounded px-1.5 py-0.5 text-xs font-semibold text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
1024
- (click)="removeDynamicKey(key)">Remove</button>
961
+ <div style="display:flex;flex-direction:column;gap:.25rem;">
962
+ <div *ngIf="isDynamicKey(key)" class="jsm-dynamic-key-header">
963
+ <span class="jsm-dynamic-key-label">{{ key }}</span>
964
+ <button type="button" class="jsm-btn-danger" (click)="removeDynamicKey(key)">Remove</button>
1025
965
  </div>
1026
- <jsm-schema-node
1027
- [schema]="schemaForKey(key)"
1028
- [control]="childControl(key)"
1029
- [parent]="controlAsGroup"
1030
- [controlKey]="key"
1031
- [path]="pathForChild(key)"
1032
- [errorsMap]="errorsMap"
1033
- [label]="key"
1034
- [required]="isRequired(key)"
1035
- [allowAdditionalProperties]="allowAdditionalProperties"
1036
- (controlReplaced)="controlReplaced.emit($event)">
1037
- </jsm-schema-node>
966
+ <jsm-schema-node [schema]="schemaForKey(key)" [control]="childControl(key)" [parent]="controlAsGroup"
967
+ [controlKey]="key" [path]="pathForChild(key)" [errorsMap]="errorsMap" [label]="key"
968
+ [required]="isRequired(key)" [allowAdditionalProperties]="allowAdditionalProperties"
969
+ (controlReplaced)="controlReplaced.emit($event)"></jsm-schema-node>
1038
970
  </div>
1039
971
  </ng-container>
1040
972
  </div>
1041
973
 
1042
- <!-- add dynamic property -->
1043
- <div *ngIf="canAddProperty" class="mt-4 rounded-lg border border-dashed border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800/40 p-3 space-y-2">
1044
- <p class="text-[10px] font-bold uppercase tracking-widest text-slate-400 dark:text-slate-500">Add property</p>
1045
- <div class="flex gap-2">
1046
- <input
1047
- class="flex-1 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 placeholder:text-slate-400 dark:placeholder:text-slate-500 px-3 py-1.5 text-sm shadow-sm outline-none transition hover:border-slate-400 dark:hover:border-slate-500 focus:border-indigo-400 focus:ring-2 focus:ring-indigo-100 dark:focus:ring-indigo-900/40"
1048
- [(ngModel)]="newPropertyKey" placeholder="propertyName" />
1049
- <button type="button"
1050
- class="rounded-lg bg-indigo-600 px-3 py-1.5 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700 active:scale-95"
1051
- (click)="addDynamicProperty()">Add</button>
974
+ <div *ngIf="canAddProperty" class="jsm-add-prop">
975
+ <p class="jsm-add-prop-label">Add property</p>
976
+ <div class="jsm-add-prop-row">
977
+ <input class="jsm-input jsm-input--sm" style="flex:1;" [(ngModel)]="newPropertyKey" placeholder="propertyName" />
978
+ <button type="button" class="jsm-btn-primary" (click)="addDynamicProperty()">Add</button>
1052
979
  </div>
1053
- <p *ngIf="propertyError" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1054
- <!-- icona errore: unico caso in cui ha senso, segnala un problema -->
1055
- <svg class="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
980
+ <p *ngIf="propertyError" class="jsm-error-msg">
981
+ <svg class="jsm-error-icon" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1056
982
  {{ propertyError }}
1057
983
  </p>
1058
984
  </div>
1059
985
 
1060
- <div *ngIf="errorsForPath.length" class="mt-3 space-y-1">
1061
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1062
- <svg class="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
986
+ <div *ngIf="errorsForPath.length" class="jsm-error-list" style="margin-top:.75rem;">
987
+ <p *ngFor="let error of errorsForPath" class="jsm-error-msg">
988
+ <svg class="jsm-error-icon" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1063
989
  {{ error }}
1064
990
  </p>
1065
991
  </div>
1066
992
  </fieldset>
1067
993
 
1068
- <!-- ═══════════════════════════════════════════════════════════
1069
- array — sempre in colonna piena, items in colonna
1070
- ═══════════════════════════════════════════════════════════ -->
1071
- <div *ngSwitchCase="'array'" class="space-y-3">
1072
- <div class="flex items-start justify-between gap-2">
994
+ <!-- ═══ array ═══ -->
995
+ <div *ngSwitchCase="'array'" class="jsm-array-block">
996
+ <div class="jsm-array-header">
1073
997
  <div>
1074
- <p class="text-sm font-semibold text-slate-800">
1075
- {{ schema.title || label }}<span *ngIf="required" class="ml-0.5 text-red-500">*</span>
998
+ <p class="jsm-array-title">
999
+ {{ schema.title || label }}<span *ngIf="required" style="color:#ef4444;margin-left:.125rem;">*</span>
1076
1000
  </p>
1077
- <p *ngIf="schema.description" class="text-xs text-slate-500 mt-0.5">{{ schema.description }}</p>
1001
+ <p *ngIf="schema.description" class="jsm-field-hint">{{ schema.description }}</p>
1078
1002
  </div>
1079
- <!-- icona + nel bottone: indica l'azione "aggiungi", non decorativa -->
1080
- <button type="button"
1081
- class="shrink-0 flex items-center gap-1 rounded-lg bg-indigo-600 px-3 py-1.5 text-xs font-semibold text-white shadow-sm transition hover:bg-indigo-700 active:scale-95 disabled:opacity-40 disabled:cursor-not-allowed"
1082
- (click)="addArrayItem()" [disabled]="!canAddArrayItem">
1083
- <svg class="w-3.5 h-3.5" viewBox="0 0 16 16" fill="none"><path d="M8 3v10M3 8h10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1003
+ <button type="button" class="jsm-btn-primary" (click)="addArrayItem()" [disabled]="!canAddArrayItem">
1004
+ <svg style="width:.875rem;height:.875rem;flex-shrink:0;" viewBox="0 0 16 16" fill="none"><path d="M8 3v10M3 8h10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1084
1005
  Add item
1085
1006
  </button>
1086
1007
  </div>
1087
1008
 
1088
- <div *ngIf="effectiveSchema.contains" class="rounded-md bg-indigo-50 dark:bg-indigo-900/30 px-3 py-1.5 text-xs text-indigo-700 dark:text-indigo-400">
1009
+ <div *ngIf="effectiveSchema.contains" class="jsm-contains-hint">
1089
1010
  Must contain matching item(s)
1090
1011
  <span *ngIf="effectiveSchema.minContains"> · min {{ effectiveSchema.minContains }}</span>
1091
1012
  <span *ngIf="effectiveSchema.maxContains"> · max {{ effectiveSchema.maxContains }}</span>
1092
1013
  </div>
1093
1014
 
1094
- <div *ngIf="metaBadges.length" class="flex flex-wrap gap-1.5">
1095
- <span *ngFor="let badge of metaBadges"
1096
- class="rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide"
1097
- [ngClass]="badgeClass(badge)">{{ badge }}</span>
1015
+ <div *ngIf="metaBadges.length" class="jsm-badge-row">
1016
+ <span *ngFor="let badge of metaBadges" class="jsm-badge" [ngClass]="badgeClass(badge)">{{ badge }}</span>
1098
1017
  </div>
1099
1018
 
1100
- <div class="space-y-2" *ngIf="arrayControls.length > 0; else emptyArray">
1101
- <div *ngFor="let item of arrayControls; let i = index"
1102
- class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-3 shadow-sm transition focus-within:border-indigo-300 dark:focus-within:border-indigo-600 focus-within:ring-2 focus-within:ring-indigo-100 dark:focus-within:ring-indigo-900/40">
1103
- <div class="flex items-center justify-between mb-3">
1104
- <span class="inline-flex h-5 w-5 items-center justify-center rounded-full bg-indigo-100 dark:bg-indigo-900/50 text-[11px] font-bold text-indigo-600 dark:text-indigo-400">{{ i + 1 }}</span>
1105
- <button type="button"
1106
- class="rounded-md border border-red-200 dark:border-red-800 px-2 py-0.5 text-xs font-semibold text-red-600 dark:text-red-400 transition hover:bg-red-50 dark:hover:bg-red-900/20"
1107
- (click)="removeArrayItem(i)">Remove</button>
1019
+ <div class="jsm-array-items" *ngIf="arrayControls.length > 0; else emptyArray">
1020
+ <div *ngFor="let item of arrayControls; let i = index" class="jsm-array-item">
1021
+ <div class="jsm-array-item-header">
1022
+ <span class="jsm-array-index">{{ i + 1 }}</span>
1023
+ <button type="button" class="jsm-btn-danger" (click)="removeArrayItem(i)">Remove</button>
1108
1024
  </div>
1109
1025
  <jsm-schema-node [schema]="schemaForIndex(i)" [control]="item" [parent]="controlAsArray"
1110
1026
  [controlKey]="i" [path]="pathForChild(i)" [errorsMap]="errorsMap" [label]="label + ' item'"
@@ -1114,124 +1030,72 @@ class JsonSchemaNodeComponent {
1114
1030
  </div>
1115
1031
 
1116
1032
  <ng-template #emptyArray>
1117
- <div class="flex flex-col items-center gap-2 rounded-xl border border-dashed border-slate-200 dark:border-slate-700 py-8 text-slate-400 dark:text-slate-500 text-xs">
1118
- No items yet — click "Add item" to start.
1119
- </div>
1033
+ <div class="jsm-empty-array">No items yet — click "Add item" to start.</div>
1120
1034
  </ng-template>
1121
1035
 
1122
- <div *ngIf="errorsForPath.length" class="space-y-1">
1123
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1124
- <svg class="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1036
+ <div *ngIf="errorsForPath.length" class="jsm-error-list">
1037
+ <p *ngFor="let error of errorsForPath" class="jsm-error-msg">
1038
+ <svg class="jsm-error-icon" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1125
1039
  {{ error }}
1126
1040
  </p>
1127
1041
  </div>
1128
1042
  </div>
1129
1043
 
1130
- <!-- ═══════════════════════════════════════════════════════════
1131
- primitive — sempre in colonna, nessuna icona decorativa.
1132
- Icona errore solo nei messaggi di validazione.
1133
- Icona chevron nel select: funzionale (indica dropdown).
1134
- ═══════════════════════════════════════════════════════════ -->
1135
- <div *ngSwitchDefault class="space-y-1.5">
1044
+ <!-- ═══ primitive ═══ -->
1045
+ <div *ngSwitchDefault class="jsm-field">
1136
1046
  <ng-container *ngIf="inputKind !== 'checkbox'">
1137
- <label class="block text-[13px] font-semibold text-slate-700 dark:text-slate-300">
1138
- {{ schema.title || label }}<span *ngIf="required" class="ml-0.5 text-red-500">*</span>
1047
+ <label class="jsm-field-label">
1048
+ {{ schema.title || label }}<span *ngIf="required" class="jsm-required">*</span>
1139
1049
  </label>
1140
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 -mt-0.5">{{ schema.description }}</p>
1050
+ <p *ngIf="schema.description" class="jsm-field-hint">{{ schema.description }}</p>
1141
1051
  </ng-container>
1142
1052
 
1143
- <div *ngIf="metaBadges.length" class="flex flex-wrap gap-1.5">
1144
- <span *ngFor="let badge of metaBadges"
1145
- class="rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide"
1146
- [ngClass]="badgeClass(badge)">{{ badge }}</span>
1053
+ <div *ngIf="metaBadges.length" class="jsm-badge-row">
1054
+ <span *ngFor="let badge of metaBadges" class="jsm-badge" [ngClass]="badgeClass(badge)">{{ badge }}</span>
1147
1055
  </div>
1148
1056
 
1149
1057
  <ng-container [ngSwitch]="inputKind">
1058
+ <input *ngSwitchCase="'text'" class="jsm-input"
1059
+ [class.jsm-input--error]="errorsForPath.length && controlAsFormControl.dirty"
1060
+ [attr.type]="inputType" [attr.placeholder]="schema.title || label"
1061
+ [formControl]="controlAsFormControl" />
1150
1062
 
1151
- <input *ngSwitchCase="'text'"
1152
- class="w-full rounded-lg border bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 placeholder:text-slate-400 dark:placeholder:text-slate-500 px-3 py-2 text-sm shadow-sm outline-none transition hover:border-slate-400 dark:hover:border-slate-500 focus:ring-2"
1153
- [class.border-slate-300]="!(errorsForPath.length && controlAsFormControl.dirty)"
1154
- [class.dark:border-slate-600]="!(errorsForPath.length && controlAsFormControl.dirty)"
1155
- [class.focus:border-indigo-400]="!(errorsForPath.length && controlAsFormControl.dirty)"
1156
- [class.focus:ring-indigo-100]="!(errorsForPath.length && controlAsFormControl.dirty)"
1157
- [class.dark:focus:ring-indigo-900/40]="!(errorsForPath.length && controlAsFormControl.dirty)"
1158
- [class.border-red-300]="errorsForPath.length && controlAsFormControl.dirty"
1159
- [class.dark:border-red-700]="errorsForPath.length && controlAsFormControl.dirty"
1160
- [class.bg-red-50]="errorsForPath.length && controlAsFormControl.dirty"
1161
- [class.dark:bg-red-900/20]="errorsForPath.length && controlAsFormControl.dirty"
1162
- [class.focus:border-red-400]="errorsForPath.length && controlAsFormControl.dirty"
1163
- [class.focus:ring-red-100]="errorsForPath.length && controlAsFormControl.dirty"
1164
- [attr.type]="inputType"
1165
- [attr.placeholder]="schema.title || label"
1166
- [formControl]="controlAsFormControl"
1167
- />
1168
-
1169
- <textarea *ngSwitchCase="'textarea'" rows="4"
1170
- class="w-full resize-y rounded-lg border bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 placeholder:text-slate-400 dark:placeholder:text-slate-500 px-3 py-2 text-sm shadow-sm outline-none transition hover:border-slate-400 dark:hover:border-slate-500 focus:ring-2"
1171
- [class.border-slate-300]="!(errorsForPath.length && controlAsFormControl.dirty)"
1172
- [class.dark:border-slate-600]="!(errorsForPath.length && controlAsFormControl.dirty)"
1173
- [class.focus:border-indigo-400]="!(errorsForPath.length && controlAsFormControl.dirty)"
1174
- [class.focus:ring-indigo-100]="!(errorsForPath.length && controlAsFormControl.dirty)"
1175
- [class.dark:focus:ring-indigo-900/40]="!(errorsForPath.length && controlAsFormControl.dirty)"
1176
- [class.border-red-300]="errorsForPath.length && controlAsFormControl.dirty"
1177
- [class.dark:border-red-700]="errorsForPath.length && controlAsFormControl.dirty"
1178
- [class.bg-red-50]="errorsForPath.length && controlAsFormControl.dirty"
1179
- [class.dark:bg-red-900/20]="errorsForPath.length && controlAsFormControl.dirty"
1180
- [class.focus:border-red-400]="errorsForPath.length && controlAsFormControl.dirty"
1181
- [class.focus:ring-red-100]="errorsForPath.length && controlAsFormControl.dirty"
1063
+ <textarea *ngSwitchCase="'textarea'" rows="4" class="jsm-textarea"
1064
+ [class.jsm-textarea--error]="errorsForPath.length && controlAsFormControl.dirty"
1182
1065
  [attr.placeholder]="schema.title || label"
1183
- [formControl]="controlAsFormControl"
1184
- ></textarea>
1066
+ [formControl]="controlAsFormControl"></textarea>
1185
1067
 
1186
- <!-- select: il chevron SVG è funzionale, indica che è un dropdown -->
1187
- <div *ngSwitchCase="'select'" class="relative">
1188
- <select
1189
- class="w-full appearance-none rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 px-3 py-2 pr-8 text-sm shadow-sm outline-none transition hover:border-slate-400 dark:hover:border-slate-500 focus:border-indigo-400 focus:ring-2 focus:ring-indigo-100 dark:focus:ring-indigo-900/40"
1190
- [class.border-red-300]="errorsForPath.length && controlAsFormControl.dirty"
1191
- [class.dark:border-red-700]="errorsForPath.length && controlAsFormControl.dirty"
1192
- [class.bg-red-50]="errorsForPath.length && controlAsFormControl.dirty"
1193
- [class.dark:bg-red-900/20]="errorsForPath.length && controlAsFormControl.dirty"
1068
+ <div *ngSwitchCase="'select'" class="jsm-select-wrap">
1069
+ <select class="jsm-select"
1070
+ [class.jsm-select--error]="errorsForPath.length && controlAsFormControl.dirty"
1194
1071
  [formControl]="controlAsFormControl">
1195
1072
  <option *ngFor="let option of effectiveSchema.enum" [ngValue]="option">{{ option }}</option>
1196
1073
  </select>
1197
- <svg class="pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 dark:text-slate-500"
1198
- viewBox="0 0 16 16" fill="none">
1074
+ <svg class="jsm-select-chevron" viewBox="0 0 16 16" fill="none">
1199
1075
  <path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
1200
1076
  </svg>
1201
1077
  </div>
1202
1078
 
1203
- <!-- checkbox come toggle switch -->
1204
- <label *ngSwitchCase="'checkbox'" class="inline-flex cursor-pointer items-center gap-2.5">
1205
- <span class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors"
1206
- [class.bg-indigo-600]="controlAsFormControl.value"
1207
- [class.bg-slate-300]="!controlAsFormControl.value"
1208
- [class.dark:bg-slate-600]="!controlAsFormControl.value">
1079
+ <label *ngSwitchCase="'checkbox'" class="jsm-toggle-label">
1080
+ <span class="jsm-toggle-track" [class.jsm-on]="controlAsFormControl.value">
1209
1081
  <input type="checkbox" class="sr-only" [formControl]="controlAsFormControl" />
1210
- <span class="inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform"
1211
- [class.translate-x-4]="controlAsFormControl.value"
1212
- [class.translate-x-0.5]="!controlAsFormControl.value"></span>
1082
+ <span class="jsm-toggle-thumb"></span>
1213
1083
  </span>
1214
- <span class="text-sm font-medium text-slate-700 dark:text-slate-300">
1215
- {{ schema.title || label }}<span *ngIf="required" class="ml-0.5 text-red-500">*</span>
1084
+ <span class="jsm-toggle-text">
1085
+ {{ schema.title || label }}<span *ngIf="required" class="jsm-required">*</span>
1216
1086
  </span>
1217
1087
  </label>
1218
-
1219
1088
  </ng-container>
1220
1089
 
1221
- <p *ngIf="inputKind === 'checkbox' && schema.description" class="text-xs text-slate-500 dark:text-slate-400">{{ schema.description }}</p>
1222
-
1223
- <p *ngIf="effectiveSchema.examples?.length" class="text-[11px] text-slate-400 dark:text-slate-500">
1224
- e.g. {{ effectiveSchema.examples?.[0] }}
1225
- </p>
1226
-
1227
- <p *ngIf="effectiveSchema.contentMediaType" class="text-[11px] text-slate-400 dark:text-slate-500">
1090
+ <p *ngIf="inputKind === 'checkbox' && schema.description" class="jsm-field-hint">{{ schema.description }}</p>
1091
+ <p *ngIf="effectiveSchema.examples?.length" class="jsm-example">e.g. {{ effectiveSchema.examples?.[0] }}</p>
1092
+ <p *ngIf="effectiveSchema.contentMediaType" class="jsm-example">
1228
1093
  {{ effectiveSchema.contentMediaType }}<span *ngIf="effectiveSchema.contentEncoding"> ({{ effectiveSchema.contentEncoding }})</span>
1229
1094
  </p>
1230
1095
 
1231
- <!-- icona errore: funzionale, segnala un problema di validazione -->
1232
- <div *ngIf="errorsForPath.length && controlAsFormControl.dirty" class="space-y-1 pt-0.5">
1233
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1234
- <svg class="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none">
1096
+ <div *ngIf="errorsForPath.length && controlAsFormControl.dirty" class="jsm-error-list">
1097
+ <p *ngFor="let error of errorsForPath" class="jsm-error-msg">
1098
+ <svg class="jsm-error-icon" viewBox="0 0 16 16" fill="none">
1235
1099
  <circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/>
1236
1100
  <path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
1237
1101
  </svg>
@@ -1241,45 +1105,30 @@ class JsonSchemaNodeComponent {
1241
1105
  </div>
1242
1106
 
1243
1107
  </ng-container>
1244
- `, isInline: true, dependencies: [{ kind: "component", type: JsonSchemaNodeComponent, selector: "jsm-schema-node", inputs: ["schema", "control", "label", "required", "path", "errorsMap", "parent", "controlKey", "allowAdditionalProperties"], outputs: ["controlReplaced"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i3.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i3.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.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: i4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1108
+ `, isInline: true, styles: [".jsm-field{display:flex;flex-direction:column;gap:.375rem}.jsm-field-label{display:block;font-size:.8125rem;font-weight:600;color:#374151}.jsm-field-hint{font-size:.75rem;color:#6b7280;margin:0;line-height:1.4}.jsm-required{color:#ef4444;margin-left:.125rem}.jsm-example{font-size:.6875rem;color:#9ca3af}.jsm-input,.jsm-textarea,.jsm-select{width:100%;border-radius:.5rem;border:1.5px solid #d1d5db;background:Canvas;padding:.5rem .75rem;font-size:.875rem;color:CanvasText;outline:none;box-shadow:0 1px 2px #0000000a;transition:border-color .15s,box-shadow .15s;font-family:inherit;box-sizing:border-box}.jsm-input:hover,.jsm-textarea:hover,.jsm-select:hover{border-color:#9ca3af}.jsm-input:focus,.jsm-textarea:focus,.jsm-select:focus{border-color:#6366f1;box-shadow:0 0 0 3px #6366f126}.jsm-input--error,.jsm-textarea--error,.jsm-select--error{border-color:#f87171!important;background:#fff5f5}.jsm-input--error:focus,.jsm-textarea--error:focus,.jsm-select--error:focus{box-shadow:0 0 0 3px #f8717133!important}.jsm-textarea{resize:vertical;min-height:6rem}.jsm-select-wrap{position:relative}.jsm-select{appearance:none;padding-right:2.25rem;cursor:pointer}.jsm-select-chevron{position:absolute;right:.625rem;top:50%;transform:translateY(-50%);width:1rem;height:1rem;color:#9ca3af;pointer-events:none}.jsm-toggle-label{display:inline-flex;align-items:center;gap:.625rem;cursor:pointer;-webkit-user-select:none;user-select:none}.jsm-toggle-track{position:relative;display:inline-flex;align-items:center;width:2.25rem;height:1.25rem;border-radius:9999px;flex-shrink:0;background:#d1d5db;transition:background .2s}.jsm-toggle-track.jsm-on{background:#4f46e5}.jsm-toggle-thumb{display:inline-block;width:1rem;height:1rem;border-radius:9999px;background:#fff;box-shadow:0 1px 3px #0003;transform:translate(.125rem);transition:transform .2s}.jsm-toggle-track.jsm-on .jsm-toggle-thumb{transform:translate(1.125rem)}.jsm-toggle-text{font-size:.875rem;font-weight:500;color:#374151}.jsm-btn-primary{display:inline-flex;align-items:center;gap:.25rem;padding:.375rem .75rem;border-radius:.5rem;font-size:.8125rem;font-weight:600;background:#4f46e5;color:#fff;border:none;cursor:pointer;box-shadow:0 1px 2px #4f46e54d;transition:background .15s,transform .1s,box-shadow .15s;white-space:nowrap;font-family:inherit}.jsm-btn-primary:hover{background:#4338ca;transform:translateY(-1px);box-shadow:0 3px 8px #4f46e54d}.jsm-btn-primary:active{transform:translateY(0)}.jsm-btn-primary:disabled{opacity:.45;cursor:not-allowed;transform:none}.jsm-btn-danger{display:inline-flex;align-items:center;gap:.25rem;padding:.25rem .5rem;border-radius:.375rem;font-size:.75rem;font-weight:600;background:transparent;color:#dc2626;border:1.5px solid #fca5a5;cursor:pointer;transition:background .15s,border-color .15s;font-family:inherit}.jsm-btn-danger:hover{background:#fee2e2;border-color:#f87171}.jsm-error-list{display:flex;flex-direction:column;gap:.25rem;margin-top:.25rem}.jsm-error-msg{display:flex;align-items:center;gap:.375rem;font-size:.75rem;color:#dc2626;font-weight:500}.jsm-error-icon{width:.875rem;height:.875rem;flex-shrink:0}.jsm-badge-row{display:flex;flex-wrap:wrap;gap:.375rem}.jsm-badge{display:inline-flex;align-items:center;border-radius:9999px;padding:.125rem .5rem;font-size:.6875rem;font-weight:600;letter-spacing:.02em;text-transform:uppercase}.jsm-badge--deprecated{background:#fef3c7;color:#92400e}.jsm-badge--readonly{background:#f1f5f9;color:#475569}.jsm-badge--writeonly{background:#dbeafe;color:#1e40af}.jsm-badge--not{background:#fee2e2;color:#991b1b}.jsm-badge--nullable{background:#f3f4f6;color:#6b7280}.jsm-fieldset{border:1.5px solid #e5e7eb;border-radius:.875rem;padding:1.25rem;background:transparent;box-shadow:0 1px 3px #0000000f;transition:box-shadow .2s;box-sizing:border-box}.jsm-fieldset:focus-within{border-color:#c7d2fe;box-shadow:0 0 0 3px #6366f114,0 1px 3px #0000000f}.jsm-legend{display:inline-flex;align-items:center;gap:.375rem;padding:0 .5rem;font-size:.875rem;font-weight:700;color:#1f2937}.jsm-obj-grid{display:grid;grid-template-columns:1fr;gap:1rem}@media (min-width:640px){.jsm-obj-grid{grid-template-columns:repeat(2,1fr)}}.jsm-obj-complex{display:flex;flex-direction:column;gap:1rem;margin-top:1rem}.jsm-dynamic-key-header{display:flex;align-items:center;justify-content:space-between;padding:.25rem .5rem;background:#f9fafb;border-radius:.375rem;border:1px dashed #d1d5db;margin-bottom:.25rem}.jsm-dynamic-key-label{font-size:.75rem;font-weight:600;color:#6b7280;font-family:monospace}.jsm-add-prop{margin-top:1rem;padding:.875rem;background:#f9fafb;border-radius:.625rem;border:1.5px dashed #d1d5db;display:flex;flex-direction:column;gap:.5rem}.jsm-add-prop-label{font-size:.75rem;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.04em}.jsm-add-prop-row{display:flex;gap:.5rem;align-items:center}.jsm-input--sm{padding:.375rem .625rem;font-size:.8125rem}.jsm-array-block{display:flex;flex-direction:column;gap:.75rem}.jsm-array-header{display:flex;align-items:flex-start;justify-content:space-between;gap:.5rem}.jsm-array-title{font-size:.875rem;font-weight:700;color:#1f2937;display:flex;align-items:center;gap:.375rem}.jsm-array-items{display:flex;flex-direction:column;gap:.75rem}.jsm-array-item{border:1.5px solid #e5e7eb;border-radius:.75rem;padding:.875rem;background:transparent;box-shadow:0 1px 2px #0000000a;transition:border-color .15s,box-shadow .15s}.jsm-array-item:focus-within{border-color:#c7d2fe;box-shadow:0 0 0 3px #6366f114}.jsm-array-item-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem}.jsm-array-index{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;border-radius:9999px;background:#eef2ff;color:#4f46e5;font-size:.6875rem;font-weight:700}.jsm-empty-array{display:flex;flex-direction:column;align-items:center;gap:.5rem;padding:2rem 1rem;border:1.5px dashed #e5e7eb;border-radius:.75rem;color:#9ca3af;font-size:.8125rem;text-align:center}.jsm-contains-hint{display:flex;align-items:center;gap:.375rem;font-size:.75rem;color:#6366f1;background:#eef2ff;border-radius:.375rem;padding:.375rem .625rem}.jsm-combinator{border:1.5px solid #e5e7eb;border-radius:.875rem;overflow:hidden;background:transparent;box-shadow:0 1px 3px #0000000f}.jsm-combinator-header{display:flex;align-items:flex-start;justify-content:space-between;padding:.875rem 1rem;background:#00000008;border-bottom:1.5px solid #e5e7eb}.jsm-combinator-title{font-size:.875rem;font-weight:700;color:#1f2937}.jsm-combinator-body{padding:1rem}.jsm-combinator-hint{font-size:.75rem;color:#6b7280;margin:.25rem 0 0}.jsm-oneof-tabs{display:flex;overflow-x:auto;border-bottom:1.5px solid #e5e7eb}.jsm-tab{padding:.5rem 1rem;font-size:.8125rem;font-weight:500;color:#6b7280;border:none;background:transparent;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1.5px;transition:color .15s,border-color .15s;white-space:nowrap;font-family:inherit}.jsm-tab:hover{color:#4f46e5}.jsm-tab--active{color:#4f46e5;border-bottom-color:#4f46e5;font-weight:600}.jsm-anyof-options{display:flex;flex-wrap:wrap;gap:.5rem;padding:.75rem 1rem;border-bottom:1.5px solid #e5e7eb}.jsm-anyof-option{display:inline-flex;align-items:center;gap:.375rem;padding:.375rem .75rem;border-radius:9999px;border:1.5px solid #e5e7eb;font-size:.8125rem;font-weight:500;color:#6b7280;cursor:pointer;transition:all .15s;background:transparent}.jsm-anyof-option:hover{border-color:#a5b4fc;color:#4f46e5}.jsm-anyof-option--active{border-color:#6366f1;background:#eef2ff;color:#4f46e5}.jsm-allof-section{border-left:3px solid #e0e7ff;padding-left:1rem}.jsm-combinator-badge{display:inline-flex;align-items:center;border-radius:9999px;padding:.125rem .5rem;font-size:.6875rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;flex-shrink:0;margin-left:.75rem}.jsm-badge--oneof{background:#dbeafe;color:#1e40af}.jsm-badge--anyof{background:#ede9fe;color:#5b21b6}.jsm-badge--allof{background:#ccfbf1;color:#065f46}.dark .jsm-field-label{color:#d1d5db}.dark .jsm-field-hint{color:#9ca3af}.dark .jsm-toggle-track{background:#475569}.dark .jsm-toggle-track.jsm-on{background:#4f46e5}.dark .jsm-toggle-text{color:#d1d5db}.dark .jsm-input,.dark .jsm-textarea,.dark .jsm-select{border-color:#475569;color:#f1f5f9}.dark .jsm-input:focus,.dark .jsm-textarea:focus,.dark .jsm-select:focus{border-color:#6366f1;box-shadow:0 0 0 3px #6366f133}.dark .jsm-input--error,.dark .jsm-textarea--error,.dark .jsm-select--error{border-color:#f87171!important;background:#f871711a}.dark .jsm-fieldset{border-color:#334155}.dark .jsm-fieldset:focus-within{border-color:#6366f1}.dark .jsm-legend{color:#f1f5f9}.dark .jsm-combinator{border-color:#334155}.dark .jsm-combinator-header{background:#ffffff0a;border-color:#334155}.dark .jsm-combinator-title{color:#f1f5f9}.dark .jsm-oneof-tabs{border-color:#334155}.dark .jsm-tab{color:#94a3b8}.dark .jsm-tab--active{color:#818cf8;border-bottom-color:#818cf8}.dark .jsm-anyof-options{border-color:#334155}.dark .jsm-anyof-option{border-color:#334155;color:#94a3b8;background:#0f172a}.dark .jsm-anyof-option--active{border-color:#6366f1;background:#6366f126;color:#818cf8}.dark .jsm-allof-section{border-left-color:#3730a3}.dark .jsm-array-item{border-color:#334155}.dark .jsm-array-item:focus-within{border-color:#6366f1}.dark .jsm-array-index{background:#6366f133;color:#818cf8}.dark .jsm-empty-array{border-color:#334155;color:#64748b}.dark .jsm-contains-hint{background:#6366f126;color:#818cf8}.dark .jsm-add-prop{background:#1e293b66;border-color:#475569}.dark .jsm-add-prop-label{color:#64748b}.dark .jsm-dynamic-key-header{background:#1e293b80;border-color:#475569}.dark .jsm-dynamic-key-label{color:#94a3b8}.dark .jsm-error-msg{color:#f87171}.dark .jsm-array-title{color:#f1f5f9}.dark .jsm-badge--deprecated{background:#d9770633;color:#fbbf24}.dark .jsm-badge--readonly{background:#1e293b;color:#94a3b8}.dark .jsm-badge--writeonly{background:#2563eb33;color:#60a5fa}.dark .jsm-badge--not{background:#dc262633;color:#f87171}.dark .jsm-badge--nullable{background:#1e293b;color:#94a3b8}.dark .jsm-select-chevron{color:#64748b}\n"], dependencies: [{ kind: "component", type: JsonSchemaNodeComponent, selector: "jsm-schema-node", inputs: ["schema", "control", "label", "required", "path", "errorsMap", "parent", "controlKey", "allowAdditionalProperties"], outputs: ["controlReplaced"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i3.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i3.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.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: i4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1245
1109
  }
1246
1110
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: JsonSchemaNodeComponent, decorators: [{
1247
1111
  type: Component,
1248
- args: [{
1249
- selector: 'jsm-schema-node',
1250
- standalone: true,
1251
- imports: [CommonModule, FormsModule, ReactiveFormsModule],
1252
- encapsulation: ViewEncapsulation.None,
1253
- template: `
1112
+ args: [{ selector: 'jsm-schema-node', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule], encapsulation: ViewEncapsulation.None, template: `
1254
1113
  <ng-container [ngSwitch]="nodeKind">
1255
1114
 
1256
- <!-- ═══════════════════════════════════════════════════════════
1257
- oneOf — tab bar: ogni opzione è una tab, nessuna icona
1258
- ═══════════════════════════════════════════════════════════ -->
1259
- <div *ngSwitchCase="'oneOf'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
1260
- <div class="flex items-start justify-between px-4 py-3 bg-slate-50 dark:bg-slate-800/60 border-b border-slate-200 dark:border-slate-700">
1115
+ <!-- ═══ oneOf ═══ -->
1116
+ <div *ngSwitchCase="'oneOf'" class="jsm-combinator">
1117
+ <div class="jsm-combinator-header">
1261
1118
  <div>
1262
- <p class="text-sm font-semibold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</p>
1263
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">{{ schema.description }}</p>
1119
+ <p class="jsm-combinator-title">{{ schema.title || label }}</p>
1120
+ <p *ngIf="schema.description" class="jsm-combinator-hint">{{ schema.description }}</p>
1264
1121
  </div>
1265
- <span class="ml-3 shrink-0 rounded-full bg-blue-100 dark:bg-blue-900/40 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-blue-700 dark:text-blue-400">oneOf</span>
1122
+ <span class="jsm-combinator-badge jsm-badge--oneof">oneOf</span>
1266
1123
  </div>
1267
- <div class="flex overflow-x-auto border-b border-slate-200 dark:border-slate-700">
1268
- <button
1269
- *ngFor="let option of schema.oneOf; let i = index"
1270
- type="button"
1271
- class="px-4 py-2.5 text-xs font-medium whitespace-nowrap border-b-2 transition-colors"
1272
- [class.border-indigo-500]="selectedOneOf === i"
1273
- [class.text-indigo-600]="selectedOneOf === i"
1274
- [class.dark:text-indigo-400]="selectedOneOf === i"
1275
- [class.font-semibold]="selectedOneOf === i"
1276
- [class.border-transparent]="selectedOneOf !== i"
1277
- [class.text-slate-500]="selectedOneOf !== i"
1278
- [class.dark:text-slate-400]="selectedOneOf !== i"
1279
- (click)="selectedOneOf = i; onOneOfChange()"
1280
- >{{ option.title || ('Option ' + (i + 1)) }}</button>
1124
+ <div class="jsm-oneof-tabs">
1125
+ <button *ngFor="let option of schema.oneOf; let i = index" type="button"
1126
+ class="jsm-tab" [class.jsm-tab--active]="selectedOneOf === i"
1127
+ (click)="selectedOneOf = i; onOneOfChange()">
1128
+ {{ option.title || ('Option ' + (i + 1)) }}
1129
+ </button>
1281
1130
  </div>
1282
- <div class="p-4" *ngIf="activeVariant">
1131
+ <div class="jsm-combinator-body" *ngIf="activeVariant">
1283
1132
  <jsm-schema-node [schema]="activeVariant" [control]="control" [parent]="parent" [controlKey]="controlKey"
1284
1133
  [path]="path" [errorsMap]="errorsMap" [label]="label" [required]="required"
1285
1134
  [allowAdditionalProperties]="allowAdditionalProperties"
@@ -1287,36 +1136,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1287
1136
  </div>
1288
1137
  </div>
1289
1138
 
1290
- <!-- ═══════════════════════════════════════════════════════════
1291
- anyOf — pill toggle, nessuna icona
1292
- ═══════════════════════════════════════════════════════════ -->
1293
- <div *ngSwitchCase="'anyOf'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
1294
- <div class="flex items-start justify-between px-4 py-3 bg-slate-50 dark:bg-slate-800/60 border-b border-slate-200 dark:border-slate-700">
1139
+ <!-- ═══ anyOf ═══ -->
1140
+ <div *ngSwitchCase="'anyOf'" class="jsm-combinator">
1141
+ <div class="jsm-combinator-header">
1295
1142
  <div>
1296
- <p class="text-sm font-semibold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</p>
1297
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">{{ schema.description }}</p>
1143
+ <p class="jsm-combinator-title">{{ schema.title || label }}</p>
1144
+ <p *ngIf="schema.description" class="jsm-combinator-hint">{{ schema.description }}</p>
1298
1145
  </div>
1299
- <span class="ml-3 shrink-0 rounded-full bg-violet-100 dark:bg-violet-900/40 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-violet-700 dark:text-violet-400">anyOf</span>
1146
+ <span class="jsm-combinator-badge jsm-badge--anyof">anyOf</span>
1300
1147
  </div>
1301
- <div class="flex flex-wrap gap-2 px-4 py-3 border-b border-slate-200 dark:border-slate-700">
1302
- <label
1303
- *ngFor="let option of schema.anyOf; let i = index"
1304
- class="inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium cursor-pointer transition-colors select-none"
1305
- [class.border-indigo-400]="selectedAnyOf.has(i)"
1306
- [class.bg-indigo-50]="selectedAnyOf.has(i)"
1307
- [class.dark:bg-indigo-900/30]="selectedAnyOf.has(i)"
1308
- [class.text-indigo-700]="selectedAnyOf.has(i)"
1309
- [class.dark:text-indigo-400]="selectedAnyOf.has(i)"
1310
- [class.border-slate-200]="!selectedAnyOf.has(i)"
1311
- [class.dark:border-slate-600]="!selectedAnyOf.has(i)"
1312
- [class.text-slate-600]="!selectedAnyOf.has(i)"
1313
- [class.dark:text-slate-400]="!selectedAnyOf.has(i)"
1314
- >
1315
- <input type="checkbox" class="h-3.5 w-3.5 accent-indigo-600" [checked]="selectedAnyOf.has(i)" (change)="toggleAnyOf(i)" />
1148
+ <div class="jsm-anyof-options">
1149
+ <label *ngFor="let option of schema.anyOf; let i = index"
1150
+ class="jsm-anyof-option" [class.jsm-anyof-option--active]="selectedAnyOf.has(i)">
1151
+ <input type="checkbox" class="sr-only" [checked]="selectedAnyOf.has(i)" (change)="toggleAnyOf(i)" />
1316
1152
  {{ option.title || ('Option ' + (i + 1)) }}
1317
1153
  </label>
1318
1154
  </div>
1319
- <div class="p-4" *ngIf="activeVariant">
1155
+ <div class="jsm-combinator-body" *ngIf="activeVariant">
1320
1156
  <jsm-schema-node [schema]="activeVariant" [control]="control" [parent]="parent" [controlKey]="controlKey"
1321
1157
  [path]="path" [errorsMap]="errorsMap" [label]="label" [required]="required"
1322
1158
  [allowAdditionalProperties]="allowAdditionalProperties"
@@ -1324,19 +1160,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1324
1160
  </div>
1325
1161
  </div>
1326
1162
 
1327
- <!-- ═══════════════════════════════════════════════════════════
1328
- allOf — sezioni in colonna con bordo sinistro
1329
- ═══════════════════════════════════════════════════════════ -->
1330
- <div *ngSwitchCase="'allOf'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
1331
- <div class="flex items-start justify-between px-4 py-3 bg-slate-50 dark:bg-slate-800/60 border-b border-slate-200 dark:border-slate-700">
1163
+ <!-- ═══ allOf ═══ -->
1164
+ <div *ngSwitchCase="'allOf'" class="jsm-combinator">
1165
+ <div class="jsm-combinator-header">
1332
1166
  <div>
1333
- <p class="text-sm font-semibold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</p>
1334
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">{{ schema.description }}</p>
1167
+ <p class="jsm-combinator-title">{{ schema.title || label }}</p>
1168
+ <p *ngIf="schema.description" class="jsm-combinator-hint">{{ schema.description }}</p>
1335
1169
  </div>
1336
- <span class="ml-3 shrink-0 rounded-full bg-teal-100 dark:bg-teal-900/40 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-teal-700 dark:text-teal-400">allOf</span>
1170
+ <span class="jsm-combinator-badge jsm-badge--allof">allOf</span>
1337
1171
  </div>
1338
- <div class="p-4 space-y-4">
1339
- <div *ngFor="let subSchema of mergedAllOf; let i = index" class="border-l-2 border-indigo-200 dark:border-indigo-700 pl-4">
1172
+ <div class="jsm-combinator-body">
1173
+ <div *ngFor="let subSchema of mergedAllOf; let i = index" class="jsm-allof-section">
1340
1174
  <jsm-schema-node [schema]="subSchema" [control]="control" [parent]="parent" [controlKey]="controlKey"
1341
1175
  [path]="path" [errorsMap]="errorsMap" [label]="subSchema.title || ('Section ' + (i + 1))" [required]="required"
1342
1176
  [allowAdditionalProperties]="allowAdditionalProperties"
@@ -1345,145 +1179,95 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1345
1179
  </div>
1346
1180
  </div>
1347
1181
 
1348
- <!-- ═══════════════════════════════════════════════════════════
1349
- object — fieldset con griglia SOLO per campi primitivi.
1350
- Logica layout:
1351
- - I figli vengono classificati come "semplici" (primitive/enum/bool)
1352
- o "complessi" (object/array/oneOf/anyOf/allOf).
1353
- - I semplici vanno in griglia 2 colonne (sm:grid-cols-2).
1354
- - I complessi vanno sempre in colonna piena (col-span-2).
1355
- ═══════════════════════════════════════════════════════════ -->
1356
- <fieldset *ngSwitchCase="'object'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm p-4 transition-shadow focus-within:border-indigo-300 dark:focus-within:border-indigo-600 focus-within:ring-2 focus-within:ring-indigo-100 dark:focus-within:ring-indigo-900/40">
1357
- <legend class="px-2 text-sm font-bold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</legend>
1358
-
1359
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 mb-3 mt-1">{{ schema.description }}</p>
1182
+ <!-- ═══ object ═══ -->
1183
+ <fieldset *ngSwitchCase="'object'" class="jsm-fieldset">
1184
+ <legend class="jsm-legend">{{ schema.title || label }}</legend>
1185
+ <p *ngIf="schema.description" class="jsm-field-hint" style="margin-bottom:.75rem;margin-top:.25rem;">{{ schema.description }}</p>
1360
1186
 
1361
- <div *ngIf="metaBadges.length" class="flex flex-wrap gap-1.5 mb-3">
1362
- <span *ngFor="let badge of metaBadges"
1363
- class="rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide"
1364
- [ngClass]="badgeClass(badge)">{{ badge }}</span>
1187
+ <div *ngIf="metaBadges.length" class="jsm-badge-row" style="margin-bottom:.75rem;">
1188
+ <span *ngFor="let badge of metaBadges" class="jsm-badge" [ngClass]="badgeClass(badge)">{{ badge }}</span>
1365
1189
  </div>
1366
1190
 
1367
- <!-- campi semplici (string/number/bool/enum) → griglia 2 colonne -->
1368
- <div class="grid grid-cols-1 sm:grid-cols-2 gap-4" [formGroup]="controlAsGroup">
1191
+ <div class="jsm-obj-grid" [formGroup]="controlAsGroup">
1369
1192
  <ng-container *ngFor="let key of simpleKeys">
1370
- <div class="flex flex-col gap-1">
1371
- <div *ngIf="isDynamicKey(key)"
1372
- class="flex items-center justify-between rounded-md border border-dashed border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800/50 px-2 py-1 mb-1">
1373
- <span class="font-mono text-xs text-slate-500 dark:text-slate-400">{{ key }}</span>
1374
- <button type="button"
1375
- class="rounded px-1.5 py-0.5 text-xs font-semibold text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
1376
- (click)="removeDynamicKey(key)">Remove</button>
1193
+ <div style="display:flex;flex-direction:column;gap:.25rem;">
1194
+ <div *ngIf="isDynamicKey(key)" class="jsm-dynamic-key-header">
1195
+ <span class="jsm-dynamic-key-label">{{ key }}</span>
1196
+ <button type="button" class="jsm-btn-danger" (click)="removeDynamicKey(key)">Remove</button>
1377
1197
  </div>
1378
- <jsm-schema-node
1379
- [schema]="schemaForKey(key)"
1380
- [control]="childControl(key)"
1381
- [parent]="controlAsGroup"
1382
- [controlKey]="key"
1383
- [path]="pathForChild(key)"
1384
- [errorsMap]="errorsMap"
1385
- [label]="key"
1386
- [required]="isRequired(key)"
1387
- [allowAdditionalProperties]="allowAdditionalProperties"
1388
- (controlReplaced)="controlReplaced.emit($event)">
1389
- </jsm-schema-node>
1198
+ <jsm-schema-node [schema]="schemaForKey(key)" [control]="childControl(key)" [parent]="controlAsGroup"
1199
+ [controlKey]="key" [path]="pathForChild(key)" [errorsMap]="errorsMap" [label]="key"
1200
+ [required]="isRequired(key)" [allowAdditionalProperties]="allowAdditionalProperties"
1201
+ (controlReplaced)="controlReplaced.emit($event)"></jsm-schema-node>
1390
1202
  </div>
1391
1203
  </ng-container>
1392
1204
  </div>
1393
1205
 
1394
- <!-- campi complessi (object/array/combinator) → colonna piena -->
1395
- <div class="space-y-4 mt-4" *ngIf="complexKeys.length > 0" [formGroup]="controlAsGroup">
1206
+ <div class="jsm-obj-complex" *ngIf="complexKeys.length > 0" [formGroup]="controlAsGroup">
1396
1207
  <ng-container *ngFor="let key of complexKeys">
1397
- <div class="flex flex-col gap-1">
1398
- <div *ngIf="isDynamicKey(key)"
1399
- class="flex items-center justify-between rounded-md border border-dashed border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800/50 px-2 py-1 mb-1">
1400
- <span class="font-mono text-xs text-slate-500 dark:text-slate-400">{{ key }}</span>
1401
- <button type="button"
1402
- class="rounded px-1.5 py-0.5 text-xs font-semibold text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
1403
- (click)="removeDynamicKey(key)">Remove</button>
1208
+ <div style="display:flex;flex-direction:column;gap:.25rem;">
1209
+ <div *ngIf="isDynamicKey(key)" class="jsm-dynamic-key-header">
1210
+ <span class="jsm-dynamic-key-label">{{ key }}</span>
1211
+ <button type="button" class="jsm-btn-danger" (click)="removeDynamicKey(key)">Remove</button>
1404
1212
  </div>
1405
- <jsm-schema-node
1406
- [schema]="schemaForKey(key)"
1407
- [control]="childControl(key)"
1408
- [parent]="controlAsGroup"
1409
- [controlKey]="key"
1410
- [path]="pathForChild(key)"
1411
- [errorsMap]="errorsMap"
1412
- [label]="key"
1413
- [required]="isRequired(key)"
1414
- [allowAdditionalProperties]="allowAdditionalProperties"
1415
- (controlReplaced)="controlReplaced.emit($event)">
1416
- </jsm-schema-node>
1213
+ <jsm-schema-node [schema]="schemaForKey(key)" [control]="childControl(key)" [parent]="controlAsGroup"
1214
+ [controlKey]="key" [path]="pathForChild(key)" [errorsMap]="errorsMap" [label]="key"
1215
+ [required]="isRequired(key)" [allowAdditionalProperties]="allowAdditionalProperties"
1216
+ (controlReplaced)="controlReplaced.emit($event)"></jsm-schema-node>
1417
1217
  </div>
1418
1218
  </ng-container>
1419
1219
  </div>
1420
1220
 
1421
- <!-- add dynamic property -->
1422
- <div *ngIf="canAddProperty" class="mt-4 rounded-lg border border-dashed border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800/40 p-3 space-y-2">
1423
- <p class="text-[10px] font-bold uppercase tracking-widest text-slate-400 dark:text-slate-500">Add property</p>
1424
- <div class="flex gap-2">
1425
- <input
1426
- class="flex-1 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 placeholder:text-slate-400 dark:placeholder:text-slate-500 px-3 py-1.5 text-sm shadow-sm outline-none transition hover:border-slate-400 dark:hover:border-slate-500 focus:border-indigo-400 focus:ring-2 focus:ring-indigo-100 dark:focus:ring-indigo-900/40"
1427
- [(ngModel)]="newPropertyKey" placeholder="propertyName" />
1428
- <button type="button"
1429
- class="rounded-lg bg-indigo-600 px-3 py-1.5 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700 active:scale-95"
1430
- (click)="addDynamicProperty()">Add</button>
1221
+ <div *ngIf="canAddProperty" class="jsm-add-prop">
1222
+ <p class="jsm-add-prop-label">Add property</p>
1223
+ <div class="jsm-add-prop-row">
1224
+ <input class="jsm-input jsm-input--sm" style="flex:1;" [(ngModel)]="newPropertyKey" placeholder="propertyName" />
1225
+ <button type="button" class="jsm-btn-primary" (click)="addDynamicProperty()">Add</button>
1431
1226
  </div>
1432
- <p *ngIf="propertyError" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1433
- <!-- icona errore: unico caso in cui ha senso, segnala un problema -->
1434
- <svg class="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1227
+ <p *ngIf="propertyError" class="jsm-error-msg">
1228
+ <svg class="jsm-error-icon" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1435
1229
  {{ propertyError }}
1436
1230
  </p>
1437
1231
  </div>
1438
1232
 
1439
- <div *ngIf="errorsForPath.length" class="mt-3 space-y-1">
1440
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1441
- <svg class="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1233
+ <div *ngIf="errorsForPath.length" class="jsm-error-list" style="margin-top:.75rem;">
1234
+ <p *ngFor="let error of errorsForPath" class="jsm-error-msg">
1235
+ <svg class="jsm-error-icon" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1442
1236
  {{ error }}
1443
1237
  </p>
1444
1238
  </div>
1445
1239
  </fieldset>
1446
1240
 
1447
- <!-- ═══════════════════════════════════════════════════════════
1448
- array — sempre in colonna piena, items in colonna
1449
- ═══════════════════════════════════════════════════════════ -->
1450
- <div *ngSwitchCase="'array'" class="space-y-3">
1451
- <div class="flex items-start justify-between gap-2">
1241
+ <!-- ═══ array ═══ -->
1242
+ <div *ngSwitchCase="'array'" class="jsm-array-block">
1243
+ <div class="jsm-array-header">
1452
1244
  <div>
1453
- <p class="text-sm font-semibold text-slate-800">
1454
- {{ schema.title || label }}<span *ngIf="required" class="ml-0.5 text-red-500">*</span>
1245
+ <p class="jsm-array-title">
1246
+ {{ schema.title || label }}<span *ngIf="required" style="color:#ef4444;margin-left:.125rem;">*</span>
1455
1247
  </p>
1456
- <p *ngIf="schema.description" class="text-xs text-slate-500 mt-0.5">{{ schema.description }}</p>
1248
+ <p *ngIf="schema.description" class="jsm-field-hint">{{ schema.description }}</p>
1457
1249
  </div>
1458
- <!-- icona + nel bottone: indica l'azione "aggiungi", non decorativa -->
1459
- <button type="button"
1460
- class="shrink-0 flex items-center gap-1 rounded-lg bg-indigo-600 px-3 py-1.5 text-xs font-semibold text-white shadow-sm transition hover:bg-indigo-700 active:scale-95 disabled:opacity-40 disabled:cursor-not-allowed"
1461
- (click)="addArrayItem()" [disabled]="!canAddArrayItem">
1462
- <svg class="w-3.5 h-3.5" viewBox="0 0 16 16" fill="none"><path d="M8 3v10M3 8h10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1250
+ <button type="button" class="jsm-btn-primary" (click)="addArrayItem()" [disabled]="!canAddArrayItem">
1251
+ <svg style="width:.875rem;height:.875rem;flex-shrink:0;" viewBox="0 0 16 16" fill="none"><path d="M8 3v10M3 8h10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1463
1252
  Add item
1464
1253
  </button>
1465
1254
  </div>
1466
1255
 
1467
- <div *ngIf="effectiveSchema.contains" class="rounded-md bg-indigo-50 dark:bg-indigo-900/30 px-3 py-1.5 text-xs text-indigo-700 dark:text-indigo-400">
1256
+ <div *ngIf="effectiveSchema.contains" class="jsm-contains-hint">
1468
1257
  Must contain matching item(s)
1469
1258
  <span *ngIf="effectiveSchema.minContains"> · min {{ effectiveSchema.minContains }}</span>
1470
1259
  <span *ngIf="effectiveSchema.maxContains"> · max {{ effectiveSchema.maxContains }}</span>
1471
1260
  </div>
1472
1261
 
1473
- <div *ngIf="metaBadges.length" class="flex flex-wrap gap-1.5">
1474
- <span *ngFor="let badge of metaBadges"
1475
- class="rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide"
1476
- [ngClass]="badgeClass(badge)">{{ badge }}</span>
1262
+ <div *ngIf="metaBadges.length" class="jsm-badge-row">
1263
+ <span *ngFor="let badge of metaBadges" class="jsm-badge" [ngClass]="badgeClass(badge)">{{ badge }}</span>
1477
1264
  </div>
1478
1265
 
1479
- <div class="space-y-2" *ngIf="arrayControls.length > 0; else emptyArray">
1480
- <div *ngFor="let item of arrayControls; let i = index"
1481
- class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-3 shadow-sm transition focus-within:border-indigo-300 dark:focus-within:border-indigo-600 focus-within:ring-2 focus-within:ring-indigo-100 dark:focus-within:ring-indigo-900/40">
1482
- <div class="flex items-center justify-between mb-3">
1483
- <span class="inline-flex h-5 w-5 items-center justify-center rounded-full bg-indigo-100 dark:bg-indigo-900/50 text-[11px] font-bold text-indigo-600 dark:text-indigo-400">{{ i + 1 }}</span>
1484
- <button type="button"
1485
- class="rounded-md border border-red-200 dark:border-red-800 px-2 py-0.5 text-xs font-semibold text-red-600 dark:text-red-400 transition hover:bg-red-50 dark:hover:bg-red-900/20"
1486
- (click)="removeArrayItem(i)">Remove</button>
1266
+ <div class="jsm-array-items" *ngIf="arrayControls.length > 0; else emptyArray">
1267
+ <div *ngFor="let item of arrayControls; let i = index" class="jsm-array-item">
1268
+ <div class="jsm-array-item-header">
1269
+ <span class="jsm-array-index">{{ i + 1 }}</span>
1270
+ <button type="button" class="jsm-btn-danger" (click)="removeArrayItem(i)">Remove</button>
1487
1271
  </div>
1488
1272
  <jsm-schema-node [schema]="schemaForIndex(i)" [control]="item" [parent]="controlAsArray"
1489
1273
  [controlKey]="i" [path]="pathForChild(i)" [errorsMap]="errorsMap" [label]="label + ' item'"
@@ -1493,124 +1277,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1493
1277
  </div>
1494
1278
 
1495
1279
  <ng-template #emptyArray>
1496
- <div class="flex flex-col items-center gap-2 rounded-xl border border-dashed border-slate-200 dark:border-slate-700 py-8 text-slate-400 dark:text-slate-500 text-xs">
1497
- No items yet — click "Add item" to start.
1498
- </div>
1280
+ <div class="jsm-empty-array">No items yet — click "Add item" to start.</div>
1499
1281
  </ng-template>
1500
1282
 
1501
- <div *ngIf="errorsForPath.length" class="space-y-1">
1502
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1503
- <svg class="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1283
+ <div *ngIf="errorsForPath.length" class="jsm-error-list">
1284
+ <p *ngFor="let error of errorsForPath" class="jsm-error-msg">
1285
+ <svg class="jsm-error-icon" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1504
1286
  {{ error }}
1505
1287
  </p>
1506
1288
  </div>
1507
1289
  </div>
1508
1290
 
1509
- <!-- ═══════════════════════════════════════════════════════════
1510
- primitive — sempre in colonna, nessuna icona decorativa.
1511
- Icona errore solo nei messaggi di validazione.
1512
- Icona chevron nel select: funzionale (indica dropdown).
1513
- ═══════════════════════════════════════════════════════════ -->
1514
- <div *ngSwitchDefault class="space-y-1.5">
1291
+ <!-- ═══ primitive ═══ -->
1292
+ <div *ngSwitchDefault class="jsm-field">
1515
1293
  <ng-container *ngIf="inputKind !== 'checkbox'">
1516
- <label class="block text-[13px] font-semibold text-slate-700 dark:text-slate-300">
1517
- {{ schema.title || label }}<span *ngIf="required" class="ml-0.5 text-red-500">*</span>
1294
+ <label class="jsm-field-label">
1295
+ {{ schema.title || label }}<span *ngIf="required" class="jsm-required">*</span>
1518
1296
  </label>
1519
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 -mt-0.5">{{ schema.description }}</p>
1297
+ <p *ngIf="schema.description" class="jsm-field-hint">{{ schema.description }}</p>
1520
1298
  </ng-container>
1521
1299
 
1522
- <div *ngIf="metaBadges.length" class="flex flex-wrap gap-1.5">
1523
- <span *ngFor="let badge of metaBadges"
1524
- class="rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide"
1525
- [ngClass]="badgeClass(badge)">{{ badge }}</span>
1300
+ <div *ngIf="metaBadges.length" class="jsm-badge-row">
1301
+ <span *ngFor="let badge of metaBadges" class="jsm-badge" [ngClass]="badgeClass(badge)">{{ badge }}</span>
1526
1302
  </div>
1527
1303
 
1528
1304
  <ng-container [ngSwitch]="inputKind">
1305
+ <input *ngSwitchCase="'text'" class="jsm-input"
1306
+ [class.jsm-input--error]="errorsForPath.length && controlAsFormControl.dirty"
1307
+ [attr.type]="inputType" [attr.placeholder]="schema.title || label"
1308
+ [formControl]="controlAsFormControl" />
1529
1309
 
1530
- <input *ngSwitchCase="'text'"
1531
- class="w-full rounded-lg border bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 placeholder:text-slate-400 dark:placeholder:text-slate-500 px-3 py-2 text-sm shadow-sm outline-none transition hover:border-slate-400 dark:hover:border-slate-500 focus:ring-2"
1532
- [class.border-slate-300]="!(errorsForPath.length && controlAsFormControl.dirty)"
1533
- [class.dark:border-slate-600]="!(errorsForPath.length && controlAsFormControl.dirty)"
1534
- [class.focus:border-indigo-400]="!(errorsForPath.length && controlAsFormControl.dirty)"
1535
- [class.focus:ring-indigo-100]="!(errorsForPath.length && controlAsFormControl.dirty)"
1536
- [class.dark:focus:ring-indigo-900/40]="!(errorsForPath.length && controlAsFormControl.dirty)"
1537
- [class.border-red-300]="errorsForPath.length && controlAsFormControl.dirty"
1538
- [class.dark:border-red-700]="errorsForPath.length && controlAsFormControl.dirty"
1539
- [class.bg-red-50]="errorsForPath.length && controlAsFormControl.dirty"
1540
- [class.dark:bg-red-900/20]="errorsForPath.length && controlAsFormControl.dirty"
1541
- [class.focus:border-red-400]="errorsForPath.length && controlAsFormControl.dirty"
1542
- [class.focus:ring-red-100]="errorsForPath.length && controlAsFormControl.dirty"
1543
- [attr.type]="inputType"
1310
+ <textarea *ngSwitchCase="'textarea'" rows="4" class="jsm-textarea"
1311
+ [class.jsm-textarea--error]="errorsForPath.length && controlAsFormControl.dirty"
1544
1312
  [attr.placeholder]="schema.title || label"
1545
- [formControl]="controlAsFormControl"
1546
- />
1313
+ [formControl]="controlAsFormControl"></textarea>
1547
1314
 
1548
- <textarea *ngSwitchCase="'textarea'" rows="4"
1549
- class="w-full resize-y rounded-lg border bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 placeholder:text-slate-400 dark:placeholder:text-slate-500 px-3 py-2 text-sm shadow-sm outline-none transition hover:border-slate-400 dark:hover:border-slate-500 focus:ring-2"
1550
- [class.border-slate-300]="!(errorsForPath.length && controlAsFormControl.dirty)"
1551
- [class.dark:border-slate-600]="!(errorsForPath.length && controlAsFormControl.dirty)"
1552
- [class.focus:border-indigo-400]="!(errorsForPath.length && controlAsFormControl.dirty)"
1553
- [class.focus:ring-indigo-100]="!(errorsForPath.length && controlAsFormControl.dirty)"
1554
- [class.dark:focus:ring-indigo-900/40]="!(errorsForPath.length && controlAsFormControl.dirty)"
1555
- [class.border-red-300]="errorsForPath.length && controlAsFormControl.dirty"
1556
- [class.dark:border-red-700]="errorsForPath.length && controlAsFormControl.dirty"
1557
- [class.bg-red-50]="errorsForPath.length && controlAsFormControl.dirty"
1558
- [class.dark:bg-red-900/20]="errorsForPath.length && controlAsFormControl.dirty"
1559
- [class.focus:border-red-400]="errorsForPath.length && controlAsFormControl.dirty"
1560
- [class.focus:ring-red-100]="errorsForPath.length && controlAsFormControl.dirty"
1561
- [attr.placeholder]="schema.title || label"
1562
- [formControl]="controlAsFormControl"
1563
- ></textarea>
1564
-
1565
- <!-- select: il chevron SVG è funzionale, indica che è un dropdown -->
1566
- <div *ngSwitchCase="'select'" class="relative">
1567
- <select
1568
- class="w-full appearance-none rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 px-3 py-2 pr-8 text-sm shadow-sm outline-none transition hover:border-slate-400 dark:hover:border-slate-500 focus:border-indigo-400 focus:ring-2 focus:ring-indigo-100 dark:focus:ring-indigo-900/40"
1569
- [class.border-red-300]="errorsForPath.length && controlAsFormControl.dirty"
1570
- [class.dark:border-red-700]="errorsForPath.length && controlAsFormControl.dirty"
1571
- [class.bg-red-50]="errorsForPath.length && controlAsFormControl.dirty"
1572
- [class.dark:bg-red-900/20]="errorsForPath.length && controlAsFormControl.dirty"
1315
+ <div *ngSwitchCase="'select'" class="jsm-select-wrap">
1316
+ <select class="jsm-select"
1317
+ [class.jsm-select--error]="errorsForPath.length && controlAsFormControl.dirty"
1573
1318
  [formControl]="controlAsFormControl">
1574
1319
  <option *ngFor="let option of effectiveSchema.enum" [ngValue]="option">{{ option }}</option>
1575
1320
  </select>
1576
- <svg class="pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 dark:text-slate-500"
1577
- viewBox="0 0 16 16" fill="none">
1321
+ <svg class="jsm-select-chevron" viewBox="0 0 16 16" fill="none">
1578
1322
  <path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
1579
1323
  </svg>
1580
1324
  </div>
1581
1325
 
1582
- <!-- checkbox come toggle switch -->
1583
- <label *ngSwitchCase="'checkbox'" class="inline-flex cursor-pointer items-center gap-2.5">
1584
- <span class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors"
1585
- [class.bg-indigo-600]="controlAsFormControl.value"
1586
- [class.bg-slate-300]="!controlAsFormControl.value"
1587
- [class.dark:bg-slate-600]="!controlAsFormControl.value">
1326
+ <label *ngSwitchCase="'checkbox'" class="jsm-toggle-label">
1327
+ <span class="jsm-toggle-track" [class.jsm-on]="controlAsFormControl.value">
1588
1328
  <input type="checkbox" class="sr-only" [formControl]="controlAsFormControl" />
1589
- <span class="inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform"
1590
- [class.translate-x-4]="controlAsFormControl.value"
1591
- [class.translate-x-0.5]="!controlAsFormControl.value"></span>
1329
+ <span class="jsm-toggle-thumb"></span>
1592
1330
  </span>
1593
- <span class="text-sm font-medium text-slate-700 dark:text-slate-300">
1594
- {{ schema.title || label }}<span *ngIf="required" class="ml-0.5 text-red-500">*</span>
1331
+ <span class="jsm-toggle-text">
1332
+ {{ schema.title || label }}<span *ngIf="required" class="jsm-required">*</span>
1595
1333
  </span>
1596
1334
  </label>
1597
-
1598
1335
  </ng-container>
1599
1336
 
1600
- <p *ngIf="inputKind === 'checkbox' && schema.description" class="text-xs text-slate-500 dark:text-slate-400">{{ schema.description }}</p>
1601
-
1602
- <p *ngIf="effectiveSchema.examples?.length" class="text-[11px] text-slate-400 dark:text-slate-500">
1603
- e.g. {{ effectiveSchema.examples?.[0] }}
1604
- </p>
1605
-
1606
- <p *ngIf="effectiveSchema.contentMediaType" class="text-[11px] text-slate-400 dark:text-slate-500">
1337
+ <p *ngIf="inputKind === 'checkbox' && schema.description" class="jsm-field-hint">{{ schema.description }}</p>
1338
+ <p *ngIf="effectiveSchema.examples?.length" class="jsm-example">e.g. {{ effectiveSchema.examples?.[0] }}</p>
1339
+ <p *ngIf="effectiveSchema.contentMediaType" class="jsm-example">
1607
1340
  {{ effectiveSchema.contentMediaType }}<span *ngIf="effectiveSchema.contentEncoding"> ({{ effectiveSchema.contentEncoding }})</span>
1608
1341
  </p>
1609
1342
 
1610
- <!-- icona errore: funzionale, segnala un problema di validazione -->
1611
- <div *ngIf="errorsForPath.length && controlAsFormControl.dirty" class="space-y-1 pt-0.5">
1612
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1613
- <svg class="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none">
1343
+ <div *ngIf="errorsForPath.length && controlAsFormControl.dirty" class="jsm-error-list">
1344
+ <p *ngFor="let error of errorsForPath" class="jsm-error-msg">
1345
+ <svg class="jsm-error-icon" viewBox="0 0 16 16" fill="none">
1614
1346
  <circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/>
1615
1347
  <path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
1616
1348
  </svg>
@@ -1620,10 +1352,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1620
1352
  </div>
1621
1353
 
1622
1354
  </ng-container>
1623
- `,
1624
- changeDetection: ChangeDetectionStrategy.OnPush,
1625
- }]
1626
- }], ctorParameters: () => [{ type: JsonSchemaFormService }, { type: JsonSchemaValidationService }], propDecorators: { schema: [{
1355
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".jsm-field{display:flex;flex-direction:column;gap:.375rem}.jsm-field-label{display:block;font-size:.8125rem;font-weight:600;color:#374151}.jsm-field-hint{font-size:.75rem;color:#6b7280;margin:0;line-height:1.4}.jsm-required{color:#ef4444;margin-left:.125rem}.jsm-example{font-size:.6875rem;color:#9ca3af}.jsm-input,.jsm-textarea,.jsm-select{width:100%;border-radius:.5rem;border:1.5px solid #d1d5db;background:Canvas;padding:.5rem .75rem;font-size:.875rem;color:CanvasText;outline:none;box-shadow:0 1px 2px #0000000a;transition:border-color .15s,box-shadow .15s;font-family:inherit;box-sizing:border-box}.jsm-input:hover,.jsm-textarea:hover,.jsm-select:hover{border-color:#9ca3af}.jsm-input:focus,.jsm-textarea:focus,.jsm-select:focus{border-color:#6366f1;box-shadow:0 0 0 3px #6366f126}.jsm-input--error,.jsm-textarea--error,.jsm-select--error{border-color:#f87171!important;background:#fff5f5}.jsm-input--error:focus,.jsm-textarea--error:focus,.jsm-select--error:focus{box-shadow:0 0 0 3px #f8717133!important}.jsm-textarea{resize:vertical;min-height:6rem}.jsm-select-wrap{position:relative}.jsm-select{appearance:none;padding-right:2.25rem;cursor:pointer}.jsm-select-chevron{position:absolute;right:.625rem;top:50%;transform:translateY(-50%);width:1rem;height:1rem;color:#9ca3af;pointer-events:none}.jsm-toggle-label{display:inline-flex;align-items:center;gap:.625rem;cursor:pointer;-webkit-user-select:none;user-select:none}.jsm-toggle-track{position:relative;display:inline-flex;align-items:center;width:2.25rem;height:1.25rem;border-radius:9999px;flex-shrink:0;background:#d1d5db;transition:background .2s}.jsm-toggle-track.jsm-on{background:#4f46e5}.jsm-toggle-thumb{display:inline-block;width:1rem;height:1rem;border-radius:9999px;background:#fff;box-shadow:0 1px 3px #0003;transform:translate(.125rem);transition:transform .2s}.jsm-toggle-track.jsm-on .jsm-toggle-thumb{transform:translate(1.125rem)}.jsm-toggle-text{font-size:.875rem;font-weight:500;color:#374151}.jsm-btn-primary{display:inline-flex;align-items:center;gap:.25rem;padding:.375rem .75rem;border-radius:.5rem;font-size:.8125rem;font-weight:600;background:#4f46e5;color:#fff;border:none;cursor:pointer;box-shadow:0 1px 2px #4f46e54d;transition:background .15s,transform .1s,box-shadow .15s;white-space:nowrap;font-family:inherit}.jsm-btn-primary:hover{background:#4338ca;transform:translateY(-1px);box-shadow:0 3px 8px #4f46e54d}.jsm-btn-primary:active{transform:translateY(0)}.jsm-btn-primary:disabled{opacity:.45;cursor:not-allowed;transform:none}.jsm-btn-danger{display:inline-flex;align-items:center;gap:.25rem;padding:.25rem .5rem;border-radius:.375rem;font-size:.75rem;font-weight:600;background:transparent;color:#dc2626;border:1.5px solid #fca5a5;cursor:pointer;transition:background .15s,border-color .15s;font-family:inherit}.jsm-btn-danger:hover{background:#fee2e2;border-color:#f87171}.jsm-error-list{display:flex;flex-direction:column;gap:.25rem;margin-top:.25rem}.jsm-error-msg{display:flex;align-items:center;gap:.375rem;font-size:.75rem;color:#dc2626;font-weight:500}.jsm-error-icon{width:.875rem;height:.875rem;flex-shrink:0}.jsm-badge-row{display:flex;flex-wrap:wrap;gap:.375rem}.jsm-badge{display:inline-flex;align-items:center;border-radius:9999px;padding:.125rem .5rem;font-size:.6875rem;font-weight:600;letter-spacing:.02em;text-transform:uppercase}.jsm-badge--deprecated{background:#fef3c7;color:#92400e}.jsm-badge--readonly{background:#f1f5f9;color:#475569}.jsm-badge--writeonly{background:#dbeafe;color:#1e40af}.jsm-badge--not{background:#fee2e2;color:#991b1b}.jsm-badge--nullable{background:#f3f4f6;color:#6b7280}.jsm-fieldset{border:1.5px solid #e5e7eb;border-radius:.875rem;padding:1.25rem;background:transparent;box-shadow:0 1px 3px #0000000f;transition:box-shadow .2s;box-sizing:border-box}.jsm-fieldset:focus-within{border-color:#c7d2fe;box-shadow:0 0 0 3px #6366f114,0 1px 3px #0000000f}.jsm-legend{display:inline-flex;align-items:center;gap:.375rem;padding:0 .5rem;font-size:.875rem;font-weight:700;color:#1f2937}.jsm-obj-grid{display:grid;grid-template-columns:1fr;gap:1rem}@media (min-width:640px){.jsm-obj-grid{grid-template-columns:repeat(2,1fr)}}.jsm-obj-complex{display:flex;flex-direction:column;gap:1rem;margin-top:1rem}.jsm-dynamic-key-header{display:flex;align-items:center;justify-content:space-between;padding:.25rem .5rem;background:#f9fafb;border-radius:.375rem;border:1px dashed #d1d5db;margin-bottom:.25rem}.jsm-dynamic-key-label{font-size:.75rem;font-weight:600;color:#6b7280;font-family:monospace}.jsm-add-prop{margin-top:1rem;padding:.875rem;background:#f9fafb;border-radius:.625rem;border:1.5px dashed #d1d5db;display:flex;flex-direction:column;gap:.5rem}.jsm-add-prop-label{font-size:.75rem;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.04em}.jsm-add-prop-row{display:flex;gap:.5rem;align-items:center}.jsm-input--sm{padding:.375rem .625rem;font-size:.8125rem}.jsm-array-block{display:flex;flex-direction:column;gap:.75rem}.jsm-array-header{display:flex;align-items:flex-start;justify-content:space-between;gap:.5rem}.jsm-array-title{font-size:.875rem;font-weight:700;color:#1f2937;display:flex;align-items:center;gap:.375rem}.jsm-array-items{display:flex;flex-direction:column;gap:.75rem}.jsm-array-item{border:1.5px solid #e5e7eb;border-radius:.75rem;padding:.875rem;background:transparent;box-shadow:0 1px 2px #0000000a;transition:border-color .15s,box-shadow .15s}.jsm-array-item:focus-within{border-color:#c7d2fe;box-shadow:0 0 0 3px #6366f114}.jsm-array-item-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem}.jsm-array-index{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;border-radius:9999px;background:#eef2ff;color:#4f46e5;font-size:.6875rem;font-weight:700}.jsm-empty-array{display:flex;flex-direction:column;align-items:center;gap:.5rem;padding:2rem 1rem;border:1.5px dashed #e5e7eb;border-radius:.75rem;color:#9ca3af;font-size:.8125rem;text-align:center}.jsm-contains-hint{display:flex;align-items:center;gap:.375rem;font-size:.75rem;color:#6366f1;background:#eef2ff;border-radius:.375rem;padding:.375rem .625rem}.jsm-combinator{border:1.5px solid #e5e7eb;border-radius:.875rem;overflow:hidden;background:transparent;box-shadow:0 1px 3px #0000000f}.jsm-combinator-header{display:flex;align-items:flex-start;justify-content:space-between;padding:.875rem 1rem;background:#00000008;border-bottom:1.5px solid #e5e7eb}.jsm-combinator-title{font-size:.875rem;font-weight:700;color:#1f2937}.jsm-combinator-body{padding:1rem}.jsm-combinator-hint{font-size:.75rem;color:#6b7280;margin:.25rem 0 0}.jsm-oneof-tabs{display:flex;overflow-x:auto;border-bottom:1.5px solid #e5e7eb}.jsm-tab{padding:.5rem 1rem;font-size:.8125rem;font-weight:500;color:#6b7280;border:none;background:transparent;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1.5px;transition:color .15s,border-color .15s;white-space:nowrap;font-family:inherit}.jsm-tab:hover{color:#4f46e5}.jsm-tab--active{color:#4f46e5;border-bottom-color:#4f46e5;font-weight:600}.jsm-anyof-options{display:flex;flex-wrap:wrap;gap:.5rem;padding:.75rem 1rem;border-bottom:1.5px solid #e5e7eb}.jsm-anyof-option{display:inline-flex;align-items:center;gap:.375rem;padding:.375rem .75rem;border-radius:9999px;border:1.5px solid #e5e7eb;font-size:.8125rem;font-weight:500;color:#6b7280;cursor:pointer;transition:all .15s;background:transparent}.jsm-anyof-option:hover{border-color:#a5b4fc;color:#4f46e5}.jsm-anyof-option--active{border-color:#6366f1;background:#eef2ff;color:#4f46e5}.jsm-allof-section{border-left:3px solid #e0e7ff;padding-left:1rem}.jsm-combinator-badge{display:inline-flex;align-items:center;border-radius:9999px;padding:.125rem .5rem;font-size:.6875rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;flex-shrink:0;margin-left:.75rem}.jsm-badge--oneof{background:#dbeafe;color:#1e40af}.jsm-badge--anyof{background:#ede9fe;color:#5b21b6}.jsm-badge--allof{background:#ccfbf1;color:#065f46}.dark .jsm-field-label{color:#d1d5db}.dark .jsm-field-hint{color:#9ca3af}.dark .jsm-toggle-track{background:#475569}.dark .jsm-toggle-track.jsm-on{background:#4f46e5}.dark .jsm-toggle-text{color:#d1d5db}.dark .jsm-input,.dark .jsm-textarea,.dark .jsm-select{border-color:#475569;color:#f1f5f9}.dark .jsm-input:focus,.dark .jsm-textarea:focus,.dark .jsm-select:focus{border-color:#6366f1;box-shadow:0 0 0 3px #6366f133}.dark .jsm-input--error,.dark .jsm-textarea--error,.dark .jsm-select--error{border-color:#f87171!important;background:#f871711a}.dark .jsm-fieldset{border-color:#334155}.dark .jsm-fieldset:focus-within{border-color:#6366f1}.dark .jsm-legend{color:#f1f5f9}.dark .jsm-combinator{border-color:#334155}.dark .jsm-combinator-header{background:#ffffff0a;border-color:#334155}.dark .jsm-combinator-title{color:#f1f5f9}.dark .jsm-oneof-tabs{border-color:#334155}.dark .jsm-tab{color:#94a3b8}.dark .jsm-tab--active{color:#818cf8;border-bottom-color:#818cf8}.dark .jsm-anyof-options{border-color:#334155}.dark .jsm-anyof-option{border-color:#334155;color:#94a3b8;background:#0f172a}.dark .jsm-anyof-option--active{border-color:#6366f1;background:#6366f126;color:#818cf8}.dark .jsm-allof-section{border-left-color:#3730a3}.dark .jsm-array-item{border-color:#334155}.dark .jsm-array-item:focus-within{border-color:#6366f1}.dark .jsm-array-index{background:#6366f133;color:#818cf8}.dark .jsm-empty-array{border-color:#334155;color:#64748b}.dark .jsm-contains-hint{background:#6366f126;color:#818cf8}.dark .jsm-add-prop{background:#1e293b66;border-color:#475569}.dark .jsm-add-prop-label{color:#64748b}.dark .jsm-dynamic-key-header{background:#1e293b80;border-color:#475569}.dark .jsm-dynamic-key-label{color:#94a3b8}.dark .jsm-error-msg{color:#f87171}.dark .jsm-array-title{color:#f1f5f9}.dark .jsm-badge--deprecated{background:#d9770633;color:#fbbf24}.dark .jsm-badge--readonly{background:#1e293b;color:#94a3b8}.dark .jsm-badge--writeonly{background:#2563eb33;color:#60a5fa}.dark .jsm-badge--not{background:#dc262633;color:#f87171}.dark .jsm-badge--nullable{background:#1e293b;color:#94a3b8}.dark .jsm-select-chevron{color:#64748b}\n"] }]
1356
+ }], ctorParameters: () => [{ type: JsonSchemaFormService }, { type: JsonSchemaValidationService }, { type: i0.ChangeDetectorRef }], propDecorators: { schema: [{
1627
1357
  type: Input,
1628
1358
  args: [{ required: true }]
1629
1359
  }], control: [{
@@ -1647,6 +1377,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1647
1377
  type: Output
1648
1378
  }] } });
1649
1379
 
1380
+ // AUTO-GENERATED — do not edit manually. Run: node scripts/generate-lib-styles.mjs
1381
+ const JSM_CSS = "/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */\n@layer properties;\n@layer theme, base, components, utilities;\n@layer theme {\n :root, :host {\n --font-sans: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\",\n \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n \"Courier New\", monospace;\n --color-red-50: oklch(97.1% 0.013 17.38);\n --color-red-300: oklch(80.8% 0.114 19.571);\n --color-red-700: oklch(50.5% 0.213 27.518);\n --color-red-900: oklch(39.6% 0.141 25.723);\n --color-amber-50: oklch(98.7% 0.022 95.277);\n --color-amber-200: oklch(92.4% 0.12 95.746);\n --color-amber-300: oklch(87.9% 0.169 91.605);\n --color-amber-400: oklch(82.8% 0.189 84.429);\n --color-amber-600: oklch(66.6% 0.179 58.318);\n --color-amber-700: oklch(55.5% 0.163 48.998);\n --color-amber-800: oklch(47.3% 0.137 46.201);\n --color-amber-900: oklch(41.4% 0.112 45.904);\n --color-emerald-50: oklch(97.9% 0.021 166.113);\n --color-emerald-400: oklch(76.5% 0.177 163.223);\n --color-emerald-700: oklch(50.8% 0.118 165.612);\n --color-emerald-900: oklch(37.8% 0.077 168.94);\n --color-indigo-100: oklch(93% 0.034 272.788);\n --color-indigo-300: oklch(78.5% 0.115 274.713);\n --color-indigo-400: oklch(67.3% 0.182 276.935);\n --color-indigo-500: oklch(58.5% 0.233 277.117);\n --color-indigo-600: oklch(51.1% 0.262 276.966);\n --color-indigo-700: oklch(45.7% 0.24 277.023);\n --color-indigo-900: oklch(35.9% 0.144 278.697);\n --color-rose-50: oklch(96.9% 0.015 12.422);\n --color-rose-400: oklch(71.2% 0.194 13.428);\n --color-rose-500: oklch(64.5% 0.246 16.439);\n --color-rose-600: oklch(58.6% 0.253 17.585);\n --color-rose-700: oklch(51.4% 0.222 16.935);\n --color-rose-900: oklch(41% 0.159 10.272);\n --color-slate-50: oklch(98.4% 0.003 247.858);\n --color-slate-100: oklch(96.8% 0.007 247.896);\n --color-slate-200: oklch(92.9% 0.013 255.508);\n --color-slate-300: oklch(86.9% 0.022 252.894);\n --color-slate-400: oklch(70.4% 0.04 256.788);\n --color-slate-500: oklch(55.4% 0.046 257.417);\n --color-slate-600: oklch(44.6% 0.043 257.281);\n --color-slate-700: oklch(37.2% 0.044 257.287);\n --color-slate-800: oklch(27.9% 0.041 260.031);\n --color-slate-900: oklch(20.8% 0.042 265.755);\n --color-slate-950: oklch(12.9% 0.042 264.695);\n --color-white: #fff;\n --spacing: 0.25rem;\n --container-2xl: 42rem;\n --container-3xl: 48rem;\n --container-6xl: 72rem;\n --text-xs: 0.75rem;\n --text-xs--line-height: calc(1 / 0.75);\n --text-sm: 0.875rem;\n --text-sm--line-height: calc(1.25 / 0.875);\n --text-lg: 1.125rem;\n --text-lg--line-height: calc(1.75 / 1.125);\n --text-xl: 1.25rem;\n --text-xl--line-height: calc(1.75 / 1.25);\n --text-3xl: 1.875rem;\n --text-3xl--line-height: calc(2.25 / 1.875);\n --text-4xl: 2.25rem;\n --text-4xl--line-height: calc(2.5 / 2.25);\n --font-weight-medium: 500;\n --font-weight-semibold: 600;\n --font-weight-bold: 700;\n --tracking-tight: -0.025em;\n --tracking-wide: 0.025em;\n --radius-lg: 0.5rem;\n --radius-xl: 0.75rem;\n --radius-2xl: 1rem;\n --animate-spin: spin 1s linear infinite;\n --default-transition-duration: 150ms;\n --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n --default-font-family: var(--font-sans);\n --default-mono-font-family: var(--font-mono);\n }\n}\n@layer base {\n *, ::after, ::before, ::backdrop, ::file-selector-button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0 solid;\n }\n html, :host {\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n tab-size: 4;\n font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\");\n font-feature-settings: var(--default-font-feature-settings, normal);\n font-variation-settings: var(--default-font-variation-settings, normal);\n -webkit-tap-highlight-color: transparent;\n }\n hr {\n height: 0;\n color: inherit;\n border-top-width: 1px;\n }\n abbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n }\n h1, h2, h3, h4, h5, h6 {\n font-size: inherit;\n font-weight: inherit;\n }\n a {\n color: inherit;\n -webkit-text-decoration: inherit;\n text-decoration: inherit;\n }\n b, strong {\n font-weight: bolder;\n }\n code, kbd, samp, pre {\n font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace);\n font-feature-settings: var(--default-mono-font-feature-settings, normal);\n font-variation-settings: var(--default-mono-font-variation-settings, normal);\n font-size: 1em;\n }\n small {\n font-size: 80%;\n }\n sub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n }\n sub {\n bottom: -0.25em;\n }\n sup {\n top: -0.5em;\n }\n table {\n text-indent: 0;\n border-color: inherit;\n border-collapse: collapse;\n }\n :-moz-focusring {\n outline: auto;\n }\n progress {\n vertical-align: baseline;\n }\n summary {\n display: list-item;\n }\n ol, ul, menu {\n list-style: none;\n }\n img, svg, video, canvas, audio, iframe, embed, object {\n display: block;\n vertical-align: middle;\n }\n img, video {\n max-width: 100%;\n height: auto;\n }\n button, input, select, optgroup, textarea, ::file-selector-button {\n font: inherit;\n font-feature-settings: inherit;\n font-variation-settings: inherit;\n letter-spacing: inherit;\n color: inherit;\n border-radius: 0;\n background-color: transparent;\n opacity: 1;\n }\n :where(select:is([multiple], [size])) optgroup {\n font-weight: bolder;\n }\n :where(select:is([multiple], [size])) optgroup option {\n padding-inline-start: 20px;\n }\n ::file-selector-button {\n margin-inline-end: 4px;\n }\n ::placeholder {\n opacity: 1;\n }\n @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {\n ::placeholder {\n color: currentcolor;\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, currentcolor 50%, transparent);\n }\n }\n }\n textarea {\n resize: vertical;\n }\n ::-webkit-search-decoration {\n -webkit-appearance: none;\n }\n ::-webkit-date-and-time-value {\n min-height: 1lh;\n text-align: inherit;\n }\n ::-webkit-datetime-edit {\n display: inline-flex;\n }\n ::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n }\n ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {\n padding-block: 0;\n }\n ::-webkit-calendar-picker-indicator {\n line-height: 1;\n }\n :-moz-ui-invalid {\n box-shadow: none;\n }\n button, input:where([type=\"button\"], [type=\"reset\"], [type=\"submit\"]), ::file-selector-button {\n appearance: button;\n }\n ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {\n height: auto;\n }\n [hidden]:where(:not([hidden=\"until-found\"])) {\n display: none !important;\n }\n}\n@layer utilities {\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border-width: 0;\n }\n .relative {\n position: relative;\n }\n .col-span-2 {\n grid-column: span 2 / span 2;\n }\n .mx-auto {\n margin-inline: auto;\n }\n .mb-2 {\n margin-bottom: calc(var(--spacing) * 2);\n }\n .mb-4 {\n margin-bottom: calc(var(--spacing) * 4);\n }\n .contents {\n display: contents;\n }\n .flex {\n display: flex;\n }\n .grid {\n display: grid;\n }\n .inline {\n display: inline;\n }\n .inline-block {\n display: inline-block;\n }\n .inline-flex {\n display: inline-flex;\n }\n .table {\n display: table;\n }\n .h-3 {\n height: calc(var(--spacing) * 3);\n }\n .h-3\\.5 {\n height: calc(var(--spacing) * 3.5);\n }\n .h-4 {\n height: calc(var(--spacing) * 4);\n }\n .h-5 {\n height: calc(var(--spacing) * 5);\n }\n .h-40 {\n height: calc(var(--spacing) * 40);\n }\n .h-56 {\n height: calc(var(--spacing) * 56);\n }\n .min-h-screen {\n min-height: 100vh;\n }\n .w-3 {\n width: calc(var(--spacing) * 3);\n }\n .w-3\\.5 {\n width: calc(var(--spacing) * 3.5);\n }\n .w-4 {\n width: calc(var(--spacing) * 4);\n }\n .w-9 {\n width: calc(var(--spacing) * 9);\n }\n .w-full {\n width: 100%;\n }\n .max-w-2xl {\n max-width: var(--container-2xl);\n }\n .max-w-3xl {\n max-width: var(--container-3xl);\n }\n .max-w-6xl {\n max-width: var(--container-6xl);\n }\n .flex-shrink {\n flex-shrink: 1;\n }\n .shrink-0 {\n flex-shrink: 0;\n }\n .border-collapse {\n border-collapse: collapse;\n }\n .translate-x-0 {\n --tw-translate-x: calc(var(--spacing) * 0);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .translate-x-0\\.5 {\n --tw-translate-x: calc(var(--spacing) * 0.5);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .translate-x-4 {\n --tw-translate-x: calc(var(--spacing) * 4);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .transform {\n transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);\n }\n .animate-spin {\n animation: var(--animate-spin);\n }\n .cursor-pointer {\n cursor: pointer;\n }\n .resize {\n resize: both;\n }\n .grid-cols-1 {\n grid-template-columns: repeat(1, minmax(0, 1fr));\n }\n .flex-col {\n flex-direction: column;\n }\n .items-center {\n align-items: center;\n }\n .justify-between {\n justify-content: space-between;\n }\n .justify-center {\n justify-content: center;\n }\n .gap-2 {\n gap: calc(var(--spacing) * 2);\n }\n .gap-3 {\n gap: calc(var(--spacing) * 3);\n }\n .gap-4 {\n gap: calc(var(--spacing) * 4);\n }\n .gap-6 {\n gap: calc(var(--spacing) * 6);\n }\n .space-y-0 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-0\\.5 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 0.5) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 0.5) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-1 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-2 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-3 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-4 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-5 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-6 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-10 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 10) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 10) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .rounded {\n border-radius: 0.25rem;\n }\n .rounded-2xl {\n border-radius: var(--radius-2xl);\n }\n .rounded-full {\n border-radius: calc(infinity * 1px);\n }\n .rounded-lg {\n border-radius: var(--radius-lg);\n }\n .rounded-xl {\n border-radius: var(--radius-xl);\n }\n .border {\n border-style: var(--tw-border-style);\n border-width: 1px;\n }\n .border-2 {\n border-style: var(--tw-border-style);\n border-width: 2px;\n }\n .border-amber-200 {\n border-color: var(--color-amber-200);\n }\n .border-amber-300 {\n border-color: var(--color-amber-300);\n }\n .border-red-300 {\n border-color: var(--color-red-300);\n }\n .border-red-700 {\n border-color: var(--color-red-700);\n }\n .border-slate-100 {\n border-color: var(--color-slate-100);\n }\n .border-slate-200 {\n border-color: var(--color-slate-200);\n }\n .border-slate-300 {\n border-color: var(--color-slate-300);\n }\n .border-t-slate-900 {\n border-top-color: var(--color-slate-900);\n }\n .bg-amber-50 {\n background-color: var(--color-amber-50);\n }\n .bg-emerald-50 {\n background-color: var(--color-emerald-50);\n }\n .bg-indigo-600 {\n background-color: var(--color-indigo-600);\n }\n .bg-red-50 {\n background-color: var(--color-red-50);\n }\n .bg-red-900 {\n background-color: var(--color-red-900);\n }\n .bg-red-900\\/20 {\n background-color: color-mix(in srgb, oklch(39.6% 0.141 25.723) 20%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-red-900) 20%, transparent);\n }\n }\n .bg-rose-50 {\n background-color: var(--color-rose-50);\n }\n .bg-slate-50 {\n background-color: var(--color-slate-50);\n }\n .bg-slate-300 {\n background-color: var(--color-slate-300);\n }\n .bg-slate-900 {\n background-color: var(--color-slate-900);\n }\n .bg-white {\n background-color: var(--color-white);\n }\n .bg-white\\/70 {\n background-color: color-mix(in srgb, #fff 70%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 70%, transparent);\n }\n }\n .bg-white\\/90 {\n background-color: color-mix(in srgb, #fff 90%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-white) 90%, transparent);\n }\n }\n .p-3 {\n padding: calc(var(--spacing) * 3);\n }\n .p-4 {\n padding: calc(var(--spacing) * 4);\n }\n .p-5 {\n padding: calc(var(--spacing) * 5);\n }\n .p-6 {\n padding: calc(var(--spacing) * 6);\n }\n .px-2 {\n padding-inline: calc(var(--spacing) * 2);\n }\n .px-3 {\n padding-inline: calc(var(--spacing) * 3);\n }\n .px-6 {\n padding-inline: calc(var(--spacing) * 6);\n }\n .py-1 {\n padding-block: calc(var(--spacing) * 1);\n }\n .py-1\\.5 {\n padding-block: calc(var(--spacing) * 1.5);\n }\n .py-2 {\n padding-block: calc(var(--spacing) * 2);\n }\n .py-2\\.5 {\n padding-block: calc(var(--spacing) * 2.5);\n }\n .py-6 {\n padding-block: calc(var(--spacing) * 6);\n }\n .py-8 {\n padding-block: calc(var(--spacing) * 8);\n }\n .py-10 {\n padding-block: calc(var(--spacing) * 10);\n }\n .font-mono {\n font-family: var(--font-mono);\n }\n .text-3xl {\n font-size: var(--text-3xl);\n line-height: var(--tw-leading, var(--text-3xl--line-height));\n }\n .text-lg {\n font-size: var(--text-lg);\n line-height: var(--tw-leading, var(--text-lg--line-height));\n }\n .text-sm {\n font-size: var(--text-sm);\n line-height: var(--tw-leading, var(--text-sm--line-height));\n }\n .text-xl {\n font-size: var(--text-xl);\n line-height: var(--tw-leading, var(--text-xl--line-height));\n }\n .text-xs {\n font-size: var(--text-xs);\n line-height: var(--tw-leading, var(--text-xs--line-height));\n }\n .text-\\[10px\\] {\n font-size: 10px;\n }\n .text-\\[11px\\] {\n font-size: 11px;\n }\n .font-bold {\n --tw-font-weight: var(--font-weight-bold);\n font-weight: var(--font-weight-bold);\n }\n .font-medium {\n --tw-font-weight: var(--font-weight-medium);\n font-weight: var(--font-weight-medium);\n }\n .font-semibold {\n --tw-font-weight: var(--font-weight-semibold);\n font-weight: var(--font-weight-semibold);\n }\n .tracking-\\[0\\.2em\\] {\n --tw-tracking: 0.2em;\n letter-spacing: 0.2em;\n }\n .tracking-tight {\n --tw-tracking: var(--tracking-tight);\n letter-spacing: var(--tracking-tight);\n }\n .tracking-wide {\n --tw-tracking: var(--tracking-wide);\n letter-spacing: var(--tracking-wide);\n }\n .whitespace-pre-wrap {\n white-space: pre-wrap;\n }\n .text-amber-600 {\n color: var(--color-amber-600);\n }\n .text-amber-700 {\n color: var(--color-amber-700);\n }\n .text-amber-800 {\n color: var(--color-amber-800);\n }\n .text-emerald-700 {\n color: var(--color-emerald-700);\n }\n .text-indigo-500 {\n color: var(--color-indigo-500);\n }\n .text-rose-500 {\n color: var(--color-rose-500);\n }\n .text-rose-600 {\n color: var(--color-rose-600);\n }\n .text-rose-700 {\n color: var(--color-rose-700);\n }\n .text-slate-400 {\n color: var(--color-slate-400);\n }\n .text-slate-500 {\n color: var(--color-slate-500);\n }\n .text-slate-600 {\n color: var(--color-slate-600);\n }\n .text-slate-700 {\n color: var(--color-slate-700);\n }\n .text-slate-800 {\n color: var(--color-slate-800);\n }\n .text-slate-900 {\n color: var(--color-slate-900);\n }\n .text-white {\n color: var(--color-white);\n }\n .uppercase {\n text-transform: uppercase;\n }\n .italic {\n font-style: italic;\n }\n .underline {\n text-decoration-line: underline;\n }\n .opacity-25 {\n opacity: 25%;\n }\n .opacity-75 {\n opacity: 75%;\n }\n .shadow {\n --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n .shadow-sm {\n --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n .outline {\n outline-style: var(--tw-outline-style);\n outline-width: 1px;\n }\n .backdrop-blur {\n --tw-backdrop-blur: blur(8px);\n -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n }\n .backdrop-filter {\n -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);\n }\n .transition {\n transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .transition-all {\n transition-property: all;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .transition-colors {\n transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .transition-transform {\n transition-property: transform, translate, scale, rotate;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .duration-200 {\n --tw-duration: 200ms;\n transition-duration: 200ms;\n }\n .focus-within\\:border-indigo-300 {\n &:focus-within {\n border-color: var(--color-indigo-300);\n }\n }\n .focus-within\\:ring-2 {\n &:focus-within {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n .hover\\:-translate-y-0\\.5 {\n &:hover {\n @media (hover: hover) {\n --tw-translate-y: calc(var(--spacing) * -0.5);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n }\n }\n .hover\\:border-slate-300 {\n &:hover {\n @media (hover: hover) {\n border-color: var(--color-slate-300);\n }\n }\n }\n .hover\\:border-slate-400 {\n &:hover {\n @media (hover: hover) {\n border-color: var(--color-slate-400);\n }\n }\n }\n .hover\\:bg-slate-50 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--color-slate-50);\n }\n }\n }\n .hover\\:bg-slate-800 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--color-slate-800);\n }\n }\n }\n .hover\\:text-rose-700 {\n &:hover {\n @media (hover: hover) {\n color: var(--color-rose-700);\n }\n }\n }\n .hover\\:shadow {\n &:hover {\n @media (hover: hover) {\n --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n }\n .hover\\:shadow-lg {\n &:hover {\n @media (hover: hover) {\n --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n }\n .hover\\:shadow-md {\n &:hover {\n @media (hover: hover) {\n --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n }\n .focus\\:border-indigo-400 {\n &:focus {\n border-color: var(--color-indigo-400);\n }\n }\n .focus\\:ring-2 {\n &:focus {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n .focus\\:ring-indigo-100 {\n &:focus {\n --tw-ring-color: var(--color-indigo-100);\n }\n }\n .focus\\:ring-indigo-500 {\n &:focus {\n --tw-ring-color: var(--color-indigo-500);\n }\n }\n .focus\\:ring-slate-900 {\n &:focus {\n --tw-ring-color: var(--color-slate-900);\n }\n }\n .focus\\:outline-none {\n &:focus {\n --tw-outline-style: none;\n outline-style: none;\n }\n }\n .sm\\:grid-cols-2 {\n @media (width >= 40rem) {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n }\n .md\\:grid-cols-2 {\n @media (width >= 48rem) {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n }\n .md\\:flex-row {\n @media (width >= 48rem) {\n flex-direction: row;\n }\n }\n .md\\:items-center {\n @media (width >= 48rem) {\n align-items: center;\n }\n }\n .md\\:justify-between {\n @media (width >= 48rem) {\n justify-content: space-between;\n }\n }\n .md\\:p-6 {\n @media (width >= 48rem) {\n padding: calc(var(--spacing) * 6);\n }\n }\n .md\\:text-4xl {\n @media (width >= 48rem) {\n font-size: var(--text-4xl);\n line-height: var(--tw-leading, var(--text-4xl--line-height));\n }\n }\n .lg\\:grid-cols-\\[360px\\,1fr\\] {\n @media (width >= 64rem) {\n grid-template-columns: 360px,1fr;\n }\n }\n .dark\\:border-amber-700 {\n &:where(.dark, .dark *) {\n border-color: var(--color-amber-700);\n }\n }\n .dark\\:border-amber-800 {\n &:where(.dark, .dark *) {\n border-color: var(--color-amber-800);\n }\n }\n .dark\\:border-slate-600 {\n &:where(.dark, .dark *) {\n border-color: var(--color-slate-600);\n }\n }\n .dark\\:border-slate-700 {\n &:where(.dark, .dark *) {\n border-color: var(--color-slate-700);\n }\n }\n .dark\\:border-t-indigo-400 {\n &:where(.dark, .dark *) {\n border-top-color: var(--color-indigo-400);\n }\n }\n .dark\\:bg-amber-900\\/20 {\n &:where(.dark, .dark *) {\n background-color: color-mix(in srgb, oklch(41.4% 0.112 45.904) 20%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-amber-900) 20%, transparent);\n }\n }\n }\n .dark\\:bg-emerald-900\\/30 {\n &:where(.dark, .dark *) {\n background-color: color-mix(in srgb, oklch(37.8% 0.077 168.94) 30%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-emerald-900) 30%, transparent);\n }\n }\n }\n .dark\\:bg-indigo-600 {\n &:where(.dark, .dark *) {\n background-color: var(--color-indigo-600);\n }\n }\n .dark\\:bg-rose-900\\/30 {\n &:where(.dark, .dark *) {\n background-color: color-mix(in srgb, oklch(41% 0.159 10.272) 30%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-rose-900) 30%, transparent);\n }\n }\n }\n .dark\\:bg-slate-600 {\n &:where(.dark, .dark *) {\n background-color: var(--color-slate-600);\n }\n }\n .dark\\:bg-slate-700 {\n &:where(.dark, .dark *) {\n background-color: var(--color-slate-700);\n }\n }\n .dark\\:bg-slate-800 {\n &:where(.dark, .dark *) {\n background-color: var(--color-slate-800);\n }\n }\n .dark\\:bg-slate-800\\/50 {\n &:where(.dark, .dark *) {\n background-color: color-mix(in srgb, oklch(27.9% 0.041 260.031) 50%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-slate-800) 50%, transparent);\n }\n }\n }\n .dark\\:bg-slate-800\\/60 {\n &:where(.dark, .dark *) {\n background-color: color-mix(in srgb, oklch(27.9% 0.041 260.031) 60%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-slate-800) 60%, transparent);\n }\n }\n }\n .dark\\:bg-slate-900 {\n &:where(.dark, .dark *) {\n background-color: var(--color-slate-900);\n }\n }\n .dark\\:bg-slate-900\\/90 {\n &:where(.dark, .dark *) {\n background-color: color-mix(in srgb, oklch(20.8% 0.042 265.755) 90%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-slate-900) 90%, transparent);\n }\n }\n }\n .dark\\:bg-slate-950 {\n &:where(.dark, .dark *) {\n background-color: var(--color-slate-950);\n }\n }\n .dark\\:text-amber-300 {\n &:where(.dark, .dark *) {\n color: var(--color-amber-300);\n }\n }\n .dark\\:text-amber-400 {\n &:where(.dark, .dark *) {\n color: var(--color-amber-400);\n }\n }\n .dark\\:text-emerald-400 {\n &:where(.dark, .dark *) {\n color: var(--color-emerald-400);\n }\n }\n .dark\\:text-indigo-400 {\n &:where(.dark, .dark *) {\n color: var(--color-indigo-400);\n }\n }\n .dark\\:text-rose-400 {\n &:where(.dark, .dark *) {\n color: var(--color-rose-400);\n }\n }\n .dark\\:text-slate-100 {\n &:where(.dark, .dark *) {\n color: var(--color-slate-100);\n }\n }\n .dark\\:text-slate-200 {\n &:where(.dark, .dark *) {\n color: var(--color-slate-200);\n }\n }\n .dark\\:text-slate-300 {\n &:where(.dark, .dark *) {\n color: var(--color-slate-300);\n }\n }\n .dark\\:text-slate-400 {\n &:where(.dark, .dark *) {\n color: var(--color-slate-400);\n }\n }\n .dark\\:text-slate-500 {\n &:where(.dark, .dark *) {\n color: var(--color-slate-500);\n }\n }\n .dark\\:text-white {\n &:where(.dark, .dark *) {\n color: var(--color-white);\n }\n }\n .dark\\:hover\\:border-slate-500 {\n &:where(.dark, .dark *) {\n &:hover {\n @media (hover: hover) {\n border-color: var(--color-slate-500);\n }\n }\n }\n }\n .dark\\:hover\\:bg-indigo-700 {\n &:where(.dark, .dark *) {\n &:hover {\n @media (hover: hover) {\n background-color: var(--color-indigo-700);\n }\n }\n }\n }\n .dark\\:hover\\:bg-slate-700 {\n &:where(.dark, .dark *) {\n &:hover {\n @media (hover: hover) {\n background-color: var(--color-slate-700);\n }\n }\n }\n }\n .dark\\:focus\\:ring-indigo-900\\/40 {\n &:where(.dark, .dark *) {\n &:focus {\n --tw-ring-color: color-mix(in srgb, oklch(35.9% 0.144 278.697) 40%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n --tw-ring-color: color-mix(in oklab, var(--color-indigo-900) 40%, transparent);\n }\n }\n }\n }\n}\n@property --tw-translate-x {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-y {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-z {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-rotate-x {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-rotate-y {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-rotate-z {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-skew-x {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-skew-y {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-space-y-reverse {\n syntax: \"*\";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-border-style {\n syntax: \"*\";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-font-weight {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-tracking {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-shadow-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-shadow-alpha {\n syntax: \"<percentage>\";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-inset-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-shadow-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-inset-shadow-alpha {\n syntax: \"<percentage>\";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-ring-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-ring-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-ring-color {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-inset-ring-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-ring-inset {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-ring-offset-width {\n syntax: \"<length>\";\n inherits: false;\n initial-value: 0px;\n}\n@property --tw-ring-offset-color {\n syntax: \"*\";\n inherits: false;\n initial-value: #fff;\n}\n@property --tw-ring-offset-shadow {\n syntax: \"*\";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-outline-style {\n syntax: \"*\";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-backdrop-blur {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-brightness {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-contrast {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-grayscale {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-hue-rotate {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-invert {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-opacity {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-saturate {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-backdrop-sepia {\n syntax: \"*\";\n inherits: false;\n}\n@property --tw-duration {\n syntax: \"*\";\n inherits: false;\n}\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n@layer properties {\n @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {\n *, ::before, ::after, ::backdrop {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-translate-z: 0;\n --tw-rotate-x: initial;\n --tw-rotate-y: initial;\n --tw-rotate-z: initial;\n --tw-skew-x: initial;\n --tw-skew-y: initial;\n --tw-space-y-reverse: 0;\n --tw-border-style: solid;\n --tw-font-weight: initial;\n --tw-tracking: initial;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-color: initial;\n --tw-shadow-alpha: 100%;\n --tw-inset-shadow: 0 0 #0000;\n --tw-inset-shadow-color: initial;\n --tw-inset-shadow-alpha: 100%;\n --tw-ring-color: initial;\n --tw-ring-shadow: 0 0 #0000;\n --tw-inset-ring-color: initial;\n --tw-inset-ring-shadow: 0 0 #0000;\n --tw-ring-inset: initial;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-outline-style: solid;\n --tw-backdrop-blur: initial;\n --tw-backdrop-brightness: initial;\n --tw-backdrop-contrast: initial;\n --tw-backdrop-grayscale: initial;\n --tw-backdrop-hue-rotate: initial;\n --tw-backdrop-invert: initial;\n --tw-backdrop-opacity: initial;\n --tw-backdrop-saturate: initial;\n --tw-backdrop-sepia: initial;\n --tw-duration: initial;\n }\n }\n}\n";
1382
+
1383
+ const STYLE_ID = 'jsm-tailwind-styles';
1384
+ class JsonSchemaStylesService {
1385
+ platformId;
1386
+ injected = false;
1387
+ constructor(platformId) {
1388
+ this.platformId = platformId;
1389
+ }
1390
+ inject() {
1391
+ if (this.injected || !isPlatformBrowser(this.platformId))
1392
+ return;
1393
+ if (typeof document === 'undefined')
1394
+ return;
1395
+ if (document.getElementById(STYLE_ID)) {
1396
+ this.injected = true;
1397
+ return;
1398
+ }
1399
+ const style = document.createElement('style');
1400
+ style.id = STYLE_ID;
1401
+ style.textContent = JSM_CSS;
1402
+ document.head.appendChild(style);
1403
+ this.injected = true;
1404
+ }
1405
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: JsonSchemaStylesService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
1406
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: JsonSchemaStylesService, providedIn: 'root' });
1407
+ }
1408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: JsonSchemaStylesService, decorators: [{
1409
+ type: Injectable,
1410
+ args: [{ providedIn: 'root' }]
1411
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1412
+ type: Inject,
1413
+ args: [PLATFORM_ID]
1414
+ }] }] });
1415
+
1650
1416
  class JsonSchemaFormComponent {
1651
1417
  schemaService;
1652
1418
  resolver;
@@ -1666,10 +1432,11 @@ class JsonSchemaFormComponent {
1666
1432
  schemaErrorList = [];
1667
1433
  loading = false;
1668
1434
  valueSub;
1669
- constructor(schemaService, resolver, validation) {
1435
+ constructor(schemaService, resolver, validation, stylesService) {
1670
1436
  this.schemaService = schemaService;
1671
1437
  this.resolver = resolver;
1672
1438
  this.validation = validation;
1439
+ stylesService.inject();
1673
1440
  }
1674
1441
  ngOnChanges(changes) {
1675
1442
  if (changes['schema'] || changes['value'] || changes['data']) {
@@ -1739,7 +1506,7 @@ class JsonSchemaFormComponent {
1739
1506
  this.valueChange.emit(this.rootControl.value);
1740
1507
  });
1741
1508
  }
1742
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: JsonSchemaFormComponent, deps: [{ token: JsonSchemaFormService }, { token: JsonSchemaResolverService }, { token: JsonSchemaValidationService }], target: i0.ɵɵFactoryTarget.Component });
1509
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: JsonSchemaFormComponent, deps: [{ token: JsonSchemaFormService }, { token: JsonSchemaResolverService }, { token: JsonSchemaValidationService }, { token: JsonSchemaStylesService }], target: i0.ɵɵFactoryTarget.Component });
1743
1510
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: JsonSchemaFormComponent, isStandalone: true, selector: "jsm-json-schema-form", inputs: { schema: "schema", value: "value", data: "data", allowAdditionalProperties: "allowAdditionalProperties" }, outputs: { formReady: "formReady", valueChange: "valueChange", schemaReady: "schemaReady", schemaErrors: "schemaErrors" }, usesOnChanges: true, ngImport: i0, template: `
1744
1511
  <div class="jsm-root">
1745
1512
  <form class="w-full space-y-5">
@@ -1855,7 +1622,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1855
1622
  </form>
1856
1623
  </div>
1857
1624
  `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".jsm-root *,.jsm-root *:before,.jsm-root *:after{box-sizing:border-box}.jsm-root input,.jsm-root textarea,.jsm-root select,.jsm-root button{font-family:inherit;font-size:inherit;line-height:inherit}.jsm-root input[type=number]::-webkit-inner-spin-button,.jsm-root input[type=number]::-webkit-outer-spin-button{opacity:1}.jsm-root input:not([type=checkbox]):not([type=radio]):not(.sr-only),.jsm-root textarea,.jsm-root select{border-width:1.5px!important;border-style:solid!important}\n"] }]
1858
- }], ctorParameters: () => [{ type: JsonSchemaFormService }, { type: JsonSchemaResolverService }, { type: JsonSchemaValidationService }], propDecorators: { schema: [{
1625
+ }], ctorParameters: () => [{ type: JsonSchemaFormService }, { type: JsonSchemaResolverService }, { type: JsonSchemaValidationService }, { type: JsonSchemaStylesService }], propDecorators: { schema: [{
1859
1626
  type: Input,
1860
1627
  args: [{ required: true }]
1861
1628
  }], value: [{
@@ -1891,5 +1658,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1891
1658
  * Generated bundle index. Do not edit.
1892
1659
  */
1893
1660
 
1894
- export { JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonSchemaNodeComponent, JsonSchemaResolverService, JsonSchemaValidationService };
1661
+ export { JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonSchemaNodeComponent, JsonSchemaResolverService, JsonSchemaStylesService, JsonSchemaValidationService };
1895
1662
  //# sourceMappingURL=elle96-ng-json-schema-manager.mjs.map