@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.
@@ -26,10 +26,10 @@ fs.mkdirSync(DEPS_DIR, { recursive: true });
26
26
 
27
27
  const previewWorkerPath = path.join(
28
28
  env.STAINLESS_ROOT,
29
- '/packages/preview-worker/dist/stainless/preview.worker.docs.js',
29
+ '/packages/preview-worker/dist/stainless/preview.worker.docs.cjs',
30
30
  );
31
31
 
32
- const readmeTemplatePath = path.join(env.STAINLESS_ROOT, '/legacy-dir-root/templates');
32
+ const readmeTemplatePath = path.join(env.STAINLESS_ROOT, 'packages/sdk-codegen/templates');
33
33
 
34
34
  fs.copyFileSync(previewWorkerPath, path.join(DEPS_DIR, 'preview.worker.docs.js'));
35
35
 
@@ -1,4 +1,5 @@
1
- import { FeedbackResponseBody, MetadataResponseBody, RequestBody, ResponseChunk } from './schemas';
1
+ import { ResponseChunk } from './schemas';
2
+ export { responseChunk, type ResponseChunk } from './schemas';
2
3
 
3
4
  export type DocsChatHandler = {
4
5
  generateResponse: (
@@ -7,12 +8,10 @@ export type DocsChatHandler = {
7
8
  priorMessages,
8
9
  }: {
9
10
  query: string;
10
- priorMessages: NonNullable<RequestBody['additionalContext']>['prior_messages'];
11
+ priorMessages: { role: 'user' | 'assistant'; content: string }[];
11
12
  },
12
13
  abortSignal: AbortSignal,
13
14
  ) => AsyncGenerator<ResponseChunk>;
14
15
 
15
- onRate: (spanId: string, score: 0 | 1) => Promise<FeedbackResponseBody>;
16
-
17
- onAssignMetadata: (spanId: string, metadata: Record<string, string>) => Promise<MetadataResponseBody>;
16
+ onRate?: (spanId: string, score: 0 | 1) => Promise<unknown>;
18
17
  };
@@ -129,6 +129,7 @@ export function useChat({ handler }: { handler: DocsChatHandler }) {
129
129
 
130
130
  try {
131
131
  let chunk: ResponseChunk | undefined = undefined;
132
+ let sawDone = false;
132
133
  for await (chunk of handler.generateResponse(
133
134
  {
134
135
  query: question,
@@ -147,6 +148,7 @@ export function useChat({ handler }: { handler: DocsChatHandler }) {
147
148
 
148
149
  if (chunk.type === 'done') {
149
150
  dispatch({ type: 'completeResponse', respondingTo: userMessageId, spanId: chunk.span_id });
151
+ sawDone = true;
150
152
  // stop reading from the stream on done
151
153
  break;
152
154
  }
@@ -190,6 +192,12 @@ export function useChat({ handler }: { handler: DocsChatHandler }) {
190
192
  respondingTo: userMessageId,
191
193
  errorMessage: 'No response received. Please try again.',
192
194
  });
195
+ } else if (!sawDone && !abortControllerRef.current.signal.aborted) {
196
+ // Generator exhausted without a `done` chunk — synthesize completion.
197
+ if (lastChunkType === 'text') {
198
+ dispatch({ type: 'completeMessage', id: currentResponseId });
199
+ }
200
+ dispatch({ type: 'completeResponse', respondingTo: userMessageId, spanId: crypto.randomUUID() });
193
201
  }
194
202
  } catch {
195
203
  dispatch({
@@ -202,14 +210,16 @@ export function useChat({ handler }: { handler: DocsChatHandler }) {
202
210
  [chatMessages, handler],
203
211
  );
204
212
 
205
- async function rateMessage(spanId: string, rating: 'up' | 'down'): Promise<boolean> {
206
- try {
207
- const { success } = await handler.onRate(spanId, { up: 1 as const, down: 0 as const }[rating]);
208
- return success;
209
- } catch {
210
- return false;
211
- }
212
- }
213
+ const rateMessage = handler.onRate
214
+ ? async (spanId: string, rating: 'up' | 'down') => {
215
+ try {
216
+ await handler.onRate?.(spanId, { up: 1 as const, down: 0 as const }[rating]);
217
+ return true;
218
+ } catch {
219
+ return false;
220
+ }
221
+ }
222
+ : undefined;
213
223
 
214
- return { chatMessages, sendMessage, rateMessage, setMetadata: handler.onAssignMetadata.bind(handler) };
224
+ return { chatMessages, sendMessage, rateMessage };
215
225
  }
@@ -1,38 +1,5 @@
1
1
  import z from 'zod';
2
2
 
3
- // TODO: replace with generated SDK types instead of copy/pasting from other repo
4
- export const requestBody = z.object({
5
- query: z.string(),
6
- sdk: z.object({
7
- project: z.string(),
8
- language: z.string(),
9
- version: z.string().optional(),
10
- }),
11
- stream: z.boolean().optional(),
12
- budget: z
13
- .object({
14
- maxTokens: z.number().optional(),
15
- })
16
- .optional(),
17
- session_id: z.string().optional(),
18
- additionalContext: z
19
- .object({
20
- prior_messages: z.array(
21
- z.object({
22
- role: z.enum(['user', 'assistant']),
23
- content: z.string(),
24
- }),
25
- ),
26
- code: z.string().optional(),
27
- intent: z.string().optional(),
28
- lsp: z.string().optional(),
29
- errors: z.string().optional(),
30
- })
31
- .optional(),
32
- browser_id: z.string().optional(),
33
- });
34
- export type RequestBody = z.input<typeof requestBody>;
35
-
36
3
  export const responseChunk = z.discriminatedUnion('type', [
37
4
  z.object({
38
5
  type: z.literal('text'),
@@ -58,13 +25,3 @@ export const responseChunk = z.discriminatedUnion('type', [
58
25
  }),
59
26
  ]);
60
27
  export type ResponseChunk = z.infer<typeof responseChunk>;
61
-
62
- export const feedbackRequestBody = z.object({ score: z.number().min(0).max(1) });
63
- export type FeedbackRequestBody = z.infer<typeof feedbackRequestBody>;
64
- export const feedbackResponseBody = z.object({ success: z.boolean() });
65
- export type FeedbackResponseBody = z.infer<typeof feedbackResponseBody>;
66
-
67
- export const metadataRequestBody = z.object({ metadata: z.record(z.string(), z.string()) });
68
- export type MetadataRequestBody = z.infer<typeof metadataRequestBody>;
69
- export const metadataResponseBody = z.object({ success: z.boolean() });
70
- export type MetadataResponseBody = z.infer<typeof metadataResponseBody>;
@@ -2,13 +2,13 @@ import { motion } from 'motion/react';
2
2
  import { Suspense, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
3
3
  import { useScrollToBottom } from './scroll-manager';
4
4
  import { useChat } from '../hook';
5
- import { DocsLanguage } from '@stainless-api/docs-ui/routing';
5
+ import type { DocsChatHandler } from '../docs-chat-handler';
6
6
 
7
7
  import ChatLog from './components/ChatLog';
8
8
  import AiChatTrigger from './Trigger';
9
9
  import ChatEmpty from './components/ChatEmpty';
10
10
  import ChatControls from './components/ChatControls';
11
- import { StainlessHandler } from '../stainless-handler';
11
+ import { AI_CHAT_HANDLER } from 'virtual:stl-docs-ai-chat';
12
12
 
13
13
  import styles from './AiChat.module.css';
14
14
  import clsx from 'clsx';
@@ -26,30 +26,18 @@ const examplesPromise = import('virtual:stl-docs-ai-chat-examples')
26
26
  .then((mod) => mod.examples)
27
27
  .catch(() => undefined);
28
28
 
29
- export default function AiChat({
30
- stainlessProject,
31
- currentLanguage,
32
- siteTitle,
33
- }: {
34
- stainlessProject: string;
35
- currentLanguage?: DocsLanguage;
36
- siteTitle?: string;
37
- }) {
38
- const handler = useMemo(
39
- () => new StainlessHandler(currentLanguage ?? 'http', siteTitle, stainlessProject),
40
- [currentLanguage, siteTitle, stainlessProject],
41
- );
42
- const { chatMessages, sendMessage, rateMessage, setMetadata } = useChat({
29
+ export default function AiChat({ siteTitle }: { siteTitle?: string }) {
30
+ if (!AI_CHAT_HANDLER) {
31
+ return null;
32
+ }
33
+ return <AiChatInner siteTitle={siteTitle} handler={AI_CHAT_HANDLER} />;
34
+ }
35
+
36
+ function AiChatInner({ siteTitle, handler }: { siteTitle?: string; handler: DocsChatHandler }) {
37
+ const { chatMessages, sendMessage, rateMessage } = useChat({
43
38
  handler,
44
39
  });
45
40
 
46
- const onCopyMessage = useCallback(
47
- (spanId: string) => {
48
- setMetadata(spanId, { copied_to_clipboard: 'true' }).catch(() => {});
49
- },
50
- [setMetadata],
51
- );
52
-
53
41
  // panel mode is supported only on larger viewports
54
42
  const supportsPanel = useSyncExternalStore(
55
43
  (cb) => {
@@ -168,7 +156,6 @@ export default function AiChat({
168
156
  <ChatLog
169
157
  messages={chatMessages}
170
158
  rateMessage={rateMessage}
171
- onCopyMessage={onCopyMessage}
172
159
  responsePending={pendingResponses > 0}
173
160
  />
174
161
  ) : (
@@ -11,12 +11,10 @@ import { LoaderCircleIcon } from 'lucide-react';
11
11
  export default function ChatLog({
12
12
  messages,
13
13
  rateMessage,
14
- onCopyMessage,
15
14
  responsePending = false,
16
15
  }: {
17
16
  messages: ChatMessage[];
18
17
  rateMessage?: (spanId: string, rating: 'up' | 'down') => Promise<boolean>;
19
- onCopyMessage?: (spanId: string) => void;
20
18
  responsePending?: boolean;
21
19
  }) {
22
20
  const lastMessage = messages.at(-1);
@@ -57,7 +55,6 @@ export default function ChatLog({
57
55
  key={msg.id}
58
56
  spanId={msg.spanId}
59
57
  rateMessage={rateMessage}
60
- onCopyMessage={onCopyMessage}
61
58
  // all "text" responses to the given message
62
59
  messages={messages.flatMap((msg2) =>
63
60
  msg2.role === 'assistant' &&
@@ -12,17 +12,14 @@ export default function MessageFeedbackButtons({
12
12
  spanId,
13
13
  messages,
14
14
  rateMessage,
15
- onCopyMessage,
16
15
  }: {
17
16
  spanId: string;
18
17
  messages: AssistantTextMessage[];
19
18
  rateMessage?: (spanId: string, rating: 'up' | 'down') => Promise<boolean>;
20
- onCopyMessage?: (spanId: string) => void;
21
19
  }) {
22
20
  // Copy response as markdown
23
21
  const [copied, setCopied] = useState(false);
24
22
  const handleCopy = useCallback(() => {
25
- onCopyMessage?.(spanId);
26
23
  const combinedText = messages.map((msg) => msg.content).join('\n\n');
27
24
  navigator.clipboard
28
25
  .writeText(combinedText)
@@ -33,7 +30,7 @@ export default function MessageFeedbackButtons({
33
30
  .catch(() => {
34
31
  setCopied(false);
35
32
  });
36
- }, [messages, spanId, onCopyMessage]);
33
+ }, [messages]);
37
34
 
38
35
  // Provide message rating
39
36
  const [rating, setRating] = useState<'up' | 'down' | null>(null);
@@ -1,5 +1,4 @@
1
1
  import { motion } from 'motion/react';
2
- import z from 'zod';
3
2
 
4
3
  import type { AssistantToolCallMessage } from '../types';
5
4
 
@@ -11,24 +10,25 @@ export default function ToolCall({
11
10
  }: {
12
11
  message: Pick<AssistantToolCallMessage, 'id' | 'toolName' | 'input'>;
13
12
  }) {
14
- // Render docs searches
15
- if (message.toolName.endsWith('search_docs')) {
16
- const parsed = z.object({ query: z.string() }).safeParse(message.input);
17
- if (parsed.success) {
18
- return (
19
- <motion.li
20
- layout="position"
21
- data-message-role="assistant"
22
- className={clsx(styles['chat-message'], styles['tool-use'])}
23
- >
24
- <p>
25
- Fetching docs for <em>{parsed.data.query}</em>
26
- </p>
27
- </motion.li>
28
- );
29
- }
30
- }
13
+ const firstStringArg = message.input
14
+ ? Object.values(message.input).find((v): v is string => typeof v === 'string')
15
+ : undefined;
31
16
 
32
- // No other tool renderers yet
33
- return null;
17
+ return (
18
+ <motion.li
19
+ layout="position"
20
+ data-message-role="assistant"
21
+ className={clsx(styles['chat-message'], styles['tool-use'])}
22
+ >
23
+ <p>
24
+ Calling <code>{message.toolName}</code>
25
+ {firstStringArg && (
26
+ <>
27
+ {' '}
28
+ with <em>{firstStringArg}</em>
29
+ </>
30
+ )}
31
+ </p>
32
+ </motion.li>
33
+ );
34
34
  }
@@ -18,13 +18,13 @@ export type AssistantToolCallMessage = BaseMessage & {
18
18
  toolName: string;
19
19
  input: Record<string, unknown> | undefined;
20
20
  };
21
- export type AssistantDoneMessage = BaseMessage & {
21
+ type AssistantDoneMessage = BaseMessage & {
22
22
  role: 'assistant';
23
23
  respondingTo: string;
24
24
  messageType: 'done';
25
25
  spanId: string;
26
26
  };
27
- export type AssistantErrorMessage = BaseMessage & {
27
+ type AssistantErrorMessage = BaseMessage & {
28
28
  role: 'assistant';
29
29
  respondingTo: string;
30
30
  messageType: 'error';
@@ -4,13 +4,9 @@
4
4
  // This conditional can’t be inlined into PageFrame because it breaks Astro’s static analysis of imports of client islands
5
5
  const AiChat = __STLDOCS_ENABLE_AI_CHAT__ ? (await import('../chat/ui/AiChat')).default : null;
6
6
 
7
- const STAINLESS_PROJECT = __STLDOCS_HAS_API_REFERENCE__
8
- ? (await import('virtual:stl-starlight-virtual-module')).STAINLESS_PROJECT
9
- : undefined;
10
-
11
7
  export default function DocsChatLazy(
12
8
  props: Omit<React.ComponentProps<NonNullable<typeof AiChat>>, 'stainlessProject'>,
13
9
  ) {
14
- if (!AiChat || !STAINLESS_PROJECT) return null;
15
- return <AiChat {...props} stainlessProject={STAINLESS_PROJECT} />;
10
+ if (!AiChat) return null;
11
+ return <AiChat {...props} />;
16
12
  }
@@ -29,9 +29,5 @@ const { hasSidebar } = Astro.locals.starlightRoute;
29
29
 
30
30
  <slot />
31
31
 
32
- {
33
- __STLDOCS_ENABLE_AI_CHAT__ && (
34
- <AiChatIsland client:load currentLanguage={Astro.locals.language} siteTitle={siteTitle} />
35
- )
36
- }
32
+ {__STLDOCS_ENABLE_AI_CHAT__ && <AiChatIsland client:load siteTitle={siteTitle} />}
37
33
  </div>
package/stl-docs/fonts.ts CHANGED
@@ -7,7 +7,7 @@ type AstroFontConfigEntry = Defined<AstroConfig['fonts']>[number];
7
7
 
8
8
  // Apply Omit to each member of the union while preserving union structure
9
9
  type PreloadFilter = { preload?: FontPreloadFilter };
10
- export type StlDocsFontConfigEntry = (AstroFontConfigEntry extends infer T
10
+ type StlDocsFontConfigEntry = (AstroFontConfigEntry extends infer T
11
11
  ? T extends unknown
12
12
  ? Omit<T, 'cssVariable'>
13
13
  : never
@@ -165,19 +165,19 @@ export function flattenFonts(fonts: StlDocsFontConfig | undefined): AstroFontCon
165
165
  fontConfigs.push({
166
166
  ...fonts.primary,
167
167
  cssVariable: '--stl-typography-font' as const,
168
- } as AstroFontConfigEntry);
168
+ });
169
169
  }
170
170
  if (fonts.heading) {
171
171
  fontConfigs.push({
172
172
  ...fonts.heading,
173
173
  cssVariable: '--stl-typography-font-heading' as const,
174
- } as AstroFontConfigEntry);
174
+ });
175
175
  }
176
176
  if (fonts.mono) {
177
177
  fontConfigs.push({
178
178
  ...fonts.mono,
179
179
  cssVariable: '--stl-typography-font-mono' as const,
180
- } as AstroFontConfigEntry);
180
+ });
181
181
  }
182
182
  if (fonts.additional) {
183
183
  fontConfigs.push(...fonts.additional.map((font) => font as AstroFontConfigEntry));
package/stl-docs/index.ts CHANGED
@@ -7,6 +7,7 @@ import type { AstroIntegration } from 'astro';
7
7
 
8
8
  import { normalizeRedirects, type NormalizedRedirectConfig } from './redirects';
9
9
  import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
10
11
  import { mkdirSync, writeFileSync } from 'fs';
11
12
  import {
12
13
  parseStlDocsConfig,
@@ -17,7 +18,6 @@ import {
17
18
  type StarlightSidebarConfig,
18
19
  } from './loadStlDocsConfig';
19
20
  import { buildVirtualModuleString } from '../shared/virtualModule';
20
- import type * as StlDocsVirtualModule from 'virtual:stl-docs-virtual-module';
21
21
  import { resolveSrcFile } from '../resolveSrcFile';
22
22
  import { stainlessDocsMarkdownRenderer } from './proseMarkdown/proseMarkdownIntegration';
23
23
  import { getSharedLogger, setSharedLogger } from '../shared/getSharedLogger';
@@ -184,24 +184,39 @@ function stainlessDocsIntegration(
184
184
  const withBase = (link: string) =>
185
185
  /^([a-z][a-z0-9+.-]*:|\/\/)/.test(link) ? link : path.posix.join(base, link);
186
186
 
187
+ let vmAiChatHandlerExport = 'export const AI_CHAT_HANDLER = undefined;';
188
+ if (config.aiChat?.handlerEntrypoint) {
189
+ const rawEntrypoint = config.aiChat.handlerEntrypoint;
190
+ const handlerEntrypoint = rawEntrypoint.startsWith('file://')
191
+ ? fileURLToPath(rawEntrypoint)
192
+ : path.isAbsolute(rawEntrypoint)
193
+ ? rawEntrypoint
194
+ : path.resolve(fileURLToPath(astroConfig.root), rawEntrypoint);
195
+ vmAiChatHandlerExport = `export { default as AI_CHAT_HANDLER } from '${handlerEntrypoint}';`;
196
+ }
197
+
187
198
  const virtualModules = new Map(
188
199
  Object.entries({
189
- 'virtual:stl-docs-virtual-module': buildVirtualModuleString({
190
- TABS: config.tabs.map((tab) => ({ ...tab, link: withBase(tab.link) })),
191
- SPLIT_TABS_ENABLED: config.splitTabsEnabled,
192
- HEADER_LINKS: config.header.links.map((link) => ({ ...link, link: withBase(link.link) })),
193
- HEADER_LAYOUT: config.header.layout,
194
- ENABLE_CLIENT_ROUTER: config.enableClientRouter,
195
- API_REFERENCE_BASE_PATH: apiReferenceBasePath ?? '/api',
196
- ENABLE_PROSE_MARKDOWN_RENDERING: config.enableProseMarkdownRendering,
197
- ENABLE_CONTEXT_MENU: config.contextMenu, // TODO: do not duplicate this between both virtual modules
198
- RENDER_PAGE_DESCRIPTIONS: config.renderPageDescriptions,
199
- FONTS: getFontRoles(config.fonts),
200
- LINK_GROUP_TITLES_TO_OVERVIEW_PAGES: config.linkGroupTitlesToOverviewPages,
201
- RENDER_CREDITS: config.credits,
202
- SITE_TITLE: config.siteTitle,
203
- ENABLE_AI_CHAT: !!config.aiChat,
204
- } satisfies typeof StlDocsVirtualModule),
200
+ 'virtual:stl-docs-virtual-module': [
201
+ buildVirtualModuleString({
202
+ TABS: config.tabs.map((tab) => ({ ...tab, link: withBase(tab.link) })),
203
+ SPLIT_TABS_ENABLED: config.splitTabsEnabled,
204
+ HEADER_LINKS: config.header.links.map((link) => ({ ...link, link: withBase(link.link) })),
205
+ HEADER_LAYOUT: config.header.layout,
206
+ ENABLE_CLIENT_ROUTER: config.enableClientRouter,
207
+ API_REFERENCE_BASE_PATH: apiReferenceBasePath ?? '/api',
208
+ ENABLE_PROSE_MARKDOWN_RENDERING: config.enableProseMarkdownRendering,
209
+ ENABLE_CONTEXT_MENU: !!config.contextMenu, // TODO: do not duplicate this between both virtual modules
210
+ CONTEXT_MENU_ENABLE_THIRD_PARTY:
211
+ (typeof config.contextMenu === 'object' ? config.contextMenu.thirdParty : null) ?? true,
212
+ RENDER_PAGE_DESCRIPTIONS: config.renderPageDescriptions,
213
+ FONTS: getFontRoles(config.fonts),
214
+ LINK_GROUP_TITLES_TO_OVERVIEW_PAGES: config.linkGroupTitlesToOverviewPages,
215
+ RENDER_CREDITS: config.credits,
216
+ SITE_TITLE: config.siteTitle,
217
+ }),
218
+ ].join('\n'),
219
+ 'virtual:stl-docs-ai-chat': vmAiChatHandlerExport,
205
220
  }),
206
221
  );
207
222
 
@@ -216,7 +231,7 @@ function stainlessDocsIntegration(
216
231
  {
217
232
  name: 'stl-docs-virtual-modules',
218
233
  resolveId(id) {
219
- // The '\0' prefix tells Vite this is a virtual module and prevents it from being resolved again.
234
+ // The '\0' prefix tells Vite "this is a virtual module" and prevents it from being resolved again.
220
235
  if (virtualModules.has(id)) return `\0${id}`;
221
236
  },
222
237
  load(id) {
@@ -231,8 +246,7 @@ function stainlessDocsIntegration(
231
246
  await generateExamplesPlugin({
232
247
  projectName: config.apiReference?.stainlessProject ?? undefined,
233
248
  logger,
234
- exampleOverrides:
235
- typeof config.aiChat === 'object' ? config.aiChat.exampleOverrides : undefined,
249
+ exampleOverrides: config.aiChat.examples,
236
250
  }),
237
251
  ]
238
252
  : []),
@@ -81,7 +81,7 @@ export type StainlessDocsUserConfig = {
81
81
  */
82
82
  disableProseMarkdownRendering?: boolean;
83
83
  disableStainlessProseIndexing?: boolean;
84
- aiChat?: { exampleOverrides?: ExamplePromptResponse } | true;
84
+ aiChat?: { handlerEntrypoint: string; examples?: ExamplePromptResponse };
85
85
  /**
86
86
  * Whether to link group titles to overview pages. Note: overview pages must already be present in the sidebar for this to work.
87
87
  *
@@ -94,7 +94,7 @@ export type StainlessDocsUserConfig = {
94
94
  *
95
95
  * @default true
96
96
  */
97
- contextMenu?: boolean;
97
+ contextMenu?: boolean | { thirdParty?: boolean };
98
98
 
99
99
  /**
100
100
  * Whether to render page descriptions in prose page headers
@@ -105,7 +105,7 @@ export type StainlessDocsUserConfig = {
105
105
  /**
106
106
  * Enable and configure Open Graph image generation.
107
107
  *
108
- * Requires `@takumi-rs/image-response` to be installed as a dependency.
108
+ * Requires `takumi-js` to be installed as a dependency.
109
109
  */
110
110
  ogImage?: OGImageConfig;
111
111
  } & PassThroughStarlightConfigOptions;
@@ -2,7 +2,7 @@ import { darkThemeVars, lightThemeVars, typography } from '../theme';
2
2
  import { resolveLocalImageFile } from '../image-gen/get-logo-url';
3
3
  import { OG_IMAGE_OPTIONS } from 'virtual:stainless-docs/docs-og-image';
4
4
 
5
- /* The default open graph image template. It is expected to be used with @takumi-rs/image-response */
5
+ /* The default open graph image template. It is expected to be used with takumi-js */
6
6
  export default function OpenGraphImage({
7
7
  title,
8
8
  description,
@@ -1,6 +1,6 @@
1
1
  // Exported utilites for @stainless-api/docs consumers
2
2
 
3
- import type { ImageResponseOptions } from '@takumi-rs/image-response';
3
+ import type { ImageResponseOptions } from 'takumi-js/response';
4
4
  import type { CSSProperties } from 'react';
5
5
 
6
6
  export type OGImageConfig = {
@@ -1,4 +1,4 @@
1
- import { ImageResponse } from '@takumi-rs/image-response';
1
+ import { ImageResponse } from 'takumi-js/response';
2
2
  import { generateDocsRoutes } from '@stainless-api/docs/generate-docs-routes';
3
3
  import { DocsLanguage, parseStainlessPath } from '@stainless-api/docs-ui/routing';
4
4
  import { getResourceFromSpec } from '@stainless-api/docs-ui/utils';
@@ -1,6 +1,6 @@
1
1
  import { getCollection } from 'astro:content';
2
2
  import { z } from 'astro/zod';
3
- import { ImageResponse } from '@takumi-rs/image-response';
3
+ import { ImageResponse } from 'takumi-js/response';
4
4
  import { Tabs } from '@stainless-api/docs/docs-config';
5
5
  import OpenGraphImage from 'virtual:stainless-docs/docs-og-image/components/OpenGraphImage';
6
6
  import { OG_IMAGE_OPTIONS } from 'virtual:stainless-docs/docs-og-image';
@@ -19,7 +19,7 @@ const stainlessComponentDefaults = {
19
19
 
20
20
  function checkTakumiInstalled(): boolean {
21
21
  try {
22
- import.meta.resolve('@takumi-rs/image-response');
22
+ import.meta.resolve('takumi-js/response');
23
23
  return true;
24
24
  } catch {
25
25
  return false;
@@ -40,8 +40,8 @@ export function ogImageStarlightPlugin(
40
40
 
41
41
  if (!checkTakumiInstalled()) {
42
42
  logger.error(
43
- 'The "@takumi-rs/image-response" package is required to use OG image generation. ' +
44
- 'Please install it: npm install @takumi-rs/image-response',
43
+ 'The "takumi-js" package is required to use OG image generation. ' +
44
+ 'Please install it: npm install takumi-js',
45
45
  );
46
46
  process.exit(1);
47
47
  }
@@ -1,4 +1,4 @@
1
- import { ImageResponseOptions } from '@takumi-rs/image-response';
1
+ import { ImageResponseOptions } from 'takumi-js/response';
2
2
  import { OG_IMAGE_OPTIONS } from 'virtual:stainless-docs/docs-og-image';
3
3
 
4
4
  const defaultRenderOptions: ImageResponseOptions & { width: number; height: number } = {
@@ -19,6 +19,7 @@ declare module 'virtual:stl-starlight-virtual-module' {
19
19
  export const PROPERTY_SETTINGS: PropertySettingsType;
20
20
  export const MIDDLEWARE: StlStarlightMiddleware;
21
21
  export const ENABLE_CONTEXT_MENU: boolean;
22
+ export const CONTEXT_MENU_ENABLE_THIRD_PARTY: boolean;
22
23
  export const STAINLESS_PROJECT: string | undefined;
23
24
  export const LLMS_TXT_DESCRIPTION: string | null;
24
25
  export const LLMS_TXT_DETAIL_THRESHOLD: number;
@@ -53,6 +54,7 @@ declare module 'virtual:stl-docs-virtual-module' {
53
54
  export const API_REFERENCE_BASE_PATH: string;
54
55
  export const ENABLE_PROSE_MARKDOWN_RENDERING: boolean;
55
56
  export const ENABLE_CONTEXT_MENU: boolean;
57
+ export const CONTEXT_MENU_ENABLE_THIRD_PARTY: boolean;
56
58
  export const RENDER_PAGE_DESCRIPTIONS: boolean;
57
59
  export const LINK_GROUP_TITLES_TO_OVERVIEW_PAGES: boolean;
58
60
  export const FONTS: {
@@ -63,7 +65,10 @@ declare module 'virtual:stl-docs-virtual-module' {
63
65
  };
64
66
  export const RENDER_CREDITS: boolean;
65
67
  export const SITE_TITLE: string;
66
- export const ENABLE_AI_CHAT: boolean;
68
+ }
69
+
70
+ declare module 'virtual:stl-docs-ai-chat' {
71
+ export const AI_CHAT_HANDLER: import('./stl-docs/chat/docs-chat-handler').DocsChatHandler | undefined;
67
72
  }
68
73
 
69
74
  declare module 'virtual:stl-docs-ai-chat-examples' {