@clidey/dory 0.27.0 → 0.30.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 (95) hide show
  1. package/bin/dist/dory.js +115 -29
  2. package/bin/dist/dory.js.map +1 -1
  3. package/package.json +12 -4
  4. package/.dockerignore +0 -4
  5. package/CLAUDE.md +0 -178
  6. package/CODE_OF_CONDUCT.md +0 -128
  7. package/CONTRIBUTING.md +0 -91
  8. package/SECURITY.md +0 -11
  9. package/bin/dory.ts +0 -433
  10. package/dist/favicon.svg +0 -75
  11. package/dist/frontmatter.json +0 -77
  12. package/dist/index.html +0 -55
  13. package/dist/llms.txt +0 -561
  14. package/dist/logo/logo.svg +0 -18
  15. package/docs/api-playground-demo.mdx +0 -119
  16. package/docs/api-reference/asyncapi.json +0 -324
  17. package/docs/api-reference/openapi.json +0 -366
  18. package/docs/api-reference/operations/create.mdx +0 -5
  19. package/docs/api-reference/operations/delete.mdx +0 -5
  20. package/docs/api-reference/operations/get.mdx +0 -5
  21. package/docs/api-reference/operations/update.mdx +0 -5
  22. package/docs/api-reference/websocket-operations/receive-chat-message.mdx +0 -5
  23. package/docs/api-reference/websocket-operations/receive-notifications.mdx +0 -5
  24. package/docs/api-reference/websocket-operations/receive-presence-update.mdx +0 -5
  25. package/docs/api-reference/websocket-operations/send-chat-message.mdx +0 -5
  26. package/docs/api-reference/websocket-operations/send-presence-update.mdx +0 -5
  27. package/docs/dory.json +0 -64
  28. package/docs/favicon.svg +0 -75
  29. package/docs/features.mdx +0 -440
  30. package/docs/introduction.mdx +0 -44
  31. package/docs/logo/logo.svg +0 -18
  32. package/docs/websocket-playground-demo.mdx +0 -135
  33. package/index.html +0 -54
  34. package/scripts/generate-frontmatter-json.js +0 -154
  35. package/scripts/generate-llm-txt.js +0 -115
  36. package/src/app.css +0 -5
  37. package/src/app.tsx +0 -13
  38. package/src/components/dropdown.tsx +0 -53
  39. package/src/components/hooks.tsx +0 -28
  40. package/src/components/loading.tsx +0 -21
  41. package/src/components/mdx-provider.tsx +0 -13
  42. package/src/components/navigation.tsx +0 -58
  43. package/src/components/notification.tsx +0 -13
  44. package/src/components/routes.tsx +0 -92
  45. package/src/components/search-index.ts +0 -43
  46. package/src/components/search.tsx +0 -435
  47. package/src/components/store.ts +0 -216
  48. package/src/config/mdx.js +0 -12
  49. package/src/config/mdx.ts +0 -12
  50. package/src/main.tsx +0 -22
  51. package/src/mdx/accordion.tsx +0 -41
  52. package/src/mdx/api-playground.tsx +0 -659
  53. package/src/mdx/api.tsx +0 -354
  54. package/src/mdx/async-api.tsx +0 -353
  55. package/src/mdx/callout.tsx +0 -64
  56. package/src/mdx/card.tsx +0 -54
  57. package/src/mdx/code.tsx +0 -289
  58. package/src/mdx/expandable.tsx +0 -36
  59. package/src/mdx/fence.tsx +0 -74
  60. package/src/mdx/icon.tsx +0 -73
  61. package/src/mdx/latex.tsx +0 -26
  62. package/src/mdx/list.tsx +0 -7
  63. package/src/mdx/mdx.tsx +0 -47
  64. package/src/mdx/mermaid.tsx +0 -147
  65. package/src/mdx/open-api.tsx +0 -311
  66. package/src/mdx/processor.ts +0 -174
  67. package/src/mdx/source.tsx +0 -58
  68. package/src/mdx/step.tsx +0 -51
  69. package/src/mdx/table.tsx +0 -20
  70. package/src/mdx/tag.tsx +0 -53
  71. package/src/mdx/text.tsx +0 -99
  72. package/src/mdx/websocket-playground.tsx +0 -824
  73. package/src/plugins/docs-assets.ts +0 -79
  74. package/src/plugins/frontmatter-dev-server.ts +0 -158
  75. package/src/plugins/frontmatter-generator.ts +0 -157
  76. package/src/plugins/llm-txt-dev-server.ts +0 -183
  77. package/src/plugins/llm-txt-generator.ts +0 -186
  78. package/src/plugins/sanitize.ts +0 -163
  79. package/src/plugins/unified-mdx-plugin.ts +0 -35
  80. package/src/theme.scss +0 -72
  81. package/src/types/config.ts +0 -47
  82. package/src/ui/header.tsx +0 -91
  83. package/src/ui/layout.tsx +0 -193
  84. package/src/ui/mobile-navigation.tsx +0 -64
  85. package/src/ui/prev-next-link.tsx +0 -74
  86. package/src/ui/table-of-content.tsx +0 -166
  87. package/src/utils/colors.ts +0 -54
  88. package/src/utils/fonts.ts +0 -23
  89. package/src/utils/functions.ts +0 -12
  90. package/src/vite-env.d.ts +0 -1
  91. package/tsconfig.app.json +0 -33
  92. package/tsconfig.bin.json +0 -24
  93. package/tsconfig.json +0 -7
  94. package/tsconfig.node.json +0 -26
  95. package/vite.config.ts +0 -41
@@ -1,435 +0,0 @@
1
- import {
2
- createAutocomplete,
3
- type AutocompleteApi,
4
- type AutocompleteCollection,
5
- type AutocompleteState,
6
- } from '@algolia/autocomplete-core'
7
- import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react'
8
- import {
9
- forwardRef,
10
- Fragment,
11
- Suspense,
12
- useCallback,
13
- useEffect,
14
- useId,
15
- useRef,
16
- useState,
17
- type JSX,
18
- } from 'react'
19
- import Highlighter from 'react-highlight-words'
20
- import { cn, Spinner, SearchInput as UxSearchInput } from '@clidey/ux'
21
- import classNames from 'classnames'
22
- import { useLocation } from 'wouter-preact'
23
- import { useMobileNavigation } from '../ui/mobile-navigation'
24
- import { usePathname, useSearchParams } from './hooks'
25
- import { search } from './search-index'
26
- import { ALL_NAVIGATION } from './store'
27
-
28
- type EmptyObject = Record<string, never>
29
- type Result = any;
30
-
31
- type Autocomplete = AutocompleteApi<
32
- Result,
33
- JSX.TargetedEvent,
34
- JSX.TargetedMouseEvent<HTMLElement>,
35
- JSX.TargetedKeyboardEvent<HTMLElement>
36
- >
37
-
38
- function useAutocomplete({ onNavigate }: { onNavigate: () => void }) {
39
- const id = useId()
40
- const [, nav] = useLocation()
41
- const [autocompleteState, setAutocompleteState] = useState<
42
- AutocompleteState<Result> | EmptyObject
43
- >({})
44
-
45
- function navigate({ itemUrl }: { itemUrl?: string }) {
46
- if (itemUrl) {
47
- nav(itemUrl);
48
- }
49
-
50
- onNavigate()
51
- }
52
-
53
- const [autocomplete] = useState<Autocomplete>(() =>
54
- createAutocomplete<
55
- Result,
56
- JSX.TargetedEvent,
57
- JSX.TargetedMouseEvent<HTMLElement>,
58
- JSX.TargetedKeyboardEvent<HTMLElement>
59
- >({
60
- id,
61
- placeholder: 'Find something...',
62
- defaultActiveItemId: 0,
63
- onStateChange({ state }) {
64
- setAutocompleteState(state)
65
- },
66
- shouldPanelOpen({ state }) {
67
- return state.query !== ''
68
- },
69
- navigator: {
70
- navigate,
71
- },
72
- getSources({ query }) {
73
- return [
74
- {
75
- sourceId: 'documentation',
76
- getItems() {
77
- return search(query, { limit: 5 })
78
- },
79
- getItemUrl({ item }) {
80
- return item.url
81
- },
82
- onSelect: navigate,
83
- },
84
- ]
85
- },
86
- }),
87
- )
88
-
89
- return { autocomplete, autocompleteState }
90
- }
91
-
92
-
93
- function HighlightQuery({ text, query }: { text: string; query: string }) {
94
- return (
95
- <Highlighter
96
- highlightClassName="underline bg-transparent text-brand-foreground"
97
- searchWords={[query]}
98
- autoEscape={true}
99
- textToHighlight={text}
100
- />
101
- )
102
- }
103
-
104
- function SearchResult({
105
- result,
106
- autocomplete,
107
- collection,
108
- query,
109
- }: {
110
- result: Result
111
- autocomplete: Autocomplete
112
- collection: AutocompleteCollection<Result>
113
- query: string
114
- }) {
115
- const id = useId()
116
-
117
- const currentPage = result.url.split('#')[0];
118
-
119
- // Get the group containing the current page
120
- const group = ALL_NAVIGATION.find(group =>
121
- group.groups.some(group =>
122
- group.pages.some(page => page.href === currentPage)
123
- )
124
- );
125
-
126
- const page = group?.groups.find(group => group.pages.find(page => page.href === currentPage));
127
-
128
- const sectionTitle = group?.title;
129
- const pageTitle = page?.title;
130
-
131
- const hierarchy: any[] = [sectionTitle, pageTitle].filter(
132
- (x): x is string => typeof x === 'string',
133
- );
134
-
135
- return (
136
- <li
137
- className={classNames(
138
- 'group block cursor-default px-4 py-3 aria-selected:bg-zinc-50 dark:aria-selected:bg-zinc-800/50',
139
- )}
140
- aria-labelledby={`${id}-hierarchy ${id}-title`}
141
- {...autocomplete.getItemProps({
142
- item: result,
143
- source: collection.source,
144
- })}
145
- >
146
- <div
147
- id={`${id}-title`}
148
- aria-hidden="true"
149
- className="text-sm font-medium text-brand-foreground group-aria-selected:text-brand-foreground"
150
- >
151
- <HighlightQuery text={result.title} query={query} />
152
- </div>
153
- {hierarchy.length > 0 && (
154
- <div
155
- id={`${id}-hierarchy`}
156
- aria-hidden="true"
157
- className="mt-1 truncate text-2xs whitespace-nowrap text-zinc-500"
158
- >
159
- {hierarchy.map((item, itemIndex, items) => (
160
- <Fragment key={itemIndex}>
161
- <HighlightQuery text={item} query={query} />
162
- <span
163
- className={
164
- itemIndex === items.length - 1
165
- ? 'sr-only'
166
- : 'mx-2 text-zinc-300 dark:text-zinc-700'
167
- }
168
- >
169
- /
170
- </span>
171
- </Fragment>
172
- ))}
173
- </div>
174
- )}
175
- </li>
176
- )
177
- }
178
-
179
- function SearchResults({
180
- autocomplete,
181
- query,
182
- collection,
183
- }: {
184
- autocomplete: Autocomplete
185
- query: string
186
- collection: AutocompleteCollection<Result>
187
- }) {
188
- if (collection.items.length === 0) {
189
- return (
190
- <div className="p-6 text-center">
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>
198
- <p className="mt-2 text-xs">
199
- Nothing found for{' '}
200
- <strong className="font-semibold break-words">
201
- &lsquo;{query}&rsquo;
202
- </strong>
203
- . Please try again.
204
- </p>
205
- </div>
206
- )
207
- }
208
-
209
- return (
210
- <ul {...autocomplete.getListProps()}>
211
- {collection.items.map((result) => (
212
- <SearchResult
213
- key={result.url}
214
- result={result}
215
- autocomplete={autocomplete}
216
- collection={collection}
217
- query={query}
218
- />
219
- ))}
220
- </ul>
221
- )
222
- }
223
-
224
- const SearchInput = forwardRef<
225
- React.ElementRef<'input'>,
226
- {
227
- autocomplete: Autocomplete
228
- autocompleteState: AutocompleteState<Result> | EmptyObject
229
- onClose: () => void
230
- }
231
- >(function SearchInput({ autocomplete, autocompleteState, onClose }, inputRef) {
232
- const inputProps = autocomplete.getInputProps({ inputElement: null })
233
-
234
- return (
235
- <div className="group relative flex">
236
- <UxSearchInput
237
- ref={inputRef}
238
- data-autofocus
239
- className={cn({
240
- 'pr-11': autocompleteState.status === 'stalled',
241
- 'pr-4': autocompleteState.status !== 'stalled',
242
- })}
243
- {...inputProps}
244
- onKeyDown={(event) => {
245
- if (
246
- event.key === 'Escape' &&
247
- !autocompleteState.isOpen &&
248
- autocompleteState.query === ''
249
- ) {
250
- // In Safari, closing the dialog with the escape key can sometimes cause the scroll position to jump to the
251
- // bottom of the page. This is a workaround for that until we can figure out a proper fix in Headless UI.
252
- if (document.activeElement instanceof HTMLElement) {
253
- document.activeElement.blur()
254
- }
255
-
256
- onClose()
257
- } else {
258
- inputProps.onKeyDown(event)
259
- }
260
- }}
261
- />
262
- {autocompleteState.status === 'stalled' && (
263
- <div className="absolute inset-y-0 right-3 flex items-center">
264
- <Spinner className="h-5 w-5 animate-spin text-brand-foreground" />
265
- </div>
266
- )}
267
- </div>
268
- )
269
- })
270
-
271
- function SearchDialog({
272
- open,
273
- setOpen,
274
- className,
275
- onNavigate = () => {},
276
- }: {
277
- open: boolean
278
- setOpen: (open: boolean) => void
279
- className?: string
280
- onNavigate?: () => void
281
- }) {
282
- const formRef = useRef<React.ElementRef<'form'>>(null)
283
- const panelRef = useRef<React.ElementRef<'div'>>(null)
284
- const inputRef = useRef<React.ElementRef<typeof SearchInput>>(null)
285
- const { autocomplete, autocompleteState } = useAutocomplete({
286
- onNavigate() {
287
- onNavigate()
288
- setOpen(false)
289
- },
290
- })
291
- const pathname = usePathname()
292
- const searchParams = useSearchParams()
293
-
294
- useEffect(() => {
295
- setOpen(false)
296
- }, [pathname, searchParams, setOpen])
297
-
298
- useEffect(() => {
299
- if (open) {
300
- return
301
- }
302
-
303
- function onKeyDown(event: KeyboardEvent) {
304
- if (event.key === 'k' && (event.metaKey || event.ctrlKey)) {
305
- event.preventDefault()
306
- setOpen(true)
307
- }
308
- }
309
-
310
- window.addEventListener('keydown', onKeyDown)
311
-
312
- return () => {
313
- window.removeEventListener('keydown', onKeyDown)
314
- }
315
- }, [open, setOpen])
316
-
317
- return (
318
- <Dialog
319
- open={open}
320
- onClose={() => {
321
- setOpen(false)
322
- autocomplete.setQuery('')
323
- }}
324
- className={classNames('fixed inset-0 z-50', className)}
325
- >
326
- <DialogBackdrop
327
- transition
328
- className="fixed inset-0 bg-zinc-400/25 backdrop-blur-xs data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-black/40"
329
- />
330
-
331
- <div className="fixed inset-0 overflow-y-auto px-4 py-4 sm:px-6 sm:py-20 md:py-32 lg:px-8 lg:py-[15vh] max-w-[calc(100vw-16px)]">
332
- <DialogPanel
333
- transition
334
- className="max-sm:mt-[25vh] mx-auto transform-gpu overflow-hidden rounded-lg bg-zinc-50 shadow-xl ring-1 ring-zinc-900/7.5 data-closed:scale-95 data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in sm:max-w-xl dark:bg-zinc-900 dark:ring-zinc-800"
335
- >
336
- <div {...autocomplete.getRootProps({})}>
337
- <form
338
- ref={formRef}
339
- {...autocomplete.getFormProps({
340
- inputElement: inputRef.current,
341
- })}
342
- >
343
- <SearchInput
344
- ref={inputRef}
345
- autocomplete={autocomplete}
346
- autocompleteState={autocompleteState}
347
- onClose={() => setOpen(false)}
348
- />
349
- <div
350
- ref={panelRef}
351
- className="border-t border-zinc-200 bg-white empty:hidden dark:border-zinc-100/5 dark:bg-white/2.5"
352
- {...autocomplete.getPanelProps({})}
353
- >
354
- {autocompleteState.isOpen && (
355
- <SearchResults
356
- autocomplete={autocomplete}
357
- query={autocompleteState.query}
358
- collection={autocompleteState.collections[0]}
359
- />
360
- )}
361
- </div>
362
- </form>
363
- </div>
364
- </DialogPanel>
365
- </div>
366
- </Dialog>
367
- )
368
- }
369
-
370
- function useSearchProps() {
371
- const inputRef = useRef<React.ElementRef<typeof SearchInput>>(null)
372
- const [open, setOpen] = useState(false)
373
-
374
- return {
375
- inputProps: {
376
- ref: inputRef,
377
- onClick() {
378
- setOpen(true)
379
- },
380
- },
381
- dialogProps: {
382
- open,
383
- setOpen: useCallback(
384
- (open: boolean) => {
385
- const { width = 0, height = 0 } =
386
- inputRef.current?.getBoundingClientRect?.() ?? {}
387
- if (!open || (width !== 0 && height !== 0)) {
388
- setOpen(open)
389
- }
390
- },
391
- [setOpen],
392
- ),
393
- },
394
- }
395
- }
396
-
397
- export function Search() {
398
- const { inputProps: buttonProps, dialogProps } = useSearchProps()
399
-
400
- return (
401
- <div className="hidden lg:block lg:max-w-md lg:flex-auto">
402
- <UxSearchInput
403
- aria-label="Find something..."
404
- placeholder="Find something..."
405
- {...buttonProps}
406
- />
407
- <Suspense fallback={null}>
408
- <SearchDialog className="hidden lg:block" {...dialogProps} />
409
- </Suspense>
410
- </div>
411
- )
412
- }
413
-
414
- export function MobileSearch() {
415
- const { close } = useMobileNavigation()
416
- const { inputProps: buttonProps, dialogProps } = useSearchProps()
417
-
418
- return (
419
- <div className="contents lg:hidden">
420
- <UxSearchInput
421
- className="text-sm"
422
- aria-label="Find something..."
423
- {...buttonProps}
424
- placeholder="Find something..."
425
- />
426
- <Suspense fallback={null}>
427
- <SearchDialog
428
- className="lg:hidden"
429
- onNavigate={close}
430
- {...dialogProps}
431
- />
432
- </Suspense>
433
- </div>
434
- )
435
- }
@@ -1,216 +0,0 @@
1
- import type { ComponentType } from 'preact';
2
- import { type RouteComponentProps } from 'wouter-preact';
3
- import docsConfig from '../../docs/dory.json' with { type: 'json' };
4
- import type { DoryConfig } from '../types/config';
5
- import { searchIndex } from './search-index';
6
-
7
- const config = docsConfig as DoryConfig;
8
-
9
- export const ALL_PAGES = Object.fromEntries(
10
- Object.entries(import.meta.glob<{ default: ComponentType; }>('../../docs/**/*.mdx'))
11
- .map(([path, loader]) => [pathFromFilename(path), loader])
12
- );
13
- export const ALL_OPENAPI = Object.fromEntries(
14
- Object.entries(import.meta.glob<{ default: string }>('../../docs/**/openapi.json', { query: 'raw', eager: true }))
15
- .map(([path, loader]) => [pathFromFilename(path), loader])
16
- );
17
- export const ALL_ASYNCAPI = Object.fromEntries(
18
- Object.entries(import.meta.glob<{ default: string }>('../../docs/**/asyncapi.json', { query: 'raw', eager: true }))
19
- .map(([path, loader]) => [pathFromFilename(path), loader])
20
- );
21
-
22
- export interface Route {
23
- path: string;
24
- component: ComponentType<RouteComponentProps<{ [param: number]: string | undefined; }>>;
25
- title?: string;
26
- description?: string;
27
- }
28
-
29
- export interface Navigation {
30
- title: string;
31
- groups: Array<{
32
- title: string;
33
- pages: Array<{
34
- title: string;
35
- href: string;
36
- }>;
37
- }>;
38
- }
39
-
40
- export const ALL_NAVIGATION: Navigation[] = config.navigation.tabs.map(tab => ({
41
- title: tab.tab,
42
- groups: tab.groups.map(group => ({
43
- title: group.group,
44
- pages: group.pages.map(page => {
45
- const path = `/${page}`;
46
- return {
47
- title: page.charAt(0).toUpperCase() + page.slice(1).replace(/-/g, ' '),
48
- href: path
49
- };
50
- })
51
- }))
52
- }));
53
-
54
- export function pathFromFilename(filename: string): string {
55
- return filename
56
- .replace('../../docs', '')
57
- .replace(/\/?index\.mdx$/, '/')
58
- .replace(/\.mdx$/, '')
59
- .toLowerCase();
60
- }
61
-
62
- function parseFrontMatter(content: string) {
63
- const frontMatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
64
- if (!frontMatterMatch) return { data: {}, content };
65
-
66
- const frontMatter = frontMatterMatch[1];
67
- const data: Record<string, any> = {};
68
-
69
- frontMatter.split('\n').forEach(line => {
70
- const [key, ...valueParts] = line.split(':');
71
- if (key && valueParts.length) {
72
- const value = valueParts.join(':').trim();
73
- data[key.trim()] = value.replace(/^["']|["']$/g, ''); // Remove quotes if present
74
- }
75
- });
76
-
77
- return { data, content: content.slice(frontMatterMatch[0].length) };
78
- }
79
-
80
- export let completeFrontMatter: Record<string, any>[] = [];
81
- let preloadedFrontMatter: Record<string, any>[] | null = null;
82
-
83
- // Preload frontmatter from JSON file (optimized approach)
84
- export async function preloadFrontmatter() {
85
- if (preloadedFrontMatter) return; // Already loaded
86
-
87
- try {
88
- const response = await fetch('/frontmatter.json');
89
- if (response.ok) {
90
- preloadedFrontMatter = await response.json();
91
- if (preloadedFrontMatter) {
92
- completeFrontMatter = [...preloadedFrontMatter];
93
-
94
- // Update navigation titles from preloaded data
95
- for (const fm of preloadedFrontMatter) {
96
- updateNavigationTitle(fm.path, fm.title);
97
- }
98
-
99
- // Add to search index
100
- await addPreloadedContentToSearch();
101
- }
102
- }
103
- } catch (error) {
104
- console.warn('Failed to load prebuilt frontmatter JSON, falling back to dynamic loading:', error);
105
- }
106
- }
107
-
108
- // Add preloaded content to search index
109
- async function addPreloadedContentToSearch() {
110
- if (!preloadedFrontMatter) return;
111
-
112
- const mdxFiles = import.meta.glob<{ default: string }>('../../docs/**/*.mdx', { query: 'raw' });
113
- const fileEntries = Object.entries(mdxFiles);
114
-
115
- await Promise.all(
116
- preloadedFrontMatter.map(async (fm) => {
117
- const fileEntry = fileEntries.find(([filePath]) => pathFromFilename(filePath) === fm.path);
118
- if (fileEntry) {
119
- const [, loader] = fileEntry;
120
- const rawContent = (await loader()).default;
121
- const { content } = parseFrontMatter(rawContent);
122
-
123
- searchIndex.add({
124
- url: fm.path,
125
- title: fm.title || '',
126
- content,
127
- pageTitle: fm.title || ''
128
- });
129
- }
130
- })
131
- );
132
- }
133
-
134
- // Load frontmatter for a specific pathname (optimized with fallback)
135
- export async function loadMDXFrontMatterForPath(pathname: string) {
136
- // If we have preloaded data, check if this path is already loaded
137
- if (preloadedFrontMatter && preloadedFrontMatter.find(fm => fm.path === pathname)) {
138
- return;
139
- }
140
-
141
- // Check if already loaded in completeFrontMatter
142
- if (completeFrontMatter.find(fm => fm.path === pathname)) return;
143
-
144
- // Fallback to dynamic loading
145
- const mdxFiles = import.meta.glob<{ default: string }>('../../docs/**/*.mdx', { query: 'raw' });
146
- const fileEntries = Object.entries(mdxFiles);
147
-
148
- const fileEntry = fileEntries.find(([path]) => pathFromFilename(path) === pathname);
149
- if (!fileEntry) return;
150
-
151
- const [path, loader] = fileEntry;
152
- const rawContent = (await loader()).default;
153
- const { data, content } = parseFrontMatter(rawContent);
154
- const filename = pathFromFilename(path);
155
-
156
- updateNavigationTitle(filename, data.title);
157
- searchIndex.add({
158
- url: filename,
159
- title: data.title || '',
160
- content,
161
- pageTitle: data.title || ''
162
- });
163
-
164
- const frontmatterItem = { ...data, path: filename };
165
- completeFrontMatter = [...completeFrontMatter, frontmatterItem];
166
- }
167
-
168
- // Load all remaining frontmatter (after initial load) - optimized with fallback
169
- export async function loadAllMDXFrontMatter(pathname: string) {
170
- // If we have preloaded data, we're already done
171
- if (preloadedFrontMatter) {
172
- return;
173
- }
174
-
175
- // Fallback to dynamic loading
176
- const mdxFiles = import.meta.glob<{ default: string }>('../../docs/**/*.mdx', { query: 'raw' });
177
- const fileEntries = Object.entries(mdxFiles);
178
-
179
- const remainingEntries = fileEntries.filter(([path]) => pathFromFilename(path) !== pathname);
180
- const newFrontmatter: Record<string, any>[] = [];
181
-
182
- await Promise.all(
183
- remainingEntries.map(async ([path, loader]) => {
184
- const rawContent = (await loader()).default;
185
- const { data, content } = parseFrontMatter(rawContent);
186
- const filename = pathFromFilename(path);
187
-
188
- updateNavigationTitle(filename, data.title);
189
- searchIndex.add({
190
- url: filename,
191
- title: data.title || '',
192
- content,
193
- pageTitle: data.title || ''
194
- });
195
-
196
- newFrontmatter.push({ ...data, path: filename });
197
- })
198
- );
199
-
200
- completeFrontMatter = [...completeFrontMatter, ...newFrontmatter];
201
- }
202
-
203
- // Shared helper
204
- function updateNavigationTitle(filename: string, title?: string) {
205
- if (!title) return;
206
-
207
- for (const tab of ALL_NAVIGATION) {
208
- for (const group of tab.groups) {
209
- for (const page of group.pages) {
210
- if (page.href === filename) {
211
- page.title = title;
212
- }
213
- }
214
- }
215
- }
216
- }
package/src/config/mdx.js DELETED
@@ -1,12 +0,0 @@
1
- import remarkGfm from 'remark-gfm';
2
- import { remarkSafeVars } from '../plugins/sanitize.ts';
3
-
4
- /**
5
- * Shared MDX configuration used by both main build (vite.config.ts)
6
- * and CLI build:file command (bin/dory.js)
7
- */
8
- export const getMdxConfig = (isDevelopment = false) => ({
9
- providerImportSource: '@mdx-js/preact',
10
- remarkPlugins: [remarkGfm, remarkSafeVars],
11
- development: isDevelopment,
12
- });
package/src/config/mdx.ts DELETED
@@ -1,12 +0,0 @@
1
- import remarkGfm from 'remark-gfm';
2
- import { remarkSafeVars } from '../plugins/sanitize';
3
-
4
- /**
5
- * Shared MDX configuration used by both main build (vite.config.ts)
6
- * and CLI build:file command (bin/dory.js)
7
- */
8
- export const getMdxConfig = (isDevelopment: boolean = false) => ({
9
- providerImportSource: '@mdx-js/preact',
10
- remarkPlugins: [remarkGfm, remarkSafeVars],
11
- development: isDevelopment,
12
- });
package/src/main.tsx DELETED
@@ -1,22 +0,0 @@
1
- import { render } from 'preact'
2
- import '@clidey/ux/styles.css';
3
- import { App } from './app.tsx'
4
- import { withMDX } from './components/mdx-provider.tsx'
5
- import { loadFonts } from './utils/fonts'
6
- import { loadColors } from './utils/colors'
7
- import { Toaster } from '@clidey/ux'
8
- import { ThemeProvider } from '@clidey/ux'
9
-
10
- // Load fonts and colors before rendering the app
11
- loadFonts()
12
- loadColors()
13
-
14
- const AppWithMDX = withMDX(App)
15
-
16
- render(
17
- <ThemeProvider storageKey="@clidey/dory/theme">
18
- <Toaster />
19
- <AppWithMDX />
20
- </ThemeProvider>,
21
- document.getElementById('app')!
22
- )