@particle-academy/fancy-pixel 0.1.0 → 0.2.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/README.md +73 -8
- package/dist/fancy-pixel.global.min.js +9 -9
- package/dist/fancy-pixel.global.min.js.map +1 -1
- package/dist/index.cjs +483 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -4
- package/dist/index.d.ts +56 -4
- package/dist/index.js +482 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
# @particle-academy/fancy-pixel
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
**All-in-one Fancy UI embed.** A single `<script>` renders the verification
|
|
4
|
+
**badge** AND pipes the site's full interaction analytics to your host —
|
|
5
|
+
Google-Analytics-style, keyed by `site_key`. Vanilla TypeScript, rendered
|
|
6
|
+
inside an **open Shadow DOM** so the host page's CSS cannot hide it (visibility
|
|
7
|
+
is part of verification).
|
|
8
|
+
|
|
9
|
+
One embed delivers three things:
|
|
10
|
+
|
|
11
|
+
1. **Badge** — a "Powered by Fancy UI" chip / mark / beacon, host-CSS-proof.
|
|
12
|
+
2. **Verification beacon** — a `sendBeacon` liveness ping to `${endpoint}/pixel`
|
|
13
|
+
on mount and every visibility change (proves the badge is really on-screen).
|
|
14
|
+
3. **Interaction analytics** — clicks, scroll depth, pointer heatmap, dwell, and
|
|
15
|
+
embedded-agent activity streamed to `${endpoint}/collect` via the bundled
|
|
16
|
+
[`@particle-academy/fancy-heuristics-js`](https://github.com/Particle-Academy/fancy-heuristics-js)
|
|
17
|
+
collector.
|
|
18
|
+
|
|
19
|
+
The collector is **bundled** into every build (including the single IIFE global
|
|
20
|
+
embed) — there is no separate install. The only third-party-free dependency is a
|
|
21
|
+
sibling `@particle-academy` package. **No `endpoint` = badge only** (no beacon,
|
|
22
|
+
no collection, no network at all).
|
|
6
23
|
|
|
7
24
|
Three styles, two placement modes:
|
|
8
25
|
|
|
@@ -30,7 +47,8 @@ npm install @particle-academy/fancy-pixel
|
|
|
30
47
|
## Embed via `<script>` (no build step)
|
|
31
48
|
|
|
32
49
|
A single tag both loads and auto-mounts, reading config from its own
|
|
33
|
-
`data-*` attributes
|
|
50
|
+
`data-*` attributes. With an `endpoint`, this **one tag** mounts the badge, fires
|
|
51
|
+
the verification beacon, and starts streaming interaction analytics:
|
|
34
52
|
|
|
35
53
|
```html
|
|
36
54
|
<script
|
|
@@ -44,7 +62,21 @@ A single tag both loads and auto-mounts, reading config from its own
|
|
|
44
62
|
|
|
45
63
|
Attributes: `data-style` (`badge`|`mark`|`beacon`), `data-mode`
|
|
46
64
|
(`placed`|`floating`), `data-site`, `data-endpoint`, optional `data-target`
|
|
47
|
-
(selector, for `placed`)
|
|
65
|
+
(selector, for `placed`), `data-href` (link target), and `data-collect`.
|
|
66
|
+
|
|
67
|
+
**Badge-only opt-out:** add `data-collect="false"` to render the badge and send
|
|
68
|
+
the liveness beacon but **not** stream interaction analytics:
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<script
|
|
72
|
+
src="https://unpkg.com/@particle-academy/fancy-pixel/dist/fancy-pixel.global.min.js"
|
|
73
|
+
data-site="YOUR_SITE_KEY"
|
|
74
|
+
data-endpoint="https://your-host/heuristics"
|
|
75
|
+
data-collect="false"
|
|
76
|
+
></script>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Omit `data-endpoint` entirely for a pure badge with no network of any kind.
|
|
48
80
|
|
|
49
81
|
The global build also exposes `window.FancyPixel.mountPixel(...)` for manual
|
|
50
82
|
mounts.
|
|
@@ -60,12 +92,15 @@ const pixel = mountPixel({
|
|
|
60
92
|
target: "#footer", // selector or Element (placed mode)
|
|
61
93
|
siteKey: "YOUR_SITE_KEY",
|
|
62
94
|
endpoint: "https://your-host/heuristics",
|
|
95
|
+
collect: true, // default; set false for badge + beacon only
|
|
63
96
|
href: "https://particle.academy", // optional link target
|
|
64
97
|
});
|
|
65
98
|
|
|
66
|
-
// pixel.host
|
|
67
|
-
// pixel.visible
|
|
68
|
-
// pixel.
|
|
99
|
+
// pixel.host -> the host element (open shadow root + data markers)
|
|
100
|
+
// pixel.visible -> current IntersectionObserver-confirmed visibility
|
|
101
|
+
// pixel.collector -> the live interaction collector (null when collect is off
|
|
102
|
+
// or no endpoint); started for you, stopped by destroy()
|
|
103
|
+
// pixel.destroy() -> stop the collector, tear down observers, remove the element
|
|
69
104
|
```
|
|
70
105
|
|
|
71
106
|
## Visibility + beacon
|
|
@@ -91,6 +126,34 @@ const pixel = mountPixel({
|
|
|
91
126
|
|
|
92
127
|
If `endpoint` is omitted, no network request is made.
|
|
93
128
|
|
|
129
|
+
## Interaction analytics (all-in-one)
|
|
130
|
+
|
|
131
|
+
When mounted **with** an `endpoint` and `collect` left on (the default),
|
|
132
|
+
fancy-pixel also starts the bundled
|
|
133
|
+
[`@particle-academy/fancy-heuristics-js`](https://github.com/Particle-Academy/fancy-heuristics-js)
|
|
134
|
+
collector. It batches the site's interaction stream — `pageview`, `click`,
|
|
135
|
+
`scroll` depth, `pointer` heatmap samples, `dwell` time, and embedded-**agent**
|
|
136
|
+
activity — and ships it to `${endpoint}/collect` via `sendBeacon`, keyed by the
|
|
137
|
+
same `siteKey`. Batches POST as:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{ "siteKey": "YOUR_SITE_KEY", "sessionId": "...", "events": [ /* Event, ... */ ] }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Each `Event` is the frozen `fancy-heuristics` wire shape (`kind`, `actor`,
|
|
144
|
+
`path`, `ts`, plus `x`/`y`/`vw`/`vh`/`scrollPct`/`dwellMs`/`targetId`/`label` as
|
|
145
|
+
relevant) — the exact shape the PHP `fancy-heuristics` ingestion already
|
|
146
|
+
validates. `destroy()` stops the collector (flushing any buffered events).
|
|
147
|
+
|
|
148
|
+
- **Opt out** with `collect: false` (programmatic) or `data-collect="false"`
|
|
149
|
+
(script tag) to keep the badge + liveness beacon but skip analytics.
|
|
150
|
+
- **No endpoint = nothing collected.** A badge with no `endpoint` makes no
|
|
151
|
+
network requests at all.
|
|
152
|
+
|
|
153
|
+
The collector is compiled directly into `dist/fancy-pixel.global.min.js`, so the
|
|
154
|
+
single external-site embed carries everything — no second `<script>`, no extra
|
|
155
|
+
install.
|
|
156
|
+
|
|
94
157
|
## Human+ contract
|
|
95
158
|
|
|
96
159
|
This is a mostly-visual component, but it is **inhabitable**: it exposes a
|
|
@@ -109,6 +172,8 @@ any static server and open `/demo/index.html` after `npm run build`.
|
|
|
109
172
|
```bash
|
|
110
173
|
npm run build # tsup -> dist/index.js (ESM), dist/index.cjs (CJS),
|
|
111
174
|
# dist/fancy-pixel.global.min.js (IIFE, globalName FancyPixel)
|
|
175
|
+
# @particle-academy/fancy-heuristics-js is inlined into ALL three
|
|
176
|
+
# bundles (never externalized), so the global is fully standalone.
|
|
112
177
|
npm test # node --test against the built ESM bundle
|
|
113
178
|
```
|
|
114
179
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var FancyPixel=(function(exports){'use strict';function
|
|
1
|
+
var FancyPixel=(function(exports){'use strict';function _(e,t){if(!e)return false;let n=e.replace(/\/+$/,"")+"/pixel",i=JSON.stringify(t);try{if(typeof navigator<"u"&&typeof navigator.sendBeacon=="function"){let o=new Blob([i],{type:"application/json"});if(navigator.sendBeacon(n,o))return !0}}catch{}try{if(typeof fetch=="function")return fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:i,keepalive:!0,mode:"cors",credentials:"omit"}).catch(()=>{}),!0}catch{}return false}function T(e,t,n,i){return {kind:e,actor:t,path:n,ts:i}}function N(e,t,n="human"){return T("pageview",n,e,t)}function U(e){let t=T("click",e.actor??"human",e.path,e.ts);return t.x=e.x,t.y=e.y,t.vw=e.vw,t.vh=e.vh,e.targetId!==void 0&&(t.targetId=e.targetId),e.label!==void 0&&(t.label=e.label),e.meta!==void 0&&(t.meta=e.meta),t}function Y(e){let t=T("scroll",e.actor??"human",e.path,e.ts);return t.scrollPct=e.scrollPct,t}function V(e){let t=T("pointer",e.actor??"human",e.path,e.ts);return t.x=e.x,t.y=e.y,t.vw=e.vw,t.vh=e.vh,t}function C(e){let t=T("dwell",e.actor??"human",e.path,e.ts);return t.dwellMs=e.dwellMs,t}function z(e,t){if(t<=0)return 100;let n=e/t*100;return n<=0?0:n>=100?100:Math.round(n)}function q(e,t){let n=e.source??"agent";if(n!=="agent"&&n!=="flow")return;let i=e.target?.elementId,o=e.target?.label,u=E(e.meta,"dwellMs");if(u!==void 0){let s=C({path:t,ts:e.timestamp,dwellMs:u,actor:"agent"});return i!==void 0&&(s.targetId=i),o!==void 0&&(s.label=o),s.meta={action:e.action,agentId:e.agentId,source:n},s}return U({path:t,ts:e.timestamp,x:E(e.meta,"x")??0,y:E(e.meta,"y")??0,vw:E(e.meta,"vw")??0,vh:E(e.meta,"vh")??0,actor:"agent",targetId:i,label:o,meta:{action:e.action,agentId:e.agentId,source:n}})}function E(e,t){let n=e?.[t];return typeof n=="number"&&Number.isFinite(n)?n:void 0}async function G(e){let t=e.loadModule??ce,n;try{n=await t();}catch{n=void 0;}if(!n||typeof n.onActivity!="function")return ()=>{};try{return n.onActivity(i=>{let o=q(i,e.path());o&&e.emit(o);})}catch{return ()=>{}}}async function ce(){let e="@particle-academy/fancy-auto-common";try{let t=await import(e);return t&&typeof t.onActivity=="function"?t:void 0}catch{return}}var W=class{constructor(e){this.events=[],this.siteKey=e.siteKey,this.url=J(e.endpoint),this.sessionId=e.sessionId,this.send=e.send??X;}add(e){this.events.push(e);}get size(){return this.events.length}get collectUrl(){return this.url}flush(){if(this.events.length===0)return;let e={siteKey:this.siteKey,sessionId:this.sessionId,events:this.events},t=JSON.stringify(e);this.send(this.url,t)&&(this.events=[]);}};function J(e){return e.replace(/\/+$/,"")+"/collect"}var X=(e,t)=>{let n=typeof navigator<"u"?navigator:void 0;if(n&&typeof n.sendBeacon=="function")try{let i=typeof Blob<"u"?new Blob([t],{type:"application/json"}):t;if(n.sendBeacon(e,i))return !0}catch{}if(typeof fetch=="function")try{return fetch(e,{method:"POST",body:t,headers:{"Content-Type":"application/json"},keepalive:!0,mode:"cors",credentials:"omit"}).catch(()=>{}),!0}catch{return false}return false},L="fancy-heuristics:sid",j=0;function A(){let e=typeof globalThis<"u"?globalThis.crypto:void 0;if(e&&typeof e.randomUUID=="function")return e.randomUUID();if(e&&typeof e.getRandomValues=="function"){let t=new Uint8Array(16);e.getRandomValues(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=[];for(let i=0;i<16;i++)n.push(t[i].toString(16).padStart(2,"0"));return n.slice(0,4).join("")+"-"+n.slice(4,6).join("")+"-"+n.slice(6,8).join("")+"-"+n.slice(8,10).join("")+"-"+n.slice(10,16).join("")}return j=j+1>>>0,`fh-${Date.now().toString(36)}-${j.toString(36)}`}function Q(e){let t=de();if(!t)return A();try{let n=t.getItem(L);if(n)return n;let i=A();return t.setItem(L,i),i}catch{return A()}}function de(){try{let e=globalThis.sessionStorage;return e?(e.getItem(L),e):void 0}catch{return}}var fe=1500,ue=120,pe=200;function ye(e){return {pageview:e?.pageview??true,dwell:e?.dwell??true,click:e?.click??true,scroll:e?.scroll??true,pointer:e?.pointer??true,agent:e?.agent??true}}function K(e){if(!e||!e.siteKey||!e.endpoint)throw new Error("createCollector: { siteKey, endpoint } are required");let t=ye(e.track),n=e.flushMs??fe,i=e.pointerThrottleMs??ue,o=e.scrollThrottleMs??pe,u=Q(),d=new W({siteKey:e.siteKey,endpoint:e.endpoint,sessionId:u}),s=false,p,h,P=[],g=0,c=0,S=0,x=0,M=0,a=()=>typeof location<"u"&&location?location.pathname:"/",m=e.now??(()=>Date.now()),v=()=>typeof window<"u"&&window.innerWidth||0,b=()=>typeof window<"u"&&window.innerHeight||0,y=r=>d.add(r),D=r=>{let l=r,f=r.target,{id:w,label:O}=Z(f);y(U({path:a(),ts:m(),x:Math.round(l.clientX??0),y:Math.round(l.clientY??0),vw:v(),vh:b(),targetId:w,label:O}));},oe=()=>{let r=m();if(r-x<o)return;x=r;let l=typeof document<"u"?document.documentElement:void 0;if(!l)return;let f=(typeof window<"u"?window.scrollY:0)||l.scrollTop||0,w=l.scrollHeight-l.clientHeight,O=z(f,w);O>S&&(S=O,y(Y({path:a(),ts:r,scrollPct:S})));},re=r=>{let l=m();if(l-M<i)return;M=l;let f=r;y(V({path:a(),ts:l,x:Math.round(f.clientX??0),y:Math.round(f.clientY??0),vw:v(),vh:b()}));},B=()=>typeof document>"u"||document.visibilityState!=="hidden",H=()=>{g===0&&(g=m());},k=()=>{g!==0&&(c+=m()-g,g=0);},le=()=>{B()?H():k();},se=()=>{k(),c>0&&(y(C({path:a(),ts:m(),dwellMs:c})),c=0),B()&&H();},ae=()=>{t.dwell&&(k(),c>0&&(y(C({path:a(),ts:m(),dwellMs:c})),c=0)),d.flush();},I=(r,l,f,w)=>{!r||typeof r.addEventListener!="function"||(r.addEventListener(l,f,w),P.push(()=>r.removeEventListener(l,f,w)));};return {start(){if(s)return;s=true;let r=typeof document<"u"?document:void 0,l=typeof window<"u"?window:void 0;t.pageview&&y(N(a(),m())),t.dwell&&(H(),I(r,"visibilitychange",le)),t.click&&I(r,"click",D,{capture:true,passive:true}),t.scroll&&I(l,"scroll",oe,{passive:true}),t.pointer&&I(l,"pointermove",re,{passive:true}),I(l,"pagehide",ae),t.agent&&G({path:a,emit:y}).then(f=>{s?h=f:f();}),typeof setInterval=="function"&&(p=setInterval(()=>{t.dwell&&se(),d.flush();},n));},stop(){if(s){s=false,p!==void 0&&(clearInterval(p),p=void 0);for(let r of P.splice(0))try{r();}catch{}if(h){try{h();}catch{}h=void 0;}t.dwell&&(k(),c>0&&(y(C({path:a(),ts:m(),dwellMs:c})),c=0)),d.flush();}},flush(){d.flush();}}}function Z(e){if(!e)return {};let t=e,n;for(let d=0;t&&d<5;d++){let s=t.getAttribute?.("data-fancy-id")??t.getAttribute?.("data-testid")??(t.id||void 0);if(s){n=s;break}t=t.parentElement;}let o=he(t??e),u={};return n!==void 0&&(u.id=n),o!==void 0&&(u.label=o),u}function he(e){let t=e.getAttribute?.("aria-label");if(t)return R(t);let n=e.textContent;if(n){let o=R(n);if(o)return o}return e.tagName?e.tagName.toLowerCase():void 0}function R(e){return e.replace(/\s+/g," ").trim().slice(0,120)}var me=e=>K({siteKey:e.siteKey,endpoint:e.endpoint});function ee(e){return me(e)}function te(e){return `<svg width="${e}" height="${e}" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false" style="display:block;flex:0 0 auto;">
|
|
2
2
|
<rect x="2" y="2" width="20" height="20" rx="6" fill="url(#fp-grad)"/>
|
|
3
3
|
<circle cx="9" cy="9" r="3" fill="#fff"/>
|
|
4
4
|
<circle cx="16.5" cy="15" r="1.6" fill="#fff" opacity="0.85"/>
|
|
@@ -8,7 +8,7 @@ var FancyPixel=(function(exports){'use strict';function h(e,t){if(!e)return fal
|
|
|
8
8
|
<stop offset="1" stop-color="#22d3ee"/>
|
|
9
9
|
</linearGradient>
|
|
10
10
|
</defs>
|
|
11
|
-
</svg>`}var
|
|
11
|
+
</svg>`}var F=`
|
|
12
12
|
:host { all: initial; }
|
|
13
13
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
14
14
|
.fp-root {
|
|
@@ -31,7 +31,7 @@ var FancyPixel=(function(exports){'use strict';function h(e,t){if(!e)return fal
|
|
|
31
31
|
@media (prefers-reduced-motion: reduce) {
|
|
32
32
|
.fp-pulse, .fp-pulse-ring { animation: none !important; }
|
|
33
33
|
}
|
|
34
|
-
`;function
|
|
34
|
+
`;function ve(e){return e==="floating"?["position:fixed","right:16px","bottom:16px","z-index:2147483647","pointer-events:none"].join(";"):["display:inline-block","vertical-align:middle"].join(";")}function ge(e,t){let n=`class="fp-link" href="${t}" target="_blank" rel="noopener noreferrer" part="link" data-fancy-pixel-link`;return e==="mark"?`<style>${F}
|
|
35
35
|
.fp-mark {
|
|
36
36
|
pointer-events:auto;
|
|
37
37
|
display:inline-flex; align-items:center; justify-content:center;
|
|
@@ -42,9 +42,9 @@ var FancyPixel=(function(exports){'use strict';function h(e,t){if(!e)return fal
|
|
|
42
42
|
</style>
|
|
43
43
|
<div class="fp-root" part="root">
|
|
44
44
|
<a ${n} aria-label="Powered by Fancy UI">
|
|
45
|
-
<span class="fp-mark">${
|
|
45
|
+
<span class="fp-mark">${te(22)}</span>
|
|
46
46
|
</a>
|
|
47
|
-
</div>`:e==="beacon"?`<style>${
|
|
47
|
+
</div>`:e==="beacon"?`<style>${F}
|
|
48
48
|
.fp-beacon {
|
|
49
49
|
pointer-events:auto;
|
|
50
50
|
position:relative; display:inline-flex;
|
|
@@ -69,7 +69,7 @@ var FancyPixel=(function(exports){'use strict';function h(e,t){if(!e)return fal
|
|
|
69
69
|
<span class="fp-dot"></span>
|
|
70
70
|
</span>
|
|
71
71
|
</a>
|
|
72
|
-
</div>`:`<style>${
|
|
72
|
+
</div>`:`<style>${F}
|
|
73
73
|
.fp-badge {
|
|
74
74
|
pointer-events:auto;
|
|
75
75
|
display:inline-flex; align-items:center; gap:8px;
|
|
@@ -93,10 +93,10 @@ var FancyPixel=(function(exports){'use strict';function h(e,t){if(!e)return fal
|
|
|
93
93
|
<div class="fp-root" part="root">
|
|
94
94
|
<a ${n} aria-label="Powered by Fancy UI">
|
|
95
95
|
<span class="fp-badge">
|
|
96
|
-
${
|
|
96
|
+
${te(16)}
|
|
97
97
|
<span><span class="fp-by">Powered by</span> <span class="fp-name">Fancy UI</span></span>
|
|
98
98
|
</span>
|
|
99
99
|
</a>
|
|
100
|
-
</div>`}function
|
|
101
|
-
exports.autoInit=
|
|
100
|
+
</div>`}function ne(e,t,n){let i=document.createElement("div");i.setAttribute("data-fancy-pixel",""),i.setAttribute("data-fancy-badge",""),i.setAttribute("data-fancy-pixel-style",e),i.setAttribute("data-fancy-pixel-mode",t),i.style.cssText=ve(t);let o=i.attachShadow({mode:"open"});return o.innerHTML=ge(e,n),i}var xe="https://particle.academy",be=["badge","mark","beacon"],we=["placed","floating"];function Pe(e){if(e instanceof Element)return e;if(typeof e=="string"){let t=document.querySelector(e);if(t)return t}return document.body}function Se(){return typeof location<"u"?location.pathname:"/"}function $(e={}){let t=be.includes(e.style)?e.style:"badge",n=we.includes(e.mode)?e.mode:"floating",i=e.siteKey??"",o=e.endpoint??"",u=e.collect!==false,d=e.href??xe,s=e.target??null,p=ne(t,n,d);n==="floating"?document.body.appendChild(p):Pe(s).appendChild(p);let h=false,P=false,g=false;function c(){o&&_(o,{siteKey:i,style:t,mode:n,visible:h,path:Se(),ts:Date.now()});}function S(){if(P)return;P=true;let v={style:t,mode:n,siteKey:i,host:p};document.dispatchEvent(new CustomEvent("fancy-pixel:shown",{detail:v}));}let x=null;function M(v){g||v===h||(h=v,h&&S(),c());}typeof IntersectionObserver=="function"?(x=new IntersectionObserver(v=>{let b=v[v.length-1];if(!b)return;let y=b.boundingClientRect,D=y.width>0&&y.height>0;M(b.isIntersecting&&b.intersectionRatio>0&&D);},{threshold:[0,.01,.5]}),x.observe(p)):M(true),c();let a=null;if(o&&u)try{a=ee({siteKey:i,endpoint:o}),a.start();}catch{a=null;}return {host:p,get visible(){return h},get collector(){return a},options:{style:t,mode:n,siteKey:i,endpoint:o,collect:u,href:d,target:s},destroy(){if(!g){if(g=true,x?.disconnect(),x=null,a){try{a.stop();}catch{}a=null;}p.remove();}}}}function Me(){if(typeof document>"u")return null;let e=document.currentScript;if(!e||!e.dataset||e.dataset.site===void 0){let i=document.querySelectorAll("script[data-site][src*='fancy-pixel'], script[data-fancy-pixel]");e=i[i.length-1]??null;}if(!e)return null;let t=e.dataset;if(t.style===void 0&&t.mode===void 0&&t.site===void 0&&t.endpoint===void 0&&t.collect===void 0&&t.fancyPixel===void 0)return null;let n=t.collect!==void 0?t.collect!=="false":void 0;return {style:t.style,mode:t.mode,siteKey:t.site,endpoint:t.endpoint,collect:n,target:t.target??null,href:t.href}}function ie(){let e=Me();if(!e)return null;let t=()=>$(e);if(typeof document<"u"&&document.readyState==="loading"){let n=null;return document.addEventListener("DOMContentLoaded",()=>{n=t();},{once:true}),n}return t()}ie();
|
|
101
|
+
exports.autoInit=ie;exports.mountPixel=$;return exports;})({});//# sourceMappingURL=fancy-pixel.global.min.js.map
|
|
102
102
|
//# sourceMappingURL=fancy-pixel.global.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/beacon.ts","../src/render.ts","../src/mount.ts","../src/autoinit.ts","../src/global.ts"],"names":["sendPixelBeacon","endpoint","payload","url","body","blob","logoGlyph","size","RESET","hostStyleFor","mode","innerFor","style","href","linkAttrs","createPixelHost","host","root","DEFAULT_HREF","STYLES","MODES","resolveTarget","target","found","currentPath","mountPixel","opts","siteKey","visible","shownDispatched","destroyed","beacon","dispatchShownOnce","detail","observer","setVisible","next","entries","entry","rect","hasSize","readScriptConfig","el","candidates","ds","autoInit","config","run","handle"],"mappings":"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","file":"fancy-pixel.global.min.js","sourcesContent":["import type { PixelBeaconPayload } from \"./types.js\";\n\n/**\n * Send a pixel liveness/visibility beacon to `${endpoint}/pixel`.\n *\n * Prefers `navigator.sendBeacon` (survives page unload, non-blocking),\n * falling back to `fetch(..., { keepalive: true })`. Both paths POST JSON.\n * Failures are swallowed — a missing beacon must never break the host page.\n *\n * @returns `true` if the beacon was handed off to the transport.\n */\nexport function sendPixelBeacon(\n endpoint: string,\n payload: PixelBeaconPayload,\n): boolean {\n if (!endpoint) return false;\n\n const url = endpoint.replace(/\\/+$/, \"\") + \"/pixel\";\n const body = JSON.stringify(payload);\n\n try {\n if (\n typeof navigator !== \"undefined\" &&\n typeof navigator.sendBeacon === \"function\"\n ) {\n // sendBeacon with a Blob preserves the JSON content type for the server.\n const blob = new Blob([body], { type: \"application/json\" });\n if (navigator.sendBeacon(url, blob)) return true;\n // sendBeacon returns false if the UA refused to queue it — fall through.\n }\n } catch {\n /* fall through to fetch */\n }\n\n try {\n if (typeof fetch === \"function\") {\n void fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n keepalive: true,\n // Cross-origin ingestion: we never read the response, so opaque is fine.\n mode: \"cors\",\n credentials: \"omit\",\n }).catch(() => {\n /* swallow */\n });\n return true;\n }\n } catch {\n /* swallow */\n }\n\n return false;\n}\n","import type { PixelMode, PixelStyle } from \"./types.js\";\n\n/**\n * The Fancy UI logo glyph as an inline SVG string. A rounded-square mark with\n * an orbiting particle — self-contained, no external asset, scales cleanly.\n */\nfunction logoGlyph(size: number): string {\n return `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" focusable=\"false\" style=\"display:block;flex:0 0 auto;\">\n <rect x=\"2\" y=\"2\" width=\"20\" height=\"20\" rx=\"6\" fill=\"url(#fp-grad)\"/>\n <circle cx=\"9\" cy=\"9\" r=\"3\" fill=\"#fff\"/>\n <circle cx=\"16.5\" cy=\"15\" r=\"1.6\" fill=\"#fff\" opacity=\"0.85\"/>\n <defs>\n <linearGradient id=\"fp-grad\" x1=\"2\" y1=\"2\" x2=\"22\" y2=\"22\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#7c5cff\"/>\n <stop offset=\"1\" stop-color=\"#22d3ee\"/>\n </linearGradient>\n </defs>\n</svg>`;\n}\n\n/**\n * Reset rules applied to the shadow root host's internal wrapper. Because we\n * render inside an open Shadow DOM, host page CSS cannot reach in to hide or\n * restyle these — visibility is part of the verification contract.\n */\nconst RESET = `\n :host { all: initial; }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n .fp-root {\n font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n }\n .fp-link {\n text-decoration: none;\n color: inherit;\n display: inline-flex;\n align-items: center;\n cursor: pointer;\n }\n @keyframes fp-pulse {\n 0% { transform: scale(0.85); opacity: 0.55; }\n 50% { transform: scale(1); opacity: 1; }\n 100% { transform: scale(0.85); opacity: 0.55; }\n }\n @media (prefers-reduced-motion: reduce) {\n .fp-pulse, .fp-pulse-ring { animation: none !important; }\n }\n`;\n\n/** Positioning styles for the host element, by mode. */\nexport function hostStyleFor(mode: PixelMode): string {\n if (mode === \"floating\") {\n return [\n \"position:fixed\",\n \"right:16px\",\n \"bottom:16px\",\n \"z-index:2147483647\",\n // A fixed corner element must not capture clicks across the page; only\n // the chip itself is interactive (pointer-events re-enabled inside).\n \"pointer-events:none\",\n ].join(\";\");\n }\n // placed: inline, flows with the target. Stays interactive.\n return [\"display:inline-block\", \"vertical-align:middle\"].join(\";\");\n}\n\n/** Build the inner HTML of the shadow root for a given style. */\nfunction innerFor(style: PixelStyle, href: string): string {\n const linkAttrs = `class=\"fp-link\" href=\"${href}\" target=\"_blank\" rel=\"noopener noreferrer\" part=\"link\" data-fancy-pixel-link`;\n\n if (style === \"mark\") {\n return `<style>${RESET}\n .fp-mark {\n pointer-events:auto;\n display:inline-flex; align-items:center; justify-content:center;\n padding:6px; border-radius:10px;\n background:rgba(17,17,27,0.92);\n box-shadow:0 2px 10px rgba(0,0,0,0.25);\n }\n </style>\n <div class=\"fp-root\" part=\"root\">\n <a ${linkAttrs} aria-label=\"Powered by Fancy UI\">\n <span class=\"fp-mark\">${logoGlyph(22)}</span>\n </a>\n </div>`;\n }\n\n if (style === \"beacon\") {\n return `<style>${RESET}\n .fp-beacon {\n pointer-events:auto;\n position:relative; display:inline-flex;\n width:14px; height:14px;\n }\n .fp-dot {\n position:absolute; inset:3px;\n border-radius:50%;\n background:linear-gradient(135deg,#7c5cff,#22d3ee);\n }\n .fp-pulse-ring {\n position:absolute; inset:0;\n border-radius:50%;\n background:radial-gradient(circle,#22d3ee 0%,rgba(34,211,238,0) 70%);\n animation:fp-pulse 1.8s ease-in-out infinite;\n }\n </style>\n <div class=\"fp-root\" part=\"root\">\n <a ${linkAttrs} aria-label=\"Powered by Fancy UI\">\n <span class=\"fp-beacon\">\n <span class=\"fp-pulse-ring fp-pulse\"></span>\n <span class=\"fp-dot\"></span>\n </span>\n </a>\n </div>`;\n }\n\n // badge (default): wordmark + glyph chip.\n return `<style>${RESET}\n .fp-badge {\n pointer-events:auto;\n display:inline-flex; align-items:center; gap:8px;\n padding:7px 11px; border-radius:999px;\n background:rgba(17,17,27,0.92);\n color:#f5f5fa;\n font-size:12px; font-weight:600; letter-spacing:0.01em;\n box-shadow:0 2px 12px rgba(0,0,0,0.28);\n border:1px solid rgba(124,92,255,0.35);\n transition:transform 120ms ease;\n }\n .fp-badge:hover { transform:translateY(-1px); }\n .fp-by { opacity:0.7; font-weight:500; }\n .fp-name {\n background:linear-gradient(135deg,#a78bfa,#22d3ee);\n -webkit-background-clip:text; background-clip:text;\n -webkit-text-fill-color:transparent; color:transparent;\n font-weight:700;\n }\n </style>\n <div class=\"fp-root\" part=\"root\">\n <a ${linkAttrs} aria-label=\"Powered by Fancy UI\">\n <span class=\"fp-badge\">\n ${logoGlyph(16)}\n <span><span class=\"fp-by\">Powered by</span> <span class=\"fp-name\">Fancy UI</span></span>\n </span>\n </a>\n </div>`;\n}\n\n/**\n * Create the host element + open shadow root for a pixel.\n *\n * The host carries the stable, host-CSS-proof markers:\n * - `data-fancy-pixel` — the stable agent/scanner handle\n * - `data-fancy-badge` — the marker ScanShowcaseSubmission detects\n * - `data-fancy-pixel-style` — the active style\n *\n * @returns the host element (shadow root already populated).\n */\nexport function createPixelHost(\n style: PixelStyle,\n mode: PixelMode,\n href: string,\n): HTMLElement {\n const host = document.createElement(\"div\");\n host.setAttribute(\"data-fancy-pixel\", \"\");\n host.setAttribute(\"data-fancy-badge\", \"\");\n host.setAttribute(\"data-fancy-pixel-style\", style);\n host.setAttribute(\"data-fancy-pixel-mode\", mode);\n host.style.cssText = hostStyleFor(mode);\n\n const root = host.attachShadow({ mode: \"open\" });\n root.innerHTML = innerFor(style, href);\n\n return host;\n}\n","import { sendPixelBeacon } from \"./beacon.js\";\nimport { createPixelHost } from \"./render.js\";\nimport type {\n MountPixelOptions,\n PixelHandle,\n PixelMode,\n PixelShownEventDetail,\n PixelStyle,\n} from \"./types.js\";\n\nconst DEFAULT_HREF = \"https://particle.academy\";\nconst STYLES: PixelStyle[] = [\"badge\", \"mark\", \"beacon\"];\nconst MODES: PixelMode[] = [\"placed\", \"floating\"];\n\nfunction resolveTarget(target: string | Element | null | undefined): Element {\n if (target instanceof Element) return target;\n if (typeof target === \"string\") {\n const found = document.querySelector(target);\n if (found) return found;\n }\n return document.body;\n}\n\nfunction currentPath(): string {\n return typeof location !== \"undefined\" ? location.pathname : \"/\";\n}\n\n/**\n * Mount a Fancy UI pixel into the page.\n *\n * Renders the chosen style inside an open Shadow DOM (host CSS cannot hide it),\n * wires an IntersectionObserver to confirm true on-screen visibility, dispatches\n * a `fancy-pixel:shown` CustomEvent on first visibility, and POSTs a liveness\n * beacon to `${endpoint}/pixel` on mount and on every visibility change.\n */\nexport function mountPixel(opts: MountPixelOptions = {}): PixelHandle {\n const style: PixelStyle = STYLES.includes(opts.style as PixelStyle)\n ? (opts.style as PixelStyle)\n : \"badge\";\n const mode: PixelMode = MODES.includes(opts.mode as PixelMode)\n ? (opts.mode as PixelMode)\n : \"floating\";\n const siteKey = opts.siteKey ?? \"\";\n const endpoint = opts.endpoint ?? \"\";\n const href = opts.href ?? DEFAULT_HREF;\n const target = opts.target ?? null;\n\n const host = createPixelHost(style, mode, href);\n\n // Mount: floating always pins to body; placed flows at the target.\n if (mode === \"floating\") {\n document.body.appendChild(host);\n } else {\n resolveTarget(target).appendChild(host);\n }\n\n let visible = false;\n let shownDispatched = false;\n let destroyed = false;\n\n function beacon(): void {\n if (!endpoint) return;\n sendPixelBeacon(endpoint, {\n siteKey,\n style,\n mode,\n visible,\n path: currentPath(),\n ts: Date.now(),\n });\n }\n\n function dispatchShownOnce(): void {\n if (shownDispatched) return;\n shownDispatched = true;\n const detail: PixelShownEventDetail = { style, mode, siteKey, host };\n document.dispatchEvent(\n new CustomEvent<PixelShownEventDetail>(\"fancy-pixel:shown\", { detail }),\n );\n }\n\n // Visibility verification. IntersectionObserver confirms the badge is truly\n // on-screen — not display:none, not 0-size, not scrolled out of view. When\n // unavailable (old/headless UAs) we optimistically assume visible so the\n // liveness beacon still fires.\n let observer: IntersectionObserver | null = null;\n\n function setVisible(next: boolean): void {\n if (destroyed || next === visible) return;\n visible = next;\n if (visible) dispatchShownOnce();\n beacon();\n }\n\n if (typeof IntersectionObserver === \"function\") {\n observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[entries.length - 1];\n if (!entry) return;\n // intersectionRatio > 0 with a non-zero box = genuinely rendered.\n const rect = entry.boundingClientRect;\n const hasSize = rect.width > 0 && rect.height > 0;\n setVisible(entry.isIntersecting && entry.intersectionRatio > 0 && hasSize);\n },\n { threshold: [0, 0.01, 0.5] },\n );\n observer.observe(host);\n } else {\n // No IO support — fire an optimistic visible beacon on mount.\n setVisible(true);\n }\n\n // Always emit a mount beacon so the server logs liveness even before the\n // observer reports (visible defaults false until proven).\n beacon();\n\n const resolved: PixelHandle[\"options\"] = {\n style,\n mode,\n siteKey,\n endpoint,\n href,\n target,\n };\n\n return {\n host,\n get visible() {\n return visible;\n },\n options: resolved,\n destroy() {\n if (destroyed) return;\n destroyed = true;\n observer?.disconnect();\n observer = null;\n host.remove();\n },\n };\n}\n","import { mountPixel } from \"./mount.js\";\nimport type { MountPixelOptions, PixelHandle } from \"./types.js\";\n\n/**\n * Read pixel config off the currently-executing <script> element's data-*\n * attributes. Used by the IIFE global bundle so a single <script> tag both\n * loads and auto-mounts the pixel:\n *\n * <script src=\".../fancy-pixel.global.min.js\"\n * data-style=\"badge\" data-mode=\"floating\"\n * data-site=\"KEY\" data-endpoint=\"https://host/heuristics\"></script>\n */\nfunction readScriptConfig(): MountPixelOptions | null {\n if (typeof document === \"undefined\") return null;\n\n // `document.currentScript` is the running script during initial parse.\n // Fall back to the last <script> carrying our data-site marker.\n let el = document.currentScript as HTMLScriptElement | null;\n if (!el || !el.dataset || el.dataset.site === undefined) {\n const candidates = document.querySelectorAll<HTMLScriptElement>(\n \"script[data-site][src*='fancy-pixel'], script[data-fancy-pixel]\",\n );\n el = candidates[candidates.length - 1] ?? null;\n }\n if (!el) return null;\n\n const ds = el.dataset;\n // Only auto-init if at least one fancy-pixel attribute is present.\n if (\n ds.style === undefined &&\n ds.mode === undefined &&\n ds.site === undefined &&\n ds.endpoint === undefined &&\n ds.fancyPixel === undefined\n ) {\n return null;\n }\n\n return {\n style: ds.style as MountPixelOptions[\"style\"],\n mode: ds.mode as MountPixelOptions[\"mode\"],\n siteKey: ds.site,\n endpoint: ds.endpoint,\n target: ds.target ?? null,\n href: ds.href,\n };\n}\n\n/**\n * Auto-mount from the script tag's data-* attributes if present.\n * Safe to call multiple times; mounts at most once per call when config exists.\n */\nexport function autoInit(): PixelHandle | null {\n const config = readScriptConfig();\n if (!config) return null;\n\n const run = (): PixelHandle => mountPixel(config);\n\n if (typeof document !== \"undefined\" && document.readyState === \"loading\") {\n // Defer to DOMContentLoaded so `placed` targets exist.\n let handle: PixelHandle | null = null;\n document.addEventListener(\n \"DOMContentLoaded\",\n () => {\n handle = run();\n },\n { once: true },\n );\n // Return null now; the handle resolves after DOMContentLoaded.\n return handle;\n }\n\n return run();\n}\n","/**\n * IIFE global entry. Built to `dist/fancy-pixel.global.min.js` with\n * `globalName: \"FancyPixel\"`. Exposes the public API on `window.FancyPixel`\n * AND auto-mounts from the script tag's data-* attributes on load.\n */\nimport { autoInit } from \"./autoinit.js\";\nimport { mountPixel } from \"./mount.js\";\n\n// Run auto-init immediately when the bundle executes (the <script> embed path).\nautoInit();\n\nexport { mountPixel, autoInit };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/beacon.ts","../node_modules/@particle-academy/fancy-heuristics-js/src/events.ts","../node_modules/@particle-academy/fancy-heuristics-js/src/agent.ts","../node_modules/@particle-academy/fancy-heuristics-js/src/buffer.ts","../node_modules/@particle-academy/fancy-heuristics-js/src/session.ts","../node_modules/@particle-academy/fancy-heuristics-js/src/collector.ts","../src/collector.ts","../src/render.ts","../src/mount.ts","../src/autoinit.ts","../src/global.ts"],"names":["sendPixelBeacon","endpoint","payload","url","body","blob","base","kind","actor","path","ts","buildPageview","buildClick","args","e","buildScroll","buildPointer","buildDwell","scrollPercent","scrollTop","scrollable","pct","mapActivityToEvent","source","targetId","label","dwellMs","numericMeta","ev","meta","key","v","tapAgentActivity","opts","load","defaultLoad","mod","spec","EventBuffer","joinCollect","defaultSend","event","batch","nav","SESSION_KEY","fallbackCounter","generateSessionId","c","bytes","hex","resolveSessionId","store","s","safeSessionStorage","existing","fresh","ss","DEFAULT_FLUSH_MS","DEFAULT_POINTER_THROTTLE_MS","DEFAULT_SCROLL_THROTTLE_MS","resolveTrack","track","createCollector","flushMs","pointerThrottleMs","scrollThrottleMs","sessionId","buffer","started","flushTimer","agentUnsub","teardown","visibleSince","accumulatedDwell","maxScrollPct","lastScrollAt","lastPointerAt","now","vw","vh","emit","onClick","me","targetEl","id","describeTarget","onScroll","t","doc","onPointerMove","pe","isVisible","openDwellWindow","closeDwellWindow","onVisibilityChange","emitDwell","onPageHide","addListener","target","type","handler","options","win","unsub","fn","el","node","depth","candidate","readLabel","out","aria","trim","text","factory","createPixelCollector","logoGlyph","size","RESET","hostStyleFor","mode","innerFor","style","href","linkAttrs","createPixelHost","host","root","DEFAULT_HREF","STYLES","MODES","resolveTarget","found","currentPath","mountPixel","siteKey","collect","visible","shownDispatched","destroyed","beacon","dispatchShownOnce","detail","observer","setVisible","next","entries","entry","rect","hasSize","collector","readScriptConfig","candidates","ds","autoInit","config","run","handle"],"mappings":"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","file":"fancy-pixel.global.min.js","sourcesContent":["import type { PixelBeaconPayload } from \"./types.js\";\n\n/**\n * Send a pixel liveness/visibility beacon to `${endpoint}/pixel`.\n *\n * Prefers `navigator.sendBeacon` (survives page unload, non-blocking),\n * falling back to `fetch(..., { keepalive: true })`. Both paths POST JSON.\n * Failures are swallowed — a missing beacon must never break the host page.\n *\n * @returns `true` if the beacon was handed off to the transport.\n */\nexport function sendPixelBeacon(\n endpoint: string,\n payload: PixelBeaconPayload,\n): boolean {\n if (!endpoint) return false;\n\n const url = endpoint.replace(/\\/+$/, \"\") + \"/pixel\";\n const body = JSON.stringify(payload);\n\n try {\n if (\n typeof navigator !== \"undefined\" &&\n typeof navigator.sendBeacon === \"function\"\n ) {\n // sendBeacon with a Blob preserves the JSON content type for the server.\n const blob = new Blob([body], { type: \"application/json\" });\n if (navigator.sendBeacon(url, blob)) return true;\n // sendBeacon returns false if the UA refused to queue it — fall through.\n }\n } catch {\n /* fall through to fetch */\n }\n\n try {\n if (typeof fetch === \"function\") {\n void fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n keepalive: true,\n // Cross-origin ingestion: we never read the response, so opaque is fine.\n mode: \"cors\",\n credentials: \"omit\",\n }).catch(() => {\n /* swallow */\n });\n return true;\n }\n } catch {\n /* swallow */\n }\n\n return false;\n}\n","/**\n * Pure event builders. Each returns a `HeuristicsEvent` matching the frozen wire\n * contract. Kept side-effect-free so they unit-test without a DOM. Optional\n * fields are omitted (never `null`) when not applicable.\n */\nimport type { Actor, EventKind, HeuristicsEvent } from \"./types.ts\";\n\n/** Common fields every event carries. */\nfunction base(kind: EventKind, actor: Actor, path: string, ts: number): HeuristicsEvent {\n return { kind, actor, path, ts };\n}\n\nexport function buildPageview(path: string, ts: number, actor: Actor = \"human\"): HeuristicsEvent {\n return base(\"pageview\", actor, path, ts);\n}\n\nexport function buildClick(args: {\n path: string;\n ts: number;\n x: number;\n y: number;\n vw: number;\n vh: number;\n targetId?: string;\n label?: string;\n actor?: Actor;\n meta?: Record<string, unknown>;\n}): HeuristicsEvent {\n const e = base(\"click\", args.actor ?? \"human\", args.path, args.ts);\n e.x = args.x;\n e.y = args.y;\n e.vw = args.vw;\n e.vh = args.vh;\n if (args.targetId !== undefined) e.targetId = args.targetId;\n if (args.label !== undefined) e.label = args.label;\n if (args.meta !== undefined) e.meta = args.meta;\n return e;\n}\n\nexport function buildScroll(args: {\n path: string;\n ts: number;\n scrollPct: number;\n actor?: Actor;\n}): HeuristicsEvent {\n const e = base(\"scroll\", args.actor ?? \"human\", args.path, args.ts);\n e.scrollPct = args.scrollPct;\n return e;\n}\n\nexport function buildPointer(args: {\n path: string;\n ts: number;\n x: number;\n y: number;\n vw: number;\n vh: number;\n actor?: Actor;\n}): HeuristicsEvent {\n const e = base(\"pointer\", args.actor ?? \"human\", args.path, args.ts);\n e.x = args.x;\n e.y = args.y;\n e.vw = args.vw;\n e.vh = args.vh;\n return e;\n}\n\nexport function buildDwell(args: {\n path: string;\n ts: number;\n dwellMs: number;\n actor?: Actor;\n}): HeuristicsEvent {\n const e = base(\"dwell\", args.actor ?? \"human\", args.path, args.ts);\n e.dwellMs = args.dwellMs;\n return e;\n}\n\n/**\n * Clamp a raw scroll position to an integer 0..100 percentage. `scrollable` is\n * `scrollHeight - clientHeight`; a non-scrollable page reports 100 (fully seen).\n */\nexport function scrollPercent(scrollTop: number, scrollable: number): number {\n if (scrollable <= 0) return 100;\n const pct = (scrollTop / scrollable) * 100;\n if (pct <= 0) return 0;\n if (pct >= 100) return 100;\n return Math.round(pct);\n}\n","/**\n * Optional agent-actor tap. If `@particle-academy/fancy-auto-common` is\n * resolvable at runtime we subscribe to its activity registry and translate\n * events whose `source` is \"agent\" or \"flow\" into `actor:\"agent\"` Events. When\n * the module is absent the subscribe call resolves to a no-op unsubscribe — the\n * collector keeps working for humans only. The import is dynamic + best-effort\n * so the peer never becomes a hard runtime dependency.\n */\nimport { buildClick, buildDwell } from \"./events.ts\";\nimport type { HeuristicsEvent } from \"./types.ts\";\n\n/** The slice of the AutoActivityEvent shape we consume. */\ninterface AutoActivityEventLike {\n agentId: string;\n agentName?: string;\n target: { kind?: string; elementId?: string; label?: string };\n action: string;\n timestamp: number;\n meta?: Record<string, unknown>;\n source?: string;\n}\n\ntype Unsubscribe = () => void;\n\n/** Module surface we rely on — narrowed so a partial mock satisfies it. */\ninterface AutoCommonModule {\n onActivity: (\n listener: (e: AutoActivityEventLike) => void,\n filter?: { source?: string },\n ) => Unsubscribe;\n}\n\nexport interface AgentTapOptions {\n path: () => string;\n emit: (event: HeuristicsEvent) => void;\n /** Override the dynamic import (tests inject a mock module here). */\n loadModule?: () => Promise<AutoCommonModule | undefined>;\n}\n\n/**\n * Map one activity event to an `actor:\"agent\"` Event. Activities carrying a\n * `dwellMs` meta become dwell events; everything else is treated as a discrete\n * \"click\"-style interaction on the named target. Returns undefined when the\n * activity isn't from an agent/flow source.\n */\nexport function mapActivityToEvent(\n e: AutoActivityEventLike,\n path: string,\n): HeuristicsEvent | undefined {\n const source = e.source ?? \"agent\";\n if (source !== \"agent\" && source !== \"flow\") return undefined;\n\n const targetId = e.target?.elementId;\n const label = e.target?.label;\n const dwellMs = numericMeta(e.meta, \"dwellMs\");\n\n if (dwellMs !== undefined) {\n const ev = buildDwell({ path, ts: e.timestamp, dwellMs, actor: \"agent\" });\n if (targetId !== undefined) ev.targetId = targetId;\n if (label !== undefined) ev.label = label;\n ev.meta = { action: e.action, agentId: e.agentId, source };\n return ev;\n }\n\n const ev = buildClick({\n path,\n ts: e.timestamp,\n x: numericMeta(e.meta, \"x\") ?? 0,\n y: numericMeta(e.meta, \"y\") ?? 0,\n vw: numericMeta(e.meta, \"vw\") ?? 0,\n vh: numericMeta(e.meta, \"vh\") ?? 0,\n actor: \"agent\",\n targetId,\n label,\n meta: { action: e.action, agentId: e.agentId, source },\n });\n return ev;\n}\n\nfunction numericMeta(meta: Record<string, unknown> | undefined, key: string): number | undefined {\n const v = meta?.[key];\n return typeof v === \"number\" && Number.isFinite(v) ? v : undefined;\n}\n\n/**\n * Subscribe to the agent bus if present. Resolves to an unsubscribe function;\n * if the peer is absent the unsubscribe is a no-op. Never throws.\n */\nexport async function tapAgentActivity(opts: AgentTapOptions): Promise<Unsubscribe> {\n const load = opts.loadModule ?? defaultLoad;\n let mod: AutoCommonModule | undefined;\n try {\n mod = await load();\n } catch {\n mod = undefined;\n }\n if (!mod || typeof mod.onActivity !== \"function\") return () => {};\n\n try {\n return mod.onActivity((e) => {\n const ev = mapActivityToEvent(e, opts.path());\n if (ev) opts.emit(ev);\n });\n } catch {\n return () => {};\n }\n}\n\n/**\n * Best-effort dynamic import of the optional peer. The specifier is hidden\n * behind a variable so bundlers don't try to resolve it at build time — it's a\n * runtime-only, optional dependency.\n */\nasync function defaultLoad(): Promise<AutoCommonModule | undefined> {\n const spec = \"@particle-academy/fancy-auto-common\";\n try {\n const mod = (await import(/* @vite-ignore */ spec)) as Partial<AutoCommonModule>;\n if (mod && typeof mod.onActivity === \"function\") {\n return mod as AutoCommonModule;\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Event buffer + transport. Accumulates events and ships them as a single\n * `CollectBatch` to `${endpoint}/collect`. Prefers `navigator.sendBeacon`\n * (survives page unload), falling back to a keepalive `fetch`. The buffer is a\n * plain array so it unit-tests with an injected `send` spy and no network.\n */\nimport type { CollectBatch, HeuristicsEvent } from \"./types.ts\";\n\n/** Pluggable transport — returns true if the batch was handed off. */\nexport type SendFn = (url: string, body: string) => boolean;\n\nexport interface BufferOptions {\n siteKey: string;\n endpoint: string;\n sessionId: string;\n /** Transport. Defaults to sendBeacon → fetch. */\n send?: SendFn;\n}\n\nexport class EventBuffer {\n private events: HeuristicsEvent[] = [];\n private readonly siteKey: string;\n private readonly url: string;\n private readonly sessionId: string;\n private readonly send: SendFn;\n\n constructor(opts: BufferOptions) {\n this.siteKey = opts.siteKey;\n this.url = joinCollect(opts.endpoint);\n this.sessionId = opts.sessionId;\n this.send = opts.send ?? defaultSend;\n }\n\n /** Queue one event for the next flush. */\n add(event: HeuristicsEvent): void {\n this.events.push(event);\n }\n\n /** Number of buffered (un-flushed) events. */\n get size(): number {\n return this.events.length;\n }\n\n /** The collect URL this buffer ships to. */\n get collectUrl(): string {\n return this.url;\n }\n\n /**\n * Ship the buffered events as one batch. No-op when empty. On a failed\n * handoff the events are kept so the next flush retries them.\n */\n flush(): void {\n if (this.events.length === 0) return;\n const batch: CollectBatch = {\n siteKey: this.siteKey,\n sessionId: this.sessionId,\n events: this.events,\n };\n const body = JSON.stringify(batch);\n const ok = this.send(this.url, body);\n if (ok) this.events = [];\n }\n}\n\n/** Append `/collect`, tolerating a trailing slash on the endpoint. */\nexport function joinCollect(endpoint: string): string {\n return endpoint.replace(/\\/+$/, \"\") + \"/collect\";\n}\n\n/** Default transport: sendBeacon, then keepalive fetch, then drop. */\nexport const defaultSend: SendFn = (url, body) => {\n const nav: Navigator | undefined =\n typeof navigator !== \"undefined\" ? navigator : undefined;\n\n if (nav && typeof nav.sendBeacon === \"function\") {\n try {\n // sendBeacon prefers a Blob so the Content-Type is explicit.\n const blob =\n typeof Blob !== \"undefined\"\n ? new Blob([body], { type: \"application/json\" })\n : body;\n if (nav.sendBeacon(url, blob as BodyInit)) return true;\n } catch {\n /* fall through to fetch */\n }\n }\n\n if (typeof fetch === \"function\") {\n try {\n void fetch(url, {\n method: \"POST\",\n body,\n headers: { \"Content-Type\": \"application/json\" },\n keepalive: true,\n // Cross-origin ingestion: the PHP routes are CSRF-exempt; we don't read\n // the response, so an opaque mode is fine.\n mode: \"cors\",\n credentials: \"omit\",\n }).catch(() => {});\n return true;\n } catch {\n return false;\n }\n }\n\n return false;\n};\n","/**\n * Session identity. One id per tab/session, persisted in `sessionStorage` so a\n * reload keeps the same id but a fresh tab starts a new one. Generated without\n * `Math.random` where the platform allows (`crypto.randomUUID`), falling back to\n * `crypto.getRandomValues`, then a last-resort time+counter id.\n */\n\nconst SESSION_KEY = \"fancy-heuristics:sid\";\n\n/** Minimal storage surface — lets tests inject a shim without a full DOM. */\nexport interface SessionStore {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\nlet fallbackCounter = 0;\n\n/** Generate a fresh session id, preferring crypto over Math.random. */\nexport function generateSessionId(): string {\n const c: Crypto | undefined =\n typeof globalThis !== \"undefined\" ? (globalThis as { crypto?: Crypto }).crypto : undefined;\n\n if (c && typeof c.randomUUID === \"function\") {\n return c.randomUUID();\n }\n\n if (c && typeof c.getRandomValues === \"function\") {\n const bytes = new Uint8Array(16);\n c.getRandomValues(bytes);\n // RFC-4122-ish v4 layout (best effort without randomUUID).\n bytes[6] = (bytes[6]! & 0x0f) | 0x40;\n bytes[8] = (bytes[8]! & 0x3f) | 0x80;\n const hex: string[] = [];\n for (let i = 0; i < 16; i++) hex.push(bytes[i]!.toString(16).padStart(2, \"0\"));\n return (\n hex.slice(0, 4).join(\"\") +\n \"-\" +\n hex.slice(4, 6).join(\"\") +\n \"-\" +\n hex.slice(6, 8).join(\"\") +\n \"-\" +\n hex.slice(8, 10).join(\"\") +\n \"-\" +\n hex.slice(10, 16).join(\"\")\n );\n }\n\n // Last resort — deterministic-ish, no Math.random. Good enough to disambiguate.\n fallbackCounter = (fallbackCounter + 1) >>> 0;\n return `fh-${Date.now().toString(36)}-${fallbackCounter.toString(36)}`;\n}\n\n/**\n * Read the persisted session id, generating + persisting one if absent. A\n * `SessionStore` may be injected (tests); otherwise `sessionStorage` is used,\n * and if even that is unavailable the id is generated per call (no persistence).\n */\nexport function resolveSessionId(store?: SessionStore): string {\n const s = store ?? safeSessionStorage();\n if (!s) return generateSessionId();\n\n try {\n const existing = s.getItem(SESSION_KEY);\n if (existing) return existing;\n const fresh = generateSessionId();\n s.setItem(SESSION_KEY, fresh);\n return fresh;\n } catch {\n return generateSessionId();\n }\n}\n\nfunction safeSessionStorage(): SessionStore | undefined {\n try {\n const ss = (globalThis as { sessionStorage?: SessionStore }).sessionStorage;\n if (!ss) return undefined;\n // Touch it — some environments throw on access (privacy mode).\n ss.getItem(SESSION_KEY);\n return ss;\n } catch {\n return undefined;\n }\n}\n\nexport { SESSION_KEY };\n","/**\n * The browser collector. Wires DOM listeners (click delegation, scroll depth,\n * pointer heatmap, dwell via visibility), batches the resulting Events, and\n * ships them to `${endpoint}/collect` on an interval + on `pagehide`. Also taps\n * the optional agent activity bus. All DOM access is feature-detected so calling\n * `createCollector(...).start()` in a non-browser context is a safe no-op.\n */\nimport { tapAgentActivity } from \"./agent.ts\";\nimport { EventBuffer } from \"./buffer.ts\";\nimport {\n buildClick,\n buildDwell,\n buildPageview,\n buildPointer,\n buildScroll,\n scrollPercent,\n} from \"./events.ts\";\nimport { resolveSessionId } from \"./session.ts\";\nimport type { Collector, CollectorOptions, HeuristicsEvent, TrackConfig } from \"./types.ts\";\n\nconst DEFAULT_FLUSH_MS = 1500;\nconst DEFAULT_POINTER_THROTTLE_MS = 120;\nconst DEFAULT_SCROLL_THROTTLE_MS = 200;\n\nfunction resolveTrack(track: TrackConfig | undefined): Required<TrackConfig> {\n return {\n pageview: track?.pageview ?? true,\n dwell: track?.dwell ?? true,\n click: track?.click ?? true,\n scroll: track?.scroll ?? true,\n pointer: track?.pointer ?? true,\n agent: track?.agent ?? true,\n };\n}\n\nexport function createCollector(opts: CollectorOptions): Collector {\n if (!opts || !opts.siteKey || !opts.endpoint) {\n throw new Error(\"createCollector: { siteKey, endpoint } are required\");\n }\n const track = resolveTrack(opts.track);\n const flushMs = opts.flushMs ?? DEFAULT_FLUSH_MS;\n const pointerThrottleMs = opts.pointerThrottleMs ?? DEFAULT_POINTER_THROTTLE_MS;\n const scrollThrottleMs = opts.scrollThrottleMs ?? DEFAULT_SCROLL_THROTTLE_MS;\n\n const sessionId = resolveSessionId();\n const buffer = new EventBuffer({\n siteKey: opts.siteKey,\n endpoint: opts.endpoint,\n sessionId,\n });\n\n // ── runtime state ──────────────────────────────────────────────────────────\n let started = false;\n let flushTimer: ReturnType<typeof setInterval> | undefined;\n let agentUnsub: (() => void) | undefined;\n const teardown: Array<() => void> = [];\n\n // dwell accounting\n let visibleSince = 0;\n let accumulatedDwell = 0;\n\n // scroll depth (monotonic max)\n let maxScrollPct = 0;\n let lastScrollAt = 0;\n\n // pointer throttle\n let lastPointerAt = 0;\n\n const path = (): string =>\n typeof location !== \"undefined\" && location ? location.pathname : \"/\";\n\n const now: () => number = opts.now ?? (() => Date.now());\n\n const vw = (): number =>\n typeof window !== \"undefined\" ? window.innerWidth || 0 : 0;\n const vh = (): number =>\n typeof window !== \"undefined\" ? window.innerHeight || 0 : 0;\n\n const emit = (e: HeuristicsEvent): void => buffer.add(e);\n\n // ── click delegation (single capture-phase listener) ────────────────────────\n const onClick = (ev: Event): void => {\n const me = ev as MouseEvent;\n const targetEl = ev.target as Element | null;\n const { id, label } = describeTarget(targetEl);\n emit(\n buildClick({\n path: path(),\n ts: now(),\n x: Math.round(me.clientX ?? 0),\n y: Math.round(me.clientY ?? 0),\n vw: vw(),\n vh: vh(),\n targetId: id,\n label,\n }),\n );\n };\n\n // ── scroll depth (throttled, passive) ───────────────────────────────────────\n const onScroll = (): void => {\n const t = now();\n if (t - lastScrollAt < scrollThrottleMs) return;\n lastScrollAt = t;\n const doc =\n typeof document !== \"undefined\" ? document.documentElement : undefined;\n if (!doc) return;\n const scrollTop =\n (typeof window !== \"undefined\" ? window.scrollY : 0) || doc.scrollTop || 0;\n const scrollable = doc.scrollHeight - doc.clientHeight;\n const pct = scrollPercent(scrollTop, scrollable);\n if (pct > maxScrollPct) {\n maxScrollPct = pct;\n emit(buildScroll({ path: path(), ts: t, scrollPct: maxScrollPct }));\n }\n };\n\n // ── pointer heatmap (throttled, passive) ────────────────────────────────────\n const onPointerMove = (ev: Event): void => {\n const t = now();\n if (t - lastPointerAt < pointerThrottleMs) return;\n lastPointerAt = t;\n const pe = ev as PointerEvent;\n emit(\n buildPointer({\n path: path(),\n ts: t,\n x: Math.round(pe.clientX ?? 0),\n y: Math.round(pe.clientY ?? 0),\n vw: vw(),\n vh: vh(),\n }),\n );\n };\n\n // ── dwell (visible-time accounting) ─────────────────────────────────────────\n const isVisible = (): boolean =>\n typeof document === \"undefined\" || document.visibilityState !== \"hidden\";\n\n const openDwellWindow = (): void => {\n if (visibleSince === 0) visibleSince = now();\n };\n\n const closeDwellWindow = (): void => {\n if (visibleSince !== 0) {\n accumulatedDwell += now() - visibleSince;\n visibleSince = 0;\n }\n };\n\n const onVisibilityChange = (): void => {\n if (isVisible()) openDwellWindow();\n else closeDwellWindow();\n };\n\n const emitDwell = (): void => {\n closeDwellWindow();\n if (accumulatedDwell > 0) {\n emit(buildDwell({ path: path(), ts: now(), dwellMs: accumulatedDwell }));\n accumulatedDwell = 0;\n }\n // reopen for any time after this flush (until real teardown)\n if (isVisible()) openDwellWindow();\n };\n\n // ── pagehide → final dwell + flush ──────────────────────────────────────────\n const onPageHide = (): void => {\n if (track.dwell) {\n closeDwellWindow();\n if (accumulatedDwell > 0) {\n emit(buildDwell({ path: path(), ts: now(), dwellMs: accumulatedDwell }));\n accumulatedDwell = 0;\n }\n }\n buffer.flush();\n };\n\n const addListener = (\n target: EventTarget | undefined,\n type: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n ): void => {\n if (!target || typeof target.addEventListener !== \"function\") return;\n target.addEventListener(type, handler, options);\n teardown.push(() => target.removeEventListener(type, handler, options));\n };\n\n return {\n start(): void {\n if (started) return;\n started = true;\n\n const doc = typeof document !== \"undefined\" ? document : undefined;\n const win = typeof window !== \"undefined\" ? window : undefined;\n\n if (track.pageview) {\n emit(buildPageview(path(), now()));\n }\n\n if (track.dwell) {\n openDwellWindow();\n addListener(doc, \"visibilitychange\", onVisibilityChange as EventListener);\n }\n\n if (track.click) {\n // ONE delegated capture-phase listener on the document.\n addListener(doc, \"click\", onClick, { capture: true, passive: true });\n }\n\n if (track.scroll) {\n addListener(win, \"scroll\", onScroll, { passive: true });\n }\n\n if (track.pointer) {\n addListener(win, \"pointermove\", onPointerMove, { passive: true });\n }\n\n // pagehide is the reliable unload signal; flush + final dwell there.\n addListener(win, \"pagehide\", onPageHide);\n\n if (track.agent) {\n void tapAgentActivity({ path, emit }).then((unsub) => {\n if (started) agentUnsub = unsub;\n else unsub();\n });\n }\n\n // periodic flush (also rolls up an interim dwell chunk)\n if (typeof setInterval === \"function\") {\n flushTimer = setInterval(() => {\n if (track.dwell) emitDwell();\n buffer.flush();\n }, flushMs);\n }\n },\n\n stop(): void {\n if (!started) return;\n started = false;\n if (flushTimer !== undefined) {\n clearInterval(flushTimer);\n flushTimer = undefined;\n }\n for (const fn of teardown.splice(0)) {\n try {\n fn();\n } catch {\n /* ignore */\n }\n }\n if (agentUnsub) {\n try {\n agentUnsub();\n } catch {\n /* ignore */\n }\n agentUnsub = undefined;\n }\n if (track.dwell) {\n closeDwellWindow();\n if (accumulatedDwell > 0) {\n emit(buildDwell({ path: path(), ts: now(), dwellMs: accumulatedDwell }));\n accumulatedDwell = 0;\n }\n }\n buffer.flush();\n },\n\n flush(): void {\n buffer.flush();\n },\n };\n}\n\n/**\n * Derive a stable handle + human label for a clicked element. Walks up to the\n * nearest element carrying an id / data-fancy-id / data-testid so nested spans\n * inside a button still resolve to the button's identity.\n */\nexport function describeTarget(el: Element | null): { id?: string; label?: string } {\n if (!el) return {};\n let node: Element | null = el;\n let id: string | undefined;\n for (let depth = 0; node && depth < 5; depth++) {\n const candidate =\n node.getAttribute?.(\"data-fancy-id\") ??\n node.getAttribute?.(\"data-testid\") ??\n (node.id || undefined);\n if (candidate) {\n id = candidate;\n break;\n }\n node = node.parentElement;\n }\n const labelSource = (node ?? el) as Element;\n const label = readLabel(labelSource);\n const out: { id?: string; label?: string } = {};\n if (id !== undefined) out.id = id;\n if (label !== undefined) out.label = label;\n return out;\n}\n\nfunction readLabel(el: Element): string | undefined {\n const aria = el.getAttribute?.(\"aria-label\");\n if (aria) return trim(aria);\n const text = (el as HTMLElement).textContent;\n if (text) {\n const t = trim(text);\n if (t) return t;\n }\n const tag = el.tagName ? el.tagName.toLowerCase() : undefined;\n return tag;\n}\n\nfunction trim(s: string): string {\n return s.replace(/\\s+/g, \" \").trim().slice(0, 120);\n}\n","/**\n * Thin adapter over the bundled `@particle-academy/fancy-heuristics-js`\n * collector. fancy-pixel is an all-in-one embed: when mounted with an\n * `endpoint`, it ALSO starts this collector so a single <script> delivers the\n * badge + verification beacon AND full interaction analytics (clicks / scroll /\n * pointer heatmap / dwell + agent activity), keyed by `siteKey`.\n *\n * The factory is indirected through a module-level reference so the (very small)\n * surface can be swapped in tests without mocking the built bundle. The\n * dependency is bundled INTO every build — including the IIFE global — so there\n * is no separate install for consumers and no bare `import`/`require` of\n * fancy-heuristics-js survives in `dist/fancy-pixel.global.min.js`.\n */\nimport { createCollector as realCreateCollector } from \"@particle-academy/fancy-heuristics-js\";\nimport type { PixelCollector } from \"./types.js\";\n\n/** Options forwarded to the underlying collector. */\nexport interface PixelCollectorOptions {\n siteKey: string;\n endpoint: string;\n}\n\n/** The collector factory signature fancy-pixel relies on. */\nexport type CollectorFactory = (opts: PixelCollectorOptions) => PixelCollector;\n\nlet factory: CollectorFactory = (opts) =>\n realCreateCollector({ siteKey: opts.siteKey, endpoint: opts.endpoint });\n\n/**\n * Create (but do not start) an interaction collector for the given site/endpoint.\n * Wraps the dependency so callers never touch its types directly.\n */\nexport function createPixelCollector(opts: PixelCollectorOptions): PixelCollector {\n return factory(opts);\n}\n\n/**\n * Test seam: override the collector factory. Exported from the package root so\n * the built bundle exposes it; pass `null` to restore the real implementation.\n * Not part of the supported public API — for tests and embedding hosts only.\n */\nexport function __setCollectorFactory(next: CollectorFactory | null): void {\n factory = next ?? ((opts) =>\n realCreateCollector({ siteKey: opts.siteKey, endpoint: opts.endpoint }));\n}\n","import type { PixelMode, PixelStyle } from \"./types.js\";\n\n/**\n * The Fancy UI logo glyph as an inline SVG string. A rounded-square mark with\n * an orbiting particle — self-contained, no external asset, scales cleanly.\n */\nfunction logoGlyph(size: number): string {\n return `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" focusable=\"false\" style=\"display:block;flex:0 0 auto;\">\n <rect x=\"2\" y=\"2\" width=\"20\" height=\"20\" rx=\"6\" fill=\"url(#fp-grad)\"/>\n <circle cx=\"9\" cy=\"9\" r=\"3\" fill=\"#fff\"/>\n <circle cx=\"16.5\" cy=\"15\" r=\"1.6\" fill=\"#fff\" opacity=\"0.85\"/>\n <defs>\n <linearGradient id=\"fp-grad\" x1=\"2\" y1=\"2\" x2=\"22\" y2=\"22\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#7c5cff\"/>\n <stop offset=\"1\" stop-color=\"#22d3ee\"/>\n </linearGradient>\n </defs>\n</svg>`;\n}\n\n/**\n * Reset rules applied to the shadow root host's internal wrapper. Because we\n * render inside an open Shadow DOM, host page CSS cannot reach in to hide or\n * restyle these — visibility is part of the verification contract.\n */\nconst RESET = `\n :host { all: initial; }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n .fp-root {\n font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n }\n .fp-link {\n text-decoration: none;\n color: inherit;\n display: inline-flex;\n align-items: center;\n cursor: pointer;\n }\n @keyframes fp-pulse {\n 0% { transform: scale(0.85); opacity: 0.55; }\n 50% { transform: scale(1); opacity: 1; }\n 100% { transform: scale(0.85); opacity: 0.55; }\n }\n @media (prefers-reduced-motion: reduce) {\n .fp-pulse, .fp-pulse-ring { animation: none !important; }\n }\n`;\n\n/** Positioning styles for the host element, by mode. */\nexport function hostStyleFor(mode: PixelMode): string {\n if (mode === \"floating\") {\n return [\n \"position:fixed\",\n \"right:16px\",\n \"bottom:16px\",\n \"z-index:2147483647\",\n // A fixed corner element must not capture clicks across the page; only\n // the chip itself is interactive (pointer-events re-enabled inside).\n \"pointer-events:none\",\n ].join(\";\");\n }\n // placed: inline, flows with the target. Stays interactive.\n return [\"display:inline-block\", \"vertical-align:middle\"].join(\";\");\n}\n\n/** Build the inner HTML of the shadow root for a given style. */\nfunction innerFor(style: PixelStyle, href: string): string {\n const linkAttrs = `class=\"fp-link\" href=\"${href}\" target=\"_blank\" rel=\"noopener noreferrer\" part=\"link\" data-fancy-pixel-link`;\n\n if (style === \"mark\") {\n return `<style>${RESET}\n .fp-mark {\n pointer-events:auto;\n display:inline-flex; align-items:center; justify-content:center;\n padding:6px; border-radius:10px;\n background:rgba(17,17,27,0.92);\n box-shadow:0 2px 10px rgba(0,0,0,0.25);\n }\n </style>\n <div class=\"fp-root\" part=\"root\">\n <a ${linkAttrs} aria-label=\"Powered by Fancy UI\">\n <span class=\"fp-mark\">${logoGlyph(22)}</span>\n </a>\n </div>`;\n }\n\n if (style === \"beacon\") {\n return `<style>${RESET}\n .fp-beacon {\n pointer-events:auto;\n position:relative; display:inline-flex;\n width:14px; height:14px;\n }\n .fp-dot {\n position:absolute; inset:3px;\n border-radius:50%;\n background:linear-gradient(135deg,#7c5cff,#22d3ee);\n }\n .fp-pulse-ring {\n position:absolute; inset:0;\n border-radius:50%;\n background:radial-gradient(circle,#22d3ee 0%,rgba(34,211,238,0) 70%);\n animation:fp-pulse 1.8s ease-in-out infinite;\n }\n </style>\n <div class=\"fp-root\" part=\"root\">\n <a ${linkAttrs} aria-label=\"Powered by Fancy UI\">\n <span class=\"fp-beacon\">\n <span class=\"fp-pulse-ring fp-pulse\"></span>\n <span class=\"fp-dot\"></span>\n </span>\n </a>\n </div>`;\n }\n\n // badge (default): wordmark + glyph chip.\n return `<style>${RESET}\n .fp-badge {\n pointer-events:auto;\n display:inline-flex; align-items:center; gap:8px;\n padding:7px 11px; border-radius:999px;\n background:rgba(17,17,27,0.92);\n color:#f5f5fa;\n font-size:12px; font-weight:600; letter-spacing:0.01em;\n box-shadow:0 2px 12px rgba(0,0,0,0.28);\n border:1px solid rgba(124,92,255,0.35);\n transition:transform 120ms ease;\n }\n .fp-badge:hover { transform:translateY(-1px); }\n .fp-by { opacity:0.7; font-weight:500; }\n .fp-name {\n background:linear-gradient(135deg,#a78bfa,#22d3ee);\n -webkit-background-clip:text; background-clip:text;\n -webkit-text-fill-color:transparent; color:transparent;\n font-weight:700;\n }\n </style>\n <div class=\"fp-root\" part=\"root\">\n <a ${linkAttrs} aria-label=\"Powered by Fancy UI\">\n <span class=\"fp-badge\">\n ${logoGlyph(16)}\n <span><span class=\"fp-by\">Powered by</span> <span class=\"fp-name\">Fancy UI</span></span>\n </span>\n </a>\n </div>`;\n}\n\n/**\n * Create the host element + open shadow root for a pixel.\n *\n * The host carries the stable, host-CSS-proof markers:\n * - `data-fancy-pixel` — the stable agent/scanner handle\n * - `data-fancy-badge` — the marker ScanShowcaseSubmission detects\n * - `data-fancy-pixel-style` — the active style\n *\n * @returns the host element (shadow root already populated).\n */\nexport function createPixelHost(\n style: PixelStyle,\n mode: PixelMode,\n href: string,\n): HTMLElement {\n const host = document.createElement(\"div\");\n host.setAttribute(\"data-fancy-pixel\", \"\");\n host.setAttribute(\"data-fancy-badge\", \"\");\n host.setAttribute(\"data-fancy-pixel-style\", style);\n host.setAttribute(\"data-fancy-pixel-mode\", mode);\n host.style.cssText = hostStyleFor(mode);\n\n const root = host.attachShadow({ mode: \"open\" });\n root.innerHTML = innerFor(style, href);\n\n return host;\n}\n","import { sendPixelBeacon } from \"./beacon.js\";\nimport { createPixelCollector } from \"./collector.js\";\nimport { createPixelHost } from \"./render.js\";\nimport type {\n MountPixelOptions,\n PixelCollector,\n PixelHandle,\n PixelMode,\n PixelShownEventDetail,\n PixelStyle,\n} from \"./types.js\";\n\nconst DEFAULT_HREF = \"https://particle.academy\";\nconst STYLES: PixelStyle[] = [\"badge\", \"mark\", \"beacon\"];\nconst MODES: PixelMode[] = [\"placed\", \"floating\"];\n\nfunction resolveTarget(target: string | Element | null | undefined): Element {\n if (target instanceof Element) return target;\n if (typeof target === \"string\") {\n const found = document.querySelector(target);\n if (found) return found;\n }\n return document.body;\n}\n\nfunction currentPath(): string {\n return typeof location !== \"undefined\" ? location.pathname : \"/\";\n}\n\n/**\n * Mount a Fancy UI pixel into the page.\n *\n * Renders the chosen style inside an open Shadow DOM (host CSS cannot hide it),\n * wires an IntersectionObserver to confirm true on-screen visibility, dispatches\n * a `fancy-pixel:shown` CustomEvent on first visibility, and POSTs a liveness\n * beacon to `${endpoint}/pixel` on mount and on every visibility change.\n *\n * When `endpoint` is set and `collect` is left on (the default), it ALSO starts\n * the bundled `@particle-academy/fancy-heuristics-js` collector, streaming the\n * site's full interaction data (clicks / scroll depth / pointer heatmap / dwell\n * + agent activity) to `${endpoint}/collect` — one embed does badge +\n * verification + analytics. With no `endpoint`, nothing is sent (badge only).\n */\nexport function mountPixel(opts: MountPixelOptions = {}): PixelHandle {\n const style: PixelStyle = STYLES.includes(opts.style as PixelStyle)\n ? (opts.style as PixelStyle)\n : \"badge\";\n const mode: PixelMode = MODES.includes(opts.mode as PixelMode)\n ? (opts.mode as PixelMode)\n : \"floating\";\n const siteKey = opts.siteKey ?? \"\";\n const endpoint = opts.endpoint ?? \"\";\n const collect = opts.collect !== false; // default on; only an explicit false opts out\n const href = opts.href ?? DEFAULT_HREF;\n const target = opts.target ?? null;\n\n const host = createPixelHost(style, mode, href);\n\n // Mount: floating always pins to body; placed flows at the target.\n if (mode === \"floating\") {\n document.body.appendChild(host);\n } else {\n resolveTarget(target).appendChild(host);\n }\n\n let visible = false;\n let shownDispatched = false;\n let destroyed = false;\n\n function beacon(): void {\n if (!endpoint) return;\n sendPixelBeacon(endpoint, {\n siteKey,\n style,\n mode,\n visible,\n path: currentPath(),\n ts: Date.now(),\n });\n }\n\n function dispatchShownOnce(): void {\n if (shownDispatched) return;\n shownDispatched = true;\n const detail: PixelShownEventDetail = { style, mode, siteKey, host };\n document.dispatchEvent(\n new CustomEvent<PixelShownEventDetail>(\"fancy-pixel:shown\", { detail }),\n );\n }\n\n // Visibility verification. IntersectionObserver confirms the badge is truly\n // on-screen — not display:none, not 0-size, not scrolled out of view. When\n // unavailable (old/headless UAs) we optimistically assume visible so the\n // liveness beacon still fires.\n let observer: IntersectionObserver | null = null;\n\n function setVisible(next: boolean): void {\n if (destroyed || next === visible) return;\n visible = next;\n if (visible) dispatchShownOnce();\n beacon();\n }\n\n if (typeof IntersectionObserver === \"function\") {\n observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[entries.length - 1];\n if (!entry) return;\n // intersectionRatio > 0 with a non-zero box = genuinely rendered.\n const rect = entry.boundingClientRect;\n const hasSize = rect.width > 0 && rect.height > 0;\n setVisible(entry.isIntersecting && entry.intersectionRatio > 0 && hasSize);\n },\n { threshold: [0, 0.01, 0.5] },\n );\n observer.observe(host);\n } else {\n // No IO support — fire an optimistic visible beacon on mount.\n setVisible(true);\n }\n\n // Always emit a mount beacon so the server logs liveness even before the\n // observer reports (visible defaults false until proven).\n beacon();\n\n // All-in-one analytics: with an endpoint and collection left on, start the\n // bundled fancy-heuristics-js collector so this single embed also streams the\n // site's interaction data to `${endpoint}/collect`, keyed by siteKey. No\n // endpoint => no collection (badge-only), matching the beacon's behaviour.\n let collector: PixelCollector | null = null;\n if (endpoint && collect) {\n try {\n collector = createPixelCollector({ siteKey, endpoint });\n collector.start();\n } catch {\n // A collector failure must never break the host page or the badge.\n collector = null;\n }\n }\n\n const resolved: PixelHandle[\"options\"] = {\n style,\n mode,\n siteKey,\n endpoint,\n collect,\n href,\n target,\n };\n\n return {\n host,\n get visible() {\n return visible;\n },\n get collector() {\n return collector;\n },\n options: resolved,\n destroy() {\n if (destroyed) return;\n destroyed = true;\n observer?.disconnect();\n observer = null;\n if (collector) {\n try {\n collector.stop();\n } catch {\n /* swallow — teardown must never throw into the host */\n }\n collector = null;\n }\n host.remove();\n },\n };\n}\n","import { mountPixel } from \"./mount.js\";\nimport type { MountPixelOptions, PixelHandle } from \"./types.js\";\n\n/**\n * Read pixel config off the currently-executing <script> element's data-*\n * attributes. Used by the IIFE global bundle so a single <script> tag both\n * loads and auto-mounts the pixel:\n *\n * <script src=\".../fancy-pixel.global.min.js\"\n * data-style=\"badge\" data-mode=\"floating\"\n * data-site=\"KEY\" data-endpoint=\"https://host/heuristics\"></script>\n *\n * Add `data-collect=\"false\"` to render the badge + liveness beacon only and\n * suppress the interaction-analytics collector (on by default when an endpoint\n * is present).\n */\nfunction readScriptConfig(): MountPixelOptions | null {\n if (typeof document === \"undefined\") return null;\n\n // `document.currentScript` is the running script during initial parse.\n // Fall back to the last <script> carrying our data-site marker.\n let el = document.currentScript as HTMLScriptElement | null;\n if (!el || !el.dataset || el.dataset.site === undefined) {\n const candidates = document.querySelectorAll<HTMLScriptElement>(\n \"script[data-site][src*='fancy-pixel'], script[data-fancy-pixel]\",\n );\n el = candidates[candidates.length - 1] ?? null;\n }\n if (!el) return null;\n\n const ds = el.dataset;\n // Only auto-init if at least one fancy-pixel attribute is present.\n if (\n ds.style === undefined &&\n ds.mode === undefined &&\n ds.site === undefined &&\n ds.endpoint === undefined &&\n ds.collect === undefined &&\n ds.fancyPixel === undefined\n ) {\n return null;\n }\n\n // `data-collect=\"false\"` opts out of analytics; absent/anything-else = default on.\n const collect =\n ds.collect !== undefined ? ds.collect !== \"false\" : undefined;\n\n return {\n style: ds.style as MountPixelOptions[\"style\"],\n mode: ds.mode as MountPixelOptions[\"mode\"],\n siteKey: ds.site,\n endpoint: ds.endpoint,\n collect,\n target: ds.target ?? null,\n href: ds.href,\n };\n}\n\n/**\n * Auto-mount from the script tag's data-* attributes if present.\n * Safe to call multiple times; mounts at most once per call when config exists.\n */\nexport function autoInit(): PixelHandle | null {\n const config = readScriptConfig();\n if (!config) return null;\n\n const run = (): PixelHandle => mountPixel(config);\n\n if (typeof document !== \"undefined\" && document.readyState === \"loading\") {\n // Defer to DOMContentLoaded so `placed` targets exist.\n let handle: PixelHandle | null = null;\n document.addEventListener(\n \"DOMContentLoaded\",\n () => {\n handle = run();\n },\n { once: true },\n );\n // Return null now; the handle resolves after DOMContentLoaded.\n return handle;\n }\n\n return run();\n}\n","/**\n * IIFE global entry. Built to `dist/fancy-pixel.global.min.js` with\n * `globalName: \"FancyPixel\"`. Exposes the public API on `window.FancyPixel`\n * AND auto-mounts from the script tag's data-* attributes on load.\n */\nimport { autoInit } from \"./autoinit.js\";\nimport { mountPixel } from \"./mount.js\";\n\n// Run auto-init immediately when the bundle executes (the <script> embed path).\nautoInit();\n\nexport { mountPixel, autoInit };\n"]}
|