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.
- package/README.md +2 -2
- package/dev-server/app/[[...slug]]/page.tsx +139 -0
- package/dev-server/app/blodemd-dev/invalidate/route.ts +12 -0
- package/dev-server/app/blodemd-dev/static/[...path]/route.ts +32 -0
- package/dev-server/app/blodemd-dev/version/route.ts +14 -0
- package/dev-server/app/blodemd-internal/proxy/route.ts +86 -0
- package/dev-server/app/error.tsx +24 -0
- package/dev-server/app/favicon.ico +0 -0
- package/dev-server/app/globals.css +4 -0
- package/dev-server/app/layout.tsx +38 -0
- package/dev-server/app/not-found.tsx +18 -0
- package/dev-server/app/search/route.ts +17 -0
- package/dev-server/components/dev-reload-script.tsx +86 -0
- package/dev-server/components/providers.tsx +15 -0
- package/dev-server/lib/dev-state.ts +8 -0
- package/dev-server/lib/local-content-source.ts +103 -0
- package/dev-server/lib/local-runtime.tsx +558 -0
- package/dev-server/next-env.d.ts +5 -0
- package/dev-server/next.config.js +46 -0
- package/dev-server/package.json +57 -0
- package/dev-server/postcss.config.mjs +7 -0
- package/dev-server/public/glide-variable.woff2 +0 -0
- package/dev-server/tsconfig.json +50 -0
- package/dist/cli.mjs +311 -86
- package/dist/cli.mjs.map +1 -1
- package/docs/app/globals.css +457 -0
- package/docs/components/api/api-playground.tsx +295 -0
- package/docs/components/api/api-reference.tsx +121 -0
- package/docs/components/content/collection-index.tsx +114 -0
- package/docs/components/docs/contextual-menu.tsx +406 -0
- package/docs/components/docs/copy-page-menu.tsx +255 -0
- package/docs/components/docs/doc-header.tsx +210 -0
- package/docs/components/docs/doc-shell.tsx +313 -0
- package/docs/components/docs/doc-sidebar.tsx +211 -0
- package/docs/components/docs/doc-toc.tsx +45 -0
- package/docs/components/docs/mobile-nav.tsx +205 -0
- package/docs/components/icons/doc-icon.tsx +96 -0
- package/docs/components/mdx/accordion.tsx +83 -0
- package/docs/components/mdx/badge.tsx +79 -0
- package/docs/components/mdx/callout.tsx +88 -0
- package/docs/components/mdx/card.tsx +110 -0
- package/docs/components/mdx/code-block.tsx +75 -0
- package/docs/components/mdx/code-group.tsx +94 -0
- package/docs/components/mdx/color.tsx +87 -0
- package/docs/components/mdx/columns.tsx +25 -0
- package/docs/components/mdx/expandable.tsx +45 -0
- package/docs/components/mdx/field-layout.tsx +77 -0
- package/docs/components/mdx/frame.tsx +23 -0
- package/docs/components/mdx/get-text-content.ts +18 -0
- package/docs/components/mdx/icon.tsx +12 -0
- package/docs/components/mdx/index.tsx +107 -0
- package/docs/components/mdx/installer.tsx +20 -0
- package/docs/components/mdx/panel.tsx +11 -0
- package/docs/components/mdx/param-field.tsx +56 -0
- package/docs/components/mdx/preview.tsx +36 -0
- package/docs/components/mdx/prompt.tsx +63 -0
- package/docs/components/mdx/request-example.tsx +27 -0
- package/docs/components/mdx/response-field.tsx +42 -0
- package/docs/components/mdx/steps.tsx +92 -0
- package/docs/components/mdx/tabs.tsx +88 -0
- package/docs/components/mdx/tile.tsx +43 -0
- package/docs/components/mdx/tooltip.tsx +71 -0
- package/docs/components/mdx/tree.tsx +120 -0
- package/docs/components/mdx/type-table.tsx +71 -0
- package/docs/components/mdx/update.tsx +44 -0
- package/docs/components/mdx/video.tsx +12 -0
- package/docs/components/mdx/view.tsx +66 -0
- package/docs/components/providers.tsx +15 -0
- package/docs/components/ui/breadcrumb.tsx +92 -0
- package/docs/components/ui/button.tsx +90 -0
- package/docs/components/ui/card.tsx +92 -0
- package/docs/components/ui/command.tsx +139 -0
- package/docs/components/ui/dialog.tsx +97 -0
- package/docs/components/ui/field.tsx +237 -0
- package/docs/components/ui/input.tsx +105 -0
- package/docs/components/ui/label.tsx +22 -0
- package/docs/components/ui/popover.tsx +72 -0
- package/docs/components/ui/search.tsx +384 -0
- package/docs/components/ui/separator.tsx +26 -0
- package/docs/components/ui/sheet.tsx +104 -0
- package/docs/components/ui/sidebar.tsx +433 -0
- package/docs/components/ui/theme-toggle.tsx +62 -0
- package/docs/components/ui/tooltip.tsx +53 -0
- package/docs/lib/contextual-options.ts +193 -0
- package/docs/lib/docs-collection.ts +22 -0
- package/docs/lib/mdx.ts +87 -0
- package/docs/lib/navigation.ts +288 -0
- package/docs/lib/openapi.ts +158 -0
- package/docs/lib/routes.ts +44 -0
- package/docs/lib/server-cache.ts +83 -0
- package/docs/lib/shiki.ts +40 -0
- package/docs/lib/theme.ts +29 -0
- package/docs/lib/toc.ts +2 -0
- package/docs/lib/utils.ts +5 -0
- package/package.json +43 -6
- package/packages/@repo/common/dist/index.d.ts +9 -0
- package/packages/@repo/common/dist/index.d.ts.map +1 -0
- package/packages/@repo/common/dist/index.js +42 -0
- package/packages/@repo/common/package.json +34 -0
- package/packages/@repo/common/src/index.ts +51 -0
- package/packages/@repo/contracts/dist/api-key.d.ts +30 -0
- package/packages/@repo/contracts/dist/api-key.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/api-key.js +20 -0
- package/packages/@repo/contracts/dist/dates.d.ts +4 -0
- package/packages/@repo/contracts/dist/dates.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/dates.js +2 -0
- package/packages/@repo/contracts/dist/deployment.d.ts +71 -0
- package/packages/@repo/contracts/dist/deployment.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/deployment.js +46 -0
- package/packages/@repo/contracts/dist/domain.d.ts +94 -0
- package/packages/@repo/contracts/dist/domain.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/domain.js +36 -0
- package/packages/@repo/contracts/dist/ids.d.ts +14 -0
- package/packages/@repo/contracts/dist/ids.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/ids.js +10 -0
- package/packages/@repo/contracts/dist/index.d.ts +10 -0
- package/packages/@repo/contracts/dist/index.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/index.js +11 -0
- package/packages/@repo/contracts/dist/pagination.d.ts +23 -0
- package/packages/@repo/contracts/dist/pagination.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/pagination.js +15 -0
- package/packages/@repo/contracts/dist/project.d.ts +25 -0
- package/packages/@repo/contracts/dist/project.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/project.js +23 -0
- package/packages/@repo/contracts/dist/tenant.d.ts +111 -0
- package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/tenant.js +56 -0
- package/packages/@repo/contracts/dist/user.d.ts +9 -0
- package/packages/@repo/contracts/dist/user.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/user.js +9 -0
- package/packages/@repo/contracts/package.json +37 -0
- package/packages/@repo/contracts/src/api-key.ts +27 -0
- package/packages/@repo/contracts/src/dates.ts +4 -0
- package/packages/@repo/contracts/src/deployment.ts +73 -0
- package/packages/@repo/contracts/src/domain.ts +51 -0
- package/packages/@repo/contracts/src/ids.ts +22 -0
- package/packages/@repo/contracts/src/index.ts +11 -0
- package/packages/@repo/contracts/src/pagination.ts +21 -0
- package/packages/@repo/contracts/src/project.ts +30 -0
- package/packages/@repo/contracts/src/tenant.ts +92 -0
- package/packages/@repo/contracts/src/user.ts +12 -0
- package/packages/@repo/models/dist/docs-config.d.ts +985 -0
- package/packages/@repo/models/dist/docs-config.d.ts.map +1 -0
- package/packages/@repo/models/dist/docs-config.js +548 -0
- package/packages/@repo/models/dist/index.d.ts +3 -0
- package/packages/@repo/models/dist/index.d.ts.map +1 -0
- package/packages/@repo/models/dist/index.js +3 -0
- package/packages/@repo/models/dist/tenant.d.ts +25 -0
- package/packages/@repo/models/dist/tenant.d.ts.map +1 -0
- package/packages/@repo/models/dist/tenant.js +1 -0
- package/packages/@repo/models/package.json +37 -0
- package/packages/@repo/models/src/docs-config.ts +648 -0
- package/packages/@repo/models/src/index.ts +3 -0
- package/packages/@repo/models/src/tenant.ts +29 -0
- package/packages/@repo/prebuild/dist/index.d.ts +2 -0
- package/packages/@repo/prebuild/dist/index.d.ts.map +1 -0
- package/packages/@repo/prebuild/dist/index.js +2 -0
- package/packages/@repo/prebuild/dist/openapi.d.ts +43 -0
- package/packages/@repo/prebuild/dist/openapi.d.ts.map +1 -0
- package/packages/@repo/prebuild/dist/openapi.js +58 -0
- package/packages/@repo/prebuild/package.json +39 -0
- package/packages/@repo/prebuild/src/index.ts +2 -0
- package/packages/@repo/prebuild/src/openapi.ts +116 -0
- package/packages/@repo/previewing/dist/blob-source.d.ts +16 -0
- package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/blob-source.js +110 -0
- package/packages/@repo/previewing/dist/content-source.d.ts +12 -0
- package/packages/@repo/previewing/dist/content-source.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/content-source.js +1 -0
- package/packages/@repo/previewing/dist/fs-source.d.ts +11 -0
- package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/fs-source.js +72 -0
- package/packages/@repo/previewing/dist/index.d.ts +120 -0
- package/packages/@repo/previewing/dist/index.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/index.js +984 -0
- package/packages/@repo/previewing/package.json +41 -0
- package/packages/@repo/previewing/src/blob-source.ts +167 -0
- package/packages/@repo/previewing/src/content-source.ts +12 -0
- package/packages/@repo/previewing/src/fs-source.ts +104 -0
- package/packages/@repo/previewing/src/index.ts +1490 -0
- package/packages/@repo/validation/dist/index.d.ts +12 -0
- package/packages/@repo/validation/dist/index.d.ts.map +1 -0
- package/packages/@repo/validation/dist/index.js +30 -0
- package/packages/@repo/validation/package.json +37 -0
- package/packages/@repo/validation/src/index.ts +59 -0
- package/packages/@repo/validation/src/mintlify-docs-schema.json +5016 -0
- package/scripts/prepare-package.mjs +39 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { normalizePath } from "@repo/common";
|
|
2
|
+
import { ArrowUpRightIcon } from "blode-icons-react";
|
|
3
|
+
import Image from "next/image";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
|
|
6
|
+
import { DocIcon } from "@/components/icons/doc-icon";
|
|
7
|
+
import { getNavPageHref, getNavPageTitle } from "@/lib/navigation";
|
|
8
|
+
import type { NavEntry, NavPage } from "@/lib/navigation";
|
|
9
|
+
import { isExternalHref, toDocHref } from "@/lib/routes";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
|
|
12
|
+
const MENU_BUTTON_CLASS =
|
|
13
|
+
"relative flex min-h-[30px] items-center gap-2 overflow-visible rounded-md border border-transparent px-2 text-[0.8rem] font-medium transition-colors hover:bg-accent/70 hover:text-foreground";
|
|
14
|
+
|
|
15
|
+
const NavIcon = ({ icon }: { icon: string }) => {
|
|
16
|
+
if (icon.startsWith("http") || icon.startsWith("/")) {
|
|
17
|
+
return (
|
|
18
|
+
<Image
|
|
19
|
+
alt=""
|
|
20
|
+
className="size-4 shrink-0"
|
|
21
|
+
height={16}
|
|
22
|
+
src={icon}
|
|
23
|
+
unoptimized
|
|
24
|
+
width={16}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return (
|
|
29
|
+
<DocIcon
|
|
30
|
+
className="size-4 shrink-0 text-muted-foreground"
|
|
31
|
+
icon={icon}
|
|
32
|
+
size={16}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const NavPageLink = ({
|
|
38
|
+
item,
|
|
39
|
+
basePath,
|
|
40
|
+
isActive,
|
|
41
|
+
}: {
|
|
42
|
+
item: NavPage;
|
|
43
|
+
basePath: string;
|
|
44
|
+
isActive: boolean;
|
|
45
|
+
}) => {
|
|
46
|
+
const displayTitle = getNavPageTitle(item);
|
|
47
|
+
const href = getNavPageHref(item, basePath);
|
|
48
|
+
const isExternal = Boolean(item.url && isExternalHref(item.url));
|
|
49
|
+
|
|
50
|
+
const linkContent = (
|
|
51
|
+
<>
|
|
52
|
+
{item.icon ? <NavIcon icon={item.icon} /> : null}
|
|
53
|
+
<span className={item.deprecated ? "line-through opacity-60" : undefined}>
|
|
54
|
+
{displayTitle}
|
|
55
|
+
</span>
|
|
56
|
+
{item.tag ? (
|
|
57
|
+
<span className="ml-auto shrink-0 rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium leading-none text-primary">
|
|
58
|
+
{item.tag}
|
|
59
|
+
</span>
|
|
60
|
+
) : null}
|
|
61
|
+
{item.deprecated && !item.tag ? (
|
|
62
|
+
<span className="ml-auto shrink-0 rounded bg-yellow-100 px-1.5 py-0.5 text-[10px] font-medium leading-none text-yellow-800 dark:bg-yellow-900/40 dark:text-yellow-300">
|
|
63
|
+
Deprecated
|
|
64
|
+
</span>
|
|
65
|
+
) : null}
|
|
66
|
+
{isExternal ? (
|
|
67
|
+
<ArrowUpRightIcon
|
|
68
|
+
aria-hidden="true"
|
|
69
|
+
className="ml-auto size-3 shrink-0 text-muted-foreground"
|
|
70
|
+
/>
|
|
71
|
+
) : null}
|
|
72
|
+
</>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const className = cn(
|
|
76
|
+
MENU_BUTTON_CLASS,
|
|
77
|
+
isActive && "border-accent bg-accent text-foreground"
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (isExternal) {
|
|
81
|
+
return (
|
|
82
|
+
<a
|
|
83
|
+
className={className}
|
|
84
|
+
href={href}
|
|
85
|
+
rel="noopener noreferrer"
|
|
86
|
+
target="_blank"
|
|
87
|
+
>
|
|
88
|
+
{linkContent}
|
|
89
|
+
</a>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Link className={className} href={href}>
|
|
95
|
+
{linkContent}
|
|
96
|
+
</Link>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const Section = ({
|
|
101
|
+
title,
|
|
102
|
+
children,
|
|
103
|
+
paddedTop = false,
|
|
104
|
+
}: {
|
|
105
|
+
title?: string;
|
|
106
|
+
children: React.ReactNode;
|
|
107
|
+
paddedTop?: boolean;
|
|
108
|
+
}) => (
|
|
109
|
+
<section
|
|
110
|
+
className={cn(
|
|
111
|
+
"relative flex w-full min-w-0 flex-col p-2",
|
|
112
|
+
paddedTop && "pt-6"
|
|
113
|
+
)}
|
|
114
|
+
>
|
|
115
|
+
{title ? (
|
|
116
|
+
<div className="mb-2 px-2 font-medium text-muted-foreground text-xs">
|
|
117
|
+
{title}
|
|
118
|
+
</div>
|
|
119
|
+
) : null}
|
|
120
|
+
<div className="w-full text-sm">{children}</div>
|
|
121
|
+
</section>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
export const DocSidebar = ({
|
|
125
|
+
entries,
|
|
126
|
+
currentPath,
|
|
127
|
+
anchors,
|
|
128
|
+
basePath,
|
|
129
|
+
}: {
|
|
130
|
+
entries: NavEntry[];
|
|
131
|
+
currentPath: string;
|
|
132
|
+
anchors?: { label: string; href: string }[];
|
|
133
|
+
basePath: string;
|
|
134
|
+
}) => {
|
|
135
|
+
const activePath = normalizePath(currentPath);
|
|
136
|
+
const isActive = (path: string) => normalizePath(path) === activePath;
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<aside
|
|
140
|
+
className="sticky top-[calc(var(--header-height)+0.6rem)] z-30 hidden h-[calc(100svh-10rem)] w-[calc(var(--spacing)*56)] shrink-0 flex-col overscroll-none bg-transparent lg:flex"
|
|
141
|
+
aria-label="Documentation navigation"
|
|
142
|
+
>
|
|
143
|
+
<div className="h-9" />
|
|
144
|
+
<div className="absolute top-8 z-10 h-8 w-full shrink-0 bg-gradient-to-b from-background via-background/80 to-background/50 blur-xs" />
|
|
145
|
+
<div className="absolute top-12 right-2 bottom-0 hidden h-full w-px bg-gradient-to-b from-transparent via-border to-transparent lg:flex" />
|
|
146
|
+
<div className="no-scrollbar mx-auto flex min-h-0 w-full flex-1 flex-col gap-2 overflow-y-auto overflow-x-hidden px-2">
|
|
147
|
+
{anchors?.length ? (
|
|
148
|
+
<Section paddedTop title="Pinned">
|
|
149
|
+
<ul className="space-y-1">
|
|
150
|
+
{anchors.map((anchor) => (
|
|
151
|
+
<li key={anchor.href}>
|
|
152
|
+
<a
|
|
153
|
+
className={cn(MENU_BUTTON_CLASS, "text-foreground")}
|
|
154
|
+
href={
|
|
155
|
+
anchor.href.startsWith("http")
|
|
156
|
+
? anchor.href
|
|
157
|
+
: toDocHref(anchor.href, basePath)
|
|
158
|
+
}
|
|
159
|
+
>
|
|
160
|
+
{anchor.label}
|
|
161
|
+
</a>
|
|
162
|
+
</li>
|
|
163
|
+
))}
|
|
164
|
+
</ul>
|
|
165
|
+
</Section>
|
|
166
|
+
) : null}
|
|
167
|
+
{entries.map((entry, index) => {
|
|
168
|
+
if (entry.type === "page") {
|
|
169
|
+
return (
|
|
170
|
+
<Section
|
|
171
|
+
key={entry.path}
|
|
172
|
+
paddedTop={index === 0 && !anchors?.length}
|
|
173
|
+
>
|
|
174
|
+
<ul>
|
|
175
|
+
<li>
|
|
176
|
+
<NavPageLink
|
|
177
|
+
basePath={basePath}
|
|
178
|
+
isActive={isActive(entry.path)}
|
|
179
|
+
item={entry}
|
|
180
|
+
/>
|
|
181
|
+
</li>
|
|
182
|
+
</ul>
|
|
183
|
+
</Section>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<Section
|
|
189
|
+
key={entry.title}
|
|
190
|
+
paddedTop={index === 0 && !anchors?.length}
|
|
191
|
+
title={entry.title}
|
|
192
|
+
>
|
|
193
|
+
<ul className="space-y-0.5">
|
|
194
|
+
{entry.items.map((item) => (
|
|
195
|
+
<li key={item.path}>
|
|
196
|
+
<NavPageLink
|
|
197
|
+
basePath={basePath}
|
|
198
|
+
isActive={isActive(item.path)}
|
|
199
|
+
item={item}
|
|
200
|
+
/>
|
|
201
|
+
</li>
|
|
202
|
+
))}
|
|
203
|
+
</ul>
|
|
204
|
+
</Section>
|
|
205
|
+
);
|
|
206
|
+
})}
|
|
207
|
+
<div className="sticky -bottom-1 z-10 h-16 shrink-0 bg-gradient-to-t from-background via-background/80 to-background/50 blur-xs" />
|
|
208
|
+
</div>
|
|
209
|
+
</aside>
|
|
210
|
+
);
|
|
211
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import type { TocItem } from "@/lib/toc";
|
|
4
|
+
|
|
5
|
+
export const DocToc = ({
|
|
6
|
+
toc,
|
|
7
|
+
contextualItems,
|
|
8
|
+
}: {
|
|
9
|
+
toc: TocItem[];
|
|
10
|
+
contextualItems?: ReactNode;
|
|
11
|
+
}) => {
|
|
12
|
+
if (!toc.length && !contextualItems) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<nav
|
|
18
|
+
aria-label="Table of contents"
|
|
19
|
+
className="sticky top-[calc(var(--header-height,4rem)+1px)] z-30 ml-auto hidden h-[90svh] w-56 shrink-0 flex-col gap-4 overflow-hidden overscroll-none pb-8 xl:flex"
|
|
20
|
+
>
|
|
21
|
+
<div className="no-scrollbar flex flex-col gap-8 overflow-y-auto px-8">
|
|
22
|
+
<div className="flex flex-col gap-2 p-4 pt-0 text-sm">
|
|
23
|
+
{toc.length > 0 ? (
|
|
24
|
+
<>
|
|
25
|
+
<p className="sticky top-0 h-6 bg-background font-medium text-muted-foreground text-xs">
|
|
26
|
+
On This Page
|
|
27
|
+
</p>
|
|
28
|
+
{toc.map((item) => (
|
|
29
|
+
<a
|
|
30
|
+
className="text-[0.8rem] text-muted-foreground no-underline transition-colors hover:text-foreground data-[depth=3]:pl-4 data-[depth=4]:pl-6"
|
|
31
|
+
data-depth={item.level}
|
|
32
|
+
href={`#${item.id}`}
|
|
33
|
+
key={item.id}
|
|
34
|
+
>
|
|
35
|
+
{item.title}
|
|
36
|
+
</a>
|
|
37
|
+
))}
|
|
38
|
+
</>
|
|
39
|
+
) : null}
|
|
40
|
+
{contextualItems}
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</nav>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { useCallback, useState } from "react";
|
|
5
|
+
import type { ReactNode } from "react";
|
|
6
|
+
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
8
|
+
import {
|
|
9
|
+
Popover,
|
|
10
|
+
PopoverContent,
|
|
11
|
+
PopoverTrigger,
|
|
12
|
+
} from "@/components/ui/popover";
|
|
13
|
+
import { getNavPageHref, getNavPageTitle } from "@/lib/navigation";
|
|
14
|
+
import type { NavEntry, NavPage, NavTab } from "@/lib/navigation";
|
|
15
|
+
import { isExternalHref, resolveHref, toDocHref } from "@/lib/routes";
|
|
16
|
+
import { cn } from "@/lib/utils";
|
|
17
|
+
|
|
18
|
+
const MobileLink = ({
|
|
19
|
+
href,
|
|
20
|
+
onOpenChange,
|
|
21
|
+
className,
|
|
22
|
+
children,
|
|
23
|
+
onClick,
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof Link> & {
|
|
26
|
+
onOpenChange?: (open: boolean) => void;
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
className?: string;
|
|
29
|
+
}) => {
|
|
30
|
+
const handleClick = useCallback(
|
|
31
|
+
(event: React.MouseEvent<HTMLAnchorElement>) => {
|
|
32
|
+
onClick?.(event);
|
|
33
|
+
onOpenChange?.(false);
|
|
34
|
+
},
|
|
35
|
+
[onClick, onOpenChange]
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Link
|
|
40
|
+
className={cn("flex items-center gap-2 text-2xl font-medium", className)}
|
|
41
|
+
href={href}
|
|
42
|
+
onClick={handleClick}
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
{children}
|
|
46
|
+
</Link>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const MobileNav = ({
|
|
51
|
+
entries,
|
|
52
|
+
globalLinks,
|
|
53
|
+
basePath,
|
|
54
|
+
tabs,
|
|
55
|
+
activeTabIndex,
|
|
56
|
+
className,
|
|
57
|
+
}: {
|
|
58
|
+
entries: NavEntry[];
|
|
59
|
+
globalLinks: { label: string; href: string }[];
|
|
60
|
+
basePath: string;
|
|
61
|
+
tabs?: NavTab[] | null;
|
|
62
|
+
activeTabIndex?: number;
|
|
63
|
+
className?: string;
|
|
64
|
+
}) => {
|
|
65
|
+
const [open, setOpen] = useState(false);
|
|
66
|
+
|
|
67
|
+
const renderPageLink = (page: NavPage) => {
|
|
68
|
+
const href = getNavPageHref(page, basePath);
|
|
69
|
+
const isExternal = Boolean(page.url && isExternalHref(page.url));
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<MobileLink
|
|
73
|
+
href={href}
|
|
74
|
+
key={page.path}
|
|
75
|
+
onOpenChange={setOpen}
|
|
76
|
+
rel={isExternal ? "noopener noreferrer" : undefined}
|
|
77
|
+
target={isExternal ? "_blank" : undefined}
|
|
78
|
+
>
|
|
79
|
+
{getNavPageTitle(page)}
|
|
80
|
+
</MobileLink>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Popover onOpenChange={setOpen} open={open}>
|
|
86
|
+
<PopoverTrigger asChild>
|
|
87
|
+
<Button
|
|
88
|
+
className={cn(
|
|
89
|
+
"extend-touch-target !p-0 h-8 touch-manipulation items-center justify-start gap-2.5 hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 active:bg-transparent dark:hover:bg-transparent",
|
|
90
|
+
className
|
|
91
|
+
)}
|
|
92
|
+
variant="ghost"
|
|
93
|
+
>
|
|
94
|
+
<div className="relative flex h-8 w-4 items-center justify-center">
|
|
95
|
+
<div className="relative size-4">
|
|
96
|
+
<span
|
|
97
|
+
className={cn(
|
|
98
|
+
"absolute left-0 block h-0.5 w-4 bg-foreground transition-all duration-100",
|
|
99
|
+
open ? "top-[0.4rem] -rotate-45" : "top-1"
|
|
100
|
+
)}
|
|
101
|
+
/>
|
|
102
|
+
<span
|
|
103
|
+
className={cn(
|
|
104
|
+
"absolute left-0 block h-0.5 w-4 bg-foreground transition-all duration-100",
|
|
105
|
+
open ? "top-[0.4rem] rotate-45" : "top-2.5"
|
|
106
|
+
)}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
<span className="sr-only">Toggle Menu</span>
|
|
110
|
+
</div>
|
|
111
|
+
<span className="flex h-8 items-center text-lg font-medium leading-none">
|
|
112
|
+
Menu
|
|
113
|
+
</span>
|
|
114
|
+
</Button>
|
|
115
|
+
</PopoverTrigger>
|
|
116
|
+
<PopoverContent
|
|
117
|
+
align="start"
|
|
118
|
+
alignOffset={-16}
|
|
119
|
+
className="no-scrollbar h-(--available-height) w-(--available-width) overflow-y-auto rounded-none border-none bg-background/90 p-0 shadow-none backdrop-blur duration-100 data-open:animate-none!"
|
|
120
|
+
side="bottom"
|
|
121
|
+
sideOffset={14}
|
|
122
|
+
>
|
|
123
|
+
<div className="flex flex-col gap-12 overflow-auto px-6 py-6">
|
|
124
|
+
{tabs?.length ? (
|
|
125
|
+
<div className="flex flex-col gap-4">
|
|
126
|
+
<div className="text-sm font-medium text-muted-foreground">
|
|
127
|
+
Sections
|
|
128
|
+
</div>
|
|
129
|
+
<div className="flex flex-col gap-3">
|
|
130
|
+
{tabs.map((tab, index) => {
|
|
131
|
+
const href =
|
|
132
|
+
(tab.href ? resolveHref(tab.href, basePath) : undefined) ??
|
|
133
|
+
(tab.slugPrefix
|
|
134
|
+
? toDocHref(tab.slugPrefix, basePath)
|
|
135
|
+
: undefined);
|
|
136
|
+
if (!href) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const isActive = index === activeTabIndex;
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<MobileLink
|
|
143
|
+
className={isActive ? "text-primary" : ""}
|
|
144
|
+
href={href}
|
|
145
|
+
key={tab.label}
|
|
146
|
+
onOpenChange={setOpen}
|
|
147
|
+
rel={
|
|
148
|
+
tab.href && isExternalHref(tab.href)
|
|
149
|
+
? "noopener noreferrer"
|
|
150
|
+
: undefined
|
|
151
|
+
}
|
|
152
|
+
target={
|
|
153
|
+
tab.href && isExternalHref(tab.href)
|
|
154
|
+
? "_blank"
|
|
155
|
+
: undefined
|
|
156
|
+
}
|
|
157
|
+
>
|
|
158
|
+
{tab.label}
|
|
159
|
+
</MobileLink>
|
|
160
|
+
);
|
|
161
|
+
})}
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
) : null}
|
|
165
|
+
{globalLinks.length > 0 ? (
|
|
166
|
+
<div className="flex flex-col gap-4">
|
|
167
|
+
<div className="text-sm font-medium text-muted-foreground">
|
|
168
|
+
Menu
|
|
169
|
+
</div>
|
|
170
|
+
<div className="flex flex-col gap-3">
|
|
171
|
+
{globalLinks.map((link) => (
|
|
172
|
+
<MobileLink
|
|
173
|
+
href={link.href}
|
|
174
|
+
key={link.label}
|
|
175
|
+
onOpenChange={setOpen}
|
|
176
|
+
rel="noopener noreferrer"
|
|
177
|
+
target="_blank"
|
|
178
|
+
>
|
|
179
|
+
{link.label}
|
|
180
|
+
</MobileLink>
|
|
181
|
+
))}
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
) : null}
|
|
185
|
+
{entries.map((entry) => {
|
|
186
|
+
if (entry.type === "page") {
|
|
187
|
+
return renderPageLink(entry);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<div className="flex flex-col gap-4" key={entry.title}>
|
|
192
|
+
<div className="text-sm font-medium text-muted-foreground">
|
|
193
|
+
{entry.title}
|
|
194
|
+
</div>
|
|
195
|
+
<div className="flex flex-col gap-3">
|
|
196
|
+
{entry.items.map((item) => renderPageLink(item))}
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
})}
|
|
201
|
+
</div>
|
|
202
|
+
</PopoverContent>
|
|
203
|
+
</Popover>
|
|
204
|
+
);
|
|
205
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import ArrowRightIcon from "blode-icons-react/icons/arrow-right";
|
|
2
|
+
import ArrowUpRightIcon from "blode-icons-react/icons/arrow-up-right";
|
|
3
|
+
import CheckIcon from "blode-icons-react/icons/check";
|
|
4
|
+
import CodeIcon from "blode-icons-react/icons/code";
|
|
5
|
+
import ConsoleIcon from "blode-icons-react/icons/console";
|
|
6
|
+
import FlagIcon from "blode-icons-react/icons/flag-1";
|
|
7
|
+
import FolderAddRightIcon from "blode-icons-react/icons/folder-add-right";
|
|
8
|
+
import GlobusIcon from "blode-icons-react/icons/globus";
|
|
9
|
+
import ImacIcon from "blode-icons-react/icons/imac";
|
|
10
|
+
import InfoIcon from "blode-icons-react/icons/info";
|
|
11
|
+
import KeyIcon from "blode-icons-react/icons/key";
|
|
12
|
+
import LockIcon from "blode-icons-react/icons/lock";
|
|
13
|
+
import MagnifyingGlassIcon from "blode-icons-react/icons/magnifying-glass";
|
|
14
|
+
import PlayIcon from "blode-icons-react/icons/play";
|
|
15
|
+
import PuzzleIcon from "blode-icons-react/icons/puzzle";
|
|
16
|
+
import RocketIcon from "blode-icons-react/icons/rocket";
|
|
17
|
+
import SparkleIcon from "blode-icons-react/icons/sparkle";
|
|
18
|
+
import StarIcon from "blode-icons-react/icons/star";
|
|
19
|
+
import TriangleExclamationIcon from "blode-icons-react/icons/triangle-exclamation";
|
|
20
|
+
import type { ComponentType, ReactElement, SVGProps } from "react";
|
|
21
|
+
|
|
22
|
+
import { cn } from "@/lib/utils";
|
|
23
|
+
|
|
24
|
+
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>;
|
|
25
|
+
interface DocIconProps {
|
|
26
|
+
icon: string;
|
|
27
|
+
color?: string;
|
|
28
|
+
size?: number;
|
|
29
|
+
className?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const DOC_ICON_MAP: Record<string, IconComponent> = {
|
|
33
|
+
"alert-triangle": TriangleExclamationIcon,
|
|
34
|
+
"arrow-right": ArrowRightIcon,
|
|
35
|
+
check: CheckIcon,
|
|
36
|
+
code: CodeIcon,
|
|
37
|
+
"external-link": ArrowUpRightIcon,
|
|
38
|
+
flag: FlagIcon,
|
|
39
|
+
"folder-plus": FolderAddRightIcon,
|
|
40
|
+
globe: GlobusIcon,
|
|
41
|
+
info: InfoIcon,
|
|
42
|
+
key: KeyIcon,
|
|
43
|
+
lock: LockIcon,
|
|
44
|
+
monitor: ImacIcon,
|
|
45
|
+
play: PlayIcon,
|
|
46
|
+
puzzle: PuzzleIcon,
|
|
47
|
+
rocket: RocketIcon,
|
|
48
|
+
search: MagnifyingGlassIcon,
|
|
49
|
+
sparkles: SparkleIcon,
|
|
50
|
+
star: StarIcon,
|
|
51
|
+
terminal: ConsoleIcon,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const getIconComponent = (icon: string): IconComponent | null =>
|
|
55
|
+
DOC_ICON_MAP[icon.trim().toLowerCase()] ?? null;
|
|
56
|
+
|
|
57
|
+
export const DocIcon = ({
|
|
58
|
+
icon,
|
|
59
|
+
color,
|
|
60
|
+
size = 16,
|
|
61
|
+
className,
|
|
62
|
+
}: DocIconProps): ReactElement => {
|
|
63
|
+
const IconComponent = getIconComponent(icon);
|
|
64
|
+
|
|
65
|
+
if (!IconComponent) {
|
|
66
|
+
return (
|
|
67
|
+
<span
|
|
68
|
+
aria-hidden
|
|
69
|
+
className={cn(
|
|
70
|
+
"inline-flex items-center justify-center font-medium uppercase leading-none",
|
|
71
|
+
className
|
|
72
|
+
)}
|
|
73
|
+
style={{
|
|
74
|
+
color: color ?? undefined,
|
|
75
|
+
fontSize: Math.max(10, Math.round(size * 0.55)),
|
|
76
|
+
height: size,
|
|
77
|
+
width: size,
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
{icon.slice(0, 2)}
|
|
81
|
+
</span>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<IconComponent
|
|
87
|
+
aria-hidden
|
|
88
|
+
className={cn("shrink-0", className)}
|
|
89
|
+
style={{
|
|
90
|
+
color: color ?? undefined,
|
|
91
|
+
height: size,
|
|
92
|
+
width: size,
|
|
93
|
+
}}
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
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 AccordionProps {
|
|
9
|
+
title: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
defaultOpen?: boolean;
|
|
12
|
+
id?: string;
|
|
13
|
+
icon?: ReactNode;
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Accordion = ({
|
|
18
|
+
title,
|
|
19
|
+
description,
|
|
20
|
+
defaultOpen = false,
|
|
21
|
+
id,
|
|
22
|
+
icon,
|
|
23
|
+
children,
|
|
24
|
+
}: AccordionProps) => {
|
|
25
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
26
|
+
const anchorId = id ?? title.toLowerCase().replaceAll(/\s+/g, "-");
|
|
27
|
+
|
|
28
|
+
const toggle = useCallback(() => setOpen((prev) => !prev), []);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="border-b border-border last:border-b-0" id={anchorId}>
|
|
32
|
+
<button
|
|
33
|
+
aria-expanded={open}
|
|
34
|
+
className="flex w-full items-center gap-3 py-4 text-left"
|
|
35
|
+
onClick={toggle}
|
|
36
|
+
type="button"
|
|
37
|
+
>
|
|
38
|
+
{icon ? (
|
|
39
|
+
<span className="flex size-5 shrink-0 items-center justify-center text-muted-foreground">
|
|
40
|
+
{icon}
|
|
41
|
+
</span>
|
|
42
|
+
) : null}
|
|
43
|
+
<div className="min-w-0 flex-1">
|
|
44
|
+
<div className="font-medium">{title}</div>
|
|
45
|
+
{description ? (
|
|
46
|
+
<div className="mt-0.5 text-sm text-muted-foreground">
|
|
47
|
+
{description}
|
|
48
|
+
</div>
|
|
49
|
+
) : null}
|
|
50
|
+
</div>
|
|
51
|
+
<svg
|
|
52
|
+
aria-hidden
|
|
53
|
+
className={cn(
|
|
54
|
+
"size-4 shrink-0 text-muted-foreground transition-transform duration-200",
|
|
55
|
+
open && "rotate-180"
|
|
56
|
+
)}
|
|
57
|
+
fill="none"
|
|
58
|
+
stroke="currentColor"
|
|
59
|
+
strokeWidth={2}
|
|
60
|
+
viewBox="0 0 24 24"
|
|
61
|
+
>
|
|
62
|
+
<path d="m6 9 6 6 6-6" strokeLinecap="round" strokeLinejoin="round" />
|
|
63
|
+
</svg>
|
|
64
|
+
</button>
|
|
65
|
+
<div
|
|
66
|
+
className={cn(
|
|
67
|
+
"grid transition-[grid-template-rows] duration-200",
|
|
68
|
+
open ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
<div className="overflow-hidden">
|
|
72
|
+
<div className="pb-4 text-sm text-muted-foreground">{children}</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const AccordionGroup = ({ children }: { children: ReactNode }) => (
|
|
80
|
+
<div className="my-4 divide-y divide-border rounded-xl border border-border px-4">
|
|
81
|
+
{children}
|
|
82
|
+
</div>
|
|
83
|
+
);
|