diff --git a/src/tui/components/agent-modal.test.ts b/src/tui/components/agent-modal.test.ts index 759f6e62d..a25ed4058 100644 --- a/src/tui/components/agent-modal.test.ts +++ b/src/tui/components/agent-modal.test.ts @@ -89,4 +89,27 @@ describe("validateProviderForm", () => { expect(result.submission.keyless).toBeUndefined(); } }); + + test("trims leading and trailing spaces on text fields at save", () => { + const result = validateProviderForm( + form({ + name: " firepass ", + baseURL: " https://firepass.example/v1 ", + apiKey: " sk-key ", + models: " fp-large , fp-small ", + defaultModel: " fp-large ", + }), + undefined, + ); + expect(result).toEqual({ + ok: true, + submission: { + name: "firepass", + baseURL: "https://firepass.example/v1", + apiKey: "sk-key", + models: ["fp-large", "fp-small"], + defaultModel: "fp-large", + }, + }); + }); }); diff --git a/src/tui/components/agent-modal.tsx b/src/tui/components/agent-modal.tsx index cc7bcf3fd..8e5860810 100644 --- a/src/tui/components/agent-modal.tsx +++ b/src/tui/components/agent-modal.tsx @@ -961,6 +961,13 @@ export function AgentModal({ const isCursor = i === formIndex; const value = formValues[field]; const isKeyless = formValues.keyless === "yes"; + // gap only between label and value — never between value and caret, + // or the caret sits after a phantom space the user did not type. + const showCaret = + isCursor && + field !== "keyless" && + field !== "bifrostVirtualKey" && + !(field === "apiKey" && isKeyless); return ( @@ -977,16 +984,16 @@ export function AgentModal({ ) : field === "apiKey" && isKeyless ? ( (disabled — keyless provider) ) : ( - 0 ? color("text") : color("muted")}> - {value.length > 0 - ? maskInput(field, value) - : field === "apiKey" && editingProvider !== undefined - ? "leave blank to keep existing" - : FIELD_HINTS[field]} - - )} - {isCursor && field !== "keyless" && field !== "bifrostVirtualKey" && !(field === "apiKey" && isKeyless) && ( - | + + 0 ? color("text") : color("muted")}> + {value.length > 0 + ? maskInput(field, value) + : field === "apiKey" && editingProvider !== undefined + ? "leave blank to keep existing" + : FIELD_HINTS[field]} + + {showCaret && |} + )} ); @@ -1052,12 +1059,12 @@ export function AgentModal({ {isCursor ? " >" : ""} ) : ( - <> + 0 ? color("text") : color("muted")}> {profileFormValues[field].length > 0 ? profileFormValues[field] : PROFILE_FIELD_HINTS[field]} {isCursor && |} - + )} ); diff --git a/src/tui/components/codex-login-modal.tsx b/src/tui/components/codex-login-modal.tsx index f24ebe074..6f5985d1e 100644 --- a/src/tui/components/codex-login-modal.tsx +++ b/src/tui/components/codex-login-modal.tsx @@ -255,10 +255,13 @@ export function CodexLoginModal({ Profile name - 0 ? color("text") : color("muted")}> - {nameValue.length > 0 ? nameValue : "personal, work, ..."} - - | + {/* Keep value and caret in one Box so gap does not insert a phantom space. */} + + 0 ? color("text") : color("muted")}> + {nameValue.length > 0 ? nameValue : "personal, work, ..."} + + | + {nameError !== null && ( diff --git a/src/tui/components/operator-modal.tsx b/src/tui/components/operator-modal.tsx index c134a89df..5eecef49d 100644 --- a/src/tui/components/operator-modal.tsx +++ b/src/tui/components/operator-modal.tsx @@ -277,10 +277,13 @@ export function OperatorModal({ {typing ? ( + {/* gap only between › and value — never between value and caret */} - {draft} - + + {draft} + + Enter to confirm · Esc to cancel