@dreamlake/dockit 0.1.1 → 0.2.1

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defineSiteConfig } from "./site-config.js";
2
2
  import { siteConfig } from "./site-config.js";
3
3
  import { initNavigation } from "./lib/navigation.js";
4
- import { getAdjacentPages, groupedPages, groupedVisiblePages, pages } from "./lib/navigation.js";
4
+ import { findPage, getAdjacentPages, groupedPages, groupedVisiblePages, normalizePath, pages } from "./lib/navigation.js";
5
5
  import { initSearchIndex } from "./lib/search-index.js";
6
6
  import { rawMarkdown, search, searchIndex } from "./lib/search-index.js";
7
7
  import { defaultResults, searchPages } from "./lib/pagefind-search.js";
@@ -26,6 +26,7 @@ import { ThemeProvider, useTheme } from "./components/ThemeProvider.js";
26
26
  import { ThemeToggle } from "./components/ThemeToggle.js";
27
27
  import { TOC } from "./components/TOC.js";
28
28
  import { Topbar } from "./components/Topbar.js";
29
+ import { VersionBadge } from "./components/VersionBadge.js";
29
30
  import { Layout, useMerge } from "./renderer/Layout.js";
30
31
  function initDocs(opts) {
31
32
  defineSiteConfig(opts.site);
@@ -52,9 +53,11 @@ export {
52
53
  ThemeProvider,
53
54
  ThemeToggle,
54
55
  Topbar,
56
+ VersionBadge,
55
57
  defaultResults,
56
58
  defineSiteConfig,
57
59
  docKey,
60
+ findPage,
58
61
  getAdjacentPages,
59
62
  groupedPages,
60
63
  groupedVisiblePages,
@@ -63,6 +66,7 @@ export {
63
66
  initSearchIndex,
64
67
  initTabs,
65
68
  mdxComponents,
69
+ normalizePath,
66
70
  pages,
67
71
  rawMarkdown,
68
72
  search,
@@ -29,6 +29,9 @@ export interface PageFrontmatter {
29
29
  draft?: boolean;
30
30
  hidden?: boolean;
31
31
  noindex?: boolean;
32
+ /** Shorthand for an internal developer note (studio's flag): implies
33
+ * both `hidden: true` and `noindex: true`. */
34
+ dev?: boolean;
32
35
  tocLevel?: 2 | 3;
33
36
  fullscreen?: boolean;
34
37
  }
@@ -54,6 +57,18 @@ export declare const groupedPages: NavGroup[];
54
57
  /** Same shape, but filtered to only-visible pages. Equivalent to
55
58
  * `groupedPages` when no page declares `hidden: true`. */
56
59
  export declare const groupedVisiblePages: NavGroup[];
60
+ /** Normalize a pathname for comparison against `PageMeta.path`: strip any
61
+ * query/hash and drop a trailing slash (except for root `/`). Vike's
62
+ * prerender and client routing can disagree on the trailing slash — e.g.
63
+ * a `/develop` prerender hydrating at `/develop/` — and a raw `===` match
64
+ * then resolves the page on the server but not the client, which renders
65
+ * *different* DOM (breadcrumb, `aria-current`) on each side and trips
66
+ * React hydration. Normalizing both sides keeps SSR and hydration in step. */
67
+ export declare function normalizePath(url: string): string;
68
+ /** Resolve the `PageMeta` for a pathname, tolerant of a trailing slash.
69
+ * Prefer this over `pages.find(p => p.path === urlPathname)` anywhere the
70
+ * result drives rendered output, so server and client agree. */
71
+ export declare function findPage(urlPathname: string): PageMeta | undefined;
57
72
  /**
58
73
  * Populate the navigation singletons from the site's eager page glob:
59
74
  *
@@ -2,6 +2,16 @@ const pages = [];
2
2
  const groupedPages = [];
3
3
  const groupedVisiblePages = [];
4
4
  let sectionOrderList = [];
5
+ function normalizePath(url) {
6
+ if (!url) return "/";
7
+ const clean = url.split("?")[0].split("#")[0];
8
+ if (clean === "/") return "/";
9
+ return clean.replace(/\/+$/, "") || "/";
10
+ }
11
+ function findPage(urlPathname) {
12
+ const norm = normalizePath(urlPathname);
13
+ return pages.find((p) => p.path === norm);
14
+ }
5
15
  function groupPages(items) {
6
16
  const map = /* @__PURE__ */ new Map();
7
17
  for (const p of items) {
@@ -18,6 +28,7 @@ function initNavigation(modules, sectionOrder = []) {
18
28
  const fm = mod.frontmatter ?? {};
19
29
  const dir = filePath.replace(/^.*?\bpages\//, "").replace(/\/\+Page\.mdx$/, "");
20
30
  const path = dir === "index" ? "/" : `/${dir}`;
31
+ const isDev = fm.dev === true;
21
32
  return {
22
33
  path,
23
34
  title: fm.title ?? dir,
@@ -25,8 +36,8 @@ function initNavigation(modules, sectionOrder = []) {
25
36
  order: fm.order ?? 99,
26
37
  description: fm.description,
27
38
  draft: fm.draft === true,
28
- hidden: fm.hidden === true,
29
- noindex: fm.noindex === true,
39
+ hidden: fm.hidden === true || isDev,
40
+ noindex: fm.noindex === true || isDev,
30
41
  tocLevel: fm.tocLevel,
31
42
  fullscreen: fm.fullscreen === true
32
43
  };
@@ -39,9 +50,10 @@ function initNavigation(modules, sectionOrder = []) {
39
50
  groupedVisiblePages.push(...groupPages(pages.filter((p) => !p.hidden)));
40
51
  }
41
52
  function getAdjacentPages(path, opts = {}) {
42
- const here = pages.find((p) => p.path === path);
53
+ const norm = normalizePath(path);
54
+ const here = pages.find((p) => p.path === norm);
43
55
  const list = opts.includeHidden || here?.hidden ? pages : pages.filter((p) => !p.hidden);
44
- const idx = list.findIndex((p) => p.path === path);
56
+ const idx = list.findIndex((p) => p.path === norm);
45
57
  return {
46
58
  current: idx >= 0 ? list[idx] : null,
47
59
  prev: idx > 0 ? list[idx - 1] : null,
@@ -49,9 +61,11 @@ function getAdjacentPages(path, opts = {}) {
49
61
  };
50
62
  }
51
63
  export {
64
+ findPage,
52
65
  getAdjacentPages,
53
66
  groupedPages,
54
67
  groupedVisiblePages,
55
68
  initNavigation,
69
+ normalizePath,
56
70
  pages
57
71
  };
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { useState, useContext, createContext, useEffect } from "react";
2
+ import { useState, useMemo, useContext, createContext, useEffect } from "react";
3
3
  import { MDXProvider } from "@mdx-js/react";
4
4
  import { usePageContext } from "vike-react/usePageContext";
5
5
  import { ThemeProvider } from "../components/ThemeProvider.js";
@@ -9,14 +9,14 @@ import { TOC } from "../components/TOC.js";
9
9
  import { DocFooter } from "../components/DocFooter.js";
10
10
  import { SearchPalette } from "../components/SearchPalette.js";
11
11
  import { mdxComponents } from "../components/mdx-components.js";
12
- import { pages } from "../lib/navigation.js";
12
+ import { findPage } from "../lib/navigation.js";
13
13
  import { siteConfig } from "../site-config.js";
14
14
  const SPRING = "cubic-bezier(0.4, 0.7, 0.3, 1)";
15
15
  const MergeContext = createContext(false);
16
16
  const useMerge = () => useContext(MergeContext);
17
17
  function PageBreadcrumb() {
18
18
  const { urlPathname } = usePageContext();
19
- const current = pages.find((p) => p.path === urlPathname);
19
+ const current = findPage(urlPathname);
20
20
  const merged = useMerge();
21
21
  if (!current) return null;
22
22
  return /* @__PURE__ */ jsxs(
@@ -81,7 +81,7 @@ function HeadSync() {
81
81
  const { urlPathname } = usePageContext();
82
82
  useEffect(() => {
83
83
  if (typeof document === "undefined") return;
84
- const current = pages.find((p) => p.path === urlPathname);
84
+ const current = findPage(urlPathname);
85
85
  document.title = current ? `${current.title} — ${siteConfig.brand}` : siteConfig.brand;
86
86
  const desc = current?.description ?? "";
87
87
  let tag = document.querySelector('meta[name="description"]');
@@ -111,13 +111,20 @@ function HeadSync() {
111
111
  }
112
112
  function Layout({ children }) {
113
113
  const { urlPathname } = usePageContext();
114
- const current = pages.find((p) => p.path === urlPathname);
114
+ const current = findPage(urlPathname);
115
115
  const isFullscreen = current?.fullscreen === true;
116
116
  const [searchOpen, setSearchOpen] = useState(false);
117
117
  const [searchQuery, setSearchQuery] = useState("");
118
118
  const [merged, setMerged] = useState(false);
119
119
  const [sidebarOpen, setSidebarOpen] = useState(false);
120
- return /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(MDXProvider, { components: mdxComponents, children: /* @__PURE__ */ jsxs(MergeContext.Provider, { value: merged, children: [
120
+ const mergedMdxComponents = useMemo(
121
+ () => ({
122
+ ...mdxComponents,
123
+ ...siteConfig.mdxComponents ?? {}
124
+ }),
125
+ []
126
+ );
127
+ return /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(MDXProvider, { components: mergedMdxComponents, children: /* @__PURE__ */ jsxs(MergeContext.Provider, { value: merged, children: [
121
128
  /* @__PURE__ */ jsx(
122
129
  Topbar,
123
130
  {
@@ -3,14 +3,14 @@ import ReactDOMServer from "react-dom/server";
3
3
  import { Layout } from "./Layout.js";
4
4
  import { PageContextProvider } from "vike-react/usePageContext";
5
5
  import { dangerouslySkipEscape, escapeInject } from "vike/server";
6
- import { pages } from "../lib/navigation.js";
6
+ import { findPage } from "../lib/navigation.js";
7
7
  import { siteConfig } from "../site-config.js";
8
8
  async function onRenderHtml(pageContext) {
9
9
  const { Page, pageProps, urlPathname } = pageContext;
10
10
  const html = ReactDOMServer.renderToString(
11
11
  /* @__PURE__ */ jsx(PageContextProvider, { pageContext, children: /* @__PURE__ */ jsx(Layout, { children: /* @__PURE__ */ jsx(Page, { ...pageProps }) }) })
12
12
  );
13
- const current = pages.find((p) => p.path === urlPathname);
13
+ const current = findPage(urlPathname);
14
14
  const title = current ? `${current.title} — ${siteConfig.brand}` : siteConfig.brand;
15
15
  const description = current?.description ?? "";
16
16
  const mdHref = current && !current.noindex ? current.path === "/" ? "/index.md" : `${current.path}.md` : null;
@@ -55,6 +55,15 @@ export interface SiteConfig {
55
55
  * "Edit this page" link (`<docsRepoUrl>/edit/<docsBranch>/<docsPagesPath>/…`).
56
56
  * Defaults to 'docs/pages'. */
57
57
  docsPagesPath?: string;
58
+ /** Topbar theme control style. `'cycle'` (default) is a single button
59
+ * that steps light → system → dark; `'segmented'` is the three-button
60
+ * slider with a sliding indicator (the uikit/dreamlake docs style). */
61
+ themeToggle?: 'cycle' | 'segmented';
62
+ /** Site-specific MDX components merged over the library's provider map,
63
+ * so pages can use custom tags (e.g. `<Card>`, `<ApiStatus>`) without
64
+ * per-page imports. Same-name entries override the library's. Typed
65
+ * loosely to avoid coupling this module to React. */
66
+ mdxComponents?: Record<string, unknown>;
58
67
  }
59
68
  export declare const siteConfig: SiteConfig;
60
69
  /** Merge site-provided values into the singleton (mutates in place). */
@@ -10,7 +10,8 @@ const siteConfig = {
10
10
  summary: "",
11
11
  versionChips: [],
12
12
  gitHash: "",
13
- docsPagesPath: "docs/pages"
13
+ docsPagesPath: "docs/pages",
14
+ themeToggle: "cycle"
14
15
  };
15
16
  function defineSiteConfig(cfg) {
16
17
  Object.assign(siteConfig, cfg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamlake/dockit",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "The DreamLake documentation shell — Vike + React + Tailwind v4 + MDX docs framework with sidebar, TOC, search, and theming",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,3 +31,14 @@ COREPACK_INTEGRITY_KEYS = "0"
31
31
 
32
32
  [context.branch-deploy]
33
33
  # Applies to every non-production branch push.
34
+
35
+ # ─── Version switcher (VersionBadge) ─────────────────────────────────
36
+ # The version-switcher dropdown on old v/* branch deploys fetches the
37
+ # production manifest cross-origin so its list never goes stale — that
38
+ # needs a CORS header on /versions.json.
39
+
40
+ [[headers]]
41
+ for = "/versions.json"
42
+
43
+ [headers.values]
44
+ Access-Control-Allow-Origin = "*"