@pure-ds/core 0.6.9 → 0.6.10

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.
Files changed (40) hide show
  1. package/custom-elements.json +71 -28
  2. package/dist/types/pds.d.ts +30 -0
  3. package/dist/types/public/assets/js/pds-manager.d.ts +2 -1
  4. package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -1
  5. package/dist/types/public/assets/js/pds.d.ts.map +1 -1
  6. package/dist/types/public/assets/pds/components/pds-form.d.ts.map +1 -1
  7. package/dist/types/public/assets/pds/components/pds-live-edit.d.ts +1 -195
  8. package/dist/types/public/assets/pds/components/pds-live-edit.d.ts.map +1 -1
  9. package/dist/types/public/assets/pds/components/pds-omnibox.d.ts +0 -2
  10. package/dist/types/public/assets/pds/components/pds-omnibox.d.ts.map +1 -1
  11. package/dist/types/src/js/pds-core/pds-config.d.ts +1306 -13
  12. package/dist/types/src/js/pds-core/pds-config.d.ts.map +1 -1
  13. package/dist/types/src/js/pds-core/pds-enhancers-meta.d.ts.map +1 -1
  14. package/dist/types/src/js/pds-core/pds-enhancers.d.ts.map +1 -1
  15. package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
  16. package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -1
  17. package/dist/types/src/js/pds-core/pds-ontology.d.ts.map +1 -1
  18. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts +1 -4
  19. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts.map +1 -1
  20. package/dist/types/src/js/pds.d.ts.map +1 -1
  21. package/package.json +2 -2
  22. package/packages/pds-cli/bin/pds-static.js +16 -1
  23. package/public/assets/js/app.js +21 -21
  24. package/public/assets/js/pds-manager.js +291 -161
  25. package/public/assets/js/pds.js +16 -16
  26. package/public/assets/pds/components/pds-form.js +124 -27
  27. package/public/assets/pds/components/pds-live-edit.js +820 -122
  28. package/public/assets/pds/components/pds-omnibox.js +10 -18
  29. package/public/assets/pds/custom-elements.json +71 -28
  30. package/public/assets/pds/pds-css-complete.json +1 -6
  31. package/public/assets/pds/pds.css-data.json +5 -35
  32. package/src/js/pds-core/pds-config.js +822 -31
  33. package/src/js/pds-core/pds-enhancers-meta.js +11 -0
  34. package/src/js/pds-core/pds-enhancers.js +113 -5
  35. package/src/js/pds-core/pds-generator.js +183 -23
  36. package/src/js/pds-core/pds-live.js +177 -2
  37. package/src/js/pds-core/pds-ontology.js +6 -0
  38. package/src/js/pds-core/pds-start-helpers.js +14 -6
  39. package/src/js/pds.d.ts +30 -0
  40. package/src/js/pds.js +36 -60
@@ -53,7 +53,6 @@ import { enums } from "./pds-enums.js";
53
53
  * @property {number} [baseUnit] - Generates spacing scale --spacing-1..N.
54
54
  * @property {number} [scaleRatio] - Reserved for derived spacing systems.
55
55
  * @property {number} [maxSpacingSteps] - Caps spacing tokens output.
56
- * @property {number | string} [containerMaxWidth] - Affects layout container sizing.
57
56
  * @property {number} [containerPadding] - Affects container padding tokens.
58
57
  * @property {number} [inputPadding] - Affects input padding tokens.
59
58
  * @property {number} [buttonPadding] - Affects button padding tokens.
@@ -150,14 +149,90 @@ import { enums } from "./pds-enums.js";
150
149
  * @property {(error: any) => void} [onError]
151
150
  */
152
151
 
152
+ /**
153
+ * @typedef {Object} PDSDesignOptionsConfig
154
+ * @property {boolean} [liquidGlassEffects]
155
+ * @property {number} [backgroundMesh]
156
+ */
157
+
158
+ /**
159
+ * @typedef {Object} PDSFormOptionsWidgetsConfig
160
+ * @property {"toggle"|"toggle-with-icons"|"checkbox"} [booleans]
161
+ * @property {"input"|"range"} [numbers]
162
+ * @property {"standard"|"dropdown"} [selects]
163
+ */
164
+
165
+ /**
166
+ * @typedef {Object} PDSFormOptionsLayoutsConfig
167
+ * @property {"default"|"flex"|"grid"|"accordion"|"tabs"|"card"} [fieldsets]
168
+ * @property {"default"|"compact"} [arrays]
169
+ */
170
+
171
+ /**
172
+ * @typedef {Object} PDSFormOptionsEnhancementsConfig
173
+ * @property {boolean} [icons]
174
+ * @property {boolean} [datalists]
175
+ * @property {boolean} [rangeOutput]
176
+ * @property {boolean} [colorInput]
177
+ */
178
+
179
+ /**
180
+ * @typedef {Object} PDSFormOptionsValidationConfig
181
+ * @property {boolean} [showErrors]
182
+ * @property {boolean} [validateOnChange]
183
+ */
184
+
185
+ /**
186
+ * @typedef {Object} PDSFormOptionsConfig
187
+ * @property {PDSFormOptionsWidgetsConfig} [widgets]
188
+ * @property {PDSFormOptionsLayoutsConfig} [layouts]
189
+ * @property {PDSFormOptionsEnhancementsConfig} [enhancements]
190
+ * @property {PDSFormOptionsValidationConfig} [validation]
191
+ */
192
+
193
+ /**
194
+ * @typedef {Object} PDSFormConfig
195
+ * @property {PDSFormOptionsConfig} [options]
196
+ */
197
+
198
+ /**
199
+ * @typedef {Object} PDSAdvancedConfig
200
+ * @property {string} [linkStyle]
201
+ * @property {string} [colorDerivation]
202
+ */
203
+
204
+ /**
205
+ * @typedef {Object} PDSA11yConfig
206
+ * @property {string | number} [minTouchTarget]
207
+ * @property {boolean} [prefersReducedMotion]
208
+ * @property {string} [focusStyle]
209
+ */
210
+
211
+ /**
212
+ * @typedef {Object} PDSConfigEditorFieldMetadata
213
+ * @property {string} [label]
214
+ * @property {string} [description]
215
+ * @property {string} [widget]
216
+ * @property {string} [icon]
217
+ * @property {number} [min]
218
+ * @property {number} [max]
219
+ * @property {number} [step]
220
+ * @property {string[]} [enum]
221
+ * @property {string} [placeholder]
222
+ * @property {number} [maxLength]
223
+ * @property {number} [rows]
224
+ * @property {Record<string, any>} [options]
225
+ */
226
+
153
227
  /**
154
228
  * @typedef {Object} PDSDesignConfig
155
229
  * @property {string} [id]
156
230
  * @property {string} [name]
157
231
  * @property {string[]} [tags]
232
+ * @property {string[]} [themes]
158
233
  * @property {string} [description]
159
- * @property {Record<string, any>} [options]
160
- * @property {Record<string, any>} [form]
234
+ * @property {PDSDesignOptionsConfig} [options]
235
+ * @property {PDSFormConfig} [form]
161
236
  * @property {PDSColorsConfig} [colors] - Affects tokens.colors and --color-* variables.
162
237
  * @property {PDSTypographyConfig} [typography] - Affects tokens.typography and --font-* variables.
163
238
  * @property {PDSSpatialRhythmConfig} [spatialRhythm] - Affects tokens.spacing and --spacing-* variables.
@@ -165,11 +240,10 @@ import { enums } from "./pds-enums.js";
165
240
  * @property {PDSBehaviorConfig} [behavior] - Affects tokens.transitions and motion variables.
166
241
  * @property {PDSLayoutConfig} [layout] - Affects tokens.layout and layout utilities.
167
242
  * @property {PDSLayersConfig} [layers] - Affects tokens.shadows/zIndex and layer effects.
168
- * @property {Record<string, any>} [advanced]
169
- * @property {Record<string, any>} [a11y]
243
+ * @property {PDSAdvancedConfig} [advanced]
244
+ * @property {PDSA11yConfig} [a11y]
170
245
  * @property {PDSIconsConfig} [icons] - Affects tokens.icons and icon component behavior.
171
246
  * @property {Record<string, any>} [components]
172
- * @property {number} [gap]
173
247
  * @property {boolean} [debug]
174
248
  */
175
249
 
@@ -197,12 +271,77 @@ const __DESIGN_CONFIG_SPEC__ = {
197
271
  type: "object",
198
272
  allowUnknown: false,
199
273
  properties: {
200
- id: { type: "string" },
201
- name: { type: "string" },
202
- tags: { type: "array", items: { type: "string" } },
203
- description: { type: "string" },
204
- options: { type: "object", allowUnknown: true },
205
- form: { type: "object", allowUnknown: true },
274
+ id: { type: "string", minLength: 1, maxLength: 64 },
275
+ name: { type: "string", minLength: 1, maxLength: 80 },
276
+ tags: { type: "array", uniqueItems: true, items: { type: "string" } },
277
+ themes: {
278
+ type: "array",
279
+ uniqueItems: true,
280
+ items: {
281
+ type: "string",
282
+ oneOf: [
283
+ { const: "light", title: "Light" },
284
+ { const: "dark", title: "Dark" },
285
+ { const: "system", title: "System" },
286
+ ],
287
+ },
288
+ },
289
+ description: { type: "string", maxLength: 500 },
290
+ options: {
291
+ type: "object",
292
+ allowUnknown: true,
293
+ properties: {
294
+ liquidGlassEffects: { type: "boolean" },
295
+ backgroundMesh: { type: "number" },
296
+ },
297
+ },
298
+ form: {
299
+ type: "object",
300
+ allowUnknown: true,
301
+ properties: {
302
+ options: {
303
+ type: "object",
304
+ allowUnknown: true,
305
+ properties: {
306
+ widgets: {
307
+ type: "object",
308
+ allowUnknown: false,
309
+ properties: {
310
+ booleans: { type: "string" },
311
+ numbers: { type: "string" },
312
+ selects: { type: "string" },
313
+ },
314
+ },
315
+ layouts: {
316
+ type: "object",
317
+ allowUnknown: false,
318
+ properties: {
319
+ fieldsets: { type: "string" },
320
+ arrays: { type: "string" },
321
+ },
322
+ },
323
+ enhancements: {
324
+ type: "object",
325
+ allowUnknown: false,
326
+ properties: {
327
+ icons: { type: "boolean" },
328
+ datalists: { type: "boolean" },
329
+ rangeOutput: { type: "boolean" },
330
+ colorInput: { type: "boolean" },
331
+ },
332
+ },
333
+ validation: {
334
+ type: "object",
335
+ allowUnknown: false,
336
+ properties: {
337
+ showErrors: { type: "boolean" },
338
+ validateOnChange: { type: "boolean" },
339
+ },
340
+ },
341
+ },
342
+ },
343
+ },
344
+ },
206
345
  colors: {
207
346
  type: "object",
208
347
  allowUnknown: false,
@@ -270,7 +409,7 @@ const __DESIGN_CONFIG_SPEC__ = {
270
409
  },
271
410
  darkMode: {
272
411
  type: "object",
273
- allowUnknown: true,
412
+ allowUnknown: false,
274
413
  properties: {
275
414
  background: {
276
415
  type: "string",
@@ -386,7 +525,6 @@ const __DESIGN_CONFIG_SPEC__ = {
386
525
  type: "number",
387
526
  relations: { tokens: ["--spacing-*"] },
388
527
  },
389
- containerMaxWidth: { type: ["number", "string"] },
390
528
  containerPadding: { type: "number" },
391
529
  inputPadding: {
392
530
  type: "number",
@@ -490,13 +628,39 @@ const __DESIGN_CONFIG_SPEC__ = {
490
628
  },
491
629
  darkMode: {
492
630
  type: "object",
493
- allowUnknown: true,
631
+ allowUnknown: false,
494
632
  properties: {
495
633
  baseShadowOpacity: { type: "number", relations: { theme: "dark", tokens: ["--shadow-*"] } },
496
634
  },
497
635
  },
498
- utilities: { type: "object", allowUnknown: true },
499
- gridSystem: { type: "object", allowUnknown: true },
636
+ utilities: {
637
+ type: "object",
638
+ allowUnknown: true,
639
+ properties: {
640
+ grid: { type: "boolean" },
641
+ flex: { type: "boolean" },
642
+ spacing: { type: "boolean" },
643
+ container: { type: "boolean" },
644
+ },
645
+ },
646
+ gridSystem: {
647
+ type: "object",
648
+ allowUnknown: true,
649
+ properties: {
650
+ columns: { type: "array", items: { type: "number" } },
651
+ autoFitBreakpoints: {
652
+ type: "object",
653
+ allowUnknown: false,
654
+ properties: {
655
+ sm: { type: "string" },
656
+ md: { type: "string" },
657
+ lg: { type: "string" },
658
+ xl: { type: "string" },
659
+ },
660
+ },
661
+ enableGapUtilities: { type: "boolean" },
662
+ },
663
+ },
500
664
  containerMaxWidth: { type: ["number", "string"] },
501
665
  },
502
666
  },
@@ -532,15 +696,30 @@ const __DESIGN_CONFIG_SPEC__ = {
532
696
  zIndexNotification: { type: "number" },
533
697
  darkMode: {
534
698
  type: "object",
535
- allowUnknown: true,
699
+ allowUnknown: false,
536
700
  properties: {
537
701
  baseShadowOpacity: { type: "number", relations: { theme: "dark", tokens: ["--shadow-*"] } },
538
702
  },
539
703
  },
540
704
  },
541
705
  },
542
- advanced: { type: "object", allowUnknown: true },
543
- a11y: { type: "object", allowUnknown: true },
706
+ advanced: {
707
+ type: "object",
708
+ allowUnknown: true,
709
+ properties: {
710
+ linkStyle: { type: "string" },
711
+ colorDerivation: { type: "string" },
712
+ },
713
+ },
714
+ a11y: {
715
+ type: "object",
716
+ allowUnknown: true,
717
+ properties: {
718
+ minTouchTarget: { type: ["string", "number"] },
719
+ prefersReducedMotion: { type: "boolean" },
720
+ focusStyle: { type: "string" },
721
+ },
722
+ },
544
723
  icons: {
545
724
  type: "object",
546
725
  allowUnknown: false,
@@ -548,14 +727,38 @@ const __DESIGN_CONFIG_SPEC__ = {
548
727
  set: { type: "string" },
549
728
  weight: { type: "string" },
550
729
  defaultSize: { type: "number", relations: { tokens: ["--icon-size"] } },
551
- sizes: { type: "object", allowUnknown: true },
730
+ sizes: {
731
+ type: "object",
732
+ allowUnknown: true,
733
+ properties: {
734
+ xs: { type: ["number", "string"] },
735
+ sm: { type: ["number", "string"] },
736
+ md: { type: ["number", "string"] },
737
+ lg: { type: ["number", "string"] },
738
+ xl: { type: ["number", "string"] },
739
+ "2xl": { type: ["number", "string"] },
740
+ },
741
+ },
552
742
  spritePath: { type: "string" },
553
743
  externalPath: { type: "string" },
554
- include: { type: "object", allowUnknown: true },
744
+ include: {
745
+ type: "object",
746
+ allowUnknown: true,
747
+ properties: {
748
+ navigation: { type: "array", items: { type: "string" } },
749
+ actions: { type: "array", items: { type: "string" } },
750
+ communication: { type: "array", items: { type: "string" } },
751
+ content: { type: "array", items: { type: "string" } },
752
+ status: { type: "array", items: { type: "string" } },
753
+ time: { type: "array", items: { type: "string" } },
754
+ commerce: { type: "array", items: { type: "string" } },
755
+ formatting: { type: "array", items: { type: "string" } },
756
+ system: { type: "array", items: { type: "string" } },
757
+ },
758
+ },
555
759
  },
556
760
  },
557
761
  components: { type: "object", allowUnknown: true },
558
- gap: { type: "number" },
559
762
  debug: { type: "boolean" },
560
763
  },
561
764
  };
@@ -678,6 +881,588 @@ export const PDS_CONFIG_RELATIONS = __collectRelations(
678
881
  ""
679
882
  );
680
883
 
884
+ export const PDS_DESIGN_CONFIG_SPEC = __DESIGN_CONFIG_SPEC__;
885
+
886
+ const __CONFIG_EDITOR_METADATA_OVERRIDES__ = {
887
+ "colors.primary": { widget: "input-color" },
888
+ "colors.secondary": { widget: "input-color" },
889
+ "colors.accent": { widget: "input-color" },
890
+ "colors.background": { widget: "input-color" },
891
+ "colors.success": { widget: "input-color" },
892
+ "colors.warning": { widget: "input-color" },
893
+ "colors.danger": { widget: "input-color" },
894
+ "colors.info": { widget: "input-color" },
895
+ "colors.gradientStops": { min: 2, max: 8, step: 1, widget: "range" },
896
+ "colors.elevationOpacity": { min: 0, max: 1, step: 0.01, widget: "range" },
897
+ "colors.darkMode.background": { widget: "input-color" },
898
+ "colors.darkMode.primary": { widget: "input-color" },
899
+ "colors.darkMode.secondary": { widget: "input-color" },
900
+ "colors.darkMode.accent": { widget: "input-color" },
901
+
902
+ "description": {
903
+ widget: "textarea",
904
+ maxLength: 500,
905
+ rows: 4,
906
+ placeholder: "Summarize the visual and interaction intent",
907
+ },
908
+
909
+ "typography.fontFamilyHeadings": {
910
+ widget: "font-family-omnibox",
911
+ icon: "text-aa",
912
+ placeholder: "Heading font stack",
913
+ },
914
+ "typography.fontFamilyBody": {
915
+ widget: "font-family-omnibox",
916
+ icon: "text-aa",
917
+ placeholder: "Body font stack",
918
+ },
919
+ "typography.fontFamilyMono": {
920
+ widget: "font-family-omnibox",
921
+ icon: "text-aa",
922
+ placeholder: "Monospace font stack",
923
+ },
924
+ "typography.baseFontSize": { min: 8, max: 32, step: 1, widget: "input-range" },
925
+ "typography.fontScale": { min: 1, max: 2, step: 0.01, widget: "range" },
926
+ "typography.fontWeightLight": { min: 100, max: 800, step: 100, widget: "input-range" },
927
+ "typography.fontWeightNormal": { min: 100, max: 800, step: 100, widget: "input-range" },
928
+ "typography.fontWeightMedium": { min: 100, max: 800, step: 100, widget: "input-range" },
929
+ "typography.fontWeightSemibold": { min: 100, max: 800, step: 100, widget: "input-range" },
930
+ "typography.fontWeightBold": { min: 100, max: 800, step: 100, widget: "input-range" },
931
+ "typography.lineHeightTight": { min: 0.75, max: 3, step: 0.001, widget: "input-range" },
932
+ "typography.lineHeightNormal": { min: 0.75, max: 3, step: 0.001, widget: "input-range" },
933
+ "typography.lineHeightRelaxed": { min: 0.75, max: 3, step: 0.001, widget: "input-range" },
934
+ "typography.letterSpacingTight": { min: -0.1, max: 0.1, step: 0.001, widget: "range" },
935
+ "typography.letterSpacingNormal": { min: -0.1, max: 0.1, step: 0.001, widget: "range" },
936
+ "typography.letterSpacingWide": { min: -0.1, max: 0.1, step: 0.001, widget: "range" },
937
+
938
+ "spatialRhythm.baseUnit": { min: 1, max: 16, step: 1, widget: "range" },
939
+ "spatialRhythm.scaleRatio": { min: 1, max: 2, step: 0.01, widget: "range" },
940
+ "spatialRhythm.maxSpacingSteps": { min: 4, max: 64, step: 1, widget: "range" },
941
+ "spatialRhythm.containerPadding": { min: 0, max: 8, step: 0.05, widget: "range" },
942
+ "spatialRhythm.inputPadding": { min: 0, max: 4, step: 0.05, widget: "range" },
943
+ "spatialRhythm.buttonPadding": { min: 0, max: 4, step: 0.05, widget: "range" },
944
+ "spatialRhythm.sectionSpacing": { min: 0, max: 8, step: 0.05, widget: "range" },
945
+
946
+ "shape.radiusSize": {
947
+ oneOf: Object.entries(enums.RadiusSizes).map(([name, value]) => ({
948
+ const: value,
949
+ title: name,
950
+ })),
951
+ },
952
+ "shape.borderWidth": {
953
+ widget: "select",
954
+ oneOf: Object.entries(enums.BorderWidths).map(([name, value]) => ({
955
+ const: value,
956
+ title: name,
957
+ })),
958
+ },
959
+ "shape.customRadius": { min: 0, max: 64, step: 1, widget: "range" },
960
+
961
+ "behavior.transitionSpeed": {
962
+ oneOf: Object.entries(enums.TransitionSpeeds).map(([name, value]) => ({
963
+ const: value,
964
+ title: name,
965
+ })),
966
+ },
967
+ "behavior.animationEasing": {
968
+ enum: Object.values(enums.AnimationEasings),
969
+ },
970
+ "behavior.customTransitionSpeed": { min: 0, max: 1000, step: 10, widget: "range" },
971
+ "behavior.focusRingWidth": { min: 0, max: 8, step: 1, widget: "range" },
972
+ "behavior.focusRingOpacity": { min: 0, max: 1, step: 0.01, widget: "range" },
973
+ "behavior.hoverOpacity": { min: 0, max: 1, step: 0.01, widget: "range" },
974
+
975
+ "layout.gridColumns": { min: 1, max: 24, step: 1, widget: "range" },
976
+ "layout.gridGutter": { min: 0, max: 8, step: 0.05, widget: "range" },
977
+ "layout.maxWidth": { widget: "input-text", placeholder: "e.g. 1200 or 1200px" },
978
+ "layout.maxWidths.sm": { widget: "input-text", placeholder: "e.g. 640 or 640px" },
979
+ "layout.maxWidths.md": { widget: "input-text", placeholder: "e.g. 768 or 768px" },
980
+ "layout.maxWidths.lg": { widget: "input-text", placeholder: "e.g. 1024 or 1024px" },
981
+ "layout.maxWidths.xl": { widget: "input-text", placeholder: "e.g. 1280 or 1280px" },
982
+ "layout.containerMaxWidth": { widget: "input-text", placeholder: "e.g. 1400px" },
983
+ "layout.containerPadding": { widget: "input-text", placeholder: "e.g. var(--spacing-6)" },
984
+ "layout.breakpoints.sm": { min: 320, max: 2560, step: 1, widget: "input-number" },
985
+ "layout.breakpoints.md": { min: 480, max: 3200, step: 1, widget: "input-number" },
986
+ "layout.breakpoints.lg": { min: 640, max: 3840, step: 1, widget: "input-number" },
987
+ "layout.breakpoints.xl": { min: 768, max: 5120, step: 1, widget: "input-number" },
988
+ "layout.baseShadowOpacity": { min: 0, max: 1, step: 0.01, widget: "range" },
989
+ "layout.darkMode.baseShadowOpacity": { min: 0, max: 1, step: 0.01, widget: "range" },
990
+ "layout.densityCompact": { min: 0.5, max: 2, step: 0.05, widget: "range" },
991
+ "layout.densityNormal": { min: 0.5, max: 2, step: 0.05, widget: "range" },
992
+ "layout.densityComfortable": { min: 0.5, max: 2, step: 0.05, widget: "range" },
993
+ "layout.buttonMinHeight": { min: 24, max: 96, step: 1, widget: "range" },
994
+ "layout.inputMinHeight": { min: 24, max: 96, step: 1, widget: "range" },
995
+
996
+ "layers.baseShadowOpacity": { min: 0, max: 1, step: 0.01, widget: "range" },
997
+ "layers.shadowBlurMultiplier": { min: 0, max: 8, step: 0.1, widget: "range" },
998
+ "layers.shadowOffsetMultiplier": { min: 0, max: 8, step: 0.1, widget: "range" },
999
+ "layers.blurLight": { min: 0, max: 48, step: 1, widget: "range" },
1000
+ "layers.blurMedium": { min: 0, max: 64, step: 1, widget: "range" },
1001
+ "layers.blurHeavy": { min: 0, max: 96, step: 1, widget: "range" },
1002
+ "layers.baseZIndex": { min: 0, max: 10000, step: 10, widget: "range" },
1003
+ "layers.zIndexStep": { min: 1, max: 100, step: 1, widget: "range" },
1004
+ "layers.darkMode.baseShadowOpacity": { min: 0, max: 1, step: 0.01, widget: "range" },
1005
+
1006
+ "advanced.linkStyle": { enum: Object.values(enums.LinkStyles) },
1007
+ "a11y.minTouchTarget": {
1008
+ oneOf: Object.entries(enums.TouchTargetSizes).map(([name, value]) => ({
1009
+ const: value,
1010
+ title: name,
1011
+ })),
1012
+ },
1013
+ "a11y.focusStyle": { enum: Object.values(enums.FocusStyles) },
1014
+
1015
+ "icons.defaultSize": { min: 8, max: 128, step: 1, widget: "range", icon: "sparkle" },
1016
+ };
1017
+
1018
+ function __toConfigPath(pathSegments = []) {
1019
+ return pathSegments.join(".");
1020
+ }
1021
+
1022
+ function __toJsonPointer(pathSegments = []) {
1023
+ return `/${pathSegments.join("/")}`;
1024
+ }
1025
+
1026
+ function __getValueAtPath(source, pathSegments = []) {
1027
+ if (!source || typeof source !== "object") return undefined;
1028
+ return pathSegments.reduce((current, segment) => {
1029
+ if (current === null || current === undefined) return undefined;
1030
+ if (typeof current !== "object") return undefined;
1031
+ return current[segment];
1032
+ }, source);
1033
+ }
1034
+
1035
+ function __resolveExampleValue(value, fallbackSource, pathSegments = []) {
1036
+ if (value !== undefined && value !== null) return value;
1037
+ const fallback = __getValueAtPath(fallbackSource, pathSegments);
1038
+ return fallback !== undefined && fallback !== null ? fallback : undefined;
1039
+ }
1040
+
1041
+ function __toTitleCase(value = "") {
1042
+ return String(value)
1043
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
1044
+ .replace(/[_-]+/g, " ")
1045
+ .replace(/\s+/g, " ")
1046
+ .trim()
1047
+ .replace(/^./, (char) => char.toUpperCase());
1048
+ }
1049
+
1050
+ function __resolveExpectedType(spec, value) {
1051
+ if (!spec) return "string";
1052
+ const expected = spec.type || "string";
1053
+ if (Array.isArray(expected)) {
1054
+ const actual = __getValueType(value);
1055
+ if (actual !== "undefined" && expected.includes(actual)) return actual;
1056
+ if (expected.includes("string")) return "string";
1057
+ return expected.find((item) => item !== "null") || expected[0] || "string";
1058
+ }
1059
+ return expected;
1060
+ }
1061
+
1062
+ function __copySchemaConstraints(target, spec, keys = []) {
1063
+ if (!target || !spec || !Array.isArray(keys)) return target;
1064
+ keys.forEach((key) => {
1065
+ if (spec[key] !== undefined) {
1066
+ target[key] = spec[key];
1067
+ }
1068
+ });
1069
+ return target;
1070
+ }
1071
+
1072
+ function __buildSchemaChoices(spec, metadata) {
1073
+ if (Array.isArray(metadata?.oneOf) && metadata.oneOf.length) {
1074
+ return metadata.oneOf;
1075
+ }
1076
+ if (Array.isArray(metadata?.enum) && metadata.enum.length) {
1077
+ return metadata.enum.map((option) => ({
1078
+ const: option,
1079
+ title: __toTitleCase(option),
1080
+ }));
1081
+ }
1082
+ if (Array.isArray(spec?.oneOf) && spec.oneOf.length) {
1083
+ return spec.oneOf;
1084
+ }
1085
+ if (Array.isArray(spec?.enum) && spec.enum.length) {
1086
+ return spec.enum.map((option) => ({
1087
+ const: option,
1088
+ title: __toTitleCase(option),
1089
+ }));
1090
+ }
1091
+ return null;
1092
+ }
1093
+
1094
+ function __normalizeEditorWidget(widget) {
1095
+ if (!widget) return widget;
1096
+ if (widget === "range") return "input-range";
1097
+ return widget;
1098
+ }
1099
+
1100
+ function __resolveSchemaTypeFromChoices(schemaType, choices) {
1101
+ if (!Array.isArray(choices) || !choices.length) return schemaType;
1102
+ const choiceTypes = new Set();
1103
+ for (const option of choices) {
1104
+ if (!option || option.const === undefined) continue;
1105
+ choiceTypes.add(__getValueType(option.const));
1106
+ }
1107
+ if (!choiceTypes.size) return schemaType;
1108
+ if (choiceTypes.size === 1) {
1109
+ const only = Array.from(choiceTypes)[0];
1110
+ if (only === "number") return "number";
1111
+ if (only === "string") return "string";
1112
+ if (only === "boolean") return "boolean";
1113
+ }
1114
+ return schemaType;
1115
+ }
1116
+
1117
+ function __inferEditorMetadata(path, spec, value) {
1118
+ const type = __resolveExpectedType(spec, value);
1119
+ const lowerPath = path.toLowerCase();
1120
+ const base = {
1121
+ label: __toTitleCase(path.split(".").slice(-1)[0] || path),
1122
+ };
1123
+
1124
+ if (type === "boolean") {
1125
+ base.widget = "toggle";
1126
+ }
1127
+
1128
+ if (type === "number") {
1129
+ base.widget = "range";
1130
+ if (lowerPath.includes("opacity")) {
1131
+ base.min = 0;
1132
+ base.max = 1;
1133
+ base.step = 0.01;
1134
+ } else if (lowerPath.includes("lineheight")) {
1135
+ base.min = 0.75;
1136
+ base.max = 3;
1137
+ base.step = 0.001;
1138
+ base.widget = "input-range";
1139
+ } else if (lowerPath.includes("fontweight")) {
1140
+ base.min = 100;
1141
+ base.max = 800;
1142
+ base.step = 100;
1143
+ base.widget = "input-range";
1144
+ } else if (lowerPath.endsWith("basefontsize")) {
1145
+ base.min = 8;
1146
+ base.max = 32;
1147
+ base.step = 1;
1148
+ base.widget = "input-range";
1149
+ } else if (lowerPath.includes("scale") || lowerPath.includes("ratio")) {
1150
+ base.min = 1;
1151
+ base.max = 2;
1152
+ base.step = 0.01;
1153
+ } else {
1154
+ base.min = 0;
1155
+ base.max = Math.max(10, Number.isFinite(Number(value)) ? Number(value) * 4 : 100);
1156
+ base.step = 1;
1157
+ }
1158
+ }
1159
+
1160
+ if (type === "string" && path.startsWith("colors.")) {
1161
+ base.widget = "input-color";
1162
+ }
1163
+
1164
+ if (type === "string" && lowerPath === "description") {
1165
+ base.widget = "textarea";
1166
+ base.maxLength = 500;
1167
+ base.rows = 4;
1168
+ }
1169
+
1170
+ const override = __CONFIG_EDITOR_METADATA_OVERRIDES__[path] || {};
1171
+ const merged = { ...base, ...override };
1172
+ if (merged.widget) {
1173
+ merged.widget = __normalizeEditorWidget(merged.widget);
1174
+ }
1175
+ return merged;
1176
+ }
1177
+
1178
+ function __buildConfigSchemaNode(
1179
+ spec,
1180
+ value,
1181
+ pathSegments,
1182
+ uiSchema,
1183
+ metadataOut,
1184
+ fallbackSource
1185
+ ) {
1186
+ if (!spec || typeof spec !== "object") return null;
1187
+ const resolvedValueForType = __resolveExampleValue(
1188
+ value,
1189
+ fallbackSource,
1190
+ pathSegments
1191
+ );
1192
+ const expectedType = __resolveExpectedType(spec, resolvedValueForType);
1193
+
1194
+ if (expectedType === "object" && spec.properties) {
1195
+ const schemaNode = { type: "object", properties: {} };
1196
+ if (pathSegments.length > 0) {
1197
+ schemaNode.title = __toTitleCase(pathSegments[pathSegments.length - 1]);
1198
+ }
1199
+ const valueNode = {};
1200
+ for (const [key, childSpec] of Object.entries(spec.properties)) {
1201
+ const childValue =
1202
+ value && typeof value === "object" && !Array.isArray(value)
1203
+ ? value[key]
1204
+ : undefined;
1205
+ const child = __buildConfigSchemaNode(
1206
+ childSpec,
1207
+ childValue,
1208
+ [...pathSegments, key],
1209
+ uiSchema,
1210
+ metadataOut,
1211
+ fallbackSource
1212
+ );
1213
+ if (!child) continue;
1214
+ schemaNode.properties[key] = child.schema;
1215
+ if (child.hasValue) {
1216
+ valueNode[key] = child.value;
1217
+ }
1218
+ }
1219
+ if (!Object.keys(schemaNode.properties).length) return null;
1220
+ return {
1221
+ schema: schemaNode,
1222
+ value: valueNode,
1223
+ hasValue: Object.keys(valueNode).length > 0,
1224
+ };
1225
+ }
1226
+
1227
+ if (expectedType === "array") {
1228
+ const path = __toConfigPath(pathSegments);
1229
+ const metadata = __inferEditorMetadata(path, spec, value);
1230
+ metadataOut[path] = metadata;
1231
+ const resolvedArrayExample = __resolveExampleValue(
1232
+ value,
1233
+ fallbackSource,
1234
+ pathSegments
1235
+ );
1236
+
1237
+ const itemType = spec.items?.type || "string";
1238
+ const normalizedItemType = Array.isArray(itemType) ? itemType[0] : itemType;
1239
+ const itemSchema = {
1240
+ type: normalizedItemType,
1241
+ };
1242
+
1243
+ const itemChoices = __buildSchemaChoices(spec?.items, null);
1244
+ if (itemChoices) {
1245
+ itemSchema.oneOf = itemChoices;
1246
+ }
1247
+
1248
+ if (
1249
+ normalizedItemType === "string" &&
1250
+ Array.isArray(resolvedArrayExample) &&
1251
+ resolvedArrayExample.length > 0
1252
+ ) {
1253
+ const firstString = resolvedArrayExample.find(
1254
+ (entry) => typeof entry === "string" && entry.trim().length > 0
1255
+ );
1256
+ if (firstString) {
1257
+ itemSchema.examples = [firstString];
1258
+ }
1259
+ }
1260
+
1261
+ __copySchemaConstraints(itemSchema, spec?.items, [
1262
+ "minimum",
1263
+ "maximum",
1264
+ "exclusiveMinimum",
1265
+ "exclusiveMaximum",
1266
+ "multipleOf",
1267
+ "minLength",
1268
+ "maxLength",
1269
+ "pattern",
1270
+ "format",
1271
+ "minItems",
1272
+ "maxItems",
1273
+ "uniqueItems",
1274
+ "description",
1275
+ "default",
1276
+ ]);
1277
+
1278
+ const schemaNode = {
1279
+ type: "array",
1280
+ items: itemSchema,
1281
+ };
1282
+
1283
+ __copySchemaConstraints(schemaNode, spec, [
1284
+ "minItems",
1285
+ "maxItems",
1286
+ "uniqueItems",
1287
+ "description",
1288
+ "default",
1289
+ ]);
1290
+
1291
+ const pointer = __toJsonPointer(pathSegments);
1292
+ const uiEntry = {};
1293
+ const itemHasChoices = Array.isArray(itemSchema.oneOf) && itemSchema.oneOf.length > 0;
1294
+ if (normalizedItemType === "string" && itemHasChoices) {
1295
+ uiEntry["ui:widget"] = schemaNode.maxItems === 1 ? "radio" : "checkbox-group";
1296
+ }
1297
+ if (
1298
+ normalizedItemType === "string" &&
1299
+ Array.isArray(resolvedArrayExample) &&
1300
+ resolvedArrayExample.length > 0
1301
+ ) {
1302
+ const placeholderPreview = resolvedArrayExample
1303
+ .filter((entry) => typeof entry === "string" && entry.trim().length > 0)
1304
+ .slice(0, 5)
1305
+ .join(", ");
1306
+ if (placeholderPreview) {
1307
+ uiEntry["ui:placeholder"] = placeholderPreview;
1308
+ }
1309
+ }
1310
+ if (Object.keys(uiEntry).length) {
1311
+ uiSchema[pointer] = {
1312
+ ...(uiSchema[pointer] || {}),
1313
+ ...uiEntry,
1314
+ };
1315
+ }
1316
+
1317
+ return {
1318
+ schema: schemaNode,
1319
+ value: Array.isArray(value) ? value : [],
1320
+ hasValue: Array.isArray(value),
1321
+ };
1322
+ }
1323
+
1324
+ const path = __toConfigPath(pathSegments);
1325
+ const metadata = __inferEditorMetadata(path, spec, resolvedValueForType);
1326
+ metadataOut[path] = metadata;
1327
+
1328
+ const choices = __buildSchemaChoices(spec, metadata);
1329
+ const schemaType = expectedType === "null" ? "string" : expectedType;
1330
+ const normalizedSchemaType = __resolveSchemaTypeFromChoices(schemaType, choices);
1331
+ const schemaNode = {
1332
+ type: normalizedSchemaType,
1333
+ title: metadata.label || __toTitleCase(pathSegments[pathSegments.length - 1] || path),
1334
+ };
1335
+
1336
+ if (choices) {
1337
+ schemaNode.oneOf = choices;
1338
+ }
1339
+
1340
+ __copySchemaConstraints(schemaNode, spec, [
1341
+ "minimum",
1342
+ "maximum",
1343
+ "exclusiveMinimum",
1344
+ "exclusiveMaximum",
1345
+ "multipleOf",
1346
+ "minLength",
1347
+ "maxLength",
1348
+ "pattern",
1349
+ "format",
1350
+ "description",
1351
+ "default",
1352
+ ]);
1353
+
1354
+ if (typeof metadata.maxLength === "number" && schemaNode.maxLength === undefined) {
1355
+ schemaNode.maxLength = metadata.maxLength;
1356
+ }
1357
+
1358
+ if (
1359
+ (schemaNode.type === "number" || schemaNode.type === "integer") &&
1360
+ typeof metadata.min === "number" &&
1361
+ schemaNode.minimum === undefined
1362
+ ) {
1363
+ schemaNode.minimum = metadata.min;
1364
+ }
1365
+ if (
1366
+ (schemaNode.type === "number" || schemaNode.type === "integer") &&
1367
+ typeof metadata.max === "number" &&
1368
+ schemaNode.maximum === undefined
1369
+ ) {
1370
+ schemaNode.maximum = metadata.max;
1371
+ }
1372
+ if (
1373
+ (schemaNode.type === "number" || schemaNode.type === "integer") &&
1374
+ typeof metadata.step === "number" &&
1375
+ schemaNode.multipleOf === undefined
1376
+ ) {
1377
+ schemaNode.multipleOf = metadata.step;
1378
+ }
1379
+
1380
+ const exampleValue = resolvedValueForType;
1381
+ if (exampleValue !== undefined) {
1382
+ schemaNode.examples = [exampleValue];
1383
+ }
1384
+
1385
+ const pointer = __toJsonPointer(pathSegments);
1386
+ const uiEntry = {};
1387
+ if (metadata.widget) uiEntry["ui:widget"] = metadata.widget;
1388
+ if (metadata.icon) uiEntry["ui:icon"] = metadata.icon;
1389
+ if (typeof metadata.min === "number") uiEntry["ui:min"] = metadata.min;
1390
+ if (typeof metadata.max === "number") uiEntry["ui:max"] = metadata.max;
1391
+ if (typeof metadata.step === "number") uiEntry["ui:step"] = metadata.step;
1392
+ if (metadata.placeholder) uiEntry["ui:placeholder"] = metadata.placeholder;
1393
+ if (typeof metadata.rows === "number") {
1394
+ uiEntry["ui:options"] = {
1395
+ ...(uiEntry["ui:options"] || {}),
1396
+ rows: metadata.rows,
1397
+ };
1398
+ }
1399
+ if (
1400
+ metadata.widget === "input-range" &&
1401
+ exampleValue !== undefined
1402
+ ) {
1403
+ uiEntry["ui:allowUnset"] = true;
1404
+ }
1405
+ if (
1406
+ typeof metadata.min === "number" ||
1407
+ typeof metadata.max === "number" ||
1408
+ typeof metadata.step === "number"
1409
+ ) {
1410
+ uiEntry["ui:options"] = {
1411
+ ...(uiEntry["ui:options"] || {}),
1412
+ ...(typeof metadata.min === "number" ? { min: metadata.min } : {}),
1413
+ ...(typeof metadata.max === "number" ? { max: metadata.max } : {}),
1414
+ ...(typeof metadata.step === "number" ? { step: metadata.step } : {}),
1415
+ };
1416
+ }
1417
+ if (Object.keys(uiEntry).length) {
1418
+ uiSchema[pointer] = uiEntry;
1419
+ }
1420
+
1421
+ return {
1422
+ schema: schemaNode,
1423
+ value,
1424
+ hasValue: value !== undefined,
1425
+ };
1426
+ }
1427
+
1428
+ export function buildDesignConfigFormSchema(designConfig = {}) {
1429
+ const metadata = {};
1430
+ const uiSchema = {
1431
+ "/colors": {
1432
+ "ui:layout": "flex",
1433
+ "ui:layoutOptions": { wrap: true, gap: "sm" },
1434
+ },
1435
+ "/colors/darkMode": {
1436
+ "ui:layout": "flex",
1437
+ "ui:layoutOptions": { wrap: true, gap: "sm" },
1438
+ },
1439
+ };
1440
+
1441
+ const fallbackSource = presets?.default && typeof presets.default === "object"
1442
+ ? presets.default
1443
+ : null;
1444
+
1445
+ const root = __buildConfigSchemaNode(
1446
+ __DESIGN_CONFIG_SPEC__,
1447
+ designConfig,
1448
+ [],
1449
+ uiSchema,
1450
+ metadata,
1451
+ fallbackSource
1452
+ );
1453
+
1454
+ return {
1455
+ schema: root?.schema || { type: "object", properties: {} },
1456
+ uiSchema,
1457
+ values: root?.value || {},
1458
+ metadata,
1459
+ };
1460
+ }
1461
+
1462
+ export function getDesignConfigEditorMetadata(designConfig = {}) {
1463
+ return buildDesignConfigFormSchema(designConfig).metadata;
1464
+ }
1465
+
681
1466
  export function validateDesignConfig(designConfig, { log, context = "PDS config" } = {}) {
682
1467
  if (!designConfig || typeof designConfig !== "object") return [];
683
1468
  const issues = [];
@@ -1253,7 +2038,6 @@ export const presets = {
1253
2038
  spatialRhythm: {
1254
2039
  baseUnit: 4,
1255
2040
  scaleRatio: 1.25,
1256
- containerMaxWidth: 1440,
1257
2041
  containerPadding: 1.5,
1258
2042
  sectionSpacing: 3.0,
1259
2043
  },
@@ -1361,7 +2145,6 @@ export const presets = {
1361
2145
  spatialRhythm: {
1362
2146
  baseUnit: 4,
1363
2147
  scaleRatio: 1.25,
1364
- containerMaxWidth: 1280,
1365
2148
  sectionSpacing: 2.5,
1366
2149
  },
1367
2150
  shape: {
@@ -1420,7 +2203,6 @@ export const presets = {
1420
2203
  spatialRhythm: {
1421
2204
  baseUnit: 4,
1422
2205
  scaleRatio: 1.25,
1423
- containerMaxWidth: 680,
1424
2206
  sectionSpacing: 1.5,
1425
2207
  },
1426
2208
  shape: {
@@ -1473,7 +2255,6 @@ export const presets = {
1473
2255
  spatialRhythm: {
1474
2256
  baseUnit: 4,
1475
2257
  scaleRatio: 1.2,
1476
- containerMaxWidth: 1600,
1477
2258
  containerPadding: 1.5,
1478
2259
  sectionSpacing: 2.0,
1479
2260
  },
@@ -1530,6 +2311,7 @@ presets.default = {
1530
2311
  icons: true, // Enable icon-enhanced inputs
1531
2312
  datalists: true, // Enable datalist autocomplete
1532
2313
  rangeOutput: true, // Use .range-output for ranges
2314
+ colorInput: true, // Use label[data-color] for color inputs
1533
2315
  },
1534
2316
  validation: {
1535
2317
  showErrors: true, // Show validation errors inline
@@ -1594,7 +2376,6 @@ presets.default = {
1594
2376
  // Advanced spacing options
1595
2377
  scaleRatio: 1.25,
1596
2378
  maxSpacingSteps: 32,
1597
- containerMaxWidth: 1200,
1598
2379
  containerPadding: 1.0,
1599
2380
  inputPadding: 0.75,
1600
2381
  buttonPadding: 1.0,
@@ -1602,6 +2383,10 @@ presets.default = {
1602
2383
  },
1603
2384
 
1604
2385
  layers: {
2386
+ baseShadowOpacity: 0.1,
2387
+ darkMode: {
2388
+ baseShadowOpacity: 0.25,
2389
+ },
1605
2390
  shadowDepth: "medium",
1606
2391
  blurLight: 4,
1607
2392
  blurMedium: 8,
@@ -1832,13 +2617,19 @@ presets.default = {
1832
2617
  },
1833
2618
  // Default sprite path for internal/dev use. For consumer apps, icons are exported to
1834
2619
  // [config.static.root]/icons/pds-icons.svg and components should consume from there.
1835
- spritePath: "public/assets/pds/icons/pds-icons.svg",
2620
+ spritePath: "/assets/pds/icons/pds-icons.svg",
1836
2621
  },
1837
2622
 
1838
- gap: 4,
1839
-
1840
2623
  debug: false,
1841
2624
  };
2625
+
2626
+ export const PDS_DEFAULT_CONFIG_EDITOR_METADATA = getDesignConfigEditorMetadata(
2627
+ presets.default
2628
+ );
2629
+
2630
+ export const PDS_DEFAULT_CONFIG_FORM_SCHEMA = buildDesignConfigFormSchema(
2631
+ presets.default
2632
+ );
1842
2633
  // Note: presets is now a stable object keyed by id
1843
2634
 
1844
2635
  /**