* feat(token): support custom auto group order
* feat(keys): enhance auto group presentation
* fix(keys): rework Auto flow border and compact inherited order
The Auto group highlight previously tinted the whole control surface
with a gradient and animated only a 1px top sweep, which read as a
background color rather than a flowing border. Replace it with a
border-only effect: an aria-hidden, pointer-events-none overlay whose
conic gradient is masked down to a thin ring hugging the rounded
perimeter, so the highlight travels around all four edges and corners
every 3.2s. The interior stays neutral with a restrained static
primary border and glow; prefers-reduced-motion hides the moving
layer while keeping the static emphasis.
The inherited global Auto order also rendered as spacious two-line
rows with circular sequence markers, wasting drawer space. Render it
as a compact wrapping strip of one-line chips (index, name, ratio
badge) with descriptions kept accessible via title and sr-only text,
scrolling only past a much smaller max height.
Custom add/remove/reorder editing, empty-array inheritance semantics,
and the submit payload are unchanged.
* fix(keys): preserve Auto inheritance and unify effects
* refactor(keys): temporarily disable AutoGroupBadge in api-key-group-cell
* fix(oauth): stop treating a foreign window.opener as a bind flow
The /oauth/:provider callback decided between an account bind and a plain
login with `window.opener ? 'bind' : 'login'`. Any tab opened from an
external link (target="_blank", Slack, mail clients, another site) carries
a live opener, and that opener survives the cross-origin round trip to the
identity provider. Such a login callback was therefore misread as a bind:
it posted a handshake to a window that speaks no such protocol, showed the
"binding your account" screen, and hung until the 30s deadline fired with
"OAuth binding timed out" — while the backend was never called at all.
Reproduced against a real Keycloak round trip: a tab opened via window.open
still reports window.opener !== null on the callback, so mode resolved to
'bind' for an ordinary OIDC login.
A bind now requires positive proof: the popup we open for it is same-origin
(about:blank) before being sent to the provider, so we stamp its own
sessionStorage. The stamp rides through the provider round trip and is
scoped to that popup alone, so a login tab can never carry it. Ambiguity
resolves to 'login', which is the recoverable direction.
Affects every provider sharing this callback (OIDC, GitHub, Discord,
LinuxDO, custom).
* fix(oauth): harden bind popup detection
* docs: add design spec for OIDC custom display name
Mirrors the existing Custom OAuth Provider name pattern so admins can
show a meaningful label instead of the hardcoded "OIDC" on the login
page and in related copy.
* feat(oidc): add configurable display name with OIDC fallback
* feat(oidc): use configured display name in provider name and status API
* feat(oidc): add display name field to default-theme OIDC settings
Claude-Session: https://claude.ai/code/session_01FDkWJqigJi9yE3HG5pjZP5
* feat(oidc): show configured display name on default-theme login button
* feat(oidc): add display name field to classic-theme OIDC settings
* feat(oidc): show configured display name on classic-theme login button
* fix(oidc): trim whitespace before applying display name fallback
* chore: remove internal design doc from PR
Design/planning docs are working artifacts for this session and
shouldn't be submitted to the upstream project.
* fix(oidc): lead with example in classic-theme display name placeholder
Reorders the combined placeholder to show the example first, then
the fallback note, matching the Custom OAuth Provider Name field's
placeholder convention (example-only) that this feature mirrors.
* fix(i18n): improve Russian grammar in OIDC display-name placeholder translation
Leads each clause with its condition/subject and adds the missing
verb, per PR review feedback.
* test(web): remove redundant OIDC harness tests
* feat: support tencent tokenhub api key via openai-compatible protocol
* fix: use tokenhub base url for tencent api key channels
* test: cover tencent key-format dispatch and add TokenHub key prompt locales
* perf(json-editor): improve JSON editing experience
- integrate Yace for syntax highlighting, history, indentation, auto-closing, and smart line breaks.
- add copy support, cursor location feedback, and synchronized content and line-number scrolling.
- extract JSON editor utilities and cover key interactions with unit tests.
* perf(system-settings): improve JSON configuration editing
- replace raw JSON textareas with the shared editor for highlighting, validation, copy, and formatting.
- preserve field-specific examples and make placeholders visible through the transparent editor layer.
- remove duplicate formatting controls while keeping existing form validation and save behavior.
* perf(json-editor): standardize JSON inputs across admin settings
- replace pure JSON textareas with the shared editor across system settings and channel workflows.
- preserve form focus, validation, placeholders, and visual or JSON editing modes.
- add happy-dom component coverage for form bindings, controlled updates, and formatting.
* fix(json-code-editor): address accessibility review findings
- Drop the unconditional aria-label that overrode every field's
label-derived accessible name; add an optional ariaLabel prop and
set it at call sites without an associated label
- Associate standalone Labels via htmlFor/id in channel-affinity views
- Hide the highlight mirror and line-number layers from the
accessibility tree (aria-hidden)
- Give the line-number gutter an opaque background so horizontally
scrolled code no longer slides under it
- Degrade to no scroll sync instead of destroying the editor when the
line-number layer is not found
* fix(channel): debounce inline priority updates
* fix(channel): commit spinner edits only on Enter or focus leave
Blurring the inline edit input to the +/- buttons flushed the pending
priority update immediately, bypassing the container focus-containment
check and defeating the debounce. Commit now happens via the container
blur handler or explicitly on Enter. Add unit tests for the priority
update scheduler.
* fix(channel): preserve row identity when priority updates reorder channels
* fix: prevent model create from wiping existing pricing for same name
* fix(models): keep create from wiping pricing for any existing name
Prefilling from the drawer's open-time model name only covered the
missing-models entry point. Submit deletes the pricing entries for the
name in the form, which is editable and starts empty from the toolbar
"Create model" button, so both a hand-typed existing name and a renamed
prefill still dropped the configured pricing.
Track the name whose pricing was actually read into the form and scope
the delete-then-readd to it, or to a name the user explicitly priced.
Editing still clears pricing by emptying the fields, a prefilled create
does too, and a name the form never loaded is left alone -- which also
stops an edit that renames onto an existing name from destroying that
name's pricing.
Read the prefill through one shared readPricingConfig instead of
duplicating the seven-map parse in both branches, and open the advanced
section for ratios that are configured as 0 rather than only truthy ones.