@salesmind-ai/design-system 0.2.1 → 0.3.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.
Files changed (142) hide show
  1. package/README.md +32 -2
  2. package/dist/AppearancePanel-UT57J69V.d.cts +51 -0
  3. package/dist/AppearancePanel-UT57J69V.d.ts +51 -0
  4. package/dist/AppearanceProvider-C36a8-eb.d.cts +45 -0
  5. package/dist/AppearanceProvider-C36a8-eb.d.ts +45 -0
  6. package/dist/Breadcrumb-RX-B_gDV.d.cts +44 -0
  7. package/dist/Breadcrumb-RX-B_gDV.d.ts +44 -0
  8. package/dist/ExportMenu-A2TLFiVv.d.cts +311 -0
  9. package/dist/ExportMenu-C8qck5AT.d.ts +311 -0
  10. package/dist/SectionShell-BfBw5q0Y.d.cts +18 -0
  11. package/dist/SectionShell-BfBw5q0Y.d.ts +18 -0
  12. package/dist/Select-BdZmK0Lt.d.cts +66 -0
  13. package/dist/Select-BdZmK0Lt.d.ts +66 -0
  14. package/dist/admin/index.cjs +2941 -0
  15. package/dist/admin/index.cjs.map +1 -0
  16. package/dist/admin/index.css +4145 -0
  17. package/dist/admin/index.css.map +1 -0
  18. package/dist/admin/index.d.cts +491 -0
  19. package/dist/admin/index.d.ts +491 -0
  20. package/dist/admin/index.js +2918 -0
  21. package/dist/admin/index.js.map +1 -0
  22. package/dist/{audit-CiyPkxk1.d.cts → audit-BS2fn7M4.d.ts} +2 -51
  23. package/dist/{audit-CiyPkxk1.d.ts → audit-DwCmg32J.d.cts} +2 -51
  24. package/dist/blog/index.cjs +1074 -0
  25. package/dist/blog/index.cjs.map +1 -0
  26. package/dist/blog/index.css +1422 -0
  27. package/dist/blog/index.css.map +1 -0
  28. package/dist/blog/index.d.cts +233 -0
  29. package/dist/blog/index.d.ts +233 -0
  30. package/dist/blog/index.js +1056 -0
  31. package/dist/blog/index.js.map +1 -0
  32. package/dist/chart-types-BGVVO-zl.d.cts +208 -0
  33. package/dist/chart-types-BGVVO-zl.d.ts +208 -0
  34. package/dist/charts/index.cjs +2698 -0
  35. package/dist/charts/index.cjs.map +1 -0
  36. package/dist/charts/index.css +1167 -0
  37. package/dist/charts/index.css.map +1 -0
  38. package/dist/charts/index.d.cts +453 -0
  39. package/dist/charts/index.d.ts +453 -0
  40. package/dist/charts/index.js +2682 -0
  41. package/dist/charts/index.js.map +1 -0
  42. package/dist/core/index.cjs +526 -395
  43. package/dist/core/index.cjs.map +1 -1
  44. package/dist/core/index.css +294 -0
  45. package/dist/core/index.css.map +1 -1
  46. package/dist/core/index.d.cts +7 -982
  47. package/dist/core/index.d.ts +7 -982
  48. package/dist/core/index.js +476 -351
  49. package/dist/core/index.js.map +1 -1
  50. package/dist/i18n/index.cjs +585 -0
  51. package/dist/i18n/index.cjs.map +1 -0
  52. package/dist/i18n/index.d.cts +855 -0
  53. package/dist/i18n/index.d.ts +855 -0
  54. package/dist/i18n/index.js +547 -0
  55. package/dist/i18n/index.js.map +1 -0
  56. package/dist/index.cjs +3 -2
  57. package/dist/index.cjs.map +1 -1
  58. package/dist/index.css +11 -7
  59. package/dist/index.css.map +1 -1
  60. package/dist/index.d.cts +22 -1290
  61. package/dist/index.d.ts +22 -1290
  62. package/dist/index.js +3 -2
  63. package/dist/index.js.map +1 -1
  64. package/dist/marketing/index.cjs +2144 -3023
  65. package/dist/marketing/index.cjs.map +1 -1
  66. package/dist/marketing/index.css +3729 -4824
  67. package/dist/marketing/index.css.map +1 -1
  68. package/dist/marketing/index.d.cts +1351 -4
  69. package/dist/marketing/index.d.ts +1351 -4
  70. package/dist/marketing/index.js +2190 -3054
  71. package/dist/marketing/index.js.map +1 -1
  72. package/dist/motion/index.cjs +1230 -0
  73. package/dist/motion/index.cjs.map +1 -0
  74. package/dist/motion/index.css +699 -0
  75. package/dist/motion/index.css.map +1 -0
  76. package/dist/motion/index.d.cts +68 -0
  77. package/dist/motion/index.d.ts +68 -0
  78. package/dist/motion/index.js +1218 -0
  79. package/dist/motion/index.js.map +1 -0
  80. package/dist/nav/index.cjs +1533 -0
  81. package/dist/nav/index.cjs.map +1 -0
  82. package/dist/nav/index.css +1984 -0
  83. package/dist/nav/index.css.map +1 -0
  84. package/dist/nav/index.d.cts +279 -0
  85. package/dist/nav/index.d.ts +279 -0
  86. package/dist/nav/index.js +1501 -0
  87. package/dist/nav/index.js.map +1 -0
  88. package/dist/report/index.cjs +26 -1649
  89. package/dist/report/index.cjs.map +1 -1
  90. package/dist/report/index.css +0 -963
  91. package/dist/report/index.css.map +1 -1
  92. package/dist/report/index.d.cts +4 -2
  93. package/dist/report/index.d.ts +4 -2
  94. package/dist/report/index.js +27 -1640
  95. package/dist/report/index.js.map +1 -1
  96. package/dist/sections/index.cjs +385 -0
  97. package/dist/sections/index.cjs.map +1 -0
  98. package/dist/sections/index.css +815 -0
  99. package/dist/sections/index.css.map +1 -0
  100. package/dist/sections/index.d.cts +69 -0
  101. package/dist/sections/index.d.ts +69 -0
  102. package/dist/sections/index.js +374 -0
  103. package/dist/sections/index.js.map +1 -0
  104. package/dist/social-proof/index.cjs +1255 -0
  105. package/dist/social-proof/index.cjs.map +1 -0
  106. package/dist/social-proof/index.css +1423 -0
  107. package/dist/social-proof/index.css.map +1 -0
  108. package/dist/social-proof/index.d.cts +258 -0
  109. package/dist/social-proof/index.d.ts +258 -0
  110. package/dist/social-proof/index.js +1238 -0
  111. package/dist/social-proof/index.js.map +1 -0
  112. package/dist/theme/index.cjs +573 -0
  113. package/dist/theme/index.cjs.map +1 -0
  114. package/dist/theme/index.css +464 -0
  115. package/dist/theme/index.css.map +1 -0
  116. package/dist/theme/index.d.cts +48 -0
  117. package/dist/theme/index.d.ts +48 -0
  118. package/dist/theme/index.js +558 -0
  119. package/dist/theme/index.js.map +1 -0
  120. package/dist/types-DAlgDGzw.d.cts +52 -0
  121. package/dist/types-DAlgDGzw.d.ts +52 -0
  122. package/dist/web/client/index.cjs +501 -0
  123. package/dist/web/client/index.cjs.map +1 -0
  124. package/dist/web/client/index.css +456 -0
  125. package/dist/web/client/index.css.map +1 -0
  126. package/dist/web/client/index.d.cts +172 -0
  127. package/dist/web/client/index.d.ts +172 -0
  128. package/dist/web/client/index.js +486 -0
  129. package/dist/web/client/index.js.map +1 -0
  130. package/dist/web/index.d.cts +6 -893
  131. package/dist/web/index.d.ts +6 -893
  132. package/dist/web/server/index.cjs +569 -0
  133. package/dist/web/server/index.cjs.map +1 -0
  134. package/dist/web/server/index.d.cts +725 -0
  135. package/dist/web/server/index.d.ts +725 -0
  136. package/dist/web/server/index.js +562 -0
  137. package/dist/web/server/index.js.map +1 -0
  138. package/package.json +81 -9
  139. package/dist/ExportMenu-hEe5MhLq.d.cts +0 -1027
  140. package/dist/ExportMenu-hEe5MhLq.d.ts +0 -1027
  141. package/dist/index-B64suAAc.d.cts +0 -1498
  142. package/dist/index-B64suAAc.d.ts +0 -1498
@@ -0,0 +1,172 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * Generic analytics script loader.
6
+ *
7
+ * Framework-agnostic: injects a `<script>` tag into `<head>` once,
8
+ * guards against double-loading, and respects cookie consent.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { createAnalyticsLoader } from '@salesmind-ai/design-system/web';
13
+ *
14
+ * const loadGA = createAnalyticsLoader({
15
+ * id: 'ga-script',
16
+ * src: (gaId) => `https://www.googletagmanager.com/gtag/js?id=${gaId}`,
17
+ * onLoad: (gaId) => {
18
+ * window.dataLayer = window.dataLayer || [];
19
+ * function gtag(...args: unknown[]) { window.dataLayer.push(args); }
20
+ * gtag('js', new Date());
21
+ * gtag('config', gaId);
22
+ * },
23
+ * });
24
+ *
25
+ * // Later, after consent:
26
+ * loadGA('G-XXXXXX');
27
+ * ```
28
+ */
29
+ interface AnalyticsLoaderConfig<TId extends string = string> {
30
+ /** Unique DOM id for the script element (prevents double-loading) */
31
+ id: string;
32
+ /** Build the script src URL from the tracking ID */
33
+ src: (trackingId: TId) => string;
34
+ /** Async attribute on the script tag (default: true) */
35
+ async?: boolean;
36
+ /** Called after the script is appended to the DOM */
37
+ onLoad?: (trackingId: TId) => void;
38
+ /** Custom inline script content instead of an external src */
39
+ inlineScript?: (trackingId: TId) => string;
40
+ }
41
+ /**
42
+ * Create a reusable analytics loader function.
43
+ * The returned function injects the script once and is safe to call multiple times.
44
+ */
45
+ declare function createAnalyticsLoader<TId extends string = string>(config: AnalyticsLoaderConfig<TId>): (trackingId: TId) => void;
46
+ declare global {
47
+ interface Window {
48
+ dataLayer: unknown[];
49
+ clarity?: (...args: unknown[]) => void;
50
+ }
51
+ }
52
+ /**
53
+ * Load Google Analytics (gtag.js).
54
+ * Call with your GA measurement ID after cookie consent.
55
+ */
56
+ declare const loadGoogleAnalytics: (trackingId: string) => void;
57
+ /**
58
+ * Load Microsoft Clarity.
59
+ * Call with your Clarity project ID after cookie consent.
60
+ */
61
+ declare const loadClarity: (trackingId: string) => void;
62
+
63
+ /** Cookie consent state */
64
+ type ConsentStatus = 'granted' | 'denied' | null;
65
+ /** Event name dispatched on window when consent changes */
66
+ declare const COOKIE_CONSENT_EVENT = "cookie_consent_granted";
67
+ /** localStorage key for cookie consent */
68
+ declare const COOKIE_CONSENT_KEY = "cookie_consent";
69
+ /** Labels for i18n support */
70
+ interface CookieConsentLabels {
71
+ title?: string;
72
+ description?: string;
73
+ privacyLinkText?: string;
74
+ acceptLabel?: string;
75
+ declineLabel?: string;
76
+ }
77
+ interface CookieConsentProps {
78
+ /** Delay in ms before showing the banner (default: 1000) */
79
+ delay?: number;
80
+ /** URL to the privacy policy page (default: "/legal/privacy") */
81
+ privacyUrl?: string;
82
+ /** Called when the user accepts cookies */
83
+ onAccept?: () => void;
84
+ /** Called when the user declines cookies */
85
+ onDecline?: () => void;
86
+ /** Override default labels for i18n */
87
+ labels?: CookieConsentLabels;
88
+ /** Custom className for the container */
89
+ className?: string;
90
+ }
91
+ /**
92
+ * CookieConsent — GDPR-compliant cookie consent banner.
93
+ *
94
+ * - Animated entrance/exit via CSS transitions (no framer-motion)
95
+ * - Respects prior consent stored in localStorage
96
+ * - Dispatches a `cookie_consent_granted` event on the window for analytics loaders
97
+ * - Uses DS Button component
98
+ *
99
+ * @example
100
+ * ```tsx
101
+ * import { CookieConsent } from '@salesmind-ai/design-system/web';
102
+ *
103
+ * <CookieConsent
104
+ * privacyUrl="/legal/privacy"
105
+ * onAccept={() => loadGoogleAnalytics('G-XXXX')}
106
+ * />
107
+ * ```
108
+ */
109
+ declare function CookieConsent({ delay, privacyUrl, onAccept, onDecline, labels, className, }: CookieConsentProps): react_jsx_runtime.JSX.Element | null;
110
+
111
+ /**
112
+ * React hook that tracks cookie consent status.
113
+ *
114
+ * Returns `true` once the user has granted cookie consent,
115
+ * `false` if denied, `null` if not yet decided.
116
+ *
117
+ * Listens for the `cookie_consent_granted` window event dispatched
118
+ * by the CookieConsent component.
119
+ *
120
+ * @example
121
+ * ```tsx
122
+ * const hasConsent = useCookieConsent();
123
+ *
124
+ * useEffect(() => {
125
+ * if (hasConsent) loadGoogleAnalytics('G-XXXX');
126
+ * }, [hasConsent]);
127
+ * ```
128
+ */
129
+ declare function useCookieConsent(): ConsentStatus;
130
+
131
+ /** Arbitrary properties bag attached to every analytics event. */
132
+ type AnalyticsEventProps = Record<string, string | number | boolean | undefined>;
133
+ /** Signature for the track function provided by AnalyticsProvider. */
134
+ type TrackFn = (event: string, props?: AnalyticsEventProps) => void;
135
+ /** Value exposed by the AnalyticsContext. */
136
+ interface AnalyticsContextValue {
137
+ track: TrackFn;
138
+ }
139
+
140
+ /** Props for the AnalyticsProvider component */
141
+ interface AnalyticsProviderProps {
142
+ /** Callback invoked on every track() call. Wire this to your analytics backend. */
143
+ onTrack: TrackFn;
144
+ /** Enable console logging in development (default: false) */
145
+ debug?: boolean;
146
+ children: ReactNode;
147
+ }
148
+ /**
149
+ * Provides analytics event tracking to all descendant DS components.
150
+ *
151
+ * @example
152
+ * ```tsx
153
+ * <AnalyticsProvider
154
+ * onTrack={(event, props) => {
155
+ * window.gtag?.('event', event, props);
156
+ * }}
157
+ * >
158
+ * <App />
159
+ * </AnalyticsProvider>
160
+ * ```
161
+ */
162
+ declare function AnalyticsProvider({ onTrack, debug, children }: AnalyticsProviderProps): react_jsx_runtime.JSX.Element;
163
+
164
+ /**
165
+ * Hook that returns the analytics `track` function.
166
+ *
167
+ * Safe to call without a wrapping `<AnalyticsProvider>` — all calls
168
+ * become no-ops in that case (Storybook, tests, SSR).
169
+ */
170
+ declare function useAnalytics(): AnalyticsContextValue;
171
+
172
+ export { type AnalyticsContextValue, type AnalyticsEventProps, type AnalyticsLoaderConfig, AnalyticsProvider, type AnalyticsProviderProps, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, type ConsentStatus, CookieConsent, type CookieConsentLabels, type CookieConsentProps, type TrackFn, createAnalyticsLoader, loadClarity, loadGoogleAnalytics, useAnalytics, useCookieConsent };
@@ -0,0 +1,486 @@
1
+ "use client";
2
+ import React, { createContext, forwardRef, useState, useEffect, useContext, useCallback, useMemo } from 'react';
3
+ import { Slot } from '@radix-ui/react-slot';
4
+ import clsx from 'clsx';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+ import { X } from 'lucide-react';
7
+
8
+ // src/web/analytics/create-analytics-loader.ts
9
+ function createAnalyticsLoader(config) {
10
+ return (trackingId) => {
11
+ if (typeof document === "undefined") return;
12
+ if (document.getElementById(config.id)) return;
13
+ const script = document.createElement("script");
14
+ script.id = config.id;
15
+ if (config.inlineScript) {
16
+ script.textContent = config.inlineScript(trackingId);
17
+ } else {
18
+ script.async = config.async ?? true;
19
+ script.src = config.src(trackingId);
20
+ }
21
+ document.head.appendChild(script);
22
+ config.onLoad?.(trackingId);
23
+ };
24
+ }
25
+ var loadGoogleAnalytics = createAnalyticsLoader({
26
+ id: "ga-script",
27
+ src: (gaId) => `https://www.googletagmanager.com/gtag/js?id=${gaId}`,
28
+ onLoad: (gaId) => {
29
+ window.dataLayer = window.dataLayer || [];
30
+ window.dataLayer.push(["js", /* @__PURE__ */ new Date()]);
31
+ window.dataLayer.push(["config", gaId]);
32
+ }
33
+ });
34
+ var loadClarity = createAnalyticsLoader({
35
+ id: "clarity-script",
36
+ src: (clarityId) => `https://www.clarity.ms/tag/${clarityId}`,
37
+ onLoad: () => {
38
+ if (!window.clarity) {
39
+ const queue = [];
40
+ window.clarity = (...args) => {
41
+ queue.push(args);
42
+ };
43
+ window.clarity.q = queue;
44
+ }
45
+ }
46
+ });
47
+ var Button = React.forwardRef(
48
+ ({ className, variant = "primary", size = "md", asChild = false, isLoading = false, children, disabled, ...props }, ref) => {
49
+ const buttonClass = clsx(
50
+ "ds-button",
51
+ `ds-button--${variant}`,
52
+ size === "icon" ? "ds-button--icon ds-button--md" : `ds-button--${size}`,
53
+ isLoading && "ds-button--loading",
54
+ className
55
+ );
56
+ if (asChild) {
57
+ return /* @__PURE__ */ jsx(
58
+ Slot,
59
+ {
60
+ ref,
61
+ "aria-disabled": isLoading || disabled || void 0,
62
+ className: buttonClass,
63
+ ...props,
64
+ children
65
+ }
66
+ );
67
+ }
68
+ return /* @__PURE__ */ jsxs(
69
+ "button",
70
+ {
71
+ ref,
72
+ disabled: isLoading || disabled,
73
+ className: buttonClass,
74
+ ...props,
75
+ children: [
76
+ isLoading && /* @__PURE__ */ jsx(
77
+ "svg",
78
+ {
79
+ className: "ds-button__spinner",
80
+ xmlns: "http://www.w3.org/2000/svg",
81
+ width: "1em",
82
+ height: "1em",
83
+ viewBox: "0 0 24 24",
84
+ fill: "none",
85
+ stroke: "currentColor",
86
+ strokeWidth: "2",
87
+ strokeLinecap: "round",
88
+ strokeLinejoin: "round",
89
+ children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
90
+ }
91
+ ),
92
+ /* @__PURE__ */ jsx("span", { className: clsx("ds-button__content", isLoading && "ds-button__content--hidden"), children })
93
+ ]
94
+ }
95
+ );
96
+ }
97
+ );
98
+ Button.displayName = "Button";
99
+ var UtmContext = createContext(null);
100
+
101
+ // src/web/utm/useUtmDefaults.ts
102
+ function useUtmDefaults() {
103
+ return useContext(UtmContext);
104
+ }
105
+
106
+ // src/web/utm/builders.ts
107
+ var PLACEHOLDER_ORIGIN = "https://__placeholder__.internal";
108
+ function buildUtmUrl(baseUrl, params) {
109
+ const isRelative = baseUrl.startsWith("/");
110
+ let url;
111
+ try {
112
+ url = isRelative ? new URL(baseUrl, PLACEHOLDER_ORIGIN) : new URL(baseUrl);
113
+ } catch {
114
+ return baseUrl;
115
+ }
116
+ const existingParams = [];
117
+ url.searchParams.forEach((value, key) => {
118
+ existingParams.push([key, value]);
119
+ });
120
+ for (const [key] of existingParams) {
121
+ url.searchParams.delete(key);
122
+ }
123
+ for (const [key, value] of existingParams) {
124
+ if (!key.startsWith("utm_")) {
125
+ url.searchParams.set(key, value);
126
+ }
127
+ }
128
+ url.searchParams.set("utm_source", params.source);
129
+ url.searchParams.set("utm_medium", params.medium);
130
+ url.searchParams.set("utm_campaign", params.campaign);
131
+ if (params.term !== void 0) {
132
+ url.searchParams.set("utm_term", params.term);
133
+ }
134
+ if (params.content !== void 0) {
135
+ url.searchParams.set("utm_content", params.content);
136
+ }
137
+ if (isRelative) {
138
+ return url.href.replace(PLACEHOLDER_ORIGIN, "");
139
+ }
140
+ return url.href;
141
+ }
142
+
143
+ // src/web/utm/classifiers.ts
144
+ var INTERNAL_PATTERNS = [
145
+ /^\/(?!\/)/,
146
+ // Relative paths
147
+ /^https?:\/\/(www\.)?sales-mind\.ai/i,
148
+ // Marketing site
149
+ /^https?:\/\/app\.sales-mind\.ai/i,
150
+ // Web app
151
+ /^https?:\/\/apps\.sales-mind\.ai/i,
152
+ // Web app (legacy)
153
+ /^https?:\/\/meet\.sales-mind\.ai/i,
154
+ // Booking
155
+ /^https?:\/\/docs\.sales-mind\.ai/i
156
+ // Docs
157
+ ];
158
+ var SYSTEM_PATTERNS = [
159
+ /^https?:\/\/.*\/api\//i,
160
+ // API endpoints
161
+ /^https?:\/\/.*\/webhook/i,
162
+ // Webhooks
163
+ /^https?:\/\/.*\/oauth/i,
164
+ // OAuth callbacks
165
+ /^https?:\/\/.*\/callback/i,
166
+ // Callbacks
167
+ /^https?:\/\/.*\.supabase\.(co|com)/i,
168
+ // Supabase
169
+ /^https?:\/\/.*\.firebaseapp\.com/i,
170
+ // Firebase
171
+ /^https?:\/\/.*\.cloudfunctions\.net/i
172
+ // Cloud Functions
173
+ ];
174
+ var ASSET_PATTERNS = [
175
+ /\.(css|js|mjs|map|woff2?|ttf|eot|svg|png|jpe?g|gif|webp|avif|ico|pdf)(\?.*)?$/i
176
+ ];
177
+ var CONVERSION_PATTERNS = [
178
+ /^https?:\/\/(www\.)?calendly\.com/i,
179
+ /^https?:\/\/(checkout\.)?stripe\.com/i,
180
+ /^https?:\/\/buy\.stripe\.com/i,
181
+ /^https?:\/\/chromewebstore\.google\.com/i,
182
+ /^https?:\/\/meet\.sales-mind\.ai/i
183
+ ];
184
+ var PROTOCOL_EXEMPT = [
185
+ /^mailto:/i,
186
+ /^tel:/i,
187
+ /^#/,
188
+ /^javascript:/i
189
+ ];
190
+ function classifyUrl(url) {
191
+ if (PROTOCOL_EXEMPT.some((p) => p.test(url))) return "protocol";
192
+ if (ASSET_PATTERNS.some((p) => p.test(url))) return "asset";
193
+ if (SYSTEM_PATTERNS.some((p) => p.test(url))) return "system";
194
+ if (CONVERSION_PATTERNS.some((p) => p.test(url))) return "conversion";
195
+ if (INTERNAL_PATTERNS.some((p) => p.test(url))) return "internal";
196
+ return "external";
197
+ }
198
+
199
+ // src/components/OutboundLink/outbound-link-utils.ts
200
+ var LEGACY_UTM_SOURCE = "salesmind";
201
+ var EXEMPT_PATTERNS = [
202
+ /^https?:\/\/(www\.)?(stripe\.com|checkout\.stripe\.com|paypal\.com)/i,
203
+ /^https?:\/\/(www\.)?github\.com\/login\/oauth/i
204
+ ];
205
+ var isExemptUrl = (urlStr) => {
206
+ if (urlStr.startsWith("mailto:") || urlStr.startsWith("tel:")) return true;
207
+ const classification = classifyUrl(urlStr);
208
+ if (classification === "system" || classification === "protocol" || classification === "asset") {
209
+ return true;
210
+ }
211
+ return EXEMPT_PATTERNS.some((pattern) => pattern.test(urlStr));
212
+ };
213
+ var appendGovernedUTMs = (href, params, preserveExisting = true) => {
214
+ try {
215
+ const url = new URL(href);
216
+ if (preserveExisting) {
217
+ const hasAll = url.searchParams.has("utm_source") && url.searchParams.has("utm_medium") && url.searchParams.has("utm_campaign");
218
+ if (hasAll) return href;
219
+ }
220
+ return buildUtmUrl(href, params);
221
+ } catch {
222
+ return href;
223
+ }
224
+ };
225
+ var appendUTMs = (href, context, pageSlug, options) => {
226
+ try {
227
+ const url = new URL(href);
228
+ const { mediumOverride = "outbound_link", campaignOverride, preserveExisting = true } = options;
229
+ const utms = {
230
+ utm_source: LEGACY_UTM_SOURCE,
231
+ utm_medium: mediumOverride,
232
+ utm_campaign: campaignOverride || pageSlug,
233
+ utm_content: context
234
+ };
235
+ Object.entries(utms).forEach(([key, value]) => {
236
+ if (value) {
237
+ if (preserveExisting && url.searchParams.has(key)) {
238
+ return;
239
+ }
240
+ url.searchParams.set(key, value);
241
+ }
242
+ });
243
+ return url.toString();
244
+ } catch {
245
+ return href;
246
+ }
247
+ };
248
+ var OutboundLink = forwardRef(
249
+ ({
250
+ href,
251
+ context,
252
+ campaignOverride,
253
+ mediumOverride = "outbound_link",
254
+ preserveExistingUTM = true,
255
+ openInNewTab = true,
256
+ disableTracking = false,
257
+ utmParams,
258
+ onClick,
259
+ children,
260
+ ...props
261
+ }, ref) => {
262
+ const contextParams = useUtmDefaults();
263
+ const resolvedUtmParams = utmParams ?? contextParams;
264
+ let hostname = "";
265
+ try {
266
+ const url = new URL(href);
267
+ hostname = url.hostname;
268
+ } catch {
269
+ }
270
+ const [finalHref, setFinalHref] = useState(href);
271
+ useEffect(() => {
272
+ let isExternal = false;
273
+ let currentMedium = mediumOverride;
274
+ try {
275
+ const url = new URL(href);
276
+ const currentHost = window.location.hostname;
277
+ isExternal = url.hostname !== currentHost;
278
+ if (isExternal && currentHost.includes("sales-mind.ai") && url.hostname.includes("sales-mind.ai")) {
279
+ if (currentMedium === "outbound_link") {
280
+ currentMedium = "cross_subdomain";
281
+ }
282
+ }
283
+ } catch {
284
+ isExternal = false;
285
+ }
286
+ const isExempt = isExemptUrl(href) || disableTracking;
287
+ if (isExternal && !isExempt) {
288
+ if (resolvedUtmParams) {
289
+ setFinalHref(appendGovernedUTMs(href, resolvedUtmParams, preserveExistingUTM));
290
+ } else {
291
+ const pageSlug = window.location.pathname.replace(/^\/|\/$/g, "") || "home";
292
+ setFinalHref(appendUTMs(href, context, pageSlug, {
293
+ mediumOverride: currentMedium,
294
+ campaignOverride,
295
+ preserveExisting: preserveExistingUTM
296
+ }));
297
+ }
298
+ } else {
299
+ setFinalHref(href);
300
+ }
301
+ }, [href, context, mediumOverride, campaignOverride, preserveExistingUTM, disableTracking, resolvedUtmParams]);
302
+ const handleClick = (e) => {
303
+ if (typeof window === "undefined" || disableTracking) {
304
+ onClick?.(e);
305
+ return;
306
+ }
307
+ let clickExternal = false;
308
+ let clickCrossSubdomain = false;
309
+ let clickMedium = mediumOverride;
310
+ try {
311
+ const url = new URL(href);
312
+ const currentHost = window.location.hostname;
313
+ clickExternal = url.hostname !== currentHost;
314
+ if (clickExternal && currentHost.includes("sales-mind.ai") && url.hostname.includes("sales-mind.ai")) {
315
+ clickCrossSubdomain = true;
316
+ if (clickMedium === "outbound_link") {
317
+ clickMedium = "cross_subdomain";
318
+ }
319
+ }
320
+ } catch {
321
+ }
322
+ if (clickExternal) {
323
+ const detail = {
324
+ destination_domain: hostname,
325
+ destination_url: finalHref,
326
+ utm_medium_type: clickMedium,
327
+ page_slug: window.location.pathname,
328
+ component_location: context,
329
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
330
+ is_cross_subdomain: clickCrossSubdomain
331
+ };
332
+ const event = new CustomEvent("outbound_click", { detail });
333
+ window.dispatchEvent(event);
334
+ }
335
+ onClick?.(e);
336
+ };
337
+ const relParts = [];
338
+ let shouldOpenNewTab = openInNewTab;
339
+ try {
340
+ const url = new URL(href);
341
+ if (typeof window !== "undefined") {
342
+ const currentHost = window.location.hostname;
343
+ if (url.hostname !== currentHost && currentHost.includes("sales-mind.ai") && url.hostname.includes("sales-mind.ai")) {
344
+ shouldOpenNewTab = false;
345
+ }
346
+ }
347
+ } catch {
348
+ }
349
+ if (shouldOpenNewTab) relParts.push("noopener", "noreferrer");
350
+ if (mediumOverride === "citation") relParts.push("nofollow");
351
+ const rel = relParts.length > 0 ? relParts.join(" ") : void 0;
352
+ return (
353
+ // eslint-disable-next-line no-restricted-syntax
354
+ /* @__PURE__ */ jsx(
355
+ "a",
356
+ {
357
+ ref,
358
+ href: finalHref,
359
+ target: shouldOpenNewTab ? "_blank" : void 0,
360
+ rel,
361
+ onClick: handleClick,
362
+ ...props,
363
+ children
364
+ }
365
+ )
366
+ );
367
+ }
368
+ );
369
+ OutboundLink.displayName = "OutboundLink";
370
+ var COOKIE_CONSENT_EVENT = "cookie_consent_granted";
371
+ var COOKIE_CONSENT_KEY = "cookie_consent";
372
+ function CookieConsent({
373
+ delay = 1e3,
374
+ privacyUrl = "/legal/privacy",
375
+ onAccept,
376
+ onDecline,
377
+ labels,
378
+ className
379
+ }) {
380
+ const [state, setState] = useState("idle");
381
+ useEffect(() => {
382
+ const consent = localStorage.getItem(COOKIE_CONSENT_KEY);
383
+ if (!consent) {
384
+ const timer = setTimeout(() => setState("open"), delay);
385
+ return () => clearTimeout(timer);
386
+ } else {
387
+ setState("closed");
388
+ }
389
+ }, [delay]);
390
+ const handleDismiss = useCallback(() => {
391
+ setState("closing");
392
+ }, []);
393
+ const handleAccept = useCallback(() => {
394
+ localStorage.setItem(COOKIE_CONSENT_KEY, "granted");
395
+ handleDismiss();
396
+ if (typeof window !== "undefined") {
397
+ window.dispatchEvent(new Event(COOKIE_CONSENT_EVENT));
398
+ }
399
+ onAccept?.();
400
+ }, [onAccept, handleDismiss]);
401
+ const handleDecline = useCallback(() => {
402
+ localStorage.setItem(COOKIE_CONSENT_KEY, "denied");
403
+ handleDismiss();
404
+ onDecline?.();
405
+ }, [onDecline, handleDismiss]);
406
+ const handleTransitionEnd = useCallback(() => {
407
+ if (state === "closing") {
408
+ setState("closed");
409
+ }
410
+ }, [state]);
411
+ if (state === "idle" || state === "closed") return null;
412
+ return /* @__PURE__ */ jsx(
413
+ "div",
414
+ {
415
+ className: `ds-cookie-consent ${className ?? ""}`,
416
+ "data-state": state === "open" ? "open" : "closed",
417
+ onTransitionEnd: handleTransitionEnd,
418
+ role: "dialog",
419
+ "aria-label": labels?.title ?? "Cookie consent",
420
+ children: /* @__PURE__ */ jsxs("div", { className: "ds-cookie-consent__inner", children: [
421
+ /* @__PURE__ */ jsxs("div", { className: "ds-cookie-consent__content", children: [
422
+ /* @__PURE__ */ jsx("h3", { className: "ds-cookie-consent__title", children: labels?.title ?? "We use cookies" }),
423
+ /* @__PURE__ */ jsxs("p", { className: "ds-cookie-consent__description", children: [
424
+ labels?.description ?? "We use tracking cookies to understand how you use the product and help us improve it.",
425
+ /* @__PURE__ */ jsx(OutboundLink, { href: privacyUrl, context: "cookie-consent-privacy", className: "ds-cookie-consent__link", children: labels?.privacyLinkText ?? "Privacy Policy" })
426
+ ] })
427
+ ] }),
428
+ /* @__PURE__ */ jsxs("div", { className: "ds-cookie-consent__actions", children: [
429
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleDecline, children: labels?.declineLabel ?? "Decline" }),
430
+ /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: handleAccept, children: labels?.acceptLabel ?? "Accept" })
431
+ ] }),
432
+ /* @__PURE__ */ jsx(
433
+ "button",
434
+ {
435
+ onClick: handleDecline,
436
+ className: "ds-cookie-consent__close",
437
+ "aria-label": "Close",
438
+ children: /* @__PURE__ */ jsx(X, { size: 16 })
439
+ }
440
+ )
441
+ ] })
442
+ }
443
+ );
444
+ }
445
+
446
+ // src/web/analytics/use-cookie-consent.ts
447
+ function useCookieConsent() {
448
+ const [status, setStatus] = useState(() => {
449
+ if (typeof window === "undefined") return null;
450
+ const stored = localStorage.getItem(COOKIE_CONSENT_KEY);
451
+ if (stored === "granted") return "granted";
452
+ if (stored === "denied") return "denied";
453
+ return null;
454
+ });
455
+ useEffect(() => {
456
+ const handleConsent = () => setStatus("granted");
457
+ window.addEventListener(COOKIE_CONSENT_EVENT, handleConsent);
458
+ return () => window.removeEventListener(COOKIE_CONSENT_EVENT, handleConsent);
459
+ }, []);
460
+ return status;
461
+ }
462
+ var AnalyticsContext = createContext(null);
463
+ function AnalyticsProvider({ onTrack, debug = false, children }) {
464
+ const track = useCallback(
465
+ (event, props) => {
466
+ if (debug && typeof console !== "undefined") {
467
+ console.log("[DS Analytics]", event, props);
468
+ }
469
+ onTrack(event, props);
470
+ },
471
+ [onTrack, debug]
472
+ );
473
+ const value = useMemo(() => ({ track }), [track]);
474
+ return /* @__PURE__ */ jsx(AnalyticsContext.Provider, { value, children });
475
+ }
476
+ var NOOP_VALUE = {
477
+ track: () => {
478
+ }
479
+ };
480
+ function useAnalytics() {
481
+ return useContext(AnalyticsContext) ?? NOOP_VALUE;
482
+ }
483
+
484
+ export { AnalyticsProvider, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, CookieConsent, createAnalyticsLoader, loadClarity, loadGoogleAnalytics, useAnalytics, useCookieConsent };
485
+ //# sourceMappingURL=out.js.map
486
+ //# sourceMappingURL=index.js.map