@vantagecompute/docusaurus-theme 0.4.9 → 0.5.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/README.md CHANGED
@@ -16,8 +16,6 @@ Add the theme to your `docusaurus.config.js`:
16
16
  const {
17
17
  staticDir,
18
18
  rehypeTabsTransform,
19
- navbarLogo,
20
- footerLogo,
21
19
  getProjectVersion,
22
20
  } = require('@vantagecompute/docusaurus-theme');
23
21
 
@@ -30,8 +28,16 @@ const config = {
30
28
  // grows and shrinks reflows the header on every release.
31
29
  tagline: `What this project does (${projectVersion})`,
32
30
 
33
- // Add the Vantage theme
34
- themes: ['@vantagecompute/docusaurus-theme'],
31
+ // Add the Vantage theme. Its one option is the external buttons on the
32
+ // developer navbar: at most two, each a label and an absolute url.
33
+ themes: [
34
+ ['@vantagecompute/docusaurus-theme', {
35
+ navbarLinks: [
36
+ {label: 'GitHub', url: 'https://github.com/vantagecompute/my-project'},
37
+ {label: 'PyPI', url: 'https://pypi.org/project/my-project/'},
38
+ ],
39
+ }],
40
+ ],
35
41
 
36
42
  // Serve shared static assets (fonts, icons, brand mark)
37
43
  staticDirectories: ['static', staticDir],
@@ -47,24 +53,13 @@ const config = {
47
53
  ],
48
54
 
49
55
  themeConfig: {
50
- navbar: {
51
- title: 'my-project',
52
- logo: navbarLogo,
53
- items: [/* ... */],
54
- },
55
- footer: {
56
- style: 'dark',
57
- logo: footerLogo,
58
- links: [/* ... */],
59
- },
56
+ // No navbar and no footer here: the theme renders both. A site under
57
+ // /developer/ gets the developer navbar, anything else the public one.
58
+ prism: {/* ... */},
60
59
  },
61
60
  };
62
61
  ```
63
62
 
64
- `navbarLogo` and `footerLogo` carry the Vantage brand mark, its alt text, and
65
- the right link target for each position. Your site needs no copy of the SVG:
66
- it is served out of `staticDir`.
67
-
68
63
  ## What's included
69
64
 
70
65
  ### Design System CSS
@@ -79,21 +74,26 @@ Served from the package once `staticDir` is in your `staticDirectories`, so no s
79
74
  - **Fonts**: Satoshi (Regular, Medium, Bold + italics) as `.woff` files
80
75
  - **Icons**: Sun/moon toggles, search, external link, GitHub, chevron SVGs
81
76
  - **Brand mark**: `vantage-logo-color.svg`, the current Vantage mark, used by
82
- `navbarLogo` and `footerLogo`. It has no dark variant on purpose: the one
83
- colour mark is drawn to read in both colour modes.
77
+ the theme's navbar. It has no dark variant on purpose: the one colour mark
78
+ is drawn to read in both colour modes.
84
79
  - **Legacy logo**: `vantage-logo.svg`, the older monochrome mark. Kept for the
85
80
  `vantage-docs` hub, which still points at it. New sites should use the
86
81
  brand mark above.
87
82
  - **Favicon**: `favicon.ico`
88
83
 
89
84
  ### Theme Component Overrides
90
- | Component | Description |
85
+ | Component | What it changes |
91
86
  |---|---|
92
- | `ColorModeToggle` | Custom sun/moon SVG icon toggle |
93
- | `DocBreadcrumbs` | Full-path breadcrumb rendering |
94
- | `Navbar/Logo` | Centered site title with the version badge beside it |
95
- | `Tabs` | Bugfix for Docusaurus 3.10 whitespace crash |
96
- | `Navbar/MobileSidebar/SecondaryMenu` | Clean secondary menu render |
87
+ | `Navbar/Content` | The whole navbar, in a public or a developer variant chosen from `baseUrl` (0.5.0) |
88
+ | `Navbar/Logo` | The brand link with the variant's baked href, the centred title and the version badge |
89
+ | `Navbar/MobileSidebar/PrimaryMenu` | The developer navbar's external buttons, in the mobile drawer (0.5.0) |
90
+ | `Navbar/SiteActions` | An empty slot in the public navbar for a site's own controls (0.5.0) |
91
+ | `Navbar/MobileSidebar/SecondaryMenu` | A clean secondary-menu render |
92
+ | `Footer` | Renders nothing (0.5.0) |
93
+ | `ColorModeToggle` | Sun and moon SVG icons in place of the default toggle |
94
+ | `DocBreadcrumbs` | Full-path breadcrumbs instead of the truncated default |
95
+ | `Tabs` | A workaround for a Docusaurus 3.10 crash |
96
+ | `MDXComponents` | Every markdown `table` renders inside a horizontal scroll region (0.4.9) |
97
97
 
98
98
  ### Utilities
99
99
  | Export | Description |
@@ -101,8 +101,7 @@ Served from the package once `staticDir` is in your `staticDirectories`, so no s
101
101
  | `staticDir` | Absolute path to this package's `static/` directory; add it to `staticDirectories` |
102
102
  | `rehypeTabsTransform` | Rehype plugin that transforms lowercase `<tabs>`/`<tabitem>` to React components |
103
103
  | `getProjectVersion()` | Project version inferred from git tags (`git describe --tags --always`), or `"dev"` |
104
- | `navbarLogo` | Navbar logo config: the brand mark, linking to `https://docs.vantagecompute.ai` |
105
- | `footerLogo` | Footer logo config: the same mark, linking to `https://vantagecompute.ai` |
104
+ | `resolveNavbarVariant(baseUrl)` | The rule that picks the public or developer navbar; exported for tooling |
106
105
 
107
106
  ## Customization
108
107
 
@@ -117,19 +116,21 @@ your-docs-site/
117
116
  index.js
118
117
  ```
119
118
 
120
- ### Overriding the logo
121
-
122
- `navbarLogo` and `footerLogo` are plain objects. Spread one to change a field,
123
- and leave the rest to the theme:
119
+ ### Navbar buttons
124
120
 
125
121
  ```js
126
- navbar: {
127
- logo: { ...navbarLogo, href: 'https://docs.vantagecompute.ai/developer/' },
128
- },
122
+ themes: [
123
+ ['@vantagecompute/docusaurus-theme', {
124
+ navbarLinks: [
125
+ {label: 'GitHub', url: 'https://github.com/vantagecompute/my-project'},
126
+ {label: 'PyPI', url: 'https://pypi.org/project/my-project/'},
127
+ ],
128
+ }],
129
+ ],
129
130
  ```
130
131
 
131
- Spread rather than mutate: the objects are shared by everything that imports
132
- them. To render no logo at all, just omit `logo`.
132
+ That is the whole navbar surface a site has. See the docs site's Customization
133
+ page for the public navbar's `SiteActions` slot.
133
134
 
134
135
  ### Extending CSS
135
136
  Add your own CSS in `src/css/custom.css` and reference it in your preset config. Your styles will layer on top of the shared design system:
package/lib/index.cjs CHANGED
@@ -3,12 +3,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.footerLogo = exports.navbarLogo = exports.rehypeTabsTransform = exports.staticDir = void 0;
6
+ exports.rehypeTabsTransform = exports.staticDir = exports.resolveNavbarVariant = exports.MAX_NAVBAR_LINKS = exports.LOGO_HREF = void 0;
7
7
  exports.default = themeVantage;
8
+ exports.validateOptions = validateOptions;
8
9
  exports.getProjectVersion = getProjectVersion;
9
10
  const node_path_1 = __importDefault(require("node:path"));
10
11
  const node_child_process_1 = require("node:child_process");
11
- function themeVantage() {
12
+ const options_cjs_1 = require("./options.cjs");
13
+ var options_cjs_2 = require("./options.cjs");
14
+ Object.defineProperty(exports, "LOGO_HREF", { enumerable: true, get: function () { return options_cjs_2.LOGO_HREF; } });
15
+ Object.defineProperty(exports, "MAX_NAVBAR_LINKS", { enumerable: true, get: function () { return options_cjs_2.MAX_NAVBAR_LINKS; } });
16
+ Object.defineProperty(exports, "resolveNavbarVariant", { enumerable: true, get: function () { return options_cjs_2.resolveNavbarVariant; } });
17
+ function themeVantage(context, options) {
18
+ const variant = (0, options_cjs_1.resolveNavbarVariant)(context.baseUrl);
19
+ const globalData = {
20
+ variant,
21
+ logoHref: options_cjs_1.LOGO_HREF[variant],
22
+ navbarLinks: options.navbarLinks,
23
+ };
12
24
  return {
13
25
  name: '@vantagecompute/docusaurus-theme',
14
26
  getThemePath() {
@@ -20,8 +32,20 @@ function themeVantage() {
20
32
  getClientModules() {
21
33
  return [node_path_1.default.resolve(__dirname, '../src/css/custom.css')];
22
34
  },
35
+ // The navbar variant and the external buttons reach the client this way.
36
+ // Nothing in themeConfig.navbar is read by the theme's components.
37
+ contentLoaded({ actions }) {
38
+ actions.setGlobalData(globalData);
39
+ },
23
40
  };
24
41
  }
42
+ /**
43
+ * Docusaurus calls this before the plugin factory. Hand-rolled rather than Joi
44
+ * so the package carries no validation dependency; see src/options.cts.
45
+ */
46
+ function validateOptions({ options, }) {
47
+ return (0, options_cjs_1.validateVantageThemeOptions)(options);
48
+ }
25
49
  /**
26
50
  * Returns the absolute path to this package's static directory.
27
51
  * Add this to your `staticDirectories` in docusaurus.config.js:
@@ -53,59 +77,4 @@ function getProjectVersion() {
53
77
  }
54
78
  // Re-export the rehype utility (plain JS, lives in src/utils/)
55
79
  exports.rehypeTabsTransform = require(node_path_1.default.resolve(__dirname, '../src/utils/rehypeTabsTransform'));
56
- /**
57
- * The Vantage colour brand mark, as a path relative to a served static
58
- * directory. It resolves once `staticDir` is in `staticDirectories`; no site
59
- * needs its own copy of the SVG.
60
- */
61
- const VANTAGE_LOGO_SRC = 'img/vantage-logo-color.svg';
62
- /**
63
- * Navbar logo for a Vantage documentation site.
64
- *
65
- * ```js
66
- * const { navbarLogo } = require('@vantagecompute/docusaurus-theme');
67
- * themeConfig: { navbar: { title: 'v8x', logo: navbarLogo, items: [...] } }
68
- * ```
69
- *
70
- * Deliberately has no `srcDark`. The single colour mark is drawn to read on
71
- * both colour modes, and a second asset would only be a second thing to keep
72
- * in sync.
73
- *
74
- * `href` points at the docs hub rather than the marketing site: from a spoke's
75
- * documentation the useful "home" is the rest of the documentation. `target`
76
- * is `_self` so that jump replaces the tab instead of opening a new one.
77
- *
78
- * Override by spreading, never by mutating -- the object is shared by every
79
- * site in the process:
80
- *
81
- * ```js
82
- * logo: { ...navbarLogo, href: 'https://docs.vantagecompute.ai/developer/' }
83
- * ```
84
- *
85
- * Omit `logo` entirely to render no navbar logo.
86
- */
87
- exports.navbarLogo = {
88
- alt: 'Vantage Compute Logo',
89
- src: VANTAGE_LOGO_SRC,
90
- href: 'https://docs.vantagecompute.ai',
91
- target: '_self',
92
- };
93
- /**
94
- * Footer logo for a Vantage documentation site.
95
- *
96
- * ```js
97
- * const { footerLogo } = require('@vantagecompute/docusaurus-theme');
98
- * themeConfig: { footer: { style: 'dark', logo: footerLogo, links: [...] } }
99
- * ```
100
- *
101
- * Same mark as {@link navbarLogo}, but `href` points at the marketing site:
102
- * the footer is where a reader who has finished reading looks for the company.
103
- *
104
- * Override by spreading; omit `logo` to render no footer logo.
105
- */
106
- exports.footerLogo = {
107
- alt: 'Vantage Compute Logo',
108
- src: VANTAGE_LOGO_SRC,
109
- href: 'https://vantagecompute.ai',
110
- };
111
80
  //# sourceMappingURL=index.cjs.map
package/lib/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.cts"],"names":[],"mappings":";;;;;;;;AAAA,0DAA6B;AAC7B,2DAA8C;AAG9C;IACE,OAAO;QACL,IAAI,EAAE,kCAAkC;QAExC,YAAY;YACV,OAAO,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QACjD,CAAC;QAED,eAAe;YACb,OAAO,CAAC,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,uCAAuC,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED,gBAAgB;YACd,OAAO,CAAC,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC,CAAC;QAC5D,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACU,QAAA,SAAS,GAAG,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAE9D;;;;GAIG;AACH;IACE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,6BAAQ,EAAC,8BAA8B,EAAE;YACvD,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,+DAA+D;AAClD,QAAA,mBAAmB,GAAG,OAAO,CAAC,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,kCAAkC,CAAC,CAAC,CAAC;AAexG;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,4BAA4B,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACU,QAAA,UAAU,GAAc;IACnC,GAAG,EAAE,sBAAsB;IAC3B,GAAG,EAAE,gBAAgB;IACrB,IAAI,EAAE,gCAAgC;IACtC,MAAM,EAAE,OAAO;CAChB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACU,QAAA,UAAU,GAAc;IACnC,GAAG,EAAE,sBAAsB;IAC3B,GAAG,EAAE,gBAAgB;IACrB,IAAI,EAAE,2BAA2B;CAClC,CAAC"}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.cts"],"names":[],"mappings":";;;;;;;;;AAAA,0DAA6B;AAC7B,2DAA4C;AAE5C,+CAMuB;AAGvB,6CAAgF;AAAxE,wGAAA,SAAS,OAAA;AAAE,+GAAA,gBAAgB,OAAA;AAAE,mHAAA,oBAAoB,OAAA;AAYzD,sBACE,OAAoB,EACpB,OAAoC;IAEpC,MAAM,OAAO,GAAG,IAAA,kCAAoB,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,UAAU,GAA2B;QACzC,OAAO;QACP,QAAQ,EAAE,uBAAS,CAAC,OAAO,CAAC;QAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,kCAAkC;QAExC,YAAY;YACV,OAAO,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QACjD,CAAC;QAED,eAAe;YACb,OAAO,CAAC,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,uCAAuC,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED,gBAAgB;YACd,OAAO,CAAC,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC,CAAC;QAC5D,CAAC;QAED,yEAAyE;QACzE,mEAAmE;QACnE,aAAa,CAAC,EAAC,OAAO,EAAC;YACrB,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,yBAAgC,EAC9B,OAAO,GAC+E;IAEtF,OAAO,IAAA,yCAA2B,EAAC,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;GAUG;AACU,QAAA,SAAS,GAAG,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAE9D;;;;GAIG;AACH;IACE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,6BAAQ,EAAC,8BAA8B,EAAE;YACvD,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,+DAA+D;AAClD,QAAA,mBAAmB,GAAG,OAAO,CAAC,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,kCAAkC,CAAC,CAAC,CAAC"}
package/lib/index.d.cts CHANGED
@@ -1,5 +1,22 @@
1
- import type { Plugin } from '@docusaurus/types';
2
- export default function themeVantage(): Plugin;
1
+ import type { LoadContext, OptionValidationContext, Plugin } from '@docusaurus/types';
2
+ import { type ResolvedVantageThemeOptions, type VantageThemeOptions } from './options.cjs';
3
+ export type { NavbarLink, NavbarVariant, VantageThemeOptions } from './options.cjs';
4
+ export { LOGO_HREF, MAX_NAVBAR_LINKS, resolveNavbarVariant } from './options.cjs';
5
+ /**
6
+ * Shape of the global data the theme's client components read through
7
+ * `usePluginData('@vantagecompute/docusaurus-theme')`.
8
+ */
9
+ export interface VantageThemeGlobalData {
10
+ variant: 'public' | 'developer';
11
+ logoHref: string;
12
+ navbarLinks: ResolvedVantageThemeOptions['navbarLinks'];
13
+ }
14
+ export default function themeVantage(context: LoadContext, options: ResolvedVantageThemeOptions): Plugin;
15
+ /**
16
+ * Docusaurus calls this before the plugin factory. Hand-rolled rather than Joi
17
+ * so the package carries no validation dependency; see src/options.cts.
18
+ */
19
+ export declare function validateOptions({ options, }: OptionValidationContext<VantageThemeOptions | undefined, ResolvedVantageThemeOptions>): ResolvedVantageThemeOptions;
3
20
  /**
4
21
  * Returns the absolute path to this package's static directory.
5
22
  * Add this to your `staticDirectories` in docusaurus.config.js:
@@ -19,56 +36,4 @@ export declare const staticDir: string;
19
36
  */
20
37
  export declare function getProjectVersion(): string;
21
38
  export declare const rehypeTabsTransform: any;
22
- /**
23
- * Shape of a Docusaurus navbar or footer logo entry.
24
- */
25
- export interface ThemeLogo {
26
- alt: string;
27
- src: string;
28
- srcDark?: string;
29
- href: string;
30
- target?: string;
31
- width?: number;
32
- height?: number;
33
- }
34
- /**
35
- * Navbar logo for a Vantage documentation site.
36
- *
37
- * ```js
38
- * const { navbarLogo } = require('@vantagecompute/docusaurus-theme');
39
- * themeConfig: { navbar: { title: 'v8x', logo: navbarLogo, items: [...] } }
40
- * ```
41
- *
42
- * Deliberately has no `srcDark`. The single colour mark is drawn to read on
43
- * both colour modes, and a second asset would only be a second thing to keep
44
- * in sync.
45
- *
46
- * `href` points at the docs hub rather than the marketing site: from a spoke's
47
- * documentation the useful "home" is the rest of the documentation. `target`
48
- * is `_self` so that jump replaces the tab instead of opening a new one.
49
- *
50
- * Override by spreading, never by mutating -- the object is shared by every
51
- * site in the process:
52
- *
53
- * ```js
54
- * logo: { ...navbarLogo, href: 'https://docs.vantagecompute.ai/developer/' }
55
- * ```
56
- *
57
- * Omit `logo` entirely to render no navbar logo.
58
- */
59
- export declare const navbarLogo: ThemeLogo;
60
- /**
61
- * Footer logo for a Vantage documentation site.
62
- *
63
- * ```js
64
- * const { footerLogo } = require('@vantagecompute/docusaurus-theme');
65
- * themeConfig: { footer: { style: 'dark', logo: footerLogo, links: [...] } }
66
- * ```
67
- *
68
- * Same mark as {@link navbarLogo}, but `href` points at the marketing site:
69
- * the footer is where a reader who has finished reading looks for the company.
70
- *
71
- * Override by spreading; omit `logo` to render no footer logo.
72
- */
73
- export declare const footerLogo: ThemeLogo;
74
39
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.cts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,CAAC,OAAO,UAAU,YAAY,IAAI,MAAM,CAgB7C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,QAAuC,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAU1C;AAGD,eAAO,MAAM,mBAAmB,KAAuE,CAAC;AAExG;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,UAAU,EAAE,SAKxB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,EAAE,SAIxB,CAAC"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.cts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,WAAW,EAAE,uBAAuB,EAAE,MAAM,EAAC,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAIL,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACzB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAC,UAAU,EAAE,aAAa,EAAE,mBAAmB,EAAC,MAAM,eAAe,CAAC;AAClF,OAAO,EAAC,SAAS,EAAE,gBAAgB,EAAE,oBAAoB,EAAC,MAAM,eAAe,CAAC;AAEhF;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,QAAQ,GAAG,WAAW,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,2BAA2B,CAAC,aAAa,CAAC,CAAC;CACzD;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAClC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,2BAA2B,GACnC,MAAM,CA6BR;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC9B,OAAO,GACR,EAAE,uBAAuB,CAAC,mBAAmB,GAAG,SAAS,EAAE,2BAA2B,CAAC,GACtF,2BAA2B,CAE5B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,QAAuC,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAU1C;AAGD,eAAO,MAAM,mBAAmB,KAAuE,CAAC"}
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ /**
3
+ * Theme options and the navbar variant rule. Pure functions with no Docusaurus
4
+ * imports, so they are tested directly against the compiled output.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.LOGO_HREF = exports.MAX_NAVBAR_LINKS = void 0;
8
+ exports.resolveNavbarVariant = resolveNavbarVariant;
9
+ exports.validateVantageThemeOptions = validateVantageThemeOptions;
10
+ const DEFAULT_PLUGIN_ID = 'default';
11
+ /** Two is GitHub plus one registry (PyPI, npm, ...). More than that is a nav, and navs drift. */
12
+ exports.MAX_NAVBAR_LINKS = 2;
13
+ /**
14
+ * Where the brand mark links, per variant. Baked in on purpose: a site cannot
15
+ * point its logo anywhere else, which is what keeps the hub consistent.
16
+ */
17
+ exports.LOGO_HREF = {
18
+ public: 'https://docs.vantagecompute.ai/',
19
+ developer: 'https://docs.vantagecompute.ai/developer/',
20
+ };
21
+ /**
22
+ * Every site published under /developer/ (the developer overview and every
23
+ * spoke) gets the developer navbar. Everything else gets the public one.
24
+ * Docusaurus normalises baseUrl to a leading and trailing slash before the
25
+ * plugin sees it.
26
+ */
27
+ function resolveNavbarVariant(baseUrl) {
28
+ return baseUrl.startsWith('/developer/') ? 'developer' : 'public';
29
+ }
30
+ const PREFIX = '[@vantagecompute/docusaurus-theme]';
31
+ function fail(message) {
32
+ throw new Error(`${PREFIX} ${message}`);
33
+ }
34
+ function isPlainObject(value) {
35
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
36
+ }
37
+ function validateLink(value, index) {
38
+ const where = `navbarLinks[${index}]`;
39
+ if (!isPlainObject(value)) {
40
+ fail(`${where} must be an object with "label" and "url".`);
41
+ }
42
+ const extra = Object.keys(value).filter((key) => key !== 'label' && key !== 'url');
43
+ if (extra.length > 0) {
44
+ fail(`${where} has unsupported propert${extra.length === 1 ? 'y' : 'ies'} ` +
45
+ `${extra.map((k) => `"${k}"`).join(', ')}. Only "label" and "url" are ` +
46
+ `accepted; the icon, external-link marker and rel attributes come from the theme.`);
47
+ }
48
+ const { label, url } = value;
49
+ if (typeof label !== 'string' || label.trim() === '') {
50
+ fail(`${where}.label must be a non-empty string.`);
51
+ }
52
+ if (typeof url !== 'string' || url.trim() === '') {
53
+ fail(`${where}.url must be a non-empty string.`);
54
+ }
55
+ let parsed;
56
+ try {
57
+ parsed = new URL(url);
58
+ }
59
+ catch {
60
+ return fail(`${where}.url must be an absolute http(s) URL, got "${url}".`);
61
+ }
62
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
63
+ fail(`${where}.url must be an absolute http(s) URL, got "${url}".`);
64
+ }
65
+ return { label: label.trim(), url };
66
+ }
67
+ /**
68
+ * Validate and normalise the theme options. Throws with a message that names
69
+ * the offending key, so a misconfigured spoke fails its build instead of
70
+ * quietly rendering something off-convention.
71
+ */
72
+ function validateVantageThemeOptions(options) {
73
+ if (options === undefined || options === null) {
74
+ return { id: DEFAULT_PLUGIN_ID, navbarLinks: [] };
75
+ }
76
+ if (!isPlainObject(options)) {
77
+ fail('theme options must be an object.');
78
+ }
79
+ const { navbarLinks, id, ...unknown } = options;
80
+ const unknownKeys = Object.keys(unknown);
81
+ if (unknownKeys.length > 0) {
82
+ fail(`unknown option${unknownKeys.length === 1 ? '' : 's'} ` +
83
+ `${unknownKeys.map((k) => `"${k}"`).join(', ')}. The only option is "navbarLinks".`);
84
+ }
85
+ const resolved = {
86
+ id: typeof id === 'string' ? id : DEFAULT_PLUGIN_ID,
87
+ navbarLinks: [],
88
+ };
89
+ if (navbarLinks === undefined) {
90
+ return resolved;
91
+ }
92
+ if (!Array.isArray(navbarLinks)) {
93
+ fail('"navbarLinks" must be an array of {label, url} objects.');
94
+ }
95
+ if (navbarLinks.length > exports.MAX_NAVBAR_LINKS) {
96
+ fail(`"navbarLinks" allows at most ${exports.MAX_NAVBAR_LINKS} entries, got ${navbarLinks.length}. ` +
97
+ `GitHub plus one package registry is the intended use.`);
98
+ }
99
+ resolved.navbarLinks = navbarLinks.map(validateLink);
100
+ return resolved;
101
+ }
102
+ //# sourceMappingURL=options.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.cjs","sourceRoot":"","sources":["../src/options.cts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Theme options and the navbar variant rule. Pure functions with no Docusaurus
3
+ * imports, so they are tested directly against the compiled output.
4
+ */
5
+ export type NavbarVariant = 'public' | 'developer';
6
+ /** One external button in the developer navbar. Nothing else is configurable. */
7
+ export interface NavbarLink {
8
+ label: string;
9
+ url: string;
10
+ }
11
+ /** What a site may pass in `themes: [['@vantagecompute/docusaurus-theme', {...}]]`. */
12
+ export interface VantageThemeOptions {
13
+ /** External buttons, at most {@link MAX_NAVBAR_LINKS}. Rendered by the developer navbar only. */
14
+ navbarLinks?: NavbarLink[];
15
+ }
16
+ /**
17
+ * Options after validation. `id` is Docusaurus's plugin-instance id. When a
18
+ * plugin exports validateOptions, Docusaurus trusts the returned object to
19
+ * carry it (its own Joi path adds one), and names the plugin's generated data
20
+ * directory after it. Leaving it out crashes the build in path.join.
21
+ */
22
+ export interface ResolvedVantageThemeOptions {
23
+ id: string;
24
+ navbarLinks: NavbarLink[];
25
+ }
26
+ /** Two is GitHub plus one registry (PyPI, npm, ...). More than that is a nav, and navs drift. */
27
+ export declare const MAX_NAVBAR_LINKS = 2;
28
+ /**
29
+ * Where the brand mark links, per variant. Baked in on purpose: a site cannot
30
+ * point its logo anywhere else, which is what keeps the hub consistent.
31
+ */
32
+ export declare const LOGO_HREF: Record<NavbarVariant, string>;
33
+ /**
34
+ * Every site published under /developer/ (the developer overview and every
35
+ * spoke) gets the developer navbar. Everything else gets the public one.
36
+ * Docusaurus normalises baseUrl to a leading and trailing slash before the
37
+ * plugin sees it.
38
+ */
39
+ export declare function resolveNavbarVariant(baseUrl: string): NavbarVariant;
40
+ /**
41
+ * Validate and normalise the theme options. Throws with a message that names
42
+ * the offending key, so a misconfigured spoke fails its build instead of
43
+ * quietly rendering something off-convention.
44
+ */
45
+ export declare function validateVantageThemeOptions(options: unknown): ResolvedVantageThemeOptions;
46
+ //# sourceMappingURL=options.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.cts","sourceRoot":"","sources":["../src/options.cts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEnD,iFAAiF;AACjF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,uFAAuF;AACvF,MAAM,WAAW,mBAAmB;IAClC,iGAAiG;IACjG,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAID,iGAAiG;AACjG,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAGnD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAEnE;AAgDD;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,2BAA2B,CAqCzF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantagecompute/docusaurus-theme",
3
- "version": "0.4.9",
3
+ "version": "0.5.1",
4
4
  "description": "Shared Vantage Compute Docusaurus theme: design system, brand assets, and common theme overrides for all vantagecompute documentation sites.",
5
5
  "license": "MIT",
6
6
  "main": "lib/index.cjs",
@@ -12,6 +12,7 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "build": "tsc",
15
+ "test": "node --test 'test/**/*.test.cjs'",
15
16
  "prepublishOnly": "npm run build"
16
17
  },
17
18
  "peerDependencies": {
@@ -849,12 +849,6 @@ html[data-theme='dark'] .provider-showcase-item {
849
849
  * --vantage-navbar-logo-hide-bp Breakpoint below which logo hides
850
850
  * --vantage-navbar-brand-pl Left padding on the brand area
851
851
  * --vantage-navbar-items-gap Gap between right-side items
852
- * --vantage-footer-bg Footer background colour
853
- * --vantage-footer-text Footer text colour
854
- * --vantage-footer-title-color Footer section-title colour
855
- * --vantage-footer-link-hover Footer link hover colour
856
- * --vantage-footer-separator Footer separator colour
857
- * --vantage-footer-logo-max-h Footer logo max-height
858
852
  */
859
853
  :root {
860
854
  --ifm-navbar-height: 64px;
@@ -869,14 +863,6 @@ html[data-theme='dark'] .provider-showcase-item {
869
863
  --vantage-navbar-logo-hide-bp: 1024px;
870
864
  --vantage-navbar-brand-pl: 6px;
871
865
  --vantage-navbar-items-gap: 10px;
872
-
873
- /* Footer defaults */
874
- --vantage-footer-bg: var(--iris-900);
875
- --vantage-footer-text: #ffffff;
876
- --vantage-footer-title-color: var(--iris-200);
877
- --vantage-footer-link-hover: var(--iris-200);
878
- --vantage-footer-separator: rgba(255, 255, 255, 0.12);
879
- --vantage-footer-logo-max-h: 60px;
880
866
  }
881
867
 
882
868
  .navbar {
@@ -1385,93 +1371,6 @@ details summary[onclick]:hover {
1385
1371
  text-decoration: underline !important;
1386
1372
  }
1387
1373
 
1388
- /* ── Footer ────────────────────────────────────────────────────────── */
1389
- .footer {
1390
- background-color: var(--vantage-footer-bg) !important;
1391
- border-top: 1px solid var(--vantage-navbar-border);
1392
- }
1393
-
1394
- .footer--dark {
1395
- background-color: var(--vantage-footer-bg) !important;
1396
- }
1397
-
1398
- .footer__title,
1399
- .footer__item,
1400
- .footer__link-item,
1401
- .footer__copyright {
1402
- color: var(--vantage-footer-text) !important;
1403
- }
1404
-
1405
- .footer__title {
1406
- font-size: 12px;
1407
- text-transform: uppercase;
1408
- letter-spacing: 0.08em;
1409
- font-weight: 700;
1410
- color: var(--vantage-footer-title-color) !important;
1411
- }
1412
-
1413
- .footer__link-item:hover {
1414
- color: var(--vantage-footer-link-hover) !important;
1415
- }
1416
-
1417
- .footer__separator {
1418
- background-color: var(--vantage-footer-separator) !important;
1419
- }
1420
-
1421
- .footer__links {
1422
- justify-content: center !important;
1423
- }
1424
-
1425
- .footer__col {
1426
- text-align: center !important;
1427
- }
1428
-
1429
- .footer__logo {
1430
- height: auto !important;
1431
- width: auto !important;
1432
- max-height: var(--vantage-footer-logo-max-h);
1433
- }
1434
-
1435
- .footer__link-social {
1436
- display: inline-flex !important;
1437
- align-items: center !important;
1438
- justify-content: center !important;
1439
- width: 40px !important;
1440
- height: 40px !important;
1441
- margin: 0 8px 8px 0 !important;
1442
- padding: 8px !important;
1443
- border-radius: 8px !important;
1444
- background-color: rgba(255, 255, 255, 0.1) !important;
1445
- transition: all 0.3s ease !important;
1446
- text-decoration: none !important;
1447
- }
1448
-
1449
- .footer__link-social:hover {
1450
- background-color: rgba(255, 255, 255, 0.2) !important;
1451
- transform: translateY(-2px) !important;
1452
- text-decoration: none !important;
1453
- }
1454
-
1455
- .footer__link-social img {
1456
- width: 24px !important;
1457
- height: 24px !important;
1458
- filter: brightness(0) invert(1) !important;
1459
- transition: all 0.3s ease !important;
1460
- }
1461
-
1462
- .footer__link-social:hover img {
1463
- filter: brightness(0) invert(1) !important;
1464
- transform: scale(1.1) !important;
1465
- }
1466
-
1467
- [data-theme='dark'] .footer__link-social img {
1468
- filter: brightness(0) invert(1) !important;
1469
- }
1470
-
1471
- [data-theme='light'] .footer__link-social img {
1472
- filter: brightness(0) invert(1) !important;
1473
- }
1474
-
1475
1374
  /* ── DocSearch (Algolia) - full theme ──────────────────────────────── */
1476
1375
  :root {
1477
1376
  /* Core */
@@ -2116,15 +2015,6 @@ article {
2116
2015
  }
2117
2016
  }
2118
2017
 
2119
- /* A site with no navbar.title still gets the version badge, alone in the
2120
- centre. Between 997 and 1024 that is a bare git hash beside the logo on a
2121
- tablet; hide it there. On wider screens the lone centred badge stays. */
2122
- @media (max-width: 1024px) {
2123
- .navbar__center-title:not(:has(.navbar__center-title-text)) {
2124
- display: none;
2125
- }
2126
- }
2127
-
2128
2018
  /* ── Tablet landscape (997 to 1199) ────────────────────────────────── */
2129
2019
  /* Docusaurus treats this band as desktop: a 300px sidebar, a right-hand TOC
2130
2020
  column and the article between them. At 1024 the article is 505px and the
package/src/index.cts CHANGED
@@ -1,8 +1,38 @@
1
1
  import path from 'node:path';
2
- import { execSync } from 'node:child_process';
3
- import type { Plugin } from '@docusaurus/types';
2
+ import {execSync} from 'node:child_process';
3
+ import type {LoadContext, OptionValidationContext, Plugin} from '@docusaurus/types';
4
+ import {
5
+ LOGO_HREF,
6
+ resolveNavbarVariant,
7
+ validateVantageThemeOptions,
8
+ type ResolvedVantageThemeOptions,
9
+ type VantageThemeOptions,
10
+ } from './options.cjs';
11
+
12
+ export type {NavbarLink, NavbarVariant, VantageThemeOptions} from './options.cjs';
13
+ export {LOGO_HREF, MAX_NAVBAR_LINKS, resolveNavbarVariant} from './options.cjs';
14
+
15
+ /**
16
+ * Shape of the global data the theme's client components read through
17
+ * `usePluginData('@vantagecompute/docusaurus-theme')`.
18
+ */
19
+ export interface VantageThemeGlobalData {
20
+ variant: 'public' | 'developer';
21
+ logoHref: string;
22
+ navbarLinks: ResolvedVantageThemeOptions['navbarLinks'];
23
+ }
24
+
25
+ export default function themeVantage(
26
+ context: LoadContext,
27
+ options: ResolvedVantageThemeOptions,
28
+ ): Plugin {
29
+ const variant = resolveNavbarVariant(context.baseUrl);
30
+ const globalData: VantageThemeGlobalData = {
31
+ variant,
32
+ logoHref: LOGO_HREF[variant],
33
+ navbarLinks: options.navbarLinks,
34
+ };
4
35
 
5
- export default function themeVantage(): Plugin {
6
36
  return {
7
37
  name: '@vantagecompute/docusaurus-theme',
8
38
 
@@ -17,9 +47,26 @@ export default function themeVantage(): Plugin {
17
47
  getClientModules() {
18
48
  return [path.resolve(__dirname, '../src/css/custom.css')];
19
49
  },
50
+
51
+ // The navbar variant and the external buttons reach the client this way.
52
+ // Nothing in themeConfig.navbar is read by the theme's components.
53
+ contentLoaded({actions}) {
54
+ actions.setGlobalData(globalData);
55
+ },
20
56
  };
21
57
  }
22
58
 
59
+ /**
60
+ * Docusaurus calls this before the plugin factory. Hand-rolled rather than Joi
61
+ * so the package carries no validation dependency; see src/options.cts.
62
+ */
63
+ export function validateOptions({
64
+ options,
65
+ }: OptionValidationContext<VantageThemeOptions | undefined, ResolvedVantageThemeOptions>):
66
+ ResolvedVantageThemeOptions {
67
+ return validateVantageThemeOptions(options);
68
+ }
69
+
23
70
  /**
24
71
  * Returns the absolute path to this package's static directory.
25
72
  * Add this to your `staticDirectories` in docusaurus.config.js:
@@ -52,74 +99,3 @@ export function getProjectVersion(): string {
52
99
 
53
100
  // Re-export the rehype utility (plain JS, lives in src/utils/)
54
101
  export const rehypeTabsTransform = require(path.resolve(__dirname, '../src/utils/rehypeTabsTransform'));
55
-
56
- /**
57
- * Shape of a Docusaurus navbar or footer logo entry.
58
- */
59
- export interface ThemeLogo {
60
- alt: string;
61
- src: string;
62
- srcDark?: string;
63
- href: string;
64
- target?: string;
65
- width?: number;
66
- height?: number;
67
- }
68
-
69
- /**
70
- * The Vantage colour brand mark, as a path relative to a served static
71
- * directory. It resolves once `staticDir` is in `staticDirectories`; no site
72
- * needs its own copy of the SVG.
73
- */
74
- const VANTAGE_LOGO_SRC = 'img/vantage-logo-color.svg';
75
-
76
- /**
77
- * Navbar logo for a Vantage documentation site.
78
- *
79
- * ```js
80
- * const { navbarLogo } = require('@vantagecompute/docusaurus-theme');
81
- * themeConfig: { navbar: { title: 'v8x', logo: navbarLogo, items: [...] } }
82
- * ```
83
- *
84
- * Deliberately has no `srcDark`. The single colour mark is drawn to read on
85
- * both colour modes, and a second asset would only be a second thing to keep
86
- * in sync.
87
- *
88
- * `href` points at the docs hub rather than the marketing site: from a spoke's
89
- * documentation the useful "home" is the rest of the documentation. `target`
90
- * is `_self` so that jump replaces the tab instead of opening a new one.
91
- *
92
- * Override by spreading, never by mutating -- the object is shared by every
93
- * site in the process:
94
- *
95
- * ```js
96
- * logo: { ...navbarLogo, href: 'https://docs.vantagecompute.ai/developer/' }
97
- * ```
98
- *
99
- * Omit `logo` entirely to render no navbar logo.
100
- */
101
- export const navbarLogo: ThemeLogo = {
102
- alt: 'Vantage Compute Logo',
103
- src: VANTAGE_LOGO_SRC,
104
- href: 'https://docs.vantagecompute.ai',
105
- target: '_self',
106
- };
107
-
108
- /**
109
- * Footer logo for a Vantage documentation site.
110
- *
111
- * ```js
112
- * const { footerLogo } = require('@vantagecompute/docusaurus-theme');
113
- * themeConfig: { footer: { style: 'dark', logo: footerLogo, links: [...] } }
114
- * ```
115
- *
116
- * Same mark as {@link navbarLogo}, but `href` points at the marketing site:
117
- * the footer is where a reader who has finished reading looks for the company.
118
- *
119
- * Override by spreading; omit `logo` to render no footer logo.
120
- */
121
- export const footerLogo: ThemeLogo = {
122
- alt: 'Vantage Compute Logo',
123
- src: VANTAGE_LOGO_SRC,
124
- href: 'https://vantagecompute.ai',
125
- };
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Theme options and the navbar variant rule. Pure functions with no Docusaurus
3
+ * imports, so they are tested directly against the compiled output.
4
+ */
5
+
6
+ export type NavbarVariant = 'public' | 'developer';
7
+
8
+ /** One external button in the developer navbar. Nothing else is configurable. */
9
+ export interface NavbarLink {
10
+ label: string;
11
+ url: string;
12
+ }
13
+
14
+ /** What a site may pass in `themes: [['@vantagecompute/docusaurus-theme', {...}]]`. */
15
+ export interface VantageThemeOptions {
16
+ /** External buttons, at most {@link MAX_NAVBAR_LINKS}. Rendered by the developer navbar only. */
17
+ navbarLinks?: NavbarLink[];
18
+ }
19
+
20
+ /**
21
+ * Options after validation. `id` is Docusaurus's plugin-instance id. When a
22
+ * plugin exports validateOptions, Docusaurus trusts the returned object to
23
+ * carry it (its own Joi path adds one), and names the plugin's generated data
24
+ * directory after it. Leaving it out crashes the build in path.join.
25
+ */
26
+ export interface ResolvedVantageThemeOptions {
27
+ id: string;
28
+ navbarLinks: NavbarLink[];
29
+ }
30
+
31
+ const DEFAULT_PLUGIN_ID = 'default';
32
+
33
+ /** Two is GitHub plus one registry (PyPI, npm, ...). More than that is a nav, and navs drift. */
34
+ export const MAX_NAVBAR_LINKS = 2;
35
+
36
+ /**
37
+ * Where the brand mark links, per variant. Baked in on purpose: a site cannot
38
+ * point its logo anywhere else, which is what keeps the hub consistent.
39
+ */
40
+ export const LOGO_HREF: Record<NavbarVariant, string> = {
41
+ public: 'https://docs.vantagecompute.ai/',
42
+ developer: 'https://docs.vantagecompute.ai/developer/',
43
+ };
44
+
45
+ /**
46
+ * Every site published under /developer/ (the developer overview and every
47
+ * spoke) gets the developer navbar. Everything else gets the public one.
48
+ * Docusaurus normalises baseUrl to a leading and trailing slash before the
49
+ * plugin sees it.
50
+ */
51
+ export function resolveNavbarVariant(baseUrl: string): NavbarVariant {
52
+ return baseUrl.startsWith('/developer/') ? 'developer' : 'public';
53
+ }
54
+
55
+ const PREFIX = '[@vantagecompute/docusaurus-theme]';
56
+
57
+ function fail(message: string): never {
58
+ throw new Error(`${PREFIX} ${message}`);
59
+ }
60
+
61
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
62
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
63
+ }
64
+
65
+ function validateLink(value: unknown, index: number): NavbarLink {
66
+ const where = `navbarLinks[${index}]`;
67
+ if (!isPlainObject(value)) {
68
+ fail(`${where} must be an object with "label" and "url".`);
69
+ }
70
+
71
+ const extra = Object.keys(value).filter((key) => key !== 'label' && key !== 'url');
72
+ if (extra.length > 0) {
73
+ fail(
74
+ `${where} has unsupported propert${extra.length === 1 ? 'y' : 'ies'} ` +
75
+ `${extra.map((k) => `"${k}"`).join(', ')}. Only "label" and "url" are ` +
76
+ `accepted; the icon, external-link marker and rel attributes come from the theme.`,
77
+ );
78
+ }
79
+
80
+ const {label, url} = value;
81
+ if (typeof label !== 'string' || label.trim() === '') {
82
+ fail(`${where}.label must be a non-empty string.`);
83
+ }
84
+ if (typeof url !== 'string' || url.trim() === '') {
85
+ fail(`${where}.url must be a non-empty string.`);
86
+ }
87
+
88
+ let parsed: URL;
89
+ try {
90
+ parsed = new URL(url);
91
+ } catch {
92
+ return fail(`${where}.url must be an absolute http(s) URL, got "${url}".`);
93
+ }
94
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
95
+ fail(`${where}.url must be an absolute http(s) URL, got "${url}".`);
96
+ }
97
+
98
+ return {label: label.trim(), url};
99
+ }
100
+
101
+ /**
102
+ * Validate and normalise the theme options. Throws with a message that names
103
+ * the offending key, so a misconfigured spoke fails its build instead of
104
+ * quietly rendering something off-convention.
105
+ */
106
+ export function validateVantageThemeOptions(options: unknown): ResolvedVantageThemeOptions {
107
+ if (options === undefined || options === null) {
108
+ return {id: DEFAULT_PLUGIN_ID, navbarLinks: []};
109
+ }
110
+ if (!isPlainObject(options)) {
111
+ fail('theme options must be an object.');
112
+ }
113
+
114
+ const {navbarLinks, id, ...unknown} = options;
115
+ const unknownKeys = Object.keys(unknown);
116
+ if (unknownKeys.length > 0) {
117
+ fail(
118
+ `unknown option${unknownKeys.length === 1 ? '' : 's'} ` +
119
+ `${unknownKeys.map((k) => `"${k}"`).join(', ')}. The only option is "navbarLinks".`,
120
+ );
121
+ }
122
+
123
+ const resolved: ResolvedVantageThemeOptions = {
124
+ id: typeof id === 'string' ? id : DEFAULT_PLUGIN_ID,
125
+ navbarLinks: [],
126
+ };
127
+
128
+ if (navbarLinks === undefined) {
129
+ return resolved;
130
+ }
131
+ if (!Array.isArray(navbarLinks)) {
132
+ fail('"navbarLinks" must be an array of {label, url} objects.');
133
+ }
134
+ if (navbarLinks.length > MAX_NAVBAR_LINKS) {
135
+ fail(
136
+ `"navbarLinks" allows at most ${MAX_NAVBAR_LINKS} entries, got ${navbarLinks.length}. ` +
137
+ `GitHub plus one package registry is the intended use.`,
138
+ );
139
+ }
140
+
141
+ resolved.navbarLinks = navbarLinks.map(validateLink);
142
+ return resolved;
143
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * No Vantage documentation site renders a footer. The collapse-sidebar control
3
+ * already frames the bottom of the screen, and the links a footer would carry
4
+ * are the navbar's external buttons. A site that still declares
5
+ * themeConfig.footer builds fine and renders nothing.
6
+ */
7
+ export default function Footer() {
8
+ return null;
9
+ }
@@ -0,0 +1,91 @@
1
+ import React from 'react';
2
+ import clsx from 'clsx';
3
+ import {ErrorCauseBoundary, ThemeClassNames} from '@docusaurus/theme-common';
4
+ import {useNavbarMobileSidebar} from '@docusaurus/theme-common/internal';
5
+ import NavbarItem from '@theme/NavbarItem';
6
+ import NavbarColorModeToggle from '@theme/Navbar/ColorModeToggle';
7
+ import SearchBar from '@theme/SearchBar';
8
+ import NavbarMobileSidebarToggle from '@theme/Navbar/MobileSidebar/Toggle';
9
+ import NavbarLogo from '@theme/Navbar/Logo';
10
+ import NavbarSearch from '@theme/Navbar/Search';
11
+ import NavbarSiteActions from '@theme/Navbar/SiteActions';
12
+ import {toNavbarItems, useVantageNavbar} from '../useVantageNavbar';
13
+
14
+ import styles from './styles.module.css';
15
+
16
+ /**
17
+ * The navbar, replacing theme-classic's Navbar/Content.
18
+ *
19
+ * Two fixed variants, chosen by the theme from the site's baseUrl:
20
+ *
21
+ * public logo | search, SiteActions slot, colour-mode toggle
22
+ * developer logo + centred title and version | external buttons, toggle
23
+ *
24
+ * themeConfig.navbar.items is never read. A site that still declares items
25
+ * gets no error and no rendering; the convention is the theme's to hold.
26
+ */
27
+ function ExternalLinks({items}) {
28
+ return (
29
+ <>
30
+ {items.map((item, i) => (
31
+ <ErrorCauseBoundary
32
+ key={i}
33
+ onError={(error) =>
34
+ new Error(
35
+ `A theme navbar link failed to render: ${JSON.stringify(item)}`,
36
+ {cause: error},
37
+ )
38
+ }>
39
+ <NavbarItem {...item} />
40
+ </ErrorCauseBoundary>
41
+ ))}
42
+ </>
43
+ );
44
+ }
45
+
46
+ function NavbarContentLayout({left, right}) {
47
+ return (
48
+ <div className="navbar__inner">
49
+ <div className={clsx(ThemeClassNames.layout.navbar.containerLeft, 'navbar__items')}>
50
+ {left}
51
+ </div>
52
+ <div
53
+ className={clsx(
54
+ ThemeClassNames.layout.navbar.containerRight,
55
+ 'navbar__items navbar__items--right',
56
+ )}>
57
+ {right}
58
+ </div>
59
+ </div>
60
+ );
61
+ }
62
+
63
+ export default function NavbarContent() {
64
+ const mobileSidebar = useNavbarMobileSidebar();
65
+ const {variant, links} = useVantageNavbar();
66
+
67
+ const left = (
68
+ <>
69
+ {!mobileSidebar.disabled && <NavbarMobileSidebarToggle />}
70
+ <NavbarLogo />
71
+ </>
72
+ );
73
+
74
+ const right =
75
+ variant === 'developer' ? (
76
+ <>
77
+ <ExternalLinks items={toNavbarItems(links)} />
78
+ <NavbarColorModeToggle className={styles.colorModeToggle} />
79
+ </>
80
+ ) : (
81
+ <>
82
+ <NavbarSearch>
83
+ <SearchBar />
84
+ </NavbarSearch>
85
+ <NavbarSiteActions />
86
+ <NavbarColorModeToggle className={styles.colorModeToggle} />
87
+ </>
88
+ );
89
+
90
+ return <NavbarContentLayout left={left} right={right} />;
91
+ }
@@ -0,0 +1,6 @@
1
+ /* The mobile drawer header has its own toggle. */
2
+ @media (max-width: 996px) {
3
+ .colorModeToggle {
4
+ display: none;
5
+ }
6
+ }
@@ -1,36 +1,39 @@
1
1
  import React from 'react';
2
- // Must be @theme-init, NOT @theme-original. This component ships inside a
3
- // theme package that sits in the theme stack, so @theme-original/Navbar/Logo
4
- // resolves back to this same component -- React SSR then recurses without
5
- // bound and exhausts the heap during static site generation. @theme-init is
6
- // the alias for a theme wrapping the implementation below it in the stack.
7
- import Logo from '@theme-init/Navbar/Logo';
8
- import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
2
+ import useBaseUrl from '@docusaurus/useBaseUrl';
3
+ import {useVantageNavbar} from '../useVantageNavbar';
4
+
5
+ // The one colour mark reads on both colour modes, so there is no srcDark and
6
+ // no second asset to keep in sync. Resolves through staticDir, which every
7
+ // Vantage site lists in staticDirectories.
8
+ const LOGO_SRC = 'img/vantage-logo-color.svg';
9
9
 
10
10
  /**
11
- * Brand logo on the left, plus a centered title with the project version
12
- * immediately to its right.
11
+ * The brand link, the centred title and the version badge.
12
+ *
13
+ * Replaces theme-classic's Logo outright instead of wrapping it, because the
14
+ * href is not the site's to choose: the theme bakes it per variant (the docs
15
+ * root for the public navbar, the developer overview for the developer one).
16
+ * A plain anchor rather than @docusaurus/Link because the jump crosses SPA
17
+ * boundaries and should be a full navigation. Same tab: command-click covers
18
+ * "open in a new tab".
13
19
  *
14
- * The title cannot just be centered in place: theme-classic renders it inside
15
- * the brand <a>, beside the logo, so centering it would drag the logo along.
16
- * Instead the in-brand title is hidden by CSS and re-rendered here as its own
17
- * absolutely-centered element, which lets the version badge sit next to it.
20
+ * The markup mirrors theme-classic's (navbar__brand > navbar__logo > img,
21
+ * plus b.navbar__title) so Infima's and this theme's CSS keep applying. On
22
+ * desktop the in-brand title is hidden by CSS and re-rendered centred below,
23
+ * which is what lets the version badge sit beside it.
18
24
  */
19
- export default function LogoWrapper(props) {
20
- const {siteConfig} = useDocusaurusContext();
21
-
22
- const raw = siteConfig.customFields?.projectVersion;
23
- const version = raw
24
- ? String(raw).startsWith('v')
25
- ? String(raw)
26
- : `v${raw}`
27
- : null;
28
-
29
- const title = siteConfig.themeConfig?.navbar?.title;
25
+ export default function NavbarLogo() {
26
+ const {logoHref, title, version} = useVantageNavbar();
27
+ const src = useBaseUrl(LOGO_SRC);
30
28
 
31
29
  return (
32
30
  <>
33
- <Logo {...props} />
31
+ <a className="navbar__brand" href={logoHref}>
32
+ <div className="navbar__logo">
33
+ <img src={src} alt="Vantage Compute Logo" />
34
+ </div>
35
+ {title && <b className="navbar__title text--truncate">{title}</b>}
36
+ </a>
34
37
  {(title || version) && (
35
38
  <div className="navbar__center-title">
36
39
  {title && <span className="navbar__center-title-text">{title}</span>}
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import {useNavbarMobileSidebar} from '@docusaurus/theme-common/internal';
3
+ import NavbarItem from '@theme/NavbarItem';
4
+ import {toNavbarItems, useVantageNavbar} from '../../useVantageNavbar';
5
+
6
+ /**
7
+ * The primary panel of the mobile drawer. theme-classic fills it from
8
+ * themeConfig.navbar.items; this theme fills it from the same external
9
+ * buttons the developer navbar shows. The public navbar has nothing to put
10
+ * here (search and the site's actions stay in the bar), so the panel is empty
11
+ * and the drawer opens straight onto the docs sidebar.
12
+ */
13
+ export default function NavbarMobilePrimaryMenu() {
14
+ const mobileSidebar = useNavbarMobileSidebar();
15
+ const {variant, links} = useVantageNavbar();
16
+
17
+ if (variant !== 'developer' || links.length === 0) {
18
+ return null;
19
+ }
20
+
21
+ return (
22
+ <ul className="menu__list">
23
+ {toNavbarItems(links).map((item, i) => (
24
+ <NavbarItem mobile {...item} onClick={() => mobileSidebar.toggle()} key={i} />
25
+ ))}
26
+ </ul>
27
+ );
28
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A slot in the public navbar, between search and the colour-mode toggle.
3
+ * Empty here. The main docs site overrides it in its own src/theme/ to render
4
+ * its Ask AI button; that component talks to the Vantage AI backend and does
5
+ * not belong in a theme package.
6
+ *
7
+ * The developer navbar does not render this slot.
8
+ */
9
+ export default function NavbarSiteActions() {
10
+ return null;
11
+ }
@@ -0,0 +1,50 @@
1
+ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
2
+ import {usePluginData} from '@docusaurus/useGlobalData';
3
+
4
+ /**
5
+ * Everything the theme's navbar components need, in one place. The variant,
6
+ * the brand-link href and the external buttons come from the theme's Node
7
+ * side (setGlobalData in lib/index.cjs). The title and version come from the
8
+ * site config. themeConfig.navbar is deliberately never read.
9
+ */
10
+ export function useVantageNavbar() {
11
+ const {siteConfig} = useDocusaurusContext();
12
+ const {variant, logoHref, navbarLinks} = usePluginData('@vantagecompute/docusaurus-theme');
13
+
14
+ // The developer navbar names the project beside its version, which is how
15
+ // a reader confirms they are on the version they think they are. The public
16
+ // navbar is logo-only: no title and no version badge, even when the site
17
+ // sets customFields.projectVersion.
18
+ const developer = variant === 'developer';
19
+ const raw = developer ? siteConfig.customFields?.projectVersion : null;
20
+ const version = raw
21
+ ? String(raw).startsWith('v')
22
+ ? String(raw)
23
+ : `v${raw}`
24
+ : null;
25
+
26
+ return {
27
+ variant,
28
+ logoHref,
29
+ links: navbarLinks,
30
+ title: developer ? siteConfig.title : null,
31
+ version,
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Map the validated {label, url} links onto theme-classic NavbarItem props.
37
+ * External destinations open in a new tab, the accepted convention for
38
+ * leaving a site. NavbarNavLink appends the external-link icon on its own
39
+ * because href is external and label is set.
40
+ */
41
+ export function toNavbarItems(links) {
42
+ return links.map((link) => ({
43
+ label: link.label,
44
+ href: link.url,
45
+ position: 'right',
46
+ target: '_blank',
47
+ rel: 'noopener noreferrer',
48
+ className: 'navbar__external-link',
49
+ }));
50
+ }