@tomehq/theme 0.4.0 → 0.6.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +1 -96
  2. package/dist/{chunk-DKSQZLWR.js → chunk-NIWVZJLG.js} +150 -45
  3. package/dist/entry.js +1 -1
  4. package/dist/index.d.ts +78 -0
  5. package/dist/index.js +1 -1
  6. package/package.json +3 -3
  7. package/src/Shell.test.tsx +30 -0
  8. package/src/Shell.tsx +35 -17
  9. package/src/entry.tsx +2 -3
  10. package/src/presets.test.ts +4 -2
  11. package/src/presets.ts +30 -0
  12. package/dist/chunk-2APCPR2Y.js +0 -2110
  13. package/dist/chunk-2AXAEADQ.js +0 -2525
  14. package/dist/chunk-2WNOJXK3.js +0 -2581
  15. package/dist/chunk-37JI6XGT.js +0 -1720
  16. package/dist/chunk-3A2LPGUL.js +0 -1991
  17. package/dist/chunk-3I2QTWTW.js +0 -1948
  18. package/dist/chunk-3I4SJMER.js +0 -2538
  19. package/dist/chunk-45M5UIAB.js +0 -2110
  20. package/dist/chunk-462AGU3S.js +0 -1959
  21. package/dist/chunk-5GVQFIPI.js +0 -2581
  22. package/dist/chunk-7MUTU5D4.js +0 -1720
  23. package/dist/chunk-7NQ4IMDY.js +0 -2294
  24. package/dist/chunk-ABNPB6BB.js +0 -2133
  25. package/dist/chunk-BZGWSKT2.js +0 -573
  26. package/dist/chunk-BZIB2LMI.js +0 -2519
  27. package/dist/chunk-CMQCNCSY.js +0 -2127
  28. package/dist/chunk-CTPOZMMK.js +0 -1703
  29. package/dist/chunk-DO544M3G.js +0 -1702
  30. package/dist/chunk-DPKZBFQP.js +0 -1777
  31. package/dist/chunk-EK7PZUEB.js +0 -2147
  32. package/dist/chunk-FMOLIHQF.js +0 -2182
  33. package/dist/chunk-FWBTK5TL.js +0 -1444
  34. package/dist/chunk-GDQIBNX5.js +0 -1962
  35. package/dist/chunk-GHQ2MODM.js +0 -2127
  36. package/dist/chunk-GR2WCRGK.js +0 -2182
  37. package/dist/chunk-H5XZVNBW.js +0 -2291
  38. package/dist/chunk-HNLKDQ64.js +0 -2139
  39. package/dist/chunk-INUMUXN5.js +0 -2095
  40. package/dist/chunk-IW3NHNOQ.js +0 -2187
  41. package/dist/chunk-JA4PMX6M.js +0 -1500
  42. package/dist/chunk-JSPFS7G5.js +0 -2102
  43. package/dist/chunk-JZRT4WNC.js +0 -1441
  44. package/dist/chunk-KQBY2JDB.js +0 -2112
  45. package/dist/chunk-LIMYFTPC.js +0 -1468
  46. package/dist/chunk-LIY62BGC.js +0 -2519
  47. package/dist/chunk-MEP7P6A7.js +0 -1500
  48. package/dist/chunk-MHYKO7KM.js +0 -2570
  49. package/dist/chunk-MSXVVBDW.js +0 -2542
  50. package/dist/chunk-NOZBIES7.js +0 -1948
  51. package/dist/chunk-O4GH3KYX.js +0 -1712
  52. package/dist/chunk-OEDJTH5F.js +0 -2569
  53. package/dist/chunk-OEXM3BEC.js +0 -1702
  54. package/dist/chunk-PGKSFQ7A.js +0 -2459
  55. package/dist/chunk-PIV6CPY2.js +0 -2395
  56. package/dist/chunk-Q7PYTVW3.js +0 -1771
  57. package/dist/chunk-QCWZYABW.js +0 -1468
  58. package/dist/chunk-QYINBNMJ.js +0 -2545
  59. package/dist/chunk-RDF25WB2.js +0 -2085
  60. package/dist/chunk-RKTT3ZEX.js +0 -1500
  61. package/dist/chunk-S47BRMNQ.js +0 -1715
  62. package/dist/chunk-S4ZH5F56.js +0 -1949
  63. package/dist/chunk-SRD7NJHS.js +0 -1949
  64. package/dist/chunk-SWFYJO5H.js +0 -2187
  65. package/dist/chunk-TQDWPSTO.js +0 -2087
  66. package/dist/chunk-TTRXRPP6.js +0 -1941
  67. package/dist/chunk-UKYFJSUA.js +0 -509
  68. package/dist/chunk-VKEQHP2E.js +0 -2133
  69. package/dist/chunk-VUT2FMSI.js +0 -1937
  70. package/dist/chunk-VVCC5JHK.js +0 -1949
  71. package/dist/chunk-W732TVBK.js +0 -1944
  72. package/dist/chunk-X4VQYPKO.js +0 -1768
  73. package/dist/chunk-YX7HV4EP.js +0 -2568
  74. package/dist/chunk-YXKONM3A.js +0 -2192
  75. package/dist/chunk-YZ3P3TNS.js +0 -1760
  76. package/dist/chunk-ZVZ7JN3V.js +0 -2568
  77. package/dist/chunk-ZXW4STTN.js +0 -2568
package/CHANGELOG.md CHANGED
@@ -1,96 +1 @@
1
- # @tomehq/theme
2
-
3
- ## 0.4.0
4
-
5
- ### Minor Changes
6
-
7
- - c4eae3b: Fix client-side navigation race conditions, content flash, and feedback section disappearing
8
-
9
- **Theme:**
10
-
11
- - Replace AbortController with navigation counter pattern for reliable race condition protection during rapid sidebar navigation
12
- - Switch content innerHTML update from `useEffect` to `useLayoutEffect` to prevent visual flash artifacts (thumbs up/down flicker)
13
- - Add `key={currentPageId}` on content div to force DOM recreation on page change
14
- - Change `overflow: hidden` to `overflow: clip` on root container to prevent feedback section from disappearing on scroll
15
- - Move `setCurrentPageId` after page load completes (load-before-push) so sidebar highlighting stays correct during navigation
16
- - Remove stale HTML comparison guard that prevented content updates during rapid navigation
17
- - Add error classes (PageNotFoundError, PageLoadError, NavigationCancelledError) for better error handling
18
-
19
- **Core:**
20
-
21
- - Page loader virtual module now throws `Error("Unknown page: " + id)` for unknown page IDs instead of silently returning null
22
-
23
- **CLI:**
24
-
25
- - Restructure `tome init` scaffolding to use Diataxis framework (tutorials, guides, reference, concepts)
26
- - Add comprehensive starter pages for each Diataxis category
27
- - Add create-tome template with same Diataxis structure
28
-
29
- ### Patch Changes
30
-
31
- - Updated dependencies [c4eae3b]
32
- - @tomehq/core@0.3.4
33
-
34
- ## 0.3.4
35
-
36
- ### Patch Changes
37
-
38
- - f03e6c2: Fix page scrolling beyond viewport bounds in all directions. Lock layout to 100vh with overflow hidden on html, body, and root container.
39
-
40
- ## 0.3.3
41
-
42
- ### Patch Changes
43
-
44
- - 062349c: Fix MCP server stdout corruption, add dashboard mobile responsiveness, improve test coverage, and update docs.
45
-
46
- - Fix: MCP CLI banner no longer writes to stdout, preventing JSON-RPC protocol corruption
47
- - Fix: API Playground prop wiring and githubSource route crash
48
- - Feat: MCP server `createMcpServer()` exported for programmatic use with graceful shutdown
49
- - Feat: Dashboard mobile-responsive layout with media query breakpoints
50
- - Feat: 13 MCP server integration tests using InMemoryTransport
51
- - Docs: Add missing typedoc CLI command, fix social link examples, update package list
52
-
53
- - Updated dependencies [062349c]
54
- - @tomehq/core@0.3.3
55
- - @tomehq/components@0.3.3
56
-
57
- ## 0.2.8
58
-
59
- ### Minor Changes
60
-
61
- - Replace hash-based SPA routing with History API (pushState + popstate + pathname parsing)
62
- - Content link interception: in-content markdown links navigate via SPA instead of full page reload
63
- - Banner link internal navigation support
64
- - Algolia search basePath stripping for correct page ID extraction
65
- - Extract routing helpers (`pathnameToPageId`, `pageIdToPath`) into testable `routing.ts` module
66
- - Extract entry helpers (`loadPage`, `computeEditUrl`, `resolveInitialPageId`, `detectCurrentVersion`) into testable `entry-helpers.ts` module
67
- - Pass `basePath` prop through Shell for correct URL construction
68
- - Updated dependencies
69
- - @tomehq/core@0.2.8
70
- - @tomehq/components@0.2.8
71
-
72
- ## 0.2.0
73
-
74
- ### Minor Changes
75
-
76
- - Shell: logo links back to landing page, dynamic version in sidebar footer
77
- - Shell: edit link support, table of contents depth config, changelog page layout
78
- - Entry: pass new config fields (editLink, tableOfContents, plugins) to Shell
79
- - Updated dependencies
80
- - @tomehq/core@0.2.0
81
- - @tomehq/components@0.2.0
82
-
83
- ## 0.1.2
84
-
85
- ### Patch Changes
86
-
87
- - Updated dependencies
88
- - @tomehq/components@0.1.1
89
-
90
- ## 0.1.1
91
-
92
- ### Patch Changes
93
-
94
- - Fix bugs found in functionality audit: invalid search provider, AI key naming mismatch, hardcoded version. Remove dead billing stubs. Add 57 API route tests.
95
- - Updated dependencies
96
- - @tomehq/core@0.1.1
1
+ # Changelog
@@ -76,6 +76,76 @@ var THEME_PRESETS = {
76
76
  hdBg: "rgba(246,244,240,0.92)"
77
77
  },
78
78
  fonts: { heading: "Cormorant Garamond", body: "Bricolage Grotesque", code: "Fira Code" }
79
+ },
80
+ cipher: {
81
+ dark: {
82
+ bg: "#050508",
83
+ sf: "#0c0c12",
84
+ sfH: "#12121a",
85
+ bd: "#1a1a25",
86
+ tx: "#d4ff00",
87
+ tx2: "#8a90a0",
88
+ txM: "#6a7080",
89
+ ac: "#6666ff",
90
+ acD: "rgba(102,102,255,0.10)",
91
+ acT: "#8080ff",
92
+ cdBg: "#08080e",
93
+ cdTx: "#b0c870",
94
+ sbBg: "#08080d",
95
+ hdBg: "rgba(5,5,8,0.88)"
96
+ },
97
+ light: {
98
+ bg: "#f0f2f5",
99
+ sf: "#ffffff",
100
+ sfH: "#e8eaef",
101
+ bd: "#d0d4db",
102
+ tx: "#0f1219",
103
+ tx2: "#4a5060",
104
+ txM: "#6a7080",
105
+ ac: "#2020cc",
106
+ acD: "rgba(32,32,204,0.08)",
107
+ acT: "#1a1aa8",
108
+ cdBg: "#e6e9ef",
109
+ cdTx: "#2a3520",
110
+ sbBg: "#ebedf2",
111
+ hdBg: "rgba(240,242,245,0.90)"
112
+ },
113
+ fonts: { heading: "Bodoni Moda", body: "Space Grotesk", code: "Source Code Pro" }
114
+ },
115
+ mint: {
116
+ dark: {
117
+ bg: "#0d1117",
118
+ sf: "#161b22",
119
+ sfH: "#1c2129",
120
+ bd: "#21262d",
121
+ tx: "#e6edf3",
122
+ tx2: "#8b949e",
123
+ txM: "#6e7681",
124
+ ac: "#0ea371",
125
+ acD: "rgba(14,163,113,0.10)",
126
+ acT: "#2dd4a0",
127
+ cdBg: "#0a0e14",
128
+ cdTx: "#adbac7",
129
+ sbBg: "#0d1117",
130
+ hdBg: "rgba(13,17,23,0.88)"
131
+ },
132
+ light: {
133
+ bg: "#ffffff",
134
+ sf: "#f6f8fa",
135
+ sfH: "#eef1f5",
136
+ bd: "#d8dee4",
137
+ tx: "#1f2328",
138
+ tx2: "#59636e",
139
+ txM: "#6e7681",
140
+ ac: "#0a7b53",
141
+ acD: "rgba(10,123,83,0.07)",
142
+ acT: "#087a50",
143
+ cdBg: "#f0f3f6",
144
+ cdTx: "#24292f",
145
+ sbBg: "#f6f8fa",
146
+ hdBg: "rgba(255,255,255,0.90)"
147
+ },
148
+ fonts: { heading: "Inter", body: "Inter", code: "Fira Code" }
79
149
  }
80
150
  };
81
151
 
@@ -842,7 +912,7 @@ function Shell({
842
912
  const [isDark, setDark] = useState2(() => {
843
913
  if (themeMode === "dark") return true;
844
914
  if (themeMode === "light") return false;
845
- return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? true;
915
+ return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
846
916
  });
847
917
  const [mobile, setMobile] = useState2(() => typeof window !== "undefined" && window.innerWidth < 768);
848
918
  const [sbOpen, setSb] = useState2(() => typeof window !== "undefined" && window.innerWidth >= 768);
@@ -1026,8 +1096,8 @@ function Shell({
1026
1096
  }, []);
1027
1097
  const allNavPages = navigation2.flatMap((g) => g.pages);
1028
1098
  const idx = allNavPages.findIndex((p) => p.id === currentPageId);
1029
- const prev = idx > 0 ? allNavPages[idx - 1] : null;
1030
- const next = idx < allNavPages.length - 1 ? allNavPages[idx + 1] : null;
1099
+ const prev = idx > 0 ? allNavPages[idx - 1] : allNavPages[allNavPages.length - 1] ?? null;
1100
+ const next = idx < allNavPages.length - 1 ? allNavPages[idx + 1] : allNavPages[0] ?? null;
1031
1101
  const breadcrumbs = getBreadcrumbs(navigation2, currentPageId, pageTitle);
1032
1102
  const togSec = (s) => setExpanded((p) => p.includes(s) ? p.filter((x) => x !== s) : [...p, s]);
1033
1103
  const cssVars = {
@@ -1589,7 +1659,7 @@ function Shell({
1589
1659
  }
1590
1660
  ),
1591
1661
  /* @__PURE__ */ jsxs2("div", { ref: contentRef, style: { flex: 1, overflow: "auto", display: "flex" }, children: [
1592
- /* @__PURE__ */ jsxs2("main", { style: { flex: 1, maxWidth: mobile ? "100%" : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }, children: [
1662
+ /* @__PURE__ */ jsxs2("main", { style: { flex: 1, maxWidth: mobile ? "100%" : apiManifest ? 1100 : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }, children: [
1593
1663
  breadcrumbs.length > 0 && /* @__PURE__ */ jsx2("nav", { "aria-label": "Breadcrumbs", "data-testid": "breadcrumbs", style: {
1594
1664
  display: "flex",
1595
1665
  alignItems: "center",
@@ -1627,7 +1697,7 @@ function Shell({
1627
1697
  currentPageId
1628
1698
  )
1629
1699
  ) }),
1630
- overrides2?.PageFooter ? /* @__PURE__ */ jsx2(
1700
+ !pageHtml && !pageComponent ? null : overrides2?.PageFooter ? /* @__PURE__ */ jsx2(
1631
1701
  overrides2.PageFooter,
1632
1702
  {
1633
1703
  editUrl,
@@ -1704,43 +1774,79 @@ function Shell({
1704
1774
  transition: "border-color .15s"
1705
1775
  }, children: "\u{1F44E}" })
1706
1776
  ] }) }),
1707
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: mobile ? "column" : "row", justifyContent: "space-between", marginTop: 16, paddingTop: 24, borderTop: "1px solid var(--bd)", gap: mobile ? 12 : 16 }, children: [
1708
- prev ? /* @__PURE__ */ jsxs2("button", { onClick: () => onNavigate(prev.id), style: {
1709
- display: "flex",
1710
- alignItems: "center",
1711
- gap: 8,
1712
- background: "none",
1713
- border: "1px solid var(--bd)",
1714
- borderRadius: 2,
1715
- padding: "10px 16px",
1716
- cursor: "pointer",
1717
- color: "var(--tx2)",
1718
- fontSize: 13,
1719
- fontFamily: "var(--font-body)",
1720
- transition: "border-color .15s, color .15s"
1721
- }, children: [
1722
- isRtl ? /* @__PURE__ */ jsx2(ArrowRight, {}) : /* @__PURE__ */ jsx2(ArrowLeft, {}),
1723
- " ",
1724
- prev.title
1725
- ] }) : /* @__PURE__ */ jsx2("div", {}),
1726
- next ? /* @__PURE__ */ jsxs2("button", { onClick: () => onNavigate(next.id), style: {
1727
- display: "flex",
1728
- alignItems: "center",
1729
- gap: 8,
1730
- background: "none",
1731
- border: "1px solid var(--bd)",
1732
- borderRadius: 2,
1733
- padding: "10px 16px",
1734
- cursor: "pointer",
1735
- color: "var(--tx2)",
1736
- fontSize: 13,
1737
- fontFamily: "var(--font-body)",
1738
- transition: "border-color .15s, color .15s"
1739
- }, children: [
1740
- next.title,
1741
- " ",
1742
- isRtl ? /* @__PURE__ */ jsx2(ArrowLeft, {}) : /* @__PURE__ */ jsx2(ArrowRight, {})
1743
- ] }) : /* @__PURE__ */ jsx2("div", {})
1777
+ /* @__PURE__ */ jsxs2("div", { style: { display: "grid", gridTemplateColumns: mobile ? "1fr" : "1fr 1fr", marginTop: 24, paddingTop: 32, paddingBottom: 40, borderTop: "1px solid var(--bd)", gap: 16 }, children: [
1778
+ prev ? /* @__PURE__ */ jsxs2(
1779
+ "button",
1780
+ {
1781
+ onClick: () => onNavigate(prev.id),
1782
+ style: {
1783
+ display: "flex",
1784
+ flexDirection: "column",
1785
+ alignItems: "flex-start",
1786
+ gap: 6,
1787
+ background: "var(--sf)",
1788
+ border: "1px solid var(--bd)",
1789
+ borderRadius: 8,
1790
+ padding: "16px 20px",
1791
+ cursor: "pointer",
1792
+ textAlign: "left",
1793
+ fontFamily: "var(--font-body)",
1794
+ transition: "all .3s ease",
1795
+ boxShadow: "0 2px 8px rgba(0,0,0,0.06)"
1796
+ },
1797
+ onMouseOver: (e) => {
1798
+ e.currentTarget.style.borderColor = "var(--ac)";
1799
+ e.currentTarget.style.boxShadow = "0 4px 16px rgba(0,0,0,0.1)";
1800
+ },
1801
+ onMouseOut: (e) => {
1802
+ e.currentTarget.style.borderColor = "var(--bd)";
1803
+ e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.06)";
1804
+ },
1805
+ children: [
1806
+ /* @__PURE__ */ jsxs2("span", { style: { fontSize: 11, color: "var(--txM)", textTransform: "uppercase", letterSpacing: "0.5px", display: "flex", alignItems: "center", gap: 4 }, children: [
1807
+ isRtl ? /* @__PURE__ */ jsx2(ArrowRight, {}) : /* @__PURE__ */ jsx2(ArrowLeft, {}),
1808
+ " Previous"
1809
+ ] }),
1810
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 14, fontWeight: 500, color: "var(--tx)" }, children: prev.title })
1811
+ ]
1812
+ }
1813
+ ) : /* @__PURE__ */ jsx2("div", {}),
1814
+ next ? /* @__PURE__ */ jsxs2(
1815
+ "button",
1816
+ {
1817
+ onClick: () => onNavigate(next.id),
1818
+ style: {
1819
+ display: "flex",
1820
+ flexDirection: "column",
1821
+ alignItems: "flex-end",
1822
+ gap: 6,
1823
+ background: "var(--sf)",
1824
+ border: "1px solid var(--bd)",
1825
+ borderRadius: 8,
1826
+ padding: "16px 20px",
1827
+ cursor: "pointer",
1828
+ textAlign: "right",
1829
+ fontFamily: "var(--font-body)",
1830
+ transition: "all .3s ease",
1831
+ boxShadow: "0 2px 8px rgba(0,0,0,0.06)"
1832
+ },
1833
+ onMouseOver: (e) => {
1834
+ e.currentTarget.style.borderColor = "var(--ac)";
1835
+ e.currentTarget.style.boxShadow = "0 4px 16px rgba(0,0,0,0.1)";
1836
+ },
1837
+ onMouseOut: (e) => {
1838
+ e.currentTarget.style.borderColor = "var(--bd)";
1839
+ e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.06)";
1840
+ },
1841
+ children: [
1842
+ /* @__PURE__ */ jsxs2("span", { style: { fontSize: 11, color: "var(--txM)", textTransform: "uppercase", letterSpacing: "0.5px", display: "flex", alignItems: "center", gap: 4 }, children: [
1843
+ "Next ",
1844
+ isRtl ? /* @__PURE__ */ jsx2(ArrowLeft, {}) : /* @__PURE__ */ jsx2(ArrowRight, {})
1845
+ ] }),
1846
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 14, fontWeight: 500, color: "var(--tx)" }, children: next.title })
1847
+ ]
1848
+ }
1849
+ ) : /* @__PURE__ */ jsx2("div", {})
1744
1850
  ] })
1745
1851
  ] })
1746
1852
  ] }),
@@ -2064,14 +2170,13 @@ var MDX_COMPONENTS = {
2064
2170
  CardGrid
2065
2171
  };
2066
2172
  var contentStyles = `
2067
- @import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@300;400;500;600;700&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400;1,700&family=Fira+Code:wght@400;500;600&display=swap');
2173
+ @import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=DM+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Bricolage+Grotesque:wght@300;400;500;600;700&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400;1,700&family=Fira+Code:wght@400;500;600&family=Bodoni+Moda:ital,wght@0,400;0,700;0,900;1,400&family=Space+Grotesk:wght@400;500;600;700&family=Source+Code+Pro:wght@400;500;600&family=Inter:wght@300;400;500;600;700&display=swap');
2068
2174
 
2069
2175
  html, body { margin: 0; padding: 0; height: 100%; overflow: clip; }
2070
2176
  #tome-root { height: 100%; overflow: clip; }
2071
2177
 
2072
2178
  .tome-content h1 { display: none; }
2073
- .tome-content h2 { font-family: var(--font-body); font-size: 1.35em; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; display: flex; align-items: center; gap: 10px; letter-spacing: 0.01em; }
2074
- .tome-content h2::before { content: "#"; font-family: var(--font-heading); font-size: 1.2em; font-weight: 300; font-style: italic; color: var(--ac); opacity: 0.5; }
2179
+ .tome-content h2 { font-family: var(--font-body); font-size: 1.35em; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; letter-spacing: 0.01em; }
2075
2180
  .tome-content h3 { font-family: var(--font-body); font-size: 1.15em; font-weight: 600; margin-top: 1.5em; margin-bottom: 0.5em; }
2076
2181
  .tome-content h4 { font-family: var(--font-body); font-size: 1.05em; font-weight: 600; margin-top: 1.2em; margin-bottom: 0.5em; }
2077
2182
  .tome-content p { color: var(--tx2); line-height: 1.8; margin-bottom: 1em; font-size: 14.5px; }
package/dist/entry.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  entry_default
3
- } from "./chunk-DKSQZLWR.js";
3
+ } from "./chunk-NIWVZJLG.js";
4
4
  export {
5
5
  entry_default as default
6
6
  };
package/dist/index.d.ts CHANGED
@@ -252,6 +252,84 @@ declare const THEME_PRESETS: {
252
252
  readonly code: "Fira Code";
253
253
  };
254
254
  };
255
+ readonly cipher: {
256
+ readonly dark: {
257
+ readonly bg: "#050508";
258
+ readonly sf: "#0c0c12";
259
+ readonly sfH: "#12121a";
260
+ readonly bd: "#1a1a25";
261
+ readonly tx: "#d4ff00";
262
+ readonly tx2: "#8a90a0";
263
+ readonly txM: "#6a7080";
264
+ readonly ac: "#6666ff";
265
+ readonly acD: "rgba(102,102,255,0.10)";
266
+ readonly acT: "#8080ff";
267
+ readonly cdBg: "#08080e";
268
+ readonly cdTx: "#b0c870";
269
+ readonly sbBg: "#08080d";
270
+ readonly hdBg: "rgba(5,5,8,0.88)";
271
+ };
272
+ readonly light: {
273
+ readonly bg: "#f0f2f5";
274
+ readonly sf: "#ffffff";
275
+ readonly sfH: "#e8eaef";
276
+ readonly bd: "#d0d4db";
277
+ readonly tx: "#0f1219";
278
+ readonly tx2: "#4a5060";
279
+ readonly txM: "#6a7080";
280
+ readonly ac: "#2020cc";
281
+ readonly acD: "rgba(32,32,204,0.08)";
282
+ readonly acT: "#1a1aa8";
283
+ readonly cdBg: "#e6e9ef";
284
+ readonly cdTx: "#2a3520";
285
+ readonly sbBg: "#ebedf2";
286
+ readonly hdBg: "rgba(240,242,245,0.90)";
287
+ };
288
+ readonly fonts: {
289
+ readonly heading: "Bodoni Moda";
290
+ readonly body: "Space Grotesk";
291
+ readonly code: "Source Code Pro";
292
+ };
293
+ };
294
+ readonly mint: {
295
+ readonly dark: {
296
+ readonly bg: "#0d1117";
297
+ readonly sf: "#161b22";
298
+ readonly sfH: "#1c2129";
299
+ readonly bd: "#21262d";
300
+ readonly tx: "#e6edf3";
301
+ readonly tx2: "#8b949e";
302
+ readonly txM: "#6e7681";
303
+ readonly ac: "#0ea371";
304
+ readonly acD: "rgba(14,163,113,0.10)";
305
+ readonly acT: "#2dd4a0";
306
+ readonly cdBg: "#0a0e14";
307
+ readonly cdTx: "#adbac7";
308
+ readonly sbBg: "#0d1117";
309
+ readonly hdBg: "rgba(13,17,23,0.88)";
310
+ };
311
+ readonly light: {
312
+ readonly bg: "#ffffff";
313
+ readonly sf: "#f6f8fa";
314
+ readonly sfH: "#eef1f5";
315
+ readonly bd: "#d8dee4";
316
+ readonly tx: "#1f2328";
317
+ readonly tx2: "#59636e";
318
+ readonly txM: "#6e7681";
319
+ readonly ac: "#0a7b53";
320
+ readonly acD: "rgba(10,123,83,0.07)";
321
+ readonly acT: "#087a50";
322
+ readonly cdBg: "#f0f3f6";
323
+ readonly cdTx: "#24292f";
324
+ readonly sbBg: "#f6f8fa";
325
+ readonly hdBg: "rgba(255,255,255,0.90)";
326
+ };
327
+ readonly fonts: {
328
+ readonly heading: "Inter";
329
+ readonly body: "Inter";
330
+ readonly code: "Fira Code";
331
+ };
332
+ };
255
333
  };
256
334
  type PresetName = keyof typeof THEME_PRESETS;
257
335
 
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  Shell,
4
4
  THEME_PRESETS,
5
5
  entry_default
6
- } from "./chunk-DKSQZLWR.js";
6
+ } from "./chunk-NIWVZJLG.js";
7
7
  export {
8
8
  AiChat,
9
9
  entry_default as App,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomehq/theme",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Tome default theme and React app shell",
5
5
  "type": "module",
6
6
  "main": "./src/index.tsx",
@@ -9,8 +9,8 @@
9
9
  "./entry": "./src/entry.tsx"
10
10
  },
11
11
  "dependencies": {
12
- "@tomehq/core": "0.3.4",
13
- "@tomehq/components": "0.3.3"
12
+ "@tomehq/components": "0.6.0",
13
+ "@tomehq/core": "0.6.0"
14
14
  },
15
15
  "peerDependencies": {
16
16
  "react": "^18.0.0 || ^19.0.0",
@@ -180,6 +180,18 @@ describe("Shell theme mode", () => {
180
180
  const buttons = footer?.querySelectorAll("button");
181
181
  expect(buttons?.length).toBeGreaterThan(0);
182
182
  });
183
+
184
+ it("defaults to light mode when mode is 'auto' and system preference is unavailable", () => {
185
+ const { container } = renderShell({
186
+ config: { ...baseConfig, theme: { preset: "amber", mode: "auto" } },
187
+ });
188
+ // matchMedia mock returns matches: false → light mode
189
+ // In light mode, the root container uses light theme background
190
+ const root = container.firstElementChild as HTMLElement;
191
+ const bg = root?.style.getPropertyValue("--bg");
192
+ // Amber light bg is #fafaf9 (not dark bg #09090b)
193
+ expect(bg).toBe("#fafaf9");
194
+ });
183
195
  });
184
196
 
185
197
  // ── TOC (TOM-52) ──────────────────────────────────────────
@@ -1211,6 +1223,24 @@ describe("Shell API reference rendering", () => {
1211
1223
  expect(screen.queryByTestId("api-playground")).not.toBeInTheDocument();
1212
1224
  expect(screen.queryByTestId("api-auth")).not.toBeInTheDocument();
1213
1225
  });
1226
+
1227
+ it("uses wider max-width for API reference pages", () => {
1228
+ const { container } = renderShell({
1229
+ apiManifest: mockManifest,
1230
+ ApiReferenceComponent: MockApiRef,
1231
+ pageHtml: undefined,
1232
+ });
1233
+ const main = container.querySelector("main") as HTMLElement;
1234
+ expect(main.style.maxWidth).toBe("1100px");
1235
+ });
1236
+
1237
+ it("uses standard max-width for non-API pages", () => {
1238
+ const { container } = renderShell({
1239
+ pageHtml: "<p>Regular prose content</p>",
1240
+ });
1241
+ const main = container.querySelector("main") as HTMLElement;
1242
+ expect(main.style.maxWidth).toBe("760px");
1243
+ });
1214
1244
  });
1215
1245
 
1216
1246
  // ── Content link interception ───────────────────────────────
package/src/Shell.tsx CHANGED
@@ -413,7 +413,7 @@ export function Shell({
413
413
  const [isDark, setDark] = useState(() => {
414
414
  if (themeMode === "dark") return true;
415
415
  if (themeMode === "light") return false;
416
- return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? true;
416
+ return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
417
417
  });
418
418
 
419
419
  const [mobile, setMobile] = useState(() => typeof window !== "undefined" && window.innerWidth < 768);
@@ -643,8 +643,8 @@ export function Shell({
643
643
  // Prev / Next
644
644
  const allNavPages = navigation.flatMap(g => g.pages);
645
645
  const idx = allNavPages.findIndex(p => p.id === currentPageId);
646
- const prev = idx > 0 ? allNavPages[idx - 1] : null;
647
- const next = idx < allNavPages.length - 1 ? allNavPages[idx + 1] : null;
646
+ const prev = idx > 0 ? allNavPages[idx - 1] : allNavPages[allNavPages.length - 1] ?? null;
647
+ const next = idx < allNavPages.length - 1 ? allNavPages[idx + 1] : allNavPages[0] ?? null;
648
648
 
649
649
  // Breadcrumbs
650
650
  const breadcrumbs = getBreadcrumbs(navigation, currentPageId, pageTitle);
@@ -1127,7 +1127,7 @@ export function Shell({
1127
1127
 
1128
1128
  {/* Content + TOC */}
1129
1129
  <div ref={contentRef} style={{ flex: 1, overflow: "auto", display: "flex" }}>
1130
- <main style={{ flex: 1, maxWidth: mobile ? "100%" : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }}>
1130
+ <main style={{ flex: 1, maxWidth: mobile ? "100%" : apiManifest ? 1100 : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }}>
1131
1131
  {breadcrumbs.length > 0 && (
1132
1132
  <nav aria-label="Breadcrumbs" data-testid="breadcrumbs" style={{
1133
1133
  display: "flex", alignItems: "center", gap: 6,
@@ -1186,7 +1186,7 @@ export function Shell({
1186
1186
  </div>
1187
1187
 
1188
1188
  {/* TOM-48: Edit this page link + TOM-54: Last updated + Feedback + Prev/Next */}
1189
- {overrides?.PageFooter ? (
1189
+ {!pageHtml && !pageComponent ? null : overrides?.PageFooter ? (
1190
1190
  <overrides.PageFooter
1191
1191
  editUrl={editUrl}
1192
1192
  lastUpdated={lastUpdated}
@@ -1243,23 +1243,41 @@ export function Shell({
1243
1243
  )}
1244
1244
  </div>
1245
1245
 
1246
- {/* Prev / Next */}
1247
- <div style={{ display: "flex", flexDirection: mobile ? "column" : "row", justifyContent: "space-between", marginTop: 16, paddingTop: 24, borderTop: "1px solid var(--bd)", gap: mobile ? 12 : 16 }}>
1246
+ {/* Prev / Next link cards */}
1247
+ <div style={{ display: "grid", gridTemplateColumns: mobile ? "1fr" : "1fr 1fr", marginTop: 24, paddingTop: 32, paddingBottom: 40, borderTop: "1px solid var(--bd)", gap: 16 }}>
1248
1248
  {prev ? (
1249
1249
  <button onClick={() => onNavigate(prev.id)} style={{
1250
- display: "flex", alignItems: "center", gap: 8, background: "none",
1251
- border: "1px solid var(--bd)", borderRadius: 2, padding: "10px 16px",
1252
- cursor: "pointer", color: "var(--tx2)", fontSize: 13, fontFamily: "var(--font-body)",
1253
- transition: "border-color .15s, color .15s",
1254
- }}>{isRtl ? <ArrowRight /> : <ArrowLeft />} {prev.title}</button>
1250
+ display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 6,
1251
+ background: "var(--sf)", border: "1px solid var(--bd)", borderRadius: 8,
1252
+ padding: "16px 20px", cursor: "pointer", textAlign: "left",
1253
+ fontFamily: "var(--font-body)", transition: "all .3s ease",
1254
+ boxShadow: "0 2px 8px rgba(0,0,0,0.06)",
1255
+ }}
1256
+ onMouseOver={(e) => { e.currentTarget.style.borderColor = "var(--ac)"; e.currentTarget.style.boxShadow = "0 4px 16px rgba(0,0,0,0.1)"; }}
1257
+ onMouseOut={(e) => { e.currentTarget.style.borderColor = "var(--bd)"; e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.06)"; }}
1258
+ >
1259
+ <span style={{ fontSize: 11, color: "var(--txM)", textTransform: "uppercase", letterSpacing: "0.5px", display: "flex", alignItems: "center", gap: 4 }}>
1260
+ {isRtl ? <ArrowRight /> : <ArrowLeft />} Previous
1261
+ </span>
1262
+ <span style={{ fontSize: 14, fontWeight: 500, color: "var(--tx)" }}>{prev.title}</span>
1263
+ </button>
1255
1264
  ) : <div />}
1256
1265
  {next ? (
1257
1266
  <button onClick={() => onNavigate(next.id)} style={{
1258
- display: "flex", alignItems: "center", gap: 8, background: "none",
1259
- border: "1px solid var(--bd)", borderRadius: 2, padding: "10px 16px",
1260
- cursor: "pointer", color: "var(--tx2)", fontSize: 13, fontFamily: "var(--font-body)",
1261
- transition: "border-color .15s, color .15s",
1262
- }}>{next.title} {isRtl ? <ArrowLeft /> : <ArrowRight />}</button>
1267
+ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 6,
1268
+ background: "var(--sf)", border: "1px solid var(--bd)", borderRadius: 8,
1269
+ padding: "16px 20px", cursor: "pointer", textAlign: "right",
1270
+ fontFamily: "var(--font-body)", transition: "all .3s ease",
1271
+ boxShadow: "0 2px 8px rgba(0,0,0,0.06)",
1272
+ }}
1273
+ onMouseOver={(e) => { e.currentTarget.style.borderColor = "var(--ac)"; e.currentTarget.style.boxShadow = "0 4px 16px rgba(0,0,0,0.1)"; }}
1274
+ onMouseOut={(e) => { e.currentTarget.style.borderColor = "var(--bd)"; e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.06)"; }}
1275
+ >
1276
+ <span style={{ fontSize: 11, color: "var(--txM)", textTransform: "uppercase", letterSpacing: "0.5px", display: "flex", alignItems: "center", gap: 4 }}>
1277
+ Next {isRtl ? <ArrowLeft /> : <ArrowRight />}
1278
+ </span>
1279
+ <span style={{ fontSize: 14, fontWeight: 500, color: "var(--tx)" }}>{next.title}</span>
1280
+ </button>
1263
1281
  ) : <div />}
1264
1282
  </div>
1265
1283
  </>
package/src/entry.tsx CHANGED
@@ -59,14 +59,13 @@ const MDX_COMPONENTS: Record<string, React.ComponentType<any>> = {
59
59
 
60
60
  // ── CONTENT STYLES ───────────────────────────────────────
61
61
  const contentStyles = `
62
- @import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@300;400;500;600;700&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400;1,700&family=Fira+Code:wght@400;500;600&display=swap');
62
+ @import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=DM+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Bricolage+Grotesque:wght@300;400;500;600;700&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400;1,700&family=Fira+Code:wght@400;500;600&family=Bodoni+Moda:ital,wght@0,400;0,700;0,900;1,400&family=Space+Grotesk:wght@400;500;600;700&family=Source+Code+Pro:wght@400;500;600&family=Inter:wght@300;400;500;600;700&display=swap');
63
63
 
64
64
  html, body { margin: 0; padding: 0; height: 100%; overflow: clip; }
65
65
  #tome-root { height: 100%; overflow: clip; }
66
66
 
67
67
  .tome-content h1 { display: none; }
68
- .tome-content h2 { font-family: var(--font-body); font-size: 1.35em; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; display: flex; align-items: center; gap: 10px; letter-spacing: 0.01em; }
69
- .tome-content h2::before { content: "#"; font-family: var(--font-heading); font-size: 1.2em; font-weight: 300; font-style: italic; color: var(--ac); opacity: 0.5; }
68
+ .tome-content h2 { font-family: var(--font-body); font-size: 1.35em; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; letter-spacing: 0.01em; }
70
69
  .tome-content h3 { font-family: var(--font-body); font-size: 1.15em; font-weight: 600; margin-top: 1.5em; margin-bottom: 0.5em; }
71
70
  .tome-content h4 { font-family: var(--font-body); font-size: 1.05em; font-weight: 600; margin-top: 1.2em; margin-bottom: 0.5em; }
72
71
  .tome-content p { color: var(--tx2); line-height: 1.8; margin-bottom: 1em; font-size: 14.5px; }
@@ -11,12 +11,14 @@ const TOKEN_KEYS = [
11
11
 
12
12
  const FONT_KEYS = ["heading", "body", "code"] as const;
13
13
 
14
- const PRESET_NAMES: PresetName[] = ["amber", "editorial"];
14
+ const PRESET_NAMES: PresetName[] = ["amber", "editorial", "cipher", "mint"];
15
15
 
16
16
  describe("THEME_PRESETS", () => {
17
- it("contains both amber and editorial presets", () => {
17
+ it("contains all presets", () => {
18
18
  expect(THEME_PRESETS).toHaveProperty("amber");
19
19
  expect(THEME_PRESETS).toHaveProperty("editorial");
20
+ expect(THEME_PRESETS).toHaveProperty("cipher");
21
+ expect(THEME_PRESETS).toHaveProperty("mint");
20
22
  });
21
23
 
22
24
  for (const name of PRESET_NAMES) {