perf(desktop): use textContent for trigger precondition

Replace composerPlainText() call inside refreshTrigger's no-trigger
fast-bail with a textContent check. textContent is a browser-native
flat traversal; composerPlainText walks recursively with chip-aware
logic. We only need to know if @ or / appears; either way the trigger
char will be in textContent because chips contain @ in their refText.

Profile shows composerPlainText was ~18ms self over a 12s typing-during-
stream window, called from refreshTrigger on every keystroke. Most of
that was the precondition check (the trigger detection path is the
slow path but only runs when a trigger char is present).
This commit is contained in:
Brooklyn Nicholson 2026-05-21 18:05:43 -05:00
parent e529694919
commit a6a78ff08a

View file

@ -408,13 +408,13 @@ export function ChatBar({
}
// Fast-bail: if neither `@` nor `/` appears in the current draft, there's
// nothing for `detectTrigger` to match. Skip the DOM range walk inside
// `textBeforeCaret` (which calls `range.toString()`, O(n) over the draft)
// and the regex pass that follows. Only when a relevant char is present
// do we pay the cost.
const text = composerPlainText(editor)
// nothing for `detectTrigger` to match. Use `textContent` (cheap browser-
// native walk) for the precondition check rather than `composerPlainText`
// (recursive child walk with chip-aware logic). Only when a trigger char
// is present do we pay the cost of the full walk + DOM range work.
const rawText = editor.textContent ?? ''
if (!text.includes('@') && !text.includes('/')) {
if (!rawText.includes('@') && !rawText.includes('/')) {
if (trigger) {
setTrigger(null)
setTriggerActive(0)
@ -424,7 +424,7 @@ export function ChatBar({
}
const before = textBeforeCaret(editor)
const detected = detectTrigger(before ?? text)
const detected = detectTrigger(before ?? composerPlainText(editor))
setTrigger(detected)
setTriggerActive(0)