@stainless-api/docs 0.1.0-beta.10 → 0.1.0-beta.101

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.
Files changed (145) hide show
  1. package/CHANGELOG.md +855 -0
  2. package/eslint-suppressions.json +27 -0
  3. package/locals.d.ts +17 -0
  4. package/package.json +49 -40
  5. package/playground-virtual-modules.d.ts +96 -0
  6. package/plugin/assets/languages/cli.svg +14 -0
  7. package/plugin/assets/languages/csharp.svg +1 -0
  8. package/plugin/assets/languages/php.svg +4 -0
  9. package/plugin/buildAlgoliaIndex.ts +40 -39
  10. package/plugin/components/MethodDescription.tsx +54 -0
  11. package/plugin/components/RequestBuilder/ParamEditor.tsx +55 -0
  12. package/plugin/components/RequestBuilder/SnippetStainlessIsland.tsx +107 -0
  13. package/plugin/components/RequestBuilder/index.tsx +37 -0
  14. package/plugin/components/RequestBuilder/props.ts +9 -0
  15. package/plugin/components/RequestBuilder/spec-helpers.ts +47 -0
  16. package/plugin/components/RequestBuilder/styles.css +67 -0
  17. package/plugin/components/SDKSelect.astro +18 -105
  18. package/plugin/components/SnippetCode.tsx +111 -66
  19. package/plugin/components/StainlessIslands.tsx +126 -0
  20. package/plugin/components/search/SearchAlgolia.astro +45 -28
  21. package/plugin/components/search/SearchIsland.tsx +47 -29
  22. package/plugin/generateAPIReferenceLink.ts +2 -2
  23. package/plugin/globalJs/ai-dropdown-options.ts +243 -0
  24. package/plugin/globalJs/code-snippets.ts +40 -11
  25. package/plugin/globalJs/copy.ts +95 -17
  26. package/plugin/globalJs/create-playground.shim.ts +3 -0
  27. package/plugin/globalJs/method-descriptions.ts +33 -0
  28. package/plugin/globalJs/navigation.ts +12 -32
  29. package/plugin/globalJs/playground-data.shim.ts +1 -0
  30. package/plugin/globalJs/playground-data.ts +14 -0
  31. package/plugin/helpers/generateDocsRoutes.ts +59 -0
  32. package/plugin/helpers/multiSpec.ts +8 -0
  33. package/plugin/index.ts +299 -118
  34. package/plugin/languages.ts +8 -2
  35. package/plugin/loadPluginConfig.ts +251 -107
  36. package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +1 -1
  37. package/plugin/react/Routing.tsx +210 -141
  38. package/plugin/referencePlaceholderUtils.ts +18 -15
  39. package/plugin/replaceSidebarPlaceholderMiddleware.ts +38 -34
  40. package/plugin/routes/Docs.astro +70 -119
  41. package/plugin/routes/DocsStatic.astro +6 -5
  42. package/plugin/routes/Overview.astro +37 -27
  43. package/plugin/routes/markdown.ts +13 -12
  44. package/plugin/{cms → sidebar-utils}/sidebar-builder.ts +49 -60
  45. package/plugin/specs/FileCache.ts +99 -0
  46. package/plugin/specs/fetchSpecSSR.ts +27 -0
  47. package/plugin/specs/generateSpec.ts +112 -0
  48. package/plugin/specs/index.ts +132 -0
  49. package/plugin/specs/inputResolver.ts +146 -0
  50. package/plugin/{cms → specs}/worker.ts +82 -5
  51. package/plugin/vendor/preview.worker.docs.js +22406 -17955
  52. package/plugin/vendor/templates/cli.md +1 -0
  53. package/plugin/vendor/templates/go.md +4 -2
  54. package/plugin/vendor/templates/java.md +3 -1
  55. package/plugin/vendor/templates/kotlin.md +3 -1
  56. package/plugin/vendor/templates/node.md +4 -2
  57. package/plugin/vendor/templates/python.md +4 -2
  58. package/plugin/vendor/templates/ruby.md +4 -2
  59. package/plugin/vendor/templates/terraform.md +1 -1
  60. package/plugin/vendor/templates/typescript.md +3 -1
  61. package/resolveSrcFile.ts +10 -0
  62. package/scripts/vendor_deps.ts +5 -5
  63. package/shared/getProsePages.ts +41 -0
  64. package/shared/getSharedLogger.ts +15 -0
  65. package/shared/terminalUtils.ts +3 -0
  66. package/shared/virtualModule.ts +54 -1
  67. package/src/content.config.ts +9 -0
  68. package/stl-docs/components/AIDropdown.tsx +63 -0
  69. package/stl-docs/components/AiChatIsland.tsx +14 -0
  70. package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +2 -2
  71. package/stl-docs/components/Head.astro +20 -0
  72. package/stl-docs/components/Header.astro +6 -8
  73. package/stl-docs/components/PageFrame.astro +18 -0
  74. package/stl-docs/components/PageTitle.astro +82 -0
  75. package/stl-docs/components/TableOfContents.astro +34 -0
  76. package/stl-docs/components/ThemeProvider.astro +36 -0
  77. package/stl-docs/components/ThemeSelect.astro +84 -139
  78. package/stl-docs/components/content-panel/ContentPanel.astro +16 -46
  79. package/stl-docs/components/headers/SplashMobileMenuToggle.astro +17 -1
  80. package/stl-docs/components/headers/StackedHeader.astro +29 -24
  81. package/stl-docs/components/icons/chat-gpt.tsx +2 -2
  82. package/stl-docs/components/icons/cursor.tsx +10 -0
  83. package/stl-docs/components/icons/gemini.tsx +19 -0
  84. package/stl-docs/components/icons/markdown.tsx +1 -1
  85. package/stl-docs/components/index.ts +1 -0
  86. package/stl-docs/components/mintlify-compat/Accordion.astro +5 -3
  87. package/stl-docs/components/mintlify-compat/AccordionGroup.astro +3 -3
  88. package/stl-docs/components/mintlify-compat/Columns.astro +40 -42
  89. package/stl-docs/components/mintlify-compat/Frame.astro +16 -18
  90. package/stl-docs/components/mintlify-compat/card.css +33 -35
  91. package/stl-docs/components/mintlify-compat/index.ts +2 -4
  92. package/stl-docs/components/nav-tabs/NavDropdown.astro +31 -70
  93. package/stl-docs/components/nav-tabs/NavTabs.astro +78 -80
  94. package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +15 -8
  95. package/stl-docs/components/nav-tabs/buildNavLinks.ts +3 -2
  96. package/stl-docs/components/pagination/HomeLink.astro +10 -0
  97. package/stl-docs/components/pagination/Pagination.astro +175 -0
  98. package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +22 -0
  99. package/stl-docs/components/pagination/PaginationLinkQuiet.astro +13 -0
  100. package/stl-docs/components/pagination/util.ts +71 -0
  101. package/stl-docs/components/scripts.ts +1 -0
  102. package/stl-docs/components/sidebars/BaseSidebar.astro +79 -2
  103. package/stl-docs/components/sidebars/SidebarWithComponents.tsx +10 -0
  104. package/stl-docs/components/sidebars/convertAstroSidebarToStl.tsx +62 -0
  105. package/stl-docs/disableCalloutSyntax.ts +36 -0
  106. package/stl-docs/fonts.ts +186 -0
  107. package/stl-docs/index.ts +154 -51
  108. package/stl-docs/loadStlDocsConfig.ts +58 -8
  109. package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +61 -0
  110. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +41 -0
  111. package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
  112. package/stl-docs/proseSearchIndexing.ts +606 -0
  113. package/stl-docs/tabsMiddleware.ts +13 -4
  114. package/styles/code.css +133 -136
  115. package/styles/links.css +11 -48
  116. package/styles/method-descriptions.css +36 -0
  117. package/styles/overrides.css +49 -57
  118. package/styles/page.css +100 -59
  119. package/styles/sdk_select.css +9 -7
  120. package/styles/search.css +57 -69
  121. package/styles/sidebar.css +26 -156
  122. package/styles/{variables.css → sl-variables.css} +3 -2
  123. package/styles/stldocs-variables.css +6 -0
  124. package/styles/toc.css +41 -34
  125. package/theme.css +11 -11
  126. package/tsconfig.json +2 -5
  127. package/virtual-module.d.ts +64 -8
  128. package/components/variables.css +0 -135
  129. package/plugin/cms/client.ts +0 -62
  130. package/plugin/cms/server.ts +0 -268
  131. package/plugin/globalJs/ai-dropdown.ts +0 -57
  132. package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -58
  133. package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -55
  134. package/stl-docs/components/mintlify-compat/Step.astro +0 -58
  135. package/stl-docs/components/mintlify-compat/Steps.astro +0 -17
  136. package/styles/fonts.css +0 -68
  137. /package/{plugin/assets → assets}/fonts/geist/OFL.txt +0 -0
  138. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
  139. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin.woff2 +0 -0
  140. /package/{plugin/assets → assets}/fonts/geist/geist-latin-ext.woff2 +0 -0
  141. /package/{plugin/assets → assets}/fonts/geist/geist-latin.woff2 +0 -0
  142. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
  143. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
  144. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
  145. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin.woff2 +0 -0
@@ -1,22 +1,25 @@
1
1
  import * as React from 'react';
2
- import { marked } from 'marked';
3
- import hljs from 'highlight.js';
4
- import { createMarkdownProcessor, type MarkdownProcessor } from '@astrojs/markdown-remark';
2
+ import { marked, Tokens } from 'marked';
3
+ import { getDocsLanguages } from '../helpers/multiSpec';
4
+
5
+ import {
6
+ createMarkdownProcessor,
7
+ CreateShikiHighlighterOptions,
8
+ type MarkdownProcessor,
9
+ } from '@astrojs/markdown-remark';
5
10
  import remarkGfmAlerts from 'remark-github-alerts';
6
11
 
7
12
  import type { MarkdownHeading } from 'astro';
8
13
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
9
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
10
- import type { DocsLanguage } from '@stainless-api/docs-ui/src/routing';
14
+ import type * as SDKJSON from '@stainless/sdk-json';
15
+ import { LanguageNames, SupportedLanguageSyntaxes, type DocsLanguage } from '@stainless-api/docs-ui/routing';
11
16
 
12
17
  import {
13
- generateRouteList,
14
18
  generateRoute,
15
19
  parseStainlessPath,
16
20
  walkTree,
17
- SupportedLanguageSyntaxes,
18
21
  getLanguageSnippet,
19
- } from '@stainless-api/docs-ui/src/routing';
22
+ } from '@stainless-api/docs-ui/routing';
20
23
 
21
24
  import {
22
25
  DocsProvider,
@@ -24,63 +27,45 @@ import {
24
27
  NavigationProvider,
25
28
  useSpec,
26
29
  type ContentPanelLayout,
27
- } from '@stainless-api/docs-ui/src/contexts';
30
+ } from '@stainless-api/docs-ui/contexts';
28
31
 
29
- import { flatResources, getResourceFromSpec } from '@stainless-api/docs-ui/src/utils';
32
+ import { flatResources, getResourceFromSpec } from '@stainless-api/docs-ui/utils';
30
33
 
31
34
  import {
32
35
  SDKMethod,
33
36
  SDKResource,
34
37
  type SDKRequestTitleProps,
35
38
  SDKBreadcrumbs,
36
- Dropdown,
37
- DropdownTrigger,
38
- DropdownMenu,
39
- DropdownItem,
40
39
  SDKIcon,
41
40
  SDKOverview,
42
41
  SDKLanguageBlock,
43
- } from '@stainless-api/docs-ui/src/components';
42
+ } from '@stainless-api/docs-ui/components';
43
+
44
+ import { Dropdown } from '@stainless-api/docs/components';
45
+
44
46
  import {
45
- BASE_PATH,
46
- EXCLUDE_LANGUAGES,
47
+ RESOLVED_API_REFERENCE_PATH,
47
48
  EXPAND_RESOURCES,
48
49
  HIGHLIGHT_THEMES,
49
50
  BREADCRUMB_CONFIG,
50
51
  PROPERTY_SETTINGS,
51
- INCLUDE_AI_DROPDOWN_OPTIONS,
52
+ ENABLE_CONTEXT_MENU,
53
+ EXPERIMENTAL_COLLAPSIBLE_METHOD_DESCRIPTIONS,
52
54
  } from 'virtual:stl-starlight-virtual-module';
53
- import style from '@stainless-api/docs-ui/src/style';
54
- import { createHighlighter, type BundledLanguage, type BundledTheme, type HighlighterGeneric } from 'shiki';
55
- import { SnippetCode, SnippetContainer, SnippetRequestContainer } from '../components/SnippetCode';
56
- import clsx from 'clsx';
55
+ import style from '@stainless-api/docs-ui/style';
56
+ import { BundledTheme, createHighlighter, HighlighterGeneric, type BundledLanguage } from 'shiki';
57
+ import {
58
+ SnippetCode,
59
+ SnippetContainer,
60
+ SnippetButtons,
61
+ SnippetFooter,
62
+ SnippetResponse,
63
+ } from '../components/SnippetCode';
57
64
  import type { StlStarlightMiddleware } from '../middlewareBuilder/stainlessMiddleware';
58
- import { ComponentProvider } from '@stainless-api/docs-ui/src/contexts/component';
59
- import { APIReferenceAIDropdown } from '../../stl-docs/components/APIReferenceAIDropdown';
60
-
61
- export function generateDocsRoutes(spec: SDKJSON.Spec) {
62
- const paths = generateRouteList({
63
- spec,
64
- excludeLanguages: EXCLUDE_LANGUAGES as DocsLanguage[],
65
- });
66
- const readmes = Object.entries(spec.readme)
67
- .filter(([language]) => language !== 'http')
68
- .filter(([language]) => !EXCLUDE_LANGUAGES.includes(language as DocsLanguage))
69
- .map(([language]) => ({
70
- slug: language,
71
- stainlessPath: null,
72
- language: language as DocsLanguage,
73
- title: 'Readme',
74
- kind: 'readme',
75
- }));
76
-
77
- return [...paths, ...readmes].map(({ slug, stainlessPath, language, title, kind }) => {
78
- return {
79
- params: { slug },
80
- props: { stainlessPath, language, title, kind },
81
- };
82
- });
83
- }
65
+ import { ComponentProvider } from '@stainless-api/docs-ui/contexts/component';
66
+ import { AIDropdown } from '../../stl-docs/components/AIDropdown';
67
+ import { ChevronsUpDownIcon } from 'lucide-react';
68
+ import { MethodDescription } from '../components/MethodDescription';
84
69
 
85
70
  function isResourceNonEmpty(resource: SDKJSON.Resource) {
86
71
  return (
@@ -121,7 +106,6 @@ export function buildSidebar(
121
106
 
122
107
  const meths: SidebarEntry[] = Object.values(resource.methods ?? [])
123
108
  .filter((method) => spec.decls?.[language]?.[method.stainlessPath])
124
- .toSorted((first, second) => first.name.localeCompare(second.name))
125
109
  .map((method) => ({
126
110
  type: 'link',
127
111
  isCurrent: current === method.stainlessPath,
@@ -153,78 +137,120 @@ export function buildPageNavigation(resource: SDKJSON.Resource, depth: number =
153
137
  return [...output, ...subs];
154
138
  }
155
139
 
156
- function renderMarkdown(content: string) {
157
- return marked.parse(content, { gfm: true }) as string;
140
+ async function renderMarkdown(content: string) {
141
+ const highlighter = await astroHighlight();
142
+
143
+ const renderer = {
144
+ code({ text, lang }: Tokens.Code) {
145
+ return shikiHighlight({
146
+ highlighter,
147
+ content: text,
148
+ language: lang,
149
+ });
150
+ },
151
+ };
152
+
153
+ marked.use({ renderer });
154
+ return marked.parse(content, {
155
+ gfm: true,
156
+ }) as string;
158
157
  }
159
158
 
160
- async function highlight(content: string, language?: string) {
161
- if (language === 'json') return hljs.highlight(content, { language }).value;
162
- const highlighter = await astroHighlight();
159
+ function shikiHighlight({
160
+ highlighter,
161
+ content,
162
+ language,
163
+ themes,
164
+ }: {
165
+ highlighter: HighlighterGeneric<BundledLanguage, BundledTheme>;
166
+ content: string;
167
+ language?: string;
168
+ themes?: CreateShikiHighlighterOptions['themes'] | Record<string, 'stainless-docs-json'>;
169
+ }) {
170
+ let _themes = themes;
171
+ if (!themes && language === 'json') {
172
+ _themes = {
173
+ light: 'stainless-docs-json',
174
+ dark: 'stainless-docs-json',
175
+ };
176
+ }
177
+
178
+ if (!_themes) {
179
+ _themes = HIGHLIGHT_THEMES;
180
+ }
163
181
  return highlighter.codeToHtml(content, {
164
182
  lang: language ?? 'javascript',
165
- themes: HIGHLIGHT_THEMES || {},
183
+ themes: _themes || {},
166
184
  });
167
185
  }
168
186
 
187
+ async function highlight(content: string, language?: string) {
188
+ const highlighter = await astroHighlight();
189
+ return shikiHighlight({ highlighter, content, language });
190
+ }
191
+
169
192
  export function SDKSelectReactComponent({
170
193
  selected,
171
194
  languages,
172
- id,
173
195
  className,
196
+ ...rest
174
197
  }: {
175
198
  selected: DocsLanguage;
176
199
  languages: DocsLanguage[];
177
- id: string;
178
200
  className?: string;
179
- }) {
201
+ } & Omit<React.ComponentProps<'div'>, 'children'>) {
180
202
  return (
181
- <Dropdown id={id} data-current-value={selected} className={className}>
182
- <DropdownTrigger
183
- className="dropdown-toggle stldocs-button-tertiary"
184
- type="button"
185
- id="stldocs-snippet-title-button"
186
- aria-expanded="false"
187
- withChevron
188
- >
189
- <SDKIcon language={getLanguageSnippet(selected)} size={16} />
190
- <span className={clsx('stl-snippet-dropdown-button-text', selected)}>{selected}</span>
191
- </DropdownTrigger>
192
- <DropdownMenu
203
+ <Dropdown data-current-value={selected} className={className} {...rest}>
204
+ <Dropdown.Trigger>
205
+ <Dropdown.TriggerSelectedItem>
206
+ <Dropdown.Icon>
207
+ <SDKIcon language={getLanguageSnippet(selected)} />
208
+ </Dropdown.Icon>
209
+ <span className="stl-snippet-dropdown-button-text">{LanguageNames[selected]}</span>
210
+ </Dropdown.TriggerSelectedItem>
211
+ <Dropdown.TriggerIcon>
212
+ <ChevronsUpDownIcon size={16} />
213
+ </Dropdown.TriggerIcon>
214
+ </Dropdown.Trigger>
215
+ <Dropdown.Menu
193
216
  className="dropdown-menu stl-sdk-select-dropdown-menu"
194
- position="below"
195
- aria-labelledby="stldocs-snippet-title-button"
217
+ aria-labelledby="stl-docs-snippet-title-button"
196
218
  >
197
219
  {languages.map((item) => (
198
- <DropdownItem key={item} value={item} selected={item === selected}>
199
- <div>
220
+ <Dropdown.MenuItem key={item} value={item} isSelected={item === selected}>
221
+ <Dropdown.Icon>
200
222
  <SDKIcon language={getLanguageSnippet(item)} size={16} />
201
- <span className={clsx('stl-snippet-dropdown-button-text', item)}>{item}</span>
202
- </div>
203
- </DropdownItem>
223
+ </Dropdown.Icon>
224
+ <Dropdown.MenuItemText>{LanguageNames[item]}</Dropdown.MenuItemText>
225
+ <Dropdown.MenuItemTemplate>
226
+ <Dropdown.Icon>
227
+ <SDKIcon language={getLanguageSnippet(item)} size={16} />
228
+ </Dropdown.Icon>
229
+ <span className="stl-snippet-dropdown-button-text">{LanguageNames[item]}</span>
230
+ </Dropdown.MenuItemTemplate>
231
+ </Dropdown.MenuItem>
204
232
  ))}
205
- </DropdownMenu>
233
+ </Dropdown.Menu>
206
234
  </Dropdown>
207
235
  );
208
236
  }
209
237
 
210
238
  function SDKRequestTitle({ snippetLanguage }: SDKRequestTitleProps) {
211
- const spec = useSpec();
212
-
213
239
  const selected = snippetLanguage.split('.').at(0) as DocsLanguage;
214
- const languages = (spec?.docs?.languages ?? ['http']).filter((lang) => !EXCLUDE_LANGUAGES.includes(lang));
240
+ const languages = getDocsLanguages();
215
241
 
216
242
  return (
217
243
  <SDKSelectReactComponent
218
244
  selected={selected || 'http'}
219
245
  languages={languages}
220
- id="stldocs-snippet-select"
221
- className="stl-sdk-select"
246
+ data-stldocs-snippet-select
247
+ className="stl-sdk-select stl-ui-not-prose"
222
248
  />
223
249
  );
224
250
  }
225
251
 
226
252
  export type SpecMetadata = [
227
- 'http' | 'node' | 'python' | 'go' | 'typescript' | 'terraform' | 'ruby' | 'java' | 'kotlin',
253
+ DocsLanguage,
228
254
  {
229
255
  repo_url?: string;
230
256
  code_url?: string;
@@ -234,15 +260,26 @@ export type SpecMetadata = [
234
260
  },
235
261
  ][];
236
262
 
263
+ const componentOverrides = {
264
+ SDKRequestTitle,
265
+ SnippetCode,
266
+ SnippetContainer,
267
+ SnippetButtons,
268
+ SnippetFooter,
269
+ SnippetResponse,
270
+ ...(EXPERIMENTAL_COLLAPSIBLE_METHOD_DESCRIPTIONS ? { MethodDescription } : {}),
271
+ } satisfies React.ComponentProps<typeof ComponentProvider>['components'];
272
+
237
273
  export function RenderLibraries({ metadata }: { metadata: SpecMetadata }) {
238
274
  return (
239
- <ComponentProvider components={{}}>
275
+ <ComponentProvider components={componentOverrides}>
240
276
  {metadata.map(([language, data]) => (
241
277
  <SDKLanguageBlock
278
+ key={language}
242
279
  language={language}
243
280
  version={data.version || ''}
244
281
  install={data.install || ''}
245
- links={{ repo: data.repo_url || '#', docs: `${BASE_PATH}/${language}` }}
282
+ links={{ repo: data.repo_url || '#', docs: `${RESOLVED_API_REFERENCE_PATH}/${language}` }}
246
283
  />
247
284
  ))}
248
285
  </ComponentProvider>
@@ -254,15 +291,8 @@ export function RenderSpecOverview({ spec, language }: { spec: SDKJSON.Spec; lan
254
291
 
255
292
  return (
256
293
  <DocsProvider spec={spec} language={language ?? 'node'}>
257
- <ComponentProvider
258
- components={{
259
- SDKRequestTitle,
260
- SnippetCode,
261
- SnippetContainer,
262
- SnippetRequestContainer,
263
- }}
264
- >
265
- <NavigationProvider basePath={BASE_PATH}>
294
+ <ComponentProvider components={componentOverrides}>
295
+ <NavigationProvider basePath={RESOLVED_API_REFERENCE_PATH}>
266
296
  <MarkdownProvider render={renderMarkdown} highlight={highlight}>
267
297
  <div className={style.Overview}>
268
298
  {resources
@@ -303,7 +333,10 @@ export function RenderSpec({
303
333
  const parsed = parseStainlessPath(path);
304
334
  const resource = getResourceFromSpec(path, spec);
305
335
 
306
- if (!resource || !parsed) return null;
336
+ if (!resource || !parsed) {
337
+ console.warn(`Could not find resource or parsed path for '${path}'`);
338
+ return null;
339
+ }
307
340
 
308
341
  return (
309
342
  <DocsProvider
@@ -314,46 +347,30 @@ export function RenderSpec({
314
347
  properties: PROPERTY_SETTINGS,
315
348
  }}
316
349
  >
317
- <ComponentProvider
318
- components={{
319
- SDKRequestTitle,
320
- SnippetCode,
321
- SnippetContainer,
322
- SnippetRequestContainer,
323
- }}
324
- >
325
- <NavigationProvider basePath={BASE_PATH} selectedPath={path}>
350
+ <ComponentProvider components={componentOverrides}>
351
+ <NavigationProvider basePath={RESOLVED_API_REFERENCE_PATH} selectedPath={path}>
326
352
  <MarkdownProvider render={renderMarkdown} highlight={highlight}>
327
- {kind === 'http_method' ? (
353
+ {
328
354
  <div className="stldocs-root stl-ui-not-prose">
329
355
  <div className="stl-page-nav-container">
330
356
  <SDKBreadcrumbs
331
357
  spec={spec as SDKJSON.Spec}
332
358
  currentPath={currentPath}
333
- basePath={BASE_PATH}
359
+ basePath={RESOLVED_API_REFERENCE_PATH}
334
360
  config={BREADCRUMB_CONFIG}
335
361
  />
336
- {INCLUDE_AI_DROPDOWN_OPTIONS && <APIReferenceAIDropdown />}
362
+ {ENABLE_CONTEXT_MENU && <AIDropdown />}
337
363
  </div>
338
- <SDKMethod
339
- method={resource.methods[parsed.method]}
340
- transformRequestSnippet={transformRequestSnippet}
341
- />
342
- </div>
343
- ) : (
344
- <div className="stldocs-root stl-ui-not-prose">
345
- <div className="stl-page-nav-container">
346
- <SDKBreadcrumbs
347
- spec={spec as SDKJSON.Spec}
348
- currentPath={currentPath}
349
- basePath={BASE_PATH}
350
- config={BREADCRUMB_CONFIG}
364
+ {kind === 'http_method' ? (
365
+ <SDKMethod
366
+ method={resource.methods[parsed.method!]!}
367
+ transformRequestSnippet={transformRequestSnippet}
351
368
  />
352
- {INCLUDE_AI_DROPDOWN_OPTIONS && <APIReferenceAIDropdown />}
353
- </div>
354
- <SDKOverview resource={resource} />
369
+ ) : (
370
+ <SDKOverview resource={resource} />
371
+ )}
355
372
  </div>
356
- )}
373
+ }
357
374
  </MarkdownProvider>
358
375
  </NavigationProvider>
359
376
  </ComponentProvider>
@@ -367,10 +384,14 @@ export function RenderMethod({ path }: { path: string }) {
367
384
 
368
385
  const parsed = parseStainlessPath(path);
369
386
  const resource = getResourceFromSpec(path, spec);
370
- if (!resource || !parsed) return null;
371
387
 
372
- const meth = resource.methods[parsed.method];
373
- return <SDKMethod method={meth} />;
388
+ if (!resource || !parsed) {
389
+ console.warn(`Could not find resource or parsed path for '${path}'`);
390
+ return null;
391
+ }
392
+
393
+ const method = resource.methods[parsed.method!]!;
394
+ return <SDKMethod method={method} />;
374
395
  }
375
396
 
376
397
  export async function getReadmeContent(spec: SDKJSON.Spec, language: DocsLanguage) {
@@ -390,8 +411,68 @@ export async function getReadmeContent(spec: SDKJSON.Spec, language: DocsLanguag
390
411
  return spec.readme[language];
391
412
  }
392
413
 
414
+ let astroShikiHighlighter:
415
+ | HighlighterGeneric<BundledLanguage, BundledTheme>
416
+ | Promise<HighlighterGeneric<BundledLanguage, BundledTheme>>
417
+ | null = null;
418
+
419
+ async function astroHighlight() {
420
+ if (astroShikiHighlighter) {
421
+ return astroShikiHighlighter;
422
+ }
423
+
424
+ astroShikiHighlighter = createHighlighter({
425
+ themes: [
426
+ 'github-light',
427
+ 'github-dark',
428
+ {
429
+ name: 'stainless-docs-json',
430
+ colors: {
431
+ 'editor.background': 'var(--stl-color-background)',
432
+ 'editor.foreground': 'var(--stl-color-foreground)',
433
+ },
434
+
435
+ tokenColors: [
436
+ {
437
+ scope: ['comment', 'punctuation.definition.comment'],
438
+ settings: { foreground: 'var(--stl-color-foreground-muted)' },
439
+ },
440
+ // numbers, booleans, null
441
+ {
442
+ scope: ['constant.numeric', 'constant.language'],
443
+ settings: { foreground: 'var(--stl-color-orange-foreground)' },
444
+ },
445
+ // strings
446
+ {
447
+ scope: ['string', 'string.quoted', 'string.template'],
448
+ settings: { foreground: 'var(--stl-color-green-foreground)' },
449
+ },
450
+ // Keys, brackets
451
+ {
452
+ scope: ['support.type', 'meta'],
453
+ settings: { foreground: 'var(--stl-color-foreground)' },
454
+ },
455
+ // brackets
456
+ {
457
+ scope: ['meta'],
458
+ settings: { foreground: 'var(--stl-color-foreground-muted)' },
459
+ },
460
+ // built-in types
461
+ {
462
+ scope: ['support.type.builtin'],
463
+ settings: { foreground: 'var(--stl-color-purple-foreground)' },
464
+ },
465
+ ],
466
+ },
467
+ ],
468
+ langs: SupportedLanguageSyntaxes,
469
+ });
470
+
471
+ return astroShikiHighlighter;
472
+ }
473
+
393
474
  // Astro's markdown processor is a singleton
394
- // Need to cache it instead of instanting per request
475
+ // Need to cache it instead of instantiating per request
395
476
  let astroMarkdownProcessor: MarkdownProcessor;
396
477
  async function astroMarkdown() {
397
478
  if (!astroMarkdownProcessor) {
@@ -407,18 +488,6 @@ async function astroMarkdown() {
407
488
  return astroMarkdownProcessor;
408
489
  }
409
490
 
410
- let astroShikiHighlighter: HighlighterGeneric<BundledLanguage, BundledTheme>;
411
- async function astroHighlight() {
412
- if (!astroShikiHighlighter) {
413
- astroShikiHighlighter = await createHighlighter({
414
- themes: ['github-light', 'github-dark'],
415
- langs: SupportedLanguageSyntaxes,
416
- });
417
- }
418
-
419
- return astroShikiHighlighter;
420
- }
421
-
422
491
  export async function astroMarkdownRender(content: string) {
423
492
  const md = await astroMarkdown();
424
493
  const output = await md.render(content);
@@ -21,27 +21,30 @@ export function makePlaceholderItems(id: number) {
21
21
 
22
22
  type StarlightConfig = Parameters<typeof starlight>[0];
23
23
 
24
- type SidebarConfigEntry = Exclude<StarlightConfig['sidebar'], undefined>[number];
24
+ export type SidebarConfigEntry = Exclude<StarlightConfig['sidebar'], undefined>[number];
25
25
 
26
26
  export function getAPIReferencePlaceholderItemFromSidebarConfig(
27
27
  sidebar: SidebarConfigEntry[],
28
- ): SidebarConfigEntry | null {
29
- for (const item of sidebar) {
30
- if (typeof item === 'string') {
31
- continue;
32
- }
33
- if ('items' in item) {
34
- const found = getAPIReferencePlaceholderItemFromSidebarConfig(item.items);
35
- if (found) {
36
- return found;
28
+ ): SidebarConfigEntry[] {
29
+ const items: SidebarConfigEntry[] = [];
30
+
31
+ function recursiveGetPlaceholderItems(entries: SidebarConfigEntry[]) {
32
+ for (const item of entries) {
33
+ if (typeof item === 'string') {
34
+ continue;
35
+ }
36
+ if ('attrs' in item && item.attrs?.about === INTERNAL_REFERENCE_ENTRY_MARKER) {
37
+ items.push(item);
38
+ }
39
+ if ('items' in item) {
40
+ recursiveGetPlaceholderItems(item.items);
37
41
  }
38
- }
39
- if ('attrs' in item && item.attrs?.about === INTERNAL_REFERENCE_ENTRY_MARKER) {
40
- return item;
41
42
  }
42
43
  }
43
44
 
44
- return null;
45
+ recursiveGetPlaceholderItems(sidebar);
46
+
47
+ return items;
45
48
  }
46
49
 
47
50
  type SidebarEntry = StarlightRouteData['sidebar'][number];
@@ -58,7 +61,7 @@ function recursiveGetPlaceholderItems(
58
61
  items: PlaceholderItemResult[],
59
62
  ): PlaceholderItemResult[] {
60
63
  for (let i = 0; i < sidebar.length; i++) {
61
- const entry = sidebar[i];
64
+ const entry = sidebar[i]!;
62
65
  if ('attrs' in entry && entry.attrs?.about === INTERNAL_REFERENCE_ENTRY_MARKER) {
63
66
  items.push({
64
67
  index: i,
@@ -1,54 +1,58 @@
1
- import { defineRouteMiddleware } from '@astrojs/starlight/route-data';
1
+ import { defineRouteMiddleware, StarlightRouteData } from '@astrojs/starlight/route-data';
2
2
 
3
- import { cmsClient } from './cms/client';
4
- import { BASE_PATH } from 'virtual:stl-starlight-virtual-module';
3
+ import { RESOLVED_API_REFERENCE_PATH } from 'virtual:stl-starlight-virtual-module';
5
4
  import { getAPIReferencePlaceholderItems } from './referencePlaceholderUtils';
6
- import { getMethodFromSDKJSON, recursiveReplacePlaceholderItems } from './generateAPIReferenceLink';
7
- import { forceGenerateRoute } from './cms/sidebar-builder';
8
- import { parseRoute } from '@stainless-api/docs-ui/src/routing';
5
+ import { parseRoute } from '@stainless-api/docs-ui/routing';
6
+ import path from 'path';
7
+ import { sidebars } from 'virtual:stl-starlight-reference-sidebars';
9
8
 
10
9
  // this fn is loaded in the plugin via addRouteMiddleware
11
10
 
11
+ type SidebarEntry = StarlightRouteData['sidebar'][number];
12
+
13
+ const removeTrailingSlash = (value: string) => (value.endsWith('/') ? value.slice(0, -1) : value);
14
+
15
+ function markCurrentItems(sidebar: SidebarEntry[], currentSlug: string) {
16
+ // IMPORTANT: we need to clone the sidebar to avoid mutating the original sidebar
17
+ const mutableSidebarInstance = structuredClone(sidebar);
18
+ const normalizedCurrentSlug = removeTrailingSlash(currentSlug);
19
+
20
+ function recursiveMarkCurrent(entries: SidebarEntry[]) {
21
+ for (const entry of entries) {
22
+ if (entry.type === 'link') {
23
+ entry.isCurrent = removeTrailingSlash(entry.href) === normalizedCurrentSlug;
24
+ }
25
+ if (entry.type === 'group') {
26
+ recursiveMarkCurrent(entry.entries);
27
+ }
28
+ }
29
+ }
30
+ recursiveMarkCurrent(mutableSidebarInstance);
31
+
32
+ return mutableSidebarInstance;
33
+ }
34
+
12
35
  export const onRequest = defineRouteMiddleware(async (context) => {
13
36
  // if using content collection schema, use: context.locals.starlightRoute.entry.data.stainlessStarlight
14
37
  // this worked without collections but relied on hijacking starlightRoute: context.props.frontmatter.stainlessStarlight
15
-
16
- const slug = `/${context.locals.starlightRoute.id}`; // same as .slug but not deprecated
38
+ const slug = path.posix.join(import.meta.env.BASE_URL ?? '', `/${context.locals.starlightRoute.id}`); // same as .slug but not deprecated
17
39
 
18
40
  context.locals.starlightRoute._stlStarlight = {
19
- basePath: BASE_PATH,
41
+ basePath: RESOLVED_API_REFERENCE_PATH,
20
42
  };
21
43
 
22
44
  const apiReferencePlaceholderItems = getAPIReferencePlaceholderItems(context.locals.starlightRoute.sidebar);
23
45
 
24
- const spec = await cmsClient.getSpec();
25
-
26
- const { language, stainlessPath } = parseRoute(BASE_PATH, slug);
27
-
28
- // This is probably temporary, but it fills in functionality needed for Mintlify imports
29
- recursiveReplacePlaceholderItems(context.locals.starlightRoute.sidebar, (entry, { endpoint, label }) => {
30
- const method = getMethodFromSDKJSON(spec, endpoint);
31
-
32
- const route = forceGenerateRoute({
33
- basePath: BASE_PATH,
34
- stainlessPath: method.stainlessPath,
35
- language,
36
- });
37
- entry.href = route;
38
- entry.isCurrent = method.stainlessPath === stainlessPath;
39
- if (!label) {
40
- entry.label = method.summary ?? method.name;
41
- }
42
- entry.attrs['data-stldocs-method'] = method.httpMethod;
43
- });
46
+ const { language } = parseRoute(RESOLVED_API_REFERENCE_PATH, slug);
44
47
 
45
48
  for (const item of apiReferencePlaceholderItems) {
46
- const entries = await cmsClient.buildSidebar({
47
- basePath: BASE_PATH,
48
- currentSlug: slug,
49
- sidebarId: item.sidebarId,
50
- });
49
+ const entries = sidebars.find((sb) => sb.id === item.sidebarId && sb.language === language)?.entries;
50
+ if (!entries) {
51
+ throw new Error(`Expected sidebar entries for sidebar ID ${item.sidebarId} and language ${language}`);
52
+ }
51
53
 
52
54
  item.group.splice(item.index, 1, ...entries);
53
55
  }
56
+
57
+ context.locals.starlightRoute.sidebar = markCurrentItems(context.locals.starlightRoute.sidebar, slug);
54
58
  });