@stainless-api/docs 0.1.0-beta.1 → 0.1.0-beta.100

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 (158) hide show
  1. package/CHANGELOG.md +917 -0
  2. package/eslint-suppressions.json +27 -0
  3. package/locals.d.ts +17 -0
  4. package/package.json +50 -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 -35
  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 -33
  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/getPageLoadEvent.ts +1 -1
  33. package/plugin/helpers/multiSpec.ts +8 -0
  34. package/plugin/index.ts +299 -117
  35. package/plugin/languages.ts +8 -2
  36. package/plugin/loadPluginConfig.ts +254 -102
  37. package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +1 -1
  38. package/plugin/react/Routing.tsx +210 -140
  39. package/plugin/referencePlaceholderUtils.ts +18 -15
  40. package/plugin/replaceSidebarPlaceholderMiddleware.ts +40 -32
  41. package/plugin/routes/Docs.astro +70 -119
  42. package/plugin/routes/DocsStatic.astro +6 -5
  43. package/plugin/routes/Overview.astro +37 -27
  44. package/plugin/routes/markdown.ts +13 -12
  45. package/plugin/{cms → sidebar-utils}/sidebar-builder.ts +49 -60
  46. package/plugin/specs/FileCache.ts +99 -0
  47. package/plugin/specs/fetchSpecSSR.ts +27 -0
  48. package/plugin/specs/generateSpec.ts +112 -0
  49. package/plugin/specs/index.ts +132 -0
  50. package/plugin/specs/inputResolver.ts +146 -0
  51. package/plugin/{cms → specs}/worker.ts +82 -5
  52. package/plugin/vendor/preview.worker.docs.js +22406 -17955
  53. package/plugin/vendor/templates/cli.md +1 -0
  54. package/plugin/vendor/templates/go.md +4 -2
  55. package/plugin/vendor/templates/java.md +3 -1
  56. package/plugin/vendor/templates/kotlin.md +3 -1
  57. package/plugin/vendor/templates/node.md +4 -2
  58. package/plugin/vendor/templates/python.md +4 -2
  59. package/plugin/vendor/templates/ruby.md +4 -2
  60. package/plugin/vendor/templates/terraform.md +1 -1
  61. package/plugin/vendor/templates/typescript.md +3 -1
  62. package/resolveSrcFile.ts +10 -0
  63. package/scripts/vendor_deps.ts +5 -5
  64. package/shared/getProsePages.ts +42 -0
  65. package/shared/getSharedLogger.ts +15 -0
  66. package/shared/terminalUtils.ts +3 -0
  67. package/shared/virtualModule.ts +54 -1
  68. package/src/content.config.ts +9 -0
  69. package/stl-docs/components/AIDropdown.tsx +63 -0
  70. package/stl-docs/components/AiChatIsland.tsx +14 -0
  71. package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +2 -2
  72. package/stl-docs/components/Head.astro +20 -0
  73. package/stl-docs/components/Header.astro +7 -9
  74. package/stl-docs/components/PageFrame.astro +18 -0
  75. package/stl-docs/components/PageTitle.astro +82 -0
  76. package/stl-docs/components/TableOfContents.astro +34 -0
  77. package/stl-docs/components/ThemeProvider.astro +36 -0
  78. package/stl-docs/components/ThemeSelect.astro +84 -144
  79. package/stl-docs/components/content-panel/ContentPanel.astro +16 -46
  80. package/stl-docs/components/headers/DefaultHeader.astro +1 -1
  81. package/stl-docs/components/headers/HeaderLinks.astro +1 -1
  82. package/stl-docs/components/headers/SplashMobileMenuToggle.astro +17 -1
  83. package/stl-docs/components/headers/StackedHeader.astro +29 -24
  84. package/stl-docs/components/icons/chat-gpt.tsx +17 -0
  85. package/stl-docs/components/icons/claude.tsx +10 -0
  86. package/stl-docs/components/icons/cursor.tsx +10 -0
  87. package/stl-docs/components/icons/gemini.tsx +19 -0
  88. package/stl-docs/components/icons/markdown.tsx +10 -0
  89. package/stl-docs/components/index.ts +1 -0
  90. package/stl-docs/components/mintlify-compat/Accordion.astro +7 -38
  91. package/stl-docs/components/mintlify-compat/AccordionGroup.astro +9 -23
  92. package/stl-docs/components/mintlify-compat/Columns.astro +40 -42
  93. package/stl-docs/components/mintlify-compat/Frame.astro +16 -18
  94. package/stl-docs/components/mintlify-compat/callouts/Callout.astro +10 -3
  95. package/stl-docs/components/mintlify-compat/callouts/Check.astro +7 -3
  96. package/stl-docs/components/mintlify-compat/callouts/Danger.astro +7 -3
  97. package/stl-docs/components/mintlify-compat/callouts/Info.astro +7 -3
  98. package/stl-docs/components/mintlify-compat/callouts/Note.astro +7 -3
  99. package/stl-docs/components/mintlify-compat/callouts/Tip.astro +7 -3
  100. package/stl-docs/components/mintlify-compat/callouts/Warning.astro +7 -3
  101. package/stl-docs/components/mintlify-compat/card.css +33 -35
  102. package/stl-docs/components/mintlify-compat/index.ts +2 -4
  103. package/stl-docs/components/nav-tabs/NavDropdown.astro +31 -75
  104. package/stl-docs/components/nav-tabs/NavTabs.astro +78 -80
  105. package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +15 -8
  106. package/stl-docs/components/nav-tabs/buildNavLinks.ts +4 -3
  107. package/stl-docs/components/pagination/HomeLink.astro +10 -0
  108. package/stl-docs/components/pagination/Pagination.astro +175 -0
  109. package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +22 -0
  110. package/stl-docs/components/pagination/PaginationLinkQuiet.astro +13 -0
  111. package/stl-docs/components/pagination/util.ts +71 -0
  112. package/stl-docs/components/scripts.ts +1 -0
  113. package/stl-docs/components/sidebars/BaseSidebar.astro +87 -0
  114. package/stl-docs/components/sidebars/SDKSelectSidebar.astro +8 -0
  115. package/stl-docs/components/sidebars/SidebarWithComponents.tsx +10 -0
  116. package/stl-docs/components/sidebars/convertAstroSidebarToStl.tsx +62 -0
  117. package/stl-docs/disableCalloutSyntax.ts +36 -0
  118. package/stl-docs/fonts.ts +186 -0
  119. package/stl-docs/index.ts +159 -43
  120. package/stl-docs/loadStlDocsConfig.ts +60 -9
  121. package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +61 -0
  122. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +41 -0
  123. package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
  124. package/stl-docs/proseSearchIndexing.ts +606 -0
  125. package/stl-docs/tabsMiddleware.ts +14 -5
  126. package/styles/code.css +133 -136
  127. package/styles/links.css +11 -48
  128. package/styles/method-descriptions.css +36 -0
  129. package/styles/overrides.css +49 -57
  130. package/styles/page.css +100 -59
  131. package/styles/sdk_select.css +9 -7
  132. package/styles/search.css +57 -69
  133. package/styles/sidebar.css +26 -156
  134. package/styles/{variables.css → sl-variables.css} +3 -2
  135. package/styles/stldocs-variables.css +6 -0
  136. package/styles/toc.css +41 -34
  137. package/theme.css +13 -3
  138. package/tsconfig.json +2 -5
  139. package/virtual-module.d.ts +65 -7
  140. package/components/variables.css +0 -139
  141. package/plugin/cms/client.ts +0 -62
  142. package/plugin/cms/server.ts +0 -268
  143. package/plugin/globalJs/ai-dropdown.ts +0 -57
  144. package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -86
  145. package/stl-docs/components/Sidebar.astro +0 -11
  146. package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -64
  147. package/stl-docs/components/mintlify-compat/Step.astro +0 -58
  148. package/stl-docs/components/mintlify-compat/Steps.astro +0 -17
  149. package/styles/fonts.css +0 -68
  150. /package/{plugin/assets → assets}/fonts/geist/OFL.txt +0 -0
  151. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
  152. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin.woff2 +0 -0
  153. /package/{plugin/assets → assets}/fonts/geist/geist-latin-ext.woff2 +0 -0
  154. /package/{plugin/assets → assets}/fonts/geist/geist-latin.woff2 +0 -0
  155. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
  156. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
  157. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
  158. /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,62 +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,
52
+ ENABLE_CONTEXT_MENU,
53
+ EXPERIMENTAL_COLLAPSIBLE_METHOD_DESCRIPTIONS,
51
54
  } from 'virtual:stl-starlight-virtual-module';
52
- import style from '@stainless-api/docs-ui/src/style';
53
- import { createHighlighter, type BundledLanguage, type BundledTheme, type HighlighterGeneric } from 'shiki';
54
- import { SnippetCode, SnippetContainer, SnippetRequestContainer } from '../components/SnippetCode';
55
- 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';
56
64
  import type { StlStarlightMiddleware } from '../middlewareBuilder/stainlessMiddleware';
57
- import { ComponentProvider } from '@stainless-api/docs-ui/src/contexts/component';
58
- import { APIReferenceAIDropdown } from '../../stl-docs/components/APIReferenceAIDropdown';
59
-
60
- export function generateDocsRoutes(spec: SDKJSON.Spec) {
61
- const paths = generateRouteList({
62
- spec,
63
- excludeLanguages: EXCLUDE_LANGUAGES as DocsLanguage[],
64
- });
65
- const readmes = Object.entries(spec.readme)
66
- .filter(([language]) => language !== 'http')
67
- .filter(([language]) => !EXCLUDE_LANGUAGES.includes(language as DocsLanguage))
68
- .map(([language]) => ({
69
- slug: language,
70
- stainlessPath: null,
71
- language: language as DocsLanguage,
72
- title: 'Readme',
73
- kind: 'readme',
74
- }));
75
-
76
- return [...paths, ...readmes].map(({ slug, stainlessPath, language, title, kind }) => {
77
- return {
78
- params: { slug },
79
- props: { stainlessPath, language, title, kind },
80
- };
81
- });
82
- }
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';
83
69
 
84
70
  function isResourceNonEmpty(resource: SDKJSON.Resource) {
85
71
  return (
@@ -120,7 +106,6 @@ export function buildSidebar(
120
106
 
121
107
  const meths: SidebarEntry[] = Object.values(resource.methods ?? [])
122
108
  .filter((method) => spec.decls?.[language]?.[method.stainlessPath])
123
- .toSorted((first, second) => first.name.localeCompare(second.name))
124
109
  .map((method) => ({
125
110
  type: 'link',
126
111
  isCurrent: current === method.stainlessPath,
@@ -152,78 +137,120 @@ export function buildPageNavigation(resource: SDKJSON.Resource, depth: number =
152
137
  return [...output, ...subs];
153
138
  }
154
139
 
155
- function renderMarkdown(content: string) {
156
- 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;
157
157
  }
158
158
 
159
- async function highlight(content: string, language?: string) {
160
- if (language === 'json') return hljs.highlight(content, { language }).value;
161
- 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
+ }
162
181
  return highlighter.codeToHtml(content, {
163
182
  lang: language ?? 'javascript',
164
- themes: HIGHLIGHT_THEMES || {},
183
+ themes: _themes || {},
165
184
  });
166
185
  }
167
186
 
187
+ async function highlight(content: string, language?: string) {
188
+ const highlighter = await astroHighlight();
189
+ return shikiHighlight({ highlighter, content, language });
190
+ }
191
+
168
192
  export function SDKSelectReactComponent({
169
193
  selected,
170
194
  languages,
171
- id,
172
195
  className,
196
+ ...rest
173
197
  }: {
174
198
  selected: DocsLanguage;
175
199
  languages: DocsLanguage[];
176
- id: string;
177
200
  className?: string;
178
- }) {
201
+ } & Omit<React.ComponentProps<'div'>, 'children'>) {
179
202
  return (
180
- <Dropdown id={id} data-current-value={selected} className={className}>
181
- <DropdownTrigger
182
- className="dropdown-toggle stldocs-button-tertiary"
183
- type="button"
184
- id="stldocs-snippet-title-button"
185
- aria-expanded="false"
186
- withChevron
187
- >
188
- <SDKIcon language={getLanguageSnippet(selected)} size={16} />
189
- <span className={clsx('stl-snippet-dropdown-button-text', selected)}>{selected}</span>
190
- </DropdownTrigger>
191
- <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
192
216
  className="dropdown-menu stl-sdk-select-dropdown-menu"
193
- position="below"
194
- aria-labelledby="stldocs-snippet-title-button"
217
+ aria-labelledby="stl-docs-snippet-title-button"
195
218
  >
196
219
  {languages.map((item) => (
197
- <DropdownItem key={item} value={item} selected={item === selected}>
198
- <div>
220
+ <Dropdown.MenuItem key={item} value={item} isSelected={item === selected}>
221
+ <Dropdown.Icon>
199
222
  <SDKIcon language={getLanguageSnippet(item)} size={16} />
200
- <span className={clsx('stl-snippet-dropdown-button-text', item)}>{item}</span>
201
- </div>
202
- </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>
203
232
  ))}
204
- </DropdownMenu>
233
+ </Dropdown.Menu>
205
234
  </Dropdown>
206
235
  );
207
236
  }
208
237
 
209
238
  function SDKRequestTitle({ snippetLanguage }: SDKRequestTitleProps) {
210
- const spec = useSpec();
211
-
212
239
  const selected = snippetLanguage.split('.').at(0) as DocsLanguage;
213
- const languages = (spec?.docs?.languages ?? ['http']).filter((lang) => !EXCLUDE_LANGUAGES.includes(lang));
240
+ const languages = getDocsLanguages();
214
241
 
215
242
  return (
216
243
  <SDKSelectReactComponent
217
244
  selected={selected || 'http'}
218
245
  languages={languages}
219
- id="stldocs-snippet-select"
220
- className="stl-sdk-select"
246
+ data-stldocs-snippet-select
247
+ className="stl-sdk-select stl-ui-not-prose"
221
248
  />
222
249
  );
223
250
  }
224
251
 
225
252
  export type SpecMetadata = [
226
- 'http' | 'node' | 'python' | 'go' | 'typescript' | 'terraform' | 'ruby' | 'java' | 'kotlin',
253
+ DocsLanguage,
227
254
  {
228
255
  repo_url?: string;
229
256
  code_url?: string;
@@ -233,15 +260,26 @@ export type SpecMetadata = [
233
260
  },
234
261
  ][];
235
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
+
236
273
  export function RenderLibraries({ metadata }: { metadata: SpecMetadata }) {
237
274
  return (
238
- <ComponentProvider components={{}}>
275
+ <ComponentProvider components={componentOverrides}>
239
276
  {metadata.map(([language, data]) => (
240
277
  <SDKLanguageBlock
278
+ key={language}
241
279
  language={language}
242
280
  version={data.version || ''}
243
281
  install={data.install || ''}
244
- links={{ repo: data.repo_url || '#', docs: `${BASE_PATH}/${language}` }}
282
+ links={{ repo: data.repo_url || '#', docs: `${RESOLVED_API_REFERENCE_PATH}/${language}` }}
245
283
  />
246
284
  ))}
247
285
  </ComponentProvider>
@@ -253,15 +291,8 @@ export function RenderSpecOverview({ spec, language }: { spec: SDKJSON.Spec; lan
253
291
 
254
292
  return (
255
293
  <DocsProvider spec={spec} language={language ?? 'node'}>
256
- <ComponentProvider
257
- components={{
258
- SDKRequestTitle,
259
- SnippetCode,
260
- SnippetContainer,
261
- SnippetRequestContainer,
262
- }}
263
- >
264
- <NavigationProvider basePath={BASE_PATH}>
294
+ <ComponentProvider components={componentOverrides}>
295
+ <NavigationProvider basePath={RESOLVED_API_REFERENCE_PATH}>
265
296
  <MarkdownProvider render={renderMarkdown} highlight={highlight}>
266
297
  <div className={style.Overview}>
267
298
  {resources
@@ -302,7 +333,10 @@ export function RenderSpec({
302
333
  const parsed = parseStainlessPath(path);
303
334
  const resource = getResourceFromSpec(path, spec);
304
335
 
305
- 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
+ }
306
340
 
307
341
  return (
308
342
  <DocsProvider
@@ -313,46 +347,30 @@ export function RenderSpec({
313
347
  properties: PROPERTY_SETTINGS,
314
348
  }}
315
349
  >
316
- <ComponentProvider
317
- components={{
318
- SDKRequestTitle,
319
- SnippetCode,
320
- SnippetContainer,
321
- SnippetRequestContainer,
322
- }}
323
- >
324
- <NavigationProvider basePath={BASE_PATH} selectedPath={path}>
350
+ <ComponentProvider components={componentOverrides}>
351
+ <NavigationProvider basePath={RESOLVED_API_REFERENCE_PATH} selectedPath={path}>
325
352
  <MarkdownProvider render={renderMarkdown} highlight={highlight}>
326
- {kind === 'http_method' ? (
353
+ {
327
354
  <div className="stldocs-root stl-ui-not-prose">
328
355
  <div className="stl-page-nav-container">
329
356
  <SDKBreadcrumbs
330
357
  spec={spec as SDKJSON.Spec}
331
358
  currentPath={currentPath}
332
- basePath={BASE_PATH}
359
+ basePath={RESOLVED_API_REFERENCE_PATH}
333
360
  config={BREADCRUMB_CONFIG}
334
361
  />
335
- <APIReferenceAIDropdown />
362
+ {ENABLE_CONTEXT_MENU && <AIDropdown />}
336
363
  </div>
337
- <SDKMethod
338
- method={resource.methods[parsed.method]}
339
- transformRequestSnippet={transformRequestSnippet}
340
- />
341
- </div>
342
- ) : (
343
- <div className="stldocs-root stl-ui-not-prose">
344
- <div className="stl-page-nav-container">
345
- <SDKBreadcrumbs
346
- spec={spec as SDKJSON.Spec}
347
- currentPath={currentPath}
348
- basePath={BASE_PATH}
349
- config={BREADCRUMB_CONFIG}
364
+ {kind === 'http_method' ? (
365
+ <SDKMethod
366
+ method={resource.methods[parsed.method!]!}
367
+ transformRequestSnippet={transformRequestSnippet}
350
368
  />
351
- <APIReferenceAIDropdown />
352
- </div>
353
- <SDKOverview resource={resource} />
369
+ ) : (
370
+ <SDKOverview resource={resource} />
371
+ )}
354
372
  </div>
355
- )}
373
+ }
356
374
  </MarkdownProvider>
357
375
  </NavigationProvider>
358
376
  </ComponentProvider>
@@ -366,10 +384,14 @@ export function RenderMethod({ path }: { path: string }) {
366
384
 
367
385
  const parsed = parseStainlessPath(path);
368
386
  const resource = getResourceFromSpec(path, spec);
369
- if (!resource || !parsed) return null;
370
387
 
371
- const meth = resource.methods[parsed.method];
372
- 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} />;
373
395
  }
374
396
 
375
397
  export async function getReadmeContent(spec: SDKJSON.Spec, language: DocsLanguage) {
@@ -389,8 +411,68 @@ export async function getReadmeContent(spec: SDKJSON.Spec, language: DocsLanguag
389
411
  return spec.readme[language];
390
412
  }
391
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
+
392
474
  // Astro's markdown processor is a singleton
393
- // Need to cache it instead of instanting per request
475
+ // Need to cache it instead of instantiating per request
394
476
  let astroMarkdownProcessor: MarkdownProcessor;
395
477
  async function astroMarkdown() {
396
478
  if (!astroMarkdownProcessor) {
@@ -406,18 +488,6 @@ async function astroMarkdown() {
406
488
  return astroMarkdownProcessor;
407
489
  }
408
490
 
409
- let astroShikiHighlighter: HighlighterGeneric<BundledLanguage, BundledTheme>;
410
- async function astroHighlight() {
411
- if (!astroShikiHighlighter) {
412
- astroShikiHighlighter = await createHighlighter({
413
- themes: ['github-light', 'github-dark'],
414
- langs: SupportedLanguageSyntaxes,
415
- });
416
- }
417
-
418
- return astroShikiHighlighter;
419
- }
420
-
421
491
  export async function astroMarkdownRender(content: string) {
422
492
  const md = await astroMarkdown();
423
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,50 +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
38
+ const slug = path.posix.join(import.meta.env.BASE_URL ?? '', `/${context.locals.starlightRoute.id}`); // same as .slug but not deprecated
15
39
 
16
- const slug = `/${context.locals.starlightRoute.id}`; // same as .slug but not deprecated
40
+ context.locals.starlightRoute._stlStarlight = {
41
+ basePath: RESOLVED_API_REFERENCE_PATH,
42
+ };
17
43
 
18
44
  const apiReferencePlaceholderItems = getAPIReferencePlaceholderItems(context.locals.starlightRoute.sidebar);
19
45
 
20
- const spec = await cmsClient.getSpec();
21
-
22
- const { language, stainlessPath } = parseRoute(BASE_PATH, slug);
23
-
24
- // This is probably temporary, but it fills in functionality needed for Mintlify imports
25
- recursiveReplacePlaceholderItems(context.locals.starlightRoute.sidebar, (entry, { endpoint, label }) => {
26
- const method = getMethodFromSDKJSON(spec, endpoint);
27
-
28
- const route = forceGenerateRoute({
29
- basePath: BASE_PATH,
30
- stainlessPath: method.stainlessPath,
31
- language,
32
- });
33
- entry.href = route;
34
- entry.isCurrent = method.stainlessPath === stainlessPath;
35
- if (!label) {
36
- entry.label = method.summary ?? method.name;
37
- }
38
- entry.attrs['data-stldocs-method'] = method.httpMethod;
39
- });
46
+ const { language } = parseRoute(RESOLVED_API_REFERENCE_PATH, slug);
40
47
 
41
48
  for (const item of apiReferencePlaceholderItems) {
42
- const entries = await cmsClient.buildSidebar({
43
- basePath: BASE_PATH,
44
- currentSlug: slug,
45
- sidebarId: item.sidebarId,
46
- });
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
+ }
47
53
 
48
54
  item.group.splice(item.index, 1, ...entries);
49
55
  }
56
+
57
+ context.locals.starlightRoute.sidebar = markCurrentItems(context.locals.starlightRoute.sidebar, slug);
50
58
  });