fix(web): reveal the caret when a persisted draft arrives after mount
ConversationSession seeds a stored draft in its own mount effect, and a parent's mount effect runs after its children's — so the unlock effect measured an empty mirror and never ran again for the draft that then appeared, leaving a restored long draft showing its head with the caret at its end. The effect now depends on the draft being non-empty; clearing on send and typing the first character flip it too, where both the focus and the reveal are no-ops. Pre-existing (the old geometry did not scroll for a programmatic value change either), fixed here because the reveal now exists. Also from review: the golden's paste goes back to a block NOT ending in a newline, so the collapsed branch keeps a real engine under it while the standalone case owns the after-newline branch; the shared line-height rule names the reveal as its third consumer.
This commit is contained in:
@@ -206,6 +206,10 @@
|
||||
share the stack, so placeholder advances agree by construction. */
|
||||
font-family: 'DshChipCell', var(--dsw-font-family);
|
||||
font-size: inherit;
|
||||
/* Three consumers, not two: the mirror sizes the stack, the layers must break
|
||||
lines identically, and the caret reveal parses this value to step one line
|
||||
down for a caret that sits after a newline. That parse needs a length, so a
|
||||
theme resolving this to `normal` would make the reveal a silent no-op. */
|
||||
line-height: inherit;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
|
||||
@@ -132,6 +132,14 @@ export function InputBar({
|
||||
// offset while the value swap puts the caret at the new draft's end, which is
|
||||
// off screen (measured on all three engines: offset 0 with the caret 940px
|
||||
// down). Suppress the walk, then reveal in our own box.
|
||||
//
|
||||
// `draft !== ''` is the third dependency because a persisted draft arrives
|
||||
// AFTER this effect: ConversationSession adopts it in its own mount effect,
|
||||
// and a parent's mount effect runs after its children's. Without that
|
||||
// dependency the reveal would measure an empty mirror and never run again for
|
||||
// the draft that then appeared, leaving a restored long draft showing its head
|
||||
// with the caret at its end. Clearing on send and typing the first character
|
||||
// flip it too, where both the focus and the reveal are no-ops.
|
||||
useEffect(() => {
|
||||
const el = inputRef.current
|
||||
if (locked || el === null) return
|
||||
@@ -139,7 +147,7 @@ export function InputBar({
|
||||
// selectionStart is number|null in lib.dom; the type-aware lint program narrows it.
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-condition
|
||||
revealCaret(el.selectionStart ?? el.value.length)
|
||||
}, [locked, sessionId])
|
||||
}, [locked, sessionId, draft !== ''])
|
||||
|
||||
// Caret restore after an edit the composer performs itself. The machine owns
|
||||
// the draft and the undo log, so paste, ctrl/meta-Enter newline and cut all
|
||||
|
||||
Reference in New Issue
Block a user