@stainless-api/docs 0.1.0-beta.15 → 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 (32) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/package.json +9 -17
  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 -17
  9. package/plugin/loadPluginConfig.ts +90 -21
  10. package/plugin/react/Routing.tsx +13 -23
  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/index.ts +58 -23
  23. package/stl-docs/loadStlDocsConfig.ts +17 -2
  24. package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +64 -0
  25. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +33 -0
  26. package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
  27. package/styles/page.css +31 -4
  28. package/virtual-module.d.ts +3 -2
  29. package/plugin/globalJs/ai-dropdown.ts +0 -57
  30. package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -58
  31. package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -55
  32. /package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @stainless-api/docs
2
2
 
3
+ ## 0.1.0-beta.16
4
+
5
+ ### Patch Changes
6
+
7
+ - 0618d05: Markdown rendering of prose pages, ai context menu, auth via cli, minor fixes
8
+ - Updated dependencies [0618d05]
9
+ - @stainless-api/docs-ui@0.1.0-beta.13
10
+ - @stainless-api/ui-primitives@0.1.0-beta.14
11
+
3
12
  ## 0.1.0-beta.15
4
13
 
5
14
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stainless-api/docs",
3
- "version": "0.1.0-beta.15",
3
+ "version": "0.1.0-beta.16",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -9,23 +9,9 @@
9
9
  "exports": {
10
10
  ".": "./stl-docs/index.ts",
11
11
  "./plugin": "./plugin/index.ts",
12
- "./OverviewRoute": "./plugin/routes/Overview.astro",
13
- "./DocsRoute": "./plugin/routes/Docs.astro",
14
- "./DocsStaticRoute": "./plugin/routes/DocsStatic.astro",
15
- "./MarkdownRoute": "./plugin/routes/markdown.ts",
16
- "./Search": "./plugin/components/search/Search.astro",
17
- "./replaceSidebarPlaceholderMiddleware": "./plugin/replaceSidebarPlaceholderMiddleware.ts",
18
12
  "./plugin/middleware": "./plugin/middlewareBuilder/stlStarlightMiddleware.ts",
19
13
  "./plugin/MiddlewareTypes": "./plugin/middlewareBuilder/stainlessMiddleware.d.ts",
20
- "./Header": "./stl-docs/components/Header.astro",
21
- "./ThemeSelect": "./stl-docs/components/ThemeSelect.astro",
22
- "./BaseSidebar": "./stl-docs/components/sidebars/BaseSidebar.astro",
23
- "./SDKSelectSidebar": "./stl-docs/components/sidebars/SDKSelectSidebar.astro",
24
- "./ContentPanel": "./stl-docs/components/content-panel/ContentPanel.astro",
25
- "./TableOfContents": "./stl-docs/components/TableOfContents.astro",
26
- "./tabsMiddleware": "./stl-docs/tabsMiddleware.ts",
27
14
  "./stainless-docs/mintlify-compat": "./stl-docs/components/mintlify-compat/index.ts",
28
- "./theme": "./theme.css",
29
15
  "./mintlify-compat.css": "./styles/mintlify-compat.css",
30
16
  "./font-imports": "./styles/fonts.css",
31
17
  "./components": "./stl-docs/components/index.ts"
@@ -57,8 +43,14 @@
57
43
  "shiki": "^3.9.2",
58
44
  "web-worker": "^1.5.0",
59
45
  "yaml": "^2.8.0",
60
- "@stainless-api/ui-primitives": "0.1.0-beta.13",
61
- "@stainless-api/docs-ui": "0.1.0-beta.12"
46
+ "node-html-parser": "^7.0.1",
47
+ "rehype-parse": "^9.0.1",
48
+ "rehype-remark": "^10.0.1",
49
+ "remark-gfm": "^4.0.1",
50
+ "remark-stringify": "^11.0.0",
51
+ "unified": "^11.0.5",
52
+ "@stainless-api/docs-ui": "0.1.0-beta.13",
53
+ "@stainless-api/ui-primitives": "0.1.0-beta.14"
62
54
  },
63
55
  "devDependencies": {
64
56
  "@markdoc/markdoc": "^0.5.2",
@@ -5,6 +5,7 @@ import type * as SDKJSON from '~/lib/json-spec-v2/types';
5
5
  import { Languages } from '@stainless-api/docs-ui/src/routing';
6
6
  import { buildIndex } from '@stainless-api/docs-ui/src/search/providers/algolia';
7
7
  import type { VersionUserConfig } from './loadPluginConfig';
8
+ import type { AstroIntegrationLogger } from 'astro';
8
9
 
9
10
  const markdocConfig = {
10
11
  nodes: {
@@ -20,7 +21,31 @@ function renderMarkdown(content?: string) {
20
21
  return Markdoc.renderers.html(transformed);
21
22
  }
22
23
 
23
- export async function buildAlgoliaIndex({ version, apiKey }: { version: VersionUserConfig; apiKey: string }) {
24
+ export async function buildAlgoliaIndex({
25
+ version,
26
+ apiKey,
27
+ logger,
28
+ }: {
29
+ version: VersionUserConfig;
30
+ apiKey: string;
31
+ logger?: AstroIntegrationLogger;
32
+ }) {
33
+ function warnLog(message: string) {
34
+ if (logger) {
35
+ logger.warn(message);
36
+ } else {
37
+ console.warn(message);
38
+ }
39
+ }
40
+
41
+ function infoLog(message: string) {
42
+ if (logger) {
43
+ logger.info(message);
44
+ } else {
45
+ console.log(message);
46
+ }
47
+ }
48
+
24
49
  const client = new Stainless({ apiKey });
25
50
  const configs = await client.projects.configs.retrieve({
26
51
  project: version.stainlessProject,
@@ -64,9 +89,9 @@ export async function buildAlgoliaIndex({ version, apiKey }: { version: VersionU
64
89
  !indexName && 'PUBLIC_ALGOLIA_INDEX',
65
90
  !algoliaWriteKey && 'PRIVATE_ALGOLIA_WRITE_KEY',
66
91
  ].filter(Boolean);
67
- console.warn(`⚠️ Skipping Algolia indexing due to missing environment variables: ${missing.join(', ')}`);
92
+ warnLog(`Skipping Algolia indexing due to missing environment variables: ${missing.join(', ')}`);
68
93
  return;
69
94
  }
70
95
  await buildIndex(appId, indexName, algoliaWriteKey, sdkJson, renderMarkdown);
71
- console.log('Indexing complete.');
96
+ infoLog('Indexing complete.');
72
97
  }
@@ -1,10 +1,11 @@
1
+ import type { AstroIntegrationLogger } from 'astro';
1
2
  import { createServer, IncomingMessage } from 'http';
2
3
  import { readFile } from 'fs/promises';
3
4
 
4
5
  import type * as SDKJSON from '~/lib/json-spec-v2/types';
5
6
  import { createSDKJSON, parseInputs, transformOAS } from './worker';
6
7
  import { Languages, parseRoute, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
7
- import Stainless from '@stainless-api/sdk';
8
+ import Stainless, { APIError } from '@stainless-api/sdk';
8
9
 
9
10
  import {
10
11
  toStarlightSidebar,
@@ -12,6 +13,7 @@ import {
12
13
  SidebarConfigItemsBuilder,
13
14
  } from './sidebar-builder';
14
15
  import type { VersionUserConfig } from '../loadPluginConfig';
16
+ import { bold } from '../../shared/terminalUtils';
15
17
 
16
18
  export type InputFilePaths = {
17
19
  oasPath?: string;
@@ -24,76 +26,105 @@ async function versionInfo(project: string, apiKey: string) {
24
26
  });
25
27
 
26
28
  const content = await data.text();
27
- return JSON.parse(content);
29
+ return JSON.parse(content) as Record<DocsLanguage, string>;
30
+ }
31
+
32
+ function redactApiKey(apiKey: string) {
33
+ return apiKey
34
+ .split('')
35
+ .map((char, index) => (index < 10 ? char : '*'))
36
+ .join('');
28
37
  }
29
38
 
30
39
  async function loadSpec({
31
40
  apiKey,
32
41
  devPaths,
33
42
  version,
43
+ logger,
34
44
  }: {
35
45
  apiKey: string;
36
46
  devPaths: InputFilePaths;
37
47
  version: VersionUserConfig;
48
+ logger: AstroIntegrationLogger;
38
49
  }) {
39
- let oasStr: string;
40
- let configStr: string;
41
- let versions: Record<DocsLanguage, string> | undefined;
42
-
43
- if (devPaths.oasPath && devPaths.configPath) {
44
- [oasStr, configStr] = await Promise.all([
45
- readFile(devPaths.oasPath, 'utf-8'),
46
- readFile(devPaths.configPath, 'utf-8'),
47
- ]);
48
- } else {
49
- const client = new Stainless({ apiKey });
50
- const configs = await client.projects.configs.retrieve({
51
- project: version.stainlessProject,
52
- branch: version.branch,
53
- include: 'openapi',
54
- });
50
+ async function unsafeLoad() {
51
+ let oasStr: string;
52
+ let configStr: string;
53
+ let versions: Record<DocsLanguage, string> | undefined;
54
+
55
+ if (devPaths.oasPath && devPaths.configPath) {
56
+ [oasStr, configStr] = await Promise.all([
57
+ readFile(devPaths.oasPath, 'utf-8'),
58
+ readFile(devPaths.configPath, 'utf-8'),
59
+ ]);
60
+ } else {
61
+ const client = new Stainless({ apiKey });
62
+ const configs = await client.projects.configs.retrieve({
63
+ project: version.stainlessProject,
64
+ branch: version.branch,
65
+ include: 'openapi',
66
+ });
55
67
 
56
- versions = await versionInfo(version.stainlessProject, apiKey);
68
+ versions = await versionInfo(version.stainlessProject, apiKey);
57
69
 
58
- const configYML = Object.values(configs)[0] as { content: any };
59
- const oasJson = Object.values(configs)[1] as { content: any };
60
- oasStr = oasJson['content'];
61
- configStr = configYML['content'];
62
- }
70
+ const configYML = Object.values(configs)[0] as { content: any };
71
+ const oasJson = Object.values(configs)[1] as { content: any };
72
+ oasStr = oasJson['content'];
73
+ configStr = configYML['content'];
74
+ }
63
75
 
64
- const { oas, config } = await parseInputs({
65
- oas: oasStr,
66
- config: configStr,
67
- });
76
+ const { oas, config } = await parseInputs({
77
+ oas: oasStr,
78
+ config: configStr,
79
+ });
68
80
 
69
- const transformedOAS = await transformOAS({ oas, config });
81
+ const transformedOAS = await transformOAS({ oas, config });
70
82
 
71
- const languages =
72
- config.docs?.languages ??
73
- (Object.entries(config.targets)
74
- // @ts-expect-error we don't have the actual Stainless config type here
75
- .filter(([name, target]) => Languages.includes(name) && !target.skip)
76
- .map(([name]) => name) as SDKJSON.SpecLanguage[]);
83
+ const languages =
84
+ config.docs?.languages ??
85
+ (Object.entries(config.targets)
86
+ // @ts-expect-error we don't have the actual Stainless config type here
87
+ .filter(([name, target]) => Languages.includes(name) && !target.skip)
88
+ .map(([name]) => name) as SDKJSON.SpecLanguage[]);
77
89
 
78
- const sdkJson = await createSDKJSON({
79
- oas: transformedOAS,
80
- config,
81
- languages,
82
- });
90
+ const sdkJson = await createSDKJSON({
91
+ oas: transformedOAS,
92
+ config,
93
+ languages,
94
+ });
83
95
 
84
- if (versions) {
85
- for (const [lang, version] of Object.entries(versions)) {
86
- const meta = sdkJson.metadata[lang as DocsLanguage];
87
- if (meta?.version) meta.version = version;
96
+ if (versions) {
97
+ for (const [lang, version] of Object.entries(versions)) {
98
+ const meta = sdkJson.metadata[lang as DocsLanguage];
99
+ if (meta?.version) meta.version = version;
100
+ }
88
101
  }
102
+
103
+ const id = crypto.randomUUID();
104
+
105
+ return {
106
+ data: sdkJson,
107
+ id,
108
+ };
89
109
  }
90
110
 
91
- const id = crypto.randomUUID();
111
+ try {
112
+ const result = await unsafeLoad();
113
+ return result;
114
+ } catch (error) {
115
+ logger.error(bold('Failed to fetch API reference information from Stainless:'));
116
+ if (error instanceof APIError && error.status >= 400 && error.status < 500) {
117
+ logger.error(`Requested project slug: "${version.stainlessProject}"`);
118
+ logger.error(`API key: "${redactApiKey(apiKey)}"`);
119
+ logger.error(
120
+ `This error can usually be corrected by re-authenticating with the Stainless. Use the CLI (stl auth login) or verify that the Stainless API key you're using can access the project mentioned above.`,
121
+ );
122
+ } else {
123
+ logger.error(error instanceof Error ? error.message : 'Unknown error');
124
+ }
92
125
 
93
- return {
94
- data: sdkJson,
95
- id,
96
- };
126
+ process.exit(1);
127
+ }
97
128
  }
98
129
 
99
130
  class Spec {
@@ -107,6 +138,7 @@ class Spec {
107
138
  apiKey: this.apiKey,
108
139
  devPaths: this.devPaths,
109
140
  version: this.version,
141
+ logger: this.logger,
110
142
  });
111
143
  }
112
144
 
@@ -128,15 +160,22 @@ class Spec {
128
160
  return spec;
129
161
  }
130
162
 
131
- constructor(apiKey: string, version: VersionUserConfig, devPaths: InputFilePaths) {
163
+ constructor(
164
+ apiKey: string,
165
+ version: VersionUserConfig,
166
+ devPaths: InputFilePaths,
167
+ private logger: AstroIntegrationLogger,
168
+ ) {
132
169
  this.specPromise = loadSpec({
133
170
  apiKey,
134
171
  devPaths,
135
172
  version,
173
+ logger,
136
174
  });
137
175
  this.devPaths = devPaths;
138
176
  this.apiKey = apiKey;
139
177
  this.version = version;
178
+ this.logger = logger;
140
179
  }
141
180
  }
142
181
 
@@ -166,14 +205,16 @@ export function startDevServer({
166
205
  devPaths,
167
206
  apiKey,
168
207
  getGeneratedSidebarConfig,
208
+ logger,
169
209
  }: {
170
210
  port: number;
171
211
  version: VersionUserConfig;
172
212
  devPaths: InputFilePaths;
173
213
  apiKey: string;
174
214
  getGeneratedSidebarConfig: (id: number) => GeneratedSidebarConfig | null;
215
+ logger: AstroIntegrationLogger;
175
216
  }) {
176
- const spec = new Spec(apiKey, version, devPaths);
217
+ const spec = new Spec(apiKey, version, devPaths, logger);
177
218
 
178
219
  const server = createServer(async (req, res) => {
179
220
  // Add CORS headers
@@ -250,7 +291,7 @@ export function startDevServer({
250
291
  });
251
292
 
252
293
  server.listen(port, () => {
253
- console.log(`Server is running on port ${port}`);
294
+ logger.debug(`Stainless spec server is running on port: ${port}`);
254
295
  });
255
296
 
256
297
  return {
@@ -266,3 +307,5 @@ export function startDevServer({
266
307
  },
267
308
  };
268
309
  }
310
+
311
+ export type DevSpecServer = Awaited<ReturnType<typeof startDevServer>>;
@@ -208,13 +208,6 @@ export class SidebarConfigItemsBuilder {
208
208
  };
209
209
  }
210
210
 
211
- private sortByLabel<T extends UserSidebarConfigItem>(items: T[]) {
212
- // sorts in place
213
- items.sort((a, b) => {
214
- return a.label.localeCompare(b.label);
215
- });
216
- }
217
-
218
211
  private generateResourceGroup(resource: SDKJSON.Resource, collapsed: boolean): ReferenceSidebarGroup {
219
212
  const entries: ReferenceSidebarConfigItem[] = [];
220
213
  if (!this.options?.excludeResourceOverviewPages) {
@@ -228,7 +221,6 @@ export class SidebarConfigItemsBuilder {
228
221
  methodPages.push(this.toMethodPage(m, langDecl));
229
222
  }
230
223
  }
231
- this.sortByLabel(methodPages);
232
224
  entries.push(...methodPages);
233
225
 
234
226
  const subresources = Object.values(resource.subresources ?? {});
@@ -238,7 +230,6 @@ export class SidebarConfigItemsBuilder {
238
230
  subresourceGroups.push(this.generateResourceGroup(sub, true));
239
231
  }
240
232
  }
241
- this.sortByLabel(subresourceGroups);
242
233
  entries.push(...subresourceGroups);
243
234
 
244
235
  return {
@@ -0,0 +1,161 @@
1
+ import { initDropdownButton } from '@stainless-api/ui-primitives/scripts';
2
+ import { getPageLoadEvent } from '../helpers/getPageLoadEvent';
3
+
4
+ export type DropdownIcon = 'markdown' | 'copy' | 'claude' | 'chatgpt';
5
+
6
+ interface DropdownOptionInputProps {
7
+ onClick: () => void;
8
+ icon: DropdownIcon;
9
+ primaryAction?: boolean;
10
+ label: string[] | string;
11
+ }
12
+
13
+ function getMarkdownUrl(type: 'relative' | 'absolute') {
14
+ const currentUrl = new URL(window.location.href);
15
+ const hasTrailingSlash = currentUrl.pathname.endsWith('/');
16
+
17
+ const markdownUrl = [
18
+ type === 'absolute' ? currentUrl.origin : '',
19
+ currentUrl.pathname,
20
+ hasTrailingSlash ? '' : '/',
21
+ 'index.md',
22
+ ].join('');
23
+
24
+ return markdownUrl;
25
+ }
26
+
27
+ function openInLLM(serviceUrl: string) {
28
+ const mdUrl = getMarkdownUrl('absolute');
29
+ const prompt = encodeURIComponent(
30
+ `Load the contents of ${mdUrl} into this chat's context so we can discuss it.`,
31
+ );
32
+ window.open(`${serviceUrl}${prompt}`, '_blank');
33
+ }
34
+
35
+ // 2d array of dropdown options
36
+ // each sub-array is a group, separated by a horizontal rule in the UI
37
+ const aiDropdownOptions: DropdownOptionInputProps[][] = [
38
+ [
39
+ {
40
+ label: 'View as Markdown',
41
+ onClick: () => {
42
+ window.open(getMarkdownUrl('absolute'), '_blank');
43
+ },
44
+ icon: 'markdown',
45
+ primaryAction: true,
46
+ },
47
+ {
48
+ label: 'Copy Markdown',
49
+ onClick: () => {
50
+ // Source: https://wolfgangrittner.dev/how-to-use-clipboard-api-in-firefox/
51
+ const markdownUrl = getMarkdownUrl('relative');
52
+ // ClipboardItem doesn't exist in every browser
53
+ // eslint-disable-next-line no-constant-binary-expression
54
+ if (typeof ClipboardItem && navigator.clipboard.write) {
55
+ // NOTE: Safari locks down the clipboard API to only work when triggered
56
+ // by a direct user interaction. You can't use it async in a promise.
57
+ // But! You can wrap the promise in a ClipboardItem, and give that to
58
+ // the clipboard API.
59
+ // Found this on https://developer.apple.com/forums/thread/691873
60
+ const text = new ClipboardItem({
61
+ 'text/plain': fetch(markdownUrl)
62
+ .then((response) => response.text())
63
+ .then((text) => new Blob([text], { type: 'text/plain' })),
64
+ });
65
+ navigator.clipboard.write([text]);
66
+ } else {
67
+ // NOTE: Firefox has support for ClipboardItem and navigator.clipboard.write,
68
+ // but those are behind `dom.events.asyncClipboard.clipboardItem` preference.
69
+ // Good news is that other than Safari, Firefox does not care about
70
+ // Clipboard API being used async in a Promise.
71
+ fetch(markdownUrl)
72
+ .then((response) => response.text())
73
+ .then((text) => navigator.clipboard.writeText(text));
74
+ }
75
+ },
76
+ icon: 'copy',
77
+ primaryAction: false,
78
+ },
79
+ ],
80
+ [
81
+ {
82
+ label: ['Open in ', 'Claude'],
83
+ onClick: () => {
84
+ openInLLM('https://claude.ai/new?q=');
85
+ },
86
+ icon: 'claude',
87
+ primaryAction: false,
88
+ },
89
+ {
90
+ label: ['Open in ', 'ChatGPT'],
91
+ onClick: () => {
92
+ openInLLM('https://chatgpt.com/?hints=search&prompt=');
93
+ },
94
+ icon: 'chatgpt',
95
+ primaryAction: false,
96
+ },
97
+ // TODO: Add Cursor support
98
+ // {
99
+ // label: ['Open in ', 'Cursor'],
100
+ // onClick: () => {
101
+ // openInLLM('https://www.cursor.so/?prompt=');
102
+ // },
103
+ // icon: 'cursor',
104
+ // primaryAction: false,
105
+ // }
106
+ ],
107
+ ];
108
+
109
+ function renderGroup(group: DropdownOptionInputProps[]) {
110
+ return group.map((option) => {
111
+ const label = typeof option.label === 'string' ? [option.label] : option.label;
112
+ return {
113
+ ...option,
114
+ label: label,
115
+ primaryAction: option.primaryAction ?? false,
116
+ id: label.join('').toLowerCase().replace(/ /g, '-'),
117
+ };
118
+ });
119
+ }
120
+
121
+ export function getAIDropdownOptions() {
122
+ const renderedOptions = aiDropdownOptions.map((group, index) => {
123
+ return {
124
+ options: renderGroup(group),
125
+ isLast: index === aiDropdownOptions.length - 1,
126
+ reactKey: index,
127
+ };
128
+ });
129
+
130
+ const allOptions = renderedOptions.flatMap((group) => group.options);
131
+ const primaryAction = allOptions.find((o) => o.primaryAction) ?? allOptions[0]!;
132
+
133
+ return {
134
+ primaryAction,
135
+ groups: renderedOptions,
136
+ };
137
+ }
138
+
139
+ export function wireAIDropdown() {
140
+ const { primaryAction, groups } = getAIDropdownOptions();
141
+ function triggerOption(id: string) {
142
+ const option = groups.flatMap((group) => group.options).find((option) => option.id === id);
143
+ if (!option) return;
144
+ option.onClick();
145
+ }
146
+
147
+ document.addEventListener(getPageLoadEvent(), () => {
148
+ const dropdowns = document.querySelectorAll('[data-dropdown-id]');
149
+ dropdowns.forEach((dropdown) => {
150
+ initDropdownButton({
151
+ dropdown: dropdown,
152
+ onSelect: (value) => {
153
+ triggerOption(value);
154
+ },
155
+ onPrimaryAction: () => {
156
+ triggerOption(primaryAction.id);
157
+ },
158
+ });
159
+ });
160
+ });
161
+ }
@@ -5,8 +5,6 @@ import { navigate } from 'astro:transitions/client';
5
5
  import { getPageLoadEvent } from '../helpers/getPageLoadEvent.ts';
6
6
 
7
7
  import { initDropdown } from '@stainless-api/docs-ui/src/components/scripts/dropdown';
8
- import { initDropdownButton } from '@stainless-api/ui-primitives/scripts';
9
- import { copyCurrentPageAsMarkdown, onSelectAIOption } from './ai-dropdown.ts';
10
8
 
11
9
  history.scrollRestoration = 'auto';
12
10
 
@@ -36,26 +34,6 @@ document.addEventListener(getPageLoadEvent(), () => {
36
34
  if (path) setTimeout(() => scrollToPath(path.slice(1)), 10);
37
35
  });
38
36
 
39
- document.addEventListener(getPageLoadEvent(), () => {
40
- initDropdownButton({
41
- dropdownId: 'ai-dropdown-button',
42
- onSelect: onSelectAIOption,
43
- onPrimaryAction: (el) => {
44
- copyCurrentPageAsMarkdown();
45
- const innerText = el.querySelector('[data-part="primary-action-text"]');
46
- if (!innerText) return;
47
-
48
- const originalInnerHtml = innerText.innerHTML;
49
- innerText.innerHTML = 'Copied!';
50
- el.classList.add('disabled');
51
- setTimeout(() => {
52
- innerText.innerHTML = originalInnerHtml;
53
- el.classList.remove('disabled');
54
- }, 1000);
55
- },
56
- });
57
- });
58
-
59
37
  document.addEventListener('click', (event) => {
60
38
  const toggle = (event.target as HTMLElement).closest(
61
39
  '[data-stldocs-property-toggle-expanded] > .stldocs-expand-toggle-content',