@clidey/dory 0.29.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 (96) 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/html-metadata-injector.ts +0 -76
  77. package/src/plugins/llm-txt-dev-server.ts +0 -183
  78. package/src/plugins/llm-txt-generator.ts +0 -186
  79. package/src/plugins/sanitize.ts +0 -163
  80. package/src/plugins/unified-mdx-plugin.ts +0 -35
  81. package/src/theme.scss +0 -72
  82. package/src/types/config.ts +0 -47
  83. package/src/ui/header.tsx +0 -91
  84. package/src/ui/layout.tsx +0 -226
  85. package/src/ui/mobile-navigation.tsx +0 -64
  86. package/src/ui/prev-next-link.tsx +0 -74
  87. package/src/ui/table-of-content.tsx +0 -166
  88. package/src/utils/colors.ts +0 -54
  89. package/src/utils/fonts.ts +0 -23
  90. package/src/utils/functions.ts +0 -12
  91. package/src/vite-env.d.ts +0 -1
  92. package/tsconfig.app.json +0 -33
  93. package/tsconfig.bin.json +0 -24
  94. package/tsconfig.json +0 -7
  95. package/tsconfig.node.json +0 -26
  96. package/vite.config.ts +0 -43
@@ -1,186 +0,0 @@
1
- import type { Plugin } from 'vite';
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- interface LlmTxtGeneratorOptions {
6
- docsDir?: string;
7
- outputPath?: string;
8
- }
9
-
10
- // Read dory.json to get navigation order (flat list, in order)
11
- function getNavigationOrder(docsDir: string): string[] {
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: string[] = [];
16
-
17
- function walkPages(pages: any[]) {
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: string) {
43
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n/);
44
- if (!frontmatterMatch) return { data: {}, content };
45
-
46
- const frontmatter = frontmatterMatch[1];
47
- const data: Record<string, string> = {};
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
- // Function to recursively find all MDX files and map by relative path (without .mdx)
61
- function findMdxFilesMap(dir: string, baseDir: string): Record<string, string> {
62
- const files: Record<string, string> = {};
63
- const items = fs.readdirSync(dir, { withFileTypes: true });
64
-
65
- for (const item of items) {
66
- const fullPath = path.join(dir, item.name);
67
- if (item.isDirectory()) {
68
- Object.assign(files, findMdxFilesMap(fullPath, baseDir));
69
- } else if (item.isFile() && item.name.endsWith('.mdx')) {
70
- // Key: relative path without .mdx, with forward slashes
71
- let rel = path.relative(baseDir, fullPath).replace(/\\/g, '/');
72
- if (rel.endsWith('.mdx')) rel = rel.slice(0, -4);
73
- files[rel] = fullPath;
74
- }
75
- }
76
-
77
- return files;
78
- }
79
-
80
- // Function to clean MDX content for LLM consumption
81
- function cleanMdxContent(content: string): string {
82
- // Remove JSX components but keep their content
83
- let cleaned = content
84
- // Remove JSX opening/closing tags but keep content
85
- .replace(/<(\w+)([^>]*?)>/g, '')
86
- .replace(/<\/\w+>/g, '')
87
- // Remove code block language specifiers
88
- .replace(/```(\w+)/g, '```')
89
- // Remove HTML comments
90
- .replace(/<!--[\s\S]*?-->/g, '')
91
- // Remove excessive whitespace
92
- .replace(/\n\s*\n\s*\n/g, '\n\n')
93
- // Remove leading/trailing whitespace
94
- .trim();
95
-
96
- return cleaned;
97
- }
98
-
99
- // Function to generate LLM text content in dory.json order, then append any extra files
100
- function generateLlmContent(docsDir: string): string {
101
- const navOrder = getNavigationOrder(docsDir);
102
- const mdxFilesMap = findMdxFilesMap(docsDir, docsDir);
103
- const usedKeys = new Set<string>();
104
- let aggregatedContent = '';
105
-
106
- // Output files in navigation order, in order as in dory.json
107
- for (const navKey of navOrder) {
108
- // Try both with and without "index"
109
- let fileKey = navKey;
110
- if (mdxFilesMap[fileKey]) {
111
- usedKeys.add(fileKey);
112
- } else if (mdxFilesMap[`${fileKey}/index`]) {
113
- fileKey = `${fileKey}/index`;
114
- usedKeys.add(fileKey);
115
- } else {
116
- continue;
117
- }
118
- const filePath = mdxFilesMap[fileKey];
119
- const rawContent = fs.readFileSync(filePath, 'utf-8');
120
- const { data, content } = parseFrontmatter(rawContent);
121
- const cleanedContent = cleanMdxContent(content);
122
-
123
- // Add file header
124
- aggregatedContent += `\n# ${fileKey}.mdx\n`;
125
-
126
- // Add title if available
127
- if (data.title) {
128
- aggregatedContent += `## ${data.title}\n\n`;
129
- }
130
-
131
- // Add description if available
132
- if (data.description) {
133
- aggregatedContent += `${data.description}\n\n`;
134
- }
135
-
136
- // Add cleaned content
137
- aggregatedContent += cleanedContent + '\n\n';
138
- aggregatedContent += '---\n\n';
139
- }
140
-
141
- // Output any extra files not in navigation order, sorted
142
- const extraKeys = Object.keys(mdxFilesMap).filter(k => !usedKeys.has(k)).sort();
143
- for (const fileKey of extraKeys) {
144
- const filePath = mdxFilesMap[fileKey];
145
- const rawContent = fs.readFileSync(filePath, 'utf-8');
146
- const { data, content } = parseFrontmatter(rawContent);
147
- const cleanedContent = cleanMdxContent(content);
148
-
149
- aggregatedContent += `\n# ${fileKey}.mdx\n`;
150
- if (data.title) {
151
- aggregatedContent += `## ${data.title}\n\n`;
152
- }
153
- if (data.description) {
154
- aggregatedContent += `${data.description}\n\n`;
155
- }
156
- aggregatedContent += cleanedContent + '\n\n';
157
- aggregatedContent += '---\n\n';
158
- }
159
-
160
- // Remove leading newlines for the very first file, so the first file is at the very top
161
- return aggregatedContent.replace(/^\n+/, '').trim();
162
- }
163
-
164
- export function llmTxtGenerator(options: LlmTxtGeneratorOptions = {}): Plugin {
165
- return {
166
- name: 'llm-txt-generator',
167
- generateBundle() {
168
- const docsDir = options.docsDir || path.join(process.cwd(), 'docs');
169
- const aggregatedContent = generateLlmContent(docsDir);
170
-
171
- this.emitFile({
172
- type: 'asset',
173
- fileName: 'llms.txt',
174
- source: aggregatedContent
175
- });
176
-
177
- // For logging, count the number of MDX files processed
178
- const mdxFilesMap = findMdxFilesMap(docsDir, docsDir);
179
- const mdxFilesCount = Object.keys(mdxFilesMap).length;
180
-
181
- console.log(`✅ Generated LLM text file`);
182
- console.log(`📄 Processed ${mdxFilesCount} MDX files`);
183
- console.log(`📝 Total content size: ${aggregatedContent.length} characters`);
184
- }
185
- };
186
- }
@@ -1,163 +0,0 @@
1
- import { visit } from 'unist-util-visit';
2
-
3
- // This plugin replaces mdxTextExpression nodes with inlineCode for safety.
4
- export const remarkSafeVars = () => {
5
- return (tree: any) => {
6
- visit(tree, 'mdxTextExpression', (node: any, index: number | undefined, parent: any) => {
7
- if (parent && typeof index === 'number') {
8
- parent.children[index] = {
9
- type: 'inlineCode',
10
- value: node.value
11
- };
12
- }
13
- });
14
- };
15
- };
16
-
17
- const KNOWN_COMPONENTS = [
18
- // Text components
19
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'Row', 'Col', 'a', 'img',
20
- // Code components
21
- 'code', 'CodeGroup', 'pre',
22
- // UI components
23
- 'Accordion', 'AccordionGroup', 'Icon', 'Card', 'CardGroup',
24
- 'Callout', 'Note', 'Warning', 'Info', 'Tip', 'Check',
25
- 'Latex', 'API', 'Expandable', 'ResponseField', 'Properties', 'Property',
26
- 'Steps', 'Step', 'table', 'th', 'td', 'ul', 'ol', 'li',
27
- 'APIPlayground', 'WebSocketPlayground', 'AsyncAPI', 'Source',
28
- // Common HTML elements that should be preserved
29
- 'div', 'span', 'br', 'hr', 'strong', 'em', 'b', 'i', 'u', 's',
30
- 'blockquote', 'nav', 'section', 'article', 'header', 'footer', 'main',
31
- 'aside', 'figure', 'figcaption', 'details', 'summary', 'mark', 'small',
32
- 'del', 'ins', 'sub', 'sup', 'kbd', 'samp', 'var', 'time', 'abbr',
33
- 'address', 'cite', 'q', 'dfn'
34
- ];
35
-
36
- // Helper: returns true if the given code string is inside a code block (```)
37
- function isInsideCodeBlock(code: string, idx: number): boolean {
38
- // Count the number of ``` before idx
39
- const before = code.slice(0, idx);
40
- const codeBlockMatches = before.match(/```/g);
41
- // Odd number of ``` means inside a code block
42
- return codeBlockMatches ? codeBlockMatches.length % 2 === 1 : false;
43
- }
44
-
45
- // Helper: checks if a known component tag has a matching closing tag
46
- function hasMatchingClosingTag(code: string, tagName: string, openingTagOffset: number): boolean {
47
- // Simple approach: look for the next occurrence of either an opening or closing tag of the same type
48
- const afterOpening = code.slice(openingTagOffset + `<${tagName}>`.length);
49
-
50
- // Find the first occurrence of either opening or closing tag
51
- const nextOpeningMatch = afterOpening.match(new RegExp(`<${tagName}>`, 'i'));
52
- const nextClosingMatch = afterOpening.match(new RegExp(`</${tagName}>`, 'i'));
53
-
54
- // If no closing tag found at all
55
- if (!nextClosingMatch) {
56
- return false;
57
- }
58
-
59
- const nextOpeningIndex = nextOpeningMatch ? nextOpeningMatch.index! : Infinity;
60
- const nextClosingIndex = nextClosingMatch ? nextClosingMatch.index! : Infinity;
61
-
62
- // If the closing tag comes before any other opening tag, this tag is properly closed
63
- if (nextClosingIndex < nextOpeningIndex) {
64
- // Check if the closing tag is inside a code block
65
- const closingTagOffset = openingTagOffset + `<${tagName}>`.length + nextClosingIndex;
66
- if (isInsideCodeBlock(code, closingTagOffset)) {
67
- return false;
68
- }
69
- return true;
70
- }
71
-
72
- return false; // Another opening tag comes before the closing tag, so this one is not properly closed
73
- }
74
-
75
- // Allow any <[a-z][a-z0-9]*> tag inside code blocks to be left as-is (not parsed as JSX).
76
- export function preprocessMdxTags() {
77
- return {
78
- name: 'preprocess-mdx-tags',
79
- enforce: 'pre' as const,
80
- transform(code: string, id: string) {
81
- if (!id.endsWith('.mdx')) return;
82
-
83
- // Remove leading whitespace from code fence blocks
84
- let processed = code.replace(/^(\s+)(```\w*)/gm, '$2');
85
-
86
- // Handle self-closing tags first (like <VideoPlayer />)
87
- processed = processed.replace(/<([A-Za-z][A-Za-z0-9]*)[^>]*\/>/g, (match, tag, offset) => {
88
- // If inside a code block, do not touch
89
- if (isInsideCodeBlock(processed, offset)) {
90
- return match;
91
- }
92
-
93
- // If unknown component, treat as text
94
- if (!KNOWN_COMPONENTS.includes(tag)) {
95
- return `\`${match}\``;
96
- }
97
- return match;
98
- });
99
-
100
- // Only replace unrecognized <someTag> with `"<someTag>"` if NOT inside a code block
101
- // This regex finds all <tag> occurrences (fixed to handle uppercase)
102
- processed = processed.replace(/<([A-Za-z][A-Za-z0-9]*)>/g, (match, tag, offset) => {
103
- // If inside a code block, do not touch
104
- if (isInsideCodeBlock(processed, offset)) {
105
- return match;
106
- }
107
-
108
- // If known component, check if it has a closing tag
109
- if (KNOWN_COMPONENTS.includes(tag)) {
110
- // If it doesn't have a matching closing tag, treat it as text
111
- if (!hasMatchingClosingTag(processed, tag, offset)) {
112
- return `\`<${tag}>\``;
113
- }
114
- // Otherwise, leave as-is for proper JSX parsing
115
- return match;
116
- }
117
- // For unknown components, always treat as text (wrap in backticks)
118
- // If already inside backticks, do not wrap again
119
- const before = processed.slice(0, offset);
120
- const backtickMatches = before.match(/`+/g);
121
- const insideBackticks = backtickMatches ? backtickMatches.reduce((acc, s) => acc + s.length, 0) % 2 === 1 : false;
122
- if (insideBackticks) {
123
- return match;
124
- }
125
- return `\`<${tag}>\``;
126
- });
127
-
128
- // This regex finds all <tag> and </tag> occurrences (fixed to handle uppercase)
129
- processed = processed.replace(/<\/?([A-Za-z][A-Za-z0-9]*)>/g, (match, tag, offset) => {
130
- // If inside a code block, do not touch
131
- if (isInsideCodeBlock(processed, offset)) {
132
- return match;
133
- }
134
-
135
- // If known component, check if it has a closing tag (only for opening tags)
136
- if (KNOWN_COMPONENTS.includes(tag)) {
137
- // Only check for matching closing tag for opening tags
138
- if (!match.startsWith('</')) {
139
- if (!hasMatchingClosingTag(processed, tag, offset)) {
140
- return `\`${match}\``;
141
- }
142
- }
143
- // Otherwise, leave as-is for proper JSX parsing
144
- return match;
145
- }
146
- // For unknown components, always treat as text (wrap in backticks)
147
- // If already inside backticks, do not wrap again
148
- const before = processed.slice(0, offset);
149
- const backtickMatches = before.match(/`+/g);
150
- const insideBackticks = backtickMatches ? backtickMatches.reduce((acc, s) => acc + s.length, 0) % 2 === 1 : false;
151
- if (insideBackticks) {
152
- return match;
153
- }
154
- return `\`${match}\``;
155
- });
156
-
157
- return {
158
- code: processed,
159
- map: null,
160
- };
161
- },
162
- };
163
- }
@@ -1,35 +0,0 @@
1
- import type { Plugin } from 'vite';
2
- import { preprocessMdxContent } from '../mdx/processor';
3
-
4
- /**
5
- * Unified MDX preprocessing plugin for Vite.
6
- * This plugin ensures that ALL MDX preprocessing goes through the same pipeline
7
- * as the CLI verify:content command.
8
- *
9
- * This is the SINGLE SOURCE OF TRUTH for MDX preprocessing.
10
- * Any changes to MDX processing should be made in src/mdx/processor.ts
11
- */
12
- export function unifiedMdxPreprocessor(): Plugin {
13
- return {
14
- name: 'unified-mdx-preprocessor',
15
- enforce: 'pre', // Must run before the MDX plugin
16
- async transform(code: string, id: string) {
17
- if (!id.endsWith('.mdx')) return;
18
-
19
- // Process through the unified preprocessing pipeline
20
- const result = await preprocessMdxContent(code, id);
21
-
22
- if (!result.success) {
23
- // Throw the error to stop the build
24
- throw result.error;
25
- }
26
-
27
- // Return the preprocessed content
28
- // The actual MDX compilation will be handled by @mdx-js/rollup plugin
29
- return {
30
- code: result.processedContent || code,
31
- map: null
32
- };
33
- }
34
- };
35
- }
package/src/theme.scss DELETED
@@ -1,72 +0,0 @@
1
- :root {
2
- font-synthesis: none;
3
- text-rendering: optimizeLegibility;
4
- -webkit-font-smoothing: antialiased;
5
- -moz-osx-font-smoothing: grayscale;
6
-
7
- // Brand colors (dynamically set from dory.json via loadColors())
8
- // These variables are set at runtime:
9
- // --brand-primary: primary color
10
- // --brand-light: light mode accent
11
- // --brand-dark: dark mode accent
12
- // --brand-foreground: legacy/fallback (set to primary)
13
-
14
- // Default fallback colors (will be overridden by loadColors())
15
- --brand-foreground: #0ea5e9;
16
- --brand-primary: #0ea5e9;
17
- --brand-light: #38bdf8;
18
- --brand-dark: #0284c7;
19
-
20
- // ShadCN/clidey/ux compatible primary color
21
- // Using light color for light mode
22
- --primary: var(--brand-light);
23
- --primary-rgb: var(--brand-light-rgb);
24
-
25
- --ring: rgba(0, 0, 0, 0.1);
26
- }
27
-
28
- // Dark mode overrides
29
- :root.dark {
30
- // Use dark color for dark mode
31
- --primary: var(--brand-dark);
32
- --primary-rgb: var(--brand-dark-rgb);
33
-
34
- --ring: rgba(255, 255, 255, 0.1);
35
- }
36
-
37
- article h2:first-of-type {
38
- display: none;
39
- }
40
-
41
- article hr:first-of-type {
42
- display: none;
43
- }
44
-
45
- article img {
46
- margin-top: 1rem;
47
- margin-bottom: 1rem;
48
- }
49
-
50
- [data-testid="header-actions"] button,
51
- [data-slot="dropdown-menu-content"] {
52
- border-color: var(--ring);
53
- }
54
-
55
- [data-slot="search-input-wrapper"] {
56
- font-size: 12px;
57
- outline: none;
58
- }
59
-
60
- [data-slot="search-input"] {
61
- font-size: 12px;
62
- }
63
-
64
- [data-slot="search-input-wrapper"]:focus-within {
65
- outline: none !important;
66
- }
67
-
68
- [role="dialog"] [data-slot="search-input-wrapper"]:focus-within {
69
- outline: none !important;
70
- border: 0px;
71
- box-shadow: none !important;
72
- }
@@ -1,47 +0,0 @@
1
- /**
2
- * TypeScript interfaces for dory.json configuration
3
- */
4
-
5
- export interface DoryColors {
6
- primary: string;
7
- light: string;
8
- dark: string;
9
- }
10
-
11
- export interface DoryFonts {
12
- family: string;
13
- source: string;
14
- format: string;
15
- }
16
-
17
- export interface DoryLogo {
18
- light: string;
19
- dark: string;
20
- }
21
-
22
- export interface DoryNavigationPage {
23
- [key: string]: string;
24
- }
25
-
26
- export interface DoryNavigationGroup {
27
- group: string;
28
- pages: string[];
29
- }
30
-
31
- export interface DoryNavigationTab {
32
- tab: string;
33
- groups: DoryNavigationGroup[];
34
- }
35
-
36
- export interface DoryNavigation {
37
- tabs: DoryNavigationTab[];
38
- }
39
-
40
- export interface DoryConfig {
41
- name: string;
42
- favicon: string;
43
- colors?: DoryColors;
44
- fonts?: DoryFonts;
45
- navigation: DoryNavigation;
46
- logo: DoryLogo;
47
- }
package/src/ui/header.tsx DELETED
@@ -1,91 +0,0 @@
1
- import classNames from 'classnames';
2
- import { useEffect, useMemo, useState } from 'preact/hooks';
3
- import { Link } from 'wouter-preact';
4
- import { usePathname } from 'wouter-preact/use-browser-location';
5
- import docsConfig from '../../docs/dory.json' with { type: 'json' };
6
- import type { DoryConfig } from '../types/config';
7
- import { MobileSearch, Search } from '../components/search';
8
- import { ALL_NAVIGATION } from '../components/store';
9
- import { MobileNavigation } from './mobile-navigation';
10
- import { ModeToggle } from '@clidey/ux';
11
-
12
- const config = docsConfig as DoryConfig;
13
-
14
- function TopLevelNavItem({ href, children, isActive }: { href: string; children: string; isActive?: boolean }) {
15
- return (
16
- <li>
17
- <Link
18
- to={href}
19
- className={classNames(
20
- "text-xs transition hover:opacity-80",
21
- {
22
- "text-brand-foreground": isActive,
23
- },
24
- )}
25
- >
26
- {children}
27
- </Link>
28
- </li>
29
- );
30
- }
31
-
32
- export function Header({ className }: { className?: string }) {
33
- const [isScrolled, setIsScrolled] = useState(false);
34
- const pathname = usePathname();
35
-
36
- const { light, dark } = useMemo(() => {
37
- return { light: config.logo.light, dark: config.logo.dark };
38
- }, []);
39
-
40
- useEffect(() => {
41
- function onScroll() {
42
- setIsScrolled(window.scrollY > 0);
43
- }
44
- onScroll();
45
- window.addEventListener('scroll', onScroll, { passive: true });
46
- return () => {
47
- window.removeEventListener('scroll', onScroll);
48
- }
49
- }, []);
50
-
51
- return (
52
- <header
53
- className={classNames(
54
- 'sticky top-0 z-50 flex flex-none flex-wrap items-center justify-between bg-white px-4 py-5 shadow-md shadow-gray-900/5 sm:px-6 lg:px-8 dark:shadow-none',
55
- className,
56
- {
57
- 'dark:bg-gray-900/95 dark:backdrop-blur-sm dark:[@supports(backdrop-filter:blur(0))]:bg-neutral-900/75': isScrolled,
58
- 'dark:bg-transparent': !isScrolled,
59
- },
60
- )}
61
- >
62
- <div className="relative flex justify-between gap-6 sm:gap-8 md:grow items-center w-full px-4">
63
- <div className="flex items-center grow gap-2">
64
- <img src={`/${light}`} alt="logo" className="w-8 hidden dark:block" />
65
- <img src={`/${dark}`} alt="logo" className="w-8 block dark:hidden" />
66
- <h1 className="text-base lg:text-2xl font-bold">{config.name}</h1>
67
- </div>
68
- <nav className="hidden md:block">
69
- <ul role="list" className="flex items-center gap-8">
70
- {ALL_NAVIGATION.map((tab) => (
71
- <TopLevelNavItem
72
- key={tab.title}
73
- href={tab.groups[0].pages[0].href}
74
- isActive={pathname.startsWith(`/${tab.groups[0].pages[0].href.split('/')[1]}`)}
75
- >
76
- {tab.title}
77
- </TopLevelNavItem>
78
- ))}
79
- </ul>
80
- </nav>
81
- <div className="hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-white/15" />
82
- <div className="flex items-center gap-3" data-testid="header-actions">
83
- <MobileSearch />
84
- <Search />
85
- <ModeToggle data-testid="mode-toggle" />
86
- <MobileNavigation />
87
- </div>
88
- </div>
89
- </header>
90
- );
91
- }