@salesmind-ai/design-system 0.1.7 → 0.1.9

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.
@@ -115,9 +115,9 @@
115
115
  --text-secondary: rgba(255, 255, 255, 0.8);
116
116
  --text-tertiary: rgba(255, 255, 255, 0.6);
117
117
  --text-disabled: rgba(255, 255, 255, 0.3);
118
- --glass-base: rgba(255, 255, 255, 0.01);
119
- --glass-base-hover: rgba(255, 255, 255, 0.03);
120
- --glass-base-active: rgba(255, 255, 255, 0.05);
118
+ --glass-base: rgba(255, 255, 255, 0.06);
119
+ --glass-base-hover: rgba(255, 255, 255, 0.09);
120
+ --glass-base-active: rgba(255, 255, 255, 0.12);
121
121
  --glass-blur: 40px;
122
122
  --glass-saturate: 160%;
123
123
  --glass-brightness: 90%;
@@ -2984,11 +2984,14 @@ a:hover {
2984
2984
  flex-shrink: 0;
2985
2985
  margin-left: var(--space-3);
2986
2986
  }
2987
- @media (max-width: 1279px) {
2987
+ @media (max-width: 1399px) {
2988
2988
  .ds-locale-dropdown__trigger {
2989
2989
  padding: var(--space-1-5) var(--space-2);
2990
2990
  gap: var(--space-1);
2991
2991
  }
2992
+ .ds-locale-dropdown__trigger-flag {
2993
+ display: none;
2994
+ }
2992
2995
  .ds-locale-dropdown__trigger-chevron {
2993
2996
  display: none;
2994
2997
  }
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  'use strict';
2
3
 
3
4
  var jsxRuntime = require('react/jsx-runtime');
@@ -656,23 +657,146 @@ var Button = React__default.default.forwardRef(
656
657
  }
657
658
  );
658
659
  Button.displayName = "Button";
660
+ var UtmContext = React.createContext(null);
661
+
662
+ // src/web/utm/useUtmDefaults.ts
663
+ function useUtmDefaults() {
664
+ return React.useContext(UtmContext);
665
+ }
666
+
667
+ // src/web/utm/builders.ts
668
+ var PLACEHOLDER_ORIGIN = "https://__placeholder__.internal";
669
+ function buildUtmUrl(baseUrl, params) {
670
+ const isRelative = baseUrl.startsWith("/");
671
+ let url;
672
+ try {
673
+ url = isRelative ? new URL(baseUrl, PLACEHOLDER_ORIGIN) : new URL(baseUrl);
674
+ } catch {
675
+ return baseUrl;
676
+ }
677
+ const existingParams = [];
678
+ url.searchParams.forEach((value, key) => {
679
+ existingParams.push([key, value]);
680
+ });
681
+ for (const [key] of existingParams) {
682
+ url.searchParams.delete(key);
683
+ }
684
+ for (const [key, value] of existingParams) {
685
+ if (!key.startsWith("utm_")) {
686
+ url.searchParams.set(key, value);
687
+ }
688
+ }
689
+ url.searchParams.set("utm_source", params.source);
690
+ url.searchParams.set("utm_medium", params.medium);
691
+ url.searchParams.set("utm_campaign", params.campaign);
692
+ if (params.term !== void 0) {
693
+ url.searchParams.set("utm_term", params.term);
694
+ }
695
+ if (params.content !== void 0) {
696
+ url.searchParams.set("utm_content", params.content);
697
+ }
698
+ if (isRelative) {
699
+ return url.href.replace(PLACEHOLDER_ORIGIN, "");
700
+ }
701
+ return url.href;
702
+ }
703
+
704
+ // src/web/utm/classifiers.ts
705
+ var INTERNAL_PATTERNS = [
706
+ /^\/(?!\/)/,
707
+ // Relative paths
708
+ /^https?:\/\/(www\.)?sales-mind\.ai/i,
709
+ // Marketing site
710
+ /^https?:\/\/app\.sales-mind\.ai/i,
711
+ // Web app
712
+ /^https?:\/\/apps\.sales-mind\.ai/i,
713
+ // Web app (legacy)
714
+ /^https?:\/\/meet\.sales-mind\.ai/i,
715
+ // Booking
716
+ /^https?:\/\/docs\.sales-mind\.ai/i
717
+ // Docs
718
+ ];
719
+ var SYSTEM_PATTERNS = [
720
+ /^https?:\/\/.*\/api\//i,
721
+ // API endpoints
722
+ /^https?:\/\/.*\/webhook/i,
723
+ // Webhooks
724
+ /^https?:\/\/.*\/oauth/i,
725
+ // OAuth callbacks
726
+ /^https?:\/\/.*\/callback/i,
727
+ // Callbacks
728
+ /^https?:\/\/.*\.supabase\.(co|com)/i,
729
+ // Supabase
730
+ /^https?:\/\/.*\.firebaseapp\.com/i,
731
+ // Firebase
732
+ /^https?:\/\/.*\.cloudfunctions\.net/i
733
+ // Cloud Functions
734
+ ];
735
+ var ASSET_PATTERNS = [
736
+ /\.(css|js|mjs|map|woff2?|ttf|eot|svg|png|jpe?g|gif|webp|avif|ico|pdf)(\?.*)?$/i
737
+ ];
738
+ var CONVERSION_PATTERNS = [
739
+ /^https?:\/\/(www\.)?calendly\.com/i,
740
+ /^https?:\/\/(checkout\.)?stripe\.com/i,
741
+ /^https?:\/\/buy\.stripe\.com/i,
742
+ /^https?:\/\/chromewebstore\.google\.com/i,
743
+ /^https?:\/\/meet\.sales-mind\.ai/i
744
+ ];
745
+ var PROTOCOL_EXEMPT = [
746
+ /^mailto:/i,
747
+ /^tel:/i,
748
+ /^#/,
749
+ /^javascript:/i
750
+ ];
751
+ function classifyUrl(url) {
752
+ if (PROTOCOL_EXEMPT.some((p) => p.test(url))) return "protocol";
753
+ if (ASSET_PATTERNS.some((p) => p.test(url))) return "asset";
754
+ if (SYSTEM_PATTERNS.some((p) => p.test(url))) return "system";
755
+ if (CONVERSION_PATTERNS.some((p) => p.test(url))) return "conversion";
756
+ if (INTERNAL_PATTERNS.some((p) => p.test(url))) return "internal";
757
+ return "external";
758
+ }
759
+ function requiresUtm(url) {
760
+ const classification = classifyUrl(url);
761
+ return classification === "external" || classification === "conversion";
762
+ }
763
+ function isUtmExempt(url) {
764
+ const classification = classifyUrl(url);
765
+ return classification === "protocol" || classification === "asset" || classification === "system" || classification === "internal";
766
+ }
659
767
 
660
768
  // src/components/OutboundLink/outbound-link-utils.ts
661
- var CONSTANT_UTM_SOURCE = "salesmind";
769
+ var LEGACY_UTM_SOURCE = "salesmind";
662
770
  var EXEMPT_PATTERNS = [
663
771
  /^https?:\/\/(www\.)?(stripe\.com|checkout\.stripe\.com|paypal\.com)/i,
664
772
  /^https?:\/\/(www\.)?github\.com\/login\/oauth/i
665
773
  ];
666
774
  var isExemptUrl = (urlStr) => {
667
775
  if (urlStr.startsWith("mailto:") || urlStr.startsWith("tel:")) return true;
776
+ const classification = classifyUrl(urlStr);
777
+ if (classification === "system" || classification === "protocol" || classification === "asset") {
778
+ return true;
779
+ }
668
780
  return EXEMPT_PATTERNS.some((pattern) => pattern.test(urlStr));
669
781
  };
782
+ var appendGovernedUTMs = (href, params, preserveExisting = true) => {
783
+ try {
784
+ const url = new URL(href);
785
+ if (preserveExisting) {
786
+ const hasAll = url.searchParams.has("utm_source") && url.searchParams.has("utm_medium") && url.searchParams.has("utm_campaign");
787
+ if (hasAll) return href;
788
+ }
789
+ return buildUtmUrl(href, params);
790
+ } catch {
791
+ return href;
792
+ }
793
+ };
670
794
  var appendUTMs = (href, context, pageSlug, options) => {
671
795
  try {
672
796
  const url = new URL(href);
673
797
  const { mediumOverride = "outbound_link", campaignOverride, preserveExisting = true } = options;
674
798
  const utms = {
675
- utm_source: CONSTANT_UTM_SOURCE,
799
+ utm_source: LEGACY_UTM_SOURCE,
676
800
  utm_medium: mediumOverride,
677
801
  utm_campaign: campaignOverride || pageSlug,
678
802
  utm_content: context
@@ -699,10 +823,13 @@ var OutboundLink = React.forwardRef(
699
823
  preserveExistingUTM = true,
700
824
  openInNewTab = true,
701
825
  disableTracking = false,
826
+ utmParams,
702
827
  onClick,
703
828
  children,
704
829
  ...props
705
830
  }, ref) => {
831
+ const contextParams = useUtmDefaults();
832
+ const resolvedUtmParams = utmParams ?? contextParams;
706
833
  let hostname = "";
707
834
  try {
708
835
  const url = new URL(href);
@@ -727,16 +854,20 @@ var OutboundLink = React.forwardRef(
727
854
  }
728
855
  const isExempt = isExemptUrl(href) || disableTracking;
729
856
  if (isExternal && !isExempt) {
730
- const pageSlug = window.location.pathname.replace(/^\/|\/$/g, "") || "home";
731
- setFinalHref(appendUTMs(href, context, pageSlug, {
732
- mediumOverride: currentMedium,
733
- campaignOverride,
734
- preserveExisting: preserveExistingUTM
735
- }));
857
+ if (resolvedUtmParams) {
858
+ setFinalHref(appendGovernedUTMs(href, resolvedUtmParams, preserveExistingUTM));
859
+ } else {
860
+ const pageSlug = window.location.pathname.replace(/^\/|\/$/g, "") || "home";
861
+ setFinalHref(appendUTMs(href, context, pageSlug, {
862
+ mediumOverride: currentMedium,
863
+ campaignOverride,
864
+ preserveExisting: preserveExistingUTM
865
+ }));
866
+ }
736
867
  } else {
737
868
  setFinalHref(href);
738
869
  }
739
- }, [href, context, mediumOverride, campaignOverride, preserveExistingUTM, disableTracking]);
870
+ }, [href, context, mediumOverride, campaignOverride, preserveExistingUTM, disableTracking, resolvedUtmParams]);
740
871
  const handleClick = (e) => {
741
872
  if (typeof window === "undefined" || disableTracking) {
742
873
  onClick?.(e);
@@ -909,20 +1040,306 @@ function useAnalytics() {
909
1040
  return React.useContext(AnalyticsContext) ?? NOOP_VALUE;
910
1041
  }
911
1042
 
1043
+ // src/web/utm/constants.ts
1044
+ var UTM_SOURCES = [
1045
+ "linkedin",
1046
+ "whatsapp",
1047
+ "intercom",
1048
+ "email",
1049
+ "website",
1050
+ "app",
1051
+ "chromeStore",
1052
+ "stripe",
1053
+ "direct"
1054
+ ];
1055
+ var UTM_MEDIUMS_MESSAGING = [
1056
+ "dm",
1057
+ "group",
1058
+ "email",
1059
+ "inAppChat",
1060
+ "organicPost",
1061
+ "paidAd",
1062
+ "qrCode",
1063
+ "redirect",
1064
+ "storeListing"
1065
+ ];
1066
+ var UTM_MEDIUMS_APP = [
1067
+ "appHome",
1068
+ "appDashboard",
1069
+ "appInbox",
1070
+ "appContacts",
1071
+ "appCampaigns",
1072
+ "appSettings",
1073
+ "appBilling",
1074
+ "appCheckout",
1075
+ "appOnboarding",
1076
+ "appSupport",
1077
+ "appCalendar"
1078
+ ];
1079
+ var UTM_MEDIUMS_WEB = [
1080
+ "webHome",
1081
+ "webDemo",
1082
+ "webPricing",
1083
+ "webFeatures",
1084
+ "webUseCase",
1085
+ "webSolution",
1086
+ "webIndustry",
1087
+ "webIntegrations",
1088
+ "webBlog",
1089
+ "webBlogPost",
1090
+ "webLanding",
1091
+ "webComparison",
1092
+ "webCaseStudy",
1093
+ "webAbout",
1094
+ "webContact",
1095
+ "webCareers",
1096
+ "webLegal",
1097
+ "webDocs",
1098
+ "webAffiliate"
1099
+ ];
1100
+ var UTM_MEDIUMS_ALL = [
1101
+ ...UTM_MEDIUMS_MESSAGING,
1102
+ ...UTM_MEDIUMS_APP,
1103
+ ...UTM_MEDIUMS_WEB
1104
+ ];
1105
+ var UTM_CAMPAIGNS = [
1106
+ "discoveryCall",
1107
+ "demo",
1108
+ "trial",
1109
+ "onboarding",
1110
+ "upgrade",
1111
+ "renewal",
1112
+ "retention",
1113
+ "accountSupport",
1114
+ "supportCall",
1115
+ "partnerCall",
1116
+ "hiring",
1117
+ "interviewCall"
1118
+ ];
1119
+ var UTM_TERMS = [
1120
+ "julienGadea",
1121
+ "bramSmith",
1122
+ "florentDupont",
1123
+ "sawLin",
1124
+ "evaSupport",
1125
+ "team",
1126
+ "auto"
1127
+ ];
1128
+ var UTM_CONTENTS = [
1129
+ "ctaPrimary",
1130
+ "ctaSecondary",
1131
+ "ctaHeader",
1132
+ "ctaFooter",
1133
+ "ctaInline",
1134
+ "buttonPrimary",
1135
+ "buttonSecondary",
1136
+ "banner",
1137
+ "popup",
1138
+ "variantA",
1139
+ "variantB",
1140
+ "variantC"
1141
+ ];
1142
+ var UTM_SOURCES_REQUIRING_SELLER = [
1143
+ "linkedin",
1144
+ "whatsapp",
1145
+ "intercom",
1146
+ "email"
1147
+ ];
1148
+
1149
+ // src/web/utm/validators.ts
1150
+ function validateUtmField(field, value) {
1151
+ switch (field) {
1152
+ case "source":
1153
+ return UTM_SOURCES.includes(value);
1154
+ case "medium":
1155
+ return UTM_MEDIUMS_ALL.includes(value);
1156
+ case "campaign":
1157
+ return UTM_CAMPAIGNS.includes(value);
1158
+ case "term":
1159
+ return UTM_TERMS.includes(value);
1160
+ case "content":
1161
+ return UTM_CONTENTS.includes(value);
1162
+ default:
1163
+ return false;
1164
+ }
1165
+ }
1166
+ function isValidUtmParams(params) {
1167
+ if (!params.source || !params.medium || !params.campaign) {
1168
+ return false;
1169
+ }
1170
+ if (!validateUtmField("source", params.source)) return false;
1171
+ if (!validateUtmField("medium", params.medium)) return false;
1172
+ if (!validateUtmField("campaign", params.campaign)) return false;
1173
+ if (params.term !== void 0 && !validateUtmField("term", params.term)) return false;
1174
+ if (params.content !== void 0 && !validateUtmField("content", params.content)) return false;
1175
+ return true;
1176
+ }
1177
+ function validateCompliance(url, params) {
1178
+ const errors = [];
1179
+ const classification = classifyUrl(url);
1180
+ const needsUtm = requiresUtm(url);
1181
+ if (!needsUtm) {
1182
+ return {
1183
+ status: "compliant",
1184
+ url,
1185
+ params: params ?? null,
1186
+ errors: []
1187
+ };
1188
+ }
1189
+ if (!params) {
1190
+ return {
1191
+ status: "blocked",
1192
+ url,
1193
+ params: null,
1194
+ errors: [`URL classified as '${classification}' requires UTM parameters but none provided`]
1195
+ };
1196
+ }
1197
+ if (!params.source) errors.push("Missing required field: utm_source");
1198
+ if (!params.medium) errors.push("Missing required field: utm_medium");
1199
+ if (!params.campaign) errors.push("Missing required field: utm_campaign");
1200
+ if (params.source && !validateUtmField("source", params.source)) {
1201
+ errors.push(`Invalid utm_source: '${params.source}'. Must be one of: ${UTM_SOURCES.join(", ")}`);
1202
+ }
1203
+ if (params.medium && !validateUtmField("medium", params.medium)) {
1204
+ errors.push(`Invalid utm_medium: '${params.medium}'. Must be one of governed enum values`);
1205
+ }
1206
+ if (params.campaign && !validateUtmField("campaign", params.campaign)) {
1207
+ errors.push(`Invalid utm_campaign: '${params.campaign}'. Must be one of: ${UTM_CAMPAIGNS.join(", ")}`);
1208
+ }
1209
+ if (params.term !== void 0 && params.term && !validateUtmField("term", params.term)) {
1210
+ errors.push(`Invalid utm_term: '${params.term}'. Must be one of: ${UTM_TERMS.join(", ")}`);
1211
+ }
1212
+ if (params.content !== void 0 && params.content && !validateUtmField("content", params.content)) {
1213
+ errors.push(`Invalid utm_content: '${params.content}'. Must be one of: ${UTM_CONTENTS.join(", ")}`);
1214
+ }
1215
+ const allValues = [];
1216
+ if (params.source) allValues.push(params.source);
1217
+ if (params.medium) allValues.push(params.medium);
1218
+ if (params.campaign) allValues.push(params.campaign);
1219
+ if (params.term) allValues.push(params.term);
1220
+ if (params.content) allValues.push(params.content);
1221
+ for (const value of allValues) {
1222
+ if (/\s/.test(value)) errors.push(`Value '${value}' contains spaces`);
1223
+ if (/_/.test(value)) errors.push(`Value '${value}' contains underscores`);
1224
+ if (/-/.test(value)) errors.push(`Value '${value}' contains hyphens`);
1225
+ if (/[^\x20-\x7E]/.test(value)) errors.push(`Value '${value}' contains non-ASCII characters`);
1226
+ if (value === value.toUpperCase() && value.length > 1) errors.push(`Value '${value}' is ALL CAPS`);
1227
+ }
1228
+ const status = errors.length === 0 ? "compliant" : "blocked";
1229
+ return { status, url, params, errors };
1230
+ }
1231
+ function classifyAndEnforce(url, params) {
1232
+ return validateCompliance(url, params);
1233
+ }
1234
+ function buildBlockedError(reason, url) {
1235
+ return {
1236
+ status: "UTM_COMPLIANCE_BLOCKED",
1237
+ reason,
1238
+ requiredFix: reason,
1239
+ correctedExample: url ?? null
1240
+ };
1241
+ }
1242
+
1243
+ // src/web/utm/attribution.ts
1244
+ function parseUtmParams(search) {
1245
+ if (!search) return {};
1246
+ const params = new URLSearchParams(search);
1247
+ const result = {};
1248
+ const source = params.get("utm_source");
1249
+ if (source && UTM_SOURCES.includes(source)) {
1250
+ result.source = source;
1251
+ }
1252
+ const medium = params.get("utm_medium");
1253
+ if (medium) {
1254
+ result.medium = medium;
1255
+ }
1256
+ const campaign = params.get("utm_campaign");
1257
+ if (campaign && UTM_CAMPAIGNS.includes(campaign)) {
1258
+ result.campaign = campaign;
1259
+ }
1260
+ const term = params.get("utm_term");
1261
+ if (term && UTM_TERMS.includes(term)) {
1262
+ result.term = term;
1263
+ }
1264
+ const content = params.get("utm_content");
1265
+ if (content && UTM_CONTENTS.includes(content)) {
1266
+ result.content = content;
1267
+ }
1268
+ return result;
1269
+ }
1270
+ function toFirstTouchAttribution(params) {
1271
+ return {
1272
+ firstTouchSource: params.source,
1273
+ firstTouchMedium: params.medium,
1274
+ firstTouchCampaign: params.campaign,
1275
+ firstTouchSeller: params.term ?? null
1276
+ };
1277
+ }
1278
+ function requiresSellerAttribution(source) {
1279
+ return UTM_SOURCES_REQUIRING_SELLER.includes(source);
1280
+ }
1281
+
1282
+ // src/web/utm/audit.ts
1283
+ function createAuditEntry(url, params, generatorContext, confidence) {
1284
+ const utmParts = [];
1285
+ if (params) {
1286
+ if (params.source) utmParts.push(`utm_source=${params.source}`);
1287
+ if (params.medium) utmParts.push(`utm_medium=${params.medium}`);
1288
+ if (params.campaign) utmParts.push(`utm_campaign=${params.campaign}`);
1289
+ if (params.term) utmParts.push(`utm_term=${params.term}`);
1290
+ if (params.content) utmParts.push(`utm_content=${params.content}`);
1291
+ }
1292
+ return {
1293
+ url,
1294
+ utmString: utmParts.join("&"),
1295
+ generatorContext,
1296
+ sellerAttribution: params?.term ?? null,
1297
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1298
+ confidence
1299
+ };
1300
+ }
1301
+ function UtmProvider({ params, children }) {
1302
+ return /* @__PURE__ */ jsxRuntime.jsx(UtmContext.Provider, { value: params, children });
1303
+ }
1304
+
912
1305
  exports.AnalyticsProvider = AnalyticsProvider;
913
1306
  exports.COOKIE_CONSENT_EVENT = COOKIE_CONSENT_EVENT;
914
1307
  exports.COOKIE_CONSENT_KEY = COOKIE_CONSENT_KEY;
915
1308
  exports.CookieConsent = CookieConsent;
916
1309
  exports.JsonLd = JsonLd;
1310
+ exports.UTM_CAMPAIGNS = UTM_CAMPAIGNS;
1311
+ exports.UTM_CONTENTS = UTM_CONTENTS;
1312
+ exports.UTM_MEDIUMS_ALL = UTM_MEDIUMS_ALL;
1313
+ exports.UTM_MEDIUMS_APP = UTM_MEDIUMS_APP;
1314
+ exports.UTM_MEDIUMS_MESSAGING = UTM_MEDIUMS_MESSAGING;
1315
+ exports.UTM_MEDIUMS_WEB = UTM_MEDIUMS_WEB;
1316
+ exports.UTM_SOURCES = UTM_SOURCES;
1317
+ exports.UTM_SOURCES_REQUIRING_SELLER = UTM_SOURCES_REQUIRING_SELLER;
1318
+ exports.UTM_TERMS = UTM_TERMS;
1319
+ exports.UtmProvider = UtmProvider;
917
1320
  exports.aggregateRatingFromTestimonials = aggregateRatingFromTestimonials;
1321
+ exports.buildBlockedError = buildBlockedError;
918
1322
  exports.buildPageGraph = buildPageGraph;
1323
+ exports.buildUtmUrl = buildUtmUrl;
919
1324
  exports.canonicalUrl = canonicalUrl;
1325
+ exports.classifyAndEnforce = classifyAndEnforce;
1326
+ exports.classifyUrl = classifyUrl;
920
1327
  exports.createAnalyticsLoader = createAnalyticsLoader;
1328
+ exports.createAuditEntry = createAuditEntry;
921
1329
  exports.createEntityIds = createEntityIds;
922
1330
  exports.createSchemaGenerators = createSchemaGenerators;
1331
+ exports.isUtmExempt = isUtmExempt;
1332
+ exports.isValidUtmParams = isValidUtmParams;
923
1333
  exports.loadClarity = loadClarity;
924
1334
  exports.loadGoogleAnalytics = loadGoogleAnalytics;
1335
+ exports.parseUtmParams = parseUtmParams;
1336
+ exports.requiresSellerAttribution = requiresSellerAttribution;
1337
+ exports.requiresUtm = requiresUtm;
1338
+ exports.toFirstTouchAttribution = toFirstTouchAttribution;
925
1339
  exports.useAnalytics = useAnalytics;
926
1340
  exports.useCookieConsent = useCookieConsent;
927
- //# sourceMappingURL=index.cjs.map
1341
+ exports.useUtmDefaults = useUtmDefaults;
1342
+ exports.validateCompliance = validateCompliance;
1343
+ exports.validateUtmField = validateUtmField;
1344
+ //# sourceMappingURL=out.js.map
928
1345
  //# sourceMappingURL=index.cjs.map