@stainless-api/docs 0.1.0-beta.135 → 0.1.0-beta.137

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @stainless-api/docs
2
2
 
3
+ ## 0.1.0-beta.137
4
+
5
+ ### Minor Changes
6
+
7
+ - ce2dd81: Add interface for customizing ai-chat behaviour
8
+
9
+ ### Patch Changes
10
+
11
+ - 5f0fdda: Vendor dependencies for Stainless Config change
12
+ - 680889d: feat: support disabling third-party context menu options
13
+
14
+ ## 0.1.0-beta.136
15
+
16
+ ### Minor Changes
17
+
18
+ - 5a6e598: [og image] migrate Takumi to v1
19
+
20
+ ### Patch Changes
21
+
22
+ - 8b7aaff: Adds markdown representation for overview
23
+ - 23bd8f7: Update preview worker
24
+
3
25
  ## 0.1.0-beta.135
4
26
 
5
27
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stainless-api/docs",
3
- "version": "0.1.0-beta.135",
3
+ "version": "0.1.0-beta.137",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -28,7 +28,8 @@
28
28
  "./components/Pagination": "./stl-docs/components/pagination/Pagination.astro",
29
29
  "./og-image/components/OpenGraphImage": "./stl-docs/og-image/components/OpenGraphImage.tsx",
30
30
  "./og-image/components/OpenGraphFunctionSignature": "./stl-docs/og-image/components/OpenGraphFunctionSignature.tsx",
31
- "./schema-extension": "./stl-docs/schema-extension.ts"
31
+ "./schema-extension": "./stl-docs/schema-extension.ts",
32
+ "./chat": "./stl-docs/chat/docs-chat-handler.ts"
32
33
  },
33
34
  "keywords": [],
34
35
  "author": "",
@@ -38,27 +39,26 @@
38
39
  },
39
40
  "peerDependencies": {
40
41
  "@astrojs/starlight": ">=0.38.0",
41
- "@takumi-rs/image-response": ">=0.66.6",
42
+ "takumi-js": ">=1.0.5",
42
43
  "astro": ">=6.0.0",
43
44
  "react": ">=19.0.0",
44
45
  "react-dom": ">=19.0.0",
45
46
  "vite": ">=7.3.1"
46
47
  },
47
48
  "peerDependenciesMeta": {
48
- "@takumi-rs/image-response": {
49
+ "takumi-js": {
49
50
  "optional": true
50
51
  }
51
52
  },
52
53
  "dependencies": {
53
- "@astrojs/markdown-remark": "^7.1.0",
54
- "@astrojs/react": "^5.0.3",
54
+ "@astrojs/markdown-remark": "^7.1.1",
55
+ "@astrojs/react": "^5.0.4",
55
56
  "@markdoc/markdoc": "^0.5.7",
56
57
  "@stainless-api/sdk": "0.5.0",
57
- "@streamparser/json-whatwg": "^0.0.22",
58
58
  "cheerio": "^1.2.0",
59
59
  "clsx": "^2.1.1",
60
- "dotenv": "17.4.1",
61
- "lucide-react": "^0.577.0",
60
+ "dotenv": "17.4.2",
61
+ "lucide-react": "^1.14.0",
62
62
  "motion": "^12.38.0",
63
63
  "node-html-parser": "^7.1.0",
64
64
  "rehype-parse": "^9.0.1",
@@ -72,24 +72,24 @@
72
72
  "shiki": "^4.0.2",
73
73
  "unified": "^11.0.5",
74
74
  "web-worker": "^1.5.0",
75
- "@stainless-api/docs-search": "0.1.0-beta.48",
76
75
  "@stainless-api/docs-ui": "0.1.0-beta.95",
77
- "@stainless-api/ui-primitives": "0.1.0-beta.53"
76
+ "@stainless-api/ui-primitives": "0.1.0-beta.53",
77
+ "@stainless-api/docs-search": "0.1.0-beta.48"
78
78
  },
79
79
  "devDependencies": {
80
- "@astrojs/check": "^0.9.8",
81
- "@takumi-rs/image-response": "^0.73.1",
80
+ "@astrojs/check": "^0.9.9",
81
+ "takumi-js": "^1.1.2",
82
82
  "@types/node": "24.12.2",
83
83
  "@types/react": "19.2.14",
84
84
  "@types/react-dom": "^19.2.3",
85
85
  "@types/react-syntax-highlighter": "^15.5.13",
86
- "astro": "^6.1.5",
86
+ "astro": "^6.2.1",
87
87
  "react": "^19.2.5",
88
88
  "react-dom": "^19.2.5",
89
- "typescript": "6.0.2",
89
+ "typescript": "6.0.3",
90
90
  "vite": "^7.3.2",
91
- "vitest": "^4.1.4",
92
- "zod": "^4.3.6",
91
+ "vitest": "^4.1.5",
92
+ "zod": "^4.4.2",
93
93
  "@stainless/eslint-config": "0.1.0-beta.2",
94
94
  "@stainless/sdk-json": "^0.1.0-beta.10"
95
95
  },
@@ -1,5 +1,6 @@
1
1
  import { initDropdownButton } from '@stainless-api/ui-primitives/scripts';
2
2
  import { getPageLoadEvent } from '../helpers/getPageLoadEvent';
3
+ import { CONTEXT_MENU_ENABLE_THIRD_PARTY } from 'virtual:stl-docs-virtual-module';
3
4
 
4
5
  export type DropdownIcon = 'markdown' | 'copy' | 'claude' | 'chatgpt' | 'gemini' | 'cursor';
5
6
 
@@ -9,6 +10,7 @@ interface DropdownOptionInputProps {
9
10
  primaryAction?: boolean;
10
11
  clientHidden?: () => boolean;
11
12
  external: boolean;
13
+ thirdParty?: boolean;
12
14
  }
13
15
 
14
16
  function option(label: string[] | string, props: DropdownOptionInputProps) {
@@ -101,6 +103,7 @@ const aiDropdownOptions: DropdownOption[][] = [
101
103
  icon: 'claude',
102
104
  primaryAction: false,
103
105
  external: true,
106
+ thirdParty: true,
104
107
  }),
105
108
  option(['Open in ', 'ChatGPT'], {
106
109
  onClick: () => {
@@ -109,6 +112,7 @@ const aiDropdownOptions: DropdownOption[][] = [
109
112
  icon: 'chatgpt',
110
113
  primaryAction: false,
111
114
  external: true,
115
+ thirdParty: true,
112
116
  }),
113
117
  option(['Open in ', 'Cursor'], {
114
118
  onClick: () => {
@@ -122,6 +126,7 @@ const aiDropdownOptions: DropdownOption[][] = [
122
126
  icon: 'cursor',
123
127
  primaryAction: false,
124
128
  external: true,
129
+ thirdParty: true,
125
130
  }),
126
131
  ],
127
132
  [
@@ -184,13 +189,16 @@ const aiDropdownOptions: DropdownOption[][] = [
184
189
  // },
185
190
 
186
191
  export function getAIDropdownOptions() {
187
- const renderedOptions = aiDropdownOptions.map((group, index) => {
188
- return {
189
- options: group,
190
- isLast: index === aiDropdownOptions.length - 1,
191
- reactKey: index,
192
- };
193
- });
192
+ const renderedOptions = aiDropdownOptions
193
+ .map((e) => e.filter((e) => CONTEXT_MENU_ENABLE_THIRD_PARTY || !e.thirdParty))
194
+ .filter((e) => e.length)
195
+ .map((group, index, array) => {
196
+ return {
197
+ options: group,
198
+ isLast: index === array.length - 1,
199
+ reactKey: index,
200
+ };
201
+ });
194
202
 
195
203
  const allOptions = renderedOptions.flatMap((group) => group.options);
196
204
  const primaryAction = allOptions.find((o) => o.primaryAction) ?? allOptions[0]!;
package/plugin/index.ts CHANGED
@@ -135,6 +135,12 @@ function stlStarlightAstroIntegration(pluginConfig: NormalizedStainlessStarlight
135
135
  prerender: pluginConfig.experimentalPrerender ? command === 'build' : false,
136
136
  });
137
137
 
138
+ injectRoute({
139
+ pattern: `${pluginConfig.basePath}/index.md`,
140
+ entrypoint: resolveSrcFile('/plugin/routes/markdown.ts'),
141
+ prerender: pluginConfig.experimentalPrerender ? command === 'build' : false,
142
+ });
143
+
138
144
  const astroFile = command === 'build' ? 'DocsStatic' : 'Docs';
139
145
  injectRoute({
140
146
  pattern: `${pluginConfig.basePath}/[...slug]`,
@@ -234,7 +240,11 @@ function stlStarlightAstroIntegration(pluginConfig: NormalizedStainlessStarlight
234
240
  EXPERIMENTAL_COLLAPSIBLE_METHOD_DESCRIPTIONS:
235
241
  pluginConfig.experimentalCollapsibleMethodDescriptions,
236
242
  PROPERTY_SETTINGS: pluginConfig.propertySettings,
237
- ENABLE_CONTEXT_MENU: pluginConfig.contextMenu,
243
+ ENABLE_CONTEXT_MENU: !!pluginConfig.contextMenu,
244
+ CONTEXT_MENU_ENABLE_THIRD_PARTY:
245
+ (typeof pluginConfig.contextMenu === 'object'
246
+ ? pluginConfig.contextMenu.thirdParty
247
+ : null) ?? true,
238
248
  EXPERIMENTAL_REQUEST_BUILDER: pluginConfig.experimentalRequestBuilder,
239
249
  STAINLESS_PROJECT: pluginConfig.stainlessProject,
240
250
  LLMS_TXT_DESCRIPTION: pluginConfig.llmsTxt.description,
@@ -149,7 +149,7 @@ export type StainlessStarlightUserConfig = {
149
149
  * @default true
150
150
  */
151
151
 
152
- contextMenu?: boolean;
152
+ contextMenu?: boolean | { thirdParty?: boolean };
153
153
 
154
154
  /** When set to true, enables the experimental request builder interface for testing API endpoints. */
155
155
  experimentalRequestBuilder?: boolean;
@@ -29,7 +29,7 @@ const metadata: SpecMetadata = langsWithSpecs.map(({ language, spec }) => [
29
29
 
30
30
  // PageTitle override will skip rendering the default Starlight title
31
31
  Astro.locals._stlStarlightPage = {
32
- hasMarkdownRoute: false,
32
+ hasMarkdownRoute: true,
33
33
  };
34
34
  ---
35
35
 
@@ -1,14 +1,23 @@
1
1
  import type { APIRoute, GetStaticPaths } from 'astro';
2
+ import type { Node } from '@markdoc/markdoc';
2
3
  import { getReadmeContent } from '../react/Routing';
3
- import { getResourceFromSpec } from '@stainless-api/docs-ui/utils';
4
+ import { getResourceFromSpec, isResourceEmpty } from '@stainless-api/docs-ui/utils';
4
5
 
5
6
  import { renderMarkdown } from '@stainless-api/docs-ui/markdown';
7
+ import * as md from '@stainless-api/docs-ui/markdown/md';
6
8
 
7
- import { parseStainlessPath, type DocsLanguage } from '@stainless-api/docs-ui/routing';
9
+ import { parseStainlessPath, generateRoute, type DocsLanguage } from '@stainless-api/docs-ui/routing';
8
10
  import type { EnvironmentType } from '@stainless-api/docs-ui/markdown/utils';
9
- import { PROPERTY_SETTINGS, MIDDLEWARE } from 'virtual:stl-starlight-virtual-module';
11
+ import {
12
+ PROPERTY_SETTINGS,
13
+ MIDDLEWARE,
14
+ RESOLVED_API_REFERENCE_PATH,
15
+ } from 'virtual:stl-starlight-virtual-module';
10
16
  import { generateAllDocsRoutes } from '../helpers/generateDocsRoutes';
11
17
  import { getSDKJSONInSSR } from '../specs/fetchSpecSSR';
18
+ import Markdoc from '@markdoc/markdoc';
19
+ import type * as SDKJSON from '@stainless/sdk-json';
20
+ import { API_REFERENCE_BASE_PATH } from 'virtual:stl-docs-virtual-module';
12
21
 
13
22
  type RouteProps = {
14
23
  stainlessPath: string;
@@ -21,20 +30,41 @@ export const getStaticPaths = (async () => {
21
30
  return paths;
22
31
  }) satisfies GetStaticPaths;
23
32
 
24
- export const GET: APIRoute<RouteProps> = async ({ props }) => {
25
- const spec = await getSDKJSONInSSR(props.language);
33
+ function renderLink(stainlessPath: string, title: string) {
34
+ const href = generateRoute(RESOLVED_API_REFERENCE_PATH, 'http', stainlessPath);
35
+ return href ? md.link(`${href}/index.md`, title) : md.text(title);
36
+ }
26
37
 
27
- if (props.kind === 'readme') {
28
- const readmeContent = await getReadmeContent(spec, props.language);
29
- return new Response(readmeContent, {
30
- headers: { 'Content-Type': 'text/plain' },
31
- });
38
+ function renderOverviewResource(resource: SDKJSON.Resource, topLevel: boolean): Node[] {
39
+ const link = renderLink(resource.stainlessPath, resource.title);
40
+
41
+ const methodItems = Object.values(resource.methods).map((method) =>
42
+ md.item(md.paragraph(renderLink(method.stainlessPath, method.title))),
43
+ );
44
+
45
+ const subItems = Object.values(resource.subresources ?? {})
46
+ .filter((sub) => !isResourceEmpty(sub))
47
+ .flatMap((sub) => renderOverviewResource(sub, false));
48
+
49
+ const nestedItems = [...methodItems, ...subItems];
50
+ const list = nestedItems.length > 0 ? [md.list(...nestedItems)] : [];
51
+
52
+ return topLevel ? [md.heading(2, [link]), ...list] : [md.item(md.paragraph(link), ...list)];
53
+ }
54
+
55
+ function renderOverview({ spec }: EnvironmentType): string {
56
+ const output: Node[] = [md.heading(1, 'API Reference')];
57
+
58
+ for (const resource of Object.values(spec.resources)) {
59
+ if (!isResourceEmpty(resource)) output.push(...renderOverviewResource(resource, true));
32
60
  }
33
61
 
34
- const parsed = parseStainlessPath(props.stainlessPath);
35
- const resource = getResourceFromSpec(props.stainlessPath, spec);
62
+ const doc = new Markdoc.Ast.Node('document', {}, output);
63
+ return Markdoc.format(doc);
64
+ }
36
65
 
37
- if (!resource) throw new Error('Invalid route');
66
+ export const GET: APIRoute<RouteProps> = async ({ props, routePattern }) => {
67
+ const spec = await getSDKJSONInSSR(props.language ?? 'http');
38
68
 
39
69
  const env: EnvironmentType = {
40
70
  spec,
@@ -48,6 +78,25 @@ export const GET: APIRoute<RouteProps> = async ({ props }) => {
48
78
  },
49
79
  };
50
80
 
81
+ if (routePattern === `${API_REFERENCE_BASE_PATH}/index.md`) {
82
+ const content = renderOverview({ ...env, language: 'http' });
83
+ return new Response(content, {
84
+ headers: { 'Content-Type': 'text/plain' },
85
+ });
86
+ }
87
+
88
+ if (props.kind === 'readme') {
89
+ const readmeContent = await getReadmeContent(spec, props.language);
90
+ return new Response(readmeContent, {
91
+ headers: { 'Content-Type': 'text/plain' },
92
+ });
93
+ }
94
+
95
+ const parsed = parseStainlessPath(props.stainlessPath);
96
+ const resource = getResourceFromSpec(props.stainlessPath, spec);
97
+
98
+ if (!resource) throw new Error('Invalid route');
99
+
51
100
  const target = props.kind === 'http_method' && parsed?.method ? resource.methods[parsed.method]! : resource;
52
101
  const output = renderMarkdown(env, target);
53
102
 
@@ -106,6 +106,4 @@ export const specCache = new FileCache({
106
106
 
107
107
  export type SpecCacheResult = Awaited<ReturnType<typeof specCache.get>>;
108
108
 
109
- export type GenerateSpecFn = typeof generateSpecFromStrings;
110
-
111
- export type SpecWithAuth = Awaited<ReturnType<GenerateSpecFn>>;
109
+ export type SpecWithAuth = Awaited<ReturnType<typeof generateSpecFromStrings>>;