@perspective-ai/sdk 1.1.0 → 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.1.0";
41
+ var SDK_VERSION = "1.1.2";
42
42
  var FEATURES = {
43
43
  RESIZE: 1 << 0,
44
44
  // 0b0001
@@ -84,7 +84,9 @@ var DATA_ATTRS = {
84
84
  brand: "data-perspective-brand",
85
85
  brandDark: "data-perspective-brand-dark",
86
86
  theme: "data-perspective-theme",
87
- noStyle: "data-perspective-no-style"
87
+ noStyle: "data-perspective-no-style",
88
+ autoOpen: "data-perspective-auto-open",
89
+ showOnce: "data-perspective-show-once"
88
90
  };
89
91
  var MESSAGE_TYPES = {
90
92
  // SDK -> Iframe (initialization)
@@ -118,9 +120,78 @@ var THEME_VALUES = {
118
120
  system: "system"
119
121
  };
120
122
  var STORAGE_KEYS = {
121
- anonId: "perspective-anon-id"
123
+ anonId: "perspective-anon-id",
124
+ triggerShown: "perspective-trigger-shown"
122
125
  };
123
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
+
124
195
  // src/config.ts
125
196
  var DEFAULT_HOST = "https://getperspective.ai";
126
197
  var globalConfig = {};
@@ -1598,6 +1669,7 @@ function createFullpage(config) {
1598
1669
 
1599
1670
  // src/browser.ts
1600
1671
  var instances = /* @__PURE__ */ new Map();
1672
+ var triggerCleanups = /* @__PURE__ */ new Map();
1601
1673
  var configCache = /* @__PURE__ */ new Map();
1602
1674
  var styledButtons = /* @__PURE__ */ new Map();
1603
1675
  var buttonThemeMediaQuery = null;
@@ -1777,6 +1849,8 @@ function destroy(researchId) {
1777
1849
  function destroyAll() {
1778
1850
  instances.forEach((instance) => instance.unmount());
1779
1851
  instances.clear();
1852
+ triggerCleanups.forEach((cleanup) => cleanup());
1853
+ triggerCleanups.clear();
1780
1854
  styledButtons.clear();
1781
1855
  teardownButtonThemeListener();
1782
1856
  }
@@ -1803,9 +1877,29 @@ function autoInit() {
1803
1877
  if (el.hasAttribute("data-perspective-initialized")) return;
1804
1878
  el.setAttribute("data-perspective-initialized", "true");
1805
1879
  const researchId = el.getAttribute(DATA_ATTRS.popup);
1806
- if (researchId) {
1807
- const params = parseParamsAttr(el);
1808
- 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 {
1809
1903
  styleButton(el, DEFAULT_THEME, brandConfig);
1810
1904
  el.addEventListener("click", (e) => {
1811
1905
  e.preventDefault();