@portel/photon 1.18.0 → 1.20.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/beam/routes/api-browse.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-browse.js +16 -4
- package/dist/auto-ui/beam/routes/api-browse.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-config.js +4 -4
- package/dist/auto-ui/beam/routes/api-config.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-marketplace.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-marketplace.js +14 -1
- package/dist/auto-ui/beam/routes/api-marketplace.js.map +1 -1
- package/dist/auto-ui/beam.d.ts.map +1 -1
- package/dist/auto-ui/beam.js +196 -77
- package/dist/auto-ui/beam.js.map +1 -1
- package/dist/auto-ui/bridge/index.d.ts.map +1 -1
- package/dist/auto-ui/bridge/index.js +17 -0
- package/dist/auto-ui/bridge/index.js.map +1 -1
- package/dist/auto-ui/streamable-http-transport.d.ts +1 -0
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +64 -16
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/auto-ui/types.d.ts +12 -0
- package/dist/auto-ui/types.d.ts.map +1 -1
- package/dist/auto-ui/types.js.map +1 -1
- package/dist/beam-form.bundle.js +49 -6
- package/dist/beam-form.bundle.js.map +2 -2
- package/dist/beam.bundle.js +2090 -512
- package/dist/beam.bundle.js.map +4 -4
- package/dist/capability-negotiator.d.ts +67 -0
- package/dist/capability-negotiator.d.ts.map +1 -0
- package/dist/capability-negotiator.js +104 -0
- package/dist/capability-negotiator.js.map +1 -0
- package/dist/channel-manager.d.ts +122 -0
- package/dist/channel-manager.d.ts.map +1 -0
- package/dist/channel-manager.js +266 -0
- package/dist/channel-manager.js.map +1 -0
- package/dist/claude-code-plugin.js +1 -1
- package/dist/cli/commands/beam.d.ts.map +1 -1
- package/dist/cli/commands/beam.js +8 -2
- package/dist/cli/commands/beam.js.map +1 -1
- package/dist/cli/commands/changelog.d.ts +9 -0
- package/dist/cli/commands/changelog.d.ts.map +1 -0
- package/dist/cli/commands/changelog.js +133 -0
- package/dist/cli/commands/changelog.js.map +1 -0
- package/dist/cli/commands/maker.d.ts.map +1 -1
- package/dist/cli/commands/maker.js +23 -2
- package/dist/cli/commands/maker.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +53 -0
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/package.d.ts.map +1 -1
- package/dist/cli/commands/package.js +43 -9
- package/dist/cli/commands/package.js.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +1 -0
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/update.d.ts +3 -2
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +50 -43
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +16 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli-alias.js +1 -1
- package/dist/cli-alias.js.map +1 -1
- package/dist/context-store.d.ts +23 -33
- package/dist/context-store.d.ts.map +1 -1
- package/dist/context-store.js +147 -97
- package/dist/context-store.js.map +1 -1
- package/dist/context.d.ts +15 -10
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +37 -13
- package/dist/context.js.map +1 -1
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +12 -0
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/server.js +34 -51
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/worker-manager.d.ts.map +1 -1
- package/dist/daemon/worker-manager.js +21 -7
- package/dist/daemon/worker-manager.js.map +1 -1
- package/dist/data-migration.d.ts +27 -0
- package/dist/data-migration.d.ts.map +1 -0
- package/dist/data-migration.js +307 -0
- package/dist/data-migration.js.map +1 -0
- package/dist/editor-support/docblock-tag-catalog.d.ts.map +1 -1
- package/dist/editor-support/docblock-tag-catalog.js +6 -0
- package/dist/editor-support/docblock-tag-catalog.js.map +1 -1
- package/dist/loader.d.ts +13 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +169 -22
- package/dist/loader.js.map +1 -1
- package/dist/marketplace-manager.d.ts +6 -0
- package/dist/marketplace-manager.d.ts.map +1 -1
- package/dist/marketplace-manager.js +185 -62
- package/dist/marketplace-manager.js.map +1 -1
- package/dist/namespace-migration.d.ts +1 -0
- package/dist/namespace-migration.d.ts.map +1 -1
- package/dist/namespace-migration.js +86 -0
- package/dist/namespace-migration.js.map +1 -1
- package/dist/photon-cli-runner.d.ts.map +1 -1
- package/dist/photon-cli-runner.js +47 -21
- package/dist/photon-cli-runner.js.map +1 -1
- package/dist/photon-doc-extractor.d.ts +1 -0
- package/dist/photon-doc-extractor.d.ts.map +1 -1
- package/dist/photon-doc-extractor.js +6 -0
- package/dist/photon-doc-extractor.js.map +1 -1
- package/dist/readme-syncer.d.ts.map +1 -1
- package/dist/readme-syncer.js +6 -1
- package/dist/readme-syncer.js.map +1 -1
- package/dist/resource-server.d.ts +105 -0
- package/dist/resource-server.d.ts.map +1 -0
- package/dist/resource-server.js +723 -0
- package/dist/resource-server.js.map +1 -0
- package/dist/serv/auth/jwt.d.ts +2 -0
- package/dist/serv/auth/jwt.d.ts.map +1 -1
- package/dist/serv/auth/jwt.js +11 -5
- package/dist/serv/auth/jwt.js.map +1 -1
- package/dist/serv/vault/token-vault.d.ts +2 -0
- package/dist/serv/vault/token-vault.d.ts.map +1 -1
- package/dist/serv/vault/token-vault.js +6 -0
- package/dist/serv/vault/token-vault.js.map +1 -1
- package/dist/server.d.ts +30 -119
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +252 -1122
- package/dist/server.js.map +1 -1
- package/dist/shared/audit.d.ts.map +1 -1
- package/dist/shared/audit.js +11 -4
- package/dist/shared/audit.js.map +1 -1
- package/dist/shared/security.d.ts +10 -0
- package/dist/shared/security.d.ts.map +1 -1
- package/dist/shared/security.js +27 -0
- package/dist/shared/security.js.map +1 -1
- package/dist/task-executor.d.ts +69 -0
- package/dist/task-executor.d.ts.map +1 -0
- package/dist/task-executor.js +182 -0
- package/dist/task-executor.js.map +1 -0
- package/dist/tasks/store.d.ts.map +1 -1
- package/dist/tasks/store.js +6 -2
- package/dist/tasks/store.js.map +1 -1
- package/dist/types/photon-instance.d.ts +50 -0
- package/dist/types/photon-instance.d.ts.map +1 -0
- package/dist/types/photon-instance.js +9 -0
- package/dist/types/photon-instance.js.map +1 -0
- package/dist/types/server-types.d.ts +61 -0
- package/dist/types/server-types.d.ts.map +1 -0
- package/dist/types/server-types.js +8 -0
- package/dist/types/server-types.js.map +1 -0
- package/dist/version-notify.d.ts +27 -0
- package/dist/version-notify.d.ts.map +1 -0
- package/dist/version-notify.js +142 -0
- package/dist/version-notify.js.map +1 -0
- package/package.json +3 -3
- package/dist/auto-ui/bridge/openai-shim.d.ts +0 -20
- package/dist/auto-ui/bridge/openai-shim.d.ts.map +0 -1
- package/dist/auto-ui/bridge/openai-shim.js +0 -231
- package/dist/auto-ui/bridge/openai-shim.js.map +0 -1
- package/dist/auto-ui/bridge/photon-app.d.ts +0 -162
- package/dist/auto-ui/bridge/photon-app.d.ts.map +0 -1
- package/dist/auto-ui/bridge/photon-app.js +0 -460
- package/dist/auto-ui/bridge/photon-app.js.map +0 -1
- package/dist/auto-ui/daemon-tools.d.ts +0 -45
- package/dist/auto-ui/daemon-tools.d.ts.map +0 -1
- package/dist/auto-ui/daemon-tools.js +0 -581
- package/dist/auto-ui/daemon-tools.js.map +0 -1
- package/dist/auto-ui/design-system/index.d.ts +0 -21
- package/dist/auto-ui/design-system/index.d.ts.map +0 -1
- package/dist/auto-ui/design-system/index.js +0 -27
- package/dist/auto-ui/design-system/index.js.map +0 -1
- package/dist/auto-ui/design-system/transaction-ui.d.ts +0 -70
- package/dist/auto-ui/design-system/transaction-ui.d.ts.map +0 -1
- package/dist/auto-ui/design-system/transaction-ui.js +0 -982
- package/dist/auto-ui/design-system/transaction-ui.js.map +0 -1
- package/dist/auto-ui/playground-server.d.ts +0 -7
- package/dist/auto-ui/playground-server.d.ts.map +0 -1
- package/dist/auto-ui/playground-server.js +0 -840
- package/dist/auto-ui/playground-server.js.map +0 -1
- package/dist/auto-ui/rendering/components.d.ts +0 -29
- package/dist/auto-ui/rendering/components.d.ts.map +0 -1
- package/dist/auto-ui/rendering/components.js +0 -1341
- package/dist/auto-ui/rendering/components.js.map +0 -1
- package/dist/auto-ui/rendering/field-analyzer.d.ts +0 -104
- package/dist/auto-ui/rendering/field-analyzer.d.ts.map +0 -1
- package/dist/auto-ui/rendering/field-analyzer.js +0 -447
- package/dist/auto-ui/rendering/field-analyzer.js.map +0 -1
- package/dist/auto-ui/rendering/field-renderers.d.ts +0 -64
- package/dist/auto-ui/rendering/field-renderers.d.ts.map +0 -1
- package/dist/auto-ui/rendering/field-renderers.js +0 -317
- package/dist/auto-ui/rendering/field-renderers.js.map +0 -1
- package/dist/auto-ui/rendering/index.d.ts +0 -28
- package/dist/auto-ui/rendering/index.d.ts.map +0 -1
- package/dist/auto-ui/rendering/index.js +0 -60
- package/dist/auto-ui/rendering/index.js.map +0 -1
- package/dist/auto-ui/rendering/layout-selector.d.ts +0 -60
- package/dist/auto-ui/rendering/layout-selector.d.ts.map +0 -1
- package/dist/auto-ui/rendering/layout-selector.js +0 -476
- package/dist/auto-ui/rendering/layout-selector.js.map +0 -1
- package/dist/markdown-utils.d.ts +0 -8
- package/dist/markdown-utils.d.ts.map +0 -1
- package/dist/markdown-utils.js +0 -64
- package/dist/markdown-utils.js.map +0 -1
- package/dist/mcp-client.d.ts +0 -9
- package/dist/mcp-client.d.ts.map +0 -1
- package/dist/mcp-client.js +0 -11
- package/dist/mcp-client.js.map +0 -1
- package/dist/mcp-elicitation.d.ts +0 -32
- package/dist/mcp-elicitation.d.ts.map +0 -1
- package/dist/mcp-elicitation.js +0 -26
- package/dist/mcp-elicitation.js.map +0 -1
- package/dist/photons/builder-compass.photon.d.ts +0 -167
- package/dist/photons/builder-compass.photon.d.ts.map +0 -1
- package/dist/photons/builder-compass.photon.js +0 -816
- package/dist/photons/builder-compass.photon.js.map +0 -1
- package/dist/photons/builder-compass.photon.ts +0 -1129
- package/dist/photons/docs/ui/docs.html +0 -441
- package/dist/photons/docs.photon.d.ts +0 -237
- package/dist/photons/docs.photon.d.ts.map +0 -1
- package/dist/photons/docs.photon.js +0 -483
- package/dist/photons/docs.photon.js.map +0 -1
- package/dist/photons/docs.photon.ts +0 -536
- package/dist/photons/slides.photon.d.ts +0 -212
- package/dist/photons/slides.photon.d.ts.map +0 -1
- package/dist/photons/slides.photon.js +0 -355
- package/dist/photons/slides.photon.js.map +0 -1
- package/dist/photons/slides.photon.ts +0 -370
- package/dist/photons/spreadsheet/ui/spreadsheet.html +0 -779
- package/dist/photons/spreadsheet.photon.d.ts +0 -554
- package/dist/photons/spreadsheet.photon.d.ts.map +0 -1
- package/dist/photons/spreadsheet.photon.js +0 -1050
- package/dist/photons/spreadsheet.photon.js.map +0 -1
- package/dist/photons/spreadsheet.photon.ts +0 -1239
- package/dist/photons/ui/builder-compass.html +0 -1199
- package/dist/photons/ui/builder-compass.photon.html +0 -380
- package/dist/security-scanner.d.ts +0 -52
- package/dist/security-scanner.d.ts.map +0 -1
- package/dist/security-scanner.js +0 -181
- package/dist/security-scanner.js.map +0 -1
- package/dist/shared/performance.d.ts +0 -65
- package/dist/shared/performance.d.ts.map +0 -1
- package/dist/shared/performance.js +0 -136
- 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(/^>\\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(/^>\\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(/^>\\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
|