@stainless-api/docs 0.1.0-beta.99 → 1.0.0-beta.140
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 +390 -0
- package/ambient.d.ts +6 -0
- package/eslint-suppressions.json +22 -6
- package/{eslint.config.js → eslint.config.ts} +1 -7
- package/package.json +57 -40
- package/plugin/buildAlgoliaIndex.ts +6 -12
- package/plugin/components/SDKSelect.astro +0 -6
- package/plugin/components/SnippetCode.tsx +6 -37
- package/plugin/components/search/SearchAlgolia.astro +1 -1
- package/plugin/components/search/SearchIsland.tsx +19 -13
- package/plugin/generateAPIReferenceLink.ts +0 -40
- package/plugin/globalJs/ai-dropdown-options.ts +22 -9
- package/plugin/globalJs/code-snippets.ts +5 -5
- package/plugin/globalJs/copy.ts +20 -91
- package/plugin/globalJs/navigation.ts +13 -13
- package/plugin/globalJs/summary-selection-tweak.ts +29 -0
- package/plugin/index.ts +107 -163
- package/plugin/loadPluginConfig.ts +49 -151
- package/plugin/markdown/highlighter.ts +100 -0
- package/plugin/markdown/index.ts +39 -0
- package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +2 -0
- package/plugin/react/Routing.tsx +10 -244
- package/plugin/referencePlaceholderUtils.ts +1 -1
- package/plugin/replaceSidebarPlaceholderMiddleware.ts +1 -1
- package/plugin/routes/Docs.astro +3 -1
- package/plugin/routes/Overview.astro +14 -7
- package/plugin/routes/llms.ts +186 -0
- package/plugin/routes/markdown.ts +62 -13
- package/plugin/sidebar-utils/sidebar-builder.ts +38 -12
- package/plugin/specs/defaultSpecLoader.ts +192 -0
- package/plugin/specs/fetchSpecSSR.ts +1 -1
- package/plugin/specs/utils.ts +86 -0
- package/shared/conditionalIntegration.ts +28 -0
- package/shared/getProsePages.ts +6 -7
- package/shared/virtualModule.ts +1 -26
- package/stl-docs/aiChatExamples.ts +31 -0
- package/stl-docs/chat/docs-chat-handler.ts +17 -0
- package/stl-docs/chat/hook.ts +225 -0
- package/stl-docs/chat/schemas.ts +27 -0
- package/stl-docs/chat/ui/AiChat.module.css +591 -0
- package/stl-docs/chat/ui/AiChat.tsx +175 -0
- package/stl-docs/chat/ui/Trigger.tsx +154 -0
- package/stl-docs/chat/ui/components/ChatControls.tsx +51 -0
- package/stl-docs/chat/ui/components/ChatEmpty.tsx +42 -0
- package/stl-docs/chat/ui/components/ChatLog.tsx +93 -0
- package/stl-docs/chat/ui/components/ChatMessage.tsx +47 -0
- package/stl-docs/chat/ui/components/CodeBlock.tsx +33 -0
- package/stl-docs/chat/ui/components/MessageFeedback.tsx +106 -0
- package/stl-docs/chat/ui/components/Table.tsx +15 -0
- package/stl-docs/chat/ui/components/ToolCall.tsx +34 -0
- package/stl-docs/chat/ui/components/hljs-github.css +81 -0
- package/stl-docs/chat/ui/scroll-manager.ts +86 -0
- package/stl-docs/chat/ui/types.ts +45 -0
- package/stl-docs/components/AiChatIsland.tsx +10 -12
- package/stl-docs/components/ContentPanel.astro +9 -0
- package/stl-docs/components/Footer.astro +89 -0
- package/stl-docs/components/Header.astro +0 -5
- package/stl-docs/components/PageFrame.astro +23 -8
- package/stl-docs/components/PageSidebar.astro +11 -0
- package/stl-docs/components/StainlessLogo.svg +4 -0
- package/stl-docs/components/TwoColumnContent.astro +2 -0
- package/stl-docs/components/headers/DefaultHeader.astro +6 -8
- package/stl-docs/components/headers/StackedHeader.astro +5 -53
- package/stl-docs/components/mintlify-compat/Accordion.astro +2 -2
- package/stl-docs/components/mintlify-compat/AccordionGroup.astro +0 -4
- package/stl-docs/components/mintlify-compat/Columns.astro +2 -2
- package/stl-docs/components/mintlify-compat/Frame.astro +2 -2
- package/stl-docs/components/mintlify-compat/Tab.astro +2 -2
- package/stl-docs/components/mintlify-compat/callouts/Callout.astro +2 -2
- package/stl-docs/components/mintlify-compat/callouts/Check.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Danger.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Info.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Note.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Tip.astro +0 -4
- package/stl-docs/components/mintlify-compat/callouts/Warning.astro +0 -4
- package/stl-docs/components/nav-tabs/NavDropdown.astro +12 -7
- package/stl-docs/components/nav-tabs/NavTabs.astro +5 -3
- package/stl-docs/components/nav-tabs/buildNavLinks.ts +2 -0
- package/stl-docs/components/pagination/Pagination.astro +4 -2
- package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +2 -2
- package/stl-docs/components/pagination/PaginationLinkQuiet.astro +2 -2
- package/stl-docs/components/pagination/util.ts +3 -3
- package/stl-docs/components/sidebars/BaseSidebar.astro +72 -1
- package/stl-docs/disableCalloutSyntax.ts +1 -1
- package/stl-docs/fonts.ts +5 -5
- package/stl-docs/index.ts +76 -53
- package/stl-docs/loadStlDocsConfig.ts +38 -8
- package/stl-docs/og-image/components/OpenGraphFunctionSignature.tsx +64 -0
- package/stl-docs/og-image/components/OpenGraphImage.tsx +126 -0
- package/stl-docs/og-image/config.ts +56 -0
- package/stl-docs/og-image/image-gen/generate-api-reference-og-image.tsx +188 -0
- package/stl-docs/og-image/image-gen/generate-og-image.tsx +119 -0
- package/stl-docs/og-image/image-gen/get-logo-url.ts +47 -0
- package/stl-docs/og-image/index.ts +135 -0
- package/stl-docs/og-image/routes/add-og-image.ts +45 -0
- package/stl-docs/og-image/routes/get-api-reference-og-image.ts +36 -0
- package/stl-docs/og-image/routes/get-og-image.ts +28 -0
- package/stl-docs/og-image/theme.ts +43 -0
- package/stl-docs/og-image/utils.ts +14 -0
- package/stl-docs/proseDocSync.test.ts +74 -0
- package/stl-docs/proseDocSync.ts +344 -0
- package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +4 -12
- package/stl-docs/schema-extension.ts +12 -0
- package/stl-docs/tabsMiddleware.ts +1 -1
- package/styles/overrides.css +2 -14
- package/styles/page.css +210 -71
- package/styles/sidebar.css +30 -17
- package/styles/sl-variables.css +3 -8
- package/styles/stldocs-variables.css +2 -2
- package/styles/toc.css +8 -0
- package/tsconfig.json +1 -1
- package/virtual-module.d.ts +35 -11
- package/playground-virtual-modules.d.ts +0 -96
- package/plugin/globalJs/create-playground.shim.ts +0 -3
- package/plugin/globalJs/playground-data.shim.ts +0 -1
- package/plugin/globalJs/playground-data.ts +0 -14
- package/plugin/specs/FileCache.ts +0 -99
- package/plugin/specs/generateSpec.ts +0 -112
- package/plugin/specs/index.ts +0 -132
- package/plugin/specs/inputResolver.ts +0 -146
- package/plugin/specs/worker.ts +0 -199
- package/plugin/vendor/preview.worker.docs.js +0 -26108
- package/plugin/vendor/templates/cli.md +0 -1
- package/plugin/vendor/templates/go.md +0 -316
- package/plugin/vendor/templates/java.md +0 -89
- package/plugin/vendor/templates/kotlin.md +0 -89
- package/plugin/vendor/templates/node.md +0 -235
- package/plugin/vendor/templates/python.md +0 -251
- package/plugin/vendor/templates/ruby.md +0 -147
- package/plugin/vendor/templates/terraform.md +0 -60
- package/plugin/vendor/templates/typescript.md +0 -319
- package/scripts/vendor_deps.ts +0 -50
- package/stl-docs/components/ClientRouterHead.astro +0 -41
- package/stl-docs/components/content-panel/ContentPanel.astro +0 -42
- package/stl-docs/components/headers/SplashMobileMenuToggle.astro +0 -65
- package/stl-docs/proseSearchIndexing.ts +0 -606
|
@@ -8,7 +8,6 @@ import style from '@stainless-api/docs-ui/style';
|
|
|
8
8
|
import * as cheerio from 'cheerio/slim';
|
|
9
9
|
import {
|
|
10
10
|
EXPERIMENTAL_COLLAPSIBLE_SNIPPETS,
|
|
11
|
-
EXPERIMENTAL_PLAYGROUNDS,
|
|
12
11
|
EXPERIMENTAL_REQUEST_BUILDER,
|
|
13
12
|
} from 'virtual:stl-starlight-virtual-module';
|
|
14
13
|
import clsx from 'clsx';
|
|
@@ -18,26 +17,6 @@ import React from 'react';
|
|
|
18
17
|
import { RequestBuilder } from './RequestBuilder';
|
|
19
18
|
import { Method } from '@stainless/sdk-json';
|
|
20
19
|
|
|
21
|
-
function PlaygroundIcon() {
|
|
22
|
-
return (
|
|
23
|
-
<svg
|
|
24
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
25
|
-
width={16}
|
|
26
|
-
height={16}
|
|
27
|
-
viewBox="0 0 24 24"
|
|
28
|
-
fill="none"
|
|
29
|
-
stroke="currentColor"
|
|
30
|
-
strokeWidth={2}
|
|
31
|
-
strokeLinecap="round"
|
|
32
|
-
strokeLinejoin="round"
|
|
33
|
-
className={'lucide ' + style.Icon}
|
|
34
|
-
aria-hidden="true"
|
|
35
|
-
>
|
|
36
|
-
<path d="m 1,2 h 1 a 4,4 0 0 1 4,4 v 1 m 5,15 H 10 A 4,4 0 0 1 6,18 V 6 a 4,4 0 0 1 4,-4 h 1 M 1,22 H 2 A 4,4 0 0 0 6,18 V 17 M 14.029059,8.147837 A 1.2853426,1.2853426 0 0 1 15.978924,7.0437277 L 22.40178,10.8959 a 1.2853426,1.2853426 0 0 1 0,2.208219 l -6.422856,3.852172 a 1.2853426,1.2853426 0 0 1 -1.949865,-1.105395 z" />
|
|
37
|
-
</svg>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
20
|
/*
|
|
42
21
|
* This may be replaced by additional data from the sdk.
|
|
43
22
|
* Without information from the sdk, we use simple heuristics per language.
|
|
@@ -49,7 +28,7 @@ function getCollapsedRanges(content: string, language: string, signature?: strin
|
|
|
49
28
|
const sigIdx = raw.findIndex((l) => l.includes(signature));
|
|
50
29
|
if (sigIdx < 0) return [];
|
|
51
30
|
|
|
52
|
-
let finalIndex
|
|
31
|
+
let finalIndex;
|
|
53
32
|
if (language === 'kotlin' || language === 'java') {
|
|
54
33
|
finalIndex = raw.findIndex((l, i) => i > sigIdx && l.trim() === '}');
|
|
55
34
|
} else if (language === 'python') {
|
|
@@ -140,7 +119,7 @@ function condensedShikiHtmlFull(docHtml: string, language: string, ranges: [numb
|
|
|
140
119
|
out.push('<span class="line ellipsis">…\n</span>');
|
|
141
120
|
didPushEllipsis = true;
|
|
142
121
|
}
|
|
143
|
-
out.push($.html(lineEl)
|
|
122
|
+
out.push($.html(lineEl));
|
|
144
123
|
});
|
|
145
124
|
|
|
146
125
|
$code.html(out.join(''));
|
|
@@ -172,21 +151,11 @@ export function SnippetContainer({ children, signature, method }: SnippetContain
|
|
|
172
151
|
);
|
|
173
152
|
}
|
|
174
153
|
|
|
175
|
-
export function SnippetButtons(
|
|
176
|
-
void content;
|
|
177
|
-
const language = useLanguage();
|
|
154
|
+
export function SnippetButtons() {
|
|
178
155
|
return (
|
|
179
|
-
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
</Button>
|
|
183
|
-
{EXPERIMENTAL_PLAYGROUNDS &&
|
|
184
|
-
(language === 'python' || language === 'typescript' || language === 'http') && (
|
|
185
|
-
<Button data-stldocs-snippet-play variant="muted" border title="Play">
|
|
186
|
-
<PlaygroundIcon />
|
|
187
|
-
</Button>
|
|
188
|
-
)}
|
|
189
|
-
</>
|
|
156
|
+
<Button variant="outline" data-stldocs-snippet-copy>
|
|
157
|
+
<CopyIcon size={16} className={style.Icon} />
|
|
158
|
+
</Button>
|
|
190
159
|
);
|
|
191
160
|
}
|
|
192
161
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
2
|
import { RESOLVED_API_REFERENCE_PATH, HIGHLIGHT_THEMES } from 'virtual:stl-starlight-virtual-module';
|
|
3
3
|
import { parseRoute, generateRoute } from '@stainless-api/docs-ui/routing';
|
|
4
4
|
import { SearchModal } from '@stainless-api/docs-search';
|
|
@@ -8,27 +8,28 @@ import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki';
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
DocsProvider,
|
|
11
|
-
type
|
|
12
|
-
MarkdownProvider,
|
|
11
|
+
type MarkdownContextValue,
|
|
13
12
|
NavigationProvider,
|
|
13
|
+
SuspensefulMarkdownProvider,
|
|
14
14
|
} from '@stainless-api/docs-ui/contexts';
|
|
15
15
|
import { ComponentProvider } from '@stainless-api/docs-ui/contexts/component';
|
|
16
16
|
import { SearchProvider } from '@stainless-api/docs-search/context';
|
|
17
17
|
import type { SearchSettings } from '@stainless-api/docs-search/types';
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
declare global {
|
|
20
|
+
var __docsSearchShikiSingleton: Promise<HighlighterGeneric<BundledLanguage, BundledTheme>> | undefined;
|
|
21
|
+
}
|
|
22
|
+
function getHighlighter() {
|
|
23
|
+
if (!globalThis.__docsSearchShikiSingleton) {
|
|
24
|
+
globalThis.__docsSearchShikiSingleton = createHighlighter({
|
|
23
25
|
themes: ['github-dark'],
|
|
24
26
|
langs: ['typescript', 'python', 'go', 'java', 'kotlin', 'ruby'],
|
|
25
27
|
});
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
return $$highlighter;
|
|
29
|
+
return globalThis.__docsSearchShikiSingleton;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
async function createMarkdownRenderer(): Promise<
|
|
32
|
+
async function createMarkdownRenderer(): Promise<MarkdownContextValue> {
|
|
32
33
|
const highlighter = await getHighlighter();
|
|
33
34
|
const markdocConfig: Markdoc.Config = {
|
|
34
35
|
nodes: {
|
|
@@ -37,8 +38,12 @@ async function createMarkdownRenderer(): Promise<MarkdownContext> {
|
|
|
37
38
|
...Markdoc.nodes.fence,
|
|
38
39
|
transform(node, config) {
|
|
39
40
|
const attributes = node.transformAttributes(config);
|
|
41
|
+
if (typeof node.attributes.language !== 'string' || typeof node.attributes.content !== 'string') {
|
|
42
|
+
throw new Error('Expected language and content to be strings');
|
|
43
|
+
}
|
|
40
44
|
const lang = node.attributes.language === 'node' ? 'typescript' : node.attributes.language;
|
|
41
45
|
const code = highlighter.codeToTokens(node.attributes.content, {
|
|
46
|
+
// @ts-expect-error - TODO: narrowing
|
|
42
47
|
lang,
|
|
43
48
|
theme: 'github-dark',
|
|
44
49
|
});
|
|
@@ -63,6 +68,7 @@ async function createMarkdownRenderer(): Promise<MarkdownContext> {
|
|
|
63
68
|
transform(node, config) {
|
|
64
69
|
const children = node.transformChildren(config);
|
|
65
70
|
const attrs = node.transformAttributes(config);
|
|
71
|
+
if (typeof attrs['href'] !== 'string') throw new Error('Expected href to be a string');
|
|
66
72
|
const href = attrs['href'].replace('docs://BASE_PATH', RESOLVED_API_REFERENCE_PATH);
|
|
67
73
|
return new Markdoc.Tag(this.render, { ...attrs, href }, children);
|
|
68
74
|
},
|
|
@@ -86,7 +92,7 @@ async function createMarkdownRenderer(): Promise<MarkdownContext> {
|
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
export function DocsSearch({ settings, currentPath }: { settings: SearchSettings; currentPath: string }) {
|
|
89
|
-
const
|
|
95
|
+
const rendererPromise = useMemo(() => createMarkdownRenderer(), []);
|
|
90
96
|
const { stainlessPath, language } = parseRoute(RESOLVED_API_REFERENCE_PATH, currentPath);
|
|
91
97
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
92
98
|
const pageFind = import.meta.env.DEV
|
|
@@ -104,13 +110,13 @@ export function DocsSearch({ settings, currentPath }: { settings: SearchSettings
|
|
|
104
110
|
<DocsProvider spec={null} language={language}>
|
|
105
111
|
<ComponentProvider>
|
|
106
112
|
<NavigationProvider basePath="/" selectedPath={stainlessPath}>
|
|
107
|
-
<
|
|
113
|
+
<SuspensefulMarkdownProvider value={rendererPromise}>
|
|
108
114
|
<SearchProvider onSelect={handleSelect} pageFind={pageFind} settings={settings}>
|
|
109
115
|
<div className="stldocs-root">
|
|
110
116
|
<SearchModal id="stldocs-search" />
|
|
111
117
|
</div>
|
|
112
118
|
</SearchProvider>
|
|
113
|
-
</
|
|
119
|
+
</SuspensefulMarkdownProvider>
|
|
114
120
|
</NavigationProvider>
|
|
115
121
|
</ComponentProvider>
|
|
116
122
|
</DocsProvider>
|
|
@@ -1,47 +1,7 @@
|
|
|
1
1
|
// This is probably temporary, but it fills in functionality needed for Mintlify imports
|
|
2
2
|
|
|
3
|
-
import type { StarlightRouteData } from '@astrojs/starlight/route-data';
|
|
4
|
-
import type * as SDKJSON from '@stainless/sdk-json';
|
|
5
|
-
import { walkTree } from '@stainless-api/docs-ui/routing';
|
|
6
|
-
|
|
7
3
|
const INTERNAL_REFERENCE_ENTRY_MARKER = 'STL_STARLIGHT_API_REFERENCE_METHOD_LINK_PLACEHOLDER';
|
|
8
4
|
|
|
9
|
-
type SidebarEntry = StarlightRouteData['sidebar'][number];
|
|
10
|
-
|
|
11
|
-
type SidebarLink = Extract<SidebarEntry, { href: string }>;
|
|
12
|
-
|
|
13
|
-
export function getMethodFromSDKJSON(spec: SDKJSON.Spec, endpoint: string) {
|
|
14
|
-
for (const entry of walkTree(spec)) {
|
|
15
|
-
if (entry.data.kind === 'http_method' && entry.data.endpoint === endpoint) {
|
|
16
|
-
return entry.data;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
throw new Error(`Endpoint ${endpoint} not found in API`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function recursiveReplacePlaceholderItems(
|
|
23
|
-
sidebar: SidebarEntry[],
|
|
24
|
-
modifyFn: (entry: SidebarLink, props: { endpoint: string; label?: string }) => void,
|
|
25
|
-
) {
|
|
26
|
-
for (const entry of sidebar) {
|
|
27
|
-
const endpoint = 'attrs' in entry && entry.attrs?.['data-stldocs-endpoint'];
|
|
28
|
-
if (
|
|
29
|
-
'attrs' in entry &&
|
|
30
|
-
entry.attrs?.about === INTERNAL_REFERENCE_ENTRY_MARKER &&
|
|
31
|
-
endpoint &&
|
|
32
|
-
typeof endpoint === 'string'
|
|
33
|
-
) {
|
|
34
|
-
modifyFn(entry, {
|
|
35
|
-
endpoint,
|
|
36
|
-
label: entry.attrs?.['data-stldocs-label'],
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
if ('entries' in entry) {
|
|
40
|
-
recursiveReplacePlaceholderItems(entry.entries, modifyFn);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
5
|
type GenerateProps = string | { label?: string; endpoint: string };
|
|
46
6
|
|
|
47
7
|
function normalizeGenerateProps(generateProps: GenerateProps) {
|
|
@@ -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
|
[
|
|
@@ -142,7 +147,9 @@ const aiDropdownOptions: DropdownOption[][] = [
|
|
|
142
147
|
.then((response) => response.text())
|
|
143
148
|
.then((text) => new Blob([text], { type: 'text/plain' })),
|
|
144
149
|
});
|
|
145
|
-
navigator.clipboard.write([text])
|
|
150
|
+
navigator.clipboard.write([text]).catch(() => {
|
|
151
|
+
console.error('Failed to copy to clipboard using ClipboardItem');
|
|
152
|
+
});
|
|
146
153
|
} else {
|
|
147
154
|
// NOTE: Firefox has support for ClipboardItem and navigator.clipboard.write,
|
|
148
155
|
// but those are behind `dom.events.asyncClipboard.clipboardItem` preference.
|
|
@@ -150,7 +157,10 @@ const aiDropdownOptions: DropdownOption[][] = [
|
|
|
150
157
|
// Clipboard API being used async in a Promise.
|
|
151
158
|
fetch(markdownUrl)
|
|
152
159
|
.then((response) => response.text())
|
|
153
|
-
.then((text) => navigator.clipboard.writeText(text))
|
|
160
|
+
.then((text) => navigator.clipboard.writeText(text))
|
|
161
|
+
.catch(() => {
|
|
162
|
+
console.error('Failed to copy to clipboard');
|
|
163
|
+
});
|
|
154
164
|
}
|
|
155
165
|
},
|
|
156
166
|
icon: 'copy',
|
|
@@ -179,13 +189,16 @@ const aiDropdownOptions: DropdownOption[][] = [
|
|
|
179
189
|
// },
|
|
180
190
|
|
|
181
191
|
export function getAIDropdownOptions() {
|
|
182
|
-
const renderedOptions = aiDropdownOptions
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
+
});
|
|
189
202
|
|
|
190
203
|
const allOptions = renderedOptions.flatMap((group) => group.options);
|
|
191
204
|
const primaryAction = allOptions.find((o) => o.primaryAction) ?? allOptions[0]!;
|
|
@@ -13,7 +13,7 @@ document.addEventListener(getPageLoadEvent(), () => {
|
|
|
13
13
|
const panelId = (tab as HTMLButtonElement).dataset.snippetResponseTabId;
|
|
14
14
|
|
|
15
15
|
if (!panelId) {
|
|
16
|
-
console.error(`No panel found for tab: ${tab}`);
|
|
16
|
+
console.error(`No panel found for tab: ${tab.outerHTML}`);
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
document
|
|
@@ -29,8 +29,8 @@ document.addEventListener(getPageLoadEvent(), () => {
|
|
|
29
29
|
document.getElementById('stl-snippet-expand-button')?.addEventListener('click', (e) => {
|
|
30
30
|
const btn = e.currentTarget as HTMLButtonElement;
|
|
31
31
|
|
|
32
|
-
const collapsedDiv = document.querySelector('.stl-snippet-code-is-collapsed')
|
|
33
|
-
const expandedDiv = document.querySelector('.stl-snippet-code-is-expanded')
|
|
32
|
+
const collapsedDiv = document.querySelector('.stl-snippet-code-is-collapsed');
|
|
33
|
+
const expandedDiv = document.querySelector('.stl-snippet-code-is-expanded');
|
|
34
34
|
|
|
35
35
|
// We need to use javascript for height animations due to having dynamic heights based on snippet size.
|
|
36
36
|
const animateHeight = (el: HTMLElement, expand: boolean) => {
|
|
@@ -71,7 +71,7 @@ document.addEventListener(getPageLoadEvent(), () => {
|
|
|
71
71
|
el.style.height = `${endHeight}px`;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
if (collapsedDiv) {
|
|
74
|
+
if (collapsedDiv instanceof HTMLElement) {
|
|
75
75
|
if (collapsedDiv) {
|
|
76
76
|
collapsedDiv.querySelector('.shiki')?.setAttribute('style', `counter-reset: codeblock-line 0`);
|
|
77
77
|
}
|
|
@@ -80,7 +80,7 @@ document.addEventListener(getPageLoadEvent(), () => {
|
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
if (expandedDiv) {
|
|
83
|
+
if (expandedDiv instanceof HTMLElement) {
|
|
84
84
|
const dataSnippetExpandedOffset = expandedDiv?.dataset.snippetExpandedOffset;
|
|
85
85
|
if (dataSnippetExpandedOffset) {
|
|
86
86
|
const offset = parseInt(dataSnippetExpandedOffset, 10);
|
package/plugin/globalJs/copy.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RESOLVED_API_REFERENCE_PATH,
|
|
3
|
-
EXPERIMENTAL_COLLAPSIBLE_SNIPPETS,
|
|
4
|
-
} from 'virtual:stl-starlight-virtual-module';
|
|
5
|
-
import { getPageLoadEvent } from '../helpers/getPageLoadEvent';
|
|
6
|
-
import { updateSelectedLanguage } from '../languages';
|
|
7
|
-
import { navigate } from 'astro/virtual-modules/transitions-router.js';
|
|
1
|
+
import { EXPERIMENTAL_COLLAPSIBLE_SNIPPETS } from 'virtual:stl-starlight-virtual-module';
|
|
8
2
|
const copyIcon = `<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>`;
|
|
9
3
|
const circleAlertIcon = `<circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/>`;
|
|
10
4
|
const checkIcon = `<path d="M20 6 9 17l-5-5"/>`;
|
|
@@ -22,94 +16,29 @@ function getContent(button: HTMLElement, full: boolean) {
|
|
|
22
16
|
contentCopy.querySelectorAll('.leading-ws').forEach((el) => el.remove());
|
|
23
17
|
}
|
|
24
18
|
|
|
25
|
-
return contentCopy.textContent
|
|
19
|
+
return contentCopy.textContent;
|
|
26
20
|
}
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
addEventListener('mouseover', preloadPlayground);
|
|
35
|
-
addEventListener('click', async (event) => {
|
|
36
|
-
const copyButton = (event.target as HTMLElement).closest('[data-stldocs-snippet-copy]') as HTMLElement;
|
|
22
|
+
addEventListener('click', (event) => {
|
|
23
|
+
if (!(event.target instanceof Element)) return;
|
|
24
|
+
const copyButton = event.target.closest('[data-stldocs-snippet-copy]');
|
|
25
|
+
if (!(copyButton instanceof HTMLElement)) return;
|
|
26
|
+
|
|
37
27
|
if (copyButton) {
|
|
38
28
|
const iconElement = copyButton.querySelector('.stldocs-icon') as SVGElement;
|
|
39
29
|
clearTimeout(copyButton.dataset.__stldocsCopyTimeout);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (playButton) {
|
|
54
|
-
showPlayground(playButton);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
async function showPlayground(playButton: HTMLElement) {
|
|
59
|
-
if (playButton.getAttribute('aria-disabled') === 'true') return;
|
|
60
|
-
const iconElement = playButton.querySelector('.stldocs-icon') as SVGElement;
|
|
61
|
-
try {
|
|
62
|
-
// use aria-disabled, not disabled, to avoid losing focus
|
|
63
|
-
playButton.setAttribute('aria-disabled', 'true');
|
|
64
|
-
playButton.setAttribute('aria-label', 'Loading playground...');
|
|
65
|
-
playButton.classList.add('stl-ui-button--loading');
|
|
66
|
-
const showPlayground = loadPlayground(playButton);
|
|
67
|
-
await showPlayground();
|
|
68
|
-
} catch (e) {
|
|
69
|
-
console.error(e);
|
|
70
|
-
iconElement.innerHTML = circleAlertIcon;
|
|
71
|
-
}
|
|
72
|
-
playButton.removeAttribute('aria-disabled');
|
|
73
|
-
playButton.removeAttribute('aria-label');
|
|
74
|
-
playButton.classList.remove('stl-ui-button--loading');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function loadPlayground(playButton: HTMLElement) {
|
|
78
|
-
(playButton as any).__playgroundLoadPromise ??= (async () => {
|
|
79
|
-
const container = playButton.closest('.stldocs-snippet') as HTMLElement;
|
|
80
|
-
const language = (container.querySelector('.stl-sdk-select') as HTMLElement).dataset.currentValue;
|
|
81
|
-
const code = getContent(playButton, true);
|
|
82
|
-
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
83
|
-
if (import.meta.env.DEV) {
|
|
84
|
-
const id = '/@id/astro:scripts/before-hydration.js';
|
|
85
|
-
await import(/* @vite-ignore */ id).catch(console.warn);
|
|
86
|
-
}
|
|
87
|
-
const { createPlayground } = await import('virtual:stl-playground/create');
|
|
88
|
-
const { default: playgroundData } = await import('virtual:stl-playground/data');
|
|
89
|
-
return createPlayground({
|
|
90
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
-
lang: language as any,
|
|
92
|
-
doc: (language === 'python' ? 'from rich import print\n' : '') + code.trimEnd(),
|
|
93
|
-
container,
|
|
94
|
-
onLanguageSelect: (value) => {
|
|
95
|
-
const originalLanguage = container.querySelector<HTMLElement>('[data-stldocs-snippet-select]')
|
|
96
|
-
?.dataset.currentValue;
|
|
97
|
-
const path: string = updateSelectedLanguage(RESOLVED_API_REFERENCE_PATH, originalLanguage, value);
|
|
98
|
-
navigate(path.replace(/(\?.+)?($|#)/, (_, str, end) => (str ? str + '&play' : '?play') + end));
|
|
99
|
-
},
|
|
100
|
-
...playgroundData,
|
|
101
|
-
});
|
|
102
|
-
})();
|
|
103
|
-
return async () => {
|
|
104
|
-
const promise = (playButton as any).__playgroundLoadPromise;
|
|
105
|
-
(playButton as any).__playgroundLoadPromise = null;
|
|
106
|
-
await ((await promise) as () => Promise<void>)();
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
document.addEventListener(getPageLoadEvent(), () => {
|
|
110
|
-
if (new URL(location.href).searchParams.has('play')) {
|
|
111
|
-
document.querySelectorAll('[data-stldocs-snippet-play]').forEach((e) => {
|
|
112
|
-
showPlayground(e as HTMLElement);
|
|
113
|
-
});
|
|
30
|
+
navigator.clipboard
|
|
31
|
+
.writeText(getContent(copyButton, false))
|
|
32
|
+
.then(() => {
|
|
33
|
+
iconElement.innerHTML = checkIcon;
|
|
34
|
+
})
|
|
35
|
+
.catch(() => {
|
|
36
|
+
iconElement.innerHTML = circleAlertIcon;
|
|
37
|
+
})
|
|
38
|
+
.finally(() => {
|
|
39
|
+
copyButton.dataset.__stldocsCopyTimeout = setTimeout(() => {
|
|
40
|
+
iconElement.innerHTML = copyIcon;
|
|
41
|
+
}).toString();
|
|
42
|
+
});
|
|
114
43
|
}
|
|
115
44
|
});
|
|
@@ -27,7 +27,9 @@ document.addEventListener(getPageLoadEvent(), () => {
|
|
|
27
27
|
root: rootElement,
|
|
28
28
|
onSelect: (value) => {
|
|
29
29
|
const originalLanguage = rootElement.dataset.currentValue;
|
|
30
|
-
navigate(updateSelectedLanguage(RESOLVED_API_REFERENCE_PATH, originalLanguage, value))
|
|
30
|
+
navigate(updateSelectedLanguage(RESOLVED_API_REFERENCE_PATH, originalLanguage, value)).catch((e) => {
|
|
31
|
+
console.error('Failed to navigate to selected language', e);
|
|
32
|
+
});
|
|
31
33
|
},
|
|
32
34
|
});
|
|
33
35
|
});
|
|
@@ -37,24 +39,22 @@ document.addEventListener(getPageLoadEvent(), () => {
|
|
|
37
39
|
});
|
|
38
40
|
|
|
39
41
|
document.addEventListener('click', (event) => {
|
|
40
|
-
const toggle =
|
|
41
|
-
'[data-stldocs-property-toggle-expanded]
|
|
42
|
-
)
|
|
42
|
+
const toggle =
|
|
43
|
+
event.target instanceof HTMLElement && event.target.closest('[data-stldocs-property-toggle-expanded]');
|
|
44
|
+
if (!toggle || !(toggle instanceof HTMLElement)) return;
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
toggle.dataset.stldocsPropertyToggleExpanded = state ? 'false' : 'true';
|
|
46
|
+
// Toggle the “expanded” state of the toggle
|
|
47
|
+
const toggleIsExpanded = toggle.dataset.stldocsPropertyToggleExpanded === 'true';
|
|
48
|
+
toggle.dataset.stldocsPropertyToggleExpanded = (!toggleIsExpanded).toString();
|
|
48
49
|
|
|
50
|
+
// Find the described “property group”
|
|
49
51
|
const targetGroup = toggle.dataset.stldocsPropertyToggleTarget;
|
|
50
52
|
if (!targetGroup) return;
|
|
51
|
-
|
|
52
53
|
const target = document.querySelector(`[data-stldocs-property-group=${targetGroup}]`);
|
|
53
54
|
if (!target) return;
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
el.open = initial === 'true' ? true : !state;
|
|
56
|
+
// Expand or collapse all <details> elements within the target group
|
|
57
|
+
[...target.getElementsByTagName('details')].forEach((el) => {
|
|
58
|
+
el.open = el.dataset.stldocsExpanderInitialState === 'true' ? true : !toggleIsExpanded;
|
|
59
59
|
});
|
|
60
60
|
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Let users select text in <summary> elements without toggling them
|
|
2
|
+
|
|
3
|
+
document.addEventListener(
|
|
4
|
+
'mousedown',
|
|
5
|
+
(e) => {
|
|
6
|
+
const summary = (e.target as HTMLElement).closest('summary.stldocs-expander-summary');
|
|
7
|
+
if (!summary) return;
|
|
8
|
+
const selectionOnDown = window.getSelection()?.toString() ?? '';
|
|
9
|
+
|
|
10
|
+
document.addEventListener(
|
|
11
|
+
'mouseup',
|
|
12
|
+
() => {
|
|
13
|
+
const selectionOnUp = window.getSelection()?.toString() ?? '';
|
|
14
|
+
|
|
15
|
+
if (selectionOnUp && selectionOnUp !== selectionOnDown) {
|
|
16
|
+
summary.addEventListener(
|
|
17
|
+
'click',
|
|
18
|
+
(e) => {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
},
|
|
21
|
+
{ once: true },
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{ once: true },
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
{ capture: true },
|
|
29
|
+
);
|