eleven / messenger Public

mac: the composer placeholder must be click-through (#1061)

The placeholder NSTextField spans the field's full width and hit-tests,
but a label's mouseDown neither focuses nor forwards — so clicking an
EMPTY composer's text line was swallowed whenever focus sat elsewhere
(after clicking message text, etc). Caught by ClickLab: hit chain ended
at the label, first responder never moved. hitTest nil, the FrostedBand
rule: decoration is click-through.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell dfe05d70bf28fe2614ab6e2eff6883dcf2707fc8 parent 0baa858
1 file changed, +11 −1
  • ios/LChatMac/MacComposerField.swift +11 −1
diff --git a/ios/LChatMac/MacComposerField.swift b/ios/LChatMac/MacComposerField.swift
index 9f985a6..6519e17 100644
--- a/ios/LChatMac/MacComposerField.swift
+++ b/ios/LChatMac/MacComposerField.swift
@@ -312,7 +312,17 @@ final class MacComposerController {
// The NSTextView itself: hand-assembled TextKit stack, placeholder, guarded
// pastes. Plain text at this stage — the rich toggles return in a later PR.
final class ComposerTextView: NSTextView {
- let placeholderLabel = NSTextField(labelWithString: "")
+ // The placeholder is decoration, and decoration must be click-through
+ // (the FrostedBand rule): a plain NSTextField label hit-tests but its
+ // mouseDown neither focuses anything nor forwards up the chain — and the
+ // label spans the field's full width (see layout()), so an EMPTY composer
+ // silently ate the very click meant to focus it whenever focus sat
+ // elsewhere (caught by ClickLab: hit chain ended at the label, responder
+ // never moved).
+ private final class ClickThroughLabel: NSTextField {
+ override func hitTest(_ point: NSPoint) -> NSView? { nil }
+ }
+ let placeholderLabel: NSTextField = ClickThroughLabel(labelWithString: "")
var onStagePaste: ((NSPasteboard) -> Bool)?
var onStageDrop: ((NSPasteboard) -> Bool)?
var onFocusChange: ((Bool) -> Void)?