From 45cb3983ee6ea4c34525f293d6a3d6264d98c9ea Mon Sep 17 00:00:00 2001 From: "mudler's LocalAI [bot]" <139863280+localai-bot@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:07:42 +0200 Subject: [PATCH] fix(ui): unmerge the class strings that left buttons in browser chrome (#11462) Eight header controls across seven pages had two or three elements' classes collapsed into one string. The wrapper or the icon ended up wearing the button classes, and the buttons themselves were left with no class at all, so they rendered in the browser's own chrome. Reported on Agent Jobs; the grep found the rest. `fas` does not draw anything by itself: it sets `font-family: "Font Awesome 6 Free"` and weight 900 on whatever carries it, and the `fa-*` class supplies the glyph via ::before. So styles the flex wrapper as a button that is both primary and secondary, points two glyphs at one ::before, and leaves both real buttons bare. Fixed, all of them keeping the correct `` child they already had: - AgentJobs, AgentTaskDetails (x2), AgentCreate - icon classes off the button. - AgentTaskDetails, AgentJobDetails - wrapper back to plain `hstack`, and the two buttons inside each get the variants the wrapper had been holding. Back is secondary and leads, Edit/Cancel is the emphatic one and trails, matching every other detail header. - VoiceLibrary, VoiceProfileCreate - the title `` had swallowed the action link's classes, so "Create voice" and "Back to library" were unstyled anchors. Back was also drawing a "+" because it had inherited fa-plus while its own fa-arrow-left sat up in the title. - P2P - a stray fa-circle-info on the title icon. The ninth instance was ImportModel, where this class of bug was first found. #11461 rewrote that file and landed first, so nothing is left to fix there. Guarded by e2e/class-hygiene.spec.js, which reads the source rather than walking routes: several of these pages need agent or voice data before they render a header, so a route walk would skip exactly the pages that had the bug. It fails on an icon-font class outside an ``/``, on two glyphs or two button variants on one element, and on a layout wrapper that is also a button. Font Awesome modifiers (fa-spin, fa-fw, sizes) are excluded, so the `fa-spinner fa-spin` idiom stays legal. e2e: 428 passed. Assisted-by: Claude Code:claude-opus-5[1m] [Read] [Edit] [Bash] [Playwright] Signed-off-by: Ettore Di Giacinto Co-authored-by: Ettore Di Giacinto --- core/http/react-ui/e2e/class-hygiene.spec.js | 111 ++++++++++++++++++ core/http/react-ui/src/pages/AgentCreate.jsx | 2 +- .../react-ui/src/pages/AgentJobDetails.jsx | 10 +- core/http/react-ui/src/pages/AgentJobs.jsx | 2 +- .../react-ui/src/pages/AgentTaskDetails.jsx | 12 +- core/http/react-ui/src/pages/P2P.jsx | 2 +- core/http/react-ui/src/pages/VoiceLibrary.jsx | 4 +- .../react-ui/src/pages/VoiceProfileCreate.jsx | 4 +- 8 files changed, 129 insertions(+), 18 deletions(-) create mode 100644 core/http/react-ui/e2e/class-hygiene.spec.js diff --git a/core/http/react-ui/e2e/class-hygiene.spec.js b/core/http/react-ui/e2e/class-hygiene.spec.js new file mode 100644 index 000000000..f9a1a9625 --- /dev/null +++ b/core/http/react-ui/e2e/class-hygiene.spec.js @@ -0,0 +1,111 @@ +import { test, expect } from '@playwright/test' +import { readFileSync, readdirSync, statSync } from 'node:fs' +import { join, dirname, relative } from 'node:path' +import { fileURLToPath } from 'node:url' + +// Font Awesome's `fas` / `far` / `fab` classes do not draw an icon on their +// own — they set `font-family: "Font Awesome 6 Free"` and a weight on whatever +// element carries them, and the matching `fa-*` class supplies the glyph +// through ::before. Put them on a