From 665ef7a31e0f6760fefe108a5500f8d71951f378 Mon Sep 17 00:00:00 2001 From: Alan Wizemann Date: Sun, 3 May 2026 16:47:40 +0200 Subject: [PATCH] fix(chat): clip placeholder to TextEditor bounds and clear it on focus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two related bugs in the Mac chat composer's placeholder overlay: * The "Message Hermes… / for commands · drag images to attach" hint had no width constraint, so on narrower window geometries it visibly overflowed past the rounded TextEditor boundary. Add `lineLimit(1)`, `truncationMode(.tail)`, and `frame(maxWidth: .infinity, alignment: .leading)` so it ellipsizes inside the field instead. * The opacity formula `text.isEmpty ? 1 : 0` only hid the placeholder once content was typed, not when the field gained focus. Standard NSTextField / UITextField semantics clear the placeholder on focus. Switch to `(text.isEmpty && !isFocused) ? 1 : 0` so the hint disappears the moment the user clicks into the field. The opaque-background ghosting mitigation from #65 is preserved unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Features/Chat/Views/RichChatInputBar.swift | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/scarf/scarf/Features/Chat/Views/RichChatInputBar.swift b/scarf/scarf/Features/Chat/Views/RichChatInputBar.swift index b21c201..14619a1 100644 --- a/scarf/scarf/Features/Chat/Views/RichChatInputBar.swift +++ b/scarf/scarf/Features/Chat/Views/RichChatInputBar.swift @@ -114,7 +114,7 @@ struct RichChatInputBar: View { // bare `if text.isEmpty` overlay renders the // translucent placeholder text on top of the // just-typed character — visible as a "behind - // or around" ghost. Two mitigations: + // or around" ghost. Three mitigations: // // 1. Pin an opaque rectangle behind the // placeholder text. During any single- @@ -125,15 +125,29 @@ struct RichChatInputBar: View { // keystroke (removes the per-keystroke // view-mutation churn the composer was // already paying for). + // 3. Constrain to a single line with + // `frame(maxWidth: .infinity)` and + // `truncationMode(.tail)` so the long-form + // hint can't escape the rounded + // TextEditor bounds when the sidebar / + // detail-pane geometry compresses the + // composer (was visibly overflowing). Text(supportsImagePrompts ? "Message Hermes… / for commands · drag images to attach" : "Message Hermes… / for commands") .scarfStyle(.body) .foregroundStyle(ScarfColor.foregroundFaint) + .lineLimit(1) + .truncationMode(.tail) + .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 14) .padding(.vertical, 10) .background(ScarfColor.backgroundSecondary) - .opacity(text.isEmpty ? 1 : 0) + // Hide once the field has any content OR + // the user is actively focused — matches + // standard NSTextField / UITextField + // placeholder semantics. + .opacity((text.isEmpty && !isFocused) ? 1 : 0) .allowsHitTesting(false) } // Drag-drop image attachments. Receives both file URLs