openmanual 0.8.2 → 0.10.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/dist/bin.js +172 -23
- package/dist/bin.js.map +1 -1
- package/dist/components/callout.d.ts +42 -0
- package/dist/components/callout.js +61 -0
- package/dist/components/mermaid.js +194 -7
- package/dist/components/provider.js +7 -1
- package/dist/components/safe-search-dialog.d.ts +30 -0
- package/dist/components/safe-search-dialog.js +77 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/restructure-tree.d.ts +3 -1
- package/dist/utils/restructure-tree.js +24 -4
- package/package.json +6 -3
package/dist/bin.js
CHANGED
|
@@ -13,6 +13,7 @@ const LogoSchema = z.union([z.string(), z.object({
|
|
|
13
13
|
light: z.string(),
|
|
14
14
|
dark: z.string()
|
|
15
15
|
})]);
|
|
16
|
+
const FaviconSchema = z.string();
|
|
16
17
|
const NavbarSchema = z.object({
|
|
17
18
|
logo: LogoSchema.optional(),
|
|
18
19
|
github: z.url().optional(),
|
|
@@ -48,6 +49,7 @@ const OpenManualConfigSchema = z.object({
|
|
|
48
49
|
siteUrl: z.url().optional(),
|
|
49
50
|
locale: z.string().optional(),
|
|
50
51
|
contentPolicy: z.enum(["strict", "all"]).optional(),
|
|
52
|
+
favicon: FaviconSchema.optional(),
|
|
51
53
|
navbar: NavbarSchema.optional(),
|
|
52
54
|
footer: FooterSchema.optional(),
|
|
53
55
|
sidebar: z.array(SidebarGroupSchema).optional(),
|
|
@@ -140,12 +142,21 @@ function mergeDefaults(config) {
|
|
|
140
142
|
};
|
|
141
143
|
}
|
|
142
144
|
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/core/generator/callout-component.ts
|
|
147
|
+
function generateCalloutComponent() {
|
|
148
|
+
return `'use client';
|
|
149
|
+
export { Callout, CalloutTitle, CalloutDescription } from 'openmanual/components/callout';
|
|
150
|
+
`;
|
|
151
|
+
}
|
|
152
|
+
|
|
143
153
|
//#endregion
|
|
144
154
|
//#region src/core/generator/global-css.ts
|
|
145
155
|
function generateGlobalCss(ctx) {
|
|
146
156
|
const { config } = ctx;
|
|
147
157
|
const primaryHue = config.theme?.primaryHue ?? 213;
|
|
148
158
|
return `@import 'tailwindcss';
|
|
159
|
+
@source './node_modules/openmanual/dist/components/**/*.js';
|
|
149
160
|
@import 'fumadocs-ui/style.css';
|
|
150
161
|
@custom-variant dark (&:is(.dark, .dark *));
|
|
151
162
|
|
|
@@ -158,6 +169,29 @@ function generateGlobalCss(ctx) {
|
|
|
158
169
|
--color-fd-muted: hsl(40, 15%, 95%); /* 柔和的暖灰背景 */
|
|
159
170
|
--color-fd-card: hsl(40, 18%, 94%); /* 卡片背景 */
|
|
160
171
|
--color-fd-popover: hsl(40, 20%, 97.5%); /* 弹窗背景 */
|
|
172
|
+
|
|
173
|
+
/* Callout 类型色 */
|
|
174
|
+
--callout-info-bg: hsl(210, 35%, 94%);
|
|
175
|
+
--callout-info-border: hsl(212, 40%, 80%);
|
|
176
|
+
--callout-info-text: hsl(213, 45%, 35%);
|
|
177
|
+
--callout-warning-bg: hsl(38, 60%, 93%);
|
|
178
|
+
--callout-warning-border: hsl(36, 55%, 78%);
|
|
179
|
+
--callout-warning-text: hsl(28, 55%, 35%);
|
|
180
|
+
--callout-danger-bg: hsl(0, 50%, 94%);
|
|
181
|
+
--callout-danger-border: hsl(0, 45%, 82%);
|
|
182
|
+
--callout-danger-text: hsl(0, 50%, 38%);
|
|
183
|
+
--callout-check-bg: hsl(150, 35%, 93%);
|
|
184
|
+
--callout-check-border: hsl(152, 35%, 78%);
|
|
185
|
+
--callout-check-text: hsl(155, 40%, 32%);
|
|
186
|
+
--callout-tip-bg: hsl(150, 35%, 93%);
|
|
187
|
+
--callout-tip-border: hsl(152, 35%, 78%);
|
|
188
|
+
--callout-tip-text: hsl(155, 40%, 32%);
|
|
189
|
+
--callout-note-bg: hsl(215, 20%, 94%);
|
|
190
|
+
--callout-note-border: hsl(215, 22%, 82%);
|
|
191
|
+
--callout-note-text: hsl(215, 25%, 40%);
|
|
192
|
+
--callout-key-bg: hsl(30, 55%, 93%);
|
|
193
|
+
--callout-key-border: hsl(28, 50%, 78%);
|
|
194
|
+
--callout-key-text: hsl(25, 50%, 35%);
|
|
161
195
|
}
|
|
162
196
|
${config.theme?.darkMode ?? true ? `
|
|
163
197
|
.dark {
|
|
@@ -181,6 +215,29 @@ ${config.theme?.darkMode ?? true ? `
|
|
|
181
215
|
--color-fd-accent-foreground: hsl(35, 12%, 88%);
|
|
182
216
|
--color-fd-ring: hsl(30, 30%, 50%);
|
|
183
217
|
--color-fd-overlay: hsla(25, 20%, 5%, 0.5);
|
|
218
|
+
|
|
219
|
+
/* Callout 类型色 */
|
|
220
|
+
--callout-info-bg: hsl(213, 25%, 16%);
|
|
221
|
+
--callout-info-border: hsl(213, 30%, 30%);
|
|
222
|
+
--callout-info-text: hsl(213, 40%, 72%);
|
|
223
|
+
--callout-warning-bg: hsl(32, 35%, 17%);
|
|
224
|
+
--callout-warning-border: hsl(30, 35%, 30%);
|
|
225
|
+
--callout-warning-text: hsl(35, 45%, 72%);
|
|
226
|
+
--callout-danger-bg: hsl(5, 25%, 17%);
|
|
227
|
+
--callout-danger-border: hsl(5, 30%, 30%);
|
|
228
|
+
--callout-danger-text: hsl(5, 40%, 72%);
|
|
229
|
+
--callout-check-bg: hsl(155, 22%, 16%);
|
|
230
|
+
--callout-check-border: hsl(155, 25%, 28%);
|
|
231
|
+
--callout-check-text: hsl(155, 35%, 68%);
|
|
232
|
+
--callout-tip-bg: hsl(155, 22%, 16%);
|
|
233
|
+
--callout-tip-border: hsl(155, 25%, 28%);
|
|
234
|
+
--callout-tip-text: hsl(155, 35%, 68%);
|
|
235
|
+
--callout-note-bg: hsl(215, 15%, 16%);
|
|
236
|
+
--callout-note-border: hsl(215, 18%, 28%);
|
|
237
|
+
--callout-note-text: hsl(215, 25%, 68%);
|
|
238
|
+
--callout-key-bg: hsl(30, 28%, 17%);
|
|
239
|
+
--callout-key-border: hsl(28, 25%, 30%);
|
|
240
|
+
--callout-key-text: hsl(25, 35%, 68%);
|
|
184
241
|
}
|
|
185
242
|
|
|
186
243
|
.dark body {
|
|
@@ -196,6 +253,21 @@ figure.shiki {
|
|
|
196
253
|
figure.shiki > div {
|
|
197
254
|
max-height: none;
|
|
198
255
|
}
|
|
256
|
+
|
|
257
|
+
/* Mermaid 全屏操作栏按钮 hover */
|
|
258
|
+
.mermaid-toolbar-btn:hover {
|
|
259
|
+
background-color: var(--hover-bg) !important;
|
|
260
|
+
color: var(--hover-color) !important;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.mermaid-toolbar-btn:hover svg {
|
|
264
|
+
color: inherit;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* Callout:去除 shadow */
|
|
268
|
+
[style*="--callout-color"] {
|
|
269
|
+
box-shadow: none;
|
|
270
|
+
}
|
|
199
271
|
`;
|
|
200
272
|
}
|
|
201
273
|
|
|
@@ -292,7 +364,7 @@ export default withMDX(config);
|
|
|
292
364
|
//#endregion
|
|
293
365
|
//#region src/core/generator/package-json.ts
|
|
294
366
|
function getOpenManualVersion() {
|
|
295
|
-
return "0.
|
|
367
|
+
return "0.10.0";
|
|
296
368
|
}
|
|
297
369
|
function generatePackageJson(ctx) {
|
|
298
370
|
const openmanualVersion = getOpenManualVersion();
|
|
@@ -313,6 +385,7 @@ function generatePackageJson(ctx) {
|
|
|
313
385
|
"fumadocs-core": "^16.7.7",
|
|
314
386
|
"fumadocs-mdx": "^14.2.11",
|
|
315
387
|
"fumadocs-ui": "^16.7.7",
|
|
388
|
+
"lucide-react": "^1.7.0",
|
|
316
389
|
mermaid: "^11.4.0",
|
|
317
390
|
next: "^16.2.1",
|
|
318
391
|
"next-themes": "^0.4.6",
|
|
@@ -353,7 +426,8 @@ import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
|
|
|
353
426
|
import { Files, File, Folder } from 'fumadocs-ui/components/files';
|
|
354
427
|
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
|
355
428
|
import { TypeTable } from 'fumadocs-ui/components/type-table';
|
|
356
|
-
import { Mermaid } from '@/components/mermaid'
|
|
429
|
+
import { Mermaid } from '@/components/mermaid';
|
|
430
|
+
import { Callout, CalloutTitle, CalloutDescription } from '@/components/callout';${pageActionsEnabled ? "\nimport { PageActions } from '@/components/page-actions';" : ""}
|
|
357
431
|
${allowedSlugsSnippet}
|
|
358
432
|
export default async function Page({ params }: { params: Promise<{ slug?: string[] }> }) {
|
|
359
433
|
const { slug } = await params;
|
|
@@ -384,7 +458,7 @@ ${pageActionsEnabled ? ` <div className="flex items-start justify-between g
|
|
|
384
458
|
<DocsDescription>{page.data.description}</DocsDescription>
|
|
385
459
|
)}`}
|
|
386
460
|
<DocsBody data-content-area>
|
|
387
|
-
<MDX components={{ ...defaultMdxComponents, Steps, Step, Tabs, Tab, Files, File, Folder, Accordion, Accordions, TypeTable, Mermaid }} />
|
|
461
|
+
<MDX components={{ ...defaultMdxComponents, Steps, Step, Tabs, Tab, Files, File, Folder, Accordion, Accordions, TypeTable, Mermaid, Callout, CalloutTitle, CalloutDescription }} />
|
|
388
462
|
</DocsBody>
|
|
389
463
|
</DocsPage>
|
|
390
464
|
);
|
|
@@ -438,7 +512,13 @@ import { Provider } from 'openmanual/components/provider';
|
|
|
438
512
|
import type { ReactNode } from 'react';
|
|
439
513
|
|
|
440
514
|
export function AppProvider({ children }: { children: ReactNode }) {
|
|
441
|
-
return
|
|
515
|
+
return (
|
|
516
|
+
<Provider
|
|
517
|
+
searchEnabled={${ctx.config.search?.enabled !== false}}
|
|
518
|
+
>
|
|
519
|
+
{children}
|
|
520
|
+
</Provider>
|
|
521
|
+
);
|
|
442
522
|
}
|
|
443
523
|
`;
|
|
444
524
|
}
|
|
@@ -472,6 +552,17 @@ export async function GET(
|
|
|
472
552
|
`;
|
|
473
553
|
}
|
|
474
554
|
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region src/core/generator/search-route.ts
|
|
557
|
+
function generateSearchRoute() {
|
|
558
|
+
return `import { source } from '@/lib/source';
|
|
559
|
+
import { createFromSource } from 'fumadocs-core/search/server';
|
|
560
|
+
|
|
561
|
+
export const revalidate = false;
|
|
562
|
+
export const { staticGET: GET } = createFromSource(source);
|
|
563
|
+
`;
|
|
564
|
+
}
|
|
565
|
+
|
|
475
566
|
//#endregion
|
|
476
567
|
//#region src/core/generator/source-config.ts
|
|
477
568
|
function generateSourceConfig(_ctx) {
|
|
@@ -613,6 +704,10 @@ async function generateAll(ctx) {
|
|
|
613
704
|
path: "lib/layout.tsx",
|
|
614
705
|
content: generateLayout(ctx)
|
|
615
706
|
},
|
|
707
|
+
{
|
|
708
|
+
path: "components/callout.tsx",
|
|
709
|
+
content: generateCalloutComponent()
|
|
710
|
+
},
|
|
616
711
|
{
|
|
617
712
|
path: "components/mermaid.tsx",
|
|
618
713
|
content: generateMermaidComponent()
|
|
@@ -624,10 +719,16 @@ async function generateAll(ctx) {
|
|
|
624
719
|
...ctx.dev ? [{
|
|
625
720
|
path: "app/api/raw/[...path]/route.ts",
|
|
626
721
|
content: generateRawContentRoute()
|
|
627
|
-
}
|
|
722
|
+
}, {
|
|
723
|
+
path: "app/api/search/route.ts",
|
|
724
|
+
content: generateSearchRoute()
|
|
725
|
+
}] : [{
|
|
726
|
+
path: "app/api/search/route.ts",
|
|
727
|
+
content: generateSearchRoute()
|
|
728
|
+
}],
|
|
628
729
|
{
|
|
629
730
|
path: "app/layout.tsx",
|
|
630
|
-
content: generateRootLayout()
|
|
731
|
+
content: generateRootLayout(ctx)
|
|
631
732
|
},
|
|
632
733
|
{
|
|
633
734
|
path: "app/provider.tsx",
|
|
@@ -656,11 +757,21 @@ async function generateAll(ctx) {
|
|
|
656
757
|
}
|
|
657
758
|
await generateMetaFiles(ctx);
|
|
658
759
|
}
|
|
659
|
-
function generateRootLayout() {
|
|
760
|
+
function generateRootLayout(ctx) {
|
|
761
|
+
const { config } = ctx;
|
|
762
|
+
const favicon = config.favicon;
|
|
660
763
|
return `import { AppLayout } from 'openmanual/components/app-layout';
|
|
661
764
|
import { AppProvider } from './provider';
|
|
662
765
|
import type { ReactNode } from 'react';
|
|
663
|
-
import '
|
|
766
|
+
${favicon ? `import type { Metadata } from 'next';
|
|
767
|
+
|
|
768
|
+
export const metadata: Metadata = {
|
|
769
|
+
icons: {
|
|
770
|
+
icon: '${favicon}',
|
|
771
|
+
},
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
` : ""}import '../global.css';
|
|
664
775
|
|
|
665
776
|
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
666
777
|
return (
|
|
@@ -686,20 +797,34 @@ function generateDocsLayout(ctx) {
|
|
|
686
797
|
const footerLine = footerText ? `\n footer: { children: '${footerText.replace(/'/g, "\\'")}' },` : "";
|
|
687
798
|
const sidebar = config.sidebar;
|
|
688
799
|
const hasSidebar = sidebar && sidebar.length > 0;
|
|
800
|
+
const iconNames = /* @__PURE__ */ new Set();
|
|
801
|
+
if (hasSidebar) for (const g of sidebar ?? []) {
|
|
802
|
+
if (g.icon) iconNames.add(g.icon);
|
|
803
|
+
for (const p of g.pages) if (p.icon) iconNames.add(p.icon);
|
|
804
|
+
}
|
|
805
|
+
const hasIcons = iconNames.size > 0;
|
|
806
|
+
const iconNameList = [...iconNames];
|
|
689
807
|
const sidebarConfigSnippet = hasSidebar ? `\nconst sidebarConfig = ${JSON.stringify((sidebar ?? []).map((g) => ({
|
|
690
808
|
group: g.group,
|
|
809
|
+
icon: g.icon,
|
|
691
810
|
collapsed: g.collapsed,
|
|
692
|
-
pages: g.pages.map((p) => ({
|
|
811
|
+
pages: g.pages.map((p) => ({
|
|
812
|
+
slug: p.slug,
|
|
813
|
+
icon: p.icon
|
|
814
|
+
}))
|
|
693
815
|
})), null, 2)} as const;
|
|
816
|
+
` : "";
|
|
817
|
+
const lucideImportLine = hasIcons ? `\nimport { ${iconNameList.join(", ")} } from 'lucide-react';` : "";
|
|
818
|
+
const iconMapSnippet = hasIcons ? `\nconst iconMap = {${iconNameList.map((name) => `\n ${name}: <${name} />,`).join("")}\n} as const;
|
|
694
819
|
` : "";
|
|
695
820
|
return `import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
|
696
821
|
import { baseOptions } from '@/lib/layout';
|
|
697
822
|
import { source } from '@/lib/source';
|
|
698
|
-
import type { ReactNode } from 'react';${hasSidebar ? "\nimport { restructureTree } from 'openmanual/utils/restructure-tree';" : ""}
|
|
699
|
-
${sidebarConfigSnippet}
|
|
823
|
+
import type { ReactNode } from 'react';${hasSidebar ? "\nimport { restructureTree } from 'openmanual/utils/restructure-tree';" : ""}${lucideImportLine}
|
|
824
|
+
${sidebarConfigSnippet}${iconMapSnippet}
|
|
700
825
|
const docsOptions = {
|
|
701
826
|
...baseOptions(),
|
|
702
|
-
${hasSidebar ? "tree: restructureTree(source.getPageTree(), sidebarConfig)," : "tree: source.getPageTree(),"}${githubLine}${linksLine}${footerLine}
|
|
827
|
+
${hasSidebar ? hasIcons ? "tree: restructureTree(source.getPageTree(), sidebarConfig, iconMap)," : "tree: restructureTree(source.getPageTree(), sidebarConfig)," : "tree: source.getPageTree(),"}${githubLine}${linksLine}${footerLine}
|
|
703
828
|
};
|
|
704
829
|
|
|
705
830
|
export default function DocsLayoutWrapper({ children }: { children: ReactNode }) {
|
|
@@ -986,14 +1111,27 @@ const devCommand = new Command("dev").description("启动开发服务器").optio
|
|
|
986
1111
|
const tempDir = await ensureTempDir(cwd);
|
|
987
1112
|
const appDir = getAppDir(cwd);
|
|
988
1113
|
const contentDir = resolve(cwd, config.contentDir ?? "content");
|
|
989
|
-
|
|
1114
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1115
|
+
const openmanualRoot = process.env.OPENMANUAL_ROOT || resolve(__dirname, "..");
|
|
1116
|
+
const ctx = {
|
|
990
1117
|
config,
|
|
991
1118
|
projectDir: cwd,
|
|
992
1119
|
appDir,
|
|
993
1120
|
contentDir: config.contentDir ?? "content",
|
|
994
1121
|
dev: true,
|
|
995
|
-
|
|
996
|
-
}
|
|
1122
|
+
openmanualRoot
|
|
1123
|
+
};
|
|
1124
|
+
if (process.env.OPENMANUAL_ROOT) await spawnInitialGenerate(openmanualRoot, cwd);
|
|
1125
|
+
else {
|
|
1126
|
+
await generateAll(ctx);
|
|
1127
|
+
await createSymlink(contentDir, resolve(appDir, "content"));
|
|
1128
|
+
const publicDir = resolve(cwd, "public");
|
|
1129
|
+
try {
|
|
1130
|
+
const { stat } = await import("node:fs/promises");
|
|
1131
|
+
await stat(publicDir);
|
|
1132
|
+
await createSymlink(publicDir, resolve(appDir, "public"));
|
|
1133
|
+
} catch {}
|
|
1134
|
+
}
|
|
997
1135
|
try {
|
|
998
1136
|
const unknownLangs = await checkCodeLangs(contentDir);
|
|
999
1137
|
if (unknownLangs.length > 0) {
|
|
@@ -1002,13 +1140,6 @@ const devCommand = new Command("dev").description("启动开发服务器").optio
|
|
|
1002
1140
|
logger.warn("建议将这些语言改为受支持的类型,或使用 \"text\" 作为默认值");
|
|
1003
1141
|
}
|
|
1004
1142
|
} catch {}
|
|
1005
|
-
await createSymlink(contentDir, resolve(appDir, "content"));
|
|
1006
|
-
const publicDir = resolve(cwd, "public");
|
|
1007
|
-
try {
|
|
1008
|
-
const { stat } = await import("node:fs/promises");
|
|
1009
|
-
await stat(publicDir);
|
|
1010
|
-
await createSymlink(publicDir, resolve(appDir, "public"));
|
|
1011
|
-
} catch {}
|
|
1012
1143
|
logger.step("安装依赖...");
|
|
1013
1144
|
await installDeps(appDir);
|
|
1014
1145
|
logger.success("开发服务器启动中...");
|
|
@@ -1080,6 +1211,24 @@ const devCommand = new Command("dev").description("启动开发服务器").optio
|
|
|
1080
1211
|
process.exit(1);
|
|
1081
1212
|
}
|
|
1082
1213
|
});
|
|
1214
|
+
function spawnInitialGenerate(openmanualRoot, cwd) {
|
|
1215
|
+
const child = spawn("node", [
|
|
1216
|
+
resolve(openmanualRoot, "dist/bin.js"),
|
|
1217
|
+
"_regenerate",
|
|
1218
|
+
"--cwd",
|
|
1219
|
+
cwd
|
|
1220
|
+
], {
|
|
1221
|
+
stdio: "inherit",
|
|
1222
|
+
env: { ...process.env }
|
|
1223
|
+
});
|
|
1224
|
+
return new Promise((promiseResolve, promiseReject) => {
|
|
1225
|
+
child.on("exit", (code) => {
|
|
1226
|
+
if (code === 0) promiseResolve();
|
|
1227
|
+
else promiseReject(/* @__PURE__ */ new Error(`初始生成失败 (exit code: ${code})`));
|
|
1228
|
+
});
|
|
1229
|
+
child.on("error", promiseReject);
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1083
1232
|
function spawnRegenerate(openmanualRoot, cwd, nextChild) {
|
|
1084
1233
|
if (nextChild.exitCode !== null) {
|
|
1085
1234
|
logger.warn("Next.js 进程已退出,跳过重新生成");
|
|
@@ -1192,7 +1341,7 @@ const regenerateCommand = new Command("_regenerate").description("内部命令
|
|
|
1192
1341
|
//#endregion
|
|
1193
1342
|
//#region src/cli/bin.ts
|
|
1194
1343
|
function getVersion() {
|
|
1195
|
-
return "0.
|
|
1344
|
+
return "0.10.0";
|
|
1196
1345
|
}
|
|
1197
1346
|
const program = new Command();
|
|
1198
1347
|
const commandName = basename(process.argv[1] ?? "openmanual");
|