No description
  • JavaScript 64.2%
  • Vue 34.6%
  • Makefile 0.5%
  • HTML 0.4%
  • CSS 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-18 03:22:22 +03:00
docs Keep hash-route reloads compatible with fragment-forwarding proxies 2026-10-18 03:16:05 +03:00
screenshots Build qBittorrent Web UI with GitLab Pajamas 2026-10-18 01:17:05 +03:00
scripts Serve external SVG sprite with WebKit proxy compatibility 2026-10-18 03:12:59 +03:00
src Remove decorative logo from torrent-list heading 2026-10-18 03:22:22 +03:00
tests Add native Pajamas menus for torrent creation 2026-10-18 03:21:18 +03:00
third_party Introduce a faceted Q mark shared across the interface and favicon 2026-10-18 02:59:34 +03:00
.gitignore Build qBittorrent Web UI with GitLab Pajamas 2026-10-18 01:17:05 +03:00
CHANGELOG.md Remove decorative logo from torrent-list heading 2026-10-18 03:22:22 +03:00
index.html Keep hash-route reloads compatible with fragment-forwarding proxies 2026-10-18 03:16:05 +03:00
Makefile Build qBittorrent Web UI with GitLab Pajamas 2026-10-18 01:17:05 +03:00
package-lock.json Build qBittorrent Web UI with GitLab Pajamas 2026-10-18 01:17:05 +03:00
package.json Serve external SVG sprite with WebKit proxy compatibility 2026-10-18 03:12:59 +03:00
postcss.config.cjs Build qBittorrent Web UI with GitLab Pajamas 2026-10-18 01:17:05 +03:00
README.md Remove decorative logo from torrent-list heading 2026-10-18 03:22:22 +03:00
tailwind.config.cjs Build qBittorrent Web UI with GitLab Pajamas 2026-10-18 01:17:05 +03:00
vite.config.js Serve external SVG sprite with WebKit proxy compatibility 2026-10-18 03:12:59 +03:00

qBittorrent Pajamas

Alternative Web UI for qBittorrent-nox, built with the actual GitLab UI components implementing the Pajamas Design System.

This is a Vue application using @gitlab/ui, @gitlab/svgs and @gitlab/fonts. Navigation, buttons, tables, progress bars, badges, search, forms, modals, tabs, menus and drawers are the library components. The official Pajamas Tailwind preset is used without theme extensions. There are no custom component styles or replacement colors, shadows, radii or typography. The app only registers the original GitLab Sans font files and emits the upstream CSS utilities.

GitLab UI 138.6.1 is the design reference. Its current semantic palette, including the selected-navigation indicator, takes precedence over screenshots of older GitLab releases. Component colors and radii are not backported.

The default GitLab Neutral theme follows the operating system's light/dark mode. There are no appearance selectors.

The workspace follows GitLab's panel layout: global search and user actions in the top bar, a collapsible neutral sidebar, and an inset content panel with native borders, rounding and shadow. Breadcrumbs remain inside the content panel. The content fills the available workspace width, with four native summary cards and a bordered transfer section. The name column receives the remaining table width while numeric/status columns retain their required space. On mobile, search stays in the top bar and navigation uses the GitLab drawer. The 240px sidebar is shown from 1200px upward; below that breakpoint it opens over the workspace. The original navigation indicator and keyboard focus remain visible inside the scrolling list, and the active filter is announced as a pressed button. All navigation groups use the documented semantic list with a 4px gap, so adjacent hover/selected backgrounds stay separate. Keyboard focus also has an inset outline in forced-colors mode, where the browser suppresses shadow focus rings.

Pajamas spacing tokens keep related controls 8px apart, content groups 16–24px apart, and table rows compact. Page-level surfaces use the 24px radius token, content regions use 12px, and controls retain their native 8px radius. The geometry review records measured values and verification. Cell spacing uses the component's thClass and tdClass API and upstream utilities. Torrent names stay on one line; full names remain available in the details panel and title tooltip.

Features

  • Login/logout with the qBittorrent session cookie; automatic session-expiry handling.
  • Live torrent and transfer data, status/category filters, search and pagination.
  • Column sorting and selection that stays stable across polling updates.
  • Resume/pause selected torrents or all torrents, add magnet links/URLs or .torrent files.
  • Native add menus in the top bar and workspace: choose a magnet link/URL or a local .torrent file to open the corresponding form.
  • Removal confirmation with an explicit option to also delete downloaded data.
  • A docked bottom panel with general details, files, peers and trackers.
  • Independent list/details scrolling, normal and expanded panel sizes, and a full-workspace details view on narrow or short screens.
  • The active details tab and panel size stay selected when inspecting other torrents or closing/reopening the panel. Background polling preserves the current table and scroll position.
  • A dedicated Settings page with nine linked sections, search and 153 supported preference fields; only changed settings and required companion flags are sent.
  • Mobile navigation through the GitLab drawer and horizontally scrollable data tables.
  • qBittorrent 5.x activity endpoints with a fallback to legacy 4.x endpoints.

All API requests use the same origin as the Web UI. Fonts and SVG sprites are bundled locally; running the theme does not require a CDN or GitLab account. The original GitLab sprite is a separate, hashed SVG file in assets/. Native GlIcon components reference its symbols with icons-<hash>.svg?sprite=1#name. The query marker keeps references compatible with WebKit, HTTP proxies and qBittorrent 4.6's request-path parser. Vite asset inlining is disabled; the sprite is copied into both public/private asset directories so it is available before and after authentication. The SVG delivery notes document the reproduced WebKit/proxy failure and the compatibility URL. The entry page similarly adds ?ui=1 when there is no query, preserving hash-route reloads through the same proxy.

Sign-in screen

The standalone sign-in page follows GitLab's compact layout: a centered faceted qbt-pajamas mark and form, open space around the controls, and resource links at the bottom. The original GitLab Bloom artwork is bundled with the theme. The form uses unmodified Pajamas fields, password visibility control, alert and confirm button. Light/dark mode follows the system.

The original SVG mark uses GitLab brand oranges and is shared by sign-in, the top bar and the favicon. It is bundled once as a hashed asset so a logo update also refreshes the favicon. Design references and upstream artwork licenses are recorded in third_party/. Fonts, icons, the logo and the background load from the qBittorrent host; the resource links only navigate to their external destinations when clicked.

Details panel

Click a torrent name to open its information below the list. While the panel is open, a plain row click inspects another torrent and selects that row. Checkboxes and modifier-clicks change the batch selection without changing the inspected torrent. The Viewing badge identifies the inspected object separately from the batch selection.

The panel uses the original GlScrollableTabs, GlTableLite, buttons, badges, tooltips and spacing utilities. Its header and tabs remain visible while its content scrolls. Use Expand details / Restore panel size to switch between approximately 40% and 60% of the workspace height; the list retains at least 240px. Opening or closing the panel preserves the list position and column widths; the inspected badge reserves its space, and the scrollbar gutter remains stable. Explicitly resizing the split keeps the inspected row visible. Only layout sizing is calculated dynamically; no component CSS is overridden and there is no custom resize control.

Below 768px workspace width or 560px workspace height, details take the workspace and Back returns to the same list position. Closing details restores focus to the torrent name. Escape closes details while focus is inside the panel, without closing it behind an open modal. Searching, changing the list page or navigating to a different category/filter dismisses the previous context. A torrent that leaves the current filter during live updates remains inspectable with an Outside current filter notice.

Forms and navigation

Settings is a full workspace page, using native Pajamas forms and the contextual sidebar rather than a modal. Direct links such as #settings/speed support browser Back/Forward. Downloads, Connection, Speed, BitTorrent, RSS, Notifications, Automation, Web UI and Advanced have grouped controls; search finds fields across all sections. Narrow screens use a native section selector.

The field schema currently covers 153 keys returned by qBittorrent 4.6.3. Controls are shown only for keys exposed by the connected server. Dependent fields appear when their controlling option is enabled. Watched folders use a directory editor. Global rate limits display KiB/s and convert to/from the API's bytes/s. Passwords are never prefilled; blank password fields preserve the current credentials.

Drafts survive section changes and visits to the torrent list. Save and Discard remain visible at the bottom of the workspace. Validation opens the relevant section, focuses the invalid field and associates native Pajamas error feedback. Enter submits the form. Pending saves continue across page navigation, disable editing/sign-out and retain errors and drafts for retry. Closing/reloading the browser with unsaved changes triggers its native warning. No preference changes are sent before an explicit Save. See API mapping and sources.

While an add or remove request is pending, Cancel, Escape, the backdrop and header close action cannot dismiss the operation. Errors remain visible in the dialog; success and session expiry can still close it. The parent breadcrumb returns to all torrents and clears status/category/search filters. Icon-only actions have native Pajamas tooltips for hover and focus.

Build and install on the qBittorrent host

Requirements: GNU Make, Node.js 22.12+ (or 20.19.x), and npm. Run the following inside this repository as the user running qBittorrent-nox, without sudo:

make install

make does the same thing. It installs the locked dependencies with npm ci (including build tools even when NODE_ENV=production), builds the application, prepares public/ and private/, and installs the theme for the current user. No ZIP or archive tool is involved.

The default installation directory is:

${XDG_DATA_HOME:-$HOME/.local/share}/qBittorrent/themes/qbt-pajamas/
├── public/
│   ├── index.html
│   ├── login.html
│   └── assets/
├── private/
│   ├── index.html
│   └── assets/
└── THIRD-PARTY-NOTICES.txt

Override the destination if needed (paths with spaces are supported):

make install THEME_DIR="$HOME/.local/share/qBittorrent/themes/qbt-pajamas"

In qBittorrent's Web UI preferences, enable Alternative Web UI and enter the absolute directory printed by make install (the parent of public/ and private/). Reload the UI. The installer does not edit qBittorrent's configuration or restart its service.

Run make install again after updating the repository. The installer stages the complete build beside the destination before replacing the old theme, removes obsolete assets, and restores the old directory if the final rename fails. An unrelated nonempty directory or a symbolic link cannot be used as the destination.

make build    # Build dist/public and dist/private without installing
make test     # Run API, model, installer and design-system tests
make clean    # Remove dist/; keep the installed theme
make help     # Show targets and the resolved installation directory

Development

QBT_URL=http://127.0.0.1:8080 npm run dev

Vite proxies /api to QBT_URL during development. Use the credentials of that qBittorrent instance. Production needs no proxy setting because qBittorrent serves the files itself.

npm run build       # Standalone Vite build in dist/
npm run preview     # Preview that build (configure a backend proxy separately)
npm run build:theme # Prepare public/private structure without installing
npm test            # API, filtering, selection, settings and design-system checks
npm run test:ui     # Build and browser integration tests using only a mocked API

For browser tests, install Chromium with npx playwright install chromium, or set BROWSER_EXECUTABLE to a local Chromium binary. The tests also detect the Chromium application on macOS. Run the same checks on Safari's engine with npx playwright install webkit followed by BROWSER=webkit npm run test:ui. The browser checks verify that icons paint from the same-origin external sprite and the details panel on a 1,000-torrent fixture, including narrow/short screens, selection, delayed responses, background errors and independent scrolling. The UI consistency suite also checks panel layout stability, navigation indicators, accessible filter state, hidden-field validation and pending modal requests. The settings-page suite covers all nine sections in light/dark mode at 320, 390 and 1512px, cross-section validation, history, retained drafts, typed API payloads, password handling, conditional fields, watched folders, partial server responses and delayed/failed requests. Its 4.6 fixture is a sanitized server response with paths, credentials and other free-text values removed. No real torrent server is modified by the tests.

Design system and dependencies

GitLab UI's Vue peer dependency determines the Vue version. Library versions and integrity hashes are recorded in package-lock.json. Updates should be tested against the official component documentation and GitLab UI CSS integration guidance.

The application is a qBittorrent theme and is not affiliated with GitLab. Upstream GitLab UI and SVGs use the MIT license; GitLab Sans uses OFL-1.1. The prepared and installed theme includes third-party notices.

Pajamas torrent workspace