blodemd 0.0.15 → 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.
- package/README.md +26 -1
- package/dist/cli.mjs +217 -217
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -36
- package/dev-server/app/[[...slug]]/page.tsx +0 -140
- package/dev-server/app/blodemd-dev/invalidate/route.ts +0 -12
- package/dev-server/app/blodemd-dev/static/[...path]/route.ts +0 -32
- package/dev-server/app/blodemd-dev/version/route.ts +0 -14
- package/dev-server/app/blodemd-internal/proxy/route.ts +0 -86
- package/dev-server/app/error.tsx +0 -24
- package/dev-server/app/favicon.ico +0 -0
- package/dev-server/app/globals.css +0 -4
- package/dev-server/app/layout.tsx +0 -38
- package/dev-server/app/not-found.tsx +0 -18
- package/dev-server/app/search/route.ts +0 -17
- package/dev-server/components/dev-reload-script.tsx +0 -86
- package/dev-server/components/providers.tsx +0 -15
- package/dev-server/lib/dev-state.ts +0 -8
- package/dev-server/lib/local-content-source.ts +0 -103
- package/dev-server/lib/local-runtime.tsx +0 -555
- package/dev-server/next-env.d.ts +0 -5
- package/dev-server/next.config.js +0 -54
- package/dev-server/package.json +0 -61
- package/dev-server/postcss.config.mjs +0 -7
- package/dev-server/public/glide-variable.woff2 +0 -0
- package/dev-server/tsconfig.json +0 -50
- package/docs/app/globals.css +0 -471
- package/docs/components/animate-ui/primitives/buttons/button.tsx +0 -14
- package/docs/components/api/api-playground.tsx +0 -467
- package/docs/components/api/api-reference.tsx +0 -157
- package/docs/components/content/collection-index.tsx +0 -114
- package/docs/components/docs/contextual-menu.tsx +0 -495
- package/docs/components/docs/copy-page-menu.tsx +0 -364
- package/docs/components/docs/doc-header.tsx +0 -223
- package/docs/components/docs/doc-shell.tsx +0 -355
- package/docs/components/docs/doc-sidebar.tsx +0 -256
- package/docs/components/docs/doc-toc.tsx +0 -46
- package/docs/components/docs/mobile-nav.tsx +0 -195
- package/docs/components/docs/sidebar-active-highlight.tsx +0 -34
- package/docs/components/docs/sidebar-scroll-area.tsx +0 -63
- package/docs/components/git/repo-picker.tsx +0 -529
- package/docs/components/icons/doc-icon.tsx +0 -96
- package/docs/components/mdx/accordion.tsx +0 -83
- package/docs/components/mdx/agent-instructions.tsx +0 -17
- package/docs/components/mdx/badge.tsx +0 -79
- package/docs/components/mdx/callout.tsx +0 -88
- package/docs/components/mdx/card.tsx +0 -110
- package/docs/components/mdx/code-block.tsx +0 -80
- package/docs/components/mdx/code-group.tsx +0 -203
- package/docs/components/mdx/color.tsx +0 -87
- package/docs/components/mdx/columns.tsx +0 -25
- package/docs/components/mdx/expandable.tsx +0 -45
- package/docs/components/mdx/field-layout.tsx +0 -77
- package/docs/components/mdx/frame.tsx +0 -23
- package/docs/components/mdx/get-text-content.ts +0 -18
- package/docs/components/mdx/icon.tsx +0 -12
- package/docs/components/mdx/iframe.tsx +0 -62
- package/docs/components/mdx/index.tsx +0 -121
- package/docs/components/mdx/installer.tsx +0 -20
- package/docs/components/mdx/panel.tsx +0 -11
- package/docs/components/mdx/param-field.tsx +0 -56
- package/docs/components/mdx/preview.tsx +0 -36
- package/docs/components/mdx/prompt.tsx +0 -63
- package/docs/components/mdx/request-example.tsx +0 -27
- package/docs/components/mdx/response-field.tsx +0 -42
- package/docs/components/mdx/steps.tsx +0 -92
- package/docs/components/mdx/tabs.tsx +0 -193
- package/docs/components/mdx/tile.tsx +0 -43
- package/docs/components/mdx/tooltip.tsx +0 -71
- package/docs/components/mdx/tree.tsx +0 -120
- package/docs/components/mdx/type-table.tsx +0 -71
- package/docs/components/mdx/update.tsx +0 -44
- package/docs/components/mdx/video.tsx +0 -45
- package/docs/components/mdx/view.tsx +0 -66
- package/docs/components/posthog-provider.tsx +0 -42
- package/docs/components/providers.tsx +0 -15
- package/docs/components/tenant-analytics.tsx +0 -49
- package/docs/components/third-parties.tsx +0 -32
- package/docs/components/ui/badge.tsx +0 -61
- package/docs/components/ui/breadcrumb.tsx +0 -112
- package/docs/components/ui/button-group.tsx +0 -83
- package/docs/components/ui/button.tsx +0 -65
- package/docs/components/ui/card.tsx +0 -92
- package/docs/components/ui/command.tsx +0 -167
- package/docs/components/ui/copy-button.tsx +0 -129
- package/docs/components/ui/dialog.tsx +0 -146
- package/docs/components/ui/field.tsx +0 -237
- package/docs/components/ui/input.tsx +0 -23
- package/docs/components/ui/kbd.tsx +0 -98
- package/docs/components/ui/label.tsx +0 -22
- package/docs/components/ui/morph-icon.tsx +0 -79
- package/docs/components/ui/popover.tsx +0 -267
- package/docs/components/ui/search-dialog.tsx +0 -431
- package/docs/components/ui/search.tsx +0 -101
- package/docs/components/ui/separator.tsx +0 -26
- package/docs/components/ui/sheet.tsx +0 -133
- package/docs/components/ui/sidebar.tsx +0 -758
- package/docs/components/ui/skeleton.tsx +0 -11
- package/docs/components/ui/switch.tsx +0 -32
- package/docs/components/ui/tabs.tsx +0 -138
- package/docs/components/ui/theme-toggle.tsx +0 -62
- package/docs/components/ui/tooltip.tsx +0 -53
- package/docs/components/web-mcp.tsx +0 -115
- package/docs/lib/agent-skills.ts +0 -48
- package/docs/lib/api-client.ts +0 -72
- package/docs/lib/config.ts +0 -6
- package/docs/lib/content-root.ts +0 -33
- package/docs/lib/content-source.ts +0 -70
- package/docs/lib/contextual-options.ts +0 -222
- package/docs/lib/dashboard-session.ts +0 -167
- package/docs/lib/db.ts +0 -13
- package/docs/lib/docs-collection.ts +0 -7
- package/docs/lib/docs-runtime.tsx +0 -745
- package/docs/lib/edge-config.ts +0 -95
- package/docs/lib/env.ts +0 -23
- package/docs/lib/etag.ts +0 -22
- package/docs/lib/github-install.ts +0 -33
- package/docs/lib/llms-full.ts +0 -82
- package/docs/lib/marketing-markdown.ts +0 -79
- package/docs/lib/marketing-site.ts +0 -31
- package/docs/lib/mdx.ts +0 -98
- package/docs/lib/navigation.ts +0 -288
- package/docs/lib/openapi-proxy.ts +0 -88
- package/docs/lib/openapi.ts +0 -158
- package/docs/lib/platform-config.ts +0 -6
- package/docs/lib/project-authz.ts +0 -46
- package/docs/lib/routes.ts +0 -103
- package/docs/lib/server-cache.ts +0 -83
- package/docs/lib/shiki.ts +0 -44
- package/docs/lib/supabase.ts +0 -37
- package/docs/lib/tenancy.ts +0 -364
- package/docs/lib/tenant-headers.ts +0 -43
- package/docs/lib/tenant-static.ts +0 -705
- package/docs/lib/tenant-utility-context.ts +0 -62
- package/docs/lib/tenants.ts +0 -72
- package/docs/lib/theme.ts +0 -29
- package/docs/lib/time-ago.ts +0 -24
- package/docs/lib/toc.ts +0 -2
- package/docs/lib/use-mobile.ts +0 -19
- package/docs/lib/use-tab-observer.ts +0 -71
- package/docs/lib/utils.ts +0 -5
- package/packages/@repo/common/dist/index.d.ts +0 -16
- package/packages/@repo/common/dist/index.d.ts.map +0 -1
- package/packages/@repo/common/dist/index.js +0 -84
- package/packages/@repo/common/package.json +0 -34
- package/packages/@repo/common/src/index.ts +0 -101
- package/packages/@repo/contracts/dist/analytics.d.ts +0 -21
- package/packages/@repo/contracts/dist/analytics.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/analytics.js +0 -18
- package/packages/@repo/contracts/dist/dates.d.ts +0 -4
- package/packages/@repo/contracts/dist/dates.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/dates.js +0 -2
- package/packages/@repo/contracts/dist/deployment.d.ts +0 -71
- package/packages/@repo/contracts/dist/deployment.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/deployment.js +0 -46
- package/packages/@repo/contracts/dist/domain.d.ts +0 -94
- package/packages/@repo/contracts/dist/domain.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/domain.js +0 -36
- package/packages/@repo/contracts/dist/git.d.ts +0 -28
- package/packages/@repo/contracts/dist/git.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/git.js +0 -24
- package/packages/@repo/contracts/dist/ids.d.ts +0 -14
- package/packages/@repo/contracts/dist/ids.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/ids.js +0 -11
- package/packages/@repo/contracts/dist/index.d.ts +0 -11
- package/packages/@repo/contracts/dist/index.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/index.js +0 -12
- package/packages/@repo/contracts/dist/pagination.d.ts +0 -23
- package/packages/@repo/contracts/dist/pagination.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/pagination.js +0 -15
- package/packages/@repo/contracts/dist/project.d.ts +0 -42
- package/packages/@repo/contracts/dist/project.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/project.js +0 -25
- package/packages/@repo/contracts/dist/tenant.d.ts +0 -147
- package/packages/@repo/contracts/dist/tenant.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/tenant.js +0 -58
- package/packages/@repo/contracts/dist/user.d.ts +0 -9
- package/packages/@repo/contracts/dist/user.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/user.js +0 -9
- package/packages/@repo/contracts/package.json +0 -37
- package/packages/@repo/contracts/src/analytics.ts +0 -33
- package/packages/@repo/contracts/src/dates.ts +0 -4
- package/packages/@repo/contracts/src/deployment.ts +0 -73
- package/packages/@repo/contracts/src/domain.ts +0 -51
- package/packages/@repo/contracts/src/git.ts +0 -31
- package/packages/@repo/contracts/src/ids.ts +0 -23
- package/packages/@repo/contracts/src/index.ts +0 -12
- package/packages/@repo/contracts/src/pagination.ts +0 -21
- package/packages/@repo/contracts/src/project.ts +0 -32
- package/packages/@repo/contracts/src/tenant.ts +0 -94
- package/packages/@repo/contracts/src/user.ts +0 -12
- package/packages/@repo/models/dist/docs-config.d.ts +0 -961
- package/packages/@repo/models/dist/docs-config.d.ts.map +0 -1
- package/packages/@repo/models/dist/docs-config.js +0 -531
- package/packages/@repo/models/dist/index.d.ts +0 -3
- package/packages/@repo/models/dist/index.d.ts.map +0 -1
- package/packages/@repo/models/dist/index.js +0 -3
- package/packages/@repo/models/dist/tenant.d.ts +0 -37
- package/packages/@repo/models/dist/tenant.d.ts.map +0 -1
- package/packages/@repo/models/dist/tenant.js +0 -1
- package/packages/@repo/models/package.json +0 -37
- package/packages/@repo/models/src/docs-config.ts +0 -629
- package/packages/@repo/models/src/index.ts +0 -3
- package/packages/@repo/models/src/tenant.ts +0 -44
- package/packages/@repo/prebuild/dist/index.d.ts +0 -2
- package/packages/@repo/prebuild/dist/index.d.ts.map +0 -1
- package/packages/@repo/prebuild/dist/index.js +0 -2
- package/packages/@repo/prebuild/dist/openapi.d.ts +0 -43
- package/packages/@repo/prebuild/dist/openapi.d.ts.map +0 -1
- package/packages/@repo/prebuild/dist/openapi.js +0 -86
- package/packages/@repo/prebuild/package.json +0 -39
- package/packages/@repo/prebuild/src/index.ts +0 -2
- package/packages/@repo/prebuild/src/openapi.ts +0 -154
- package/packages/@repo/previewing/dist/blob-source.d.ts +0 -16
- package/packages/@repo/previewing/dist/blob-source.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/blob-source.js +0 -128
- package/packages/@repo/previewing/dist/config-loader.d.ts +0 -134
- package/packages/@repo/previewing/dist/config-loader.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/config-loader.js +0 -231
- package/packages/@repo/previewing/dist/constants.d.ts +0 -15
- package/packages/@repo/previewing/dist/constants.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/constants.js +0 -14
- package/packages/@repo/previewing/dist/content-index.d.ts +0 -7
- package/packages/@repo/previewing/dist/content-index.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/content-index.js +0 -291
- package/packages/@repo/previewing/dist/content-source.d.ts +0 -12
- package/packages/@repo/previewing/dist/content-source.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/content-source.js +0 -1
- package/packages/@repo/previewing/dist/fs-source.d.ts +0 -11
- package/packages/@repo/previewing/dist/fs-source.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/fs-source.js +0 -71
- package/packages/@repo/previewing/dist/index.d.ts +0 -16
- package/packages/@repo/previewing/dist/index.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/index.js +0 -13
- package/packages/@repo/previewing/dist/markdown/agent-markdown.d.ts +0 -3
- package/packages/@repo/previewing/dist/markdown/agent-markdown.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/markdown/agent-markdown.js +0 -176
- package/packages/@repo/previewing/dist/markdown/format.d.ts +0 -8
- package/packages/@repo/previewing/dist/markdown/format.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/markdown/format.js +0 -43
- package/packages/@repo/previewing/dist/markdown/links.d.ts +0 -3
- package/packages/@repo/previewing/dist/markdown/links.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/markdown/links.js +0 -31
- package/packages/@repo/previewing/dist/openapi-pages.d.ts +0 -7
- package/packages/@repo/previewing/dist/openapi-pages.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/openapi-pages.js +0 -200
- package/packages/@repo/previewing/dist/search-index.d.ts +0 -4
- package/packages/@repo/previewing/dist/search-index.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/search-index.js +0 -23
- package/packages/@repo/previewing/dist/serialization.d.ts +0 -13
- package/packages/@repo/previewing/dist/serialization.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/serialization.js +0 -108
- package/packages/@repo/previewing/dist/toc-index.d.ts +0 -5
- package/packages/@repo/previewing/dist/toc-index.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/toc-index.js +0 -53
- package/packages/@repo/previewing/dist/types.d.ts +0 -92
- package/packages/@repo/previewing/dist/types.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/types.js +0 -1
- package/packages/@repo/previewing/dist/utility-index.d.ts +0 -7
- package/packages/@repo/previewing/dist/utility-index.d.ts.map +0 -1
- package/packages/@repo/previewing/dist/utility-index.js +0 -201
- package/packages/@repo/previewing/package.json +0 -41
- package/packages/@repo/previewing/src/blob-source.ts +0 -185
- package/packages/@repo/previewing/src/config-loader.ts +0 -316
- package/packages/@repo/previewing/src/constants.ts +0 -16
- package/packages/@repo/previewing/src/content-index.ts +0 -384
- package/packages/@repo/previewing/src/content-source.ts +0 -12
- package/packages/@repo/previewing/src/fs-source.ts +0 -103
- package/packages/@repo/previewing/src/index.ts +0 -71
- package/packages/@repo/previewing/src/markdown/agent-markdown.ts +0 -254
- package/packages/@repo/previewing/src/markdown/format.ts +0 -63
- package/packages/@repo/previewing/src/markdown/links.ts +0 -46
- package/packages/@repo/previewing/src/openapi-pages.ts +0 -319
- package/packages/@repo/previewing/src/search-index.ts +0 -38
- package/packages/@repo/previewing/src/serialization.ts +0 -191
- package/packages/@repo/previewing/src/toc-index.ts +0 -72
- package/packages/@repo/previewing/src/types.ts +0 -105
- package/packages/@repo/previewing/src/utility-index.ts +0 -304
- package/packages/@repo/validation/dist/index.d.ts +0 -12
- package/packages/@repo/validation/dist/index.d.ts.map +0 -1
- package/packages/@repo/validation/dist/index.js +0 -30
- package/packages/@repo/validation/package.json +0 -38
- package/packages/@repo/validation/src/blodemd-docs-schema.json +0 -3575
- package/packages/@repo/validation/src/index.ts +0 -59
- package/scripts/prepare-package.mjs +0 -53
- package/scripts/repo-packages.mjs +0 -8
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { slugify } from "@repo/common";
|
|
4
|
-
import {
|
|
5
|
-
CheckIcon,
|
|
6
|
-
ChevronDownSmallIcon,
|
|
7
|
-
ClaudeaiIcon,
|
|
8
|
-
CopySimpleIcon,
|
|
9
|
-
FileTextIcon,
|
|
10
|
-
OpenaiIcon,
|
|
11
|
-
} from "blode-icons-react";
|
|
12
|
-
import type React from "react";
|
|
13
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
Popover,
|
|
17
|
-
PopoverContent,
|
|
18
|
-
PopoverTrigger,
|
|
19
|
-
} from "@/components/ui/popover";
|
|
20
|
-
|
|
21
|
-
interface CopyPageMenuProps {
|
|
22
|
-
content?: string;
|
|
23
|
-
contentUrl?: string;
|
|
24
|
-
title: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
type CopyStatus = "copied" | "error" | "idle";
|
|
28
|
-
|
|
29
|
-
const MARKDOWN_ACCEPT = "text/markdown,text/plain;q=0.9,*/*;q=0.8";
|
|
30
|
-
|
|
31
|
-
const LEADING_H1_REGEX = /^#\s+([^\r\n]+)(?:\r?\n(?:\r?\n)?)?/;
|
|
32
|
-
|
|
33
|
-
const stripMatchingLeadingH1 = (source: string, title: string) => {
|
|
34
|
-
const trimmed = source.trimStart();
|
|
35
|
-
const match = LEADING_H1_REGEX.exec(trimmed);
|
|
36
|
-
if (!match) {
|
|
37
|
-
return trimmed.trim();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const [headingLine = "", headingTitle = ""] = match;
|
|
41
|
-
if (slugify(headingTitle) !== slugify(title)) {
|
|
42
|
-
return trimmed.trim();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return trimmed.slice(headingLine.length).trim();
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const formatMarkdownForCopy = (source: string, title: string) => {
|
|
49
|
-
const content = stripMatchingLeadingH1(source, title);
|
|
50
|
-
if (!content) {
|
|
51
|
-
return `# ${title}`;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return `# ${title}\n\n${content}`;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const getCopyLabel = (copyStatus: CopyStatus) => {
|
|
58
|
-
switch (copyStatus) {
|
|
59
|
-
case "copied": {
|
|
60
|
-
return "Copied";
|
|
61
|
-
}
|
|
62
|
-
case "error": {
|
|
63
|
-
return "Copy failed";
|
|
64
|
-
}
|
|
65
|
-
default: {
|
|
66
|
-
return "Copy page";
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const getCopyDescription = (copyStatus: CopyStatus) => {
|
|
72
|
-
switch (copyStatus) {
|
|
73
|
-
case "copied": {
|
|
74
|
-
return "Copied page markdown to clipboard";
|
|
75
|
-
}
|
|
76
|
-
case "error": {
|
|
77
|
-
return "Clipboard access was blocked or the page markdown could not be loaded";
|
|
78
|
-
}
|
|
79
|
-
default: {
|
|
80
|
-
return "Copy page as Markdown for LLMs";
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const getCopyIcon = (copyStatus: CopyStatus) =>
|
|
86
|
-
copyStatus === "copied" ? CheckIcon : CopySimpleIcon;
|
|
87
|
-
|
|
88
|
-
const MenuItem = ({
|
|
89
|
-
children,
|
|
90
|
-
href,
|
|
91
|
-
onSelect,
|
|
92
|
-
}: {
|
|
93
|
-
children: React.ReactNode;
|
|
94
|
-
href?: string;
|
|
95
|
-
onSelect?: () => Promise<void> | void;
|
|
96
|
-
}) =>
|
|
97
|
-
href ? (
|
|
98
|
-
<a
|
|
99
|
-
className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm transition-colors hover:bg-secondary/25 focus-visible:bg-secondary/25 focus-visible:outline-none"
|
|
100
|
-
href={href}
|
|
101
|
-
onClick={onSelect}
|
|
102
|
-
rel="noopener noreferrer"
|
|
103
|
-
target="_blank"
|
|
104
|
-
>
|
|
105
|
-
{children}
|
|
106
|
-
</a>
|
|
107
|
-
) : (
|
|
108
|
-
<button
|
|
109
|
-
className="flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm transition-colors hover:bg-secondary/25 focus-visible:bg-secondary/25 focus-visible:outline-none"
|
|
110
|
-
onClick={onSelect}
|
|
111
|
-
type="button"
|
|
112
|
-
>
|
|
113
|
-
{children}
|
|
114
|
-
</button>
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
const MenuIcon = ({ children }: { children: React.ReactNode }) => (
|
|
118
|
-
<div className="flex size-8 shrink-0 items-center justify-center rounded-md border border-border">
|
|
119
|
-
{children}
|
|
120
|
-
</div>
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const ExternalArrow = () => (
|
|
124
|
-
<svg
|
|
125
|
-
aria-hidden="true"
|
|
126
|
-
className="ml-1 inline-block size-3"
|
|
127
|
-
fill="none"
|
|
128
|
-
stroke="currentColor"
|
|
129
|
-
strokeLinecap="round"
|
|
130
|
-
strokeLinejoin="round"
|
|
131
|
-
strokeWidth={2}
|
|
132
|
-
viewBox="0 0 24 24"
|
|
133
|
-
>
|
|
134
|
-
<path d="M7 17L17 7M7 7h10v10" />
|
|
135
|
-
</svg>
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
export const CopyPageMenu = ({
|
|
139
|
-
content,
|
|
140
|
-
contentUrl,
|
|
141
|
-
title,
|
|
142
|
-
}: CopyPageMenuProps) => {
|
|
143
|
-
const resetTimerRef = useRef<number | null>(null);
|
|
144
|
-
const [copyStatus, setCopyStatus] = useState<CopyStatus>("idle");
|
|
145
|
-
const [fetchedContent, setFetchedContent] = useState<string | null>(null);
|
|
146
|
-
const [menuOpen, setMenuOpen] = useState(false);
|
|
147
|
-
|
|
148
|
-
const pageUrl = useMemo(
|
|
149
|
-
() =>
|
|
150
|
-
typeof window === "undefined"
|
|
151
|
-
? ""
|
|
152
|
-
: new URL(contentUrl ?? window.location.href, window.location.href)
|
|
153
|
-
.href,
|
|
154
|
-
[contentUrl]
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
useEffect(
|
|
158
|
-
() => () => {
|
|
159
|
-
if (resetTimerRef.current !== null) {
|
|
160
|
-
window.clearTimeout(resetTimerRef.current);
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
[]
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
useEffect(() => {
|
|
167
|
-
if (content || !contentUrl) {
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const controller = new AbortController();
|
|
172
|
-
|
|
173
|
-
const prefetch = async () => {
|
|
174
|
-
try {
|
|
175
|
-
const res = await fetch(contentUrl, {
|
|
176
|
-
headers: { accept: MARKDOWN_ACCEPT },
|
|
177
|
-
signal: controller.signal,
|
|
178
|
-
});
|
|
179
|
-
const text = res.ok ? await res.text() : null;
|
|
180
|
-
if (text) {
|
|
181
|
-
setFetchedContent(text);
|
|
182
|
-
}
|
|
183
|
-
} catch {
|
|
184
|
-
// Ignore prefetch failures — the copy handler retries on demand
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
prefetch();
|
|
189
|
-
|
|
190
|
-
return () => controller.abort();
|
|
191
|
-
}, [content, contentUrl]);
|
|
192
|
-
|
|
193
|
-
const closeMenu = useCallback(() => {
|
|
194
|
-
setMenuOpen(false);
|
|
195
|
-
}, []);
|
|
196
|
-
|
|
197
|
-
const setTemporaryCopyStatus = useCallback(
|
|
198
|
-
(nextStatus: "copied" | "error") => {
|
|
199
|
-
if (resetTimerRef.current !== null) {
|
|
200
|
-
window.clearTimeout(resetTimerRef.current);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
setCopyStatus(nextStatus);
|
|
204
|
-
resetTimerRef.current = window.setTimeout(() => {
|
|
205
|
-
setCopyStatus("idle");
|
|
206
|
-
resetTimerRef.current = null;
|
|
207
|
-
}, 2000);
|
|
208
|
-
},
|
|
209
|
-
[]
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
const getContent = useCallback(async () => {
|
|
213
|
-
const resolvedContent = content ?? fetchedContent;
|
|
214
|
-
if (resolvedContent) {
|
|
215
|
-
return resolvedContent;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (!contentUrl) {
|
|
219
|
-
return "";
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const response = await fetch(contentUrl, {
|
|
223
|
-
headers: { accept: MARKDOWN_ACCEPT },
|
|
224
|
-
});
|
|
225
|
-
if (!response.ok) {
|
|
226
|
-
throw new Error(`Failed to load page markdown: ${response.status}`);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const nextContent = await response.text();
|
|
230
|
-
setFetchedContent(nextContent);
|
|
231
|
-
return nextContent;
|
|
232
|
-
}, [content, contentUrl, fetchedContent]);
|
|
233
|
-
|
|
234
|
-
const handleCopy = useCallback(async () => {
|
|
235
|
-
try {
|
|
236
|
-
// Use ClipboardItem with a promise-based blob so the clipboard "slot"
|
|
237
|
-
// is claimed synchronously during the tap gesture. iOS Safari revokes
|
|
238
|
-
// user-activation if an async boundary (e.g. a network fetch) sits
|
|
239
|
-
// between the tap and the clipboard call, so the old writeText path
|
|
240
|
-
// fails when tapped before the pre-fetch finishes.
|
|
241
|
-
if (typeof ClipboardItem !== "undefined" && navigator.clipboard.write) {
|
|
242
|
-
const blobPromise = (async () => {
|
|
243
|
-
const text = await getContent();
|
|
244
|
-
const markdown = formatMarkdownForCopy(text, title);
|
|
245
|
-
return new Blob([markdown], { type: "text/plain" });
|
|
246
|
-
})();
|
|
247
|
-
const item = new ClipboardItem({ "text/plain": blobPromise });
|
|
248
|
-
await navigator.clipboard.write([item]);
|
|
249
|
-
} else {
|
|
250
|
-
const nextContent = await getContent();
|
|
251
|
-
const markdown = formatMarkdownForCopy(nextContent, title);
|
|
252
|
-
await navigator.clipboard.writeText(markdown);
|
|
253
|
-
}
|
|
254
|
-
setTemporaryCopyStatus("copied");
|
|
255
|
-
closeMenu();
|
|
256
|
-
} catch {
|
|
257
|
-
setTemporaryCopyStatus("error");
|
|
258
|
-
}
|
|
259
|
-
}, [closeMenu, getContent, setTemporaryCopyStatus, title]);
|
|
260
|
-
|
|
261
|
-
const chatgptUrl = pageUrl
|
|
262
|
-
? `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read from ${pageUrl} so I can ask questions about it.`)}`
|
|
263
|
-
: undefined;
|
|
264
|
-
const claudeUrl = pageUrl
|
|
265
|
-
? `https://claude.ai/new?q=${encodeURIComponent(`Read from ${pageUrl} so I can ask questions about it.`)}`
|
|
266
|
-
: undefined;
|
|
267
|
-
const copyLabel = getCopyLabel(copyStatus);
|
|
268
|
-
const copyDescription = getCopyDescription(copyStatus);
|
|
269
|
-
const CopyIcon = getCopyIcon(copyStatus);
|
|
270
|
-
|
|
271
|
-
return (
|
|
272
|
-
<Popover onOpenChange={setMenuOpen} open={menuOpen}>
|
|
273
|
-
<div className="flex shrink-0 items-center">
|
|
274
|
-
<button
|
|
275
|
-
className="inline-flex items-center gap-2 rounded-l-xl border border-r-0 border-border px-3 py-1.5 text-sm font-medium transition-colors hover:bg-secondary/25"
|
|
276
|
-
onClick={handleCopy}
|
|
277
|
-
type="button"
|
|
278
|
-
>
|
|
279
|
-
<CopyIcon aria-hidden="true" className="size-[18px]" />
|
|
280
|
-
<span>{copyLabel}</span>
|
|
281
|
-
</button>
|
|
282
|
-
|
|
283
|
-
<PopoverTrigger asChild>
|
|
284
|
-
<button
|
|
285
|
-
aria-expanded={menuOpen}
|
|
286
|
-
aria-label="More actions"
|
|
287
|
-
className="inline-flex items-center self-stretch rounded-r-xl border border-border px-2 transition-colors hover:bg-secondary/25"
|
|
288
|
-
type="button"
|
|
289
|
-
>
|
|
290
|
-
<ChevronDownSmallIcon
|
|
291
|
-
aria-hidden="true"
|
|
292
|
-
className="size-[18px] text-muted-foreground"
|
|
293
|
-
/>
|
|
294
|
-
</button>
|
|
295
|
-
</PopoverTrigger>
|
|
296
|
-
</div>
|
|
297
|
-
|
|
298
|
-
<PopoverContent align="end" className="w-[280px] rounded-xl p-1">
|
|
299
|
-
{contentUrl ? (
|
|
300
|
-
<MenuItem href={contentUrl} onSelect={closeMenu}>
|
|
301
|
-
<MenuIcon>
|
|
302
|
-
<FileTextIcon aria-hidden="true" className="size-[18px]" />
|
|
303
|
-
</MenuIcon>
|
|
304
|
-
<div className="flex-1">
|
|
305
|
-
<div className="font-medium">
|
|
306
|
-
View as markdown
|
|
307
|
-
<ExternalArrow />
|
|
308
|
-
</div>
|
|
309
|
-
<div className="text-xs text-muted-foreground">
|
|
310
|
-
Open the raw .md source in a new tab
|
|
311
|
-
</div>
|
|
312
|
-
</div>
|
|
313
|
-
</MenuItem>
|
|
314
|
-
) : null}
|
|
315
|
-
|
|
316
|
-
<MenuItem onSelect={handleCopy}>
|
|
317
|
-
<MenuIcon>
|
|
318
|
-
<CopyIcon aria-hidden="true" className="size-[18px]" />
|
|
319
|
-
</MenuIcon>
|
|
320
|
-
<div>
|
|
321
|
-
<div className="font-medium">{copyLabel}</div>
|
|
322
|
-
<div className="text-xs text-muted-foreground">
|
|
323
|
-
{copyDescription}
|
|
324
|
-
</div>
|
|
325
|
-
</div>
|
|
326
|
-
</MenuItem>
|
|
327
|
-
|
|
328
|
-
{chatgptUrl ? (
|
|
329
|
-
<MenuItem href={chatgptUrl} onSelect={closeMenu}>
|
|
330
|
-
<MenuIcon>
|
|
331
|
-
<OpenaiIcon aria-hidden="true" className="size-[18px]" />
|
|
332
|
-
</MenuIcon>
|
|
333
|
-
<div className="flex-1">
|
|
334
|
-
<div className="font-medium">
|
|
335
|
-
Open in ChatGPT
|
|
336
|
-
<ExternalArrow />
|
|
337
|
-
</div>
|
|
338
|
-
<div className="text-xs text-muted-foreground">
|
|
339
|
-
Ask questions about this page
|
|
340
|
-
</div>
|
|
341
|
-
</div>
|
|
342
|
-
</MenuItem>
|
|
343
|
-
) : null}
|
|
344
|
-
|
|
345
|
-
{claudeUrl ? (
|
|
346
|
-
<MenuItem href={claudeUrl} onSelect={closeMenu}>
|
|
347
|
-
<MenuIcon>
|
|
348
|
-
<ClaudeaiIcon aria-hidden="true" className="size-[18px]" />
|
|
349
|
-
</MenuIcon>
|
|
350
|
-
<div className="flex-1">
|
|
351
|
-
<div className="font-medium">
|
|
352
|
-
Open in Claude
|
|
353
|
-
<ExternalArrow />
|
|
354
|
-
</div>
|
|
355
|
-
<div className="text-xs text-muted-foreground">
|
|
356
|
-
Ask questions about this page
|
|
357
|
-
</div>
|
|
358
|
-
</div>
|
|
359
|
-
</MenuItem>
|
|
360
|
-
) : null}
|
|
361
|
-
</PopoverContent>
|
|
362
|
-
</Popover>
|
|
363
|
-
);
|
|
364
|
-
};
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import type { SiteConfig } from "@repo/models";
|
|
2
|
-
import Image from "next/image";
|
|
3
|
-
import Link from "next/link";
|
|
4
|
-
|
|
5
|
-
import { MobileNav } from "@/components/docs/mobile-nav";
|
|
6
|
-
import { Search } from "@/components/ui/search";
|
|
7
|
-
import { ThemeToggle } from "@/components/ui/theme-toggle";
|
|
8
|
-
import type { NavEntry, NavTab } from "@/lib/navigation";
|
|
9
|
-
import { isExternalHref, resolveHref, toDocHref } from "@/lib/routes";
|
|
10
|
-
import { cn } from "@/lib/utils";
|
|
11
|
-
|
|
12
|
-
const EMPTY_NAV: NavEntry[] = [];
|
|
13
|
-
|
|
14
|
-
const Dropdown = ({
|
|
15
|
-
label,
|
|
16
|
-
items,
|
|
17
|
-
basePath,
|
|
18
|
-
}: {
|
|
19
|
-
label: string;
|
|
20
|
-
items: { label: string; url: string }[];
|
|
21
|
-
basePath: string;
|
|
22
|
-
}) => {
|
|
23
|
-
if (!items.length) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
return (
|
|
27
|
-
<details className="relative">
|
|
28
|
-
<summary className="cursor-pointer rounded-full border border-border bg-background px-3 py-1.5 text-sm">
|
|
29
|
-
{label}
|
|
30
|
-
</summary>
|
|
31
|
-
<div className="absolute right-0 top-11 z-20 grid min-w-36 overflow-hidden rounded-xl border border-border bg-popover shadow-popover">
|
|
32
|
-
{items.map((item) => {
|
|
33
|
-
const href = resolveHref(item.url, basePath);
|
|
34
|
-
const isExternal = isExternalHref(item.url);
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<Link
|
|
38
|
-
className="px-3 py-2 hover:bg-accent"
|
|
39
|
-
href={href}
|
|
40
|
-
key={item.label}
|
|
41
|
-
rel={isExternal ? "noopener noreferrer" : undefined}
|
|
42
|
-
target={isExternal ? "_blank" : undefined}
|
|
43
|
-
>
|
|
44
|
-
{item.label}
|
|
45
|
-
</Link>
|
|
46
|
-
);
|
|
47
|
-
})}
|
|
48
|
-
</div>
|
|
49
|
-
</details>
|
|
50
|
-
);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const HeaderTabs = ({
|
|
54
|
-
tabs,
|
|
55
|
-
activeTabIndex = 0,
|
|
56
|
-
basePath,
|
|
57
|
-
}: {
|
|
58
|
-
tabs: NavTab[];
|
|
59
|
-
activeTabIndex?: number;
|
|
60
|
-
basePath: string;
|
|
61
|
-
}) => (
|
|
62
|
-
<nav
|
|
63
|
-
aria-label="Navigation tabs"
|
|
64
|
-
className="ml-4 hidden items-center gap-0.5 lg:flex"
|
|
65
|
-
>
|
|
66
|
-
{tabs.map((tab, index) => {
|
|
67
|
-
const isActive = index === activeTabIndex;
|
|
68
|
-
const href =
|
|
69
|
-
(tab.href ? resolveHref(tab.href, basePath) : undefined) ??
|
|
70
|
-
(tab.slugPrefix ? toDocHref(tab.slugPrefix, basePath) : undefined);
|
|
71
|
-
|
|
72
|
-
if (!href) {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const isExternal = Boolean(tab.href && isExternalHref(tab.href));
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<Link
|
|
80
|
-
className={cn(
|
|
81
|
-
"relative px-2.5 py-1.5 text-sm transition-colors",
|
|
82
|
-
isActive
|
|
83
|
-
? "font-medium text-primary"
|
|
84
|
-
: "text-muted-foreground hover:text-foreground"
|
|
85
|
-
)}
|
|
86
|
-
href={href}
|
|
87
|
-
key={tab.label}
|
|
88
|
-
rel={isExternal ? "noopener noreferrer" : undefined}
|
|
89
|
-
target={isExternal ? "_blank" : undefined}
|
|
90
|
-
>
|
|
91
|
-
{tab.label}
|
|
92
|
-
{isActive ? (
|
|
93
|
-
<span className="absolute inset-x-1 -bottom-3.5 h-0.5 rounded-full bg-primary" />
|
|
94
|
-
) : null}
|
|
95
|
-
</Link>
|
|
96
|
-
);
|
|
97
|
-
})}
|
|
98
|
-
</nav>
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// oxlint-disable-next-line eslint/complexity
|
|
102
|
-
export const DocHeader = ({
|
|
103
|
-
config,
|
|
104
|
-
basePath,
|
|
105
|
-
tabs,
|
|
106
|
-
activeTabIndex,
|
|
107
|
-
nav = EMPTY_NAV,
|
|
108
|
-
}: {
|
|
109
|
-
config: SiteConfig;
|
|
110
|
-
basePath: string;
|
|
111
|
-
tabs?: NavTab[] | null;
|
|
112
|
-
activeTabIndex?: number;
|
|
113
|
-
nav?: NavEntry[];
|
|
114
|
-
}) => {
|
|
115
|
-
const globalLinks = config.navigation?.global?.links ?? [];
|
|
116
|
-
const versions = config.navigation?.versions ?? [];
|
|
117
|
-
const languages = config.navigation?.languages ?? [];
|
|
118
|
-
const [primaryVersion] = versions;
|
|
119
|
-
const [primaryLanguage] = languages;
|
|
120
|
-
const searchDisabled = config.features?.search === false;
|
|
121
|
-
const themeToggleDisabled = config.features?.themeToggle === false;
|
|
122
|
-
const logoHref = config.logo?.href ?? toDocHref("index", basePath);
|
|
123
|
-
const logoIsExternal = Boolean(
|
|
124
|
-
config.logo?.href && isExternalHref(config.logo.href)
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<header
|
|
129
|
-
className="sticky top-0 z-50 w-full bg-background"
|
|
130
|
-
data-markdown-ignore=""
|
|
131
|
-
>
|
|
132
|
-
<div className="container-wrapper px-4 lg:px-8">
|
|
133
|
-
<div className="flex h-(--header-height) items-center">
|
|
134
|
-
<MobileNav
|
|
135
|
-
activeTabIndex={activeTabIndex}
|
|
136
|
-
basePath={basePath}
|
|
137
|
-
className="flex lg:hidden"
|
|
138
|
-
entries={nav}
|
|
139
|
-
globalLinks={globalLinks}
|
|
140
|
-
tabs={tabs}
|
|
141
|
-
/>
|
|
142
|
-
<Link
|
|
143
|
-
className="flex items-center gap-2.5"
|
|
144
|
-
href={logoHref}
|
|
145
|
-
rel={logoIsExternal ? "noopener noreferrer" : undefined}
|
|
146
|
-
target={logoIsExternal ? "_blank" : undefined}
|
|
147
|
-
>
|
|
148
|
-
{config.logo?.light ? (
|
|
149
|
-
<Image
|
|
150
|
-
alt={config.logo.alt ?? config.name}
|
|
151
|
-
className="dark:hidden"
|
|
152
|
-
data-logo="light"
|
|
153
|
-
height={32}
|
|
154
|
-
loading="eager"
|
|
155
|
-
src={config.logo.light}
|
|
156
|
-
unoptimized
|
|
157
|
-
width={140}
|
|
158
|
-
/>
|
|
159
|
-
) : null}
|
|
160
|
-
{config.logo?.dark ? (
|
|
161
|
-
<Image
|
|
162
|
-
alt={config.logo.alt ?? config.name}
|
|
163
|
-
className="hidden dark:inline-block"
|
|
164
|
-
data-logo="dark"
|
|
165
|
-
height={32}
|
|
166
|
-
loading="eager"
|
|
167
|
-
src={config.logo.dark}
|
|
168
|
-
unoptimized
|
|
169
|
-
width={140}
|
|
170
|
-
/>
|
|
171
|
-
) : null}
|
|
172
|
-
{config.logo?.light || config.logo?.dark ? null : (
|
|
173
|
-
<span className="text-xl font-bold">{config.name}</span>
|
|
174
|
-
)}
|
|
175
|
-
</Link>
|
|
176
|
-
{tabs?.length ? (
|
|
177
|
-
<HeaderTabs
|
|
178
|
-
activeTabIndex={activeTabIndex}
|
|
179
|
-
basePath={basePath}
|
|
180
|
-
tabs={tabs}
|
|
181
|
-
/>
|
|
182
|
-
) : null}
|
|
183
|
-
<nav
|
|
184
|
-
aria-label="External links"
|
|
185
|
-
className="ml-1 hidden items-center gap-0 text-sm text-muted-foreground lg:flex"
|
|
186
|
-
>
|
|
187
|
-
{globalLinks.map((link) => (
|
|
188
|
-
<a
|
|
189
|
-
className="rounded-lg px-2.5 py-1.5 transition-colors hover:bg-muted hover:text-foreground"
|
|
190
|
-
href={link.href}
|
|
191
|
-
key={link.label}
|
|
192
|
-
rel="noopener noreferrer"
|
|
193
|
-
target="_blank"
|
|
194
|
-
>
|
|
195
|
-
{link.label}
|
|
196
|
-
</a>
|
|
197
|
-
))}
|
|
198
|
-
</nav>
|
|
199
|
-
<div className="ml-auto flex items-center gap-2 md:flex-1 md:justify-end">
|
|
200
|
-
{searchDisabled ? null : (
|
|
201
|
-
<Search basePath={basePath} key={basePath} />
|
|
202
|
-
)}
|
|
203
|
-
{primaryVersion ? (
|
|
204
|
-
<Dropdown
|
|
205
|
-
basePath={basePath}
|
|
206
|
-
items={versions}
|
|
207
|
-
label={primaryVersion.label}
|
|
208
|
-
/>
|
|
209
|
-
) : null}
|
|
210
|
-
{primaryLanguage ? (
|
|
211
|
-
<Dropdown
|
|
212
|
-
basePath={basePath}
|
|
213
|
-
items={languages}
|
|
214
|
-
label={primaryLanguage.label}
|
|
215
|
-
/>
|
|
216
|
-
) : null}
|
|
217
|
-
{themeToggleDisabled ? null : <ThemeToggle />}
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
</header>
|
|
222
|
-
);
|
|
223
|
-
};
|