diff --git a/bun.lock b/bun.lock index ddd9478..080c5a8 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,7 @@ "": { "name": "screenly-rss-reader", "dependencies": { - "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.8.6", + "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.8.8", "hono": "^4.12.33", "qrcode-generator": "^2.0.4", }, @@ -191,7 +191,7 @@ "@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="], - "@screenly-labs/signage-kit": ["@screenly-labs/signage-kit@github:Screenly-Labs/signage-kit#7b0d8f1", { "dependencies": { "@fontsource-variable/bricolage-grotesque": "^5.3.0", "@fontsource-variable/fraunces": "^5.3.0", "@fontsource-variable/hanken-grotesk": "^5.3.0", "@fontsource-variable/jetbrains-mono": "^5.3.0", "@fontsource-variable/newsreader": "^5.3.0", "@fontsource/space-mono": "^5.3.0" }, "peerDependencies": { "@csstools/postcss-cascade-layers": "^6", "browserslist": "^4", "esbuild": "^0.28", "lightningcss": "^1.32", "postcss": "^8" }, "optionalPeers": ["@csstools/postcss-cascade-layers", "postcss"] }, "Screenly-Labs-signage-kit-7b0d8f1", "sha512-x61h6zlMCOW0MYcuIQgNeZF3TEI02rmafrASohDN5NT/PD0YSnANfWcKbZx32ItiC3fDihpeQH5c4Iu5GiKUiw=="], + "@screenly-labs/signage-kit": ["@screenly-labs/signage-kit@github:Screenly-Labs/signage-kit#85934fa", { "dependencies": { "@fontsource-variable/bricolage-grotesque": "^5.3.0", "@fontsource-variable/fraunces": "^5.3.0", "@fontsource-variable/hanken-grotesk": "^5.3.0", "@fontsource-variable/jetbrains-mono": "^5.3.0", "@fontsource-variable/newsreader": "^5.3.0", "@fontsource/space-mono": "^5.3.0" }, "peerDependencies": { "@csstools/postcss-cascade-layers": "^6", "browserslist": "^4", "esbuild": "^0.28", "lightningcss": "^1.32", "postcss": "^8" }, "optionalPeers": ["@csstools/postcss-cascade-layers", "postcss"] }, "Screenly-Labs-signage-kit-85934fa", "sha512-RcJ7vWlJWT/TFu5YdrAQAbqaiHnqobWRk2XwaC0kW1X+bCzJoDWT9kS/pMDxGEeNEixMZWtEG/vjBdg/x0fIOw=="], "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="], diff --git a/package.json b/package.json index c837dea..847c800 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "license": "AGPL-3.0-only", "dependencies": { - "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.8.6", + "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.8.8", "hono": "^4.12.33", "qrcode-generator": "^2.0.4" }, diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 7c1c9d5..f5a6601 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,4 +1,6 @@ import { html, raw } from 'hono/html' +import { analyticsBootstrap } from '@screenly-labs/signage-kit/analytics-bootstrap' +import { PLAYER_PROFILE_PATH } from '@screenly-labs/signage-kit/analytics-server' import { GATE } from '@screenly-labs/signage-kit/gate' import type { Child } from 'hono/jsx' @@ -20,32 +22,24 @@ const sentryScript = (id?: string) => ? html`` : '' -// Inline a value as a JS object literal inside a ` would close the tag early, -// and U+2028/U+2029 are valid in JSON but not in JS string literals. Escaping -// them as \u… keeps the JSON valid while neutralizing both hazards, so a feed -// title can never break out of the script. -const SCRIPT_UNSAFE = /[<>\u2028\u2029]/g -const jsonForScript = (value: unknown): string => - JSON.stringify(value).replace( - SCRIPT_UNSAFE, - (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}` - ) - // The `source`/`source_title` on the config call ride along on GA4's automatic // page_view, so a visit is attributed to its feed (main.ts tags the same pair on // every later event). +// The kit bootstrap owns the config call so it can pin client_id to the Screenly device id, +// making one screen one GA4 user: GA4's own client_id lives in the _ga cookie and these players +// largely boot with fresh storage, so it churns. The feed pair is passed as configParams and +// merged under client_id, so the feed attribution above is unchanged. const gaScript = (id?: string, feedId?: string, feedTitle?: string) => id ? html` - ` + ${raw( + analyticsBootstrap({ + gaId: id, + profilePath: PLAYER_PROFILE_PATH, + configParams: { source: feedId ?? '', source_title: feedTitle ?? '' } + }) + )}` : '' const Layout = (props: LayoutProps) => html`