@portel/photon-core 1.5.0 → 2.1.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 (163) hide show
  1. package/dist/auto-ui.js +1 -1
  2. package/dist/auto-ui.js.map +1 -1
  3. package/dist/base.d.ts +1 -1
  4. package/dist/base.d.ts.map +1 -1
  5. package/dist/base.js +2 -2
  6. package/dist/base.js.map +1 -1
  7. package/dist/cli-ui-renderer.js +1 -1
  8. package/dist/cli-ui-renderer.js.map +1 -1
  9. package/dist/design-system/index.d.ts +21 -0
  10. package/dist/design-system/index.d.ts.map +1 -0
  11. package/dist/design-system/index.js +27 -0
  12. package/dist/design-system/index.js.map +1 -0
  13. package/dist/design-system/tokens.d.ts +149 -0
  14. package/dist/design-system/tokens.d.ts.map +1 -0
  15. package/dist/design-system/tokens.js +413 -0
  16. package/dist/design-system/tokens.js.map +1 -0
  17. package/dist/design-system/transaction-ui.d.ts +70 -0
  18. package/dist/design-system/transaction-ui.d.ts.map +1 -0
  19. package/dist/design-system/transaction-ui.js +982 -0
  20. package/dist/design-system/transaction-ui.js.map +1 -0
  21. package/dist/generator.d.ts +56 -6
  22. package/dist/generator.d.ts.map +1 -1
  23. package/dist/generator.js.map +1 -1
  24. package/dist/index.d.ts +6 -7
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +46 -56
  27. package/dist/index.js.map +1 -1
  28. package/dist/io.d.ts +103 -2
  29. package/dist/io.d.ts.map +1 -1
  30. package/dist/io.js +37 -1
  31. package/dist/io.js.map +1 -1
  32. package/dist/rendering/components.d.ts +29 -0
  33. package/dist/rendering/components.d.ts.map +1 -0
  34. package/dist/rendering/components.js +773 -0
  35. package/dist/rendering/components.js.map +1 -0
  36. package/dist/rendering/field-analyzer.d.ts +48 -0
  37. package/dist/rendering/field-analyzer.d.ts.map +1 -0
  38. package/dist/rendering/field-analyzer.js +270 -0
  39. package/dist/rendering/field-analyzer.js.map +1 -0
  40. package/dist/rendering/field-renderers.d.ts +64 -0
  41. package/dist/rendering/field-renderers.d.ts.map +1 -0
  42. package/dist/rendering/field-renderers.js +317 -0
  43. package/dist/rendering/field-renderers.js.map +1 -0
  44. package/dist/rendering/index.d.ts +28 -0
  45. package/dist/rendering/index.d.ts.map +1 -0
  46. package/dist/rendering/index.js +60 -0
  47. package/dist/rendering/index.js.map +1 -0
  48. package/dist/rendering/layout-selector.d.ts +48 -0
  49. package/dist/rendering/layout-selector.d.ts.map +1 -0
  50. package/dist/rendering/layout-selector.js +347 -0
  51. package/dist/rendering/layout-selector.js.map +1 -0
  52. package/dist/rendering/template-engine.d.ts +41 -0
  53. package/dist/rendering/template-engine.d.ts.map +1 -0
  54. package/dist/rendering/template-engine.js +236 -0
  55. package/dist/rendering/template-engine.js.map +1 -0
  56. package/dist/schema-extractor.d.ts +30 -0
  57. package/dist/schema-extractor.d.ts.map +1 -1
  58. package/dist/schema-extractor.js +205 -12
  59. package/dist/schema-extractor.js.map +1 -1
  60. package/dist/stateful.js +1 -1
  61. package/dist/stateful.js.map +1 -1
  62. package/dist/types.d.ts +9 -1
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/dist/ucp/ap2/handlers.d.ts +242 -0
  66. package/dist/ucp/ap2/handlers.d.ts.map +1 -0
  67. package/dist/ucp/ap2/handlers.js +482 -0
  68. package/dist/ucp/ap2/handlers.js.map +1 -0
  69. package/dist/ucp/ap2/mandates.d.ts +95 -0
  70. package/dist/ucp/ap2/mandates.d.ts.map +1 -0
  71. package/dist/ucp/ap2/mandates.js +234 -0
  72. package/dist/ucp/ap2/mandates.js.map +1 -0
  73. package/dist/ucp/ap2/types.d.ts +305 -0
  74. package/dist/ucp/ap2/types.d.ts.map +1 -0
  75. package/dist/ucp/ap2/types.js +8 -0
  76. package/dist/ucp/ap2/types.js.map +1 -0
  77. package/dist/ucp/capabilities/checkout.d.ts +118 -0
  78. package/dist/ucp/capabilities/checkout.d.ts.map +1 -0
  79. package/dist/ucp/capabilities/checkout.js +344 -0
  80. package/dist/ucp/capabilities/checkout.js.map +1 -0
  81. package/dist/ucp/capabilities/identity.d.ts +130 -0
  82. package/dist/ucp/capabilities/identity.d.ts.map +1 -0
  83. package/dist/ucp/capabilities/identity.js +290 -0
  84. package/dist/ucp/capabilities/identity.js.map +1 -0
  85. package/dist/ucp/capabilities/order.d.ts +142 -0
  86. package/dist/ucp/capabilities/order.d.ts.map +1 -0
  87. package/dist/ucp/capabilities/order.js +383 -0
  88. package/dist/ucp/capabilities/order.js.map +1 -0
  89. package/dist/ucp/index.d.ts +18 -0
  90. package/dist/ucp/index.d.ts.map +1 -0
  91. package/dist/ucp/index.js +19 -0
  92. package/dist/ucp/index.js.map +1 -0
  93. package/dist/ucp/manifest.d.ts +62 -0
  94. package/dist/ucp/manifest.d.ts.map +1 -0
  95. package/dist/ucp/manifest.js +180 -0
  96. package/dist/ucp/manifest.js.map +1 -0
  97. package/dist/ucp/types.d.ts +327 -0
  98. package/dist/ucp/types.d.ts.map +1 -0
  99. package/dist/ucp/types.js +8 -0
  100. package/dist/ucp/types.js.map +1 -0
  101. package/package.json +3 -4
  102. package/src/auto-ui.ts +1 -1
  103. package/src/base.ts +2 -2
  104. package/src/cli-ui-renderer.ts +1 -1
  105. package/src/design-system/index.ts +30 -0
  106. package/src/design-system/tokens.ts +451 -0
  107. package/src/design-system/transaction-ui.ts +1038 -0
  108. package/src/generator.ts +58 -2
  109. package/src/index.ts +135 -124
  110. package/src/io.ts +108 -3
  111. package/src/rendering/components.ts +785 -0
  112. package/src/rendering/field-analyzer.ts +299 -0
  113. package/src/rendering/field-renderers.ts +356 -0
  114. package/src/rendering/index.ts +63 -0
  115. package/src/rendering/layout-selector.ts +390 -0
  116. package/src/rendering/template-engine.ts +254 -0
  117. package/src/schema-extractor.ts +225 -12
  118. package/src/stateful.ts +1 -1
  119. package/src/types.ts +10 -1
  120. package/src/ucp/ap2/handlers.ts +779 -0
  121. package/src/ucp/ap2/mandates.ts +354 -0
  122. package/src/ucp/ap2/types.ts +441 -0
  123. package/src/ucp/capabilities/checkout.ts +497 -0
  124. package/src/ucp/capabilities/identity.ts +425 -0
  125. package/src/ucp/capabilities/order.ts +549 -0
  126. package/src/ucp/index.ts +27 -0
  127. package/src/ucp/manifest.ts +257 -0
  128. package/src/ucp/types.ts +454 -0
  129. package/dist/cli-formatter.d.ts +0 -92
  130. package/dist/cli-formatter.d.ts.map +0 -1
  131. package/dist/cli-formatter.js +0 -486
  132. package/dist/cli-formatter.js.map +0 -1
  133. package/dist/context.d.ts +0 -6
  134. package/dist/context.d.ts.map +0 -1
  135. package/dist/context.js +0 -3
  136. package/dist/context.js.map +0 -1
  137. package/dist/elicit.d.ts +0 -93
  138. package/dist/elicit.d.ts.map +0 -1
  139. package/dist/elicit.js +0 -373
  140. package/dist/elicit.js.map +0 -1
  141. package/dist/mcp-client.d.ts +0 -218
  142. package/dist/mcp-client.d.ts.map +0 -1
  143. package/dist/mcp-client.js +0 -424
  144. package/dist/mcp-client.js.map +0 -1
  145. package/dist/mcp-sdk-transport.d.ts +0 -88
  146. package/dist/mcp-sdk-transport.d.ts.map +0 -1
  147. package/dist/mcp-sdk-transport.js +0 -360
  148. package/dist/mcp-sdk-transport.js.map +0 -1
  149. package/dist/photon-config.d.ts +0 -86
  150. package/dist/photon-config.d.ts.map +0 -1
  151. package/dist/photon-config.js +0 -156
  152. package/dist/photon-config.js.map +0 -1
  153. package/dist/progress.d.ts +0 -93
  154. package/dist/progress.d.ts.map +0 -1
  155. package/dist/progress.js +0 -195
  156. package/dist/progress.js.map +0 -1
  157. package/src/cli-formatter.ts +0 -579
  158. package/src/context.ts +0 -7
  159. package/src/elicit.ts +0 -438
  160. package/src/mcp-client.ts +0 -561
  161. package/src/mcp-sdk-transport.ts +0 -449
  162. package/src/photon-config.ts +0 -201
  163. package/src/progress.ts +0 -224
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Layout Selector - Auto-detect best layout for data
3
+ *
4
+ * Determines the optimal component/layout to render data based on:
5
+ * 1. Explicit @format annotation (highest priority)
6
+ * 2. Data shape (array vs object vs primitive)
7
+ * 3. Data content (has images -> grid, etc.)
8
+ */
9
+ // Map legacy @format values to new layout types
10
+ const FORMAT_TO_LAYOUT = {
11
+ 'table': 'list', // table -> list (smart rendering)
12
+ 'list': 'list',
13
+ 'grid': 'grid',
14
+ 'card': 'card',
15
+ 'kv': 'kv',
16
+ 'tree': 'tree',
17
+ 'json': 'json',
18
+ 'markdown': 'markdown',
19
+ 'mermaid': 'mermaid',
20
+ 'code': 'code',
21
+ 'text': 'text',
22
+ 'primitive': 'text',
23
+ 'chips': 'chips',
24
+ 'html': 'html',
25
+ };
26
+ /**
27
+ * Select the best layout type for given data
28
+ */
29
+ export function selectLayout(data, format, hints) {
30
+ // 1. Explicit format takes precedence (backward compat)
31
+ if (format) {
32
+ // Handle code:language format
33
+ if (format.startsWith('code:'))
34
+ return 'code';
35
+ const layout = FORMAT_TO_LAYOUT[format] || 'json';
36
+ // Smart fallback: if list/table format but data is not an array, use card
37
+ if ((layout === 'list' || format === 'table') && !Array.isArray(data) && typeof data === 'object' && data !== null) {
38
+ return 'card';
39
+ }
40
+ return layout;
41
+ }
42
+ // 2. Null/undefined
43
+ if (data === null || data === undefined) {
44
+ return 'text';
45
+ }
46
+ // 3. Primitives
47
+ if (typeof data === 'string') {
48
+ // Check if it looks like markdown
49
+ if (hasMarkdownSyntax(data))
50
+ return 'markdown';
51
+ // Check if it looks like mermaid
52
+ if (data.trim().startsWith('graph ') || data.trim().startsWith('flowchart ')) {
53
+ return 'mermaid';
54
+ }
55
+ return 'text';
56
+ }
57
+ if (typeof data === 'number' || typeof data === 'boolean') {
58
+ return 'text';
59
+ }
60
+ // 4. Arrays
61
+ if (Array.isArray(data)) {
62
+ if (data.length === 0)
63
+ return 'text'; // Empty array
64
+ const first = data[0];
65
+ // Array of strings -> chips
66
+ if (typeof first === 'string') {
67
+ return 'chips';
68
+ }
69
+ // Array of objects
70
+ if (typeof first === 'object' && first !== null) {
71
+ // Check if items have image fields -> grid
72
+ if (hasImageFields(first)) {
73
+ return 'grid';
74
+ }
75
+ // Default: list
76
+ return 'list';
77
+ }
78
+ // Mixed or primitive arrays
79
+ return 'chips';
80
+ }
81
+ // 5. Objects
82
+ if (typeof data === 'object') {
83
+ // Check for special fields
84
+ if ('diagram' in data && typeof data.diagram === 'string') {
85
+ return 'mermaid';
86
+ }
87
+ // Check if deeply nested -> tree
88
+ if (isNested(data)) {
89
+ return 'tree';
90
+ }
91
+ // Flat object -> card (or kv for many fields)
92
+ const fieldCount = Object.keys(data).length;
93
+ if (fieldCount > 10) {
94
+ return 'kv'; // Too many fields for card
95
+ }
96
+ return 'card';
97
+ }
98
+ // Fallback
99
+ return 'json';
100
+ }
101
+ /**
102
+ * Check if object has fields that look like actual images (not just icon characters)
103
+ * We need to verify the VALUE looks like an image URL, not just the field name
104
+ */
105
+ export function hasImageFields(obj) {
106
+ const imageFieldNames = /^(image|photo|thumbnail|picture|poster|cover)$/i;
107
+ const avatarFieldName = /^(avatar)$/i;
108
+ const imageUrlPattern = /\.(jpg|jpeg|png|gif|webp|svg)(\?.*)?$/i;
109
+ const dataUrlPattern = /^data:image\//i;
110
+ for (const [key, value] of Object.entries(obj)) {
111
+ // Skip non-string values
112
+ if (typeof value !== 'string')
113
+ continue;
114
+ // Check for image URL patterns in value
115
+ if (imageUrlPattern.test(value) || dataUrlPattern.test(value))
116
+ return true;
117
+ // For image field names (not avatar), check if value looks like a URL
118
+ if (imageFieldNames.test(key) && (value.startsWith('http') || value.startsWith('/'))) {
119
+ return true;
120
+ }
121
+ // For avatar fields specifically, only treat as image if it's actually a URL
122
+ // Single characters or short strings are icons, not images
123
+ if (avatarFieldName.test(key) && value.length > 10 && (value.startsWith('http') || value.startsWith('/'))) {
124
+ return true;
125
+ }
126
+ }
127
+ return false;
128
+ }
129
+ /**
130
+ * Check if object is nested (has object/array values)
131
+ */
132
+ export function isNested(obj, depth = 0) {
133
+ if (depth > 2)
134
+ return true; // Max depth check
135
+ for (const value of Object.values(obj)) {
136
+ if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object') {
137
+ return true;
138
+ }
139
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
140
+ if (isNested(value, depth + 1))
141
+ return true;
142
+ }
143
+ }
144
+ return false;
145
+ }
146
+ /**
147
+ * Check if string contains markdown syntax
148
+ */
149
+ export function hasMarkdownSyntax(text) {
150
+ // Check for common markdown patterns
151
+ const patterns = [
152
+ /^#{1,6}\s/m, // Headers
153
+ /\*\*[^*]+\*\*/, // Bold
154
+ /\*[^*]+\*/, // Italic
155
+ /\[[^\]]+\]\([^)]+\)/, // Links
156
+ /```[\s\S]*```/, // Code blocks
157
+ /^\s*[-*+]\s/m, // Lists
158
+ /^\s*\d+\.\s/m, // Numbered lists
159
+ ];
160
+ return patterns.some(p => p.test(text));
161
+ }
162
+ /**
163
+ * Parse layout hints from nested JSDoc syntax
164
+ * Example: {@title name, @subtitle email, @style inset}
165
+ */
166
+ export function parseLayoutHints(hintsString) {
167
+ const hints = {};
168
+ if (!hintsString)
169
+ return hints;
170
+ // Split by comma and parse each hint
171
+ const parts = hintsString.split(',').map(s => s.trim());
172
+ for (const part of parts) {
173
+ // Match @key value or @key value:renderer
174
+ const match = part.match(/@(\w+)\s+([^:]+)(?::(\w+))?/);
175
+ if (match) {
176
+ const [, key, value, renderer] = match;
177
+ const cleanValue = value.trim();
178
+ switch (key) {
179
+ case 'title':
180
+ hints.title = cleanValue;
181
+ break;
182
+ case 'subtitle':
183
+ hints.subtitle = cleanValue;
184
+ break;
185
+ case 'icon':
186
+ hints.icon = cleanValue;
187
+ break;
188
+ case 'badge':
189
+ hints.badge = cleanValue;
190
+ break;
191
+ case 'detail':
192
+ hints.detail = cleanValue;
193
+ break;
194
+ case 'image':
195
+ hints.image = cleanValue;
196
+ break;
197
+ case 'style':
198
+ hints.style = cleanValue;
199
+ break;
200
+ case 'accessory':
201
+ hints.accessory = cleanValue;
202
+ break;
203
+ case 'columns':
204
+ hints.columns = parseInt(cleanValue, 10);
205
+ break;
206
+ case 'fields':
207
+ hints.fields = cleanValue.split(/\s+/);
208
+ break;
209
+ }
210
+ }
211
+ }
212
+ return hints;
213
+ }
214
+ /**
215
+ * Generate JavaScript code for layout selector (to embed in HTML)
216
+ */
217
+ export function generateLayoutSelectorJS() {
218
+ return `
219
+ // Layout Selector - Auto-detect best layout for data
220
+ const FORMAT_TO_LAYOUT = {
221
+ 'table': 'list',
222
+ 'list': 'list',
223
+ 'grid': 'grid',
224
+ 'card': 'card',
225
+ 'kv': 'kv',
226
+ 'tree': 'tree',
227
+ 'json': 'json',
228
+ 'markdown': 'markdown',
229
+ 'mermaid': 'mermaid',
230
+ 'code': 'code',
231
+ 'text': 'text',
232
+ 'primitive': 'text',
233
+ 'chips': 'chips',
234
+ 'html': 'html',
235
+ };
236
+
237
+ function selectLayout(data, format, hints) {
238
+ if (format) {
239
+ if (format.startsWith('code:')) return 'code';
240
+ var layout = FORMAT_TO_LAYOUT[format] || 'json';
241
+ // Smart fallback: if list/table format but data is not an array, use card
242
+ if ((layout === 'list' || format === 'table') && !Array.isArray(data) && typeof data === 'object' && data !== null) {
243
+ return 'card';
244
+ }
245
+ return layout;
246
+ }
247
+
248
+ if (data === null || data === undefined) return 'text';
249
+
250
+ if (typeof data === 'string') {
251
+ if (hasMarkdownSyntax(data)) return 'markdown';
252
+ if (data.trim().startsWith('graph ') || data.trim().startsWith('flowchart ')) {
253
+ return 'mermaid';
254
+ }
255
+ return 'text';
256
+ }
257
+
258
+ if (typeof data === 'number' || typeof data === 'boolean') return 'text';
259
+
260
+ if (Array.isArray(data)) {
261
+ if (data.length === 0) return 'text';
262
+ const first = data[0];
263
+ if (typeof first === 'string') return 'chips';
264
+ if (typeof first === 'object' && first !== null) {
265
+ if (hasImageFields(first)) return 'grid';
266
+ return 'list';
267
+ }
268
+ return 'chips';
269
+ }
270
+
271
+ if (typeof data === 'object') {
272
+ if ('diagram' in data && typeof data.diagram === 'string') return 'mermaid';
273
+ if (isNested(data)) return 'tree';
274
+ const fieldCount = Object.keys(data).length;
275
+ if (fieldCount > 10) return 'kv';
276
+ return 'card';
277
+ }
278
+
279
+ return 'json';
280
+ }
281
+
282
+ function hasImageFields(obj) {
283
+ const imageFieldNames = /^(image|photo|thumbnail|picture|poster|cover)$/i;
284
+ const avatarFieldName = /^(avatar)$/i;
285
+ const imageUrlPattern = /\\.(jpg|jpeg|png|gif|webp|svg)(\\?.*)?$/i;
286
+ const dataUrlPattern = /^data:image\\//i;
287
+ for (const [key, value] of Object.entries(obj)) {
288
+ if (typeof value !== 'string') continue;
289
+ if (imageUrlPattern.test(value) || dataUrlPattern.test(value)) return true;
290
+ if (imageFieldNames.test(key) && (value.startsWith('http') || value.startsWith('/'))) return true;
291
+ if (avatarFieldName.test(key) && value.length > 10 && (value.startsWith('http') || value.startsWith('/'))) return true;
292
+ }
293
+ return false;
294
+ }
295
+
296
+ function isNested(obj, depth = 0) {
297
+ if (depth > 2) return true;
298
+ for (const value of Object.values(obj)) {
299
+ if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object') return true;
300
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
301
+ if (isNested(value, depth + 1)) return true;
302
+ }
303
+ }
304
+ return false;
305
+ }
306
+
307
+ function hasMarkdownSyntax(text) {
308
+ const patterns = [
309
+ /^#{1,6}\\s/m,
310
+ /\\*\\*[^*]+\\*\\*/,
311
+ /\\*[^*]+\\*/,
312
+ /\\[[^\\]]+\\]\\([^)]+\\)/,
313
+ /\`\`\`[\\s\\S]*\`\`\`/,
314
+ /^\\s*[-*+]\\s/m,
315
+ /^\\s*\\d+\\.\\s/m,
316
+ ];
317
+ return patterns.some(p => p.test(text));
318
+ }
319
+
320
+ function parseLayoutHints(hintsString) {
321
+ const hints = {};
322
+ if (!hintsString) return hints;
323
+ const parts = hintsString.split(',').map(s => s.trim());
324
+ for (const part of parts) {
325
+ const match = part.match(/@(\\w+)\\s+([^:]+)(?::(\\w+))?/);
326
+ if (match) {
327
+ const [, key, value, renderer] = match;
328
+ const cleanValue = value.trim();
329
+ switch (key) {
330
+ case 'title': hints.title = cleanValue; break;
331
+ case 'subtitle': hints.subtitle = cleanValue; break;
332
+ case 'icon': hints.icon = cleanValue; break;
333
+ case 'badge': hints.badge = cleanValue; break;
334
+ case 'detail': hints.detail = cleanValue; break;
335
+ case 'image': hints.image = cleanValue; break;
336
+ case 'style': hints.style = cleanValue; break;
337
+ case 'accessory': hints.accessory = cleanValue; break;
338
+ case 'columns': hints.columns = parseInt(cleanValue, 10); break;
339
+ case 'fields': hints.fields = cleanValue.split(/\\s+/); break;
340
+ }
341
+ }
342
+ }
343
+ return hints;
344
+ }
345
+ `;
346
+ }
347
+ //# sourceMappingURL=layout-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-selector.js","sourceRoot":"","sources":["../../src/rendering/layout-selector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Template Engine - Custom HTML Template Support
3
+ *
4
+ * Enables custom UI templates that map elements to class methods.
5
+ * Perfect for specialized interfaces like:
6
+ * - TV remote control
7
+ * - Numeric keypad
8
+ * - Dashboard with gauges
9
+ * - Media player controls
10
+ *
11
+ * Template Binding Attributes:
12
+ * - data-method: Method to call on click
13
+ * - data-args: JSON arguments to pass
14
+ * - data-result: Container for method output
15
+ * - data-bind: Live data binding
16
+ * - data-if: Conditional visibility
17
+ */
18
+ export interface TemplateBinding {
19
+ element: string;
20
+ method: string;
21
+ args?: any;
22
+ event?: string;
23
+ }
24
+ export interface TemplateConfig {
25
+ id: string;
26
+ path: string;
27
+ bindings?: TemplateBinding[];
28
+ }
29
+ /**
30
+ * Parse template HTML and extract data-method bindings
31
+ */
32
+ export declare function parseTemplateBindings(html: string): TemplateBinding[];
33
+ /**
34
+ * Generate JavaScript for template engine (to embed in HTML)
35
+ */
36
+ export declare function generateTemplateEngineJS(): string;
37
+ /**
38
+ * Generate CSS for template engine
39
+ */
40
+ export declare function generateTemplateEngineCSS(): string;
41
+ //# sourceMappingURL=template-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-engine.d.ts","sourceRoot":"","sources":["../../src/rendering/template-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CAWrE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAwIjD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CA8DlD"}
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Template Engine - Custom HTML Template Support
3
+ *
4
+ * Enables custom UI templates that map elements to class methods.
5
+ * Perfect for specialized interfaces like:
6
+ * - TV remote control
7
+ * - Numeric keypad
8
+ * - Dashboard with gauges
9
+ * - Media player controls
10
+ *
11
+ * Template Binding Attributes:
12
+ * - data-method: Method to call on click
13
+ * - data-args: JSON arguments to pass
14
+ * - data-result: Container for method output
15
+ * - data-bind: Live data binding
16
+ * - data-if: Conditional visibility
17
+ */
18
+ /**
19
+ * Parse template HTML and extract data-method bindings
20
+ */
21
+ export function parseTemplateBindings(html) {
22
+ const bindings = [];
23
+ // Match elements with data-method attribute
24
+ const methodRegex = /data-method=["']([^"']+)["']/g;
25
+ const argsRegex = /data-args=["']([^"']+)["']/g;
26
+ // Note: This is a simplified parser - actual implementation
27
+ // would use DOM parsing in the browser
28
+ return bindings;
29
+ }
30
+ /**
31
+ * Generate JavaScript for template engine (to embed in HTML)
32
+ */
33
+ export function generateTemplateEngineJS() {
34
+ return `
35
+ // ==========================================================================
36
+ // Template Engine - Custom UI Template Support
37
+ // ==========================================================================
38
+
39
+ /**
40
+ * Initialize template bindings for custom UI
41
+ * Finds all elements with data-method and binds click handlers
42
+ */
43
+ function initTemplateBindings(container, invokeMethod) {
44
+ // Find all elements with data-method attribute
45
+ const methodElements = container.querySelectorAll('[data-method]');
46
+
47
+ methodElements.forEach(el => {
48
+ const methodName = el.getAttribute('data-method');
49
+ const argsStr = el.getAttribute('data-args');
50
+ const eventType = el.getAttribute('data-event') || 'click';
51
+
52
+ // Parse args if present
53
+ let args = {};
54
+ if (argsStr) {
55
+ try {
56
+ args = JSON.parse(argsStr);
57
+ } catch (e) {
58
+ console.warn('Invalid data-args JSON:', argsStr);
59
+ }
60
+ }
61
+
62
+ // Add event listener
63
+ el.addEventListener(eventType, async (e) => {
64
+ e.preventDefault();
65
+ e.stopPropagation();
66
+
67
+ // Add loading state
68
+ el.classList.add('template-loading');
69
+
70
+ try {
71
+ // Invoke the method
72
+ const result = await invokeMethod(methodName, args);
73
+
74
+ // Find result container and update
75
+ const resultContainer = container.querySelector('[data-result]');
76
+ if (resultContainer && result !== undefined) {
77
+ // Use smart rendering for the result
78
+ const rendered = renderSmartResult(result, null, null);
79
+ if (rendered) {
80
+ resultContainer.innerHTML = rendered;
81
+ } else {
82
+ resultContainer.textContent = JSON.stringify(result, null, 2);
83
+ }
84
+ }
85
+ } catch (error) {
86
+ console.error('Template method error:', error);
87
+ const resultContainer = container.querySelector('[data-result]');
88
+ if (resultContainer) {
89
+ resultContainer.innerHTML = '<div class="template-error">' + escapeHtml(error.message) + '</div>';
90
+ }
91
+ } finally {
92
+ el.classList.remove('template-loading');
93
+ }
94
+ });
95
+ });
96
+
97
+ // Handle data-bind for live updates
98
+ const bindElements = container.querySelectorAll('[data-bind]');
99
+ bindElements.forEach(el => {
100
+ const bindKey = el.getAttribute('data-bind');
101
+ // Store reference for later updates
102
+ el._bindKey = bindKey;
103
+ });
104
+
105
+ // Handle data-if for conditional visibility
106
+ const ifElements = container.querySelectorAll('[data-if]');
107
+ ifElements.forEach(el => {
108
+ const condition = el.getAttribute('data-if');
109
+ el._ifCondition = condition;
110
+ // Initially hidden until condition is evaluated
111
+ el.style.display = 'none';
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Update bound elements with new data
117
+ */
118
+ function updateTemplateBindings(container, data) {
119
+ // Update data-bind elements
120
+ const bindElements = container.querySelectorAll('[data-bind]');
121
+ bindElements.forEach(el => {
122
+ const bindKey = el._bindKey || el.getAttribute('data-bind');
123
+ if (bindKey && data[bindKey] !== undefined) {
124
+ if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
125
+ el.value = data[bindKey];
126
+ } else {
127
+ el.textContent = data[bindKey];
128
+ }
129
+ }
130
+ });
131
+
132
+ // Update data-if elements
133
+ const ifElements = container.querySelectorAll('[data-if]');
134
+ ifElements.forEach(el => {
135
+ const condition = el._ifCondition || el.getAttribute('data-if');
136
+ if (condition) {
137
+ // Evaluate condition against data
138
+ const value = data[condition];
139
+ el.style.display = value ? '' : 'none';
140
+ }
141
+ });
142
+ }
143
+
144
+ /**
145
+ * Load and render a custom template
146
+ */
147
+ async function loadTemplate(templatePath, container, invokeMethod) {
148
+ try {
149
+ // Fetch template content
150
+ const response = await fetch('/api/template?path=' + encodeURIComponent(templatePath));
151
+ if (!response.ok) {
152
+ throw new Error('Failed to load template: ' + response.statusText);
153
+ }
154
+
155
+ const html = await response.text();
156
+ container.innerHTML = html;
157
+
158
+ // Initialize bindings
159
+ initTemplateBindings(container, invokeMethod);
160
+
161
+ return true;
162
+ } catch (error) {
163
+ console.error('Template load error:', error);
164
+ container.innerHTML = '<div class="template-error">Failed to load template: ' + escapeHtml(error.message) + '</div>';
165
+ return false;
166
+ }
167
+ }
168
+ `;
169
+ }
170
+ /**
171
+ * Generate CSS for template engine
172
+ */
173
+ export function generateTemplateEngineCSS() {
174
+ return `
175
+ /* Template Engine Styles */
176
+ .template-loading {
177
+ opacity: 0.6;
178
+ pointer-events: none;
179
+ position: relative;
180
+ }
181
+
182
+ .template-loading::after {
183
+ content: '';
184
+ position: absolute;
185
+ top: 50%;
186
+ left: 50%;
187
+ width: 16px;
188
+ height: 16px;
189
+ margin: -8px 0 0 -8px;
190
+ border: 2px solid var(--accent);
191
+ border-top-color: transparent;
192
+ border-radius: 50%;
193
+ animation: template-spin 0.8s linear infinite;
194
+ }
195
+
196
+ @keyframes template-spin {
197
+ to { transform: rotate(360deg); }
198
+ }
199
+
200
+ .template-error {
201
+ color: var(--error);
202
+ padding: 12px;
203
+ background: rgba(239, 68, 68, 0.1);
204
+ border-radius: 8px;
205
+ font-size: 0.875rem;
206
+ }
207
+
208
+ /* Template button styles */
209
+ [data-method] {
210
+ cursor: pointer;
211
+ transition: all 0.15s ease;
212
+ }
213
+
214
+ [data-method]:hover {
215
+ filter: brightness(1.1);
216
+ }
217
+
218
+ [data-method]:active {
219
+ transform: scale(0.98);
220
+ }
221
+
222
+ /* Result container */
223
+ [data-result] {
224
+ min-height: 40px;
225
+ padding: 8px;
226
+ border-radius: 8px;
227
+ background: var(--bg-tertiary);
228
+ }
229
+
230
+ [data-result]:empty::before {
231
+ content: '—';
232
+ color: var(--text-muted);
233
+ }
234
+ `;
235
+ }
236
+ //# sourceMappingURL=template-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-engine.js","sourceRoot":"","sources":["../../src/rendering/template-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAeH;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,MAAM,QAAQ,GAAsB,EAAE,CAAC;IAEvC,4CAA4C;IAC5C,MAAM,WAAW,GAAG,+BAA+B,CAAC;IACpD,MAAM,SAAS,GAAG,6BAA6B,CAAC;IAEhD,4DAA4D;IAC5D,uCAAuC;IAEvC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsIR,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4DR,CAAC;AACF,CAAC"}