@teamvelix/velix 5.2.5 → 5.2.7

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.7";
705
711
 
706
712
  // logger.ts
707
713
  var colors = {
@@ -1620,27 +1626,30 @@ 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
1641
  * { margin: 0; padding: 0; box-sizing: border-box; }
1633
1642
  body {
1634
1643
  margin: 0;
1635
- background: linear-gradient(135deg, #0F172A 0%, #1E293B 100%);
1644
+ background: #161616;
1636
1645
  color: white;
1637
- font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1646
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1638
1647
  display: flex;
1639
1648
  align-items: center;
1640
1649
  justify-content: center;
1641
1650
  min-height: 100vh;
1642
1651
  text-align: center;
1643
- overflow: hidden;
1652
+ -webkit-font-smoothing: antialiased;
1644
1653
  }
1645
1654
  .container {
1646
1655
  max-width: 700px;
@@ -1648,483 +1657,425 @@ function generate404Page(pathname = "/") {
1648
1657
  position: relative;
1649
1658
  z-index: 10;
1650
1659
  }
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
1660
  h1 {
1659
- font-size: 180px;
1661
+ font-size: 160px;
1660
1662
  font-weight: 900;
1661
1663
  margin: 0;
1662
- background: linear-gradient(135deg, #22D3EE 0%, #2563EB 100%);
1664
+ background: linear-gradient(135deg, #00e87a 0%, #22d3ee 100%);
1663
1665
  -webkit-background-clip: text;
1664
1666
  -webkit-text-fill-color: transparent;
1665
1667
  line-height: 1;
1666
1668
  letter-spacing: -0.05em;
1667
- animation: fadeInUp 0.6s ease-out;
1669
+ animation: fadeIn 0.5s ease-out;
1668
1670
  }
1669
1671
  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;
1672
+ font-size: 32px;
1673
+ font-weight: 700;
1674
+ margin: 24px 0 12px;
1675
+ color: #ededed;
1676
+ animation: fadeIn 0.5s ease-out 0.1s backwards;
1675
1677
  }
1676
1678
  p {
1677
- color: #94A3B8;
1678
- font-size: 18px;
1679
+ color: #888;
1680
+ font-size: 16px;
1679
1681
  line-height: 1.7;
1680
- margin-bottom: 40px;
1681
- animation: fadeInUp 0.6s ease-out 0.2s backwards;
1682
+ margin-bottom: 36px;
1683
+ animation: fadeIn 0.5s ease-out 0.2s backwards;
1682
1684
  }
1683
1685
  code {
1684
- background: rgba(255,255,255,0.1);
1685
- padding: 4px 10px;
1686
+ background: #222;
1687
+ padding: 3px 8px;
1686
1688
  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);
1689
+ font-family: 'JetBrains Mono', monospace;
1690
+ color: #00e87a;
1691
+ font-size: 14px;
1692
+ border: 1px solid #333;
1691
1693
  }
1692
1694
  .btn-group {
1693
1695
  display: flex;
1694
- gap: 16px;
1696
+ gap: 12px;
1695
1697
  justify-content: center;
1696
- flex-wrap: wrap;
1697
- animation: fadeInUp 0.6s ease-out 0.3s backwards;
1698
+ animation: fadeIn 0.5s ease-out 0.3s backwards;
1698
1699
  }
1699
1700
  .btn {
1700
1701
  display: inline-block;
1701
- background: linear-gradient(135deg, #2563EB 0%, #1D4ED8 100%);
1702
+ background: #222;
1702
1703
  color: white;
1703
1704
  text-decoration: none;
1704
- padding: 14px 36px;
1705
- border-radius: 12px;
1705
+ padding: 12px 28px;
1706
+ border-radius: 10px;
1706
1707
  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;
1708
+ font-size: 14px;
1709
+ transition: all 0.2s;
1710
+ border: 1px solid #333;
1711
1711
  cursor: pointer;
1712
1712
  }
1713
- .btn:hover {
1714
- transform: translateY(-2px);
1715
- box-shadow: 0 15px 40px rgba(37, 99, 235, 0.4);
1716
- }
1717
- .btn-outline {
1718
- background: transparent;
1719
- color: #22D3EE;
1720
- border: 2px solid #22D3EE;
1721
- box-shadow: none;
1713
+ .btn:hover { background: #333; }
1714
+ .btn-accent {
1715
+ background: #00e87a;
1716
+ color: #000;
1717
+ border-color: #00e87a;
1722
1718
  }
1723
- .btn-outline:hover {
1724
- background: rgba(34, 211, 238, 0.1);
1725
- box-shadow: 0 10px 30px rgba(34, 211, 238, 0.2);
1726
- }
1727
- @keyframes fadeInUp {
1728
- from {
1729
- opacity: 0;
1730
- transform: translateY(30px);
1731
- }
1732
- to {
1733
- opacity: 1;
1734
- transform: translateY(0);
1735
- }
1719
+ .btn-accent:hover { background: #00d46e; }
1720
+ @keyframes fadeIn {
1721
+ from { opacity: 0; transform: translateY(16px); }
1722
+ to { opacity: 1; transform: translateY(0); }
1736
1723
  }
1737
1724
  </style>
1738
1725
  </head>
1739
1726
  <body>
1740
- <div class="bg-pattern"></div>
1741
1727
  <div class="container">
1742
1728
  <h1>404</h1>
1743
1729
  <h2>Page Not Found</h2>
1744
1730
  <p>The page <code>${pathname}</code> could not be found.<br>It may have been moved or deleted.</p>
1745
1731
  <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>
1732
+ <a href="/" class="btn btn-accent">Return Home</a>
1733
+ <a href="javascript:history.back()" class="btn">Go Back</a>
1748
1734
  </div>
1749
1735
  </div>
1750
1736
  </body>
1751
1737
  </html>`;
1752
1738
  }
1753
1739
  function generate500Page(options) {
1754
- const { title, message, stack, isDev, pathname } = options;
1755
- let callStackCards = "";
1756
- let frameCount = 0;
1740
+ const { message, stack, isDev, pathname } = options;
1741
+ let frames = [];
1742
+ let sourceSnippet = "";
1743
+ let errorLine = 0;
1744
+ let errorFile = "";
1757
1745
  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
- `;
1746
+ const stackLines = stack.split("\\n").filter((line) => line.trim());
1747
+ 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: "" };
1757
+ });
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;
1762
+ if (errorFile && errorLine > 0) {
1763
+ try {
1764
+ const fs8 = __require("fs");
1765
+ if (fs8.existsSync(errorFile)) {
1766
+ const src = fs8.readFileSync(errorFile, "utf-8").split("\\n");
1767
+ const start = Math.max(0, errorLine - 3);
1768
+ const end = Math.min(src.length, errorLine + 3);
1769
+ sourceSnippet = src.slice(start, end).map((l, i) => {
1770
+ const num = start + i + 1;
1771
+ 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>`;
1775
+ }).join("");
1776
+ }
1777
+ } catch (_) {
1778
+ }
1775
1779
  }
1776
- return "";
1777
- }).join("");
1780
+ }
1778
1781
  }
1782
+ const userFrames = frames.filter((f) => f.file && !f.file.includes("node_modules"));
1783
+ const ignoredCount = frames.length - userFrames.length;
1784
+ const shortFile = errorFile.replace(/\\/g, "/").replace(/.*\/(?=app\/|server\/|src\/|pages\/)/, "");
1779
1785
  return `<!DOCTYPE html>
1780
1786
  <html lang="en">
1781
1787
  <head>
1782
1788
  <meta charset="UTF-8">
1783
1789
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1784
- <title>Error | Velix v5</title>
1790
+ <title>Error | Velix v${VERSION}</title>
1785
1791
  <link rel="icon" href="/favicon.webp">
1786
1792
  <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
+ }
1806
+
1787
1807
  * { 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;
1808
+ body {
1809
+ background: var(--bg-body);
1810
+ color: var(--text-main);
1811
+ font-family: var(--font-sans);
1793
1812
  min-height: 100vh;
1794
- overflow-x: hidden;
1795
- }
1796
- .container {
1797
- max-width: 900px;
1798
- margin: 0 auto;
1799
- padding: 40px 20px;
1800
- }
1801
- .error-header {
1802
- background: linear-gradient(135deg, #EF4444 0%, #DC2626 100%);
1803
- color: white;
1804
- padding: 20px 28px;
1805
- border-radius: 16px;
1806
- display: flex;
1807
- 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;
1817
- flex-shrink: 0;
1818
- }
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;
1825
- 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);
1813
+ line-height: 1.7;
1814
+ margin-bottom: 12px;
1815
+ word-break: break-word;
1831
1816
  }
1832
- .route-badge {
1833
- background: linear-gradient(135deg, #0C4A6E 0%, #075985 100%);
1834
- color: #22D3EE;
1835
- padding: 10px 18px;
1836
- border-radius: 10px;
1817
+ .error-hint {
1818
+ color: #22c55e;
1837
1819
  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;
1820
+ margin-bottom: 20px;
1856
1821
  }
1857
- .stack-section {
1858
- margin-top: 32px;
1822
+
1823
+ /* Source block */
1824
+ .source-block {
1825
+ background: #1a1a1a;
1826
+ border: 1px solid #2a2a2a;
1827
+ border-radius: 12px;
1828
+ overflow: hidden;
1829
+ margin-bottom: 24px;
1859
1830
  }
1860
- .stack-header {
1831
+ .source-header {
1861
1832
  display: flex;
1862
1833
  align-items: center;
1863
1834
  justify-content: space-between;
1864
- margin-bottom: 20px;
1835
+ padding: 10px 16px;
1836
+ background: #1e1e1e;
1837
+ border-bottom: 1px solid #2a2a2a;
1838
+ font-size: 13px;
1839
+ color: #aaa;
1865
1840
  }
1866
- .stack-title {
1867
- font-size: 18px;
1868
- font-weight: 700;
1869
- color: #F1F5F9;
1870
- display: flex;
1871
- align-items: center;
1872
- gap: 10px;
1841
+ .source-header .file-icon { color: #666; margin-right: 8px; }
1842
+ .source-header a {
1843
+ color: #666;
1844
+ text-decoration: none;
1845
+ transition: color .15s;
1873
1846
  }
1874
- .frame-counter {
1875
- background: linear-gradient(135deg, #1F2937 0%, #111827 100%);
1876
- color: #22D3EE;
1877
- padding: 6px 14px;
1878
- border-radius: 8px;
1847
+ .source-header a:hover { color: #fff; }
1848
+ .source-code {
1849
+ padding: 12px 0;
1850
+ overflow-x: auto;
1851
+ font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', Menlo, monospace;
1879
1852
  font-size: 13px;
1880
- font-weight: 700;
1881
- border: 1px solid rgba(34, 211, 238, 0.2);
1882
- }
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;
1853
+ line-height: 1.7;
1892
1854
  }
1893
- .error-card.active {
1894
- display: block;
1895
- animation: slideIn 0.3s ease-out;
1855
+ .src-line {
1856
+ padding: 0 16px;
1857
+ white-space: pre;
1858
+ color: #888;
1896
1859
  }
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);
1860
+ .src-err {
1861
+ background: rgba(255,50,50,0.1);
1862
+ color: #ff8a8a;
1863
+ font-weight: 500;
1901
1864
  }
1902
- .card-header {
1865
+
1866
+ /* Call stack */
1867
+ .stack-section { margin-bottom: 24px; }
1868
+ .stack-header {
1903
1869
  display: flex;
1904
- justify-content: space-between;
1905
1870
  align-items: center;
1871
+ justify-content: space-between;
1906
1872
  margin-bottom: 12px;
1907
1873
  }
1908
- .card-title {
1909
- color: #22D3EE;
1874
+ .stack-label {
1910
1875
  font-size: 15px;
1911
1876
  font-weight: 700;
1912
- font-family: 'Courier New', monospace;
1877
+ color: #ededed;
1878
+ display: flex;
1879
+ align-items: center;
1880
+ gap: 8px;
1913
1881
  }
1914
- .card-number {
1915
- background: rgba(34, 211, 238, 0.2);
1916
- color: #22D3EE;
1917
- padding: 4px 10px;
1882
+ .stack-count {
1883
+ background: #2a2a2a;
1884
+ color: #aaa;
1885
+ padding: 2px 8px;
1918
1886
  border-radius: 6px;
1919
1887
  font-size: 12px;
1920
- font-weight: 700;
1888
+ font-weight: 600;
1921
1889
  }
1922
- .card-location {
1923
- color: #94A3B8;
1890
+ .stack-toggle {
1924
1891
  font-size: 13px;
1925
- font-family: 'Courier New', monospace;
1926
- word-break: break-all;
1927
- line-height: 1.6;
1928
- }
1929
- .pagination {
1892
+ color: #888;
1893
+ cursor: pointer;
1894
+ background: none;
1895
+ border: none;
1930
1896
  display: flex;
1931
- justify-content: center;
1932
1897
  align-items: center;
1933
- gap: 12px;
1934
- margin-top: 24px;
1935
- }
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;
1946
- }
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);
1898
+ gap: 4px;
1899
+ transition: color .15s;
1952
1900
  }
1953
- .pagination-btn:disabled {
1954
- opacity: 0.3;
1955
- cursor: not-allowed;
1901
+ .stack-toggle:hover { color: #fff; }
1902
+
1903
+ .frame {
1904
+ padding: 12px 0;
1905
+ border-bottom: 1px solid #1e1e1e;
1956
1906
  }
1957
- .pagination-info {
1958
- color: #94A3B8;
1907
+ .frame:last-child { border-bottom: none; }
1908
+ .frame-name {
1959
1909
  font-size: 14px;
1960
1910
  font-weight: 600;
1961
- }
1962
- .footer {
1963
- margin-top: 48px;
1964
- padding-top: 28px;
1965
- border-top: 1px solid rgba(34, 211, 238, 0.2);
1911
+ color: #ededed;
1966
1912
  display: flex;
1967
- justify-content: space-between;
1968
1913
  align-items: center;
1969
- flex-wrap: wrap;
1970
- gap: 20px;
1914
+ gap: 6px;
1915
+ margin-bottom: 4px;
1971
1916
  }
1972
- .brand {
1973
- display: flex;
1974
- align-items: center;
1975
- gap: 10px;
1976
- font-weight: 700;
1977
- color: #22D3EE;
1978
- font-size: 15px;
1917
+ .frame-name a {
1918
+ color: #666;
1919
+ text-decoration: none;
1920
+ transition: color .15s;
1979
1921
  }
1980
- .brand img {
1981
- width: 20px;
1982
- height: 20px;
1922
+ .frame-name a:hover { color: #fff; }
1923
+ .frame-loc {
1924
+ font-size: 13px;
1925
+ color: #666;
1926
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
1983
1927
  }
1984
- .footer-links {
1928
+
1929
+ /* Footer */
1930
+ .footer-row {
1985
1931
  display: flex;
1986
- gap: 24px;
1987
- flex-wrap: wrap;
1932
+ justify-content: flex-end;
1933
+ padding: 16px 0;
1934
+ border-top: 1px solid #1e1e1e;
1935
+ margin-top: 8px;
1988
1936
  }
1989
- .footer-links a {
1990
- color: #60A5FA;
1937
+ .footer-row a {
1938
+ color: #ff6b6b;
1991
1939
  text-decoration: none;
1992
- 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);
1940
+ font-size: 13px;
1941
+ font-weight: 500;
1942
+ display: flex;
1943
+ align-items: center;
1944
+ gap: 6px;
1945
+ transition: opacity .15s;
1999
1946
  }
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;
1947
+ .footer-row a:hover { opacity: 0.8; }
1948
+
1949
+ /* Prod mode */
1950
+ .prod-card {
1951
+ background: #1a1a1a;
1952
+ border: 1px solid #2a2a2a;
1953
+ border-radius: 16px;
1954
+ padding: 48px 32px;
2006
1955
  text-align: center;
2007
- font-size: 15px;
1956
+ margin-top: 32px;
2008
1957
  }
2009
- @keyframes slideIn {
2010
- from {
2011
- opacity: 0;
2012
- transform: translateY(10px);
2013
- }
2014
- to {
2015
- opacity: 1;
2016
- transform: translateY(0);
2017
- }
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;
2018
1971
  }
1972
+ .prod-card a:hover { background: #333; }
2019
1973
  </style>
2020
1974
  </head>
2021
1975
  <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>
1976
+ <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>
2028
1988
  </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>
1989
+
1990
+ <!-- Error Tag -->
1991
+ <div class="error-tag">Unhandled Runtime Error</div>
1992
+
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>` : ""}
1996
+
1997
+ ${isDev && sourceSnippet ? `
1998
+ <!-- Source Code Block -->
1999
+ <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>
2035
2005
  </div>
2036
-
2037
- ${isDev && callStackCards ? `
2006
+ ` : ""}
2007
+
2008
+ ${isDev && frames.length > 0 ? `
2009
+ <!-- Call Stack -->
2038
2010
  <div class="stack-section">
2039
2011
  <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>
2012
+ <div class="stack-label">
2044
2013
  Call Stack
2014
+ <span class="stack-count">${frames.length}</span>
2045
2015
  </div>
2046
- <div class="frame-counter">${frameCount}</div>
2016
+ ${ignoredCount > 0 ? `<button class="stack-toggle" onclick="toggleIgnored()">Show ${ignoredCount} ignore-listed frames \u21D5</button>` : ""}
2047
2017
  </div>
2048
- <div id="error-cards">
2049
- ${callStackCards}
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("")}
2050
2028
  </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>
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>
2061
2036
  </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>
2037
+ `).join("")}
2068
2038
  </div>
2069
- ` : ""}
2070
2039
  </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>' : ""}
2083
- </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
+
2048
+ <!-- Footer -->
2049
+ <div class="footer-row">
2050
+ <a href="javascript:location.reload()">Was this helpful? \u21BB</a>
2084
2051
  </div>
2085
2052
  </div>
2086
-
2087
- ${isDev && frameCount > 0 ? `
2053
+
2088
2054
  <script>
2089
2055
  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);
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;
2115
2066
  }
2116
2067
  }
2117
-
2118
- // Show first page on load
2119
- showPage(1);
2120
-
2121
- // Keyboard navigation
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
+
2122
2074
  document.addEventListener('keydown', (e) => {
2123
2075
  if (e.key === 'ArrowLeft') changePage(-1);
2124
2076
  if (e.key === 'ArrowRight') changePage(1);
2125
2077
  });
2126
2078
  </script>
2127
- ` : ""}
2128
2079
  </body>
2129
2080
  </html>`;
2130
2081
  }