mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-14 07:07:33 -04:00
* feat(ui): add installed model lifecycle Models now owns catalog exploration and installed runtime controls under one canonical route. URL-owned state keeps lifecycle context recoverable through links and browser history. Assisted-by: Codex:gpt-5 Playwright * feat(ui): add installed backend lifecycle Backends split discovery from backend-binary management. The canonical page now keeps both lifecycle views under one URL-backed shell while it preserves target-node placement. Assisted-by: Codex:gpt-5 Playwright * fix(ui): repair lifecycle state updates Installed models lost distributed refreshes and kept a deleted selection. Backend searches also stopped tracking URL changes, while batch upgrades stopped after their first error. Preserve background refreshes and finish each requested batch action. Drive catalog results from URL-backed state without losing full metadata. Assisted-by: Codex:gpt-5 [Playwright] * feat(ui): make resource pages canonical Replace Host navigation with canonical Models and Backends lifecycle routes, preserve legacy management URLs, and surface shared host capacity on the Operate overview. Assisted-by: Codex:gpt-5 [Playwright] * feat(ui): complete canonical resource lifecycle Finish the responsive list-to-detail behavior, remove the retired Host implementation, and keep Explore focused on discovery while Installed owns destructive actions. Update regression coverage, localization, documentation, and development binding for the canonical resource pages. Assisted-by: Codex:gpt-5 [Playwright] * docs(ui): record the UI design context Record the approved users, brand character, and design principles so future interface work uses the same product direction. Index the context from the repository's agent instructions. Assisted-by: Codex:gpt-5 --------- Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
51 lines
2.3 KiB
JavaScript
51 lines
2.3 KiB
JavaScript
import { test, expect } from './coverage-fixtures.js'
|
|
|
|
// A standing guard against the two defects an earlier automated edit left
|
|
// scattered through the pages: icons stripped of their fa-* class (which render
|
|
// nothing at all), and controls left with the user agent's own chrome, which is
|
|
// a pale grey button on a dark ground.
|
|
const ROUTES = [
|
|
'/app', '/app/chat', '/app/models', '/app/studio', '/app/talk',
|
|
'/app/agents', '/app/skills', '/app/collections', '/app/agent-jobs',
|
|
'/app/fine-tune', '/app/quantize', '/app/face', '/app/voice',
|
|
'/app/models?view=installed', '/app/backends', '/app/activity', '/app/operate',
|
|
'/app/settings', '/app/traces', '/app/usage', '/app/nodes', '/app/p2p',
|
|
'/app/voice-library', '/app/voice-library/new', '/app/account',
|
|
]
|
|
|
|
test('no page renders a dead icon or a default-chrome control', async ({ page }) => {
|
|
// One test walks every route, so its budget has to scale with the list rather
|
|
// than sit on Playwright's per-test default of 30s. At 25 routes that default
|
|
// allows ~1.2s per navigation, which holds on a developer machine and does
|
|
// not on a loaded CI runner: the suite went red on the commit that added this
|
|
// spec, timing out mid-loop at waitForTimeout rather than at any single goto,
|
|
// which is what cumulative slowness looks like as opposed to one hung route.
|
|
// Six seconds a route absorbs a slow runner and still fails promptly if a
|
|
// route really does hang.
|
|
test.setTimeout(ROUTES.length * 6_000)
|
|
|
|
const findings = []
|
|
for (const route of ROUTES) {
|
|
await page.goto(route)
|
|
await page.waitForTimeout(400)
|
|
const found = await page.evaluate(() => {
|
|
const out = []
|
|
for (const el of document.querySelectorAll('button, a')) {
|
|
if (el.getBoundingClientRect().width === 0) continue
|
|
const cs = getComputedStyle(el)
|
|
if (cs.borderTopStyle === 'outset' || cs.backgroundColor === 'rgb(239, 239, 239)') {
|
|
out.push(`default-chrome: "${(el.textContent || '').trim().slice(0, 24)}" [${el.className}]`)
|
|
}
|
|
}
|
|
for (const i of document.querySelectorAll('i')) {
|
|
if (!/\bfa-/.test((i.className || '').toString())) {
|
|
out.push(`dead-icon: [${i.className}]`)
|
|
}
|
|
}
|
|
return [...new Set(out)]
|
|
})
|
|
for (const f of found) findings.push(`${route} — ${f}`)
|
|
}
|
|
expect(findings).toEqual([])
|
|
})
|