fix: OS updates UX improvements — merged packages view, hold/unhold, modal accessibility - #25
Merged
Conversation
- Implemented a new JavaScript utility to enforce character limits on input fields and textareas. - Added visual feedback with a character counter that changes color based on proximity to the limit. - Integrated the utility to initialize on DOMContentLoaded. refactor: overhaul toast notification system - Enhanced the toast notification system with new features including dismissible toasts, custom buttons, and progress bars. - Introduced a more flexible API for showing toasts with options for duration and persistence. - Improved styling and structure for better user experience and maintainability. - Added drag-to-dismiss functionality and ensured unique IDs for each toast. - Implemented comprehensive tests for the toast library to ensure reliability. test: add unit tests for toast library functionality - Created a test suite for the toast library covering various scenarios including creation, dismissal, and custom button functionality. - Ensured that the toast system behaves correctly under different conditions and configurations.
… add reboot state management and polling; enhance package upgrade/uninstall functionality for pip and pipx; implement live PyPI package lookup; improve UI feedback during operations.
… package management functionality
…modal accessibility, and cleanup - Merge Available Updates and Installed Packages into single OS Packages section with Updates/All Packages filter dropdown; lazy-load full package list only when needed - Add hold/unhold package support with held badge in grid and Unhold action button - Remove Revert package feature (older versions not reliably available in apt repos) - Available Version column shows current version in muted text when up to date, green when an update exists - Normalize upgradable package shape for grid so Updates view loads fast (small list) - Add PageURL to upgradable packages endpoint - Add Escape key handler for all modals app-wide (OS updates + HAProxy editor) - Focus primary action/close button when modals open for keyboard navigation - Enter key submits confirm modal when input field is shown - Add hold/unhold API routes and handlers (agent + dashboard) - Remove GetPackageVersions from PackageManager interface and all implementations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enhances the OS Updates gear UX and expands underlying package-management capabilities across the dashboard and agent, including new hold/unhold actions, Python tools status, modal accessibility improvements, and richer agent-side operations/logging.
Changes:
- Reworked toast utilities (new dismissal APIs, options, progress/drag behavior) and added a character-limit helper used across multiple templates.
- Expanded OS updates + package management APIs/models (installed packages listing, hold/unhold, snapshot preview/restore streaming, persisted update logs; broader distro/package-manager support on the agent).
- Tightened input length validation across several handlers/templates and adjusted agent operational settings (systemd unit + deploy process).
Reviewed changes
Copilot reviewed 46 out of 46 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| gearbox/static/js/utils/toast.test.html | Adds a browser-based test page for the toast utility. |
| gearbox/static/js/utils/toast.js | Rewrites toast system with options, timers/progress bar, drag-to-dismiss, and new public APIs. |
| gearbox/static/js/utils/char-limit.js | Adds a generic char-limit counter utility driven by data-charlimit. |
| gearbox/static/js/haproxy_config/editor.js | Improves modal focus management and adds Escape-to-close for multiple modals. |
| gearbox/internal/gears/os_updates/handlers.go | Updates OS updates page data wiring (Python tools status field). |
| gearbox/internal/framework/templates/pages/login.templ | Adds maxlength constraints to login inputs. |
| gearbox/internal/framework/templates/pages/haproxy_settings.templ | Adds maxlength/data-charlimit constraints to HAProxy server form fields. |
| gearbox/internal/framework/templates/pages/haproxy_config/modals.templ | Adds maxlength/data-charlimit constraints for modal inputs. |
| gearbox/internal/framework/templates/pages/gears.templ | Renames Pipx setting UI to “Python tools” and updates config accessor. |
| gearbox/internal/framework/templates/pages/alerts.templ | Adds maxlength/data-charlimit to action note textareas. |
| gearbox/internal/framework/templates/pages/alert_rules.templ | Adds maxlength/data-charlimit to rule name/description inputs. |
| gearbox/internal/framework/templates/layouts/base.templ | Adjusts Tabulator header filter styling; loads char-limit.js. |
| gearbox/internal/framework/templates/components/table.templ | Adds maxlength to table search input. |
| gearbox/internal/framework/handler/users.go | Adds server-side max-length validation for multiple user/account forms. |
| gearbox/internal/framework/handler/os_updates_error_test.go | Adds tests to prevent duplicated “Failed to …” error prefixes with JS error handling. |
| gearbox/internal/framework/handler/login.go | Adds server-side max-length validation for login credentials. |
| gearbox/internal/framework/handler/haproxy_config.go | Adds server-side max-length validation for HAProxy box create/update. |
| gearbox/internal/framework/handler/gears.go | Updates OS updates config parsing to use ShowPythonTools. |
| gearbox/internal/framework/database/gears.go | Renames config field to ShowPythonTools (keeps json:"show_pipx" for compatibility) and updates defaults. |
| gearbox/internal/framework/agent/models.go | Extends agent API models (installed packages metadata, holds, snapshot preview, update logs, pip status, combined python tools). |
| gearbox/internal/framework/agent/client_test.go | Adds tests for update logs endpoints and improved plain-text HTTP error propagation. |
| gearbox/internal/framework/agent/client.go | Improves error parsing; adds long-running request path; adds multiple OS updates/package mgmt client methods and route updates. |
| gearbox/cmd/server/main.go | Registers new dashboard routes for OS updates (preview, installed packages, hold/unhold, logs, operation status, pip/pypi endpoints). |
| gearbox-agent/internal/gears/updates/updates_test.go | Adds tests for parsing update history logs and command error output preservation. |
| gearbox-agent/internal/gears/updates/pm_zypper.go | Adds zypper package manager implementation. |
| gearbox-agent/internal/gears/updates/pm_yum.go | Adds yum package manager implementation. |
| gearbox-agent/internal/gears/updates/pm_pacman.go | Adds pacman package manager implementation. |
| gearbox-agent/internal/gears/updates/pm_interface.go | Introduces a common PackageManager interface. |
| gearbox-agent/internal/gears/updates/pm_dnf.go | Adds dnf package manager implementation. |
| gearbox-agent/internal/gears/updates/pm_apt.go | Adds apt package manager implementation (history parsing, holds, installed/search, snapshots). |
| gearbox-agent/internal/gears/updates/pm_apk.go | Adds apk package manager implementation. |
| gearbox-agent/internal/gears/updates/detector.go | Detects and caches appropriate package manager based on OS/distro info. |
| gearbox-agent/internal/gears/updates/apt_runner_test.go | Adds tests around streaming runner behavior and log serialization. |
| gearbox-agent/internal/gears/updates/apt_runner.go | Adds persistent update logs + streaming restore support and log capture. |
| gearbox-agent/internal/gears/security/plugin.go | Adds length validation to security gear request fields. |
| gearbox-agent/internal/framework/events/bus.go | Clarifies apt event semantics as “package manager” events (keeps names for compatibility). |
| gearbox-agent/deploy/gearbox-agent.service | Changes runtime environment and relaxes hardening to allow broader system operations. |
| gearbox-agent/Makefile | Deploy now ships the systemd service file and reloads daemon on target host. |
- toast.js: fix enforceMaxToasts() infinite loop by splicing the toast from activeToasts synchronously before calling dismiss(), so the array length decreases each iteration - apt_runner.go: guard GetUpdateLog against panic when id < 8 chars; return an error instead of slicing out of bounds - Remove toast.test.html from static/ (publicly served); test harness should not ship in production builds - Add validateSnapshotID() to reject snapshot IDs containing path separators or other unsafe characters, preventing path traversal in updates.go, apt_runner.go, and pm_apt.go - Restore systemd hardening: add ProtectHome=read-only, PrivateTmp=true, and ProtectKernelTunables=true while keeping NoNewPrivileges=false and omitting ProtectSystem=strict (agent needs write access to /etc and /var) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove hardcoded API key from functional_test.go comment; replace with <YOUR_API_KEY> placeholder that matches gitleaks allowlist pattern - Fix runCommandWithOutput to return errors.New(errDetail) instead of wrapping the raw exit error — prevents bare "exit status N" suffix in user-facing messages and avoids including "Failed to" prefixes from systemctl/shutdown output in error responses - Apply same fix to runPipxCommandWithOutput and runPipCommandWithOutput - Add "Failed to" prefix exclusion in extractErrorLines fallback path Fixes TestHandleScheduleReboot, TestHandleConfigureUnattended, TestAllEndpointsNoFailedToPrefix, TestTriggerUpdateCheck_ErrorIncludesAptOutput, and TestFullErrorChain_CheckForUpdates. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add isValidPackageName() validation to HoldPackage and UnholdPackage in pm_apt.go, consistent with InstallPackage and RemovePackage - Fix unsupportedPM.ListHeldPackages() to return errNotSupported instead of (nil, nil), making it consistent with all other unsupportedPM stubs - Log a warning instead of silently ignoring MkdirAll error in NewAptRunner - Guard initCharLimit against elements not yet in the DOM (no parentElement) by returning early if parentElement is nil Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Commit 2fdb5ba introduced functional_test.go with an example API key value in a doc comment (for illustrating how to run the test). It was not a real credential and has since been replaced with <YOUR_API_KEY> placeholder, but gitleaks scans the full commit history so the original commit still triggers the rule. Allowlist the commit SHA explicitly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moving el._charLimitInit = true to after the parentElement guard prevents elements called before DOM insertion from being permanently marked as initialized. Now callers can safely retry initCharLimit() after inserting the element into the DOM. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test Plan
Closes #18
🤖 Generated with Claude Code