@perspective-ai/sdk 1.0.0-alpha.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/README.md +333 -0
- package/dist/browser.cjs +1939 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +213 -0
- package/dist/browser.d.ts +213 -0
- package/dist/browser.js +1900 -0
- package/dist/browser.js.map +1 -0
- package/dist/cdn/perspective.global.js +406 -0
- package/dist/cdn/perspective.global.js.map +1 -0
- package/dist/constants.cjs +142 -0
- package/dist/constants.cjs.map +1 -0
- package/dist/constants.d.cts +104 -0
- package/dist/constants.d.ts +104 -0
- package/dist/constants.js +127 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.cjs +1596 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +1579 -0
- package/dist/index.js.map +1 -0
- package/package.json +83 -0
- package/src/browser.test.ts +388 -0
- package/src/browser.ts +509 -0
- package/src/config.test.ts +81 -0
- package/src/config.ts +95 -0
- package/src/constants.ts +214 -0
- package/src/float.test.ts +332 -0
- package/src/float.ts +231 -0
- package/src/fullpage.test.ts +224 -0
- package/src/fullpage.ts +126 -0
- package/src/iframe.test.ts +1037 -0
- package/src/iframe.ts +421 -0
- package/src/index.ts +61 -0
- package/src/loading.ts +90 -0
- package/src/popup.test.ts +344 -0
- package/src/popup.ts +157 -0
- package/src/slider.test.ts +277 -0
- package/src/slider.ts +158 -0
- package/src/styles.ts +395 -0
- package/src/types.ts +148 -0
- package/src/utils.test.ts +162 -0
- package/src/utils.ts +86 -0
- package/src/widget.test.ts +375 -0
- package/src/widget.ts +195 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1579 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
var DEFAULT_HOST = "https://getperspective.ai";
|
|
3
|
+
var globalConfig = {};
|
|
4
|
+
function configure(config) {
|
|
5
|
+
globalConfig = { ...globalConfig, ...config };
|
|
6
|
+
}
|
|
7
|
+
function getConfig() {
|
|
8
|
+
return { ...globalConfig };
|
|
9
|
+
}
|
|
10
|
+
function hasDom() {
|
|
11
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
12
|
+
}
|
|
13
|
+
function normalizeToOrigin(host) {
|
|
14
|
+
try {
|
|
15
|
+
return new URL(host).origin;
|
|
16
|
+
} catch {
|
|
17
|
+
return host;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function getHost(instanceHost) {
|
|
21
|
+
if (instanceHost) {
|
|
22
|
+
return normalizeToOrigin(instanceHost);
|
|
23
|
+
}
|
|
24
|
+
if (globalConfig.host) {
|
|
25
|
+
return normalizeToOrigin(globalConfig.host);
|
|
26
|
+
}
|
|
27
|
+
if (hasDom()) {
|
|
28
|
+
const scriptHost = getScriptHost();
|
|
29
|
+
if (scriptHost) {
|
|
30
|
+
return scriptHost;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return DEFAULT_HOST;
|
|
34
|
+
}
|
|
35
|
+
var capturedScriptHost = null;
|
|
36
|
+
function getScriptHost() {
|
|
37
|
+
if (capturedScriptHost !== null) {
|
|
38
|
+
return capturedScriptHost;
|
|
39
|
+
}
|
|
40
|
+
if (!hasDom()) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const currentScript = document.currentScript;
|
|
44
|
+
if (currentScript?.src) {
|
|
45
|
+
try {
|
|
46
|
+
capturedScriptHost = new URL(currentScript.src).origin;
|
|
47
|
+
return capturedScriptHost;
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
capturedScriptHost = "";
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (hasDom()) {
|
|
55
|
+
getScriptHost();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/constants.ts
|
|
59
|
+
var SDK_VERSION = "1.0.0";
|
|
60
|
+
var FEATURES = {
|
|
61
|
+
RESIZE: 1 << 0,
|
|
62
|
+
// 0b0001
|
|
63
|
+
THEME_SYNC: 1 << 1,
|
|
64
|
+
// 0b0010
|
|
65
|
+
ANON_ID: 1 << 2,
|
|
66
|
+
// 0b0100
|
|
67
|
+
SCROLLBAR_STYLES: 1 << 3
|
|
68
|
+
// 0b1000
|
|
69
|
+
};
|
|
70
|
+
var CURRENT_FEATURES = FEATURES.RESIZE | FEATURES.THEME_SYNC | FEATURES.ANON_ID | FEATURES.SCROLLBAR_STYLES;
|
|
71
|
+
var PARAM_KEYS = {
|
|
72
|
+
// User identification
|
|
73
|
+
email: "email",
|
|
74
|
+
name: "name",
|
|
75
|
+
// Navigation
|
|
76
|
+
returnUrl: "returnUrl",
|
|
77
|
+
// Interview behavior
|
|
78
|
+
voice: "voice",
|
|
79
|
+
scroll: "scroll",
|
|
80
|
+
hideProgress: "hideProgress",
|
|
81
|
+
hideGreeting: "hideGreeting",
|
|
82
|
+
hideBranding: "hideBranding",
|
|
83
|
+
// Interview mode & auth
|
|
84
|
+
mode: "mode",
|
|
85
|
+
invite: "invite",
|
|
86
|
+
// System (internal)
|
|
87
|
+
embed: "embed",
|
|
88
|
+
embedType: "embed_type",
|
|
89
|
+
theme: "theme"
|
|
90
|
+
};
|
|
91
|
+
var BRAND_KEYS = {
|
|
92
|
+
// Light mode
|
|
93
|
+
primary: "brand.primary",
|
|
94
|
+
secondary: "brand.secondary",
|
|
95
|
+
bg: "brand.bg",
|
|
96
|
+
text: "brand.text",
|
|
97
|
+
// Dark mode
|
|
98
|
+
darkPrimary: "brand.dark.primary",
|
|
99
|
+
darkSecondary: "brand.dark.secondary",
|
|
100
|
+
darkBg: "brand.dark.bg",
|
|
101
|
+
darkText: "brand.dark.text"
|
|
102
|
+
};
|
|
103
|
+
var UTM_PARAMS = [
|
|
104
|
+
"utm_source",
|
|
105
|
+
"utm_medium",
|
|
106
|
+
"utm_campaign",
|
|
107
|
+
"utm_term",
|
|
108
|
+
"utm_content"
|
|
109
|
+
];
|
|
110
|
+
var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
111
|
+
PARAM_KEYS.embed,
|
|
112
|
+
PARAM_KEYS.embedType,
|
|
113
|
+
PARAM_KEYS.theme,
|
|
114
|
+
BRAND_KEYS.primary,
|
|
115
|
+
BRAND_KEYS.secondary,
|
|
116
|
+
BRAND_KEYS.bg,
|
|
117
|
+
BRAND_KEYS.text,
|
|
118
|
+
BRAND_KEYS.darkPrimary,
|
|
119
|
+
BRAND_KEYS.darkSecondary,
|
|
120
|
+
BRAND_KEYS.darkBg,
|
|
121
|
+
BRAND_KEYS.darkText,
|
|
122
|
+
...UTM_PARAMS
|
|
123
|
+
]);
|
|
124
|
+
var DATA_ATTRS = {
|
|
125
|
+
widget: "data-perspective-widget",
|
|
126
|
+
popup: "data-perspective-popup",
|
|
127
|
+
slider: "data-perspective-slider",
|
|
128
|
+
float: "data-perspective-float",
|
|
129
|
+
// Primary name
|
|
130
|
+
chat: "data-perspective-chat",
|
|
131
|
+
// Legacy alias
|
|
132
|
+
fullpage: "data-perspective-fullpage",
|
|
133
|
+
params: "data-perspective-params",
|
|
134
|
+
brand: "data-perspective-brand",
|
|
135
|
+
brandDark: "data-perspective-brand-dark",
|
|
136
|
+
theme: "data-perspective-theme",
|
|
137
|
+
noStyle: "data-perspective-no-style"
|
|
138
|
+
};
|
|
139
|
+
var MESSAGE_TYPES = {
|
|
140
|
+
// SDK -> Iframe (initialization)
|
|
141
|
+
init: "perspective:init",
|
|
142
|
+
// Iframe -> SDK
|
|
143
|
+
ready: "perspective:ready",
|
|
144
|
+
resize: "perspective:resize",
|
|
145
|
+
submit: "perspective:submit",
|
|
146
|
+
close: "perspective:close",
|
|
147
|
+
error: "perspective:error",
|
|
148
|
+
redirect: "perspective:redirect",
|
|
149
|
+
// SDK -> Iframe (internal)
|
|
150
|
+
anonId: "perspective:anon-id",
|
|
151
|
+
injectStyles: "perspective:inject-styles",
|
|
152
|
+
themeChange: "perspective:theme-change",
|
|
153
|
+
// Iframe -> SDK (internal)
|
|
154
|
+
requestScrollbarStyles: "perspective:request-scrollbar-styles"
|
|
155
|
+
};
|
|
156
|
+
var PARAM_VALUES = {
|
|
157
|
+
true: "true"};
|
|
158
|
+
var THEME_VALUES = {
|
|
159
|
+
dark: "dark",
|
|
160
|
+
light: "light",
|
|
161
|
+
system: "system"
|
|
162
|
+
};
|
|
163
|
+
var STORAGE_KEYS = {
|
|
164
|
+
anonId: "perspective-anon-id"
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// src/utils.ts
|
|
168
|
+
function cn(...classes) {
|
|
169
|
+
return classes.map((c) => (c || "").split(" ")).flat().filter(Boolean).join(" ");
|
|
170
|
+
}
|
|
171
|
+
function getThemeClass(theme) {
|
|
172
|
+
return theme && theme !== THEME_VALUES.system ? `perspective-${theme}-theme` : void 0;
|
|
173
|
+
}
|
|
174
|
+
function resolveIsDark(theme) {
|
|
175
|
+
if (theme === THEME_VALUES.dark) return true;
|
|
176
|
+
if (theme === THEME_VALUES.light) return false;
|
|
177
|
+
if (!hasDom()) return false;
|
|
178
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
179
|
+
}
|
|
180
|
+
function resolveTheme(themeOverride) {
|
|
181
|
+
return resolveIsDark(themeOverride) ? "dark" : "light";
|
|
182
|
+
}
|
|
183
|
+
function normalizeHex(color) {
|
|
184
|
+
const trimmed = color.trim();
|
|
185
|
+
if (!trimmed) return void 0;
|
|
186
|
+
const normalized = trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
187
|
+
if (/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(normalized)) {
|
|
188
|
+
return normalized;
|
|
189
|
+
}
|
|
190
|
+
const hexChars = normalized.slice(1).replace(/[^0-9a-fA-F]/g, "");
|
|
191
|
+
if (hexChars.length >= 6) return `#${hexChars.slice(0, 6)}`;
|
|
192
|
+
if (hexChars.length >= 3) return `#${hexChars.slice(0, 3)}`;
|
|
193
|
+
return void 0;
|
|
194
|
+
}
|
|
195
|
+
function hexToRgba(hex, alpha) {
|
|
196
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
197
|
+
if (!result || !result[1] || !result[2] || !result[3]) {
|
|
198
|
+
return `rgba(118, 41, 200, ${alpha})`;
|
|
199
|
+
}
|
|
200
|
+
const r = parseInt(result[1], 16);
|
|
201
|
+
const g = parseInt(result[2], 16);
|
|
202
|
+
const b = parseInt(result[3], 16);
|
|
203
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/iframe.ts
|
|
207
|
+
function isAllowedRedirectUrl(url) {
|
|
208
|
+
if (!url || typeof url !== "string") return false;
|
|
209
|
+
try {
|
|
210
|
+
const parsed = new URL(url, window.location.origin);
|
|
211
|
+
const protocol = parsed.protocol.toLowerCase();
|
|
212
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
213
|
+
if (protocol === "https:") return true;
|
|
214
|
+
if (protocol === "http:" && (hostname === "localhost" || hostname === "127.0.0.1"))
|
|
215
|
+
return true;
|
|
216
|
+
return false;
|
|
217
|
+
} catch {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function getOrCreateAnonId() {
|
|
222
|
+
if (!hasDom()) return "";
|
|
223
|
+
try {
|
|
224
|
+
let id = localStorage.getItem(STORAGE_KEYS.anonId);
|
|
225
|
+
if (!id) {
|
|
226
|
+
id = crypto.randomUUID();
|
|
227
|
+
localStorage.setItem(STORAGE_KEYS.anonId, id);
|
|
228
|
+
}
|
|
229
|
+
return id;
|
|
230
|
+
} catch {
|
|
231
|
+
return crypto.randomUUID();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function getUtmParams() {
|
|
235
|
+
if (!hasDom()) return {};
|
|
236
|
+
const params = {};
|
|
237
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
238
|
+
for (const key of UTM_PARAMS) {
|
|
239
|
+
const value = searchParams.get(key);
|
|
240
|
+
if (value) {
|
|
241
|
+
params[key] = value;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return params;
|
|
245
|
+
}
|
|
246
|
+
function buildIframeUrl(researchId, type, host, customParams, brand, themeOverride) {
|
|
247
|
+
const url = new URL(`${host}/interview/${researchId}`);
|
|
248
|
+
url.searchParams.set(PARAM_KEYS.embed, PARAM_VALUES.true);
|
|
249
|
+
url.searchParams.set(PARAM_KEYS.embedType, type === "float" ? "chat" : type);
|
|
250
|
+
if (hasDom()) {
|
|
251
|
+
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
252
|
+
if (themeOverride && themeOverride !== THEME_VALUES.system) {
|
|
253
|
+
url.searchParams.set(PARAM_KEYS.theme, themeOverride);
|
|
254
|
+
} else {
|
|
255
|
+
url.searchParams.set(
|
|
256
|
+
PARAM_KEYS.theme,
|
|
257
|
+
isDark ? THEME_VALUES.dark : THEME_VALUES.light
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
url.searchParams.set(PARAM_KEYS.theme, themeOverride || THEME_VALUES.light);
|
|
262
|
+
}
|
|
263
|
+
const utmParams = getUtmParams();
|
|
264
|
+
for (const [key, value] of Object.entries(utmParams)) {
|
|
265
|
+
url.searchParams.set(key, value);
|
|
266
|
+
}
|
|
267
|
+
const setColor = (key, color) => {
|
|
268
|
+
if (!color) return;
|
|
269
|
+
const normalized = normalizeHex(color);
|
|
270
|
+
if (normalized) url.searchParams.set(key, normalized);
|
|
271
|
+
};
|
|
272
|
+
if (brand?.light) {
|
|
273
|
+
setColor(BRAND_KEYS.primary, brand.light.primary);
|
|
274
|
+
setColor(BRAND_KEYS.secondary, brand.light.secondary);
|
|
275
|
+
setColor(BRAND_KEYS.bg, brand.light.bg);
|
|
276
|
+
setColor(BRAND_KEYS.text, brand.light.text);
|
|
277
|
+
}
|
|
278
|
+
if (brand?.dark) {
|
|
279
|
+
setColor(BRAND_KEYS.darkPrimary, brand.dark.primary);
|
|
280
|
+
setColor(BRAND_KEYS.darkSecondary, brand.dark.secondary);
|
|
281
|
+
setColor(BRAND_KEYS.darkBg, brand.dark.bg);
|
|
282
|
+
setColor(BRAND_KEYS.darkText, brand.dark.text);
|
|
283
|
+
}
|
|
284
|
+
if (customParams) {
|
|
285
|
+
for (const [key, value] of Object.entries(customParams)) {
|
|
286
|
+
if (!RESERVED_PARAMS.has(key)) {
|
|
287
|
+
url.searchParams.set(key, value);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return url.toString();
|
|
292
|
+
}
|
|
293
|
+
function createIframe(researchId, type, host, params, brand, themeOverride) {
|
|
294
|
+
if (!hasDom()) {
|
|
295
|
+
return {};
|
|
296
|
+
}
|
|
297
|
+
const iframe = document.createElement("iframe");
|
|
298
|
+
iframe.src = buildIframeUrl(
|
|
299
|
+
researchId,
|
|
300
|
+
type,
|
|
301
|
+
host,
|
|
302
|
+
params,
|
|
303
|
+
brand,
|
|
304
|
+
themeOverride
|
|
305
|
+
);
|
|
306
|
+
iframe.setAttribute("allow", "microphone; camera");
|
|
307
|
+
iframe.setAttribute("allowfullscreen", "true");
|
|
308
|
+
iframe.setAttribute(
|
|
309
|
+
"sandbox",
|
|
310
|
+
"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation"
|
|
311
|
+
);
|
|
312
|
+
iframe.setAttribute("data-perspective", "true");
|
|
313
|
+
iframe.style.cssText = "border:none;";
|
|
314
|
+
return iframe;
|
|
315
|
+
}
|
|
316
|
+
function setupMessageListener(researchId, config, iframe, host, options) {
|
|
317
|
+
if (!hasDom()) {
|
|
318
|
+
return () => {
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const handler = (event) => {
|
|
322
|
+
if (event.origin !== host) return;
|
|
323
|
+
if (event.source !== iframe.contentWindow) return;
|
|
324
|
+
if (typeof event.data?.type !== "string") return;
|
|
325
|
+
if (!event.data.type.startsWith("perspective:")) return;
|
|
326
|
+
if (event.data.researchId !== researchId) return;
|
|
327
|
+
switch (event.data.type) {
|
|
328
|
+
case MESSAGE_TYPES.ready:
|
|
329
|
+
sendScrollbarStyles(iframe, host);
|
|
330
|
+
sendMessage(iframe, host, {
|
|
331
|
+
type: MESSAGE_TYPES.anonId,
|
|
332
|
+
anonId: getOrCreateAnonId()
|
|
333
|
+
});
|
|
334
|
+
sendMessage(iframe, host, {
|
|
335
|
+
type: MESSAGE_TYPES.init,
|
|
336
|
+
version: SDK_VERSION,
|
|
337
|
+
features: CURRENT_FEATURES,
|
|
338
|
+
researchId
|
|
339
|
+
});
|
|
340
|
+
config.onReady?.();
|
|
341
|
+
break;
|
|
342
|
+
case MESSAGE_TYPES.resize:
|
|
343
|
+
if (!options?.skipResize) {
|
|
344
|
+
iframe.style.height = `${event.data.height}px`;
|
|
345
|
+
}
|
|
346
|
+
break;
|
|
347
|
+
case MESSAGE_TYPES.submit:
|
|
348
|
+
config.onSubmit?.({ researchId });
|
|
349
|
+
break;
|
|
350
|
+
case MESSAGE_TYPES.close:
|
|
351
|
+
config.onClose?.();
|
|
352
|
+
break;
|
|
353
|
+
case MESSAGE_TYPES.error:
|
|
354
|
+
const error = new Error(
|
|
355
|
+
event.data.error
|
|
356
|
+
);
|
|
357
|
+
error.code = event.data.code || "UNKNOWN";
|
|
358
|
+
config.onError?.(error);
|
|
359
|
+
break;
|
|
360
|
+
case MESSAGE_TYPES.redirect:
|
|
361
|
+
const redirectUrl = event.data.url;
|
|
362
|
+
if (!isAllowedRedirectUrl(redirectUrl)) {
|
|
363
|
+
console.warn(
|
|
364
|
+
"[Perspective] Blocked unsafe redirect URL:",
|
|
365
|
+
redirectUrl
|
|
366
|
+
);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (config.onNavigate) {
|
|
370
|
+
config.onNavigate(redirectUrl);
|
|
371
|
+
} else {
|
|
372
|
+
window.location.href = redirectUrl;
|
|
373
|
+
}
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
window.addEventListener("message", handler);
|
|
378
|
+
return () => window.removeEventListener("message", handler);
|
|
379
|
+
}
|
|
380
|
+
function sendMessage(iframe, host, message) {
|
|
381
|
+
if (!hasDom()) return;
|
|
382
|
+
iframe.contentWindow?.postMessage(message, host);
|
|
383
|
+
}
|
|
384
|
+
var activeIframes = /* @__PURE__ */ new Map();
|
|
385
|
+
function registerIframe(iframe, host) {
|
|
386
|
+
activeIframes.set(iframe, host);
|
|
387
|
+
return () => {
|
|
388
|
+
activeIframes.delete(iframe);
|
|
389
|
+
if (activeIframes.size === 0) {
|
|
390
|
+
teardownGlobalListeners();
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function getScrollbarStyles() {
|
|
395
|
+
if (!hasDom()) return "";
|
|
396
|
+
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
397
|
+
const borderColor = isDark ? "hsl(217 33% 17%)" : "hsl(240 6% 90%)";
|
|
398
|
+
return `
|
|
399
|
+
* {
|
|
400
|
+
scrollbar-width: thin;
|
|
401
|
+
scrollbar-color: ${borderColor} transparent;
|
|
402
|
+
}
|
|
403
|
+
*::-webkit-scrollbar {
|
|
404
|
+
width: 10px;
|
|
405
|
+
height: 10px;
|
|
406
|
+
}
|
|
407
|
+
*::-webkit-scrollbar-track {
|
|
408
|
+
background: transparent;
|
|
409
|
+
}
|
|
410
|
+
*::-webkit-scrollbar-thumb {
|
|
411
|
+
background-color: ${borderColor};
|
|
412
|
+
border-radius: 9999px;
|
|
413
|
+
border: 2px solid transparent;
|
|
414
|
+
background-clip: padding-box;
|
|
415
|
+
}
|
|
416
|
+
*::-webkit-scrollbar-thumb:hover {
|
|
417
|
+
background-color: color-mix(in srgb, ${borderColor} 80%, currentColor);
|
|
418
|
+
}
|
|
419
|
+
`;
|
|
420
|
+
}
|
|
421
|
+
function sendScrollbarStyles(iframe, host) {
|
|
422
|
+
const styles = getScrollbarStyles();
|
|
423
|
+
sendMessage(iframe, host, {
|
|
424
|
+
type: MESSAGE_TYPES.injectStyles,
|
|
425
|
+
styles
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
function notifyThemeChange() {
|
|
429
|
+
if (!hasDom()) return;
|
|
430
|
+
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
431
|
+
activeIframes.forEach((host, iframe) => {
|
|
432
|
+
const message = {
|
|
433
|
+
type: MESSAGE_TYPES.themeChange,
|
|
434
|
+
theme: isDark ? THEME_VALUES.dark : THEME_VALUES.light
|
|
435
|
+
};
|
|
436
|
+
sendMessage(iframe, host, message);
|
|
437
|
+
sendScrollbarStyles(iframe, host);
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
var themeListener = null;
|
|
441
|
+
var themeMediaQuery = null;
|
|
442
|
+
var globalMessageHandler = null;
|
|
443
|
+
var globalListenersInitialized = false;
|
|
444
|
+
function setupThemeListener() {
|
|
445
|
+
if (themeListener || !hasDom()) return;
|
|
446
|
+
themeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
447
|
+
themeListener = () => notifyThemeChange();
|
|
448
|
+
themeMediaQuery.addEventListener("change", themeListener);
|
|
449
|
+
}
|
|
450
|
+
function teardownThemeListener() {
|
|
451
|
+
if (themeListener && themeMediaQuery) {
|
|
452
|
+
themeMediaQuery.removeEventListener("change", themeListener);
|
|
453
|
+
themeListener = null;
|
|
454
|
+
themeMediaQuery = null;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function setupGlobalListeners() {
|
|
458
|
+
if (!hasDom() || globalMessageHandler) return;
|
|
459
|
+
setupThemeListener();
|
|
460
|
+
globalMessageHandler = (event) => {
|
|
461
|
+
if (!event.data?.type?.startsWith("perspective:")) return;
|
|
462
|
+
if (event.data.type === MESSAGE_TYPES.requestScrollbarStyles) {
|
|
463
|
+
const iframes = Array.from(
|
|
464
|
+
document.querySelectorAll("iframe[data-perspective]")
|
|
465
|
+
);
|
|
466
|
+
const sourceIframe = iframes.find(
|
|
467
|
+
(iframe) => iframe.contentWindow === event.source
|
|
468
|
+
);
|
|
469
|
+
if (sourceIframe) {
|
|
470
|
+
const host = activeIframes.get(sourceIframe);
|
|
471
|
+
if (host && event.origin === host) {
|
|
472
|
+
sendScrollbarStyles(sourceIframe, host);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
window.addEventListener("message", globalMessageHandler);
|
|
478
|
+
}
|
|
479
|
+
function teardownGlobalListeners() {
|
|
480
|
+
if (globalMessageHandler) {
|
|
481
|
+
window.removeEventListener("message", globalMessageHandler);
|
|
482
|
+
globalMessageHandler = null;
|
|
483
|
+
}
|
|
484
|
+
teardownThemeListener();
|
|
485
|
+
globalListenersInitialized = false;
|
|
486
|
+
}
|
|
487
|
+
function ensureGlobalListeners() {
|
|
488
|
+
if (globalListenersInitialized) return;
|
|
489
|
+
globalListenersInitialized = true;
|
|
490
|
+
setupGlobalListeners();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// src/loading.ts
|
|
494
|
+
var DEFAULT_COLORS = {
|
|
495
|
+
light: {
|
|
496
|
+
bg: "#ffffff",
|
|
497
|
+
primary: "#7629C8"
|
|
498
|
+
},
|
|
499
|
+
dark: {
|
|
500
|
+
bg: "#02040a",
|
|
501
|
+
primary: "#B170FF"
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
function getLoadingColors(options) {
|
|
505
|
+
const theme = resolveTheme(options?.theme);
|
|
506
|
+
const isDark = theme === "dark";
|
|
507
|
+
const brandColors = isDark ? options?.brand?.dark : options?.brand?.light;
|
|
508
|
+
return {
|
|
509
|
+
bg: brandColors?.bg || (isDark ? DEFAULT_COLORS.dark.bg : DEFAULT_COLORS.light.bg),
|
|
510
|
+
primary: brandColors?.primary || (isDark ? DEFAULT_COLORS.dark.primary : DEFAULT_COLORS.light.primary)
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
function createLoadingIndicator(options) {
|
|
514
|
+
if (!hasDom()) {
|
|
515
|
+
return { remove: () => {
|
|
516
|
+
}, style: {} };
|
|
517
|
+
}
|
|
518
|
+
const colors = getLoadingColors(options);
|
|
519
|
+
const container = document.createElement("div");
|
|
520
|
+
container.className = "perspective-loading";
|
|
521
|
+
container.style.cssText = `
|
|
522
|
+
position: absolute;
|
|
523
|
+
top: 0;
|
|
524
|
+
left: 0;
|
|
525
|
+
right: 0;
|
|
526
|
+
bottom: 0;
|
|
527
|
+
display: flex;
|
|
528
|
+
align-items: center;
|
|
529
|
+
justify-content: center;
|
|
530
|
+
background: ${colors.bg};
|
|
531
|
+
transition: opacity 0.3s ease;
|
|
532
|
+
z-index: 1;
|
|
533
|
+
`;
|
|
534
|
+
const spinner = document.createElement("div");
|
|
535
|
+
spinner.style.cssText = `
|
|
536
|
+
width: 2.5rem;
|
|
537
|
+
height: 2.5rem;
|
|
538
|
+
border: 3px solid ${hexToRgba(colors.primary, 0.15)};
|
|
539
|
+
border-top-color: ${colors.primary};
|
|
540
|
+
border-radius: 50%;
|
|
541
|
+
animation: perspective-spin 0.8s linear infinite;
|
|
542
|
+
`;
|
|
543
|
+
container.appendChild(spinner);
|
|
544
|
+
return container;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/styles.ts
|
|
548
|
+
var stylesInjected = false;
|
|
549
|
+
var LIGHT_THEME = `
|
|
550
|
+
--perspective-overlay-bg: rgba(0, 0, 0, 0.5);
|
|
551
|
+
--perspective-modal-bg: #ffffff;
|
|
552
|
+
--perspective-modal-text: #151B23;
|
|
553
|
+
--perspective-close-bg: rgba(0, 0, 0, 0.1);
|
|
554
|
+
--perspective-close-text: #666666;
|
|
555
|
+
--perspective-close-hover-bg: rgba(0, 0, 0, 0.2);
|
|
556
|
+
--perspective-close-hover-text: #333333;
|
|
557
|
+
--perspective-border: hsl(240 6% 90%);
|
|
558
|
+
`;
|
|
559
|
+
var DARK_THEME = `
|
|
560
|
+
--perspective-overlay-bg: rgba(0, 0, 0, 0.7);
|
|
561
|
+
--perspective-modal-bg: #02040a;
|
|
562
|
+
--perspective-modal-text: #ffffff;
|
|
563
|
+
--perspective-close-bg: rgba(255, 255, 255, 0.1);
|
|
564
|
+
--perspective-close-text: #a0a0a0;
|
|
565
|
+
--perspective-close-hover-bg: rgba(255, 255, 255, 0.2);
|
|
566
|
+
--perspective-close-hover-text: #ffffff;
|
|
567
|
+
--perspective-border: hsl(217 33% 17%);
|
|
568
|
+
`;
|
|
569
|
+
function injectStyles() {
|
|
570
|
+
if (!hasDom()) return;
|
|
571
|
+
if (stylesInjected) return;
|
|
572
|
+
stylesInjected = true;
|
|
573
|
+
const style = document.createElement("style");
|
|
574
|
+
style.id = "perspective-embed-styles";
|
|
575
|
+
style.textContent = `
|
|
576
|
+
/* Theme-aware color variables */
|
|
577
|
+
.perspective-embed-root, .perspective-light-theme {
|
|
578
|
+
${LIGHT_THEME}
|
|
579
|
+
--perspective-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
580
|
+
--perspective-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
581
|
+
--perspective-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
582
|
+
--perspective-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
583
|
+
--perspective-radius: 1.2rem;
|
|
584
|
+
--perspective-radius-sm: calc(var(--perspective-radius) - 4px);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/* Dark theme */
|
|
588
|
+
.perspective-dark-theme {
|
|
589
|
+
${DARK_THEME}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/* System dark mode support */
|
|
593
|
+
@media (prefers-color-scheme: dark) {
|
|
594
|
+
.perspective-embed-root:not(.perspective-light-theme) {
|
|
595
|
+
${DARK_THEME}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/* Scrollbar styling */
|
|
600
|
+
.perspective-modal,
|
|
601
|
+
.perspective-slider,
|
|
602
|
+
.perspective-float-window,
|
|
603
|
+
.perspective-chat-window {
|
|
604
|
+
scrollbar-width: thin;
|
|
605
|
+
scrollbar-color: var(--perspective-border) transparent;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.perspective-modal::-webkit-scrollbar,
|
|
609
|
+
.perspective-slider::-webkit-scrollbar,
|
|
610
|
+
.perspective-float-window::-webkit-scrollbar,
|
|
611
|
+
.perspective-chat-window::-webkit-scrollbar {
|
|
612
|
+
width: 10px;
|
|
613
|
+
height: 10px;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.perspective-modal::-webkit-scrollbar-track,
|
|
617
|
+
.perspective-slider::-webkit-scrollbar-track,
|
|
618
|
+
.perspective-float-window::-webkit-scrollbar-track,
|
|
619
|
+
.perspective-chat-window::-webkit-scrollbar-track {
|
|
620
|
+
background: transparent;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.perspective-modal::-webkit-scrollbar-thumb,
|
|
624
|
+
.perspective-slider::-webkit-scrollbar-thumb,
|
|
625
|
+
.perspective-float-window::-webkit-scrollbar-thumb,
|
|
626
|
+
.perspective-chat-window::-webkit-scrollbar-thumb {
|
|
627
|
+
background-color: var(--perspective-border);
|
|
628
|
+
border-radius: 9999px;
|
|
629
|
+
border: 2px solid transparent;
|
|
630
|
+
background-clip: padding-box;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.perspective-modal::-webkit-scrollbar-thumb:hover,
|
|
634
|
+
.perspective-slider::-webkit-scrollbar-thumb:hover,
|
|
635
|
+
.perspective-float-window::-webkit-scrollbar-thumb:hover,
|
|
636
|
+
.perspective-chat-window::-webkit-scrollbar-thumb:hover {
|
|
637
|
+
background-color: color-mix(in srgb, var(--perspective-border) 80%, currentColor);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/* Overlay for popup/modal */
|
|
641
|
+
.perspective-overlay {
|
|
642
|
+
position: fixed;
|
|
643
|
+
inset: 0;
|
|
644
|
+
background: var(--perspective-overlay-bg);
|
|
645
|
+
display: flex;
|
|
646
|
+
align-items: center;
|
|
647
|
+
justify-content: center;
|
|
648
|
+
z-index: 9999;
|
|
649
|
+
animation: perspective-fade-in 0.2s ease-out;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
@keyframes perspective-fade-in {
|
|
653
|
+
from { opacity: 0; }
|
|
654
|
+
to { opacity: 1; }
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
@keyframes perspective-spin {
|
|
658
|
+
to { transform: rotate(360deg); }
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/* Modal container */
|
|
662
|
+
.perspective-modal {
|
|
663
|
+
position: relative;
|
|
664
|
+
width: 90%;
|
|
665
|
+
max-width: 600px;
|
|
666
|
+
height: 80vh;
|
|
667
|
+
max-height: 700px;
|
|
668
|
+
background: var(--perspective-modal-bg);
|
|
669
|
+
color: var(--perspective-modal-text);
|
|
670
|
+
border-radius: var(--perspective-radius);
|
|
671
|
+
overflow: hidden;
|
|
672
|
+
box-shadow: var(--perspective-shadow-xl);
|
|
673
|
+
animation: perspective-slide-up 0.3s ease-out;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
@keyframes perspective-slide-up {
|
|
677
|
+
from {
|
|
678
|
+
opacity: 0;
|
|
679
|
+
transform: translateY(20px) scale(0.95);
|
|
680
|
+
}
|
|
681
|
+
to {
|
|
682
|
+
opacity: 1;
|
|
683
|
+
transform: translateY(0) scale(1);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.perspective-modal iframe {
|
|
688
|
+
width: 100%;
|
|
689
|
+
height: 100%;
|
|
690
|
+
border: none;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/* Close button */
|
|
694
|
+
.perspective-close {
|
|
695
|
+
position: absolute;
|
|
696
|
+
top: 1rem;
|
|
697
|
+
right: 1.5rem;
|
|
698
|
+
width: 2rem;
|
|
699
|
+
height: 2rem;
|
|
700
|
+
border: none;
|
|
701
|
+
background: var(--perspective-close-bg);
|
|
702
|
+
color: var(--perspective-close-text);
|
|
703
|
+
border-radius: 50%;
|
|
704
|
+
cursor: pointer;
|
|
705
|
+
display: flex;
|
|
706
|
+
align-items: center;
|
|
707
|
+
justify-content: center;
|
|
708
|
+
font-size: 1rem;
|
|
709
|
+
z-index: 10;
|
|
710
|
+
transition: background-color 0.2s ease, color 0.2s ease;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.perspective-close:hover {
|
|
714
|
+
background: var(--perspective-close-hover-bg);
|
|
715
|
+
color: var(--perspective-close-hover-text);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.perspective-close:focus-visible {
|
|
719
|
+
outline: 2px solid currentColor;
|
|
720
|
+
outline-offset: 2px;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.perspective-close svg {
|
|
724
|
+
width: 1rem;
|
|
725
|
+
height: 1rem;
|
|
726
|
+
stroke-width: 2;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/* Slider drawer */
|
|
730
|
+
.perspective-slider {
|
|
731
|
+
position: fixed;
|
|
732
|
+
top: 0;
|
|
733
|
+
right: 0;
|
|
734
|
+
width: 100%;
|
|
735
|
+
max-width: 450px;
|
|
736
|
+
height: 100%;
|
|
737
|
+
background: var(--perspective-modal-bg);
|
|
738
|
+
color: var(--perspective-modal-text);
|
|
739
|
+
box-shadow: var(--perspective-shadow-xl);
|
|
740
|
+
z-index: 9999;
|
|
741
|
+
animation: perspective-slide-in 0.3s ease-out;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
@keyframes perspective-slide-in {
|
|
745
|
+
from { transform: translateX(100%); }
|
|
746
|
+
to { transform: translateX(0); }
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.perspective-slider iframe {
|
|
750
|
+
width: 100%;
|
|
751
|
+
height: 100%;
|
|
752
|
+
border: none;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.perspective-slider .perspective-close {
|
|
756
|
+
top: 1rem;
|
|
757
|
+
right: 2rem;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/* Slider backdrop */
|
|
761
|
+
.perspective-slider-backdrop {
|
|
762
|
+
position: fixed;
|
|
763
|
+
inset: 0;
|
|
764
|
+
background: var(--perspective-overlay-bg);
|
|
765
|
+
z-index: 9998;
|
|
766
|
+
animation: perspective-fade-in 0.2s ease-out;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/* Float bubble (and legacy chat-bubble alias) */
|
|
770
|
+
.perspective-float-bubble,
|
|
771
|
+
.perspective-chat-bubble {
|
|
772
|
+
position: fixed;
|
|
773
|
+
bottom: 1.5rem;
|
|
774
|
+
right: 1.5rem;
|
|
775
|
+
width: 3.75rem;
|
|
776
|
+
height: 3.75rem;
|
|
777
|
+
border-radius: 50%;
|
|
778
|
+
background: var(--perspective-float-bg, var(--perspective-chat-bg, #7629C8));
|
|
779
|
+
color: white;
|
|
780
|
+
border: none;
|
|
781
|
+
cursor: pointer;
|
|
782
|
+
box-shadow: var(--perspective-float-shadow, var(--perspective-chat-shadow, 0 4px 12px rgba(118, 41, 200, 0.4)));
|
|
783
|
+
z-index: 9996;
|
|
784
|
+
display: flex;
|
|
785
|
+
align-items: center;
|
|
786
|
+
justify-content: center;
|
|
787
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
.perspective-float-bubble:hover,
|
|
791
|
+
.perspective-chat-bubble:hover {
|
|
792
|
+
transform: scale(1.05);
|
|
793
|
+
box-shadow: var(--perspective-float-shadow-hover, var(--perspective-chat-shadow-hover, 0 6px 16px rgba(118, 41, 200, 0.5)));
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.perspective-float-bubble:focus-visible,
|
|
797
|
+
.perspective-chat-bubble:focus-visible {
|
|
798
|
+
outline: 2px solid currentColor;
|
|
799
|
+
outline-offset: 2px;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
.perspective-float-bubble svg,
|
|
803
|
+
.perspective-chat-bubble svg {
|
|
804
|
+
width: 1.75rem;
|
|
805
|
+
height: 1.75rem;
|
|
806
|
+
stroke-width: 2;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/* Float window (and legacy chat-window alias) */
|
|
810
|
+
.perspective-float-window,
|
|
811
|
+
.perspective-chat-window {
|
|
812
|
+
position: fixed;
|
|
813
|
+
bottom: 6.25rem;
|
|
814
|
+
right: 1.5rem;
|
|
815
|
+
width: 380px;
|
|
816
|
+
height: calc(100vh - 8.75rem);
|
|
817
|
+
max-height: 600px;
|
|
818
|
+
background: var(--perspective-modal-bg);
|
|
819
|
+
color: var(--perspective-modal-text);
|
|
820
|
+
border-radius: var(--perspective-radius);
|
|
821
|
+
overflow: hidden;
|
|
822
|
+
box-shadow: var(--perspective-shadow-xl);
|
|
823
|
+
z-index: 9997;
|
|
824
|
+
animation: perspective-float-open 0.3s ease-out;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
@keyframes perspective-float-open {
|
|
828
|
+
from {
|
|
829
|
+
opacity: 0;
|
|
830
|
+
transform: translateY(20px) scale(0.9);
|
|
831
|
+
}
|
|
832
|
+
to {
|
|
833
|
+
opacity: 1;
|
|
834
|
+
transform: translateY(0) scale(1);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
.perspective-float-window iframe,
|
|
839
|
+
.perspective-chat-window iframe {
|
|
840
|
+
width: 100%;
|
|
841
|
+
height: 100%;
|
|
842
|
+
border: none;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
.perspective-float-window .perspective-close,
|
|
846
|
+
.perspective-chat-window .perspective-close {
|
|
847
|
+
top: 1rem;
|
|
848
|
+
right: 1.5rem;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/* Fullpage */
|
|
852
|
+
.perspective-fullpage {
|
|
853
|
+
position: fixed;
|
|
854
|
+
inset: 0;
|
|
855
|
+
z-index: 9999;
|
|
856
|
+
background: var(--perspective-modal-bg);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
.perspective-fullpage iframe {
|
|
860
|
+
width: 100%;
|
|
861
|
+
height: 100%;
|
|
862
|
+
border: none;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/* Responsive */
|
|
866
|
+
@media (max-width: 640px) {
|
|
867
|
+
.perspective-modal {
|
|
868
|
+
width: 100%;
|
|
869
|
+
height: 100%;
|
|
870
|
+
max-width: none;
|
|
871
|
+
max-height: none;
|
|
872
|
+
border-radius: 0;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.perspective-slider {
|
|
876
|
+
max-width: 100%;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.perspective-float-window,
|
|
880
|
+
.perspective-chat-window {
|
|
881
|
+
width: calc(100% - 2rem);
|
|
882
|
+
right: 1rem;
|
|
883
|
+
bottom: 5.625rem;
|
|
884
|
+
height: calc(100vh - 7.5rem);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.perspective-float-bubble,
|
|
888
|
+
.perspective-chat-bubble {
|
|
889
|
+
bottom: 1rem;
|
|
890
|
+
right: 1rem;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
@media (max-width: 450px) {
|
|
895
|
+
.perspective-float-window,
|
|
896
|
+
.perspective-chat-window {
|
|
897
|
+
width: calc(100% - 1rem);
|
|
898
|
+
right: 0.5rem;
|
|
899
|
+
bottom: 5rem;
|
|
900
|
+
height: calc(100vh - 6.5rem);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
.perspective-float-bubble,
|
|
904
|
+
.perspective-chat-bubble {
|
|
905
|
+
bottom: 0.75rem;
|
|
906
|
+
right: 0.75rem;
|
|
907
|
+
width: 3.5rem;
|
|
908
|
+
height: 3.5rem;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.perspective-float-bubble svg,
|
|
912
|
+
.perspective-chat-bubble svg {
|
|
913
|
+
width: 1.5rem;
|
|
914
|
+
height: 1.5rem;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
`;
|
|
918
|
+
document.head.appendChild(style);
|
|
919
|
+
}
|
|
920
|
+
var MIC_ICON = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
921
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 18.75a6 6 0 006-6v-1.5m-6 7.5a6 6 0 01-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 01-3-3V4.5a3 3 0 116 0v8.25a3 3 0 01-3 3z" />
|
|
922
|
+
</svg>`;
|
|
923
|
+
var CLOSE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
924
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
925
|
+
</svg>`;
|
|
926
|
+
|
|
927
|
+
// src/widget.ts
|
|
928
|
+
var widgetResources = /* @__PURE__ */ new WeakMap();
|
|
929
|
+
function createNoOpHandle(researchId, type) {
|
|
930
|
+
return {
|
|
931
|
+
unmount: () => {
|
|
932
|
+
},
|
|
933
|
+
update: () => {
|
|
934
|
+
},
|
|
935
|
+
destroy: () => {
|
|
936
|
+
},
|
|
937
|
+
researchId,
|
|
938
|
+
type,
|
|
939
|
+
iframe: null,
|
|
940
|
+
container: null
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
function createExistingWidgetHandle(container, researchId) {
|
|
944
|
+
const existingWrapper = container.querySelector(
|
|
945
|
+
".perspective-embed-root"
|
|
946
|
+
);
|
|
947
|
+
const existingIframe = container.querySelector(
|
|
948
|
+
"iframe[data-perspective]"
|
|
949
|
+
);
|
|
950
|
+
let destroyed = false;
|
|
951
|
+
const unmount = () => {
|
|
952
|
+
if (destroyed) return;
|
|
953
|
+
destroyed = true;
|
|
954
|
+
if (existingIframe) {
|
|
955
|
+
const resources = widgetResources.get(existingIframe);
|
|
956
|
+
if (resources) {
|
|
957
|
+
resources.cleanup();
|
|
958
|
+
resources.unregister();
|
|
959
|
+
widgetResources.delete(existingIframe);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
existingWrapper?.remove();
|
|
963
|
+
};
|
|
964
|
+
return {
|
|
965
|
+
unmount,
|
|
966
|
+
update: () => {
|
|
967
|
+
},
|
|
968
|
+
destroy: unmount,
|
|
969
|
+
researchId,
|
|
970
|
+
type: "widget",
|
|
971
|
+
iframe: existingIframe,
|
|
972
|
+
container
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
function createWidget(container, config) {
|
|
976
|
+
const { researchId } = config;
|
|
977
|
+
if (!hasDom() || !container) {
|
|
978
|
+
return createNoOpHandle(researchId, "widget");
|
|
979
|
+
}
|
|
980
|
+
if (container.querySelector("iframe[data-perspective]")) {
|
|
981
|
+
return createExistingWidgetHandle(container, researchId);
|
|
982
|
+
}
|
|
983
|
+
const host = getHost(config.host);
|
|
984
|
+
injectStyles();
|
|
985
|
+
ensureGlobalListeners();
|
|
986
|
+
const wrapper = document.createElement("div");
|
|
987
|
+
wrapper.className = cn("perspective-embed-root", getThemeClass(config.theme));
|
|
988
|
+
wrapper.style.cssText = "position:relative;width:100%;height:100%;min-height:500px;";
|
|
989
|
+
const loading = createLoadingIndicator({
|
|
990
|
+
theme: config.theme,
|
|
991
|
+
brand: config.brand
|
|
992
|
+
});
|
|
993
|
+
wrapper.appendChild(loading);
|
|
994
|
+
const iframe = createIframe(
|
|
995
|
+
researchId,
|
|
996
|
+
"widget",
|
|
997
|
+
host,
|
|
998
|
+
config.params,
|
|
999
|
+
config.brand,
|
|
1000
|
+
config.theme
|
|
1001
|
+
);
|
|
1002
|
+
iframe.style.width = "100%";
|
|
1003
|
+
iframe.style.height = "100%";
|
|
1004
|
+
iframe.style.minHeight = "500px";
|
|
1005
|
+
iframe.style.opacity = "0";
|
|
1006
|
+
iframe.style.transition = "opacity 0.3s ease";
|
|
1007
|
+
wrapper.appendChild(iframe);
|
|
1008
|
+
container.appendChild(wrapper);
|
|
1009
|
+
let currentConfig = { ...config };
|
|
1010
|
+
const cleanup = setupMessageListener(
|
|
1011
|
+
researchId,
|
|
1012
|
+
{
|
|
1013
|
+
get onReady() {
|
|
1014
|
+
return () => {
|
|
1015
|
+
loading.style.opacity = "0";
|
|
1016
|
+
iframe.style.opacity = "1";
|
|
1017
|
+
setTimeout(() => loading.remove(), 300);
|
|
1018
|
+
currentConfig.onReady?.();
|
|
1019
|
+
};
|
|
1020
|
+
},
|
|
1021
|
+
get onSubmit() {
|
|
1022
|
+
return currentConfig.onSubmit;
|
|
1023
|
+
},
|
|
1024
|
+
get onNavigate() {
|
|
1025
|
+
return currentConfig.onNavigate;
|
|
1026
|
+
},
|
|
1027
|
+
get onClose() {
|
|
1028
|
+
return currentConfig.onClose;
|
|
1029
|
+
},
|
|
1030
|
+
get onError() {
|
|
1031
|
+
return currentConfig.onError;
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
iframe,
|
|
1035
|
+
host,
|
|
1036
|
+
{ skipResize: true }
|
|
1037
|
+
);
|
|
1038
|
+
const unregisterIframe = registerIframe(iframe, host);
|
|
1039
|
+
widgetResources.set(iframe, {
|
|
1040
|
+
cleanup,
|
|
1041
|
+
unregister: unregisterIframe,
|
|
1042
|
+
wrapper
|
|
1043
|
+
});
|
|
1044
|
+
let destroyed = false;
|
|
1045
|
+
const unmount = () => {
|
|
1046
|
+
if (destroyed) return;
|
|
1047
|
+
destroyed = true;
|
|
1048
|
+
cleanup();
|
|
1049
|
+
unregisterIframe();
|
|
1050
|
+
widgetResources.delete(iframe);
|
|
1051
|
+
wrapper.remove();
|
|
1052
|
+
};
|
|
1053
|
+
return {
|
|
1054
|
+
unmount,
|
|
1055
|
+
update: (options) => {
|
|
1056
|
+
currentConfig = { ...currentConfig, ...options };
|
|
1057
|
+
},
|
|
1058
|
+
destroy: unmount,
|
|
1059
|
+
researchId,
|
|
1060
|
+
type: "widget",
|
|
1061
|
+
iframe,
|
|
1062
|
+
container
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// src/popup.ts
|
|
1067
|
+
function createNoOpHandle2(researchId) {
|
|
1068
|
+
return {
|
|
1069
|
+
unmount: () => {
|
|
1070
|
+
},
|
|
1071
|
+
update: () => {
|
|
1072
|
+
},
|
|
1073
|
+
destroy: () => {
|
|
1074
|
+
},
|
|
1075
|
+
researchId,
|
|
1076
|
+
type: "popup",
|
|
1077
|
+
iframe: null,
|
|
1078
|
+
container: null
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
function openPopup(config) {
|
|
1082
|
+
const { researchId } = config;
|
|
1083
|
+
if (!hasDom()) {
|
|
1084
|
+
return createNoOpHandle2(researchId);
|
|
1085
|
+
}
|
|
1086
|
+
const host = getHost(config.host);
|
|
1087
|
+
injectStyles();
|
|
1088
|
+
ensureGlobalListeners();
|
|
1089
|
+
const overlay = document.createElement("div");
|
|
1090
|
+
overlay.className = cn(
|
|
1091
|
+
"perspective-overlay perspective-embed-root",
|
|
1092
|
+
getThemeClass(config.theme)
|
|
1093
|
+
);
|
|
1094
|
+
const modal = document.createElement("div");
|
|
1095
|
+
modal.className = "perspective-modal";
|
|
1096
|
+
const closeBtn = document.createElement("button");
|
|
1097
|
+
closeBtn.className = "perspective-close";
|
|
1098
|
+
closeBtn.innerHTML = CLOSE_ICON;
|
|
1099
|
+
closeBtn.setAttribute("aria-label", "Close");
|
|
1100
|
+
const loading = createLoadingIndicator({
|
|
1101
|
+
theme: config.theme,
|
|
1102
|
+
brand: config.brand
|
|
1103
|
+
});
|
|
1104
|
+
loading.style.borderRadius = "16px";
|
|
1105
|
+
const iframe = createIframe(
|
|
1106
|
+
researchId,
|
|
1107
|
+
"popup",
|
|
1108
|
+
host,
|
|
1109
|
+
config.params,
|
|
1110
|
+
config.brand,
|
|
1111
|
+
config.theme
|
|
1112
|
+
);
|
|
1113
|
+
iframe.style.opacity = "0";
|
|
1114
|
+
iframe.style.transition = "opacity 0.3s ease";
|
|
1115
|
+
modal.appendChild(closeBtn);
|
|
1116
|
+
modal.appendChild(loading);
|
|
1117
|
+
modal.appendChild(iframe);
|
|
1118
|
+
overlay.appendChild(modal);
|
|
1119
|
+
document.body.appendChild(overlay);
|
|
1120
|
+
let currentConfig = { ...config };
|
|
1121
|
+
let isOpen = true;
|
|
1122
|
+
let messageCleanup = null;
|
|
1123
|
+
const unregisterIframe = registerIframe(iframe, host);
|
|
1124
|
+
const destroy = () => {
|
|
1125
|
+
if (!isOpen) return;
|
|
1126
|
+
isOpen = false;
|
|
1127
|
+
messageCleanup?.();
|
|
1128
|
+
unregisterIframe();
|
|
1129
|
+
overlay.remove();
|
|
1130
|
+
document.removeEventListener("keydown", escHandler);
|
|
1131
|
+
currentConfig.onClose?.();
|
|
1132
|
+
};
|
|
1133
|
+
messageCleanup = setupMessageListener(
|
|
1134
|
+
researchId,
|
|
1135
|
+
{
|
|
1136
|
+
get onReady() {
|
|
1137
|
+
return () => {
|
|
1138
|
+
loading.style.opacity = "0";
|
|
1139
|
+
iframe.style.opacity = "1";
|
|
1140
|
+
setTimeout(() => loading.remove(), 300);
|
|
1141
|
+
currentConfig.onReady?.();
|
|
1142
|
+
};
|
|
1143
|
+
},
|
|
1144
|
+
get onSubmit() {
|
|
1145
|
+
return currentConfig.onSubmit;
|
|
1146
|
+
},
|
|
1147
|
+
get onNavigate() {
|
|
1148
|
+
return currentConfig.onNavigate;
|
|
1149
|
+
},
|
|
1150
|
+
get onClose() {
|
|
1151
|
+
return destroy;
|
|
1152
|
+
},
|
|
1153
|
+
get onError() {
|
|
1154
|
+
return currentConfig.onError;
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
iframe,
|
|
1158
|
+
host,
|
|
1159
|
+
{ skipResize: true }
|
|
1160
|
+
);
|
|
1161
|
+
closeBtn.addEventListener("click", destroy);
|
|
1162
|
+
overlay.addEventListener("click", (e) => {
|
|
1163
|
+
if (e.target === overlay) destroy();
|
|
1164
|
+
});
|
|
1165
|
+
const escHandler = (e) => {
|
|
1166
|
+
if (e.key === "Escape") {
|
|
1167
|
+
destroy();
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
document.addEventListener("keydown", escHandler);
|
|
1171
|
+
return {
|
|
1172
|
+
unmount: destroy,
|
|
1173
|
+
update: (options) => {
|
|
1174
|
+
currentConfig = { ...currentConfig, ...options };
|
|
1175
|
+
},
|
|
1176
|
+
destroy,
|
|
1177
|
+
researchId,
|
|
1178
|
+
type: "popup",
|
|
1179
|
+
iframe,
|
|
1180
|
+
container: overlay
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// src/slider.ts
|
|
1185
|
+
function createNoOpHandle3(researchId) {
|
|
1186
|
+
return {
|
|
1187
|
+
unmount: () => {
|
|
1188
|
+
},
|
|
1189
|
+
update: () => {
|
|
1190
|
+
},
|
|
1191
|
+
destroy: () => {
|
|
1192
|
+
},
|
|
1193
|
+
researchId,
|
|
1194
|
+
type: "slider",
|
|
1195
|
+
iframe: null,
|
|
1196
|
+
container: null
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
function openSlider(config) {
|
|
1200
|
+
const { researchId } = config;
|
|
1201
|
+
if (!hasDom()) {
|
|
1202
|
+
return createNoOpHandle3(researchId);
|
|
1203
|
+
}
|
|
1204
|
+
const host = getHost(config.host);
|
|
1205
|
+
injectStyles();
|
|
1206
|
+
ensureGlobalListeners();
|
|
1207
|
+
const backdrop = document.createElement("div");
|
|
1208
|
+
backdrop.className = cn(
|
|
1209
|
+
"perspective-slider-backdrop perspective-embed-root",
|
|
1210
|
+
getThemeClass(config.theme)
|
|
1211
|
+
);
|
|
1212
|
+
const slider = document.createElement("div");
|
|
1213
|
+
slider.className = cn(
|
|
1214
|
+
"perspective-slider perspective-embed-root",
|
|
1215
|
+
getThemeClass(config.theme)
|
|
1216
|
+
);
|
|
1217
|
+
const closeBtn = document.createElement("button");
|
|
1218
|
+
closeBtn.className = "perspective-close";
|
|
1219
|
+
closeBtn.innerHTML = CLOSE_ICON;
|
|
1220
|
+
closeBtn.setAttribute("aria-label", "Close");
|
|
1221
|
+
const loading = createLoadingIndicator({
|
|
1222
|
+
theme: config.theme,
|
|
1223
|
+
brand: config.brand
|
|
1224
|
+
});
|
|
1225
|
+
const iframe = createIframe(
|
|
1226
|
+
researchId,
|
|
1227
|
+
"slider",
|
|
1228
|
+
host,
|
|
1229
|
+
config.params,
|
|
1230
|
+
config.brand,
|
|
1231
|
+
config.theme
|
|
1232
|
+
);
|
|
1233
|
+
iframe.style.opacity = "0";
|
|
1234
|
+
iframe.style.transition = "opacity 0.3s ease";
|
|
1235
|
+
slider.appendChild(closeBtn);
|
|
1236
|
+
slider.appendChild(loading);
|
|
1237
|
+
slider.appendChild(iframe);
|
|
1238
|
+
document.body.appendChild(backdrop);
|
|
1239
|
+
document.body.appendChild(slider);
|
|
1240
|
+
let currentConfig = { ...config };
|
|
1241
|
+
let isOpen = true;
|
|
1242
|
+
let messageCleanup = null;
|
|
1243
|
+
const unregisterIframe = registerIframe(iframe, host);
|
|
1244
|
+
const destroy = () => {
|
|
1245
|
+
if (!isOpen) return;
|
|
1246
|
+
isOpen = false;
|
|
1247
|
+
messageCleanup?.();
|
|
1248
|
+
unregisterIframe();
|
|
1249
|
+
slider.remove();
|
|
1250
|
+
backdrop.remove();
|
|
1251
|
+
document.removeEventListener("keydown", escHandler);
|
|
1252
|
+
currentConfig.onClose?.();
|
|
1253
|
+
};
|
|
1254
|
+
messageCleanup = setupMessageListener(
|
|
1255
|
+
researchId,
|
|
1256
|
+
{
|
|
1257
|
+
get onReady() {
|
|
1258
|
+
return () => {
|
|
1259
|
+
loading.style.opacity = "0";
|
|
1260
|
+
iframe.style.opacity = "1";
|
|
1261
|
+
setTimeout(() => loading.remove(), 300);
|
|
1262
|
+
currentConfig.onReady?.();
|
|
1263
|
+
};
|
|
1264
|
+
},
|
|
1265
|
+
get onSubmit() {
|
|
1266
|
+
return currentConfig.onSubmit;
|
|
1267
|
+
},
|
|
1268
|
+
get onNavigate() {
|
|
1269
|
+
return currentConfig.onNavigate;
|
|
1270
|
+
},
|
|
1271
|
+
get onClose() {
|
|
1272
|
+
return destroy;
|
|
1273
|
+
},
|
|
1274
|
+
get onError() {
|
|
1275
|
+
return currentConfig.onError;
|
|
1276
|
+
}
|
|
1277
|
+
},
|
|
1278
|
+
iframe,
|
|
1279
|
+
host,
|
|
1280
|
+
{ skipResize: true }
|
|
1281
|
+
);
|
|
1282
|
+
closeBtn.addEventListener("click", destroy);
|
|
1283
|
+
backdrop.addEventListener("click", destroy);
|
|
1284
|
+
const escHandler = (e) => {
|
|
1285
|
+
if (e.key === "Escape") {
|
|
1286
|
+
destroy();
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
document.addEventListener("keydown", escHandler);
|
|
1290
|
+
return {
|
|
1291
|
+
unmount: destroy,
|
|
1292
|
+
update: (options) => {
|
|
1293
|
+
currentConfig = { ...currentConfig, ...options };
|
|
1294
|
+
},
|
|
1295
|
+
destroy,
|
|
1296
|
+
researchId,
|
|
1297
|
+
type: "slider",
|
|
1298
|
+
iframe,
|
|
1299
|
+
container: slider
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// src/float.ts
|
|
1304
|
+
function createNoOpHandle4(researchId) {
|
|
1305
|
+
return {
|
|
1306
|
+
unmount: () => {
|
|
1307
|
+
},
|
|
1308
|
+
update: () => {
|
|
1309
|
+
},
|
|
1310
|
+
destroy: () => {
|
|
1311
|
+
},
|
|
1312
|
+
open: () => {
|
|
1313
|
+
},
|
|
1314
|
+
close: () => {
|
|
1315
|
+
},
|
|
1316
|
+
toggle: () => {
|
|
1317
|
+
},
|
|
1318
|
+
isOpen: false,
|
|
1319
|
+
researchId,
|
|
1320
|
+
type: "float",
|
|
1321
|
+
iframe: null,
|
|
1322
|
+
container: null
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
function createFloatBubble(config) {
|
|
1326
|
+
const { researchId, _themeConfig, theme, brand } = config;
|
|
1327
|
+
if (!hasDom()) {
|
|
1328
|
+
return createNoOpHandle4(researchId);
|
|
1329
|
+
}
|
|
1330
|
+
const host = getHost(config.host);
|
|
1331
|
+
injectStyles();
|
|
1332
|
+
ensureGlobalListeners();
|
|
1333
|
+
const bubble = document.createElement("button");
|
|
1334
|
+
bubble.className = cn(
|
|
1335
|
+
"perspective-float-bubble perspective-embed-root",
|
|
1336
|
+
getThemeClass(config.theme)
|
|
1337
|
+
);
|
|
1338
|
+
bubble.innerHTML = MIC_ICON;
|
|
1339
|
+
bubble.setAttribute("aria-label", "Open chat");
|
|
1340
|
+
bubble.setAttribute("data-perspective", "float-bubble");
|
|
1341
|
+
if (_themeConfig || brand) {
|
|
1342
|
+
const isDark = resolveIsDark(theme);
|
|
1343
|
+
const bg = isDark ? brand?.dark?.primary ?? _themeConfig?.darkPrimaryColor ?? "#a78bfa" : brand?.light?.primary ?? _themeConfig?.primaryColor ?? "#7c3aed";
|
|
1344
|
+
bubble.style.setProperty("--perspective-float-bg", bg);
|
|
1345
|
+
bubble.style.setProperty(
|
|
1346
|
+
"--perspective-float-shadow",
|
|
1347
|
+
`0 4px 12px ${bg}66`
|
|
1348
|
+
);
|
|
1349
|
+
bubble.style.setProperty(
|
|
1350
|
+
"--perspective-float-shadow-hover",
|
|
1351
|
+
`0 6px 16px ${bg}80`
|
|
1352
|
+
);
|
|
1353
|
+
bubble.style.backgroundColor = bg;
|
|
1354
|
+
bubble.style.boxShadow = `0 4px 12px ${bg}66`;
|
|
1355
|
+
}
|
|
1356
|
+
document.body.appendChild(bubble);
|
|
1357
|
+
let floatWindow = null;
|
|
1358
|
+
let iframe = null;
|
|
1359
|
+
let cleanup = null;
|
|
1360
|
+
let unregisterIframe = null;
|
|
1361
|
+
let isOpen = false;
|
|
1362
|
+
let currentConfig = { ...config };
|
|
1363
|
+
const openFloat = () => {
|
|
1364
|
+
if (isOpen) return;
|
|
1365
|
+
isOpen = true;
|
|
1366
|
+
floatWindow = document.createElement("div");
|
|
1367
|
+
floatWindow.className = cn(
|
|
1368
|
+
"perspective-float-window perspective-embed-root",
|
|
1369
|
+
getThemeClass(currentConfig.theme)
|
|
1370
|
+
);
|
|
1371
|
+
const closeBtn = document.createElement("button");
|
|
1372
|
+
closeBtn.className = "perspective-close";
|
|
1373
|
+
closeBtn.innerHTML = CLOSE_ICON;
|
|
1374
|
+
closeBtn.setAttribute("aria-label", "Close chat");
|
|
1375
|
+
closeBtn.addEventListener("click", closeFloat);
|
|
1376
|
+
const loading = createLoadingIndicator({
|
|
1377
|
+
theme: currentConfig.theme,
|
|
1378
|
+
brand: currentConfig.brand
|
|
1379
|
+
});
|
|
1380
|
+
loading.style.borderRadius = "16px";
|
|
1381
|
+
iframe = createIframe(
|
|
1382
|
+
researchId,
|
|
1383
|
+
"float",
|
|
1384
|
+
host,
|
|
1385
|
+
currentConfig.params,
|
|
1386
|
+
currentConfig.brand,
|
|
1387
|
+
currentConfig.theme
|
|
1388
|
+
);
|
|
1389
|
+
iframe.style.opacity = "0";
|
|
1390
|
+
iframe.style.transition = "opacity 0.3s ease";
|
|
1391
|
+
floatWindow.appendChild(closeBtn);
|
|
1392
|
+
floatWindow.appendChild(loading);
|
|
1393
|
+
floatWindow.appendChild(iframe);
|
|
1394
|
+
document.body.appendChild(floatWindow);
|
|
1395
|
+
cleanup = setupMessageListener(
|
|
1396
|
+
researchId,
|
|
1397
|
+
{
|
|
1398
|
+
get onReady() {
|
|
1399
|
+
return () => {
|
|
1400
|
+
loading.style.opacity = "0";
|
|
1401
|
+
iframe.style.opacity = "1";
|
|
1402
|
+
setTimeout(() => loading.remove(), 300);
|
|
1403
|
+
currentConfig.onReady?.();
|
|
1404
|
+
};
|
|
1405
|
+
},
|
|
1406
|
+
get onSubmit() {
|
|
1407
|
+
return currentConfig.onSubmit;
|
|
1408
|
+
},
|
|
1409
|
+
get onNavigate() {
|
|
1410
|
+
return currentConfig.onNavigate;
|
|
1411
|
+
},
|
|
1412
|
+
get onClose() {
|
|
1413
|
+
return closeFloat;
|
|
1414
|
+
},
|
|
1415
|
+
get onError() {
|
|
1416
|
+
return currentConfig.onError;
|
|
1417
|
+
}
|
|
1418
|
+
},
|
|
1419
|
+
iframe,
|
|
1420
|
+
host,
|
|
1421
|
+
{ skipResize: true }
|
|
1422
|
+
);
|
|
1423
|
+
if (iframe) {
|
|
1424
|
+
unregisterIframe = registerIframe(iframe, host);
|
|
1425
|
+
}
|
|
1426
|
+
bubble.innerHTML = CLOSE_ICON;
|
|
1427
|
+
bubble.setAttribute("aria-label", "Close chat");
|
|
1428
|
+
};
|
|
1429
|
+
const closeFloat = () => {
|
|
1430
|
+
if (!isOpen) return;
|
|
1431
|
+
isOpen = false;
|
|
1432
|
+
cleanup?.();
|
|
1433
|
+
unregisterIframe?.();
|
|
1434
|
+
floatWindow?.remove();
|
|
1435
|
+
floatWindow = null;
|
|
1436
|
+
iframe = null;
|
|
1437
|
+
cleanup = null;
|
|
1438
|
+
unregisterIframe = null;
|
|
1439
|
+
bubble.innerHTML = MIC_ICON;
|
|
1440
|
+
bubble.setAttribute("aria-label", "Open chat");
|
|
1441
|
+
currentConfig.onClose?.();
|
|
1442
|
+
};
|
|
1443
|
+
bubble.addEventListener("click", () => {
|
|
1444
|
+
if (isOpen) {
|
|
1445
|
+
closeFloat();
|
|
1446
|
+
} else {
|
|
1447
|
+
openFloat();
|
|
1448
|
+
}
|
|
1449
|
+
});
|
|
1450
|
+
const unmount = () => {
|
|
1451
|
+
closeFloat();
|
|
1452
|
+
bubble.remove();
|
|
1453
|
+
};
|
|
1454
|
+
return {
|
|
1455
|
+
unmount,
|
|
1456
|
+
update: (options) => {
|
|
1457
|
+
currentConfig = { ...currentConfig, ...options };
|
|
1458
|
+
},
|
|
1459
|
+
destroy: unmount,
|
|
1460
|
+
open: openFloat,
|
|
1461
|
+
close: closeFloat,
|
|
1462
|
+
toggle: () => {
|
|
1463
|
+
if (isOpen) {
|
|
1464
|
+
closeFloat();
|
|
1465
|
+
} else {
|
|
1466
|
+
openFloat();
|
|
1467
|
+
}
|
|
1468
|
+
},
|
|
1469
|
+
get isOpen() {
|
|
1470
|
+
return isOpen;
|
|
1471
|
+
},
|
|
1472
|
+
researchId,
|
|
1473
|
+
type: "float",
|
|
1474
|
+
get iframe() {
|
|
1475
|
+
return iframe;
|
|
1476
|
+
},
|
|
1477
|
+
container: bubble
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
var createChatBubble = createFloatBubble;
|
|
1481
|
+
|
|
1482
|
+
// src/fullpage.ts
|
|
1483
|
+
function createNoOpHandle5(researchId) {
|
|
1484
|
+
return {
|
|
1485
|
+
unmount: () => {
|
|
1486
|
+
},
|
|
1487
|
+
update: () => {
|
|
1488
|
+
},
|
|
1489
|
+
destroy: () => {
|
|
1490
|
+
},
|
|
1491
|
+
researchId,
|
|
1492
|
+
type: "fullpage",
|
|
1493
|
+
iframe: null,
|
|
1494
|
+
container: null
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
function createFullpage(config) {
|
|
1498
|
+
const { researchId } = config;
|
|
1499
|
+
if (!hasDom()) {
|
|
1500
|
+
return createNoOpHandle5(researchId);
|
|
1501
|
+
}
|
|
1502
|
+
const host = getHost(config.host);
|
|
1503
|
+
injectStyles();
|
|
1504
|
+
ensureGlobalListeners();
|
|
1505
|
+
const container = document.createElement("div");
|
|
1506
|
+
container.className = cn(
|
|
1507
|
+
"perspective-embed-root perspective-fullpage",
|
|
1508
|
+
getThemeClass(config.theme)
|
|
1509
|
+
);
|
|
1510
|
+
const loading = createLoadingIndicator({
|
|
1511
|
+
theme: config.theme,
|
|
1512
|
+
brand: config.brand
|
|
1513
|
+
});
|
|
1514
|
+
container.appendChild(loading);
|
|
1515
|
+
const iframe = createIframe(
|
|
1516
|
+
researchId,
|
|
1517
|
+
"fullpage",
|
|
1518
|
+
host,
|
|
1519
|
+
config.params,
|
|
1520
|
+
config.brand,
|
|
1521
|
+
config.theme
|
|
1522
|
+
);
|
|
1523
|
+
iframe.style.opacity = "0";
|
|
1524
|
+
iframe.style.transition = "opacity 0.3s ease";
|
|
1525
|
+
container.appendChild(iframe);
|
|
1526
|
+
document.body.appendChild(container);
|
|
1527
|
+
let currentConfig = { ...config };
|
|
1528
|
+
let messageCleanup = null;
|
|
1529
|
+
const unregisterIframe = registerIframe(iframe, host);
|
|
1530
|
+
const unmount = () => {
|
|
1531
|
+
messageCleanup?.();
|
|
1532
|
+
unregisterIframe();
|
|
1533
|
+
container.remove();
|
|
1534
|
+
currentConfig.onClose?.();
|
|
1535
|
+
};
|
|
1536
|
+
messageCleanup = setupMessageListener(
|
|
1537
|
+
researchId,
|
|
1538
|
+
{
|
|
1539
|
+
get onReady() {
|
|
1540
|
+
return () => {
|
|
1541
|
+
loading.style.opacity = "0";
|
|
1542
|
+
iframe.style.opacity = "1";
|
|
1543
|
+
setTimeout(() => loading.remove(), 300);
|
|
1544
|
+
currentConfig.onReady?.();
|
|
1545
|
+
};
|
|
1546
|
+
},
|
|
1547
|
+
get onSubmit() {
|
|
1548
|
+
return currentConfig.onSubmit;
|
|
1549
|
+
},
|
|
1550
|
+
get onNavigate() {
|
|
1551
|
+
return currentConfig.onNavigate;
|
|
1552
|
+
},
|
|
1553
|
+
get onClose() {
|
|
1554
|
+
return unmount;
|
|
1555
|
+
},
|
|
1556
|
+
get onError() {
|
|
1557
|
+
return currentConfig.onError;
|
|
1558
|
+
}
|
|
1559
|
+
},
|
|
1560
|
+
iframe,
|
|
1561
|
+
host,
|
|
1562
|
+
{ skipResize: true }
|
|
1563
|
+
);
|
|
1564
|
+
return {
|
|
1565
|
+
unmount,
|
|
1566
|
+
update: (options) => {
|
|
1567
|
+
currentConfig = { ...currentConfig, ...options };
|
|
1568
|
+
},
|
|
1569
|
+
destroy: unmount,
|
|
1570
|
+
researchId,
|
|
1571
|
+
type: "fullpage",
|
|
1572
|
+
iframe,
|
|
1573
|
+
container
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
export { BRAND_KEYS, CURRENT_FEATURES, DATA_ATTRS, FEATURES, MESSAGE_TYPES, PARAM_KEYS, SDK_VERSION, THEME_VALUES, configure, createChatBubble, createFloatBubble, createFullpage, createWidget, getConfig, openPopup, openSlider };
|
|
1578
|
+
//# sourceMappingURL=index.js.map
|
|
1579
|
+
//# sourceMappingURL=index.js.map
|