@kpritam/grimoire-output-docusaurus 0.1.8
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/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/internal/assets.d.ts +9 -0
- package/dist/internal/assets.js +50 -0
- package/dist/internal/docusaurusConfig.d.ts +9 -0
- package/dist/internal/docusaurusConfig.js +259 -0
- package/dist/internal/spellbookAssets.d.ts +39 -0
- package/dist/internal/spellbookAssets.js +68 -0
- package/dist/layer.d.ts +3 -0
- package/dist/layer.js +6 -0
- package/dist/shared.d.ts +10 -0
- package/dist/shared.js +36 -0
- package/dist/upstream.d.ts +6 -0
- package/dist/upstream.js +84 -0
- package/package.json +59 -0
- package/src/index.ts +1 -0
- package/src/internal/assets.ts +66 -0
- package/src/internal/docusaurusConfig.ts +281 -0
- package/src/internal/spellbookAssets.ts +80 -0
- package/src/layer.ts +12 -0
- package/src/shared.ts +43 -0
- package/src/upstream.ts +119 -0
- package/templates/spellbook/spellbookPlugin.ts +156 -0
- package/templates/spellbook/src/components/SpellbookChat/ChatEngine.ts +79 -0
- package/templates/spellbook/src/components/SpellbookChat/ChatErrorBoundary.tsx +65 -0
- package/templates/spellbook/src/components/SpellbookChat/Markdown.tsx +259 -0
- package/templates/spellbook/src/components/SpellbookChat/README.md +111 -0
- package/templates/spellbook/src/components/SpellbookChat/SettingsPanel.tsx +376 -0
- package/templates/spellbook/src/components/SpellbookChat/VoiceMode.tsx +867 -0
- package/templates/spellbook/src/components/SpellbookChat/index.tsx +744 -0
- package/templates/spellbook/src/components/SpellbookChat/markdown.module.css +343 -0
- package/templates/spellbook/src/components/SpellbookChat/secretStore.ts +106 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/anthropic.ts +36 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/createCloudProvider.ts +112 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/google.ts +33 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/index.ts +32 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/mapFinishReason.ts +23 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/ollama.ts +44 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/openai.ts +34 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/openaiRealtime.ts +320 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/types.ts +172 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/webllm.ts +214 -0
- package/templates/spellbook/src/components/SpellbookChat/styles.module.css +852 -0
- package/templates/spellbook/src/components/SpellbookChat/systemPrompt.ts +107 -0
- package/templates/spellbook/src/components/SpellbookChat/transformers-ssr-stub.ts +16 -0
- package/templates/spellbook/src/components/SpellbookChat/types.ts +52 -0
- package/templates/spellbook/src/components/SpellbookChat/useBundleLoader.ts +46 -0
- package/templates/spellbook/src/components/SpellbookChat/useChatEngine.ts +524 -0
- package/templates/spellbook/src/components/SpellbookChat/useEmbeddings.ts +147 -0
- package/templates/spellbook/src/components/SpellbookChat/useRetrieval.ts +377 -0
- package/templates/spellbook/src/components/SpellbookChat/useSileroVAD.ts +236 -0
- package/templates/spellbook/src/components/SpellbookChat/useSpeechRecognition.ts +271 -0
- package/templates/spellbook/src/components/SpellbookChat/useSpeechSynthesis.ts +229 -0
- package/templates/spellbook/src/components/SpellbookChat/useUnifiedSTT.ts +134 -0
- package/templates/spellbook/src/components/SpellbookChat/useWhisperSTT.ts +411 -0
- package/templates/spellbook/src/components/SpellbookChat/vad-ssr-stub.ts +25 -0
- package/templates/spellbook/src/components/SpellbookChat/voiceDebug.ts +60 -0
- package/templates/spellbook/src/components/SpellbookChat/voiceFsm.ts +196 -0
- package/templates/spellbook/src/components/SpellbookChat/voiceStyles.module.css +334 -0
- package/templates/spellbook/src/components/SpellbookChat/webllm-ssr-stub.ts +8 -0
- package/templates/spellbook/src/components/SpellbookChatDisabled.tsx +20 -0
- package/templates/spellbook/src/theme/Root.tsx +29 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
const genImports = (spellbook) => {
|
|
2
|
+
const spellbookImport = spellbook
|
|
3
|
+
? `import { spellbookWebpackPlugin } from "./spellbookPlugin";\n`
|
|
4
|
+
: "";
|
|
5
|
+
return `import type { Config } from "@docusaurus/types";
|
|
6
|
+
import type * as Preset from "@docusaurus/preset-classic";
|
|
7
|
+
import { themes as prismThemes } from "prism-react-renderer";
|
|
8
|
+
${spellbookImport}`;
|
|
9
|
+
};
|
|
10
|
+
const genWatcherEnv = () => `if (!Object.hasOwn(process.env, "WATCHPACK_POLLING")) {
|
|
11
|
+
process.env.WATCHPACK_POLLING = "true";
|
|
12
|
+
}
|
|
13
|
+
if (!Object.hasOwn(process.env, "CHOKIDAR_USEPOLLING")) {
|
|
14
|
+
process.env.CHOKIDAR_USEPOLLING = "true";
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
const genDevWatchPlugin = () => `const grimoireDevWatchPlugin = () => ({
|
|
18
|
+
name: "grimoire-dev-watch",
|
|
19
|
+
configureWebpack() {
|
|
20
|
+
return {
|
|
21
|
+
watchOptions: {
|
|
22
|
+
ignored: [
|
|
23
|
+
"**/.git/**",
|
|
24
|
+
"**/node_modules/**",
|
|
25
|
+
"**/.docusaurus/**",
|
|
26
|
+
"**/build/**",
|
|
27
|
+
"**/dist/**",
|
|
28
|
+
"**/.turbo/**",
|
|
29
|
+
"**/.next/**",
|
|
30
|
+
"**/coverage/**",
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
`;
|
|
37
|
+
const genSidebarHelpers = () => `const SIDEBAR_ACRONYMS = new Set([
|
|
38
|
+
"cli", "ci", "cd", "ai", "api", "sdk", "url", "uri", "http", "https",
|
|
39
|
+
"json", "yaml", "md", "mdx", "sha", "dsl", "ide", "ui", "ux", "io",
|
|
40
|
+
"pr", "mr", "os", "sql", "tls", "ssh", "cors", "dns", "tcp", "udp", "jwt",
|
|
41
|
+
]);
|
|
42
|
+
const humanizeSidebarLabel = (slug: string): string =>
|
|
43
|
+
slug
|
|
44
|
+
.split(/[-_\\s]+/)
|
|
45
|
+
.map((w, i) => {
|
|
46
|
+
if (!w) return w;
|
|
47
|
+
if (SIDEBAR_ACRONYMS.has(w.toLowerCase())) return w.toUpperCase();
|
|
48
|
+
if (/^v\\d/.test(w)) return w.toLowerCase();
|
|
49
|
+
return i === 0 ? w.charAt(0).toUpperCase() + w.slice(1) : w;
|
|
50
|
+
})
|
|
51
|
+
.join(" ");
|
|
52
|
+
|
|
53
|
+
const SIDEBAR_WEIGHTS: Record<string, number> = {
|
|
54
|
+
"index": -1000,
|
|
55
|
+
"introduction": -900,
|
|
56
|
+
"intro": -900,
|
|
57
|
+
"getting-started": -800,
|
|
58
|
+
"quickstart": -780,
|
|
59
|
+
"quick-start": -780,
|
|
60
|
+
"installation": -760,
|
|
61
|
+
"install": -760,
|
|
62
|
+
"tutorial": -700,
|
|
63
|
+
"overview": -680,
|
|
64
|
+
"concepts": -400,
|
|
65
|
+
"guides": -300,
|
|
66
|
+
"how-to": -280,
|
|
67
|
+
"recipes": -260,
|
|
68
|
+
"reference": 200,
|
|
69
|
+
"api": 220,
|
|
70
|
+
"cli": 220,
|
|
71
|
+
"configuration": 230,
|
|
72
|
+
"config": 230,
|
|
73
|
+
"architecture": 600,
|
|
74
|
+
"internals": 700,
|
|
75
|
+
"development": 780,
|
|
76
|
+
"contributing": 800,
|
|
77
|
+
"roadmap": 880,
|
|
78
|
+
"changelog": 900,
|
|
79
|
+
"faq": 920,
|
|
80
|
+
"troubleshooting": 940,
|
|
81
|
+
};
|
|
82
|
+
const sidebarWeight = (slug: string): number =>
|
|
83
|
+
SIDEBAR_WEIGHTS[slug.toLowerCase()] ?? 0;
|
|
84
|
+
`;
|
|
85
|
+
const genSidebarItemsGenerator = () => ` sidebarItemsGenerator: async ({
|
|
86
|
+
defaultSidebarItemsGenerator,
|
|
87
|
+
...args
|
|
88
|
+
}) => {
|
|
89
|
+
const items = await defaultSidebarItemsGenerator(args);
|
|
90
|
+
const hiddenAnywhere = new Set(["README"]);
|
|
91
|
+
const hiddenAtRoot = new Set(["index"]);
|
|
92
|
+
const slugTail = (id: string): string => {
|
|
93
|
+
const segs = id.split("/");
|
|
94
|
+
return segs[segs.length - 1] ?? id;
|
|
95
|
+
};
|
|
96
|
+
const looksRawSlug = (s: string): boolean =>
|
|
97
|
+
/^[a-z0-9]+([-_][a-z0-9]+)*$/.test(s);
|
|
98
|
+
type Item = (typeof items)[number];
|
|
99
|
+
const itemKey = (item: Item): string => {
|
|
100
|
+
if (item.type === "doc") return slugTail(item.id).toLowerCase();
|
|
101
|
+
if (item.type === "category") return item.label.toLowerCase();
|
|
102
|
+
return "";
|
|
103
|
+
};
|
|
104
|
+
const sortTopLevel = (list: Item[]): Item[] =>
|
|
105
|
+
[...list].sort((a, b) => {
|
|
106
|
+
const ka = itemKey(a);
|
|
107
|
+
const kb = itemKey(b);
|
|
108
|
+
const wa = sidebarWeight(ka);
|
|
109
|
+
const wb = sidebarWeight(kb);
|
|
110
|
+
if (wa !== wb) return wa - wb;
|
|
111
|
+
return ka.localeCompare(kb);
|
|
112
|
+
});
|
|
113
|
+
const walk = (item: Item, depth: number): Item | null => {
|
|
114
|
+
if (item.type === "doc") {
|
|
115
|
+
const tail = slugTail(item.id);
|
|
116
|
+
if (hiddenAnywhere.has(tail)) return null;
|
|
117
|
+
if (depth === 0 && hiddenAtRoot.has(tail)) return null;
|
|
118
|
+
if (!item.label && looksRawSlug(tail)) {
|
|
119
|
+
return { ...item, label: humanizeSidebarLabel(tail) };
|
|
120
|
+
}
|
|
121
|
+
return item;
|
|
122
|
+
}
|
|
123
|
+
if (item.type === "category") {
|
|
124
|
+
const label = looksRawSlug(item.label)
|
|
125
|
+
? humanizeSidebarLabel(item.label)
|
|
126
|
+
: item.label;
|
|
127
|
+
const children = item.items
|
|
128
|
+
.map((c: Item) => walk(c, depth + 1))
|
|
129
|
+
.filter((x: Item | null): x is Item => x !== null);
|
|
130
|
+
return {
|
|
131
|
+
...item,
|
|
132
|
+
label,
|
|
133
|
+
collapsed: depth === 0 ? false : item.collapsed,
|
|
134
|
+
items: children,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return item;
|
|
138
|
+
};
|
|
139
|
+
return sortTopLevel(
|
|
140
|
+
items
|
|
141
|
+
.map((c: Item) => walk(c, 0))
|
|
142
|
+
.filter((x: Item | null): x is Item => x !== null),
|
|
143
|
+
);
|
|
144
|
+
},
|
|
145
|
+
`;
|
|
146
|
+
const markdownBlock = (mermaid) => mermaid
|
|
147
|
+
? `markdown: {
|
|
148
|
+
mermaid: true,
|
|
149
|
+
format: "mdx",
|
|
150
|
+
},
|
|
151
|
+
`
|
|
152
|
+
: "";
|
|
153
|
+
const themesSection = (mermaid) => mermaid ? ` themes: ["@docusaurus/theme-mermaid"],\n` : "";
|
|
154
|
+
const mermaidPrismTheme = (mermaid) => mermaid
|
|
155
|
+
? `,
|
|
156
|
+
mermaid: { theme: { light: "neutral", dark: "dark" } }`
|
|
157
|
+
: "";
|
|
158
|
+
const repoNavItem = (repo) => repo !== undefined && repo !== ""
|
|
159
|
+
? ` {
|
|
160
|
+
href: ${JSON.stringify(repo)},
|
|
161
|
+
position: "right",
|
|
162
|
+
className: "navbar__item navbar__item--github",
|
|
163
|
+
"aria-label": "View source on GitHub",
|
|
164
|
+
title: "GitHub",
|
|
165
|
+
label: "GitHub",
|
|
166
|
+
},
|
|
167
|
+
`
|
|
168
|
+
: "";
|
|
169
|
+
export const genDocusaurusConfig = (opts) => {
|
|
170
|
+
const mb = markdownBlock(opts.mermaid);
|
|
171
|
+
const ts = themesSection(opts.mermaid);
|
|
172
|
+
const mpt = mermaidPrismTheme(opts.mermaid);
|
|
173
|
+
const rni = repoNavItem(opts.repo);
|
|
174
|
+
const spellbookOn = opts.spellbook === true;
|
|
175
|
+
const pluginsList = spellbookOn
|
|
176
|
+
? "[grimoireDevWatchPlugin, spellbookWebpackPlugin]"
|
|
177
|
+
: "[grimoireDevWatchPlugin]";
|
|
178
|
+
return `${genImports(spellbookOn)}
|
|
179
|
+
// macOS EMFILE workaround when \`docs.path\` walks the whole tome: force
|
|
180
|
+
// Webpack/Watchpack and chokidar onto polling. Opt out with
|
|
181
|
+
// WATCHPACK_POLLING=false / CHOKIDAR_USEPOLLING=false.
|
|
182
|
+
${genWatcherEnv()}
|
|
183
|
+
${genDevWatchPlugin()}
|
|
184
|
+
// Sidebar polish: humanize raw slugs into Sentence-case, upper-case known
|
|
185
|
+
// technical acronyms, and weight common top-level slugs (getting-started
|
|
186
|
+
// first, architecture / internals last). Agent-authored \`sidebar_label\` /
|
|
187
|
+
// \`sidebar_position\` always wins over these fallbacks.
|
|
188
|
+
${genSidebarHelpers()}
|
|
189
|
+
const config: Config = {
|
|
190
|
+
title: ${JSON.stringify(opts.siteTitle)},
|
|
191
|
+
tagline: ${JSON.stringify(opts.tagline)},
|
|
192
|
+
favicon: "img/logo.svg",
|
|
193
|
+
url: "https://example.com",
|
|
194
|
+
baseUrl: "/",
|
|
195
|
+
organizationName: "grimoire",
|
|
196
|
+
projectName: "docs",
|
|
197
|
+
onBrokenLinks: "warn",
|
|
198
|
+
${mb} presets: [
|
|
199
|
+
[
|
|
200
|
+
"classic",
|
|
201
|
+
{
|
|
202
|
+
docs: {
|
|
203
|
+
path: "../",
|
|
204
|
+
exclude: [
|
|
205
|
+
"site/**",
|
|
206
|
+
"**/node_modules/**",
|
|
207
|
+
"**/.git/**",
|
|
208
|
+
".grimoire/**",
|
|
209
|
+
"**/.grimoire/**",
|
|
210
|
+
".grimoire-seal",
|
|
211
|
+
"**/.grimoire-seal",
|
|
212
|
+
".grimoire-progress.json",
|
|
213
|
+
"**/.grimoire-progress.json",
|
|
214
|
+
// README.md and index.md (slug: /) would both claim "/" — keep the
|
|
215
|
+
// landing page and exclude README globally.
|
|
216
|
+
"**/README.md",
|
|
217
|
+
],
|
|
218
|
+
routeBasePath: "/",
|
|
219
|
+
sidebarPath: "./sidebars.ts",
|
|
220
|
+
editLocalizedFiles: false,
|
|
221
|
+
editUrl: undefined,
|
|
222
|
+
${genSidebarItemsGenerator()} },
|
|
223
|
+
blog: false,
|
|
224
|
+
theme: {
|
|
225
|
+
customCss: "./src/css/custom.css",
|
|
226
|
+
},
|
|
227
|
+
} satisfies Preset.Options,
|
|
228
|
+
],
|
|
229
|
+
],
|
|
230
|
+
plugins: ${pluginsList},
|
|
231
|
+
${ts} themeConfig: {
|
|
232
|
+
colorMode: {
|
|
233
|
+
defaultMode: "light",
|
|
234
|
+
respectPrefersColorScheme: true,
|
|
235
|
+
},
|
|
236
|
+
navbar: {
|
|
237
|
+
title: ${JSON.stringify(opts.siteTitle)},
|
|
238
|
+
logo: {
|
|
239
|
+
alt: ${JSON.stringify(opts.siteTitle)},
|
|
240
|
+
src: "img/logo.svg",
|
|
241
|
+
srcDark: "img/logo-dark.svg",
|
|
242
|
+
},
|
|
243
|
+
items: [
|
|
244
|
+
${rni} ],
|
|
245
|
+
},
|
|
246
|
+
footer: {
|
|
247
|
+
style: "dark",
|
|
248
|
+
copyright: \`© \${new Date().getFullYear()} \${${JSON.stringify(opts.siteTitle)}}\`,
|
|
249
|
+
},
|
|
250
|
+
prism: {
|
|
251
|
+
theme: prismThemes.github,
|
|
252
|
+
darkTheme: prismThemes.dracula,
|
|
253
|
+
}${mpt},
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export default config;
|
|
258
|
+
`;
|
|
259
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Copy the SpellbookChat React tree, the `theme/Root.tsx` swizzle, the
|
|
4
|
+
* `SpellbookChatDisabled.tsx` stub, AND the root-level `spellbookPlugin.ts`
|
|
5
|
+
* into the scaffolded site. The template layout mirrors the site layout 1:1
|
|
6
|
+
* so this is a single recursive copy.
|
|
7
|
+
*
|
|
8
|
+
* Files are **overwritten** on every run. This is safe because the entire
|
|
9
|
+
* copied tree is package-managed: it's regenerated from
|
|
10
|
+
* `tome/site/src/components/SpellbookChat/` on every release of
|
|
11
|
+
* `@kpritam/grimoire-output-docusaurus` via `sync-spellbook-templates.mjs`.
|
|
12
|
+
* Customisation should happen in user-owned files (e.g. theme overrides),
|
|
13
|
+
* not by editing these in place.
|
|
14
|
+
*/
|
|
15
|
+
export declare const writeSpellbookAssets: (fs: FileSystem.FileSystem, pathApi: Path.Path, siteDir: string) => Effect.Effect<readonly string[], import("@kpritam/grimoire-core").OutputWriteError, never>;
|
|
16
|
+
/**
|
|
17
|
+
* npm dependencies the SpellbookChat components require at runtime.
|
|
18
|
+
* Keep in sync with `tome/site/package.json` so the canonical site and
|
|
19
|
+
* scaffolded sites resolve identical versions.
|
|
20
|
+
*
|
|
21
|
+
* Notable additions over the v0.1.6 baseline:
|
|
22
|
+
* - `streamdown` replaces `react-markdown` for streaming-aware rendering
|
|
23
|
+
* - `@ricky0123/vad-web` + `onnxruntime-web` power Silero VAD for voice
|
|
24
|
+
* mode (Web Audio + ONNX in-browser)
|
|
25
|
+
*/
|
|
26
|
+
export declare const SPELLBOOK_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
27
|
+
/**
|
|
28
|
+
* Build-time dependencies the Spellbook plugin imports directly.
|
|
29
|
+
*
|
|
30
|
+
* `spellbookPlugin.ts` calls `new webpack.NormalModuleReplacementPlugin(...)`
|
|
31
|
+
* to swap the chat tree for the disabled stub, so `webpack` must be
|
|
32
|
+
* resolvable both at type-check time (`tsc --noEmit`) and when Docusaurus
|
|
33
|
+
* loads the plugin. Strict pnpm node_modules layouts only expose packages
|
|
34
|
+
* that are direct dependencies, so we must declare it here even though
|
|
35
|
+
* `@docusaurus/core` already pulls it in transitively.
|
|
36
|
+
*
|
|
37
|
+
* Pinned to `^5.95.0` to match `@docusaurus/core@3.10.x`'s declared range.
|
|
38
|
+
*/
|
|
39
|
+
export declare const SPELLBOOK_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { siteScaffoldPhases } from "@kpritam/grimoire-core/services/SiteScaffold";
|
|
3
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
4
|
+
// Loaded from `dist/internal/spellbookAssets.js`; `templates/spellbook` lives
|
|
5
|
+
// two levels up alongside the published `dist/` and `src/` folders.
|
|
6
|
+
const spellbookTemplatesRoot = (pathApi) => {
|
|
7
|
+
const here = fileURLToPath(new URL(".", import.meta.url));
|
|
8
|
+
return pathApi.resolve(here, "..", "..", "templates", "spellbook");
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Copy the SpellbookChat React tree, the `theme/Root.tsx` swizzle, the
|
|
12
|
+
* `SpellbookChatDisabled.tsx` stub, AND the root-level `spellbookPlugin.ts`
|
|
13
|
+
* into the scaffolded site. The template layout mirrors the site layout 1:1
|
|
14
|
+
* so this is a single recursive copy.
|
|
15
|
+
*
|
|
16
|
+
* Files are **overwritten** on every run. This is safe because the entire
|
|
17
|
+
* copied tree is package-managed: it's regenerated from
|
|
18
|
+
* `tome/site/src/components/SpellbookChat/` on every release of
|
|
19
|
+
* `@kpritam/grimoire-output-docusaurus` via `sync-spellbook-templates.mjs`.
|
|
20
|
+
* Customisation should happen in user-owned files (e.g. theme overrides),
|
|
21
|
+
* not by editing these in place.
|
|
22
|
+
*/
|
|
23
|
+
export const writeSpellbookAssets = (fs, pathApi, siteDir) => siteScaffoldPhases
|
|
24
|
+
.writeManagedAssets({
|
|
25
|
+
templatesRoot: spellbookTemplatesRoot(pathApi),
|
|
26
|
+
siteDir
|
|
27
|
+
})
|
|
28
|
+
.pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, pathApi));
|
|
29
|
+
/**
|
|
30
|
+
* npm dependencies the SpellbookChat components require at runtime.
|
|
31
|
+
* Keep in sync with `tome/site/package.json` so the canonical site and
|
|
32
|
+
* scaffolded sites resolve identical versions.
|
|
33
|
+
*
|
|
34
|
+
* Notable additions over the v0.1.6 baseline:
|
|
35
|
+
* - `streamdown` replaces `react-markdown` for streaming-aware rendering
|
|
36
|
+
* - `@ricky0123/vad-web` + `onnxruntime-web` power Silero VAD for voice
|
|
37
|
+
* mode (Web Audio + ONNX in-browser)
|
|
38
|
+
*/
|
|
39
|
+
export const SPELLBOOK_DEPENDENCIES = {
|
|
40
|
+
"@ai-sdk/anthropic": "^3.0.77",
|
|
41
|
+
"@ai-sdk/google": "^3.0.73",
|
|
42
|
+
"@ai-sdk/openai": "^3.0.63",
|
|
43
|
+
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
44
|
+
"@huggingface/transformers": "^4.2.0",
|
|
45
|
+
"@mlc-ai/web-llm": "^0.2.83",
|
|
46
|
+
"@ricky0123/vad-web": "^0.0.30",
|
|
47
|
+
ai: "^6.0.180",
|
|
48
|
+
"highlight.js": "^11.11.1",
|
|
49
|
+
"onnxruntime-web": "^1.26.0",
|
|
50
|
+
"rehype-highlight": "^7.0.2",
|
|
51
|
+
"remark-gfm": "^4.0.1",
|
|
52
|
+
streamdown: "^2.5.0"
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Build-time dependencies the Spellbook plugin imports directly.
|
|
56
|
+
*
|
|
57
|
+
* `spellbookPlugin.ts` calls `new webpack.NormalModuleReplacementPlugin(...)`
|
|
58
|
+
* to swap the chat tree for the disabled stub, so `webpack` must be
|
|
59
|
+
* resolvable both at type-check time (`tsc --noEmit`) and when Docusaurus
|
|
60
|
+
* loads the plugin. Strict pnpm node_modules layouts only expose packages
|
|
61
|
+
* that are direct dependencies, so we must declare it here even though
|
|
62
|
+
* `@docusaurus/core` already pulls it in transitively.
|
|
63
|
+
*
|
|
64
|
+
* Pinned to `^5.95.0` to match `@docusaurus/core@3.10.x`'s declared range.
|
|
65
|
+
*/
|
|
66
|
+
export const SPELLBOOK_DEV_DEPENDENCIES = {
|
|
67
|
+
webpack: "^5.95.0"
|
|
68
|
+
};
|
package/dist/layer.d.ts
ADDED
package/dist/layer.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SiteGenerator } from "@kpritam/grimoire-core";
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
|
+
import { scaffoldUpstream } from "./upstream.js";
|
|
4
|
+
export const DocusaurusLayer = Layer.succeed(SiteGenerator, {
|
|
5
|
+
scaffold: (opts) => scaffoldUpstream(opts).pipe(Effect.withSpan("grimoire.site.docusaurus.upstream"))
|
|
6
|
+
});
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PackageJsonPatches } from "@kpritam/grimoire-core/services/SiteScaffold";
|
|
2
|
+
export declare const genDocusaurusConfig: (opts: {
|
|
3
|
+
readonly siteTitle: string;
|
|
4
|
+
readonly tagline: string;
|
|
5
|
+
readonly mermaid: boolean;
|
|
6
|
+
readonly repo?: string | undefined;
|
|
7
|
+
readonly spellbook?: boolean | undefined;
|
|
8
|
+
}) => string;
|
|
9
|
+
export declare const docusaurusPackagePatches: (mermaid: boolean, spellbook: boolean) => PackageJsonPatches;
|
|
10
|
+
export declare const genSidebarsTs: () => string;
|
package/dist/shared.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { genDocusaurusConfig as genDocusaurusConfigInner } from "./internal/docusaurusConfig.js";
|
|
2
|
+
import { SPELLBOOK_DEPENDENCIES, SPELLBOOK_DEV_DEPENDENCIES } from "./internal/spellbookAssets.js";
|
|
3
|
+
export const genDocusaurusConfig = genDocusaurusConfigInner;
|
|
4
|
+
export const docusaurusPackagePatches = (mermaid, spellbook) => ({
|
|
5
|
+
private: true,
|
|
6
|
+
scripts: {
|
|
7
|
+
start: "CHOKIDAR_USEPOLLING=true WATCHPACK_POLLING=true docusaurus start",
|
|
8
|
+
serve: "CHOKIDAR_USEPOLLING=true WATCHPACK_POLLING=true docusaurus serve",
|
|
9
|
+
typecheck: "tsc --noEmit"
|
|
10
|
+
},
|
|
11
|
+
dependencies: {
|
|
12
|
+
...(mermaid
|
|
13
|
+
? {
|
|
14
|
+
"@docusaurus/theme-mermaid": "^3.10.0",
|
|
15
|
+
"@mermaid-js/layout-elk": "^0.1.9"
|
|
16
|
+
}
|
|
17
|
+
: {}),
|
|
18
|
+
...(spellbook ? SPELLBOOK_DEPENDENCIES : {})
|
|
19
|
+
},
|
|
20
|
+
devDependencies: spellbook ? SPELLBOOK_DEV_DEPENDENCIES : {}
|
|
21
|
+
});
|
|
22
|
+
export const genSidebarsTs = () => {
|
|
23
|
+
return `import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
|
|
24
|
+
|
|
25
|
+
const sidebars: SidebarsConfig = {
|
|
26
|
+
grimoire: [
|
|
27
|
+
{
|
|
28
|
+
type: "autogenerated",
|
|
29
|
+
dirName: ".",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default sidebars;
|
|
35
|
+
`;
|
|
36
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ScaffoldOptions } from "@kpritam/grimoire-core";
|
|
2
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
3
|
+
/** Spawns `npx create-docusaurus`, applies Grimoire fix-up; skips full scaffold when `site/package.json` exists. */
|
|
4
|
+
export declare const scaffoldUpstream: (opts: ScaffoldOptions) => Effect.Effect<{
|
|
5
|
+
filesWritten: string[];
|
|
6
|
+
}, import("@kpritam/grimoire-core").OutputWriteError, FileSystem.FileSystem | Path.Path | import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
|
package/dist/upstream.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { detectPackageManager, diagramEngineUsesMermaid, outputWriteError, partialScaffoldError } from "@kpritam/grimoire-core";
|
|
2
|
+
import { removeFiles, siteScaffoldPhases } from "@kpritam/grimoire-core/services/SiteScaffold";
|
|
3
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
4
|
+
import { DOCUSAURUS_PLACEHOLDER_ASSETS, DOCUSAURUS_SAMPLE_RELPATHS } from "./internal/assets.js";
|
|
5
|
+
import { writeSpellbookAssets } from "./internal/spellbookAssets.js";
|
|
6
|
+
import { docusaurusPackagePatches, genDocusaurusConfig, genSidebarsTs } from "./shared.js";
|
|
7
|
+
/** Spawns `npx create-docusaurus`, applies Grimoire fix-up; skips full scaffold when `site/package.json` exists. */
|
|
8
|
+
export const scaffoldUpstream = (opts) => Effect.gen(function* () {
|
|
9
|
+
const fs = yield* FileSystem.FileSystem;
|
|
10
|
+
const pathApi = yield* Path.Path;
|
|
11
|
+
const siteTitle = opts.siteTitle ?? "Grimoire";
|
|
12
|
+
const tagline = opts.tagline ?? "Documentation generated by Grimoire";
|
|
13
|
+
const mermaidOn = diagramEngineUsesMermaid(opts.diagramEngine);
|
|
14
|
+
const spellbookOn = opts.spellbook === true;
|
|
15
|
+
const logStream = opts.logStream === true;
|
|
16
|
+
yield* fs
|
|
17
|
+
.makeDirectory(opts.tomeDir, { recursive: true })
|
|
18
|
+
.pipe(Effect.mapError((e) => outputWriteError(opts.tomeDir, e)));
|
|
19
|
+
const written = [];
|
|
20
|
+
const pkgPath = pathApi.join(opts.siteDir, "package.json");
|
|
21
|
+
if (yield* siteScaffoldPhases.existing(opts.siteDir, pkgPath)) {
|
|
22
|
+
for (const p of yield* siteScaffoldPhases.writePlaceholderAssets(opts.siteDir, DOCUSAURUS_PLACEHOLDER_ASSETS)) {
|
|
23
|
+
written.push(p);
|
|
24
|
+
}
|
|
25
|
+
if (spellbookOn) {
|
|
26
|
+
yield* siteScaffoldPhases.patchPackageJson(pkgPath, docusaurusPackagePatches(mermaidOn, spellbookOn));
|
|
27
|
+
written.push(pkgPath);
|
|
28
|
+
for (const p of yield* writeSpellbookAssets(fs, pathApi, opts.siteDir)) {
|
|
29
|
+
written.push(p);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { filesWritten: written };
|
|
33
|
+
}
|
|
34
|
+
const siteDirExists = yield* fs.exists(opts.siteDir).pipe(Effect.orElseSucceed(() => false));
|
|
35
|
+
if (siteDirExists) {
|
|
36
|
+
return yield* Effect.fail(partialScaffoldError({ siteDir: opts.siteDir, pkgPath }));
|
|
37
|
+
}
|
|
38
|
+
const pm = yield* detectPackageManager(fs, pathApi, opts.root);
|
|
39
|
+
yield* siteScaffoldPhases.spawnUpstream({
|
|
40
|
+
binary: "npx",
|
|
41
|
+
args: [
|
|
42
|
+
"--yes",
|
|
43
|
+
"create-docusaurus@^3.10",
|
|
44
|
+
pathApi.basename(opts.siteDir),
|
|
45
|
+
"classic",
|
|
46
|
+
"--typescript",
|
|
47
|
+
"--skip-install",
|
|
48
|
+
"--package-manager",
|
|
49
|
+
pm
|
|
50
|
+
],
|
|
51
|
+
cwd: opts.tomeDir,
|
|
52
|
+
label: "docusaurus",
|
|
53
|
+
logStream
|
|
54
|
+
});
|
|
55
|
+
for (const p of yield* removeFiles(opts.siteDir, DOCUSAURUS_SAMPLE_RELPATHS)) {
|
|
56
|
+
written.push(`-${p}`);
|
|
57
|
+
}
|
|
58
|
+
yield* siteScaffoldPhases.patchPackageJson(pkgPath, docusaurusPackagePatches(mermaidOn, spellbookOn));
|
|
59
|
+
written.push(pkgPath);
|
|
60
|
+
const docusaurusConfigPath = pathApi.join(opts.siteDir, "docusaurus.config.ts");
|
|
61
|
+
yield* siteScaffoldPhases.writeFrameworkConfig(docusaurusConfigPath, genDocusaurusConfig({
|
|
62
|
+
siteTitle,
|
|
63
|
+
tagline,
|
|
64
|
+
mermaid: mermaidOn,
|
|
65
|
+
spellbook: spellbookOn,
|
|
66
|
+
...(opts.siteRepo !== undefined ? { repo: opts.siteRepo } : {})
|
|
67
|
+
}));
|
|
68
|
+
written.push(docusaurusConfigPath);
|
|
69
|
+
for (const p of yield* removeFiles(opts.siteDir, ["docusaurus.config.js", "sidebars.js"])) {
|
|
70
|
+
written.push(`-${p}`);
|
|
71
|
+
}
|
|
72
|
+
const sidebarsPath = pathApi.join(opts.siteDir, "sidebars.ts");
|
|
73
|
+
yield* siteScaffoldPhases.writeFrameworkConfig(sidebarsPath, genSidebarsTs());
|
|
74
|
+
written.push(sidebarsPath);
|
|
75
|
+
for (const p of yield* siteScaffoldPhases.writePlaceholderAssets(opts.siteDir, DOCUSAURUS_PLACEHOLDER_ASSETS)) {
|
|
76
|
+
written.push(p);
|
|
77
|
+
}
|
|
78
|
+
if (spellbookOn) {
|
|
79
|
+
for (const p of yield* writeSpellbookAssets(fs, pathApi, opts.siteDir)) {
|
|
80
|
+
written.push(p);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { filesWritten: written };
|
|
84
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kpritam/grimoire-output-docusaurus",
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"description": "Grimoire site generator that emits a Docusaurus-compatible site from generated docs.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/kpritam/grimoire/tree/main/packages/output/docusaurus#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/kpritam/grimoire.git",
|
|
10
|
+
"directory": "packages/output/docusaurus"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/kpritam/grimoire/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"grimoire",
|
|
17
|
+
"output",
|
|
18
|
+
"docusaurus",
|
|
19
|
+
"documentation",
|
|
20
|
+
"static-site"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"module": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"src",
|
|
36
|
+
"templates"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=22"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"effect": "4.0.0-beta.51",
|
|
46
|
+
"@kpritam/grimoire-core": "0.1.8"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^25.7.0",
|
|
50
|
+
"tsdown": "^0.21.10",
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"sync-templates": "node scripts/sync-spellbook-templates.mjs",
|
|
55
|
+
"build": "node scripts/sync-spellbook-templates.mjs && rm -rf dist && tsc -p tsconfig.json --emitDeclarationOnly false --incremental false --composite false --sourceMap false",
|
|
56
|
+
"check": "tsc -p tsconfig.json --noEmit --emitDeclarationOnly false --incremental false --composite false --sourceMap false",
|
|
57
|
+
"clean": "rm -rf dist"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DocusaurusLayer } from "./layer.js"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { OutputWriteError } from "@kpritam/grimoire-core"
|
|
2
|
+
import type { PlaceholderAsset } from "@kpritam/grimoire-core/services/SiteScaffold"
|
|
3
|
+
import { removeFiles } from "@kpritam/grimoire-core/services/SiteScaffold"
|
|
4
|
+
import { Effect, FileSystem, Path } from "effect"
|
|
5
|
+
|
|
6
|
+
export const PLACEHOLDER_CSS = `/* Replaced by the Grimoire agent during cast/sync with brand-aware styles. */
|
|
7
|
+
:root {
|
|
8
|
+
color-scheme: light dark;
|
|
9
|
+
}
|
|
10
|
+
`
|
|
11
|
+
|
|
12
|
+
export const PLACEHOLDER_LOGO_SVG = `<?xml version="1.0" encoding="UTF-8"?>
|
|
13
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none" role="img" aria-label="Grimoire">
|
|
14
|
+
<title>Grimoire</title>
|
|
15
|
+
<g fill="#4a2f1a">
|
|
16
|
+
<rect x="11" y="16" width="3.5" height="40"/>
|
|
17
|
+
<rect x="18" y="12" width="3.5" height="44"/>
|
|
18
|
+
<rect x="25" y="18" width="3.5" height="38"/>
|
|
19
|
+
<rect x="32" y="10" width="3.5" height="46"/>
|
|
20
|
+
<rect x="39" y="14" width="3.5" height="42"/>
|
|
21
|
+
</g>
|
|
22
|
+
<circle cx="51" cy="16" r="6" fill="#b9532a"/>
|
|
23
|
+
</svg>
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
export const PLACEHOLDER_LOGO_SVG_DARK = `<?xml version="1.0" encoding="UTF-8"?>
|
|
27
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none" role="img" aria-label="Grimoire">
|
|
28
|
+
<title>Grimoire</title>
|
|
29
|
+
<g fill="#e8d6b8">
|
|
30
|
+
<rect x="11" y="16" width="3.5" height="40"/>
|
|
31
|
+
<rect x="18" y="12" width="3.5" height="44"/>
|
|
32
|
+
<rect x="25" y="18" width="3.5" height="38"/>
|
|
33
|
+
<rect x="32" y="10" width="3.5" height="46"/>
|
|
34
|
+
<rect x="39" y="14" width="3.5" height="42"/>
|
|
35
|
+
</g>
|
|
36
|
+
<circle cx="51" cy="16" r="6" fill="#e1a06b"/>
|
|
37
|
+
</svg>
|
|
38
|
+
`
|
|
39
|
+
|
|
40
|
+
export const DOCUSAURUS_PLACEHOLDER_ASSETS: ReadonlyArray<PlaceholderAsset> = [
|
|
41
|
+
{ relPath: "src/css/custom.css", content: PLACEHOLDER_CSS },
|
|
42
|
+
{ relPath: "static/img/logo.svg", content: PLACEHOLDER_LOGO_SVG },
|
|
43
|
+
{ relPath: "static/img/logo-dark.svg", content: PLACEHOLDER_LOGO_SVG_DARK }
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
export const DOCUSAURUS_SAMPLE_RELPATHS: ReadonlyArray<string> = [
|
|
47
|
+
"blog",
|
|
48
|
+
"docs",
|
|
49
|
+
"src/pages/index.tsx",
|
|
50
|
+
"src/pages/index.jsx",
|
|
51
|
+
"src/pages/index.js",
|
|
52
|
+
"src/pages/markdown-page.md",
|
|
53
|
+
"src/pages/markdown-page.mdx",
|
|
54
|
+
"src/pages/index.module.css",
|
|
55
|
+
"src/components"
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
export const removeDocusaurusSamples = (
|
|
59
|
+
fs: FileSystem.FileSystem,
|
|
60
|
+
pathApi: Path.Path,
|
|
61
|
+
siteDir: string
|
|
62
|
+
): Effect.Effect<ReadonlyArray<string>, OutputWriteError> =>
|
|
63
|
+
removeFiles(siteDir, DOCUSAURUS_SAMPLE_RELPATHS).pipe(
|
|
64
|
+
Effect.provideService(FileSystem.FileSystem, fs),
|
|
65
|
+
Effect.provideService(Path.Path, pathApi)
|
|
66
|
+
)
|