@heyo-sh/create-heyo-docs 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deployments/cloudflare/astro/package.json +2 -1
- package/deployments/cloudflare/astro/src/env.d.ts +4 -1
- package/deployments/cloudflare/astro/src/pages/heyo-docs-internal/ai-chat.ts +9 -7
- package/deployments/cloudflare/next/app/heyo-docs-internal/ai-chat/route.ts +9 -7
- package/deployments/cloudflare/next/cloudflare-env.d.ts +4 -1
- package/deployments/cloudflare/react-router/app/cloudflare-env.d.ts +4 -1
- package/deployments/cloudflare/react-router/app/routes/ai-chat.ts +9 -8
- package/deployments/cloudflare/react-router/package.json +2 -1
- package/deployments/vercel/astro/package.json +1 -0
- package/dist/index.js +24 -4
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/astro/content/index.mdx +8 -0
- package/templates/astro/heyo-docs.config.ts +4 -48
- package/templates/astro/src/components/docs-app.tsx +30 -130
- package/templates/astro/src/heyo-docs-icons.tsx +3 -1
- package/templates/astro/src/layouts/docs-layout.astro +10 -436
- package/templates/astro/src/lib/docs.ts +3 -2
- package/templates/astro/src/lib/openapi-request.ts +6 -146
- package/templates/astro/src/pages/[...slug].astro +7 -15
- package/templates/astro/src/pages/[...slug].md.ts +4 -1
- package/templates/astro/src/pages/heyo-docs-internal/ai-chat.ts +5 -7
- package/templates/astro/src/pages/heyo-docs-internal/openapi-request.ts +6 -0
- package/templates/astro/src/pages/index.astro +18 -9
- package/templates/astro/src/pages/llms-full.txt.ts +1 -1
- package/templates/astro/src/pages/llms.txt.ts +1 -1
- package/templates/astro/src/pages/rss.xml.ts +1 -1
- package/templates/astro/src/pages/sitemap.xml.ts +2 -2
- package/templates/astro/src/virtual.d.ts +5 -5
- package/templates/next/app/[[...slug]]/page.tsx +10 -9
- package/templates/next/app/components/docs-app.tsx +28 -120
- package/templates/next/app/heyo-docs-icons.tsx +1 -1
- package/templates/next/app/heyo-docs-internal/ai-chat/route.ts +6 -7
- package/templates/next/app/heyo-docs-internal/markdown/[[...slug]]/route.ts +1 -1
- package/templates/next/app/layout.tsx +25 -444
- package/templates/next/app/lib/docs.ts +3 -3
- package/templates/next/app/lib/openapi-request.ts +4 -136
- package/templates/next/app/llms-full.txt/route.ts +1 -1
- package/templates/next/app/llms.txt/route.ts +1 -1
- package/templates/next/app/rss.xml/route.ts +1 -1
- package/templates/next/app/sitemap.xml/route.ts +1 -1
- package/templates/next/content/index.mdx +8 -0
- package/templates/next/heyo-docs.config.ts +3 -47
- package/templates/next/next.config.ts +7 -1
- package/templates/react-router/app/heyo-docs-icons.tsx +3 -1
- package/templates/react-router/app/root.tsx +28 -465
- package/templates/react-router/app/routes/ai-chat.ts +4 -8
- package/templates/react-router/app/routes/docs.tsx +22 -63
- package/templates/react-router/app/routes/home.tsx +6 -1
- package/templates/react-router/app/routes/llms-full.ts +1 -1
- package/templates/react-router/app/routes/llms.ts +1 -1
- package/templates/react-router/app/routes/markdown.ts +4 -1
- package/templates/react-router/app/routes/openapi-request.ts +11 -144
- package/templates/react-router/app/routes/rss.ts +1 -1
- package/templates/react-router/app/routes/sitemap.ts +5 -8
- package/templates/react-router/app/virtual.d.ts +5 -5
- package/templates/react-router/content/index.mdx +8 -0
- package/templates/react-router/heyo-docs.config.ts +4 -48
- package/templates/astro/content/api-overview.mdx +0 -20
- package/templates/astro/content/assets/local-architecture.svg +0 -22
- package/templates/astro/content/assets/quick-reference.txt +0 -5
- package/templates/astro/content/changelog.mdx +0 -38
- package/templates/astro/content/configuration.mdx +0 -117
- package/templates/astro/content/deploying.mdx +0 -70
- package/templates/astro/content/essentials/callouts.mdx +0 -34
- package/templates/astro/content/essentials/code-blocks.mdx +0 -43
- package/templates/astro/content/essentials/lists.mdx +0 -36
- package/templates/astro/content/essentials/tables.mdx +0 -30
- package/templates/astro/content/essentials/tabs.mdx +0 -40
- package/templates/astro/content/essentials/text-styling.mdx +0 -43
- package/templates/astro/content/getting-started.mdx +0 -16
- package/templates/astro/content/mdx-components.mdx +0 -44
- package/templates/astro/content/openapi-petstore.json +0 -160
- package/templates/astro/content/openapi.json +0 -278
- package/templates/astro/content/quickstart.mdx +0 -60
- package/templates/astro/src/components/theme-provider.tsx +0 -106
- package/templates/astro/src/lib/seo.ts +0 -221
- package/templates/astro/src/lib/theme.ts +0 -74
- package/templates/next/app/components/theme-provider.tsx +0 -93
- package/templates/next/app/lib/seo.ts +0 -189
- package/templates/next/app/lib/theme.ts +0 -61
- package/templates/next/content/api-overview.mdx +0 -20
- package/templates/next/content/assets/local-architecture.svg +0 -22
- package/templates/next/content/assets/quick-reference.txt +0 -5
- package/templates/next/content/changelog.mdx +0 -38
- package/templates/next/content/configuration.mdx +0 -117
- package/templates/next/content/deploying.mdx +0 -70
- package/templates/next/content/essentials/callouts.mdx +0 -34
- package/templates/next/content/essentials/code-blocks.mdx +0 -43
- package/templates/next/content/essentials/lists.mdx +0 -36
- package/templates/next/content/essentials/tables.mdx +0 -30
- package/templates/next/content/essentials/tabs.mdx +0 -40
- package/templates/next/content/essentials/text-styling.mdx +0 -43
- package/templates/next/content/getting-started.mdx +0 -16
- package/templates/next/content/mdx-components.mdx +0 -41
- package/templates/next/content/openapi-petstore.json +0 -160
- package/templates/next/content/openapi.json +0 -278
- package/templates/next/content/quickstart.mdx +0 -60
- package/templates/react-router/app/components/theme-provider.tsx +0 -106
- package/templates/react-router/app/lib/theme.ts +0 -74
- package/templates/react-router/content/api-overview.mdx +0 -20
- package/templates/react-router/content/assets/local-architecture.svg +0 -22
- package/templates/react-router/content/assets/quick-reference.txt +0 -5
- package/templates/react-router/content/changelog.mdx +0 -38
- package/templates/react-router/content/configuration.mdx +0 -117
- package/templates/react-router/content/deploying.mdx +0 -70
- package/templates/react-router/content/essentials/callouts.mdx +0 -34
- package/templates/react-router/content/essentials/code-blocks.mdx +0 -43
- package/templates/react-router/content/essentials/lists.mdx +0 -36
- package/templates/react-router/content/essentials/tables.mdx +0 -30
- package/templates/react-router/content/essentials/tabs.mdx +0 -40
- package/templates/react-router/content/essentials/text-styling.mdx +0 -43
- package/templates/react-router/content/getting-started.mdx +0 -16
- package/templates/react-router/content/mdx-components.mdx +0 -293
- package/templates/react-router/content/openapi-petstore.json +0 -160
- package/templates/react-router/content/openapi.json +0 -278
- package/templates/react-router/content/quickstart.mdx +0 -60
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
export type Theme = "dark" | "light" | "system";
|
|
2
|
-
export type ResolvedTheme = Exclude<Theme, "system">;
|
|
3
|
-
|
|
4
|
-
export const DEFAULT_THEME: Theme = "system";
|
|
5
|
-
export const THEME_STORAGE_KEY = "heyo-docs-theme";
|
|
6
|
-
|
|
7
|
-
const themeValues = new Set<Theme>(["light", "dark", "system"]);
|
|
8
|
-
|
|
9
|
-
export function isTheme(value: string | null): value is Theme {
|
|
10
|
-
return value !== null && themeValues.has(value as Theme);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function getStoredTheme(
|
|
14
|
-
storageKey: string,
|
|
15
|
-
fallbackTheme: Theme,
|
|
16
|
-
): Theme {
|
|
17
|
-
if (typeof window === "undefined") return fallbackTheme;
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const storedTheme = window.localStorage.getItem(storageKey);
|
|
21
|
-
return isTheme(storedTheme) ? storedTheme : fallbackTheme;
|
|
22
|
-
} catch {
|
|
23
|
-
return fallbackTheme;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function getSystemTheme(): ResolvedTheme {
|
|
28
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
29
|
-
? "dark"
|
|
30
|
-
: "light";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function resolveTheme(theme: Theme): ResolvedTheme {
|
|
34
|
-
return theme === "system" ? getSystemTheme() : theme;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function applyTheme(theme: Theme): ResolvedTheme {
|
|
38
|
-
const resolvedTheme = resolveTheme(theme);
|
|
39
|
-
const root = window.document.documentElement;
|
|
40
|
-
|
|
41
|
-
root.classList.remove("light", "dark");
|
|
42
|
-
root.classList.add(resolvedTheme);
|
|
43
|
-
root.style.colorScheme = resolvedTheme;
|
|
44
|
-
|
|
45
|
-
return resolvedTheme;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function getThemeScript(storageKey: string, defaultTheme: Theme) {
|
|
49
|
-
return `(() => {
|
|
50
|
-
const storageKey = ${JSON.stringify(storageKey)};
|
|
51
|
-
const fallbackTheme = ${JSON.stringify(defaultTheme)};
|
|
52
|
-
const isTheme = (value) => value === "light" || value === "dark" || value === "system";
|
|
53
|
-
|
|
54
|
-
const storedTheme = (() => {
|
|
55
|
-
try {
|
|
56
|
-
const value = window.localStorage.getItem(storageKey);
|
|
57
|
-
return isTheme(value) ? value : fallbackTheme;
|
|
58
|
-
} catch {
|
|
59
|
-
return fallbackTheme;
|
|
60
|
-
}
|
|
61
|
-
})();
|
|
62
|
-
|
|
63
|
-
const resolvedTheme = storedTheme === "system"
|
|
64
|
-
? window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
65
|
-
? "dark"
|
|
66
|
-
: "light"
|
|
67
|
-
: storedTheme;
|
|
68
|
-
|
|
69
|
-
const root = document.documentElement;
|
|
70
|
-
root.classList.remove("light", "dark");
|
|
71
|
-
root.classList.add(resolvedTheme);
|
|
72
|
-
root.style.colorScheme = resolvedTheme;
|
|
73
|
-
})();`;
|
|
74
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
createContext,
|
|
5
|
-
useContext,
|
|
6
|
-
useEffect,
|
|
7
|
-
useState,
|
|
8
|
-
type ReactNode,
|
|
9
|
-
} from "react";
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
applyTheme,
|
|
13
|
-
DEFAULT_THEME,
|
|
14
|
-
getStoredTheme,
|
|
15
|
-
type ResolvedTheme,
|
|
16
|
-
THEME_STORAGE_KEY,
|
|
17
|
-
type Theme,
|
|
18
|
-
} from "../lib/theme";
|
|
19
|
-
|
|
20
|
-
type ThemeProviderState = {
|
|
21
|
-
theme: Theme;
|
|
22
|
-
resolvedTheme: ResolvedTheme;
|
|
23
|
-
mounted: boolean;
|
|
24
|
-
setTheme: (theme: Theme) => void;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const ThemeProviderContext = createContext<ThemeProviderState | undefined>(
|
|
28
|
-
undefined,
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
export function ThemeProvider({
|
|
32
|
-
children,
|
|
33
|
-
defaultTheme = DEFAULT_THEME,
|
|
34
|
-
storageKey = THEME_STORAGE_KEY,
|
|
35
|
-
}: {
|
|
36
|
-
children: ReactNode;
|
|
37
|
-
defaultTheme?: Theme;
|
|
38
|
-
storageKey?: string;
|
|
39
|
-
}) {
|
|
40
|
-
const [theme, setThemeState] = useState<Theme>(defaultTheme);
|
|
41
|
-
const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(
|
|
42
|
-
defaultTheme === "system" ? "light" : defaultTheme,
|
|
43
|
-
);
|
|
44
|
-
const [mounted, setMounted] = useState(false);
|
|
45
|
-
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
const syncTheme = () => {
|
|
48
|
-
const nextTheme = getStoredTheme(storageKey, defaultTheme);
|
|
49
|
-
setThemeState(nextTheme);
|
|
50
|
-
setResolvedTheme(applyTheme(nextTheme));
|
|
51
|
-
};
|
|
52
|
-
syncTheme();
|
|
53
|
-
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
54
|
-
const onSystemThemeChange = () => syncTheme();
|
|
55
|
-
const onStorage = (event: StorageEvent) => {
|
|
56
|
-
if (event.key === null || event.key === storageKey) syncTheme();
|
|
57
|
-
};
|
|
58
|
-
mediaQuery.addEventListener("change", onSystemThemeChange);
|
|
59
|
-
window.addEventListener("storage", onStorage);
|
|
60
|
-
setMounted(true);
|
|
61
|
-
return () => {
|
|
62
|
-
mediaQuery.removeEventListener("change", onSystemThemeChange);
|
|
63
|
-
window.removeEventListener("storage", onStorage);
|
|
64
|
-
};
|
|
65
|
-
}, [defaultTheme, storageKey]);
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<ThemeProviderContext.Provider
|
|
69
|
-
value={{
|
|
70
|
-
theme,
|
|
71
|
-
resolvedTheme,
|
|
72
|
-
mounted,
|
|
73
|
-
setTheme: (nextTheme) => {
|
|
74
|
-
try {
|
|
75
|
-
window.localStorage.setItem(storageKey, nextTheme);
|
|
76
|
-
} catch {
|
|
77
|
-
// The requested preference still applies when storage is unavailable.
|
|
78
|
-
}
|
|
79
|
-
setThemeState(nextTheme);
|
|
80
|
-
setResolvedTheme(applyTheme(nextTheme));
|
|
81
|
-
},
|
|
82
|
-
}}
|
|
83
|
-
>
|
|
84
|
-
{children}
|
|
85
|
-
</ThemeProviderContext.Provider>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function useTheme() {
|
|
90
|
-
const context = useContext(ThemeProviderContext);
|
|
91
|
-
if (!context) throw new Error("useTheme must be used within a ThemeProvider");
|
|
92
|
-
return context;
|
|
93
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import type { Metadata } from "next";
|
|
2
|
-
import {
|
|
3
|
-
type ChangelogGroupConfig,
|
|
4
|
-
type DocsPage,
|
|
5
|
-
type HeyoDocsConfig,
|
|
6
|
-
type NavigationGroup,
|
|
7
|
-
type OpenApiEndpoint,
|
|
8
|
-
} from "@heyo-sh/heyo-docs";
|
|
9
|
-
import {
|
|
10
|
-
navigationGroupContainsPath,
|
|
11
|
-
navigationPages,
|
|
12
|
-
navigationSectionPathForPath,
|
|
13
|
-
} from "@heyo-sh/heyo-docs/node";
|
|
14
|
-
|
|
15
|
-
interface DocsSeoInput {
|
|
16
|
-
config: HeyoDocsConfig;
|
|
17
|
-
pathname: string;
|
|
18
|
-
page?: DocsPage;
|
|
19
|
-
endpoint?: OpenApiEndpoint;
|
|
20
|
-
changelogGroup?: ChangelogGroupConfig;
|
|
21
|
-
navigation: NavigationGroup[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function canonicalFor({ config, pathname, page }: DocsSeoInput) {
|
|
25
|
-
return (
|
|
26
|
-
page?.seo.canonical ??
|
|
27
|
-
(config.siteUrl ? `${config.siteUrl}${pathname}` : undefined)
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function website(config: HeyoDocsConfig) {
|
|
32
|
-
return {
|
|
33
|
-
"@type": "WebSite",
|
|
34
|
-
name: config.title,
|
|
35
|
-
...(config.siteUrl ? { url: config.siteUrl } : {}),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function breadcrumbItems({
|
|
40
|
-
changelogGroup,
|
|
41
|
-
navigation,
|
|
42
|
-
pathname,
|
|
43
|
-
title,
|
|
44
|
-
}: Pick<DocsSeoInput, "changelogGroup" | "navigation" | "pathname"> & {
|
|
45
|
-
title: string;
|
|
46
|
-
}) {
|
|
47
|
-
if (changelogGroup) return [{ label: changelogGroup.group, href: pathname }];
|
|
48
|
-
const group = navigation.find((candidate) =>
|
|
49
|
-
navigationGroupContainsPath(candidate, pathname),
|
|
50
|
-
);
|
|
51
|
-
const sections = group
|
|
52
|
-
? navigationSectionPathForPath(group.sections, pathname)
|
|
53
|
-
: undefined;
|
|
54
|
-
if (!group) return [{ label: title, href: pathname }];
|
|
55
|
-
return [
|
|
56
|
-
{ label: group.group, href: navigationPages(group.sections)[0]?.slug },
|
|
57
|
-
...(sections ?? []).map((section) => ({
|
|
58
|
-
label: section.section,
|
|
59
|
-
href: navigationPages([section])[0]?.slug,
|
|
60
|
-
})),
|
|
61
|
-
{ label: title, href: pathname },
|
|
62
|
-
];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function absoluteUrl(siteUrl: string | undefined, href: string | undefined) {
|
|
66
|
-
if (!siteUrl || !href) return undefined;
|
|
67
|
-
return /^https?:\/\//.test(href)
|
|
68
|
-
? href
|
|
69
|
-
: new URL(href, `${siteUrl}/`).toString();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Next Metadata plus JSON-LD for MDX, changelog and generated API pages. */
|
|
73
|
-
export function docsSeo(input: DocsSeoInput): {
|
|
74
|
-
metadata: Metadata;
|
|
75
|
-
structuredData: unknown[];
|
|
76
|
-
} {
|
|
77
|
-
const { changelogGroup, config, endpoint, navigation, page, pathname } =
|
|
78
|
-
input;
|
|
79
|
-
const title = page?.seo.title ?? `${endpoint!.title} | ${config.title}`;
|
|
80
|
-
const description =
|
|
81
|
-
page?.seo.description ??
|
|
82
|
-
endpoint?.description ??
|
|
83
|
-
`${endpoint!.method.toUpperCase()} ${endpoint!.path} API endpoint.`;
|
|
84
|
-
const canonical = canonicalFor(input);
|
|
85
|
-
const pageType = endpoint
|
|
86
|
-
? {
|
|
87
|
-
"@context": "https://schema.org",
|
|
88
|
-
"@type": "APIReference",
|
|
89
|
-
headline: title,
|
|
90
|
-
description,
|
|
91
|
-
...(canonical ? { url: canonical } : {}),
|
|
92
|
-
isPartOf: website(config),
|
|
93
|
-
mainEntity: {
|
|
94
|
-
"@type": "EntryPoint",
|
|
95
|
-
name:
|
|
96
|
-
endpoint.operationId ??
|
|
97
|
-
`${endpoint.method.toUpperCase()} ${endpoint.path}`,
|
|
98
|
-
httpMethod: endpoint.method.toUpperCase(),
|
|
99
|
-
urlTemplate: endpoint.servers[0]
|
|
100
|
-
? `${endpoint.servers[0].replace(/\/$/, "")}${endpoint.path}`
|
|
101
|
-
: endpoint.path,
|
|
102
|
-
...(endpoint.requestBody?.contentType
|
|
103
|
-
? { encodingType: endpoint.requestBody.contentType }
|
|
104
|
-
: {}),
|
|
105
|
-
...(endpoint.responses[0]?.contentType
|
|
106
|
-
? { contentType: endpoint.responses[0].contentType }
|
|
107
|
-
: {}),
|
|
108
|
-
},
|
|
109
|
-
}
|
|
110
|
-
: changelogGroup
|
|
111
|
-
? {
|
|
112
|
-
"@context": "https://schema.org",
|
|
113
|
-
"@type": "CollectionPage",
|
|
114
|
-
headline: title,
|
|
115
|
-
description,
|
|
116
|
-
...(canonical ? { url: canonical } : {}),
|
|
117
|
-
isPartOf: website(config),
|
|
118
|
-
...(page?.changelogUpdates?.length
|
|
119
|
-
? {
|
|
120
|
-
hasPart: page.changelogUpdates.map((update) => ({
|
|
121
|
-
"@type": "TechArticle",
|
|
122
|
-
headline: update.label,
|
|
123
|
-
...(update.tags.length
|
|
124
|
-
? { keywords: update.tags.join(", ") }
|
|
125
|
-
: {}),
|
|
126
|
-
...(canonical ? { url: `${canonical}#${update.id}` } : {}),
|
|
127
|
-
})),
|
|
128
|
-
}
|
|
129
|
-
: {}),
|
|
130
|
-
}
|
|
131
|
-
: {
|
|
132
|
-
"@context": "https://schema.org",
|
|
133
|
-
"@type": "TechArticle",
|
|
134
|
-
headline: title,
|
|
135
|
-
description,
|
|
136
|
-
...(canonical ? { url: canonical } : {}),
|
|
137
|
-
isPartOf: website(config),
|
|
138
|
-
};
|
|
139
|
-
const crumbs = breadcrumbItems({
|
|
140
|
-
changelogGroup,
|
|
141
|
-
navigation,
|
|
142
|
-
pathname,
|
|
143
|
-
title: page?.title ?? endpoint!.title,
|
|
144
|
-
});
|
|
145
|
-
const structuredData = [
|
|
146
|
-
pageType,
|
|
147
|
-
{
|
|
148
|
-
"@context": "https://schema.org",
|
|
149
|
-
"@type": "BreadcrumbList",
|
|
150
|
-
itemListElement: [
|
|
151
|
-
{ label: config.title, href: config.siteUrl },
|
|
152
|
-
...crumbs,
|
|
153
|
-
].map((item, index, items) => ({
|
|
154
|
-
"@type": "ListItem",
|
|
155
|
-
position: index + 1,
|
|
156
|
-
name: item.label,
|
|
157
|
-
...((
|
|
158
|
-
index === items.length - 1
|
|
159
|
-
? canonical
|
|
160
|
-
: absoluteUrl(config.siteUrl, item.href)
|
|
161
|
-
)
|
|
162
|
-
? {
|
|
163
|
-
item:
|
|
164
|
-
index === items.length - 1
|
|
165
|
-
? canonical
|
|
166
|
-
: absoluteUrl(config.siteUrl, item.href),
|
|
167
|
-
}
|
|
168
|
-
: {}),
|
|
169
|
-
})),
|
|
170
|
-
},
|
|
171
|
-
];
|
|
172
|
-
return {
|
|
173
|
-
metadata: {
|
|
174
|
-
title,
|
|
175
|
-
description,
|
|
176
|
-
robots: { index: true, follow: true },
|
|
177
|
-
alternates: canonical ? { canonical } : undefined,
|
|
178
|
-
openGraph: {
|
|
179
|
-
type: "article",
|
|
180
|
-
siteName: config.title,
|
|
181
|
-
title,
|
|
182
|
-
description,
|
|
183
|
-
...(canonical ? { url: canonical } : {}),
|
|
184
|
-
},
|
|
185
|
-
twitter: { card: "summary", title, description },
|
|
186
|
-
},
|
|
187
|
-
structuredData,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
export type Theme = "dark" | "light" | "system";
|
|
2
|
-
export type ResolvedTheme = Exclude<Theme, "system">;
|
|
3
|
-
|
|
4
|
-
export const DEFAULT_THEME: Theme = "system";
|
|
5
|
-
export const THEME_STORAGE_KEY = "heyo-docs-theme";
|
|
6
|
-
|
|
7
|
-
const themeValues = new Set<Theme>(["light", "dark", "system"]);
|
|
8
|
-
|
|
9
|
-
export function isTheme(value: string | null): value is Theme {
|
|
10
|
-
return value !== null && themeValues.has(value as Theme);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function getStoredTheme(
|
|
14
|
-
storageKey: string,
|
|
15
|
-
fallbackTheme: Theme,
|
|
16
|
-
): Theme {
|
|
17
|
-
if (typeof window === "undefined") return fallbackTheme;
|
|
18
|
-
try {
|
|
19
|
-
const storedTheme = window.localStorage.getItem(storageKey);
|
|
20
|
-
return isTheme(storedTheme) ? storedTheme : fallbackTheme;
|
|
21
|
-
} catch {
|
|
22
|
-
return fallbackTheme;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function applyTheme(theme: Theme): ResolvedTheme {
|
|
27
|
-
const resolvedTheme =
|
|
28
|
-
theme === "system"
|
|
29
|
-
? window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
30
|
-
? "dark"
|
|
31
|
-
: "light"
|
|
32
|
-
: theme;
|
|
33
|
-
const root = window.document.documentElement;
|
|
34
|
-
root.classList.remove("light", "dark");
|
|
35
|
-
root.classList.add(resolvedTheme);
|
|
36
|
-
root.style.colorScheme = resolvedTheme;
|
|
37
|
-
return resolvedTheme;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function getThemeScript(storageKey: string, defaultTheme: Theme) {
|
|
41
|
-
return `(() => {
|
|
42
|
-
const storageKey = ${JSON.stringify(storageKey)};
|
|
43
|
-
const fallbackTheme = ${JSON.stringify(defaultTheme)};
|
|
44
|
-
const isTheme = (value) => value === "light" || value === "dark" || value === "system";
|
|
45
|
-
const storedTheme = (() => {
|
|
46
|
-
try {
|
|
47
|
-
const value = window.localStorage.getItem(storageKey);
|
|
48
|
-
return isTheme(value) ? value : fallbackTheme;
|
|
49
|
-
} catch {
|
|
50
|
-
return fallbackTheme;
|
|
51
|
-
}
|
|
52
|
-
})();
|
|
53
|
-
const resolvedTheme = storedTheme === "system"
|
|
54
|
-
? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
|
|
55
|
-
: storedTheme;
|
|
56
|
-
const root = document.documentElement;
|
|
57
|
-
root.classList.remove("light", "dark");
|
|
58
|
-
root.classList.add(resolvedTheme);
|
|
59
|
-
root.style.colorScheme = resolvedTheme;
|
|
60
|
-
})();`;
|
|
61
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: API overview
|
|
3
|
-
description: Learn how to authenticate and explore the API reference.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# API overview
|
|
7
|
-
|
|
8
|
-
The API reference is generated from `openapi.json`. Use the endpoint pages to
|
|
9
|
-
enter parameters, send a request, and copy a cURL or JavaScript example.
|
|
10
|
-
|
|
11
|
-
## Authentication
|
|
12
|
-
|
|
13
|
-
The example API uses a Bearer token. Enter it once on any protected endpoint;
|
|
14
|
-
Heyo Docs keeps it in this browser while you move between API pages.
|
|
15
|
-
|
|
16
|
-
## Custom pages first
|
|
17
|
-
|
|
18
|
-
This ordinary MDX page belongs to the OpenAPI group. Its section is configured
|
|
19
|
-
before the generated endpoint sections, so it always appears at the top of the
|
|
20
|
-
sidebar.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="360" viewBox="0 0 960 360" fill="none">
|
|
2
|
-
<rect width="960" height="360" rx="24" fill="#F7F7F8"/>
|
|
3
|
-
<g fill="#FFFFFF" stroke="#D4D4D8" stroke-width="2">
|
|
4
|
-
<rect x="72" y="108" width="220" height="144" rx="18"/>
|
|
5
|
-
<rect x="370" y="108" width="220" height="144" rx="18"/>
|
|
6
|
-
<rect x="668" y="108" width="220" height="144" rx="18"/>
|
|
7
|
-
</g>
|
|
8
|
-
<g fill="#18181B" font-family="Arial, sans-serif" text-anchor="middle">
|
|
9
|
-
<text x="182" y="166" font-size="17" font-weight="700">Content</text>
|
|
10
|
-
<text x="182" y="194" font-size="14" fill="#71717A">MDX and assets</text>
|
|
11
|
-
<text x="480" y="166" font-size="17" font-weight="700">Build</text>
|
|
12
|
-
<text x="480" y="194" font-size="14" fill="#71717A">Vite bundles files</text>
|
|
13
|
-
<text x="778" y="166" font-size="17" font-weight="700">Documentation</text>
|
|
14
|
-
<text x="778" y="194" font-size="14" fill="#71717A">Static output</text>
|
|
15
|
-
</g>
|
|
16
|
-
<g stroke="#71717A" stroke-linecap="round" stroke-width="2">
|
|
17
|
-
<path d="M314 180h34"/>
|
|
18
|
-
<path d="m340 172 8 8-8 8"/>
|
|
19
|
-
<path d="M612 180h34"/>
|
|
20
|
-
<path d="m638 172 8 8-8 8"/>
|
|
21
|
-
</g>
|
|
22
|
-
</svg>
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Changelog
|
|
3
|
-
description: Mock release notes for the Heyo Docs example.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Changelog
|
|
7
|
-
|
|
8
|
-
<Update label="April 2026" tags={["New", "Documentation"]}>
|
|
9
|
-
|
|
10
|
-
## Starter documentation
|
|
11
|
-
|
|
12
|
-
New projects and runnable examples now include a structured Quickstart, basic
|
|
13
|
-
configuration guide, deployment overview, and essential writing patterns.
|
|
14
|
-
|
|
15
|
-
## Square API group
|
|
16
|
-
|
|
17
|
-
The example navigation now keeps the pinned Square OpenAPI schema in its own
|
|
18
|
-
group, making large-schema behaviour easy to inspect.
|
|
19
|
-
|
|
20
|
-
</Update>
|
|
21
|
-
|
|
22
|
-
<Update label="March 2026" tags={["Improved", "Developer experience"]}>
|
|
23
|
-
|
|
24
|
-
## Clearer navigation
|
|
25
|
-
|
|
26
|
-
External documentation links can display an icon, and sidebar groups now keep
|
|
27
|
-
onboarding, API reference, and release notes separate.
|
|
28
|
-
|
|
29
|
-
</Update>
|
|
30
|
-
|
|
31
|
-
<Update label="February 2026" tags={["Fixed"]}>
|
|
32
|
-
|
|
33
|
-
## More predictable previews
|
|
34
|
-
|
|
35
|
-
Preview builds now validate configured routes before mock release notes are
|
|
36
|
-
published.
|
|
37
|
-
|
|
38
|
-
</Update>
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Basic Configuration
|
|
3
|
-
description: Set the identity, content source, and sidebar structure for a Heyo Docs site.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Basic Configuration
|
|
7
|
-
|
|
8
|
-
`heyo-docs.config.ts` is the single source of truth for a Heyo Docs site.
|
|
9
|
-
The framework integration and the documentation UI read the same validated
|
|
10
|
-
object, so a change to navigation or metadata does not need to be repeated in
|
|
11
|
-
multiple places.
|
|
12
|
-
|
|
13
|
-
## Start with the site model
|
|
14
|
-
|
|
15
|
-
The generated project already includes a working configuration. Update the
|
|
16
|
-
identity fields first, then point `content` at the directory that contains
|
|
17
|
-
your MDX files.
|
|
18
|
-
|
|
19
|
-
<CodeBlock language="ts" title="heyo-docs.config.ts" showLineNumbers>
|
|
20
|
-
{`import { heyoDocs } from "@heyo-sh/heyo-docs";
|
|
21
|
-
|
|
22
|
-
export default heyoDocs({
|
|
23
|
-
title: "{{PROJECT_NAME}}",
|
|
24
|
-
description: "Guides and reference for {{PROJECT_NAME}}.",
|
|
25
|
-
content: "./content",
|
|
26
|
-
theme: "grain",
|
|
27
|
-
mode: "system",
|
|
28
|
-
branding: { name: "{{PROJECT_NAME}}" },
|
|
29
|
-
});`}
|
|
30
|
-
</CodeBlock>
|
|
31
|
-
|
|
32
|
-
Set `siteUrl` to the final public origin before publishing. It is used for
|
|
33
|
-
canonical URLs, social metadata, the sitemap, RSS, and AI-facing discovery
|
|
34
|
-
files.
|
|
35
|
-
|
|
36
|
-
## Organize the sidebar
|
|
37
|
-
|
|
38
|
-
Use `groups` to define the order readers see. A group contains named sections
|
|
39
|
-
and their MDX page references; a reference is relative to `content` and does
|
|
40
|
-
not include the `.mdx` extension.
|
|
41
|
-
|
|
42
|
-
<CodeBlock language="ts" title="heyo-docs.config.ts">
|
|
43
|
-
{`groups: [
|
|
44
|
-
{
|
|
45
|
-
group: "Documentation",
|
|
46
|
-
icon: "globe",
|
|
47
|
-
sections: [
|
|
48
|
-
{
|
|
49
|
-
section: "Get Started",
|
|
50
|
-
icon: "lightbulb",
|
|
51
|
-
pages: ["quickstart", "configuration"],
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
section: "Essentials",
|
|
55
|
-
icon: "book",
|
|
56
|
-
pages: ["essentials/text-styling", "essentials/code-blocks"],
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
},
|
|
60
|
-
],`}
|
|
61
|
-
</CodeBlock>
|
|
62
|
-
|
|
63
|
-
The order is meaningful: it controls the sidebar and the previous/next links at
|
|
64
|
-
the end of each article. Use a page object with `title` and `src` when a
|
|
65
|
-
sidebar entry should point to a page outside the local documentation.
|
|
66
|
-
|
|
67
|
-
## AI chat
|
|
68
|
-
|
|
69
|
-
Add `ai.chat` to enable a documentation-aware chat through [Pi](https://github.com/badlogic/pi-mono).
|
|
70
|
-
Use any provider identifier bundled with the installed Pi version, such as
|
|
71
|
-
`openai`, `anthropic`, `google`, `openrouter`, or `xai`. Authentication stays
|
|
72
|
-
server-only and is supplied by the route that handles the chat request.
|
|
73
|
-
|
|
74
|
-
<CodeBlock language="ts" title="heyo-docs.config.ts">
|
|
75
|
-
{`ai: {
|
|
76
|
-
chat: {
|
|
77
|
-
provider: "openai",
|
|
78
|
-
model: "gpt-5-mini",
|
|
79
|
-
variant: "right", // "center" shows the compact prompt first
|
|
80
|
-
icon: "chat",
|
|
81
|
-
text: "AI Chat",
|
|
82
|
-
name: "Docs Assistant", // name shown in the drawer and message labels
|
|
83
|
-
placeholder: "Ask AI about the docs", // used in the compact prompt and drawer
|
|
84
|
-
},
|
|
85
|
-
},`}
|
|
86
|
-
</CodeBlock>
|
|
87
|
-
|
|
88
|
-
`provider` and `model` are required. Keep the credential out of this
|
|
89
|
-
configuration and pass it as `auth` to `createAiChatResponse` in the server
|
|
90
|
-
route. Cloudflare routes obtain the key from the Worker `env` binding; Node.js
|
|
91
|
-
routes can read their server environment there. GitHub Copilot and OpenAI Codex
|
|
92
|
-
use `auth: { type: "oauth", getAccessToken }`; Amazon Bedrock uses either
|
|
93
|
-
`{ type: "aws", region?, profile? }` and the AWS credential chain, or
|
|
94
|
-
`{ type: "bedrock-bearer", token, region? }`. The model must be an actual model
|
|
95
|
-
identifier, not the provider name. Amazon Bedrock requires a Node.js chat
|
|
96
|
-
endpoint. `variant` defaults to `right`. `name`
|
|
97
|
-
defaults to `AI`; it is used for the drawer title and the assistant's message
|
|
98
|
-
label. `placeholder` defaults to
|
|
99
|
-
`Ask AI about the docs` and is shared by the compact prompt and drawer input.
|
|
100
|
-
The drawer stays open while
|
|
101
|
-
readers navigate between documentation pages and does not block the rest of the
|
|
102
|
-
page.
|
|
103
|
-
|
|
104
|
-
Its chrome follows the selected documentation theme. For example, `grain` uses
|
|
105
|
-
a full-height right panel with a left border, while the other themes keep a
|
|
106
|
-
floating panel treatment.
|
|
107
|
-
|
|
108
|
-
## Verify the result
|
|
109
|
-
|
|
110
|
-
Run the project's typecheck and build commands after changing configuration.
|
|
111
|
-
They resolve each configured MDX page and report missing paths or malformed
|
|
112
|
-
navigation before the site is deployed.
|
|
113
|
-
|
|
114
|
-
<Callout type="info" title="Keep page metadata close to the page">
|
|
115
|
-
Put a page's <code>title</code> and <code>description</code> in its MDX
|
|
116
|
-
frontmatter. Keep shared site settings in <code>heyo-docs.config.ts</code>.
|
|
117
|
-
</Callout>
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Deploying
|
|
3
|
-
description: Build and publish your Heyo Docs site with the deployment target chosen during setup.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Deploying
|
|
7
|
-
|
|
8
|
-
A Heyo Docs site is built as part of the framework application. The creator asks
|
|
9
|
-
for a deployment target when the project is created and adds the corresponding
|
|
10
|
-
configuration, so the same MDX content, navigation, search, and metadata are
|
|
11
|
-
used locally and in production.
|
|
12
|
-
|
|
13
|
-
## Choose the target during setup
|
|
14
|
-
|
|
15
|
-
Select Vercel or Cloudflare when running the creator if you already know where
|
|
16
|
-
the site will live. The generated project includes the adapter and deployment
|
|
17
|
-
configuration required by that platform. You can also choose to configure
|
|
18
|
-
deployment later and keep the starter focused on local development.
|
|
19
|
-
|
|
20
|
-
## Build before publishing
|
|
21
|
-
|
|
22
|
-
Run the production build on your machine before creating a deployment. It
|
|
23
|
-
resolves every configured page, validates the navigation tree, and generates
|
|
24
|
-
the sitemap, RSS feed, robots file, and AI-facing documentation endpoints.
|
|
25
|
-
|
|
26
|
-
<CodeBlock language="bash" title="Terminal">
|
|
27
|
-
{"bun run build"}
|
|
28
|
-
</CodeBlock>
|
|
29
|
-
|
|
30
|
-
Use the equivalent `npm run build`, `pnpm build`, or `yarn build` command
|
|
31
|
-
when the project uses a different package manager.
|
|
32
|
-
|
|
33
|
-
## Cloudflare secrets
|
|
34
|
-
|
|
35
|
-
For AI chat, save the provider key as a Worker secret:
|
|
36
|
-
|
|
37
|
-
<CodeBlock language="bash" title="Terminal">
|
|
38
|
-
{"bunx wrangler secret put OPENAI_API_KEY"}
|
|
39
|
-
</CodeBlock>
|
|
40
|
-
|
|
41
|
-
Keep the credential out of `heyo-docs.config.ts`. The Cloudflare overlay reads
|
|
42
|
-
the native Worker `env.OPENAI_API_KEY` binding in the AI route, after the
|
|
43
|
-
request reaches the Worker. It is never part of the documentation build or the
|
|
44
|
-
browser bundle.
|
|
45
|
-
|
|
46
|
-
<Callout type="tip" title="Set the canonical URL">
|
|
47
|
-
Add the final public origin as <code>siteUrl</code> in
|
|
48
|
-
<code>heyo-docs.config.ts</code> before the first production release. It keeps
|
|
49
|
-
canonical links, social metadata, and generated discovery files consistent.
|
|
50
|
-
</Callout>
|
|
51
|
-
|
|
52
|
-
## Publish the project
|
|
53
|
-
|
|
54
|
-
For a generated Vercel or Cloudflare project, connect the repository in the
|
|
55
|
-
provider dashboard or run the deployment script included by the selected setup.
|
|
56
|
-
The provider builds the application with the same command you verified locally.
|
|
57
|
-
|
|
58
|
-
<Steps>
|
|
59
|
-
<Step title="Push the documentation source">
|
|
60
|
-
Commit the MDX content and <code>heyo-docs.config.ts</code> together so the
|
|
61
|
-
deployed navigation matches the reviewed pages.
|
|
62
|
-
</Step>
|
|
63
|
-
<Step title="Create a preview">
|
|
64
|
-
Review the provider's preview URL for layout, links, search, and metadata.
|
|
65
|
-
</Step>
|
|
66
|
-
<Step title="Promote the production build">
|
|
67
|
-
Publish once the preview is correct and the configured <code>siteUrl</code>
|
|
68
|
-
matches the public domain.
|
|
69
|
-
</Step>
|
|
70
|
-
</Steps>
|