Skip to content

feat(media): init - #188

Open
coryrylan wants to merge 1 commit into
mainfrom
topic-media-feat
Open

feat(media): init#188
coryrylan wants to merge 1 commit into
mainfrom
topic-media-feat

Conversation

@coryrylan

@coryrylan coryrylan commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features
    • Added media controller and controls for playback, mute, pause, seeking, fullscreen, playback rate, time, and volume.
    • Added public package entry points, import-map support, and media metadata including data.html.json.
  • Documentation
    • Added a Media documentation section with guides for all media components.
  • Bug Fixes
    • Improved API search ranking so exact matches appear first.
    • Expanded invoker-trigger validation for media controls, including fullscreen buttons.

@coryrylan
coryrylan requested a review from johnyanarella July 22, 2026 21:15
@coryrylan coryrylan self-assigned this Jul 22, 2026
Copilot AI review requested due to automatic review settings July 22, 2026 21:15
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a media controller and seven media controls with shared state, commands, form integration, accessibility, SSR, visual, and Lighthouse tests. Updates package exports, build/test wiring, metadata generation, lint validation, playground imports, documentation navigation, and media component documentation.

Changes

Media component system

Layer / File(s) Summary
Media contracts and controller flow
projects/media/src/internal/*, projects/media/src/controller/*
Defines media state and command contracts, resolves command targets, synchronizes native media state, and handles playback, mute, seek, volume, rate, and fullscreen commands.
Media controls and shared behavior
projects/media/src/{mute-button,pause-button,fullscreen-button,seek-button,time-range,volume-range,playback-rate-select}/*
Adds media controls with controller synchronization, command dispatch, form behavior, i18n labels, custom-element registration, shared styles, examples, and component tests.
Package and workflow integration
projects/media/package.json, projects/media/vite.config.ts, projects/media/vitest.*, projects/media/tsconfig.lib.json
Publishes component entry points and custom data, adds SSR and visual test tasks, wires build inputs and dependencies, and configures aliases and test discovery.
Metadata, lint, and playground integration
projects/internals/metadata/*, projects/lint/src/eslint/rules/*, projects/internals/tools/src/playground/utils.ts, knip.config.js
Includes media artifacts in metadata generation, validates media controls as invoker elements, adds media CDN imports, and includes examples in unused-entry analysis.
Examples and documentation
projects/site/src/docs/media/*, projects/site/src/_11ty/layouts/common.js, projects/site/src/_11ty/layouts/docs.css
Adds media documentation, navigation entries, usage examples, and a video width constraint for documentation content.

Estimated code review effort: 5 (Critical) | ~120 minutes

Suggested reviewers: johnyanarella

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title identifies a media feature initialization but is too vague to describe the substantial controller and control components added. Replace “init” with a concise summary of the primary change, such as adding media controllers and playback control components.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch topic-media-feat

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR initializes the new @nvidia-elements/media package and wires it into the Elements monorepo ecosystem (docs site, tooling, linting, metadata), adding a media controller plus a first set of media control components.

Changes:

  • Add @nvidia-elements/media components (controller, playback controls) with unit/axe/visual/SSR/lighthouse coverage and baselines.
  • Add documentation pages + docs navigation entries for the new media components.
  • Integrate the new package into monorepo tooling (Wireit, metadata aggregation, playground import map, lint rule allowlists) and adjust lighthouse size thresholds.

Reviewed changes

Copilot reviewed 167 out of 170 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
knip.config.js Include example entrypoints in Knip analysis to account for new examples.
pnpm-lock.yaml Add workspace dependency link for @nvidia-elements/forms under the media importer.
projects/core/src/accordion/accordion.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/alert/alert.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/combobox/combobox.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/color/color.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/copy-button/copy-button.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/datetime/datetime.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/dialog/dialog.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/drawer/drawer.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/dropdown/dropdown.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/dropdown-group/dropdown-group.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/dropzone/dropzone.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/index.test.lighthouse.ts Adjust bundle size threshold for lighthouse baseline.
projects/core/src/internal/controllers/i18n.controller.test.ts Update expected i18n registry shape with new media strings.
projects/core/src/internal/services/i18n.service.test.ts Update expected i18n defaults and update behavior assertions.
projects/core/src/internal/services/i18n.service.ts Add new default i18n strings used by media components.
projects/core/src/month/month.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/notification/notification.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/pagination/pagination.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/panel/panel.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/password/password.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/preferences-input/preferences-input.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/resize-handle/resize-handle.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/search/search.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/select/select.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/sort-button/sort-button.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/steps/steps.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/tag/tag.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/time/time.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/toast/toast.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/toggletip/toggletip.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/tree/tree.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/core/src/week/week.test.lighthouse.ts Adjust JS size threshold for lighthouse baseline.
projects/internals/metadata/package.json Include media in metadata build inputs/coverage aggregation and tasks.
projects/internals/metadata/src/services/api.service.test.ts Add test ensuring exact-match API search ordering.
projects/internals/metadata/src/services/projects.service.test.ts Assert ProjectsService caching behavior.
projects/internals/metadata/src/services/releases.service.test.ts Assert ReleasesService caching behavior.
projects/internals/metadata/src/tasks/api.utils.test.ts Add assertions for projected mixin API attributes on media elements.
projects/internals/metadata/src/tasks/api.utils.ts Add media package to API aggregation inputs.
projects/internals/metadata/static/adoption.json Update published metadata snapshot (LFS pointer).
projects/internals/metadata/static/lighthouse.json Update published metadata snapshot (LFS pointer).
projects/internals/metadata/static/releases.json Update published metadata snapshot (LFS pointer).
projects/internals/metadata/static/tests.json Update published metadata snapshot (LFS pointer).
projects/internals/tools/src/api/service.test.ts Extend API service tests for mixin attribute projection and naming.
projects/internals/tools/src/playground/utils.ts Add @nvidia-elements/media to the playground import map.
projects/lint/src/eslint/rules/no-invalid-invoker-triggers.test.ts Update rule tests to include media invoker elements.
projects/lint/src/eslint/rules/no-invalid-invoker-triggers.ts Allow media components as valid invoker-trigger elements.
projects/media/.visual/media-controller.dark.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-controller.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-fullscreen-button.dark.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-fullscreen-button.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-mute-button.dark.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-mute-button.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-pause-button.dark.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-pause-button.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-playback-rate-select.dark.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-playback-rate-select.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-seek-button.dark.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-seek-button.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-time-range.dark.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-time-range.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-volume-range.dark.png Add visual baseline asset (LFS pointer).
projects/media/.visual/media-volume-range.png Add visual baseline asset (LFS pointer).
projects/media/package.json Add exports for media components and wire up Wireit tasks (SSR/visual), plus dependency updates.
projects/media/tsconfig.lib.json Exclude *.test.visual.ts from library compilation.
projects/media/vite.config.ts Add local aliasing for @nvidia-elements/media during build/dev.
projects/media/vitest.ssr.ts Add SSR test config with dist aliasing and junit output.
projects/media/vitest.visual.html Add visual test HTML harness for the media package.
projects/media/vitest.visual.ts Add visual test config + junit output for media package.
projects/media/src/controller/controller.css Add controller layout styles for slotted media + controls.
projects/media/src/controller/controller.examples.ts Add controller examples (default composition, commands, form values, card).
projects/media/src/controller/controller.test.axe.ts Add axe coverage for controller compositions.
projects/media/src/controller/controller.test.lighthouse.ts Add lighthouse baseline for controller.
projects/media/src/controller/controller.test.ssr.ts Add SSR baseline test for controller.
projects/media/src/controller/controller.test.ts Add unit tests for controller command handling and state syncing.
projects/media/src/controller/controller.test.visual.ts Add visual regression coverage for controller.
projects/media/src/controller/controller.ts Implement media controller command handling + state projection.
projects/media/src/controller/define.ts Register nve-media-controller.
projects/media/src/controller/index.ts Export controller + media state event types.
projects/media/src/declarations.d.ts Add CSS module typings for the media project.
projects/media/src/fullscreen-button/fullscreen-button.css Add fullscreen button stylesheet header.
projects/media/src/fullscreen-button/fullscreen-button.examples.ts Add fullscreen button example.
projects/media/src/fullscreen-button/fullscreen-button.test.axe.ts Add axe coverage for fullscreen button.
projects/media/src/fullscreen-button/fullscreen-button.test.lighthouse.ts Add lighthouse coverage for fullscreen button.
projects/media/src/fullscreen-button/fullscreen-button.test.ssr.ts Add SSR baseline for fullscreen button.
projects/media/src/fullscreen-button/fullscreen-button.test.ts Add unit tests for fullscreen button behavior.
projects/media/src/fullscreen-button/fullscreen-button.test.visual.ts Add visual coverage for fullscreen button.
projects/media/src/fullscreen-button/fullscreen-button.ts Implement fullscreen command button synced to controller state.
projects/media/src/fullscreen-button/define.ts Register nve-media-fullscreen-button.
projects/media/src/fullscreen-button/index.ts Export fullscreen button.
projects/media/src/internal/button-form-control-usage.test.ts Add shared tests validating ButtonFormControlMixin behavior in media buttons.
projects/media/src/internal/command-target.ts Implement commandfor / commandForElement target resolution.
projects/media/src/internal/controllers/media-state.controller.test.ts Add unit tests for MediaStateController retargeting + sync behavior.
projects/media/src/internal/controllers/media-state.controller.ts Add reactive controller to sync host from media-state-change events.
projects/media/src/internal/media-button.css Add shared button styles for media controls.
projects/media/src/internal/media-command.ts Add media command constants and types.
projects/media/src/internal/media-range.css Add shared slider styling for time/volume ranges.
projects/media/src/internal/media-state.ts Add MediaState model + helpers (validation, equality, event constant).
projects/media/src/mute-button/mute-button.css Add mute button stylesheet header.
projects/media/src/mute-button/mute-button.examples.ts Add mute button example.
projects/media/src/mute-button/mute-button.test.axe.ts Add axe coverage for mute button.
projects/media/src/mute-button/mute-button.test.lighthouse.ts Add lighthouse coverage for mute button.
projects/media/src/mute-button/mute-button.test.ssr.ts Add SSR baseline for mute button.
projects/media/src/mute-button/mute-button.test.ts Add unit tests for mute button state + commands + forms.
projects/media/src/mute-button/mute-button.test.visual.ts Add visual coverage for mute button.
projects/media/src/mute-button/mute-button.ts Implement mute toggle button synced to controller muted state.
projects/media/src/mute-button/define.ts Register nve-media-mute-button.
projects/media/src/mute-button/index.ts Export mute button.
projects/media/src/pause-button/pause-button.css Add pause button stylesheet header.
projects/media/src/pause-button/pause-button.examples.ts Add pause button example.
projects/media/src/pause-button/pause-button.test.axe.ts Add axe coverage for pause button.
projects/media/src/pause-button/pause-button.test.lighthouse.ts Add lighthouse coverage for pause button.
projects/media/src/pause-button/pause-button.test.ssr.ts Add SSR baseline for pause button.
projects/media/src/pause-button/pause-button.test.ts Add unit tests for pause button state + commands + forms.
projects/media/src/pause-button/pause-button.test.visual.ts Add visual coverage for pause button.
projects/media/src/pause-button/pause-button.ts Implement pause toggle button synced to paused/ended state.
projects/media/src/pause-button/define.ts Register nve-media-pause-button.
projects/media/src/pause-button/index.ts Export pause button.
projects/media/src/playback-rate-select/playback-rate-select.css Add select styles for playback-rate select.
projects/media/src/playback-rate-select/playback-rate-select.examples.ts Add playback rate select examples (default + disabled).
projects/media/src/playback-rate-select/playback-rate-select.test.axe.ts Add axe coverage for playback rate select.
projects/media/src/playback-rate-select/playback-rate-select.test.lighthouse.ts Add lighthouse coverage for playback rate select.
projects/media/src/playback-rate-select/playback-rate-select.test.ssr.ts Add SSR baseline for playback rate select.
projects/media/src/playback-rate-select/playback-rate-select.test.ts Add unit tests for playback rate select API + sync + forms.
projects/media/src/playback-rate-select/playback-rate-select.test.visual.ts Add visual coverage for playback rate select.
projects/media/src/playback-rate-select/playback-rate-select.ts Implement playback rate select synced to controller rate.
projects/media/src/playback-rate-select/define.ts Register nve-media-playback-rate-select.
projects/media/src/playback-rate-select/index.ts Export playback rate select.
projects/media/src/seek-button/seek-button.css Add seek button stylesheet header.
projects/media/src/seek-button/seek-button.examples.ts Add seek button toolbar example.
projects/media/src/seek-button/seek-button.test.axe.ts Add axe coverage for seek button.
projects/media/src/seek-button/seek-button.test.lighthouse.ts Add lighthouse coverage for seek button.
projects/media/src/seek-button/seek-button.test.ssr.ts Add SSR baseline for seek button.
projects/media/src/seek-button/seek-button.test.ts Add unit tests for derived command behavior + labels/icons.
projects/media/src/seek-button/seek-button.test.visual.ts Add visual coverage for seek button.
projects/media/src/seek-button/seek-button.ts Implement seek button with derived commands and i18n-driven labels.
projects/media/src/seek-button/define.ts Register nve-media-seek-button.
projects/media/src/seek-button/index.ts Export seek button.
projects/media/src/time-range/define.ts Register nve-media-time-range.
projects/media/src/time-range/index.ts Export time range.
projects/media/src/time-range/time-range.css Add time range stylesheet header.
projects/media/src/time-range/time-range.examples.ts Add time range example.
projects/media/src/time-range/time-range.test.axe.ts Add axe coverage for time range.
projects/media/src/time-range/time-range.test.lighthouse.ts Add lighthouse coverage for time range.
projects/media/src/time-range/time-range.test.ssr.ts Add SSR baseline for time range.
projects/media/src/time-range/time-range.test.ts Add unit tests for time range sync/commands/forms.
projects/media/src/time-range/time-range.test.visual.ts Add visual coverage for time range.
projects/media/src/time-range/time-range.ts Implement time scrubbing range synced to controller time/duration.
projects/media/src/volume-range/define.ts Register nve-media-volume-range.
projects/media/src/volume-range/index.ts Export volume range.
projects/media/src/volume-range/volume-range.css Add volume range stylesheet header.
projects/media/src/volume-range/volume-range.examples.ts Add volume range example.
projects/media/src/volume-range/volume-range.test.axe.ts Add axe coverage for volume range.
projects/media/src/volume-range/volume-range.test.lighthouse.ts Add lighthouse coverage for volume range.
projects/media/src/volume-range/volume-range.test.ssr.ts Add SSR baseline for volume range.
projects/media/src/volume-range/volume-range.test.ts Add unit tests for volume range sync/commands/forms.
projects/media/src/volume-range/volume-range.test.visual.ts Add visual coverage for volume range.
projects/media/src/volume-range/volume-range.ts Implement volume range synced to controller volume.
projects/site/src/_11ty/layouts/common.js Add Media section and entries to docs navigation tree.
projects/site/src/docs/media/controller.md Add docs page for media controller.
projects/site/src/docs/media/fullscreen-button.md Add docs page for fullscreen button.
projects/site/src/docs/media/mute-button.md Add docs page for mute button.
projects/site/src/docs/media/pause-button.md Add docs page for pause button.
projects/site/src/docs/media/playback-rate-select.md Add docs page for playback rate select.
projects/site/src/docs/media/seek-button.md Add docs page for seek button.
projects/site/src/docs/media/time-range.md Add docs page for time range.
projects/site/src/docs/media/volume-range.md Add docs page for volume range.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread projects/media/src/internal/media-button.css
Comment thread projects/media/src/controller/controller.ts
Comment thread projects/media/src/volume-range/volume-range.ts
Comment thread projects/media/src/time-range/time-range.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@projects/internals/metadata/src/services/api.service.test.ts`:
- Around line 43-47: Update the test “should prioritize exact matches over fuzzy
matches” to control the search results so a fuzzy match appears before the exact
“nve-button” match, then assert that exact-match promotion moves “nve-button” to
index 0. Ensure the test would fail if the exactMatchIndex > 0 promotion logic
regresses.

In `@projects/media/src/controller/controller.test.axe.ts`:
- Line 30: Wrap the test body that mounts the fixture and calls elementIsStable,
runAxe, and the assertion in a try/finally block, moving removeFixture(fixture)
into finally. Preserve the existing test behavior while guaranteeing fixture
cleanup when any step fails.

In `@projects/media/src/internal/media-button.css`:
- Line 23: Resolve the Stylelint violations in
projects/media/src/internal/media-button.css at lines 23-23, 171-171, and
225-225, and projects/media/src/internal/media-range.css at line 17-17: add the
required blank lines before display and pointer-events declarations, and change
currentColor to lowercase currentcolor.
- Around line 54-57: Update the readonly cursor custom property in both readonly
blocks to use a valid CSS cursor keyword, replacing `cursor` with `default` or
the intended keyword while preserving the existing text-decoration behavior.

In `@projects/media/src/playback-rate-select/playback-rate-select.css`:
- Line 16: In projects/media/src/playback-rate-select/playback-rate-select.css,
add a blank line before the standard declarations display: block; at lines 16-16
and background: var(--background); at lines 31-31, separating them from the
preceding custom-property declarations to satisfy Stylelint.
- Around line 41-45: In the playback-rate select style rule, move the font:
inherit declaration before line-height: var(--height) !important so the font
shorthand does not override the later line-height declaration. Preserve the
existing values and ordering of the other declarations.

In `@projects/media/src/playback-rate-select/playback-rate-select.ts`:
- Around line 121-129: Update `#handleMediaState` and the option-rendering flow to
ensure a valid positive state.playbackRate such as 1.25 is included in the
rendered rates when it is absent from rates, so the select displays the active
value. Preserve existing default-rate rendering and avoid adding invalid or
non-finite values.

In `@projects/media/src/time-range/time-range.ts`:
- Around line 49-53: Extract the shared command properties contract used by
time-range, volume-range, and playback-rate-select into a reusable mixin or base
contract, including command, commandfor, and commandForElement with their
existing types and defaults. Update each control to consume that shared
contract, while keeping internal/command-target.ts focused solely on target
resolution.

In `@projects/media/src/volume-range/volume-range.ts`:
- Around line 63-65: Update the volume-range media-state handling so the
mediaDisabled state is synchronized from the media state’s relevant disabled
capability, rather than remaining false after initialization. Extend
`#handleMediaState` alongside its existing volume synchronization, ensuring the
disabled input and null form-value paths react to target state while preserving
current volume behavior; add coverage for the disabled-state transition.

In `@projects/site/src/docs/api-design/media.md`:
- Around line 88-103: Keep the playback-rate contract consistent in both
documented locations: update projects/site/src/docs/api-design/media.md lines
88-103 and projects/site/src/docs/media/controller.md lines 32-45 to either
document playbackRate in mediaState and the reflected playback-rate attribute at
both sites, or explicitly remove both from the controller API documentation at
both sites.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: a4feeb98-2a99-409c-a5d7-aea26ea0f502

📥 Commits

Reviewing files that changed from the base of the PR and between 287e6c3 and 2d1a9a5.

⛔ Files ignored due to path filters (18)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • projects/media/.visual/media-controller.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-controller.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.png is excluded by !**/*.png
  • projects/site/public/static/video/particle.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (152)
  • knip.config.js
  • projects/core/src/accordion/accordion.test.lighthouse.ts
  • projects/core/src/alert/alert.test.lighthouse.ts
  • projects/core/src/color/color.test.lighthouse.ts
  • projects/core/src/combobox/combobox.test.lighthouse.ts
  • projects/core/src/copy-button/copy-button.test.lighthouse.ts
  • projects/core/src/datetime/datetime.test.lighthouse.ts
  • projects/core/src/dialog/dialog.test.lighthouse.ts
  • projects/core/src/drawer/drawer.test.lighthouse.ts
  • projects/core/src/dropdown-group/dropdown-group.test.lighthouse.ts
  • projects/core/src/dropdown/dropdown.test.lighthouse.ts
  • projects/core/src/dropzone/dropzone.test.lighthouse.ts
  • projects/core/src/index.test.lighthouse.ts
  • projects/core/src/internal/controllers/i18n.controller.test.ts
  • projects/core/src/internal/services/i18n.service.test.ts
  • projects/core/src/internal/services/i18n.service.ts
  • projects/core/src/month/month.test.lighthouse.ts
  • projects/core/src/notification/notification.test.lighthouse.ts
  • projects/core/src/pagination/pagination.test.lighthouse.ts
  • projects/core/src/panel/panel.test.lighthouse.ts
  • projects/core/src/password/password.test.lighthouse.ts
  • projects/core/src/preferences-input/preferences-input.test.lighthouse.ts
  • projects/core/src/resize-handle/resize-handle.test.lighthouse.ts
  • projects/core/src/search/search.test.lighthouse.ts
  • projects/core/src/select/select.test.lighthouse.ts
  • projects/core/src/sort-button/sort-button.test.lighthouse.ts
  • projects/core/src/steps/steps.test.lighthouse.ts
  • projects/core/src/tag/tag.test.lighthouse.ts
  • projects/core/src/time/time.test.lighthouse.ts
  • projects/core/src/toast/toast.test.lighthouse.ts
  • projects/core/src/toggletip/toggletip.test.lighthouse.ts
  • projects/core/src/tree/tree.test.lighthouse.ts
  • projects/core/src/week/week.test.lighthouse.ts
  • projects/internals/metadata/package.json
  • projects/internals/metadata/src/services/api.service.test.ts
  • projects/internals/metadata/src/services/projects.service.test.ts
  • projects/internals/metadata/src/services/releases.service.test.ts
  • projects/internals/metadata/src/tasks/api.utils.test.ts
  • projects/internals/metadata/src/tasks/api.utils.ts
  • projects/internals/metadata/static/adoption.json
  • projects/internals/metadata/static/lighthouse.json
  • projects/internals/metadata/static/releases.json
  • projects/internals/metadata/static/tests.json
  • projects/internals/tools/src/api/service.test.ts
  • projects/internals/tools/src/playground/utils.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.test.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.ts
  • projects/media/package.json
  • projects/media/src/controller/controller.css
  • projects/media/src/controller/controller.examples.ts
  • projects/media/src/controller/controller.test.axe.ts
  • projects/media/src/controller/controller.test.lighthouse.ts
  • projects/media/src/controller/controller.test.ssr.ts
  • projects/media/src/controller/controller.test.ts
  • projects/media/src/controller/controller.test.visual.ts
  • projects/media/src/controller/controller.ts
  • projects/media/src/controller/define.ts
  • projects/media/src/controller/index.ts
  • projects/media/src/declarations.d.ts
  • projects/media/src/fullscreen-button/define.ts
  • projects/media/src/fullscreen-button/fullscreen-button.css
  • projects/media/src/fullscreen-button/fullscreen-button.examples.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.axe.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.lighthouse.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ssr.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.visual.ts
  • projects/media/src/fullscreen-button/fullscreen-button.ts
  • projects/media/src/fullscreen-button/index.ts
  • projects/media/src/internal/button-form-control-usage.test.ts
  • projects/media/src/internal/command-target.ts
  • projects/media/src/internal/controllers/media-state.controller.test.ts
  • projects/media/src/internal/controllers/media-state.controller.ts
  • projects/media/src/internal/media-button.css
  • projects/media/src/internal/media-command.ts
  • projects/media/src/internal/media-range.css
  • projects/media/src/internal/media-state.ts
  • projects/media/src/mute-button/define.ts
  • projects/media/src/mute-button/index.ts
  • projects/media/src/mute-button/mute-button.css
  • projects/media/src/mute-button/mute-button.examples.ts
  • projects/media/src/mute-button/mute-button.test.axe.ts
  • projects/media/src/mute-button/mute-button.test.lighthouse.ts
  • projects/media/src/mute-button/mute-button.test.ssr.ts
  • projects/media/src/mute-button/mute-button.test.ts
  • projects/media/src/mute-button/mute-button.test.visual.ts
  • projects/media/src/mute-button/mute-button.ts
  • projects/media/src/pause-button/define.ts
  • projects/media/src/pause-button/index.ts
  • projects/media/src/pause-button/pause-button.css
  • projects/media/src/pause-button/pause-button.examples.ts
  • projects/media/src/pause-button/pause-button.test.axe.ts
  • projects/media/src/pause-button/pause-button.test.lighthouse.ts
  • projects/media/src/pause-button/pause-button.test.ssr.ts
  • projects/media/src/pause-button/pause-button.test.ts
  • projects/media/src/pause-button/pause-button.test.visual.ts
  • projects/media/src/pause-button/pause-button.ts
  • projects/media/src/playback-rate-select/define.ts
  • projects/media/src/playback-rate-select/index.ts
  • projects/media/src/playback-rate-select/playback-rate-select.css
  • projects/media/src/playback-rate-select/playback-rate-select.examples.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.axe.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.lighthouse.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ssr.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.visual.ts
  • projects/media/src/playback-rate-select/playback-rate-select.ts
  • projects/media/src/seek-button/define.ts
  • projects/media/src/seek-button/index.ts
  • projects/media/src/seek-button/seek-button.css
  • projects/media/src/seek-button/seek-button.examples.ts
  • projects/media/src/seek-button/seek-button.test.axe.ts
  • projects/media/src/seek-button/seek-button.test.lighthouse.ts
  • projects/media/src/seek-button/seek-button.test.ssr.ts
  • projects/media/src/seek-button/seek-button.test.ts
  • projects/media/src/seek-button/seek-button.test.visual.ts
  • projects/media/src/seek-button/seek-button.ts
  • projects/media/src/time-range/define.ts
  • projects/media/src/time-range/index.ts
  • projects/media/src/time-range/time-range.css
  • projects/media/src/time-range/time-range.examples.ts
  • projects/media/src/time-range/time-range.test.axe.ts
  • projects/media/src/time-range/time-range.test.lighthouse.ts
  • projects/media/src/time-range/time-range.test.ssr.ts
  • projects/media/src/time-range/time-range.test.ts
  • projects/media/src/time-range/time-range.test.visual.ts
  • projects/media/src/time-range/time-range.ts
  • projects/media/src/volume-range/define.ts
  • projects/media/src/volume-range/index.ts
  • projects/media/src/volume-range/volume-range.css
  • projects/media/src/volume-range/volume-range.examples.ts
  • projects/media/src/volume-range/volume-range.test.axe.ts
  • projects/media/src/volume-range/volume-range.test.lighthouse.ts
  • projects/media/src/volume-range/volume-range.test.ssr.ts
  • projects/media/src/volume-range/volume-range.test.ts
  • projects/media/src/volume-range/volume-range.test.visual.ts
  • projects/media/src/volume-range/volume-range.ts
  • projects/media/tsconfig.lib.json
  • projects/media/vite.config.ts
  • projects/media/vitest.ssr.ts
  • projects/media/vitest.visual.html
  • projects/media/vitest.visual.ts
  • projects/site/src/_11ty/layouts/common.js
  • projects/site/src/docs/api-design/media.md
  • projects/site/src/docs/media/controller.md
  • projects/site/src/docs/media/fullscreen-button.md
  • projects/site/src/docs/media/mute-button.md
  • projects/site/src/docs/media/pause-button.md
  • projects/site/src/docs/media/playback-rate-select.md
  • projects/site/src/docs/media/seek-button.md
  • projects/site/src/docs/media/time-range.md
  • projects/site/src/docs/media/volume-range.md

Comment thread projects/internals/metadata/src/services/api.service.test.ts
Comment thread projects/media/src/controller/controller.test.axe.ts Outdated
Comment thread projects/media/src/internal/media-button.css
Comment thread projects/media/src/internal/media-button.css
Comment thread projects/media/src/playback-rate-select/playback-rate-select.css
Comment thread projects/media/src/playback-rate-select/playback-rate-select.css
Comment thread projects/media/src/playback-rate-select/playback-rate-select.ts
Comment thread projects/media/src/time-range/time-range.ts
Comment thread projects/media/src/volume-range/volume-range.ts
Comment thread projects/site/src/docs/api-design/media.md Outdated
@coderabbitai coderabbitai Bot mentioned this pull request Jul 22, 2026
Copilot AI review requested due to automatic review settings July 22, 2026 21:52
@coryrylan
coryrylan marked this pull request as draft July 22, 2026 21:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 167 out of 170 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread projects/media/src/playback-rate-select/playback-rate-select.ts
Copilot AI review requested due to automatic review settings July 22, 2026 22:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

♻️ Duplicate comments (1)
projects/site/src/docs/api-design/media.md (1)

92-103: 🗄️ Data Integrity & Integration | 🟡 Minor

The state table still omits playback-rate.

mediaState and projects/site/src/docs/media/controller.md document playback rate, but this reflected-attribute table does not. Add a playback-rate row sourced from media.playbackRate; this is the same unresolved contract drift raised in the previous review.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/site/src/docs/api-design/media.md` around lines 92 - 103, Add the
missing playback-rate entry to the reflected-attribute table, using a number
type and sourcing it from media.playbackRate. Keep the existing mediaState and
event documentation unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@projects/lint/src/eslint/rules/no-invalid-invoker-triggers.ts`:
- Around line 27-34: Update the invoker validation around BUTTON_TYPE_ELEMENTS
and INVOKER_ATTRIBUTES so the newly added media elements are allowed only for
commandfor, while popovertarget and interestfor retain their intended element
restrictions. Split the allowlist by invoker attribute or otherwise encode the
attribute-specific support matrix, and add or update coverage to verify each
attribute independently.

In `@projects/media/package.json`:
- Around line 325-345: Update the test:visual script configuration to track the
assets imported by vitest.visual.html: add tsconfig.json and the relevant
themes/styles dist CSS globs to files, and explicitly declare the themes and
styles build scripts as non-cascading dependencies alongside the existing
dependencies. Align this dependency and invalidation tracking with test:ssr
without changing the visual test command.

In `@projects/media/src/controller/controller.examples.ts`:
- Around line 93-100: Move the controller-form input listener out of the inline
module script in the example template and bind it through the Lit component or
example harness instead. Ensure the handler associated with the controller form
updates the preview <pre> with current FormData values so the inline nve-canvas
preview remains dynamic.

In `@projects/media/src/controller/controller.ts`:
- Around line 36-49: The controller’s command documentation omits the supported
backward and forward seek commands. Update the JSDoc command block near the
existing seek entries to add `@command` entries for --seek-backward and
--seek-forward, matching the registered mediaCommands.seekBackward and
mediaCommands.seekForward handlers in `#commandHandlers`.

In `@projects/media/src/fullscreen-button/fullscreen-button.ts`:
- Around line 87-91: Update `#syncPressedState` to reset this.pressed to false
when state is null, while preserving the existing state.fullscreen assignment
for non-null MediaState values.

In `@projects/media/src/internal/media-state.ts`:
- Around line 48-59: Update mediaStatesEqual to compare numeric fields using
NaN-safe equality, so identical NaN values such as an unknown duration are
treated as equal while preserving normal equality for other values. Apply this
to the relevant numeric MediaState properties, including currentTime, duration,
playbackRate, and volume.

In `@projects/media/src/pause-button/pause-button.test.ts`:
- Around line 126-167: Update the affected tests to wrap the console.warn spy
and form fixture lifecycle in try/finally blocks, ensuring warn.mockRestore()
and removeFixture(formFixture) run even when assertions or setup fail. Preserve
the existing test behavior and assertions.

In `@projects/media/src/seek-button/seek-button.test.ts`:
- Around line 86-97: Ensure fixture cleanup runs even when assertions fail: wrap
the commandFixture test flow in
projects/media/src/seek-button/seek-button.test.ts lines 86-97 with try/finally
and remove commandFixture in finally; apply the same pattern to formFixture in
projects/media/src/time-range/time-range.test.ts lines 141-153. Preserve the
existing test assertions and interaction flow.

In `@projects/media/src/seek-button/seek-button.ts`:
- Line 44: Validate the reflected action value before deriving the seek command
in the seek-button component. Update the action-handling logic around the action
property to normalize unsupported runtime strings to the safe default forward
action (or reject them consistently), ensuring command state, icon, and label
remain valid. Add coverage for an invalid action provided through the reflected
attribute path.

In `@projects/media/src/time-range/time-range.ts`:
- Around line 123-126: Update `#handleMediaState` to handle a null mediaState by
disabling the range and resetting its form value before returning. Ensure the
previous target’s enabled state and time value are cleared so the range cannot
display or submit stale data.

In `@projects/site/src/docs/api-design/media.md`:
- Around line 111-127: Update the media API proposal to document all
playback-rate surfaces: add --set-playback-rate using source.valueAsNumber to
the command table, list nve-media-playback-rate-select among form-associated
controls, and include its package directory plus an implementation-plan step.
Use the existing media-command.ts and playback-rate-select documentation as the
authoritative references.

In `@projects/site/src/docs/media/volume-range.md`:
- Around line 16-22: Remove the misleading value="0.8" attribute from the
nve-media-volume-range example, leaving the controller-driven mediaState
synchronization as the source of truth.

---

Duplicate comments:
In `@projects/site/src/docs/api-design/media.md`:
- Around line 92-103: Add the missing playback-rate entry to the
reflected-attribute table, using a number type and sourcing it from
media.playbackRate. Keep the existing mediaState and event documentation
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 89f75859-dcd1-4343-b8b7-f8845377f1a9

📥 Commits

Reviewing files that changed from the base of the PR and between 2d1a9a5 and 6a3cf4c.

⛔ Files ignored due to path filters (18)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • projects/media/.visual/media-controller.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-controller.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.png is excluded by !**/*.png
  • projects/site/public/static/video/particle.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (146)
  • knip.config.js
  • projects/core/src/accordion/accordion.test.lighthouse.ts
  • projects/core/src/alert/alert.test.lighthouse.ts
  • projects/core/src/color/color.test.lighthouse.ts
  • projects/core/src/combobox/combobox.test.lighthouse.ts
  • projects/core/src/copy-button/copy-button.test.lighthouse.ts
  • projects/core/src/datetime/datetime.test.lighthouse.ts
  • projects/core/src/dialog/dialog.test.lighthouse.ts
  • projects/core/src/drawer/drawer.test.lighthouse.ts
  • projects/core/src/dropdown-group/dropdown-group.test.lighthouse.ts
  • projects/core/src/dropdown/dropdown.test.lighthouse.ts
  • projects/core/src/dropzone/dropzone.test.lighthouse.ts
  • projects/core/src/index.test.lighthouse.ts
  • projects/core/src/month/month.test.lighthouse.ts
  • projects/core/src/notification/notification.test.lighthouse.ts
  • projects/core/src/pagination/pagination.test.lighthouse.ts
  • projects/core/src/panel/panel.test.lighthouse.ts
  • projects/core/src/password/password.test.lighthouse.ts
  • projects/core/src/preferences-input/preferences-input.test.lighthouse.ts
  • projects/core/src/resize-handle/resize-handle.test.lighthouse.ts
  • projects/core/src/search/search.test.lighthouse.ts
  • projects/core/src/select/select.test.lighthouse.ts
  • projects/core/src/sort-button/sort-button.test.lighthouse.ts
  • projects/core/src/steps/steps.test.lighthouse.ts
  • projects/core/src/tag/tag.test.lighthouse.ts
  • projects/core/src/time/time.test.lighthouse.ts
  • projects/core/src/toast/toast.test.lighthouse.ts
  • projects/core/src/toggletip/toggletip.test.lighthouse.ts
  • projects/core/src/tree/tree.test.lighthouse.ts
  • projects/core/src/week/week.test.lighthouse.ts
  • projects/internals/metadata/package.json
  • projects/internals/metadata/src/tasks/api.utils.test.ts
  • projects/internals/metadata/src/tasks/api.utils.ts
  • projects/internals/metadata/static/adoption.json
  • projects/internals/metadata/static/lighthouse.json
  • projects/internals/metadata/static/releases.json
  • projects/internals/metadata/static/tests.json
  • projects/internals/tools/src/api/service.test.ts
  • projects/internals/tools/src/playground/utils.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.test.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.ts
  • projects/media/package.json
  • projects/media/src/controller/controller.css
  • projects/media/src/controller/controller.examples.ts
  • projects/media/src/controller/controller.test.axe.ts
  • projects/media/src/controller/controller.test.lighthouse.ts
  • projects/media/src/controller/controller.test.ssr.ts
  • projects/media/src/controller/controller.test.ts
  • projects/media/src/controller/controller.test.visual.ts
  • projects/media/src/controller/controller.ts
  • projects/media/src/controller/define.ts
  • projects/media/src/controller/index.ts
  • projects/media/src/declarations.d.ts
  • projects/media/src/fullscreen-button/define.ts
  • projects/media/src/fullscreen-button/fullscreen-button.css
  • projects/media/src/fullscreen-button/fullscreen-button.examples.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.axe.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.lighthouse.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ssr.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.visual.ts
  • projects/media/src/fullscreen-button/fullscreen-button.ts
  • projects/media/src/fullscreen-button/index.ts
  • projects/media/src/internal/button-form-control-usage.test.ts
  • projects/media/src/internal/command-target.ts
  • projects/media/src/internal/controllers/media-state.controller.test.ts
  • projects/media/src/internal/controllers/media-state.controller.ts
  • projects/media/src/internal/media-button.css
  • projects/media/src/internal/media-command.ts
  • projects/media/src/internal/media-range.css
  • projects/media/src/internal/media-state.ts
  • projects/media/src/mute-button/define.ts
  • projects/media/src/mute-button/index.ts
  • projects/media/src/mute-button/mute-button.css
  • projects/media/src/mute-button/mute-button.examples.ts
  • projects/media/src/mute-button/mute-button.test.axe.ts
  • projects/media/src/mute-button/mute-button.test.lighthouse.ts
  • projects/media/src/mute-button/mute-button.test.ssr.ts
  • projects/media/src/mute-button/mute-button.test.ts
  • projects/media/src/mute-button/mute-button.test.visual.ts
  • projects/media/src/mute-button/mute-button.ts
  • projects/media/src/pause-button/define.ts
  • projects/media/src/pause-button/index.ts
  • projects/media/src/pause-button/pause-button.css
  • projects/media/src/pause-button/pause-button.examples.ts
  • projects/media/src/pause-button/pause-button.test.axe.ts
  • projects/media/src/pause-button/pause-button.test.lighthouse.ts
  • projects/media/src/pause-button/pause-button.test.ssr.ts
  • projects/media/src/pause-button/pause-button.test.ts
  • projects/media/src/pause-button/pause-button.test.visual.ts
  • projects/media/src/pause-button/pause-button.ts
  • projects/media/src/playback-rate-select/define.ts
  • projects/media/src/playback-rate-select/index.ts
  • projects/media/src/playback-rate-select/playback-rate-select.css
  • projects/media/src/playback-rate-select/playback-rate-select.examples.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.axe.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.lighthouse.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ssr.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.visual.ts
  • projects/media/src/playback-rate-select/playback-rate-select.ts
  • projects/media/src/seek-button/define.ts
  • projects/media/src/seek-button/index.ts
  • projects/media/src/seek-button/seek-button.css
  • projects/media/src/seek-button/seek-button.examples.ts
  • projects/media/src/seek-button/seek-button.test.axe.ts
  • projects/media/src/seek-button/seek-button.test.lighthouse.ts
  • projects/media/src/seek-button/seek-button.test.ssr.ts
  • projects/media/src/seek-button/seek-button.test.ts
  • projects/media/src/seek-button/seek-button.test.visual.ts
  • projects/media/src/seek-button/seek-button.ts
  • projects/media/src/time-range/define.ts
  • projects/media/src/time-range/index.ts
  • projects/media/src/time-range/time-range.css
  • projects/media/src/time-range/time-range.examples.ts
  • projects/media/src/time-range/time-range.test.axe.ts
  • projects/media/src/time-range/time-range.test.lighthouse.ts
  • projects/media/src/time-range/time-range.test.ssr.ts
  • projects/media/src/time-range/time-range.test.ts
  • projects/media/src/time-range/time-range.test.visual.ts
  • projects/media/src/time-range/time-range.ts
  • projects/media/src/volume-range/define.ts
  • projects/media/src/volume-range/index.ts
  • projects/media/src/volume-range/volume-range.css
  • projects/media/src/volume-range/volume-range.examples.ts
  • projects/media/src/volume-range/volume-range.test.axe.ts
  • projects/media/src/volume-range/volume-range.test.lighthouse.ts
  • projects/media/src/volume-range/volume-range.test.ssr.ts
  • projects/media/src/volume-range/volume-range.test.ts
  • projects/media/src/volume-range/volume-range.test.visual.ts
  • projects/media/src/volume-range/volume-range.ts
  • projects/media/tsconfig.lib.json
  • projects/media/vite.config.ts
  • projects/media/vitest.ssr.ts
  • projects/media/vitest.visual.html
  • projects/media/vitest.visual.ts
  • projects/site/src/_11ty/layouts/common.js
  • projects/site/src/docs/api-design/media.md
  • projects/site/src/docs/media/controller.md
  • projects/site/src/docs/media/fullscreen-button.md
  • projects/site/src/docs/media/mute-button.md
  • projects/site/src/docs/media/pause-button.md
  • projects/site/src/docs/media/playback-rate-select.md
  • projects/site/src/docs/media/seek-button.md
  • projects/site/src/docs/media/time-range.md
  • projects/site/src/docs/media/volume-range.md

Comment thread projects/lint/src/eslint/rules/no-invalid-invoker-triggers.ts
Comment thread projects/media/package.json
Comment thread projects/media/src/controller/controller.examples.ts
Comment thread projects/media/src/controller/controller.ts
Comment thread projects/media/src/internal/media-state.ts
Comment thread projects/media/src/seek-button/seek-button.test.ts
Comment thread projects/media/src/seek-button/seek-button.ts
Comment thread projects/media/src/time-range/time-range.ts
Comment thread projects/site/src/docs/api-design/media.md Outdated
Comment thread projects/site/src/docs/media/volume-range.md

@coryrylan coryrylan Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: select should have inline style disable like icon button here

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 167 out of 170 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (3)

projects/media/src/internal/media-button.css:57

  • --cursor: cursor; is not a valid CSS cursor value. Because [internal-host] applies cursor: var(--cursor), this will fall back to the browser default in unpredictable ways. Use a valid value such as default for readonly state.
    projects/media/src/internal/media-button.css:150
  • --cursor: cursor; is not a valid CSS cursor value, so the inline readonly button state won’t reliably present the intended cursor. Use a valid value (e.g. default).
    projects/media/src/playback-rate-select/playback-rate-select.ts:84
  • If i18n.playbackRateOption is missing/undefined (for example, a consumer provides a partial i18n object), formatI18n(...) returns undefined and the <option> label renders blank. Provide a fallback label (e.g. the raw rate).

Comment thread projects/media/src/internal/command-target.ts
Copilot AI review requested due to automatic review settings July 28, 2026 13:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 28, 2026 20:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 28, 2026 21:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 28, 2026 21:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 28, 2026 21:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coryrylan
coryrylan marked this pull request as ready for review July 28, 2026 22:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@projects/internals/metadata/src/services/api.service.test.ts`:
- Around line 45-50: Wrap the ApiService.search call in a try/finally block so
the MiniSearch.prototype spy created by searchSpy is always restored, including
when the search throws. Keep the existing search arguments and result assertions
unchanged, and move searchSpy.mockRestore() into the finally block.

In `@projects/media/src/controller/controller.test.ts`:
- Around line 358-386: Restore the original descriptors for
document.fullscreenElement and document.exitFullscreen after the “should handle
full-screen commands” test, including deleting properties that were not
originally present and reinstating captured descriptors when they were. Use a
finally block or dedicated cleanup so restoration runs even if an assertion
fails, without changing the test’s fullscreen behavior.

In `@projects/media/src/controller/controller.ts`:
- Line 66: Remove the static formAssociated flag from MediaController unless it
is intended to submit form data; if form participation is required, instead add
attachInternals() and setFormValue() plumbing and connect it to the MediaState
value.

In `@projects/media/src/declarations.d.ts`:
- Around line 9-12: Update the `*.css?inline` module declaration to expose
`content` through a default export instead of `export =`, matching the package’s
default-import usage and Vite’s inline CSS module shape.

In `@projects/media/src/internal/button-form-control-usage.test.ts`:
- Around line 77-84: Correct the property assignment in the test setup to use
the ButtonFormControlMixinInstance’s readOnly property, matching the existing
button.readOnly assertions and other usage. Keep the rest of the readonly
behavior test unchanged.

In
`@projects/media/src/playback-rate-select/playback-rate-select.test.lighthouse.ts`:
- Line 9: Update the report name passed to lighthouseRunner.getReport in the
playback-rate-select test to use the full element tag, matching sibling
lighthouse tests and the generated artifact directory naming convention.

In `@projects/media/vite.config.ts`:
- Line 2: Update the Vite import in the configuration to import UserConfig via a
type-only import, while keeping defineConfig and mergeConfig as regular runtime
imports.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 715e2861-665b-4895-9cdb-be7e63c470c4

📥 Commits

Reviewing files that changed from the base of the PR and between 6a3cf4c and 8730c65.

⛔ Files ignored due to path filters (18)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • projects/media/.visual/media-controller.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-controller.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.png is excluded by !**/*.png
  • projects/site/public/static/video/particle.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (117)
  • knip.config.js
  • projects/internals/metadata/package.json
  • projects/internals/metadata/src/services/api.service.test.ts
  • projects/internals/metadata/src/tasks/api.utils.test.ts
  • projects/internals/metadata/src/tasks/api.utils.ts
  • projects/internals/metadata/static/adoption.json
  • projects/internals/metadata/static/releases.json
  • projects/internals/metadata/static/tests.json
  • projects/internals/tools/src/api/service.test.ts
  • projects/internals/tools/src/playground/utils.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.test.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.ts
  • projects/media/package.json
  • projects/media/src/controller/controller.css
  • projects/media/src/controller/controller.examples.ts
  • projects/media/src/controller/controller.test.axe.ts
  • projects/media/src/controller/controller.test.lighthouse.ts
  • projects/media/src/controller/controller.test.ssr.ts
  • projects/media/src/controller/controller.test.ts
  • projects/media/src/controller/controller.test.visual.ts
  • projects/media/src/controller/controller.ts
  • projects/media/src/controller/define.ts
  • projects/media/src/controller/index.ts
  • projects/media/src/declarations.d.ts
  • projects/media/src/fullscreen-button/define.ts
  • projects/media/src/fullscreen-button/fullscreen-button.css
  • projects/media/src/fullscreen-button/fullscreen-button.examples.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.axe.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.lighthouse.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ssr.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.visual.ts
  • projects/media/src/fullscreen-button/fullscreen-button.ts
  • projects/media/src/fullscreen-button/index.ts
  • projects/media/src/internal/button-form-control-usage.test.ts
  • projects/media/src/internal/command-target.ts
  • projects/media/src/internal/controllers/media-state.controller.test.ts
  • projects/media/src/internal/controllers/media-state.controller.ts
  • projects/media/src/internal/media-button.css
  • projects/media/src/internal/media-command.ts
  • projects/media/src/internal/media-range.css
  • projects/media/src/internal/media-state.ts
  • projects/media/src/mute-button/define.ts
  • projects/media/src/mute-button/index.ts
  • projects/media/src/mute-button/mute-button.css
  • projects/media/src/mute-button/mute-button.examples.ts
  • projects/media/src/mute-button/mute-button.test.axe.ts
  • projects/media/src/mute-button/mute-button.test.lighthouse.ts
  • projects/media/src/mute-button/mute-button.test.ssr.ts
  • projects/media/src/mute-button/mute-button.test.ts
  • projects/media/src/mute-button/mute-button.test.visual.ts
  • projects/media/src/mute-button/mute-button.ts
  • projects/media/src/pause-button/define.ts
  • projects/media/src/pause-button/index.ts
  • projects/media/src/pause-button/pause-button.css
  • projects/media/src/pause-button/pause-button.examples.ts
  • projects/media/src/pause-button/pause-button.test.axe.ts
  • projects/media/src/pause-button/pause-button.test.lighthouse.ts
  • projects/media/src/pause-button/pause-button.test.ssr.ts
  • projects/media/src/pause-button/pause-button.test.ts
  • projects/media/src/pause-button/pause-button.test.visual.ts
  • projects/media/src/pause-button/pause-button.ts
  • projects/media/src/playback-rate-select/define.ts
  • projects/media/src/playback-rate-select/index.ts
  • projects/media/src/playback-rate-select/playback-rate-select.css
  • projects/media/src/playback-rate-select/playback-rate-select.examples.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.axe.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.lighthouse.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ssr.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.visual.ts
  • projects/media/src/playback-rate-select/playback-rate-select.ts
  • projects/media/src/seek-button/define.ts
  • projects/media/src/seek-button/index.ts
  • projects/media/src/seek-button/seek-button.css
  • projects/media/src/seek-button/seek-button.examples.ts
  • projects/media/src/seek-button/seek-button.test.axe.ts
  • projects/media/src/seek-button/seek-button.test.lighthouse.ts
  • projects/media/src/seek-button/seek-button.test.ssr.ts
  • projects/media/src/seek-button/seek-button.test.ts
  • projects/media/src/seek-button/seek-button.test.visual.ts
  • projects/media/src/seek-button/seek-button.ts
  • projects/media/src/time-range/define.ts
  • projects/media/src/time-range/index.ts
  • projects/media/src/time-range/time-range.css
  • projects/media/src/time-range/time-range.examples.ts
  • projects/media/src/time-range/time-range.test.axe.ts
  • projects/media/src/time-range/time-range.test.lighthouse.ts
  • projects/media/src/time-range/time-range.test.ssr.ts
  • projects/media/src/time-range/time-range.test.ts
  • projects/media/src/time-range/time-range.test.visual.ts
  • projects/media/src/time-range/time-range.ts
  • projects/media/src/volume-range/define.ts
  • projects/media/src/volume-range/index.ts
  • projects/media/src/volume-range/volume-range.css
  • projects/media/src/volume-range/volume-range.examples.ts
  • projects/media/src/volume-range/volume-range.test.axe.ts
  • projects/media/src/volume-range/volume-range.test.lighthouse.ts
  • projects/media/src/volume-range/volume-range.test.ssr.ts
  • projects/media/src/volume-range/volume-range.test.ts
  • projects/media/src/volume-range/volume-range.test.visual.ts
  • projects/media/src/volume-range/volume-range.ts
  • projects/media/tsconfig.lib.json
  • projects/media/vite.config.ts
  • projects/media/vitest.ssr.ts
  • projects/media/vitest.visual.html
  • projects/media/vitest.visual.ts
  • projects/site/src/_11ty/layouts/common.js
  • projects/site/src/_11ty/layouts/docs.css
  • projects/site/src/docs/media/controller.md
  • projects/site/src/docs/media/fullscreen-button.md
  • projects/site/src/docs/media/mute-button.md
  • projects/site/src/docs/media/pause-button.md
  • projects/site/src/docs/media/playback-rate-select.md
  • projects/site/src/docs/media/seek-button.md
  • projects/site/src/docs/media/time-range.md
  • projects/site/src/docs/media/volume-range.md

Comment thread projects/internals/metadata/src/services/api.service.test.ts
Comment on lines +358 to +386
it('should handle full-screen commands', async () => {
const requestFullscreen = vi.fn().mockResolvedValue(undefined);
const exitFullscreen = vi.fn().mockResolvedValue(undefined);
Object.defineProperty(controller, 'requestFullscreen', { value: requestFullscreen, configurable: true });
Object.defineProperty(globalThis.document, 'exitFullscreen', { value: exitFullscreen, configurable: true });

dispatchCommand(controller, mediaCommands.enterFullscreen);
expect(requestFullscreen).toHaveBeenCalled();

Object.defineProperty(globalThis.document, 'fullscreenElement', { value: controller, configurable: true });
const stateChange = untilEvent<MediaStateChangeEvent>(controller, mediaStateChange);
globalThis.document.dispatchEvent(new Event('fullscreenchange'));
expect((await stateChange).detail.fullscreen).toBe(true);
expect(controller.hasAttribute('fullscreen')).toBe(true);

dispatchCommand(controller, mediaCommands.exitFullscreen);
expect(exitFullscreen).toHaveBeenCalled();

Object.defineProperty(globalThis.document, 'fullscreenElement', {
value: document.createElement('div'),
configurable: true
});
dispatchCommand(controller, mediaCommands.exitFullscreen);
expect(exitFullscreen).toHaveBeenCalledTimes(1);

Object.defineProperty(globalThis.document, 'fullscreenElement', { value: null, configurable: true });
dispatchCommand(controller, mediaCommands.exitFullscreen);
expect(exitFullscreen).toHaveBeenCalledTimes(1);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Restore the document overrides after the full-screen test.

vi.restoreAllMocks() in afterEach (Line 49) only reverts vi.spyOn/vi.fn mocks; the raw Object.defineProperty writes to globalThis.document for fullscreenElement and exitFullscreen persist for the rest of the environment. fullscreenElement is left as a static null value descriptor, shadowing the native accessor, so any later test that relies on real full-screen state (or a reordering of tests in this file) silently sees a frozen value.

🧪 Suggested cleanup
   it('should handle full-screen commands', async () => {
+    const fullscreenElementDescriptor = Object.getOwnPropertyDescriptor(Document.prototype, 'fullscreenElement');
+    const exitFullscreenDescriptor = Object.getOwnPropertyDescriptor(Document.prototype, 'exitFullscreen');
     const requestFullscreen = vi.fn().mockResolvedValue(undefined);

then delete the ad-hoc own properties (and reinstate the captured descriptors when present) in a finally block or a dedicated afterEach.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/controller/controller.test.ts` around lines 358 - 386,
Restore the original descriptors for document.fullscreenElement and
document.exitFullscreen after the “should handle full-screen commands” test,
including deleting properties that were not originally present and reinstating
captured descriptors when they were. Use a finally block or dedicated cleanup so
restoration runs even if an assertion fails, without changing the test’s
fullscreen behavior.

Comment thread projects/media/src/controller/controller.ts Outdated
Comment thread projects/media/src/declarations.d.ts
Comment thread projects/media/src/internal/button-form-control-usage.test.ts
Comment thread projects/media/src/playback-rate-select/playback-rate-select.test.lighthouse.ts Outdated
Comment thread projects/media/vite.config.ts
Copilot AI review requested due to automatic review settings July 30, 2026 21:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

♻️ Duplicate comments (6)
projects/internals/metadata/src/services/api.service.test.ts (1)

44-53: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Restore the prototype spy in finally.

searchSpy.mockRestore() (Line 50) only runs if ApiService.search('nve-button') resolves without throwing. If it throws, the MiniSearch.prototype.search mock leaks into subsequent tests. This was already flagged on a previous commit and remains unaddressed.

🔧 Proposed fix
   it('should prioritize exact matches over fuzzy matches', async () => {
     const searchSpy = vi.spyOn(MiniSearch.prototype, 'search').mockReturnValue([
       { id: 'nve-button-group', terms: ['button'], queryTerms: ['button'], score: 2, match: {} },
       { id: 'nve-button', terms: ['button'], queryTerms: ['button'], score: 1, match: {} }
     ]);
-    const results = await ApiService.search('nve-button');
-    searchSpy.mockRestore();
-
-    expect(results[0]?.name).toBe('nve-button');
+    try {
+      const results = await ApiService.search('nve-button');
+      expect(results[0]?.name).toBe('nve-button');
+    } finally {
+      searchSpy.mockRestore();
+    }
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/internals/metadata/src/services/api.service.test.ts` around lines 44
- 53, Update the “should prioritize exact matches over fuzzy matches” test to
restore the MiniSearch.prototype.search spy in a finally block that always
executes, including when ApiService.search throws. Keep the existing search
invocation and assertion behavior unchanged.
projects/media/package.json (1)

329-349: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

test:visual still doesn't declare a build dependency on themes/styles.

vitest.visual.html @imports @nvidia-elements/themes/fonts/inter.css, themes/index.css, themes/dark.css, and @nvidia-elements/styles/typography.css/layout.css, all resolved from those packages' dist output. test:ssr (Line 373-399) tracks this correctly via "../themes/dist/**/*.css" in files and a non-cascading ../themes:build dependency, but test:visual's files/dependencies still omit tsconfig.json and any themes/styles glob or build dependency. Wireit only builds/fingerprints declared dependencies, so there's no guarantee ../themes:build (and ../styles:build) ran before test:visual, and no cache invalidation when that CSS changes — risking stale snapshots passing as valid.

This was already flagged on a previous commit of this PR and remains unaddressed in the current diff.

🔧 Proposed fix to align test:visual with test:ssr's dependency tracking
     "test:visual": {
       "command": "vitest run --config=vitest.visual.ts",
       "clean": false,
       "files": [
         "dist/**/*.js",
         "src/**/*.test.visual.ts",
         "vitest.visual.html",
-        "vitest.visual.ts"
+        "vitest.visual.ts",
+        "tsconfig.json",
+        "../themes/dist/**/*.css",
+        "../styles/dist/**/*.css"
       ],
       "output": [
         ".visual/*.png",
         "coverage/visual/**"
       ],
       "dependencies": [
         "../internals/vite:ci",
         {
           "script": "build",
           "cascade": false
+        },
+        {
+          "script": "../themes:build",
+          "cascade": false
+        },
+        {
+          "script": "../styles:build",
+          "cascade": false
         }
       ]
     },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/package.json` around lines 329 - 349, Update the test:visual
configuration to track the generated assets it imports: add tsconfig.json and
the relevant ../themes/dist/**/*.css and ../styles/dist/**/*.css globs to files,
and add non-cascading build dependencies for ../themes and ../styles alongside
the existing internals/vite dependency. Preserve the current visual test command
and outputs.
projects/media/src/internal/media-state.ts (1)

48-60: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

NaN comparison bug persists.

mediaStatesEqual still compares currentTime, duration, playbackRate, and volume with ===, so NaN !== NaN breaks equality for otherwise-identical states (e.g., an unknown duration), defeating change suppression. This mirrors a previously raised comment on this exact segment that hasn't been applied yet.

🐛 Proposed fix
 export function mediaStatesEqual(a: MediaState, b: MediaState) {
   return (
-    a.currentTime === b.currentTime &&
-    a.duration === b.duration &&
+    Object.is(a.currentTime, b.currentTime) &&
+    Object.is(a.duration, b.duration) &&
     a.ended === b.ended &&
     a.fullscreen === b.fullscreen &&
     a.muted === b.muted &&
     a.paused === b.paused &&
-    a.playbackRate === b.playbackRate &&
+    Object.is(a.playbackRate, b.playbackRate) &&
     a.seeking === b.seeking &&
-    a.volume === b.volume
+    Object.is(a.volume, b.volume)
   );
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/internal/media-state.ts` around lines 48 - 60, Update
mediaStatesEqual to compare currentTime, duration, playbackRate, and volume
using NaN-safe equality so identical states containing NaN are treated as equal.
Keep the existing strict comparisons for the remaining MediaState fields.
projects/media/src/time-range/time-range.ts (2)

123-133: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Stale slider state persists when the media target disappears.

#handleMediaState still returns early on null (Line 124-126) without resetting mediaDisabled, min/max, or valueAsNumber, so the previously-enabled slider and its last time value remain live and submittable after the target is gone. Previously flagged and still unresolved.

🐛 Proposed fix
   `#handleMediaState` = (mediaState: MediaState | null) => {
     if (!mediaState) {
+      this.mediaDisabled = true;
+      this.min = 0;
+      this.max = 0;
+      this.valueAsNumber = 0;
       return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/time-range/time-range.ts` around lines 123 - 133, Update
`#handleMediaState` to reset mediaDisabled, min, max, and valueAsNumber to their
disabled/empty state when mediaState is null before returning. Preserve the
existing duration and current-time handling for non-null media states.

49-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Command contract (command/commandfor/commandForElement) still duplicated.

Same properties are redeclared verbatim in playback-rate-select.ts; a shared mixin/base contract would keep these in sync. Previously flagged and still unresolved.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/time-range/time-range.ts` around lines 49 - 53, Extract
the shared command contract properties command, commandfor, and
commandForElement into a reusable mixin or base class, then have both the
time-range component and playback-rate-select reuse it instead of redeclaring
them. Preserve the existing Lit property metadata, defaults, and types so both
components expose the same contract.
projects/media/src/playback-rate-select/playback-rate-select.ts (1)

121-148: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Active playback rate outside rates still renders as unselected.

#handleMediaState (Lines 121-129) can set this.value to any finite positive playbackRate (e.g. 1.25), but getRateOptions (Lines 141-148) only renders options derived from this.rates (default [0.5, 1, 1.5, 2]). When the live rate isn't in that list, the native <select> has no matching <option>, so selectedIndex becomes -1 and the control appears unselected despite an active rate. This was flagged in a prior review and remains unresolved.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/playback-rate-select/playback-rate-select.ts` around lines
121 - 148, Update getRateOptions to include the current valid playback rate when
it is absent from the configured or default rates, so `#handleMediaState` values
always have a matching option. Preserve filtering, deduplication, and the
fallback to "1", and connect the options generation to the active rate without
changing invalid-rate behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@projects/media/package.json`:
- Around line 203-211: Add the existing test:lighthouse task to the
ci.dependencies array in projects/media/package.json, preserving the current
dependency entries and ordering conventions so Lighthouse checks run in the main
CI pipeline.

In `@projects/media/src/controller/controller.ts`:
- Line 14: Update MediaStateController dispatch calls to use the exported
mediaStateChange constant from ../internal/media-state.js instead of the raw
'media-state-change' literal, including the additional occurrence, and import
that constant alongside createMediaState and mediaStatesEqual.
- Around line 110-126: Restore the media reference when the controller
reconnects by calling `#syncMediaSlot` from connectedCallback after registering
the relevant listeners. Keep disconnectedCallback clearing the media and
preserve the existing firstUpdated and slotchange synchronization paths.
- Around line 167-185: In `#setMediaState`, perform
mediaStatesEqual(this.#mediaState, state) before all attribute mutations and
return immediately for identical snapshots. Keep `#currentTime` assigned
unconditionally before the equality check, while updating the remaining
attributes and dispatching media-state-change only when the state differs.
- Around line 232-236: Update `#seekToEnd` to retrieve the media through
`#getMedia`() before checking its duration, so an unslotted media element follows
the missing-media diagnostic path. Preserve the existing finite-duration seek
behavior and invalid-command-value warning for media whose duration is
unavailable.

In `@projects/media/src/fullscreen-button/fullscreen-button.ts`:
- Around line 87-91: Reset each media control’s bound state when its controller
emits null: in projects/media/src/fullscreen-button/fullscreen-button.ts:87-91,
update `#syncPressedState` to set this.pressed to false; in
projects/media/src/mute-button/mute-button.ts:95-99, update the corresponding
state sync to set this.checked to false; and in
projects/media/src/volume-range/volume-range.ts:124-132, set this.mediaDisabled
to true instead of returning with stale slider state.

In `@projects/media/src/internal/controllers/media-state.controller.ts`:
- Around line 26-46: Cache the command-target lookup input in
`#syncCommandTarget`, such as the resolved commandfor identifier or
commandForElement reference, and skip getCommandTarget when that input is
unchanged across hostConnected, hostUpdate, and hostUpdated calls. Invalidate or
refresh the cache when the relevant command-target input changes, while
preserving the existing `#commandTarget` identity check and synchronization timing
behavior.

In `@projects/media/src/internal/media-range.css`:
- Around line 4-20: In the :host rule, add a blank line between the final custom
property declaration (--track-progress) and the standard display declaration,
preserving all existing values and ordering.

In `@projects/media/src/mute-button/mute-button.test.axe.ts`:
- Around line 13-22: Make Axe test fixture cleanup exception-safe by wrapping
each test body in try/finally and moving removeFixture(fixture) into the finally
block. Apply this in projects/media/src/mute-button/mute-button.test.axe.ts
lines 13-22, projects/media/src/time-range/time-range.test.axe.ts lines 13-21,
and projects/media/src/volume-range/volume-range.test.axe.ts lines 13-21;
preserve the existing fixture creation, stability wait, and Axe assertions.

In `@projects/media/src/pause-button/pause-button.test.axe.ts`:
- Around line 18-22: Wrap the fixture setup, stability wait, axe execution, and
assertion in a try/finally block within the affected pause-button accessibility
test, and move removeFixture(fixture) into the finally block. Match the cleanup
structure used by controller.test.axe.ts so cleanup runs even when any preceding
operation throws.

In `@projects/media/src/volume-range/volume-range.examples.ts`:
- Around line 16-19: Remove the misleading value="0.8" attribute from the
nve-media-volume-range example inside the nve-media-controller, allowing the
controller state to synchronize with the media default.

In `@projects/site/src/docs/media/mute-button.md`:
- Line 29: Update the wording in the control-usage sentence to replace the
ambiguous “media review” phrase with “media or playback settings” or the
repository’s established domain term, while preserving the intended meaning and
technical writing style.

---

Duplicate comments:
In `@projects/internals/metadata/src/services/api.service.test.ts`:
- Around line 44-53: Update the “should prioritize exact matches over fuzzy
matches” test to restore the MiniSearch.prototype.search spy in a finally block
that always executes, including when ApiService.search throws. Keep the existing
search invocation and assertion behavior unchanged.

In `@projects/media/package.json`:
- Around line 329-349: Update the test:visual configuration to track the
generated assets it imports: add tsconfig.json and the relevant
../themes/dist/**/*.css and ../styles/dist/**/*.css globs to files, and add
non-cascading build dependencies for ../themes and ../styles alongside the
existing internals/vite dependency. Preserve the current visual test command and
outputs.

In `@projects/media/src/internal/media-state.ts`:
- Around line 48-60: Update mediaStatesEqual to compare currentTime, duration,
playbackRate, and volume using NaN-safe equality so identical states containing
NaN are treated as equal. Keep the existing strict comparisons for the remaining
MediaState fields.

In `@projects/media/src/playback-rate-select/playback-rate-select.ts`:
- Around line 121-148: Update getRateOptions to include the current valid
playback rate when it is absent from the configured or default rates, so
`#handleMediaState` values always have a matching option. Preserve filtering,
deduplication, and the fallback to "1", and connect the options generation to
the active rate without changing invalid-rate behavior.

In `@projects/media/src/time-range/time-range.ts`:
- Around line 123-133: Update `#handleMediaState` to reset mediaDisabled, min,
max, and valueAsNumber to their disabled/empty state when mediaState is null
before returning. Preserve the existing duration and current-time handling for
non-null media states.
- Around line 49-53: Extract the shared command contract properties command,
commandfor, and commandForElement into a reusable mixin or base class, then have
both the time-range component and playback-rate-select reuse it instead of
redeclaring them. Preserve the existing Lit property metadata, defaults, and
types so both components expose the same contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 4f9a73e1-3cd6-4d81-9feb-910d01ca68ca

📥 Commits

Reviewing files that changed from the base of the PR and between 8730c65 and 473c4b4.

⛔ Files ignored due to path filters (18)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • projects/media/.visual/media-controller.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-controller.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.png is excluded by !**/*.png
  • projects/site/public/static/video/particle.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (117)
  • knip.config.js
  • projects/internals/metadata/package.json
  • projects/internals/metadata/src/services/api.service.test.ts
  • projects/internals/metadata/src/tasks/api.utils.test.ts
  • projects/internals/metadata/src/tasks/api.utils.ts
  • projects/internals/metadata/static/adoption.json
  • projects/internals/metadata/static/releases.json
  • projects/internals/metadata/static/tests.json
  • projects/internals/tools/src/api/service.test.ts
  • projects/internals/tools/src/playground/utils.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.test.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.ts
  • projects/media/package.json
  • projects/media/src/controller/controller.css
  • projects/media/src/controller/controller.examples.ts
  • projects/media/src/controller/controller.test.axe.ts
  • projects/media/src/controller/controller.test.lighthouse.ts
  • projects/media/src/controller/controller.test.ssr.ts
  • projects/media/src/controller/controller.test.ts
  • projects/media/src/controller/controller.test.visual.ts
  • projects/media/src/controller/controller.ts
  • projects/media/src/controller/define.ts
  • projects/media/src/controller/index.ts
  • projects/media/src/declarations.d.ts
  • projects/media/src/fullscreen-button/define.ts
  • projects/media/src/fullscreen-button/fullscreen-button.css
  • projects/media/src/fullscreen-button/fullscreen-button.examples.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.axe.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.lighthouse.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ssr.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.visual.ts
  • projects/media/src/fullscreen-button/fullscreen-button.ts
  • projects/media/src/fullscreen-button/index.ts
  • projects/media/src/internal/button-form-control-usage.test.ts
  • projects/media/src/internal/command-target.ts
  • projects/media/src/internal/controllers/media-state.controller.test.ts
  • projects/media/src/internal/controllers/media-state.controller.ts
  • projects/media/src/internal/media-button.css
  • projects/media/src/internal/media-command.ts
  • projects/media/src/internal/media-range.css
  • projects/media/src/internal/media-state.ts
  • projects/media/src/mute-button/define.ts
  • projects/media/src/mute-button/index.ts
  • projects/media/src/mute-button/mute-button.css
  • projects/media/src/mute-button/mute-button.examples.ts
  • projects/media/src/mute-button/mute-button.test.axe.ts
  • projects/media/src/mute-button/mute-button.test.lighthouse.ts
  • projects/media/src/mute-button/mute-button.test.ssr.ts
  • projects/media/src/mute-button/mute-button.test.ts
  • projects/media/src/mute-button/mute-button.test.visual.ts
  • projects/media/src/mute-button/mute-button.ts
  • projects/media/src/pause-button/define.ts
  • projects/media/src/pause-button/index.ts
  • projects/media/src/pause-button/pause-button.css
  • projects/media/src/pause-button/pause-button.examples.ts
  • projects/media/src/pause-button/pause-button.test.axe.ts
  • projects/media/src/pause-button/pause-button.test.lighthouse.ts
  • projects/media/src/pause-button/pause-button.test.ssr.ts
  • projects/media/src/pause-button/pause-button.test.ts
  • projects/media/src/pause-button/pause-button.test.visual.ts
  • projects/media/src/pause-button/pause-button.ts
  • projects/media/src/playback-rate-select/define.ts
  • projects/media/src/playback-rate-select/index.ts
  • projects/media/src/playback-rate-select/playback-rate-select.css
  • projects/media/src/playback-rate-select/playback-rate-select.examples.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.axe.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.lighthouse.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ssr.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.visual.ts
  • projects/media/src/playback-rate-select/playback-rate-select.ts
  • projects/media/src/seek-button/define.ts
  • projects/media/src/seek-button/index.ts
  • projects/media/src/seek-button/seek-button.css
  • projects/media/src/seek-button/seek-button.examples.ts
  • projects/media/src/seek-button/seek-button.test.axe.ts
  • projects/media/src/seek-button/seek-button.test.lighthouse.ts
  • projects/media/src/seek-button/seek-button.test.ssr.ts
  • projects/media/src/seek-button/seek-button.test.ts
  • projects/media/src/seek-button/seek-button.test.visual.ts
  • projects/media/src/seek-button/seek-button.ts
  • projects/media/src/time-range/define.ts
  • projects/media/src/time-range/index.ts
  • projects/media/src/time-range/time-range.css
  • projects/media/src/time-range/time-range.examples.ts
  • projects/media/src/time-range/time-range.test.axe.ts
  • projects/media/src/time-range/time-range.test.lighthouse.ts
  • projects/media/src/time-range/time-range.test.ssr.ts
  • projects/media/src/time-range/time-range.test.ts
  • projects/media/src/time-range/time-range.test.visual.ts
  • projects/media/src/time-range/time-range.ts
  • projects/media/src/volume-range/define.ts
  • projects/media/src/volume-range/index.ts
  • projects/media/src/volume-range/volume-range.css
  • projects/media/src/volume-range/volume-range.examples.ts
  • projects/media/src/volume-range/volume-range.test.axe.ts
  • projects/media/src/volume-range/volume-range.test.lighthouse.ts
  • projects/media/src/volume-range/volume-range.test.ssr.ts
  • projects/media/src/volume-range/volume-range.test.ts
  • projects/media/src/volume-range/volume-range.test.visual.ts
  • projects/media/src/volume-range/volume-range.ts
  • projects/media/tsconfig.lib.json
  • projects/media/vite.config.ts
  • projects/media/vitest.ssr.ts
  • projects/media/vitest.visual.html
  • projects/media/vitest.visual.ts
  • projects/site/src/_11ty/layouts/common.js
  • projects/site/src/_11ty/layouts/docs.css
  • projects/site/src/docs/media/controller.md
  • projects/site/src/docs/media/fullscreen-button.md
  • projects/site/src/docs/media/mute-button.md
  • projects/site/src/docs/media/pause-button.md
  • projects/site/src/docs/media/playback-rate-select.md
  • projects/site/src/docs/media/seek-button.md
  • projects/site/src/docs/media/time-range.md
  • projects/site/src/docs/media/volume-range.md

Comment thread projects/media/package.json
Comment thread projects/media/src/controller/controller.ts
Comment thread projects/media/src/controller/controller.ts
Comment thread projects/media/src/controller/controller.ts
Comment thread projects/media/src/controller/controller.ts
Comment thread projects/media/src/internal/media-range.css
Comment thread projects/media/src/mute-button/mute-button.test.axe.ts
Comment thread projects/media/src/pause-button/pause-button.test.axe.ts
Comment thread projects/media/src/volume-range/volume-range.examples.ts
Comment thread projects/site/src/docs/media/mute-button.md
Copilot AI review requested due to automatic review settings July 30, 2026 23:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

♻️ Duplicate comments (5)
projects/media/src/playback-rate-select/playback-rate-select.ts (1)

121-130: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Render media rates not present in rates (unresolved from prior review).

#handleMediaState can set this.value to a valid rate (e.g., 1.25) that isn't rendered as an <option> by getRateOptions (Lines 141-148), leaving the select appearing unselected despite an active playback rate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/playback-rate-select/playback-rate-select.ts` around lines
121 - 130, Update `#handleMediaState` so a valid playbackRate is only assigned to
this.value when it exists in the rates used by getRateOptions; otherwise
preserve the existing selectable value or apply the established fallback rather
than setting an unrendered option value.
projects/media/src/seek-button/seek-button.ts (1)

44-44: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate action before deriving seek command (unresolved from prior review).

action is a reflected attribute; an out-of-union runtime value causes getSeekCommand (Lines 139-146) to return undefined for this.command, while the icon/label silently fall back to 'start'/null. This still isn't normalized or rejected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/seek-button/seek-button.ts` at line 44, Validate and
normalize the reflected action property before it is used by getSeekCommand,
icon, or label derivation. Restrict runtime values to the supported
MediaSeekAction union, apply the established default for invalid values, and
ensure command, icon, and label all use the normalized action rather than
silently accepting an invalid attribute.
projects/media/src/time-range/time-range.ts (1)

123-133: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reset the range when its media target disappears (unresolved from prior review).

Returning on null preserves the prior enabled slider and its stale form value — the range can still display/submit the last target's time after the target is gone.

🐛 Proposed fix
   `#handleMediaState` = (mediaState: MediaState | null) => {
     if (!mediaState) {
+      this.mediaDisabled = true;
+      this.min = 0;
+      this.max = 0;
+      this.valueAsNumber = 0;
       return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/time-range/time-range.ts` around lines 123 - 133, Update
`#handleMediaState` so a null mediaState resets the range to its disabled, empty
state instead of returning with the previous target’s values. Clear or reset
mediaDisabled, min, max, and valueAsNumber consistently with the existing
duration-invalid state, while preserving the current processing for non-null
media states.
projects/media/src/seek-button/seek-button.test.ts (1)

85-98: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Ensure commandFixture cleanup runs even on assertion failure (unresolved from prior review).

removeFixture(commandFixture) only runs after the assertions succeed; a failed assertion leaves DOM state behind for later tests.

♻️ Proposed fix
   it('should preserve an explicit command attribute', async () => {
     const commandFixture = await createFixture(html`
       <nve-media-seek-button commandfor="seek-target" command="--seek" action="end"></nve-media-seek-button>
       <div id="seek-target"></div>
     `);
-    const explicitButton = getElement<MediaSeekButton>(commandFixture, MediaSeekButton.metadata.tag);
-    const explicitTarget = getElement(commandFixture, '`#seek-target`');
-    await elementIsStable(explicitButton);
-
-    const event = untilEvent<Event & { command: string }>(explicitTarget, 'command');
-    await emulateClick(explicitButton);
-    expect((await event).command).toBe(mediaCommands.seek);
-    removeFixture(commandFixture);
+    try {
+      const explicitButton = getElement<MediaSeekButton>(commandFixture, MediaSeekButton.metadata.tag);
+      const explicitTarget = getElement(commandFixture, '`#seek-target`');
+      await elementIsStable(explicitButton);
+
+      const event = untilEvent<Event & { command: string }>(explicitTarget, 'command');
+      await emulateClick(explicitButton);
+      expect((await event).command).toBe(mediaCommands.seek);
+    } finally {
+      removeFixture(commandFixture);
+    }
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/seek-button/seek-button.test.ts` around lines 85 - 98,
Ensure the test body in “should preserve an explicit command attribute” always
removes commandFixture by moving cleanup into a finally-style path that runs
whether event handling or the command assertion succeeds or fails. Preserve the
existing fixture creation and assertion behavior while guaranteeing
removeFixture(commandFixture) executes on every exit path.
projects/media/src/pause-button/pause-button.test.axe.ts (1)

13-22: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Protect fixture cleanup with try/finally.

A failed stability check, axe scan, or assertion skips cleanup and can contaminate later tests.

  • projects/media/src/pause-button/pause-button.test.axe.ts#L13-L22: move removeFixture(fixture) into a finally block.
  • projects/media/src/seek-button/seek-button.test.axe.ts#L13-L23: move removeFixture(fixture) into the same try/finally structure.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@projects/media/src/pause-button/pause-button.test.axe.ts` around lines 13 -
22, Protect fixture cleanup in the axe tests by wrapping the stability check,
axe scan, and assertions in try/finally, with removeFixture(fixture) in finally.
Apply this structure in projects/media/src/pause-button/pause-button.test.axe.ts
lines 13-22 and projects/media/src/seek-button/seek-button.test.axe.ts lines
13-23, preserving each test’s existing setup and assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@projects/internals/metadata/src/tasks/api.utils.test.ts`:
- Around line 23-25: Update the smoke test around getApi to invoke and await
getApi(), then assert the returned metadata collections; alternatively remove
the redundant test. Do not merely check that the imported getApi binding is
defined.

In `@projects/internals/tools/src/playground/utils.ts`:
- Around line 292-293: Add test coverage in the “all NVE packages” import-map
test for both `@nvidia-elements/media` and `@nvidia-elements/media/` entries,
validating their expected CDN mappings including the trailing slash. Ensure the
test fails if either entry is removed or malformed.

In `@projects/media/src/internal/button-form-control-usage.test.ts`:
- Around line 111-121: Await both emulateClick calls in the disabled and
readOnly button test before asserting count, matching the existing async usage
of emulateClick elsewhere. Keep the elementIsStable waits and the final
expect(count).toBe(0) unchanged.

In `@projects/media/src/internal/controllers/media-state.controller.ts`:
- Around line 45-55: Update `#syncCommandTarget` so `#commandTargetInput` is
assigned only after getCommandTarget successfully resolves a target; when
resolution returns null, leave the cached input unchanged so later host updates
retry resolution. Preserve the existing early return for unchanged inputs once a
valid target has been resolved.

In `@projects/media/src/internal/media-button.css`:
- Around line 147-150: Update the readonly inline host rule’s --cursor value to
the valid default cursor keyword, matching the existing :host([readonly])
behavior. Preserve the --text-decoration declaration and the surrounding
selector.

In `@projects/media/src/pause-button/pause-button.ts`:
- Around line 65-75: Guard the `#onClick` handler in the pause button so it does
not call toggle when the button is disabled or readonly. Preserve normal
toggling for enabled, editable buttons and keep the existing listener lifecycle
in connectedCallback and disconnectedCallback unchanged.

In `@projects/media/src/playback-rate-select/playback-rate-select.examples.ts`:
- Around line 11-13: Update the JSDoc `@summary` for the playback-rate select
example to clearly state that the form submits the selected playback speed and
the control sends media commands, following the repository’s technical writing
style.

In `@projects/media/src/playback-rate-select/playback-rate-select.test.axe.ts`:
- Around line 13-22: Update the fixture lifecycle in the axe test around
createFixture: place elementIsStable and the axe assertion inside a try block,
and move removeFixture(fixture) into finally so cleanup runs even when
assertions or runAxe reject. Preserve the existing test setup and
zero-violations assertion.

In `@projects/media/src/playback-rate-select/playback-rate-select.test.ts`:
- Around line 128-160: Wrap the form interaction and assertions in the “should
submit selected form data” test with a try/finally block, ensuring
removeFixture(formFixture) executes in the finally clause even when an assertion
or callback fails. Keep the existing test setup and expectations unchanged.

In `@projects/media/src/volume-range/volume-range.test.ts`:
- Around line 110-121: Wrap each affected test body, including the numeric
form-data test and the following test, in a try/finally block so
removeFixture(formFixture) executes regardless of assertion or setup failure.
Keep the existing assertions and fixture setup unchanged, placing cleanup in
each test’s finally block.

---

Duplicate comments:
In `@projects/media/src/pause-button/pause-button.test.axe.ts`:
- Around line 13-22: Protect fixture cleanup in the axe tests by wrapping the
stability check, axe scan, and assertions in try/finally, with
removeFixture(fixture) in finally. Apply this structure in
projects/media/src/pause-button/pause-button.test.axe.ts lines 13-22 and
projects/media/src/seek-button/seek-button.test.axe.ts lines 13-23, preserving
each test’s existing setup and assertions.

In `@projects/media/src/playback-rate-select/playback-rate-select.ts`:
- Around line 121-130: Update `#handleMediaState` so a valid playbackRate is only
assigned to this.value when it exists in the rates used by getRateOptions;
otherwise preserve the existing selectable value or apply the established
fallback rather than setting an unrendered option value.

In `@projects/media/src/seek-button/seek-button.test.ts`:
- Around line 85-98: Ensure the test body in “should preserve an explicit
command attribute” always removes commandFixture by moving cleanup into a
finally-style path that runs whether event handling or the command assertion
succeeds or fails. Preserve the existing fixture creation and assertion behavior
while guaranteeing removeFixture(commandFixture) executes on every exit path.

In `@projects/media/src/seek-button/seek-button.ts`:
- Line 44: Validate and normalize the reflected action property before it is
used by getSeekCommand, icon, or label derivation. Restrict runtime values to
the supported MediaSeekAction union, apply the established default for invalid
values, and ensure command, icon, and label all use the normalized action rather
than silently accepting an invalid attribute.

In `@projects/media/src/time-range/time-range.ts`:
- Around line 123-133: Update `#handleMediaState` so a null mediaState resets the
range to its disabled, empty state instead of returning with the previous
target’s values. Clear or reset mediaDisabled, min, max, and valueAsNumber
consistently with the existing duration-invalid state, while preserving the
current processing for non-null media states.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 67283f05-c089-45c5-aae5-9d1b19aaefb5

📥 Commits

Reviewing files that changed from the base of the PR and between 473c4b4 and 8edfb69.

⛔ Files ignored due to path filters (18)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • projects/media/.visual/media-controller.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-controller.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-fullscreen-button.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-mute-button.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-pause-button.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-playback-rate-select.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-seek-button.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-time-range.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.dark.png is excluded by !**/*.png
  • projects/media/.visual/media-volume-range.png is excluded by !**/*.png
  • projects/site/public/static/video/particle.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (117)
  • knip.config.js
  • projects/internals/metadata/package.json
  • projects/internals/metadata/src/services/api.service.test.ts
  • projects/internals/metadata/src/tasks/api.utils.test.ts
  • projects/internals/metadata/src/tasks/api.utils.ts
  • projects/internals/metadata/static/adoption.json
  • projects/internals/metadata/static/releases.json
  • projects/internals/metadata/static/tests.json
  • projects/internals/tools/src/api/service.test.ts
  • projects/internals/tools/src/playground/utils.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.test.ts
  • projects/lint/src/eslint/rules/no-invalid-invoker-triggers.ts
  • projects/media/package.json
  • projects/media/src/controller/controller.css
  • projects/media/src/controller/controller.examples.ts
  • projects/media/src/controller/controller.test.axe.ts
  • projects/media/src/controller/controller.test.lighthouse.ts
  • projects/media/src/controller/controller.test.ssr.ts
  • projects/media/src/controller/controller.test.ts
  • projects/media/src/controller/controller.test.visual.ts
  • projects/media/src/controller/controller.ts
  • projects/media/src/controller/define.ts
  • projects/media/src/controller/index.ts
  • projects/media/src/declarations.d.ts
  • projects/media/src/fullscreen-button/define.ts
  • projects/media/src/fullscreen-button/fullscreen-button.css
  • projects/media/src/fullscreen-button/fullscreen-button.examples.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.axe.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.lighthouse.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ssr.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.ts
  • projects/media/src/fullscreen-button/fullscreen-button.test.visual.ts
  • projects/media/src/fullscreen-button/fullscreen-button.ts
  • projects/media/src/fullscreen-button/index.ts
  • projects/media/src/internal/button-form-control-usage.test.ts
  • projects/media/src/internal/command-target.ts
  • projects/media/src/internal/controllers/media-state.controller.test.ts
  • projects/media/src/internal/controllers/media-state.controller.ts
  • projects/media/src/internal/media-button.css
  • projects/media/src/internal/media-command.ts
  • projects/media/src/internal/media-range.css
  • projects/media/src/internal/media-state.ts
  • projects/media/src/mute-button/define.ts
  • projects/media/src/mute-button/index.ts
  • projects/media/src/mute-button/mute-button.css
  • projects/media/src/mute-button/mute-button.examples.ts
  • projects/media/src/mute-button/mute-button.test.axe.ts
  • projects/media/src/mute-button/mute-button.test.lighthouse.ts
  • projects/media/src/mute-button/mute-button.test.ssr.ts
  • projects/media/src/mute-button/mute-button.test.ts
  • projects/media/src/mute-button/mute-button.test.visual.ts
  • projects/media/src/mute-button/mute-button.ts
  • projects/media/src/pause-button/define.ts
  • projects/media/src/pause-button/index.ts
  • projects/media/src/pause-button/pause-button.css
  • projects/media/src/pause-button/pause-button.examples.ts
  • projects/media/src/pause-button/pause-button.test.axe.ts
  • projects/media/src/pause-button/pause-button.test.lighthouse.ts
  • projects/media/src/pause-button/pause-button.test.ssr.ts
  • projects/media/src/pause-button/pause-button.test.ts
  • projects/media/src/pause-button/pause-button.test.visual.ts
  • projects/media/src/pause-button/pause-button.ts
  • projects/media/src/playback-rate-select/define.ts
  • projects/media/src/playback-rate-select/index.ts
  • projects/media/src/playback-rate-select/playback-rate-select.css
  • projects/media/src/playback-rate-select/playback-rate-select.examples.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.axe.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.lighthouse.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ssr.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.ts
  • projects/media/src/playback-rate-select/playback-rate-select.test.visual.ts
  • projects/media/src/playback-rate-select/playback-rate-select.ts
  • projects/media/src/seek-button/define.ts
  • projects/media/src/seek-button/index.ts
  • projects/media/src/seek-button/seek-button.css
  • projects/media/src/seek-button/seek-button.examples.ts
  • projects/media/src/seek-button/seek-button.test.axe.ts
  • projects/media/src/seek-button/seek-button.test.lighthouse.ts
  • projects/media/src/seek-button/seek-button.test.ssr.ts
  • projects/media/src/seek-button/seek-button.test.ts
  • projects/media/src/seek-button/seek-button.test.visual.ts
  • projects/media/src/seek-button/seek-button.ts
  • projects/media/src/time-range/define.ts
  • projects/media/src/time-range/index.ts
  • projects/media/src/time-range/time-range.css
  • projects/media/src/time-range/time-range.examples.ts
  • projects/media/src/time-range/time-range.test.axe.ts
  • projects/media/src/time-range/time-range.test.lighthouse.ts
  • projects/media/src/time-range/time-range.test.ssr.ts
  • projects/media/src/time-range/time-range.test.ts
  • projects/media/src/time-range/time-range.test.visual.ts
  • projects/media/src/time-range/time-range.ts
  • projects/media/src/volume-range/define.ts
  • projects/media/src/volume-range/index.ts
  • projects/media/src/volume-range/volume-range.css
  • projects/media/src/volume-range/volume-range.examples.ts
  • projects/media/src/volume-range/volume-range.test.axe.ts
  • projects/media/src/volume-range/volume-range.test.lighthouse.ts
  • projects/media/src/volume-range/volume-range.test.ssr.ts
  • projects/media/src/volume-range/volume-range.test.ts
  • projects/media/src/volume-range/volume-range.test.visual.ts
  • projects/media/src/volume-range/volume-range.ts
  • projects/media/tsconfig.lib.json
  • projects/media/vite.config.ts
  • projects/media/vitest.ssr.ts
  • projects/media/vitest.visual.html
  • projects/media/vitest.visual.ts
  • projects/site/src/_11ty/layouts/common.js
  • projects/site/src/_11ty/layouts/docs.css
  • projects/site/src/docs/media/controller.md
  • projects/site/src/docs/media/fullscreen-button.md
  • projects/site/src/docs/media/mute-button.md
  • projects/site/src/docs/media/pause-button.md
  • projects/site/src/docs/media/playback-rate-select.md
  • projects/site/src/docs/media/seek-button.md
  • projects/site/src/docs/media/time-range.md
  • projects/site/src/docs/media/volume-range.md

Comment thread projects/internals/metadata/src/tasks/api.utils.test.ts
Comment thread projects/internals/tools/src/playground/utils.ts
Comment thread projects/media/src/internal/button-form-control-usage.test.ts
Comment thread projects/media/src/internal/controllers/media-state.controller.ts
Comment thread projects/media/src/internal/media-button.css
Comment thread projects/media/src/pause-button/pause-button.ts
Comment thread projects/media/src/volume-range/volume-range.test.ts
Signed-off-by: Cory Rylan <crylan@nvidia.com>
Copilot AI review requested due to automatic review settings July 31, 2026 02:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants