@teamvelix/velix 5.2.6 → 5.2.8

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,5 +1,11 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
3
9
  var __esm = (fn, res) => function __init() {
4
10
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
11
  };
@@ -701,7 +707,7 @@ init_esm_shims();
701
707
 
702
708
  // version.ts
703
709
  init_esm_shims();
704
- var VERSION = "5.2.3";
710
+ var VERSION = "5.2.8";
705
711
 
706
712
  // logger.ts
707
713
  var colors = {
@@ -1620,511 +1626,514 @@ function generateDevToolsHtml(isDev, ctx = {}) {
1620
1626
 
1621
1627
  // server/error-pages.ts
1622
1628
  init_esm_shims();
1629
+ function escapeHtml2(str) {
1630
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1631
+ }
1623
1632
  function generate404Page(pathname = "/") {
1624
1633
  return `<!DOCTYPE html>
1625
1634
  <html lang="en">
1626
1635
  <head>
1627
1636
  <meta charset="UTF-8">
1628
1637
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1629
- <title>404 - Page Not Found | Velix v5</title>
1638
+ <title>404 - Page Not Found | Velix v${VERSION}</title>
1630
1639
  <link rel="icon" href="/favicon.webp">
1631
1640
  <style>
1632
- * { margin: 0; padding: 0; box-sizing: border-box; }
1633
- body {
1634
- margin: 0;
1635
- background: linear-gradient(135deg, #0F172A 0%, #1E293B 100%);
1636
- color: white;
1637
- font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1638
- display: flex;
1639
- align-items: center;
1640
- justify-content: center;
1641
- min-height: 100vh;
1642
- text-align: center;
1643
- overflow: hidden;
1644
- }
1645
- .container {
1646
- max-width: 700px;
1647
- padding: 60px 40px;
1648
- position: relative;
1649
- z-index: 10;
1650
- }
1651
- .bg-pattern {
1652
- position: absolute;
1653
- inset: 0;
1654
- background-image: radial-gradient(circle at 20% 50%, rgba(34, 211, 238, 0.1) 0%, transparent 50%),
1655
- radial-gradient(circle at 80% 80%, rgba(37, 99, 235, 0.1) 0%, transparent 50%);
1656
- z-index: 1;
1657
- }
1658
- h1 {
1659
- font-size: 180px;
1660
- font-weight: 900;
1661
- margin: 0;
1662
- background: linear-gradient(135deg, #22D3EE 0%, #2563EB 100%);
1663
- -webkit-background-clip: text;
1664
- -webkit-text-fill-color: transparent;
1665
- line-height: 1;
1666
- letter-spacing: -0.05em;
1667
- animation: fadeInUp 0.6s ease-out;
1668
- }
1669
- h2 {
1670
- font-size: 36px;
1671
- font-weight: 800;
1672
- margin: 30px 0 15px;
1673
- color: #F8FAFC;
1674
- animation: fadeInUp 0.6s ease-out 0.1s backwards;
1641
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1642
+ body {
1643
+ background: #0a0a0a;
1644
+ color: #ededed;
1645
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1646
+ display: flex;
1647
+ align-items: center;
1648
+ justify-content: center;
1649
+ min-height: 100vh;
1650
+ -webkit-font-smoothing: antialiased;
1675
1651
  }
1676
- p {
1677
- color: #94A3B8;
1678
- font-size: 18px;
1679
- line-height: 1.7;
1680
- margin-bottom: 40px;
1681
- animation: fadeInUp 0.6s ease-out 0.2s backwards;
1652
+ .container {
1653
+ display: flex;
1654
+ align-items: center;
1655
+ gap: 32px;
1656
+ padding: 32px;
1682
1657
  }
1683
- code {
1684
- background: rgba(255,255,255,0.1);
1685
- padding: 4px 10px;
1686
- border-radius: 6px;
1687
- font-family: 'Fira Code', 'Courier New', monospace;
1688
- color: #22D3EE;
1689
- font-size: 16px;
1690
- border: 1px solid rgba(34, 211, 238, 0.2);
1658
+ .code {
1659
+ font-size: 48px;
1660
+ font-weight: 700;
1661
+ line-height: 1;
1662
+ color: #fff;
1663
+ border-right: 1px solid rgba(255,255,255,0.15);
1664
+ padding-right: 32px;
1691
1665
  }
1692
- .btn-group {
1693
- display: flex;
1694
- gap: 16px;
1695
- justify-content: center;
1696
- flex-wrap: wrap;
1697
- animation: fadeInUp 0.6s ease-out 0.3s backwards;
1666
+ .body h1 {
1667
+ font-size: 18px;
1668
+ font-weight: 600;
1669
+ color: #ededed;
1670
+ margin-bottom: 8px;
1698
1671
  }
1699
- .btn {
1700
- display: inline-block;
1701
- background: linear-gradient(135deg, #2563EB 0%, #1D4ED8 100%);
1702
- color: white;
1703
- text-decoration: none;
1704
- padding: 14px 36px;
1705
- border-radius: 12px;
1706
- font-weight: 600;
1707
- font-size: 16px;
1708
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1709
- box-shadow: 0 10px 30px rgba(37, 99, 235, 0.3);
1710
- border: none;
1711
- cursor: pointer;
1672
+ .body p {
1673
+ font-size: 14px;
1674
+ color: #888;
1675
+ margin-bottom: 20px;
1712
1676
  }
1713
- .btn:hover {
1714
- transform: translateY(-2px);
1715
- box-shadow: 0 15px 40px rgba(37, 99, 235, 0.4);
1677
+ .body p code {
1678
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1679
+ font-size: 13px;
1680
+ background: rgba(255,255,255,0.07);
1681
+ padding: 2px 6px;
1682
+ border-radius: 4px;
1683
+ color: #ccc;
1716
1684
  }
1717
- .btn-outline {
1718
- background: transparent;
1719
- color: #22D3EE;
1720
- border: 2px solid #22D3EE;
1721
- box-shadow: none;
1685
+ .actions { display: flex; gap: 10px; }
1686
+ .btn {
1687
+ display: inline-block;
1688
+ padding: 8px 18px;
1689
+ border-radius: 8px;
1690
+ font-size: 14px;
1691
+ font-weight: 500;
1692
+ text-decoration: none;
1693
+ transition: all .15s;
1694
+ border: 1px solid transparent;
1722
1695
  }
1723
- .btn-outline:hover {
1724
- background: rgba(34, 211, 238, 0.1);
1725
- box-shadow: 0 10px 30px rgba(34, 211, 238, 0.2);
1696
+ .btn-primary {
1697
+ background: #fff;
1698
+ color: #000;
1726
1699
  }
1727
- @keyframes fadeInUp {
1728
- from {
1729
- opacity: 0;
1730
- transform: translateY(30px);
1731
- }
1732
- to {
1733
- opacity: 1;
1734
- transform: translateY(0);
1735
- }
1700
+ .btn-primary:hover { background: #e5e5e5; }
1701
+ .btn-secondary {
1702
+ background: rgba(255,255,255,0.06);
1703
+ color: #ededed;
1704
+ border-color: rgba(255,255,255,0.1);
1736
1705
  }
1706
+ .btn-secondary:hover { background: rgba(255,255,255,0.1); }
1737
1707
  </style>
1738
1708
  </head>
1739
1709
  <body>
1740
- <div class="bg-pattern"></div>
1741
1710
  <div class="container">
1742
- <h1>404</h1>
1743
- <h2>Page Not Found</h2>
1744
- <p>The page <code>${pathname}</code> could not be found.<br>It may have been moved or deleted.</p>
1745
- <div class="btn-group">
1746
- <a href="/" class="btn">Return Home</a>
1747
- <a href="javascript:history.back()" class="btn btn-outline">Go Back</a>
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>
1748
1719
  </div>
1749
1720
  </div>
1750
1721
  </body>
1751
1722
  </html>`;
1752
1723
  }
1753
1724
  function generate500Page(options) {
1754
- const { title, message, stack, isDev, pathname } = options;
1755
- let callStackCards = "";
1756
- let frameCount = 0;
1725
+ const { message, stack, isDev, pathname } = options;
1726
+ let frames = [];
1727
+ let sourceSnippet = "";
1728
+ let errorLine = 0;
1729
+ let errorFile = "";
1757
1730
  if (isDev && stack) {
1758
- const stackLines = stack.split("\n").filter((line) => line.trim());
1759
- const frames = stackLines.slice(1).filter((line) => line.includes("at "));
1760
- frameCount = frames.length;
1761
- callStackCards = frames.map((frame, index) => {
1762
- const match = frame.match(/at\s+(.+?)\s+\((.+?)\)/) || frame.match(/at\s+(.+)/);
1763
- if (match) {
1764
- const funcName = match[1] || "anonymous";
1765
- const location = match[2] || match[1];
1766
- return `
1767
- <div class="error-card" data-frame="${index}">
1768
- <div class="card-header">
1769
- <div class="card-title">${funcName.trim()}</div>
1770
- <div class="card-number">${index + 1}</div>
1771
- </div>
1772
- <div class="card-location">${location.trim()}</div>
1773
- </div>
1774
- `;
1731
+ const stackLines = stack.split("\n").filter((l) => l.trim());
1732
+ frames = stackLines.slice(1).filter((line) => line.includes("at ")).map((frame) => {
1733
+ const m1 = frame.match(/at\s+(.+?)\s+\((.+?):(\d+):\d+\)/);
1734
+ if (m1) return { call: m1[1].trim(), file: m1[2], line: parseInt(m1[3], 10) };
1735
+ const m2 = frame.match(/at\s+(.+?):(\d+):\d+/);
1736
+ if (m2) return { call: "(anonymous)", file: m2[1].trim(), line: parseInt(m2[2], 10) };
1737
+ return { call: frame.replace(/^\s*at\s+/, "").trim(), file: "", line: null };
1738
+ });
1739
+ const firstUserFrame = frames.find((f) => f.file && !f.file.includes("node_modules")) || frames[0];
1740
+ if (firstUserFrame) {
1741
+ errorFile = firstUserFrame.file;
1742
+ errorLine = firstUserFrame.line || 0;
1743
+ if (errorFile && errorLine > 0) {
1744
+ try {
1745
+ const fs8 = __require("fs");
1746
+ if (fs8.existsSync(errorFile)) {
1747
+ const src = fs8.readFileSync(errorFile, "utf-8").split("\n");
1748
+ const start = Math.max(0, errorLine - 4);
1749
+ const end = Math.min(src.length, errorLine + 3);
1750
+ sourceSnippet = src.slice(start, end).map((l, i) => {
1751
+ const num = start + i + 1;
1752
+ const isErr = num === errorLine;
1753
+ const numStr = String(num).padStart(3, " ");
1754
+ if (isErr) {
1755
+ return `<div class="src-line src-err"><span class="ln">${numStr}</span>${escapeHtml2(l)}</div>`;
1756
+ }
1757
+ return `<div class="src-line"><span class="ln">${numStr}</span>${escapeHtml2(l)}</div>`;
1758
+ }).join("");
1759
+ }
1760
+ } catch (_) {
1761
+ }
1775
1762
  }
1776
- return "";
1777
- }).join("");
1763
+ }
1764
+ }
1765
+ const userFrames = frames.filter((f) => f.file && !f.file.includes("node_modules"));
1766
+ const ignoredFrames = frames.filter((f) => !f.file || f.file.includes("node_modules"));
1767
+ const shortFile = errorFile.replace(/\\/g, "/").replace(/.*\/(?=app\/|server\/|src\/|pages\/)/, "");
1768
+ if (!isDev) {
1769
+ return `<!DOCTYPE html>
1770
+ <html lang="en">
1771
+ <head>
1772
+ <meta charset="UTF-8">
1773
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1774
+ <title>500 - Internal Server Error | Velix</title>
1775
+ <link rel="icon" href="/favicon.webp">
1776
+ <style>
1777
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1778
+ body {
1779
+ background: #0a0a0a;
1780
+ color: #ededed;
1781
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1782
+ display: flex; align-items: center; justify-content: center;
1783
+ min-height: 100vh;
1784
+ -webkit-font-smoothing: antialiased;
1785
+ }
1786
+ .container { display: flex; align-items: center; gap: 32px; padding: 32px; }
1787
+ .code { font-size: 48px; font-weight: 700; color: #fff; border-right: 1px solid rgba(255,255,255,0.15); padding-right: 32px; }
1788
+ .body h1 { font-size: 18px; font-weight: 600; color: #ededed; margin-bottom: 8px; }
1789
+ .body p { font-size: 14px; color: #888; margin-bottom: 20px; }
1790
+ .btn { display: inline-block; padding: 8px 18px; border-radius: 8px; font-size: 14px; font-weight: 500; text-decoration: none; background: #fff; color: #000; transition: all .15s; }
1791
+ .btn:hover { background: #e5e5e5; }
1792
+ </style>
1793
+ </head>
1794
+ <body>
1795
+ <div class="container">
1796
+ <div class="code">500</div>
1797
+ <div class="body">
1798
+ <h1>Internal Server Error</h1>
1799
+ <p>A server-side exception has occurred. Please try again later.</p>
1800
+ <a href="/" class="btn">Return Home</a>
1801
+ </div>
1802
+ </div>
1803
+ </body>
1804
+ </html>`;
1778
1805
  }
1779
1806
  return `<!DOCTYPE html>
1780
1807
  <html lang="en">
1781
1808
  <head>
1782
1809
  <meta charset="UTF-8">
1783
1810
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1784
- <title>Error | Velix v5</title>
1811
+ <title>Error | Velix v${VERSION}</title>
1785
1812
  <link rel="icon" href="/favicon.webp">
1786
1813
  <style>
1787
- * { margin: 0; padding: 0; box-sizing: border-box; }
1788
- body {
1789
- margin: 0;
1790
- background: linear-gradient(135deg, #0F172A 0%, #1E293B 100%);
1791
- color: #F8FAFC;
1792
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1814
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1815
+
1816
+ 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;
1793
1820
  min-height: 100vh;
1794
- overflow-x: hidden;
1821
+ display: flex;
1822
+ align-items: center;
1823
+ justify-content: center;
1824
+ padding: 24px;
1825
+ -webkit-font-smoothing: antialiased;
1795
1826
  }
1796
- .container {
1797
- max-width: 900px;
1798
- margin: 0 auto;
1799
- padding: 40px 20px;
1827
+
1828
+ /* \u2500\u2500 Overlay Card \u2500\u2500 */
1829
+ .overlay {
1830
+ 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;
1837
+ display: flex;
1838
+ flex-direction: column;
1839
+ max-height: calc(100vh - 48px);
1800
1840
  }
1801
- .error-header {
1802
- background: linear-gradient(135deg, #EF4444 0%, #DC2626 100%);
1803
- color: white;
1804
- padding: 20px 28px;
1805
- border-radius: 16px;
1841
+
1842
+ /* \u2500\u2500 Header Bar \u2500\u2500 */
1843
+ .header {
1806
1844
  display: flex;
1807
1845
  align-items: center;
1808
- gap: 14px;
1809
- margin-bottom: 28px;
1810
- font-size: 18px;
1811
- font-weight: 700;
1812
- box-shadow: 0 10px 40px rgba(239, 68, 68, 0.3);
1813
- }
1814
- .error-header svg {
1815
- width: 24px;
1816
- height: 24px;
1846
+ justify-content: space-between;
1847
+ padding: 12px 20px;
1848
+ background: #161616;
1849
+ border-bottom: 1px solid #2a2a2a;
1850
+ gap: 12px;
1817
1851
  flex-shrink: 0;
1818
1852
  }
1819
- .error-badge {
1820
- display: inline-block;
1821
- background: linear-gradient(135deg, #1E40AF 0%, #1E3A8A 100%);
1822
- color: #60A5FA;
1823
- padding: 6px 16px;
1824
- border-radius: 8px;
1853
+ .header-left {
1854
+ display: flex;
1855
+ align-items: center;
1856
+ gap: 8px;
1825
1857
  font-size: 13px;
1826
- font-weight: 700;
1827
- margin-bottom: 18px;
1828
- text-transform: uppercase;
1829
- letter-spacing: 0.8px;
1830
- box-shadow: 0 4px 12px rgba(30, 64, 175, 0.3);
1858
+ color: #888;
1831
1859
  }
1832
- .route-badge {
1833
- background: linear-gradient(135deg, #0C4A6E 0%, #075985 100%);
1834
- color: #22D3EE;
1835
- padding: 10px 18px;
1836
- border-radius: 10px;
1837
- font-size: 14px;
1838
- margin-bottom: 24px;
1839
- font-family: 'Courier New', monospace;
1840
- box-shadow: 0 4px 12px rgba(12, 74, 110, 0.3);
1841
- border: 1px solid rgba(34, 211, 238, 0.2);
1842
- }
1843
- .error-message {
1844
- background: rgba(239, 68, 68, 0.1);
1845
- border-left: 4px solid #EF4444;
1846
- padding: 18px 20px;
1847
- border-radius: 10px;
1848
- margin-bottom: 32px;
1849
- }
1850
- .error-message-text {
1851
- color: #FCA5A5;
1852
- font-size: 16px;
1853
- font-weight: 600;
1854
- line-height: 1.6;
1855
- font-family: 'Courier New', monospace;
1856
- }
1857
- .stack-section {
1858
- margin-top: 32px;
1860
+ .counter-badge {
1861
+ background: #1e1e1e;
1862
+ border: 1px solid #333;
1863
+ border-radius: 6px;
1864
+ padding: 2px 8px;
1865
+ font-size: 12px;
1866
+ color: #999;
1867
+ font-variant-numeric: tabular-nums;
1859
1868
  }
1860
- .stack-header {
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;
1861
1877
  display: flex;
1862
1878
  align-items: center;
1863
- justify-content: space-between;
1864
- margin-bottom: 20px;
1879
+ justify-content: center;
1880
+ font-size: 13px;
1881
+ transition: all .15s;
1865
1882
  }
1866
- .stack-title {
1867
- font-size: 18px;
1868
- font-weight: 700;
1869
- color: #F1F5F9;
1883
+ .nav-btn:hover:not(:disabled) { background: #2a2a2a; color: #fff; }
1884
+ .nav-btn:disabled { opacity: 0.3; cursor: default; }
1885
+ .framework-tag {
1870
1886
  display: flex;
1871
1887
  align-items: center;
1872
- gap: 10px;
1888
+ gap: 6px;
1889
+ font-size: 12px;
1890
+ color: #666;
1873
1891
  }
1874
- .frame-counter {
1875
- background: linear-gradient(135deg, #1F2937 0%, #111827 100%);
1876
- color: #22D3EE;
1877
- padding: 6px 14px;
1878
- border-radius: 8px;
1879
- font-size: 13px;
1892
+ .framework-tag::before {
1893
+ content: '';
1894
+ display: inline-block;
1895
+ width: 6px; height: 6px;
1896
+ border-radius: 50%;
1897
+ background: #3b82f6;
1898
+ }
1899
+
1900
+ /* \u2500\u2500 Scrollable Content \u2500\u2500 */
1901
+ .content {
1902
+ overflow-y: auto;
1903
+ flex: 1;
1904
+ }
1905
+
1906
+ /* \u2500\u2500 Error Summary \u2500\u2500 */
1907
+ .error-summary {
1908
+ padding: 28px 24px 20px;
1909
+ border-bottom: 1px solid #1e1e1e;
1910
+ }
1911
+ .error-label {
1912
+ display: inline-flex;
1913
+ align-items: center;
1914
+ gap: 6px;
1915
+ font-size: 11px;
1880
1916
  font-weight: 700;
1881
- border: 1px solid rgba(34, 211, 238, 0.2);
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;
1882
1925
  }
1883
- .error-card {
1884
- background: linear-gradient(135deg, #1E293B 0%, #0F172A 100%);
1885
- border: 1px solid rgba(34, 211, 238, 0.2);
1886
- border-radius: 14px;
1887
- padding: 20px;
1888
- margin-bottom: 12px;
1889
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1890
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
1891
- display: none;
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;
1892
1934
  }
1893
- .error-card.active {
1894
- display: block;
1895
- animation: slideIn 0.3s ease-out;
1935
+ .error-location {
1936
+ margin-top: 10px;
1937
+ font-size: 13px;
1938
+ color: #888;
1896
1939
  }
1897
- .error-card:hover {
1898
- border-color: rgba(34, 211, 238, 0.5);
1899
- box-shadow: 0 8px 24px rgba(34, 211, 238, 0.2);
1900
- transform: translateY(-2px);
1940
+ .error-location strong { color: #ccc; }
1941
+
1942
+ /* \u2500\u2500 Source Block \u2500\u2500 */
1943
+ .source-block {
1944
+ border-bottom: 1px solid #1e1e1e;
1901
1945
  }
1902
- .card-header {
1946
+ .source-topbar {
1903
1947
  display: flex;
1904
- justify-content: space-between;
1905
1948
  align-items: center;
1906
- margin-bottom: 12px;
1907
- }
1908
- .card-title {
1909
- color: #22D3EE;
1910
- font-size: 15px;
1911
- font-weight: 700;
1912
- font-family: 'Courier New', monospace;
1913
- }
1914
- .card-number {
1915
- background: rgba(34, 211, 238, 0.2);
1916
- color: #22D3EE;
1917
- padding: 4px 10px;
1918
- border-radius: 6px;
1949
+ padding: 10px 16px;
1950
+ background: #161616;
1951
+ border-bottom: 1px solid #1e1e1e;
1952
+ gap: 8px;
1919
1953
  font-size: 12px;
1920
- font-weight: 700;
1954
+ color: #666;
1921
1955
  }
1922
- .card-location {
1923
- color: #94A3B8;
1956
+ .source-topbar .filepath { color: #aaa; font-family: ui-monospace, monospace; }
1957
+ .source-code {
1958
+ background: #0d0d0d;
1959
+ overflow-x: auto;
1960
+ padding: 10px 0;
1961
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1924
1962
  font-size: 13px;
1925
- font-family: 'Courier New', monospace;
1926
- word-break: break-all;
1927
- line-height: 1.6;
1963
+ line-height: 1.7;
1928
1964
  }
1929
- .pagination {
1965
+ .src-line {
1930
1966
  display: flex;
1931
- justify-content: center;
1932
- align-items: center;
1933
- gap: 12px;
1934
- margin-top: 24px;
1967
+ align-items: flex-start;
1968
+ padding: 0 20px;
1969
+ white-space: pre;
1970
+ color: #555;
1935
1971
  }
1936
- .pagination-btn {
1937
- background: linear-gradient(135deg, #1E293B 0%, #0F172A 100%);
1938
- border: 1px solid rgba(34, 211, 238, 0.3);
1939
- color: #22D3EE;
1940
- padding: 10px 20px;
1941
- border-radius: 10px;
1942
- font-size: 14px;
1943
- font-weight: 600;
1944
- cursor: pointer;
1945
- transition: all 0.2s;
1972
+ .src-line .ln {
1973
+ color: #3a3a3a;
1974
+ text-align: right;
1975
+ width: 36px;
1976
+ margin-right: 16px;
1977
+ flex-shrink: 0;
1978
+ user-select: none;
1946
1979
  }
1947
- .pagination-btn:hover:not(:disabled) {
1948
- background: linear-gradient(135deg, #22D3EE 0%, #06B6D4 100%);
1949
- color: #0F172A;
1950
- transform: translateY(-2px);
1951
- box-shadow: 0 6px 20px rgba(34, 211, 238, 0.4);
1980
+ .src-err {
1981
+ background: rgba(239, 68, 68, 0.08);
1982
+ color: #fca5a5;
1983
+ position: relative;
1952
1984
  }
1953
- .pagination-btn:disabled {
1954
- opacity: 0.3;
1955
- cursor: not-allowed;
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;
1956
1993
  }
1957
- .pagination-info {
1958
- color: #94A3B8;
1959
- font-size: 14px;
1960
- font-weight: 600;
1994
+
1995
+ /* \u2500\u2500 Call Stack \u2500\u2500 */
1996
+ .callstack {
1997
+ padding: 20px 24px;
1961
1998
  }
1962
- .footer {
1963
- margin-top: 48px;
1964
- padding-top: 28px;
1965
- border-top: 1px solid rgba(34, 211, 238, 0.2);
1999
+ .callstack-header {
1966
2000
  display: flex;
1967
- justify-content: space-between;
1968
2001
  align-items: center;
1969
- flex-wrap: wrap;
1970
- gap: 20px;
2002
+ justify-content: space-between;
2003
+ margin-bottom: 14px;
2004
+ }
2005
+ .callstack-title {
2006
+ font-size: 13px;
2007
+ font-weight: 600;
2008
+ color: #aaa;
2009
+ text-transform: uppercase;
2010
+ letter-spacing: 0.06em;
1971
2011
  }
1972
- .brand {
2012
+ .ignore-btn {
2013
+ font-size: 12px;
2014
+ color: #555;
2015
+ background: none;
2016
+ border: none;
2017
+ cursor: pointer;
1973
2018
  display: flex;
1974
2019
  align-items: center;
1975
- gap: 10px;
1976
- font-weight: 700;
1977
- color: #22D3EE;
1978
- font-size: 15px;
1979
- }
1980
- .brand img {
1981
- width: 20px;
1982
- height: 20px;
2020
+ gap: 4px;
2021
+ transition: color .15s;
1983
2022
  }
1984
- .footer-links {
1985
- display: flex;
1986
- gap: 24px;
1987
- flex-wrap: wrap;
2023
+ .ignore-btn:hover { color: #aaa; }
2024
+ .ignore-caret { transition: transform .2s; }
2025
+ .ignore-btn.open .ignore-caret { transform: rotate(90deg); }
2026
+
2027
+ .frame {
2028
+ padding: 10px 12px;
2029
+ border-radius: 6px;
2030
+ margin-bottom: 4px;
2031
+ font-size: 13px;
2032
+ cursor: default;
1988
2033
  }
1989
- .footer-links a {
1990
- color: #60A5FA;
1991
- text-decoration: none;
2034
+ .frame:hover { background: rgba(255,255,255,0.03); }
2035
+ .frame-call {
2036
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1992
2037
  font-weight: 600;
1993
- transition: all 0.2s;
1994
- font-size: 14px;
1995
- }
1996
- .footer-links a:hover {
1997
- color: #22D3EE;
1998
- transform: translateY(-1px);
2038
+ color: #e2e8f0;
2039
+ margin-bottom: 3px;
1999
2040
  }
2000
- .no-stack {
2001
- background: rgba(239, 68, 68, 0.1);
2002
- border: 1px solid rgba(239, 68, 68, 0.3);
2003
- border-radius: 12px;
2004
- padding: 32px;
2005
- color: #FCA5A5;
2006
- text-align: center;
2007
- font-size: 15px;
2008
- }
2009
- @keyframes slideIn {
2010
- from {
2011
- opacity: 0;
2012
- transform: translateY(10px);
2013
- }
2014
- to {
2015
- opacity: 1;
2016
- transform: translateY(0);
2017
- }
2041
+ .frame-loc {
2042
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2043
+ font-size: 12px;
2044
+ color: #555;
2018
2045
  }
2046
+ .frame.ignored .frame-call { color: #444; }
2047
+
2048
+ .ignored-section { margin-top: 8px; display: none; }
2049
+ .ignored-section.open { display: block; }
2019
2050
  </style>
2020
2051
  </head>
2021
2052
  <body>
2022
- <div class="container">
2023
- <div class="error-header">
2024
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
2025
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
2026
- </svg>
2027
- <span>Unhandled Runtime Error</span>
2053
+ <div class="overlay">
2054
+
2055
+ <!-- Header bar -->
2056
+ <div class="header">
2057
+ <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>
2062
+ </div>
2063
+ <div class="framework-tag">Velix v${VERSION} \xB7 esbuild</div>
2064
+ </div>
2065
+
2066
+ <!-- Scrollable content -->
2067
+ <div class="content">
2068
+
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>` : ""}
2028
2077
  </div>
2029
-
2030
- <div class="error-badge">SERVER ERROR 500</div>
2031
- ${pathname ? `<div class="route-badge">Route: ${pathname}</div>` : ""}
2032
-
2033
- <div class="error-message">
2034
- <div class="error-message-text">${message}</div>
2078
+
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>
2035
2087
  </div>
2036
-
2037
- ${isDev && callStackCards ? `
2038
- <div class="stack-section">
2039
- <div class="stack-header">
2040
- <div class="stack-title">
2041
- <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2042
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
2043
- </svg>
2044
- Call Stack
2045
- </div>
2046
- <div class="frame-counter">${frameCount}</div>
2047
- </div>
2048
- <div id="error-cards">
2049
- ${callStackCards}
2050
- </div>
2051
- ${frameCount > 1 ? `
2052
- <div class="pagination">
2053
- <button class="pagination-btn" id="prev-btn" onclick="changePage(-1)">
2054
- <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-right:4px;">
2055
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
2056
- </svg>
2057
- Previous
2058
- </button>
2059
- <div class="pagination-info">
2060
- <span id="current-page">1</span> / <span id="total-pages">${frameCount}</span>
2061
- </div>
2062
- <button class="pagination-btn" id="next-btn" onclick="changePage(1)">
2063
- Next
2064
- <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px;">
2065
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
2066
- </svg>
2067
- </button>
2068
- </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>
2069
2101
  ` : ""}
2070
- </div>
2071
- ` : '<div class="no-stack">An error occurred while processing your request. Please check the server logs for more details.</div>'}
2072
-
2073
- <div class="footer">
2074
- <div class="brand">
2075
- <img src="/__velix/logo.webp" alt="Velix" onerror="this.style.display='none'"/>
2076
- <span>Velix v${VERSION}</span>
2077
- </div>
2078
- <div class="footer-links">
2079
- <a href="/">Home</a>
2080
- <a href="javascript:location.reload()">Reload</a>
2081
- <a href="https://github.com/velix/velix/tree/main/docs" target="_blank">Documentation</a>
2082
- ${isDev ? '<span style="color:#94A3B8;">Development Mode</span>' : ""}
2102
+ </div>
2103
+
2104
+ <!-- User frames -->
2105
+ ${userFrames.map((f) => `
2106
+ <div class="frame">
2107
+ <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>
2109
+ </div>
2110
+ `).join("")}
2111
+
2112
+ <!-- Ignored frames -->
2113
+ ${ignoredFrames.length > 0 ? `
2114
+ <div class="ignored-section" id="ignoredSection">
2115
+ ${ignoredFrames.map((f) => `
2116
+ <div class="frame ignored">
2117
+ <div class="frame-call">${escapeHtml2(f.call)}</div>
2118
+ <div class="frame-loc">${escapeHtml2(f.file)}${f.line ? `:${f.line}` : ""}</div>
2083
2119
  </div>
2120
+ `).join("")}
2121
+ </div>
2122
+ ` : ""}
2084
2123
  </div>
2085
- </div>
2086
-
2087
- ${isDev && frameCount > 0 ? `
2124
+
2125
+ </div><!-- /content -->
2126
+ </div><!-- /overlay -->
2127
+
2088
2128
  <script>
2089
- let currentPage = 1;
2090
- const totalPages = ${frameCount};
2091
- const cards = document.querySelectorAll('.error-card');
2092
- const prevBtn = document.getElementById('prev-btn');
2093
- const nextBtn = document.getElementById('next-btn');
2094
- const currentPageSpan = document.getElementById('current-page');
2095
-
2096
- function showPage(page) {
2097
- cards.forEach((card, index) => {
2098
- card.classList.remove('active');
2099
- if (index === page - 1) {
2100
- card.classList.add('active');
2101
- }
2102
- });
2103
-
2104
- currentPage = page;
2105
- currentPageSpan.textContent = page;
2106
-
2107
- if (prevBtn) prevBtn.disabled = page === 1;
2108
- if (nextBtn) nextBtn.disabled = page === totalPages;
2109
- }
2110
-
2111
- function changePage(direction) {
2112
- const newPage = currentPage + direction;
2113
- if (newPage >= 1 && newPage <= totalPages) {
2114
- showPage(newPage);
2115
- }
2116
- }
2117
-
2118
- // Show first page on load
2119
- showPage(1);
2120
-
2121
- // Keyboard navigation
2122
- document.addEventListener('keydown', (e) => {
2123
- if (e.key === 'ArrowLeft') changePage(-1);
2124
- if (e.key === 'ArrowRight') changePage(1);
2125
- });
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);
2135
+ }
2126
2136
  </script>
2127
- ` : ""}
2128
2137
  </body>
2129
2138
  </html>`;
2130
2139
  }