feat(web): declare a provider and its models from the Models page
The Models page could name a provider's key and little else. Adding an OpenAI-compatible gateway meant opening $DSH_HOME/settings.yaml and knowing the profile shape; correcting a stale context window meant the same. This layer puts both on the page: a card that declares a route pi-ai does not ship — id, endpoint, protocol, key, models — and a model list on the pi-ai editor that can ask the provider what it serves and adopt the answer. It follows the DeepSeek catalog editor that landed in #1050 rather than inventing a second look for the same job. Both editors now share the section shell and heading, the danger-tinted delete, the add-model button, the empty state, the per-row validator that names a bad row by its position, and one K/M capacity vocabulary — 256K and 1M are read and spelled back, while settings.yaml still stores plain token counts. The row type is structurally open like that editor's, so a profile field this card does not edit survives an edit here. Three of that editor's decisions replaced weaker ones this branch had made. Inheritance now reads the composition base rather than the effective value, which would echo an override back the moment a reset dropped it. Validation names the offending row instead of stating a blanket problem. And emptying the list is no longer conflated with handing the catalog back to the adapter — those are separate acts, with separate affordances. The create write carries the revision the card opened at, so a route another tab declared meanwhile is a conflict rather than a silent overwrite of its profile.
This commit is contained in:
@@ -1,8 +1,18 @@
|
||||
/**
|
||||
* Models section stylesheet contract, asserted against the CSS text on disk.
|
||||
*
|
||||
* The section paints in both themes, and a `--dsw-*` name the theme does not
|
||||
* declare fails silently: the browser takes the `var()` fallback, so the sheet
|
||||
* still renders and only the dark theme looks wrong. Checking the names against
|
||||
* the sheet that declares them is what turns that into a test failure.
|
||||
*/
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const css = readFileSync(fileURLToPath(new URL('../src/client/ModelsSection.module.css', import.meta.url)), 'utf8')
|
||||
// The theme package maps `./styles/*` to `./src/styles/*`, so the declarations
|
||||
// stay on the source plane rather than needing a build.
|
||||
const tokens = readFileSync(
|
||||
fileURLToPath(new URL('../../ui-theme/src/styles/design-platform.css', import.meta.url)),
|
||||
'utf8',
|
||||
@@ -35,4 +45,10 @@ describe('ModelsSection theme styles', () => {
|
||||
expect(block('.rowCard')).toContain('border: 1px solid var(--dsw-alias-border-l2)')
|
||||
expect(block('.rowCard')).not.toMatch(/\bbackground\s*:/)
|
||||
})
|
||||
|
||||
it('never falls back to a literal colour', () => {
|
||||
// A token that resolves is never the problem; an undeclared one takes this
|
||||
// branch, and a literal here is a single colour for both themes.
|
||||
expect(css).not.toMatch(/var\(--dsw-[a-z0-9-]+\s*,\s*(?:#|rgb|rgba|hsl|hsla)/)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user