vocs 2.0.17 → 2.1.2
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/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/globals.d.ts +16 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/config.d.ts +28 -0
- package/dist/internal/config.d.ts.map +1 -1
- package/dist/internal/config.js +10 -2
- package/dist/internal/config.js.map +1 -1
- package/dist/internal/llms.d.ts +21 -2
- package/dist/internal/llms.d.ts.map +1 -1
- package/dist/internal/llms.js +41 -1
- package/dist/internal/llms.js.map +1 -1
- package/dist/internal/markdown-negotiation.d.ts +36 -0
- package/dist/internal/markdown-negotiation.d.ts.map +1 -0
- package/dist/internal/markdown-negotiation.js +81 -0
- package/dist/internal/markdown-negotiation.js.map +1 -0
- package/dist/internal/markdown.d.ts.map +1 -1
- package/dist/internal/markdown.js +5 -0
- package/dist/internal/markdown.js.map +1 -1
- package/dist/internal/openapi/anchors.d.ts +25 -0
- package/dist/internal/openapi/anchors.d.ts.map +1 -0
- package/dist/internal/openapi/anchors.js +37 -0
- package/dist/internal/openapi/anchors.js.map +1 -0
- package/dist/internal/openapi/app.d.ts +89 -0
- package/dist/internal/openapi/app.d.ts.map +1 -0
- package/dist/internal/openapi/app.js +62 -0
- package/dist/internal/openapi/app.js.map +1 -0
- package/dist/internal/openapi/index.d.ts +8 -0
- package/dist/internal/openapi/index.d.ts.map +1 -0
- package/dist/internal/openapi/index.js +5 -0
- package/dist/internal/openapi/index.js.map +1 -0
- package/dist/internal/openapi/markdown.d.ts +42 -0
- package/dist/internal/openapi/markdown.d.ts.map +1 -0
- package/dist/internal/openapi/markdown.js +235 -0
- package/dist/internal/openapi/markdown.js.map +1 -0
- package/dist/internal/openapi/openapi.d.ts +187 -0
- package/dist/internal/openapi/openapi.d.ts.map +1 -0
- package/dist/internal/openapi/openapi.js +44 -0
- package/dist/internal/openapi/openapi.js.map +1 -0
- package/dist/internal/openapi/openrpc.d.ts +90 -0
- package/dist/internal/openapi/openrpc.d.ts.map +1 -0
- package/dist/internal/openapi/openrpc.js +213 -0
- package/dist/internal/openapi/openrpc.js.map +1 -0
- package/dist/internal/openapi/parser.d.ts +181 -0
- package/dist/internal/openapi/parser.d.ts.map +1 -0
- package/dist/internal/openapi/parser.js +329 -0
- package/dist/internal/openapi/parser.js.map +1 -0
- package/dist/internal/openapi/registry.d.ts +36 -0
- package/dist/internal/openapi/registry.d.ts.map +1 -0
- package/dist/internal/openapi/registry.js +79 -0
- package/dist/internal/openapi/registry.js.map +1 -0
- package/dist/internal/openapi/sample.d.ts +115 -0
- package/dist/internal/openapi/sample.d.ts.map +1 -0
- package/dist/internal/openapi/sample.js +434 -0
- package/dist/internal/openapi/sample.js.map +1 -0
- package/dist/internal/openapi/search.d.ts +19 -0
- package/dist/internal/openapi/search.d.ts.map +1 -0
- package/dist/internal/openapi/search.js +98 -0
- package/dist/internal/openapi/search.js.map +1 -0
- package/dist/internal/openapi/sidebar.d.ts +30 -0
- package/dist/internal/openapi/sidebar.d.ts.map +1 -0
- package/dist/internal/openapi/sidebar.js +67 -0
- package/dist/internal/openapi/sidebar.js.map +1 -0
- package/dist/internal/openapi/union.d.ts +36 -0
- package/dist/internal/openapi/union.d.ts.map +1 -0
- package/dist/internal/openapi/union.js +69 -0
- package/dist/internal/openapi/union.js.map +1 -0
- package/dist/internal/search.d.ts.map +1 -1
- package/dist/internal/search.js +20 -0
- package/dist/internal/search.js.map +1 -1
- package/dist/internal/vite-plugins.d.ts +12 -0
- package/dist/internal/vite-plugins.d.ts.map +1 -1
- package/dist/internal/vite-plugins.js +102 -11
- package/dist/internal/vite-plugins.js.map +1 -1
- package/dist/react/Badge.d.ts +2 -3
- package/dist/react/Badge.d.ts.map +1 -1
- package/dist/react/Layout.client.d.ts.map +1 -1
- package/dist/react/Layout.client.js +2 -2
- package/dist/react/Layout.client.js.map +1 -1
- package/dist/react/OpenApi.d.ts +6 -0
- package/dist/react/OpenApi.d.ts.map +1 -0
- package/dist/react/OpenApi.js +6 -0
- package/dist/react/OpenApi.js.map +1 -0
- package/dist/react/internal/CodeToHtml.client.d.ts +53 -2
- package/dist/react/internal/CodeToHtml.client.d.ts.map +1 -1
- package/dist/react/internal/CodeToHtml.client.js +154 -21
- package/dist/react/internal/CodeToHtml.client.js.map +1 -1
- package/dist/react/internal/Sidebar.d.ts.map +1 -1
- package/dist/react/internal/Sidebar.js +99 -2
- package/dist/react/internal/Sidebar.js.map +1 -1
- package/dist/react/internal/openapi/CodeSample.client.d.ts +21 -0
- package/dist/react/internal/openapi/CodeSample.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/CodeSample.client.js +134 -0
- package/dist/react/internal/openapi/CodeSample.client.js.map +1 -0
- package/dist/react/internal/openapi/CollapsibleChildren.client.d.ts +17 -0
- package/dist/react/internal/openapi/CollapsibleChildren.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/CollapsibleChildren.client.js +18 -0
- package/dist/react/internal/openapi/CollapsibleChildren.client.js.map +1 -0
- package/dist/react/internal/openapi/Disclosure.client.d.ts +28 -0
- package/dist/react/internal/openapi/Disclosure.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/Disclosure.client.js +38 -0
- package/dist/react/internal/openapi/Disclosure.client.js.map +1 -0
- package/dist/react/internal/openapi/Endpoints.d.ts +26 -0
- package/dist/react/internal/openapi/Endpoints.d.ts.map +1 -0
- package/dist/react/internal/openapi/Endpoints.js +33 -0
- package/dist/react/internal/openapi/Endpoints.js.map +1 -0
- package/dist/react/internal/openapi/EndpointsView.d.ts +24 -0
- package/dist/react/internal/openapi/EndpointsView.d.ts.map +1 -0
- package/dist/react/internal/openapi/EndpointsView.js +26 -0
- package/dist/react/internal/openapi/EndpointsView.js.map +1 -0
- package/dist/react/internal/openapi/EnumValues.client.d.ts +14 -0
- package/dist/react/internal/openapi/EnumValues.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/EnumValues.client.js +20 -0
- package/dist/react/internal/openapi/EnumValues.client.js.map +1 -0
- package/dist/react/internal/openapi/HeadingAnchor.d.ts +15 -0
- package/dist/react/internal/openapi/HeadingAnchor.d.ts.map +1 -0
- package/dist/react/internal/openapi/HeadingAnchor.js +12 -0
- package/dist/react/internal/openapi/HeadingAnchor.js.map +1 -0
- package/dist/react/internal/openapi/OpenApiPage.d.ts +79 -0
- package/dist/react/internal/openapi/OpenApiPage.d.ts.map +1 -0
- package/dist/react/internal/openapi/OpenApiPage.js +72 -0
- package/dist/react/internal/openapi/OpenApiPage.js.map +1 -0
- package/dist/react/internal/openapi/Operation.d.ts +25 -0
- package/dist/react/internal/openapi/Operation.d.ts.map +1 -0
- package/dist/react/internal/openapi/Operation.js +101 -0
- package/dist/react/internal/openapi/Operation.js.map +1 -0
- package/dist/react/internal/openapi/Playground.client.d.ts +33 -0
- package/dist/react/internal/openapi/Playground.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/Playground.client.js +170 -0
- package/dist/react/internal/openapi/Playground.client.js.map +1 -0
- package/dist/react/internal/openapi/PropertyExample.client.d.ts +17 -0
- package/dist/react/internal/openapi/PropertyExample.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/PropertyExample.client.js +21 -0
- package/dist/react/internal/openapi/PropertyExample.client.js.map +1 -0
- package/dist/react/internal/openapi/Reference.d.ts +55 -0
- package/dist/react/internal/openapi/Reference.d.ts.map +1 -0
- package/dist/react/internal/openapi/Reference.js +42 -0
- package/dist/react/internal/openapi/Reference.js.map +1 -0
- package/dist/react/internal/openapi/Schema.d.ts +110 -0
- package/dist/react/internal/openapi/Schema.d.ts.map +1 -0
- package/dist/react/internal/openapi/Schema.js +239 -0
- package/dist/react/internal/openapi/Schema.js.map +1 -0
- package/dist/react/internal/openapi/SchemaUnion.client.d.ts +25 -0
- package/dist/react/internal/openapi/SchemaUnion.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/SchemaUnion.client.js +48 -0
- package/dist/react/internal/openapi/SchemaUnion.client.js.map +1 -0
- package/dist/react/internal/openapi/anchor-navigation.client.d.ts +47 -0
- package/dist/react/internal/openapi/anchor-navigation.client.d.ts.map +1 -0
- package/dist/react/internal/openapi/anchor-navigation.client.js +120 -0
- package/dist/react/internal/openapi/anchor-navigation.client.js.map +1 -0
- package/dist/react/internal/openapi/auth.d.ts +28 -0
- package/dist/react/internal/openapi/auth.d.ts.map +1 -0
- package/dist/react/internal/openapi/auth.js +75 -0
- package/dist/react/internal/openapi/auth.js.map +1 -0
- package/dist/react/useLayout.d.ts +2 -0
- package/dist/react/useLayout.d.ts.map +1 -1
- package/dist/react/useLayout.js +2 -0
- package/dist/react/useLayout.js.map +1 -1
- package/dist/server/handlers.d.ts +1 -1
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server/handlers.js +26 -5
- package/dist/server/handlers.js.map +1 -1
- package/dist/server/og-assets.d.ts +5 -0
- package/dist/server/og-assets.d.ts.map +1 -0
- package/dist/server/og-assets.js +11 -0
- package/dist/server/og-assets.js.map +1 -0
- package/dist/server/openapi/assets.d.ts +33 -0
- package/dist/server/openapi/assets.d.ts.map +1 -0
- package/dist/server/openapi/assets.generated.d.ts +9 -0
- package/dist/server/openapi/assets.generated.d.ts.map +1 -0
- package/dist/server/openapi/assets.generated.js +1091 -0
- package/dist/server/openapi/assets.generated.js.map +1 -0
- package/dist/server/openapi/assets.js +32 -0
- package/dist/server/openapi/assets.js.map +1 -0
- package/dist/server/openapi/handler.d.ts +103 -0
- package/dist/server/openapi/handler.d.ts.map +1 -0
- package/dist/server/openapi/handler.js +198 -0
- package/dist/server/openapi/handler.js.map +1 -0
- package/dist/server/openapi/handler.test.d.ts +2 -0
- package/dist/server/openapi/handler.test.d.ts.map +1 -0
- package/dist/server/openapi/handler.test.js +203 -0
- package/dist/server/openapi/handler.test.js.map +1 -0
- package/dist/server/openapi/html.d.ts +16 -0
- package/dist/server/openapi/html.d.ts.map +1 -0
- package/dist/server/openapi/html.js +75 -0
- package/dist/server/openapi/html.js.map +1 -0
- package/dist/server/openapi/pages.d.ts +33 -0
- package/dist/server/openapi/pages.d.ts.map +1 -0
- package/dist/server/openapi/pages.js +130 -0
- package/dist/server/openapi/pages.js.map +1 -0
- package/dist/server/openapi/pages.test.d.ts +2 -0
- package/dist/server/openapi/pages.test.d.ts.map +1 -0
- package/dist/server/openapi/pages.test.js +94 -0
- package/dist/server/openapi/pages.test.js.map +1 -0
- package/dist/server/openapi/state.d.ts +42 -0
- package/dist/server/openapi/state.d.ts.map +1 -0
- package/dist/server/openapi/state.js +101 -0
- package/dist/server/openapi/state.js.map +1 -0
- package/dist/styles/index.css +16 -0
- package/dist/styles/markdown.css +9 -7
- package/dist/styles/openapi-playground.css +80 -0
- package/dist/styles/openapi.css +660 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +1 -0
- package/dist/vite.js.map +1 -1
- package/dist/waku/internal/middleware/md-router.d.ts +0 -4
- package/dist/waku/internal/middleware/md-router.d.ts.map +1 -1
- package/dist/waku/internal/middleware/md-router.js +3 -48
- package/dist/waku/internal/middleware/md-router.js.map +1 -1
- package/dist/waku/internal/patches/adapters/vercel-build-enhancer.js +1 -1
- package/dist/waku/internal/patches/adapters/vercel-build-enhancer.js.map +1 -1
- package/dist/waku/internal/patches/router.d.ts.map +1 -1
- package/dist/waku/internal/patches/router.js +114 -1
- package/dist/waku/internal/patches/router.js.map +1 -1
- package/package.json +5 -1
- package/src/config.ts +1 -0
- package/src/globals.d.ts +16 -0
- package/src/index.ts +1 -0
- package/src/internal/config.ts +40 -1
- package/src/internal/llms.ts +51 -1
- package/src/internal/markdown-negotiation.test.ts +42 -0
- package/src/internal/markdown-negotiation.ts +95 -0
- package/src/internal/markdown.ts +5 -0
- package/src/internal/openapi/anchors.ts +44 -0
- package/src/internal/openapi/app.ts +127 -0
- package/src/internal/openapi/index.ts +24 -0
- package/src/internal/openapi/markdown.test.ts +115 -0
- package/src/internal/openapi/markdown.ts +275 -0
- package/src/internal/openapi/openapi.ts +212 -0
- package/src/internal/openapi/openrpc.test.ts +239 -0
- package/src/internal/openapi/openrpc.ts +295 -0
- package/src/internal/openapi/parser.test.ts +203 -0
- package/src/internal/openapi/parser.ts +613 -0
- package/src/internal/openapi/registry.test.ts +89 -0
- package/src/internal/openapi/registry.ts +89 -0
- package/src/internal/openapi/sample.test.ts +283 -0
- package/src/internal/openapi/sample.ts +562 -0
- package/src/internal/openapi/search.test.ts +62 -0
- package/src/internal/openapi/search.ts +108 -0
- package/src/internal/openapi/sidebar.test.ts +131 -0
- package/src/internal/openapi/sidebar.ts +94 -0
- package/src/internal/openapi/union.test.ts +51 -0
- package/src/internal/openapi/union.ts +74 -0
- package/src/internal/search.ts +20 -0
- package/src/internal/test/virtual-config.stub.ts +14 -0
- package/src/internal/vite-plugins.ts +106 -11
- package/src/openapi-app/App.tsx +64 -0
- package/src/openapi-app/blocks.tsx +33 -0
- package/src/openapi-app/client.tsx +25 -0
- package/src/openapi-app/links.test.ts +84 -0
- package/src/openapi-app/links.ts +66 -0
- package/src/openapi-app/payload.ts +20 -0
- package/src/openapi-app/virtual/config.ts +7 -0
- package/src/openapi-app/virtual/group-icons.ts +2 -0
- package/src/openapi-app/virtual/langs.ts +6 -0
- package/src/openapi-app/virtual/openapi.ts +10 -0
- package/src/openapi-app/virtual/search-index.ts +21 -0
- package/src/openapi-app/virtual/slots.ts +4 -0
- package/src/openapi-app/virtual/user-styles.ts +2 -0
- package/src/openapi-app/waku.tsx +154 -0
- package/src/react/Badge.tsx +2 -3
- package/src/react/Layout.client.tsx +17 -4
- package/src/react/OpenApi.tsx +5 -0
- package/src/react/internal/CodeToHtml.client.tsx +283 -22
- package/src/react/internal/Sidebar.tsx +126 -22
- package/src/react/internal/openapi/CodeSample.client.tsx +294 -0
- package/src/react/internal/openapi/CollapsibleChildren.client.tsx +41 -0
- package/src/react/internal/openapi/Disclosure.client.tsx +67 -0
- package/src/react/internal/openapi/Endpoints.tsx +58 -0
- package/src/react/internal/openapi/EndpointsView.tsx +76 -0
- package/src/react/internal/openapi/EnumValues.client.tsx +49 -0
- package/src/react/internal/openapi/HeadingAnchor.tsx +28 -0
- package/src/react/internal/openapi/OpenApiPage.tsx +173 -0
- package/src/react/internal/openapi/Operation.test.tsx +101 -0
- package/src/react/internal/openapi/Operation.tsx +335 -0
- package/src/react/internal/openapi/Playground.client.tsx +234 -0
- package/src/react/internal/openapi/PropertyExample.client.tsx +55 -0
- package/src/react/internal/openapi/Reference.tsx +120 -0
- package/src/react/internal/openapi/Schema.tsx +467 -0
- package/src/react/internal/openapi/SchemaUnion.client.tsx +123 -0
- package/src/react/internal/openapi/anchor-navigation.client.ts +154 -0
- package/src/react/internal/openapi/auth.ts +69 -0
- package/src/react/useLayout.ts +4 -0
- package/src/server/handlers.ts +31 -6
- package/src/server/og-assets.ts +14 -0
- package/src/server/openapi/assets.generated.ts +1093 -0
- package/src/server/openapi/assets.ts +57 -0
- package/src/server/openapi/handler.test.ts +244 -0
- package/src/server/openapi/handler.ts +277 -0
- package/src/server/openapi/html.ts +84 -0
- package/src/server/openapi/pages.test.ts +111 -0
- package/src/server/openapi/pages.ts +153 -0
- package/src/server/openapi/state.ts +136 -0
- package/src/styles/index.css +16 -0
- package/src/styles/markdown.css +9 -7
- package/src/styles/openapi-playground.css +80 -0
- package/src/styles/openapi.css +660 -0
- package/src/vite.ts +1 -0
- package/src/waku/internal/middleware/md-router.ts +8 -52
- package/src/waku/internal/patches/adapters/vercel-build-enhancer.ts +1 -1
- package/src/waku/internal/patches/router.ts +131 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { compile, compileSource, compileTraits } from './pages.js'
|
|
3
|
+
|
|
4
|
+
describe('compile', () => {
|
|
5
|
+
test('compiles inline content without filesystem access', async () => {
|
|
6
|
+
const pages = await compile([{ path: '/auth', content: '# Authentication\n\nUse a token.' }])
|
|
7
|
+
expect(pages).toHaveLength(1)
|
|
8
|
+
expect(pages[0]?.path).toBe('/auth')
|
|
9
|
+
expect(pages[0]?.title).toBe('Authentication')
|
|
10
|
+
expect(pages[0]?.blocks[0]).toMatchObject({ type: 'html' })
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('applies a title override', async () => {
|
|
14
|
+
const pages = await compile([{ path: '/auth', content: '# Heading', title: 'Custom' }])
|
|
15
|
+
expect(pages[0]?.title).toBe('Custom')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('applies title and description overrides', async () => {
|
|
19
|
+
const pages = await compile([
|
|
20
|
+
{ path: '/auth', content: 'Body.', title: 'Authentication', description: 'Use a token.' },
|
|
21
|
+
])
|
|
22
|
+
expect(pages[0]?.title).toBe('Authentication')
|
|
23
|
+
expect(pages[0]?.description).toBe('Use a token.')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('throws when a page has neither file nor content', async () => {
|
|
27
|
+
await expect(compile([{ path: '/auth' }])).rejects.toThrow(/either `file` or `content`/)
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
describe('compileSource', () => {
|
|
32
|
+
test('compiles markdown to an html block', () => {
|
|
33
|
+
const page = compileSource('/auth', '# Authentication\n\nUse a token.')
|
|
34
|
+
expect(page.path).toBe('/auth')
|
|
35
|
+
expect(page.title).toBe('Authentication')
|
|
36
|
+
expect(page.blocks).toHaveLength(1)
|
|
37
|
+
expect(page.blocks[0]).toMatchObject({ type: 'html' })
|
|
38
|
+
if (page.blocks[0]?.type === 'html') expect(page.blocks[0].html).toContain('Authentication')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('reads title from frontmatter', () => {
|
|
42
|
+
const page = compileSource('/', '---\ntitle: Overview\n---\n\nHello.')
|
|
43
|
+
expect(page.title).toBe('Overview')
|
|
44
|
+
expect(page.blocks).toHaveLength(1)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('adds slug ids to body headings (for the outline + anchors)', () => {
|
|
48
|
+
const page = compileSource('/auth', 'Intro.\n\n## Getting a key\n\n### Header format')
|
|
49
|
+
const html = page.blocks[0]?.type === 'html' ? page.blocks[0].html : ''
|
|
50
|
+
expect(html).toContain('id="getting-a-key"')
|
|
51
|
+
expect(html).toContain('id="header-format"')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('reads title and description from frontmatter', () => {
|
|
55
|
+
const page = compileSource(
|
|
56
|
+
'/auth',
|
|
57
|
+
'---\ntitle: Authentication\ndescription: How auth works.\n---\n\nHello.',
|
|
58
|
+
)
|
|
59
|
+
expect(page.title).toBe('Authentication')
|
|
60
|
+
expect(page.description).toBe('How auth works.')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('splits around <OpenApi.Endpoints /> into blocks', () => {
|
|
64
|
+
const page = compileSource('/', 'Intro text.\n\n<OpenApi.Endpoints />\n\nMore text.')
|
|
65
|
+
expect(page.blocks.map((block) => block.type)).toEqual(['html', 'endpoints', 'html'])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('parses an Endpoints path attribute and strips esm imports', () => {
|
|
69
|
+
const page = compileSource(
|
|
70
|
+
'/',
|
|
71
|
+
'import { OpenApi } from \'vocs\'\n\n<OpenApi.Endpoints path="/api" />',
|
|
72
|
+
)
|
|
73
|
+
const endpoints = page.blocks.find((block) => block.type === 'endpoints')
|
|
74
|
+
expect(endpoints).toMatchObject({ type: 'endpoints', path: '/api' })
|
|
75
|
+
// The import line is stripped (not rendered as prose).
|
|
76
|
+
expect(page.blocks.some((b) => b.type === 'html' && b.html.includes('import'))).toBe(false)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('normalizes the route path', () => {
|
|
80
|
+
expect(compileSource('auth/', 'x').path).toBe('/auth')
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
describe('compileTraits', () => {
|
|
85
|
+
test('uses tag name as title and x-subtitle as description', () => {
|
|
86
|
+
const pages = compileTraits([
|
|
87
|
+
{ id: 'authentication', name: 'Authentication', description: 'Body.', subtitle: 'How auth.' },
|
|
88
|
+
])
|
|
89
|
+
expect(pages).toHaveLength(1)
|
|
90
|
+
expect(pages[0]?.path).toBe('/authentication')
|
|
91
|
+
expect(pages[0]?.title).toBe('Authentication')
|
|
92
|
+
expect(pages[0]?.description).toBe('How auth.')
|
|
93
|
+
expect(pages[0]?.blocks[0]).toMatchObject({ type: 'html' })
|
|
94
|
+
// Trait pages render their title/subtitle as an on-page header (the body
|
|
95
|
+
// has no heading of its own).
|
|
96
|
+
expect(pages[0]?.header).toBe(true)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('frontmatter overrides tag name and subtitle', () => {
|
|
100
|
+
const pages = compileTraits([
|
|
101
|
+
{
|
|
102
|
+
id: 'auth',
|
|
103
|
+
name: 'Auth',
|
|
104
|
+
subtitle: 'Tag subtitle.',
|
|
105
|
+
description: '---\ntitle: Custom\ndescription: FM subtitle.\n---\n\nBody.',
|
|
106
|
+
},
|
|
107
|
+
])
|
|
108
|
+
expect(pages[0]?.title).toBe('Custom')
|
|
109
|
+
expect(pages[0]?.description).toBe('FM subtitle.')
|
|
110
|
+
})
|
|
111
|
+
})
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import * as Markdown from '../../internal/markdown.js'
|
|
2
|
+
import type { CompiledPage, PageBlock } from '../../internal/openapi/app.js'
|
|
3
|
+
import type * as OpenApi from '../../internal/openapi/openapi.js'
|
|
4
|
+
import { normalizePath } from '../../internal/openapi/openapi.js'
|
|
5
|
+
import type { IrTrait } from '../../internal/openapi/parser.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Reads and compiles consumer-authored `.md`/`.mdx` override/guide pages for the
|
|
9
|
+
* standalone handler into serializable {@link CompiledPage}s.
|
|
10
|
+
*
|
|
11
|
+
* The standalone handler has no filesystem router, so pages are supplied
|
|
12
|
+
* explicitly via `config.pages`. Each file is read once at boot and split into
|
|
13
|
+
* an ordered list of {@link PageBlock}s: prose is rendered to HTML with the same
|
|
14
|
+
* `data-v` typography as the rest of the docs, while inline
|
|
15
|
+
* `<OpenApi.Endpoints />` components become `endpoints` blocks re-hydrated as
|
|
16
|
+
* real React on the client.
|
|
17
|
+
*
|
|
18
|
+
* MDX `import`/`export` statements are stripped (the only supported component is
|
|
19
|
+
* `<OpenApi.Endpoints />`); anything else renders as Markdown.
|
|
20
|
+
*/
|
|
21
|
+
export async function compile(
|
|
22
|
+
pages: OpenApi.Page[] | undefined,
|
|
23
|
+
options: compile.Options = {},
|
|
24
|
+
): Promise<CompiledPage[]> {
|
|
25
|
+
if (!pages || pages.length === 0) return []
|
|
26
|
+
const { rootDir = typeof process !== 'undefined' ? process.cwd() : '.' } = options
|
|
27
|
+
|
|
28
|
+
// Only touch the filesystem when a page actually needs a file read, so inline
|
|
29
|
+
// `content` pages work on runtimes without `node:fs` (e.g. Cloudflare Workers).
|
|
30
|
+
const needsFs = pages.some((page) => page.content === undefined && page.file !== undefined)
|
|
31
|
+
const fsModule = needsFs
|
|
32
|
+
? await Promise.all([
|
|
33
|
+
import('node:fs/promises').then((module) => ({ default: module })),
|
|
34
|
+
import('node:path'),
|
|
35
|
+
])
|
|
36
|
+
: undefined
|
|
37
|
+
|
|
38
|
+
const compiled: CompiledPage[] = []
|
|
39
|
+
for (const page of pages) {
|
|
40
|
+
let source: string
|
|
41
|
+
if (page.content !== undefined) source = page.content
|
|
42
|
+
else if (page.file !== undefined && fsModule) {
|
|
43
|
+
const [{ default: fs }, path] = fsModule
|
|
44
|
+
const filePath = path.isAbsolute(page.file) ? page.file : path.resolve(rootDir, page.file)
|
|
45
|
+
source = await fs.readFile(filePath, 'utf-8')
|
|
46
|
+
} else throw new Error(`[vocs] Page "${page.path}" must define either \`file\` or \`content\`.`)
|
|
47
|
+
|
|
48
|
+
const result = compileSource(page.path, source)
|
|
49
|
+
if (page.title) result.title = page.title
|
|
50
|
+
if (page.description) result.description = page.description
|
|
51
|
+
compiled.push(result)
|
|
52
|
+
}
|
|
53
|
+
return compiled
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export declare namespace compile {
|
|
57
|
+
type Options = {
|
|
58
|
+
/** Directory `file` paths are resolved against. @default process.cwd() */
|
|
59
|
+
rootDir?: string | undefined
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Compiles doc-only "trait" tags (`x-traitTag: true`) into guide
|
|
65
|
+
* {@link CompiledPage}s. The tag `name` is the title and `x-subtitle` the
|
|
66
|
+
* subtitle, unless overridden by Markdown frontmatter in the description.
|
|
67
|
+
*/
|
|
68
|
+
export function compileTraits(traits: readonly IrTrait[]): CompiledPage[] {
|
|
69
|
+
return traits.map((trait) => {
|
|
70
|
+
const result = compileSource(`/${trait.id}`, trait.description ?? '')
|
|
71
|
+
return {
|
|
72
|
+
...result,
|
|
73
|
+
title: result.title ?? trait.name,
|
|
74
|
+
description: result.description ?? trait.subtitle,
|
|
75
|
+
// The tag name/subtitle are the page header (the body has no heading).
|
|
76
|
+
header: true,
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Matches a self-closing `<OpenApi.Endpoints ... />` or `<Endpoints ... />`. */
|
|
82
|
+
const endpointsRe = /<(?:OpenApi\.)?Endpoints\b([^>]*?)\/>/g
|
|
83
|
+
/** Matches an ESM `import`/`export` statement line. */
|
|
84
|
+
const esmLineRe = /^\s*(?:import|export)\b.*$/gm
|
|
85
|
+
/** Matches a leading YAML frontmatter block. */
|
|
86
|
+
const frontmatterRe = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/
|
|
87
|
+
|
|
88
|
+
/** Compiles a single page source string into a {@link CompiledPage}. */
|
|
89
|
+
export function compileSource(routePath: string, source: string): CompiledPage {
|
|
90
|
+
const { body, title, description } = stripFrontmatter(source)
|
|
91
|
+
|
|
92
|
+
const blocks: PageBlock[] = []
|
|
93
|
+
let lastIndex = 0
|
|
94
|
+
for (const match of body.matchAll(endpointsRe)) {
|
|
95
|
+
const index = match.index ?? 0
|
|
96
|
+
const before = body.slice(lastIndex, index)
|
|
97
|
+
pushHtml(blocks, before)
|
|
98
|
+
blocks.push({ type: 'endpoints', path: parsePath(match[1] ?? '') })
|
|
99
|
+
lastIndex = index + match[0].length
|
|
100
|
+
}
|
|
101
|
+
pushHtml(blocks, body.slice(lastIndex))
|
|
102
|
+
|
|
103
|
+
// Raw Markdown for the page's `.md` / agent-facing version: drop ESM lines and
|
|
104
|
+
// the `<OpenApi.Endpoints />` component (it has no Markdown representation).
|
|
105
|
+
const markdown = body.replace(esmLineRe, '').replace(endpointsRe, '').trim() || undefined
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
path: normalizePath(routePath),
|
|
109
|
+
title: title ?? firstHeading(body),
|
|
110
|
+
description,
|
|
111
|
+
blocks,
|
|
112
|
+
markdown,
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Renders a Markdown segment to an HTML block (ESM lines stripped). */
|
|
117
|
+
function pushHtml(blocks: PageBlock[], markdown: string): void {
|
|
118
|
+
const cleaned = markdown.replace(esmLineRe, '').trim()
|
|
119
|
+
if (!cleaned) return
|
|
120
|
+
blocks.push({ type: 'html', html: Markdown.toHtml(cleaned) })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Extracts the `path="..."` attribute from an `<Endpoints>` tag, if present. */
|
|
124
|
+
function parsePath(attrs: string): string | undefined {
|
|
125
|
+
const match = attrs.match(/\bpath\s*=\s*["']([^"']*)["']/)
|
|
126
|
+
return match?.[1]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Splits a leading YAML frontmatter block, returning the body, `title`, and `description`. */
|
|
130
|
+
function stripFrontmatter(source: string): {
|
|
131
|
+
body: string
|
|
132
|
+
title?: string | undefined
|
|
133
|
+
description?: string | undefined
|
|
134
|
+
} {
|
|
135
|
+
const match = source.match(frontmatterRe)
|
|
136
|
+
if (!match) return { body: source }
|
|
137
|
+
const title = readField(match[1], 'title')
|
|
138
|
+
const description = readField(match[1], 'description')
|
|
139
|
+
return { body: source.slice(match[0].length), title, description }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Reads a scalar `key: value` field from a YAML frontmatter block. */
|
|
143
|
+
function readField(frontmatter: string | undefined, key: string): string | undefined {
|
|
144
|
+
return frontmatter
|
|
145
|
+
?.match(new RegExp(`^${key}\\s*:\\s*(.+)$`, 'm'))?.[1]
|
|
146
|
+
?.trim()
|
|
147
|
+
.replace(/^["']|["']$/g, '')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Finds the first `# heading` in a Markdown body. */
|
|
151
|
+
function firstHeading(body: string): string | undefined {
|
|
152
|
+
return body.match(/^#\s+(.+)$/m)?.[1]?.trim()
|
|
153
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import * as Config from '../../internal/config.js'
|
|
2
|
+
import * as ConfigSerializer from '../../internal/config-serializer.js'
|
|
3
|
+
import type { Payload } from '../../internal/openapi/app.js'
|
|
4
|
+
import type * as OpenApi from '../../internal/openapi/openapi.js'
|
|
5
|
+
import { parse } from '../../internal/openapi/parser.js'
|
|
6
|
+
import * as Sidebar from '../../internal/openapi/sidebar.js'
|
|
7
|
+
import * as Pages from './pages.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parses the configured spec, compiles override/guide pages, and synthesizes a
|
|
11
|
+
* Vocs config — everything the static browser bundle needs to render the real
|
|
12
|
+
* Vocs layout for the API reference.
|
|
13
|
+
*
|
|
14
|
+
* The sidebar mirrors the site integration: optional `sidebar.top` items, the
|
|
15
|
+
* generated `Introduction` + per-category items, then optional `sidebar.bottom`
|
|
16
|
+
* items.
|
|
17
|
+
*/
|
|
18
|
+
export async function prepare(
|
|
19
|
+
config: OpenApi.Config,
|
|
20
|
+
options: prepare.Options = {},
|
|
21
|
+
): Promise<Payload> {
|
|
22
|
+
const { rootDir, baseUrl } = options
|
|
23
|
+
|
|
24
|
+
const [ir, configPages] = await Promise.all([
|
|
25
|
+
parse(config, { rootDir, baseUrl }),
|
|
26
|
+
Pages.compile(config.pages, { rootDir }),
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
// Doc-only `x-traitTag` tags become guide pages. By default they nest under
|
|
30
|
+
// `Introduction`; an `x-parent` (group name) places them under that operation
|
|
31
|
+
// group instead, or under a new sidebar group when the name has no operations.
|
|
32
|
+
const traitPages = Pages.compileTraits(ir.traits)
|
|
33
|
+
const pages = [...traitPages, ...configPages]
|
|
34
|
+
|
|
35
|
+
const base = ir.path === '/' ? '' : ir.path.replace(/\/$/, '')
|
|
36
|
+
const item = (trait: (typeof ir.traits)[number]) => ({
|
|
37
|
+
text: trait.name,
|
|
38
|
+
link: `${base}/${trait.id}`,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const groupIdByName = new Map(ir.groups.map((group) => [group.name, group.id]))
|
|
42
|
+
const traitIntro: { text: string; link: string }[] = []
|
|
43
|
+
const groupExtras = new Map<string, { text: string; link: string }[]>()
|
|
44
|
+
const extraGroups = new Map<string, { text: string; link: string }[]>()
|
|
45
|
+
for (const trait of ir.traits) {
|
|
46
|
+
if (!trait.parent) {
|
|
47
|
+
traitIntro.push(item(trait))
|
|
48
|
+
continue
|
|
49
|
+
}
|
|
50
|
+
const groupId = groupIdByName.get(trait.parent)
|
|
51
|
+
const bucket = groupId ? groupExtras : extraGroups
|
|
52
|
+
const key = groupId ?? trait.parent
|
|
53
|
+
bucket.set(key, [...(bucket.get(key) ?? []), item(trait)])
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const top = config.sidebar?.top ?? []
|
|
57
|
+
const bottom = config.sidebar?.bottom ?? []
|
|
58
|
+
const intro = [...traitIntro, ...(config.sidebar?.intro ?? [])]
|
|
59
|
+
const collapsed = config.sidebar?.collapsed ?? false
|
|
60
|
+
const newGroups = [...extraGroups].map(([name, items]) => ({
|
|
61
|
+
text: name,
|
|
62
|
+
collapsed,
|
|
63
|
+
items,
|
|
64
|
+
}))
|
|
65
|
+
const sidebar = [
|
|
66
|
+
...top,
|
|
67
|
+
...Sidebar.toSidebar(ir, { intro, groupExtras, collapsed }),
|
|
68
|
+
...newGroups,
|
|
69
|
+
...bottom,
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
// A real Vocs config so the browser bundle renders the genuine layout/chrome.
|
|
73
|
+
// `sidebar` is an array (covers the whole section, no path-scoping needed).
|
|
74
|
+
const vocsConfig = Config.define({
|
|
75
|
+
title: ir.info.title,
|
|
76
|
+
description: ir.info.description,
|
|
77
|
+
rootDir,
|
|
78
|
+
sidebar,
|
|
79
|
+
...config.vocs,
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
ir,
|
|
84
|
+
// `vocs.title` overrides the spec title (used for the HTML shell `<title>`).
|
|
85
|
+
title: vocsConfig.title ?? ir.info.title,
|
|
86
|
+
sidebar,
|
|
87
|
+
pages,
|
|
88
|
+
// Serialize functions (e.g. search/feedback adapters) so they survive the
|
|
89
|
+
// JSON embed; the browser deserializes via `virtual:vocs/config`.
|
|
90
|
+
config: ConfigSerializer.serializeFunctions(vocsConfig),
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export declare namespace prepare {
|
|
95
|
+
type Options = {
|
|
96
|
+
/** Directory file-path specs/pages are resolved against. */
|
|
97
|
+
rootDir?: string | undefined
|
|
98
|
+
/**
|
|
99
|
+
* Base URL relative `x-openrpc` URLs (e.g. `/openrpc.json`) resolve against
|
|
100
|
+
* — the request origin, so the renderer can fetch a host-relative OpenRPC
|
|
101
|
+
* document at runtime.
|
|
102
|
+
*/
|
|
103
|
+
baseUrl?: string | undefined
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolves consumer-supplied custom CSS for the shell `<head>`: an inline
|
|
109
|
+
* string is returned as-is, while `{ file }` is read once from disk (resolved
|
|
110
|
+
* against `rootDir`). Returns `undefined` when no CSS is configured.
|
|
111
|
+
*
|
|
112
|
+
* Only touches the filesystem when a `file` is configured, so inline-string CSS
|
|
113
|
+
* works on runtimes without `node:fs` (e.g. Cloudflare Workers).
|
|
114
|
+
*/
|
|
115
|
+
export async function resolveCss(
|
|
116
|
+
css: string | { file: string } | undefined,
|
|
117
|
+
options: resolveCss.Options = {},
|
|
118
|
+
): Promise<string | undefined> {
|
|
119
|
+
if (css === undefined) return undefined
|
|
120
|
+
if (typeof css === 'string') return css
|
|
121
|
+
|
|
122
|
+
const { rootDir = typeof process !== 'undefined' ? process.cwd() : '.' } = options
|
|
123
|
+
const [{ default: fs }, path] = await Promise.all([
|
|
124
|
+
import('node:fs/promises').then((module) => ({ default: module })),
|
|
125
|
+
import('node:path'),
|
|
126
|
+
])
|
|
127
|
+
const filePath = path.isAbsolute(css.file) ? css.file : path.resolve(rootDir, css.file)
|
|
128
|
+
return fs.readFile(filePath, 'utf-8')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export declare namespace resolveCss {
|
|
132
|
+
type Options = {
|
|
133
|
+
/** Directory `file` paths are resolved against. @default process.cwd() */
|
|
134
|
+
rootDir?: string | undefined
|
|
135
|
+
}
|
|
136
|
+
}
|
package/src/styles/index.css
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
@import "./theme.css" layer(vocs_theme);
|
|
8
8
|
@import "./markdown.css" layer(vocs_components);
|
|
9
9
|
@import "./twoslash.css" layer(vocs_components);
|
|
10
|
+
@import "./openapi.css" layer(vocs_components);
|
|
10
11
|
|
|
11
12
|
@custom-variant dark (&:where([style*=":dark"], [style*=":dark"] *, [style*=": dark"], [style*=": dark"] *));
|
|
12
13
|
|
|
@@ -149,6 +150,21 @@
|
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
/* Full-bleed content (e.g. OpenAPI reference): span the full available width.
|
|
154
|
+
Collapsing the centering gutter to the sidebar width re-flows the sidebar,
|
|
155
|
+
logo, top nav, surface background, and main margin together (they all derive
|
|
156
|
+
from `--vocs-spacing-gutter`), so content starts flush after the sidebar.
|
|
157
|
+
The article/footer max-width and the operation grid columns are set via
|
|
158
|
+
Tailwind utilities on the elements so they cascade in the utilities layer
|
|
159
|
+
rather than being overridden by it. */
|
|
160
|
+
[data-v-content-width="full"] {
|
|
161
|
+
/* biome-ignore lint/correctness/noUnknownFunction: _ */
|
|
162
|
+
@media (width >= theme(--breakpoint-lg)) {
|
|
163
|
+
--vocs-spacing-gutter: var(--vocs-spacing-sidebar);
|
|
164
|
+
--vocs-spacing-logo: var(--vocs-spacing-sidebar);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
152
168
|
/* Minimal/no-sidebar layout - content scrolls above topnav */
|
|
153
169
|
[data-layout="minimal"] > [data-v-main],
|
|
154
170
|
[data-layout="blank"] > [data-v-main] {
|
package/src/styles/markdown.css
CHANGED
|
@@ -81,7 +81,7 @@ hgroup[data-v] {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
hgroup[data-v],
|
|
84
|
-
article[data-v-content] > h1[data-v] {
|
|
84
|
+
:is(article[data-v-content], [data-v-openapi-intro], [data-v-openapi-guide]) > h1[data-v] {
|
|
85
85
|
@apply vocs:border-b vocs:border-primary vocs:pb-[0.75em];
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -90,7 +90,7 @@ hgroup[data-v] p {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
hgroup[data-v]:not(:last-child),
|
|
93
|
-
article[data-v-content] > h1[data-v]:not(:last-child) {
|
|
93
|
+
:is(article[data-v-content], [data-v-openapi-intro], [data-v-openapi-guide]) > h1[data-v]:not(:last-child) {
|
|
94
94
|
margin-block-end: calc(calc(var(--vocs-spacing) * 8) * calc(1 - var(--tw-space-y-reverse))) !important;
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -98,17 +98,19 @@ article[data-v-content] > h1[data-v]:not(:last-child) {
|
|
|
98
98
|
|
|
99
99
|
/* #region Secondary Headings (h2, h3, h4, h5, h6) */
|
|
100
100
|
|
|
101
|
-
article[data-v-content]
|
|
101
|
+
:is(article[data-v-content], [data-v-openapi-intro], [data-v-openapi-guide])
|
|
102
|
+
> :not(:is(hgroup[data-v], h1[data-v]))
|
|
103
|
+
+ h2:not(:only-child) {
|
|
102
104
|
@apply vocs:pt-8 vocs:mt-10! vocs:border-t vocs:border-primary;
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
article[data-v-content] > h3:not(:only-child) {
|
|
107
|
+
:is(article[data-v-content], [data-v-openapi-intro], [data-v-openapi-guide]) > h3:not(:only-child) {
|
|
106
108
|
@apply vocs:pt-4;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
article[data-v-content] > h4:not(:only-child),
|
|
110
|
-
article[data-v-content] > h5:not(:only-child),
|
|
111
|
-
article[data-v-content] > h6:not(:only-child) {
|
|
111
|
+
:is(article[data-v-content], [data-v-openapi-intro], [data-v-openapi-guide]) > h4:not(:only-child),
|
|
112
|
+
:is(article[data-v-content], [data-v-openapi-intro], [data-v-openapi-guide]) > h5:not(:only-child),
|
|
113
|
+
:is(article[data-v-content], [data-v-openapi-intro], [data-v-openapi-guide]) > h6:not(:only-child) {
|
|
112
114
|
@apply vocs:pt-4;
|
|
113
115
|
}
|
|
114
116
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Maps Scalar API client theme variables onto Vocs theme tokens so the modal
|
|
3
|
+
* matches the surrounding docs. Scoped to our Scalar host and given extra
|
|
4
|
+
* specificity (`.scalar-app[data-...]`) so it wins over Scalar's own
|
|
5
|
+
* `.light-mode`/`.dark-mode` definitions. Vocs tokens use `light-dark()`, which
|
|
6
|
+
* resolves from the inherited `color-scheme`, so a single value covers both
|
|
7
|
+
* modes. Variables with no clean Vocs equivalent (radius, shadows, brightness)
|
|
8
|
+
* are intentionally left at Scalar's defaults.
|
|
9
|
+
*/
|
|
10
|
+
.scalar-app[data-v-openapi-playground-root],
|
|
11
|
+
.scalar-app[data-v-openapi-playground-root] .light-mode,
|
|
12
|
+
.scalar-app[data-v-openapi-playground-root] .dark-mode {
|
|
13
|
+
/* Surfaces & text.
|
|
14
|
+
* Scalar has four elevation levels; Vocs's `--vocs-color-gray*` tokens are
|
|
15
|
+
* absolute (not theme-adaptive), so map each level with an explicit
|
|
16
|
+
* `light-dark()` to keep elevation monotonic in both modes:
|
|
17
|
+
* light: white -> 13 -> 12 -> 11 (progressively darker)
|
|
18
|
+
* dark: 2 -> 3 -> 4 -> 5 (progressively lighter) */
|
|
19
|
+
--scalar-background-1: light-dark(white, var(--vocs-color-gray2));
|
|
20
|
+
--scalar-background-2: light-dark(var(--vocs-color-gray13), var(--vocs-color-gray3));
|
|
21
|
+
--scalar-background-3: light-dark(var(--vocs-color-gray12), var(--vocs-color-gray4));
|
|
22
|
+
--scalar-background-4: light-dark(var(--vocs-color-gray11), var(--vocs-color-gray5));
|
|
23
|
+
--scalar-background-accent: var(--vocs-color-accenta3);
|
|
24
|
+
--scalar-color-1: var(--vocs-text-color-primary);
|
|
25
|
+
--scalar-color-2: var(--vocs-text-color-secondary);
|
|
26
|
+
--scalar-color-3: var(--vocs-text-color-muted);
|
|
27
|
+
--scalar-color-accent: var(--vocs-color-accent);
|
|
28
|
+
--scalar-border-color: var(--vocs-border-color-primary);
|
|
29
|
+
|
|
30
|
+
/* Links */
|
|
31
|
+
--scalar-link-color: var(--vocs-text-color-link);
|
|
32
|
+
--scalar-link-color-hover: var(--vocs-text-color-link-hover);
|
|
33
|
+
--scalar-link-color-visited: var(--vocs-text-color-link);
|
|
34
|
+
|
|
35
|
+
/* Semantic colors */
|
|
36
|
+
--scalar-color-red: var(--vocs-color-red);
|
|
37
|
+
--scalar-color-danger: var(--vocs-color-red);
|
|
38
|
+
--scalar-color-green: var(--vocs-color-green);
|
|
39
|
+
--scalar-color-blue: var(--vocs-color-blue);
|
|
40
|
+
--scalar-color-yellow: var(--vocs-color-yellow);
|
|
41
|
+
--scalar-color-orange: var(--vocs-color-yellow); /* no Vocs orange */
|
|
42
|
+
--scalar-color-alert: var(--vocs-color-yellow); /* no Vocs orange */
|
|
43
|
+
--scalar-color-purple: var(--vocs-color-iris);
|
|
44
|
+
--scalar-background-danger: var(--vocs-color-destructive-tint);
|
|
45
|
+
|
|
46
|
+
/* Buttons */
|
|
47
|
+
--scalar-button-1: var(--vocs-text-color-heading);
|
|
48
|
+
--scalar-button-1-color: var(--vocs-background-color-surface);
|
|
49
|
+
--scalar-button-1-hover: var(--vocs-text-color-primary);
|
|
50
|
+
|
|
51
|
+
/* Modal sidebar */
|
|
52
|
+
--scalar-sidebar-background-1: var(--vocs-background-color-surface);
|
|
53
|
+
--scalar-sidebar-color-1: var(--vocs-text-color-primary);
|
|
54
|
+
--scalar-sidebar-color-2: var(--vocs-text-color-secondary);
|
|
55
|
+
--scalar-sidebar-border-color: var(--vocs-border-color-primary);
|
|
56
|
+
--scalar-sidebar-item-hover-background: var(--vocs-background-color-primary);
|
|
57
|
+
--scalar-sidebar-item-active-background: var(--vocs-background-color-primary);
|
|
58
|
+
--scalar-sidebar-color-active: var(--vocs-color-accent);
|
|
59
|
+
--scalar-sidebar-search-background: var(--vocs-background-color-primary);
|
|
60
|
+
--scalar-sidebar-search-border-color: var(--vocs-border-color-primary);
|
|
61
|
+
--scalar-sidebar-search-color: var(--vocs-text-color-muted);
|
|
62
|
+
|
|
63
|
+
/* Fonts */
|
|
64
|
+
--scalar-font: var(--vocs-font-sans);
|
|
65
|
+
--scalar-font-code: var(--vocs-font-mono);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/*
|
|
69
|
+
* DataTable enable/disable checkbox (e.g. query/header parameter rows).
|
|
70
|
+
* Scalar always renders the checkmark glyph and only swaps its color: a faint
|
|
71
|
+
* `--scalar-background-2` ghost when unchecked, `--scalar-color-1` when checked.
|
|
72
|
+
* Against Vocs surfaces that ghost reads as a solid (near-black) check, so
|
|
73
|
+
* disabled params look identical to enabled ones. Hide the glyph entirely when
|
|
74
|
+
* the checkbox is unchecked; the checked state keeps Scalar's `text-c-1`. The
|
|
75
|
+
* hover affordance box is a separate `<div>` and is left untouched.
|
|
76
|
+
*/
|
|
77
|
+
.scalar-app input[type="checkbox"].peer:not(:checked) ~ div .scalar-icon,
|
|
78
|
+
.scalar-app input[type="checkbox"].peer:not(:checked) ~ div svg {
|
|
79
|
+
color: transparent;
|
|
80
|
+
}
|