@usecross/docs 0.11.0 → 0.12.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/dist/index.d.ts +158 -5
- package/dist/index.js +1260 -31
- package/dist/index.js.map +1 -1
- package/dist/ssr.d.ts +1 -1
- package/dist/types-_anC1UJu.d.ts +320 -0
- package/package.json +1 -1
- package/src/components/Sidebar.tsx +171 -28
- package/src/components/TableOfContents.tsx +1 -1
- package/src/components/api/APILayout.tsx +231 -0
- package/src/components/api/APIPage.tsx +216 -0
- package/src/components/api/Breadcrumb.tsx +98 -0
- package/src/components/api/ClassDoc.tsx +257 -0
- package/src/components/api/CodeSpan.tsx +53 -0
- package/src/components/api/Docstring.tsx +269 -0
- package/src/components/api/FunctionDoc.tsx +130 -0
- package/src/components/api/ModuleDoc.tsx +298 -0
- package/src/components/api/ParameterTable.tsx +183 -0
- package/src/components/api/Signature.tsx +317 -0
- package/src/components/api/TableOfContents.tsx +50 -0
- package/src/components/api/index.ts +17 -0
- package/src/components/index.ts +12 -0
- package/src/index.ts +31 -0
- package/src/types.ts +218 -2
- package/dist/types-Qbhh2Ihz.d.ts +0 -136
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import type { GriffeClass, GriffeFunction, GriffeAttribute, GriffeExpression } from '../../types'
|
|
3
|
+
import { Docstring } from './Docstring'
|
|
4
|
+
import { FunctionDoc } from './FunctionDoc'
|
|
5
|
+
import { CodeSpan } from './CodeSpan'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Render a type annotation expression or value to string
|
|
9
|
+
*/
|
|
10
|
+
function renderExpression(expr: GriffeExpression | string | undefined): string {
|
|
11
|
+
if (!expr) return ''
|
|
12
|
+
if (typeof expr === 'string') return expr
|
|
13
|
+
if (expr.str) return expr.str
|
|
14
|
+
if (expr.canonical) return expr.canonical
|
|
15
|
+
|
|
16
|
+
const exprAny = expr as any
|
|
17
|
+
|
|
18
|
+
// Handle ExprName with member reference
|
|
19
|
+
if (expr.name && typeof expr.name === 'string') return expr.name
|
|
20
|
+
|
|
21
|
+
// Handle ExprBoolOp (like `config or StrawberryConfig()`)
|
|
22
|
+
if (exprAny.cls === 'ExprBoolOp' && exprAny.operator && Array.isArray(exprAny.values)) {
|
|
23
|
+
return exprAny.values.map((v: any) => renderExpression(v)).join(` ${exprAny.operator} `)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Handle ExprBinOp (like `type | None`)
|
|
27
|
+
if (exprAny.cls === 'ExprBinOp' && exprAny.left && exprAny.right) {
|
|
28
|
+
const left = renderExpression(exprAny.left)
|
|
29
|
+
const right = renderExpression(exprAny.right)
|
|
30
|
+
const op = exprAny.operator || '|'
|
|
31
|
+
return `${left} ${op} ${right}`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Handle ExprCall (like `StrawberryConfig()`)
|
|
35
|
+
if (exprAny.cls === 'ExprCall' && exprAny.function) {
|
|
36
|
+
const funcName = renderExpression(exprAny.function)
|
|
37
|
+
const args = Array.isArray(exprAny.arguments)
|
|
38
|
+
? exprAny.arguments.map((a: any) => renderExpression(a)).join(', ')
|
|
39
|
+
: ''
|
|
40
|
+
return `${funcName}(${args})`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Handle ExprAttribute (like contextlib.asynccontextmanager)
|
|
44
|
+
if (exprAny.cls === 'ExprAttribute' && Array.isArray(exprAny.values)) {
|
|
45
|
+
return exprAny.values.map((v: any) => renderExpression(v)).join('.')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Handle ExprList and ExprTuple
|
|
49
|
+
if ('elements' in exprAny && Array.isArray(exprAny.elements)) {
|
|
50
|
+
const inner = exprAny.elements.map((el: any) => renderExpression(el)).join(', ')
|
|
51
|
+
return exprAny.cls === 'ExprTuple' ? `(${inner})` : `[${inner}]`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Handle ExprDict
|
|
55
|
+
if (exprAny.cls === 'ExprDict' && Array.isArray(exprAny.keys) && Array.isArray(exprAny.values)) {
|
|
56
|
+
const pairs = exprAny.keys.map((k: any, i: number) =>
|
|
57
|
+
`${renderExpression(k)}: ${renderExpression(exprAny.values[i])}`
|
|
58
|
+
).join(', ')
|
|
59
|
+
return `{${pairs}}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle ExprSubscript (like Dict[str, int])
|
|
63
|
+
if (exprAny.left && exprAny.slice) {
|
|
64
|
+
const left = renderExpression(exprAny.left)
|
|
65
|
+
const slice = renderExpression(exprAny.slice)
|
|
66
|
+
return `${left}[${slice}]`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Handle slice expressions
|
|
70
|
+
if ('slice' in exprAny && exprAny.slice && !exprAny.left) {
|
|
71
|
+
return renderExpression(exprAny.slice)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Fallback for unknown expressions - try to avoid [object Object]
|
|
75
|
+
if (typeof expr === 'object') {
|
|
76
|
+
return JSON.stringify(expr)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return String(expr)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Collapsible method item with arrow indicator (strawberry.rocks style)
|
|
84
|
+
*/
|
|
85
|
+
function CollapsibleMethod({ method }: { method: GriffeFunction }) {
|
|
86
|
+
const [expanded, setExpanded] = useState(false)
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className="border-b border-gray-200 dark:border-gray-700 last:border-b-0">
|
|
90
|
+
<button
|
|
91
|
+
onClick={() => setExpanded(!expanded)}
|
|
92
|
+
className="w-full flex items-center gap-2 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"
|
|
93
|
+
>
|
|
94
|
+
<span className="font-mono text-base font-semibold text-gray-900 dark:text-white">
|
|
95
|
+
{method.name}
|
|
96
|
+
</span>
|
|
97
|
+
<span className="text-gray-400 text-sm">
|
|
98
|
+
{expanded ? '▲' : '▼'}
|
|
99
|
+
</span>
|
|
100
|
+
</button>
|
|
101
|
+
{expanded && (
|
|
102
|
+
<div className="pb-6">
|
|
103
|
+
<FunctionDoc fn={method} isMethod showName={false} />
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
106
|
+
</div>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface ClassDocProps {
|
|
111
|
+
cls: GriffeClass
|
|
112
|
+
/** URL prefix for links */
|
|
113
|
+
prefix?: string
|
|
114
|
+
/** Current path for breadcrumb */
|
|
115
|
+
currentPath?: string
|
|
116
|
+
/** GitHub repo URL for source links */
|
|
117
|
+
githubUrl?: string
|
|
118
|
+
/** Additional CSS class */
|
|
119
|
+
className?: string
|
|
120
|
+
/** Override display path (e.g., for aliases to show alias name instead of target path) */
|
|
121
|
+
displayPath?: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Renders documentation for a class matching strawberry.rocks design.
|
|
126
|
+
* Includes: Title, Constructor, Methods (collapsible), Attributes, and footer.
|
|
127
|
+
*/
|
|
128
|
+
export function ClassDoc({ cls, prefix: _prefix = '/api', currentPath: _currentPath, githubUrl, className = '', displayPath }: ClassDocProps) {
|
|
129
|
+
const members = cls.members ?? {}
|
|
130
|
+
|
|
131
|
+
// Separate members by type
|
|
132
|
+
const methods: GriffeFunction[] = []
|
|
133
|
+
const attributes: GriffeAttribute[] = []
|
|
134
|
+
|
|
135
|
+
for (const member of Object.values(members)) {
|
|
136
|
+
// Skip private members
|
|
137
|
+
if (member.name.startsWith('_') && !member.name.startsWith('__')) continue
|
|
138
|
+
|
|
139
|
+
if (member.kind === 'function') {
|
|
140
|
+
methods.push(member as GriffeFunction)
|
|
141
|
+
} else if (member.kind === 'attribute') {
|
|
142
|
+
attributes.push(member as GriffeAttribute)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Sort methods: __init__ first, then public methods alphabetically, skip other dunders
|
|
147
|
+
const initMethod = methods.find(m => m.name === '__init__')
|
|
148
|
+
const publicMethods = methods
|
|
149
|
+
.filter(m => m.name !== '__init__' && !m.name.startsWith('_'))
|
|
150
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
151
|
+
|
|
152
|
+
// Sort attributes alphabetically, skip private ones
|
|
153
|
+
const publicAttributes = attributes
|
|
154
|
+
.filter(a => !a.name.startsWith('_'))
|
|
155
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
156
|
+
|
|
157
|
+
// Get relative filepath for display (prefer package-relative path)
|
|
158
|
+
const relativeFilepath = cls.relative_package_filepath || cls.relative_filepath || cls.filepath
|
|
159
|
+
|
|
160
|
+
// Build GitHub URL for source link
|
|
161
|
+
const githubSourceUrl = githubUrl && relativeFilepath && cls.lineno
|
|
162
|
+
? `${githubUrl}/blob/main/${relativeFilepath}#L${cls.lineno}-L${cls.endlineno || cls.lineno}`
|
|
163
|
+
: undefined
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div className={className}>
|
|
167
|
+
{/* Title - monospace like strawberry.rocks */}
|
|
168
|
+
<h1 id={cls.name} className="font-mono text-2xl font-normal text-gray-900 dark:text-white mb-8">
|
|
169
|
+
{displayPath || cls.path || cls.name}
|
|
170
|
+
</h1>
|
|
171
|
+
|
|
172
|
+
{/* Constructor section */}
|
|
173
|
+
{initMethod && (
|
|
174
|
+
<section id="constructor" className="mb-8">
|
|
175
|
+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
|
176
|
+
Constructor:
|
|
177
|
+
</h2>
|
|
178
|
+
|
|
179
|
+
{/* Docstring description */}
|
|
180
|
+
{initMethod.docstring && (
|
|
181
|
+
<div className="mb-6">
|
|
182
|
+
<Docstring docstring={initMethod.docstring} showOnlyText />
|
|
183
|
+
</div>
|
|
184
|
+
)}
|
|
185
|
+
|
|
186
|
+
<FunctionDoc fn={initMethod} isMethod showName={false} />
|
|
187
|
+
</section>
|
|
188
|
+
)}
|
|
189
|
+
|
|
190
|
+
{/* Class docstring if no __init__ */}
|
|
191
|
+
{!initMethod && cls.docstring && (
|
|
192
|
+
<section className="mb-8">
|
|
193
|
+
<Docstring docstring={cls.docstring} />
|
|
194
|
+
</section>
|
|
195
|
+
)}
|
|
196
|
+
|
|
197
|
+
{/* Methods section - collapsible */}
|
|
198
|
+
{publicMethods.length > 0 && (
|
|
199
|
+
<section id="methods" className="mb-8">
|
|
200
|
+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
|
201
|
+
Methods:
|
|
202
|
+
</h2>
|
|
203
|
+
<div>
|
|
204
|
+
{publicMethods.map((method) => (
|
|
205
|
+
<CollapsibleMethod key={method.name} method={method} />
|
|
206
|
+
))}
|
|
207
|
+
</div>
|
|
208
|
+
</section>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{/* Attributes section */}
|
|
212
|
+
{publicAttributes.length > 0 && (
|
|
213
|
+
<section id="attributes" className="mb-8">
|
|
214
|
+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
|
215
|
+
Attributes:
|
|
216
|
+
</h2>
|
|
217
|
+
<div className="space-y-2">
|
|
218
|
+
{publicAttributes.map((attr) => (
|
|
219
|
+
<div key={attr.name} className="flex items-baseline gap-2">
|
|
220
|
+
<CodeSpan>{attr.name}:</CodeSpan>
|
|
221
|
+
{attr.annotation && (
|
|
222
|
+
<span className="text-sm text-gray-600 dark:text-gray-400 font-mono">
|
|
223
|
+
{renderExpression(attr.annotation)}
|
|
224
|
+
</span>
|
|
225
|
+
)}
|
|
226
|
+
</div>
|
|
227
|
+
))}
|
|
228
|
+
</div>
|
|
229
|
+
</section>
|
|
230
|
+
)}
|
|
231
|
+
|
|
232
|
+
{/* Footer with file path and GitHub link */}
|
|
233
|
+
{relativeFilepath && (
|
|
234
|
+
<footer className="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700 space-y-4">
|
|
235
|
+
<p className="flex items-center gap-2">
|
|
236
|
+
<span className="text-sm font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
|
237
|
+
File path:
|
|
238
|
+
</span>
|
|
239
|
+
<CodeSpan allowCopy>{relativeFilepath}</CodeSpan>
|
|
240
|
+
</p>
|
|
241
|
+
{githubSourceUrl && (
|
|
242
|
+
<p>
|
|
243
|
+
<a
|
|
244
|
+
href={githubSourceUrl}
|
|
245
|
+
target="_blank"
|
|
246
|
+
rel="noopener noreferrer"
|
|
247
|
+
className="text-sm font-semibold text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 uppercase tracking-wide"
|
|
248
|
+
>
|
|
249
|
+
Open in GitHub
|
|
250
|
+
</a>
|
|
251
|
+
</p>
|
|
252
|
+
)}
|
|
253
|
+
</footer>
|
|
254
|
+
)}
|
|
255
|
+
</div>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { cn } from '../../lib/utils'
|
|
2
|
+
|
|
3
|
+
interface CodeSpanProps {
|
|
4
|
+
children: React.ReactNode
|
|
5
|
+
/** Visual variant */
|
|
6
|
+
variant?: 'default' | 'simple'
|
|
7
|
+
/** Allow copy on click */
|
|
8
|
+
allowCopy?: boolean
|
|
9
|
+
/** Additional CSS class */
|
|
10
|
+
className?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Styled code span component matching strawberry.rocks design.
|
|
15
|
+
* Uses coral/pink color for code badges.
|
|
16
|
+
*/
|
|
17
|
+
export function CodeSpan({ children, variant = 'default', allowCopy = false, className }: CodeSpanProps) {
|
|
18
|
+
const handleCopy = () => {
|
|
19
|
+
if (allowCopy && typeof children === 'string') {
|
|
20
|
+
navigator.clipboard.writeText(children)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (variant === 'simple') {
|
|
25
|
+
return (
|
|
26
|
+
<code
|
|
27
|
+
onClick={allowCopy ? handleCopy : undefined}
|
|
28
|
+
className={cn(
|
|
29
|
+
'font-mono text-[0.9em] font-semibold text-gray-900 dark:text-white',
|
|
30
|
+
allowCopy && 'cursor-pointer hover:text-primary-600 dark:hover:text-primary-400',
|
|
31
|
+
className
|
|
32
|
+
)}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</code>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<code
|
|
41
|
+
onClick={allowCopy ? handleCopy : undefined}
|
|
42
|
+
className={cn(
|
|
43
|
+
'inline-flex items-center px-2 py-0.5 rounded font-mono text-sm',
|
|
44
|
+
'bg-red-50 text-red-600 border border-red-200',
|
|
45
|
+
'dark:bg-red-900/20 dark:text-red-400 dark:border-red-800/50',
|
|
46
|
+
allowCopy && 'cursor-pointer hover:bg-red-100 dark:hover:bg-red-900/30',
|
|
47
|
+
className
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
</code>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import type { GriffeDocstring, GriffeDocstringSection, GriffeDocstringElement, GriffeExpression } from '../../types'
|
|
2
|
+
import { Markdown } from '../Markdown'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Render a type annotation expression to string
|
|
6
|
+
*/
|
|
7
|
+
function renderExpression(expr: GriffeExpression | string | undefined): string {
|
|
8
|
+
if (!expr) return ''
|
|
9
|
+
if (typeof expr === 'string') return expr
|
|
10
|
+
if (expr.str) return expr.str
|
|
11
|
+
if (expr.canonical) return expr.canonical
|
|
12
|
+
|
|
13
|
+
const exprAny = expr as any
|
|
14
|
+
|
|
15
|
+
// Handle ExprName with member reference
|
|
16
|
+
if (expr.name && typeof expr.name === 'string') return expr.name
|
|
17
|
+
|
|
18
|
+
// Handle ExprBoolOp (like `config or StrawberryConfig()`)
|
|
19
|
+
if (exprAny.cls === 'ExprBoolOp' && exprAny.operator && Array.isArray(exprAny.values)) {
|
|
20
|
+
return exprAny.values.map((v: any) => renderExpression(v)).join(` ${exprAny.operator} `)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Handle ExprBinOp (like `type | None`)
|
|
24
|
+
if (exprAny.cls === 'ExprBinOp' && exprAny.left && exprAny.right) {
|
|
25
|
+
const left = renderExpression(exprAny.left)
|
|
26
|
+
const right = renderExpression(exprAny.right)
|
|
27
|
+
const op = exprAny.operator || '|'
|
|
28
|
+
return `${left} ${op} ${right}`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Handle ExprCall (like `StrawberryConfig()`)
|
|
32
|
+
if (exprAny.cls === 'ExprCall' && exprAny.function) {
|
|
33
|
+
const funcName = renderExpression(exprAny.function)
|
|
34
|
+
const args = Array.isArray(exprAny.arguments)
|
|
35
|
+
? exprAny.arguments.map((a: any) => renderExpression(a)).join(', ')
|
|
36
|
+
: ''
|
|
37
|
+
return `${funcName}(${args})`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Handle ExprAttribute (like contextlib.asynccontextmanager)
|
|
41
|
+
if (exprAny.cls === 'ExprAttribute' && Array.isArray(exprAny.values)) {
|
|
42
|
+
return exprAny.values.map((v: any) => renderExpression(v)).join('.')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Handle ExprList and ExprTuple
|
|
46
|
+
if ('elements' in exprAny && Array.isArray(exprAny.elements)) {
|
|
47
|
+
const inner = exprAny.elements.map((el: any) => renderExpression(el)).join(', ')
|
|
48
|
+
return exprAny.cls === 'ExprTuple' ? `(${inner})` : `[${inner}]`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle ExprDict
|
|
52
|
+
if (exprAny.cls === 'ExprDict' && Array.isArray(exprAny.keys) && Array.isArray(exprAny.values)) {
|
|
53
|
+
const pairs = exprAny.keys.map((k: any, i: number) =>
|
|
54
|
+
`${renderExpression(k)}: ${renderExpression(exprAny.values[i])}`
|
|
55
|
+
).join(', ')
|
|
56
|
+
return `{${pairs}}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle ExprSubscript (like Dict[str, int])
|
|
60
|
+
if (exprAny.left && exprAny.slice) {
|
|
61
|
+
const left = renderExpression(exprAny.left)
|
|
62
|
+
const slice = renderExpression(exprAny.slice)
|
|
63
|
+
return `${left}[${slice}]`
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle slice expressions
|
|
67
|
+
if ('slice' in exprAny && exprAny.slice && !exprAny.left) {
|
|
68
|
+
return renderExpression(exprAny.slice)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Fallback for unknown expressions
|
|
72
|
+
if (typeof expr === 'object') {
|
|
73
|
+
return JSON.stringify(expr)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return String(expr)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface DocstringSectionProps {
|
|
80
|
+
section: GriffeDocstringSection
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function DocstringSection({ section }: DocstringSectionProps) {
|
|
84
|
+
switch (section.kind) {
|
|
85
|
+
case 'text':
|
|
86
|
+
return (
|
|
87
|
+
<div className="prose prose-sm dark:prose-invert max-w-none">
|
|
88
|
+
<Markdown content={section.value as string} />
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
case 'parameters':
|
|
93
|
+
return (
|
|
94
|
+
<div className="mt-4">
|
|
95
|
+
<h4 className="text-sm font-semibold text-gray-900 dark:text-white mb-2">Parameters</h4>
|
|
96
|
+
<dl className="space-y-2">
|
|
97
|
+
{(section.value as GriffeDocstringElement[])?.map((param) => (
|
|
98
|
+
<div key={param.name} className="grid grid-cols-[auto_1fr] gap-x-3">
|
|
99
|
+
<dt className="font-mono text-sm">
|
|
100
|
+
<span className="text-orange-600 dark:text-orange-400">{param.name}</span>
|
|
101
|
+
{param.annotation && (
|
|
102
|
+
<span className="text-gray-500 dark:text-gray-400">
|
|
103
|
+
{' '}({renderExpression(param.annotation)})
|
|
104
|
+
</span>
|
|
105
|
+
)}
|
|
106
|
+
</dt>
|
|
107
|
+
<dd className="text-sm text-gray-600 dark:text-gray-300">
|
|
108
|
+
{param.description}
|
|
109
|
+
</dd>
|
|
110
|
+
</div>
|
|
111
|
+
))}
|
|
112
|
+
</dl>
|
|
113
|
+
</div>
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
case 'returns':
|
|
117
|
+
return (
|
|
118
|
+
<div className="mt-4">
|
|
119
|
+
<h4 className="text-sm font-semibold text-gray-900 dark:text-white mb-2">Returns</h4>
|
|
120
|
+
<div className="text-sm text-gray-600 dark:text-gray-300">
|
|
121
|
+
{Array.isArray(section.value) ? (
|
|
122
|
+
(section.value as GriffeDocstringElement[]).map((ret, i) => (
|
|
123
|
+
<div key={i}>
|
|
124
|
+
{ret.annotation && (
|
|
125
|
+
<span className="font-mono text-green-600 dark:text-green-400">
|
|
126
|
+
{renderExpression(ret.annotation)}
|
|
127
|
+
</span>
|
|
128
|
+
)}
|
|
129
|
+
{ret.description && <span> - {ret.description}</span>}
|
|
130
|
+
</div>
|
|
131
|
+
))
|
|
132
|
+
) : (
|
|
133
|
+
section.value
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
case 'raises':
|
|
140
|
+
return (
|
|
141
|
+
<div className="mt-4">
|
|
142
|
+
<h4 className="text-sm font-semibold text-gray-900 dark:text-white mb-2">Raises</h4>
|
|
143
|
+
<dl className="space-y-2">
|
|
144
|
+
{(section.value as GriffeDocstringElement[])?.map((exc, i) => (
|
|
145
|
+
<div key={i} className="grid grid-cols-[auto_1fr] gap-x-3">
|
|
146
|
+
<dt className="font-mono text-sm text-red-600 dark:text-red-400">
|
|
147
|
+
{exc.annotation ? renderExpression(exc.annotation) : exc.name}
|
|
148
|
+
</dt>
|
|
149
|
+
<dd className="text-sm text-gray-600 dark:text-gray-300">
|
|
150
|
+
{exc.description}
|
|
151
|
+
</dd>
|
|
152
|
+
</div>
|
|
153
|
+
))}
|
|
154
|
+
</dl>
|
|
155
|
+
</div>
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
case 'examples':
|
|
159
|
+
return (
|
|
160
|
+
<div className="mt-4">
|
|
161
|
+
<h4 className="text-sm font-semibold text-gray-900 dark:text-white mb-2">Examples</h4>
|
|
162
|
+
<pre className="bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto text-sm">
|
|
163
|
+
<code>{section.value as string}</code>
|
|
164
|
+
</pre>
|
|
165
|
+
</div>
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
case 'attributes':
|
|
169
|
+
return (
|
|
170
|
+
<div className="mt-4">
|
|
171
|
+
<h4 className="text-sm font-semibold text-gray-900 dark:text-white mb-2">Attributes</h4>
|
|
172
|
+
<dl className="space-y-2">
|
|
173
|
+
{(section.value as GriffeDocstringElement[])?.map((attr) => (
|
|
174
|
+
<div key={attr.name} className="grid grid-cols-[auto_1fr] gap-x-3">
|
|
175
|
+
<dt className="font-mono text-sm">
|
|
176
|
+
<span className="text-orange-600 dark:text-orange-400">{attr.name}</span>
|
|
177
|
+
{attr.annotation && (
|
|
178
|
+
<span className="text-gray-500 dark:text-gray-400">
|
|
179
|
+
{' '}({renderExpression(attr.annotation)})
|
|
180
|
+
</span>
|
|
181
|
+
)}
|
|
182
|
+
</dt>
|
|
183
|
+
<dd className="text-sm text-gray-600 dark:text-gray-300">
|
|
184
|
+
{attr.description}
|
|
185
|
+
</dd>
|
|
186
|
+
</div>
|
|
187
|
+
))}
|
|
188
|
+
</dl>
|
|
189
|
+
</div>
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
case 'deprecated':
|
|
193
|
+
return (
|
|
194
|
+
<div className="mt-4 p-3 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg">
|
|
195
|
+
<h4 className="text-sm font-semibold text-yellow-800 dark:text-yellow-200 mb-1">Deprecated</h4>
|
|
196
|
+
<p className="text-sm text-yellow-700 dark:text-yellow-300">{section.value as string}</p>
|
|
197
|
+
</div>
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
case 'admonition':
|
|
201
|
+
return (
|
|
202
|
+
<div className="mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
|
|
203
|
+
{section.title && (
|
|
204
|
+
<h4 className="text-sm font-semibold text-blue-800 dark:text-blue-200 mb-1">{section.title}</h4>
|
|
205
|
+
)}
|
|
206
|
+
<p className="text-sm text-blue-700 dark:text-blue-300">{section.value as string}</p>
|
|
207
|
+
</div>
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
default:
|
|
211
|
+
if (section.title) {
|
|
212
|
+
return (
|
|
213
|
+
<div className="mt-4">
|
|
214
|
+
<h4 className="text-sm font-semibold text-gray-900 dark:text-white mb-2">{section.title}</h4>
|
|
215
|
+
<div className="text-sm text-gray-600 dark:text-gray-300">
|
|
216
|
+
{typeof section.value === 'string' ? section.value : JSON.stringify(section.value)}
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
return null
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
interface DocstringProps {
|
|
226
|
+
docstring: GriffeDocstring | undefined
|
|
227
|
+
/** Show raw text instead of parsed sections */
|
|
228
|
+
raw?: boolean
|
|
229
|
+
/** Only show the text/description part, skip parameters/returns/etc */
|
|
230
|
+
showOnlyText?: boolean
|
|
231
|
+
/** Additional CSS class */
|
|
232
|
+
className?: string
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Renders a parsed docstring with sections for parameters, returns, raises, etc.
|
|
237
|
+
*/
|
|
238
|
+
export function Docstring({ docstring, raw = false, showOnlyText = false, className = '' }: DocstringProps) {
|
|
239
|
+
if (!docstring) return null
|
|
240
|
+
|
|
241
|
+
// If raw mode or no parsed sections, show raw value
|
|
242
|
+
if (raw || !docstring.parsed || docstring.parsed.length === 0) {
|
|
243
|
+
return (
|
|
244
|
+
<div className={`prose prose-sm dark:prose-invert max-w-none ${className}`}>
|
|
245
|
+
<p className="whitespace-pre-wrap text-gray-600 dark:text-gray-300">{docstring.value}</p>
|
|
246
|
+
</div>
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// If showOnlyText, only render the first text section (the description)
|
|
251
|
+
if (showOnlyText) {
|
|
252
|
+
const firstTextSection = docstring.parsed.find(s => s.kind === 'text')
|
|
253
|
+
if (!firstTextSection) return null
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<div className={`prose prose-sm dark:prose-invert max-w-none ${className}`}>
|
|
257
|
+
<Markdown content={firstTextSection.value as string} />
|
|
258
|
+
</div>
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<div className={className}>
|
|
264
|
+
{docstring.parsed.map((section, i) => (
|
|
265
|
+
<DocstringSection key={`${section.kind}-${i}`} section={section} />
|
|
266
|
+
))}
|
|
267
|
+
</div>
|
|
268
|
+
)
|
|
269
|
+
}
|