@vistagenic/vista 0.3.0 → 0.3.2

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/dev-error.js CHANGED
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.VistaDevErrorBoundary = exports.DevErrorBoundary = void 0;
7
+ exports.fromCaughtError = fromCaughtError;
7
8
  exports.renderErrorHTML = renderErrorHTML;
8
9
  exports.ErrorOverlay = ErrorOverlay;
9
10
  exports.VistaErrorOverlay = ErrorOverlay;
@@ -22,21 +23,21 @@ const OVERLAY_STYLES = `
22
23
  * { box-sizing: border-box; }
23
24
  html, body {
24
25
  margin: 0;
25
- min-height: 100%;
26
- background: #0c0d10;
27
- color: #e8ebf1;
28
- font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
29
- }
30
- body {
31
- line-height: 1.45;
26
+ height: 100%;
27
+ overflow: hidden;
28
+ background: #050505;
29
+ color: #ededed;
30
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
32
31
  }
32
+ body { line-height: 1.5; }
33
33
  .vista-error-page-root {
34
34
  min-height: 100vh;
35
+ height: 100vh;
35
36
  position: relative;
36
37
  display: flex;
37
38
  align-items: center;
38
39
  justify-content: center;
39
- padding: 24px;
40
+ padding: 20px;
40
41
  overflow: hidden;
41
42
  }
42
43
  .vista-error-page-root[data-embedded='true'] {
@@ -47,203 +48,511 @@ body {
47
48
  .vista-error-backdrop {
48
49
  position: absolute;
49
50
  inset: 0;
50
- background: radial-gradient(circle at top, rgba(255, 89, 60, 0.22), rgba(10, 11, 15, 0.94) 44%);
51
- backdrop-filter: blur(5px);
51
+ background: rgba(0, 0, 0, 0.72);
52
52
  }
53
53
  .vista-error-panel {
54
54
  position: relative;
55
- width: min(980px, 100%);
56
- max-height: min(86vh, 860px);
55
+ width: min(1180px, calc(100vw - 32px));
56
+ height: min(820px, calc(100vh - 32px));
57
57
  display: flex;
58
58
  flex-direction: column;
59
- border-radius: 16px;
59
+ border-radius: 8px;
60
60
  overflow: hidden;
61
- border: 1px solid rgba(255, 255, 255, 0.14);
62
- background: linear-gradient(180deg, rgba(24, 27, 33, 0.97), rgba(9, 11, 15, 0.98));
63
- box-shadow: 0 34px 92px rgba(0, 0, 0, 0.54);
64
- animation: vista-overlay-in 170ms ease;
61
+ background: #1e1e1e;
62
+ border: 1px solid #2b2b2b;
63
+ box-shadow: 0 24px 80px rgba(0, 0, 0, 0.62);
64
+ animation: vista-overlay-in 160ms ease;
65
65
  }
66
- .vista-error-header {
66
+ .vista-ide-workbench {
67
67
  display: flex;
68
- justify-content: space-between;
69
- align-items: flex-start;
70
- gap: 14px;
71
- padding: 15px 17px;
72
- border-bottom: 1px solid rgba(255, 255, 255, 0.12);
73
- background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
68
+ flex: 1;
69
+ min-height: 0;
74
70
  }
75
- .vista-error-head-left {
76
- min-width: 0;
71
+ .vista-ide-activity {
72
+ width: 48px;
73
+ flex-shrink: 0;
77
74
  display: flex;
78
75
  flex-direction: column;
79
- gap: 8px;
80
- }
81
- .vista-error-top-row {
82
- display: flex;
83
76
  align-items: center;
84
- gap: 10px;
77
+ padding-top: 8px;
78
+ background: #181818;
79
+ border-right: 1px solid #2b2b2b;
85
80
  }
86
- .vista-error-badge {
81
+ .vista-ide-activity-item {
82
+ width: 48px;
83
+ height: 40px;
87
84
  display: inline-flex;
88
85
  align-items: center;
89
- gap: 7px;
90
- height: 24px;
91
- padding: 0 10px;
92
- border-radius: 999px;
93
- border: 1px solid rgba(248, 113, 113, 0.48);
94
- background: rgba(248, 113, 113, 0.14);
95
- color: #fecaca;
96
- font-size: 11px;
97
- font-weight: 700;
98
- letter-spacing: 0.07em;
99
- text-transform: uppercase;
100
- white-space: nowrap;
86
+ justify-content: center;
87
+ color: #858585;
101
88
  }
102
- .vista-error-badge-dot {
103
- width: 7px;
104
- height: 7px;
105
- border-radius: 999px;
106
- background: #f87171;
107
- box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.24);
89
+ .vista-ide-activity-item.is-active {
90
+ color: #ffffff;
91
+ box-shadow: inset 2px 0 0 #0078d4;
108
92
  }
109
- .vista-error-pagination {
93
+ .vista-ide-activity-item svg {
94
+ width: 22px;
95
+ height: 22px;
96
+ display: block;
97
+ }
98
+ .vista-ide-main {
99
+ display: flex;
100
+ flex-direction: column;
101
+ flex: 1;
102
+ min-width: 0;
103
+ min-height: 0;
104
+ }
105
+ .vista-ide-titlebar {
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: space-between;
109
+ gap: 12px;
110
+ height: 38px;
111
+ padding: 0 10px;
112
+ background: #181818;
113
+ border-bottom: 1px solid #2b2b2b;
114
+ flex-shrink: 0;
115
+ }
116
+ .vista-ide-traffic {
110
117
  display: inline-flex;
111
118
  align-items: center;
112
119
  gap: 6px;
113
- padding: 2px 3px;
120
+ width: 54px;
121
+ }
122
+ .vista-ide-traffic span {
123
+ width: 10px;
124
+ height: 10px;
114
125
  border-radius: 999px;
115
- border: 1px solid rgba(255, 255, 255, 0.18);
116
- background: rgba(255, 255, 255, 0.04);
117
126
  }
118
- .vista-error-page-btn {
119
- width: 22px;
120
- height: 22px;
127
+ .vista-ide-traffic .is-close { background: #ff5f57; }
128
+ .vista-ide-traffic .is-min { background: #febc2e; }
129
+ .vista-ide-traffic .is-max { background: #28c840; }
130
+ .vista-ide-title {
131
+ min-width: 0;
132
+ flex: 1;
133
+ text-align: center;
134
+ color: #c6c6c6;
135
+ font-size: 12px;
136
+ font-weight: 500;
137
+ overflow: hidden;
138
+ text-overflow: ellipsis;
139
+ white-space: nowrap;
140
+ }
141
+ .vista-error-toolbar {
142
+ display: flex;
143
+ align-items: center;
144
+ gap: 8px;
145
+ flex-shrink: 0;
146
+ }
147
+ .vista-ide-tabbar {
148
+ display: flex;
149
+ align-items: stretch;
150
+ justify-content: space-between;
151
+ height: 35px;
152
+ background: #181818;
153
+ border-bottom: 1px solid #2b2b2b;
154
+ flex-shrink: 0;
155
+ }
156
+ .vista-ide-tabs {
157
+ display: flex;
158
+ min-width: 0;
159
+ overflow: hidden;
160
+ scrollbar-width: none;
161
+ }
162
+ .vista-ide-tabs::-webkit-scrollbar { display: none; }
163
+ .vista-ide-tab {
121
164
  display: inline-flex;
122
165
  align-items: center;
123
- justify-content: center;
124
- border: none;
125
- border-radius: 999px;
126
- background: rgba(255, 255, 255, 0.13);
127
- color: #f8fafc;
128
- font-size: 11px;
129
- line-height: 1;
166
+ gap: 8px;
167
+ height: 35px;
168
+ padding: 0 14px;
169
+ border: 0;
170
+ border-right: 1px solid #2b2b2b;
171
+ background: #1e1e1e;
172
+ color: #cccccc;
173
+ font-size: 12px;
130
174
  cursor: pointer;
175
+ box-shadow: inset 0 -1px 0 #0078d4;
131
176
  }
132
- .vista-error-page-btn:disabled {
133
- opacity: 0.35;
134
- cursor: not-allowed;
177
+ .vista-ide-tab:hover { color: #fff; }
178
+ .vista-ide-tab.is-inactive {
179
+ background: #181818;
180
+ color: #8b8b8b;
181
+ box-shadow: none;
135
182
  }
136
- .vista-error-page-btn:not(:disabled):hover {
137
- background: rgba(255, 255, 255, 0.24);
183
+ .vista-ide-tab-icon {
184
+ width: 14px;
185
+ height: 14px;
186
+ flex-shrink: 0;
138
187
  }
139
- .vista-error-page-count {
140
- min-width: 56px;
141
- text-align: center;
188
+ .vista-ide-crumbs {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 6px;
192
+ height: 24px;
193
+ padding: 0 12px;
194
+ background: #1e1e1e;
195
+ border-bottom: 1px solid #2b2b2b;
196
+ color: #8b8b8b;
142
197
  font-size: 11px;
143
- font-weight: 630;
144
- color: rgba(241, 245, 249, 0.95);
145
- font-variant-numeric: tabular-nums;
198
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
199
+ white-space: nowrap;
200
+ overflow: hidden;
201
+ text-overflow: ellipsis;
146
202
  }
147
- .vista-error-title {
148
- margin: 0;
149
- font-size: 18px;
150
- line-height: 1.32;
151
- font-weight: 640;
152
- color: #f8fafc;
153
- letter-spacing: 0.01em;
154
- word-break: break-word;
203
+ .vista-error-code {
204
+ display: flex;
205
+ flex-direction: column;
206
+ flex: 1;
207
+ min-height: 0;
208
+ background: #1e1e1e;
209
+ overflow: hidden;
210
+ }
211
+ .vista-error-editor-host {
212
+ flex: 1;
213
+ min-height: 0;
214
+ overflow: auto;
155
215
  }
156
- .vista-error-meta {
216
+ .vista-error-editor {
157
217
  margin: 0;
218
+ min-height: 100%;
219
+ padding: 8px 0 12px;
220
+ overflow: visible;
221
+ font-size: 13px;
222
+ line-height: 20px;
223
+ font-family: ui-monospace, SFMono-Regular, "Cascadia Code", Menlo, Consolas, monospace;
224
+ background: #1e1e1e;
225
+ }
226
+ .vista-error-editor-line {
227
+ display: grid;
228
+ grid-template-columns: 56px minmax(0, 1fr);
229
+ min-height: 20px;
230
+ }
231
+ .vista-error-editor-line.is-error {
232
+ background: rgba(248, 81, 73, 0.12);
233
+ }
234
+ .vista-error-editor-line.is-server {
235
+ background: rgba(248, 81, 73, 0.1);
236
+ }
237
+ .vista-error-editor-line.is-client {
238
+ background: rgba(63, 185, 80, 0.1);
239
+ }
240
+ .vista-ide-source-badge {
241
+ padding: 0 7px;
242
+ border-radius: 999px;
243
+ font-size: 10px;
244
+ font-weight: 600;
245
+ letter-spacing: 0.04em;
246
+ text-transform: uppercase;
247
+ background: rgba(255, 255, 255, 0.12);
248
+ }
249
+ .vista-ide-source-badge.is-server { background: #1f6feb; }
250
+ .vista-ide-source-badge.is-client { background: #238636; }
251
+ .vista-ide-source-badge.is-hydration { background: #9e6a03; }
252
+ .vista-error-hint {
253
+ margin: 8px 0 0;
254
+ color: #8b949e;
158
255
  font-size: 12px;
159
- color: rgba(226, 232, 240, 0.72);
256
+ line-height: 1.5;
160
257
  }
161
- .vista-error-controls {
258
+ .vista-error-hint[hidden] { display: none; }
259
+ .vista-error-problem-icon.is-hydration { background: #d29922; }
260
+ .vista-error-problem-icon.is-server { background: #58a6ff; }
261
+ .vista-error-kind.is-hydration { color: #d29922; }
262
+ .vista-error-kind.is-server { color: #58a6ff; }
263
+ .vista-error-gutter {
264
+ padding: 0 12px 0 0;
265
+ border-right: 1px solid #2b2b2b;
266
+ color: #6e7681;
267
+ text-align: right;
268
+ user-select: none;
269
+ font-variant-numeric: tabular-nums;
270
+ }
271
+ .vista-error-editor-line.is-error .vista-error-gutter { color: #f85149; font-weight: 600; }
272
+ .vista-error-source {
273
+ position: relative;
274
+ padding: 0 16px 0 12px;
275
+ color: #d4d4d4;
276
+ white-space: pre;
277
+ overflow: hidden;
278
+ }
279
+ .vista-error-squiggle {
280
+ position: absolute;
281
+ left: 12px;
282
+ bottom: 0;
283
+ height: 4px;
284
+ width: 3.2ch;
285
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='4' viewBox='0 0 6 4'%3E%3Cpath d='M0 3 Q1.5 0 3 3 T6 3' fill='none' stroke='%23f85149' stroke-width='1.4'/%3E%3C/svg%3E");
286
+ background-repeat: repeat-x;
287
+ background-size: 6px 4px;
288
+ pointer-events: none;
289
+ }
290
+ .vista-tok-kw { color: #c586c0; }
291
+ .vista-tok-str { color: #ce9178; }
292
+ .vista-tok-cmt { color: #6a9955; font-style: italic; }
293
+ .vista-tok-tag { color: #4ec9b0; }
294
+ .vista-tok-num { color: #b5cea8; }
295
+ .vista-ide-statusbar {
162
296
  display: flex;
163
297
  align-items: center;
164
- gap: 8px;
298
+ justify-content: space-between;
299
+ gap: 12px;
300
+ height: 22px;
301
+ padding: 0 10px;
302
+ background: #007acc;
303
+ color: #ffffff;
304
+ font-size: 12px;
305
+ flex-shrink: 0;
165
306
  }
166
- .vista-error-btn {
167
- width: 30px;
168
- height: 30px;
169
- padding: 0;
170
- border-radius: 9px;
171
- border: 1px solid rgba(255, 255, 255, 0.22);
172
- background: rgba(255, 255, 255, 0.04);
173
- color: #e2e8f0;
174
- display: inline-flex;
307
+ .vista-ide-statusbar span { white-space: nowrap; }
308
+ .vista-ide-panel {
309
+ display: flex;
310
+ flex-direction: column;
311
+ background: #181818;
312
+ border-top: 1px solid #2b2b2b;
313
+ overflow: hidden;
314
+ flex: 0 0 auto;
315
+ max-height: 38%;
316
+ min-height: 148px;
317
+ }
318
+ .vista-ide-panel-tabs {
319
+ display: flex;
175
320
  align-items: center;
176
- justify-content: center;
177
- cursor: pointer;
178
- transition: all 120ms ease;
321
+ gap: 16px;
322
+ height: 32px;
323
+ padding: 0 8px 0 12px;
324
+ border-bottom: 1px solid #2b2b2b;
325
+ color: #bbbbbb;
326
+ font-size: 11px;
327
+ font-weight: 600;
328
+ letter-spacing: 0.06em;
329
+ flex-shrink: 0;
179
330
  }
180
- .vista-error-btn:hover {
181
- background: rgba(255, 255, 255, 0.12);
182
- border-color: rgba(255, 255, 255, 0.38);
331
+ .vista-ide-panel-tabs .is-active {
332
+ color: #ffffff;
333
+ box-shadow: inset 0 -1px 0 #ffffff;
334
+ padding-bottom: 8px;
183
335
  }
184
- .vista-error-btn svg {
185
- width: 14px;
186
- height: 14px;
187
- display: block;
336
+ .vista-ide-panel-toggle {
337
+ margin-left: auto;
188
338
  }
189
- .vista-error-btn:focus-visible,
190
- .vista-error-page-btn:focus-visible,
191
- .vista-error-location:focus-visible {
192
- outline: 2px solid rgba(255, 255, 255, 0.7);
193
- outline-offset: 2px;
339
+ .vista-ide-panel-toggle[hidden] { display: none; }
340
+ .vista-ide-panel-toggle .vista-ide-icon-restore { display: none; }
341
+ .vista-ide-main[data-panel-expanded='true'] .vista-ide-tabbar,
342
+ .vista-ide-main[data-panel-expanded='true'] .vista-ide-crumbs,
343
+ .vista-ide-main[data-panel-expanded='true'] .vista-error-code,
344
+ .vista-ide-main[data-panel-expanded='true'] .vista-ide-statusbar {
345
+ display: none;
346
+ }
347
+ .vista-ide-main[data-panel-expanded='true'] .vista-ide-panel {
348
+ flex: 1;
349
+ max-height: none;
350
+ min-height: 0;
351
+ border-top: 0;
194
352
  }
353
+ .vista-ide-main[data-panel-expanded='true'] .vista-ide-icon-expand { display: none; }
354
+ .vista-ide-main[data-panel-expanded='true'] .vista-ide-icon-restore { display: block; }
355
+ .vista-ide-main[data-terminal='true'] .vista-ide-tabbar,
356
+ .vista-ide-main[data-terminal='true'] .vista-ide-crumbs,
357
+ .vista-ide-main[data-terminal='true'] .vista-error-code,
358
+ .vista-ide-main[data-terminal='true'] .vista-ide-statusbar {
359
+ display: none;
360
+ }
361
+ .vista-ide-main[data-terminal='true'] .vista-ide-panel {
362
+ flex: 1;
363
+ max-height: none;
364
+ min-height: 0;
365
+ border-top: 0;
366
+ }
367
+ .vista-ide-main[data-terminal='true'] .vista-ide-panel-toggle { display: none; }
195
368
  .vista-error-body {
196
- padding: 16px 17px 17px;
369
+ flex: 1;
370
+ min-height: 0;
197
371
  overflow: auto;
198
- display: grid;
199
- grid-template-columns: 1fr;
200
- gap: 11px;
372
+ padding: 10px 14px 14px;
201
373
  }
202
- .vista-error-card {
203
- border: 1px solid rgba(255, 255, 255, 0.11);
204
- border-radius: 11px;
205
- background: rgba(255, 255, 255, 0.03);
206
- padding: 12px;
374
+ .vista-error-editor-host,
375
+ .vista-error-body,
376
+ .vista-ide-panel {
377
+ scrollbar-width: thin;
378
+ scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
207
379
  }
208
- .vista-error-card-title {
209
- margin: 0 0 8px;
210
- font-size: 11px;
211
- letter-spacing: 0.08em;
212
- text-transform: uppercase;
213
- font-weight: 700;
214
- color: rgba(226, 232, 240, 0.76);
380
+ .vista-error-editor-host::-webkit-scrollbar,
381
+ .vista-error-body::-webkit-scrollbar,
382
+ .vista-ide-panel::-webkit-scrollbar {
383
+ width: 6px;
384
+ height: 6px;
385
+ }
386
+ .vista-error-editor-host::-webkit-scrollbar-track,
387
+ .vista-error-body::-webkit-scrollbar-track,
388
+ .vista-ide-panel::-webkit-scrollbar-track,
389
+ .vista-error-editor-host::-webkit-scrollbar-corner,
390
+ .vista-error-body::-webkit-scrollbar-corner,
391
+ .vista-ide-panel::-webkit-scrollbar-corner {
392
+ background: transparent;
393
+ }
394
+ .vista-error-editor-host::-webkit-scrollbar-thumb,
395
+ .vista-error-body::-webkit-scrollbar-thumb,
396
+ .vista-ide-panel::-webkit-scrollbar-thumb {
397
+ background-color: rgba(255, 255, 255, 0.16);
398
+ border-radius: 999px;
399
+ }
400
+ .vista-error-editor-host::-webkit-scrollbar-thumb:hover,
401
+ .vista-error-body::-webkit-scrollbar-thumb:hover,
402
+ .vista-ide-panel::-webkit-scrollbar-thumb:hover {
403
+ background-color: rgba(255, 255, 255, 0.34);
215
404
  }
216
- .vista-error-pre {
405
+ .vista-error-kind {
217
406
  margin: 0;
407
+ color: #f85149;
408
+ font-size: 12px;
409
+ font-weight: 600;
410
+ }
411
+ .vista-error-headline {
412
+ margin: 6px 0 0;
413
+ color: #e6edf3;
414
+ font-size: 13px;
415
+ font-weight: 500;
416
+ line-height: 1.5;
417
+ word-break: break-word;
418
+ }
419
+ .vista-error-detail {
420
+ margin: 6px 0 0;
421
+ color: #8b949e;
422
+ font-size: 12px;
423
+ line-height: 1.5;
218
424
  white-space: pre-wrap;
219
425
  word-break: break-word;
220
- font-size: 12.5px;
221
- line-height: 1.58;
222
- color: #f1f5f9;
223
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
224
426
  }
225
- .vista-error-tok-error { color: #fca5a5; font-weight: 640; }
226
- .vista-error-tok-path { color: #93c5fd; }
227
- .vista-error-tok-string { color: #86efac; }
228
- .vista-error-tok-stack { color: #cbd5e1; }
229
- .vista-error-tok-line { color: #fbbf24; }
230
- .vista-error-tok-caret { color: #f87171; font-weight: 700; }
231
- .vista-error-location {
427
+ .vista-error-problem {
428
+ display: flex;
429
+ gap: 8px;
430
+ align-items: flex-start;
431
+ padding: 8px 2px 4px;
432
+ }
433
+ .vista-error-problem-icon {
434
+ width: 14px;
435
+ height: 14px;
436
+ margin-top: 2px;
437
+ border-radius: 999px;
438
+ background: #f85149;
439
+ flex-shrink: 0;
440
+ }
441
+ .vista-error-detail[hidden],
442
+ .vista-error-code[hidden],
443
+ .vista-error-stack[hidden] { display: none; }
444
+ .vista-error-stack { margin-top: 8px; }
445
+ .vista-error-stack-title {
446
+ margin: 0 0 6px;
447
+ color: #bbbbbb;
448
+ font-size: 11px;
449
+ font-weight: 600;
450
+ letter-spacing: 0.06em;
451
+ }
452
+ .vista-error-frames {
453
+ display: flex;
454
+ flex-direction: column;
455
+ }
456
+ .vista-error-frame {
457
+ display: flex;
458
+ flex-direction: column;
459
+ align-items: flex-start;
460
+ gap: 1px;
232
461
  width: 100%;
233
- display: inline-flex;
234
- align-items: center;
235
- justify-content: flex-start;
236
- border: 1px solid rgba(255, 255, 255, 0.16);
237
- background: rgba(255, 255, 255, 0.04);
238
- color: #93c5fd;
239
- border-radius: 8px;
240
- padding: 8px 10px;
462
+ margin: 0;
463
+ padding: 6px 8px;
464
+ border: 0;
465
+ border-radius: 0;
466
+ background: transparent;
467
+ color: inherit;
468
+ text-align: left;
469
+ cursor: pointer;
470
+ }
471
+ .vista-error-frame:hover { background: #2a2d2e; }
472
+ .vista-error-frame-name {
241
473
  font-size: 12px;
474
+ font-weight: 500;
475
+ color: #e6edf3;
242
476
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
477
+ }
478
+ .vista-error-frame-loc {
479
+ font-size: 11px;
480
+ color: #8b949e;
481
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
482
+ }
483
+ .vista-error-pagination {
484
+ display: inline-flex;
485
+ align-items: center;
486
+ gap: 2px;
487
+ }
488
+ .vista-error-pagination[hidden] { display: none; }
489
+ .vista-error-page-btn {
490
+ width: 28px;
491
+ height: 28px;
492
+ display: inline-flex;
493
+ align-items: center;
494
+ justify-content: center;
495
+ border: 0;
496
+ border-radius: 6px;
497
+ background: transparent;
498
+ color: #a1a1a1;
499
+ font-size: 16px;
500
+ line-height: 1;
501
+ cursor: pointer;
502
+ }
503
+ .vista-error-page-btn:disabled { opacity: 0.28; cursor: default; }
504
+ .vista-error-page-btn:not(:disabled):hover { background: #2a2a2a; color: #ededed; }
505
+ .vista-error-page-count {
506
+ min-width: 36px;
507
+ padding: 0 4px;
508
+ text-align: center;
509
+ font-size: 13px;
510
+ font-weight: 500;
511
+ color: #a1a1a1;
512
+ font-variant-numeric: tabular-nums;
513
+ }
514
+ .vista-error-brand {
515
+ display: inline-flex;
516
+ align-items: center;
517
+ gap: 8px;
518
+ color: #a1a1a1;
519
+ font-size: 13px;
520
+ font-weight: 500;
521
+ white-space: nowrap;
522
+ }
523
+ .vista-error-brand-dot {
524
+ width: 8px;
525
+ height: 8px;
526
+ border-radius: 999px;
527
+ background: #22c55e;
528
+ box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.16);
529
+ }
530
+ .vista-error-toolbar-actions {
531
+ display: inline-flex;
532
+ align-items: center;
533
+ gap: 2px;
534
+ }
535
+ .vista-error-icon-btn {
536
+ width: 28px;
537
+ height: 28px;
538
+ padding: 0;
539
+ border: 0;
540
+ border-radius: 6px;
541
+ background: transparent;
542
+ color: #a1a1a1;
543
+ display: inline-flex;
544
+ align-items: center;
545
+ justify-content: center;
243
546
  cursor: pointer;
244
547
  }
245
- .vista-error-location:hover {
246
- background: rgba(255, 255, 255, 0.1);
548
+ .vista-error-icon-btn:hover { background: #2a2a2a; color: #ededed; }
549
+ .vista-ide-panel-tabs .vista-error-icon-btn:hover { background: #2a2a2a; }
550
+ .vista-error-icon-btn svg { width: 14px; height: 14px; display: block; }
551
+ .vista-error-icon-btn:focus-visible,
552
+ .vista-error-page-btn:focus-visible,
553
+ .vista-ide-tab:focus-visible {
554
+ outline: 2px solid #ededed;
555
+ outline-offset: 1px;
247
556
  }
248
557
  .vista-error-page-root[data-minimized='true'] .vista-error-backdrop,
249
558
  .vista-error-page-root[data-minimized='true'] .vista-error-panel {
@@ -251,49 +560,38 @@ body {
251
560
  }
252
561
  .vista-error-minimized-trigger {
253
562
  position: fixed;
254
- left: 28px;
255
- bottom: 22px;
563
+ left: 20px;
564
+ bottom: 20px;
256
565
  z-index: 2147483647;
257
- height: 40px;
566
+ height: 36px;
258
567
  border-radius: 999px;
259
- border: 1px solid rgba(248, 113, 113, 0.9);
260
- background: rgba(53, 11, 16, 0.95);
261
- color: #fecaca;
262
- box-shadow: 0 10px 24px rgba(248, 113, 113, 0.24);
568
+ border: 1px solid #262626;
569
+ background: #0a0a0a;
570
+ color: #ededed;
571
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
263
572
  display: inline-flex;
264
573
  align-items: center;
265
- gap: 6px;
266
- padding: 0 10px;
574
+ gap: 8px;
575
+ padding: 0 12px 0 10px;
267
576
  cursor: pointer;
268
577
  }
269
- .vista-error-minimized-trigger[hidden] {
270
- display: none;
271
- }
272
- .vista-error-minimized-logo {
273
- width: 14px;
274
- height: 14px;
275
- display: block;
276
- }
277
- .vista-error-minimized-close {
278
- font-size: 12px;
279
- line-height: 1;
280
- color: #fca5a5;
281
- }
578
+ .vista-error-minimized-trigger[hidden] { display: none; }
579
+ .vista-error-minimized-logo { width: 13px; height: 13px; display: block; }
580
+ .vista-error-minimized-close { font-size: 14px; line-height: 1; color: #a1a1a1; }
282
581
  .vista-error-minimized-count {
283
- font-size: 11px;
284
- font-weight: 670;
582
+ font-size: 12px;
583
+ font-weight: 600;
285
584
  font-variant-numeric: tabular-nums;
585
+ color: #ff6369;
286
586
  }
287
587
  @keyframes vista-overlay-in {
288
- from { opacity: 0; transform: translateY(6px) scale(0.99); }
588
+ from { opacity: 0; transform: translateY(8px) scale(0.98); }
289
589
  to { opacity: 1; transform: translateY(0) scale(1); }
290
590
  }
291
- @media (max-width: 860px) {
292
- .vista-error-page-root { padding: 12px; }
293
- .vista-error-header { flex-direction: column; }
294
- .vista-error-controls { width: 100%; justify-content: flex-end; }
295
- .vista-error-title { font-size: 16px; }
296
- .vista-error-minimized-trigger { left: 16px; bottom: 14px; }
591
+ @media (max-width: 720px) {
592
+ .vista-error-page-root { padding: 16px 10px; }
593
+ .vista-error-headline { font-size: 14px; }
594
+ .vista-error-minimized-trigger { left: 12px; bottom: 12px; }
297
595
  }
298
596
  `;
299
597
  const OVERLAY_SCRIPT = `
@@ -346,16 +644,20 @@ function escapeForInlineScript(text) {
346
644
  }
347
645
  function colorizeLineHtml(line) {
348
646
  let escaped = escapeHtml(line);
647
+ const isActive = /^\s*>/.test(line);
349
648
  if (/^\s*\^+/.test(line)) {
350
649
  return `<span class="vista-error-tok-caret">${escaped}</span>`;
351
650
  }
352
651
  if (/^\s*at\s+/.test(line)) {
353
652
  return `<span class="vista-error-tok-stack">${escaped}</span>`;
354
653
  }
355
- const numbered = escaped.match(/^(\s*\d+)(\s*\|)(.*)$/);
654
+ const numbered = escaped.match(/^(\s*>?\s*\d+)(\s*\|)(.*)$/);
356
655
  if (numbered) {
357
656
  escaped = `<span class="vista-error-tok-line">${numbered[1]}</span>${numbered[2]}${numbered[3]}`;
358
657
  }
658
+ if (isActive) {
659
+ escaped = `<span class="vista-error-tok-active">${escaped}</span>`;
660
+ }
359
661
  escaped = escaped.replace(/((?:[A-Za-z]:)?[\\/\w.@%:-]+\.(?:tsx?|jsx?|mjs|cjs|css|json|mdx?)(?::\d+(?::\d+)?)?)/g, '<span class="vista-error-tok-path">$1</span>');
360
662
  escaped = escaped.replace(/(&quot;[^&]+&quot;|&#039;[^&]+&#039;|`[^`]+`)/g, '<span class="vista-error-tok-string">$1</span>');
361
663
  escaped = escaped.replace(/(TypeError:|ReferenceError:|SyntaxError:|Error:|Cannot find module|Module not found|Build Error|Runtime Error|Hydration Error)/g, '<span class="vista-error-tok-error">$1</span>');
@@ -367,6 +669,333 @@ function colorizeBlockHtml(value) {
367
669
  .map((line) => colorizeLineHtml(line))
368
670
  .join('\n');
369
671
  }
672
+ const SOURCE_KEYWORDS = /^(import|from|function|return|const|let|var|export|default|class|extends|if|else|async|await|new|type|interface|true|false|null|undefined|typeof|void|as|of|in|for|while|switch|case|break|continue|try|catch|finally|this)\b/;
673
+ function highlightSource(text) {
674
+ let html = '';
675
+ let i = 0;
676
+ while (i < text.length) {
677
+ const ch = text[i];
678
+ if (ch === "'" || ch === '"' || ch === '`') {
679
+ let j = i + 1;
680
+ while (j < text.length && text[j] !== ch) {
681
+ j += text[j] === '\\' ? 2 : 1;
682
+ }
683
+ html += `<span class="vista-tok-str">${escapeHtml(text.slice(i, Math.min(j + 1, text.length)))}</span>`;
684
+ i = j + 1;
685
+ continue;
686
+ }
687
+ if (ch === '/' && text[i + 1] === '/') {
688
+ html += `<span class="vista-tok-cmt">${escapeHtml(text.slice(i))}</span>`;
689
+ break;
690
+ }
691
+ if (ch === '<' && /[A-Za-z/]/.test(text[i + 1] || '')) {
692
+ const tag = text.slice(i).match(/^<\/?[A-Za-z][\w.-]*/);
693
+ if (tag) {
694
+ html += `<span class="vista-tok-tag">${escapeHtml(tag[0])}</span>`;
695
+ i += tag[0].length;
696
+ continue;
697
+ }
698
+ }
699
+ if (/[0-9]/.test(ch)) {
700
+ const num = text.slice(i).match(/^[0-9]+(?:\.[0-9]+)?/);
701
+ if (num) {
702
+ html += `<span class="vista-tok-num">${escapeHtml(num[0])}</span>`;
703
+ i += num[0].length;
704
+ continue;
705
+ }
706
+ }
707
+ if (/[A-Za-z_$]/.test(ch)) {
708
+ const ident = text.slice(i).match(/^[A-Za-z_$][\w$]*/);
709
+ if (ident) {
710
+ html += SOURCE_KEYWORDS.test(ident[0])
711
+ ? `<span class="vista-tok-kw">${escapeHtml(ident[0])}</span>`
712
+ : escapeHtml(ident[0]);
713
+ i += ident[0].length;
714
+ continue;
715
+ }
716
+ }
717
+ html += escapeHtml(ch);
718
+ i += 1;
719
+ }
720
+ return html;
721
+ }
722
+ function parseCodeFrameLines(codeFrame) {
723
+ const rawLines = String(codeFrame || '').split(/\r?\n/);
724
+ const rows = [];
725
+ for (let i = 0; i < rawLines.length; i += 1) {
726
+ const line = rawLines[i];
727
+ const numbered = line.match(/^(\s*>)?\s*(\d+)\s+\|(.*)$/);
728
+ if (numbered) {
729
+ let text = numbered[3];
730
+ if (text.startsWith(' '))
731
+ text = text.slice(1);
732
+ const row = {
733
+ n: Number(numbered[2]),
734
+ text,
735
+ error: Boolean(numbered[1] && numbered[1].includes('>')),
736
+ squiggleAt: -1,
737
+ };
738
+ const next = rawLines[i + 1] || '';
739
+ const caret = next.match(/\|(.*)$/);
740
+ if (caret && caret[1].includes('^')) {
741
+ let mark = caret[1];
742
+ if (mark.startsWith(' '))
743
+ mark = mark.slice(1);
744
+ row.squiggleAt = Math.max(0, mark.indexOf('^'));
745
+ row.error = true;
746
+ i += 1;
747
+ }
748
+ rows.push(row);
749
+ continue;
750
+ }
751
+ if (/^\s*\|?\s*\^/.test(line))
752
+ continue;
753
+ if (line.trim().length === 0)
754
+ continue;
755
+ rows.push({ n: null, text: line, error: false, squiggleAt: -1 });
756
+ }
757
+ return rows;
758
+ }
759
+ function fillEditorRows(rows, errorLine, errorColumn) {
760
+ const numbered = rows.filter((row) => typeof row.n === 'number');
761
+ if (numbered.length === 0)
762
+ return rows;
763
+ const min = Math.min(...numbered.map((row) => row.n));
764
+ const max = Math.max(...numbered.map((row) => row.n));
765
+ const byNumber = new Map(numbered.map((row) => [row.n, row]));
766
+ const filled = [];
767
+ for (let n = min; n <= max; n += 1) {
768
+ const existing = byNumber.get(n);
769
+ if (existing) {
770
+ if (existing.squiggleAt < 0 && errorLine === n && errorColumn) {
771
+ existing.squiggleAt = Math.max(0, errorColumn - 1);
772
+ existing.error = true;
773
+ }
774
+ filled.push(existing);
775
+ }
776
+ else {
777
+ filled.push({
778
+ n,
779
+ text: '',
780
+ error: errorLine === n,
781
+ squiggleAt: errorLine === n && errorColumn ? Math.max(0, errorColumn - 1) : -1,
782
+ });
783
+ }
784
+ }
785
+ return filled;
786
+ }
787
+ function renderIdeCodeHtml(codeFrame, errorLine, errorColumn) {
788
+ const rows = fillEditorRows(parseCodeFrameLines(codeFrame), errorLine, errorColumn);
789
+ const numbered = rows.filter((row) => typeof row.n === 'number');
790
+ if (numbered.length === 0) {
791
+ return renderLogHtml(codeFrame);
792
+ }
793
+ return `<div class="vista-error-editor">${rows
794
+ .map((row) => {
795
+ const squiggle = row.squiggleAt >= 0
796
+ ? `<span class="vista-error-squiggle" style="width:3.2ch;margin-left:${row.squiggleAt}ch"></span>`
797
+ : '';
798
+ return `<div class="vista-error-editor-line${row.error ? ' is-error' : ''}"><span class="vista-error-gutter">${row.n ?? ''}</span><span class="vista-error-source">${highlightSource(row.text)}${squiggle}</span></div>`;
799
+ })
800
+ .join('')}</div>`;
801
+ }
802
+ function renderLogHtml(text, lineClass = '') {
803
+ const lines = String(text || '').split(/\r?\n/);
804
+ if (lines.length === 1 && !lines[0]) {
805
+ return '<div class="vista-error-editor"><div class="vista-error-editor-line"><span class="vista-error-gutter"></span><span class="vista-error-source"></span></div></div>';
806
+ }
807
+ return `<div class="vista-error-editor">${lines
808
+ .map((line, index) => `<div class="vista-error-editor-line${lineClass}"><span class="vista-error-gutter">${index + 1}</span><span class="vista-error-source">${highlightSource(line)}</span></div>`)
809
+ .join('')}</div>`;
810
+ }
811
+ function renderLocationEditor(location) {
812
+ if (location.kind === 'server')
813
+ return renderLogHtml(location.codeFrame || '', ' is-server');
814
+ if (location.kind === 'client')
815
+ return renderLogHtml(location.codeFrame || '', ' is-client');
816
+ if (location.kind === 'log')
817
+ return renderLogHtml(location.codeFrame || location.file);
818
+ return renderIdeCodeHtml(location.codeFrame || '', location.line, location.column);
819
+ }
820
+ function tabLabel(location) {
821
+ if (location.kind === 'server')
822
+ return 'server';
823
+ if (location.kind === 'client')
824
+ return 'client';
825
+ return fileBasename(location.file);
826
+ }
827
+ function renderFileTabsHtml(related, activeIndex) {
828
+ return related
829
+ .map((location, index) => {
830
+ const name = tabLabel(location);
831
+ const inactive = index === activeIndex ? '' : ' is-inactive';
832
+ return `<button type="button" class="vista-ide-tab${inactive}" data-vista-file-tab="${index}"><svg class="vista-ide-tab-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true"><rect x="3" y="2" width="10" height="12" rx="1.5" stroke="#4fc1ff" stroke-width="1.4"/><path d="M6 6h4M6 9h4M6 12h2" stroke="#4fc1ff" stroke-width="1.2" stroke-linecap="round"/></svg><span>${escapeHtml(name)}</span></button>`;
833
+ })
834
+ .join('');
835
+ }
836
+ function isNoiseFile(file) {
837
+ const normalized = file.replace(/\\/g, '/');
838
+ return /node_modules\/|node:|webpack\/runtime|\/vista\/dist\//.test(normalized);
839
+ }
840
+ function parseLocationsFromText(text) {
841
+ const found = [];
842
+ const seen = new Set();
843
+ const pattern = /(?:ERROR in\s+)?((?:[A-Za-z]:)?[./\\]?[\w.@/-]+\.(?:tsx?|jsx?|mjs|cjs|css|json|mdx?))(?::(\d+)(?::(\d+))?)?/g;
844
+ let match;
845
+ while ((match = pattern.exec(text))) {
846
+ const file = match[1].replace(/\\/g, '/').replace(/^\.\//, '');
847
+ if (isNoiseFile(file))
848
+ continue;
849
+ const key = `${file}:${match[2] || ''}`;
850
+ if (seen.has(key) || seen.has(file))
851
+ continue;
852
+ seen.add(file);
853
+ seen.add(key);
854
+ found.push({
855
+ file,
856
+ line: match[2] ? Number(match[2]) : undefined,
857
+ column: match[3] ? Number(match[3]) : undefined,
858
+ kind: 'source',
859
+ });
860
+ if (found.length >= 8)
861
+ break;
862
+ }
863
+ return found;
864
+ }
865
+ function parseHydrationMismatch(message) {
866
+ const serverLines = [];
867
+ const clientLines = [];
868
+ for (const rawLine of String(message || '').split(/\r?\n/)) {
869
+ if (/^\s*-\s+(A |Variable |Date |External |Invalid |It can also|https?:)/i.test(rawLine))
870
+ continue;
871
+ if (/^\s*-\s+/.test(rawLine)) {
872
+ serverLines.push(rawLine.replace(/^\s*-\s?/, ''));
873
+ }
874
+ else if (/^\s*\+\s+/.test(rawLine)) {
875
+ clientLines.push(rawLine.replace(/^\s*\+\s?/, ''));
876
+ }
877
+ }
878
+ if (serverLines.length === 0 && clientLines.length === 0)
879
+ return null;
880
+ return { server: serverLines.join('\n'), client: clientLines.join('\n') };
881
+ }
882
+ function defaultHint(error) {
883
+ if (error.hint)
884
+ return error.hint;
885
+ if (error.type === 'hydration') {
886
+ return 'Server HTML did not match the client. Check Date.now(), Math.random(), invalid tag nesting, or a missing \'use client\'.';
887
+ }
888
+ if (error.source === 'server') {
889
+ return 'Thrown while rendering on the server. Open the first app frame in the call stack.';
890
+ }
891
+ if (error.type === 'build' && (error.related?.length || 0) > 1) {
892
+ return `${error.related.length} files are involved. Switch editor tabs to inspect each one.`;
893
+ }
894
+ return '';
895
+ }
896
+ function enrichError(error) {
897
+ const message = error.message || '';
898
+ const stack = error.stack || '';
899
+ let type = error.type;
900
+ let source = error.source;
901
+ if (type !== 'hydration' && /hydration|did not match|server rendered html|text content does not match/i.test(message)) {
902
+ type = 'hydration';
903
+ }
904
+ if (!source && /Flight SSR|Server Error|renderToString|rsc-engine|vista[\\/]server/i.test(`${message}\n${stack}`)) {
905
+ source = 'server';
906
+ }
907
+ const related = [];
908
+ const seen = new Set();
909
+ const add = (location) => {
910
+ const file = location.file.replace(/\\/g, '/').replace(/^\.\//, '');
911
+ if (!file || isNoiseFile(file))
912
+ return;
913
+ const kind = location.kind || 'source';
914
+ const key = kind === 'source' || kind === 'log' ? `${kind}:${file}` : `${kind}:${file}:${location.line || ''}`;
915
+ if (seen.has(key))
916
+ return;
917
+ seen.add(key);
918
+ related.push({ ...location, file, kind });
919
+ };
920
+ if (error.file) {
921
+ add({
922
+ file: error.file,
923
+ line: error.line,
924
+ column: error.column,
925
+ codeFrame: error.codeFrame,
926
+ kind: 'source',
927
+ });
928
+ }
929
+ for (const frame of parseStackFrames(stack)) {
930
+ if (!frame.file)
931
+ continue;
932
+ add({ file: frame.file, line: frame.line || undefined, column: frame.column || undefined, kind: 'source' });
933
+ }
934
+ for (const location of parseLocationsFromText(`${message}\n${stack}`)) {
935
+ add(location);
936
+ }
937
+ const mismatch = type === 'hydration' ? parseHydrationMismatch(message) : null;
938
+ if (mismatch) {
939
+ if (mismatch.server)
940
+ add({ file: 'server.html', codeFrame: mismatch.server, kind: 'server' });
941
+ if (mismatch.client)
942
+ add({ file: 'client.html', codeFrame: mismatch.client, kind: 'client' });
943
+ }
944
+ if (related.length === 0) {
945
+ add({
946
+ file: error.file || (type === 'build' ? 'build.log' : 'error.log'),
947
+ line: error.line,
948
+ column: error.column,
949
+ codeFrame: error.codeFrame || (error.stack ? error.stack : error.message),
950
+ kind: error.codeFrame ? 'source' : 'log',
951
+ });
952
+ }
953
+ else if (error.codeFrame && !related[0].codeFrame) {
954
+ related[0] = { ...related[0], codeFrame: error.codeFrame };
955
+ }
956
+ const primary = related.find((item) => item.kind === 'source') || related[0];
957
+ const next = {
958
+ ...error,
959
+ type,
960
+ source,
961
+ file: error.file || primary?.file,
962
+ line: error.line || primary?.line,
963
+ column: error.column || primary?.column,
964
+ related,
965
+ };
966
+ next.hint = defaultHint(next);
967
+ return next;
968
+ }
969
+ function parseStackFrames(stack) {
970
+ return parseStackTrace(stack).map((line) => {
971
+ const match = line.match(/^\s*at\s+(?:async\s+)?(?:(.+?)\s+\()?((?:[A-Za-z]:)?[^()\s]+):(\d+):(\d+)\)?\s*$/);
972
+ if (match) {
973
+ return {
974
+ name: (match[1] || '(anonymous)').replace(/^Object\./, ''),
975
+ file: match[2],
976
+ line: Number(match[3]),
977
+ column: Number(match[4]),
978
+ };
979
+ }
980
+ return { name: line.replace(/^\s*at\s+/, ''), file: '', line: 0, column: 0 };
981
+ });
982
+ }
983
+ function renderStackFramesHtml(stack) {
984
+ const frames = parseStackFrames(stack);
985
+ if (frames.length === 0)
986
+ return '';
987
+ return `<div class="vista-error-frames">${frames
988
+ .map((frame) => {
989
+ const loc = frame.file
990
+ ? `${frame.file.replace(/\\/g, '/')}${frame.line ? `:${frame.line}` : ''}${frame.column ? `:${frame.column}` : ''}`
991
+ : '';
992
+ const open = frame.file
993
+ ? ` data-vista-open-file="${escapeHtml(frame.file)}" data-vista-open-line="${frame.line || 1}" data-vista-open-col="${frame.column || 1}"`
994
+ : '';
995
+ return `<button type="button" class="vista-error-frame"${open}><span class="vista-error-frame-name">${escapeHtml(frame.name)}</span>${loc ? `<span class="vista-error-frame-loc">${escapeHtml(loc)}</span>` : ''}</button>`;
996
+ })
997
+ .join('')}</div>`;
998
+ }
370
999
  function parseStackTrace(stack) {
371
1000
  if (!stack)
372
1001
  return [];
@@ -376,43 +1005,125 @@ function parseStackTrace(stack) {
376
1005
  .filter((line) => line.trim().length > 0);
377
1006
  }
378
1007
  function normalizeError(error) {
379
- return {
1008
+ return enrichError({
380
1009
  type: error?.type === 'build' || error?.type === 'hydration' || error?.type === 'runtime'
381
1010
  ? error.type
382
1011
  : 'runtime',
1012
+ source: error?.source === 'server' || error?.source === 'client' ? error.source : undefined,
383
1013
  message: typeof error?.message === 'string' ? error.message : 'Unknown Error',
384
1014
  stack: typeof error?.stack === 'string' ? error.stack : undefined,
385
1015
  file: typeof error?.file === 'string' ? error.file : undefined,
386
1016
  line: typeof error?.line === 'number' ? error.line : undefined,
387
1017
  column: typeof error?.column === 'number' ? error.column : undefined,
388
1018
  codeFrame: typeof error?.codeFrame === 'string' ? error.codeFrame : undefined,
389
- };
1019
+ related: Array.isArray(error?.related) ? error.related : undefined,
1020
+ hint: typeof error?.hint === 'string' ? error.hint : undefined,
1021
+ });
390
1022
  }
391
1023
  function normalizeErrors(errors) {
392
1024
  if (!Array.isArray(errors) || errors.length === 0) {
393
- return [
394
- {
395
- type: 'runtime',
396
- message: 'Unknown Error',
397
- },
398
- ];
1025
+ return [normalizeError({ type: 'runtime', message: 'Unknown Error' })];
399
1026
  }
400
1027
  return errors.map((error) => normalizeError(error));
401
1028
  }
402
- function getErrorTypeLabel(type) {
403
- if (type === 'build')
1029
+ function locationHasCode(location) {
1030
+ if (!location)
1031
+ return false;
1032
+ if ((location.kind || 'source') !== 'source')
1033
+ return false;
1034
+ return Boolean(location.codeFrame && String(location.codeFrame).trim());
1035
+ }
1036
+ function hasCodeView(error) {
1037
+ const related = error.related || [];
1038
+ if (related.some(locationHasCode))
1039
+ return true;
1040
+ return Boolean(error.codeFrame && String(error.codeFrame).trim());
1041
+ }
1042
+ function getErrorTypeLabel(error) {
1043
+ if (error.type === 'build')
404
1044
  return 'Build Error';
405
- if (type === 'hydration')
1045
+ if (error.type === 'hydration')
406
1046
  return 'Hydration Error';
407
- return 'Runtime Error';
1047
+ if (error.source === 'server')
1048
+ return 'Server Error';
1049
+ return 'Unhandled Runtime Error';
1050
+ }
1051
+ function problemTone(error) {
1052
+ if (error.type === 'hydration')
1053
+ return 'hydration';
1054
+ if (error.source === 'server')
1055
+ return 'server';
1056
+ return '';
408
1057
  }
409
- function getErrorTitle(message, fallback) {
410
- const lines = message
1058
+ function sourceBadge(error) {
1059
+ if (error.type === 'hydration')
1060
+ return { label: 'Hydration', tone: 'hydration' };
1061
+ if (error.source === 'server')
1062
+ return { label: 'Server', tone: 'server' };
1063
+ if (error.source === 'client')
1064
+ return { label: 'Client', tone: 'client' };
1065
+ if (error.type === 'build')
1066
+ return { label: 'Build', tone: '' };
1067
+ return { label: 'Client', tone: 'client' };
1068
+ }
1069
+ function fromCaughtError(error, options) {
1070
+ const err = error instanceof Error ? error : new Error(String(error || 'Unknown Error'));
1071
+ return normalizeError({
1072
+ type: options?.type || 'runtime',
1073
+ source: options?.source,
1074
+ message: err.message || 'Unknown Error',
1075
+ stack: err.stack,
1076
+ });
1077
+ }
1078
+ function splitMessage(message) {
1079
+ const lines = String(message || '')
411
1080
  .split(/\r?\n/)
412
1081
  .map((line) => line.trim())
413
1082
  .filter(Boolean);
414
- const title = lines[0] || fallback;
415
- return title.length > 140 ? `${title.slice(0, 137)}...` : title;
1083
+ return {
1084
+ headline: lines[0] || 'Unknown Error',
1085
+ detail: lines.slice(1).join('\n'),
1086
+ };
1087
+ }
1088
+ function formatCodeHeader(error) {
1089
+ if (!error.file)
1090
+ return '';
1091
+ const file = error.file.replace(/\\/g, '/');
1092
+ if (!error.line)
1093
+ return file;
1094
+ return `${file} (${error.line}${error.column ? `:${error.column}` : ''})`;
1095
+ }
1096
+ function fileBasename(file) {
1097
+ if (!file)
1098
+ return 'untitled';
1099
+ const normalized = file.replace(/\\/g, '/');
1100
+ return normalized.split('/').filter(Boolean).pop() || normalized;
1101
+ }
1102
+ function fileBreadcrumb(file) {
1103
+ if (!file)
1104
+ return '';
1105
+ return file.replace(/\\/g, '/').split('/').filter(Boolean).join(' › ');
1106
+ }
1107
+ function languageFromFile(file) {
1108
+ const ext = (file || '').split('.').pop()?.toLowerCase();
1109
+ if (ext === 'tsx')
1110
+ return 'TypeScript React';
1111
+ if (ext === 'ts')
1112
+ return 'TypeScript';
1113
+ if (ext === 'jsx')
1114
+ return 'JavaScript React';
1115
+ if (ext === 'js' || ext === 'mjs' || ext === 'cjs')
1116
+ return 'JavaScript';
1117
+ if (ext === 'css')
1118
+ return 'CSS';
1119
+ if (ext === 'json')
1120
+ return 'JSON';
1121
+ return 'Plain Text';
1122
+ }
1123
+ function statusLine(error) {
1124
+ if (!error.line)
1125
+ return 'Ready';
1126
+ return `Ln ${error.line}, Col ${error.column || 1}`;
416
1127
  }
417
1128
  function formatLocation(error) {
418
1129
  if (!error.file)
@@ -423,7 +1134,7 @@ function formatLocation(error) {
423
1134
  return `${normalized}:${error.line}${error.column ? `:${error.column}` : ''}`;
424
1135
  }
425
1136
  function serializeErrorForCopy(error) {
426
- const label = getErrorTypeLabel(error.type);
1137
+ const label = getErrorTypeLabel(error);
427
1138
  const parts = [label, '', error.message || 'Unknown Error'];
428
1139
  if (error.file) {
429
1140
  parts.push('');
@@ -485,14 +1196,40 @@ function copyTextInBrowser(value) {
485
1196
  function renderErrorHTML(errors) {
486
1197
  const normalizedErrors = normalizeErrors(errors);
487
1198
  const firstError = normalizedErrors[0];
488
- const firstLabel = getErrorTypeLabel(firstError.type);
489
- const firstTitle = getErrorTitle(firstError.message || 'Unknown Error', firstLabel);
490
- const firstLocation = formatLocation(firstError);
1199
+ const firstLabel = getErrorTypeLabel(firstError);
1200
+ const firstParts = splitMessage(firstError.message || 'Unknown Error');
1201
+ const firstRelated = firstError.related && firstError.related.length > 0
1202
+ ? firstError.related
1203
+ : [
1204
+ {
1205
+ file: firstError.file || 'untitled',
1206
+ line: firstError.line,
1207
+ column: firstError.column,
1208
+ codeFrame: firstError.codeFrame,
1209
+ kind: 'source',
1210
+ },
1211
+ ];
1212
+ const firstLocationEntry = firstRelated[0];
1213
+ const firstLocation = formatCodeHeader(firstError) || formatLocation(firstError);
491
1214
  const firstStack = parseStackTrace(firstError.stack || '').join('\n');
492
- const firstCode = firstError.codeFrame || '';
493
- const firstMessageHtml = colorizeBlockHtml(firstError.message || 'Unknown Error');
494
- const firstCodeHtml = colorizeBlockHtml(firstCode);
495
- const firstStackHtml = colorizeBlockHtml(firstStack);
1215
+ const firstCodeHtml = renderLocationEditor(firstLocationEntry);
1216
+ const firstCrumbs = fileBreadcrumb(firstLocationEntry.file) || firstLocation || 'Source';
1217
+ const firstStatus = firstLocationEntry.line
1218
+ ? `Ln ${firstLocationEntry.line}, Col ${firstLocationEntry.column || 1}`
1219
+ : statusLine(firstError);
1220
+ const firstLang = firstLocationEntry.kind === 'server' || firstLocationEntry.kind === 'client'
1221
+ ? 'HTML'
1222
+ : firstLocationEntry.kind === 'log'
1223
+ ? 'Log'
1224
+ : languageFromFile(firstLocationEntry.file);
1225
+ const firstStackHtml = renderStackFramesHtml(firstStack);
1226
+ const firstTabsHtml = renderFileTabsHtml(firstRelated, 0);
1227
+ const firstTone = problemTone(firstError);
1228
+ const firstBadge = sourceBadge(firstError);
1229
+ const firstHint = firstError.hint || '';
1230
+ const firstTerminal = !hasCodeView(firstError);
1231
+ const firstTitleName = firstTerminal ? firstLabel : tabLabel(firstLocationEntry);
1232
+ const showPager = normalizedErrors.length > 1;
496
1233
  const serializedErrors = escapeForInlineScript(JSON.stringify(normalizedErrors));
497
1234
  return `<!DOCTYPE html>
498
1235
  <html lang="en">
@@ -526,13 +1263,17 @@ function renderErrorHTML(errors) {
526
1263
  errors = [{ type: 'runtime', message: 'Unknown Error' }];
527
1264
  }
528
1265
 
529
- var state = { index: 0, errors: errors };
1266
+ var state = { index: 0, fileTab: 0, errors: errors };
530
1267
  var typeNode;
531
1268
  var titleNode;
532
- var metaNode;
533
- var messageNode;
534
- var locationWrap;
535
- var locationText;
1269
+ var hintNode;
1270
+ var fileTabsNode;
1271
+ var sourceBadgeNode;
1272
+ var problemIconNode;
1273
+ var tabNameNodes;
1274
+ var crumbsNode;
1275
+ var statusLnNode;
1276
+ var statusLangNode;
536
1277
  var locationButton;
537
1278
  var codeWrap;
538
1279
  var codeNode;
@@ -548,29 +1289,34 @@ function renderErrorHTML(errors) {
548
1289
  var backdropNode;
549
1290
  var minimizedTrigger;
550
1291
  var minimizedCount;
1292
+ var mainNode;
1293
+ var panelExpandButton;
551
1294
 
552
- function getLabel(type) {
553
- if (type === 'build') return 'Build Error';
554
- if (type === 'hydration') return 'Hydration Error';
555
- return 'Runtime Error';
1295
+ function getLabel(error) {
1296
+ if (error.type === 'build') return 'Build Error';
1297
+ if (error.type === 'hydration') return 'Hydration Error';
1298
+ if (error.source === 'server') return 'Server Error';
1299
+ return 'Unhandled Runtime Error';
556
1300
  }
557
1301
 
558
- function getTitle(message, fallback) {
1302
+ function splitMessage(message) {
559
1303
  var lines = String(message || '')
560
1304
  .split(/\\r?\\n/)
561
1305
  .map(function(line) { return line.trim(); })
562
1306
  .filter(function(line) { return line.length > 0; });
563
- var title = lines.length ? lines[0] : fallback;
564
- return title.length > 140 ? title.slice(0, 137) + '...' : title;
565
- }
566
-
567
- function pad2(value) {
568
- return value < 10 ? '0' + value : String(value);
1307
+ return {
1308
+ headline: lines.length ? lines[0] : 'Unknown Error',
1309
+ detail: lines.slice(1).join('\\n')
1310
+ };
569
1311
  }
570
1312
 
571
- function formatTimeNow() {
572
- var now = new Date();
573
- return pad2(now.getHours()) + ':' + pad2(now.getMinutes()) + ':' + pad2(now.getSeconds());
1313
+ function formatCodeHeader(error) {
1314
+ if (!error || !error.file) return '';
1315
+ var file = String(error.file).replace(/\\\\/g, '/');
1316
+ var line = Number(error.line || 0);
1317
+ var column = Number(error.column || 0);
1318
+ if (!line) return file;
1319
+ return file + ' (' + line + (column ? ':' + column : '') + ')';
574
1320
  }
575
1321
 
576
1322
  function currentError() {
@@ -599,44 +1345,200 @@ function renderErrorHTML(errors) {
599
1345
  .replace(/'/g, '&#039;');
600
1346
  }
601
1347
 
602
- function colorizeLine(line) {
603
- var escaped = escapeHtml(line);
1348
+ function highlightSource(text) {
1349
+ var html = '';
1350
+ var i = 0;
1351
+ var keywords = /^(import|from|function|return|const|let|var|export|default|class|extends|if|else|async|await|new|type|interface|true|false|null|undefined|typeof|void|as|of|in|for|while|switch|case|break|continue|try|catch|finally|this)\\b/;
1352
+ while (i < text.length) {
1353
+ var ch = text.charAt(i);
1354
+ if (ch === String.fromCharCode(39) || ch === String.fromCharCode(34) || ch === String.fromCharCode(96)) {
1355
+ var j = i + 1;
1356
+ while (j < text.length && text.charAt(j) !== ch) {
1357
+ j += text.charAt(j) === '\\\\' ? 2 : 1;
1358
+ }
1359
+ html += '<span class=\"vista-tok-str\">' + escapeHtml(text.slice(i, Math.min(j + 1, text.length))) + '</span>';
1360
+ i = j + 1;
1361
+ continue;
1362
+ }
1363
+ if (ch === '/' && text.charAt(i + 1) === '/') {
1364
+ html += '<span class=\"vista-tok-cmt\">' + escapeHtml(text.slice(i)) + '</span>';
1365
+ break;
1366
+ }
1367
+ if (ch === '<' && /[A-Za-z\\/]/.test(text.charAt(i + 1) || '')) {
1368
+ var tag = text.slice(i).match(/^<\\/?[A-Za-z][\\w.-]*/);
1369
+ if (tag) {
1370
+ html += '<span class=\"vista-tok-tag\">' + escapeHtml(tag[0]) + '</span>';
1371
+ i += tag[0].length;
1372
+ continue;
1373
+ }
1374
+ }
1375
+ if (/[0-9]/.test(ch)) {
1376
+ var num = text.slice(i).match(/^[0-9]+(?:\\.[0-9]+)?/);
1377
+ if (num) {
1378
+ html += '<span class=\"vista-tok-num\">' + escapeHtml(num[0]) + '</span>';
1379
+ i += num[0].length;
1380
+ continue;
1381
+ }
1382
+ }
1383
+ if (/[A-Za-z_$]/.test(ch)) {
1384
+ var ident = text.slice(i).match(/^[A-Za-z_$][\\w$]*/);
1385
+ if (ident) {
1386
+ html += keywords.test(ident[0])
1387
+ ? '<span class=\"vista-tok-kw\">' + escapeHtml(ident[0]) + '</span>'
1388
+ : escapeHtml(ident[0]);
1389
+ i += ident[0].length;
1390
+ continue;
1391
+ }
1392
+ }
1393
+ html += escapeHtml(ch);
1394
+ i += 1;
1395
+ }
1396
+ return html;
1397
+ }
604
1398
 
605
- if (/^\\s*\\^+/.test(line)) {
606
- return '<span class=\"vista-error-tok-caret\">' + escaped + '</span>';
1399
+ function renderIdeCode(codeFrame, errorLine, errorColumn) {
1400
+ var rawLines = String(codeFrame || '').split(/\\r?\\n/);
1401
+ var rows = [];
1402
+ for (var i = 0; i < rawLines.length; i += 1) {
1403
+ var line = rawLines[i];
1404
+ var numbered = line.match(/^(\\s*>)?\\s*(\\d+)\\s+\\|(.*)$/);
1405
+ if (!numbered) continue;
1406
+ var src = numbered[3];
1407
+ if (src.charAt(0) === ' ') src = src.slice(1);
1408
+ var isError = !!(numbered[1] && numbered[1].indexOf('>') !== -1);
1409
+ var squiggleAt = -1;
1410
+ var next = rawLines[i + 1] || '';
1411
+ var caret = next.match(/\\|(.*)$/);
1412
+ if (caret && caret[1].indexOf('^') !== -1) {
1413
+ var mark = caret[1];
1414
+ if (mark.charAt(0) === ' ') mark = mark.slice(1);
1415
+ squiggleAt = Math.max(0, mark.indexOf('^'));
1416
+ isError = true;
1417
+ i += 1;
1418
+ }
1419
+ rows.push({ n: Number(numbered[2]), text: src, error: isError, squiggleAt: squiggleAt });
607
1420
  }
608
- if (/^\\s*at\\s+/.test(line)) {
609
- return '<span class=\"vista-error-tok-stack\">' + escaped + '</span>';
1421
+ if (!rows.length) return '';
1422
+ var min = rows[0].n;
1423
+ var max = rows[0].n;
1424
+ var byN = {};
1425
+ for (var r = 0; r < rows.length; r += 1) {
1426
+ byN[rows[r].n] = rows[r];
1427
+ if (rows[r].n < min) min = rows[r].n;
1428
+ if (rows[r].n > max) max = rows[r].n;
610
1429
  }
1430
+ var html = '';
1431
+ for (var n = min; n <= max; n += 1) {
1432
+ var row = byN[n] || { n: n, text: '', error: Number(errorLine) === n, squiggleAt: -1 };
1433
+ if (row.squiggleAt < 0 && Number(errorLine) === n && errorColumn) {
1434
+ row.squiggleAt = Math.max(0, Number(errorColumn) - 1);
1435
+ row.error = true;
1436
+ }
1437
+ html += '<div class=\"vista-error-editor-line' + (row.error ? ' is-error' : '') + '\">';
1438
+ html += '<span class=\"vista-error-gutter\">' + row.n + '</span>';
1439
+ html += '<span class=\"vista-error-source\">' + highlightSource(row.text);
1440
+ if (row.squiggleAt >= 0) {
1441
+ html += '<span class=\"vista-error-squiggle\" style=\"width:3.2ch;margin-left:' + row.squiggleAt + 'ch\"></span>';
1442
+ }
1443
+ html += '</span></div>';
1444
+ }
1445
+ return '<div class=\"vista-error-editor\">' + html + '</div>';
1446
+ }
611
1447
 
612
- var numbered = escaped.match(/^(\\s*\\d+)(\\s*\\|)(.*)$/);
613
- if (numbered) {
614
- escaped =
615
- '<span class=\"vista-error-tok-line\">' + numbered[1] + '</span>' + numbered[2] + numbered[3];
1448
+ function renderLog(text, extraClass) {
1449
+ var lines = String(text || '').split(/\\r?\\n/);
1450
+ var html = '';
1451
+ for (var i = 0; i < lines.length; i += 1) {
1452
+ html += '<div class=\"vista-error-editor-line' + (extraClass || '') + '\">';
1453
+ html += '<span class=\"vista-error-gutter\">' + (i + 1) + '</span>';
1454
+ html += '<span class=\"vista-error-source\">' + highlightSource(lines[i]) + '</span></div>';
616
1455
  }
1456
+ return '<div class=\"vista-error-editor\">' + html + '</div>';
1457
+ }
617
1458
 
618
- escaped = escaped.replace(
619
- /((?:[A-Za-z]:)?[\\\\/\\w.@%:-]+\\.(?:tsx?|jsx?|mjs|cjs|css|json|mdx?)(?::\\d+(?::\\d+)?)?)/g,
620
- '<span class=\"vista-error-tok-path\">$1</span>'
621
- );
622
- escaped = escaped.replace(
623
- /(&quot;[^&]+&quot;|&#039;[^&]+&#039;)/g,
624
- '<span class=\"vista-error-tok-string\">$1</span>'
625
- );
626
- escaped = escaped.replace(
627
- /(TypeError:|ReferenceError:|SyntaxError:|Error:|Cannot find module|Module not found|Build Error|Runtime Error|Hydration Error)/g,
628
- '<span class=\"vista-error-tok-error\">$1</span>'
629
- );
630
- return escaped;
1459
+ function relatedFor(error) {
1460
+ if (error.related && error.related.length) return error.related;
1461
+ return [{ file: error.file || 'untitled', line: error.line, column: error.column, codeFrame: error.codeFrame, kind: 'source' }];
631
1462
  }
632
1463
 
633
- function colorizeBlock(value) {
634
- return String(value || '')
635
- .split(/\\r?\\n/)
636
- .map(function(line) {
637
- return colorizeLine(line);
638
- })
639
- .join('\\n');
1464
+ function locationHasCode(loc) {
1465
+ if (!loc) return false;
1466
+ if ((loc.kind || 'source') !== 'source') return false;
1467
+ return !!(loc.codeFrame && String(loc.codeFrame).trim());
1468
+ }
1469
+
1470
+ function hasCodeView(error) {
1471
+ var related = relatedFor(error);
1472
+ for (var i = 0; i < related.length; i += 1) {
1473
+ if (locationHasCode(related[i])) return true;
1474
+ }
1475
+ return !!(error.codeFrame && String(error.codeFrame).trim());
1476
+ }
1477
+
1478
+ function tabLabelFor(loc) {
1479
+ if (loc.kind === 'server') return 'server';
1480
+ if (loc.kind === 'client') return 'client';
1481
+ var file = String(loc.file || 'untitled').replace(/\\\\/g, '/');
1482
+ return file.split('/').filter(Boolean).pop() || file;
1483
+ }
1484
+
1485
+ function renderLocation(loc) {
1486
+ if (loc.kind === 'server') return renderLog(loc.codeFrame || '', ' is-server');
1487
+ if (loc.kind === 'client') return renderLog(loc.codeFrame || '', ' is-client');
1488
+ if (loc.kind === 'log') return renderLog(loc.codeFrame || loc.file || '');
1489
+ var frame = loc.codeFrame || '';
1490
+ var html = renderIdeCode(frame, loc.line, loc.column);
1491
+ return html || renderLog(frame);
1492
+ }
1493
+
1494
+ function renderFileTabs(related, active) {
1495
+ var html = '';
1496
+ for (var i = 0; i < related.length; i += 1) {
1497
+ html += '<button type=\"button\" class=\"vista-ide-tab' + (i === active ? '' : ' is-inactive') + '\" data-vista-file-tab=\"' + i + '\">';
1498
+ html += '<svg class=\"vista-ide-tab-icon\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><rect x=\"3\" y=\"2\" width=\"10\" height=\"12\" rx=\"1.5\" stroke=\"#4fc1ff\" stroke-width=\"1.4\"/><path d=\"M6 6h4M6 9h4M6 12h2\" stroke=\"#4fc1ff\" stroke-width=\"1.2\" stroke-linecap=\"round\"/></svg>';
1499
+ html += '<span>' + escapeHtml(tabLabelFor(related[i])) + '</span></button>';
1500
+ }
1501
+ return html;
1502
+ }
1503
+
1504
+ function langFor(loc) {
1505
+ if (loc.kind === 'server' || loc.kind === 'client') return 'HTML';
1506
+ if (loc.kind === 'log') return 'Log';
1507
+ var ext = String(loc.file || '').split('.').pop().toLowerCase();
1508
+ if (ext === 'tsx') return 'TypeScript React';
1509
+ if (ext === 'ts') return 'TypeScript';
1510
+ if (ext === 'jsx') return 'JavaScript React';
1511
+ if (ext === 'js' || ext === 'mjs' || ext === 'cjs') return 'JavaScript';
1512
+ return 'Plain Text';
1513
+ }
1514
+
1515
+ function renderStackFrames(stack) {
1516
+ var lines = String(stack || '').split(/\\r?\\n/).filter(function(line) { return line.trim().length > 0; });
1517
+ var html = '';
1518
+ for (var s = 0; s < lines.length; s += 1) {
1519
+ var line = lines[s];
1520
+ var match = line.match(/^\\s*at\\s+(?:async\\s+)?(?:(.+?)\\s+\\()?((?:[A-Za-z]:)?[^()\\s]+):(\\d+):(\\d+)\\)?\\s*$/);
1521
+ var name = line.replace(/^\\s*at\\s+/, '');
1522
+ var file = '';
1523
+ var ln = 1;
1524
+ var col = 1;
1525
+ var loc = '';
1526
+ if (match) {
1527
+ name = (match[1] || '(anonymous)').replace(/^Object\\./, '');
1528
+ file = match[2];
1529
+ ln = Number(match[3]);
1530
+ col = Number(match[4]);
1531
+ loc = file.replace(/\\\\/g, '/') + ':' + ln + ':' + col;
1532
+ }
1533
+ html += '<button type=\"button\" class=\"vista-error-frame\"';
1534
+ if (file) {
1535
+ html += ' data-vista-open-file=\"' + escapeHtml(file) + '\" data-vista-open-line=\"' + ln + '\" data-vista-open-col=\"' + col + '\"';
1536
+ }
1537
+ html += '><span class=\"vista-error-frame-name\">' + escapeHtml(name) + '</span>';
1538
+ if (loc) html += '<span class=\"vista-error-frame-loc\">' + escapeHtml(loc) + '</span>';
1539
+ html += '</button>';
1540
+ }
1541
+ return html ? '<div class=\"vista-error-frames\">' + html + '</div>' : '';
640
1542
  }
641
1543
 
642
1544
  function minimizeOverlay() {
@@ -663,41 +1565,84 @@ function renderErrorHTML(errors) {
663
1565
  var error = currentError();
664
1566
  if (!error) return;
665
1567
 
666
- var label = getLabel(error.type);
1568
+ var label = getLabel(error);
667
1569
  var message = typeof error.message === 'string' ? error.message : String(error.message || 'Unknown Error');
668
- var title = getTitle(message, label);
669
- var location = formatLocation(error);
670
- var codeFrame = typeof error.codeFrame === 'string' ? error.codeFrame : '';
1570
+ var parts = splitMessage(message);
671
1571
  var stack = typeof error.stack === 'string' ? error.stack.trim() : '';
1572
+ var related = relatedFor(error);
1573
+ if (state.fileTab > related.length - 1) state.fileTab = 0;
1574
+ var loc = related[state.fileTab] || related[0];
1575
+ var terminal = !hasCodeView(error);
1576
+ var file = String((loc && loc.file) || error.file || '').replace(/\\\\/g, '/');
1577
+ var tabName = terminal ? label : (loc ? tabLabelFor(loc) : 'untitled');
1578
+ var crumbs = file ? file.split('/').filter(Boolean).join(' › ') : 'Source';
1579
+ var lang = loc ? langFor(loc) : 'Plain Text';
1580
+ var tone = error.type === 'hydration' ? 'hydration' : error.source === 'server' ? 'server' : '';
1581
+ var badge = error.type === 'hydration' ? 'Hydration' : error.source === 'server' ? 'Server' : error.type === 'build' ? 'Build' : 'Client';
1582
+ var badgeTone = error.type === 'hydration' ? 'hydration' : error.source === 'server' ? 'server' : error.source === 'client' || error.type !== 'build' ? 'client' : '';
1583
+ var hint = error.hint || '';
672
1584
 
673
- if (typeNode) typeNode.textContent = label;
674
- if (titleNode) titleNode.textContent = title;
675
- if (metaNode) {
676
- metaNode.textContent =
677
- 'Vista Dev Overlay | ' + formatTimeNow() + ' | ' + (state.index + 1) + ' of ' + state.errors.length;
1585
+ if (typeNode) {
1586
+ typeNode.textContent = label;
1587
+ typeNode.className = 'vista-error-kind' + (tone ? ' is-' + tone : '');
1588
+ }
1589
+ if (problemIconNode) {
1590
+ problemIconNode.className = 'vista-error-problem-icon' + (tone ? ' is-' + tone : '');
1591
+ }
1592
+ if (titleNode) titleNode.textContent = parts.headline;
1593
+ if (detailNode) {
1594
+ detailNode.textContent = parts.detail;
1595
+ detailNode.hidden = parts.detail.length === 0;
1596
+ }
1597
+ if (hintNode) {
1598
+ hintNode.textContent = hint;
1599
+ hintNode.hidden = hint.length === 0;
678
1600
  }
679
- if (messageNode) messageNode.innerHTML = colorizeBlock(message);
680
1601
 
681
- if (locationWrap) locationWrap.hidden = location.length === 0;
682
- if (locationText) locationText.textContent = location;
1602
+ if (tabNameNodes) {
1603
+ for (var t = 0; t < tabNameNodes.length; t += 1) tabNameNodes[t].textContent = tabName;
1604
+ }
1605
+ if (fileTabsNode) fileTabsNode.innerHTML = renderFileTabs(related, state.fileTab);
1606
+ if (crumbsNode) crumbsNode.textContent = crumbs;
1607
+ if (statusLnNode) statusLnNode.textContent = loc && loc.line ? ('Ln ' + loc.line + ', Col ' + (loc.column || 1)) : (error.line ? ('Ln ' + error.line + ', Col ' + (error.column || 1)) : 'Ready');
1608
+ if (statusLangNode) statusLangNode.textContent = lang;
1609
+ if (sourceBadgeNode) {
1610
+ sourceBadgeNode.textContent = badge;
1611
+ sourceBadgeNode.className = 'vista-ide-source-badge' + (badgeTone ? ' is-' + badgeTone : '');
1612
+ }
683
1613
 
684
- if (codeWrap) codeWrap.hidden = codeFrame.trim().length === 0;
685
- if (codeNode) codeNode.innerHTML = colorizeBlock(codeFrame);
1614
+ if (codeNode) codeNode.innerHTML = renderLocation(loc || { file: file, codeFrame: error.codeFrame, line: error.line, column: error.column, kind: 'source' });
686
1615
 
687
1616
  if (stackWrap) stackWrap.hidden = stack.length === 0;
688
- if (stackNode) stackNode.innerHTML = colorizeBlock(stack);
1617
+ if (stackNode) stackNode.innerHTML = renderStackFrames(stack);
689
1618
 
1619
+ if (countNode) countNode.textContent = (state.index + 1) + '/' + state.errors.length;
690
1620
  if (paginationNode) paginationNode.hidden = state.errors.length <= 1;
691
- if (countNode) countNode.textContent = (state.index + 1) + ' / ' + state.errors.length;
692
1621
  if (prevButton) prevButton.disabled = state.index <= 0;
693
1622
  if (nextButton) nextButton.disabled = state.index >= state.errors.length - 1;
694
1623
  if (minimizedCount) minimizedCount.textContent = String(state.errors.length);
1624
+ if (mainNode) {
1625
+ if (terminal) {
1626
+ mainNode.setAttribute('data-terminal', 'true');
1627
+ mainNode.removeAttribute('data-panel-expanded');
1628
+ } else {
1629
+ mainNode.removeAttribute('data-terminal');
1630
+ }
1631
+ }
1632
+ if (panelExpandButton) {
1633
+ panelExpandButton.hidden = terminal;
1634
+ if (terminal) {
1635
+ panelExpandButton.setAttribute('aria-pressed', 'false');
1636
+ panelExpandButton.setAttribute('aria-label', 'Expand problems panel');
1637
+ }
1638
+ }
695
1639
  }
696
1640
 
697
1641
  function changeIndex(offset) {
698
1642
  var next = state.index + offset;
699
1643
  if (next < 0 || next > state.errors.length - 1) return;
700
1644
  state.index = next;
1645
+ state.fileTab = 0;
701
1646
  render();
702
1647
  }
703
1648
 
@@ -707,11 +1652,15 @@ function renderErrorHTML(errors) {
707
1652
  backdropNode = document.querySelector('[data-vista-error-backdrop]');
708
1653
  typeNode = document.querySelector('[data-vista-error-type]');
709
1654
  titleNode = document.querySelector('[data-vista-error-title]');
710
- metaNode = document.querySelector('[data-vista-error-meta]');
711
- messageNode = document.querySelector('[data-vista-error-message]');
712
- locationWrap = document.querySelector('[data-vista-location-wrap]');
713
- locationText = document.querySelector('[data-vista-location-text]');
714
- locationButton = document.querySelector('[data-vista-location-trigger]');
1655
+ detailNode = document.querySelector('[data-vista-error-detail]');
1656
+ hintNode = document.querySelector('[data-vista-error-hint]');
1657
+ problemIconNode = document.querySelector('.vista-error-problem-icon');
1658
+ tabNameNodes = document.querySelectorAll('[data-vista-tab-name]');
1659
+ crumbsNode = document.querySelector('[data-vista-crumbs]');
1660
+ statusLnNode = document.querySelector('[data-vista-status-ln]');
1661
+ statusLangNode = document.querySelector('[data-vista-status-lang]');
1662
+ sourceBadgeNode = document.querySelector('[data-vista-source-badge]');
1663
+ fileTabsNode = document.querySelector('[data-vista-file-tabs]');
715
1664
  codeWrap = document.querySelector('[data-vista-code-wrap]');
716
1665
  codeNode = document.querySelector('[data-vista-code]');
717
1666
  stackWrap = document.querySelector('[data-vista-stack-wrap]');
@@ -723,6 +1672,8 @@ function renderErrorHTML(errors) {
723
1672
  copyButton = document.querySelector('[data-vista-copy-btn]');
724
1673
  minimizedTrigger = document.querySelector('[data-vista-minimized-trigger]');
725
1674
  minimizedCount = document.querySelector('[data-vista-minimized-count]');
1675
+ mainNode = document.querySelector('[data-vista-ide-main]');
1676
+ panelExpandButton = document.querySelector('[data-vista-panel-expand]');
726
1677
 
727
1678
  if (prevButton) {
728
1679
  prevButton.addEventListener('click', function() { changeIndex(-1); });
@@ -736,7 +1687,7 @@ function renderErrorHTML(errors) {
736
1687
  copyButton.addEventListener('click', function() {
737
1688
  var error = currentError();
738
1689
  if (!error) return;
739
- var label = getLabel(error.type);
1690
+ var label = getLabel(error);
740
1691
  var message = typeof error.message === 'string' ? error.message : String(error.message || 'Unknown Error');
741
1692
  var text = label + '\\n\\n' + message;
742
1693
  var location = formatLocation(error);
@@ -747,11 +1698,48 @@ function renderErrorHTML(errors) {
747
1698
  });
748
1699
  }
749
1700
 
750
- if (locationButton) {
751
- locationButton.addEventListener('click', function() {
1701
+ if (fileTabsNode) {
1702
+ fileTabsNode.addEventListener('click', function(event) {
1703
+ var target = event.target && event.target.closest ? event.target.closest('[data-vista-file-tab]') : null;
1704
+ if (!target) return;
1705
+ var nextTab = Number(target.getAttribute('data-vista-file-tab') || 0);
752
1706
  var error = currentError();
753
- if (!error || !error.file) return;
754
- vistaOpenInEditor(error.file, Number(error.line || 1), Number(error.column || 1));
1707
+ var related = error ? relatedFor(error) : [];
1708
+ var loc = related[nextTab];
1709
+ if (nextTab === state.fileTab && loc && loc.kind === 'source' && loc.file && loc.file.indexOf('.html') === -1) {
1710
+ vistaOpenInEditor(loc.file, Number(loc.line || 1), Number(loc.column || 1));
1711
+ return;
1712
+ }
1713
+ state.fileTab = nextTab;
1714
+ render();
1715
+ });
1716
+ }
1717
+
1718
+ if (panelExpandButton && mainNode) {
1719
+ panelExpandButton.addEventListener('click', function() {
1720
+ if (mainNode.getAttribute('data-terminal') === 'true') return;
1721
+ var expanded = mainNode.getAttribute('data-panel-expanded') === 'true';
1722
+ if (expanded) {
1723
+ mainNode.removeAttribute('data-panel-expanded');
1724
+ panelExpandButton.setAttribute('aria-pressed', 'false');
1725
+ panelExpandButton.setAttribute('aria-label', 'Expand problems panel');
1726
+ } else {
1727
+ mainNode.setAttribute('data-panel-expanded', 'true');
1728
+ panelExpandButton.setAttribute('aria-pressed', 'true');
1729
+ panelExpandButton.setAttribute('aria-label', 'Restore problems panel');
1730
+ }
1731
+ });
1732
+ }
1733
+
1734
+ if (stackWrap) {
1735
+ stackWrap.addEventListener('click', function(event) {
1736
+ var target = event.target && event.target.closest ? event.target.closest('[data-vista-open-file]') : null;
1737
+ if (!target) return;
1738
+ vistaOpenInEditor(
1739
+ target.getAttribute('data-vista-open-file'),
1740
+ Number(target.getAttribute('data-vista-open-line') || 1),
1741
+ Number(target.getAttribute('data-vista-open-col') || 1)
1742
+ );
755
1743
  });
756
1744
  }
757
1745
 
@@ -779,6 +1767,14 @@ function renderErrorHTML(errors) {
779
1767
  } else if (event.key === 'ArrowRight') {
780
1768
  changeIndex(1);
781
1769
  } else if (event.key === 'Escape') {
1770
+ if (mainNode && mainNode.getAttribute('data-panel-expanded') === 'true' && mainNode.getAttribute('data-terminal') !== 'true') {
1771
+ mainNode.removeAttribute('data-panel-expanded');
1772
+ if (panelExpandButton) {
1773
+ panelExpandButton.setAttribute('aria-pressed', 'false');
1774
+ panelExpandButton.setAttribute('aria-label', 'Expand problems panel');
1775
+ }
1776
+ return;
1777
+ }
782
1778
  minimizeOverlay();
783
1779
  }
784
1780
  });
@@ -795,53 +1791,73 @@ function renderErrorHTML(errors) {
795
1791
  <div class="vista-error-page-root" data-vista-error-root>
796
1792
  <div class="vista-error-backdrop" data-vista-error-backdrop></div>
797
1793
  <section class="vista-error-panel" data-vista-error-panel role="dialog" aria-label="Vista Error Overlay" aria-modal="true">
798
- <header class="vista-error-header">
799
- <div class="vista-error-head-left">
800
- <div class="vista-error-top-row">
801
- <span class="vista-error-badge" data-vista-error-type>
802
- <span class="vista-error-badge-dot"></span>${escapeHtml(firstLabel)}
803
- </span>
804
- <div class="vista-error-pagination" data-vista-pagination${normalizedErrors.length <= 1 ? ' hidden' : ''}>
805
- <button type="button" class="vista-error-page-btn" data-vista-prev-btn aria-label="Previous error">&lt;</button>
806
- <span class="vista-error-page-count" data-vista-count>1 / ${normalizedErrors.length}</span>
807
- <button type="button" class="vista-error-page-btn" data-vista-next-btn aria-label="Next error">&gt;</button>
808
- </div>
809
- </div>
810
- <h2 class="vista-error-title" data-vista-error-title>${escapeHtml(firstTitle)}</h2>
811
- <p class="vista-error-meta" data-vista-error-meta>Vista Dev Overlay</p>
1794
+ <div class="vista-ide-titlebar">
1795
+ <div class="vista-ide-traffic" aria-hidden="true">
1796
+ <span class="is-close"></span><span class="is-min"></span><span class="is-max"></span>
812
1797
  </div>
813
- <div class="vista-error-controls">
814
- <button type="button" class="vista-error-btn" data-vista-copy-btn aria-label="Copy error">
815
- <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><rect x="9" y="9" width="10" height="10" rx="2" stroke="currentColor" stroke-width="2"/><rect x="5" y="5" width="10" height="10" rx="2" stroke="currentColor" stroke-width="2"/></svg>
1798
+ <div class="vista-ide-title">Vista — <span data-vista-tab-name>${escapeHtml(firstTitleName)}</span></div>
1799
+ <div class="vista-error-toolbar">
1800
+ <button type="button" class="vista-error-icon-btn" data-vista-copy-btn aria-label="Copy error">
1801
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><rect x="9" y="9" width="10" height="10" rx="2" stroke="currentColor" stroke-width="1.6"/><rect x="5" y="5" width="10" height="10" rx="2" stroke="currentColor" stroke-width="1.6"/></svg>
816
1802
  </button>
817
- <button type="button" class="vista-error-btn" onclick="vistaReload()" aria-label="Reload page">
818
- <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path d="M20 12a8 8 0 1 1-2.34-5.66" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><path d="M20 4V10H14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
1803
+ <button type="button" class="vista-error-icon-btn" onclick="vistaReload()" aria-label="Reload page">
1804
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path d="M20 12a8 8 0 1 1-2.34-5.66" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><path d="M20 4V10H14" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
819
1805
  </button>
1806
+ <div class="vista-error-pagination" data-vista-pagination${showPager ? '' : ' hidden'}>
1807
+ <button type="button" class="vista-error-page-btn" data-vista-prev-btn aria-label="Previous error">‹</button>
1808
+ <span class="vista-error-page-count" data-vista-count>1/${normalizedErrors.length}</span>
1809
+ <button type="button" class="vista-error-page-btn" data-vista-next-btn aria-label="Next error">›</button>
1810
+ </div>
1811
+ </div>
1812
+ </div>
1813
+ <div class="vista-ide-workbench">
1814
+ <aside class="vista-ide-activity" aria-hidden="true">
1815
+ <span class="vista-ide-activity-item is-active">
1816
+ <svg viewBox="0 0 24 24" fill="none"><rect x="4" y="3" width="10" height="13" rx="1.2" stroke="currentColor" stroke-width="1.6"/><rect x="10" y="8" width="10" height="13" rx="1.2" stroke="currentColor" stroke-width="1.6"/></svg>
1817
+ </span>
1818
+ </aside>
1819
+ <div class="vista-ide-main" data-vista-ide-main${firstTerminal ? ' data-terminal="true"' : ''}>
1820
+ <div class="vista-ide-tabbar">
1821
+ <div class="vista-ide-tabs" data-vista-file-tabs>
1822
+ ${firstTabsHtml}
1823
+ </div>
1824
+ </div>
1825
+ <div class="vista-ide-crumbs" data-vista-crumbs>${escapeHtml(firstCrumbs || firstLocation || 'Source')}</div>
1826
+ <div class="vista-error-code" data-vista-code-wrap>
1827
+ <div class="vista-error-editor-host" data-vista-code>${firstCodeHtml}</div>
1828
+ </div>
1829
+ <div class="vista-ide-statusbar">
1830
+ <span data-vista-status-ln>${escapeHtml(firstStatus)}</span>
1831
+ <span data-vista-status-lang>${escapeHtml(firstLang)}</span>
1832
+ <span class="vista-ide-source-badge${firstBadge ? ` is-${firstBadge.tone}` : ''}" data-vista-source-badge>${escapeHtml(firstBadge ? firstBadge.label : 'Client')}</span>
1833
+ <span>UTF-8</span>
1834
+ </div>
1835
+ <div class="vista-ide-panel">
1836
+ <div class="vista-ide-panel-tabs">
1837
+ <span class="is-active">PROBLEMS</span>
1838
+ <span>CALL STACK</span>
1839
+ <button type="button" class="vista-error-icon-btn vista-ide-panel-toggle" data-vista-panel-expand aria-label="Expand problems panel" aria-pressed="false"${firstTerminal ? ' hidden' : ''}>
1840
+ <svg class="vista-ide-icon-expand" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 6.5V3h3.5M13 6.5V3h-3.5M3 9.5V13h3.5M13 9.5V13h-3.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
1841
+ <svg class="vista-ide-icon-restore" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M6.5 3v3.5H3M9.5 3v3.5H13M6.5 13V9.5H3M9.5 13V9.5H13" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
1842
+ </button>
1843
+ </div>
1844
+ <div class="vista-error-body">
1845
+ <div class="vista-error-problem">
1846
+ <span class="vista-error-problem-icon${firstTone ? ` is-${firstTone}` : ''}" aria-hidden="true"></span>
1847
+ <div>
1848
+ <p class="vista-error-kind${firstTone ? ` is-${firstTone}` : ''}" data-vista-error-type>${escapeHtml(firstLabel)}</p>
1849
+ <h1 class="vista-error-headline" data-vista-error-title>${escapeHtml(firstParts.headline)}</h1>
1850
+ <p class="vista-error-detail" data-vista-error-detail${firstParts.detail ? '' : ' hidden'}>${escapeHtml(firstParts.detail)}</p>
1851
+ <p class="vista-error-hint" data-vista-error-hint${firstHint ? '' : ' hidden'}>${escapeHtml(firstHint)}</p>
1852
+ </div>
1853
+ </div>
1854
+ <div class="vista-error-stack" data-vista-stack-wrap${firstStack ? '' : ' hidden'}>
1855
+ <h2 class="vista-error-stack-title">CALL STACK</h2>
1856
+ <div class="vista-error-frames-host" data-vista-stack>${firstStackHtml}</div>
1857
+ </div>
1858
+ </div>
1859
+ </div>
820
1860
  </div>
821
- </header>
822
-
823
- <div class="vista-error-body">
824
- <section class="vista-error-card" data-vista-location-wrap${firstLocation ? '' : ' hidden'}>
825
- <h3 class="vista-error-card-title">Location</h3>
826
- <button type="button" class="vista-error-location" data-vista-location-trigger>
827
- <span data-vista-location-text>${escapeHtml(firstLocation)}</span>
828
- </button>
829
- </section>
830
-
831
- <section class="vista-error-card">
832
- <h3 class="vista-error-card-title">Message</h3>
833
- <pre class="vista-error-pre" data-vista-error-message>${firstMessageHtml}</pre>
834
- </section>
835
-
836
- <section class="vista-error-card" data-vista-code-wrap${firstCode ? '' : ' hidden'}>
837
- <h3 class="vista-error-card-title">Code Frame</h3>
838
- <pre class="vista-error-pre" data-vista-code>${firstCodeHtml}</pre>
839
- </section>
840
-
841
- <section class="vista-error-card" data-vista-stack-wrap${firstStack ? '' : ' hidden'}>
842
- <h3 class="vista-error-card-title">Stack Trace</h3>
843
- <pre class="vista-error-pre" data-vista-stack>${firstStackHtml}</pre>
844
- </section>
845
1861
  </div>
846
1862
  </section>
847
1863
  <button type="button" class="vista-error-minimized-trigger" data-vista-minimized-trigger hidden aria-label="Reopen error overlay">
@@ -860,33 +1876,59 @@ function ErrorOverlay({ errors }) {
860
1876
  const normalizedErrors = react_1.default.useMemo(() => normalizeErrors(errors), [errors]);
861
1877
  const [activeIndex, setActiveIndex] = react_1.default.useState(0);
862
1878
  const [isMinimized, setIsMinimized] = react_1.default.useState(false);
1879
+ const [panelExpanded, setPanelExpanded] = react_1.default.useState(false);
1880
+ const [fileTab, setFileTab] = react_1.default.useState(0);
863
1881
  react_1.default.useEffect(() => {
864
1882
  if (activeIndex > normalizedErrors.length - 1) {
865
1883
  setActiveIndex(0);
866
1884
  }
1885
+ setFileTab(0);
867
1886
  }, [activeIndex, normalizedErrors.length]);
868
1887
  const currentIndex = Math.max(0, Math.min(activeIndex, normalizedErrors.length - 1));
869
1888
  const error = normalizedErrors[currentIndex];
870
1889
  if (!error) {
871
1890
  return (0, jsx_runtime_1.jsx)("div", {});
872
1891
  }
873
- const label = getErrorTypeLabel(error.type);
874
- const title = getErrorTitle(error.message || 'Unknown Error', label);
875
- const location = formatLocation(error);
1892
+ const related = error.related && error.related.length > 0
1893
+ ? error.related
1894
+ : [
1895
+ {
1896
+ file: error.file || 'untitled',
1897
+ line: error.line,
1898
+ column: error.column,
1899
+ codeFrame: error.codeFrame,
1900
+ kind: 'source',
1901
+ },
1902
+ ];
1903
+ const activeLocation = related[Math.max(0, Math.min(fileTab, related.length - 1))] || related[0];
1904
+ const isTerminal = !hasCodeView(error);
1905
+ const label = getErrorTypeLabel(error);
1906
+ const tone = problemTone(error);
1907
+ const badge = sourceBadge(error);
1908
+ const parts = splitMessage(error.message || 'Unknown Error');
1909
+ const location = formatCodeHeader(error) || formatLocation(error);
876
1910
  const stackText = parseStackTrace(error.stack || '').join('\n');
877
- const codeFrame = error.codeFrame || '';
878
- const messageHtml = colorizeBlockHtml(error.message || 'Unknown Error');
879
- const codeHtml = colorizeBlockHtml(codeFrame);
880
- const stackHtml = colorizeBlockHtml(stackText);
1911
+ const codeHtml = renderLocationEditor(activeLocation);
1912
+ const stackHtml = renderStackFramesHtml(stackText);
1913
+ const tabName = tabLabel(activeLocation);
1914
+ const crumbs = fileBreadcrumb(activeLocation.file) || location || 'Source';
1915
+ const status = activeLocation.line
1916
+ ? `Ln ${activeLocation.line}, Col ${activeLocation.column || 1}`
1917
+ : statusLine(error);
1918
+ const language = activeLocation.kind === 'server' || activeLocation.kind === 'client'
1919
+ ? 'HTML'
1920
+ : activeLocation.kind === 'log'
1921
+ ? 'Log'
1922
+ : languageFromFile(activeLocation.file);
881
1923
  const minimizeOverlay = react_1.default.useCallback(() => {
882
1924
  setIsMinimized(true);
883
1925
  if (typeof window !== 'undefined') {
884
1926
  const indicator = window.__VISTA_DEVTOOLS_INDICATOR__;
885
1927
  if (indicator && typeof indicator.setError === 'function') {
886
- indicator.setError(title || 'Error', normalizedErrors.length);
1928
+ indicator.setError(parts.headline || 'Error', normalizedErrors.length);
887
1929
  }
888
1930
  }
889
- }, [normalizedErrors.length, title]);
1931
+ }, [normalizedErrors.length, parts.headline]);
890
1932
  const restoreOverlay = react_1.default.useCallback(() => {
891
1933
  setIsMinimized(false);
892
1934
  if (typeof window !== 'undefined') {
@@ -899,7 +1941,12 @@ function ErrorOverlay({ errors }) {
899
1941
  react_1.default.useEffect(() => {
900
1942
  const onKeyDown = (event) => {
901
1943
  if (event.key === 'Escape') {
902
- minimizeOverlay();
1944
+ if (panelExpanded && !isTerminal) {
1945
+ setPanelExpanded(false);
1946
+ }
1947
+ else {
1948
+ minimizeOverlay();
1949
+ }
903
1950
  }
904
1951
  else if (event.key === 'ArrowLeft') {
905
1952
  setActiveIndex((idx) => Math.max(0, idx - 1));
@@ -910,12 +1957,23 @@ function ErrorOverlay({ errors }) {
910
1957
  };
911
1958
  window.addEventListener('keydown', onKeyDown);
912
1959
  return () => window.removeEventListener('keydown', onKeyDown);
913
- }, [minimizeOverlay, normalizedErrors.length]);
914
- return ((0, jsx_runtime_1.jsxs)("div", { className: "vista-error-page-root", "data-embedded": "true", "data-minimized": isMinimized ? 'true' : undefined, children: [(0, jsx_runtime_1.jsx)("style", { children: OVERLAY_STYLES }), (0, jsx_runtime_1.jsx)("div", { className: "vista-error-backdrop", onClick: minimizeOverlay }), (0, jsx_runtime_1.jsxs)("section", { className: "vista-error-panel", role: "dialog", "aria-label": "Vista Error Overlay", "aria-modal": true, onClick: (event) => event.stopPropagation(), children: [(0, jsx_runtime_1.jsxs)("header", { className: "vista-error-header", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vista-error-head-left", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vista-error-top-row", children: [(0, jsx_runtime_1.jsxs)("span", { className: "vista-error-badge", children: [(0, jsx_runtime_1.jsx)("span", { className: "vista-error-badge-dot" }), label] }), normalizedErrors.length > 1 ? ((0, jsx_runtime_1.jsxs)("div", { className: "vista-error-pagination", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vista-error-page-btn", onClick: () => setActiveIndex((idx) => Math.max(0, idx - 1)), disabled: currentIndex === 0, "aria-label": "Previous error", children: '<' }), (0, jsx_runtime_1.jsxs)("span", { className: "vista-error-page-count", children: [currentIndex + 1, " / ", normalizedErrors.length] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vista-error-page-btn", onClick: () => setActiveIndex((idx) => Math.min(normalizedErrors.length - 1, idx + 1)), disabled: currentIndex >= normalizedErrors.length - 1, "aria-label": "Next error", children: '>' })] })) : null] }), (0, jsx_runtime_1.jsx)("h2", { className: "vista-error-title", children: title }), (0, jsx_runtime_1.jsx)("p", { className: "vista-error-meta", children: "Vista Dev Overlay" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vista-error-controls", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vista-error-btn", onClick: () => copyTextInBrowser(serializeErrorForCopy(error)), "aria-label": "Copy error", children: (0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", children: [(0, jsx_runtime_1.jsx)("rect", { x: "9", y: "9", width: "10", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "2" }), (0, jsx_runtime_1.jsx)("rect", { x: "5", y: "5", width: "10", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "2" })] }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vista-error-btn", onClick: () => {
1960
+ }, [minimizeOverlay, normalizedErrors.length, panelExpanded, isTerminal]);
1961
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "vista-error-page-root", "data-embedded": "true", "data-minimized": isMinimized ? 'true' : undefined, children: [(0, jsx_runtime_1.jsx)("style", { children: OVERLAY_STYLES }), (0, jsx_runtime_1.jsx)("div", { className: "vista-error-backdrop", onClick: minimizeOverlay }), (0, jsx_runtime_1.jsxs)("section", { className: "vista-error-panel", role: "dialog", "aria-label": "Vista Error Overlay", "aria-modal": true, onClick: (event) => event.stopPropagation(), children: [(0, jsx_runtime_1.jsxs)("div", { className: "vista-ide-titlebar", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vista-ide-traffic", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("span", { className: "is-close" }), (0, jsx_runtime_1.jsx)("span", { className: "is-min" }), (0, jsx_runtime_1.jsx)("span", { className: "is-max" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vista-ide-title", children: ["Vista \u2014 ", (0, jsx_runtime_1.jsx)("span", { children: isTerminal ? label : tabName })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vista-error-toolbar", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vista-error-icon-btn", onClick: () => copyTextInBrowser(serializeErrorForCopy(error)), "aria-label": "Copy error", children: (0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", children: [(0, jsx_runtime_1.jsx)("rect", { x: "9", y: "9", width: "10", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "1.6" }), (0, jsx_runtime_1.jsx)("rect", { x: "5", y: "5", width: "10", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "1.6" })] }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vista-error-icon-btn", onClick: () => {
915
1962
  if (typeof window !== 'undefined') {
916
1963
  window.location.reload();
917
1964
  }
918
- }, "aria-label": "Reload page", children: (0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", children: [(0, jsx_runtime_1.jsx)("path", { d: "M20 12a8 8 0 1 1-2.34-5.66", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }), (0, jsx_runtime_1.jsx)("path", { d: "M20 4V10H14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })] }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vista-error-body", children: [location ? ((0, jsx_runtime_1.jsxs)("section", { className: "vista-error-card", children: [(0, jsx_runtime_1.jsx)("h3", { className: "vista-error-card-title", children: "Location" }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vista-error-location", onClick: () => openInEditorFromBrowser(error.file, error.line, error.column), children: location })] })) : null, (0, jsx_runtime_1.jsxs)("section", { className: "vista-error-card", children: [(0, jsx_runtime_1.jsx)("h3", { className: "vista-error-card-title", children: "Message" }), (0, jsx_runtime_1.jsx)("pre", { className: "vista-error-pre", dangerouslySetInnerHTML: { __html: messageHtml } })] }), codeFrame ? ((0, jsx_runtime_1.jsxs)("section", { className: "vista-error-card", children: [(0, jsx_runtime_1.jsx)("h3", { className: "vista-error-card-title", children: "Code Frame" }), (0, jsx_runtime_1.jsx)("pre", { className: "vista-error-pre", dangerouslySetInnerHTML: { __html: codeHtml } })] })) : null, stackText ? ((0, jsx_runtime_1.jsxs)("section", { className: "vista-error-card", children: [(0, jsx_runtime_1.jsx)("h3", { className: "vista-error-card-title", children: "Stack Trace" }), (0, jsx_runtime_1.jsx)("pre", { className: "vista-error-pre", dangerouslySetInnerHTML: { __html: stackHtml } })] })) : null] })] }), isMinimized ? ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "vista-error-minimized-trigger", onClick: restoreOverlay, "aria-label": "Reopen error overlay", children: [(0, jsx_runtime_1.jsx)("svg", { className: "vista-error-minimized-logo", viewBox: "0 0 168 177", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: (0, jsx_runtime_1.jsx)("path", { d: "M81.872 176.988L-2.01405e-06 -2.68173e-06H30.5816L83.5576 121.604L136.774 -2.68173e-06H167.115L85.484 176.988H81.872Z", fill: "white" }) }), (0, jsx_runtime_1.jsx)("span", { className: "vista-error-minimized-close", children: "\u00D7" }), (0, jsx_runtime_1.jsx)("span", { className: "vista-error-minimized-count", children: normalizedErrors.length })] })) : null] }));
1965
+ }, "aria-label": "Reload page", children: (0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", children: [(0, jsx_runtime_1.jsx)("path", { d: "M20 12a8 8 0 1 1-2.34-5.66", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }), (0, jsx_runtime_1.jsx)("path", { d: "M20 4V10H14", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" })] }) }), normalizedErrors.length > 1 ? ((0, jsx_runtime_1.jsxs)("div", { className: "vista-error-pagination", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "vista-error-page-btn", onClick: () => setActiveIndex((idx) => Math.max(0, idx - 1)), disabled: currentIndex === 0, "aria-label": "Previous error", children: "\u2039" }), (0, jsx_runtime_1.jsxs)("span", { className: "vista-error-page-count", children: [currentIndex + 1, "/", normalizedErrors.length] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vista-error-page-btn", onClick: () => setActiveIndex((idx) => Math.min(normalizedErrors.length - 1, idx + 1)), disabled: currentIndex >= normalizedErrors.length - 1, "aria-label": "Next error", children: "\u203A" })] })) : null] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vista-ide-workbench", children: [(0, jsx_runtime_1.jsx)("aside", { className: "vista-ide-activity", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("span", { className: "vista-ide-activity-item is-active", children: (0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", children: [(0, jsx_runtime_1.jsx)("rect", { x: "4", y: "3", width: "10", height: "13", rx: "1.2", stroke: "currentColor", strokeWidth: "1.6" }), (0, jsx_runtime_1.jsx)("rect", { x: "10", y: "8", width: "10", height: "13", rx: "1.2", stroke: "currentColor", strokeWidth: "1.6" })] }) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "vista-ide-main", "data-panel-expanded": !isTerminal && panelExpanded ? 'true' : undefined, "data-terminal": isTerminal ? 'true' : undefined, children: [(0, jsx_runtime_1.jsx)("div", { className: "vista-ide-tabbar", children: (0, jsx_runtime_1.jsx)("div", { className: "vista-ide-tabs", children: related.map((item, index) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: `vista-ide-tab${index === fileTab ? '' : ' is-inactive'}`, onClick: () => {
1966
+ if (index === fileTab && item.kind === 'source' && item.file && !item.file.endsWith('.html')) {
1967
+ openInEditorFromBrowser(item.file, item.line, item.column);
1968
+ return;
1969
+ }
1970
+ setFileTab(index);
1971
+ }, children: [(0, jsx_runtime_1.jsxs)("svg", { className: "vista-ide-tab-icon", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("rect", { x: "3", y: "2", width: "10", height: "12", rx: "1.5", stroke: "#4fc1ff", strokeWidth: "1.4" }), (0, jsx_runtime_1.jsx)("path", { d: "M6 6h4M6 9h4M6 12h2", stroke: "#4fc1ff", strokeWidth: "1.2", strokeLinecap: "round" })] }), (0, jsx_runtime_1.jsx)("span", { children: tabLabel(item) })] }, `${item.kind || 'source'}:${item.file}:${index}`))) }) }), (0, jsx_runtime_1.jsx)("div", { className: "vista-ide-crumbs", children: crumbs }), (0, jsx_runtime_1.jsx)("div", { className: "vista-error-code", children: (0, jsx_runtime_1.jsx)("div", { className: "vista-error-editor-host", dangerouslySetInnerHTML: { __html: codeHtml } }) }), (0, jsx_runtime_1.jsxs)("div", { className: "vista-ide-statusbar", children: [(0, jsx_runtime_1.jsx)("span", { children: status }), (0, jsx_runtime_1.jsx)("span", { children: language }), badge ? ((0, jsx_runtime_1.jsx)("span", { className: `vista-ide-source-badge${badge.tone ? ` is-${badge.tone}` : ''}`, children: badge.label })) : null, (0, jsx_runtime_1.jsx)("span", { children: "UTF-8" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vista-ide-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vista-ide-panel-tabs", children: [(0, jsx_runtime_1.jsx)("span", { className: "is-active", children: "PROBLEMS" }), (0, jsx_runtime_1.jsx)("span", { children: "CALL STACK" }), !isTerminal ? ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "vista-error-icon-btn vista-ide-panel-toggle", onClick: () => setPanelExpanded((open) => !open), "aria-label": panelExpanded ? 'Restore problems panel' : 'Expand problems panel', "aria-pressed": panelExpanded, children: [(0, jsx_runtime_1.jsx)("svg", { className: "vista-ide-icon-expand", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("path", { d: "M3 6.5V3h3.5M13 6.5V3h-3.5M3 9.5V13h3.5M13 9.5V13h-3.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }) }), (0, jsx_runtime_1.jsx)("svg", { className: "vista-ide-icon-restore", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("path", { d: "M6.5 3v3.5H3M9.5 3v3.5H13M6.5 13V9.5H3M9.5 13V9.5H13", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }) })] })) : null] }), (0, jsx_runtime_1.jsxs)("div", { className: "vista-error-body", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vista-error-problem", children: [(0, jsx_runtime_1.jsx)("span", { className: `vista-error-problem-icon${tone ? ` is-${tone}` : ''}`, "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: `vista-error-kind${tone ? ` is-${tone}` : ''}`, children: label }), (0, jsx_runtime_1.jsx)("h1", { className: "vista-error-headline", children: parts.headline }), parts.detail ? (0, jsx_runtime_1.jsx)("p", { className: "vista-error-detail", children: parts.detail }) : null, error.hint ? (0, jsx_runtime_1.jsx)("p", { className: "vista-error-hint", children: error.hint }) : null] })] }), stackText ? ((0, jsx_runtime_1.jsxs)("div", { className: "vista-error-stack", children: [(0, jsx_runtime_1.jsx)("h2", { className: "vista-error-stack-title", children: "CALL STACK" }), (0, jsx_runtime_1.jsx)("div", { className: "vista-error-frames-host", dangerouslySetInnerHTML: { __html: stackHtml }, onClick: (event) => {
1972
+ const target = event.target.closest('[data-vista-open-file]');
1973
+ if (!target)
1974
+ return;
1975
+ openInEditorFromBrowser(target.getAttribute('data-vista-open-file') || undefined, Number(target.getAttribute('data-vista-open-line') || 1), Number(target.getAttribute('data-vista-open-col') || 1));
1976
+ } })] })) : null] })] })] })] })] }), isMinimized ? ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "vista-error-minimized-trigger", onClick: restoreOverlay, "aria-label": "Reopen error overlay", children: [(0, jsx_runtime_1.jsx)("svg", { className: "vista-error-minimized-logo", viewBox: "0 0 168 177", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: (0, jsx_runtime_1.jsx)("path", { d: "M81.872 176.988L-2.01405e-06 -2.68173e-06H30.5816L83.5576 121.604L136.774 -2.68173e-06H167.115L85.484 176.988H81.872Z", fill: "white" }) }), (0, jsx_runtime_1.jsx)("span", { className: "vista-error-minimized-close", children: "\u00D7" }), (0, jsx_runtime_1.jsx)("span", { className: "vista-error-minimized-count", children: normalizedErrors.length })] })) : null] }));
919
1977
  }
920
1978
  // ============================================================================
921
1979
  // Error Boundary (client-side)