@doccov/fumadocs-adapter 0.0.1
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/bunup.config.ts +10 -0
- package/dist/components/index.d.ts +160 -0
- package/dist/components/index.js +237 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +10 -0
- package/dist/server.d.ts +34 -0
- package/dist/server.js +38 -0
- package/dist/shared/chunk-pqaj3kdh.js +1488 -0
- package/package.json +33 -0
- package/src/components/api-page.tsx +90 -0
- package/src/components/class-page.tsx +165 -0
- package/src/components/code-example.tsx +40 -0
- package/src/components/collapsible-method.tsx +185 -0
- package/src/components/coverage-badge.tsx +80 -0
- package/src/components/enum-page.tsx +86 -0
- package/src/components/examples.tsx +84 -0
- package/src/components/expandable-property.tsx +240 -0
- package/src/components/function-page.tsx +93 -0
- package/src/components/index.ts +51 -0
- package/src/components/interface-page.tsx +94 -0
- package/src/components/members-section.tsx +193 -0
- package/src/components/method-section.tsx +18 -0
- package/src/components/parameter-card.tsx +53 -0
- package/src/components/signature.tsx +108 -0
- package/src/components/type-table.tsx +80 -0
- package/src/components/variable-page.tsx +78 -0
- package/src/index.ts +8 -0
- package/src/server.ts +80 -0
- package/src/styles/docskit.css +130 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface ExamplesSectionProps {
|
|
6
|
+
examples: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function CopyButton({ text }: { text: string }) {
|
|
10
|
+
const [copied, setCopied] = useState(false);
|
|
11
|
+
|
|
12
|
+
const handleCopy = async () => {
|
|
13
|
+
await navigator.clipboard.writeText(text);
|
|
14
|
+
setCopied(true);
|
|
15
|
+
setTimeout(() => setCopied(false), 2000);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<button
|
|
20
|
+
type="button"
|
|
21
|
+
onClick={handleCopy}
|
|
22
|
+
className="absolute top-2 right-2 p-1.5 rounded-md bg-fd-secondary hover:bg-fd-accent text-fd-muted-foreground hover:text-fd-foreground transition-colors opacity-0 group-hover:opacity-100"
|
|
23
|
+
aria-label="Copy code"
|
|
24
|
+
>
|
|
25
|
+
{copied ? (
|
|
26
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
27
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
28
|
+
</svg>
|
|
29
|
+
) : (
|
|
30
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
31
|
+
<path
|
|
32
|
+
strokeLinecap="round"
|
|
33
|
+
strokeLinejoin="round"
|
|
34
|
+
strokeWidth={2}
|
|
35
|
+
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
36
|
+
/>
|
|
37
|
+
</svg>
|
|
38
|
+
)}
|
|
39
|
+
</button>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function ExamplesSection({ examples }: ExamplesSectionProps) {
|
|
44
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
45
|
+
|
|
46
|
+
if (!examples?.length) return null;
|
|
47
|
+
|
|
48
|
+
const showTabs = examples.length > 1;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className="my-6">
|
|
52
|
+
<h3 className="text-lg font-semibold mb-3">Examples</h3>
|
|
53
|
+
|
|
54
|
+
{showTabs && (
|
|
55
|
+
<div className="flex gap-1 mb-2 border-b border-fd-border">
|
|
56
|
+
{examples.map((_, index) => (
|
|
57
|
+
<button
|
|
58
|
+
key={index}
|
|
59
|
+
type="button"
|
|
60
|
+
onClick={() => setActiveIndex(index)}
|
|
61
|
+
className={`px-3 py-1.5 text-sm font-medium transition-colors ${
|
|
62
|
+
activeIndex === index
|
|
63
|
+
? 'text-fd-primary border-b-2 border-fd-primary -mb-px'
|
|
64
|
+
: 'text-fd-muted-foreground hover:text-fd-foreground'
|
|
65
|
+
}`}
|
|
66
|
+
>
|
|
67
|
+
Example {index + 1}
|
|
68
|
+
</button>
|
|
69
|
+
))}
|
|
70
|
+
</div>
|
|
71
|
+
)}
|
|
72
|
+
|
|
73
|
+
<div className="group relative">
|
|
74
|
+
<pre className="overflow-x-auto rounded-lg border border-fd-border bg-fd-secondary p-4">
|
|
75
|
+
<code className="font-mono text-sm text-fd-foreground whitespace-pre">
|
|
76
|
+
{examples[activeIndex]}
|
|
77
|
+
</code>
|
|
78
|
+
</pre>
|
|
79
|
+
<CopyButton text={examples[activeIndex]} />
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import type { SpecSignatureParameter, SpecSchema } from '@openpkg-ts/spec';
|
|
5
|
+
|
|
6
|
+
export interface ExpandablePropertyProps {
|
|
7
|
+
param: SpecSignatureParameter;
|
|
8
|
+
depth?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface NestedPropertyProps {
|
|
12
|
+
name: string;
|
|
13
|
+
schema: SpecSchema;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
depth?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Chevron icon component
|
|
19
|
+
function ChevronIcon({ expanded }: { expanded: boolean }) {
|
|
20
|
+
return (
|
|
21
|
+
<svg
|
|
22
|
+
width="12"
|
|
23
|
+
height="12"
|
|
24
|
+
viewBox="0 0 12 12"
|
|
25
|
+
fill="none"
|
|
26
|
+
className={`transition-transform duration-200 ${expanded ? 'rotate-90' : ''}`}
|
|
27
|
+
>
|
|
28
|
+
<path
|
|
29
|
+
d="M4.5 2.5L8 6L4.5 9.5"
|
|
30
|
+
stroke="currentColor"
|
|
31
|
+
strokeWidth="1.5"
|
|
32
|
+
strokeLinecap="round"
|
|
33
|
+
strokeLinejoin="round"
|
|
34
|
+
/>
|
|
35
|
+
</svg>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function formatType(schema: SpecSchema): string {
|
|
40
|
+
if (!schema) return 'unknown';
|
|
41
|
+
if (typeof schema === 'string') return schema;
|
|
42
|
+
if (typeof schema === 'object' && schema !== null) {
|
|
43
|
+
const s = schema as Record<string, unknown>;
|
|
44
|
+
|
|
45
|
+
// Use tsType if available (most readable)
|
|
46
|
+
if (s.tsType && typeof s.tsType === 'string') {
|
|
47
|
+
const tsType = s.tsType as string;
|
|
48
|
+
if (tsType.length > 80) {
|
|
49
|
+
return tsType.slice(0, 77) + '...';
|
|
50
|
+
}
|
|
51
|
+
return tsType;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Handle refs
|
|
55
|
+
if (s.$ref && typeof s.$ref === 'string') {
|
|
56
|
+
return (s.$ref as string).replace('#/types/', '');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle enums
|
|
60
|
+
if (s.enum && Array.isArray(s.enum)) {
|
|
61
|
+
const enumVals = (s.enum as unknown[]).map(v => JSON.stringify(v)).join(' | ');
|
|
62
|
+
if (enumVals.length > 50) return enumVals.slice(0, 47) + '...';
|
|
63
|
+
return enumVals;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle anyOf/oneOf
|
|
67
|
+
if (s.anyOf && Array.isArray(s.anyOf)) {
|
|
68
|
+
return (s.anyOf as SpecSchema[]).map(formatType).join(' | ');
|
|
69
|
+
}
|
|
70
|
+
if (s.oneOf && Array.isArray(s.oneOf)) {
|
|
71
|
+
return (s.oneOf as SpecSchema[]).map(formatType).join(' | ');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Handle arrays
|
|
75
|
+
if (s.type === 'array' && s.items) {
|
|
76
|
+
return `${formatType(s.items as SpecSchema)}[]`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Handle basic types
|
|
80
|
+
if (s.type) return String(s.type);
|
|
81
|
+
}
|
|
82
|
+
return 'unknown';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getNestedProperties(schema: SpecSchema): Record<string, SpecSchema> | null {
|
|
86
|
+
if (!schema || typeof schema !== 'object') return null;
|
|
87
|
+
const s = schema as Record<string, unknown>;
|
|
88
|
+
if (s.type === 'object' && s.properties && typeof s.properties === 'object') {
|
|
89
|
+
return s.properties as Record<string, SpecSchema>;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getRequiredFields(schema: SpecSchema): string[] {
|
|
95
|
+
if (!schema || typeof schema !== 'object') return [];
|
|
96
|
+
const s = schema as Record<string, unknown>;
|
|
97
|
+
if (Array.isArray(s.required)) {
|
|
98
|
+
return s.required as string[];
|
|
99
|
+
}
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function countProperties(schema: SpecSchema): number {
|
|
104
|
+
const props = getNestedProperties(schema);
|
|
105
|
+
return props ? Object.keys(props).length : 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Nested property row with expandable nested objects
|
|
110
|
+
*/
|
|
111
|
+
export function NestedProperty({ name, schema, required = false, depth = 0 }: NestedPropertyProps) {
|
|
112
|
+
const [expanded, setExpanded] = useState(false);
|
|
113
|
+
const type = formatType(schema);
|
|
114
|
+
const nestedProps = getNestedProperties(schema);
|
|
115
|
+
const nestedCount = countProperties(schema);
|
|
116
|
+
const hasNested = nestedCount > 0;
|
|
117
|
+
|
|
118
|
+
// Get description from schema
|
|
119
|
+
const schemaObj = schema as Record<string, unknown> | null;
|
|
120
|
+
const description = schemaObj?.description as string | undefined;
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<div className="flex flex-col border-b border-fd-border last:border-0">
|
|
124
|
+
{/* Property row */}
|
|
125
|
+
<div className="flex flex-row items-start gap-2 py-2.5 px-3">
|
|
126
|
+
{/* Name and type */}
|
|
127
|
+
<div className="flex-1 min-w-0">
|
|
128
|
+
<div className="flex items-baseline gap-2 flex-wrap">
|
|
129
|
+
<span className="font-mono text-sm font-medium text-fd-foreground">
|
|
130
|
+
{name}
|
|
131
|
+
{!required && '?'}:
|
|
132
|
+
</span>
|
|
133
|
+
<span className="font-mono text-sm text-fd-muted-foreground">
|
|
134
|
+
{hasNested ? 'object' : type}
|
|
135
|
+
</span>
|
|
136
|
+
</div>
|
|
137
|
+
{description && (
|
|
138
|
+
<p className="text-sm text-fd-muted-foreground mt-0.5 leading-relaxed">
|
|
139
|
+
{description}
|
|
140
|
+
</p>
|
|
141
|
+
)}
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
{/* Expand badge for nested objects */}
|
|
145
|
+
{hasNested && (
|
|
146
|
+
<button
|
|
147
|
+
onClick={() => setExpanded(!expanded)}
|
|
148
|
+
className="flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-md
|
|
149
|
+
bg-fd-muted text-fd-muted-foreground hover:bg-fd-accent hover:text-fd-accent-foreground
|
|
150
|
+
transition-colors cursor-pointer shrink-0"
|
|
151
|
+
>
|
|
152
|
+
<ChevronIcon expanded={expanded} />
|
|
153
|
+
<span>{nestedCount} properties</span>
|
|
154
|
+
</button>
|
|
155
|
+
)}
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
{/* Expanded nested properties */}
|
|
159
|
+
{hasNested && expanded && nestedProps && (
|
|
160
|
+
<div className="mx-3 mb-3 rounded-lg border border-fd-border bg-fd-card/50 overflow-hidden">
|
|
161
|
+
{Object.entries(nestedProps).map(([propName, propSchema]) => (
|
|
162
|
+
<NestedProperty
|
|
163
|
+
key={propName}
|
|
164
|
+
name={propName}
|
|
165
|
+
schema={propSchema}
|
|
166
|
+
required={getRequiredFields(schema).includes(propName)}
|
|
167
|
+
depth={depth + 1}
|
|
168
|
+
/>
|
|
169
|
+
))}
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Top-level expandable property for method parameters
|
|
178
|
+
* Entry point for rendering a parameter with progressive disclosure
|
|
179
|
+
*/
|
|
180
|
+
export function ExpandableProperty({ param, depth = 0 }: ExpandablePropertyProps) {
|
|
181
|
+
const [expanded, setExpanded] = useState(false);
|
|
182
|
+
const type = formatType(param.schema);
|
|
183
|
+
const isOptional = param.required === false;
|
|
184
|
+
const nestedProps = getNestedProperties(param.schema);
|
|
185
|
+
const nestedCount = countProperties(param.schema);
|
|
186
|
+
const hasNested = nestedCount > 0;
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<div className="flex flex-col border-b border-fd-border last:border-0">
|
|
190
|
+
{/* Parameter row */}
|
|
191
|
+
<div className="flex flex-row items-start gap-2 py-3">
|
|
192
|
+
{/* Name and type */}
|
|
193
|
+
<div className="flex-1 min-w-0">
|
|
194
|
+
<div className="flex items-baseline gap-2 flex-wrap">
|
|
195
|
+
<span className="font-mono text-sm font-medium text-fd-foreground">
|
|
196
|
+
{param.name}
|
|
197
|
+
{isOptional && '?'}:
|
|
198
|
+
</span>
|
|
199
|
+
<span className="font-mono text-sm text-fd-muted-foreground">
|
|
200
|
+
{hasNested ? 'object' : type}
|
|
201
|
+
</span>
|
|
202
|
+
</div>
|
|
203
|
+
{param.description && (
|
|
204
|
+
<p className="text-sm text-fd-muted-foreground mt-1 leading-relaxed">
|
|
205
|
+
{param.description}
|
|
206
|
+
</p>
|
|
207
|
+
)}
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
{/* Expand badge for nested objects */}
|
|
211
|
+
{hasNested && (
|
|
212
|
+
<button
|
|
213
|
+
onClick={() => setExpanded(!expanded)}
|
|
214
|
+
className="flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-md
|
|
215
|
+
bg-fd-muted text-fd-muted-foreground hover:bg-fd-accent hover:text-fd-accent-foreground
|
|
216
|
+
transition-colors cursor-pointer shrink-0"
|
|
217
|
+
>
|
|
218
|
+
<ChevronIcon expanded={expanded} />
|
|
219
|
+
<span>{nestedCount} properties</span>
|
|
220
|
+
</button>
|
|
221
|
+
)}
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
{/* Expanded nested properties */}
|
|
225
|
+
{hasNested && expanded && nestedProps && (
|
|
226
|
+
<div className="ml-4 mb-3 rounded-lg border border-fd-border bg-fd-card/50 overflow-hidden">
|
|
227
|
+
{Object.entries(nestedProps).map(([propName, propSchema]) => (
|
|
228
|
+
<NestedProperty
|
|
229
|
+
key={propName}
|
|
230
|
+
name={propName}
|
|
231
|
+
schema={propSchema}
|
|
232
|
+
required={getRequiredFields(param.schema).includes(propName)}
|
|
233
|
+
depth={depth + 1}
|
|
234
|
+
/>
|
|
235
|
+
))}
|
|
236
|
+
</div>
|
|
237
|
+
)}
|
|
238
|
+
</div>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { OpenPkg, SpecExport } from '@openpkg-ts/spec';
|
|
4
|
+
import { ParameterCard } from './parameter-card';
|
|
5
|
+
import { CodeExample } from './code-example';
|
|
6
|
+
import { CoverageBadge } from './coverage-badge';
|
|
7
|
+
|
|
8
|
+
export interface FunctionPageProps {
|
|
9
|
+
export: SpecExport;
|
|
10
|
+
spec: OpenPkg;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function formatSchema(schema: unknown): string {
|
|
14
|
+
if (!schema) return 'unknown';
|
|
15
|
+
if (typeof schema === 'string') return schema;
|
|
16
|
+
if (typeof schema === 'object' && schema !== null) {
|
|
17
|
+
const s = schema as Record<string, unknown>;
|
|
18
|
+
if (s.$ref && typeof s.$ref === 'string') {
|
|
19
|
+
return s.$ref.replace('#/types/', '');
|
|
20
|
+
}
|
|
21
|
+
if (s.tsType) return String(s.tsType);
|
|
22
|
+
if (s.type) return String(s.type);
|
|
23
|
+
}
|
|
24
|
+
return 'unknown';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function FunctionPage({ export: exp, spec }: FunctionPageProps) {
|
|
28
|
+
const sig = exp.signatures?.[0];
|
|
29
|
+
const hasExamples = exp.examples && exp.examples.length > 0;
|
|
30
|
+
const hasParams = sig?.parameters && sig.parameters.length > 0;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="space-y-6 not-prose">
|
|
34
|
+
{/* Description */}
|
|
35
|
+
{exp.description && (
|
|
36
|
+
<p className="text-fd-muted-foreground leading-relaxed">
|
|
37
|
+
{exp.description}
|
|
38
|
+
</p>
|
|
39
|
+
)}
|
|
40
|
+
|
|
41
|
+
{/* Returns */}
|
|
42
|
+
{sig?.returns && (
|
|
43
|
+
<p className="text-fd-muted-foreground text-sm">
|
|
44
|
+
<span className="font-medium text-fd-foreground">Returns:</span>{' '}
|
|
45
|
+
{sig.returns.description || `A ${sig.returns.tsType ?? formatSchema(sig.returns.schema)}`}
|
|
46
|
+
</p>
|
|
47
|
+
)}
|
|
48
|
+
|
|
49
|
+
{/* Two-column layout - using inline styles to override prose */}
|
|
50
|
+
<div
|
|
51
|
+
className="not-prose"
|
|
52
|
+
style={{
|
|
53
|
+
display: hasExamples ? 'grid' : 'block',
|
|
54
|
+
gridTemplateColumns: hasExamples ? 'repeat(2, minmax(0, 1fr))' : undefined,
|
|
55
|
+
gap: '2rem',
|
|
56
|
+
alignItems: 'start'
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{/* Left column: Parameters */}
|
|
60
|
+
<div className="space-y-6">
|
|
61
|
+
{hasParams && (
|
|
62
|
+
<div>
|
|
63
|
+
<h3 className="text-sm font-semibold uppercase tracking-wide text-fd-muted-foreground mb-4">
|
|
64
|
+
Parameters
|
|
65
|
+
</h3>
|
|
66
|
+
<div className="space-y-3">
|
|
67
|
+
{sig.parameters!.map((param, index) => (
|
|
68
|
+
<ParameterCard key={param.name ?? index} param={param} spec={spec} />
|
|
69
|
+
))}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
{/* Right column: Examples */}
|
|
76
|
+
{hasExamples && (
|
|
77
|
+
<div style={{ position: 'sticky', top: '5rem' }}>
|
|
78
|
+
<h3 className="text-sm font-semibold uppercase tracking-wide text-fd-muted-foreground mb-4">
|
|
79
|
+
Example
|
|
80
|
+
</h3>
|
|
81
|
+
<CodeExample
|
|
82
|
+
code={exp.examples![0]}
|
|
83
|
+
filename={`${exp.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}.ts`}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{/* Coverage */}
|
|
90
|
+
{exp.docs && <CoverageBadge docs={exp.docs} />}
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Main component
|
|
2
|
+
export { APIPage } from './api-page';
|
|
3
|
+
export type { APIPageProps } from './api-page';
|
|
4
|
+
|
|
5
|
+
// Page components
|
|
6
|
+
export { FunctionPage } from './function-page';
|
|
7
|
+
export type { FunctionPageProps } from './function-page';
|
|
8
|
+
|
|
9
|
+
export { ClassPage } from './class-page';
|
|
10
|
+
export type { ClassPageProps } from './class-page';
|
|
11
|
+
|
|
12
|
+
export { InterfacePage } from './interface-page';
|
|
13
|
+
export type { InterfacePageProps } from './interface-page';
|
|
14
|
+
|
|
15
|
+
export { EnumPage } from './enum-page';
|
|
16
|
+
export type { EnumPageProps } from './enum-page';
|
|
17
|
+
|
|
18
|
+
export { VariablePage } from './variable-page';
|
|
19
|
+
export type { VariablePageProps } from './variable-page';
|
|
20
|
+
|
|
21
|
+
// Shared components
|
|
22
|
+
export { TypeTable } from './type-table';
|
|
23
|
+
export type { TypeTableProps } from './type-table';
|
|
24
|
+
|
|
25
|
+
export { Signature } from './signature';
|
|
26
|
+
export type { SignatureProps } from './signature';
|
|
27
|
+
|
|
28
|
+
export { ExamplesSection } from './examples';
|
|
29
|
+
export type { ExamplesSectionProps } from './examples';
|
|
30
|
+
|
|
31
|
+
export { CoverageBadge } from './coverage-badge';
|
|
32
|
+
export type { CoverageBadgeProps } from './coverage-badge';
|
|
33
|
+
|
|
34
|
+
export { MembersSection } from './members-section';
|
|
35
|
+
export type { MembersSectionProps } from './members-section';
|
|
36
|
+
|
|
37
|
+
export { ParameterCard } from './parameter-card';
|
|
38
|
+
export type { ParameterCardProps } from './parameter-card';
|
|
39
|
+
|
|
40
|
+
export { CodeExample } from './code-example';
|
|
41
|
+
export type { CodeExampleProps } from './code-example';
|
|
42
|
+
|
|
43
|
+
export { ExpandableProperty, NestedProperty } from './expandable-property';
|
|
44
|
+
export type { ExpandablePropertyProps, NestedPropertyProps } from './expandable-property';
|
|
45
|
+
|
|
46
|
+
export { CollapsibleMethod } from './collapsible-method';
|
|
47
|
+
export type { CollapsibleMethodProps } from './collapsible-method';
|
|
48
|
+
|
|
49
|
+
export { MethodSection } from './method-section';
|
|
50
|
+
export type { MethodSectionProps } from './method-section';
|
|
51
|
+
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { OpenPkg, SpecExport } from '@openpkg-ts/spec';
|
|
4
|
+
import { Signature } from './signature';
|
|
5
|
+
import { TypeTable } from './type-table';
|
|
6
|
+
import { ExamplesSection } from './examples';
|
|
7
|
+
import { CoverageBadge } from './coverage-badge';
|
|
8
|
+
|
|
9
|
+
export interface InterfacePageProps {
|
|
10
|
+
export: SpecExport;
|
|
11
|
+
spec: OpenPkg;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function InterfacePage({ export: exp, spec }: InterfacePageProps) {
|
|
15
|
+
// For interfaces/types, members are the properties
|
|
16
|
+
const properties = exp.members?.filter(
|
|
17
|
+
(m) => m.kind === 'property' || m.kind === 'field' || !m.kind
|
|
18
|
+
);
|
|
19
|
+
const methods = exp.members?.filter((m) => m.kind === 'method' || m.kind === 'function');
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="space-y-6">
|
|
23
|
+
{/* Description */}
|
|
24
|
+
{exp.description && (
|
|
25
|
+
<p className="text-fd-muted-foreground text-base leading-relaxed">{exp.description}</p>
|
|
26
|
+
)}
|
|
27
|
+
|
|
28
|
+
{/* Signature */}
|
|
29
|
+
<section>
|
|
30
|
+
<h2 className="text-xl font-semibold mb-2">Declaration</h2>
|
|
31
|
+
<Signature export={exp} />
|
|
32
|
+
</section>
|
|
33
|
+
|
|
34
|
+
{/* Extends */}
|
|
35
|
+
{exp.extends && (
|
|
36
|
+
<section>
|
|
37
|
+
<h2 className="text-xl font-semibold mb-2">Extends</h2>
|
|
38
|
+
<div className="rounded-lg border border-fd-border bg-fd-card p-4">
|
|
39
|
+
<code className="font-mono text-sm text-fd-primary">{exp.extends}</code>
|
|
40
|
+
</div>
|
|
41
|
+
</section>
|
|
42
|
+
)}
|
|
43
|
+
|
|
44
|
+
{/* Properties */}
|
|
45
|
+
{properties && properties.length > 0 && (
|
|
46
|
+
<section>
|
|
47
|
+
<h2 className="text-xl font-semibold mb-2">Properties</h2>
|
|
48
|
+
<TypeTable items={properties} spec={spec} showRequired={true} />
|
|
49
|
+
</section>
|
|
50
|
+
)}
|
|
51
|
+
|
|
52
|
+
{/* Methods */}
|
|
53
|
+
{methods && methods.length > 0 && (
|
|
54
|
+
<section>
|
|
55
|
+
<h2 className="text-xl font-semibold mb-2">Methods</h2>
|
|
56
|
+
<div className="space-y-4">
|
|
57
|
+
{methods.map((method, index) => {
|
|
58
|
+
const sig = method.signatures?.[0];
|
|
59
|
+
const params = sig?.parameters ?? [];
|
|
60
|
+
const returnType = sig?.returns?.tsType ?? 'void';
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div key={method.name ?? index} className="rounded-lg border border-fd-border p-4">
|
|
64
|
+
<code className="font-mono text-sm text-fd-primary">
|
|
65
|
+
{method.name}(
|
|
66
|
+
{params
|
|
67
|
+
.map((p) => {
|
|
68
|
+
const optional = p.required === false ? '?' : '';
|
|
69
|
+
const type =
|
|
70
|
+
typeof p.schema === 'string' ? p.schema : (p.schema as any)?.tsType ?? 'any';
|
|
71
|
+
return `${p.name}${optional}: ${type}`;
|
|
72
|
+
})
|
|
73
|
+
.join(', ')}
|
|
74
|
+
): {returnType}
|
|
75
|
+
</code>
|
|
76
|
+
{method.description && (
|
|
77
|
+
<p className="text-sm text-fd-muted-foreground mt-2">{method.description}</p>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
})}
|
|
82
|
+
</div>
|
|
83
|
+
</section>
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
{/* Examples */}
|
|
87
|
+
{exp.examples && exp.examples.length > 0 && <ExamplesSection examples={exp.examples} />}
|
|
88
|
+
|
|
89
|
+
{/* Coverage */}
|
|
90
|
+
{exp.docs && <CoverageBadge docs={exp.docs} />}
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|