@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.
@@ -707,7 +707,7 @@ init_esm_shims();
707
707
 
708
708
  // version.ts
709
709
  init_esm_shims();
710
- var VERSION = "5.2.8";
710
+ var VERSION = "5.3.0";
711
711
 
712
712
  // logger.ts
713
713
  var colors = {
@@ -1307,73 +1307,76 @@ function generateDevToolsHtml(isDev, ctx = {}) {
1307
1307
  return `
1308
1308
  <style>
1309
1309
  /* \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 */
1310
- #__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;}
1311
- #__vdt-btn:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(34,211,238,.25);}
1312
- #__vdt-dot{position:absolute;top:-3px;right:-3px;width:11px;height:11px;border-radius:50%;background:#22D3EE;border:2px solid #111827;transition:background .3s;}
1313
- #__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;}
1310
+ #__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;}
1311
+ #__vdt-btn:hover{transform:scale(1.05);background:#0F172A;border-color:rgba(255,255,255,0.2);}
1312
+ #__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;}
1313
+ #__vdt-btn.vdt-compiling #__vdt-status-text, #__vdt-btn.vdt-navigating #__vdt-status-text{opacity:1;max-width:120px;}
1314
+
1315
+ #__vdt-dot{display:none;}
1316
+ #__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;}
1314
1317
  /* header */
1315
- .__vdt-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #1f2937;}
1318
+ .__vdt-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #0F172A;}
1316
1319
  .__vdt-brand{display:flex;align-items:center;gap:8px;font-weight:700;font-size:14px;color:#f9fafb;}
1317
- .__vdt-badge{font-size:11px;font-weight:600;background:#1f2937;color:#9ca3af;border-radius:5px;padding:2px 7px;letter-spacing:.3px;}
1320
+ .__vdt-badge{font-size:11px;font-weight:600;background:#0F172A;color:#9ca3af;border-radius:5px;padding:2px 7px;letter-spacing:.3px;}
1318
1321
  .__vdt-close{background:none;border:none;color:#6b7280;cursor:pointer;font-size:20px;line-height:1;padding:0;transition:color .15s;}
1319
1322
  .__vdt-close:hover{color:#f9fafb;}
1320
1323
  /* tabs */
1321
- .__vdt-tabs{display:flex;border-bottom:1px solid #1f2937;}
1324
+ .__vdt-tabs{display:flex;border-bottom:1px solid #0F172A;}
1322
1325
  .__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;}
1323
1326
  .__vdt-tab:hover{color:#d1d5db;}
1324
- .__vdt-tab.active{color:#10b981;border-bottom-color:#10b981;}
1327
+ .__vdt-tab.active{color:#2563EB;border-bottom-color:#2563EB;}
1325
1328
  /* body */
1326
1329
  .__vdt-body{padding:14px 16px;min-height:200px;}
1327
1330
  .__vdt-section{margin-bottom:14px;}
1328
1331
  .__vdt-label{font-size:11px;color:#6b7280;text-transform:uppercase;letter-spacing:.6px;margin-bottom:8px;}
1329
- .__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;}
1330
- .__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;}
1332
+ .__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;}
1333
+ .__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;}
1331
1334
  .__vdt-row-left{display:flex;align-items:center;gap:8px;color:#d1d5db;}
1332
- .__vdt-row-right{font-family:ui-monospace,'Cascadia Code',monospace;color:#10b981;font-size:12px;font-weight:500;}
1335
+ .__vdt-row-right{font-family:ui-monospace,'Cascadia Code',monospace;color:#2563EB;font-size:12px;font-weight:500;}
1333
1336
  .__vdt-pill{font-size:10px;font-weight:600;padding:2px 7px;border-radius:4px;letter-spacing:.2px;}
1334
- .__vdt-pill-green{background:#052e16;color:#4ade80;border:1px solid #166534;}
1337
+ .__vdt-pill-green{background:#162032;color:#38BDF8;border:1px solid #2563EB;}
1335
1338
  .__vdt-pill-blue{background:#0c1a3a;color:#60a5fa;border:1px solid #1e3a8a;}
1336
- .__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;}
1339
+ .__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;}
1337
1340
  .__vdt-render svg{color:#6b7280;}
1338
1341
  /* vitals */
1339
- .__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;}
1342
+ .__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;}
1340
1343
  .__vdt-vital-left{display:flex;align-items:center;gap:8px;}
1341
- .__vdt-dot-good{width:8px;height:8px;border-radius:50%;background:#10b981;flex-shrink:0;}
1344
+ .__vdt-dot-good{width:8px;height:8px;border-radius:50%;background:#2563EB;flex-shrink:0;}
1342
1345
  .__vdt-dot-warn{width:8px;height:8px;border-radius:50%;background:#f59e0b;flex-shrink:0;}
1343
1346
  .__vdt-dot-bad{width:8px;height:8px;border-radius:50%;background:#ef4444;flex-shrink:0;}
1344
1347
  .__vdt-vital-name{color:#d1d5db;font-weight:500;}
1345
- .__vdt-vital-val{font-family:ui-monospace,'Cascadia Code',monospace;color:#10b981;font-size:12px;font-weight:600;}
1346
- .__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;}
1348
+ .__vdt-vital-val{font-family:ui-monospace,'Cascadia Code',monospace;color:#2563EB;font-size:12px;font-weight:600;}
1349
+ .__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;}
1347
1350
  /* info rows */
1348
- .__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;}
1351
+ .__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;}
1349
1352
  .__vdt-info-left{display:flex;align-items:center;gap:8px;color:#d1d5db;}
1350
1353
  .__vdt-info-val{font-family:ui-monospace,'Cascadia Code',monospace;color:#9ca3af;font-size:12px;}
1351
- .__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;}
1354
+ .__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;}
1352
1355
  /* footer */
1353
- .__vdt-footer{padding:10px 16px;border-top:1px solid #1f2937;display:flex;align-items:center;justify-content:space-between;}
1356
+ .__vdt-footer{padding:10px 16px;border-top:1px solid #0F172A;display:flex;align-items:center;justify-content:space-between;}
1354
1357
  .__vdt-host{color:#6b7280;font-size:11px;font-family:ui-monospace,'Cascadia Code',monospace;}
1355
- .__vdt-conn{display:flex;align-items:center;gap:5px;font-size:11px;color:#10b981;}
1356
- .__vdt-conn-dot{width:6px;height:6px;border-radius:50%;background:#10b981;}
1358
+ .__vdt-conn{display:flex;align-items:center;gap:5px;font-size:11px;color:#2563EB;}
1359
+ .__vdt-conn-dot{width:6px;height:6px;border-radius:50%;background:#2563EB;}
1357
1360
 
1358
1361
  /* \u2500\u2500 Widget state colors \u2500\u2500 */
1359
1362
  .vdt-idle{border-color:#22D3EE !important;}
1360
- .vdt-compiling{border-color:#4ade80 !important;}
1363
+ .vdt-compiling{border-color:#38BDF8 !important;}
1361
1364
  .vdt-rendering{border-color:#fb923c !important;}
1362
1365
  .vdt-navigating{border-color:#60a5fa !important;}
1363
1366
  .vdt-error{border-color:#f87171 !important;}
1364
1367
  #__vdt-dot.idle{background:#22D3EE;}
1365
- #__vdt-dot.compiling{background:#4ade80;}
1368
+ #__vdt-dot.compiling{background:#38BDF8;}
1366
1369
  #__vdt-dot.rendering{background:#fb923c;}
1367
1370
  #__vdt-dot.navigating{background:#60a5fa;}
1368
1371
  #__vdt-dot.error{background:#f87171;}
1369
1372
  @keyframes __vdt-pulse{0%,100%{opacity:1;}50%{opacity:.4;}}
1370
- .vdt-compiling #__vdt-dot,.vdt-rendering #__vdt-dot,.vdt-navigating #__vdt-dot{animation:__vdt-pulse .9s ease-in-out infinite;}
1373
+ .vdt-compiling svg, .vdt-navigating svg{animation:__vdt-pulse .8s alternate infinite;}
1371
1374
  </style>
1372
1375
 
1373
1376
  <!-- \u2500\u2500 Floating button \u2500\u2500 -->
1374
1377
  <button id="__vdt-btn" class="vdt-idle" onclick="__vdtOpen()" title="Velix DevTools (${version})">
1375
1378
  ${ICON_LOGO}
1376
- <div id="__vdt-dot" class="idle"></div>
1379
+ <span id="__vdt-status-text"></span>
1377
1380
  </button>
1378
1381
 
1379
1382
  <!-- \u2500\u2500 Panel \u2500\u2500 -->
@@ -1524,7 +1527,7 @@ function generateDevToolsHtml(isDev, ctx = {}) {
1524
1527
  dot.className = v <= threshGood ? '__vdt-dot-good' : v <= threshOk ? '__vdt-dot-warn' : '__vdt-dot-bad';
1525
1528
  }
1526
1529
  if(el && typeof v === 'number'){
1527
- el.style.color = v <= threshGood ? '#10b981' : v <= threshOk ? '#f59e0b' : '#ef4444';
1530
+ el.style.color = v <= threshGood ? '#2563EB' : v <= threshOk ? '#f59e0b' : '#ef4444';
1528
1531
  }
1529
1532
  }
1530
1533
 
@@ -1574,13 +1577,18 @@ function generateDevToolsHtml(isDev, ctx = {}) {
1574
1577
 
1575
1578
  function setStatus(s){
1576
1579
  if(btn){ btn.className = 'vdt-' + s; }
1577
- if(dot){ dot.className = s; }
1580
+ var text = document.getElementById('__vdt-status-text');
1581
+ if(text) {
1582
+ if(s === 'compiling') text.textContent = 'Compiling...';
1583
+ else if(s === 'navigating') text.textContent = 'Loading...';
1584
+ else text.textContent = '';
1585
+ }
1578
1586
  }
1579
1587
 
1580
1588
  function setConn(ok){
1581
- if(connDot) connDot.style.background = ok ? '#10b981' : '#ef4444';
1589
+ if(connDot) connDot.style.background = ok ? '#2563EB' : '#ef4444';
1582
1590
  if(connText) connText.textContent = ok ? 'connected' : 'disconnected';
1583
- if(connText) connText.style.color = ok ? '#10b981' : '#ef4444';
1591
+ if(connText) connText.style.color = ok ? '#2563EB' : '#ef4444';
1584
1592
  }
1585
1593
 
1586
1594
  var ws = new WebSocket('ws://' + window.location.host + '/__velix_hmr');
@@ -1635,88 +1643,203 @@ function generate404Page(pathname = "/") {
1635
1643
  <head>
1636
1644
  <meta charset="UTF-8">
1637
1645
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1638
- <title>404 - Page Not Found | Velix v${VERSION}</title>
1646
+ <title>404 \u2014 Page Not Found | Velix v${VERSION}</title>
1639
1647
  <link rel="icon" href="/favicon.webp">
1648
+ <link rel="preconnect" href="https://fonts.googleapis.com">
1649
+ <link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
1640
1650
  <style>
1641
1651
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1652
+
1653
+ @keyframes fadeUp {
1654
+ from { opacity: 0; transform: translateY(16px); }
1655
+ to { opacity: 1; transform: translateY(0); }
1656
+ }
1657
+ @keyframes pulse {
1658
+ 0%, 100% { opacity: 1; }
1659
+ 50% { opacity: 0.4; }
1660
+ }
1661
+
1642
1662
  body {
1643
- background: #0a0a0a;
1644
- color: #ededed;
1645
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1663
+ background-color: #0B1120;
1664
+ background-image:
1665
+ linear-gradient(to right, rgba(255, 255, 255, 0.05) 1px, transparent 1px),
1666
+ linear-gradient(to bottom, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
1667
+ background-size: 60px 60px;
1668
+ color: #F8FAFC;
1669
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1646
1670
  display: flex;
1671
+ flex-direction: column;
1647
1672
  align-items: center;
1648
1673
  justify-content: center;
1649
1674
  min-height: 100vh;
1675
+ padding: 2rem;
1650
1676
  -webkit-font-smoothing: antialiased;
1651
1677
  }
1652
- .container {
1678
+
1679
+ /* radial vignette over the grid */
1680
+ body::before {
1681
+ content: '';
1682
+ position: fixed;
1683
+ inset: 0;
1684
+ background: radial-gradient(ellipse 80% 60% at 50% 50%, transparent 0%, #0B1120 80%);
1685
+ pointer-events: none;
1686
+ z-index: 0;
1687
+ }
1688
+
1689
+ .card {
1690
+ position: relative;
1691
+ z-index: 1;
1653
1692
  display: flex;
1693
+ flex-direction: column;
1654
1694
  align-items: center;
1655
- gap: 32px;
1656
- padding: 32px;
1695
+ text-align: center;
1696
+ max-width: 480px;
1697
+ animation: fadeUp 0.25s ease forwards;
1657
1698
  }
1658
- .code {
1659
- font-size: 48px;
1660
- font-weight: 700;
1699
+
1700
+ /* \u2500\u2500 glowing 404 number \u2500\u2500 */
1701
+ .status-number {
1702
+ font-family: 'DM Mono', monospace;
1703
+ font-size: clamp(80px, 18vw, 140px);
1704
+ font-weight: 500;
1661
1705
  line-height: 1;
1662
- color: #fff;
1663
- border-right: 1px solid rgba(255,255,255,0.15);
1664
- padding-right: 32px;
1665
- }
1666
- .body h1 {
1667
- font-size: 18px;
1668
- font-weight: 600;
1669
- color: #ededed;
1706
+ letter-spacing: -0.04em;
1707
+ color: #F8FAFC;
1708
+ position: relative;
1670
1709
  margin-bottom: 8px;
1671
1710
  }
1672
- .body p {
1673
- font-size: 14px;
1674
- color: #888;
1675
- margin-bottom: 20px;
1711
+ .status-number::after {
1712
+ content: attr(data-value);
1713
+ position: absolute;
1714
+ inset: 0;
1715
+ color: #2563EB;
1716
+ opacity: 0.12;
1717
+ filter: blur(24px);
1718
+ pointer-events: none;
1719
+ }
1720
+
1721
+ /* \u2500\u2500 accent line \u2500\u2500 */
1722
+ .accent-line {
1723
+ width: 40px;
1724
+ height: 3px;
1725
+ background: #2563EB;
1726
+ border-radius: 9999px;
1727
+ margin-bottom: 24px;
1676
1728
  }
1677
- .body p code {
1678
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1729
+
1730
+ /* \u2500\u2500 heading \u2500\u2500 */
1731
+ h1 {
1732
+ font-size: 22px;
1733
+ font-weight: 700;
1734
+ color: #fff;
1735
+ margin-bottom: 10px;
1736
+ letter-spacing: -0.01em;
1737
+ }
1738
+
1739
+ /* \u2500\u2500 route badge \u2500\u2500 */
1740
+ .route-badge {
1741
+ display: inline-flex;
1742
+ align-items: center;
1743
+ gap: 8px;
1744
+ font-family: 'DM Mono', monospace;
1679
1745
  font-size: 13px;
1680
- background: rgba(255,255,255,0.07);
1681
- padding: 2px 6px;
1682
- border-radius: 4px;
1683
- color: #ccc;
1746
+ color: #94A3B8;
1747
+ background: #0F172A;
1748
+ border: 1px solid #1E293B;
1749
+ padding: 5px 14px;
1750
+ border-radius: 9999px;
1751
+ margin-bottom: 32px;
1752
+ }
1753
+ .route-badge .dot {
1754
+ width: 6px;
1755
+ height: 6px;
1756
+ border-radius: 50%;
1757
+ background: #ff6b6b;
1758
+ animation: pulse 1.8s ease-in-out infinite;
1759
+ flex-shrink: 0;
1760
+ }
1761
+
1762
+ /* \u2500\u2500 actions \u2500\u2500 */
1763
+ .actions {
1764
+ display: flex;
1765
+ gap: 12px;
1684
1766
  }
1685
- .actions { display: flex; gap: 10px; }
1686
1767
  .btn {
1687
- display: inline-block;
1688
- padding: 8px 18px;
1689
- border-radius: 8px;
1768
+ display: inline-flex;
1769
+ align-items: center;
1770
+ gap: 6px;
1771
+ padding: 10px 22px;
1772
+ border-radius: 10px;
1690
1773
  font-size: 14px;
1691
- font-weight: 500;
1774
+ font-weight: 600;
1692
1775
  text-decoration: none;
1693
- transition: all .15s;
1694
- border: 1px solid transparent;
1776
+ transition: all 0.15s ease;
1695
1777
  }
1696
1778
  .btn-primary {
1697
- background: #fff;
1698
- color: #000;
1779
+ background: #2563EB;
1780
+ color: #0B1120;
1781
+ border: 1px solid #2563EB;
1782
+ }
1783
+ .btn-primary:hover {
1784
+ background: #3B82F6;
1785
+ transform: translateY(-1px);
1786
+ box-shadow: 0 0 20px rgba(37, 99, 235, 0.35);
1699
1787
  }
1700
- .btn-primary:hover { background: #e5e5e5; }
1701
1788
  .btn-secondary {
1702
- background: rgba(255,255,255,0.06);
1703
- color: #ededed;
1704
- border-color: rgba(255,255,255,0.1);
1789
+ background: transparent;
1790
+ color: #F8FAFC;
1791
+ border: 1px solid #1E293B;
1792
+ }
1793
+ .btn-secondary:hover {
1794
+ background: #0F172A;
1795
+ border-color: #334155;
1796
+ }
1797
+
1798
+ /* \u2500\u2500 footer branding \u2500\u2500 */
1799
+ .brand {
1800
+ position: fixed;
1801
+ bottom: 28px;
1802
+ left: 50%;
1803
+ transform: translateX(-50%);
1804
+ display: flex;
1805
+ align-items: center;
1806
+ gap: 8px;
1807
+ font-size: 12px;
1808
+ color: #475569;
1809
+ font-family: 'DM Mono', monospace;
1810
+ }
1811
+ .brand-dot {
1812
+ width: 6px;
1813
+ height: 6px;
1814
+ border-radius: 50%;
1815
+ background: #2563EB;
1705
1816
  }
1706
- .btn-secondary:hover { background: rgba(255,255,255,0.1); }
1707
1817
  </style>
1708
1818
  </head>
1709
1819
  <body>
1710
- <div class="container">
1711
- <div class="code">404</div>
1712
- <div class="body">
1713
- <h1>This page could not be found</h1>
1714
- <p>The path <code>${escapeHtml2(pathname)}</code> does not exist.</p>
1715
- <div class="actions">
1716
- <a href="/" class="btn btn-primary">Return Home</a>
1717
- <a href="javascript:history.back()" class="btn btn-secondary">Go Back</a>
1718
- </div>
1719
- </div>
1820
+ <div class="card">
1821
+ <div class="status-number" data-value="404">404</div>
1822
+ <div class="accent-line"></div>
1823
+ <h1>Page not found</h1>
1824
+ <div class="route-badge">
1825
+ <span class="dot"></span>
1826
+ <span>${escapeHtml2(pathname)}</span>
1827
+ </div>
1828
+ <div class="actions">
1829
+ <a href="/" class="btn btn-primary">
1830
+ <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>
1831
+ Home
1832
+ </a>
1833
+ <a href="javascript:history.back()" class="btn btn-secondary">
1834
+ <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>
1835
+ Go back
1836
+ </a>
1837
+ </div>
1838
+ </div>
1839
+
1840
+ <div class="brand">
1841
+ <span class="brand-dot"></span>
1842
+ Velix v${VERSION}
1720
1843
  </div>
1721
1844
  </body>
1722
1845
  </html>`;
@@ -1747,14 +1870,23 @@ function generate500Page(options) {
1747
1870
  const src = fs8.readFileSync(errorFile, "utf-8").split("\n");
1748
1871
  const start = Math.max(0, errorLine - 4);
1749
1872
  const end = Math.min(src.length, errorLine + 3);
1873
+ const syntaxHighlight = (code) => {
1874
+ let hl = code;
1875
+ 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>');
1876
+ hl = hl.replace(/(&quot;.*?&quot;|&#39;.*?&#39;|`.*?`)/g, '<span style="color:#86efac">$1</span>');
1877
+ hl = hl.replace(/(\\&#x2F;\\&#x2F;.*)$/g, '<span style="color:#94A3B8">$1</span>');
1878
+ hl = hl.replace(/\\b(\\d+)\\b/g, '<span style="color:#f59e0b">$1</span>');
1879
+ return hl;
1880
+ };
1750
1881
  sourceSnippet = src.slice(start, end).map((l, i) => {
1751
1882
  const num = start + i + 1;
1752
1883
  const isErr = num === errorLine;
1753
1884
  const numStr = String(num).padStart(3, " ");
1885
+ const highlightedLine = syntaxHighlight(escapeHtml2(l));
1754
1886
  if (isErr) {
1755
- return `<div class="src-line src-err"><span class="ln">${numStr}</span>${escapeHtml2(l)}</div>`;
1887
+ 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>`;
1756
1888
  }
1757
- return `<div class="src-line"><span class="ln">${numStr}</span>${escapeHtml2(l)}</div>`;
1889
+ return `<div class="src-line"><span class="ln">${numStr}</span><span class="active-arrow"> </span><span class="code-content">${highlightedLine}</span></div>`;
1758
1890
  }).join("");
1759
1891
  }
1760
1892
  } catch (_) {
@@ -1762,6 +1894,13 @@ function generate500Page(options) {
1762
1894
  }
1763
1895
  }
1764
1896
  }
1897
+ const formatErrorMessage = (msg) => {
1898
+ let formatted = escapeHtml2(msg);
1899
+ const pathSpan = `<span style="color:#2563EB;cursor:pointer;" onclick="console.log('open in editor')">`;
1900
+ formatted = formatted.replace(/([A-Z]:\\[^\s<]+|\/(?:Users|home|var|srv)\/[^\s<]+)/g, pathSpan + "$1</span>");
1901
+ formatted = formatted.replace(/(ERROR:)/g, '<strong style="color:#ff6b6b">$1</strong>');
1902
+ return formatted;
1903
+ };
1765
1904
  const userFrames = frames.filter((f) => f.file && !f.file.includes("node_modules"));
1766
1905
  const ignoredFrames = frames.filter((f) => !f.file || f.file.includes("node_modules"));
1767
1906
  const shortFile = errorFile.replace(/\\/g, "/").replace(/.*\/(?=app\/|server\/|src\/|pages\/)/, "");
@@ -1776,7 +1915,7 @@ function generate500Page(options) {
1776
1915
  <style>
1777
1916
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1778
1917
  body {
1779
- background: #0a0a0a;
1918
+ background: #0B1120;
1780
1919
  color: #ededed;
1781
1920
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1782
1921
  display: flex; align-items: center; justify-content: center;
@@ -1811,327 +1950,459 @@ function generate500Page(options) {
1811
1950
  <title>Error | Velix v${VERSION}</title>
1812
1951
  <link rel="icon" href="/favicon.webp">
1813
1952
  <style>
1953
+ @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');
1954
+
1814
1955
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1815
1956
 
1957
+ @keyframes fadeIn {
1958
+ from { opacity: 0; transform: translateY(10px); }
1959
+ to { opacity: 1; transform: translateY(0); }
1960
+ }
1961
+
1816
1962
  body {
1817
- background: rgba(0, 0, 0, 0.85);
1818
- color: #ededed;
1819
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1963
+ background-color: #0B1120;
1964
+ background-image:
1965
+ linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),
1966
+ linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
1967
+ background-size: 60px 60px;
1968
+ color: #F8FAFC;
1969
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1820
1970
  min-height: 100vh;
1821
1971
  display: flex;
1822
- align-items: center;
1972
+ align-items: flex-start;
1823
1973
  justify-content: center;
1824
- padding: 24px;
1974
+ padding: 2rem;
1825
1975
  -webkit-font-smoothing: antialiased;
1826
1976
  }
1827
1977
 
1828
- /* \u2500\u2500 Overlay Card \u2500\u2500 */
1829
- .overlay {
1978
+ /* Radial mask for the grid background */
1979
+ .bg-mask {
1980
+ position: fixed;
1981
+ top: 0; left: 0; right: 0; bottom: 0;
1982
+ background: radial-gradient(circle at center, transparent 0%, #0B1120 80%);
1983
+ z-index: -1;
1984
+ pointer-events: none;
1985
+ }
1986
+
1987
+ /* \u2500\u2500 Main Container \u2500\u2500 */
1988
+ .container {
1830
1989
  width: 100%;
1831
- max-width: 940px;
1832
- background: #111;
1833
- border: 1px solid #2a2a2a;
1834
- border-radius: 12px;
1835
- box-shadow: 0 24px 64px rgba(0,0,0,0.6);
1836
- overflow: hidden;
1990
+ max-width: 780px;
1991
+ margin-top: 4vh;
1992
+ animation: fadeIn 0.2s ease forwards;
1837
1993
  display: flex;
1838
1994
  flex-direction: column;
1839
- max-height: calc(100vh - 48px);
1995
+ gap: 24px;
1840
1996
  }
1841
1997
 
1842
1998
  /* \u2500\u2500 Header Bar \u2500\u2500 */
1843
1999
  .header {
2000
+ position: relative;
2001
+ background: transparent;
1844
2002
  display: flex;
1845
2003
  align-items: center;
1846
2004
  justify-content: space-between;
1847
- padding: 12px 20px;
1848
- background: #161616;
1849
- border-bottom: 1px solid #2a2a2a;
1850
- gap: 12px;
1851
- flex-shrink: 0;
2005
+ padding-top: 12px;
2006
+ }
2007
+ .header::before {
2008
+ content: '';
2009
+ position: absolute;
2010
+ top: -12px; left: 0; right: 0;
2011
+ height: 4px;
2012
+ background: #ff6b6b;
2013
+ border-radius: 4px;
1852
2014
  }
1853
2015
  .header-left {
1854
2016
  display: flex;
1855
2017
  align-items: center;
1856
- gap: 8px;
1857
- font-size: 13px;
1858
- color: #888;
2018
+ gap: 12px;
1859
2019
  }
1860
- .counter-badge {
1861
- background: #1e1e1e;
1862
- border: 1px solid #333;
1863
- border-radius: 6px;
1864
- padding: 2px 8px;
2020
+ .header-icon {
2021
+ width: 20px;
2022
+ height: 20px;
2023
+ fill: #ff6b6b;
2024
+ }
2025
+ .header-title {
2026
+ font-size: 18px;
2027
+ font-weight: 700;
2028
+ color: #fff;
2029
+ }
2030
+ .dev-badge {
2031
+ background: rgba(255, 107, 107, 0.1);
2032
+ border: 1px solid rgba(255, 107, 107, 0.2);
2033
+ color: #ff6b6b;
2034
+ padding: 2px 10px;
2035
+ border-radius: 9999px;
1865
2036
  font-size: 12px;
1866
- color: #999;
1867
- font-variant-numeric: tabular-nums;
2037
+ font-weight: 600;
2038
+ letter-spacing: 0.05em;
1868
2039
  }
1869
- .nav-btn {
1870
- background: #1e1e1e;
1871
- border: 1px solid #333;
1872
- border-radius: 6px;
1873
- color: #888;
1874
- cursor: pointer;
1875
- width: 24px;
1876
- height: 24px;
2040
+
2041
+ /* \u2500\u2500 Status & Route \u2500\u2500 */
2042
+ .status-bar {
1877
2043
  display: flex;
1878
2044
  align-items: center;
1879
- justify-content: center;
2045
+ gap: 12px;
2046
+ margin-top: 8px;
2047
+ font-family: 'DM Mono', monospace;
1880
2048
  font-size: 13px;
1881
- transition: all .15s;
1882
2049
  }
1883
- .nav-btn:hover:not(:disabled) { background: #2a2a2a; color: #fff; }
1884
- .nav-btn:disabled { opacity: 0.3; cursor: default; }
1885
- .framework-tag {
1886
- display: flex;
1887
- align-items: center;
1888
- gap: 6px;
1889
- font-size: 12px;
1890
- color: #666;
2050
+ .status-badge {
2051
+ background: #0F172A;
2052
+ border: 1px solid #1E293B;
2053
+ color: #ff6b6b;
2054
+ padding: 4px 10px;
2055
+ border-radius: 6px;
1891
2056
  }
1892
- .framework-tag::before {
1893
- content: '';
1894
- display: inline-block;
1895
- width: 6px; height: 6px;
1896
- border-radius: 50%;
1897
- background: #3b82f6;
2057
+ .route-badge {
2058
+ background: #0F172A;
2059
+ border: 1px solid #1E293B;
2060
+ color: #2563EB;
2061
+ padding: 4px 10px;
2062
+ border-radius: 6px;
1898
2063
  }
1899
-
1900
- /* \u2500\u2500 Scrollable Content \u2500\u2500 */
1901
- .content {
1902
- overflow-y: auto;
1903
- flex: 1;
2064
+ .separator { color: #94A3B8; }
2065
+ .timestamp { color: #94A3B8; font-family: system-ui; font-size: 13px; }
2066
+
2067
+ /* \u2500\u2500 Error Message \u2500\u2500 */
2068
+ .error-message-box {
2069
+ background: #0F172A;
2070
+ border: 1px solid #1E293B;
2071
+ border-left: 3px solid #ff6b6b;
2072
+ border-radius: 10px;
2073
+ padding: 1rem 1.25rem;
2074
+ font-family: 'DM Mono', monospace;
2075
+ font-size: 14px;
2076
+ color: #F8FAFC;
2077
+ line-height: 1.6;
2078
+ white-space: pre-wrap;
2079
+ word-break: break-word;
1904
2080
  }
1905
2081
 
1906
- /* \u2500\u2500 Error Summary \u2500\u2500 */
1907
- .error-summary {
1908
- padding: 28px 24px 20px;
1909
- border-bottom: 1px solid #1e1e1e;
2082
+ /* \u2500\u2500 Source Code Viewer \u2500\u2500 */
2083
+ .source-viewer {
2084
+ display: flex;
2085
+ flex-direction: column;
2086
+ gap: 12px;
1910
2087
  }
1911
- .error-label {
1912
- display: inline-flex;
2088
+ .source-header {
2089
+ display: flex;
1913
2090
  align-items: center;
1914
- gap: 6px;
1915
- font-size: 11px;
1916
- font-weight: 700;
1917
- letter-spacing: 0.08em;
1918
- text-transform: uppercase;
1919
- color: #f87171;
1920
- background: rgba(248, 113, 113, 0.1);
1921
- border: 1px solid rgba(248, 113, 113, 0.25);
1922
- padding: 3px 8px;
1923
- border-radius: 4px;
1924
- margin-bottom: 14px;
1925
- }
1926
- .error-label svg { width: 10px; height: 10px; fill: currentColor; }
1927
- .error-message {
1928
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1929
- font-size: 18px;
1930
- font-weight: 600;
1931
- line-height: 1.5;
1932
- color: #fff;
1933
- word-break: break-word;
2091
+ justify-content: space-between;
1934
2092
  }
1935
- .error-location {
1936
- margin-top: 10px;
2093
+ .source-filepath {
2094
+ font-family: 'DM Mono', monospace;
1937
2095
  font-size: 13px;
1938
- color: #888;
1939
- }
1940
- .error-location strong { color: #ccc; }
1941
-
1942
- /* \u2500\u2500 Source Block \u2500\u2500 */
1943
- .source-block {
1944
- border-bottom: 1px solid #1e1e1e;
2096
+ color: #2563EB;
1945
2097
  }
1946
- .source-topbar {
2098
+ .open-in-editor {
2099
+ color: #94A3B8;
2100
+ cursor: pointer;
2101
+ transition: color 0.15s;
1947
2102
  display: flex;
1948
2103
  align-items: center;
1949
- padding: 10px 16px;
1950
- background: #161616;
1951
- border-bottom: 1px solid #1e1e1e;
1952
- gap: 8px;
1953
- font-size: 12px;
1954
- color: #666;
2104
+ gap: 6px;
2105
+ font-size: 13px;
2106
+ text-decoration: none;
1955
2107
  }
1956
- .source-topbar .filepath { color: #aaa; font-family: ui-monospace, monospace; }
1957
- .source-code {
1958
- background: #0d0d0d;
2108
+ .open-in-editor:hover { color: #F8FAFC; }
2109
+ .open-in-editor svg { width: 14px; height: 14px; stroke: currentColor; fill: none; }
2110
+
2111
+ .source-body {
2112
+ background: #020617;
2113
+ border: 1px solid #1E293B;
2114
+ border-radius: 8px;
2115
+ padding: 16px 0;
1959
2116
  overflow-x: auto;
1960
- padding: 10px 0;
1961
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2117
+ font-family: 'DM Mono', monospace;
1962
2118
  font-size: 13px;
1963
- line-height: 1.7;
2119
+ line-height: 1.6;
1964
2120
  }
1965
2121
  .src-line {
1966
2122
  display: flex;
1967
- align-items: flex-start;
1968
- padding: 0 20px;
1969
- white-space: pre;
1970
- color: #555;
2123
+ padding: 0 16px;
1971
2124
  }
1972
- .src-line .ln {
1973
- color: #3a3a3a;
2125
+ .ln {
2126
+ color: #475569;
2127
+ width: 32px;
1974
2128
  text-align: right;
1975
- width: 36px;
1976
2129
  margin-right: 16px;
1977
- flex-shrink: 0;
1978
2130
  user-select: none;
2131
+ flex-shrink: 0;
1979
2132
  }
1980
- .src-err {
1981
- background: rgba(239, 68, 68, 0.08);
1982
- color: #fca5a5;
1983
- position: relative;
2133
+ .active-arrow {
2134
+ color: transparent;
2135
+ margin-right: 12px;
2136
+ width: 14px;
2137
+ text-align: center;
2138
+ flex-shrink: 0;
1984
2139
  }
1985
- .src-err .ln { color: #ef4444; }
1986
- .src-err::before {
1987
- content: '';
1988
- position: absolute;
1989
- left: 0;
1990
- top: 0; bottom: 0;
1991
- width: 3px;
1992
- background: #ef4444;
2140
+ .code-content { white-space: pre; }
2141
+
2142
+ .src-err {
2143
+ background: rgba(37, 99, 235, 0.04);
1993
2144
  }
2145
+ .src-err .ln { color: #2563EB; }
2146
+ .src-err .active-arrow { color: #2563EB; }
2147
+ .src-err .code-content { color: #F8FAFC; }
1994
2148
 
1995
2149
  /* \u2500\u2500 Call Stack \u2500\u2500 */
1996
- .callstack {
1997
- padding: 20px 24px;
2150
+ .callstack-section {
2151
+ margin-top: 16px;
2152
+ display: flex;
2153
+ flex-direction: column;
2154
+ gap: 12px;
1998
2155
  }
1999
2156
  .callstack-header {
2000
2157
  display: flex;
2001
2158
  align-items: center;
2002
2159
  justify-content: space-between;
2003
- margin-bottom: 14px;
2004
2160
  }
2005
2161
  .callstack-title {
2006
- font-size: 13px;
2162
+ font-size: 16px;
2007
2163
  font-weight: 600;
2008
- color: #aaa;
2009
- text-transform: uppercase;
2010
- letter-spacing: 0.06em;
2164
+ display: flex;
2165
+ align-items: center;
2166
+ gap: 12px;
2011
2167
  }
2012
- .ignore-btn {
2168
+ .callstack-count {
2169
+ background: #1E293B;
2170
+ color: #F8FAFC;
2171
+ padding: 2px 10px;
2172
+ border-radius: 9999px;
2013
2173
  font-size: 12px;
2014
- color: #555;
2174
+ font-weight: 500;
2175
+ }
2176
+
2177
+ .toggle-node-modules {
2178
+ color: #94A3B8;
2179
+ font-size: 13px;
2015
2180
  background: none;
2016
2181
  border: none;
2017
2182
  cursor: pointer;
2183
+ transition: color 0.15s;
2184
+ }
2185
+ .toggle-node-modules:hover { color: #F8FAFC; }
2186
+
2187
+ .frame {
2188
+ background: #0F172A;
2189
+ border: 1px solid #1E293B;
2190
+ border-radius: 8px;
2191
+ padding: 0.875rem 1rem;
2018
2192
  display: flex;
2193
+ justify-content: space-between;
2019
2194
  align-items: center;
2195
+ transition: background-color 0.15s, transform 0.15s;
2196
+ }
2197
+ .frame:hover { background: #1E293B; }
2198
+
2199
+ .frame-info {
2200
+ display: flex;
2201
+ flex-direction: column;
2020
2202
  gap: 4px;
2021
- transition: color .15s;
2022
2203
  }
2023
- .ignore-btn:hover { color: #aaa; }
2024
- .ignore-caret { transition: transform .2s; }
2025
- .ignore-btn.open .ignore-caret { transform: rotate(90deg); }
2204
+ .frame-call {
2205
+ font-family: 'DM Mono', monospace;
2206
+ font-size: 14px;
2207
+ font-weight: 700;
2208
+ color: #2563EB;
2209
+ }
2210
+ .frame-file {
2211
+ font-family: 'DM Mono', monospace;
2212
+ font-size: 13px;
2213
+ color: #94A3B8;
2214
+ }
2215
+
2216
+ .frame.node-modules .frame-call { color: #94A3B8; font-weight: 400; }
2217
+ .frame.node-modules .frame-file { color: #475569; }
2218
+
2219
+ .frame-number {
2220
+ background: #1E293B;
2221
+ color: #94A3B8;
2222
+ padding: 2px 8px;
2223
+ border-radius: 6px;
2224
+ font-size: 12px;
2225
+ font-variant-numeric: tabular-nums;
2226
+ }
2026
2227
 
2027
- .frame {
2028
- padding: 10px 12px;
2228
+ .pagination {
2229
+ display: flex;
2230
+ justify-content: flex-end;
2231
+ gap: 8px;
2232
+ margin-top: 8px;
2233
+ }
2234
+ .page-btn {
2235
+ background: transparent;
2236
+ border: 1px solid #1E293B;
2237
+ color: #F8FAFC;
2238
+ padding: 6px 14px;
2029
2239
  border-radius: 6px;
2030
- margin-bottom: 4px;
2031
2240
  font-size: 13px;
2032
- cursor: default;
2241
+ cursor: pointer;
2242
+ transition: all 0.15s;
2033
2243
  }
2034
- .frame:hover { background: rgba(255,255,255,0.03); }
2035
- .frame-call {
2036
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2244
+ .page-btn:hover:not(:disabled) { background: #1E293B; }
2245
+ .page-btn:disabled { opacity: 0.3; cursor: default; }
2246
+
2247
+ .node-modules-list { display: none; flex-direction: column; gap: 12px; margin-top: 12px; }
2248
+ .node-modules-list.visible { display: flex; }
2249
+
2250
+ /* \u2500\u2500 Footer \u2500\u2500 */
2251
+ .footer {
2252
+ margin-top: 32px;
2253
+ padding-top: 24px;
2254
+ border-top: 1px solid #1E293B;
2255
+ display: flex;
2256
+ justify-content: space-between;
2257
+ align-items: center;
2258
+ }
2259
+ .footer-left {
2260
+ display: flex;
2261
+ align-items: center;
2262
+ gap: 8px;
2263
+ font-size: 13px;
2037
2264
  font-weight: 600;
2038
- color: #e2e8f0;
2039
- margin-bottom: 3px;
2265
+ color: #F8FAFC;
2266
+ }
2267
+ .velix-dot { width: 8px; height: 8px; border-radius: 50%; background: #2563EB; }
2268
+ .footer-right {
2269
+ display: flex;
2270
+ align-items: center;
2271
+ gap: 16px;
2040
2272
  }
2041
- .frame-loc {
2042
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2273
+ .footer-link {
2274
+ color: #94A3B8;
2275
+ text-decoration: none;
2276
+ font-size: 13px;
2277
+ transition: color 0.15s;
2278
+ }
2279
+ .footer-link:hover { color: #F8FAFC; }
2280
+ .dev-pill {
2281
+ background: #1E293B;
2282
+ color: #F8FAFC;
2283
+ padding: 4px 12px;
2284
+ border-radius: 9999px;
2043
2285
  font-size: 12px;
2044
- color: #555;
2045
2286
  }
2046
- .frame.ignored .frame-call { color: #444; }
2047
-
2048
- .ignored-section { margin-top: 8px; display: none; }
2049
- .ignored-section.open { display: block; }
2050
2287
  </style>
2051
2288
  </head>
2052
2289
  <body>
2053
- <div class="overlay">
2054
-
2055
- <!-- Header bar -->
2290
+ <div class="bg-mask"></div>
2291
+ <div class="container">
2292
+
2293
+ <!-- Header -->
2056
2294
  <div class="header">
2057
2295
  <div class="header-left">
2058
- <button class="nav-btn" disabled>\u2039</button>
2059
- <span class="counter-badge">1 / 1</span>
2060
- <button class="nav-btn" disabled>\u203A</button>
2061
- <span style="margin-left: 4px;">Unhandled Runtime Error</span>
2296
+ <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>
2297
+ <div class="header-title">Unhandled Runtime Error</div>
2062
2298
  </div>
2063
- <div class="framework-tag">Velix v${VERSION} \xB7 esbuild</div>
2299
+ <div class="dev-badge">DEV</div>
2064
2300
  </div>
2065
2301
 
2066
- <!-- Scrollable content -->
2067
- <div class="content">
2302
+ <!-- Status & Route -->
2303
+ <div class="status-bar">
2304
+ <div class="status-badge">${options.statusCode || 500}</div>
2305
+ <div class="separator">\xB7</div>
2306
+ <div class="route-badge">Route: ${pathname || "/"}</div>
2307
+ <div class="separator">\xB7</div>
2308
+ <div class="timestamp">just now</div>
2309
+ </div>
2068
2310
 
2069
- <!-- Error summary -->
2070
- <div class="error-summary">
2071
- <div class="error-label">
2072
- <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>
2073
- Runtime Error
2074
- </div>
2075
- <div class="error-message">${escapeHtml2(message)}</div>
2076
- ${pathname ? `<div class="error-location">in <strong>${escapeHtml2(pathname)}</strong></div>` : ""}
2077
- </div>
2311
+ <!-- Error Message -->
2312
+ <div class="error-message-box">
2313
+ ${formatErrorMessage(message)}
2314
+ </div>
2078
2315
 
2079
- ${sourceSnippet ? `
2080
- <!-- Source code snippet -->
2081
- <div class="source-block">
2082
- <div class="source-topbar">
2083
- <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>
2084
- <span class="filepath">${escapeHtml2(shortFile)}${errorLine ? `:${errorLine}` : ""}</span>
2085
- </div>
2086
- <div class="source-code">${sourceSnippet}</div>
2316
+ ${sourceSnippet ? `
2317
+ <!-- Source Viewer -->
2318
+ <div class="source-viewer">
2319
+ <div class="source-header">
2320
+ <div class="source-filepath">${escapeHtml2(shortFile)}${errorLine ? ` (${errorLine})` : ""} @ runtime</div>
2321
+ <a href="#" class="open-in-editor" onclick="console.log('Open in editor clicked')">
2322
+ Open in editor
2323
+ <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>
2324
+ </a>
2087
2325
  </div>
2088
- ` : ""}
2089
-
2090
- <!-- Call stack -->
2091
- <div class="callstack">
2092
- <div class="callstack-header">
2093
- <div class="callstack-title">Call Stack</div>
2094
- ${ignoredFrames.length > 0 ? `
2095
- <button class="ignore-btn" id="ignoreBtn" onclick="toggleIgnored()">
2096
- <svg class="ignore-caret" width="12" height="12" viewBox="0 0 12 12" fill="none">
2097
- <path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
2098
- </svg>
2099
- ${ignoredFrames.length} ignored frames
2100
- </button>
2101
- ` : ""}
2326
+ <div class="source-body">
2327
+ ${sourceSnippet}
2328
+ </div>
2329
+ </div>
2330
+ ` : ""}
2331
+
2332
+ <!-- Call Stack -->
2333
+ <div class="callstack-section">
2334
+ <div class="callstack-header">
2335
+ <div class="callstack-title">
2336
+ Call Stack
2337
+ <div class="callstack-count">${frames.length}</div>
2102
2338
  </div>
2339
+ ${ignoredFrames.length > 0 ? `
2340
+ <button class="toggle-node-modules" id="toggleNodeModules" onclick="toggleNodeModules()">
2341
+ Show ${ignoredFrames.length} node_modules frames
2342
+ </button>
2343
+ ` : ""}
2344
+ </div>
2103
2345
 
2104
- <!-- User frames -->
2105
- ${userFrames.map((f) => `
2106
- <div class="frame">
2346
+ ${userFrames.map((f, i) => `
2347
+ <div class="frame">
2348
+ <div class="frame-info">
2107
2349
  <div class="frame-call">${escapeHtml2(f.call)}</div>
2108
- <div class="frame-loc">${escapeHtml2(f.file.replace(/\\/g, "/").replace(/.*\/(?=app\/|server\/|src\/|pages\/)/, ""))}${f.line ? `:${f.line}` : ""}</div>
2350
+ <div class="frame-file">${escapeHtml2(f.file.replace(/\\\\/g, "/"))}${f.line ? `:${f.line}` : ""}</div>
2109
2351
  </div>
2110
- `).join("")}
2352
+ <div class="frame-number">${i + 1}</div>
2353
+ </div>
2354
+ `).join("")}
2111
2355
 
2112
- <!-- Ignored frames -->
2113
- ${ignoredFrames.length > 0 ? `
2114
- <div class="ignored-section" id="ignoredSection">
2115
- ${ignoredFrames.map((f) => `
2116
- <div class="frame ignored">
2356
+ ${ignoredFrames.length > 0 ? `
2357
+ <div class="node-modules-list" id="nodeModulesList">
2358
+ ${ignoredFrames.map((f, i) => `
2359
+ <div class="frame node-modules">
2360
+ <div class="frame-info">
2117
2361
  <div class="frame-call">${escapeHtml2(f.call)}</div>
2118
- <div class="frame-loc">${escapeHtml2(f.file)}${f.line ? `:${f.line}` : ""}</div>
2362
+ <div class="frame-file">${escapeHtml2(f.file)}${f.line ? `:${f.line}` : ""}</div>
2119
2363
  </div>
2120
- `).join("")}
2364
+ <div class="frame-number">${userFrames.length + i + 1}</div>
2121
2365
  </div>
2122
- ` : ""}
2366
+ `).join("")}
2367
+ </div>
2368
+ ` : ""}
2369
+
2370
+ <div class="pagination">
2371
+ <button class="page-btn" disabled>Previous</button>
2372
+ <button class="page-btn" disabled>Next</button>
2373
+ </div>
2374
+ </div>
2375
+
2376
+ <!-- Footer -->
2377
+ <div class="footer">
2378
+ <div class="footer-left">
2379
+ <div class="velix-dot"></div>
2380
+ Velix v${VERSION}
2123
2381
  </div>
2382
+ <div class="footer-right">
2383
+ <a href="/" class="footer-link">Home</a>
2384
+ <a href="#" class="footer-link" onclick="window.location.reload()">Reload</a>
2385
+ <a href="https://velixcloud.io/docs" class="footer-link" target="_blank">Documentation</a>
2386
+ <div class="dev-pill">Development Mode</div>
2387
+ </div>
2388
+ </div>
2124
2389
 
2125
- </div><!-- /content -->
2126
- </div><!-- /overlay -->
2390
+ </div>
2127
2391
 
2128
2392
  <script>
2129
- function toggleIgnored() {
2130
- const btn = document.getElementById('ignoreBtn');
2131
- const sec = document.getElementById('ignoredSection');
2132
- if (!btn || !sec) return;
2133
- const open = sec.classList.toggle('open');
2134
- btn.classList.toggle('open', open);
2393
+ function toggleNodeModules() {
2394
+ const list = document.getElementById('nodeModulesList');
2395
+ const btn = document.getElementById('toggleNodeModules');
2396
+ if (!list || !btn) return;
2397
+
2398
+ const isVisible = list.classList.contains('visible');
2399
+ if (isVisible) {
2400
+ list.classList.remove('visible');
2401
+ btn.textContent = 'Show ' + list.children.length + ' node_modules frames';
2402
+ } else {
2403
+ list.classList.add('visible');
2404
+ btn.textContent = 'Hide node_modules frames';
2405
+ }
2135
2406
  }
2136
2407
  </script>
2137
2408
  </body>