@elle96/ng-json-schema-manager 0.1.2 → 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>
979
-
980
- <p *ngIf="schema.description" class="text-xs text-slate-500 dark:text-slate-400 mb-3 mt-1">{{ schema.description }}</p>
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>
981
939
 
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,119 +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"
1063
+ <textarea *ngSwitchCase="'textarea'" rows="4" class="jsm-textarea"
1064
+ [class.jsm-textarea--error]="errorsForPath.length && controlAsFormControl.dirty"
1165
1065
  [attr.placeholder]="schema.title || label"
1166
- [formControl]="controlAsFormControl"
1167
- />
1066
+ [formControl]="controlAsFormControl"></textarea>
1168
1067
 
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"
1182
- [attr.placeholder]="schema.title || label"
1183
- [formControl]="controlAsFormControl"
1184
- ></textarea>
1185
-
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="jsm-toggle-track" [class.jsm-toggle-on]="controlAsFormControl.value">
1079
+ <label *ngSwitchCase="'checkbox'" class="jsm-toggle-label">
1080
+ <span class="jsm-toggle-track" [class.jsm-on]="controlAsFormControl.value">
1206
1081
  <input type="checkbox" class="sr-only" [formControl]="controlAsFormControl" />
1207
1082
  <span class="jsm-toggle-thumb"></span>
1208
1083
  </span>
1209
- <span class="text-sm font-medium text-slate-700 dark:text-slate-300">
1210
- {{ 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>
1211
1086
  </span>
1212
1087
  </label>
1213
-
1214
1088
  </ng-container>
1215
1089
 
1216
- <p *ngIf="inputKind === 'checkbox' && schema.description" class="text-xs text-slate-500 dark:text-slate-400">{{ schema.description }}</p>
1217
-
1218
- <p *ngIf="effectiveSchema.examples?.length" class="text-[11px] text-slate-400 dark:text-slate-500">
1219
- e.g. {{ effectiveSchema.examples?.[0] }}
1220
- </p>
1221
-
1222
- <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">
1223
1093
  {{ effectiveSchema.contentMediaType }}<span *ngIf="effectiveSchema.contentEncoding"> ({{ effectiveSchema.contentEncoding }})</span>
1224
1094
  </p>
1225
1095
 
1226
- <!-- icona errore: funzionale, segnala un problema di validazione -->
1227
- <div *ngIf="errorsForPath.length && controlAsFormControl.dirty" class="space-y-1 pt-0.5">
1228
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1229
- <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">
1230
1099
  <circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/>
1231
1100
  <path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
1232
1101
  </svg>
@@ -1236,40 +1105,30 @@ class JsonSchemaNodeComponent {
1236
1105
  </div>
1237
1106
 
1238
1107
  </ng-container>
1239
- `, isInline: true, styles: [".jsm-toggle-track{position:relative;display:inline-flex;align-items:center;width:2.25rem;height:1.25rem;border-radius:9999px;flex-shrink:0;transition:background-color .2s;background-color:#cbd5e1}.jsm-toggle-track.jsm-toggle-on{background-color:#4f46e5}.dark .jsm-toggle-track:not(.jsm-toggle-on){background-color:#475569}.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-toggle-on .jsm-toggle-thumb{transform:translate(1.125rem)}\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 });
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 });
1240
1109
  }
1241
1110
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: JsonSchemaNodeComponent, decorators: [{
1242
1111
  type: Component,
1243
1112
  args: [{ selector: 'jsm-schema-node', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule], encapsulation: ViewEncapsulation.None, template: `
1244
1113
  <ng-container [ngSwitch]="nodeKind">
1245
1114
 
1246
- <!-- ═══════════════════════════════════════════════════════════
1247
- oneOf — tab bar: ogni opzione è una tab, nessuna icona
1248
- ═══════════════════════════════════════════════════════════ -->
1249
- <div *ngSwitchCase="'oneOf'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
1250
- <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">
1251
1118
  <div>
1252
- <p class="text-sm font-semibold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</p>
1253
- <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>
1254
1121
  </div>
1255
- <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>
1256
1123
  </div>
1257
- <div class="flex overflow-x-auto border-b border-slate-200 dark:border-slate-700">
1258
- <button
1259
- *ngFor="let option of schema.oneOf; let i = index"
1260
- type="button"
1261
- class="px-4 py-2.5 text-xs font-medium whitespace-nowrap border-b-2 transition-colors"
1262
- [class.border-indigo-500]="selectedOneOf === i"
1263
- [class.text-indigo-600]="selectedOneOf === i"
1264
- [class.dark:text-indigo-400]="selectedOneOf === i"
1265
- [class.font-semibold]="selectedOneOf === i"
1266
- [class.border-transparent]="selectedOneOf !== i"
1267
- [class.text-slate-500]="selectedOneOf !== i"
1268
- [class.dark:text-slate-400]="selectedOneOf !== i"
1269
- (click)="selectedOneOf = i; onOneOfChange()"
1270
- >{{ 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>
1271
1130
  </div>
1272
- <div class="p-4" *ngIf="activeVariant">
1131
+ <div class="jsm-combinator-body" *ngIf="activeVariant">
1273
1132
  <jsm-schema-node [schema]="activeVariant" [control]="control" [parent]="parent" [controlKey]="controlKey"
1274
1133
  [path]="path" [errorsMap]="errorsMap" [label]="label" [required]="required"
1275
1134
  [allowAdditionalProperties]="allowAdditionalProperties"
@@ -1277,36 +1136,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1277
1136
  </div>
1278
1137
  </div>
1279
1138
 
1280
- <!-- ═══════════════════════════════════════════════════════════
1281
- anyOf — pill toggle, nessuna icona
1282
- ═══════════════════════════════════════════════════════════ -->
1283
- <div *ngSwitchCase="'anyOf'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
1284
- <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">
1285
1142
  <div>
1286
- <p class="text-sm font-semibold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</p>
1287
- <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>
1288
1145
  </div>
1289
- <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>
1290
1147
  </div>
1291
- <div class="flex flex-wrap gap-2 px-4 py-3 border-b border-slate-200 dark:border-slate-700">
1292
- <label
1293
- *ngFor="let option of schema.anyOf; let i = index"
1294
- 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"
1295
- [class.border-indigo-400]="selectedAnyOf.has(i)"
1296
- [class.bg-indigo-50]="selectedAnyOf.has(i)"
1297
- [class.dark:bg-indigo-900/30]="selectedAnyOf.has(i)"
1298
- [class.text-indigo-700]="selectedAnyOf.has(i)"
1299
- [class.dark:text-indigo-400]="selectedAnyOf.has(i)"
1300
- [class.border-slate-200]="!selectedAnyOf.has(i)"
1301
- [class.dark:border-slate-600]="!selectedAnyOf.has(i)"
1302
- [class.text-slate-600]="!selectedAnyOf.has(i)"
1303
- [class.dark:text-slate-400]="!selectedAnyOf.has(i)"
1304
- >
1305
- <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)" />
1306
1152
  {{ option.title || ('Option ' + (i + 1)) }}
1307
1153
  </label>
1308
1154
  </div>
1309
- <div class="p-4" *ngIf="activeVariant">
1155
+ <div class="jsm-combinator-body" *ngIf="activeVariant">
1310
1156
  <jsm-schema-node [schema]="activeVariant" [control]="control" [parent]="parent" [controlKey]="controlKey"
1311
1157
  [path]="path" [errorsMap]="errorsMap" [label]="label" [required]="required"
1312
1158
  [allowAdditionalProperties]="allowAdditionalProperties"
@@ -1314,19 +1160,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1314
1160
  </div>
1315
1161
  </div>
1316
1162
 
1317
- <!-- ═══════════════════════════════════════════════════════════
1318
- allOf — sezioni in colonna con bordo sinistro
1319
- ═══════════════════════════════════════════════════════════ -->
1320
- <div *ngSwitchCase="'allOf'" class="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
1321
- <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">
1322
1166
  <div>
1323
- <p class="text-sm font-semibold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</p>
1324
- <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>
1325
1169
  </div>
1326
- <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>
1327
1171
  </div>
1328
- <div class="p-4 space-y-4">
1329
- <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">
1330
1174
  <jsm-schema-node [schema]="subSchema" [control]="control" [parent]="parent" [controlKey]="controlKey"
1331
1175
  [path]="path" [errorsMap]="errorsMap" [label]="subSchema.title || ('Section ' + (i + 1))" [required]="required"
1332
1176
  [allowAdditionalProperties]="allowAdditionalProperties"
@@ -1335,145 +1179,95 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1335
1179
  </div>
1336
1180
  </div>
1337
1181
 
1338
- <!-- ═══════════════════════════════════════════════════════════
1339
- object — fieldset con griglia SOLO per campi primitivi.
1340
- Logica layout:
1341
- - I figli vengono classificati come "semplici" (primitive/enum/bool)
1342
- o "complessi" (object/array/oneOf/anyOf/allOf).
1343
- - I semplici vanno in griglia 2 colonne (sm:grid-cols-2).
1344
- - I complessi vanno sempre in colonna piena (col-span-2).
1345
- ═══════════════════════════════════════════════════════════ -->
1346
- <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">
1347
- <legend class="px-2 text-sm font-bold text-slate-800 dark:text-slate-200">{{ schema.title || label }}</legend>
1348
-
1349
- <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>
1350
1186
 
1351
- <div *ngIf="metaBadges.length" class="flex flex-wrap gap-1.5 mb-3">
1352
- <span *ngFor="let badge of metaBadges"
1353
- class="rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide"
1354
- [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>
1355
1189
  </div>
1356
1190
 
1357
- <!-- campi semplici (string/number/bool/enum) → griglia 2 colonne -->
1358
- <div class="grid grid-cols-1 sm:grid-cols-2 gap-4" [formGroup]="controlAsGroup">
1191
+ <div class="jsm-obj-grid" [formGroup]="controlAsGroup">
1359
1192
  <ng-container *ngFor="let key of simpleKeys">
1360
- <div class="flex flex-col gap-1">
1361
- <div *ngIf="isDynamicKey(key)"
1362
- 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">
1363
- <span class="font-mono text-xs text-slate-500 dark:text-slate-400">{{ key }}</span>
1364
- <button type="button"
1365
- 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"
1366
- (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>
1367
1197
  </div>
1368
- <jsm-schema-node
1369
- [schema]="schemaForKey(key)"
1370
- [control]="childControl(key)"
1371
- [parent]="controlAsGroup"
1372
- [controlKey]="key"
1373
- [path]="pathForChild(key)"
1374
- [errorsMap]="errorsMap"
1375
- [label]="key"
1376
- [required]="isRequired(key)"
1377
- [allowAdditionalProperties]="allowAdditionalProperties"
1378
- (controlReplaced)="controlReplaced.emit($event)">
1379
- </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>
1380
1202
  </div>
1381
1203
  </ng-container>
1382
1204
  </div>
1383
1205
 
1384
- <!-- campi complessi (object/array/combinator) → colonna piena -->
1385
- <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">
1386
1207
  <ng-container *ngFor="let key of complexKeys">
1387
- <div class="flex flex-col gap-1">
1388
- <div *ngIf="isDynamicKey(key)"
1389
- 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">
1390
- <span class="font-mono text-xs text-slate-500 dark:text-slate-400">{{ key }}</span>
1391
- <button type="button"
1392
- 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"
1393
- (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>
1394
1212
  </div>
1395
- <jsm-schema-node
1396
- [schema]="schemaForKey(key)"
1397
- [control]="childControl(key)"
1398
- [parent]="controlAsGroup"
1399
- [controlKey]="key"
1400
- [path]="pathForChild(key)"
1401
- [errorsMap]="errorsMap"
1402
- [label]="key"
1403
- [required]="isRequired(key)"
1404
- [allowAdditionalProperties]="allowAdditionalProperties"
1405
- (controlReplaced)="controlReplaced.emit($event)">
1406
- </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>
1407
1217
  </div>
1408
1218
  </ng-container>
1409
1219
  </div>
1410
1220
 
1411
- <!-- add dynamic property -->
1412
- <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">
1413
- <p class="text-[10px] font-bold uppercase tracking-widest text-slate-400 dark:text-slate-500">Add property</p>
1414
- <div class="flex gap-2">
1415
- <input
1416
- 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"
1417
- [(ngModel)]="newPropertyKey" placeholder="propertyName" />
1418
- <button type="button"
1419
- 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"
1420
- (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>
1421
1226
  </div>
1422
- <p *ngIf="propertyError" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1423
- <!-- icona errore: unico caso in cui ha senso, segnala un problema -->
1424
- <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>
1425
1229
  {{ propertyError }}
1426
1230
  </p>
1427
1231
  </div>
1428
1232
 
1429
- <div *ngIf="errorsForPath.length" class="mt-3 space-y-1">
1430
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1431
- <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>
1432
1236
  {{ error }}
1433
1237
  </p>
1434
1238
  </div>
1435
1239
  </fieldset>
1436
1240
 
1437
- <!-- ═══════════════════════════════════════════════════════════
1438
- array — sempre in colonna piena, items in colonna
1439
- ═══════════════════════════════════════════════════════════ -->
1440
- <div *ngSwitchCase="'array'" class="space-y-3">
1441
- <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">
1442
1244
  <div>
1443
- <p class="text-sm font-semibold text-slate-800">
1444
- {{ 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>
1445
1247
  </p>
1446
- <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>
1447
1249
  </div>
1448
- <!-- icona + nel bottone: indica l'azione "aggiungi", non decorativa -->
1449
- <button type="button"
1450
- 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"
1451
- (click)="addArrayItem()" [disabled]="!canAddArrayItem">
1452
- <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>
1453
1252
  Add item
1454
1253
  </button>
1455
1254
  </div>
1456
1255
 
1457
- <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">
1458
1257
  Must contain matching item(s)
1459
1258
  <span *ngIf="effectiveSchema.minContains"> · min {{ effectiveSchema.minContains }}</span>
1460
1259
  <span *ngIf="effectiveSchema.maxContains"> · max {{ effectiveSchema.maxContains }}</span>
1461
1260
  </div>
1462
1261
 
1463
- <div *ngIf="metaBadges.length" class="flex flex-wrap gap-1.5">
1464
- <span *ngFor="let badge of metaBadges"
1465
- class="rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide"
1466
- [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>
1467
1264
  </div>
1468
1265
 
1469
- <div class="space-y-2" *ngIf="arrayControls.length > 0; else emptyArray">
1470
- <div *ngFor="let item of arrayControls; let i = index"
1471
- 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">
1472
- <div class="flex items-center justify-between mb-3">
1473
- <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>
1474
- <button type="button"
1475
- 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"
1476
- (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>
1477
1271
  </div>
1478
1272
  <jsm-schema-node [schema]="schemaForIndex(i)" [control]="item" [parent]="controlAsArray"
1479
1273
  [controlKey]="i" [path]="pathForChild(i)" [errorsMap]="errorsMap" [label]="label + ' item'"
@@ -1483,119 +1277,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1483
1277
  </div>
1484
1278
 
1485
1279
  <ng-template #emptyArray>
1486
- <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">
1487
- No items yet — click "Add item" to start.
1488
- </div>
1280
+ <div class="jsm-empty-array">No items yet — click "Add item" to start.</div>
1489
1281
  </ng-template>
1490
1282
 
1491
- <div *ngIf="errorsForPath.length" class="space-y-1">
1492
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1493
- <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>
1494
1286
  {{ error }}
1495
1287
  </p>
1496
1288
  </div>
1497
1289
  </div>
1498
1290
 
1499
- <!-- ═══════════════════════════════════════════════════════════
1500
- primitive — sempre in colonna, nessuna icona decorativa.
1501
- Icona errore solo nei messaggi di validazione.
1502
- Icona chevron nel select: funzionale (indica dropdown).
1503
- ═══════════════════════════════════════════════════════════ -->
1504
- <div *ngSwitchDefault class="space-y-1.5">
1291
+ <!-- ═══ primitive ═══ -->
1292
+ <div *ngSwitchDefault class="jsm-field">
1505
1293
  <ng-container *ngIf="inputKind !== 'checkbox'">
1506
- <label class="block text-[13px] font-semibold text-slate-700 dark:text-slate-300">
1507
- {{ 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>
1508
1296
  </label>
1509
- <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>
1510
1298
  </ng-container>
1511
1299
 
1512
- <div *ngIf="metaBadges.length" class="flex flex-wrap gap-1.5">
1513
- <span *ngFor="let badge of metaBadges"
1514
- class="rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide"
1515
- [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>
1516
1302
  </div>
1517
1303
 
1518
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" />
1519
1309
 
1520
- <input *ngSwitchCase="'text'"
1521
- 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"
1522
- [class.border-slate-300]="!(errorsForPath.length && controlAsFormControl.dirty)"
1523
- [class.dark:border-slate-600]="!(errorsForPath.length && controlAsFormControl.dirty)"
1524
- [class.focus:border-indigo-400]="!(errorsForPath.length && controlAsFormControl.dirty)"
1525
- [class.focus:ring-indigo-100]="!(errorsForPath.length && controlAsFormControl.dirty)"
1526
- [class.dark:focus:ring-indigo-900/40]="!(errorsForPath.length && controlAsFormControl.dirty)"
1527
- [class.border-red-300]="errorsForPath.length && controlAsFormControl.dirty"
1528
- [class.dark:border-red-700]="errorsForPath.length && controlAsFormControl.dirty"
1529
- [class.bg-red-50]="errorsForPath.length && controlAsFormControl.dirty"
1530
- [class.dark:bg-red-900/20]="errorsForPath.length && controlAsFormControl.dirty"
1531
- [class.focus:border-red-400]="errorsForPath.length && controlAsFormControl.dirty"
1532
- [class.focus:ring-red-100]="errorsForPath.length && controlAsFormControl.dirty"
1533
- [attr.type]="inputType"
1310
+ <textarea *ngSwitchCase="'textarea'" rows="4" class="jsm-textarea"
1311
+ [class.jsm-textarea--error]="errorsForPath.length && controlAsFormControl.dirty"
1534
1312
  [attr.placeholder]="schema.title || label"
1535
- [formControl]="controlAsFormControl"
1536
- />
1313
+ [formControl]="controlAsFormControl"></textarea>
1537
1314
 
1538
- <textarea *ngSwitchCase="'textarea'" rows="4"
1539
- 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"
1540
- [class.border-slate-300]="!(errorsForPath.length && controlAsFormControl.dirty)"
1541
- [class.dark:border-slate-600]="!(errorsForPath.length && controlAsFormControl.dirty)"
1542
- [class.focus:border-indigo-400]="!(errorsForPath.length && controlAsFormControl.dirty)"
1543
- [class.focus:ring-indigo-100]="!(errorsForPath.length && controlAsFormControl.dirty)"
1544
- [class.dark:focus:ring-indigo-900/40]="!(errorsForPath.length && controlAsFormControl.dirty)"
1545
- [class.border-red-300]="errorsForPath.length && controlAsFormControl.dirty"
1546
- [class.dark:border-red-700]="errorsForPath.length && controlAsFormControl.dirty"
1547
- [class.bg-red-50]="errorsForPath.length && controlAsFormControl.dirty"
1548
- [class.dark:bg-red-900/20]="errorsForPath.length && controlAsFormControl.dirty"
1549
- [class.focus:border-red-400]="errorsForPath.length && controlAsFormControl.dirty"
1550
- [class.focus:ring-red-100]="errorsForPath.length && controlAsFormControl.dirty"
1551
- [attr.placeholder]="schema.title || label"
1552
- [formControl]="controlAsFormControl"
1553
- ></textarea>
1554
-
1555
- <!-- select: il chevron SVG è funzionale, indica che è un dropdown -->
1556
- <div *ngSwitchCase="'select'" class="relative">
1557
- <select
1558
- 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"
1559
- [class.border-red-300]="errorsForPath.length && controlAsFormControl.dirty"
1560
- [class.dark:border-red-700]="errorsForPath.length && controlAsFormControl.dirty"
1561
- [class.bg-red-50]="errorsForPath.length && controlAsFormControl.dirty"
1562
- [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"
1563
1318
  [formControl]="controlAsFormControl">
1564
1319
  <option *ngFor="let option of effectiveSchema.enum" [ngValue]="option">{{ option }}</option>
1565
1320
  </select>
1566
- <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"
1567
- viewBox="0 0 16 16" fill="none">
1321
+ <svg class="jsm-select-chevron" viewBox="0 0 16 16" fill="none">
1568
1322
  <path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
1569
1323
  </svg>
1570
1324
  </div>
1571
1325
 
1572
- <!-- checkbox come toggle switch -->
1573
- <label *ngSwitchCase="'checkbox'" class="inline-flex cursor-pointer items-center gap-2.5">
1574
- <span class="jsm-toggle-track" [class.jsm-toggle-on]="controlAsFormControl.value">
1326
+ <label *ngSwitchCase="'checkbox'" class="jsm-toggle-label">
1327
+ <span class="jsm-toggle-track" [class.jsm-on]="controlAsFormControl.value">
1575
1328
  <input type="checkbox" class="sr-only" [formControl]="controlAsFormControl" />
1576
1329
  <span class="jsm-toggle-thumb"></span>
1577
1330
  </span>
1578
- <span class="text-sm font-medium text-slate-700 dark:text-slate-300">
1579
- {{ 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>
1580
1333
  </span>
1581
1334
  </label>
1582
-
1583
1335
  </ng-container>
1584
1336
 
1585
- <p *ngIf="inputKind === 'checkbox' && schema.description" class="text-xs text-slate-500 dark:text-slate-400">{{ schema.description }}</p>
1586
-
1587
- <p *ngIf="effectiveSchema.examples?.length" class="text-[11px] text-slate-400 dark:text-slate-500">
1588
- e.g. {{ effectiveSchema.examples?.[0] }}
1589
- </p>
1590
-
1591
- <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">
1592
1340
  {{ effectiveSchema.contentMediaType }}<span *ngIf="effectiveSchema.contentEncoding"> ({{ effectiveSchema.contentEncoding }})</span>
1593
1341
  </p>
1594
1342
 
1595
- <!-- icona errore: funzionale, segnala un problema di validazione -->
1596
- <div *ngIf="errorsForPath.length && controlAsFormControl.dirty" class="space-y-1 pt-0.5">
1597
- <p *ngFor="let error of errorsForPath" class="flex items-center gap-1 text-xs font-medium text-red-600 dark:text-red-400">
1598
- <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">
1599
1346
  <circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/>
1600
1347
  <path d="M8 5v3.5M8 11v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
1601
1348
  </svg>
@@ -1605,8 +1352,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1605
1352
  </div>
1606
1353
 
1607
1354
  </ng-container>
1608
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".jsm-toggle-track{position:relative;display:inline-flex;align-items:center;width:2.25rem;height:1.25rem;border-radius:9999px;flex-shrink:0;transition:background-color .2s;background-color:#cbd5e1}.jsm-toggle-track.jsm-toggle-on{background-color:#4f46e5}.dark .jsm-toggle-track:not(.jsm-toggle-on){background-color:#475569}.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-toggle-on .jsm-toggle-thumb{transform:translate(1.125rem)}\n"] }]
1609
- }], 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: [{
1610
1357
  type: Input,
1611
1358
  args: [{ required: true }]
1612
1359
  }], control: [{
@@ -1630,6 +1377,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1630
1377
  type: Output
1631
1378
  }] } });
1632
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
+
1633
1416
  class JsonSchemaFormComponent {
1634
1417
  schemaService;
1635
1418
  resolver;
@@ -1649,10 +1432,11 @@ class JsonSchemaFormComponent {
1649
1432
  schemaErrorList = [];
1650
1433
  loading = false;
1651
1434
  valueSub;
1652
- constructor(schemaService, resolver, validation) {
1435
+ constructor(schemaService, resolver, validation, stylesService) {
1653
1436
  this.schemaService = schemaService;
1654
1437
  this.resolver = resolver;
1655
1438
  this.validation = validation;
1439
+ stylesService.inject();
1656
1440
  }
1657
1441
  ngOnChanges(changes) {
1658
1442
  if (changes['schema'] || changes['value'] || changes['data']) {
@@ -1722,7 +1506,7 @@ class JsonSchemaFormComponent {
1722
1506
  this.valueChange.emit(this.rootControl.value);
1723
1507
  });
1724
1508
  }
1725
- 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 });
1726
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: `
1727
1511
  <div class="jsm-root">
1728
1512
  <form class="w-full space-y-5">
@@ -1838,7 +1622,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1838
1622
  </form>
1839
1623
  </div>
1840
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"] }]
1841
- }], ctorParameters: () => [{ type: JsonSchemaFormService }, { type: JsonSchemaResolverService }, { type: JsonSchemaValidationService }], propDecorators: { schema: [{
1625
+ }], ctorParameters: () => [{ type: JsonSchemaFormService }, { type: JsonSchemaResolverService }, { type: JsonSchemaValidationService }, { type: JsonSchemaStylesService }], propDecorators: { schema: [{
1842
1626
  type: Input,
1843
1627
  args: [{ required: true }]
1844
1628
  }], value: [{
@@ -1874,5 +1658,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1874
1658
  * Generated bundle index. Do not edit.
1875
1659
  */
1876
1660
 
1877
- export { JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonSchemaNodeComponent, JsonSchemaResolverService, JsonSchemaValidationService };
1661
+ export { JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonSchemaNodeComponent, JsonSchemaResolverService, JsonSchemaStylesService, JsonSchemaValidationService };
1878
1662
  //# sourceMappingURL=elle96-ng-json-schema-manager.mjs.map