zudoku 0.80.1 → 0.81.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.
Files changed (34) hide show
  1. package/dist/cli/cli.js +53 -10
  2. package/dist/declarations/config/validators/ZudokuConfig.d.ts +28 -0
  3. package/dist/declarations/lib/components/context/ZudokuContext.d.ts +1 -1
  4. package/dist/declarations/lib/components/navigation/SidebarToggle.d.ts +1 -0
  5. package/dist/declarations/lib/components/navigation/sidebarStore.d.ts +6 -0
  6. package/dist/declarations/lib/core/ZudokuContext.d.ts +5 -0
  7. package/dist/declarations/lib/hooks/index.d.ts +1 -0
  8. package/dist/declarations/lib/plugins/markdown/index.d.ts +2 -1
  9. package/dist/declarations/lib/plugins/search-inkeep/index.d.ts +8 -2
  10. package/dist/flat-config.d.ts +10 -0
  11. package/docs/components/callout.mdx +11 -18
  12. package/docs/configuration/search.md +36 -0
  13. package/docs/configuration/site.md +38 -0
  14. package/docs/customization/colors-theme.mdx +51 -40
  15. package/package.json +4 -4
  16. package/src/app/main.css +4 -1
  17. package/src/config/validators/ZudokuConfig.ts +24 -0
  18. package/src/lib/components/Layout.tsx +6 -1
  19. package/src/lib/components/Main.tsx +4 -0
  20. package/src/lib/components/NotFoundPage.tsx +1 -1
  21. package/src/lib/components/navigation/NavigationCategory.tsx +4 -2
  22. package/src/lib/components/navigation/NavigationItem.tsx +4 -4
  23. package/src/lib/components/navigation/NavigationWrapper.tsx +9 -2
  24. package/src/lib/components/navigation/SidebarToggle.tsx +68 -0
  25. package/src/lib/components/navigation/sidebarStore.ts +8 -0
  26. package/src/lib/core/ZudokuContext.ts +5 -0
  27. package/src/lib/hooks/index.ts +1 -0
  28. package/src/lib/plugins/markdown/MdxPage.tsx +8 -1
  29. package/src/lib/plugins/markdown/index.tsx +2 -0
  30. package/src/lib/plugins/openapi/SchemaInfo.tsx +7 -1
  31. package/src/lib/plugins/search-inkeep/index.tsx +45 -14
  32. package/src/vite/mdx/remark-link-rewrite.ts +39 -2
  33. package/src/vite/plugin-markdown-export.ts +19 -14
  34. package/src/vite/plugin-mdx.ts +18 -1
package/dist/cli/cli.js CHANGED
@@ -3023,6 +3023,7 @@ var init_ZudokuConfig = __esm({
3023
3023
  showVersionSelect: z7.enum(["always", "if-available", "hide"]),
3024
3024
  expandAllTags: z7.boolean(),
3025
3025
  showInfoPage: z7.boolean(),
3026
+ disableSecurity: z7.boolean(),
3026
3027
  schemaDownload: z7.object({
3027
3028
  enabled: z7.boolean(),
3028
3029
  fileName: z7.string().regex(/^[A-Za-z0-9_-]+$/).optional()
@@ -3184,6 +3185,7 @@ var init_ZudokuConfig = __esm({
3184
3185
  disablePager: z7.boolean(),
3185
3186
  showLastModified: z7.boolean(),
3186
3187
  fullWidth: z7.boolean().optional(),
3188
+ centered: z7.boolean().optional(),
3187
3189
  suggestEdit: z7.object({
3188
3190
  url: z7.string(),
3189
3191
  text: z7.string().optional()
@@ -3204,7 +3206,10 @@ var init_ZudokuConfig = __esm({
3204
3206
  integrationId: z7.string(),
3205
3207
  organizationId: z7.string(),
3206
3208
  primaryBrandColor: z7.string(),
3207
- organizationDisplayName: z7.string()
3209
+ organizationDisplayName: z7.string(),
3210
+ searchSettings: z7.custom().optional(),
3211
+ aiChatSettings: z7.custom().optional(),
3212
+ modalSettings: z7.custom().optional()
3208
3213
  }),
3209
3214
  z7.object({
3210
3215
  type: z7.literal("pagefind"),
@@ -3389,6 +3394,11 @@ var init_ZudokuConfig = __esm({
3389
3394
  );
3390
3395
  ThemeConfigSchema = z7.object({
3391
3396
  registryUrl: z7.string().url().optional(),
3397
+ /**
3398
+ * @deprecated Import a `.css` file from your `zudoku.config.ts` instead.
3399
+ * Inline CSS via this option still works but requires a dev server restart
3400
+ * on every change and provides no editor tooling.
3401
+ */
3392
3402
  customCss: z7.union([z7.string(), CssObject]).optional(),
3393
3403
  light: ThemeSchema.optional(),
3394
3404
  dark: ThemeSchema.optional(),
@@ -3401,6 +3411,11 @@ var init_ZudokuConfig = __esm({
3401
3411
  dir: z7.enum(["ltr", "rtl"]).optional(),
3402
3412
  logo: LogoSchema,
3403
3413
  showPoweredBy: z7.boolean().optional(),
3414
+ sidebar: z7.object({
3415
+ collapsible: z7.boolean().optional(),
3416
+ toggleVisibility: z7.enum(["always", "hover"]).optional(),
3417
+ togglePosition: z7.enum(["top", "center", "bottom"]).optional()
3418
+ }).optional(),
3404
3419
  notFoundPage: z7.custom(),
3405
3420
  banner: z7.object({
3406
3421
  message: z7.custom(),
@@ -6670,6 +6685,7 @@ var plugin_docs_default = viteDocsPlugin;
6670
6685
  // src/vite/plugin-markdown-export.ts
6671
6686
  init_loader();
6672
6687
  init_ProtectedRoutesSchema();
6688
+ init_ZudokuConfig();
6673
6689
  init_joinUrl();
6674
6690
  import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
6675
6691
  import path13 from "node:path";
@@ -6711,8 +6727,9 @@ var viteMarkdownExportPlugin = () => {
6711
6727
  },
6712
6728
  async buildStart() {
6713
6729
  const config = getCurrentConfig();
6714
- const llmsConfig = config.docs?.llms;
6715
- const needsMdFiles = config.docs?.publishMarkdown || llmsConfig?.llmsTxt || llmsConfig?.llmsTxtFull;
6730
+ const docsConfig = DocsConfigSchema.parse(config.docs ?? {});
6731
+ const llmsConfig = docsConfig.llms ?? {};
6732
+ const needsMdFiles = docsConfig.publishMarkdown || llmsConfig.llmsTxt || llmsConfig.llmsTxtFull;
6716
6733
  if (config.__meta.mode === "standalone" || !needsMdFiles) {
6717
6734
  return;
6718
6735
  }
@@ -6736,8 +6753,9 @@ var viteMarkdownExportPlugin = () => {
6736
6753
  },
6737
6754
  async configureServer(server) {
6738
6755
  const config = getCurrentConfig();
6739
- const llmsConfig = config.docs?.llms;
6740
- const needsMdFiles = config.docs?.publishMarkdown || llmsConfig?.llmsTxt || llmsConfig?.llmsTxtFull;
6756
+ const docsConfig = DocsConfigSchema.parse(config.docs ?? {});
6757
+ const llmsConfig = docsConfig.llms ?? {};
6758
+ const needsMdFiles = docsConfig.publishMarkdown || llmsConfig.llmsTxt || llmsConfig.llmsTxtFull;
6741
6759
  if (!needsMdFiles) return;
6742
6760
  markdownFiles = await resolveCustomNavigationPaths(
6743
6761
  config,
@@ -6763,8 +6781,9 @@ var viteMarkdownExportPlugin = () => {
6763
6781
  },
6764
6782
  async closeBundle() {
6765
6783
  const config = getCurrentConfig();
6766
- const llmsConfig = config.docs?.llms;
6767
- const needsMdFiles = config.docs?.publishMarkdown || llmsConfig?.llmsTxt || llmsConfig?.llmsTxtFull;
6784
+ const docsConfig = DocsConfigSchema.parse(config.docs ?? {});
6785
+ const llmsConfig = docsConfig.llms ?? {};
6786
+ const needsMdFiles = docsConfig.publishMarkdown || llmsConfig.llmsTxt || llmsConfig.llmsTxtFull;
6768
6787
  if (process.env.NODE_ENV !== "production" || Object.keys(markdownFiles).length === 0 || !needsMdFiles) {
6769
6788
  return;
6770
6789
  }
@@ -6795,7 +6814,7 @@ var viteMarkdownExportPlugin = () => {
6795
6814
  console.warn(`Failed to export markdown for ${routePath}:`, error);
6796
6815
  }
6797
6816
  }
6798
- if (config.docs?.llms?.llmsTxt || config.docs?.llms?.llmsTxtFull) {
6817
+ if (llmsConfig.llmsTxt || llmsConfig.llmsTxtFull) {
6799
6818
  const markdownInfoPath = path13.join(
6800
6819
  config.__meta.rootDir,
6801
6820
  "node_modules/.zudoku/markdown-info.json"
@@ -7039,11 +7058,27 @@ var remarkLastModified = () => {
7039
7058
  // src/vite/mdx/remark-link-rewrite.ts
7040
7059
  import path15 from "node:path";
7041
7060
  import { visit as visit5 } from "unist-util-visit";
7042
- var remarkLinkRewrite = (basePath = "") => (tree) => {
7061
+ var markdownExtension = /\.mdx?$/;
7062
+ var resolveToRoute = (url, filePath, routesByFile) => {
7063
+ const [, pathname = "", suffix = ""] = url.match(/^([^#?]*)(.*)$/) ?? [];
7064
+ if (!markdownExtension.test(pathname) || pathname.startsWith("/")) {
7065
+ return void 0;
7066
+ }
7067
+ const targetFile = path15.resolve(path15.dirname(filePath), pathname).replace(markdownExtension, "");
7068
+ const route = routesByFile.get(targetFile);
7069
+ if (route === void 0) return void 0;
7070
+ return `${route}${suffix}`;
7071
+ };
7072
+ var remarkLinkRewrite = (basePath = "", routesByFile = /* @__PURE__ */ new Map()) => (tree, vfile) => {
7043
7073
  visit5(tree, "link", (node) => {
7044
7074
  if (!node.url) return;
7045
7075
  if (node.url.startsWith("http") || node.url.startsWith("mailto:")) return;
7046
7076
  node.url = node.url.replace(/\\/g, "/");
7077
+ const resolved = resolveToRoute(node.url, vfile.path, routesByFile);
7078
+ if (resolved !== void 0) {
7079
+ node.url = resolved;
7080
+ return;
7081
+ }
7047
7082
  const base = path15.posix.join(basePath);
7048
7083
  if (basePath && node.url.startsWith(base)) {
7049
7084
  node.url = node.url.slice(base.length);
@@ -7218,6 +7253,14 @@ var viteMdxPlugin = async () => {
7218
7253
  const config = getCurrentConfig();
7219
7254
  const buildConfig = await getBuildConfig();
7220
7255
  const highlighter = await highlighterPromise;
7256
+ const routesByFile = /* @__PURE__ */ new Map();
7257
+ const fileMapping = await resolveCustomNavigationPaths(
7258
+ config,
7259
+ await globMarkdownFiles(config, { absolute: true })
7260
+ );
7261
+ for (const [route, filePath] of Object.entries(fileMapping)) {
7262
+ routesByFile.set(filePath.replace(/\.mdx?$/, ""), route);
7263
+ }
7221
7264
  const defaultRemarkPlugins = [
7222
7265
  remarkStaticGeneration,
7223
7266
  [remarkInjectFilepath, config.__meta.rootDir],
@@ -7232,7 +7275,7 @@ var viteMdxPlugin = async () => {
7232
7275
  remarkDirective,
7233
7276
  remarkDirectiveRehype,
7234
7277
  remarkCodeTabs,
7235
- [remarkLinkRewrite, config.basePath],
7278
+ [remarkLinkRewrite, config.basePath, routesByFile],
7236
7279
  [remarkNormalizeImageUrl, config.basePath],
7237
7280
  ...config.build?.remarkPlugins ?? []
7238
7281
  ];
@@ -57,6 +57,7 @@ declare const ApiOptionsSchema: z.ZodObject<{
57
57
  }>>;
58
58
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
59
59
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
60
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
60
61
  schemaDownload: z.ZodOptional<z.ZodObject<{
61
62
  enabled: z.ZodOptional<z.ZodBoolean>;
62
63
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -92,6 +93,7 @@ declare const ApiSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
92
93
  }>>;
93
94
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
94
95
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
96
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
95
97
  schemaDownload: z.ZodOptional<z.ZodObject<{
96
98
  enabled: z.ZodOptional<z.ZodBoolean>;
97
99
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -127,6 +129,7 @@ declare const ApiSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
127
129
  }>>;
128
130
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
129
131
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
132
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
130
133
  schemaDownload: z.ZodOptional<z.ZodObject<{
131
134
  enabled: z.ZodOptional<z.ZodBoolean>;
132
135
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -162,6 +165,7 @@ declare const ApiSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
162
165
  }>>;
163
166
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
164
167
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
168
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
165
169
  schemaDownload: z.ZodOptional<z.ZodObject<{
166
170
  enabled: z.ZodOptional<z.ZodBoolean>;
167
171
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -302,6 +306,7 @@ export declare const DocsConfigSchema: z.ZodObject<{
302
306
  disablePager: z.ZodOptional<z.ZodBoolean>;
303
307
  showLastModified: z.ZodOptional<z.ZodBoolean>;
304
308
  fullWidth: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
309
+ centered: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
305
310
  suggestEdit: z.ZodOptional<z.ZodOptional<z.ZodObject<{
306
311
  url: z.ZodString;
307
312
  text: z.ZodOptional<z.ZodString>;
@@ -524,6 +529,18 @@ export declare const ZudokuConfig: z.ZodObject<{
524
529
  reloadDocument: z.ZodOptional<z.ZodBoolean>;
525
530
  }, z.core.$strip>>;
526
531
  showPoweredBy: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
532
+ sidebar: z.ZodOptional<z.ZodOptional<z.ZodObject<{
533
+ collapsible: z.ZodOptional<z.ZodBoolean>;
534
+ toggleVisibility: z.ZodOptional<z.ZodEnum<{
535
+ always: "always";
536
+ hover: "hover";
537
+ }>>;
538
+ togglePosition: z.ZodOptional<z.ZodEnum<{
539
+ center: "center";
540
+ top: "top";
541
+ bottom: "bottom";
542
+ }>>;
543
+ }, z.core.$strip>>>;
527
544
  notFoundPage: z.ZodOptional<z.ZodCustom<NonNullable<ReactNode>, NonNullable<ReactNode>>>;
528
545
  banner: z.ZodOptional<z.ZodObject<{
529
546
  message: z.ZodCustom<NonNullable<ReactNode>, NonNullable<ReactNode>>;
@@ -6861,6 +6878,9 @@ export declare const ZudokuConfig: z.ZodObject<{
6861
6878
  organizationId: z.ZodString;
6862
6879
  primaryBrandColor: z.ZodString;
6863
6880
  organizationDisplayName: z.ZodString;
6881
+ searchSettings: z.ZodOptional<z.ZodCustom<import("@inkeep/cxkit-types").InkeepSearchSettings & Record<string, unknown>, import("@inkeep/cxkit-types").InkeepSearchSettings & Record<string, unknown>>>;
6882
+ aiChatSettings: z.ZodOptional<z.ZodCustom<import("@inkeep/cxkit-types").InkeepAIChatSettings & Record<string, unknown>, import("@inkeep/cxkit-types").InkeepAIChatSettings & Record<string, unknown>>>;
6883
+ modalSettings: z.ZodOptional<z.ZodCustom<import("@inkeep/cxkit-types").InkeepModalSettings & Record<string, unknown>, import("@inkeep/cxkit-types").InkeepModalSettings & Record<string, unknown>>>;
6864
6884
  }, z.core.$loose>, z.ZodObject<{
6865
6885
  type: z.ZodLiteral<"pagefind">;
6866
6886
  ranking: z.ZodOptional<z.ZodObject<{
@@ -6890,6 +6910,7 @@ export declare const ZudokuConfig: z.ZodObject<{
6890
6910
  disablePager: z.ZodOptional<z.ZodBoolean>;
6891
6911
  showLastModified: z.ZodOptional<z.ZodBoolean>;
6892
6912
  fullWidth: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
6913
+ centered: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
6893
6914
  suggestEdit: z.ZodOptional<z.ZodOptional<z.ZodObject<{
6894
6915
  url: z.ZodString;
6895
6916
  text: z.ZodOptional<z.ZodString>;
@@ -6923,6 +6944,7 @@ export declare const ZudokuConfig: z.ZodObject<{
6923
6944
  }>>;
6924
6945
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
6925
6946
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
6947
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
6926
6948
  schemaDownload: z.ZodOptional<z.ZodObject<{
6927
6949
  enabled: z.ZodOptional<z.ZodBoolean>;
6928
6950
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -6958,6 +6980,7 @@ export declare const ZudokuConfig: z.ZodObject<{
6958
6980
  }>>;
6959
6981
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
6960
6982
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
6983
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
6961
6984
  schemaDownload: z.ZodOptional<z.ZodObject<{
6962
6985
  enabled: z.ZodOptional<z.ZodBoolean>;
6963
6986
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -6993,6 +7016,7 @@ export declare const ZudokuConfig: z.ZodObject<{
6993
7016
  }>>;
6994
7017
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
6995
7018
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
7019
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
6996
7020
  schemaDownload: z.ZodOptional<z.ZodObject<{
6997
7021
  enabled: z.ZodOptional<z.ZodBoolean>;
6998
7022
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -7024,6 +7048,7 @@ export declare const ZudokuConfig: z.ZodObject<{
7024
7048
  }>>;
7025
7049
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
7026
7050
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
7051
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
7027
7052
  schemaDownload: z.ZodOptional<z.ZodObject<{
7028
7053
  enabled: z.ZodOptional<z.ZodBoolean>;
7029
7054
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -7059,6 +7084,7 @@ export declare const ZudokuConfig: z.ZodObject<{
7059
7084
  }>>;
7060
7085
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
7061
7086
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
7087
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
7062
7088
  schemaDownload: z.ZodOptional<z.ZodObject<{
7063
7089
  enabled: z.ZodOptional<z.ZodBoolean>;
7064
7090
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -7094,6 +7120,7 @@ export declare const ZudokuConfig: z.ZodObject<{
7094
7120
  }>>;
7095
7121
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
7096
7122
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
7123
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
7097
7124
  schemaDownload: z.ZodOptional<z.ZodObject<{
7098
7125
  enabled: z.ZodOptional<z.ZodBoolean>;
7099
7126
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -7194,6 +7221,7 @@ export declare const ZudokuConfig: z.ZodObject<{
7194
7221
  }>>;
7195
7222
  expandAllTags: z.ZodOptional<z.ZodBoolean>;
7196
7223
  showInfoPage: z.ZodOptional<z.ZodBoolean>;
7224
+ disableSecurity: z.ZodOptional<z.ZodBoolean>;
7197
7225
  schemaDownload: z.ZodOptional<z.ZodObject<{
7198
7226
  enabled: z.ZodOptional<z.ZodBoolean>;
7199
7227
  fileName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -1,6 +1,6 @@
1
1
  import type { NavigationItem } from "../../../config/validators/NavigationSchema.js";
2
2
  export declare const useZudoku: () => import("../../core/ZudokuContext.js").ZudokuContext;
3
- export declare const useApiIdentities: () => import("@tanstack/react-query").UseQueryResult<import("../../core/ZudokuContext.js").ApiIdentity[], Error>;
3
+ export declare const useApiIdentities: () => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../../core/ZudokuContext.js").ApiIdentity[]>, Error>;
4
4
  export declare const useCurrentNavigation: () => {
5
5
  navigation: NavigationItem[];
6
6
  topNavItem: import("../../../config/validators/NavigationSchema.js").NavigationDoc | import("../../../config/validators/NavigationSchema.js").NavigationLink | import("../../../config/validators/NavigationSchema.js").NavigationCategory | import("../../../config/validators/NavigationSchema.js").NavigationCustomPage | import("../../../config/validators/NavigationSchema.js").NavigationSeparator | {
@@ -0,0 +1 @@
1
+ export declare const SidebarToggle: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ type SidebarState = {
2
+ isCollapsed: boolean;
3
+ toggle: () => void;
4
+ };
5
+ export declare const useSidebar: import("zustand").UseBoundStore<import("zustand").StoreApi<SidebarState>>;
6
+ export {};
@@ -51,6 +51,11 @@ type Metadata = Partial<{
51
51
  type Site = Partial<{
52
52
  dir?: "ltr" | "rtl";
53
53
  showPoweredBy?: boolean;
54
+ sidebar?: {
55
+ collapsible?: boolean;
56
+ toggleVisibility?: "always" | "hover";
57
+ togglePosition?: "top" | "center" | "bottom";
58
+ };
54
59
  title?: string;
55
60
  logo?: {
56
61
  src: {
@@ -5,4 +5,5 @@ export { CACHE_KEYS, useCache } from "../components/cache.js";
5
5
  export { useZudoku } from "../components/context/ZudokuContext.js";
6
6
  export { useExposedProps } from "../util/useExposedProps.js";
7
7
  export { useEvent } from "./useEvent.js";
8
+ export { useSidebar } from "../components/navigation/sidebarStore.js";
8
9
  export { useCopyToClipboard } from "../util/useCopyToClipboard.js";
@@ -7,7 +7,7 @@ export interface MarkdownPluginOptions extends ZudokuDocsConfig {
7
7
  basePath: string;
8
8
  fileImports: Record<string, () => Promise<MDXImport>>;
9
9
  }
10
- export type MarkdownPluginDefaultOptions = Pick<Frontmatter, "toc" | "disablePager" | "showLastModified" | "suggestEdit" | "copyPage" | "fullWidth">;
10
+ export type MarkdownPluginDefaultOptions = Pick<Frontmatter, "toc" | "disablePager" | "showLastModified" | "suggestEdit" | "copyPage" | "fullWidth" | "centered">;
11
11
  export type Frontmatter = {
12
12
  title?: string;
13
13
  description?: string;
@@ -24,6 +24,7 @@ export type Frontmatter = {
24
24
  } | false;
25
25
  copyPage?: boolean;
26
26
  fullWidth?: boolean;
27
+ centered?: boolean;
27
28
  };
28
29
  export type MDXImport = {
29
30
  tableOfContents: Toc;
@@ -1,8 +1,14 @@
1
- import type { InkeepBaseSettings, InkeepJS } from "@inkeep/cxkit-types";
1
+ import type { InkeepAIChatSettings, InkeepBaseSettings, InkeepJS, InkeepModalSettings, InkeepSearchSettings } from "@inkeep/cxkit-types";
2
2
  import type { ZudokuPlugin } from "../../core/plugins.js";
3
3
  declare global {
4
4
  interface Window {
5
5
  Inkeep: InkeepJS | undefined;
6
6
  }
7
7
  }
8
- export declare const inkeepSearchPlugin: (settings: InkeepBaseSettings) => ZudokuPlugin;
8
+ export type InkeepSearchPluginOptions = InkeepBaseSettings & Record<string, unknown> & {
9
+ type?: "inkeep";
10
+ searchSettings?: InkeepSearchSettings & Record<string, unknown>;
11
+ aiChatSettings?: InkeepAIChatSettings & Record<string, unknown>;
12
+ modalSettings?: InkeepModalSettings & Record<string, unknown>;
13
+ };
14
+ export declare const inkeepSearchPlugin: (settings: InkeepSearchPluginOptions) => ZudokuPlugin;
@@ -136,6 +136,11 @@ export interface FlatZudokuConfig {
136
136
  dir?: ("ltr" | "rtl")
137
137
  logo?: _Schema0
138
138
  showPoweredBy?: boolean
139
+ sidebar?: {
140
+ collapsible?: boolean
141
+ toggleVisibility?: ("always" | "hover")
142
+ togglePosition?: ("top" | "center" | "bottom")
143
+ }
139
144
  notFoundPage?: unknown
140
145
  banner?: {
141
146
  message: unknown
@@ -351,6 +356,9 @@ export interface FlatZudokuConfig {
351
356
  organizationId: string
352
357
  primaryBrandColor: string
353
358
  organizationDisplayName: string
359
+ searchSettings?: unknown
360
+ aiChatSettings?: unknown
361
+ modalSettings?: unknown
354
362
  [k: string]: unknown
355
363
  } | {
356
364
  type: "pagefind"
@@ -376,6 +384,7 @@ export interface FlatZudokuConfig {
376
384
  disablePager?: boolean
377
385
  showLastModified?: boolean
378
386
  fullWidth?: boolean
387
+ centered?: boolean
379
388
  suggestEdit?: {
380
389
  url: string
381
390
  text?: string
@@ -494,6 +503,7 @@ export interface _Schema22 {
494
503
  showVersionSelect?: ("always" | "if-available" | "hide")
495
504
  expandAllTags?: boolean
496
505
  showInfoPage?: boolean
506
+ disableSecurity?: boolean
497
507
  schemaDownload?: {
498
508
  enabled?: boolean
499
509
  fileName?: string
@@ -193,24 +193,17 @@ Or pass any React node as the `icon` to override the default for the chosen `typ
193
193
  ## Customize colors
194
194
 
195
195
  Each callout type is driven by a single CSS variable that determines the icon, border tint, and
196
- background tint via `color-mix`. Override any of them in your theme's `customCss` to recolor a type
197
- globally:
198
-
199
- ```ts title="zudoku.config.ts"
200
- export default {
201
- theme: {
202
- customCss: `
203
- :root {
204
- --callout-tip: oklch(0.65 0.18 160);
205
- --callout-sparkles: oklch(0.6 0.22 320);
206
- }
207
- .dark {
208
- --callout-tip: oklch(0.78 0.18 160);
209
- --callout-sparkles: oklch(0.75 0.2 320);
210
- }
211
- `,
212
- },
213
- };
196
+ background tint via `color-mix`. Override any of them in your stylesheet to recolor a type globally:
197
+
198
+ ```css title=styles.css
199
+ :root {
200
+ --callout-tip: oklch(0.65 0.18 160);
201
+ --callout-sparkles: oklch(0.6 0.22 320);
202
+ }
203
+ .dark {
204
+ --callout-tip: oklch(0.78 0.18 160);
205
+ --callout-sparkles: oklch(0.75 0.2 320);
206
+ }
214
207
  ```
215
208
 
216
209
  All available variables:
@@ -168,3 +168,39 @@ your [Zudoku Configuration](./overview.md):
168
168
  // ...
169
169
  }
170
170
  ```
171
+
172
+ ### Customizing Inkeep
173
+
174
+ Any other [base settings](https://docs.inkeep.com/cloud/ui-components/common-settings/base) can be
175
+ set alongside the required fields, for example `filters` to scope results or `transformSource` to
176
+ customize how sources are displayed.
177
+
178
+ You can also pass
179
+ [`searchSettings`](https://docs.inkeep.com/cloud/ui-components/common-settings/search),
180
+ [`aiChatSettings`](https://docs.inkeep.com/cloud/ui-components/common-settings/ai-chat), and
181
+ `modalSettings` to customize the respective parts of the search experience. They are merged with
182
+ Zudoku's defaults and passed through to Inkeep as-is, so any option Inkeep supports can be used —
183
+ including ones added after this Zudoku version was released.
184
+
185
+ For example, to categorize results into tabs based on their URL:
186
+
187
+ ```typescript
188
+ {
189
+ // ...
190
+ search: {
191
+ type: "inkeep",
192
+ // ...required fields from above
193
+ transformSource: (source) => {
194
+ if (!source.url.includes("/blog/")) return source;
195
+ return { ...source, tabs: [...(source.tabs ?? []), "Blog"] };
196
+ },
197
+ searchSettings: {
198
+ tabs: [["All", { isAlwaysVisible: true }], "Blog"],
199
+ },
200
+ aiChatSettings: {
201
+ aiAssistantName: "My Assistant",
202
+ },
203
+ },
204
+ // ...
205
+ }
206
+ ```
@@ -127,6 +127,44 @@ export const NotFound = () => (
127
127
 
128
128
  ## Layout
129
129
 
130
+ ### Collapsible Sidebar
131
+
132
+ The navigation sidebar is collapsible by default. A small toggle button on the sidebar's right
133
+ border lets users hide and reveal it. Configure the behavior under `site.sidebar`:
134
+
135
+ ```tsx title=zudoku.config.tsx
136
+ {
137
+ site: {
138
+ sidebar: {
139
+ collapsible: true, // default: true. Set to false to disable the toggle entirely.
140
+ toggleVisibility: "always", // "always" (default) or "hover" — show button only when hovering the sidebar's right edge
141
+ togglePosition: "bottom", // "top", "center", or "bottom" (default)
142
+ },
143
+ }
144
+ }
145
+ ```
146
+
147
+ For finer vertical placement, override the `--sidebar-toggle-y` CSS variable in your stylesheet:
148
+
149
+ ```css
150
+ :root {
151
+ --sidebar-toggle-y: 30%;
152
+ }
153
+ ```
154
+
155
+ The toggle button carries `aria-expanded="true"` when the sidebar is open and `"false"` when
156
+ collapsed. Combine it with the `[data-sidebar-toggle]` selector to position the button differently
157
+ per state:
158
+
159
+ ```css
160
+ [data-sidebar-toggle][aria-expanded="true"] {
161
+ --sidebar-toggle-y: 20%;
162
+ }
163
+ [data-sidebar-toggle][aria-expanded="false"] {
164
+ --sidebar-toggle-y: 80%;
165
+ }
166
+ ```
167
+
130
168
  ### Banner
131
169
 
132
170
  Add a banner message to the top of the page:
@@ -164,29 +164,55 @@ const config = {
164
164
  };
165
165
  ```
166
166
 
167
- Alternatively, you can copy the CSS code and paste it into your `customCss` configuration:
167
+ Alternatively, paste the copied CSS into a stylesheet and import it from your config:
168
+
169
+ ```css title=styles.css
170
+ /* Copied CSS code */
171
+ ```
168
172
 
169
173
  ```ts title=zudoku.config.ts
170
- const config = {
171
- theme: {
172
- customCss: `
173
- /* Copied CSS code */
174
- `,
175
- },
176
- };
174
+ import "./styles.css";
177
175
  ```
178
176
 
179
177
  ## Custom CSS
180
178
 
181
- For advanced styling, you can add custom CSS either as a string or structured object:
179
+ The recommended way to add custom styles is to write a `.css` file alongside your config and import
180
+ it. This gives you HMR during development, syntax highlighting, autocompletion, and lets you split
181
+ styles across files as your site grows.
182
182
 
183
- :::note
183
+ ```css title=styles.css
184
+ .custom {
185
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
186
+ }
187
+ ```
184
188
 
185
- Changes to `customCss` require restarting the development server to take effect.
189
+ ```ts title=zudoku.config.ts
190
+ import "./styles.css";
186
191
 
187
- :::
192
+ const config = {
193
+ // ...
194
+ };
195
+ ```
196
+
197
+ If TypeScript reports `Cannot find module './styles.css'`, add `zudoku/client` to your tsconfig
198
+ types so CSS side-effect imports are recognized:
199
+
200
+ ```json title=tsconfig.json
201
+ {
202
+ "compilerOptions": {
203
+ "types": ["zudoku/client"]
204
+ }
205
+ }
206
+ ```
207
+
208
+ Projects created with `create-zudoku` include this by default.
209
+
210
+ ### Inline alternatives (deprecated)
188
211
 
189
- ### CSS String
212
+ The `theme.customCss` option is deprecated and will be removed in a future release. It still accepts
213
+ a CSS string or object for backwards compatibility, but every change requires restarting the dev
214
+ server and you lose syntax highlighting, autocompletion, and HMR. Migrate to an imported `.css`
215
+ file.
190
216
 
191
217
  ```ts title=zudoku.config.ts
192
218
  const config = {
@@ -200,37 +226,22 @@ const config = {
200
226
  };
201
227
  ```
202
228
 
203
- ### CSS Object
204
-
205
- ```ts title=zudoku.config.ts
206
- const config = {
207
- theme: {
208
- customCss: {
209
- ".custom": {
210
- background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
211
- },
212
- },
213
- },
214
- };
215
- ```
216
-
217
229
  ### Enabling Code Ligatures
218
230
 
219
231
  Zudoku disables ligatures in code blocks by default to avoid unwanted glyph joining in fonts like
220
232
  Geist Mono (e.g. `---`, `###`, `|--|`). If you're using a coding font designed around ligatures
221
- (Fira Code, JetBrains Mono, etc.), re-enable them via `customCss`:
222
-
223
- ```ts title=zudoku.config.ts
224
- const config = {
225
- theme: {
226
- customCss: `
227
- code, pre, kbd, samp, .shiki, .shiki span {
228
- font-variant-ligatures: normal;
229
- font-feature-settings: normal;
230
- }
231
- `,
232
- },
233
- };
233
+ (Fira Code, JetBrains Mono, etc.), re-enable them in your CSS file:
234
+
235
+ ```css title=styles.css
236
+ code,
237
+ pre,
238
+ kbd,
239
+ samp,
240
+ .shiki,
241
+ .shiki span {
242
+ font-variant-ligatures: normal;
243
+ font-feature-settings: normal;
244
+ }
234
245
  ```
235
246
 
236
247
  ## Default Theme
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.80.1",
3
+ "version": "0.81.0",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -213,12 +213,12 @@
213
213
  "@tailwindcss/typography": "0.5.19",
214
214
  "@tailwindcss/vite": "4.3.0",
215
215
  "@tanem/react-nprogress": "6.0.3",
216
- "@tanstack/react-query": "5.97.0",
216
+ "@tanstack/react-query": "5.101.0",
217
217
  "@types/react": "19.2.14",
218
218
  "@types/react-dom": "19.2.3",
219
- "@vitejs/plugin-react": "6.0.1",
219
+ "@vitejs/plugin-react": "6.0.2",
220
220
  "@x0k/json-schema-merge": "1.0.3",
221
- "@unhead/react": "3.1.0",
221
+ "@unhead/react": "3.1.3",
222
222
  "@zuplo/mcp": "0.0.32",
223
223
  "bs58": "6.0.0",
224
224
  "class-variance-authority": "0.7.1",