When data-domain attribute and pulseConfig are both unavailable (common
with GTM which strips data-* attributes), the script now falls back to
location.hostname. This is safe because the backend already validates
Origin/Referer against the registered domain. Strips www. prefix on
auto-detected hostname to match typical Pulse registration patterns.
Script detection now also searches by src URL and supports a global
config object (window.pulseConfig) for environments where data-*
attributes are not preserved on the injected script element.
Use real browser logos from alrra/browser-logos (SVG where available,
PNG fallback for archived browsers). Replaces the generic globe icon
with actual Chrome, Firefox, Safari, Edge, Opera, Brave, Vivaldi, Arc,
Samsung Internet, UC Browser, Yandex, Waterfox, Pale Moon, DuckDuckGo,
Maxthon, Silk, Puffin, Tor, and Opera Mini logos.
window.innerWidth is 0 in hidden/minimized tabs, causing the heuristic bot
scorer (added in #40) to drop legitimate custom events with a score of 5.
Use window.screen.width as fallback, matching the existing trackPageview logic.
IsSuspiciousEvent already scores 0x0 screens as +5 (bot threshold).
Keeping the check client-side hides bot traffic from analysis and
is trivially bypassable.
Server-side heuristic scoring already catches these patterns via
IsSuspiciousEvent. Client-side checks are trivially bypassable
(script is public) and add payload weight for all real users.
Move rage click and dead click detection (35% of script.js) into
script.frustration.js as an optional add-on. Core script drops from
8.1KB to 5.7KB gzipped. Add-on auto-discovers core via window.pulse
polling and supports opt-out via data-no-rage/data-no-dead attributes.
- Expose cleanPath on window.pulse for add-on consumption
- Add script.frustration.js to middleware PUBLIC_ROUTES
- Update integration guides, ScriptSetupBlock, and FrustrationTable
empty state to reference the add-on script
- Skip rage clicks when text is selected (triple-click to select)
- Exclude tabindex="-1" elements from dead click interactive selector
- Observe document.body for DOM changes (modals, drawers, overlays)
- Listen for popstate/hashchange to detect SPA navigations
Remove Performance tab, PerformanceStats component, settings toggle,
Web Vitals observers from tracking script, and all related API types
and SWR hooks. Duration tracking is preserved.
Performance metrics moved from dashboard into a new Performance tab.
Fixed null handling so "No data" shows instead of misleading zeros.
Script no longer sends INP=0 when no interaction occurred.
Switch from blocklist (strip known-bad params) to allowlist (only keep
UTM/attribution params). Eliminates cache-busters like _t and _ from
page paths without maintaining an ever-growing blocklist.
Dashboard filtering: FilterBar pills, AddFilterDropdown with dimension/
operator/value steps, URL-serialized filters, all SWR hooks filter-aware.
Custom event properties: pulse.track() accepts props object, EventProperties
panel with auto-discovered key tabs and value bar charts, clickable goal rows.
Updated changelog with both features under v0.13.0-alpha.
- 404 detection: checks document.title for "404" or "not found", fires custom event, SPA-aware
- Scroll depth: passive scroll listener fires events at 25/50/75/100% thresholds
- ScrollDepth dashboard card: progress bar visualization showing % of visitors reaching each threshold
- Scroll events filtered out of GoalStats to avoid duplication
- Both features on by default, opt-out via data-no-404 / data-no-scroll
Adds a single click listener in the tracking script that detects
external link clicks and file download clicks, firing outbound_link
and file_download custom events. On by default, opt-out via
data-no-outbound / data-no-downloads attributes.