@clidey/dory 1.0.8 → 1.10.0
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/README.md +59 -164
- package/bin/chat-server.ts +270 -0
- package/bin/dist/analytics-server.d.ts +11 -0
- package/bin/dist/analytics-server.d.ts.map +1 -0
- package/bin/dist/analytics-server.js +121 -0
- package/bin/dist/analytics-server.js.map +1 -0
- package/bin/dist/automation.d.ts +35 -0
- package/bin/dist/automation.d.ts.map +1 -0
- package/bin/dist/automation.js +259 -0
- package/bin/dist/automation.js.map +1 -0
- package/bin/dist/chat-server.d.ts +8 -0
- package/bin/dist/chat-server.d.ts.map +1 -0
- package/bin/dist/chat-server.js +322 -0
- package/bin/dist/chat-server.js.map +1 -0
- package/bin/dist/check.d.ts +16 -0
- package/bin/dist/check.d.ts.map +1 -0
- package/bin/dist/check.js +247 -0
- package/bin/dist/check.js.map +1 -0
- package/bin/dist/dependency-overlay.d.ts +7 -0
- package/bin/dist/dependency-overlay.d.ts.map +1 -0
- package/bin/dist/dependency-overlay.js +137 -0
- package/bin/dist/dependency-overlay.js.map +1 -0
- package/bin/dist/dory.js +254 -402
- package/bin/dist/dory.js.map +1 -1
- package/bin/dist/i18n-init.d.ts +6 -0
- package/bin/dist/i18n-init.d.ts.map +1 -0
- package/bin/dist/i18n-init.js +56 -0
- package/bin/dist/i18n-init.js.map +1 -0
- package/bin/dist/mcp-http.d.ts +8 -0
- package/bin/dist/mcp-http.d.ts.map +1 -0
- package/bin/dist/mcp-http.js +72 -0
- package/bin/dist/mcp-http.js.map +1 -0
- package/bin/dist/mcp.d.ts +2 -0
- package/bin/dist/mcp.d.ts.map +1 -0
- package/bin/dist/mcp.js +81 -0
- package/bin/dist/mcp.js.map +1 -0
- package/bin/dist/preview-entry.d.ts +2 -0
- package/bin/dist/preview-entry.d.ts.map +1 -0
- package/bin/dist/preview-entry.js +6 -0
- package/bin/dist/preview-entry.js.map +1 -0
- package/bin/dist/preview-server.d.ts +5 -0
- package/bin/dist/preview-server.d.ts.map +1 -0
- package/bin/dist/preview-server.js +146 -0
- package/bin/dist/preview-server.js.map +1 -0
- package/bin/dist/preview-static.d.ts +7 -0
- package/bin/dist/preview-static.d.ts.map +1 -0
- package/bin/dist/preview-static.js +70 -0
- package/bin/dist/preview-static.js.map +1 -0
- package/bin/dist/project-config.d.ts +22 -0
- package/bin/dist/project-config.d.ts.map +1 -0
- package/bin/dist/project-config.js +44 -0
- package/bin/dist/project-config.js.map +1 -0
- package/dist/index.html +57 -0
- package/dist/runtime/dory-runtime.css +2 -0
- package/dist/runtime/dory-runtime.html +14 -0
- package/dist/runtime/dory-runtime.js +5490 -0
- package/docs/api-playground-demo.mdx +119 -0
- package/docs/api-reference/asyncapi.json +324 -0
- package/docs/api-reference/openapi.json +366 -0
- package/docs/api-reference/operations/create.mdx +5 -0
- package/docs/api-reference/operations/delete.mdx +5 -0
- package/docs/api-reference/operations/get.mdx +5 -0
- package/docs/api-reference/operations/update.mdx +5 -0
- package/docs/api-reference/websocket-operations/receive-chat-message.mdx +5 -0
- package/docs/api-reference/websocket-operations/receive-notifications.mdx +5 -0
- package/docs/api-reference/websocket-operations/receive-presence-update.mdx +5 -0
- package/docs/api-reference/websocket-operations/send-chat-message.mdx +5 -0
- package/docs/api-reference/websocket-operations/send-presence-update.mdx +5 -0
- package/docs/assets/dory.jpeg +0 -0
- package/docs/components/accordion.mdx +145 -0
- package/docs/components/callouts.mdx +134 -0
- package/docs/components/cards.mdx +153 -0
- package/docs/components/code-blocks.mdx +281 -0
- package/docs/components/expandable.mdx +189 -0
- package/docs/components/icons.mdx +71 -0
- package/docs/components/images.mdx +82 -0
- package/docs/components/latex.mdx +148 -0
- package/docs/components/layout.mdx +178 -0
- package/docs/components/lists.mdx +134 -0
- package/docs/components/mermaid.mdx +149 -0
- package/docs/components/overview.mdx +81 -0
- package/docs/components/source.mdx +130 -0
- package/docs/components/steps.mdx +122 -0
- package/docs/components/tables.mdx +114 -0
- package/docs/components/tags.mdx +179 -0
- package/docs/components/typography.mdx +185 -0
- package/docs/configuration/analytics.mdx +45 -0
- package/docs/configuration/chat.mdx +59 -0
- package/docs/configuration/dory-json.mdx +278 -0
- package/docs/configuration/embed.mdx +73 -0
- package/docs/configuration/fonts.mdx +86 -0
- package/docs/configuration/internationalization.mdx +61 -0
- package/docs/configuration/navigation.mdx +217 -0
- package/docs/configuration/theming.mdx +166 -0
- package/docs/contributing.mdx +47 -0
- package/docs/dory.json +136 -0
- package/docs/embed-example.html +176 -0
- package/docs/favicon.svg +18 -0
- package/docs/features.mdx +440 -0
- package/docs/getting-started/automation.mdx +76 -0
- package/docs/getting-started/configuration.mdx +95 -0
- package/docs/getting-started/custom-components.mdx +60 -0
- package/docs/getting-started/installation.mdx +88 -0
- package/docs/getting-started/quick-start.mdx +53 -0
- package/docs/introduction.mdx +50 -0
- package/docs/logo/logo.svg +18 -0
- package/docs/playgrounds/api-playground.mdx +122 -0
- package/docs/playgrounds/websocket-playground.mdx +98 -0
- package/docs/social-card.png +0 -0
- package/docs/social-card.svg +17 -0
- package/docs/websocket-playground-demo.mdx +135 -0
- package/package.json +23 -17
- package/scripts/verify-component-examples.mjs +126 -0
- package/scripts/verify-runtime-parity.mjs +147 -0
- package/src/app.css +22 -1
- package/src/assets/fonts/HankenGrotesk.ttf +0 -0
- package/src/assets/fonts/OFL.txt +94 -0
- package/src/components/chat-assistant.tsx +229 -0
- package/src/components/dropdown.tsx +3 -3
- package/src/components/error-boundary.tsx +2 -2
- package/src/components/hooks.tsx +20 -1
- package/src/components/navigation.tsx +82 -51
- package/src/components/notification.tsx +2 -3
- package/src/components/routes.tsx +17 -19
- package/src/components/search-index.test.ts +15 -0
- package/src/components/search-index.ts +49 -48
- package/src/components/search.tsx +208 -104
- package/src/components/store.ts +27 -9
- package/src/config/mdx-common.ts +11 -0
- package/src/config/mdx.ts +10 -11
- package/src/embed/loader.ts +0 -3
- package/src/embed/types.ts +0 -2
- package/src/embed/widget-iframe.ts +19 -21
- package/src/embedded-runtime/config.ts +138 -0
- package/src/embedded-runtime/index.tsx +680 -0
- package/src/embedded-runtime/runtime.html +14 -0
- package/src/entry-server.tsx +46 -124
- package/src/main.tsx +17 -5
- package/src/mdx/accordion.tsx +3 -3
- package/src/mdx/api-playground.tsx +18 -16
- package/src/mdx/async-api.tsx +2 -2
- package/src/mdx/card.tsx +3 -2
- package/src/mdx/code.tsx +11 -9
- package/src/mdx/fence.tsx +6 -5
- package/src/mdx/icon-grid.tsx +4 -1
- package/src/mdx/icon-names.test.ts +15 -0
- package/src/mdx/icon-names.ts +60 -0
- package/src/mdx/icon-source-variants.ts +102 -0
- package/src/mdx/icon.tsx +12 -73
- package/src/mdx/latex.test.ts +38 -0
- package/src/mdx/latex.tsx +42 -64
- package/src/mdx/mdx.tsx +1 -1
- package/src/mdx/mermaid.tsx +12 -6
- package/src/mdx/open-api.tsx +130 -74
- package/src/mdx/preprocess.test.ts +16 -0
- package/src/mdx/preprocess.ts +61 -0
- package/src/mdx/processor.test.ts +135 -37
- package/src/mdx/processor.ts +54 -92
- package/src/mdx/source.tsx +5 -4
- package/src/mdx/specimen.tsx +57 -43
- package/src/mdx/tabs.tsx +32 -21
- package/src/mdx/text.tsx +11 -11
- package/src/mdx/try-it-panel.tsx +284 -0
- package/src/mdx/type-scale.tsx +7 -7
- package/src/mdx/websocket-playground.tsx +21 -14
- package/src/plugins/analytics-injector.ts +8 -0
- package/src/plugins/chat-dev-server.ts +32 -0
- package/src/plugins/docs-assets.test.ts +25 -0
- package/src/plugins/docs-assets.ts +6 -2
- package/src/plugins/frontmatter-dev-server.ts +2 -2
- package/src/plugins/frontmatter-generator.ts +1 -1
- package/src/plugins/html-files-middleware.test.ts +21 -1
- package/src/plugins/html-files-middleware.ts +26 -6
- package/src/plugins/html-metadata-injector.ts +24 -4
- package/src/plugins/llm-txt-dev-server.ts +2 -2
- package/src/plugins/llm-txt-generator.ts +21 -4
- package/src/plugins/prerender.ts +47 -20
- package/src/plugins/robots-generator.ts +1 -1
- package/src/plugins/sanitize.ts +6 -5
- package/src/plugins/shared.ts +6 -18
- package/src/plugins/sitemap-generator.ts +1 -1
- package/src/plugins/social-preview.test.ts +71 -0
- package/src/plugins/social-preview.ts +87 -0
- package/src/plugins/unified-mdx-plugin.ts +1 -1
- package/src/theme.scss +630 -207
- package/src/types/config.ts +53 -1
- package/src/ui/breadcrumb.tsx +17 -0
- package/src/ui/docs-header-view.tsx +32 -0
- package/src/ui/docs-navigation-view.tsx +69 -0
- package/src/ui/docs-page-frame.tsx +50 -0
- package/src/ui/dory-mark.tsx +9 -0
- package/src/ui/header.tsx +33 -90
- package/src/ui/layout.tsx +99 -153
- package/src/ui/mobile-navigation.tsx +10 -8
- package/src/ui/page-actions.tsx +67 -0
- package/src/ui/page-feedback.tsx +50 -0
- package/src/ui/page-footer.tsx +23 -0
- package/src/ui/prev-next-link.tsx +15 -69
- package/src/ui/prev-next-view.tsx +27 -0
- package/src/ui/table-of-content-view.tsx +167 -0
- package/src/ui/table-of-content.tsx +25 -183
- package/src/utils/analytics.test.ts +36 -0
- package/src/utils/analytics.ts +35 -0
- package/src/utils/colors.ts +7 -0
- package/src/utils/content-scope.test.ts +26 -0
- package/src/utils/content-scope.ts +39 -0
- package/src/utils/search-content.ts +12 -0
- package/src/utils/theme.ts +1 -7
- package/vite.config.ssr.ts +4 -4
- package/vite.config.ts +22 -16
package/src/mdx/processor.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { compile } from '@mdx-js/mdx';
|
|
2
|
-
import {
|
|
3
|
-
import { getMdxConfig } from '../config/mdx';
|
|
1
|
+
import { compile, createProcessor } from '@mdx-js/mdx';
|
|
2
|
+
import { preprocessMdxSource } from './preprocess.ts';
|
|
3
|
+
import { getMdxConfig } from '../config/mdx.ts';
|
|
4
4
|
|
|
5
5
|
export interface ProcessMdxOptions {
|
|
6
6
|
isDevelopment?: boolean;
|
|
@@ -25,101 +25,63 @@ export async function preprocessMdxContent(
|
|
|
25
25
|
error?: Error;
|
|
26
26
|
}> {
|
|
27
27
|
try {
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (yamlBlockMatch) {
|
|
42
|
-
processedContent = processedContent.slice(yamlBlockMatch[0].length);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Step 1: Apply preprocessor (handles MDX tags, code blocks, etc.)
|
|
46
|
-
const preprocessor = preprocessMdxTags();
|
|
47
|
-
|
|
48
|
-
if (preprocessor.transform) {
|
|
49
|
-
const result = preprocessor.transform(processedContent, fileName);
|
|
50
|
-
if (result && typeof result === 'object' && result.code) {
|
|
51
|
-
processedContent = result.code;
|
|
52
|
-
} else if (typeof result === 'string') {
|
|
53
|
-
processedContent = result;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Step 2: Convert URLs in angle brackets to markdown links
|
|
58
|
-
// Handles cases like <https://example.com> which MDX would interpret as JSX
|
|
59
|
-
// But preserves them inside code blocks
|
|
60
|
-
|
|
61
|
-
// Process line by line to handle code blocks correctly
|
|
62
|
-
const lines = processedContent.split('\n');
|
|
63
|
-
let inCodeBlock = false;
|
|
64
|
-
|
|
65
|
-
processedContent = lines.map(line => {
|
|
66
|
-
// Check if we're entering or leaving a code block (with optional indentation)
|
|
67
|
-
if (line.trim().startsWith('```')) {
|
|
68
|
-
inCodeBlock = !inCodeBlock;
|
|
69
|
-
return line;
|
|
28
|
+
const processed = preprocessMdxSource(content, fileName);
|
|
29
|
+
// Only real top-level ESM declarations register static JSX components.
|
|
30
|
+
// The browser's embedded runtime does not call this trusted build processor.
|
|
31
|
+
const importedNames = (source: string) => {
|
|
32
|
+
const names: string[] = [];
|
|
33
|
+
let declarations = 0;
|
|
34
|
+
for (const node of createProcessor().parse(source).children) {
|
|
35
|
+
if (node.type !== 'mdxjsEsm') continue;
|
|
36
|
+
for (const statement of node.data?.estree?.body ?? []) {
|
|
37
|
+
if (statement.type !== 'ImportDeclaration') continue;
|
|
38
|
+
declarations++;
|
|
39
|
+
for (const specifier of statement.specifiers) names.push(specifier.local.name);
|
|
40
|
+
}
|
|
70
41
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
42
|
+
return { names, declarations };
|
|
43
|
+
};
|
|
44
|
+
let importedComponents: string[] = [];
|
|
45
|
+
if (/^import(?:\s|$)/m.test(processed)) {
|
|
46
|
+
try {
|
|
47
|
+
importedComponents = importedNames(processed).names;
|
|
48
|
+
} catch {
|
|
49
|
+
// Escaped closing tags can temporarily make imported JSX with props
|
|
50
|
+
// malformed. Gather provisional names from isolated import declarations.
|
|
51
|
+
// They are never trusted until the complete document's AST verifies them.
|
|
52
|
+
const candidates = new Set<string>();
|
|
53
|
+
const lines = processed.split('\n');
|
|
54
|
+
let fence = '';
|
|
55
|
+
for (let index = 0; index < lines.length; index++) {
|
|
56
|
+
const marker = lines[index].match(/^\s*(`{3,}|~{3,})(.*)$/);
|
|
57
|
+
if (marker) {
|
|
58
|
+
if (!fence) fence = marker[1];
|
|
59
|
+
else if (marker[1][0] === fence[0] && marker[1].length >= fence.length && !marker[2].trim()) fence = '';
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (fence || !/^import(?:\s|$)/.test(lines[index])) continue;
|
|
63
|
+
for (let end = index; end < lines.length; end++) {
|
|
64
|
+
try {
|
|
65
|
+
const result = importedNames(lines.slice(index, end + 1).join('\n'));
|
|
66
|
+
if (!result.declarations) continue;
|
|
67
|
+
for (const name of result.names) candidates.add(name);
|
|
68
|
+
index = end;
|
|
69
|
+
break;
|
|
70
|
+
} catch { /* The declaration may span more lines. */ }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const provisional = preprocessMdxSource(content, fileName, [...candidates]);
|
|
74
|
+
importedComponents = importedNames(provisional).names;
|
|
96
75
|
}
|
|
97
|
-
|
|
98
|
-
// Process remaining text after last inline code (or entire line if no inline code)
|
|
99
|
-
const remainingText = line.substring(lastIndex);
|
|
100
|
-
result += remainingText.replace(/<(https?:\/\/[^\s>]+)>/g, '[$1]($1)');
|
|
101
|
-
|
|
102
|
-
return result;
|
|
103
|
-
}).join('\n');
|
|
104
|
-
|
|
105
|
-
// Step 3: Add any future preprocessing steps here
|
|
106
|
-
// Example: processedContent = await yourNewPreprocessor(processedContent);
|
|
107
|
-
// Example: processedContent = removeHTMLComments(processedContent);
|
|
108
|
-
// Example: processedContent = expandCustomShortcodes(processedContent);
|
|
109
|
-
// ALL preprocessing added here will automatically apply to:
|
|
110
|
-
// - CLI commands (verify:content)
|
|
111
|
-
// - Vite build (pnpm run build)
|
|
112
|
-
// - Vite dev server (pnpm run dev)
|
|
113
|
-
|
|
76
|
+
}
|
|
114
77
|
return {
|
|
115
78
|
success: true,
|
|
116
|
-
processedContent
|
|
79
|
+
processedContent: importedComponents.length
|
|
80
|
+
? preprocessMdxSource(content, fileName, importedComponents)
|
|
81
|
+
: processed,
|
|
117
82
|
};
|
|
118
83
|
} catch (error) {
|
|
119
|
-
return {
|
|
120
|
-
success: false,
|
|
121
|
-
error: error as Error
|
|
122
|
-
};
|
|
84
|
+
return { success: false, error: error as Error };
|
|
123
85
|
}
|
|
124
86
|
}
|
|
125
87
|
|
package/src/mdx/source.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { CatalogIcon } from '@clidey/ux'
|
|
1
2
|
import classNames from 'classnames'
|
|
2
|
-
|
|
3
|
+
|
|
3
4
|
import { useEffect, useState } from 'preact/hooks'
|
|
4
5
|
import { Fence } from './fence'
|
|
5
6
|
|
|
@@ -121,7 +122,7 @@ export const Source = ({ url, branch = "main", paths }: SourceProps) => {
|
|
|
121
122
|
: 'border-[var(--border)] bg-muted text-foreground hover:border-[var(--primary)]',
|
|
122
123
|
)}
|
|
123
124
|
>
|
|
124
|
-
<
|
|
125
|
+
<CatalogIcon name="file" aria-hidden="true" className="h-3.5 w-3.5 text-muted-foreground" />
|
|
125
126
|
{path}
|
|
126
127
|
{range && <span className="text-muted-foreground">{range}</span>}
|
|
127
128
|
</button>
|
|
@@ -129,7 +130,7 @@ export const Source = ({ url, branch = "main", paths }: SourceProps) => {
|
|
|
129
130
|
</div>
|
|
130
131
|
<div className="dory-code mt-3 overflow-hidden rounded-lg border border-[var(--border)] bg-card">
|
|
131
132
|
<div className="flex min-h-10 items-center gap-2 border-b border-[var(--border)] bg-muted px-4">
|
|
132
|
-
<
|
|
133
|
+
<CatalogIcon name="file" aria-hidden="true" className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
|
133
134
|
<p className="mr-auto truncate font-mono text-xs text-foreground">
|
|
134
135
|
{repo} · {current.path}
|
|
135
136
|
{current.range && <span className="ml-2 text-muted-foreground">{current.range}</span>}
|
|
@@ -141,7 +142,7 @@ export const Source = ({ url, branch = "main", paths }: SourceProps) => {
|
|
|
141
142
|
className="inline-flex shrink-0 items-center gap-1 text-xs font-medium text-[var(--primary)] hover:underline"
|
|
142
143
|
>
|
|
143
144
|
{openLabel}
|
|
144
|
-
<
|
|
145
|
+
<CatalogIcon name="external" aria-hidden="true" className="h-3 w-3" />
|
|
145
146
|
</a>
|
|
146
147
|
</div>
|
|
147
148
|
{ready ? (
|
package/src/mdx/specimen.tsx
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
|
-
import classNames from 'classnames'
|
|
2
1
|
import { Children, isValidElement } from 'preact/compat'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { Tabs as UxTabs, TabsList, TabsTrigger, TabsContent, cn } from '@clidey/ux'
|
|
3
|
+
import { handleTabKeyDown } from './tabs'
|
|
4
|
+
import { CopyButton, fencePropsOf } from './code'
|
|
5
|
+
import { Fence, Pre } from './fence'
|
|
6
6
|
|
|
7
|
-
/**
|
|
7
|
+
/** Props tab body: a PROP / DESCRIPTION header followed by <Prop> rows. */
|
|
8
8
|
export function SpecimenProps({ children }: { children: preact.ComponentChildren }) {
|
|
9
|
-
return
|
|
9
|
+
return (
|
|
10
|
+
<div className="dory-props">
|
|
11
|
+
<div className="grid grid-cols-[minmax(0,1fr)_minmax(0,1.7fr)] gap-6 bg-muted px-5 py-2.5 font-mono text-[11px] uppercase tracking-[0.16em] text-muted-foreground">
|
|
12
|
+
<span>Prop</span>
|
|
13
|
+
<span>Description</span>
|
|
14
|
+
</div>
|
|
15
|
+
{children}
|
|
16
|
+
</div>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** One row of a Specimen's Props tab: mono name with its type underneath, and a description. */
|
|
21
|
+
export function Prop({ name, type, children }: { name: string; type?: string; children: preact.ComponentChildren }) {
|
|
22
|
+
return (
|
|
23
|
+
<div className="grid grid-cols-[minmax(0,1fr)_minmax(0,1.7fr)] gap-6 border-t border-[var(--border)] px-5 py-3.5">
|
|
24
|
+
<div className="min-w-0">
|
|
25
|
+
<p className="font-mono text-[13px] font-semibold text-foreground">{name}</p>
|
|
26
|
+
{type && <p className="mt-0.5 font-mono text-xs text-[var(--primary)]">{type}</p>}
|
|
27
|
+
</div>
|
|
28
|
+
<div className="text-sm leading-relaxed text-muted-foreground [&_p]:mb-0">{children}</div>
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
10
31
|
}
|
|
11
32
|
|
|
12
33
|
type SpecimenTab = 'Preview' | 'Code' | 'Props'
|
|
13
34
|
|
|
14
|
-
function
|
|
15
|
-
return isValidElement(child) && (child as preact.VNode).type === Pre && fencePropsOf(child as preact.JSX.Element).language
|
|
35
|
+
function isSourceFence(child: preact.ComponentChild): boolean {
|
|
36
|
+
return isValidElement(child) && (child as preact.VNode).type === Pre && ['mdx', 'tsx', 'jsx'].includes(fencePropsOf(child as preact.JSX.Element).language)
|
|
16
37
|
}
|
|
17
38
|
|
|
18
39
|
function isPropsBlock(child: preact.ComponentChild): boolean {
|
|
@@ -21,49 +42,42 @@ function isPropsBlock(child: preact.ComponentChild): boolean {
|
|
|
21
42
|
|
|
22
43
|
/**
|
|
23
44
|
* Framed component example with Preview / Code / Props tabs.
|
|
24
|
-
* An
|
|
45
|
+
* An MDX, TSX, or JSX fence becomes the Code tab, a <SpecimenProps> block becomes the
|
|
25
46
|
* Props tab, and everything else is the rendered preview.
|
|
26
47
|
*/
|
|
27
|
-
export function Specimen({ label, children
|
|
48
|
+
export function Specimen({ label, children, padding = 'default' }: {
|
|
49
|
+
label?: string
|
|
50
|
+
children: preact.ComponentChildren
|
|
51
|
+
/** Use none only when the example supplies its own preview spacing. */
|
|
52
|
+
padding?: 'default' | 'none'
|
|
53
|
+
}) {
|
|
54
|
+
const previewClassName = cn('m-0 min-w-0 [&>:first-child]:mt-0 [&>:last-child]:mb-0', padding === 'none' ? 'p-0' : 'p-5 sm:p-6')
|
|
28
55
|
const items = Children.toArray(children)
|
|
29
|
-
const code = items.filter(
|
|
56
|
+
const code = items.filter(isSourceFence)
|
|
30
57
|
const props = items.filter(isPropsBlock)
|
|
31
|
-
const preview = items.filter((child) => !
|
|
58
|
+
const preview = items.filter((child) => !isSourceFence(child) && !isPropsBlock(child))
|
|
32
59
|
|
|
33
60
|
const tabs: SpecimenTab[] = ['Preview']
|
|
34
61
|
if (code.length > 0) tabs.push('Code')
|
|
35
62
|
if (props.length > 0) tabs.push('Props')
|
|
36
63
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<div className="flex min-h-10 items-center justify-between gap-3 border-b border-[var(--border)] py-1.5 pl-4 pr-1.5">
|
|
43
|
-
<p className="truncate font-mono text-xs text-[var(--primary)]">{label}</p>
|
|
44
|
-
{tabs.length > 1 && (
|
|
45
|
-
<div role="tablist" className="flex shrink-0 rounded-md bg-muted p-0.5">
|
|
46
|
-
{tabs.map((tab) => (
|
|
47
|
-
<button
|
|
48
|
-
key={tab}
|
|
49
|
-
type="button"
|
|
50
|
-
role="tab"
|
|
51
|
-
aria-selected={active === tab}
|
|
52
|
-
onClick={() => setActive(tab)}
|
|
53
|
-
className={classNames('rounded-[5px] px-2.5 py-1 text-xs font-medium transition', {
|
|
54
|
-
'bg-card text-foreground shadow-[0_1px_2px_rgb(14_34_64/0.08)] dark:bg-[var(--border)]': active === tab,
|
|
55
|
-
'text-muted-foreground hover:text-foreground': active !== tab,
|
|
56
|
-
})}
|
|
57
|
-
>
|
|
58
|
-
{tab}
|
|
59
|
-
</button>
|
|
60
|
-
))}
|
|
61
|
-
</div>
|
|
62
|
-
)}
|
|
63
|
-
</div>
|
|
64
|
-
<div role="tabpanel" className="p-5 [&>:first-child]:mt-0 [&>:last-child]:mb-0">
|
|
65
|
-
{panels[active]}
|
|
66
|
-
</div>
|
|
64
|
+
const renderCode = () => code.map((child, index) => {
|
|
65
|
+
const fence = fencePropsOf(child as preact.JSX.Element)
|
|
66
|
+
return <div key={index} className="relative max-h-[32rem] overflow-auto px-5 py-4 font-mono text-[12.5px] leading-[1.7]">
|
|
67
|
+
<Fence language={fence.language} meta={{ ...fence.meta, title: undefined }}>{fence.code}</Fence>
|
|
68
|
+
<CopyButton code={fence.code} />
|
|
67
69
|
</div>
|
|
68
|
-
)
|
|
70
|
+
})
|
|
71
|
+
if (tabs.length === 1) return <div className="dory-specimen not-prose my-5 overflow-hidden rounded-lg border border-[var(--border)] bg-card"><div className="px-4 py-3 font-mono text-xs text-[var(--primary)]">{label}</div><div data-dory-preview data-padding={padding} className={previewClassName}>{preview}</div></div>
|
|
72
|
+
return <UxTabs defaultValue="Preview" className="dory-specimen not-prose my-5 gap-0 overflow-hidden rounded-lg border border-[var(--border)] bg-card">
|
|
73
|
+
<div className="flex min-h-12 flex-wrap items-center justify-between gap-3 border-b border-[var(--border)] py-2 pl-4 pr-2">
|
|
74
|
+
<p className="truncate font-mono text-xs text-[var(--primary)]">{label}</p>
|
|
75
|
+
<TabsList aria-label={`${label ?? 'Component'} example views`} onKeyDown={handleTabKeyDown}>
|
|
76
|
+
{tabs.map(tab => <TabsTrigger key={tab} value={tab}>{tab}</TabsTrigger>)}
|
|
77
|
+
</TabsList>
|
|
78
|
+
</div>
|
|
79
|
+
<TabsContent value="Preview" data-dory-preview data-padding={padding} className={previewClassName}>{preview}</TabsContent>
|
|
80
|
+
{code.length > 0 && <TabsContent value="Code" className="m-0 p-0">{renderCode()}</TabsContent>}
|
|
81
|
+
{props.length > 0 && <TabsContent value="Props" className="m-0 p-0">{props}</TabsContent>}
|
|
82
|
+
</UxTabs>
|
|
69
83
|
}
|
package/src/mdx/tabs.tsx
CHANGED
|
@@ -1,39 +1,50 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Tabs as UxTabs, TabsContent, TabsList, TabsTrigger } from '@clidey/ux'
|
|
2
2
|
import { Children, isValidElement } from 'preact/compat'
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
/** Keyboard fallback for UX tabs rendered through Preact compatibility. */
|
|
5
|
+
export function handleTabKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
|
6
|
+
// Radix roving focus does not handle arrow keys through Preact compat.
|
|
7
|
+
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return
|
|
8
|
+
const tabs = Array.from(event.currentTarget.querySelectorAll<HTMLButtonElement>('[role="tab"]:not([disabled])'))
|
|
9
|
+
const current = tabs.indexOf(event.target as HTMLButtonElement)
|
|
10
|
+
if (current < 0 || tabs.length === 0) return
|
|
11
|
+
event.preventDefault()
|
|
12
|
+
const next = event.key === 'Home' ? 0 : event.key === 'End' ? tabs.length - 1
|
|
13
|
+
: (current + (event.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length
|
|
14
|
+
tabs[next].focus()
|
|
15
|
+
tabs[next].click()
|
|
16
|
+
}
|
|
4
17
|
|
|
5
18
|
/** One tab panel inside <Tabs>. Renders its children when active. */
|
|
6
19
|
export function Tab({ children }: { title: string; children: preact.ComponentChildren }) {
|
|
7
20
|
return <>{children}</>
|
|
8
21
|
}
|
|
9
22
|
|
|
10
|
-
/**
|
|
23
|
+
/** UX tab strip for any MDX content, e.g. several diagrams of one thing. */
|
|
11
24
|
export function Tabs({ children }: { children: preact.ComponentChildren }) {
|
|
12
25
|
const panels = Children.toArray(children).filter(isValidElement) as preact.VNode<{ title: string }>[]
|
|
13
|
-
const [active, setActive] = useState(0)
|
|
14
26
|
|
|
15
27
|
return (
|
|
16
|
-
<
|
|
17
|
-
<
|
|
28
|
+
<UxTabs defaultValue="0" className="dory-tabs not-prose my-5">
|
|
29
|
+
<TabsList
|
|
30
|
+
className="max-w-full justify-start overflow-x-auto"
|
|
31
|
+
onKeyDown={handleTabKeyDown}
|
|
32
|
+
>
|
|
18
33
|
{panels.map((panel, index) => (
|
|
19
|
-
<
|
|
34
|
+
<TabsTrigger
|
|
20
35
|
key={index}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
aria-selected={active === index}
|
|
24
|
-
onClick={() => setActive(index)}
|
|
25
|
-
className={classNames('-mb-px border-b-2 pb-2.5 pt-1 text-sm font-medium transition', {
|
|
26
|
-
'border-[var(--primary)] text-foreground': active === index,
|
|
27
|
-
'border-transparent text-muted-foreground hover:text-foreground': active !== index,
|
|
28
|
-
})}
|
|
36
|
+
value={String(index)}
|
|
37
|
+
className="whitespace-nowrap"
|
|
29
38
|
>
|
|
30
39
|
{panel.props.title}
|
|
31
|
-
</
|
|
40
|
+
</TabsTrigger>
|
|
32
41
|
))}
|
|
33
|
-
</
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
</TabsList>
|
|
43
|
+
{panels.map((panel, index) => (
|
|
44
|
+
<TabsContent key={index} value={String(index)} className="pt-4 [&>:first-child]:mt-0 [&>:last-child]:mb-0">
|
|
45
|
+
{panel}
|
|
46
|
+
</TabsContent>
|
|
47
|
+
))}
|
|
48
|
+
</UxTabs>
|
|
38
49
|
)
|
|
39
50
|
}
|
package/src/mdx/text.tsx
CHANGED
|
@@ -4,42 +4,42 @@ import classNames from "classnames"
|
|
|
4
4
|
import { useState } from "preact/hooks"
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
export const h1 = function H1(props: preact.
|
|
7
|
+
export const h1 = function H1(props: preact.ComponentProps<'h1'>) {
|
|
8
8
|
return <h1 className="dory-h1 font-heading mb-3 mt-10" {...props} />
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export const h2 = function H2({ children, ...props }: preact.
|
|
11
|
+
export const h2 = function H2({ children, ...props }: preact.ComponentProps<'h2'>) {
|
|
12
12
|
return (
|
|
13
|
-
<h2 className="dory-h2 font-heading mb-
|
|
13
|
+
<h2 className="dory-h2 font-heading mb-4 mt-16" {...props}>
|
|
14
14
|
<span>{children}</span>
|
|
15
15
|
{props.id && <a href={`#${props.id}`} className="dory-anchor" aria-label="Link to this section">#</a>}
|
|
16
16
|
</h2>
|
|
17
17
|
)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export const h3 = function H3(props: preact.
|
|
21
|
-
return <h3 className="dory-h3 font-heading mb-
|
|
20
|
+
export const h3 = function H3(props: preact.ComponentProps<'h3'>) {
|
|
21
|
+
return <h3 className="dory-h3 font-heading mb-3 mt-10" {...props} />
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export const h4 = function H4(props: preact.
|
|
24
|
+
export const h4 = function H4(props: preact.ComponentProps<'h4'>) {
|
|
25
25
|
return <h4 className="dory-h4 font-heading mb-1.5 mt-5" {...props} />
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export const h5 = function H5(props: preact.
|
|
28
|
+
export const h5 = function H5(props: preact.ComponentProps<'h5'>) {
|
|
29
29
|
return <h5 className="text-lg font-bold mb-2" {...props} />
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export const h6 = function H6(props: preact.
|
|
32
|
+
export const h6 = function H6(props: preact.ComponentProps<'h6'>) {
|
|
33
33
|
return <h6 className="text-base font-bold mb-2" {...props} />
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export const p = function P(props: preact.
|
|
36
|
+
export const p = function P(props: preact.ComponentProps<'p'>) {
|
|
37
37
|
return <p className="mb-2 inline-block" {...props} />
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export function Row({ children, cols = 1, className }: { children: preact.ComponentChildren, cols?: number, className?: string }) {
|
|
41
41
|
return (
|
|
42
|
-
<div className={cn(`py-6 grid grid-cols-1 items-start gap-
|
|
42
|
+
<div className={cn(`dory-row py-6 grid min-w-0 grid-cols-1 items-start gap-6 sm:grid-cols-[repeat(var(--dory-columns),minmax(0,1fr))] xl:max-w-none`, className)} style={{ '--dory-columns': cols }}>
|
|
43
43
|
{children}
|
|
44
44
|
</div>
|
|
45
45
|
)
|
|
@@ -58,7 +58,7 @@ export function Col({
|
|
|
58
58
|
}) {
|
|
59
59
|
return (
|
|
60
60
|
<div
|
|
61
|
-
className={classNames("[&>:first-child]:mt-0 [&>:last-child]:mb-0", {
|
|
61
|
+
className={classNames("min-w-0 [&>:first-child]:mt-0 [&>:last-child]:mb-0", {
|
|
62
62
|
'xl:sticky xl:top-24': sticky,
|
|
63
63
|
})}
|
|
64
64
|
style={{
|