@stainless-api/docs 0.1.0-beta.7 → 0.1.0-beta.70

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 (120) hide show
  1. package/CHANGELOG.md +554 -0
  2. package/README.md +1 -1
  3. package/eslint-suppressions.json +52 -0
  4. package/locals.d.ts +17 -0
  5. package/package.json +51 -40
  6. package/plugin/assets/languages/csharp.svg +1 -0
  7. package/plugin/buildAlgoliaIndex.ts +32 -7
  8. package/plugin/cms/server.ts +130 -58
  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/SDKSelect.astro +7 -87
  13. package/plugin/components/SnippetCode.tsx +53 -8
  14. package/plugin/components/search/SearchAlgolia.astro +45 -28
  15. package/plugin/components/search/SearchIsland.tsx +38 -24
  16. package/plugin/create-playground.shim.tsx +3 -0
  17. package/plugin/generateAPIReferenceLink.ts +2 -2
  18. package/plugin/globalJs/ai-dropdown-options.ts +243 -0
  19. package/plugin/globalJs/code-snippets.ts +15 -8
  20. package/plugin/globalJs/copy.ts +81 -16
  21. package/plugin/globalJs/method-descriptions.ts +33 -0
  22. package/plugin/globalJs/navigation.ts +7 -4
  23. package/plugin/helpers/generateDocsRoutes.ts +27 -0
  24. package/plugin/index.ts +178 -35
  25. package/plugin/languages.ts +5 -2
  26. package/plugin/loadPluginConfig.ts +121 -32
  27. package/plugin/middlewareBuilder/stainlessMiddleware.d.ts +1 -1
  28. package/plugin/react/Routing.tsx +208 -129
  29. package/plugin/referencePlaceholderUtils.ts +1 -1
  30. package/plugin/replaceSidebarPlaceholderMiddleware.ts +5 -1
  31. package/plugin/routes/Docs.astro +62 -89
  32. package/plugin/routes/DocsStatic.astro +1 -1
  33. package/plugin/routes/Overview.astro +10 -16
  34. package/plugin/routes/markdown.ts +9 -8
  35. package/plugin/vendor/preview.worker.docs.js +19768 -17702
  36. package/plugin/vendor/templates/go.md +1 -1
  37. package/plugin/vendor/templates/python.md +1 -1
  38. package/resolveSrcFile.ts +10 -0
  39. package/scripts/vendor_deps.ts +5 -5
  40. package/shared/getProsePages.ts +42 -0
  41. package/shared/getSharedLogger.ts +15 -0
  42. package/shared/terminalUtils.ts +3 -0
  43. package/src/content.config.ts +9 -0
  44. package/stl-docs/components/AIDropdown.tsx +63 -0
  45. package/stl-docs/components/AiChatIsland.tsx +14 -0
  46. package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +10 -18
  47. package/stl-docs/components/Head.astro +16 -0
  48. package/stl-docs/components/Header.astro +6 -8
  49. package/stl-docs/components/PageFrame.astro +18 -0
  50. package/stl-docs/components/PageTitle.astro +82 -0
  51. package/stl-docs/components/TableOfContents.astro +34 -0
  52. package/stl-docs/components/ThemeProvider.astro +36 -0
  53. package/stl-docs/components/ThemeSelect.astro +84 -139
  54. package/stl-docs/components/content-panel/ContentPanel.astro +16 -25
  55. package/stl-docs/components/headers/SplashMobileMenuToggle.astro +17 -1
  56. package/stl-docs/components/headers/StackedHeader.astro +29 -24
  57. package/stl-docs/components/icons/chat-gpt.tsx +17 -0
  58. package/stl-docs/components/icons/claude.tsx +10 -0
  59. package/stl-docs/components/icons/cursor.tsx +10 -0
  60. package/stl-docs/components/icons/gemini.tsx +19 -0
  61. package/stl-docs/components/icons/markdown.tsx +10 -0
  62. package/stl-docs/components/index.ts +1 -0
  63. package/stl-docs/components/mintlify-compat/Accordion.astro +7 -5
  64. package/stl-docs/components/mintlify-compat/AccordionGroup.astro +7 -3
  65. package/stl-docs/components/mintlify-compat/Columns.astro +40 -42
  66. package/stl-docs/components/mintlify-compat/Frame.astro +16 -18
  67. package/stl-docs/components/mintlify-compat/callouts/Callout.astro +1 -1
  68. package/stl-docs/components/mintlify-compat/callouts/Check.astro +1 -1
  69. package/stl-docs/components/mintlify-compat/callouts/Danger.astro +1 -1
  70. package/stl-docs/components/mintlify-compat/callouts/Info.astro +1 -1
  71. package/stl-docs/components/mintlify-compat/callouts/Note.astro +1 -1
  72. package/stl-docs/components/mintlify-compat/callouts/Tip.astro +1 -1
  73. package/stl-docs/components/mintlify-compat/callouts/Warning.astro +1 -1
  74. package/stl-docs/components/mintlify-compat/card.css +33 -35
  75. package/stl-docs/components/mintlify-compat/index.ts +2 -4
  76. package/stl-docs/components/nav-tabs/NavDropdown.astro +31 -70
  77. package/stl-docs/components/nav-tabs/NavTabs.astro +78 -80
  78. package/stl-docs/components/nav-tabs/SecondaryNavTabs.astro +15 -8
  79. package/stl-docs/components/nav-tabs/buildNavLinks.ts +3 -2
  80. package/stl-docs/components/pagination/HomeLink.astro +10 -0
  81. package/stl-docs/components/pagination/Pagination.astro +175 -0
  82. package/stl-docs/components/pagination/PaginationLinkEmphasized.astro +22 -0
  83. package/stl-docs/components/pagination/PaginationLinkQuiet.astro +13 -0
  84. package/stl-docs/components/pagination/util.ts +71 -0
  85. package/stl-docs/components/scripts.ts +1 -0
  86. package/stl-docs/disableCalloutSyntax.ts +36 -0
  87. package/stl-docs/index.ts +141 -50
  88. package/stl-docs/loadStlDocsConfig.ts +45 -5
  89. package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +61 -0
  90. package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +39 -0
  91. package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
  92. package/stl-docs/proseSearchIndexing.ts +450 -0
  93. package/stl-docs/tabsMiddleware.ts +11 -3
  94. package/styles/code.css +108 -140
  95. package/styles/fonts.css +32 -17
  96. package/styles/links.css +11 -48
  97. package/styles/method-descriptions.css +36 -0
  98. package/styles/overrides.css +48 -60
  99. package/styles/page.css +92 -52
  100. package/styles/sdk_select.css +9 -7
  101. package/styles/search.css +56 -69
  102. package/styles/sidebar.css +211 -131
  103. package/styles/{variables.css → sl-variables.css} +3 -2
  104. package/styles/stldocs-variables.css +6 -0
  105. package/styles/toc.css +41 -34
  106. package/theme.css +10 -10
  107. package/tsconfig.json +2 -5
  108. package/virtual-module.d.ts +26 -4
  109. package/components/variables.css +0 -135
  110. package/stl-docs/components/mintlify-compat/Step.astro +0 -58
  111. package/stl-docs/components/mintlify-compat/Steps.astro +0 -17
  112. /package/{plugin/assets → assets}/fonts/geist/OFL.txt +0 -0
  113. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin-ext.woff2 +0 -0
  114. /package/{plugin/assets → assets}/fonts/geist/geist-italic-latin.woff2 +0 -0
  115. /package/{plugin/assets → assets}/fonts/geist/geist-latin-ext.woff2 +0 -0
  116. /package/{plugin/assets → assets}/fonts/geist/geist-latin.woff2 +0 -0
  117. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin-ext.woff2 +0 -0
  118. /package/{plugin/assets → assets}/fonts/geist/geist-mono-italic-latin.woff2 +0 -0
  119. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin-ext.woff2 +0 -0
  120. /package/{plugin/assets → assets}/fonts/geist/geist-mono-latin.woff2 +0 -0
@@ -31,7 +31,7 @@ SDK_ReleasePleaseMarkdownBlockEnd
31
31
 
32
32
  ## Requirements
33
33
 
34
- This library requires Go 1.18+.
34
+ This library requires Go 1.22+.
35
35
 
36
36
  ## Usage
37
37
 
@@ -242,7 +242,7 @@ print(SDK_PythonPackageImportName.__version__)
242
242
 
243
243
  ## Requirements
244
244
 
245
- Python 3.8 or higher.
245
+ Python 3.9 or higher.
246
246
 
247
247
  ## Contributing
248
248
 
@@ -0,0 +1,10 @@
1
+ import { join } from 'path';
2
+ const dirName = import.meta.dirname;
3
+ /**
4
+ * Resolve a source file path relative to the stl-starlight package.
5
+ * @param projectPath - The relative path to the source file.
6
+ * @returns The absolute path to the source file.
7
+ */
8
+ export function resolveSrcFile(...projectPath: string[]) {
9
+ return join(dirName, ...projectPath);
10
+ }
@@ -3,7 +3,7 @@ import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { z } from 'zod';
5
5
 
6
- import { Languages } from '@stainless-api/docs-ui/src/routing';
6
+ import { Languages } from '@stainless-api/docs-ui/routing';
7
7
 
8
8
  const DEPS_DIR = './plugin/vendor';
9
9
 
@@ -11,12 +11,12 @@ config();
11
11
 
12
12
  const envSchema = z.object({
13
13
  // absolute path to the Stainless monorepo root
14
- STAINLESS_MONOREPO_PATH: z.string(),
14
+ STAINLESS_ROOT: z.string(),
15
15
  });
16
16
 
17
17
  const env = envSchema.parse(process.env);
18
18
 
19
- console.log(`Using Stainless monorepo at ${env.STAINLESS_MONOREPO_PATH}`);
19
+ console.log(`Using Stainless monorepo at ${env.STAINLESS_ROOT}`);
20
20
 
21
21
  if (fs.existsSync(DEPS_DIR)) {
22
22
  fs.rmSync(DEPS_DIR, { recursive: true });
@@ -25,11 +25,11 @@ if (fs.existsSync(DEPS_DIR)) {
25
25
  fs.mkdirSync(DEPS_DIR, { recursive: true });
26
26
 
27
27
  const previewWorkerPath = path.join(
28
- env.STAINLESS_MONOREPO_PATH,
28
+ env.STAINLESS_ROOT,
29
29
  '/packages/preview-worker/dist/stainless/preview.worker.docs.js',
30
30
  );
31
31
 
32
- const readmeTemplatePath = path.join(env.STAINLESS_MONOREPO_PATH, '/legacy-dir-root/templates');
32
+ const readmeTemplatePath = path.join(env.STAINLESS_ROOT, '/legacy-dir-root/templates');
33
33
 
34
34
  fs.copyFileSync(previewWorkerPath, path.join(DEPS_DIR, 'preview.worker.docs.js'));
35
35
 
@@ -0,0 +1,42 @@
1
+ import { readdir } from 'fs/promises';
2
+ import { join, relative } from 'path';
3
+
4
+ /**
5
+ * Get all prose pages after a build, by reading all HTML files from the
6
+ * given output directory, and not from assets.
7
+ *
8
+ * We cannot use the `assets` map here because it is not guaranteed to
9
+ * contain all files, especially if they were generated by other integrations.
10
+ * Other astro integrations may hijack the "[...slug]" entrypoint, and any files
11
+ * previously in the [...slug] asset map entry would be lost (this is where starlight stores
12
+ * its prose HTML files).
13
+ */
14
+ export async function getProsePages({
15
+ apiReferenceBasePath,
16
+ outputBasePath,
17
+ }: {
18
+ apiReferenceBasePath: string | null;
19
+ outputBasePath: string;
20
+ }): Promise<string[]> {
21
+ const allFiles = await readdir(outputBasePath, {
22
+ recursive: true,
23
+ withFileTypes: true,
24
+ });
25
+
26
+ const htmlFiles = allFiles
27
+ .filter((file) => file.isFile() && file.name.endsWith('.html'))
28
+ .map((file) => join(file.parentPath, file.name));
29
+
30
+ // Filter out API reference pages
31
+ const pagesToRender = htmlFiles.filter((absPath) => {
32
+ if (!apiReferenceBasePath) {
33
+ return true;
34
+ }
35
+ const relPath = relative(outputBasePath, absPath);
36
+ // Normalize by removing leading/trailing slashes from apiReferenceBasePath
37
+ const normalizedApiPath = apiReferenceBasePath.replace(/^\/+|\/+$/g, '');
38
+ return !relPath.startsWith(normalizedApiPath);
39
+ });
40
+
41
+ return pagesToRender;
42
+ }
@@ -0,0 +1,15 @@
1
+ import { AstroIntegrationLogger } from 'astro';
2
+
3
+ let sharedLogger: AstroIntegrationLogger | null = null;
4
+
5
+ // This is probably temporary, but it's a quick way to share a logger between our many integrations
6
+ // we want to share a logger so they have the same "stainless" label
7
+
8
+ export function setSharedLogger(logger: AstroIntegrationLogger) {
9
+ sharedLogger = logger;
10
+ }
11
+
12
+ // a fallback is probably not required, but it's a good safeguard in case we somehow call a logger before the shared logger is set
13
+ export function getSharedLogger({ fallback }: { fallback: AstroIntegrationLogger }) {
14
+ return sharedLogger ?? fallback;
15
+ }
@@ -0,0 +1,3 @@
1
+ export function bold(text: string) {
2
+ return `\x1b[1m${text}\x1b[0m`;
3
+ }
@@ -0,0 +1,9 @@
1
+ // Stub here so that `astro sync` generates the correct types for the `docs` content collection
2
+
3
+ import { defineCollection } from 'astro:content';
4
+ import { docsLoader } from '@astrojs/starlight/loaders';
5
+ import { docsSchema } from '@astrojs/starlight/schema';
6
+
7
+ export const collections = {
8
+ docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
9
+ };
@@ -0,0 +1,63 @@
1
+ import { DropdownButton } from '@stainless-api/ui-primitives';
2
+ import { CopyIcon } from 'lucide-react';
3
+ import { ChatGPTIcon } from './icons/chat-gpt';
4
+ import { ClaudeIcon } from './icons/claude';
5
+ import { GeminiIcon } from './icons/gemini';
6
+ import { MarkdownIcon } from './icons/markdown';
7
+ import { CursorIcon } from './icons/cursor';
8
+ import React from 'react';
9
+
10
+ import { getAIDropdownOptions, type DropdownIcon } from '../../plugin/globalJs/ai-dropdown-options';
11
+
12
+ const iconMap: { [key in DropdownIcon]: React.ReactNode } = {
13
+ markdown: <MarkdownIcon />,
14
+ copy: <CopyIcon size={16} />,
15
+ claude: <ClaudeIcon />,
16
+ chatgpt: <ChatGPTIcon />,
17
+ gemini: <GeminiIcon />,
18
+ cursor: <CursorIcon />,
19
+ };
20
+
21
+ const { primaryAction, groups } = getAIDropdownOptions();
22
+
23
+ function ItemLabel({ label }: { label: string[] }) {
24
+ if (label.length > 1) {
25
+ return (
26
+ <DropdownButton.MenuItemText subtle>
27
+ {label[0]}
28
+ <strong>{label[1]}</strong>
29
+ </DropdownButton.MenuItemText>
30
+ );
31
+ }
32
+
33
+ return (
34
+ <DropdownButton.MenuItemText>
35
+ <strong>{label[0]}</strong>
36
+ </DropdownButton.MenuItemText>
37
+ );
38
+ }
39
+
40
+ export function AIDropdown() {
41
+ return (
42
+ <DropdownButton data-dropdown-id="ai-dropdown-button">
43
+ <DropdownButton.PrimaryAction>
44
+ {iconMap[primaryAction.icon]}
45
+ <DropdownButton.PrimaryActionText>{primaryAction.label}</DropdownButton.PrimaryActionText>
46
+ </DropdownButton.PrimaryAction>
47
+ <DropdownButton.Trigger />
48
+ <DropdownButton.Menu>
49
+ {groups.map((group) => (
50
+ <React.Fragment key={group.reactKey}>
51
+ {group.options.map((option) => (
52
+ <DropdownButton.MenuItem value={option.id} isExternalLink={option.external} key={option.id}>
53
+ <DropdownButton.Icon>{iconMap[option.icon]}</DropdownButton.Icon>
54
+ <ItemLabel label={option.label} />
55
+ </DropdownButton.MenuItem>
56
+ ))}
57
+ {group.isLast ? null : <hr />}
58
+ </React.Fragment>
59
+ ))}
60
+ </DropdownButton.Menu>
61
+ </DropdownButton>
62
+ );
63
+ }
@@ -0,0 +1,14 @@
1
+ import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
2
+ import AiChat, { STAINLESS_PROJECT } from 'virtual:stl-docs/components/AiChat.tsx';
3
+
4
+ export default function AiChatIsland({
5
+ currentLanguage,
6
+ siteTitle,
7
+ }: {
8
+ currentLanguage: DocsLanguage | undefined;
9
+ siteTitle: string | undefined;
10
+ }) {
11
+ if (!AiChat) throw new Error('AiChatIsland was rendered but could not load AiChat component');
12
+ if (!STAINLESS_PROJECT) return null;
13
+ return <AiChat projectId={STAINLESS_PROJECT} language={currentLanguage} siteTitle={siteTitle} />;
14
+ }
@@ -1,29 +1,26 @@
1
1
  import { ChevronRight } from 'lucide-react';
2
- import { Join } from '@stainless-api/docs-ui/src/components';
3
- import style from '@stainless-api/docs-ui/src/style';
2
+ import { Join } from '@stainless-api/docs-ui/components';
3
+ import style from '@stainless-api/docs-ui/style';
4
4
  import type { StarlightRouteData } from '@astrojs/starlight/route-data';
5
5
 
6
6
  type Breadcrumb = { title: string; href: string };
7
7
 
8
- // because this component is used in Stainless Docs and stl-starlight, we need to conditionally import the BASE_PATH from the virtual module
9
- let basePath: string | null = null;
10
- try {
11
- const mod = await import('virtual:stl-starlight-virtual-module');
12
- basePath = mod.BASE_PATH;
13
- // eslint-disable-next-line no-empty, @typescript-eslint/no-unused-vars
14
- } catch (_e) {}
15
-
16
8
  // Normalize paths to avoid mismatches due to trailing slashes
17
9
  function normalizePath(path: string) {
18
10
  return path.endsWith('/') ? path : path + '/';
19
11
  }
20
12
 
21
- function findBreadcrumbTrail(
22
- entries: StarlightRouteData['sidebar'],
13
+ export function findBreadcrumbTrail(
14
+ sidebarEntry: StarlightRouteData['sidebar'],
23
15
  targetPath: string,
24
16
  includeCurrentPage: boolean,
25
17
  trail: Breadcrumb[] = [],
26
18
  ): Breadcrumb[] | null {
19
+ const entries = sidebarEntry.filter((entry) => {
20
+ return !(
21
+ entry as StarlightRouteData['sidebar'][number] & { attrs?: { class?: string } }
22
+ ).attrs?.class?.includes('stl-mobile-only-sidebar-item');
23
+ });
27
24
  const normalizedTarget = normalizePath(targetPath);
28
25
 
29
26
  for (const entry of entries) {
@@ -57,12 +54,7 @@ export function ContentBreadcrumbs({
57
54
  currentPath: string;
58
55
  sidebarEntry: StarlightRouteData['sidebar'];
59
56
  }) {
60
- if (basePath && currentPath.startsWith(basePath)) {
61
- return null;
62
- }
63
-
64
- const entryWithoutTopLevelLinks = sidebarEntry.filter((entry) => entry.type !== 'link');
65
- const breadcrumbs = findBreadcrumbTrail(entryWithoutTopLevelLinks, currentPath, true);
57
+ const breadcrumbs = findBreadcrumbTrail(sidebarEntry, currentPath, true);
66
58
 
67
59
  if (!breadcrumbs) {
68
60
  return null;
@@ -0,0 +1,16 @@
1
+ ---
2
+ import Default from '@astrojs/starlight/components/Head.astro';
3
+
4
+ // TODO: for users who are overriding the font stack in their own styles, how can we know that and
5
+ // preload their font instead of ours?
6
+ import geistPath from '../../assets/fonts/geist/geist-latin.woff2';
7
+ ---
8
+
9
+ <Default />
10
+
11
+ <link rel="preload" as="font" type="font/woff2" crossorigin="anonymous" href={geistPath} />
12
+
13
+ <script>
14
+ import { wireAIDropdown } from '../../plugin/globalJs/ai-dropdown-options.ts';
15
+ wireAIDropdown();
16
+ </script>
@@ -23,12 +23,9 @@ const shouldRenderSearch = !!(
23
23
 
24
24
  <style is:global>
25
25
  @layer starlight.core {
26
- header {
27
- border-color: var(--sl-color-hairline);
28
-
29
- &.header {
30
- border-bottom-color: var(--sl-color-hairline);
31
- }
26
+ header,
27
+ header.header {
28
+ border-color: var(--stl-color-border-faint);
32
29
  }
33
30
 
34
31
  .header {
@@ -44,9 +41,10 @@ const shouldRenderSearch = !!(
44
41
  overflow: clip;
45
42
  /* Avoid clipping focus ring around link inside title wrapper. */
46
43
  padding: 0.25rem;
47
- padding-left: 0;
48
44
  margin: -0.25rem;
49
45
  min-width: 0;
46
+ /* Max height should match button height */
47
+ max-height: 32px;
50
48
  }
51
49
 
52
50
  .default-tabs-container {
@@ -84,7 +82,7 @@ const shouldRenderSearch = !!(
84
82
 
85
83
  @media (min-width: 50rem) {
86
84
  .default-tabs-container {
87
- min-width: 440px;
85
+ min-width: 540px;
88
86
  }
89
87
  }
90
88
  }
@@ -0,0 +1,18 @@
1
+ ---
2
+ import Default from '@astrojs/starlight/components/PageFrame.astro';
3
+ import AiChat from 'virtual:stl-docs/components/AiChat.tsx'; // conditionally resolves to null if ai chat module is not injected
4
+ import AiChatIsland from './AiChatIsland.tsx'; // entrypoint for client island can’t be a virtual module
5
+
6
+ import starlightConfig from 'virtual:starlight/user-config';
7
+ const locale = Astro.currentLocale ?? starlightConfig.defaultLocale.lang;
8
+ const siteTitle = locale && starlightConfig.title[locale];
9
+ ---
10
+
11
+ <Default>
12
+ <slot name="header" slot="header" />
13
+ <slot name="sidebar" slot="sidebar" />
14
+
15
+ <slot />
16
+
17
+ {!!AiChat && <AiChatIsland client:load currentLanguage={Astro.locals.language} siteTitle={siteTitle} />}
18
+ </Default>
@@ -0,0 +1,82 @@
1
+ ---
2
+ import {
3
+ ENABLE_PROSE_MARKDOWN_RENDERING,
4
+ ENABLE_CONTEXT_MENU,
5
+ RENDER_PAGE_DESCRIPTIONS,
6
+ } from 'virtual:stl-docs-virtual-module';
7
+ import type { StarlightRouteData } from '@astrojs/starlight/route-data';
8
+ import Default from '@astrojs/starlight/components/PageTitle.astro';
9
+ import { ContentBreadcrumbs } from './ContentBreadcrumbs';
10
+ import { AIDropdown } from './AIDropdown';
11
+
12
+ function sidebarHasEntry(sidebarEntry: StarlightRouteData['sidebar'], currentPath?: string) {
13
+ if (!currentPath) return false;
14
+ const normalizePath = (path: string) => {
15
+ return path.endsWith('/') ? path : path + '/';
16
+ };
17
+
18
+ const normalizedCurrent = normalizePath(currentPath);
19
+
20
+ for (const entry of sidebarEntry) {
21
+ if (entry.type === 'link') {
22
+ const normalizedHref = normalizePath(entry.href);
23
+ if (normalizedHref === normalizedCurrent) {
24
+ return true;
25
+ }
26
+ } else if (entry.type === 'group') {
27
+ const hasInGroup = sidebarHasEntry(entry.entries, currentPath);
28
+ if (hasInGroup) return true;
29
+ }
30
+ }
31
+
32
+ return false;
33
+ }
34
+
35
+ const skipRenderingStarlightTitle = Astro.locals._stlStarlightPage?.skipRenderingStarlightTitle ?? false;
36
+
37
+ const hasMarkdownRoute = Astro.locals._stlStarlightPage?.hasMarkdownRoute ?? true;
38
+ // ⬆️ hopefully this is temporary. But for now, we don't have a markdown representation of the API reference overview page
39
+
40
+ const currentPath = Astro.url.pathname;
41
+
42
+ function shouldShowAIDropdown() {
43
+ // Hide if there is no associated sidebar entry. This applies to pages like the 404 page.
44
+ const hasSidebarEntry = sidebarHasEntry(Astro.locals.starlightRoute.sidebar, currentPath);
45
+ if (!hasSidebarEntry) return false;
46
+
47
+ if (!hasMarkdownRoute) return false;
48
+
49
+ if (!ENABLE_PROSE_MARKDOWN_RENDERING) return false;
50
+ if (!ENABLE_CONTEXT_MENU) return false;
51
+
52
+ return true;
53
+ }
54
+
55
+ const showAIDropdown = shouldShowAIDropdown();
56
+
57
+ const description = Astro.locals.starlightRoute.entry.data.description;
58
+ const shouldRenderDescription = RENDER_PAGE_DESCRIPTIONS;
59
+ ---
60
+
61
+ {
62
+ skipRenderingStarlightTitle ? null : (
63
+ <>
64
+ <div class="stl-ui-not-prose stl-page-nav-container stl-prose-page-nav-container">
65
+ <ContentBreadcrumbs currentPath={currentPath} sidebarEntry={Astro.locals.starlightRoute.sidebar} />
66
+ {showAIDropdown && <AIDropdown />}
67
+ </div>
68
+ <Default />
69
+ {!!description && shouldRenderDescription && <p class="description">{description}</p>}
70
+ </>
71
+ )
72
+ }
73
+
74
+ <style>
75
+ p.description {
76
+ font-size: var(--stl-typography-scale-xl);
77
+ font-weight: 400;
78
+ line-height: 1.4;
79
+ letter-spacing: -0.02em;
80
+ margin-top: 0.4em;
81
+ }
82
+ </style>
@@ -0,0 +1,34 @@
1
+ ---
2
+ import Default from '@astrojs/starlight/components/TableOfContents.astro';
3
+ ---
4
+
5
+ <Default {...Astro.props} />
6
+
7
+ <script>
8
+ /**
9
+ * Starlight will mark TOC links as active as they are scrolled to. However, it does not mark
10
+ * a link as active if it cannot be scrolled to on the page. This happens for small sections at
11
+ * the bottom of the page which are not large enough to scroll to the top of the viewport.
12
+ *
13
+ * This script will mark a section as active if its corresponding hash is in the URL, even if
14
+ * it cannot be scrolled to.
15
+ */
16
+ document.addEventListener('DOMContentLoaded', () => {
17
+ const tocLinks = document.querySelectorAll('starlight-toc a') as NodeListOf<HTMLAnchorElement>;
18
+
19
+ function updateAriaCurrent() {
20
+ const current = decodeURIComponent(window.location.hash || '');
21
+ tocLinks.forEach((link) => {
22
+ const linkHash = decodeURIComponent(link.hash || link.getAttribute('href') || '');
23
+ if (linkHash === current) {
24
+ link.setAttribute('aria-current', 'true');
25
+ } else {
26
+ link.removeAttribute('aria-current');
27
+ }
28
+ });
29
+ }
30
+
31
+ window.addEventListener('hashchange', updateAriaCurrent);
32
+ updateAriaCurrent();
33
+ });
34
+ </script>
@@ -0,0 +1,36 @@
1
+ {/* Inlined to avoid FOUC. All theme selects are initialized here */}
2
+ <script is:inline>
3
+ window.didInitThemePickers = window.didInitThemePickers ?? false;
4
+
5
+ // Only run once.
6
+ if (!window.didInitThemePickers) {
7
+ window.didInitThemePickers = true;
8
+
9
+ // The stored theme will be either 'auto', 'light', 'dark' or null.
10
+ const storedTheme = typeof localStorage !== 'undefined' && localStorage.getItem('starlight-theme');
11
+
12
+ // This theme is either 'light' or 'dark'. It's used for setting the data-theme attribute.
13
+ const theme =
14
+ !storedTheme || storedTheme === 'auto'
15
+ ? window.matchMedia('(prefers-color-scheme: light)').matches
16
+ ? 'light'
17
+ : 'dark'
18
+ : storedTheme;
19
+
20
+ document.documentElement.dataset.theme = theme === 'light' ? 'light' : 'dark';
21
+
22
+ const themeSelects = document.querySelectorAll('[data-theme-select]');
23
+ const selectedThemeValue = storedTheme || 'auto';
24
+
25
+ themeSelects.forEach((select) => {
26
+ const tmpl = select.querySelector(`[data-value="${selectedThemeValue}"] template`);
27
+ const selectedSlot = select.querySelector('[data-part="trigger-selected"]');
28
+
29
+ selectedSlot.innerHTML = '';
30
+ if (tmpl) {
31
+ selectedSlot.appendChild(tmpl.content.cloneNode(true));
32
+ return;
33
+ }
34
+ });
35
+ }
36
+ </script>