Files
Raunak Bhagat 0fe91853b1 feat(opal): stored-secret password tag, tab keyboard nav, tertiary actions (#16275)
## Description

**1. Stored secrets in `InputPasswordTypeIn`**
- New prop `existingValue`: a stored value from the backend, already
masked.
- While `value === existingValue`:
- the field shows the value as a medium blue `Tag` with the key icon,
and the input itself stays empty;
  - the reveal toggle is disabled;
  - Backspace or Delete clears the value;
  - typing or pasting replaces the value with the new text.
- After the first edit, the field is a normal password field again.
- To keep the stored secret on submit, the caller compares the submitted
value with `existingValue`.
- The tag has no tooltip.
- An unchanged existing value does not block submission: the native
`required` check is off while the tag shows. A `readOnly` field keeps
its existing value, and the caller's `onKeyDown` runs first and can
cancel the deletion.
- The `mask` prop and its ✱ overlay are removed. Password fields always
use the browser's own dots.

**Callers that edit a stored secret now pass `existingValue`**
- LLM provider modals (`APIKeyField`, Bedrock, Ollama), SSO provider
modal, index settings, web search, voice, tracing, image generation
(OpenAI, Azure), OpenAPI OAuth client secret, MCP API token and OAuth
secret, MCP user credentials, Craft app user credentials, Slack bot
tokens.
- In each one, the stored masked string is the field's initial value. So
the untouched field keeps Formik's `dirty` false, and a Save gated on
`dirty` stays blocked. Submit logic does not change: an unchanged value
still counts as unchanged.
- SSO used `isNonRevealable` on edit only to protect the stored value.
It uses `existingValue` now.
- No change where the form starts empty for a stored secret: the hooks
modal (only a placeholder holds the masked key), the Discord bot config
and the custom-analytics key.

**Raw password inputs move to `InputPasswordTypeIn`**
- Slack bot tokens, reset password, impersonate (API key,
`autoComplete="new-password"`), custom analytics secret key, plugin
connection dialog.

**2. Keyboard and tab navigation (from the closed #16238)**
These are the Opal parts of the closed Plugins PR. The plugins-specific
parts are left out.
- `Tabs`:
- new `activationMode`. In `"manual"`, the default, arrows move focus
and Enter or Space selects.
  - a focus ring shows while the keyboard moves focus;
- Tab enters the list on the selected tab and the next Tab leaves it, as
the ARIA tabs pattern says;
- Escape leaves the tabs, but only from a tab: a control in
`rightChildren` keeps focus;
  - only a left click focuses a tab.
- `orientation` is omitted from the props: Opal tabs are horizontal
only.
- `Interactive` (stateless): keyboard focus shows the hover state in
place of the browser's ring.
- `useGridNavigation`: keyboard focus on a control inside a grid item
shows again.
- `InputTypeIn` clear button: it keeps focus in the field after it
clears the value, it has an `aria-label`, and it sends `onChange` a real
change event.

- Escape on a control beside a text field (the clear button, the reveal
toggle, a chevron, a chip) returns focus to the field, also when a
tooltip consumed the key. This covers `InputTypeIn` and every input
built on it, plus `TagField`. On `InputTime` and `InputDatePicker`,
which have no single input, it focuses the first segment.

**3. Input action buttons**
- The right-side icon buttons use `tertiary` prominence: the clear
buttons of `InputTypeIn`, `TagField`, `InputTime` and `InputDatePicker`,
and the date picker's calendar button.
- The `InputTypeIn` and `UploadControl` clear buttons have tooltips.
- The tooltip-only clear and calendar buttons of `TagField`, `InputTime`
and `InputDatePicker` get an `aria-label`, so they have an accessible
name.
- The clear button is always an input's last control. `InputDatePicker`
drew it before the calendar button, and `TagField` (`InputTypeInTag`,
the multi-select) drew it before the dropdown chevron.

- `TagField` (`InputTypeInTag`, the multi-select) uses `size="sm"` for
its clear button, as the other inputs do. It used `xs`.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
- [x] [Optional] Override Linear Check

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds an `existingValue` prop to `InputPasswordTypeIn` so forms that edit
backend secrets show the saved value as a key tag instead of editable
masked text, and polishes Opal tab keyboard navigation, focus handling,
and input action buttons.

**Stored secrets**
- `existingValue` marks a stored backend value: while the field's value
equals it, the value renders as a key tag, the input stays empty, and
the reveal toggle is disabled.
- Backspace or Delete clears it; typing or pasting replaces it whole. A
read-only field keeps the value, the caller's `onKeyDown` can cancel the
deletion, and an unchanged tag doesn't fail a native required check.
- Secret-editing forms (Slack tokens, SSO, MCP, language-model and
image-gen modals, tracing, voice, web search, and more) now pass
`existingValue`; a few raw password inputs move to
`InputPasswordTypeIn`. The `mask` prop and idle asterisk overlay are
removed.
- E2E specs for OpenAPI OAuth, index settings, and web search now verify
the stored value appears as a tag with an empty input.

**Keyboard and input actions**
- `Tabs` defaults to manual activation: arrows move focus, Enter or
Space selects, a focus ring tracks keyboard movement, and Escape leaves
the tabs. Only a plain left click focuses a tab; a caller's `onKeyDown`
can cancel the Escape.
- Keyboard focus on `Interactive` surfaces replaces the browser ring
with the hover state; `useGridNavigation` now shows focus on controls
inside grid items.
- `InputTypeIn`'s clear button sends a real change event, keeps focus in
the field after clearing, and gains an `aria-label`.
- Escape on an input's side controls (clear, reveal toggle, chevron,
chip) returns focus to the field, even when a tooltip consumed the key;
on `InputTime` and `InputDatePicker` it focuses the first segment.
- Input action buttons use `tertiary` prominence, the clear button is
always an input's last control, and `TagField`'s clear button is `sm`
like the other inputs'.

<sup>Written for commit 66ab5fd5b3.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16275?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="View guided diff"
src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a>
<a
href="https://www.cubic.dev/action/auto-fix/pr/onyx-dot-app/onyx/16275?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16275&source=description"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"><img
alt="Turn on auto-fix"
src="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Password fields display saved credentials as masked, non-revealable
tags. Clear them with Backspace or Delete, or replace them by typing or
pasting.
* Tabs use manual activation: arrow keys move focus, while Enter or
Space selects.

* **Improvements**
* Credential fields preserve saved values while editing across
administrative and configuration forms.
* Clear controls provide localized labels and tooltips, and return focus
to the field.
  * Escape restores focus from date, time, tag, and input controls.
* Keyboard focus styling is more consistent across interactive controls
and grid navigation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-20 01:32:59 +00:00
..