binja 0.6.0 → 0.6.1
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/debug/index.js +427 -384
- package/dist/debug/panel.d.ts +10 -8
- package/dist/debug/panel.d.ts.map +1 -1
- package/dist/index.js +427 -384
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5284,473 +5284,516 @@ function endDebugCollection() {
|
|
|
5284
5284
|
|
|
5285
5285
|
// src/debug/panel.ts
|
|
5286
5286
|
var DEFAULT_OPTIONS = {
|
|
5287
|
-
position: "bottom
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5287
|
+
position: "bottom",
|
|
5288
|
+
height: 300,
|
|
5289
|
+
width: 400,
|
|
5290
|
+
open: false,
|
|
5291
|
+
dark: true
|
|
5291
5292
|
};
|
|
5292
5293
|
function generateDebugPanel(data, options = {}) {
|
|
5293
5294
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
5294
|
-
const id = `binja-
|
|
5295
|
-
const
|
|
5295
|
+
const id = `binja-dbg-${Date.now()}`;
|
|
5296
|
+
const c2 = opts.dark ? darkTheme : lightTheme;
|
|
5296
5297
|
return `
|
|
5297
5298
|
<!-- Binja Debug Panel -->
|
|
5298
|
-
<div id="${id}" class="binja-
|
|
5299
|
-
<style>${generateStyles(id,
|
|
5300
|
-
${
|
|
5301
|
-
|
|
5302
|
-
<script>${generateScript(id)}</script>
|
|
5299
|
+
<div id="${id}" class="binja-devtools" data-position="${opts.position}" data-open="${opts.open}">
|
|
5300
|
+
<style>${generateStyles(id, c2, opts)}</style>
|
|
5301
|
+
${generateHTML(id, data, c2, opts)}
|
|
5302
|
+
<script>${generateScript(id, data, opts)}</script>
|
|
5303
5303
|
</div>
|
|
5304
5304
|
<!-- /Binja Debug Panel -->
|
|
5305
5305
|
`;
|
|
5306
5306
|
}
|
|
5307
5307
|
var darkTheme = {
|
|
5308
|
-
bg: "#
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
text: "#
|
|
5314
|
-
textSecondary: "#
|
|
5315
|
-
textMuted: "#
|
|
5316
|
-
accent: "#
|
|
5317
|
-
accentHover: "#
|
|
5318
|
-
success: "#
|
|
5319
|
-
warning: "#
|
|
5320
|
-
error: "#
|
|
5321
|
-
info: "#
|
|
5308
|
+
bg: "#1e1e1e",
|
|
5309
|
+
bgPanel: "#252526",
|
|
5310
|
+
bgHover: "#2a2d2e",
|
|
5311
|
+
bgActive: "#37373d",
|
|
5312
|
+
border: "#3c3c3c",
|
|
5313
|
+
text: "#cccccc",
|
|
5314
|
+
textSecondary: "#969696",
|
|
5315
|
+
textMuted: "#6e6e6e",
|
|
5316
|
+
accent: "#0078d4",
|
|
5317
|
+
accentHover: "#1c86d8",
|
|
5318
|
+
success: "#4ec9b0",
|
|
5319
|
+
warning: "#dcdcaa",
|
|
5320
|
+
error: "#f14c4c",
|
|
5321
|
+
info: "#75beff",
|
|
5322
|
+
string: "#ce9178",
|
|
5323
|
+
number: "#b5cea8",
|
|
5324
|
+
keyword: "#569cd6"
|
|
5322
5325
|
};
|
|
5323
5326
|
var lightTheme = {
|
|
5324
|
-
bg: "#
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
text: "#
|
|
5330
|
-
textSecondary: "#
|
|
5331
|
-
textMuted: "#
|
|
5332
|
-
accent: "#
|
|
5333
|
-
accentHover: "#
|
|
5334
|
-
success: "#
|
|
5335
|
-
warning: "#
|
|
5336
|
-
error: "#
|
|
5337
|
-
info: "#
|
|
5327
|
+
bg: "#f3f3f3",
|
|
5328
|
+
bgPanel: "#ffffff",
|
|
5329
|
+
bgHover: "#e8e8e8",
|
|
5330
|
+
bgActive: "#d4d4d4",
|
|
5331
|
+
border: "#d4d4d4",
|
|
5332
|
+
text: "#1e1e1e",
|
|
5333
|
+
textSecondary: "#616161",
|
|
5334
|
+
textMuted: "#a0a0a0",
|
|
5335
|
+
accent: "#0078d4",
|
|
5336
|
+
accentHover: "#106ebe",
|
|
5337
|
+
success: "#16825d",
|
|
5338
|
+
warning: "#bf8803",
|
|
5339
|
+
error: "#cd3131",
|
|
5340
|
+
info: "#0078d4",
|
|
5341
|
+
string: "#a31515",
|
|
5342
|
+
number: "#098658",
|
|
5343
|
+
keyword: "#0000ff"
|
|
5338
5344
|
};
|
|
5339
5345
|
function generateStyles(id, c2, opts) {
|
|
5340
|
-
const pos = getPosition(opts.position);
|
|
5341
5346
|
return `
|
|
5342
|
-
#${id} { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
|
5347
|
+
#${id} { --bg: ${c2.bg}; --bg-panel: ${c2.bgPanel}; --bg-hover: ${c2.bgHover}; --bg-active: ${c2.bgActive}; --border: ${c2.border}; --text: ${c2.text}; --text-secondary: ${c2.textSecondary}; --text-muted: ${c2.textMuted}; --accent: ${c2.accent}; --success: ${c2.success}; --warning: ${c2.warning}; --error: ${c2.error}; --string: ${c2.string}; --number: ${c2.number}; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; font-size: 12px; line-height: 1.4; color: var(--text); }
|
|
5343
5348
|
#${id} * { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
#${id} .
|
|
5347
|
-
#${id} .
|
|
5348
|
-
#${id} .
|
|
5349
|
-
#${id} .
|
|
5350
|
-
#${id} .
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
#${id} .
|
|
5354
|
-
#${id} .
|
|
5355
|
-
#${id} .
|
|
5356
|
-
#${id} .
|
|
5357
|
-
#${id} .
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
#${id} .
|
|
5361
|
-
#${id} .
|
|
5362
|
-
#${id} .
|
|
5363
|
-
#${id} .
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
#${id} .
|
|
5367
|
-
#${id} .
|
|
5368
|
-
#${id} .
|
|
5369
|
-
#${id} .
|
|
5370
|
-
#${id} .
|
|
5371
|
-
#${id} .
|
|
5372
|
-
#${id} .
|
|
5373
|
-
#${id} .
|
|
5374
|
-
#${id} .
|
|
5375
|
-
#${id} .
|
|
5376
|
-
#${id} .
|
|
5377
|
-
#${id} .
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
#${id} .
|
|
5381
|
-
#${id} .
|
|
5382
|
-
#${id} .
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
#${id} .
|
|
5386
|
-
#${id} .
|
|
5387
|
-
#${id} .
|
|
5388
|
-
#${id} .
|
|
5389
|
-
#${id} .
|
|
5390
|
-
#${id} .
|
|
5391
|
-
#${id} .
|
|
5392
|
-
#${id} .
|
|
5393
|
-
#${id} .
|
|
5394
|
-
#${id} .
|
|
5395
|
-
#${id} .
|
|
5396
|
-
#${id} .
|
|
5397
|
-
#${id} .
|
|
5398
|
-
#${id} .
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
#${id} .
|
|
5402
|
-
#${id} .
|
|
5403
|
-
#${id} .
|
|
5404
|
-
#${id} .
|
|
5405
|
-
#${id} .
|
|
5406
|
-
#${id} .
|
|
5407
|
-
#${id} .
|
|
5408
|
-
#${id} .
|
|
5409
|
-
#${id} .
|
|
5410
|
-
#${id} .
|
|
5411
|
-
#${id} .
|
|
5412
|
-
#${id} .
|
|
5413
|
-
#${id} .
|
|
5414
|
-
#${id} .
|
|
5415
|
-
#${id} .
|
|
5416
|
-
#${id} .
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
#${id} .
|
|
5420
|
-
#${id} .
|
|
5421
|
-
#${id} .
|
|
5422
|
-
#${id} .
|
|
5423
|
-
#${id} .
|
|
5424
|
-
#${id} .
|
|
5425
|
-
#${id} .
|
|
5426
|
-
#${id} .
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
#${id} .
|
|
5430
|
-
#${id} .
|
|
5431
|
-
#${id} .
|
|
5432
|
-
#${id} .
|
|
5433
|
-
#${id} .
|
|
5434
|
-
#${id} .
|
|
5435
|
-
#${id} .
|
|
5349
|
+
|
|
5350
|
+
/* Toggle Button */
|
|
5351
|
+
#${id} .devtools-toggle { position: fixed; bottom: 10px; right: 10px; z-index: 2147483646; display: flex; align-items: center; gap: 6px; padding: 8px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--text); cursor: pointer; font-size: 11px; font-weight: 500; box-shadow: 0 2px 8px rgba(0,0,0,0.2); transition: all 0.15s; }
|
|
5352
|
+
#${id} .devtools-toggle:hover { background: var(--bg-hover); border-color: var(--accent); }
|
|
5353
|
+
#${id} .devtools-toggle svg { width: 14px; height: 14px; color: var(--accent); }
|
|
5354
|
+
#${id} .devtools-toggle .badge { padding: 2px 6px; background: var(--accent); color: #fff; border-radius: 3px; font-size: 10px; }
|
|
5355
|
+
#${id}[data-open="true"] .devtools-toggle { display: none; }
|
|
5356
|
+
|
|
5357
|
+
/* Panel Container */
|
|
5358
|
+
#${id} .devtools-panel { display: none; position: fixed; z-index: 2147483647; background: var(--bg); border: 1px solid var(--border); box-shadow: 0 -2px 12px rgba(0,0,0,0.3); }
|
|
5359
|
+
#${id}[data-open="true"] .devtools-panel { display: flex; flex-direction: column; }
|
|
5360
|
+
#${id}[data-position="bottom"] .devtools-panel { left: 0; right: 0; bottom: 0; height: ${opts.height}px; border-left: none; border-right: none; border-bottom: none; }
|
|
5361
|
+
#${id}[data-position="right"] .devtools-panel { top: 0; right: 0; bottom: 0; width: ${opts.width}px; border-top: none; border-right: none; border-bottom: none; }
|
|
5362
|
+
#${id}[data-position="popup"] .devtools-panel { bottom: 50px; right: 20px; width: 700px; height: 500px; border-radius: 8px; }
|
|
5363
|
+
|
|
5364
|
+
/* Resize Handle */
|
|
5365
|
+
#${id} .devtools-resize { position: absolute; background: transparent; }
|
|
5366
|
+
#${id}[data-position="bottom"] .devtools-resize { top: 0; left: 0; right: 0; height: 4px; cursor: ns-resize; }
|
|
5367
|
+
#${id}[data-position="right"] .devtools-resize { top: 0; left: 0; bottom: 0; width: 4px; cursor: ew-resize; }
|
|
5368
|
+
#${id} .devtools-resize:hover { background: var(--accent); }
|
|
5369
|
+
|
|
5370
|
+
/* Toolbar */
|
|
5371
|
+
#${id} .devtools-toolbar { display: flex; align-items: center; justify-content: space-between; height: 30px; padding: 0 8px; background: var(--bg-panel); border-bottom: 1px solid var(--border); flex-shrink: 0; }
|
|
5372
|
+
#${id} .devtools-tabs { display: flex; height: 100%; }
|
|
5373
|
+
#${id} .devtools-tab { display: flex; align-items: center; gap: 4px; padding: 0 12px; border: none; background: none; color: var(--text-secondary); font-size: 11px; cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.1s; }
|
|
5374
|
+
#${id} .devtools-tab:hover { color: var(--text); background: var(--bg-hover); }
|
|
5375
|
+
#${id} .devtools-tab.active { color: var(--text); border-bottom-color: var(--accent); }
|
|
5376
|
+
#${id} .devtools-tab svg { width: 12px; height: 12px; opacity: 0.7; }
|
|
5377
|
+
#${id} .devtools-tab .count { margin-left: 4px; padding: 1px 5px; background: var(--bg-active); border-radius: 8px; font-size: 10px; }
|
|
5378
|
+
#${id} .devtools-tab .count.warn { background: rgba(241,76,76,0.2); color: var(--error); }
|
|
5379
|
+
#${id} .devtools-actions { display: flex; align-items: center; gap: 4px; }
|
|
5380
|
+
#${id} .devtools-btn { display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; border: none; background: none; color: var(--text-secondary); cursor: pointer; border-radius: 3px; }
|
|
5381
|
+
#${id} .devtools-btn:hover { background: var(--bg-hover); color: var(--text); }
|
|
5382
|
+
#${id} .devtools-btn svg { width: 14px; height: 14px; }
|
|
5383
|
+
|
|
5384
|
+
/* Content Area */
|
|
5385
|
+
#${id} .devtools-content { flex: 1; overflow: hidden; }
|
|
5386
|
+
#${id} .devtools-pane { display: none; height: 100%; overflow: auto; padding: 8px; }
|
|
5387
|
+
#${id} .devtools-pane.active { display: block; }
|
|
5388
|
+
|
|
5389
|
+
/* Performance Tab */
|
|
5390
|
+
#${id} .perf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-bottom: 12px; }
|
|
5391
|
+
#${id} .perf-card { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; padding: 10px; text-align: center; }
|
|
5392
|
+
#${id} .perf-card-value { font-size: 20px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; color: var(--success); }
|
|
5393
|
+
#${id} .perf-card-label { font-size: 10px; color: var(--text-muted); margin-top: 4px; text-transform: uppercase; }
|
|
5394
|
+
#${id} .perf-breakdown { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; overflow: hidden; }
|
|
5395
|
+
#${id} .perf-row { display: flex; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--border); }
|
|
5396
|
+
#${id} .perf-row:last-child { border-bottom: none; }
|
|
5397
|
+
#${id} .perf-row-label { flex: 1; font-size: 11px; color: var(--text-secondary); }
|
|
5398
|
+
#${id} .perf-row-value { font-family: 'SF Mono', Monaco, monospace; font-size: 11px; min-width: 60px; text-align: right; }
|
|
5399
|
+
#${id} .perf-row-bar { flex: 2; height: 4px; background: var(--bg-active); border-radius: 2px; margin: 0 12px; overflow: hidden; }
|
|
5400
|
+
#${id} .perf-row-fill { height: 100%; border-radius: 2px; }
|
|
5401
|
+
#${id} .perf-row-fill.lexer { background: ${c2.info}; }
|
|
5402
|
+
#${id} .perf-row-fill.parser { background: ${c2.warning}; }
|
|
5403
|
+
#${id} .perf-row-fill.render { background: ${c2.success}; }
|
|
5404
|
+
|
|
5405
|
+
/* Context Tab - Tree View */
|
|
5406
|
+
#${id} .tree { font-family: 'SF Mono', Monaco, Consolas, monospace; font-size: 11px; }
|
|
5407
|
+
#${id} .tree-item { }
|
|
5408
|
+
#${id} .tree-row { display: flex; align-items: center; padding: 2px 4px; cursor: default; border-radius: 2px; }
|
|
5409
|
+
#${id} .tree-row:hover { background: var(--bg-hover); }
|
|
5410
|
+
#${id} .tree-row.expandable { cursor: pointer; }
|
|
5411
|
+
#${id} .tree-arrow { width: 12px; height: 12px; color: var(--text-muted); transition: transform 0.1s; flex-shrink: 0; }
|
|
5412
|
+
#${id} .tree-item.open > .tree-row .tree-arrow { transform: rotate(90deg); }
|
|
5413
|
+
#${id} .tree-key { color: var(--keyword); margin-right: 4px; }
|
|
5414
|
+
#${id} .tree-colon { color: var(--text-muted); margin-right: 6px; }
|
|
5415
|
+
#${id} .tree-value { color: var(--text); }
|
|
5416
|
+
#${id} .tree-value.string { color: var(--string); }
|
|
5417
|
+
#${id} .tree-value.number { color: var(--number); }
|
|
5418
|
+
#${id} .tree-value.null { color: var(--text-muted); font-style: italic; }
|
|
5419
|
+
#${id} .tree-type { color: var(--text-muted); margin-left: 6px; font-size: 10px; }
|
|
5420
|
+
#${id} .tree-children { display: none; padding-left: 16px; }
|
|
5421
|
+
#${id} .tree-item.open > .tree-children { display: block; }
|
|
5422
|
+
|
|
5423
|
+
/* Templates Tab */
|
|
5424
|
+
#${id} .template-list { display: flex; flex-direction: column; gap: 4px; }
|
|
5425
|
+
#${id} .template-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; }
|
|
5426
|
+
#${id} .template-icon { width: 14px; height: 14px; color: var(--text-muted); }
|
|
5427
|
+
#${id} .template-name { font-family: 'SF Mono', Monaco, monospace; font-size: 11px; }
|
|
5428
|
+
#${id} .template-badge { font-size: 9px; padding: 2px 6px; border-radius: 3px; text-transform: uppercase; font-weight: 500; }
|
|
5429
|
+
#${id} .template-badge.root { background: rgba(0,120,212,0.15); color: var(--accent); }
|
|
5430
|
+
#${id} .template-badge.extends { background: rgba(156,86,246,0.15); color: #9c56f6; }
|
|
5431
|
+
#${id} .template-badge.include { background: rgba(78,201,176,0.15); color: var(--success); }
|
|
5432
|
+
|
|
5433
|
+
/* Queries Tab */
|
|
5434
|
+
#${id} .queries-stats { display: flex; gap: 16px; padding: 8px 12px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; margin-bottom: 8px; }
|
|
5435
|
+
#${id} .queries-stat { text-align: center; }
|
|
5436
|
+
#${id} .queries-stat-value { font-size: 16px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; }
|
|
5437
|
+
#${id} .queries-stat-value.warn { color: var(--warning); }
|
|
5438
|
+
#${id} .queries-stat-value.error { color: var(--error); }
|
|
5439
|
+
#${id} .queries-stat-label { font-size: 9px; color: var(--text-muted); text-transform: uppercase; }
|
|
5440
|
+
#${id} .query-list { display: flex; flex-direction: column; gap: 4px; }
|
|
5441
|
+
#${id} .query-item { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; overflow: hidden; }
|
|
5442
|
+
#${id} .query-item.n1 { border-left: 3px solid var(--error); }
|
|
5443
|
+
#${id} .query-item.slow { border-left: 3px solid var(--warning); }
|
|
5444
|
+
#${id} .query-header { display: flex; align-items: center; gap: 8px; padding: 6px 10px; }
|
|
5445
|
+
#${id} .query-sql { flex: 1; font-family: 'SF Mono', Monaco, monospace; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text); }
|
|
5446
|
+
#${id} .query-meta { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
|
|
5447
|
+
#${id} .query-badge { font-size: 9px; padding: 2px 5px; border-radius: 3px; font-weight: 600; }
|
|
5448
|
+
#${id} .query-badge.n1 { background: rgba(241,76,76,0.15); color: var(--error); }
|
|
5449
|
+
#${id} .query-source { font-size: 9px; padding: 2px 5px; border-radius: 3px; background: var(--bg-active); color: var(--text-muted); text-transform: uppercase; }
|
|
5450
|
+
#${id} .query-time { font-family: 'SF Mono', Monaco, monospace; font-size: 10px; color: var(--text-secondary); }
|
|
5451
|
+
#${id} .query-time.slow { color: var(--warning); }
|
|
5452
|
+
#${id} .query-rows { font-size: 10px; color: var(--text-muted); }
|
|
5453
|
+
|
|
5454
|
+
/* Filters Tab */
|
|
5455
|
+
#${id} .filter-grid { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
5456
|
+
#${id} .filter-chip { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; font-family: 'SF Mono', Monaco, monospace; font-size: 11px; }
|
|
5457
|
+
#${id} .filter-count { color: var(--accent); font-weight: 600; font-size: 10px; }
|
|
5458
|
+
|
|
5459
|
+
/* Cache Tab */
|
|
5460
|
+
#${id} .cache-stats { display: flex; gap: 20px; }
|
|
5461
|
+
#${id} .cache-stat { flex: 1; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; padding: 16px; text-align: center; }
|
|
5462
|
+
#${id} .cache-value { font-size: 32px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; }
|
|
5463
|
+
#${id} .cache-value.hit { color: var(--success); }
|
|
5464
|
+
#${id} .cache-value.miss { color: var(--error); }
|
|
5465
|
+
#${id} .cache-label { font-size: 11px; color: var(--text-muted); margin-top: 4px; }
|
|
5466
|
+
|
|
5467
|
+
/* Warnings Tab */
|
|
5468
|
+
#${id} .warning-list { display: flex; flex-direction: column; gap: 6px; }
|
|
5469
|
+
#${id} .warning-item { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; background: rgba(241,76,76,0.08); border: 1px solid rgba(241,76,76,0.2); border-radius: 4px; }
|
|
5470
|
+
#${id} .warning-icon { color: var(--error); flex-shrink: 0; width: 14px; height: 14px; }
|
|
5471
|
+
#${id} .warning-text { font-size: 11px; color: var(--text); }
|
|
5472
|
+
|
|
5473
|
+
/* Empty State */
|
|
5474
|
+
#${id} .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--text-muted); font-size: 12px; }
|
|
5475
|
+
#${id} .empty-state svg { width: 32px; height: 32px; margin-bottom: 8px; opacity: 0.5; }
|
|
5436
5476
|
`;
|
|
5437
5477
|
}
|
|
5438
|
-
function getPosition(pos) {
|
|
5439
|
-
switch (pos) {
|
|
5440
|
-
case "bottom-left":
|
|
5441
|
-
return "bottom: 16px; left: 16px;";
|
|
5442
|
-
case "top-right":
|
|
5443
|
-
return "top: 16px; right: 16px;";
|
|
5444
|
-
case "top-left":
|
|
5445
|
-
return "top: 16px; left: 16px;";
|
|
5446
|
-
default:
|
|
5447
|
-
return "bottom: 16px; right: 16px;";
|
|
5448
|
-
}
|
|
5449
|
-
}
|
|
5450
5478
|
var icons = {
|
|
5451
5479
|
logo: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>`,
|
|
5452
5480
|
close: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>`,
|
|
5453
|
-
|
|
5481
|
+
minimize: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14"/></svg>`,
|
|
5482
|
+
dock: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 15h18"/></svg>`,
|
|
5483
|
+
dockRight: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M15 3v18"/></svg>`,
|
|
5484
|
+
popup: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 3v6h6"/></svg>`,
|
|
5485
|
+
arrow: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>`,
|
|
5454
5486
|
perf: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>`,
|
|
5455
|
-
template: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6"/></svg>`,
|
|
5456
5487
|
context: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z"/></svg>`,
|
|
5488
|
+
template: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6"/></svg>`,
|
|
5457
5489
|
filter: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>`,
|
|
5490
|
+
database: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>`,
|
|
5458
5491
|
cache: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a10 10 0 1010 10H12V2z"/><path d="M12 2a10 10 0 00-8.66 15"/></svg>`,
|
|
5459
|
-
warning: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg
|
|
5460
|
-
file: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"/></svg>`,
|
|
5461
|
-
database: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>`
|
|
5492
|
+
warning: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>`
|
|
5462
5493
|
};
|
|
5463
|
-
function
|
|
5494
|
+
function generateHTML(id, data, c2, opts) {
|
|
5464
5495
|
const time2 = (data.totalTime || 0).toFixed(1);
|
|
5496
|
+
const queryCount = data.queries?.length || 0;
|
|
5497
|
+
const hasWarnings = data.warnings.length > 0 || data.queryStats?.n1Count > 0;
|
|
5498
|
+
const tabs = [
|
|
5499
|
+
{ id: "perf", icon: icons.perf, label: "Performance", count: `${time2}ms` },
|
|
5500
|
+
{ id: "context", icon: icons.context, label: "Context", count: Object.keys(data.contextSnapshot).length || null },
|
|
5501
|
+
{ id: "templates", icon: icons.template, label: "Templates", count: data.templateChain.length || null },
|
|
5502
|
+
{ id: "filters", icon: icons.filter, label: "Filters", count: data.filtersUsed.size || null },
|
|
5503
|
+
{ id: "queries", icon: icons.database, label: "Queries", count: queryCount || null, warn: data.queryStats?.n1Count > 0 },
|
|
5504
|
+
{ id: "cache", icon: icons.cache, label: "Cache", count: data.cacheHits + data.cacheMisses || null },
|
|
5505
|
+
{ id: "warnings", icon: icons.warning, label: "Warnings", count: data.warnings.length || null, warn: hasWarnings }
|
|
5506
|
+
];
|
|
5507
|
+
const tabsHtml = tabs.map((t, i) => {
|
|
5508
|
+
const countHtml = t.count !== null ? `<span class="count${t.warn ? " warn" : ""}">${t.count}</span>` : "";
|
|
5509
|
+
return `<button class="devtools-tab${i === 0 ? " active" : ""}" data-tab="${t.id}">${t.icon}${t.label}${countHtml}</button>`;
|
|
5510
|
+
}).join("");
|
|
5465
5511
|
return `
|
|
5466
|
-
<button class="
|
|
5512
|
+
<button class="devtools-toggle" onclick="document.getElementById('${id}').dataset.open='true'">
|
|
5467
5513
|
${icons.logo}
|
|
5468
5514
|
<span>Binja</span>
|
|
5469
|
-
<span class="
|
|
5470
|
-
</button
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
<div class="
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
<
|
|
5481
|
-
<span class="dbg-badge time">${time2}ms</span>
|
|
5482
|
-
<button class="dbg-close" onclick="document.querySelector('#${id} .dbg-panel').classList.remove('open');document.querySelector('#${id} .dbg-toggle').style.display='inline-flex'">${icons.close}</button>
|
|
5515
|
+
<span class="badge">${time2}ms</span>
|
|
5516
|
+
</button>
|
|
5517
|
+
|
|
5518
|
+
<div class="devtools-panel">
|
|
5519
|
+
<div class="devtools-resize"></div>
|
|
5520
|
+
<div class="devtools-toolbar">
|
|
5521
|
+
<div class="devtools-tabs">${tabsHtml}</div>
|
|
5522
|
+
<div class="devtools-actions">
|
|
5523
|
+
<button class="devtools-btn" title="Dock to bottom" data-dock="bottom">${icons.dock}</button>
|
|
5524
|
+
<button class="devtools-btn" title="Dock to right" data-dock="right">${icons.dockRight}</button>
|
|
5525
|
+
<button class="devtools-btn" title="Popup" data-dock="popup">${icons.popup}</button>
|
|
5526
|
+
<button class="devtools-btn" title="Close" onclick="document.getElementById('${id}').dataset.open='false'">${icons.close}</button>
|
|
5483
5527
|
</div>
|
|
5484
5528
|
</div>
|
|
5485
|
-
<div class="
|
|
5486
|
-
${
|
|
5487
|
-
${
|
|
5488
|
-
${
|
|
5489
|
-
${
|
|
5490
|
-
${
|
|
5491
|
-
${
|
|
5492
|
-
${
|
|
5529
|
+
<div class="devtools-content">
|
|
5530
|
+
${generatePerfPane(data)}
|
|
5531
|
+
${generateContextPane(data)}
|
|
5532
|
+
${generateTemplatesPane(data)}
|
|
5533
|
+
${generateFiltersPane(data)}
|
|
5534
|
+
${generateQueriesPane(data)}
|
|
5535
|
+
${generateCachePane(data)}
|
|
5536
|
+
${generateWarningsPane(data)}
|
|
5493
5537
|
</div>
|
|
5494
5538
|
</div>`;
|
|
5495
5539
|
}
|
|
5496
|
-
function
|
|
5540
|
+
function generatePerfPane(data) {
|
|
5497
5541
|
const total = data.totalTime || 0.01;
|
|
5498
5542
|
const lexer = data.lexerTime || 0;
|
|
5499
5543
|
const parser = data.parserTime || 0;
|
|
5500
5544
|
const render = data.renderTime || 0;
|
|
5545
|
+
const mode = data.mode === "aot" ? "AOT" : "Runtime";
|
|
5501
5546
|
return `
|
|
5502
|
-
<div class="
|
|
5503
|
-
<div class="
|
|
5504
|
-
<
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
<div class="dbg-section-content">
|
|
5508
|
-
<div class="dbg-row">
|
|
5509
|
-
<span class="dbg-label">Lexer</span>
|
|
5510
|
-
<span class="dbg-value">${lexer.toFixed(2)}ms</span>
|
|
5547
|
+
<div class="devtools-pane active" data-pane="perf">
|
|
5548
|
+
<div class="perf-grid">
|
|
5549
|
+
<div class="perf-card">
|
|
5550
|
+
<div class="perf-card-value">${total.toFixed(2)}ms</div>
|
|
5551
|
+
<div class="perf-card-label">Total Time</div>
|
|
5511
5552
|
</div>
|
|
5512
|
-
<div class="
|
|
5513
|
-
|
|
5514
|
-
<
|
|
5515
|
-
<span class="dbg-value">${parser.toFixed(2)}ms</span>
|
|
5553
|
+
<div class="perf-card">
|
|
5554
|
+
<div class="perf-card-value" style="color:var(--accent)">${mode}</div>
|
|
5555
|
+
<div class="perf-card-label">Mode</div>
|
|
5516
5556
|
</div>
|
|
5517
|
-
<div class="
|
|
5518
|
-
|
|
5519
|
-
<
|
|
5520
|
-
<span class="dbg-value">${render.toFixed(2)}ms</span>
|
|
5557
|
+
<div class="perf-card">
|
|
5558
|
+
<div class="perf-card-value">${data.templateChain.length}</div>
|
|
5559
|
+
<div class="perf-card-label">Templates</div>
|
|
5521
5560
|
</div>
|
|
5522
|
-
<div class="
|
|
5523
|
-
|
|
5524
|
-
<
|
|
5525
|
-
<span class="dbg-value" style="font-weight:600">${total.toFixed(2)}ms</span>
|
|
5561
|
+
<div class="perf-card">
|
|
5562
|
+
<div class="perf-card-value">${data.queries?.length || 0}</div>
|
|
5563
|
+
<div class="perf-card-label">Queries</div>
|
|
5526
5564
|
</div>
|
|
5527
5565
|
</div>
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
<div class="
|
|
5535
|
-
|
|
5536
|
-
<
|
|
5537
|
-
<span class="
|
|
5566
|
+
<div class="perf-breakdown">
|
|
5567
|
+
<div class="perf-row">
|
|
5568
|
+
<span class="perf-row-label">Lexer</span>
|
|
5569
|
+
<div class="perf-row-bar"><div class="perf-row-fill lexer" style="width:${lexer / total * 100}%"></div></div>
|
|
5570
|
+
<span class="perf-row-value">${lexer.toFixed(2)}ms</span>
|
|
5571
|
+
</div>
|
|
5572
|
+
<div class="perf-row">
|
|
5573
|
+
<span class="perf-row-label">Parser</span>
|
|
5574
|
+
<div class="perf-row-bar"><div class="perf-row-fill parser" style="width:${parser / total * 100}%"></div></div>
|
|
5575
|
+
<span class="perf-row-value">${parser.toFixed(2)}ms</span>
|
|
5576
|
+
</div>
|
|
5577
|
+
<div class="perf-row">
|
|
5578
|
+
<span class="perf-row-label">Render</span>
|
|
5579
|
+
<div class="perf-row-bar"><div class="perf-row-fill render" style="width:${render / total * 100}%"></div></div>
|
|
5580
|
+
<span class="perf-row-value">${render.toFixed(2)}ms</span>
|
|
5538
5581
|
</div>
|
|
5539
|
-
`).join("");
|
|
5540
|
-
return `
|
|
5541
|
-
<div class="dbg-section">
|
|
5542
|
-
<div class="dbg-section-header" onclick="this.parentElement.classList.toggle('open')">
|
|
5543
|
-
<span class="dbg-section-title">${icons.template} Templates</span>
|
|
5544
|
-
<span class="dbg-section-meta">${data.templateChain.length}</span>
|
|
5545
|
-
<span class="dbg-chevron">${icons.chevron}</span>
|
|
5546
|
-
</div>
|
|
5547
|
-
<div class="dbg-section-content">
|
|
5548
|
-
<div class="dbg-templates">${templates}</div>
|
|
5549
5582
|
</div>
|
|
5550
5583
|
</div>`;
|
|
5551
5584
|
}
|
|
5552
|
-
function
|
|
5585
|
+
function generateContextPane(data) {
|
|
5553
5586
|
const keys = Object.keys(data.contextSnapshot);
|
|
5554
|
-
if (keys.length === 0)
|
|
5555
|
-
return ""
|
|
5556
|
-
const items2 = keys.map((key) => renderContextValue(key, data.contextSnapshot[key])).join("");
|
|
5557
|
-
return `
|
|
5558
|
-
<div class="dbg-section">
|
|
5559
|
-
<div class="dbg-section-header" onclick="this.parentElement.classList.toggle('open')">
|
|
5560
|
-
<span class="dbg-section-title">${icons.context} Context</span>
|
|
5561
|
-
<span class="dbg-section-meta">${keys.length} vars</span>
|
|
5562
|
-
<span class="dbg-chevron">${icons.chevron}</span>
|
|
5563
|
-
</div>
|
|
5564
|
-
<div class="dbg-section-content">
|
|
5565
|
-
<div class="dbg-ctx-grid">${items2}</div>
|
|
5566
|
-
</div>
|
|
5567
|
-
</div>`;
|
|
5568
|
-
}
|
|
5569
|
-
function renderContextValue(key, ctx) {
|
|
5570
|
-
const arrow = ctx.expandable ? `<svg class="dbg-ctx-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>` : "";
|
|
5571
|
-
const expandableClass = ctx.expandable ? "expandable" : "";
|
|
5572
|
-
const onClick = ctx.expandable ? `onclick="this.parentElement.classList.toggle('open')"` : "";
|
|
5573
|
-
let children = "";
|
|
5574
|
-
if (ctx.expandable && ctx.children) {
|
|
5575
|
-
const childItems = Object.entries(ctx.children).map(([k, v]) => renderContextValue(k, v)).join("");
|
|
5576
|
-
children = `<div class="dbg-ctx-children">${childItems}</div>`;
|
|
5587
|
+
if (keys.length === 0) {
|
|
5588
|
+
return `<div class="devtools-pane" data-pane="context"><div class="empty-state">${icons.context}<span>No context variables</span></div></div>`;
|
|
5577
5589
|
}
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
<div class="dbg-ctx-row ${expandableClass}" ${onClick}>
|
|
5581
|
-
<div class="dbg-ctx-key">
|
|
5582
|
-
${arrow}
|
|
5583
|
-
<span class="dbg-ctx-name">${escapeHtml(key)}</span>
|
|
5584
|
-
<span class="dbg-ctx-type">${ctx.type}</span>
|
|
5585
|
-
</div>
|
|
5586
|
-
<span class="dbg-ctx-preview">${escapeHtml(ctx.preview)}</span>
|
|
5587
|
-
</div>
|
|
5588
|
-
${children}
|
|
5589
|
-
</div>`;
|
|
5590
|
+
const items2 = keys.map((key) => renderTreeItem(key, data.contextSnapshot[key])).join("");
|
|
5591
|
+
return `<div class="devtools-pane" data-pane="context"><div class="tree">${items2}</div></div>`;
|
|
5590
5592
|
}
|
|
5591
|
-
function
|
|
5592
|
-
const
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
const
|
|
5593
|
+
function renderTreeItem(key, ctx) {
|
|
5594
|
+
const hasChildren = ctx.expandable && ctx.children && Object.keys(ctx.children).length > 0;
|
|
5595
|
+
const arrowHtml = hasChildren ? `<span class="tree-arrow">${icons.arrow}</span>` : '<span class="tree-arrow" style="visibility:hidden">${icons.arrow}</span>';
|
|
5596
|
+
const expandableClass = hasChildren ? "expandable" : "";
|
|
5597
|
+
const valueClass = getValueClass(ctx.type);
|
|
5598
|
+
let childrenHtml = "";
|
|
5599
|
+
if (hasChildren && ctx.children) {
|
|
5600
|
+
childrenHtml = `<div class="tree-children">${Object.entries(ctx.children).map(([k, v]) => renderTreeItem(k, v)).join("")}</div>`;
|
|
5601
|
+
}
|
|
5596
5602
|
return `
|
|
5597
|
-
<div class="
|
|
5598
|
-
<div class="
|
|
5599
|
-
|
|
5600
|
-
<span class="
|
|
5601
|
-
<span class="
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
<div class="dbg-filters">${items2}</div>
|
|
5603
|
+
<div class="tree-item">
|
|
5604
|
+
<div class="tree-row ${expandableClass}">
|
|
5605
|
+
${arrowHtml}
|
|
5606
|
+
<span class="tree-key">${escapeHtml(key)}</span>
|
|
5607
|
+
<span class="tree-colon">:</span>
|
|
5608
|
+
<span class="tree-value ${valueClass}">${escapeHtml(ctx.preview)}</span>
|
|
5609
|
+
<span class="tree-type">${ctx.type}</span>
|
|
5605
5610
|
</div>
|
|
5611
|
+
${childrenHtml}
|
|
5606
5612
|
</div>`;
|
|
5607
5613
|
}
|
|
5608
|
-
function
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
<span class="dbg-section-meta">${(data.cacheHits / total * 100).toFixed(0)}% hit</span>
|
|
5617
|
-
<span class="dbg-chevron">${icons.chevron}</span>
|
|
5618
|
-
</div>
|
|
5619
|
-
<div class="dbg-section-content">
|
|
5620
|
-
<div class="dbg-cache">
|
|
5621
|
-
<div class="dbg-cache-stat">
|
|
5622
|
-
<div class="dbg-cache-num hit">${data.cacheHits}</div>
|
|
5623
|
-
<div class="dbg-cache-label">Cache Hits</div>
|
|
5624
|
-
</div>
|
|
5625
|
-
<div class="dbg-cache-stat">
|
|
5626
|
-
<div class="dbg-cache-num miss">${data.cacheMisses}</div>
|
|
5627
|
-
<div class="dbg-cache-label">Cache Misses</div>
|
|
5628
|
-
</div>
|
|
5629
|
-
</div>
|
|
5630
|
-
</div>
|
|
5631
|
-
</div>`;
|
|
5614
|
+
function getValueClass(type) {
|
|
5615
|
+
if (type === "string")
|
|
5616
|
+
return "string";
|
|
5617
|
+
if (type === "number" || type === "integer" || type === "float")
|
|
5618
|
+
return "number";
|
|
5619
|
+
if (type === "null" || type === "undefined")
|
|
5620
|
+
return "null";
|
|
5621
|
+
return "";
|
|
5632
5622
|
}
|
|
5633
|
-
function
|
|
5634
|
-
if (data.
|
|
5635
|
-
return ""
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
<span class="
|
|
5623
|
+
function generateTemplatesPane(data) {
|
|
5624
|
+
if (data.templateChain.length === 0) {
|
|
5625
|
+
return `<div class="devtools-pane" data-pane="templates"><div class="empty-state">${icons.template}<span>No templates loaded</span></div></div>`;
|
|
5626
|
+
}
|
|
5627
|
+
const items2 = data.templateChain.map((t) => `
|
|
5628
|
+
<div class="template-item">
|
|
5629
|
+
<span class="template-icon">${icons.template}</span>
|
|
5630
|
+
<span class="template-name">${escapeHtml(t.name)}</span>
|
|
5631
|
+
<span class="template-badge ${t.type}">${t.type}</span>
|
|
5640
5632
|
</div>
|
|
5641
5633
|
`).join("");
|
|
5642
|
-
return
|
|
5643
|
-
<div class="dbg-section open">
|
|
5644
|
-
<div class="dbg-section-header" onclick="this.parentElement.classList.toggle('open')">
|
|
5645
|
-
<span class="dbg-section-title">${icons.warning} Warnings</span>
|
|
5646
|
-
<span class="dbg-section-meta" style="color:#eab308">${data.warnings.length}</span>
|
|
5647
|
-
<span class="dbg-chevron">${icons.chevron}</span>
|
|
5648
|
-
</div>
|
|
5649
|
-
<div class="dbg-section-content">
|
|
5650
|
-
<div class="dbg-warnings">${items2}</div>
|
|
5651
|
-
</div>
|
|
5652
|
-
</div>`;
|
|
5634
|
+
return `<div class="devtools-pane" data-pane="templates"><div class="template-list">${items2}</div></div>`;
|
|
5653
5635
|
}
|
|
5654
|
-
function
|
|
5655
|
-
|
|
5656
|
-
|
|
5636
|
+
function generateFiltersPane(data) {
|
|
5637
|
+
const filters = Array.from(data.filtersUsed.entries());
|
|
5638
|
+
if (filters.length === 0) {
|
|
5639
|
+
return `<div class="devtools-pane" data-pane="filters"><div class="empty-state">${icons.filter}<span>No filters used</span></div></div>`;
|
|
5640
|
+
}
|
|
5641
|
+
const items2 = filters.map(([name, count]) => `
|
|
5642
|
+
<span class="filter-chip">${escapeHtml(name)}<span class="filter-count">\xD7${count}</span></span>
|
|
5643
|
+
`).join("");
|
|
5644
|
+
return `<div class="devtools-pane" data-pane="filters"><div class="filter-grid">${items2}</div></div>`;
|
|
5645
|
+
}
|
|
5646
|
+
function generateQueriesPane(data) {
|
|
5647
|
+
if (!data.queries || data.queries.length === 0) {
|
|
5648
|
+
return `<div class="devtools-pane" data-pane="queries"><div class="empty-state">${icons.database}<span>No queries recorded</span></div></div>`;
|
|
5649
|
+
}
|
|
5657
5650
|
const stats = data.queryStats;
|
|
5658
|
-
const hasIssues = stats.slowCount > 0 || stats.n1Count > 0;
|
|
5659
5651
|
const statsHtml = `
|
|
5660
|
-
<div class="
|
|
5661
|
-
<div class="
|
|
5662
|
-
<div class="
|
|
5663
|
-
<div class="
|
|
5652
|
+
<div class="queries-stats">
|
|
5653
|
+
<div class="queries-stat">
|
|
5654
|
+
<div class="queries-stat-value">${stats.count}</div>
|
|
5655
|
+
<div class="queries-stat-label">Queries</div>
|
|
5664
5656
|
</div>
|
|
5665
|
-
<div class="
|
|
5666
|
-
<div class="
|
|
5667
|
-
<div class="
|
|
5657
|
+
<div class="queries-stat">
|
|
5658
|
+
<div class="queries-stat-value">${stats.totalDuration.toFixed(1)}ms</div>
|
|
5659
|
+
<div class="queries-stat-label">Total</div>
|
|
5668
5660
|
</div>
|
|
5669
|
-
<div class="
|
|
5670
|
-
<div class="
|
|
5671
|
-
<div class="
|
|
5661
|
+
<div class="queries-stat">
|
|
5662
|
+
<div class="queries-stat-value ${stats.slowCount > 0 ? "warn" : ""}">${stats.slowCount}</div>
|
|
5663
|
+
<div class="queries-stat-label">Slow</div>
|
|
5672
5664
|
</div>
|
|
5673
|
-
<div class="
|
|
5674
|
-
<div class="
|
|
5675
|
-
<div class="
|
|
5665
|
+
<div class="queries-stat">
|
|
5666
|
+
<div class="queries-stat-value ${stats.n1Count > 0 ? "error" : ""}">${stats.n1Count}</div>
|
|
5667
|
+
<div class="queries-stat-label">N+1</div>
|
|
5676
5668
|
</div>
|
|
5677
5669
|
</div>`;
|
|
5678
5670
|
const queries = data.queries.map((q) => {
|
|
5679
5671
|
const isSlow = q.duration > 100;
|
|
5680
|
-
const classes = [
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
].filter(Boolean).join(" ");
|
|
5685
|
-
const badges = [];
|
|
5686
|
-
if (q.isN1) {
|
|
5687
|
-
badges.push('<span class="dbg-query-badge n1">N+1</span>');
|
|
5688
|
-
}
|
|
5689
|
-
const rowsText = q.rows !== undefined ? `<span class="dbg-query-rows">${q.rows} rows</span>` : "";
|
|
5690
|
-
const sourceText = q.source ? `<span class="dbg-query-source">${escapeHtml(q.source)}</span>` : "";
|
|
5672
|
+
const classes = ["query-item", q.isN1 ? "n1" : "", isSlow ? "slow" : ""].filter(Boolean).join(" ");
|
|
5673
|
+
const badge = q.isN1 ? '<span class="query-badge n1">N+1</span>' : "";
|
|
5674
|
+
const source = q.source ? `<span class="query-source">${escapeHtml(q.source)}</span>` : "";
|
|
5675
|
+
const rows = q.rows !== undefined ? `<span class="query-rows">${q.rows} rows</span>` : "";
|
|
5691
5676
|
return `
|
|
5692
5677
|
<div class="${classes}">
|
|
5693
|
-
<div class="
|
|
5694
|
-
<span class="
|
|
5695
|
-
<div class="
|
|
5696
|
-
${
|
|
5697
|
-
${
|
|
5698
|
-
${rowsText}
|
|
5699
|
-
<span class="dbg-query-time ${isSlow ? "slow" : ""}">${q.duration.toFixed(1)}ms</span>
|
|
5678
|
+
<div class="query-header">
|
|
5679
|
+
<span class="query-sql" title="${escapeHtml(q.sql)}">${escapeHtml(q.sql)}</span>
|
|
5680
|
+
<div class="query-meta">
|
|
5681
|
+
${badge}${source}${rows}
|
|
5682
|
+
<span class="query-time ${isSlow ? "slow" : ""}">${q.duration.toFixed(1)}ms</span>
|
|
5700
5683
|
</div>
|
|
5701
5684
|
</div>
|
|
5702
5685
|
</div>`;
|
|
5703
5686
|
}).join("");
|
|
5704
|
-
|
|
5687
|
+
return `<div class="devtools-pane" data-pane="queries">${statsHtml}<div class="query-list">${queries}</div></div>`;
|
|
5688
|
+
}
|
|
5689
|
+
function generateCachePane(data) {
|
|
5690
|
+
const total = data.cacheHits + data.cacheMisses;
|
|
5691
|
+
if (total === 0) {
|
|
5692
|
+
return `<div class="devtools-pane" data-pane="cache"><div class="empty-state">${icons.cache}<span>No cache activity</span></div></div>`;
|
|
5693
|
+
}
|
|
5694
|
+
const hitRate = (data.cacheHits / total * 100).toFixed(0);
|
|
5705
5695
|
return `
|
|
5706
|
-
<div class="
|
|
5707
|
-
<div class="
|
|
5708
|
-
<
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5696
|
+
<div class="devtools-pane" data-pane="cache">
|
|
5697
|
+
<div class="cache-stats">
|
|
5698
|
+
<div class="cache-stat">
|
|
5699
|
+
<div class="cache-value hit">${data.cacheHits}</div>
|
|
5700
|
+
<div class="cache-label">Cache Hits</div>
|
|
5701
|
+
</div>
|
|
5702
|
+
<div class="cache-stat">
|
|
5703
|
+
<div class="cache-value miss">${data.cacheMisses}</div>
|
|
5704
|
+
<div class="cache-label">Cache Misses</div>
|
|
5705
|
+
</div>
|
|
5706
|
+
<div class="cache-stat">
|
|
5707
|
+
<div class="cache-value">${hitRate}%</div>
|
|
5708
|
+
<div class="cache-label">Hit Rate</div>
|
|
5709
|
+
</div>
|
|
5715
5710
|
</div>
|
|
5716
5711
|
</div>`;
|
|
5717
5712
|
}
|
|
5718
|
-
function
|
|
5713
|
+
function generateWarningsPane(data) {
|
|
5714
|
+
if (data.warnings.length === 0) {
|
|
5715
|
+
return `<div class="devtools-pane" data-pane="warnings"><div class="empty-state">${icons.warning}<span>No warnings</span></div></div>`;
|
|
5716
|
+
}
|
|
5717
|
+
const items2 = data.warnings.map((w) => `
|
|
5718
|
+
<div class="warning-item">
|
|
5719
|
+
<span class="warning-icon">${icons.warning}</span>
|
|
5720
|
+
<span class="warning-text">${escapeHtml(w)}</span>
|
|
5721
|
+
</div>
|
|
5722
|
+
`).join("");
|
|
5723
|
+
return `<div class="devtools-pane" data-pane="warnings"><div class="warning-list">${items2}</div></div>`;
|
|
5724
|
+
}
|
|
5725
|
+
function generateScript(id, data, opts) {
|
|
5719
5726
|
return `
|
|
5720
|
-
(function(){
|
|
5721
|
-
var
|
|
5722
|
-
if (!
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
};
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5727
|
+
(function() {
|
|
5728
|
+
var root = document.getElementById('${id}');
|
|
5729
|
+
if (!root) return;
|
|
5730
|
+
|
|
5731
|
+
// Tab switching
|
|
5732
|
+
root.querySelectorAll('.devtools-tab').forEach(function(tab) {
|
|
5733
|
+
tab.addEventListener('click', function() {
|
|
5734
|
+
root.querySelectorAll('.devtools-tab').forEach(function(t) { t.classList.remove('active'); });
|
|
5735
|
+
root.querySelectorAll('.devtools-pane').forEach(function(p) { p.classList.remove('active'); });
|
|
5736
|
+
tab.classList.add('active');
|
|
5737
|
+
var pane = root.querySelector('[data-pane="' + tab.dataset.tab + '"]');
|
|
5738
|
+
if (pane) pane.classList.add('active');
|
|
5739
|
+
});
|
|
5740
|
+
});
|
|
5741
|
+
|
|
5742
|
+
// Tree expand/collapse
|
|
5743
|
+
root.querySelectorAll('.tree-row.expandable').forEach(function(row) {
|
|
5744
|
+
row.addEventListener('click', function() {
|
|
5745
|
+
row.parentElement.classList.toggle('open');
|
|
5746
|
+
});
|
|
5747
|
+
});
|
|
5748
|
+
|
|
5749
|
+
// Dock position switching
|
|
5750
|
+
root.querySelectorAll('[data-dock]').forEach(function(btn) {
|
|
5751
|
+
btn.addEventListener('click', function() {
|
|
5752
|
+
root.dataset.position = btn.dataset.dock;
|
|
5753
|
+
});
|
|
5754
|
+
});
|
|
5755
|
+
|
|
5756
|
+
// Resize functionality
|
|
5757
|
+
var resize = root.querySelector('.devtools-resize');
|
|
5758
|
+
var panel = root.querySelector('.devtools-panel');
|
|
5759
|
+
if (resize && panel) {
|
|
5760
|
+
var isResizing = false;
|
|
5761
|
+
var startY, startX, startHeight, startWidth;
|
|
5762
|
+
|
|
5763
|
+
resize.addEventListener('mousedown', function(e) {
|
|
5764
|
+
isResizing = true;
|
|
5765
|
+
startY = e.clientY;
|
|
5766
|
+
startX = e.clientX;
|
|
5767
|
+
startHeight = panel.offsetHeight;
|
|
5768
|
+
startWidth = panel.offsetWidth;
|
|
5769
|
+
document.body.style.cursor = root.dataset.position === 'right' ? 'ew-resize' : 'ns-resize';
|
|
5770
|
+
e.preventDefault();
|
|
5771
|
+
});
|
|
5772
|
+
|
|
5773
|
+
document.addEventListener('mousemove', function(e) {
|
|
5774
|
+
if (!isResizing) return;
|
|
5775
|
+
if (root.dataset.position === 'bottom') {
|
|
5776
|
+
var newHeight = startHeight - (e.clientY - startY);
|
|
5777
|
+
if (newHeight > 100 && newHeight < window.innerHeight * 0.8) {
|
|
5778
|
+
panel.style.height = newHeight + 'px';
|
|
5779
|
+
}
|
|
5780
|
+
} else if (root.dataset.position === 'right') {
|
|
5781
|
+
var newWidth = startWidth - (e.clientX - startX);
|
|
5782
|
+
if (newWidth > 200 && newWidth < window.innerWidth * 0.6) {
|
|
5783
|
+
panel.style.width = newWidth + 'px';
|
|
5784
|
+
}
|
|
5785
|
+
}
|
|
5786
|
+
});
|
|
5787
|
+
|
|
5788
|
+
document.addEventListener('mouseup', function() {
|
|
5789
|
+
isResizing = false;
|
|
5790
|
+
document.body.style.cursor = '';
|
|
5791
|
+
});
|
|
5792
|
+
}
|
|
5750
5793
|
})();`;
|
|
5751
5794
|
}
|
|
5752
5795
|
function escapeHtml(str) {
|
|
5753
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5796
|
+
return String(str).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5754
5797
|
}
|
|
5755
5798
|
|
|
5756
5799
|
// src/debug/index.ts
|