@pure-ds/core 0.6.9 → 0.6.11

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 (90) hide show
  1. package/custom-elements.json +865 -35
  2. package/dist/types/pds.d.ts +31 -0
  3. package/dist/types/public/assets/js/pds-manager.d.ts +100 -2
  4. package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -1
  5. package/dist/types/public/assets/js/pds.d.ts.map +1 -1
  6. package/dist/types/public/assets/pds/components/pds-form.d.ts.map +1 -1
  7. package/dist/types/public/assets/pds/components/pds-live-converter.d.ts +8 -0
  8. package/dist/types/public/assets/pds/components/pds-live-converter.d.ts.map +1 -0
  9. package/dist/types/public/assets/pds/components/pds-live-edit.d.ts +1 -195
  10. package/dist/types/public/assets/pds/components/pds-live-edit.d.ts.map +1 -1
  11. package/dist/types/public/assets/pds/components/pds-live-importer.d.ts +2 -0
  12. package/dist/types/public/assets/pds/components/pds-live-importer.d.ts.map +1 -0
  13. package/dist/types/public/assets/pds/components/pds-live-template-canvas.d.ts +2 -0
  14. package/dist/types/public/assets/pds/components/pds-live-template-canvas.d.ts.map +1 -0
  15. package/dist/types/public/assets/pds/components/pds-omnibox.d.ts +0 -2
  16. package/dist/types/public/assets/pds/components/pds-omnibox.d.ts.map +1 -1
  17. package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts +20 -0
  18. package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts.map +1 -1
  19. package/dist/types/public/assets/pds/components/pds-toaster.d.ts +1 -1
  20. package/dist/types/public/assets/pds/components/pds-toaster.d.ts.map +1 -1
  21. package/dist/types/public/assets/pds/components/pds-treeview.d.ts +37 -0
  22. package/dist/types/public/assets/pds/components/pds-treeview.d.ts.map +1 -0
  23. package/dist/types/src/js/common/toast.d.ts +8 -0
  24. package/dist/types/src/js/common/toast.d.ts.map +1 -1
  25. package/dist/types/src/js/pds-core/pds-config.d.ts +1306 -13
  26. package/dist/types/src/js/pds-core/pds-config.d.ts.map +1 -1
  27. package/dist/types/src/js/pds-core/pds-enhancers-meta.d.ts.map +1 -1
  28. package/dist/types/src/js/pds-core/pds-enhancers.d.ts.map +1 -1
  29. package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
  30. package/dist/types/src/js/pds-core/pds-live.d.ts +2 -1
  31. package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -1
  32. package/dist/types/src/js/pds-core/pds-ontology.d.ts.map +1 -1
  33. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts +1 -4
  34. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts.map +1 -1
  35. package/dist/types/src/js/pds-live-manager/conversion-service.d.ts +66 -0
  36. package/dist/types/src/js/pds-live-manager/conversion-service.d.ts.map +1 -0
  37. package/dist/types/src/js/pds-live-manager/import-contract.d.ts +15 -0
  38. package/dist/types/src/js/pds-live-manager/import-contract.d.ts.map +1 -0
  39. package/dist/types/src/js/pds-live-manager/import-history-service.d.ts +32 -0
  40. package/dist/types/src/js/pds-live-manager/import-history-service.d.ts.map +1 -0
  41. package/dist/types/src/js/pds-live-manager/import-service.d.ts +21 -0
  42. package/dist/types/src/js/pds-live-manager/import-service.d.ts.map +1 -0
  43. package/dist/types/src/js/pds-live-manager/template-service.d.ts +17 -0
  44. package/dist/types/src/js/pds-live-manager/template-service.d.ts.map +1 -0
  45. package/dist/types/src/js/pds-manager.d.ts +4 -0
  46. package/dist/types/src/js/pds.d.ts.map +1 -1
  47. package/package.json +7 -3
  48. package/packages/pds-cli/README.md +51 -0
  49. package/packages/pds-cli/bin/pds-import.js +176 -0
  50. package/packages/pds-cli/bin/pds-static.js +31 -1
  51. package/packages/pds-cli/bin/postinstall.mjs +17 -8
  52. package/public/assets/js/app.js +23 -147
  53. package/public/assets/js/pds-manager.js +481 -248
  54. package/public/assets/js/pds.js +16 -16
  55. package/public/assets/pds/components/pds-form.js +124 -27
  56. package/public/assets/pds/components/pds-live-converter.js +47 -0
  57. package/public/assets/pds/components/pds-live-edit.js +1626 -211
  58. package/public/assets/pds/components/pds-live-importer.js +772 -0
  59. package/public/assets/pds/components/pds-live-template-canvas.js +171 -0
  60. package/public/assets/pds/components/pds-omnibox.js +146 -20
  61. package/public/assets/pds/components/pds-scrollrow.js +56 -1
  62. package/public/assets/pds/components/pds-toaster.js +50 -5
  63. package/public/assets/pds/components/pds-treeview.js +972 -0
  64. package/public/assets/pds/custom-elements.json +865 -35
  65. package/public/assets/pds/pds-css-complete.json +7 -7
  66. package/public/assets/pds/pds.css-data.json +5 -35
  67. package/public/assets/pds/templates/commerce-scroll-explorer.html +115 -0
  68. package/public/assets/pds/templates/content-brand-showcase.html +110 -0
  69. package/public/assets/pds/templates/feedback-ops-dashboard.html +91 -0
  70. package/public/assets/pds/templates/release-readiness-radar.html +69 -0
  71. package/public/assets/pds/templates/support-command-center.html +92 -0
  72. package/public/assets/pds/templates/templates.json +53 -0
  73. package/public/assets/pds/templates/workspace-settings-lab.html +131 -0
  74. package/public/assets/pds/vscode-custom-data.json +54 -4
  75. package/readme.md +34 -0
  76. package/src/js/pds-core/pds-config.js +831 -40
  77. package/src/js/pds-core/pds-enhancers-meta.js +11 -0
  78. package/src/js/pds-core/pds-enhancers.js +259 -5
  79. package/src/js/pds-core/pds-generator.js +353 -52
  80. package/src/js/pds-core/pds-live.js +630 -15
  81. package/src/js/pds-core/pds-ontology.js +6 -0
  82. package/src/js/pds-core/pds-start-helpers.js +14 -6
  83. package/src/js/pds-live-manager/conversion-service.js +3136 -0
  84. package/src/js/pds-live-manager/import-contract.js +57 -0
  85. package/src/js/pds-live-manager/import-history-service.js +145 -0
  86. package/src/js/pds-live-manager/import-service.js +255 -0
  87. package/src/js/pds-live-manager/tailwind-conversion-rules.json +383 -0
  88. package/src/js/pds-live-manager/template-service.js +170 -0
  89. package/src/js/pds.d.ts +31 -0
  90. package/src/js/pds.js +71 -60
@@ -4,7 +4,18 @@
4
4
  */
5
5
  import { Generator } from "./pds-generator.js";
6
6
  import { applyStyles, adoptLayers, adoptPrimitives } from "./pds-runtime.js";
7
- import { presets, defaultLog, PDS_CONFIG_RELATIONS } from "./pds-config.js";
7
+ import {
8
+ presets,
9
+ defaultLog,
10
+ PDS_CONFIG_RELATIONS,
11
+ PDS_DESIGN_CONFIG_SPEC,
12
+ PDS_DEFAULT_CONFIG_EDITOR_METADATA,
13
+ PDS_DEFAULT_CONFIG_FORM_SCHEMA,
14
+ buildDesignConfigFormSchema,
15
+ getDesignConfigEditorMetadata,
16
+ validateDesignConfig,
17
+ validateInitConfig,
18
+ } from "./pds-config.js";
8
19
  import { defaultPDSEnhancers } from "./pds-enhancers.js";
9
20
  import { defaultPDSEnhancerMetadata } from "./pds-enhancers-meta.js";
10
21
  import { resolvePublicAssetURL } from "./pds-paths.js";
@@ -21,12 +32,439 @@ import {
21
32
  } from "./pds-start-helpers.js";
22
33
  import {
23
34
  isPresetThemeCompatible,
35
+ normalizePresetThemes,
24
36
  resolveThemePreference,
25
37
  } from "./pds-theme-utils.js";
26
38
 
27
39
  let __liveApiReady = false;
28
40
  let __queryClass = null;
29
41
 
42
+ const LIVE_EDIT_TOGGLE_ID = "pds-live-edit-toggle";
43
+ const LIVE_EDIT_TOGGLE_STYLE_ID = "pds-live-edit-toggle-style";
44
+
45
+ function whenDocumentBodyReady(callback) {
46
+ if (typeof document === "undefined" || typeof callback !== "function") return;
47
+ if (document.body) {
48
+ callback();
49
+ return;
50
+ }
51
+
52
+ const onReady = () => {
53
+ if (!document.body) return;
54
+ document.removeEventListener("DOMContentLoaded", onReady);
55
+ callback();
56
+ };
57
+
58
+ document.addEventListener("DOMContentLoaded", onReady, { once: true });
59
+ }
60
+
61
+ function mountLiveEdit(options = {}) {
62
+ const interactive = options?.interactive !== false;
63
+ if (typeof document === "undefined") return;
64
+ whenDocumentBodyReady(() => {
65
+ if (!document.querySelector("pds-live-edit")) {
66
+ const liveEditor = document.createElement("pds-live-edit");
67
+ if (!interactive) {
68
+ liveEditor.setAttribute("data-pds-live-settings-only", "true");
69
+ }
70
+ document.body.appendChild(liveEditor);
71
+ } else if (!interactive) {
72
+ const existing = document.querySelector("pds-live-edit");
73
+ if (existing) {
74
+ existing.setAttribute("data-pds-live-settings-only", "true");
75
+ }
76
+ }
77
+ });
78
+ }
79
+
80
+ function waitFor(ms = 0) {
81
+ return new Promise((resolve) => {
82
+ setTimeout(resolve, Math.max(0, Number(ms) || 0));
83
+ });
84
+ }
85
+
86
+ async function ensureLiveEditInstance(options = {}) {
87
+ const mountIfMissing = options?.mountIfMissing !== false;
88
+ const interactive = options?.interactive !== false;
89
+ const requiredMethod =
90
+ typeof options?.requiredMethod === "string" && options.requiredMethod.trim()
91
+ ? options.requiredMethod.trim()
92
+ : "openDesignSettings";
93
+ const timeoutMs = Number.isFinite(Number(options?.timeoutMs))
94
+ ? Number(options.timeoutMs)
95
+ : 2400;
96
+
97
+ if (typeof document === "undefined") return null;
98
+ if (!mountIfMissing && !document.querySelector("pds-live-edit")) {
99
+ return null;
100
+ }
101
+ if (mountIfMissing) {
102
+ mountLiveEdit({ interactive });
103
+ }
104
+
105
+ const startedAt = Date.now();
106
+ while (Date.now() - startedAt < timeoutMs) {
107
+ const liveEditor = document.querySelector("pds-live-edit");
108
+ if (!liveEditor) {
109
+ await waitFor(40);
110
+ continue;
111
+ }
112
+
113
+ if (typeof liveEditor?.[requiredMethod] === "function") {
114
+ return liveEditor;
115
+ }
116
+
117
+ if (
118
+ typeof customElements !== "undefined" &&
119
+ typeof customElements.whenDefined === "function"
120
+ ) {
121
+ try {
122
+ await Promise.race([customElements.whenDefined("pds-live-edit"), waitFor(80)]);
123
+ } catch (error) {
124
+ await waitFor(40);
125
+ }
126
+ continue;
127
+ }
128
+
129
+ await waitFor(40);
130
+ }
131
+
132
+ const fallback = document.querySelector("pds-live-edit");
133
+ if (fallback && typeof fallback?.[requiredMethod] === "function") {
134
+ return fallback;
135
+ }
136
+ return null;
137
+ }
138
+
139
+ function unmountLiveEdit() {
140
+ if (typeof document === "undefined") return;
141
+ const editors = document.querySelectorAll("pds-live-edit");
142
+ editors.forEach((editor) => {
143
+ if (typeof editor?.setInteractiveEditingEnabled === "function") {
144
+ editor.setInteractiveEditingEnabled(false);
145
+ }
146
+ editor.remove();
147
+ });
148
+ }
149
+
150
+ function getLiveEditInteractiveState(liveEditor) {
151
+ if (!liveEditor) return false;
152
+ if (typeof liveEditor.isInteractiveEditingEnabled === "function") {
153
+ return Boolean(liveEditor.isInteractiveEditingEnabled());
154
+ }
155
+ return true;
156
+ }
157
+
158
+ function ensureLiveEditToggleStyles() {
159
+ if (typeof document === "undefined") return;
160
+ if (document.getElementById(LIVE_EDIT_TOGGLE_STYLE_ID)) return;
161
+
162
+ const style = document.createElement("style");
163
+ style.id = LIVE_EDIT_TOGGLE_STYLE_ID;
164
+ style.textContent = /*css*/`
165
+ :where(.pds-live-edit-toggle-nav) {
166
+ position: fixed;
167
+ top: var(--spacing-3);
168
+ right: var(--spacing-3);
169
+ z-index: var(--z-dropdown, 1050);
170
+ }
171
+
172
+ :where(.pds-live-edit-toggle-nav menu) {
173
+ min-width: 220px;
174
+ }
175
+
176
+ :where(.pds-live-edit-toggle-nav menu a[data-pds-live-action]) {
177
+ cursor: pointer;
178
+ }
179
+
180
+ :where(.pds-live-edit-toggle-nav menu li.pds-live-shared-quick-mode-item) {
181
+ list-style: none;
182
+ margin: 0;
183
+ padding: 0;
184
+ }
185
+
186
+ :where(.pds-live-edit-toggle-nav menu li.pds-live-shared-quick-mode-item .pds-live-editor-menu) {
187
+ display: grid;
188
+ gap: var(--spacing-2);
189
+ padding: var(--spacing-2);
190
+ border-bottom: var(--border-width-thin) solid var(--color-border);
191
+ }
192
+
193
+ :where(.pds-live-edit-toggle-nav menu li.pds-live-shared-quick-mode-item label) {
194
+ display: grid;
195
+ gap: var(--spacing-1);
196
+ margin: 0;
197
+ }
198
+
199
+ :where(.pds-live-edit-toggle-nav menu li.pds-live-shared-quick-mode-item label > span) {
200
+ font-size: var(--font-size-xs);
201
+ color: var(--color-text-muted);
202
+ }
203
+
204
+ :where(.pds-live-edit-toggle-nav menu li > hr) {
205
+ border: 0;
206
+ border-top: var(--border-width-thin) solid var(--color-border);
207
+ margin: var(--spacing-2) 0 var(--spacing-1) 0;
208
+ }
209
+
210
+ :where(.pds-live-edit-toggle-nav menu li:has(> hr)) {
211
+ padding: 0;
212
+ margin: 0;
213
+ list-style: none;
214
+ pointer-events: none;
215
+ }
216
+
217
+ :where(.pds-live-edit-toggle-nav menu a[data-pds-live-action="reset-config"]) {
218
+ color: var(--color-danger-700);
219
+ }
220
+
221
+ :where(.pds-live-edit-toggle-nav menu a[data-pds-live-action="reset-config"] pds-icon) {
222
+ color: var(--color-danger-700);
223
+ }
224
+ `;
225
+ document.head.appendChild(style);
226
+ }
227
+
228
+ function updateLiveEditToggleState(button, isEnabled) {
229
+ if (!button) return;
230
+
231
+ button.classList.toggle("btn-primary", isEnabled);
232
+ button.classList.toggle("btn-secondary", !isEnabled);
233
+ button.setAttribute("aria-pressed", isEnabled ? "true" : "false");
234
+
235
+ const label = "PDS Manager";
236
+ button.setAttribute("aria-label", label);
237
+ button.setAttribute("title", label);
238
+ }
239
+
240
+ async function ensureLiveEditToggleButton() {
241
+ if (typeof document === "undefined") return null;
242
+
243
+ ensureLiveEditToggleStyles();
244
+
245
+ let button = document.getElementById(LIVE_EDIT_TOGGLE_ID);
246
+ if (!button) {
247
+ const nav = document.createElement("nav");
248
+ nav.className = "pds-live-edit-toggle-nav";
249
+ nav.setAttribute("data-dropdown", "");
250
+ nav.setAttribute("data-mode", "auto");
251
+ nav.setAttribute("data-pds-live-edit-ignore", "true");
252
+
253
+ button = document.createElement("button");
254
+ button.id = LIVE_EDIT_TOGGLE_ID;
255
+ button.type = "button";
256
+ button.className = "icon-only btn-secondary";
257
+ button.setAttribute("data-pds-live-edit-ignore", "true");
258
+ button.innerHTML = '<pds-icon icon="cursor-click" size="sm"></pds-icon>';
259
+
260
+ const menu = document.createElement("menu");
261
+ menu.setAttribute("data-pds-live-edit-ignore", "true");
262
+
263
+ const createItem = (action, label, icon) => {
264
+ const li = document.createElement("li");
265
+ const link = document.createElement("a");
266
+ link.href = "#";
267
+ link.dataset.pdsLiveAction = action;
268
+ link.setAttribute("data-pds-live-edit-ignore", "true");
269
+
270
+ const iconEl = document.createElement("pds-icon");
271
+ iconEl.setAttribute("icon", icon);
272
+ iconEl.setAttribute("size", "sm");
273
+
274
+ link.append(iconEl, document.createTextNode(` ${label}`));
275
+ li.appendChild(link);
276
+ return li;
277
+ };
278
+
279
+ const createSeparator = () => {
280
+ const li = document.createElement("li");
281
+ li.setAttribute("data-pds-live-edit-ignore", "true");
282
+ const hr = document.createElement("hr");
283
+ hr.setAttribute("aria-hidden", "true");
284
+ li.appendChild(hr);
285
+ return li;
286
+ };
287
+
288
+ menu.appendChild(createItem("toggle", "Toggle live editing", "pencil"));
289
+ menu.appendChild(createItem("open-settings", "Open Settings", "gear"));
290
+ menu.appendChild(createSeparator());
291
+ menu.appendChild(createItem("reset-config", "Reset Config", "arrow-counter-clockwise"));
292
+
293
+ await ensureSharedQuickModeToggleMenuItem(menu);
294
+
295
+ nav.append(button, menu);
296
+
297
+ whenDocumentBodyReady(() => {
298
+ if (!document.getElementById(LIVE_EDIT_TOGGLE_ID)) {
299
+ document.body.appendChild(nav);
300
+ }
301
+ });
302
+ }
303
+
304
+ return button;
305
+ }
306
+
307
+ async function ensureSharedQuickModeToggleMenuItem(menu) {
308
+ if (!(menu instanceof Element)) return;
309
+ if (menu.__pdsLiveSharedMenuItemInFlight) {
310
+ return menu.__pdsLiveSharedMenuItemInFlight;
311
+ }
312
+
313
+ menu.__pdsLiveSharedMenuItemInFlight = (async () => {
314
+ menu
315
+ .querySelectorAll("li.pds-live-shared-quick-mode-item")
316
+ .forEach((node) => node.remove());
317
+
318
+ const liveEditor = await ensureLiveEditInstance({
319
+ mountIfMissing: true,
320
+ interactive: false,
321
+ requiredMethod: "createSharedQuickModeMenuItem",
322
+ timeoutMs: 7000,
323
+ });
324
+
325
+ if (!liveEditor || typeof liveEditor.createSharedQuickModeMenuItem !== "function") {
326
+ return;
327
+ }
328
+
329
+ const sharedItem = await liveEditor.createSharedQuickModeMenuItem();
330
+ if (!(sharedItem instanceof Element)) return;
331
+
332
+ sharedItem.classList.add("pds-live-shared-quick-mode-item");
333
+
334
+ const resetLink = menu.querySelector('a[data-pds-live-action="reset-config"]');
335
+ const resetItem = resetLink?.closest("li") || null;
336
+ const previousItem = resetItem?.previousElementSibling || null;
337
+ const separatorBeforeReset =
338
+ previousItem && previousItem.querySelector?.(":scope > hr") ? previousItem : null;
339
+
340
+ if (separatorBeforeReset) {
341
+ menu.insertBefore(sharedItem, separatorBeforeReset);
342
+ return;
343
+ }
344
+
345
+ if (resetItem) {
346
+ menu.insertBefore(sharedItem, resetItem);
347
+ return;
348
+ }
349
+
350
+ menu.appendChild(sharedItem);
351
+ })();
352
+
353
+ try {
354
+ await menu.__pdsLiveSharedMenuItemInFlight;
355
+ } finally {
356
+ menu.__pdsLiveSharedMenuItemInFlight = null;
357
+ }
358
+ }
359
+
360
+ function teardownLiveEditToggle() {
361
+ if (typeof document === "undefined") return;
362
+ const button = document.getElementById(LIVE_EDIT_TOGGLE_ID);
363
+ if (button) {
364
+ const nav = button.closest(".pds-live-edit-toggle-nav");
365
+ if (nav) {
366
+ nav.remove();
367
+ } else {
368
+ button.remove();
369
+ }
370
+ }
371
+ const style = document.getElementById(LIVE_EDIT_TOGGLE_STYLE_ID);
372
+ if (style) style.remove();
373
+ unmountLiveEdit();
374
+ }
375
+
376
+ async function initializeLiveEditToggle() {
377
+ if (typeof document === "undefined") return;
378
+ const toggleButton = await ensureLiveEditToggleButton();
379
+ if (!toggleButton) return;
380
+
381
+ const setLiveEditEnabled = async (enabled) => {
382
+ if (enabled) {
383
+ const liveEditor = await ensureLiveEditInstance({ mountIfMissing: true });
384
+ if (liveEditor && typeof liveEditor.setInteractiveEditingEnabled === "function") {
385
+ liveEditor.setInteractiveEditingEnabled(true);
386
+ }
387
+ } else {
388
+ unmountLiveEdit();
389
+ }
390
+ updateLiveEditToggleState(toggleButton, enabled);
391
+ };
392
+
393
+ void setLiveEditEnabled(false);
394
+
395
+ const actionHost = toggleButton.closest(".pds-live-edit-toggle-nav") || toggleButton;
396
+ if (toggleButton.__pdsLiveEditActionHandler) {
397
+ actionHost.removeEventListener("click", toggleButton.__pdsLiveEditActionHandler);
398
+ }
399
+
400
+ const actionHandler = async (event) => {
401
+ const actionElement = event.target?.closest?.("[data-pds-live-action]");
402
+ if (!actionElement) return;
403
+
404
+ event.preventDefault();
405
+ const action = String(actionElement.dataset.pdsLiveAction || "");
406
+
407
+ if (action === "toggle") {
408
+ const liveEditor = await ensureLiveEditInstance({ mountIfMissing: false });
409
+ const isEnabled = getLiveEditInteractiveState(liveEditor);
410
+ await setLiveEditEnabled(!isEnabled);
411
+ return;
412
+ }
413
+
414
+ if (action === "open-settings") {
415
+ const liveEditor = await ensureLiveEditInstance({
416
+ mountIfMissing: true,
417
+ requiredMethod: "openDesignSettings",
418
+ interactive: false,
419
+ });
420
+ if (liveEditor && typeof liveEditor.setInteractiveEditingEnabled === "function") {
421
+ liveEditor.setInteractiveEditingEnabled(false);
422
+ }
423
+ if (liveEditor && typeof liveEditor.openDesignSettings === "function") {
424
+ updateLiveEditToggleState(toggleButton, false);
425
+ await liveEditor.openDesignSettings();
426
+ }
427
+ return;
428
+ }
429
+
430
+ if (action === "reset-config") {
431
+ const liveEditor = await ensureLiveEditInstance({
432
+ mountIfMissing: true,
433
+ requiredMethod: "resetConfig",
434
+ interactive: false,
435
+ });
436
+ updateLiveEditToggleState(toggleButton, false);
437
+ if (liveEditor && typeof liveEditor.resetConfig === "function") {
438
+ await liveEditor.resetConfig();
439
+ }
440
+ return;
441
+ }
442
+ };
443
+
444
+ toggleButton.__pdsLiveEditActionHandler = actionHandler;
445
+ actionHost.addEventListener("click", actionHandler);
446
+
447
+ if (toggleButton.__pdsLiveEditDisableHandler) {
448
+ document.removeEventListener("pds:live-edit:disable", toggleButton.__pdsLiveEditDisableHandler);
449
+ }
450
+ if (toggleButton.__pdsLiveEditEnableHandler) {
451
+ document.removeEventListener("pds:live-edit:enable", toggleButton.__pdsLiveEditEnableHandler);
452
+ }
453
+
454
+ const disableHandler = () => {
455
+ void setLiveEditEnabled(false);
456
+ };
457
+
458
+ const enableHandler = () => {
459
+ void setLiveEditEnabled(true);
460
+ };
461
+
462
+ toggleButton.__pdsLiveEditDisableHandler = disableHandler;
463
+ document.addEventListener("pds:live-edit:disable", disableHandler);
464
+ toggleButton.__pdsLiveEditEnableHandler = enableHandler;
465
+ document.addEventListener("pds:live-edit:enable", enableHandler);
466
+ }
467
+
30
468
  function getStoredLiveConfig() {
31
469
  if (typeof window === "undefined" || !window.localStorage) return null;
32
470
  try {
@@ -90,7 +528,143 @@ function applyStoredConfigOverrides(baseConfig) {
90
528
  return nextConfig;
91
529
  }
92
530
 
93
- async function __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIfNeeded }) {
531
+ function buildPresetOmniboxSettings(PDS, options = {}) {
532
+ const {
533
+ hideCategory = true,
534
+ itemGrid = "45px 1fr",
535
+ includeIncompatible = true,
536
+ disableIncompatible = true,
537
+ categoryName = "Presets",
538
+ theme,
539
+ onSelect,
540
+ iconHandler,
541
+ } = options || {};
542
+
543
+ const resolvedTheme = resolveThemePreference(theme ?? PDS?.theme);
544
+
545
+ const defaultIconHandler = (item) => {
546
+ const preset = findPresetById(item?.id);
547
+ const colors = preset?.colors || {};
548
+ const primary = colors?.primary;
549
+ const secondary = colors?.secondary;
550
+ const accent = colors?.accent;
551
+
552
+ if (primary && secondary && accent) {
553
+ return `<span style="display:flex;gap:1px;flex-shrink:0;" aria-hidden="true">
554
+ <span style="display:inline-block;width:10px;height:20px;background-color:${primary};">&nbsp;</span>
555
+ <span style="display:inline-block;width:10px;height:20px;background-color:${secondary};">&nbsp;</span>
556
+ <span style="display:inline-block;width:10px;height:20px;background-color:${accent};">&nbsp;</span>
557
+ </span>`;
558
+ }
559
+
560
+ if (item?.icon) {
561
+ return `<pds-icon icon="${item.icon}" size="sm"></pds-icon>`;
562
+ }
563
+ return "";
564
+ };
565
+
566
+ const getPresetEntries = () => {
567
+ const source = PDS?.presets || {};
568
+ return Object.values(source || {}).filter((preset) => {
569
+ const presetId = preset?.id || preset?.name;
570
+ return Boolean(presetId);
571
+ });
572
+ };
573
+
574
+ const findPresetById = (presetId) => {
575
+ if (!presetId) return null;
576
+ const entries = getPresetEntries();
577
+ return (
578
+ entries.find((preset) => String(preset?.id || preset?.name) === String(presetId)) ||
579
+ null
580
+ );
581
+ };
582
+
583
+ return {
584
+ hideCategory,
585
+ itemGrid,
586
+ iconHandler:
587
+ typeof iconHandler === "function" ? iconHandler : defaultIconHandler,
588
+ categories: {
589
+ [categoryName]: {
590
+ trigger: () => true,
591
+ getItems: (context = {}) => {
592
+ const query = String(context?.search || "").toLowerCase().trim();
593
+ const entries = getPresetEntries();
594
+
595
+ return entries
596
+ .filter((preset) => {
597
+ const name = String(preset?.name || preset?.id || "").toLowerCase();
598
+ const description = String(preset?.description || "").toLowerCase();
599
+ const tags = Array.isArray(preset?.tags)
600
+ ? preset.tags.map((tag) => String(tag).toLowerCase())
601
+ : [];
602
+
603
+ if (query) {
604
+ const matchesQuery =
605
+ name.includes(query) ||
606
+ description.includes(query) ||
607
+ tags.some((tag) => tag.includes(query));
608
+ if (!matchesQuery) return false;
609
+ }
610
+
611
+ const compatible = isPresetThemeCompatible(preset, resolvedTheme);
612
+ if (!includeIncompatible && !compatible) return false;
613
+ return true;
614
+ })
615
+ .map((preset) => {
616
+ const presetId = preset?.id || preset?.name;
617
+ const compatible = isPresetThemeCompatible(preset, resolvedTheme);
618
+ const supportedThemes = normalizePresetThemes(preset);
619
+ const themeHint =
620
+ supportedThemes.length === 1
621
+ ? `${supportedThemes[0]} only`
622
+ : `Not available in ${resolvedTheme} mode`;
623
+ const baseDescription = String(preset?.description || "").trim();
624
+ const description = compatible
625
+ ? baseDescription
626
+ : baseDescription
627
+ ? `${baseDescription} - ${themeHint}`
628
+ : themeHint;
629
+
630
+ return {
631
+ id: presetId,
632
+ text: preset?.name || String(presetId),
633
+ description,
634
+ icon: "palette",
635
+ class:
636
+ !compatible && disableIncompatible ? "disabled" : "",
637
+ disabled: !compatible && disableIncompatible,
638
+ tooltip: !compatible ? themeHint : "",
639
+ };
640
+ })
641
+ .sort((a, b) =>
642
+ String(a.text || "").localeCompare(String(b.text || ""))
643
+ );
644
+ },
645
+ action: async (selection) => {
646
+ if (!selection?.id) return selection?.id;
647
+ if (selection?.disabled) return selection?.id;
648
+
649
+ const preset = findPresetById(selection.id);
650
+ if (!preset) return selection?.id;
651
+
652
+ if (typeof onSelect === "function") {
653
+ return await onSelect({ preset, selection, resolvedTheme });
654
+ }
655
+
656
+ if (typeof PDS?.applyLivePreset === "function") {
657
+ await PDS.applyLivePreset(preset.id || selection.id);
658
+ }
659
+
660
+ return preset.id || selection.id;
661
+ },
662
+ },
663
+ },
664
+ };
665
+ }
666
+
667
+ async function __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIfNeeded, emitConfigChanged }) {
94
668
  if (__liveApiReady) return;
95
669
 
96
670
  const [ontologyModule, enumsModule, queryModule, commonModule] =
@@ -113,9 +687,21 @@ async function __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIf
113
687
  PDS.common = commonModule || {};
114
688
  PDS.presets = presets;
115
689
  PDS.configRelations = PDS_CONFIG_RELATIONS;
690
+ PDS.configSpec = PDS_DESIGN_CONFIG_SPEC;
691
+ PDS.configEditorMetadata = PDS_DEFAULT_CONFIG_EDITOR_METADATA;
692
+ PDS.configFormSchema = PDS_DEFAULT_CONFIG_FORM_SCHEMA;
693
+ PDS.buildConfigFormSchema = buildDesignConfigFormSchema;
694
+ PDS.getConfigEditorMetadata = getDesignConfigEditorMetadata;
116
695
  PDS.enhancerMetadata = defaultPDSEnhancerMetadata;
117
696
  PDS.applyStyles = function(generator) {
118
- return applyStyles(generator || Generator.instance);
697
+ const targetGenerator = generator || Generator.instance;
698
+ applyStyles(targetGenerator);
699
+ if (typeof emitConfigChanged === "function") {
700
+ emitConfigChanged({
701
+ mode: "live",
702
+ source: "live:styles-applied",
703
+ });
704
+ }
119
705
  };
120
706
  PDS.adoptLayers = function(shadowRoot, layers, additionalSheets) {
121
707
  return adoptLayers(
@@ -140,6 +726,9 @@ async function __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIf
140
726
  const queryEngine = new __queryClass(PDS);
141
727
  return await queryEngine.search(question);
142
728
  };
729
+ PDS.buildPresetOmniboxSettings = function(options = {}) {
730
+ return buildPresetOmniboxSettings(PDS, options);
731
+ };
143
732
 
144
733
  PDS.applyLivePreset = async function(presetId, options = {}) {
145
734
  if (!presetId) return false;
@@ -158,6 +747,8 @@ async function __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIf
158
747
  const normalized = normalizeInitConfig(inputConfig, {}, {
159
748
  presets,
160
749
  defaultLog,
750
+ validateDesignConfig,
751
+ validateInitConfig,
161
752
  });
162
753
 
163
754
  const resolvedTheme = resolveThemePreference(PDS.theme);
@@ -189,7 +780,7 @@ async function __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIf
189
780
  }
190
781
  }
191
782
 
192
- await applyStyles(generator);
783
+ PDS.applyStyles?.(generator);
193
784
 
194
785
  const presetInfo = normalized.presetInfo || { id: presetId, name: presetId };
195
786
  PDS.currentPreset = presetInfo;
@@ -199,6 +790,12 @@ async function __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIf
199
790
  design: structuredClone(normalized.generatorConfig.design),
200
791
  theme: normalized.generatorConfig.theme || baseConfig.theme,
201
792
  });
793
+ PDS.configEditorMetadata = getDesignConfigEditorMetadata(
794
+ normalized.generatorConfig.design
795
+ );
796
+ PDS.configFormSchema = buildDesignConfigFormSchema(
797
+ normalized.generatorConfig.design
798
+ );
202
799
 
203
800
  const persist = options?.persist !== false;
204
801
  if (persist && typeof window !== "undefined") {
@@ -293,7 +890,7 @@ async function __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIf
293
890
  }
294
891
 
295
892
 
296
- export async function startLive(PDS, config, { emitReady, applyResolvedTheme, setupSystemListenerIfNeeded }) {
893
+ export async function startLive(PDS, config, { emitReady, emitConfigChanged, applyResolvedTheme, setupSystemListenerIfNeeded }) {
297
894
  if (!config || typeof config !== "object") {
298
895
  throw new Error(
299
896
  "PDS.start({ mode: 'live', ... }) requires a valid configuration object"
@@ -303,7 +900,7 @@ export async function startLive(PDS, config, { emitReady, applyResolvedTheme, se
303
900
  config = applyStoredConfigOverrides(config);
304
901
 
305
902
  // Attach live-only API surface (ontology, presets, query, etc.)
306
- await __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIfNeeded });
903
+ await __attachLiveAPIs(PDS, { applyResolvedTheme, setupSystemListenerIfNeeded, emitConfigChanged });
307
904
  attachFoucListener(PDS);
308
905
 
309
906
  // FOUC Prevention: Use constructable stylesheet for synchronous, immediate effect
@@ -351,7 +948,12 @@ export async function startLive(PDS, config, { emitReady, applyResolvedTheme, se
351
948
  });
352
949
 
353
950
  // 2) Normalize first-arg API: support { preset, design, enhancers }
354
- const normalized = normalizeInitConfig(config, {}, { presets, defaultLog });
951
+ const normalized = normalizeInitConfig(config, {}, {
952
+ presets,
953
+ defaultLog,
954
+ validateDesignConfig,
955
+ validateInitConfig,
956
+ });
355
957
  if (manageTheme && !isPresetThemeCompatible(normalized.generatorConfig.design, resolvedTheme)) {
356
958
  const presetName =
357
959
  normalized.presetInfo?.name ||
@@ -441,7 +1043,7 @@ export async function startLive(PDS, config, { emitReady, applyResolvedTheme, se
441
1043
 
442
1044
  // Apply styles globally if requested (default behavior)
443
1045
  if (applyGlobalStyles) {
444
- await applyStyles(Generator.instance);
1046
+ PDS.applyStyles?.(Generator.instance);
445
1047
 
446
1048
  if (typeof window !== "undefined") {
447
1049
  setTimeout(() => {
@@ -509,18 +1111,31 @@ export async function startLive(PDS, config, { emitReady, applyResolvedTheme, se
509
1111
  theme: resolvedTheme,
510
1112
  enhancers: mergedEnhancers,
511
1113
  });
1114
+ PDS.configEditorMetadata = getDesignConfigEditorMetadata(
1115
+ normalized.generatorConfig.design
1116
+ );
1117
+ PDS.configFormSchema = buildDesignConfigFormSchema(
1118
+ normalized.generatorConfig.design
1119
+ );
1120
+ if (typeof emitConfigChanged === "function") {
1121
+ emitConfigChanged({
1122
+ mode: "live",
1123
+ source: "live:config-applied",
1124
+ preset: normalized.generatorConfig.preset,
1125
+ });
1126
+ }
512
1127
 
513
-
514
- if (config?.liveEdit && typeof document !== "undefined") {
1128
+ if (typeof document !== "undefined") {
515
1129
  try {
516
- if (!document.querySelector("pds-live-edit")) {
1130
+ if (config?.liveEdit) {
517
1131
  setTimeout(() => {
518
- const liveEditor = document.createElement("pds-live-edit");
519
- document.body.appendChild(liveEditor);
520
- }, 1000);
1132
+ void initializeLiveEditToggle();
1133
+ }, 0);
1134
+ } else {
1135
+ teardownLiveEditToggle();
521
1136
  }
522
1137
  } catch (error) {
523
- generatorConfig?.log?.("warn", "Live editor failed to start:", error);
1138
+ generatorConfig?.log?.("warn", "Live editor toggle failed to start:", error);
524
1139
  }
525
1140
  }
526
1141