prev-cli 0.2.1 → 0.3.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/cli.js +105 -31
- package/dist/vite/plugins/fumadocs-plugin.d.ts +9 -0
- package/package.json +5 -2
- package/src/theme/{Layout.tsx → diagrams.tsx} +4 -18
- package/src/theme/entry.tsx +90 -2
- package/src/theme/styles.css +80 -255
- package/dist/theme/App.d.ts +0 -1
- package/dist/theme/Layout.d.ts +0 -2
- package/dist/theme/Sidebar.d.ts +0 -1
- package/dist/theme/entry.d.ts +0 -1
- package/src/theme/App.tsx +0 -33
- package/src/theme/Sidebar.tsx +0 -75
package/dist/cli.js
CHANGED
|
@@ -9,13 +9,12 @@ import { createServer as createServer2, build, preview } from "vite";
|
|
|
9
9
|
// src/vite/config.ts
|
|
10
10
|
import { createLogger } from "vite";
|
|
11
11
|
import react from "@vitejs/plugin-react-swc";
|
|
12
|
-
import tailwindcss from "@tailwindcss/vite";
|
|
13
12
|
import mdx from "@mdx-js/rollup";
|
|
14
13
|
import remarkGfm from "remark-gfm";
|
|
15
14
|
import rehypeHighlight from "rehype-highlight";
|
|
16
|
-
import
|
|
15
|
+
import path5 from "path";
|
|
17
16
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18
|
-
import { existsSync as
|
|
17
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
19
18
|
|
|
20
19
|
// src/utils/cache.ts
|
|
21
20
|
import { createHash } from "crypto";
|
|
@@ -269,6 +268,72 @@ function entryPlugin(rootDir) {
|
|
|
269
268
|
};
|
|
270
269
|
}
|
|
271
270
|
|
|
271
|
+
// src/vite/plugins/fumadocs-plugin.ts
|
|
272
|
+
import path4 from "path";
|
|
273
|
+
import { existsSync as existsSync2 } from "fs";
|
|
274
|
+
function fumadocsPlugin(nodeModulesPath) {
|
|
275
|
+
const fumadocsCorePath = path4.join(nodeModulesPath, "fumadocs-core");
|
|
276
|
+
const fumadocsUiPath = path4.join(nodeModulesPath, "fumadocs-ui");
|
|
277
|
+
const fumadocsUiScopedPath = path4.join(nodeModulesPath, "@fumadocs/ui");
|
|
278
|
+
function resolveSubpath(pkgPath, subpath) {
|
|
279
|
+
const directPath = path4.join(pkgPath, "dist", `${subpath}.js`);
|
|
280
|
+
if (existsSync2(directPath)) {
|
|
281
|
+
return directPath;
|
|
282
|
+
}
|
|
283
|
+
const indexPath = path4.join(pkgPath, "dist", subpath, "index.js");
|
|
284
|
+
if (existsSync2(indexPath)) {
|
|
285
|
+
return indexPath;
|
|
286
|
+
}
|
|
287
|
+
const rawPath = path4.join(pkgPath, "dist", subpath);
|
|
288
|
+
if (existsSync2(rawPath)) {
|
|
289
|
+
return rawPath;
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
name: "fumadocs-resolver",
|
|
295
|
+
enforce: "pre",
|
|
296
|
+
resolveId(id, importer) {
|
|
297
|
+
if (importer && (id.startsWith("./") || id.startsWith("../"))) {
|
|
298
|
+
if (importer.includes("fumadocs-core") || importer.includes("fumadocs-ui") || importer.includes("@fumadocs")) {
|
|
299
|
+
const importerDir = path4.dirname(importer);
|
|
300
|
+
const resolved = path4.resolve(importerDir, id);
|
|
301
|
+
const withExt = resolved.endsWith(".js") ? resolved : `${resolved}.js`;
|
|
302
|
+
if (existsSync2(withExt)) {
|
|
303
|
+
return withExt;
|
|
304
|
+
}
|
|
305
|
+
const indexPath = path4.join(resolved, "index.js");
|
|
306
|
+
if (existsSync2(indexPath)) {
|
|
307
|
+
return indexPath;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (id.startsWith("fumadocs-core/")) {
|
|
312
|
+
const subpath = id.slice("fumadocs-core/".length);
|
|
313
|
+
const resolved = resolveSubpath(fumadocsCorePath, subpath);
|
|
314
|
+
if (resolved) {
|
|
315
|
+
return resolved;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (id.startsWith("fumadocs-ui/")) {
|
|
319
|
+
const subpath = id.slice("fumadocs-ui/".length);
|
|
320
|
+
const resolved = resolveSubpath(fumadocsUiPath, subpath);
|
|
321
|
+
if (resolved) {
|
|
322
|
+
return resolved;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (id.startsWith("@fumadocs/ui/")) {
|
|
326
|
+
const subpath = id.slice("@fumadocs/ui/".length);
|
|
327
|
+
const resolved = resolveSubpath(fumadocsUiScopedPath, subpath);
|
|
328
|
+
if (resolved) {
|
|
329
|
+
return resolved;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
272
337
|
// src/vite/config.ts
|
|
273
338
|
function createFriendlyLogger() {
|
|
274
339
|
const logger = createLogger("info", { allowClearScreen: false });
|
|
@@ -324,10 +389,10 @@ function createFriendlyLogger() {
|
|
|
324
389
|
};
|
|
325
390
|
}
|
|
326
391
|
function findCliRoot2() {
|
|
327
|
-
let dir =
|
|
392
|
+
let dir = path5.dirname(fileURLToPath2(import.meta.url));
|
|
328
393
|
for (let i = 0;i < 10; i++) {
|
|
329
|
-
const pkgPath =
|
|
330
|
-
if (
|
|
394
|
+
const pkgPath = path5.join(dir, "package.json");
|
|
395
|
+
if (existsSync3(pkgPath)) {
|
|
331
396
|
try {
|
|
332
397
|
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
333
398
|
if (pkg.name === "prev-cli") {
|
|
@@ -335,24 +400,24 @@ function findCliRoot2() {
|
|
|
335
400
|
}
|
|
336
401
|
} catch {}
|
|
337
402
|
}
|
|
338
|
-
const parent =
|
|
403
|
+
const parent = path5.dirname(dir);
|
|
339
404
|
if (parent === dir)
|
|
340
405
|
break;
|
|
341
406
|
dir = parent;
|
|
342
407
|
}
|
|
343
|
-
return
|
|
408
|
+
return path5.dirname(path5.dirname(fileURLToPath2(import.meta.url)));
|
|
344
409
|
}
|
|
345
410
|
function findNodeModules(cliRoot2) {
|
|
346
|
-
const localNodeModules =
|
|
347
|
-
if (
|
|
411
|
+
const localNodeModules = path5.join(cliRoot2, "node_modules");
|
|
412
|
+
if (existsSync3(path5.join(localNodeModules, "react"))) {
|
|
348
413
|
return localNodeModules;
|
|
349
414
|
}
|
|
350
415
|
let dir = cliRoot2;
|
|
351
416
|
for (let i = 0;i < 10; i++) {
|
|
352
|
-
const parent =
|
|
417
|
+
const parent = path5.dirname(dir);
|
|
353
418
|
if (parent === dir)
|
|
354
419
|
break;
|
|
355
|
-
if (
|
|
420
|
+
if (path5.basename(parent) === "node_modules" && existsSync3(path5.join(parent, "react"))) {
|
|
356
421
|
return parent;
|
|
357
422
|
}
|
|
358
423
|
dir = parent;
|
|
@@ -361,7 +426,7 @@ function findNodeModules(cliRoot2) {
|
|
|
361
426
|
}
|
|
362
427
|
var cliRoot2 = findCliRoot2();
|
|
363
428
|
var cliNodeModules = findNodeModules(cliRoot2);
|
|
364
|
-
var srcRoot2 =
|
|
429
|
+
var srcRoot2 = path5.join(cliRoot2, "src");
|
|
365
430
|
async function createViteConfig(options) {
|
|
366
431
|
const { rootDir, mode, port } = options;
|
|
367
432
|
const cacheDir = await ensureCacheDir(rootDir);
|
|
@@ -372,26 +437,34 @@ async function createViteConfig(options) {
|
|
|
372
437
|
customLogger: createFriendlyLogger(),
|
|
373
438
|
logLevel: mode === "production" ? "silent" : "info",
|
|
374
439
|
plugins: [
|
|
440
|
+
fumadocsPlugin(cliNodeModules),
|
|
375
441
|
mdx({
|
|
376
442
|
remarkPlugins: [remarkGfm],
|
|
377
443
|
rehypePlugins: [rehypeHighlight]
|
|
378
444
|
}),
|
|
379
445
|
react(),
|
|
380
|
-
tailwindcss(),
|
|
381
446
|
pagesPlugin(rootDir),
|
|
382
447
|
entryPlugin(rootDir)
|
|
383
448
|
],
|
|
384
449
|
resolve: {
|
|
385
450
|
alias: {
|
|
386
|
-
"@prev/ui":
|
|
387
|
-
"@prev/theme":
|
|
388
|
-
react:
|
|
389
|
-
"react-dom":
|
|
390
|
-
"react-router
|
|
391
|
-
mermaid:
|
|
392
|
-
dayjs:
|
|
393
|
-
"@terrastruct/d2":
|
|
394
|
-
}
|
|
451
|
+
"@prev/ui": path5.join(srcRoot2, "ui"),
|
|
452
|
+
"@prev/theme": path5.join(srcRoot2, "theme"),
|
|
453
|
+
react: path5.join(cliNodeModules, "react"),
|
|
454
|
+
"react-dom": path5.join(cliNodeModules, "react-dom"),
|
|
455
|
+
"@tanstack/react-router": path5.join(cliNodeModules, "@tanstack/react-router"),
|
|
456
|
+
mermaid: path5.join(cliNodeModules, "mermaid"),
|
|
457
|
+
dayjs: path5.join(cliNodeModules, "dayjs"),
|
|
458
|
+
"@terrastruct/d2": path5.join(cliNodeModules, "@terrastruct/d2")
|
|
459
|
+
},
|
|
460
|
+
dedupe: [
|
|
461
|
+
"react",
|
|
462
|
+
"react-dom",
|
|
463
|
+
"@tanstack/react-router",
|
|
464
|
+
"fumadocs-core",
|
|
465
|
+
"fumadocs-ui",
|
|
466
|
+
"@fumadocs/ui"
|
|
467
|
+
]
|
|
395
468
|
},
|
|
396
469
|
optimizeDeps: {
|
|
397
470
|
entries: [],
|
|
@@ -399,17 +472,18 @@ async function createViteConfig(options) {
|
|
|
399
472
|
"react",
|
|
400
473
|
"react-dom",
|
|
401
474
|
"react-dom/client",
|
|
402
|
-
"react-router
|
|
475
|
+
"@tanstack/react-router",
|
|
403
476
|
"react/jsx-runtime",
|
|
404
477
|
"react/jsx-dev-runtime",
|
|
405
478
|
"mermaid",
|
|
406
479
|
"dayjs",
|
|
407
|
-
"@terrastruct/d2"
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
"
|
|
411
|
-
"
|
|
412
|
-
"
|
|
480
|
+
"@terrastruct/d2",
|
|
481
|
+
"fumadocs-core/framework",
|
|
482
|
+
"fumadocs-core/framework/tanstack",
|
|
483
|
+
"fumadocs-core/link",
|
|
484
|
+
"fumadocs-core/breadcrumb",
|
|
485
|
+
"fumadocs-ui/provider/tanstack",
|
|
486
|
+
"fumadocs-ui/layouts/docs"
|
|
413
487
|
]
|
|
414
488
|
},
|
|
415
489
|
ssr: {
|
|
@@ -427,7 +501,7 @@ async function createViteConfig(options) {
|
|
|
427
501
|
strictPort: false
|
|
428
502
|
},
|
|
429
503
|
build: {
|
|
430
|
-
outDir:
|
|
504
|
+
outDir: path5.join(rootDir, "dist"),
|
|
431
505
|
reportCompressedSize: false,
|
|
432
506
|
chunkSizeWarningLimit: 1e4
|
|
433
507
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* Vite plugin to resolve fumadocs subpath exports.
|
|
4
|
+
*
|
|
5
|
+
* Fumadocs uses Node.js subpath exports which Vite's esbuild pre-bundler
|
|
6
|
+
* can't resolve when packages are in CLI's node_modules (not user's project).
|
|
7
|
+
* This plugin uses resolveId hook to dynamically resolve imports to dist files.
|
|
8
|
+
*/
|
|
9
|
+
export declare function fumadocsPlugin(nodeModulesPath: string): Plugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prev-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Transform MDX directories into beautiful documentation websites",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,12 +41,15 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@mdx-js/rollup": "^3.0.0",
|
|
44
|
-
"@terrastruct/d2": "^0.1.33",
|
|
45
44
|
"@tailwindcss/vite": "^4.0.0",
|
|
45
|
+
"@tanstack/react-router": "^1.145.7",
|
|
46
|
+
"@terrastruct/d2": "^0.1.33",
|
|
46
47
|
"@vitejs/plugin-react-swc": "^4.2.2",
|
|
47
48
|
"class-variance-authority": "^0.7.0",
|
|
48
49
|
"clsx": "^2.1.0",
|
|
49
50
|
"fast-glob": "^3.3.0",
|
|
51
|
+
"fumadocs-core": "^16.4.3",
|
|
52
|
+
"fumadocs-ui": "^16.4.3",
|
|
50
53
|
"lucide-react": "^0.460.0",
|
|
51
54
|
"mermaid": "^11.0.0",
|
|
52
55
|
"react": "^19.0.0",
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { Sidebar } from './Sidebar'
|
|
4
|
-
import './styles.css'
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import { useLocation } from '@tanstack/react-router'
|
|
5
3
|
|
|
6
4
|
// Simple hash function for cache keys
|
|
7
5
|
function hashCode(str: string): string {
|
|
@@ -159,7 +157,8 @@ async function renderDiagrams() {
|
|
|
159
157
|
])
|
|
160
158
|
}
|
|
161
159
|
|
|
162
|
-
|
|
160
|
+
// Hook for diagram rendering
|
|
161
|
+
export function useDiagrams() {
|
|
163
162
|
const location = useLocation()
|
|
164
163
|
|
|
165
164
|
useEffect(() => {
|
|
@@ -172,17 +171,4 @@ export function Layout() {
|
|
|
172
171
|
cleanupDiagrams()
|
|
173
172
|
}
|
|
174
173
|
}, [location.pathname])
|
|
175
|
-
|
|
176
|
-
return (
|
|
177
|
-
<div className="min-h-screen bg-background text-foreground">
|
|
178
|
-
<div className="flex">
|
|
179
|
-
<Sidebar />
|
|
180
|
-
<main className="flex-1 p-8 max-w-4xl animate-fade-in">
|
|
181
|
-
<article className="prose max-w-none">
|
|
182
|
-
<Outlet />
|
|
183
|
-
</article>
|
|
184
|
-
</main>
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
)
|
|
188
174
|
}
|
package/src/theme/entry.tsx
CHANGED
|
@@ -1,10 +1,98 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { createRoot } from 'react-dom/client'
|
|
3
|
+
import {
|
|
4
|
+
RouterProvider,
|
|
5
|
+
createRouter,
|
|
6
|
+
createRootRoute,
|
|
7
|
+
createRoute,
|
|
8
|
+
Outlet,
|
|
9
|
+
Link,
|
|
10
|
+
} from '@tanstack/react-router'
|
|
11
|
+
import { TanstackProvider } from 'fumadocs-core/framework/tanstack'
|
|
12
|
+
import { DocsLayout } from 'fumadocs-ui/layouts/docs'
|
|
13
|
+
import type { PageTree } from 'fumadocs-core/server'
|
|
14
|
+
import { pages, sidebar } from 'virtual:prev-pages'
|
|
15
|
+
import { useDiagrams } from './diagrams'
|
|
16
|
+
import 'fumadocs-ui/style.css'
|
|
3
17
|
import './styles.css'
|
|
4
|
-
import { App } from './App'
|
|
5
18
|
|
|
19
|
+
// Convert prev-cli sidebar to Fumadocs PageTree format
|
|
20
|
+
function convertToPageTree(items: any[]): PageTree.Root {
|
|
21
|
+
function convertItem(item: any): PageTree.Item | PageTree.Folder {
|
|
22
|
+
if (item.children) {
|
|
23
|
+
return {
|
|
24
|
+
type: 'folder',
|
|
25
|
+
name: item.title,
|
|
26
|
+
children: item.children.map(convertItem),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
type: 'page',
|
|
31
|
+
name: item.title,
|
|
32
|
+
url: item.route || '/',
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
name: 'docs',
|
|
38
|
+
children: items.map(convertItem),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Dynamic imports for MDX pages
|
|
43
|
+
const pageModules = import.meta.glob('/**/*.mdx', { eager: true })
|
|
44
|
+
|
|
45
|
+
function getPageComponent(file: string): React.ComponentType | null {
|
|
46
|
+
const mod = pageModules[`/${file}`] as { default: React.ComponentType } | undefined
|
|
47
|
+
return mod?.default || null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Page wrapper that renders diagrams after content loads
|
|
51
|
+
function PageWrapper({ Component }: { Component: React.ComponentType }) {
|
|
52
|
+
useDiagrams()
|
|
53
|
+
return <Component />
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Root layout with Fumadocs DocsLayout - TanstackProvider wraps inside router
|
|
57
|
+
function RootLayout() {
|
|
58
|
+
const pageTree = convertToPageTree(sidebar)
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<TanstackProvider>
|
|
62
|
+
<DocsLayout
|
|
63
|
+
tree={pageTree}
|
|
64
|
+
nav={{ enabled: false }}
|
|
65
|
+
>
|
|
66
|
+
<article className="prose max-w-none">
|
|
67
|
+
<Outlet />
|
|
68
|
+
</article>
|
|
69
|
+
</DocsLayout>
|
|
70
|
+
</TanstackProvider>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Create root route
|
|
75
|
+
const rootRoute = createRootRoute({
|
|
76
|
+
component: RootLayout,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Create routes from pages
|
|
80
|
+
const pageRoutes = pages.map((page: { route: string; file: string }) => {
|
|
81
|
+
const Component = getPageComponent(page.file)
|
|
82
|
+
return createRoute({
|
|
83
|
+
getParentRoute: () => rootRoute,
|
|
84
|
+
path: page.route === '/' ? '/' : page.route,
|
|
85
|
+
component: Component ? () => <PageWrapper Component={Component} /> : () => null,
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Create router
|
|
90
|
+
const routeTree = rootRoute.addChildren(pageRoutes)
|
|
91
|
+
const router = createRouter({ routeTree })
|
|
92
|
+
|
|
93
|
+
// Mount app - RouterProvider must be outermost so TanStack Router context is available
|
|
6
94
|
const container = document.getElementById('root')
|
|
7
95
|
if (container) {
|
|
8
96
|
const root = createRoot(container)
|
|
9
|
-
root.render(<
|
|
97
|
+
root.render(<RouterProvider router={router} />)
|
|
10
98
|
}
|
package/src/theme/styles.css
CHANGED
|
@@ -1,274 +1,99 @@
|
|
|
1
|
-
/* Fonts loaded via HTML preload for better performance */
|
|
2
|
-
@import "tailwindcss";
|
|
3
|
-
|
|
4
|
-
@custom-variant dark (&:is(.dark *));
|
|
5
|
-
|
|
6
|
-
/* Scan CLI source files for Tailwind classes */
|
|
7
|
-
@source "../theme";
|
|
8
|
-
@source "../ui";
|
|
9
|
-
|
|
10
1
|
/*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Using Tailwind's stone palette for warmth
|
|
2
|
+
Prev CLI Theme Customization
|
|
3
|
+
Overrides fumadocs-ui's default theme with warm stone palette
|
|
14
4
|
*/
|
|
15
|
-
@theme {
|
|
16
|
-
/* Font families - DM Sans for clean, geometric type */
|
|
17
|
-
--font-display: "DM Sans", system-ui, sans-serif;
|
|
18
|
-
--font-body: "DM Sans", system-ui, sans-serif;
|
|
19
|
-
--font-sans: "DM Sans", system-ui, sans-serif;
|
|
20
|
-
--font-mono: "IBM Plex Mono", ui-monospace, monospace;
|
|
21
|
-
}
|
|
22
5
|
|
|
23
|
-
/*
|
|
24
|
-
|
|
25
|
-
--
|
|
26
|
-
--
|
|
27
|
-
--radius-lg: var(--radius);
|
|
28
|
-
--radius-xl: calc(var(--radius) + 4px);
|
|
29
|
-
--color-background: var(--background);
|
|
30
|
-
--color-foreground: var(--foreground);
|
|
31
|
-
--color-card: var(--card);
|
|
32
|
-
--color-card-foreground: var(--card-foreground);
|
|
33
|
-
--color-popover: var(--popover);
|
|
34
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
35
|
-
--color-primary: var(--primary);
|
|
36
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
37
|
-
--color-secondary: var(--secondary);
|
|
38
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
39
|
-
--color-muted: var(--muted);
|
|
40
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
41
|
-
--color-accent: var(--accent);
|
|
42
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
43
|
-
--color-destructive: var(--destructive);
|
|
44
|
-
--color-border: var(--border);
|
|
45
|
-
--color-input: var(--input);
|
|
46
|
-
--color-ring: var(--ring);
|
|
47
|
-
--color-chart-1: var(--chart-1);
|
|
48
|
-
--color-chart-2: var(--chart-2);
|
|
49
|
-
--color-chart-3: var(--chart-3);
|
|
50
|
-
--color-chart-4: var(--chart-4);
|
|
51
|
-
--color-chart-5: var(--chart-5);
|
|
52
|
-
--color-sidebar: var(--sidebar);
|
|
53
|
-
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
54
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
55
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
56
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
57
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
58
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
59
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
6
|
+
/* Custom fonts */
|
|
7
|
+
:root {
|
|
8
|
+
--fd-font-sans: "DM Sans", system-ui, sans-serif;
|
|
9
|
+
--fd-font-mono: "IBM Plex Mono", ui-monospace, monospace;
|
|
60
10
|
}
|
|
61
11
|
|
|
62
|
-
/*
|
|
63
|
-
Light theme - Warm Stone palette
|
|
64
|
-
Professional, editorial aesthetic with warmth
|
|
65
|
-
*/
|
|
12
|
+
/* Override fumadocs colors with warm stone palette - Light theme */
|
|
66
13
|
:root {
|
|
67
|
-
--
|
|
68
|
-
|
|
69
|
-
--
|
|
70
|
-
--foreground: oklch(0.
|
|
71
|
-
--card: oklch(
|
|
72
|
-
--card-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
73
|
-
--popover: oklch(
|
|
74
|
-
--popover-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
75
|
-
|
|
76
|
-
--primary: oklch(0.
|
|
77
|
-
--primary-foreground: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
78
|
-
|
|
79
|
-
--secondary: oklch(0.
|
|
80
|
-
--
|
|
81
|
-
|
|
82
|
-
--
|
|
83
|
-
--muted-foreground: oklch(0.444 0.011 73.639); /* stone-600 #57534E */
|
|
84
|
-
/* Accent - Lime green for subtle pops */
|
|
85
|
-
--accent: oklch(0.970 0.001 106.424); /* stone-100 */
|
|
86
|
-
--accent-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
87
|
-
/* Destructive - Rose */
|
|
88
|
-
--destructive: oklch(0.586 0.253 17.585); /* rose-600 */
|
|
89
|
-
/* Borders & Inputs - Stone very light */
|
|
90
|
-
--border: oklch(0.923 0.003 48.717); /* stone-200 #E7E5E4 */
|
|
91
|
-
--input: oklch(0.923 0.003 48.717); /* stone-200 */
|
|
92
|
-
--ring: oklch(0.553 0.013 58.071); /* stone-500 */
|
|
93
|
-
/* Chart colors */
|
|
94
|
-
--chart-1: oklch(0.532 0.157 131.589); /* lime-600 #65A30D */
|
|
95
|
-
--chart-2: oklch(0.6 0.118 184.704); /* teal-600 */
|
|
96
|
-
--chart-3: oklch(0.398 0.07 227.392); /* cyan-800 */
|
|
97
|
-
--chart-4: oklch(0.828 0.189 84.429); /* yellow-400 */
|
|
98
|
-
--chart-5: oklch(0.769 0.188 70.08); /* amber-400 */
|
|
99
|
-
/* Sidebar - Stone */
|
|
100
|
-
--sidebar: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
101
|
-
--sidebar-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
102
|
-
--sidebar-primary: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
103
|
-
--sidebar-primary-foreground: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
104
|
-
--sidebar-accent: oklch(0.970 0.001 106.424); /* stone-100 */
|
|
105
|
-
--sidebar-accent-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
106
|
-
--sidebar-border: oklch(0.923 0.003 48.717); /* stone-200 */
|
|
107
|
-
--sidebar-ring: oklch(0.553 0.013 58.071); /* stone-500 */
|
|
14
|
+
--fd-background: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
15
|
+
--fd-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
16
|
+
--fd-muted: oklch(0.970 0.001 106.424); /* stone-100 */
|
|
17
|
+
--fd-muted-foreground: oklch(0.444 0.011 73.639); /* stone-600 */
|
|
18
|
+
--fd-card: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
19
|
+
--fd-card-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
20
|
+
--fd-popover: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
21
|
+
--fd-popover-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
22
|
+
--fd-border: oklch(0.869 0.005 106.424); /* stone-300 */
|
|
23
|
+
--fd-primary: oklch(0.268 0.007 34.298); /* stone-800 */
|
|
24
|
+
--fd-primary-foreground: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
25
|
+
--fd-secondary: oklch(0.923 0.003 106.424); /* stone-200 */
|
|
26
|
+
--fd-secondary-foreground: oklch(0.268 0.007 34.298); /* stone-800 */
|
|
27
|
+
--fd-accent: oklch(0.923 0.003 106.424); /* stone-200 */
|
|
28
|
+
--fd-accent-foreground: oklch(0.268 0.007 34.298); /* stone-800 */
|
|
29
|
+
--fd-ring: oklch(0.553 0.013 73.639); /* stone-500 */
|
|
108
30
|
}
|
|
109
31
|
|
|
110
|
-
/*
|
|
111
|
-
|
|
112
|
-
*/
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
--
|
|
116
|
-
--
|
|
117
|
-
--card: oklch(0.
|
|
118
|
-
--
|
|
119
|
-
--popover: oklch(0.
|
|
120
|
-
--
|
|
121
|
-
|
|
122
|
-
--primary: oklch(0.
|
|
123
|
-
--
|
|
124
|
-
|
|
125
|
-
--
|
|
126
|
-
--
|
|
127
|
-
|
|
128
|
-
--muted: oklch(0.318 0.008 43.185); /* stone-700 */
|
|
129
|
-
--muted-foreground: oklch(0.709 0.01 56.259); /* stone-400 #A8A29E */
|
|
130
|
-
/* Accent - Stone dark */
|
|
131
|
-
--accent: oklch(0.318 0.008 43.185); /* stone-700 */
|
|
132
|
-
--accent-foreground: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
133
|
-
/* Destructive - Rose lighter for dark mode */
|
|
134
|
-
--destructive: oklch(0.712 0.194 13.428); /* rose-400 */
|
|
135
|
-
/* Borders & Inputs - Stone dark */
|
|
136
|
-
--border: oklch(0.370 0.010 67.558); /* stone-600 */
|
|
137
|
-
--input: oklch(0.370 0.010 67.558); /* stone-600 */
|
|
138
|
-
--ring: oklch(0.553 0.013 58.071); /* stone-500 */
|
|
139
|
-
/* Chart colors - brighter for dark mode */
|
|
140
|
-
--chart-1: oklch(0.648 0.2 131.684); /* lime-500 */
|
|
141
|
-
--chart-2: oklch(0.696 0.17 162.48); /* emerald-400 */
|
|
142
|
-
--chart-3: oklch(0.769 0.188 70.08); /* amber-400 */
|
|
143
|
-
--chart-4: oklch(0.627 0.265 303.9); /* purple-500 */
|
|
144
|
-
--chart-5: oklch(0.645 0.246 16.439); /* rose-500 */
|
|
145
|
-
/* Sidebar - Stone dark */
|
|
146
|
-
--sidebar: oklch(0.268 0.007 34.298); /* stone-800 */
|
|
147
|
-
--sidebar-foreground: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
148
|
-
--sidebar-primary: oklch(0.648 0.2 131.684); /* lime-500 */
|
|
149
|
-
--sidebar-primary-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
150
|
-
--sidebar-accent: oklch(0.318 0.008 43.185); /* stone-700 */
|
|
151
|
-
--sidebar-accent-foreground: oklch(0.985 0.001 106.424); /* stone-50 */
|
|
152
|
-
--sidebar-border: oklch(0.370 0.010 67.558); /* stone-600 */
|
|
153
|
-
--sidebar-ring: oklch(0.553 0.013 58.071); /* stone-500 */
|
|
32
|
+
/* Dark theme with warm stone palette */
|
|
33
|
+
.dark, [data-theme="dark"] {
|
|
34
|
+
--fd-background: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
35
|
+
--fd-foreground: oklch(0.970 0.001 106.424); /* stone-100 */
|
|
36
|
+
--fd-muted: oklch(0.268 0.007 34.298); /* stone-800 */
|
|
37
|
+
--fd-muted-foreground: oklch(0.709 0.010 106.424); /* stone-400 */
|
|
38
|
+
--fd-card: oklch(0.268 0.007 34.298); /* stone-800 */
|
|
39
|
+
--fd-card-foreground: oklch(0.970 0.001 106.424); /* stone-100 */
|
|
40
|
+
--fd-popover: oklch(0.268 0.007 34.298); /* stone-800 */
|
|
41
|
+
--fd-popover-foreground: oklch(0.970 0.001 106.424); /* stone-100 */
|
|
42
|
+
--fd-border: oklch(0.444 0.011 73.639); /* stone-600 */
|
|
43
|
+
--fd-primary: oklch(0.970 0.001 106.424); /* stone-100 */
|
|
44
|
+
--fd-primary-foreground: oklch(0.216 0.006 56.043); /* stone-900 */
|
|
45
|
+
--fd-secondary: oklch(0.341 0.009 56.043); /* stone-700 */
|
|
46
|
+
--fd-secondary-foreground: oklch(0.970 0.001 106.424); /* stone-100 */
|
|
47
|
+
--fd-accent: oklch(0.341 0.009 56.043); /* stone-700 */
|
|
48
|
+
--fd-accent-foreground: oklch(0.970 0.001 106.424); /* stone-100 */
|
|
49
|
+
--fd-ring: oklch(0.553 0.013 73.639); /* stone-500 */
|
|
154
50
|
}
|
|
155
51
|
|
|
156
|
-
/*
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
font-family: var(--font-display);
|
|
166
|
-
@apply font-semibold tracking-tight;
|
|
167
|
-
}
|
|
168
|
-
code, pre, kbd {
|
|
169
|
-
font-family: var(--font-mono);
|
|
170
|
-
}
|
|
52
|
+
/* Diagram container styles */
|
|
53
|
+
.mermaid-diagram,
|
|
54
|
+
.d2-diagram {
|
|
55
|
+
margin: 1rem 0;
|
|
56
|
+
padding: 1rem;
|
|
57
|
+
background: var(--fd-muted);
|
|
58
|
+
border-radius: 0.5rem;
|
|
59
|
+
display: flex;
|
|
60
|
+
justify-content: center;
|
|
171
61
|
}
|
|
172
62
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
@keyframes fade-in {
|
|
180
|
-
from {
|
|
181
|
-
opacity: 0;
|
|
182
|
-
}
|
|
183
|
-
to {
|
|
184
|
-
opacity: 1;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.animate-collapse-down {
|
|
189
|
-
animation: collapse-down 200ms ease-out;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.animate-collapse-up {
|
|
193
|
-
animation: collapse-up 200ms ease-out;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
@keyframes collapse-down {
|
|
197
|
-
from {
|
|
198
|
-
height: 0;
|
|
199
|
-
}
|
|
200
|
-
to {
|
|
201
|
-
height: var(--radix-collapsible-content-height);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
@keyframes collapse-up {
|
|
206
|
-
from {
|
|
207
|
-
height: var(--radix-collapsible-content-height);
|
|
208
|
-
}
|
|
209
|
-
to {
|
|
210
|
-
height: 0;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
63
|
+
.mermaid-diagram svg,
|
|
64
|
+
.d2-diagram svg {
|
|
65
|
+
max-width: 100%;
|
|
66
|
+
height: auto;
|
|
213
67
|
}
|
|
214
68
|
|
|
215
|
-
/*
|
|
216
|
-
@
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
--tw-prose-lead: var(--muted-foreground);
|
|
221
|
-
--tw-prose-links: var(--primary);
|
|
222
|
-
--tw-prose-bold: var(--foreground);
|
|
223
|
-
--tw-prose-counters: var(--muted-foreground);
|
|
224
|
-
--tw-prose-bullets: var(--muted-foreground);
|
|
225
|
-
--tw-prose-hr: var(--border);
|
|
226
|
-
--tw-prose-quotes: var(--foreground);
|
|
227
|
-
--tw-prose-quote-borders: var(--border);
|
|
228
|
-
--tw-prose-captions: var(--muted-foreground);
|
|
229
|
-
--tw-prose-kbd: var(--foreground);
|
|
230
|
-
--tw-prose-kbd-shadows: var(--ring);
|
|
231
|
-
--tw-prose-code: var(--foreground);
|
|
232
|
-
--tw-prose-pre-code: var(--foreground);
|
|
233
|
-
--tw-prose-pre-bg: var(--muted);
|
|
234
|
-
--tw-prose-th-borders: var(--border);
|
|
235
|
-
--tw-prose-td-borders: var(--border);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
.prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
|
|
239
|
-
@apply bg-muted px-1.5 py-0.5 rounded-sm text-sm font-mono;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
.prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *))::before,
|
|
243
|
-
.prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *))::after {
|
|
244
|
-
content: none;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
.prose :where(pre):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
|
|
248
|
-
@apply bg-muted border border-border rounded-lg;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.prose :where(a):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
|
|
252
|
-
@apply text-foreground font-medium underline underline-offset-4 decoration-border hover:decoration-foreground transition-colors;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
.prose :where(h1, h2, h3, h4, h5, h6):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
|
|
256
|
-
@apply font-display;
|
|
257
|
-
}
|
|
69
|
+
/* Smooth page transitions */
|
|
70
|
+
@keyframes fade-in {
|
|
71
|
+
from { opacity: 0; transform: translateY(4px); }
|
|
72
|
+
to { opacity: 1; transform: translateY(0); }
|
|
73
|
+
}
|
|
258
74
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
75
|
+
.animate-fade-in {
|
|
76
|
+
animation: fade-in 0.2s ease-out;
|
|
77
|
+
}
|
|
262
78
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
79
|
+
/* Typography improvements */
|
|
80
|
+
.prose {
|
|
81
|
+
--tw-prose-body: var(--fd-foreground);
|
|
82
|
+
--tw-prose-headings: var(--fd-foreground);
|
|
83
|
+
--tw-prose-links: var(--fd-primary);
|
|
84
|
+
--tw-prose-code: var(--fd-foreground);
|
|
85
|
+
--tw-prose-pre-bg: var(--fd-muted);
|
|
86
|
+
}
|
|
266
87
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
88
|
+
/* Code block styling */
|
|
89
|
+
pre code {
|
|
90
|
+
font-family: var(--fd-font-mono);
|
|
91
|
+
font-size: 0.875rem;
|
|
92
|
+
line-height: 1.7;
|
|
93
|
+
}
|
|
270
94
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
95
|
+
/* Sidebar active item highlight */
|
|
96
|
+
[data-active="true"] {
|
|
97
|
+
background: var(--fd-accent);
|
|
98
|
+
color: var(--fd-accent-foreground);
|
|
274
99
|
}
|
package/dist/theme/App.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function App(): import("react/jsx-runtime").JSX.Element;
|
package/dist/theme/Layout.d.ts
DELETED
package/dist/theme/Sidebar.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function Sidebar(): import("react/jsx-runtime").JSX.Element;
|
package/dist/theme/entry.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import './styles.css';
|
package/src/theme/App.tsx
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { BrowserRouter, Routes, Route } from 'react-router-dom'
|
|
3
|
-
import { Layout } from './Layout'
|
|
4
|
-
import { pages } from 'virtual:prev-pages'
|
|
5
|
-
|
|
6
|
-
// Dynamic imports for MDX pages
|
|
7
|
-
const pageModules = import.meta.glob('/**/*.mdx', { eager: true })
|
|
8
|
-
|
|
9
|
-
function getPageComponent(file: string) {
|
|
10
|
-
const mod = pageModules[`/${file}`] as { default: React.ComponentType }
|
|
11
|
-
return mod?.default
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function App() {
|
|
15
|
-
return (
|
|
16
|
-
<BrowserRouter>
|
|
17
|
-
<Routes>
|
|
18
|
-
<Route element={<Layout />}>
|
|
19
|
-
{pages.map((page: { route: string; file: string }) => {
|
|
20
|
-
const Component = getPageComponent(page.file)
|
|
21
|
-
return Component ? (
|
|
22
|
-
<Route
|
|
23
|
-
key={page.route}
|
|
24
|
-
path={page.route}
|
|
25
|
-
element={<Component />}
|
|
26
|
-
/>
|
|
27
|
-
) : null
|
|
28
|
-
})}
|
|
29
|
-
</Route>
|
|
30
|
-
</Routes>
|
|
31
|
-
</BrowserRouter>
|
|
32
|
-
)
|
|
33
|
-
}
|
package/src/theme/Sidebar.tsx
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { Link, useLocation } from 'react-router-dom'
|
|
3
|
-
import { sidebar } from 'virtual:prev-pages'
|
|
4
|
-
import { cn } from '../ui/utils'
|
|
5
|
-
|
|
6
|
-
interface SidebarItem {
|
|
7
|
-
title: string
|
|
8
|
-
route?: string
|
|
9
|
-
children?: SidebarItem[]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function Sidebar() {
|
|
13
|
-
const location = useLocation()
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<aside className="w-64 border-r border-sidebar-border bg-sidebar p-4 min-h-screen sticky top-0">
|
|
17
|
-
<nav>
|
|
18
|
-
<ul className="space-y-1">
|
|
19
|
-
{(sidebar as SidebarItem[]).map((item, i) => (
|
|
20
|
-
<SidebarItemComponent
|
|
21
|
-
key={i}
|
|
22
|
-
item={item}
|
|
23
|
-
currentPath={location.pathname}
|
|
24
|
-
/>
|
|
25
|
-
))}
|
|
26
|
-
</ul>
|
|
27
|
-
</nav>
|
|
28
|
-
</aside>
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function SidebarItemComponent({
|
|
33
|
-
item,
|
|
34
|
-
currentPath
|
|
35
|
-
}: {
|
|
36
|
-
item: SidebarItem
|
|
37
|
-
currentPath: string
|
|
38
|
-
}) {
|
|
39
|
-
const isActive = item.route === currentPath
|
|
40
|
-
|
|
41
|
-
if (item.children) {
|
|
42
|
-
return (
|
|
43
|
-
<li className="mt-4 first:mt-0">
|
|
44
|
-
<span className="font-semibold text-xs text-sidebar-foreground/60 uppercase tracking-wider">
|
|
45
|
-
{item.title}
|
|
46
|
-
</span>
|
|
47
|
-
<ul className="ml-3 mt-2 space-y-1">
|
|
48
|
-
{item.children.map((child, i) => (
|
|
49
|
-
<SidebarItemComponent
|
|
50
|
-
key={i}
|
|
51
|
-
item={child}
|
|
52
|
-
currentPath={currentPath}
|
|
53
|
-
/>
|
|
54
|
-
))}
|
|
55
|
-
</ul>
|
|
56
|
-
</li>
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<li>
|
|
62
|
-
<Link
|
|
63
|
-
to={item.route || '/'}
|
|
64
|
-
className={cn(
|
|
65
|
-
'block py-1.5 px-2 rounded-md text-sm transition-colors',
|
|
66
|
-
isActive
|
|
67
|
-
? 'bg-sidebar-accent text-sidebar-accent-foreground font-medium'
|
|
68
|
-
: 'text-sidebar-foreground/70 hover:text-sidebar-foreground hover:bg-sidebar-accent/50'
|
|
69
|
-
)}
|
|
70
|
-
>
|
|
71
|
-
{item.title}
|
|
72
|
-
</Link>
|
|
73
|
-
</li>
|
|
74
|
-
)
|
|
75
|
-
}
|