@stainless-api/docs 0.1.0-beta.14 → 0.1.0-beta.16
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 +18 -0
- package/package.json +11 -18
- package/plugin/buildAlgoliaIndex.ts +28 -3
- package/plugin/cms/server.ts +95 -52
- package/plugin/cms/sidebar-builder.ts +0 -9
- package/plugin/globalJs/ai-dropdown-options.ts +161 -0
- package/plugin/globalJs/navigation.ts +0 -22
- package/plugin/index.ts +46 -35
- package/plugin/loadPluginConfig.ts +90 -21
- package/plugin/react/Routing.tsx +18 -28
- package/plugin/routes/Docs.astro +7 -16
- package/plugin/routes/Overview.astro +7 -5
- package/plugin/vendor/preview.worker.docs.js +6357 -6132
- package/resolveSrcFile.ts +10 -0
- package/shared/getSharedLogger.ts +15 -0
- package/shared/terminalUtils.ts +3 -0
- package/stl-docs/components/AIDropdown.tsx +52 -0
- package/stl-docs/components/Head.astro +9 -0
- package/stl-docs/components/PageTitle.astro +67 -0
- package/stl-docs/components/content-panel/ContentPanel.astro +8 -35
- package/stl-docs/components/icons/chat-gpt.tsx +1 -1
- package/stl-docs/components/index.ts +2 -0
- package/stl-docs/disableCalloutSyntax.ts +36 -0
- package/stl-docs/index.ts +61 -18
- package/stl-docs/loadStlDocsConfig.ts +17 -2
- package/stl-docs/proseMarkdown/proseMarkdownIntegration.ts +64 -0
- package/stl-docs/proseMarkdown/proseMarkdownMiddleware.ts +33 -0
- package/stl-docs/proseMarkdown/toMarkdown.ts +158 -0
- package/styles/code.css +0 -5
- package/styles/page.css +31 -4
- package/virtual-module.d.ts +3 -2
- package/plugin/globalJs/ai-dropdown.ts +0 -57
- package/stl-docs/components/APIReferenceAIDropdown.tsx +0 -58
- package/stl-docs/components/content-panel/ProseAIDropdown.tsx +0 -55
- /package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx} +0 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { unified } from 'unified';
|
|
2
|
+
import rehypeParse from 'rehype-parse';
|
|
3
|
+
import rehypeRemark from 'rehype-remark';
|
|
4
|
+
import remarkGfm from 'remark-gfm';
|
|
5
|
+
import remarkStringify from 'remark-stringify';
|
|
6
|
+
import { HTMLElement, parse } from 'node-html-parser';
|
|
7
|
+
|
|
8
|
+
type PaginationLink = {
|
|
9
|
+
href: string;
|
|
10
|
+
label: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type PaginationItems = {
|
|
14
|
+
prev: PaginationLink | null;
|
|
15
|
+
next: PaginationLink | null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function parsePaginationLink(footer: HTMLElement, rel: 'next' | 'prev'): PaginationLink | null {
|
|
19
|
+
const link = footer.querySelector(`.pagination-links a[rel="${rel}"]`);
|
|
20
|
+
if (!link) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const title = link.querySelector('.link-title');
|
|
25
|
+
if (!title) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const href = link.getAttribute('href');
|
|
30
|
+
if (!href) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
href,
|
|
36
|
+
label: title.text,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isRelativeLink(href: string) {
|
|
41
|
+
return href.startsWith('/');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hasExtension(href: string) {
|
|
45
|
+
return href.includes('.');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function removeTrailingSlash(href: string) {
|
|
49
|
+
return href.replace(/\/$/, '');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function makeMarkdownLinks(el: HTMLElement) {
|
|
53
|
+
el.querySelectorAll('a').forEach((a) => {
|
|
54
|
+
const href = a.getAttribute('href');
|
|
55
|
+
if (!href) {
|
|
56
|
+
return a;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (isRelativeLink(href) && !hasExtension(href)) {
|
|
60
|
+
if (href === '/') {
|
|
61
|
+
a.setAttribute('href', '/index.md');
|
|
62
|
+
} else {
|
|
63
|
+
a.setAttribute('href', `${removeTrailingSlash(href)}/index.md`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return a;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function removeHiddenElements(el: HTMLElement) {
|
|
71
|
+
const hiddenSelectors = ['.sl-anchor-link'];
|
|
72
|
+
for (const selector of hiddenSelectors) {
|
|
73
|
+
const hiddenElements = el.querySelectorAll(selector);
|
|
74
|
+
for (const hiddenElement of hiddenElements) {
|
|
75
|
+
hiddenElement.remove();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function toMarkdown(html: string) {
|
|
81
|
+
const root = parse(html);
|
|
82
|
+
|
|
83
|
+
const mainEl = root.querySelector('main');
|
|
84
|
+
|
|
85
|
+
if (!mainEl) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
makeMarkdownLinks(mainEl);
|
|
90
|
+
|
|
91
|
+
const footer = mainEl.querySelector('footer');
|
|
92
|
+
|
|
93
|
+
const markdownContentEl = mainEl.querySelector('.sl-markdown-content');
|
|
94
|
+
if (!markdownContentEl) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
removeHiddenElements(markdownContentEl);
|
|
99
|
+
|
|
100
|
+
const articleContent = markdownContentEl.innerHTML;
|
|
101
|
+
|
|
102
|
+
const paginationLinks: PaginationItems = {
|
|
103
|
+
prev: null,
|
|
104
|
+
next: null,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (footer) {
|
|
108
|
+
paginationLinks.prev = parsePaginationLink(footer, 'prev');
|
|
109
|
+
paginationLinks.next = parsePaginationLink(footer, 'next');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let md = (
|
|
113
|
+
await unified()
|
|
114
|
+
.use(rehypeParse, { fragment: true }) // parse HTML
|
|
115
|
+
.use(rehypeRemark) // rehype (HTML) -> remark (MD AST)
|
|
116
|
+
.use(remarkGfm) // tables, strikethrough, autolinks, etc.
|
|
117
|
+
.use(remarkStringify, {
|
|
118
|
+
fences: true,
|
|
119
|
+
bullet: '-',
|
|
120
|
+
listItemIndent: 'one',
|
|
121
|
+
rule: '-',
|
|
122
|
+
})
|
|
123
|
+
.process(articleContent)
|
|
124
|
+
).toString();
|
|
125
|
+
|
|
126
|
+
const title = root.querySelector('title')?.textContent;
|
|
127
|
+
const description = root.querySelector('meta[name="description"]')?.attributes.content;
|
|
128
|
+
const lastUpdated = root.querySelector('.meta time')?.attributes.datetime;
|
|
129
|
+
|
|
130
|
+
// let htmlUrl = url.toString().replace('.md', '');
|
|
131
|
+
// if (htmlUrl.endsWith('/index')) {
|
|
132
|
+
// htmlUrl = htmlUrl.replace('/index', '');
|
|
133
|
+
// }
|
|
134
|
+
|
|
135
|
+
md = [
|
|
136
|
+
'---',
|
|
137
|
+
`title: ${title}`,
|
|
138
|
+
description ? `description: ${description}` : [],
|
|
139
|
+
lastUpdated ? `lastUpdated: ${lastUpdated}` : [],
|
|
140
|
+
// `source_url:`,
|
|
141
|
+
// ` html: ${htmlUrl}`,
|
|
142
|
+
// ` md: ${url.toString()}`,
|
|
143
|
+
'---\n',
|
|
144
|
+
md,
|
|
145
|
+
]
|
|
146
|
+
.flat()
|
|
147
|
+
.join('\n');
|
|
148
|
+
|
|
149
|
+
if (paginationLinks.prev) {
|
|
150
|
+
md += `\n\n[Previous](${paginationLinks.prev.href})`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (paginationLinks.next) {
|
|
154
|
+
md += `\n\n[Next](${paginationLinks.next.href})`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return md;
|
|
158
|
+
}
|
package/styles/code.css
CHANGED
package/styles/page.css
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
.page {
|
|
14
13
|
position: relative;
|
|
15
14
|
max-width: var(--sl-page-max-width);
|
|
@@ -27,6 +26,10 @@
|
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
28
|
|
|
29
|
+
:root {
|
|
30
|
+
--sl-mobile-toc-height: 4rem;
|
|
31
|
+
}
|
|
32
|
+
|
|
30
33
|
@media (min-width: 50rem) {
|
|
31
34
|
/* on desktop, adjust sidebar so that its _text content_ aligns with the page left edge.
|
|
32
35
|
* padding (visible on hover) bleeds out beyond the page left edge, covered by --stl-ui-page-padding-inline */
|
|
@@ -56,11 +59,35 @@
|
|
|
56
59
|
align-items: center;
|
|
57
60
|
justify-content: space-between;
|
|
58
61
|
gap: 1rem;
|
|
59
|
-
padding: 1rem 0 0
|
|
62
|
+
padding: 1rem 0 0 0;
|
|
63
|
+
flex-wrap: wrap;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.stl-page-nav-container {
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
justify-content: space-between;
|
|
70
|
+
gap: 1rem;
|
|
71
|
+
padding: 1rem 0 0 0;
|
|
60
72
|
flex-wrap: wrap;
|
|
61
73
|
}
|
|
62
|
-
|
|
74
|
+
|
|
75
|
+
.stldocs-root .stl-page-nav-container {
|
|
76
|
+
padding: 1rem 0 0 var(--stldocs-content-padding);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@media (min-width: 50rem) {
|
|
63
80
|
:root[data-has-sidebar] {
|
|
64
|
-
--sl-content-inline-start: calc(var(--sl-sidebar-width) +
|
|
81
|
+
--sl-content-inline-start: calc(var(--sl-sidebar-width) + 2rem);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.stldocs-root .stl-page-nav-container {
|
|
85
|
+
padding: 1rem 0 0 0;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@media (min-width: 72rem) {
|
|
90
|
+
:root {
|
|
91
|
+
--sl-mobile-toc-height: 0rem;
|
|
65
92
|
}
|
|
66
93
|
}
|
package/virtual-module.d.ts
CHANGED
|
@@ -18,7 +18,6 @@ declare module 'virtual:stl-starlight-virtual-module' {
|
|
|
18
18
|
export const PROPERTY_SETTINGS: PropertySettingsType;
|
|
19
19
|
export const MIDDLEWARE: StlStarlightMiddleware;
|
|
20
20
|
export const SEARCH: StainlessStarlightUserConfig['search'];
|
|
21
|
-
export const INCLUDE_AI_DROPDOWN_OPTIONS: boolean;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
declare module 'virtual:stl-docs-virtual-module' {
|
|
@@ -41,5 +40,7 @@ declare module 'virtual:stl-docs-virtual-module' {
|
|
|
41
40
|
export const SPLIT_TABS_ENABLED: boolean;
|
|
42
41
|
export const HEADER_LAYOUT: 'default' | 'stacked';
|
|
43
42
|
export const ENABLE_CLIENT_ROUTER: boolean;
|
|
44
|
-
export const
|
|
43
|
+
export const API_REFERENCE_BASE_PATH: string | null;
|
|
44
|
+
export const ENABLE_PROSE_MARKDOWN_RENDERING: boolean;
|
|
45
|
+
export const ENABLE_CONTEXT_MENU: boolean;
|
|
45
46
|
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export const copyCurrentPageAsMarkdown = async () => {
|
|
2
|
-
const currentUrl = new URL(window.location.href);
|
|
3
|
-
const markdownUrl = `${currentUrl.origin}${currentUrl.pathname}.md`;
|
|
4
|
-
|
|
5
|
-
try {
|
|
6
|
-
const response = await fetch(markdownUrl);
|
|
7
|
-
if (!response.ok) {
|
|
8
|
-
throw new Error('Network response was not ok');
|
|
9
|
-
}
|
|
10
|
-
const markdown = await response.text();
|
|
11
|
-
await navigator.clipboard.writeText(markdown);
|
|
12
|
-
console.log('Markdown copied to clipboard');
|
|
13
|
-
} catch (error) {
|
|
14
|
-
console.error('There has been a problem with your fetch operation:', error);
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export function onSelectAIOption(value: string, makeMarkdownLink: boolean = true) {
|
|
19
|
-
if (value === 'copy-markdown') {
|
|
20
|
-
copyCurrentPageAsMarkdown();
|
|
21
|
-
}
|
|
22
|
-
if (value === 'view-as-markdown') {
|
|
23
|
-
const currentUrl = new URL(window.location.href);
|
|
24
|
-
const markdownUrl = `${currentUrl.origin}${currentUrl.pathname}.md`;
|
|
25
|
-
window.open(markdownUrl, '_blank');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (value === 'chat-gpt') {
|
|
29
|
-
const currentUrl = new URL(window.location.href);
|
|
30
|
-
const llmUrl = makeMarkdownLink ? `${currentUrl.origin}${currentUrl.pathname}.md` : currentUrl.href;
|
|
31
|
-
|
|
32
|
-
const prompt = `Read from ${llmUrl} so I can ask questions about it.`;
|
|
33
|
-
|
|
34
|
-
const chatGptUrl = `https://chatgpt.com/?hints=search&prompt=${encodeURIComponent(prompt)}`;
|
|
35
|
-
window.open(chatGptUrl, '_blank');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (value === 'claude') {
|
|
39
|
-
const currentUrl = new URL(window.location.href);
|
|
40
|
-
const llmUrl = makeMarkdownLink ? `${currentUrl.origin}${currentUrl.pathname}.md` : currentUrl.href;
|
|
41
|
-
|
|
42
|
-
const prompt = `Read from ${llmUrl} so I can ask questions about it.`;
|
|
43
|
-
|
|
44
|
-
const claudeUrl = `https://claude.ai/new?q=${encodeURIComponent(prompt)}`;
|
|
45
|
-
window.open(claudeUrl, '_blank');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (value === 'cursor') {
|
|
49
|
-
const currentUrl = new URL(window.location.href);
|
|
50
|
-
const llmUrl = makeMarkdownLink ? `${currentUrl.origin}${currentUrl.pathname}.md` : currentUrl.href;
|
|
51
|
-
|
|
52
|
-
const prompt = `Read from ${llmUrl} so I can ask questions about it.`;
|
|
53
|
-
|
|
54
|
-
const cursorUrl = `https://www.cursor.so/?prompt=${encodeURIComponent(prompt)}`;
|
|
55
|
-
window.open(cursorUrl, '_blank');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
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 { MarkdownIcon } from './icons/markdown';
|
|
6
|
-
|
|
7
|
-
export function AIDropdownOptions() {
|
|
8
|
-
return (
|
|
9
|
-
<>
|
|
10
|
-
<DropdownButton.MenuItem value="claude" isExternalLink>
|
|
11
|
-
<DropdownButton.MenuItemIcon>
|
|
12
|
-
<ClaudeIcon />
|
|
13
|
-
</DropdownButton.MenuItemIcon>
|
|
14
|
-
<DropdownButton.MenuItemText subtle>
|
|
15
|
-
Open in
|
|
16
|
-
<strong> Claude</strong>
|
|
17
|
-
</DropdownButton.MenuItemText>
|
|
18
|
-
</DropdownButton.MenuItem>
|
|
19
|
-
<DropdownButton.MenuItem value="chat-gpt" isExternalLink>
|
|
20
|
-
<DropdownButton.MenuItemIcon>
|
|
21
|
-
<ChatGPTIcon />
|
|
22
|
-
</DropdownButton.MenuItemIcon>
|
|
23
|
-
<DropdownButton.MenuItemText subtle>
|
|
24
|
-
Open in
|
|
25
|
-
<strong> ChatGPT</strong>
|
|
26
|
-
</DropdownButton.MenuItemText>
|
|
27
|
-
</DropdownButton.MenuItem>
|
|
28
|
-
</>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function APIReferenceAIDropdown() {
|
|
33
|
-
return (
|
|
34
|
-
<DropdownButton id="ai-dropdown-button">
|
|
35
|
-
<DropdownButton.PrimaryAction>
|
|
36
|
-
<CopyIcon size={16} />
|
|
37
|
-
<DropdownButton.PrimaryActionText>Copy Markdown</DropdownButton.PrimaryActionText>
|
|
38
|
-
</DropdownButton.PrimaryAction>
|
|
39
|
-
<DropdownButton.Trigger />
|
|
40
|
-
<DropdownButton.Menu>
|
|
41
|
-
<AIDropdownOptions />
|
|
42
|
-
<hr />
|
|
43
|
-
<DropdownButton.MenuItem value="copy-as-markdown" isExternalLink>
|
|
44
|
-
<DropdownButton.MenuItemIcon>
|
|
45
|
-
<CopyIcon size={16} />
|
|
46
|
-
</DropdownButton.MenuItemIcon>
|
|
47
|
-
<DropdownButton.MenuItemText>Copy Markdown</DropdownButton.MenuItemText>
|
|
48
|
-
</DropdownButton.MenuItem>
|
|
49
|
-
<DropdownButton.MenuItem value="view-as-markdown" isExternalLink>
|
|
50
|
-
<DropdownButton.MenuItemIcon>
|
|
51
|
-
<MarkdownIcon />
|
|
52
|
-
</DropdownButton.MenuItemIcon>
|
|
53
|
-
<DropdownButton.MenuItemText>View as Markdown</DropdownButton.MenuItemText>
|
|
54
|
-
</DropdownButton.MenuItem>
|
|
55
|
-
</DropdownButton.Menu>
|
|
56
|
-
</DropdownButton>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { DropdownButton } from '@stainless-api/ui-primitives';
|
|
2
|
-
import { AIDropdownOptions } from '../APIReferenceAIDropdown';
|
|
3
|
-
import type { StarlightRouteData } from '@astrojs/starlight/route-data';
|
|
4
|
-
import { ClaudeIcon } from '../icons/claude';
|
|
5
|
-
|
|
6
|
-
const sidebarHasEntry = (sidebarEntry: StarlightRouteData['sidebar'], currentPath?: string) => {
|
|
7
|
-
if (!currentPath) return false;
|
|
8
|
-
const normalizePath = (path: string) => {
|
|
9
|
-
return path.endsWith('/') ? path : path + '/';
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const normalizedCurrent = normalizePath(currentPath);
|
|
13
|
-
|
|
14
|
-
for (const entry of sidebarEntry) {
|
|
15
|
-
if (entry.type === 'link') {
|
|
16
|
-
const normalizedHref = normalizePath(entry.href);
|
|
17
|
-
if (normalizedHref === normalizedCurrent) {
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
} else if (entry.type === 'group') {
|
|
21
|
-
const hasInGroup = sidebarHasEntry(entry.entries, currentPath);
|
|
22
|
-
if (hasInGroup) return true;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return false;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export function ProseAIDropdown({
|
|
30
|
-
currentPath,
|
|
31
|
-
sidebarEntry,
|
|
32
|
-
}: {
|
|
33
|
-
currentPath: string;
|
|
34
|
-
sidebarEntry: StarlightRouteData['sidebar'];
|
|
35
|
-
}) {
|
|
36
|
-
// Hide if there is no associated sidebar entry. This applies to pages like the 404 page.
|
|
37
|
-
const hasEntry = sidebarHasEntry(sidebarEntry, currentPath);
|
|
38
|
-
|
|
39
|
-
if (!hasEntry) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<DropdownButton id="prose-ai-dropdown-button">
|
|
45
|
-
<DropdownButton.PrimaryAction>
|
|
46
|
-
<ClaudeIcon />
|
|
47
|
-
Open in Claude
|
|
48
|
-
</DropdownButton.PrimaryAction>
|
|
49
|
-
<DropdownButton.Trigger />
|
|
50
|
-
<DropdownButton.Menu>
|
|
51
|
-
<AIDropdownOptions />
|
|
52
|
-
</DropdownButton.Menu>
|
|
53
|
-
</DropdownButton>
|
|
54
|
-
);
|
|
55
|
-
}
|
/package/stl-docs/components/{content-panel/ContentBreadcrumbs.tsx → ContentBreadcrumbs.tsx}
RENAMED
|
File without changes
|