prev-cli 0.17.1 → 0.18.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/package.json +1 -3
- package/src/theme/Layout.tsx +2 -0
- package/src/theme/Preview.tsx +10 -10
- package/src/theme/TOCPanel.tsx +6 -6
- package/src/theme/Toolbar.tsx +5 -5
- package/src/theme/icons.tsx +118 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prev-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Transform MDX directories into beautiful documentation websites",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@mdx-js/react": "^3.1.1",
|
|
44
44
|
"@mdx-js/rollup": "^3.0.0",
|
|
45
|
-
"@tabler/icons-react": "^3.36.1",
|
|
46
45
|
"@tailwindcss/vite": "^4.0.0",
|
|
47
46
|
"@tanstack/react-router": "^1.145.7",
|
|
48
47
|
"@terrastruct/d2": "^0.1.33",
|
|
@@ -53,7 +52,6 @@
|
|
|
53
52
|
"fumadocs-core": "^16.4.3",
|
|
54
53
|
"fumadocs-ui": "^16.4.3",
|
|
55
54
|
"js-yaml": "^4.1.1",
|
|
56
|
-
"lucide-react": "^0.460.0",
|
|
57
55
|
"mermaid": "^11.0.0",
|
|
58
56
|
"picomatch": "^4.0.3",
|
|
59
57
|
"react": "^19.0.0",
|
package/src/theme/Layout.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import type { PageTree } from 'fumadocs-core/server'
|
|
|
3
3
|
import { config } from 'virtual:prev-config'
|
|
4
4
|
import { Toolbar } from './Toolbar'
|
|
5
5
|
import { TOCPanel } from './TOCPanel'
|
|
6
|
+
import { IconSprite } from './icons'
|
|
6
7
|
import './Toolbar.css'
|
|
7
8
|
import './TOCPanel.css'
|
|
8
9
|
|
|
@@ -39,6 +40,7 @@ export function Layout({ tree, children }: LayoutProps) {
|
|
|
39
40
|
|
|
40
41
|
return (
|
|
41
42
|
<div className="prev-layout-floating">
|
|
43
|
+
<IconSprite />
|
|
42
44
|
<Toolbar
|
|
43
45
|
tree={tree}
|
|
44
46
|
onThemeToggle={handleThemeToggle}
|
package/src/theme/Preview.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef } from 'react'
|
|
2
|
-
import { IconDeviceMobile, IconDeviceTablet, IconDeviceDesktop, IconArrowsMaximize, IconArrowsMinimize, IconAdjustmentsHorizontal, IconX, IconLoader2, IconArrowLeft } from '@tabler/icons-react'
|
|
3
2
|
import { Link } from '@tanstack/react-router'
|
|
3
|
+
import { Icon, IconSprite } from './icons'
|
|
4
4
|
import type { PreviewConfig, PreviewMessage, BuildResult } from '../preview-runtime/types'
|
|
5
5
|
|
|
6
6
|
interface PreviewProps {
|
|
@@ -193,7 +193,7 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
193
193
|
active={deviceMode === 'mobile' && customWidth === null}
|
|
194
194
|
title="Mobile (375px)"
|
|
195
195
|
>
|
|
196
|
-
<
|
|
196
|
+
<Icon name="mobile" size={16} />
|
|
197
197
|
</IconButton>
|
|
198
198
|
|
|
199
199
|
<IconButton
|
|
@@ -201,7 +201,7 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
201
201
|
active={deviceMode === 'tablet' && customWidth === null}
|
|
202
202
|
title="Tablet (768px)"
|
|
203
203
|
>
|
|
204
|
-
<
|
|
204
|
+
<Icon name="tablet" size={16} />
|
|
205
205
|
</IconButton>
|
|
206
206
|
|
|
207
207
|
<IconButton
|
|
@@ -209,7 +209,7 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
209
209
|
active={deviceMode === 'desktop' && customWidth === null}
|
|
210
210
|
title="Desktop (100%)"
|
|
211
211
|
>
|
|
212
|
-
<
|
|
212
|
+
<Icon name="desktop" size={16} />
|
|
213
213
|
</IconButton>
|
|
214
214
|
|
|
215
215
|
<div style={{ width: '1px', height: '16px', backgroundColor: 'var(--fd-border, #e4e4e7)', margin: '0 4px' }} />
|
|
@@ -221,7 +221,7 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
221
221
|
active={showSlider || customWidth !== null}
|
|
222
222
|
title="Custom width"
|
|
223
223
|
>
|
|
224
|
-
<
|
|
224
|
+
<Icon name="sliders" size={16} />
|
|
225
225
|
</IconButton>
|
|
226
226
|
|
|
227
227
|
{showSlider && (
|
|
@@ -246,7 +246,7 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
246
246
|
onClick={() => setShowSlider(false)}
|
|
247
247
|
style={{ padding: '2px', background: 'none', border: 'none', cursor: 'pointer', color: 'var(--fd-muted-foreground, #a1a1aa)' }}
|
|
248
248
|
>
|
|
249
|
-
<
|
|
249
|
+
<Icon name="x" size={12} />
|
|
250
250
|
</button>
|
|
251
251
|
</div>
|
|
252
252
|
<input
|
|
@@ -270,7 +270,7 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
270
270
|
active={isFullscreen}
|
|
271
271
|
title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
|
|
272
272
|
>
|
|
273
|
-
{isFullscreen ?
|
|
273
|
+
<Icon name={isFullscreen ? 'minimize' : 'maximize'} size={16} />
|
|
274
274
|
</IconButton>
|
|
275
275
|
</div>
|
|
276
276
|
)
|
|
@@ -371,7 +371,7 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
371
371
|
fontSize: '14px',
|
|
372
372
|
}}
|
|
373
373
|
>
|
|
374
|
-
<
|
|
374
|
+
<Icon name="arrow-left" size={16} />
|
|
375
375
|
Back
|
|
376
376
|
</Link>
|
|
377
377
|
<div style={{ width: '1px', height: '20px', backgroundColor: 'var(--fd-border, #e4e4e7)' }} />
|
|
@@ -379,7 +379,7 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
379
379
|
{displayTitle}
|
|
380
380
|
</span>
|
|
381
381
|
{mode === 'wasm' && buildStatus === 'building' && (
|
|
382
|
-
<
|
|
382
|
+
<Icon name="loader" size={16} style={{ color: 'var(--fd-primary, #3b82f6)', animation: 'spin 1s linear infinite' }} />
|
|
383
383
|
)}
|
|
384
384
|
{mode === 'wasm' && buildTime && (
|
|
385
385
|
<span style={{ fontSize: '12px', color: 'var(--fd-muted-foreground, #a1a1aa)' }}>{buildTime}ms</span>
|
|
@@ -464,7 +464,7 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
464
464
|
{displayTitle}
|
|
465
465
|
</span>
|
|
466
466
|
{mode === 'wasm' && buildStatus === 'building' && (
|
|
467
|
-
<
|
|
467
|
+
<Icon name="loader" size={14} style={{ color: 'var(--fd-primary, #3b82f6)', animation: 'spin 1s linear infinite' }} />
|
|
468
468
|
)}
|
|
469
469
|
{mode === 'wasm' && buildStatus === 'error' && (
|
|
470
470
|
<span style={{ fontSize: '12px', color: '#ef4444' }}>Error</span>
|
package/src/theme/TOCPanel.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef } from 'react'
|
|
2
2
|
import { Link, useLocation } from '@tanstack/react-router'
|
|
3
3
|
import type { PageTree } from 'fumadocs-core/server'
|
|
4
|
-
import {
|
|
4
|
+
import { Icon } from './icons'
|
|
5
5
|
|
|
6
6
|
interface TOCPanelProps {
|
|
7
7
|
tree: PageTree.Root
|
|
@@ -83,7 +83,7 @@ export function TOCPanel({ tree, onClose }: TOCPanelProps) {
|
|
|
83
83
|
<div className="toc-overlay-content" ref={panelRef}>
|
|
84
84
|
<div className="toc-overlay-header">
|
|
85
85
|
<span>Navigation</span>
|
|
86
|
-
<button className="toc-close-btn" onClick={onClose}><
|
|
86
|
+
<button className="toc-close-btn" onClick={onClose}><Icon name="x" size={16} /></button>
|
|
87
87
|
</div>
|
|
88
88
|
<nav className="toc-nav">
|
|
89
89
|
{orderedItems.map((item) => (
|
|
@@ -104,7 +104,7 @@ export function TOCPanel({ tree, onClose }: TOCPanelProps) {
|
|
|
104
104
|
<div className="toc-dropdown" ref={panelRef}>
|
|
105
105
|
<div className="toc-dropdown-header">
|
|
106
106
|
<span>Navigation</span>
|
|
107
|
-
<button className="toc-close-btn" onClick={onClose}><
|
|
107
|
+
<button className="toc-close-btn" onClick={onClose}><Icon name="x" size={16} /></button>
|
|
108
108
|
</div>
|
|
109
109
|
<nav className="toc-nav">
|
|
110
110
|
{orderedItems.map((item, i) => (
|
|
@@ -146,8 +146,8 @@ function TOCItem({ item, location, onNavigate, depth = 0 }: TOCItemProps) {
|
|
|
146
146
|
onClick={() => setIsOpen(!isOpen)}
|
|
147
147
|
style={{ paddingLeft: `${depth * 12 + 8}px` }}
|
|
148
148
|
>
|
|
149
|
-
<
|
|
150
|
-
<
|
|
149
|
+
<Icon name="chevron-right" size={14} className={`folder-chevron ${isOpen ? 'open' : ''}`} />
|
|
150
|
+
<Icon name="folder" size={14} className="toc-icon" />
|
|
151
151
|
{item.name}
|
|
152
152
|
</button>
|
|
153
153
|
{isOpen && (
|
|
@@ -176,7 +176,7 @@ function TOCItem({ item, location, onNavigate, depth = 0 }: TOCItemProps) {
|
|
|
176
176
|
style={{ paddingLeft: `${depth * 12 + 12}px` }}
|
|
177
177
|
onClick={onNavigate}
|
|
178
178
|
>
|
|
179
|
-
<
|
|
179
|
+
<Icon name="file" size={14} className="toc-icon" />
|
|
180
180
|
{item.name}
|
|
181
181
|
</Link>
|
|
182
182
|
)
|
package/src/theme/Toolbar.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect } from 'react'
|
|
|
2
2
|
import { Link, useLocation } from '@tanstack/react-router'
|
|
3
3
|
import type { PageTree } from 'fumadocs-core/server'
|
|
4
4
|
import { previews } from 'virtual:prev-previews'
|
|
5
|
-
import {
|
|
5
|
+
import { Icon } from './icons'
|
|
6
6
|
import './Toolbar.css'
|
|
7
7
|
|
|
8
8
|
interface ToolbarProps {
|
|
@@ -61,12 +61,12 @@ export function Toolbar({ tree, onThemeToggle, onWidthToggle, isDark, isFullWidt
|
|
|
61
61
|
onClick={onTocToggle}
|
|
62
62
|
title="Table of Contents"
|
|
63
63
|
>
|
|
64
|
-
<
|
|
64
|
+
<Icon name="menu" size={18} />
|
|
65
65
|
</button>
|
|
66
66
|
|
|
67
67
|
{previews && previews.length > 0 && (
|
|
68
68
|
<Link to="/previews" className={`toolbar-btn ${isOnPreviews ? 'active' : ''}`} title="Previews">
|
|
69
|
-
<
|
|
69
|
+
<Icon name="grid" size={18} />
|
|
70
70
|
</Link>
|
|
71
71
|
)}
|
|
72
72
|
|
|
@@ -78,7 +78,7 @@ export function Toolbar({ tree, onThemeToggle, onWidthToggle, isDark, isFullWidt
|
|
|
78
78
|
onClick={onWidthToggle}
|
|
79
79
|
title={isFullWidth ? 'Constrain width' : 'Full width'}
|
|
80
80
|
>
|
|
81
|
-
{isFullWidth ?
|
|
81
|
+
<Icon name={isFullWidth ? 'minimize' : 'maximize'} size={18} />
|
|
82
82
|
</button>
|
|
83
83
|
|
|
84
84
|
<button
|
|
@@ -86,7 +86,7 @@ export function Toolbar({ tree, onThemeToggle, onWidthToggle, isDark, isFullWidt
|
|
|
86
86
|
onClick={onThemeToggle}
|
|
87
87
|
title={isDark ? 'Light mode' : 'Dark mode'}
|
|
88
88
|
>
|
|
89
|
-
{isDark ?
|
|
89
|
+
<Icon name={isDark ? 'sun' : 'moon'} size={18} />
|
|
90
90
|
</button>
|
|
91
91
|
</div>
|
|
92
92
|
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
// SVG Sprite containing all icons used in the theme
|
|
4
|
+
// Icons sourced from Tabler Icons (MIT License)
|
|
5
|
+
export function IconSprite() {
|
|
6
|
+
return (
|
|
7
|
+
<svg xmlns="http://www.w3.org/2000/svg" style={{ display: 'none' }}>
|
|
8
|
+
{/* Menu (hamburger) */}
|
|
9
|
+
<symbol id="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
10
|
+
<path d="M4 6h16M4 12h16M4 18h16" />
|
|
11
|
+
</symbol>
|
|
12
|
+
|
|
13
|
+
{/* Layout Grid */}
|
|
14
|
+
<symbol id="icon-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
15
|
+
<rect x="3" y="3" width="7" height="7" rx="1" />
|
|
16
|
+
<rect x="14" y="3" width="7" height="7" rx="1" />
|
|
17
|
+
<rect x="3" y="14" width="7" height="7" rx="1" />
|
|
18
|
+
<rect x="14" y="14" width="7" height="7" rx="1" />
|
|
19
|
+
</symbol>
|
|
20
|
+
|
|
21
|
+
{/* Sun */}
|
|
22
|
+
<symbol id="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
23
|
+
<circle cx="12" cy="12" r="4" />
|
|
24
|
+
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" />
|
|
25
|
+
</symbol>
|
|
26
|
+
|
|
27
|
+
{/* Moon */}
|
|
28
|
+
<symbol id="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
29
|
+
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9z" />
|
|
30
|
+
</symbol>
|
|
31
|
+
|
|
32
|
+
{/* Maximize */}
|
|
33
|
+
<symbol id="icon-maximize" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
34
|
+
<path d="M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3" />
|
|
35
|
+
</symbol>
|
|
36
|
+
|
|
37
|
+
{/* Minimize */}
|
|
38
|
+
<symbol id="icon-minimize" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
39
|
+
<path d="M8 3v3a2 2 0 0 1-2 2H3M21 8h-3a2 2 0 0 1-2-2V3M3 16h3a2 2 0 0 1 2 2v3M16 21v-3a2 2 0 0 1 2-2h3" />
|
|
40
|
+
</symbol>
|
|
41
|
+
|
|
42
|
+
{/* X (close) */}
|
|
43
|
+
<symbol id="icon-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
44
|
+
<path d="M18 6L6 18M6 6l12 12" />
|
|
45
|
+
</symbol>
|
|
46
|
+
|
|
47
|
+
{/* Chevron Right */}
|
|
48
|
+
<symbol id="icon-chevron-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
49
|
+
<path d="M9 18l6-6-6-6" />
|
|
50
|
+
</symbol>
|
|
51
|
+
|
|
52
|
+
{/* File */}
|
|
53
|
+
<symbol id="icon-file" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
54
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
55
|
+
<path d="M14 2v6h6M16 13H8M16 17H8M10 9H8" />
|
|
56
|
+
</symbol>
|
|
57
|
+
|
|
58
|
+
{/* Folder */}
|
|
59
|
+
<symbol id="icon-folder" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
60
|
+
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
|
|
61
|
+
</symbol>
|
|
62
|
+
|
|
63
|
+
{/* Smartphone (mobile) */}
|
|
64
|
+
<symbol id="icon-mobile" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
65
|
+
<rect x="5" y="2" width="14" height="20" rx="2" ry="2" />
|
|
66
|
+
<path d="M12 18h.01" />
|
|
67
|
+
</symbol>
|
|
68
|
+
|
|
69
|
+
{/* Tablet */}
|
|
70
|
+
<symbol id="icon-tablet" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
71
|
+
<rect x="4" y="2" width="16" height="20" rx="2" ry="2" />
|
|
72
|
+
<path d="M12 18h.01" />
|
|
73
|
+
</symbol>
|
|
74
|
+
|
|
75
|
+
{/* Monitor (desktop) */}
|
|
76
|
+
<symbol id="icon-desktop" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
77
|
+
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
|
|
78
|
+
<path d="M8 21h8M12 17v4" />
|
|
79
|
+
</symbol>
|
|
80
|
+
|
|
81
|
+
{/* Sliders (adjustments) */}
|
|
82
|
+
<symbol id="icon-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
83
|
+
<path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6" />
|
|
84
|
+
</symbol>
|
|
85
|
+
|
|
86
|
+
{/* Loader (spinner) */}
|
|
87
|
+
<symbol id="icon-loader" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
88
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
89
|
+
</symbol>
|
|
90
|
+
|
|
91
|
+
{/* Arrow Left */}
|
|
92
|
+
<symbol id="icon-arrow-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
93
|
+
<path d="M19 12H5M12 19l-7-7 7-7" />
|
|
94
|
+
</symbol>
|
|
95
|
+
</svg>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Icon component that references symbols from the sprite
|
|
100
|
+
interface IconProps {
|
|
101
|
+
name: 'menu' | 'grid' | 'sun' | 'moon' | 'maximize' | 'minimize' | 'x' | 'chevron-right' | 'file' | 'folder' | 'mobile' | 'tablet' | 'desktop' | 'sliders' | 'loader' | 'arrow-left'
|
|
102
|
+
size?: number
|
|
103
|
+
className?: string
|
|
104
|
+
style?: React.CSSProperties
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function Icon({ name, size = 24, className, style }: IconProps) {
|
|
108
|
+
return (
|
|
109
|
+
<svg
|
|
110
|
+
width={size}
|
|
111
|
+
height={size}
|
|
112
|
+
className={className}
|
|
113
|
+
style={style}
|
|
114
|
+
>
|
|
115
|
+
<use href={`#icon-${name}`} />
|
|
116
|
+
</svg>
|
|
117
|
+
)
|
|
118
|
+
}
|