@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.
- package/dist/auto-ui.js +1 -1
- package/dist/auto-ui.js.map +1 -1
- package/dist/base.d.ts +1 -1
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +2 -2
- package/dist/base.js.map +1 -1
- package/dist/cli-ui-renderer.js +1 -1
- package/dist/cli-ui-renderer.js.map +1 -1
- package/dist/design-system/index.d.ts +21 -0
- package/dist/design-system/index.d.ts.map +1 -0
- package/dist/design-system/index.js +27 -0
- package/dist/design-system/index.js.map +1 -0
- package/dist/design-system/tokens.d.ts +149 -0
- package/dist/design-system/tokens.d.ts.map +1 -0
- package/dist/design-system/tokens.js +413 -0
- package/dist/design-system/tokens.js.map +1 -0
- package/dist/design-system/transaction-ui.d.ts +70 -0
- package/dist/design-system/transaction-ui.d.ts.map +1 -0
- package/dist/design-system/transaction-ui.js +982 -0
- package/dist/design-system/transaction-ui.js.map +1 -0
- package/dist/generator.d.ts +56 -6
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js.map +1 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -56
- package/dist/index.js.map +1 -1
- package/dist/io.d.ts +103 -2
- package/dist/io.d.ts.map +1 -1
- package/dist/io.js +37 -1
- package/dist/io.js.map +1 -1
- package/dist/rendering/components.d.ts +29 -0
- package/dist/rendering/components.d.ts.map +1 -0
- package/dist/rendering/components.js +773 -0
- package/dist/rendering/components.js.map +1 -0
- package/dist/rendering/field-analyzer.d.ts +48 -0
- package/dist/rendering/field-analyzer.d.ts.map +1 -0
- package/dist/rendering/field-analyzer.js +270 -0
- package/dist/rendering/field-analyzer.js.map +1 -0
- package/dist/rendering/field-renderers.d.ts +64 -0
- package/dist/rendering/field-renderers.d.ts.map +1 -0
- package/dist/rendering/field-renderers.js +317 -0
- package/dist/rendering/field-renderers.js.map +1 -0
- package/dist/rendering/index.d.ts +28 -0
- package/dist/rendering/index.d.ts.map +1 -0
- package/dist/rendering/index.js +60 -0
- package/dist/rendering/index.js.map +1 -0
- package/dist/rendering/layout-selector.d.ts +48 -0
- package/dist/rendering/layout-selector.d.ts.map +1 -0
- package/dist/rendering/layout-selector.js +347 -0
- package/dist/rendering/layout-selector.js.map +1 -0
- package/dist/rendering/template-engine.d.ts +41 -0
- package/dist/rendering/template-engine.d.ts.map +1 -0
- package/dist/rendering/template-engine.js +236 -0
- package/dist/rendering/template-engine.js.map +1 -0
- package/dist/schema-extractor.d.ts +30 -0
- package/dist/schema-extractor.d.ts.map +1 -1
- package/dist/schema-extractor.js +205 -12
- package/dist/schema-extractor.js.map +1 -1
- package/dist/stateful.js +1 -1
- package/dist/stateful.js.map +1 -1
- package/dist/types.d.ts +9 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/ucp/ap2/handlers.d.ts +242 -0
- package/dist/ucp/ap2/handlers.d.ts.map +1 -0
- package/dist/ucp/ap2/handlers.js +482 -0
- package/dist/ucp/ap2/handlers.js.map +1 -0
- package/dist/ucp/ap2/mandates.d.ts +95 -0
- package/dist/ucp/ap2/mandates.d.ts.map +1 -0
- package/dist/ucp/ap2/mandates.js +234 -0
- package/dist/ucp/ap2/mandates.js.map +1 -0
- package/dist/ucp/ap2/types.d.ts +305 -0
- package/dist/ucp/ap2/types.d.ts.map +1 -0
- package/dist/ucp/ap2/types.js +8 -0
- package/dist/ucp/ap2/types.js.map +1 -0
- package/dist/ucp/capabilities/checkout.d.ts +118 -0
- package/dist/ucp/capabilities/checkout.d.ts.map +1 -0
- package/dist/ucp/capabilities/checkout.js +344 -0
- package/dist/ucp/capabilities/checkout.js.map +1 -0
- package/dist/ucp/capabilities/identity.d.ts +130 -0
- package/dist/ucp/capabilities/identity.d.ts.map +1 -0
- package/dist/ucp/capabilities/identity.js +290 -0
- package/dist/ucp/capabilities/identity.js.map +1 -0
- package/dist/ucp/capabilities/order.d.ts +142 -0
- package/dist/ucp/capabilities/order.d.ts.map +1 -0
- package/dist/ucp/capabilities/order.js +383 -0
- package/dist/ucp/capabilities/order.js.map +1 -0
- package/dist/ucp/index.d.ts +18 -0
- package/dist/ucp/index.d.ts.map +1 -0
- package/dist/ucp/index.js +19 -0
- package/dist/ucp/index.js.map +1 -0
- package/dist/ucp/manifest.d.ts +62 -0
- package/dist/ucp/manifest.d.ts.map +1 -0
- package/dist/ucp/manifest.js +180 -0
- package/dist/ucp/manifest.js.map +1 -0
- package/dist/ucp/types.d.ts +327 -0
- package/dist/ucp/types.d.ts.map +1 -0
- package/dist/ucp/types.js +8 -0
- package/dist/ucp/types.js.map +1 -0
- package/package.json +3 -4
- package/src/auto-ui.ts +1 -1
- package/src/base.ts +2 -2
- package/src/cli-ui-renderer.ts +1 -1
- package/src/design-system/index.ts +30 -0
- package/src/design-system/tokens.ts +451 -0
- package/src/design-system/transaction-ui.ts +1038 -0
- package/src/generator.ts +58 -2
- package/src/index.ts +135 -124
- package/src/io.ts +108 -3
- package/src/rendering/components.ts +785 -0
- package/src/rendering/field-analyzer.ts +299 -0
- package/src/rendering/field-renderers.ts +356 -0
- package/src/rendering/index.ts +63 -0
- package/src/rendering/layout-selector.ts +390 -0
- package/src/rendering/template-engine.ts +254 -0
- package/src/schema-extractor.ts +225 -12
- package/src/stateful.ts +1 -1
- package/src/types.ts +10 -1
- package/src/ucp/ap2/handlers.ts +779 -0
- package/src/ucp/ap2/mandates.ts +354 -0
- package/src/ucp/ap2/types.ts +441 -0
- package/src/ucp/capabilities/checkout.ts +497 -0
- package/src/ucp/capabilities/identity.ts +425 -0
- package/src/ucp/capabilities/order.ts +549 -0
- package/src/ucp/index.ts +27 -0
- package/src/ucp/manifest.ts +257 -0
- package/src/ucp/types.ts +454 -0
- package/dist/cli-formatter.d.ts +0 -92
- package/dist/cli-formatter.d.ts.map +0 -1
- package/dist/cli-formatter.js +0 -486
- package/dist/cli-formatter.js.map +0 -1
- package/dist/context.d.ts +0 -6
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -3
- package/dist/context.js.map +0 -1
- package/dist/elicit.d.ts +0 -93
- package/dist/elicit.d.ts.map +0 -1
- package/dist/elicit.js +0 -373
- package/dist/elicit.js.map +0 -1
- package/dist/mcp-client.d.ts +0 -218
- package/dist/mcp-client.d.ts.map +0 -1
- package/dist/mcp-client.js +0 -424
- package/dist/mcp-client.js.map +0 -1
- package/dist/mcp-sdk-transport.d.ts +0 -88
- package/dist/mcp-sdk-transport.d.ts.map +0 -1
- package/dist/mcp-sdk-transport.js +0 -360
- package/dist/mcp-sdk-transport.js.map +0 -1
- package/dist/photon-config.d.ts +0 -86
- package/dist/photon-config.d.ts.map +0 -1
- package/dist/photon-config.js +0 -156
- package/dist/photon-config.js.map +0 -1
- package/dist/progress.d.ts +0 -93
- package/dist/progress.d.ts.map +0 -1
- package/dist/progress.js +0 -195
- package/dist/progress.js.map +0 -1
- package/src/cli-formatter.ts +0 -579
- package/src/context.ts +0 -7
- package/src/elicit.ts +0 -438
- package/src/mcp-client.ts +0 -561
- package/src/mcp-sdk-transport.ts +0 -449
- package/src/photon-config.ts +0 -201
- 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"}
|