Skip to content

feat(gui): restyle the frontend to match the autobrr design language - #278

Open
dangerouslaser wants to merge 5 commits into
autobrr:mainfrom
dangerouslaser:feat/autobrr-ui
Open

feat(gui): restyle the frontend to match the autobrr design language#278
dangerouslaser wants to merge 5 commits into
autobrr:mainfrom
dangerouslaser:feat/autobrr-ui

Conversation

@dangerouslaser

@dangerouslaser dangerouslaser commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

What

Restyles the entire GUI frontend to match the autobrr design language, so upbrr looks and feels like the rest of the autobrr family. No page logic, workflow behavior, or API/runtime contracts change.

Design system (taken from autobrr's web/ source):

  • Tailwind zinc mapped onto gray, plus the in-between shades autobrr generates with tailwind-lerp-colors (gray-815 inputs, gray-925 header gradient, gray-775 borders, etc. — hardcoded, no new dependency), blue accent, class-based dark/light mode, and the same self-hosted Inter Var typeface.
  • autobrr's topographic background texture, copied verbatim and applied to #root the same way.

App shell:

  • The fixed left sidebar is replaced by autobrr's top header: logo, primary nav (Upload / Logging / History), a Docs-style external GitHub link, theme toggle, and — in browser mode — an autobrr-style user dropdown menu (Settings / Log out). Desktop (Wails) mode gets a gear button for Settings. Mobile uses the same disclosure pattern as autobrr's MobileNav.
  • The conditional workflow steps (Tracker Data → … → Tracker Upload) render as an autobrr filter-details-style tab bar (border-b-2, blue active underline) that appears once a release is loaded.

Screens:

  • Settings is rebuilt as autobrr's settings card: one rounded bordered card with an icon sidebar (heroicons per section, autobrr's exact active/hover treatment) and a section header with actions on the content side.
  • All shared CSS (panels, buttons, inputs, dialogs, badges, banners, login card) and the ui primitives (Button, Checkbox, Switch) restyled to autobrr's card/form/button styles; remaining white-alpha/glass utilities routed through theme variables so light mode is fully supported.

Fix along the way: the stylesheet only includes @tailwind utilities (no preflight), so Tailwind border/divide utilities on non-button elements rendered border-style: none and were invisible. A preflight-lite border reset restores them (with explicit base styles for select/textarea to replace the UA borders it removes).

Screenshots

Upload (dark) Upload (light)
upload dark upload light
Settings History
settings history
Mobile menu

mobile

(Screenshot data is synthetic; history entries are placeholder releases.)

Testing

  • lint, lint:style, lint:dead, typecheck, format:check, build all clean; 202/202 unit tests pass (one settings test updated for the new sidebar markup, app.test.ts nav references updated for the Upload label).
  • e2e specs select by role/label and were kept in sync (web-smoke pins exact: true on the Upload button so it can't collide with the Tracker Upload tab).
  • Manually exercised in browser mode (dark + light + mobile viewports) and running on a personal instance.

Summary by CodeRabbit

  • New Features

    • Introduced a responsive top navigation with mobile menu support and account actions.
    • Added icon-based Settings navigation for easier access to configuration sections.
    • Added self-hosted font loading, theme-color support, and a refreshed visual background pattern.
  • Style

    • Updated application-wide colors, spacing, borders, controls, dialogs, cards, and status indicators for a consistent modern theme.
    • Improved light and dark theme consistency across workflow pages.
  • Bug Fixes

    • Updated upload interactions throughout the interface and automated checks to use the correct Upload action.

dangerouslaser and others added 5 commits July 21, 2026 13:27
Replace the custom glassmorphism theme (amber/teal accents, gradient
orbs, fixed left sidebar) with autobrr's design system so upbrr looks
and feels like the rest of the autobrr family:

- Adopt autobrr's palette: Tailwind zinc mapped onto gray with the
  in-between shades autobrr generates (gray-815 inputs, gray-925
  header gradient, gray-775 borders), blue accent, class-based dark
  mode, and the self-hosted Inter Var typeface.
- Replace the fixed sidebar with autobrr's top header nav (logo,
  rounded nav links, theme toggle, GitHub link, mobile disclosure
  menu) and move the web-session Logout/username into the header.
- Render the conditional workflow steps as an autobrr filter-details
  style tab bar (border-b-2, blue active underline) under the header.
- Restyle shared CSS (panels, buttons, inputs, dialogs, badges,
  banners) and the ui primitives (Button, Checkbox, Switch) to
  autobrr's card/form/button styles; settings section nav now uses
  autobrr's settings sub-nav treatment (border-l-4 active state).
- Route remaining glass utilities (white/alpha borders and fills,
  teal/amber accents) through theme variables so light mode is fully
  supported everywhere.

All page logic, tab flow, and e2e-facing roles/labels are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xture

- Add autobrr's topographic background texture (copied verbatim from
  autobrr web/src/index.css), applied to #root with min-height so the
  tile covers pages taller than one viewport.
- Promote Input to a top-level nav item and make the header match
  autobrr's: all primary sections in the left nav, GitHub as a
  Docs-style external text link, and the right side reduced to the
  theme toggle plus an autobrr-style user dropdown menu (username +
  user icon; Settings / Log out items) in web mode.
- Drop Input from the workflow tab bar (it is top-level nav now), so
  the bar only appears once a release is loaded.
- Remove the header version readout and the now-unused applicationInfo
  state in App; the version remains in Settings > Application Details.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add a preflight-lite border reset: without Tailwind preflight,
  border/divide utilities rendered border-style:none, which is why the
  header separator (and several other utility borders) were invisible.
  Form controls (select/textarea) get explicit base styles to replace
  the UA borders the reset removes.
- Move Settings out of the main nav: it lives in the user dropdown in
  web mode and behind a gear icon button in desktop mode; the mobile
  menu keeps a Settings entry.
- Rebuild the Settings screen as autobrr's settings card: one rounded
  card with an icon sidebar (heroicons per section, autobrr's exact
  active/hover treatment) and a section header with the description and
  Reload/Export/Import/Save actions on the content side.
- Drop the redundant "upbrr" eyebrow labels from pages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The header entry point reads Upload (the app's job); the page itself
keeps its Build Release Name heading. The smoke spec pins exact name
matching so it cannot collide with the Tracker Upload workflow tab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The frontend receives a broad visual refresh with token-based theming, self-hosted fonts, responsive header navigation, authenticated user actions, redesigned settings navigation, and updated workflow-page styling. Tests now target the renamed “Upload” navigation control.

Changes

Frontend refresh

Layer / File(s) Summary
Theme and styling foundation
gui/frontend/index.html, gui/frontend/src/styles.css, gui/frontend/tailwind.config.js
Adds Inter Variable, theme tokens, shared control styles, updated overlays, web-auth styling, pattern background, custom gray colors, and a table shadow.
Application navigation and authentication
gui/frontend/src/app.tsx, gui/frontend/src/webRoot.tsx, gui/frontend/package.json, gui/frontend/src/app.test.ts, gui/frontend/e2e/web-smoke.spec.ts
Replaces the sidebar with responsive header/workflow navigation, adds Radix user menus and logout wiring, and updates navigation test targets.
Settings navigation redesign
gui/frontend/src/pages/settings/index.tsx, gui/frontend/src/pages/settings/index.test.tsx, gui/frontend/src/styles.css
Adds icon-based responsive settings navigation while retaining existing section rendering and status behavior.
Workflow surface styling
gui/frontend/src/components/*, gui/frontend/src/pages/*, gui/frontend/src/hooks/useSettingsState.tsx
Migrates workflow panels, controls, lists, images, progress states, and inputs from fixed colors to theme variables.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • autobrr/upbrr#234: Updates frontend/Wails bridge types and UI flows for DVD menu capture features, overlapping with related menu-image workflow areas.

Suggested reviewers: audionut

Poem

I’m a rabbit hopping through the new blue glow,
With token-painted panels in a tidy row.
The header folds neatly when screens grow small,
“Upload” now answers each test’s call.
Fonts bloom locally, patterns softly spin—
A carrot for every refreshed login!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: restyling the frontend to match the autobrr design language.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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

🧹 Nitpick comments (2)
gui/frontend/src/components/ui/checkbox.tsx (1)

33-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Centralize interactive primitives to preserve interface parity.

The shared token updates do not reach the duplicate Checkbox, PillCheckbox, and Switch implementations found across the frontend pages and embedded web surface.

  • gui/frontend/src/components/ui/checkbox.tsx#L33-L33: consolidate local Checkbox copies onto this shared component or synchronize their tokens.
  • gui/frontend/src/components/ui/checkbox.tsx#L78-L78: consolidate local PillCheckbox copies or synchronize their tokens.
  • gui/frontend/src/components/ui/switch.tsx#L26-L35: consolidate local Switch copies or synchronize their tokens.
🤖 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 `@gui/frontend/src/components/ui/checkbox.tsx` at line 33, Centralize the
duplicate interactive primitives so all frontend pages and the embedded web
surface use the shared implementations and retain interface parity. Update
gui/frontend/src/components/ui/checkbox.tsx lines 33-33 (Checkbox) and 78-78
(PillCheckbox), and gui/frontend/src/components/ui/switch.tsx lines 26-35
(Switch), by consolidating local copies onto the shared components or
synchronizing their shared tokens.

Source: Coding guidelines

gui/frontend/src/pages/settings/index.tsx (1)

788-826: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Combined sections array is built twice.

[...settingsSections, applicationDetailsSection, trackerAuthSection] is constructed once for activeSectionLabel (line 789) and again for the nav render (line 806). Hoist to a single (optionally memoized) value to avoid the duplicate work and keep both spots in sync.

♻️ Suggested refactor
+  const allSettingsSections = [...settingsSections, applicationDetailsSection, trackerAuthSection];
   const activeSectionLabel =
-    [...settingsSections, applicationDetailsSection, trackerAuthSection].find(
-      (item) => item.key === settingsSection,
-    )?.label ?? "Settings";
+    allSettingsSections.find((item) => item.key === settingsSection)?.label ?? "Settings";
-              {[...settingsSections, applicationDetailsSection, trackerAuthSection].map(
-                (section) => (
+              {allSettingsSections.map(
+                (section) => (
🤖 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 `@gui/frontend/src/pages/settings/index.tsx` around lines 788 - 826, Hoist the
combined sections collection used by activeSectionLabel and the navigation
rendering into a single local value, optionally memoized consistently with the
surrounding component. Replace both [...settingsSections,
applicationDetailsSection, trackerAuthSection] expressions with that shared
value so the label lookup and nav remain synchronized.
🤖 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 `@gui/frontend/index.html`:
- Around line 13-19: Update the font preload link for Inter-Variable.woff2 to
use anonymous CORS by replacing the credentials mode with
crossorigin="anonymous" (or the equivalent bare crossorigin attribute), keeping
the existing preload, font type, and URL unchanged.

In `@gui/frontend/src/app.tsx`:
- Around line 4263-4270: Update the logo button’s aria-label in the
selectTab("input") navigation control to announce “go to Upload” instead of “go
to Input”, keeping the destination behavior unchanged.

In `@gui/frontend/src/pages/settings/index.tsx`:
- Around line 806-826: Add an aria-current indicator to each settings-section
button rendered by the map in the settings navigation, setting it to "page" only
when settingsSection matches section.key and leaving it unset for inactive
sections. Preserve the existing visual styling and setSettingsSection behavior.
- Around line 66-71: Replace the duplicated SVG path in the "Torrent Specific"
entry of the sidebar icon configuration with a distinct icon path, while leaving
the "Client Handling" icon unchanged and preserving both section keys.

In `@gui/frontend/src/styles.css`:
- Line 9: Update the font-family declaration in styles.css to remove quotes
around the single-word Roboto family name, while preserving quotes for
multi-word family names such as "Segoe UI" and "Helvetica Neue".

---

Nitpick comments:
In `@gui/frontend/src/components/ui/checkbox.tsx`:
- Line 33: Centralize the duplicate interactive primitives so all frontend pages
and the embedded web surface use the shared implementations and retain interface
parity. Update gui/frontend/src/components/ui/checkbox.tsx lines 33-33
(Checkbox) and 78-78 (PillCheckbox), and
gui/frontend/src/components/ui/switch.tsx lines 26-35 (Switch), by consolidating
local copies onto the shared components or synchronizing their shared tokens.

In `@gui/frontend/src/pages/settings/index.tsx`:
- Around line 788-826: Hoist the combined sections collection used by
activeSectionLabel and the navigation rendering into a single local value,
optionally memoized consistently with the surrounding component. Replace both
[...settingsSections, applicationDetailsSection, trackerAuthSection] expressions
with that shared value so the label lookup and nav remain synchronized.
🪄 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: CHILL

Plan: Pro

Run ID: e929f425-7e26-478e-b797-7a59b36d69a4

📥 Commits

Reviewing files that changed from the base of the PR and between 1c8fa3d and 45b6035.

⛔ Files ignored due to path filters (3)
  • gui/frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • gui/frontend/public/Inter-Variable.woff2 is excluded by !**/*.woff2
  • gui/frontend/src/assets/logo.png is excluded by !**/*.png
📒 Files selected for processing (27)
  • gui/frontend/e2e/web-smoke.spec.ts
  • gui/frontend/index.html
  • gui/frontend/package.json
  • gui/frontend/src/app.test.ts
  • gui/frontend/src/app.tsx
  • gui/frontend/src/components/LogSettingsPanel.tsx
  • gui/frontend/src/components/ui/button.tsx
  • gui/frontend/src/components/ui/checkbox.tsx
  • gui/frontend/src/components/ui/switch.tsx
  • gui/frontend/src/components/ui/tracker-icon.tsx
  • gui/frontend/src/hooks/useSettingsState.tsx
  • gui/frontend/src/pages/bluray_candidates/index.tsx
  • gui/frontend/src/pages/description_builder/index.tsx
  • gui/frontend/src/pages/dupe_check/index.tsx
  • gui/frontend/src/pages/history/index.tsx
  • gui/frontend/src/pages/input/index.tsx
  • gui/frontend/src/pages/logging/index.tsx
  • gui/frontend/src/pages/menu_images/index.tsx
  • gui/frontend/src/pages/playlist_selection/index.tsx
  • gui/frontend/src/pages/settings/index.test.tsx
  • gui/frontend/src/pages/settings/index.tsx
  • gui/frontend/src/pages/tracker_data/index.tsx
  • gui/frontend/src/pages/tracker_upload/index.tsx
  • gui/frontend/src/pages/upload_images/index.tsx
  • gui/frontend/src/styles.css
  • gui/frontend/src/webRoot.tsx
  • gui/frontend/tailwind.config.js
💤 Files with no reviewable changes (1)
  • gui/frontend/src/pages/logging/index.tsx

Comment thread gui/frontend/index.html
Comment on lines 13 to 19
<link
href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Space+Grotesk:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
rel="preload"
href="/Inter-Variable.woff2"
as="font"
type="font/woff2"
crossorigin="use-credentials"
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant HTML and CSS around the font preload/faces.
sed -n '1,120p' gui/frontend/index.html

printf '\n---\n'

# Find the font-face declaration and related references.
rg -n -A6 -B4 '`@font-face`|Inter-Variable\.woff2|font-display|crossorigin' gui/frontend -g '!**/node_modules/**'

Repository: autobrr/upbrr

Length of output: 3207


🌐 Web query:

HTML font preload crossorigin use-credentials anonymous CORS matching same-origin @font-face request credentials mode

💡 Result:

When preloading a font in HTML, you must include the crossorigin attribute on the element to ensure the preload request matches the actual font request initiated by CSS @font-face [1][2][3]. If this attribute is missing, the browser will likely perform the preload without CORS, whereas CSS @font-face requests for fonts are mandatorily performed using anonymous-mode CORS [2][4][5]. This mismatch causes the browser to treat them as different requests, resulting in the font being downloaded twice [2][6][3]. The technical details and requirements are as follows: 1. CORS and Credentials Mode: By default, @font-face requests are made with the CORS mode set to cors and the credentials mode set to same-origin [4][7][5]. The crossorigin attribute on a element enables CORS for that preload request [8][5]. Using the attribute without a value, or with the value anonymous, sets the credentials mode to same-origin [8][3]. This aligns the preload with the default @font-face behavior, even for same-origin fonts [1][3]. 2. Anonymous vs. Use-Credentials: - The anonymous value (or the attribute alone) is the standard requirement for most font loads because it uses CORS without sending user credentials (cookies, HTTP authentication) [8][3]. - The use-credentials value should only be used if the font resource specifically requires credentials to be sent (e.g., if the server requires authentication to access the font file) [8]. 3. Browser Behavior: Although some browsers (like Firefox) may automatically handle this correctly regardless of the attribute, it is a strict requirement in Chromium-based browsers and others to explicitly include the crossorigin attribute to avoid double-downloading [6][5]. In summary, for standard font preloading, always use to ensure the credentials and CORS modes match the @font-face request [1][3][9].

Citations:


Use anonymous CORS for the font preload. crossorigin="use-credentials" won’t match the @font-face fetch here, so the browser can treat this as a separate request and download the font twice. Use crossorigin (or crossorigin="anonymous") unless the font file actually requires credentials.

🤖 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 `@gui/frontend/index.html` around lines 13 - 19, Update the font preload link
for Inter-Variable.woff2 to use anonymous CORS by replacing the credentials mode
with crossorigin="anonymous" (or the equivalent bare crossorigin attribute),
keeping the existing preload, font type, and URL unchanged.

Comment thread gui/frontend/src/app.tsx
Comment on lines +4263 to +4270
<button
className="flex shrink-0 items-center rounded-full border-0 bg-transparent p-0 shadow-none"
type="button"
onClick={() => selectTab("input")}
aria-label="upbrr — go to Input"
>
<img src={logoUrl} alt="upbrr" className="h-10 w-10" />
</button>

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

Stale aria-label after the Input → Upload rename.

The nav item is now labeled "Upload", but the logo button still announces "go to Input" to assistive tech. Align the label with the renamed destination.

Proposed fix
-                    aria-label="upbrr — go to Input"
+                    aria-label="upbrr — go to Upload"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
className="flex shrink-0 items-center rounded-full border-0 bg-transparent p-0 shadow-none"
type="button"
onClick={() => selectTab("input")}
aria-label="upbrr — go to Input"
>
<img src={logoUrl} alt="upbrr" className="h-10 w-10" />
</button>
<button
className="flex shrink-0 items-center rounded-full border-0 bg-transparent p-0 shadow-none"
type="button"
onClick={() => selectTab("input")}
aria-label="upbrr — go to Upload"
>
<img src={logoUrl} alt="upbrr" className="h-10 w-10" />
</button>
🤖 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 `@gui/frontend/src/app.tsx` around lines 4263 - 4270, Update the logo button’s
aria-label in the selectTab("input") navigation control to announce “go to
Upload” instead of “go to Input”, keeping the destination behavior unchanged.

Comment on lines +66 to +71
"Client Handling": [
"M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75",
],
"Torrent Specific": [
"M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75",
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Duplicate icon path data for two different sections.

The "Client Handling" and "Torrent Specific" entries use the exact same SVG path string, so both sidebar items render an identical icon. This looks like a copy/paste slip rather than an intentional shared glyph, undermining the goal of distinct per-section icons.

🎨 Suggested fix
   "Torrent Specific": [
-    "M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75",
+    "M12 6.75a.75.75 0 01.75-.75h6.75a.75.75 0 010 1.5h-6.75A.75.75 0 0112 6.75zm-8.25 0a.75.75 0 01.75-.75h3a.75.75 0 010 1.5h-3a.75.75 0 01-.75-.75zm8.25 5.25a.75.75 0 01.75-.75h6.75a.75.75 0 010 1.5h-6.75a.75.75 0 01-.75-.75zm-8.25 0a.75.75 0 01.75-.75h3a.75.75 0 010 1.5h-3a.75.75 0 01-.75-.75zM12 17.25a.75.75 0 01.75-.75h6.75a.75.75 0 010 1.5h-6.75a.75.75 0 01-.75-.75zm-8.25 0a.75.75 0 01.75-.75h3a.75.75 0 010 1.5h-3a.75.75 0 01-.75-.75z",
   ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"Client Handling": [
"M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75",
],
"Torrent Specific": [
"M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75",
],
"Client Handling": [
"M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75",
],
"Torrent Specific": [
"M12 6.75a.75.75 0 01.75-.75h6.75a.75.75 0 010 1.5h-6.75A.75.75 0 0112 6.75zm-8.25 0a.75.75 0 01.75-.75h3a.75.75 0 010 1.5h-3a.75.75 0 01-.75-.75zm8.25 5.25a.75.75 0 01.75-.75h6.75a.75.75 0 010 1.5h-6.75a.75.75 0 01-.75-.75zm-8.25 0a.75.75 0 01.75-.75h3a.75.75 0 010 1.5h-3a.75.75 0 01-.75-.75zM12 17.25a.75.75 0 01.75-.75h6.75a.75.75 0 010 1.5h-6.75a.75.75 0 01-.75-.75zm-8.25 0a.75.75 0 01.75-.75h3a.75.75 0 010 1.5h-3a.75.75 0 01-.75-.75z",
],
🤖 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 `@gui/frontend/src/pages/settings/index.tsx` around lines 66 - 71, Replace the
duplicated SVG path in the "Torrent Specific" entry of the sidebar icon
configuration with a distinct icon path, while leaving the "Client Handling"
icon unchanged and preserving both section keys.

Comment on lines +806 to 826
{[...settingsSections, applicationDetailsSection, trackerAuthSection].map(
(section) => (
<button
key={section.key}
type="button"
className="config-status-banner__toggle"
onClick={() => setWarningsExpanded((prev) => !prev)}
className={cn(
"group flex w-full items-center rounded-none border-0 border-l-4 bg-transparent px-3 py-2 text-left text-sm font-medium shadow-none transition",
settingsSection === section.key
? "border-sky-500 bg-blue-100 font-bold text-sky-700 hover:bg-blue-200 hover:text-sky-900 dark:border-blue-500 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600 dark:hover:text-white"
: "border-transparent text-gray-900 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-gray-300",
)}
onClick={() => setSettingsSection(section.key)}
>
{warningsExpanded ? "Hide" : "Show"} {configOpStatus.warnings.length} warning
{configOpStatus.warnings.length !== 1 ? "s" : ""}
<SectionGlyph
label={section.label}
className="-ml-1 mr-3 h-6 w-6 shrink-0 text-gray-500 group-hover:text-gray-600 dark:text-gray-400 dark:group-hover:text-gray-300"
/>
<span className="truncate">{section.label}</span>
</button>
{warningsExpanded ? (
<ul className="config-status-banner__warning-list">
{configOpStatus.warnings.map((w, i) => (
<li key={i}>{w}</li>
))}
</ul>
) : null}
</div>
) : null}
</div>
<button
type="button"
className="config-status-banner__dismiss"
onClick={dismissConfigOpStatus}
aria-label="Dismiss"
>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path
d="M10.5 3.5 3.5 10.5M3.5 3.5l7 7"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
</button>
</div>
) : null}

<div className="settings-shell">
<div className="settings-tags">
{settingsSections.map((section) => (
<button
key={section.key}
type="button"
className={cn(
"flex h-8 w-full items-center rounded-md px-3 text-left text-sm font-medium transition",
settingsSection === section.key
? "bg-[var(--accent)] text-slate-950 shadow-[0_8px_24px_rgba(245,185,66,0.16)]"
: "text-[var(--muted)] hover:bg-white/10 hover:text-[var(--text)]",
)}
onClick={() => setSettingsSection(section.key)}
>
{section.label}
</button>
))}
<button
key={applicationDetailsSection.key}
type="button"
className={cn(
"flex h-8 w-full items-center rounded-md px-3 text-left text-sm font-medium transition",
settingsSection === applicationDetailsSection.key
? "bg-[var(--accent)] text-slate-950 shadow-[0_8px_24px_rgba(245,185,66,0.16)]"
: "text-[var(--muted)] hover:bg-white/10 hover:text-[var(--text)]",
),
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Active settings-section tab has no semantic "current" indicator.

The selected nav button is only distinguished by color/border; no aria-current is set, so screen-reader users get no announcement of which settings section is active.

♿ Suggested fix
                   <button
                     key={section.key}
                     type="button"
+                    aria-current={settingsSection === section.key ? "page" : undefined}
                     className={cn(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{[...settingsSections, applicationDetailsSection, trackerAuthSection].map(
(section) => (
<button
key={section.key}
type="button"
className="config-status-banner__toggle"
onClick={() => setWarningsExpanded((prev) => !prev)}
className={cn(
"group flex w-full items-center rounded-none border-0 border-l-4 bg-transparent px-3 py-2 text-left text-sm font-medium shadow-none transition",
settingsSection === section.key
? "border-sky-500 bg-blue-100 font-bold text-sky-700 hover:bg-blue-200 hover:text-sky-900 dark:border-blue-500 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600 dark:hover:text-white"
: "border-transparent text-gray-900 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-gray-300",
)}
onClick={() => setSettingsSection(section.key)}
>
{warningsExpanded ? "Hide" : "Show"} {configOpStatus.warnings.length} warning
{configOpStatus.warnings.length !== 1 ? "s" : ""}
<SectionGlyph
label={section.label}
className="-ml-1 mr-3 h-6 w-6 shrink-0 text-gray-500 group-hover:text-gray-600 dark:text-gray-400 dark:group-hover:text-gray-300"
/>
<span className="truncate">{section.label}</span>
</button>
{warningsExpanded ? (
<ul className="config-status-banner__warning-list">
{configOpStatus.warnings.map((w, i) => (
<li key={i}>{w}</li>
))}
</ul>
) : null}
</div>
) : null}
</div>
<button
type="button"
className="config-status-banner__dismiss"
onClick={dismissConfigOpStatus}
aria-label="Dismiss"
>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path
d="M10.5 3.5 3.5 10.5M3.5 3.5l7 7"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
</button>
</div>
) : null}
<div className="settings-shell">
<div className="settings-tags">
{settingsSections.map((section) => (
<button
key={section.key}
type="button"
className={cn(
"flex h-8 w-full items-center rounded-md px-3 text-left text-sm font-medium transition",
settingsSection === section.key
? "bg-[var(--accent)] text-slate-950 shadow-[0_8px_24px_rgba(245,185,66,0.16)]"
: "text-[var(--muted)] hover:bg-white/10 hover:text-[var(--text)]",
)}
onClick={() => setSettingsSection(section.key)}
>
{section.label}
</button>
))}
<button
key={applicationDetailsSection.key}
type="button"
className={cn(
"flex h-8 w-full items-center rounded-md px-3 text-left text-sm font-medium transition",
settingsSection === applicationDetailsSection.key
? "bg-[var(--accent)] text-slate-950 shadow-[0_8px_24px_rgba(245,185,66,0.16)]"
: "text-[var(--muted)] hover:bg-white/10 hover:text-[var(--text)]",
),
)}
{[...settingsSections, applicationDetailsSection, trackerAuthSection].map(
(section) => (
<button
key={section.key}
type="button"
aria-current={settingsSection === section.key ? "page" : undefined}
className={cn(
"group flex w-full items-center rounded-none border-0 border-l-4 bg-transparent px-3 py-2 text-left text-sm font-medium shadow-none transition",
settingsSection === section.key
? "border-sky-500 bg-blue-100 font-bold text-sky-700 hover:bg-blue-200 hover:text-sky-900 dark:border-blue-500 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600 dark:hover:text-white"
: "border-transparent text-gray-900 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-gray-300",
)}
onClick={() => setSettingsSection(section.key)}
>
<SectionGlyph
label={section.label}
className="-ml-1 mr-3 h-6 w-6 shrink-0 text-gray-500 group-hover:text-gray-600 dark:text-gray-400 dark:group-hover:text-gray-300"
/>
<span className="truncate">{section.label}</span>
</button>
),
)}
🤖 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 `@gui/frontend/src/pages/settings/index.tsx` around lines 806 - 826, Add an
aria-current indicator to each settings-section button rendered by the map in
the settings navigation, setting it to "page" only when settingsSection matches
section.key and leaving it unset for inactive sections. Preserve the existing
visual styling and setSettingsSection behavior.

-apple-system,
BlinkMacSystemFont,
"Segoe UI",
"Roboto",

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

Remove quotes around Roboto to satisfy Stylelint.

font-family-name-quotes flags "Roboto"; single-word family names must be unquoted (multi-word names like "Segoe UI" / "Helvetica Neue" still need quotes).

Proposed fix
     "Segoe UI",
-    "Roboto",
+    Roboto,
     "Helvetica Neue",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"Roboto",
Roboto,
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 9-9: Expected no quotes around "Roboto" (font-family-name-quotes)

(font-family-name-quotes)

🤖 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 `@gui/frontend/src/styles.css` at line 9, Update the font-family declaration in
styles.css to remove quotes around the single-word Roboto family name, while
preserving quotes for multi-word family names such as "Segoe UI" and "Helvetica
Neue".

Source: Linters/SAST tools

@Audionut

Copy link
Copy Markdown
Contributor

Thanks for the pr's.

I can't say I'm a huge fan of this tbh. In any event, there is quite an amount of backend work going on. It would be worth waiting for #273 to be merged before continuing effort.

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.

2 participants