@docubook/flame 1.3.8 → 1.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/.docu/components/Context.tsx +4 -1
- package/.docu/components/Menu.tsx +63 -0
- package/.docu/components/SidebarGroupHeader.tsx +21 -0
- package/.docu/node/client-routes.ts +2 -2
- package/.docu/node/helpers.ts +22 -1
- package/.docu/node/types.ts +8 -0
- package/README.md +118 -2
- package/docu.schema.json +37 -2
- package/package.json +2 -2
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { cn } from "../node/utils";
|
|
5
5
|
import { Dropdown, DropdownItem } from "@docubook/ui-react/dropdown";
|
|
6
|
-
import { routes } from "../node/client-routes";
|
|
6
|
+
import { routes, config as docuConfig } from "../node/client-routes";
|
|
7
7
|
import { ChevronsUpDown, Check } from "lucide-react";
|
|
8
8
|
import { renderLucideIcon } from "./Lucide";
|
|
9
9
|
|
|
@@ -30,6 +30,9 @@ export function Context({ className }: ContextProps) {
|
|
|
30
30
|
typeof window !== "undefined" ? window.location.pathname : "/"
|
|
31
31
|
);
|
|
32
32
|
const mounted = typeof window !== "undefined";
|
|
33
|
+
|
|
34
|
+
const mode = docuConfig.sidebar?.context || "dropdown";
|
|
35
|
+
if (mode === "separator") return null;
|
|
33
36
|
const activeRoute = pathname.startsWith("/docs") ? getActiveContextRoute(pathname) : undefined;
|
|
34
37
|
const contextRoutes = getContextRoutes();
|
|
35
38
|
const fallbackRoute = routes[0];
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import Sublink from "./Sublink";
|
|
5
|
+
import SidebarGroupHeader from "./SidebarGroupHeader";
|
|
5
6
|
import type { DocuRoute } from "../node/types";
|
|
6
7
|
import { cn } from "../node/utils";
|
|
8
|
+
import { config as docuConfig } from "../node/client-routes";
|
|
7
9
|
|
|
8
10
|
interface MenuProps {
|
|
9
11
|
onNavigate?: () => void;
|
|
@@ -33,6 +35,67 @@ export default function Menu({ onNavigate, className = "", pathname, routes = []
|
|
|
33
35
|
|
|
34
36
|
if (!currentPath.startsWith("/docs")) return null;
|
|
35
37
|
|
|
38
|
+
const mode = docuConfig.sidebar?.context || "dropdown";
|
|
39
|
+
|
|
40
|
+
// Separator mode: render all context sections as group headers + nav items
|
|
41
|
+
if (mode === "separator") {
|
|
42
|
+
const contextRoutes = menuRoutes.filter((r) => r.context);
|
|
43
|
+
|
|
44
|
+
// No context routes defined — fall back to flat list of all routes
|
|
45
|
+
if (contextRoutes.length === 0) {
|
|
46
|
+
return (
|
|
47
|
+
<nav
|
|
48
|
+
aria-label="Documentation navigation"
|
|
49
|
+
className={cn("transition-all duration-200", className)}
|
|
50
|
+
>
|
|
51
|
+
<ul className="flex flex-col gap-1.5 py-4">
|
|
52
|
+
{menuRoutes.map((route) => (
|
|
53
|
+
<li key={route.href}>
|
|
54
|
+
<Sublink
|
|
55
|
+
{...route}
|
|
56
|
+
href={route.href}
|
|
57
|
+
level={0}
|
|
58
|
+
onNavigate={onNavigate}
|
|
59
|
+
parentHref="/docs"
|
|
60
|
+
/>
|
|
61
|
+
</li>
|
|
62
|
+
))}
|
|
63
|
+
</ul>
|
|
64
|
+
</nav>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<nav
|
|
70
|
+
aria-label="Documentation navigation"
|
|
71
|
+
className={cn("transition-all duration-200", className)}
|
|
72
|
+
>
|
|
73
|
+
{contextRoutes.map((route, i) => (
|
|
74
|
+
<div key={route.href} className={i > 0 ? "mt-6 lg:mt-8" : ""}>
|
|
75
|
+
<SidebarGroupHeader
|
|
76
|
+
icon={route.context?.icon}
|
|
77
|
+
title={route.context?.title || route.title}
|
|
78
|
+
/>
|
|
79
|
+
<ul className="border-base-300 flex flex-col gap-1.5 border-l-2 pb-0.5 pl-3 pt-0.5">
|
|
80
|
+
{route.items?.map((item) => (
|
|
81
|
+
<li key={item.href}>
|
|
82
|
+
<Sublink
|
|
83
|
+
{...item}
|
|
84
|
+
href={item.href}
|
|
85
|
+
level={0}
|
|
86
|
+
onNavigate={onNavigate}
|
|
87
|
+
parentHref={`/docs${route.href}`}
|
|
88
|
+
/>
|
|
89
|
+
</li>
|
|
90
|
+
))}
|
|
91
|
+
</ul>
|
|
92
|
+
</div>
|
|
93
|
+
))}
|
|
94
|
+
</nav>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Dropdown mode: render only the active context section
|
|
36
99
|
const isDocsRoot = currentPath === "/docs" || currentPath === "/docs/";
|
|
37
100
|
const currentContext = isDocsRoot
|
|
38
101
|
? menuRoutes[0]?.href.replace(/^\/+|\/+$/, "")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { renderLucideIcon } from "./Lucide";
|
|
2
|
+
|
|
3
|
+
interface SidebarGroupHeaderProps {
|
|
4
|
+
icon?: string;
|
|
5
|
+
title: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default function SidebarGroupHeader({ icon, title }: SidebarGroupHeaderProps) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="sidebar-group-header mb-1.5 flex items-center gap-2.5 font-medium text-gray-900 dark:text-gray-200">
|
|
11
|
+
{icon && (
|
|
12
|
+
<span className="flex h-4 w-4 shrink-0 items-center justify-center">
|
|
13
|
+
{renderLucideIcon(icon, "h-3.5 w-3.5")}
|
|
14
|
+
</span>
|
|
15
|
+
)}
|
|
16
|
+
<h3 className="sidebar-title font-[inherit] text-[length:inherit] leading-[inherit]">
|
|
17
|
+
<span>{title}</span>
|
|
18
|
+
</h3>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { DocuRoute } from "./types";
|
|
1
|
+
import type { DocuRoute, DocuConfig } from "./types";
|
|
2
2
|
import docuConfig from "../../docu.json" with { type: "json" };
|
|
3
3
|
|
|
4
4
|
export const routes: DocuRoute[] = docuConfig.routes || [];
|
|
5
|
-
export const config = docuConfig;
|
|
5
|
+
export const config = docuConfig as unknown as DocuConfig;
|
package/.docu/node/helpers.ts
CHANGED
|
@@ -4,11 +4,32 @@ import type { SocialLink } from "./types";
|
|
|
4
4
|
const docuConfig = loadDocuConfig();
|
|
5
5
|
|
|
6
6
|
export function getEditLink(url: string, filePath: string): string {
|
|
7
|
-
const configPath = docuConfig?.repo?.path ||
|
|
7
|
+
const configPath = docuConfig?.repo?.path || detectPlatformPath(url);
|
|
8
8
|
const encodedPath = filePath.replace(/^\//, "").split("/").map(encodeURIComponent).join("/");
|
|
9
9
|
return `${url}/${configPath}`.replace("{filePath}", encodedPath);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Detect the default edit path template from the repo URL hostname.
|
|
14
|
+
* Supports GitHub, GitLab, Bitbucket, Gitea (cloud + self-hosted),
|
|
15
|
+
* Gogs, Forgejo, and Codeberg.
|
|
16
|
+
* Falls back to Gitea-style for unknown hosts — override via repo.path.
|
|
17
|
+
*/
|
|
18
|
+
export function detectPlatformPath(url: string): string {
|
|
19
|
+
try {
|
|
20
|
+
const host = new URL(url).hostname;
|
|
21
|
+
if (host === "github.com") return "blob/main/{filePath}";
|
|
22
|
+
if (host === "gitlab.com") return "-/blob/main/{filePath}";
|
|
23
|
+
if (host === "bitbucket.org") return "src/main/{filePath}";
|
|
24
|
+
if (host === "gitea.com") return "src/branch/main/{filePath}";
|
|
25
|
+
if (host === "codeberg.org") return "src/branch/main/{filePath}";
|
|
26
|
+
// Gogs, Forgejo, or any self-hosted Gitea-compatible forge
|
|
27
|
+
return "src/branch/main/{filePath}";
|
|
28
|
+
} catch {
|
|
29
|
+
return "blob/main/{filePath}";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
12
33
|
export function isEditEnabled(): boolean {
|
|
13
34
|
return docuConfig?.repo?.edit ?? false;
|
|
14
35
|
}
|
package/.docu/node/types.ts
CHANGED
|
@@ -34,6 +34,13 @@ export interface DocuFooter {
|
|
|
34
34
|
social: SocialLink[];
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
export interface DocuSidebar {
|
|
38
|
+
/** How context sections are displayed.
|
|
39
|
+
* "dropdown" — context switcher dropdown (default)
|
|
40
|
+
* "separator" — inline group headers in sidebar */
|
|
41
|
+
context?: "dropdown" | "separator";
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
export interface RepoConfig {
|
|
38
45
|
url: string;
|
|
39
46
|
path: string;
|
|
@@ -75,6 +82,7 @@ export interface DocuConfig {
|
|
|
75
82
|
navbar: DocuNavbar;
|
|
76
83
|
footer: DocuFooter;
|
|
77
84
|
repo: RepoConfig;
|
|
85
|
+
sidebar?: DocuSidebar;
|
|
78
86
|
routes: DocuRoute[];
|
|
79
87
|
themes?: {
|
|
80
88
|
colors: ThemeConfig;
|
package/README.md
CHANGED
|
@@ -105,14 +105,91 @@ bun run deploy # Build + prepare for GitHub Pages
|
|
|
105
105
|
},
|
|
106
106
|
"repo": {
|
|
107
107
|
"url": "https://github.com/you/repo",
|
|
108
|
-
"path": "blob/main/{filePath}",
|
|
109
108
|
"edit": true
|
|
110
109
|
},
|
|
111
110
|
"routes": []
|
|
112
111
|
}
|
|
113
112
|
```
|
|
114
113
|
|
|
115
|
-
###
|
|
114
|
+
### Repo & Edit Links
|
|
115
|
+
|
|
116
|
+
The `repo` section enables **"Edit this page"** links on every doc page, pointing directly to the source file in your repository.
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"repo": {
|
|
121
|
+
"url": "https://github.com/you/repo",
|
|
122
|
+
"edit": true
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
| Property | Type | Required | Description |
|
|
128
|
+
|----------|------|----------|-------------|
|
|
129
|
+
| `url` | `string` (URI) | ✅ Yes | Base URL of your repository |
|
|
130
|
+
| `edit` | `boolean` | ✅ Yes | Show or hide the edit link on pages |
|
|
131
|
+
| `path` | `string` | No | Path template override — see below |
|
|
132
|
+
|
|
133
|
+
#### Platform Auto-Detection
|
|
134
|
+
|
|
135
|
+
When `path` is omitted, Flame detects the correct path format from `url` automatically:
|
|
136
|
+
|
|
137
|
+
| Platform | Domain | Auto-generated path |
|
|
138
|
+
|----------|--------|---------------------|
|
|
139
|
+
| GitHub | `github.com` | `blob/main/{filePath}` |
|
|
140
|
+
| GitLab | `gitlab.com` | `-/blob/main/{filePath}` |
|
|
141
|
+
| Bitbucket | `bitbucket.org` | `src/main/{filePath}` |
|
|
142
|
+
| Gitea Cloud | `gitea.com` | `src/branch/main/{filePath}` |
|
|
143
|
+
| Codeberg (Forgejo) | `codeberg.org` | `src/branch/main/{filePath}` |
|
|
144
|
+
| Gogs / self-hosted Gitea / Forgejo | any other host | `src/branch/main/{filePath}` |
|
|
145
|
+
|
|
146
|
+
For most single-repo projects this is all you need — just set `url` and `edit: true`.
|
|
147
|
+
|
|
148
|
+
#### When to set `path` manually
|
|
149
|
+
|
|
150
|
+
Override `path` when auto-detection is not enough. The value must contain `{filePath}` as a placeholder:
|
|
151
|
+
|
|
152
|
+
**Monorepo** — docs live in a subdirectory, not the repo root:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"repo": {
|
|
157
|
+
"url": "https://github.com/org/monorepo",
|
|
158
|
+
"path": "blob/main/apps/docs/{filePath}",
|
|
159
|
+
"edit": true
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Non-default branch** — your default branch is not `main`:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"repo": {
|
|
169
|
+
"url": "https://github.com/you/repo",
|
|
170
|
+
"path": "blob/master/{filePath}",
|
|
171
|
+
"edit": true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Self-hosted GitLab on a custom domain** — auto-detect falls back to Gitea format, which is wrong for GitLab:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"repo": {
|
|
181
|
+
"url": "https://git.mycompany.com/team/repo",
|
|
182
|
+
"path": "-/blob/main/{filePath}",
|
|
183
|
+
"edit": true
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
> **Rule of thumb:** if your docs are at the root of the repo and the branch is `main`, skip `path` — auto-detect handles it. Add `path` only when you need to point to a subdirectory, a different branch, or a self-hosted platform with a non-standard URL format.
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
### Homepage
|
|
116
193
|
|
|
117
194
|
The `home` section configures your landing page with a hero section and feature cards:
|
|
118
195
|
|
|
@@ -212,6 +289,43 @@ Theme resolution follows this order (first match wins):
|
|
|
212
289
|
|
|
213
290
|
---
|
|
214
291
|
|
|
292
|
+
### Sidebar
|
|
293
|
+
|
|
294
|
+
Controls how documentation sections appear in the sidebar. Defaults to **dropdown** mode when not configured.
|
|
295
|
+
|
|
296
|
+
To switch to **separator** mode, add `sidebar` to the top level of your `docu.json`:
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"sidebar": {
|
|
301
|
+
"context": "separator"
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
| Mode | Description |
|
|
307
|
+
|------|-------------|
|
|
308
|
+
| `"dropdown"` (default) | Compact view — a dropdown at the top of the sidebar lets users switch between sections. Only the active section's items are shown. |
|
|
309
|
+
| `"separator"` | All sections visible — group header (icon + title) with tree connector line. Items nest under their section. |
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
Default (dropdown) Separator
|
|
313
|
+
┌──────────────────┐ 📖 Guides
|
|
314
|
+
│ 📖 Guides ▼ │ │
|
|
315
|
+
├──────────────────┤ ├─ Introduction
|
|
316
|
+
│ Introduction │ ├─ Installation
|
|
317
|
+
│ Installation │ │
|
|
318
|
+
└──────────────────┘ 🧩 Markdown
|
|
319
|
+
│
|
|
320
|
+
├─ Accordion
|
|
321
|
+
├─ Button
|
|
322
|
+
└─ Card
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
> Omit `sidebar` entirely → dropdown mode. Set `"context": "separator"` → separator mode. Mode is static per page, no runtime switching.
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
215
329
|
### Routes
|
|
216
330
|
|
|
217
331
|
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.
|
|
@@ -241,6 +355,8 @@ To define navigation manually, populate the `routes` array:
|
|
|
241
355
|
|
|
242
356
|
> Manual routes take priority — if `routes` has entries, folder scanning is skipped entirely.
|
|
243
357
|
|
|
358
|
+
> Context on a route (`icon`, `title`, `description`) provides metadata for the sidebar context switcher. In **dropdown** mode it fills the dropdown; in **separator** mode it renders the group header + tree. The icon name must match a [Lucide icon](https://lucide.dev/icons) export (e.g., `"BookOpen"`, `"Layers"`).
|
|
359
|
+
|
|
244
360
|
---
|
|
245
361
|
|
|
246
362
|
## Routing
|
package/docu.schema.json
CHANGED
|
@@ -105,14 +105,49 @@
|
|
|
105
105
|
"description": "Repository configuration for edit links",
|
|
106
106
|
"additionalProperties": false,
|
|
107
107
|
"properties": {
|
|
108
|
-
"url": {
|
|
108
|
+
"url": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"format": "uri",
|
|
111
|
+
"description": "Repository base URL. Supported platforms: GitHub, GitLab, Bitbucket, Gitea (gitea.com), Codeberg (codeberg.org), Gogs, Forgejo, or any self-hosted Gitea-compatible forge.",
|
|
112
|
+
"examples": [
|
|
113
|
+
"https://github.com/owner/repo",
|
|
114
|
+
"https://gitlab.com/owner/repo",
|
|
115
|
+
"https://bitbucket.org/owner/repo",
|
|
116
|
+
"https://gitea.com/owner/repo",
|
|
117
|
+
"https://codeberg.org/owner/repo"
|
|
118
|
+
]
|
|
119
|
+
},
|
|
109
120
|
"path": {
|
|
110
121
|
"type": "string",
|
|
111
|
-
"description": "Path template for edit links.
|
|
122
|
+
"description": "Path template for edit links. Must contain {filePath} as a placeholder. Format: {platform-path}/{optional-subdir}/{filePath}. Leave empty to auto-detect from repo.url hostname.",
|
|
123
|
+
"pattern": "\\{filePath\\}",
|
|
124
|
+
"examples": [
|
|
125
|
+
"blob/main/{filePath}",
|
|
126
|
+
"blob/main/apps/web/{filePath}",
|
|
127
|
+
"blob/develop/{filePath}",
|
|
128
|
+
"-/blob/main/{filePath}",
|
|
129
|
+
"-/blob/main/docs/{filePath}",
|
|
130
|
+
"src/main/{filePath}",
|
|
131
|
+
"src/branch/main/{filePath}",
|
|
132
|
+
"src/branch/main/docs/{filePath}"
|
|
133
|
+
]
|
|
112
134
|
},
|
|
113
135
|
"edit": { "type": "boolean", "description": "Enable edit link on pages" }
|
|
114
136
|
}
|
|
115
137
|
},
|
|
138
|
+
"sidebar": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"description": "Sidebar configuration",
|
|
141
|
+
"additionalProperties": false,
|
|
142
|
+
"properties": {
|
|
143
|
+
"context": {
|
|
144
|
+
"type": "string",
|
|
145
|
+
"enum": ["dropdown", "separator"],
|
|
146
|
+
"default": "dropdown",
|
|
147
|
+
"description": "How context sections are displayed in sidebar — dropdown (switcher) or separator (inline group headers)"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
116
151
|
"routes": {
|
|
117
152
|
"type": "array",
|
|
118
153
|
"description": "Documentation navigation routes. Leave empty for auto-detection from docs/ folder.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docubook/flame",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"react": "^19.2.7",
|
|
53
53
|
"react-dom": "^19.2.7",
|
|
54
54
|
"unified": "^11.0.0",
|
|
55
|
-
"@docubook/core": "^1.7.2",
|
|
56
55
|
"@docubook/mdx-content": "^3.2.2",
|
|
57
56
|
"@docubook/themes-colors": "^0.10.2",
|
|
57
|
+
"@docubook/core": "^1.7.2",
|
|
58
58
|
"@docubook/ui-react": "^0.1.4"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|