@tomehq/theme 0.2.5 → 0.2.6
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.
- package/dist/{chunk-7MUTU5D4.js → chunk-YZ3P3TNS.js} +69 -29
- package/dist/entry.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/Shell.tsx +63 -30
- package/src/entry.tsx +9 -0
|
@@ -796,14 +796,15 @@ function Shell({
|
|
|
796
796
|
if (themeMode === "light") return false;
|
|
797
797
|
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? true;
|
|
798
798
|
});
|
|
799
|
-
const [
|
|
799
|
+
const [mobile, setMobile] = useState2(() => typeof window !== "undefined" && window.innerWidth < 768);
|
|
800
|
+
const [sbOpen, setSb] = useState2(() => typeof window !== "undefined" && window.innerWidth >= 768);
|
|
800
801
|
const [searchOpen, setSearch] = useState2(false);
|
|
801
802
|
const [versionDropdownOpen, setVersionDropdown] = useState2(false);
|
|
802
803
|
const [localeDropdownOpen, setLocaleDropdown] = useState2(false);
|
|
803
804
|
const isOldVersion = versioning && currentVersion && currentVersion !== versioning.current;
|
|
804
805
|
const [expanded, setExpanded] = useState2(navigation2.map((n) => n.section));
|
|
805
806
|
const contentRef = useRef2(null);
|
|
806
|
-
const [wide, setWide] = useState2(
|
|
807
|
+
const [wide, setWide] = useState2(() => typeof window !== "undefined" && window.innerWidth > 1100);
|
|
807
808
|
const preset = config2.theme?.preset || "amber";
|
|
808
809
|
const baseTokens = THEME_PRESETS[preset]?.[isDark ? "dark" : "light"] || THEME_PRESETS.amber.dark;
|
|
809
810
|
const accentOverride = config2.theme?.accent ? buildAccentOverride(config2.theme.accent, isDark) : null;
|
|
@@ -825,11 +826,23 @@ function Shell({
|
|
|
825
826
|
document.documentElement.classList.toggle("dark", isDark);
|
|
826
827
|
}, [isDark]);
|
|
827
828
|
useEffect2(() => {
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
829
|
+
const handleResize = () => {
|
|
830
|
+
const w = window.innerWidth;
|
|
831
|
+
setWide(w > 1100);
|
|
832
|
+
setMobile(w < 768);
|
|
833
|
+
};
|
|
834
|
+
handleResize();
|
|
835
|
+
window.addEventListener("resize", handleResize);
|
|
836
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
832
837
|
}, []);
|
|
838
|
+
useEffect2(() => {
|
|
839
|
+
if (mobile && sbOpen) {
|
|
840
|
+
document.body.style.overflow = "hidden";
|
|
841
|
+
return () => {
|
|
842
|
+
document.body.style.overflow = "";
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
}, [mobile, sbOpen]);
|
|
833
846
|
useEffect2(() => {
|
|
834
847
|
contentRef.current?.scrollTo(0, 0);
|
|
835
848
|
}, [currentPageId]);
|
|
@@ -944,10 +957,18 @@ function Shell({
|
|
|
944
957
|
onNavigate(id);
|
|
945
958
|
setSearch(false);
|
|
946
959
|
},
|
|
947
|
-
onClose: () => setSearch(false)
|
|
960
|
+
onClose: () => setSearch(false),
|
|
961
|
+
mobile
|
|
948
962
|
}
|
|
949
963
|
) : null,
|
|
950
964
|
/* @__PURE__ */ jsxs2("div", { style: { display: "flex", height: "100vh" }, children: [
|
|
965
|
+
mobile && sbOpen && /* @__PURE__ */ jsx2("div", { onClick: () => setSb(false), style: {
|
|
966
|
+
position: "fixed",
|
|
967
|
+
inset: 0,
|
|
968
|
+
zIndex: 200,
|
|
969
|
+
background: "rgba(0,0,0,0.4)",
|
|
970
|
+
backdropFilter: "blur(2px)"
|
|
971
|
+
} }),
|
|
951
972
|
/* @__PURE__ */ jsxs2("aside", { style: {
|
|
952
973
|
width: sbOpen ? 270 : 0,
|
|
953
974
|
minWidth: sbOpen ? 270 : 0,
|
|
@@ -956,13 +977,17 @@ function Shell({
|
|
|
956
977
|
display: "flex",
|
|
957
978
|
flexDirection: "column",
|
|
958
979
|
transition: "width .2s, min-width .2s",
|
|
959
|
-
overflow: "hidden"
|
|
980
|
+
overflow: "hidden",
|
|
981
|
+
...mobile ? { position: "fixed", top: 0, left: 0, bottom: 0, zIndex: 201 } : {}
|
|
960
982
|
}, children: [
|
|
961
983
|
/* @__PURE__ */ jsxs2("a", { href: "/", style: { padding: "18px 20px", display: "flex", alignItems: "baseline", gap: 6, borderBottom: "1px solid var(--bd)", textDecoration: "none", color: "inherit" }, children: [
|
|
962
984
|
/* @__PURE__ */ jsx2("span", { style: { fontFamily: "var(--font-heading)", fontSize: 22, fontWeight: 700, fontStyle: "italic" }, children: config2.name }),
|
|
963
985
|
/* @__PURE__ */ jsx2("span", { style: { width: 5, height: 5, borderRadius: "50%", background: "var(--ac)", display: "inline-block" } })
|
|
964
986
|
] }),
|
|
965
|
-
/* @__PURE__ */ jsx2("div", { style: { padding: "12px 14px" }, children: /* @__PURE__ */ jsxs2("button", { onClick: () =>
|
|
987
|
+
/* @__PURE__ */ jsx2("div", { style: { padding: "12px 14px" }, children: /* @__PURE__ */ jsxs2("button", { onClick: () => {
|
|
988
|
+
setSearch(true);
|
|
989
|
+
if (mobile) setSb(false);
|
|
990
|
+
}, style: {
|
|
966
991
|
display: "flex",
|
|
967
992
|
alignItems: "center",
|
|
968
993
|
gap: 8,
|
|
@@ -1003,7 +1028,10 @@ function Shell({
|
|
|
1003
1028
|
] }),
|
|
1004
1029
|
expanded.includes(sec.section) && /* @__PURE__ */ jsx2("div", { style: { marginLeft: 8, borderLeft: "1px solid var(--bd)", paddingLeft: 0 }, children: sec.pages.map((p) => {
|
|
1005
1030
|
const active = currentPageId === p.id;
|
|
1006
|
-
return /* @__PURE__ */ jsx2("button", { onClick: () =>
|
|
1031
|
+
return /* @__PURE__ */ jsx2("button", { onClick: () => {
|
|
1032
|
+
onNavigate(p.id);
|
|
1033
|
+
if (mobile) setSb(false);
|
|
1034
|
+
}, style: {
|
|
1007
1035
|
display: "flex",
|
|
1008
1036
|
alignItems: "center",
|
|
1009
1037
|
gap: 10,
|
|
@@ -1024,7 +1052,7 @@ function Shell({
|
|
|
1024
1052
|
}) })
|
|
1025
1053
|
] }, sec.section)) }),
|
|
1026
1054
|
/* @__PURE__ */ jsxs2("div", { style: { padding: "12px 16px", borderTop: "1px solid var(--bd)", display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
1027
|
-
themeMode === "auto" ? /* @__PURE__ */ jsx2("button", { onClick: () => setDark((d) => !d), style: { background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }, children: isDark ? /* @__PURE__ */ jsx2(SunIcon, {}) : /* @__PURE__ */ jsx2(MoonIcon, {}) }) : /* @__PURE__ */ jsx2("div", {}),
|
|
1055
|
+
themeMode === "auto" ? /* @__PURE__ */ jsx2("button", { "aria-label": isDark ? "Switch to light mode" : "Switch to dark mode", onClick: () => setDark((d) => !d), style: { background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }, children: isDark ? /* @__PURE__ */ jsx2(SunIcon, {}) : /* @__PURE__ */ jsx2(MoonIcon, {}) }) : /* @__PURE__ */ jsx2("div", {}),
|
|
1028
1056
|
/* @__PURE__ */ jsxs2("span", { style: { fontSize: 11, color: "var(--txM)", letterSpacing: 0.2 }, children: [
|
|
1029
1057
|
"Built with ",
|
|
1030
1058
|
"\u2661",
|
|
@@ -1037,14 +1065,14 @@ function Shell({
|
|
|
1037
1065
|
/* @__PURE__ */ jsxs2("header", { style: {
|
|
1038
1066
|
display: "flex",
|
|
1039
1067
|
alignItems: "center",
|
|
1040
|
-
gap: 12,
|
|
1041
|
-
padding: "10px 24px",
|
|
1068
|
+
gap: mobile ? 8 : 12,
|
|
1069
|
+
padding: mobile ? "8px 12px" : "10px 24px",
|
|
1042
1070
|
borderBottom: "1px solid var(--bd)",
|
|
1043
1071
|
background: "var(--hdBg)",
|
|
1044
1072
|
backdropFilter: "blur(12px)"
|
|
1045
1073
|
}, children: [
|
|
1046
|
-
/* @__PURE__ */ jsx2("button", { onClick: () => setSb(!sbOpen), style: { background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }, children: sbOpen ? /* @__PURE__ */ jsx2(XIcon, {}) : /* @__PURE__ */ jsx2(MenuIcon, {}) }),
|
|
1047
|
-
/* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", gap: 8, fontFamily: "var(--font-code)", fontSize: 11, color: "var(--txM)", letterSpacing: ".03em", flex: 1 }, children: navigation2.map((s) => {
|
|
1074
|
+
/* @__PURE__ */ jsx2("button", { "aria-label": sbOpen ? "Close sidebar" : "Open sidebar", onClick: () => setSb(!sbOpen), style: { background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }, children: sbOpen ? /* @__PURE__ */ jsx2(XIcon, {}) : /* @__PURE__ */ jsx2(MenuIcon, {}) }),
|
|
1075
|
+
mobile ? /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, color: "var(--ac)", fontFamily: "var(--font-code)", flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: navigation2.flatMap((s) => s.pages).find((p) => p.id === currentPageId)?.title || "" }) : /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", gap: 8, fontFamily: "var(--font-code)", fontSize: 11, color: "var(--txM)", letterSpacing: ".03em", flex: 1 }, children: navigation2.map((s) => {
|
|
1048
1076
|
const f = s.pages.find((p) => p.id === currentPageId);
|
|
1049
1077
|
if (!f) return null;
|
|
1050
1078
|
return /* @__PURE__ */ jsxs2("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
@@ -1053,7 +1081,7 @@ function Shell({
|
|
|
1053
1081
|
/* @__PURE__ */ jsx2("span", { style: { color: "var(--ac)" }, children: f.title })
|
|
1054
1082
|
] }, s.section);
|
|
1055
1083
|
}) }),
|
|
1056
|
-
config2.topNav && config2.topNav.length > 0 && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
|
|
1084
|
+
config2.topNav && config2.topNav.length > 0 && !mobile && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
|
|
1057
1085
|
config2.topNav.map((link) => {
|
|
1058
1086
|
const isExternal = link.href.startsWith("http") || !link.href.startsWith("#");
|
|
1059
1087
|
return /* @__PURE__ */ jsxs2(
|
|
@@ -1283,8 +1311,8 @@ function Shell({
|
|
|
1283
1311
|
}
|
|
1284
1312
|
),
|
|
1285
1313
|
/* @__PURE__ */ jsxs2("div", { ref: contentRef, style: { flex: 1, overflow: "auto", display: "flex" }, children: [
|
|
1286
|
-
/* @__PURE__ */ jsxs2("main", { style: { flex: 1, maxWidth: 760, padding: "40px 48px 80px", margin: "0 auto" }, children: [
|
|
1287
|
-
/* @__PURE__ */ jsx2("h1", { style: { fontFamily: "var(--font-heading)", fontSize: 38, fontWeight: 400, fontStyle: "italic", lineHeight: 1.15, marginBottom: 8 }, children: pageTitle }),
|
|
1314
|
+
/* @__PURE__ */ jsxs2("main", { style: { flex: 1, maxWidth: mobile ? "100%" : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }, children: [
|
|
1315
|
+
/* @__PURE__ */ jsx2("h1", { style: { fontFamily: "var(--font-heading)", fontSize: mobile ? 26 : 38, fontWeight: 400, fontStyle: "italic", lineHeight: 1.15, marginBottom: 8 }, children: pageTitle }),
|
|
1288
1316
|
pageDescription && /* @__PURE__ */ jsx2("p", { style: { fontSize: 16, color: "var(--tx2)", lineHeight: 1.6, marginBottom: 32 }, children: pageDescription }),
|
|
1289
1317
|
/* @__PURE__ */ jsx2("div", { style: { borderTop: "1px solid var(--bd)", paddingTop: 28 }, children: changelogEntries && changelogEntries.length > 0 ? /* @__PURE__ */ jsx2(ChangelogView, { entries: changelogEntries }) : PageComponent ? /* @__PURE__ */ jsx2("div", { className: "tome-content", children: /* @__PURE__ */ jsx2(PageComponent, { components: mdxComponents || {} }) }) : /* @__PURE__ */ jsx2(
|
|
1290
1318
|
"div",
|
|
@@ -1293,7 +1321,7 @@ function Shell({
|
|
|
1293
1321
|
dangerouslySetInnerHTML: { __html: (pageHtml || "").replace(/<h1[^>]*>[\s\S]*?<\/h1>\s*/, "") }
|
|
1294
1322
|
}
|
|
1295
1323
|
) }),
|
|
1296
|
-
(editUrl || lastUpdated) && /* @__PURE__ */ jsxs2("div", { style: { marginTop: 40, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 }, children: [
|
|
1324
|
+
(editUrl || lastUpdated) && /* @__PURE__ */ jsxs2("div", { style: { marginTop: 40, display: "flex", flexDirection: mobile ? "column" : "row", alignItems: mobile ? "flex-start" : "center", justifyContent: "space-between", gap: mobile ? 8 : 16 }, children: [
|
|
1297
1325
|
editUrl && /* @__PURE__ */ jsx2("div", { "data-testid": "edit-page-link", children: /* @__PURE__ */ jsxs2(
|
|
1298
1326
|
"a",
|
|
1299
1327
|
{
|
|
@@ -1323,7 +1351,7 @@ function Shell({
|
|
|
1323
1351
|
formatRelativeDate(lastUpdated)
|
|
1324
1352
|
] })
|
|
1325
1353
|
] }),
|
|
1326
|
-
/* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", marginTop: editUrl || lastUpdated ? 16 : 48, paddingTop: 24, borderTop: "1px solid var(--bd)", gap: 16 }, children: [
|
|
1354
|
+
/* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: mobile ? "column" : "row", justifyContent: "space-between", marginTop: editUrl || lastUpdated ? 16 : 48, paddingTop: 24, borderTop: "1px solid var(--bd)", gap: mobile ? 12 : 16 }, children: [
|
|
1327
1355
|
prev ? /* @__PURE__ */ jsxs2("button", { onClick: () => onNavigate(prev.id), style: {
|
|
1328
1356
|
display: "flex",
|
|
1329
1357
|
alignItems: "center",
|
|
@@ -1406,7 +1434,7 @@ ${d.content}`).join("\n\n") ?? allPages.map((p) => `- ${p.title}${p.description
|
|
|
1406
1434
|
)
|
|
1407
1435
|
] });
|
|
1408
1436
|
}
|
|
1409
|
-
function SearchModal({ allPages, onNavigate, onClose }) {
|
|
1437
|
+
function SearchModal({ allPages, onNavigate, onClose, mobile }) {
|
|
1410
1438
|
const [q, setQ] = useState2("");
|
|
1411
1439
|
const [results, setResults] = useState2([]);
|
|
1412
1440
|
const [selected, setSelected] = useState2(0);
|
|
@@ -1478,17 +1506,20 @@ function SearchModal({ allPages, onNavigate, onClose }) {
|
|
|
1478
1506
|
background: "rgba(0,0,0,0.55)",
|
|
1479
1507
|
backdropFilter: "blur(6px)",
|
|
1480
1508
|
display: "flex",
|
|
1481
|
-
alignItems: "flex-start",
|
|
1509
|
+
alignItems: mobile ? "stretch" : "flex-start",
|
|
1482
1510
|
justifyContent: "center",
|
|
1483
|
-
paddingTop: "12vh"
|
|
1511
|
+
paddingTop: mobile ? 0 : "12vh"
|
|
1484
1512
|
}, children: /* @__PURE__ */ jsxs2("div", { onClick: (e) => e.stopPropagation(), style: {
|
|
1485
1513
|
background: "var(--sf)",
|
|
1486
|
-
border: "1px solid var(--bd)",
|
|
1487
|
-
borderRadius: 2,
|
|
1514
|
+
border: mobile ? "none" : "1px solid var(--bd)",
|
|
1515
|
+
borderRadius: mobile ? 0 : 2,
|
|
1488
1516
|
width: "100%",
|
|
1489
|
-
maxWidth: 520,
|
|
1490
|
-
boxShadow: "0 24px 80px rgba(0,0,0,0.4)",
|
|
1491
|
-
overflow: "hidden"
|
|
1517
|
+
maxWidth: mobile ? "100%" : 520,
|
|
1518
|
+
boxShadow: mobile ? "none" : "0 24px 80px rgba(0,0,0,0.4)",
|
|
1519
|
+
overflow: "hidden",
|
|
1520
|
+
display: "flex",
|
|
1521
|
+
flexDirection: "column",
|
|
1522
|
+
...mobile ? { height: "100%" } : {}
|
|
1492
1523
|
}, children: [
|
|
1493
1524
|
/* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "14px 18px", borderBottom: "1px solid var(--bd)" }, children: [
|
|
1494
1525
|
/* @__PURE__ */ jsx2(SearchIcon, {}),
|
|
@@ -1505,7 +1536,7 @@ function SearchModal({ allPages, onNavigate, onClose }) {
|
|
|
1505
1536
|
),
|
|
1506
1537
|
/* @__PURE__ */ jsx2("kbd", { style: { fontFamily: "var(--font-code)", fontSize: 10, color: "var(--txM)", background: "var(--cdBg)", padding: "2px 6px", borderRadius: 2, border: "1px solid var(--bd)" }, children: "ESC" })
|
|
1507
1538
|
] }),
|
|
1508
|
-
results.length > 0 && /* @__PURE__ */ jsx2("div", { style: { padding: 6, maxHeight: 360, overflow: "auto" }, children: results.map((r, i) => /* @__PURE__ */ jsxs2(
|
|
1539
|
+
results.length > 0 && /* @__PURE__ */ jsx2("div", { style: { padding: 6, maxHeight: mobile ? "none" : 360, overflow: "auto", flex: mobile ? 1 : void 0 }, children: results.map((r, i) => /* @__PURE__ */ jsxs2(
|
|
1509
1540
|
"button",
|
|
1510
1541
|
{
|
|
1511
1542
|
onClick: () => onNavigate(r.id),
|
|
@@ -1587,6 +1618,15 @@ var contentStyles = `
|
|
|
1587
1618
|
.tome-content img { max-width: 100%; border-radius: 2px; }
|
|
1588
1619
|
.tome-content hr { border: none; border-top: 1px solid var(--bd); margin: 2em 0; }
|
|
1589
1620
|
|
|
1621
|
+
/* Mobile responsive content */
|
|
1622
|
+
@media (max-width: 767px) {
|
|
1623
|
+
.tome-content h2 { font-size: 1.2em; margin-top: 1.5em; }
|
|
1624
|
+
.tome-content h3 { font-size: 1.05em; }
|
|
1625
|
+
.tome-content pre code { font-size: 12px; padding: 0.8em 1em; }
|
|
1626
|
+
.tome-content table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
|
1627
|
+
.tome-content blockquote { margin: 0.8em 0; }
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1590
1630
|
/* Selection style */
|
|
1591
1631
|
::selection { background: var(--acD); color: var(--ac); }
|
|
1592
1632
|
|
package/dist/entry.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomehq/theme",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
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/components": "0.2.
|
|
13
|
-
"@tomehq/core": "0.2.
|
|
12
|
+
"@tomehq/components": "0.2.6",
|
|
13
|
+
"@tomehq/core": "0.2.6"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
16
|
"react": "^18.0.0 || ^19.0.0",
|
package/src/Shell.tsx
CHANGED
|
@@ -331,7 +331,8 @@ export function Shell({
|
|
|
331
331
|
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? true;
|
|
332
332
|
});
|
|
333
333
|
|
|
334
|
-
const [
|
|
334
|
+
const [mobile, setMobile] = useState(() => typeof window !== "undefined" && window.innerWidth < 768);
|
|
335
|
+
const [sbOpen, setSb] = useState(() => typeof window !== "undefined" && window.innerWidth >= 768);
|
|
335
336
|
const [searchOpen, setSearch] = useState(false);
|
|
336
337
|
const [versionDropdownOpen, setVersionDropdown] = useState(false);
|
|
337
338
|
const [localeDropdownOpen, setLocaleDropdown] = useState(false);
|
|
@@ -340,7 +341,7 @@ export function Shell({
|
|
|
340
341
|
const isOldVersion = versioning && currentVersion && currentVersion !== versioning.current;
|
|
341
342
|
const [expanded, setExpanded] = useState<string[]>(navigation.map(n => n.section));
|
|
342
343
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
343
|
-
const [wide, setWide] = useState(
|
|
344
|
+
const [wide, setWide] = useState(() => typeof window !== "undefined" && window.innerWidth > 1100);
|
|
344
345
|
|
|
345
346
|
const preset = (config.theme?.preset || "amber") as PresetName;
|
|
346
347
|
const baseTokens = THEME_PRESETS[preset]?.[isDark ? "dark" : "light"] || THEME_PRESETS.amber.dark;
|
|
@@ -377,11 +378,24 @@ export function Shell({
|
|
|
377
378
|
}, [isDark]);
|
|
378
379
|
|
|
379
380
|
useEffect(() => {
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
381
|
+
const handleResize = () => {
|
|
382
|
+
const w = window.innerWidth;
|
|
383
|
+
setWide(w > 1100);
|
|
384
|
+
setMobile(w < 768);
|
|
385
|
+
};
|
|
386
|
+
handleResize();
|
|
387
|
+
window.addEventListener("resize", handleResize);
|
|
388
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
383
389
|
}, []);
|
|
384
390
|
|
|
391
|
+
// Lock body scroll when mobile sidebar is open
|
|
392
|
+
useEffect(() => {
|
|
393
|
+
if (mobile && sbOpen) {
|
|
394
|
+
document.body.style.overflow = "hidden";
|
|
395
|
+
return () => { document.body.style.overflow = ""; };
|
|
396
|
+
}
|
|
397
|
+
}, [mobile, sbOpen]);
|
|
398
|
+
|
|
385
399
|
useEffect(() => { contentRef.current?.scrollTo(0, 0); }, [currentPageId]);
|
|
386
400
|
|
|
387
401
|
// ── TOC: Config-based depth filtering + frontmatter opt-out ──
|
|
@@ -500,16 +514,25 @@ export function Shell({
|
|
|
500
514
|
allPages={allPages}
|
|
501
515
|
onNavigate={(id) => { onNavigate(id); setSearch(false); }}
|
|
502
516
|
onClose={() => setSearch(false)}
|
|
517
|
+
mobile={mobile}
|
|
503
518
|
/>
|
|
504
519
|
) : null}
|
|
505
520
|
|
|
506
521
|
<div style={{ display: "flex", height: "100vh" }}>
|
|
522
|
+
{/* Mobile sidebar backdrop */}
|
|
523
|
+
{mobile && sbOpen && (
|
|
524
|
+
<div onClick={() => setSb(false)} style={{
|
|
525
|
+
position: "fixed", inset: 0, zIndex: 200,
|
|
526
|
+
background: "rgba(0,0,0,0.4)", backdropFilter: "blur(2px)",
|
|
527
|
+
}} />
|
|
528
|
+
)}
|
|
507
529
|
{/* Sidebar */}
|
|
508
530
|
<aside style={{
|
|
509
531
|
width: sbOpen ? 270 : 0, minWidth: sbOpen ? 270 : 0,
|
|
510
532
|
background: "var(--sbBg)", borderRight: "1px solid var(--bd)",
|
|
511
533
|
display: "flex", flexDirection: "column",
|
|
512
534
|
transition: "width .2s, min-width .2s", overflow: "hidden",
|
|
535
|
+
...(mobile ? { position: "fixed" as const, top: 0, left: 0, bottom: 0, zIndex: 201 } : {}),
|
|
513
536
|
}}>
|
|
514
537
|
<a href="/" style={{ padding: "18px 20px", display: "flex", alignItems: "baseline", gap: 6, borderBottom: "1px solid var(--bd)", textDecoration: "none", color: "inherit" }}>
|
|
515
538
|
<span style={{ fontFamily: "var(--font-heading)", fontSize: 22, fontWeight: 700, fontStyle: "italic" }}>
|
|
@@ -519,7 +542,7 @@ export function Shell({
|
|
|
519
542
|
</a>
|
|
520
543
|
|
|
521
544
|
<div style={{ padding: "12px 14px" }}>
|
|
522
|
-
<button onClick={() => setSearch(true)} style={{
|
|
545
|
+
<button onClick={() => { setSearch(true); if (mobile) setSb(false); }} style={{
|
|
523
546
|
display: "flex", alignItems: "center", gap: 8, width: "100%",
|
|
524
547
|
background: "var(--cdBg)", border: "1px solid var(--bd)", borderRadius: 2,
|
|
525
548
|
padding: "8px 12px", cursor: "pointer", color: "var(--txM)", fontSize: 12.5,
|
|
@@ -545,7 +568,7 @@ export function Shell({
|
|
|
545
568
|
{sec.pages.map(p => {
|
|
546
569
|
const active = currentPageId === p.id;
|
|
547
570
|
return (
|
|
548
|
-
<button key={p.id} onClick={() => onNavigate(p.id)} style={{
|
|
571
|
+
<button key={p.id} onClick={() => { onNavigate(p.id); if (mobile) setSb(false); }} style={{
|
|
549
572
|
display: "flex", alignItems: "center", gap: 10, width: "100%",
|
|
550
573
|
textAlign: "left", background: "none",
|
|
551
574
|
border: "none", borderRadius: 0,
|
|
@@ -567,7 +590,7 @@ export function Shell({
|
|
|
567
590
|
<div style={{ padding: "12px 16px", borderTop: "1px solid var(--bd)", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
568
591
|
{/* TOM-12: Only show toggle when mode is "auto" */}
|
|
569
592
|
{themeMode === "auto" ? (
|
|
570
|
-
<button onClick={() => setDark(d => !d)} style={{ background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }}>
|
|
593
|
+
<button aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"} onClick={() => setDark(d => !d)} style={{ background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }}>
|
|
571
594
|
{isDark ? <SunIcon /> : <MoonIcon />}
|
|
572
595
|
</button>
|
|
573
596
|
) : <div />}
|
|
@@ -580,24 +603,30 @@ export function Shell({
|
|
|
580
603
|
<div style={{ flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }}>
|
|
581
604
|
{/* Header */}
|
|
582
605
|
<header style={{
|
|
583
|
-
display: "flex", alignItems: "center", gap: 12, padding: "10px 24px",
|
|
606
|
+
display: "flex", alignItems: "center", gap: mobile ? 8 : 12, padding: mobile ? "8px 12px" : "10px 24px",
|
|
584
607
|
borderBottom: "1px solid var(--bd)", background: "var(--hdBg)", backdropFilter: "blur(12px)",
|
|
585
608
|
}}>
|
|
586
|
-
<button onClick={() => setSb(!sbOpen)} style={{ background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }}>
|
|
609
|
+
<button aria-label={sbOpen ? "Close sidebar" : "Open sidebar"} onClick={() => setSb(!sbOpen)} style={{ background: "none", border: "none", color: "var(--txM)", cursor: "pointer", display: "flex" }}>
|
|
587
610
|
{sbOpen ? <XIcon /> : <MenuIcon />}
|
|
588
611
|
</button>
|
|
589
|
-
|
|
590
|
-
{
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
612
|
+
{mobile ? (
|
|
613
|
+
<span style={{ fontSize: 13, color: "var(--ac)", fontFamily: "var(--font-code)", flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
|
614
|
+
{navigation.flatMap(s => s.pages).find(p => p.id === currentPageId)?.title || ""}
|
|
615
|
+
</span>
|
|
616
|
+
) : (
|
|
617
|
+
<div style={{ display: "flex", alignItems: "center", gap: 8, fontFamily: "var(--font-code)", fontSize: 11, color: "var(--txM)", letterSpacing: ".03em", flex: 1 }}>
|
|
618
|
+
{navigation.map(s => {
|
|
619
|
+
const f = s.pages.find(p => p.id === currentPageId);
|
|
620
|
+
if (!f) return null;
|
|
621
|
+
return <span key={s.section} style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
622
|
+
<span>{s.section}</span><ChevRight /><span style={{ color: "var(--ac)" }}>{f.title}</span>
|
|
623
|
+
</span>;
|
|
624
|
+
})}
|
|
625
|
+
</div>
|
|
626
|
+
)}
|
|
598
627
|
|
|
599
628
|
{/* Top Nav Links */}
|
|
600
|
-
{config.topNav && config.topNav.length > 0 && (
|
|
629
|
+
{config.topNav && config.topNav.length > 0 && !mobile && (
|
|
601
630
|
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
|
602
631
|
{config.topNav.map((link) => {
|
|
603
632
|
const isExternal = link.href.startsWith("http") || !link.href.startsWith("#");
|
|
@@ -771,8 +800,8 @@ export function Shell({
|
|
|
771
800
|
|
|
772
801
|
{/* Content + TOC */}
|
|
773
802
|
<div ref={contentRef} style={{ flex: 1, overflow: "auto", display: "flex" }}>
|
|
774
|
-
<main style={{ flex: 1, maxWidth: 760, padding: "40px 48px 80px", margin: "0 auto" }}>
|
|
775
|
-
<h1 style={{ fontFamily: "var(--font-heading)", fontSize: 38, fontWeight: 400, fontStyle: "italic", lineHeight: 1.15, marginBottom: 8 }}>
|
|
803
|
+
<main style={{ flex: 1, maxWidth: mobile ? "100%" : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }}>
|
|
804
|
+
<h1 style={{ fontFamily: "var(--font-heading)", fontSize: mobile ? 26 : 38, fontWeight: 400, fontStyle: "italic", lineHeight: 1.15, marginBottom: 8 }}>
|
|
776
805
|
{pageTitle}
|
|
777
806
|
</h1>
|
|
778
807
|
{pageDescription && <p style={{ fontSize: 16, color: "var(--tx2)", lineHeight: 1.6, marginBottom: 32 }}>{pageDescription}</p>}
|
|
@@ -794,7 +823,7 @@ export function Shell({
|
|
|
794
823
|
|
|
795
824
|
{/* TOM-48: Edit this page link + TOM-54: Last updated */}
|
|
796
825
|
{(editUrl || lastUpdated) && (
|
|
797
|
-
<div style={{ marginTop: 40, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 }}>
|
|
826
|
+
<div style={{ marginTop: 40, display: "flex", flexDirection: mobile ? "column" : "row", alignItems: mobile ? "flex-start" : "center", justifyContent: "space-between", gap: mobile ? 8 : 16 }}>
|
|
798
827
|
{editUrl && (
|
|
799
828
|
<div data-testid="edit-page-link">
|
|
800
829
|
<a
|
|
@@ -822,7 +851,7 @@ export function Shell({
|
|
|
822
851
|
)}
|
|
823
852
|
|
|
824
853
|
{/* Prev / Next */}
|
|
825
|
-
<div style={{ display: "flex", justifyContent: "space-between", marginTop: (editUrl || lastUpdated) ? 16 : 48, paddingTop: 24, borderTop: "1px solid var(--bd)", gap: 16 }}>
|
|
854
|
+
<div style={{ display: "flex", flexDirection: mobile ? "column" : "row", justifyContent: "space-between", marginTop: (editUrl || lastUpdated) ? 16 : 48, paddingTop: 24, borderTop: "1px solid var(--bd)", gap: mobile ? 12 : 16 }}>
|
|
826
855
|
{prev ? (
|
|
827
856
|
<button onClick={() => onNavigate(prev.id)} style={{
|
|
828
857
|
display: "flex", alignItems: "center", gap: 8, background: "none",
|
|
@@ -898,10 +927,11 @@ interface SearchResult {
|
|
|
898
927
|
}
|
|
899
928
|
|
|
900
929
|
// ── SEARCH MODAL (TOM-15) ────────────────────────────────
|
|
901
|
-
function SearchModal({ allPages, onNavigate, onClose }: {
|
|
930
|
+
function SearchModal({ allPages, onNavigate, onClose, mobile }: {
|
|
902
931
|
allPages: Array<{ id: string; title: string; description?: string }>;
|
|
903
932
|
onNavigate: (id: string) => void;
|
|
904
933
|
onClose: () => void;
|
|
934
|
+
mobile?: boolean;
|
|
905
935
|
}) {
|
|
906
936
|
const [q, setQ] = useState("");
|
|
907
937
|
const [results, setResults] = useState<SearchResult[]>([]);
|
|
@@ -993,12 +1023,15 @@ function SearchModal({ allPages, onNavigate, onClose }: {
|
|
|
993
1023
|
return (
|
|
994
1024
|
<div onClick={onClose} style={{
|
|
995
1025
|
position: "fixed", inset: 0, zIndex: 1000, background: "rgba(0,0,0,0.55)",
|
|
996
|
-
backdropFilter: "blur(6px)", display: "flex",
|
|
997
|
-
|
|
1026
|
+
backdropFilter: "blur(6px)", display: "flex",
|
|
1027
|
+
alignItems: mobile ? "stretch" : "flex-start",
|
|
1028
|
+
justifyContent: "center", paddingTop: mobile ? 0 : "12vh",
|
|
998
1029
|
}}>
|
|
999
1030
|
<div onClick={e => e.stopPropagation()} style={{
|
|
1000
|
-
background: "var(--sf)", border: "1px solid var(--bd)", borderRadius: 2,
|
|
1001
|
-
width: "100%", maxWidth: 520, boxShadow: "0 24px 80px rgba(0,0,0,0.4)",
|
|
1031
|
+
background: "var(--sf)", border: mobile ? "none" : "1px solid var(--bd)", borderRadius: mobile ? 0 : 2,
|
|
1032
|
+
width: "100%", maxWidth: mobile ? "100%" : 520, boxShadow: mobile ? "none" : "0 24px 80px rgba(0,0,0,0.4)",
|
|
1033
|
+
overflow: "hidden", display: "flex", flexDirection: "column" as const,
|
|
1034
|
+
...(mobile ? { height: "100%" } : {}),
|
|
1002
1035
|
}}>
|
|
1003
1036
|
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "14px 18px", borderBottom: "1px solid var(--bd)" }}>
|
|
1004
1037
|
<SearchIcon />
|
|
@@ -1009,7 +1042,7 @@ function SearchModal({ allPages, onNavigate, onClose }: {
|
|
|
1009
1042
|
/>
|
|
1010
1043
|
<kbd style={{ fontFamily: "var(--font-code)", fontSize: 10, color: "var(--txM)", background: "var(--cdBg)", padding: "2px 6px", borderRadius: 2, border: "1px solid var(--bd)" }}>ESC</kbd>
|
|
1011
1044
|
</div>
|
|
1012
|
-
{results.length > 0 && <div style={{ padding: 6, maxHeight: 360, overflow: "auto" }}>
|
|
1045
|
+
{results.length > 0 && <div style={{ padding: 6, maxHeight: mobile ? "none" : 360, overflow: "auto", flex: mobile ? 1 : undefined }}>
|
|
1013
1046
|
{results.map((r, i) => (
|
|
1014
1047
|
<button key={r.id + i} onClick={() => onNavigate(r.id)} style={{
|
|
1015
1048
|
display: "block", width: "100%", textAlign: "left",
|
package/src/entry.tsx
CHANGED
|
@@ -59,6 +59,15 @@ const contentStyles = `
|
|
|
59
59
|
.tome-content img { max-width: 100%; border-radius: 2px; }
|
|
60
60
|
.tome-content hr { border: none; border-top: 1px solid var(--bd); margin: 2em 0; }
|
|
61
61
|
|
|
62
|
+
/* Mobile responsive content */
|
|
63
|
+
@media (max-width: 767px) {
|
|
64
|
+
.tome-content h2 { font-size: 1.2em; margin-top: 1.5em; }
|
|
65
|
+
.tome-content h3 { font-size: 1.05em; }
|
|
66
|
+
.tome-content pre code { font-size: 12px; padding: 0.8em 1em; }
|
|
67
|
+
.tome-content table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
|
68
|
+
.tome-content blockquote { margin: 0.8em 0; }
|
|
69
|
+
}
|
|
70
|
+
|
|
62
71
|
/* Selection style */
|
|
63
72
|
::selection { background: var(--acD); color: var(--ac); }
|
|
64
73
|
|