Skip to content

Adopt Biome and reformat the frontend - #215

Merged
kristofferR merged 2 commits into
mainfrom
chore/biome-formatting
Jul 25, 2026
Merged

Adopt Biome and reformat the frontend#215
kristofferR merged 2 commits into
mainfrom
chore/biome-formatting

Conversation

@kristofferR

@kristofferR kristofferR commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Mechanical prep, split out of #214 so that PR's real diff is reviewable.

The project had no frontend linter or formatter, so style had drifted file by
file. This adds Biome with the config used across my other repos, and runs it
once over the existing sources.

Everything here is mechanical: line rewrapping to the 100-column width, and
import sorting. No behaviour changes.

Why separate

A formatting pass touches almost every file, which buries a real diff. It also
pushed #214 to 121 files — past CodeRabbit's 100-file limit, so it declined to
review that PR at all:

Review skipped — Too many files! This PR contains 119 files, which is 19 over
the limit of 100.

Landing the formatting on its own drops #214 to roughly 30 files, so the
substantive changes there can actually be reviewed.

Validation

bun run typecheck, bun run test:frontend (90 pass), and bun run build all
pass unchanged. The lint/format scripts are added here; CI enforcement of them
lands with #214, together with the clippy and rustfmt gates.

Review with whitespace hidden — GitHub's ?w=1, or git diff -w.

Summary by CodeRabbit

  • New Features
    • Added project-wide linting and formatting commands.
  • Refactor
    • Standardized code formatting, imports, and styling across the application.
    • Removed advanced ffmpeg/ffprobe timeout settings from the Settings panel.
  • Tests
    • Updated test formatting to align with the new project style.
  • Chores
    • Added automated code-quality configuration, including CSS and accessibility checks.

The project had no frontend linter or formatter, so style drifted file by
file. This adds Biome with the config the rest of my repos use, and runs
it once over the existing sources.

Everything here is mechanical: line rewrapping to the 100-column width and
import sorting. No behaviour changes, and typecheck, tests, and the
production build all pass unchanged.

Kept separate from the work that follows because a formatting pass touches
almost every file, which buries a real diff — and pushes a combined PR past
CodeRabbit's 100-file review limit.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 05d978b9-720d-4b72-a8fc-1bba202c5df1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Biome configuration and npm scripts were added. The repository’s TypeScript, TSX, CSS, store, hook, library, and test files were reformatted, with existing behavior generally preserved. The Advanced settings UI also removes the ffmpeg/ffprobe subsection.

Changes

Biome adoption and application formatting

Layer / File(s) Summary
Biome configuration and scripts
biome.jsonc, package.json, scripts/benchmark-ui-filter.ts
Adds Biome formatting/linting configuration, npm lint and format scripts, and the Biome development dependency.
Application and settings flow
src/App.tsx, src/main.tsx, src/components/SettingsPanel.tsx, src/store/*
Reformats application orchestration and store wiring; removes the ffmpeg/ffprobe subsection from Advanced settings.
UI components and styling
src/components/*, src/index.css
Applies formatting, import ordering, JSX reflow, and CSS declaration normalization across the UI.
Hooks and libraries
src/hooks/*, src/lib/*
Reformats scan, playback, playlist, update-check, and utility implementations without stated behavioral changes.
Tests
tests/*
Reformats existing fixtures, imports, calls, and assertions without changing expected results.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Poem

A bunny hops through Biome’s lines,
Two-space trails and tidy signs.
Scan and stream, now neatly dressed,
Old tests hop along the rest.
“ffmpeg’s nook is gone,” I sing—
Clean code makes my whiskers spring!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding Biome and reformatting the frontend codebase.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/biome-formatting

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 65450e18a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/SettingsPanel.tsx (1)

1229-1318: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Ffmpeg/ffprobe settings become unreachable in the UI.

The Advanced tab's ffmpeg/ffprobe subsection (inputs for ffprobe_timeout_secs and ffmpeg_bitrate_timeout_secs) is removed, but AppSettings and ScanPresetConfig still define both fields, and applyScanPresetConfig still copies them from presets. With no remaining UI control, users can no longer directly adjust these timeouts — they're now frozen at whatever value a loaded preset (or the initial default) provides. This is a real behavior change bundled into a PR described as "mechanical... no behavior changes." Please confirm this removal is intentional and that there's an alternate path (e.g., preset-only configuration) for users who need to tune these timeouts.

🤖 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 `@src/components/SettingsPanel.tsx` around lines 1229 - 1318, Restore the
Advanced tab’s ffmpeg/ffprobe subsection containing the `ffprobe_timeout_secs`
and `ffmpeg_bitrate_timeout_secs` inputs so users can continue adjusting both
settings directly. Keep their existing `updateSetting` handlers and validation
bounds, and preserve the surrounding `AppSettings`, `ScanPresetConfig`, and
`applyScanPresetConfig` 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 `@biome.jsonc`:
- Around line 3-6: Update the Biome files.includes configuration to exclude
shell scripts from scanning by narrowing the scripts/** pattern to
supported-language files such as scripts/**/*.ts, while preserving the existing
src/**, tests/**, worktree exclusion, and dist exclusion entries.

In `@src/components/HistoryPanel.tsx`:
- Around line 46-53: Update the dialog implementations in
src/components/HistoryPanel.tsx lines 46-53 and
src/components/KeyboardShortcutsDialog.tsx lines 77-83 to use native dialog
behavior with focus trapping via showModal()/close(), while preserving existing
close behavior. In HistoryPanel, add Escape handling and make the backdrop
dismissal keyboard accessible; in KeyboardShortcutsDialog, retain its existing
Escape listener and add a keyboard-accessible backdrop handler.

In `@src/components/SavedPlaylistEditorDialog.tsx`:
- Line 164: Update the Display Name and Username label/input pairs in
SavedPlaylistEditorDialog to use matching htmlFor and id attributes, following
the existing Xtream password field pattern; keep each label associated with its
adjacent input.

In `@src/components/SettingsPanel.tsx`:
- Line 743: Associate every affected label with its form control in
SettingsPanel by adding a unique id to each preset select and timeout,
concurrency, retries, and threshold input, then setting the corresponding label
htmlFor to that id. Apply the same pairing consistently at the referenced
control blocks without changing their existing behavior.

In `@src/components/ThumbnailPanel.tsx`:
- Around line 620-637: Update the theater-mode toggle button in the
ThumbnailPanel JSX to add an aria-label that matches its current action, using
the same conditional “Exit fullscreen”/“Fullscreen” text as the title while
preserving the existing icon and click behavior.

---

Outside diff comments:
In `@src/components/SettingsPanel.tsx`:
- Around line 1229-1318: Restore the Advanced tab’s ffmpeg/ffprobe subsection
containing the `ffprobe_timeout_secs` and `ffmpeg_bitrate_timeout_secs` inputs
so users can continue adjusting both settings directly. Keep their existing
`updateSetting` handlers and validation bounds, and preserve the surrounding
`AppSettings`, `ScanPresetConfig`, and `applyScanPresetConfig` behavior.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 286ed6c3-12dd-4102-adbc-787aa4fca6ed

📥 Commits

Reviewing files that changed from the base of the PR and between a47f71d and 65450e1.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (79)
  • biome.jsonc
  • package.json
  • scripts/benchmark-ui-filter.ts
  • src/App.tsx
  • src/SettingsWindow.tsx
  • src/components/AppBanners.tsx
  • src/components/CastMenu.tsx
  • src/components/ChannelRow.tsx
  • src/components/ChannelTable.tsx
  • src/components/ErrorBoundary.tsx
  • src/components/ExportMenu.tsx
  • src/components/FilterBar.tsx
  • src/components/HistoryPanel.tsx
  • src/components/KeyboardShortcutsDialog.tsx
  • src/components/LogWindowContent.tsx
  • src/components/OpenSourceDialog.tsx
  • src/components/PasswordField.tsx
  • src/components/PlaylistReportPanel.tsx
  • src/components/ProgressBar.tsx
  • src/components/SFSymbols.tsx
  • src/components/SavedPlaylistEditorDialog.tsx
  • src/components/SavedPlaylistsDialog.tsx
  • src/components/SettingsPanel.tsx
  • src/components/StartScreen.tsx
  • src/components/StatsPanel.tsx
  • src/components/StatusBadge.tsx
  • src/components/StreamPlayer.tsx
  • src/components/ThumbnailPanel.tsx
  • src/components/Toolbar.tsx
  • src/hooks/useChromecast.ts
  • src/hooks/useMenuEventBridge.ts
  • src/hooks/usePlaylistSources.ts
  • src/hooks/useScan.helpers.ts
  • src/hooks/useScan.ts
  • src/hooks/useStreamPlayer.ts
  • src/hooks/useUpdateCheck.ts
  • src/index.css
  • src/lib/cast.ts
  • src/lib/channelResults.ts
  • src/lib/duplicates.ts
  • src/lib/errors.ts
  • src/lib/extinf.ts
  • src/lib/filters.ts
  • src/lib/format.ts
  • src/lib/haptics.ts
  • src/lib/languageDistribution.ts
  • src/lib/logger.ts
  • src/lib/logoCache.ts
  • src/lib/perf.ts
  • src/lib/playback.ts
  • src/lib/playlistReportVisibility.ts
  • src/lib/recentPlaylists.ts
  • src/lib/runtimeMonitor.ts
  • src/lib/savedPlaylists.ts
  • src/lib/scanState.ts
  • src/lib/shortcuts.ts
  • src/lib/sourceFilter.ts
  • src/lib/tableColumns.ts
  • src/lib/tauri.ts
  • src/lib/thumbnailState.ts
  • src/lib/workerSupport.ts
  • src/main.tsx
  • src/store/slices/settingsSlice.ts
  • src/store/slices/uiSlice.ts
  • src/store/store.ts
  • src/store/types.ts
  • tests/channelResults.test.ts
  • tests/duplicates.test.ts
  • tests/errors.test.ts
  • tests/exportScope.test.ts
  • tests/filters.logic.test.ts
  • tests/format.test.ts
  • tests/playlistReportVisibility.test.ts
  • tests/savedPlaylists.test.ts
  • tests/scanErrorEvents.test.ts
  • tests/sourceFilter.test.ts
  • tests/tableColumns.test.ts
  • tests/useScan.helpers.test.ts
  • tests/useStreamPlayer.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Define agent interfaces and implementations in TypeScript for agent systems
Use clear naming conventions for agent classes and methods
Document agent behavior and responsibilities in comments or docstrings
Implement error handling in agent methods

Files:

  • tests/useScan.helpers.test.ts
  • tests/format.test.ts
  • src/lib/scanState.ts
  • scripts/benchmark-ui-filter.ts
  • tests/channelResults.test.ts
  • tests/duplicates.test.ts
  • tests/tableColumns.test.ts
  • src/components/StatusBadge.tsx
  • src/lib/languageDistribution.ts
  • tests/playlistReportVisibility.test.ts
  • tests/errors.test.ts
  • src/hooks/useScan.helpers.ts
  • src/components/PasswordField.tsx
  • tests/scanErrorEvents.test.ts
  • src/lib/logoCache.ts
  • src/components/ErrorBoundary.tsx
  • src/lib/duplicates.ts
  • src/lib/thumbnailState.ts
  • tests/exportScope.test.ts
  • src/lib/format.ts
  • src/lib/cast.ts
  • src/components/KeyboardShortcutsDialog.tsx
  • src/lib/haptics.ts
  • src/components/SavedPlaylistsDialog.tsx
  • src/store/slices/uiSlice.ts
  • src/lib/channelResults.ts
  • src/lib/savedPlaylists.ts
  • src/lib/perf.ts
  • tests/filters.logic.test.ts
  • tests/savedPlaylists.test.ts
  • src/store/types.ts
  • src/lib/workerSupport.ts
  • src/lib/recentPlaylists.ts
  • src/SettingsWindow.tsx
  • src/lib/logger.ts
  • src/lib/tableColumns.ts
  • src/main.tsx
  • src/components/FilterBar.tsx
  • src/components/ProgressBar.tsx
  • src/store/store.ts
  • src/lib/shortcuts.ts
  • src/components/ExportMenu.tsx
  • src/components/SavedPlaylistEditorDialog.tsx
  • src/lib/extinf.ts
  • src/hooks/useChromecast.ts
  • src/components/HistoryPanel.tsx
  • src/lib/filters.ts
  • src/components/AppBanners.tsx
  • src/store/slices/settingsSlice.ts
  • tests/sourceFilter.test.ts
  • src/components/StatsPanel.tsx
  • src/components/ChannelRow.tsx
  • src/components/SFSymbols.tsx
  • src/lib/playback.ts
  • src/components/CastMenu.tsx
  • src/lib/sourceFilter.ts
  • src/hooks/useMenuEventBridge.ts
  • src/components/StreamPlayer.tsx
  • src/components/StartScreen.tsx
  • src/lib/errors.ts
  • src/hooks/useUpdateCheck.ts
  • tests/useStreamPlayer.test.ts
  • src/lib/tauri.ts
  • src/components/LogWindowContent.tsx
  • src/lib/playlistReportVisibility.ts
  • src/lib/runtimeMonitor.ts
  • src/components/OpenSourceDialog.tsx
  • src/components/ThumbnailPanel.tsx
  • src/hooks/useScan.ts
  • src/components/PlaylistReportPanel.tsx
  • src/hooks/useStreamPlayer.ts
  • src/components/Toolbar.tsx
  • src/components/ChannelTable.tsx
  • src/components/SettingsPanel.tsx
  • src/hooks/usePlaylistSources.ts
  • src/App.tsx
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use strict TypeScript with no unused locals or parameters.

Files:

  • src/lib/scanState.ts
  • src/components/StatusBadge.tsx
  • src/lib/languageDistribution.ts
  • src/hooks/useScan.helpers.ts
  • src/components/PasswordField.tsx
  • src/lib/logoCache.ts
  • src/components/ErrorBoundary.tsx
  • src/lib/duplicates.ts
  • src/lib/thumbnailState.ts
  • src/lib/format.ts
  • src/lib/cast.ts
  • src/components/KeyboardShortcutsDialog.tsx
  • src/lib/haptics.ts
  • src/components/SavedPlaylistsDialog.tsx
  • src/store/slices/uiSlice.ts
  • src/lib/channelResults.ts
  • src/lib/savedPlaylists.ts
  • src/lib/perf.ts
  • src/store/types.ts
  • src/lib/workerSupport.ts
  • src/lib/recentPlaylists.ts
  • src/SettingsWindow.tsx
  • src/lib/logger.ts
  • src/lib/tableColumns.ts
  • src/main.tsx
  • src/components/FilterBar.tsx
  • src/components/ProgressBar.tsx
  • src/store/store.ts
  • src/lib/shortcuts.ts
  • src/components/ExportMenu.tsx
  • src/components/SavedPlaylistEditorDialog.tsx
  • src/lib/extinf.ts
  • src/hooks/useChromecast.ts
  • src/components/HistoryPanel.tsx
  • src/lib/filters.ts
  • src/components/AppBanners.tsx
  • src/store/slices/settingsSlice.ts
  • src/components/StatsPanel.tsx
  • src/components/ChannelRow.tsx
  • src/components/SFSymbols.tsx
  • src/lib/playback.ts
  • src/components/CastMenu.tsx
  • src/lib/sourceFilter.ts
  • src/hooks/useMenuEventBridge.ts
  • src/components/StreamPlayer.tsx
  • src/components/StartScreen.tsx
  • src/lib/errors.ts
  • src/hooks/useUpdateCheck.ts
  • src/lib/tauri.ts
  • src/components/LogWindowContent.tsx
  • src/lib/playlistReportVisibility.ts
  • src/lib/runtimeMonitor.ts
  • src/components/OpenSourceDialog.tsx
  • src/components/ThumbnailPanel.tsx
  • src/hooks/useScan.ts
  • src/components/PlaylistReportPanel.tsx
  • src/hooks/useStreamPlayer.ts
  • src/components/Toolbar.tsx
  • src/components/ChannelTable.tsx
  • src/components/SettingsPanel.tsx
  • src/hooks/usePlaylistSources.ts
  • src/App.tsx
src/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Implement components as functional React components using hooks, and use Tailwind CSS for styling instead of CSS-in-JS.

Files:

  • src/components/StatusBadge.tsx
  • src/components/PasswordField.tsx
  • src/components/ErrorBoundary.tsx
  • src/components/KeyboardShortcutsDialog.tsx
  • src/components/SavedPlaylistsDialog.tsx
  • src/components/FilterBar.tsx
  • src/components/ProgressBar.tsx
  • src/components/ExportMenu.tsx
  • src/components/SavedPlaylistEditorDialog.tsx
  • src/components/HistoryPanel.tsx
  • src/components/AppBanners.tsx
  • src/components/StatsPanel.tsx
  • src/components/ChannelRow.tsx
  • src/components/SFSymbols.tsx
  • src/components/CastMenu.tsx
  • src/components/StreamPlayer.tsx
  • src/components/StartScreen.tsx
  • src/components/LogWindowContent.tsx
  • src/components/OpenSourceDialog.tsx
  • src/components/ThumbnailPanel.tsx
  • src/components/PlaylistReportPanel.tsx
  • src/components/Toolbar.tsx
  • src/components/ChannelTable.tsx
  • src/components/SettingsPanel.tsx
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use a virtualized table via @tanstack/react-virtual for playlists containing 1000 or more channels.

Files:

  • src/components/StatusBadge.tsx
  • src/components/PasswordField.tsx
  • src/components/ErrorBoundary.tsx
  • src/components/KeyboardShortcutsDialog.tsx
  • src/components/SavedPlaylistsDialog.tsx
  • src/components/FilterBar.tsx
  • src/components/ProgressBar.tsx
  • src/components/ExportMenu.tsx
  • src/components/SavedPlaylistEditorDialog.tsx
  • src/components/HistoryPanel.tsx
  • src/components/AppBanners.tsx
  • src/components/StatsPanel.tsx
  • src/components/ChannelRow.tsx
  • src/components/SFSymbols.tsx
  • src/components/CastMenu.tsx
  • src/components/StreamPlayer.tsx
  • src/components/StartScreen.tsx
  • src/components/LogWindowContent.tsx
  • src/components/OpenSourceDialog.tsx
  • src/components/ThumbnailPanel.tsx
  • src/components/PlaylistReportPanel.tsx
  • src/components/Toolbar.tsx
  • src/components/ChannelTable.tsx
  • src/components/SettingsPanel.tsx
🪛 ast-grep (0.44.1)
src/lib/extinf.ts

[warning] 41-44: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
\\b${escapedAttribute}=(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|'([^'\\\\]*(?:\\\\.[^'\\\\]*)*)'|([^\\s]+)),
"i",
)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)

🪛 OpenGrep (1.25.0)
src/lib/extinf.ts

[ERROR] 47-47: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🪛 React Doctor (0.7.6)
src/components/KeyboardShortcutsDialog.tsx

[warning] 79-79: Keyboard users can tab out of this role="dialog" modal because it has no built-in focus trapping, so use the native <dialog>, which gives you focus trapping, Escape to close, and the backdrop for free.

Replace the wrapper with <dialog> and open it with dialog.showModal(). For the trigger, prefer <button commandfor="id" command="show-modal"> (Chrome 135+), or a useRef with dialogRef.current?.showModal().

(prefer-html-dialog)


[warning] 83-83: Keyboard users can't trigger this click handler because there's no keyboard one, so add onKeyUp, onKeyDown, or onKeyPress.

Pair onClick with a key handler so keyboard users can trigger it.

(click-events-have-key-events)


[warning] 83-83: Screen reader users can't tell this click handler is interactive because it has no role, so add a role or use a button or link.

Give clickable static elements a role, or use a button or link.

(no-static-element-interactions)

src/components/SavedPlaylistEditorDialog.tsx

[warning] 164-164: Screen reader users can't tell which input this label names because it's tied to none, so add htmlFor or wrap the input inside it.

Tie every label to a control with htmlFor, or by nesting the input.

(label-has-associated-control)


[warning] 220-220: Screen reader users can't tell which input this label names because it's tied to none, so add htmlFor or wrap the input inside it.

Tie every label to a control with htmlFor, or by nesting the input.

(label-has-associated-control)

src/components/HistoryPanel.tsx

[warning] 49-49: Keyboard users can tab out of this role="dialog" modal because it has no built-in focus trapping, so use the native <dialog>, which gives you focus trapping, Escape to close, and the backdrop for free.

Replace the wrapper with <dialog> and open it with dialog.showModal(). For the trigger, prefer <button commandfor="id" command="show-modal"> (Chrome 135+), or a useRef with dialogRef.current?.showModal().

(prefer-html-dialog)


[warning] 53-53: Keyboard users can't trigger this click handler because there's no keyboard one, so add onKeyUp, onKeyDown, or onKeyPress.

Pair onClick with a key handler so keyboard users can trigger it.

(click-events-have-key-events)


[warning] 53-53: Screen reader users can't tell this click handler is interactive because it has no role, so add a role or use a button or link.

Give clickable static elements a role, or use a button or link.

(no-static-element-interactions)

src/components/ThumbnailPanel.tsx

[warning] 134-134: This component misses React Compiler's automatic memoization & re-renders more than it should: This value cannot be modified. Rewrite the flagged code so the compiler can optimize it.

Modifying component props or hook arguments is not allowed. Consider using a local variable instead.

(immutability)


[warning] 542-542: Screen reader users can't tell this click handler is interactive because it has no role, so add a role or use a button or link.

Give clickable static elements a role, or use a button or link.

(no-static-element-interactions)


[warning] 620-620: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)

src/components/PlaylistReportPanel.tsx

[warning] 258-258: Screen reader users can't tell this click handler is interactive because it has no role, so add a role or use a button or link.

Give clickable static elements a role, or use a button or link.

(no-static-element-interactions)

src/components/SettingsPanel.tsx

[warning] 555-555: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)


[warning] 743-743: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)


[warning] 844-844: Screen reader users can't tell which input this label names because it's tied to none, so add htmlFor or wrap the input inside it.

Tie every label to a control with htmlFor, or by nesting the input.

(label-has-associated-control)


[warning] 847-847: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)


[warning] 861-861: Screen reader users can't tell which input this label names because it's tied to none, so add htmlFor or wrap the input inside it.

Tie every label to a control with htmlFor, or by nesting the input.

(label-has-associated-control)


[warning] 886-886: Screen reader users can't tell which input this label names because it's tied to none, so add htmlFor or wrap the input inside it.

Tie every label to a control with htmlFor, or by nesting the input.

(label-has-associated-control)


[warning] 920-920: Screen reader users can't tell which input this label names because it's tied to none, so add htmlFor or wrap the input inside it.

Tie every label to a control with htmlFor, or by nesting the input.

(label-has-associated-control)


[warning] 923-923: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)


[warning] 940-940: Screen reader users can't tell which input this label names because it's tied to none, so add htmlFor or wrap the input inside it.

Tie every label to a control with htmlFor, or by nesting the input.

(label-has-associated-control)


[warning] 964-964: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)


[warning] 990-990: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)


[warning] 1027-1027: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)


[warning] 1111-1111: Screen reader users can't tell which input this label names because it's tied to none, so add htmlFor or wrap the input inside it.

Tie every label to a control with htmlFor, or by nesting the input.

(label-has-associated-control)


[warning] 1114-1114: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)


[warning] 1131-1131: Screen reader users can't tell which input this label names because it's tied to none, so add htmlFor or wrap the input inside it.

Tie every label to a control with htmlFor, or by nesting the input.

(label-has-associated-control)


[warning] 1134-1134: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.

Give every interactive control a label screen readers can read.

(control-has-associated-label)

🔇 Additional comments (82)
tests/channelResults.test.ts (1)

67-67: LGTM!

tests/duplicates.test.ts (1)

57-57: LGTM!

tests/errors.test.ts (1)

23-26: LGTM!

Also applies to: 40-42

tests/exportScope.test.ts (1)

3-6: LGTM!

Also applies to: 65-65

tests/filters.logic.test.ts (1)

79-87: LGTM!

Also applies to: 98-98, 132-132

tests/format.test.ts (1)

12-12: LGTM!

tests/useStreamPlayer.test.ts (1)

5-5: LGTM!

Also applies to: 16-17, 68-68, 79-79, 90-90, 105-118, 178-179, 239-239

tests/playlistReportVisibility.test.ts (1)

23-24: LGTM!

tests/savedPlaylists.test.ts (2)

10-10: LGTM!


104-104: LGTM!

tests/scanErrorEvents.test.ts (1)

17-17: LGTM!

tests/sourceFilter.test.ts (5)

11-11: LGTM!


44-44: LGTM!

Also applies to: 59-59, 74-74


95-96: LGTM!


110-110: LGTM!


130-156: LGTM!

tests/tableColumns.test.ts (1)

10-14: LGTM!

tests/useScan.helpers.test.ts (1)

2-5: LGTM!

biome.jsonc (1)

8-69: LGTM!

package.json (1)

16-18: LGTM!

Also applies to: 42-42

scripts/benchmark-ui-filter.ts (1)

3-3: LGTM!

src/App.tsx (1)

1-51: LGTM!

Also applies to: 61-73, 155-157, 303-368, 407-447, 565-617, 708-811, 863-922, 953-1013, 1113-1224, 1273-1516, 1618-1644

src/SettingsWindow.tsx (1)

2-2: LGTM!

src/main.tsx (1)

4-11: LGTM!

Also applies to: 34-34

src/store/slices/settingsSlice.ts (1)

3-7: LGTM!

Also applies to: 40-40, 62-80

src/store/slices/uiSlice.ts (1)

4-4: LGTM!

Also applies to: 74-74

src/store/store.ts (1)

2-10: LGTM!

src/store/types.ts (1)

1-3: LGTM!

src/components/AppBanners.tsx (1)

3-8: LGTM!

Also applies to: 55-55

src/components/FilterBar.tsx (1)

4-12: LGTM!

Also applies to: 27-30

src/components/StartScreen.tsx (1)

2-5: LGTM!

Also applies to: 74-98, 112-119

src/hooks/useMenuEventBridge.ts (1)

3-4: LGTM!

Also applies to: 108-110

src/hooks/usePlaylistSources.ts (1)

2-15: LGTM!

Also applies to: 25-47, 71-71, 130-130, 152-152, 201-206, 221-221, 279-287, 314-314, 336-342, 441-449, 469-479, 521-521, 543-569, 593-616, 683-688, 757-758, 820-820

src/hooks/useUpdateCheck.ts (1)

2-13: LGTM!

Also applies to: 66-67, 85-158

src/index.css (1)

5-7: LGTM!

Also applies to: 160-162, 175-176, 281-281, 328-341, 373-375, 405-405, 439-448, 469-471, 504-516

src/lib/errors.ts (1)

22-28: LGTM!

Also applies to: 45-50

src/lib/recentPlaylists.ts (1)

21-26: LGTM!

src/lib/savedPlaylists.ts (1)

77-77: LGTM!

src/lib/sourceFilter.ts (1)

1-1: LGTM!

Also applies to: 49-49, 58-58, 122-125

src/components/CastMenu.tsx (1)

2-4: LGTM!

Also applies to: 47-47, 139-141, 199-199, 296-297

src/components/PlaylistReportPanel.tsx (1)

2-11: LGTM!

Also applies to: 45-45, 76-77, 86-91, 118-128, 176-190, 251-276, 303-326, 351-359, 371-373, 397-399, 422-443, 459-461, 474-485, 504-508, 526-527, 546-557

src/components/ProgressBar.tsx (1)

20-24: LGTM!

Also applies to: 39-39, 68-68

src/components/SFSymbols.tsx (1)

8-30: LGTM!

Also applies to: 43-45, 59-66

src/components/SavedPlaylistEditorDialog.tsx (1)

2-3: LGTM!

Also applies to: 47-47, 110-110, 273-273

src/components/StatsPanel.tsx (1)

1-13: LGTM!

Also applies to: 82-82

src/components/StatusBadge.tsx (1)

2-6: LGTM!

src/components/StreamPlayer.tsx (1)

1-17: LGTM!

Also applies to: 110-113, 142-142, 155-162

src/components/ThumbnailPanel.tsx (1)

1-22: LGTM!

Also applies to: 108-111, 128-146, 230-231, 265-271, 315-317, 342-353, 365-374, 394-394, 411-417, 506-514, 540-619, 621-746

src/lib/cast.ts (1)

1-1: LGTM!

Also applies to: 31-31

src/lib/scanState.ts (1)

1-1: LGTM!

Also applies to: 3-4

src/lib/thumbnailState.ts (1)

32-32: LGTM!

Also applies to: 81-81

src/components/ChannelRow.tsx (1)

2-10: LGTM!

Also applies to: 60-69, 79-79, 160-169, 190-200, 233-233

src/components/ChannelTable.tsx (1)

1-37: LGTM!

Also applies to: 70-75, 102-108, 152-152, 162-162, 191-192, 293-294, 407-411, 541-541, 643-643, 683-685, 769-769, 832-832, 916-916, 940-940, 985-985, 1104-1104, 1178-1178, 1208-1209, 1241-1253, 1286-1290, 1368-1383, 1449-1486

src/components/SavedPlaylistsDialog.tsx (1)

2-4: LGTM!

Also applies to: 48-48

src/hooks/useScan.helpers.ts (1)

55-55: LGTM!

src/hooks/useScan.ts (1)

2-11: LGTM!

Also applies to: 22-29, 53-53, 89-89, 199-200, 285-301, 311-318, 332-332, 347-347, 357-357, 375-375, 428-434, 485-485, 604-604, 629-644

src/lib/channelResults.ts (1)

44-44: LGTM!

Also applies to: 69-69

src/lib/duplicates.ts (1)

20-20: LGTM!

src/lib/extinf.ts (1)

36-36: LGTM!

Also applies to: 42-50

src/lib/filters.ts (1)

1-2: LGTM!

Also applies to: 118-118, 226-226, 315-318

src/lib/shortcuts.ts (1)

34-36: LGTM!

Also applies to: 38-38

src/lib/tableColumns.ts (1)

40-60: LGTM!

src/components/ErrorBoundary.tsx (1)

1-1: LGTM!

src/components/ExportMenu.tsx (1)

2-10: LGTM!

Also applies to: 41-43, 308-322

src/components/LogWindowContent.tsx (1)

4-4: LGTM!

Also applies to: 15-15, 43-45, 59-59, 88-91, 118-118, 280-283

src/components/OpenSourceDialog.tsx (1)

2-10: LGTM!

Also applies to: 119-119, 168-168, 177-178, 192-199, 229-233, 262-272, 328-329, 391-392, 509-509, 522-522, 536-536, 629-629, 684-684

src/components/PasswordField.tsx (1)

2-2: LGTM!

src/components/SettingsPanel.tsx (1)

2-12: LGTM!

Also applies to: 63-63, 114-114, 145-145, 215-215, 263-263, 365-365, 392-392, 433-433, 494-497, 520-1228

src/components/Toolbar.tsx (1)

12-48: LGTM!

Also applies to: 107-112, 141-149, 195-195, 214-214, 233-246, 272-275, 389-389, 405-405, 424-430, 592-606

src/hooks/useChromecast.ts (1)

2-4: LGTM!

Also applies to: 36-48

src/lib/tauri.ts (1)

2-24: LGTM!

Also applies to: 116-130, 175-175, 269-269

src/hooks/useStreamPlayer.ts (1)

2-26: LGTM!

Also applies to: 205-205, 235-236, 254-254, 373-373, 442-442, 652-655, 697-697, 791-791, 811-811, 842-852, 869-869, 901-914

src/lib/format.ts (1)

97-97: LGTM!

src/lib/haptics.ts (1)

3-4: LGTM!

src/lib/languageDistribution.ts (1)

30-30: LGTM!

src/lib/logger.ts (1)

4-6: LGTM!

Also applies to: 65-65

src/lib/logoCache.ts (1)

113-113: LGTM!

src/lib/perf.ts (1)

87-87: LGTM!

src/lib/playback.ts (1)

176-189: LGTM!

Also applies to: 222-236, 317-317

src/lib/playlistReportVisibility.ts (1)

21-25: LGTM!

src/lib/runtimeMonitor.ts (1)

5-23: LGTM!

Also applies to: 153-153, 177-177, 199-199, 220-226, 254-254, 343-346, 370-370, 388-388, 398-398

src/lib/workerSupport.ts (1)

23-23: LGTM!

Comment thread biome.jsonc Outdated
Comment thread src/components/HistoryPanel.tsx
Comment thread src/components/SavedPlaylistEditorDialog.tsx
Comment thread src/components/SettingsPanel.tsx
Comment thread src/components/ThumbnailPanel.tsx
scripts/ is mostly shell and plain text; only the two benchmark files are
TypeScript. Biome already skips the rest by extension, but naming them in
the glob is misleading about what the config covers.
@kristofferR
kristofferR merged commit 0b7ee12 into main Jul 25, 2026
9 checks passed
@kristofferR
kristofferR deleted the chore/biome-formatting branch July 25, 2026 04:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant