shokupan 0.10.5 → 0.12.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/README.md +46 -1815
- package/dist/{analyzer-BqIe1p0R.js → analyzer-BkNQHWj4.js} +3 -8
- package/dist/{analyzer-BqIe1p0R.js.map → analyzer-BkNQHWj4.js.map} +1 -1
- package/dist/{analyzer-CKLGLFtx.cjs → analyzer-DM-OlRq8.cjs} +2 -7
- package/dist/{analyzer-CKLGLFtx.cjs.map → analyzer-DM-OlRq8.cjs.map} +1 -1
- package/dist/{analyzer.impl-D9Yi1Hax.cjs → analyzer.impl-CVJ8zfGQ.cjs} +596 -42
- package/dist/analyzer.impl-CVJ8zfGQ.cjs.map +1 -0
- package/dist/{analyzer.impl-CV6W1Eq7.js → analyzer.impl-CsA1bS_s.js} +596 -42
- package/dist/analyzer.impl-CsA1bS_s.js.map +1 -0
- package/dist/cli.cjs +206 -18
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +206 -18
- package/dist/cli.js.map +1 -1
- package/dist/context.d.ts +46 -9
- package/dist/index.cjs +3239 -1173
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3236 -1171
- package/dist/index.js.map +1 -1
- package/dist/plugins/application/api-explorer/static/explorer-client.mjs +375 -29
- package/dist/plugins/application/api-explorer/static/style.css +327 -8
- package/dist/plugins/application/api-explorer/static/theme.css +11 -2
- package/dist/plugins/application/asyncapi/generator.d.ts +4 -0
- package/dist/plugins/application/asyncapi/static/asyncapi-client.mjs +154 -22
- package/dist/plugins/application/asyncapi/static/style.css +24 -8
- package/dist/plugins/application/auth.d.ts +5 -0
- package/dist/plugins/application/dashboard/fetch-interceptor.d.ts +119 -0
- package/dist/plugins/application/dashboard/metrics-collector.d.ts +38 -2
- package/dist/plugins/application/dashboard/plugin.d.ts +53 -1
- package/dist/plugins/application/dashboard/static/charts.js +127 -62
- package/dist/plugins/application/dashboard/static/client.js +160 -0
- package/dist/plugins/application/dashboard/static/graph.mjs +167 -56
- package/dist/plugins/application/dashboard/static/reactflow.css +20 -10
- package/dist/plugins/application/dashboard/static/registry.js +112 -8
- package/dist/plugins/application/dashboard/static/requests.js +1167 -71
- package/dist/plugins/application/dashboard/static/styles.css +186 -14
- package/dist/plugins/application/dashboard/static/tabs.js +44 -9
- package/dist/plugins/application/dashboard/static/tabulator.css +23 -3
- package/dist/plugins/application/dashboard/static/theme.css +11 -2
- package/dist/plugins/application/mcp-server/plugin.d.ts +39 -0
- package/dist/plugins/application/openapi/analyzer.impl.d.ts +65 -1
- package/dist/plugins/application/openapi/openapi.d.ts +3 -0
- package/dist/plugins/application/shared/ast-utils.d.ts +7 -0
- package/dist/plugins/middleware/compression.d.ts +12 -2
- package/dist/plugins/middleware/rate-limit.d.ts +5 -0
- package/dist/router.d.ts +59 -19
- package/dist/server.d.ts +22 -0
- package/dist/shokupan.d.ts +31 -3
- package/dist/util/adapter/bun.d.ts +8 -0
- package/dist/util/adapter/filesystem.d.ts +20 -0
- package/dist/util/adapter/index.d.ts +4 -0
- package/dist/util/adapter/interface.d.ts +12 -0
- package/dist/util/adapter/node.d.ts +8 -0
- package/dist/util/adapter/wintercg.d.ts +5 -0
- package/dist/util/body-parser.d.ts +30 -0
- package/dist/util/controller-scanner.d.ts +4 -0
- package/dist/util/cpu-monitor.d.ts +2 -0
- package/dist/util/decorators.d.ts +20 -3
- package/dist/util/di.d.ts +3 -8
- package/dist/util/metadata.d.ts +18 -0
- package/dist/util/middleware-tracker.d.ts +10 -0
- package/dist/util/request.d.ts +1 -0
- package/dist/util/symbol.d.ts +1 -0
- package/dist/util/types.d.ts +167 -1
- package/package.json +7 -5
- package/dist/analyzer.impl-CV6W1Eq7.js.map +0 -1
- package/dist/analyzer.impl-D9Yi1Hax.cjs.map +0 -1
- package/dist/http-server-BEMPIs33.cjs +0 -85
- package/dist/http-server-BEMPIs33.cjs.map +0 -1
- package/dist/http-server-CCeagTyU.js +0 -68
- package/dist/http-server-CCeagTyU.js.map +0 -1
- package/dist/plugins/application/dashboard/static/failures.js +0 -85
- package/dist/plugins/application/dashboard/static/poll.js +0 -146
- package/dist/plugins/application/http-server.d.ts +0 -13
|
@@ -40,6 +40,16 @@ function handleHashNavigation() {
|
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// Check if it's a middleware view
|
|
44
|
+
if (hash.startsWith('middleware-')) {
|
|
45
|
+
const middlewareId = hash.replace('middleware-', '');
|
|
46
|
+
const middleware = explorerData.middlewareRegistry?.[middlewareId];
|
|
47
|
+
if (middleware) {
|
|
48
|
+
renderMiddlewareView(middleware, container);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
43
53
|
// Find route
|
|
44
54
|
const route = explorerData.routes.find(r => r.op.operationId === hash);
|
|
45
55
|
if (route) {
|
|
@@ -60,19 +70,202 @@ function renderInfoSection(info) {
|
|
|
60
70
|
`;
|
|
61
71
|
}
|
|
62
72
|
|
|
73
|
+
function renderMiddlewareView(middleware, container) {
|
|
74
|
+
const html = `
|
|
75
|
+
<div class="middleware-detail-view">
|
|
76
|
+
<div class="middleware-header">
|
|
77
|
+
<h1>${middleware.name}</h1>
|
|
78
|
+
<div class="middleware-meta">
|
|
79
|
+
${middleware.scope ? `<span class="badge">${middleware.scope}</span>` : ''}
|
|
80
|
+
${middleware.file ? `
|
|
81
|
+
<a href="vscode://file/${middleware.file}:${middleware.startLine || 1}" class="doc-source-link">
|
|
82
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
83
|
+
<polyline points="16 18 22 12 16 6"></polyline>
|
|
84
|
+
<polyline points="8 6 2 12 8 18"></polyline>
|
|
85
|
+
</svg>
|
|
86
|
+
${middleware.file.split('/').pop()}:${middleware.startLine || 1}
|
|
87
|
+
</a>
|
|
88
|
+
` : ''}
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
${middleware.responseTypes || middleware.headers ? `
|
|
93
|
+
<div class="middleware-capabilities">
|
|
94
|
+
${middleware.responseTypes ? `
|
|
95
|
+
<div class="capability-section">
|
|
96
|
+
<h3>Response Types</h3>
|
|
97
|
+
<div class="response-list">
|
|
98
|
+
${Object.entries(middleware.responseTypes).map(([code, resp]) => `
|
|
99
|
+
<div class="response-item">
|
|
100
|
+
<code class="status-code">${code}</code>
|
|
101
|
+
<span>${resp.description || 'Response'}</span>
|
|
102
|
+
</div>
|
|
103
|
+
`).join('')}
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
` : ''}
|
|
107
|
+
${middleware.headers && middleware.headers.length > 0 ? `
|
|
108
|
+
<div class="capability-section">
|
|
109
|
+
<h3>Headers</h3>
|
|
110
|
+
<div class="header-list">
|
|
111
|
+
${middleware.headers.map(h => `<code class="header-name">${h}</code>`).join('')}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
` : ''}
|
|
115
|
+
</div>
|
|
116
|
+
` : ''}
|
|
117
|
+
|
|
118
|
+
${middleware.file ? `
|
|
119
|
+
<div class="source-section">
|
|
120
|
+
<h3>Source Code</h3>
|
|
121
|
+
<div id="monaco-middleware-source" class="monaco-container" style="height: 400px;"></div>
|
|
122
|
+
</div>
|
|
123
|
+
` : ''}
|
|
124
|
+
|
|
125
|
+
${middleware.usedBy && middleware.usedBy.length > 0 ? `
|
|
126
|
+
<div class="usage-section">
|
|
127
|
+
<h3>Used By (${middleware.usedBy.length} routes)</h3>
|
|
128
|
+
<div class="table-container">
|
|
129
|
+
<table class="data-table">
|
|
130
|
+
<thead>
|
|
131
|
+
<tr>
|
|
132
|
+
<th>Method</th>
|
|
133
|
+
<th>Path</th>
|
|
134
|
+
<th>Description</th>
|
|
135
|
+
</tr>
|
|
136
|
+
</thead>
|
|
137
|
+
<tbody>
|
|
138
|
+
${middleware.usedBy.map(routePath => {
|
|
139
|
+
const route = explorerData.routes.find(r => r.path === routePath);
|
|
140
|
+
if (route) {
|
|
141
|
+
return `
|
|
142
|
+
<tr>
|
|
143
|
+
<td class="col-method"><span class="badge badge-${route.method.toUpperCase()}">${route.method.toUpperCase()}</span></td>
|
|
144
|
+
<td class="col-path"><a href="#${route.op.operationId}" class="route-link">${routePath}</a></td>
|
|
145
|
+
<td class="col-desc">${route.op.summary || route.op.description || '-'}</td>
|
|
146
|
+
</tr>
|
|
147
|
+
`;
|
|
148
|
+
}
|
|
149
|
+
return `
|
|
150
|
+
<tr>
|
|
151
|
+
<td colspan="3">${routePath} (Route definition not found)</td>
|
|
152
|
+
</tr>
|
|
153
|
+
`;
|
|
154
|
+
}).join('')}
|
|
155
|
+
</tbody>
|
|
156
|
+
</table>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
` : ''}
|
|
160
|
+
</div>
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
container.innerHTML = html;
|
|
164
|
+
|
|
165
|
+
// Initialize Monaco for source code
|
|
166
|
+
if (middleware.file && typeof require !== 'undefined') {
|
|
167
|
+
require.config({ paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs' } });
|
|
168
|
+
require(['vs/editor/editor.main'], function () {
|
|
169
|
+
const monacoContainer = document.getElementById('monaco-middleware-source');
|
|
170
|
+
if (monacoContainer) {
|
|
171
|
+
if (currentEditors.source) currentEditors.source.dispose();
|
|
172
|
+
|
|
173
|
+
currentEditors.source = monaco.editor.create(monacoContainer, {
|
|
174
|
+
value: '// Loading source...',
|
|
175
|
+
language: 'typescript',
|
|
176
|
+
theme: 'vs-dark',
|
|
177
|
+
minimap: { enabled: false },
|
|
178
|
+
lineNumbers: 'on',
|
|
179
|
+
readOnly: true,
|
|
180
|
+
scrollBeyondLastLine: false,
|
|
181
|
+
automaticLayout: true,
|
|
182
|
+
glyphMargin: false,
|
|
183
|
+
folding: false,
|
|
184
|
+
lineNumbersMinChars: 3,
|
|
185
|
+
fontSize: 13,
|
|
186
|
+
fontFamily: 'JetBrains Mono',
|
|
187
|
+
renderLineHighlight: 'none'
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Load source
|
|
191
|
+
fetch(`_source?file=${encodeURIComponent(middleware.file)}`)
|
|
192
|
+
.then(res => {
|
|
193
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
194
|
+
return res.text();
|
|
195
|
+
})
|
|
196
|
+
.then(text => {
|
|
197
|
+
if (currentEditors.source) {
|
|
198
|
+
currentEditors.source.setValue(text);
|
|
199
|
+
|
|
200
|
+
// Highlight the middleware function
|
|
201
|
+
if (middleware.startLine) {
|
|
202
|
+
const endLine = middleware.endLine || middleware.startLine;
|
|
203
|
+
const decorations = [{
|
|
204
|
+
range: new monaco.Range(middleware.startLine, 1, endLine, 1),
|
|
205
|
+
options: {
|
|
206
|
+
isWholeLine: true,
|
|
207
|
+
className: 'closure-highlight'
|
|
208
|
+
}
|
|
209
|
+
}];
|
|
210
|
+
currentEditors.source.deltaDecorations([], decorations);
|
|
211
|
+
currentEditors.source.revealLineInCenter(middleware.startLine);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
.catch(err => {
|
|
216
|
+
if (currentEditors.source) {
|
|
217
|
+
currentEditors.source.setValue(`// Failed to load source: ${err.message}`);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
63
225
|
// Helper to recursively render schema properties
|
|
64
|
-
function renderSchema(schema, depth = 0) {
|
|
226
|
+
function renderSchema(schema, depth = 0, isResponse = false) {
|
|
65
227
|
if (!schema) return '';
|
|
66
228
|
|
|
67
229
|
const indent = depth * 16;
|
|
68
230
|
const type = schema.type || 'any';
|
|
69
231
|
const required = schema.required || [];
|
|
70
232
|
|
|
233
|
+
// Handle oneOf (multiple possible schemas)
|
|
234
|
+
if (schema.oneOf) {
|
|
235
|
+
return `
|
|
236
|
+
<div style="margin-left: ${indent}px;">
|
|
237
|
+
<div style="font-weight: 500; color: var(--text-primary); margin-bottom: 8px;">
|
|
238
|
+
<span style="color: var(--text-secondary); font-size: 0.85rem;">One of the following:</span>
|
|
239
|
+
</div>
|
|
240
|
+
${schema.oneOf.map((subSchema, idx) => `
|
|
241
|
+
<div style="border-left: 3px solid #4caf50; padding-left: 12px; margin-bottom: 12px;">
|
|
242
|
+
<div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 4px;">Option ${idx + 1}:</div>
|
|
243
|
+
${renderSchema(subSchema, 0, isResponse)}
|
|
244
|
+
</div>
|
|
245
|
+
`).join('')}
|
|
246
|
+
</div>
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
|
|
71
250
|
if (type === 'object' && schema.properties) {
|
|
72
251
|
const props = Object.entries(schema.properties).map(([key, prop]) => {
|
|
73
252
|
const isRequired = required.includes(key);
|
|
74
|
-
const
|
|
75
|
-
const
|
|
253
|
+
const isUnknown = prop['x-unknown'] === true;
|
|
254
|
+
const propType = isUnknown ? 'unknown' : (prop.type || 'any');
|
|
255
|
+
const hasNested = (prop.type === 'object' && prop.properties) || (prop.type === 'array' && prop.items);
|
|
256
|
+
|
|
257
|
+
// For responses, show "optional" for non-required fields
|
|
258
|
+
// For requests, show "required" for required fields
|
|
259
|
+
let badgeHtml = '';
|
|
260
|
+
if (isResponse) {
|
|
261
|
+
if (!isRequired) {
|
|
262
|
+
badgeHtml = '<div class="property-optional" style="margin-left: auto; font-size: 0.75rem; color: #9e9e9e; text-transform: uppercase; font-style: italic;">optional</div>';
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
if (isRequired) {
|
|
266
|
+
badgeHtml = '<div class="property-required" style="margin-left: auto; font-size: 0.75rem; color: #f44336; text-transform: uppercase;">required</div>';
|
|
267
|
+
}
|
|
268
|
+
}
|
|
76
269
|
|
|
77
270
|
return `
|
|
78
271
|
<div style="margin-left: ${indent}px;">
|
|
@@ -80,11 +273,12 @@ function renderSchema(schema, depth = 0) {
|
|
|
80
273
|
<div class="property-name" style="font-family: monospace; font-weight: 500; color: var(--text-primary);">${key}</div>
|
|
81
274
|
<span class="property-detail" style="color: var(--text-secondary); font-size: 0.85rem;">
|
|
82
275
|
<span class="property-detail-value">${propType}</span>
|
|
276
|
+
${isUnknown ? '<span class="unknown-marker" title="Type could not be determined statically" style="color: #ff9800; margin-left: 4px;">⚠️</span>' : ''}
|
|
83
277
|
</span>
|
|
84
|
-
${
|
|
278
|
+
${badgeHtml}
|
|
85
279
|
</div>
|
|
86
280
|
${prop.description ? `<div style="color: var(--text-secondary); font-size: 0.85rem; margin-left: 0; margin-top: -4px; margin-bottom: 4px;">${prop.description}</div>` : ''}
|
|
87
|
-
${hasNested ? renderSchema(propType === 'array' ? prop.items : prop, depth + 1) : ''}
|
|
281
|
+
${hasNested ? renderSchema(propType === 'array' ? prop.items : prop, depth + 1, isResponse) : ''}
|
|
88
282
|
</div>
|
|
89
283
|
`;
|
|
90
284
|
}).join('');
|
|
@@ -95,12 +289,18 @@ function renderSchema(schema, depth = 0) {
|
|
|
95
289
|
<div style="font-family: monospace; font-size: 0.85rem; color: var(--text-secondary);">
|
|
96
290
|
[array items]
|
|
97
291
|
</div>
|
|
98
|
-
${renderSchema(schema.items, depth + 1)}
|
|
292
|
+
${renderSchema(schema.items, depth + 1, isResponse)}
|
|
99
293
|
</div>
|
|
100
294
|
`;
|
|
101
295
|
}
|
|
102
296
|
|
|
103
|
-
return
|
|
297
|
+
return `
|
|
298
|
+
<div style="margin-left: ${indent}px; padding: 4px 0;">
|
|
299
|
+
<span class="property-detail-value" style="color: var(--text-secondary); font-family: monospace;">${type}</span>
|
|
300
|
+
${schema.format ? `<span style="color: var(--text-secondary); font-size: 0.85rem; margin-left: 6px;">(${schema.format})</span>` : ''}
|
|
301
|
+
${schema.description ? `<div style="color: var(--text-secondary); font-size: 0.85rem; margin-top: 4px;">${schema.description}</div>` : ''}
|
|
302
|
+
</div>
|
|
303
|
+
`;
|
|
104
304
|
}
|
|
105
305
|
|
|
106
306
|
// Helper to highlight path operators
|
|
@@ -168,11 +368,38 @@ function renderRequestView(route, container) {
|
|
|
168
368
|
<div class="info-header">
|
|
169
369
|
<h2 class="info-title">${summary}</h2>
|
|
170
370
|
<div class="info-meta">
|
|
371
|
+
${op['x-shokupan-builtin'] ? `
|
|
372
|
+
<div class="meta-row">
|
|
373
|
+
<span class="builtin-badge" title="This endpoint is provided by a built-in plugin">
|
|
374
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
375
|
+
<rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
|
|
376
|
+
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path>
|
|
377
|
+
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
|
|
378
|
+
</svg>
|
|
379
|
+
Built-in Plugin ${op['x-shokupan-plugin-name'] ? `(${op['x-shokupan-plugin-name']})` : ''}
|
|
380
|
+
</span>
|
|
381
|
+
</div>
|
|
382
|
+
` : ''}
|
|
171
383
|
${op.tags ? `<div class="meta-row"><strong>Tags:</strong> ${op.tags.map(t => `<span class="badge">${t}</span>`).join('')}</div>` : ''}
|
|
172
384
|
</div>
|
|
173
385
|
</div>
|
|
174
386
|
|
|
175
387
|
${op.description ? `<div class="markdown-content" style="margin:16px 0;">${parseMarkdown(op.description)}</div>` : ''}
|
|
388
|
+
|
|
389
|
+
${op['x-source-info']?.isRuntime ? `
|
|
390
|
+
<div class="alert alert-warning" style="margin: 16px 0; padding: 12px; background: rgba(255, 152, 0, 0.1); border-left: 3px solid #ff9800; border-radius: 4px;">
|
|
391
|
+
<strong style="color: #ff9800;">⚠️ Warning:</strong> This route's path could not be statically determined.
|
|
392
|
+
<div style="margin-top: 4px; font-size: 0.9em; opacity: 0.8;">
|
|
393
|
+
The path depends on runtime variables (e.g., process.env). Static analysis features like type inference may be limited.
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
` : ''}
|
|
397
|
+
|
|
398
|
+
${op['x-warning'] ? `
|
|
399
|
+
<div class="alert alert-warning" style="margin: 16px 0; padding: 12px; background: rgba(255, 152, 0, 0.1); border-left: 3px solid #ff9800; border-radius: 4px;">
|
|
400
|
+
<strong style="color: #ff9800;">⚠️ Warning:</strong> ${op['x-warning-reason'] || 'This operation could not be fully analyzed statically'}
|
|
401
|
+
</div>
|
|
402
|
+
` : ''}
|
|
176
403
|
|
|
177
404
|
${op.tags && op.tags.length > 0 ? `
|
|
178
405
|
<div class="hierarchy-section" style="margin:16px 0;">
|
|
@@ -259,7 +486,7 @@ function renderRequestView(route, container) {
|
|
|
259
486
|
` : ''}
|
|
260
487
|
${schema ? `
|
|
261
488
|
<div style="margin-top: 8px; background: var(--bg-primary); padding: 8px; border-radius: 4px;">
|
|
262
|
-
${renderSchema(schema)}
|
|
489
|
+
${renderSchema(schema, 0, true)}
|
|
263
490
|
</div>
|
|
264
491
|
` : ''}
|
|
265
492
|
</div>
|
|
@@ -476,6 +703,132 @@ function renderRequestView(route, container) {
|
|
|
476
703
|
setupPanelResizer(container);
|
|
477
704
|
}
|
|
478
705
|
|
|
706
|
+
const STORAGE_PREFIX = 'shokupan:explorer:';
|
|
707
|
+
|
|
708
|
+
function saveState(key, value) {
|
|
709
|
+
try {
|
|
710
|
+
localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(value));
|
|
711
|
+
} catch (e) {
|
|
712
|
+
console.warn('Failed to save state', e);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function getState(key, defaultValue) {
|
|
717
|
+
try {
|
|
718
|
+
const item = localStorage.getItem(STORAGE_PREFIX + key);
|
|
719
|
+
return item ? JSON.parse(item) : defaultValue;
|
|
720
|
+
} catch (e) {
|
|
721
|
+
return defaultValue;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function setupSidebar() {
|
|
726
|
+
const sidebar = document.querySelector('.sidebar');
|
|
727
|
+
const content = document.querySelector('.content');
|
|
728
|
+
if (!sidebar) return;
|
|
729
|
+
|
|
730
|
+
// Restore state
|
|
731
|
+
const savedWidth = getState('sidebar_width', 300);
|
|
732
|
+
const isCollapsed = getState('sidebar_collapsed', false);
|
|
733
|
+
|
|
734
|
+
if (savedWidth) sidebar.style.width = `${savedWidth}px`;
|
|
735
|
+
if (isCollapsed) {
|
|
736
|
+
sidebar.classList.add('collapsed');
|
|
737
|
+
content.classList.add('no-sidebar');
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// 1. Toggle Sidebar Logic
|
|
741
|
+
const toggleBtn = document.querySelector('.toggle-sidebar');
|
|
742
|
+
const expandBtn = document.querySelector('.sidebar-collapse-trigger');
|
|
743
|
+
|
|
744
|
+
const toggleSidebar = (collapse) => {
|
|
745
|
+
if (collapse) {
|
|
746
|
+
sidebar.classList.add('collapsed');
|
|
747
|
+
content.classList.add('no-sidebar');
|
|
748
|
+
saveState('sidebar_collapsed', true);
|
|
749
|
+
} else {
|
|
750
|
+
sidebar.classList.remove('collapsed');
|
|
751
|
+
content.classList.remove('no-sidebar');
|
|
752
|
+
saveState('sidebar_collapsed', false);
|
|
753
|
+
}
|
|
754
|
+
// Trigger resize for editors
|
|
755
|
+
setTimeout(() => {
|
|
756
|
+
Object.values(currentEditors).forEach(editor => editor && editor.layout());
|
|
757
|
+
}, 300);
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
if (toggleBtn) {
|
|
761
|
+
toggleBtn.addEventListener('click', () => toggleSidebar(true));
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (expandBtn) {
|
|
765
|
+
expandBtn.addEventListener('click', () => toggleSidebar(false));
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// 2. Resize Logic
|
|
769
|
+
setupSidebarResizer(sidebar);
|
|
770
|
+
|
|
771
|
+
// 3. Collapsible Groups (top-level)
|
|
772
|
+
document.querySelectorAll('.nav-group-title').forEach(title => {
|
|
773
|
+
title.addEventListener('click', (e) => {
|
|
774
|
+
const group = e.currentTarget.parentElement;
|
|
775
|
+
group.classList.toggle('collapsed');
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
// 4. Collapsible Subgroups (nested)
|
|
780
|
+
document.querySelectorAll('.nav-subgroup-title').forEach(title => {
|
|
781
|
+
title.addEventListener('click', (e) => {
|
|
782
|
+
e.stopPropagation(); // Prevent bubbling to parent group
|
|
783
|
+
const subgroup = e.currentTarget.parentElement;
|
|
784
|
+
subgroup.classList.toggle('collapsed');
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function setupSidebarResizer(sidebar) {
|
|
790
|
+
const resizer = sidebar.querySelector('.resize-handle');
|
|
791
|
+
if (!resizer) return;
|
|
792
|
+
|
|
793
|
+
let isResizing = false;
|
|
794
|
+
let startX, startWidth;
|
|
795
|
+
|
|
796
|
+
resizer.addEventListener('mousedown', (e) => {
|
|
797
|
+
isResizing = true;
|
|
798
|
+
startX = e.clientX;
|
|
799
|
+
startWidth = sidebar.getBoundingClientRect().width;
|
|
800
|
+
|
|
801
|
+
sidebar.classList.add('resizing'); // Disable transition
|
|
802
|
+
document.body.style.cursor = 'col-resize';
|
|
803
|
+
document.body.style.userSelect = 'none';
|
|
804
|
+
e.preventDefault();
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
document.addEventListener('mousemove', (e) => {
|
|
808
|
+
if (!isResizing) return;
|
|
809
|
+
|
|
810
|
+
const newWidth = startWidth + (e.clientX - startX);
|
|
811
|
+
// Clean constraints: min 150px, max 800px
|
|
812
|
+
const clamped = Math.max(150, Math.min(800, newWidth));
|
|
813
|
+
|
|
814
|
+
sidebar.style.width = `${clamped}px`;
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
document.addEventListener('mouseup', () => {
|
|
818
|
+
if (isResizing) {
|
|
819
|
+
isResizing = false;
|
|
820
|
+
sidebar.classList.remove('resizing');
|
|
821
|
+
document.body.style.cursor = '';
|
|
822
|
+
document.body.style.userSelect = '';
|
|
823
|
+
|
|
824
|
+
saveState('sidebar_width', sidebar.getBoundingClientRect().width);
|
|
825
|
+
|
|
826
|
+
// Layout editors
|
|
827
|
+
Object.values(currentEditors).forEach(editor => editor && editor.layout());
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
479
832
|
function setupPanelResizer(container) {
|
|
480
833
|
const resizer = container.querySelector('.panel-resizer');
|
|
481
834
|
const topPanel = container.querySelector('.request-panel');
|
|
@@ -483,6 +836,10 @@ function setupPanelResizer(container) {
|
|
|
483
836
|
|
|
484
837
|
if (!resizer || !topPanel || !bottomPanel) return;
|
|
485
838
|
|
|
839
|
+
// Restore state
|
|
840
|
+
const savedSplit = getState('panel_split', 50); // Default 50%
|
|
841
|
+
topPanel.style.flex = `0 0 ${savedSplit}%`;
|
|
842
|
+
|
|
486
843
|
let isResizing = false;
|
|
487
844
|
|
|
488
845
|
resizer.addEventListener('mousedown', (e) => {
|
|
@@ -517,6 +874,13 @@ function setupPanelResizer(container) {
|
|
|
517
874
|
document.body.style.cursor = '';
|
|
518
875
|
document.body.style.userSelect = '';
|
|
519
876
|
|
|
877
|
+
// Save state
|
|
878
|
+
const currentFlex = topPanel.style.flex;
|
|
879
|
+
const match = currentFlex.match(/([\d.]+)%/);
|
|
880
|
+
if (match) {
|
|
881
|
+
saveState('panel_split', parseFloat(match[1]));
|
|
882
|
+
}
|
|
883
|
+
|
|
520
884
|
// Trigger monaco layout as size changed
|
|
521
885
|
if (currentEditors.request) currentEditors.request.layout();
|
|
522
886
|
if (currentEditors.response) currentEditors.response.layout();
|
|
@@ -609,11 +973,12 @@ function initMonaco() {
|
|
|
609
973
|
const endLine = highlights[1] || startLine;
|
|
610
974
|
|
|
611
975
|
if (startLine > 0) {
|
|
976
|
+
const highlightClass = sourceInfo.isRuntime ? 'closure-highlight-dynamic' : 'closure-highlight';
|
|
612
977
|
decorations.push({
|
|
613
978
|
range: new monaco.Range(startLine, 1, endLine, 1),
|
|
614
979
|
options: {
|
|
615
980
|
isWholeLine: true,
|
|
616
|
-
className:
|
|
981
|
+
className: highlightClass
|
|
617
982
|
}
|
|
618
983
|
});
|
|
619
984
|
currentEditors.source.revealLineInCenter(startLine);
|
|
@@ -628,6 +993,7 @@ function initMonaco() {
|
|
|
628
993
|
if (h.type === 'emit') className = 'emit-highlight';
|
|
629
994
|
else if (h.type === 'return-success') className = 'success-line-highlight';
|
|
630
995
|
else if (h.type === 'return-warning') className = 'warning-line-highlight';
|
|
996
|
+
else if (h.type === 'dynamic-path') className = 'closure-highlight-dynamic';
|
|
631
997
|
// Fallback for older 'return' type if any mixed
|
|
632
998
|
else if (h.type === 'return') className = 'warning-line-highlight';
|
|
633
999
|
|
|
@@ -904,24 +1270,4 @@ function parseMarkdown(text) {
|
|
|
904
1270
|
return marked.parse(text, { renderer });
|
|
905
1271
|
}
|
|
906
1272
|
|
|
907
|
-
function setupSidebar() {
|
|
908
|
-
const sidebar = document.querySelector('.sidebar');
|
|
909
|
-
if (!sidebar) return;
|
|
910
1273
|
|
|
911
|
-
// Collapsible Groups (top-level)
|
|
912
|
-
document.querySelectorAll('.nav-group-title').forEach(title => {
|
|
913
|
-
title.addEventListener('click', (e) => {
|
|
914
|
-
const group = e.currentTarget.parentElement;
|
|
915
|
-
group.classList.toggle('collapsed');
|
|
916
|
-
});
|
|
917
|
-
});
|
|
918
|
-
|
|
919
|
-
// Collapsible Subgroups (nested)
|
|
920
|
-
document.querySelectorAll('.nav-subgroup-title').forEach(title => {
|
|
921
|
-
title.addEventListener('click', (e) => {
|
|
922
|
-
e.stopPropagation(); // Prevent bubbling to parent group
|
|
923
|
-
const subgroup = e.currentTarget.parentElement;
|
|
924
|
-
subgroup.classList.toggle('collapsed');
|
|
925
|
-
});
|
|
926
|
-
});
|
|
927
|
-
}
|