@pure-ds/core 0.4.37 → 0.5.2

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 (146) hide show
  1. package/dist/types/pds.d.ts +34 -11
  2. package/dist/types/public/assets/auto-definer-XWHRBQPU.d.ts +9 -0
  3. package/dist/types/public/assets/auto-definer-XWHRBQPU.d.ts.map +1 -0
  4. package/dist/types/public/assets/chunk-746HIXIK.d.ts +52 -0
  5. package/dist/types/public/assets/chunk-746HIXIK.d.ts.map +1 -0
  6. package/dist/types/public/assets/chunk-APJV5T3J.d.ts +106 -0
  7. package/dist/types/public/assets/chunk-APJV5T3J.d.ts.map +1 -0
  8. package/dist/types/public/assets/chunk-BEPKFFM7.d.ts +398 -0
  9. package/dist/types/public/assets/chunk-BEPKFFM7.d.ts.map +1 -0
  10. package/dist/types/public/assets/chunk-ISS7UH5H.d.ts +2424 -0
  11. package/dist/types/public/assets/chunk-ISS7UH5H.d.ts.map +1 -0
  12. package/dist/types/public/assets/chunk-RUPLQUDG.d.ts +582 -0
  13. package/dist/types/public/assets/chunk-RUPLQUDG.d.ts.map +1 -0
  14. package/dist/types/public/assets/chunk-USML4NYF.d.ts +18 -0
  15. package/dist/types/public/assets/chunk-USML4NYF.d.ts.map +1 -0
  16. package/dist/types/public/assets/chunk-Z47A3HLT.d.ts +3 -0
  17. package/dist/types/public/assets/chunk-Z47A3HLT.d.ts.map +1 -0
  18. package/dist/types/public/assets/js/auto-definer-HZLD2XF4.d.ts +9 -0
  19. package/dist/types/public/assets/js/auto-definer-HZLD2XF4.d.ts.map +1 -0
  20. package/dist/types/public/assets/js/chunk-6A6DFAIG.d.ts +88 -0
  21. package/dist/types/public/assets/js/chunk-6A6DFAIG.d.ts.map +1 -0
  22. package/dist/types/public/assets/js/chunk-746HIXIK.d.ts +52 -0
  23. package/dist/types/public/assets/js/chunk-746HIXIK.d.ts.map +1 -0
  24. package/dist/types/public/assets/js/chunk-A3TZGIYX.d.ts +4 -0
  25. package/dist/types/public/assets/js/chunk-A3TZGIYX.d.ts.map +1 -0
  26. package/dist/types/public/assets/js/chunk-BEPKFFM7.d.ts +398 -0
  27. package/dist/types/public/assets/js/chunk-BEPKFFM7.d.ts.map +1 -0
  28. package/dist/types/public/assets/js/chunk-OTTRJ5MB.d.ts +1695 -0
  29. package/dist/types/public/assets/js/chunk-OTTRJ5MB.d.ts.map +1 -0
  30. package/dist/types/public/assets/js/chunk-RBPKHG76.d.ts +747 -0
  31. package/dist/types/public/assets/js/chunk-RBPKHG76.d.ts.map +1 -0
  32. package/dist/types/public/assets/js/chunk-RUPLQUDG.d.ts +582 -0
  33. package/dist/types/public/assets/js/chunk-RUPLQUDG.d.ts.map +1 -0
  34. package/dist/types/public/assets/js/chunk-SMD2R3CX.d.ts +68 -0
  35. package/dist/types/public/assets/js/chunk-SMD2R3CX.d.ts.map +1 -0
  36. package/dist/types/public/assets/js/chunk-Y73DA2D5.d.ts +15 -0
  37. package/dist/types/public/assets/js/chunk-Y73DA2D5.d.ts.map +1 -0
  38. package/dist/types/public/assets/js/chunks/auto-definer-X7MSXKTU.d.ts +9 -0
  39. package/dist/types/public/assets/js/chunks/auto-definer-X7MSXKTU.d.ts.map +1 -0
  40. package/dist/types/public/assets/js/chunks/chunk-7BDQH5CT.d.ts +485 -0
  41. package/dist/types/public/assets/js/chunks/chunk-7BDQH5CT.d.ts.map +1 -0
  42. package/dist/types/public/assets/js/chunks/chunk-MWB3S7NG.d.ts +3 -0
  43. package/dist/types/public/assets/js/chunks/chunk-MWB3S7NG.d.ts.map +1 -0
  44. package/dist/types/public/assets/js/chunks/chunk-WIMLORAU.d.ts +5 -0
  45. package/dist/types/public/assets/js/chunks/chunk-WIMLORAU.d.ts.map +1 -0
  46. package/dist/types/public/assets/js/chunks/chunk-WN4Y2ELN.d.ts +833 -0
  47. package/dist/types/public/assets/js/chunks/chunk-WN4Y2ELN.d.ts.map +1 -0
  48. package/dist/types/public/assets/js/chunks/chunk-XQOUIBLO.d.ts +1687 -0
  49. package/dist/types/public/assets/js/chunks/chunk-XQOUIBLO.d.ts.map +1 -0
  50. package/dist/types/public/assets/js/chunks/font-loader-VN5SRNOD.d.ts +5 -0
  51. package/dist/types/public/assets/js/chunks/font-loader-VN5SRNOD.d.ts.map +1 -0
  52. package/dist/types/public/assets/js/chunks/pds-live-validation-BQPWN5JG.d.ts +38 -0
  53. package/dist/types/public/assets/js/chunks/pds-live-validation-BQPWN5JG.d.ts.map +1 -0
  54. package/dist/types/public/assets/js/common-WIAC4WAJ.d.ts +4 -0
  55. package/dist/types/public/assets/js/common-WIAC4WAJ.d.ts.map +1 -0
  56. package/dist/types/public/assets/js/pds-config-WEBAXXSM.d.ts +4 -0
  57. package/dist/types/public/assets/js/pds-config-WEBAXXSM.d.ts.map +1 -0
  58. package/dist/types/public/assets/js/pds-core/pds-generator.d.ts +700 -0
  59. package/dist/types/public/assets/js/pds-core/pds-generator.d.ts.map +1 -0
  60. package/dist/types/public/assets/js/pds-core/pds-utilities.d.ts +27 -0
  61. package/dist/types/public/assets/js/pds-core/pds-utilities.d.ts.map +1 -0
  62. package/dist/types/public/assets/js/pds-enums-DCBZHS64.d.ts +3 -0
  63. package/dist/types/public/assets/js/pds-enums-DCBZHS64.d.ts.map +1 -0
  64. package/dist/types/public/assets/js/pds-gen.d.ts +106 -0
  65. package/dist/types/public/assets/js/pds-gen.d.ts.map +1 -0
  66. package/dist/types/public/assets/js/pds-live.d.ts +11 -0
  67. package/dist/types/public/assets/js/pds-live.d.ts.map +1 -0
  68. package/dist/types/public/assets/js/pds-manager.d.ts +1047 -0
  69. package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -0
  70. package/dist/types/public/assets/js/pds-ontology-2DICJXHO.d.ts +9 -0
  71. package/dist/types/public/assets/js/pds-ontology-2DICJXHO.d.ts.map +1 -0
  72. package/dist/types/public/assets/js/pds-query-B54LBKKR.d.ts +70 -0
  73. package/dist/types/public/assets/js/pds-query-B54LBKKR.d.ts.map +1 -0
  74. package/dist/types/public/assets/js/pds.d.ts +1 -18
  75. package/dist/types/public/assets/js/pds.d.ts.map +1 -1
  76. package/dist/types/public/assets/pds-ontology-ZO6TJHO3.d.ts +9 -0
  77. package/dist/types/public/assets/pds-ontology-ZO6TJHO3.d.ts.map +1 -0
  78. package/dist/types/src/js/common/pds-core/pds-config.d.ts +757 -0
  79. package/dist/types/src/js/common/pds-core/pds-config.d.ts.map +1 -0
  80. package/dist/types/src/js/common/pds-core/pds-enhancers.d.ts +28 -0
  81. package/dist/types/src/js/common/pds-core/pds-enhancers.d.ts.map +1 -0
  82. package/dist/types/src/js/common/pds-core/pds-enums.d.ts +87 -0
  83. package/dist/types/src/js/common/pds-core/pds-enums.d.ts.map +1 -0
  84. package/dist/types/src/js/common/pds-core/pds-generator.d.ts +700 -0
  85. package/dist/types/src/js/common/pds-core/pds-generator.d.ts.map +1 -0
  86. package/dist/types/src/js/common/pds-core/pds-ontology.d.ts +380 -0
  87. package/dist/types/src/js/common/pds-core/pds-ontology.d.ts.map +1 -0
  88. package/dist/types/src/js/common/pds-core/pds-paths.d.ts +37 -0
  89. package/dist/types/src/js/common/pds-core/pds-paths.d.ts.map +1 -0
  90. package/dist/types/src/js/common/pds-core/pds-query.d.ts +102 -0
  91. package/dist/types/src/js/common/pds-core/pds-query.d.ts.map +1 -0
  92. package/dist/types/src/js/common/pds-core/pds-registry.d.ts +35 -0
  93. package/dist/types/src/js/common/pds-core/pds-registry.d.ts.map +1 -0
  94. package/dist/types/src/js/common/pds-core/pds-utilities.d.ts +27 -0
  95. package/dist/types/src/js/common/pds-core/pds-utilities.d.ts.map +1 -0
  96. package/dist/types/src/js/pds-core/pds-generator.d.ts +38 -46
  97. package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
  98. package/dist/types/src/js/pds-core/pds-live.d.ts +39 -0
  99. package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -0
  100. package/dist/types/src/js/pds-core/pds-runtime.d.ts +39 -0
  101. package/dist/types/src/js/pds-core/pds-runtime.d.ts.map +1 -0
  102. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts +60 -0
  103. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts.map +1 -0
  104. package/dist/types/src/js/pds-core/pds-utilities.d.ts +27 -0
  105. package/dist/types/src/js/pds-core/pds-utilities.d.ts.map +1 -0
  106. package/dist/types/src/js/pds-gen.d.ts +48 -0
  107. package/dist/types/src/js/pds-gen.d.ts.map +1 -0
  108. package/dist/types/src/js/pds-live-runtime.d.ts +7 -0
  109. package/dist/types/src/js/pds-live-runtime.d.ts.map +1 -0
  110. package/dist/types/src/js/pds-live-validation.d.ts +44 -0
  111. package/dist/types/src/js/pds-live-validation.d.ts.map +1 -0
  112. package/dist/types/src/js/pds-live.d.ts +11 -0
  113. package/dist/types/src/js/pds-live.d.ts.map +1 -0
  114. package/dist/types/src/js/pds-manager.d.ts +2 -0
  115. package/dist/types/src/js/pds-manager.d.ts.map +1 -0
  116. package/dist/types/src/js/pds.d.ts +6 -33
  117. package/dist/types/src/js/pds.d.ts.map +1 -1
  118. package/package.json +11 -12
  119. package/packages/pds-cli/bin/{generate-css-data.mjs → generate-css-data.js} +563 -563
  120. package/packages/pds-cli/bin/{generate-manifest.mjs → generate-manifest.js} +352 -352
  121. package/packages/pds-cli/bin/{pds-build-icons.mjs → pds-build-icons.js} +152 -152
  122. package/packages/pds-cli/bin/{pds-dx.mjs → pds-dx.js} +114 -114
  123. package/packages/pds-cli/bin/{pds-init-config.mjs → pds-init-config.js} +34 -34
  124. package/packages/pds-cli/bin/{pds-setup-copilot.mjs → pds-setup-copilot.js} +106 -106
  125. package/packages/pds-cli/bin/{pds-static.mjs → pds-static.js} +597 -581
  126. package/packages/pds-cli/bin/{pds.mjs → pds.js} +127 -127
  127. package/packages/pds-cli/bin/postinstall.mjs +522 -563
  128. package/packages/pds-cli/bin/{sync-assets.mjs → sync-assets.js} +251 -251
  129. package/packages/pds-cli/lib/{asset-roots.mjs → asset-roots.js} +47 -47
  130. package/packages/pds-cli/lib/{fs-writer.mjs → fs-writer.js} +75 -75
  131. package/public/assets/js/app.js +95 -118
  132. package/public/assets/js/pds-manager.js +3251 -0
  133. package/public/assets/js/pds.js +10 -3201
  134. package/readme.md +2014 -2016
  135. package/src/js/pds-core/pds-enhancers.js +518 -518
  136. package/src/js/pds-core/pds-enums.js +86 -86
  137. package/src/js/pds-core/pds-generator.js +255 -185
  138. package/src/js/pds-core/pds-live.js +434 -0
  139. package/src/js/pds-core/pds-paths.js +109 -109
  140. package/src/js/pds-core/pds-registry.js +79 -79
  141. package/src/js/pds-core/pds-runtime.js +184 -0
  142. package/src/js/pds-core/pds-start-helpers.js +405 -0
  143. package/src/js/pds.d.ts +34 -11
  144. package/src/js/pds.js +43 -1182
  145. package/getting-started.md +0 -626
  146. package/src/js/pds-core/pds.d.ts +0 -129
package/src/js/pds.js CHANGED
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * This object exposes the core runtime building blocks for the Pure Design System.
7
7
  * It intentionally provides a small, stable surface area so consuming apps can:
8
- * - programmatically generate design system artifacts (via `Generator`),
8
+ * - programmatically generate design system artifacts (via `getGenerator()` in live mode),
9
9
  * - adopt styles into Shadow DOM (via `adoptLayers` / `adoptPrimitives`),
10
10
  * - query runtime mode and obtain constructable stylesheets (via `registry`).
11
11
  *
@@ -21,14 +21,13 @@
21
21
  * `CSSStyleSheet.replaceSync()` — callers should guard invalid input or wrap calls in try/catch.
22
22
  *
23
23
  * @typedef {Object} PDSAPI
24
- * @property {typeof import("./pds-core/pds-generator.js").Generator} Generator - Generator class to produce design system assets
25
24
  * @property {import("./pds-core/pds-registry.js").PDSRegistry} registry - Singleton runtime registry for live/static mode
26
- * @property {any} ontology - Ontology helpers and metadata for components
25
+ * @property {(generator?: import("./pds-core/pds-generator.js").Generator) => void} applyStyles - Apply generated styles to the document (live-only)
27
26
  * @property {(shadowRoot: ShadowRoot, layers?: string[], additionalSheets?: CSSStyleSheet[]) => Promise<void>} adoptLayers - Adopt multiple layers into a ShadowRoot. May log errors and fallback to additionalSheets when static imports fail.
28
27
  * @property {(shadowRoot: ShadowRoot, additionalSheets?: CSSStyleSheet[]) => Promise<void>} adoptPrimitives - Adopt primitives layer into a ShadowRoot. Designed as a convenience for components.
29
28
  * @property {(css:string) => CSSStyleSheet} createStylesheet - Create a constructable stylesheet from CSS text. @throws {DOMException} on invalid CSS in some browsers.
30
29
  * @property {() => boolean} isLiveMode - Returns true when running in live/designer-backed mode
31
- * @property {(el: Element) => import("./pds-core/pds-ontology.js").ComponentDef | null} findComponentForElement - Helper to find a component definition for a DOM element
30
+ * @property {() => Promise<typeof import("./pds-core/pds-generator.js").Generator>} getGenerator - Live-only accessor for the Generator class
32
31
  */
33
32
 
34
33
  /**
@@ -45,35 +44,28 @@ PDS.initializing = false;
45
44
  PDS.currentPreset = null;
46
45
 
47
46
  import {
48
- Generator,
49
47
  adoptLayers,
50
48
  adoptPrimitives,
51
49
  createStylesheet,
52
- } from "./pds-core/pds-generator.js";
50
+ } from "./pds-core/pds-runtime.js";
53
51
  import { registry } from "./pds-core/pds-registry.js";
54
- import ontology from "./pds-core/pds-ontology.js";
55
- import { findComponentForElement } from "./pds-core/pds-ontology.js";
56
- import { presets, defaultLog } from "./pds-core/pds-config.js";
57
- import { enums } from "./pds-core/pds-enums.js";
58
52
  import { ask } from "./common/ask.js";
59
53
  import { toast } from "./common/toast.js";
60
- import { PDSQuery } from "./pds-core/pds-query.js";
61
- import * as common from "./common/common.js";
62
54
  import { defaultPDSEnhancers } from "./pds-core/pds-enhancers.js";
63
55
  import { resolvePublicAssetURL } from "./pds-core/pds-paths.js";
64
-
65
- // Font loading utilities
66
- import { loadTypographyFonts } from "./common/font-loader.js";
67
-
68
- /** Generator class — use to programmatically create design system assets from a config */
69
- PDS.Generator = Generator;
56
+ import {
57
+ ensureAbsoluteAssetURL,
58
+ ensureTrailingSlash,
59
+ normalizeInitConfig,
60
+ resolveRuntimeAssetRoot,
61
+ resolveThemeAndApply,
62
+ setupAutoDefinerAndEnhancers,
63
+ stripFunctions,
64
+ } from "./pds-core/pds-start-helpers.js";
70
65
 
71
66
  /** Singleton runtime registry. Use `registry.setLiveMode()` to enable live mode or `registry.setStaticMode()` for static assets */
72
67
  PDS.registry = registry;
73
68
 
74
- /** Ontology and metadata about components and tokens */
75
- PDS.ontology = ontology;
76
-
77
69
  /** Adopt a set of layered stylesheets into a ShadowRoot */
78
70
  PDS.adoptLayers = adoptLayers;
79
71
 
@@ -83,35 +75,12 @@ PDS.adoptPrimitives = adoptPrimitives;
83
75
  /** Create a constructable CSSStyleSheet from a CSS string */
84
76
  PDS.createStylesheet = createStylesheet;
85
77
 
78
+
86
79
  /** Return true when running inside a live/designer-backed environment */
87
80
  PDS.isLiveMode = () => registry.isLive;
88
- PDS.enums = enums;
89
-
90
81
  PDS.ask = ask;
91
82
  PDS.toast = toast;
92
83
 
93
- // Expose common utilities (deepMerge, isObject, etc.)
94
- PDS.common = common;
95
-
96
- // Expose presets object directly
97
- PDS.presets = presets;
98
-
99
- /** Find a component definition (ontology) for a given DOM element */
100
- PDS.findComponentForElement = findComponentForElement;
101
-
102
- /**
103
- * Smart query interface for design system questions
104
- * @param {string} question - Natural language query about tokens, components, utilities, or patterns
105
- * @returns {Promise<Array>} Array of results with text, value, icon, category, code examples
106
- * @example
107
- * const results = await PDS.query("what is the focus border color on inputs?");
108
- * const results = await PDS.query("how do I create an icon-only button?");
109
- */
110
- PDS.query = async function(question) {
111
- const queryEngine = new PDSQuery(PDS);
112
- return await queryEngine.search(question);
113
- };
114
-
115
84
  function __emitPDSReady(detail) {
116
85
  const hasCustomEvent = typeof CustomEvent === "function";
117
86
 
@@ -150,56 +119,12 @@ Object.defineProperty(PDS, "currentConfig", {
150
119
  configurable: false,
151
120
  });
152
121
 
153
- /**
154
- * Compiled design system state - provides structured access to all generated tokens,
155
- * layers, and metadata. Available in live mode when a generator is active.
156
- * Returns the generator's compiled representation or null if not in live mode.
157
- *
158
- * Structure includes:
159
- * - tokens: All generated token groups (colors, spacing, typography, etc.)
160
- * - layers: CSS content and metadata for each layer (tokens, primitives, components, utilities)
161
- * - config: Configuration snapshot used to generate the current state
162
- * - capabilities: Runtime environment capabilities
163
- * - references: Links to ontology and enums for introspection
164
- * - meta: Computed metadata about the design system
165
- * - helpers: Utility methods to query the compiled state
166
- */
167
- Object.defineProperty(PDS, "compiled", {
168
- get() {
169
- // Only available in live mode when we have a generator
170
- if (PDS.registry?.isLive && Generator.instance) {
171
- return Generator.instance.compiled;
172
- }
173
- return null;
174
- },
175
- enumerable: true,
176
- configurable: false,
177
- });
178
-
179
122
  // Always expose PDS on the window in browser contexts so consumers can access it in both live and static modes
180
123
  if (typeof window !== "undefined") {
181
124
  // @ts-ignore
182
125
  window.PDS = PDS;
183
126
  }
184
127
 
185
- // ---------------------------------------------------------------------------
186
- // FOUC Prevention: Add pds-ready class when PDS is fully initialized in live mode
187
- // This works in conjunction with CSS injected by the live() function
188
- if (typeof document !== "undefined") {
189
- PDS.addEventListener("pds:ready", (event) => {
190
- const mode = event.detail?.mode;
191
- if (mode) {
192
- // Add mode-specific class (pds-live or pds-static)
193
- document.documentElement.classList.add(`pds-${mode}`);
194
-
195
- // Only add pds-ready class in live mode for FOUC prevention
196
- if (mode === "live") {
197
- document.documentElement.classList.add("pds-ready");
198
- }
199
- }
200
- });
201
- }
202
-
203
128
  // ---------------------------------------------------------------------------
204
129
  // Theme management (centralized on PDS.theme)
205
130
  // Consumers may read/write `PDS.theme` with values: 'system' | 'light' | 'dark'
@@ -313,6 +238,10 @@ Object.defineProperty(PDS, "theme", {
313
238
  },
314
239
  });
315
240
 
241
+ // Internal theme helpers (used by configurator/live tooling)
242
+ PDS._applyResolvedTheme = __applyResolvedTheme;
243
+ PDS._setupSystemListenerIfNeeded = __setupSystemListenerIfNeeded;
244
+
316
245
  // ----------------------------------------------------------------------------
317
246
  // Default Enhancers — first-class citizens alongside AutoDefiner
318
247
  // ----------------------------------------------------------------------------
@@ -323,264 +252,7 @@ Object.defineProperty(PDS, "theme", {
323
252
  */
324
253
  PDS.defaultEnhancers = defaultPDSEnhancers;
325
254
 
326
- /**
327
- * Validate a design configuration for accessibility sanity checks.
328
- * Currently validates color contrast for primary buttons and base surface text
329
- * in both light and dark themes.
330
- *
331
- * @param {object} designConfig - A full or partial PDS config object
332
- * @param {object} [options]
333
- * @param {number} [options.minContrast=4.5] - Minimum contrast ratio for normal text
334
- * @returns {{ ok: boolean, issues: Array<{path:string, message:string, ratio:number, min:number, context?:string}> }}
335
- */
336
- function validateDesign(designConfig = {}, options = {}) {
337
- const MIN = Number(options.minContrast || 4.5);
338
255
 
339
- // Local helpers (keep public; no dependency on private Generator methods)
340
- const hexToRgb = (hex) => {
341
- const h = String(hex || "").replace("#", "");
342
- const full =
343
- h.length === 3
344
- ? h
345
- .split("")
346
- .map((c) => c + c)
347
- .join("")
348
- : h;
349
- const num = parseInt(full || "0", 16);
350
- return { r: (num >> 16) & 255, g: (num >> 8) & 255, b: num & 255 };
351
- };
352
- const luminance = (hex) => {
353
- const { r, g, b } = hexToRgb(hex);
354
- const srgb = [r / 255, g / 255, b / 255].map((v) =>
355
- v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)
356
- );
357
- return 0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2];
358
- };
359
- const contrast = (a, b) => {
360
- if (!a || !b) return 0;
361
- const L1 = luminance(a);
362
- const L2 = luminance(b);
363
- const lighter = Math.max(L1, L2);
364
- const darker = Math.min(L1, L2);
365
- return (lighter + 0.05) / (darker + 0.05);
366
- };
367
-
368
- const issues = [];
369
- try {
370
- // Build tokens from the candidate config
371
- const gen = new PDS.Generator({ design: structuredClone(designConfig) });
372
- const c = gen.tokens.colors;
373
-
374
- // Light theme checks - use computed interactive tokens
375
- const light = {
376
- surfaceBg: c.surface?.base,
377
- surfaceText: c.gray?.[900] || "#000000",
378
- primaryFill: c.interactive?.light?.fill || c.primary?.[600],
379
- primaryText: c.interactive?.light?.text || c.primary?.[600],
380
- };
381
-
382
- // Primary button (light): check button fill with white text
383
- const lightBtnRatio = contrast(light.primaryFill, "#ffffff");
384
- if (lightBtnRatio < MIN) {
385
- issues.push({
386
- path: "/colors/primary",
387
- message: `Primary button contrast too low in light theme (${lightBtnRatio.toFixed(
388
- 2
389
- )} < ${MIN}). Choose a darker primary.`,
390
- ratio: lightBtnRatio,
391
- min: MIN,
392
- context: "light/btn-primary",
393
- });
394
- }
395
-
396
- // Surface text (light): text vs surface base
397
- const lightTextRatio = contrast(light.surfaceBg, light.surfaceText);
398
- if (lightTextRatio < MIN) {
399
- issues.push({
400
- path: "/colors/background",
401
- message: `Base text contrast on surface (light) is too low (${lightTextRatio.toFixed(
402
- 2
403
- )} < ${MIN}). Adjust background or secondary (gray).`,
404
- ratio: lightTextRatio,
405
- min: MIN,
406
- context: "light/surface-text",
407
- });
408
- }
409
-
410
- // Primary text for outline/link: check link text on surface
411
- const lightOutlineRatio = contrast(light.primaryText, light.surfaceBg);
412
- if (lightOutlineRatio < MIN) {
413
- issues.push({
414
- path: "/colors/primary",
415
- message: `Primary text on surface is too low for outline/link styles (light) (${lightOutlineRatio.toFixed(
416
- 2
417
- )} < ${MIN}). Choose a darker primary or lighter surface.`,
418
- ratio: lightOutlineRatio,
419
- min: MIN,
420
- context: "light/outline",
421
- });
422
- }
423
-
424
- // Dark theme checks - use computed interactive tokens
425
- const d = c.dark;
426
- if (d) {
427
- const dark = {
428
- surfaceBg: d.surface?.base || c.surface?.inverse,
429
- primaryFill: c.interactive?.dark?.fill || d.primary?.[600],
430
- primaryText: c.interactive?.dark?.text || d.primary?.[600],
431
- };
432
-
433
- // Primary button (dark): check button fill with white text
434
- const darkBtnRatio = contrast(dark.primaryFill, "#ffffff");
435
- if (darkBtnRatio < MIN) {
436
- issues.push({
437
- path: "/colors/darkMode/primary",
438
- message: `Primary button contrast too low in dark theme (${darkBtnRatio.toFixed(
439
- 2
440
- )} < ${MIN}). Override darkMode.primary or pick a brighter hue.`,
441
- ratio: darkBtnRatio,
442
- min: MIN,
443
- context: "dark/btn-primary",
444
- });
445
- }
446
-
447
- // Outline/link style in dark: check link text on dark surface
448
- const darkOutlineRatio = contrast(dark.primaryText, dark.surfaceBg);
449
- if (darkOutlineRatio < MIN) {
450
- issues.push({
451
- path: "/colors/darkMode/primary",
452
- message: `Primary text on surface is too low for outline/link styles (dark) (${darkOutlineRatio.toFixed(
453
- 2
454
- )} < ${MIN}). Override darkMode.primary/background.`,
455
- ratio: darkOutlineRatio,
456
- min: MIN,
457
- context: "dark/outline",
458
- });
459
- }
460
- }
461
- } catch (err) {
462
- issues.push({
463
- path: "/",
464
- message: `Validation failed: ${String(err?.message || err)}`,
465
- ratio: 0,
466
- min: 0,
467
- });
468
- }
469
-
470
- return { ok: issues.length === 0, issues };
471
- }
472
-
473
- /** Expose validator on the public API */
474
- PDS.validateDesign = validateDesign;
475
-
476
- /**
477
- * Validate multiple design configurations at once.
478
- * Useful for build-time enforcement of preset compliance.
479
- *
480
- * @param {Array<object>} designs - Array of design configs; items may include an optional `name` property.
481
- * @param {object} [options] - Options forwarded to validateDesign (e.g., { minContrast })
482
- * @returns {{ ok: boolean, results: Array<{ name?: string, ok: boolean, issues: Array<{path:string, message:string, ratio:number, min:number, context?:string}> }> }}
483
- */
484
- function validateDesigns(designs = [], options = {}) {
485
- const results = [];
486
-
487
- const list = Array.isArray(designs)
488
- ? designs
489
- : designs && typeof designs === "object"
490
- ? Object.values(designs)
491
- : [];
492
-
493
- for (const item of list) {
494
- let name;
495
- let configToValidate = null;
496
-
497
- // Accept a few shapes:
498
- // - string => treat as preset id/name
499
- // - { preset, design?, name? } => resolve preset then merge overrides
500
- // - full config object (legacy) => validate directly
501
- if (typeof item === "string") {
502
- const id = String(item).toLowerCase();
503
- const found =
504
- presets?.[id] ||
505
- Object.values(presets || {}).find(
506
- (p) =>
507
- __slugify(p.name) === id ||
508
- String(p.name || "").toLowerCase() === id
509
- );
510
- if (!found) {
511
- results.push({
512
- name: item,
513
- ok: false,
514
- issues: [
515
- {
516
- path: "/",
517
- message: `Preset not found: ${item}`,
518
- ratio: 0,
519
- min: 0,
520
- },
521
- ],
522
- });
523
- continue;
524
- }
525
- name = found.name || id;
526
- configToValidate = structuredClone(found);
527
- } else if (item && typeof item === "object") {
528
- name = item.name || item.preset || undefined;
529
- if ("preset" in item || "design" in item) {
530
- const effectivePreset = String(item.preset || "default").toLowerCase();
531
- const found =
532
- presets?.[effectivePreset] ||
533
- Object.values(presets || {}).find(
534
- (p) =>
535
- __slugify(p.name) === effectivePreset ||
536
- String(p.name || "").toLowerCase() === effectivePreset
537
- );
538
- if (!found) {
539
- results.push({
540
- name,
541
- ok: false,
542
- issues: [
543
- {
544
- path: "/",
545
- message: `Preset not found: ${item.preset}`,
546
- ratio: 0,
547
- min: 0,
548
- },
549
- ],
550
- });
551
- continue;
552
- }
553
- let base = structuredClone(found);
554
- if (item.design && typeof item.design === "object") {
555
- base = __deepMerge(base, structuredClone(item.design));
556
- }
557
- configToValidate = base;
558
- } else {
559
- // Assume a full config object
560
- configToValidate = item;
561
- }
562
- }
563
-
564
- if (!configToValidate) {
565
- results.push({
566
- name,
567
- ok: false,
568
- issues: [
569
- { path: "/", message: "Invalid design entry", ratio: 0, min: 0 },
570
- ],
571
- });
572
- continue;
573
- }
574
-
575
- const { ok, issues } = validateDesign(configToValidate, options);
576
- results.push({ name, ok, issues });
577
- }
578
-
579
- return { ok: results.every((r) => r.ok), results };
580
- }
581
-
582
- /** Expose batch validator on the public API */
583
- PDS.validateDesigns = validateDesigns;
584
256
 
585
257
  /**
586
258
  * Initialize PDS in live mode with the given configuration (new unified shape).
@@ -615,677 +287,6 @@ PDS.validateDesigns = validateDesigns;
615
287
  * autoDefine: { predefine: ['pds-icon'] }
616
288
  * });
617
289
  */
618
- // Internal: resolve theme and set html[data-theme], return resolvedTheme and storedTheme
619
- function __resolveThemeAndApply({ manageTheme, themeStorageKey }) {
620
- let resolvedTheme = "light";
621
- let storedTheme = null;
622
- if (manageTheme && typeof window !== "undefined") {
623
- // Read raw preference (may be null, 'system', 'light', 'dark') using provided storage key
624
- try {
625
- storedTheme = localStorage.getItem(themeStorageKey) || null;
626
- } catch (e) {
627
- storedTheme = null;
628
- }
629
-
630
- // Apply the resolved theme and ensure system listener exists when needed
631
- try {
632
- __applyResolvedTheme(storedTheme);
633
- __setupSystemListenerIfNeeded(storedTheme);
634
- } catch (e) {}
635
-
636
- // Compute explicit resolvedTheme to return
637
- if (storedTheme) {
638
- if (storedTheme === "system") {
639
- const prefersDark =
640
- window.matchMedia &&
641
- window.matchMedia("(prefers-color-scheme: dark)").matches;
642
- resolvedTheme = prefersDark ? "dark" : "light";
643
- } else {
644
- resolvedTheme = storedTheme;
645
- }
646
- } else {
647
- const prefersDark =
648
- window.matchMedia &&
649
- window.matchMedia("(prefers-color-scheme: dark)").matches;
650
- resolvedTheme = prefersDark ? "dark" : "light";
651
- }
652
- }
653
- return { resolvedTheme, storedTheme };
654
- }
655
-
656
- // Internal: deep merge utility (arrays replace; objects merge)
657
- function __deepMerge(target = {}, source = {}) {
658
- if (!source || typeof source !== "object") return target;
659
- const out = Array.isArray(target) ? [...target] : { ...target };
660
- for (const [key, value] of Object.entries(source)) {
661
- if (value && typeof value === "object" && !Array.isArray(value)) {
662
- out[key] = __deepMerge(
663
- out[key] && typeof out[key] === "object" ? out[key] : {},
664
- value
665
- );
666
- } else {
667
- out[key] = value;
668
- }
669
- }
670
- return out;
671
- }
672
-
673
- // Internal: create a slug for matching names like "Paper & Ink" -> "paper-and-ink"
674
- function __slugify(str = "") {
675
- return String(str)
676
- .toLowerCase()
677
- .replace(/&/g, " and ")
678
- .replace(/[^a-z0-9]+/g, "-")
679
- .replace(/^-+|-+$/g, "");
680
- }
681
-
682
- // Internal: recursively remove functions from an object to make it cloneable
683
- function __stripFunctions(obj) {
684
- if (obj === null || obj === undefined) return obj;
685
- if (typeof obj === "function") return undefined;
686
- if (typeof obj !== "object") return obj;
687
-
688
- if (Array.isArray(obj)) {
689
- return obj.map(item => __stripFunctions(item)).filter(item => item !== undefined);
690
- }
691
-
692
- const result = {};
693
- for (const key in obj) {
694
- if (obj.hasOwnProperty(key)) {
695
- const value = obj[key];
696
- if (typeof value !== "function") {
697
- const stripped = __stripFunctions(value);
698
- if (stripped !== undefined) {
699
- result[key] = stripped;
700
- }
701
- }
702
- }
703
- }
704
- return result;
705
- }
706
-
707
- const __ABSOLUTE_URL_PATTERN__ = /^[a-z][a-z0-9+\-.]*:\/\//i;
708
- const __MODULE_URL__ = (() => {
709
- try {
710
- return import.meta.url;
711
- } catch (e) {
712
- return undefined;
713
- }
714
- })();
715
-
716
- function __ensureAbsoluteAssetURL(value, options = {}) {
717
- if (!value || __ABSOLUTE_URL_PATTERN__.test(value)) {
718
- return value;
719
- }
720
-
721
- const { preferModule = true } = options;
722
-
723
- const tryModule = () => {
724
- if (!__MODULE_URL__) return null;
725
- try {
726
- return new URL(value, __MODULE_URL__).href;
727
- } catch (e) {
728
- return null;
729
- }
730
- };
731
-
732
- const tryWindow = () => {
733
- if (typeof window === "undefined" || !window.location?.origin) {
734
- return null;
735
- }
736
- try {
737
- return new URL(value, window.location.origin).href;
738
- } catch (e) {
739
- return null;
740
- }
741
- };
742
-
743
- const resolved = preferModule
744
- ? tryModule() || tryWindow()
745
- : tryWindow() || tryModule();
746
-
747
- return resolved || value;
748
- }
749
-
750
- const __ensureTrailingSlash = (value) =>
751
- typeof value === "string" && value.length && !value.endsWith("/")
752
- ? `${value}/`
753
- : value;
754
-
755
- const __MODULE_DEFAULT_ASSET_ROOT__ = (() => {
756
- if (!__MODULE_URL__) return undefined;
757
- try {
758
- const parsed = new URL(__MODULE_URL__);
759
- if (/\/public\/assets\/js\//.test(parsed.pathname)) {
760
- return new URL("../pds/", __MODULE_URL__).href;
761
- }
762
- } catch (e) {
763
- return undefined;
764
- }
765
- return undefined;
766
- })();
767
-
768
- function __resolveRuntimeAssetRoot(config) {
769
- const hasCustomRoot = Boolean(config?.public?.root || config?.static?.root);
770
- let candidate = resolvePublicAssetURL(config);
771
-
772
- if (!hasCustomRoot && __MODULE_DEFAULT_ASSET_ROOT__) {
773
- candidate = __MODULE_DEFAULT_ASSET_ROOT__;
774
- }
775
-
776
- return __ensureTrailingSlash(__ensureAbsoluteAssetURL(candidate));
777
- }
778
-
779
- // Internal: normalize first-arg config to a full generator config and extract enhancers if provided inline
780
- function __normalizeInitConfig(inputConfig = {}, options = {}) {
781
- // If caller passed a plain design config (legacy), keep as-is
782
- const hasDesignKeys =
783
- typeof inputConfig === "object" &&
784
- ("colors" in inputConfig ||
785
- "typography" in inputConfig ||
786
- "spatialRhythm" in inputConfig ||
787
- "shape" in inputConfig ||
788
- "behavior" in inputConfig ||
789
- "layout" in inputConfig ||
790
- "advanced" in inputConfig ||
791
- "a11y" in inputConfig ||
792
- "components" in inputConfig ||
793
- "icons" in inputConfig);
794
-
795
- // Extract potential inline enhancers from config; prefer inline over options
796
- let inlineEnhancers = inputConfig && inputConfig.enhancers;
797
- if (inlineEnhancers && !Array.isArray(inlineEnhancers)) {
798
- // If an object was provided, convert to array of values
799
- inlineEnhancers = Object.values(inlineEnhancers);
800
- }
801
- const enhancers = inlineEnhancers ?? options.enhancers ?? [];
802
-
803
- // New API: { preset?: string, design?: object }
804
- const presetId = inputConfig && inputConfig.preset;
805
- const designOverrides = inputConfig && inputConfig.design;
806
-
807
- const hasNewShape =
808
- "preset" in (inputConfig || {}) ||
809
- "design" in (inputConfig || {}) ||
810
- "enhancers" in (inputConfig || {});
811
-
812
- let generatorConfig;
813
- let presetInfo = null;
814
-
815
- if (hasNewShape) {
816
- // Always resolve a preset; default if none provided
817
- const effectivePreset = String(presetId || "default").toLowerCase();
818
- const found =
819
- presets?.[effectivePreset] ||
820
- Object.values(presets || {}).find(
821
- (p) =>
822
- __slugify(p.name) === effectivePreset ||
823
- String(p.name || "").toLowerCase() === effectivePreset
824
- );
825
- if (!found)
826
- throw new Error(`PDS preset not found: "${presetId || "default"}"`);
827
-
828
- presetInfo = {
829
- id: found.id || __slugify(found.name),
830
- name: found.name || found.id || String(effectivePreset),
831
- };
832
-
833
- // Merge preset with design overrides
834
- let mergedDesign = structuredClone(found);
835
- if (designOverrides && typeof designOverrides === "object") {
836
- // Strip functions before cloning to avoid DataCloneError
837
- const cloneableDesign = __stripFunctions(designOverrides);
838
- mergedDesign = __deepMerge(mergedDesign, structuredClone(cloneableDesign));
839
- }
840
-
841
- // Build structured config with design nested
842
- // Exclude runtime-specific properties that shouldn't be passed to Generator
843
- const {
844
- mode, autoDefine, applyGlobalStyles, manageTheme,
845
- themeStorageKey, preloadStyles, criticalLayers,
846
- preset: _preset, design: _design, enhancers: _enhancers,
847
- log: userLog, // Extract log if provided at root
848
- ...otherProps
849
- } = inputConfig;
850
-
851
- generatorConfig = {
852
- ...otherProps, // Keep only generator-relevant properties
853
- design: mergedDesign,
854
- preset: presetInfo.name,
855
- // Add log method at root level (use user's or default)
856
- log: userLog || defaultLog,
857
- };
858
- } else if (hasDesignKeys) {
859
- // Back-compat: treat the provided object as the full design, wrap it
860
- // Extract log before cloning to avoid DataCloneError
861
- const { log: userLog, ...designConfig } = inputConfig;
862
- generatorConfig = {
863
- design: structuredClone(designConfig),
864
- log: userLog || defaultLog,
865
- };
866
- } else {
867
- // Nothing recognizable: use default preset
868
- const foundDefault =
869
- presets?.["default"] ||
870
- Object.values(presets || {}).find((p) => __slugify(p.name) === "default");
871
- if (!foundDefault) throw new Error("PDS default preset not available");
872
- presetInfo = {
873
- id: foundDefault.id || "default",
874
- name: foundDefault.name || "Default",
875
- };
876
- generatorConfig = {
877
- design: structuredClone(foundDefault),
878
- preset: presetInfo.name,
879
- log: defaultLog,
880
- };
881
- }
882
-
883
- return { generatorConfig, enhancers, presetInfo };
884
- }
885
-
886
- // Internal: setup AutoDefiner and run enhancers
887
- async function __setupAutoDefinerAndEnhancers(options) {
888
- const {
889
- autoDefineBaseURL = "/auto-define/",
890
- autoDefinePreload = [],
891
- autoDefineMapper = null,
892
- enhancers = [],
893
- // New: raw overrides for AutoDefiner config (scanExisting, observeShadows, etc.)
894
- autoDefineOverrides = null,
895
- autoDefinePreferModule = true,
896
- } = options;
897
-
898
- // // Warn if assets not present (best-effort)
899
- // try {
900
- // if (typeof window !== "undefined") {
901
- // const response = await fetch(`${autoDefineBaseURL}pds-icon.js`, {
902
- // method: "HEAD",
903
- // });
904
- // if (!response.ok) {
905
- // // No config available in this context, using console
906
- // console.warn("⚠️ PDS components not found in auto-define directory.");
907
- // }
908
- // }
909
- // } catch {}
910
-
911
- // Merge defaults with user enhancers (user overrides by selector)
912
- const mergedEnhancers = (() => {
913
- const map = new Map();
914
- (PDS.defaultEnhancers || []).forEach((e) => map.set(e.selector, e));
915
- (enhancers || []).forEach((e) => map.set(e.selector, e));
916
- return Array.from(map.values());
917
- })();
918
-
919
- // Setup AutoDefiner in browser context (it already observes shadow DOMs)
920
- let autoDefiner = null;
921
- if (typeof window !== "undefined" && typeof document !== "undefined") {
922
- // Dynamically import AutoDefiner to avoid Node/CJS interop at build time
923
- let AutoDefinerCtor = null;
924
- try {
925
- const mod = await import("pure-web/auto-definer");
926
- AutoDefinerCtor =
927
- mod?.AutoDefiner || mod?.default?.AutoDefiner || mod?.default || null;
928
- } catch (e) {
929
- // No config available in this context, using console
930
- console.warn("AutoDefiner not available:", e?.message || e);
931
- }
932
-
933
- const defaultMapper = (tag) => {
934
- switch (tag) {
935
- case "pds-tabpanel":
936
- return "pds-tabstrip.js";
937
- default:
938
- return `${tag}.js`;
939
- }
940
- };
941
-
942
- // Respect user overrides but never allow them to overwrite our mapper wrapper.
943
- const { mapper: _overrideMapperIgnored, ...restAutoDefineOverrides } =
944
- autoDefineOverrides && typeof autoDefineOverrides === "object"
945
- ? autoDefineOverrides
946
- : {};
947
-
948
- const normalizedBaseURL = autoDefineBaseURL
949
- ? __ensureTrailingSlash(
950
- __ensureAbsoluteAssetURL(autoDefineBaseURL, {
951
- preferModule: autoDefinePreferModule,
952
- })
953
- )
954
- : autoDefineBaseURL;
955
-
956
- const autoDefineConfig = {
957
- baseURL: normalizedBaseURL,
958
- predefine: autoDefinePreload,
959
- scanExisting: true,
960
- observeShadows: true,
961
- patchAttachShadow: true,
962
- debounceMs: 16,
963
- enhancers: mergedEnhancers,
964
- onError: (tag, err) => {
965
- if (typeof tag === "string" && tag.startsWith("pds-")) {
966
- // Check if this is a Lit-dependent component with missing #pds/lit import map
967
- const litDependentComponents = ['pds-form', 'pds-drawer'];
968
- const isLitComponent = litDependentComponents.includes(tag);
969
- const isMissingLitError = err?.message?.includes('#pds/lit') ||
970
- err?.message?.includes('Failed to resolve module specifier');
971
-
972
- if (isLitComponent && isMissingLitError) {
973
- console.error(
974
- `❌ PDS component <${tag}> requires Lit but #pds/lit is not in import map.\n` +
975
- `Add this to your HTML <head>:\n` +
976
- `<script type="importmap">\n` +
977
- ` { "imports": { "#pds/lit": "./path/to/lit.js" } }\n` +
978
- `</script>\n` +
979
- `See: https://github.com/pure-ds/core#lit-components`
980
- );
981
- } else {
982
- // Generic component not found warning
983
- console.warn(
984
- `⚠️ PDS component <${tag}> not found. Assets may not be installed.`
985
- );
986
- }
987
- } else {
988
- console.error(`❌ Auto-define error for <${tag}>:`, err);
989
- }
990
- },
991
- // Apply all user overrides except mapper so we can still wrap it
992
- ...restAutoDefineOverrides,
993
- mapper: (tag) => {
994
- // If already defined, do nothing
995
- if (customElements.get(tag)) return null;
996
-
997
- // If a custom mapper exists, let it try first; if it returns a non-value, fallback to default
998
- if (typeof autoDefineMapper === "function") {
999
- try {
1000
- const mapped = autoDefineMapper(tag);
1001
- if (mapped === undefined) {
1002
- return defaultMapper(tag);
1003
- }
1004
- return mapped;
1005
- } catch (e) {
1006
- // Be resilient: if custom mapper throws, fall back to default
1007
- console.warn(
1008
- "Custom autoDefine.mapper error; falling back to default:",
1009
- e?.message || e
1010
- );
1011
- return defaultMapper(tag);
1012
- }
1013
- }
1014
-
1015
- // No custom mapper provided — use default
1016
- return defaultMapper(tag);
1017
- },
1018
- };
1019
-
1020
- if (AutoDefinerCtor) {
1021
- autoDefiner = new AutoDefinerCtor(autoDefineConfig);
1022
- if (
1023
- autoDefinePreload.length > 0 &&
1024
- typeof AutoDefinerCtor.define === "function"
1025
- ) {
1026
- await AutoDefinerCtor.define(...autoDefinePreload, {
1027
- baseURL: autoDefineBaseURL,
1028
- mapper: autoDefineConfig.mapper,
1029
- onError: autoDefineConfig.onError,
1030
- });
1031
- }
1032
- }
1033
- }
1034
- // Rely on AutoDefiner to run enhancers across light and shadow DOMs
1035
-
1036
- return { autoDefiner, mergedEnhancers };
1037
- }
1038
-
1039
- async function live(config) {
1040
- if (!config || typeof config !== "object") {
1041
- throw new Error(
1042
- "PDS.start({ mode: 'live', ... }) requires a valid configuration object"
1043
- );
1044
- }
1045
-
1046
- // FOUC Prevention: Use constructable stylesheet for synchronous, immediate effect
1047
- if (typeof document !== "undefined" && document.adoptedStyleSheets) {
1048
- const css = /*css*/`
1049
- html { opacity: 0; }
1050
- html.pds-ready { opacity: 1; transition: opacity 0.3s ease-in; }
1051
- `
1052
- try {
1053
- // Check if we've already added the FOUC prevention sheet
1054
- const hasFoucSheet = document.adoptedStyleSheets.some(sheet => sheet._pdsFouc);
1055
- if (!hasFoucSheet) {
1056
- const foucSheet = new CSSStyleSheet();
1057
- foucSheet.replaceSync(css);
1058
- foucSheet._pdsFouc = true;
1059
- document.adoptedStyleSheets = [foucSheet, ...document.adoptedStyleSheets];
1060
- }
1061
- } catch (e) {
1062
- // Fallback for browsers that don't support constructable stylesheets
1063
- // No config available here, using console
1064
- console.warn("Constructable stylesheets not supported, using <style> tag fallback:", e);
1065
- const existingFoucStyle = document.head.querySelector("style[data-pds-fouc]");
1066
- if (!existingFoucStyle) {
1067
- const foucStyle = document.createElement("style");
1068
- foucStyle.setAttribute("data-pds-fouc", "");
1069
- foucStyle.textContent = css;
1070
- document.head.insertBefore(foucStyle, document.head.firstChild);
1071
- }
1072
- }
1073
- }
1074
-
1075
- // PDS is exposed on window at module init for both modes
1076
-
1077
- // Extract runtime flags directly from unified config
1078
- let applyGlobalStyles = config.applyGlobalStyles ?? true;
1079
- let manageTheme = config.manageTheme ?? true;
1080
- let themeStorageKey = config.themeStorageKey ?? "pure-ds-theme";
1081
- let preloadStyles = config.preloadStyles ?? false;
1082
- let criticalLayers = config.criticalLayers ?? ["tokens", "primitives"];
1083
-
1084
- // New unified shape: autoDefine inside the first argument
1085
- const cfgAuto = (config && config.autoDefine) || null;
1086
- if (cfgAuto && typeof cfgAuto === "object") {
1087
- // no-op here; resolved below for __setupAutoDefinerAndEnhancers
1088
- }
1089
-
1090
- try {
1091
- // 1) Handle theme preference
1092
- const { resolvedTheme, storedTheme } = __resolveThemeAndApply({
1093
- manageTheme,
1094
- themeStorageKey,
1095
- });
1096
-
1097
- // 2) Normalize first-arg API: support { preset, design, enhancers }
1098
- const normalized = __normalizeInitConfig(config, {});
1099
- const userEnhancers = normalized.enhancers;
1100
- // Extract log function before cloning to avoid DataCloneError
1101
- const { log: logFn, ...configToClone } = normalized.generatorConfig;
1102
- const generatorConfig = structuredClone(configToClone);
1103
- // Add log back after cloning
1104
- generatorConfig.log = logFn;
1105
- if (manageTheme) {
1106
- generatorConfig.theme = resolvedTheme;
1107
- }
1108
-
1109
- const generator = new PDS.Generator(generatorConfig);
1110
-
1111
- // 3) Load fonts from Google Fonts if needed (before applying styles)
1112
- if (generatorConfig.design?.typography) {
1113
- try {
1114
- await loadTypographyFonts(generatorConfig.design.typography);
1115
- } catch (ex) {
1116
- generatorConfig?.log?.("warn", "Failed to load some fonts from Google Fonts:", ex);
1117
- // Continue anyway - the system will fall back to default fonts
1118
- }
1119
- }
1120
-
1121
- // 4) Preload critical styles synchronously to prevent flash
1122
- if (preloadStyles && typeof window !== "undefined" && document.head) {
1123
- try {
1124
- // Generate critical CSS layers synchronously
1125
- const criticalCSS = criticalLayers
1126
- .map((layer) => {
1127
- try {
1128
- return generator.css?.[layer] || "";
1129
- } catch (e) {
1130
- generatorConfig?.log?.(
1131
- "warn",
1132
- `Failed to generate critical CSS for layer "${layer}":`,
1133
- e
1134
- );
1135
- return "";
1136
- }
1137
- })
1138
- .filter((css) => css.trim())
1139
- .join("\n");
1140
-
1141
- if (criticalCSS) {
1142
- // Remove any existing PDS critical styles
1143
- const existingCritical = document.head.querySelector(
1144
- "style[data-pds-critical]"
1145
- );
1146
- if (existingCritical) {
1147
- existingCritical.remove();
1148
- }
1149
-
1150
- // Inject critical CSS as a <style> tag in head
1151
- const styleEl = document.createElement("style");
1152
- styleEl.setAttribute("data-pds-critical", "");
1153
- styleEl.textContent = criticalCSS;
1154
-
1155
- // Insert early in head, but after charset/viewport if present
1156
- const insertAfter = document.head.querySelector(
1157
- 'meta[charset], meta[name="viewport"]'
1158
- );
1159
- if (insertAfter) {
1160
- insertAfter.parentNode.insertBefore(
1161
- styleEl,
1162
- insertAfter.nextSibling
1163
- );
1164
- } else {
1165
- document.head.insertBefore(styleEl, document.head.firstChild);
1166
- }
1167
- }
1168
- } catch (error) {
1169
- generatorConfig?.log?.("warn", "Failed to preload critical styles:", error);
1170
- // Continue without critical styles - better than crashing
1171
- }
1172
- }
1173
-
1174
- // Set the registry to live mode
1175
- PDS.registry.setLiveMode();
1176
-
1177
- // Log preset info if available
1178
- if (normalized.presetInfo?.name) {
1179
- generatorConfig?.log?.("log", `PDS live with preset "${normalized.presetInfo.name}"`);
1180
- } else {
1181
- generatorConfig?.log?.("log", "PDS live with custom config");
1182
- }
1183
-
1184
- // Apply styles globally if requested (default behavior)
1185
- if (applyGlobalStyles) {
1186
- await PDS.Generator.applyStyles();
1187
-
1188
- // Clean up critical styles after adoptedStyleSheets are applied
1189
- if (typeof window !== "undefined") {
1190
- // Small delay to ensure adoptedStyleSheets have taken effect
1191
- setTimeout(() => {
1192
- // Remove any previously inlined critical/preload styles that were unlayered
1193
- const criticalStyle = document.head.querySelector(
1194
- "style[data-pds-critical]"
1195
- );
1196
- if (criticalStyle) criticalStyle.remove();
1197
-
1198
- const preloadStyle = document.head.querySelector(
1199
- "style[data-pds-preload]"
1200
- );
1201
- if (preloadStyle) preloadStyle.remove();
1202
-
1203
- // Remove legacy fallback runtime style tag if present
1204
- const legacyRuntime = document.getElementById(
1205
- "pds-runtime-stylesheet"
1206
- );
1207
- if (legacyRuntime) legacyRuntime.remove();
1208
- }, 100);
1209
- }
1210
- }
1211
-
1212
- // Note: auto-define base URL is used internally; no globals are written
1213
-
1214
- // Derive a sensible default AutoDefiner base for LIVE mode too when not provided.
1215
- // Use the normalized public asset root so live and static modes share the same directory layout.
1216
- const assetRootURL = __resolveRuntimeAssetRoot(config);
1217
-
1218
- let derivedAutoDefineBaseURL;
1219
- if (cfgAuto && cfgAuto.baseURL) {
1220
- derivedAutoDefineBaseURL = __ensureTrailingSlash(
1221
- __ensureAbsoluteAssetURL(cfgAuto.baseURL, { preferModule: false })
1222
- );
1223
- } else {
1224
- derivedAutoDefineBaseURL = `${assetRootURL}components/`;
1225
- }
1226
-
1227
- // 5) Set up AutoDefiner + run enhancers (defaults merged with user)
1228
- let autoDefiner = null;
1229
- let mergedEnhancers = [];
1230
- try {
1231
- const res = await __setupAutoDefinerAndEnhancers({
1232
- autoDefineBaseURL: derivedAutoDefineBaseURL,
1233
- autoDefinePreload:
1234
- (cfgAuto && Array.isArray(cfgAuto.predefine) && cfgAuto.predefine) ||
1235
- [],
1236
- autoDefineMapper:
1237
- (cfgAuto && typeof cfgAuto.mapper === "function" && cfgAuto.mapper) ||
1238
- null,
1239
- enhancers: userEnhancers,
1240
- autoDefineOverrides: cfgAuto || null,
1241
- autoDefinePreferModule: !(cfgAuto && cfgAuto.baseURL),
1242
- });
1243
- autoDefiner = res.autoDefiner;
1244
- mergedEnhancers = res.mergedEnhancers || [];
1245
- } catch (error) {
1246
- generatorConfig?.log?.("error", "❌ Failed to initialize AutoDefiner/Enhancers:", error);
1247
- }
1248
-
1249
- // Determine resolved config to expose (generator stores input as options)
1250
- const resolvedConfig = generator?.options || generatorConfig;
1251
-
1252
- // Expose current config as frozen read-only on PDS, preserving input shape
1253
- // Strip functions before cloning to avoid DataCloneError
1254
- const cloneableConfig = __stripFunctions(config);
1255
- PDS.currentConfig = Object.freeze({
1256
- mode: "live",
1257
- ...structuredClone(cloneableConfig),
1258
- design: structuredClone(normalized.generatorConfig.design),
1259
- preset: normalized.generatorConfig.preset,
1260
- theme: resolvedTheme,
1261
- enhancers: mergedEnhancers,
1262
- });
1263
-
1264
- // Emit event to notify that PDS is ready (unified)
1265
- __emitPDSReady({
1266
- mode: "live",
1267
- generator,
1268
- config: resolvedConfig,
1269
- theme: resolvedTheme,
1270
- autoDefiner,
1271
- });
1272
-
1273
- return {
1274
- generator,
1275
- config: resolvedConfig,
1276
- theme: resolvedTheme,
1277
- autoDefiner,
1278
- };
1279
- } catch (error) {
1280
- // Emit error event
1281
- PDS.dispatchEvent(
1282
- new CustomEvent("pds:error", {
1283
- detail: { error },
1284
- })
1285
- );
1286
- throw error;
1287
- }
1288
- }
1289
290
 
1290
291
  /**
1291
292
  * Start PDS given a unified configuration and an explicit mode.
@@ -1298,7 +299,19 @@ async function start(config) {
1298
299
  const mode = (config && config.mode) || "live";
1299
300
  const { mode: _omit, ...rest } = config || {};
1300
301
  if (mode === "static") return staticInit(rest);
1301
- return live(rest);
302
+ const assetRootURL = resolveRuntimeAssetRoot(rest, { resolvePublicAssetURL });
303
+ const managerUrl =
304
+ rest?.managerURL ||
305
+ rest?.public?.managerURL ||
306
+ rest?.manager?.url ||
307
+ new URL("core/pds-manager.js", assetRootURL).href ||
308
+ new URL("./pds-manager.js", import.meta.url).href;
309
+ const { startLive } = await import(managerUrl);
310
+ return startLive(PDS, rest, {
311
+ emitReady: __emitPDSReady,
312
+ applyResolvedTheme: __applyResolvedTheme,
313
+ setupSystemListenerIfNeeded: __setupSystemListenerIfNeeded,
314
+ });
1302
315
  }
1303
316
 
1304
317
  /** Primary unified entry point */
@@ -1337,12 +350,12 @@ async function staticInit(config) {
1337
350
  const manageTheme = config.manageTheme ?? true;
1338
351
  const themeStorageKey = config.themeStorageKey ?? "pure-ds-theme";
1339
352
  let staticPaths = config.staticPaths ?? {};
1340
- const assetRootURL = __resolveRuntimeAssetRoot(config);
353
+ const assetRootURL = resolveRuntimeAssetRoot(config, { resolvePublicAssetURL });
1341
354
  const cfgAuto = (config && config.autoDefine) || null;
1342
355
  let autoDefineBaseURL;
1343
356
  if (cfgAuto && cfgAuto.baseURL) {
1344
- autoDefineBaseURL = __ensureTrailingSlash(
1345
- __ensureAbsoluteAssetURL(cfgAuto.baseURL, { preferModule: false })
357
+ autoDefineBaseURL = ensureTrailingSlash(
358
+ ensureAbsoluteAssetURL(cfgAuto.baseURL, { preferModule: false })
1346
359
  );
1347
360
  } else {
1348
361
  autoDefineBaseURL = `${assetRootURL}components/`;
@@ -1354,13 +367,16 @@ async function staticInit(config) {
1354
367
 
1355
368
  try {
1356
369
  // 1) Theme
1357
- const { resolvedTheme } = __resolveThemeAndApply({
370
+ const { resolvedTheme } = resolveThemeAndApply({
1358
371
  manageTheme,
1359
372
  themeStorageKey,
373
+ applyResolvedTheme: __applyResolvedTheme,
374
+ setupSystemListenerIfNeeded: __setupSystemListenerIfNeeded,
1360
375
  });
1361
376
 
1362
377
  // Normalize first-arg to allow { preset, design, enhancers }
1363
- const normalized = __normalizeInitConfig(config, {});
378
+ const { presets, defaultLog } = await import("./pds-core/pds-config.js");
379
+ const normalized = normalizeInitConfig(config, {}, { presets, defaultLog });
1364
380
  const userEnhancers = normalized.enhancers;
1365
381
 
1366
382
  // 2) Derive static asset URLs from the normalized public root
@@ -1399,14 +415,14 @@ async function staticInit(config) {
1399
415
  let autoDefiner = null;
1400
416
  let mergedEnhancers = [];
1401
417
  try {
1402
- const res = await __setupAutoDefinerAndEnhancers({
418
+ const res = await setupAutoDefinerAndEnhancers({
1403
419
  autoDefineBaseURL,
1404
420
  autoDefinePreload,
1405
421
  autoDefineMapper,
1406
422
  enhancers: userEnhancers,
1407
423
  autoDefineOverrides: cfgAuto || null,
1408
424
  autoDefinePreferModule: !(cfgAuto && cfgAuto.baseURL),
1409
- });
425
+ }, { baseEnhancers: defaultPDSEnhancers });
1410
426
  autoDefiner = res.autoDefiner;
1411
427
  mergedEnhancers = res.mergedEnhancers || [];
1412
428
  } catch (error) {
@@ -1419,7 +435,7 @@ async function staticInit(config) {
1419
435
 
1420
436
  // Expose current config as frozen read-only on PDS, preserving input shape
1421
437
  // Strip functions before cloning to avoid DataCloneError
1422
- const cloneableConfig = __stripFunctions(config);
438
+ const cloneableConfig = stripFunctions(config);
1423
439
  PDS.currentConfig = Object.freeze({
1424
440
  mode: "static",
1425
441
  ...structuredClone(cloneableConfig),
@@ -1449,163 +465,8 @@ async function staticInit(config) {
1449
465
 
1450
466
  // Note: PDS.static is not exported. Use PDS.start({ mode: 'static', ... }).
1451
467
 
1452
- /**
1453
- * Change the current theme programmatically.
1454
- * This updates localStorage, the data-theme attribute, and regenerates styles if in live mode.
1455
- *
1456
- * @param {string} theme - Theme to apply: 'light', 'dark', or 'system'
1457
- * @param {object} [options] - Optional settings
1458
- * @param {string} [options.storageKey='pure-ds-theme'] - localStorage key for theme preference
1459
- * @param {boolean} [options.persist=true] - Whether to save to localStorage
1460
- * @returns {Promise<string>} The resolved theme ('light' or 'dark')
1461
- */
1462
- async function setTheme(theme, options = {}) {
1463
- const { storageKey = "pure-ds-theme", persist = true } = options;
1464
-
1465
- if (!["light", "dark", "system"].includes(theme)) {
1466
- throw new Error(
1467
- `Invalid theme "${theme}". Must be "light", "dark", or "system".`
1468
- );
1469
- }
1470
-
1471
- if (typeof window === "undefined") {
1472
- return theme === "system" ? "light" : theme;
1473
- }
1474
-
1475
- let resolvedTheme = theme;
1476
-
1477
- // Resolve 'system' to actual preference
1478
- if (theme === "system") {
1479
- const prefersDark =
1480
- window.matchMedia &&
1481
- window.matchMedia("(prefers-color-scheme: dark)").matches;
1482
- resolvedTheme = prefersDark ? "dark" : "light";
1483
- }
1484
-
1485
- // Update data-theme attribute
1486
- document.documentElement.setAttribute("data-theme", resolvedTheme);
1487
-
1488
- // Persist to localStorage if requested
1489
- if (persist) {
1490
- localStorage.setItem(storageKey, theme);
1491
- }
1492
-
1493
- // If we're in live mode, regenerate styles with new theme
1494
- if (PDS.registry.isLive && Generator.instance) {
1495
- try {
1496
- const currentGenerator = Generator.instance;
1497
- if (currentGenerator && currentGenerator.configure) {
1498
- // Update the generator's config with new theme
1499
- const newConfig = { ...currentGenerator.config, theme: resolvedTheme };
1500
- currentGenerator.configure(newConfig);
1501
-
1502
- // Reapply styles
1503
- await PDS.Generator.applyStyles();
1504
- }
1505
- } catch (error) {
1506
- console.warn("Failed to update styles for new theme:", error);
1507
- }
1508
- }
1509
-
1510
- // Emit theme change event (unified)
1511
- PDS.dispatchEvent(
1512
- new CustomEvent("pds:theme:changed", {
1513
- detail: {
1514
- theme: resolvedTheme,
1515
- requested: theme,
1516
- source: "programmatic",
1517
- },
1518
- })
1519
- );
1520
-
1521
- return resolvedTheme;
1522
- }
1523
-
1524
- /** Change the current theme programmatically */
1525
- PDS.setTheme = setTheme;
1526
-
1527
- /**
1528
- * Preload minimal CSS to prevent flash of unstyled content.
1529
- * Call this BEFORE any DOM content is rendered for best results.
1530
- * This is a lightweight alternative to full PDS.start({ mode: 'live' }) initialization.
1531
- *
1532
- * @param {object} config - Minimal PDS config (colors at minimum)
1533
- * @param {object} [options] - Optional settings
1534
- * @param {string} [options.theme] - Theme to generate for ('light', 'dark', or 'system')
1535
- * @param {string[]} [options.layers=['tokens']] - Which CSS layers to preload
1536
- * @returns {void}
1537
- *
1538
- * @example
1539
- * ```html
1540
- * <script type="module">
1541
- * import { PDS } from 'pure-ds/pds-core';
1542
- * // Call immediately to prevent flash
1543
- * PDS.preloadCritical({ colors: { primary: '#007acc' } });
1544
- * </script>
1545
- * ```
1546
- */
1547
- function preloadCritical(config, options = {}) {
1548
- if (typeof window === "undefined" || !document.head || !config) {
1549
- return;
1550
- }
1551
-
1552
- const { theme, layers = ["tokens"] } = options;
1553
-
1554
- try {
1555
- // Resolve theme quickly
1556
- let resolvedTheme = theme || "light";
1557
- if (theme === "system" || !theme) {
1558
- const prefersDark =
1559
- window.matchMedia &&
1560
- window.matchMedia("(prefers-color-scheme: dark)").matches;
1561
- resolvedTheme = prefersDark ? "dark" : "light";
1562
- }
1563
-
1564
- // Set theme attribute immediately
1565
- document.documentElement.setAttribute("data-theme", resolvedTheme);
1566
-
1567
- // Generate minimal CSS synchronously
1568
- // Normalize config to ensure design property exists
1569
- const tempConfig = config.design
1570
- ? { ...config, theme: resolvedTheme }
1571
- : { design: config, theme: resolvedTheme };
1572
- const tempGenerator = new PDS.Generator(tempConfig);
1573
-
1574
- const criticalCSS = layers
1575
- .map((layer) => {
1576
- try {
1577
- return tempGenerator.css?.[layer] || "";
1578
- } catch (e) {
1579
- return "";
1580
- }
1581
- })
1582
- .filter((css) => css.trim())
1583
- .join("\n");
1584
-
1585
- if (criticalCSS) {
1586
- // Remove any existing critical styles
1587
- const existing = document.head.querySelector("style[data-pds-preload]");
1588
- if (existing) existing.remove();
1589
-
1590
- // Inject immediately
1591
- const styleEl = document.createElement("style");
1592
- styleEl.setAttribute("data-pds-preload", "");
1593
- styleEl.textContent = criticalCSS;
1594
-
1595
- // Insert as early as possible
1596
- document.head.insertBefore(styleEl, document.head.firstChild);
1597
- }
1598
- } catch (error) {
1599
- // Fail silently - better than blocking page load
1600
- // No config available in this context, using console
1601
- console.warn("PDS preload failed:", error);
1602
- }
1603
- }
1604
-
1605
- /** Preload minimal CSS to prevent flash of unstyled content */
1606
- PDS.preloadCritical = preloadCritical;
1607
468
 
1608
469
  // Note: PDS object is not frozen to allow runtime properties like currentConfig
1609
470
  // to be set during initialization. The config object itself is frozen for immutability.
1610
471
 
1611
- export { PDS, validateDesign };
472
+ export { PDS };