fix(webui): ARIA-valid hover text, alias metadata guard, context-chip polish

CompactNumber.vue and ProviderRateBadge.vue both put aria-label on a
plain <span> — axe's aria-prohibited-attr rule: a <span>'s implicit
ARIA role ("generic") does not support accessible-name computation at
all, so the attribute was simply invalid there, not just discouraged.
Both now use the visible-text-hidden-plus-sr-only-full-text pattern
instead: the compact/label text is marked aria-hidden="true", and a
sr-only sibling span carries the exact value/detail for assistive tech.
title stays on the outer element for mouse hover, unchanged. Folded
review minor.

ProvidersView.vue: the alias table's ModelChip cell read
row.original.modelMeta.contextTokens without a guard, unlike the
provider path's own modelMetaFor fallback — a response from a server
build older than this field (version skew) would throw instead of just
omitting the hover detail. Now optional-chained to match. The visible
context chip beside each model gains title="context window" and
text-xs (was an arbitrary text-[10px]), matching the rest of the panel's
muted-label sizing convention. Folded review minors.
This commit is contained in:
2026-08-22 02:13:12 +01:00
parent 5e3cf919d9
commit 9b60c0afae
3 changed files with 44 additions and 18 deletions
+11 -4
View File
@@ -179,12 +179,18 @@ const aliasColumns: ColumnDef<AdminAliasView, unknown>[] = [
id: 'alias',
header: 'Alias',
accessorFn: (a) => a.alias,
// Optional-chained (review fix, folded minor) the same way
// modelMetaFor guards the provider path below: AdminAliasView.
// modelMeta is typed as always-present, but a real response from a
// server build older than this field (version skew) would otherwise
// throw on `.modelMeta.contextTokens` here instead of just omitting
// the hover detail.
cell: ({ row }) =>
h(ModelChip, {
id: row.original.alias,
contextTokens: row.original.modelMeta.contextTokens,
inputPerMTokUsd: row.original.modelMeta.inputPerMTokUsd,
outputPerMTokUsd: row.original.modelMeta.outputPerMTokUsd,
contextTokens: row.original.modelMeta?.contextTokens,
inputPerMTokUsd: row.original.modelMeta?.inputPerMTokUsd,
outputPerMTokUsd: row.original.modelMeta?.outputPerMTokUsd,
}),
},
{
@@ -331,7 +337,8 @@ const aliasEmptyMessage = computed(() =>
-->
<span
v-if="modelMetaFor(p, m).contextTokens !== undefined"
class="text-[10px] text-muted-foreground tabular-nums"
title="context window"
class="text-xs text-muted-foreground tabular-nums"
>
{{ formatContextWindow(modelMetaFor(p, m).contextTokens as number) }}
</span>