@stainless-api/docs 0.1.0-beta.83 → 0.1.0-beta.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @stainless-api/docs
2
2
 
3
+ ## 0.1.0-beta.85
4
+
5
+ ### Patch Changes
6
+
7
+ - 4b1a91d: pagefind-ignore for breadcrumbs & ai dropdown
8
+ - Updated dependencies [274cefc]
9
+ - @stainless-api/docs-ui@0.1.0-beta.63
10
+ - @stainless-api/docs-search@0.1.0-beta.16
11
+
12
+ ## 0.1.0-beta.84
13
+
14
+ ### Minor Changes
15
+
16
+ - 6ef241e: Sidebar improvements; rename Sidebar to SDKSidebar
17
+
18
+ ### Patch Changes
19
+
20
+ - d3a85b5: feat: improve not implemented fallback ui
21
+ - 5156697: include markdown alt link for all pages
22
+ - dedd93b: fix incorrect link rel=alternate markdown URLs
23
+ - b532d45: use method summary as page title
24
+ - Updated dependencies [6ef241e]
25
+ - Updated dependencies [d3a85b5]
26
+ - Updated dependencies [d3a85b5]
27
+ - Updated dependencies [2dcb5fb]
28
+ - @stainless-api/docs-ui@0.1.0-beta.62
29
+ - @stainless-api/docs-search@0.1.0-beta.15
30
+
3
31
  ## 0.1.0-beta.83
4
32
 
5
33
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stainless-api/docs",
3
- "version": "0.1.0-beta.83",
3
+ "version": "0.1.0-beta.85",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -30,7 +30,7 @@
30
30
  "node": ">=18.17.1"
31
31
  },
32
32
  "peerDependencies": {
33
- "@astrojs/starlight": ">=0.37.0",
33
+ "@astrojs/starlight": ">=0.37.4",
34
34
  "astro": ">=5.15.3",
35
35
  "react": ">=19.0.0",
36
36
  "react-dom": ">=19.0.0",
@@ -58,8 +58,8 @@
58
58
  "vite-plugin-prebundle-workers": "^0.2.0",
59
59
  "web-worker": "^1.5.0",
60
60
  "yaml": "^2.8.2",
61
- "@stainless-api/docs-search": "0.1.0-beta.14",
62
- "@stainless-api/docs-ui": "0.1.0-beta.61",
61
+ "@stainless-api/docs-search": "0.1.0-beta.16",
62
+ "@stainless-api/docs-ui": "0.1.0-beta.63",
63
63
  "@stainless-api/ui-primitives": "0.1.0-beta.45"
64
64
  },
65
65
  "devDependencies": {
@@ -15,7 +15,7 @@ export function generateDocsRoutes(spec: SDKJSON.Spec, excludeLanguages: DocsLan
15
15
  stainlessPath: null,
16
16
  language: language as DocsLanguage,
17
17
  title: 'Readme',
18
- kind: 'readme',
18
+ kind: 'readme' as const,
19
19
  }));
20
20
 
21
21
  return [...paths, ...readmes].map(({ slug, stainlessPath, language, title, kind }) => {
package/plugin/index.ts CHANGED
@@ -30,6 +30,7 @@ import { resolveSrcFile } from '../resolveSrcFile';
30
30
  import { mkdir, writeFile } from 'fs/promises';
31
31
  import { fileURLToPath } from 'url';
32
32
  import prebundleWorkers from 'vite-plugin-prebundle-workers';
33
+ import { generateMissingRouteList } from '@stainless-api/docs-ui/routing';
33
34
 
34
35
  export { generateAPILink } from './generateAPIReferenceLink';
35
36
  export type { ReferenceSidebarConfigItem };
@@ -377,6 +378,31 @@ async function stlStarlightAstroIntegration(
377
378
  astroBase,
378
379
  };
379
380
  await writeFile(path.join(stainlessDir, 'stl-manifest.json'), JSON.stringify(manifest, null, 2));
381
+
382
+ // Generate a list of missing API routes to enable graceful handling of unimplemented SDK methods.
383
+ // When users switch languages in the docs, some API methods may not be implemented in the target SDK.
384
+ // Instead of showing a generic 404, we statically generate pages for these routes and mark them
385
+ // in this file so Cloudflare can serve them with a 404 status. These pages display helpful information
386
+ // about the missing method and provide links to SDKs where it is available.
387
+ const { data: spec } = (await (
388
+ await fetch(`http://localhost:${CMS_PORT}/retrieve_spec`, {
389
+ method: 'POST',
390
+ body: JSON.stringify({}),
391
+ })
392
+ ).json()) as SpecResp;
393
+ if (spec) {
394
+ const missingRoutes = await generateMissingRouteList({
395
+ spec,
396
+ basePath: path.posix.join(astroBase, pluginConfig.basePath),
397
+ });
398
+ await mkdir(stainlessDir, { recursive: true });
399
+ await writeFile(
400
+ path.join(stainlessDir, 'missing-routes.json'),
401
+ JSON.stringify(missingRoutes, null, 2),
402
+ );
403
+ } else {
404
+ console.log('Could not retrieve spec for missing routes check');
405
+ }
380
406
  },
381
407
  },
382
408
  };
@@ -409,6 +435,7 @@ export function stainlessStarlight(someUserConfig: SomeStainlessStarlightUserCon
409
435
  }
410
436
  process.exit(1);
411
437
  }
438
+
412
439
  const config = configParseResult.config;
413
440
 
414
441
  const isReactLoaded = astroConfig.integrations.find(({ name }) => name === '@astrojs/react');
@@ -3,8 +3,8 @@ import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
3
3
  import { cmsClient } from '../cms/client';
4
4
  import { getReadmeContent, buildPageNavigation, RenderSpec, astroMarkdownRender } from '../react/Routing';
5
5
  import { getResourceFromSpec } from '@stainless-api/docs-ui/utils';
6
+ import { parseStainlessPath } from '@stainless-api/docs-ui/routing';
6
7
  import {
7
- BASE_PATH,
8
8
  CONTENT_PANEL_LAYOUT,
9
9
  MIDDLEWARE,
10
10
  EXPERIMENTAL_REQUEST_BUILDER,
@@ -43,27 +43,28 @@ const pageNav =
43
43
 
44
44
  Astro.locals.language = props.language;
45
45
 
46
+ // Use first heading in README as page title
46
47
  if (readme) {
47
48
  const repo = spec.metadata?.[props.language]?.code_url;
48
49
  readme.code = readme.code.replace(/<a href="(?!(?:https?:\/\/|\/\/))([^"]+)"/g, `<a href="${repo}/$1"`);
49
50
  props.title = readme.metadata.headings[0]!.text ?? 'Overview';
50
51
  }
52
+
53
+ // use summary for the page title of method pages
54
+ if (props.kind === 'http_method' && props.stainlessPath) {
55
+ const parsed = parseStainlessPath(props.stainlessPath);
56
+ const resource = getResourceFromSpec(props.stainlessPath, spec);
57
+ if (parsed?.method && resource?.methods[parsed.method]) {
58
+ const method = resource.methods[parsed.method];
59
+ props.title = method?.summary ?? method?.title ?? props.title;
60
+ }
61
+ }
51
62
  ---
52
63
 
53
64
  <StarlightPage
54
65
  headings={pageNav}
55
66
  frontmatter={{
56
- title: props?.title,
57
- head: [
58
- {
59
- tag: 'link',
60
- attrs: {
61
- rel: 'alternate',
62
- type: 'text/markdown',
63
- href: `${BASE_PATH}/${Astro.params.slug}.md`,
64
- },
65
- },
66
- ],
67
+ title: props.title,
67
68
  pagefind: false,
68
69
  tableOfContents: ['resource', 'readme'].includes(props.kind) ? { maxHeadingLevel: 6 } : false,
69
70
  }}
@@ -55,3 +55,5 @@ export const GET: APIRoute<RouteProps> = async ({ props }) => {
55
55
  headers: { 'Content-Type': 'text/plain' },
56
56
  });
57
57
  };
58
+
59
+ export const prerender = true;
@@ -1,14 +1,16 @@
1
1
  ---
2
2
  import Default from '@astrojs/starlight/components/Head.astro';
3
-
3
+ import path from 'path';
4
4
  // TODO: for users who are overriding the font stack in their own styles, how can we know that and
5
5
  // preload their font instead of ours?
6
6
  import geistPath from '../../assets/fonts/geist/geist-latin.woff2';
7
+
8
+ const mdPath = path.posix.join(Astro.url.pathname, 'index.md');
7
9
  ---
8
10
 
9
11
  <Default />
10
-
11
12
  <link rel="preload" as="font" type="font/woff2" crossorigin="anonymous" href={geistPath} />
13
+ <link rel="alternate" type="text/markdown" href={mdPath} />
12
14
 
13
15
  <script>
14
16
  import { wireAIDropdown } from '../../plugin/globalJs/ai-dropdown-options.ts';
@@ -61,7 +61,7 @@ const shouldRenderDescription = RENDER_PAGE_DESCRIPTIONS;
61
61
  {
62
62
  skipRenderingStarlightTitle ? null : (
63
63
  <>
64
- <div class="stl-ui-not-prose stl-page-nav-container stl-prose-page-nav-container">
64
+ <div class="stl-ui-not-prose stl-page-nav-container stl-prose-page-nav-container" data-pagefind-ignore>
65
65
  <ContentBreadcrumbs currentPath={currentPath} sidebarEntry={Astro.locals.starlightRoute.sidebar} />
66
66
  {showAIDropdown && <AIDropdown />}
67
67
  </div>
@@ -1,10 +1,15 @@
1
1
  ---
2
- import Default from '@astrojs/starlight/components/Sidebar.astro';
3
2
  import HeaderLinks from '../headers/HeaderLinks.astro';
3
+ import { SidebarWithComponents } from './SidebarWithComponents';
4
+ import SidebarPersister from '@astrojs/starlight/components/SidebarPersister.astro';
5
+
6
+ const { sidebar } = Astro.locals.starlightRoute;
4
7
  ---
5
8
 
6
9
  <div class="stl-sidebar-header-links">
7
10
  <HeaderLinks />
8
11
  </div>
9
12
  <slot name="sdk-select" />
10
- <Default><slot /></Default>
13
+ <SidebarPersister>
14
+ <SidebarWithComponents entries={sidebar} withStarlightRestoration />
15
+ </SidebarPersister>
@@ -0,0 +1,10 @@
1
+ import { StlSidebar, StlSidebarProps } from '@stainless-api/docs-ui/components';
2
+ import { ComponentProvider } from '@stainless-api/docs-ui/contexts/component';
3
+
4
+ export function SidebarWithComponents(props: StlSidebarProps) {
5
+ return (
6
+ <ComponentProvider>
7
+ <StlSidebar {...props} />
8
+ </ComponentProvider>
9
+ );
10
+ }
package/styles/page.css CHANGED
@@ -35,7 +35,9 @@
35
35
  /* on desktop, adjust sidebar so that its _text content_ aligns with the page left edge.
36
36
  * padding (visible on hover) bleeds out beyond the page left edge, covered by --stl-ui-page-padding-inline */
37
37
  .sidebar-pane {
38
- margin-inline-start: calc(-1 * (var(--sl-sidebar-pad-x) + var(--stl-sidebar-item-padding-inline)));
38
+ /* 12px comes from --stl-sidebar-item-padding-inline (which is unfortunately defined deeper than
39
+ this component). Please keep in sync. */
40
+ margin-inline-start: calc(-1 * (var(--sl-sidebar-pad-x) + 12px));
39
41
  }
40
42
 
41
43
  .header,
@@ -11,220 +11,10 @@
11
11
  }
12
12
  }
13
13
 
14
- .sidebar {
15
- --stl-sidebar-item-padding-inline: 12px;
16
- --stl-sidebar-item-padding-block: 6px;
17
- --stl-sidebar-indent: 12px;
18
- font-size: var(--stl-typography-scale-sm);
19
- }
20
-
21
14
  .sidebar-pane {
22
15
  border-inline-end: 1px solid var(--stl-color-border-faint);
23
16
  }
24
17
 
25
- .sidebar-content {
26
- ul,
27
- summary {
28
- list-style-type: none;
29
- }
30
-
31
- /* collapsible sections */
32
- details > summary {
33
- display: flex;
34
- align-items: center;
35
- font-weight: 500;
36
-
37
- .caret {
38
- margin-left: auto;
39
- margin-right: -4px;
40
- opacity: 0.65;
41
- transition:
42
- opacity 0.1s ease-out,
43
- transform 0.1s ease-out;
44
- }
45
- }
46
- details[open] > summary .caret {
47
- opacity: 1;
48
- transform: rotate(90deg);
49
- }
50
-
51
- /* list items */
52
- summary,
53
- li {
54
- margin: 0;
55
- border-radius: 8px;
56
- }
57
- summary,
58
- li a {
59
- cursor: pointer;
60
- padding: var(--stl-sidebar-item-padding-block) var(--stl-sidebar-item-padding-inline);
61
- &:hover,
62
- &[aria-current='page'] {
63
- color: var(--stl-color-foreground);
64
- }
65
- }
66
-
67
- li a {
68
- color: var(--stl-color-foreground-reduced);
69
- font-weight: 400;
70
- display: flex;
71
- text-decoration: none;
72
- span {
73
- font-weight: inherit;
74
- }
75
-
76
- &:hover {
77
- text-decoration: underline;
78
- text-decoration-color: var(--stl-color-foreground-reduced);
79
- }
80
- &[aria-current='page'] {
81
- font-weight: 500;
82
- }
83
- }
84
- li:has(> a:is(:hover, [aria-current='page'])),
85
- summary:hover {
86
- background-color: var(--stl-color-background-hover);
87
- }
88
-
89
- /* nested list items have flat left edge */
90
- ul ul :is(summary, li) {
91
- border-start-start-radius: 0;
92
- border-end-start-radius: 0;
93
- }
94
- ul {
95
- padding: 0;
96
- }
97
- ul ul li {
98
- border-inline-start: 1px solid var(--stl-color-border-faint);
99
- margin-inline-start: var(--stl-sidebar-indent);
100
- &:has(> a[aria-current='page']) {
101
- border-inline-start: 2px solid var(--stl-color-accent-border-strong);
102
- & > a {
103
- margin-left: -1px;
104
- }
105
- }
106
- }
107
-
108
- /* Method & resource icons */
109
-
110
- a[data-stldocs-method],
111
- a[data-stldocs-resource] {
112
- --stl-ui-sidebar-icon-size-outer: 18px;
113
- --stl-ui-sidebar-icon-margin: 8px;
114
- }
115
- a[data-stldocs-method] {
116
- --stl-ui-sidebar-icon-size-inner: 16px;
117
- --stl-ui-sidebar-icon-color-inverse-background: var(--stl-ui-sidebar-icon-color);
118
-
119
- &[data-stldocs-method='get'] {
120
- --stl-ui-sidebar-icon-color: var(--stl-color-green-foreground);
121
- --stl-ui-sidebar-icon-color-border: var(--stl-color-green-border);
122
- --stl-ui-sidebar-icon-color-background: var(--stl-color-green-muted-background);
123
- --stl-ui-sidebar-icon-color-background-hover: var(--stl-color-green-muted-background-hover);
124
- --stl-ui-sidebar-icon-color-inverse-background: var(--stl-color-green-inverse-background);
125
- --stl-ui-sidebar-icon-color-inverse-foreground: var(--stl-color-green-inverse-foreground);
126
- }
127
- &[data-stldocs-method='post'] {
128
- --stl-ui-sidebar-icon-color: var(--stl-color-blue-foreground);
129
- --stl-ui-sidebar-icon-color-border: var(--stl-color-blue-border);
130
- --stl-ui-sidebar-icon-color-background: var(--stl-color-blue-muted-background);
131
- --stl-ui-sidebar-icon-color-background-hover: var(--stl-color-blue-muted-background-hover);
132
- --stl-ui-sidebar-icon-color-inverse-background: var(--stl-color-blue-inverse-background);
133
- --stl-ui-sidebar-icon-color-inverse-foreground: var(--stl-color-blue-inverse-foreground);
134
- }
135
- &[data-stldocs-method='patch'],
136
- &[data-stldocs-method='put'] {
137
- --stl-ui-sidebar-icon-color: var(--stl-color-orange-foreground);
138
- --stl-ui-sidebar-icon-color-border: var(--stl-color-orange-border);
139
- --stl-ui-sidebar-icon-color-background: var(--stl-color-orange-muted-background);
140
- --stl-ui-sidebar-icon-color-background-hover: var(--stl-color-orange-muted-background-hover);
141
- --stl-ui-sidebar-icon-color-inverse-background: var(--stl-color-orange-inverse-background);
142
- --stl-ui-sidebar-icon-color-inverse-foreground: var(--stl-color-orange-inverse-foreground);
143
- }
144
- &[data-stldocs-method='delete'] {
145
- --stl-ui-sidebar-icon-color: var(--stl-color-red-foreground);
146
- --stl-ui-sidebar-icon-color-border: var(--stl-color-red-border);
147
- --stl-ui-sidebar-icon-color-background: var(--stl-color-red-muted-background);
148
- --stl-ui-sidebar-icon-color-background-hover: var(--stl-color-red-muted-background-hover);
149
- --stl-ui-sidebar-icon-color-inverse-background: var(--stl-color-red-inverse-background);
150
- --stl-ui-sidebar-icon-color-inverse-foreground: var(--stl-color-red-inverse-foreground);
151
- }
152
-
153
- &[data-stldocs-method='get'] {
154
- --stl-ui-sidebar-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 7 7 17"/><path d="M17 17H7V7"/></svg>');
155
- }
156
- &[data-stldocs-method='post'],
157
- &[data-stldocs-method='put'],
158
- &[data-stldocs-method='patch'] {
159
- --stl-ui-sidebar-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 7h10v10"/><path d="M7 17 17 7"/></svg>');
160
- }
161
- &[data-stldocs-method='delete'] {
162
- --stl-ui-sidebar-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>');
163
- }
164
- }
165
-
166
- a[data-stldocs-resource] {
167
- /* not yet implemented in markup */
168
- --stl-ui-sidebar-icon-color: var(--stl-color-foreground-muted);
169
- --stl-ui-sidebar-icon-color-border: var(--stl-color-border);
170
- --stl-ui-sidebar-icon-color-background: var(--stl-color-faint-background);
171
- --stl-ui-sidebar-icon-color-background-hover: var(--stl-color-faint-background-hover);
172
- --stl-ui-sidebar-icon-color-inverse-background: var(--stl-color-inverse-background);
173
- --stl-ui-sidebar-icon-color-inverse-foreground: var(--stl-color-inverse-foreground);
174
-
175
- --stl-ui-sidebar-icon-size-inner: 14px;
176
- --stl-ui-sidebar-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1"/><path d="M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1"/></svg>');
177
- }
178
-
179
- a[data-stldocs-method],
180
- a[data-stldocs-resource] {
181
- display: flex;
182
- gap: var(--stl-ui-sidebar-icon-margin);
183
- align-items: center;
184
- position: relative;
185
-
186
- &::before,
187
- &::after {
188
- content: '';
189
- width: var(--stl-ui-sidebar-icon-size-outer);
190
- height: var(--stl-ui-sidebar-icon-size-outer);
191
- display: block;
192
- }
193
- &::before {
194
- border-radius: 4px;
195
- background-color: var(--stl-ui-sidebar-icon-color-background);
196
- flex: 0 0 auto;
197
- border: 1px solid var(--stl-ui-sidebar-icon-color-border, transparent);
198
- }
199
- /* yuck (we can clean this up once we are changing sidebar markup) */
200
- &::after {
201
- background-color: var(--stl-ui-sidebar-icon-color);
202
- mask-image: var(--stl-ui-sidebar-icon-url);
203
- mask-size: var(--stl-ui-sidebar-icon-size-inner) var(--stl-ui-sidebar-icon-size-inner);
204
- mask-repeat: no-repeat;
205
- mask-position: center;
206
- position: absolute;
207
- left: var(--stl-sidebar-item-padding-inline);
208
- top: 50%;
209
- transform: translateY(-50%);
210
- }
211
-
212
- &:hover {
213
- &::before {
214
- background-color: var(--stl-ui-sidebar-icon-color-background-hover);
215
- }
216
- }
217
- &[aria-current='page'] {
218
- &::before {
219
- background-color: var(--stl-ui-sidebar-icon-color-inverse-background);
220
- }
221
- &::after {
222
- background-color: var(--stl-ui-sidebar-icon-color-inverse-foreground);
223
- }
224
- }
225
- }
226
- }
227
-
228
18
  .sidebar-content .stl-mobile-only-sidebar-item-last {
229
19
  margin-bottom: 2rem;
230
20
  position: relative;
package/theme.css CHANGED
@@ -23,3 +23,4 @@
23
23
  @import '@stainless-api/docs-ui/styles/primitives.css';
24
24
  @import '@stainless-api/docs-ui/styles/main.css';
25
25
  @import '@stainless-api/docs-ui/styles/search.css';
26
+ @import '@stainless-api/docs-ui/styles/sidebar.css';