@perspective-ai/sdk 1.0.1 → 1.1.2

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/browser.cjs CHANGED
@@ -38,7 +38,7 @@ __export(browser_exports, {
38
38
  module.exports = __toCommonJS(browser_exports);
39
39
 
40
40
  // src/constants.ts
41
- var SDK_VERSION = "1.0.0";
41
+ var SDK_VERSION = "1.1.2";
42
42
  var FEATURES = {
43
43
  RESIZE: 1 << 0,
44
44
  // 0b0001
@@ -67,26 +67,9 @@ var BRAND_KEYS = {
67
67
  darkBg: "brand.dark.bg",
68
68
  darkText: "brand.dark.text"
69
69
  };
70
- var UTM_PARAMS = [
71
- "utm_source",
72
- "utm_medium",
73
- "utm_campaign",
74
- "utm_term",
75
- "utm_content"
76
- ];
77
70
  var RESERVED_PARAMS = /* @__PURE__ */ new Set([
78
- PARAM_KEYS.embed,
79
- PARAM_KEYS.embedType,
80
- PARAM_KEYS.theme,
81
- BRAND_KEYS.primary,
82
- BRAND_KEYS.secondary,
83
- BRAND_KEYS.bg,
84
- BRAND_KEYS.text,
85
- BRAND_KEYS.darkPrimary,
86
- BRAND_KEYS.darkSecondary,
87
- BRAND_KEYS.darkBg,
88
- BRAND_KEYS.darkText,
89
- ...UTM_PARAMS
71
+ ...Object.values(PARAM_KEYS),
72
+ ...Object.values(BRAND_KEYS)
90
73
  ]);
91
74
  var DATA_ATTRS = {
92
75
  widget: "data-perspective-widget",
@@ -101,7 +84,9 @@ var DATA_ATTRS = {
101
84
  brand: "data-perspective-brand",
102
85
  brandDark: "data-perspective-brand-dark",
103
86
  theme: "data-perspective-theme",
104
- noStyle: "data-perspective-no-style"
87
+ noStyle: "data-perspective-no-style",
88
+ autoOpen: "data-perspective-auto-open",
89
+ showOnce: "data-perspective-show-once"
105
90
  };
106
91
  var MESSAGE_TYPES = {
107
92
  // SDK -> Iframe (initialization)
@@ -135,9 +120,78 @@ var THEME_VALUES = {
135
120
  system: "system"
136
121
  };
137
122
  var STORAGE_KEYS = {
138
- anonId: "perspective-anon-id"
123
+ anonId: "perspective-anon-id",
124
+ triggerShown: "perspective-trigger-shown"
139
125
  };
140
126
 
127
+ // src/triggers.ts
128
+ function parseTriggerAttr(value) {
129
+ const trimmed = value.trim();
130
+ if (trimmed.startsWith("timeout:")) {
131
+ const delay = parseInt(trimmed.slice("timeout:".length), 10);
132
+ if (isNaN(delay) || delay < 0) {
133
+ throw new Error(`Invalid timeout delay: "${value}"`);
134
+ }
135
+ return { type: "timeout", delay };
136
+ }
137
+ if (trimmed === "timeout") {
138
+ return { type: "timeout", delay: 5e3 };
139
+ }
140
+ if (trimmed === "exit-intent") {
141
+ return { type: "exit-intent" };
142
+ }
143
+ throw new Error(
144
+ `Unknown trigger type: "${value}". Expected "timeout:<ms>" or "exit-intent".`
145
+ );
146
+ }
147
+ function setupTrigger(config, callback) {
148
+ if (config.type === "timeout") {
149
+ const timer = setTimeout(callback, config.delay);
150
+ return () => clearTimeout(timer);
151
+ }
152
+ if (config.type === "exit-intent") {
153
+ const handler = (e) => {
154
+ if (e.clientY <= 0) {
155
+ callback();
156
+ document.removeEventListener("mouseleave", handler);
157
+ }
158
+ };
159
+ document.addEventListener("mouseleave", handler);
160
+ return () => document.removeEventListener("mouseleave", handler);
161
+ }
162
+ const _exhaustive = config;
163
+ throw new Error(
164
+ `Unknown trigger type: ${_exhaustive.type}`
165
+ );
166
+ }
167
+ function storageKey(researchId) {
168
+ return `${STORAGE_KEYS.triggerShown}:${researchId}`;
169
+ }
170
+ function parseShowOnceAttr(value) {
171
+ if (!value) return "session";
172
+ const trimmed = value.trim();
173
+ if (trimmed === "visitor") return "visitor";
174
+ if (trimmed === "false") return false;
175
+ return "session";
176
+ }
177
+ function shouldShow(researchId, showOnce) {
178
+ if (showOnce === false) return true;
179
+ try {
180
+ const storage = showOnce === "visitor" ? localStorage : sessionStorage;
181
+ return storage.getItem(storageKey(researchId)) === null;
182
+ } catch {
183
+ return true;
184
+ }
185
+ }
186
+ function markShown(researchId, showOnce) {
187
+ if (showOnce === false) return;
188
+ try {
189
+ const storage = showOnce === "visitor" ? localStorage : sessionStorage;
190
+ storage.setItem(storageKey(researchId), "1");
191
+ } catch {
192
+ }
193
+ }
194
+
141
195
  // src/config.ts
142
196
  var DEFAULT_HOST = "https://getperspective.ai";
143
197
  var globalConfig = {};
@@ -262,13 +316,12 @@ function getOrCreateAnonId() {
262
316
  return crypto.randomUUID();
263
317
  }
264
318
  }
265
- function getUtmParams() {
319
+ function getParentSearchParams() {
266
320
  if (!hasDom()) return {};
267
321
  const params = {};
268
322
  const searchParams = new URLSearchParams(window.location.search);
269
- for (const key of UTM_PARAMS) {
270
- const value = searchParams.get(key);
271
- if (value) {
323
+ for (const [key, value] of searchParams.entries()) {
324
+ if (!RESERVED_PARAMS.has(key)) {
272
325
  params[key] = value;
273
326
  }
274
327
  }
@@ -291,8 +344,8 @@ function buildIframeUrl(researchId, type, host, customParams, brand, themeOverri
291
344
  } else {
292
345
  url.searchParams.set(PARAM_KEYS.theme, themeOverride || THEME_VALUES.light);
293
346
  }
294
- const utmParams = getUtmParams();
295
- for (const [key, value] of Object.entries(utmParams)) {
347
+ const parentParams = getParentSearchParams();
348
+ for (const [key, value] of Object.entries(parentParams)) {
296
349
  url.searchParams.set(key, value);
297
350
  }
298
351
  const setColor = (key, color) => {
@@ -1616,6 +1669,7 @@ function createFullpage(config) {
1616
1669
 
1617
1670
  // src/browser.ts
1618
1671
  var instances = /* @__PURE__ */ new Map();
1672
+ var triggerCleanups = /* @__PURE__ */ new Map();
1619
1673
  var configCache = /* @__PURE__ */ new Map();
1620
1674
  var styledButtons = /* @__PURE__ */ new Map();
1621
1675
  var buttonThemeMediaQuery = null;
@@ -1795,6 +1849,8 @@ function destroy(researchId) {
1795
1849
  function destroyAll() {
1796
1850
  instances.forEach((instance) => instance.unmount());
1797
1851
  instances.clear();
1852
+ triggerCleanups.forEach((cleanup) => cleanup());
1853
+ triggerCleanups.clear();
1798
1854
  styledButtons.clear();
1799
1855
  teardownButtonThemeListener();
1800
1856
  }
@@ -1821,9 +1877,29 @@ function autoInit() {
1821
1877
  if (el.hasAttribute("data-perspective-initialized")) return;
1822
1878
  el.setAttribute("data-perspective-initialized", "true");
1823
1879
  const researchId = el.getAttribute(DATA_ATTRS.popup);
1824
- if (researchId) {
1825
- const params = parseParamsAttr(el);
1826
- const brandConfig = extractBrandConfig(el);
1880
+ if (!researchId) return;
1881
+ const params = parseParamsAttr(el);
1882
+ const brandConfig = extractBrandConfig(el);
1883
+ const autoOpenAttr = el.getAttribute(DATA_ATTRS.autoOpen);
1884
+ if (autoOpenAttr) {
1885
+ try {
1886
+ const trigger = parseTriggerAttr(autoOpenAttr);
1887
+ const showOnce = parseShowOnceAttr(
1888
+ el.getAttribute(DATA_ATTRS.showOnce)
1889
+ );
1890
+ if (shouldShow(researchId, showOnce)) {
1891
+ triggerCleanups.get(researchId)?.();
1892
+ const cleanup = setupTrigger(trigger, () => {
1893
+ triggerCleanups.delete(researchId);
1894
+ markShown(researchId, showOnce);
1895
+ init({ researchId, type: "popup", params, ...brandConfig });
1896
+ });
1897
+ triggerCleanups.set(researchId, cleanup);
1898
+ }
1899
+ } catch (e) {
1900
+ console.warn("[Perspective]", e.message);
1901
+ }
1902
+ } else {
1827
1903
  styleButton(el, DEFAULT_THEME, brandConfig);
1828
1904
  el.addEventListener("click", (e) => {
1829
1905
  e.preventDefault();