@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.
Files changed (75) hide show
  1. package/bin/dory.ts +18 -4
  2. package/dist/api-playground-demo.mdx +119 -0
  3. package/dist/api-reference/asyncapi.json +324 -0
  4. package/dist/api-reference/openapi.json +366 -0
  5. package/dist/api-reference/operations/create.mdx +5 -0
  6. package/dist/api-reference/operations/delete.mdx +5 -0
  7. package/dist/api-reference/operations/get.mdx +5 -0
  8. package/dist/api-reference/operations/update.mdx +5 -0
  9. package/dist/api-reference/websocket-operations/receive-chat-message.mdx +5 -0
  10. package/dist/api-reference/websocket-operations/receive-notifications.mdx +5 -0
  11. package/dist/api-reference/websocket-operations/receive-presence-update.mdx +5 -0
  12. package/dist/api-reference/websocket-operations/send-chat-message.mdx +5 -0
  13. package/dist/api-reference/websocket-operations/send-presence-update.mdx +5 -0
  14. package/dist/dory.json +64 -0
  15. package/dist/favicon.svg +75 -0
  16. package/dist/features.mdx +440 -0
  17. package/dist/frontmatter.json +77 -0
  18. package/dist/index.html +55 -0
  19. package/dist/introduction.mdx +44 -0
  20. package/dist/llms.txt +561 -0
  21. package/dist/websocket-playground-demo.mdx +135 -0
  22. package/docs/dory.json +5 -0
  23. package/docs/favicon.svg +1 -1
  24. package/docs/introduction.mdx +2 -2
  25. package/package.json +13 -10
  26. package/src/app.css +2 -0
  27. package/src/components/dropdown.tsx +30 -26
  28. package/src/components/loading.tsx +6 -8
  29. package/src/components/navigation.tsx +8 -6
  30. package/src/components/notification.tsx +9 -93
  31. package/src/components/routes.tsx +1 -1
  32. package/src/components/search.tsx +31 -104
  33. package/src/components/store.ts +4 -1
  34. package/src/main.tsx +9 -5
  35. package/src/mdx/accordion.tsx +21 -28
  36. package/src/mdx/api-playground.tsx +68 -83
  37. package/src/mdx/api.tsx +21 -21
  38. package/src/mdx/async-api.tsx +15 -31
  39. package/src/mdx/callout.tsx +27 -43
  40. package/src/mdx/card.tsx +20 -14
  41. package/src/mdx/code.tsx +26 -26
  42. package/src/mdx/expandable.tsx +21 -25
  43. package/src/mdx/fence.tsx +2 -2
  44. package/src/mdx/list.tsx +2 -2
  45. package/src/mdx/open-api.tsx +31 -45
  46. package/src/mdx/table.tsx +8 -3
  47. package/src/mdx/tag.tsx +27 -40
  48. package/src/mdx/text.tsx +3 -2
  49. package/src/mdx/websocket-playground.tsx +150 -81
  50. package/src/theme.scss +50 -106
  51. package/src/types/config.ts +47 -0
  52. package/src/ui/header.tsx +12 -9
  53. package/src/ui/layout.tsx +5 -4
  54. package/src/ui/mobile-navigation.tsx +36 -63
  55. package/src/ui/table-of-content.tsx +3 -3
  56. package/src/utils/colors.ts +54 -0
  57. package/src/utils/fonts.ts +4 -1
  58. package/vite.config.ts +2 -0
  59. package/.claude/settings.json +0 -29
  60. package/.claude/settings.local.json +0 -12
  61. package/.github/ISSUE_TEMPLATE/anything-else.md +0 -10
  62. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -39
  63. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  64. package/.github/dependabot.yml +0 -11
  65. package/.github/workflows/claude.yml +0 -37
  66. package/docs/assets/dory.jpeg +0 -0
  67. package/k8s/Dockerfile +0 -14
  68. package/k8s/config/nginx-default.conf +0 -22
  69. package/k8s/dev.Dockerfile +0 -16
  70. package/k8s/docs.Dockerfile +0 -15
  71. package/public/demo.mov +0 -0
  72. package/src/components/dark-mode-toggle.tsx +0 -25
  73. package/src/index.css +0 -24
  74. package/src/utils/hooks.ts +0 -46
  75. /package/{assets → dist/logo}/logo.svg +0 -0
@@ -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 Docucod-generated documentation
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 Docucod-generated documentation with:
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.21.4",
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.4",
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.8",
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.8",
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,3 +1,5 @@
1
1
  @import "tailwindcss";
2
+
2
3
  @custom-variant dark (&:where(.dark, .dark *));
3
4
 
5
+
@@ -1,6 +1,14 @@
1
- import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
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
- <Menu as="div" className={`relative inline-block text-left ${className}`}>
22
- <div>
23
- <MenuButton className="inline-flex w-full justify-center gap-x-1.5 rounded-md bg-[#1e1e1e] px-3 py-2 text-sm font-semibold text-[#ffffff] shadow-xs ring-1 ring-[#444444] ring-inset hover:bg-[#2a2a2a]">
29
+ <DropdownMenu>
30
+ <DropdownMenuTrigger>
31
+ <Button className={className}>
24
32
  {buttonLabel}
25
- <ChevronDownIcon aria-hidden="true" className="-mr-1 size-5 text-[#aaaaaa]" />
26
- </MenuButton>
27
- </div>
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
- <div key={index} className={item.divider ? 'py-1' : ''}>
34
- <MenuItem className="group">
35
- <a href={item.href} onClick={item.onClick} className="group flex items-center px-4 py-2 text-sm text-[#ffffff] data-focus:bg-[#2a2a2a] data-focus:text-[#ffffff] data-focus:outline-hidden cursor-pointer">
36
- {item.icon && (
37
- <span className="mr-3 size-5 text-[#aaaaaa] group-data-focus:text-[#cccccc]">
38
- {item.icon}
39
- </span>
40
- )}
41
- {item.label}
42
- </a>
43
- </MenuItem>
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
- </MenuItems>
47
- </Menu>
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
- hideText?: boolean;
6
+ showText?: boolean;
6
7
  loadingText?: string;
7
8
  textClassName?: string;
8
9
  }
9
10
 
10
- export const Loading = ({ className, hideText, loadingText, textClassName }: ILoadingProps) => {
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
- <svg aria-hidden="true" className={classNames("w-8 h-8 text-gray-200 animate-spin fill-black/50", className)} viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
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
- !hideText &&
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="docucod-navigation text-base lg:text-sm">
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-slate-900 dark:text-white">{currentTab.title}</h2>
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 text-slate-900 dark:text-white">{group.title}</h3>
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 to={page.href} className={classNames(
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-sky-500 before:bg-sky-500': isActive,
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 { Transition } from '@headlessui/react';
2
- import { CheckCircleIcon } from '@heroicons/react/24/outline';
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
- const context = useContext(NotificationContext);
20
- if (!context) {
21
- throw new Error('useNotification must be used within a NotificationProvider');
22
- }
23
- return context;
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('.docucod-navigation a');
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
- <NoResultsIcon className="mx-auto h-5 w-5 stroke-zinc-900 dark:stroke-zinc-600" />
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, resultIndex) => (
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 h-12">
288
- <SearchIcon className="pointer-events-none absolute top-0 left-3 h-full w-5 stroke-zinc-500" />
289
- <input
235
+ <div className="group relative flex">
236
+ <UxSearchInput
290
237
  ref={inputRef}
291
238
  data-autofocus
292
- className={classNames(
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
- <LoadingIcon className="h-5 w-5 animate-spin stroke-zinc-200 text-zinc-900 dark:stroke-zinc-800 dark:text-emerald-400" />
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 buttonRef = useRef<React.ElementRef<'button'>>(null)
371
+ const inputRef = useRef<React.ElementRef<typeof SearchInput>>(null)
428
372
  const [open, setOpen] = useState(false)
429
373
 
430
374
  return {
431
- buttonProps: {
432
- ref: buttonRef,
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
- buttonRef.current?.getBoundingClientRect() ?? {}
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 [modifierKey, setModifierKey] = useState<string>()
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
- <button
466
- type="button"
467
- className="hidden h-8 w-full items-center gap-2 rounded-full bg-white pr-3 pl-2 text-sm text-zinc-500 ring-1 ring-zinc-900/10 transition hover:ring-zinc-900/20 lg:flex dark:bg-white/5 dark:text-zinc-400 dark:ring-white/10 dark:ring-inset dark:hover:ring-white/20"
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
- <button
491
- type="button"
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
- <span className="absolute size-12 [@media(pointer:fine)]:hidden" />
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"
@@ -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[] = docsConfig.navigation.tabs.map(tab => ({
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 './index.css'
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 { NotificationProvider } from './components/notification.tsx'
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
- <NotificationProvider>
17
+ <ThemeProvider storageKey="@clidey/dory/theme">
18
+ <Toaster />
15
19
  <AppWithMDX />
16
- </NotificationProvider>,
20
+ </ThemeProvider>,
17
21
  document.getElementById('app')!
18
22
  )