blume 1.1.1 → 1.1.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.
@@ -707,6 +707,15 @@ export const buildNavigation = (
707
707
  }
708
708
  }
709
709
 
710
+ // A tab pointing at the tree root spans the whole sidebar rather than one
711
+ // section, so it must not feed tab-section hoisting. `tabs` carries final
712
+ // paths (localized, then based), so the root is compared in the same space —
713
+ // a root-level `(group)` folder's routePath is exactly the based/localized
714
+ // prefix (`/docs`, `/fr`) and a bare `"/"` check would miss the match (or,
715
+ // under a base, falsely scope a group named like the prefix). Carried on the
716
+ // returned navigation so render-time scoping compares in the same space too.
717
+ const rootTabPath = withBasePath(basePath, options.localizedRoot ?? "/");
718
+
710
719
  if (options.sidebar) {
711
720
  const sidebar = buildConfigSidebar(
712
721
  options.sidebar,
@@ -716,19 +725,13 @@ export const buildNavigation = (
716
725
  );
717
726
  return {
718
727
  featured,
728
+ root: rootTabPath,
719
729
  selectors,
720
730
  sidebar,
721
731
  tabs: withTabHrefs(tabs, sidebar),
722
732
  };
723
733
  }
724
734
 
725
- // A tab pointing at the tree root spans the whole sidebar rather than one
726
- // section, so it must not feed tab-section hoisting. `tabs` carries final
727
- // paths (localized, then based), so the root is compared in the same space —
728
- // a root-level `(group)` folder's routePath is exactly the based/localized
729
- // prefix (`/docs`, `/fr`) and a bare `"/"` check would miss the match (or,
730
- // under a base, falsely scope a group named like the prefix).
731
- const rootTabPath = withBasePath(basePath, options.localizedRoot ?? "/");
732
735
  const sidebar = buildFileSystemSidebar(
733
736
  pages,
734
737
  options.folderMeta,
@@ -742,6 +745,7 @@ export const buildNavigation = (
742
745
  );
743
746
  return {
744
747
  featured,
748
+ root: rootTabPath,
745
749
  selectors,
746
750
  sidebar,
747
751
  tabs: withTabHrefs(tabs, sidebar),
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  import type { ComponentMarkdown } from "../ai/component-markdown.ts";
4
+ import type { CodeTheme } from "../markdown/themes.ts";
4
5
  import { normalizeRoute } from "../openapi/references.ts";
5
6
  import { normalizeXHandle } from "../seo/x-handle.ts";
6
7
  import { FONT_SLUGS, isFontSlug } from "../theme/fonts.ts";
@@ -950,9 +951,38 @@ const githubConfigSchema = z.strictObject({
950
951
  repo: z.string(),
951
952
  });
952
953
 
954
+ const codeThemeSchema = z.custom<CodeTheme>((value) => {
955
+ if (typeof value === "string") {
956
+ return true;
957
+ }
958
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
959
+ return false;
960
+ }
961
+ // Token rules live in `settings` (Shiki's canonical field, also the TextMate
962
+ // form themes like createCssVariablesTheme() produce) or `tokenColors` (the
963
+ // VS Code spelling Shiki falls back to). A colors-only theme (editor fg/bg,
964
+ // no token rules) is also valid — Shiki renders it from `colors` alone. Each
965
+ // field present must have the right shape, and at least one must be present.
966
+ const theme = value as Record<string, unknown>;
967
+ const settingsValid =
968
+ theme.settings === undefined || Array.isArray(theme.settings);
969
+ const tokenColorsValid =
970
+ theme.tokenColors === undefined || Array.isArray(theme.tokenColors);
971
+ const colorsValid =
972
+ theme.colors === undefined ||
973
+ (typeof theme.colors === "object" &&
974
+ theme.colors !== null &&
975
+ !Array.isArray(theme.colors));
976
+ const hasContent =
977
+ theme.settings !== undefined ||
978
+ theme.tokenColors !== undefined ||
979
+ theme.colors !== undefined;
980
+ return settingsValid && tokenColorsValid && colorsValid && hasContent;
981
+ }, "Expected a Shiki theme name or custom theme object");
982
+
953
983
  const codeBlockThemeSchema = z.strictObject({
954
- dark: z.string().default("github-dark"),
955
- light: z.string().default("github-light"),
984
+ dark: codeThemeSchema.default("github-dark"),
985
+ light: codeThemeSchema.default("github-light"),
956
986
  });
957
987
 
958
988
  const codeBlocksConfigSchema = z.strictObject({
package/src/core/types.ts CHANGED
@@ -227,6 +227,13 @@ export interface Navigation {
227
227
  tabs: NavTab[];
228
228
  selectors: NavSelector[];
229
229
  sidebar: NavNode[];
230
+ /**
231
+ * The tree root in final path space — localized and based (`/`, `/en`,
232
+ * `/docs`). Tab paths arrive in the same space, so the tab sitting at this
233
+ * path spans the whole tree and must be scoped as the root tab, not as a
234
+ * section tab. Absent on older serialized graphs; treat as `/`.
235
+ */
236
+ root?: string;
230
237
  /** Pinned links shown above the sidebar sections, unscoped by tab. */
231
238
  featured: FeaturedLink[];
232
239
  /** Repo URL for the header link, or null when hidden (`navigation.repo`). */
@@ -21,6 +21,8 @@ import { tableWrapPlugin } from "./table-wrap.ts";
21
21
  import { DEFAULT_CODE_THEMES } from "./themes.ts";
22
22
  import type { CodeThemes } from "./themes.ts";
23
23
 
24
+ export type { CodeTheme, CodeThemes } from "./themes.ts";
25
+
24
26
  /** A Shiki transformer, derived from the upstream factories' return type. */
25
27
  type ShikiTransformer = ReturnType<typeof transformerNotationDiff>;
26
28
 
@@ -64,7 +64,7 @@ type InlineHighlighter = (
64
64
  defaultColor: false;
65
65
  lang: string;
66
66
  structure: "inline";
67
- themes: { dark: string; light: string };
67
+ themes: CodeThemes;
68
68
  }
69
69
  ) => Promise<{ children: HastNode[] }>;
70
70
 
@@ -6,6 +6,11 @@
6
6
  * single home for the github fallback used when nothing is configured.
7
7
  */
8
8
 
9
+ import type { ThemeRegistrationAny } from "shiki";
10
+
11
+ /** A bundled Shiki theme name or an inline custom Shiki theme definition. */
12
+ export type CodeTheme = string | ThemeRegistrationAny;
13
+
9
14
  /**
10
15
  * A light/dark Shiki theme pair (`markdown.codeBlocks.theme`). A `type` (not an
11
16
  * `interface`) so it keeps the implicit index signature Shiki's `themes`
@@ -13,8 +18,8 @@
13
18
  */
14
19
  // oxlint-disable-next-line typescript/consistent-type-definitions -- interface loses the implicit index signature Shiki's `themes` param needs
15
20
  export type CodeThemes = {
16
- dark: string;
17
- light: string;
21
+ dark: CodeTheme;
22
+ light: CodeTheme;
18
23
  };
19
24
 
20
25
  /** The default pair, used when `markdown.codeBlocks.theme` is unset. */