fix(quick-panel): reset stale state on panel close to prevent model deselection (#14378)

### What this PR does

Before this PR:
When multiple models are selected for simultaneous calling and a message
containing "@" is pasted (e.g., `@Reed grandmom said yes`), pressing
Enter deselects the last selected model instead of sending the message
to all selected models.

<img width="1728" height="886" alt="PixPin_2026-04-18_22-36-23"
src="https://github.com/user-attachments/assets/83932a64-7d4e-4a49-be55-f4aa1dd79d99"
/>


After this PR:
Pressing Enter after pasting a message containing "@" correctly sends
the message to all selected models without any model deselection. The
QuickPanel state (`prevSymbolRef`, `prevSearchTextRef`, `index`) is
properly reset when the panel fully closes, preventing stale state from
affecting the next panel session.


<img width="1554" height="830" alt="PixPin_2026-04-18_22-38-49"
src="https://github.com/user-attachments/assets/15b356fa-2ed1-4a88-919f-7efe2fdf01b3"
/>

Fixes #14085

### Why we need it and why it was done in this way

The following tradeoffs were made:
- Resetting state inside `useMemo` is technically a side effect (calling
`setIndex`), but this pattern was already pre-existing in the codebase
and works correctly due to React's batched updates. A more proper
solution would move `index` management to `useEffect`, but that's beyond
the scope of this hotfix.

The following alternatives were considered:
- Reset in provider's `close()` function — rejected: would require
updating shared component
- Reset in `useEffect` on visibility change — rejected: timing issues,
`useMemo` runs before effects
- Track close action in separate state — rejected: over-engineered for a
simple reset

Links to places where the discussion took place: Issue #14085

### Breaking changes

None.

### Special notes for your reviewer

This is a minimal hotfix targeting a regression bug in v1.8.4. The fix
adds state reset logic in the `useMemo` entry guard when QuickPanel
fully closes (`isVisible=false` and `symbol` cleared).

### Checklist

- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: [Write code that humans can
understand](https://en.wikiquote.org/wiki/Martin_Fowler#code-for-humans)
and [Keep it simple](https://en.wikipedia.org/wiki/KISS_principle)
- [ ] Refactor: You have [left the code cleaner than you found it (Boy
Scout
Rule)](https://learning.oreilly.com/library/view/97-things-every/9780596809515/ch08.html)
- [ ] Upgrade: Impact of this change on upgrade flows was considered and
addressed if required
- [ ] Documentation: A [user-guide update](https://docs.cherry-ai.com)
was considered and is present (link) or not required. Check this only
when the PR introduces or changes a user-facing feature or behavior.
- [x] Self-review: I have reviewed my own code (e.g., via
[`/gh-pr-review`](/.claude/skills/gh-pr-review/SKILL.md), `gh pr diff`,
or GitHub UI) before requesting review from others

### Release note

```release-note
Fixed a bug where pasting a message containing "@" and pressing Enter would deselect the last selected model in multi-model calling mode instead of sending the message.
```

Signed-off-by: George·Dong <GeorgeDong32@qq.com>
Co-authored-by: SuYao <sy20010504@gmail.com>
Co-authored-by: 亢奋猫 <kangfenmao@qq.com>
This commit is contained in:
George·Dong
2026-04-20 21:38:22 +08:00
committed by GitHub
parent 260c5458f2
commit cd6d84c847

View File

@@ -79,7 +79,13 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
const sortFn = ctx.sortFn || defaultSortFn
// 处理搜索,过滤列表(始终保留 alwaysVisible 项在顶部)
const list = useMemo(() => {
if (!ctx.isVisible && !ctx.symbol) return []
// Reset stale state when panel fully closes (both isVisible false AND symbol cleared)
if (!ctx.isVisible && !ctx.symbol) {
prevSymbolRef.current = ''
prevSearchTextRef.current = ''
setIndex(-1)
return []
}
const baseList = (ctx.list || []).filter((item) => !item.hidden)