blodemd 0.0.13 → 0.0.15
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 +1 -1
- package/dev-server/lib/local-runtime.tsx +12 -15
- package/dev-server/package.json +5 -1
- package/dist/cli.mjs +1211 -983
- package/dist/cli.mjs.map +1 -1
- package/docs/app/globals.css +1 -1
- package/docs/components/api/api-playground.tsx +2 -5
- package/docs/components/api/api-reference.tsx +29 -3
- package/docs/components/docs/contextual-menu.tsx +37 -33
- package/docs/components/docs/copy-page-menu.tsx +18 -0
- package/docs/components/docs/doc-header.tsx +4 -1
- package/docs/components/docs/doc-shell.tsx +48 -18
- package/docs/components/docs/doc-sidebar.tsx +131 -89
- package/docs/components/docs/doc-toc.tsx +1 -0
- package/docs/components/docs/mobile-nav.tsx +72 -68
- package/docs/components/docs/sidebar-active-highlight.tsx +34 -0
- package/docs/components/docs/sidebar-scroll-area.tsx +9 -4
- package/docs/components/git/repo-picker.tsx +5 -2
- package/docs/components/mdx/index.tsx +31 -21
- package/docs/components/posthog-provider.tsx +42 -0
- package/docs/components/tenant-analytics.tsx +49 -0
- package/docs/components/third-parties.tsx +4 -1
- package/docs/components/ui/copy-button.tsx +16 -2
- package/docs/components/ui/search-dialog.tsx +431 -0
- package/docs/components/ui/search.tsx +44 -427
- package/docs/components/web-mcp.tsx +115 -0
- package/docs/lib/agent-skills.ts +48 -0
- package/docs/lib/config.ts +4 -5
- package/docs/lib/docs-collection.ts +7 -22
- package/docs/lib/docs-runtime.tsx +160 -10
- package/docs/lib/llms-full.ts +82 -0
- package/docs/lib/marketing-markdown.ts +79 -0
- package/docs/lib/marketing-site.ts +31 -0
- package/docs/lib/mdx.ts +17 -6
- package/docs/lib/routes.ts +19 -3
- package/docs/lib/shiki.ts +4 -0
- package/docs/lib/tenancy.ts +15 -2
- package/docs/lib/tenant-headers.ts +29 -0
- package/docs/lib/tenant-static.ts +73 -99
- package/package.json +8 -5
- package/packages/@repo/common/package.json +1 -1
- package/packages/@repo/contracts/dist/analytics.d.ts +21 -0
- package/packages/@repo/contracts/dist/analytics.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/analytics.js +18 -0
- package/packages/@repo/contracts/dist/deployment.js +1 -1
- package/packages/@repo/contracts/dist/git.js +2 -2
- package/packages/@repo/contracts/dist/ids.d.ts.map +1 -1
- package/packages/@repo/contracts/dist/ids.js +2 -1
- package/packages/@repo/contracts/dist/index.d.ts +1 -0
- package/packages/@repo/contracts/dist/index.d.ts.map +1 -1
- package/packages/@repo/contracts/dist/index.js +1 -0
- package/packages/@repo/contracts/dist/project.d.ts +18 -1
- package/packages/@repo/contracts/dist/project.d.ts.map +1 -1
- package/packages/@repo/contracts/dist/project.js +3 -1
- package/packages/@repo/contracts/dist/tenant.d.ts +36 -0
- package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -1
- package/packages/@repo/contracts/dist/tenant.js +2 -0
- package/packages/@repo/contracts/package.json +1 -1
- package/packages/@repo/contracts/src/analytics.ts +33 -0
- package/packages/@repo/contracts/src/deployment.ts +1 -1
- package/packages/@repo/contracts/src/git.ts +2 -2
- package/packages/@repo/contracts/src/ids.ts +2 -1
- package/packages/@repo/contracts/src/index.ts +1 -0
- package/packages/@repo/contracts/src/project.ts +3 -1
- package/packages/@repo/contracts/src/tenant.ts +2 -0
- package/packages/@repo/models/dist/docs-config.d.ts +0 -198
- package/packages/@repo/models/dist/docs-config.d.ts.map +1 -1
- package/packages/@repo/models/dist/docs-config.js +3 -2
- package/packages/@repo/models/dist/tenant.d.ts +12 -0
- package/packages/@repo/models/dist/tenant.d.ts.map +1 -1
- package/packages/@repo/models/package.json +1 -1
- package/packages/@repo/models/src/docs-config.ts +3 -3
- package/packages/@repo/models/src/tenant.ts +15 -0
- package/packages/@repo/prebuild/dist/openapi.d.ts.map +1 -1
- package/packages/@repo/prebuild/dist/openapi.js +32 -4
- package/packages/@repo/prebuild/package.json +1 -1
- package/packages/@repo/prebuild/src/openapi.ts +42 -4
- package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -1
- package/packages/@repo/previewing/dist/blob-source.js +18 -5
- package/packages/@repo/previewing/dist/config-loader.d.ts +134 -0
- package/packages/@repo/previewing/dist/config-loader.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/config-loader.js +231 -0
- package/packages/@repo/previewing/dist/constants.d.ts +15 -0
- package/packages/@repo/previewing/dist/constants.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/constants.js +14 -0
- package/packages/@repo/previewing/dist/content-index.d.ts +7 -0
- package/packages/@repo/previewing/dist/content-index.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/content-index.js +291 -0
- package/packages/@repo/previewing/dist/index.d.ts +13 -121
- package/packages/@repo/previewing/dist/index.d.ts.map +1 -1
- package/packages/@repo/previewing/dist/index.js +11 -1003
- package/packages/@repo/previewing/dist/markdown/agent-markdown.d.ts +3 -0
- package/packages/@repo/previewing/dist/markdown/agent-markdown.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/markdown/agent-markdown.js +176 -0
- package/packages/@repo/previewing/dist/markdown/format.d.ts +8 -0
- package/packages/@repo/previewing/dist/markdown/format.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/markdown/format.js +43 -0
- package/packages/@repo/previewing/dist/markdown/links.d.ts +3 -0
- package/packages/@repo/previewing/dist/markdown/links.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/markdown/links.js +31 -0
- package/packages/@repo/previewing/dist/openapi-pages.d.ts +7 -0
- package/packages/@repo/previewing/dist/openapi-pages.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/openapi-pages.js +200 -0
- package/packages/@repo/previewing/dist/search-index.d.ts +4 -0
- package/packages/@repo/previewing/dist/search-index.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/search-index.js +23 -0
- package/packages/@repo/previewing/dist/serialization.d.ts +13 -0
- package/packages/@repo/previewing/dist/serialization.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/serialization.js +108 -0
- package/packages/@repo/previewing/dist/toc-index.d.ts +5 -0
- package/packages/@repo/previewing/dist/toc-index.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/toc-index.js +53 -0
- package/packages/@repo/previewing/dist/types.d.ts +92 -0
- package/packages/@repo/previewing/dist/types.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/types.js +1 -0
- package/packages/@repo/previewing/dist/utility-index.d.ts +7 -0
- package/packages/@repo/previewing/dist/utility-index.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/utility-index.js +201 -0
- package/packages/@repo/previewing/package.json +1 -1
- package/packages/@repo/previewing/src/blob-source.ts +20 -5
- package/packages/@repo/previewing/src/config-loader.ts +316 -0
- package/packages/@repo/previewing/src/constants.ts +16 -0
- package/packages/@repo/previewing/src/content-index.ts +384 -0
- package/packages/@repo/previewing/src/index.ts +69 -1515
- package/packages/@repo/previewing/src/markdown/agent-markdown.ts +254 -0
- package/packages/@repo/previewing/src/markdown/format.ts +63 -0
- package/packages/@repo/previewing/src/markdown/links.ts +46 -0
- package/packages/@repo/previewing/src/openapi-pages.ts +319 -0
- package/packages/@repo/previewing/src/search-index.ts +38 -0
- package/packages/@repo/previewing/src/serialization.ts +191 -0
- package/packages/@repo/previewing/src/toc-index.ts +72 -0
- package/packages/@repo/previewing/src/types.ts +105 -0
- package/packages/@repo/previewing/src/utility-index.ts +304 -0
- package/packages/@repo/validation/package.json +1 -1
- package/scripts/repo-packages.mjs +8 -0
- package/docs/components/ui/site-footer.tsx +0 -41
|
@@ -109,82 +109,86 @@ export const MobileNav = ({
|
|
|
109
109
|
side="bottom"
|
|
110
110
|
sideOffset={14}
|
|
111
111
|
>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<div className="
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
112
|
+
{open ? (
|
|
113
|
+
<div className="flex flex-col gap-12 overflow-auto px-6 py-6">
|
|
114
|
+
{tabs?.length ? (
|
|
115
|
+
<div className="flex flex-col gap-4">
|
|
116
|
+
<div className="text-sm font-medium text-muted-foreground">
|
|
117
|
+
Sections
|
|
118
|
+
</div>
|
|
119
|
+
<div className="flex flex-col gap-3">
|
|
120
|
+
{tabs.map((tab, index) => {
|
|
121
|
+
const href =
|
|
122
|
+
(tab.href
|
|
123
|
+
? resolveHref(tab.href, basePath)
|
|
124
|
+
: undefined) ??
|
|
125
|
+
(tab.slugPrefix
|
|
126
|
+
? toDocHref(tab.slugPrefix, basePath)
|
|
127
|
+
: undefined);
|
|
128
|
+
if (!href) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const isActive = index === activeTabIndex;
|
|
129
132
|
|
|
130
|
-
|
|
133
|
+
return (
|
|
134
|
+
<MobileLink
|
|
135
|
+
className={isActive ? "text-primary" : ""}
|
|
136
|
+
href={href}
|
|
137
|
+
key={tab.label}
|
|
138
|
+
onOpenChange={setOpen}
|
|
139
|
+
rel={
|
|
140
|
+
tab.href && isExternalHref(tab.href)
|
|
141
|
+
? "noopener noreferrer"
|
|
142
|
+
: undefined
|
|
143
|
+
}
|
|
144
|
+
target={
|
|
145
|
+
tab.href && isExternalHref(tab.href)
|
|
146
|
+
? "_blank"
|
|
147
|
+
: undefined
|
|
148
|
+
}
|
|
149
|
+
>
|
|
150
|
+
{tab.label}
|
|
151
|
+
</MobileLink>
|
|
152
|
+
);
|
|
153
|
+
})}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
) : null}
|
|
157
|
+
{globalLinks.length > 0 ? (
|
|
158
|
+
<div className="flex flex-col gap-4">
|
|
159
|
+
<div className="flex flex-col gap-3">
|
|
160
|
+
{globalLinks.map((link) => (
|
|
131
161
|
<MobileLink
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
key={tab.label}
|
|
162
|
+
href={link.href}
|
|
163
|
+
key={link.label}
|
|
135
164
|
onOpenChange={setOpen}
|
|
136
|
-
rel=
|
|
137
|
-
|
|
138
|
-
? "noopener noreferrer"
|
|
139
|
-
: undefined
|
|
140
|
-
}
|
|
141
|
-
target={
|
|
142
|
-
tab.href && isExternalHref(tab.href)
|
|
143
|
-
? "_blank"
|
|
144
|
-
: undefined
|
|
145
|
-
}
|
|
165
|
+
rel="noopener noreferrer"
|
|
166
|
+
target="_blank"
|
|
146
167
|
>
|
|
147
|
-
{
|
|
168
|
+
{link.label}
|
|
148
169
|
</MobileLink>
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
) : null}
|
|
154
|
-
{globalLinks.length > 0 ? (
|
|
155
|
-
<div className="flex flex-col gap-4">
|
|
156
|
-
<div className="flex flex-col gap-3">
|
|
157
|
-
{globalLinks.map((link) => (
|
|
158
|
-
<MobileLink
|
|
159
|
-
href={link.href}
|
|
160
|
-
key={link.label}
|
|
161
|
-
onOpenChange={setOpen}
|
|
162
|
-
rel="noopener noreferrer"
|
|
163
|
-
target="_blank"
|
|
164
|
-
>
|
|
165
|
-
{link.label}
|
|
166
|
-
</MobileLink>
|
|
167
|
-
))}
|
|
170
|
+
))}
|
|
171
|
+
</div>
|
|
168
172
|
</div>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
173
|
+
) : null}
|
|
174
|
+
{entries.map((entry) => {
|
|
175
|
+
if (entry.type === "page") {
|
|
176
|
+
return renderPageLink(entry);
|
|
177
|
+
}
|
|
175
178
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
179
|
+
return (
|
|
180
|
+
<div className="flex flex-col gap-4" key={entry.title}>
|
|
181
|
+
<div className="text-sm font-medium text-muted-foreground">
|
|
182
|
+
{entry.title}
|
|
183
|
+
</div>
|
|
184
|
+
<div className="flex flex-col gap-3">
|
|
185
|
+
{entry.items.map((item) => renderPageLink(item))}
|
|
186
|
+
</div>
|
|
180
187
|
</div>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
);
|
|
186
|
-
})}
|
|
187
|
-
</div>
|
|
188
|
+
);
|
|
189
|
+
})}
|
|
190
|
+
</div>
|
|
191
|
+
) : null}
|
|
188
192
|
</PopoverContent>
|
|
189
193
|
</Popover>
|
|
190
194
|
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { normalizePath } from "@repo/common";
|
|
4
|
+
import { usePathname } from "next/navigation";
|
|
5
|
+
import { useEffect } from "react";
|
|
6
|
+
|
|
7
|
+
const ACTIVE_CLASSES = ["border-accent", "bg-accent", "text-foreground"];
|
|
8
|
+
|
|
9
|
+
export const SidebarActiveHighlight = ({ basePath }: { basePath: string }) => {
|
|
10
|
+
const pathname = usePathname();
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const stripped = basePath
|
|
14
|
+
? pathname.replace(new RegExp(`^${basePath}`), "")
|
|
15
|
+
: pathname;
|
|
16
|
+
const target = normalizePath(stripped) || "index";
|
|
17
|
+
|
|
18
|
+
const links = document.querySelectorAll<HTMLElement>(
|
|
19
|
+
"[data-sidebar-link][data-path]"
|
|
20
|
+
);
|
|
21
|
+
for (const link of links) {
|
|
22
|
+
const isActive = link.dataset.path === target;
|
|
23
|
+
if (isActive) {
|
|
24
|
+
link.dataset.active = "true";
|
|
25
|
+
link.classList.add(...ACTIVE_CLASSES);
|
|
26
|
+
} else {
|
|
27
|
+
delete link.dataset.active;
|
|
28
|
+
link.classList.remove(...ACTIVE_CLASSES);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}, [basePath, pathname]);
|
|
32
|
+
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEffect,
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
4
|
|
|
5
5
|
let savedScrollTop = 0;
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ export const SidebarScrollArea = ({
|
|
|
13
13
|
}) => {
|
|
14
14
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
useEffect(() => {
|
|
17
17
|
const el = scrollRef.current;
|
|
18
18
|
if (!el) {
|
|
19
19
|
return;
|
|
@@ -21,12 +21,17 @@ export const SidebarScrollArea = ({
|
|
|
21
21
|
|
|
22
22
|
if (savedScrollTop > 0) {
|
|
23
23
|
el.scrollTop = savedScrollTop;
|
|
24
|
-
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Defer to allow SidebarActiveHighlight to mark the active link first.
|
|
28
|
+
const id = requestAnimationFrame(() => {
|
|
25
29
|
const active = el.querySelector<HTMLElement>("[data-active]");
|
|
26
30
|
if (active) {
|
|
27
31
|
active.scrollIntoView({ behavior: "instant", block: "center" });
|
|
28
32
|
}
|
|
29
|
-
}
|
|
33
|
+
});
|
|
34
|
+
return () => cancelAnimationFrame(id);
|
|
30
35
|
});
|
|
31
36
|
|
|
32
37
|
useEffect(() => {
|
|
@@ -49,13 +49,13 @@ import { GITHUB_INSTALL_STATE_KEY } from "@/lib/github-install";
|
|
|
49
49
|
import { timeAgo } from "@/lib/time-ago";
|
|
50
50
|
import { cn } from "@/lib/utils";
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
interface RepoPickerInstallation {
|
|
53
53
|
id: number;
|
|
54
54
|
accountLogin: string;
|
|
55
55
|
accountType: string;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
interface RepoPickerProps {
|
|
59
59
|
accessToken: string;
|
|
60
60
|
installations: RepoPickerInstallation[];
|
|
61
61
|
onAddAccount: () => void;
|
|
@@ -387,6 +387,9 @@ export const RepoPicker = ({
|
|
|
387
387
|
const handlePick = (repo: RepoSummary) => {
|
|
388
388
|
setSelected(repo);
|
|
389
389
|
setBranch(repo.defaultBranch || "main");
|
|
390
|
+
// Reset the docs path so an edit made for a previous repo doesn't carry
|
|
391
|
+
// over to a freshly picked one.
|
|
392
|
+
setDocsPath("docs");
|
|
390
393
|
setError(null);
|
|
391
394
|
};
|
|
392
395
|
|
|
@@ -2,6 +2,8 @@ import type { MDXComponents } from "mdx/types";
|
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import type { ComponentProps } from "react";
|
|
4
4
|
|
|
5
|
+
import { isExternalHref, resolveHref } from "@/lib/routes";
|
|
6
|
+
|
|
5
7
|
import { Accordion, AccordionGroup } from "./accordion";
|
|
6
8
|
import { AgentInstructions } from "./agent-instructions";
|
|
7
9
|
import { Badge } from "./badge";
|
|
@@ -42,30 +44,36 @@ const Tree = Object.assign(
|
|
|
42
44
|
}
|
|
43
45
|
);
|
|
44
46
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
const createMdxLink = (basePath: string, currentPath: string) => {
|
|
48
|
+
const MdxLink = ({
|
|
49
|
+
href,
|
|
50
|
+
children,
|
|
51
|
+
...props
|
|
52
|
+
}: React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
|
53
|
+
if (!href) {
|
|
54
|
+
return <a {...props}>{children}</a>;
|
|
55
|
+
}
|
|
56
|
+
if (isExternalHref(href)) {
|
|
57
|
+
return (
|
|
58
|
+
<a {...props} href={href} rel="noopener noreferrer" target="_blank">
|
|
59
|
+
{children}
|
|
60
|
+
</a>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
55
63
|
return (
|
|
56
|
-
<
|
|
64
|
+
<Link href={resolveHref(href, basePath, currentPath)} {...props}>
|
|
57
65
|
{children}
|
|
58
|
-
</
|
|
66
|
+
</Link>
|
|
59
67
|
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
{children}
|
|
64
|
-
</Link>
|
|
65
|
-
);
|
|
68
|
+
};
|
|
69
|
+
MdxLink.displayName = "MdxLink";
|
|
70
|
+
return MdxLink;
|
|
66
71
|
};
|
|
67
72
|
|
|
68
|
-
export const
|
|
73
|
+
export const createMdxComponents = (
|
|
74
|
+
basePath = "",
|
|
75
|
+
currentPath = ""
|
|
76
|
+
): MDXComponents => ({
|
|
69
77
|
Accordion,
|
|
70
78
|
AccordionGroup,
|
|
71
79
|
AgentInstructions,
|
|
@@ -106,6 +114,8 @@ export const mdxComponents: MDXComponents = {
|
|
|
106
114
|
View,
|
|
107
115
|
ViewGroup,
|
|
108
116
|
Warning,
|
|
109
|
-
a:
|
|
117
|
+
a: createMdxLink(basePath, currentPath),
|
|
110
118
|
pre: CodeBlock,
|
|
111
|
-
};
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export const mdxComponents: MDXComponents = createMdxComponents();
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { usePathname, useSearchParams } from "next/navigation";
|
|
4
|
+
import posthogJs from "posthog-js";
|
|
5
|
+
import { useEffect } from "react";
|
|
6
|
+
|
|
7
|
+
const DEFAULT_POSTHOG_HOST = "https://us.i.posthogJs.com";
|
|
8
|
+
|
|
9
|
+
interface PostHogProviderProps {
|
|
10
|
+
projectKey: string;
|
|
11
|
+
host?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const PostHogProvider = ({ projectKey, host }: PostHogProviderProps) => {
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!projectKey) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (posthogJs.__loaded) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
posthogJs.init(projectKey, {
|
|
23
|
+
api_host: host || DEFAULT_POSTHOG_HOST,
|
|
24
|
+
capture_pageleave: true,
|
|
25
|
+
capture_pageview: false,
|
|
26
|
+
person_profiles: "identified_only",
|
|
27
|
+
});
|
|
28
|
+
}, [projectKey, host]);
|
|
29
|
+
|
|
30
|
+
const pathname = usePathname();
|
|
31
|
+
const searchParams = useSearchParams();
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!(pathname && posthogJs.__loaded)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const query = searchParams?.toString();
|
|
37
|
+
const url = query ? `${pathname}?${query}` : pathname;
|
|
38
|
+
posthogJs.capture("$pageview", { $current_url: url });
|
|
39
|
+
}, [pathname, searchParams]);
|
|
40
|
+
|
|
41
|
+
return null;
|
|
42
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { GoogleAnalytics } from "@next/third-parties/google";
|
|
2
|
+
import { headers } from "next/headers";
|
|
3
|
+
import Script from "next/script";
|
|
4
|
+
import { Suspense } from "react";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
decodeTenantAnalyticsHeader,
|
|
8
|
+
TENANT_HEADERS,
|
|
9
|
+
} from "@/lib/tenant-headers";
|
|
10
|
+
|
|
11
|
+
import { PostHogProvider } from "./posthog-provider";
|
|
12
|
+
|
|
13
|
+
// Consent Mode v2 default-denied bootstrap. Replace with a real CMP when ready.
|
|
14
|
+
const CONSENT_DEFAULTS_SCRIPT = `window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('consent','default',{ad_storage:'denied',ad_user_data:'denied',ad_personalization:'denied',analytics_storage:'denied',wait_for_update:500});`;
|
|
15
|
+
|
|
16
|
+
export const TenantAnalytics = async () => {
|
|
17
|
+
if (process.env.VERCEL_ENV !== "production") {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const headerStore = await headers();
|
|
22
|
+
const analytics = decodeTenantAnalyticsHeader(
|
|
23
|
+
headerStore.get(TENANT_HEADERS.ANALYTICS)
|
|
24
|
+
);
|
|
25
|
+
if (!analytics) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
{analytics.ga4?.measurementId ? (
|
|
32
|
+
<>
|
|
33
|
+
<Script id="ga-consent-defaults" strategy="afterInteractive">
|
|
34
|
+
{CONSENT_DEFAULTS_SCRIPT}
|
|
35
|
+
</Script>
|
|
36
|
+
<GoogleAnalytics gaId={analytics.ga4.measurementId} />
|
|
37
|
+
</>
|
|
38
|
+
) : null}
|
|
39
|
+
{analytics.posthog?.projectKey ? (
|
|
40
|
+
<Suspense fallback={null}>
|
|
41
|
+
<PostHogProvider
|
|
42
|
+
host={analytics.posthog.host}
|
|
43
|
+
projectKey={analytics.posthog.projectKey}
|
|
44
|
+
/>
|
|
45
|
+
</Suspense>
|
|
46
|
+
) : null}
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
@@ -5,6 +5,9 @@ import { SpeedInsights } from "@vercel/speed-insights/next";
|
|
|
5
5
|
import { usePathname } from "next/navigation";
|
|
6
6
|
|
|
7
7
|
const PRIVATE_PATH_PREFIXES = ["/app", "/oauth"];
|
|
8
|
+
const DEFAULT_BLODEMD_GA_ID = "G-WE1RCNSC4E";
|
|
9
|
+
const BLODEMD_GA_ID =
|
|
10
|
+
process.env.NEXT_PUBLIC_BLODEMD_GA_ID?.trim() || DEFAULT_BLODEMD_GA_ID;
|
|
8
11
|
|
|
9
12
|
const isPrivatePath = (pathname: string | null) => {
|
|
10
13
|
if (!pathname) {
|
|
@@ -23,7 +26,7 @@ export const ThirdParties = () => {
|
|
|
23
26
|
return (
|
|
24
27
|
<>
|
|
25
28
|
<SpeedInsights />
|
|
26
|
-
<GoogleAnalytics gaId=
|
|
29
|
+
{BLODEMD_GA_ID ? <GoogleAnalytics gaId={BLODEMD_GA_ID} /> : null}
|
|
27
30
|
</>
|
|
28
31
|
);
|
|
29
32
|
};
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "blode-icons-react";
|
|
7
7
|
import { cva } from "class-variance-authority";
|
|
8
8
|
import type { VariantProps } from "class-variance-authority";
|
|
9
|
-
import { useCallback } from "react";
|
|
9
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
10
10
|
import type { MouseEvent } from "react";
|
|
11
11
|
|
|
12
12
|
import { Button as ButtonPrimitive } from "@/components/animate-ui/primitives/buttons/button";
|
|
@@ -68,6 +68,16 @@ const CopyButton = ({
|
|
|
68
68
|
onChange: onCopiedChange,
|
|
69
69
|
value: copied,
|
|
70
70
|
});
|
|
71
|
+
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
72
|
+
|
|
73
|
+
useEffect(
|
|
74
|
+
() => () => {
|
|
75
|
+
if (timeoutRef.current) {
|
|
76
|
+
clearTimeout(timeoutRef.current);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
[]
|
|
80
|
+
);
|
|
71
81
|
|
|
72
82
|
const handleCopy = useCallback(
|
|
73
83
|
async (e: MouseEvent<HTMLButtonElement>) => {
|
|
@@ -80,7 +90,10 @@ const CopyButton = ({
|
|
|
80
90
|
await navigator.clipboard.writeText(content);
|
|
81
91
|
setIsCopied(true);
|
|
82
92
|
onCopiedChange?.(true, content);
|
|
83
|
-
|
|
93
|
+
if (timeoutRef.current) {
|
|
94
|
+
clearTimeout(timeoutRef.current);
|
|
95
|
+
}
|
|
96
|
+
timeoutRef.current = setTimeout(() => {
|
|
84
97
|
setIsCopied(false);
|
|
85
98
|
onCopiedChange?.(false);
|
|
86
99
|
}, delay);
|
|
@@ -102,6 +115,7 @@ const CopyButton = ({
|
|
|
102
115
|
{...props}
|
|
103
116
|
>
|
|
104
117
|
<span
|
|
118
|
+
aria-hidden="true"
|
|
105
119
|
className="inline-flex animate-in fade-in-0 zoom-in-95 duration-200 motion-reduce:animate-none"
|
|
106
120
|
data-slot="copy-button-icon"
|
|
107
121
|
key={isCopied ? "check" : "copy"}
|