@umami/shiso 1.1.12 → 1.3.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.
- package/dist/chunks/App.js +137 -68
- package/package.json +1 -1
- package/src/components/DocContent.tsx +74 -3
- package/src/components/PageLinks.tsx +2 -2
- package/src/components/SideNav.tsx +10 -10
- package/src/components/TopNav.tsx +2 -2
- package/src/components/docs/ZoomableImage.tsx +1 -22
- package/src/components/icons/index.ts +1 -0
- package/src/lib/site-model.ts +1 -0
- package/src/lib/types.ts +6 -0
- package/src/styles/global.css +26 -4
- package/vite.config.ts +71 -0
package/dist/chunks/App.js
CHANGED
|
@@ -3290,11 +3290,11 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
3290
3290
|
* This source code is licensed under the ISC license.
|
|
3291
3291
|
* See the LICENSE file in the root directory of this source tree.
|
|
3292
3292
|
*/
|
|
3293
|
-
const __iconNode$
|
|
3293
|
+
const __iconNode$18 = [["path", {
|
|
3294
3294
|
d: "M20 6 9 17l-5-5",
|
|
3295
3295
|
key: "1gmf2c"
|
|
3296
3296
|
}]];
|
|
3297
|
-
const Check$1 = createLucideIcon("check", __iconNode$
|
|
3297
|
+
const Check$1 = createLucideIcon("check", __iconNode$18);
|
|
3298
3298
|
|
|
3299
3299
|
//#endregion
|
|
3300
3300
|
//#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/chevron-down.mjs
|
|
@@ -3304,11 +3304,11 @@ const Check$1 = createLucideIcon("check", __iconNode$17);
|
|
|
3304
3304
|
* This source code is licensed under the ISC license.
|
|
3305
3305
|
* See the LICENSE file in the root directory of this source tree.
|
|
3306
3306
|
*/
|
|
3307
|
-
const __iconNode$
|
|
3307
|
+
const __iconNode$17 = [["path", {
|
|
3308
3308
|
d: "m6 9 6 6 6-6",
|
|
3309
3309
|
key: "qrunsl"
|
|
3310
3310
|
}]];
|
|
3311
|
-
const ChevronDown = createLucideIcon("chevron-down", __iconNode$
|
|
3311
|
+
const ChevronDown = createLucideIcon("chevron-down", __iconNode$17);
|
|
3312
3312
|
|
|
3313
3313
|
//#endregion
|
|
3314
3314
|
//#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/chevron-right.mjs
|
|
@@ -3318,11 +3318,11 @@ const ChevronDown = createLucideIcon("chevron-down", __iconNode$16);
|
|
|
3318
3318
|
* This source code is licensed under the ISC license.
|
|
3319
3319
|
* See the LICENSE file in the root directory of this source tree.
|
|
3320
3320
|
*/
|
|
3321
|
-
const __iconNode$
|
|
3321
|
+
const __iconNode$16 = [["path", {
|
|
3322
3322
|
d: "m9 18 6-6-6-6",
|
|
3323
3323
|
key: "mthhwq"
|
|
3324
3324
|
}]];
|
|
3325
|
-
const ChevronRight = createLucideIcon("chevron-right", __iconNode$
|
|
3325
|
+
const ChevronRight = createLucideIcon("chevron-right", __iconNode$16);
|
|
3326
3326
|
|
|
3327
3327
|
//#endregion
|
|
3328
3328
|
//#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/chevron-up.mjs
|
|
@@ -3332,11 +3332,11 @@ const ChevronRight = createLucideIcon("chevron-right", __iconNode$15);
|
|
|
3332
3332
|
* This source code is licensed under the ISC license.
|
|
3333
3333
|
* See the LICENSE file in the root directory of this source tree.
|
|
3334
3334
|
*/
|
|
3335
|
-
const __iconNode$
|
|
3335
|
+
const __iconNode$15 = [["path", {
|
|
3336
3336
|
d: "m18 15-6-6-6 6",
|
|
3337
3337
|
key: "153udz"
|
|
3338
3338
|
}]];
|
|
3339
|
-
const ChevronUp = createLucideIcon("chevron-up", __iconNode$
|
|
3339
|
+
const ChevronUp = createLucideIcon("chevron-up", __iconNode$15);
|
|
3340
3340
|
|
|
3341
3341
|
//#endregion
|
|
3342
3342
|
//#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/circle-alert.mjs
|
|
@@ -3346,7 +3346,7 @@ const ChevronUp = createLucideIcon("chevron-up", __iconNode$14);
|
|
|
3346
3346
|
* This source code is licensed under the ISC license.
|
|
3347
3347
|
* See the LICENSE file in the root directory of this source tree.
|
|
3348
3348
|
*/
|
|
3349
|
-
const __iconNode$
|
|
3349
|
+
const __iconNode$14 = [
|
|
3350
3350
|
["circle", {
|
|
3351
3351
|
cx: "12",
|
|
3352
3352
|
cy: "12",
|
|
@@ -3368,7 +3368,7 @@ const __iconNode$13 = [
|
|
|
3368
3368
|
key: "4dfq90"
|
|
3369
3369
|
}]
|
|
3370
3370
|
];
|
|
3371
|
-
const CircleAlert = createLucideIcon("circle-alert", __iconNode$
|
|
3371
|
+
const CircleAlert = createLucideIcon("circle-alert", __iconNode$14);
|
|
3372
3372
|
|
|
3373
3373
|
//#endregion
|
|
3374
3374
|
//#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/copy.mjs
|
|
@@ -3378,7 +3378,7 @@ const CircleAlert = createLucideIcon("circle-alert", __iconNode$13);
|
|
|
3378
3378
|
* This source code is licensed under the ISC license.
|
|
3379
3379
|
* See the LICENSE file in the root directory of this source tree.
|
|
3380
3380
|
*/
|
|
3381
|
-
const __iconNode$
|
|
3381
|
+
const __iconNode$13 = [["rect", {
|
|
3382
3382
|
width: "14",
|
|
3383
3383
|
height: "14",
|
|
3384
3384
|
x: "8",
|
|
@@ -3390,7 +3390,7 @@ const __iconNode$12 = [["rect", {
|
|
|
3390
3390
|
d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",
|
|
3391
3391
|
key: "zix9uf"
|
|
3392
3392
|
}]];
|
|
3393
|
-
const Copy = createLucideIcon("copy", __iconNode$
|
|
3393
|
+
const Copy = createLucideIcon("copy", __iconNode$13);
|
|
3394
3394
|
|
|
3395
3395
|
//#endregion
|
|
3396
3396
|
//#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/external-link.mjs
|
|
@@ -3400,7 +3400,7 @@ const Copy = createLucideIcon("copy", __iconNode$12);
|
|
|
3400
3400
|
* This source code is licensed under the ISC license.
|
|
3401
3401
|
* See the LICENSE file in the root directory of this source tree.
|
|
3402
3402
|
*/
|
|
3403
|
-
const __iconNode$
|
|
3403
|
+
const __iconNode$12 = [
|
|
3404
3404
|
["path", {
|
|
3405
3405
|
d: "M15 3h6v6",
|
|
3406
3406
|
key: "1q9fwt"
|
|
@@ -3414,7 +3414,39 @@ const __iconNode$11 = [
|
|
|
3414
3414
|
key: "a6xqqp"
|
|
3415
3415
|
}]
|
|
3416
3416
|
];
|
|
3417
|
-
const ExternalLink = createLucideIcon("external-link", __iconNode$
|
|
3417
|
+
const ExternalLink = createLucideIcon("external-link", __iconNode$12);
|
|
3418
|
+
|
|
3419
|
+
//#endregion
|
|
3420
|
+
//#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/file-text.mjs
|
|
3421
|
+
/**
|
|
3422
|
+
* @license lucide-react v1.28.0 - ISC
|
|
3423
|
+
*
|
|
3424
|
+
* This source code is licensed under the ISC license.
|
|
3425
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
3426
|
+
*/
|
|
3427
|
+
const __iconNode$11 = [
|
|
3428
|
+
["path", {
|
|
3429
|
+
d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
|
|
3430
|
+
key: "1oefj6"
|
|
3431
|
+
}],
|
|
3432
|
+
["path", {
|
|
3433
|
+
d: "M14 2v5a1 1 0 0 0 1 1h5",
|
|
3434
|
+
key: "wfsgrz"
|
|
3435
|
+
}],
|
|
3436
|
+
["path", {
|
|
3437
|
+
d: "M10 9H8",
|
|
3438
|
+
key: "b1mrlr"
|
|
3439
|
+
}],
|
|
3440
|
+
["path", {
|
|
3441
|
+
d: "M16 13H8",
|
|
3442
|
+
key: "t4e002"
|
|
3443
|
+
}],
|
|
3444
|
+
["path", {
|
|
3445
|
+
d: "M16 17H8",
|
|
3446
|
+
key: "z1uh3a"
|
|
3447
|
+
}]
|
|
3448
|
+
];
|
|
3449
|
+
const FileText = createLucideIcon("file-text", __iconNode$11);
|
|
3418
3450
|
|
|
3419
3451
|
//#endregion
|
|
3420
3452
|
//#region ../../node_modules/.pnpm/lucide-react@1.28.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/globe.mjs
|
|
@@ -19706,7 +19738,7 @@ DialogBackdrop.displayName = "DialogBackdrop";
|
|
|
19706
19738
|
*
|
|
19707
19739
|
* Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
|
|
19708
19740
|
*/
|
|
19709
|
-
const DialogClose
|
|
19741
|
+
const DialogClose = /*#__PURE__*/ React$1.forwardRef(function DialogClose(componentProps, forwardedRef) {
|
|
19710
19742
|
const { render, className, style, disabled = false, nativeButton = true, ...elementProps } = componentProps;
|
|
19711
19743
|
const store = useDialogRootContext();
|
|
19712
19744
|
const open = store.useState("open");
|
|
@@ -19728,7 +19760,7 @@ const DialogClose$1 = /*#__PURE__*/ React$1.forwardRef(function DialogClose(comp
|
|
|
19728
19760
|
]
|
|
19729
19761
|
});
|
|
19730
19762
|
});
|
|
19731
|
-
DialogClose
|
|
19763
|
+
DialogClose.displayName = "DialogClose";
|
|
19732
19764
|
|
|
19733
19765
|
//#endregion
|
|
19734
19766
|
//#region ../../node_modules/.pnpm/@base-ui+react@1.7.0_@types_e9c1e83f6bc6140c3efaf3427f2fbf0a/node_modules/@base-ui/react/dialog/portal/DialogPortalContext.mjs
|
|
@@ -20463,12 +20495,6 @@ function DialogPortal$1({ ...props }) {
|
|
|
20463
20495
|
...props
|
|
20464
20496
|
});
|
|
20465
20497
|
}
|
|
20466
|
-
function DialogClose({ ...props }) {
|
|
20467
|
-
return /* @__PURE__ */ jsx(DialogClose$1, {
|
|
20468
|
-
"data-slot": "dialog-close",
|
|
20469
|
-
...props
|
|
20470
|
-
});
|
|
20471
|
-
}
|
|
20472
20498
|
function DialogOverlay$1({ className, ...props }) {
|
|
20473
20499
|
return /* @__PURE__ */ jsx(DialogBackdrop, {
|
|
20474
20500
|
"data-slot": "dialog-overlay",
|
|
@@ -20481,7 +20507,7 @@ function DialogContent$1({ className, children, showCloseButton = true, overlayC
|
|
|
20481
20507
|
"data-slot": "dialog-content",
|
|
20482
20508
|
className: cn("fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
|
|
20483
20509
|
...props,
|
|
20484
|
-
children: [children, showCloseButton && /* @__PURE__ */ jsxs(DialogClose
|
|
20510
|
+
children: [children, showCloseButton && /* @__PURE__ */ jsxs(DialogClose, {
|
|
20485
20511
|
"data-slot": "dialog-close",
|
|
20486
20512
|
render: /* @__PURE__ */ jsx(Button, {
|
|
20487
20513
|
variant: "ghost",
|
|
@@ -20514,40 +20540,29 @@ function ZoomableImage({ noZoom = false, className, alt, src, ...props }) {
|
|
|
20514
20540
|
});
|
|
20515
20541
|
if (noZoom || !src) return image;
|
|
20516
20542
|
const label = alt ? `View “${alt}” full size` : "View image full size";
|
|
20517
|
-
return /* @__PURE__ */ jsxs(Dialog$1, { children: [
|
|
20518
|
-
/* @__PURE__ */ jsx(
|
|
20519
|
-
|
|
20520
|
-
|
|
20521
|
-
|
|
20522
|
-
|
|
20523
|
-
"aria-label": label
|
|
20524
|
-
}),
|
|
20525
|
-
children: image
|
|
20526
|
-
}),
|
|
20527
|
-
/* @__PURE__ */ jsxs(DialogContent$1, {
|
|
20528
|
-
showCloseButton: false,
|
|
20529
|
-
overlayClassName: "bg-black/80 supports-backdrop-filter:backdrop-blur-sm",
|
|
20530
|
-
className: "flex max-h-[calc(100vh-2rem)] w-auto max-w-[calc(100vw-2rem)] items-center justify-center bg-transparent p-0 ring-0 shadow-none sm:max-w-[calc(100vw-2rem)]",
|
|
20531
|
-
children: [/* @__PURE__ */ jsx(DialogTitle, {
|
|
20532
|
-
className: "sr-only",
|
|
20533
|
-
children: alt || "Full-size image"
|
|
20534
|
-
}), /* @__PURE__ */ jsx("img", {
|
|
20535
|
-
src,
|
|
20536
|
-
srcSet: props.srcSet,
|
|
20537
|
-
sizes: props.sizes,
|
|
20538
|
-
alt: alt || "",
|
|
20539
|
-
className: "max-h-[calc(100vh-2rem)] max-w-[calc(100vw-2rem)] object-contain"
|
|
20540
|
-
})]
|
|
20543
|
+
return /* @__PURE__ */ jsxs(Dialog$1, { children: [/* @__PURE__ */ jsx(DialogTrigger, {
|
|
20544
|
+
render: /* @__PURE__ */ jsx("button", {
|
|
20545
|
+
type: "button",
|
|
20546
|
+
"data-slot": "zoomable-image",
|
|
20547
|
+
className: "my-6 block w-fit max-w-full cursor-zoom-in rounded-lg text-left outline-none focus-visible:ring-3 focus-visible:ring-ring/50",
|
|
20548
|
+
"aria-label": label
|
|
20541
20549
|
}),
|
|
20542
|
-
|
|
20543
|
-
|
|
20544
|
-
|
|
20545
|
-
|
|
20546
|
-
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
|
|
20550
|
+
children: image
|
|
20551
|
+
}), /* @__PURE__ */ jsxs(DialogContent$1, {
|
|
20552
|
+
showCloseButton: false,
|
|
20553
|
+
overlayClassName: "bg-black/80 supports-backdrop-filter:backdrop-blur-sm",
|
|
20554
|
+
className: "flex max-h-[calc(100vh-2rem)] w-auto max-w-[calc(100vw-2rem)] items-center justify-center bg-transparent p-0 ring-0 shadow-none sm:max-w-[calc(100vw-2rem)]",
|
|
20555
|
+
children: [/* @__PURE__ */ jsx(DialogTitle, {
|
|
20556
|
+
className: "sr-only",
|
|
20557
|
+
children: alt || "Full-size image"
|
|
20558
|
+
}), /* @__PURE__ */ jsx("img", {
|
|
20559
|
+
src,
|
|
20560
|
+
srcSet: props.srcSet,
|
|
20561
|
+
sizes: props.sizes,
|
|
20562
|
+
alt: alt || "",
|
|
20563
|
+
className: "max-h-[calc(100vh-2rem)] max-w-[calc(100vw-2rem)] object-contain"
|
|
20564
|
+
})]
|
|
20565
|
+
})] });
|
|
20551
20566
|
}
|
|
20552
20567
|
|
|
20553
20568
|
//#endregion
|
|
@@ -22903,6 +22918,7 @@ const SHISO_THEME_LABELS = {
|
|
|
22903
22918
|
searchUnavailable: "Search unavailable",
|
|
22904
22919
|
noResults: "No results",
|
|
22905
22920
|
lastUpdated: "Last updated on",
|
|
22921
|
+
relatedTopics: "Related topics",
|
|
22906
22922
|
notFound: "Page not found",
|
|
22907
22923
|
dismissBanner: "Dismiss banner",
|
|
22908
22924
|
toggleTheme: "Toggle theme",
|
|
@@ -26167,7 +26183,7 @@ function TopNav({ docs, label }) {
|
|
|
26167
26183
|
const tabs = docs.tabs.filter((tab) => !tab.hidden);
|
|
26168
26184
|
if (!tabs.length) return null;
|
|
26169
26185
|
const selected = docs.pages.find((item) => item.url === pathname)?.tabId || [...tabs].sort((a, b) => b.url.length - a.url.length).find((tab) => pathname === tab.url || pathname.startsWith(`${tab.url}/`))?.id || tabs[0]?.id;
|
|
26170
|
-
const tabClass = (tab) =>
|
|
26186
|
+
const tabClass = (tab) => cn("flex h-full items-center gap-1 whitespace-nowrap border-transparent border-b-2 font-medium", {
|
|
26171
26187
|
"border-b-primary text-foreground": tab.id === selected,
|
|
26172
26188
|
"text-muted-foreground hover:text-foreground": tab.id !== selected
|
|
26173
26189
|
});
|
|
@@ -26609,6 +26625,31 @@ function ContextualMenu({ options, labels }) {
|
|
|
26609
26625
|
|
|
26610
26626
|
//#endregion
|
|
26611
26627
|
//#region src/components/DocContent.tsx
|
|
26628
|
+
/**
|
|
26629
|
+
* Related-topics entries from frontmatter. Bare paths resolve their title from
|
|
26630
|
+
* the docs registry; unknown internal paths without an explicit title are
|
|
26631
|
+
* skipped so dead links never render.
|
|
26632
|
+
*/
|
|
26633
|
+
function resolveRelated(entries) {
|
|
26634
|
+
if (!Array.isArray(entries)) return [];
|
|
26635
|
+
const links = [];
|
|
26636
|
+
for (const entry of entries) {
|
|
26637
|
+
const href = typeof entry === "string" ? entry : entry?.href;
|
|
26638
|
+
if (!href || typeof href !== "string") continue;
|
|
26639
|
+
const external = !href.startsWith("/");
|
|
26640
|
+
const title = (typeof entry === "object" ? entry.title : void 0) || (external ? href : getPageByPathname(href)?.label);
|
|
26641
|
+
if (!title) {
|
|
26642
|
+
if (import.meta.env.DEV) console.warn(`[shiso] Related topic "${href}" does not match a page and has no title — skipped.`);
|
|
26643
|
+
continue;
|
|
26644
|
+
}
|
|
26645
|
+
links.push({
|
|
26646
|
+
href,
|
|
26647
|
+
title,
|
|
26648
|
+
external
|
|
26649
|
+
});
|
|
26650
|
+
}
|
|
26651
|
+
return links;
|
|
26652
|
+
}
|
|
26612
26653
|
function DocContent({ page, doc, site }) {
|
|
26613
26654
|
const pagerPages = getScopeForPage(docsSite, page).docs.pages.filter((item) => !item.hidden);
|
|
26614
26655
|
const pageIndex = pagerPages.findIndex((item) => item.slug === page.slug);
|
|
@@ -26620,6 +26661,7 @@ function DocContent({ page, doc, site }) {
|
|
|
26620
26661
|
const lastModified = (typeof doc.frontmatter?.timestamp === "boolean" ? doc.frontmatter.timestamp : site.showTimestamp) ? getLastModified(page.filePath) : void 0;
|
|
26621
26662
|
const eyebrow = site.styling.eyebrows === "breadcrumbs" ? [.../* @__PURE__ */ new Set([page.tabLabel, page.section])].filter(Boolean).join(" / ") : page.section;
|
|
26622
26663
|
const contextualOptions = resolveContextualOptions(site.contextualOptions, page, site.labels);
|
|
26664
|
+
const related = resolveRelated(doc.frontmatter?.related);
|
|
26623
26665
|
const dateFormat = new Intl.DateTimeFormat(resolveLocale(page.language, site.locale), {
|
|
26624
26666
|
dateStyle: "medium",
|
|
26625
26667
|
timeZone: "UTC"
|
|
@@ -26660,6 +26702,33 @@ function DocContent({ page, doc, site }) {
|
|
|
26660
26702
|
})
|
|
26661
26703
|
]
|
|
26662
26704
|
}),
|
|
26705
|
+
related.length > 0 && /* @__PURE__ */ jsxs("nav", {
|
|
26706
|
+
className: "mt-8",
|
|
26707
|
+
"aria-label": site.labels.relatedTopics,
|
|
26708
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
26709
|
+
className: "text-sm text-muted-foreground",
|
|
26710
|
+
children: site.labels.relatedTopics
|
|
26711
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
26712
|
+
className: "mt-3 flex flex-col gap-2 text-sm",
|
|
26713
|
+
children: related.map(({ href, title, external }) => /* @__PURE__ */ jsxs("li", {
|
|
26714
|
+
className: "flex items-center gap-2",
|
|
26715
|
+
children: [/* @__PURE__ */ jsx(FileText, {
|
|
26716
|
+
size: 14,
|
|
26717
|
+
className: "shrink-0 text-muted-foreground"
|
|
26718
|
+
}), external ? /* @__PURE__ */ jsx("a", {
|
|
26719
|
+
href,
|
|
26720
|
+
target: "_blank",
|
|
26721
|
+
rel: "noreferrer",
|
|
26722
|
+
className: "text-foreground hover:text-primary",
|
|
26723
|
+
children: title
|
|
26724
|
+
}) : /* @__PURE__ */ jsx(Link, {
|
|
26725
|
+
to: href,
|
|
26726
|
+
className: "text-foreground hover:text-primary",
|
|
26727
|
+
children: title
|
|
26728
|
+
})]
|
|
26729
|
+
}, href))
|
|
26730
|
+
})]
|
|
26731
|
+
}),
|
|
26663
26732
|
/* @__PURE__ */ jsxs("div", {
|
|
26664
26733
|
className: "mt-8 flex items-center justify-between",
|
|
26665
26734
|
children: [/* @__PURE__ */ jsx(NavigationButton, {
|
|
@@ -26773,7 +26842,7 @@ function PageLinks({ items = [], title, navigationLabel }) {
|
|
|
26773
26842
|
const isActive = hash === id;
|
|
26774
26843
|
return /* @__PURE__ */ jsx("a", {
|
|
26775
26844
|
href: `#${id}`,
|
|
26776
|
-
className:
|
|
26845
|
+
className: cn("block border-l px-3 py-1.5", {
|
|
26777
26846
|
"border-l-primary font-medium text-primary": isActive,
|
|
26778
26847
|
"border-border text-muted-foreground hover:text-foreground": !isActive
|
|
26779
26848
|
}),
|
|
@@ -26828,23 +26897,23 @@ function CollapsibleGroup({ node, pathname, depth, drilldown, expandLabel, colla
|
|
|
26828
26897
|
const chevron = /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0 origin-center text-muted-foreground transition-[color,transform] duration-150 group-hover/section:text-sidebar-accent-foreground group-aria-expanded/collapsible-trigger:rotate-90" });
|
|
26829
26898
|
const isTopLevel = depth === 0;
|
|
26830
26899
|
const rootSelected = node.root?.page.url === pathname;
|
|
26831
|
-
const headerClass = isTopLevel ? "flex min-w-0 items-center text-sidebar-foreground" :
|
|
26900
|
+
const headerClass = isTopLevel ? "flex min-w-0 items-center text-sidebar-foreground" : cn("flex items-center border-l border-l-sidebar-border text-muted-foreground hover:text-sidebar-accent-foreground", { "pl-6": depth > 1 });
|
|
26832
26901
|
const fixedHeader = /* @__PURE__ */ jsx("div", {
|
|
26833
|
-
className:
|
|
26902
|
+
className: cn(headerClass, {
|
|
26834
26903
|
[selectedClass]: rootSelected && !isTopLevel,
|
|
26835
26904
|
"text-sidebar-primary": rootSelected && isTopLevel
|
|
26836
26905
|
}),
|
|
26837
26906
|
children: node.root ? /* @__PURE__ */ jsxs(Link, {
|
|
26838
26907
|
to: node.root.page.url,
|
|
26839
|
-
className:
|
|
26908
|
+
className: cn(isTopLevel ? sectionLabelClass : groupLabelClass, "flex-1"),
|
|
26840
26909
|
children: [resolveIcon(node.icon), node.label]
|
|
26841
26910
|
}) : /* @__PURE__ */ jsxs("span", {
|
|
26842
|
-
className:
|
|
26911
|
+
className: cn(isTopLevel ? sectionLabelClass : groupLabelClass, "flex-1"),
|
|
26843
26912
|
children: [resolveIcon(node.icon), node.label]
|
|
26844
26913
|
})
|
|
26845
26914
|
});
|
|
26846
26915
|
const header = !collapsible ? fixedHeader : drilldown !== false && node.root ? /* @__PURE__ */ jsxs("div", {
|
|
26847
|
-
className:
|
|
26916
|
+
className: cn(headerClass, "group/section flex items-center", {
|
|
26848
26917
|
[selectedClass]: rootSelected && !isTopLevel,
|
|
26849
26918
|
"text-sidebar-primary": rootSelected && isTopLevel
|
|
26850
26919
|
}),
|
|
@@ -26866,7 +26935,7 @@ function CollapsibleGroup({ node, pathname, depth, drilldown, expandLabel, colla
|
|
|
26866
26935
|
render: /* @__PURE__ */ jsx(Button, {
|
|
26867
26936
|
type: "button",
|
|
26868
26937
|
variant: "ghost",
|
|
26869
|
-
className:
|
|
26938
|
+
className: cn(headerClass, "group/section group/collapsible-trigger flex h-auto w-full items-center justify-start gap-[0.4rem] whitespace-normal rounded-none px-0 py-0 text-left hover:bg-transparent aria-expanded:bg-transparent dark:hover:bg-transparent", {
|
|
26870
26939
|
"pb-2 font-bold text-sidebar-foreground": isTopLevel,
|
|
26871
26940
|
"border-l border-l-sidebar-border px-3 py-2 font-medium text-muted-foreground hover:text-sidebar-accent-foreground": !isTopLevel
|
|
26872
26941
|
})
|
|
@@ -26917,7 +26986,7 @@ function NavNodes({ nodes, pathname, depth, drilldown, expandLabel, collapseLabe
|
|
|
26917
26986
|
href: node.href,
|
|
26918
26987
|
target: node.target,
|
|
26919
26988
|
rel: node.target === "_blank" ? "noreferrer" : void 0,
|
|
26920
|
-
className:
|
|
26989
|
+
className: cn("flex min-w-0 items-center gap-[0.4rem] border-l border-l-sidebar-border px-3 py-[0.55rem] text-muted-foreground hover:text-sidebar-accent-foreground [overflow-wrap:anywhere]", { "pl-6": depth > 1 }),
|
|
26921
26990
|
children: [
|
|
26922
26991
|
resolveIcon(node.icon),
|
|
26923
26992
|
node.label,
|
|
@@ -26934,7 +27003,7 @@ function NavNodes({ nodes, pathname, depth, drilldown, expandLabel, collapseLabe
|
|
|
26934
27003
|
const isSelected = url === pathname;
|
|
26935
27004
|
rendered.push(/* @__PURE__ */ jsxs(Link, {
|
|
26936
27005
|
to: url,
|
|
26937
|
-
className:
|
|
27006
|
+
className: cn("flex min-w-0 items-center gap-[0.4rem] border-l border-l-sidebar-border px-3 py-[0.55rem] [overflow-wrap:anywhere]", {
|
|
26938
27007
|
"pl-6": depth > 1,
|
|
26939
27008
|
[selectedClass]: isSelected,
|
|
26940
27009
|
"text-muted-foreground hover:text-sidebar-accent-foreground": !isSelected
|
|
@@ -26966,7 +27035,7 @@ function SideNav({ tabs, navigation, anchors, activeTabId, isSticky, drilldown,
|
|
|
26966
27035
|
const { pathname } = useLocation();
|
|
26967
27036
|
const nodes = navigation[activeTabId] || navigation[tabs[0]?.id] || [];
|
|
26968
27037
|
return /* @__PURE__ */ jsx(ScrollArea, {
|
|
26969
|
-
className:
|
|
27038
|
+
className: cn("w-full max-w-full", { "h-full": isSticky }),
|
|
26970
27039
|
children: /* @__PURE__ */ jsxs("nav", {
|
|
26971
27040
|
className: "flex w-full flex-col gap-6 pr-4 text-sm",
|
|
26972
27041
|
"aria-label": navigationLabel,
|
|
@@ -27022,7 +27091,7 @@ function SheetContent({ className, children, side = "right", showCloseButton = t
|
|
|
27022
27091
|
"data-side": side,
|
|
27023
27092
|
className: cn("fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm", className),
|
|
27024
27093
|
...props,
|
|
27025
|
-
children: [children, showCloseButton && /* @__PURE__ */ jsxs(DialogClose
|
|
27094
|
+
children: [children, showCloseButton && /* @__PURE__ */ jsxs(DialogClose, {
|
|
27026
27095
|
"data-slot": "sheet-close",
|
|
27027
27096
|
render: /* @__PURE__ */ jsx(Button, {
|
|
27028
27097
|
variant: "ghost",
|
package/package.json
CHANGED
|
@@ -1,12 +1,56 @@
|
|
|
1
1
|
import { Link } from 'react-router';
|
|
2
2
|
import { ContextualMenu } from '@/components/ContextualMenu';
|
|
3
|
-
import { ChevronRight } from '@/components/icons';
|
|
3
|
+
import { ChevronRight, FileText } from '@/components/icons';
|
|
4
4
|
import { getLastModified } from '@/lib/content';
|
|
5
5
|
import { getScopeForPage } from '@/lib/docs-config';
|
|
6
6
|
import { resolveLocale } from '@/lib/locale';
|
|
7
|
-
import { docsSite } from '@/lib/site-config';
|
|
7
|
+
import { docsSite, getPageByPathname } from '@/lib/site-config';
|
|
8
8
|
import { resolveContextualOptions } from '@/lib/site-model';
|
|
9
|
-
import type { DocModule, NormalizedDocsPage, SiteModel } from '@/lib/types';
|
|
9
|
+
import type { DocModule, NormalizedDocsPage, RelatedEntry, SiteModel } from '@/lib/types';
|
|
10
|
+
|
|
11
|
+
interface RelatedLink {
|
|
12
|
+
href: string;
|
|
13
|
+
title: string;
|
|
14
|
+
external: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Related-topics entries from frontmatter. Bare paths resolve their title from
|
|
19
|
+
* the docs registry; unknown internal paths without an explicit title are
|
|
20
|
+
* skipped so dead links never render.
|
|
21
|
+
*/
|
|
22
|
+
function resolveRelated(entries: unknown): RelatedLink[] {
|
|
23
|
+
if (!Array.isArray(entries)) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const links: RelatedLink[] = [];
|
|
28
|
+
|
|
29
|
+
for (const entry of entries as RelatedEntry[]) {
|
|
30
|
+
const href = typeof entry === 'string' ? entry : entry?.href;
|
|
31
|
+
|
|
32
|
+
if (!href || typeof href !== 'string') {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const external = !href.startsWith('/');
|
|
37
|
+
const explicitTitle = typeof entry === 'object' ? entry.title : undefined;
|
|
38
|
+
const title = explicitTitle || (external ? href : getPageByPathname(href)?.label);
|
|
39
|
+
|
|
40
|
+
if (!title) {
|
|
41
|
+
if (import.meta.env.DEV) {
|
|
42
|
+
console.warn(
|
|
43
|
+
`[shiso] Related topic "${href}" does not match a page and has no title — skipped.`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
links.push({ href, title, external });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return links;
|
|
53
|
+
}
|
|
10
54
|
|
|
11
55
|
export interface DocContentProps {
|
|
12
56
|
page: NormalizedDocsPage;
|
|
@@ -34,6 +78,7 @@ export function DocContent({ page, doc, site }: DocContentProps) {
|
|
|
34
78
|
? [...new Set([page.tabLabel, page.section])].filter(Boolean).join(' / ')
|
|
35
79
|
: page.section;
|
|
36
80
|
const contextualOptions = resolveContextualOptions(site.contextualOptions, page, site.labels);
|
|
81
|
+
const related = resolveRelated(doc.frontmatter?.related);
|
|
37
82
|
// Dates follow the page's language when it is a valid locale code.
|
|
38
83
|
const dateFormat = new Intl.DateTimeFormat(resolveLocale(page.language, site.locale), {
|
|
39
84
|
dateStyle: 'medium',
|
|
@@ -61,6 +106,32 @@ export function DocContent({ page, doc, site }: DocContentProps) {
|
|
|
61
106
|
<time dateTime={lastModified}>{dateFormat.format(new Date(lastModified))}</time>
|
|
62
107
|
</div>
|
|
63
108
|
)}
|
|
109
|
+
{related.length > 0 && (
|
|
110
|
+
<nav className="mt-8" aria-label={site.labels.relatedTopics}>
|
|
111
|
+
<div className="text-sm text-muted-foreground">{site.labels.relatedTopics}</div>
|
|
112
|
+
<ul className="mt-3 flex flex-col gap-2 text-sm">
|
|
113
|
+
{related.map(({ href, title, external }) => (
|
|
114
|
+
<li key={href} className="flex items-center gap-2">
|
|
115
|
+
<FileText size={14} className="shrink-0 text-muted-foreground" />
|
|
116
|
+
{external ? (
|
|
117
|
+
<a
|
|
118
|
+
href={href}
|
|
119
|
+
target="_blank"
|
|
120
|
+
rel="noreferrer"
|
|
121
|
+
className="text-foreground hover:text-primary"
|
|
122
|
+
>
|
|
123
|
+
{title}
|
|
124
|
+
</a>
|
|
125
|
+
) : (
|
|
126
|
+
<Link to={href} className="text-foreground hover:text-primary">
|
|
127
|
+
{title}
|
|
128
|
+
</Link>
|
|
129
|
+
)}
|
|
130
|
+
</li>
|
|
131
|
+
))}
|
|
132
|
+
</ul>
|
|
133
|
+
</nav>
|
|
134
|
+
)}
|
|
64
135
|
<div className="mt-8 flex items-center justify-between">
|
|
65
136
|
<NavigationButton {...prev} isPrev />
|
|
66
137
|
<NavigationButton {...next} />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { cn } from '@/lib/utils';
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import type { TocEntry } from '@/lib/types';
|
|
4
4
|
|
|
@@ -55,7 +55,7 @@ export function PageLinks({ items = [], title, navigationLabel }: PageLinksProps
|
|
|
55
55
|
<a
|
|
56
56
|
key={id}
|
|
57
57
|
href={`#${id}`}
|
|
58
|
-
className={
|
|
58
|
+
className={cn('block border-l px-3 py-1.5', {
|
|
59
59
|
'border-l-primary font-medium text-primary': isActive,
|
|
60
60
|
'border-border text-muted-foreground hover:text-foreground': !isActive,
|
|
61
61
|
})}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { cn } from '@/lib/utils';
|
|
2
2
|
import { type ReactNode, useEffect, useState } from 'react';
|
|
3
3
|
import { Link, useLocation, useNavigate } from 'react-router';
|
|
4
4
|
import { resolveIcon } from '@/components/docs/utils';
|
|
@@ -96,14 +96,14 @@ function CollapsibleGroup({
|
|
|
96
96
|
const rootSelected = node.root?.page.url === pathname;
|
|
97
97
|
const headerClass = isTopLevel
|
|
98
98
|
? 'flex min-w-0 items-center text-sidebar-foreground'
|
|
99
|
-
:
|
|
99
|
+
: cn(
|
|
100
100
|
'flex items-center border-l border-l-sidebar-border text-muted-foreground hover:text-sidebar-accent-foreground',
|
|
101
101
|
{ 'pl-6': depth > 1 },
|
|
102
102
|
);
|
|
103
103
|
|
|
104
104
|
const fixedHeader = (
|
|
105
105
|
<div
|
|
106
|
-
className={
|
|
106
|
+
className={cn(headerClass, {
|
|
107
107
|
[selectedClass]: rootSelected && !isTopLevel,
|
|
108
108
|
'text-sidebar-primary': rootSelected && isTopLevel,
|
|
109
109
|
})}
|
|
@@ -111,13 +111,13 @@ function CollapsibleGroup({
|
|
|
111
111
|
{node.root ? (
|
|
112
112
|
<Link
|
|
113
113
|
to={node.root.page.url}
|
|
114
|
-
className={
|
|
114
|
+
className={cn(isTopLevel ? sectionLabelClass : groupLabelClass, 'flex-1')}
|
|
115
115
|
>
|
|
116
116
|
{resolveIcon(node.icon)}
|
|
117
117
|
{node.label}
|
|
118
118
|
</Link>
|
|
119
119
|
) : (
|
|
120
|
-
<span className={
|
|
120
|
+
<span className={cn(isTopLevel ? sectionLabelClass : groupLabelClass, 'flex-1')}>
|
|
121
121
|
{resolveIcon(node.icon)}
|
|
122
122
|
{node.label}
|
|
123
123
|
</span>
|
|
@@ -130,7 +130,7 @@ function CollapsibleGroup({
|
|
|
130
130
|
fixedHeader
|
|
131
131
|
) : drilldown !== false && node.root ? (
|
|
132
132
|
<div
|
|
133
|
-
className={
|
|
133
|
+
className={cn(headerClass, 'group/section flex items-center', {
|
|
134
134
|
[selectedClass]: rootSelected && !isTopLevel,
|
|
135
135
|
'text-sidebar-primary': rootSelected && isTopLevel,
|
|
136
136
|
})}
|
|
@@ -159,7 +159,7 @@ function CollapsibleGroup({
|
|
|
159
159
|
<Button
|
|
160
160
|
type="button"
|
|
161
161
|
variant="ghost"
|
|
162
|
-
className={
|
|
162
|
+
className={cn(
|
|
163
163
|
headerClass,
|
|
164
164
|
'group/section group/collapsible-trigger flex h-auto w-full items-center justify-start gap-[0.4rem] whitespace-normal rounded-none px-0 py-0 text-left hover:bg-transparent aria-expanded:bg-transparent dark:hover:bg-transparent',
|
|
165
165
|
{
|
|
@@ -243,7 +243,7 @@ function NavNodes({
|
|
|
243
243
|
href={node.href}
|
|
244
244
|
target={node.target}
|
|
245
245
|
rel={node.target === '_blank' ? 'noreferrer' : undefined}
|
|
246
|
-
className={
|
|
246
|
+
className={cn(
|
|
247
247
|
'flex min-w-0 items-center gap-[0.4rem] border-l border-l-sidebar-border px-3 py-[0.55rem] text-muted-foreground hover:text-sidebar-accent-foreground [overflow-wrap:anywhere]',
|
|
248
248
|
{ 'pl-6': depth > 1 },
|
|
249
249
|
)}
|
|
@@ -264,7 +264,7 @@ function NavNodes({
|
|
|
264
264
|
<Link
|
|
265
265
|
key={url}
|
|
266
266
|
to={url}
|
|
267
|
-
className={
|
|
267
|
+
className={cn(
|
|
268
268
|
'flex min-w-0 items-center gap-[0.4rem] border-l border-l-sidebar-border px-3 py-[0.55rem] [overflow-wrap:anywhere]',
|
|
269
269
|
{
|
|
270
270
|
'pl-6': depth > 1,
|
|
@@ -319,7 +319,7 @@ export function SideNav({
|
|
|
319
319
|
const nodes = navigation[activeTabId] || navigation[tabs[0]?.id] || [];
|
|
320
320
|
|
|
321
321
|
return (
|
|
322
|
-
<ScrollArea className={
|
|
322
|
+
<ScrollArea className={cn('w-full max-w-full', { 'h-full': isSticky })}>
|
|
323
323
|
<nav className="flex w-full flex-col gap-6 pr-4 text-sm" aria-label={navigationLabel}>
|
|
324
324
|
{anchors.length ? (
|
|
325
325
|
<NavNodes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { cn } from '@/lib/utils';
|
|
2
2
|
import { Link, useLocation, useNavigate } from 'react-router';
|
|
3
3
|
import { ConfiguredIcon } from '@/components/ConfiguredIcon';
|
|
4
4
|
import { ChevronRight } from '@/components/icons';
|
|
@@ -62,7 +62,7 @@ export function TopNav({ docs, label }: { docs: NormalizedDocsConfig; label: str
|
|
|
62
62
|
.find(tab => pathname === tab.url || pathname.startsWith(`${tab.url}/`))?.id ||
|
|
63
63
|
tabs[0]?.id;
|
|
64
64
|
const tabClass = (tab: DocsTab) =>
|
|
65
|
-
|
|
65
|
+
cn(
|
|
66
66
|
'flex h-full items-center gap-1 whitespace-nowrap border-transparent border-b-2 font-medium',
|
|
67
67
|
{
|
|
68
68
|
'border-b-primary text-foreground': tab.id === selected,
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
import { XIcon } from 'lucide-react';
|
|
2
1
|
import type { ImgHTMLAttributes } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
Dialog,
|
|
5
|
-
DialogClose,
|
|
6
|
-
DialogContent,
|
|
7
|
-
DialogPortal,
|
|
8
|
-
DialogTitle,
|
|
9
|
-
DialogTrigger,
|
|
10
|
-
} from '@/components/ui/dialog';
|
|
2
|
+
import { Dialog, DialogContent, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
|
11
3
|
import { cn } from '@/lib/utils';
|
|
12
4
|
|
|
13
5
|
export interface ZoomableImageProps extends ImgHTMLAttributes<HTMLImageElement> {
|
|
@@ -64,19 +56,6 @@ export function ZoomableImage({
|
|
|
64
56
|
className="max-h-[calc(100vh-2rem)] max-w-[calc(100vw-2rem)] object-contain"
|
|
65
57
|
/>
|
|
66
58
|
</DialogContent>
|
|
67
|
-
<DialogPortal>
|
|
68
|
-
<DialogClose
|
|
69
|
-
render={
|
|
70
|
-
<button
|
|
71
|
-
type="button"
|
|
72
|
-
className="fixed top-4 right-4 z-[60] inline-flex size-9 items-center justify-center rounded-full bg-black/60 text-white outline-none backdrop-blur-sm hover:bg-black/80 focus-visible:ring-3 focus-visible:ring-white/60"
|
|
73
|
-
aria-label="Close full-size image"
|
|
74
|
-
/>
|
|
75
|
-
}
|
|
76
|
-
>
|
|
77
|
-
<XIcon className="size-5" />
|
|
78
|
-
</DialogClose>
|
|
79
|
-
</DialogPortal>
|
|
80
59
|
</Dialog>
|
|
81
60
|
);
|
|
82
61
|
}
|
package/src/lib/site-model.ts
CHANGED
|
@@ -27,6 +27,7 @@ const SHISO_THEME_LABELS: ThemeLabels = {
|
|
|
27
27
|
searchUnavailable: 'Search unavailable',
|
|
28
28
|
noResults: 'No results',
|
|
29
29
|
lastUpdated: 'Last updated on',
|
|
30
|
+
relatedTopics: 'Related topics',
|
|
30
31
|
notFound: 'Page not found',
|
|
31
32
|
dismissBanner: 'Dismiss banner',
|
|
32
33
|
toggleTheme: 'Toggle theme',
|
package/src/lib/types.ts
CHANGED
|
@@ -444,6 +444,7 @@ export interface ThemeLabels {
|
|
|
444
444
|
searchUnavailable: string;
|
|
445
445
|
noResults: string;
|
|
446
446
|
lastUpdated: string;
|
|
447
|
+
relatedTopics: string;
|
|
447
448
|
notFound: string;
|
|
448
449
|
dismissBanner: string;
|
|
449
450
|
toggleTheme: string;
|
|
@@ -499,12 +500,17 @@ export interface TocEntry {
|
|
|
499
500
|
size: number;
|
|
500
501
|
}
|
|
501
502
|
|
|
503
|
+
/** A related-topics entry: a page path, or a link with an explicit title. */
|
|
504
|
+
export type RelatedEntry = string | { href: string; title?: string };
|
|
505
|
+
|
|
502
506
|
export interface DocFrontmatter {
|
|
503
507
|
title?: string;
|
|
504
508
|
description?: string;
|
|
505
509
|
noindex?: boolean;
|
|
506
510
|
/** Overrides the site-wide `metadata.timestamp` setting for this page. */
|
|
507
511
|
timestamp?: boolean;
|
|
512
|
+
/** Related pages rendered above the prev/next pager. */
|
|
513
|
+
related?: RelatedEntry[];
|
|
508
514
|
[key: string]: unknown;
|
|
509
515
|
}
|
|
510
516
|
|
package/src/styles/global.css
CHANGED
|
@@ -146,24 +146,46 @@
|
|
|
146
146
|
font-size: 0.875rem;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
/* Styled to match the PropertiesTable component: rounded outer border,
|
|
150
|
+
horizontal row separators only, and no header fill. */
|
|
149
151
|
.docs-markdown table {
|
|
150
152
|
width: 100%;
|
|
151
|
-
border-collapse: collapse;
|
|
152
153
|
margin: 1rem 0;
|
|
154
|
+
font-size: 0.875rem;
|
|
155
|
+
border-collapse: separate;
|
|
156
|
+
border-spacing: 0;
|
|
157
|
+
border: 1px solid var(--border);
|
|
158
|
+
border-radius: var(--radius-lg);
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
.docs-markdown th,
|
|
156
162
|
.docs-markdown td {
|
|
157
|
-
border:
|
|
158
|
-
|
|
163
|
+
border: none;
|
|
164
|
+
border-bottom: 1px solid var(--border);
|
|
165
|
+
padding: 0.6rem 0.75rem;
|
|
159
166
|
text-align: left;
|
|
167
|
+
vertical-align: top;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.docs-markdown th:first-child,
|
|
171
|
+
.docs-markdown td:first-child {
|
|
172
|
+
padding-left: 1.5rem;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.docs-markdown th:last-child,
|
|
176
|
+
.docs-markdown td:last-child {
|
|
177
|
+
padding-right: 1.5rem;
|
|
160
178
|
}
|
|
161
179
|
|
|
162
180
|
.docs-markdown th {
|
|
163
|
-
background:
|
|
181
|
+
background: transparent;
|
|
164
182
|
font-weight: 600;
|
|
165
183
|
}
|
|
166
184
|
|
|
185
|
+
.docs-markdown tbody tr:last-child td {
|
|
186
|
+
border-bottom: none;
|
|
187
|
+
}
|
|
188
|
+
|
|
167
189
|
.docs-markdown table code:not(pre code) {
|
|
168
190
|
font-size: 0.75rem;
|
|
169
191
|
}
|
package/vite.config.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import { fileURLToPath } from 'node:url';
|
|
3
4
|
import tailwindcss from '@tailwindcss/vite';
|
|
@@ -281,6 +282,75 @@ function shisoHtml(getDocsConfig: () => DocsConfig): Plugin {
|
|
|
281
282
|
};
|
|
282
283
|
}
|
|
283
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Serves the raw markdown source for `<route>.md` URLs during development,
|
|
287
|
+
* mirroring the `.md` copies the prerenderer publishes next to every page in
|
|
288
|
+
* production builds. The contextual menu's copy/view options and AI links
|
|
289
|
+
* depend on these URLs.
|
|
290
|
+
*/
|
|
291
|
+
function shisoMarkdownDev(getDocsConfig: () => DocsConfig, root: string): Plugin {
|
|
292
|
+
return {
|
|
293
|
+
name: 'shiso-markdown-dev',
|
|
294
|
+
apply: 'serve',
|
|
295
|
+
configureServer(server) {
|
|
296
|
+
server.middlewares.use(async (req, res, next) => {
|
|
297
|
+
const url = (req.url || '').split('?')[0];
|
|
298
|
+
|
|
299
|
+
if ((req.method !== 'GET' && req.method !== 'HEAD') || !url.endsWith('.md')) {
|
|
300
|
+
return next();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const shiso =
|
|
304
|
+
(getDocsConfig() as { $shiso?: { docsPrefix?: string; contentDir?: string } }).$shiso ||
|
|
305
|
+
{};
|
|
306
|
+
const contentDir = (shiso.contentDir ?? 'content/docs').replace(/^\/+|\/+$/g, '');
|
|
307
|
+
const prefixValue = (shiso.docsPrefix ?? '/docs').trim().replace(/\/+$/, '');
|
|
308
|
+
const docsPrefix =
|
|
309
|
+
!prefixValue || prefixValue === '/'
|
|
310
|
+
? ''
|
|
311
|
+
: prefixValue.startsWith('/')
|
|
312
|
+
? prefixValue
|
|
313
|
+
: `/${prefixValue}`;
|
|
314
|
+
|
|
315
|
+
let route = decodeURIComponent(url).slice(0, -'.md'.length);
|
|
316
|
+
const base = server.config.base.replace(/\/+$/, '');
|
|
317
|
+
|
|
318
|
+
if (base && route.startsWith(base)) {
|
|
319
|
+
route = route.slice(base.length);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (docsPrefix && route.startsWith(docsPrefix)) {
|
|
323
|
+
route = route.slice(docsPrefix.length);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const slug = route.replace(/^\/+/, '') || 'index';
|
|
327
|
+
const contentRoot = path.resolve(root, contentDir);
|
|
328
|
+
const candidates = [`${slug}.mdx`, `${slug}.md`, `${slug}/index.mdx`, `${slug}/index.md`];
|
|
329
|
+
|
|
330
|
+
for (const candidate of candidates) {
|
|
331
|
+
const filePath = path.resolve(contentRoot, candidate);
|
|
332
|
+
|
|
333
|
+
// Never read outside the content directory.
|
|
334
|
+
if (!filePath.startsWith(contentRoot + path.sep)) {
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
const source = await readFile(filePath, 'utf8');
|
|
340
|
+
res.setHeader('Content-Type', 'text/markdown; charset=utf-8');
|
|
341
|
+
res.end(source);
|
|
342
|
+
return;
|
|
343
|
+
} catch {
|
|
344
|
+
// Try the next candidate.
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
next();
|
|
349
|
+
});
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
284
354
|
const MDX_REACT_ENTRY = fileURLToPath(import.meta.resolve('@mdx-js/react'));
|
|
285
355
|
const LUCIDE_REACT_ENTRY = path.join(
|
|
286
356
|
path.dirname(fileURLToPath(import.meta.resolve('lucide-react/package.json'))),
|
|
@@ -319,6 +389,7 @@ export default defineConfig(async () => {
|
|
|
319
389
|
path.join(generatedRoot, 'search-index.generated.ts'),
|
|
320
390
|
),
|
|
321
391
|
shisoHtml(getDocsConfig),
|
|
392
|
+
shisoMarkdownDev(getDocsConfig, projectRoot),
|
|
322
393
|
shisoMdx(),
|
|
323
394
|
react({ include: /\.(mdx|md|tsx|ts|jsx|js)$/ }),
|
|
324
395
|
],
|