@stainless-api/docs 0.1.0-beta.8 → 0.1.0-beta.80
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/CHANGELOG.md +636 -0
- package/eslint-suppressions.json +52 -0
- package/locals.d.ts +17 -0
- package/package.json +50 -39
- package/plugin/assets/languages/cli.svg +14 -0
- package/plugin/assets/languages/csharp.svg +1 -0
- package/plugin/buildAlgoliaIndex.ts +32 -7
- package/plugin/cms/server.ts +130 -59
- package/plugin/cms/sidebar-builder.ts +7 -26
- package/plugin/cms/worker.ts +83 -5
- package/plugin/components/MethodDescription.tsx +54 -0
- package/plugin/components/RequestBuilder/ParamEditor.tsx +55 -0
- package/plugin/components/RequestBuilder/SnippetStainlessIsland.tsx +107 -0
- package/plugin/components/RequestBuilder/index.tsx +31 -0
- package/plugin/components/RequestBuilder/props.ts +9 -0
- package/plugin/components/RequestBuilder/spec-helpers.ts +50 -0
- package/plugin/components/RequestBuilder/styles.css +67 -0
- package/plugin/components/SDKSelect.astro +7 -87
- package/plugin/components/SnippetCode.tsx +107 -67
- package/plugin/components/StainlessIslands.tsx +126 -0
- package/plugin/components/search/SearchAlgolia.astro +45 -28
- package/plugin/components/search/SearchIsland.tsx +45 -27
- package/plugin/generateAPIReferenceLink.ts +2 -2
- package/plugin/globalJs/ai-dropdown-options.ts +243 -0
- package/plugin/globalJs/code-snippets.ts +15 -8
- package/plugin/globalJs/copy.ts +91 -17
- package/plugin/globalJs/create-playground.shim.ts +3 -0
- package/plugin/globalJs/method-descriptions.ts +33 -0
- package/plugin/globalJs/navigation.ts +7 -26
- package/plugin/globalJs/playground-data.shim.ts +1 -0
- package/plugin/globalJs/playground-data.ts +14 -0
- package/plugin/helpers/generateDocsRoutes.ts +27 -0
- package/plugin/index.ts +198 -36
- package/plugin/languages.ts +7 -2
- package/plugin/loadPluginConfig.ts +122 -36
- package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +1 -1
- package/plugin/react/Routing.tsx +199 -126
- package/plugin/referencePlaceholderUtils.ts +1 -1
- package/plugin/replaceSidebarPlaceholderMiddleware.ts +3 -3
- package/plugin/routes/Docs.astro +55 -102
- package/plugin/routes/DocsStatic.astro +1 -1
- package/plugin/routes/Overview.astro +10 -16
- package/plugin/routes/markdown.ts +9 -8
- package/plugin/vendor/preview.worker.docs.js +20928 -17830
- package/plugin/vendor/templates/go.md +1 -1
- package/plugin/vendor/templates/python.md +1 -1
- package/resolveSrcFile.ts +10 -0
- package/scripts/vendor_deps.ts +5 -5
- package/shared/getProsePages.ts +42 -0
- package/shared/getSharedLogger.ts +15 -0
- package/shared/terminalUtils.ts +3 -0
- package/src/content.config.ts +9 -0
- package/stl-docs/components/AIDropdown.tsx +63 -0
- package/stl-docs/components/AiChatIsland.tsx +14 -0
- package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +2 -2
- package/stl-docs/components/Head.astro +16 -0
- package/stl-docs/components/Header.astro +6 -8
- package/stl-docs/components/PageFrame.astro +18 -0
- package/stl-docs/components/PageTitle.astro +82 -0
- package/stl-docs/components/TableOfContents.astro +34 -0
- package/stl-docs/components/ThemeProvider.astro +36 -0
- package/stl-docs/components/ThemeSelect.astro +84 -139
- package/stl-docs/components/content-panel/ContentPanel.astro +15 -46
- package/stl-docs/components/headers/SplashMobileMenuToggle.astro +17 -1
- package/stl-docs/components/headers/StackedHeader.astro +29 -24
- package/stl-docs/components/icons/chat-gpt.tsx +2 -2
- package/stl-docs/components/icons/cursor.tsx +10 -0
- package/stl-docs/components/icons/gemini.tsx +19 -0
- package/stl-docs/components/icons/markdown.tsx +1 -1
- package/stl-docs/components/index.ts +1 -0
- package/stl-docs/components/mintlify-compat/Accordion.astro +7 -5
- package/stl-docs/components/mintlify-compat/AccordionGroup.astro +7 -3
- package/stl-docs/components/mintlify-compat/Columns.astro +40 -42
- package/stl-docs/components/mintlify-compat/Frame.astro +16 -18
- package/stl-docs/components/mintlify-compat/callouts/Callout.astro +1 -1
- package/stl-docs/components/mintlify-compat/callouts/Check.astro +1 -1
- package/stl-docs/components/mintlify-compat/callouts/Danger.astro +1 -1
- package/stl-docs/components/mintlify-compat/callouts/Info.astro +1 -1
- package/stl-docs/components/mintlify-compat/callouts/Note.astro +1 -1
- package/stl-docs/components/mintlify-compat/callouts/Tip.astro +1 -1
- package/stl-docs/components/mintlify-compat/callouts/Warning.astro +1 -1
- package/stl-docs/components/mintlify-compat/card.css +33 -35
- package/stl-docs/components/mintlify-compat/index.ts +2 -4
- package/stl-docs/components/nav-tabs/NavDropdown.astro +31 -70
- package/stl-docs/components/nav-tabs/NavTabs.astro +78 -80
- package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +15 -8
- package/stl-docs/components/nav-tabs/buildNavLinks.ts +3 -2
- package/stl-docs/components/pagination/HomeLink.astro +10 -0
- package/stl-docs/components/pagination/Pagination.astro +175 -0
- package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +22 -0
- package/stl-docs/components/pagination/PaginationLinkQuiet.astro +13 -0
- package/stl-docs/components/pagination/util.ts +71 -0
- package/stl-docs/components/scripts.ts +1 -0
- package/stl-docs/disableCalloutSyntax.ts +36 -0
- package/stl-docs/index.ts +147 -52
- package/stl-docs/loadStlDocsConfig.ts +45 -7
- package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +61 -0
- package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +39 -0
- package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
- package/stl-docs/proseSearchIndexing.ts +450 -0
- package/stl-docs/tabsMiddleware.ts +13 -4
- package/styles/code.css +128 -136
- package/styles/fonts.css +32 -17
- package/styles/links.css +11 -48
- package/styles/method-descriptions.css +36 -0
- package/styles/overrides.css +49 -57
- package/styles/page.css +90 -59
- package/styles/sdk_select.css +9 -7
- package/styles/search.css +57 -69
- package/styles/sidebar.css +211 -131
- package/styles/{variables.css → sl-variables.css} +3 -2
- package/styles/stldocs-variables.css +6 -0
- package/styles/toc.css +41 -34
- package/theme.css +10 -10
- package/tsconfig.json +2 -5
- package/virtual-module.d.ts +121 -5
- package/components/variables.css +0 -135
- package/plugin/globalJs/ai-dropdown.ts +0 -57
- package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -58
- package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -55
- package/stl-docs/components/mintlify-compat/Step.astro +0 -58
- package/stl-docs/components/mintlify-compat/Steps.astro +0 -17
- /package/{plugin/assets → assets}/fonts/geist/OFL.txt +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-latin-ext.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-latin.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
- /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin.woff2 +0 -0
|
@@ -1,13 +1,42 @@
|
|
|
1
|
-
import
|
|
2
|
-
SnippetCodeProps,
|
|
3
|
-
SnippetContainerProps,
|
|
4
|
-
|
|
5
|
-
} from '@stainless-api/docs-ui/
|
|
6
|
-
import { useHighlight, useLanguage } from '@stainless-api/docs-ui/
|
|
7
|
-
import style from '@stainless-api/docs-ui/
|
|
1
|
+
import {
|
|
2
|
+
type SnippetCodeProps,
|
|
3
|
+
type SnippetContainerProps,
|
|
4
|
+
SnippetResponse as DocsUiSnippetResponse,
|
|
5
|
+
} from '@stainless-api/docs-ui/components';
|
|
6
|
+
import { useHighlight, useLanguage } from '@stainless-api/docs-ui/contexts';
|
|
7
|
+
import style from '@stainless-api/docs-ui/style';
|
|
8
8
|
import * as cheerio from 'cheerio/slim';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
EXPERIMENTAL_COLLAPSIBLE_SNIPPETS,
|
|
11
|
+
EXPERIMENTAL_PLAYGROUNDS,
|
|
12
|
+
EXPERIMENTAL_REQUEST_BUILDER,
|
|
13
|
+
} from 'virtual:stl-starlight-virtual-module';
|
|
10
14
|
import clsx from 'clsx';
|
|
15
|
+
import { Button } from '@stainless-api/ui-primitives';
|
|
16
|
+
import { CopyIcon, PlayIcon } from 'lucide-react';
|
|
17
|
+
import React from 'react';
|
|
18
|
+
import { RequestBuilder } from './RequestBuilder';
|
|
19
|
+
|
|
20
|
+
function PlaygroundIcon() {
|
|
21
|
+
return (
|
|
22
|
+
<svg
|
|
23
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
24
|
+
width={16}
|
|
25
|
+
height={16}
|
|
26
|
+
viewBox="0 0 24 24"
|
|
27
|
+
fill="none"
|
|
28
|
+
stroke="currentColor"
|
|
29
|
+
strokeWidth={2}
|
|
30
|
+
strokeLinecap="round"
|
|
31
|
+
strokeLinejoin="round"
|
|
32
|
+
className={'lucide ' + style.Icon}
|
|
33
|
+
aria-hidden="true"
|
|
34
|
+
>
|
|
35
|
+
<path d="m 1,2 h 1 a 4,4 0 0 1 4,4 v 1 m 5,15 H 10 A 4,4 0 0 1 6,18 V 6 a 4,4 0 0 1 4,-4 h 1 M 1,22 H 2 A 4,4 0 0 0 6,18 V 17 M 14.029059,8.147837 A 1.2853426,1.2853426 0 0 1 15.978924,7.0437277 L 22.40178,10.8959 a 1.2853426,1.2853426 0 0 1 0,2.208219 l -6.422856,3.852172 a 1.2853426,1.2853426 0 0 1 -1.949865,-1.105395 z" />
|
|
36
|
+
</svg>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
11
40
|
/*
|
|
12
41
|
* This may be replaced by additional data from the sdk.
|
|
13
42
|
* Without information from the sdk, we use simple heuristics per language.
|
|
@@ -53,7 +82,7 @@ function wrapFirstNSpaces($line: cheerio.Cheerio<any>, n: number) {
|
|
|
53
82
|
const m = inner.match(new RegExp(`^( {1,${n}})`));
|
|
54
83
|
if (!m) return;
|
|
55
84
|
|
|
56
|
-
const lead = m[1]
|
|
85
|
+
const lead = m[1]!;
|
|
57
86
|
$firstSpan.html(`<span class="leading-ws">${lead}</span>${inner.slice(lead.length)}`);
|
|
58
87
|
}
|
|
59
88
|
|
|
@@ -123,61 +152,36 @@ function useIsCollapsible({ signature }: { signature?: string }): boolean {
|
|
|
123
152
|
return Boolean(EXPERIMENTAL_COLLAPSIBLE_SNIPPETS && signature && language);
|
|
124
153
|
}
|
|
125
154
|
|
|
126
|
-
export function
|
|
155
|
+
export function SnippetContainer({ children, signature, method }: SnippetContainerProps) {
|
|
127
156
|
const isCollapsible = useIsCollapsible({ signature });
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
{children}
|
|
132
|
-
{signature && isCollapsible && (
|
|
133
|
-
<button
|
|
134
|
-
className={clsx(style.Button, style.ButtonSecondary, 'stl-snippet-expand-button')}
|
|
135
|
-
id="stl-snippet-expand-button"
|
|
136
|
-
>
|
|
137
|
-
Show more
|
|
138
|
-
</button>
|
|
139
|
-
)}
|
|
140
|
-
</div>
|
|
157
|
+
const className = clsx(
|
|
158
|
+
style.Snippet,
|
|
159
|
+
isCollapsible ? 'stl-snippet-collapsible' : 'stl-snippet-non-collapsible',
|
|
141
160
|
);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
export function SnippetContainer({ children, signature }: SnippetContainerProps) {
|
|
145
|
-
const isCollapsible = useIsCollapsible({ signature });
|
|
146
|
-
|
|
147
|
-
return (
|
|
148
|
-
<div
|
|
149
|
-
className={clsx(
|
|
150
|
-
style.Snippet,
|
|
151
|
-
isCollapsible ? 'stl-snippet-collapsible' : 'stl-snippet-non-collapsible',
|
|
152
|
-
)}
|
|
153
|
-
>
|
|
161
|
+
return EXPERIMENTAL_REQUEST_BUILDER ? (
|
|
162
|
+
<RequestBuilder className={className} method={method}>
|
|
154
163
|
{children}
|
|
155
|
-
</
|
|
164
|
+
</RequestBuilder>
|
|
165
|
+
) : (
|
|
166
|
+
<div className={className}>{children}</div>
|
|
156
167
|
);
|
|
157
168
|
}
|
|
158
169
|
|
|
159
|
-
export function
|
|
160
|
-
content
|
|
161
|
-
language
|
|
162
|
-
signature,
|
|
163
|
-
highlighted,
|
|
164
|
-
}: SnippetCodeProps & { signature: string; highlighted: string; language: string }) {
|
|
165
|
-
const ranges = getCollapsedRanges(content, language, signature);
|
|
166
|
-
const html = condensedShikiHtmlFull(highlighted, language, ranges);
|
|
167
|
-
const offset = getCounterOffset(ranges);
|
|
168
|
-
|
|
170
|
+
export function SnippetButtons({ content }: { content: string }) {
|
|
171
|
+
void content;
|
|
172
|
+
const language = useLanguage();
|
|
169
173
|
return (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
174
|
+
<>
|
|
175
|
+
<Button variant="outline" data-stldocs-snippet-copy>
|
|
176
|
+
<CopyIcon size={16} className={style.Icon} />
|
|
177
|
+
</Button>
|
|
178
|
+
{EXPERIMENTAL_PLAYGROUNDS &&
|
|
179
|
+
(language === 'python' || language === 'typescript' || language === 'http') && (
|
|
180
|
+
<Button data-stldocs-snippet-play variant="muted" border title="Play">
|
|
181
|
+
<PlaygroundIcon />
|
|
182
|
+
</Button>
|
|
183
|
+
)}
|
|
184
|
+
</>
|
|
181
185
|
);
|
|
182
186
|
}
|
|
183
187
|
|
|
@@ -197,16 +201,52 @@ export function SnippetCode({ content, signature, language: forcedLanguage }: Sn
|
|
|
197
201
|
}
|
|
198
202
|
|
|
199
203
|
return (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
204
|
+
<>
|
|
205
|
+
<div
|
|
206
|
+
className={clsx(style.SnippetCode, isCollapsible && 'stl-snippet-code-is-collapsed')}
|
|
207
|
+
data-snippet-expanded-offset={offset}
|
|
208
|
+
data-stldocs-copy-content
|
|
209
|
+
dangerouslySetInnerHTML={{ __html: highlighted }}
|
|
210
|
+
/>
|
|
211
|
+
{signature && isCollapsible && (
|
|
212
|
+
<Button
|
|
213
|
+
className={'stl-snippet-expand-button'}
|
|
214
|
+
id="stl-snippet-expand-button"
|
|
215
|
+
size="sm"
|
|
216
|
+
variant="outline"
|
|
217
|
+
>
|
|
218
|
+
Show more
|
|
219
|
+
</Button>
|
|
220
|
+
)}
|
|
221
|
+
{EXPERIMENTAL_REQUEST_BUILDER && (
|
|
222
|
+
<div className="request-builder-container" style={{ display: 'contents' }}></div>
|
|
223
|
+
)}
|
|
224
|
+
</>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function SnippetFooter() {
|
|
229
|
+
if (!EXPERIMENTAL_REQUEST_BUILDER) return null;
|
|
230
|
+
return (
|
|
231
|
+
<div className={clsx(style.SnippetFooter, 'try-it-footer')}>
|
|
232
|
+
{EXPERIMENTAL_REQUEST_BUILDER && (
|
|
233
|
+
<div className="request-builder-footer" style={{ display: 'contents' }}></div>
|
|
234
|
+
)}
|
|
235
|
+
<Button variant="accent" className="try-it-button">
|
|
236
|
+
<Button.Label>Try it</Button.Label>
|
|
237
|
+
<Button.Icon icon={PlayIcon} />
|
|
238
|
+
</Button>
|
|
210
239
|
</div>
|
|
211
240
|
);
|
|
212
241
|
}
|
|
242
|
+
|
|
243
|
+
export function SnippetResponse({ ...props }: React.ComponentProps<typeof DocsUiSnippetResponse>) {
|
|
244
|
+
return (
|
|
245
|
+
<>
|
|
246
|
+
<DocsUiSnippetResponse {...props} />
|
|
247
|
+
{EXPERIMENTAL_REQUEST_BUILDER && (
|
|
248
|
+
<div className="request-builder-response" style={{ display: 'contents' }} />
|
|
249
|
+
)}
|
|
250
|
+
</>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Technical overview:
|
|
3
|
+
* - `StainlessIslands.ts` is an Astro Client Island wherein we register a HTML custom element called `stl-island`
|
|
4
|
+
* - When a `<stl-island component="MyComponent">` is added to the DOM, we:
|
|
5
|
+
* 1. receive a callback from the browser
|
|
6
|
+
* 2. dynamic-import `MyComponent`
|
|
7
|
+
* 3. create an instance of `MyComponent`, to which we pass the **`stl-island` DOM node** as a prop named `parent`
|
|
8
|
+
* 4. register the (`stlIslandDomNode` → `ReactNode`) pair in a global map named `roots`
|
|
9
|
+
* - The actual Astro client island is a simple React component that renders all of the ReactNodes from the global `roots` map
|
|
10
|
+
* - it uses a `useSyncExternalStore` to be notified of changes to the global `roots` map by registering a callback
|
|
11
|
+
* - Each “stainless island” (instantiated by the custom element handler and rendered by the Astro client island) is responsible for using the `parent` prop it receives to identify one or more **portal targets** into which it can render its contents.
|
|
12
|
+
* - the react parent of all Stainless Islands is the global `<StainlessIslands client:load />` singleton, but the _DOM parent_ is the various portal targets of all of the stainless islands
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useSyncExternalStore, ReactNode } from 'react';
|
|
16
|
+
|
|
17
|
+
type StlIslandComponent = ({ parent }: { parent: HTMLElement }) => ReactNode;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Register new Stainless Islands in this map.
|
|
21
|
+
* The component should be the default export and should be able to be dynamic-imported.
|
|
22
|
+
* The component should accept a single prop: `parent: HTMLElement`, which is the DOM node of the `<stl-island>` element.
|
|
23
|
+
* The component can create portals to render into the DOM subtree of the `parent`.
|
|
24
|
+
*/
|
|
25
|
+
const componentsMap: Record<string, () => Promise<{ default: StlIslandComponent }>> = {
|
|
26
|
+
SnippetStainlessIsland: () => import('./RequestBuilder/SnippetStainlessIsland'),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
interface State {
|
|
30
|
+
roots: Map<HTMLElement, ReactNode>;
|
|
31
|
+
onRootsChange?: (() => void) | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// keep state in import.meta.hot.data so that our record of our react roots is not lost across HMR
|
|
35
|
+
const state: State = import.meta.hot?.data?.state ?? {
|
|
36
|
+
roots: new Map(),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (import.meta.hot?.data) {
|
|
40
|
+
import.meta.hot.data.state = state;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let key = 0;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Custom element mounts and unmounts components and gives them a reference to the parent
|
|
47
|
+
* so they can render in portals.
|
|
48
|
+
*/
|
|
49
|
+
class StlIsland extends (globalThis?.HTMLElement ?? Object) {
|
|
50
|
+
connectedCallback(this: StlIsland) {
|
|
51
|
+
const componentName = this.getAttribute('component');
|
|
52
|
+
if (!componentName) {
|
|
53
|
+
console.error('[stl-island] missing required attribute "component"');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!componentsMap[componentName]) {
|
|
58
|
+
console.error(`[stl-island] unknown component "${componentName}"`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
componentsMap[componentName]().then(
|
|
63
|
+
({ default: Component }) => {
|
|
64
|
+
state.roots = new Map(state.roots).set(this, <Component parent={this} key={key++} />);
|
|
65
|
+
state.onRootsChange?.();
|
|
66
|
+
},
|
|
67
|
+
(e) => {
|
|
68
|
+
console.error(`[stl-island] failed to load component "${componentName}":`, e);
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
connectedMoveCallback() {
|
|
73
|
+
// empty so we don't get disconnected/reconnected if the dom element gets moved
|
|
74
|
+
}
|
|
75
|
+
disconnectedCallback() {
|
|
76
|
+
state.roots = new Map(state.roots);
|
|
77
|
+
state.roots.delete(this);
|
|
78
|
+
state.onRootsChange?.();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (typeof customElements !== 'undefined' && !customElements.get('stl-island')) {
|
|
83
|
+
customElements.define('stl-island', StlIsland);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
declare global {
|
|
87
|
+
interface HTMLElementTagNameMap {
|
|
88
|
+
'stl-island': StlIsland;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
declare module 'react' {
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
93
|
+
namespace JSX {
|
|
94
|
+
interface IntrinsicElements {
|
|
95
|
+
// client-loaded
|
|
96
|
+
'stl-island': React.DetailedHTMLProps<React.HTMLAttributes<StlIsland>, StlIsland> & {
|
|
97
|
+
component: keyof typeof componentsMap;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Renders all StainlessIslands that have been registered in the global `state.roots` map */
|
|
104
|
+
function StainlessIslandsInner() {
|
|
105
|
+
const roots = useSyncExternalStore<Map<HTMLElement, ReactNode> | null>(
|
|
106
|
+
(onChange) => {
|
|
107
|
+
state.onRootsChange = onChange;
|
|
108
|
+
return () => {
|
|
109
|
+
state.onRootsChange = undefined;
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
() => {
|
|
113
|
+
return state.roots;
|
|
114
|
+
},
|
|
115
|
+
() => null,
|
|
116
|
+
);
|
|
117
|
+
if (!roots) return null;
|
|
118
|
+
return [...roots.values()];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function StainlessIslands() {
|
|
122
|
+
// Astro tries to call this function outside of react to detect if it's
|
|
123
|
+
// an Astro JSX or React JSX component, which causes warnings if we use hooks,
|
|
124
|
+
// so we use a wrapper component to avoid the hook calls.
|
|
125
|
+
return <StainlessIslandsInner />;
|
|
126
|
+
}
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { Icon } from '@astrojs/starlight/components';
|
|
3
2
|
import { DocsSearch } from './SearchIsland';
|
|
4
|
-
import {
|
|
3
|
+
import { Button } from '@stainless-api/ui-primitives';
|
|
4
|
+
import { SearchIcon, XIcon } from 'lucide-react';
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<site-search>
|
|
8
|
-
<
|
|
9
|
-
|
|
8
|
+
<Button
|
|
9
|
+
popoverTarget="stldocs-search"
|
|
10
10
|
data-open-modal
|
|
11
11
|
aria-label={Astro.locals.t('search.label')}
|
|
12
12
|
aria-keyshortcuts="Control+K"
|
|
13
|
-
|
|
13
|
+
variant="outline"
|
|
14
|
+
className="stl-algolia-search"
|
|
14
15
|
>
|
|
15
|
-
<
|
|
16
|
+
<SearchIcon className="icon-search" size={14} />
|
|
17
|
+
<XIcon className="icon-close" size={14} />
|
|
16
18
|
<span class="sl-hidden md:sl-block" aria-hidden="true">{Astro.locals.t('search.label')}</span>
|
|
17
19
|
<kbd class="sl-hidden md:sl-flex" style="display: none;">
|
|
18
20
|
<kbd>{Astro.locals.t('search.ctrlKey')}</kbd><kbd>K</kbd>
|
|
19
21
|
</kbd>
|
|
20
|
-
</
|
|
22
|
+
</Button>
|
|
21
23
|
|
|
22
24
|
<DocsSearch
|
|
23
25
|
client:only="react"
|
|
@@ -31,27 +33,6 @@ import { SEARCH } from 'virtual:stl-starlight-virtual-module';
|
|
|
31
33
|
/>
|
|
32
34
|
</site-search>
|
|
33
35
|
|
|
34
|
-
{
|
|
35
|
-
SEARCH?.enableAISearch === true && (
|
|
36
|
-
<button id="chat-button" popovertarget="stldocs-chat" data-open-modal>
|
|
37
|
-
<Icon name="comment" />
|
|
38
|
-
</button>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
<style>
|
|
43
|
-
#chat-button {
|
|
44
|
-
background: var(--sl-color-bg-ui);
|
|
45
|
-
border: 1px solid var(--sl-color-hairline);
|
|
46
|
-
height: 2.25rem;
|
|
47
|
-
width: 2.25rem;
|
|
48
|
-
|
|
49
|
-
&:hover {
|
|
50
|
-
border: 1px solid rgb(64, 64, 64);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
</style>
|
|
54
|
-
|
|
55
36
|
<script is:inline>
|
|
56
37
|
function setupShortcut() {
|
|
57
38
|
const openBtn = document.querySelector('button[data-open-modal]');
|
|
@@ -78,3 +59,39 @@ import { SEARCH } from 'virtual:stl-starlight-virtual-module';
|
|
|
78
59
|
}
|
|
79
60
|
});
|
|
80
61
|
</script>
|
|
62
|
+
|
|
63
|
+
<style is:inline>
|
|
64
|
+
.default-tabs-container .stl-algolia-search {
|
|
65
|
+
max-width: unset;
|
|
66
|
+
width: unset;
|
|
67
|
+
flex-grow: 1;
|
|
68
|
+
}
|
|
69
|
+
.stl-algolia-search {
|
|
70
|
+
padding: 0;
|
|
71
|
+
}
|
|
72
|
+
@media (min-width: 50rem) {
|
|
73
|
+
.stl-algolia-search {
|
|
74
|
+
padding: 8px 10px;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.stl-algolia-search {
|
|
79
|
+
.icon-search {
|
|
80
|
+
display: inline;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.icon-close {
|
|
84
|
+
display: none;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
site-search:has(#stldocs-search[data-stldocs-modal-open='true']) .stl-algolia-search {
|
|
89
|
+
.icon-search {
|
|
90
|
+
display: none;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.icon-close {
|
|
94
|
+
display: inline;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { BASE_PATH,
|
|
3
|
-
import { parseRoute, generateRoute } from '@stainless-api/docs-ui/
|
|
4
|
-
import { SearchModal } from '@stainless-api/docs-
|
|
5
|
-
import { ChatModal } from '@stainless-api/docs-ui/src/components/chat';
|
|
2
|
+
import { BASE_PATH, HIGHLIGHT_THEMES } from 'virtual:stl-starlight-virtual-module';
|
|
3
|
+
import { parseRoute, generateRoute } from '@stainless-api/docs-ui/routing';
|
|
4
|
+
import { SearchModal } from '@stainless-api/docs-search';
|
|
6
5
|
import * as Markdoc from '@markdoc/markdoc';
|
|
7
6
|
import { createHighlighter } from 'shiki';
|
|
8
7
|
import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki';
|
|
9
8
|
|
|
10
9
|
import {
|
|
11
10
|
DocsProvider,
|
|
11
|
+
type MarkdownContext,
|
|
12
12
|
MarkdownProvider,
|
|
13
13
|
NavigationProvider,
|
|
14
|
-
|
|
15
|
-
} from '@stainless-api/docs-ui/
|
|
16
|
-
import
|
|
14
|
+
} from '@stainless-api/docs-ui/contexts';
|
|
15
|
+
import { ComponentProvider } from '@stainless-api/docs-ui/contexts/component';
|
|
16
|
+
import { SearchProvider } from '@stainless-api/docs-search/context';
|
|
17
|
+
import type { SearchSettings } from '@stainless-api/docs-search/types';
|
|
17
18
|
|
|
18
19
|
let $$highlighter: HighlighterGeneric<BundledLanguage, BundledTheme> | null = null;
|
|
19
20
|
async function getHighlighter() {
|
|
@@ -27,7 +28,7 @@ async function getHighlighter() {
|
|
|
27
28
|
return $$highlighter;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
async function createMarkdownRenderer() {
|
|
31
|
+
async function createMarkdownRenderer(): Promise<MarkdownContext> {
|
|
31
32
|
const highlighter = await getHighlighter();
|
|
32
33
|
const markdocConfig: Markdoc.Config = {
|
|
33
34
|
nodes: {
|
|
@@ -37,7 +38,10 @@ async function createMarkdownRenderer() {
|
|
|
37
38
|
transform(node, config) {
|
|
38
39
|
const attributes = node.transformAttributes(config);
|
|
39
40
|
const lang = node.attributes.language === 'node' ? 'typescript' : node.attributes.language;
|
|
40
|
-
const code = highlighter.codeToTokens(node.attributes.content, {
|
|
41
|
+
const code = highlighter.codeToTokens(node.attributes.content, {
|
|
42
|
+
lang,
|
|
43
|
+
theme: 'github-dark',
|
|
44
|
+
});
|
|
41
45
|
|
|
42
46
|
return new Markdoc.Tag(
|
|
43
47
|
'pre',
|
|
@@ -66,35 +70,49 @@ async function createMarkdownRenderer() {
|
|
|
66
70
|
},
|
|
67
71
|
};
|
|
68
72
|
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
return {
|
|
74
|
+
render: (content: string) => {
|
|
75
|
+
const ast = Markdoc.parse(content);
|
|
76
|
+
const transformed = Markdoc.transform(ast, markdocConfig);
|
|
77
|
+
return Markdoc.renderers.html(transformed);
|
|
78
|
+
},
|
|
79
|
+
highlight: (content: string, language: string) => {
|
|
80
|
+
return highlighter.codeToHtml(content, {
|
|
81
|
+
lang: language ?? 'javascript',
|
|
82
|
+
themes: HIGHLIGHT_THEMES || {},
|
|
83
|
+
});
|
|
84
|
+
},
|
|
73
85
|
};
|
|
74
86
|
}
|
|
75
87
|
|
|
76
88
|
export function DocsSearch({ settings, currentPath }: { settings: SearchSettings; currentPath: string }) {
|
|
77
|
-
const
|
|
89
|
+
const markdownRenderer = React.use(createMarkdownRenderer());
|
|
78
90
|
const { stainlessPath, language } = parseRoute(BASE_PATH, currentPath);
|
|
79
|
-
|
|
91
|
+
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
92
|
+
const pageFind = import.meta.env.DEV
|
|
93
|
+
? undefined
|
|
94
|
+
: `${import.meta.env.BASE_URL}/pagefind/pagefind.js`.replace(/\/+/g, '/');
|
|
80
95
|
|
|
81
|
-
function handleSelect(
|
|
82
|
-
const url =
|
|
96
|
+
function handleSelect(selectedPath: string) {
|
|
97
|
+
const url = selectedPath.startsWith('/')
|
|
98
|
+
? selectedPath
|
|
99
|
+
: generateRoute(BASE_PATH, language, selectedPath);
|
|
83
100
|
if (url) window.location.href = url;
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
return (
|
|
87
104
|
<DocsProvider spec={null} language={language}>
|
|
88
|
-
<
|
|
89
|
-
<
|
|
90
|
-
<
|
|
91
|
-
<
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
105
|
+
<ComponentProvider>
|
|
106
|
+
<NavigationProvider basePath="/" selectedPath={stainlessPath}>
|
|
107
|
+
<MarkdownProvider {...markdownRenderer}>
|
|
108
|
+
<SearchProvider onSelect={handleSelect} pageFind={pageFind} settings={settings}>
|
|
109
|
+
<div className="stldocs-root">
|
|
110
|
+
<SearchModal id="stldocs-search" />
|
|
111
|
+
</div>
|
|
112
|
+
</SearchProvider>
|
|
113
|
+
</MarkdownProvider>
|
|
114
|
+
</NavigationProvider>
|
|
115
|
+
</ComponentProvider>
|
|
98
116
|
</DocsProvider>
|
|
99
117
|
);
|
|
100
118
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// This is probably temporary, but it fills in functionality needed for Mintlify imports
|
|
2
2
|
|
|
3
3
|
import type { StarlightRouteData } from '@astrojs/starlight/route-data';
|
|
4
|
-
import type * as SDKJSON from '
|
|
5
|
-
import { walkTree } from '@stainless-api/docs-ui/
|
|
4
|
+
import type * as SDKJSON from '@stainless/sdk-json';
|
|
5
|
+
import { walkTree } from '@stainless-api/docs-ui/routing';
|
|
6
6
|
|
|
7
7
|
const INTERNAL_REFERENCE_ENTRY_MARKER = 'STL_STARLIGHT_API_REFERENCE_METHOD_LINK_PLACEHOLDER';
|
|
8
8
|
|