Skip to content

Commit ec64b00

Browse files
authored
v0.9.31: mothership activity headers, deploy reliability
2 parents 27b5356 + 6381473 commit ec64b00

91 files changed

Lines changed: 4010 additions & 1065 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/docs-negotiation.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ jobs:
3535
DOCS_E2E_BASE_URL: http://127.0.0.1:3001
3636
DOCS_NEGOTIATION_REPORT_PATH: ${{ github.workspace }}/test-results/docs-negotiation.json
3737
DOCS_HEADER_REPORT_PATH: ${{ github.workspace }}/test-results/docs-headers.json
38+
DOCS_NAVIGATION_REPORT_PATH: ${{ github.workspace }}/test-results/navigation/docs.json
3839
steps:
3940
- name: Checkout code
4041
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
@@ -46,13 +47,16 @@ jobs:
4647
run: bun run --cwd apps/docs build
4748
- name: Verify native header edge cases
4849
run: bun run --cwd apps/docs test:headers:e2e
50+
- name: Install browser for navigation checks
51+
run: bunx --no-install playwright install --with-deps chromium
4952
- name: Verify HTTP content negotiation
5053
run: |
5154
mkdir -p test-results
5255
bun run --cwd apps/docs start --port 3001 >test-results/docs-server.log 2>&1 &
5356
DOCS_SERVER_PID=$!
5457
trap 'kill "$DOCS_SERVER_PID" 2>/dev/null || true' EXIT
5558
bun run --cwd apps/docs test:negotiation:e2e
59+
bun run --cwd apps/docs test:navigation:e2e
5660
- name: Upload HTTP regression report
5761
if: always()
5862
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2

‎apps/docs/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ Use the owner and generation commands listed in the editorial guide. Do not hand
2525
## Validate
2626

2727
From the repository root, run the required lint, type, audit, and test gates in the development guidelines. From this directory, `bun run type-check` compiles MDX and checks the docs workspace, and `bun run build` validates the production site. Inspect affected page types on desktop and a narrow viewport, and check internal links and existing anchors after heading changes.
28+
29+
The docs HTTP workflow also checks sidebar navigation, API language switching, and copying Markdown on desktop and mobile layouts. It uploads screenshots and a JSON report with the HTTP regression artifacts. These checks run against the built site in CI; no visitor telemetry is collected. Use Vercel's build logs and usage reports to compare deployment cost and duration.

‎apps/docs/app/[[...slug]]/page.tsx‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { CodeBlock } from '@/components/ui/code-block'
2626
import { Heading } from '@/components/ui/heading'
2727
import { IntegrationIcon } from '@/components/ui/integration-icon'
2828
import { ResponseSection } from '@/components/ui/response-section'
29-
import { getApiSpecContent, getAuthenticatedCodeSamples, openapi } from '@/lib/openapi'
29+
import { getAuthenticatedCodeSamples, openapi } from '@/lib/openapi'
3030
import { simShikiOptions } from '@/lib/shiki-theme'
3131
import { type PageData, source } from '@/lib/source'
3232
import { DOCS_BASE_URL } from '@/lib/urls'
@@ -266,11 +266,6 @@ export default async function Page(props: { params: Promise<{ slug?: string[] }>
266266

267267
if (isOpenAPI && data.getAPIPageProps) {
268268
const apiProps = data.getAPIPageProps()
269-
const apiPageContent = getApiSpecContent(
270-
data.title,
271-
data.description,
272-
apiProps.operations ?? []
273-
)
274269

275270
return (
276271
<>
@@ -306,7 +301,10 @@ export default async function Page(props: { params: Promise<{ slug?: string[] }>
306301
>
307302
<PageHeader title={data.title} className='api-page-header'>
308303
<div className='hidden sm:flex'>
309-
<LLMCopyButton content={apiPageContent} />
304+
<LLMCopyButton
305+
key={page.url}
306+
markdownUrl={`/llms.mdx${page.url === '/' ? '' : page.url}`}
307+
/>
310308
</div>
311309
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
312310
</PageHeader>
@@ -318,23 +316,22 @@ export default async function Page(props: { params: Promise<{ slug?: string[] }>
318316
)
319317
}
320318

321-
const MDX = data.body
322-
const markdownContent = await data.getText('processed')
319+
const { body: MDX, toc, lastModified } = await data.load()
323320

324321
return (
325322
<>
326323
<StructuredData
327324
title={data.title}
328325
description={data.description || ''}
329326
url={`${BASE_URL}${page.url}`}
330-
dateModified={data.lastModified?.toISOString()}
327+
dateModified={lastModified?.toISOString()}
331328
breadcrumb={breadcrumbs}
332329
/>
333330
<DocsPage
334331
className={DOCS_PAGE_CLASS_NAME}
335332
role='main'
336333
tabIndex={-1}
337-
toc={data.toc.filter(
334+
toc={toc.filter(
338335
(item) => isContentHeading(item) && (slug?.[0] !== 'integrations' || item.depth <= 3)
339336
)}
340337
full={data.full}
@@ -359,7 +356,10 @@ export default async function Page(props: { params: Promise<{ slug?: string[] }>
359356
className='max-sm:[&>h1]:basis-0 max-sm:[&_a]:size-10'
360357
>
361358
<div className='hidden sm:flex'>
362-
<LLMCopyButton content={markdownContent} />
359+
<LLMCopyButton
360+
key={page.url}
361+
markdownUrl={`/llms.mdx${page.url === '/' ? '' : page.url}`}
362+
/>
363363
</div>
364364
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
365365
</PageHeader>

‎apps/docs/app/sitemap.ts‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import { DOCS_BASE_URL } from '@/lib/urls'
44

55
export const revalidate = 3600
66

7-
export default function sitemap(): MetadataRoute.Sitemap {
7+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
88
// The docs root redirects to /introduction, which is listed on its own.
9-
return source
10-
.getPages()
11-
.filter((page) => page.url !== '/')
12-
.map((page) => ({
9+
const entries: MetadataRoute.Sitemap = []
10+
for (const page of source.getPages()) {
11+
if (page.url === '/') continue
12+
const data = page.data.type === 'docs' ? await page.data.load() : undefined
13+
entries.push({
1314
url: `${DOCS_BASE_URL}${page.url}`,
14-
lastModified: 'lastModified' in page.data ? page.data.lastModified : undefined,
15-
}))
15+
lastModified: data?.lastModified,
16+
})
17+
}
18+
return entries
1619
}

‎apps/docs/components/docs-layout/sidebar-components.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export function SidebarFolder({ item, children }: SidebarFolderProps) {
112112
)}
113113
{hasChildren && (
114114
<div id={contentId} hidden={!open}>
115-
<div className='mt-px ml-4 flex flex-col gap-px'>{children}</div>
115+
{open && <div className='mt-px ml-4 flex flex-col gap-px'>{children}</div>}
116116
</div>
117117
)}
118118
</div>
Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,68 @@
11
'use client'
22

3-
import { Chip } from '@sim/emcn'
3+
import { useRef, useState } from 'react'
4+
import { Chip, useCopyToClipboard } from '@sim/emcn'
45
import { Check, Duplicate } from '@sim/emcn/icons'
5-
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button'
66

7-
export function LLMCopyButton({ content }: { content: string }) {
8-
const [checked, onClick] = useCopyButton(() => navigator.clipboard.writeText(content))
7+
interface LLMCopyButtonProps {
8+
markdownUrl: string
9+
}
10+
11+
export function LLMCopyButton({ markdownUrl }: LLMCopyButtonProps) {
12+
const { copied, copy } = useCopyToClipboard()
13+
const [status, setStatus] = useState<'idle' | 'pending' | 'ready' | 'failed'>('idle')
14+
const preparedMarkdown = useRef<string | null>(null)
15+
const pending = status === 'pending'
16+
const checked = copied && status === 'idle'
17+
18+
async function onClick() {
19+
setStatus('pending')
20+
const prepare = async () => {
21+
const response = await fetch(markdownUrl, {
22+
signal: typeof AbortSignal.timeout === 'function' ? AbortSignal.timeout(10000) : undefined,
23+
})
24+
if (!response.ok) throw new Error('Could not load page Markdown')
25+
const markdown = await response.text()
26+
if (!markdown.trim()) throw new Error('Page Markdown is empty')
27+
return markdown
28+
}
29+
try {
30+
// Promise-backed writes preserve Safari's transient user activation during the fetch.
31+
if (typeof ClipboardItem !== 'undefined' && typeof navigator.clipboard.write === 'function') {
32+
const success = await copy({ prepare, fallback: '' })
33+
setStatus(success ? 'idle' : 'failed')
34+
return
35+
}
36+
if (preparedMarkdown.current === null) {
37+
preparedMarkdown.current = await prepare()
38+
setStatus('ready')
39+
return
40+
}
41+
const success = await copy(preparedMarkdown.current)
42+
setStatus(success ? 'idle' : 'failed')
43+
} catch {
44+
setStatus('failed')
45+
}
46+
}
947

1048
return (
11-
<Chip
12-
onClick={onClick}
13-
leftIcon={checked ? Check : Duplicate}
14-
aria-label={checked ? 'Copied to clipboard' : 'Copy page content'}
15-
>
16-
{checked ? 'Copied' : 'Copy page'}
17-
</Chip>
49+
<div className='relative'>
50+
<Chip
51+
onClick={onClick}
52+
disabled={pending}
53+
aria-busy={pending}
54+
leftIcon={checked ? Check : Duplicate}
55+
aria-label={checked ? 'Copied to clipboard' : 'Copy page content'}
56+
>
57+
{checked ? 'Copied' : pending ? 'Copying…' : 'Copy page'}
58+
</Chip>
59+
<span role='status' className='absolute top-full right-0 whitespace-nowrap text-caption'>
60+
{status === 'failed'
61+
? 'Could not copy. Try again.'
62+
: status === 'ready'
63+
? 'Click again to copy.'
64+
: ''}
65+
</span>
66+
</div>
1867
)
1968
}

‎apps/docs/lib/source.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,5 @@ export type PageData = DocData &
129129
description?: string
130130
full?: boolean
131131
integration?: { type: string; color: string }
132+
load: () => Promise<DocData & { lastModified?: Date }>
132133
}

‎apps/docs/next.config.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const withMDX = createMDX()
66

77
const config: NextConfig = {
88
reactStrictMode: true,
9-
productionBrowserSourceMaps: true,
109
transpilePackages: ['@sim/emcn', '@sim/workflow-renderer'],
1110
images: {
1211
unoptimized: true,

‎apps/docs/package.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"lint:check": "biome check .",
1919
"format": "biome format --write .",
2020
"format:check": "biome format .",
21-
"llms-full:generate": "fumadocs-mdx && bun scripts/generate-llms-full.ts"
21+
"llms-full:generate": "fumadocs-mdx && bun scripts/generate-llms-full.ts",
22+
"test:navigation:e2e": "bun scripts/test-navigation-e2e.ts"
2223
},
2324
"dependencies": {
2425
"@sim/db": "workspace:*",
@@ -46,6 +47,7 @@
4647
"zod": "4.3.6"
4748
},
4849
"devDependencies": {
50+
"@playwright/test": "1.61.1",
4951
"@sim/tsconfig": "workspace:*",
5052
"@tailwindcss/postcss": "^4.3.3",
5153
"@types/node": "24.19.2",

0 commit comments

Comments
 (0)