Skip to content

chore(deps-dev): Bump the dev-dependencies group with 5 updates#12

Closed
dependabot[bot] wants to merge 1 commit intomainfrom
dependabot/npm_and_yarn/dev-dependencies-3a6b72160b
Closed

chore(deps-dev): Bump the dev-dependencies group with 5 updates#12
dependabot[bot] wants to merge 1 commit intomainfrom
dependabot/npm_and_yarn/dev-dependencies-3a6b72160b

Conversation

@dependabot
Copy link
Copy Markdown
Contributor

@dependabot dependabot Bot commented on behalf of github Feb 15, 2026

Bumps the dev-dependencies group with 5 updates:

Package From To
@commitlint/cli 19.8.1 20.4.1
@commitlint/config-conventional 19.8.1 20.4.1
turbo 2.8.7 2.8.9
eslint 9.39.2 10.0.0
eslint-config-next 15.5.12 16.1.6

Updates @commitlint/cli from 19.8.1 to 20.4.1

Release notes

Sourced from @​commitlint/cli's releases.

v20.4.1

20.4.1 (2026-02-02)

Reverts

Chore

Full Changelog: conventional-changelog/commitlint@v20.4.0...v20.4.1

v20.4.0

20.4.0 (2026-01-30)

Features

Refactor

New Contributors

Full Changelog: conventional-changelog/commitlint@v20.3.1...v20.4.0

v20.3.1

20.3.1 (2026-01-08)

Bug Fixes

... (truncated)

Changelog

Sourced from @​commitlint/cli's changelog.

20.4.1 (2026-02-02)

Note: Version bump only for package @​commitlint/cli

20.4.0 (2026-01-30)

Features

20.3.1 (2026-01-08)

Note: Version bump only for package @​commitlint/cli

20.3.0 (2026-01-01)

Note: Version bump only for package @​commitlint/cli

20.2.0 (2025-12-05)

Note: Version bump only for package @​commitlint/cli

20.1.0 (2025-09-30)

Note: Version bump only for package @​commitlint/cli

... (truncated)

Commits

Updates @commitlint/config-conventional from 19.8.1 to 20.4.1

Release notes

Sourced from @​commitlint/config-conventional's releases.

v20.4.1

20.4.1 (2026-02-02)

Reverts

Chore

Full Changelog: conventional-changelog/commitlint@v20.4.0...v20.4.1

v20.4.0

20.4.0 (2026-01-30)

Features

Refactor

New Contributors

Full Changelog: conventional-changelog/commitlint@v20.3.1...v20.4.0

v20.3.1

20.3.1 (2026-01-08)

Bug Fixes

... (truncated)

Changelog

Sourced from @​commitlint/config-conventional's changelog.

20.4.1 (2026-02-02)

Note: Version bump only for package @​commitlint/config-conventional

20.4.0 (2026-01-30)

Features

20.3.1 (2026-01-08)

Note: Version bump only for package @​commitlint/config-conventional

20.3.0 (2026-01-01)

Note: Version bump only for package @​commitlint/config-conventional

20.2.0 (2025-12-05)

Note: Version bump only for package @​commitlint/config-conventional

20.0.0 (2025-09-25)

Note: Version bump only for package @​commitlint/config-conventional

Commits

Updates turbo from 2.8.7 to 2.8.9

Release notes

Sourced from turbo's releases.

Turborepo v2.8.9-canary.3

Full Changelog: vercel/turborepo@v2.8.9...v2.8.9-canary.3

Turborepo v2.8.8

What's Changed

@​turbo/codemod

Examples

Changelog

New Contributors

Full Changelog: vercel/turborepo@v2.8.7...v2.8.8

Turborepo v2.8.8-canary.7

What's Changed

Changelog

Full Changelog: vercel/turborepo@v2.8.8-canary.6...v2.8.8-canary.7

... (truncated)

Commits

Updates eslint from 9.39.2 to 10.0.0

Release notes

Sourced from eslint's releases.

v10.0.0

Breaking Changes

  • f9e54f4 feat!: estimate rule-tester failure location (#20420) (ST-DDT)
  • a176319 feat!: replace chalk with styleText and add color to ResultsMeta (#20227) (루밀LuMir)
  • c7046e6 feat!: enable JSX reference tracking (#20152) (Pixel998)
  • fa31a60 feat!: add name to configs (#20015) (Kirk Waiblinger)
  • 3383e7e fix!: remove deprecated SourceCode methods (#20137) (Pixel998)
  • 501abd0 feat!: update dependency minimatch to v10 (#20246) (renovate[bot])
  • ca4d3b4 fix!: stricter rule tester assertions for valid test cases (#20125) (唯然)
  • 96512a6 fix!: Remove deprecated rule context methods (#20086) (Nicholas C. Zakas)
  • c69fdac feat!: remove eslintrc support (#20037) (Francesco Trotta)
  • 208b5cc feat!: Use ScopeManager#addGlobals() (#20132) (Milos Djermanovic)
  • a2ee188 fix!: add uniqueItems: true in no-invalid-regexp option (#20155) (Tanuj Kanti)
  • a89059d feat!: Program range span entire source text (#20133) (Pixel998)
  • 39a6424 fix!: assert 'text' is a string across all RuleFixer methods (#20082) (Pixel998)
  • f28fbf8 fix!: Deprecate "always" and "as-needed" options of the radix rule (#20223) (Milos Djermanovic)
  • aa3fb2b fix!: tighten func-names schema (#20119) (Pixel998)
  • f6c0ed0 feat!: report eslint-env comments as errors (#20128) (Francesco Trotta)
  • 4bf739f fix!: remove deprecated LintMessage#nodeType and TestCaseError#type (#20096) (Pixel998)
  • 523c076 feat!: drop support for jiti < 2.2.0 (#20016) (michael faith)
  • 454a292 feat!: update eslint:recommended configuration (#20210) (Pixel998)
  • 4f880ee feat!: remove v10_* and inactive unstable_* flags (#20225) (sethamus)
  • f18115c feat!: no-shadow-restricted-names report globalThis by default (#20027) (sethamus)
  • c6358c3 feat!: Require Node.js ^20.19.0 || ^22.13.0 || >=24 (#20160) (Milos Djermanovic)

Features

  • bff9091 feat: handle Array.fromAsync in array-callback-return (#20457) (Francesco Trotta)
  • 290c594 feat: add self to no-implied-eval rule (#20468) (sethamus)
  • 43677de feat: fix handling of function and class expression names in no-shadow (#20432) (Milos Djermanovic)
  • f0cafe5 feat: rule tester add assertion option requireData (#20409) (fnx)
  • f7ab693 feat: output RuleTester test case failure index (#19976) (ST-DDT)
  • 7cbcbf9 feat: add countThis option to max-params (#20236) (Gerkin)
  • f148a5e feat: add error assertion options (#20247) (ST-DDT)
  • 09e6654 feat: update error loc of require-yield and no-useless-constructor (#20267) (Tanuj Kanti)

Bug Fixes

  • 436b82f fix: update eslint (#20473) (renovate[bot])
  • 1d29d22 fix: detect default this binding in Array.fromAsync callbacks (#20456) (Francesco Trotta)
  • 727451e fix: fix regression of global mode report range in strict rule (#20462) (ntnyq)
  • e80485f fix: remove fake FlatESLint and LegacyESLint exports (#20460) (Francesco Trotta)
  • 9eeff3b fix: update esquery (#20423) (cryptnix)
  • b34b938 fix: use Error.prepareStackTrace to estimate failing test location (#20436) (Francesco Trotta)
  • 51aab53 fix: update eslint (#20443) (renovate[bot])
  • 23490b2 fix: handle space before colon in RuleTester location estimation (#20433) (Francesco Trotta)
  • f244dbf fix: use MessagePlaceholderData type from @eslint/core (#20348) (루밀LuMir)
  • d186f8c fix: update eslint (#20427) (renovate[bot])
  • 2332262 fix: error location should not modify error message in RuleTester (#20421) (Milos Djermanovic)
  • ab99b21 fix: ensure filename is passed as third argument to verifyAndFix() (#20405) (루밀LuMir)
  • 8a60f3b fix: remove ecmaVersion and sourceType from ParserOptions type (#20415) (Pixel998)
  • eafd727 fix: remove TDZ scope type (#20231) (jaymarvelz)

... (truncated)

Commits
  • 4e6c4ac 10.0.0
  • ddd8a22 Build: changelog update for 10.0.0
  • bff9091 feat: handle Array.fromAsync in array-callback-return (#20457)
  • 1ece282 chore: ignore /docs/v9.x in link checker (#20452)
  • 034e139 ci: add type integration test for @html-eslint/eslint-plugin (#20345)
  • f3fbc2f chore: set @eslint/js version to 10.0.0 to skip releasing it (#20466)
  • e978dda docs: Update README
  • 4cecf83 docs: Update README
  • c79f0ab docs: Update README
  • afc0681 chore: remove scopeManager.addGlobals patch for typescript-eslint parser (#20...
  • Additional commits viewable in compare view

Updates eslint-config-next from 15.5.12 to 16.1.6

Release notes

Sourced from eslint-config-next's releases.

v16.1.6

[!NOTE] This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • Upgrade to swc 54 (#88207)
  • implement LRU cache with invocation ID scoping for minimal mode response cache (#88509)
  • tweak LRU sentinel key (#89123)

Credits

Huge thanks to @​mischnic, @​wyattjoh, and @​ztanner for helping!

v16.1.5

Please refer the following changelogs for more information about this security release:

https://vercel.com/changelog/summaries-of-cve-2025-59471-and-cve-2025-59472 https://vercel.com/changelog/summary-of-cve-2026-23864

v16.1.4

[!NOTE] This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • Only filter next config if experimental flag is enabled (#88733)

Credits

Huge thanks to @​mischnic for helping!

v16.1.3

[!NOTE] This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

  • Fix linked list bug in LRU deleteFromLru (#88652)
  • Fix relative same host redirects in node middleware (#88253)

Credits

Huge thanks to @​acdlite and @​ijjk for helping!

v16.1.2

[!NOTE] This release is backporting bug fixes. It does not include all pending features/changes on canary.

Core Changes

... (truncated)

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore <dependency name> major version will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
  • @dependabot ignore <dependency name> minor version will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
  • @dependabot ignore <dependency name> will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
  • @dependabot unignore <dependency name> will remove all of the ignore conditions of the specified dependency
  • @dependabot unignore <dependency name> <ignore condition> will remove the ignore condition of the specified dependency and ignore conditions

Bumps the dev-dependencies group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) | `19.8.1` | `20.4.1` |
| [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) | `19.8.1` | `20.4.1` |
| [turbo](https://github.com/vercel/turborepo) | `2.8.7` | `2.8.9` |
| [eslint](https://github.com/eslint/eslint) | `9.39.2` | `10.0.0` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `15.5.12` | `16.1.6` |


Updates `@commitlint/cli` from 19.8.1 to 20.4.1
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v20.4.1/@commitlint/cli)

Updates `@commitlint/config-conventional` from 19.8.1 to 20.4.1
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v20.4.1/@commitlint/config-conventional)

Updates `turbo` from 2.8.7 to 2.8.9
- [Release notes](https://github.com/vercel/turborepo/releases)
- [Changelog](https://github.com/vercel/turborepo/blob/main/RELEASE.md)
- [Commits](vercel/turborepo@v2.8.7...v2.8.9)

Updates `eslint` from 9.39.2 to 10.0.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v9.39.2...v10.0.0)

Updates `eslint-config-next` from 15.5.12 to 16.1.6
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v16.1.6/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 20.4.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: dev-dependencies
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 20.4.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: dev-dependencies
- dependency-name: turbo
  dependency-version: 2.8.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: eslint
  dependency-version: 10.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: dev-dependencies
- dependency-name: eslint-config-next
  dependency-version: 16.1.6
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Feb 15, 2026
@dependabot @github
Copy link
Copy Markdown
Contributor Author

dependabot Bot commented on behalf of github Feb 16, 2026

Looks like these dependencies are updatable in another way, so this is no longer needed.

@dependabot dependabot Bot closed this Feb 16, 2026
@dependabot dependabot Bot deleted the dependabot/npm_and_yarn/dev-dependencies-3a6b72160b branch February 16, 2026 21:18
Ur-imazing added a commit that referenced this pull request Apr 20, 2026
- ContentRail: pass `item` directly through the renderItem hooks
  callback instead of looking it up via `data[index]` at callback
  time. FlatList fires focus callbacks asynchronously and an Apollo
  cache update can shrink `data` between render and callback, making
  `data[index]` undefined and crashing consumers that read
  `item.documentId` (review finding P1 #2).
- ContentRail: remove the redundant wrapper `<View onFocus>`. Both
  the wrapper and `hooks.onFocus` resolved to the same
  `handleItemFocus(index)` call and fired per focus event on
  platforms where View focus bubbles, double-dispatching the
  debounce timer and any analytics (P2 #5).
- ContentRail: drop the dead `focusMemory` module-level Map. It was
  written on every focus event but never read anywhere in the
  codebase (P3 #11).
- HomeHero: move `HeroEntry` type to module scope — it was defined
  inside the component body for no reason (P3 #16).
- HomeScreen: replace the local `COLORS` constant that duplicated
  `src/lib/colors.ts`. Every other component imports the canonical
  tokens; `index.tsx` was silently drifting (P3 #13).
- HomeScreen: derive `effectiveCommittedId = committedId ??
  homepageExperience?.documentId ?? null` so the hero renders on
  first paint rather than waiting for the `useEffect`-seeded
  `committedId`. Previously a blank hero surface flashed for
  ~50-100ms on cold mount while the effect fired (P2 #9).
- HomeScreen: depend the accessibility-announce effect on
  `hero?.id`/title/subtitle rather than the `hero` object reference,
  so Apollo cache re-normalisations that produce a new object
  identity for the same experience don't trigger spurious re-runs
  (P3 #14).
- queries.ts: remove orphaned comment referencing GET_WATCH_EXPERIENCE
  that trailed LIST_EXPERIENCES after the surrounding context was
  moved (P3 #12).
Ur-imazing added a commit that referenced this pull request Apr 20, 2026
…803)

* feat(tv): include VideoHero block in LIST_EXPERIENCES for focus-driven hero

Extend the home-screen listing query to carry each experience's first
ComponentSectionsVideoHero block alongside its lightweight fields so
the rail-driven hero can swap without a second round-trip per focus
change. Non-VideoHero blocks are returned with __typename only.

Plan: docs/plans/2026-04-17-001-feat-tv-focus-driven-hero-plan.md (Unit 1)

* feat(tv): add onItemFocus prop to ContentRail

Surface per-item focus events to rail consumers so the home screen
can drive a focus-driven hero. Preserves existing focusMemory write
behavior; the new prop is optional and additive.

Plan: docs/plans/2026-04-17-001-feat-tv-focus-driven-hero-plan.md (Unit 2)

* feat(tv): focus-driven hero swaps with rail card focus

Rewrite HomeHero to accept a single hero prop and cross-dissolve
between previous and current media via two stacked layers. The Explore
CTA lives in a stable text overlay (not a crossfading layer) so its
first-mount focus claim and identity survive hero swaps. Honors
AccessibilityInfo reduce-motion by snapping between states.

In HomeScreen, replace the dual LIST_EXPERIENCES + GET_WATCH_EXPERIENCE
queries with the extended LIST_EXPERIENCES (now includes each
experience's first VideoHero block). A 300ms trailing-only debounce
timer holds a committed-experience id; rail focus resets the timer,
commit fires on timeout. Initial render seeds the committed id to
isHomepage. Explore CTA targets whichever experience the hero
currently reflects, never a transiently-focused card.

Accessibility: after commit, dispatch AccessibilityInfo.announceForAccessibility
with the new hero's title + subtitle, guarded against duplicate
announcements when focus returns to the already-committed card.

Plan: docs/plans/2026-04-17-001-feat-tv-focus-driven-hero-plan.md (Units 3-6)

* fix(tv): wire rail focus through FocusableCard, not wrapper View

The wrapper View's onFocus does not reliably fire on react-native-tvos
when a nested Pressable inside FocusableCard gains focus, so the
focus-driven hero swap never triggered on real hardware. Pass an
explicit focus hook into renderItem and plumb it straight into
FocusableCard's onFocus prop, where it fires deterministically. Keep
the wrapper View's onFocus as a fallback so existing callers that
don't consume the hook still get focusMemory updates.

* fix(tv): show poster during HLS init to prevent black hero flash

When the focus-driven hero swaps to a new experience's streaming URL,
the native VideoView surface renders black while HLS loads the
manifest, estimates bandwidth, and decodes the first frame — often
200–800ms on TV hardware, and unmaskable on Android TV where the
VideoView punches through the RN view hierarchy.

Render the poster image as a base layer that always paints first, and
only mount the VideoView once the player reports readyToPlay. When
the video is ready, fade it in over the poster in 200ms. The user
never sees a black hero during a swap.

* fix(tv): freeze outgoing video and hold new poster for a seamless swap

Replace the prev/current slot pattern with a keyed layer stack so
each MediaLayer stays mounted across a hero commit. The outgoing
layer now stays where it was — its VideoView keeps painting the
video's last frame during the fade instead of re-mounting against
the outgoing experience's poster image (which was the jarring
mid-transition still the user was seeing).

Pause the outgoing player on deactivate so the painted frame
freezes instead of continuing to animate during the fade.

On the incoming layer, after the player reports readyToPlay hold
the poster visible for 1s, then crossfade to the video over 500ms.
This gives the eye a single stable still between the outgoing and
incoming videos rather than a rapid-fire video→still→still→video
sequence. Reduce Motion skips the hold and snaps.

* chore(tv): shorten hero poster hold to 500ms

* fix(tv): keep focus out of home hero video, add silent focus target on detail

Home: propagate pointerEvents="none" to every wrapper around the
VideoView inside MediaLayer so the TV focus engine doesn't stop on
the native video surface when the user D-pads up out of the rail.
Focus now consistently lands on the Explore CTA whether the hero's
video is playing or still loading as a poster.

Experience detail: the hero was non-focusable, which prevented the
user from scrolling back to the top of the screen once they moved
down into the blocks. Add a full-bleed Pressable behind the text
overlay as a silent focus target — invisible focus state, but
acceptable as a D-pad UP landing spot so ScrollView can scroll the
hero back into view.

* fix(tv): let hero-wide TVFocusGuide redirect rail UP-focus to Explore

The previous guide only wrapped the text container at the bottom of
the hero. When D-padding UP from the rail, focus tried to land
somewhere in the video region above the guide — the native VideoView
caught it and the Explore button was never reached while the video
was mounted.

Wrap the entire hero container in TVFocusGuideView so any upward
focus attempt into the hero area is redirected to the Explore
Pressable, regardless of whether the video is mounted or not.

* fix(tv): let rail receive focus directly on first DOWN from Explore

trapFocusDown=false on the hero TVFocusGuideView so DOWN from Explore
exits the hero region in a single press instead of bouncing off the
guide's bounds once.

* fix(tv): make Explore a sibling of the hero focus guide, not a descendant

Explore Pressable was inside the TVFocusGuideView, so DOWN from
Explore bounced off the guide's bounds once and required a second
press to reach the rail. Move the guide to wrap only the media
layers and gradient so Explore sits as a sibling in the view
hierarchy. The guide still catches upward focus attempts into the
media region and redirects them to Explore, but no longer traps
Explore's own outbound DOWN movement.

* fix(tv): single-press DOWN from Explore to rail via nextFocusDown

trapFocusDown on the hero's TVFocusGuideView didn't prevent Explore's
outbound DOWN from bouncing off the guide's bounds, so the rail
required two presses. Plumb the rail's TVFocusGuideView handle up to
HomeScreen, pass it into HomeHero as `nextFocusDownHandle`, and bind
it to Explore's `nextFocusDown` prop. Focus now crosses directly from
Explore into the rail on a single D-pad press without any guide
bounce. Verified end-to-end via keystroke-driven screenshots on the
tvOS simulator: DOWN → rail focus, UP → Explore focus, DOWN again →
rail focus on first press.

Also switch Explore's ref to a callback ref + state-backed node
handle so the hero's focus-guide destinations always resolve on
first render (React commits refs after render, so a render-time
read of `exploreRef.current` was null initially and left the guide
without a destination).

Route Explore's node handle through a callback-ref so the hero focus
guide has a valid destination from first render.

* docs(tv): add focus-driven hero brainstorm and plan

Brainstorm requirements and implementation plan that drove the
focus-driven hero feature shipped in the preceding tv commits.

* fix(tv): explore wins initial focus and never drops into limbo

Pre-fix: ContentRail's TVFocusGuideView autoFocus claimed initial
focus for the first rail card, overriding the Explore Pressable's
hasTVPreferredFocus. Pressing UP or DOWN from Explore could leave
focus stranded in the hero's non-focusable video region.

Changes:
- Drop autoFocus from ContentRail's TVFocusGuideView so Explore's
  hasTVPreferredFocus wins on first mount.
- Wrap the hero's text overlay in a TVFocusGuideView with
  trapFocusUp + autoFocus so UP from Explore keeps Explore focused
  instead of leaking focus into the video area.
- Plumb the Explore Pressable's native handle up through
  onExploreHandleChange so each rail card gets nextFocusUp routed
  directly to Explore.
- Keep nextFocusDown on Explore so DOWN moves into the rail.
- Make Explore's focused state visible: add a white border + glow
  on top of the 1.08x scale, since tvOS Pressable shadows in the
  button's own primary color were nearly invisible against the
  hero gradient.
- Disable ScrollView scroll on the home screen (content fits and
  scroll attempts were blurring Explore on UP).

* fix(tv): single-press DOWN from Explore while keeping UP trapped

Move the `trapFocusUp` TVFocusGuideView from the text container up
to the hero container. Wrapping only the text container made the
guide's frame tight enough that the first DOWN press got absorbed
by the guide instead of routing through Explore's `nextFocusDown`
to the rail.

Wrapping the entire hero keeps UP trapped inside the hero (no
focusable above Explore in the hero — so UP becomes a no-op), while
DOWN from Explore exits the hero boundary in a single press and
lands on the rail via the `nextFocusDown` handle.

Verified on tvOS simulator via keystroke-driven screenshots:
- Initial: Explore visibly focused
- UP from Explore: stays on Explore
- DOWN from Explore: first press focuses a rail card
- UP from rail: returns to Explore

* fix(tv): harden hero against VideoView hijacking focus while playing

When the hero's native VideoView is actively painting, the tvOS
focus engine treats it as a focus candidate even with
focusable={false} on VideoView itself. This caused UP/DOWN from
Explore to blur into limbo while the video played.

Layer the guards so tvOS reliably skips over the video surface:
- isTVSelectable={false} on every View/Animated.View wrapping the
  media layers inside the hero (outer and inner).
- Self-referencing nextFocusUp on the Explore Pressable so UP from
  Explore resolves back to Explore instead of the video above.
- Retain the outer hero TVFocusGuideView trapFocusUp as a second
  line of defense.

DOWN from Explore now reaches a rail card in a single press even
with the video playing. UP from rail into Explore and initial focus
on Explore are preserved.

* refactor(tv): make the home hero non-interactive, rail owns all focus

Remove the Explore CTA and all the focus-routing plumbing that tried
to coordinate focus between it and the rail. The native VideoView
kept hijacking focus away from any interactive element placed above
the rail, which forced an ever-growing list of guards (nextFocusUp
self-references, TVFocusGuideView trapFocusUp wrappers,
isTVSelectable={false} on every wrapper, state-backed node handles,
etc.) and still wasn't robust once the video was painting.

New model:
- HomeHero is now purely presentational — title, subtitle, and the
  crossfading video/poster. No Pressable, no focus concerns.
- ContentRail's TVFocusGuideView autoFocus claims initial focus on
  the first card. The user navigates experiences by D-padding the
  rail and pressing Select to open the focused experience.
- Drop the Explore-related props from HomeHero/ContentRail/FocusableCard
  (nextFocusUp, nextFocusDown, onExploreHandleChange,
  onFocusHandleChange, itemNextFocusUp).
- Re-enable ScrollView scroll on the home screen.

Net effect: something is always visibly focused (the rail auto-focuses
on mount), the video hero no longer competes for focus, and the
whole focus system is dramatically simpler.

* fix(tv): apply code review safe_auto findings

- ContentRail: pass `item` directly through the renderItem hooks
  callback instead of looking it up via `data[index]` at callback
  time. FlatList fires focus callbacks asynchronously and an Apollo
  cache update can shrink `data` between render and callback, making
  `data[index]` undefined and crashing consumers that read
  `item.documentId` (review finding P1 #2).
- ContentRail: remove the redundant wrapper `<View onFocus>`. Both
  the wrapper and `hooks.onFocus` resolved to the same
  `handleItemFocus(index)` call and fired per focus event on
  platforms where View focus bubbles, double-dispatching the
  debounce timer and any analytics (P2 #5).
- ContentRail: drop the dead `focusMemory` module-level Map. It was
  written on every focus event but never read anywhere in the
  codebase (P3 #11).
- HomeHero: move `HeroEntry` type to module scope — it was defined
  inside the component body for no reason (P3 #16).
- HomeScreen: replace the local `COLORS` constant that duplicated
  `src/lib/colors.ts`. Every other component imports the canonical
  tokens; `index.tsx` was silently drifting (P3 #13).
- HomeScreen: derive `effectiveCommittedId = committedId ??
  homepageExperience?.documentId ?? null` so the hero renders on
  first paint rather than waiting for the `useEffect`-seeded
  `committedId`. Previously a blank hero surface flashed for
  ~50-100ms on cold mount while the effect fired (P2 #9).
- HomeScreen: depend the accessibility-announce effect on
  `hero?.id`/title/subtitle rather than the `hero` object reference,
  so Apollo cache re-normalisations that produce a new object
  identity for the same experience don't trigger spurious re-runs
  (P3 #14).
- queries.ts: remove orphaned comment referencing GET_WATCH_EXPERIENCE
  that trailed LIST_EXPERIENCES after the surrounding context was
  moved (P3 #12).

* feat(tv): focus-driven hero swaps to selected experience's video

Home hero now renders the VideoHero block of whichever Experience card
is focused in the rail, so the background poster/video tracks D-pad
selection instead of being hard-coded. Adds compile-time asserts that
gql.tada's discriminated union for the blocks dynamic zone did not
collapse to `never`, since that failure mode is silent under tsc.
Documents the intentional divergence from the mobile LIST_EXPERIENCES
shape so future sync passes don't clobber the per-experience hero data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(solutions): capture TV focus-driven hero patterns and refresh the cluster

Adds a new best-practices learning documenting the patterns that came out
of PR #803 (non-interactive hero, rail-owns-focus, poster-hold during HLS
source swap, compile-time `never`-collapse assert for gql.tada dynamic
zones), and refreshes four adjacent docs whose guidance was either
superseded or incomplete in light of it:

- ui-bugs/tv-videoview-steals-dpad-focus — Prevention superseded the
  "wrap the hero in TVFocusGuideView" recommendation for hero-above-rail
  layouts; now points at the new learning.
- ui-bugs/tv-video-hero-blank-autoplay — adds a "Source swap on focus
  change" section covering the poster-hold technique.
- best-practices/react-native-tvos-porting-pitfalls — adds Pitfall 6
  (background VideoView + focusable siblings).
- best-practices/expo-tv-platform-setup-sdui-monorepo — Section 3 notes
  the LIST_EXPERIENCES divergence; Section 6 adds the rail-owns-focus
  pattern. Also drops a duplicate last_updated key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants