@clidey/dory 1.0.7 → 1.9.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 +12 -1
- 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/dory.js +252 -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 +159 -0
- package/bin/dist/preview-server.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 +175 -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/dory.json +129 -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/installation.mdx +88 -0
- package/docs/getting-started/quick-start.mdx +53 -0
- package/docs/getting-started/whodb-ee-apps.mdx +122 -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-runtime-parity.mjs +146 -0
- package/src/app.css +19 -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 +16 -4
- 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.tsx +3 -2
- 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.ts +4 -96
- package/src/mdx/source.tsx +5 -4
- package/src/mdx/specimen.tsx +38 -6
- package/src/mdx/tabs.tsx +29 -21
- package/src/mdx/text.tsx +9 -9
- package/src/mdx/try-it-panel.tsx +284 -0
- package/src/mdx/type-scale.tsx +4 -4
- 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 +1 -1
- 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 +3 -3
- package/vite.config.ts +21 -15
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'none'; img-src https: data: blob:; font-src 'self' data:; base-uri 'none'; form-action 'none'">
|
|
7
|
+
<title>Dory documentation</title>
|
|
8
|
+
<link rel="stylesheet" href="./dory-runtime.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body style="margin:0;min-height:100vh">
|
|
11
|
+
<div id="dory-root"></div>
|
|
12
|
+
<script src="./dory-runtime.js"></script>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
package/src/entry-server.tsx
CHANGED
|
@@ -2,26 +2,34 @@ import type { ComponentType } from 'preact';
|
|
|
2
2
|
import { renderToString } from 'preact-render-to-string';
|
|
3
3
|
import { Router } from 'wouter-preact';
|
|
4
4
|
import { MDXProvider } from '@mdx-js/preact';
|
|
5
|
-
import { ThemeProvider } from '@clidey/ux';
|
|
5
|
+
import { ThemeProvider, Toaster } from '@clidey/ux';
|
|
6
6
|
import * as mdxComponents from './mdx/mdx';
|
|
7
7
|
import docsConfig from '../docs/dory.json' with { type: 'json' };
|
|
8
8
|
import type { DoryConfig } from './types/config';
|
|
9
9
|
import { PageMeta } from './ui/page-meta';
|
|
10
|
-
import {
|
|
10
|
+
import { Header } from './ui/header';
|
|
11
|
+
import { Navigation } from './components/navigation';
|
|
12
|
+
import { Breadcrumb } from './ui/breadcrumb';
|
|
13
|
+
import { PageFeedback } from './ui/page-feedback';
|
|
14
|
+
import { PageFooter } from './ui/page-footer';
|
|
15
|
+
import { PrevNextLinks } from './ui/prev-next-link';
|
|
16
|
+
import { TableOfContents } from './ui/table-of-content';
|
|
17
|
+
import { BASE_PATH, withBase } from './utils/base-path';
|
|
18
|
+
import { contentScope } from './utils/content-scope';
|
|
11
19
|
|
|
12
20
|
const config = docsConfig as DoryConfig;
|
|
13
21
|
|
|
14
|
-
const navigationTabs = config.navigation?.tabs ?? [];
|
|
15
22
|
if (!config.navigation?.tabs) {
|
|
16
23
|
console.warn('dory.json has no navigation.tabs — SSR navigation will be empty.');
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
function pathFromFilename(filename: string): string {
|
|
20
|
-
|
|
27
|
+
const path = filename
|
|
21
28
|
.replace(/^.*?docs/, '')
|
|
22
29
|
.replace(/\/?index\.mdx$/, '/')
|
|
23
30
|
.replace(/\.mdx$/, '')
|
|
24
31
|
.toLowerCase();
|
|
32
|
+
return path.length > 1 ? path.replace(/\/$/, '') : path;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
const pages = import.meta.glob('../docs/**/*.mdx');
|
|
@@ -44,19 +52,21 @@ interface NavTab {
|
|
|
44
52
|
groups: NavGroup[];
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
function buildNavigation(frontmatter?: Record<string, any>[]): NavTab[] {
|
|
48
|
-
|
|
55
|
+
function buildNavigation(routePath: string, frontmatter?: Record<string, any>[]): NavTab[] {
|
|
56
|
+
const scope = contentScope(config, routePath);
|
|
57
|
+
return scope.navigation.tabs.map(tab => ({
|
|
49
58
|
title: tab.tab,
|
|
50
59
|
groups: tab.groups.map(group => ({
|
|
51
60
|
title: group.group,
|
|
52
|
-
pages: group.pages.map(
|
|
53
|
-
const
|
|
61
|
+
pages: group.pages.map(entry => {
|
|
62
|
+
const page = typeof entry === 'string' ? entry : entry.page;
|
|
63
|
+
const href = `${scope.prefix}/${page}`;
|
|
54
64
|
const fm = frontmatter?.find(f => f.path === href);
|
|
55
65
|
return {
|
|
56
66
|
title: fm?.title || page.charAt(0).toUpperCase() + page.slice(1).replace(/-/g, ' '),
|
|
57
67
|
href,
|
|
58
68
|
};
|
|
59
|
-
})
|
|
69
|
+
}).filter(page => !scope.prefix || Boolean(allPages[page.href]))
|
|
60
70
|
}))
|
|
61
71
|
}));
|
|
62
72
|
}
|
|
@@ -69,7 +79,7 @@ export async function render(routePath: string, frontmatter?: Record<string, any
|
|
|
69
79
|
const module = await loader() as { default: ComponentType };
|
|
70
80
|
const Content = module.default;
|
|
71
81
|
|
|
72
|
-
const navigation = buildNavigation(frontmatter);
|
|
82
|
+
const navigation = buildNavigation(routePath, frontmatter);
|
|
73
83
|
const pageFm = frontmatter?.find(fm => fm.path === routePath);
|
|
74
84
|
|
|
75
85
|
// Find group/page titles (Layout uses swapped names: "title: group, group: title")
|
|
@@ -87,107 +97,35 @@ export async function render(routePath: string, frontmatter?: Record<string, any
|
|
|
87
97
|
}
|
|
88
98
|
}
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
const allLinks = navigation.flatMap(tab =>
|
|
92
|
-
tab.groups.flatMap(group => group.pages)
|
|
93
|
-
);
|
|
94
|
-
const linkIndex = allLinks.findIndex(link => link.href === routePath);
|
|
95
|
-
const prevPage = linkIndex > 0 ? allLinks[linkIndex - 1] : null;
|
|
96
|
-
const nextPage = linkIndex < allLinks.length - 1 ? allLinks[linkIndex + 1] : null;
|
|
100
|
+
const isOpenAPIPage = Boolean(pageFm?.openapi);
|
|
97
101
|
|
|
98
102
|
return renderToString(
|
|
99
103
|
<ThemeProvider>
|
|
104
|
+
<Toaster />
|
|
100
105
|
<Router base={BASE_PATH} ssrPath={withBase(routePath)}>
|
|
101
106
|
<MDXProvider components={mdxComponents}>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<header class="sticky top-0 z-50 flex flex-none flex-wrap items-center justify-between bg-white px-4 py-5 shadow-md shadow-gray-900/5 sm:px-6 lg:px-8 dark:shadow-none dark:bg-transparent">
|
|
112
|
-
<div class="relative flex justify-between gap-6 sm:gap-8 md:grow items-center w-full px-4">
|
|
113
|
-
<div class="flex items-center grow gap-2">
|
|
114
|
-
{config.logo && (
|
|
115
|
-
<>
|
|
116
|
-
<img src={assetUrl(config.logo.light)} alt="logo" class="w-8 hidden dark:block" />
|
|
117
|
-
<img src={assetUrl(config.logo.dark)} alt="logo" class="w-8 block dark:hidden" />
|
|
118
|
-
</>
|
|
119
|
-
)}
|
|
120
|
-
<h1 class="text-base lg:text-2xl font-bold">{config.name}</h1>
|
|
121
|
-
</div>
|
|
122
|
-
<nav class="hidden md:block">
|
|
123
|
-
<ul role="list" class="flex items-center gap-8">
|
|
124
|
-
{navigation.map(tab => {
|
|
125
|
-
const isActive = tab === currentTab;
|
|
126
|
-
return (
|
|
127
|
-
<li key={tab.title}>
|
|
128
|
-
<a
|
|
129
|
-
href={withBase(tab.groups[0].pages[0].href)}
|
|
130
|
-
class={`text-xs transition hover:opacity-80${isActive ? ' text-brand-foreground' : ''}`}
|
|
131
|
-
>
|
|
132
|
-
{tab.title}
|
|
133
|
-
</a>
|
|
134
|
-
</li>
|
|
135
|
-
);
|
|
136
|
-
})}
|
|
137
|
-
</ul>
|
|
138
|
-
</nav>
|
|
139
|
-
<div class="hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-white/15" />
|
|
140
|
-
{/* Search, ModeToggle, MobileNav — rendered client-side during hydration.
|
|
141
|
-
Static placeholders cause ref mismatches with Algolia autocomplete. */}
|
|
142
|
-
<div class="flex items-center gap-3" data-testid="header-actions" />
|
|
143
|
-
</div>
|
|
144
|
-
</header>
|
|
145
|
-
|
|
146
|
-
{/* Container — matches src/ui/layout.tsx structure */}
|
|
147
|
-
<div id="container" class="relative flex w-full max-w-8xl sm:px-2 lg:px-8">
|
|
148
|
-
<div class="hidden lg:relative lg:block lg:flex-none">
|
|
149
|
-
<div class="sticky top-[4.75rem] -ml-0.5 h-[calc(100vh-4.75rem)] w-64 overflow-x-hidden overflow-y-auto py-16 pr-8 pl-0.5 xl:w-72 xl:pr-16">
|
|
150
|
-
<nav class="dory-navigation text-base lg:text-sm">
|
|
151
|
-
<ul role="list" class="space-y-9">
|
|
152
|
-
{currentTab && (
|
|
153
|
-
<li>
|
|
154
|
-
<h2 class="font-display font-medium text-xs">{currentTab.title}</h2>
|
|
155
|
-
{currentTab.groups.map(group => (
|
|
156
|
-
<div key={group.title}>
|
|
157
|
-
<h3 class="mt-4 text-sm font-semibold">{group.title}</h3>
|
|
158
|
-
<ul role="list" class="mt-2 space-y-2 border-l-2 border-border lg:mt-4 lg:space-y-4 lg:border-border">
|
|
159
|
-
{group.pages.map(page => (
|
|
160
|
-
<li key={page.href} class="relative">
|
|
161
|
-
<a
|
|
162
|
-
href={withBase(page.href)}
|
|
163
|
-
class={routePath === page.href
|
|
164
|
-
? 'block w-full pl-3.5 font-semibold text-brand-foreground before:pointer-events-none before:absolute before:top-1/2 before:-left-1 before:h-1.5 before:w-1.5 before:-translate-y-1/2 before:rounded-full'
|
|
165
|
-
: 'block w-full pl-3.5 text-muted-foreground before:hidden before:bg-slate-300 hover:text-muted-foreground hover:before:block dark:text-muted-foreground dark:before:bg-slate-700 dark:hover:text-slate-300 before:pointer-events-none before:absolute before:top-1/2 before:-left-1 before:h-1.5 before:w-1.5 before:-translate-y-1/2 before:rounded-full'}
|
|
166
|
-
>
|
|
167
|
-
{page.title}
|
|
168
|
-
</a>
|
|
169
|
-
</li>
|
|
170
|
-
))}
|
|
171
|
-
</ul>
|
|
172
|
-
</div>
|
|
173
|
-
))}
|
|
174
|
-
</li>
|
|
175
|
-
)}
|
|
176
|
-
</ul>
|
|
177
|
-
</nav>
|
|
107
|
+
{/* SYNC NOTE: this shell mirrors src/ui/layout.tsx (non-landing return).
|
|
108
|
+
Header and Navigation are the real components; Header gets `ssr` so the
|
|
109
|
+
client-only widgets (search, theme toggle, mobile menu) mount on hydration. */}
|
|
110
|
+
<div class="dory-shell flex min-h-screen w-full flex-col bg-[var(--sidebar)]">
|
|
111
|
+
<Header ssr />
|
|
112
|
+
<div class="flex flex-1">
|
|
113
|
+
<aside class="hidden w-60 shrink-0 lg:block">
|
|
114
|
+
<div class="sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto px-3 pb-6 pt-4">
|
|
115
|
+
<Navigation frontmatter={frontmatter} />
|
|
178
116
|
</div>
|
|
179
|
-
</
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
<div class="flex justify-between
|
|
185
|
-
<
|
|
186
|
-
{groupTitle && <h1 class="font-display text-lg tracking-tight">{groupTitle}</h1>}
|
|
187
|
-
{pageFm?.title && <p class="text-4xl tracking-tight">{pageFm.title}</p>}
|
|
188
|
-
{pageFm?.lead && <p class="dory-lead">{pageFm.lead}</p>}
|
|
189
|
-
</header>
|
|
117
|
+
</aside>
|
|
118
|
+
<main class="dory-main min-w-0 flex-1 bg-background text-foreground px-6 py-10 sm:px-10 lg:px-12">
|
|
119
|
+
<div class="mx-auto flex w-full max-w-[66rem] justify-center gap-14">
|
|
120
|
+
<article class={`dory-article flex min-w-0 flex-1 flex-col ${isOpenAPIPage ? 'max-w-[66rem]' : 'max-w-[47rem]'}`}>
|
|
121
|
+
{/* Page actions (Copy page / AI actions) are client-only and mount on hydration. */}
|
|
122
|
+
<div class="mb-6 flex min-h-9 flex-wrap items-center justify-between gap-4">
|
|
123
|
+
<Breadcrumb group={groupTitle} title={pageFm?.title} />
|
|
190
124
|
</div>
|
|
125
|
+
<header class="mb-10">
|
|
126
|
+
{pageFm?.title && <h1 class="dory-page-title">{pageFm.title}</h1>}
|
|
127
|
+
{pageFm?.lead && <p class="dory-lead">{pageFm.lead}</p>}
|
|
128
|
+
</header>
|
|
191
129
|
<PageMeta meta={pageFm?.meta} />
|
|
192
130
|
<div class="flex-1 min-h-[calc(100vh-2rem)]">
|
|
193
131
|
{/* Matches App > div.antialiased > div.flex > Routes > Content */}
|
|
@@ -197,27 +135,11 @@ export async function render(routePath: string, frontmatter?: Record<string, any
|
|
|
197
135
|
</div>
|
|
198
136
|
</div>
|
|
199
137
|
</div>
|
|
200
|
-
{
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
<div>
|
|
204
|
-
<p class="font-display text-sm font-medium">Previous</p>
|
|
205
|
-
<div class="mt-1">
|
|
206
|
-
<a href={withBase(prevPage.href)} class="text-base font-semibold hover:opacity-80">{prevPage.title}</a>
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
)}
|
|
210
|
-
{nextPage && (
|
|
211
|
-
<div class="ml-auto text-right">
|
|
212
|
-
<p class="font-display text-sm font-medium">Next</p>
|
|
213
|
-
<div class="mt-1">
|
|
214
|
-
<a href={withBase(nextPage.href)} class="text-base font-semibold hover:opacity-80">{nextPage.title}</a>
|
|
215
|
-
</div>
|
|
216
|
-
</div>
|
|
217
|
-
)}
|
|
218
|
-
</div>
|
|
219
|
-
)}
|
|
138
|
+
<PageFeedback pathname={routePath} />
|
|
139
|
+
<PrevNextLinks navigation={navigation} />
|
|
140
|
+
<PageFooter />
|
|
220
141
|
</article>
|
|
142
|
+
{!isOpenAPIPage && <TableOfContents />}
|
|
221
143
|
</div>
|
|
222
144
|
</main>
|
|
223
145
|
</div>
|
package/src/main.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import { render, hydrate } from 'preact'
|
|
|
2
2
|
import { Router } from 'wouter-preact'
|
|
3
3
|
import '@clidey/ux/styles.css';
|
|
4
4
|
import '@clidey/ux/brand.css';
|
|
5
|
+
import '@clidey/ux/theme-fonts.css';
|
|
5
6
|
import { App } from './app.tsx'
|
|
6
7
|
import { withMDX } from './components/mdx-provider.tsx'
|
|
7
8
|
import { ErrorBoundary } from './components/error-boundary.tsx'
|
|
@@ -13,7 +14,7 @@ import { ThemeProvider } from '@clidey/ux'
|
|
|
13
14
|
import { preloadFrontmatter } from './components/store'
|
|
14
15
|
import docsConfig from '../docs/dory.json' with { type: 'json' };
|
|
15
16
|
import type { DoryConfig } from './types/config';
|
|
16
|
-
import { BASE_PATH } from './utils/base-path';
|
|
17
|
+
import { BASE_PATH, withBase } from './utils/base-path';
|
|
17
18
|
|
|
18
19
|
const config = docsConfig as DoryConfig;
|
|
19
20
|
|
|
@@ -29,11 +30,19 @@ const AppWithMDX = withMDX(App)
|
|
|
29
30
|
|
|
30
31
|
const appRoot = document.getElementById('app')!
|
|
31
32
|
const hasSSRContent = appRoot.children.length > 0
|
|
33
|
+
const currentPath = window.location.pathname.replace(/\/$/, '') || '/'
|
|
34
|
+
const renderedPath = window.__DORY_ROUTE__ ? withBase(window.__DORY_ROUTE__).replace(/\/$/, '') || '/' : undefined
|
|
35
|
+
const rootPath = withBase('/').replace(/\/$/, '') || '/'
|
|
36
|
+
const canHydrate = hasSSRContent && (!renderedPath || currentPath === renderedPath || currentPath === rootPath)
|
|
32
37
|
|
|
33
|
-
const
|
|
38
|
+
const embedParams = new URLSearchParams(window.location.search);
|
|
39
|
+
const requestedTheme = embedParams.get('embedded') === 'true' ? embedParams.get('theme') : null;
|
|
40
|
+
const embedTheme = requestedTheme === 'dark' || requestedTheme === 'light' ? requestedTheme : null;
|
|
41
|
+
const defaultTheme = embedTheme ?? (config.theme?.mode === 'dark' ? 'dark' : config.theme?.mode === 'light' ? 'light' : 'system');
|
|
42
|
+
if (embedTheme) document.documentElement.style.colorScheme = embedTheme;
|
|
34
43
|
|
|
35
44
|
const tree = (
|
|
36
|
-
<ThemeProvider defaultTheme={defaultTheme} storageKey=
|
|
45
|
+
<ThemeProvider defaultTheme={defaultTheme} storageKey={embedTheme ? `@clidey/dory/embed-theme-${embedTheme}` : '@clidey/dory/theme'}>
|
|
37
46
|
<Toaster />
|
|
38
47
|
<ErrorBoundary>
|
|
39
48
|
<Router base={BASE_PATH}>
|
|
@@ -45,8 +54,11 @@ const tree = (
|
|
|
45
54
|
|
|
46
55
|
// If SSR content exists, hydrate (attach JS to existing DOM).
|
|
47
56
|
// Otherwise, render from scratch (dev mode, non-SSR pages).
|
|
48
|
-
if (
|
|
57
|
+
if (canHydrate) {
|
|
49
58
|
hydrate(tree, appRoot)
|
|
50
59
|
} else {
|
|
60
|
+
// A static host may serve the prerendered index.html for an unknown URL.
|
|
61
|
+
// Its first-page markup cannot hydrate into the 404 view.
|
|
62
|
+
if (hasSSRContent) appRoot.replaceChildren()
|
|
51
63
|
render(tree, appRoot)
|
|
52
64
|
}
|
package/src/mdx/accordion.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CatalogIcon,
|
|
2
2
|
Accordion as UXAccordion,
|
|
3
3
|
AccordionItem,
|
|
4
4
|
AccordionTrigger,
|
|
5
5
|
AccordionContent
|
|
6
6
|
} from '@clidey/ux'
|
|
7
7
|
import classNames from 'classnames'
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
import { Icon } from './icon'
|
|
10
10
|
|
|
11
11
|
export function AccordionGroup({ children, numbered = false }: { children: React.ReactNode; numbered?: boolean }) {
|
|
@@ -32,7 +32,7 @@ export function Accordion({
|
|
|
32
32
|
{icon && <Icon icon={icon} className="h-3.5 w-3.5 !stroke-[var(--primary)] text-[var(--primary)]" />}
|
|
33
33
|
<span className="text-sm font-semibold">{title}</span>
|
|
34
34
|
</div>
|
|
35
|
-
<
|
|
35
|
+
<CatalogIcon name="plus" aria-hidden="true" className="size-3.5 shrink-0 text-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-45" />
|
|
36
36
|
</AccordionTrigger>
|
|
37
37
|
<AccordionContent>
|
|
38
38
|
<div className="px-4 pb-3 leading-relaxed text-muted-foreground">
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { Button, TextArea, useTheme, Input } from '@clidey/ux'
|
|
1
|
+
import { CatalogIcon, Button, TextArea, useTheme, Input as UxInput } from '@clidey/ux'
|
|
2
|
+
|
|
3
|
+
// The ux Input is typed against React's input props, which Preact 11's compat types no longer expose.
|
|
4
|
+
const Input = UxInput as unknown as (props: preact.JSX.IntrinsicElements['input']) => preact.JSX.Element
|
|
2
5
|
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
|
|
3
|
-
|
|
6
|
+
|
|
4
7
|
import classNames from 'classnames'
|
|
5
|
-
|
|
8
|
+
|
|
6
9
|
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
|
7
10
|
import { Highlight, themes } from 'prism-react-renderer'
|
|
8
11
|
import Dropdown from '../components/dropdown'
|
|
@@ -176,10 +179,10 @@ export const HeadersTab = ({ requestConfig, setRequestConfig }: HeadersTabProps)
|
|
|
176
179
|
className="flex-1 text-sm"
|
|
177
180
|
/>
|
|
178
181
|
<Button onClick={() => saveHeader(idx)}>
|
|
179
|
-
<
|
|
182
|
+
<CatalogIcon name="check" className="w-4 h-4" />
|
|
180
183
|
</Button>
|
|
181
184
|
<Button onClick={() => cancelEdit(idx)}>
|
|
182
|
-
<
|
|
185
|
+
<CatalogIcon name="close" className="w-4 h-4" />
|
|
183
186
|
</Button>
|
|
184
187
|
</>
|
|
185
188
|
) : (
|
|
@@ -187,17 +190,17 @@ export const HeadersTab = ({ requestConfig, setRequestConfig }: HeadersTabProps)
|
|
|
187
190
|
<span className="text-sm w-32 truncate">{hdr.key}</span>
|
|
188
191
|
<span className="text-sm flex-1 truncate">{hdr.value}</span>
|
|
189
192
|
<Button onClick={() => editHeader(idx)}>
|
|
190
|
-
<
|
|
193
|
+
<CatalogIcon name="edit" className="w-4 h-4" />
|
|
191
194
|
</Button>
|
|
192
195
|
<Button onClick={() => removeHeader(idx)} className="text-red-400">
|
|
193
|
-
<
|
|
196
|
+
<CatalogIcon name="trash" className="w-4 h-4" />
|
|
194
197
|
</Button>
|
|
195
198
|
</>
|
|
196
199
|
)}
|
|
197
200
|
</div>
|
|
198
201
|
))}
|
|
199
202
|
<Button onClick={addEmptyHeaderRow} className="text-sm mt-2 w-fit">
|
|
200
|
-
<
|
|
203
|
+
<CatalogIcon name="plus" className="w-4 h-4" /> Add Header
|
|
201
204
|
</Button>
|
|
202
205
|
</div>
|
|
203
206
|
)
|
|
@@ -284,10 +287,10 @@ export const ParamsTab = ({ queryParams, setQueryParams }: ParamsTabProps) => {
|
|
|
284
287
|
<Input value={p.name} onChange={e => updateParam(i, 'name', (e.target as HTMLInputElement).value)} placeholder="Param name" className="w-32 text-sm" />
|
|
285
288
|
<Input value={p.value} onChange={e => updateParam(i, 'value', (e.target as HTMLInputElement).value)} placeholder="Param value" className="flex-1 text-sm" />
|
|
286
289
|
<Button onClick={() => saveParam(i)}>
|
|
287
|
-
<
|
|
290
|
+
<CatalogIcon name="check" className="w-4 h-4" />
|
|
288
291
|
</Button>
|
|
289
292
|
<Button onClick={() => cancelEdit(i)}>
|
|
290
|
-
<
|
|
293
|
+
<CatalogIcon name="close" className="w-4 h-4" />
|
|
291
294
|
</Button>
|
|
292
295
|
</>
|
|
293
296
|
) : (
|
|
@@ -295,23 +298,22 @@ export const ParamsTab = ({ queryParams, setQueryParams }: ParamsTabProps) => {
|
|
|
295
298
|
<span className="w-32 truncate text-sm">{p.name}</span>
|
|
296
299
|
<span className="flex-1 truncate text-sm">{p.value}</span>
|
|
297
300
|
<Button onClick={() => editParam(i)}>
|
|
298
|
-
<
|
|
301
|
+
<CatalogIcon name="edit" className="w-4 h-4" />
|
|
299
302
|
</Button>
|
|
300
303
|
<Button onClick={() => removeParam(i)} className="text-red-400">
|
|
301
|
-
<
|
|
304
|
+
<CatalogIcon name="trash" className="w-4 h-4" />
|
|
302
305
|
</Button>
|
|
303
306
|
</>
|
|
304
307
|
)}
|
|
305
308
|
</div>
|
|
306
309
|
))}
|
|
307
310
|
<Button onClick={addParamRow} className="mt-2 text-sm self-end">
|
|
308
|
-
<
|
|
311
|
+
<CatalogIcon name="plus" className="w-4 h-4" /> Add Parameter
|
|
309
312
|
</Button>
|
|
310
313
|
</div>
|
|
311
314
|
);
|
|
312
315
|
};
|
|
313
316
|
|
|
314
|
-
|
|
315
317
|
export function APIPlayground({
|
|
316
318
|
method,
|
|
317
319
|
url,
|
|
@@ -596,7 +598,7 @@ export function APIPlayground({
|
|
|
596
598
|
{/* Response */}
|
|
597
599
|
<div className="flex-1">
|
|
598
600
|
<div className="px-4 py-3">
|
|
599
|
-
<h3 className="text-sm font-semibold">Response</h3>
|
|
601
|
+
<h3 className="text-sm font-semibold" data-toc-ignore>Response</h3>
|
|
600
602
|
</div>
|
|
601
603
|
|
|
602
604
|
<div className="p-4">
|
|
@@ -659,4 +661,4 @@ export function APIPlayground({
|
|
|
659
661
|
</div>
|
|
660
662
|
</div>
|
|
661
663
|
)
|
|
662
|
-
}
|
|
664
|
+
}
|
package/src/mdx/async-api.tsx
CHANGED
|
@@ -131,7 +131,7 @@ export function AsyncAPI({
|
|
|
131
131
|
|
|
132
132
|
return (
|
|
133
133
|
<div className="dory-asyncapi max-w-none w-full">
|
|
134
|
-
{(operation.description || operation.summary) && <p className="dory-lead -mt-4 mb-6">{operation.description || operation.summary}</p>}
|
|
134
|
+
{(operation.description || operation.summary) && <p className="dory-lead -mt-4! mb-6">{operation.description || operation.summary}</p>}
|
|
135
135
|
|
|
136
136
|
<h2 id="connect" className="dory-h2 font-heading mb-3 mt-10"><span>Connect</span></h2>
|
|
137
137
|
<WebSocketPanel url={channel.address ?? selectedChannel} servers={wsServers} headers={defaultHeaders} />
|
|
@@ -150,7 +150,7 @@ export function AsyncAPI({
|
|
|
150
150
|
<tbody>
|
|
151
151
|
{rows.map((row) => (
|
|
152
152
|
<tr key={`${row.direction}-${row.name}`}>
|
|
153
|
-
<Td><span className="font-mono text-
|
|
153
|
+
<Td><span className="font-mono text-[13px] text-foreground">{row.name}</span></Td>
|
|
154
154
|
<Td>
|
|
155
155
|
<span className={row.direction === 'SEND' ? 'font-mono text-xs font-semibold text-[var(--brand-readable,#b65401)]' : 'font-mono text-xs font-semibold text-[var(--primary)]'}>
|
|
156
156
|
{row.direction}
|
package/src/mdx/card.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import classNames from "classnames";
|
|
|
2
2
|
import { withBase } from '../utils/base-path';
|
|
3
3
|
import { Icon } from "./icon";
|
|
4
4
|
import type { ComponentChildren } from "preact";
|
|
5
|
+
import { Card as UxCard } from '@clidey/ux';
|
|
5
6
|
|
|
6
7
|
export function CardGroup({ children, cols = 2, numbered = false }: { children: React.ReactNode; cols?: number; numbered?: boolean }) {
|
|
7
8
|
return (
|
|
@@ -31,7 +32,7 @@ export function Card({
|
|
|
31
32
|
children?: ComponentChildren
|
|
32
33
|
}) {
|
|
33
34
|
return (
|
|
34
|
-
<
|
|
35
|
+
<UxCard className="dory-card group relative gap-0 rounded-lg border-[var(--border)] bg-card p-4 shadow-none transition-shadow hover:shadow-[0_2px_10px_rgb(14_34_64/0.07)]">
|
|
35
36
|
{href && <a href={withBase(href)} className="absolute inset-0 z-10 rounded-lg" aria-label={title} />}
|
|
36
37
|
{(icon || href) && (
|
|
37
38
|
<div className="mb-3 flex items-start justify-between">
|
|
@@ -47,6 +48,6 @@ export function Card({
|
|
|
47
48
|
<div className="mt-1 text-[13px] leading-normal text-muted-foreground [&_p]:mb-0">
|
|
48
49
|
{description ?? children}
|
|
49
50
|
</div>
|
|
50
|
-
</
|
|
51
|
+
</UxCard>
|
|
51
52
|
)
|
|
52
53
|
}
|
package/src/mdx/code.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import {
|
|
5
5
|
Children,
|
|
@@ -8,12 +8,13 @@ import {
|
|
|
8
8
|
useEffect,
|
|
9
9
|
useState
|
|
10
10
|
} from 'react'
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
import { Tag } from './tag'
|
|
13
13
|
import { diffBaseLanguage, isTerminalSession, parseFenceMeta, type FenceMeta } from './fence-meta'
|
|
14
14
|
import { toTitleCase } from '../utils/functions'
|
|
15
|
-
import { Button } from '@clidey/ux'
|
|
16
|
-
|
|
15
|
+
import { CatalogIcon, Button } from '@clidey/ux'
|
|
16
|
+
|
|
17
|
+
import { captureDocsEvent } from '../utils/analytics'
|
|
17
18
|
|
|
18
19
|
export const supportedLanguages = {
|
|
19
20
|
'python': 'Python',
|
|
@@ -87,7 +88,7 @@ function languageName(language: string) {
|
|
|
87
88
|
return supportedLanguages[language as keyof typeof supportedLanguages] || toTitleCase(language)
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
function CopyButton({ code }: { code: string }) {
|
|
91
|
+
export function CopyButton({ code }: { code: string }) {
|
|
91
92
|
const [copyCount, setCopyCount] = useState(0)
|
|
92
93
|
const copied = copyCount > 0
|
|
93
94
|
|
|
@@ -111,6 +112,7 @@ function CopyButton({ code }: { code: string }) {
|
|
|
111
112
|
onClick={() => {
|
|
112
113
|
window.navigator.clipboard.writeText(code).then(() => {
|
|
113
114
|
setCopyCount((count) => count + 1)
|
|
115
|
+
captureDocsEvent('docs_code_copied', { path: window.location.pathname })
|
|
114
116
|
})
|
|
115
117
|
}}
|
|
116
118
|
>
|
|
@@ -121,7 +123,7 @@ function CopyButton({ code }: { code: string }) {
|
|
|
121
123
|
copied && '-translate-y-1.5 opacity-0',
|
|
122
124
|
)}
|
|
123
125
|
>
|
|
124
|
-
<
|
|
126
|
+
<CatalogIcon name="clipboard" className="h-3.5 w-3.5 stroke-muted-foreground transition-colors group-hover/button:stroke-foreground" />
|
|
125
127
|
</span>
|
|
126
128
|
<span
|
|
127
129
|
aria-hidden={!copied}
|
|
@@ -130,7 +132,7 @@ function CopyButton({ code }: { code: string }) {
|
|
|
130
132
|
!copied && 'translate-y-1.5 opacity-0',
|
|
131
133
|
)}
|
|
132
134
|
>
|
|
133
|
-
<
|
|
135
|
+
<CatalogIcon name="check" className="h-3.5 w-3.5" />
|
|
134
136
|
</span>
|
|
135
137
|
</Button>
|
|
136
138
|
)
|
|
@@ -161,8 +163,8 @@ export function FenceHeader({ language, code, meta }: FenceProps) {
|
|
|
161
163
|
return (
|
|
162
164
|
<div className="flex min-h-10 items-center gap-2 border-b border-[var(--border)] bg-muted pl-4 pr-12">
|
|
163
165
|
{terminal
|
|
164
|
-
? <
|
|
165
|
-
: meta.title && <
|
|
166
|
+
? <CatalogIcon name="terminal" aria-hidden="true" className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
|
167
|
+
: meta.title && <CatalogIcon name="file" aria-hidden="true" className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />}
|
|
166
168
|
<p className="mr-auto truncate font-mono text-xs font-medium text-foreground">{title}</p>
|
|
167
169
|
{diff && (
|
|
168
170
|
<span className="font-mono text-xs">
|
package/src/mdx/fence.tsx
CHANGED
|
@@ -8,13 +8,14 @@ import classNames from 'classnames'
|
|
|
8
8
|
|
|
9
9
|
// Colors resolve through CSS variables so one theme serves both light and dark mode.
|
|
10
10
|
const codeTheme: PrismTheme = {
|
|
11
|
-
plain: { color: 'var(--
|
|
11
|
+
plain: { color: 'var(--syntax-plain)', backgroundColor: 'transparent' },
|
|
12
12
|
styles: [
|
|
13
|
-
{ types: ['comment', 'prolog', 'doctype', 'cdata'], style: { color: 'var(--
|
|
14
|
-
{ types: ['keyword', 'builtin', 'function', 'tag', 'selector', '
|
|
15
|
-
{ types: ['
|
|
13
|
+
{ types: ['comment', 'prolog', 'doctype', 'cdata'], style: { color: 'var(--syntax-comment)', fontStyle: 'italic' } },
|
|
14
|
+
{ types: ['keyword', 'builtin', 'function', 'tag', 'selector', 'property', 'class-name'], style: { color: 'var(--syntax-keyword)' } },
|
|
15
|
+
{ types: ['number', 'boolean', 'constant'], style: { color: 'var(--syntax-number)' } },
|
|
16
|
+
{ types: ['string', 'char', 'attr-value', 'url', 'regex', 'inserted'], style: { color: 'var(--syntax-string)' } },
|
|
16
17
|
{ types: ['deleted'], style: { color: 'var(--destructive)' } },
|
|
17
|
-
{ types: ['punctuation', 'operator'], style: { color: 'var(--
|
|
18
|
+
{ types: ['punctuation', 'operator'], style: { color: 'var(--syntax-plain)' } },
|
|
18
19
|
],
|
|
19
20
|
}
|
|
20
21
|
|
package/src/mdx/icon-grid.tsx
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { useNotification } from '../components/notification'
|
|
2
2
|
import { Icon } from './icon'
|
|
3
|
+
import { icons as catalog } from '@clidey/ux/icons'
|
|
4
|
+
|
|
5
|
+
const iconNames = Object.keys(catalog).sort()
|
|
3
6
|
|
|
4
7
|
/** Grid of icon tiles. Clicking a tile copies its `<Icon icon="…" />` tag. */
|
|
5
|
-
export function IconGrid({ icons, cols = 4 }: { icons
|
|
8
|
+
export function IconGrid({ icons = iconNames, cols = 4 }: { icons?: string[]; cols?: number }) {
|
|
6
9
|
const { showNotification } = useNotification()
|
|
7
10
|
|
|
8
11
|
const copy = (name: string) => {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { resolveIconName } from './icon-names'
|
|
3
|
+
|
|
4
|
+
describe('MDX icon name compatibility', () => {
|
|
5
|
+
it('keeps source-prefixed variants distinct from the default icon', () => {
|
|
6
|
+
expect(resolveIconName('chevron-down')).toBe('chevron-down')
|
|
7
|
+
expect(resolveIconName('enterprise/chevron-down')).toBe('chevron-down-alt-2')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('preserves legacy document names', () => {
|
|
11
|
+
expect(resolveIconName('Search')).toBe('search')
|
|
12
|
+
expect(resolveIconName('alert-triangle')).toBe('warning')
|
|
13
|
+
expect(resolveIconName('sparkles')).toBe('ai')
|
|
14
|
+
})
|
|
15
|
+
})
|