@teamvelix/velix 5.2.7 → 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.
@@ -707,7 +707,7 @@ init_esm_shims();
707
707
 
708
708
  // version.ts
709
709
  init_esm_shims();
710
- var VERSION = "5.2.7";
710
+ var VERSION = "5.2.8";
711
711
 
712
712
  // logger.ts
713
713
  var colors = {
@@ -1638,99 +1638,84 @@ function generate404Page(pathname = "/") {
1638
1638
  <title>404 - Page Not Found | Velix v${VERSION}</title>
1639
1639
  <link rel="icon" href="/favicon.webp">
1640
1640
  <style>
1641
- * { margin: 0; padding: 0; box-sizing: border-box; }
1642
- body {
1643
- margin: 0;
1644
- background: #161616;
1645
- color: white;
1646
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1647
- display: flex;
1648
- align-items: center;
1649
- justify-content: center;
1650
- min-height: 100vh;
1651
- text-align: center;
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;
1652
1650
  -webkit-font-smoothing: antialiased;
1653
1651
  }
1654
- .container {
1655
- max-width: 700px;
1656
- padding: 60px 40px;
1657
- position: relative;
1658
- z-index: 10;
1652
+ .container {
1653
+ display: flex;
1654
+ align-items: center;
1655
+ gap: 32px;
1656
+ padding: 32px;
1659
1657
  }
1660
- h1 {
1661
- font-size: 160px;
1662
- font-weight: 900;
1663
- margin: 0;
1664
- background: linear-gradient(135deg, #00e87a 0%, #22d3ee 100%);
1665
- -webkit-background-clip: text;
1666
- -webkit-text-fill-color: transparent;
1667
- line-height: 1;
1668
- letter-spacing: -0.05em;
1669
- animation: fadeIn 0.5s ease-out;
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;
1670
1665
  }
1671
- h2 {
1672
- font-size: 32px;
1673
- font-weight: 700;
1674
- margin: 24px 0 12px;
1666
+ .body h1 {
1667
+ font-size: 18px;
1668
+ font-weight: 600;
1675
1669
  color: #ededed;
1676
- animation: fadeIn 0.5s ease-out 0.1s backwards;
1670
+ margin-bottom: 8px;
1677
1671
  }
1678
- p {
1679
- color: #888;
1680
- font-size: 16px;
1681
- line-height: 1.7;
1682
- margin-bottom: 36px;
1683
- animation: fadeIn 0.5s ease-out 0.2s backwards;
1684
- }
1685
- code {
1686
- background: #222;
1687
- padding: 3px 8px;
1688
- border-radius: 6px;
1689
- font-family: 'JetBrains Mono', monospace;
1690
- color: #00e87a;
1672
+ .body p {
1691
1673
  font-size: 14px;
1692
- border: 1px solid #333;
1674
+ color: #888;
1675
+ margin-bottom: 20px;
1693
1676
  }
1694
- .btn-group {
1695
- display: flex;
1696
- gap: 12px;
1697
- justify-content: center;
1698
- animation: fadeIn 0.5s ease-out 0.3s backwards;
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;
1699
1684
  }
1700
- .btn {
1701
- display: inline-block;
1702
- background: #222;
1703
- color: white;
1704
- text-decoration: none;
1705
- padding: 12px 28px;
1706
- border-radius: 10px;
1707
- font-weight: 600;
1685
+ .actions { display: flex; gap: 10px; }
1686
+ .btn {
1687
+ display: inline-block;
1688
+ padding: 8px 18px;
1689
+ border-radius: 8px;
1708
1690
  font-size: 14px;
1709
- transition: all 0.2s;
1710
- border: 1px solid #333;
1711
- cursor: pointer;
1691
+ font-weight: 500;
1692
+ text-decoration: none;
1693
+ transition: all .15s;
1694
+ border: 1px solid transparent;
1712
1695
  }
1713
- .btn:hover { background: #333; }
1714
- .btn-accent {
1715
- background: #00e87a;
1716
- color: #000;
1717
- border-color: #00e87a;
1696
+ .btn-primary {
1697
+ background: #fff;
1698
+ color: #000;
1718
1699
  }
1719
- .btn-accent:hover { background: #00d46e; }
1720
- @keyframes fadeIn {
1721
- from { opacity: 0; transform: translateY(16px); }
1722
- to { opacity: 1; transform: translateY(0); }
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);
1723
1705
  }
1706
+ .btn-secondary:hover { background: rgba(255,255,255,0.1); }
1724
1707
  </style>
1725
1708
  </head>
1726
1709
  <body>
1727
1710
  <div class="container">
1728
- <h1>404</h1>
1729
- <h2>Page Not Found</h2>
1730
- <p>The page <code>${pathname}</code> could not be found.<br>It may have been moved or deleted.</p>
1731
- <div class="btn-group">
1732
- <a href="/" class="btn btn-accent">Return Home</a>
1733
- <a href="javascript:history.back()" class="btn">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>
1734
1719
  </div>
1735
1720
  </div>
1736
1721
  </body>
@@ -1743,35 +1728,33 @@ function generate500Page(options) {
1743
1728
  let errorLine = 0;
1744
1729
  let errorFile = "";
1745
1730
  if (isDev && stack) {
1746
- const stackLines = stack.split("\\n").filter((line) => line.trim());
1731
+ const stackLines = stack.split("\n").filter((l) => l.trim());
1747
1732
  frames = stackLines.slice(1).filter((line) => line.includes("at ")).map((frame) => {
1748
- const match = frame.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/) || frame.match(/at\s+(.+?):(\d+):(\d+)/);
1749
- if (match && match.length === 5) {
1750
- return { funcName: match[1].trim(), file: match[2], line: match[3], col: match[4] };
1751
- }
1752
- if (match && match.length === 4) {
1753
- return { funcName: "(anonymous)", file: match[1].trim(), line: match[2], col: match[3] };
1754
- }
1755
- const simpleMatch = frame.match(/at\s+(.+)/);
1756
- return { funcName: simpleMatch?.[1]?.trim() || "unknown", file: "", line: "", col: "" };
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 };
1757
1738
  });
1758
- if (frames.length > 0) {
1759
- const firstFrame = frames.find((f) => f.file && !f.file.includes("node_modules")) || frames[0];
1760
- errorFile = firstFrame.file;
1761
- errorLine = parseInt(firstFrame.line, 10) || 0;
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;
1762
1743
  if (errorFile && errorLine > 0) {
1763
1744
  try {
1764
1745
  const fs8 = __require("fs");
1765
1746
  if (fs8.existsSync(errorFile)) {
1766
- const src = fs8.readFileSync(errorFile, "utf-8").split("\\n");
1767
- const start = Math.max(0, errorLine - 3);
1747
+ const src = fs8.readFileSync(errorFile, "utf-8").split("\n");
1748
+ const start = Math.max(0, errorLine - 4);
1768
1749
  const end = Math.min(src.length, errorLine + 3);
1769
1750
  sourceSnippet = src.slice(start, end).map((l, i) => {
1770
1751
  const num = start + i + 1;
1771
1752
  const isErr = num === errorLine;
1772
- const prefix = isErr ? "> " : " ";
1773
- const numStr = String(num).padStart(4, " ");
1774
- return `<div class="src-line${isErr ? " src-err" : ""}">${prefix}${numStr} | ${escapeHtml2(l)}</div>`;
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>`;
1775
1758
  }).join("");
1776
1759
  }
1777
1760
  } catch (_) {
@@ -1780,8 +1763,46 @@ function generate500Page(options) {
1780
1763
  }
1781
1764
  }
1782
1765
  const userFrames = frames.filter((f) => f.file && !f.file.includes("node_modules"));
1783
- const ignoredCount = frames.length - userFrames.length;
1766
+ const ignoredFrames = frames.filter((f) => !f.file || f.file.includes("node_modules"));
1784
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>`;
1805
+ }
1785
1806
  return `<!DOCTYPE html>
1786
1807
  <html lang="en">
1787
1808
  <head>
@@ -1790,291 +1811,328 @@ function generate500Page(options) {
1790
1811
  <title>Error | Velix v${VERSION}</title>
1791
1812
  <link rel="icon" href="/favicon.webp">
1792
1813
  <style>
1793
- :root {
1794
- --bg-body: #000;
1795
- --bg-card: #111;
1796
- --border: #333;
1797
- --text-main: #ededed;
1798
- --text-muted: #888;
1799
- --red-tag: #e5484d;
1800
- --red-bg: rgba(229, 72, 77, 0.15);
1801
- --code-bg: #000;
1802
- --code-hl: rgba(255,255,255,0.1);
1803
- --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1804
- --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
1805
- }
1814
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
1806
1815
 
1807
- * { margin: 0; padding: 0; box-sizing: border-box; }
1808
1816
  body {
1809
- background: var(--bg-body);
1810
- color: var(--text-main);
1811
- font-family: var(--font-sans);
1817
+ background: rgba(0, 0, 0, 0.85);
1818
+ color: #ededed;
1819
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
1812
1820
  min-height: 100vh;
1813
- line-height: 1.7;
1814
- margin-bottom: 12px;
1815
- word-break: break-word;
1816
- }
1817
- .error-hint {
1818
- color: #22c55e;
1819
- font-size: 14px;
1820
- margin-bottom: 20px;
1821
+ display: flex;
1822
+ align-items: center;
1823
+ justify-content: center;
1824
+ padding: 24px;
1825
+ -webkit-font-smoothing: antialiased;
1821
1826
  }
1822
1827
 
1823
- /* Source block */
1824
- .source-block {
1825
- background: #1a1a1a;
1828
+ /* \u2500\u2500 Overlay Card \u2500\u2500 */
1829
+ .overlay {
1830
+ width: 100%;
1831
+ max-width: 940px;
1832
+ background: #111;
1826
1833
  border: 1px solid #2a2a2a;
1827
1834
  border-radius: 12px;
1835
+ box-shadow: 0 24px 64px rgba(0,0,0,0.6);
1828
1836
  overflow: hidden;
1829
- margin-bottom: 24px;
1837
+ display: flex;
1838
+ flex-direction: column;
1839
+ max-height: calc(100vh - 48px);
1830
1840
  }
1831
- .source-header {
1841
+
1842
+ /* \u2500\u2500 Header Bar \u2500\u2500 */
1843
+ .header {
1832
1844
  display: flex;
1833
1845
  align-items: center;
1834
1846
  justify-content: space-between;
1835
- padding: 10px 16px;
1836
- background: #1e1e1e;
1847
+ padding: 12px 20px;
1848
+ background: #161616;
1837
1849
  border-bottom: 1px solid #2a2a2a;
1850
+ gap: 12px;
1851
+ flex-shrink: 0;
1852
+ }
1853
+ .header-left {
1854
+ display: flex;
1855
+ align-items: center;
1856
+ gap: 8px;
1838
1857
  font-size: 13px;
1839
- color: #aaa;
1858
+ color: #888;
1859
+ }
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;
1868
+ }
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;
1877
+ display: flex;
1878
+ align-items: center;
1879
+ justify-content: center;
1880
+ font-size: 13px;
1881
+ transition: all .15s;
1840
1882
  }
1841
- .source-header .file-icon { color: #666; margin-right: 8px; }
1842
- .source-header a {
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;
1843
1890
  color: #666;
1844
- text-decoration: none;
1845
- transition: color .15s;
1846
1891
  }
1847
- .source-header a:hover { color: #fff; }
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;
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;
1934
+ }
1935
+ .error-location {
1936
+ margin-top: 10px;
1937
+ 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;
1945
+ }
1946
+ .source-topbar {
1947
+ display: flex;
1948
+ 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;
1955
+ }
1956
+ .source-topbar .filepath { color: #aaa; font-family: ui-monospace, monospace; }
1848
1957
  .source-code {
1849
- padding: 12px 0;
1958
+ background: #0d0d0d;
1850
1959
  overflow-x: auto;
1851
- font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', Menlo, monospace;
1960
+ padding: 10px 0;
1961
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1852
1962
  font-size: 13px;
1853
1963
  line-height: 1.7;
1854
1964
  }
1855
1965
  .src-line {
1856
- padding: 0 16px;
1966
+ display: flex;
1967
+ align-items: flex-start;
1968
+ padding: 0 20px;
1857
1969
  white-space: pre;
1858
- color: #888;
1970
+ color: #555;
1971
+ }
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;
1859
1979
  }
1860
1980
  .src-err {
1861
- background: rgba(255,50,50,0.1);
1862
- color: #ff8a8a;
1863
- font-weight: 500;
1981
+ background: rgba(239, 68, 68, 0.08);
1982
+ color: #fca5a5;
1983
+ position: relative;
1984
+ }
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;
1864
1993
  }
1865
1994
 
1866
- /* Call stack */
1867
- .stack-section { margin-bottom: 24px; }
1868
- .stack-header {
1869
- display: flex;
1870
- align-items: center;
1871
- justify-content: space-between;
1872
- margin-bottom: 12px;
1995
+ /* \u2500\u2500 Call Stack \u2500\u2500 */
1996
+ .callstack {
1997
+ padding: 20px 24px;
1873
1998
  }
1874
- .stack-label {
1875
- font-size: 15px;
1876
- font-weight: 700;
1877
- color: #ededed;
1999
+ .callstack-header {
1878
2000
  display: flex;
1879
2001
  align-items: center;
1880
- gap: 8px;
2002
+ justify-content: space-between;
2003
+ margin-bottom: 14px;
1881
2004
  }
1882
- .stack-count {
1883
- background: #2a2a2a;
1884
- color: #aaa;
1885
- padding: 2px 8px;
1886
- border-radius: 6px;
1887
- font-size: 12px;
2005
+ .callstack-title {
2006
+ font-size: 13px;
1888
2007
  font-weight: 600;
2008
+ color: #aaa;
2009
+ text-transform: uppercase;
2010
+ letter-spacing: 0.06em;
1889
2011
  }
1890
- .stack-toggle {
1891
- font-size: 13px;
1892
- color: #888;
1893
- cursor: pointer;
2012
+ .ignore-btn {
2013
+ font-size: 12px;
2014
+ color: #555;
1894
2015
  background: none;
1895
2016
  border: none;
2017
+ cursor: pointer;
1896
2018
  display: flex;
1897
2019
  align-items: center;
1898
2020
  gap: 4px;
1899
2021
  transition: color .15s;
1900
2022
  }
1901
- .stack-toggle:hover { color: #fff; }
2023
+ .ignore-btn:hover { color: #aaa; }
2024
+ .ignore-caret { transition: transform .2s; }
2025
+ .ignore-btn.open .ignore-caret { transform: rotate(90deg); }
1902
2026
 
1903
2027
  .frame {
1904
- padding: 12px 0;
1905
- border-bottom: 1px solid #1e1e1e;
1906
- }
1907
- .frame:last-child { border-bottom: none; }
1908
- .frame-name {
1909
- font-size: 14px;
1910
- font-weight: 600;
1911
- color: #ededed;
1912
- display: flex;
1913
- align-items: center;
1914
- gap: 6px;
2028
+ padding: 10px 12px;
2029
+ border-radius: 6px;
1915
2030
  margin-bottom: 4px;
1916
- }
1917
- .frame-name a {
1918
- color: #666;
1919
- text-decoration: none;
1920
- transition: color .15s;
1921
- }
1922
- .frame-name a:hover { color: #fff; }
1923
- .frame-loc {
1924
2031
  font-size: 13px;
1925
- color: #666;
1926
- font-family: 'JetBrains Mono', 'Fira Code', monospace;
2032
+ cursor: default;
1927
2033
  }
1928
-
1929
- /* Footer */
1930
- .footer-row {
1931
- display: flex;
1932
- justify-content: flex-end;
1933
- padding: 16px 0;
1934
- border-top: 1px solid #1e1e1e;
1935
- margin-top: 8px;
2034
+ .frame:hover { background: rgba(255,255,255,0.03); }
2035
+ .frame-call {
2036
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2037
+ font-weight: 600;
2038
+ color: #e2e8f0;
2039
+ margin-bottom: 3px;
1936
2040
  }
1937
- .footer-row a {
1938
- color: #ff6b6b;
1939
- text-decoration: none;
1940
- font-size: 13px;
1941
- font-weight: 500;
1942
- display: flex;
1943
- align-items: center;
1944
- gap: 6px;
1945
- transition: opacity .15s;
2041
+ .frame-loc {
2042
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2043
+ font-size: 12px;
2044
+ color: #555;
1946
2045
  }
1947
- .footer-row a:hover { opacity: 0.8; }
2046
+ .frame.ignored .frame-call { color: #444; }
1948
2047
 
1949
- /* Prod mode */
1950
- .prod-card {
1951
- background: #1a1a1a;
1952
- border: 1px solid #2a2a2a;
1953
- border-radius: 16px;
1954
- padding: 48px 32px;
1955
- text-align: center;
1956
- margin-top: 32px;
1957
- }
1958
- .prod-card h2 { font-size: 20px; margin-bottom: 8px; color: #fff; }
1959
- .prod-card p { font-size: 15px; color: #888; margin-bottom: 24px; }
1960
- .prod-card a {
1961
- display: inline-block;
1962
- padding: 10px 24px;
1963
- border-radius: 10px;
1964
- background: #222;
1965
- border: 1px solid #333;
1966
- color: #fff;
1967
- text-decoration: none;
1968
- font-weight: 500;
1969
- font-size: 14px;
1970
- transition: all .15s;
1971
- }
1972
- .prod-card a:hover { background: #333; }
2048
+ .ignored-section { margin-top: 8px; display: none; }
2049
+ .ignored-section.open { display: block; }
1973
2050
  </style>
1974
2051
  </head>
1975
2052
  <body>
1976
2053
  <div class="overlay">
1977
- <!-- Top Bar -->
1978
- <div class="top-bar">
1979
- <div class="page-nav">
1980
- <button id="prev-btn" onclick="changePage(-1)" disabled>\u2039</button>
1981
- <span><span id="current-page">1</span>/<span id="total-pages">1</span></span>
1982
- <button id="next-btn" onclick="changePage(1)" disabled>\u203A</button>
1983
- </div>
1984
- <div class="version-badge">
1985
- <span class="version-dot"></span>
1986
- Velix v${VERSION} esbuild
1987
- </div>
1988
- </div>
1989
2054
 
1990
- <!-- Error Tag -->
1991
- <div class="error-tag">Unhandled Runtime Error</div>
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>
1992
2065
 
1993
- <!-- Error Message -->
1994
- <div class="error-msg">${escapeHtml2(message)}</div>
1995
- ${pathname ? `<div class="error-hint">Check the render method of '<strong>${escapeHtml2(pathname)}</strong>'.</div>` : ""}
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>` : ""}
2077
+ </div>
1996
2078
 
1997
- ${isDev && sourceSnippet ? `
1998
- <!-- Source Code Block -->
2079
+ ${sourceSnippet ? `
2080
+ <!-- Source code snippet -->
1999
2081
  <div class="source-block">
2000
- <div class="source-header">
2001
- <span><span class="file-icon">\u2699</span> ${escapeHtml2(shortFile)}${errorLine ? ` (${errorLine})` : ""}${userFrames[0]?.funcName ? ` @ ${escapeHtml2(userFrames[0].funcName)}` : ""}</span>
2002
- <a href="#" title="Open in editor">\u2197</a>
2003
- </div>
2004
- <div class="source-code">${sourceSnippet}</div>
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>
2005
2087
  </div>
2006
2088
  ` : ""}
2007
2089
 
2008
- ${isDev && frames.length > 0 ? `
2009
- <!-- Call Stack -->
2010
- <div class="stack-section">
2011
- <div class="stack-header">
2012
- <div class="stack-label">
2013
- Call Stack
2014
- <span class="stack-count">${frames.length}</span>
2015
- </div>
2016
- ${ignoredCount > 0 ? `<button class="stack-toggle" onclick="toggleIgnored()">Show ${ignoredCount} ignore-listed frames \u21D5</button>` : ""}
2017
- </div>
2018
- <div id="frames-list">
2019
- ${userFrames.map((f, i) => `
2020
- <div class="frame" data-frame="${i}">
2021
- <div class="frame-name">
2022
- <strong>${escapeHtml2(f.funcName)}</strong>
2023
- <a href="#" title="Open in editor">\u2197</a>
2024
- </div>
2025
- <div class="frame-loc">${escapeHtml2(f.file.replace(/.*[\\\/](?=app[\\\/]|server[\\\/]|src[\\\/]|pages[\\\/])/, ""))}${f.line ? ` (${f.line}:${f.col})` : ""}</div>
2026
- </div>
2027
- `).join("")}
2028
- </div>
2029
- <div id="ignored-frames" style="display:none;">
2030
- ${frames.filter((f) => !f.file || f.file.includes("node_modules")).map((f, i) => `
2031
- <div class="frame" data-frame="ignored-${i}">
2032
- <div class="frame-name" style="color:#666;">
2033
- <strong>${escapeHtml2(f.funcName)}</strong>
2034
- </div>
2035
- <div class="frame-loc">${escapeHtml2(f.file)}${f.line ? ` (${f.line}:${f.col})` : ""}</div>
2036
- </div>
2037
- `).join("")}
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
+ ` : ""}
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>
2038
2119
  </div>
2120
+ `).join("")}
2121
+ </div>
2122
+ ` : ""}
2039
2123
  </div>
2040
- ` : `
2041
- <div class="prod-card">
2042
- <h2>Application Error</h2>
2043
- <p>A server-side error occurred. Check your server logs for more details.</p>
2044
- <a href="/">Return Home</a>
2045
- </div>
2046
- `}
2047
2124
 
2048
- <!-- Footer -->
2049
- <div class="footer-row">
2050
- <a href="javascript:location.reload()">Was this helpful? \u21BB</a>
2051
- </div>
2052
- </div>
2125
+ </div><!-- /content -->
2126
+ </div><!-- /overlay -->
2053
2127
 
2054
2128
  <script>
2055
- let currentPage = 1;
2056
- const totalPages = 1;
2057
- document.getElementById('total-pages').textContent = totalPages;
2058
-
2059
- function changePage(d) {
2060
- const next = currentPage + d;
2061
- if (next >= 1 && next <= totalPages) {
2062
- currentPage = next;
2063
- document.getElementById('current-page').textContent = currentPage;
2064
- document.getElementById('prev-btn').disabled = currentPage === 1;
2065
- document.getElementById('next-btn').disabled = currentPage === totalPages;
2066
- }
2067
- }
2068
-
2069
- function toggleIgnored() {
2070
- const el = document.getElementById('ignored-frames');
2071
- if (el) el.style.display = el.style.display === 'none' ? 'block' : 'none';
2072
- }
2073
-
2074
- document.addEventListener('keydown', (e) => {
2075
- if (e.key === 'ArrowLeft') changePage(-1);
2076
- if (e.key === 'ArrowRight') changePage(1);
2077
- });
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
+ }
2078
2136
  </script>
2079
2137
  </body>
2080
2138
  </html>`;