Skip to content

Refine the connect modal's API-key credential UX#1195

Merged
RhysSullivan merged 1 commit into
mainfrom
connect-modal-ux
Jun 28, 2026
Merged

Refine the connect modal's API-key credential UX#1195
RhysSullivan merged 1 commit into
mainfrom
connect-modal-ux

Conversation

@RhysSullivan

Copy link
Copy Markdown
Owner

Summary

Refines how a user reads and enters an API-key credential in the connect modal. Three related changes:

  • Merged credential field. The placement's lead + prefix (e.g. Authorization: Bearer ) is a fixed, non-editable addon inside the credential input (its own muted segment + divider), so the field reads as the header value being built and you type only the token. The separate Authorization: Bearer •••••• preview line is dropped. autoComplete="off" plus password-manager ignore attributes stop the browser from offering to generate a password here; the app's own 1Password picker still covers filling an existing secret.
  • Attached tabs. The auth-method tabs (API key / OAuth2 / +) are joined to the panel as one card: a full-width, rounded-top header with a hairline divider into the content, instead of a pill bar floating above a separate card.
  • Prefix trailing-space warning (amber). A non-empty prefix with no trailing space is sent joined to the value (Bearer + token → Bearertoken), almost always a mistake. An amber warning surfaces in the placement editor; it's advisory, not blocking, and clears once a space is added. (The design system holds color to destructive red, so this amber is a deliberate, scoped exception.)

Recording

Connect modal → merged credential field → Add authentication method (presets + placement editor) → a bare prefix warns, and restoring the space clears it.

Connect modal · API key credential UX

Tests

New selfhost browser scenario e2e/selfhost/connect-modal-credential-ux.test.ts covers the merged affix field, the add-method editor's presets, and the prefix warning appearing then clearing.

- Merged credential field: the placement's lead + prefix is a fixed,
  non-editable addon inside the field, so it reads as the header value being
  built ("Authorization: Bearer <token>") and the separate preview line is
  dropped. autoComplete/ignore attrs stop the browser and password managers
  from offering to GENERATE a password here; the app's own 1Password picker
  still covers filling an existing secret.
- Attached tabs: the auth-method tabs join the panel as one card (full-width
  rounded-top header, hairline divider) instead of a pill bar floating above a
  separate card.
- Prefix trailing-space warning (amber): a non-empty prefix with no trailing
  space is sent joined to the value ("Bearer" + token -> "Bearertoken").
  Advisory, not blocking.
@RhysSullivan RhysSullivan merged commit b1e73d2 into main Jun 28, 2026
10 of 14 checks passed
@RhysSullivan RhysSullivan deleted the connect-modal-ux branch June 28, 2026 23:22
@github-actions

github-actions Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@greptile-apps

greptile-apps Bot commented Jun 28, 2026

Copy link
Copy Markdown

Greptile Summary

This PR refines the connect modal's API-key credential UX with three coordinated changes: a merged credential field that renders the placement's lead and prefix as a non-editable affix inside the input, tab-list styling that attaches the method tabs flush to the content panel, and an amber advisory warning in the placement editor when a prefix has no trailing space.

  • The singleCredentialAffix memo computes the affix from the first non-env placement's carrier/name/prefix, passes it through CredentialValueFields into PasteCredentialInputs, where a custom inline <input> replaces the shadcn Input with the affix span prepended. The separate preview line is dropped, and the placement-preview section is gated on !singleCredentialAffix.
  • The barePrefix check in PlacementEditor is a simple some() over placements, surfaced as an amber section with an icon and explanatory copy.
  • A new selfhost browser e2e scenario covers the merged affix display, add-method preset buttons, and the prefix warning appear/clear cycle using the service-injection pattern.

Confidence Score: 4/5

Safe to merge for the common single-placement case; the edge cases noted are worth addressing before shipping to users who configure multi-placement methods.

The core affix UX and warning logic are correct for the typical bearer/API-key method. Two edge cases stand out: the raw input in the affix branch has no accessible label (screen readers get nothing useful), and when a method carries more than one non-env placement the second placement silently disappears from the UI because both the preview section and the affix only cover the first.

packages/react/src/components/add-account-modal.tsx — the affix input branch and the placement-preview gating condition.

Important Files Changed

Filename Overview
packages/react/src/components/add-account-modal.tsx Adds singleCredentialAffix memo to merge placement lead+prefix into the credential field as a non-editable affix, updates TabsList/TabsContent styling for attached tabs, and passes the affix down through CredentialValueFields to PasteCredentialInputs. The custom inline in the affix branch lacks an accessible label.
packages/react/src/components/placement-editor.tsx Adds PLACEMENT_PRESETS for quick bearer/query presets, imports TriangleAlertIcon, and computes barePrefix to render an amber warning banner when a prefix has no trailing space. Logic is correct and advisory-only.
e2e/selfhost/connect-modal-credential-ux.test.ts New selfhost browser scenario covering merged affix display, add-method preset buttons, and the prefix warning appear/clear cycle. Correctly cleans up via Effect.ensuring, uses a prefixed slug for isolation, and follows the service-injection pattern.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[method resolved] --> B{singleInput AND NOT isEnvMethod?}
    B -- No --> C[Plain credential input, no affix]
    B -- Yes --> D[Find first non-env placement]
    D --> E{Placement found?}
    E -- No --> C
    E -- Yes --> F[Build lead: header = 'Name: ', query = '?name=']
    F --> G[Append prefix: affix = lead + prefix]
    G --> H[singleCredentialAffix is truthy]
    H --> I[Render merged field: fixed affix span + raw input]
    H --> J[Hide placement-preview section]
    J --> K{Multiple non-env placements?}
    K -- Yes --> L[Only first affix shown - others invisible]
    K -- No --> M[Single placement shown correctly]

    N[PlacementEditor] --> O{barePrefix? prefix non-empty and no trailing space}
    O -- Yes --> P[Amber warning banner]
    O -- No --> Q[No warning]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[method resolved] --> B{singleInput AND NOT isEnvMethod?}
    B -- No --> C[Plain credential input, no affix]
    B -- Yes --> D[Find first non-env placement]
    D --> E{Placement found?}
    E -- No --> C
    E -- Yes --> F[Build lead: header = 'Name: ', query = '?name=']
    F --> G[Append prefix: affix = lead + prefix]
    G --> H[singleCredentialAffix is truthy]
    H --> I[Render merged field: fixed affix span + raw input]
    H --> J[Hide placement-preview section]
    J --> K{Multiple non-env placements?}
    K -- Yes --> L[Only first affix shown - others invisible]
    K -- No --> M[Single placement shown correctly]

    N[PlacementEditor] --> O{barePrefix? prefix non-empty and no trailing space}
    O -- Yes --> P[Amber warning banner]
    O -- No --> Q[No warning]
Loading

Comments Outside Diff (1)

  1. packages/react/src/components/add-account-modal.tsx, line 1739-1750 (link)

    P2 Silent omission of additional placements when affix is active. singleCredentialAffix uses find() to pick only the first non-env placement. The placement-preview section is now gated on !singleCredentialAffix, so if a method carries two non-env placements (e.g., both a header and a query param) the second placement is entirely invisible: not shown in the preview, not shown in the affix. The user connects without knowing their credential is being sent to an additional location. A guard that falls back to the plain preview for the multi-placement case would avoid the silent drop.

Reviews (1): Last reviewed commit: "Refine the connect modal's API-key crede..." | Re-trigger Greptile

Comment on lines +229 to +237
{/* oxlint-disable-next-line react/forbid-elements */}
<input
type="password"
autoComplete="off"
data-1p-ignore
data-lpignore="true"
data-bwignore
data-form-type="other"
placeholder="token"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The raw <input> in the affix branch has no programmatic label. When affix is truthy, no <Label> is rendered and there is no id to associate one with, so screen readers will announce this field with only its type ("password edit") and no description. Adding an aria-label directly on the element covers the gap without altering the visual layout.

Suggested change
{/* oxlint-disable-next-line react/forbid-elements */}
<input
type="password"
autoComplete="off"
data-1p-ignore
data-lpignore="true"
data-bwignore
data-form-type="other"
placeholder="token"
{/* oxlint-disable-next-line react/forbid-elements */}
<input
aria-label={`${input.label} value`}
type="password"
autoComplete="off"
data-1p-ignore
data-lpignore="true"
data-bwignore
data-form-type="other"
placeholder="token"

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant