@stainless-api/docs 0.1.0-beta.8 → 0.1.0-beta.80

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 (131) hide show
  1. package/CHANGELOG.md +636 -0
  2. package/eslint-suppressions.json +52 -0
  3. package/locals.d.ts +17 -0
  4. package/package.json +50 -39
  5. package/plugin/assets/languages/cli.svg +14 -0
  6. package/plugin/assets/languages/csharp.svg +1 -0
  7. package/plugin/buildAlgoliaIndex.ts +32 -7
  8. package/plugin/cms/server.ts +130 -59
  9. package/plugin/cms/sidebar-builder.ts +7 -26
  10. package/plugin/cms/worker.ts +83 -5
  11. package/plugin/components/MethodDescription.tsx +54 -0
  12. package/plugin/components/RequestBuilder/ParamEditor.tsx +55 -0
  13. package/plugin/components/RequestBuilder/SnippetStainlessIsland.tsx +107 -0
  14. package/plugin/components/RequestBuilder/index.tsx +31 -0
  15. package/plugin/components/RequestBuilder/props.ts +9 -0
  16. package/plugin/components/RequestBuilder/spec-helpers.ts +50 -0
  17. package/plugin/components/RequestBuilder/styles.css +67 -0
  18. package/plugin/components/SDKSelect.astro +7 -87
  19. package/plugin/components/SnippetCode.tsx +107 -67
  20. package/plugin/components/StainlessIslands.tsx +126 -0
  21. package/plugin/components/search/SearchAlgolia.astro +45 -28
  22. package/plugin/components/search/SearchIsland.tsx +45 -27
  23. package/plugin/generateAPIReferenceLink.ts +2 -2
  24. package/plugin/globalJs/ai-dropdown-options.ts +243 -0
  25. package/plugin/globalJs/code-snippets.ts +15 -8
  26. package/plugin/globalJs/copy.ts +91 -17
  27. package/plugin/globalJs/create-playground.shim.ts +3 -0
  28. package/plugin/globalJs/method-descriptions.ts +33 -0
  29. package/plugin/globalJs/navigation.ts +7 -26
  30. package/plugin/globalJs/playground-data.shim.ts +1 -0
  31. package/plugin/globalJs/playground-data.ts +14 -0
  32. package/plugin/helpers/generateDocsRoutes.ts +27 -0
  33. package/plugin/index.ts +198 -36
  34. package/plugin/languages.ts +7 -2
  35. package/plugin/loadPluginConfig.ts +122 -36
  36. package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +1 -1
  37. package/plugin/react/Routing.tsx +199 -126
  38. package/plugin/referencePlaceholderUtils.ts +1 -1
  39. package/plugin/replaceSidebarPlaceholderMiddleware.ts +3 -3
  40. package/plugin/routes/Docs.astro +55 -102
  41. package/plugin/routes/DocsStatic.astro +1 -1
  42. package/plugin/routes/Overview.astro +10 -16
  43. package/plugin/routes/markdown.ts +9 -8
  44. package/plugin/vendor/preview.worker.docs.js +20928 -17830
  45. package/plugin/vendor/templates/go.md +1 -1
  46. package/plugin/vendor/templates/python.md +1 -1
  47. package/resolveSrcFile.ts +10 -0
  48. package/scripts/vendor_deps.ts +5 -5
  49. package/shared/getProsePages.ts +42 -0
  50. package/shared/getSharedLogger.ts +15 -0
  51. package/shared/terminalUtils.ts +3 -0
  52. package/src/content.config.ts +9 -0
  53. package/stl-docs/components/AIDropdown.tsx +63 -0
  54. package/stl-docs/components/AiChatIsland.tsx +14 -0
  55. package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +2 -2
  56. package/stl-docs/components/Head.astro +16 -0
  57. package/stl-docs/components/Header.astro +6 -8
  58. package/stl-docs/components/PageFrame.astro +18 -0
  59. package/stl-docs/components/PageTitle.astro +82 -0
  60. package/stl-docs/components/TableOfContents.astro +34 -0
  61. package/stl-docs/components/ThemeProvider.astro +36 -0
  62. package/stl-docs/components/ThemeSelect.astro +84 -139
  63. package/stl-docs/components/content-panel/ContentPanel.astro +15 -46
  64. package/stl-docs/components/headers/SplashMobileMenuToggle.astro +17 -1
  65. package/stl-docs/components/headers/StackedHeader.astro +29 -24
  66. package/stl-docs/components/icons/chat-gpt.tsx +2 -2
  67. package/stl-docs/components/icons/cursor.tsx +10 -0
  68. package/stl-docs/components/icons/gemini.tsx +19 -0
  69. package/stl-docs/components/icons/markdown.tsx +1 -1
  70. package/stl-docs/components/index.ts +1 -0
  71. package/stl-docs/components/mintlify-compat/Accordion.astro +7 -5
  72. package/stl-docs/components/mintlify-compat/AccordionGroup.astro +7 -3
  73. package/stl-docs/components/mintlify-compat/Columns.astro +40 -42
  74. package/stl-docs/components/mintlify-compat/Frame.astro +16 -18
  75. package/stl-docs/components/mintlify-compat/callouts/Callout.astro +1 -1
  76. package/stl-docs/components/mintlify-compat/callouts/Check.astro +1 -1
  77. package/stl-docs/components/mintlify-compat/callouts/Danger.astro +1 -1
  78. package/stl-docs/components/mintlify-compat/callouts/Info.astro +1 -1
  79. package/stl-docs/components/mintlify-compat/callouts/Note.astro +1 -1
  80. package/stl-docs/components/mintlify-compat/callouts/Tip.astro +1 -1
  81. package/stl-docs/components/mintlify-compat/callouts/Warning.astro +1 -1
  82. package/stl-docs/components/mintlify-compat/card.css +33 -35
  83. package/stl-docs/components/mintlify-compat/index.ts +2 -4
  84. package/stl-docs/components/nav-tabs/NavDropdown.astro +31 -70
  85. package/stl-docs/components/nav-tabs/NavTabs.astro +78 -80
  86. package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +15 -8
  87. package/stl-docs/components/nav-tabs/buildNavLinks.ts +3 -2
  88. package/stl-docs/components/pagination/HomeLink.astro +10 -0
  89. package/stl-docs/components/pagination/Pagination.astro +175 -0
  90. package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +22 -0
  91. package/stl-docs/components/pagination/PaginationLinkQuiet.astro +13 -0
  92. package/stl-docs/components/pagination/util.ts +71 -0
  93. package/stl-docs/components/scripts.ts +1 -0
  94. package/stl-docs/disableCalloutSyntax.ts +36 -0
  95. package/stl-docs/index.ts +147 -52
  96. package/stl-docs/loadStlDocsConfig.ts +45 -7
  97. package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +61 -0
  98. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +39 -0
  99. package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
  100. package/stl-docs/proseSearchIndexing.ts +450 -0
  101. package/stl-docs/tabsMiddleware.ts +13 -4
  102. package/styles/code.css +128 -136
  103. package/styles/fonts.css +32 -17
  104. package/styles/links.css +11 -48
  105. package/styles/method-descriptions.css +36 -0
  106. package/styles/overrides.css +49 -57
  107. package/styles/page.css +90 -59
  108. package/styles/sdk_select.css +9 -7
  109. package/styles/search.css +57 -69
  110. package/styles/sidebar.css +211 -131
  111. package/styles/{variables.css → sl-variables.css} +3 -2
  112. package/styles/stldocs-variables.css +6 -0
  113. package/styles/toc.css +41 -34
  114. package/theme.css +10 -10
  115. package/tsconfig.json +2 -5
  116. package/virtual-module.d.ts +121 -5
  117. package/components/variables.css +0 -135
  118. package/plugin/globalJs/ai-dropdown.ts +0 -57
  119. package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -58
  120. package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -55
  121. package/stl-docs/components/mintlify-compat/Step.astro +0 -58
  122. package/stl-docs/components/mintlify-compat/Steps.astro +0 -17
  123. /package/{plugin/assets → assets}/fonts/geist/OFL.txt +0 -0
  124. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
  125. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin.woff2 +0 -0
  126. /package/{plugin/assets → assets}/fonts/geist/geist-latin-ext.woff2 +0 -0
  127. /package/{plugin/assets → assets}/fonts/geist/geist-latin.woff2 +0 -0
  128. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
  129. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
  130. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
  131. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin.woff2 +0 -0
@@ -1,10 +1,11 @@
1
1
  import Worker from 'web-worker';
2
- import { Languages, type DocsLanguage } from '@stainless-api/docs-ui/src/routing';
3
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
2
+ import { Languages, type DocsLanguage } from '@stainless-api/docs-ui/routing';
3
+ import type * as SDKJSON from '@stainless/sdk-json';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { dirname, resolve } from 'node:path';
6
6
  import fs from 'fs/promises';
7
7
  import pathutils from 'path';
8
+ import type { VersionUserConfig } from '../loadPluginConfig';
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url);
10
11
  const __dirname = dirname(__filename);
@@ -12,7 +13,81 @@ const __dirname = dirname(__filename);
12
13
  const workerPath = resolve(__dirname, '..', 'vendor', 'preview.worker.docs.js');
13
14
 
14
15
  type OpenAPIDocument = Record<string, any>;
15
- type ParsedConfig = Record<string, any>;
16
+ export type ParsedConfig = {
17
+ docs:
18
+ | {
19
+ title?: string | undefined;
20
+ favicon?: string | undefined;
21
+ logo_icon?: string | undefined;
22
+ search?:
23
+ | {
24
+ algolia?:
25
+ | {
26
+ app_id: string;
27
+ index_name: string;
28
+ search_key: string;
29
+ }
30
+ | undefined;
31
+ }
32
+ | undefined;
33
+ description?: string | undefined;
34
+ languages?:
35
+ | ('node' | 'typescript' | 'python' | 'java' | 'kotlin' | 'go' | 'ruby' | 'terraform' | 'http')[]
36
+ | undefined;
37
+ snippets?:
38
+ | {
39
+ exclude_languages?: string[] | undefined;
40
+ }
41
+ | undefined;
42
+ show_security?: boolean | undefined;
43
+ show_readme?: boolean | undefined;
44
+ base_path?: string | undefined;
45
+ navigation?:
46
+ | {
47
+ menubar?:
48
+ | {
49
+ title: string;
50
+ icon?: string;
51
+ variant?: string;
52
+ href?: string | undefined;
53
+ page?: string | undefined;
54
+ }[]
55
+ | undefined;
56
+ sidebar?:
57
+ | {
58
+ title: string;
59
+ icon?: string;
60
+ variant?: string;
61
+ href?: string | undefined;
62
+ page?: string | undefined;
63
+ }[]
64
+ | undefined;
65
+ }
66
+ | undefined;
67
+ pages?: unknown;
68
+ resources?: unknown[] | undefined;
69
+ }
70
+ | undefined;
71
+ targets: Record<string, { skip?: boolean }>;
72
+ client_settings: {
73
+ opts: {
74
+ [x: string]: {
75
+ type: 'string' | 'number' | 'boolean' | 'null' | 'integer';
76
+ nullable: boolean;
77
+ description?: string | undefined;
78
+ example?: unknown;
79
+ default?: unknown;
80
+ read_env?: string | undefined;
81
+ auth?:
82
+ | {
83
+ security_scheme: string;
84
+ role?: 'value' | 'password' | 'username' | 'client_id' | 'client_secret' | undefined;
85
+ }
86
+ | undefined;
87
+ };
88
+ };
89
+ };
90
+ };
16
91
 
17
92
  function runJob({ type, signal, data }: { type: string; signal?: AbortSignal; data: any }) {
18
93
  const stainlessWorker = new Worker(workerPath, {
@@ -22,6 +97,7 @@ function runJob({ type, signal, data }: { type: string; signal?: AbortSignal; da
22
97
 
23
98
  return new Promise<any>((resolve, reject) => {
24
99
  stainlessWorker.addEventListener('error', (e) => {
100
+ e.preventDefault();
25
101
  reject(e);
26
102
  });
27
103
 
@@ -85,10 +161,12 @@ export async function createSDKJSON({
85
161
  oas,
86
162
  config,
87
163
  languages,
164
+ version,
88
165
  }: {
89
166
  oas: OpenAPIDocument;
90
167
  config: ParsedConfig;
91
168
  languages: DocsLanguage[];
169
+ version: VersionUserConfig;
92
170
  }) {
93
171
  const templatePath = resolve(__dirname, '../vendor/templates');
94
172
  const readmeLoader = await Promise.all(
@@ -98,7 +176,7 @@ export async function createSDKJSON({
98
176
  try {
99
177
  const content = await fs.readFile(mdfile);
100
178
  return [language, content.toString()];
101
- } catch (err) {
179
+ } catch {
102
180
  return [language, null];
103
181
  }
104
182
  }),
@@ -113,7 +191,7 @@ export async function createSDKJSON({
113
191
  config,
114
192
  languages,
115
193
  transform: false,
116
- projectName: '',
194
+ projectName: version.stainlessProject,
117
195
  readmeTemplates,
118
196
  },
119
197
  });
@@ -0,0 +1,54 @@
1
+ import { MethodDescriptionProps } from '@stainless-api/docs-ui/components';
2
+ import { useComponents } from '@stainless-api/docs-ui/contexts/use-components';
3
+ import style from '@stainless-api/docs-ui/style';
4
+ import { Button } from '@stainless-api/ui-primitives';
5
+
6
+ function shouldCollapseDescription(description: string) {
7
+ const MIN_CHARS = 400;
8
+ const MIN_LINES = 6;
9
+
10
+ const lineCount = description.split('\n').length;
11
+
12
+ if (description.length >= MIN_CHARS) return true;
13
+ if (lineCount >= MIN_LINES) return true;
14
+
15
+ // Markdown structure often means longer content
16
+ if (/#\s/.test(description)) return true; // has headings
17
+ if (/```/.test(description)) return true; // has code blocks
18
+ if (/^\s*[-*]\s+/m.test(description)) return true; // has lists
19
+
20
+ return false;
21
+ }
22
+
23
+ export function MethodDescription({ description }: MethodDescriptionProps) {
24
+ const { Markdown } = useComponents();
25
+
26
+ if (description) {
27
+ // Attempt to determine if we should make the description collapsible initially
28
+ // or not. If we get this right, there will be no FOUC.
29
+ const collapsible = shouldCollapseDescription(description);
30
+
31
+ return (
32
+ <div className="stl-method-description">
33
+ <div
34
+ className={style.MethodDescription}
35
+ data-stldocs-property-group="method-description"
36
+ data-collapsed={collapsible ? 'true' : 'false'}
37
+ >
38
+ <Markdown content={description} />
39
+ </div>
40
+ <div className="stl-method-description-overflow-wrapper">
41
+ <Button
42
+ type="button"
43
+ data-method-description-toggle
44
+ size="sm"
45
+ variant="ghost"
46
+ hidden={!collapsible}
47
+ >
48
+ Show more
49
+ </Button>
50
+ </div>
51
+ </div>
52
+ );
53
+ }
54
+ }
@@ -0,0 +1,55 @@
1
+ import { useMemo } from 'react';
2
+ import { Button } from '@stainless-api/ui-primitives';
3
+ import type { Param } from './spec-helpers';
4
+ import { InfoIcon } from 'lucide-react';
5
+ import { Input } from '@stainless-api/docs-ui/components';
6
+
7
+ function setHighlight(stainlessPath: string, highlighted: boolean) {
8
+ const ele = document.getElementById(stainlessPath);
9
+ if (!ele) return;
10
+ ele.classList.toggle('stldocs-property-highlighted', highlighted);
11
+ if (highlighted) {
12
+ if (location.hash) {
13
+ const prevScroll = document.documentElement.scrollTop;
14
+ location.hash = '';
15
+ document.documentElement.scrollTop = prevScroll;
16
+ }
17
+ if (document.body.clientWidth >= 1280) {
18
+ ele.scrollIntoView({
19
+ behavior: 'smooth',
20
+ });
21
+ }
22
+ }
23
+ }
24
+
25
+ function htmlToText(html: string) {
26
+ const template = document.createElement('template');
27
+ template.innerHTML = html;
28
+ return template.content.textContent;
29
+ }
30
+
31
+ export function ParamEditor({ param }: { param: Param }) {
32
+ const type = useMemo(() => htmlToText(param.type), [param.type]);
33
+
34
+ return (
35
+ <label className="request-builder-param">
36
+ <Button
37
+ className="request-builder-param-info-button"
38
+ variant="ghost"
39
+ href={`#${encodeURIComponent(param.stainlessPath)}`}
40
+ >
41
+ <Button.Icon icon={InfoIcon} size={16} />
42
+ </Button>
43
+
44
+ <span className="request-builder-param-label">{param.key}</span>
45
+ <span className="request-builder-param-colon">:</span>
46
+
47
+ <Input
48
+ className="request-builder-param-value"
49
+ onFocus={() => setHighlight(param.stainlessPath, true)}
50
+ onBlur={() => setHighlight(param.stainlessPath, false)}
51
+ placeholder={type}
52
+ />
53
+ </label>
54
+ );
55
+ }
@@ -0,0 +1,107 @@
1
+ import { useState, useMemo, useEffect, useId, useSyncExternalStore, Activity, Fragment } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { Button } from '@stainless-api/ui-primitives';
4
+ import { PlayIcon, RotateCcw } from 'lucide-react';
5
+ import { SnippetStainlessIslandPropsSchema } from './props';
6
+ import { ParamEditor } from './ParamEditor';
7
+ import './styles.css';
8
+
9
+ function useRequiredChild<T extends Element = Element>(
10
+ parent: Element | null,
11
+ selector: string,
12
+ ): React.RefObject<T> {
13
+ const elementRef = useMemo(() => {
14
+ const el = parent?.querySelector<T>(selector);
15
+ return el ? { current: el } : null;
16
+ }, [parent, selector]);
17
+ if (!elementRef) throw new Error(`Required child not found: ${selector}`);
18
+ return elementRef;
19
+ }
20
+
21
+ function useSetVisibility(elementRef: React.RefObject<HTMLElement>, visible: boolean) {
22
+ useEffect(() => {
23
+ elementRef.current.style.display = visible ? '' : 'none';
24
+ }, [elementRef, visible]);
25
+ }
26
+
27
+ export default function SnippetStainlessIsland({ parent }: { parent: HTMLElement }) {
28
+ const [expanded, setExpanded] = useState(false);
29
+
30
+ const trigger = useRequiredChild<HTMLButtonElement>(parent, '.try-it-footer .try-it-button');
31
+ const codeContainer = useRequiredChild<HTMLElement>(parent, '.stldocs-snippet-code');
32
+ const exampleContainer = useRequiredChild<HTMLElement>(parent, '.stldocs-snippet-multi-response');
33
+ useSetVisibility(codeContainer, !expanded);
34
+ useSetVisibility(exampleContainer, !expanded);
35
+ useSetVisibility(trigger, !expanded);
36
+ // Attach click handler to trigger
37
+ useEffect(() => {
38
+ if (!trigger.current) return;
39
+ const ac = new AbortController();
40
+ trigger.current.addEventListener('click', () => setExpanded(true), { signal: ac.signal });
41
+ return () => ac.abort();
42
+ });
43
+
44
+ const requestBuilderContainer = useRequiredChild<HTMLElement>(parent, '.request-builder-container');
45
+ const requestBuilderFooter = useRequiredChild<HTMLElement>(parent, '.request-builder-footer');
46
+ const requestBuilderResponse = useRequiredChild<HTMLElement>(parent, '.request-builder-response');
47
+ const requestBuilderProps = useRequiredChild<HTMLTemplateElement>(parent, '.request-builder-props').current;
48
+ const serializedProps = useSyncExternalStore(
49
+ (cb) => {
50
+ const mutationObserver = new MutationObserver(() => cb());
51
+ mutationObserver.observe(requestBuilderProps, { childList: true });
52
+ return () => mutationObserver.disconnect();
53
+ },
54
+ () => requestBuilderProps.content.textContent,
55
+ );
56
+ const deserializedProps = SnippetStainlessIslandPropsSchema.parse(JSON.parse(serializedProps));
57
+ const groupedParams = Map.groupBy(deserializedProps.params, (p) => p.location);
58
+
59
+ const formId = `request-builder-form-${useId()}`;
60
+
61
+ return (
62
+ <>
63
+ {createPortal(
64
+ <Activity mode={expanded ? 'visible' : 'hidden'}>
65
+ <form
66
+ onSubmit={(e) => {
67
+ alert('TODO: Submit button clicked');
68
+ e.preventDefault();
69
+ }}
70
+ id={formId}
71
+ >
72
+ {[...groupedParams.entries()].map(([location, params]) => (
73
+ <Fragment key={location}>
74
+ <h4>{location} parameters</h4>
75
+ {params.map((e) => (
76
+ <ParamEditor param={e} key={e.location + e.key} />
77
+ ))}
78
+ </Fragment>
79
+ ))}
80
+ </form>
81
+ </Activity>,
82
+ requestBuilderContainer.current,
83
+ )}
84
+
85
+ {createPortal(
86
+ <Activity mode={expanded ? 'visible' : 'hidden'}>
87
+ <Button variant="ghost" border={true} onClick={() => setExpanded(false)}>
88
+ <Button.Icon icon={RotateCcw} />
89
+ </Button>
90
+
91
+ <Button variant="success" className="send-button" type="submit" form={formId}>
92
+ <Button.Label>Send</Button.Label>
93
+ <Button.Icon icon={PlayIcon} />
94
+ </Button>
95
+ </Activity>,
96
+ requestBuilderFooter.current,
97
+ )}
98
+
99
+ {createPortal(
100
+ <Activity mode={expanded ? 'visible' : 'hidden'}>
101
+ <div>{/* TODO */}</div>
102
+ </Activity>,
103
+ requestBuilderResponse.current,
104
+ )}
105
+ </>
106
+ );
107
+ }
@@ -0,0 +1,31 @@
1
+ import { ReactNode } from 'react';
2
+ import * as SDKJSON from '@stainless/sdk-json';
3
+ import { useSpec } from '@stainless-api/docs-ui/contexts';
4
+ import { extractParams } from './spec-helpers';
5
+ import type { SnippetStainlessIslandProps } from './props';
6
+
7
+ /** Load and process the spec on the server side to avoid inflating client bundle */
8
+ export function RequestBuilder({
9
+ className,
10
+ children,
11
+ method,
12
+ }: {
13
+ className: string;
14
+ children: ReactNode;
15
+ method: SDKJSON.Method;
16
+ }) {
17
+ const spec = useSpec();
18
+ if (!spec) throw new Error('Spec is required for RequestBuilder');
19
+ const params = spec && extractParams(spec, method);
20
+ const [httpMethod, path] = method.endpoint.split(' ') as [string, string];
21
+
22
+ return (
23
+ <stl-island component="SnippetStainlessIsland" className={className}>
24
+ {/* Pass state down to the client component. TODO: we need a better solution for this */}
25
+ <template className="request-builder-props">
26
+ {JSON.stringify({ method: httpMethod, path, params } satisfies SnippetStainlessIslandProps)}
27
+ </template>
28
+ {children}
29
+ </stl-island>
30
+ );
31
+ }
@@ -0,0 +1,9 @@
1
+ import z from 'zod';
2
+ import { ParamSchema } from './spec-helpers';
3
+
4
+ export const SnippetStainlessIslandPropsSchema = z.object({
5
+ method: z.string(),
6
+ path: z.string(),
7
+ params: z.array(ParamSchema),
8
+ });
9
+ export type SnippetStainlessIslandProps = z.infer<typeof SnippetStainlessIslandPropsSchema>;
@@ -0,0 +1,50 @@
1
+ import type * as SDKJSON from '@stainless/sdk-json';
2
+ import { printer } from '@stainless-api/docs-ui/markdown';
3
+ import z from 'zod';
4
+
5
+ export const ParamSchema = z.object({
6
+ stainlessPath: z.string(),
7
+ location: z.string(),
8
+ key: z.string(),
9
+ type: z.string(),
10
+ });
11
+
12
+ export type Param = z.infer<typeof ParamSchema>;
13
+
14
+ export function extractParams(spec: SDKJSON.Spec | undefined, method: SDKJSON.Method): Param[] {
15
+ const httpDecls = spec?.decls?.http;
16
+ if (!httpDecls) throw new Error('expected http language to be present in SDKJSON');
17
+ const decl = httpDecls?.[method.stainlessPath];
18
+ if (decl?.kind !== 'HttpDeclFunction') {
19
+ throw new Error(
20
+ 'expected HttpDeclFunction at stainlessPath "' + method.stainlessPath + '", got ' + decl?.kind,
21
+ );
22
+ }
23
+ const bodyTypes = Object.keys(decl.bodyParamsChildren ?? {});
24
+ if (bodyTypes.length > 0 && !bodyTypes.includes('application/json')) {
25
+ throw new Error('TODO: support non-json body params');
26
+ }
27
+ const bodyParams = decl.bodyParamsChildren?.['application/json'];
28
+ const params = [
29
+ ...Object.entries(decl.paramsChildren ?? {}).map(([location, children]) => ({ location, children })),
30
+ ...(bodyParams ? [{ location: 'body', children: bodyParams }] : []),
31
+ ]
32
+ .filter((e) => e.children.length)
33
+ .flatMap(({ location, children }) =>
34
+ children.map((child) => {
35
+ const resolved = httpDecls[child];
36
+ if (resolved?.kind !== 'HttpDeclProperty') {
37
+ throw new Error(
38
+ 'expected HttpDeclProperty at stainlessPath "' + child + '", got ' + resolved?.kind,
39
+ );
40
+ }
41
+ return {
42
+ stainlessPath: resolved.stainlessPath,
43
+ location,
44
+ key: resolved.key,
45
+ type: (resolved.optional ? 'optional ' : '') + printer.type('http', resolved.type),
46
+ };
47
+ }),
48
+ );
49
+ return params;
50
+ }
@@ -0,0 +1,67 @@
1
+ .request-builder-container form {
2
+ display: grid;
3
+
4
+ /* prettier-ignore */
5
+ grid-template-columns: /*info button*/max-content /*label*/max-content /*colon*/max-content /*input*/1fr;
6
+ font-family: var(--stl-typography-font);
7
+ font-size: var(--stl-typography-text-body-sm);
8
+ color: var(--stl-color-foreground);
9
+
10
+ & > * {
11
+ padding-inline-start: 12px;
12
+ padding-inline-end: 8px;
13
+ }
14
+
15
+ & > h4 {
16
+ grid-column: 1 / -1;
17
+ text-transform: capitalize;
18
+ font-size: var(--stl-typography-scale-sm);
19
+ margin-top: 0;
20
+ margin-bottom: 0.25em;
21
+ color: var(--stl-color-foreground-muted);
22
+ padding-top: 0.75rem;
23
+
24
+ &:not(:first-child) {
25
+ border-top: 1px solid var(--stl-color-border);
26
+ }
27
+ }
28
+
29
+ .request-builder-param {
30
+ grid-column: 1 / -1;
31
+ display: grid;
32
+ grid-template-columns: subgrid;
33
+ align-items: center;
34
+ column-gap: 0.5rem;
35
+ padding-block: 0.35rem;
36
+
37
+ .request-builder-param-info-button {
38
+ color: var(--stl-color-foreground-muted);
39
+ margin-inline: -4px;
40
+ padding: 0;
41
+ }
42
+ .request-builder-param-label {
43
+ font-family: var(--stl-typography-font-mono);
44
+ }
45
+ .request-builder-param-colon {
46
+ font-family: var(--stl-typography-font-mono);
47
+ color: var(--stl-color-foreground-muted);
48
+ }
49
+ /* TODO: new input component that is better stylable */
50
+ .request-builder-param-value {
51
+ input {
52
+ margin: 6px 8px;
53
+ font-size: inherit;
54
+ }
55
+ }
56
+
57
+ &:has(+ h4),
58
+ &:last-child {
59
+ padding-bottom: 0.75rem;
60
+ }
61
+ }
62
+ }
63
+
64
+ .request-builder-footer .stl-ui-button--ghost .stl-ui-button__icon {
65
+ color: var(--stl-color-foreground);
66
+ opacity: var(--stl-opacity-level-040);
67
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
- import type { DocsLanguage } from '@stainless-api/docs-ui/src/routing';
3
- import { parseRoute } from '@stainless-api/docs-ui/src/routing';
2
+ import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
3
+ import { parseRoute } from '@stainless-api/docs-ui/routing';
4
4
  import { cmsClient } from '../cms/client';
5
5
  import { BASE_PATH, DEFAULT_LANGUAGE, EXCLUDE_LANGUAGES } from 'virtual:stl-starlight-virtual-module';
6
6
  import { Languages } from '../languages';
@@ -53,100 +53,20 @@ const readmeSlug = language === 'http' ? BASE_PATH : `${BASE_PATH}/${language}`;
53
53
  )
54
54
  }
55
55
 
56
- <style>
57
- @layer starlight.core {
58
- label {
59
- --sl-label-icon-size: 16px;
60
- --sl-caret-size: 1.25rem;
61
- --sl-inline-padding: 0.5rem;
62
- position: relative;
63
- display: flex;
64
- align-items: center;
65
- gap: 0.25rem;
66
- color: var(--sl-color-gray-1);
67
- }
68
-
69
- label:hover {
70
- color: var(--sl-color-gray-2);
71
- }
72
-
73
- .icon {
74
- position: absolute;
75
- top: 50%;
76
- transform: translateY(-50%);
77
- pointer-events: none;
78
- width: 16px;
79
- }
80
-
81
- select {
82
- padding-block: 0.3rem;
83
- padding-inline: calc(var(--sl-label-icon-size) + var(--sl-inline-padding) + 0.5rem)
84
- calc(var(--sl-caret-size) + var(--sl-inline-padding) + 0.25rem);
85
- margin-inline: calc(var(--sl-inline-padding) * -1);
86
- width: calc(var(--sl-select-width) + var(--sl-inline-padding) * 2);
87
- text-overflow: ellipsis;
88
- color: inherit;
89
- cursor: pointer;
90
- appearance: none;
91
- font-weight: 600;
92
- text-transform: capitalize;
93
- }
94
-
95
- select:active {
96
- font-weight: inherit;
97
- /* font-family: sans-serif;
98
- font-weight: 400; */
99
- }
100
-
101
- option {
102
- background-color: var(--sl-color-bg-nav);
103
- color: var(--sl-color-gray-1);
104
- }
105
-
106
- @media (min-width: 50rem) {
107
- select {
108
- font-size: var(--sl-text-sm);
109
- }
110
- }
111
- }
112
-
113
- @layer starlight.components {
114
- .label-icon {
115
- font-size: var(--sl-label-icon-size);
116
- inset-inline-start: 0;
117
- }
118
-
119
- .caret {
120
- font-size: var(--sl-caret-size);
121
- inset-inline-end: 0;
122
- }
123
- }
124
-
125
- .custom-select-wrapper {
126
- --sl-inline-padding: 0.5rem;
127
- position: relative;
128
- display: inline-block;
129
- /* These match the padding on the sidebar menu */
130
- padding-left: var(--sl-inline-padding);
131
- padding-right: var(--sl-inline-padding);
132
-
133
- .icon.http path {
134
- fill: var(--sl-color-text);
135
- }
136
- }
137
- </style>
138
56
  <script>
139
57
  import { navigate } from 'astro:transitions/client';
140
58
  import { updateSelectedLanguage } from '../languages';
141
- import { initDropdown } from '@stainless-api/docs-ui/src/components/scripts/dropdown';
59
+ import { initDropdown } from '@stainless-api/docs/components/scripts';
142
60
  import { BASE_PATH } from 'virtual:stl-starlight-virtual-module';
143
61
  import { getPageLoadEvent } from '../helpers/getPageLoadEvent';
144
62
 
145
63
  document.addEventListener(getPageLoadEvent(), () => {
64
+ const sdkSelect = document.getElementById('sidebar-sdk-select');
65
+ if (!sdkSelect) return;
146
66
  initDropdown({
147
- dropdownId: 'sidebar-sdk-select',
67
+ root: sdkSelect,
148
68
  onSelect: (value) => {
149
- const originalLanguage = document.getElementById('sidebar-sdk-select')?.dataset.currentValue;
69
+ const originalLanguage = sdkSelect.dataset.currentValue;
150
70
  navigate(updateSelectedLanguage(BASE_PATH, originalLanguage, value));
151
71
  },
152
72
  });