@clidey/dory 0.21.4 → 0.23.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/bin/dory.ts +18 -4
- package/dist/api-playground-demo.mdx +119 -0
- package/dist/api-reference/asyncapi.json +324 -0
- package/dist/api-reference/openapi.json +366 -0
- package/dist/api-reference/operations/create.mdx +5 -0
- package/dist/api-reference/operations/delete.mdx +5 -0
- package/dist/api-reference/operations/get.mdx +5 -0
- package/dist/api-reference/operations/update.mdx +5 -0
- package/dist/api-reference/websocket-operations/receive-chat-message.mdx +5 -0
- package/dist/api-reference/websocket-operations/receive-notifications.mdx +5 -0
- package/dist/api-reference/websocket-operations/receive-presence-update.mdx +5 -0
- package/dist/api-reference/websocket-operations/send-chat-message.mdx +5 -0
- package/dist/api-reference/websocket-operations/send-presence-update.mdx +5 -0
- package/dist/dory.json +64 -0
- package/dist/favicon.svg +75 -0
- package/dist/features.mdx +440 -0
- package/dist/frontmatter.json +77 -0
- package/dist/index.html +55 -0
- package/dist/introduction.mdx +44 -0
- package/dist/llms.txt +561 -0
- package/dist/websocket-playground-demo.mdx +135 -0
- package/docs/dory.json +5 -0
- package/docs/favicon.svg +1 -1
- package/docs/introduction.mdx +2 -2
- package/package.json +13 -10
- package/src/app.css +2 -0
- package/src/components/dropdown.tsx +30 -26
- package/src/components/loading.tsx +6 -8
- package/src/components/navigation.tsx +8 -6
- package/src/components/notification.tsx +9 -93
- package/src/components/routes.tsx +1 -1
- package/src/components/search.tsx +31 -104
- package/src/components/store.ts +4 -1
- package/src/main.tsx +9 -5
- package/src/mdx/accordion.tsx +21 -28
- package/src/mdx/api-playground.tsx +68 -83
- package/src/mdx/api.tsx +21 -21
- package/src/mdx/async-api.tsx +15 -31
- package/src/mdx/callout.tsx +27 -43
- package/src/mdx/card.tsx +20 -14
- package/src/mdx/code.tsx +26 -26
- package/src/mdx/expandable.tsx +21 -25
- package/src/mdx/fence.tsx +2 -2
- package/src/mdx/list.tsx +2 -2
- package/src/mdx/open-api.tsx +31 -45
- package/src/mdx/table.tsx +8 -3
- package/src/mdx/tag.tsx +27 -40
- package/src/mdx/text.tsx +3 -2
- package/src/mdx/websocket-playground.tsx +150 -81
- package/src/theme.scss +50 -106
- package/src/types/config.ts +47 -0
- package/src/ui/header.tsx +12 -9
- package/src/ui/layout.tsx +5 -4
- package/src/ui/mobile-navigation.tsx +36 -63
- package/src/ui/table-of-content.tsx +3 -3
- package/src/utils/colors.ts +54 -0
- package/src/utils/fonts.ts +4 -1
- package/vite.config.ts +2 -0
- package/.claude/settings.json +0 -29
- package/.claude/settings.local.json +0 -12
- package/.github/ISSUE_TEMPLATE/anything-else.md +0 -10
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -39
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/claude.yml +0 -37
- package/docs/assets/dory.jpeg +0 -0
- package/k8s/Dockerfile +0 -14
- package/k8s/config/nginx-default.conf +0 -22
- package/k8s/dev.Dockerfile +0 -16
- package/k8s/docs.Dockerfile +0 -15
- package/public/demo.mov +0 -0
- package/src/components/dark-mode-toggle.tsx +0 -25
- package/src/index.css +0 -24
- package/src/utils/hooks.ts +0 -46
- /package/{assets → dist/logo}/logo.svg +0 -0
package/docs/introduction.mdx
CHANGED
|
@@ -10,7 +10,7 @@ description: 'Welcome to Docucod and Dory documentation'
|
|
|
10
10
|
This documentation is powered by two complementary tools:
|
|
11
11
|
|
|
12
12
|
1. **Docucod** - An AI-powered documentation assistant that automatically generates and maintains documentation
|
|
13
|
-
2. **Dory** - A lightweight static site generator specifically designed to deploy
|
|
13
|
+
2. **Dory** - A lightweight static site generator specifically designed to deploy dory-generated documentation
|
|
14
14
|
|
|
15
15
|
## What is Docucod?
|
|
16
16
|
|
|
@@ -22,7 +22,7 @@ Docucod analyzes your codebase and automatically:
|
|
|
22
22
|
|
|
23
23
|
## What is Dory?
|
|
24
24
|
|
|
25
|
-
Dory is a lightweight static site generator built for developers who want fast, clean, and customizable documentation. It was created specifically to deploy
|
|
25
|
+
Dory is a lightweight static site generator built for developers who want fast, clean, and customizable documentation. It was created specifically to deploy dory-generated documentation with:
|
|
26
26
|
|
|
27
27
|
- 🧠 **Simple** — Drop in `.mdx` files and configure one `dory.json`
|
|
28
28
|
- ⚡ **Fast** — Instant hot reload in dev, quick static builds for prod
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clidey/dory",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.23.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A CLI tool for building and previewing documentation sites",
|
|
7
7
|
"keywords": [
|
|
@@ -24,20 +24,27 @@
|
|
|
24
24
|
"bugs": {
|
|
25
25
|
"url": "https://github.com/clidey/dory/issues"
|
|
26
26
|
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"dev": "vite",
|
|
29
|
+
"build": "tsc -b && vite build",
|
|
30
|
+
"preview": "vite preview",
|
|
31
|
+
"publish": "npm publish --access public"
|
|
32
|
+
},
|
|
27
33
|
"dependencies": {
|
|
28
34
|
"@algolia/autocomplete-core": "^1.19.2",
|
|
35
|
+
"@clidey/ux": "^0.36.0",
|
|
29
36
|
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
|
30
37
|
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
|
31
38
|
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
|
32
39
|
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
|
33
40
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
|
34
|
-
"@headlessui/react": "^2.2.
|
|
41
|
+
"@headlessui/react": "^2.2.9",
|
|
35
42
|
"@heroicons/react": "^2.2.0",
|
|
36
43
|
"@mdx-js/mdx": "^3.1.0",
|
|
37
44
|
"@mdx-js/preact": "^3.1.0",
|
|
38
45
|
"@mdx-js/rollup": "^3.1.0",
|
|
39
46
|
"@preact/preset-vite": "^2.10.1",
|
|
40
|
-
"@tailwindcss/vite": "^4.1.
|
|
47
|
+
"@tailwindcss/vite": "^4.1.16",
|
|
41
48
|
"@types/react-highlight-words": "^0.20.0",
|
|
42
49
|
"classnames": "^2.5.1",
|
|
43
50
|
"flexsearch": "^0.8.205",
|
|
@@ -54,7 +61,7 @@
|
|
|
54
61
|
"saas": "^1.0.0",
|
|
55
62
|
"sass-embedded": "^1.89.2",
|
|
56
63
|
"sirv": "^3.0.1",
|
|
57
|
-
"tailwindcss": "^4.1.
|
|
64
|
+
"tailwindcss": "^4.1.16",
|
|
58
65
|
"tsx": "^4.20.4",
|
|
59
66
|
"typescript": "~5.8.3",
|
|
60
67
|
"unist-util-visit": "^5.0.0",
|
|
@@ -62,12 +69,8 @@
|
|
|
62
69
|
"vite-plugin-mdx": "^3.6.1",
|
|
63
70
|
"wouter-preact": "^3.7.1"
|
|
64
71
|
},
|
|
72
|
+
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
|
|
65
73
|
"devDependencies": {
|
|
66
74
|
"@types/node": "^24.0.12"
|
|
67
|
-
},
|
|
68
|
-
"scripts": {
|
|
69
|
-
"dev": "vite",
|
|
70
|
-
"build": "tsc -b && vite build",
|
|
71
|
-
"preview": "vite preview"
|
|
72
75
|
}
|
|
73
|
-
}
|
|
76
|
+
}
|
package/src/app.css
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
DropdownMenu,
|
|
3
|
+
DropdownMenuTrigger,
|
|
4
|
+
DropdownMenuContent,
|
|
5
|
+
DropdownMenuItem,
|
|
6
|
+
DropdownMenuSeparator,
|
|
7
|
+
Button
|
|
8
|
+
} from '@clidey/ux'
|
|
2
9
|
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
|
3
10
|
import type { ComponentChildren } from 'preact'
|
|
11
|
+
import type { ReactNode } from 'preact/compat'
|
|
4
12
|
|
|
5
13
|
interface DropdownItem {
|
|
6
14
|
icon?: ComponentChildren
|
|
@@ -11,39 +19,35 @@ interface DropdownItem {
|
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
interface DropdownProps {
|
|
14
|
-
buttonLabel: string
|
|
22
|
+
buttonLabel: string | ReactNode;
|
|
15
23
|
items: DropdownItem[]
|
|
16
24
|
className?: string
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
export default function Dropdown({ buttonLabel, items, className = '' }: DropdownProps) {
|
|
20
28
|
return (
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
<
|
|
29
|
+
<DropdownMenu>
|
|
30
|
+
<DropdownMenuTrigger>
|
|
31
|
+
<Button className={className}>
|
|
24
32
|
{buttonLabel}
|
|
25
|
-
<ChevronDownIcon
|
|
26
|
-
</
|
|
27
|
-
</
|
|
28
|
-
|
|
29
|
-
<MenuItems
|
|
30
|
-
transition
|
|
31
|
-
className="absolute left-0 right-0 z-10 mt-2 w-full origin-top-right divide-y divide-[#444444] rounded-md bg-[#1e1e1e] shadow-lg ring-1 ring-[#444444]/50 transition focus:outline-hidden data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in">
|
|
33
|
+
<ChevronDownIcon className="ml-2 h-4 w-4" />
|
|
34
|
+
</Button>
|
|
35
|
+
</DropdownMenuTrigger>
|
|
36
|
+
<DropdownMenuContent>
|
|
32
37
|
{items.map((item, index) => (
|
|
33
|
-
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
</div>
|
|
38
|
+
<>
|
|
39
|
+
{item.divider && index > 0 && <DropdownMenuSeparator />}
|
|
40
|
+
<DropdownMenuItem
|
|
41
|
+
key={index}
|
|
42
|
+
onClick={item.onClick}
|
|
43
|
+
className="cursor-pointer"
|
|
44
|
+
>
|
|
45
|
+
{item.icon && <span className="mr-2">{item.icon}</span>}
|
|
46
|
+
{item.label}
|
|
47
|
+
</DropdownMenuItem>
|
|
48
|
+
</>
|
|
45
49
|
))}
|
|
46
|
-
</
|
|
47
|
-
</
|
|
50
|
+
</DropdownMenuContent>
|
|
51
|
+
</DropdownMenu>
|
|
48
52
|
)
|
|
49
53
|
}
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
+
import { Spinner } from '@clidey/ux';
|
|
1
2
|
import classNames from "classnames";
|
|
2
3
|
|
|
3
4
|
type ILoadingProps = {
|
|
4
5
|
className?: string;
|
|
5
|
-
|
|
6
|
+
showText?: boolean;
|
|
6
7
|
loadingText?: string;
|
|
7
8
|
textClassName?: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export const Loading = ({ className,
|
|
11
|
+
export const Loading = ({ className, showText = false, loadingText, textClassName }: ILoadingProps) => {
|
|
11
12
|
return (
|
|
12
|
-
<div className="h-full w-full flex flex-col justify-center items-center gap-2" role="status">
|
|
13
|
-
<
|
|
14
|
-
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
|
|
15
|
-
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
|
|
16
|
-
</svg>
|
|
13
|
+
<div className="h-full w-full flex flex-col justify-center items-center gap-2 min-h-[400px]" role="status">
|
|
14
|
+
<Spinner className={className} />
|
|
17
15
|
{
|
|
18
|
-
|
|
16
|
+
showText &&
|
|
19
17
|
<div className={classNames("text-sm text-gray-500", textClassName)}>{ loadingText ?? "Loading..."}</div>
|
|
20
18
|
}
|
|
21
19
|
</div>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { useMemo } from 'preact/hooks';
|
|
3
|
+
import { Link } from 'wouter-preact';
|
|
3
4
|
import { usePathname } from './hooks';
|
|
4
5
|
import { ALL_NAVIGATION } from './store';
|
|
5
|
-
import { Link } from 'wouter-preact';
|
|
6
6
|
|
|
7
7
|
export function Navigation() {
|
|
8
8
|
const pathname = usePathname();
|
|
@@ -20,23 +20,25 @@ export function Navigation() {
|
|
|
20
20
|
}, [effectivePath]);
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
|
-
<nav className="
|
|
23
|
+
<nav className="dory-navigation text-base lg:text-sm">
|
|
24
24
|
<ul role="list" className="space-y-9">
|
|
25
25
|
{currentTab && (
|
|
26
26
|
<li key={currentTab.title}>
|
|
27
|
-
<h2 className="font-display font-medium text-
|
|
27
|
+
<h2 className="font-display font-medium text-xs">{currentTab.title}</h2>
|
|
28
28
|
{currentTab.groups.map(group => (
|
|
29
29
|
<div key={group.title}>
|
|
30
|
-
<h3 className="mt-4 text-sm font-semibold
|
|
30
|
+
<h3 className="mt-4 text-sm font-semibold">{group.title}</h3>
|
|
31
31
|
<ul role="list" className="mt-2 space-y-2 border-l-2 border-slate-100 lg:mt-4 lg:space-y-4 lg:border-slate-200 dark:border-slate-800">
|
|
32
32
|
{group.pages.map((page) => {
|
|
33
33
|
const isActive = effectivePath === page.href;
|
|
34
34
|
return (
|
|
35
35
|
<li key={page.href} className="relative">
|
|
36
|
-
<Link
|
|
36
|
+
<Link
|
|
37
|
+
href={page.href}
|
|
38
|
+
className={classNames(
|
|
37
39
|
'block w-full pl-3.5 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',
|
|
38
40
|
{
|
|
39
|
-
'font-semibold text-
|
|
41
|
+
'font-semibold text-brand-foreground': isActive,
|
|
40
42
|
'text-slate-500 before:hidden before:bg-slate-300 hover:text-slate-600 hover:before:block dark:text-slate-400 dark:before:bg-slate-700 dark:hover:text-slate-300': !isActive
|
|
41
43
|
},
|
|
42
44
|
)}>
|
|
@@ -1,97 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { createContext, type ComponentChildren } from 'preact';
|
|
4
|
-
import { useContext, useState } from 'preact/hooks';
|
|
5
|
-
|
|
6
|
-
interface Notification {
|
|
7
|
-
id: number;
|
|
8
|
-
message: string;
|
|
9
|
-
description?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface NotificationContextType {
|
|
13
|
-
showNotification: (message: string, description?: string) => void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const NotificationContext = createContext<NotificationContextType | null>(null);
|
|
1
|
+
import { toast } from '@clidey/ux';
|
|
2
|
+
import { InfoIcon } from 'lucide-react';
|
|
17
3
|
|
|
18
4
|
export function useNotification() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface NotificationProviderProps {
|
|
27
|
-
children: ComponentChildren;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function NotificationProvider({ children }: NotificationProviderProps) {
|
|
31
|
-
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
32
|
-
|
|
33
|
-
const showNotification = (message: string, description?: string) => {
|
|
34
|
-
const id = Date.now();
|
|
35
|
-
setNotifications(prev => [...prev, { id, message, description }]);
|
|
36
|
-
|
|
37
|
-
// Auto-dismiss after 5 seconds
|
|
38
|
-
setTimeout(() => {
|
|
39
|
-
setNotifications(prev => prev.filter(notif => notif.id !== id));
|
|
40
|
-
}, 5000);
|
|
5
|
+
return {
|
|
6
|
+
showNotification: (message: string, description?: string) => {
|
|
7
|
+
toast(message, {
|
|
8
|
+
icon: <InfoIcon className="h-4 w-4" />,
|
|
9
|
+
description: description,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
41
12
|
};
|
|
42
|
-
|
|
43
|
-
// const dismissNotification = (id: number) => {
|
|
44
|
-
// setNotifications(prev => prev.filter(notif => notif.id !== id));
|
|
45
|
-
// };
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<NotificationContext.Provider value={{ showNotification }}>
|
|
49
|
-
<div
|
|
50
|
-
aria-live="assertive"
|
|
51
|
-
className="pointer-events-none fixed inset-0 flex items-start justify-end px-8 py-6 sm:p-6 z-[100]"
|
|
52
|
-
>
|
|
53
|
-
<div className="flex w-full flex-col items-end space-y-4">
|
|
54
|
-
{notifications.map((notif) => (
|
|
55
|
-
<Transition
|
|
56
|
-
key={notif.id}
|
|
57
|
-
show={true}
|
|
58
|
-
enter="transform ease-out duration-300 transition"
|
|
59
|
-
enterFrom="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
|
|
60
|
-
enterTo="translate-y-0 opacity-100 sm:translate-x-0"
|
|
61
|
-
leave="transition ease-in duration-100"
|
|
62
|
-
leaveFrom="opacity-100"
|
|
63
|
-
leaveTo="opacity-0"
|
|
64
|
-
>
|
|
65
|
-
<div className="pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-[#222222] shadow-lg ring-1 ring-[#444444]/5">
|
|
66
|
-
<div className="p-4">
|
|
67
|
-
<div className="flex items-start">
|
|
68
|
-
<div className="shrink-0">
|
|
69
|
-
<CheckCircleIcon aria-hidden="true" className="size-6 text-green-400" />
|
|
70
|
-
</div>
|
|
71
|
-
<div className="ml-3 w-0 flex-1 pt-0.5">
|
|
72
|
-
<p className="text-sm font-medium text-[#ffffff]">{notif.message}</p>
|
|
73
|
-
{notif.description && (
|
|
74
|
-
<p className="mt-1 text-sm text-[#aaaaaa]">{notif.description}</p>
|
|
75
|
-
)}
|
|
76
|
-
</div>
|
|
77
|
-
{/* <div className="ml-4 flex shrink-0">
|
|
78
|
-
<button
|
|
79
|
-
type="button"
|
|
80
|
-
onClick={() => dismissNotification(notif.id)}
|
|
81
|
-
className="inline-flex rounded-md bg-transparent text-[#aaaaaa] hover:text-[#ffffff] focus:ring-2 focus:ring-[#444444] focus:ring-offset-2 focus:outline-hidden"
|
|
82
|
-
>
|
|
83
|
-
<span className="sr-only">Close</span>
|
|
84
|
-
<XMarkIcon aria-hidden="true" className="size-5" />
|
|
85
|
-
</button>
|
|
86
|
-
</div> */}
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
</Transition>
|
|
91
|
-
))}
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
{children}
|
|
95
|
-
</NotificationContext.Provider>
|
|
96
|
-
);
|
|
97
13
|
}
|
|
@@ -12,7 +12,7 @@ export default function Routes() {
|
|
|
12
12
|
const [loading, setLoading] = useState(true);
|
|
13
13
|
|
|
14
14
|
useEffect(() => {
|
|
15
|
-
const links = document.querySelectorAll('.
|
|
15
|
+
const links = document.querySelectorAll('.dory-navigation a');
|
|
16
16
|
const listeners: (() => void)[] = [];
|
|
17
17
|
const preloaded = new Set();
|
|
18
18
|
|
|
@@ -17,13 +17,13 @@ import {
|
|
|
17
17
|
type JSX,
|
|
18
18
|
} from 'react'
|
|
19
19
|
import Highlighter from 'react-highlight-words'
|
|
20
|
-
|
|
21
|
-
import { useMobileNavigation } from '../ui/mobile-navigation'
|
|
22
|
-
import { useLocation } from 'wouter-preact'
|
|
20
|
+
import { cn, Spinner, SearchInput as UxSearchInput } from '@clidey/ux'
|
|
23
21
|
import classNames from 'classnames'
|
|
22
|
+
import { useLocation } from 'wouter-preact'
|
|
23
|
+
import { useMobileNavigation } from '../ui/mobile-navigation'
|
|
24
24
|
import { usePathname, useSearchParams } from './hooks'
|
|
25
|
-
import { ALL_NAVIGATION } from './store'
|
|
26
25
|
import { search } from './search-index'
|
|
26
|
+
import { ALL_NAVIGATION } from './store'
|
|
27
27
|
|
|
28
28
|
type EmptyObject = Record<string, never>
|
|
29
29
|
type Result = any;
|
|
@@ -89,58 +89,6 @@ function useAutocomplete({ onNavigate }: { onNavigate: () => void }) {
|
|
|
89
89
|
return { autocomplete, autocompleteState }
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
function SearchIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
|
93
|
-
return (
|
|
94
|
-
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" {...props}>
|
|
95
|
-
<path
|
|
96
|
-
strokeLinecap="round"
|
|
97
|
-
strokeLinejoin="round"
|
|
98
|
-
d="M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25"
|
|
99
|
-
/>
|
|
100
|
-
</svg>
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function NoResultsIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
|
105
|
-
return (
|
|
106
|
-
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" {...props}>
|
|
107
|
-
<path
|
|
108
|
-
strokeLinecap="round"
|
|
109
|
-
strokeLinejoin="round"
|
|
110
|
-
d="M12.01 12a4.237 4.237 0 0 0 1.24-3c0-.62-.132-1.207-.37-1.738M12.01 12A4.237 4.237 0 0 1 9 13.25c-.635 0-1.237-.14-1.777-.388M12.01 12l3.24 3.25m-3.715-9.661a4.25 4.25 0 0 0-5.975 5.908M4.5 15.5l11-11"
|
|
111
|
-
/>
|
|
112
|
-
</svg>
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function LoadingIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
|
117
|
-
const id = useId()
|
|
118
|
-
|
|
119
|
-
return (
|
|
120
|
-
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" {...props}>
|
|
121
|
-
<circle cx="10" cy="10" r="5.5" strokeLinejoin="round" />
|
|
122
|
-
<path
|
|
123
|
-
stroke={`url(#${id})`}
|
|
124
|
-
strokeLinecap="round"
|
|
125
|
-
strokeLinejoin="round"
|
|
126
|
-
d="M15.5 10a5.5 5.5 0 1 0-5.5 5.5"
|
|
127
|
-
/>
|
|
128
|
-
<defs>
|
|
129
|
-
<linearGradient
|
|
130
|
-
id={id}
|
|
131
|
-
x1="13"
|
|
132
|
-
x2="9.5"
|
|
133
|
-
y1="9"
|
|
134
|
-
y2="15"
|
|
135
|
-
gradientUnits="userSpaceOnUse"
|
|
136
|
-
>
|
|
137
|
-
<stop stopColor="currentColor" />
|
|
138
|
-
<stop offset="1" stopColor="currentColor" stopOpacity="0" />
|
|
139
|
-
</linearGradient>
|
|
140
|
-
</defs>
|
|
141
|
-
</svg>
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
92
|
|
|
145
93
|
function HighlightQuery({ text, query }: { text: string; query: string }) {
|
|
146
94
|
return (
|
|
@@ -155,13 +103,11 @@ function HighlightQuery({ text, query }: { text: string; query: string }) {
|
|
|
155
103
|
|
|
156
104
|
function SearchResult({
|
|
157
105
|
result,
|
|
158
|
-
resultIndex,
|
|
159
106
|
autocomplete,
|
|
160
107
|
collection,
|
|
161
108
|
query,
|
|
162
109
|
}: {
|
|
163
110
|
result: Result
|
|
164
|
-
resultIndex: number
|
|
165
111
|
autocomplete: Autocomplete
|
|
166
112
|
collection: AutocompleteCollection<Result>
|
|
167
113
|
query: string
|
|
@@ -190,9 +136,6 @@ function SearchResult({
|
|
|
190
136
|
<li
|
|
191
137
|
className={classNames(
|
|
192
138
|
'group block cursor-default px-4 py-3 aria-selected:bg-zinc-50 dark:aria-selected:bg-zinc-800/50',
|
|
193
|
-
{
|
|
194
|
-
'border-t border-zinc-100 dark:border-zinc-800': resultIndex > 0,
|
|
195
|
-
}
|
|
196
139
|
)}
|
|
197
140
|
aria-labelledby={`${id}-hierarchy ${id}-title`}
|
|
198
141
|
{...autocomplete.getItemProps({
|
|
@@ -245,7 +188,13 @@ function SearchResults({
|
|
|
245
188
|
if (collection.items.length === 0) {
|
|
246
189
|
return (
|
|
247
190
|
<div className="p-6 text-center">
|
|
248
|
-
<
|
|
191
|
+
<svg className="mx-auto h-5 w-5 stroke-zinc-900 dark:stroke-zinc-600" viewBox="0 0 20 20" fill="none" aria-hidden="true">
|
|
192
|
+
<path
|
|
193
|
+
strokeLinecap="round"
|
|
194
|
+
strokeLinejoin="round"
|
|
195
|
+
d="M12.01 12a4.237 4.237 0 0 0 1.24-3c0-.62-.132-1.207-.37-1.738M12.01 12A4.237 4.237 0 0 1 9 13.25c-.635 0-1.237-.14-1.777-.388M12.01 12l3.24 3.25m-3.715-9.661a4.25 4.25 0 0 0-5.975 5.908M4.5 15.5l11-11"
|
|
196
|
+
/>
|
|
197
|
+
</svg>
|
|
249
198
|
<p className="mt-2 text-xs text-zinc-700 dark:text-zinc-400">
|
|
250
199
|
Nothing found for{' '}
|
|
251
200
|
<strong className="font-semibold break-words text-zinc-900 dark:text-white">
|
|
@@ -259,11 +208,10 @@ function SearchResults({
|
|
|
259
208
|
|
|
260
209
|
return (
|
|
261
210
|
<ul {...autocomplete.getListProps()}>
|
|
262
|
-
{collection.items.map((result
|
|
211
|
+
{collection.items.map((result) => (
|
|
263
212
|
<SearchResult
|
|
264
213
|
key={result.url}
|
|
265
214
|
result={result}
|
|
266
|
-
resultIndex={resultIndex}
|
|
267
215
|
autocomplete={autocomplete}
|
|
268
216
|
collection={collection}
|
|
269
217
|
query={query}
|
|
@@ -284,18 +232,14 @@ const SearchInput = forwardRef<
|
|
|
284
232
|
const inputProps = autocomplete.getInputProps({ inputElement: null })
|
|
285
233
|
|
|
286
234
|
return (
|
|
287
|
-
<div className="group relative flex
|
|
288
|
-
<
|
|
289
|
-
<input
|
|
235
|
+
<div className="group relative flex">
|
|
236
|
+
<UxSearchInput
|
|
290
237
|
ref={inputRef}
|
|
291
238
|
data-autofocus
|
|
292
|
-
className={
|
|
293
|
-
'flex-auto appearance-none bg-transparent pl-10 text-zinc-900 outline-hidden placeholder:text-zinc-500 focus:w-full focus:flex-none sm:text-sm dark:text-white [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden [&::-webkit-search-results-button]:hidden [&::-webkit-search-results-decoration]:hidden',
|
|
294
|
-
{
|
|
239
|
+
className={cn({
|
|
295
240
|
'pr-11': autocompleteState.status === 'stalled',
|
|
296
241
|
'pr-4': autocompleteState.status !== 'stalled',
|
|
297
|
-
|
|
298
|
-
)}
|
|
242
|
+
})}
|
|
299
243
|
{...inputProps}
|
|
300
244
|
onKeyDown={(event) => {
|
|
301
245
|
if (
|
|
@@ -317,7 +261,7 @@ const SearchInput = forwardRef<
|
|
|
317
261
|
/>
|
|
318
262
|
{autocompleteState.status === 'stalled' && (
|
|
319
263
|
<div className="absolute inset-y-0 right-3 flex items-center">
|
|
320
|
-
<
|
|
264
|
+
<Spinner className="h-5 w-5 animate-spin stroke-zinc-200 text-zinc-900 dark:stroke-zinc-800 dark:text-emerald-400" />
|
|
321
265
|
</div>
|
|
322
266
|
)}
|
|
323
267
|
</div>
|
|
@@ -424,12 +368,12 @@ function SearchDialog({
|
|
|
424
368
|
}
|
|
425
369
|
|
|
426
370
|
function useSearchProps() {
|
|
427
|
-
const
|
|
371
|
+
const inputRef = useRef<React.ElementRef<typeof SearchInput>>(null)
|
|
428
372
|
const [open, setOpen] = useState(false)
|
|
429
373
|
|
|
430
374
|
return {
|
|
431
|
-
|
|
432
|
-
ref:
|
|
375
|
+
inputProps: {
|
|
376
|
+
ref: inputRef,
|
|
433
377
|
onClick() {
|
|
434
378
|
setOpen(true)
|
|
435
379
|
},
|
|
@@ -439,7 +383,7 @@ function useSearchProps() {
|
|
|
439
383
|
setOpen: useCallback(
|
|
440
384
|
(open: boolean) => {
|
|
441
385
|
const { width = 0, height = 0 } =
|
|
442
|
-
|
|
386
|
+
inputRef.current?.getBoundingClientRect?.() ?? {}
|
|
443
387
|
if (!open || (width !== 0 && height !== 0)) {
|
|
444
388
|
setOpen(open)
|
|
445
389
|
}
|
|
@@ -451,29 +395,15 @@ function useSearchProps() {
|
|
|
451
395
|
}
|
|
452
396
|
|
|
453
397
|
export function Search() {
|
|
454
|
-
const
|
|
455
|
-
const { buttonProps, dialogProps } = useSearchProps()
|
|
456
|
-
|
|
457
|
-
useEffect(() => {
|
|
458
|
-
setModifierKey(
|
|
459
|
-
/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? '⌘' : 'Ctrl ',
|
|
460
|
-
)
|
|
461
|
-
}, [])
|
|
398
|
+
const { inputProps: buttonProps, dialogProps } = useSearchProps()
|
|
462
399
|
|
|
463
400
|
return (
|
|
464
401
|
<div className="hidden lg:block lg:max-w-md lg:flex-auto">
|
|
465
|
-
<
|
|
466
|
-
|
|
467
|
-
|
|
402
|
+
<UxSearchInput
|
|
403
|
+
aria-label="Find something..."
|
|
404
|
+
placeholder="Find something..."
|
|
468
405
|
{...buttonProps}
|
|
469
|
-
|
|
470
|
-
<SearchIcon className="h-5 w-5 stroke-current" />
|
|
471
|
-
Can I help you find something?
|
|
472
|
-
<kbd className="ml-auto text-2xs text-zinc-400 dark:text-zinc-500">
|
|
473
|
-
<kbd className="font-sans">{modifierKey}</kbd>
|
|
474
|
-
<kbd className="font-sans">K</kbd>
|
|
475
|
-
</kbd>
|
|
476
|
-
</button>
|
|
406
|
+
/>
|
|
477
407
|
<Suspense fallback={null}>
|
|
478
408
|
<SearchDialog className="hidden lg:block" {...dialogProps} />
|
|
479
409
|
</Suspense>
|
|
@@ -483,19 +413,16 @@ export function Search() {
|
|
|
483
413
|
|
|
484
414
|
export function MobileSearch() {
|
|
485
415
|
const { close } = useMobileNavigation()
|
|
486
|
-
const { buttonProps, dialogProps } = useSearchProps()
|
|
416
|
+
const { inputProps: buttonProps, dialogProps } = useSearchProps()
|
|
487
417
|
|
|
488
418
|
return (
|
|
489
419
|
<div className="contents lg:hidden">
|
|
490
|
-
<
|
|
491
|
-
|
|
492
|
-
className="relative flex size-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 lg:hidden dark:hover:bg-white/5"
|
|
420
|
+
<UxSearchInput
|
|
421
|
+
className="text-sm"
|
|
493
422
|
aria-label="Find something..."
|
|
494
423
|
{...buttonProps}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
<SearchIcon className="h-8 w-8 stroke-zinc-900 dark:stroke-white" />
|
|
498
|
-
</button>
|
|
424
|
+
placeholder="Find something..."
|
|
425
|
+
/>
|
|
499
426
|
<Suspense fallback={null}>
|
|
500
427
|
<SearchDialog
|
|
501
428
|
className="lg:hidden"
|
package/src/components/store.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ComponentType } from 'preact';
|
|
2
2
|
import { type RouteComponentProps } from 'wouter-preact';
|
|
3
3
|
import docsConfig from '../../docs/dory.json';
|
|
4
|
+
import type { DoryConfig } from '../types/config';
|
|
4
5
|
import { searchIndex } from './search-index';
|
|
5
6
|
|
|
7
|
+
const config = docsConfig as DoryConfig;
|
|
8
|
+
|
|
6
9
|
export const ALL_PAGES = Object.fromEntries(
|
|
7
10
|
Object.entries(import.meta.glob<{ default: ComponentType; }>('../../docs/**/*.mdx'))
|
|
8
11
|
.map(([path, loader]) => [pathFromFilename(path), loader])
|
|
@@ -34,7 +37,7 @@ export interface Navigation {
|
|
|
34
37
|
}>;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
export const ALL_NAVIGATION: Navigation[] =
|
|
40
|
+
export const ALL_NAVIGATION: Navigation[] = config.navigation.tabs.map(tab => ({
|
|
38
41
|
title: tab.tab,
|
|
39
42
|
groups: tab.groups.map(group => ({
|
|
40
43
|
title: group.group,
|
package/src/main.tsx
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { render } from 'preact'
|
|
2
|
-
import '
|
|
2
|
+
import '@clidey/ux/styles.css';
|
|
3
3
|
import { App } from './app.tsx'
|
|
4
4
|
import { withMDX } from './components/mdx-provider.tsx'
|
|
5
5
|
import { loadFonts } from './utils/fonts'
|
|
6
|
-
import {
|
|
6
|
+
import { loadColors } from './utils/colors'
|
|
7
|
+
import { Toaster } from '@clidey/ux'
|
|
8
|
+
import { ThemeProvider } from '@clidey/ux'
|
|
7
9
|
|
|
8
|
-
// Load fonts before rendering the app
|
|
10
|
+
// Load fonts and colors before rendering the app
|
|
9
11
|
loadFonts()
|
|
12
|
+
loadColors()
|
|
10
13
|
|
|
11
14
|
const AppWithMDX = withMDX(App)
|
|
12
15
|
|
|
13
16
|
render(
|
|
14
|
-
<
|
|
17
|
+
<ThemeProvider storageKey="@clidey/dory/theme">
|
|
18
|
+
<Toaster />
|
|
15
19
|
<AppWithMDX />
|
|
16
|
-
</
|
|
20
|
+
</ThemeProvider>,
|
|
17
21
|
document.getElementById('app')!
|
|
18
22
|
)
|