@burdenoff/website-sdk 2026.824.2 → 2026.828.1
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 +88 -0
- package/dist/client/index.d.mts +2 -3
- package/dist/client/index.d.ts +2 -3
- package/dist/client/index.js +13 -7
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +13 -7
- package/dist/client/index.mjs.map +1 -1
- package/dist/components/contact.d.mts +2 -2
- package/dist/components/contact.d.ts +2 -2
- package/dist/components/contact.js +13 -7
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +13 -7
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/electron-download.d.mts +2 -2
- package/dist/components/electron-download.d.ts +2 -2
- package/dist/components/explore-cta.d.mts +26 -0
- package/dist/components/explore-cta.d.ts +26 -0
- package/dist/components/explore-cta.js +95 -0
- package/dist/components/explore-cta.js.map +1 -0
- package/dist/components/explore-cta.mjs +93 -0
- package/dist/components/explore-cta.mjs.map +1 -0
- package/dist/components/explore.d.mts +43 -0
- package/dist/components/explore.d.ts +43 -0
- package/dist/components/explore.js +2219 -0
- package/dist/components/explore.js.map +1 -0
- package/dist/components/explore.mjs +2193 -0
- package/dist/components/explore.mjs.map +1 -0
- package/dist/components/launch-countdown.d.mts +2 -2
- package/dist/components/launch-countdown.d.ts +2 -2
- package/dist/components/newsletter.d.mts +3 -3
- package/dist/components/newsletter.d.ts +3 -3
- package/dist/components/newsletter.js +13 -7
- package/dist/components/newsletter.js.map +1 -1
- package/dist/components/newsletter.mjs +13 -7
- package/dist/components/newsletter.mjs.map +1 -1
- package/dist/components/partners.d.mts +2 -2
- package/dist/components/partners.d.ts +2 -2
- package/dist/components/partners.js +13 -7
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs +13 -7
- package/dist/components/partners.mjs.map +1 -1
- package/dist/components/pricing.d.mts +3 -3
- package/dist/components/pricing.d.ts +3 -3
- package/dist/components/pricing.js +13 -7
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +13 -7
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/components/problems-solved.d.mts +2 -2
- package/dist/components/problems-solved.d.ts +2 -2
- package/dist/components/resource-links.d.mts +2 -2
- package/dist/components/resource-links.d.ts +2 -2
- package/dist/components/resource-links.js +13 -7
- package/dist/components/resource-links.js.map +1 -1
- package/dist/components/resource-links.mjs +13 -7
- package/dist/components/resource-links.mjs.map +1 -1
- package/dist/components/social-links.d.mts +2 -2
- package/dist/components/social-links.d.ts +2 -2
- package/dist/components/social-links.js +13 -7
- package/dist/components/social-links.js.map +1 -1
- package/dist/components/social-links.mjs +13 -7
- package/dist/components/social-links.mjs.map +1 -1
- package/dist/components/unsubscribe.d.mts +2 -2
- package/dist/components/unsubscribe.d.ts +2 -2
- package/dist/components/unsubscribe.js +13 -7
- package/dist/components/unsubscribe.js.map +1 -1
- package/dist/components/unsubscribe.mjs +13 -7
- package/dist/components/unsubscribe.mjs.map +1 -1
- package/dist/components/waitlist.d.mts +3 -3
- package/dist/components/waitlist.d.ts +3 -3
- package/dist/components/waitlist.js +13 -7
- package/dist/components/waitlist.js.map +1 -1
- package/dist/components/waitlist.mjs +13 -7
- package/dist/components/waitlist.mjs.map +1 -1
- package/dist/components/website-switcher.d.mts +2 -2
- package/dist/components/website-switcher.d.ts +2 -2
- package/dist/content/index.d.mts +2 -3
- package/dist/content/index.d.ts +2 -3
- package/dist/content/index.js +13 -7
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +13 -7
- package/dist/content/index.mjs.map +1 -1
- package/dist/{graphql-client-Cv6TO6hn.d.mts → graphql-client-BXn6RYV0.d.mts} +31 -4
- package/dist/{graphql-client-Cv6TO6hn.d.ts → graphql-client-BXn6RYV0.d.ts} +31 -4
- package/dist/hooks/index.d.mts +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1145 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +1140 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/{index-CFsUFxYo.d.ts → index-BXg7cQLm.d.ts} +11 -11
- package/dist/index-CUJTTlYx.d.mts +244 -0
- package/dist/index-CUJTTlYx.d.ts +244 -0
- package/dist/{index-CuMRiKGB.d.mts → index-DYxQiEm5.d.mts} +11 -11
- package/dist/index.d.mts +17 -15
- package/dist/index.d.ts +17 -15
- package/dist/index.js +1987 -426
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1977 -428
- package/dist/index.mjs.map +1 -1
- package/dist/product/index.d.mts +2 -2
- package/dist/product/index.d.ts +2 -2
- package/dist/product/index.js +13 -7
- package/dist/product/index.js.map +1 -1
- package/dist/product/index.mjs +13 -7
- package/dist/product/index.mjs.map +1 -1
- package/dist/{product-card-DL9O3Ez9.d.mts → product-card-CGxETsFa.d.mts} +7 -7
- package/dist/{product-card-DL9O3Ez9.d.ts → product-card-CGxETsFa.d.ts} +7 -7
- package/dist/{provider-D-Do5PmD.d.ts → provider-CUfI8meE.d.mts} +3 -3
- package/dist/{provider-BhsbxVPo.d.mts → provider-D_uUM7_e.d.ts} +3 -3
- package/dist/seo/index.d.mts +2 -2
- package/dist/seo/index.d.ts +2 -2
- package/dist/store/index.d.mts +3 -3
- package/dist/store/index.d.ts +3 -3
- package/dist/store/index.js +13 -7
- package/dist/store/index.js.map +1 -1
- package/dist/store/index.mjs +13 -7
- package/dist/store/index.mjs.map +1 -1
- package/dist/utils/index.d.mts +49 -1
- package/dist/utils/index.d.ts +49 -1
- package/dist/utils/index.js +70 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +68 -1
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +40 -30
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var lucideReact = require('lucide-react');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
function useCurrentPath(explicit) {
|
|
13
|
+
const [path, setPath] = react.useState(explicit ?? null);
|
|
14
|
+
react.useEffect(() => {
|
|
15
|
+
if (explicit !== void 0) {
|
|
16
|
+
setPath(explicit);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (typeof window === "undefined") return;
|
|
20
|
+
const read = () => setPath(window.location.pathname);
|
|
21
|
+
read();
|
|
22
|
+
window.addEventListener("popstate", read);
|
|
23
|
+
return () => window.removeEventListener("popstate", read);
|
|
24
|
+
}, [explicit]);
|
|
25
|
+
return path;
|
|
26
|
+
}
|
|
27
|
+
function samePath(a, b) {
|
|
28
|
+
if (!a) return false;
|
|
29
|
+
const norm = (v) => v.length > 1 ? v.replace(/\/+$/, "") : v;
|
|
30
|
+
return norm(a) === norm(b);
|
|
31
|
+
}
|
|
32
|
+
function ExploreCta({
|
|
33
|
+
variant,
|
|
34
|
+
href = "/explore",
|
|
35
|
+
label = "Ask AI",
|
|
36
|
+
className,
|
|
37
|
+
onNavigate,
|
|
38
|
+
onClick,
|
|
39
|
+
currentPath
|
|
40
|
+
}) {
|
|
41
|
+
const path = useCurrentPath(currentPath);
|
|
42
|
+
const isActive = samePath(path, href);
|
|
43
|
+
if (variant === "floating" && isActive) return null;
|
|
44
|
+
const handleClick = (event) => {
|
|
45
|
+
onClick?.();
|
|
46
|
+
if (!onNavigate) return;
|
|
47
|
+
if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
onNavigate(href);
|
|
52
|
+
};
|
|
53
|
+
const classes = {
|
|
54
|
+
header: "inline-flex items-center gap-1.5 rounded-full bg-gradient-to-r from-primary to-primary/70 px-3.5 py-1.5 text-sm font-medium text-primary-foreground shadow-sm transition-opacity hover:opacity-90",
|
|
55
|
+
mobile: "inline-flex w-full items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 text-sm font-medium text-card-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
56
|
+
floating: "fixed bottom-5 right-5 z-40 inline-flex items-center gap-2 rounded-full bg-gradient-to-r from-primary to-primary/70 px-4 py-3 text-sm font-medium text-primary-foreground shadow-lg transition-opacity hover:opacity-90"
|
|
57
|
+
};
|
|
58
|
+
const hooks = {
|
|
59
|
+
header: "cta-header",
|
|
60
|
+
mobile: "cta-mobile",
|
|
61
|
+
floating: "cta-floating"
|
|
62
|
+
};
|
|
63
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
64
|
+
"a",
|
|
65
|
+
{
|
|
66
|
+
"data-boff-explore": hooks[variant],
|
|
67
|
+
href,
|
|
68
|
+
onClick: handleClick,
|
|
69
|
+
"aria-label": label,
|
|
70
|
+
"aria-current": isActive ? "page" : void 0,
|
|
71
|
+
className: cn(
|
|
72
|
+
classes[variant],
|
|
73
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
74
|
+
className
|
|
75
|
+
),
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
78
|
+
lucideReact.Sparkles,
|
|
79
|
+
{
|
|
80
|
+
className: cn(
|
|
81
|
+
"shrink-0",
|
|
82
|
+
variant === "floating" ? "h-4 w-4" : "h-3.5 w-3.5"
|
|
83
|
+
),
|
|
84
|
+
"aria-hidden": "true"
|
|
85
|
+
}
|
|
86
|
+
),
|
|
87
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(variant === "floating" && "hidden sm:inline"), children: label })
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
exports.ExploreCta = ExploreCta;
|
|
94
|
+
//# sourceMappingURL=explore-cta.js.map
|
|
95
|
+
//# sourceMappingURL=explore-cta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/cn.ts","../../src/components/explore-cta.tsx"],"names":["twMerge","clsx","useState","useEffect","jsxs","jsx","Sparkles"],"mappings":";;;;;;;;AAmBO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAOA,qBAAA,CAAQC,SAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACyCA,SAAS,eAAe,QAAA,EAAkC;AACxD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIC,cAAA,CAAwB,YAAY,IAAI,CAAA;AAEhE,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,MAAA,OAAA,CAAQ,QAAQ,CAAA;AAChB,MAAA;AAAA,IACF;AACA,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,IAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,MAAA,CAAO,SAAS,QAAQ,CAAA;AACnD,IAAA,IAAA,EAAK;AACL,IAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,IAAI,CAAA;AACxC,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,UAAA,EAAY,IAAI,CAAA;AAAA,EAC1D,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,OAAO,IAAA;AACT;AAGA,SAAS,QAAA,CAAS,GAAkB,CAAA,EAAoB;AACtD,EAAA,IAAI,CAAC,GAAG,OAAO,KAAA;AACf,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAe,CAAA,CAAE,MAAA,GAAS,IAAI,CAAA,CAAE,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA,GAAI,CAAA;AACpE,EAAA,OAAO,IAAA,CAAK,CAAC,CAAA,KAAM,IAAA,CAAK,CAAC,CAAA;AAC3B;AAEO,SAAS,UAAA,CAAW;AAAA,EACzB,OAAA;AAAA,EACA,IAAA,GAAO,UAAA;AAAA,EACP,KAAA,GAAQ,QAAA;AAAA,EACR,SAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAAoB;AAClB,EAAA,MAAM,IAAA,GAAO,eAAe,WAAW,CAAA;AACvC,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,IAAA,EAAM,IAAI,CAAA;AAIpC,EAAA,IAAI,OAAA,KAAY,UAAA,IAAc,QAAA,EAAU,OAAO,IAAA;AAE/C,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAA+C;AAClE,IAAA,OAAA,IAAU;AACV,IAAA,IAAI,CAAC,UAAA,EAAY;AAEjB,IAAA,IACE,KAAA,CAAM,gBAAA,IACN,KAAA,CAAM,OAAA,IACN,KAAA,CAAM,OAAA,IACN,KAAA,CAAM,QAAA,IACN,KAAA,CAAM,MAAA,IACN,KAAA,CAAM,MAAA,KAAW,CAAA,EACjB;AACA,MAAA;AAAA,IACF;AACA,IAAA,KAAA,CAAM,cAAA,EAAe;AACrB,IAAA,UAAA,CAAW,IAAI,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,OAAA,GAAsD;AAAA,IAC1D,MAAA,EACE,mMAAA;AAAA,IACF,MAAA,EACE,iMAAA;AAAA,IACF,QAAA,EACE;AAAA,GACJ;AAEA,EAAA,MAAM,KAAA,GAAoD;AAAA,IACxD,MAAA,EAAQ,YAAA;AAAA,IACR,MAAA,EAAQ,YAAA;AAAA,IACR,QAAA,EAAU;AAAA,GACZ;AAEA,EAAA,uBACEC,eAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,mBAAA,EAAmB,MAAM,OAAO,CAAA;AAAA,MAChC,IAAA;AAAA,MACA,OAAA,EAAS,WAAA;AAAA,MACT,YAAA,EAAY,KAAA;AAAA,MACZ,cAAA,EAAc,WAAW,MAAA,GAAS,MAAA;AAAA,MAClC,SAAA,EAAW,EAAA;AAAA,QACT,QAAQ,OAAO,CAAA;AAAA,QACf,0IAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAAC,cAAA;AAAA,UAACC,oBAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAW,EAAA;AAAA,cACT,UAAA;AAAA,cACA,OAAA,KAAY,aAAa,SAAA,GAAY;AAAA,aACvC;AAAA,YACA,aAAA,EAAY;AAAA;AAAA,SACd;AAAA,wBAGAD,cAAA,CAAC,UAAK,SAAA,EAAW,EAAA,CAAG,YAAY,UAAA,IAAc,kBAAkB,GAC7D,QAAA,EAAA,KAAA,EACH;AAAA;AAAA;AAAA,GACF;AAEJ","file":"explore-cta.js","sourcesContent":["/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx and tailwind-merge for optimal class handling\n */\n\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge multiple class names intelligently\n * - Handles conditional classes via clsx\n * - Deduplicates and resolves Tailwind conflicts via tailwind-merge\n *\n * @example\n * ```ts\n * cn('px-4 py-2', 'bg-blue-500', { 'text-white': isActive })\n * // => 'px-4 py-2 bg-blue-500 text-white' (if isActive is true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * ExploreCta — the entry point to `/explore` from a site's navigation.\n *\n * Three shapes of the same link, so a product site can place it once in the\n * desktop header, once in the mobile menu, and optionally as a floating bubble\n * on content pages, without any of them re-implementing the styling:\n *\n * - `header` — a gradient sparkle pill for the desktop nav.\n * - `mobile` — a compact full-width row for a mobile menu list.\n * - `floating` — a fixed bottom-right bubble; hides itself on `/explore`\n * so it never covers the page it links to.\n *\n * Import from the package ROOT (`@burdenoff/website-sdk`) for consistency with\n * `ExplorePage`, which must be root-imported because of tsup's\n * `splitting: false`. This component holds no context of its own, so a\n * sub-path import also works — but keeping both on the root import is one less\n * footgun for the 38 sites that ship them together.\n *\n * @example\n * ```tsx\n * import { ExploreCta } from '@burdenoff/website-sdk'\n *\n * <ExploreCta variant=\"header\" onNavigate={(href) => navigate(href)} />\n * <ExploreCta variant=\"floating\" currentPath={location.pathname} />\n * ```\n */\n\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nimport { Sparkles } from \"lucide-react\";\n\nimport { cn } from \"../utils/cn\";\n\nexport interface ExploreCtaProps {\n /** Which of the three presentations to render. */\n variant: \"header\" | \"mobile\" | \"floating\";\n /** Target route. Default `/explore`. */\n href?: string;\n /** Visible label / accessible name. Default `Ask AI`. */\n label?: string;\n /** Extra classes for the rendered element. */\n className?: string;\n /** SPA navigation. When omitted the component renders a plain `<a href>`. */\n onNavigate?: (href: string) => void;\n /** Extra click handler (analytics, closing a mobile menu, …). */\n onClick?: () => void;\n /**\n * Current route, when the host knows it (`useLocation().pathname`).\n * Used to mark the link as `aria-current=\"page\"` and to hide the floating\n * bubble on the Explore page itself. Falls back to `window.location.pathname`,\n * which cannot observe SPA `pushState` navigations on its own.\n */\n currentPath?: string;\n}\n\n/**\n * Resolve the active path without breaking SSR/prerender: the first render\n * always returns `null` (matching the server output), and the real value lands\n * in an effect. `popstate` covers back/forward; hosts that navigate with\n * `pushState` should pass `currentPath` from their router.\n */\nfunction useCurrentPath(explicit?: string): string | null {\n const [path, setPath] = useState<string | null>(explicit ?? null);\n\n useEffect(() => {\n if (explicit !== undefined) {\n setPath(explicit);\n return;\n }\n if (typeof window === \"undefined\") return;\n const read = () => setPath(window.location.pathname);\n read();\n window.addEventListener(\"popstate\", read);\n return () => window.removeEventListener(\"popstate\", read);\n }, [explicit]);\n\n return path;\n}\n\n/** `/explore` and `/explore/` are the same page. */\nfunction samePath(a: string | null, b: string): boolean {\n if (!a) return false;\n const norm = (v: string) => (v.length > 1 ? v.replace(/\\/+$/, \"\") : v);\n return norm(a) === norm(b);\n}\n\nexport function ExploreCta({\n variant,\n href = \"/explore\",\n label = \"Ask AI\",\n className,\n onNavigate,\n onClick,\n currentPath,\n}: ExploreCtaProps) {\n const path = useCurrentPath(currentPath);\n const isActive = samePath(path, href);\n\n // The floating bubble exists to get visitors TO /explore. On /explore it is\n // pure obstruction, so it removes itself.\n if (variant === \"floating\" && isActive) return null;\n\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n onClick?.();\n if (!onNavigate) return;\n // Let the browser handle modified clicks (new tab, download, …).\n if (\n event.defaultPrevented ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey ||\n event.button !== 0\n ) {\n return;\n }\n event.preventDefault();\n onNavigate(href);\n };\n\n const classes: Record<ExploreCtaProps[\"variant\"], string> = {\n header:\n \"inline-flex items-center gap-1.5 rounded-full bg-gradient-to-r from-primary to-primary/70 px-3.5 py-1.5 text-sm font-medium text-primary-foreground shadow-sm transition-opacity hover:opacity-90\",\n mobile:\n \"inline-flex w-full items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 text-sm font-medium text-card-foreground transition-colors hover:bg-accent hover:text-accent-foreground\",\n floating:\n \"fixed bottom-5 right-5 z-40 inline-flex items-center gap-2 rounded-full bg-gradient-to-r from-primary to-primary/70 px-4 py-3 text-sm font-medium text-primary-foreground shadow-lg transition-opacity hover:opacity-90\",\n };\n\n const hooks: Record<ExploreCtaProps[\"variant\"], string> = {\n header: \"cta-header\",\n mobile: \"cta-mobile\",\n floating: \"cta-floating\",\n };\n\n return (\n <a\n data-boff-explore={hooks[variant]}\n href={href}\n onClick={handleClick}\n aria-label={label}\n aria-current={isActive ? \"page\" : undefined}\n className={cn(\n classes[variant],\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n className,\n )}\n >\n <Sparkles\n className={cn(\n \"shrink-0\",\n variant === \"floating\" ? \"h-4 w-4\" : \"h-3.5 w-3.5\",\n )}\n aria-hidden=\"true\"\n />\n {/* Floating: label is visible from `sm` up; `aria-label` carries the\n accessible name at every width. */}\n <span className={cn(variant === \"floating\" && \"hidden sm:inline\")}>\n {label}\n </span>\n </a>\n );\n}\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { Sparkles } from 'lucide-react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
function useCurrentPath(explicit) {
|
|
11
|
+
const [path, setPath] = useState(explicit ?? null);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (explicit !== void 0) {
|
|
14
|
+
setPath(explicit);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (typeof window === "undefined") return;
|
|
18
|
+
const read = () => setPath(window.location.pathname);
|
|
19
|
+
read();
|
|
20
|
+
window.addEventListener("popstate", read);
|
|
21
|
+
return () => window.removeEventListener("popstate", read);
|
|
22
|
+
}, [explicit]);
|
|
23
|
+
return path;
|
|
24
|
+
}
|
|
25
|
+
function samePath(a, b) {
|
|
26
|
+
if (!a) return false;
|
|
27
|
+
const norm = (v) => v.length > 1 ? v.replace(/\/+$/, "") : v;
|
|
28
|
+
return norm(a) === norm(b);
|
|
29
|
+
}
|
|
30
|
+
function ExploreCta({
|
|
31
|
+
variant,
|
|
32
|
+
href = "/explore",
|
|
33
|
+
label = "Ask AI",
|
|
34
|
+
className,
|
|
35
|
+
onNavigate,
|
|
36
|
+
onClick,
|
|
37
|
+
currentPath
|
|
38
|
+
}) {
|
|
39
|
+
const path = useCurrentPath(currentPath);
|
|
40
|
+
const isActive = samePath(path, href);
|
|
41
|
+
if (variant === "floating" && isActive) return null;
|
|
42
|
+
const handleClick = (event) => {
|
|
43
|
+
onClick?.();
|
|
44
|
+
if (!onNavigate) return;
|
|
45
|
+
if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
onNavigate(href);
|
|
50
|
+
};
|
|
51
|
+
const classes = {
|
|
52
|
+
header: "inline-flex items-center gap-1.5 rounded-full bg-gradient-to-r from-primary to-primary/70 px-3.5 py-1.5 text-sm font-medium text-primary-foreground shadow-sm transition-opacity hover:opacity-90",
|
|
53
|
+
mobile: "inline-flex w-full items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 text-sm font-medium text-card-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
54
|
+
floating: "fixed bottom-5 right-5 z-40 inline-flex items-center gap-2 rounded-full bg-gradient-to-r from-primary to-primary/70 px-4 py-3 text-sm font-medium text-primary-foreground shadow-lg transition-opacity hover:opacity-90"
|
|
55
|
+
};
|
|
56
|
+
const hooks = {
|
|
57
|
+
header: "cta-header",
|
|
58
|
+
mobile: "cta-mobile",
|
|
59
|
+
floating: "cta-floating"
|
|
60
|
+
};
|
|
61
|
+
return /* @__PURE__ */ jsxs(
|
|
62
|
+
"a",
|
|
63
|
+
{
|
|
64
|
+
"data-boff-explore": hooks[variant],
|
|
65
|
+
href,
|
|
66
|
+
onClick: handleClick,
|
|
67
|
+
"aria-label": label,
|
|
68
|
+
"aria-current": isActive ? "page" : void 0,
|
|
69
|
+
className: cn(
|
|
70
|
+
classes[variant],
|
|
71
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
72
|
+
className
|
|
73
|
+
),
|
|
74
|
+
children: [
|
|
75
|
+
/* @__PURE__ */ jsx(
|
|
76
|
+
Sparkles,
|
|
77
|
+
{
|
|
78
|
+
className: cn(
|
|
79
|
+
"shrink-0",
|
|
80
|
+
variant === "floating" ? "h-4 w-4" : "h-3.5 w-3.5"
|
|
81
|
+
),
|
|
82
|
+
"aria-hidden": "true"
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
/* @__PURE__ */ jsx("span", { className: cn(variant === "floating" && "hidden sm:inline"), children: label })
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { ExploreCta };
|
|
92
|
+
//# sourceMappingURL=explore-cta.mjs.map
|
|
93
|
+
//# sourceMappingURL=explore-cta.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/cn.ts","../../src/components/explore-cta.tsx"],"names":[],"mappings":"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","file":"explore-cta.mjs","sourcesContent":["/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx and tailwind-merge for optimal class handling\n */\n\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge multiple class names intelligently\n * - Handles conditional classes via clsx\n * - Deduplicates and resolves Tailwind conflicts via tailwind-merge\n *\n * @example\n * ```ts\n * cn('px-4 py-2', 'bg-blue-500', { 'text-white': isActive })\n * // => 'px-4 py-2 bg-blue-500 text-white' (if isActive is true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * ExploreCta — the entry point to `/explore` from a site's navigation.\n *\n * Three shapes of the same link, so a product site can place it once in the\n * desktop header, once in the mobile menu, and optionally as a floating bubble\n * on content pages, without any of them re-implementing the styling:\n *\n * - `header` — a gradient sparkle pill for the desktop nav.\n * - `mobile` — a compact full-width row for a mobile menu list.\n * - `floating` — a fixed bottom-right bubble; hides itself on `/explore`\n * so it never covers the page it links to.\n *\n * Import from the package ROOT (`@burdenoff/website-sdk`) for consistency with\n * `ExplorePage`, which must be root-imported because of tsup's\n * `splitting: false`. This component holds no context of its own, so a\n * sub-path import also works — but keeping both on the root import is one less\n * footgun for the 38 sites that ship them together.\n *\n * @example\n * ```tsx\n * import { ExploreCta } from '@burdenoff/website-sdk'\n *\n * <ExploreCta variant=\"header\" onNavigate={(href) => navigate(href)} />\n * <ExploreCta variant=\"floating\" currentPath={location.pathname} />\n * ```\n */\n\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nimport { Sparkles } from \"lucide-react\";\n\nimport { cn } from \"../utils/cn\";\n\nexport interface ExploreCtaProps {\n /** Which of the three presentations to render. */\n variant: \"header\" | \"mobile\" | \"floating\";\n /** Target route. Default `/explore`. */\n href?: string;\n /** Visible label / accessible name. Default `Ask AI`. */\n label?: string;\n /** Extra classes for the rendered element. */\n className?: string;\n /** SPA navigation. When omitted the component renders a plain `<a href>`. */\n onNavigate?: (href: string) => void;\n /** Extra click handler (analytics, closing a mobile menu, …). */\n onClick?: () => void;\n /**\n * Current route, when the host knows it (`useLocation().pathname`).\n * Used to mark the link as `aria-current=\"page\"` and to hide the floating\n * bubble on the Explore page itself. Falls back to `window.location.pathname`,\n * which cannot observe SPA `pushState` navigations on its own.\n */\n currentPath?: string;\n}\n\n/**\n * Resolve the active path without breaking SSR/prerender: the first render\n * always returns `null` (matching the server output), and the real value lands\n * in an effect. `popstate` covers back/forward; hosts that navigate with\n * `pushState` should pass `currentPath` from their router.\n */\nfunction useCurrentPath(explicit?: string): string | null {\n const [path, setPath] = useState<string | null>(explicit ?? null);\n\n useEffect(() => {\n if (explicit !== undefined) {\n setPath(explicit);\n return;\n }\n if (typeof window === \"undefined\") return;\n const read = () => setPath(window.location.pathname);\n read();\n window.addEventListener(\"popstate\", read);\n return () => window.removeEventListener(\"popstate\", read);\n }, [explicit]);\n\n return path;\n}\n\n/** `/explore` and `/explore/` are the same page. */\nfunction samePath(a: string | null, b: string): boolean {\n if (!a) return false;\n const norm = (v: string) => (v.length > 1 ? v.replace(/\\/+$/, \"\") : v);\n return norm(a) === norm(b);\n}\n\nexport function ExploreCta({\n variant,\n href = \"/explore\",\n label = \"Ask AI\",\n className,\n onNavigate,\n onClick,\n currentPath,\n}: ExploreCtaProps) {\n const path = useCurrentPath(currentPath);\n const isActive = samePath(path, href);\n\n // The floating bubble exists to get visitors TO /explore. On /explore it is\n // pure obstruction, so it removes itself.\n if (variant === \"floating\" && isActive) return null;\n\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n onClick?.();\n if (!onNavigate) return;\n // Let the browser handle modified clicks (new tab, download, …).\n if (\n event.defaultPrevented ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey ||\n event.button !== 0\n ) {\n return;\n }\n event.preventDefault();\n onNavigate(href);\n };\n\n const classes: Record<ExploreCtaProps[\"variant\"], string> = {\n header:\n \"inline-flex items-center gap-1.5 rounded-full bg-gradient-to-r from-primary to-primary/70 px-3.5 py-1.5 text-sm font-medium text-primary-foreground shadow-sm transition-opacity hover:opacity-90\",\n mobile:\n \"inline-flex w-full items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 text-sm font-medium text-card-foreground transition-colors hover:bg-accent hover:text-accent-foreground\",\n floating:\n \"fixed bottom-5 right-5 z-40 inline-flex items-center gap-2 rounded-full bg-gradient-to-r from-primary to-primary/70 px-4 py-3 text-sm font-medium text-primary-foreground shadow-lg transition-opacity hover:opacity-90\",\n };\n\n const hooks: Record<ExploreCtaProps[\"variant\"], string> = {\n header: \"cta-header\",\n mobile: \"cta-mobile\",\n floating: \"cta-floating\",\n };\n\n return (\n <a\n data-boff-explore={hooks[variant]}\n href={href}\n onClick={handleClick}\n aria-label={label}\n aria-current={isActive ? \"page\" : undefined}\n className={cn(\n classes[variant],\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n className,\n )}\n >\n <Sparkles\n className={cn(\n \"shrink-0\",\n variant === \"floating\" ? \"h-4 w-4\" : \"h-3.5 w-3.5\",\n )}\n aria-hidden=\"true\"\n />\n {/* Floating: label is visible from `sm` up; `aria-label` carries the\n accessible name at every width. */}\n <span className={cn(variant === \"floating\" && \"hidden sm:inline\")}>\n {label}\n </span>\n </a>\n );\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
interface ExplorePageProps {
|
|
4
|
+
/** SEO metadata. Rendered through `PageHead` when provided. */
|
|
5
|
+
seo?: {
|
|
6
|
+
title?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
keywords?: string;
|
|
9
|
+
image?: string;
|
|
10
|
+
url?: string;
|
|
11
|
+
};
|
|
12
|
+
/** Slug of the product site this page lives on — a soft hint to the harness. */
|
|
13
|
+
productSlug?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Example prompts shown when `exploreCatalog` supplies none. Resolution
|
|
16
|
+
* order: catalog → this prop → the SDK's built-in list.
|
|
17
|
+
*/
|
|
18
|
+
examplePrompts?: string[];
|
|
19
|
+
/** Extra classes for the page root. */
|
|
20
|
+
className?: string;
|
|
21
|
+
/**
|
|
22
|
+
* `viewport` (default) pins the chat to the viewport with an internally
|
|
23
|
+
* scrolling transcript — the right feel for a dedicated `/explore` route.
|
|
24
|
+
* `auto` lets the page grow, for embedding inside a longer page.
|
|
25
|
+
*/
|
|
26
|
+
heightMode?: "viewport" | "auto";
|
|
27
|
+
/**
|
|
28
|
+
* Chrome to subtract in `viewport` mode — the site header (and footer)
|
|
29
|
+
* height. A number is treated as pixels. Default `0`.
|
|
30
|
+
*/
|
|
31
|
+
viewportOffset?: string | number;
|
|
32
|
+
/** SPA navigation for internal CTA paths. Falls back to a plain link. */
|
|
33
|
+
onNavigate?: (path: string) => void;
|
|
34
|
+
/** Fired with the message text every time a send is accepted. */
|
|
35
|
+
onSend?: (message: string) => void;
|
|
36
|
+
/** Overrides `exploreCatalog.welcomeTitle` — also the page `<h1>`. */
|
|
37
|
+
welcomeTitle?: string;
|
|
38
|
+
/** Overrides `exploreCatalog.welcomeBody`. */
|
|
39
|
+
welcomeBody?: string;
|
|
40
|
+
}
|
|
41
|
+
declare function ExplorePage({ seo, productSlug, examplePrompts, className, heightMode, viewportOffset, onNavigate, onSend, welcomeTitle, welcomeBody, }: ExplorePageProps): react.JSX.Element;
|
|
42
|
+
|
|
43
|
+
export { ExplorePage, type ExplorePageProps };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
interface ExplorePageProps {
|
|
4
|
+
/** SEO metadata. Rendered through `PageHead` when provided. */
|
|
5
|
+
seo?: {
|
|
6
|
+
title?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
keywords?: string;
|
|
9
|
+
image?: string;
|
|
10
|
+
url?: string;
|
|
11
|
+
};
|
|
12
|
+
/** Slug of the product site this page lives on — a soft hint to the harness. */
|
|
13
|
+
productSlug?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Example prompts shown when `exploreCatalog` supplies none. Resolution
|
|
16
|
+
* order: catalog → this prop → the SDK's built-in list.
|
|
17
|
+
*/
|
|
18
|
+
examplePrompts?: string[];
|
|
19
|
+
/** Extra classes for the page root. */
|
|
20
|
+
className?: string;
|
|
21
|
+
/**
|
|
22
|
+
* `viewport` (default) pins the chat to the viewport with an internally
|
|
23
|
+
* scrolling transcript — the right feel for a dedicated `/explore` route.
|
|
24
|
+
* `auto` lets the page grow, for embedding inside a longer page.
|
|
25
|
+
*/
|
|
26
|
+
heightMode?: "viewport" | "auto";
|
|
27
|
+
/**
|
|
28
|
+
* Chrome to subtract in `viewport` mode — the site header (and footer)
|
|
29
|
+
* height. A number is treated as pixels. Default `0`.
|
|
30
|
+
*/
|
|
31
|
+
viewportOffset?: string | number;
|
|
32
|
+
/** SPA navigation for internal CTA paths. Falls back to a plain link. */
|
|
33
|
+
onNavigate?: (path: string) => void;
|
|
34
|
+
/** Fired with the message text every time a send is accepted. */
|
|
35
|
+
onSend?: (message: string) => void;
|
|
36
|
+
/** Overrides `exploreCatalog.welcomeTitle` — also the page `<h1>`. */
|
|
37
|
+
welcomeTitle?: string;
|
|
38
|
+
/** Overrides `exploreCatalog.welcomeBody`. */
|
|
39
|
+
welcomeBody?: string;
|
|
40
|
+
}
|
|
41
|
+
declare function ExplorePage({ seo, productSlug, examplePrompts, className, heightMode, viewportOffset, onNavigate, onSend, welcomeTitle, welcomeBody, }: ExplorePageProps): react.JSX.Element;
|
|
42
|
+
|
|
43
|
+
export { ExplorePage, type ExplorePageProps };
|