@portel/photon 1.18.0 → 1.19.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 (172) hide show
  1. package/dist/auto-ui/beam.d.ts.map +1 -1
  2. package/dist/auto-ui/beam.js +14 -4
  3. package/dist/auto-ui/beam.js.map +1 -1
  4. package/dist/beam-form.bundle.js +5 -3
  5. package/dist/beam-form.bundle.js.map +2 -2
  6. package/dist/beam.bundle.js +686 -30
  7. package/dist/beam.bundle.js.map +3 -3
  8. package/dist/claude-code-plugin.js +1 -1
  9. package/dist/cli/commands/beam.d.ts.map +1 -1
  10. package/dist/cli/commands/beam.js +8 -2
  11. package/dist/cli/commands/beam.js.map +1 -1
  12. package/dist/cli/commands/changelog.d.ts +9 -0
  13. package/dist/cli/commands/changelog.d.ts.map +1 -0
  14. package/dist/cli/commands/changelog.js +133 -0
  15. package/dist/cli/commands/changelog.js.map +1 -0
  16. package/dist/cli/commands/maker.d.ts.map +1 -1
  17. package/dist/cli/commands/maker.js +23 -2
  18. package/dist/cli/commands/maker.js.map +1 -1
  19. package/dist/cli/commands/mcp.d.ts.map +1 -1
  20. package/dist/cli/commands/mcp.js +53 -0
  21. package/dist/cli/commands/mcp.js.map +1 -1
  22. package/dist/cli/commands/package.d.ts.map +1 -1
  23. package/dist/cli/commands/package.js +18 -2
  24. package/dist/cli/commands/package.js.map +1 -1
  25. package/dist/cli/commands/run.d.ts.map +1 -1
  26. package/dist/cli/commands/run.js +1 -0
  27. package/dist/cli/commands/run.js.map +1 -1
  28. package/dist/cli/commands/update.d.ts +3 -2
  29. package/dist/cli/commands/update.d.ts.map +1 -1
  30. package/dist/cli/commands/update.js +50 -43
  31. package/dist/cli/commands/update.js.map +1 -1
  32. package/dist/cli/index.d.ts.map +1 -1
  33. package/dist/cli/index.js +16 -2
  34. package/dist/cli/index.js.map +1 -1
  35. package/dist/cli-alias.js +1 -1
  36. package/dist/cli-alias.js.map +1 -1
  37. package/dist/context-store.d.ts +23 -33
  38. package/dist/context-store.d.ts.map +1 -1
  39. package/dist/context-store.js +147 -97
  40. package/dist/context-store.js.map +1 -1
  41. package/dist/context.d.ts +15 -10
  42. package/dist/context.d.ts.map +1 -1
  43. package/dist/context.js +37 -13
  44. package/dist/context.js.map +1 -1
  45. package/dist/daemon/server.js +4 -2
  46. package/dist/daemon/server.js.map +1 -1
  47. package/dist/data-migration.d.ts +27 -0
  48. package/dist/data-migration.d.ts.map +1 -0
  49. package/dist/data-migration.js +307 -0
  50. package/dist/data-migration.js.map +1 -0
  51. package/dist/editor-support/docblock-tag-catalog.d.ts.map +1 -1
  52. package/dist/editor-support/docblock-tag-catalog.js +6 -0
  53. package/dist/editor-support/docblock-tag-catalog.js.map +1 -1
  54. package/dist/loader.d.ts +10 -0
  55. package/dist/loader.d.ts.map +1 -1
  56. package/dist/loader.js +97 -12
  57. package/dist/loader.js.map +1 -1
  58. package/dist/marketplace-manager.d.ts.map +1 -1
  59. package/dist/marketplace-manager.js +25 -5
  60. package/dist/marketplace-manager.js.map +1 -1
  61. package/dist/photon-cli-runner.d.ts.map +1 -1
  62. package/dist/photon-cli-runner.js +47 -21
  63. package/dist/photon-cli-runner.js.map +1 -1
  64. package/dist/photon-doc-extractor.d.ts +1 -0
  65. package/dist/photon-doc-extractor.d.ts.map +1 -1
  66. package/dist/photon-doc-extractor.js +6 -0
  67. package/dist/photon-doc-extractor.js.map +1 -1
  68. package/dist/readme-syncer.d.ts.map +1 -1
  69. package/dist/readme-syncer.js +6 -1
  70. package/dist/readme-syncer.js.map +1 -1
  71. package/dist/server.d.ts +40 -0
  72. package/dist/server.d.ts.map +1 -1
  73. package/dist/server.js +143 -28
  74. package/dist/server.js.map +1 -1
  75. package/dist/shared/audit.js +4 -4
  76. package/dist/shared/audit.js.map +1 -1
  77. package/dist/tasks/store.d.ts.map +1 -1
  78. package/dist/tasks/store.js +6 -2
  79. package/dist/tasks/store.js.map +1 -1
  80. package/dist/version-notify.d.ts +27 -0
  81. package/dist/version-notify.d.ts.map +1 -0
  82. package/dist/version-notify.js +142 -0
  83. package/dist/version-notify.js.map +1 -0
  84. package/package.json +2 -2
  85. package/dist/auto-ui/bridge/openai-shim.d.ts +0 -20
  86. package/dist/auto-ui/bridge/openai-shim.d.ts.map +0 -1
  87. package/dist/auto-ui/bridge/openai-shim.js +0 -231
  88. package/dist/auto-ui/bridge/openai-shim.js.map +0 -1
  89. package/dist/auto-ui/bridge/photon-app.d.ts +0 -162
  90. package/dist/auto-ui/bridge/photon-app.d.ts.map +0 -1
  91. package/dist/auto-ui/bridge/photon-app.js +0 -460
  92. package/dist/auto-ui/bridge/photon-app.js.map +0 -1
  93. package/dist/auto-ui/daemon-tools.d.ts +0 -45
  94. package/dist/auto-ui/daemon-tools.d.ts.map +0 -1
  95. package/dist/auto-ui/daemon-tools.js +0 -581
  96. package/dist/auto-ui/daemon-tools.js.map +0 -1
  97. package/dist/auto-ui/design-system/index.d.ts +0 -21
  98. package/dist/auto-ui/design-system/index.d.ts.map +0 -1
  99. package/dist/auto-ui/design-system/index.js +0 -27
  100. package/dist/auto-ui/design-system/index.js.map +0 -1
  101. package/dist/auto-ui/design-system/transaction-ui.d.ts +0 -70
  102. package/dist/auto-ui/design-system/transaction-ui.d.ts.map +0 -1
  103. package/dist/auto-ui/design-system/transaction-ui.js +0 -982
  104. package/dist/auto-ui/design-system/transaction-ui.js.map +0 -1
  105. package/dist/auto-ui/playground-server.d.ts +0 -7
  106. package/dist/auto-ui/playground-server.d.ts.map +0 -1
  107. package/dist/auto-ui/playground-server.js +0 -840
  108. package/dist/auto-ui/playground-server.js.map +0 -1
  109. package/dist/auto-ui/rendering/components.d.ts +0 -29
  110. package/dist/auto-ui/rendering/components.d.ts.map +0 -1
  111. package/dist/auto-ui/rendering/components.js +0 -1341
  112. package/dist/auto-ui/rendering/components.js.map +0 -1
  113. package/dist/auto-ui/rendering/field-analyzer.d.ts +0 -104
  114. package/dist/auto-ui/rendering/field-analyzer.d.ts.map +0 -1
  115. package/dist/auto-ui/rendering/field-analyzer.js +0 -447
  116. package/dist/auto-ui/rendering/field-analyzer.js.map +0 -1
  117. package/dist/auto-ui/rendering/field-renderers.d.ts +0 -64
  118. package/dist/auto-ui/rendering/field-renderers.d.ts.map +0 -1
  119. package/dist/auto-ui/rendering/field-renderers.js +0 -317
  120. package/dist/auto-ui/rendering/field-renderers.js.map +0 -1
  121. package/dist/auto-ui/rendering/index.d.ts +0 -28
  122. package/dist/auto-ui/rendering/index.d.ts.map +0 -1
  123. package/dist/auto-ui/rendering/index.js +0 -60
  124. package/dist/auto-ui/rendering/index.js.map +0 -1
  125. package/dist/auto-ui/rendering/layout-selector.d.ts +0 -60
  126. package/dist/auto-ui/rendering/layout-selector.d.ts.map +0 -1
  127. package/dist/auto-ui/rendering/layout-selector.js +0 -476
  128. package/dist/auto-ui/rendering/layout-selector.js.map +0 -1
  129. package/dist/markdown-utils.d.ts +0 -8
  130. package/dist/markdown-utils.d.ts.map +0 -1
  131. package/dist/markdown-utils.js +0 -64
  132. package/dist/markdown-utils.js.map +0 -1
  133. package/dist/mcp-client.d.ts +0 -9
  134. package/dist/mcp-client.d.ts.map +0 -1
  135. package/dist/mcp-client.js +0 -11
  136. package/dist/mcp-client.js.map +0 -1
  137. package/dist/mcp-elicitation.d.ts +0 -32
  138. package/dist/mcp-elicitation.d.ts.map +0 -1
  139. package/dist/mcp-elicitation.js +0 -26
  140. package/dist/mcp-elicitation.js.map +0 -1
  141. package/dist/photons/builder-compass.photon.d.ts +0 -167
  142. package/dist/photons/builder-compass.photon.d.ts.map +0 -1
  143. package/dist/photons/builder-compass.photon.js +0 -816
  144. package/dist/photons/builder-compass.photon.js.map +0 -1
  145. package/dist/photons/builder-compass.photon.ts +0 -1129
  146. package/dist/photons/docs/ui/docs.html +0 -441
  147. package/dist/photons/docs.photon.d.ts +0 -237
  148. package/dist/photons/docs.photon.d.ts.map +0 -1
  149. package/dist/photons/docs.photon.js +0 -483
  150. package/dist/photons/docs.photon.js.map +0 -1
  151. package/dist/photons/docs.photon.ts +0 -536
  152. package/dist/photons/slides.photon.d.ts +0 -212
  153. package/dist/photons/slides.photon.d.ts.map +0 -1
  154. package/dist/photons/slides.photon.js +0 -355
  155. package/dist/photons/slides.photon.js.map +0 -1
  156. package/dist/photons/slides.photon.ts +0 -370
  157. package/dist/photons/spreadsheet/ui/spreadsheet.html +0 -779
  158. package/dist/photons/spreadsheet.photon.d.ts +0 -554
  159. package/dist/photons/spreadsheet.photon.d.ts.map +0 -1
  160. package/dist/photons/spreadsheet.photon.js +0 -1050
  161. package/dist/photons/spreadsheet.photon.js.map +0 -1
  162. package/dist/photons/spreadsheet.photon.ts +0 -1239
  163. package/dist/photons/ui/builder-compass.html +0 -1199
  164. package/dist/photons/ui/builder-compass.photon.html +0 -380
  165. package/dist/security-scanner.d.ts +0 -52
  166. package/dist/security-scanner.d.ts.map +0 -1
  167. package/dist/security-scanner.js +0 -181
  168. package/dist/security-scanner.js.map +0 -1
  169. package/dist/shared/performance.d.ts +0 -65
  170. package/dist/shared/performance.d.ts.map +0 -1
  171. package/dist/shared/performance.js +0 -136
  172. package/dist/shared/performance.js.map +0 -1
@@ -1,840 +0,0 @@
1
- /**
2
- * Multi-Photon Playground Server
3
- *
4
- * Serves an interactive UI for testing all installed photons
5
- */
6
- import * as http from 'http';
7
- import { listPhotonMCPs, resolvePhotonPath } from '../path-resolver.js';
8
- import { PhotonLoader } from '../loader.js';
9
- import { logger } from '../shared/logger.js';
10
- import { SchemaExtractor, executeGenerator, isAsyncGenerator, } from '@portel/photon-core';
11
- export async function startPlaygroundServer(workingDir, port) {
12
- // Discover all photons
13
- const photonList = await listPhotonMCPs(workingDir);
14
- if (photonList.length === 0) {
15
- logger.warn('No photons found in ' + workingDir);
16
- console.log('\nCreate a photon with: photon maker new <name>');
17
- process.exit(1);
18
- }
19
- // Extract metadata for all photons (use PhotonLoader for proper dependency handling)
20
- const photons = [];
21
- const loader = new PhotonLoader(false, logger);
22
- for (const name of photonList) {
23
- const photonPath = await resolvePhotonPath(name, workingDir);
24
- if (!photonPath)
25
- continue;
26
- try {
27
- // Load photon using PhotonLoader (handles deps, TypeScript, injections)
28
- const mcp = await loader.loadFile(photonPath);
29
- if (!mcp.instance) {
30
- logger.warn(`Failed to get instance for ${name}`);
31
- continue;
32
- }
33
- // Extract schema for UI
34
- const extractor = new SchemaExtractor();
35
- const schemas = await extractor.extractFromFile(photonPath);
36
- // Filter out lifecycle methods (onInitialize, etc)
37
- const lifecycleMethods = ['onInitialize', 'onShutdown', 'constructor'];
38
- const methods = schemas
39
- .filter((schema) => !lifecycleMethods.includes(schema.name))
40
- .map((schema) => ({
41
- name: schema.name,
42
- description: schema.description || '',
43
- params: schema.inputSchema || { type: 'object', properties: {}, required: [] },
44
- returns: { type: 'object' },
45
- }));
46
- photons.push({
47
- name,
48
- path: photonPath,
49
- methods,
50
- });
51
- }
52
- catch (error) {
53
- logger.warn(`Failed to load ${name}: ${error}`);
54
- }
55
- }
56
- // Create HTTP server
57
- const server = http.createServer(async (req, res) => {
58
- const url = new URL(req.url || '/', `http://${req.headers.host}`);
59
- // Serve playground HTML
60
- if (url.pathname === '/' || url.pathname === '/index.html') {
61
- res.writeHead(200, { 'Content-Type': 'text/html' });
62
- res.end(generatePlaygroundHTML(photons, port));
63
- return;
64
- }
65
- // API: List photons
66
- if (url.pathname === '/api/photons') {
67
- res.writeHead(200, { 'Content-Type': 'application/json' });
68
- res.end(JSON.stringify(photons));
69
- return;
70
- }
71
- // API: Invoke method
72
- if (url.pathname === '/api/invoke' && req.method === 'POST') {
73
- let body = '';
74
- req.on('data', (chunk) => (body += chunk));
75
- req.on('end', async () => {
76
- try {
77
- const { photon, method, params } = JSON.parse(body);
78
- // Find photon path
79
- const photonPath = await resolvePhotonPath(photon, workingDir);
80
- if (!photonPath) {
81
- res.writeHead(404, { 'Content-Type': 'application/json' });
82
- res.end(JSON.stringify({ error: 'Photon not found' }));
83
- return;
84
- }
85
- // Load and invoke directly
86
- const { PhotonLoader } = await import('../loader.js');
87
- const loader = new PhotonLoader(false, logger);
88
- const mcp = await loader.loadFile(photonPath);
89
- // Get the instance
90
- const instance = mcp.instance;
91
- if (!instance) {
92
- throw new Error('Failed to load photon instance');
93
- }
94
- // Invoke the method
95
- if (typeof instance[method] !== 'function') {
96
- throw new Error(`Method ${method} not found`);
97
- }
98
- // Call method with params object - photon methods expect a single params object
99
- const methodResult = instance[method](params);
100
- // Check if it's a generator - use SSE for streaming
101
- let result;
102
- if (isAsyncGenerator(methodResult)) {
103
- // Setup SSE for streaming progress
104
- res.writeHead(200, {
105
- 'Content-Type': 'text/event-stream',
106
- 'Cache-Control': 'no-cache',
107
- Connection: 'keep-alive',
108
- });
109
- try {
110
- result = await executeGenerator(methodResult, {
111
- inputProvider: async (ask) => {
112
- // Send ask event to client
113
- res.write(`data: ${JSON.stringify({ type: 'ask', data: ask })}\n\n`);
114
- // For now, throw error - playground doesn't support interactive input yet
115
- throw new Error(`Interactive input not supported in playground: ${ask.message}`);
116
- },
117
- outputHandler: async (emit) => {
118
- // Stream progress updates to client
119
- res.write(`data: ${JSON.stringify({ type: 'progress', data: emit })}\n\n`);
120
- },
121
- });
122
- // Send final result
123
- res.write(`data: ${JSON.stringify({ type: 'result', data: result })}\n\n`);
124
- res.write('data: [DONE]\n\n');
125
- res.end();
126
- }
127
- catch (error) {
128
- res.write(`data: ${JSON.stringify({ type: 'error', data: { message: error.message } })}\n\n`);
129
- res.end();
130
- }
131
- }
132
- else {
133
- // Non-generator: regular JSON response
134
- if (methodResult && typeof methodResult.then === 'function') {
135
- result = await methodResult;
136
- }
137
- else {
138
- result = methodResult;
139
- }
140
- logger.info(`Sending result to client:`, result);
141
- res.writeHead(200, { 'Content-Type': 'application/json' });
142
- res.end(JSON.stringify({ result }));
143
- }
144
- }
145
- catch (error) {
146
- res.writeHead(500, { 'Content-Type': 'application/json' });
147
- res.end(JSON.stringify({ error: error.message }));
148
- }
149
- });
150
- return;
151
- }
152
- // 404
153
- res.writeHead(404);
154
- res.end('Not Found');
155
- });
156
- server.listen(port, () => {
157
- console.log(`\n🎮 Photon Playground running at http://localhost:${port}`);
158
- console.log(`📦 ${photons.length} photon(s) loaded`);
159
- console.log(`\nPress Ctrl+C to stop\n`);
160
- });
161
- }
162
- function generatePlaygroundHTML(photons, port) {
163
- return `<!DOCTYPE html>
164
- <html lang="en">
165
- <head>
166
- <meta charset="UTF-8">
167
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
168
- <title>Photon Playground</title>
169
- <style>
170
- * {
171
- margin: 0;
172
- padding: 0;
173
- box-sizing: border-box;
174
- }
175
-
176
- body {
177
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', 'Helvetica Neue', Arial, sans-serif;
178
- display: flex;
179
- height: 100vh;
180
- background: #f5f5f5;
181
- line-height: 1.6;
182
- }
183
-
184
- .sidebar {
185
- width: 300px;
186
- background: #2c3e50;
187
- color: white;
188
- overflow-y: auto;
189
- border-right: 1px solid #34495e;
190
- }
191
-
192
- .sidebar-header {
193
- padding: 20px;
194
- background: #1a252f;
195
- border-bottom: 1px solid #34495e;
196
- }
197
-
198
- .sidebar-header h1 {
199
- font-size: 20px;
200
- margin-bottom: 5px;
201
- }
202
-
203
- .sidebar-header p {
204
- font-size: 12px;
205
- color: #95a5a6;
206
- }
207
-
208
- .photon-item {
209
- border-bottom: 1px solid #34495e;
210
- }
211
-
212
- .photon-header {
213
- padding: 15px 20px;
214
- cursor: pointer;
215
- display: flex;
216
- align-items: center;
217
- justify-content: space-between;
218
- transition: background 0.2s;
219
- }
220
-
221
- .photon-header:hover {
222
- background: #34495e;
223
- }
224
-
225
- .photon-header.active {
226
- background: #34495e;
227
- }
228
-
229
- .photon-name {
230
- font-weight: 600;
231
- font-size: 14px;
232
- }
233
-
234
- .method-count {
235
- font-size: 12px;
236
- color: #95a5a6;
237
- }
238
-
239
- .method-list {
240
- display: none;
241
- background: #1a252f;
242
- }
243
-
244
- .method-list.expanded {
245
- display: block;
246
- }
247
-
248
- .method-item {
249
- padding: 10px 20px 10px 40px;
250
- cursor: pointer;
251
- font-size: 13px;
252
- transition: background 0.2s;
253
- border-left: 3px solid transparent;
254
- }
255
-
256
- .method-item:hover {
257
- background: #2c3e50;
258
- }
259
-
260
- .method-item.active {
261
- background: #2c3e50;
262
- border-left-color: #3498db;
263
- }
264
-
265
- .main-content {
266
- flex: 1;
267
- display: flex;
268
- flex-direction: column;
269
- overflow: hidden;
270
- }
271
-
272
- .content-header {
273
- padding: 20px 30px;
274
- background: white;
275
- border-bottom: 1px solid #e0e0e0;
276
- }
277
-
278
- .content-header h2 {
279
- font-size: 24px;
280
- margin-bottom: 5px;
281
- }
282
-
283
- .content-header p {
284
- color: #666;
285
- font-size: 14px;
286
- }
287
-
288
- .content-body {
289
- flex: 1;
290
- overflow-y: auto;
291
- padding: 30px;
292
- }
293
-
294
- .tabs {
295
- display: flex;
296
- gap: 10px;
297
- margin-bottom: 20px;
298
- border-bottom: 2px solid #e0e0e0;
299
- }
300
-
301
- .tab {
302
- padding: 10px 20px;
303
- cursor: pointer;
304
- border: none;
305
- background: none;
306
- font-size: 14px;
307
- font-weight: 500;
308
- color: #666;
309
- border-bottom: 2px solid transparent;
310
- margin-bottom: -2px;
311
- transition: all 0.2s;
312
- }
313
-
314
- .tab:hover {
315
- color: #3498db;
316
- }
317
-
318
- .tab.active {
319
- color: #3498db;
320
- border-bottom-color: #3498db;
321
- }
322
-
323
- .tab-content {
324
- display: none;
325
- }
326
-
327
- .tab-content.active {
328
- display: block;
329
- }
330
-
331
- .form-group {
332
- margin-bottom: 20px;
333
- }
334
-
335
- .form-group label {
336
- display: block;
337
- margin-bottom: 8px;
338
- font-weight: 500;
339
- font-size: 14px;
340
- color: #333;
341
- }
342
-
343
- .form-group input,
344
- .form-group textarea {
345
- width: 100%;
346
- padding: 10px;
347
- border: 1px solid #ddd;
348
- border-radius: 4px;
349
- font-family: inherit;
350
- font-size: 14px;
351
- }
352
-
353
- .form-group textarea {
354
- min-height: 100px;
355
- font-family: 'Monaco', 'Menlo', monospace;
356
- }
357
-
358
- .btn {
359
- padding: 10px 20px;
360
- border: none;
361
- border-radius: 4px;
362
- font-size: 14px;
363
- font-weight: 500;
364
- cursor: pointer;
365
- transition: all 0.2s;
366
- }
367
-
368
- .btn-primary {
369
- background: #3498db;
370
- color: white;
371
- }
372
-
373
- .btn-primary:hover {
374
- background: #2980b9;
375
- }
376
-
377
- .btn-primary:disabled {
378
- background: #95a5a6;
379
- cursor: not-allowed;
380
- }
381
-
382
- .result-container {
383
- margin-top: 20px;
384
- padding: 20px;
385
- background: #f8f9fa;
386
- border-radius: 4px;
387
- border: 1px solid #e0e0e0;
388
- }
389
-
390
- .result-container h3 {
391
- font-size: 16px;
392
- margin-bottom: 10px;
393
- }
394
-
395
- .result-content {
396
- background: white;
397
- padding: 15px;
398
- border-radius: 4px;
399
- font-family: inherit;
400
- font-size: 14px;
401
- white-space: normal;
402
- word-break: break-word;
403
- max-height: 400px;
404
- overflow-y: auto;
405
- }
406
-
407
- .progress-container {
408
- display: flex;
409
- align-items: center;
410
- padding: 20px;
411
- background: white;
412
- border-radius: 4px;
413
- color: #3498db;
414
- font-size: 14px;
415
- }
416
-
417
- .spinner {
418
- display: inline-block;
419
- width: 16px;
420
- height: 16px;
421
- border: 2px solid #f3f3f3;
422
- border-top: 2px solid #3498db;
423
- border-radius: 50%;
424
- animation: spin 1s linear infinite;
425
- margin-right: 10px;
426
- }
427
-
428
- @keyframes spin {
429
- 0% { transform: rotate(0deg); }
430
- 100% { transform: rotate(360deg); }
431
- }
432
-
433
- .empty-state {
434
- text-align: center;
435
- padding: 60px 20px;
436
- color: #666;
437
- }
438
-
439
- .empty-state h3 {
440
- font-size: 20px;
441
- margin-bottom: 10px;
442
- }
443
- </style>
444
- </head>
445
- <body>
446
- <div class="sidebar">
447
- <div class="sidebar-header">
448
- <h1>🎮 Photon Playground</h1>
449
- <p>${photons.length} photon(s) available</p>
450
- </div>
451
- <div id="photon-tree"></div>
452
- </div>
453
-
454
- <div class="main-content">
455
- <div class="content-header">
456
- <h2 id="method-title">Select a method</h2>
457
- <p id="method-description"></p>
458
- </div>
459
- <div class="content-body">
460
- <div id="empty-state" class="empty-state">
461
- <h3>👈 Select a method to get started</h3>
462
- <p>Choose a photon and method from the sidebar</p>
463
- </div>
464
- <div id="method-interface" style="display: none;">
465
- <div class="tabs">
466
- <button class="tab active" data-tab="ui">UI</button>
467
- <button class="tab" data-tab="data">Data</button>
468
- </div>
469
-
470
- <div class="tab-content active" data-content="ui">
471
- <form id="method-form">
472
- <div id="form-fields"></div>
473
- <button type="submit" class="btn btn-primary" id="invoke-btn">
474
- <span id="invoke-label">Invoke</span>
475
- </button>
476
- </form>
477
- <div id="result-ui" style="display: none;"></div>
478
- </div>
479
-
480
- <div class="tab-content" data-content="data">
481
- <div class="result-container">
482
- <h3>Response Data</h3>
483
- <div class="result-content" id="result-data">No data yet</div>
484
- </div>
485
- </div>
486
- </div>
487
- </div>
488
- </div>
489
-
490
- <script>
491
- const photons = ${JSON.stringify(photons)};
492
- let currentPhoton = null;
493
- let currentMethod = null;
494
- let currentResult = null;
495
-
496
- // Markdown helpers for rendering doc blocks
497
- const markdownLinkRegex = /\\[([^\\]]+)\\]\\(([^)]+)\\)/g;
498
- const markdownBoldRegex = /\\*\\*([^*]+)\\*\\*/g;
499
-
500
- // Render photon tree
501
- function renderTree() {
502
- const tree = document.getElementById('photon-tree');
503
- tree.innerHTML = photons.map(photon => \`
504
- <div class="photon-item">
505
- <div class="photon-header" onclick="togglePhoton('\${photon.name}')">
506
- <span class="photon-name">\${photon.name}</span>
507
- <span class="method-count">\${photon.methods.length}</span>
508
- </div>
509
- <div class="method-list" id="methods-\${photon.name}">
510
- \${photon.methods.map(method => \`
511
- <div class="method-item" onclick="selectMethod('\${photon.name}', '\${method.name}')">
512
- \${method.name}
513
- </div>
514
- \`).join('')}
515
- </div>
516
- </div>
517
- \`).join('');
518
- }
519
-
520
- function togglePhoton(name) {
521
- const methods = document.getElementById(\`methods-\${name}\`);
522
- const header = methods.previousElementSibling;
523
- methods.classList.toggle('expanded');
524
- header.classList.toggle('active');
525
- }
526
-
527
- function selectMethod(photonName, methodName) {
528
- currentPhoton = photons.find(p => p.name === photonName);
529
- currentMethod = currentPhoton.methods.find(m => m.name === methodName);
530
- currentResult = null;
531
-
532
- // Update UI
533
- document.querySelectorAll('.method-item').forEach(el => el.classList.remove('active'));
534
- event.target.classList.add('active');
535
-
536
- document.getElementById('method-title').textContent = \`\${photonName}.\${methodName}()\`;
537
- document.getElementById('method-description').textContent = currentMethod.description;
538
- document.getElementById('empty-state').style.display = 'none';
539
- document.getElementById('method-interface').style.display = 'block';
540
-
541
- renderForm();
542
- }
543
-
544
- function renderForm() {
545
- const fields = document.getElementById('form-fields');
546
- const properties = currentMethod.params?.properties || {};
547
- const required = currentMethod.params?.required || [];
548
-
549
- // Update button label with method name (capitalize first letter)
550
- const invokeLabel = document.getElementById('invoke-label');
551
- invokeLabel.textContent = currentMethod.name.charAt(0).toUpperCase() + currentMethod.name.slice(1);
552
-
553
- const fieldEntries = Object.entries(properties);
554
-
555
- if (fieldEntries.length === 0) {
556
- fields.innerHTML = '<p style="color: #666; font-size: 14px;">No parameters required</p>';
557
- } else {
558
- fields.innerHTML = fieldEntries.map(([name, schema]) => {
559
- const isRequired = required.includes(name);
560
- const type = schema.type === 'number' ? 'number' : 'text';
561
-
562
- return \`
563
- <div class="form-group">
564
- <label>
565
- \${name}
566
- \${isRequired ? '<span style="color: red;">*</span>' : ''}
567
- \${schema.description ? '<span style="color: #666; font-weight: normal; font-size: 12px;"> - ' + schema.description + '</span>' : ''}
568
- </label>
569
- <input
570
- type="\${type}"
571
- name="\${name}"
572
- placeholder="Enter \${name}"
573
- \${isRequired ? 'required' : ''}
574
- />
575
- </div>
576
- \`;
577
- }).join('');
578
- }
579
-
580
- document.getElementById('result-ui').style.display = 'none';
581
- document.getElementById('result-data').textContent = 'No data yet';
582
- }
583
-
584
- // Handle form submission
585
- document.getElementById('method-form').addEventListener('submit', async (e) => {
586
- e.preventDefault();
587
-
588
- const formData = new FormData(e.target);
589
- const params = {};
590
- for (const [key, value] of formData.entries()) {
591
- const schema = currentMethod.params?.properties?.[key];
592
- if (schema?.type === 'number') {
593
- params[key] = Number(value);
594
- } else {
595
- params[key] = value;
596
- }
597
- }
598
-
599
- const btn = document.getElementById('invoke-btn');
600
- btn.disabled = true;
601
- btn.innerHTML = '<span class="spinner"></span> Invoking...';
602
-
603
- // Show progress area
604
- const resultUI = document.getElementById('result-ui');
605
- resultUI.style.display = 'block';
606
- resultUI.innerHTML = '<div class="progress-container"><div class="spinner"></div> <span id="progress-text">Starting...</span></div>';
607
-
608
- try {
609
- const response = await fetch('/api/invoke', {
610
- method: 'POST',
611
- headers: { 'Content-Type': 'application/json' },
612
- body: JSON.stringify({
613
- photon: currentPhoton.name,
614
- method: currentMethod.name,
615
- params
616
- }),
617
- signal: AbortSignal.timeout(120000), // 2min for method calls
618
- });
619
-
620
- // Check if it's SSE (for generators)
621
- const contentType = response.headers.get('content-type');
622
- if (contentType && contentType.includes('text/event-stream')) {
623
- // Handle SSE streaming
624
- const reader = response.body.getReader();
625
- const decoder = new TextDecoder();
626
- let buffer = '';
627
-
628
- while (true) {
629
- const { done, value } = await reader.read();
630
- if (done) break;
631
-
632
- buffer += decoder.decode(value, { stream: true });
633
- const lines = buffer.split('\\n\\n');
634
- buffer = lines.pop() || '';
635
-
636
- for (const line of lines) {
637
- if (!line.startsWith('data: ')) continue;
638
- const data = line.slice(6);
639
- if (data === '[DONE]') break;
640
-
641
- try {
642
- const event = JSON.parse(data);
643
-
644
- if (event.type === 'progress') {
645
- // Update progress display
646
- const resultUI = document.getElementById('result-ui');
647
- if (resultUI) {
648
- const emit = event.data;
649
- let progressHTML = '';
650
-
651
- if (emit.emit === 'progress' && emit.value !== undefined) {
652
- // Show progress bar for percentage-based progress
653
- const percent = Math.round((emit.value || 0) * 100);
654
- progressHTML = \`
655
- <div class="progress-container">
656
- <div style="margin-bottom: 8px;">\${emit.message || 'Processing...'}</div>
657
- <div style="background: #e0e0e0; height: 8px; border-radius: 4px; overflow: hidden;">
658
- <div style="background: #3498db; height: 100%; width: \${percent}%; transition: width 0.3s;"></div>
659
- </div>
660
- <div style="margin-top: 4px; font-size: 12px; color: #666;">\${percent}%</div>
661
- </div>
662
- \`;
663
- } else {
664
- // Show spinner for status messages or unknown progress
665
- const message = emit.message || emit.data?.message || 'Processing...';
666
- progressHTML = \`
667
- <div class="progress-container">
668
- <div class="spinner"></div>
669
- <span id="progress-text">\${message}</span>
670
- </div>
671
- \`;
672
- }
673
-
674
- resultUI.innerHTML = progressHTML;
675
- }
676
- } else if (event.type === 'result') {
677
- currentResult = event.data;
678
- renderResult();
679
- } else if (event.type === 'error') {
680
- throw new Error(event.data.message);
681
- }
682
- } catch (e) {
683
- console.error('Failed to parse SSE event:', e);
684
- }
685
- }
686
- }
687
- } else {
688
- // Regular JSON response (non-generator)
689
- const data = await response.json();
690
-
691
- if (data.error) {
692
- throw new Error(data.error);
693
- }
694
-
695
- currentResult = data.result;
696
- renderResult();
697
- }
698
- } catch (error) {
699
- alert('Error: ' + error.message);
700
- resultUI.innerHTML = '<p style="color: #d32f2f;">Error: ' + error.message + '</p>';
701
- } finally {
702
- btn.disabled = false;
703
- const label = currentMethod.name.charAt(0).toUpperCase() + currentMethod.name.slice(1);
704
- btn.innerHTML = '<span id="invoke-label">' + label + '</span>';
705
- }
706
- });
707
-
708
- function renderResult() {
709
- const resultUI = document.getElementById('result-ui');
710
- const resultData = document.getElementById('result-data');
711
-
712
- // Update Data tab
713
- resultData.textContent = JSON.stringify(currentResult, null, 2);
714
-
715
- // Update UI tab - clear spinner and show result
716
- resultUI.style.display = 'block';
717
- resultUI.innerHTML = \`
718
- <div class="result-container">
719
- <h3>Result</h3>
720
- <div class="result-content">\${formatResult(currentResult)}</div>
721
- </div>
722
- \`;
723
- }
724
-
725
- function formatResult(result) {
726
- // Handle null/undefined
727
- if (result === null || result === undefined) {
728
- return '<p style="color: #666;">No result returned</p>';
729
- }
730
-
731
- // Handle arrays
732
- if (Array.isArray(result)) {
733
- if (result.length === 0) {
734
- return '<p style="color: #666;">Empty result</p>';
735
- }
736
-
737
- // Check if array contains markdown strings
738
- if (typeof result[0] === 'string') {
739
- return '<div class="markdown-list" style="line-height: 1.8;">' +
740
- result.map((item, idx) => {
741
- let text = String(item);
742
- // Convert markdown links to bold HTML links (no underline)
743
- text = text.replace(markdownLinkRegex, '<a href="$2" target="_blank" style="color: #3498db; text-decoration: none; font-weight: 600;">$1</a>');
744
- // Convert bold
745
- text = text.replace(markdownBoldRegex, '<strong>$1</strong>');
746
- // Convert blockquotes (lines starting with >)
747
- text = text.replace(/^&gt;\\s*(.*)$/gm, '<blockquote style="margin: 10px 0; padding: 10px 15px; background: #ecf0f1; border-left: 4px solid #95a5a6; color: #555;">$1</blockquote>');
748
- text = text.replace(/^>\\s*(.*)$/gm, '<blockquote style="margin: 10px 0; padding: 10px 15px; background: #ecf0f1; border-left: 4px solid #95a5a6; color: #555;">$1</blockquote>');
749
- // Convert headers
750
- text = text.replace(/^### (.*$)/gm, '<h4 style="margin: 15px 0 8px 0; font-size: 16px; font-weight: 600;">$1</h4>');
751
- text = text.replace(/^## (.*$)/gm, '<h3 style="margin: 20px 0 10px 0; font-size: 18px; font-weight: 600;">$1</h3>');
752
- // Convert line breaks
753
- text = text.replace(/\\n/g, '<br/>');
754
-
755
- return '<div style="margin: 15px 0; padding: 15px; background: #f8f9fa; border-radius: 6px; border-left: 3px solid #3498db;"><div style="font-size: 12px; color: #95a5a6; margin-bottom: 8px;">Entry ' + (idx + 1) + '</div>' + text + '</div>';
756
- }).join('') +
757
- '</div>';
758
- }
759
-
760
- // Check if it's markdown content objects (has .text property)
761
- if (result[0]?.text) {
762
- return '<div class="markdown-list" style="line-height: 1.8;">' +
763
- result.map((item, idx) => {
764
- let text = item.text || '';
765
- // Convert markdown links to bold HTML links (no underline)
766
- text = text.replace(markdownLinkRegex, '<a href="$2" target="_blank" style="color: #3498db; text-decoration: none; font-weight: 600;">$1</a>');
767
- // Convert bold
768
- text = text.replace(markdownBoldRegex, '<strong>$1</strong>');
769
- // Convert blockquotes
770
- text = text.replace(/^&gt;\\s*(.*)$/gm, '<blockquote style="margin: 10px 0; padding: 10px 15px; background: #ecf0f1; border-left: 4px solid #95a5a6; color: #555;">$1</blockquote>');
771
- text = text.replace(/^>\\s*(.*)$/gm, '<blockquote style="margin: 10px 0; padding: 10px 15px; background: #ecf0f1; border-left: 4px solid #95a5a6; color: #555;">$1</blockquote>');
772
- // Add title if present
773
- if (item.title) {
774
- return '<div style="margin: 15px 0; padding: 15px; background: #f8f9fa; border-radius: 6px; border-left: 3px solid #3498db;"><div style="font-size: 12px; color: #95a5a6; margin-bottom: 8px;">Entry ' + (idx + 1) + '</div><strong style="font-size: 16px;">' + item.title + '</strong><br/><div style="margin-top: 8px;">' + text + '</div></div>';
775
- }
776
- return '<div style="margin: 15px 0; padding: 15px; background: #f8f9fa; border-radius: 6px; border-left: 3px solid #3498db;"><div style="font-size: 12px; color: #95a5a6; margin-bottom: 8px;">Entry ' + (idx + 1) + '</div>' + text + '</div>';
777
- }).join('') +
778
- '</div>';
779
- }
780
-
781
- // Regular array - format as list
782
- return '<ul style="list-style: none; padding: 0;">' +
783
- result.map((item, idx) => {
784
- if (typeof item === 'object') {
785
- return '<li style="margin: 10px 0; padding: 10px; background: #f8f9fa; border-radius: 4px;"><pre style="margin: 0;">' +
786
- JSON.stringify(item, null, 2) + '</pre></li>';
787
- }
788
- return '<li style="margin: 5px 0; padding: 8px; background: #f8f9fa; border-radius: 4px;"><strong>Item ' + (idx + 1) + ':</strong> ' + String(item) + '</li>';
789
- }).join('') +
790
- '</ul>';
791
- }
792
-
793
- // Handle objects
794
- if (typeof result === 'object' && result !== null) {
795
- return '<pre style="margin: 0; padding: 15px; background: #f8f9fa; border-radius: 6px; overflow-x: auto;">' + JSON.stringify(result, null, 2) + '</pre>';
796
- }
797
-
798
- // Handle strings (apply markdown rendering)
799
- if (typeof result === 'string') {
800
- let text = result;
801
- // Convert markdown links to bold HTML links (no underline)
802
- text = text.replace(markdownLinkRegex, '<a href="$2" target="_blank" style="color: #3498db; text-decoration: none; font-weight: 600;">$1</a>');
803
- // Convert bold
804
- text = text.replace(markdownBoldRegex, '<strong>$1</strong>');
805
- // Convert blockquotes (lines starting with >)
806
- text = text.replace(/^&gt;\\s*(.*)$/gm, '<blockquote style="margin: 10px 0; padding: 10px 15px; background: #ecf0f1; border-left: 4px solid #95a5a6; color: #555;">$1</blockquote>');
807
- text = text.replace(/^>\\s*(.*)$/gm, '<blockquote style="margin: 10px 0; padding: 10px 15px; background: #ecf0f1; border-left: 4px solid #95a5a6; color: #555;">$1</blockquote>');
808
- // Convert headers
809
- text = text.replace(/^### (.*$)/gm, '<h4 style="margin: 15px 0 8px 0; font-size: 16px; font-weight: 600;">$1</h4>');
810
- text = text.replace(/^## (.*$)/gm, '<h3 style="margin: 20px 0 10px 0; font-size: 18px; font-weight: 600;">$1</h3>');
811
- text = text.replace(/^# (.*$)/gm, '<h2 style="margin: 20px 0 10px 0; font-size: 20px; font-weight: 600;">$1</h2>');
812
- // Convert line breaks
813
- text = text.replace(/\\n/g, '<br/>');
814
- return '<div style="padding: 15px; background: #f8f9fa; border-radius: 6px; line-height: 1.8;">' + text + '</div>';
815
- }
816
-
817
- // Handle other primitives
818
- return '<div style="padding: 15px; background: #f8f9fa; border-radius: 6px;">' + String(result) + '</div>';
819
- }
820
-
821
- // Tab switching
822
- document.querySelectorAll('.tab').forEach(tab => {
823
- tab.addEventListener('click', () => {
824
- const tabName = tab.dataset.tab;
825
-
826
- document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
827
- document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
828
-
829
- tab.classList.add('active');
830
- document.querySelector(\`[data-content="\${tabName}"]\`).classList.add('active');
831
- });
832
- });
833
-
834
- // Initialize
835
- renderTree();
836
- </script>
837
- </body>
838
- </html>`;
839
- }
840
- //# sourceMappingURL=playground-server.js.map