@docubook/flame 1.0.0-beta.80 → 1.0.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.
Files changed (35) hide show
  1. package/.docu/components/Anchor.tsx +1 -1
  2. package/.docu/components/Breadcrumb.tsx +6 -1
  3. package/.docu/components/Context.tsx +2 -1
  4. package/.docu/components/Navbar.tsx +2 -2
  5. package/.docu/components/Pagination.tsx +15 -2
  6. package/.docu/components/Search.tsx +4 -10
  7. package/.docu/components/Sidebar.tsx +15 -4
  8. package/.docu/components/Theme.tsx +1 -1
  9. package/.docu/components/registry.ts +21 -87
  10. package/.docu/node/build.ts +6 -2
  11. package/.docu/node/client.ts +1 -8
  12. package/.docu/node/html.ts +12 -0
  13. package/.docu/node/parse-tocs.ts +10 -0
  14. package/.docu/node/search-indexer.ts +16 -2
  15. package/.docu/node/server.ts +1 -13
  16. package/.docu/node/utils.ts +6 -4
  17. package/.docu/pages/docs/[[...slug]].tsx +6 -1
  18. package/.docu/styles/globals.css +8 -0
  19. package/README.md +2 -2
  20. package/package.json +9 -5
  21. package/template/docs/index.mdx +123 -17
  22. package/.docu/components/base/breadcrumbs.tsx +0 -64
  23. package/.docu/components/base/collapse.tsx +0 -186
  24. package/.docu/components/base/drawer.tsx +0 -164
  25. package/.docu/components/base/dropdown.tsx +0 -94
  26. package/.docu/components/base/input.tsx +0 -53
  27. package/.docu/components/base/kbd.tsx +0 -51
  28. package/.docu/components/base/modal.tsx +0 -56
  29. package/.docu/components/base/navbar.tsx +0 -132
  30. package/.docu/components/base/pagination/index.ts +0 -21
  31. package/.docu/components/base/pagination/pagination-docs.tsx +0 -39
  32. package/.docu/components/base/pagination/pagination-numbers.tsx +0 -379
  33. package/.docu/components/base/pagination/types.ts +0 -129
  34. package/.docu/components/base/theme-controller.tsx +0 -358
  35. package/.docu/components/base/toggle.tsx +0 -172
@@ -1,22 +1,39 @@
1
1
  ---
2
- title: DocuBook Flame
3
- description: A Bun-native framework for modern documentation experiences.
2
+ title: DocuBook Flame 🔥
3
+ description: Fast as flame — a Bun-native framework for modern documentation experiences.
4
4
  date: 2026-05-24
5
5
  ---
6
6
 
7
7
  **@docubook/flame** is a lightweight runtime for building documentation websites using React, MDX, and filesystem-based routing — all running on Bun.
8
8
 
9
+ No heavy abstractions. No complex tooling. Just a minimal layer between your content and the browser.
10
+
11
+ > **Lightweight** — 📦 ~57 kB packed, ~207 kB unpacked. No bloat, just fire.
12
+
13
+ ## Features
14
+
15
+ - **Bun-native** — instant startup, native TypeScript, fast builds
16
+ - **React-first** — JSX/TSX, hooks, component composition
17
+ - **MDX content** — write Markdown with embedded React components
18
+ - **Filesystem routing** — auto-detect routes from `docs/` folder
19
+ - **Lightweight SSR** — React server-side rendering without a heavy framework
20
+ - **Client hydration** — interactive islands for sidebar, TOC, and MDX components
21
+ - **HMR** — instant reload on docs changes during development
22
+ - **Static build** — pre-render all pages to static HTML for deployment
23
+
9
24
  ## Quick Start
10
25
 
11
26
  ```bash
12
27
  mkdir my-docs && cd my-docs
13
- bun add @docubook/flame
28
+ bun add @docubook/flame@rc
14
29
  bunx flame init
15
30
  bun run dev
16
31
  ```
17
32
 
18
33
  ## Project Structure
19
34
 
35
+ After `flame init`, your project looks like:
36
+
20
37
  ```
21
38
  my-docs/
22
39
  ├── docs/ # Your MDX content
@@ -24,17 +41,17 @@ my-docs/
24
41
  ├── docu.json # Site configuration (navbar, routes, meta)
25
42
  ├── package.json # Dependencies
26
43
  └── .docu/
27
- └── dist/ # Build output (after bun run build)
44
+ └── dist/ # Build output (after `bun run build`)
28
45
  ```
29
46
 
30
47
  ## Commands
31
48
 
32
- | Command | Description |
33
- | ----------------- | -------------------------------- |
34
- | `bun run dev` | Start dev server with HMR |
35
- | `bun run build` | Static build to `.docu/dist/` |
36
- | `bun run preview` | Serve built output locally |
37
- | `bun run deploy` | Build + prepare for GitHub Pages |
49
+ ```bash
50
+ bun run dev # Start dev server with HMR
51
+ bun run build # Static build to .docu/dist/
52
+ bun run preview # Serve built output locally
53
+ bun run deploy # Build + prepare for GitHub Pages
54
+ ```
38
55
 
39
56
  ## Configuration
40
57
 
@@ -57,7 +74,34 @@ my-docs/
57
74
  }
58
75
  ```
59
76
 
60
- When `routes` is empty, navigation is auto-generated from your `docs/` folder structure.
77
+ ### Routes
78
+
79
+ When `routes` is an empty array `[]`, Flame automatically scans your `docs/` folder at build-time and generates the sidebar navigation from the directory structure. Folders become collapsible sections, and `.mdx`/`.md` files become links — sorted alphabetically.
80
+
81
+ To define navigation manually, populate the `routes` array:
82
+
83
+ ```json
84
+ {
85
+ "routes": [
86
+ {
87
+ "title": "Getting Started",
88
+ "href": "/getting-started",
89
+ "noLink": true,
90
+ "context": {
91
+ "icon": "BookOpen",
92
+ "title": "Guides",
93
+ "description": "Set up your Documentation"
94
+ },
95
+ "items": [
96
+ { "title": "Introduction", "href": "/introduction" },
97
+ { "title": "Installation", "href": "/installation" }
98
+ ]
99
+ }
100
+ ]
101
+ }
102
+ ```
103
+
104
+ > Manual routes take priority — if `routes` has entries, folder scanning is skipped entirely.
61
105
 
62
106
  ## Routing
63
107
 
@@ -72,6 +116,45 @@ docs/
72
116
  └── card.mdx → /docs/components/card
73
117
  ```
74
118
 
119
+ ## Assets
120
+
121
+ Place images and static files in `docs/assets/`. They are copied to the build output and accessible at `/docs/assets/`.
122
+
123
+ ```
124
+ docs/
125
+ ├── assets/
126
+ │ └── images/
127
+ │ ├── logo.svg
128
+ │ └── screenshot.png
129
+ └── getting-started/
130
+ └── introduction.mdx
131
+ ```
132
+
133
+ Reference in MDX:
134
+
135
+ ```mdx
136
+ ![Screenshot](/docs/assets/images/screenshot.png)
137
+ ```
138
+
139
+ > The `docs/assets/` directory is excluded from route scanning — files inside it won't appear in the sidebar.
140
+
141
+ ## Architecture
142
+
143
+ - **Bun** — runtime, bundler, file watcher
144
+ - **React + React DOM** — rendering (SSR + client hydration)
145
+ - **@docubook/core** — MDX compilation, rehype/remark plugins
146
+ - **@docubook/mdx-content** — pre-built MDX components
147
+ - **Tailwind CSS + daisyUI** — styling
148
+
149
+ ## Comparison
150
+
151
+ | Framework | Runtime | UI | Approach |
152
+ | ------------------- | ------- | --------- | --------------------------- |
153
+ | Docusaurus | Node.js | React | Full-featured, plugin-heavy |
154
+ | VitePress | Node.js | Vue | Lightweight, Vue-only |
155
+ | Nextra | Node.js | React | Next.js-based |
156
+ | **@docubook/flame** | **Bun** | **React** | **Minimal, Bun-native SSR** |
157
+
75
158
  ## Deployment
76
159
 
77
160
  ### GitHub Pages
@@ -80,27 +163,50 @@ docs/
80
163
  bun run deploy
81
164
  ```
82
165
 
83
- This will run a production build, add `.nojekyll`, and generate `.github/workflows/deploy.yml`.
166
+ This will:
167
+
168
+ 1. Run production build → output to `.docu/dist/`
169
+ 2. Add `.nojekyll` file
170
+ 3. Generate `.github/workflows/deploy.yml` (first run only)
84
171
 
85
172
  Then push to GitHub and enable Pages: **Settings → Pages → Source: GitHub Actions**
86
173
 
87
- ### Other Hosts
174
+ ### Manual / Other Hosts
88
175
 
89
176
  ```bash
90
177
  bun run build
91
178
  ```
92
179
 
93
- Upload `.docu/dist/` to any static hosting (Netlify, Cloudflare Pages, Vercel, S3, etc).
180
+ Upload the contents of `.docu/dist/` to any static hosting (Netlify, Cloudflare Pages, Vercel, S3, etc).
94
181
 
95
182
  ## Environment Variables
96
183
 
97
- Copy `.env.example` to `.env`:
184
+ Copy `.env.example` to `.env` to customize:
98
185
 
99
- ```bash
186
+ ```env
187
+ # Server port (default: 3000)
100
188
  PORT=3000
101
- # SENTRY_DSN=https://your-dsn@sentry.io/project-id
189
+
190
+ # Error Monitoring (optional)
191
+ SENTRY_DSN=https://your-dsn@sentry.io/project-id
192
+ ```
193
+
194
+ ## Error Monitoring (Optional)
195
+
196
+ Flame has built-in [Sentry](https://sentry.io) support for error tracking. To enable:
197
+
198
+ ```bash
199
+ bun add @sentry/bun
102
200
  ```
103
201
 
202
+ Then set environment variables:
203
+
204
+ ```env
205
+ SENTRY_DSN=https://your-dsn@sentry.io/project-id
206
+ ```
207
+
208
+ Errors during dev server and build will be automatically captured. No configuration needed beyond the DSN.
209
+
104
210
  ## Requirements
105
211
 
106
212
  - [Bun](https://bun.sh) >= 1.1.0
@@ -1,64 +0,0 @@
1
- import { ReactNode } from "react";
2
-
3
- export interface BreadcrumbProps {
4
- children: ReactNode;
5
- className?: string;
6
- }
7
-
8
- export interface BreadcrumbItemProps {
9
- children: ReactNode;
10
- className?: string;
11
- }
12
-
13
- export interface BreadcrumbLinkProps {
14
- href?: string;
15
- children: ReactNode;
16
- className?: string;
17
- onClick?: () => void;
18
- }
19
-
20
- export function Breadcrumb({ children, className = "" }: BreadcrumbProps) {
21
- return (
22
- <div className={`breadcrumbs text-sm ${className}`}>
23
- <ul>{children}</ul>
24
- </div>
25
- );
26
- }
27
-
28
- export function BreadcrumbItem({ children, className = "" }: BreadcrumbItemProps) {
29
- return <li className={className}>{children}</li>;
30
- }
31
-
32
- export function BreadcrumbLink({ href, children, className = "", onClick }: BreadcrumbLinkProps) {
33
- const baseClass = "link link-hover";
34
-
35
- if (href) {
36
- return (
37
- <a href={href} className={`${baseClass} ${className}`}>
38
- {children}
39
- </a>
40
- );
41
- }
42
-
43
- if (onClick) {
44
- return (
45
- <button type="button" onClick={onClick} className={`${baseClass} ${className}`}>
46
- {children}
47
- </button>
48
- );
49
- }
50
-
51
- return <span className={className}>{children}</span>;
52
- }
53
-
54
- export function BreadcrumbPage({ children, className = "" }: BreadcrumbItemProps) {
55
- return <span className={className}>{children}</span>;
56
- }
57
-
58
- export function BreadcrumbSeparator({ className = "" }: { className?: string }) {
59
- return <li className={className}></li>;
60
- }
61
-
62
- export function BreadcrumbList({ children }: BreadcrumbItemProps) {
63
- return <>{children}</>;
64
- }
@@ -1,186 +0,0 @@
1
- "use client";
2
-
3
- import { cn } from "../../node/utils";
4
- import { ChevronDown, ChevronRight, Plus, Minus } from "lucide-react";
5
- import { useState, type ReactNode } from "react";
6
-
7
- type CollapseVariant = "arrow" | "plus";
8
-
9
- interface CollapseProps {
10
- title: ReactNode;
11
- children: ReactNode;
12
- defaultOpen?: boolean;
13
- onOpenChange?: (open: boolean) => void;
14
- variant?: CollapseVariant;
15
- className?: string;
16
- titleClassName?: string;
17
- contentClassName?: string;
18
- disabled?: boolean;
19
- }
20
-
21
- export default function Collapse({
22
- title,
23
- children,
24
- defaultOpen = false,
25
- onOpenChange,
26
- variant = "arrow",
27
- className,
28
- titleClassName,
29
- contentClassName,
30
- disabled = false,
31
- }: CollapseProps) {
32
- const [isOpen, setIsOpen] = useState(defaultOpen);
33
-
34
- const handleToggle = () => {
35
- if (disabled) return;
36
- const newState = !isOpen;
37
- setIsOpen(newState);
38
- onOpenChange?.(newState);
39
- };
40
-
41
- const Icon = variant === "arrow" ? (isOpen ? ChevronDown : ChevronRight) : isOpen ? Minus : Plus;
42
-
43
- const variantClass = variant === "arrow" ? "collapse-arrow" : "collapse-plus";
44
-
45
- return (
46
- <div
47
- className={cn(
48
- "bg-base-100 border-base-200 collapse rounded-lg border",
49
- isOpen && "collapse-open",
50
- !isOpen && "collapse-close",
51
- variantClass,
52
- disabled && "cursor-not-allowed opacity-50",
53
- className
54
- )}
55
- >
56
- <input
57
- type="checkbox"
58
- checked={isOpen}
59
- onChange={handleToggle}
60
- disabled={disabled}
61
- aria-expanded={isOpen}
62
- />
63
- <div
64
- className={cn(
65
- "collapse-title text-base-content cursor-pointer font-medium",
66
- titleClassName
67
- )}
68
- onClick={handleToggle}
69
- role="button"
70
- tabIndex={disabled ? -1 : 0}
71
- onKeyDown={(e) => {
72
- if (!disabled && (e.key === "Enter" || e.key === " ")) {
73
- e.preventDefault();
74
- setIsOpen(!isOpen);
75
- }
76
- }}
77
- >
78
- <div className="flex items-center justify-between pr-8">
79
- <span className="flex-1">{title}</span>
80
- <Icon
81
- className={cn(
82
- "text-base-content/60 h-4 w-4 transition-transform duration-200",
83
- isOpen && "rotate-180"
84
- )}
85
- aria-hidden="true"
86
- />
87
- </div>
88
- </div>
89
- <div className={cn("collapse-content text-base-content/80", contentClassName)}>
90
- {children}
91
- </div>
92
- </div>
93
- );
94
- }
95
-
96
- interface AccordionItem {
97
- id: string;
98
- title: ReactNode;
99
- content: ReactNode;
100
- }
101
-
102
- interface AccordionProps {
103
- items: AccordionItem[];
104
- defaultOpen?: string;
105
- variant?: CollapseVariant;
106
- className?: string;
107
- allowMultiple?: boolean;
108
- }
109
-
110
- export function Accordion({
111
- items,
112
- defaultOpen,
113
- variant = "arrow",
114
- className,
115
- allowMultiple = false,
116
- }: AccordionProps) {
117
- const [openIds, setOpenIds] = useState<Set<string>>(
118
- defaultOpen ? new Set([defaultOpen]) : new Set()
119
- );
120
-
121
- const toggle = (id: string) => {
122
- setOpenIds((prev) => {
123
- const next = new Set(prev);
124
- if (next.has(id)) {
125
- next.delete(id);
126
- } else {
127
- if (!allowMultiple) {
128
- next.clear();
129
- }
130
- next.add(id);
131
- }
132
- return next;
133
- });
134
- };
135
-
136
- return (
137
- <div className={cn("flex flex-col gap-3", className)}>
138
- {items.map((item) => {
139
- const isOpen = openIds.has(item.id);
140
- const Icon =
141
- variant === "arrow" ? (isOpen ? ChevronDown : ChevronRight) : isOpen ? Minus : Plus;
142
-
143
- return (
144
- <div
145
- key={item.id}
146
- className={cn(
147
- "bg-base-100 border-base-200 collapse rounded-lg border",
148
- isOpen && "collapse-open",
149
- !isOpen && "collapse-close",
150
- variant === "arrow" ? "collapse-arrow" : "collapse-plus"
151
- )}
152
- >
153
- <input type="checkbox" checked={isOpen} onChange={() => toggle(item.id)} />
154
- <div
155
- className="collapse-title text-base-content cursor-pointer pr-8 font-medium"
156
- onClick={() => toggle(item.id)}
157
- role="button"
158
- tabIndex={0}
159
- onKeyDown={(e) => {
160
- if (e.key === "Enter" || e.key === " ") {
161
- e.preventDefault();
162
- toggle(item.id);
163
- }
164
- }}
165
- >
166
- <div className="flex items-center justify-between">
167
- <span className="flex-1">{item.title}</span>
168
- <Icon
169
- className={cn(
170
- "text-base-content/60 h-4 w-4 transition-transform duration-200",
171
- isOpen && "rotate-180"
172
- )}
173
- aria-hidden="true"
174
- />
175
- </div>
176
- </div>
177
- <div className="collapse-content text-base-content/80">{item.content}</div>
178
- </div>
179
- );
180
- })}
181
- </div>
182
- );
183
- }
184
-
185
- export { Collapse };
186
- export type { CollapseProps, AccordionItem, AccordionProps };
@@ -1,164 +0,0 @@
1
- "use client";
2
-
3
- import { cn } from "../../node/utils";
4
- import { X } from "lucide-react";
5
- import { useState, type ReactNode } from "react";
6
-
7
- type DrawerSide = "left" | "right";
8
-
9
- interface DrawerProps {
10
- id: string;
11
- children: ReactNode;
12
- defaultOpen?: boolean;
13
- side?: DrawerSide;
14
- breakpoint?: string;
15
- withOverlay?: boolean;
16
- overlayClassName?: string;
17
- sideClassName?: string;
18
- contentClassName?: string;
19
- className?: string;
20
- }
21
-
22
- export default function Drawer({
23
- id,
24
- children,
25
- defaultOpen = false,
26
- side = "left",
27
- breakpoint = "lg",
28
- withOverlay = true,
29
- overlayClassName,
30
- sideClassName,
31
- contentClassName,
32
- className,
33
- }: DrawerProps) {
34
- const [isOpen, setIsOpen] = useState(defaultOpen);
35
-
36
- const toggleDrawer = () => setIsOpen(!isOpen);
37
- const closeDrawer = () => setIsOpen(false);
38
-
39
- const sidePositionClass = side === "right" ? "drawer-end" : "";
40
-
41
- return (
42
- <div
43
- className={cn(
44
- "drawer",
45
- breakpoint ? `${breakpoint}:drawer-open` : "",
46
- sidePositionClass,
47
- className
48
- )}
49
- >
50
- <input
51
- id={id}
52
- type="checkbox"
53
- className="drawer-toggle"
54
- checked={isOpen}
55
- onChange={toggleDrawer}
56
- aria-label="Toggle drawer"
57
- />
58
-
59
- <div className={cn("drawer-content flex flex-col", contentClassName)}>
60
- {typeof children === "function"
61
- ? (
62
- children as (props: {
63
- open: boolean;
64
- toggle: () => void;
65
- close: () => void;
66
- }) => ReactNode
67
- )({ open: isOpen, toggle: toggleDrawer, close: closeDrawer })
68
- : children}
69
- </div>
70
-
71
- <div className={cn("drawer-side", sideClassName)}>
72
- {withOverlay && (
73
- <label
74
- htmlFor={id}
75
- aria-label="Close sidebar"
76
- className={cn("drawer-overlay", overlayClassName)}
77
- onClick={closeDrawer}
78
- />
79
- )}
80
-
81
- <div className="bg-base-200 min-h-full w-80 p-4">
82
- <div className="mb-4 flex items-center justify-between">
83
- <span className="text-lg font-semibold">Menu</span>
84
- <button
85
- onClick={closeDrawer}
86
- className="btn btn-ghost btn-sm btn-circle"
87
- aria-label="Close drawer"
88
- >
89
- <X className="h-5 w-5" />
90
- </button>
91
- </div>
92
- </div>
93
- </div>
94
- </div>
95
- );
96
- }
97
-
98
- export function useDrawerState(initialState = false) {
99
- const [isOpen, setIsOpen] = useState(initialState);
100
-
101
- return {
102
- isOpen,
103
- open: () => setIsOpen(true),
104
- close: () => setIsOpen(false),
105
- toggle: () => setIsOpen((prev) => !prev),
106
- };
107
- }
108
-
109
- interface DrawerTriggerProps {
110
- drawerId: string;
111
- children: ReactNode;
112
- className?: string;
113
- }
114
-
115
- export function DrawerTrigger({ drawerId, children, className }: DrawerTriggerProps) {
116
- return (
117
- <label htmlFor={drawerId} className={cn("label cursor-pointer", className)}>
118
- {children}
119
- </label>
120
- );
121
- }
122
-
123
- interface DrawerContentProps {
124
- children: ReactNode;
125
- className?: string;
126
- }
127
-
128
- export function DrawerContent({ children, className }: DrawerContentProps) {
129
- return <div className={cn("drawer-content", className)}>{children}</div>;
130
- }
131
-
132
- interface DrawerSidePanelProps {
133
- id: string;
134
- children: ReactNode;
135
- side?: DrawerSide;
136
- withOverlay?: boolean;
137
- overlayClassName?: string;
138
- className?: string;
139
- }
140
-
141
- export function DrawerSidePanel({
142
- id,
143
- children,
144
- side = "left",
145
- withOverlay = true,
146
- overlayClassName,
147
- className,
148
- }: DrawerSidePanelProps) {
149
- return (
150
- <div className={cn("drawer-side", side === "right" ? "drawer-end" : "", className)}>
151
- {withOverlay && (
152
- <label
153
- htmlFor={id}
154
- aria-label="Close sidebar"
155
- className={cn("drawer-overlay", overlayClassName)}
156
- />
157
- )}
158
- {children}
159
- </div>
160
- );
161
- }
162
-
163
- export { Drawer };
164
- export type { DrawerProps, DrawerSide };