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