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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prev-cli",
3
- "version": "0.17.1",
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",
@@ -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}
@@ -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
- <IconDeviceMobile size={16} />
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
- <IconDeviceTablet size={16} />
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
- <IconDeviceDesktop size={16} />
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
- <IconAdjustmentsHorizontal size={16} />
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
- <IconX size={12} />
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 ? <IconArrowsMinimize size={16} /> : <IconArrowsMaximize size={16} />}
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
- <IconArrowLeft size={16} />
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
- <IconLoader2 size={16} style={{ color: 'var(--fd-primary, #3b82f6)', animation: 'spin 1s linear infinite' }} />
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
- <IconLoader2 size={14} style={{ color: 'var(--fd-primary, #3b82f6)', animation: 'spin 1s linear infinite' }} />
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>
@@ -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 { IconX, IconChevronRight, IconFile, IconFolder } from '@tabler/icons-react'
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}><IconX size={16} /></button>
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}><IconX size={16} /></button>
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
- <IconChevronRight size={14} className={`folder-chevron ${isOpen ? 'open' : ''}`} />
150
- <IconFolder size={14} className="toc-icon" />
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
- <IconFile size={14} className="toc-icon" />
179
+ <Icon name="file" size={14} className="toc-icon" />
180
180
  {item.name}
181
181
  </Link>
182
182
  )
@@ -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 { IconMenu2, IconLayoutGrid, IconSun, IconMoon, IconArrowsMaximize, IconArrowsMinimize } from '@tabler/icons-react'
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
- <IconMenu2 size={18} />
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
- <IconLayoutGrid size={18} />
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 ? <IconArrowsMinimize size={18} /> : <IconArrowsMaximize size={18} />}
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 ? <IconSun size={18} /> : <IconMoon size={18} />}
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
+ }