@stainless-api/docs-ui 0.1.0-beta.2 → 0.1.0-beta.20
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.js +1312 -1871
- package/dist/mcp.cjs +983441 -0
- package/dist/routing.js +4 -4
- package/dist/styles/main.css +743 -747
- package/dist/styles/primitives.css +444 -426
- package/dist/styles/resets.css +33 -41
- package/dist/styles/search.css +265 -248
- package/dist/styles/sidebar.css +58 -60
- package/dist/styles/snippets.css +86 -88
- package/dist/styles/variables.css +85 -89
- package/package.json +19 -10
- package/src/components/breadcrumbs.tsx +1 -1
- package/src/components/chat.tsx +18 -15
- package/src/components/method.tsx +12 -11
- package/src/components/overview.tsx +32 -19
- package/src/components/primitives.tsx +36 -19
- package/src/components/properties.tsx +4 -2
- package/src/components/scripts/dropdown.ts +1 -1
- package/src/components/sdk.tsx +28 -22
- package/src/components/sidebar.tsx +3 -3
- package/src/components/snippets.tsx +29 -11
- package/src/contexts/component-generics.tsx +10 -15
- package/src/contexts/docs.tsx +15 -4
- package/src/contexts/index.tsx +8 -5
- package/src/contexts/markdown.tsx +7 -6
- package/src/contexts/search.tsx +4 -5
- package/src/hooks/use-strict-context.tsx +16 -0
- package/src/languages/go.tsx +3 -3
- package/src/languages/http.tsx +31 -23
- package/src/languages/index.ts +7 -7
- package/src/languages/java.tsx +4 -4
- package/src/languages/python.tsx +12 -9
- package/src/languages/ruby.tsx +20 -13
- package/src/languages/typescript.tsx +18 -12
- package/src/markdown/index.ts +17 -12
- package/src/markdown/utils.ts +6 -3
- package/src/routing.ts +9 -9
- package/src/search/form.tsx +11 -8
- package/src/search/indexer.ts +17 -15
- package/src/search/mcp.ts +108 -16
- package/src/search/printer.tsx +1 -1
- package/src/search/providers/algolia.ts +5 -5
- package/src/search/providers/fuse.ts +4 -4
- package/src/search/providers/pagefind.ts +1 -1
- package/src/search/providers/walker.ts +5 -3
- package/src/search/results.tsx +9 -7
- package/src/search/types.ts +2 -2
- package/src/style.ts +1 -1
- package/src/styles/main.css +743 -747
- package/src/styles/primitives.css +444 -426
- package/src/styles/resets.css +33 -41
- package/src/styles/search.css +265 -248
- package/src/styles/sidebar.css +58 -60
- package/src/styles/snippets.css +86 -88
- package/src/styles/variables.css +85 -89
- package/src/utils.ts +14 -15
- package/dist/mcp.js +0 -16003
|
@@ -18,23 +18,24 @@ export const HttpMethodIcons: Record<HTTPMethod, LucideIcon> = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export type MethodIconProps = {
|
|
21
|
-
httpMethod
|
|
21
|
+
httpMethod?: string;
|
|
22
22
|
showName?: boolean;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
export function MethodIconBadge({ httpMethod, showName }: MethodIconProps) {
|
|
26
|
-
if (!HttpMethods.includes(httpMethod)) return null;
|
|
26
|
+
if (!httpMethod || !HttpMethods.includes(httpMethod)) return null;
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
29
|
<span
|
|
30
30
|
className={clsx(style.MethodRouteHttpMethod, style.MethodRouteHttpMethodIconOnly)}
|
|
31
31
|
data-method={httpMethod}
|
|
32
32
|
>
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
{HttpMethodIcons[httpMethod] &&
|
|
34
|
+
React.createElement(HttpMethodIcons[httpMethod], {
|
|
35
|
+
size: 14,
|
|
36
|
+
strokeWidth: 3,
|
|
37
|
+
className: style.Icon,
|
|
38
|
+
})}
|
|
38
39
|
{showName && httpMethod}
|
|
39
40
|
</span>
|
|
40
41
|
);
|
|
@@ -42,15 +43,17 @@ export function MethodIconBadge({ httpMethod, showName }: MethodIconProps) {
|
|
|
42
43
|
|
|
43
44
|
export type MethodHeaderProps = {
|
|
44
45
|
title: ReactNode;
|
|
46
|
+
level?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5';
|
|
45
47
|
signature?: ReactNode;
|
|
46
48
|
badges?: ReactNode;
|
|
47
49
|
children?: ReactNode;
|
|
48
50
|
};
|
|
49
51
|
|
|
50
|
-
export function MethodHeader({ title, badges, signature, children }: MethodHeaderProps) {
|
|
52
|
+
export function MethodHeader({ title, badges, signature, children, level }: MethodHeaderProps) {
|
|
53
|
+
const Heading = level ?? 'h5';
|
|
51
54
|
return (
|
|
52
55
|
<div className={style.MethodHeader}>
|
|
53
|
-
<
|
|
56
|
+
<Heading className={style.MethodTitle}>{title}</Heading>
|
|
54
57
|
{badges && <div className={style.MethodBadges}>{badges}</div>}
|
|
55
58
|
{signature}
|
|
56
59
|
{children}
|
|
@@ -133,8 +136,6 @@ export type MethodProps = {
|
|
|
133
136
|
} & React.HTMLProps<HTMLDivElement>;
|
|
134
137
|
|
|
135
138
|
export function Method({ id, header, children, className, ...props }: MethodProps) {
|
|
136
|
-
const Docs = useComponents();
|
|
137
|
-
|
|
138
139
|
return (
|
|
139
140
|
<div id={id} className={clsx(style.Method, className)} tabIndex={0} {...props}>
|
|
140
141
|
{header}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ChevronRight } from 'lucide-react';
|
|
3
|
-
import type * as SDKJSON from '
|
|
3
|
+
import type * as SDKJSON from '@stainless/sdk-json';
|
|
4
4
|
|
|
5
5
|
import { flatResources, getResourceFromSpec } from '../utils';
|
|
6
6
|
import { useDeclaration, useLanguage, useLanguageComponents, useSpec } from '../contexts';
|
|
@@ -54,10 +54,11 @@ export type SDKMethodSummaryProps = {
|
|
|
54
54
|
export function SDKMethodSummary({ method }: SDKMethodSummaryProps) {
|
|
55
55
|
const Docs = useComponents();
|
|
56
56
|
const Lang = useLanguageComponents();
|
|
57
|
-
const decl = useDeclaration(method.stainlessPath);
|
|
57
|
+
const decl = useDeclaration(method.stainlessPath, true);
|
|
58
58
|
|
|
59
59
|
return (
|
|
60
60
|
<Docs.MethodHeader
|
|
61
|
+
level="h5"
|
|
61
62
|
title={<Docs.Link stainlessPath={method.stainlessPath}>{method.summary ?? method.title}</Docs.Link>}
|
|
62
63
|
signature={<Lang.MethodSignature decl={decl} />}
|
|
63
64
|
badges={method.deprecated && <Docs.Badge id="deprecated">Deprecated</Docs.Badge>}
|
|
@@ -75,11 +76,11 @@ export function SDKResource({ resource, parents, showModels }: SDKResourceProps
|
|
|
75
76
|
const spec = useSpec();
|
|
76
77
|
|
|
77
78
|
const methods = Object.values(resource.methods).filter(
|
|
78
|
-
(method) => spec
|
|
79
|
+
(method) => spec?.decls?.[language]?.[method.stainlessPath],
|
|
79
80
|
);
|
|
80
81
|
|
|
81
82
|
const models = Object.values(resource.models).filter(
|
|
82
|
-
(model) => spec
|
|
83
|
+
(model) => spec?.decls?.[language]?.[`${model.stainlessPath} > (schema)`],
|
|
83
84
|
);
|
|
84
85
|
|
|
85
86
|
return (
|
|
@@ -88,13 +89,11 @@ export function SDKResource({ resource, parents, showModels }: SDKResourceProps
|
|
|
88
89
|
<Docs.SDKResourceHeader resource={resource} parents={parents} />
|
|
89
90
|
{methods.length > 0 && (
|
|
90
91
|
<div className={style.ResourceContentGroup}>
|
|
91
|
-
{methods
|
|
92
|
-
.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
</div>
|
|
97
|
-
))}
|
|
92
|
+
{methods.map((method) => (
|
|
93
|
+
<div className={style.MethodSummary} key={method.stainlessPath}>
|
|
94
|
+
<Docs.SDKMethodSummary method={method} />
|
|
95
|
+
</div>
|
|
96
|
+
))}
|
|
98
97
|
</div>
|
|
99
98
|
)}
|
|
100
99
|
|
|
@@ -145,15 +144,29 @@ export function SDKRoot({ stainlessPath }: SDKRootProps) {
|
|
|
145
144
|
const Docs = useComponents();
|
|
146
145
|
|
|
147
146
|
const parsed = parseStainlessPath(stainlessPath);
|
|
148
|
-
const resource = getResourceFromSpec(stainlessPath, spec);
|
|
147
|
+
const resource = spec && getResourceFromSpec(stainlessPath, spec);
|
|
149
148
|
|
|
150
|
-
if (!resource || !parsed)
|
|
149
|
+
if (!resource || !parsed) {
|
|
150
|
+
console.warn(`Could not find resource or parsed path for '${stainlessPath}'`);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
151
153
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
if (parsed.method) {
|
|
155
|
+
const method = resource.methods[parsed.method];
|
|
156
|
+
if (!method) {
|
|
157
|
+
console.warn(`Method '${parsed.method}' not found in resource '${resource.stainlessPath}'`);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return (
|
|
161
|
+
<div className={style.Root}>
|
|
162
|
+
<Docs.SDKMethod method={method} />
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
157
166
|
|
|
158
|
-
return
|
|
167
|
+
return (
|
|
168
|
+
<div className={style.Root}>
|
|
169
|
+
<Docs.SDKOverview resource={resource} />
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
159
172
|
}
|
|
@@ -10,7 +10,12 @@ type JoinProps = { items: ReactNode[]; limit?: number; children: ReactNode };
|
|
|
10
10
|
export function Join({ items, limit, children }: JoinProps) {
|
|
11
11
|
const arr =
|
|
12
12
|
limit && items.length > limit + 1
|
|
13
|
-
? [
|
|
13
|
+
? [
|
|
14
|
+
...items.slice(0, limit),
|
|
15
|
+
<span className={style.Truncation} key="truncation">
|
|
16
|
+
{items.length - limit} more
|
|
17
|
+
</span>,
|
|
18
|
+
]
|
|
14
19
|
: items;
|
|
15
20
|
|
|
16
21
|
return arr.map((item, index) => (
|
|
@@ -31,7 +36,8 @@ type ExpanderProps = {
|
|
|
31
36
|
};
|
|
32
37
|
|
|
33
38
|
export function Expander({ id, open, summary, virtual, muted, children }: ExpanderProps) {
|
|
34
|
-
const
|
|
39
|
+
const settings = useSettings();
|
|
40
|
+
const virtualExpanders = settings?.virtualExpanders;
|
|
35
41
|
|
|
36
42
|
if (virtual || virtualExpanders)
|
|
37
43
|
return (
|
|
@@ -154,8 +160,8 @@ export function Link({ stainlessPath, scroll = true, children, ...props }: LinkP
|
|
|
154
160
|
|
|
155
161
|
const href = React.useMemo(() => {
|
|
156
162
|
if (props.href) return props.href;
|
|
157
|
-
if (stainlessPath) return generateRoute(basePath, language, stainlessPath);
|
|
158
|
-
}, [basePath, language, stainlessPath]);
|
|
163
|
+
if (stainlessPath && basePath) return generateRoute(basePath, language, stainlessPath);
|
|
164
|
+
}, [basePath, language, stainlessPath, props.href]);
|
|
159
165
|
|
|
160
166
|
const handleClick = React.useCallback(
|
|
161
167
|
(e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
@@ -163,7 +169,7 @@ export function Link({ stainlessPath, scroll = true, children, ...props }: LinkP
|
|
|
163
169
|
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
164
170
|
if (href) onNavigate?.(e, { href, language, stainlessPath, scroll });
|
|
165
171
|
},
|
|
166
|
-
[href, scroll, onNavigate],
|
|
172
|
+
[href, scroll, onNavigate, language, props, stainlessPath],
|
|
167
173
|
);
|
|
168
174
|
|
|
169
175
|
if (!href) return children;
|
|
@@ -180,7 +186,10 @@ export type InputProps = {
|
|
|
180
186
|
right?: ReactNode;
|
|
181
187
|
} & React.InputHTMLAttributes<HTMLInputElement>;
|
|
182
188
|
|
|
183
|
-
export const Input = React.forwardRef<HTMLInputElement, InputProps>((
|
|
189
|
+
export const Input = React.forwardRef<HTMLInputElement, InputProps>(function Input(
|
|
190
|
+
{ left, right, ...props },
|
|
191
|
+
ref,
|
|
192
|
+
) {
|
|
184
193
|
return (
|
|
185
194
|
<div className={style.Input}>
|
|
186
195
|
{left}
|
|
@@ -195,15 +204,16 @@ export type ToggleButtonProps = {
|
|
|
195
204
|
selected?: boolean;
|
|
196
205
|
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
197
206
|
|
|
198
|
-
export const ToggleButton = React.forwardRef<HTMLButtonElement, ToggleButtonProps>(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
);
|
|
207
|
+
export const ToggleButton = React.forwardRef<HTMLButtonElement, ToggleButtonProps>(function ToggleButton(
|
|
208
|
+
{ children, selected, ...props },
|
|
209
|
+
ref,
|
|
210
|
+
) {
|
|
211
|
+
return (
|
|
212
|
+
<button {...props} ref={ref} className={style.ToggleButton} data-stldocs-toggle-selected={selected}>
|
|
213
|
+
{children}
|
|
214
|
+
</button>
|
|
215
|
+
);
|
|
216
|
+
});
|
|
207
217
|
|
|
208
218
|
export type ListViewProps<TItem> = {
|
|
209
219
|
items: Array<TItem>;
|
|
@@ -222,6 +232,13 @@ export function ListView<TItem>({ items, itemDelegate, onSelectListItem, ...rest
|
|
|
222
232
|
setKeyboardIndex(0);
|
|
223
233
|
}, [items]);
|
|
224
234
|
|
|
235
|
+
const handleSelect = React.useCallback(() => {
|
|
236
|
+
const item = items[selectedIndex];
|
|
237
|
+
if (item) {
|
|
238
|
+
onSelectListItem?.(item);
|
|
239
|
+
}
|
|
240
|
+
}, [items, selectedIndex, onSelectListItem]);
|
|
241
|
+
|
|
225
242
|
React.useEffect(() => {
|
|
226
243
|
function handleKeyPress(ev: KeyboardEvent) {
|
|
227
244
|
switch (ev.key) {
|
|
@@ -243,17 +260,17 @@ export function ListView<TItem>({ items, itemDelegate, onSelectListItem, ...rest
|
|
|
243
260
|
|
|
244
261
|
case 'Enter':
|
|
245
262
|
ev.preventDefault();
|
|
246
|
-
|
|
263
|
+
handleSelect();
|
|
247
264
|
break;
|
|
248
265
|
}
|
|
249
266
|
}
|
|
250
267
|
|
|
251
268
|
addEventListener('keydown', handleKeyPress);
|
|
252
269
|
return () => removeEventListener('keydown', handleKeyPress);
|
|
253
|
-
}, [items, selectedIndex]);
|
|
270
|
+
}, [items, selectedIndex, handleSelect]);
|
|
254
271
|
|
|
255
272
|
React.useEffect(() => {
|
|
256
|
-
if (!keyboardIndex) {
|
|
273
|
+
if (!keyboardIndex || !itemRef.current || !listRef.current) {
|
|
257
274
|
listRef?.current?.scroll(0, 0);
|
|
258
275
|
return;
|
|
259
276
|
}
|
|
@@ -273,7 +290,7 @@ export function ListView<TItem>({ items, itemDelegate, onSelectListItem, ...rest
|
|
|
273
290
|
ref={index === selectedIndex ? itemRef : null}
|
|
274
291
|
className={style.ListViewItem}
|
|
275
292
|
data-stldocs-listview-selected={index === selectedIndex}
|
|
276
|
-
onClick={
|
|
293
|
+
onClick={handleSelect}
|
|
277
294
|
onMouseMove={() => setSelectedIndex(index)}
|
|
278
295
|
>
|
|
279
296
|
{itemDelegate(item, index === selectedIndex)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
2
|
import { useLanguage, useSettings } from '../contexts';
|
|
3
3
|
import { useComponents } from '../contexts/use-components';
|
|
4
4
|
import style from '../style';
|
|
@@ -72,7 +72,9 @@ export function Property({
|
|
|
72
72
|
const Docs = useComponents();
|
|
73
73
|
const language = useLanguage();
|
|
74
74
|
|
|
75
|
-
const
|
|
75
|
+
const properties = useSettings()?.properties;
|
|
76
|
+
const collapseDescription = properties?.collapseDescription;
|
|
77
|
+
const types = properties?.types;
|
|
76
78
|
|
|
77
79
|
const textContent = (
|
|
78
80
|
<>
|
package/src/components/sdk.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type * as SDKJSON from '
|
|
2
|
+
import type * as SDKJSON from '@stainless/sdk-json';
|
|
3
3
|
import { useComponents } from '../contexts/use-components';
|
|
4
4
|
import {
|
|
5
5
|
useLanguageComponents,
|
|
@@ -32,7 +32,7 @@ export type PropertyModelContextType = {
|
|
|
32
32
|
propertyPath?: string;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
export const PropertyModelContext = React.createContext<PropertyModelContextType>(
|
|
35
|
+
export const PropertyModelContext = React.createContext<PropertyModelContextType>({});
|
|
36
36
|
export function usePropertyModel() {
|
|
37
37
|
return React.useContext(PropertyModelContext);
|
|
38
38
|
}
|
|
@@ -93,16 +93,15 @@ type SDKDeclarationProps = {
|
|
|
93
93
|
export function SDKDeclaration({ path, expand, depth = 0 }: SDKDeclarationProps) {
|
|
94
94
|
const Lang = useLanguageComponents();
|
|
95
95
|
const Docs = useComponents();
|
|
96
|
-
const decl = useDeclaration(path);
|
|
96
|
+
const decl = useDeclaration(path, true);
|
|
97
97
|
const settings = useSettings();
|
|
98
98
|
const model = usePropertyModel();
|
|
99
99
|
const nesting = useReferenceNesting();
|
|
100
100
|
const { selectedPath } = useNavigation();
|
|
101
101
|
|
|
102
|
-
if (!decl) return null;
|
|
103
|
-
|
|
104
102
|
if (decl.kind.endsWith('Reference')) {
|
|
105
|
-
const refId =
|
|
103
|
+
const refId =
|
|
104
|
+
'type' in decl && typeof decl.type !== 'string' && '$ref' in decl.type ? decl.type['$ref'] : undefined;
|
|
106
105
|
if (refId && refId !== path && !nesting.includes(refId)) {
|
|
107
106
|
return (
|
|
108
107
|
<ReferenceNestingContext.Provider value={[...nesting, refId]}>
|
|
@@ -112,11 +111,14 @@ export function SDKDeclaration({ path, expand, depth = 0 }: SDKDeclarationProps)
|
|
|
112
111
|
}
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
const isUnion =
|
|
114
|
+
const isUnion =
|
|
115
|
+
'childrenParentSchema' in decl &&
|
|
116
|
+
decl.childrenParentSchema &&
|
|
117
|
+
['enum', 'union'].includes(decl.childrenParentSchema);
|
|
116
118
|
const id = model?.propertyPath ? `${model.propertyPath} + ${path}` : path;
|
|
117
119
|
const shouldExpand =
|
|
118
|
-
(selectedPath
|
|
119
|
-
(settings
|
|
120
|
+
(selectedPath?.startsWith(path) && nesting.length < 1) ||
|
|
121
|
+
(settings?.properties?.expandDepth && depth <= settings?.properties?.expandDepth && !isUnion) ||
|
|
120
122
|
expand;
|
|
121
123
|
|
|
122
124
|
const content = (
|
|
@@ -125,18 +127,18 @@ export function SDKDeclaration({ path, expand, depth = 0 }: SDKDeclarationProps)
|
|
|
125
127
|
<Docs.Property
|
|
126
128
|
id={id}
|
|
127
129
|
expand={shouldExpand}
|
|
128
|
-
constraints={
|
|
130
|
+
constraints={'constraints' in decl && <Docs.SDKConstraints constraints={decl['constraints']} />}
|
|
129
131
|
declaration={<Lang.Declaration decl={decl} />}
|
|
130
|
-
description={decl['docstring']}
|
|
132
|
+
description={'docstring' in decl ? decl['docstring'] : undefined}
|
|
131
133
|
deprecated={decl.deprecated}
|
|
132
134
|
{...props}
|
|
133
135
|
>
|
|
134
136
|
{'children' in decl &&
|
|
135
|
-
decl.children
|
|
136
|
-
(settings
|
|
137
|
+
(decl.children?.length ?? 0) > 0 &&
|
|
138
|
+
(settings?.properties?.includeModelProperties !== false || !('modelPath' in decl)) && (
|
|
137
139
|
<>
|
|
138
140
|
{isUnion && <div className={style.PropertyAnnotation}>Accepts one of the following:</div>}
|
|
139
|
-
<Docs.SDKChildren paths={decl.children} depth={depth + 1} />
|
|
141
|
+
<Docs.SDKChildren paths={decl.children ?? []} depth={depth + 1} />
|
|
140
142
|
</>
|
|
141
143
|
)}
|
|
142
144
|
</Docs.Property>
|
|
@@ -239,10 +241,11 @@ export type SDKMethodProps = {
|
|
|
239
241
|
export function SDKMethodHeader({ method }: SDKMethodProps) {
|
|
240
242
|
const Docs = useComponents();
|
|
241
243
|
const Lang = useLanguageComponents();
|
|
242
|
-
const decl = useDeclaration(method.stainlessPath);
|
|
244
|
+
const decl = useDeclaration(method.stainlessPath, true);
|
|
243
245
|
|
|
244
246
|
return (
|
|
245
247
|
<Docs.MethodHeader
|
|
248
|
+
level="h1"
|
|
246
249
|
title={method.summary ?? method.title}
|
|
247
250
|
signature={<Lang.MethodSignature decl={decl} />}
|
|
248
251
|
badges={method.deprecated && <Docs.Badge id="deprecated">Deprecated</Docs.Badge>}
|
|
@@ -255,7 +258,7 @@ export function SDKMethodHeader({ method }: SDKMethodProps) {
|
|
|
255
258
|
export function SDKMethodInfo({ method }: SDKMethodProps) {
|
|
256
259
|
const Docs = useComponents();
|
|
257
260
|
const Lang = useLanguageComponents();
|
|
258
|
-
const decl = useDeclaration(method.stainlessPath);
|
|
261
|
+
const decl = useDeclaration(method.stainlessPath, true);
|
|
259
262
|
const spec = useSpec();
|
|
260
263
|
const language = useLanguage();
|
|
261
264
|
|
|
@@ -263,8 +266,10 @@ export function SDKMethodInfo({ method }: SDKMethodProps) {
|
|
|
263
266
|
|
|
264
267
|
function shouldExpand(items: SDKJSON.ID[]) {
|
|
265
268
|
if (items.length > 1) return false;
|
|
266
|
-
const
|
|
267
|
-
|
|
269
|
+
const item = items[0];
|
|
270
|
+
if (!item) return false;
|
|
271
|
+
const decl = spec?.decls?.[language]?.[item];
|
|
272
|
+
return decl && 'children' in decl && decl.children && decl.children.length > 0;
|
|
268
273
|
}
|
|
269
274
|
|
|
270
275
|
return (
|
|
@@ -278,6 +283,7 @@ export function SDKMethodInfo({ method }: SDKMethodProps) {
|
|
|
278
283
|
}
|
|
279
284
|
returns={
|
|
280
285
|
'responseChildren' in decl &&
|
|
286
|
+
decl.responseChildren &&
|
|
281
287
|
decl.responseChildren.length > 0 && (
|
|
282
288
|
<Docs.SDKChildren expand={shouldExpand(decl.responseChildren)} paths={decl.responseChildren} />
|
|
283
289
|
)
|
|
@@ -288,7 +294,7 @@ export function SDKMethodInfo({ method }: SDKMethodProps) {
|
|
|
288
294
|
|
|
289
295
|
export function SDKMethod({ method, transformRequestSnippet }: SDKMethodProps) {
|
|
290
296
|
const Docs = useComponents();
|
|
291
|
-
const decl = useDeclaration(method?.stainlessPath);
|
|
297
|
+
const decl = useDeclaration(method?.stainlessPath, true);
|
|
292
298
|
const layout = useContentPanelLayout();
|
|
293
299
|
|
|
294
300
|
if (!decl) return;
|
|
@@ -323,7 +329,7 @@ export type SDKModelProps = {
|
|
|
323
329
|
|
|
324
330
|
export function SDKModel({ model }: SDKModelProps) {
|
|
325
331
|
const Docs = useComponents();
|
|
326
|
-
const decl = useDeclaration(`${model.stainlessPath} > (schema)
|
|
332
|
+
const decl = useDeclaration(`${model.stainlessPath} > (schema)`, true);
|
|
327
333
|
|
|
328
334
|
if (!decl) return null;
|
|
329
335
|
|
|
@@ -383,9 +389,9 @@ export function SDKLanguageBlock({ language, version, install, links }: SDKLangu
|
|
|
383
389
|
|
|
384
390
|
<div className={style.LanguageBlockInstall} data-stldocs-copy-parent>
|
|
385
391
|
<pre data-stldocs-copy-content>{install}</pre>{' '}
|
|
386
|
-
<
|
|
392
|
+
<Button variant="ghost" size="sm" data-stldocs-snippet-copy>
|
|
387
393
|
<Copy size={16} className={style.Icon} />
|
|
388
|
-
</
|
|
394
|
+
</Button>
|
|
389
395
|
</div>
|
|
390
396
|
|
|
391
397
|
<div className={style.LanguageBlockLinks}>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type * as SDKJSON from '
|
|
2
|
+
import type * as SDKJSON from '@stainless/sdk-json';
|
|
3
3
|
import { useNavigation } from '../contexts';
|
|
4
4
|
import { useComponents } from '../contexts/use-components';
|
|
5
5
|
import { ChevronDown, ChevronRight } from 'lucide-react';
|
|
@@ -50,8 +50,8 @@ export type SidebarResourceProps = {
|
|
|
50
50
|
|
|
51
51
|
export function SidebarResource({ resource }: SidebarResourceProps) {
|
|
52
52
|
const Docs = useComponents();
|
|
53
|
-
const { selectedPath
|
|
54
|
-
const subresources = Object.values(resource.subresources).map((sub) => (
|
|
53
|
+
const { selectedPath } = useNavigation();
|
|
54
|
+
const subresources = Object.values(resource.subresources ?? {}).map((sub) => (
|
|
55
55
|
<SidebarResource resource={sub} key={sub.stainlessPath} />
|
|
56
56
|
));
|
|
57
57
|
|
|
@@ -4,8 +4,9 @@ import { useDeclaration, useHighlight, useLanguage, useSnippet } from '../contex
|
|
|
4
4
|
import { useComponents } from '../contexts/use-components';
|
|
5
5
|
import style from '../style';
|
|
6
6
|
import clsx from 'clsx';
|
|
7
|
-
import type * as SDKJSON from '
|
|
7
|
+
import type * as SDKJSON from '@stainless/sdk-json';
|
|
8
8
|
import type { TransformRequestSnippetFn } from './sdk';
|
|
9
|
+
import { Button } from '@stainless-api/ui-primitives';
|
|
9
10
|
|
|
10
11
|
export type SnippetCodeProps = {
|
|
11
12
|
content: string;
|
|
@@ -58,15 +59,24 @@ export function Snippet({ requestTitle, method, transformRequestSnippet }: Snipp
|
|
|
58
59
|
const language = useLanguage();
|
|
59
60
|
const [CopyButtonIcon, setCopyIcon] = React.useState<LucideIcon>(CopyIcon);
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
const originalSnippet = useSnippet(
|
|
63
|
+
method.stainlessPath,
|
|
64
|
+
undefined,
|
|
65
|
+
language === 'http' ? 'curl' : 'default',
|
|
66
|
+
);
|
|
67
|
+
const decl = useDeclaration(method.stainlessPath, false);
|
|
68
|
+
|
|
69
|
+
if (!originalSnippet) {
|
|
70
|
+
console.warn(`Snippet not found for method '${method.stainlessPath}'`);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
63
73
|
|
|
64
|
-
const signature = 'qualified' in decl ? decl.qualified : undefined;
|
|
74
|
+
const signature = decl && 'qualified' in decl ? decl.qualified : undefined;
|
|
65
75
|
const responses = method.exampleResponses;
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
const snippet = transformRequestSnippet
|
|
78
|
+
? transformRequestSnippet({ snippet: originalSnippet, language })
|
|
79
|
+
: originalSnippet;
|
|
70
80
|
|
|
71
81
|
async function handleCopy() {
|
|
72
82
|
try {
|
|
@@ -90,9 +100,9 @@ export function Snippet({ requestTitle, method, transformRequestSnippet }: Snipp
|
|
|
90
100
|
<h5>{method.summary}</h5>
|
|
91
101
|
</div>
|
|
92
102
|
<div className={style.SnippetRequestTitleContent}>{requestTitle}</div>
|
|
93
|
-
<
|
|
103
|
+
<Button variant="ghost" data-stldocs-snippet-copy>
|
|
94
104
|
<CopyButtonIcon size={16} className={style.Icon} onClick={handleCopy} />
|
|
95
|
-
</
|
|
105
|
+
</Button>
|
|
96
106
|
</div>
|
|
97
107
|
<Docs.SnippetCode content={snippet} signature={signature} />
|
|
98
108
|
</div>
|
|
@@ -112,9 +122,15 @@ export function SnippetResponse({ responses }: { responses: SDKJSON.Method['exam
|
|
|
112
122
|
|
|
113
123
|
const mappedResponses = Object.keys(responses)
|
|
114
124
|
.map((key) => {
|
|
125
|
+
const responseContent = responses[key];
|
|
126
|
+
if (!responseContent) return null;
|
|
127
|
+
|
|
115
128
|
// Get the first response type ie application/json or text/plain
|
|
116
|
-
const responseType = Object.keys(
|
|
117
|
-
|
|
129
|
+
const responseType = Object.keys(responseContent)[0];
|
|
130
|
+
if (!responseType) return null;
|
|
131
|
+
|
|
132
|
+
const response = responseContent[responseType];
|
|
133
|
+
if (!response) return null;
|
|
118
134
|
|
|
119
135
|
const examples = response?.examples;
|
|
120
136
|
|
|
@@ -122,6 +138,8 @@ export function SnippetResponse({ responses }: { responses: SDKJSON.Method['exam
|
|
|
122
138
|
|
|
123
139
|
// Get the first example type, ie Example or html
|
|
124
140
|
const exampleType = Object.keys(examples)[0];
|
|
141
|
+
if (!exampleType) return null;
|
|
142
|
+
|
|
125
143
|
let value = examples[exampleType]?.value;
|
|
126
144
|
|
|
127
145
|
if (!value) return null;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// This file should never import from ../components or ../languages to avoid circular dependencies
|
|
2
2
|
|
|
3
3
|
import * as React from 'react';
|
|
4
|
+
import { createStrictContext } from '../hooks/use-strict-context';
|
|
4
5
|
|
|
5
6
|
type DeepPartialMap<L> = { [K in keyof L]?: Partial<L[K]> };
|
|
6
7
|
|
|
@@ -9,13 +10,10 @@ export type ComponentsContextType<C, L> = {
|
|
|
9
10
|
language: L;
|
|
10
11
|
};
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
-
ComponentContext.displayName = 'ComponentContext';
|
|
13
|
+
const [Provider, useComponentContext] = createStrictContext<ComponentsContextType<any, any>>('Component');
|
|
14
14
|
|
|
15
|
-
export function useComponents<C = unknown>() {
|
|
16
|
-
|
|
17
|
-
if (!ctx) throw new Error('useComponents must be used within a ComponentContext.Provider');
|
|
18
|
-
return ctx.components as C;
|
|
15
|
+
export function useComponents<C = unknown>(): C {
|
|
16
|
+
return useComponentContext().components as C;
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
export function customizeComponents<C, L>(
|
|
@@ -24,13 +22,10 @@ export function customizeComponents<C, L>(
|
|
|
24
22
|
): { components: C; language: L } {
|
|
25
23
|
const mergedComponents = { ...defaults.components, ...(overrides.components ?? {}) } as C;
|
|
26
24
|
|
|
27
|
-
const mergedLanguage = Object.keys(defaults.language as Record<string, unknown>).reduce(
|
|
28
|
-
(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
{} as any as L,
|
|
33
|
-
);
|
|
25
|
+
const mergedLanguage = Object.keys(defaults.language as Record<string, unknown>).reduce((acc, key) => {
|
|
26
|
+
acc[key] = { ...(defaults.language as any)[key], ...((overrides.language as any)?.[key] ?? {}) };
|
|
27
|
+
return acc;
|
|
28
|
+
}, {} as any);
|
|
34
29
|
|
|
35
30
|
return { components: mergedComponents, language: mergedLanguage };
|
|
36
31
|
}
|
|
@@ -42,7 +37,7 @@ export function ComponentProvider<C, L>({
|
|
|
42
37
|
value: ComponentsContextType<C, L>;
|
|
43
38
|
children: React.ReactNode;
|
|
44
39
|
}) {
|
|
45
|
-
return <
|
|
40
|
+
return <Provider value={value}>{children}</Provider>;
|
|
46
41
|
}
|
|
47
42
|
|
|
48
|
-
export {
|
|
43
|
+
export { useComponentContext };
|
package/src/contexts/docs.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { Spec, SpecLanguage } from '
|
|
2
|
+
import type { LanguageDeclNodes, SnippetLanguage, Spec, SpecLanguage } from '@stainless/sdk-json';
|
|
3
3
|
|
|
4
4
|
export type ContentPanelLayout = 'double-pane' | 'single-pane';
|
|
5
5
|
export type PropertySettingsType = {
|
|
@@ -40,13 +40,24 @@ export function useSettings() {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function useSnippet(stainlessPath: string, language?: SpecLanguage, variant?: string) {
|
|
43
|
-
const
|
|
43
|
+
const defaultLanguage = useLanguage();
|
|
44
|
+
const snippetLanguage = [language ?? defaultLanguage, variant ?? 'default'].join('.') as SnippetLanguage;
|
|
44
45
|
|
|
45
46
|
return useSpec()?.snippets?.[snippetLanguage]?.[stainlessPath];
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
type Declaration = LanguageDeclNodes[SpecLanguage];
|
|
50
|
+
export function useDeclaration<Required extends boolean>(
|
|
51
|
+
stainlessPath: string,
|
|
52
|
+
required: Required,
|
|
53
|
+
language?: SpecLanguage,
|
|
54
|
+
): (Required extends true ? never : undefined) | Declaration {
|
|
55
|
+
const defaultLanguage = useLanguage();
|
|
56
|
+
const decl = useSpec()?.decls?.[language ?? defaultLanguage]?.[stainlessPath];
|
|
57
|
+
if (required && !decl) {
|
|
58
|
+
throw new Error(`Declaration not found for '${stainlessPath}'`);
|
|
59
|
+
}
|
|
60
|
+
return decl!;
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
export function useResource(name: string) {
|
package/src/contexts/index.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { ComponentContext } from './component-generics';
|
|
3
1
|
import { useLanguage } from './docs';
|
|
4
2
|
import { LanguageComponentDefinition } from '../languages';
|
|
3
|
+
import { useComponentContext } from './component-generics';
|
|
5
4
|
|
|
6
5
|
// DO NOT re-export component contexts from here. Only export generics.
|
|
7
6
|
export * from './navigation';
|
|
@@ -10,8 +9,12 @@ export * from './component-generics';
|
|
|
10
9
|
export * from './search';
|
|
11
10
|
export * from './docs';
|
|
12
11
|
|
|
13
|
-
export function useLanguageComponents() {
|
|
12
|
+
export function useLanguageComponents(): LanguageComponentDefinition {
|
|
14
13
|
const language = useLanguage();
|
|
15
|
-
const context =
|
|
16
|
-
|
|
14
|
+
const context = useComponentContext();
|
|
15
|
+
const definition = context.language[language];
|
|
16
|
+
if (!definition) {
|
|
17
|
+
throw new Error(`Language component definition not found for language: ${language}`);
|
|
18
|
+
}
|
|
19
|
+
return definition;
|
|
17
20
|
}
|