@tomehq/theme 0.3.0 → 0.3.2
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-GR2WCRGK.js → chunk-2AXAEADQ.js} +496 -153
- package/dist/entry.js +1 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +1 -1
- package/package.json +5 -5
- package/src/Shell.test.tsx +183 -0
- package/src/Shell.tsx +231 -21
- package/src/entry.tsx +207 -20
- package/src/global.d.ts +11 -0
- package/dist/chunk-2APCPR2Y.js +0 -2110
- package/dist/chunk-37JI6XGT.js +0 -1720
- package/dist/chunk-3A2LPGUL.js +0 -1991
- package/dist/chunk-3I2QTWTW.js +0 -1948
- package/dist/chunk-45M5UIAB.js +0 -2110
- package/dist/chunk-462AGU3S.js +0 -1959
- package/dist/chunk-7MUTU5D4.js +0 -1720
- package/dist/chunk-ABNPB6BB.js +0 -2133
- package/dist/chunk-BZGWSKT2.js +0 -573
- package/dist/chunk-CMQCNCSY.js +0 -2127
- package/dist/chunk-CTPOZMMK.js +0 -1703
- package/dist/chunk-DO544M3G.js +0 -1702
- package/dist/chunk-DPKZBFQP.js +0 -1777
- package/dist/chunk-EK7PZUEB.js +0 -2147
- package/dist/chunk-FMOLIHQF.js +0 -2182
- package/dist/chunk-FWBTK5TL.js +0 -1444
- package/dist/chunk-GDQIBNX5.js +0 -1962
- package/dist/chunk-GHQ2MODM.js +0 -2127
- package/dist/chunk-HNLKDQ64.js +0 -2139
- package/dist/chunk-INUMUXN5.js +0 -2095
- package/dist/chunk-JA4PMX6M.js +0 -1500
- package/dist/chunk-JSPFS7G5.js +0 -2102
- package/dist/chunk-JZRT4WNC.js +0 -1441
- package/dist/chunk-KQBY2JDB.js +0 -2112
- package/dist/chunk-LIMYFTPC.js +0 -1468
- package/dist/chunk-MEP7P6A7.js +0 -1500
- package/dist/chunk-NOZBIES7.js +0 -1948
- package/dist/chunk-O4GH3KYX.js +0 -1712
- package/dist/chunk-OEXM3BEC.js +0 -1702
- package/dist/chunk-Q7PYTVW3.js +0 -1771
- package/dist/chunk-QCWZYABW.js +0 -1468
- package/dist/chunk-RDF25WB2.js +0 -2085
- package/dist/chunk-RKTT3ZEX.js +0 -1500
- package/dist/chunk-S47BRMNQ.js +0 -1715
- package/dist/chunk-S4ZH5F56.js +0 -1949
- package/dist/chunk-SRD7NJHS.js +0 -1949
- package/dist/chunk-TQDWPSTO.js +0 -2087
- package/dist/chunk-TTRXRPP6.js +0 -1941
- package/dist/chunk-UKYFJSUA.js +0 -509
- package/dist/chunk-VKEQHP2E.js +0 -2133
- package/dist/chunk-VUT2FMSI.js +0 -1937
- package/dist/chunk-VVCC5JHK.js +0 -1949
- package/dist/chunk-W732TVBK.js +0 -1944
- package/dist/chunk-X4VQYPKO.js +0 -1768
- package/dist/chunk-YZ3P3TNS.js +0 -1760
package/src/Shell.tsx
CHANGED
|
@@ -212,6 +212,27 @@ const GlobeIcon = () => <Icon d="M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18ZM3.6 9h16.
|
|
|
212
212
|
// ── TOP NAV EXTERNAL LINK ICON ────────────────────────────
|
|
213
213
|
const ExtLinkIcon = () => <Icon d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3" size={11} />;
|
|
214
214
|
|
|
215
|
+
// ── SOCIAL LINK ICONS ──────────────────────────────────
|
|
216
|
+
const SOCIAL_ICON_PATHS: Record<string, string> = {
|
|
217
|
+
github: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z",
|
|
218
|
+
twitter: "M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865l8.875 11.633Z",
|
|
219
|
+
discord: "M13.545 2.907a13.227 13.227 0 00-3.257-1.011.05.05 0 00-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 00-3.658 0 8.258 8.258 0 00-.412-.833.051.051 0 00-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 00-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 003.995 2.02.05.05 0 00.056-.019c.308-.42.582-.863.818-1.329a.05.05 0 00-.028-.07 8.735 8.735 0 01-1.248-.595.05.05 0 01-.005-.083c.084-.063.168-.129.248-.195a.05.05 0 01.051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 01.053.007c.08.066.164.132.248.195a.051.051 0 01-.004.085c-.399.232-.813.431-1.249.594a.05.05 0 00-.03.07c.24.465.515.909.817 1.329a.05.05 0 00.056.019 13.235 13.235 0 004.001-2.02.049.049 0 00.021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 00-.02-.019z",
|
|
220
|
+
linkedin: "M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854V1.146zm4.943 12.248V6.169H2.542v7.225h2.401zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248-.822 0-1.359.54-1.359 1.248 0 .694.521 1.248 1.327 1.248h.016zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016a5.54 5.54 0 01.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225h2.4z",
|
|
221
|
+
youtube: "M8.051 1.999h.089c.822.003 4.987.033 6.11.335a2.01 2.01 0 011.415 1.42c.101.38.172.883.22 1.402l.01.104.022.26.008.104c.065.914.073 1.77.074 1.957v.075c-.001.194-.01 1.108-.082 2.06l-.008.105-.009.104c-.05.572-.124 1.14-.235 1.558a2.007 2.007 0 01-1.415 1.42c-1.16.312-5.569.334-6.18.335h-.142c-.309 0-1.587-.006-2.927-.052l-.17-.006-.087-.004-.171-.007-.171-.007c-1.11-.049-2.167-.128-2.654-.26a2.007 2.007 0 01-1.415-1.419c-.111-.417-.185-.986-.235-1.558L.09 9.82l-.008-.104A31.4 31.4 0 010 7.68v-.123c.002-.215.01-.958.064-1.778l.007-.103.003-.052.008-.104.022-.26.01-.104c.048-.519.119-1.023.22-1.402a2.007 2.007 0 011.415-1.42c.487-.13 1.544-.21 2.654-.26l.17-.007.172-.006.086-.003.171-.007A99.788 99.788 0 017.858 2h.193zM6.4 5.209v4.818l4.157-2.408L6.4 5.209z",
|
|
222
|
+
mastodon: "M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765 2.79.765.504 1.783.504 5.253c-.005.995-.01 2.19.013 3.44.075 4.21.56 8.354 3.383 9.386 1.302.476 2.418.576 3.317.507 1.628-.125 2.541-.8 2.541-.8l-.054-1.182s-1.163.366-2.47.322c-1.293-.044-2.658-.138-2.867-1.716a3.23 3.23 0 01-.028-.465s1.27.31 2.879.384c.984.045 1.905-.058 2.842-.17zM13 8.59V5.319c0-.67-.17-1.2-.507-1.592-.348-.4-.806-.605-1.373-.605-.656 0-1.154.252-1.486.756L9.2 4.595l-.434-.717c-.332-.504-.83-.756-1.486-.756-.567 0-1.025.204-1.373.605-.338.392-.507.923-.507 1.592V8.59h1.69V5.468c0-.67.285-1.012.855-1.012.63 0 .946.404.946 1.204V7.11h1.682V5.66c0-.8.316-1.204.946-1.204.57 0 .855.342.855 1.012V8.59H13z",
|
|
223
|
+
bluesky: "M3.468 1.948C5.303 3.325 7.276 6.118 8 7.616c.724-1.498 2.697-4.29 4.532-5.668C13.855 1.013 16 .638 16 3.14c0 .5-.286 4.2-.454 4.8-.585 2.093-2.716 2.628-4.544 2.305 3.195.564 4.007 2.433 2.25 4.302-3.337 3.548-4.8-1.244-5.252-2.547 0 0-.116-.334-.166-.334h.332C8.166 11.666 8.05 12 8.05 12c-.452 1.303-1.916 6.095-5.252 2.547-1.756-1.869-.946-3.738 2.25-4.302-1.829.323-3.96-.212-4.544-2.305C.336 7.34.05 3.64.05 3.14.05.638 2.195 1.013 3.468 1.948z",
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const SocialIcon = ({ platform, customIcon }: { platform: string; customIcon?: string }) => {
|
|
227
|
+
const d = platform === "custom" && customIcon ? customIcon : SOCIAL_ICON_PATHS[platform];
|
|
228
|
+
if (!d) return null;
|
|
229
|
+
return (
|
|
230
|
+
<svg width={16} height={16} viewBox="0 0 16 16" fill="currentColor">
|
|
231
|
+
<path d={d} />
|
|
232
|
+
</svg>
|
|
233
|
+
);
|
|
234
|
+
};
|
|
235
|
+
|
|
215
236
|
// ── SHELL COMPONENT ──────────────────────────────────────
|
|
216
237
|
export interface VersioningInfo {
|
|
217
238
|
current: string;
|
|
@@ -222,6 +243,7 @@ export interface I18nInfo {
|
|
|
222
243
|
defaultLocale: string;
|
|
223
244
|
locales: string[];
|
|
224
245
|
localeNames?: Record<string, string>;
|
|
246
|
+
localeDirs?: Record<string, "ltr" | "rtl">;
|
|
225
247
|
}
|
|
226
248
|
|
|
227
249
|
// ── CHANGELOG VIEW (TOM-49) ─────────────────────────────
|
|
@@ -290,6 +312,34 @@ function ChangelogView({ entries }: { entries: ChangelogViewEntry[] }) {
|
|
|
290
312
|
);
|
|
291
313
|
}
|
|
292
314
|
|
|
315
|
+
// ── BREADCRUMBS ─────────────────────────────────────────
|
|
316
|
+
type BreadcrumbItem = { label: string; href: string | null };
|
|
317
|
+
|
|
318
|
+
function getBreadcrumbs(
|
|
319
|
+
navigation: Array<{ section: string; pages: Array<{ title: string; id: string; urlPath: string }> }>,
|
|
320
|
+
currentPageId: string,
|
|
321
|
+
pageTitle: string,
|
|
322
|
+
): BreadcrumbItem[] {
|
|
323
|
+
if (currentPageId === "index") return [];
|
|
324
|
+
|
|
325
|
+
for (const section of navigation) {
|
|
326
|
+
const found = section.pages.find(p => p.id === currentPageId);
|
|
327
|
+
if (found) {
|
|
328
|
+
const crumbs: BreadcrumbItem[] = [];
|
|
329
|
+
// Section label — link to first page in section
|
|
330
|
+
const firstPage = section.pages[0];
|
|
331
|
+
crumbs.push({
|
|
332
|
+
label: section.section,
|
|
333
|
+
href: firstPage ? firstPage.urlPath : null,
|
|
334
|
+
});
|
|
335
|
+
// Current page (last crumb, not a link)
|
|
336
|
+
crumbs.push({ label: pageTitle, href: null });
|
|
337
|
+
return crumbs;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
|
|
293
343
|
interface ShellProps {
|
|
294
344
|
config: {
|
|
295
345
|
name: string;
|
|
@@ -299,11 +349,12 @@ interface ShellProps {
|
|
|
299
349
|
toc?: { enabled?: boolean; depth?: number };
|
|
300
350
|
topNav?: Array<{ label: string; href: string }>;
|
|
301
351
|
banner?: { text: string; link?: string; dismissible?: boolean };
|
|
352
|
+
socialLinks?: Array<{ platform: string; url: string; label?: string; icon?: string }>;
|
|
302
353
|
[key: string]: unknown;
|
|
303
354
|
};
|
|
304
355
|
navigation: Array<{
|
|
305
356
|
section: string;
|
|
306
|
-
pages: Array<{ title: string; id: string; urlPath: string; icon?: string }>;
|
|
357
|
+
pages: Array<{ title: string; id: string; urlPath: string; icon?: string; badge?: { text: string; variant: string } }>;
|
|
307
358
|
}>;
|
|
308
359
|
currentPageId: string;
|
|
309
360
|
pageHtml?: string;
|
|
@@ -324,13 +375,27 @@ interface ShellProps {
|
|
|
324
375
|
currentLocale?: string;
|
|
325
376
|
docContext?: Array<{ id: string; title: string; content: string }>;
|
|
326
377
|
basePath?: string;
|
|
378
|
+
isDraft?: boolean;
|
|
379
|
+
dir?: "ltr" | "rtl";
|
|
380
|
+
overrides?: {
|
|
381
|
+
Header?: React.ComponentType<any>;
|
|
382
|
+
Footer?: React.ComponentType<any>;
|
|
383
|
+
Sidebar?: React.ComponentType<any>;
|
|
384
|
+
Toc?: React.ComponentType<any>;
|
|
385
|
+
PageFooter?: React.ComponentType<any>;
|
|
386
|
+
};
|
|
327
387
|
}
|
|
328
388
|
|
|
329
389
|
export function Shell({
|
|
330
390
|
config, navigation, currentPageId, pageHtml, pageComponent, mdxComponents,
|
|
331
391
|
pageTitle, pageDescription, headings, tocEnabled = true, editUrl, lastUpdated, changelogEntries, onNavigate, allPages,
|
|
332
|
-
versioning, currentVersion, i18n, currentLocale, docContext, basePath = "",
|
|
392
|
+
versioning, currentVersion, i18n, currentLocale, docContext, basePath = "", isDraft, dir: dirProp, overrides,
|
|
333
393
|
}: ShellProps) {
|
|
394
|
+
// RTL support: resolve text direction from prop, i18n.localeDirs, or default to "ltr"
|
|
395
|
+
const resolvedLocale = currentLocale || i18n?.defaultLocale || "en";
|
|
396
|
+
const dir: "ltr" | "rtl" = dirProp || i18n?.localeDirs?.[resolvedLocale] || "ltr";
|
|
397
|
+
const isRtl = dir === "rtl";
|
|
398
|
+
|
|
334
399
|
const themeMode = config.theme?.mode || "auto";
|
|
335
400
|
|
|
336
401
|
// TOM-12: Initialize dark mode from config.theme.mode + system preference
|
|
@@ -571,6 +636,9 @@ export function Shell({
|
|
|
571
636
|
const prev = idx > 0 ? allNavPages[idx - 1] : null;
|
|
572
637
|
const next = idx < allNavPages.length - 1 ? allNavPages[idx + 1] : null;
|
|
573
638
|
|
|
639
|
+
// Breadcrumbs
|
|
640
|
+
const breadcrumbs = getBreadcrumbs(navigation, currentPageId, pageTitle);
|
|
641
|
+
|
|
574
642
|
const togSec = (s: string) => setExpanded(p => p.includes(s) ? p.filter(x => x !== s) : [...p, s]);
|
|
575
643
|
|
|
576
644
|
const cssVars: Record<string, string> = {
|
|
@@ -590,7 +658,7 @@ export function Shell({
|
|
|
590
658
|
const bannerIsInternal = bannerLink ? (bannerLink.startsWith("#") || (basePath && bannerLink.startsWith(basePath + "/"))) : false;
|
|
591
659
|
|
|
592
660
|
return (
|
|
593
|
-
<div className="tome-grain" style={{ ...cssVars as React.CSSProperties, color: "var(--tx)", background: "var(--bg)", fontFamily: "var(--font-body)", minHeight: "100vh", overflow: "hidden" }}>
|
|
661
|
+
<div dir={dir} className="tome-grain" style={{ ...cssVars as React.CSSProperties, color: "var(--tx)", background: "var(--bg)", fontFamily: "var(--font-body)", minHeight: "100vh", overflow: "hidden" }}>
|
|
594
662
|
{/* Banner */}
|
|
595
663
|
{config.banner?.text && !bannerDismissed && (
|
|
596
664
|
<div style={{
|
|
@@ -653,7 +721,7 @@ export function Shell({
|
|
|
653
721
|
/>
|
|
654
722
|
) : null}
|
|
655
723
|
|
|
656
|
-
<div style={{ display: "flex", flex: 1, height: config.banner?.text && !bannerDismissed ? "calc(100vh - 32px)" : "100vh" }}>
|
|
724
|
+
<div style={{ display: "flex", flexDirection: isRtl ? "row-reverse" : "row", flex: 1, height: config.banner?.text && !bannerDismissed ? "calc(100vh - 32px)" : "100vh" }}>
|
|
657
725
|
{/* Mobile sidebar backdrop */}
|
|
658
726
|
{mobile && sbOpen && (
|
|
659
727
|
<div onClick={() => setSb(false)} style={{
|
|
@@ -662,12 +730,25 @@ export function Shell({
|
|
|
662
730
|
}} />
|
|
663
731
|
)}
|
|
664
732
|
{/* Sidebar */}
|
|
733
|
+
{overrides?.Sidebar ? (
|
|
734
|
+
<overrides.Sidebar
|
|
735
|
+
config={config}
|
|
736
|
+
navigation={navigation}
|
|
737
|
+
currentPageId={currentPageId}
|
|
738
|
+
onNavigate={onNavigate}
|
|
739
|
+
mobile={mobile}
|
|
740
|
+
sbOpen={sbOpen}
|
|
741
|
+
setSbOpen={setSb}
|
|
742
|
+
versioning={versioning}
|
|
743
|
+
currentVersion={currentVersion}
|
|
744
|
+
/>
|
|
745
|
+
) : (
|
|
665
746
|
<aside style={{
|
|
666
747
|
width: sbOpen ? 270 : 0, minWidth: sbOpen ? 270 : 0,
|
|
667
|
-
background: "var(--sbBg)", borderRight: "1px solid var(--bd)",
|
|
748
|
+
background: "var(--sbBg)", [isRtl ? "borderLeft" : "borderRight"]: "1px solid var(--bd)",
|
|
668
749
|
display: "flex", flexDirection: "column",
|
|
669
750
|
transition: "width .2s, min-width .2s", overflow: "hidden",
|
|
670
|
-
...(mobile ? { position: "fixed" as const, top: 0, left: 0, bottom: 0, zIndex: 201 } : {}),
|
|
751
|
+
...(mobile ? { position: "fixed" as const, top: 0, [isRtl ? "right" : "left"]: 0, bottom: 0, zIndex: 201 } : {}),
|
|
671
752
|
}}>
|
|
672
753
|
<a href="/" style={{ padding: "18px 20px", display: "flex", alignItems: "baseline", gap: 6, borderBottom: "1px solid var(--bd)", textDecoration: "none", color: "inherit" }}>
|
|
673
754
|
<span style={{ fontFamily: "var(--font-heading)", fontSize: 22, fontWeight: 700, fontStyle: "italic" }}>
|
|
@@ -683,7 +764,7 @@ export function Shell({
|
|
|
683
764
|
padding: "8px 12px", cursor: "pointer", color: "var(--txM)", fontSize: 12.5,
|
|
684
765
|
fontFamily: "var(--font-body)",
|
|
685
766
|
}}>
|
|
686
|
-
<SearchIcon /><span style={{ flex: 1, textAlign: "left" }}>Search...</span>
|
|
767
|
+
<SearchIcon /><span style={{ flex: 1, textAlign: isRtl ? "right" : "left" }}>Search...</span>
|
|
687
768
|
<kbd style={{ fontFamily: "var(--font-code)", fontSize: 9, background: "var(--sf)", border: "1px solid var(--bd)", borderRadius: 2, padding: "2px 6px" }}>{"\u2318K"}</kbd>
|
|
688
769
|
</button>
|
|
689
770
|
</div>
|
|
@@ -699,21 +780,38 @@ export function Shell({
|
|
|
699
780
|
}}>
|
|
700
781
|
{expanded.includes(sec.section) ? <ChevDown /> : <ChevRight />}{sec.section}
|
|
701
782
|
</button>
|
|
702
|
-
{expanded.includes(sec.section) && <div style={{ marginLeft: 8, borderLeft: "1px solid var(--bd)", paddingLeft: 0 }}>
|
|
783
|
+
{expanded.includes(sec.section) && <div style={{ [isRtl ? "marginRight" : "marginLeft"]: 8, [isRtl ? "borderRight" : "borderLeft"]: "1px solid var(--bd)", [isRtl ? "paddingRight" : "paddingLeft"]: 0 }}>
|
|
703
784
|
{sec.pages.map(p => {
|
|
704
785
|
const active = currentPageId === p.id;
|
|
705
786
|
return (
|
|
706
787
|
<button key={p.id} onClick={() => { onNavigate(p.id); if (mobile) setSb(false); }} style={{
|
|
707
788
|
display: "flex", alignItems: "center", gap: 10, width: "100%",
|
|
708
|
-
textAlign: "left", background: "none",
|
|
789
|
+
textAlign: isRtl ? "right" : "left", background: "none",
|
|
709
790
|
border: "none", borderRadius: 0,
|
|
710
|
-
borderLeft: active ? "2px solid var(--ac)" : "2px solid transparent",
|
|
791
|
+
[isRtl ? "borderRight" : "borderLeft"]: active ? "2px solid var(--ac)" : "2px solid transparent",
|
|
711
792
|
padding: "7px 14px", cursor: "pointer",
|
|
712
793
|
color: active ? "var(--ac)" : "var(--tx2)", fontSize: 13,
|
|
713
794
|
fontWeight: active ? 500 : 400, fontFamily: "var(--font-body)",
|
|
714
795
|
transition: "all .12s",
|
|
715
796
|
}}>
|
|
716
797
|
{p.title}
|
|
798
|
+
{p.badge && (() => {
|
|
799
|
+
const badgeColors: Record<string, { bg: string; text: string }> = {
|
|
800
|
+
default: { bg: "var(--sf)", text: "var(--tx2)" },
|
|
801
|
+
info: { bg: "rgba(59,130,246,0.15)", text: "rgb(59,130,246)" },
|
|
802
|
+
success: { bg: "rgba(34,197,94,0.15)", text: "rgb(34,197,94)" },
|
|
803
|
+
warning: { bg: "rgba(234,179,8,0.15)", text: "rgb(202,138,4)" },
|
|
804
|
+
danger: { bg: "rgba(239,68,68,0.15)", text: "rgb(239,68,68)" },
|
|
805
|
+
};
|
|
806
|
+
const bc = badgeColors[p.badge!.variant || "default"] || badgeColors.default;
|
|
807
|
+
return (
|
|
808
|
+
<span style={{
|
|
809
|
+
fontSize: 10, fontWeight: 600, padding: "2px 6px",
|
|
810
|
+
borderRadius: 4, marginLeft: 6, whiteSpace: "nowrap",
|
|
811
|
+
background: bc.bg, color: bc.text,
|
|
812
|
+
}}>{p.badge!.text}</span>
|
|
813
|
+
);
|
|
814
|
+
})()}
|
|
717
815
|
</button>
|
|
718
816
|
);
|
|
719
817
|
})}
|
|
@@ -734,11 +832,11 @@ export function Shell({
|
|
|
734
832
|
onNavigate(targetId);
|
|
735
833
|
}}
|
|
736
834
|
style={{
|
|
737
|
-
flex: 1, padding: "
|
|
835
|
+
flex: 1, padding: "3px 0", textAlign: "center",
|
|
738
836
|
background: v === (currentVersion || versioning.current) ? "var(--acD)" : "var(--sf)",
|
|
739
837
|
border: "1px solid var(--bd)", borderRadius: 2, cursor: "pointer",
|
|
740
838
|
color: v === (currentVersion || versioning.current) ? "var(--ac)" : "var(--tx2)",
|
|
741
|
-
fontSize:
|
|
839
|
+
fontSize: 11, fontFamily: "var(--font-code)",
|
|
742
840
|
fontWeight: v === versioning.current ? 600 : 400,
|
|
743
841
|
}}
|
|
744
842
|
>
|
|
@@ -758,10 +856,30 @@ export function Shell({
|
|
|
758
856
|
<span style={{ fontFamily: "var(--font-code)", fontSize: 10, color: "var(--txM)" }}>{typeof __TOME_VERSION__ !== "undefined" && __TOME_VERSION__ ? `v${__TOME_VERSION__}` : "v0.1.0"}</span>
|
|
759
857
|
</div>
|
|
760
858
|
</aside>
|
|
859
|
+
)}
|
|
761
860
|
|
|
762
861
|
{/* Main area */}
|
|
763
862
|
<div style={{ flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }}>
|
|
764
863
|
{/* Header */}
|
|
864
|
+
{overrides?.Header ? (
|
|
865
|
+
<overrides.Header
|
|
866
|
+
config={config}
|
|
867
|
+
navigation={navigation}
|
|
868
|
+
currentPageId={currentPageId}
|
|
869
|
+
onNavigate={onNavigate}
|
|
870
|
+
mobile={mobile}
|
|
871
|
+
sbOpen={sbOpen}
|
|
872
|
+
setSbOpen={setSb}
|
|
873
|
+
isDark={isDark}
|
|
874
|
+
setDark={setDark}
|
|
875
|
+
versioning={versioning}
|
|
876
|
+
currentVersion={currentVersion}
|
|
877
|
+
i18n={i18n}
|
|
878
|
+
currentLocale={currentLocale}
|
|
879
|
+
onSearchOpen={() => setSearch(true)}
|
|
880
|
+
basePath={basePath}
|
|
881
|
+
/>
|
|
882
|
+
) : (
|
|
765
883
|
<header style={{
|
|
766
884
|
display: "flex", alignItems: "center", gap: mobile ? 8 : 12, padding: mobile ? "8px 12px" : "10px 24px",
|
|
767
885
|
borderBottom: "1px solid var(--bd)", background: "var(--hdBg)", backdropFilter: "blur(12px)",
|
|
@@ -820,6 +938,30 @@ export function Shell({
|
|
|
820
938
|
</div>
|
|
821
939
|
)}
|
|
822
940
|
|
|
941
|
+
{/* Social Links */}
|
|
942
|
+
{config.socialLinks && config.socialLinks.length > 0 && !mobile && (
|
|
943
|
+
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
944
|
+
{config.socialLinks.map((link) => (
|
|
945
|
+
<a
|
|
946
|
+
key={link.url}
|
|
947
|
+
href={link.url}
|
|
948
|
+
target="_blank"
|
|
949
|
+
rel="noopener noreferrer"
|
|
950
|
+
aria-label={link.label || link.platform}
|
|
951
|
+
data-testid={`social-link-${link.platform}`}
|
|
952
|
+
style={{
|
|
953
|
+
display: "flex", alignItems: "center", justifyContent: "center",
|
|
954
|
+
color: "var(--tx2)", cursor: "pointer", transition: "color .15s",
|
|
955
|
+
}}
|
|
956
|
+
onMouseOver={(e) => (e.currentTarget.style.color = "var(--tx)")}
|
|
957
|
+
onMouseOut={(e) => (e.currentTarget.style.color = "var(--tx2)")}
|
|
958
|
+
>
|
|
959
|
+
<SocialIcon platform={link.platform} customIcon={link.icon} />
|
|
960
|
+
</a>
|
|
961
|
+
))}
|
|
962
|
+
</div>
|
|
963
|
+
)}
|
|
964
|
+
|
|
823
965
|
{/* Theme toggle in header on mobile */}
|
|
824
966
|
{mobile && themeMode === "auto" && (
|
|
825
967
|
<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", flexShrink: 0 }}>
|
|
@@ -947,6 +1089,7 @@ export function Shell({
|
|
|
947
1089
|
</div>
|
|
948
1090
|
)}
|
|
949
1091
|
</header>
|
|
1092
|
+
)}
|
|
950
1093
|
|
|
951
1094
|
{/* TOM-30: Old version banner */}
|
|
952
1095
|
{isOldVersion && (
|
|
@@ -975,9 +1118,42 @@ export function Shell({
|
|
|
975
1118
|
{/* Content + TOC */}
|
|
976
1119
|
<div ref={contentRef} style={{ flex: 1, overflow: "auto", display: "flex" }}>
|
|
977
1120
|
<main style={{ flex: 1, maxWidth: mobile ? "100%" : 760, padding: mobile ? "24px 16px 60px" : "40px 48px 80px", margin: "0 auto", minWidth: 0 }}>
|
|
1121
|
+
{breadcrumbs.length > 0 && (
|
|
1122
|
+
<nav aria-label="Breadcrumbs" data-testid="breadcrumbs" style={{
|
|
1123
|
+
display: "flex", alignItems: "center", gap: 6,
|
|
1124
|
+
fontSize: 13, color: "var(--tx2)", marginBottom: 8,
|
|
1125
|
+
}}>
|
|
1126
|
+
{breadcrumbs.map((crumb, i) => (
|
|
1127
|
+
<React.Fragment key={i}>
|
|
1128
|
+
{i > 0 && <span style={{ color: "var(--tx2)", opacity: 0.5 }}>{"\u203A"}</span>}
|
|
1129
|
+
{i < breadcrumbs.length - 1 && crumb.href !== null ? (
|
|
1130
|
+
<a
|
|
1131
|
+
href={crumb.href}
|
|
1132
|
+
onClick={(e: React.MouseEvent) => {
|
|
1133
|
+
e.preventDefault();
|
|
1134
|
+
// Find the page id for this href
|
|
1135
|
+
const page = navigation.flatMap(s => s.pages).find(p => p.urlPath === crumb.href);
|
|
1136
|
+
if (page) onNavigate(page.id);
|
|
1137
|
+
}}
|
|
1138
|
+
style={{ color: "var(--tx2)", textDecoration: "none", cursor: "pointer" }}
|
|
1139
|
+
>
|
|
1140
|
+
{crumb.label}
|
|
1141
|
+
</a>
|
|
1142
|
+
) : (
|
|
1143
|
+
<span style={i === breadcrumbs.length - 1 ? { color: "var(--tx)" } : undefined}>{crumb.label}</span>
|
|
1144
|
+
)}
|
|
1145
|
+
</React.Fragment>
|
|
1146
|
+
))}
|
|
1147
|
+
</nav>
|
|
1148
|
+
)}
|
|
978
1149
|
<h1 style={{ fontFamily: "var(--font-heading)", fontSize: mobile ? 26 : 38, fontWeight: 400, fontStyle: "italic", lineHeight: 1.15, marginBottom: 8 }}>
|
|
979
1150
|
{pageTitle}
|
|
980
1151
|
</h1>
|
|
1152
|
+
{isDraft && (
|
|
1153
|
+
<div data-testid="draft-banner" style={{ background: "#fef3c7", color: "#92400e", padding: "8px 16px", borderRadius: 6, fontSize: 13, marginBottom: 16 }}>
|
|
1154
|
+
Draft — This page is only visible in development
|
|
1155
|
+
</div>
|
|
1156
|
+
)}
|
|
981
1157
|
{pageDescription && <p style={{ fontSize: 16, color: "var(--tx2)", lineHeight: 1.6, marginBottom: 32 }}>{pageDescription}</p>}
|
|
982
1158
|
<div style={{ borderTop: "1px solid var(--bd)", paddingTop: 28 }}>
|
|
983
1159
|
{/* TOM-49: Changelog page type */}
|
|
@@ -995,7 +1171,19 @@ export function Shell({
|
|
|
995
1171
|
)}
|
|
996
1172
|
</div>
|
|
997
1173
|
|
|
998
|
-
{/* TOM-48: Edit this page link + TOM-54: Last updated */}
|
|
1174
|
+
{/* TOM-48: Edit this page link + TOM-54: Last updated + Feedback + Prev/Next */}
|
|
1175
|
+
{overrides?.PageFooter ? (
|
|
1176
|
+
<overrides.PageFooter
|
|
1177
|
+
editUrl={editUrl}
|
|
1178
|
+
lastUpdated={lastUpdated}
|
|
1179
|
+
currentPageId={currentPageId}
|
|
1180
|
+
prev={prev}
|
|
1181
|
+
next={next}
|
|
1182
|
+
onNavigate={onNavigate}
|
|
1183
|
+
mobile={mobile}
|
|
1184
|
+
/>
|
|
1185
|
+
) : (
|
|
1186
|
+
<>
|
|
999
1187
|
{(editUrl || lastUpdated) && (
|
|
1000
1188
|
<div style={{ marginTop: 40, display: "flex", flexDirection: mobile ? "column" : "row", alignItems: mobile ? "flex-start" : "center", justifyContent: "space-between", gap: mobile ? 8 : 16 }}>
|
|
1001
1189
|
{editUrl && (
|
|
@@ -1049,7 +1237,7 @@ export function Shell({
|
|
|
1049
1237
|
border: "1px solid var(--bd)", borderRadius: 2, padding: "10px 16px",
|
|
1050
1238
|
cursor: "pointer", color: "var(--tx2)", fontSize: 13, fontFamily: "var(--font-body)",
|
|
1051
1239
|
transition: "border-color .15s, color .15s",
|
|
1052
|
-
}}
|
|
1240
|
+
}}>{isRtl ? <ArrowRight /> : <ArrowLeft />} {prev.title}</button>
|
|
1053
1241
|
) : <div />}
|
|
1054
1242
|
{next ? (
|
|
1055
1243
|
<button onClick={() => onNavigate(next.id)} style={{
|
|
@@ -1057,16 +1245,27 @@ export function Shell({
|
|
|
1057
1245
|
border: "1px solid var(--bd)", borderRadius: 2, padding: "10px 16px",
|
|
1058
1246
|
cursor: "pointer", color: "var(--tx2)", fontSize: 13, fontFamily: "var(--font-body)",
|
|
1059
1247
|
transition: "border-color .15s, color .15s",
|
|
1060
|
-
}}>{next.title} <ArrowRight
|
|
1248
|
+
}}>{next.title} {isRtl ? <ArrowLeft /> : <ArrowRight />}</button>
|
|
1061
1249
|
) : <div />}
|
|
1062
1250
|
</div>
|
|
1251
|
+
</>
|
|
1252
|
+
)}
|
|
1063
1253
|
</main>
|
|
1064
1254
|
|
|
1065
1255
|
{/* TOC (TOM-52) */}
|
|
1066
|
-
{
|
|
1067
|
-
|
|
1256
|
+
{overrides?.Toc ? (
|
|
1257
|
+
showToc && filteredHeadings.length >= 2 && wide && (
|
|
1258
|
+
<overrides.Toc
|
|
1259
|
+
headings={filteredHeadings}
|
|
1260
|
+
activeHeadingId={activeHeadingId}
|
|
1261
|
+
onScrollToHeading={scrollToHeading}
|
|
1262
|
+
/>
|
|
1263
|
+
)
|
|
1264
|
+
) : (
|
|
1265
|
+
showToc && filteredHeadings.length >= 2 && wide && (
|
|
1266
|
+
<aside data-testid="toc-sidebar" style={{ width: 200, padding: isRtl ? "40px 0 40px 16px" : "40px 16px 40px 0", position: "sticky", top: 0, alignSelf: "flex-start", flexShrink: 0 }}>
|
|
1068
1267
|
<div style={{ fontSize: 10, fontWeight: 600, textTransform: "uppercase", letterSpacing: ".1em", color: "var(--txM)", marginBottom: 12, fontFamily: "var(--font-code)" }}>On this page</div>
|
|
1069
|
-
<nav aria-label="Table of contents" style={{ borderLeft: "1px solid var(--bd)", paddingLeft: 0 }}>
|
|
1268
|
+
<nav aria-label="Table of contents" style={{ [isRtl ? "borderRight" : "borderLeft"]: "1px solid var(--bd)", [isRtl ? "paddingRight" : "paddingLeft"]: 0 }}>
|
|
1070
1269
|
{filteredHeadings.map((h, i) => {
|
|
1071
1270
|
const isActive = activeHeadingId === h.id;
|
|
1072
1271
|
return (
|
|
@@ -1081,22 +1280,33 @@ export function Shell({
|
|
|
1081
1280
|
fontWeight: isActive ? 500 : 400,
|
|
1082
1281
|
textDecoration: "none",
|
|
1083
1282
|
padding: "4px 12px",
|
|
1084
|
-
paddingLeft: 12 + (h.depth - 2) * 12,
|
|
1283
|
+
[isRtl ? "paddingRight" : "paddingLeft"]: 12 + (h.depth - 2) * 12,
|
|
1085
1284
|
lineHeight: 1.4,
|
|
1086
1285
|
transition: "color .15s, font-weight .15s",
|
|
1087
|
-
borderLeft: isActive ? "2px solid var(--ac)" : "2px solid transparent",
|
|
1088
|
-
marginLeft: -1,
|
|
1286
|
+
[isRtl ? "borderRight" : "borderLeft"]: isActive ? "2px solid var(--ac)" : "2px solid transparent",
|
|
1287
|
+
[isRtl ? "marginRight" : "marginLeft"]: -1,
|
|
1089
1288
|
}}
|
|
1090
1289
|
>{h.text}</a>
|
|
1091
1290
|
);
|
|
1092
1291
|
})}
|
|
1093
1292
|
</nav>
|
|
1094
1293
|
</aside>
|
|
1294
|
+
)
|
|
1095
1295
|
)}
|
|
1096
1296
|
</div>
|
|
1097
1297
|
</div>
|
|
1098
1298
|
</div>
|
|
1099
1299
|
|
|
1300
|
+
{/* Footer override */}
|
|
1301
|
+
{overrides?.Footer && (
|
|
1302
|
+
<overrides.Footer
|
|
1303
|
+
config={config}
|
|
1304
|
+
navigation={navigation}
|
|
1305
|
+
currentPageId={currentPageId}
|
|
1306
|
+
onNavigate={onNavigate}
|
|
1307
|
+
/>
|
|
1308
|
+
)}
|
|
1309
|
+
|
|
1100
1310
|
{/* TOM-32: AI Chat Widget (BYOK) */}
|
|
1101
1311
|
{config.ai?.enabled && (
|
|
1102
1312
|
<AiChat
|