kenobi-js 0.1.28 → 0.1.29

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/dist/kenobi.js CHANGED
@@ -1128,9 +1128,9 @@ var KenobiLib = (() => {
1128
1128
  }
1129
1129
  ), "LogoIcon");
1130
1130
  var STYLES = `
1131
- :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-focus-blur: 10px; --kb-kbd-bg: rgba(255, 255, 255, 0.1); --kb-kbd-border: rgba(255, 255, 255, 0.2); --kb-kbd-text: rgba(255, 255, 255, 0.6); --kb-watermark-text: rgba(255, 255, 255, 0.4); font-family: var(--kb-font-family); }
1132
- .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-kbd-bg: #f1f5f9; --kb-kbd-border: #e2e8f0; --kb-kbd-text: #64748b; --kb-watermark-text: #94a3b8; }
1133
- .theme-dark { --kb-bg-container: #0f172a; --kb-border-container: #334155; --kb-shadow-container: 0 25px 50px -12px rgba(0, 0, 0, 0.25); --kb-backdrop-blur: 0px; --kb-text-title: #f1f5f9; --kb-text-subtitle: #94a3b8; --kb-btn-dismiss-bg: #1e293b; --kb-btn-dismiss-border: #334155; --kb-btn-dismiss-text: #cbd5e1; --kb-btn-dismiss-hover-bg: #334155; --kb-btn-dismiss-hover-text: #ffffff; --kb-btn-trigger-bg: #ffffff; --kb-btn-trigger-text: #0f172a; --kb-btn-trigger-hover-bg: #f1f5f9; --kb-progress-track: #1e293b; --kb-progress-indicator: #ffffff; --kb-popover-bg: #0f172a; --kb-popover-border: #334155; --kb-popover-text: #f1f5f9; --kb-input-bg: #0f172a; --kb-input-border: #475569; --kb-input-text: #f1f5f9; --kb-input-placeholder: #94a3b8; --kb-form-label: #e2e8f0; --kb-success-color: #6ee7b7; --kb-kbd-bg: #1e293b; --kb-kbd-border: #334155; --kb-kbd-text: #94a3b8; --kb-watermark-text: #64748b; }
1131
+ :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.1); --kb-kbd-border: rgba(255, 255, 255, 0.2); --kb-kbd-text: rgba(255, 255, 255, 0.6); --kb-watermark-text: rgba(255, 255, 255, 0.4); font-family: var(--kb-font-family); }
1132
+ .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; }
1133
+ .theme-dark { --kb-bg-container: #0f172a; --kb-border-container: #334155; --kb-shadow-container: 0 25px 50px -12px rgba(0, 0, 0, 0.25); --kb-backdrop-blur: 0px; --kb-text-title: #f1f5f9; --kb-text-subtitle: #94a3b8; --kb-btn-dismiss-bg: #1e293b; --kb-btn-dismiss-border: #334155; --kb-btn-dismiss-text: #cbd5e1; --kb-btn-dismiss-hover-bg: #334155; --kb-btn-dismiss-hover-text: #ffffff; --kb-btn-trigger-bg: #ffffff; --kb-btn-trigger-text: #0f172a; --kb-btn-trigger-hover-bg: #f1f5f9; --kb-progress-track: #1e293b; --kb-progress-indicator: #ffffff; --kb-popover-bg: #0f172a; --kb-popover-border: #334155; --kb-popover-text: #f1f5f9; --kb-input-bg: #0f172a; --kb-input-border: #475569; --kb-input-text: #f1f5f9; --kb-input-placeholder: #94a3b8; --kb-form-label: #e2e8f0; --kb-success-color: #6ee7b7; --kb-error-text: #ef4444; --kb-kbd-bg: #1e293b; --kb-kbd-border: #334155; --kb-kbd-text: #94a3b8; --kb-watermark-text: #64748b; }
1134
1134
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
1135
1135
  #cue-card-root { position: fixed; z-index: 2147483647; pointer-events: none; width: 100%; height: 0; top: 0; left: 0; isolation: isolate; }
1136
1136
  .container { 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: 0.25rem; border-bottom-left-radius: 0.25rem; border-bottom-right-radius: 0.25rem; padding: 1rem; width: auto; min-width: 320px; max-width: 90vw; opacity: 0; z-index: 1; font-family: var(--kb-font-family); }
@@ -1176,6 +1176,9 @@ var KenobiLib = (() => {
1176
1176
  .form-input { flex: 1; background-color: var(--kb-input-bg); border: 1px solid var(--kb-input-border); color: var(--kb-input-text); border-radius: 0.375rem; padding: 0.5rem 0.75rem; font-size: 0.875rem; outline: none; transition: border-color 0.2s; min-height: 44px; }
1177
1177
  .form-input::placeholder { color: var(--kb-input-placeholder); }
1178
1178
  .form-input:focus { border-color: var(--kb-progress-indicator); }
1179
+ .form-input.error { border-color: var(--kb-error-text); }
1180
+ .form-label.error { color: var(--kb-error-text); }
1181
+ .form-message { color: var(--kb-error-text); font-size: 0.75rem; margin-top: 0.25rem; font-weight: 500; }
1179
1182
  .btn-submit { background-color: var(--kb-btn-trigger-bg); color: var(--kb-btn-trigger-text); padding: 0.5rem 1rem; display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; }
1180
1183
  .btn-submit:disabled { opacity: 0.7; cursor: not-allowed; }
1181
1184
  .kbd-hint { display: flex; align-items: center; gap: 0.5rem; position: absolute; top: calc(100% + 0.5rem); left: 50%; transform: translateX(-50%); opacity: 0; pointer-events: none; transition: opacity 0.2s ease; white-space: nowrap; }
@@ -1288,6 +1291,7 @@ var KenobiLib = (() => {
1288
1291
  const timerRafRef = A2(null);
1289
1292
  const hasTimerCompletedRef = A2(false);
1290
1293
  const [isHovering, setIsHovering] = d2(false);
1294
+ const [errors, setErrors] = d2({});
1291
1295
  const [timerProgress, setTimerProgressState] = d2(0);
1292
1296
  const timerProgressRef = A2(0);
1293
1297
  const timerEnabled = config.enableTimer !== false;
@@ -1508,17 +1512,81 @@ var KenobiLib = (() => {
1508
1512
  }
1509
1513
  return txt.btnResting || "Show me";
1510
1514
  })();
1515
+ const validateField = /* @__PURE__ */ __name((name, value, fieldConfig) => {
1516
+ if (!fieldConfig) return null;
1517
+ if (fieldConfig.required && !value.trim()) {
1518
+ return fieldConfig.errorMessage || `${fieldConfig.label} is required`;
1519
+ }
1520
+ if (!value) return null;
1521
+ if (fieldConfig.type === "email") {
1522
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1523
+ if (!emailRegex.test(value)) {
1524
+ return fieldConfig.errorMessage || "Please enter a valid email address";
1525
+ }
1526
+ }
1527
+ if (fieldConfig.type === "url") {
1528
+ try {
1529
+ new URL(value);
1530
+ } catch {
1531
+ return fieldConfig.errorMessage || "Please enter a valid URL (e.g., https://example.com)";
1532
+ }
1533
+ }
1534
+ if (fieldConfig.minLength && value.length < fieldConfig.minLength) {
1535
+ return fieldConfig.errorMessage || `Must be at least ${fieldConfig.minLength} characters`;
1536
+ }
1537
+ if (fieldConfig.maxLength && value.length > fieldConfig.maxLength) {
1538
+ return fieldConfig.errorMessage || `Must be at most ${fieldConfig.maxLength} characters`;
1539
+ }
1540
+ if (fieldConfig.pattern) {
1541
+ const regex = new RegExp(fieldConfig.pattern);
1542
+ if (!regex.test(value)) {
1543
+ return fieldConfig.errorMessage || "Invalid format";
1544
+ }
1545
+ }
1546
+ return null;
1547
+ }, "validateField");
1511
1548
  const handleSubmit = /* @__PURE__ */ __name((e3) => {
1512
1549
  e3.preventDefault();
1513
1550
  if (isPending) return;
1514
1551
  const formData = new FormData(e3.target);
1515
1552
  const values = {};
1553
+ const newErrors = {};
1554
+ let hasErrors = false;
1516
1555
  formData.forEach((value, key) => {
1517
- values[key] = value.toString();
1556
+ const strValue = value.toString();
1557
+ values[key] = strValue;
1558
+ const fieldConfig = config.fields.find((f4) => f4.name === key);
1559
+ const error = validateField(key, strValue, fieldConfig);
1560
+ if (error) {
1561
+ newErrors[key] = error;
1562
+ hasErrors = true;
1563
+ }
1518
1564
  });
1565
+ if (hasErrors) {
1566
+ setErrors(newErrors);
1567
+ return;
1568
+ }
1569
+ setErrors({});
1519
1570
  setIsOpen(false);
1520
1571
  config.onSubmitPersonalization?.(values);
1521
1572
  }, "handleSubmit");
1573
+ const handleInput = /* @__PURE__ */ __name((e3) => {
1574
+ const target = e3.target;
1575
+ const name = target.name;
1576
+ if (errors[name]) {
1577
+ const fieldConfig = config.fields.find((f4) => f4.name === name);
1578
+ const error = validateField(name, target.value, fieldConfig);
1579
+ setErrors((prev) => {
1580
+ const next = { ...prev };
1581
+ if (error) {
1582
+ next[name] = error;
1583
+ } else {
1584
+ delete next[name];
1585
+ }
1586
+ return next;
1587
+ });
1588
+ }
1589
+ }, "handleInput");
1522
1590
  const togglePopover = /* @__PURE__ */ __name((e3) => {
1523
1591
  e3.stopPropagation();
1524
1592
  if (isPending || isSuccess) return;
@@ -1659,17 +1727,24 @@ var KenobiLib = (() => {
1659
1727
  children: [
1660
1728
  /* @__PURE__ */ u3("form", { onSubmit: handleSubmit, ref: formRef, children: [
1661
1729
  config.fields.map((field, idx) => /* @__PURE__ */ u3("div", { class: "form-group", children: [
1662
- /* @__PURE__ */ u3("label", { class: "form-label", children: field.label }),
1730
+ /* @__PURE__ */ u3(
1731
+ "label",
1732
+ {
1733
+ class: `form-label ${errors[field.name] ? "error" : ""}`,
1734
+ children: field.label
1735
+ }
1736
+ ),
1663
1737
  /* @__PURE__ */ u3("div", { class: "input-row", children: [
1664
1738
  /* @__PURE__ */ u3(
1665
1739
  "input",
1666
1740
  {
1667
1741
  ref: idx === 0 ? firstInputRef : void 0,
1668
- class: "form-input",
1742
+ class: `form-input ${errors[field.name] ? "error" : ""}`,
1669
1743
  name: field.name,
1670
1744
  type: field.type || "text",
1671
1745
  placeholder: field.placeholder,
1672
- required: field.required
1746
+ required: field.required,
1747
+ onInput: handleInput
1673
1748
  }
1674
1749
  ),
1675
1750
  config.fields.length === 1 && /* @__PURE__ */ u3(
@@ -1681,7 +1756,8 @@ var KenobiLib = (() => {
1681
1756
  children: isPending ? /* @__PURE__ */ u3(LoaderIcon, {}) : /* @__PURE__ */ u3(ArrowRightIcon, {})
1682
1757
  }
1683
1758
  )
1684
- ] })
1759
+ ] }),
1760
+ errors[field.name] && /* @__PURE__ */ u3("div", { class: "form-message", children: errors[field.name] })
1685
1761
  ] }, field.name)),
1686
1762
  config.fields.length > 1 && /* @__PURE__ */ u3(
1687
1763
  "button",
@@ -1844,6 +1920,10 @@ var KenobiLib = (() => {
1844
1920
  setTheme(theme) {
1845
1921
  this.update({ theme });
1846
1922
  }
1923
+ openForm() {
1924
+ this.isOpen = true;
1925
+ this.render();
1926
+ }
1847
1927
  render() {
1848
1928
  const mountPoint = this.shadow.lastElementChild;
1849
1929
  G(
@@ -1875,7 +1955,9 @@ var KenobiLib = (() => {
1875
1955
  durationMs: 300,
1876
1956
  delayMs: 0,
1877
1957
  easing: "ease",
1878
- animateSize: true
1958
+ animateSize: true,
1959
+ overflow: "hidden"
1960
+ // Default to hidden for clean size animations; use "visible" for gradient text
1879
1961
  };
1880
1962
  var createNormalizedTransitionConfig = /* @__PURE__ */ __name((config) => ({
1881
1963
  enabled: config?.enabled ?? DEFAULT_TRANSITION_CONFIG.enabled,
@@ -1883,6 +1965,7 @@ var KenobiLib = (() => {
1883
1965
  delayMs: config?.delayMs ?? DEFAULT_TRANSITION_CONFIG.delayMs,
1884
1966
  easing: config?.easing ?? DEFAULT_TRANSITION_CONFIG.easing,
1885
1967
  animateSize: config?.animateSize ?? DEFAULT_TRANSITION_CONFIG.animateSize,
1968
+ overflow: config?.overflow ?? DEFAULT_TRANSITION_CONFIG.overflow,
1886
1969
  textAnimation: config?.textAnimation,
1887
1970
  enter: config?.enter,
1888
1971
  exit: config?.exit
@@ -1893,6 +1976,7 @@ var KenobiLib = (() => {
1893
1976
  delayMs: override?.delayMs ?? base.delayMs,
1894
1977
  easing: override?.easing ?? base.easing,
1895
1978
  animateSize: override?.animateSize ?? base.animateSize,
1979
+ overflow: override?.overflow ?? base.overflow,
1896
1980
  textAnimation: override?.textAnimation ?? base.textAnimation,
1897
1981
  enter: override?.enter ?? base.enter,
1898
1982
  exit: override?.exit ?? base.exit
@@ -1905,6 +1989,7 @@ var KenobiLib = (() => {
1905
1989
  return false;
1906
1990
  }, "isAnimatableElement");
1907
1991
  var isEasing = /* @__PURE__ */ __name((value) => typeof value === "string", "isEasing");
1992
+ var isTransitionOverflow = /* @__PURE__ */ __name((value) => value === "hidden" || value === "visible" || value === "clip", "isTransitionOverflow");
1908
1993
  var isAnimationSplitTarget = /* @__PURE__ */ __name((value) => value === "line" || value === "word" || value === "character", "isAnimationSplitTarget");
1909
1994
  var isKeyframeDefinition = /* @__PURE__ */ __name((value) => {
1910
1995
  if (typeof value !== "object" || value === null) return false;
@@ -1934,11 +2019,12 @@ var KenobiLib = (() => {
1934
2019
  delayMs,
1935
2020
  easing,
1936
2021
  animateSize,
2022
+ overflow,
1937
2023
  textAnimation,
1938
2024
  enter,
1939
2025
  exit
1940
2026
  } = candidate;
1941
- return (enabled === void 0 || typeof enabled === "boolean") && (durationMs === void 0 || typeof durationMs === "number") && (delayMs === void 0 || typeof delayMs === "number") && (easing === void 0 || isEasing(easing)) && (animateSize === void 0 || typeof animateSize === "boolean") && (textAnimation === void 0 || isTextAnimationConfig(textAnimation)) && (enter === void 0 || isAnimationSettings(enter)) && (exit === void 0 || isAnimationSettings(exit));
2027
+ return (enabled === void 0 || typeof enabled === "boolean") && (durationMs === void 0 || typeof durationMs === "number") && (delayMs === void 0 || typeof delayMs === "number") && (easing === void 0 || isEasing(easing)) && (animateSize === void 0 || typeof animateSize === "boolean") && (overflow === void 0 || isTransitionOverflow(overflow)) && (textAnimation === void 0 || isTextAnimationConfig(textAnimation)) && (enter === void 0 || isAnimationSettings(enter)) && (exit === void 0 || isAnimationSettings(exit));
1942
2028
  }, "isTransitionConfig");
1943
2029
  var _TransitionOrchestrator = class _TransitionOrchestrator {
1944
2030
  constructor() {
@@ -1959,7 +2045,7 @@ var KenobiLib = (() => {
1959
2045
  __publicField(this, "replaceWithTransition", /* @__PURE__ */ __name(async (oldElement, newMarkup, config) => {
1960
2046
  const wrapper = document.createElement("div");
1961
2047
  wrapper.style.position = "relative";
1962
- wrapper.style.overflow = "hidden";
2048
+ wrapper.style.overflow = config.overflow;
1963
2049
  const oldRect = oldElement.getBoundingClientRect();
1964
2050
  const oldStyle = window.getComputedStyle(oldElement);
1965
2051
  wrapper.style.display = oldStyle.display;
@@ -2147,17 +2233,70 @@ var KenobiLib = (() => {
2147
2233
  if (text.trim().length === 0) {
2148
2234
  return currentDelay;
2149
2235
  }
2236
+ if (config.by === "character") {
2237
+ const words = text.split(/(\s+)/);
2238
+ const fragment2 = document.createDocumentFragment();
2239
+ let delay2 = currentDelay;
2240
+ words.forEach((word) => {
2241
+ if (word.length === 0) return;
2242
+ if (word.trim().length === 0) {
2243
+ fragment2.appendChild(document.createTextNode(word));
2244
+ return;
2245
+ }
2246
+ const wordWrapper = document.createElement("span");
2247
+ wordWrapper.style.display = "inline-block";
2248
+ wordWrapper.style.whiteSpace = "nowrap";
2249
+ if (isBackgroundClippedToText2) {
2250
+ wordWrapper.style.setProperty("background", "inherit");
2251
+ wordWrapper.style.setProperty("-webkit-background-clip", "text");
2252
+ wordWrapper.style.setProperty("background-clip", "text");
2253
+ wordWrapper.style.setProperty("color", "transparent");
2254
+ }
2255
+ const chars = word.split("");
2256
+ chars.forEach((char) => {
2257
+ const span = document.createElement("span");
2258
+ span.textContent = char;
2259
+ span.style.display = "inline-block";
2260
+ span.style.opacity = "0";
2261
+ if (isBackgroundClippedToText2) {
2262
+ span.style.setProperty("background", "inherit");
2263
+ span.style.setProperty("-webkit-background-clip", "text");
2264
+ span.style.setProperty("background-clip", "text");
2265
+ span.style.setProperty("color", "transparent");
2266
+ span.style.paddingBottom = "0.1em";
2267
+ span.style.marginBottom = "-0.1em";
2268
+ }
2269
+ const keyframes = this.resolveKeyframes(config.keyframes);
2270
+ const duration = config.durationMs ?? transitionConfig.durationMs ?? 300;
2271
+ const easing = transitionConfig.easing ?? "ease";
2272
+ const animation = span.animate(keyframes, {
2273
+ duration,
2274
+ easing,
2275
+ delay: delay2,
2276
+ fill: "forwards"
2277
+ });
2278
+ animation.addEventListener("finish", () => {
2279
+ animation.commitStyles();
2280
+ animation.cancel();
2281
+ });
2282
+ wordWrapper.appendChild(span);
2283
+ delay2 += config.staggerMs ?? 50;
2284
+ });
2285
+ fragment2.appendChild(wordWrapper);
2286
+ });
2287
+ node.parentNode?.replaceChild(fragment2, node);
2288
+ return delay2;
2289
+ }
2150
2290
  let segments;
2151
2291
  switch (config.by) {
2152
- case "character":
2153
- segments = text.split("");
2154
- break;
2155
2292
  case "word":
2156
2293
  segments = text.split(/(\s+)/);
2157
2294
  break;
2158
2295
  case "line":
2159
2296
  segments = text.split("\n");
2160
2297
  break;
2298
+ default:
2299
+ segments = text.split("");
2161
2300
  }
2162
2301
  const fragment = document.createDocumentFragment();
2163
2302
  let delay = currentDelay;
@@ -2244,6 +2383,7 @@ var KenobiLib = (() => {
2244
2383
  __publicField(this, "changeStack", /* @__PURE__ */ new Map());
2245
2384
  __publicField(this, "visitorKey", null);
2246
2385
  __publicField(this, "orchestrator");
2386
+ __publicField(this, "cueCardInstance", null);
2247
2387
  //
2248
2388
  __publicField(this, "currentPath");
2249
2389
  __publicField(this, "currentUrl");
@@ -2831,6 +2971,122 @@ var KenobiLib = (() => {
2831
2971
  el.dispatchEvent(new Event("input", { bubbles: true }));
2832
2972
  el.dispatchEvent(new Event("change", { bubbles: true }));
2833
2973
  }, "setInputValue"));
2974
+ /*
2975
+ ----
2976
+ -- CUE CARD AUTO-MOUNT & PERSONALIZATION
2977
+ ----
2978
+ */
2979
+ /**
2980
+ * Initializes and mounts a CueCard with default configuration.
2981
+ * Called automatically when publishableKey is provided.
2982
+ *
2983
+ * Default config matches the template editor settings.
2984
+ * Future: This config will be fetched per-org from the database.
2985
+ */
2986
+ __publicField(this, "initCueCard", /* @__PURE__ */ __name(() => {
2987
+ if (this.cueCardInstance) {
2988
+ this.log("warn", "CueCard already initialized, skipping...");
2989
+ return;
2990
+ }
2991
+ this.cueCardInstance = new CueCard({
2992
+ theme: "light",
2993
+ position: "top-center",
2994
+ fields: [
2995
+ {
2996
+ name: "companyDomain",
2997
+ label: "Company Name or Domain",
2998
+ placeholder: "example.com",
2999
+ required: true,
3000
+ minLength: 3,
3001
+ errorMessage: "Company name or domain is required"
3002
+ }
3003
+ ],
3004
+ hasPersonalization: true,
3005
+ isVisible: true,
3006
+ enableFocusMode: true,
3007
+ showKeyboardHints: true,
3008
+ focusBlurIntensity: "8px",
3009
+ focusDelayMs: 2e3,
3010
+ enableLauncher: true,
3011
+ onSubmitPersonalization: /* @__PURE__ */ __name((values) => {
3012
+ this.log("debug", "CueCard submitted:", values);
3013
+ void this.personalize(values);
3014
+ }, "onSubmitPersonalization"),
3015
+ onOpenChange: /* @__PURE__ */ __name((isOpen) => {
3016
+ this.log("debug", "CueCard open state changed:", isOpen);
3017
+ }, "onOpenChange")
3018
+ });
3019
+ this.cueCardInstance.mount();
3020
+ this.log("debug", "CueCard mounted successfully");
3021
+ }, "initCueCard"));
3022
+ /**
3023
+ * Triggers personalization by calling the API with the provided input.
3024
+ * Updates CueCard status during the flow and applies transformations.
3025
+ *
3026
+ * @param input - User-provided fields (e.g., { companyDomain: "acme.com" })
3027
+ */
3028
+ __publicField(this, "personalize", /* @__PURE__ */ __name(async (input) => {
3029
+ if (!this.config.publishableKey) {
3030
+ this.log("error", "Cannot personalize: publishableKey not configured");
3031
+ return;
3032
+ }
3033
+ const startTime = /* @__PURE__ */ new Date();
3034
+ this.log("debug", "Starting personalization with input:", input);
3035
+ if (this.cueCardInstance) {
3036
+ this.cueCardInstance.update({ status: "starting", progressPct: 10 });
3037
+ }
3038
+ try {
3039
+ const response = await fetch(`${this.config.apiHost}/v1/personalize`, {
3040
+ method: "POST",
3041
+ headers: {
3042
+ "Content-Type": "application/json"
3043
+ },
3044
+ body: JSON.stringify({
3045
+ publishableKey: this.config.publishableKey,
3046
+ input,
3047
+ pagePath: this.currentPath
3048
+ })
3049
+ });
3050
+ if (this.cueCardInstance) {
3051
+ this.cueCardInstance.update({ progressPct: 50 });
3052
+ }
3053
+ if (!response.ok) {
3054
+ const errorBody = await response.json().catch(() => ({}));
3055
+ throw new Error(errorBody.error || `API error: ${response.status}`);
3056
+ }
3057
+ const result = await response.json();
3058
+ this.log("debug", "Personalization API response:", result);
3059
+ if (this.cueCardInstance) {
3060
+ this.cueCardInstance.update({ progressPct: 80 });
3061
+ }
3062
+ if (result.transformations && Array.isArray(result.transformations)) {
3063
+ await this.applyTransformations(result.transformations, {
3064
+ shouldConsolidate: true
3065
+ });
3066
+ }
3067
+ if (this.cueCardInstance) {
3068
+ this.cueCardInstance.update({ status: "success", progressPct: 100 });
3069
+ }
3070
+ const endTime = /* @__PURE__ */ new Date();
3071
+ const duration = this.getDurationInMilliseconds(startTime, endTime);
3072
+ this.log(
3073
+ "debug",
3074
+ `Personalization complete, took ${this.formatThousands(duration)} ms`
3075
+ );
3076
+ } catch (error) {
3077
+ this.log("error", "Personalization failed:", error);
3078
+ if (this.cueCardInstance) {
3079
+ this.cueCardInstance.update({ status: "error", progressPct: 0 });
3080
+ }
3081
+ }
3082
+ }, "personalize"));
3083
+ /**
3084
+ * Returns the CueCard instance if one was auto-mounted.
3085
+ * Useful for external control of the CueCard state.
3086
+ */
3087
+ __publicField(this, "getCueCard", /* @__PURE__ */ __name(() => {
3088
+ return this.cueCardInstance;
3089
+ }, "getCueCard"));
2834
3090
  this.config = {
2835
3091
  apiHost: "https://kenobi.ai/api",
2836
3092
  debug: false,
@@ -2858,6 +3114,10 @@ var KenobiLib = (() => {
2858
3114
  this.log("debug", "Auto-transform is requested... Starting...");
2859
3115
  void this.startAutoTransform();
2860
3116
  }
3117
+ if (this.config.publishableKey) {
3118
+ this.log("debug", "PublishableKey provided, auto-mounting CueCard...");
3119
+ this.initCueCard();
3120
+ }
2861
3121
  }
2862
3122
  };
2863
3123
  __name(_Kenobi, "Kenobi");