-
Notifications
You must be signed in to change notification settings - Fork 625
feat: add ESC key to close floating chat window #728
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -169,6 +169,13 @@ const handleGoSettings = () => { | |
| } | ||
| } | ||
|
|
||
| // 处理ESC键 - 关闭悬浮聊天窗口 | ||
| const handleEscKey = (event: KeyboardEvent) => { | ||
| if (event.key === 'Escape') { | ||
| window.electron.ipcRenderer.send('close-floating-window') | ||
| } | ||
| } | ||
|
|
||
|
Comment on lines
+172
to
+178
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainESC handler in App.vue + raw IPC won’t close the floating window (sender check blocks it). Move to floating window and use presenter.
Apply this diff to improve correctness and align with guidelines (still in App.vue if you decide to keep it here), and make the comment English. Note: add windowPresenter (see snippet below). -// 处理ESC键 - 关闭悬浮聊天窗口
-const handleEscKey = (event: KeyboardEvent) => {
- if (event.key === 'Escape') {
- window.electron.ipcRenderer.send('close-floating-window')
- }
-}
+// Handle ESC key — close the floating chat window
+const handleEscKey = (event: KeyboardEvent) => {
+ if (event.key !== 'Escape' || event.isComposing || event.repeat) return
+ const active = document.activeElement as HTMLElement | null
+ const tag = active?.tagName?.toLowerCase()
+ const isTyping =
+ active?.isContentEditable ||
+ tag === 'input' ||
+ tag === 'textarea' ||
+ (active as HTMLInputElement)?.type === 'text'
+ if (isTyping) return
+ try {
+ windowPresenter.hideFloatingChatWindow()
+ } catch (err) {
+ console.error('[ERROR][App] Failed to hide floating chat window on ESC:', err)
+ }
+}Additionally required (outside this hunk): declare the presenter once near your other presenters: const windowPresenter = usePresenter('windowPresenter')Recommended: register this ESC handler in the floating chat window’s renderer root instead of App.vue, to ensure ESC only closes when that window has focus and to avoid no-ops due to sender mismatch. If you prefer IPC, keep the handler in the floating window but call a typed presenter instead of raw IPC. To confirm the current ESC listener runs in the floating window (it likely doesn’t), run: 🏁 Script executed: #!/bin/bash
# 1) Find the FloatingChatWindow implementation and see what it loads
fd -t f -a FloatingChatWindow . | while read -r f; do
echo "=== $f ==="
rg -n "class FloatingChatWindow|load(File|URL)|index\.html" -A 5 -B 5 "$f"
done
# 2) Check if any renderer other than App.vue registers the ESC listener
rg -n "addEventListener\\(['\"]keydown['\"].*handleEscKey" -A 2 -B 2
# 3) Check whether App.vue is used by the floating renderer entry (often a different HTML/entry)
rg -n "renderer/.*/floating|floating.*index\\.html|Floating" -S src | sed -n '1,200p'Length of output: 15467 🏁 Script executed: #!/bin/bash
# 1) Search for 'close-floating-window' IPC channel usage
rg -n "close-floating-window" -S src
# 2) Find where ipcMain listens for raw IPC messages in main process
rg -n "ipcMain\.on" -S src/main
rg -n "ipcMain\.handle" -S src/mainLength of output: 871 🏁 Script executed: #!/bin/bash
# Show handler implementation for 'close-floating-window' in windowPresenter
rg -n "ipcMain.on('close-floating-window'" -A5 -B5 src/main/presenter/windowPresenter/index.tsLength of output: 198 🏁 Script executed: #!/bin/bash
# Locate 'close-floating-window' handler in windowPresenter and show surrounding context
file="src/main/presenter/windowPresenter/index.ts"
line=$(grep -n "close-floating-window" "$file" | head -n1 | cut -d: -f1)
if [[ -n "$line" ]]; then
start=$((line - 5))
[[ $start -lt 1 ]] && start=1
end=$((line + 5))
sed -n "${start},${end}p" "$file"
else
echo "Handler not found in $file"
fiLength of output: 622 Use Presenter for ESC Handler & Scope to Floating Chat Window The current ESC listener in App.vue sends a raw IPC message from the main shell window, which is ignored by the
File: -// 处理ESC键 - 关闭悬浮聊天窗口
-const handleEscKey = (event: KeyboardEvent) => {
- if (event.key === 'Escape') {
- window.electron.ipcRenderer.send('close-floating-window')
- }
-}
+// Handle ESC key — close the floating chat window
+const handleEscKey = (event: KeyboardEvent) => {
+ if (event.key !== 'Escape' || event.isComposing || event.repeat) return
+ const active = document.activeElement as HTMLElement | null
+ const tag = active?.tagName?.toLowerCase()
+ const isTyping =
+ active?.isContentEditable ||
+ tag === 'input' ||
+ tag === 'textarea' ||
+ (active as HTMLInputElement)?.type === 'text'
+ if (isTyping) return
+ try {
+ windowPresenter.hideFloatingChatWindow()
+ } catch (err) {
+ console.error('[ERROR][App] Failed to hide floating chat window on ESC:', err)
+ }
+}And near your other presenters, add: const windowPresenter = usePresenter('windowPresenter')🤖 Prompt for AI Agents |
||
| getInitComplete() | ||
|
|
||
| onMounted(() => { | ||
|
|
@@ -179,6 +186,8 @@ onMounted(() => { | |
| document.body.classList.add(themeStore.themeMode) | ||
| document.body.classList.add(settingsStore.fontSizeClass) | ||
|
|
||
| window.addEventListener('keydown', handleEscKey) | ||
|
|
||
| // 监听全局错误通知事件 | ||
| window.electron.ipcRenderer.on(NOTIFICATION_EVENTS.SHOW_ERROR, (_event, error) => { | ||
| showErrorToast(error) | ||
|
|
@@ -287,6 +296,8 @@ onBeforeUnmount(() => { | |
| errorDisplayTimer.value = null | ||
| } | ||
|
|
||
| window.removeEventListener('keydown', handleEscKey) | ||
|
|
||
| // 移除快捷键事件监听 | ||
| window.electron.ipcRenderer.removeAllListeners(SHORTCUT_EVENTS.ZOOM_IN) | ||
| window.electron.ipcRenderer.removeAllListeners(SHORTCUT_EVENTS.ZOOM_OUT) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Ad-hoc IPC channel bypasses presenter pattern and silently no-ops with current App.vue sender. Add error handling or switch to presenter.
Apply this minimal hardening (if you keep the IPC):
Optional follow-ups:
To verify the mismatch causing a no-op, search where the ESC event is emitted and confirm sender IDs:
🏁 Script executed:
Length of output: 588
#!/bin/bash
Verify that hideFloatingChatWindow is defined in the presenter
rg -n "hideFloatingChatWindow" -S src/main/presenter/windowPresenter/index.ts -A 5 -B 5
🏁 Script executed:
Length of output: 1799
Refine close-floating-window IPC handling
The current
ipcMain.on('close-floating-window', …)only hides the floating window when the sender’s WebContents ID matches the floating window, but your ESC handler lives in App.vue (main window). As a result, pressing Escape in the floating window won’t trigger hideFloatingChatWindow, and pressing it in the main window is intentionally ignored by your guard.• Code Locations:
src/main/presenter/windowPresenter/index.ts(lines 58–68): rawipcMainlistenersrc/renderer/src/App.vue(lines 173–177): ESC →ipcRenderer.send('close-floating-window')src/renderer/src/composables/usePresenter.ts: currently does not exposehideFloatingChatWindow• Actionable Fixes:
hideFloatingChatWindowvia your existing Presenter bridge:hideFloatingChatWindowtousePresenter.tsipcRenderer.send(...)ipcMain.on('close-floating-window', (event) => { - // 检查发送者是否是悬浮聊天窗口 - const webContentsId = event.sender.id - if ( - this.floatingChatWindow && - this.floatingChatWindow.getWindow()?.webContents.id === webContentsId - ) { - this.hideFloatingChatWindow() - } + try { + const senderId = event.sender.id + const floatId = this.floatingChatWindow?.getWindow()?.webContents.id + if (floatId === senderId) { + this.hideFloatingChatWindow() + } else { + console.debug( + `Ignored close-floating-window from ${senderId}, expected ${floatId}` + ) + } + } catch (err) { + console.error('Failed to handle close-floating-window IPC:', err) + }• Optional Follow-Ups:
WINDOW_EVENTS.CLOSE_FLOATING_WINDOW)📝 Committable suggestion
🤖 Prompt for AI Agents