@pure-ds/core 0.3.0

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 (129) hide show
  1. package/CSS-INTELLISENSE-LIMITATION.md +98 -0
  2. package/CSS-INTELLISENSE-QUICK-REF.md +238 -0
  3. package/INTELLISENSE.md +384 -0
  4. package/LICENSE +15 -0
  5. package/custom-elements-manifest.config.js +30 -0
  6. package/custom-elements.json +2003 -0
  7. package/dist/types/index.d.ts +2 -0
  8. package/dist/types/packages/pds-configurator/src/figma-export.d.ts +13 -0
  9. package/dist/types/packages/pds-configurator/src/figma-export.d.ts.map +1 -0
  10. package/dist/types/packages/pds-configurator/src/pds-config-form.d.ts +2 -0
  11. package/dist/types/packages/pds-configurator/src/pds-config-form.d.ts.map +1 -0
  12. package/dist/types/packages/pds-configurator/src/pds-configurator.d.ts +2 -0
  13. package/dist/types/packages/pds-configurator/src/pds-configurator.d.ts.map +1 -0
  14. package/dist/types/packages/pds-configurator/src/pds-demo.d.ts +2 -0
  15. package/dist/types/packages/pds-configurator/src/pds-demo.d.ts.map +1 -0
  16. package/dist/types/pds.config.d.ts +13 -0
  17. package/dist/types/pds.config.d.ts.map +1 -0
  18. package/dist/types/pds.d.ts +408 -0
  19. package/dist/types/public/assets/js/app.d.ts +2 -0
  20. package/dist/types/public/assets/js/app.d.ts.map +1 -0
  21. package/dist/types/public/assets/js/pds.d.ts +23 -0
  22. package/dist/types/public/assets/js/pds.d.ts.map +1 -0
  23. package/dist/types/public/assets/pds/components/pds-calendar.d.ts +23 -0
  24. package/dist/types/public/assets/pds/components/pds-calendar.d.ts.map +1 -0
  25. package/dist/types/public/assets/pds/components/pds-drawer.d.ts +2 -0
  26. package/dist/types/public/assets/pds/components/pds-drawer.d.ts.map +1 -0
  27. package/dist/types/public/assets/pds/components/pds-icon.d.ts +53 -0
  28. package/dist/types/public/assets/pds/components/pds-icon.d.ts.map +1 -0
  29. package/dist/types/public/assets/pds/components/pds-jsonform.d.ts +104 -0
  30. package/dist/types/public/assets/pds/components/pds-jsonform.d.ts.map +1 -0
  31. package/dist/types/public/assets/pds/components/pds-richtext.d.ts +121 -0
  32. package/dist/types/public/assets/pds/components/pds-richtext.d.ts.map +1 -0
  33. package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts +61 -0
  34. package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts.map +1 -0
  35. package/dist/types/public/assets/pds/components/pds-splitpanel.d.ts +1 -0
  36. package/dist/types/public/assets/pds/components/pds-splitpanel.d.ts.map +1 -0
  37. package/dist/types/public/assets/pds/components/pds-tabstrip.d.ts +39 -0
  38. package/dist/types/public/assets/pds/components/pds-tabstrip.d.ts.map +1 -0
  39. package/dist/types/public/assets/pds/components/pds-toaster.d.ts +111 -0
  40. package/dist/types/public/assets/pds/components/pds-toaster.d.ts.map +1 -0
  41. package/dist/types/public/assets/pds/components/pds-upload.d.ts +83 -0
  42. package/dist/types/public/assets/pds/components/pds-upload.d.ts.map +1 -0
  43. package/dist/types/src/js/app.d.ts +2 -0
  44. package/dist/types/src/js/app.d.ts.map +1 -0
  45. package/dist/types/src/js/common/ask.d.ts +22 -0
  46. package/dist/types/src/js/common/ask.d.ts.map +1 -0
  47. package/dist/types/src/js/common/common.d.ts +3 -0
  48. package/dist/types/src/js/common/common.d.ts.map +1 -0
  49. package/dist/types/src/js/common/font-loader.d.ts +24 -0
  50. package/dist/types/src/js/common/font-loader.d.ts.map +1 -0
  51. package/dist/types/src/js/common/msg.d.ts +3 -0
  52. package/dist/types/src/js/common/msg.d.ts.map +1 -0
  53. package/dist/types/src/js/lit.d.ts +25 -0
  54. package/dist/types/src/js/lit.d.ts.map +1 -0
  55. package/dist/types/src/js/pds-configurator/figma-export.d.ts +13 -0
  56. package/dist/types/src/js/pds-configurator/figma-export.d.ts.map +1 -0
  57. package/dist/types/src/js/pds-configurator/pds-config-form.d.ts +2 -0
  58. package/dist/types/src/js/pds-configurator/pds-config-form.d.ts.map +1 -0
  59. package/dist/types/src/js/pds-configurator/pds-configurator.d.ts +2 -0
  60. package/dist/types/src/js/pds-configurator/pds-configurator.d.ts.map +1 -0
  61. package/dist/types/src/js/pds-configurator/pds-demo.d.ts +2 -0
  62. package/dist/types/src/js/pds-configurator/pds-demo.d.ts.map +1 -0
  63. package/dist/types/src/js/pds-core/pds-config.d.ts +758 -0
  64. package/dist/types/src/js/pds-core/pds-config.d.ts.map +1 -0
  65. package/dist/types/src/js/pds-core/pds-enhancer-metadata.d.ts +6 -0
  66. package/dist/types/src/js/pds-core/pds-enhancer-metadata.d.ts.map +1 -0
  67. package/dist/types/src/js/pds-core/pds-enhancers.d.ts +14 -0
  68. package/dist/types/src/js/pds-core/pds-enhancers.d.ts.map +1 -0
  69. package/dist/types/src/js/pds-core/pds-enums.d.ts +87 -0
  70. package/dist/types/src/js/pds-core/pds-enums.d.ts.map +1 -0
  71. package/dist/types/src/js/pds-core/pds-generator.d.ts +741 -0
  72. package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -0
  73. package/dist/types/src/js/pds-core/pds-ontology.d.ts +48 -0
  74. package/dist/types/src/js/pds-core/pds-ontology.d.ts.map +1 -0
  75. package/dist/types/src/js/pds-core/pds-paths.d.ts +37 -0
  76. package/dist/types/src/js/pds-core/pds-paths.d.ts.map +1 -0
  77. package/dist/types/src/js/pds-core/pds-query.d.ts +102 -0
  78. package/dist/types/src/js/pds-core/pds-query.d.ts.map +1 -0
  79. package/dist/types/src/js/pds-core/pds-registry.d.ts +40 -0
  80. package/dist/types/src/js/pds-core/pds-registry.d.ts.map +1 -0
  81. package/dist/types/src/js/pds.d.ts +109 -0
  82. package/dist/types/src/js/pds.d.ts.map +1 -0
  83. package/dist/types/src/pds-core/pds-api.d.ts +31 -0
  84. package/dist/types/src/pds-core/pds-api.d.ts.map +1 -0
  85. package/package.json +104 -0
  86. package/packages/pds-cli/README.md +15 -0
  87. package/packages/pds-cli/bin/generate-css-data.js +565 -0
  88. package/packages/pds-cli/bin/generate-manifest.js +352 -0
  89. package/packages/pds-cli/bin/pds-build-icons.js +152 -0
  90. package/packages/pds-cli/bin/pds-dx.js +114 -0
  91. package/packages/pds-cli/bin/pds-static.js +556 -0
  92. package/packages/pds-cli/bin/pds.js +127 -0
  93. package/packages/pds-cli/bin/postinstall.js +380 -0
  94. package/packages/pds-cli/bin/sync-assets.js +252 -0
  95. package/packages/pds-cli/lib/asset-roots.js +47 -0
  96. package/packages/pds-cli/lib/fs-writer.js +75 -0
  97. package/pds.css-data.json +5 -0
  98. package/pds.html-data.json +5 -0
  99. package/public/assets/js/app.js +5719 -0
  100. package/public/assets/js/lit.js +131 -0
  101. package/public/assets/js/pds.js +3423 -0
  102. package/public/assets/pds/components/pds-calendar.js +837 -0
  103. package/public/assets/pds/components/pds-drawer.js +857 -0
  104. package/public/assets/pds/components/pds-icon.js +338 -0
  105. package/public/assets/pds/components/pds-jsonform.js +1775 -0
  106. package/public/assets/pds/components/pds-richtext.js +1035 -0
  107. package/public/assets/pds/components/pds-scrollrow.js +331 -0
  108. package/public/assets/pds/components/pds-splitpanel.js +401 -0
  109. package/public/assets/pds/components/pds-tabstrip.js +251 -0
  110. package/public/assets/pds/components/pds-toaster.js +446 -0
  111. package/public/assets/pds/components/pds-upload.js +657 -0
  112. package/public/assets/pds/custom-elements.json +2003 -0
  113. package/public/assets/pds/icons/pds-icons.svg +498 -0
  114. package/public/assets/pds/pds-css-complete.json +1861 -0
  115. package/public/assets/pds/pds.css-data.json +2152 -0
  116. package/public/assets/pds/vscode-custom-data.json +824 -0
  117. package/readme.md +1870 -0
  118. package/src/js/pds-core/pds-config.js +1162 -0
  119. package/src/js/pds-core/pds-enhancer-metadata.js +75 -0
  120. package/src/js/pds-core/pds-enhancers.js +357 -0
  121. package/src/js/pds-core/pds-enums.js +86 -0
  122. package/src/js/pds-core/pds-generator.js +5317 -0
  123. package/src/js/pds-core/pds-ontology.js +256 -0
  124. package/src/js/pds-core/pds-paths.js +109 -0
  125. package/src/js/pds-core/pds-query.js +571 -0
  126. package/src/js/pds-core/pds-registry.js +129 -0
  127. package/src/js/pds-core/pds.d.ts +129 -0
  128. package/src/js/pds.d.ts +408 -0
  129. package/src/js/pds.js +1579 -0
@@ -0,0 +1,571 @@
1
+ /**
2
+ * PDSQuery - Smart query engine for the Pure Design System
3
+ *
4
+ * Interprets natural language questions and maps them to PDS runtime data
5
+ * structures (tokens, components, utilities, patterns) using keyword matching,
6
+ * intent detection, and semantic scoring.
7
+ *
8
+ * @example
9
+ * const query = new PDSQuery(PDS);
10
+ * const results = await query.search("what is the focus border color on inputs?");
11
+ * // Returns array of scored results with text, value, icon, category
12
+ */
13
+
14
+ export class PDSQuery {
15
+ constructor(pds) {
16
+ this.pds = pds;
17
+
18
+ // Keyword dictionaries for intent detection
19
+ this.intents = {
20
+ color: ['color', 'colours', 'shade', 'tint', 'hue', 'foreground', 'background', 'text', 'fill', 'bg', 'fg'],
21
+ spacing: ['spacing', 'space', 'gap', 'padding', 'margin', 'distance', 'rhythm'],
22
+ typography: ['font', 'text', 'type', 'typography', 'heading', 'body', 'size', 'weight', 'family'],
23
+ border: ['border', 'outline', 'stroke', 'edge', 'frame'],
24
+ radius: ['radius', 'rounded', 'corner', 'curve', 'round'],
25
+ shadow: ['shadow', 'elevation', 'depth', 'glow', 'drop-shadow'],
26
+ component: ['component', 'element', 'widget'],
27
+ utility: ['utility', 'class', 'helper', 'css'],
28
+ layout: ['layout', 'container', 'grid', 'flex', 'group', 'arrange', 'organize'],
29
+ pattern: ['pattern', 'example', 'template', 'structure'],
30
+ interaction: ['hover', 'focus', 'active', 'disabled', 'pressed', 'selected', 'checked'],
31
+ };
32
+
33
+ // Entity/element keywords
34
+ this.entities = {
35
+ button: ['button', 'btn', 'cta'],
36
+ input: ['input', 'field', 'textbox', 'text-field', 'form-control'],
37
+ card: ['card', 'panel'],
38
+ badge: ['badge', 'pill', 'tag', 'chip'],
39
+ surface: ['surface', 'background', 'layer', 'container'],
40
+ icon: ['icon', 'svg', 'glyph', 'symbol'],
41
+ link: ['link', 'anchor', 'hyperlink'],
42
+ nav: ['nav', 'navigation', 'menu'],
43
+ modal: ['modal', 'dialog', 'popup', 'overlay'],
44
+ drawer: ['drawer', 'sidebar', 'panel'],
45
+ tab: ['tab', 'tabstrip'],
46
+ toast: ['toast', 'notification', 'alert', 'message'],
47
+ };
48
+
49
+ // Question patterns
50
+ this.questionWords = ['what', 'which', 'how', 'where', 'when', 'show', 'find', 'get', 'give', 'tell'];
51
+ }
52
+
53
+ /**
54
+ * Main search entry point
55
+ * @param {string} query - Natural language question
56
+ * @returns {Promise<Array>} Array of results with text, value, icon, category, score
57
+ */
58
+ async search(query) {
59
+ if (!query || query.length < 2) return [];
60
+
61
+ const normalized = query.toLowerCase().trim();
62
+ const tokens = this.tokenize(normalized);
63
+
64
+ // Detect intent and entities from query
65
+ const context = this.analyzeQuery(tokens, normalized);
66
+
67
+ // Generate results from multiple strategies
68
+ const results = [];
69
+
70
+ // Strategy 1: Direct token/color queries
71
+ if (context.intents.has('color')) {
72
+ results.push(...this.queryColors(context, normalized));
73
+ }
74
+
75
+ // Strategy 2: Utility class queries
76
+ if (context.intents.has('utility') || context.intents.has('border') ||
77
+ context.intents.has('layout') || normalized.includes('class')) {
78
+ results.push(...this.queryUtilities(context, normalized));
79
+ }
80
+
81
+ // Strategy 3: Component queries
82
+ if (context.intents.has('component') || context.entities.size > 0) {
83
+ results.push(...this.queryComponents(context, normalized));
84
+ }
85
+
86
+ // Strategy 4: Pattern/layout queries
87
+ if (context.intents.has('layout') || context.intents.has('pattern')) {
88
+ results.push(...this.queryPatterns(context, normalized));
89
+ }
90
+
91
+ // Strategy 5: Typography queries
92
+ if (context.intents.has('typography')) {
93
+ results.push(...this.queryTypography(context, normalized));
94
+ }
95
+
96
+ // Strategy 6: Spacing queries
97
+ if (context.intents.has('spacing')) {
98
+ results.push(...this.querySpacing(context, normalized));
99
+ }
100
+
101
+ // Deduplicate by value, keeping highest score
102
+ const seen = new Map();
103
+ for (const result of results) {
104
+ const key = result.value;
105
+ if (!seen.has(key) || seen.get(key).score < result.score) {
106
+ seen.set(key, result);
107
+ }
108
+ }
109
+
110
+ // Sort by score descending, limit to top 10
111
+ return Array.from(seen.values())
112
+ .sort((a, b) => b.score - a.score)
113
+ .slice(0, 10);
114
+ }
115
+
116
+ /**
117
+ * Tokenize and normalize query string
118
+ */
119
+ tokenize(text) {
120
+ return text.toLowerCase()
121
+ .replace(/[?!.]/g, '')
122
+ .split(/\s+/)
123
+ .filter(t => t.length > 0);
124
+ }
125
+
126
+ /**
127
+ * Analyze query to extract intents and entities
128
+ */
129
+ analyzeQuery(tokens, fullText) {
130
+ const context = {
131
+ intents: new Set(),
132
+ entities: new Set(),
133
+ modifiers: new Set(),
134
+ isQuestion: false,
135
+ tokens,
136
+ fullText
137
+ };
138
+
139
+ // Check if it's a question
140
+ context.isQuestion = this.questionWords.some(qw => tokens.includes(qw));
141
+
142
+ // Match intents
143
+ for (const [intent, keywords] of Object.entries(this.intents)) {
144
+ if (keywords.some(kw => tokens.includes(kw) || fullText.includes(kw))) {
145
+ context.intents.add(intent);
146
+ }
147
+ }
148
+
149
+ // Match entities
150
+ for (const [entity, keywords] of Object.entries(this.entities)) {
151
+ if (keywords.some(kw => tokens.includes(kw) || fullText.includes(kw))) {
152
+ context.entities.add(entity);
153
+ }
154
+ }
155
+
156
+ // Extract interaction modifiers
157
+ if (tokens.includes('hover') || fullText.includes('hover')) context.modifiers.add('hover');
158
+ if (tokens.includes('focus') || fullText.includes('focus')) context.modifiers.add('focus');
159
+ if (tokens.includes('active') || fullText.includes('active')) context.modifiers.add('active');
160
+ if (tokens.includes('disabled') || fullText.includes('disabled')) context.modifiers.add('disabled');
161
+
162
+ return context;
163
+ }
164
+
165
+ /**
166
+ * Query color tokens and surfaces
167
+ */
168
+ queryColors(context, query) {
169
+ const results = [];
170
+ const compiled = this.pds.compiled;
171
+
172
+ if (!compiled?.tokens?.colors) return results;
173
+
174
+ const colors = compiled.tokens.colors;
175
+ const entities = Array.from(context.entities);
176
+ const modifiers = Array.from(context.modifiers);
177
+
178
+ // Specific color + element queries: "focus border color on inputs"
179
+ if (modifiers.includes('focus') && context.intents.has('border') && entities.includes('input')) {
180
+ results.push({
181
+ text: 'Focus border color: var(--color-primary-500)',
182
+ value: '--color-primary-500',
183
+ icon: 'palette',
184
+ category: 'Color Token',
185
+ score: 100,
186
+ cssVar: 'var(--color-primary-500)',
187
+ description: 'Primary color used for focus states on form inputs'
188
+ });
189
+ }
190
+
191
+ // Foreground on surface queries
192
+ if ((query.includes('foreground') || query.includes('text')) &&
193
+ (query.includes('surface') || context.entities.has('surface'))) {
194
+ results.push({
195
+ text: 'Text on surface: var(--surface-text)',
196
+ value: '--surface-text',
197
+ icon: 'palette',
198
+ category: 'Surface Token',
199
+ score: 95,
200
+ cssVar: 'var(--surface-text)',
201
+ description: 'Default text color for current surface'
202
+ });
203
+ results.push({
204
+ text: 'Secondary text: var(--surface-text-secondary)',
205
+ value: '--surface-text-secondary',
206
+ icon: 'palette',
207
+ category: 'Surface Token',
208
+ score: 90,
209
+ cssVar: 'var(--surface-text-secondary)',
210
+ description: 'Secondary/muted text on surface'
211
+ });
212
+ }
213
+
214
+ // Generic color scale queries
215
+ if (query.includes('primary') || query.includes('accent') || query.includes('secondary')) {
216
+ const scale = query.includes('primary') ? 'primary' :
217
+ query.includes('accent') ? 'accent' : 'secondary';
218
+
219
+ for (const shade of [500, 600, 700]) {
220
+ const varName = `--color-${scale}-${shade}`;
221
+ results.push({
222
+ text: `${scale.charAt(0).toUpperCase() + scale.slice(1)} ${shade}: var(${varName})`,
223
+ value: varName,
224
+ icon: 'palette',
225
+ category: 'Color Scale',
226
+ score: 80 - (shade - 500) / 100,
227
+ cssVar: `var(${varName})`,
228
+ description: `${scale} color scale shade ${shade}`
229
+ });
230
+ }
231
+ }
232
+
233
+ // Button color queries
234
+ if (entities.includes('button') && context.intents.has('color')) {
235
+ const modifier = modifiers[0];
236
+ if (modifier) {
237
+ results.push({
238
+ text: `Button ${modifier} fill: var(--${modifier === 'hover' ? 'primary' : 'primary'}-fill-${modifier})`,
239
+ value: `--primary-fill-${modifier}`,
240
+ icon: 'palette',
241
+ category: 'Interactive Token',
242
+ score: 92,
243
+ description: `Button background color in ${modifier} state`
244
+ });
245
+ } else {
246
+ results.push({
247
+ text: 'Button fill: var(--primary-fill)',
248
+ value: '--primary-fill',
249
+ icon: 'palette',
250
+ category: 'Interactive Token',
251
+ score: 88,
252
+ description: 'Default button background color'
253
+ });
254
+ }
255
+ }
256
+
257
+ return results;
258
+ }
259
+
260
+ /**
261
+ * Query utility classes
262
+ */
263
+ queryUtilities(context, query) {
264
+ const results = [];
265
+ const ontology = this.pds.ontology;
266
+
267
+ if (!ontology?.utilities) return results;
268
+
269
+ const utilities = ontology.utilities;
270
+
271
+ // Border utilities
272
+ if (context.intents.has('border')) {
273
+ const borderUtils = utilities.filter(u =>
274
+ u.includes('border') || u.includes('outline')
275
+ );
276
+
277
+ borderUtils.forEach(util => {
278
+ let score = 80;
279
+ if (query.includes('gradient') && util.includes('gradient')) score = 95;
280
+ if (query.includes('glow') && util.includes('glow')) score = 95;
281
+
282
+ results.push({
283
+ text: `${util} - Border utility class`,
284
+ value: util,
285
+ icon: 'code',
286
+ category: 'Utility Class',
287
+ score,
288
+ code: `<div class="${util}">...</div>`,
289
+ description: this.describeUtility(util)
290
+ });
291
+ });
292
+ }
293
+
294
+ // Layout utilities
295
+ if (context.intents.has('layout')) {
296
+ const layoutUtils = utilities.filter(u =>
297
+ u.includes('flex') || u.includes('grid') || u.includes('items-') ||
298
+ u.includes('justify-') || u.includes('gap-')
299
+ );
300
+
301
+ layoutUtils.forEach(util => {
302
+ results.push({
303
+ text: `${util} - Layout utility`,
304
+ value: util,
305
+ icon: 'layout',
306
+ category: 'Utility Class',
307
+ score: 85,
308
+ code: `<div class="${util}">...</div>`,
309
+ description: this.describeUtility(util)
310
+ });
311
+ });
312
+ }
313
+
314
+ // Button group utilities
315
+ if (query.includes('group') && context.entities.has('button')) {
316
+ results.push({
317
+ text: '.btn-group - Group buttons together',
318
+ value: '.btn-group',
319
+ icon: 'code',
320
+ category: 'Utility Class',
321
+ score: 90,
322
+ code: `<div class="btn-group">\n <button class="btn-primary">One</button>\n <button class="btn-primary">Two</button>\n</div>`,
323
+ description: 'Container for grouped buttons with connected styling'
324
+ });
325
+ }
326
+
327
+ return results;
328
+ }
329
+
330
+ /**
331
+ * Query components
332
+ */
333
+ queryComponents(context, query) {
334
+ const results = [];
335
+ const ontology = this.pds.ontology;
336
+
337
+ if (!ontology?.components && !ontology?.primitives) return results;
338
+
339
+ // Search custom components
340
+ if (ontology.components) {
341
+ ontology.components.forEach(comp => {
342
+ const matchScore = this.scoreMatch(query, comp.name + ' ' + comp.id);
343
+ if (matchScore > 50) {
344
+ results.push({
345
+ text: `<${comp.id}> - ${comp.name}`,
346
+ value: comp.id,
347
+ icon: 'brackets-curly',
348
+ category: 'Web Component',
349
+ score: matchScore,
350
+ code: `<${comp.id}></${comp.id}>`,
351
+ description: comp.description || `${comp.name} web component`
352
+ });
353
+ }
354
+ });
355
+ }
356
+
357
+ // Search primitives (native HTML elements with PDS styling)
358
+ if (ontology.primitives) {
359
+ ontology.primitives.forEach(prim => {
360
+ const matchScore = this.scoreMatch(query, prim.name + ' ' + prim.id);
361
+ if (matchScore > 50) {
362
+ const selector = prim.selectors?.[0] || prim.id;
363
+ results.push({
364
+ text: `${selector} - ${prim.name}`,
365
+ value: prim.id,
366
+ icon: 'tag',
367
+ category: 'Primitive',
368
+ score: matchScore - 5,
369
+ code: this.generatePrimitiveExample(prim),
370
+ description: prim.description || `${prim.name} primitive element`
371
+ });
372
+ }
373
+ });
374
+ }
375
+
376
+ // Icon-specific queries
377
+ if (query.includes('icon') && (query.includes('only') || query.includes('button'))) {
378
+ results.push({
379
+ text: 'Icon-only button: <button class="btn-icon">',
380
+ value: 'btn-icon',
381
+ icon: 'star',
382
+ category: 'Pattern',
383
+ score: 95,
384
+ code: `<button class="btn-icon btn-primary">\n <pds-icon icon="heart"></pds-icon>\n</button>`,
385
+ description: 'Button with only an icon, no text label'
386
+ });
387
+ }
388
+
389
+ return results;
390
+ }
391
+
392
+ /**
393
+ * Query layout patterns
394
+ */
395
+ queryPatterns(context, query) {
396
+ const results = [];
397
+ const ontology = this.pds.ontology;
398
+
399
+ if (!ontology?.layoutPatterns) return results;
400
+
401
+ ontology.layoutPatterns.forEach(pattern => {
402
+ const matchScore = this.scoreMatch(query, pattern.name + ' ' + pattern.id + ' ' + (pattern.description || ''));
403
+ if (matchScore > 50) {
404
+ const selector = pattern.selectors?.[0] || `.${pattern.id}`;
405
+ results.push({
406
+ text: `${pattern.name} - ${pattern.description || 'Layout pattern'}`,
407
+ value: pattern.id,
408
+ icon: 'layout',
409
+ category: 'Layout Pattern',
410
+ score: matchScore,
411
+ code: `<div class="${selector.replace('.', '')}">\n <!-- content -->\n</div>`,
412
+ description: pattern.description || pattern.name
413
+ });
414
+ }
415
+ });
416
+
417
+ // Container queries
418
+ if (query.includes('container') || query.includes('group')) {
419
+ results.push({
420
+ text: 'Card - Container for grouping content',
421
+ value: 'card',
422
+ icon: 'layout',
423
+ category: 'Primitive',
424
+ score: 88,
425
+ code: `<article class="card">\n <header>\n <h3>Title</h3>\n </header>\n <p>Content...</p>\n</article>`,
426
+ description: 'Card container with optional header, body, and footer'
427
+ });
428
+
429
+ results.push({
430
+ text: 'Section - Semantic container for grouping',
431
+ value: 'section',
432
+ icon: 'layout',
433
+ category: 'Pattern',
434
+ score: 85,
435
+ code: `<section>\n <h2>Section Title</h2>\n <!-- content -->\n</section>`,
436
+ description: 'Semantic section element for content grouping'
437
+ });
438
+ }
439
+
440
+ return results;
441
+ }
442
+
443
+ /**
444
+ * Query typography tokens
445
+ */
446
+ queryTypography(context, query) {
447
+ const results = [];
448
+ const compiled = this.pds.compiled;
449
+
450
+ if (!compiled?.tokens?.typography) return results;
451
+
452
+ const typo = compiled.tokens.typography;
453
+
454
+ if (query.includes('heading') || query.includes('title')) {
455
+ results.push({
456
+ text: 'Heading font: var(--font-family-heading)',
457
+ value: '--font-family-heading',
458
+ icon: 'text-aa',
459
+ category: 'Typography Token',
460
+ score: 85,
461
+ cssVar: 'var(--font-family-heading)',
462
+ description: 'Font family for headings'
463
+ });
464
+ }
465
+
466
+ if (query.includes('body') || query.includes('text')) {
467
+ results.push({
468
+ text: 'Body font: var(--font-family-body)',
469
+ value: '--font-family-body',
470
+ icon: 'text-aa',
471
+ category: 'Typography Token',
472
+ score: 85,
473
+ cssVar: 'var(--font-family-body)',
474
+ description: 'Font family for body text'
475
+ });
476
+ }
477
+
478
+ return results;
479
+ }
480
+
481
+ /**
482
+ * Query spacing tokens
483
+ */
484
+ querySpacing(context, query) {
485
+ const results = [];
486
+ const compiled = this.pds.compiled;
487
+
488
+ if (!compiled?.tokens?.spacing) return results;
489
+
490
+ const spacing = compiled.tokens.spacing;
491
+
492
+ // Show common spacing values
493
+ for (const [key, value] of Object.entries(spacing)) {
494
+ if (['2', '4', '6', '8'].includes(key)) {
495
+ results.push({
496
+ text: `Spacing ${key}: var(--spacing-${key})`,
497
+ value: `--spacing-${key}`,
498
+ icon: 'ruler',
499
+ category: 'Spacing Token',
500
+ score: 75,
501
+ cssVar: `var(--spacing-${key})`,
502
+ description: `Spacing value: ${value}`
503
+ });
504
+ }
505
+ }
506
+
507
+ return results;
508
+ }
509
+
510
+ /**
511
+ * Calculate match score between query and target text
512
+ */
513
+ scoreMatch(query, target) {
514
+ const queryLower = query.toLowerCase();
515
+ const targetLower = target.toLowerCase();
516
+
517
+ let score = 0;
518
+
519
+ // Exact match
520
+ if (queryLower === targetLower) return 100;
521
+
522
+ // Contains full query
523
+ if (targetLower.includes(queryLower)) score += 80;
524
+
525
+ // Word overlap
526
+ const queryWords = this.tokenize(queryLower);
527
+ const targetWords = this.tokenize(targetLower);
528
+ const overlap = queryWords.filter(w => targetWords.includes(w)).length;
529
+ score += (overlap / queryWords.length) * 40;
530
+
531
+ // Starts with
532
+ if (targetLower.startsWith(queryLower)) score += 20;
533
+
534
+ return Math.min(100, score);
535
+ }
536
+
537
+ /**
538
+ * Generate example code for a primitive
539
+ */
540
+ generatePrimitiveExample(primitive) {
541
+ const selector = primitive.selectors?.[0] || primitive.id;
542
+
543
+ if (selector.includes('button') || primitive.id === 'button') {
544
+ return '<button class="btn-primary">Click me</button>';
545
+ }
546
+ if (selector.includes('card') || primitive.id === 'card') {
547
+ return '<article class="card">\n <h3>Title</h3>\n <p>Content</p>\n</article>';
548
+ }
549
+ if (selector.includes('badge') || primitive.id === 'badge') {
550
+ return '<span class="badge">New</span>';
551
+ }
552
+
553
+ return `<${selector}>Content</${selector}>`;
554
+ }
555
+
556
+ /**
557
+ * Describe utility class purpose
558
+ */
559
+ describeUtility(utilClass) {
560
+ if (utilClass.includes('border-gradient')) return 'Apply animated gradient border effect';
561
+ if (utilClass.includes('border-glow')) return 'Apply glowing border effect';
562
+ if (utilClass.includes('flex')) return 'Flexbox container utility';
563
+ if (utilClass.includes('grid')) return 'Grid container utility';
564
+ if (utilClass.includes('gap-')) return 'Set gap between flex/grid children';
565
+ if (utilClass.includes('items-')) return 'Align items in flex container';
566
+ if (utilClass.includes('justify-')) return 'Justify content in flex container';
567
+ if (utilClass === '.btn-group') return 'Group buttons with connected styling';
568
+
569
+ return 'Utility class for styling';
570
+ }
571
+ }
@@ -0,0 +1,129 @@
1
+
2
+ // ============================================================================
3
+ // PDS REGISTRY - Global mode manager for live vs static mode
4
+ // ============================================================================
5
+
6
+ class PDSRegistry {
7
+ constructor() {
8
+ this._mode = "static"; // Default to static mode
9
+ this._designer = null;
10
+ this._staticPaths = {
11
+ tokens: "/assets/pds/styles/pds-tokens.css.js",
12
+ primitives: "/assets/pds/styles/pds-primitives.css.js",
13
+ components: "/assets/pds/styles/pds-components.css.js",
14
+ utilities: "/assets/pds/styles/pds-utilities.css.js",
15
+ styles: "/assets/pds/styles/pds-styles.css.js",
16
+ };
17
+ }
18
+
19
+ /**
20
+ * Set the designer instance and switch to live mode
21
+ */
22
+ setDesigner(designer, meta = {}) {
23
+ this._designer = designer;
24
+ this._mode = "live";
25
+ const presetName = meta?.presetName;
26
+ if (presetName) {
27
+ designer?.options?.log?.("log", `PDS live with preset "${presetName}"`);
28
+ } else {
29
+ designer?.options?.log?.("log", "PDS live with custom config");
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Switch to static mode with custom paths
35
+ * Called by consumers who want to use static CSS files
36
+ */
37
+ setStaticMode(paths = {}) {
38
+ this._mode = "static";
39
+ this._staticPaths = { ...this._staticPaths, ...paths };
40
+ // Note: No access to config in static mode, using console
41
+ console.log("[PDS Registry] Switched to STATIC mode", this._staticPaths);
42
+ }
43
+
44
+ /**
45
+ * Get stylesheet for adoption in shadow DOM
46
+ * Returns CSSStyleSheet object (constructable stylesheet)
47
+ */
48
+ async getStylesheet(layer) {
49
+ if (this._mode === "live" && this._designer) {
50
+ // Return constructable stylesheet from live designer
51
+ switch (layer) {
52
+ case "tokens":
53
+ return this._designer.tokensStylesheet;
54
+ case "primitives":
55
+ return this._designer.primitivesStylesheet;
56
+ case "components":
57
+ return this._designer.componentsStylesheet;
58
+ case "utilities":
59
+ return this._designer.utilitiesStylesheet;
60
+ default:
61
+ this._designer?.options?.log?.("warn", `[PDS Registry] Unknown layer: ${layer}`);
62
+ return null;
63
+ }
64
+ } else {
65
+ // Import from static path
66
+ try {
67
+ const module = await import(this._staticPaths[layer]);
68
+ return module[layer]; // Return exported stylesheet
69
+ } catch (error) {
70
+ // No access to config in static mode, fall back to console
71
+ console.error(`[PDS Registry] Failed to load static ${layer}:`, error);
72
+ console.error(`[PDS Registry] Looking for: ${this._staticPaths[layer]}`);
73
+ console.error(`[PDS Registry] Make sure you've run 'npm run pds:export' and configured PDS.start() with the correct static.root path`);
74
+ // Return empty stylesheet as fallback
75
+ const fallback = new CSSStyleSheet();
76
+ fallback.replaceSync("/* Failed to load " + layer + " */");
77
+ return fallback;
78
+ }
79
+ }
80
+ }
81
+
82
+ // /**
83
+ // * Get BLOB URL for a layer (live mode only)
84
+ // * Used for @import statements in CSS
85
+ // */
86
+ // getBlobURL(layer) {
87
+ // if (this._mode === "live" && this._designer) {
88
+ // switch (layer) {
89
+ // case "tokens":
90
+ // return this._designer.tokensBlobURL;
91
+ // case "primitives":
92
+ // return this._designer.primitivesBlobURL;
93
+ // case "components":
94
+ // return this._designer.componentsBlobURL;
95
+ // case "utilities":
96
+ // return this._designer.utilitiesBlobURL;
97
+ // case "styles":
98
+ // return this._designer.stylesBlobURL;
99
+ // default:
100
+ // return null;
101
+ // }
102
+ // }
103
+ // return null;
104
+ // }
105
+
106
+ /**
107
+ * Get current mode
108
+ */
109
+ get mode() {
110
+ return this._mode;
111
+ }
112
+
113
+ /**
114
+ * Check if in live mode
115
+ */
116
+ get isLive() {
117
+ return this._mode === "live" && this._designer !== null;
118
+ }
119
+
120
+ /**
121
+ * Check if designer is available
122
+ */
123
+ get hasDesigner() {
124
+ return this._designer !== null;
125
+ }
126
+ }
127
+
128
+ // Export singleton instance
129
+ export const registry = new PDSRegistry();