@stainless-api/docs 0.1.0-beta.14 → 0.1.0-beta.16

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 (35) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +11 -18
  3. package/plugin/buildAlgoliaIndex.ts +28 -3
  4. package/plugin/cms/server.ts +95 -52
  5. package/plugin/cms/sidebar-builder.ts +0 -9
  6. package/plugin/globalJs/ai-dropdown-options.ts +161 -0
  7. package/plugin/globalJs/navigation.ts +0 -22
  8. package/plugin/index.ts +46 -35
  9. package/plugin/loadPluginConfig.ts +90 -21
  10. package/plugin/react/Routing.tsx +18 -28
  11. package/plugin/routes/Docs.astro +7 -16
  12. package/plugin/routes/Overview.astro +7 -5
  13. package/plugin/vendor/preview.worker.docs.js +6357 -6132
  14. package/resolveSrcFile.ts +10 -0
  15. package/shared/getSharedLogger.ts +15 -0
  16. package/shared/terminalUtils.ts +3 -0
  17. package/stl-docs/components/AIDropdown.tsx +52 -0
  18. package/stl-docs/components/Head.astro +9 -0
  19. package/stl-docs/components/PageTitle.astro +67 -0
  20. package/stl-docs/components/content-panel/ContentPanel.astro +8 -35
  21. package/stl-docs/components/icons/chat-gpt.tsx +1 -1
  22. package/stl-docs/components/index.ts +2 -0
  23. package/stl-docs/disableCalloutSyntax.ts +36 -0
  24. package/stl-docs/index.ts +61 -18
  25. package/stl-docs/loadStlDocsConfig.ts +17 -2
  26. package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +64 -0
  27. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +33 -0
  28. package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
  29. package/styles/code.css +0 -5
  30. package/styles/page.css +31 -4
  31. package/virtual-module.d.ts +3 -2
  32. package/plugin/globalJs/ai-dropdown.ts +0 -57
  33. package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -58
  34. package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -55
  35. /package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +0 -0
package/plugin/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import react from '@astrojs/react';
2
2
  import type { StarlightPlugin } from '@astrojs/starlight/types';
3
- import type { AstroIntegration } from 'astro';
3
+ import type { AstroIntegration, AstroIntegrationLogger } from 'astro';
4
4
  import { config } from 'dotenv';
5
5
  import getPort from 'get-port';
6
6
  import { startDevServer } from './cms/server';
@@ -23,11 +23,15 @@ import {
23
23
  import { buildVirtualModuleString } from '../shared/virtualModule';
24
24
  import path from 'path';
25
25
  import fs from 'fs';
26
+ import { getSharedLogger } from '../shared/getSharedLogger';
27
+ import { resolveSrcFile } from '../resolveSrcFile';
26
28
 
27
29
  export { generateAPILink } from './generateAPIReferenceLink';
28
30
  export type { ReferenceSidebarConfigItem };
29
31
 
30
- config();
32
+ config({
33
+ quiet: true,
34
+ });
31
35
 
32
36
  let sidebarIdCounter = 0;
33
37
 
@@ -108,6 +112,7 @@ function tmpGetCMSServerConfig(specRetrieverConfig: SpecRetrieverConfig) {
108
112
 
109
113
  async function stlStarlightAstroIntegration(
110
114
  pluginConfig: NormalizedStainlessStarlightConfig,
115
+ stlStarlightPluginLogger: AstroIntegrationLogger,
111
116
  ): Promise<AstroIntegration> {
112
117
  const virtualId = `virtual:stl-starlight-virtual-module`;
113
118
  // The '\0' prefix tells Vite “this is a virtual module” and prevents it from being resolved again.
@@ -119,9 +124,10 @@ async function stlStarlightAstroIntegration(
119
124
 
120
125
  const cmsServer = await startDevServer({
121
126
  port: CMS_PORT,
122
- apiKey,
127
+ apiKey: apiKey.value,
123
128
  version,
124
129
  devPaths,
130
+ logger: stlStarlightPluginLogger,
125
131
  getGeneratedSidebarConfig: (id: number) => {
126
132
  const config = sidebarConfigs.get(id);
127
133
  if (!config) {
@@ -134,55 +140,43 @@ async function stlStarlightAstroIntegration(
134
140
  return {
135
141
  name: 'stl-starlight-astro',
136
142
  hooks: {
137
- 'astro:config:setup': async ({ injectRoute, updateConfig, logger, command, config: astroConfig }) => {
143
+ 'astro:config:setup': async ({
144
+ injectRoute,
145
+ updateConfig,
146
+ logger: localLogger,
147
+ command,
148
+ config: astroConfig,
149
+ }) => {
150
+ const logger = getSharedLogger({ fallback: localLogger });
138
151
  const projectDir = astroConfig.root.pathname;
139
152
 
140
153
  const middlewareFile = path.join(projectDir, 'middleware.stainless.ts');
141
154
 
142
155
  let vmMiddlewareExport = 'export const MIDDLEWARE = {};';
143
156
  if (fs.existsSync(middlewareFile)) {
144
- logger.info(`Loading middleware from ${middlewareFile}`);
157
+ logger.debug(`Loading middleware from ${middlewareFile}`);
145
158
  vmMiddlewareExport = `export { default as MIDDLEWARE } from '${middlewareFile}';`;
146
159
  }
147
160
 
148
161
  injectRoute({
149
- pattern: `${pluginConfig.basePath}/[...slug].md`,
150
- entrypoint: '@stainless-api/docs/MarkdownRoute',
162
+ pattern: `${pluginConfig.basePath}/[...slug]/index.md`,
163
+ entrypoint: resolveSrcFile('/plugin/routes/markdown.ts'),
151
164
  prerender: command === 'build',
152
165
  });
153
166
 
154
- const astroFile = command === 'build' ? 'DocsStaticRoute' : 'DocsRoute';
167
+ const astroFile = command === 'build' ? 'DocsStatic' : 'Docs';
155
168
  injectRoute({
156
169
  pattern: `${pluginConfig.basePath}/[...slug]`,
157
- // in prod I think this points to @stainless-starlight/components/docs.astro
158
- entrypoint: `@stainless-api/docs/${astroFile}`,
170
+ entrypoint: resolveSrcFile(`/plugin/routes/${astroFile}.astro`),
159
171
  prerender: command === 'build',
160
172
  });
161
173
 
162
174
  injectRoute({
163
175
  pattern: pluginConfig.basePath,
164
- entrypoint: '@stainless-api/docs/OverviewRoute',
176
+ entrypoint: resolveSrcFile('/plugin/routes/Overview.astro'),
165
177
  prerender: command === 'build',
166
178
  });
167
179
 
168
- // Remove starlight callout syntax in favor of our own callout component
169
- // updateConfig always deeply merges arrays so we need to mutate remarkPlugins directly
170
- // in order to remove plugins that starlight added
171
- astroConfig.markdown.remarkPlugins = astroConfig.markdown.remarkPlugins.filter((plugin, i, arr) => {
172
- if (typeof plugin !== 'function') return true;
173
- // remove:
174
- // 1. remarkDirective plugin
175
- if (plugin.name === 'remarkDirective') return false;
176
- // 2. directly followed by remarkAsides plugin (inner function named 'attacher')
177
- if (plugin.name === 'attacher') {
178
- const prev = arr[i - 1];
179
- if (typeof prev === 'function' && prev.name === 'remarkDirective') return false;
180
- }
181
- // 3. remarkDirectivesRestoration plugin
182
- if (plugin.name === 'remarkDirectivesRestoration') return false;
183
- return true;
184
- });
185
-
186
180
  updateConfig({
187
181
  vite: {
188
182
  ssr: {
@@ -231,7 +225,6 @@ async function stlStarlightAstroIntegration(
231
225
  EXPERIMENTAL_COLLAPSIBLE_SNIPPETS: pluginConfig.experimentalCollapsibleSnippets,
232
226
  PROPERTY_SETTINGS: pluginConfig.propertySettings,
233
227
  SEARCH: pluginConfig.search,
234
- INCLUDE_AI_DROPDOWN_OPTIONS: pluginConfig.includeAIDropdownOptions,
235
228
  }),
236
229
  vmMiddlewareExport,
237
230
  ].join('\n');
@@ -260,15 +253,20 @@ export function stainlessStarlight(someUserConfig: SomeStainlessStarlightUserCon
260
253
  command,
261
254
  config: starlightConfig,
262
255
  astroConfig,
263
- logger,
256
+ logger: localLogger,
264
257
  }) => {
265
258
  if (command !== 'build' && command !== 'dev') {
266
259
  return;
267
260
  }
268
261
 
262
+ const logger = getSharedLogger({ fallback: localLogger });
263
+
269
264
  const configParseResult = parseStarlightPluginConfig(someUserConfig, command);
270
265
  if (configParseResult.result === 'error') {
271
- logger.error(configParseResult.message);
266
+ const errorLines = configParseResult.message.split('\n');
267
+ for (const line of errorLines) {
268
+ logger.error(line);
269
+ }
272
270
  process.exit(1);
273
271
  }
274
272
  const config = configParseResult.config;
@@ -279,17 +277,30 @@ export function stainlessStarlight(someUserConfig: SomeStainlessStarlightUserCon
279
277
  addIntegration(react());
280
278
  }
281
279
 
280
+ if ('apiKey' in config.specRetrieverConfig) {
281
+ if (!config.specRetrieverConfig.apiKey) {
282
+ logger.info(`Stainless credentials not loaded`);
283
+ } else if (config.specRetrieverConfig.apiKey.source === 'explicit-config') {
284
+ logger.info(`Stainless credentials loaded from user config`);
285
+ } else if (config.specRetrieverConfig.apiKey.source === 'environment-variable') {
286
+ logger.info('Stainless credentials loaded from `STAINLESS_API_KEY` environment variable');
287
+ } else if (config.specRetrieverConfig.apiKey.source === 'cli') {
288
+ logger.info('Stainless credentials loaded from `stl` CLI');
289
+ }
290
+ }
291
+
282
292
  if (
283
293
  command === 'build' &&
284
294
  config.specRetrieverConfig.kind === 'local_spec_server_with_remote_files'
285
295
  ) {
286
296
  await buildAlgoliaIndex({
287
297
  version: config.specRetrieverConfig.version,
288
- apiKey: config.specRetrieverConfig.apiKey,
298
+ apiKey: config.specRetrieverConfig.apiKey.value,
299
+ logger,
289
300
  });
290
301
  }
291
302
 
292
- addIntegration(await stlStarlightAstroIntegration(config));
303
+ addIntegration(await stlStarlightAstroIntegration(config, logger));
293
304
 
294
305
  if (starlightConfig.sidebar) {
295
306
  // for pagination (https://starlight.astro.build/reference/configuration/#pagination) to work correctly
@@ -314,7 +325,7 @@ export function stainlessStarlight(someUserConfig: SomeStainlessStarlightUserCon
314
325
  });
315
326
 
316
327
  addRouteMiddleware({
317
- entrypoint: '@stainless-api/docs/replaceSidebarPlaceholderMiddleware',
328
+ entrypoint: resolveSrcFile('/plugin/replaceSidebarPlaceholderMiddleware.ts'),
318
329
  order: 'post',
319
330
  });
320
331
  },
@@ -1,9 +1,12 @@
1
1
  import path from 'path';
2
+ import { homedir } from 'os';
3
+ import { existsSync, readFileSync } from 'fs';
2
4
 
3
5
  import type { CreateShikiHighlighterOptions } from '@astrojs/markdown-remark';
4
6
  import type { DocsLanguage } from '@stainless-api/docs-ui/src/routing';
5
7
  import type { PropertySettingsType } from '@stainless-api/docs-ui/src/contexts';
6
8
  import type { InputFilePaths } from '../plugin/cms/server';
9
+ import { bold } from '../shared/terminalUtils';
7
10
 
8
11
  export type AstroCommand = 'dev' | 'build' | 'preview' | 'sync';
9
12
 
@@ -18,7 +21,7 @@ export type VersionUserConfig = {
18
21
  type BreadcrumbUserConfig = {
19
22
  /**
20
23
  * Include the current page in the breadcrumb list.
21
- * Defaults to `false`.
24
+ * Default: `false`
22
25
  */
23
26
  includeCurrentPage?: boolean;
24
27
  };
@@ -26,7 +29,12 @@ type BreadcrumbUserConfig = {
26
29
  export type StainlessStarlightUserConfig = {
27
30
  /**
28
31
  * Optional api key for Stainless API.
29
- * If not provided, it will look for the STAINLESS_API_KEY environment variable.
32
+ * If not provided, we will handle Stainless auth via the `stl` CLI or look for the STAINLESS_API_KEY environment variable.
33
+ * Precedence:
34
+ * 1. Explicity `apiKey` option provided
35
+ * 2. `STAINLESS_API_KEY` environment variable
36
+ * 3. Login status from the `stl` CLI
37
+ * 4. Error (no auth found)
30
38
  */
31
39
  apiKey?: string;
32
40
 
@@ -42,7 +50,7 @@ export type StainlessStarlightUserConfig = {
42
50
 
43
51
  /**
44
52
  * Optional mount point for API reference docs.
45
- * Defaults to `/api`.
53
+ * Default: `/api`
46
54
  * Example: `/my-api` → docs available at `/my-api/…`.
47
55
  */
48
56
  basePath?: string;
@@ -55,7 +63,7 @@ export type StainlessStarlightUserConfig = {
55
63
 
56
64
  /**
57
65
  * Optional language to treat as the default when the user hasn't selected one.
58
- * Defaults to `"http"` when none is provided.
66
+ * Default: `"http"`
59
67
  * Example: `"python"`
60
68
  */
61
69
  defaultLanguage?: DocsLanguage;
@@ -90,7 +98,7 @@ export type StainlessStarlightUserConfig = {
90
98
  contentPanel?: {
91
99
  /**
92
100
  * Optional layout for the content panel.
93
- * Defaults to `"double-pane"`.
101
+ * Default: `"double-pane"`
94
102
  */
95
103
  layout?: ContentLayout;
96
104
  };
@@ -107,6 +115,8 @@ export type StainlessStarlightUserConfig = {
107
115
  /**
108
116
  * When set to `true`, the enableAISearch` setting turns on support for
109
117
  * LLM-based conversations with the API documentation
118
+ *
119
+ * Default: `false`
110
120
  */
111
121
  enableAISearch?: boolean;
112
122
  };
@@ -114,16 +124,10 @@ export type StainlessStarlightUserConfig = {
114
124
  /**
115
125
  * Enable experimental collapsible code snippets. Snippets will be collapsed by default for
116
126
  * single-pane and mobile layouts.
117
- * Defaults to `false`.
127
+ *
128
+ * Default: `false`
118
129
  */
119
130
  experimentalCollapsibleSnippets?: boolean;
120
-
121
- /**
122
- * When set to `true`, a dropdown button will be included on all pages to allow users to
123
- * view or send markdown to an LLM.
124
- * Defaults to `false`.
125
- */
126
- includeAIDropdownOptions?: boolean;
127
131
  };
128
132
 
129
133
  export type ExternalSpecServerUserConfig = Omit<StainlessStarlightUserConfig, 'stainlessProject'> & {
@@ -140,15 +144,29 @@ function getLocalFilePaths(command: AstroCommand): InputFilePaths | null {
140
144
  if (command !== 'dev') {
141
145
  return null;
142
146
  }
143
- if (!process.env.OPENAPI_PATH || !process.env.STAINLESS_SPEC_PATH) {
147
+
148
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
149
+ const oasPath = process.env.OPENAPI_PATH;
150
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
151
+ const configPath = process.env.STAINLESS_CONFIG_PATH;
152
+
153
+ if (!oasPath || !configPath) {
144
154
  return null;
145
155
  }
156
+
146
157
  return {
147
- oasPath: resolvePath(process.env.OPENAPI_PATH),
148
- configPath: resolvePath(process.env.STAINLESS_SPEC_PATH),
158
+ oasPath: resolvePath(oasPath),
159
+ configPath: resolvePath(configPath),
149
160
  };
150
161
  }
151
162
 
163
+ export type ApiKeySource = 'explicit-config' | 'environment-variable' | 'cli';
164
+
165
+ export type LoadedApiKey = {
166
+ value: string;
167
+ source: ApiKeySource;
168
+ };
169
+
152
170
  export type SpecRetrieverConfig =
153
171
  | {
154
172
  kind: 'external_spec_server';
@@ -159,16 +177,63 @@ export type SpecRetrieverConfig =
159
177
  kind: 'local_spec_server_with_files';
160
178
  stainlessProject: string;
161
179
  devPaths: InputFilePaths;
162
- apiKey: string | null;
180
+ apiKey: LoadedApiKey | null;
163
181
  version: VersionUserConfig;
164
182
  }
165
183
  | {
166
184
  kind: 'local_spec_server_with_remote_files';
167
185
  stainlessProject: string;
168
- apiKey: string;
186
+ apiKey: LoadedApiKey;
169
187
  version: VersionUserConfig;
170
188
  };
171
189
 
190
+ function parseAuthJson(authJsonStr: string) {
191
+ let json: unknown;
192
+ try {
193
+ json = JSON.parse(authJsonStr);
194
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
195
+ } catch (_error) {
196
+ return null;
197
+ }
198
+
199
+ if (typeof json !== 'object' || json === null) {
200
+ return null;
201
+ }
202
+ if (!('access_token' in json)) {
203
+ return null;
204
+ }
205
+ const accessToken = json['access_token'];
206
+ if (typeof accessToken !== 'string') {
207
+ return null;
208
+ }
209
+ return accessToken;
210
+ }
211
+
212
+ function loadApiKey(configValue: string | undefined): LoadedApiKey | null {
213
+ if (typeof configValue === 'string') {
214
+ return { value: configValue, source: 'explicit-config' };
215
+ }
216
+ if (process.env.STAINLESS_API_KEY) {
217
+ return { value: process.env.STAINLESS_API_KEY, source: 'environment-variable' };
218
+ }
219
+
220
+ const homeDirPath = homedir();
221
+
222
+ const authJsonPath = path.join(homeDirPath, '.config', 'stainless', 'auth.json');
223
+
224
+ if (!existsSync(authJsonPath)) {
225
+ return null;
226
+ }
227
+
228
+ const authJsonStr = readFileSync(authJsonPath, 'utf-8');
229
+ const accessToken = parseAuthJson(authJsonStr);
230
+ if (!accessToken) {
231
+ return null;
232
+ }
233
+
234
+ return { value: accessToken, source: 'cli' };
235
+ }
236
+
172
237
  function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: AstroCommand) {
173
238
  const configWithDefaults = {
174
239
  basePath: partial.basePath ?? '/api',
@@ -197,7 +262,6 @@ function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: Ast
197
262
  search: {
198
263
  enableAISearch: partial.search?.enableAISearch ?? false,
199
264
  },
200
- includeAIDropdownOptions: partial.includeAIDropdownOptions ?? false,
201
265
  };
202
266
 
203
267
  function getSpecRetrieverConfig(): SpecRetrieverConfig {
@@ -213,7 +277,7 @@ function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: Ast
213
277
  throw new Error('You must provide a stainlessProject when using Stainless Starlight');
214
278
  }
215
279
 
216
- const apiKey = partial.apiKey ?? process.env.STAINLESS_API_KEY ?? null;
280
+ const apiKey = loadApiKey(partial.apiKey);
217
281
 
218
282
  const version = {
219
283
  stainlessProject: partial.stainlessProject,
@@ -234,7 +298,12 @@ function normalizeConfig(partial: SomeStainlessStarlightUserConfig, command: Ast
234
298
 
235
299
  if (!apiKey) {
236
300
  throw new Error(
237
- 'Please provide a Stainless API key via the STAINLESS_API_KEY environment variable or the apiKey option in the Stainless Starlight config.',
301
+ [
302
+ bold('No Stainless credentials found. Please choose one of the following options to authenticate with Stainless:'),
303
+ '- Run `stl auth login` to authenticate via the Stainless CLI',
304
+ '- Provide a Stainless API key via the `STAINLESS_API_KEY` environment variable (eg. in a .env file)',
305
+ '- Set the `apiKey` option in the Stainless Docs config',
306
+ ].join('\n'),
238
307
  );
239
308
  }
240
309
 
@@ -7,7 +7,7 @@ import remarkGfmAlerts from 'remark-github-alerts';
7
7
  import type { MarkdownHeading } from 'astro';
8
8
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
9
9
  import type * as SDKJSON from '~/lib/json-spec-v2/types';
10
- import type { DocsLanguage } from '@stainless-api/docs-ui/src/routing';
10
+ import { LanguageNames, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
11
11
 
12
12
  import {
13
13
  generateRouteList,
@@ -48,15 +48,15 @@ import {
48
48
  HIGHLIGHT_THEMES,
49
49
  BREADCRUMB_CONFIG,
50
50
  PROPERTY_SETTINGS,
51
- INCLUDE_AI_DROPDOWN_OPTIONS,
52
51
  } from 'virtual:stl-starlight-virtual-module';
52
+ // TODO (eventually): this file should really probably only be importing from the starlight virtual module
53
+ import { ENABLE_CONTEXT_MENU } from 'virtual:stl-docs-virtual-module';
53
54
  import style from '@stainless-api/docs-ui/src/style';
54
55
  import { createHighlighter, type BundledLanguage, type BundledTheme, type HighlighterGeneric } from 'shiki';
55
56
  import { SnippetCode, SnippetContainer, SnippetRequestContainer } from '../components/SnippetCode';
56
- import clsx from 'clsx';
57
57
  import type { StlStarlightMiddleware } from '../middlewareBuilder/stainlessMiddleware';
58
58
  import { ComponentProvider } from '@stainless-api/docs-ui/src/contexts/component';
59
- import { APIReferenceAIDropdown } from '../../stl-docs/components/APIReferenceAIDropdown';
59
+ import { AIDropdown } from '../../stl-docs/components/AIDropdown';
60
60
 
61
61
  export function generateDocsRoutes(spec: SDKJSON.Spec) {
62
62
  const paths = generateRouteList({
@@ -121,7 +121,6 @@ export function buildSidebar(
121
121
 
122
122
  const meths: SidebarEntry[] = Object.values(resource.methods ?? [])
123
123
  .filter((method) => spec.decls?.[language]?.[method.stainlessPath])
124
- .toSorted((first, second) => first.name.localeCompare(second.name))
125
124
  .map((method) => ({
126
125
  type: 'link',
127
126
  isCurrent: current === method.stainlessPath,
@@ -182,23 +181,23 @@ export function SDKSelectReactComponent({
182
181
  <DropdownTrigger
183
182
  className="dropdown-toggle"
184
183
  type="button"
185
- id="stldocs-snippet-title-button"
184
+ id="stl-docs-snippet-title-button"
186
185
  aria-expanded="false"
187
186
  withChevron
188
187
  >
189
188
  <SDKIcon language={getLanguageSnippet(selected)} size={16} />
190
- <span className={clsx('stl-snippet-dropdown-button-text', selected)}>{selected}</span>
189
+ <span className="stl-snippet-dropdown-button-text">{LanguageNames[selected]}</span>
191
190
  </DropdownTrigger>
192
191
  <DropdownMenu
193
192
  className="dropdown-menu stl-sdk-select-dropdown-menu"
194
193
  position="below"
195
- aria-labelledby="stldocs-snippet-title-button"
194
+ aria-labelledby="stl-docs-snippet-title-button"
196
195
  >
197
196
  {languages.map((item) => (
198
197
  <DropdownItem key={item} value={item} selected={item === selected}>
199
198
  <div>
200
199
  <SDKIcon language={getLanguageSnippet(item)} size={16} />
201
- <span className={clsx('stl-snippet-dropdown-button-text', item)}>{item}</span>
200
+ <span className="stl-snippet-dropdown-button-text">{LanguageNames[item]}</span>
202
201
  </div>
203
202
  </DropdownItem>
204
203
  ))}
@@ -324,7 +323,7 @@ export function RenderSpec({
324
323
  >
325
324
  <NavigationProvider basePath={BASE_PATH} selectedPath={path}>
326
325
  <MarkdownProvider render={renderMarkdown} highlight={highlight}>
327
- {kind === 'http_method' ? (
326
+ {
328
327
  <div className="stldocs-root stl-ui-not-prose">
329
328
  <div className="stl-page-nav-container">
330
329
  <SDKBreadcrumbs
@@ -333,27 +332,18 @@ export function RenderSpec({
333
332
  basePath={BASE_PATH}
334
333
  config={BREADCRUMB_CONFIG}
335
334
  />
336
- {INCLUDE_AI_DROPDOWN_OPTIONS && <APIReferenceAIDropdown />}
335
+ {ENABLE_CONTEXT_MENU && <AIDropdown />}
337
336
  </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}
337
+ {kind === 'http_method' ? (
338
+ <SDKMethod
339
+ method={resource.methods[parsed.method]}
340
+ transformRequestSnippet={transformRequestSnippet}
351
341
  />
352
- {INCLUDE_AI_DROPDOWN_OPTIONS && <APIReferenceAIDropdown />}
353
- </div>
354
- <SDKOverview resource={resource} />
342
+ ) : (
343
+ <SDKOverview resource={resource} />
344
+ )}
355
345
  </div>
356
- )}
346
+ }
357
347
  </MarkdownProvider>
358
348
  </NavigationProvider>
359
349
  </ComponentProvider>
@@ -21,6 +21,12 @@ if (!route) {
21
21
  throw new Error("No such route");
22
22
  }
23
23
 
24
+ // PageTitle override will skip rendering the default Starlight title
25
+ // @ts-expect-error - _stlStarlightPage isn't typed yet
26
+ Astro.locals._stlStarlightPage = {
27
+ skipRenderingStarlightTitle: route.props.kind === 'readme' ? false : true,
28
+ }
29
+
24
30
  const readmeContent = await getReadmeContent(spec, route.props.language);
25
31
  const readme =
26
32
  route.props.kind === "readme"
@@ -69,11 +75,6 @@ if (readme) {
69
75
  tableOfContents: ["resource", "readme"].includes(props.kind)
70
76
  ? { maxHeadingLevel: 6 }
71
77
  : false,
72
- // @ts-ignore
73
- stainlessStarlight: {
74
- basePath: BASE_PATH,
75
- ...props,
76
- },
77
78
  }}
78
79
  >
79
80
  {
@@ -107,17 +108,7 @@ if (readme) {
107
108
  [data-has-sidebar]:not([data-has-toc]) .sl-container {
108
109
  max-width: 1428px;
109
110
  }
110
-
111
- .content-panel:nth-of-type(1) {
112
- display: none;
113
- }
114
- .content-panel:nth-of-type(2) .stl-page-nav-container {
115
- display: none;
116
- }
117
-
118
- .content-panel:nth-of-type(2) .stldocs-root .stl-page-nav-container {
119
- display: flex;
120
- }
111
+
121
112
  </style>
122
113
  </div>
123
114
  ) : (
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
3
- import { BASE_PATH, EXCLUDE_LANGUAGES } from 'virtual:stl-starlight-virtual-module';
3
+ import { EXCLUDE_LANGUAGES } from 'virtual:stl-starlight-virtual-module';
4
4
  import { cmsClient } from '../cms/client';
5
5
  import type { DocsLanguage } from '@stainless-api/docs-ui/src/routing';
6
6
  import { RenderLibraries, RenderSpecOverview, type SpecMetadata } from '../react/Routing';
@@ -12,6 +12,12 @@ const metadata = languages
12
12
  .filter((language) => !['http', 'terraform'].includes(language) && spec.metadata[language])
13
13
  .filter((language) => !EXCLUDE_LANGUAGES.includes(language))
14
14
  .map((language) => [language, spec.metadata[language]]) as SpecMetadata;
15
+
16
+ // PageTitle override will skip rendering the default Starlight title
17
+ // @ts-expect-error - _stlStarlightPage isn't typed yet
18
+ Astro.locals._stlStarlightPage = {
19
+ hasMarkdownRoute: false,
20
+ };
15
21
  ---
16
22
 
17
23
  <StarlightPage
@@ -19,10 +25,6 @@ const metadata = languages
19
25
  title: 'API Reference',
20
26
  pagefind: false,
21
27
  tableOfContents: false,
22
- stainlessStarlight: {
23
- basePath: BASE_PATH,
24
- language: 'http',
25
- },
26
28
  }}
27
29
  >
28
30
  <h3>Libraries</h3>