kenobi-js 0.1.42 → 0.1.44

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/browser/dist.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Kenobi SDK v0.1.41
2
+ * Kenobi SDK v0.1.42
3
3
  * (c) 2026 Kenobi.ai
4
4
  */
5
5
  "use strict";
@@ -1129,6 +1129,200 @@ var KenobiLib = (() => {
1129
1129
  )
1130
1130
  }
1131
1131
  ), "ArrowPathIcon");
1132
+ var IDENTITY_RESOLVED_EVENT = "kenobi:identity-resolved";
1133
+ var KENOBI_SNITCHER_SCRIPT_ID = "kenobi-snitcher";
1134
+ var DEFAULT_DEANON_TIMEOUT_MS = 1600;
1135
+ var dispatchCueCardEvent = /* @__PURE__ */ __name((eventName, detail) => {
1136
+ if (typeof window === "undefined") return;
1137
+ window.dispatchEvent(
1138
+ new CustomEvent(`kenobi:${eventName}`, {
1139
+ detail,
1140
+ bubbles: true
1141
+ })
1142
+ );
1143
+ }, "dispatchCueCardEvent");
1144
+ var resolveDeanonymizedCompany = /* @__PURE__ */ __name((detail) => {
1145
+ if (!detail || detail.type !== "business") return null;
1146
+ const company = detail.company;
1147
+ const name = company?.name || detail.domain || void 0;
1148
+ const domain = company?.domain || detail.domain || void 0;
1149
+ if (!name) return null;
1150
+ return {
1151
+ name,
1152
+ domain,
1153
+ logoUrl: company?.logoUrl || (domain ? `https://logo.clearbit.com/${domain}` : void 0)
1154
+ };
1155
+ }, "resolveDeanonymizedCompany");
1156
+ var normalizeDomain = /* @__PURE__ */ __name((value) => {
1157
+ if (!value) return "";
1158
+ return value.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").replace(/:\d+$/, "").replace(/^www\./i, "").trim().toLowerCase();
1159
+ }, "normalizeDomain");
1160
+ var ensureKenobiSnitcherLoaded = /* @__PURE__ */ __name((config) => {
1161
+ if (typeof window === "undefined" || typeof document === "undefined") return;
1162
+ if (window.__kenobiSnitcherLoaded || window.__kenobiSnitcherLoading) return;
1163
+ if (window.Snitcher || window.SpotterSettings) {
1164
+ window.__kenobiSnitcherLoaded = true;
1165
+ return;
1166
+ }
1167
+ if (document.getElementById(KENOBI_SNITCHER_SCRIPT_ID)) {
1168
+ window.__kenobiSnitcherLoaded = true;
1169
+ return;
1170
+ }
1171
+ const snitcher = config?.snitcher;
1172
+ if (!snitcher?.spotterToken || !snitcher.profileId) {
1173
+ return;
1174
+ }
1175
+ window.__kenobiSnitcherLoading = true;
1176
+ const resolvedLogoTemplate = config?.logoUrlTemplate || "https://logo.clearbit.com/{domain}";
1177
+ const apiEndpoint = snitcher.apiEndpoint || `${window.location.origin}/api/snitcher-proxy/radar`;
1178
+ const cdn = snitcher.cdn || `${window.location.origin}/api/snitcher-proxy/cdn`;
1179
+ const namespace = snitcher.namespace || "Snitcher";
1180
+ const snitcherScript = `
1181
+ (function () {
1182
+ function spotterCb(response) {
1183
+ var rawDomain = (response.company && response.company.domain) || response.domain || "";
1184
+ var normalizedDomain = rawDomain
1185
+ .replace(/^https?:\\/\\//i, "")
1186
+ .replace(/\\/.*$/, "")
1187
+ .replace(/:\\d+$/, "")
1188
+ .replace(/^www\\./i, "");
1189
+ var identity = {
1190
+ provider: "snitcher",
1191
+ type: response.type === "business" ? "business" : "unknown",
1192
+ domain: response.domain || null,
1193
+ ip: response.ip,
1194
+ company: response.company
1195
+ ? {
1196
+ id: response.company.id,
1197
+ name: response.company.name || response.domain,
1198
+ domain: response.company.domain || response.domain,
1199
+ logoUrl: "${resolvedLogoTemplate}".replace("{domain}", normalizedDomain),
1200
+ industry: response.company.industry,
1201
+ location: response.company.location,
1202
+ employeeRange: response.company.employee_range,
1203
+ foundedYear: response.company.founded_year,
1204
+ description: response.company.description,
1205
+ geo: response.company.geo
1206
+ ? {
1207
+ city: response.company.geo.city,
1208
+ country: response.company.geo.country,
1209
+ countryCode: response.company.geo.country_code,
1210
+ region: response.company.geo.region,
1211
+ postalCode: response.company.geo.postal_code,
1212
+ address:
1213
+ response.company.geo.full_address ||
1214
+ response.company.geo.address,
1215
+ }
1216
+ : undefined,
1217
+ profiles: response.company.profiles
1218
+ ? {
1219
+ linkedin: response.company.profiles.linkedin?.url,
1220
+ twitter: response.company.profiles.twitter?.url,
1221
+ facebook: response.company.profiles.facebook?.url,
1222
+ crunchbase: response.company.profiles.crunchbase?.url,
1223
+ }
1224
+ : undefined,
1225
+ }
1226
+ : undefined,
1227
+ raw: response,
1228
+ };
1229
+
1230
+ window.__kenobiIdentityResolution = identity;
1231
+ window.dispatchEvent(
1232
+ new CustomEvent("${IDENTITY_RESOLVED_EVENT}", {
1233
+ detail: identity,
1234
+ bubbles: true,
1235
+ })
1236
+ );
1237
+ }
1238
+
1239
+ window.SpotterSettings = {
1240
+ token: "${snitcher.spotterToken}",
1241
+ callback: spotterCb,
1242
+ };
1243
+
1244
+ !(function (e) {
1245
+ "use strict";
1246
+ var t = e && e.namespace;
1247
+ if (t && e.profileId && e.cdn) {
1248
+ var i = window[t];
1249
+ if (((i && Array.isArray(i)) || (i = window[t] = []), !i.initialized && !i._loaded))
1250
+ if (i._loaded) console && console.warn("[Radar] Duplicate initialization attempted");
1251
+ else {
1252
+ i._loaded = !0;
1253
+ [
1254
+ "track",
1255
+ "page",
1256
+ "identify",
1257
+ "group",
1258
+ "alias",
1259
+ "ready",
1260
+ "debug",
1261
+ "on",
1262
+ "off",
1263
+ "once",
1264
+ "trackClick",
1265
+ "trackSubmit",
1266
+ "trackLink",
1267
+ "trackForm",
1268
+ "pageview",
1269
+ "screen",
1270
+ "reset",
1271
+ "register",
1272
+ "setAnonymousId",
1273
+ "addSourceMiddleware",
1274
+ "addIntegrationMiddleware",
1275
+ "addDestinationMiddleware",
1276
+ "giveCookieConsent",
1277
+ ].forEach(function (e) {
1278
+ var a;
1279
+ i[e] =
1280
+ ((a = e),
1281
+ function () {
1282
+ var e = window[t];
1283
+ if (e.initialized) return e[a].apply(e, arguments);
1284
+ var i = [].slice.call(arguments);
1285
+ return i.unshift(a), e.push(i), e;
1286
+ });
1287
+ }),
1288
+ -1 === e.apiEndpoint.indexOf("http") &&
1289
+ (e.apiEndpoint = "https://" + e.apiEndpoint),
1290
+ (i.bootstrap = function () {
1291
+ var t,
1292
+ i = document.createElement("script");
1293
+ (i.async = !0),
1294
+ (i.type = "text/javascript"),
1295
+ (i.id = "__radar__"),
1296
+ i.setAttribute("data-settings", JSON.stringify(e)),
1297
+ (i.src = [
1298
+ -1 !== (t = e.cdn).indexOf("http") ? "" : "https://",
1299
+ t,
1300
+ "/releases/latest/radar.min.js",
1301
+ ].join(""));
1302
+ var a = document.scripts[0];
1303
+ a.parentNode.insertBefore(i, a);
1304
+ }),
1305
+ i.bootstrap();
1306
+ }
1307
+ } else
1308
+ "undefined" != typeof console &&
1309
+ console.error("[Radar] Configuration incomplete");
1310
+ })({
1311
+ apiEndpoint: "${apiEndpoint}",
1312
+ cdn: "${cdn}",
1313
+ namespace: "${namespace}",
1314
+ profileId: "${snitcher.profileId}",
1315
+ });
1316
+ })();
1317
+ `;
1318
+ const script = document.createElement("script");
1319
+ script.id = KENOBI_SNITCHER_SCRIPT_ID;
1320
+ script.type = "text/javascript";
1321
+ script.text = snitcherScript;
1322
+ document.head?.appendChild(script);
1323
+ window.__kenobiSnitcherLoaded = true;
1324
+ window.__kenobiSnitcherLoading = false;
1325
+ }, "ensureKenobiSnitcherLoaded");
1132
1326
  var STYLES = `
1133
1327
  :host { --kb-font-family: system-ui, -apple-system, sans-serif; --kb-bg-container: rgba(255, 255, 255, 0.05); --kb-border-container: rgba(255, 255, 255, 0.2); --kb-shadow-container: 0 25px 50px -12px rgba(0, 0, 0, 0.25); --kb-backdrop-blur: 16px; --kb-text-title: #ffffff; --kb-text-subtitle: rgba(255, 255, 255, 0.7); --kb-btn-dismiss-bg: rgba(0, 0, 0, 0.7); --kb-btn-dismiss-border: rgba(255, 255, 255, 0.2); --kb-btn-dismiss-text: rgba(255, 255, 255, 0.8); --kb-btn-dismiss-hover-bg: rgba(0, 0, 0, 0.9); --kb-btn-dismiss-hover-text: #ffffff; --kb-btn-trigger-bg: #ffffff; --kb-btn-trigger-text: #000000; --kb-btn-trigger-hover-bg: rgba(255, 255, 255, 0.9); --kb-progress-track: rgba(255, 255, 255, 0.2); --kb-progress-indicator: #ffffff; --kb-popover-bg: rgba(255, 255, 255, 0.05); --kb-popover-border: rgba(255, 255, 255, 0.2); --kb-popover-text: #ffffff; --kb-input-bg: transparent; --kb-input-border: rgba(255, 255, 255, 0.3); --kb-input-text: #ffffff; --kb-input-placeholder: rgba(255, 255, 255, 0.6); --kb-form-label: #ffffff; --kb-success-color: #6ee7b7; --kb-error-text: #ef4444; --kb-focus-blur: 10px; --kb-kbd-bg: rgba(255, 255, 255, 0.15); --kb-kbd-border: rgba(255, 255, 255, 0.4); --kb-kbd-text: #ffffff; --kb-watermark-text: rgba(255, 255, 255, 0.4); font-family: var(--kb-font-family); }
1134
1328
  .theme-light { --kb-bg-container: #ffffff; --kb-border-container: #e2e8f0; --kb-shadow-container: 0 20px 25px -5px rgba(0, 0, 0, 0.1); --kb-backdrop-blur: 0px; --kb-text-title: #0f172a; --kb-text-subtitle: #475569; --kb-btn-dismiss-bg: #ffffff; --kb-btn-dismiss-border: #e2e8f0; --kb-btn-dismiss-text: #64748b; --kb-btn-dismiss-hover-bg: #f1f5f9; --kb-btn-dismiss-hover-text: #0f172a; --kb-btn-trigger-bg: #0f172a; --kb-btn-trigger-text: #ffffff; --kb-btn-trigger-hover-bg: #1e293b; --kb-progress-track: #e2e8f0; --kb-progress-indicator: #0f172a; --kb-popover-bg: #ffffff; --kb-popover-border: #e2e8f0; --kb-popover-text: #0f172a; --kb-input-bg: #ffffff; --kb-input-border: #cbd5e1; --kb-input-text: #0f172a; --kb-input-placeholder: #94a3b8; --kb-form-label: #334155; --kb-success-color: #059669; --kb-error-text: #ef4444; --kb-kbd-bg: #f1f5f9; --kb-kbd-border: #e2e8f0; --kb-kbd-text: #64748b; --kb-watermark-text: #94a3b8; }
@@ -1139,9 +1333,18 @@ var KenobiLib = (() => {
1139
1333
  .backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0); backdrop-filter: blur(0); -webkit-backdrop-filter: blur(0); transition: all 0.5s ease; z-index: 0; pointer-events: none; }
1140
1334
  .backdrop.active { background: rgba(0,0,0,0.1); backdrop-filter: blur(var(--kb-focus-blur)); -webkit-backdrop-filter: blur(var(--kb-focus-blur)); }
1141
1335
  .launcher { position: absolute; pointer-events: auto; background-color: var(--kb-bg-container); border: 1px solid var(--kb-border-container); box-shadow: var(--kb-shadow-container); backdrop-filter: blur(var(--kb-backdrop-blur)); -webkit-backdrop-filter: blur(var(--kb-backdrop-blur)); border-radius: 9999px; padding: 0.5rem 1rem; display: flex; align-items: center; gap: 0.75rem; cursor: pointer; transition: transform 0.2s, opacity 0.3s; opacity: 0; z-index: 1; color: var(--kb-text-title); font-weight: 500; font-size: 0.875rem; font-family: var(--kb-font-family); }
1142
- .launcher .kbd-hint .kbd { background: var(--kb-kbd-bg); border: 1px solid var(--kb-kbd-border); color: var(--kb-kbd-text); }
1143
- .launcher .kbd-hint .kbd-text { color: var(--kb-kbd-text); }
1144
1336
  .launcher:hover { transform: scale(1.02); background-color: var(--kb-bg-container); }
1337
+ .kb-chip-row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
1338
+ .kb-chip { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.35rem 0.8rem; border-radius: 9999px; border: 1px solid rgba(255,255,255,0.85); background: linear-gradient(135deg, rgba(255,255,255,0.28), rgba(255,255,255,0.08)); color: var(--kb-text-title); font-size: 0.8rem; font-weight: 600; backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); box-shadow: 0 8px 18px rgba(0,0,0,0.4), inset 0 0 0 1px rgba(255,255,255,0.45); }
1339
+ .kb-chip-logo { width: 18px; height: 18px; border-radius: 9999px; overflow: hidden; background: rgba(255,255,255,0.2); display: flex; align-items: center; justify-content: center; }
1340
+ .kb-chip-logo img { width: 100%; height: 100%; object-fit: cover; display: block; }
1341
+ .kb-chip-fallback { width: 18px; height: 18px; border-radius: 9999px; display: flex; align-items: center; justify-content: center; font-size: 0.7rem; font-weight: 700; color: var(--kb-text-title); background: rgba(255,255,255,0.25); }
1342
+ .kb-chip-action { margin-left: 0.35rem; font-size: 0.7rem; color: var(--kb-text-subtitle); text-decoration: underline; background: none; border: none; padding: 0; cursor: pointer; }
1343
+ .kb-chip-action:hover { color: var(--kb-text-title); }
1344
+ .kb-chip--launcher { padding: 0.25rem 0.65rem; font-size: 0.7rem; }
1345
+ .kb-chip-row.launcher-row { margin-bottom: 0; }
1346
+ .launcher-label { display: inline-flex; align-items: center; gap: 0.5rem; }
1347
+ .subtitle-chip { margin-top: 0.35rem; }
1145
1348
  .pos-top-center { left: 50%; translate: -50% 0; top: 1rem; }
1146
1349
  .pos-top-right { right: 1rem; top: 1rem; left: auto; }
1147
1350
  @media (max-width: 768px) { .pos-top-center, .pos-top-right { left: 0.5rem; right: 0.5rem; top: 0.5rem; translate: none; width: auto; max-width: none; } }
@@ -1156,7 +1359,7 @@ var KenobiLib = (() => {
1156
1359
  .text-content { flex: 1; user-select: none; }
1157
1360
  .text-row { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; }
1158
1361
  .title { color: var(--kb-text-title); font-size: 1rem; font-weight: 600; line-height: 1.5; }
1159
- .subtitle { color: var(--kb-text-title); font-size: 0.875rem; line-height: 1.25; opacity: 0.8; }
1362
+ .subtitle-text { color: var(--kb-text-title); font-size: 0.875rem; line-height: 1.25; opacity: 0.8; }
1160
1363
  .btn { display: inline-flex; align-items: center; justify-content: center; border-radius: 0.375rem; font-weight: 500; font-size: 0.875rem; line-height: 1.25rem; padding: 0.5rem 1rem; transition: all 0.2s; cursor: pointer; border: none; outline: none; }
1161
1364
  .btn-trigger { background-color: var(--kb-btn-trigger-bg); color: var(--kb-btn-trigger-text); min-width: 8rem; gap: 0.5rem; transition: opacity 0.2s ease-out, background-color 0.2s ease-out; }
1162
1365
  .btn-trigger.hidden { opacity: 0; pointer-events: none; }
@@ -1204,19 +1407,17 @@ var KenobiLib = (() => {
1204
1407
  .popover-watermark { position: absolute; top: 100%; left: 0; right: 0; text-align: center; font-size: 0.625rem; color: var(--kb-watermark-text); opacity: 0.6; font-weight: 500; letter-spacing: 0.025em; padding-top: 0.5rem; }
1205
1408
  .popover-watermark a { color: inherit; text-decoration: none; cursor: pointer; }
1206
1409
  .popover-watermark a:hover { text-decoration: underline; opacity: 1; }
1207
- /* Launcher hint positioning */
1208
- .launcher .kbd-hint { position: static; transform: none; opacity: 0.6; transition: opacity 0.2s; }
1209
- .launcher:hover .kbd-hint { opacity: 1; }
1210
- .pos-top-right.launcher .kbd-hint { left: auto; right: auto; }
1410
+ /* Launcher helper hint (below launcher) */
1411
+ .launcher-hint { position: absolute; top: calc(100% + 0.4rem); left: 50%; transform: translateX(-50%); pointer-events: none; white-space: nowrap; }
1412
+ .launcher-hint.visible { opacity: 0.65; }
1211
1413
  /* Launcher dismiss button */
1212
1414
  .launcher .btn-dismiss.launcher-dismiss { top: -0.375rem; left: -0.375rem; width: 1.25rem; height: 1.25rem; opacity: 0; transition: opacity 0.2s ease-out, background-color 0.3s ease-out, color 0.3s ease-out; }
1213
1415
  .launcher:hover .btn-dismiss.launcher-dismiss { opacity: 1; }
1214
1416
  /* Launcher logo */
1215
1417
  .launcher-logo { width: 20px; height: 20px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
1216
1418
  .launcher-logo svg { width: 20px; height: 20px; }
1217
- /* Mobile: hide keyboard hints, show dismiss button */
1419
+ /* Mobile: show dismiss button */
1218
1420
  @media (max-width: 768px) {
1219
- .launcher .kbd-hint { display: none; }
1220
1421
  .launcher .btn-dismiss.launcher-dismiss { opacity: 1; }
1221
1422
  }
1222
1423
  /* Toggle personalization button */
@@ -1226,6 +1427,11 @@ var KenobiLib = (() => {
1226
1427
  /* Custom tooltip for toggle button */
1227
1428
  .btn-toggle-personalization .toggle-tooltip { position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%); white-space: nowrap; background-color: var(--kb-bg-container); border: 1px solid var(--kb-border-container); color: var(--kb-text-title); font-size: 0.7rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 4px; opacity: 0; visibility: hidden; transition: opacity 0.15s ease-out, visibility 0.15s ease-out; pointer-events: none; z-index: 100; box-shadow: 0 2px 8px rgba(0,0,0,0.15); }
1228
1429
  .btn-toggle-personalization:hover .toggle-tooltip { opacity: 1; visibility: visible; }
1430
+ .btn-toggle-personalization.loading { cursor: default; opacity: 0.7; }
1431
+ .btn-toggle-personalization.loading:hover { transform: none; }
1432
+ .btn-toggle-personalization:disabled { cursor: default; }
1433
+ .launcher-domain { text-decoration: underline; text-underline-offset: 2px; }
1434
+ .launcher-actions { display: flex; align-items: center; gap: 0.25rem; }
1229
1435
  `;
1230
1436
  var useEnterExitAnimation = /* @__PURE__ */ __name((ref, isVisible, config) => {
1231
1437
  const hasEnteredRef = A2(false);
@@ -1321,7 +1527,14 @@ var KenobiLib = (() => {
1321
1527
  }
1322
1528
  return styles;
1323
1529
  }, "getThemeStyles");
1324
- var CueCardContent = /* @__PURE__ */ __name(({ config, isOpen, skipFocus, setIsOpen }) => {
1530
+ var CueCardContent = /* @__PURE__ */ __name(({
1531
+ config,
1532
+ isOpen,
1533
+ skipFocus,
1534
+ setIsOpen,
1535
+ deanonymizedCompany,
1536
+ onClearDeanonymizedCompany
1537
+ }) => {
1325
1538
  const isInline = config.mountMode === "inline";
1326
1539
  const containerRef = A2(null);
1327
1540
  const launcherRef = A2(null);
@@ -1348,6 +1561,14 @@ var KenobiLib = (() => {
1348
1561
  const timerProgressRef = A2(0);
1349
1562
  const timerEnabled = config.enableTimer !== false;
1350
1563
  const timerDuration = Math.max(500, config.timerDurationMs ?? 2e4);
1564
+ const isPending = config.status === "starting";
1565
+ const isSuccess = config.status === "success";
1566
+ const isError = config.status === "error";
1567
+ const isDeanonymized = !!deanonymizedCompany;
1568
+ const isPopoverEnabled = !isDeanonymized;
1569
+ const isPopoverOpen = isPopoverEnabled ? isOpen : false;
1570
+ const deanonymizedLabel = deanonymizedCompany?.name || deanonymizedCompany?.domain || "";
1571
+ const deanonymizedDomain = deanonymizedCompany?.domain;
1351
1572
  const setTimerProgress = q2((value) => {
1352
1573
  timerProgressRef.current = value;
1353
1574
  setTimerProgressState(value);
@@ -1415,7 +1636,7 @@ var KenobiLib = (() => {
1415
1636
  skipFocusRef.current = true;
1416
1637
  }
1417
1638
  y2(() => {
1418
- if (isOpen) {
1639
+ if (isPopoverOpen) {
1419
1640
  if (hasHadSuccess && formRef.current) {
1420
1641
  formRef.current.reset();
1421
1642
  }
@@ -1430,15 +1651,15 @@ var KenobiLib = (() => {
1430
1651
  return () => clearTimeout(focusTimer);
1431
1652
  }
1432
1653
  }
1433
- }, [isOpen]);
1434
- const prevIsOpenForExpandRef = A2(isOpen);
1654
+ }, [isPopoverOpen]);
1655
+ const prevIsOpenForExpandRef = A2(isPopoverOpen);
1435
1656
  y2(() => {
1436
1657
  const wasOpen = prevIsOpenForExpandRef.current;
1437
- prevIsOpenForExpandRef.current = isOpen;
1438
- if (!wasOpen && isOpen && mode === "launcher") {
1658
+ prevIsOpenForExpandRef.current = isPopoverOpen;
1659
+ if (!wasOpen && isPopoverOpen && mode === "launcher") {
1439
1660
  setMode("card");
1440
1661
  }
1441
- }, [isOpen, mode]);
1662
+ }, [isPopoverOpen, mode]);
1442
1663
  const isFirstAppearance = !hasEnteredRef.current;
1443
1664
  const wasLauncherPreviously = previousModeRef.current === "launcher";
1444
1665
  const cardEntranceDelay = isFirstAppearance ? config.entranceDelayMs || 0 : wasLauncherPreviously && mode === "card" ? 400 : 0;
@@ -1528,7 +1749,7 @@ var KenobiLib = (() => {
1528
1749
  e3.preventDefault();
1529
1750
  if (mode === "launcher") {
1530
1751
  setMode("card");
1531
- setIsOpen(true);
1752
+ setIsOpen(isPopoverEnabled);
1532
1753
  } else if (mode === "card") {
1533
1754
  if (config.enableLauncher) {
1534
1755
  setMode("launcher");
@@ -1542,29 +1763,61 @@ var KenobiLib = (() => {
1542
1763
  return () => window.removeEventListener("keydown", handleKey);
1543
1764
  }, [
1544
1765
  config.isVisible,
1545
- isOpen,
1766
+ isPopoverOpen,
1546
1767
  mode,
1547
1768
  config.enableLauncher,
1548
1769
  setIsOpen,
1549
- config.onDismiss
1770
+ config.onDismiss,
1771
+ isPopoverEnabled
1550
1772
  ]);
1773
+ const [isMobile, setIsMobile] = d2(false);
1551
1774
  const isMac = typeof navigator !== "undefined" && /Mac/.test(navigator.userAgent);
1552
- const shortcutDisplay = (() => {
1775
+ y2(() => {
1776
+ if (typeof window === "undefined") return;
1777
+ const media = window.matchMedia("(max-width: 768px)");
1778
+ const update = /* @__PURE__ */ __name(() => setIsMobile(media.matches), "update");
1779
+ update();
1780
+ if ("addEventListener" in media) {
1781
+ media.addEventListener("change", update);
1782
+ return () => media.removeEventListener("change", update);
1783
+ }
1784
+ window.addEventListener("resize", update);
1785
+ return () => window.removeEventListener("resize", update);
1786
+ }, []);
1787
+ const launcherHint = (() => {
1788
+ if (isMobile) {
1789
+ return { keys: [], label: "tap to launch" };
1790
+ }
1553
1791
  if (config.keyboardShortcut) {
1554
1792
  const mods = config.keyboardShortcut.modifiers || [];
1555
1793
  const keys = [];
1556
- if (mods.includes("meta")) keys.push(isMac ? "\u2318" : "Win");
1794
+ if (mods.includes("meta")) keys.push(isMac ? "\u2318" : "Ctrl");
1557
1795
  if (mods.includes("ctrl")) keys.push("Ctrl");
1558
1796
  if (mods.includes("alt")) keys.push(isMac ? "Opt" : "Alt");
1559
1797
  if (mods.includes("shift")) keys.push("Shift");
1560
1798
  keys.push(config.keyboardShortcut.key.toUpperCase());
1561
- return keys;
1799
+ return { keys, label: "to launch" };
1562
1800
  }
1563
- return [isMac ? "\u2318" : "Ctrl", "P"];
1801
+ return { keys: [isMac ? "\u2318" : "Ctrl", "P"], label: "to launch" };
1564
1802
  })();
1565
- const isPending = config.status === "starting";
1566
- const isSuccess = config.status === "success";
1567
- const isError = config.status === "error";
1803
+ const [logoFailed, setLogoFailed] = d2(false);
1804
+ const [logoAttempt, setLogoAttempt] = d2(
1805
+ "primary"
1806
+ );
1807
+ y2(() => {
1808
+ setLogoFailed(false);
1809
+ setLogoAttempt("primary");
1810
+ }, [deanonymizedCompany?.logoUrl, deanonymizedLabel, deanonymizedDomain]);
1811
+ y2(() => {
1812
+ if (!isPopoverEnabled && isOpen) {
1813
+ setIsOpen(false);
1814
+ }
1815
+ }, [isPopoverEnabled, isOpen, setIsOpen]);
1816
+ const logoUrlTemplate = config.deanonymization?.logoUrlTemplate;
1817
+ const normalizedLogoDomain = normalizeDomain(deanonymizedDomain);
1818
+ const fallbackLogoUrl = normalizedLogoDomain ? `https://logo.clearbit.com/${normalizedLogoDomain}` : void 0;
1819
+ const primaryLogoUrl = normalizedLogoDomain && logoUrlTemplate ? logoUrlTemplate.replace("{domain}", normalizedLogoDomain) : deanonymizedCompany?.logoUrl || fallbackLogoUrl;
1820
+ const resolvedLogoUrl = logoAttempt === "fallback" && fallbackLogoUrl && fallbackLogoUrl !== primaryLogoUrl ? fallbackLogoUrl : primaryLogoUrl;
1568
1821
  const showAsSuccess = isSuccess && isShowingInitialSuccessRef.current;
1569
1822
  const txt = config.textOverrides || {};
1570
1823
  const titleText = (() => {
@@ -1591,6 +1844,94 @@ var KenobiLib = (() => {
1591
1844
  }
1592
1845
  return txt.btnResting || "Show me";
1593
1846
  })();
1847
+ const launcherLabelText = (() => {
1848
+ if (isDeanonymized) {
1849
+ if (hasHadSuccess) {
1850
+ return txt.launcherLabelDeanonymizedSuccess || txt.launcherLabelDeanonymized || "See it again for";
1851
+ }
1852
+ return txt.launcherLabelDeanonymized || "See it in action for";
1853
+ }
1854
+ if (hasHadSuccess) {
1855
+ return txt.launcherLabelSuccess || txt.launcherLabel || "Personalize again";
1856
+ }
1857
+ return txt.launcherLabel || "Personalize";
1858
+ })();
1859
+ const renderDeanonymizedChip = /* @__PURE__ */ __name((variant) => {
1860
+ if (!deanonymizedCompany || !deanonymizedLabel) return null;
1861
+ const isLauncher = variant === "launcher";
1862
+ const fallbackLetter = deanonymizedLabel.trim().slice(0, 1).toUpperCase();
1863
+ const showLogo = !!resolvedLogoUrl && !logoFailed;
1864
+ return /* @__PURE__ */ u3("div", { class: `kb-chip ${isLauncher ? "kb-chip--launcher" : ""}`, children: [
1865
+ showLogo ? /* @__PURE__ */ u3("span", { class: "kb-chip-logo", children: /* @__PURE__ */ u3(
1866
+ "img",
1867
+ {
1868
+ src: resolvedLogoUrl,
1869
+ alt: "",
1870
+ referrerPolicy: "no-referrer",
1871
+ onError: () => {
1872
+ if (logoAttempt === "primary" && fallbackLogoUrl && fallbackLogoUrl !== primaryLogoUrl) {
1873
+ setLogoAttempt("fallback");
1874
+ return;
1875
+ }
1876
+ setLogoFailed(true);
1877
+ }
1878
+ }
1879
+ ) }) : /* @__PURE__ */ u3("span", { class: "kb-chip-fallback", children: fallbackLetter }),
1880
+ /* @__PURE__ */ u3("span", { children: deanonymizedLabel }),
1881
+ onClearDeanonymizedCompany && /* @__PURE__ */ u3(
1882
+ "button",
1883
+ {
1884
+ class: "kb-chip-action",
1885
+ type: "button",
1886
+ onClick: (e3) => {
1887
+ e3.stopPropagation();
1888
+ onClearDeanonymizedCompany();
1889
+ setIsOpen(true);
1890
+ },
1891
+ children: "Not you?"
1892
+ }
1893
+ )
1894
+ ] });
1895
+ }, "renderDeanonymizedChip");
1896
+ const buildDeanonymizedValues = /* @__PURE__ */ __name(() => {
1897
+ const normalizedInputDomain = normalizeDomain(deanonymizedDomain);
1898
+ const fallbackValue = (normalizedInputDomain || deanonymizedDomain || deanonymizedLabel || "").toString();
1899
+ const values = {};
1900
+ config.fields.forEach((field) => {
1901
+ const key = field.name;
1902
+ const lowerKey = key.toLowerCase();
1903
+ if (lowerKey.includes("domain")) {
1904
+ values[key] = normalizedInputDomain || fallbackValue;
1905
+ return;
1906
+ }
1907
+ if (lowerKey.includes("company")) {
1908
+ values[key] = deanonymizedLabel;
1909
+ return;
1910
+ }
1911
+ values[key] = fallbackValue;
1912
+ });
1913
+ return values;
1914
+ }, "buildDeanonymizedValues");
1915
+ const submitValues = /* @__PURE__ */ __name((values) => {
1916
+ const newErrors = {};
1917
+ let hasErrors = false;
1918
+ config.fields.forEach((field) => {
1919
+ const rawValue = values[field.name] ?? "";
1920
+ const value = typeof rawValue === "string" ? rawValue : String(rawValue);
1921
+ const error = validateField(field.name, value, field);
1922
+ if (error) {
1923
+ newErrors[field.name] = error;
1924
+ hasErrors = true;
1925
+ }
1926
+ });
1927
+ if (hasErrors) {
1928
+ setErrors(newErrors);
1929
+ return;
1930
+ }
1931
+ setErrors({});
1932
+ setIsOpen(false);
1933
+ config.onSubmitPersonalization?.(values);
1934
+ }, "submitValues");
1594
1935
  const validateField = /* @__PURE__ */ __name((name, value, fieldConfig) => {
1595
1936
  if (!fieldConfig) return null;
1596
1937
  if (fieldConfig.required && !value.trim()) {
@@ -1629,25 +1970,11 @@ var KenobiLib = (() => {
1629
1970
  if (isPending) return;
1630
1971
  const formData = new FormData(e3.target);
1631
1972
  const values = {};
1632
- const newErrors = {};
1633
- let hasErrors = false;
1634
1973
  formData.forEach((value, key) => {
1635
1974
  const strValue = value.toString();
1636
1975
  values[key] = strValue;
1637
- const fieldConfig = config.fields.find((f4) => f4.name === key);
1638
- const error = validateField(key, strValue, fieldConfig);
1639
- if (error) {
1640
- newErrors[key] = error;
1641
- hasErrors = true;
1642
- }
1643
1976
  });
1644
- if (hasErrors) {
1645
- setErrors(newErrors);
1646
- return;
1647
- }
1648
- setErrors({});
1649
- setIsOpen(false);
1650
- config.onSubmitPersonalization?.(values);
1977
+ submitValues(values);
1651
1978
  }, "handleSubmit");
1652
1979
  const handleInput = /* @__PURE__ */ __name((e3) => {
1653
1980
  const target = e3.target;
@@ -1666,10 +1993,15 @@ var KenobiLib = (() => {
1666
1993
  });
1667
1994
  }
1668
1995
  }, "handleInput");
1996
+ const handleDeanonymizedSubmit = /* @__PURE__ */ __name(() => {
1997
+ if (isPending || !isDeanonymized) return;
1998
+ submitValues(buildDeanonymizedValues());
1999
+ }, "handleDeanonymizedSubmit");
1669
2000
  const togglePopover = /* @__PURE__ */ __name((e3) => {
1670
2001
  e3.stopPropagation();
2002
+ if (isDeanonymized) return;
1671
2003
  if (isPending || isSuccess) return;
1672
- const opening = !isOpen;
2004
+ const opening = !isPopoverOpen;
1673
2005
  setIsOpen(opening);
1674
2006
  if (opening && config.enableClickSheen) {
1675
2007
  setSheenActive(true);
@@ -1679,7 +2011,7 @@ var KenobiLib = (() => {
1679
2011
  y2(() => {
1680
2012
  if (!timerEnabled) return;
1681
2013
  if (hasTimerCompletedRef.current) return;
1682
- const shouldRun = config.isVisible && mode === "card" && config.status === "resting" && !isOpen && !isHovering;
2014
+ const shouldRun = config.isVisible && mode === "card" && config.status === "resting" && !isPopoverOpen && !isHovering;
1683
2015
  if (!shouldRun) {
1684
2016
  if (timerRafRef.current !== null) {
1685
2017
  cancelAnimationFrame(timerRafRef.current);
@@ -1723,7 +2055,7 @@ var KenobiLib = (() => {
1723
2055
  config.onDismiss,
1724
2056
  config.status,
1725
2057
  isHovering,
1726
- isOpen,
2058
+ isPopoverOpen,
1727
2059
  mode,
1728
2060
  setIsOpen,
1729
2061
  timerDuration,
@@ -1742,6 +2074,7 @@ var KenobiLib = (() => {
1742
2074
  const isContainerVisible = !!config.isVisible && mode === "card" && !isDismissed;
1743
2075
  const isLauncherVisible = !!config.isVisible && mode === "launcher" && !isDismissed;
1744
2076
  const isAnythingVisible = isContainerVisible || isLauncherVisible;
2077
+ const showLauncherHint = !!config.showKeyboardHints && isLauncherVisible;
1745
2078
  return /* @__PURE__ */ u3(k, { children: [
1746
2079
  /* @__PURE__ */ u3(
1747
2080
  "div",
@@ -1755,12 +2088,12 @@ var KenobiLib = (() => {
1755
2088
  "div",
1756
2089
  {
1757
2090
  ref: containerRef,
1758
- class: `container theme-${config.theme || "glass"} pos-${config.position || "top-center"}${isOpen ? " popover-open" : ""}`,
2091
+ class: `container theme-${config.theme || "glass"} pos-${config.position || "top-center"}${isPopoverOpen ? " popover-open" : ""}`,
1759
2092
  style: {
1760
2093
  ...getThemeStyles(config.customTheme),
1761
2094
  pointerEvents: isContainerVisible ? "auto" : "none"
1762
2095
  },
1763
- onClick: () => !isOpen && !isPending && !isSuccess && setIsOpen(true),
2096
+ onClick: () => !isPopoverOpen && !isPending && !isSuccess && isPopoverEnabled && setIsOpen(true),
1764
2097
  onMouseEnter: () => setIsHovering(true),
1765
2098
  onMouseLeave: () => setIsHovering(false),
1766
2099
  children: [
@@ -1785,7 +2118,7 @@ var KenobiLib = (() => {
1785
2118
  /* @__PURE__ */ u3("div", { class: "content-wrapper", children: /* @__PURE__ */ u3("div", { class: "text-content", children: /* @__PURE__ */ u3("div", { class: "text-row", children: [
1786
2119
  /* @__PURE__ */ u3("div", { children: [
1787
2120
  /* @__PURE__ */ u3("div", { class: "title", children: titleText }),
1788
- /* @__PURE__ */ u3("div", { class: "subtitle", children: subtitleText })
2121
+ isDeanonymized ? /* @__PURE__ */ u3("div", { class: "subtitle-chip", children: renderDeanonymizedChip("card") }) : /* @__PURE__ */ u3("div", { class: "subtitle-text", children: subtitleText })
1789
2122
  ] }),
1790
2123
  isPending ? /* @__PURE__ */ u3("span", { class: "status-text", children: [
1791
2124
  /* @__PURE__ */ u3(LoaderIcon, {}),
@@ -1794,9 +2127,9 @@ var KenobiLib = (() => {
1794
2127
  ] }) : /* @__PURE__ */ u3(
1795
2128
  "button",
1796
2129
  {
1797
- class: `btn btn-trigger ${showAsSuccess ? "success" : ""}${isOpen || isExitingToLauncher ? " hidden" : ""}`,
2130
+ class: `btn btn-trigger ${showAsSuccess ? "success" : ""}${isPopoverOpen || isExitingToLauncher ? " hidden" : ""}`,
1798
2131
  disabled: showAsSuccess,
1799
- onClick: togglePopover,
2132
+ onClick: isDeanonymized ? handleDeanonymizedSubmit : togglePopover,
1800
2133
  children: showAsSuccess ? /* @__PURE__ */ u3(k, { children: [
1801
2134
  /* @__PURE__ */ u3(CheckIcon, {}),
1802
2135
  " ",
@@ -1814,10 +2147,10 @@ var KenobiLib = (() => {
1814
2147
  }
1815
2148
  }
1816
2149
  ) }) }),
1817
- /* @__PURE__ */ u3(
2150
+ !isDeanonymized && /* @__PURE__ */ u3(
1818
2151
  "div",
1819
2152
  {
1820
- class: `popover ${isOpen ? "open" : ""}`,
2153
+ class: `popover ${isPopoverOpen ? "open" : ""}`,
1821
2154
  onClick: (e3) => e3.stopPropagation(),
1822
2155
  children: [
1823
2156
  /* @__PURE__ */ u3("form", { onSubmit: handleSubmit, ref: formRef, children: [
@@ -1884,7 +2217,7 @@ var KenobiLib = (() => {
1884
2217
  config.showKeyboardHints && config.enableFocusMode && /* @__PURE__ */ u3(
1885
2218
  "div",
1886
2219
  {
1887
- class: `kbd-hint ${!isOpen && !isPending && !isSuccess ? "visible" : ""}`,
2220
+ class: `kbd-hint ${!isPopoverOpen && !isPending && !isSuccess ? "visible" : ""}`,
1888
2221
  children: [
1889
2222
  /* @__PURE__ */ u3("div", { class: "kbd-group", children: /* @__PURE__ */ u3("div", { class: "kbd", children: "Esc" }) }),
1890
2223
  /* @__PURE__ */ u3("span", { class: "kbd-text", children: "to dismiss" })
@@ -1908,7 +2241,7 @@ var KenobiLib = (() => {
1908
2241
  e3.stopPropagation();
1909
2242
  setIsDismissed(false);
1910
2243
  setMode("card");
1911
- setIsOpen(true);
2244
+ setIsOpen(isPopoverEnabled);
1912
2245
  },
1913
2246
  role: "button",
1914
2247
  tabIndex: 0,
@@ -1938,23 +2271,56 @@ var KenobiLib = (() => {
1938
2271
  }
1939
2272
  ),
1940
2273
  config.showLauncherLogo !== false && /* @__PURE__ */ u3("div", { class: "launcher-logo", children: /* @__PURE__ */ u3(LogoIcon, {}) }),
1941
- hasHadSuccess ? config.textOverrides?.launcherLabelSuccess || config.textOverrides?.launcherLabel || "Personalize again" : config.textOverrides?.launcherLabel || "Personalize",
1942
- config.enableUndoToggle && hasHadSuccess && /* @__PURE__ */ u3(
1943
- "button",
1944
- {
1945
- class: "btn-toggle-personalization",
1946
- "aria-label": config.isPersonalized ? "Show original" : "Show personalized",
1947
- onClick: (e3) => {
1948
- e3.stopPropagation();
1949
- config.onTogglePersonalization?.();
1950
- },
1951
- children: [
1952
- /* @__PURE__ */ u3(ArrowPathIcon, {}),
1953
- /* @__PURE__ */ u3("span", { class: "toggle-tooltip", children: config.isPersonalized ? "Show original" : "Show personalized" })
1954
- ]
1955
- }
1956
- ),
1957
- config.showKeyboardHints && /* @__PURE__ */ u3("div", { class: "kbd-hint visible", children: /* @__PURE__ */ u3("div", { class: "kbd-group", children: shortcutDisplay.map((k3, i3) => /* @__PURE__ */ u3("div", { class: "kbd", children: k3 }, i3)) }) })
2274
+ /* @__PURE__ */ u3("div", { class: "launcher-label", children: [
2275
+ /* @__PURE__ */ u3("span", { children: (() => {
2276
+ const forMatch = launcherLabelText.match(/^(For\s+)(.+)$/i);
2277
+ if (forMatch) {
2278
+ return /* @__PURE__ */ u3(k, { children: [
2279
+ forMatch[1],
2280
+ /* @__PURE__ */ u3("span", { class: "launcher-domain", children: forMatch[2] })
2281
+ ] });
2282
+ }
2283
+ return launcherLabelText;
2284
+ })() }),
2285
+ isDeanonymized && renderDeanonymizedChip("launcher")
2286
+ ] }),
2287
+ /* @__PURE__ */ u3("div", { class: "launcher-actions", children: [
2288
+ config.enableUndoToggle && hasHadSuccess && /* @__PURE__ */ u3(
2289
+ "button",
2290
+ {
2291
+ class: "btn-toggle-personalization",
2292
+ "aria-label": config.isPersonalized ? "Show original" : "Show personalized",
2293
+ onClick: (e3) => {
2294
+ e3.stopPropagation();
2295
+ config.onTogglePersonalization?.();
2296
+ },
2297
+ children: [
2298
+ /* @__PURE__ */ u3(ArrowPathIcon, {}),
2299
+ /* @__PURE__ */ u3("span", { class: "toggle-tooltip", children: config.isPersonalized ? "Show original" : "Show personalized" })
2300
+ ]
2301
+ }
2302
+ ),
2303
+ config.launcherAction && config.launcherAction.status !== "idle" && hasHadSuccess && /* @__PURE__ */ u3(
2304
+ "button",
2305
+ {
2306
+ class: `btn-toggle-personalization${config.launcherAction.status === "loading" ? " loading" : ""}`,
2307
+ "aria-label": config.launcherAction.tooltip || "Action",
2308
+ onClick: (e3) => {
2309
+ e3.stopPropagation();
2310
+ config.launcherAction?.onClick?.();
2311
+ },
2312
+ disabled: config.launcherAction.status === "loading",
2313
+ children: [
2314
+ config.launcherAction.status === "loading" ? /* @__PURE__ */ u3(LoaderIcon, {}) : /* @__PURE__ */ u3(ArrowRightIcon, {}),
2315
+ config.launcherAction.tooltip && /* @__PURE__ */ u3("span", { class: "toggle-tooltip", children: config.launcherAction.tooltip })
2316
+ ]
2317
+ }
2318
+ )
2319
+ ] }),
2320
+ showLauncherHint && /* @__PURE__ */ u3("div", { class: "kbd-hint visible launcher-hint", "aria-hidden": "true", children: [
2321
+ launcherHint.keys.length > 0 && /* @__PURE__ */ u3("div", { class: "kbd-group", children: launcherHint.keys.map((key, index) => /* @__PURE__ */ u3("div", { class: "kbd", children: key }, index)) }),
2322
+ /* @__PURE__ */ u3("span", { class: "kbd-text", children: launcherHint.label })
2323
+ ] })
1958
2324
  ]
1959
2325
  }
1960
2326
  )
@@ -1968,6 +2334,33 @@ var KenobiLib = (() => {
1968
2334
  __publicField(this, "isOpen", false);
1969
2335
  __publicField(this, "skipFocus", false);
1970
2336
  __publicField(this, "mountTarget", null);
2337
+ __publicField(this, "deanonymizedCompany", null);
2338
+ __publicField(this, "deanonymizationStatus", "idle");
2339
+ __publicField(this, "deanonymizationCleanup", null);
2340
+ __publicField(this, "hasStartedDeanonymization", false);
2341
+ __publicField(this, "clearDeanonymizedCompany", /* @__PURE__ */ __name(() => {
2342
+ const previousCompany = this.deanonymizedCompany;
2343
+ const shouldResetStatus = this.config.status === "success" || this.config.isPersonalized;
2344
+ if (this.config.isPersonalized) {
2345
+ this.config.onTogglePersonalization?.();
2346
+ }
2347
+ this.deanonymizedCompany = null;
2348
+ this.deanonymizationStatus = "failed";
2349
+ if (previousCompany) {
2350
+ dispatchCueCardEvent("deanonymization:not-you", {
2351
+ company: previousCompany
2352
+ });
2353
+ }
2354
+ if (shouldResetStatus) {
2355
+ this.config = {
2356
+ ...this.config,
2357
+ status: "resting",
2358
+ progressPct: 0,
2359
+ isPersonalized: false
2360
+ };
2361
+ }
2362
+ this.render();
2363
+ }, "clearDeanonymizedCompany"));
1971
2364
  this.config = {
1972
2365
  position: "top-center",
1973
2366
  direction: "top-to-bottom",
@@ -1990,6 +2383,9 @@ var KenobiLib = (() => {
1990
2383
  ...config
1991
2384
  };
1992
2385
  this.mountTarget = config.mountNode ?? null;
2386
+ if (this.isDeanonymizationEnabled()) {
2387
+ this.deanonymizationStatus = "pending";
2388
+ }
1993
2389
  this.host = document.createElement("div");
1994
2390
  this.host.id = "kenobi-cue-card-root";
1995
2391
  this.applyHostStyles();
@@ -2038,20 +2434,95 @@ var KenobiLib = (() => {
2038
2434
  resolvedTarget.appendChild(this.host);
2039
2435
  }
2040
2436
  this.applyHostStyles();
2437
+ this.startDeanonymization();
2041
2438
  this.render();
2042
2439
  }
2043
2440
  unmount() {
2044
2441
  G(null, this.shadow.lastElementChild);
2045
2442
  this.host.remove();
2443
+ this.deanonymizationCleanup?.();
2444
+ this.deanonymizationCleanup = null;
2046
2445
  }
2047
2446
  update(newState) {
2048
2447
  if (newState.status === "starting" && this.config.status !== "starting") {
2049
2448
  this.isOpen = false;
2050
2449
  }
2450
+ const wasDeanonymizationEnabled = this.isDeanonymizationEnabled();
2051
2451
  this.config = { ...this.config, ...newState };
2452
+ const isDeanonymizationEnabled = this.isDeanonymizationEnabled();
2453
+ if (isDeanonymizationEnabled && !wasDeanonymizationEnabled) {
2454
+ this.deanonymizationStatus = "pending";
2455
+ this.startDeanonymization();
2456
+ }
2457
+ if (!isDeanonymizationEnabled && wasDeanonymizationEnabled) {
2458
+ this.deanonymizedCompany = null;
2459
+ this.deanonymizationStatus = "idle";
2460
+ this.deanonymizationCleanup?.();
2461
+ this.deanonymizationCleanup = null;
2462
+ this.hasStartedDeanonymization = false;
2463
+ }
2052
2464
  this.applyHostStyles();
2053
2465
  this.render();
2054
2466
  }
2467
+ isDeanonymizationEnabled() {
2468
+ return this.config.deanonymization?.provider === "Kenobi";
2469
+ }
2470
+ startDeanonymization() {
2471
+ if (!this.isDeanonymizationEnabled()) return;
2472
+ if (this.hasStartedDeanonymization) return;
2473
+ if (this.deanonymizationStatus !== "pending") return;
2474
+ this.hasStartedDeanonymization = true;
2475
+ if (typeof window === "undefined") {
2476
+ this.deanonymizationStatus = "failed";
2477
+ this.render();
2478
+ return;
2479
+ }
2480
+ const cachedIdentity = window.__kenobiIdentityResolution;
2481
+ if (cachedIdentity) {
2482
+ this.handleDeanonymizationResult(cachedIdentity);
2483
+ return;
2484
+ }
2485
+ const handleEvent = /* @__PURE__ */ __name((event) => {
2486
+ const detail = event.detail;
2487
+ this.handleDeanonymizationResult(detail);
2488
+ }, "handleEvent");
2489
+ window.addEventListener(IDENTITY_RESOLVED_EVENT, handleEvent);
2490
+ ensureKenobiSnitcherLoaded(this.config.deanonymization);
2491
+ const timeoutMs = this.config.deanonymization?.timeoutMs ?? DEFAULT_DEANON_TIMEOUT_MS;
2492
+ const timeoutId = window.setTimeout(() => {
2493
+ window.removeEventListener(IDENTITY_RESOLVED_EVENT, handleEvent);
2494
+ this.handleDeanonymizationResult(null);
2495
+ }, timeoutMs);
2496
+ this.deanonymizationCleanup = () => {
2497
+ window.removeEventListener(IDENTITY_RESOLVED_EVENT, handleEvent);
2498
+ window.clearTimeout(timeoutId);
2499
+ };
2500
+ }
2501
+ handleDeanonymizationResult(detail) {
2502
+ if (this.deanonymizationStatus !== "pending") return;
2503
+ this.deanonymizationCleanup?.();
2504
+ this.deanonymizationCleanup = null;
2505
+ const company = resolveDeanonymizedCompany(detail);
2506
+ this.deanonymizedCompany = company;
2507
+ this.deanonymizationStatus = company ? "resolved" : "failed";
2508
+ if (company) {
2509
+ dispatchCueCardEvent("deanonymization:success", {
2510
+ company,
2511
+ detail
2512
+ });
2513
+ } else {
2514
+ dispatchCueCardEvent("deanonymization:fail", {
2515
+ detail
2516
+ });
2517
+ }
2518
+ this.render();
2519
+ }
2520
+ getRenderConfig() {
2521
+ if (this.deanonymizationStatus === "pending") {
2522
+ return { ...this.config, isVisible: false };
2523
+ }
2524
+ return this.config;
2525
+ }
2055
2526
  setTheme(theme) {
2056
2527
  this.update({ theme });
2057
2528
  }
@@ -2068,9 +2539,11 @@ var KenobiLib = (() => {
2068
2539
  /* @__PURE__ */ u3(
2069
2540
  CueCardContent,
2070
2541
  {
2071
- config: this.config,
2542
+ config: this.getRenderConfig(),
2072
2543
  isOpen: this.isOpen,
2073
2544
  skipFocus: skipFocusOnce,
2545
+ deanonymizedCompany: this.deanonymizedCompany,
2546
+ onClearDeanonymizedCompany: this.clearDeanonymizedCompany,
2074
2547
  setIsOpen: (val) => {
2075
2548
  this.isOpen = val;
2076
2549
  this.config.onOpenChange?.(val);
@@ -2492,6 +2965,8 @@ var KenobiLib = (() => {
2492
2965
  };
2493
2966
  __name(_TransitionOrchestrator, "TransitionOrchestrator");
2494
2967
  var TransitionOrchestrator = _TransitionOrchestrator;
2968
+ var IDENTITY_RESOLVED_EVENT2 = "kenobi:identity-resolved";
2969
+ var DEFAULT_DEANON_TIMEOUT_MS2 = 1600;
2495
2970
  var isCueCardConfigResponse = /* @__PURE__ */ __name((value) => {
2496
2971
  if (typeof value !== "object" || value === null) return false;
2497
2972
  const candidate = value;
@@ -2528,6 +3003,10 @@ var KenobiLib = (() => {
2528
3003
  __publicField(this, "visitorKey", null);
2529
3004
  __publicField(this, "orchestrator");
2530
3005
  __publicField(this, "cueCardInstance", null);
3006
+ __publicField(this, "preparedPersonalization", null);
3007
+ __publicField(this, "isPreparingPersonalization", false);
3008
+ __publicField(this, "preparePersonalizationPromise", null);
3009
+ __publicField(this, "deanonymizationPrefetchCleanup", null);
2531
3010
  //
2532
3011
  __publicField(this, "currentPath");
2533
3012
  __publicField(this, "currentUrl");
@@ -3227,6 +3706,8 @@ var KenobiLib = (() => {
3227
3706
  }
3228
3707
  this.log("debug", "Active template found, mounting CueCard...");
3229
3708
  const serverConfig = configResponse.config;
3709
+ const deanonymizationConfig = serverConfig?.deanonymization;
3710
+ const shouldWaitForDeanonymization = !!deanonymizationConfig?.provider;
3230
3711
  this.cueCardInstance = new CueCard({
3231
3712
  theme: serverConfig?.theme ?? "light",
3232
3713
  position: serverConfig?.position ?? "top-center",
@@ -3241,7 +3722,7 @@ var KenobiLib = (() => {
3241
3722
  errorMessage: "Company name or domain is required"
3242
3723
  }
3243
3724
  ],
3244
- isVisible: true,
3725
+ isVisible: !shouldWaitForDeanonymization,
3245
3726
  enableFocusMode: serverConfig?.enableFocusMode ?? false,
3246
3727
  showWatermark: serverConfig?.showWatermark ?? false,
3247
3728
  showKeyboardHints: serverConfig?.showKeyboardHints ?? true,
@@ -3249,6 +3730,7 @@ var KenobiLib = (() => {
3249
3730
  customTheme: serverConfig?.customTheme,
3250
3731
  textOverrides: serverConfig?.textOverrides,
3251
3732
  enableUndoToggle: serverConfig?.enableUndoToggle ?? false,
3733
+ deanonymization: deanonymizationConfig,
3252
3734
  isPersonalized: this.isPersonalized,
3253
3735
  onDismiss: /* @__PURE__ */ __name(() => {
3254
3736
  this.log("debug", "CueCard dismissed by user");
@@ -3277,8 +3759,220 @@ var KenobiLib = (() => {
3277
3759
  }, "onTogglePersonalization")
3278
3760
  });
3279
3761
  this.cueCardInstance.mount();
3762
+ if (shouldWaitForDeanonymization) {
3763
+ this.setupDeanonymizationPrefetch(
3764
+ deanonymizationConfig,
3765
+ serverConfig?.fields
3766
+ );
3767
+ }
3280
3768
  this.log("debug", "CueCard mounted successfully");
3281
3769
  }, "initCueCard"));
3770
+ __publicField(this, "isSameInput", /* @__PURE__ */ __name((a3, b) => {
3771
+ const aKeys = Object.keys(a3).sort();
3772
+ const bKeys = Object.keys(b).sort();
3773
+ if (aKeys.length !== bKeys.length) return false;
3774
+ for (let i3 = 0; i3 < aKeys.length; i3 += 1) {
3775
+ const key = aKeys[i3];
3776
+ if (key !== bKeys[i3]) return false;
3777
+ if ((a3[key] ?? "") !== (b[key] ?? "")) return false;
3778
+ }
3779
+ return true;
3780
+ }, "isSameInput"));
3781
+ __publicField(this, "buildInputFromFields", /* @__PURE__ */ __name((fields, companyName, companyDomain) => {
3782
+ const fallback = companyDomain || companyName;
3783
+ if (!fallback) return null;
3784
+ const input = {};
3785
+ fields.forEach((field) => {
3786
+ const key = field.name;
3787
+ const lowerKey = key.toLowerCase();
3788
+ if (lowerKey.includes("domain")) {
3789
+ input[key] = companyDomain || fallback;
3790
+ return;
3791
+ }
3792
+ if (lowerKey.includes("company")) {
3793
+ input[key] = companyName || fallback;
3794
+ return;
3795
+ }
3796
+ input[key] = fallback;
3797
+ });
3798
+ return input;
3799
+ }, "buildInputFromFields"));
3800
+ __publicField(this, "buildDeanonymizedInput", /* @__PURE__ */ __name((detail, fields) => {
3801
+ const name = detail.company?.name || detail.domain || "";
3802
+ const domain = detail.company?.domain || detail.domain || "";
3803
+ if (!fields || fields.length === 0) {
3804
+ if (!name && !domain) return null;
3805
+ return {
3806
+ companyName: name || domain,
3807
+ companyDomain: domain || name
3808
+ };
3809
+ }
3810
+ return this.buildInputFromFields(fields, name, domain);
3811
+ }, "buildDeanonymizedInput"));
3812
+ __publicField(this, "requestPersonalization", /* @__PURE__ */ __name(async (input, options) => {
3813
+ const payload = {
3814
+ publicKey: this.config.publicKey,
3815
+ input,
3816
+ pagePath: this.currentPath
3817
+ };
3818
+ if (options?.notifySlack === false) {
3819
+ payload.notifySlack = false;
3820
+ }
3821
+ if (options?.notifyOnly) {
3822
+ payload.notifyOnly = true;
3823
+ }
3824
+ if (options?.predictionSource) {
3825
+ payload.predictionSource = options.predictionSource;
3826
+ }
3827
+ if (options?.prefetchedTransformations) {
3828
+ payload.prefetchedTransformations = options.prefetchedTransformations;
3829
+ }
3830
+ if (options?.prefetchedMetadata) {
3831
+ payload.prefetchedMetadata = options.prefetchedMetadata;
3832
+ }
3833
+ const response = await fetch(`${this.config.apiHost}/v1/personalize`, {
3834
+ method: "POST",
3835
+ headers: {
3836
+ "Content-Type": "application/json"
3837
+ },
3838
+ body: JSON.stringify(payload)
3839
+ });
3840
+ if (!response.ok) {
3841
+ const errorBody = await response.json().catch(() => ({}));
3842
+ throw new Error(errorBody.error || `API error: ${response.status}`);
3843
+ }
3844
+ const result = await response.json();
3845
+ this.log("debug", "Personalization API response:", result);
3846
+ return result;
3847
+ }, "requestPersonalization"));
3848
+ __publicField(this, "preparePersonalization", /* @__PURE__ */ __name(async (input, options) => {
3849
+ if (!this.config.publicKey) {
3850
+ this.log("error", "Cannot prepare personalization: publicKey not configured");
3851
+ return null;
3852
+ }
3853
+ if (this.preparedPersonalization?.input && this.isSameInput(this.preparedPersonalization.input, input)) {
3854
+ if (options?.source && !this.preparedPersonalization.source) {
3855
+ this.preparedPersonalization = {
3856
+ ...this.preparedPersonalization,
3857
+ source: options.source
3858
+ };
3859
+ }
3860
+ return this.preparedPersonalization.result;
3861
+ }
3862
+ if (this.preparePersonalizationPromise) {
3863
+ return this.preparePersonalizationPromise;
3864
+ }
3865
+ this.isPreparingPersonalization = true;
3866
+ this.preparePersonalizationPromise = (async () => {
3867
+ try {
3868
+ const result = await this.requestPersonalization(input, {
3869
+ notifySlack: false,
3870
+ predictionSource: options?.source
3871
+ });
3872
+ this.preparedPersonalization = { input, result, source: options?.source };
3873
+ this.dispatchKenobiEvent("personalization:ready", {
3874
+ input,
3875
+ pagePath: this.currentPath,
3876
+ transformationsCount: result.transformations?.length ?? 0,
3877
+ metadata: result.metadata,
3878
+ source: options?.source
3879
+ });
3880
+ return result;
3881
+ } catch (error) {
3882
+ this.log("error", "Personalization prefetch failed:", error);
3883
+ return null;
3884
+ } finally {
3885
+ this.isPreparingPersonalization = false;
3886
+ this.preparePersonalizationPromise = null;
3887
+ }
3888
+ })();
3889
+ return this.preparePersonalizationPromise;
3890
+ }, "preparePersonalization"));
3891
+ __publicField(this, "notifyPreparedPersonalization", /* @__PURE__ */ __name(async (prepared) => {
3892
+ if (!prepared.result.transformations || !prepared.result.metadata) {
3893
+ this.log(
3894
+ "warn",
3895
+ "Prepared personalization missing transformations or metadata for notify"
3896
+ );
3897
+ return;
3898
+ }
3899
+ try {
3900
+ await this.requestPersonalization(prepared.input, {
3901
+ notifySlack: true,
3902
+ notifyOnly: true,
3903
+ predictionSource: prepared.source,
3904
+ prefetchedTransformations: prepared.result.transformations,
3905
+ prefetchedMetadata: prepared.result.metadata
3906
+ });
3907
+ } catch (error) {
3908
+ this.log(
3909
+ "warn",
3910
+ "Failed to send Slack notification for prepared personalization:",
3911
+ error
3912
+ );
3913
+ }
3914
+ }, "notifyPreparedPersonalization"));
3915
+ __publicField(this, "setupDeanonymizationPrefetch", /* @__PURE__ */ __name((config, fields) => {
3916
+ if (!this.cueCardInstance) return;
3917
+ if (typeof window === "undefined") {
3918
+ this.cueCardInstance.update({ isVisible: true });
3919
+ return;
3920
+ }
3921
+ if (this.deanonymizationPrefetchCleanup) return;
3922
+ let resolved = false;
3923
+ let hasIdentity = false;
3924
+ let shouldDisableDeanonymization = false;
3925
+ const finish = /* @__PURE__ */ __name(() => {
3926
+ if (resolved) return;
3927
+ resolved = true;
3928
+ if (shouldDisableDeanonymization) {
3929
+ this.cueCardInstance?.update({
3930
+ deanonymization: void 0,
3931
+ isVisible: true
3932
+ });
3933
+ } else {
3934
+ this.cueCardInstance?.update({ isVisible: true });
3935
+ }
3936
+ this.deanonymizationPrefetchCleanup?.();
3937
+ this.deanonymizationPrefetchCleanup = null;
3938
+ }, "finish");
3939
+ const handleIdentity = /* @__PURE__ */ __name(async (event) => {
3940
+ if (resolved) return;
3941
+ const detail = event.detail;
3942
+ hasIdentity = true;
3943
+ if (detail?.type !== "business") {
3944
+ finish();
3945
+ return;
3946
+ }
3947
+ const input = this.buildDeanonymizedInput(detail, fields);
3948
+ if (!input) {
3949
+ finish();
3950
+ return;
3951
+ }
3952
+ const prepared = await this.preparePersonalization(input, {
3953
+ source: "deanonymized"
3954
+ });
3955
+ if (!prepared) {
3956
+ shouldDisableDeanonymization = true;
3957
+ }
3958
+ finish();
3959
+ }, "handleIdentity");
3960
+ const cachedIdentity = window.__kenobiIdentityResolution;
3961
+ if (cachedIdentity) {
3962
+ void handleIdentity({ detail: cachedIdentity });
3963
+ }
3964
+ window.addEventListener(IDENTITY_RESOLVED_EVENT2, handleIdentity);
3965
+ const timeoutMs = config?.timeoutMs ?? DEFAULT_DEANON_TIMEOUT_MS2;
3966
+ const timeoutId = window.setTimeout(() => {
3967
+ if (!hasIdentity) {
3968
+ finish();
3969
+ }
3970
+ }, timeoutMs + 50);
3971
+ this.deanonymizationPrefetchCleanup = () => {
3972
+ window.removeEventListener(IDENTITY_RESOLVED_EVENT2, handleIdentity);
3973
+ window.clearTimeout(timeoutId);
3974
+ };
3975
+ }, "setupDeanonymizationPrefetch"));
3282
3976
  /**
3283
3977
  * Syncs CueCard visibility based on the current path.
3284
3978
  * Checks with the server if an active template exists for the new path.
@@ -3319,46 +4013,36 @@ var KenobiLib = (() => {
3319
4013
  __publicField(this, "personalize", /* @__PURE__ */ __name(async (input) => {
3320
4014
  if (!this.config.publicKey) {
3321
4015
  this.log("error", "Cannot personalize: publicKey not configured");
3322
- return;
4016
+ return null;
3323
4017
  }
3324
4018
  const startTime = /* @__PURE__ */ new Date();
3325
4019
  this.log("debug", "Starting personalization with input:", input);
4020
+ const prepared = this.preparedPersonalization?.input && this.isSameInput(this.preparedPersonalization.input, input) ? this.preparedPersonalization : null;
4021
+ const isDeanonymizedInput = prepared?.source === "deanonymized";
3326
4022
  this.dispatchKenobiEvent("personalization:start", {
3327
4023
  input,
3328
- pagePath: this.currentPath
4024
+ pagePath: this.currentPath,
4025
+ deanonymized: isDeanonymizedInput
3329
4026
  });
3330
- if (this.cueCardInstance) {
3331
- this.cueCardInstance.update({ status: "starting", progressPct: 10 });
3332
- }
3333
4027
  try {
3334
- const response = await fetch(`${this.config.apiHost}/v1/personalize`, {
3335
- method: "POST",
3336
- headers: {
3337
- "Content-Type": "application/json"
3338
- },
3339
- body: JSON.stringify({
3340
- publicKey: this.config.publicKey,
3341
- input,
3342
- pagePath: this.currentPath
3343
- })
3344
- });
3345
4028
  if (this.cueCardInstance) {
3346
- this.cueCardInstance.update({ progressPct: 50 });
3347
- }
3348
- if (!response.ok) {
3349
- const errorBody = await response.json().catch(() => ({}));
3350
- throw new Error(errorBody.error || `API error: ${response.status}`);
4029
+ this.cueCardInstance.update({
4030
+ status: "starting",
4031
+ progressPct: prepared ? 80 : 10
4032
+ });
3351
4033
  }
3352
- const result = await response.json();
3353
- this.log("debug", "Personalization API response:", result);
3354
- if (this.cueCardInstance) {
3355
- this.cueCardInstance.update({ progressPct: 80 });
4034
+ if (prepared) {
4035
+ void this.notifyPreparedPersonalization(prepared);
3356
4036
  }
4037
+ const result = prepared ? prepared.result : await this.requestPersonalization(input);
3357
4038
  if (result.transformations && Array.isArray(result.transformations)) {
3358
4039
  await this.applyTransformations(result.transformations, {
3359
4040
  shouldConsolidate: true
3360
4041
  });
3361
4042
  }
4043
+ if (prepared) {
4044
+ this.preparedPersonalization = null;
4045
+ }
3362
4046
  if (this.cueCardInstance) {
3363
4047
  this.cueCardInstance.update({ status: "success", progressPct: 100 });
3364
4048
  }
@@ -3373,8 +4057,11 @@ var KenobiLib = (() => {
3373
4057
  pagePath: this.currentPath,
3374
4058
  durationMs: duration,
3375
4059
  transformationsCount: result.transformations?.length ?? 0,
3376
- metadata: result.metadata
4060
+ metadata: result.metadata,
4061
+ source: prepared ? "prefetch" : "api",
4062
+ deanonymized: isDeanonymizedInput
3377
4063
  });
4064
+ return result;
3378
4065
  } catch (error) {
3379
4066
  this.log("error", "Personalization failed:", error);
3380
4067
  if (this.cueCardInstance) {
@@ -3383,9 +4070,11 @@ var KenobiLib = (() => {
3383
4070
  this.dispatchKenobiEvent("personalization:error", {
3384
4071
  input,
3385
4072
  pagePath: this.currentPath,
3386
- error: error instanceof Error ? error.message : String(error)
4073
+ error: error instanceof Error ? error.message : String(error),
4074
+ deanonymized: isDeanonymizedInput
3387
4075
  });
3388
4076
  }
4077
+ return null;
3389
4078
  }, "personalize"));
3390
4079
  /**
3391
4080
  * Returns the CueCard instance if one was auto-mounted.