@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.
- package/bin/dist/dory.js +115 -29
- package/bin/dist/dory.js.map +1 -1
- package/package.json +12 -4
- package/.dockerignore +0 -4
- package/CLAUDE.md +0 -178
- package/CODE_OF_CONDUCT.md +0 -128
- package/CONTRIBUTING.md +0 -91
- package/SECURITY.md +0 -11
- package/bin/dory.ts +0 -433
- package/dist/favicon.svg +0 -75
- package/dist/frontmatter.json +0 -77
- package/dist/index.html +0 -55
- package/dist/llms.txt +0 -561
- package/dist/logo/logo.svg +0 -18
- package/docs/api-playground-demo.mdx +0 -119
- package/docs/api-reference/asyncapi.json +0 -324
- package/docs/api-reference/openapi.json +0 -366
- package/docs/api-reference/operations/create.mdx +0 -5
- package/docs/api-reference/operations/delete.mdx +0 -5
- package/docs/api-reference/operations/get.mdx +0 -5
- package/docs/api-reference/operations/update.mdx +0 -5
- package/docs/api-reference/websocket-operations/receive-chat-message.mdx +0 -5
- package/docs/api-reference/websocket-operations/receive-notifications.mdx +0 -5
- package/docs/api-reference/websocket-operations/receive-presence-update.mdx +0 -5
- package/docs/api-reference/websocket-operations/send-chat-message.mdx +0 -5
- package/docs/api-reference/websocket-operations/send-presence-update.mdx +0 -5
- package/docs/dory.json +0 -64
- package/docs/favicon.svg +0 -75
- package/docs/features.mdx +0 -440
- package/docs/introduction.mdx +0 -44
- package/docs/logo/logo.svg +0 -18
- package/docs/websocket-playground-demo.mdx +0 -135
- package/index.html +0 -54
- package/scripts/generate-frontmatter-json.js +0 -154
- package/scripts/generate-llm-txt.js +0 -115
- package/src/app.css +0 -5
- package/src/app.tsx +0 -13
- package/src/components/dropdown.tsx +0 -53
- package/src/components/hooks.tsx +0 -28
- package/src/components/loading.tsx +0 -21
- package/src/components/mdx-provider.tsx +0 -13
- package/src/components/navigation.tsx +0 -58
- package/src/components/notification.tsx +0 -13
- package/src/components/routes.tsx +0 -92
- package/src/components/search-index.ts +0 -43
- package/src/components/search.tsx +0 -435
- package/src/components/store.ts +0 -216
- package/src/config/mdx.js +0 -12
- package/src/config/mdx.ts +0 -12
- package/src/main.tsx +0 -22
- package/src/mdx/accordion.tsx +0 -41
- package/src/mdx/api-playground.tsx +0 -659
- package/src/mdx/api.tsx +0 -354
- package/src/mdx/async-api.tsx +0 -353
- package/src/mdx/callout.tsx +0 -64
- package/src/mdx/card.tsx +0 -54
- package/src/mdx/code.tsx +0 -289
- package/src/mdx/expandable.tsx +0 -36
- package/src/mdx/fence.tsx +0 -74
- package/src/mdx/icon.tsx +0 -73
- package/src/mdx/latex.tsx +0 -26
- package/src/mdx/list.tsx +0 -7
- package/src/mdx/mdx.tsx +0 -47
- package/src/mdx/mermaid.tsx +0 -147
- package/src/mdx/open-api.tsx +0 -311
- package/src/mdx/processor.ts +0 -174
- package/src/mdx/source.tsx +0 -58
- package/src/mdx/step.tsx +0 -51
- package/src/mdx/table.tsx +0 -20
- package/src/mdx/tag.tsx +0 -53
- package/src/mdx/text.tsx +0 -99
- package/src/mdx/websocket-playground.tsx +0 -824
- package/src/plugins/docs-assets.ts +0 -79
- package/src/plugins/frontmatter-dev-server.ts +0 -158
- package/src/plugins/frontmatter-generator.ts +0 -157
- package/src/plugins/llm-txt-dev-server.ts +0 -183
- package/src/plugins/llm-txt-generator.ts +0 -186
- package/src/plugins/sanitize.ts +0 -163
- package/src/plugins/unified-mdx-plugin.ts +0 -35
- package/src/theme.scss +0 -72
- package/src/types/config.ts +0 -47
- package/src/ui/header.tsx +0 -91
- package/src/ui/layout.tsx +0 -193
- package/src/ui/mobile-navigation.tsx +0 -64
- package/src/ui/prev-next-link.tsx +0 -74
- package/src/ui/table-of-content.tsx +0 -166
- package/src/utils/colors.ts +0 -54
- package/src/utils/fonts.ts +0 -23
- package/src/utils/functions.ts +0 -12
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.app.json +0 -33
- package/tsconfig.bin.json +0 -24
- package/tsconfig.json +0 -7
- package/tsconfig.node.json +0 -26
- package/vite.config.ts +0 -41
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
|
-
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
-
const __dirname = path.dirname(__filename);
|
|
9
|
-
|
|
10
|
-
// Read dory.json to get navigation order (flat list, in order)
|
|
11
|
-
function getNavigationOrder(docsDir) {
|
|
12
|
-
const doryJsonPath = path.join(docsDir, 'dory.json');
|
|
13
|
-
if (!fs.existsSync(doryJsonPath)) return [];
|
|
14
|
-
const doryJson = JSON.parse(fs.readFileSync(doryJsonPath, 'utf-8'));
|
|
15
|
-
const order = [];
|
|
16
|
-
|
|
17
|
-
function walkPages(pages) {
|
|
18
|
-
for (const page of pages) {
|
|
19
|
-
if (typeof page === 'string') {
|
|
20
|
-
order.push(page);
|
|
21
|
-
} else if (typeof page === 'object' && page.pages) {
|
|
22
|
-
walkPages(page.pages);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (doryJson.navigation && Array.isArray(doryJson.navigation.tabs)) {
|
|
28
|
-
for (const tab of doryJson.navigation.tabs) {
|
|
29
|
-
if (tab.groups && Array.isArray(tab.groups)) {
|
|
30
|
-
for (const group of tab.groups) {
|
|
31
|
-
if (group.pages && Array.isArray(group.pages)) {
|
|
32
|
-
walkPages(group.pages);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return order;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Function to parse frontmatter from MDX content
|
|
42
|
-
function parseFrontmatter(content) {
|
|
43
|
-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
44
|
-
if (!frontmatterMatch) return { data: {}, content };
|
|
45
|
-
|
|
46
|
-
const frontmatter = frontmatterMatch[1];
|
|
47
|
-
const data = {};
|
|
48
|
-
|
|
49
|
-
frontmatter.split('\n').forEach(line => {
|
|
50
|
-
const [key, ...valueParts] = line.split(':');
|
|
51
|
-
if (key && valueParts.length) {
|
|
52
|
-
const value = valueParts.join(':').trim();
|
|
53
|
-
data[key.trim()] = value.replace(/^["']|["']$/g, '');
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
return { data, content: content.slice(frontmatterMatch[0].length) };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Convert filename to path format used by the store
|
|
61
|
-
function pathFromFilename(filename, docsDir) {
|
|
62
|
-
return filename
|
|
63
|
-
.replace(docsDir, '')
|
|
64
|
-
.replace(/\/?index\.mdx$/, '/')
|
|
65
|
-
.replace(/\.mdx$/, '')
|
|
66
|
-
.replace(/\\/g, '/')
|
|
67
|
-
.toLowerCase();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Function to recursively find all MDX files and map by relative path (without .mdx)
|
|
71
|
-
function findMdxFilesMap(dir, baseDir) {
|
|
72
|
-
const files = {};
|
|
73
|
-
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
74
|
-
|
|
75
|
-
for (const item of items) {
|
|
76
|
-
const fullPath = path.join(dir, item.name);
|
|
77
|
-
if (item.isDirectory()) {
|
|
78
|
-
Object.assign(files, findMdxFilesMap(fullPath, baseDir));
|
|
79
|
-
} else if (item.isFile() && item.name.endsWith('.mdx')) {
|
|
80
|
-
// Key: relative path without .mdx, with forward slashes
|
|
81
|
-
let rel = path.relative(baseDir, fullPath).replace(/\\/g, '/');
|
|
82
|
-
if (rel.endsWith('.mdx')) rel = rel.slice(0, -4);
|
|
83
|
-
files[rel] = fullPath;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return files;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Main function to generate frontmatter JSON
|
|
91
|
-
async function generateFrontmatterJson() {
|
|
92
|
-
const docsDir = path.join(__dirname, '..', 'docs');
|
|
93
|
-
const outputDir = path.join(__dirname, '..', 'dist');
|
|
94
|
-
|
|
95
|
-
// Ensure output directory exists
|
|
96
|
-
if (!fs.existsSync(outputDir)) {
|
|
97
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const navOrder = getNavigationOrder(docsDir);
|
|
101
|
-
const mdxFilesMap = findMdxFilesMap(docsDir, docsDir);
|
|
102
|
-
const usedKeys = new Set();
|
|
103
|
-
const frontmatterArray = [];
|
|
104
|
-
|
|
105
|
-
// Process files in navigation order first
|
|
106
|
-
for (const navKey of navOrder) {
|
|
107
|
-
// Try both with and without "index"
|
|
108
|
-
let fileKey = navKey;
|
|
109
|
-
if (mdxFilesMap[fileKey]) {
|
|
110
|
-
usedKeys.add(fileKey);
|
|
111
|
-
} else if (mdxFilesMap[`${fileKey}/index`]) {
|
|
112
|
-
fileKey = `${fileKey}/index`;
|
|
113
|
-
usedKeys.add(fileKey);
|
|
114
|
-
} else {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const filePath = mdxFilesMap[fileKey];
|
|
119
|
-
const rawContent = fs.readFileSync(filePath, 'utf-8');
|
|
120
|
-
const { data } = parseFrontmatter(rawContent);
|
|
121
|
-
const pathname = pathFromFilename(filePath, docsDir);
|
|
122
|
-
|
|
123
|
-
frontmatterArray.push({
|
|
124
|
-
...data,
|
|
125
|
-
path: pathname
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Process any extra files not in navigation order, sorted
|
|
130
|
-
const extraKeys = Object.keys(mdxFilesMap).filter(k => !usedKeys.has(k)).sort();
|
|
131
|
-
for (const fileKey of extraKeys) {
|
|
132
|
-
const filePath = mdxFilesMap[fileKey];
|
|
133
|
-
const rawContent = fs.readFileSync(filePath, 'utf-8');
|
|
134
|
-
const { data } = parseFrontmatter(rawContent);
|
|
135
|
-
const pathname = pathFromFilename(filePath, docsDir);
|
|
136
|
-
|
|
137
|
-
frontmatterArray.push({
|
|
138
|
-
...data,
|
|
139
|
-
path: pathname
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Write to output file
|
|
144
|
-
const jsonContent = JSON.stringify(frontmatterArray, null, 2);
|
|
145
|
-
const outputPath = path.join(outputDir, 'frontmatter.json');
|
|
146
|
-
fs.writeFileSync(outputPath, jsonContent);
|
|
147
|
-
|
|
148
|
-
console.log(`✅ Generated frontmatter JSON file: ${outputPath}`);
|
|
149
|
-
console.log(`📄 Processed ${frontmatterArray.length} MDX files`);
|
|
150
|
-
console.log(`📝 Total JSON size: ${jsonContent.length} characters`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Run the script
|
|
154
|
-
generateFrontmatterJson().catch(console.error);
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
|
-
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
-
const __dirname = path.dirname(__filename);
|
|
9
|
-
|
|
10
|
-
// Function to parse frontmatter from MDX content
|
|
11
|
-
function parseFrontmatter(content) {
|
|
12
|
-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
13
|
-
if (!frontmatterMatch) return { data: {}, content };
|
|
14
|
-
|
|
15
|
-
const frontmatter = frontmatterMatch[1];
|
|
16
|
-
const data = {};
|
|
17
|
-
|
|
18
|
-
frontmatter.split('\n').forEach(line => {
|
|
19
|
-
const [key, ...valueParts] = line.split(':');
|
|
20
|
-
if (key && valueParts.length) {
|
|
21
|
-
const value = valueParts.join(':').trim();
|
|
22
|
-
data[key.trim()] = value.replace(/^["']|["']$/g, '');
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
return { data, content: content.slice(frontmatterMatch[0].length) };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Function to recursively find all MDX files
|
|
30
|
-
function findMdxFiles(dir) {
|
|
31
|
-
const files = [];
|
|
32
|
-
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
33
|
-
|
|
34
|
-
for (const item of items) {
|
|
35
|
-
const fullPath = path.join(dir, item.name);
|
|
36
|
-
if (item.isDirectory()) {
|
|
37
|
-
files.push(...findMdxFiles(fullPath));
|
|
38
|
-
} else if (item.isFile() && item.name.endsWith('.mdx')) {
|
|
39
|
-
files.push(fullPath);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return files;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Function to clean MDX content for LLM consumption
|
|
47
|
-
function cleanMdxContent(content) {
|
|
48
|
-
// Remove JSX components but keep their content
|
|
49
|
-
let cleaned = content
|
|
50
|
-
// Remove JSX opening/closing tags but keep content
|
|
51
|
-
.replace(/<(\w+)([^>]*?)>/g, '')
|
|
52
|
-
.replace(/<\/\w+>/g, '')
|
|
53
|
-
// Remove code block language specifiers
|
|
54
|
-
.replace(/```(\w+)/g, '```')
|
|
55
|
-
// Remove HTML comments
|
|
56
|
-
.replace(/<!--[\s\S]*?-->/g, '')
|
|
57
|
-
// Remove excessive whitespace
|
|
58
|
-
.replace(/\n\s*\n\s*\n/g, '\n\n')
|
|
59
|
-
// Remove leading/trailing whitespace
|
|
60
|
-
.trim();
|
|
61
|
-
|
|
62
|
-
return cleaned;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Main function to generate LLM text
|
|
66
|
-
async function generateLlmTxt() {
|
|
67
|
-
const docsDir = path.join(__dirname, '..', 'docs');
|
|
68
|
-
const outputDir = path.join(__dirname, '..', 'dist');
|
|
69
|
-
|
|
70
|
-
// Ensure output directory exists
|
|
71
|
-
if (!fs.existsSync(outputDir)) {
|
|
72
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const mdxFiles = findMdxFiles(docsDir);
|
|
76
|
-
let aggregatedContent = '';
|
|
77
|
-
|
|
78
|
-
// Sort files for consistent output
|
|
79
|
-
mdxFiles.sort();
|
|
80
|
-
|
|
81
|
-
for (const filePath of mdxFiles) {
|
|
82
|
-
const rawContent = fs.readFileSync(filePath, 'utf-8');
|
|
83
|
-
const { data, content } = parseFrontmatter(rawContent);
|
|
84
|
-
const cleanedContent = cleanMdxContent(content);
|
|
85
|
-
|
|
86
|
-
// Add file header
|
|
87
|
-
const relativePath = path.relative(docsDir, filePath);
|
|
88
|
-
aggregatedContent += `\n# ${relativePath}\n`;
|
|
89
|
-
|
|
90
|
-
// Add title if available
|
|
91
|
-
if (data.title) {
|
|
92
|
-
aggregatedContent += `## ${data.title}\n\n`;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Add description if available
|
|
96
|
-
if (data.description) {
|
|
97
|
-
aggregatedContent += `${data.description}\n\n`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Add cleaned content
|
|
101
|
-
aggregatedContent += cleanedContent + '\n\n';
|
|
102
|
-
aggregatedContent += '---\n\n';
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Write to output file
|
|
106
|
-
const outputPath = path.join(outputDir, 'llms.txt');
|
|
107
|
-
fs.writeFileSync(outputPath, aggregatedContent.trim());
|
|
108
|
-
|
|
109
|
-
console.log(`✅ Generated LLM text file: ${outputPath}`);
|
|
110
|
-
console.log(`📄 Processed ${mdxFiles.length} MDX files`);
|
|
111
|
-
console.log(`📝 Total content size: ${aggregatedContent.length} characters`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Run the script
|
|
115
|
-
generateLlmTxt().catch(console.error);
|
package/src/app.css
DELETED
package/src/app.tsx
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DropdownMenu,
|
|
3
|
-
DropdownMenuTrigger,
|
|
4
|
-
DropdownMenuContent,
|
|
5
|
-
DropdownMenuItem,
|
|
6
|
-
DropdownMenuSeparator,
|
|
7
|
-
Button
|
|
8
|
-
} from '@clidey/ux'
|
|
9
|
-
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
|
10
|
-
import type { ComponentChildren } from 'preact'
|
|
11
|
-
import type { ReactNode } from 'preact/compat'
|
|
12
|
-
|
|
13
|
-
interface DropdownItem {
|
|
14
|
-
icon?: ComponentChildren
|
|
15
|
-
label: string
|
|
16
|
-
href?: string
|
|
17
|
-
onClick?: () => void
|
|
18
|
-
divider?: boolean
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface DropdownProps {
|
|
22
|
-
buttonLabel: string | ReactNode;
|
|
23
|
-
items: DropdownItem[]
|
|
24
|
-
className?: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default function Dropdown({ buttonLabel, items, className = '' }: DropdownProps) {
|
|
28
|
-
return (
|
|
29
|
-
<DropdownMenu>
|
|
30
|
-
<DropdownMenuTrigger>
|
|
31
|
-
<Button className={className}>
|
|
32
|
-
{buttonLabel}
|
|
33
|
-
<ChevronDownIcon className="ml-2 h-4 w-4" />
|
|
34
|
-
</Button>
|
|
35
|
-
</DropdownMenuTrigger>
|
|
36
|
-
<DropdownMenuContent>
|
|
37
|
-
{items.map((item, index) => (
|
|
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
|
-
</>
|
|
49
|
-
))}
|
|
50
|
-
</DropdownMenuContent>
|
|
51
|
-
</DropdownMenu>
|
|
52
|
-
)
|
|
53
|
-
}
|
package/src/components/hooks.tsx
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'preact/hooks';
|
|
2
|
-
import { useLocation } from 'wouter-preact';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Custom hook to get the current pathname
|
|
6
|
-
*/
|
|
7
|
-
export const usePathname = () => {
|
|
8
|
-
const [pathname] = useLocation();
|
|
9
|
-
return pathname;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Custom hook to get the URL search params as a URLSearchParams instance
|
|
14
|
-
*/
|
|
15
|
-
export const useSearchParams = () => {
|
|
16
|
-
const [searchParams, setSearchParams] = useState(new URLSearchParams(window.location.search));
|
|
17
|
-
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
const handlePopState = () => {
|
|
20
|
-
setSearchParams(new URLSearchParams(window.location.search));
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
window.addEventListener('popstate', handlePopState);
|
|
24
|
-
return () => window.removeEventListener('popstate', handlePopState);
|
|
25
|
-
}, []);
|
|
26
|
-
|
|
27
|
-
return searchParams;
|
|
28
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Spinner } from '@clidey/ux';
|
|
2
|
-
import classNames from "classnames";
|
|
3
|
-
|
|
4
|
-
type ILoadingProps = {
|
|
5
|
-
className?: string;
|
|
6
|
-
showText?: boolean;
|
|
7
|
-
loadingText?: string;
|
|
8
|
-
textClassName?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const Loading = ({ className, showText = false, loadingText, textClassName }: ILoadingProps) => {
|
|
12
|
-
return (
|
|
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} />
|
|
15
|
-
{
|
|
16
|
-
showText &&
|
|
17
|
-
<div className={classNames("text-sm text-gray-500", textClassName)}>{ loadingText ?? "Loading..."}</div>
|
|
18
|
-
}
|
|
19
|
-
</div>
|
|
20
|
-
)
|
|
21
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { MDXProvider } from '@mdx-js/preact';
|
|
2
|
-
import Layout from '../ui/layout';
|
|
3
|
-
import * as mdx from '../mdx/mdx';
|
|
4
|
-
|
|
5
|
-
export const withMDX = (Content: preact.ComponentType) => () => {
|
|
6
|
-
return (
|
|
7
|
-
<MDXProvider components={mdx}>
|
|
8
|
-
<Layout>
|
|
9
|
-
<Content />
|
|
10
|
-
</Layout>
|
|
11
|
-
</MDXProvider>
|
|
12
|
-
);
|
|
13
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import classNames from 'classnames';
|
|
2
|
-
import { useMemo } from 'preact/hooks';
|
|
3
|
-
import { Link } from 'wouter-preact';
|
|
4
|
-
import { usePathname } from './hooks';
|
|
5
|
-
import { ALL_NAVIGATION } from './store';
|
|
6
|
-
|
|
7
|
-
export function Navigation() {
|
|
8
|
-
const pathname = usePathname();
|
|
9
|
-
|
|
10
|
-
const effectivePath = pathname === '/'
|
|
11
|
-
? ALL_NAVIGATION[0]?.groups[0]?.pages[0]?.href ?? '/'
|
|
12
|
-
: pathname;
|
|
13
|
-
|
|
14
|
-
const currentTab = useMemo(() => {
|
|
15
|
-
return ALL_NAVIGATION.find(tab =>
|
|
16
|
-
tab.groups.some(group =>
|
|
17
|
-
group.pages.some(page => page.href === effectivePath)
|
|
18
|
-
)
|
|
19
|
-
);
|
|
20
|
-
}, [effectivePath]);
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<nav className="dory-navigation text-base lg:text-sm">
|
|
24
|
-
<ul role="list" className="space-y-9">
|
|
25
|
-
{currentTab && (
|
|
26
|
-
<li key={currentTab.title}>
|
|
27
|
-
<h2 className="font-display font-medium text-xs">{currentTab.title}</h2>
|
|
28
|
-
{currentTab.groups.map(group => (
|
|
29
|
-
<div key={group.title}>
|
|
30
|
-
<h3 className="mt-4 text-sm font-semibold">{group.title}</h3>
|
|
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
|
-
{group.pages.map((page) => {
|
|
33
|
-
const isActive = effectivePath === page.href;
|
|
34
|
-
return (
|
|
35
|
-
<li key={page.href} className="relative">
|
|
36
|
-
<Link
|
|
37
|
-
href={page.href}
|
|
38
|
-
className={classNames(
|
|
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',
|
|
40
|
-
{
|
|
41
|
-
'font-semibold text-brand-foreground': isActive,
|
|
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
|
|
43
|
-
},
|
|
44
|
-
)}>
|
|
45
|
-
{page.title}
|
|
46
|
-
</Link>
|
|
47
|
-
</li>
|
|
48
|
-
);
|
|
49
|
-
})}
|
|
50
|
-
</ul>
|
|
51
|
-
</div>
|
|
52
|
-
))}
|
|
53
|
-
</li>
|
|
54
|
-
)}
|
|
55
|
-
</ul>
|
|
56
|
-
</nav>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { toast } from '@clidey/ux';
|
|
2
|
-
import { InfoIcon } from 'lucide-react';
|
|
3
|
-
|
|
4
|
-
export function useNotification() {
|
|
5
|
-
return {
|
|
6
|
-
showNotification: (message: string, description?: string) => {
|
|
7
|
-
toast(message, {
|
|
8
|
-
icon: <InfoIcon className="h-4 w-4" />,
|
|
9
|
-
description: description,
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'preact/hooks';
|
|
2
|
-
import { Route, Router, useLocation } from 'wouter-preact';
|
|
3
|
-
import { usePathname } from './hooks';
|
|
4
|
-
import { ALL_NAVIGATION, ALL_PAGES, pathFromFilename } from './store';
|
|
5
|
-
import { Loading } from './loading';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export default function Routes() {
|
|
9
|
-
const [routes, setRoutes] = useState<any[]>([]);
|
|
10
|
-
const pathname = usePathname();
|
|
11
|
-
const [, navigate] = useLocation();
|
|
12
|
-
const [loading, setLoading] = useState(true);
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
const links = document.querySelectorAll('.dory-navigation a');
|
|
16
|
-
const listeners: (() => void)[] = [];
|
|
17
|
-
const preloaded = new Set();
|
|
18
|
-
|
|
19
|
-
links.forEach(link => {
|
|
20
|
-
const href = link.getAttribute('href');
|
|
21
|
-
if (!href) return;
|
|
22
|
-
|
|
23
|
-
const loaderEntry = Object.entries(ALL_PAGES).find(
|
|
24
|
-
([path]) => path === href
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
if (!loaderEntry) return;
|
|
28
|
-
|
|
29
|
-
const [path, loader] = loaderEntry;
|
|
30
|
-
|
|
31
|
-
const handleMouseEnter = () => {
|
|
32
|
-
if (!preloaded.has(path)) {
|
|
33
|
-
loader();
|
|
34
|
-
preloaded.add(path);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
link.addEventListener('mouseenter', handleMouseEnter);
|
|
39
|
-
listeners.push(() => link.removeEventListener('mouseenter', handleMouseEnter));
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return () => {
|
|
43
|
-
listeners.forEach(unsub => unsub());
|
|
44
|
-
};
|
|
45
|
-
}, []);
|
|
46
|
-
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
setLoading(true);
|
|
49
|
-
const loadRoutes = async () => {
|
|
50
|
-
// For root path, load the first navigation page
|
|
51
|
-
const targetPath = pathname === '/' ? ALL_NAVIGATION[0].groups[0].pages[0].href : pathname;
|
|
52
|
-
|
|
53
|
-
const entries = await Promise.all(
|
|
54
|
-
Object.entries(ALL_PAGES).filter(([path]) => path === targetPath).map(async ([path, loader]) => {
|
|
55
|
-
const module = await loader();
|
|
56
|
-
const routePath = pathname === '/' ? '/' : pathFromFilename(path);
|
|
57
|
-
return {
|
|
58
|
-
path: routePath,
|
|
59
|
-
component: module.default,
|
|
60
|
-
};
|
|
61
|
-
})
|
|
62
|
-
);
|
|
63
|
-
setRoutes(entries);
|
|
64
|
-
setLoading(false);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
loadRoutes();
|
|
68
|
-
}, [pathname]);
|
|
69
|
-
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
const page = ALL_NAVIGATION.find((tab) => tab.groups.find((group) => group.pages.find((page) => page.href === pathname)));
|
|
72
|
-
if (!page && pathname !== '/') {
|
|
73
|
-
navigate(ALL_NAVIGATION[0].groups[0].pages[0].href);
|
|
74
|
-
window.location.reload();
|
|
75
|
-
}
|
|
76
|
-
}, [routes, pathname]);
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
80
|
-
}, [pathname]);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (loading) return <div className="h-[25vh] flex grow"><Loading /></div>;
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<Router>
|
|
87
|
-
{routes.map(({ path, component: Component }) => (
|
|
88
|
-
<Route key={path} path={path} component={Component} />
|
|
89
|
-
))}
|
|
90
|
-
</Router>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Document } from "flexsearch";
|
|
2
|
-
|
|
3
|
-
export const searchIndex = new Document({
|
|
4
|
-
tokenize: 'full',
|
|
5
|
-
document: {
|
|
6
|
-
id: 'url',
|
|
7
|
-
index: 'content',
|
|
8
|
-
store: ['title', 'pageTitle'],
|
|
9
|
-
},
|
|
10
|
-
context: {
|
|
11
|
-
resolution: 9,
|
|
12
|
-
depth: 2,
|
|
13
|
-
bidirectional: true
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
export function addToSearchIndex(filename: string, data: any, content: string) {
|
|
18
|
-
searchIndex.add({
|
|
19
|
-
url: filename,
|
|
20
|
-
title: data.title || '',
|
|
21
|
-
content: content,
|
|
22
|
-
pageTitle: data.title
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function search(query: string, options = {}) {
|
|
27
|
-
if (!searchIndex) return [];
|
|
28
|
-
|
|
29
|
-
const result = searchIndex.search(query, {
|
|
30
|
-
...options,
|
|
31
|
-
enrich: true,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (result.length === 0) {
|
|
35
|
-
return [];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return result[0].result.map((item) => ({
|
|
39
|
-
url: item.id,
|
|
40
|
-
title: item.doc?.title || '',
|
|
41
|
-
pageTitle: item.doc?.pageTitle || '',
|
|
42
|
-
}));
|
|
43
|
-
}
|