@openpkg-ts/fumadocs-adapter 0.6.0 → 0.6.2

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @openpkg-ts/fumadocs-adapter
2
2
 
3
- Fumadocs integration for OpenPkg API documentation. Provides CSS variables and theming for `@openpkg-ts/doc-generator` styled components.
3
+ Fumadocs integration for OpenPkg API documentation. Provides virtual source generation, styled components, and theming.
4
4
 
5
5
  ## Installation
6
6
 
@@ -33,24 +33,30 @@ Tailwind v4 excludes `node_modules` by default. Add this directive to include st
33
33
  @source "../node_modules/@openpkg-ts/doc-generator/dist/**/*.js";
34
34
  ```
35
35
 
36
- Without this, utility classes like `lg:grid-cols-[1fr,minmax(0,420px)]` won't be included in your production build.
37
-
38
- ## Layout Requirements
39
-
40
- Styled components use a two-column layout at the `lg:` breakpoint (1024px). For best results:
41
-
42
- - Content area should be >= 1024px wide
43
- - Consider using `full` layout for API pages or hiding the TOC to maximize content width
44
- - On narrower viewports, the layout automatically stacks vertically
45
-
46
36
  ## Navigation Modes
47
37
 
48
- Two navigation patterns are supported:
38
+ Two navigation patterns are supported via `openpkgSource`:
49
39
 
50
- ### Mode A: Single Page (Stripe-style)
40
+ ### Single-Page Mode
51
41
 
52
42
  All exports on one scrollable page with filters and optional TOC:
53
43
 
44
+ ```ts
45
+ // lib/api-source.ts
46
+ import { loader } from 'fumadocs-core/source';
47
+ import { openpkgSource, type OpenPkg } from '@openpkg-ts/fumadocs-adapter';
48
+ import spec from './openpkg.json';
49
+
50
+ export const apiSource = loader({
51
+ baseUrl: '/docs/api',
52
+ source: openpkgSource({
53
+ spec: spec as OpenPkg,
54
+ baseDir: 'api',
55
+ mode: 'single',
56
+ }),
57
+ });
58
+ ```
59
+
54
60
  ```tsx
55
61
  // app/docs/(api)/api/page.tsx
56
62
  import { FullAPIReferencePage, type OpenPkg } from '@openpkg-ts/fumadocs-adapter';
@@ -61,76 +67,118 @@ export default function APIPage() {
61
67
  <FullAPIReferencePage
62
68
  spec={spec as OpenPkg}
63
69
  title="API Reference"
64
- showTOC // sticky sidebar navigation
65
- showFilters // kind filter buttons (functions, types, etc.)
70
+ showTOC // sticky sidebar navigation
71
+ showFilters // kind filter buttons
66
72
  />
67
73
  );
68
74
  }
69
75
  ```
70
76
 
71
- Props:
72
- - `showTOC` - Enable sticky sidebar with anchor links
73
- - `showFilters` - Show kind filter buttons (functions, classes, etc.)
74
- - `kinds` - Limit to specific export kinds: `['function', 'type']`
75
-
76
- ### Mode B: Index + Individual Pages
77
-
78
- Grid of cards linking to individual pages:
79
-
80
77
  ```tsx
81
- // app/docs/(api)/api/page.tsx (index)
82
- import { ExportIndexPage, type OpenPkg } from '@openpkg-ts/fumadocs-adapter';
83
- import spec from '@/lib/openpkg.json';
84
-
85
- export default function APIIndexPage() {
86
- return (
87
- <ExportIndexPage
88
- spec={spec as OpenPkg}
89
- baseHref="/docs/api"
90
- showSearch // search input
91
- showFilters // category filter buttons
92
- />
93
- );
94
- }
95
-
96
- // app/docs/(api)/api/[kind]/[slug]/page.tsx (detail)
97
- import { FunctionPage, type OpenPkg, type SpecExport } from '@openpkg-ts/fumadocs-adapter';
98
- import spec from '@/lib/openpkg.json';
78
+ // app/docs/(api)/api/layout.tsx
79
+ import { apiSource } from '@/lib/api-source';
80
+ import { DocsLayout } from 'fumadocs-ui/layouts/docs';
99
81
 
100
- export default function ExportDetailPage({ params }) {
101
- const exp = (spec as OpenPkg).exports.find(e => e.id === params.slug);
102
- return <FunctionPage export={exp as SpecExport} spec={spec as OpenPkg} />;
82
+ export default function APILayout({ children }) {
83
+ return <DocsLayout tree={apiSource.pageTree}>{children}</DocsLayout>;
103
84
  }
104
85
  ```
105
86
 
106
- ## Using openpkgSource for Fumadocs Integration
87
+ ### Multi-Page Mode
107
88
 
108
- Generate a page tree for Fumadocs sidebar:
89
+ Index page with cards + individual pages per export:
109
90
 
110
- ```tsx
91
+ ```ts
111
92
  // lib/api-source.ts
112
93
  import { loader } from 'fumadocs-core/source';
113
- import { openpkgSource, type OpenPkg } from '@openpkg-ts/fumadocs-adapter';
94
+ import { openpkgSource, openpkgPlugin, type OpenPkg } from '@openpkg-ts/fumadocs-adapter';
114
95
  import spec from './openpkg.json';
115
96
 
116
97
  export const apiSource = loader({
117
- baseUrl: '/docs/api',
118
- source: openpkgSource({ spec: spec as OpenPkg, baseDir: '' }),
98
+ baseUrl: '/docs/reference',
99
+ source: openpkgSource({
100
+ spec: spec as OpenPkg,
101
+ baseDir: 'reference',
102
+ mode: 'pages', // individual pages per export
103
+ indexPage: true, // generate index page with cards
104
+ }),
105
+ plugins: [openpkgPlugin()], // adds kind badges to sidebar
119
106
  });
120
107
  ```
121
108
 
122
- Then use in your layout:
109
+ ```tsx
110
+ // app/docs/(reference)/reference/[[...slug]]/page.tsx
111
+ import { apiSource } from '@/lib/api-source';
112
+ import { notFound } from 'next/navigation';
113
+ import {
114
+ APIPage,
115
+ ExportIndexPage,
116
+ type OpenPkgIndexPageData,
117
+ type OpenPkgPageData,
118
+ } from '@openpkg-ts/fumadocs-adapter';
119
+
120
+ export default async function ReferencePage({ params }) {
121
+ const { slug } = await params;
122
+ const page = apiSource.getPage(slug);
123
+ if (!page) notFound();
124
+
125
+ const data = page.data;
126
+
127
+ // Index page
128
+ if ('isIndex' in data && data.isIndex) {
129
+ return (
130
+ <ExportIndexPage
131
+ spec={data.spec}
132
+ baseHref="/docs/reference"
133
+ />
134
+ );
135
+ }
136
+
137
+ // Individual export page
138
+ return (
139
+ <APIPage
140
+ spec={data.spec}
141
+ id={data.export.id}
142
+ baseHref="/docs/reference"
143
+ />
144
+ );
145
+ }
146
+
147
+ export function generateStaticParams() {
148
+ return apiSource.generateParams();
149
+ }
150
+ ```
123
151
 
124
152
  ```tsx
125
- // app/docs/(api)/layout.tsx
126
- import { DocsLayout } from 'fumadocs-ui/layouts/docs';
153
+ // app/docs/(reference)/reference/layout.tsx
127
154
  import { apiSource } from '@/lib/api-source';
155
+ import { DocsLayout } from 'fumadocs-ui/layouts/docs';
128
156
 
129
- export default function APILayout({ children }) {
157
+ export default function ReferenceLayout({ children }) {
130
158
  return <DocsLayout tree={apiSource.pageTree}>{children}</DocsLayout>;
131
159
  }
132
160
  ```
133
161
 
162
+ ## openpkgSource Options
163
+
164
+ | Option | Type | Default | Description |
165
+ |--------|------|---------|-------------|
166
+ | `spec` | `OpenPkg` | required | The OpenPkg spec object |
167
+ | `baseDir` | `string` | `'api'` | Base directory for generated pages |
168
+ | `mode` | `'pages' \| 'single'` | `'pages'` | Navigation mode |
169
+ | `indexPage` | `boolean` | `true` | Generate index page (pages mode only) |
170
+
171
+ ## FullAPIReferencePage Props
172
+
173
+ | Prop | Type | Default | Description |
174
+ |------|------|---------|-------------|
175
+ | `spec` | `OpenPkg` | required | The OpenPkg spec |
176
+ | `title` | `string` | spec.meta.name | Page title |
177
+ | `description` | `ReactNode` | - | Description below title |
178
+ | `showTOC` | `boolean` | `false` | Show sticky sidebar TOC |
179
+ | `showFilters` | `boolean` | `true` | Show kind filter buttons |
180
+ | `kinds` | `SpecExportKind[]` | all | Filter to specific kinds |
181
+
134
182
  ## CSS Variables Reference
135
183
 
136
184
  ### DocsKit Variables (`--dk-*`)
@@ -144,10 +192,6 @@ export default function APILayout({ children }) {
144
192
  | `--dk-tab-active-foreground` | Active tab text |
145
193
  | `--dk-selection` | Text selection color |
146
194
 
147
- ### CodeHike Variables (`--ch-*`)
148
-
149
- GitHub syntax theme colors (`--ch-0` through `--ch-26`). Automatically adapts to light/dark mode.
150
-
151
195
  ### API Reference Variables (`--api-*`)
152
196
 
153
197
  | Variable | Purpose |
package/dist/index.js CHANGED
@@ -24,6 +24,21 @@ var KIND_LABELS = {
24
24
  reference: "References",
25
25
  external: "External"
26
26
  };
27
+ var KIND_SLUGS = {
28
+ function: "functions",
29
+ class: "classes",
30
+ interface: "interfaces",
31
+ type: "types",
32
+ enum: "enums",
33
+ variable: "variables",
34
+ namespace: "namespaces",
35
+ module: "modules",
36
+ reference: "references",
37
+ external: "externals"
38
+ };
39
+ function pluralizeKind(kind) {
40
+ return KIND_SLUGS[kind] || `${kind}s`;
41
+ }
27
42
  function openpkgSource(options) {
28
43
  const { baseDir = "api", indexPage = true, mode = "pages" } = options;
29
44
  const docs = "getAllExports" in options.spec ? options.spec : createDocs(options.spec);
@@ -67,7 +82,7 @@ function openpkgSource(options) {
67
82
  }
68
83
  for (const kind of KIND_ORDER) {
69
84
  if (groupedByKind.has(kind)) {
70
- rootPages.push(`...${kind}s`);
85
+ rootPages.push(`...${pluralizeKind(kind)}`);
71
86
  }
72
87
  }
73
88
  files.push({
@@ -96,8 +111,9 @@ function openpkgSource(options) {
96
111
  const kindExports = groupedByKind.get(kind);
97
112
  if (!kindExports || kindExports.length === 0)
98
113
  continue;
99
- const kindDir = `${baseDir}/${kind}s`;
100
- const label = KIND_LABELS[kind] || `${kind}s`;
114
+ const kindSlug = pluralizeKind(kind);
115
+ const kindDir = `${baseDir}/${kindSlug}`;
116
+ const label = KIND_LABELS[kind] || kindSlug;
101
117
  const sortedExports = [...kindExports].sort((a, b) => a.name.localeCompare(b.name));
102
118
  files.push({
103
119
  type: "meta",
@@ -112,7 +128,7 @@ function openpkgSource(options) {
112
128
  files.push({
113
129
  type: "page",
114
130
  path: `${kindDir}/${exp.id}.mdx`,
115
- slugs: [kind + "s", exp.id],
131
+ slugs: [kindSlug, exp.id],
116
132
  data: {
117
133
  title: exp.name,
118
134
  description: exp.description,
@@ -162,8 +178,9 @@ function openpkgPlugin(options = {}) {
162
178
  const kind = pageData.export?.kind;
163
179
  if (!kind || !KIND_BADGES[kind])
164
180
  return node;
165
- const badge = createElement(KindBadge, { kind });
166
- const newName = createElement("span", { className: "openpkg-sidebar-item" }, badge, node.name);
181
+ const badge = createElement(KindBadge, { kind, key: "badge" });
182
+ const name = typeof node.name === "string" ? createElement("span", { key: "name" }, node.name) : node.name;
183
+ const newName = createElement("span", { className: "openpkg-sidebar-item" }, badge, name);
167
184
  return { ...node, name: newName };
168
185
  }
169
186
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpkg-ts/fumadocs-adapter",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Fumadocs integration for OpenPkg API documentation",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -28,7 +28,7 @@
28
28
  "typecheck": "tsc --noEmit"
29
29
  },
30
30
  "dependencies": {
31
- "@openpkg-ts/doc-generator": "^0.6.0"
31
+ "@openpkg-ts/doc-generator": "^0.6.2"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "fumadocs-core": "^16.0.0",
@@ -29,6 +29,28 @@
29
29
  --color-secondary-foreground: var(--secondary-foreground);
30
30
  --color-accent: var(--accent);
31
31
  --color-accent-foreground: var(--accent-foreground);
32
+
33
+ /* Docskit code block colors - mapped to CodeHike GitHub theme vars */
34
+ --color-dk-background: var(--ch-16);
35
+ --color-dk-border: var(--ch-23);
36
+ --color-dk-tabs-background: var(--ch-22);
37
+ --color-dk-tab-inactive-foreground: var(--ch-15);
38
+ --color-dk-tab-active-foreground: var(--ch-4);
39
+ --color-dk-selection: var(--ch-20);
40
+ --color-dk-line-number: var(--ch-24);
41
+ --color-dk-active-border: var(--ch-3);
42
+
43
+ /* CodeHike syntax colors for Tailwind utilities */
44
+ --color-ch-0: var(--ch-0);
45
+ --color-ch-1: var(--ch-1);
46
+ --color-ch-2: var(--ch-2);
47
+ --color-ch-3: var(--ch-3);
48
+ --color-ch-4: var(--ch-4);
49
+ --color-ch-5: var(--ch-5);
50
+ --color-ch-6: var(--ch-6);
51
+ --color-ch-7: var(--ch-7);
52
+ --color-ch-8: var(--ch-8);
53
+ --color-ch-9: var(--ch-9);
32
54
  }
33
55
 
34
56
  @layer base {
@@ -47,122 +69,37 @@
47
69
  --secondary-foreground: var(--color-fd-secondary-foreground, var(--color-fd-foreground));
48
70
  --accent: var(--color-fd-accent);
49
71
  --accent-foreground: var(--color-fd-accent-foreground, var(--color-fd-foreground));
50
-
51
- /* Docskit code block colors - mapped to Fumadocs variables */
52
- --dk-background: var(--color-fd-card, hsl(0 0% 3%));
53
- --dk-border: var(--color-fd-border, hsl(0 0% 18%));
54
- --dk-tabs-background: var(--color-fd-muted, hsl(0 0% 10%));
55
- --dk-tab-inactive-foreground: var(--color-fd-muted-foreground, hsl(0 0% 60%));
56
- --dk-tab-active-foreground: var(--color-fd-foreground, hsl(0 0% 98%));
57
- --dk-selection: var(--color-fd-primary, hsl(220 70% 50%));
58
-
59
- /* Line highlight colors */
60
- --dk-line-bg: transparent;
61
- --dk-line-border: transparent;
62
-
63
- /* API Reference Typography */
64
- --api-font-mono: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
65
- --api-font-display: var(--font-sans, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
66
-
67
- /* API Reference Colors (dark mode first) */
68
- --api-bg-primary: var(--color-fd-background, hsl(0 0% 3%));
69
- --api-bg-secondary: var(--color-fd-muted, hsl(0 0% 10%));
70
- --api-bg-card: var(--color-fd-card, hsl(0 0% 6%));
71
- --api-border: var(--color-fd-border, hsl(0 0% 18%));
72
- --api-text-primary: var(--color-fd-foreground, hsl(0 0% 98%));
73
- --api-text-secondary: var(--color-fd-muted-foreground, hsl(0 0% 60%));
74
- --api-accent: var(--color-fd-primary, hsl(220 70% 55%));
75
- --api-accent-muted: var(--color-fd-primary, hsl(220 70% 55%) / 0.15);
76
-
77
- }
78
-
79
- /* Light mode overrides */
80
- .light,
81
- [data-theme="light"] {
82
- --dk-background: var(--color-fd-card, hsl(0 0% 98%));
83
- --dk-border: var(--color-fd-border, hsl(0 0% 85%));
84
- --dk-tabs-background: var(--color-fd-muted, hsl(0 0% 95%));
85
- --dk-tab-inactive-foreground: var(--color-fd-muted-foreground, hsl(0 0% 45%));
86
- --dk-tab-active-foreground: var(--color-fd-foreground, hsl(0 0% 10%));
87
-
88
- /* API Reference Colors (light mode) */
89
- --api-bg-primary: var(--color-fd-background, hsl(0 0% 100%));
90
- --api-bg-secondary: var(--color-fd-muted, hsl(0 0% 96%));
91
- --api-bg-card: var(--color-fd-card, hsl(0 0% 99%));
92
- --api-border: var(--color-fd-border, hsl(0 0% 85%));
93
- --api-text-primary: var(--color-fd-foreground, hsl(0 0% 10%));
94
- --api-text-secondary: var(--color-fd-muted-foreground, hsl(0 0% 45%));
95
- --api-accent: var(--color-fd-primary, hsl(220 70% 50%));
96
- --api-accent-muted: var(--color-fd-primary, hsl(220 70% 50%) / 0.1);
97
-
98
72
  }
99
73
  }
100
74
 
101
- /* Tailwind utility classes for docskit */
75
+ /* Tailwind utility classes for docskit - selection needs special handling */
102
76
  @layer utilities {
103
- .bg-dk-background {
104
- background-color: var(--dk-background);
105
- }
106
- .bg-dk-tabs-background {
107
- background-color: var(--dk-tabs-background);
108
- }
109
- .border-dk-border {
110
- border-color: var(--dk-border);
111
- }
112
- .text-dk-tab-inactive-foreground {
113
- color: var(--dk-tab-inactive-foreground);
114
- }
115
- .text-dk-tab-active-foreground {
116
- color: var(--dk-tab-active-foreground);
117
- }
118
77
  .selection\:bg-dk-selection::selection,
119
78
  .selection\:bg-dk-selection *::selection {
120
- background-color: var(--dk-selection);
121
- }
122
-
123
- /* API Reference utilities */
124
- .font-api-mono {
125
- font-family: var(--api-font-mono);
126
- }
127
- .font-api-display {
128
- font-family: var(--api-font-display);
129
- }
130
- .bg-api-primary {
131
- background-color: var(--api-bg-primary);
132
- }
133
- .bg-api-secondary {
134
- background-color: var(--api-bg-secondary);
135
- }
136
- .bg-api-card {
137
- background-color: var(--api-bg-card);
138
- }
139
- .border-api {
140
- border-color: var(--api-border);
141
- }
142
- .text-api-primary {
143
- color: var(--api-text-primary);
144
- }
145
- .text-api-secondary {
146
- color: var(--api-text-secondary);
147
- }
148
- .text-api-accent {
149
- color: var(--api-accent);
150
- }
151
- .bg-api-accent-muted {
152
- background-color: var(--api-accent-muted);
79
+ background-color: var(--ch-20);
153
80
  }
154
81
  }
155
82
 
156
- /* Stripe-style FunctionPage layout */
83
+ /* API Reference page layouts */
157
84
  @layer components {
158
- .doccov-function-page {
159
- /* Full width within container */
85
+ /* Common padding for all doccov pages - matches Fumadocs OpenAPI */
86
+ .doccov-function-page,
87
+ .doccov-class-page,
88
+ .doccov-interface-page,
89
+ .doccov-enum-page,
90
+ .doccov-variable-page,
91
+ .doccov-index-page,
92
+ .doccov-full-reference-page {
160
93
  width: 100%;
94
+ max-width: 1200px;
95
+ padding: 32px 24px 24px;
161
96
  }
162
97
 
163
98
  /* Mobile-first: stack layout on small screens */
164
99
  @media (max-width: 1023px) {
165
- .doccov-function-page aside {
100
+ .doccov-function-page aside,
101
+ .doccov-class-page aside,
102
+ .doccov-interface-page aside {
166
103
  margin-top: 2rem;
167
104
  position: relative;
168
105
  top: auto;
@@ -173,8 +110,9 @@
173
110
 
174
111
  /* Large screens: two-column with sticky sidebar */
175
112
  @media (min-width: 1024px) {
176
- .doccov-function-page {
177
- /* Prevent overflow in grid */
113
+ .doccov-function-page,
114
+ .doccov-class-page,
115
+ .doccov-interface-page {
178
116
  overflow: visible;
179
117
  }
180
118
  }