@stainless-api/docs-ui 0.1.0-beta.13 → 0.1.0-beta.15
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/package.json +15 -7
- package/src/components/breadcrumbs.tsx +1 -1
- package/src/components/chat.tsx +18 -15
- package/src/components/method.tsx +8 -9
- package/src/components/overview.tsx +26 -12
- 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 +25 -20
- package/src/components/sidebar.tsx +3 -3
- package/src/components/snippets.tsx +26 -9
- 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 +26 -24
- 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 +7 -6
- package/src/search/types.ts +2 -2
- package/src/styles/main.css +2 -1
- package/src/utils.ts +9 -8
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stainless-api/docs-ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.1.0-beta.
|
|
4
|
+
"version": "0.1.0-beta.15",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -10,17 +10,21 @@
|
|
|
10
10
|
"dist",
|
|
11
11
|
"src"
|
|
12
12
|
],
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"react": ">=19.0.0",
|
|
15
|
+
"react-dom": ">=19.0.0"
|
|
16
|
+
},
|
|
13
17
|
"dependencies": {
|
|
14
18
|
"@algolia/client-search": "^5.25.0",
|
|
19
|
+
"@langchain/community": "^0.3.16",
|
|
15
20
|
"@markdoc/markdoc": "^0.5.2",
|
|
16
21
|
"ai": "4.3.19",
|
|
17
22
|
"clsx": "^2.1.1",
|
|
18
23
|
"fuse.js": "^7.1.0",
|
|
19
24
|
"htmlparser2": "^10.0.0",
|
|
20
25
|
"lucide-react": "^0.544.0",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"@stainless-api/ui-primitives": "0.1.0-beta.14"
|
|
26
|
+
"natural": "^8.0.1",
|
|
27
|
+
"@stainless-api/ui-primitives": "0.1.0-beta.15"
|
|
24
28
|
},
|
|
25
29
|
"devDependencies": {
|
|
26
30
|
"@types/node": "^24.4.0",
|
|
@@ -28,12 +32,16 @@
|
|
|
28
32
|
"@types/react-dom": "^19.2.2",
|
|
29
33
|
"dotenv": "17.2.3",
|
|
30
34
|
"esbuild": "^0.25.4",
|
|
35
|
+
"react": "^19.2.0",
|
|
36
|
+
"react-dom": "^19.2.0",
|
|
31
37
|
"tsx": "^4.20.3",
|
|
32
|
-
"
|
|
38
|
+
"typescript": "5.9.3",
|
|
39
|
+
"@stainless/eslint-config": "0.0.0",
|
|
40
|
+
"@stainless/sdk-json": "^0.0.0"
|
|
33
41
|
},
|
|
34
42
|
"scripts": {
|
|
35
|
-
"symlink": "node make-symlink.js",
|
|
36
43
|
"build": "node build.js",
|
|
37
|
-
"lint": "eslint ."
|
|
44
|
+
"lint": "eslint .",
|
|
45
|
+
"check:types": "tsc --noEmit"
|
|
38
46
|
}
|
|
39
47
|
}
|
package/src/components/chat.tsx
CHANGED
|
@@ -4,29 +4,32 @@ import { ArrowUpIcon, BotMessageSquareIcon, LoaderCircleIcon, SquareIcon, TrashI
|
|
|
4
4
|
import { useLanguage, useSearchContext } from '../contexts';
|
|
5
5
|
import { Markdown } from './primitives';
|
|
6
6
|
import type { SearchSettings } from '../search/types';
|
|
7
|
-
import type { UIMessage } from 'ai';
|
|
7
|
+
import type { Message, UIMessage } from 'ai';
|
|
8
8
|
import style from '../style';
|
|
9
9
|
|
|
10
10
|
const BASEURL = 'https://askai.algolia.com';
|
|
11
11
|
|
|
12
12
|
function buildFetch({ appId, searchKey, indexName, assistant }: SearchSettings) {
|
|
13
|
-
const headers = {
|
|
13
|
+
const headers: Record<string, string> = {
|
|
14
14
|
'X-Algolia-Application-Id': appId,
|
|
15
15
|
'X-Algolia-API-Key': searchKey,
|
|
16
16
|
'X-Algolia-Index-Name': `${indexName}-chat`,
|
|
17
|
-
'X-Algolia-Assistant-Id': assistant,
|
|
18
17
|
};
|
|
18
|
+
if (assistant) {
|
|
19
|
+
headers['X-Algolia-Assistant-Id'] = assistant;
|
|
20
|
+
}
|
|
19
21
|
|
|
20
|
-
return async (input, init) => {
|
|
22
|
+
return async (input: RequestInfo | URL, init: RequestInit | undefined) => {
|
|
21
23
|
const response = await fetch(`${BASEURL}/chat/token`, { method: 'POST', headers });
|
|
22
|
-
const data = await response.json()
|
|
24
|
+
const data = (await response.json()) as Record<string, unknown>;
|
|
25
|
+
const reqHeaders = new Headers(init?.headers);
|
|
26
|
+
reqHeaders.set('Authorization', `TOKEN ${data['token']}`);
|
|
27
|
+
for (const name in headers) {
|
|
28
|
+
reqHeaders.set(name, headers[name]!);
|
|
29
|
+
}
|
|
23
30
|
return fetch(input, {
|
|
24
31
|
...init,
|
|
25
|
-
headers:
|
|
26
|
-
...init.headers,
|
|
27
|
-
...headers,
|
|
28
|
-
Authorization: `TOKEN ${data['token']}`,
|
|
29
|
-
},
|
|
32
|
+
headers: reqHeaders,
|
|
30
33
|
});
|
|
31
34
|
};
|
|
32
35
|
}
|
|
@@ -53,7 +56,7 @@ export function Chat() {
|
|
|
53
56
|
const content = sessionStorage.getItem('stldocs-chat-messages');
|
|
54
57
|
if (!content) return;
|
|
55
58
|
|
|
56
|
-
setMessages(JSON.parse(content));
|
|
59
|
+
setMessages(JSON.parse(content) as Message[]);
|
|
57
60
|
setTimeout(() => {
|
|
58
61
|
if (!messageListRef.current) return;
|
|
59
62
|
messageListRef.current.scrollTop = messageListRef.current.scrollHeight;
|
|
@@ -61,8 +64,8 @@ export function Chat() {
|
|
|
61
64
|
}, [setMessages]);
|
|
62
65
|
|
|
63
66
|
React.useEffect(() => {
|
|
64
|
-
if (status !== 'submitted') return;
|
|
65
|
-
const messageElements = messageListRef
|
|
67
|
+
if (status !== 'submitted' || !messageListRef.current) return;
|
|
68
|
+
const messageElements = messageListRef.current.querySelectorAll('[data-stldocs-chat-message]');
|
|
66
69
|
[...messageElements].at(-1)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
67
70
|
}, [status]);
|
|
68
71
|
|
|
@@ -75,7 +78,7 @@ export function Chat() {
|
|
|
75
78
|
setInput('');
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
function handleKeyUp(ev) {
|
|
81
|
+
function handleKeyUp(ev: React.KeyboardEvent) {
|
|
79
82
|
if (ev.key === 'Enter' && !ev.shiftKey) {
|
|
80
83
|
ev.preventDefault();
|
|
81
84
|
handleSend();
|
|
@@ -134,7 +137,7 @@ export type ChatModalProps = {
|
|
|
134
137
|
};
|
|
135
138
|
|
|
136
139
|
export function ChatModal({ id, open: isOpen }: ChatModalProps) {
|
|
137
|
-
const [open, setOpen] = React.useState<boolean>(isOpen);
|
|
140
|
+
const [open, setOpen] = React.useState<boolean>(isOpen ?? false);
|
|
138
141
|
|
|
139
142
|
return (
|
|
140
143
|
<div
|
|
@@ -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
|
);
|
|
@@ -135,8 +136,6 @@ export type MethodProps = {
|
|
|
135
136
|
} & React.HTMLProps<HTMLDivElement>;
|
|
136
137
|
|
|
137
138
|
export function Method({ id, header, children, className, ...props }: MethodProps) {
|
|
138
|
-
const Docs = useComponents();
|
|
139
|
-
|
|
140
139
|
return (
|
|
141
140
|
<div id={id} className={clsx(style.Method, className)} tabIndex={0} {...props}>
|
|
142
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,7 +54,7 @@ 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
|
|
@@ -76,11 +76,11 @@ export function SDKResource({ resource, parents, showModels }: SDKResourceProps
|
|
|
76
76
|
const spec = useSpec();
|
|
77
77
|
|
|
78
78
|
const methods = Object.values(resource.methods).filter(
|
|
79
|
-
(method) => spec
|
|
79
|
+
(method) => spec?.decls?.[language]?.[method.stainlessPath],
|
|
80
80
|
);
|
|
81
81
|
|
|
82
82
|
const models = Object.values(resource.models).filter(
|
|
83
|
-
(model) => spec
|
|
83
|
+
(model) => spec?.decls?.[language]?.[`${model.stainlessPath} > (schema)`],
|
|
84
84
|
);
|
|
85
85
|
|
|
86
86
|
return (
|
|
@@ -144,15 +144,29 @@ export function SDKRoot({ stainlessPath }: SDKRootProps) {
|
|
|
144
144
|
const Docs = useComponents();
|
|
145
145
|
|
|
146
146
|
const parsed = parseStainlessPath(stainlessPath);
|
|
147
|
-
const resource = getResourceFromSpec(stainlessPath, spec);
|
|
147
|
+
const resource = spec && getResourceFromSpec(stainlessPath, spec);
|
|
148
148
|
|
|
149
|
-
if (!resource || !parsed)
|
|
149
|
+
if (!resource || !parsed) {
|
|
150
|
+
console.warn(`Could not find resource or parsed path for '${stainlessPath}'`);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
150
153
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
+
}
|
|
156
166
|
|
|
157
|
-
return
|
|
167
|
+
return (
|
|
168
|
+
<div className={style.Root}>
|
|
169
|
+
<Docs.SDKOverview resource={resource} />
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
158
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,7 +241,7 @@ 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
|
|
@@ -256,7 +258,7 @@ export function SDKMethodHeader({ method }: SDKMethodProps) {
|
|
|
256
258
|
export function SDKMethodInfo({ method }: SDKMethodProps) {
|
|
257
259
|
const Docs = useComponents();
|
|
258
260
|
const Lang = useLanguageComponents();
|
|
259
|
-
const decl = useDeclaration(method.stainlessPath);
|
|
261
|
+
const decl = useDeclaration(method.stainlessPath, true);
|
|
260
262
|
const spec = useSpec();
|
|
261
263
|
const language = useLanguage();
|
|
262
264
|
|
|
@@ -264,8 +266,10 @@ export function SDKMethodInfo({ method }: SDKMethodProps) {
|
|
|
264
266
|
|
|
265
267
|
function shouldExpand(items: SDKJSON.ID[]) {
|
|
266
268
|
if (items.length > 1) return false;
|
|
267
|
-
const
|
|
268
|
-
|
|
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;
|
|
269
273
|
}
|
|
270
274
|
|
|
271
275
|
return (
|
|
@@ -279,6 +283,7 @@ export function SDKMethodInfo({ method }: SDKMethodProps) {
|
|
|
279
283
|
}
|
|
280
284
|
returns={
|
|
281
285
|
'responseChildren' in decl &&
|
|
286
|
+
decl.responseChildren &&
|
|
282
287
|
decl.responseChildren.length > 0 && (
|
|
283
288
|
<Docs.SDKChildren expand={shouldExpand(decl.responseChildren)} paths={decl.responseChildren} />
|
|
284
289
|
)
|
|
@@ -289,7 +294,7 @@ export function SDKMethodInfo({ method }: SDKMethodProps) {
|
|
|
289
294
|
|
|
290
295
|
export function SDKMethod({ method, transformRequestSnippet }: SDKMethodProps) {
|
|
291
296
|
const Docs = useComponents();
|
|
292
|
-
const decl = useDeclaration(method?.stainlessPath);
|
|
297
|
+
const decl = useDeclaration(method?.stainlessPath, true);
|
|
293
298
|
const layout = useContentPanelLayout();
|
|
294
299
|
|
|
295
300
|
if (!decl) return;
|
|
@@ -324,7 +329,7 @@ export type SDKModelProps = {
|
|
|
324
329
|
|
|
325
330
|
export function SDKModel({ model }: SDKModelProps) {
|
|
326
331
|
const Docs = useComponents();
|
|
327
|
-
const decl = useDeclaration(`${model.stainlessPath} > (schema)
|
|
332
|
+
const decl = useDeclaration(`${model.stainlessPath} > (schema)`, true);
|
|
328
333
|
|
|
329
334
|
if (!decl) return null;
|
|
330
335
|
|
|
@@ -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,7 +4,7 @@ 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
9
|
import { Button } from '@stainless-api/ui-primitives';
|
|
10
10
|
|
|
@@ -59,15 +59,24 @@ export function Snippet({ requestTitle, method, transformRequestSnippet }: Snipp
|
|
|
59
59
|
const language = useLanguage();
|
|
60
60
|
const [CopyButtonIcon, setCopyIcon] = React.useState<LucideIcon>(CopyIcon);
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
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
|
+
}
|
|
64
73
|
|
|
65
|
-
const signature = 'qualified' in decl ? decl.qualified : undefined;
|
|
74
|
+
const signature = decl && 'qualified' in decl ? decl.qualified : undefined;
|
|
66
75
|
const responses = method.exampleResponses;
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
const snippet = transformRequestSnippet
|
|
78
|
+
? transformRequestSnippet({ snippet: originalSnippet, language })
|
|
79
|
+
: originalSnippet;
|
|
71
80
|
|
|
72
81
|
async function handleCopy() {
|
|
73
82
|
try {
|
|
@@ -113,9 +122,15 @@ export function SnippetResponse({ responses }: { responses: SDKJSON.Method['exam
|
|
|
113
122
|
|
|
114
123
|
const mappedResponses = Object.keys(responses)
|
|
115
124
|
.map((key) => {
|
|
125
|
+
const responseContent = responses[key];
|
|
126
|
+
if (!responseContent) return null;
|
|
127
|
+
|
|
116
128
|
// Get the first response type ie application/json or text/plain
|
|
117
|
-
const responseType = Object.keys(
|
|
118
|
-
|
|
129
|
+
const responseType = Object.keys(responseContent)[0];
|
|
130
|
+
if (!responseType) return null;
|
|
131
|
+
|
|
132
|
+
const response = responseContent[responseType];
|
|
133
|
+
if (!response) return null;
|
|
119
134
|
|
|
120
135
|
const examples = response?.examples;
|
|
121
136
|
|
|
@@ -123,6 +138,8 @@ export function SnippetResponse({ responses }: { responses: SDKJSON.Method['exam
|
|
|
123
138
|
|
|
124
139
|
// Get the first example type, ie Example or html
|
|
125
140
|
const exampleType = Object.keys(examples)[0];
|
|
141
|
+
if (!exampleType) return null;
|
|
142
|
+
|
|
126
143
|
let value = examples[exampleType]?.value;
|
|
127
144
|
|
|
128
145
|
if (!value) return null;
|