Skip to content

feat(web): consolidated single-component header for Unraid 7.3+ (fixes mobile overlap)#2037

Open
elibosley wants to merge 25 commits into
mainfrom
feat/consolidated-header
Open

feat(web): consolidated single-component header for Unraid 7.3+ (fixes mobile overlap)#2037
elibosley wants to merge 25 commits into
mainfrom
feat/consolidated-header

Conversation

@elibosley

@elibosley elibosley commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Consolidates the Unraid header into a single web component (<unraid-header>, Header.standalone.vue) that owns the entire header and its responsive layout, and fixes the broken mobile header where the "Reboot Required for Update" banner, the server-name title, and the notification bell overlapped on narrow screens.

Gated to Unraid 7.3+; older releases fall back to the existing multi-component header. The gate lives in webgui Header.php (see companion PR).

Before: header split across components

Piece Component Mounted by
Logo + OS/API version dropdown + reboot/update banner HeaderOsVersion.standalone.vue (<unraid-header-os-version>) webgui Header.php
Array-usage graph (sidebar themes) #array-usage-sidenav webgui Header.php
Server name/title, server status, notifications bell, account dropdown/avatar UserProfile.standalone.vue (<unraid-user-profile>) api plugin myservers2.php

UserProfile was absolute top-0 right-0 pl-[30%] positioned over the logo/version area. On narrow screens the reboot banner (left) and the title + bell (absolutely positioned right) collided.

After: one component, one layout

<unraid-header> composes all of it in a single mobile-first flex flow:

  • mobile: flex-col — logo/version/banner stacks above server-status/name/bell/avatar (no overlap; the bell/avatar row flex-wraps).
  • sm+: flex-row … justify-between — logo cluster left, account cluster right (the prior desktop look).

No cluster is absolutely positioned anymore, which is the actual fix.

Reuse / no duplication

  • Logo + version dropdown + reboot/update banner extracted into shared Header/HeaderVersion.vue, now used by both <unraid-header> and the legacy HeaderOsVersion.standalone.vue — single source of truth.
  • Server-status, notifications bell (Notifications/Sidebar.vue), and the account dropdown are the existing UserProfile/* children, reused as-is.
  • Server-prop hydration extracted to Header/useServerProp.ts.

Backwards compatibility (7.3+ gate)

  • The legacy <unraid-header-os-version> + <unraid-user-profile> components are unchanged and remain the < 7.3 fallback.
  • Version gating uses version_compare($osVersion, '7.3', '>=') in webgui Header.php (companion PR), mirroring the docker-overview 7.3+ approach.

Coordination with persistent-notifications (#2033)

The consolidated header does not mount the deprecated CriticalNotifications popover. Per #2033 (OS-471) persistent/"pinned" alerts now live in the notification bell, so the header uses only the surviving bell (Notifications/Sidebar.vue) and does not reintroduce the popover being retired.

Testing

  • Header.test.ts (new): renders all regions in one component; hydrates server store; asserts the responsive layout invariant (mobile-first flex-colsm:flex-row, no absolute-positioned cluster); LAN-IP copy.
  • Updated component-registry.test.ts priority-order assertion.
  • HeaderOsVersion / UserProfile tests still pass unchanged (extraction preserves output).
  • pnpm build succeeds — <unraid-header> compiles to its own chunk; HeaderVersion is a shared chunk.
  • Lint clean; new files type-check clean (repo has pre-existing unrelated type-check failures).

Note

Live mobile rendering should be spot-checked once the dev server is running (I don't start it per repo convention). Companion webgui PR wires the mount + gate.

Summary by CodeRabbit

  • New Features
    • Introduced a refreshed, consolidated header with branding, OS/API version details, notifications/user actions, and improved responsiveness.
    • Added an array utilization bar showing used percent, with offline vs started messaging.
    • Added copy-to-clipboard for the server’s LAN IP when clipboard support is available.
  • Bug Fixes
    • Improved header layout layering to prevent element overlap, including responsive grid behavior.
    • Updated component registry priority and expanded automated coverage for header rendering and interactions.

Introduce <unraid-header> (Header.standalone.vue) which owns the entire
header in one web component: Unraid logo, OS/API version dropdown and
reboot/update banner, server name, server status, notifications bell, and
the account dropdown/avatar.

The header was previously split across <unraid-header-os-version> and
<unraid-user-profile>, with the user-profile cluster absolutely positioned
over the logo/version area. On narrow screens the reboot banner, server
title, and notification bell overlapped. The consolidated component lays
everything out in a single mobile-first flex flow (stacked on mobile,
two clusters justified apart on sm+), removing the overlap.

Logo + version + reboot banner logic is extracted into a shared
Header/HeaderVersion.vue used by both the new header and the legacy
HeaderOsVersion.standalone.vue, so there is a single source of truth. The
legacy multi-component header stays intact as the < 7.3 fallback.

The consolidated header intentionally does not mount the deprecated
CriticalNotifications popover (removed by the persistent-notifications work,
#2033); persistent alerts now live in the bell.

Gating lives in webgui Header.php via version_compare (separate change).
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 46db31d0-17cc-4faa-9544-96deba003a26

📥 Commits

Reviewing files that changed from the base of the PR and between ae0a69b and 003854b.

📒 Files selected for processing (1)
  • web/src/components/Header.standalone.vue

Walkthrough

Adds a standalone Unraid header component with server hydration, array usage polling, logo/version subcomponents, and LAN IP copy behavior. Updates component registration, generated Vue/GraphQL types, header CSS, locale strings, and test coverage.

Changes

Header Consolidation Feature

Layer / File(s) Summary
Server hydration composable
web/src/components/Header/useServerProp.ts
Hydrates server store from a prop value, triggers callback watcher logic, and sets a mock session cookie in development.
Array capacity GraphQL query and generated types
web/src/components/Header/arrayCapacity.query.ts, web/src/composables/gql/gql.ts, web/src/composables/gql/graphql.ts
Adds ARRAY_CAPACITY_QUERY and the corresponding GraphQL document and typed query entries.
ArrayUsage component
web/src/components/Header/ArrayUsage.vue, web/src/locales/en.json, web/components.d.ts
Renders the array usage bar with percentage/color logic and adds the related locale and global component declarations.
HeaderLogo and HeaderVersion subcomponents
web/src/themes/types.d.ts, web/src/components/Header/HeaderLogo.vue, web/src/components/Header/HeaderVersion.vue
Adds the logo style type, the external logo link component, and the version dropdown with copy actions, update status, and changelog modal.
Header.standalone.vue main component
web/src/components/Header.standalone.vue
Combines logo, version, usage, description, notifications, dropdown, and LAN IP copy behavior with grid-based layout and styling.
Legacy HeaderOsVersion refactor
web/src/components/HeaderOsVersion.standalone.vue
Delegates header rendering to the new logo and version subcomponents.
Component registry and generated declarations
web/src/components/Wrapper/component-registry.ts, web/__test__/components/Wrapper/component-registry.test.ts, web/components.d.ts
Registers the standalone header selector first and updates the registry test and global component typings.
Header.standalone test suite
web/__test__/components/Header.test.ts
Adds tests for rendering, hydration, missing data, layout, array usage visibility, and clipboard copy.

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

Suggested reviewers: SimonFair

Poem

A header grew bright with a tidy new face,
With logo and version in well-placed grace.
The array bar hums, and the clipboard pings,
While this bunny does hops for the code that now sings. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: a consolidated header for Unraid 7.3+ and the mobile overlap fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 feat/consolidated-header

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

@elibosley

Copy link
Copy Markdown
Member Author

Companion webgui gate/mount PR: unraid/webgui#2689 (renders <unraid-header> on 7.3+, legacy fallback below).

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.25197% with 60 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.88%. Comparing base (2679fda) to head (deb43f5).

Files with missing lines Patch % Lines
web/src/components/Header/HeaderVersion.vue 70.78% 52 Missing ⚠️
web/src/components/Header/useServerProp.ts 80.55% 7 Missing ⚠️
web/src/components/Header.standalone.vue 98.78% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2037      +/-   ##
==========================================
+ Coverage   52.77%   52.88%   +0.10%     
==========================================
  Files        1035     1041       +6     
  Lines       72060    72241     +181     
  Branches     8303     8322      +19     
==========================================
+ Hits        38031    38204     +173     
- Misses      33903    33911       +8     
  Partials      126      126              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

This plugin has been deployed to Cloudflare R2 and is available for testing.
Download it at this URL:

https://preview.dl.unraid.net/unraid-api/tag/PR2037/dynamix.unraid.net.plg

elibosley added 2 commits July 9, 2026 00:15
Replace the legacy webgui my_usage() widget (jQuery-injected into
#array-usage-sidenav for sidebar themes) with a self-contained ArrayUsage.vue
inside <unraid-header>, driven by the GraphQL array.capacity field. Shown when
webgui passes show-array-usage (sidebar theme + usage display enabled), matching
where the legacy widget appeared. Color thresholds follow the legacy
usage_color() defaults (70% warning / 90% critical).
Rework the consolidated header onto a CSS grid so a single account-actions
block (bell + menu/avatar) repositions across the sm breakpoint without
duplicating stateful components:
- mobile: logo and account icons share the top row (app-style), then version +
  reboot banner, then array usage/status, then server name.
- sm+: two columns — logo/version left, status over name+actions right (the
  prior desktop look).

Splits the logo into Header/HeaderLogo.vue so it can sit on the account row on
mobile; HeaderVersion.vue now renders only the version dropdown + reboot banner.
HeaderOsVersion.standalone.vue (the < 7.3 fallback) composes both and is visually
unchanged. Fixes the over-tall, left-adrift mobile header from the first pass.
@coderabbitai coderabbitai Bot requested a review from SimonFair July 9, 2026 04:36
elibosley added 2 commits July 9, 2026 00:37
<unraid-header> defaulted to display:inline, so the header grid shrink-wrapped
and the account controls bunched against the logo instead of spanning the row.
Add a global unraid-header.unapi { display:block; width:100% } rule (matching the
existing unraid-modals.unapi precedent).
Move the server status (uptime + license tier) up: it shares the top row with
the logo and account controls once there's horizontal room (>=480px), and sits
just under them on the narrowest screens. Version and server name drop down
below. Tightens the vertical rhythm.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
web/src/components/Header/HeaderVersion.vue (1)

36-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove redundant comments.

Several comments here restate what the code already expresses (e.g. "Initialize all stores", "Track if we've loaded the versions yet", "Use lazy query and only load when dropdown is opened"). Consider dropping the ones that don't add clarity.

As per coding guidelines: "Never add comments unless they are needed for clarity of function".

Also applies to: 45-51, 59-59

🤖 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 `@web/src/components/Header/HeaderVersion.vue` at line 36, Remove the redundant
comments in HeaderVersion.vue that only restate the surrounding code, including
the ones near the store initialization and the versions-loading logic. Keep only
comments that add non-obvious clarification, and delete the rest around the
relevant setup and query code in the HeaderVersion component.

Source: Coding guidelines

web/src/components/Header/HeaderLogo.vue (1)

8-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

querySelector('.logo') removes the class from only the first match.

If more than one legacy .logo element can exist in the host DOM, only the first is cleaned up. If a single element is guaranteed this is fine; otherwise use querySelectorAll and iterate. Also note this mutates DOM outside the component's own tree, which is fragile if the surrounding markup changes.

🤖 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 `@web/src/components/Header/HeaderLogo.vue` around lines 8 - 13, The cleanup in
HeaderLogo’s onMounted/nextTick block only removes the legacy class from the
first `.logo` match, so update it to handle all matching legacy elements if
multiple can exist by iterating over the full result set instead of a single
querySelector result. Also keep the logic scoped to the component’s own rendered
markup where possible, since querying the global document is brittle; use the
HeaderLogo wrapper or a local ref if available.
web/src/components/Header/ArrayUsage.vue (1)

22-27: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Query errors are silently swallowed.

Only result is destructured; if the query errors, the bar simply hides (hasData becomes falsy) with no diagnostic signal. Consider also destructuring error and logging it for observability.

♻️ Proposed fix
-const { result } = useQuery(ARRAY_CAPACITY_QUERY, null, {
+const { result, error } = useQuery(ARRAY_CAPACITY_QUERY, null, {
   pollInterval: 30_000,
   fetchPolicy: 'cache-and-network',
 });
+
+watch(error, (err) => {
+  if (err) console.error('[ArrayUsage] failed to fetch array capacity', err);
+});
🤖 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 `@web/src/components/Header/ArrayUsage.vue` around lines 22 - 27, The array
capacity query in ArrayUsage.vue is swallowing failures because only
useQuery(...).result is used and any query error just makes the bar disappear.
Update the useQuery destructuring to also capture error, then add
logging/diagnostic handling in the same component so query failures are
observable even when array data is missing. Keep the existing computed array
usage, but wire error handling around ARRAY_CAPACITY_QUERY in ArrayUsage.
🤖 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 `@web/__test__/components/Header.test.ts`:
- Around line 197-201: The Header test is over-constrained by checking the exact
thrown message text. Update the test in Header.test.ts so it only asserts that
mounting Header without the server prop throws, using the existing mount/Header
setup, and remove the exact string expectation to make the assertion resilient
to wording changes.

In `@web/src/components/Header.standalone.vue`:
- Around line 98-102: The Header.standalone.vue template is rendering the server
description through v-html without sanitization, which can expose stored XSS.
Update the description flow used by the header: sanitize or escape the value
before it reaches the template, and ensure the serverStore description
assignment from data.description does not pass raw HTML through unchanged. Keep
the change localized around the description handling in the header component and
the store field that feeds it.

In `@web/src/components/Header/HeaderVersion.vue`:
- Around line 83-85: The openApiChangelog helper in HeaderVersion.vue opens a
new tab with window.open but does not prevent reverse tabnabbing. Update the
window.open call to explicitly use noopener (and keep _blank) so the GitHub
releases page cannot access window.opener; the fix belongs in the
openApiChangelog function.

---

Nitpick comments:
In `@web/src/components/Header/ArrayUsage.vue`:
- Around line 22-27: The array capacity query in ArrayUsage.vue is swallowing
failures because only useQuery(...).result is used and any query error just
makes the bar disappear. Update the useQuery destructuring to also capture
error, then add logging/diagnostic handling in the same component so query
failures are observable even when array data is missing. Keep the existing
computed array usage, but wire error handling around ARRAY_CAPACITY_QUERY in
ArrayUsage.

In `@web/src/components/Header/HeaderLogo.vue`:
- Around line 8-13: The cleanup in HeaderLogo’s onMounted/nextTick block only
removes the legacy class from the first `.logo` match, so update it to handle
all matching legacy elements if multiple can exist by iterating over the full
result set instead of a single querySelector result. Also keep the logic scoped
to the component’s own rendered markup where possible, since querying the global
document is brittle; use the HeaderLogo wrapper or a local ref if available.

In `@web/src/components/Header/HeaderVersion.vue`:
- Line 36: Remove the redundant comments in HeaderVersion.vue that only restate
the surrounding code, including the ones near the store initialization and the
versions-loading logic. Keep only comments that add non-obvious clarification,
and delete the rest around the relevant setup and query code in the
HeaderVersion component.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f09bf1e7-2145-4745-b4b2-4bc9635fd693

📥 Commits

Reviewing files that changed from the base of the PR and between 2679fda and 1ae725d.

📒 Files selected for processing (14)
  • web/__test__/components/Header.test.ts
  • web/__test__/components/Wrapper/component-registry.test.ts
  • web/components.d.ts
  • web/src/components/Header.standalone.vue
  • web/src/components/Header/ArrayUsage.vue
  • web/src/components/Header/HeaderLogo.vue
  • web/src/components/Header/HeaderVersion.vue
  • web/src/components/Header/arrayCapacity.query.ts
  • web/src/components/Header/useServerProp.ts
  • web/src/components/HeaderOsVersion.standalone.vue
  • web/src/components/Wrapper/component-registry.ts
  • web/src/composables/gql/gql.ts
  • web/src/composables/gql/graphql.ts
  • web/src/locales/en.json

Comment on lines +197 to +201
it('throws when the server prop is missing', () => {
expect(() => mount(Header, { props: {}, global: { plugins: [pinia], stubs } })).toThrow(
'Server data not present'
);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Avoid asserting the exact error message text.

Per coding guidelines, tests should assert that an error is thrown, not the exact wording, to avoid breaking on incidental message changes.

✏️ Proposed fix
   it('throws when the server prop is missing', () => {
-    expect(() => mount(Header, { props: {}, global: { plugins: [pinia], stubs } })).toThrow(
-      'Server data not present'
-    );
+    expect(() => mount(Header, { props: {}, global: { plugins: [pinia], stubs } })).toThrow();
   });
📝 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
it('throws when the server prop is missing', () => {
expect(() => mount(Header, { props: {}, global: { plugins: [pinia], stubs } })).toThrow(
'Server data not present'
);
});
it('throws when the server prop is missing', () => {
expect(() => mount(Header, { props: {}, global: { plugins: [pinia], stubs } })).toThrow();
});
🤖 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 `@web/__test__/components/Header.test.ts` around lines 197 - 201, The Header
test is over-constrained by checking the exact thrown message text. Update the
test in Header.test.ts so it only asserts that mounting Header without the
server prop throws, using the existing mount/Header setup, and remove the exact
string expectation to make the assertion resilient to wording changes.

Source: Coding guidelines

Comment on lines +98 to +102
<template v-if="description && theme?.descriptionShow">
<span
class="hidden truncate text-right text-base md:!inline-flex md:!items-center"
v-html="description"
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if description is sanitized anywhere before or during storage/hydration
rg -n -A3 -B3 'description' web/src/components/Header/useServerProp.ts web/src/store/server.ts 2>/dev/null
rg -n 'sanitize|DOMPurify' web/src -g '*.ts' -g '*.vue'

Repository: unraid/api

Length of output: 7246


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant files first
ast-grep outline web/src/components/Header.standalone.vue --view expanded
ast-grep outline web/src/store/server.ts --view expanded
ast-grep outline web/src/components/Header/useServerProp.ts --view expanded 2>/dev/null || true

# Read the relevant sections around description flow and template rendering
sed -n '1,180p' web/src/components/Header.standalone.vue
sed -n '1,220p' web/src/components/Header/useServerProp.ts
sed -n '1040,1085p' web/src/store/server.ts

# Look for any sanitization or HTML escaping specifically tied to description/header rendering
rg -n -A3 -B3 'v-html|description|sanitize|DOMPurify|escapeHtml|innerHTML' web/src/components web/src/store web/src/composables web/src/helpers -g '*.ts' -g '*.vue'

Repository: unraid/api

Length of output: 50367


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the server contract and any description-related helpers in smaller slices
sed -n '1,220p' web/types/server.ts
sed -n '1,260p' web/src/store/server.ts
sed -n '1,120p' web/src/components/UserProfile.standalone.vue

# Focused search for the server description field and sanitization around the path
rg -n -A2 -B2 'descriptionShow|server\.description|data\.description|description' web/types web/src/components/Header web/src/components/UserProfile web/src/store web/src/composables web/src/helpers -g '*.ts' -g '*.vue'

Repository: unraid/api

Length of output: 50366


Sanitize the server description before v-html
web/src/components/Header.standalone.vue:98-102 renders serverStore.description as raw HTML, and the store copies data.description through unchanged. If that field accepts admin-entered HTML, this is stored XSS for every viewer; escape it or sanitize it first.

🤖 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 `@web/src/components/Header.standalone.vue` around lines 98 - 102, The
Header.standalone.vue template is rendering the server description through
v-html without sanitization, which can expose stored XSS. Update the description
flow used by the header: sanitize or escape the value before it reaches the
template, and ensure the serverStore description assignment from
data.description does not pass raw HTML through unchanged. Keep the change
localized around the description handling in the header component and the store
field that feeds it.

Comment on lines +83 to +85
const openApiChangelog = () => {
window.open('https://github.com/unraid/api/releases', '_blank');
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Add noopener to window.open to prevent reverse tabnabbing.

Unlike anchor target="_blank" (where modern browsers imply noopener), window.open does not apply it automatically, so the opened page can reach back via window.opener.

🛡️ Proposed fix
 const openApiChangelog = () => {
-  window.open('https://github.com/unraid/api/releases', '_blank');
+  window.open('https://github.com/unraid/api/releases', '_blank', 'noopener');
 };
📝 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
const openApiChangelog = () => {
window.open('https://github.com/unraid/api/releases', '_blank');
};
const openApiChangelog = () => {
window.open('https://github.com/unraid/api/releases', '_blank', 'noopener');
};
🤖 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 `@web/src/components/Header/HeaderVersion.vue` around lines 83 - 85, The
openApiChangelog helper in HeaderVersion.vue opens a new tab with window.open
but does not prevent reverse tabnabbing. Update the window.open call to
explicitly use noopener (and keep _blank) so the GitHub releases page cannot
access window.opener; the fix belongs in the openApiChangelog function.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@web/src/components/Header.standalone.vue`:
- Around line 151-192: The header grid in Header.standalone.vue is still fixed
to a two-column layout at all widths, so it needs responsive behavior added
here. Update the .unraid-header-shell styles to use a single-column stacked
layout on mobile, then apply the existing left/right split only at the sm
breakpoint and above. Keep the placement rules for .uh-meta-right, .uh-logo,
.uh-version, and .uh-nav-right aligned with that breakpoint switch.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: da0bc6e8-02ad-4205-9de4-3665d11d32c1

📥 Commits

Reviewing files that changed from the base of the PR and between 1ae725d and ae0a69b.

📒 Files selected for processing (4)
  • web/src/assets/main.css
  • web/src/components/Header.standalone.vue
  • web/src/components/Header/HeaderLogo.vue
  • web/src/themes/types.d.ts
✅ Files skipped from review due to trivial changes (2)
  • web/src/assets/main.css
  • web/src/themes/types.d.ts

Comment on lines +151 to +192
<style scoped>
/*
* Compact mirror: logo and status share the top row; version and server
* controls share the bottom row.
*/
.unraid-header-shell {
display: grid;
column-gap: 0.75rem;
row-gap: 0.25rem;
align-items: stretch;
grid-template-columns: minmax(0, 1fr) auto;
grid-template-rows: auto auto;
}

.uh-meta-right {
grid-column: 2;
grid-row: 1;
align-self: start;
justify-self: end;
}

.uh-logo {
grid-column: 1;
grid-row: 1;
align-self: center;
justify-self: start;
}

.uh-version {
grid-column: 1;
grid-row: 2;
align-self: center;
justify-self: start;
}

.uh-nav-right {
grid-column: 2;
grid-row: 2;
align-self: center;
justify-self: end;
}
</style>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for responsive overrides of the header grid outside this file
rg -n 'unraid-header-shell|uh-meta-right|uh-logo|uh-version|uh-nav-right' web/src/assets/main.css
rg -n '`@media`' web/src/assets/main.css

Repository: unraid/api

Length of output: 179


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant global stylesheet section and any responsive rules for the header classes
rg -n -A20 -B20 'unraid-header-shell|uh-meta-right|uh-logo|uh-version|uh-nav-right|`@media` \(max-width: 768px\)' web/src/assets/main.css

# Show the relevant component template/classes to compare against the stylesheet
rg -n -A40 -B20 'unraid-header-shell|uh-meta-right|uh-logo|uh-version|uh-nav-right' web/src/components/Header.standalone.vue

Repository: unraid/api

Length of output: 5130


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search the whole repo for any responsive overrides or alternate definitions
rg -n -A6 -B6 'unraid-header-shell|uh-meta-right|uh-logo|uh-version|uh-nav-right|grid-template-columns: minmax\(0, 1fr\) auto|grid-template-rows: auto auto' .

# Look for any media queries that mention the header component or these class names
rg -n -A6 -B6 '`@media`|Header\.standalone|UnraidHeader|unraid-header' web/src

Repository: unraid/api

Length of output: 13609


Add the mobile breakpoint for this grid web/src/components/Header.standalone.vue:156-192 keeps .unraid-header-shell locked to a 2-column layout at every width, so the header still won’t stack on small screens. Add the mobile single-column rules here and switch to the left/right split at sm+.

🤖 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 `@web/src/components/Header.standalone.vue` around lines 151 - 192, The header
grid in Header.standalone.vue is still fixed to a two-column layout at all
widths, so it needs responsive behavior added here. Update the
.unraid-header-shell styles to use a single-column stacked layout on mobile,
then apply the existing left/right split only at the sm breakpoint and above.
Keep the placement rules for .uh-meta-right, .uh-logo, .uh-version, and
.uh-nav-right aligned with that breakpoint switch.

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