@opendocsdev/cli 0.2.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 (78) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +300 -0
  3. package/dist/bin/opendocs.js +712 -0
  4. package/dist/bin/opendocs.js.map +1 -0
  5. package/dist/templates/api-reference.mdx +308 -0
  6. package/dist/templates/components.mdx +286 -0
  7. package/dist/templates/configuration.mdx +190 -0
  8. package/dist/templates/docs.json +27 -0
  9. package/dist/templates/introduction.mdx +25 -0
  10. package/dist/templates/logo.svg +4 -0
  11. package/dist/templates/quickstart.mdx +59 -0
  12. package/dist/templates/writing-content.mdx +236 -0
  13. package/package.json +92 -0
  14. package/src/engine/astro.config.ts +75 -0
  15. package/src/engine/src/components/Analytics.astro +57 -0
  16. package/src/engine/src/components/ApiPlayground.astro +24 -0
  17. package/src/engine/src/components/Callout.astro +66 -0
  18. package/src/engine/src/components/Card.astro +75 -0
  19. package/src/engine/src/components/CardGroup.astro +29 -0
  20. package/src/engine/src/components/CodeGroup.astro +231 -0
  21. package/src/engine/src/components/CopyButton.astro +179 -0
  22. package/src/engine/src/components/Steps.astro +27 -0
  23. package/src/engine/src/components/Tab.astro +21 -0
  24. package/src/engine/src/components/TableOfContents.astro +119 -0
  25. package/src/engine/src/components/Tabs.astro +135 -0
  26. package/src/engine/src/components/index.ts +107 -0
  27. package/src/engine/src/components/react/ApiPlayground/AuthSection.tsx +91 -0
  28. package/src/engine/src/components/react/ApiPlayground/CodeBlock.tsx +66 -0
  29. package/src/engine/src/components/react/ApiPlayground/CodeSnippets.tsx +66 -0
  30. package/src/engine/src/components/react/ApiPlayground/CollapsibleSection.tsx +26 -0
  31. package/src/engine/src/components/react/ApiPlayground/KeyValueEditor.tsx +58 -0
  32. package/src/engine/src/components/react/ApiPlayground/ResponseDisplay.tsx +109 -0
  33. package/src/engine/src/components/react/ApiPlayground/Spinner.tsx +6 -0
  34. package/src/engine/src/components/react/ApiPlayground/constants.ts +16 -0
  35. package/src/engine/src/components/react/ApiPlayground/generators.test.ts +130 -0
  36. package/src/engine/src/components/react/ApiPlayground/generators.ts +75 -0
  37. package/src/engine/src/components/react/ApiPlayground/index.tsx +490 -0
  38. package/src/engine/src/components/react/ApiPlayground/types.ts +35 -0
  39. package/src/engine/src/components/react/Callout.tsx +54 -0
  40. package/src/engine/src/components/react/Card.tsx +48 -0
  41. package/src/engine/src/components/react/CardGroup.tsx +24 -0
  42. package/src/engine/src/components/react/FeedbackWidget.tsx +166 -0
  43. package/src/engine/src/components/react/GitHubLink.tsx +28 -0
  44. package/src/engine/src/components/react/NavigationCard.tsx +53 -0
  45. package/src/engine/src/components/react/PageActions.tsx +124 -0
  46. package/src/engine/src/components/react/PageFooter.tsx +91 -0
  47. package/src/engine/src/components/react/SearchModal.tsx +358 -0
  48. package/src/engine/src/components/react/SearchProvider.tsx +37 -0
  49. package/src/engine/src/components/react/Sidebar.tsx +369 -0
  50. package/src/engine/src/components/react/SidebarSearchTrigger.tsx +57 -0
  51. package/src/engine/src/components/react/Steps.tsx +25 -0
  52. package/src/engine/src/components/react/ThemeToggle.tsx +72 -0
  53. package/src/engine/src/components/react/index.ts +14 -0
  54. package/src/engine/src/env.d.ts +10 -0
  55. package/src/engine/src/layouts/DocsLayout.astro +357 -0
  56. package/src/engine/src/lib/__tests__/markdown.test.ts +124 -0
  57. package/src/engine/src/lib/__tests__/mdx-loader.test.ts +205 -0
  58. package/src/engine/src/lib/config.ts +79 -0
  59. package/src/engine/src/lib/markdown.ts +54 -0
  60. package/src/engine/src/lib/mdx-loader.ts +143 -0
  61. package/src/engine/src/lib/mdx-utils.ts +72 -0
  62. package/src/engine/src/lib/remark-opendocs.ts +195 -0
  63. package/src/engine/src/lib/utils.ts +221 -0
  64. package/src/engine/src/pages/[...slug].astro +115 -0
  65. package/src/engine/src/pages/index.astro +71 -0
  66. package/src/engine/src/scripts/mobile-sidebar.ts +56 -0
  67. package/src/engine/src/scripts/theme-init.ts +25 -0
  68. package/src/engine/src/styles/global.css +703 -0
  69. package/src/engine/tailwind.config.mjs +60 -0
  70. package/src/engine/tsconfig.json +15 -0
  71. package/src/templates/api-reference.mdx +308 -0
  72. package/src/templates/components.mdx +286 -0
  73. package/src/templates/configuration.mdx +190 -0
  74. package/src/templates/docs.json +27 -0
  75. package/src/templates/introduction.mdx +25 -0
  76. package/src/templates/logo.svg +4 -0
  77. package/src/templates/quickstart.mdx +59 -0
  78. package/src/templates/writing-content.mdx +236 -0
@@ -0,0 +1,369 @@
1
+ import { useState, useEffect } from "react";
2
+ import { ChevronRight, ChevronDown } from "lucide-react";
3
+ import {
4
+ cn,
5
+ pageToUrl,
6
+ formatPageName,
7
+ isPageActive,
8
+ isPageWithChildren as isPageWithChildrenUtil,
9
+ isNestedGroup as isNestedGroupUtil,
10
+ type PageWithChildren,
11
+ type NestedGroup,
12
+ type NavigationGroup,
13
+ } from "../../lib/utils";
14
+ import { ThemeToggle } from "./ThemeToggle";
15
+ import { GitHubLink } from "./GitHubLink";
16
+ import { SidebarSearchTrigger } from "./SidebarSearchTrigger";
17
+
18
+ interface LogoConfig {
19
+ light?: string;
20
+ dark?: string;
21
+ }
22
+
23
+ interface SidebarProps {
24
+ siteName: string;
25
+ navigation: NavigationGroup[];
26
+ logo?: string | LogoConfig;
27
+ currentPath: string;
28
+ githubUrl?: string;
29
+ }
30
+
31
+ // Check if any page in a group of children is active
32
+ function hasActiveChild(children: string[], currentPath: string): boolean {
33
+ return children.some((child) => isPageActive(child, currentPath));
34
+ }
35
+
36
+ // Get the page string from a PageItem (returns null for nested groups)
37
+ function getPageString(item: unknown): string | null {
38
+ if (typeof item === "string") return item;
39
+ if (isPageWithChildrenUtil(item)) return item.page;
40
+ return null;
41
+ }
42
+
43
+ // Check if any page in a nested group or its children is active
44
+ function hasActiveInGroup(pages: readonly unknown[], currentPath: string): boolean {
45
+ return pages.some((item) => {
46
+ if (typeof item === "string") {
47
+ return isPageActive(item, currentPath);
48
+ }
49
+ if (isPageWithChildrenUtil(item)) {
50
+ return (
51
+ isPageActive(item.page, currentPath) ||
52
+ hasActiveChild(item.children, currentPath)
53
+ );
54
+ }
55
+ if (isNestedGroupUtil(item)) {
56
+ return hasActiveInGroup(item.pages, currentPath);
57
+ }
58
+ return false;
59
+ });
60
+ }
61
+
62
+ interface CollapsibleNavItemProps {
63
+ item: PageWithChildren;
64
+ currentPath: string;
65
+ }
66
+
67
+ function CollapsibleNavItem({ item, currentPath }: CollapsibleNavItemProps) {
68
+ const isParentActive = isPageActive(item.page, currentPath);
69
+ const hasActiveChildPage = hasActiveChild(item.children, currentPath);
70
+
71
+ // Initialize expanded if this item or a child is active
72
+ const [isExpanded, setIsExpanded] = useState(isParentActive || hasActiveChildPage);
73
+
74
+ // Auto-expand when navigating to an active page
75
+ useEffect(() => {
76
+ if (isParentActive || hasActiveChildPage) {
77
+ setIsExpanded(true);
78
+ }
79
+ }, [isParentActive, hasActiveChildPage]);
80
+
81
+ return (
82
+ <li>
83
+ {/* Parent item with chevron */}
84
+ <a
85
+ href={pageToUrl(item.page)}
86
+ className={cn(
87
+ "group flex items-center gap-2 px-2 py-1.5 text-sm rounded-md transition-colors",
88
+ isParentActive
89
+ ? "bg-[var(--color-primary-light)] text-[var(--color-primary)] font-medium"
90
+ : "text-[var(--color-muted)] hover:bg-[var(--color-surface-sunken)] hover:text-[var(--color-foreground)]",
91
+ )}
92
+ >
93
+ <button
94
+ type="button"
95
+ onClick={(e) => {
96
+ e.preventDefault();
97
+ e.stopPropagation();
98
+ setIsExpanded(!isExpanded);
99
+ }}
100
+ className="flex items-center justify-center w-4 h-4 -ml-0.5"
101
+ aria-expanded={isExpanded}
102
+ aria-label={isExpanded ? "Collapse" : "Expand"}
103
+ >
104
+ <ChevronRight
105
+ className={cn(
106
+ "w-3 h-3 text-[var(--color-muted)] transition-transform duration-200",
107
+ isExpanded && "rotate-90",
108
+ )}
109
+ />
110
+ </button>
111
+ <span>{formatPageName(item.page)}</span>
112
+ </a>
113
+
114
+ {/* Children */}
115
+ <div
116
+ className={cn(
117
+ "overflow-hidden transition-all duration-200",
118
+ isExpanded ? "max-h-96 opacity-100" : "max-h-0 opacity-0",
119
+ )}
120
+ >
121
+ <ul className="mt-2 space-y-0.5">
122
+ {item.children.map((child) => {
123
+ const isActive = isPageActive(child, currentPath);
124
+ return (
125
+ <li key={child}>
126
+ <a
127
+ href={pageToUrl(child)}
128
+ className={cn(
129
+ "block py-1.5 pl-6 pr-2 text-sm rounded-r-md transition-colors border-l-2 -ml-px",
130
+ isActive
131
+ ? "border-[var(--color-primary)] bg-[var(--color-primary-light)] text-[var(--color-primary)] font-medium"
132
+ : "border-transparent text-[var(--color-muted)] hover:text-[var(--color-foreground)] hover:border-[var(--color-border)]",
133
+ )}
134
+ >
135
+ {formatPageName(child)}
136
+ </a>
137
+ </li>
138
+ );
139
+ })}
140
+ </ul>
141
+ </div>
142
+ </li>
143
+ );
144
+ }
145
+
146
+ // Component for Mintlify-style nested groups
147
+ interface NestedGroupNavItemProps {
148
+ item: NestedGroup;
149
+ currentPath: string;
150
+ }
151
+
152
+ function NestedGroupNavItem({ item, currentPath }: NestedGroupNavItemProps) {
153
+ const hasActivePage = hasActiveInGroup(item.pages, currentPath);
154
+
155
+ // Initialize expanded if any page in group is active
156
+ const [isExpanded, setIsExpanded] = useState(hasActivePage);
157
+
158
+ // Auto-expand when navigating to an active page
159
+ useEffect(() => {
160
+ if (hasActivePage) {
161
+ setIsExpanded(true);
162
+ }
163
+ }, [hasActivePage]);
164
+
165
+ return (
166
+ <li>
167
+ {/* Group header with chevron on far right */}
168
+ <button
169
+ type="button"
170
+ onClick={() => setIsExpanded(!isExpanded)}
171
+ className="group flex items-center justify-between w-full px-2 py-1.5 text-sm rounded-md transition-colors text-[var(--color-muted)] hover:bg-[var(--color-surface-sunken)] hover:text-[var(--color-foreground)]"
172
+ aria-expanded={isExpanded}
173
+ >
174
+ <span className="font-medium">{item.group}</span>
175
+ <ChevronDown
176
+ className={cn(
177
+ "w-4 h-4 transition-transform duration-200",
178
+ isExpanded && "rotate-180",
179
+ )}
180
+ />
181
+ </button>
182
+
183
+ {/* Nested pages */}
184
+ <div
185
+ className={cn(
186
+ "overflow-hidden transition-all duration-200",
187
+ isExpanded ? "max-h-[500px] opacity-100" : "max-h-0 opacity-0",
188
+ )}
189
+ >
190
+ <ul className="mt-2 space-y-0.5">
191
+ {item.pages.map((nestedItem, index) => {
192
+ const pageString = getPageString(nestedItem);
193
+
194
+ // Handle nested groups recursively
195
+ if (isNestedGroupUtil(nestedItem)) {
196
+ return (
197
+ <NestedGroupNavItem
198
+ key={`nested-${nestedItem.group}-${index}`}
199
+ item={nestedItem}
200
+ currentPath={currentPath}
201
+ />
202
+ );
203
+ }
204
+
205
+ // Handle legacy page with children
206
+ if (isPageWithChildrenUtil(nestedItem)) {
207
+ return (
208
+ <CollapsibleNavItem
209
+ key={pageString}
210
+ item={nestedItem}
211
+ currentPath={currentPath}
212
+ />
213
+ );
214
+ }
215
+
216
+ // Handle simple string page
217
+ const isActive = isPageActive(pageString as string, currentPath);
218
+ return (
219
+ <li key={pageString}>
220
+ <a
221
+ href={pageToUrl(pageString as string)}
222
+ className={cn(
223
+ "block py-1.5 pl-6 pr-2 text-sm rounded-r-md transition-colors border-l-2 -ml-px",
224
+ isActive
225
+ ? "border-[var(--color-primary)] bg-[var(--color-primary-light)] text-[var(--color-primary)] font-medium"
226
+ : "border-transparent text-[var(--color-muted)] hover:text-[var(--color-foreground)] hover:border-[var(--color-border)]",
227
+ )}
228
+ >
229
+ {formatPageName(pageString as string)}
230
+ </a>
231
+ </li>
232
+ );
233
+ })}
234
+ </ul>
235
+ </div>
236
+ </li>
237
+ );
238
+ }
239
+
240
+ // Logo component that handles theme switching with CSS instead of JS state
241
+ function Logo({ logo, siteName }: { logo?: string | LogoConfig; siteName: string }) {
242
+ if (!logo) {
243
+ return (
244
+ <a
245
+ href="/"
246
+ className="text-lg font-semibold text-[var(--color-foreground)] hover:text-[var(--color-muted)] transition-colors"
247
+ >
248
+ {siteName}
249
+ </a>
250
+ );
251
+ }
252
+
253
+ // String logo - no theme switching needed
254
+ if (typeof logo === "string") {
255
+ return (
256
+ <a href="/" className="flex items-center gap-2">
257
+ <img src={logo} alt={siteName} className="h-8 w-auto" />
258
+ </a>
259
+ );
260
+ }
261
+
262
+ // Object logo with light/dark variants - use CSS for theme switching
263
+ const { light, dark } = logo;
264
+
265
+ if (dark) {
266
+ return (
267
+ <a href="/" className="flex items-center gap-2">
268
+ <img src={light} alt={siteName} className="h-8 w-auto dark:hidden" />
269
+ <img src={dark} alt={siteName} className="h-8 w-auto hidden dark:block" />
270
+ </a>
271
+ );
272
+ }
273
+
274
+ // Only light logo provided
275
+ return (
276
+ <a href="/" className="flex items-center gap-2">
277
+ <img src={light} alt={siteName} className="h-8 w-auto" />
278
+ </a>
279
+ );
280
+ }
281
+
282
+ export function Sidebar({
283
+ siteName,
284
+ navigation,
285
+ logo,
286
+ currentPath,
287
+ githubUrl,
288
+ }: SidebarProps) {
289
+ // No more isDark state or MutationObserver - CSS handles theme switching
290
+
291
+ return (
292
+ <div className="flex flex-col h-full">
293
+ {/* Logo/Site name + Theme toggle */}
294
+ <div className="flex items-center justify-between h-16 px-4">
295
+ <Logo logo={logo} siteName={siteName} />
296
+ <ThemeToggle />
297
+ </div>
298
+
299
+ {/* Search trigger */}
300
+ <div className="px-4 py-3">
301
+ <SidebarSearchTrigger />
302
+ </div>
303
+
304
+ {/* Navigation */}
305
+ <nav className="sidebar-nav flex-1 overflow-y-auto px-4 py-4">
306
+ {navigation.map((group) => (
307
+ <div key={group.group} className="mb-6">
308
+ <h3 className="mb-2 px-2 text-xs font-bold uppercase tracking-wider text-[var(--color-foreground)]">
309
+ {group.group}
310
+ </h3>
311
+ <ul className="space-y-0.5">
312
+ {group.pages.map((item, index) => {
313
+ const pageString = getPageString(item);
314
+
315
+ // Handle Mintlify-style nested groups
316
+ if (isNestedGroupUtil(item)) {
317
+ return (
318
+ <NestedGroupNavItem
319
+ key={`group-${item.group}-${index}`}
320
+ item={item}
321
+ currentPath={currentPath}
322
+ />
323
+ );
324
+ }
325
+
326
+ // Handle legacy page with children
327
+ if (isPageWithChildrenUtil(item)) {
328
+ return (
329
+ <CollapsibleNavItem
330
+ key={pageString}
331
+ item={item}
332
+ currentPath={currentPath}
333
+ />
334
+ );
335
+ }
336
+
337
+ // Handle simple string page
338
+ return (
339
+ <li key={pageString}>
340
+ <a
341
+ href={pageToUrl(pageString as string)}
342
+ className={cn(
343
+ "block px-2 py-1.5 text-sm rounded-md transition-colors",
344
+ isPageActive(pageString as string, currentPath)
345
+ ? "bg-[var(--color-primary-light)] text-[var(--color-primary)] font-medium"
346
+ : "text-[var(--color-muted)] hover:bg-[var(--color-surface-sunken)] hover:text-[var(--color-foreground)]",
347
+ )}
348
+ >
349
+ {formatPageName(pageString as string)}
350
+ </a>
351
+ </li>
352
+ );
353
+ })}
354
+ </ul>
355
+ </div>
356
+ ))}
357
+ </nav>
358
+
359
+ {/* Bottom section with GitHub link */}
360
+ {githubUrl && (
361
+ <div className="px-4 py-3">
362
+ <GitHubLink url={githubUrl} />
363
+ </div>
364
+ )}
365
+ </div>
366
+ );
367
+ }
368
+
369
+ export default Sidebar;
@@ -0,0 +1,57 @@
1
+ import { useState, useEffect } from "react";
2
+ import { Search } from "lucide-react";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ interface SidebarSearchTriggerProps {
6
+ className?: string;
7
+ }
8
+
9
+ export function SidebarSearchTrigger({ className }: SidebarSearchTriggerProps) {
10
+ // Start with null to indicate "not yet determined" - renders placeholder during SSR
11
+ const [isMac, setIsMac] = useState<boolean | null>(null);
12
+
13
+ useEffect(() => {
14
+ // Detect platform on client - runs once after hydration
15
+ const isMacOS =
16
+ navigator.userAgentData?.platform?.toLowerCase().includes("mac") ??
17
+ /mac/i.test(navigator.userAgent);
18
+ setIsMac(isMacOS);
19
+ }, []);
20
+
21
+ const handleClick = () => {
22
+ // Dispatch custom event that SearchProvider listens for
23
+ // This is appropriate for Astro islands architecture where React trees are separate
24
+ document.dispatchEvent(new CustomEvent("open-search"));
25
+ };
26
+
27
+ return (
28
+ <button
29
+ type="button"
30
+ onClick={handleClick}
31
+ className={cn(
32
+ "flex items-center w-full gap-2 px-3 py-2 text-sm rounded-lg transition-colors",
33
+ "text-[var(--color-muted)] bg-[var(--color-surface-sunken)]",
34
+ "border border-[var(--color-border)]",
35
+ "hover:border-[var(--color-border-muted)] hover:text-[var(--color-foreground)]",
36
+ className,
37
+ )}
38
+ aria-label="Search documentation"
39
+ >
40
+ <Search className="w-4 h-4 flex-shrink-0" aria-hidden="true" />
41
+ <span className="flex-1 text-left">Search...</span>
42
+ <kbd
43
+ className={cn(
44
+ "hidden sm:inline-flex items-center gap-0.5 px-1.5 py-0.5",
45
+ "text-xs font-medium text-[var(--color-muted-foreground)]",
46
+ "bg-[var(--color-surface)] border border-[var(--color-border)] rounded",
47
+ // Hide until we know the platform to prevent hydration mismatch flash
48
+ isMac === null && "invisible",
49
+ )}
50
+ >
51
+ <span className="text-xs">{isMac === false ? "Ctrl" : "⌘"}</span>K
52
+ </kbd>
53
+ </button>
54
+ );
55
+ }
56
+
57
+ export default SidebarSearchTrigger;
@@ -0,0 +1,25 @@
1
+ interface StepsProps {
2
+ title?: string;
3
+ children: React.ReactNode;
4
+ }
5
+
6
+ export function Steps({ title, children }: StepsProps) {
7
+ return (
8
+ <div className="steps my-8 not-prose">
9
+ {title && (
10
+ <h4 className="steps-title text-lg font-semibold text-[var(--color-foreground)] mb-4">
11
+ {title}
12
+ </h4>
13
+ )}
14
+ <div className="steps-list relative pl-12">
15
+ <div
16
+ className="steps-line absolute left-4 top-4 bottom-4 w-0.5 bg-[var(--color-border)]"
17
+ aria-hidden="true"
18
+ />
19
+ {children}
20
+ </div>
21
+ </div>
22
+ );
23
+ }
24
+
25
+ export default Steps;
@@ -0,0 +1,72 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Sun, Moon } from "lucide-react";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ interface ThemeToggleProps {
6
+ className?: string;
7
+ }
8
+
9
+ export function ThemeToggle({ className }: ThemeToggleProps) {
10
+ const [isDark, setIsDark] = useState(false);
11
+ const [mounted, setMounted] = useState(false);
12
+
13
+ useEffect(() => {
14
+ setMounted(true);
15
+ // Check initial theme
16
+ const isDarkMode = document.documentElement.classList.contains("dark");
17
+ setIsDark(isDarkMode);
18
+ }, []);
19
+
20
+ const toggleTheme = () => {
21
+ const newIsDark = !isDark;
22
+ setIsDark(newIsDark);
23
+
24
+ if (newIsDark) {
25
+ document.documentElement.classList.add("dark");
26
+ localStorage.setItem("theme", "dark");
27
+ } else {
28
+ document.documentElement.classList.remove("dark");
29
+ localStorage.setItem("theme", "light");
30
+ }
31
+ };
32
+
33
+ // Prevent hydration mismatch by showing a placeholder until mounted
34
+ if (!mounted) {
35
+ return (
36
+ <button
37
+ type="button"
38
+ className={cn(
39
+ "flex items-center justify-center w-9 h-9 rounded-lg transition-colors",
40
+ "text-[var(--color-muted)] hover:text-[var(--color-foreground)]",
41
+ "hover:bg-[var(--color-surface-sunken)]",
42
+ className
43
+ )}
44
+ aria-label="Toggle theme"
45
+ >
46
+ <span className="w-5 h-5" />
47
+ </button>
48
+ );
49
+ }
50
+
51
+ return (
52
+ <button
53
+ type="button"
54
+ onClick={toggleTheme}
55
+ className={cn(
56
+ "flex items-center justify-center w-9 h-9 rounded-lg transition-colors",
57
+ "text-[var(--color-muted)] hover:text-[var(--color-foreground)]",
58
+ "hover:bg-[var(--color-surface-sunken)]",
59
+ className
60
+ )}
61
+ aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"}
62
+ >
63
+ {isDark ? (
64
+ <Sun className="w-5 h-5" />
65
+ ) : (
66
+ <Moon className="w-5 h-5" />
67
+ )}
68
+ </button>
69
+ );
70
+ }
71
+
72
+ export default ThemeToggle;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * React components for @opendocs/components
3
+ *
4
+ * These are used when importing components in MDX snippets:
5
+ * import { Callout, Card } from '@opendocs/components'
6
+ *
7
+ * Note: The main docs pages use Astro components (passed via components prop).
8
+ * These React versions are specifically for snippet imports.
9
+ */
10
+
11
+ export { Callout } from "./Callout";
12
+ export { Card } from "./Card";
13
+ export { CardGroup } from "./CardGroup";
14
+ export { Steps } from "./Steps";
@@ -0,0 +1,10 @@
1
+ /// <reference path="../.astro/types.d.ts" />
2
+ /// <reference types="astro/client" />
3
+
4
+ interface ImportMetaEnv {
5
+ readonly OPENDOCS_PROJECT_DIR: string;
6
+ }
7
+
8
+ interface ImportMeta {
9
+ readonly env: ImportMetaEnv;
10
+ }