@@ -159,6 +159,10 @@ function PasteCredentialInputs(props: {
159159 /** Force the per-input label even for a single input (env vars name their
160160 * field rather than relying on a generic "value / token" placeholder). */
161161 readonly showLabels ?: boolean ;
162+ /** Single-input only: the placement's lead + prefix (e.g. "Authorization:
163+ * Bearer "), rendered as a non-editable affix INSIDE the field so the input
164+ * reads as the header value being built. Replaces the separate preview line. */
165+ readonly affix ?: string | null ;
162166 readonly values : Readonly < Record < string , string > > ;
163167 readonly onChange : ( values : Record < string , string > ) => void ;
164168} ) {
@@ -178,7 +182,11 @@ function PasteCredentialInputs(props: {
178182 < Input
179183 id = { `credential-input-${ input . variable } ` }
180184 type = "password"
181- autoComplete = "new-password"
185+ autoComplete = "off"
186+ data-1p-ignore
187+ data-lpignore = "true"
188+ data-bwignore
189+ data-form-type = "other"
182190 placeholder = { `paste ${ input . label } ` }
183191 value = { props . values [ input . variable ] ?? "" }
184192 onChange = { ( e : React . ChangeEvent < HTMLInputElement > ) =>
@@ -206,20 +214,55 @@ function PasteCredentialInputs(props: {
206214 { labelled && (
207215 < Label className = "font-mono text-xs text-muted-foreground" > { input . label } </ Label >
208216 ) }
209- < Input
210- type = "password"
211- autoComplete = "new-password"
212- placeholder = { labelled ? "secret value" : "paste the value / token" }
213- value = { props . values [ input . variable ] ?? "" }
214- onChange = { ( e : React . ChangeEvent < HTMLInputElement > ) =>
215- props . onChange ( {
216- ...props . values ,
217- [ input . variable ] : e . target . value ,
218- } )
219- }
220- className = "font-mono"
221- data-ph-block
222- />
217+ { props . affix ? (
218+ // Merged field: the placement's lead + prefix is a FIXED addon (its
219+ // own muted segment, divider, non-selectable), and the user types
220+ // only the token after it — the field reads as the header value being
221+ // built. Only the border reacts to focus (no full-field glow). The
222+ // autoComplete/ignore attrs stop the browser and password managers
223+ // from offering to GENERATE a password here; the app's own 1Password
224+ // picker covers filling an existing secret.
225+ < div className = "flex h-9 w-full min-w-0 items-stretch overflow-hidden rounded-md border border-input bg-transparent font-mono text-sm shadow-xs transition-colors focus-within:border-ring dark:bg-input/30" >
226+ < span className = "flex shrink-0 select-none items-center whitespace-pre border-r border-input bg-muted/40 px-3 text-muted-foreground/70" >
227+ { props . affix }
228+ </ span >
229+ { /* oxlint-disable-next-line react/forbid-elements */ }
230+ < input
231+ type = "password"
232+ autoComplete = "off"
233+ data-1p-ignore
234+ data-lpignore = "true"
235+ data-bwignore
236+ data-form-type = "other"
237+ placeholder = "token"
238+ value = { props . values [ input . variable ] ?? "" }
239+ onChange = { ( e : React . ChangeEvent < HTMLInputElement > ) =>
240+ props . onChange ( { ...props . values , [ input . variable ] : e . target . value } )
241+ }
242+ className = "min-w-0 flex-1 bg-transparent px-3 outline-none placeholder:text-muted-foreground/60"
243+ data-ph-block
244+ />
245+ </ div >
246+ ) : (
247+ < Input
248+ type = "password"
249+ autoComplete = "off"
250+ data-1p-ignore
251+ data-lpignore = "true"
252+ data-bwignore
253+ data-form-type = "other"
254+ placeholder = { labelled ? "secret value" : "paste the value / token" }
255+ value = { props . values [ input . variable ] ?? "" }
256+ onChange = { ( e : React . ChangeEvent < HTMLInputElement > ) =>
257+ props . onChange ( {
258+ ...props . values ,
259+ [ input . variable ] : e . target . value ,
260+ } )
261+ }
262+ className = "font-mono"
263+ data-ph-block
264+ />
265+ ) }
223266 </ div >
224267 ) ) }
225268 </ div >
@@ -297,6 +340,8 @@ function CredentialValueFields(props: {
297340 readonly inputs : readonly CredentialInput [ ] ;
298341 readonly singleInput : boolean ;
299342 readonly showLabels ?: boolean ;
343+ /** Single-input only: placement lead + prefix to merge into the field. */
344+ readonly affix ?: string | null ;
300345 /** Allow an external provider (1Password) origin. Off for env methods: the
301346 * wire's external `from` is keyed under the canonical `token` variable, but
302347 * an env credential must be keyed under its env var name, so a 1Password
@@ -351,6 +396,7 @@ function CredentialValueFields(props: {
351396 inputs = { props . inputs }
352397 singleInput = { props . singleInput }
353398 showLabels = { props . showLabels }
399+ affix = { props . affix }
354400 values = { props . values }
355401 onChange = { props . onValuesChange }
356402 />
@@ -1002,6 +1048,20 @@ function AddAccountModalView(props: AddAccountModalProps) {
10021048 // registration entirely.
10031049 const isCimd = isOAuth && method ?. oauth ?. supportsClientIdMetadataDocument === true ;
10041050 const cimdActive = isCimd ;
1051+ // Single-input header/query methods: the placement's lead + prefix (e.g.
1052+ // "Authorization: Bearer ") merges INTO the credential field as a non-editable
1053+ // affix, so the input reads as the header value being built and the separate
1054+ // preview line is dropped. Env and multi-input methods keep the plain field.
1055+ const singleCredentialAffix = useMemo < string | null > ( ( ) => {
1056+ if ( ! method || ! singleInput || isEnvMethod ) return null ;
1057+ const placement = method . placements . find ( ( p ) => p . carrier !== "env" ) ;
1058+ if ( ! placement ) return null ;
1059+ const lead =
1060+ placement . carrier === "header"
1061+ ? `${ placement . name || "Authorization" } : `
1062+ : `?${ placement . name || "api_key" } =` ;
1063+ return `${ lead } ${ placement . prefix ?? "" } ` ;
1064+ } , [ method , singleInput , isEnvMethod ] ) ;
10051065 // DCR-capable: the integration advertises dynamic registration (MCP oauth2),
10061066 // OR carries a discovery URL we can probe at connect time. When DCR-capable
10071067 // and not yet fallen back, we skip the app picker entirely (Option A).
@@ -1607,9 +1667,9 @@ function AddAccountModalView(props: AddAccountModalProps) {
16071667 < Tabs
16081668 value = { methodId }
16091669 onValueChange = { selectMethod }
1610- className = "w-full min-w-0 max-w-full gap-3 "
1670+ className = "w-full min-w-0 max-w-full gap-0 "
16111671 >
1612- < TabsList className = "flex h-10 w-fit min-w-0 max-w-full justify-start overflow-x-auto overflow-y-hidden p-1 [scrollbar-width:thin]" >
1672+ < TabsList className = "flex h-10 w-full min-w-0 max-w-full justify-start overflow-x-auto overflow-y-hidden rounded-b-none rounded-t-md border border-b-0 border-border/60 bg-muted/30 p-1 [scrollbar-width:thin]" >
16131673 < div className = "flex w-max shrink-0 items-stretch gap-1" >
16141674 { allMethods . map ( ( m : AuthMethod ) => (
16151675 < div
@@ -1669,10 +1729,14 @@ function AddAccountModalView(props: AddAccountModalProps) {
16691729 "mt-0 min-w-0 space-y-5" ,
16701730 // No-auth renders no fields, so skip the framed box that
16711731 // would otherwise show up as an empty bordered panel.
1672- isNoAuth ? null : "rounded-md border border-border/60 bg-muted/15 p-4" ,
1732+ // Otherwise the panel attaches to the tab header above
1733+ // (square top, rounded bottom).
1734+ isNoAuth
1735+ ? null
1736+ : "rounded-b-md rounded-t-none border border-border/60 bg-muted/15 p-4" ,
16731737 ) }
16741738 >
1675- { method ?. placements && ! isEnvMethod && singleInput
1739+ { method ?. placements && ! isEnvMethod && singleInput && ! singleCredentialAffix
16761740 ? ( ( ) => {
16771741 const shown = method . placements . filter ( ( p ) => p . carrier !== "env" ) ;
16781742 return shown . length > 0 ? (
@@ -1818,6 +1882,7 @@ function AddAccountModalView(props: AddAccountModalProps) {
18181882 inputs = { credentialInputs }
18191883 singleInput = { singleInput }
18201884 showLabels = { isEnvMethod }
1885+ affix = { singleCredentialAffix }
18211886 allowExternalProvider = { ! isEnvMethod }
18221887 values = { values }
18231888 onValuesChange = { setValues }
0 commit comments