prev-cli 0.3.3 → 0.4.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 +28 -97
- package/package.json +1 -1
- package/src/theme/Layout.tsx +116 -0
- package/src/theme/entry.tsx +25 -16
- package/src/theme/styles.css +126 -2
package/dist/cli.js
CHANGED
|
@@ -12,9 +12,9 @@ import react from "@vitejs/plugin-react-swc";
|
|
|
12
12
|
import mdx from "@mdx-js/rollup";
|
|
13
13
|
import remarkGfm from "remark-gfm";
|
|
14
14
|
import rehypeHighlight from "rehype-highlight";
|
|
15
|
-
import
|
|
15
|
+
import path4 from "path";
|
|
16
16
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
17
|
-
import { existsSync as
|
|
17
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
18
18
|
|
|
19
19
|
// src/utils/cache.ts
|
|
20
20
|
import { createHash } from "crypto";
|
|
@@ -268,72 +268,6 @@ function entryPlugin(rootDir) {
|
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
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
|
-
|
|
337
271
|
// src/vite/config.ts
|
|
338
272
|
function createFriendlyLogger() {
|
|
339
273
|
const logger = createLogger("info", { allowClearScreen: false });
|
|
@@ -389,10 +323,10 @@ function createFriendlyLogger() {
|
|
|
389
323
|
};
|
|
390
324
|
}
|
|
391
325
|
function findCliRoot2() {
|
|
392
|
-
let dir =
|
|
326
|
+
let dir = path4.dirname(fileURLToPath2(import.meta.url));
|
|
393
327
|
for (let i = 0;i < 10; i++) {
|
|
394
|
-
const pkgPath =
|
|
395
|
-
if (
|
|
328
|
+
const pkgPath = path4.join(dir, "package.json");
|
|
329
|
+
if (existsSync2(pkgPath)) {
|
|
396
330
|
try {
|
|
397
331
|
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
398
332
|
if (pkg.name === "prev-cli") {
|
|
@@ -400,24 +334,24 @@ function findCliRoot2() {
|
|
|
400
334
|
}
|
|
401
335
|
} catch {}
|
|
402
336
|
}
|
|
403
|
-
const parent =
|
|
337
|
+
const parent = path4.dirname(dir);
|
|
404
338
|
if (parent === dir)
|
|
405
339
|
break;
|
|
406
340
|
dir = parent;
|
|
407
341
|
}
|
|
408
|
-
return
|
|
342
|
+
return path4.dirname(path4.dirname(fileURLToPath2(import.meta.url)));
|
|
409
343
|
}
|
|
410
344
|
function findNodeModules(cliRoot2) {
|
|
411
|
-
const localNodeModules =
|
|
412
|
-
if (
|
|
345
|
+
const localNodeModules = path4.join(cliRoot2, "node_modules");
|
|
346
|
+
if (existsSync2(path4.join(localNodeModules, "react"))) {
|
|
413
347
|
return localNodeModules;
|
|
414
348
|
}
|
|
415
349
|
let dir = cliRoot2;
|
|
416
350
|
for (let i = 0;i < 10; i++) {
|
|
417
|
-
const parent =
|
|
351
|
+
const parent = path4.dirname(dir);
|
|
418
352
|
if (parent === dir)
|
|
419
353
|
break;
|
|
420
|
-
if (
|
|
354
|
+
if (path4.basename(parent) === "node_modules" && existsSync2(path4.join(parent, "react"))) {
|
|
421
355
|
return parent;
|
|
422
356
|
}
|
|
423
357
|
dir = parent;
|
|
@@ -426,7 +360,7 @@ function findNodeModules(cliRoot2) {
|
|
|
426
360
|
}
|
|
427
361
|
var cliRoot2 = findCliRoot2();
|
|
428
362
|
var cliNodeModules = findNodeModules(cliRoot2);
|
|
429
|
-
var srcRoot2 =
|
|
363
|
+
var srcRoot2 = path4.join(cliRoot2, "src");
|
|
430
364
|
async function createViteConfig(options) {
|
|
431
365
|
const { rootDir, mode, port } = options;
|
|
432
366
|
const cacheDir = await ensureCacheDir(rootDir);
|
|
@@ -437,7 +371,6 @@ async function createViteConfig(options) {
|
|
|
437
371
|
customLogger: createFriendlyLogger(),
|
|
438
372
|
logLevel: mode === "production" ? "silent" : "info",
|
|
439
373
|
plugins: [
|
|
440
|
-
fumadocsPlugin(cliNodeModules),
|
|
441
374
|
mdx({
|
|
442
375
|
remarkPlugins: [remarkGfm],
|
|
443
376
|
rehypePlugins: [rehypeHighlight]
|
|
@@ -448,22 +381,19 @@ async function createViteConfig(options) {
|
|
|
448
381
|
],
|
|
449
382
|
resolve: {
|
|
450
383
|
alias: {
|
|
451
|
-
"@prev/ui":
|
|
452
|
-
"@prev/theme":
|
|
453
|
-
react:
|
|
454
|
-
"react-dom":
|
|
455
|
-
"@tanstack/react-router":
|
|
456
|
-
mermaid:
|
|
457
|
-
dayjs:
|
|
458
|
-
"@terrastruct/d2":
|
|
384
|
+
"@prev/ui": path4.join(srcRoot2, "ui"),
|
|
385
|
+
"@prev/theme": path4.join(srcRoot2, "theme"),
|
|
386
|
+
react: path4.join(cliNodeModules, "react"),
|
|
387
|
+
"react-dom": path4.join(cliNodeModules, "react-dom"),
|
|
388
|
+
"@tanstack/react-router": path4.join(cliNodeModules, "@tanstack/react-router"),
|
|
389
|
+
mermaid: path4.join(cliNodeModules, "mermaid"),
|
|
390
|
+
dayjs: path4.join(cliNodeModules, "dayjs"),
|
|
391
|
+
"@terrastruct/d2": path4.join(cliNodeModules, "@terrastruct/d2")
|
|
459
392
|
},
|
|
460
393
|
dedupe: [
|
|
461
394
|
"react",
|
|
462
395
|
"react-dom",
|
|
463
|
-
"@tanstack/react-router"
|
|
464
|
-
"fumadocs-core",
|
|
465
|
-
"fumadocs-ui",
|
|
466
|
-
"@fumadocs/ui"
|
|
396
|
+
"@tanstack/react-router"
|
|
467
397
|
]
|
|
468
398
|
},
|
|
469
399
|
optimizeDeps: {
|
|
@@ -478,11 +408,6 @@ async function createViteConfig(options) {
|
|
|
478
408
|
"mermaid",
|
|
479
409
|
"dayjs",
|
|
480
410
|
"@terrastruct/d2"
|
|
481
|
-
],
|
|
482
|
-
exclude: [
|
|
483
|
-
"fumadocs-core",
|
|
484
|
-
"fumadocs-ui",
|
|
485
|
-
"@fumadocs/ui"
|
|
486
411
|
]
|
|
487
412
|
},
|
|
488
413
|
ssr: {
|
|
@@ -493,6 +418,12 @@ async function createViteConfig(options) {
|
|
|
493
418
|
strictPort: false,
|
|
494
419
|
fs: {
|
|
495
420
|
allow: [rootDir, cliRoot2]
|
|
421
|
+
},
|
|
422
|
+
warmup: {
|
|
423
|
+
clientFiles: [
|
|
424
|
+
path4.join(srcRoot2, "theme/entry.tsx"),
|
|
425
|
+
path4.join(srcRoot2, "theme/styles.css")
|
|
426
|
+
]
|
|
496
427
|
}
|
|
497
428
|
},
|
|
498
429
|
preview: {
|
|
@@ -500,7 +431,7 @@ async function createViteConfig(options) {
|
|
|
500
431
|
strictPort: false
|
|
501
432
|
},
|
|
502
433
|
build: {
|
|
503
|
-
outDir:
|
|
434
|
+
outDir: path4.join(rootDir, "dist"),
|
|
504
435
|
reportCompressedSize: false,
|
|
505
436
|
chunkSizeWarningLimit: 1e4
|
|
506
437
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Link, useLocation } from '@tanstack/react-router'
|
|
3
|
+
import type { PageTree } from 'fumadocs-core/server'
|
|
4
|
+
|
|
5
|
+
interface LayoutProps {
|
|
6
|
+
tree: PageTree.Root
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface SidebarItemProps {
|
|
11
|
+
item: PageTree.Item | PageTree.Folder
|
|
12
|
+
depth?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function SidebarItem({ item, depth = 0 }: SidebarItemProps) {
|
|
16
|
+
const location = useLocation()
|
|
17
|
+
const [isOpen, setIsOpen] = useState(true)
|
|
18
|
+
|
|
19
|
+
if (item.type === 'folder') {
|
|
20
|
+
return (
|
|
21
|
+
<div className="sidebar-folder">
|
|
22
|
+
<button
|
|
23
|
+
className="sidebar-folder-toggle"
|
|
24
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
25
|
+
style={{ paddingLeft: `${depth * 12 + 8}px` }}
|
|
26
|
+
>
|
|
27
|
+
<span className={`folder-icon ${isOpen ? 'open' : ''}`}>
|
|
28
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
|
|
29
|
+
<path d="M4.5 2L8.5 6L4.5 10" stroke="currentColor" strokeWidth="1.5" fill="none"/>
|
|
30
|
+
</svg>
|
|
31
|
+
</span>
|
|
32
|
+
{item.name}
|
|
33
|
+
</button>
|
|
34
|
+
{isOpen && (
|
|
35
|
+
<div className="sidebar-folder-children">
|
|
36
|
+
{item.children.map((child, i) => (
|
|
37
|
+
<SidebarItem key={i} item={child} depth={depth + 1} />
|
|
38
|
+
))}
|
|
39
|
+
</div>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const isActive = location.pathname === item.url ||
|
|
46
|
+
(item.url === '/' && location.pathname === '/')
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Link
|
|
50
|
+
to={item.url}
|
|
51
|
+
className={`sidebar-link ${isActive ? 'active' : ''}`}
|
|
52
|
+
style={{ paddingLeft: `${depth * 12 + 16}px` }}
|
|
53
|
+
>
|
|
54
|
+
{item.name}
|
|
55
|
+
</Link>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ThemeToggle() {
|
|
60
|
+
const [isDark, setIsDark] = useState(() => {
|
|
61
|
+
if (typeof window !== 'undefined') {
|
|
62
|
+
return document.documentElement.classList.contains('dark')
|
|
63
|
+
}
|
|
64
|
+
return false
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const toggle = () => {
|
|
68
|
+
const newDark = !isDark
|
|
69
|
+
setIsDark(newDark)
|
|
70
|
+
document.documentElement.classList.toggle('dark', newDark)
|
|
71
|
+
localStorage.setItem('theme', newDark ? 'dark' : 'light')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<button className="theme-toggle" onClick={toggle} aria-label="Toggle theme">
|
|
76
|
+
{isDark ? (
|
|
77
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
78
|
+
<circle cx="12" cy="12" r="5"/>
|
|
79
|
+
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
|
80
|
+
</svg>
|
|
81
|
+
) : (
|
|
82
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
83
|
+
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
|
|
84
|
+
</svg>
|
|
85
|
+
)}
|
|
86
|
+
</button>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function Layout({ tree, children }: LayoutProps) {
|
|
91
|
+
// Initialize theme from localStorage
|
|
92
|
+
React.useEffect(() => {
|
|
93
|
+
const saved = localStorage.getItem('theme')
|
|
94
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
95
|
+
const isDark = saved === 'dark' || (!saved && prefersDark)
|
|
96
|
+
document.documentElement.classList.toggle('dark', isDark)
|
|
97
|
+
}, [])
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div className="prev-layout">
|
|
101
|
+
<aside className="prev-sidebar">
|
|
102
|
+
<nav className="sidebar-nav">
|
|
103
|
+
{tree.children.map((item, i) => (
|
|
104
|
+
<SidebarItem key={i} item={item} />
|
|
105
|
+
))}
|
|
106
|
+
</nav>
|
|
107
|
+
<div className="sidebar-footer">
|
|
108
|
+
<ThemeToggle />
|
|
109
|
+
</div>
|
|
110
|
+
</aside>
|
|
111
|
+
<main className="prev-main">
|
|
112
|
+
{children}
|
|
113
|
+
</main>
|
|
114
|
+
</div>
|
|
115
|
+
)
|
|
116
|
+
}
|
package/src/theme/entry.tsx
CHANGED
|
@@ -6,16 +6,30 @@ import {
|
|
|
6
6
|
createRootRoute,
|
|
7
7
|
createRoute,
|
|
8
8
|
Outlet,
|
|
9
|
-
Link,
|
|
10
9
|
} 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
10
|
import { pages, sidebar } from 'virtual:prev-pages'
|
|
15
11
|
import { useDiagrams } from './diagrams'
|
|
16
|
-
import '
|
|
12
|
+
import { Layout } from './Layout'
|
|
17
13
|
import './styles.css'
|
|
18
14
|
|
|
15
|
+
// PageTree types (simplified from fumadocs-core)
|
|
16
|
+
namespace PageTree {
|
|
17
|
+
export interface Item {
|
|
18
|
+
type: 'page'
|
|
19
|
+
name: string
|
|
20
|
+
url: string
|
|
21
|
+
}
|
|
22
|
+
export interface Folder {
|
|
23
|
+
type: 'folder'
|
|
24
|
+
name: string
|
|
25
|
+
children: (Item | Folder)[]
|
|
26
|
+
}
|
|
27
|
+
export interface Root {
|
|
28
|
+
name: string
|
|
29
|
+
children: (Item | Folder)[]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
// Convert prev-cli sidebar to Fumadocs PageTree format
|
|
20
34
|
function convertToPageTree(items: any[]): PageTree.Root {
|
|
21
35
|
function convertItem(item: any): PageTree.Item | PageTree.Folder {
|
|
@@ -53,21 +67,16 @@ function PageWrapper({ Component }: { Component: React.ComponentType }) {
|
|
|
53
67
|
return <Component />
|
|
54
68
|
}
|
|
55
69
|
|
|
56
|
-
// Root layout with
|
|
70
|
+
// Root layout with custom lightweight Layout
|
|
57
71
|
function RootLayout() {
|
|
58
72
|
const pageTree = convertToPageTree(sidebar)
|
|
59
73
|
|
|
60
74
|
return (
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<article className="prev-content">
|
|
67
|
-
<Outlet />
|
|
68
|
-
</article>
|
|
69
|
-
</DocsLayout>
|
|
70
|
-
</TanstackProvider>
|
|
75
|
+
<Layout tree={pageTree}>
|
|
76
|
+
<article className="prev-content">
|
|
77
|
+
<Outlet />
|
|
78
|
+
</article>
|
|
79
|
+
</Layout>
|
|
71
80
|
)
|
|
72
81
|
}
|
|
73
82
|
|
package/src/theme/styles.css
CHANGED
|
@@ -1,8 +1,128 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Prev CLI Theme
|
|
3
|
-
Overrides fumadocs-ui's default theme with warm stone palette
|
|
2
|
+
Prev CLI Theme - Lightweight Documentation Layout
|
|
4
3
|
*/
|
|
5
4
|
|
|
5
|
+
/* Reset */
|
|
6
|
+
*, *::before, *::after {
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
font-family: var(--fd-font-sans);
|
|
13
|
+
background: var(--fd-background);
|
|
14
|
+
color: var(--fd-foreground);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Layout structure */
|
|
18
|
+
.prev-layout {
|
|
19
|
+
display: grid;
|
|
20
|
+
grid-template-columns: 260px 1fr;
|
|
21
|
+
min-height: 100vh;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Sidebar */
|
|
25
|
+
.prev-sidebar {
|
|
26
|
+
position: sticky;
|
|
27
|
+
top: 0;
|
|
28
|
+
height: 100vh;
|
|
29
|
+
background: var(--fd-background);
|
|
30
|
+
border-right: 1px solid var(--fd-border);
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.sidebar-nav {
|
|
37
|
+
flex: 1;
|
|
38
|
+
overflow-y: auto;
|
|
39
|
+
padding: 1rem 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.sidebar-link {
|
|
43
|
+
display: block;
|
|
44
|
+
padding: 0.5rem 1rem;
|
|
45
|
+
color: var(--fd-muted-foreground);
|
|
46
|
+
text-decoration: none;
|
|
47
|
+
font-size: 0.9rem;
|
|
48
|
+
border-radius: 0.375rem;
|
|
49
|
+
transition: all 0.15s ease;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.sidebar-link:hover {
|
|
53
|
+
background: var(--fd-muted);
|
|
54
|
+
color: var(--fd-foreground);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.sidebar-link.active {
|
|
58
|
+
background: var(--fd-accent);
|
|
59
|
+
color: var(--fd-accent-foreground);
|
|
60
|
+
font-weight: 500;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.sidebar-folder-toggle {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 0.5rem;
|
|
67
|
+
width: 100%;
|
|
68
|
+
padding: 0.5rem 1rem;
|
|
69
|
+
background: none;
|
|
70
|
+
border: none;
|
|
71
|
+
color: var(--fd-foreground);
|
|
72
|
+
font-size: 0.9rem;
|
|
73
|
+
font-weight: 500;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
border-radius: 0.375rem;
|
|
76
|
+
text-align: left;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.sidebar-folder-toggle:hover {
|
|
80
|
+
background: var(--fd-muted);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.folder-icon {
|
|
84
|
+
display: flex;
|
|
85
|
+
transition: transform 0.15s ease;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.folder-icon.open {
|
|
89
|
+
transform: rotate(90deg);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.sidebar-folder-children {
|
|
93
|
+
margin-left: 0.5rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.sidebar-footer {
|
|
97
|
+
padding: 1rem;
|
|
98
|
+
border-top: 1px solid var(--fd-border);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.theme-toggle {
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
width: 36px;
|
|
106
|
+
height: 36px;
|
|
107
|
+
background: var(--fd-muted);
|
|
108
|
+
border: none;
|
|
109
|
+
border-radius: 0.5rem;
|
|
110
|
+
color: var(--fd-muted-foreground);
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
transition: all 0.15s ease;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.theme-toggle:hover {
|
|
116
|
+
background: var(--fd-accent);
|
|
117
|
+
color: var(--fd-accent-foreground);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Main content area */
|
|
121
|
+
.prev-main {
|
|
122
|
+
padding: 2rem;
|
|
123
|
+
overflow-x: hidden;
|
|
124
|
+
}
|
|
125
|
+
|
|
6
126
|
/* Custom fonts */
|
|
7
127
|
:root {
|
|
8
128
|
--fd-font-sans: "DM Sans", system-ui, sans-serif;
|
|
@@ -87,6 +207,10 @@
|
|
|
87
207
|
line-height: 1.75;
|
|
88
208
|
color: var(--fd-foreground);
|
|
89
209
|
font-size: 1rem;
|
|
210
|
+
padding-left: 2.5rem !important;
|
|
211
|
+
padding-right: 1.5rem !important;
|
|
212
|
+
padding-top: 0.5rem !important;
|
|
213
|
+
padding-bottom: 3rem !important;
|
|
90
214
|
}
|
|
91
215
|
|
|
92
216
|
/* Heading styles with better spacing */
|