@sonordev/site-kit 2.8.2 → 3.0.0
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/EngageWidget-LRE4VD7O.js +14 -0
- package/dist/{EngageWidget-JIXUBG47.js.map → EngageWidget-LRE4VD7O.js.map} +1 -1
- package/dist/EngageWidget-OAOLB6ED.mjs +5 -0
- package/dist/{EngageWidget-4W5EM4UO.mjs.map → EngageWidget-OAOLB6ED.mjs.map} +1 -1
- package/dist/blog/index.js +2 -1
- package/dist/blog/index.js.map +1 -1
- package/dist/blog/index.mjs +2 -1
- package/dist/blog/index.mjs.map +1 -1
- package/dist/chunk-2FEQ6TYD.mjs +37 -0
- package/dist/chunk-2FEQ6TYD.mjs.map +1 -0
- package/dist/{chunk-5SQ4NRPH.mjs → chunk-3AEOGV6V.mjs} +6 -4
- package/dist/chunk-3AEOGV6V.mjs.map +1 -0
- package/dist/chunk-662YHKWW.mjs +10 -0
- package/dist/chunk-662YHKWW.mjs.map +1 -0
- package/dist/{chunk-MNOVPHL6.mjs → chunk-72MRAYAS.mjs} +19 -14
- package/dist/chunk-72MRAYAS.mjs.map +1 -0
- package/dist/{chunk-5QLM6LZ2.mjs → chunk-APRAGA5G.mjs} +3 -3
- package/dist/{chunk-5QLM6LZ2.mjs.map → chunk-APRAGA5G.mjs.map} +1 -1
- package/dist/{chunk-MWE2HRPU.js → chunk-D5XTX5EB.js} +19 -14
- package/dist/chunk-D5XTX5EB.js.map +1 -0
- package/dist/{chunk-HANRPNKG.js → chunk-FMI65E5Y.js} +3 -3
- package/dist/{chunk-HANRPNKG.js.map → chunk-FMI65E5Y.js.map} +1 -1
- package/dist/{chunk-SLJJJWMV.js → chunk-FPU2QPO4.js} +338 -33
- package/dist/chunk-FPU2QPO4.js.map +1 -0
- package/dist/{chunk-PKUTYMN7.js → chunk-J25JPU77.js} +2 -2
- package/dist/chunk-J25JPU77.js.map +1 -0
- package/dist/chunk-J7QI3GDX.js +12 -0
- package/dist/chunk-J7QI3GDX.js.map +1 -0
- package/dist/chunk-MLJXELPA.js +693 -0
- package/dist/chunk-MLJXELPA.js.map +1 -0
- package/dist/{chunk-L7XPPFSS.mjs → chunk-QDKQZ5XH.mjs} +2 -2
- package/dist/chunk-QDKQZ5XH.mjs.map +1 -0
- package/dist/{chunk-IAV32YHL.mjs → chunk-RV45YMBJ.mjs} +338 -33
- package/dist/chunk-RV45YMBJ.mjs.map +1 -0
- package/dist/chunk-TCVHI3JP.mjs +686 -0
- package/dist/chunk-TCVHI3JP.mjs.map +1 -0
- package/dist/{chunk-PAF5IGGF.js → chunk-XNUPJC6R.js} +6 -4
- package/dist/chunk-XNUPJC6R.js.map +1 -0
- package/dist/chunk-YBKALLBH.js +46 -0
- package/dist/chunk-YBKALLBH.js.map +1 -0
- package/dist/engage/index.d.mts +2 -2
- package/dist/engage/index.d.ts +2 -2
- package/dist/engage/index.js +5 -4
- package/dist/engage/index.js.map +1 -1
- package/dist/engage/index.mjs +2 -1
- package/dist/engage/index.mjs.map +1 -1
- package/dist/forms/index.js +19 -683
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/index.mjs +4 -676
- package/dist/forms/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/layout/client.js +2 -2
- package/dist/layout/client.mjs +1 -1
- package/dist/layout/index.js +5 -5
- package/dist/layout/index.mjs +2 -2
- package/dist/llms/index.d.mts +18 -4
- package/dist/llms/index.d.ts +18 -4
- package/dist/llms/index.js +26 -51
- package/dist/llms/index.js.map +1 -1
- package/dist/llms/index.mjs +16 -41
- package/dist/llms/index.mjs.map +1 -1
- package/dist/middleware/index.d.mts +62 -1
- package/dist/middleware/index.d.ts +62 -1
- package/dist/middleware/index.js +105 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +103 -1
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/seo/index.d.mts +10 -205
- package/dist/seo/index.d.ts +10 -205
- package/dist/seo/index.js +16 -15
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +7 -6
- package/dist/seo/index.mjs.map +1 -1
- package/dist/seo/server.d.mts +2 -106
- package/dist/seo/server.d.ts +2 -106
- package/dist/seo/server.js +56 -197
- package/dist/seo/server.js.map +1 -1
- package/dist/seo/server.mjs +2 -183
- package/dist/seo/server.mjs.map +1 -1
- package/dist/server-l6LMNvF1.d.ts +360 -0
- package/dist/server-mX5At_6x.d.mts +360 -0
- package/dist/sitemap/index.d.mts +1 -1
- package/dist/sitemap/index.d.ts +1 -1
- package/dist/sitemap/index.js +2 -2
- package/dist/sitemap/index.mjs +1 -1
- package/dist/slots/contract.d.mts +83 -0
- package/dist/slots/contract.d.ts +83 -0
- package/dist/slots/contract.js +41 -0
- package/dist/slots/contract.js.map +1 -0
- package/dist/slots/contract.mjs +4 -0
- package/dist/slots/contract.mjs.map +1 -0
- package/dist/slots/index.d.mts +104 -0
- package/dist/slots/index.d.ts +104 -0
- package/dist/slots/index.js +186 -0
- package/dist/slots/index.js.map +1 -0
- package/dist/slots/index.mjs +146 -0
- package/dist/slots/index.mjs.map +1 -0
- package/dist/{types-DYyIAgQg.d.ts → types-DPsbBKiu.d.mts} +1 -1
- package/dist/{types-DYyIAgQg.d.mts → types-DPsbBKiu.d.ts} +1 -1
- package/dist/{types-BnCwwUX3.d.mts → types-D_1iUxOQ.d.mts} +18 -1
- package/dist/{types-BnCwwUX3.d.ts → types-D_1iUxOQ.d.ts} +18 -1
- package/dist/{types-J7Z_FqmV.d.mts → types-DhCAWUmd.d.mts} +8 -0
- package/dist/{types-0NuBL1Gg.d.ts → types-xcP_owwn.d.ts} +8 -0
- package/package.json +11 -1
- package/dist/EngageWidget-4W5EM4UO.mjs +0 -4
- package/dist/EngageWidget-JIXUBG47.js +0 -13
- package/dist/chunk-5SQ4NRPH.mjs.map +0 -1
- package/dist/chunk-IAV32YHL.mjs.map +0 -1
- package/dist/chunk-L7XPPFSS.mjs.map +0 -1
- package/dist/chunk-MNOVPHL6.mjs.map +0 -1
- package/dist/chunk-MWE2HRPU.js.map +0 -1
- package/dist/chunk-PAF5IGGF.js.map +0 -1
- package/dist/chunk-PKUTYMN7.js.map +0 -1
- package/dist/chunk-SLJJJWMV.js.map +0 -1
- package/dist/routing-C7gmHWm9.d.ts +0 -163
- package/dist/routing-trNzR1Pz.d.mts +0 -163
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/identity.tsx","../src/layout/SiteKitClientProviders.tsx"],"names":["createContext","useMemo","jsx","dynamic","useState","useRef","useCallback","Fragment","jsxs","Suspense"],"mappings":"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","file":"chunk-HANRPNKG.js","sourcesContent":["/**\n * @sonordev/site-kit/shared — Unified Identity Management\n *\n * Single source of truth for visitor and session IDs across\n * Analytics, Signal, and Engage modules.\n *\n * Storage keys:\n * localStorage: _sk_vid (visitor ID, persists ~forever)\n * sessionStorage: _sk_sid (session ID, per-tab)\n * sessionStorage: _sk_stime (last activity timestamp for timeout)\n *\n * Storage keys are prefixed with `_sk_*`.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo } from 'react'\n\n// ============================================\n// ID Generation\n// ============================================\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)\n })\n}\n\n/**\n * Get or create a persistent visitor ID.\n * Stored in localStorage under `_sk_vid`.\n */\nexport function getOrCreateVisitorId(): string {\n if (typeof window === 'undefined') return ''\n\n let visitorId = localStorage.getItem('_sk_vid')\n\n if (!visitorId) {\n visitorId = generateId()\n }\n\n localStorage.setItem('_sk_vid', visitorId)\n return visitorId\n}\n\n/**\n * Get or create a session ID with inactivity timeout.\n * Stored in sessionStorage under `_sk_sid`.\n * Session expires after `timeoutMinutes` of inactivity (default 30).\n */\nexport function getOrCreateSessionId(timeoutMinutes = 30): string {\n if (typeof window === 'undefined') return ''\n\n const now = Date.now()\n const timeoutMs = timeoutMinutes * 60 * 1000\n\n const existingSession = sessionStorage.getItem('_sk_sid')\n const lastActivity = sessionStorage.getItem('_sk_stime')\n\n if (existingSession && lastActivity) {\n const elapsed = now - parseInt(lastActivity, 10)\n if (elapsed < timeoutMs) {\n // Session still active — refresh timestamp\n sessionStorage.setItem('_sk_stime', now.toString())\n sessionStorage.setItem('_sk_sid', existingSession)\n return existingSession\n }\n }\n\n // New session (first visit or timeout expired)\n const newSession = generateId()\n sessionStorage.setItem('_sk_sid', newSession)\n sessionStorage.setItem('_sk_stime', now.toString())\n return newSession\n}\n\n// ============================================\n// React Context\n// ============================================\n\nexport interface SiteKitIdentity {\n visitorId: string\n sessionId: string\n}\n\nconst SiteKitIdentityContext = createContext<SiteKitIdentity | null>(null)\n\nexport interface SiteKitIdentityProviderProps {\n visitorId: string\n sessionId: string\n children: React.ReactNode\n}\n\nexport function SiteKitIdentityProvider({\n visitorId,\n sessionId,\n children,\n}: SiteKitIdentityProviderProps) {\n const value = useMemo(() => ({ visitorId, sessionId }), [visitorId, sessionId])\n\n return (\n <SiteKitIdentityContext.Provider value={value}>\n {children}\n </SiteKitIdentityContext.Provider>\n )\n}\n\n/**\n * Access the shared visitor/session identity.\n * Falls back to generating IDs directly if used outside of SiteKitIdentityProvider.\n */\nexport function useSiteKitIdentity(): SiteKitIdentity {\n const context = useContext(SiteKitIdentityContext)\n if (context) return context\n\n // Fallback for standalone usage outside SiteKitLayout\n return {\n visitorId: getOrCreateVisitorId(),\n sessionId: getOrCreateSessionId(),\n }\n}\n","'use client'\n\n/**\n * SiteKitClientProviders — Client Island\n *\n * This is the 'use client' boundary for SiteKitLayout.\n * It composes the client-only providers (Analytics, Engage, Signal, SitemapSync)\n * that require React context or browser APIs.\n *\n * All modules are lazy-loaded via next/dynamic so their JS is only downloaded\n * when the feature is enabled — keeping the initial bundle lightweight.\n *\n * All modules share a unified identity (visitor ID + session ID) via\n * SiteKitIdentityProvider, ensuring consistent tracking across Analytics,\n * Signal, and Engage.\n *\n * Props are passed down from the server-side SiteKitLayout component,\n * which reads env vars at render time.\n */\n\nimport React, { Suspense, useCallback, useRef, useState, type ReactNode } from 'react'\nimport dynamic from 'next/dynamic'\nimport {\n SiteKitIdentityProvider,\n getOrCreateVisitorId,\n getOrCreateSessionId,\n} from '../shared/identity'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\n// Lazy-load feature modules — JS only downloaded when feature is enabled\nconst LazyAnalyticsProvider = dynamic(\n () => import('../analytics/AnalyticsProvider').then(m => ({ default: m.AnalyticsProvider })),\n { ssr: false },\n)\nconst LazyEngageWidget = dynamic(\n () => import('../engage/EngageWidget').then(m => ({ default: m.EngageWidget })),\n { ssr: false },\n)\nconst LazySignalBridge = dynamic(\n () => import('../signal/SignalBridge').then(m => ({ default: m.SignalBridge })),\n { ssr: false },\n)\nconst LazySitemapSync = dynamic(\n () => import('../SitemapSync').then(m => ({ default: m.SitemapSync })),\n { ssr: false },\n)\n\nexport interface SiteKitClientProvidersProps {\n children: ReactNode\n apiKey: string\n apiUrl: string\n projectId?: string\n analytics?: boolean | AnalyticsConfig\n engage?: boolean | EngageConfig\n signal?: boolean | SignalConfig\n sitemapSync?: boolean\n debug?: boolean\n}\n\nexport function SiteKitClientProviders({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n debug = false,\n}: SiteKitClientProvidersProps) {\n // Hoist analytics config so the `site` dimension is resolvable up here.\n const analyticsConfig: AnalyticsConfig =\n typeof analytics === 'object' ? analytics : {}\n\n // Resolve the sub-site identifier — the dimension that lets one Sonor project\n // host many sites (e.g. truepowersystems.com + ohiopowerstudies.com under one\n // TPS project). Precedence: explicit `analytics.site` > NEXT_PUBLIC_SITE_URL\n // host > window.location.host. Always a string (possibly empty).\n const explicitSite = analyticsConfig.site\n const envSiteUrl =\n typeof process !== 'undefined'\n ? process.env.NEXT_PUBLIC_SITE_URL\n : undefined\n let resolvedSite = explicitSite ?? ''\n if (!resolvedSite && envSiteUrl) {\n try {\n resolvedSite = new URL(envSiteUrl).host\n } catch {\n /* malformed URL — fall through to client-side host */\n }\n }\n if (!resolvedSite && typeof window !== 'undefined') {\n resolvedSite = window.location.host\n }\n const site = resolvedSite\n\n // Set window globals for modules that still read from them.\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n ;(window as any).__SITE_KIT_SITE__ = site\n }\n\n // Unified identity — single source of truth for all modules\n const [visitorId] = useState(() => getOrCreateVisitorId())\n const [sessionId] = useState(() => getOrCreateSessionId())\n\n // Page metadata bridge: Analytics writes, Signal reads\n const pageMetadataRef = useRef<Record<string, unknown> | null>(null)\n const onPageMetadata = useCallback((metadata: Record<string, unknown>) => {\n pageMetadataRef.current = metadata\n }, [])\n\n let content = <>{children}</>\n\n // Wrap with SignalBridge if enabled.\n // NOTE: SignalBridge still wraps {content} and is dynamic(ssr:false) — the\n // same SSR-de-opt caveat as Analytics applies. It is left wrapping because\n // A/B experiment variant swapping may depend on enclosing page content; if a\n // site enables `signal` and SSR breaks, convert this to a sibling too.\n if (signal) {\n const signalConfig: SignalConfig =\n typeof signal === 'object' ? signal : {}\n content = (\n <LazySignalBridge\n enabled\n realtime={signalConfig.realtime !== false}\n experiments={signalConfig.experiments !== false}\n behaviorTracking={signalConfig.behaviorTracking !== false}\n visitorId={visitorId}\n sessionId={sessionId}\n pageMetadataRef={pageMetadataRef}\n >\n {content}\n </LazySignalBridge>\n )\n }\n\n // Add Analytics if enabled (renders alongside, doesn't wrap).\n //\n // CRITICAL: LazyAnalyticsProvider is dynamic(..., { ssr: false }). Wrapping\n // {content} inside an ssr:false component makes the entire page tree skip\n // server rendering — the route de-opts to 100% client rendering and the\n // hero / LCP element only paints after hydration (mobile LCP ~4s, Lighthouse\n // ~85). Analytics tracks via effects + a metadata ref, so it never needs to\n // wrap page content — mount it as a childless sibling, exactly like Engage.\n if (analytics) {\n content = (\n <>\n {content}\n <Suspense fallback={null}>\n <LazyAnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n site={site}\n trackPageViews={analyticsConfig.trackPageViews !== false}\n trackWebVitals={analyticsConfig.trackWebVitals !== false}\n trackScrollDepth={analyticsConfig.trackScrollDepth !== false}\n trackClicks={analyticsConfig.trackClicks !== false}\n debug={debug}\n externalVisitorId={visitorId}\n externalSessionId={sessionId}\n onPageMetadata={onPageMetadata}\n />\n </Suspense>\n </>\n )\n }\n\n // Add Engage widget if enabled (renders alongside, doesn't wrap)\n if (engage) {\n const engageConfig: EngageConfig =\n typeof engage === 'object' ? engage : {}\n content = (\n <>\n {content}\n <LazyEngageWidget\n apiUrl={apiUrl}\n apiKey={apiKey}\n projectId={projectId}\n position={engageConfig.position || 'bottom-right'}\n chatEnabled={engageConfig.chatEnabled !== false}\n />\n </>\n )\n }\n\n // Add SitemapSync if enabled\n if (sitemapSync) {\n content = (\n <>\n {content}\n <LazySitemapSync debug={debug} />\n </>\n )\n }\n\n return (\n <SiteKitIdentityProvider visitorId={visitorId} sessionId={sessionId}>\n {content}\n </SiteKitIdentityProvider>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/identity.tsx","../src/layout/SiteKitClientProviders.tsx"],"names":["createContext","useMemo","jsx","dynamic","useState","useRef","useCallback","Fragment","jsxs","Suspense"],"mappings":";;;;;;;;;;;AAsBA,SAAS,UAAA,GAAqB;AAC5B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,UAAA,EAAY;AACtD,IAAA,OAAO,OAAO,UAAA,EAAW;AAAA,EAC3B;AAEA,EAAA,OAAO,sCAAA,CAAuC,OAAA,CAAQ,OAAA,EAAS,CAAC,CAAA,KAAM;AACpE,IAAA,MAAM,CAAA,GAAK,IAAA,CAAK,MAAA,EAAO,GAAI,EAAA,GAAM,CAAA;AACjC,IAAA,OAAA,CAAQ,MAAM,GAAA,GAAM,CAAA,GAAK,IAAI,CAAA,GAAO,CAAA,EAAK,SAAS,EAAE,CAAA;AAAA,EACtD,CAAC,CAAA;AACH;AAMO,SAAS,oBAAA,GAA+B;AAC7C,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,EAAA;AAE1C,EAAA,IAAI,SAAA,GAAY,YAAA,CAAa,OAAA,CAAQ,SAAS,CAAA;AAE9C,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,SAAA,GAAY,UAAA,EAAW;AAAA,EACzB;AAEA,EAAA,YAAA,CAAa,OAAA,CAAQ,WAAW,SAAS,CAAA;AACzC,EAAA,OAAO,SAAA;AACT;AAOO,SAAS,oBAAA,CAAqB,iBAAiB,EAAA,EAAY;AAChE,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,EAAA;AAE1C,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,MAAM,SAAA,GAAY,iBAAiB,EAAA,GAAK,GAAA;AAExC,EAAA,MAAM,eAAA,GAAkB,cAAA,CAAe,OAAA,CAAQ,SAAS,CAAA;AACxD,EAAA,MAAM,YAAA,GAAe,cAAA,CAAe,OAAA,CAAQ,WAAW,CAAA;AAEvD,EAAA,IAAI,mBAAmB,YAAA,EAAc;AACnC,IAAA,MAAM,OAAA,GAAU,GAAA,GAAM,QAAA,CAAS,YAAA,EAAc,EAAE,CAAA;AAC/C,IAAA,IAAI,UAAU,SAAA,EAAW;AAEvB,MAAA,cAAA,CAAe,OAAA,CAAQ,WAAA,EAAa,GAAA,CAAI,QAAA,EAAU,CAAA;AAClD,MAAA,cAAA,CAAe,OAAA,CAAQ,WAAW,eAAe,CAAA;AACjD,MAAA,OAAO,eAAA;AAAA,IACT;AAAA,EACF;AAGA,EAAA,MAAM,aAAa,UAAA,EAAW;AAC9B,EAAA,cAAA,CAAe,OAAA,CAAQ,WAAW,UAAU,CAAA;AAC5C,EAAA,cAAA,CAAe,OAAA,CAAQ,WAAA,EAAa,GAAA,CAAI,QAAA,EAAU,CAAA;AAClD,EAAA,OAAO,UAAA;AACT;AAWA,IAAM,sBAAA,GAAyBA,oBAAsC,IAAI,CAAA;AAQlE,SAAS,uBAAA,CAAwB;AAAA,EACtC,SAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAiC;AAC/B,EAAA,MAAM,KAAA,GAAQC,aAAA,CAAQ,OAAO,EAAE,SAAA,EAAW,WAAU,CAAA,EAAI,CAAC,SAAA,EAAW,SAAS,CAAC,CAAA;AAE9E,EAAA,uBACEC,cAAA,CAAC,sBAAA,CAAuB,QAAA,EAAvB,EAAgC,OAC9B,QAAA,EACH,CAAA;AAEJ;AChFA,IAAM,qBAAA,GAAwBC,wBAAA;AAAA,EAC5B,MAAM,OAAO,iCAAgC,CAAA,CAAE,IAAA,CAAK,QAAM,EAAE,OAAA,EAAS,CAAA,CAAE,iBAAA,EAAkB,CAAE,CAAA;AAAA,EAC3F,EAAE,KAAK,KAAA;AACT,CAAA;AACA,IAAM,gBAAA,GAAmBA,wBAAA;AAAA,EACvB,MAAM,OAAO,4BAAwB,CAAA,CAAE,IAAA,CAAK,QAAM,EAAE,OAAA,EAAS,CAAA,CAAE,YAAA,EAAa,CAAE,CAAA;AAAA,EAC9E,EAAE,KAAK,KAAA;AACT,CAAA;AACA,IAAM,gBAAA,GAAmBA,wBAAA;AAAA,EACvB,MAAM,OAAO,4BAAwB,CAAA,CAAE,IAAA,CAAK,QAAM,EAAE,OAAA,EAAS,CAAA,CAAE,YAAA,EAAa,CAAE,CAAA;AAAA,EAC9E,EAAE,KAAK,KAAA;AACT,CAAA;AACA,IAAM,eAAA,GAAkBA,wBAAA;AAAA,EACtB,MAAM,OAAO,2BAAgB,CAAA,CAAE,IAAA,CAAK,QAAM,EAAE,OAAA,EAAS,CAAA,CAAE,WAAA,EAAY,CAAE,CAAA;AAAA,EACrE,EAAE,KAAK,KAAA;AACT,CAAA;AAcO,SAAS,sBAAA,CAAuB;AAAA,EACrC,QAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA,GAAY,IAAA;AAAA,EACZ,MAAA,GAAS,IAAA;AAAA,EACT,MAAA,GAAS,KAAA;AAAA,EACT,WAAA,GAAc,IAAA;AAAA,EACd,KAAA,GAAQ;AACV,CAAA,EAAgC;AAE9B,EAAA,MAAM,eAAA,GACJ,OAAO,SAAA,KAAc,QAAA,GAAW,YAAY,EAAC;AAM/C,EAAA,MAAM,eAAe,eAAA,CAAgB,IAAA;AACrC,EAAA,MAAM,aACJ,OAAO,OAAA,KAAY,WAAA,GACf,OAAA,CAAQ,IAAI,oBAAA,GACZ,MAAA;AACN,EAAA,IAAI,eAAe,YAAA,IAAgB,EAAA;AACnC,EAAA,IAAI,CAAC,gBAAgB,UAAA,EAAY;AAC/B,IAAA,IAAI;AACF,MAAA,YAAA,GAAe,IAAI,GAAA,CAAI,UAAU,CAAA,CAAE,IAAA;AAAA,IACrC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACA,EAAA,IAAI,CAAC,YAAA,IAAgB,OAAO,MAAA,KAAW,WAAA,EAAa;AAClD,IAAA,YAAA,GAAe,OAAO,QAAA,CAAS,IAAA;AAAA,EACjC;AACA,EAAA,MAAM,IAAA,GAAO,YAAA;AAGb,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAChC,IAAC,OAAe,oBAAA,GAAuB,MAAA;AACvC,IAAC,OAAe,oBAAA,GAAuB,MAAA;AACvC,IAAC,OAAe,kBAAA,GAAqB,KAAA;AACrC,IAAC,OAAe,iBAAA,GAAoB,IAAA;AAAA,EACvC;AAGA,EAAA,MAAM,CAAC,SAAS,CAAA,GAAIC,cAAA,CAAS,MAAM,sBAAsB,CAAA;AACzD,EAAA,MAAM,CAAC,SAAS,CAAA,GAAIA,cAAA,CAAS,MAAM,sBAAsB,CAAA;AAGzD,EAAA,MAAM,eAAA,GAAkBC,aAAuC,IAAI,CAAA;AACnE,EAAA,MAAM,cAAA,GAAiBC,iBAAA,CAAY,CAAC,QAAA,KAAsC;AACxE,IAAA,eAAA,CAAgB,OAAA,GAAU,QAAA;AAAA,EAC5B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,IAAI,OAAA,mBAAUJ,cAAAA,CAAAK,mBAAA,EAAA,EAAG,QAAA,EAAS,CAAA;AAO1B,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,YAAA,GACJ,OAAO,MAAA,KAAW,QAAA,GAAW,SAAS,EAAC;AACzC,IAAA,OAAA,mBACEL,cAAAA;AAAA,MAAC,gBAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAO,IAAA;AAAA,QACP,QAAA,EAAU,aAAa,QAAA,KAAa,KAAA;AAAA,QACpC,WAAA,EAAa,aAAa,WAAA,KAAgB,KAAA;AAAA,QAC1C,gBAAA,EAAkB,aAAa,gBAAA,KAAqB,KAAA;AAAA,QACpD,SAAA;AAAA,QACA,SAAA;AAAA,QACA,eAAA;AAAA,QAEC,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,EAEJ;AAUA,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,OAAA,mBACEM,eAAA,CAAAD,mBAAA,EAAA,EACG,QAAA,EAAA;AAAA,MAAA,OAAA;AAAA,sBACDL,cAAAA,CAACO,cAAA,EAAA,EAAS,QAAA,EAAU,MAClB,QAAA,kBAAAP,cAAAA;AAAA,QAAC,qBAAA;AAAA,QAAA;AAAA,UACC,MAAA;AAAA,UACA,MAAA;AAAA,UACA,IAAA;AAAA,UACA,cAAA,EAAgB,gBAAgB,cAAA,KAAmB,KAAA;AAAA,UACnD,cAAA,EAAgB,gBAAgB,cAAA,KAAmB,KAAA;AAAA,UACnD,gBAAA,EAAkB,gBAAgB,gBAAA,KAAqB,KAAA;AAAA,UACvD,WAAA,EAAa,gBAAgB,WAAA,KAAgB,KAAA;AAAA,UAC7C,KAAA;AAAA,UACA,iBAAA,EAAmB,SAAA;AAAA,UACnB,iBAAA,EAAmB,SAAA;AAAA,UACnB;AAAA;AAAA,OACF,EACF;AAAA,KAAA,EACF,CAAA;AAAA,EAEJ;AAGA,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,YAAA,GACJ,OAAO,MAAA,KAAW,QAAA,GAAW,SAAS,EAAC;AACzC,IAAA,OAAA,mBACEM,eAAA,CAAAD,mBAAA,EAAA,EACG,QAAA,EAAA;AAAA,MAAA,OAAA;AAAA,sBACDL,cAAAA;AAAA,QAAC,gBAAA;AAAA,QAAA;AAAA,UACC,MAAA;AAAA,UACA,MAAA;AAAA,UACA,SAAA;AAAA,UACA,QAAA,EAAU,aAAa,QAAA,IAAY,cAAA;AAAA,UACnC,WAAA,EAAa,aAAa,WAAA,KAAgB;AAAA;AAAA;AAC5C,KAAA,EACF,CAAA;AAAA,EAEJ;AAGA,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAA,mBACEM,eAAA,CAAAD,mBAAA,EAAA,EACG,QAAA,EAAA;AAAA,MAAA,OAAA;AAAA,sBACDL,cAAAA,CAAC,eAAA,EAAA,EAAgB,KAAA,EAAc;AAAA,KAAA,EACjC,CAAA;AAAA,EAEJ;AAEA,EAAA,uBACEA,cAAAA,CAAC,uBAAA,EAAA,EAAwB,SAAA,EAAsB,WAC5C,QAAA,EAAA,OAAA,EACH,CAAA;AAEJ","file":"chunk-FMI65E5Y.js","sourcesContent":["/**\n * @sonordev/site-kit/shared — Unified Identity Management\n *\n * Single source of truth for visitor and session IDs across\n * Analytics, Signal, and Engage modules.\n *\n * Storage keys:\n * localStorage: _sk_vid (visitor ID, persists ~forever)\n * sessionStorage: _sk_sid (session ID, per-tab)\n * sessionStorage: _sk_stime (last activity timestamp for timeout)\n *\n * Storage keys are prefixed with `_sk_*`.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo } from 'react'\n\n// ============================================\n// ID Generation\n// ============================================\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)\n })\n}\n\n/**\n * Get or create a persistent visitor ID.\n * Stored in localStorage under `_sk_vid`.\n */\nexport function getOrCreateVisitorId(): string {\n if (typeof window === 'undefined') return ''\n\n let visitorId = localStorage.getItem('_sk_vid')\n\n if (!visitorId) {\n visitorId = generateId()\n }\n\n localStorage.setItem('_sk_vid', visitorId)\n return visitorId\n}\n\n/**\n * Get or create a session ID with inactivity timeout.\n * Stored in sessionStorage under `_sk_sid`.\n * Session expires after `timeoutMinutes` of inactivity (default 30).\n */\nexport function getOrCreateSessionId(timeoutMinutes = 30): string {\n if (typeof window === 'undefined') return ''\n\n const now = Date.now()\n const timeoutMs = timeoutMinutes * 60 * 1000\n\n const existingSession = sessionStorage.getItem('_sk_sid')\n const lastActivity = sessionStorage.getItem('_sk_stime')\n\n if (existingSession && lastActivity) {\n const elapsed = now - parseInt(lastActivity, 10)\n if (elapsed < timeoutMs) {\n // Session still active — refresh timestamp\n sessionStorage.setItem('_sk_stime', now.toString())\n sessionStorage.setItem('_sk_sid', existingSession)\n return existingSession\n }\n }\n\n // New session (first visit or timeout expired)\n const newSession = generateId()\n sessionStorage.setItem('_sk_sid', newSession)\n sessionStorage.setItem('_sk_stime', now.toString())\n return newSession\n}\n\n// ============================================\n// React Context\n// ============================================\n\nexport interface SiteKitIdentity {\n visitorId: string\n sessionId: string\n}\n\nconst SiteKitIdentityContext = createContext<SiteKitIdentity | null>(null)\n\nexport interface SiteKitIdentityProviderProps {\n visitorId: string\n sessionId: string\n children: React.ReactNode\n}\n\nexport function SiteKitIdentityProvider({\n visitorId,\n sessionId,\n children,\n}: SiteKitIdentityProviderProps) {\n const value = useMemo(() => ({ visitorId, sessionId }), [visitorId, sessionId])\n\n return (\n <SiteKitIdentityContext.Provider value={value}>\n {children}\n </SiteKitIdentityContext.Provider>\n )\n}\n\n/**\n * Access the shared visitor/session identity.\n * Falls back to generating IDs directly if used outside of SiteKitIdentityProvider.\n */\nexport function useSiteKitIdentity(): SiteKitIdentity {\n const context = useContext(SiteKitIdentityContext)\n if (context) return context\n\n // Fallback for standalone usage outside SiteKitLayout\n return {\n visitorId: getOrCreateVisitorId(),\n sessionId: getOrCreateSessionId(),\n }\n}\n","'use client'\n\n/**\n * SiteKitClientProviders — Client Island\n *\n * This is the 'use client' boundary for SiteKitLayout.\n * It composes the client-only providers (Analytics, Engage, Signal, SitemapSync)\n * that require React context or browser APIs.\n *\n * All modules are lazy-loaded via next/dynamic so their JS is only downloaded\n * when the feature is enabled — keeping the initial bundle lightweight.\n *\n * All modules share a unified identity (visitor ID + session ID) via\n * SiteKitIdentityProvider, ensuring consistent tracking across Analytics,\n * Signal, and Engage.\n *\n * Props are passed down from the server-side SiteKitLayout component,\n * which reads env vars at render time.\n */\n\nimport React, { Suspense, useCallback, useRef, useState, type ReactNode } from 'react'\nimport dynamic from 'next/dynamic'\nimport {\n SiteKitIdentityProvider,\n getOrCreateVisitorId,\n getOrCreateSessionId,\n} from '../shared/identity'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\n// Lazy-load feature modules — JS only downloaded when feature is enabled\nconst LazyAnalyticsProvider = dynamic(\n () => import('../analytics/AnalyticsProvider').then(m => ({ default: m.AnalyticsProvider })),\n { ssr: false },\n)\nconst LazyEngageWidget = dynamic(\n () => import('../engage/EngageWidget').then(m => ({ default: m.EngageWidget })),\n { ssr: false },\n)\nconst LazySignalBridge = dynamic(\n () => import('../signal/SignalBridge').then(m => ({ default: m.SignalBridge })),\n { ssr: false },\n)\nconst LazySitemapSync = dynamic(\n () => import('../SitemapSync').then(m => ({ default: m.SitemapSync })),\n { ssr: false },\n)\n\nexport interface SiteKitClientProvidersProps {\n children: ReactNode\n apiKey: string\n apiUrl: string\n projectId?: string\n analytics?: boolean | AnalyticsConfig\n engage?: boolean | EngageConfig\n signal?: boolean | SignalConfig\n sitemapSync?: boolean\n debug?: boolean\n}\n\nexport function SiteKitClientProviders({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n debug = false,\n}: SiteKitClientProvidersProps) {\n // Hoist analytics config so the `site` dimension is resolvable up here.\n const analyticsConfig: AnalyticsConfig =\n typeof analytics === 'object' ? analytics : {}\n\n // Resolve the sub-site identifier — the dimension that lets one Sonor project\n // host many sites (e.g. truepowersystems.com + ohiopowerstudies.com under one\n // TPS project). Precedence: explicit `analytics.site` > NEXT_PUBLIC_SITE_URL\n // host > window.location.host. Always a string (possibly empty).\n const explicitSite = analyticsConfig.site\n const envSiteUrl =\n typeof process !== 'undefined'\n ? process.env.NEXT_PUBLIC_SITE_URL\n : undefined\n let resolvedSite = explicitSite ?? ''\n if (!resolvedSite && envSiteUrl) {\n try {\n resolvedSite = new URL(envSiteUrl).host\n } catch {\n /* malformed URL — fall through to client-side host */\n }\n }\n if (!resolvedSite && typeof window !== 'undefined') {\n resolvedSite = window.location.host\n }\n const site = resolvedSite\n\n // Set window globals for modules that still read from them.\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n ;(window as any).__SITE_KIT_SITE__ = site\n }\n\n // Unified identity — single source of truth for all modules\n const [visitorId] = useState(() => getOrCreateVisitorId())\n const [sessionId] = useState(() => getOrCreateSessionId())\n\n // Page metadata bridge: Analytics writes, Signal reads\n const pageMetadataRef = useRef<Record<string, unknown> | null>(null)\n const onPageMetadata = useCallback((metadata: Record<string, unknown>) => {\n pageMetadataRef.current = metadata\n }, [])\n\n let content = <>{children}</>\n\n // Wrap with SignalBridge if enabled.\n // NOTE: SignalBridge still wraps {content} and is dynamic(ssr:false) — the\n // same SSR-de-opt caveat as Analytics applies. It is left wrapping because\n // A/B experiment variant swapping may depend on enclosing page content; if a\n // site enables `signal` and SSR breaks, convert this to a sibling too.\n if (signal) {\n const signalConfig: SignalConfig =\n typeof signal === 'object' ? signal : {}\n content = (\n <LazySignalBridge\n enabled\n realtime={signalConfig.realtime !== false}\n experiments={signalConfig.experiments !== false}\n behaviorTracking={signalConfig.behaviorTracking !== false}\n visitorId={visitorId}\n sessionId={sessionId}\n pageMetadataRef={pageMetadataRef}\n >\n {content}\n </LazySignalBridge>\n )\n }\n\n // Add Analytics if enabled (renders alongside, doesn't wrap).\n //\n // CRITICAL: LazyAnalyticsProvider is dynamic(..., { ssr: false }). Wrapping\n // {content} inside an ssr:false component makes the entire page tree skip\n // server rendering — the route de-opts to 100% client rendering and the\n // hero / LCP element only paints after hydration (mobile LCP ~4s, Lighthouse\n // ~85). Analytics tracks via effects + a metadata ref, so it never needs to\n // wrap page content — mount it as a childless sibling, exactly like Engage.\n if (analytics) {\n content = (\n <>\n {content}\n <Suspense fallback={null}>\n <LazyAnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n site={site}\n trackPageViews={analyticsConfig.trackPageViews !== false}\n trackWebVitals={analyticsConfig.trackWebVitals !== false}\n trackScrollDepth={analyticsConfig.trackScrollDepth !== false}\n trackClicks={analyticsConfig.trackClicks !== false}\n debug={debug}\n externalVisitorId={visitorId}\n externalSessionId={sessionId}\n onPageMetadata={onPageMetadata}\n />\n </Suspense>\n </>\n )\n }\n\n // Add Engage widget if enabled (renders alongside, doesn't wrap)\n if (engage) {\n const engageConfig: EngageConfig =\n typeof engage === 'object' ? engage : {}\n content = (\n <>\n {content}\n <LazyEngageWidget\n apiUrl={apiUrl}\n apiKey={apiKey}\n projectId={projectId}\n position={engageConfig.position || 'bottom-right'}\n chatEnabled={engageConfig.chatEnabled !== false}\n />\n </>\n )\n }\n\n // Add SitemapSync if enabled\n if (sitemapSync) {\n content = (\n <>\n {content}\n <LazySitemapSync debug={debug} />\n </>\n )\n }\n\n return (\n <SiteKitIdentityProvider visitorId={visitorId} sessionId={sessionId}>\n {content}\n </SiteKitIdentityProvider>\n )\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
var chunkMLJXELPA_js = require('./chunk-MLJXELPA.js');
|
|
4
5
|
var React2 = require('react');
|
|
5
6
|
var ReactDOM = require('react-dom');
|
|
6
7
|
var navigation = require('next/navigation');
|
|
@@ -13,7 +14,260 @@ var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
|
13
14
|
var ReactDOM__default = /*#__PURE__*/_interopDefault(ReactDOM);
|
|
14
15
|
var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
function alpha(rgb, a) {
|
|
18
|
+
return `rgba(var(--sk-primary-rgb, ${rgb}), ${a})`;
|
|
19
|
+
}
|
|
20
|
+
function asString(v) {
|
|
21
|
+
return typeof v === "string" && v.trim() ? v : void 0;
|
|
22
|
+
}
|
|
23
|
+
function asRecord(v) {
|
|
24
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
25
|
+
}
|
|
26
|
+
function isNavigable(action) {
|
|
27
|
+
return /^(https?:\/\/|\/|#|tel:|mailto:)/i.test(action.trim());
|
|
28
|
+
}
|
|
29
|
+
var DISPLAY_ONLY = ["heading", "section_header", "paragraph"];
|
|
30
|
+
function InlineManagedForm({ slug, prefill, primaryColor, primaryRgb }) {
|
|
31
|
+
const {
|
|
32
|
+
form,
|
|
33
|
+
isLoading,
|
|
34
|
+
fetchError,
|
|
35
|
+
submitError,
|
|
36
|
+
visibleFields,
|
|
37
|
+
values,
|
|
38
|
+
errors,
|
|
39
|
+
setFieldValue,
|
|
40
|
+
submit,
|
|
41
|
+
isSubmitting,
|
|
42
|
+
isComplete
|
|
43
|
+
} = chunkMLJXELPA_js.useForm(slug, { initialValues: prefill || {} });
|
|
44
|
+
const cardStyle = {
|
|
45
|
+
marginTop: 8,
|
|
46
|
+
padding: 12,
|
|
47
|
+
borderRadius: 12,
|
|
48
|
+
border: `1px solid ${alpha(primaryRgb, 0.25)}`,
|
|
49
|
+
backgroundColor: alpha(primaryRgb, 0.04)
|
|
50
|
+
};
|
|
51
|
+
if (isLoading) {
|
|
52
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: cardStyle, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, fontSize: 13, color: "var(--sk-text-tertiary, #9ca3af)" }, children: "Loading form\u2026" }) });
|
|
53
|
+
}
|
|
54
|
+
if (fetchError || !form) {
|
|
55
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: cardStyle, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, fontSize: 13, color: "var(--sk-text-tertiary, #9ca3af)" }, children: "This form is unavailable right now." }) });
|
|
56
|
+
}
|
|
57
|
+
if (isComplete) {
|
|
58
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: cardStyle, role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, fontSize: 13.5, color: "var(--sk-text-primary, #111827)", lineHeight: 1.5 }, children: form.success_message }) });
|
|
59
|
+
}
|
|
60
|
+
const inputBase = {
|
|
61
|
+
width: "100%",
|
|
62
|
+
padding: "8px 10px",
|
|
63
|
+
borderRadius: 8,
|
|
64
|
+
// Longhand (not the `border` shorthand) so the error-state `borderColor`
|
|
65
|
+
// override doesn't mix shorthand+longhand and trigger React's style warning.
|
|
66
|
+
borderWidth: 1,
|
|
67
|
+
borderStyle: "solid",
|
|
68
|
+
borderColor: "var(--sk-border, #e5e7eb)",
|
|
69
|
+
backgroundColor: "var(--sk-bg, #ffffff)",
|
|
70
|
+
color: "var(--sk-text-primary, #111827)",
|
|
71
|
+
fontSize: 13.5,
|
|
72
|
+
boxSizing: "border-box",
|
|
73
|
+
outline: "none"
|
|
74
|
+
};
|
|
75
|
+
const renderField = (field) => {
|
|
76
|
+
const value = values[field.slug];
|
|
77
|
+
const strValue = value == null ? "" : String(value);
|
|
78
|
+
const err = errors[field.slug];
|
|
79
|
+
const labelEl = !field.hide_label && field.label ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
80
|
+
"label",
|
|
81
|
+
{
|
|
82
|
+
htmlFor: `echo-form-${form.id}-${field.slug}`,
|
|
83
|
+
style: { display: "block", fontSize: 12.5, fontWeight: 600, marginBottom: 4, color: "var(--sk-text-secondary, #4b5563)" },
|
|
84
|
+
children: [
|
|
85
|
+
field.label,
|
|
86
|
+
field.is_required ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--sk-error, #ef4444)" }, children: " *" }) : null
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
) : null;
|
|
90
|
+
const id = `echo-form-${form.id}-${field.slug}`;
|
|
91
|
+
const common = {
|
|
92
|
+
id,
|
|
93
|
+
placeholder: field.placeholder || (field.hide_label ? field.label : void 0),
|
|
94
|
+
"aria-invalid": !!err,
|
|
95
|
+
style: { ...inputBase, ...err ? { borderColor: "var(--sk-error, #ef4444)" } : {} }
|
|
96
|
+
};
|
|
97
|
+
let control;
|
|
98
|
+
switch (field.field_type) {
|
|
99
|
+
case "textarea":
|
|
100
|
+
control = /* @__PURE__ */ jsxRuntime.jsx(
|
|
101
|
+
"textarea",
|
|
102
|
+
{
|
|
103
|
+
...common,
|
|
104
|
+
rows: 3,
|
|
105
|
+
value: strValue,
|
|
106
|
+
onChange: (e) => setFieldValue(field.slug, e.target.value),
|
|
107
|
+
style: { ...common.style, resize: "vertical" }
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
break;
|
|
111
|
+
case "select":
|
|
112
|
+
control = /* @__PURE__ */ jsxRuntime.jsxs("select", { ...common, value: strValue, onChange: (e) => setFieldValue(field.slug, e.target.value), children: [
|
|
113
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: field.placeholder || "Select\u2026" }),
|
|
114
|
+
(field.options || []).map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
115
|
+
] });
|
|
116
|
+
break;
|
|
117
|
+
case "radio":
|
|
118
|
+
control = /* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", style: { display: "flex", flexDirection: "column", gap: 4 }, children: (field.options || []).map((opt) => /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: 13, cursor: "pointer" }, children: [
|
|
119
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
120
|
+
"input",
|
|
121
|
+
{
|
|
122
|
+
type: "radio",
|
|
123
|
+
name: id,
|
|
124
|
+
checked: strValue === opt.value,
|
|
125
|
+
onChange: () => setFieldValue(field.slug, opt.value)
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
opt.label
|
|
129
|
+
] }, opt.value)) });
|
|
130
|
+
break;
|
|
131
|
+
case "checkbox":
|
|
132
|
+
control = /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: 13, cursor: "pointer" }, children: [
|
|
133
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
134
|
+
"input",
|
|
135
|
+
{
|
|
136
|
+
type: "checkbox",
|
|
137
|
+
checked: value === true || value === "true",
|
|
138
|
+
onChange: (e) => setFieldValue(field.slug, e.target.checked)
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
field.placeholder || field.label
|
|
142
|
+
] });
|
|
143
|
+
break;
|
|
144
|
+
default: {
|
|
145
|
+
const typeMap = {
|
|
146
|
+
email: "email",
|
|
147
|
+
phone: "tel",
|
|
148
|
+
tel: "tel",
|
|
149
|
+
number: "number",
|
|
150
|
+
date: "date",
|
|
151
|
+
time: "time",
|
|
152
|
+
datetime: "datetime-local"
|
|
153
|
+
};
|
|
154
|
+
control = /* @__PURE__ */ jsxRuntime.jsx(
|
|
155
|
+
"input",
|
|
156
|
+
{
|
|
157
|
+
...common,
|
|
158
|
+
type: typeMap[field.field_type] || "text",
|
|
159
|
+
value: strValue,
|
|
160
|
+
onChange: (e) => setFieldValue(field.slug, e.target.value)
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 8 }, children: [
|
|
166
|
+
field.field_type === "checkbox" ? null : labelEl,
|
|
167
|
+
control,
|
|
168
|
+
err ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "4px 0 0", fontSize: 11.5, color: "var(--sk-error, #ef4444)" }, children: err }) : null
|
|
169
|
+
] }, field.id || field.slug);
|
|
170
|
+
};
|
|
171
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
172
|
+
"form",
|
|
173
|
+
{
|
|
174
|
+
style: cardStyle,
|
|
175
|
+
onSubmit: (e) => {
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
void submit();
|
|
178
|
+
},
|
|
179
|
+
children: [
|
|
180
|
+
form.name ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13.5, fontWeight: 600, marginBottom: 8, color: "var(--sk-text-primary, #111827)" }, children: form.name }) : null,
|
|
181
|
+
visibleFields.map((field) => {
|
|
182
|
+
if (field.field_type === "hidden") return null;
|
|
183
|
+
if (DISPLAY_ONLY.includes(field.field_type)) {
|
|
184
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "0 0 8px", fontSize: 12.5, color: "var(--sk-text-secondary, #4b5563)" }, children: field.label }, field.id || field.slug);
|
|
185
|
+
}
|
|
186
|
+
return renderField(field);
|
|
187
|
+
}),
|
|
188
|
+
submitError ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", style: { margin: "0 0 8px", fontSize: 12, color: "var(--sk-error, #ef4444)" }, children: submitError }) : null,
|
|
189
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
190
|
+
"button",
|
|
191
|
+
{
|
|
192
|
+
type: "submit",
|
|
193
|
+
disabled: isSubmitting,
|
|
194
|
+
style: {
|
|
195
|
+
width: "100%",
|
|
196
|
+
padding: "9px 14px",
|
|
197
|
+
borderRadius: 8,
|
|
198
|
+
border: "none",
|
|
199
|
+
backgroundColor: primaryColor,
|
|
200
|
+
color: "#fff",
|
|
201
|
+
fontSize: 13.5,
|
|
202
|
+
fontWeight: 600,
|
|
203
|
+
cursor: isSubmitting ? "default" : "pointer",
|
|
204
|
+
opacity: isSubmitting ? 0.7 : 1
|
|
205
|
+
},
|
|
206
|
+
children: isSubmitting ? "Sending\u2026" : form.submit_button_text || "Submit"
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
function SuggestActionChip({ action, message, primaryColor, primaryRgb, onSuggest }) {
|
|
214
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
215
|
+
"button",
|
|
216
|
+
{
|
|
217
|
+
type: "button",
|
|
218
|
+
onClick: () => onSuggest(action, message),
|
|
219
|
+
style: {
|
|
220
|
+
padding: "6px 12px",
|
|
221
|
+
borderRadius: 16,
|
|
222
|
+
border: `1px solid ${primaryColor}`,
|
|
223
|
+
backgroundColor: alpha(primaryRgb, 0.063),
|
|
224
|
+
color: primaryColor,
|
|
225
|
+
fontSize: 13,
|
|
226
|
+
cursor: "pointer"
|
|
227
|
+
},
|
|
228
|
+
children: message
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
function EchoUiActions({ actions, primaryColor, primaryRgb, onSuggest }) {
|
|
233
|
+
if (!actions?.length) return null;
|
|
234
|
+
const chips = actions.filter((a) => a?.type === "suggest_action");
|
|
235
|
+
const forms = actions.filter((a) => a?.type === "show_form");
|
|
236
|
+
if (!chips.length && !forms.length) return null;
|
|
237
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 8, display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
238
|
+
forms.map((a, i) => {
|
|
239
|
+
const slug = asString(a.data?.formSlug);
|
|
240
|
+
if (!slug) return null;
|
|
241
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
242
|
+
InlineManagedForm,
|
|
243
|
+
{
|
|
244
|
+
slug,
|
|
245
|
+
prefill: asRecord(a.data?.prefill),
|
|
246
|
+
primaryColor,
|
|
247
|
+
primaryRgb
|
|
248
|
+
},
|
|
249
|
+
`form-${slug}-${i}`
|
|
250
|
+
);
|
|
251
|
+
}),
|
|
252
|
+
chips.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: chips.map((a, i) => {
|
|
253
|
+
const message = asString(a.data?.message);
|
|
254
|
+
const action = asString(a.data?.action);
|
|
255
|
+
const label = message || action;
|
|
256
|
+
if (!label) return null;
|
|
257
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
258
|
+
SuggestActionChip,
|
|
259
|
+
{
|
|
260
|
+
action,
|
|
261
|
+
message: label,
|
|
262
|
+
primaryColor,
|
|
263
|
+
primaryRgb,
|
|
264
|
+
onSuggest
|
|
265
|
+
},
|
|
266
|
+
`chip-${i}`
|
|
267
|
+
);
|
|
268
|
+
}) }) : null
|
|
269
|
+
] });
|
|
270
|
+
}
|
|
17
271
|
function getApiConfig() {
|
|
18
272
|
const isDev = typeof process !== "undefined" && process.env?.NODE_ENV === "development";
|
|
19
273
|
const defaultApiUrl = isDev && process.env?.SONOR_API_URL ? process.env.SONOR_API_URL : "https://api.sonor.io";
|
|
@@ -71,8 +325,8 @@ function hexToRgbString(hex, fallback = "59, 130, 246") {
|
|
|
71
325
|
if (!match) return fallback;
|
|
72
326
|
return `${parseInt(match[1], 16)}, ${parseInt(match[2], 16)}, ${parseInt(match[3], 16)}`;
|
|
73
327
|
}
|
|
74
|
-
function hexAlpha(rgbVar,
|
|
75
|
-
return `rgba(var(${rgbVar}, ${fallbackRgb}), ${
|
|
328
|
+
function hexAlpha(rgbVar, alpha2, fallbackRgb = "59, 130, 246") {
|
|
329
|
+
return `rgba(var(${rgbVar}, ${fallbackRgb}), ${alpha2})`;
|
|
76
330
|
}
|
|
77
331
|
function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl, signalUrl: propSignalUrl, suggested_questions: propSuggestedQuestions, assistant_name: propAssistantName, welcome_message: propWelcomeMessage, assistant_avatar: propAssistantAvatar }) {
|
|
78
332
|
const [resolvedProjectId, setResolvedProjectId] = React2.useState(propProjectId || null);
|
|
@@ -268,11 +522,29 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl, sign
|
|
|
268
522
|
const aiContent = data?.content ?? data?.response ?? data?.message ?? "I'm sorry, I couldn't process that.";
|
|
269
523
|
const suggestions = data?.suggestions;
|
|
270
524
|
const offerHandoff = data?.offerHandoff === true || data?.offer_handoff === true;
|
|
525
|
+
const uiActions = Array.isArray(data?.uiActions) ? data.uiActions.filter((a) => a && typeof a === "object" && typeof a.type === "string" && a.data && typeof a.data === "object") : void 0;
|
|
271
526
|
const newConversationId = data?.conversationId ?? conversationId;
|
|
272
|
-
return { content: aiContent, suggestions, offerHandoff, conversationId: newConversationId };
|
|
527
|
+
return { content: aiContent, suggestions, offerHandoff, uiActions, conversationId: newConversationId };
|
|
273
528
|
},
|
|
274
529
|
[signalUrl, visitorId, fetchSiteContext]
|
|
275
530
|
);
|
|
531
|
+
const pushEchoReply = React2.useCallback(
|
|
532
|
+
(reply, probeContent) => {
|
|
533
|
+
setMessages((prev) => [
|
|
534
|
+
...prev,
|
|
535
|
+
{
|
|
536
|
+
id: `ai-${Date.now()}`,
|
|
537
|
+
role: "assistant",
|
|
538
|
+
content: reply.content,
|
|
539
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
540
|
+
...reply.suggestions?.length ? { suggestions: reply.suggestions } : {},
|
|
541
|
+
...reply.uiActions?.length ? { uiActions: reply.uiActions } : {},
|
|
542
|
+
...reply.offerHandoff || userRequestedHandoff(probeContent) ? { offerHandoff: true } : {}
|
|
543
|
+
}
|
|
544
|
+
]);
|
|
545
|
+
},
|
|
546
|
+
[]
|
|
547
|
+
);
|
|
276
548
|
const handleSocketMessage = React2.useCallback((data) => {
|
|
277
549
|
switch (data.type || data.event) {
|
|
278
550
|
case "message": {
|
|
@@ -475,19 +747,8 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl, sign
|
|
|
475
747
|
{ id: `u-${Date.now()}`, role: "user", content: pending, timestamp: /* @__PURE__ */ new Date() }
|
|
476
748
|
]);
|
|
477
749
|
try {
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
setMessages((prev) => [
|
|
481
|
-
...prev,
|
|
482
|
-
{
|
|
483
|
-
id: `ai-${Date.now()}`,
|
|
484
|
-
role: "assistant",
|
|
485
|
-
content: aiContent,
|
|
486
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
487
|
-
...suggestions?.length ? { suggestions } : {},
|
|
488
|
-
...offerHandoff || userWantsHandoff ? { offerHandoff: true } : {}
|
|
489
|
-
}
|
|
490
|
-
]);
|
|
750
|
+
const reply = await sendToSignalApi(pending, id);
|
|
751
|
+
pushEchoReply(reply, pending);
|
|
491
752
|
} catch (err) {
|
|
492
753
|
console.error("[ChatWidget] Signal API error:", err);
|
|
493
754
|
setMessages((prev) => [
|
|
@@ -530,7 +791,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl, sign
|
|
|
530
791
|
return () => {
|
|
531
792
|
if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current);
|
|
532
793
|
};
|
|
533
|
-
}, [isOpen, showWelcome, checkingAvailability, showOfflineForm, sessionId, initSession, connectSocket, sendToSignalApi]);
|
|
794
|
+
}, [isOpen, showWelcome, checkingAvailability, showOfflineForm, sessionId, initSession, connectSocket, sendToSignalApi, pushEchoReply]);
|
|
534
795
|
const handleToggle = React2.useCallback(() => {
|
|
535
796
|
setIsOpen((prev) => !prev);
|
|
536
797
|
}, []);
|
|
@@ -622,19 +883,9 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl, sign
|
|
|
622
883
|
setIsLoading(true);
|
|
623
884
|
if (widgetConfig?.signal_enabled && !isHandedOff && hasText) {
|
|
624
885
|
try {
|
|
625
|
-
const
|
|
886
|
+
const reply = await sendToSignalApi(content, sessionId);
|
|
626
887
|
const lastUserContent = messages.filter((m) => m.role === "user").slice(-1)[0]?.content ?? "";
|
|
627
|
-
|
|
628
|
-
...prev,
|
|
629
|
-
{
|
|
630
|
-
id: `ai-${Date.now()}`,
|
|
631
|
-
role: "assistant",
|
|
632
|
-
content: aiContent,
|
|
633
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
634
|
-
...suggestions?.length ? { suggestions } : {},
|
|
635
|
-
...offerHandoff || userRequestedHandoff(lastUserContent) ? { offerHandoff: true } : {}
|
|
636
|
-
}
|
|
637
|
-
]);
|
|
888
|
+
pushEchoReply(reply, lastUserContent);
|
|
638
889
|
} catch (err) {
|
|
639
890
|
console.error("[ChatWidget] Signal API error:", err);
|
|
640
891
|
setMessages((prev) => [
|
|
@@ -670,7 +921,52 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl, sign
|
|
|
670
921
|
}
|
|
671
922
|
}, 3e3);
|
|
672
923
|
},
|
|
673
|
-
[inputValue, isLoading, pendingFiles, uploadWidgetFile, widgetConfig?.signal_enabled, isHandedOff, sendToSignalApi, sessionId]
|
|
924
|
+
[inputValue, isLoading, pendingFiles, uploadWidgetFile, widgetConfig?.signal_enabled, isHandedOff, sendToSignalApi, sessionId, pushEchoReply]
|
|
925
|
+
);
|
|
926
|
+
const sendEchoText = React2.useCallback(
|
|
927
|
+
async (text) => {
|
|
928
|
+
const content = text.trim();
|
|
929
|
+
if (!content || isLoading) return;
|
|
930
|
+
setMessages((prev) => [
|
|
931
|
+
...prev,
|
|
932
|
+
{ id: `user-${Date.now()}`, role: "user", content, timestamp: /* @__PURE__ */ new Date() }
|
|
933
|
+
]);
|
|
934
|
+
setIsLoading(true);
|
|
935
|
+
try {
|
|
936
|
+
const reply = await sendToSignalApi(content, sessionId);
|
|
937
|
+
pushEchoReply(reply, content);
|
|
938
|
+
} catch (err) {
|
|
939
|
+
console.error("[ChatWidget] Signal API error:", err);
|
|
940
|
+
setMessages((prev) => [
|
|
941
|
+
...prev,
|
|
942
|
+
{
|
|
943
|
+
id: `err-${Date.now()}`,
|
|
944
|
+
role: "assistant",
|
|
945
|
+
content: "I apologize, but I encountered an error. Would you like to speak with a team member?",
|
|
946
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
947
|
+
suggestions: ["Talk to a person", "Try again"],
|
|
948
|
+
offerHandoff: true
|
|
949
|
+
}
|
|
950
|
+
]);
|
|
951
|
+
} finally {
|
|
952
|
+
setIsLoading(false);
|
|
953
|
+
}
|
|
954
|
+
},
|
|
955
|
+
[isLoading, sessionId, sendToSignalApi, pushEchoReply]
|
|
956
|
+
);
|
|
957
|
+
const handleSuggestAction = React2.useCallback(
|
|
958
|
+
(action, message) => {
|
|
959
|
+
const a = (action || "").trim();
|
|
960
|
+
if (a && isNavigable(a)) {
|
|
961
|
+
if (typeof window !== "undefined") {
|
|
962
|
+
if (/^https?:\/\//i.test(a)) window.open(a, "_blank", "noopener,noreferrer");
|
|
963
|
+
else window.location.href = a;
|
|
964
|
+
}
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
if (message?.trim()) void sendEchoText(message);
|
|
968
|
+
},
|
|
969
|
+
[sendEchoText]
|
|
674
970
|
);
|
|
675
971
|
const retryFailedSend = React2.useCallback(() => {
|
|
676
972
|
if (!lastFailedSend || !sessionId) return;
|
|
@@ -1148,6 +1444,15 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl, sign
|
|
|
1148
1444
|
},
|
|
1149
1445
|
i
|
|
1150
1446
|
)) }) : null,
|
|
1447
|
+
message.uiActions?.length ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1448
|
+
EchoUiActions,
|
|
1449
|
+
{
|
|
1450
|
+
actions: message.uiActions,
|
|
1451
|
+
primaryColor,
|
|
1452
|
+
primaryRgb,
|
|
1453
|
+
onSuggest: handleSuggestAction
|
|
1454
|
+
}
|
|
1455
|
+
) : null,
|
|
1151
1456
|
message.sendFailed && lastFailedSend && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1152
1457
|
"button",
|
|
1153
1458
|
{
|
|
@@ -2022,5 +2327,5 @@ function getDeviceType() {
|
|
|
2022
2327
|
exports.ChatWidget = ChatWidget;
|
|
2023
2328
|
exports.DesignRenderer = DesignRenderer;
|
|
2024
2329
|
exports.EngageWidget = EngageWidget;
|
|
2025
|
-
//# sourceMappingURL=chunk-
|
|
2026
|
-
//# sourceMappingURL=chunk-
|
|
2330
|
+
//# sourceMappingURL=chunk-FPU2QPO4.js.map
|
|
2331
|
+
//# sourceMappingURL=chunk-FPU2QPO4.js.map
|