@stainless-api/docs 0.1.0-beta.12 → 0.1.0-beta.121

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