You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Added
Widgets: a PlayStation "latest trophy" footer widget — surfaces the most recently earned trophy (tier sprite, game, platform) from Sony's first-party trophy API (psn-api, authenticated with an PSN_NPSSO token exchanged for cached short-lived auth tokens). Gated behind a psn_trophy_enabled Storyblok config flag, and the Discord and Letterboxd footer widgets are now flag-gated too.
Security: baseline security headers on every document response in the edge proxy — Strict-Transport-Security, Referrer-Policy, X-Content-Type-Options, and Permissions-Policy — alongside the existing CSP frame-ancestors directive. X-Frame-Options is intentionally omitted so CSP remains the single source of framing policy and keeps the Storyblok Visual Editor able to embed the site.
Now-playing: the widget now surfaces Spotify Premium-missing and error states — a typed SpotifyForbiddenError on 403, an errorCode exposed by useNowPlaying, and a red "danger" 403/500 artwork state — with matching Storybook stories.
UI: Video gains an objectFit prop and the Slideshow/Video loading path is hardened — the skeleton resolves on canplay/error (not just loadeddata) so a slow or failed source falls back to the poster, muted autoplay is set imperatively, and prefers-reduced-motion is respected (no video or carousel autoplay, paused shimmer). First Video tests added.
Changed
Storyblok-richtext: migrated rich-text rendering to @storyblok/react v7 / @storyblok/richtext v5. The richTextResolver<ReactNode> + tag-keyed tagRenderers map is replaced by createRichTextRenderer and a node/mark-keyed components map, the custom Tiptap image/inline-code extensions are dropped (v5 carries copyright natively and treats inline code as a distinct code mark), and the v5 emoji node is handled inline. The public API (StoryblokRichText, renderStoryblokRichText, StoryblokRichTextProps) is unchanged.
Letterboxd: films are ordered by watched date (newest first) instead of raw feed order, HTML entities in titles are decoded (e.g. Johnny & Me), and member likes surface a heart in the widget.
Fixed
Portfolio: added a root not-found.tsx boundary so the home route renders the branded 404 (instead of Next.js's default) when the home story is unpublished or fails to load, and as a global fallback for any unmatched URL routed through the root layout.
Widgets: each footer widget now triggers a single hover preview at one size (1:1 for Discord, 2:3 for Letterboxd) instead of two conflicting previews from the thumbnail and the name.
Observability: the Sentry release now resolves from the build-time NEXT_PUBLIC_APP_VERSION (git tag / GitHub release / commit SHA) instead of the unmaintained npm_package_version0.0.0 placeholder, so releases report meaningfully.
Spotify: a 403 (e.g. no Premium) is treated as "nothing playing" rather than throwing, and forbidden playback is no longer logged on every poll.
Performance
API: CDN cache headers (public, s-maxage + stale-while-revalidate) added to the Discord (30s window) and Letterboxd (5min window) routes, mirroring the Spotify now-playing pattern, so repeat hits are served from the CDN edge instead of always reaching the origin. Error and unconfigured responses stay uncached.
Tooling
Tests: Vitest coverage thresholds (statements/lines 80%, functions 75%, branches 70%) with html/lcov reporters, plus IntersectionObserver / ResizeObserver / matchMedia stubs in the global setup so UI components mount cleanly under jsdom. New tests fill the lowest-covered areas — every Sb* blok and helper in storyblok-ui, storyblok-utils (media/plain-text/presets/preview/responsive), now-playing, spotify color extraction, and ui card/filter/spotify-id.
CI: Codecov bundle analysis wired via Next 16's Turbopack-native experimental-analyze and @codecov/nextjs-webpack-plugin, with a non-gating CI job uploading the report per PR.
CI: Codecov config tuned for the monorepo — per-package component breakdown (ui, storyblok, integrations, foundations, portfolio), a softened informational patch target, and a components-based comment layout.