nestjs-devtools 1.3.2 → 1.5.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.
@@ -3,17 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PROFILER_PAGE = void 0;
4
4
  const PROFILER_PAGE = (config) => {
5
5
  const { theme = 'dark' } = config ?? {};
6
- const dark = theme === 'dark';
7
- const bg = dark ? '#111827' : '#f8fafc';
8
- const fg = dark ? '#f3f4f6' : '#1f2937';
9
- const cardBg = dark ? '#1f2937' : '#ffffff';
10
- const borderClr = dark ? '#374151' : '#e5e7eb';
11
- const headerBg = dark ? '#1f2937' : '#ffffff';
12
- const dimText = dark ? '#9ca3af' : '#6b7280';
13
- const dimmerText = dark ? '#6b7280' : '#9ca3af';
14
- const accentText = dark ? '#60a5fa' : '#2563eb';
15
- const navBg = dark ? '#1f2937' : '#f1f5f9';
16
- const navFg = dark ? '#d1d5db' : '#475569';
17
6
  return `<!DOCTYPE html>
18
7
  <html lang="en">
19
8
  <head>
@@ -22,187 +11,240 @@ const PROFILER_PAGE = (config) => {
22
11
  <title>NestJS Devtools Profiler</title>
23
12
  <style>
24
13
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
14
+ html, body { height: 100%; }
25
15
  body {
26
- font-family: -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
27
- background: ${bg}; color: ${fg}; font-size: 13px; line-height: 1.5;
28
- min-height: 100vh;
16
+ font-family: -apple-system,BlinkMacSystemFont,sans-serif;
17
+ background: #f5f5f5; color: #222; font-size: 13px; line-height: 1.5;
29
18
  }
30
- .header {
31
- background: ${headerBg}; border-bottom: 2px solid #3b82f6;
32
- padding: 16px 24px; display: flex; align-items: center; gap: 12px;
19
+ .toolbar {
20
+ background: #1a1a1a; color: #ccc; display: flex; align-items: center;
21
+ height: 30px; padding: 0 10px; font-size: 11px;
22
+ box-shadow: 0 1px 4px rgba(0,0,0,0.3); position: sticky; top: 0; z-index: 100;
33
23
  }
34
- .header h1 { font-size: 18px; font-weight: 700; color: ${accentText}; }
35
- .header span { color: ${dimText}; font-size: 13px; }
36
- .container { max-width: 1200px; margin: 0 auto; padding: 24px; }
37
- .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-bottom: 24px; }
38
- .card {
39
- background: ${cardBg}; border-radius: 8px; border: 1px solid ${borderClr};
40
- overflow: hidden; cursor: pointer; transition: border-color .15s;
24
+ .toolbar .brand { font-weight: 700; font-size: 12px; color: #888; padding: 0 12px 0 4px; }
25
+ .toolbar .title { color: #aaa; font-size: 11px; }
26
+ .toolbar .nav { margin-left: auto; display: flex; align-items: center; height: 100%; }
27
+ .toolbar .nav a {
28
+ display: inline-flex; align-items: center; height: 100%; padding: 0 10px;
29
+ color: #888; text-decoration: none; font-size: 11px; cursor: pointer;
30
+ border-left: 1px solid #333; transition: background .1s;
41
31
  }
42
- .card:hover { border-color: #3b82f6; }
43
- .card-header {
44
- padding: 12px 16px; display: flex; align-items: center; gap: 10px;
45
- border-bottom: 1px solid ${borderClr};
32
+ .toolbar .nav a:hover { background: #333; color: #ddd; }
33
+ .toolbar .nav a.active { color: #fff; background: #333; }
34
+ .layout { display: flex; min-height: calc(100vh - 30px); }
35
+ .sidebar {
36
+ width: 240px; background: #fff; border-right: 1px solid #ddd;
37
+ padding: 16px; flex-shrink: 0; font-size: 12px;
46
38
  }
47
- .card-header .icon { font-size: 18px; }
48
- .card-header .label { font-weight: 600; font-size: 13px; }
49
- .card-header .summary { color: ${dimText}; font-size: 11px; margin-left: auto; }
50
- .card-body { padding: 0; max-height: 0; overflow: hidden; transition: max-height .2s, padding .2s; }
51
- .card-body.open { max-height: 2000px; padding: 12px 16px; overflow-y: auto; }
52
- .card-body table { width: 100%; border-collapse: collapse; font-size: 12px; }
53
- .card-body td, .card-body th {
54
- padding: 6px 8px; border-bottom: 1px solid ${borderClr}; text-align: left;
55
- vertical-align: top; word-break: break-word;
39
+ .sidebar h3 { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; color: #999; margin-bottom: 10px; }
40
+ .sidebar .metric { display: flex; justify-content: space-between; padding: 4px 0; font-size: 12px; }
41
+ .sidebar .metric .label { color: #888; }
42
+ .sidebar .metric .value { color: #222; font-weight: 500; text-align: right; }
43
+ .sidebar .section { margin-bottom: 16px; }
44
+ .sidebar .badge { display: inline-block; padding: 1px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; }
45
+ .badge-get { background: #d4edda; color: #155724; }
46
+ .badge-post { background: #cce5ff; color: #004085; }
47
+ .badge-put { background: #fff3cd; color: #856404; }
48
+ .badge-delete { background: #f8d7da; color: #721c24; }
49
+ .badge-patch { background: #e8daef; color: #6c3483; }
50
+ .main { flex: 1; padding: 20px; }
51
+ .panel { background: #fff; border: 1px solid #ddd; margin-bottom: 12px; border-radius: 0; }
52
+ .panel-header {
53
+ padding: 8px 12px; display: flex; align-items: center; gap: 8px;
54
+ cursor: pointer; user-select: none; font-size: 12px; font-weight: 600;
55
+ border-bottom: 1px solid #eee; color: #333;
56
56
  }
57
- .card-body th { color: ${dimText}; font-weight: 600; width: 120px; }
58
- .badge {
59
- display: inline-block; padding: 2px 8px; border-radius: 4px;
60
- font-size: 11px; font-weight: 600;
61
- }
62
- .badge-get { background: #065f46; color: #6ee7b7; }
63
- .badge-post { background: #1e3a5f; color: #93c5fd; }
64
- .badge-put { background: #713f12; color: #fcd34d; }
65
- .badge-delete { background: #7f1d1d; color: #fca5a5; }
66
- .badge-patch { background: #581c87; color: #d8b4fe; }
67
- .empty { padding: 24px; text-align: center; color: ${dimmerText}; }
68
- .mono { font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace; font-size: 11px; }
69
- .history-table { width: 100%; border-collapse: collapse; margin-top: 16px; }
70
- .history-table th { padding: 8px 12px; text-align: left; color: ${dimText}; font-weight: 600; border-bottom: 2px solid ${borderClr}; }
71
- .history-table td { padding: 8px 12px; border-bottom: 1px solid ${borderClr}; }
72
- .history-table tr:hover td { background: ${cardBg}; }
73
- .error-text { color: #ef4444; }
74
- .warn-text { color: #f59e0b; }
75
- .ok-text { color: #10b981; }
76
- .nav { display: flex; gap: 8px; margin-bottom: 24px; }
77
- .nav button {
78
- padding: 8px 16px; border-radius: 6px; border: 1px solid ${borderClr};
79
- background: ${navBg}; color: ${navFg}; cursor: pointer; font-size: 13px;
80
- }
81
- .nav button.active { border-color: #3b82f6; color: ${accentText}; background: ${dark ? '#1e3a5f' : '#dbeafe'}; }
82
- .nav button:hover { background: ${dark ? '#374151' : '#e2e8f0'}; }
83
- .section { display: none; }
84
- .section.active { display: block; }
85
- .qlabel { display: inline-block; padding: 1px 6px; border-radius: 3px; font-size: 10px; font-weight: 700; margin-right: 4px; }
86
- .qlabel-1 { background: #3b82f6; color: #fff; }
87
- .qlabel-2 { background: #8b5cf6; color: #fff; }
88
- .qlabel-3 { background: #10b981; color: #fff; }
57
+ .panel-header:hover { background: #fafafa; }
58
+ .panel-header .summary { color: #999; font-weight: 400; font-size: 11px; margin-left: auto; }
59
+ .panel-body { display: none; padding: 10px 12px 12px; font-size: 12px; }
60
+ .panel-body.open { display: block; }
61
+ .panel-body table { width: 100%; border-collapse: collapse; }
62
+ .panel-body td, .panel-body th { padding: 4px 8px; text-align: left; vertical-align: top; border-bottom: 1px solid #eee; font-size: 12px; }
63
+ .panel-body th { color: #888; font-weight: 500; width: 100px; }
64
+ .panel-body tr:last-child td, .panel-body tr:last-child th { border-bottom: none; }
65
+ .empty { color: #bbb; font-size: 12px; }
66
+ .mono { font-family: SF Mono,Menlo,monospace; font-size: 11px; }
67
+ .query-table { width: 100%; }
68
+ .query-row { display: flex; align-items: flex-start; padding: 5px 0; gap: 6px; border-bottom: 1px solid #eee; font-size: 11px; }
69
+ .query-row:last-child { border-bottom: none; }
70
+ .query-num { color: #999; width: 22px; flex-shrink: 0; }
71
+ .query-sql { font-family: SF Mono,Menlo,monospace; word-break: break-all; flex: 1; }
72
+ .query-time { color: #b8860b; white-space: nowrap; font-family: SF Mono,Menlo,monospace; flex-shrink: 0; }
73
+ .log-row { display: flex; align-items: flex-start; gap: 8px; padding: 3px 0; border-bottom: 1px solid #eee; font-size: 11px; }
74
+ .log-row:last-child { border-bottom: none; }
75
+ .log-level { width: 44px; font-weight: 600; font-size: 10px; text-transform: uppercase; flex-shrink: 0; }
76
+ .log-error { color: #e74c3c; }
77
+ .log-warn { color: #f39c12; }
78
+ .log-info { color: #888; }
79
+ .route-group { padding: 4px 0; }
80
+ .route-group-header { font-weight: 600; font-size: 11px; color: #2e8b57; padding: 3px 0; }
81
+ .route-item { padding: 1px 0 1px 14px; font-family: SF Mono,Menlo,monospace; font-size: 11px; }
82
+ .route-controller { color: #999; }
83
+ .exception-box { background: #fdf0ef; border-radius: 3px; padding: 8px; }
84
+ .exception-box .msg { color: #e74c3c; font-weight: 600; font-size: 12px; margin-bottom: 4px; }
85
+ .exception-box .stack { font-family: SF Mono,Menlo,monospace; font-size: 11px; color: #888; white-space: pre-wrap; line-height: 1.4; }
86
+ .field-row { display: flex; padding: 3px 0; font-size: 11px; }
87
+ .field-label { color: #888; width: 90px; flex-shrink: 0; }
88
+ .field-value { word-break: break-all; }
89
+ .error-text { color: #e74c3c; font-weight: 600; }
90
+ .warn-text { color: #f39c12; font-weight: 600; }
91
+ .ok-text { color: #27ae60; font-weight: 600; }
92
+ .status-badge { display: inline-block; padding: 1px 6px; border-radius: 2px; font-size: 11px; font-weight: 600; }
93
+ .status-200 { background: #d4edda; color: #155724; }
94
+ .status-300 { background: #fff3cd; color: #856404; }
95
+ .status-400 { background: #f8d7da; color: #721c24; }
96
+ .status-500 { background: #f8d7da; color: #721c24; }
89
97
  </style>
90
98
  </head>
91
99
  <body>
92
- <div class="header">
93
- <h1>NDev</h1>
94
- <span>NestJS Devtools Profiler</span>
95
- </div>
96
- <div class="container">
100
+ <div class="toolbar">
101
+ <span class="brand">ND</span>
102
+ <span class="title">NestJS Devtools Profiler</span>
97
103
  <div class="nav">
98
- <button class="active" data-tab="dashboard">Dashboard</button>
99
- <button data-tab="history">History</button>
104
+ <a class="active" data-tab="dashboard">Dashboard</a>
105
+ <a data-tab="history">History</a>
100
106
  </div>
101
- <div id="tab-dashboard" class="section active">
102
- <div class="grid" id="panel-grid"></div>
107
+ </div>
108
+ <div class="layout">
109
+ <div class="sidebar" id="sidebar">
110
+ <div class="section">
111
+ <h3>Request</h3>
112
+ <div id="sidebar-metrics"></div>
113
+ </div>
103
114
  </div>
104
- <div id="tab-history" class="section">
105
- <table class="history-table">
106
- <thead><tr><th>Time</th><th>Method</th><th>Path</th><th>Status</th><th>Duration</th></tr></thead>
107
- <tbody id="history-body"></tbody>
108
- </table>
115
+ <div class="main">
116
+ <div id="tab-dashboard" class="tab-content">
117
+ <div id="panel-list"></div>
118
+ </div>
119
+ <div id="tab-history" class="tab-content" style="display:none">
120
+ <div class="panel">
121
+ <table style="width:100%;border-collapse:collapse;font-size:12px">
122
+ <thead><tr style="background:#fafafa"><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Time</th><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Method</th><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Path</th><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Status</th><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Duration</th></tr></thead>
123
+ <tbody id="history-body"></tbody>
124
+ </table>
125
+ </div>
126
+ </div>
109
127
  </div>
110
128
  </div>
111
129
  <script>
112
- (function() {
130
+ (function(){
113
131
  var BASE = window.location.pathname.replace(/\\/__devtools\\/?$/, '');
132
+ var activeTab = 'dashboard';
133
+ var panelData = {};
134
+
114
135
  function escape(s) { return s==null?'':String(s).replace(/[&<>"]/g,function(m){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[m]}); }
115
136
 
116
- document.querySelectorAll('[data-tab]').forEach(function(btn){
117
- btn.addEventListener('click', function(){
137
+ document.querySelectorAll('[data-tab]').forEach(function(el){
138
+ el.addEventListener('click', function(){
118
139
  document.querySelectorAll('[data-tab]').forEach(function(b){b.classList.remove('active')});
119
- document.querySelectorAll('.section').forEach(function(s){s.classList.remove('active')});
120
- btn.classList.add('active');
121
- document.getElementById('tab-'+btn.getAttribute('data-tab')).classList.add('active');
122
- if (btn.getAttribute('data-tab')==='history') loadHistory();
140
+ document.querySelectorAll('.tab-content').forEach(function(c){c.style.display='none'});
141
+ el.classList.add('active');
142
+ activeTab = el.getAttribute('data-tab');
143
+ document.getElementById('tab-'+activeTab).style.display='';
144
+ if (activeTab==='history') loadHistory();
123
145
  });
124
146
  });
125
147
 
126
- function renderDetails(detail) {
148
+ function renderPanelContent(detail) {
127
149
  if (!detail || !detail.details) return '<div class="empty">No data</div>';
128
150
  var d = detail.details;
129
151
  var h = '<table>';
130
152
  for (var k in d) {
131
153
  var v = d[k];
132
- if (k === 'queries' && Array.isArray(v)) {
133
- h += '<tr><th>Queries</th><td>';
154
+ if (k==='queries' && Array.isArray(v)) {
155
+ if (v.length===0) { h+='<tr><td colspan="2" style="padding:6px 0"><span class="empty">No queries</span></td></tr>'; continue; }
156
+ h+='<tr><th>Queries</th><td>';
134
157
  v.forEach(function(q,i){
135
- var cls = 'qlabel-'+((i%3)+1);
136
- h += '<div style="padding:4px 0;border-bottom:1px solid #1f2937">';
137
- h += '<span class="qlabel '+cls+'">#'+(i+1)+'</span>';
138
- h += '<span class="mono">'+escape(q.sql||'')+'</span>';
139
- h += '<span style="float:right;color:#fbbf24">'+escape(q.duration||'')+'</span>';
140
- h += '</div>';
158
+ h+='<div class="query-row"><span class="query-num">'+(i+1)+'</span><span class="query-sql">'+escape(q.sql||'')+'</span><span class="query-time">'+escape(q.duration||'')+'</span></div>';
141
159
  });
142
- h += '</td></tr>';
160
+ h+='</td></tr>';
143
161
  continue;
144
162
  }
145
- if (k === 'entries' && Array.isArray(v)) {
146
- h += '<tr><th>Logs</th><td>';
163
+ if (k==='entries' && Array.isArray(v)) {
164
+ if (v.length===0) { h+='<tr><td colspan="2" style="padding:6px 0"><span class="empty">No entries</span></td></tr>'; continue; }
165
+ h+='<tr><th>Logs</th><td>';
147
166
  v.forEach(function(e){
148
- var lc = e.level==='error' ? 'error-text' : e.level==='warn' ? 'warn-text' : '';
149
- h += '<div style="padding:3px 0;border-bottom:1px solid #1f2937">';
150
- h += '<span class="badge '+lc+'" style="background:#1f2937;text-transform:uppercase;font-size:10px;margin-right:6px">'+escape(e.level||'')+'</span>';
151
- h += '<span>'+escape(e.message||'')+'</span>';
152
- h += '</div>';
167
+ var lc = e.level==='error' ? 'log-error' : e.level==='warn' ? 'log-warn' : 'log-info';
168
+ h+='<div class="log-row"><span class="log-level '+lc+'">'+escape(e.level||'')+'</span><span>'+escape(e.message||'')+'</span></div>';
153
169
  });
154
- h += '</td></tr>';
170
+ h+='</td></tr>';
155
171
  continue;
156
172
  }
157
- if (k === 'grouped' && Array.isArray(v)) {
158
- h += '<tr><th>Routes</th><td>';
173
+ if (k==='grouped' && Array.isArray(v)) {
174
+ if (v.length===0) { h+='<tr><td colspan="2" style="padding:6px 0"><span class="empty">No routes</span></td></tr>'; continue; }
175
+ h+='<tr><th>Routes</th><td>';
159
176
  v.forEach(function(g){
160
- h += '<div style="font-weight:600;color:#60a5fa;padding:4px 0">'+escape(g.method||'')+' ('+(g.count||0)+')</div>';
177
+ h+='<div class="route-group"><div class="route-group-header">'+escape(g.method||'')+' <span style="color:#999;font-weight:400">('+(g.count||0)+')</span></div>';
161
178
  if (g.routes) g.routes.forEach(function(r){
162
- h += '<div style="padding:2px 0 2px 16px;font-family:monospace;font-size:11px">';
163
- h += escape(r.path||'')+' <span style="color:#6b7280">('+escape(r.controller||'')+')</span></div>';
179
+ h+='<div class="route-item">'+escape(r.path||'')+' <span class="route-controller">('+escape(r.controller||'')+')</span></div>';
164
180
  });
181
+ h+='</div>';
165
182
  });
166
- h += '</td></tr>';
183
+ h+='</td></tr>';
167
184
  continue;
168
185
  }
169
- if (k === 'exception' && v && typeof v === 'object') {
170
- h += '<tr><th>Exception</th><td>';
171
- h += '<div class="error-text" style="font-weight:600">'+escape(v.message||'')+'</div>';
172
- h += '<pre class="mono" style="margin-top:6px;color:#9ca3af;white-space:pre-wrap">'+escape(v.stack||'')+'</pre>';
173
- h += '</td></tr>';
186
+ if (k==='exception' && v && typeof v==='object') {
187
+ h+='<tr><td colspan="2"><div class="exception-box"><div class="msg">'+escape(v.message||'')+'</div><div class="stack">'+escape(v.stack||'')+'</div></div></td></tr>';
174
188
  continue;
175
189
  }
176
- if (k === 'stack') continue;
177
- h += '<tr><th>'+escape(k)+'</th><td>'+escape(typeof v==='object'?JSON.stringify(v):v)+'</td></tr>';
190
+ if (k==='stack') continue;
191
+ h+='<tr><th>'+escape(k)+'</th><td class="mono">'+escape(typeof v==='object'?JSON.stringify(v):v)+'</td></tr>';
178
192
  }
179
- h += '</table>';
193
+ h+='</table>';
180
194
  return h;
181
195
  }
182
196
 
183
197
  function loadDashboard() {
184
198
  fetch(BASE + '/__devtools/data').then(function(r){return r.json()}).then(function(d){
185
- var grid = document.getElementById('panel-grid');
186
- grid.innerHTML = '';
187
- var icons = {request:'🌐',performance:'⚡',database:'🗄️',routes:'📋',logs:'📝'};
188
- Object.keys(d).forEach(function(key){
199
+ panelData = d;
200
+ var list = document.getElementById('panel-list');
201
+ var sidebar = document.getElementById('sidebar-metrics');
202
+ list.innerHTML = '';
203
+ sidebar.innerHTML = '';
204
+ var order = ['request','performance','database','routes','logs'];
205
+ var firstReq = null;
206
+ order.forEach(function(key){
189
207
  var p = d[key];
190
208
  if (!p || !p.label) return;
191
- var card = document.createElement('div');
192
- card.className = 'card';
193
- card.innerHTML =
194
- '<div class="card-header"><span class="icon">'+(icons[key]||'🔧')+'</span>'+
195
- '<span class="label">'+escape(p.label)+'</span>'+
196
- '<span class="summary">'+escape(p.summary||'')+'</span></div>'+
197
- '<div class="card-body">'+renderDetails(p)+'</div>';
198
- card.querySelector('.card-header').addEventListener('click', function(){
199
- var body = card.querySelector('.card-body');
209
+ if (!firstReq) firstReq = p;
210
+ var panel = document.createElement('div');
211
+ panel.className = 'panel';
212
+ panel.innerHTML =
213
+ '<div class="panel-header"><span>'+escape(p.label)+'</span><span class="summary">'+escape(p.summary||'')+'</span></div>'+
214
+ '<div class="panel-body">'+renderPanelContent(p)+'</div>';
215
+ panel.querySelector('.panel-header').addEventListener('click', function(){
216
+ var body = this.nextElementSibling;
200
217
  body.classList.toggle('open');
201
218
  });
202
- grid.appendChild(card);
219
+ list.appendChild(panel);
203
220
  });
221
+ for (var key in d) {
222
+ if (order.indexOf(key)!==-1) continue;
223
+ var p = d[key];
224
+ if (!p || !p.label) continue;
225
+ var panel = document.createElement('div');
226
+ panel.className = 'panel';
227
+ panel.innerHTML =
228
+ '<div class="panel-header"><span>'+escape(p.label)+'</span><span class="summary">'+escape(p.summary||'')+'</span></div>'+
229
+ '<div class="panel-body">'+renderPanelContent(p)+'</div>';
230
+ panel.querySelector('.panel-header').addEventListener('click', function(){
231
+ var body = this.nextElementSibling;
232
+ body.classList.toggle('open');
233
+ });
234
+ list.appendChild(panel);
235
+ }
236
+ if (firstReq && firstReq.details) {
237
+ var dd = firstReq.details;
238
+ sidebar.innerHTML =
239
+ '<div class="metric"><span class="label">Method</span><span class="value"><span class="badge badge-'+((dd.method||'get').toLowerCase())+'">'+escape(dd.method||'')+'</span></span></div>'+
240
+ '<div class="metric"><span class="label">Path</span><span class="value mono">'+escape(dd.path||'')+'</span></div>'+
241
+ '<div class="metric"><span class="label">Status</span><span class="value"><span class="status-badge status-'+(dd.status||'')+'">'+escape(dd.status||'')+'</span></span></div>'+
242
+ '<div class="metric"><span class="label">Duration</span><span class="value">'+escape(dd.duration||'')+'</span></div>'+
243
+ '<div class="metric"><span class="label">Memory</span><span class="value">'+escape(dd.memory||'')+'</span></div>'+
244
+ '<div class="metric"><span class="label">Route</span><span class="value mono" style="font-size:10px">'+escape(dd.route||'')+'</span></div>';
245
+ }
204
246
  }).catch(function(){
205
- document.getElementById('panel-grid').innerHTML = '<div class="empty">Failed to load profiler data. Make sure the server is running.</div>';
247
+ document.getElementById('panel-list').innerHTML = '<div class="panel"><div class="panel-body open" style="display:block"><span class="empty">Failed to load profiler data.</span></div></div>';
206
248
  });
207
249
  }
208
250
 
@@ -210,23 +252,24 @@ const PROFILER_PAGE = (config) => {
210
252
  fetch(BASE + '/__devtools/history').then(function(r){return r.json()}).then(function(rows){
211
253
  var tbody = document.getElementById('history-body');
212
254
  tbody.innerHTML = '';
213
- if (!rows || rows.length === 0) {
214
- tbody.innerHTML = '<tr><td colspan="5" class="empty">No requests recorded yet</td></tr>';
255
+ if (!rows || rows.length===0) {
256
+ tbody.innerHTML = '<tr><td colspan="5" style="padding:20px;text-align:center;color:#bbb">No requests recorded yet</td></tr>';
215
257
  return;
216
258
  }
217
259
  rows.forEach(function(r){
218
- var statusClass = r.statusCode >= 400 ? 'error-text' : r.statusCode >= 300 ? 'warn-text' : 'ok-text';
219
- var methodBadge = 'badge-'+r.method.toLowerCase();
260
+ var sc = r.statusCode;
261
+ var scCls = sc>=400 ? 'error-text' : sc>=300 ? 'warn-text' : 'ok-text';
262
+ var mCls = 'badge-'+r.method.toLowerCase();
220
263
  tbody.innerHTML += '<tr>'+
221
- '<td>'+escape(new Date(r.timestamp).toLocaleTimeString())+'</td>'+
222
- '<td><span class="badge '+methodBadge+'">'+escape(r.method)+'</span></td>'+
223
- '<td class="mono">'+escape(r.path)+'</td>'+
224
- '<td><span class="'+statusClass+'">'+r.statusCode+'</span></td>'+
225
- '<td>'+r.duration.toFixed(1)+' ms</td>'+
264
+ '<td style="padding:8px 12px;border-bottom:1px solid #eee;color:#888">'+escape(new Date(r.timestamp).toLocaleTimeString())+'</td>'+
265
+ '<td style="padding:8px 12px;border-bottom:1px solid #eee"><span class="badge '+mCls+'">'+escape(r.method)+'</span></td>'+
266
+ '<td style="padding:8px 12px;border-bottom:1px solid #eee;font-family:SF Mono,Menlo,monospace;font-size:11px">'+escape(r.path)+'</td>'+
267
+ '<td style="padding:8px 12px;border-bottom:1px solid #eee"><span class="'+scCls+'">'+sc+'</span></td>'+
268
+ '<td style="padding:8px 12px;border-bottom:1px solid #eee;color:#888;font-family:SF Mono,Menlo,monospace">'+(r.duration!=null?r.duration.toFixed(1):'')+' ms</td>'+
226
269
  '</tr>';
227
270
  });
228
271
  }).catch(function(){
229
- document.getElementById('history-body').innerHTML = '<tr><td colspan="5" class="empty">Failed to load history</td></tr>';
272
+ document.getElementById('history-body').innerHTML = '<tr><td colspan="5" style="padding:20px;text-align:center;color:#bbb">Failed to load history</td></tr>';
230
273
  });
231
274
  }
232
275
 
@@ -1 +1 @@
1
- {"version":3,"file":"profiler.js","sourceRoot":"","sources":["../../toolbar/profiler.ts"],"names":[],"mappings":";;;AAIO,MAAM,aAAa,GAAG,CAAC,MAAoC,EAAE,EAAE;IACpE,MAAM,EAAE,KAAK,GAAG,MAAM,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,KAAK,KAAK,MAAM,CAAC;IAC9B,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACxC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAChD,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAChD,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3C,OAAO;;;;;;;;;;kBAUS,EAAE,YAAY,EAAE;;;;kBAIhB,QAAQ;;;2DAGiC,UAAU;0BAC3C,OAAO;;;;kBAIf,MAAM,2CAA2C,SAAS;;;;;;+BAM7C,SAAS;;;;mCAIL,OAAO;;;;;iDAKO,SAAS;;;2BAG/B,OAAO;;;;;;;;;;uDAUqB,UAAU;;;oEAGG,OAAO,gDAAgD,SAAS;oEAChE,SAAS;6CAChC,MAAM;;;;;;+DAMY,SAAS;kBACtD,KAAK,YAAY,KAAK;;uDAEe,UAAU,iBAAiB,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;oCAC1E,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA2JxD,CAAC;AACT,CAAC,CAAC;AA3OW,QAAA,aAAa,iBA2OxB"}
1
+ {"version":3,"file":"profiler.js","sourceRoot":"","sources":["../../toolbar/profiler.ts"],"names":[],"mappings":";;;AAIO,MAAM,aAAa,GAAG,CAAC,MAAoC,EAAE,EAAE;IACpE,MAAM,EAAE,KAAK,GAAG,MAAM,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;IAExC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAkRD,CAAC;AACT,CAAC,CAAC;AAtRW,QAAA,aAAa,iBAsRxB"}
@@ -6,116 +6,118 @@ const TOOLBAR_TEMPLATE = (config) => {
6
6
  const id = '__nestDevtools';
7
7
  const uid = Math.random().toString(36).slice(2, 8);
8
8
  const isTop = position.startsWith('top');
9
- const isRight = position.endsWith('right');
10
9
  const outerPos = isTop ? 'top:0' : 'bottom:0';
11
- const outerSide = isRight ? 'right:0' : 'left:0';
12
- const outerBorder = isTop ? 'border-bottom:2px solid #3b82f6' : 'border-top:2px solid #3b82f6';
13
10
  const outerShadow = isTop
14
- ? 'box-shadow:0 4px 12px rgba(0,0,0,0.35)'
15
- : 'box-shadow:0 -4px 12px rgba(0,0,0,0.35)';
16
- const dark = theme === 'dark';
17
- const bg = dark ? '#1f2937' : '#ffffff';
18
- const fg = dark ? '#f3f4f6' : '#1f2937';
19
- const panelBg = dark ? '#111827' : '#f3f4f6';
20
- const borderClr = dark ? '#374151' : '#e5e7eb';
21
- const btnFg = dark ? '#d1d5db' : '#374151';
22
- const btnBg = dark ? '#374151' : '#e5e7eb';
23
- const btnHover = dark ? '#4b5563' : '#d1d5db';
24
- const dimText = dark ? '#9ca3af' : '#6b7280';
25
- const dimmerText = dark ? '#6b7280' : '#9ca3af';
26
- const accentText = dark ? '#60a5fa' : '#2563eb';
27
- const warnClr = '#f59e0b';
28
- const errClr = '#ef4444';
29
- const sqlTimeClr = dark ? '#fbbf24' : '#b45309';
11
+ ? 'box-shadow:0 2px 14px rgba(0,0,0,0.5)'
12
+ : 'box-shadow:0 -2px 14px rgba(0,0,0,0.5)';
30
13
  return `<script id="${id}">
31
14
  (function(){
32
15
  var D=document, B=D.body, C='__nestDt'+'${uid}';
33
16
  var p = window.location.pathname.replace(\/[^\/]*$/,'') + '/__devtools';
34
17
  var c=D.createElement('div'); c.id=C;
35
- c.innerHTML='<div style="position:fixed;${outerPos};${outerSide};z-index:99999;font-family:system-ui,BlinkMacSystemFont,sans-serif;font-size:12px;background:${bg};color:${fg};${outerBorder};width:100%;${outerShadow}"><div style="display:flex;align-items:center;height:34px;padding:0 8px;gap:4px;overflow-x:auto;white-space:nowrap;scrollbar-width:none"><span style="font-weight:800;font-size:13px;color:${accentText};padding:0 10px 0 4px;letter-spacing:-0.5px">NDev</span><div id="'+C+'-bar"></div><a href="'+p+'" target="_blank" style="color:${dimmerText};text-decoration:none;padding:4px 8px;font-size:11px;margin-left:auto" title="Open profiler">&#x25A3;</a><span id="'+C+'-toggle" style="color:${dimmerText};cursor:pointer;padding:4px 8px;font-size:14px">&#x2039;</span></div><div id="'+C+'-panel" style="display:none;border-top:1px solid ${borderClr};background:${panelBg};font-size:12px;line-height:1.5"></div></div>';
18
+ c.innerHTML='<div style="position:fixed;${outerPos};left:0;right:0;z-index:99999;font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-size:11px;background:#1a1a1a;color:#ccc;${outerShadow}"><div style="display:flex;align-items:center;height:26px;padding:0 6px;overflow-x:auto;white-space:nowrap;scrollbar-width:none"><span style="font-weight:700;font-size:12px;color:#888;padding:0 10px 0 4px;letter-spacing:0;flex-shrink:0">ND</span><div id="'+C+'-bar" style="display:flex;align-items:center;flex:1;height:100%"></div><a href="'+p+'" target="_blank" style="display:inline-flex;align-items:center;gap:4px;color:#666;text-decoration:none;padding:0 8px;font-size:11px;height:100%;flex-shrink:0" title="Open profiler"><svg width="11" height="11" viewBox="0 0 16 16" fill="none"><path d="M2 4h12M2 8h12M2 12h12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></a><span id="'+C+'-toggle" style="display:inline-flex;align-items:center;justify-content:center;color:#666;cursor:pointer;padding:0 6px;font-size:13px;height:100%;flex-shrink:0">\u2039</span></div><div id="'+C+'-panel" style="display:none;background:#2b2b2b;font-size:12px;line-height:1.5;color:#ccc;max-height:420px;overflow:hidden"></div></div>';
36
19
  B.appendChild(c);
37
20
 
38
21
  var lastActive=null;
39
22
  function renderPanel(name, detail) {
40
- var p=D.getElementById(C+'-panel');
41
- if (lastActive===name) { p.style.display='none'; lastActive=null; return; }
23
+ var pnl=D.getElementById(C+'-panel');
24
+ if (lastActive===name) { pnl.style.display='none'; lastActive=null; return; }
42
25
  lastActive=name;
43
- var h='<div style="padding:12px 16px;max-height:360px;overflow-y:auto">';
44
- h+='<div style="display:flex;align-items:center;margin-bottom:10px;gap:8px">';
45
- h+='<span style="font-weight:700;font-size:13px">'+detail.label+'</span>';
46
- h+='<span style="color:${dimText};font-size:11px">'+detail.summary+'</span>';
47
- h+='<span style="margin-left:auto;cursor:pointer;color:${dimmerText};font-size:16px" onclick="document.getElementById('+C+'-panel).style.display=\\'none\\';lastActive=null">\u00d7</span>';
26
+ var h='<div style="display:flex;align-items:center;gap:10px;padding:9px 14px;border-bottom:1px solid #333">';
27
+ h+='<span style="font-weight:600;font-size:13px;color:#eee">'+escapeHtml(detail.label)+'</span>';
28
+ h+='<span style="color:#888;font-size:11px">'+escapeHtml(detail.summary)+'</span>';
29
+ h+='<span style="margin-left:auto;cursor:pointer;color:#666;padding:2px;font-size:15px" onclick="document.getElementById('+C+'-panel).style.display=\\'none\\';lastActive=null">\u00d7</span>';
48
30
  h+='</div>';
49
- h+='<table style="width:100%;border-collapse:collapse">';
31
+ h+='<div style="padding:10px 14px 14px;overflow-y:auto;max-height:360px">';
32
+ var fields=[];
50
33
  for (var k in detail.details) {
51
34
  var v=detail.details[k];
52
- if (k==='queries' && Array.isArray(v)) {
53
- for (var i=0;i<v.length;i++) {
54
- h+='<tr><td style="padding:4px 8px;color:${dimText};border-bottom:1px solid ${borderClr};vertical-align:top;width:80px">#'+(i+1)+'</td><td style="padding:4px 8px;border-bottom:1px solid ${borderClr};font-family:monospace;font-size:11px;word-break:break-all">'+escapeHtml(v[i].sql||'')+'</td><td style="padding:4px 8px;border-bottom:1px solid ${borderClr};text-align:right;color:${sqlTimeClr}">'+escapeHtml(v[i].duration||'')+'</td></tr>';
35
+ if (k==='queries' && Array.isArray(v)) { fields.push({type:'queries',data:v}); continue; }
36
+ if (k==='entries' && Array.isArray(v)) { fields.push({type:'log',data:v}); continue; }
37
+ if (k==='grouped' && Array.isArray(v)) { fields.push({type:'routes',data:v}); continue; }
38
+ if (k==='exception' && v && typeof v==='object') { fields.push({type:'exception',data:v}); continue; }
39
+ if (k==='stack') continue;
40
+ fields.push({type:'pair',key:k,val:v});
41
+ }
42
+ for (var fi=0;fi<fields.length;fi++) {
43
+ var f=fields[fi];
44
+ if (f.type==='queries') {
45
+ if (f.data.length===0) { h+='<div style="padding:6px 0;color:#666">No queries</div>'; continue; }
46
+ for (var qi=0;qi<f.data.length;qi++) {
47
+ h+='<div style="display:flex;align-items:flex-start;gap:6px;padding:5px 0;border-bottom:1px solid #333;font-size:11px"><span style="color:#666;width:22px;flex-shrink:0">'+(qi+1)+'</span><span style="font-family:SF Mono,Menlo,monospace;word-break:break-all;flex:1">'+escapeHtml(f.data[qi].sql||'')+'</span><span style="color:#dfb840;white-space:nowrap;flex-shrink:0;font-family:SF Mono,Menlo,monospace">'+escapeHtml(f.data[qi].duration||'')+'</span></div>';
55
48
  }
56
49
  continue;
57
50
  }
58
- if (k==='entries' && Array.isArray(v)) {
59
- for (var i=0;i<v.length;i++) {
60
- var e=v[i];
61
- var lc=e.level==='error'?'${errClr}':e.level==='warn'?'${warnClr}':'${dimText}';
62
- h+='<tr><td style="padding:3px 8px;color:'+lc+';border-bottom:1px solid ${borderClr};width:60px;text-transform:uppercase;font-weight:600;font-size:10px">'+escapeHtml(e.level||'')+'</td><td style="padding:3px 8px;border-bottom:1px solid ${borderClr};word-break:break-all">'+escapeHtml(e.message||'')+'</td></tr>';
51
+ if (f.type==='log') {
52
+ if (f.data.length===0) { h+='<div style="padding:6px 0;color:#666">No entries</div>'; continue; }
53
+ for (var li=0;li<f.data.length;li++) {
54
+ var e=f.data[li];
55
+ var lc=e.level==='error'?'#e74c3c':e.level==='warn'?'#f39c12':'#888';
56
+ h+='<div style="display:flex;align-items:flex-start;gap:8px;padding:4px 0;border-bottom:1px solid #333;font-size:11px"><span style="color:'+lc+';width:44px;font-weight:600;font-size:10px;text-transform:uppercase;flex-shrink:0">'+escapeHtml(e.level||'')+'</span><span>'+escapeHtml(e.message||'')+'</span></div>';
63
57
  }
64
58
  continue;
65
59
  }
66
- if (k==='grouped' && Array.isArray(v)) {
67
- for (var i=0;i<v.length;i++) {
68
- var g=v[i];
69
- h+='<tr><td colspan="3" style="padding:6px 8px;color:${accentText};font-weight:600;border-bottom:1px solid ${borderClr}">'+escapeHtml(g.method||'')+' ('+(g.count||0)+')</td></tr>';
60
+ if (f.type==='routes') {
61
+ for (var gi=0;gi<f.data.length;gi++) {
62
+ var g=f.data[gi];
63
+ h+='<div style="padding:5px 0 2px;font-weight:600;font-size:11px;color:#5d9">'+escapeHtml(g.method||'')+' <span style="color:#666;font-weight:400">('+(g.count||0)+')</span></div>';
70
64
  if (g.routes) {
71
- for (var j=0;j<g.routes.length;j++) {
72
- h+='<tr><td style="padding:2px 8px 2px 24px;border-bottom:1px solid ${borderClr};font-family:monospace;font-size:11px" colspan="3">'+escapeHtml(g.routes[j].path||'')+' <span style="color:${dimmerText}">('+escapeHtml(g.routes[j].controller||'')+')</span></td></tr>';
65
+ for (var rj=0;rj<g.routes.length;rj++) {
66
+ h+='<div style="padding:2px 0 2px 14px;font-family:SF Mono,Menlo,monospace;font-size:11px">'+escapeHtml(g.routes[rj].path||'')+' <span style="color:#666">('+escapeHtml(g.routes[rj].controller||'')+')</span></div>';
73
67
  }
74
68
  }
75
69
  }
76
70
  continue;
77
71
  }
78
- h+='<tr><td style="padding:4px 8px;color:${dimText};border-bottom:1px solid ${borderClr};width:120px;vertical-align:top">'+escapeHtml(k)+'</td><td style="padding:4px 8px;border-bottom:1px solid ${borderClr};word-break:break-all">'+escapeHtml(String(v))+'</td></tr>';
72
+ if (f.type==='exception') {
73
+ h+='<div style="padding:8px;background:rgba(231,76,60,0.1);border-radius:4px"><div style="color:#e74c3c;font-weight:600;font-size:12px;margin-bottom:4px">'+escapeHtml(f.data.message||'')+'</div><pre style="font-family:SF Mono,Menlo,monospace;font-size:11px;color:#888;white-space:pre-wrap;margin:0;line-height:1.4">'+escapeHtml(f.data.stack||'')+'</pre></div>';
74
+ continue;
75
+ }
76
+ h+='<div style="display:flex;padding:3px 0;font-size:11px"><span style="color:#888;width:90px;flex-shrink:0">'+escapeHtml(f.key)+'</span><span style="word-break:break-all">'+escapeHtml(typeof f.val==='object'?JSON.stringify(f.val):String(f.val))+'</span></div>';
79
77
  }
80
- h+='</table></div>';
81
- p.innerHTML=h;
82
- p.style.display='block';
83
- D.getElementById(C+'-toggle').innerHTML='&#x203a;';
78
+ h+='</div>';
79
+ pnl.innerHTML=h;
80
+ pnl.style.display='block';
81
+ D.getElementById(C+'-toggle').innerHTML='\u203a';
84
82
  }
85
83
 
86
84
  function escapeHtml(s) {
87
85
  if (s==null) return '';
88
- return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
86
+ return String(s).replace(/[&<>"]/g,function(m){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[m]});
87
+ }
88
+
89
+ function makeBadge(html, cls) {
90
+ var el=D.createElement('span');
91
+ el.style.cssText='display:inline-flex;align-items:center;height:100%;padding:0 8px;color:#ccc;cursor:pointer;gap:4px;font-size:11px;border-right:1px solid #333;transition:background .1s';
92
+ el.innerHTML=html;
93
+ el.onmouseenter=function(){this.style.background='#333'};
94
+ el.onmouseleave=function(){this.style.background='transparent'};
95
+ if (cls) el.className=cls;
96
+ return el;
89
97
  }
90
98
 
91
99
  var furl = p + '/data';
92
100
  fetch(furl).then(function(r){return r.json()}).then(function(d){
93
101
  var bar=D.getElementById(C+'-bar');
94
- var panels=[];
95
- if(d.request) panels.push(d.request);
96
- if(d.performance) panels.push(d.performance);
97
- if(d.database) panels.push(d.database);
98
- if(d.routes) panels.push(d.routes);
99
- if(d.logs) panels.push(d.logs);
100
- for(var k in d){if(panels.indexOf(d[k])===-1&&d[k]&&d[k].label)panels.push(d[k]);}
101
- var icons={request:'🌐',performance:'⚡',database:'🗄️',routes:'📋',logs:'📝','default':'🔧'};
102
- panels.forEach(function(p,i){
103
- var ic=icons[p.label.toLowerCase()]||icons['default'];
104
- var btn=D.createElement('span');
105
- btn.style.cssText='display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:4px;cursor:pointer;color:${btnFg};background:${btnBg};margin:0 2px;transition:background .15s';
106
- btn.innerHTML=ic+' '+escapeHtml(p.summary);
107
- btn.onmouseenter=function(){this.style.background='${btnHover}'};
108
- btn.onmouseleave=function(){this.style.background='${btnBg}'};
109
- btn.onclick=function(n){return function(){
110
- var key=Object.keys(d).filter(function(k){return d[k]===d[n]})[0];
102
+ var order=['request','performance','database','routes','logs'];
103
+ var items=[];
104
+ for(var oi=0;oi<order.length;oi++){if(d[order[oi]])items.push({key:order[oi],data:d[order[oi]]});}
105
+ for(var k in d){var found=false;for(var pi=0;pi<items.length;pi++){if(items[pi].key===k){found=true;break}}if(!found&&d[k]&&d[k].label)items.push({key:k,data:d[k]});}
106
+ items.forEach(function(item){
107
+ var p=item.data;
108
+ var badge=makeBadge('<span style="color:#888;font-weight:500">'+escapeHtml(p.summary)+'</span>');
109
+ badge.onclick=function(){
110
+ var key=Object.keys(d).filter(function(kr){return d[kr]===d[p]})[0];
111
111
  renderPanel(key,d[key]);
112
- }}(i);
113
- bar.appendChild(btn);
112
+ };
113
+ bar.appendChild(badge);
114
114
  });
115
115
  D.getElementById(C+'-toggle').onclick=function(){
116
116
  var pnl=D.getElementById(C+'-panel');
117
- pnl.style.display=pnl.style.display==='none'?'block':'none';
118
- this.innerHTML=pnl.style.display==='block'?'\u203A':'\u2039';
117
+ var isOpen=pnl.style.display==='block';
118
+ pnl.style.display=isOpen?'none':'block';
119
+ this.innerHTML=isOpen?'\u2039':'\u203a';
120
+ if(isOpen)lastActive=null;
119
121
  };
120
122
  }).catch(function(){});
121
123
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"template.js","sourceRoot":"","sources":["../../toolbar/template.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../toolbar/template.ts"],"names":[],"mappings":";;;AAKO,MAAM,gBAAgB,GAAG,CAAC,MAAuC,EAAE,EAAE;IAC1E,MAAM,EAAE,QAAQ,GAAG,aAAa,EAAE,KAAK,GAAG,MAAM,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;IAClE,MAAM,EAAE,GAAG,gBAAgB,CAAC;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;IAC9C,MAAM,WAAW,GAAG,KAAK;QACvB,CAAC,CAAC,uCAAuC;QACzC,CAAC,CAAC,wCAAwC,CAAC;IAE7C,OAAO,eAAe,EAAE;;4CAEkB,GAAG;;;4CAGH,QAAQ,sIAAsI,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA0G3L,CAAC;AACX,CAAC,CAAC;AA1HW,QAAA,gBAAgB,oBA0H3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-devtools",
3
- "version": "1.3.2",
3
+ "version": "1.5.0",
4
4
  "description": "Symfony-style debug toolbar for NestJS applications",
5
5
  "keywords": [
6
6
  "nestjs",