@perspective-ai/sdk 1.0.0 → 1.0.1

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
@@ -51,21 +51,6 @@ var FEATURES = {
51
51
  };
52
52
  var CURRENT_FEATURES = FEATURES.RESIZE | FEATURES.THEME_SYNC | FEATURES.ANON_ID | FEATURES.SCROLLBAR_STYLES;
53
53
  var PARAM_KEYS = {
54
- // User identification
55
- email: "email",
56
- name: "name",
57
- // Navigation
58
- returnUrl: "returnUrl",
59
- // Interview behavior
60
- voice: "voice",
61
- scroll: "scroll",
62
- hideProgress: "hideProgress",
63
- hideGreeting: "hideGreeting",
64
- hideBranding: "hideBranding",
65
- // Interview mode & auth
66
- mode: "mode",
67
- invite: "invite",
68
- // System (internal)
69
54
  embed: "embed",
70
55
  embedType: "embed_type",
71
56
  theme: "theme"
@@ -141,8 +126,6 @@ var ERROR_CODES = {
141
126
  UNKNOWN: "UNKNOWN"
142
127
  };
143
128
  var PARAM_VALUES = {
144
- disabled: "0",
145
- enabled: "1",
146
129
  true: "true",
147
130
  false: "false"
148
131
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/browser.ts","../src/constants.ts","../src/config.ts","../src/utils.ts","../src/iframe.ts","../src/loading.ts","../src/styles.ts","../src/widget.ts","../src/popup.ts","../src/slider.ts","../src/float.ts","../src/fullpage.ts"],"sourcesContent":["/**\n * Perspective Embed SDK - Browser Entry\n *\n * CDN/Script Tag Entry Point - auto-init, attaches to window.Perspective\n *\n * Usage:\n * <script src=\"https://getperspective.ai/v1/perspective.js\"></script>\n *\n * <!-- Auto-init with data attributes -->\n * <div data-perspective-widget=\"research_xxx\"></div>\n * <button data-perspective-popup=\"research_xxx\">Open Survey</button>\n *\n * <!-- Or programmatic -->\n * <script>\n * Perspective.openPopup({ researchId: 'xxx' });\n * </script>\n */\n\nimport type {\n BrandColors,\n EmbedConfig,\n EmbedHandle,\n FloatHandle,\n ThemeConfig,\n} from \"./types\";\nimport { DATA_ATTRS, THEME_VALUES } from \"./constants\";\nimport { createWidget } from \"./widget\";\nimport { openPopup } from \"./popup\";\nimport { openSlider } from \"./slider\";\nimport { createFloatBubble, createChatBubble } from \"./float\";\nimport { createFullpage } from \"./fullpage\";\nimport { configure, getConfig, hasDom, getHost } from \"./config\";\nimport { resolveIsDark } from \"./utils\";\n\n// Track all active instances\nconst instances: Map<string, EmbedHandle | FloatHandle> = new Map();\n\n// Theme config cache\nconst configCache: Map<string, ThemeConfig> = new Map();\n\ntype ButtonStyleConfig = {\n themeConfig: ThemeConfig;\n theme?: EmbedConfig[\"theme\"];\n brand?: EmbedConfig[\"brand\"];\n};\nconst styledButtons = new Map<HTMLElement, ButtonStyleConfig>();\nlet buttonThemeMediaQuery: MediaQueryList | null = null;\n\nconst DEFAULT_THEME: ThemeConfig = {\n primaryColor: \"#7c3aed\",\n textColor: \"#ffffff\",\n darkPrimaryColor: \"#a78bfa\",\n darkTextColor: \"#ffffff\",\n};\n\n/**\n * Fetch theme config from API (cached)\n */\nasync function fetchConfig(researchId: string): Promise<ThemeConfig> {\n if (configCache.has(researchId)) {\n return configCache.get(researchId)!;\n }\n\n try {\n const host = getHost();\n const res = await fetch(`${host}/api/v1/embed/config/${researchId}`);\n if (!res.ok) return DEFAULT_THEME;\n const config = await res.json();\n configCache.set(researchId, config);\n return config;\n } catch {\n return DEFAULT_THEME;\n }\n}\n\n/**\n * Apply theme styles to a button element\n */\nfunction styleButton(\n el: HTMLElement,\n themeConfig: ThemeConfig,\n options?: { theme?: EmbedConfig[\"theme\"]; brand?: EmbedConfig[\"brand\"] }\n): void {\n if (el.hasAttribute(DATA_ATTRS.noStyle)) return;\n\n styledButtons.set(el, {\n themeConfig,\n theme: options?.theme,\n brand: options?.brand,\n });\n\n updateButtonTheme(el, {\n themeConfig,\n theme: options?.theme,\n brand: options?.brand,\n });\n}\n\n/**\n * Update button styles based on theme\n */\nfunction updateButtonTheme(el: HTMLElement, config: ButtonStyleConfig): void {\n const { themeConfig, theme, brand } = config;\n const isDark = resolveIsDark(theme);\n\n const bg = isDark\n ? (brand?.dark?.primary ?? themeConfig.darkPrimaryColor)\n : (brand?.light?.primary ?? themeConfig.primaryColor);\n const text = isDark\n ? (brand?.dark?.text ?? themeConfig.darkTextColor)\n : (brand?.light?.text ?? themeConfig.textColor);\n\n el.style.backgroundColor = bg;\n el.style.color = text;\n el.style.padding = \"10px 20px\";\n el.style.border = \"none\";\n el.style.borderRadius = \"8px\";\n el.style.fontWeight = \"500\";\n el.style.cursor = \"pointer\";\n}\n\n/**\n * Update all styled buttons when theme changes\n */\nfunction updateAllButtonThemes(): void {\n styledButtons.forEach((config, el) => {\n if (document.contains(el)) {\n updateButtonTheme(el, config);\n } else {\n styledButtons.delete(el);\n }\n });\n}\n\nlet buttonThemeListener: ((e: MediaQueryListEvent) => void) | null = null;\n\nfunction setupButtonThemeListener(): void {\n if (buttonThemeListener || !hasDom()) return;\n\n buttonThemeMediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n buttonThemeListener = () => updateAllButtonThemes();\n buttonThemeMediaQuery.addEventListener(\"change\", buttonThemeListener);\n}\n\nfunction teardownButtonThemeListener(): void {\n if (buttonThemeListener && buttonThemeMediaQuery) {\n buttonThemeMediaQuery.removeEventListener(\"change\", buttonThemeListener);\n buttonThemeListener = null;\n buttonThemeMediaQuery = null;\n }\n}\n\n/**\n * Parse params from data attribute (format: \"key1=value1,key2=value2\")\n */\nfunction parseParamsAttr(el: HTMLElement): Record<string, string> | undefined {\n const paramsStr = el.getAttribute(DATA_ATTRS.params);\n if (!paramsStr) return undefined;\n\n const params: Record<string, string> = {};\n for (const pair of paramsStr.split(\",\")) {\n const [key, ...valueParts] = pair.trim().split(\"=\");\n if (key) {\n params[key.trim()] = valueParts.join(\"=\").trim();\n }\n }\n return Object.keys(params).length > 0 ? params : undefined;\n}\n\n/**\n * Parse brand colors from data attribute (format: \"primary=#xxx,bg=#yyy\")\n */\nfunction parseBrandAttr(attrValue: string | null): BrandColors | undefined {\n if (!attrValue) return undefined;\n\n const colors: BrandColors = {};\n for (const pair of attrValue.split(\",\")) {\n const [key, ...valueParts] = pair.trim().split(\"=\");\n if (key && valueParts.length > 0) {\n const value = valueParts.join(\"=\").trim();\n if (value) {\n const k = key.trim() as keyof BrandColors;\n if (\n k === \"primary\" ||\n k === \"secondary\" ||\n k === \"bg\" ||\n k === \"text\"\n ) {\n colors[k] = value;\n }\n }\n }\n }\n return Object.keys(colors).length > 0 ? colors : undefined;\n}\n\n/**\n * Extract brand config and theme from element attributes\n */\nfunction extractBrandConfig(\n el: HTMLElement\n): Pick<EmbedConfig, \"brand\" | \"theme\"> {\n const light = parseBrandAttr(el.getAttribute(DATA_ATTRS.brand));\n const dark = parseBrandAttr(el.getAttribute(DATA_ATTRS.brandDark));\n const themeAttr = el.getAttribute(DATA_ATTRS.theme);\n\n const config: Pick<EmbedConfig, \"brand\" | \"theme\"> = {};\n\n if (light || dark) {\n config.brand = {};\n if (light) config.brand.light = light;\n if (dark) config.brand.dark = dark;\n }\n\n if (\n themeAttr === THEME_VALUES.dark ||\n themeAttr === THEME_VALUES.light ||\n themeAttr === THEME_VALUES.system\n ) {\n config.theme = themeAttr;\n }\n\n return config;\n}\n\n/**\n * Initialize an embed programmatically\n */\nfunction init(config: EmbedConfig): EmbedHandle | FloatHandle {\n const { researchId } = config;\n // Normalize legacy \"chat\" type to \"float\"\n const type = config.type === \"chat\" ? \"float\" : (config.type ?? \"widget\");\n\n // Destroy existing instance for this research\n if (instances.has(researchId)) {\n instances.get(researchId)!.unmount();\n instances.delete(researchId);\n }\n\n let instance: EmbedHandle | FloatHandle;\n\n switch (type) {\n case \"popup\":\n instance = openPopup(config);\n break;\n case \"slider\":\n instance = openSlider(config);\n break;\n case \"float\":\n instance = createFloatBubble(config);\n break;\n case \"fullpage\":\n instance = createFullpage(config);\n break;\n default:\n throw new Error(\n `Unknown embed type \"${type}\". Valid types: popup, slider, float, fullpage (use init()), or widget (use mount()).`\n );\n }\n\n instances.set(researchId, instance);\n return instance;\n}\n\n/**\n * Mount a widget into a container element\n */\nfunction mount(\n container: HTMLElement | string,\n config: EmbedConfig\n): EmbedHandle {\n const { researchId } = config;\n const type = config.type === \"chat\" ? \"float\" : (config.type ?? \"widget\");\n\n const el =\n typeof container === \"string\"\n ? document.querySelector<HTMLElement>(container)\n : container;\n\n if (!el) {\n throw new Error(`Container not found: ${container}`);\n }\n\n // Destroy existing instance\n if (instances.has(researchId)) {\n instances.get(researchId)!.unmount();\n instances.delete(researchId);\n }\n\n let instance: EmbedHandle;\n\n switch (type) {\n case \"widget\":\n instance = createWidget(el, config);\n break;\n default:\n // For popup/slider/float, just use init - container not used\n instance = init({ ...config, type }) as EmbedHandle;\n return instance;\n }\n\n instances.set(researchId, instance);\n return instance;\n}\n\n/**\n * Destroy an embed instance\n */\nfunction destroy(researchId: string): void {\n const instance = instances.get(researchId);\n if (instance) {\n instance.unmount();\n instances.delete(researchId);\n }\n}\n\nfunction destroyAll(): void {\n instances.forEach((instance) => instance.unmount());\n instances.clear();\n styledButtons.clear();\n teardownButtonThemeListener();\n}\n\n/**\n * Auto-initialize embeds from data attributes\n */\nfunction autoInit(): void {\n if (!hasDom()) return;\n\n setupButtonThemeListener();\n\n // Widget embeds\n document\n .querySelectorAll<HTMLElement>(`[${DATA_ATTRS.widget}]`)\n .forEach((el) => {\n const researchId = el.getAttribute(DATA_ATTRS.widget);\n if (researchId && !instances.has(researchId)) {\n const params = parseParamsAttr(el);\n const brandConfig = extractBrandConfig(el);\n mount(el, { researchId, type: \"widget\", params, ...brandConfig });\n }\n });\n\n // Fullpage embeds\n document\n .querySelectorAll<HTMLElement>(`[${DATA_ATTRS.fullpage}]`)\n .forEach((el) => {\n const researchId = el.getAttribute(DATA_ATTRS.fullpage);\n if (researchId && !instances.has(researchId)) {\n const params = parseParamsAttr(el);\n const brandConfig = extractBrandConfig(el);\n init({ researchId, type: \"fullpage\", params, ...brandConfig });\n }\n });\n\n // Popup triggers\n document\n .querySelectorAll<HTMLElement>(`[${DATA_ATTRS.popup}]`)\n .forEach((el) => {\n if (el.hasAttribute(\"data-perspective-initialized\")) return;\n el.setAttribute(\"data-perspective-initialized\", \"true\");\n\n const researchId = el.getAttribute(DATA_ATTRS.popup);\n if (researchId) {\n const params = parseParamsAttr(el);\n const brandConfig = extractBrandConfig(el);\n styleButton(el, DEFAULT_THEME, brandConfig);\n el.addEventListener(\"click\", (e) => {\n e.preventDefault();\n init({ researchId, type: \"popup\", params, ...brandConfig });\n });\n fetchConfig(researchId).then((config) => {\n styleButton(el, config, brandConfig);\n });\n }\n });\n\n // Slider triggers\n document\n .querySelectorAll<HTMLElement>(`[${DATA_ATTRS.slider}]`)\n .forEach((el) => {\n if (el.hasAttribute(\"data-perspective-initialized\")) return;\n el.setAttribute(\"data-perspective-initialized\", \"true\");\n\n const researchId = el.getAttribute(DATA_ATTRS.slider);\n if (researchId) {\n const params = parseParamsAttr(el);\n const brandConfig = extractBrandConfig(el);\n styleButton(el, DEFAULT_THEME, brandConfig);\n el.addEventListener(\"click\", (e) => {\n e.preventDefault();\n init({ researchId, type: \"slider\", params, ...brandConfig });\n });\n fetchConfig(researchId).then((config) => {\n styleButton(el, config, brandConfig);\n });\n }\n });\n\n // Float bubble - supports both data-perspective-float and data-perspective-chat (legacy)\n const floatSelector = `[${DATA_ATTRS.float}], [${DATA_ATTRS.chat}]`;\n const floatEl = document.querySelector<HTMLElement>(floatSelector);\n if (floatEl) {\n const researchId =\n floatEl.getAttribute(DATA_ATTRS.float) ||\n floatEl.getAttribute(DATA_ATTRS.chat);\n if (researchId && !instances.has(researchId)) {\n const params = parseParamsAttr(floatEl);\n const brandConfig = extractBrandConfig(floatEl);\n init({\n researchId,\n type: \"float\",\n params,\n ...brandConfig,\n _themeConfig: DEFAULT_THEME,\n } as EmbedConfig & { _themeConfig: ThemeConfig });\n\n fetchConfig(researchId).then((config) => {\n // Update bubble color with fetched theme\n const bubble = document.querySelector<HTMLElement>(\n '[data-perspective=\"float-bubble\"]'\n );\n if (bubble && !floatEl.hasAttribute(DATA_ATTRS.noStyle)) {\n const isDark = resolveIsDark(brandConfig.theme);\n const bg = isDark\n ? (brandConfig.brand?.dark?.primary ?? config.darkPrimaryColor)\n : (brandConfig.brand?.light?.primary ?? config.primaryColor);\n bubble.style.setProperty(\"--perspective-float-bg\", bg);\n bubble.style.setProperty(\n \"--perspective-float-shadow\",\n `0 4px 12px ${bg}66`\n );\n bubble.style.setProperty(\n \"--perspective-float-shadow-hover\",\n `0 6px 16px ${bg}80`\n );\n bubble.style.backgroundColor = bg;\n bubble.style.boxShadow = `0 4px 12px ${bg}66`;\n }\n });\n }\n }\n}\n\n// Build the public API\nconst Perspective = {\n // Configuration\n configure,\n getConfig,\n\n // Instance management\n init,\n mount,\n destroy,\n destroyAll,\n autoInit,\n\n // Direct creation functions (primary API)\n createWidget,\n openPopup,\n openSlider,\n createFloatBubble,\n createFullpage,\n\n // Legacy alias\n createChatBubble,\n};\n\ndeclare global {\n interface Window {\n __PERSPECTIVE_SDK_INITIALIZED__?: boolean;\n Perspective?: typeof Perspective;\n }\n}\n\n// Prevent duplicate initialization when script is loaded multiple times\n// (e.g., SPAs, tag managers, hot-reload, or accidental double-include).\n// Without this guard, each script evaluation would register new listeners\n// and create isolated module state, causing memory leaks and duplicate handlers.\nif (hasDom() && !window.__PERSPECTIVE_SDK_INITIALIZED__) {\n window.__PERSPECTIVE_SDK_INITIALIZED__ = true;\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", autoInit, { once: true });\n } else {\n autoInit();\n }\n\n window.Perspective = Perspective;\n}\n\n// Export for module usage\nexport {\n configure,\n getConfig,\n init,\n mount,\n destroy,\n destroyAll,\n autoInit,\n createWidget,\n openPopup,\n openSlider,\n createFloatBubble,\n createChatBubble,\n createFullpage,\n};\n\nexport default Perspective;\n","/**\n * Shared constants for Perspective Embed SDK\n * This file is SSR-safe - no DOM access at import time\n * Used by both SDK bundle and the main Perspective app\n */\n\n// ============================================================================\n// SDK Version & Features\n// ============================================================================\n\n/** SDK version for handshake protocol */\nexport const SDK_VERSION = \"1.0.0\";\n\n/** Feature flags as bitset for version negotiation */\nexport const FEATURES = {\n RESIZE: 1 << 0, // 0b0001\n THEME_SYNC: 1 << 1, // 0b0010\n ANON_ID: 1 << 2, // 0b0100\n SCROLLBAR_STYLES: 1 << 3, // 0b1000\n} as const;\n\n/** Current SDK feature set */\nexport const CURRENT_FEATURES =\n FEATURES.RESIZE |\n FEATURES.THEME_SYNC |\n FEATURES.ANON_ID |\n FEATURES.SCROLLBAR_STYLES;\n\n// ============================================================================\n// URL Parameter Keys\n// ============================================================================\n\nexport const PARAM_KEYS = {\n // User identification\n email: \"email\",\n name: \"name\",\n\n // Navigation\n returnUrl: \"returnUrl\",\n\n // Interview behavior\n voice: \"voice\",\n scroll: \"scroll\",\n hideProgress: \"hideProgress\",\n hideGreeting: \"hideGreeting\",\n hideBranding: \"hideBranding\",\n\n // Interview mode & auth\n mode: \"mode\",\n invite: \"invite\",\n\n // System (internal)\n embed: \"embed\",\n embedType: \"embed_type\",\n theme: \"theme\",\n} as const;\n\nexport type ParamKey = (typeof PARAM_KEYS)[keyof typeof PARAM_KEYS];\n\n// ============================================================================\n// Brand Color Keys\n// ============================================================================\n\nexport const BRAND_KEYS = {\n // Light mode\n primary: \"brand.primary\",\n secondary: \"brand.secondary\",\n bg: \"brand.bg\",\n text: \"brand.text\",\n\n // Dark mode\n darkPrimary: \"brand.dark.primary\",\n darkSecondary: \"brand.dark.secondary\",\n darkBg: \"brand.dark.bg\",\n darkText: \"brand.dark.text\",\n} as const;\n\nexport type BrandKey = (typeof BRAND_KEYS)[keyof typeof BRAND_KEYS];\n\n// ============================================================================\n// UTM Parameters (auto-forwarded from parent URL)\n// ============================================================================\n\nexport const UTM_PARAMS = [\n \"utm_source\",\n \"utm_medium\",\n \"utm_campaign\",\n \"utm_term\",\n \"utm_content\",\n] as const;\n\nexport type UtmParam = (typeof UTM_PARAMS)[number];\n\n// ============================================================================\n// Reserved Parameters (cannot be overridden via custom params)\n// ============================================================================\n\nexport const RESERVED_PARAMS: Set<string> = new Set([\n PARAM_KEYS.embed,\n PARAM_KEYS.embedType,\n PARAM_KEYS.theme,\n BRAND_KEYS.primary,\n BRAND_KEYS.secondary,\n BRAND_KEYS.bg,\n BRAND_KEYS.text,\n BRAND_KEYS.darkPrimary,\n BRAND_KEYS.darkSecondary,\n BRAND_KEYS.darkBg,\n BRAND_KEYS.darkText,\n ...UTM_PARAMS,\n]);\n\n// ============================================================================\n// Data Attributes (HTML declarative initialization)\n// ============================================================================\n\nexport const DATA_ATTRS = {\n widget: \"data-perspective-widget\",\n popup: \"data-perspective-popup\",\n slider: \"data-perspective-slider\",\n float: \"data-perspective-float\", // Primary name\n chat: \"data-perspective-chat\", // Legacy alias\n fullpage: \"data-perspective-fullpage\",\n params: \"data-perspective-params\",\n brand: \"data-perspective-brand\",\n brandDark: \"data-perspective-brand-dark\",\n theme: \"data-perspective-theme\",\n noStyle: \"data-perspective-no-style\",\n} as const;\n\nexport type DataAttr = (typeof DATA_ATTRS)[keyof typeof DATA_ATTRS];\n\n// ============================================================================\n// PostMessage Event Types\n// ============================================================================\n\nexport const MESSAGE_TYPES = {\n // SDK -> Iframe (initialization)\n init: \"perspective:init\",\n\n // Iframe -> SDK\n ready: \"perspective:ready\",\n resize: \"perspective:resize\",\n submit: \"perspective:submit\",\n close: \"perspective:close\",\n error: \"perspective:error\",\n redirect: \"perspective:redirect\",\n\n // SDK -> Iframe (internal)\n anonId: \"perspective:anon-id\",\n injectStyles: \"perspective:inject-styles\",\n themeChange: \"perspective:theme-change\",\n\n // Iframe -> SDK (internal)\n requestScrollbarStyles: \"perspective:request-scrollbar-styles\",\n} as const;\n\nexport type MessageType = (typeof MESSAGE_TYPES)[keyof typeof MESSAGE_TYPES];\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\nexport const ERROR_CODES = {\n SDK_OUTDATED: \"SDK_OUTDATED\",\n INVALID_RESEARCH: \"INVALID_RESEARCH\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\n// ============================================================================\n// Param Values (for boolean-like string params)\n// ============================================================================\n\nexport const PARAM_VALUES = {\n disabled: \"0\",\n enabled: \"1\",\n true: \"true\",\n false: \"false\",\n} as const;\n\n// ============================================================================\n// Theme Values\n// ============================================================================\n\nexport const THEME_VALUES = {\n dark: \"dark\",\n light: \"light\",\n system: \"system\",\n} as const;\n\nexport type ThemeValue = (typeof THEME_VALUES)[keyof typeof THEME_VALUES];\n\n// ============================================================================\n// Interview Mode Values (for mode param)\n// ============================================================================\n\nexport const MODE_VALUES = {\n preview: \"preview\",\n restart: \"restart\",\n normal: \"normal\",\n simulated: \"simulated\",\n} as const;\n\nexport type ModeValue = (typeof MODE_VALUES)[keyof typeof MODE_VALUES];\n\n// ============================================================================\n// localStorage Keys\n// ============================================================================\n\nexport const STORAGE_KEYS = {\n anonId: \"perspective-anon-id\",\n} as const;\n","/**\n * Embed SDK configuration\n * SSR-safe - DOM access is guarded and lazy\n */\n\nimport type { SDKConfig } from \"./types\";\n\n/** Default production host */\nconst DEFAULT_HOST = \"https://getperspective.ai\";\n\n/** Global SDK configuration - can be set before creating embeds */\nlet globalConfig: SDKConfig = {};\n\n/**\n * Configure the SDK globally\n * Call this before creating any embeds if you need to override defaults\n */\nexport function configure(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get the current SDK configuration\n */\nexport function getConfig(): SDKConfig {\n return { ...globalConfig };\n}\n\n/**\n * Check if DOM is available (SSR safety)\n */\nexport function hasDom(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\nfunction normalizeToOrigin(host: string): string {\n try {\n return new URL(host).origin;\n } catch {\n return host;\n }\n}\n\nexport function getHost(instanceHost?: string): string {\n // Instance-level override\n if (instanceHost) {\n return normalizeToOrigin(instanceHost);\n }\n\n // Global config override\n if (globalConfig.host) {\n return normalizeToOrigin(globalConfig.host);\n }\n\n // Try to infer from script src (only in browser, only at load time)\n if (hasDom()) {\n const scriptHost = getScriptHost();\n if (scriptHost) {\n return scriptHost;\n }\n }\n\n return DEFAULT_HOST;\n}\n\n// Capture script src at load time - document.currentScript only available during initial execution\nlet capturedScriptHost: string | null = null;\n\nfunction getScriptHost(): string | null {\n if (capturedScriptHost !== null) {\n return capturedScriptHost;\n }\n\n if (!hasDom()) {\n return null;\n }\n\n const currentScript = document.currentScript as HTMLScriptElement | null;\n if (currentScript?.src) {\n try {\n capturedScriptHost = new URL(currentScript.src).origin;\n return capturedScriptHost;\n } catch {\n // Invalid URL, ignore\n }\n }\n\n capturedScriptHost = \"\"; // Mark as attempted\n return null;\n}\n\n// Capture script host immediately when module loads (in browser)\nif (hasDom()) {\n getScriptHost();\n}\n","/**\n * Shared utilities for the Perspective Embed SDK\n * SSR-safe - DOM access is guarded\n */\n\nimport { THEME_VALUES, type ThemeValue } from \"./constants\";\nimport { hasDom } from \"./config\";\n\n/**\n * Join class names, filtering out falsy values\n */\nexport function cn(...classes: (string | false | null | undefined)[]): string {\n return classes\n .map((c) => (c || \"\").split(\" \"))\n .flat()\n .filter(Boolean)\n .join(\" \");\n}\n\n/**\n * Get the perspective theme class based on the theme value\n * Returns \"perspective-{theme}-theme\" when theme is available, undefined otherwise\n */\nexport function getThemeClass(theme: string | undefined): string | undefined {\n return theme && theme !== THEME_VALUES.system\n ? `perspective-${theme}-theme`\n : undefined;\n}\n\n/**\n * Resolve whether dark mode should be used.\n * Priority: 1) explicit theme override, 2) system preference\n * SSR-safe: defaults to light theme on server\n */\nexport function resolveIsDark(theme?: ThemeValue | string): boolean {\n if (theme === THEME_VALUES.dark) return true;\n if (theme === THEME_VALUES.light) return false;\n // system or undefined → use system preference (or light on server)\n if (!hasDom()) return false;\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n}\n\n/**\n * Resolve effective theme based on override and system preference\n * Returns the theme string ('light' or 'dark')\n */\nexport function resolveTheme(themeOverride?: ThemeValue): \"light\" | \"dark\" {\n return resolveIsDark(themeOverride) ? \"dark\" : \"light\";\n}\n\n/**\n * Normalize and validate hex color. Returns undefined for invalid colors.\n */\nexport function normalizeHex(color: string): string | undefined {\n const trimmed = color.trim();\n if (!trimmed) return undefined;\n\n const normalized = trimmed.startsWith(\"#\") ? trimmed : `#${trimmed}`;\n\n // Validate hex format (#RGB, #RRGGBB, or #RRGGBBAA)\n if (/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(normalized)) {\n return normalized;\n }\n\n // Try to extract valid hex chars\n const hexChars = normalized.slice(1).replace(/[^0-9a-fA-F]/g, \"\");\n if (hexChars.length >= 6) return `#${hexChars.slice(0, 6)}`;\n if (hexChars.length >= 3) return `#${hexChars.slice(0, 3)}`;\n\n return undefined;\n}\n\n/**\n * Convert hex to rgba for spinner track\n */\nexport function hexToRgba(hex: string, alpha: number): string {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n if (!result || !result[1] || !result[2] || !result[3]) {\n return `rgba(118, 41, 200, ${alpha})`;\n }\n\n const r = parseInt(result[1], 16);\n const g = parseInt(result[2], 16);\n const b = parseInt(result[3], 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n","/**\n * iframe creation and postMessage communication\n * SSR-safe - all DOM access is guarded\n */\n\nimport type {\n BrandColors,\n EmbedConfig,\n EmbedMessage,\n EmbedType,\n} from \"./types\";\nimport { hasDom } from \"./config\";\nimport {\n UTM_PARAMS,\n RESERVED_PARAMS,\n PARAM_KEYS,\n BRAND_KEYS,\n MESSAGE_TYPES,\n THEME_VALUES,\n PARAM_VALUES,\n STORAGE_KEYS,\n SDK_VERSION,\n CURRENT_FEATURES,\n ERROR_CODES,\n} from \"./constants\";\nimport { normalizeHex } from \"./utils\";\n\n/** Validate redirect URL - allow https, http localhost, and relative URLs */\nfunction isAllowedRedirectUrl(url: string): boolean {\n if (!url || typeof url !== \"string\") return false;\n try {\n const parsed = new URL(url, window.location.origin);\n const protocol = parsed.protocol.toLowerCase();\n const hostname = parsed.hostname.toLowerCase();\n\n if (protocol === \"https:\") return true;\n if (\n protocol === \"http:\" &&\n (hostname === \"localhost\" || hostname === \"127.0.0.1\")\n )\n return true;\n\n return false;\n } catch {\n return false;\n }\n}\n\n/** Get or create persistent anonymous ID */\nfunction getOrCreateAnonId(): string {\n if (!hasDom()) return \"\";\n\n try {\n let id = localStorage.getItem(STORAGE_KEYS.anonId);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(STORAGE_KEYS.anonId, id);\n }\n return id;\n } catch {\n // localStorage might be blocked\n return crypto.randomUUID();\n }\n}\n\n/** Collect UTM params from current page URL */\nfunction getUtmParams(): Record<string, string> {\n if (!hasDom()) return {};\n\n const params: Record<string, string> = {};\n const searchParams = new URLSearchParams(window.location.search);\n\n for (const key of UTM_PARAMS) {\n const value = searchParams.get(key);\n if (value) {\n params[key] = value;\n }\n }\n\n return params;\n}\n\n/** Build iframe URL with all params */\nfunction buildIframeUrl(\n researchId: string,\n type: EmbedType,\n host: string,\n customParams?: Record<string, string>,\n brand?: { light?: BrandColors; dark?: BrandColors },\n themeOverride?: \"dark\" | \"light\" | \"system\"\n): string {\n const url = new URL(`${host}/interview/${researchId}`);\n\n // Base embed params\n url.searchParams.set(PARAM_KEYS.embed, PARAM_VALUES.true);\n url.searchParams.set(PARAM_KEYS.embedType, type === \"float\" ? \"chat\" : type);\n\n // Detect and pass system theme preference (can be overridden)\n if (hasDom()) {\n const isDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n if (themeOverride && themeOverride !== THEME_VALUES.system) {\n url.searchParams.set(PARAM_KEYS.theme, themeOverride);\n } else {\n url.searchParams.set(\n PARAM_KEYS.theme,\n isDark ? THEME_VALUES.dark : THEME_VALUES.light\n );\n }\n } else {\n // SSR fallback\n url.searchParams.set(PARAM_KEYS.theme, themeOverride || THEME_VALUES.light);\n }\n\n // Auto-forward UTM params from parent\n const utmParams = getUtmParams();\n for (const [key, value] of Object.entries(utmParams)) {\n url.searchParams.set(key, value);\n }\n\n // Helper to set param only if color is valid\n const setColor = (key: string, color: string | undefined) => {\n if (!color) return;\n const normalized = normalizeHex(color);\n if (normalized) url.searchParams.set(key, normalized);\n };\n\n // Add brand colors using short keys\n if (brand?.light) {\n setColor(BRAND_KEYS.primary, brand.light.primary);\n setColor(BRAND_KEYS.secondary, brand.light.secondary);\n setColor(BRAND_KEYS.bg, brand.light.bg);\n setColor(BRAND_KEYS.text, brand.light.text);\n }\n\n // Add dark mode brand colors\n if (brand?.dark) {\n setColor(BRAND_KEYS.darkPrimary, brand.dark.primary);\n setColor(BRAND_KEYS.darkSecondary, brand.dark.secondary);\n setColor(BRAND_KEYS.darkBg, brand.dark.bg);\n setColor(BRAND_KEYS.darkText, brand.dark.text);\n }\n\n // Add custom params, filtering out reserved keys\n if (customParams) {\n for (const [key, value] of Object.entries(customParams)) {\n if (!RESERVED_PARAMS.has(key)) {\n url.searchParams.set(key, value);\n }\n }\n }\n\n return url.toString();\n}\n\nexport function createIframe(\n researchId: string,\n type: EmbedType,\n host: string,\n params?: Record<string, string>,\n brand?: { light?: BrandColors; dark?: BrandColors },\n themeOverride?: \"dark\" | \"light\" | \"system\"\n): HTMLIFrameElement {\n if (!hasDom()) {\n // Return a stub for SSR\n return {} as HTMLIFrameElement;\n }\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = buildIframeUrl(\n researchId,\n type,\n host,\n params,\n brand,\n themeOverride\n );\n iframe.setAttribute(\"allow\", \"microphone; camera\");\n iframe.setAttribute(\"allowfullscreen\", \"true\");\n iframe.setAttribute(\n \"sandbox\",\n \"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n );\n iframe.setAttribute(\"data-perspective\", \"true\");\n iframe.style.cssText = \"border:none;\";\n\n return iframe;\n}\n\nexport function setupMessageListener(\n researchId: string,\n config: Partial<EmbedConfig>,\n iframe: HTMLIFrameElement,\n host: string,\n options?: { skipResize?: boolean }\n): () => void {\n if (!hasDom()) {\n return () => {};\n }\n\n const handler = (event: MessageEvent<EmbedMessage>) => {\n // Security: Only accept messages from our embed host and from the expected iframe\n if (event.origin !== host) return;\n if (event.source !== iframe.contentWindow) return;\n\n // Only process messages from our embed\n if (typeof event.data?.type !== \"string\") return;\n if (!event.data.type.startsWith(\"perspective:\")) return;\n if (event.data.researchId !== researchId) return;\n\n switch (event.data.type) {\n case MESSAGE_TYPES.ready:\n // Send scrollbar styles when iframe is ready\n sendScrollbarStyles(iframe, host);\n // Send anon_id for anonymous auth\n sendMessage(iframe, host, {\n type: MESSAGE_TYPES.anonId,\n anonId: getOrCreateAnonId(),\n });\n // Send init message with version/features for handshake\n sendMessage(iframe, host, {\n type: MESSAGE_TYPES.init,\n version: SDK_VERSION,\n features: CURRENT_FEATURES,\n researchId,\n });\n config.onReady?.();\n break;\n\n case MESSAGE_TYPES.resize:\n // Auto-resize iframe height (skip for fixed-container embeds)\n if (!options?.skipResize) {\n iframe.style.height = `${event.data.height}px`;\n }\n break;\n\n case MESSAGE_TYPES.submit:\n config.onSubmit?.({ researchId });\n break;\n\n case MESSAGE_TYPES.close:\n config.onClose?.();\n break;\n\n case MESSAGE_TYPES.error: {\n const error = new Error(\n event.data.error\n ) as import(\"./types\").EmbedError;\n error.code =\n (event.data.code as import(\"./types\").ErrorCode) || \"UNKNOWN\";\n\n // Always log critical errors to console\n if (error.code === ERROR_CODES.SDK_OUTDATED) {\n console.error(\n \"[Perspective] SDK version outdated. Please update @perspective-ai/sdk to the latest version.\",\n error.message\n );\n } else {\n console.error(\"[Perspective] Embed error:\", error.message);\n }\n\n config.onError?.(error);\n break;\n }\n\n case MESSAGE_TYPES.redirect:\n const redirectUrl = event.data.url;\n // Security: Only allow http(s) and localhost URLs\n if (!isAllowedRedirectUrl(redirectUrl)) {\n console.warn(\n \"[Perspective] Blocked unsafe redirect URL:\",\n redirectUrl\n );\n return;\n }\n if (config.onNavigate) {\n config.onNavigate(redirectUrl);\n } else {\n // Fallback: auto-navigate parent page\n window.location.href = redirectUrl;\n }\n break;\n }\n };\n\n window.addEventListener(\"message\", handler);\n return () => window.removeEventListener(\"message\", handler);\n}\n\n/** Send a message to the embed iframe */\nexport function sendMessage(\n iframe: HTMLIFrameElement,\n host: string,\n message: { type: string; [key: string]: unknown }\n): void {\n if (!hasDom()) return;\n iframe.contentWindow?.postMessage(message, host);\n}\n\n/** Track all active iframes for theme change notifications */\nconst activeIframes = new Map<HTMLIFrameElement, string>();\n\nexport function registerIframe(\n iframe: HTMLIFrameElement,\n host: string\n): () => void {\n activeIframes.set(iframe, host);\n return () => {\n activeIframes.delete(iframe);\n if (activeIframes.size === 0) {\n teardownGlobalListeners();\n }\n };\n}\n\n/** Get scrollbar CSS styles */\nfunction getScrollbarStyles(): string {\n if (!hasDom()) return \"\";\n\n const isDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n const borderColor = isDark ? \"hsl(217 33% 17%)\" : \"hsl(240 6% 90%)\";\n\n return `\n * {\n scrollbar-width: thin;\n scrollbar-color: ${borderColor} transparent;\n }\n *::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n }\n *::-webkit-scrollbar-track {\n background: transparent;\n }\n *::-webkit-scrollbar-thumb {\n background-color: ${borderColor};\n border-radius: 9999px;\n border: 2px solid transparent;\n background-clip: padding-box;\n }\n *::-webkit-scrollbar-thumb:hover {\n background-color: color-mix(in srgb, ${borderColor} 80%, currentColor);\n }\n `;\n}\n\n/** Send scrollbar styles to an iframe */\nexport function sendScrollbarStyles(\n iframe: HTMLIFrameElement,\n host: string\n): void {\n const styles = getScrollbarStyles();\n sendMessage(iframe, host, {\n type: MESSAGE_TYPES.injectStyles,\n styles,\n });\n}\n\n/** Notify all active iframes of theme change */\nexport function notifyThemeChange(): void {\n if (!hasDom()) return;\n\n const isDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n activeIframes.forEach((host, iframe) => {\n const message = {\n type: MESSAGE_TYPES.themeChange,\n theme: isDark ? THEME_VALUES.dark : THEME_VALUES.light,\n };\n sendMessage(iframe, host, message);\n sendScrollbarStyles(iframe, host);\n });\n}\n\nlet themeListener: ((e: MediaQueryListEvent) => void) | null = null;\nlet themeMediaQuery: MediaQueryList | null = null;\nlet globalMessageHandler: ((event: MessageEvent) => void) | null = null;\nlet globalListenersInitialized = false;\n\nfunction setupThemeListener(): void {\n if (themeListener || !hasDom()) return;\n\n themeMediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n themeListener = () => notifyThemeChange();\n themeMediaQuery.addEventListener(\"change\", themeListener);\n}\n\nfunction teardownThemeListener(): void {\n if (themeListener && themeMediaQuery) {\n themeMediaQuery.removeEventListener(\"change\", themeListener);\n themeListener = null;\n themeMediaQuery = null;\n }\n}\n\nfunction setupGlobalListeners(): void {\n if (!hasDom() || globalMessageHandler) return;\n\n setupThemeListener();\n\n globalMessageHandler = (event: MessageEvent) => {\n if (!event.data?.type?.startsWith(\"perspective:\")) return;\n if (event.data.type === MESSAGE_TYPES.requestScrollbarStyles) {\n const iframes = Array.from(\n document.querySelectorAll(\"iframe[data-perspective]\")\n );\n const sourceIframe = iframes.find(\n (iframe) => (iframe as HTMLIFrameElement).contentWindow === event.source\n ) as HTMLIFrameElement | undefined;\n if (sourceIframe) {\n const host = activeIframes.get(sourceIframe);\n if (host && event.origin === host) {\n sendScrollbarStyles(sourceIframe, host);\n }\n }\n }\n };\n\n window.addEventListener(\"message\", globalMessageHandler);\n}\n\nfunction teardownGlobalListeners(): void {\n if (globalMessageHandler) {\n window.removeEventListener(\"message\", globalMessageHandler);\n globalMessageHandler = null;\n }\n teardownThemeListener();\n globalListenersInitialized = false;\n}\n\nexport function ensureGlobalListeners(): void {\n if (globalListenersInitialized) return;\n globalListenersInitialized = true;\n setupGlobalListeners();\n}\n","/**\n * Loading indicator for embed iframes\n * SSR-safe - returns no-op on server\n */\n\nimport type { BrandColors, ThemeValue } from \"./types\";\nimport { hasDom } from \"./config\";\nimport { resolveTheme, hexToRgba } from \"./utils\";\n\n/** Default colors matching codebase theme */\nconst DEFAULT_COLORS = {\n light: {\n bg: \"#ffffff\",\n primary: \"#7629C8\",\n },\n dark: {\n bg: \"#02040a\",\n primary: \"#B170FF\",\n },\n};\n\nexport interface LoadingOptions {\n /** Theme override: 'dark', 'light', or 'system' (uses system preference) */\n theme?: ThemeValue;\n /** Brand colors - uses primary color for spinner */\n brand?: {\n light?: BrandColors;\n dark?: BrandColors;\n };\n}\n\n/** Get colors for loading indicator based on theme and brand */\nfunction getLoadingColors(options?: LoadingOptions): {\n bg: string;\n primary: string;\n} {\n const theme = resolveTheme(options?.theme);\n const isDark = theme === \"dark\";\n\n // Get brand colors for current theme\n const brandColors = isDark ? options?.brand?.dark : options?.brand?.light;\n\n return {\n bg:\n brandColors?.bg ||\n (isDark ? DEFAULT_COLORS.dark.bg : DEFAULT_COLORS.light.bg),\n primary:\n brandColors?.primary ||\n (isDark ? DEFAULT_COLORS.dark.primary : DEFAULT_COLORS.light.primary),\n };\n}\n\nexport function createLoadingIndicator(options?: LoadingOptions): HTMLElement {\n // SSR safety - return empty div on server\n if (!hasDom()) {\n return { remove: () => {}, style: {} } as unknown as HTMLElement;\n }\n\n const colors = getLoadingColors(options);\n\n const container = document.createElement(\"div\");\n container.className = \"perspective-loading\";\n container.style.cssText = `\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: ${colors.bg};\n transition: opacity 0.3s ease;\n z-index: 1;\n `;\n\n // Create spinner (keyframes defined in styles.ts)\n const spinner = document.createElement(\"div\");\n spinner.style.cssText = `\n width: 2.5rem;\n height: 2.5rem;\n border: 3px solid ${hexToRgba(colors.primary, 0.15)};\n border-top-color: ${colors.primary};\n border-radius: 50%;\n animation: perspective-spin 0.8s linear infinite;\n `;\n\n container.appendChild(spinner);\n return container;\n}\n","/**\n * CSS styles injected by the embed script\n * SSR-safe - DOM access is guarded\n */\n\nimport { hasDom } from \"./config\";\n\nlet stylesInjected = false;\n\nconst LIGHT_THEME = `\n --perspective-overlay-bg: rgba(0, 0, 0, 0.5);\n --perspective-modal-bg: #ffffff;\n --perspective-modal-text: #151B23;\n --perspective-close-bg: rgba(0, 0, 0, 0.1);\n --perspective-close-text: #666666;\n --perspective-close-hover-bg: rgba(0, 0, 0, 0.2);\n --perspective-close-hover-text: #333333;\n --perspective-border: hsl(240 6% 90%);\n`;\n\nconst DARK_THEME = `\n --perspective-overlay-bg: rgba(0, 0, 0, 0.7);\n --perspective-modal-bg: #02040a;\n --perspective-modal-text: #ffffff;\n --perspective-close-bg: rgba(255, 255, 255, 0.1);\n --perspective-close-text: #a0a0a0;\n --perspective-close-hover-bg: rgba(255, 255, 255, 0.2);\n --perspective-close-hover-text: #ffffff;\n --perspective-border: hsl(217 33% 17%);\n`;\n\nexport function injectStyles(): void {\n if (!hasDom()) return;\n if (stylesInjected) return;\n stylesInjected = true;\n\n const style = document.createElement(\"style\");\n style.id = \"perspective-embed-styles\";\n style.textContent = `\n /* Theme-aware color variables */\n .perspective-embed-root, .perspective-light-theme {\n ${LIGHT_THEME}\n --perspective-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n --perspective-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);\n --perspective-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n --perspective-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n --perspective-radius: 1.2rem;\n --perspective-radius-sm: calc(var(--perspective-radius) - 4px);\n }\n\n /* Dark theme */\n .perspective-dark-theme {\n ${DARK_THEME}\n }\n\n /* System dark mode support */\n @media (prefers-color-scheme: dark) {\n .perspective-embed-root:not(.perspective-light-theme) {\n ${DARK_THEME}\n }\n }\n\n /* Scrollbar styling */\n .perspective-modal,\n .perspective-slider,\n .perspective-float-window,\n .perspective-chat-window {\n scrollbar-width: thin;\n scrollbar-color: var(--perspective-border) transparent;\n }\n\n .perspective-modal::-webkit-scrollbar,\n .perspective-slider::-webkit-scrollbar,\n .perspective-float-window::-webkit-scrollbar,\n .perspective-chat-window::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n }\n\n .perspective-modal::-webkit-scrollbar-track,\n .perspective-slider::-webkit-scrollbar-track,\n .perspective-float-window::-webkit-scrollbar-track,\n .perspective-chat-window::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .perspective-modal::-webkit-scrollbar-thumb,\n .perspective-slider::-webkit-scrollbar-thumb,\n .perspective-float-window::-webkit-scrollbar-thumb,\n .perspective-chat-window::-webkit-scrollbar-thumb {\n background-color: var(--perspective-border);\n border-radius: 9999px;\n border: 2px solid transparent;\n background-clip: padding-box;\n }\n\n .perspective-modal::-webkit-scrollbar-thumb:hover,\n .perspective-slider::-webkit-scrollbar-thumb:hover,\n .perspective-float-window::-webkit-scrollbar-thumb:hover,\n .perspective-chat-window::-webkit-scrollbar-thumb:hover {\n background-color: color-mix(in srgb, var(--perspective-border) 80%, currentColor);\n }\n\n /* Overlay for popup/modal */\n .perspective-overlay {\n position: fixed;\n inset: 0;\n background: var(--perspective-overlay-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n animation: perspective-fade-in 0.2s ease-out;\n }\n\n @keyframes perspective-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n @keyframes perspective-spin {\n to { transform: rotate(360deg); }\n }\n\n /* Modal container */\n .perspective-modal {\n position: relative;\n width: 90%;\n max-width: 600px;\n height: 80vh;\n max-height: 700px;\n background: var(--perspective-modal-bg);\n color: var(--perspective-modal-text);\n border-radius: var(--perspective-radius);\n overflow: hidden;\n box-shadow: var(--perspective-shadow-xl);\n animation: perspective-slide-up 0.3s ease-out;\n }\n\n @keyframes perspective-slide-up {\n from {\n opacity: 0;\n transform: translateY(20px) scale(0.95);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n .perspective-modal iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n /* Close button */\n .perspective-close {\n position: absolute;\n top: 1rem;\n right: 1.5rem;\n width: 2rem;\n height: 2rem;\n border: none;\n background: var(--perspective-close-bg);\n color: var(--perspective-close-text);\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1rem;\n z-index: 10;\n transition: background-color 0.2s ease, color 0.2s ease;\n }\n\n .perspective-close:hover {\n background: var(--perspective-close-hover-bg);\n color: var(--perspective-close-hover-text);\n }\n\n .perspective-close:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 2px;\n }\n\n .perspective-close svg {\n width: 1rem;\n height: 1rem;\n stroke-width: 2;\n }\n\n /* Slider drawer */\n .perspective-slider {\n position: fixed;\n top: 0;\n right: 0;\n width: 100%;\n max-width: 450px;\n height: 100%;\n background: var(--perspective-modal-bg);\n color: var(--perspective-modal-text);\n box-shadow: var(--perspective-shadow-xl);\n z-index: 9999;\n animation: perspective-slide-in 0.3s ease-out;\n }\n\n @keyframes perspective-slide-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n\n .perspective-slider iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n .perspective-slider .perspective-close {\n top: 1rem;\n right: 2rem;\n }\n\n /* Slider backdrop */\n .perspective-slider-backdrop {\n position: fixed;\n inset: 0;\n background: var(--perspective-overlay-bg);\n z-index: 9998;\n animation: perspective-fade-in 0.2s ease-out;\n }\n\n /* Float bubble (and legacy chat-bubble alias) */\n .perspective-float-bubble,\n .perspective-chat-bubble {\n position: fixed;\n bottom: 1.5rem;\n right: 1.5rem;\n width: 3.75rem;\n height: 3.75rem;\n border-radius: 50%;\n background: var(--perspective-float-bg, var(--perspective-chat-bg, #7629C8));\n color: white;\n border: none;\n cursor: pointer;\n box-shadow: var(--perspective-float-shadow, var(--perspective-chat-shadow, 0 4px 12px rgba(118, 41, 200, 0.4)));\n z-index: 9996;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n\n .perspective-float-bubble:hover,\n .perspective-chat-bubble:hover {\n transform: scale(1.05);\n box-shadow: var(--perspective-float-shadow-hover, var(--perspective-chat-shadow-hover, 0 6px 16px rgba(118, 41, 200, 0.5)));\n }\n\n .perspective-float-bubble:focus-visible,\n .perspective-chat-bubble:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 2px;\n }\n\n .perspective-float-bubble svg,\n .perspective-chat-bubble svg {\n width: 1.75rem;\n height: 1.75rem;\n stroke-width: 2;\n }\n\n /* Float window (and legacy chat-window alias) */\n .perspective-float-window,\n .perspective-chat-window {\n position: fixed;\n bottom: 6.25rem;\n right: 1.5rem;\n width: 380px;\n height: calc(100vh - 8.75rem);\n max-height: 600px;\n background: var(--perspective-modal-bg);\n color: var(--perspective-modal-text);\n border-radius: var(--perspective-radius);\n overflow: hidden;\n box-shadow: var(--perspective-shadow-xl);\n z-index: 9997;\n animation: perspective-float-open 0.3s ease-out;\n }\n\n @keyframes perspective-float-open {\n from {\n opacity: 0;\n transform: translateY(20px) scale(0.9);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n .perspective-float-window iframe,\n .perspective-chat-window iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n .perspective-float-window .perspective-close,\n .perspective-chat-window .perspective-close {\n top: 1rem;\n right: 1.5rem;\n }\n\n /* Fullpage */\n .perspective-fullpage {\n position: fixed;\n inset: 0;\n z-index: 9999;\n background: var(--perspective-modal-bg);\n }\n\n .perspective-fullpage iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n /* Responsive */\n @media (max-width: 640px) {\n .perspective-modal {\n width: 100%;\n height: 100%;\n max-width: none;\n max-height: none;\n border-radius: 0;\n }\n\n .perspective-slider {\n max-width: 100%;\n }\n\n .perspective-float-window,\n .perspective-chat-window {\n width: calc(100% - 2rem);\n right: 1rem;\n bottom: 5.625rem;\n height: calc(100vh - 7.5rem);\n }\n\n .perspective-float-bubble,\n .perspective-chat-bubble {\n bottom: 1rem;\n right: 1rem;\n }\n }\n\n @media (max-width: 450px) {\n .perspective-float-window,\n .perspective-chat-window {\n width: calc(100% - 1rem);\n right: 0.5rem;\n bottom: 5rem;\n height: calc(100vh - 6.5rem);\n }\n\n .perspective-float-bubble,\n .perspective-chat-bubble {\n bottom: 0.75rem;\n right: 0.75rem;\n width: 3.5rem;\n height: 3.5rem;\n }\n\n .perspective-float-bubble svg,\n .perspective-chat-bubble svg {\n width: 1.5rem;\n height: 1.5rem;\n }\n }\n `;\n\n document.head.appendChild(style);\n}\n\nexport const MIC_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <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\" />\n</svg>`;\n\n/** @deprecated Use MIC_ICON instead */\nexport const CHAT_ICON = MIC_ICON;\n\nexport const CLOSE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n</svg>`;\n","/**\n * Inline widget embed - renders directly in a container element\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, EmbedHandle } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles } from \"./styles\";\nimport { cn, getThemeClass } from \"./utils\";\n\ntype WidgetResources = {\n cleanup: () => void;\n unregister: () => void;\n wrapper: HTMLElement;\n};\n\nconst widgetResources = new WeakMap<HTMLIFrameElement, WidgetResources>();\n\nfunction createNoOpHandle(researchId: string, type: \"widget\"): EmbedHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n researchId,\n type,\n iframe: null,\n container: null,\n };\n}\n\nfunction createExistingWidgetHandle(\n container: HTMLElement,\n researchId: string\n): EmbedHandle {\n const existingWrapper = container.querySelector<HTMLElement>(\n \".perspective-embed-root\"\n );\n const existingIframe = container.querySelector<HTMLIFrameElement>(\n \"iframe[data-perspective]\"\n );\n\n let destroyed = false;\n\n const unmount = () => {\n if (destroyed) return;\n destroyed = true;\n\n if (existingIframe) {\n const resources = widgetResources.get(existingIframe);\n if (resources) {\n resources.cleanup();\n resources.unregister();\n widgetResources.delete(existingIframe);\n }\n }\n existingWrapper?.remove();\n };\n\n return {\n unmount,\n update: () => {},\n destroy: unmount,\n researchId,\n type: \"widget\" as const,\n iframe: existingIframe,\n container,\n };\n}\n\nexport function createWidget(\n container: HTMLElement | null,\n config: EmbedConfig\n): EmbedHandle {\n const { researchId } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom() || !container) {\n return createNoOpHandle(researchId, \"widget\");\n }\n\n // Idempotency check for React Strict Mode\n if (container.querySelector(\"iframe[data-perspective]\")) {\n return createExistingWidgetHandle(container, researchId);\n }\n\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create wrapper for positioning\n const wrapper = document.createElement(\"div\");\n wrapper.className = cn(\"perspective-embed-root\", getThemeClass(config.theme));\n wrapper.style.cssText =\n \"position:relative;width:100%;height:100%;min-height:500px;\";\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: config.theme,\n brand: config.brand,\n });\n wrapper.appendChild(loading);\n\n // Create iframe (hidden initially)\n const iframe = createIframe(\n researchId,\n \"widget\",\n host,\n config.params,\n config.brand,\n config.theme\n );\n iframe.style.width = \"100%\";\n iframe.style.height = \"100%\";\n iframe.style.minHeight = \"500px\";\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n wrapper.appendChild(iframe);\n container.appendChild(wrapper);\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n\n // Set up message listener with loading state handling\n const cleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n // Hide loading, show iframe\n loading.style.opacity = \"0\";\n iframe.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return currentConfig.onClose;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n // Register iframe for theme change notifications\n const unregisterIframe = registerIframe(iframe, host);\n\n widgetResources.set(iframe, {\n cleanup,\n unregister: unregisterIframe,\n wrapper,\n });\n\n let destroyed = false;\n\n const unmount = () => {\n if (destroyed) return;\n destroyed = true;\n\n cleanup();\n unregisterIframe();\n widgetResources.delete(iframe);\n wrapper.remove();\n };\n\n return {\n unmount,\n update: (options) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy: unmount,\n researchId,\n type: \"widget\" as const,\n iframe,\n container,\n };\n}\n","/**\n * Popup/modal embed - opens in a centered modal overlay\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, EmbedHandle } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles, CLOSE_ICON } from \"./styles\";\nimport { cn, getThemeClass } from \"./utils\";\n\nfunction createNoOpHandle(researchId: string): EmbedHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n researchId,\n type: \"popup\",\n iframe: null,\n container: null,\n };\n}\n\nexport function openPopup(config: EmbedConfig): EmbedHandle {\n const { researchId } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom()) {\n return createNoOpHandle(researchId);\n }\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create overlay\n const overlay = document.createElement(\"div\");\n overlay.className = cn(\n \"perspective-overlay perspective-embed-root\",\n getThemeClass(config.theme)\n );\n\n // Create modal container\n const modal = document.createElement(\"div\");\n modal.className = \"perspective-modal\";\n\n // Create close button\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"perspective-close\";\n closeBtn.innerHTML = CLOSE_ICON;\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: config.theme,\n brand: config.brand,\n });\n loading.style.borderRadius = \"16px\";\n\n // Create iframe (hidden initially)\n const iframe = createIframe(\n researchId,\n \"popup\",\n host,\n config.params,\n config.brand,\n config.theme\n );\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n modal.appendChild(closeBtn);\n modal.appendChild(loading);\n modal.appendChild(iframe);\n overlay.appendChild(modal);\n document.body.appendChild(overlay);\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n let isOpen = true;\n let messageCleanup: (() => void) | null = null;\n\n // Register iframe for theme change notifications\n const unregisterIframe = registerIframe(iframe, host);\n\n const destroy = () => {\n if (!isOpen) return;\n isOpen = false;\n messageCleanup?.();\n unregisterIframe();\n overlay.remove();\n document.removeEventListener(\"keydown\", escHandler);\n currentConfig.onClose?.();\n };\n\n // Set up message listener with loading state handling\n messageCleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n loading.style.opacity = \"0\";\n iframe.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return destroy;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n // Close handlers\n closeBtn.addEventListener(\"click\", destroy);\n overlay.addEventListener(\"click\", (e) => {\n if (e.target === overlay) destroy();\n });\n\n // ESC key closes\n const escHandler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n destroy();\n }\n };\n document.addEventListener(\"keydown\", escHandler);\n\n return {\n unmount: destroy,\n update: (options: Parameters<EmbedHandle[\"update\"]>[0]) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy,\n researchId,\n type: \"popup\" as const,\n iframe,\n container: overlay,\n };\n}\n","/**\n * Slider/drawer embed - slides in from the right\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, EmbedHandle } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles, CLOSE_ICON } from \"./styles\";\nimport { cn, getThemeClass } from \"./utils\";\n\nfunction createNoOpHandle(researchId: string): EmbedHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n researchId,\n type: \"slider\",\n iframe: null,\n container: null,\n };\n}\n\nexport function openSlider(config: EmbedConfig): EmbedHandle {\n const { researchId } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom()) {\n return createNoOpHandle(researchId);\n }\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create backdrop\n const backdrop = document.createElement(\"div\");\n backdrop.className = cn(\n \"perspective-slider-backdrop perspective-embed-root\",\n getThemeClass(config.theme)\n );\n\n // Create slider container\n const slider = document.createElement(\"div\");\n slider.className = cn(\n \"perspective-slider perspective-embed-root\",\n getThemeClass(config.theme)\n );\n\n // Create close button\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"perspective-close\";\n closeBtn.innerHTML = CLOSE_ICON;\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: config.theme,\n brand: config.brand,\n });\n\n // Create iframe (hidden initially)\n const iframe = createIframe(\n researchId,\n \"slider\",\n host,\n config.params,\n config.brand,\n config.theme\n );\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n slider.appendChild(closeBtn);\n slider.appendChild(loading);\n slider.appendChild(iframe);\n document.body.appendChild(backdrop);\n document.body.appendChild(slider);\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n let isOpen = true;\n let messageCleanup: (() => void) | null = null;\n\n // Register iframe for theme change notifications\n const unregisterIframe = registerIframe(iframe, host);\n\n const destroy = () => {\n if (!isOpen) return;\n isOpen = false;\n messageCleanup?.();\n unregisterIframe();\n slider.remove();\n backdrop.remove();\n document.removeEventListener(\"keydown\", escHandler);\n currentConfig.onClose?.();\n };\n\n // Set up message listener with loading state handling\n messageCleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n loading.style.opacity = \"0\";\n iframe.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return destroy;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n // Close handlers\n closeBtn.addEventListener(\"click\", destroy);\n backdrop.addEventListener(\"click\", destroy);\n\n // ESC key closes\n const escHandler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n destroy();\n }\n };\n document.addEventListener(\"keydown\", escHandler);\n\n return {\n unmount: destroy,\n update: (options: Parameters<EmbedHandle[\"update\"]>[0]) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy,\n researchId,\n type: \"slider\" as const,\n iframe,\n container: slider,\n };\n}\n","/**\n * Floating bubble embed - floating button that opens a chat window\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, FloatHandle, ThemeConfig } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles, MIC_ICON, CLOSE_ICON } from \"./styles\";\nimport { cn, getThemeClass, resolveIsDark } from \"./utils\";\n\ntype FloatConfig = EmbedConfig & { _themeConfig?: ThemeConfig };\n\nfunction createNoOpHandle(researchId: string): FloatHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n open: () => {},\n close: () => {},\n toggle: () => {},\n isOpen: false,\n researchId,\n type: \"float\",\n iframe: null,\n container: null,\n };\n}\n\nexport function createFloatBubble(config: FloatConfig): FloatHandle {\n const { researchId, _themeConfig, theme, brand } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom()) {\n return createNoOpHandle(researchId);\n }\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create bubble button\n const bubble = document.createElement(\"button\");\n bubble.className = cn(\n \"perspective-float-bubble perspective-embed-root\",\n getThemeClass(config.theme)\n );\n bubble.innerHTML = MIC_ICON;\n bubble.setAttribute(\"aria-label\", \"Open chat\");\n bubble.setAttribute(\"data-perspective\", \"float-bubble\");\n\n // Apply theme color if available\n if (_themeConfig || brand) {\n const isDark = resolveIsDark(theme);\n const bg = isDark\n ? (brand?.dark?.primary ?? _themeConfig?.darkPrimaryColor ?? \"#a78bfa\")\n : (brand?.light?.primary ?? _themeConfig?.primaryColor ?? \"#7c3aed\");\n bubble.style.setProperty(\"--perspective-float-bg\", bg);\n bubble.style.setProperty(\n \"--perspective-float-shadow\",\n `0 4px 12px ${bg}66`\n );\n bubble.style.setProperty(\n \"--perspective-float-shadow-hover\",\n `0 6px 16px ${bg}80`\n );\n bubble.style.backgroundColor = bg;\n bubble.style.boxShadow = `0 4px 12px ${bg}66`;\n }\n\n document.body.appendChild(bubble);\n\n let floatWindow: HTMLElement | null = null;\n let iframe: HTMLIFrameElement | null = null;\n let cleanup: (() => void) | null = null;\n let unregisterIframe: (() => void) | null = null;\n let isOpen = false;\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n\n const openFloat = () => {\n if (isOpen) return;\n isOpen = true;\n\n // Create float window\n floatWindow = document.createElement(\"div\");\n floatWindow.className = cn(\n \"perspective-float-window perspective-embed-root\",\n getThemeClass(currentConfig.theme)\n );\n\n // Create close button\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"perspective-close\";\n closeBtn.innerHTML = CLOSE_ICON;\n closeBtn.setAttribute(\"aria-label\", \"Close chat\");\n closeBtn.addEventListener(\"click\", closeFloat);\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: currentConfig.theme,\n brand: currentConfig.brand,\n });\n loading.style.borderRadius = \"16px\";\n\n // Create iframe (hidden initially)\n iframe = createIframe(\n researchId,\n \"float\",\n host,\n currentConfig.params,\n currentConfig.brand,\n currentConfig.theme\n );\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n floatWindow.appendChild(closeBtn);\n floatWindow.appendChild(loading);\n floatWindow.appendChild(iframe);\n document.body.appendChild(floatWindow);\n\n // Set up message listener with loading state handling\n cleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n loading.style.opacity = \"0\";\n iframe!.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return closeFloat;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n // Register iframe for theme change notifications\n if (iframe) {\n unregisterIframe = registerIframe(iframe, host);\n }\n\n // Update bubble icon to close\n bubble.innerHTML = CLOSE_ICON;\n bubble.setAttribute(\"aria-label\", \"Close chat\");\n };\n\n const closeFloat = () => {\n if (!isOpen) return;\n isOpen = false;\n\n cleanup?.();\n unregisterIframe?.();\n floatWindow?.remove();\n floatWindow = null;\n iframe = null;\n cleanup = null;\n unregisterIframe = null;\n\n // Restore bubble icon\n bubble.innerHTML = MIC_ICON;\n bubble.setAttribute(\"aria-label\", \"Open chat\");\n\n currentConfig.onClose?.();\n };\n\n // Toggle on bubble click\n bubble.addEventListener(\"click\", () => {\n if (isOpen) {\n closeFloat();\n } else {\n openFloat();\n }\n });\n\n const unmount = () => {\n closeFloat();\n bubble.remove();\n };\n\n return {\n unmount,\n update: (options: Parameters<FloatHandle[\"update\"]>[0]) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy: unmount,\n open: openFloat,\n close: closeFloat,\n toggle: () => {\n if (isOpen) {\n closeFloat();\n } else {\n openFloat();\n }\n },\n get isOpen() {\n return isOpen;\n },\n researchId,\n type: \"float\" as const,\n get iframe() {\n return iframe;\n },\n container: bubble,\n };\n}\n\n/** @deprecated Use createFloatBubble instead */\nexport const createChatBubble = createFloatBubble;\n","/**\n * Fullpage embed - takes over entire viewport\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, EmbedHandle } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles } from \"./styles\";\nimport { cn, getThemeClass } from \"./utils\";\n\nfunction createNoOpHandle(researchId: string): EmbedHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n researchId,\n type: \"fullpage\" as const,\n iframe: null,\n container: null,\n };\n}\n\nexport function createFullpage(config: EmbedConfig): EmbedHandle {\n const { researchId } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom()) {\n return createNoOpHandle(researchId);\n }\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create fullpage container\n const container = document.createElement(\"div\");\n container.className = cn(\n \"perspective-embed-root perspective-fullpage\",\n getThemeClass(config.theme)\n );\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: config.theme,\n brand: config.brand,\n });\n container.appendChild(loading);\n\n // Create iframe (hidden initially)\n const iframe = createIframe(\n researchId,\n \"fullpage\",\n host,\n config.params,\n config.brand,\n config.theme\n );\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n container.appendChild(iframe);\n document.body.appendChild(container);\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n let messageCleanup: (() => void) | null = null;\n\n // Register iframe for theme change notifications\n const unregisterIframe = registerIframe(iframe, host);\n\n const unmount = () => {\n messageCleanup?.();\n unregisterIframe();\n container.remove();\n currentConfig.onClose?.();\n };\n\n // Set up message listener\n messageCleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n loading.style.opacity = \"0\";\n iframe.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return unmount;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n return {\n unmount,\n update: (options) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy: unmount,\n researchId,\n type: \"fullpage\" as const,\n iframe,\n container,\n };\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["createNoOpHandle","destroy","createNoOpHandle","destroy","createNoOpHandle","createNoOpHandle"]}
1
+ {"version":3,"sources":["../src/browser.ts","../src/constants.ts","../src/config.ts","../src/utils.ts","../src/iframe.ts","../src/loading.ts","../src/styles.ts","../src/widget.ts","../src/popup.ts","../src/slider.ts","../src/float.ts","../src/fullpage.ts"],"sourcesContent":["/**\n * Perspective Embed SDK - Browser Entry\n *\n * CDN/Script Tag Entry Point - auto-init, attaches to window.Perspective\n *\n * Usage:\n * <script src=\"https://getperspective.ai/v1/perspective.js\"></script>\n *\n * <!-- Auto-init with data attributes -->\n * <div data-perspective-widget=\"research_xxx\"></div>\n * <button data-perspective-popup=\"research_xxx\">Open Survey</button>\n *\n * <!-- Or programmatic -->\n * <script>\n * Perspective.openPopup({ researchId: 'xxx' });\n * </script>\n */\n\nimport type {\n BrandColors,\n EmbedConfig,\n EmbedHandle,\n FloatHandle,\n ThemeConfig,\n} from \"./types\";\nimport { DATA_ATTRS, THEME_VALUES } from \"./constants\";\nimport { createWidget } from \"./widget\";\nimport { openPopup } from \"./popup\";\nimport { openSlider } from \"./slider\";\nimport { createFloatBubble, createChatBubble } from \"./float\";\nimport { createFullpage } from \"./fullpage\";\nimport { configure, getConfig, hasDom, getHost } from \"./config\";\nimport { resolveIsDark } from \"./utils\";\n\n// Track all active instances\nconst instances: Map<string, EmbedHandle | FloatHandle> = new Map();\n\n// Theme config cache\nconst configCache: Map<string, ThemeConfig> = new Map();\n\ntype ButtonStyleConfig = {\n themeConfig: ThemeConfig;\n theme?: EmbedConfig[\"theme\"];\n brand?: EmbedConfig[\"brand\"];\n};\nconst styledButtons = new Map<HTMLElement, ButtonStyleConfig>();\nlet buttonThemeMediaQuery: MediaQueryList | null = null;\n\nconst DEFAULT_THEME: ThemeConfig = {\n primaryColor: \"#7c3aed\",\n textColor: \"#ffffff\",\n darkPrimaryColor: \"#a78bfa\",\n darkTextColor: \"#ffffff\",\n};\n\n/**\n * Fetch theme config from API (cached)\n */\nasync function fetchConfig(researchId: string): Promise<ThemeConfig> {\n if (configCache.has(researchId)) {\n return configCache.get(researchId)!;\n }\n\n try {\n const host = getHost();\n const res = await fetch(`${host}/api/v1/embed/config/${researchId}`);\n if (!res.ok) return DEFAULT_THEME;\n const config = await res.json();\n configCache.set(researchId, config);\n return config;\n } catch {\n return DEFAULT_THEME;\n }\n}\n\n/**\n * Apply theme styles to a button element\n */\nfunction styleButton(\n el: HTMLElement,\n themeConfig: ThemeConfig,\n options?: { theme?: EmbedConfig[\"theme\"]; brand?: EmbedConfig[\"brand\"] }\n): void {\n if (el.hasAttribute(DATA_ATTRS.noStyle)) return;\n\n styledButtons.set(el, {\n themeConfig,\n theme: options?.theme,\n brand: options?.brand,\n });\n\n updateButtonTheme(el, {\n themeConfig,\n theme: options?.theme,\n brand: options?.brand,\n });\n}\n\n/**\n * Update button styles based on theme\n */\nfunction updateButtonTheme(el: HTMLElement, config: ButtonStyleConfig): void {\n const { themeConfig, theme, brand } = config;\n const isDark = resolveIsDark(theme);\n\n const bg = isDark\n ? (brand?.dark?.primary ?? themeConfig.darkPrimaryColor)\n : (brand?.light?.primary ?? themeConfig.primaryColor);\n const text = isDark\n ? (brand?.dark?.text ?? themeConfig.darkTextColor)\n : (brand?.light?.text ?? themeConfig.textColor);\n\n el.style.backgroundColor = bg;\n el.style.color = text;\n el.style.padding = \"10px 20px\";\n el.style.border = \"none\";\n el.style.borderRadius = \"8px\";\n el.style.fontWeight = \"500\";\n el.style.cursor = \"pointer\";\n}\n\n/**\n * Update all styled buttons when theme changes\n */\nfunction updateAllButtonThemes(): void {\n styledButtons.forEach((config, el) => {\n if (document.contains(el)) {\n updateButtonTheme(el, config);\n } else {\n styledButtons.delete(el);\n }\n });\n}\n\nlet buttonThemeListener: ((e: MediaQueryListEvent) => void) | null = null;\n\nfunction setupButtonThemeListener(): void {\n if (buttonThemeListener || !hasDom()) return;\n\n buttonThemeMediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n buttonThemeListener = () => updateAllButtonThemes();\n buttonThemeMediaQuery.addEventListener(\"change\", buttonThemeListener);\n}\n\nfunction teardownButtonThemeListener(): void {\n if (buttonThemeListener && buttonThemeMediaQuery) {\n buttonThemeMediaQuery.removeEventListener(\"change\", buttonThemeListener);\n buttonThemeListener = null;\n buttonThemeMediaQuery = null;\n }\n}\n\n/**\n * Parse params from data attribute (format: \"key1=value1,key2=value2\")\n */\nfunction parseParamsAttr(el: HTMLElement): Record<string, string> | undefined {\n const paramsStr = el.getAttribute(DATA_ATTRS.params);\n if (!paramsStr) return undefined;\n\n const params: Record<string, string> = {};\n for (const pair of paramsStr.split(\",\")) {\n const [key, ...valueParts] = pair.trim().split(\"=\");\n if (key) {\n params[key.trim()] = valueParts.join(\"=\").trim();\n }\n }\n return Object.keys(params).length > 0 ? params : undefined;\n}\n\n/**\n * Parse brand colors from data attribute (format: \"primary=#xxx,bg=#yyy\")\n */\nfunction parseBrandAttr(attrValue: string | null): BrandColors | undefined {\n if (!attrValue) return undefined;\n\n const colors: BrandColors = {};\n for (const pair of attrValue.split(\",\")) {\n const [key, ...valueParts] = pair.trim().split(\"=\");\n if (key && valueParts.length > 0) {\n const value = valueParts.join(\"=\").trim();\n if (value) {\n const k = key.trim() as keyof BrandColors;\n if (\n k === \"primary\" ||\n k === \"secondary\" ||\n k === \"bg\" ||\n k === \"text\"\n ) {\n colors[k] = value;\n }\n }\n }\n }\n return Object.keys(colors).length > 0 ? colors : undefined;\n}\n\n/**\n * Extract brand config and theme from element attributes\n */\nfunction extractBrandConfig(\n el: HTMLElement\n): Pick<EmbedConfig, \"brand\" | \"theme\"> {\n const light = parseBrandAttr(el.getAttribute(DATA_ATTRS.brand));\n const dark = parseBrandAttr(el.getAttribute(DATA_ATTRS.brandDark));\n const themeAttr = el.getAttribute(DATA_ATTRS.theme);\n\n const config: Pick<EmbedConfig, \"brand\" | \"theme\"> = {};\n\n if (light || dark) {\n config.brand = {};\n if (light) config.brand.light = light;\n if (dark) config.brand.dark = dark;\n }\n\n if (\n themeAttr === THEME_VALUES.dark ||\n themeAttr === THEME_VALUES.light ||\n themeAttr === THEME_VALUES.system\n ) {\n config.theme = themeAttr;\n }\n\n return config;\n}\n\n/**\n * Initialize an embed programmatically\n */\nfunction init(config: EmbedConfig): EmbedHandle | FloatHandle {\n const { researchId } = config;\n // Normalize legacy \"chat\" type to \"float\"\n const type = config.type === \"chat\" ? \"float\" : (config.type ?? \"widget\");\n\n // Destroy existing instance for this research\n if (instances.has(researchId)) {\n instances.get(researchId)!.unmount();\n instances.delete(researchId);\n }\n\n let instance: EmbedHandle | FloatHandle;\n\n switch (type) {\n case \"popup\":\n instance = openPopup(config);\n break;\n case \"slider\":\n instance = openSlider(config);\n break;\n case \"float\":\n instance = createFloatBubble(config);\n break;\n case \"fullpage\":\n instance = createFullpage(config);\n break;\n default:\n throw new Error(\n `Unknown embed type \"${type}\". Valid types: popup, slider, float, fullpage (use init()), or widget (use mount()).`\n );\n }\n\n instances.set(researchId, instance);\n return instance;\n}\n\n/**\n * Mount a widget into a container element\n */\nfunction mount(\n container: HTMLElement | string,\n config: EmbedConfig\n): EmbedHandle {\n const { researchId } = config;\n const type = config.type === \"chat\" ? \"float\" : (config.type ?? \"widget\");\n\n const el =\n typeof container === \"string\"\n ? document.querySelector<HTMLElement>(container)\n : container;\n\n if (!el) {\n throw new Error(`Container not found: ${container}`);\n }\n\n // Destroy existing instance\n if (instances.has(researchId)) {\n instances.get(researchId)!.unmount();\n instances.delete(researchId);\n }\n\n let instance: EmbedHandle;\n\n switch (type) {\n case \"widget\":\n instance = createWidget(el, config);\n break;\n default:\n // For popup/slider/float, just use init - container not used\n instance = init({ ...config, type }) as EmbedHandle;\n return instance;\n }\n\n instances.set(researchId, instance);\n return instance;\n}\n\n/**\n * Destroy an embed instance\n */\nfunction destroy(researchId: string): void {\n const instance = instances.get(researchId);\n if (instance) {\n instance.unmount();\n instances.delete(researchId);\n }\n}\n\nfunction destroyAll(): void {\n instances.forEach((instance) => instance.unmount());\n instances.clear();\n styledButtons.clear();\n teardownButtonThemeListener();\n}\n\n/**\n * Auto-initialize embeds from data attributes\n */\nfunction autoInit(): void {\n if (!hasDom()) return;\n\n setupButtonThemeListener();\n\n // Widget embeds\n document\n .querySelectorAll<HTMLElement>(`[${DATA_ATTRS.widget}]`)\n .forEach((el) => {\n const researchId = el.getAttribute(DATA_ATTRS.widget);\n if (researchId && !instances.has(researchId)) {\n const params = parseParamsAttr(el);\n const brandConfig = extractBrandConfig(el);\n mount(el, { researchId, type: \"widget\", params, ...brandConfig });\n }\n });\n\n // Fullpage embeds\n document\n .querySelectorAll<HTMLElement>(`[${DATA_ATTRS.fullpage}]`)\n .forEach((el) => {\n const researchId = el.getAttribute(DATA_ATTRS.fullpage);\n if (researchId && !instances.has(researchId)) {\n const params = parseParamsAttr(el);\n const brandConfig = extractBrandConfig(el);\n init({ researchId, type: \"fullpage\", params, ...brandConfig });\n }\n });\n\n // Popup triggers\n document\n .querySelectorAll<HTMLElement>(`[${DATA_ATTRS.popup}]`)\n .forEach((el) => {\n if (el.hasAttribute(\"data-perspective-initialized\")) return;\n el.setAttribute(\"data-perspective-initialized\", \"true\");\n\n const researchId = el.getAttribute(DATA_ATTRS.popup);\n if (researchId) {\n const params = parseParamsAttr(el);\n const brandConfig = extractBrandConfig(el);\n styleButton(el, DEFAULT_THEME, brandConfig);\n el.addEventListener(\"click\", (e) => {\n e.preventDefault();\n init({ researchId, type: \"popup\", params, ...brandConfig });\n });\n fetchConfig(researchId).then((config) => {\n styleButton(el, config, brandConfig);\n });\n }\n });\n\n // Slider triggers\n document\n .querySelectorAll<HTMLElement>(`[${DATA_ATTRS.slider}]`)\n .forEach((el) => {\n if (el.hasAttribute(\"data-perspective-initialized\")) return;\n el.setAttribute(\"data-perspective-initialized\", \"true\");\n\n const researchId = el.getAttribute(DATA_ATTRS.slider);\n if (researchId) {\n const params = parseParamsAttr(el);\n const brandConfig = extractBrandConfig(el);\n styleButton(el, DEFAULT_THEME, brandConfig);\n el.addEventListener(\"click\", (e) => {\n e.preventDefault();\n init({ researchId, type: \"slider\", params, ...brandConfig });\n });\n fetchConfig(researchId).then((config) => {\n styleButton(el, config, brandConfig);\n });\n }\n });\n\n // Float bubble - supports both data-perspective-float and data-perspective-chat (legacy)\n const floatSelector = `[${DATA_ATTRS.float}], [${DATA_ATTRS.chat}]`;\n const floatEl = document.querySelector<HTMLElement>(floatSelector);\n if (floatEl) {\n const researchId =\n floatEl.getAttribute(DATA_ATTRS.float) ||\n floatEl.getAttribute(DATA_ATTRS.chat);\n if (researchId && !instances.has(researchId)) {\n const params = parseParamsAttr(floatEl);\n const brandConfig = extractBrandConfig(floatEl);\n init({\n researchId,\n type: \"float\",\n params,\n ...brandConfig,\n _themeConfig: DEFAULT_THEME,\n } as EmbedConfig & { _themeConfig: ThemeConfig });\n\n fetchConfig(researchId).then((config) => {\n // Update bubble color with fetched theme\n const bubble = document.querySelector<HTMLElement>(\n '[data-perspective=\"float-bubble\"]'\n );\n if (bubble && !floatEl.hasAttribute(DATA_ATTRS.noStyle)) {\n const isDark = resolveIsDark(brandConfig.theme);\n const bg = isDark\n ? (brandConfig.brand?.dark?.primary ?? config.darkPrimaryColor)\n : (brandConfig.brand?.light?.primary ?? config.primaryColor);\n bubble.style.setProperty(\"--perspective-float-bg\", bg);\n bubble.style.setProperty(\n \"--perspective-float-shadow\",\n `0 4px 12px ${bg}66`\n );\n bubble.style.setProperty(\n \"--perspective-float-shadow-hover\",\n `0 6px 16px ${bg}80`\n );\n bubble.style.backgroundColor = bg;\n bubble.style.boxShadow = `0 4px 12px ${bg}66`;\n }\n });\n }\n }\n}\n\n// Build the public API\nconst Perspective = {\n // Configuration\n configure,\n getConfig,\n\n // Instance management\n init,\n mount,\n destroy,\n destroyAll,\n autoInit,\n\n // Direct creation functions (primary API)\n createWidget,\n openPopup,\n openSlider,\n createFloatBubble,\n createFullpage,\n\n // Legacy alias\n createChatBubble,\n};\n\ndeclare global {\n interface Window {\n __PERSPECTIVE_SDK_INITIALIZED__?: boolean;\n Perspective?: typeof Perspective;\n }\n}\n\n// Prevent duplicate initialization when script is loaded multiple times\n// (e.g., SPAs, tag managers, hot-reload, or accidental double-include).\n// Without this guard, each script evaluation would register new listeners\n// and create isolated module state, causing memory leaks and duplicate handlers.\nif (hasDom() && !window.__PERSPECTIVE_SDK_INITIALIZED__) {\n window.__PERSPECTIVE_SDK_INITIALIZED__ = true;\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", autoInit, { once: true });\n } else {\n autoInit();\n }\n\n window.Perspective = Perspective;\n}\n\n// Export for module usage\nexport {\n configure,\n getConfig,\n init,\n mount,\n destroy,\n destroyAll,\n autoInit,\n createWidget,\n openPopup,\n openSlider,\n createFloatBubble,\n createChatBubble,\n createFullpage,\n};\n\nexport default Perspective;\n","/**\n * Shared constants for Perspective Embed SDK\n * This file is SSR-safe - no DOM access at import time\n * Used by both SDK bundle and the main Perspective app\n */\n\n// ============================================================================\n// SDK Version & Features\n// ============================================================================\n\n/** SDK version for handshake protocol */\nexport const SDK_VERSION = \"1.0.0\";\n\n/** Feature flags as bitset for version negotiation */\nexport const FEATURES = {\n RESIZE: 1 << 0, // 0b0001\n THEME_SYNC: 1 << 1, // 0b0010\n ANON_ID: 1 << 2, // 0b0100\n SCROLLBAR_STYLES: 1 << 3, // 0b1000\n} as const;\n\n/** Current SDK feature set */\nexport const CURRENT_FEATURES =\n FEATURES.RESIZE |\n FEATURES.THEME_SYNC |\n FEATURES.ANON_ID |\n FEATURES.SCROLLBAR_STYLES;\n\n// ============================================================================\n// URL Parameter Keys\n// ============================================================================\n\n// Embed parameters\nexport const PARAM_KEYS = {\n embed: \"embed\",\n embedType: \"embed_type\",\n theme: \"theme\",\n} as const;\n\nexport type ParamKey = (typeof PARAM_KEYS)[keyof typeof PARAM_KEYS];\n\n// ============================================================================\n// Brand Color Keys\n// ============================================================================\n\nexport const BRAND_KEYS = {\n // Light mode\n primary: \"brand.primary\",\n secondary: \"brand.secondary\",\n bg: \"brand.bg\",\n text: \"brand.text\",\n\n // Dark mode\n darkPrimary: \"brand.dark.primary\",\n darkSecondary: \"brand.dark.secondary\",\n darkBg: \"brand.dark.bg\",\n darkText: \"brand.dark.text\",\n} as const;\n\nexport type BrandKey = (typeof BRAND_KEYS)[keyof typeof BRAND_KEYS];\n\n// ============================================================================\n// UTM Parameters (auto-forwarded from parent URL)\n// ============================================================================\n\nexport const UTM_PARAMS = [\n \"utm_source\",\n \"utm_medium\",\n \"utm_campaign\",\n \"utm_term\",\n \"utm_content\",\n] as const;\n\nexport type UtmParam = (typeof UTM_PARAMS)[number];\n\n// ============================================================================\n// Reserved Parameters (cannot be overridden via custom params)\n// ============================================================================\n\nexport const RESERVED_PARAMS: Set<string> = new Set([\n PARAM_KEYS.embed,\n PARAM_KEYS.embedType,\n PARAM_KEYS.theme,\n BRAND_KEYS.primary,\n BRAND_KEYS.secondary,\n BRAND_KEYS.bg,\n BRAND_KEYS.text,\n BRAND_KEYS.darkPrimary,\n BRAND_KEYS.darkSecondary,\n BRAND_KEYS.darkBg,\n BRAND_KEYS.darkText,\n ...UTM_PARAMS,\n]);\n\n// ============================================================================\n// Data Attributes (HTML declarative initialization)\n// ============================================================================\n\nexport const DATA_ATTRS = {\n widget: \"data-perspective-widget\",\n popup: \"data-perspective-popup\",\n slider: \"data-perspective-slider\",\n float: \"data-perspective-float\", // Primary name\n chat: \"data-perspective-chat\", // Legacy alias\n fullpage: \"data-perspective-fullpage\",\n params: \"data-perspective-params\",\n brand: \"data-perspective-brand\",\n brandDark: \"data-perspective-brand-dark\",\n theme: \"data-perspective-theme\",\n noStyle: \"data-perspective-no-style\",\n} as const;\n\nexport type DataAttr = (typeof DATA_ATTRS)[keyof typeof DATA_ATTRS];\n\n// ============================================================================\n// PostMessage Event Types\n// ============================================================================\n\nexport const MESSAGE_TYPES = {\n // SDK -> Iframe (initialization)\n init: \"perspective:init\",\n\n // Iframe -> SDK\n ready: \"perspective:ready\",\n resize: \"perspective:resize\",\n submit: \"perspective:submit\",\n close: \"perspective:close\",\n error: \"perspective:error\",\n redirect: \"perspective:redirect\",\n\n // SDK -> Iframe (internal)\n anonId: \"perspective:anon-id\",\n injectStyles: \"perspective:inject-styles\",\n themeChange: \"perspective:theme-change\",\n\n // Iframe -> SDK (internal)\n requestScrollbarStyles: \"perspective:request-scrollbar-styles\",\n} as const;\n\nexport type MessageType = (typeof MESSAGE_TYPES)[keyof typeof MESSAGE_TYPES];\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\nexport const ERROR_CODES = {\n SDK_OUTDATED: \"SDK_OUTDATED\",\n INVALID_RESEARCH: \"INVALID_RESEARCH\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\n// ============================================================================\n// Param Values (for boolean-like string params)\n// ============================================================================\n\nexport const PARAM_VALUES = {\n true: \"true\",\n false: \"false\",\n} as const;\n\n// ============================================================================\n// Theme Values\n// ============================================================================\n\nexport const THEME_VALUES = {\n dark: \"dark\",\n light: \"light\",\n system: \"system\",\n} as const;\n\nexport type ThemeValue = (typeof THEME_VALUES)[keyof typeof THEME_VALUES];\n\n// ============================================================================\n// Interview Mode Values (for mode param)\n// ============================================================================\n\nexport const MODE_VALUES = {\n preview: \"preview\",\n restart: \"restart\",\n normal: \"normal\",\n simulated: \"simulated\",\n} as const;\n\nexport type ModeValue = (typeof MODE_VALUES)[keyof typeof MODE_VALUES];\n\n// ============================================================================\n// localStorage Keys\n// ============================================================================\n\nexport const STORAGE_KEYS = {\n anonId: \"perspective-anon-id\",\n} as const;\n","/**\n * Embed SDK configuration\n * SSR-safe - DOM access is guarded and lazy\n */\n\nimport type { SDKConfig } from \"./types\";\n\n/** Default production host */\nconst DEFAULT_HOST = \"https://getperspective.ai\";\n\n/** Global SDK configuration - can be set before creating embeds */\nlet globalConfig: SDKConfig = {};\n\n/**\n * Configure the SDK globally\n * Call this before creating any embeds if you need to override defaults\n */\nexport function configure(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get the current SDK configuration\n */\nexport function getConfig(): SDKConfig {\n return { ...globalConfig };\n}\n\n/**\n * Check if DOM is available (SSR safety)\n */\nexport function hasDom(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\nfunction normalizeToOrigin(host: string): string {\n try {\n return new URL(host).origin;\n } catch {\n return host;\n }\n}\n\nexport function getHost(instanceHost?: string): string {\n // Instance-level override\n if (instanceHost) {\n return normalizeToOrigin(instanceHost);\n }\n\n // Global config override\n if (globalConfig.host) {\n return normalizeToOrigin(globalConfig.host);\n }\n\n // Try to infer from script src (only in browser, only at load time)\n if (hasDom()) {\n const scriptHost = getScriptHost();\n if (scriptHost) {\n return scriptHost;\n }\n }\n\n return DEFAULT_HOST;\n}\n\n// Capture script src at load time - document.currentScript only available during initial execution\nlet capturedScriptHost: string | null = null;\n\nfunction getScriptHost(): string | null {\n if (capturedScriptHost !== null) {\n return capturedScriptHost;\n }\n\n if (!hasDom()) {\n return null;\n }\n\n const currentScript = document.currentScript as HTMLScriptElement | null;\n if (currentScript?.src) {\n try {\n capturedScriptHost = new URL(currentScript.src).origin;\n return capturedScriptHost;\n } catch {\n // Invalid URL, ignore\n }\n }\n\n capturedScriptHost = \"\"; // Mark as attempted\n return null;\n}\n\n// Capture script host immediately when module loads (in browser)\nif (hasDom()) {\n getScriptHost();\n}\n","/**\n * Shared utilities for the Perspective Embed SDK\n * SSR-safe - DOM access is guarded\n */\n\nimport { THEME_VALUES, type ThemeValue } from \"./constants\";\nimport { hasDom } from \"./config\";\n\n/**\n * Join class names, filtering out falsy values\n */\nexport function cn(...classes: (string | false | null | undefined)[]): string {\n return classes\n .map((c) => (c || \"\").split(\" \"))\n .flat()\n .filter(Boolean)\n .join(\" \");\n}\n\n/**\n * Get the perspective theme class based on the theme value\n * Returns \"perspective-{theme}-theme\" when theme is available, undefined otherwise\n */\nexport function getThemeClass(theme: string | undefined): string | undefined {\n return theme && theme !== THEME_VALUES.system\n ? `perspective-${theme}-theme`\n : undefined;\n}\n\n/**\n * Resolve whether dark mode should be used.\n * Priority: 1) explicit theme override, 2) system preference\n * SSR-safe: defaults to light theme on server\n */\nexport function resolveIsDark(theme?: ThemeValue | string): boolean {\n if (theme === THEME_VALUES.dark) return true;\n if (theme === THEME_VALUES.light) return false;\n // system or undefined → use system preference (or light on server)\n if (!hasDom()) return false;\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n}\n\n/**\n * Resolve effective theme based on override and system preference\n * Returns the theme string ('light' or 'dark')\n */\nexport function resolveTheme(themeOverride?: ThemeValue): \"light\" | \"dark\" {\n return resolveIsDark(themeOverride) ? \"dark\" : \"light\";\n}\n\n/**\n * Normalize and validate hex color. Returns undefined for invalid colors.\n */\nexport function normalizeHex(color: string): string | undefined {\n const trimmed = color.trim();\n if (!trimmed) return undefined;\n\n const normalized = trimmed.startsWith(\"#\") ? trimmed : `#${trimmed}`;\n\n // Validate hex format (#RGB, #RRGGBB, or #RRGGBBAA)\n if (/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(normalized)) {\n return normalized;\n }\n\n // Try to extract valid hex chars\n const hexChars = normalized.slice(1).replace(/[^0-9a-fA-F]/g, \"\");\n if (hexChars.length >= 6) return `#${hexChars.slice(0, 6)}`;\n if (hexChars.length >= 3) return `#${hexChars.slice(0, 3)}`;\n\n return undefined;\n}\n\n/**\n * Convert hex to rgba for spinner track\n */\nexport function hexToRgba(hex: string, alpha: number): string {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n if (!result || !result[1] || !result[2] || !result[3]) {\n return `rgba(118, 41, 200, ${alpha})`;\n }\n\n const r = parseInt(result[1], 16);\n const g = parseInt(result[2], 16);\n const b = parseInt(result[3], 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n","/**\n * iframe creation and postMessage communication\n * SSR-safe - all DOM access is guarded\n */\n\nimport type {\n BrandColors,\n EmbedConfig,\n EmbedMessage,\n EmbedType,\n} from \"./types\";\nimport { hasDom } from \"./config\";\nimport {\n UTM_PARAMS,\n RESERVED_PARAMS,\n PARAM_KEYS,\n BRAND_KEYS,\n MESSAGE_TYPES,\n THEME_VALUES,\n PARAM_VALUES,\n STORAGE_KEYS,\n SDK_VERSION,\n CURRENT_FEATURES,\n ERROR_CODES,\n} from \"./constants\";\nimport { normalizeHex } from \"./utils\";\n\n/** Validate redirect URL - allow https, http localhost, and relative URLs */\nfunction isAllowedRedirectUrl(url: string): boolean {\n if (!url || typeof url !== \"string\") return false;\n try {\n const parsed = new URL(url, window.location.origin);\n const protocol = parsed.protocol.toLowerCase();\n const hostname = parsed.hostname.toLowerCase();\n\n if (protocol === \"https:\") return true;\n if (\n protocol === \"http:\" &&\n (hostname === \"localhost\" || hostname === \"127.0.0.1\")\n )\n return true;\n\n return false;\n } catch {\n return false;\n }\n}\n\n/** Get or create persistent anonymous ID */\nfunction getOrCreateAnonId(): string {\n if (!hasDom()) return \"\";\n\n try {\n let id = localStorage.getItem(STORAGE_KEYS.anonId);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(STORAGE_KEYS.anonId, id);\n }\n return id;\n } catch {\n // localStorage might be blocked\n return crypto.randomUUID();\n }\n}\n\n/** Collect UTM params from current page URL */\nfunction getUtmParams(): Record<string, string> {\n if (!hasDom()) return {};\n\n const params: Record<string, string> = {};\n const searchParams = new URLSearchParams(window.location.search);\n\n for (const key of UTM_PARAMS) {\n const value = searchParams.get(key);\n if (value) {\n params[key] = value;\n }\n }\n\n return params;\n}\n\n/** Build iframe URL with all params */\nfunction buildIframeUrl(\n researchId: string,\n type: EmbedType,\n host: string,\n customParams?: Record<string, string>,\n brand?: { light?: BrandColors; dark?: BrandColors },\n themeOverride?: \"dark\" | \"light\" | \"system\"\n): string {\n const url = new URL(`${host}/interview/${researchId}`);\n\n // Base embed params\n url.searchParams.set(PARAM_KEYS.embed, PARAM_VALUES.true);\n url.searchParams.set(PARAM_KEYS.embedType, type === \"float\" ? \"chat\" : type);\n\n // Detect and pass system theme preference (can be overridden)\n if (hasDom()) {\n const isDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n if (themeOverride && themeOverride !== THEME_VALUES.system) {\n url.searchParams.set(PARAM_KEYS.theme, themeOverride);\n } else {\n url.searchParams.set(\n PARAM_KEYS.theme,\n isDark ? THEME_VALUES.dark : THEME_VALUES.light\n );\n }\n } else {\n // SSR fallback\n url.searchParams.set(PARAM_KEYS.theme, themeOverride || THEME_VALUES.light);\n }\n\n // Auto-forward UTM params from parent\n const utmParams = getUtmParams();\n for (const [key, value] of Object.entries(utmParams)) {\n url.searchParams.set(key, value);\n }\n\n // Helper to set param only if color is valid\n const setColor = (key: string, color: string | undefined) => {\n if (!color) return;\n const normalized = normalizeHex(color);\n if (normalized) url.searchParams.set(key, normalized);\n };\n\n // Add brand colors using short keys\n if (brand?.light) {\n setColor(BRAND_KEYS.primary, brand.light.primary);\n setColor(BRAND_KEYS.secondary, brand.light.secondary);\n setColor(BRAND_KEYS.bg, brand.light.bg);\n setColor(BRAND_KEYS.text, brand.light.text);\n }\n\n // Add dark mode brand colors\n if (brand?.dark) {\n setColor(BRAND_KEYS.darkPrimary, brand.dark.primary);\n setColor(BRAND_KEYS.darkSecondary, brand.dark.secondary);\n setColor(BRAND_KEYS.darkBg, brand.dark.bg);\n setColor(BRAND_KEYS.darkText, brand.dark.text);\n }\n\n // Add custom params, filtering out reserved keys\n if (customParams) {\n for (const [key, value] of Object.entries(customParams)) {\n if (!RESERVED_PARAMS.has(key)) {\n url.searchParams.set(key, value);\n }\n }\n }\n\n return url.toString();\n}\n\nexport function createIframe(\n researchId: string,\n type: EmbedType,\n host: string,\n params?: Record<string, string>,\n brand?: { light?: BrandColors; dark?: BrandColors },\n themeOverride?: \"dark\" | \"light\" | \"system\"\n): HTMLIFrameElement {\n if (!hasDom()) {\n // Return a stub for SSR\n return {} as HTMLIFrameElement;\n }\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = buildIframeUrl(\n researchId,\n type,\n host,\n params,\n brand,\n themeOverride\n );\n iframe.setAttribute(\"allow\", \"microphone; camera\");\n iframe.setAttribute(\"allowfullscreen\", \"true\");\n iframe.setAttribute(\n \"sandbox\",\n \"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n );\n iframe.setAttribute(\"data-perspective\", \"true\");\n iframe.style.cssText = \"border:none;\";\n\n return iframe;\n}\n\nexport function setupMessageListener(\n researchId: string,\n config: Partial<EmbedConfig>,\n iframe: HTMLIFrameElement,\n host: string,\n options?: { skipResize?: boolean }\n): () => void {\n if (!hasDom()) {\n return () => {};\n }\n\n const handler = (event: MessageEvent<EmbedMessage>) => {\n // Security: Only accept messages from our embed host and from the expected iframe\n if (event.origin !== host) return;\n if (event.source !== iframe.contentWindow) return;\n\n // Only process messages from our embed\n if (typeof event.data?.type !== \"string\") return;\n if (!event.data.type.startsWith(\"perspective:\")) return;\n if (event.data.researchId !== researchId) return;\n\n switch (event.data.type) {\n case MESSAGE_TYPES.ready:\n // Send scrollbar styles when iframe is ready\n sendScrollbarStyles(iframe, host);\n // Send anon_id for anonymous auth\n sendMessage(iframe, host, {\n type: MESSAGE_TYPES.anonId,\n anonId: getOrCreateAnonId(),\n });\n // Send init message with version/features for handshake\n sendMessage(iframe, host, {\n type: MESSAGE_TYPES.init,\n version: SDK_VERSION,\n features: CURRENT_FEATURES,\n researchId,\n });\n config.onReady?.();\n break;\n\n case MESSAGE_TYPES.resize:\n // Auto-resize iframe height (skip for fixed-container embeds)\n if (!options?.skipResize) {\n iframe.style.height = `${event.data.height}px`;\n }\n break;\n\n case MESSAGE_TYPES.submit:\n config.onSubmit?.({ researchId });\n break;\n\n case MESSAGE_TYPES.close:\n config.onClose?.();\n break;\n\n case MESSAGE_TYPES.error: {\n const error = new Error(\n event.data.error\n ) as import(\"./types\").EmbedError;\n error.code =\n (event.data.code as import(\"./types\").ErrorCode) || \"UNKNOWN\";\n\n // Always log critical errors to console\n if (error.code === ERROR_CODES.SDK_OUTDATED) {\n console.error(\n \"[Perspective] SDK version outdated. Please update @perspective-ai/sdk to the latest version.\",\n error.message\n );\n } else {\n console.error(\"[Perspective] Embed error:\", error.message);\n }\n\n config.onError?.(error);\n break;\n }\n\n case MESSAGE_TYPES.redirect:\n const redirectUrl = event.data.url;\n // Security: Only allow http(s) and localhost URLs\n if (!isAllowedRedirectUrl(redirectUrl)) {\n console.warn(\n \"[Perspective] Blocked unsafe redirect URL:\",\n redirectUrl\n );\n return;\n }\n if (config.onNavigate) {\n config.onNavigate(redirectUrl);\n } else {\n // Fallback: auto-navigate parent page\n window.location.href = redirectUrl;\n }\n break;\n }\n };\n\n window.addEventListener(\"message\", handler);\n return () => window.removeEventListener(\"message\", handler);\n}\n\n/** Send a message to the embed iframe */\nexport function sendMessage(\n iframe: HTMLIFrameElement,\n host: string,\n message: { type: string; [key: string]: unknown }\n): void {\n if (!hasDom()) return;\n iframe.contentWindow?.postMessage(message, host);\n}\n\n/** Track all active iframes for theme change notifications */\nconst activeIframes = new Map<HTMLIFrameElement, string>();\n\nexport function registerIframe(\n iframe: HTMLIFrameElement,\n host: string\n): () => void {\n activeIframes.set(iframe, host);\n return () => {\n activeIframes.delete(iframe);\n if (activeIframes.size === 0) {\n teardownGlobalListeners();\n }\n };\n}\n\n/** Get scrollbar CSS styles */\nfunction getScrollbarStyles(): string {\n if (!hasDom()) return \"\";\n\n const isDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n const borderColor = isDark ? \"hsl(217 33% 17%)\" : \"hsl(240 6% 90%)\";\n\n return `\n * {\n scrollbar-width: thin;\n scrollbar-color: ${borderColor} transparent;\n }\n *::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n }\n *::-webkit-scrollbar-track {\n background: transparent;\n }\n *::-webkit-scrollbar-thumb {\n background-color: ${borderColor};\n border-radius: 9999px;\n border: 2px solid transparent;\n background-clip: padding-box;\n }\n *::-webkit-scrollbar-thumb:hover {\n background-color: color-mix(in srgb, ${borderColor} 80%, currentColor);\n }\n `;\n}\n\n/** Send scrollbar styles to an iframe */\nexport function sendScrollbarStyles(\n iframe: HTMLIFrameElement,\n host: string\n): void {\n const styles = getScrollbarStyles();\n sendMessage(iframe, host, {\n type: MESSAGE_TYPES.injectStyles,\n styles,\n });\n}\n\n/** Notify all active iframes of theme change */\nexport function notifyThemeChange(): void {\n if (!hasDom()) return;\n\n const isDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n activeIframes.forEach((host, iframe) => {\n const message = {\n type: MESSAGE_TYPES.themeChange,\n theme: isDark ? THEME_VALUES.dark : THEME_VALUES.light,\n };\n sendMessage(iframe, host, message);\n sendScrollbarStyles(iframe, host);\n });\n}\n\nlet themeListener: ((e: MediaQueryListEvent) => void) | null = null;\nlet themeMediaQuery: MediaQueryList | null = null;\nlet globalMessageHandler: ((event: MessageEvent) => void) | null = null;\nlet globalListenersInitialized = false;\n\nfunction setupThemeListener(): void {\n if (themeListener || !hasDom()) return;\n\n themeMediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n themeListener = () => notifyThemeChange();\n themeMediaQuery.addEventListener(\"change\", themeListener);\n}\n\nfunction teardownThemeListener(): void {\n if (themeListener && themeMediaQuery) {\n themeMediaQuery.removeEventListener(\"change\", themeListener);\n themeListener = null;\n themeMediaQuery = null;\n }\n}\n\nfunction setupGlobalListeners(): void {\n if (!hasDom() || globalMessageHandler) return;\n\n setupThemeListener();\n\n globalMessageHandler = (event: MessageEvent) => {\n if (!event.data?.type?.startsWith(\"perspective:\")) return;\n if (event.data.type === MESSAGE_TYPES.requestScrollbarStyles) {\n const iframes = Array.from(\n document.querySelectorAll(\"iframe[data-perspective]\")\n );\n const sourceIframe = iframes.find(\n (iframe) => (iframe as HTMLIFrameElement).contentWindow === event.source\n ) as HTMLIFrameElement | undefined;\n if (sourceIframe) {\n const host = activeIframes.get(sourceIframe);\n if (host && event.origin === host) {\n sendScrollbarStyles(sourceIframe, host);\n }\n }\n }\n };\n\n window.addEventListener(\"message\", globalMessageHandler);\n}\n\nfunction teardownGlobalListeners(): void {\n if (globalMessageHandler) {\n window.removeEventListener(\"message\", globalMessageHandler);\n globalMessageHandler = null;\n }\n teardownThemeListener();\n globalListenersInitialized = false;\n}\n\nexport function ensureGlobalListeners(): void {\n if (globalListenersInitialized) return;\n globalListenersInitialized = true;\n setupGlobalListeners();\n}\n","/**\n * Loading indicator for embed iframes\n * SSR-safe - returns no-op on server\n */\n\nimport type { BrandColors, ThemeValue } from \"./types\";\nimport { hasDom } from \"./config\";\nimport { resolveTheme, hexToRgba } from \"./utils\";\n\n/** Default colors matching codebase theme */\nconst DEFAULT_COLORS = {\n light: {\n bg: \"#ffffff\",\n primary: \"#7629C8\",\n },\n dark: {\n bg: \"#02040a\",\n primary: \"#B170FF\",\n },\n};\n\nexport interface LoadingOptions {\n /** Theme override: 'dark', 'light', or 'system' (uses system preference) */\n theme?: ThemeValue;\n /** Brand colors - uses primary color for spinner */\n brand?: {\n light?: BrandColors;\n dark?: BrandColors;\n };\n}\n\n/** Get colors for loading indicator based on theme and brand */\nfunction getLoadingColors(options?: LoadingOptions): {\n bg: string;\n primary: string;\n} {\n const theme = resolveTheme(options?.theme);\n const isDark = theme === \"dark\";\n\n // Get brand colors for current theme\n const brandColors = isDark ? options?.brand?.dark : options?.brand?.light;\n\n return {\n bg:\n brandColors?.bg ||\n (isDark ? DEFAULT_COLORS.dark.bg : DEFAULT_COLORS.light.bg),\n primary:\n brandColors?.primary ||\n (isDark ? DEFAULT_COLORS.dark.primary : DEFAULT_COLORS.light.primary),\n };\n}\n\nexport function createLoadingIndicator(options?: LoadingOptions): HTMLElement {\n // SSR safety - return empty div on server\n if (!hasDom()) {\n return { remove: () => {}, style: {} } as unknown as HTMLElement;\n }\n\n const colors = getLoadingColors(options);\n\n const container = document.createElement(\"div\");\n container.className = \"perspective-loading\";\n container.style.cssText = `\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: ${colors.bg};\n transition: opacity 0.3s ease;\n z-index: 1;\n `;\n\n // Create spinner (keyframes defined in styles.ts)\n const spinner = document.createElement(\"div\");\n spinner.style.cssText = `\n width: 2.5rem;\n height: 2.5rem;\n border: 3px solid ${hexToRgba(colors.primary, 0.15)};\n border-top-color: ${colors.primary};\n border-radius: 50%;\n animation: perspective-spin 0.8s linear infinite;\n `;\n\n container.appendChild(spinner);\n return container;\n}\n","/**\n * CSS styles injected by the embed script\n * SSR-safe - DOM access is guarded\n */\n\nimport { hasDom } from \"./config\";\n\nlet stylesInjected = false;\n\nconst LIGHT_THEME = `\n --perspective-overlay-bg: rgba(0, 0, 0, 0.5);\n --perspective-modal-bg: #ffffff;\n --perspective-modal-text: #151B23;\n --perspective-close-bg: rgba(0, 0, 0, 0.1);\n --perspective-close-text: #666666;\n --perspective-close-hover-bg: rgba(0, 0, 0, 0.2);\n --perspective-close-hover-text: #333333;\n --perspective-border: hsl(240 6% 90%);\n`;\n\nconst DARK_THEME = `\n --perspective-overlay-bg: rgba(0, 0, 0, 0.7);\n --perspective-modal-bg: #02040a;\n --perspective-modal-text: #ffffff;\n --perspective-close-bg: rgba(255, 255, 255, 0.1);\n --perspective-close-text: #a0a0a0;\n --perspective-close-hover-bg: rgba(255, 255, 255, 0.2);\n --perspective-close-hover-text: #ffffff;\n --perspective-border: hsl(217 33% 17%);\n`;\n\nexport function injectStyles(): void {\n if (!hasDom()) return;\n if (stylesInjected) return;\n stylesInjected = true;\n\n const style = document.createElement(\"style\");\n style.id = \"perspective-embed-styles\";\n style.textContent = `\n /* Theme-aware color variables */\n .perspective-embed-root, .perspective-light-theme {\n ${LIGHT_THEME}\n --perspective-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n --perspective-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);\n --perspective-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n --perspective-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n --perspective-radius: 1.2rem;\n --perspective-radius-sm: calc(var(--perspective-radius) - 4px);\n }\n\n /* Dark theme */\n .perspective-dark-theme {\n ${DARK_THEME}\n }\n\n /* System dark mode support */\n @media (prefers-color-scheme: dark) {\n .perspective-embed-root:not(.perspective-light-theme) {\n ${DARK_THEME}\n }\n }\n\n /* Scrollbar styling */\n .perspective-modal,\n .perspective-slider,\n .perspective-float-window,\n .perspective-chat-window {\n scrollbar-width: thin;\n scrollbar-color: var(--perspective-border) transparent;\n }\n\n .perspective-modal::-webkit-scrollbar,\n .perspective-slider::-webkit-scrollbar,\n .perspective-float-window::-webkit-scrollbar,\n .perspective-chat-window::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n }\n\n .perspective-modal::-webkit-scrollbar-track,\n .perspective-slider::-webkit-scrollbar-track,\n .perspective-float-window::-webkit-scrollbar-track,\n .perspective-chat-window::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .perspective-modal::-webkit-scrollbar-thumb,\n .perspective-slider::-webkit-scrollbar-thumb,\n .perspective-float-window::-webkit-scrollbar-thumb,\n .perspective-chat-window::-webkit-scrollbar-thumb {\n background-color: var(--perspective-border);\n border-radius: 9999px;\n border: 2px solid transparent;\n background-clip: padding-box;\n }\n\n .perspective-modal::-webkit-scrollbar-thumb:hover,\n .perspective-slider::-webkit-scrollbar-thumb:hover,\n .perspective-float-window::-webkit-scrollbar-thumb:hover,\n .perspective-chat-window::-webkit-scrollbar-thumb:hover {\n background-color: color-mix(in srgb, var(--perspective-border) 80%, currentColor);\n }\n\n /* Overlay for popup/modal */\n .perspective-overlay {\n position: fixed;\n inset: 0;\n background: var(--perspective-overlay-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n animation: perspective-fade-in 0.2s ease-out;\n }\n\n @keyframes perspective-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n @keyframes perspective-spin {\n to { transform: rotate(360deg); }\n }\n\n /* Modal container */\n .perspective-modal {\n position: relative;\n width: 90%;\n max-width: 600px;\n height: 80vh;\n max-height: 700px;\n background: var(--perspective-modal-bg);\n color: var(--perspective-modal-text);\n border-radius: var(--perspective-radius);\n overflow: hidden;\n box-shadow: var(--perspective-shadow-xl);\n animation: perspective-slide-up 0.3s ease-out;\n }\n\n @keyframes perspective-slide-up {\n from {\n opacity: 0;\n transform: translateY(20px) scale(0.95);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n .perspective-modal iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n /* Close button */\n .perspective-close {\n position: absolute;\n top: 1rem;\n right: 1.5rem;\n width: 2rem;\n height: 2rem;\n border: none;\n background: var(--perspective-close-bg);\n color: var(--perspective-close-text);\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1rem;\n z-index: 10;\n transition: background-color 0.2s ease, color 0.2s ease;\n }\n\n .perspective-close:hover {\n background: var(--perspective-close-hover-bg);\n color: var(--perspective-close-hover-text);\n }\n\n .perspective-close:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 2px;\n }\n\n .perspective-close svg {\n width: 1rem;\n height: 1rem;\n stroke-width: 2;\n }\n\n /* Slider drawer */\n .perspective-slider {\n position: fixed;\n top: 0;\n right: 0;\n width: 100%;\n max-width: 450px;\n height: 100%;\n background: var(--perspective-modal-bg);\n color: var(--perspective-modal-text);\n box-shadow: var(--perspective-shadow-xl);\n z-index: 9999;\n animation: perspective-slide-in 0.3s ease-out;\n }\n\n @keyframes perspective-slide-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n\n .perspective-slider iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n .perspective-slider .perspective-close {\n top: 1rem;\n right: 2rem;\n }\n\n /* Slider backdrop */\n .perspective-slider-backdrop {\n position: fixed;\n inset: 0;\n background: var(--perspective-overlay-bg);\n z-index: 9998;\n animation: perspective-fade-in 0.2s ease-out;\n }\n\n /* Float bubble (and legacy chat-bubble alias) */\n .perspective-float-bubble,\n .perspective-chat-bubble {\n position: fixed;\n bottom: 1.5rem;\n right: 1.5rem;\n width: 3.75rem;\n height: 3.75rem;\n border-radius: 50%;\n background: var(--perspective-float-bg, var(--perspective-chat-bg, #7629C8));\n color: white;\n border: none;\n cursor: pointer;\n box-shadow: var(--perspective-float-shadow, var(--perspective-chat-shadow, 0 4px 12px rgba(118, 41, 200, 0.4)));\n z-index: 9996;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n\n .perspective-float-bubble:hover,\n .perspective-chat-bubble:hover {\n transform: scale(1.05);\n box-shadow: var(--perspective-float-shadow-hover, var(--perspective-chat-shadow-hover, 0 6px 16px rgba(118, 41, 200, 0.5)));\n }\n\n .perspective-float-bubble:focus-visible,\n .perspective-chat-bubble:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 2px;\n }\n\n .perspective-float-bubble svg,\n .perspective-chat-bubble svg {\n width: 1.75rem;\n height: 1.75rem;\n stroke-width: 2;\n }\n\n /* Float window (and legacy chat-window alias) */\n .perspective-float-window,\n .perspective-chat-window {\n position: fixed;\n bottom: 6.25rem;\n right: 1.5rem;\n width: 380px;\n height: calc(100vh - 8.75rem);\n max-height: 600px;\n background: var(--perspective-modal-bg);\n color: var(--perspective-modal-text);\n border-radius: var(--perspective-radius);\n overflow: hidden;\n box-shadow: var(--perspective-shadow-xl);\n z-index: 9997;\n animation: perspective-float-open 0.3s ease-out;\n }\n\n @keyframes perspective-float-open {\n from {\n opacity: 0;\n transform: translateY(20px) scale(0.9);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n .perspective-float-window iframe,\n .perspective-chat-window iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n .perspective-float-window .perspective-close,\n .perspective-chat-window .perspective-close {\n top: 1rem;\n right: 1.5rem;\n }\n\n /* Fullpage */\n .perspective-fullpage {\n position: fixed;\n inset: 0;\n z-index: 9999;\n background: var(--perspective-modal-bg);\n }\n\n .perspective-fullpage iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n /* Responsive */\n @media (max-width: 640px) {\n .perspective-modal {\n width: 100%;\n height: 100%;\n max-width: none;\n max-height: none;\n border-radius: 0;\n }\n\n .perspective-slider {\n max-width: 100%;\n }\n\n .perspective-float-window,\n .perspective-chat-window {\n width: calc(100% - 2rem);\n right: 1rem;\n bottom: 5.625rem;\n height: calc(100vh - 7.5rem);\n }\n\n .perspective-float-bubble,\n .perspective-chat-bubble {\n bottom: 1rem;\n right: 1rem;\n }\n }\n\n @media (max-width: 450px) {\n .perspective-float-window,\n .perspective-chat-window {\n width: calc(100% - 1rem);\n right: 0.5rem;\n bottom: 5rem;\n height: calc(100vh - 6.5rem);\n }\n\n .perspective-float-bubble,\n .perspective-chat-bubble {\n bottom: 0.75rem;\n right: 0.75rem;\n width: 3.5rem;\n height: 3.5rem;\n }\n\n .perspective-float-bubble svg,\n .perspective-chat-bubble svg {\n width: 1.5rem;\n height: 1.5rem;\n }\n }\n `;\n\n document.head.appendChild(style);\n}\n\nexport const MIC_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <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\" />\n</svg>`;\n\n/** @deprecated Use MIC_ICON instead */\nexport const CHAT_ICON = MIC_ICON;\n\nexport const CLOSE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n</svg>`;\n","/**\n * Inline widget embed - renders directly in a container element\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, EmbedHandle } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles } from \"./styles\";\nimport { cn, getThemeClass } from \"./utils\";\n\ntype WidgetResources = {\n cleanup: () => void;\n unregister: () => void;\n wrapper: HTMLElement;\n};\n\nconst widgetResources = new WeakMap<HTMLIFrameElement, WidgetResources>();\n\nfunction createNoOpHandle(researchId: string, type: \"widget\"): EmbedHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n researchId,\n type,\n iframe: null,\n container: null,\n };\n}\n\nfunction createExistingWidgetHandle(\n container: HTMLElement,\n researchId: string\n): EmbedHandle {\n const existingWrapper = container.querySelector<HTMLElement>(\n \".perspective-embed-root\"\n );\n const existingIframe = container.querySelector<HTMLIFrameElement>(\n \"iframe[data-perspective]\"\n );\n\n let destroyed = false;\n\n const unmount = () => {\n if (destroyed) return;\n destroyed = true;\n\n if (existingIframe) {\n const resources = widgetResources.get(existingIframe);\n if (resources) {\n resources.cleanup();\n resources.unregister();\n widgetResources.delete(existingIframe);\n }\n }\n existingWrapper?.remove();\n };\n\n return {\n unmount,\n update: () => {},\n destroy: unmount,\n researchId,\n type: \"widget\" as const,\n iframe: existingIframe,\n container,\n };\n}\n\nexport function createWidget(\n container: HTMLElement | null,\n config: EmbedConfig\n): EmbedHandle {\n const { researchId } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom() || !container) {\n return createNoOpHandle(researchId, \"widget\");\n }\n\n // Idempotency check for React Strict Mode\n if (container.querySelector(\"iframe[data-perspective]\")) {\n return createExistingWidgetHandle(container, researchId);\n }\n\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create wrapper for positioning\n const wrapper = document.createElement(\"div\");\n wrapper.className = cn(\"perspective-embed-root\", getThemeClass(config.theme));\n wrapper.style.cssText =\n \"position:relative;width:100%;height:100%;min-height:500px;\";\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: config.theme,\n brand: config.brand,\n });\n wrapper.appendChild(loading);\n\n // Create iframe (hidden initially)\n const iframe = createIframe(\n researchId,\n \"widget\",\n host,\n config.params,\n config.brand,\n config.theme\n );\n iframe.style.width = \"100%\";\n iframe.style.height = \"100%\";\n iframe.style.minHeight = \"500px\";\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n wrapper.appendChild(iframe);\n container.appendChild(wrapper);\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n\n // Set up message listener with loading state handling\n const cleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n // Hide loading, show iframe\n loading.style.opacity = \"0\";\n iframe.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return currentConfig.onClose;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n // Register iframe for theme change notifications\n const unregisterIframe = registerIframe(iframe, host);\n\n widgetResources.set(iframe, {\n cleanup,\n unregister: unregisterIframe,\n wrapper,\n });\n\n let destroyed = false;\n\n const unmount = () => {\n if (destroyed) return;\n destroyed = true;\n\n cleanup();\n unregisterIframe();\n widgetResources.delete(iframe);\n wrapper.remove();\n };\n\n return {\n unmount,\n update: (options) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy: unmount,\n researchId,\n type: \"widget\" as const,\n iframe,\n container,\n };\n}\n","/**\n * Popup/modal embed - opens in a centered modal overlay\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, EmbedHandle } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles, CLOSE_ICON } from \"./styles\";\nimport { cn, getThemeClass } from \"./utils\";\n\nfunction createNoOpHandle(researchId: string): EmbedHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n researchId,\n type: \"popup\",\n iframe: null,\n container: null,\n };\n}\n\nexport function openPopup(config: EmbedConfig): EmbedHandle {\n const { researchId } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom()) {\n return createNoOpHandle(researchId);\n }\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create overlay\n const overlay = document.createElement(\"div\");\n overlay.className = cn(\n \"perspective-overlay perspective-embed-root\",\n getThemeClass(config.theme)\n );\n\n // Create modal container\n const modal = document.createElement(\"div\");\n modal.className = \"perspective-modal\";\n\n // Create close button\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"perspective-close\";\n closeBtn.innerHTML = CLOSE_ICON;\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: config.theme,\n brand: config.brand,\n });\n loading.style.borderRadius = \"16px\";\n\n // Create iframe (hidden initially)\n const iframe = createIframe(\n researchId,\n \"popup\",\n host,\n config.params,\n config.brand,\n config.theme\n );\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n modal.appendChild(closeBtn);\n modal.appendChild(loading);\n modal.appendChild(iframe);\n overlay.appendChild(modal);\n document.body.appendChild(overlay);\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n let isOpen = true;\n let messageCleanup: (() => void) | null = null;\n\n // Register iframe for theme change notifications\n const unregisterIframe = registerIframe(iframe, host);\n\n const destroy = () => {\n if (!isOpen) return;\n isOpen = false;\n messageCleanup?.();\n unregisterIframe();\n overlay.remove();\n document.removeEventListener(\"keydown\", escHandler);\n currentConfig.onClose?.();\n };\n\n // Set up message listener with loading state handling\n messageCleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n loading.style.opacity = \"0\";\n iframe.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return destroy;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n // Close handlers\n closeBtn.addEventListener(\"click\", destroy);\n overlay.addEventListener(\"click\", (e) => {\n if (e.target === overlay) destroy();\n });\n\n // ESC key closes\n const escHandler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n destroy();\n }\n };\n document.addEventListener(\"keydown\", escHandler);\n\n return {\n unmount: destroy,\n update: (options: Parameters<EmbedHandle[\"update\"]>[0]) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy,\n researchId,\n type: \"popup\" as const,\n iframe,\n container: overlay,\n };\n}\n","/**\n * Slider/drawer embed - slides in from the right\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, EmbedHandle } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles, CLOSE_ICON } from \"./styles\";\nimport { cn, getThemeClass } from \"./utils\";\n\nfunction createNoOpHandle(researchId: string): EmbedHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n researchId,\n type: \"slider\",\n iframe: null,\n container: null,\n };\n}\n\nexport function openSlider(config: EmbedConfig): EmbedHandle {\n const { researchId } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom()) {\n return createNoOpHandle(researchId);\n }\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create backdrop\n const backdrop = document.createElement(\"div\");\n backdrop.className = cn(\n \"perspective-slider-backdrop perspective-embed-root\",\n getThemeClass(config.theme)\n );\n\n // Create slider container\n const slider = document.createElement(\"div\");\n slider.className = cn(\n \"perspective-slider perspective-embed-root\",\n getThemeClass(config.theme)\n );\n\n // Create close button\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"perspective-close\";\n closeBtn.innerHTML = CLOSE_ICON;\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: config.theme,\n brand: config.brand,\n });\n\n // Create iframe (hidden initially)\n const iframe = createIframe(\n researchId,\n \"slider\",\n host,\n config.params,\n config.brand,\n config.theme\n );\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n slider.appendChild(closeBtn);\n slider.appendChild(loading);\n slider.appendChild(iframe);\n document.body.appendChild(backdrop);\n document.body.appendChild(slider);\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n let isOpen = true;\n let messageCleanup: (() => void) | null = null;\n\n // Register iframe for theme change notifications\n const unregisterIframe = registerIframe(iframe, host);\n\n const destroy = () => {\n if (!isOpen) return;\n isOpen = false;\n messageCleanup?.();\n unregisterIframe();\n slider.remove();\n backdrop.remove();\n document.removeEventListener(\"keydown\", escHandler);\n currentConfig.onClose?.();\n };\n\n // Set up message listener with loading state handling\n messageCleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n loading.style.opacity = \"0\";\n iframe.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return destroy;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n // Close handlers\n closeBtn.addEventListener(\"click\", destroy);\n backdrop.addEventListener(\"click\", destroy);\n\n // ESC key closes\n const escHandler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n destroy();\n }\n };\n document.addEventListener(\"keydown\", escHandler);\n\n return {\n unmount: destroy,\n update: (options: Parameters<EmbedHandle[\"update\"]>[0]) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy,\n researchId,\n type: \"slider\" as const,\n iframe,\n container: slider,\n };\n}\n","/**\n * Floating bubble embed - floating button that opens a chat window\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, FloatHandle, ThemeConfig } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles, MIC_ICON, CLOSE_ICON } from \"./styles\";\nimport { cn, getThemeClass, resolveIsDark } from \"./utils\";\n\ntype FloatConfig = EmbedConfig & { _themeConfig?: ThemeConfig };\n\nfunction createNoOpHandle(researchId: string): FloatHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n open: () => {},\n close: () => {},\n toggle: () => {},\n isOpen: false,\n researchId,\n type: \"float\",\n iframe: null,\n container: null,\n };\n}\n\nexport function createFloatBubble(config: FloatConfig): FloatHandle {\n const { researchId, _themeConfig, theme, brand } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom()) {\n return createNoOpHandle(researchId);\n }\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create bubble button\n const bubble = document.createElement(\"button\");\n bubble.className = cn(\n \"perspective-float-bubble perspective-embed-root\",\n getThemeClass(config.theme)\n );\n bubble.innerHTML = MIC_ICON;\n bubble.setAttribute(\"aria-label\", \"Open chat\");\n bubble.setAttribute(\"data-perspective\", \"float-bubble\");\n\n // Apply theme color if available\n if (_themeConfig || brand) {\n const isDark = resolveIsDark(theme);\n const bg = isDark\n ? (brand?.dark?.primary ?? _themeConfig?.darkPrimaryColor ?? \"#a78bfa\")\n : (brand?.light?.primary ?? _themeConfig?.primaryColor ?? \"#7c3aed\");\n bubble.style.setProperty(\"--perspective-float-bg\", bg);\n bubble.style.setProperty(\n \"--perspective-float-shadow\",\n `0 4px 12px ${bg}66`\n );\n bubble.style.setProperty(\n \"--perspective-float-shadow-hover\",\n `0 6px 16px ${bg}80`\n );\n bubble.style.backgroundColor = bg;\n bubble.style.boxShadow = `0 4px 12px ${bg}66`;\n }\n\n document.body.appendChild(bubble);\n\n let floatWindow: HTMLElement | null = null;\n let iframe: HTMLIFrameElement | null = null;\n let cleanup: (() => void) | null = null;\n let unregisterIframe: (() => void) | null = null;\n let isOpen = false;\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n\n const openFloat = () => {\n if (isOpen) return;\n isOpen = true;\n\n // Create float window\n floatWindow = document.createElement(\"div\");\n floatWindow.className = cn(\n \"perspective-float-window perspective-embed-root\",\n getThemeClass(currentConfig.theme)\n );\n\n // Create close button\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"perspective-close\";\n closeBtn.innerHTML = CLOSE_ICON;\n closeBtn.setAttribute(\"aria-label\", \"Close chat\");\n closeBtn.addEventListener(\"click\", closeFloat);\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: currentConfig.theme,\n brand: currentConfig.brand,\n });\n loading.style.borderRadius = \"16px\";\n\n // Create iframe (hidden initially)\n iframe = createIframe(\n researchId,\n \"float\",\n host,\n currentConfig.params,\n currentConfig.brand,\n currentConfig.theme\n );\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n floatWindow.appendChild(closeBtn);\n floatWindow.appendChild(loading);\n floatWindow.appendChild(iframe);\n document.body.appendChild(floatWindow);\n\n // Set up message listener with loading state handling\n cleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n loading.style.opacity = \"0\";\n iframe!.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return closeFloat;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n // Register iframe for theme change notifications\n if (iframe) {\n unregisterIframe = registerIframe(iframe, host);\n }\n\n // Update bubble icon to close\n bubble.innerHTML = CLOSE_ICON;\n bubble.setAttribute(\"aria-label\", \"Close chat\");\n };\n\n const closeFloat = () => {\n if (!isOpen) return;\n isOpen = false;\n\n cleanup?.();\n unregisterIframe?.();\n floatWindow?.remove();\n floatWindow = null;\n iframe = null;\n cleanup = null;\n unregisterIframe = null;\n\n // Restore bubble icon\n bubble.innerHTML = MIC_ICON;\n bubble.setAttribute(\"aria-label\", \"Open chat\");\n\n currentConfig.onClose?.();\n };\n\n // Toggle on bubble click\n bubble.addEventListener(\"click\", () => {\n if (isOpen) {\n closeFloat();\n } else {\n openFloat();\n }\n });\n\n const unmount = () => {\n closeFloat();\n bubble.remove();\n };\n\n return {\n unmount,\n update: (options: Parameters<FloatHandle[\"update\"]>[0]) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy: unmount,\n open: openFloat,\n close: closeFloat,\n toggle: () => {\n if (isOpen) {\n closeFloat();\n } else {\n openFloat();\n }\n },\n get isOpen() {\n return isOpen;\n },\n researchId,\n type: \"float\" as const,\n get iframe() {\n return iframe;\n },\n container: bubble,\n };\n}\n\n/** @deprecated Use createFloatBubble instead */\nexport const createChatBubble = createFloatBubble;\n","/**\n * Fullpage embed - takes over entire viewport\n * SSR-safe - returns no-op handle on server\n */\n\nimport type { EmbedConfig, EmbedHandle } from \"./types\";\nimport { hasDom, getHost } from \"./config\";\nimport {\n createIframe,\n setupMessageListener,\n registerIframe,\n ensureGlobalListeners,\n} from \"./iframe\";\nimport { createLoadingIndicator } from \"./loading\";\nimport { injectStyles } from \"./styles\";\nimport { cn, getThemeClass } from \"./utils\";\n\nfunction createNoOpHandle(researchId: string): EmbedHandle {\n return {\n unmount: () => {},\n update: () => {},\n destroy: () => {},\n researchId,\n type: \"fullpage\" as const,\n iframe: null,\n container: null,\n };\n}\n\nexport function createFullpage(config: EmbedConfig): EmbedHandle {\n const { researchId } = config;\n\n // SSR safety: return no-op handle\n if (!hasDom()) {\n return createNoOpHandle(researchId);\n }\n const host = getHost(config.host);\n\n injectStyles();\n ensureGlobalListeners();\n\n // Create fullpage container\n const container = document.createElement(\"div\");\n container.className = cn(\n \"perspective-embed-root perspective-fullpage\",\n getThemeClass(config.theme)\n );\n\n // Create loading indicator with theme and brand colors\n const loading = createLoadingIndicator({\n theme: config.theme,\n brand: config.brand,\n });\n container.appendChild(loading);\n\n // Create iframe (hidden initially)\n const iframe = createIframe(\n researchId,\n \"fullpage\",\n host,\n config.params,\n config.brand,\n config.theme\n );\n iframe.style.opacity = \"0\";\n iframe.style.transition = \"opacity 0.3s ease\";\n\n container.appendChild(iframe);\n document.body.appendChild(container);\n\n // Mutable config reference for updates\n let currentConfig = { ...config };\n let messageCleanup: (() => void) | null = null;\n\n // Register iframe for theme change notifications\n const unregisterIframe = registerIframe(iframe, host);\n\n const unmount = () => {\n messageCleanup?.();\n unregisterIframe();\n container.remove();\n currentConfig.onClose?.();\n };\n\n // Set up message listener\n messageCleanup = setupMessageListener(\n researchId,\n {\n get onReady() {\n return () => {\n loading.style.opacity = \"0\";\n iframe.style.opacity = \"1\";\n setTimeout(() => loading.remove(), 300);\n currentConfig.onReady?.();\n };\n },\n get onSubmit() {\n return currentConfig.onSubmit;\n },\n get onNavigate() {\n return currentConfig.onNavigate;\n },\n get onClose() {\n return unmount;\n },\n get onError() {\n return currentConfig.onError;\n },\n },\n iframe,\n host,\n { skipResize: true }\n );\n\n return {\n unmount,\n update: (options) => {\n currentConfig = { ...currentConfig, ...options };\n },\n destroy: unmount,\n researchId,\n type: \"fullpage\" as const,\n iframe,\n container,\n };\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["createNoOpHandle","destroy","createNoOpHandle","destroy","createNoOpHandle","createNoOpHandle"]}
package/dist/browser.js CHANGED
@@ -12,21 +12,6 @@ var FEATURES = {
12
12
  };
13
13
  var CURRENT_FEATURES = FEATURES.RESIZE | FEATURES.THEME_SYNC | FEATURES.ANON_ID | FEATURES.SCROLLBAR_STYLES;
14
14
  var PARAM_KEYS = {
15
- // User identification
16
- email: "email",
17
- name: "name",
18
- // Navigation
19
- returnUrl: "returnUrl",
20
- // Interview behavior
21
- voice: "voice",
22
- scroll: "scroll",
23
- hideProgress: "hideProgress",
24
- hideGreeting: "hideGreeting",
25
- hideBranding: "hideBranding",
26
- // Interview mode & auth
27
- mode: "mode",
28
- invite: "invite",
29
- // System (internal)
30
15
  embed: "embed",
31
16
  embedType: "embed_type",
32
17
  theme: "theme"
@@ -102,8 +87,6 @@ var ERROR_CODES = {
102
87
  UNKNOWN: "UNKNOWN"
103
88
  };
104
89
  var PARAM_VALUES = {
105
- disabled: "0",
106
- enabled: "1",
107
90
  true: "true",
108
91
  false: "false"
109
92
  };