## Description
#16242 moved `sections/knowledge/SourceHierarchyBrowser.tsx` to Opal
`SelectButton`. That file was the last caller of
`@/refresh-components/buttons/SelectButton`, so nothing imports the
refresh component now. The only references left were its own story and
lint config.
This PR removes:
- `web/src/refresh-components/buttons/SelectButton.tsx` and
`SelectButton.stories.tsx`.
- Its two baseline entries in `web/.oxlintrc.json`.
- Its row in
`web/tools/oxlint/opal/rules/no-replaced-refresh-import.ts`. The rule's
comment says to delete the row when the refresh module is deleted.
- The `src/refresh-components/buttons` floor in
`web/.type-coverage-baseline.yaml`, lowered from 99.7 to 99.6. The
deleted file was fully typed, so the directory ratio fell by a rounding
step.
## How Has This Been Tested?
- `bun run types:check`: no type errors.
- `bunx oxlint`: no new errors.
- Pre-commit hooks (oxfmt, oxlint, type coverage, feature-map integrity)
pass.
## Additional Options
- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
Link to Devin session:
https://app.devin.ai/sessions/ba1242cc5338467c8db2fdfa02b81d10
Open in Devin Desktop:
https://app.devin.ai/desktop/session/ba1242cc5338467c8db2fdfa02b81d10?variant=devin
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Removes the now-unused `SelectButton` from `refresh-components` after
#16242 moved the last caller to Opal's component.
- Deletes `SelectButton.tsx` and its story file, along with its lint
baseline entries.
- Removes the `SelectButton` row from the `no-replaced-refresh-import`
rule.
- Lowers the type coverage baseline for `src/refresh-components/buttons`
from 99.7 to 99.6, since the removed file was fully typed.
<sup>Written for commit 6856447fb6.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16301?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/16301?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16301&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
* **Removed**
* Removed the SelectButton component and its Storybook examples.
* Removed support for tracking SelectButton as a design-system and
layout baseline item.
* Removed related replacement guidance for SelectButton imports.
* **Maintenance**
* Adjusted the type-coverage threshold for refresh button components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## 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 -->
## Description
This is the third of four cleanup PRs on top of #16234, which is merged
(stack: #16240 icons, #16241 raw elements, #16242 refresh components,
#16243 Text). It migrates the refresh-components that
`opal/no-replaced-refresh-import` flags, except `Text`, which the next
PR migrates. These are `Card`, `Truncated`, `SelectButton`, `LineItem`
and `Divider`. 13 of 22 files are migrated, and the other 9 are kept for
the reasons below.
### Changes (under `web/src/`)
**`Card` to Opal `Card`** (9 sites in 4 files:
`app/admin/discord-bot/page.tsx`,
`app/admin/discord-bot/[guild-id]/page.tsx`,
`app/app/settings/usage/UsageSettings.tsx`,
`views/admin/GroupsPage/GroupPermissionsSection.tsx`)
- The refresh `Card` put an inner `Section` inside its box, but Opal
`Card` does not. So each site wraps its children in `<Section
alignItems="start" height="fit">` and passes `rounding={4}` for the same
16px corner. One discord-bot card has a single full-width `Section`
child, so it gets no wrapper.
- The variants map like this:
- `primary` → `border="solid"`;
- `secondary` → `color="transparent" border="solid"`;
- the computed `"disabled"` variant → `border="solid" disabled={...}`.
**`Truncated` to `OverflowText`** (9 files:
`app/ee/admin/theme/Preview.tsx`, `lib/app/components.tsx`,
`refresh-components/messages/InfoBlock.tsx`,
`sections/onboarding/components/LLMProviderCard.tsx`,
`ee/sections/SearchCard.tsx`, `layouts/general-layouts.tsx`,
`sections/document-sidebar/ChatDocumentDisplay.tsx`,
`sections/knowledge/agent-knowledge/KnowledgeSearch.tsx`,
`sections/knowledge/SourceHierarchyBrowser.tsx`)
- Each site becomes `as="p" textPosition="text-start"`, with `font` and
`color` set explicitly.
- Sites with no color flag get `color="text-05"`, because the refresh
default is `text-05` and the Opal default is `text-04`.
- `className="line-clamp-2"` becomes `maxLines={2}`.
- `applicationName` can be null, so it renders `{applicationName ??
""}`.
**`SelectButton` to Opal `SelectButton`**
(`sections/knowledge/SourceHierarchyBrowser.tsx`)
- It uses `variant="select-light"`, and `transient` maps to
`interaction={sortDropdownOpen ? "hover" : "rest"}`.
### Visual differences reviewers should check
- **`ChatDocumentDisplay`:** the old tooltip opened with `side="start"`.
`OverflowText` has no side prop, so its tooltip always opens above the
text, aligned to the start.
- **`SourceHierarchyBrowser` sort button:** the label at rest changes
from `text-03` to `text-04`. The size is Opal's default `lg`, where the
old button used `px-2 py-2 rounded-12`.
- **All `OverflowText` sites:**
- Opal `Text` always adds `px-[2px]`.
- The old `grow w-full` wrapper is gone. The parents are start-aligned,
so the layout should look the same.
- **Card borders:** Opal draws `border-border-01`. The old card used the
global default border color.
### Kept on purpose
- **`app/admin/billing/BillingDetailsView.tsx`, `PlansView.tsx`:** these
Cards pass a `className` (`billing-card-enter`, `billing-payment-card`,
`plan-card`), and Opal `Card` takes no `className`. The deprecation note
on refresh `Card` already says these need a decision.
- **`app/admin/billing/CheckoutView.tsx`:** one Card is clickable and
has `className` and `data-selected`, so it is probably a `SelectCard`.
That needs a design decision.
- **`refresh-components/buttons/LineItem.tsx`:** its title `Truncated`
gets per-variant `line-item-text-*` classes that `OverflowText` cannot
take.
- **`refresh-components/buttons/source-tag/SourceTag.tsx`:** its
`Truncated` shares a `TextStyleProps` object with the refresh `Text`
beside it.
- **`refresh-components/commandmenu/CommandMenu.tsx`:**
- Its `Divider`s use `icon` and highlight states that Opal `Divider`
does not support.
- Its `LineItem` rows depend on muted, action and emphasized colors and
on the keyboard-nav hover in `line-item.css`.
- **`views/admin/UsersPage/EditUserModal.tsx`,
`ServiceAccountsPage/EditServiceAccountModal.tsx`,
`ServiceAccountsPage/ApiKeyFormModal.tsx`:** each has `skeleton`
LineItems (dashed border, 12px text), and neither `LineItemButton` nor
`EmptyMessageCard` matches that look.
## How Has This Been Tested?
- oxlint: in the 13 migrated files, the only
`opal(no-replaced-refresh-import)` diagnostics left are refresh `Text`,
which the next PR migrates. The full run has 0 errors.
- `bun run types:check`: no type errors, and type coverage stays at
99.04%.
- pre-commit (oxfmt, oxlint) passes on the changed files.
## Additional Options
- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Hovering over a suppressed tooltip no longer closes its enclosing
tooltip.
* **UI Improvements**
* Text exceeding available space now has consistent overflow handling
across navigation, search results, document lists, onboarding, and
informational messages.
* Admin, usage, permissions, and Discord settings cards now have
consistent borders, rounded corners, disabled states, and content
alignment.
* The knowledge source sort control now reflects its interaction state
while the dropdown is open.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->