@teamvelix/velix 5.2.9 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { definePlugin, PluginHooks, logger_default, loadPlugins, pluginManager, buildRouteTree, matchRoute, VERSION } from './chunk-RU23OKU5.js';
1
+ import { definePlugin, PluginHooks, logger_default, loadPlugins, pluginManager, buildRouteTree, matchRoute, VERSION } from './chunk-IH5DV2WC.js';
2
2
  import { loadConfig, resolvePaths } from './chunk-F24Q2MX3.js';
3
3
  import { getRegisteredIslands, generateAdvancedHydrationScript } from './chunk-JI2HJFUT.js';
4
4
  import { __dirname as __dirname$1, __require } from './chunk-BQ4HFLRL.js';
@@ -861,73 +861,76 @@ function generateDevToolsHtml(isDev, ctx = {}) {
861
861
  return `
862
862
  <style>
863
863
  /* \u2500\u2500 Velix DevTools v5.1 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
864
- #__vdt-btn{position:fixed;bottom:16px;left:16px;z-index:9999;width:40px;height:40px;border-radius:50%;background:#111827;border:2px solid #22D3EE;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,.5);transition:transform .2s,box-shadow .2s;outline:none;}
865
- #__vdt-btn:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(34,211,238,.25);}
866
- #__vdt-dot{position:absolute;top:-3px;right:-3px;width:11px;height:11px;border-radius:50%;background:#22D3EE;border:2px solid #111827;transition:background .3s;}
867
- #__vdt{display:none;position:fixed;bottom:68px;left:16px;width:340px;background:#111827;color:#f3f4f6;border-radius:14px;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:13px;box-shadow:0 24px 60px rgba(0,0,0,.6);z-index:10000;border:1px solid #1f2937;overflow:hidden;}
864
+ #__vdt-btn{position:fixed;bottom:16px;left:16px;z-index:9999;min-width:40px;height:40px;border-radius:20px;background:#0B1120;border:1px solid rgba(255,255,255,0.1);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,.5);transition:all .3s cubic-bezier(0.4, 0, 0.2, 1);outline:none;padding:0 12px;gap:8px;color:#fff;overflow:hidden;}
865
+ #__vdt-btn:hover{transform:scale(1.05);background:#0F172A;border-color:rgba(255,255,255,0.2);}
866
+ #__vdt-status-text{font-size:12px;font-weight:600;white-space:nowrap;opacity:0;max-width:0;transition:all .3s cubic-bezier(0.4, 0, 0.2, 1);font-family:ui-sans-serif,system-ui,sans-serif;letter-spacing:0.3px;}
867
+ #__vdt-btn.vdt-compiling #__vdt-status-text, #__vdt-btn.vdt-navigating #__vdt-status-text{opacity:1;max-width:120px;}
868
+
869
+ #__vdt-dot{display:none;}
870
+ #__vdt{display:none;position:fixed;bottom:68px;left:16px;width:340px;background:#0B1120;color:#f3f4f6;border-radius:14px;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:13px;box-shadow:0 24px 60px rgba(0,0,0,.6);z-index:10000;border:1px solid #0F172A;overflow:hidden;}
868
871
  /* header */
869
- .__vdt-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #1f2937;}
872
+ .__vdt-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #0F172A;}
870
873
  .__vdt-brand{display:flex;align-items:center;gap:8px;font-weight:700;font-size:14px;color:#f9fafb;}
871
- .__vdt-badge{font-size:11px;font-weight:600;background:#1f2937;color:#9ca3af;border-radius:5px;padding:2px 7px;letter-spacing:.3px;}
874
+ .__vdt-badge{font-size:11px;font-weight:600;background:#0F172A;color:#9ca3af;border-radius:5px;padding:2px 7px;letter-spacing:.3px;}
872
875
  .__vdt-close{background:none;border:none;color:#6b7280;cursor:pointer;font-size:20px;line-height:1;padding:0;transition:color .15s;}
873
876
  .__vdt-close:hover{color:#f9fafb;}
874
877
  /* tabs */
875
- .__vdt-tabs{display:flex;border-bottom:1px solid #1f2937;}
878
+ .__vdt-tabs{display:flex;border-bottom:1px solid #0F172A;}
876
879
  .__vdt-tab{flex:1;padding:10px 0;background:none;border:none;color:#6b7280;font-size:12px;font-weight:500;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:5px;border-bottom:2px solid transparent;transition:color .15s,border-color .15s;font-family:inherit;letter-spacing:.1px;}
877
880
  .__vdt-tab:hover{color:#d1d5db;}
878
- .__vdt-tab.active{color:#10b981;border-bottom-color:#10b981;}
881
+ .__vdt-tab.active{color:#2563EB;border-bottom-color:#2563EB;}
879
882
  /* body */
880
883
  .__vdt-body{padding:14px 16px;min-height:200px;}
881
884
  .__vdt-section{margin-bottom:14px;}
882
885
  .__vdt-label{font-size:11px;color:#6b7280;text-transform:uppercase;letter-spacing:.6px;margin-bottom:8px;}
883
- .__vdt-box{background:#1f2937;border-radius:8px;padding:10px 13px;font-size:13px;color:#e5e7eb;font-family:ui-monospace,'Cascadia Code','Fira Code',monospace;border:1px solid #374151;}
884
- .__vdt-row{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;background:#1f2937;border-radius:8px;margin-bottom:6px;border:1px solid #374151;}
886
+ .__vdt-box{background:#0F172A;border-radius:8px;padding:10px 13px;font-size:13px;color:#e5e7eb;font-family:ui-monospace,'Cascadia Code','Fira Code',monospace;border:1px solid #162032;}
887
+ .__vdt-row{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;background:#0F172A;border-radius:8px;margin-bottom:6px;border:1px solid #162032;}
885
888
  .__vdt-row-left{display:flex;align-items:center;gap:8px;color:#d1d5db;}
886
- .__vdt-row-right{font-family:ui-monospace,'Cascadia Code',monospace;color:#10b981;font-size:12px;font-weight:500;}
889
+ .__vdt-row-right{font-family:ui-monospace,'Cascadia Code',monospace;color:#2563EB;font-size:12px;font-weight:500;}
887
890
  .__vdt-pill{font-size:10px;font-weight:600;padding:2px 7px;border-radius:4px;letter-spacing:.2px;}
888
- .__vdt-pill-green{background:#052e16;color:#4ade80;border:1px solid #166534;}
891
+ .__vdt-pill-green{background:#162032;color:#38BDF8;border:1px solid #2563EB;}
889
892
  .__vdt-pill-blue{background:#0c1a3a;color:#60a5fa;border:1px solid #1e3a8a;}
890
- .__vdt-render{display:flex;align-items:center;gap:6px;padding:8px 13px;background:#1f2937;border-radius:8px;border:1px solid #374151;color:#9ca3af;font-size:12px;}
893
+ .__vdt-render{display:flex;align-items:center;gap:6px;padding:8px 13px;background:#0F172A;border-radius:8px;border:1px solid #162032;color:#9ca3af;font-size:12px;}
891
894
  .__vdt-render svg{color:#6b7280;}
892
895
  /* vitals */
893
- .__vdt-vital{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;background:#1f2937;border-radius:8px;margin-bottom:6px;border:1px solid #374151;}
896
+ .__vdt-vital{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;background:#0F172A;border-radius:8px;margin-bottom:6px;border:1px solid #162032;}
894
897
  .__vdt-vital-left{display:flex;align-items:center;gap:8px;}
895
- .__vdt-dot-good{width:8px;height:8px;border-radius:50%;background:#10b981;flex-shrink:0;}
898
+ .__vdt-dot-good{width:8px;height:8px;border-radius:50%;background:#2563EB;flex-shrink:0;}
896
899
  .__vdt-dot-warn{width:8px;height:8px;border-radius:50%;background:#f59e0b;flex-shrink:0;}
897
900
  .__vdt-dot-bad{width:8px;height:8px;border-radius:50%;background:#ef4444;flex-shrink:0;}
898
901
  .__vdt-vital-name{color:#d1d5db;font-weight:500;}
899
- .__vdt-vital-val{font-family:ui-monospace,'Cascadia Code',monospace;color:#10b981;font-size:12px;font-weight:600;}
900
- .__vdt-build{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;color:#9ca3af;font-size:12px;border-top:1px solid #374151;margin-top:8px;padding-top:12px;}
902
+ .__vdt-vital-val{font-family:ui-monospace,'Cascadia Code',monospace;color:#2563EB;font-size:12px;font-weight:600;}
903
+ .__vdt-build{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;color:#9ca3af;font-size:12px;border-top:1px solid #162032;margin-top:8px;padding-top:12px;}
901
904
  /* info rows */
902
- .__vdt-info-row{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;background:#1f2937;border-radius:8px;margin-bottom:6px;border:1px solid #374151;}
905
+ .__vdt-info-row{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;background:#0F172A;border-radius:8px;margin-bottom:6px;border:1px solid #162032;}
903
906
  .__vdt-info-left{display:flex;align-items:center;gap:8px;color:#d1d5db;}
904
907
  .__vdt-info-val{font-family:ui-monospace,'Cascadia Code',monospace;color:#9ca3af;font-size:12px;}
905
- .__vdt-dev-badge{display:flex;align-items:center;gap:8px;padding:10px 13px;background:#052e16;border:1px solid #166534;border-radius:8px;color:#4ade80;font-size:12px;font-weight:500;margin-top:10px;}
908
+ .__vdt-dev-badge{display:flex;align-items:center;gap:8px;padding:10px 13px;background:#162032;border:1px solid #2563EB;border-radius:8px;color:#38BDF8;font-size:12px;font-weight:500;margin-top:10px;}
906
909
  /* footer */
907
- .__vdt-footer{padding:10px 16px;border-top:1px solid #1f2937;display:flex;align-items:center;justify-content:space-between;}
910
+ .__vdt-footer{padding:10px 16px;border-top:1px solid #0F172A;display:flex;align-items:center;justify-content:space-between;}
908
911
  .__vdt-host{color:#6b7280;font-size:11px;font-family:ui-monospace,'Cascadia Code',monospace;}
909
- .__vdt-conn{display:flex;align-items:center;gap:5px;font-size:11px;color:#10b981;}
910
- .__vdt-conn-dot{width:6px;height:6px;border-radius:50%;background:#10b981;}
912
+ .__vdt-conn{display:flex;align-items:center;gap:5px;font-size:11px;color:#2563EB;}
913
+ .__vdt-conn-dot{width:6px;height:6px;border-radius:50%;background:#2563EB;}
911
914
 
912
915
  /* \u2500\u2500 Widget state colors \u2500\u2500 */
913
916
  .vdt-idle{border-color:#22D3EE !important;}
914
- .vdt-compiling{border-color:#4ade80 !important;}
917
+ .vdt-compiling{border-color:#38BDF8 !important;}
915
918
  .vdt-rendering{border-color:#fb923c !important;}
916
919
  .vdt-navigating{border-color:#60a5fa !important;}
917
920
  .vdt-error{border-color:#f87171 !important;}
918
921
  #__vdt-dot.idle{background:#22D3EE;}
919
- #__vdt-dot.compiling{background:#4ade80;}
922
+ #__vdt-dot.compiling{background:#38BDF8;}
920
923
  #__vdt-dot.rendering{background:#fb923c;}
921
924
  #__vdt-dot.navigating{background:#60a5fa;}
922
925
  #__vdt-dot.error{background:#f87171;}
923
926
  @keyframes __vdt-pulse{0%,100%{opacity:1;}50%{opacity:.4;}}
924
- .vdt-compiling #__vdt-dot,.vdt-rendering #__vdt-dot,.vdt-navigating #__vdt-dot{animation:__vdt-pulse .9s ease-in-out infinite;}
927
+ .vdt-compiling svg, .vdt-navigating svg{animation:__vdt-pulse .8s alternate infinite;}
925
928
  </style>
926
929
 
927
930
  <!-- \u2500\u2500 Floating button \u2500\u2500 -->
928
931
  <button id="__vdt-btn" class="vdt-idle" onclick="__vdtOpen()" title="Velix DevTools (${version})">
929
932
  ${ICON_LOGO}
930
- <div id="__vdt-dot" class="idle"></div>
933
+ <span id="__vdt-status-text"></span>
931
934
  </button>
932
935
 
933
936
  <!-- \u2500\u2500 Panel \u2500\u2500 -->
@@ -1078,7 +1081,7 @@ function generateDevToolsHtml(isDev, ctx = {}) {
1078
1081
  dot.className = v <= threshGood ? '__vdt-dot-good' : v <= threshOk ? '__vdt-dot-warn' : '__vdt-dot-bad';
1079
1082
  }
1080
1083
  if(el && typeof v === 'number'){
1081
- el.style.color = v <= threshGood ? '#10b981' : v <= threshOk ? '#f59e0b' : '#ef4444';
1084
+ el.style.color = v <= threshGood ? '#2563EB' : v <= threshOk ? '#f59e0b' : '#ef4444';
1082
1085
  }
1083
1086
  }
1084
1087
 
@@ -1128,13 +1131,18 @@ function generateDevToolsHtml(isDev, ctx = {}) {
1128
1131
 
1129
1132
  function setStatus(s){
1130
1133
  if(btn){ btn.className = 'vdt-' + s; }
1131
- if(dot){ dot.className = s; }
1134
+ var text = document.getElementById('__vdt-status-text');
1135
+ if(text) {
1136
+ if(s === 'compiling') text.textContent = 'Compiling...';
1137
+ else if(s === 'navigating') text.textContent = 'Loading...';
1138
+ else text.textContent = '';
1139
+ }
1132
1140
  }
1133
1141
 
1134
1142
  function setConn(ok){
1135
- if(connDot) connDot.style.background = ok ? '#10b981' : '#ef4444';
1143
+ if(connDot) connDot.style.background = ok ? '#2563EB' : '#ef4444';
1136
1144
  if(connText) connText.textContent = ok ? 'connected' : 'disconnected';
1137
- if(connText) connText.style.color = ok ? '#10b981' : '#ef4444';
1145
+ if(connText) connText.style.color = ok ? '#2563EB' : '#ef4444';
1138
1146
  }
1139
1147
 
1140
1148
  var ws = new WebSocket('ws://' + window.location.host + '/__velix_hmr');
@@ -1188,88 +1196,203 @@ function generate404Page(pathname = "/") {
1188
1196
  <head>
1189
1197
  <meta charset="UTF-8">
1190
1198
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1191
- <title>404 - Page Not Found | Velix v${VERSION}</title>
1199
+ <title>404 \u2014 Page Not Found | Velix v${VERSION}</title>
1192
1200
  <link rel="icon" href="/favicon.webp">
1201
+ <link rel="preconnect" href="https://fonts.googleapis.com">
1202
+ <link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
1193
1203
  <style>
1194
1204
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1205
+
1206
+ @keyframes fadeUp {
1207
+ from { opacity: 0; transform: translateY(16px); }
1208
+ to { opacity: 1; transform: translateY(0); }
1209
+ }
1210
+ @keyframes pulse {
1211
+ 0%, 100% { opacity: 1; }
1212
+ 50% { opacity: 0.4; }
1213
+ }
1214
+
1195
1215
  body {
1196
- background: #0a0a0a;
1197
- color: #ededed;
1198
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1216
+ background-color: #0B1120;
1217
+ background-image:
1218
+ linear-gradient(to right, rgba(255, 255, 255, 0.05) 1px, transparent 1px),
1219
+ linear-gradient(to bottom, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
1220
+ background-size: 60px 60px;
1221
+ color: #F8FAFC;
1222
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1199
1223
  display: flex;
1224
+ flex-direction: column;
1200
1225
  align-items: center;
1201
1226
  justify-content: center;
1202
1227
  min-height: 100vh;
1228
+ padding: 2rem;
1203
1229
  -webkit-font-smoothing: antialiased;
1204
1230
  }
1205
- .container {
1231
+
1232
+ /* radial vignette over the grid */
1233
+ body::before {
1234
+ content: '';
1235
+ position: fixed;
1236
+ inset: 0;
1237
+ background: radial-gradient(ellipse 80% 60% at 50% 50%, transparent 0%, #0B1120 80%);
1238
+ pointer-events: none;
1239
+ z-index: 0;
1240
+ }
1241
+
1242
+ .card {
1243
+ position: relative;
1244
+ z-index: 1;
1206
1245
  display: flex;
1246
+ flex-direction: column;
1207
1247
  align-items: center;
1208
- gap: 32px;
1209
- padding: 32px;
1248
+ text-align: center;
1249
+ max-width: 480px;
1250
+ animation: fadeUp 0.25s ease forwards;
1210
1251
  }
1211
- .code {
1212
- font-size: 48px;
1213
- font-weight: 700;
1252
+
1253
+ /* \u2500\u2500 glowing 404 number \u2500\u2500 */
1254
+ .status-number {
1255
+ font-family: 'DM Mono', monospace;
1256
+ font-size: clamp(80px, 18vw, 140px);
1257
+ font-weight: 500;
1214
1258
  line-height: 1;
1215
- color: #fff;
1216
- border-right: 1px solid rgba(255,255,255,0.15);
1217
- padding-right: 32px;
1218
- }
1219
- .body h1 {
1220
- font-size: 18px;
1221
- font-weight: 600;
1222
- color: #ededed;
1259
+ letter-spacing: -0.04em;
1260
+ color: #F8FAFC;
1261
+ position: relative;
1223
1262
  margin-bottom: 8px;
1224
1263
  }
1225
- .body p {
1226
- font-size: 14px;
1227
- color: #888;
1228
- margin-bottom: 20px;
1264
+ .status-number::after {
1265
+ content: attr(data-value);
1266
+ position: absolute;
1267
+ inset: 0;
1268
+ color: #2563EB;
1269
+ opacity: 0.12;
1270
+ filter: blur(24px);
1271
+ pointer-events: none;
1272
+ }
1273
+
1274
+ /* \u2500\u2500 accent line \u2500\u2500 */
1275
+ .accent-line {
1276
+ width: 40px;
1277
+ height: 3px;
1278
+ background: #2563EB;
1279
+ border-radius: 9999px;
1280
+ margin-bottom: 24px;
1229
1281
  }
1230
- .body p code {
1231
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1282
+
1283
+ /* \u2500\u2500 heading \u2500\u2500 */
1284
+ h1 {
1285
+ font-size: 22px;
1286
+ font-weight: 700;
1287
+ color: #fff;
1288
+ margin-bottom: 10px;
1289
+ letter-spacing: -0.01em;
1290
+ }
1291
+
1292
+ /* \u2500\u2500 route badge \u2500\u2500 */
1293
+ .route-badge {
1294
+ display: inline-flex;
1295
+ align-items: center;
1296
+ gap: 8px;
1297
+ font-family: 'DM Mono', monospace;
1232
1298
  font-size: 13px;
1233
- background: rgba(255,255,255,0.07);
1234
- padding: 2px 6px;
1235
- border-radius: 4px;
1236
- color: #ccc;
1299
+ color: #94A3B8;
1300
+ background: #0F172A;
1301
+ border: 1px solid #1E293B;
1302
+ padding: 5px 14px;
1303
+ border-radius: 9999px;
1304
+ margin-bottom: 32px;
1305
+ }
1306
+ .route-badge .dot {
1307
+ width: 6px;
1308
+ height: 6px;
1309
+ border-radius: 50%;
1310
+ background: #ff6b6b;
1311
+ animation: pulse 1.8s ease-in-out infinite;
1312
+ flex-shrink: 0;
1313
+ }
1314
+
1315
+ /* \u2500\u2500 actions \u2500\u2500 */
1316
+ .actions {
1317
+ display: flex;
1318
+ gap: 12px;
1237
1319
  }
1238
- .actions { display: flex; gap: 10px; }
1239
1320
  .btn {
1240
- display: inline-block;
1241
- padding: 8px 18px;
1242
- border-radius: 8px;
1321
+ display: inline-flex;
1322
+ align-items: center;
1323
+ gap: 6px;
1324
+ padding: 10px 22px;
1325
+ border-radius: 10px;
1243
1326
  font-size: 14px;
1244
- font-weight: 500;
1327
+ font-weight: 600;
1245
1328
  text-decoration: none;
1246
- transition: all .15s;
1247
- border: 1px solid transparent;
1329
+ transition: all 0.15s ease;
1248
1330
  }
1249
1331
  .btn-primary {
1250
- background: #fff;
1251
- color: #000;
1332
+ background: #2563EB;
1333
+ color: #0B1120;
1334
+ border: 1px solid #2563EB;
1335
+ }
1336
+ .btn-primary:hover {
1337
+ background: #3B82F6;
1338
+ transform: translateY(-1px);
1339
+ box-shadow: 0 0 20px rgba(37, 99, 235, 0.35);
1252
1340
  }
1253
- .btn-primary:hover { background: #e5e5e5; }
1254
1341
  .btn-secondary {
1255
- background: rgba(255,255,255,0.06);
1256
- color: #ededed;
1257
- border-color: rgba(255,255,255,0.1);
1342
+ background: transparent;
1343
+ color: #F8FAFC;
1344
+ border: 1px solid #1E293B;
1345
+ }
1346
+ .btn-secondary:hover {
1347
+ background: #0F172A;
1348
+ border-color: #334155;
1349
+ }
1350
+
1351
+ /* \u2500\u2500 footer branding \u2500\u2500 */
1352
+ .brand {
1353
+ position: fixed;
1354
+ bottom: 28px;
1355
+ left: 50%;
1356
+ transform: translateX(-50%);
1357
+ display: flex;
1358
+ align-items: center;
1359
+ gap: 8px;
1360
+ font-size: 12px;
1361
+ color: #475569;
1362
+ font-family: 'DM Mono', monospace;
1363
+ }
1364
+ .brand-dot {
1365
+ width: 6px;
1366
+ height: 6px;
1367
+ border-radius: 50%;
1368
+ background: #2563EB;
1258
1369
  }
1259
- .btn-secondary:hover { background: rgba(255,255,255,0.1); }
1260
1370
  </style>
1261
1371
  </head>
1262
1372
  <body>
1263
- <div class="container">
1264
- <div class="code">404</div>
1265
- <div class="body">
1266
- <h1>This page could not be found</h1>
1267
- <p>The path <code>${escapeHtml2(pathname)}</code> does not exist.</p>
1268
- <div class="actions">
1269
- <a href="/" class="btn btn-primary">Return Home</a>
1270
- <a href="javascript:history.back()" class="btn btn-secondary">Go Back</a>
1271
- </div>
1272
- </div>
1373
+ <div class="card">
1374
+ <div class="status-number" data-value="404">404</div>
1375
+ <div class="accent-line"></div>
1376
+ <h1>Page not found</h1>
1377
+ <div class="route-badge">
1378
+ <span class="dot"></span>
1379
+ <span>${escapeHtml2(pathname)}</span>
1380
+ </div>
1381
+ <div class="actions">
1382
+ <a href="/" class="btn btn-primary">
1383
+ <svg width="14" height="14" fill="none" viewBox="0 0 24 24"><path d="M3 12L12 3l9 9M5 10v9a1 1 0 001 1h4v-5h4v5h4a1 1 0 001-1v-9" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
1384
+ Home
1385
+ </a>
1386
+ <a href="javascript:history.back()" class="btn btn-secondary">
1387
+ <svg width="14" height="14" fill="none" viewBox="0 0 24 24"><path d="M19 12H5M5 12l7-7M5 12l7 7" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
1388
+ Go back
1389
+ </a>
1390
+ </div>
1391
+ </div>
1392
+
1393
+ <div class="brand">
1394
+ <span class="brand-dot"></span>
1395
+ Velix v${VERSION}
1273
1396
  </div>
1274
1397
  </body>
1275
1398
  </html>`;
@@ -1300,14 +1423,23 @@ function generate500Page(options) {
1300
1423
  const src = fs4.readFileSync(errorFile, "utf-8").split("\n");
1301
1424
  const start = Math.max(0, errorLine - 4);
1302
1425
  const end = Math.min(src.length, errorLine + 3);
1426
+ const syntaxHighlight = (code) => {
1427
+ let hl = code;
1428
+ hl = hl.replace(/\\b(let|const|function|return|import|export|if|else|for|while|await|async|class|var)\\b/g, '<span style="color:#c084fc">$1</span>');
1429
+ hl = hl.replace(/(&quot;.*?&quot;|&#39;.*?&#39;|`.*?`)/g, '<span style="color:#86efac">$1</span>');
1430
+ hl = hl.replace(/(\\&#x2F;\\&#x2F;.*)$/g, '<span style="color:#94A3B8">$1</span>');
1431
+ hl = hl.replace(/\\b(\\d+)\\b/g, '<span style="color:#f59e0b">$1</span>');
1432
+ return hl;
1433
+ };
1303
1434
  sourceSnippet = src.slice(start, end).map((l, i) => {
1304
1435
  const num = start + i + 1;
1305
1436
  const isErr = num === errorLine;
1306
1437
  const numStr = String(num).padStart(3, " ");
1438
+ const highlightedLine = syntaxHighlight(escapeHtml2(l));
1307
1439
  if (isErr) {
1308
- return `<div class="src-line src-err"><span class="ln">${numStr}</span>${escapeHtml2(l)}</div>`;
1440
+ return `<div class="src-line src-err"><span class="ln">${numStr}</span><span class="active-arrow">\u25B6</span><span class="code-content">${highlightedLine}</span></div>`;
1309
1441
  }
1310
- return `<div class="src-line"><span class="ln">${numStr}</span>${escapeHtml2(l)}</div>`;
1442
+ return `<div class="src-line"><span class="ln">${numStr}</span><span class="active-arrow"> </span><span class="code-content">${highlightedLine}</span></div>`;
1311
1443
  }).join("");
1312
1444
  }
1313
1445
  } catch (_) {
@@ -1315,6 +1447,13 @@ function generate500Page(options) {
1315
1447
  }
1316
1448
  }
1317
1449
  }
1450
+ const formatErrorMessage = (msg) => {
1451
+ let formatted = escapeHtml2(msg);
1452
+ const pathSpan = `<span style="color:#2563EB;cursor:pointer;" onclick="console.log('open in editor')">`;
1453
+ formatted = formatted.replace(/([A-Z]:\\[^\s<]+|\/(?:Users|home|var|srv)\/[^\s<]+)/g, pathSpan + "$1</span>");
1454
+ formatted = formatted.replace(/(ERROR:)/g, '<strong style="color:#ff6b6b">$1</strong>');
1455
+ return formatted;
1456
+ };
1318
1457
  const userFrames = frames.filter((f) => f.file && !f.file.includes("node_modules"));
1319
1458
  const ignoredFrames = frames.filter((f) => !f.file || f.file.includes("node_modules"));
1320
1459
  const shortFile = errorFile.replace(/\\/g, "/").replace(/.*\/(?=app\/|server\/|src\/|pages\/)/, "");
@@ -1329,7 +1468,7 @@ function generate500Page(options) {
1329
1468
  <style>
1330
1469
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1331
1470
  body {
1332
- background: #0a0a0a;
1471
+ background: #0B1120;
1333
1472
  color: #ededed;
1334
1473
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1335
1474
  display: flex; align-items: center; justify-content: center;
@@ -1364,327 +1503,459 @@ function generate500Page(options) {
1364
1503
  <title>Error | Velix v${VERSION}</title>
1365
1504
  <link rel="icon" href="/favicon.webp">
1366
1505
  <style>
1506
+ @import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap');
1507
+
1367
1508
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1368
1509
 
1510
+ @keyframes fadeIn {
1511
+ from { opacity: 0; transform: translateY(10px); }
1512
+ to { opacity: 1; transform: translateY(0); }
1513
+ }
1514
+
1369
1515
  body {
1370
- background: rgba(0, 0, 0, 0.85);
1371
- color: #ededed;
1372
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1516
+ background-color: #0B1120;
1517
+ background-image:
1518
+ linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),
1519
+ linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
1520
+ background-size: 60px 60px;
1521
+ color: #F8FAFC;
1522
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1373
1523
  min-height: 100vh;
1374
1524
  display: flex;
1375
- align-items: center;
1525
+ align-items: flex-start;
1376
1526
  justify-content: center;
1377
- padding: 24px;
1527
+ padding: 2rem;
1378
1528
  -webkit-font-smoothing: antialiased;
1379
1529
  }
1380
1530
 
1381
- /* \u2500\u2500 Overlay Card \u2500\u2500 */
1382
- .overlay {
1531
+ /* Radial mask for the grid background */
1532
+ .bg-mask {
1533
+ position: fixed;
1534
+ top: 0; left: 0; right: 0; bottom: 0;
1535
+ background: radial-gradient(circle at center, transparent 0%, #0B1120 80%);
1536
+ z-index: -1;
1537
+ pointer-events: none;
1538
+ }
1539
+
1540
+ /* \u2500\u2500 Main Container \u2500\u2500 */
1541
+ .container {
1383
1542
  width: 100%;
1384
- max-width: 940px;
1385
- background: #111;
1386
- border: 1px solid #2a2a2a;
1387
- border-radius: 12px;
1388
- box-shadow: 0 24px 64px rgba(0,0,0,0.6);
1389
- overflow: hidden;
1543
+ max-width: 780px;
1544
+ margin-top: 4vh;
1545
+ animation: fadeIn 0.2s ease forwards;
1390
1546
  display: flex;
1391
1547
  flex-direction: column;
1392
- max-height: calc(100vh - 48px);
1548
+ gap: 24px;
1393
1549
  }
1394
1550
 
1395
1551
  /* \u2500\u2500 Header Bar \u2500\u2500 */
1396
1552
  .header {
1553
+ position: relative;
1554
+ background: transparent;
1397
1555
  display: flex;
1398
1556
  align-items: center;
1399
1557
  justify-content: space-between;
1400
- padding: 12px 20px;
1401
- background: #161616;
1402
- border-bottom: 1px solid #2a2a2a;
1403
- gap: 12px;
1404
- flex-shrink: 0;
1558
+ padding-top: 12px;
1559
+ }
1560
+ .header::before {
1561
+ content: '';
1562
+ position: absolute;
1563
+ top: -12px; left: 0; right: 0;
1564
+ height: 4px;
1565
+ background: #ff6b6b;
1566
+ border-radius: 4px;
1405
1567
  }
1406
1568
  .header-left {
1407
1569
  display: flex;
1408
1570
  align-items: center;
1409
- gap: 8px;
1410
- font-size: 13px;
1411
- color: #888;
1571
+ gap: 12px;
1412
1572
  }
1413
- .counter-badge {
1414
- background: #1e1e1e;
1415
- border: 1px solid #333;
1416
- border-radius: 6px;
1417
- padding: 2px 8px;
1573
+ .header-icon {
1574
+ width: 20px;
1575
+ height: 20px;
1576
+ fill: #ff6b6b;
1577
+ }
1578
+ .header-title {
1579
+ font-size: 18px;
1580
+ font-weight: 700;
1581
+ color: #fff;
1582
+ }
1583
+ .dev-badge {
1584
+ background: rgba(255, 107, 107, 0.1);
1585
+ border: 1px solid rgba(255, 107, 107, 0.2);
1586
+ color: #ff6b6b;
1587
+ padding: 2px 10px;
1588
+ border-radius: 9999px;
1418
1589
  font-size: 12px;
1419
- color: #999;
1420
- font-variant-numeric: tabular-nums;
1590
+ font-weight: 600;
1591
+ letter-spacing: 0.05em;
1421
1592
  }
1422
- .nav-btn {
1423
- background: #1e1e1e;
1424
- border: 1px solid #333;
1425
- border-radius: 6px;
1426
- color: #888;
1427
- cursor: pointer;
1428
- width: 24px;
1429
- height: 24px;
1593
+
1594
+ /* \u2500\u2500 Status & Route \u2500\u2500 */
1595
+ .status-bar {
1430
1596
  display: flex;
1431
1597
  align-items: center;
1432
- justify-content: center;
1598
+ gap: 12px;
1599
+ margin-top: 8px;
1600
+ font-family: 'DM Mono', monospace;
1433
1601
  font-size: 13px;
1434
- transition: all .15s;
1435
1602
  }
1436
- .nav-btn:hover:not(:disabled) { background: #2a2a2a; color: #fff; }
1437
- .nav-btn:disabled { opacity: 0.3; cursor: default; }
1438
- .framework-tag {
1439
- display: flex;
1440
- align-items: center;
1441
- gap: 6px;
1442
- font-size: 12px;
1443
- color: #666;
1603
+ .status-badge {
1604
+ background: #0F172A;
1605
+ border: 1px solid #1E293B;
1606
+ color: #ff6b6b;
1607
+ padding: 4px 10px;
1608
+ border-radius: 6px;
1444
1609
  }
1445
- .framework-tag::before {
1446
- content: '';
1447
- display: inline-block;
1448
- width: 6px; height: 6px;
1449
- border-radius: 50%;
1450
- background: #3b82f6;
1610
+ .route-badge {
1611
+ background: #0F172A;
1612
+ border: 1px solid #1E293B;
1613
+ color: #2563EB;
1614
+ padding: 4px 10px;
1615
+ border-radius: 6px;
1451
1616
  }
1617
+ .separator { color: #94A3B8; }
1618
+ .timestamp { color: #94A3B8; font-family: system-ui; font-size: 13px; }
1452
1619
 
1453
- /* \u2500\u2500 Scrollable Content \u2500\u2500 */
1454
- .content {
1455
- overflow-y: auto;
1456
- flex: 1;
1620
+ /* \u2500\u2500 Error Message \u2500\u2500 */
1621
+ .error-message-box {
1622
+ background: #0F172A;
1623
+ border: 1px solid #1E293B;
1624
+ border-left: 3px solid #ff6b6b;
1625
+ border-radius: 10px;
1626
+ padding: 1rem 1.25rem;
1627
+ font-family: 'DM Mono', monospace;
1628
+ font-size: 14px;
1629
+ color: #F8FAFC;
1630
+ line-height: 1.6;
1631
+ white-space: pre-wrap;
1632
+ word-break: break-word;
1457
1633
  }
1458
1634
 
1459
- /* \u2500\u2500 Error Summary \u2500\u2500 */
1460
- .error-summary {
1461
- padding: 28px 24px 20px;
1462
- border-bottom: 1px solid #1e1e1e;
1635
+ /* \u2500\u2500 Source Code Viewer \u2500\u2500 */
1636
+ .source-viewer {
1637
+ display: flex;
1638
+ flex-direction: column;
1639
+ gap: 12px;
1463
1640
  }
1464
- .error-label {
1465
- display: inline-flex;
1641
+ .source-header {
1642
+ display: flex;
1466
1643
  align-items: center;
1467
- gap: 6px;
1468
- font-size: 11px;
1469
- font-weight: 700;
1470
- letter-spacing: 0.08em;
1471
- text-transform: uppercase;
1472
- color: #f87171;
1473
- background: rgba(248, 113, 113, 0.1);
1474
- border: 1px solid rgba(248, 113, 113, 0.25);
1475
- padding: 3px 8px;
1476
- border-radius: 4px;
1477
- margin-bottom: 14px;
1478
- }
1479
- .error-label svg { width: 10px; height: 10px; fill: currentColor; }
1480
- .error-message {
1481
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1482
- font-size: 18px;
1483
- font-weight: 600;
1484
- line-height: 1.5;
1485
- color: #fff;
1486
- word-break: break-word;
1644
+ justify-content: space-between;
1487
1645
  }
1488
- .error-location {
1489
- margin-top: 10px;
1646
+ .source-filepath {
1647
+ font-family: 'DM Mono', monospace;
1490
1648
  font-size: 13px;
1491
- color: #888;
1649
+ color: #2563EB;
1492
1650
  }
1493
- .error-location strong { color: #ccc; }
1494
-
1495
- /* \u2500\u2500 Source Block \u2500\u2500 */
1496
- .source-block {
1497
- border-bottom: 1px solid #1e1e1e;
1498
- }
1499
- .source-topbar {
1651
+ .open-in-editor {
1652
+ color: #94A3B8;
1653
+ cursor: pointer;
1654
+ transition: color 0.15s;
1500
1655
  display: flex;
1501
1656
  align-items: center;
1502
- padding: 10px 16px;
1503
- background: #161616;
1504
- border-bottom: 1px solid #1e1e1e;
1505
- gap: 8px;
1506
- font-size: 12px;
1507
- color: #666;
1657
+ gap: 6px;
1658
+ font-size: 13px;
1659
+ text-decoration: none;
1508
1660
  }
1509
- .source-topbar .filepath { color: #aaa; font-family: ui-monospace, monospace; }
1510
- .source-code {
1511
- background: #0d0d0d;
1661
+ .open-in-editor:hover { color: #F8FAFC; }
1662
+ .open-in-editor svg { width: 14px; height: 14px; stroke: currentColor; fill: none; }
1663
+
1664
+ .source-body {
1665
+ background: #020617;
1666
+ border: 1px solid #1E293B;
1667
+ border-radius: 8px;
1668
+ padding: 16px 0;
1512
1669
  overflow-x: auto;
1513
- padding: 10px 0;
1514
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1670
+ font-family: 'DM Mono', monospace;
1515
1671
  font-size: 13px;
1516
- line-height: 1.7;
1672
+ line-height: 1.6;
1517
1673
  }
1518
1674
  .src-line {
1519
1675
  display: flex;
1520
- align-items: flex-start;
1521
- padding: 0 20px;
1522
- white-space: pre;
1523
- color: #555;
1676
+ padding: 0 16px;
1524
1677
  }
1525
- .src-line .ln {
1526
- color: #3a3a3a;
1678
+ .ln {
1679
+ color: #475569;
1680
+ width: 32px;
1527
1681
  text-align: right;
1528
- width: 36px;
1529
1682
  margin-right: 16px;
1530
- flex-shrink: 0;
1531
1683
  user-select: none;
1684
+ flex-shrink: 0;
1532
1685
  }
1533
- .src-err {
1534
- background: rgba(239, 68, 68, 0.08);
1535
- color: #fca5a5;
1536
- position: relative;
1686
+ .active-arrow {
1687
+ color: transparent;
1688
+ margin-right: 12px;
1689
+ width: 14px;
1690
+ text-align: center;
1691
+ flex-shrink: 0;
1537
1692
  }
1538
- .src-err .ln { color: #ef4444; }
1539
- .src-err::before {
1540
- content: '';
1541
- position: absolute;
1542
- left: 0;
1543
- top: 0; bottom: 0;
1544
- width: 3px;
1545
- background: #ef4444;
1693
+ .code-content { white-space: pre; }
1694
+
1695
+ .src-err {
1696
+ background: rgba(37, 99, 235, 0.04);
1546
1697
  }
1698
+ .src-err .ln { color: #2563EB; }
1699
+ .src-err .active-arrow { color: #2563EB; }
1700
+ .src-err .code-content { color: #F8FAFC; }
1547
1701
 
1548
1702
  /* \u2500\u2500 Call Stack \u2500\u2500 */
1549
- .callstack {
1550
- padding: 20px 24px;
1703
+ .callstack-section {
1704
+ margin-top: 16px;
1705
+ display: flex;
1706
+ flex-direction: column;
1707
+ gap: 12px;
1551
1708
  }
1552
1709
  .callstack-header {
1553
1710
  display: flex;
1554
1711
  align-items: center;
1555
1712
  justify-content: space-between;
1556
- margin-bottom: 14px;
1557
1713
  }
1558
1714
  .callstack-title {
1559
- font-size: 13px;
1715
+ font-size: 16px;
1560
1716
  font-weight: 600;
1561
- color: #aaa;
1562
- text-transform: uppercase;
1563
- letter-spacing: 0.06em;
1717
+ display: flex;
1718
+ align-items: center;
1719
+ gap: 12px;
1564
1720
  }
1565
- .ignore-btn {
1721
+ .callstack-count {
1722
+ background: #1E293B;
1723
+ color: #F8FAFC;
1724
+ padding: 2px 10px;
1725
+ border-radius: 9999px;
1566
1726
  font-size: 12px;
1567
- color: #555;
1727
+ font-weight: 500;
1728
+ }
1729
+
1730
+ .toggle-node-modules {
1731
+ color: #94A3B8;
1732
+ font-size: 13px;
1568
1733
  background: none;
1569
1734
  border: none;
1570
1735
  cursor: pointer;
1736
+ transition: color 0.15s;
1737
+ }
1738
+ .toggle-node-modules:hover { color: #F8FAFC; }
1739
+
1740
+ .frame {
1741
+ background: #0F172A;
1742
+ border: 1px solid #1E293B;
1743
+ border-radius: 8px;
1744
+ padding: 0.875rem 1rem;
1571
1745
  display: flex;
1746
+ justify-content: space-between;
1572
1747
  align-items: center;
1748
+ transition: background-color 0.15s, transform 0.15s;
1749
+ }
1750
+ .frame:hover { background: #1E293B; }
1751
+
1752
+ .frame-info {
1753
+ display: flex;
1754
+ flex-direction: column;
1573
1755
  gap: 4px;
1574
- transition: color .15s;
1575
1756
  }
1576
- .ignore-btn:hover { color: #aaa; }
1577
- .ignore-caret { transition: transform .2s; }
1578
- .ignore-btn.open .ignore-caret { transform: rotate(90deg); }
1757
+ .frame-call {
1758
+ font-family: 'DM Mono', monospace;
1759
+ font-size: 14px;
1760
+ font-weight: 700;
1761
+ color: #2563EB;
1762
+ }
1763
+ .frame-file {
1764
+ font-family: 'DM Mono', monospace;
1765
+ font-size: 13px;
1766
+ color: #94A3B8;
1767
+ }
1768
+
1769
+ .frame.node-modules .frame-call { color: #94A3B8; font-weight: 400; }
1770
+ .frame.node-modules .frame-file { color: #475569; }
1771
+
1772
+ .frame-number {
1773
+ background: #1E293B;
1774
+ color: #94A3B8;
1775
+ padding: 2px 8px;
1776
+ border-radius: 6px;
1777
+ font-size: 12px;
1778
+ font-variant-numeric: tabular-nums;
1779
+ }
1579
1780
 
1580
- .frame {
1581
- padding: 10px 12px;
1781
+ .pagination {
1782
+ display: flex;
1783
+ justify-content: flex-end;
1784
+ gap: 8px;
1785
+ margin-top: 8px;
1786
+ }
1787
+ .page-btn {
1788
+ background: transparent;
1789
+ border: 1px solid #1E293B;
1790
+ color: #F8FAFC;
1791
+ padding: 6px 14px;
1582
1792
  border-radius: 6px;
1583
- margin-bottom: 4px;
1584
1793
  font-size: 13px;
1585
- cursor: default;
1794
+ cursor: pointer;
1795
+ transition: all 0.15s;
1586
1796
  }
1587
- .frame:hover { background: rgba(255,255,255,0.03); }
1588
- .frame-call {
1589
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1797
+ .page-btn:hover:not(:disabled) { background: #1E293B; }
1798
+ .page-btn:disabled { opacity: 0.3; cursor: default; }
1799
+
1800
+ .node-modules-list { display: none; flex-direction: column; gap: 12px; margin-top: 12px; }
1801
+ .node-modules-list.visible { display: flex; }
1802
+
1803
+ /* \u2500\u2500 Footer \u2500\u2500 */
1804
+ .footer {
1805
+ margin-top: 32px;
1806
+ padding-top: 24px;
1807
+ border-top: 1px solid #1E293B;
1808
+ display: flex;
1809
+ justify-content: space-between;
1810
+ align-items: center;
1811
+ }
1812
+ .footer-left {
1813
+ display: flex;
1814
+ align-items: center;
1815
+ gap: 8px;
1816
+ font-size: 13px;
1590
1817
  font-weight: 600;
1591
- color: #e2e8f0;
1592
- margin-bottom: 3px;
1818
+ color: #F8FAFC;
1593
1819
  }
1594
- .frame-loc {
1595
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1820
+ .velix-dot { width: 8px; height: 8px; border-radius: 50%; background: #2563EB; }
1821
+ .footer-right {
1822
+ display: flex;
1823
+ align-items: center;
1824
+ gap: 16px;
1825
+ }
1826
+ .footer-link {
1827
+ color: #94A3B8;
1828
+ text-decoration: none;
1829
+ font-size: 13px;
1830
+ transition: color 0.15s;
1831
+ }
1832
+ .footer-link:hover { color: #F8FAFC; }
1833
+ .dev-pill {
1834
+ background: #1E293B;
1835
+ color: #F8FAFC;
1836
+ padding: 4px 12px;
1837
+ border-radius: 9999px;
1596
1838
  font-size: 12px;
1597
- color: #555;
1598
1839
  }
1599
- .frame.ignored .frame-call { color: #444; }
1600
-
1601
- .ignored-section { margin-top: 8px; display: none; }
1602
- .ignored-section.open { display: block; }
1603
1840
  </style>
1604
1841
  </head>
1605
1842
  <body>
1606
- <div class="overlay">
1607
-
1608
- <!-- Header bar -->
1843
+ <div class="bg-mask"></div>
1844
+ <div class="container">
1845
+
1846
+ <!-- Header -->
1609
1847
  <div class="header">
1610
1848
  <div class="header-left">
1611
- <button class="nav-btn" disabled>\u2039</button>
1612
- <span class="counter-badge">1 / 1</span>
1613
- <button class="nav-btn" disabled>\u203A</button>
1614
- <span style="margin-left: 4px;">Unhandled Runtime Error</span>
1849
+ <svg class="header-icon" viewBox="0 0 24 24"><path d="M12 2L1 21h22L12 2zm0 3.83L19.17 19H4.83L12 5.83zM11 10h2v5h-2v-5zm0 6h2v2h-2v-2z"/></svg>
1850
+ <div class="header-title">Unhandled Runtime Error</div>
1615
1851
  </div>
1616
- <div class="framework-tag">Velix v${VERSION} \xB7 esbuild</div>
1852
+ <div class="dev-badge">DEV</div>
1617
1853
  </div>
1618
1854
 
1619
- <!-- Scrollable content -->
1620
- <div class="content">
1855
+ <!-- Status & Route -->
1856
+ <div class="status-bar">
1857
+ <div class="status-badge">${options.statusCode || 500}</div>
1858
+ <div class="separator">\xB7</div>
1859
+ <div class="route-badge">Route: ${pathname || "/"}</div>
1860
+ <div class="separator">\xB7</div>
1861
+ <div class="timestamp">just now</div>
1862
+ </div>
1621
1863
 
1622
- <!-- Error summary -->
1623
- <div class="error-summary">
1624
- <div class="error-label">
1625
- <svg viewBox="0 0 16 16"><path d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1zm-.75 3.5a.75.75 0 0 1 1.5 0v4a.75.75 0 0 1-1.5 0v-4zm.75 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5z"/></svg>
1626
- Runtime Error
1627
- </div>
1628
- <div class="error-message">${escapeHtml2(message)}</div>
1629
- ${pathname ? `<div class="error-location">in <strong>${escapeHtml2(pathname)}</strong></div>` : ""}
1630
- </div>
1864
+ <!-- Error Message -->
1865
+ <div class="error-message-box">
1866
+ ${formatErrorMessage(message)}
1867
+ </div>
1631
1868
 
1632
- ${sourceSnippet ? `
1633
- <!-- Source code snippet -->
1634
- <div class="source-block">
1635
- <div class="source-topbar">
1636
- <svg width="12" height="12" fill="none" viewBox="0 0 24 24"><path stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z"/><polyline stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" points="14 2 14 8 20 8"/></svg>
1637
- <span class="filepath">${escapeHtml2(shortFile)}${errorLine ? `:${errorLine}` : ""}</span>
1638
- </div>
1639
- <div class="source-code">${sourceSnippet}</div>
1869
+ ${sourceSnippet ? `
1870
+ <!-- Source Viewer -->
1871
+ <div class="source-viewer">
1872
+ <div class="source-header">
1873
+ <div class="source-filepath">${escapeHtml2(shortFile)}${errorLine ? ` (${errorLine})` : ""} @ runtime</div>
1874
+ <a href="#" class="open-in-editor" onclick="console.log('Open in editor clicked')">
1875
+ Open in editor
1876
+ <svg viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path></svg>
1877
+ </a>
1640
1878
  </div>
1641
- ` : ""}
1879
+ <div class="source-body">
1880
+ ${sourceSnippet}
1881
+ </div>
1882
+ </div>
1883
+ ` : ""}
1642
1884
 
1643
- <!-- Call stack -->
1644
- <div class="callstack">
1645
- <div class="callstack-header">
1646
- <div class="callstack-title">Call Stack</div>
1647
- ${ignoredFrames.length > 0 ? `
1648
- <button class="ignore-btn" id="ignoreBtn" onclick="toggleIgnored()">
1649
- <svg class="ignore-caret" width="12" height="12" viewBox="0 0 12 12" fill="none">
1650
- <path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
1651
- </svg>
1652
- ${ignoredFrames.length} ignored frames
1653
- </button>
1654
- ` : ""}
1885
+ <!-- Call Stack -->
1886
+ <div class="callstack-section">
1887
+ <div class="callstack-header">
1888
+ <div class="callstack-title">
1889
+ Call Stack
1890
+ <div class="callstack-count">${frames.length}</div>
1655
1891
  </div>
1892
+ ${ignoredFrames.length > 0 ? `
1893
+ <button class="toggle-node-modules" id="toggleNodeModules" onclick="toggleNodeModules()">
1894
+ Show ${ignoredFrames.length} node_modules frames
1895
+ </button>
1896
+ ` : ""}
1897
+ </div>
1656
1898
 
1657
- <!-- User frames -->
1658
- ${userFrames.map((f) => `
1659
- <div class="frame">
1899
+ ${userFrames.map((f, i) => `
1900
+ <div class="frame">
1901
+ <div class="frame-info">
1660
1902
  <div class="frame-call">${escapeHtml2(f.call)}</div>
1661
- <div class="frame-loc">${escapeHtml2(f.file.replace(/\\/g, "/").replace(/.*\/(?=app\/|server\/|src\/|pages\/)/, ""))}${f.line ? `:${f.line}` : ""}</div>
1903
+ <div class="frame-file">${escapeHtml2(f.file.replace(/\\\\/g, "/"))}${f.line ? `:${f.line}` : ""}</div>
1662
1904
  </div>
1663
- `).join("")}
1905
+ <div class="frame-number">${i + 1}</div>
1906
+ </div>
1907
+ `).join("")}
1664
1908
 
1665
- <!-- Ignored frames -->
1666
- ${ignoredFrames.length > 0 ? `
1667
- <div class="ignored-section" id="ignoredSection">
1668
- ${ignoredFrames.map((f) => `
1669
- <div class="frame ignored">
1909
+ ${ignoredFrames.length > 0 ? `
1910
+ <div class="node-modules-list" id="nodeModulesList">
1911
+ ${ignoredFrames.map((f, i) => `
1912
+ <div class="frame node-modules">
1913
+ <div class="frame-info">
1670
1914
  <div class="frame-call">${escapeHtml2(f.call)}</div>
1671
- <div class="frame-loc">${escapeHtml2(f.file)}${f.line ? `:${f.line}` : ""}</div>
1915
+ <div class="frame-file">${escapeHtml2(f.file)}${f.line ? `:${f.line}` : ""}</div>
1672
1916
  </div>
1673
- `).join("")}
1917
+ <div class="frame-number">${userFrames.length + i + 1}</div>
1674
1918
  </div>
1675
- ` : ""}
1919
+ `).join("")}
1920
+ </div>
1921
+ ` : ""}
1922
+
1923
+ <div class="pagination">
1924
+ <button class="page-btn" disabled>Previous</button>
1925
+ <button class="page-btn" disabled>Next</button>
1926
+ </div>
1927
+ </div>
1928
+
1929
+ <!-- Footer -->
1930
+ <div class="footer">
1931
+ <div class="footer-left">
1932
+ <div class="velix-dot"></div>
1933
+ Velix v${VERSION}
1676
1934
  </div>
1935
+ <div class="footer-right">
1936
+ <a href="/" class="footer-link">Home</a>
1937
+ <a href="#" class="footer-link" onclick="window.location.reload()">Reload</a>
1938
+ <a href="https://velixcloud.io/docs" class="footer-link" target="_blank">Documentation</a>
1939
+ <div class="dev-pill">Development Mode</div>
1940
+ </div>
1941
+ </div>
1677
1942
 
1678
- </div><!-- /content -->
1679
- </div><!-- /overlay -->
1943
+ </div>
1680
1944
 
1681
1945
  <script>
1682
- function toggleIgnored() {
1683
- const btn = document.getElementById('ignoreBtn');
1684
- const sec = document.getElementById('ignoredSection');
1685
- if (!btn || !sec) return;
1686
- const open = sec.classList.toggle('open');
1687
- btn.classList.toggle('open', open);
1946
+ function toggleNodeModules() {
1947
+ const list = document.getElementById('nodeModulesList');
1948
+ const btn = document.getElementById('toggleNodeModules');
1949
+ if (!list || !btn) return;
1950
+
1951
+ const isVisible = list.classList.contains('visible');
1952
+ if (isVisible) {
1953
+ list.classList.remove('visible');
1954
+ btn.textContent = 'Show ' + list.children.length + ' node_modules frames';
1955
+ } else {
1956
+ list.classList.add('visible');
1957
+ btn.textContent = 'Hide node_modules frames';
1958
+ }
1688
1959
  }
1689
1960
  </script>
1690
1961
  </body>
@@ -1805,6 +2076,7 @@ async function createServer(options = {}) {
1805
2076
  if (!res.headersSent) {
1806
2077
  res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
1807
2078
  res.end(generate500Page({
2079
+ statusCode: 500,
1808
2080
  message: err.message || "An unexpected error occurred",
1809
2081
  stack: isDev ? err.stack : void 0,
1810
2082
  isDev,
@@ -2140,6 +2412,7 @@ async function handlePageRoute(route, routes, req, res, url, config, isDev, proj
2140
2412
  logger_default.error(`Render error: ${route.path}`, error);
2141
2413
  res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
2142
2414
  res.end(generate500Page({
2415
+ statusCode: 500,
2143
2416
  message: error.message || "Failed to render page",
2144
2417
  stack: isDev ? error.stack : void 0,
2145
2418
  isDev,
@@ -2318,5 +2591,5 @@ function parseRawBody(req) {
2318
2591
  var server_default = { createServer, tailwindPlugin };
2319
2592
 
2320
2593
  export { RedirectError, bindArgs, callServerAction, composeMiddleware, cookies, createServer, deserializeArgs, executeAction, formAction, generateJsonLd, generateMetadataTags, generateRobotsTxt, generateSitemap, getAction, getMethod, getPathname, headers, html, isMethod, json, jsonLd, loadMiddleware, mergeMetadata, middlewares, notFound, parseFormData, parseJson, parseSearchParams, redirect, registerAction, runMiddleware, serverAction, server_default, tailwindPlugin, text, useActionContext, useVelixAction };
2321
- //# sourceMappingURL=chunk-HMCBOHTD.js.map
2322
- //# sourceMappingURL=chunk-HMCBOHTD.js.map
2594
+ //# sourceMappingURL=chunk-OKEPYKAP.js.map
2595
+ //# sourceMappingURL=chunk-OKEPYKAP.js.map