@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.
- package/dist/types/pds.d.ts +34 -11
- package/dist/types/public/assets/auto-definer-XWHRBQPU.d.ts +9 -0
- package/dist/types/public/assets/auto-definer-XWHRBQPU.d.ts.map +1 -0
- package/dist/types/public/assets/chunk-746HIXIK.d.ts +52 -0
- package/dist/types/public/assets/chunk-746HIXIK.d.ts.map +1 -0
- package/dist/types/public/assets/chunk-APJV5T3J.d.ts +106 -0
- package/dist/types/public/assets/chunk-APJV5T3J.d.ts.map +1 -0
- package/dist/types/public/assets/chunk-BEPKFFM7.d.ts +398 -0
- package/dist/types/public/assets/chunk-BEPKFFM7.d.ts.map +1 -0
- package/dist/types/public/assets/chunk-ISS7UH5H.d.ts +2424 -0
- package/dist/types/public/assets/chunk-ISS7UH5H.d.ts.map +1 -0
- package/dist/types/public/assets/chunk-RUPLQUDG.d.ts +582 -0
- package/dist/types/public/assets/chunk-RUPLQUDG.d.ts.map +1 -0
- package/dist/types/public/assets/chunk-USML4NYF.d.ts +18 -0
- package/dist/types/public/assets/chunk-USML4NYF.d.ts.map +1 -0
- package/dist/types/public/assets/chunk-Z47A3HLT.d.ts +3 -0
- package/dist/types/public/assets/chunk-Z47A3HLT.d.ts.map +1 -0
- package/dist/types/public/assets/js/auto-definer-HZLD2XF4.d.ts +9 -0
- package/dist/types/public/assets/js/auto-definer-HZLD2XF4.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunk-6A6DFAIG.d.ts +88 -0
- package/dist/types/public/assets/js/chunk-6A6DFAIG.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunk-746HIXIK.d.ts +52 -0
- package/dist/types/public/assets/js/chunk-746HIXIK.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunk-A3TZGIYX.d.ts +4 -0
- package/dist/types/public/assets/js/chunk-A3TZGIYX.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunk-BEPKFFM7.d.ts +398 -0
- package/dist/types/public/assets/js/chunk-BEPKFFM7.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunk-OTTRJ5MB.d.ts +1695 -0
- package/dist/types/public/assets/js/chunk-OTTRJ5MB.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunk-RBPKHG76.d.ts +747 -0
- package/dist/types/public/assets/js/chunk-RBPKHG76.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunk-RUPLQUDG.d.ts +582 -0
- package/dist/types/public/assets/js/chunk-RUPLQUDG.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunk-SMD2R3CX.d.ts +68 -0
- package/dist/types/public/assets/js/chunk-SMD2R3CX.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunk-Y73DA2D5.d.ts +15 -0
- package/dist/types/public/assets/js/chunk-Y73DA2D5.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunks/auto-definer-X7MSXKTU.d.ts +9 -0
- package/dist/types/public/assets/js/chunks/auto-definer-X7MSXKTU.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunks/chunk-7BDQH5CT.d.ts +485 -0
- package/dist/types/public/assets/js/chunks/chunk-7BDQH5CT.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunks/chunk-MWB3S7NG.d.ts +3 -0
- package/dist/types/public/assets/js/chunks/chunk-MWB3S7NG.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunks/chunk-WIMLORAU.d.ts +5 -0
- package/dist/types/public/assets/js/chunks/chunk-WIMLORAU.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunks/chunk-WN4Y2ELN.d.ts +833 -0
- package/dist/types/public/assets/js/chunks/chunk-WN4Y2ELN.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunks/chunk-XQOUIBLO.d.ts +1687 -0
- package/dist/types/public/assets/js/chunks/chunk-XQOUIBLO.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunks/font-loader-VN5SRNOD.d.ts +5 -0
- package/dist/types/public/assets/js/chunks/font-loader-VN5SRNOD.d.ts.map +1 -0
- package/dist/types/public/assets/js/chunks/pds-live-validation-BQPWN5JG.d.ts +38 -0
- package/dist/types/public/assets/js/chunks/pds-live-validation-BQPWN5JG.d.ts.map +1 -0
- package/dist/types/public/assets/js/common-WIAC4WAJ.d.ts +4 -0
- package/dist/types/public/assets/js/common-WIAC4WAJ.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds-config-WEBAXXSM.d.ts +4 -0
- package/dist/types/public/assets/js/pds-config-WEBAXXSM.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds-core/pds-generator.d.ts +700 -0
- package/dist/types/public/assets/js/pds-core/pds-generator.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds-core/pds-utilities.d.ts +27 -0
- package/dist/types/public/assets/js/pds-core/pds-utilities.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds-enums-DCBZHS64.d.ts +3 -0
- package/dist/types/public/assets/js/pds-enums-DCBZHS64.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds-gen.d.ts +106 -0
- package/dist/types/public/assets/js/pds-gen.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds-live.d.ts +11 -0
- package/dist/types/public/assets/js/pds-live.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds-manager.d.ts +1047 -0
- package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds-ontology-2DICJXHO.d.ts +9 -0
- package/dist/types/public/assets/js/pds-ontology-2DICJXHO.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds-query-B54LBKKR.d.ts +70 -0
- package/dist/types/public/assets/js/pds-query-B54LBKKR.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds.d.ts +1 -18
- package/dist/types/public/assets/js/pds.d.ts.map +1 -1
- package/dist/types/public/assets/pds-ontology-ZO6TJHO3.d.ts +9 -0
- package/dist/types/public/assets/pds-ontology-ZO6TJHO3.d.ts.map +1 -0
- package/dist/types/src/js/common/pds-core/pds-config.d.ts +757 -0
- package/dist/types/src/js/common/pds-core/pds-config.d.ts.map +1 -0
- package/dist/types/src/js/common/pds-core/pds-enhancers.d.ts +28 -0
- package/dist/types/src/js/common/pds-core/pds-enhancers.d.ts.map +1 -0
- package/dist/types/src/js/common/pds-core/pds-enums.d.ts +87 -0
- package/dist/types/src/js/common/pds-core/pds-enums.d.ts.map +1 -0
- package/dist/types/src/js/common/pds-core/pds-generator.d.ts +700 -0
- package/dist/types/src/js/common/pds-core/pds-generator.d.ts.map +1 -0
- package/dist/types/src/js/common/pds-core/pds-ontology.d.ts +380 -0
- package/dist/types/src/js/common/pds-core/pds-ontology.d.ts.map +1 -0
- package/dist/types/src/js/common/pds-core/pds-paths.d.ts +37 -0
- package/dist/types/src/js/common/pds-core/pds-paths.d.ts.map +1 -0
- package/dist/types/src/js/common/pds-core/pds-query.d.ts +102 -0
- package/dist/types/src/js/common/pds-core/pds-query.d.ts.map +1 -0
- package/dist/types/src/js/common/pds-core/pds-registry.d.ts +35 -0
- package/dist/types/src/js/common/pds-core/pds-registry.d.ts.map +1 -0
- package/dist/types/src/js/common/pds-core/pds-utilities.d.ts +27 -0
- package/dist/types/src/js/common/pds-core/pds-utilities.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-generator.d.ts +38 -46
- package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-live.d.ts +39 -0
- package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-runtime.d.ts +39 -0
- package/dist/types/src/js/pds-core/pds-runtime.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-start-helpers.d.ts +60 -0
- package/dist/types/src/js/pds-core/pds-start-helpers.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-utilities.d.ts +27 -0
- package/dist/types/src/js/pds-core/pds-utilities.d.ts.map +1 -0
- package/dist/types/src/js/pds-gen.d.ts +48 -0
- package/dist/types/src/js/pds-gen.d.ts.map +1 -0
- package/dist/types/src/js/pds-live-runtime.d.ts +7 -0
- package/dist/types/src/js/pds-live-runtime.d.ts.map +1 -0
- package/dist/types/src/js/pds-live-validation.d.ts +44 -0
- package/dist/types/src/js/pds-live-validation.d.ts.map +1 -0
- package/dist/types/src/js/pds-live.d.ts +11 -0
- package/dist/types/src/js/pds-live.d.ts.map +1 -0
- package/dist/types/src/js/pds-manager.d.ts +2 -0
- package/dist/types/src/js/pds-manager.d.ts.map +1 -0
- package/dist/types/src/js/pds.d.ts +6 -33
- package/dist/types/src/js/pds.d.ts.map +1 -1
- package/package.json +11 -12
- package/packages/pds-cli/bin/{generate-css-data.mjs → generate-css-data.js} +563 -563
- package/packages/pds-cli/bin/{generate-manifest.mjs → generate-manifest.js} +352 -352
- package/packages/pds-cli/bin/{pds-build-icons.mjs → pds-build-icons.js} +152 -152
- package/packages/pds-cli/bin/{pds-dx.mjs → pds-dx.js} +114 -114
- package/packages/pds-cli/bin/{pds-init-config.mjs → pds-init-config.js} +34 -34
- package/packages/pds-cli/bin/{pds-setup-copilot.mjs → pds-setup-copilot.js} +106 -106
- package/packages/pds-cli/bin/{pds-static.mjs → pds-static.js} +597 -581
- package/packages/pds-cli/bin/{pds.mjs → pds.js} +127 -127
- package/packages/pds-cli/bin/postinstall.mjs +522 -563
- package/packages/pds-cli/bin/{sync-assets.mjs → sync-assets.js} +251 -251
- package/packages/pds-cli/lib/{asset-roots.mjs → asset-roots.js} +47 -47
- package/packages/pds-cli/lib/{fs-writer.mjs → fs-writer.js} +75 -75
- package/public/assets/js/app.js +95 -118
- package/public/assets/js/pds-manager.js +3251 -0
- package/public/assets/js/pds.js +10 -3201
- package/readme.md +2014 -2016
- package/src/js/pds-core/pds-enhancers.js +518 -518
- package/src/js/pds-core/pds-enums.js +86 -86
- package/src/js/pds-core/pds-generator.js +255 -185
- package/src/js/pds-core/pds-live.js +434 -0
- package/src/js/pds-core/pds-paths.js +109 -109
- package/src/js/pds-core/pds-registry.js +79 -79
- package/src/js/pds-core/pds-runtime.js +184 -0
- package/src/js/pds-core/pds-start-helpers.js +405 -0
- package/src/js/pds.d.ts +34 -11
- package/src/js/pds.js +43 -1182
- package/getting-started.md +0 -626
- 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 `
|
|
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 {
|
|
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 {(
|
|
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-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
1345
|
-
|
|
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 } =
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
472
|
+
export { PDS };
|