blodemd 0.0.5 → 0.0.7

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 (187) hide show
  1. package/README.md +2 -2
  2. package/dev-server/app/[[...slug]]/page.tsx +139 -0
  3. package/dev-server/app/blodemd-dev/invalidate/route.ts +12 -0
  4. package/dev-server/app/blodemd-dev/static/[...path]/route.ts +32 -0
  5. package/dev-server/app/blodemd-dev/version/route.ts +14 -0
  6. package/dev-server/app/blodemd-internal/proxy/route.ts +86 -0
  7. package/dev-server/app/error.tsx +24 -0
  8. package/dev-server/app/favicon.ico +0 -0
  9. package/dev-server/app/globals.css +4 -0
  10. package/dev-server/app/layout.tsx +38 -0
  11. package/dev-server/app/not-found.tsx +18 -0
  12. package/dev-server/app/search/route.ts +17 -0
  13. package/dev-server/components/dev-reload-script.tsx +86 -0
  14. package/dev-server/components/providers.tsx +15 -0
  15. package/dev-server/lib/dev-state.ts +8 -0
  16. package/dev-server/lib/local-content-source.ts +103 -0
  17. package/dev-server/lib/local-runtime.tsx +558 -0
  18. package/dev-server/next-env.d.ts +5 -0
  19. package/dev-server/next.config.js +46 -0
  20. package/dev-server/package.json +57 -0
  21. package/dev-server/postcss.config.mjs +7 -0
  22. package/dev-server/public/glide-variable.woff2 +0 -0
  23. package/dev-server/tsconfig.json +50 -0
  24. package/dist/cli.mjs +311 -86
  25. package/dist/cli.mjs.map +1 -1
  26. package/docs/app/globals.css +457 -0
  27. package/docs/components/api/api-playground.tsx +295 -0
  28. package/docs/components/api/api-reference.tsx +121 -0
  29. package/docs/components/content/collection-index.tsx +114 -0
  30. package/docs/components/docs/contextual-menu.tsx +406 -0
  31. package/docs/components/docs/copy-page-menu.tsx +255 -0
  32. package/docs/components/docs/doc-header.tsx +210 -0
  33. package/docs/components/docs/doc-shell.tsx +313 -0
  34. package/docs/components/docs/doc-sidebar.tsx +211 -0
  35. package/docs/components/docs/doc-toc.tsx +45 -0
  36. package/docs/components/docs/mobile-nav.tsx +205 -0
  37. package/docs/components/icons/doc-icon.tsx +96 -0
  38. package/docs/components/mdx/accordion.tsx +83 -0
  39. package/docs/components/mdx/badge.tsx +79 -0
  40. package/docs/components/mdx/callout.tsx +88 -0
  41. package/docs/components/mdx/card.tsx +110 -0
  42. package/docs/components/mdx/code-block.tsx +75 -0
  43. package/docs/components/mdx/code-group.tsx +94 -0
  44. package/docs/components/mdx/color.tsx +87 -0
  45. package/docs/components/mdx/columns.tsx +25 -0
  46. package/docs/components/mdx/expandable.tsx +45 -0
  47. package/docs/components/mdx/field-layout.tsx +77 -0
  48. package/docs/components/mdx/frame.tsx +23 -0
  49. package/docs/components/mdx/get-text-content.ts +18 -0
  50. package/docs/components/mdx/icon.tsx +12 -0
  51. package/docs/components/mdx/index.tsx +107 -0
  52. package/docs/components/mdx/installer.tsx +20 -0
  53. package/docs/components/mdx/panel.tsx +11 -0
  54. package/docs/components/mdx/param-field.tsx +56 -0
  55. package/docs/components/mdx/preview.tsx +36 -0
  56. package/docs/components/mdx/prompt.tsx +63 -0
  57. package/docs/components/mdx/request-example.tsx +27 -0
  58. package/docs/components/mdx/response-field.tsx +42 -0
  59. package/docs/components/mdx/steps.tsx +92 -0
  60. package/docs/components/mdx/tabs.tsx +88 -0
  61. package/docs/components/mdx/tile.tsx +43 -0
  62. package/docs/components/mdx/tooltip.tsx +71 -0
  63. package/docs/components/mdx/tree.tsx +120 -0
  64. package/docs/components/mdx/type-table.tsx +71 -0
  65. package/docs/components/mdx/update.tsx +44 -0
  66. package/docs/components/mdx/video.tsx +12 -0
  67. package/docs/components/mdx/view.tsx +66 -0
  68. package/docs/components/providers.tsx +15 -0
  69. package/docs/components/ui/breadcrumb.tsx +92 -0
  70. package/docs/components/ui/button.tsx +90 -0
  71. package/docs/components/ui/card.tsx +92 -0
  72. package/docs/components/ui/command.tsx +139 -0
  73. package/docs/components/ui/dialog.tsx +97 -0
  74. package/docs/components/ui/field.tsx +237 -0
  75. package/docs/components/ui/input.tsx +105 -0
  76. package/docs/components/ui/label.tsx +22 -0
  77. package/docs/components/ui/popover.tsx +72 -0
  78. package/docs/components/ui/search.tsx +384 -0
  79. package/docs/components/ui/separator.tsx +26 -0
  80. package/docs/components/ui/sheet.tsx +104 -0
  81. package/docs/components/ui/sidebar.tsx +433 -0
  82. package/docs/components/ui/theme-toggle.tsx +62 -0
  83. package/docs/components/ui/tooltip.tsx +53 -0
  84. package/docs/lib/contextual-options.ts +193 -0
  85. package/docs/lib/docs-collection.ts +22 -0
  86. package/docs/lib/mdx.ts +87 -0
  87. package/docs/lib/navigation.ts +288 -0
  88. package/docs/lib/openapi.ts +158 -0
  89. package/docs/lib/routes.ts +44 -0
  90. package/docs/lib/server-cache.ts +83 -0
  91. package/docs/lib/shiki.ts +40 -0
  92. package/docs/lib/theme.ts +29 -0
  93. package/docs/lib/toc.ts +2 -0
  94. package/docs/lib/utils.ts +5 -0
  95. package/package.json +43 -6
  96. package/packages/@repo/common/dist/index.d.ts +9 -0
  97. package/packages/@repo/common/dist/index.d.ts.map +1 -0
  98. package/packages/@repo/common/dist/index.js +42 -0
  99. package/packages/@repo/common/package.json +34 -0
  100. package/packages/@repo/common/src/index.ts +51 -0
  101. package/packages/@repo/contracts/dist/api-key.d.ts +30 -0
  102. package/packages/@repo/contracts/dist/api-key.d.ts.map +1 -0
  103. package/packages/@repo/contracts/dist/api-key.js +20 -0
  104. package/packages/@repo/contracts/dist/dates.d.ts +4 -0
  105. package/packages/@repo/contracts/dist/dates.d.ts.map +1 -0
  106. package/packages/@repo/contracts/dist/dates.js +2 -0
  107. package/packages/@repo/contracts/dist/deployment.d.ts +71 -0
  108. package/packages/@repo/contracts/dist/deployment.d.ts.map +1 -0
  109. package/packages/@repo/contracts/dist/deployment.js +46 -0
  110. package/packages/@repo/contracts/dist/domain.d.ts +94 -0
  111. package/packages/@repo/contracts/dist/domain.d.ts.map +1 -0
  112. package/packages/@repo/contracts/dist/domain.js +36 -0
  113. package/packages/@repo/contracts/dist/ids.d.ts +14 -0
  114. package/packages/@repo/contracts/dist/ids.d.ts.map +1 -0
  115. package/packages/@repo/contracts/dist/ids.js +10 -0
  116. package/packages/@repo/contracts/dist/index.d.ts +10 -0
  117. package/packages/@repo/contracts/dist/index.d.ts.map +1 -0
  118. package/packages/@repo/contracts/dist/index.js +11 -0
  119. package/packages/@repo/contracts/dist/pagination.d.ts +23 -0
  120. package/packages/@repo/contracts/dist/pagination.d.ts.map +1 -0
  121. package/packages/@repo/contracts/dist/pagination.js +15 -0
  122. package/packages/@repo/contracts/dist/project.d.ts +25 -0
  123. package/packages/@repo/contracts/dist/project.d.ts.map +1 -0
  124. package/packages/@repo/contracts/dist/project.js +23 -0
  125. package/packages/@repo/contracts/dist/tenant.d.ts +111 -0
  126. package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -0
  127. package/packages/@repo/contracts/dist/tenant.js +56 -0
  128. package/packages/@repo/contracts/dist/user.d.ts +9 -0
  129. package/packages/@repo/contracts/dist/user.d.ts.map +1 -0
  130. package/packages/@repo/contracts/dist/user.js +9 -0
  131. package/packages/@repo/contracts/package.json +37 -0
  132. package/packages/@repo/contracts/src/api-key.ts +27 -0
  133. package/packages/@repo/contracts/src/dates.ts +4 -0
  134. package/packages/@repo/contracts/src/deployment.ts +73 -0
  135. package/packages/@repo/contracts/src/domain.ts +51 -0
  136. package/packages/@repo/contracts/src/ids.ts +22 -0
  137. package/packages/@repo/contracts/src/index.ts +11 -0
  138. package/packages/@repo/contracts/src/pagination.ts +21 -0
  139. package/packages/@repo/contracts/src/project.ts +30 -0
  140. package/packages/@repo/contracts/src/tenant.ts +92 -0
  141. package/packages/@repo/contracts/src/user.ts +12 -0
  142. package/packages/@repo/models/dist/docs-config.d.ts +985 -0
  143. package/packages/@repo/models/dist/docs-config.d.ts.map +1 -0
  144. package/packages/@repo/models/dist/docs-config.js +548 -0
  145. package/packages/@repo/models/dist/index.d.ts +3 -0
  146. package/packages/@repo/models/dist/index.d.ts.map +1 -0
  147. package/packages/@repo/models/dist/index.js +3 -0
  148. package/packages/@repo/models/dist/tenant.d.ts +25 -0
  149. package/packages/@repo/models/dist/tenant.d.ts.map +1 -0
  150. package/packages/@repo/models/dist/tenant.js +1 -0
  151. package/packages/@repo/models/package.json +37 -0
  152. package/packages/@repo/models/src/docs-config.ts +648 -0
  153. package/packages/@repo/models/src/index.ts +3 -0
  154. package/packages/@repo/models/src/tenant.ts +29 -0
  155. package/packages/@repo/prebuild/dist/index.d.ts +2 -0
  156. package/packages/@repo/prebuild/dist/index.d.ts.map +1 -0
  157. package/packages/@repo/prebuild/dist/index.js +2 -0
  158. package/packages/@repo/prebuild/dist/openapi.d.ts +43 -0
  159. package/packages/@repo/prebuild/dist/openapi.d.ts.map +1 -0
  160. package/packages/@repo/prebuild/dist/openapi.js +58 -0
  161. package/packages/@repo/prebuild/package.json +39 -0
  162. package/packages/@repo/prebuild/src/index.ts +2 -0
  163. package/packages/@repo/prebuild/src/openapi.ts +116 -0
  164. package/packages/@repo/previewing/dist/blob-source.d.ts +16 -0
  165. package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -0
  166. package/packages/@repo/previewing/dist/blob-source.js +110 -0
  167. package/packages/@repo/previewing/dist/content-source.d.ts +12 -0
  168. package/packages/@repo/previewing/dist/content-source.d.ts.map +1 -0
  169. package/packages/@repo/previewing/dist/content-source.js +1 -0
  170. package/packages/@repo/previewing/dist/fs-source.d.ts +11 -0
  171. package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -0
  172. package/packages/@repo/previewing/dist/fs-source.js +72 -0
  173. package/packages/@repo/previewing/dist/index.d.ts +120 -0
  174. package/packages/@repo/previewing/dist/index.d.ts.map +1 -0
  175. package/packages/@repo/previewing/dist/index.js +984 -0
  176. package/packages/@repo/previewing/package.json +41 -0
  177. package/packages/@repo/previewing/src/blob-source.ts +167 -0
  178. package/packages/@repo/previewing/src/content-source.ts +12 -0
  179. package/packages/@repo/previewing/src/fs-source.ts +104 -0
  180. package/packages/@repo/previewing/src/index.ts +1490 -0
  181. package/packages/@repo/validation/dist/index.d.ts +12 -0
  182. package/packages/@repo/validation/dist/index.d.ts.map +1 -0
  183. package/packages/@repo/validation/dist/index.js +30 -0
  184. package/packages/@repo/validation/package.json +37 -0
  185. package/packages/@repo/validation/src/index.ts +59 -0
  186. package/packages/@repo/validation/src/mintlify-docs-schema.json +5016 -0
  187. package/scripts/prepare-package.mjs +39 -0
@@ -0,0 +1,71 @@
1
+ "use client";
2
+
3
+ import { useCallback, useRef, useState } from "react";
4
+ import type { ReactNode } from "react";
5
+
6
+ interface TooltipProps {
7
+ tip: string;
8
+ headline?: string;
9
+ cta?: string;
10
+ href?: string;
11
+ children: ReactNode;
12
+ }
13
+
14
+ export const Tooltip = ({
15
+ tip,
16
+ headline,
17
+ cta,
18
+ href,
19
+ children,
20
+ }: TooltipProps) => {
21
+ const [open, setOpen] = useState(false);
22
+ const timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);
23
+
24
+ const show = useCallback(() => {
25
+ if (timeoutRef.current) {
26
+ clearTimeout(timeoutRef.current);
27
+ }
28
+ setOpen(true);
29
+ }, []);
30
+
31
+ const hide = useCallback(() => {
32
+ timeoutRef.current = setTimeout(() => setOpen(false), 150);
33
+ }, []);
34
+
35
+ return (
36
+ <span className="relative inline-block">
37
+ <button
38
+ className="cursor-help border-b border-dashed border-muted-foreground bg-transparent p-0 text-inherit"
39
+ onBlur={hide}
40
+ onFocus={show}
41
+ onMouseEnter={show}
42
+ onMouseLeave={hide}
43
+ type="button"
44
+ >
45
+ {children}
46
+ </button>
47
+ {open ? (
48
+ <span
49
+ className="absolute bottom-full left-1/2 z-50 mb-2 w-max max-w-64 -translate-x-1/2 rounded-lg border border-border bg-popover px-3 py-2 text-sm text-popover-foreground shadow-md"
50
+ onMouseEnter={show}
51
+ onMouseLeave={hide}
52
+ role="tooltip"
53
+ >
54
+ {headline ? (
55
+ <span className="mb-1 block font-semibold">{headline}</span>
56
+ ) : null}
57
+ <span className="block">{tip}</span>
58
+ {cta && href ? (
59
+ <a
60
+ className="mt-1 block text-primary hover:underline"
61
+ href={href}
62
+ rel="noopener noreferrer"
63
+ >
64
+ {cta}
65
+ </a>
66
+ ) : null}
67
+ </span>
68
+ ) : null}
69
+ </span>
70
+ );
71
+ };
@@ -0,0 +1,120 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import type { ReactNode } from "react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ interface TreeFileProps {
9
+ name: string;
10
+ }
11
+
12
+ export const TreeFile = ({ name }: TreeFileProps) => (
13
+ <div className="flex items-center gap-2 py-0.5 pl-5 text-sm">
14
+ <svg
15
+ aria-hidden
16
+ className="size-4 shrink-0 text-muted-foreground"
17
+ fill="none"
18
+ stroke="currentColor"
19
+ strokeWidth={2}
20
+ viewBox="0 0 24 24"
21
+ >
22
+ <path
23
+ d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
24
+ strokeLinecap="round"
25
+ strokeLinejoin="round"
26
+ />
27
+ <path
28
+ d="M14 2v4a2 2 0 0 0 2 2h4"
29
+ strokeLinecap="round"
30
+ strokeLinejoin="round"
31
+ />
32
+ </svg>
33
+ <span className="text-foreground">{name}</span>
34
+ </div>
35
+ );
36
+
37
+ interface TreeFolderProps {
38
+ name: string;
39
+ defaultOpen?: boolean;
40
+ openable?: boolean;
41
+ children?: ReactNode;
42
+ }
43
+
44
+ export const TreeFolder = ({
45
+ name,
46
+ defaultOpen = false,
47
+ openable = true,
48
+ children,
49
+ }: TreeFolderProps) => {
50
+ const [open, setOpen] = useState(defaultOpen);
51
+
52
+ const toggle = useCallback(() => {
53
+ if (openable) {
54
+ setOpen((prev) => !prev);
55
+ }
56
+ }, [openable]);
57
+
58
+ return (
59
+ <div>
60
+ <button
61
+ className={cn(
62
+ "flex w-full items-center gap-2 py-0.5 text-sm border-none bg-transparent text-left",
63
+ openable && "cursor-pointer hover:bg-accent/50 rounded"
64
+ )}
65
+ disabled={!openable}
66
+ onClick={toggle}
67
+ type="button"
68
+ >
69
+ <svg
70
+ aria-hidden
71
+ className={cn(
72
+ "size-3 shrink-0 text-muted-foreground transition-transform",
73
+ open && "rotate-90"
74
+ )}
75
+ fill="currentColor"
76
+ viewBox="0 0 24 24"
77
+ >
78
+ <path d="m9 18 6-6-6-6" />
79
+ </svg>
80
+ <svg
81
+ aria-hidden
82
+ className="size-4 shrink-0 text-muted-foreground"
83
+ fill="none"
84
+ stroke="currentColor"
85
+ strokeWidth={2}
86
+ viewBox="0 0 24 24"
87
+ >
88
+ {open ? (
89
+ <path
90
+ d="M5 19a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h4l2 2h4a2 2 0 0 1 2 2v1M5 19h14a2 2 0 0 0 2-2v-5a2 2 0 0 0-2-2H9a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2Z"
91
+ strokeLinecap="round"
92
+ strokeLinejoin="round"
93
+ />
94
+ ) : (
95
+ <path
96
+ d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"
97
+ strokeLinecap="round"
98
+ strokeLinejoin="round"
99
+ />
100
+ )}
101
+ </svg>
102
+ <span className="font-medium text-foreground">{name}</span>
103
+ </button>
104
+ {open && children ? (
105
+ <div className="ml-3 border-l border-border pl-2">{children}</div>
106
+ ) : null}
107
+ </div>
108
+ );
109
+ };
110
+
111
+ const TreeRoot = ({ children }: { children: ReactNode }) => (
112
+ <div className="my-4 rounded-xl border border-border bg-card p-4 font-mono text-sm">
113
+ {children}
114
+ </div>
115
+ );
116
+
117
+ export const Tree = Object.assign(TreeRoot, {
118
+ File: TreeFile,
119
+ Folder: TreeFolder,
120
+ });
@@ -0,0 +1,71 @@
1
+ interface TypeField {
2
+ description?: string;
3
+ type?: string;
4
+ optional?: boolean;
5
+ required?: boolean;
6
+ default?: string;
7
+ }
8
+
9
+ export const TypeTable = ({ type }: { type: Record<string, TypeField> }) => {
10
+ const entries = Object.entries(type ?? {}).map(([name, field]) => ({
11
+ name,
12
+ ...field,
13
+ }));
14
+ const hasDefault = entries.some((entry) => entry.default);
15
+
16
+ return (
17
+ <div className="no-scrollbar my-4 w-full overflow-y-auto rounded-xl border border-border">
18
+ <table className="relative w-full overflow-hidden border-none text-sm">
19
+ <thead>
20
+ <tr>
21
+ <th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
22
+ Field
23
+ </th>
24
+ <th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
25
+ Type
26
+ </th>
27
+ <th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
28
+ Description
29
+ </th>
30
+ <th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
31
+ Required
32
+ </th>
33
+ {hasDefault ? (
34
+ <th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
35
+ Default
36
+ </th>
37
+ ) : null}
38
+ </tr>
39
+ </thead>
40
+ <tbody>
41
+ {entries.map((entry) => {
42
+ const required = entry.required ?? !entry.optional;
43
+ return (
44
+ <tr key={entry.name}>
45
+ <td className="border-b border-border px-3 py-2.5 text-left align-top">
46
+ <code className="rounded-md bg-muted px-1.5 py-0.5 font-mono text-xs">
47
+ {entry.name}
48
+ </code>
49
+ </td>
50
+ <td className="border-b border-border px-3 py-2.5 text-left align-top">
51
+ {entry.type ?? "\u2014"}
52
+ </td>
53
+ <td className="border-b border-border px-3 py-2.5 text-left align-top">
54
+ {entry.description ?? ""}
55
+ </td>
56
+ <td className="border-b border-border px-3 py-2.5 text-left align-top">
57
+ {required ? "Yes" : "Optional"}
58
+ </td>
59
+ {hasDefault ? (
60
+ <td className="border-b border-border px-3 py-2.5 text-left align-top">
61
+ {entry.default ?? "\u2014"}
62
+ </td>
63
+ ) : null}
64
+ </tr>
65
+ );
66
+ })}
67
+ </tbody>
68
+ </table>
69
+ </div>
70
+ );
71
+ };
@@ -0,0 +1,44 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ interface UpdateProps {
4
+ label: string;
5
+ description?: string;
6
+ tags?: string[];
7
+ children: ReactNode;
8
+ }
9
+
10
+ export const Update = ({ label, description, tags, children }: UpdateProps) => {
11
+ const anchorId = label.toLowerCase().replaceAll(/\s+/g, "-");
12
+
13
+ return (
14
+ <div
15
+ className="relative border-b border-border py-8 first:pt-0 last:border-b-0"
16
+ id={anchorId}
17
+ >
18
+ <div className="mb-4 flex flex-wrap items-center gap-3">
19
+ <a
20
+ className="text-lg font-semibold text-foreground hover:underline"
21
+ href={`#${anchorId}`}
22
+ >
23
+ {label}
24
+ </a>
25
+ {description ? (
26
+ <span className="rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-muted-foreground">
27
+ {description}
28
+ </span>
29
+ ) : null}
30
+ {tags?.map((tag) => (
31
+ <span
32
+ className="rounded-full bg-primary/10 px-2.5 py-0.5 text-xs font-medium text-primary"
33
+ key={tag}
34
+ >
35
+ {tag}
36
+ </span>
37
+ ))}
38
+ </div>
39
+ <div className="prose prose-sm text-muted-foreground [&>p:first-child]:mt-0 [&>p:last-child]:mb-0">
40
+ {children}
41
+ </div>
42
+ </div>
43
+ );
44
+ };
@@ -0,0 +1,12 @@
1
+ export const Video = ({ src }: { src: string }) => (
2
+ <div className="relative overflow-hidden rounded-xl border border-border bg-black pt-[56.25%]">
3
+ <iframe
4
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
5
+ allowFullScreen
6
+ className="absolute inset-0 h-full w-full border-0"
7
+ sandbox="allow-popups allow-presentation allow-scripts"
8
+ src={src}
9
+ title="Video"
10
+ />
11
+ </div>
12
+ );
@@ -0,0 +1,66 @@
1
+ "use client";
2
+
3
+ import { isValidElement, useCallback, useMemo, useState } from "react";
4
+ import type { MouseEvent, ReactElement, ReactNode } from "react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ interface ViewItemProps {
9
+ title: string;
10
+ icon?: ReactNode;
11
+ children: ReactNode;
12
+ }
13
+
14
+ export const View = ({ children }: ViewItemProps) => children as ReactElement;
15
+
16
+ interface ViewGroupProps {
17
+ children: ReactNode;
18
+ }
19
+
20
+ export const ViewGroup = ({ children }: ViewGroupProps) => {
21
+ const items = useMemo(() => {
22
+ const nodes = Array.isArray(children) ? children : [children];
23
+ return nodes.filter(
24
+ (child): child is ReactElement<ViewItemProps> =>
25
+ isValidElement<ViewItemProps>(child) && child.type === View
26
+ );
27
+ }, [children]);
28
+
29
+ const [active, setActive] = useState(0);
30
+
31
+ const handleSelect = useCallback((event: MouseEvent<HTMLButtonElement>) => {
32
+ const index = Number(event.currentTarget.dataset.index ?? "0");
33
+ setActive(index);
34
+ }, []);
35
+
36
+ if (!items.length) {
37
+ return children as ReactElement;
38
+ }
39
+
40
+ return (
41
+ <div className="my-4">
42
+ <div className="mb-4 flex flex-wrap gap-2">
43
+ {items.map((item, index) => (
44
+ <button
45
+ className={cn(
46
+ "inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium transition-colors",
47
+ index === active
48
+ ? "bg-primary text-primary-foreground"
49
+ : "bg-muted text-muted-foreground hover:text-foreground"
50
+ )}
51
+ data-index={index}
52
+ key={item.props.title}
53
+ onClick={handleSelect}
54
+ type="button"
55
+ >
56
+ {item.props.icon ? (
57
+ <span className="shrink-0">{item.props.icon}</span>
58
+ ) : null}
59
+ {item.props.title}
60
+ </button>
61
+ ))}
62
+ </div>
63
+ <div>{items[active]}</div>
64
+ </div>
65
+ );
66
+ };
@@ -0,0 +1,15 @@
1
+ "use client";
2
+
3
+ import { ThemeProvider } from "next-themes";
4
+
5
+ export const Providers = ({ children }: { children: React.ReactNode }) => (
6
+ <ThemeProvider
7
+ attribute="class"
8
+ defaultTheme="system"
9
+ disableTransitionOnChange
10
+ enableColorScheme
11
+ storageKey="theme"
12
+ >
13
+ {children}
14
+ </ThemeProvider>
15
+ );
@@ -0,0 +1,92 @@
1
+ import { Slot } from "radix-ui";
2
+ import type { ComponentProps } from "react";
3
+
4
+ import { cn } from "@/lib/utils";
5
+
6
+ const Breadcrumb = ({ ...props }: ComponentProps<"nav">) => (
7
+ <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
8
+ );
9
+
10
+ const BreadcrumbList = ({ className, ...props }: ComponentProps<"ol">) => (
11
+ <ol
12
+ data-slot="breadcrumb-list"
13
+ className={cn(
14
+ "flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground break-words sm:gap-2.5",
15
+ className
16
+ )}
17
+ {...props}
18
+ />
19
+ );
20
+
21
+ const BreadcrumbItem = ({ className, ...props }: ComponentProps<"li">) => (
22
+ <li
23
+ data-slot="breadcrumb-item"
24
+ className={cn("inline-flex items-center gap-1.5", className)}
25
+ {...props}
26
+ />
27
+ );
28
+
29
+ const BreadcrumbLink = ({
30
+ asChild,
31
+ className,
32
+ ...props
33
+ }: ComponentProps<"a"> & { asChild?: boolean }) => {
34
+ const Comp = asChild ? Slot.Root : "a";
35
+
36
+ return (
37
+ <Comp
38
+ data-slot="breadcrumb-link"
39
+ className={cn("transition-colors hover:text-foreground", className)}
40
+ {...props}
41
+ />
42
+ );
43
+ };
44
+
45
+ const BreadcrumbPage = ({ className, ...props }: ComponentProps<"span">) => (
46
+ <span
47
+ data-slot="breadcrumb-page"
48
+ aria-disabled="true"
49
+ aria-current="page"
50
+ className={cn("font-normal text-foreground", className)}
51
+ {...props}
52
+ />
53
+ );
54
+
55
+ const BreadcrumbSeparator = ({
56
+ children,
57
+ className,
58
+ ...props
59
+ }: ComponentProps<"li">) => (
60
+ <li
61
+ data-slot="breadcrumb-separator"
62
+ role="presentation"
63
+ aria-hidden="true"
64
+ className={cn("[&>svg]:size-3.5", className)}
65
+ {...props}
66
+ >
67
+ {children ?? (
68
+ <svg
69
+ xmlns="http://www.w3.org/2000/svg"
70
+ width="16"
71
+ height="16"
72
+ viewBox="0 0 24 24"
73
+ fill="none"
74
+ stroke="currentColor"
75
+ strokeWidth="2"
76
+ strokeLinecap="round"
77
+ strokeLinejoin="round"
78
+ >
79
+ <path d="m9 18 6-6-6-6" />
80
+ </svg>
81
+ )}
82
+ </li>
83
+ );
84
+
85
+ export {
86
+ Breadcrumb,
87
+ BreadcrumbItem,
88
+ BreadcrumbLink,
89
+ BreadcrumbList,
90
+ BreadcrumbPage,
91
+ BreadcrumbSeparator,
92
+ };
@@ -0,0 +1,90 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props";
2
+ import { useRender } from "@base-ui/react/use-render";
3
+ import { cva } from "class-variance-authority";
4
+ import type { VariantProps } from "class-variance-authority";
5
+ import type * as React from "react";
6
+
7
+ import { cn } from "@/lib/utils";
8
+
9
+ const buttonVariants = cva(
10
+ "group/button inline-flex shrink-0 select-none items-center justify-center whitespace-nowrap rounded-lg border border-transparent bg-clip-padding font-medium text-sm outline-none transition-[color,background-color,border-color,box-shadow,opacity,transform] duration-150 ease-out focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-45 aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed aria-disabled:opacity-45 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
11
+ {
12
+ defaultVariants: {
13
+ size: "default",
14
+ variant: "default",
15
+ },
16
+ variants: {
17
+ size: {
18
+ default:
19
+ "h-10 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
20
+ icon: "size-10",
21
+ "icon-lg": "size-11",
22
+ "icon-sm":
23
+ "size-9 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),12px)]",
24
+ "icon-xs":
25
+ "size-8 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),10px)] [&_svg:not([class*='size-'])]:size-3",
26
+ input:
27
+ "h-[var(--field-height)] gap-2 rounded-[var(--field-radius)] px-[var(--field-padding-x)] py-[var(--field-padding-y)]",
28
+ "input-sm":
29
+ "h-[var(--field-height-sm)] gap-2 rounded-[var(--field-radius)] px-[var(--field-padding-x)] py-[var(--field-padding-y)]",
30
+ lg: "h-11 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3.5 has-data-[icon=inline-start]:pl-3.5",
31
+ sm: "h-9 gap-1 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),12px)] px-3 text-[0.8rem] has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3.5",
32
+ xs: "h-8 gap-1 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),10px)] px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
33
+ },
34
+ variant: {
35
+ default:
36
+ "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/95 aria-pressed:bg-primary/95",
37
+ destructive:
38
+ "bg-red-600 text-white hover:bg-red-700 focus-visible:border-red-600 focus-visible:ring-red-500/30 active:bg-red-800 aria-pressed:bg-red-800 dark:bg-red-500 dark:aria-pressed:bg-red-300 dark:active:bg-red-300 dark:hover:bg-red-400",
39
+ destructiveSecondary:
40
+ "border-red-200 text-red-700 hover:bg-red-50 active:bg-red-100 aria-pressed:bg-red-100 dark:border-red-800 dark:text-red-300 dark:aria-pressed:bg-red-950 dark:active:bg-red-950 dark:hover:bg-red-950/60",
41
+ ghost:
42
+ "hover:bg-muted hover:text-foreground active:bg-muted/80 aria-expanded:bg-muted aria-pressed:bg-muted/80 dark:aria-pressed:bg-muted/60 dark:active:bg-muted/60 dark:hover:bg-muted/50",
43
+ input:
44
+ "border-input bg-card font-normal font-sans text-base text-foreground leading-snug shadow-input hover:border-input-hover focus-visible:ring-2 focus-visible:ring-ring/15 focus-visible:ring-offset-1 focus-visible:ring-offset-background active:border-input-hover/80 aria-pressed:border-input-hover aria-invalid:border-destructive-foreground data-[placeholder]:text-placeholder-foreground",
45
+ link: "text-primary underline-offset-4 hover:underline active:opacity-80 aria-pressed:underline aria-pressed:opacity-80",
46
+ outline:
47
+ "border-border bg-background hover:bg-muted hover:text-foreground active:bg-muted/80 aria-expanded:bg-muted aria-pressed:bg-muted/80 dark:border-input dark:bg-input/30 dark:aria-pressed:bg-input/60 dark:active:bg-input/60 dark:hover:bg-input/50",
48
+ secondary:
49
+ "bg-secondary text-secondary-foreground hover:bg-secondary/85 active:bg-secondary/75 aria-expanded:bg-secondary aria-pressed:bg-secondary/75",
50
+ success:
51
+ "bg-green-600 text-white hover:bg-green-700 focus-visible:border-green-600 focus-visible:ring-green-500/30 active:bg-green-800 aria-pressed:bg-green-800 dark:bg-green-500 dark:aria-pressed:bg-green-300 dark:active:bg-green-300 dark:hover:bg-green-400",
52
+ successSecondary:
53
+ "border-green-200 text-green-700 hover:bg-green-50 active:bg-green-100 aria-pressed:bg-green-100 dark:border-green-800 dark:text-green-300 dark:aria-pressed:bg-green-950 dark:active:bg-green-950 dark:hover:bg-green-950/60",
54
+ warning:
55
+ "bg-yellow-600 text-white hover:bg-yellow-700 focus-visible:border-yellow-600 focus-visible:ring-yellow-500/30 active:bg-yellow-800 aria-pressed:bg-yellow-800 dark:bg-yellow-500 dark:text-yellow-950 dark:aria-pressed:bg-yellow-300 dark:active:bg-yellow-300 dark:hover:bg-yellow-400",
56
+ warningSecondary:
57
+ "border-yellow-200 text-yellow-700 hover:bg-yellow-50 active:bg-yellow-100 aria-pressed:bg-yellow-100 dark:border-yellow-800 dark:text-yellow-300 dark:aria-pressed:bg-yellow-950 dark:active:bg-yellow-950 dark:hover:bg-yellow-950/60",
58
+ },
59
+ },
60
+ }
61
+ );
62
+
63
+ const Button = ({
64
+ className,
65
+ variant = "default",
66
+ size = "default",
67
+ asChild = false,
68
+ children,
69
+ ...props
70
+ }: React.ComponentProps<"button"> &
71
+ VariantProps<typeof buttonVariants> & {
72
+ asChild?: boolean;
73
+ }) =>
74
+ useRender({
75
+ defaultTagName: "button",
76
+ props: mergeProps<"button">(
77
+ {
78
+ className: cn(buttonVariants({ className, size, variant })),
79
+ },
80
+ asChild ? props : { ...props, children }
81
+ ),
82
+ render: asChild ? (children as React.ReactElement) : undefined,
83
+ state: {
84
+ size,
85
+ slot: "button",
86
+ variant,
87
+ },
88
+ });
89
+
90
+ export { Button, buttonVariants };
@@ -0,0 +1,92 @@
1
+ import type * as React from "react";
2
+
3
+ import { cn } from "@/lib/utils";
4
+
5
+ const Card = ({
6
+ className,
7
+ size = "default",
8
+ ...props
9
+ }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) => (
10
+ <div
11
+ className={cn(
12
+ "group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-card-foreground text-sm ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 has-data-[slot=card-footer]:pb-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
13
+ className
14
+ )}
15
+ data-size={size}
16
+ data-slot="card"
17
+ {...props}
18
+ />
19
+ );
20
+
21
+ const CardHeader = ({ className, ...props }: React.ComponentProps<"div">) => (
22
+ <div
23
+ className={cn(
24
+ "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
25
+ className
26
+ )}
27
+ data-slot="card-header"
28
+ {...props}
29
+ />
30
+ );
31
+
32
+ const CardTitle = ({ className, ...props }: React.ComponentProps<"div">) => (
33
+ <div
34
+ className={cn(
35
+ "font-medium text-base leading-snug group-data-[size=sm]/card:text-sm",
36
+ className
37
+ )}
38
+ data-slot="card-title"
39
+ {...props}
40
+ />
41
+ );
42
+
43
+ const CardDescription = ({
44
+ className,
45
+ ...props
46
+ }: React.ComponentProps<"div">) => (
47
+ <div
48
+ className={cn("text-muted-foreground text-sm", className)}
49
+ data-slot="card-description"
50
+ {...props}
51
+ />
52
+ );
53
+
54
+ const CardAction = ({ className, ...props }: React.ComponentProps<"div">) => (
55
+ <div
56
+ className={cn(
57
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
58
+ className
59
+ )}
60
+ data-slot="card-action"
61
+ {...props}
62
+ />
63
+ );
64
+
65
+ const CardContent = ({ className, ...props }: React.ComponentProps<"div">) => (
66
+ <div
67
+ className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
68
+ data-slot="card-content"
69
+ {...props}
70
+ />
71
+ );
72
+
73
+ const CardFooter = ({ className, ...props }: React.ComponentProps<"div">) => (
74
+ <div
75
+ className={cn(
76
+ "flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3",
77
+ className
78
+ )}
79
+ data-slot="card-footer"
80
+ {...props}
81
+ />
82
+ );
83
+
84
+ export {
85
+ Card,
86
+ CardHeader,
87
+ CardFooter,
88
+ CardTitle,
89
+ CardAction,
90
+ CardDescription,
91
+ CardContent,
92
+ };