-
Notifications
You must be signed in to change notification settings - Fork 614
feat(shell): overlay tooltips for AppBar #1183
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
Conversation
WalkthroughThis PR introduces a tooltip overlay system for the Electron app. It adds a new renderer build entry for the tooltip overlay HTML, implements window lifecycle management for tooltip overlays in the main process, integrates hover-triggered tooltip interactions in the AppBar UI, creates a tooltip overlay renderer module with IPC event handlers, and adds localized UI strings across 11 language files for tooltip-related actions. Changes
Sequence DiagramsequenceDiagram
participant User
participant AppBar as AppBar.vue<br/>(Renderer)
participant IPC as IPC Bridge
participant WinPres as WindowPresenter<br/>(Main)
participant Overlay as TooltipOverlay<br/>Window
User->>AppBar: Hover over button
AppBar->>AppBar: Start tooltipTimer<br/>(debounce)
AppBar->>IPC: shell-tooltip:show<br/>(x, y, text)
IPC->>WinPres: Receive show event
WinPres->>WinPres: getOrCreateTooltipOverlay()
WinPres->>Overlay: Create/show window<br/>if not exists
WinPres->>IPC: shell-tooltip-overlay:show<br/>(payload)
IPC->>Overlay: Receive show event
Overlay->>Overlay: Update text, position,<br/>set visible
Overlay-->>User: Tooltip displayed
User->>AppBar: Mouse leave
AppBar->>IPC: shell-tooltip:hide
IPC->>WinPres: Receive hide event
WinPres->>Overlay: Send hide signal
Overlay->>Overlay: Hide tooltip
Overlay-->>User: Tooltip hidden
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 1
🧹 Nitpick comments (7)
src/renderer/src/i18n/pt-BR/common.json (1)
82-87: Apply hierarchical naming to new translation keys per coding guidelines.The new keys (scrollLeft, scrollRight, history, minimize, maximize, restore) use flat naming, but the coding guidelines require dot-separated hierarchical structure (e.g.,
common.button.submit). These keys represent distinct feature areas (window controls, history, appbar actions) and should use nested naming to reflect their context.Suggested structure:
{ ... "saving": "Salvando", "appbar": { "scrollLeft": "Rolar para a esquerda", "scrollRight": "Rolar para a direita" }, "common": { "history": "Histórico" }, "window": { "minimize": "Minimizar", "maximize": "Maximizar", "restore": "Restaurar" } }Alternatively, if keeping a flat structure, use dot notation:
{ "appbar.scrollLeft": "Rolar para a esquerda", "appbar.scrollRight": "Rolar para a direita", "common.history": "Histórico", "window.minimize": "Minimizar", "window.maximize": "Maximizar", "window.restore": "Restaurar" }Ensure all corresponding locale files are updated consistently with the same hierarchical structure.
src/renderer/src/i18n/zh-TW/common.json (1)
82-87: Consider adopting hierarchical key structure across translation files.The new UI keys (scrollLeft, scrollRight, history, minimize, maximize, restore) follow the existing flat camelCase pattern in this file. However, the coding guideline specifies using dot-separated hierarchical structure (e.g.,
common.button.submit). These keys could benefit from hierarchical naming such as:
appbar.scrollLeft,appbar.scrollRightwindow.minimize,window.maximize,window.restorecommon.historyThis would improve consistency with the stated naming convention, though it would require refactoring across all 11 language files mentioned in the PR. Given the broader scope, this can be deferred to a separate consistency pass.
The translations themselves are accurate and idiomatic for Traditional Chinese (Taiwan).
src/renderer/src/i18n/zh-CN/common.json (1)
82-87: Consider adopting dot-separated hierarchical naming for new keys.The new keys (
scrollLeft,scrollRight,history,minimize,maximize,restore) use camelCase at the root level. Per the i18n coding guidelines, translation keys should follow a dot-separated hierarchical structure (e.g.,appbar.scrollLeft,window.minimize) for better organization and maintainability.While the existing file has a mixed pattern, new keys for this feature should establish a clearer hierarchy. This would improve discoverability and reduce key collisions as the translation set grows.
Consider refactoring to use hierarchical naming:
- "scrollLeft": "向左滚动", - "scrollRight": "向右滚动", - "history": "历史", - "minimize": "最小化", - "maximize": "最大化", - "restore": "还原" + "appbar.scrollLeft": "向左滚动", + "appbar.scrollRight": "向右滚动", + "appbar.history": "历史", + "window.minimize": "最小化", + "window.maximize": "最大化", + "window.restore": "还原"Or, group under a single top-level category if the keys belong together:
- "scrollLeft": "向左滚动", - "scrollRight": "向右滚动", - "history": "历史", - "minimize": "最小化", - "maximize": "最大化", - "restore": "还原" + "tooltip": { + "scrollLeft": "向左滚动", + "scrollRight": "向右滚动", + "history": "历史", + "minimize": "最小化", + "maximize": "最大化", + "restore": "还原" + }If these keys are added with the same naming pattern across all 11 language files, consider whether the code consuming these translations has been updated to match the new key paths.
src/renderer/src/i18n/fa-IR/common.json (1)
82-87: Consider grouping tooltip keys under a parent category.The coding guidelines specify using "dot-separated hierarchical structure" for organization. These tooltip-related keys could be grouped under a parent key (e.g.,
tooltip: { scrollLeft, scrollRight, ... }) for better hierarchy and consistency with the pattern used elsewhere in the file (e.g., theerrorobject). This would align the key structure with the stated naming conventions while maintaining readability.src/main/presenter/windowPresenter/index.ts (1)
956-961: Pre-creating the overlay is a nice UX optimization; consider gating if memory becomes a concern.src/renderer/shell/tooltip-overlay/main.ts (1)
3-7: Consider adding type safety and error boundaries.While the implementation is functional, consider:
- The type definition could be moved to a shared types file for reuse across the codebase
- Adding basic error handling in the
showfunction for invalid coordinates or textExample enhancement:
+// Validate payload before showing const show = (payload: TooltipOverlayShowPayload) => { + if (!payload.text || typeof payload.x !== 'number' || typeof payload.y !== 'number') { + console.warn('Invalid tooltip payload:', payload) + return + } + textNode.textContent = payload.text tooltip.style.left = `${payload.x}px` tooltip.style.top = `${payload.y}px` tooltip.style.transform = 'translate(-50%, 0)' tooltip.classList.remove('hidden') }Also applies to: 36-44
src/renderer/shell/components/AppBar.vue (1)
253-258: Consider throttling position updates during scroll.The
updateTooltipPositionis called on every scroll event viarequestAnimationFrame. For smoother performance with many tabs, consider throttling position updates.Example using a throttle helper:
import { useThrottleFn } from '@vueuse/core' // In component setup const throttledUpdatePosition = useThrottleFn(updateTooltipPosition, 16) // ~60fps const onTabContainerWrapperScroll = () => { requestAnimationFrame(() => { tabContainerWrapperScrollLeft.value = tabContainerWrapper.value?.scrollLeft ?? 0 throttledUpdatePosition() }) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (18)
electron.vite.config.ts(1 hunks)src/main/presenter/windowPresenter/index.ts(6 hunks)src/renderer/shell/components/AppBar.vue(9 hunks)src/renderer/shell/components/app-bar/AppBarTabItem.vue(2 hunks)src/renderer/shell/tooltip-overlay/index.html(1 hunks)src/renderer/shell/tooltip-overlay/main.ts(1 hunks)src/renderer/src/i18n/da-DK/common.json(1 hunks)src/renderer/src/i18n/en-US/common.json(1 hunks)src/renderer/src/i18n/fa-IR/common.json(1 hunks)src/renderer/src/i18n/fr-FR/common.json(1 hunks)src/renderer/src/i18n/he-IL/common.json(1 hunks)src/renderer/src/i18n/ja-JP/common.json(1 hunks)src/renderer/src/i18n/ko-KR/common.json(1 hunks)src/renderer/src/i18n/pt-BR/common.json(1 hunks)src/renderer/src/i18n/ru-RU/common.json(1 hunks)src/renderer/src/i18n/zh-CN/common.json(1 hunks)src/renderer/src/i18n/zh-HK/common.json(1 hunks)src/renderer/src/i18n/zh-TW/common.json(1 hunks)
🧰 Additional context used
📓 Path-based instructions (22)
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use English for logs and comments (Chinese text exists in legacy code, but new code should use English)
Files:
electron.vite.config.tssrc/renderer/shell/components/app-bar/AppBarTabItem.vuesrc/renderer/shell/components/AppBar.vuesrc/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Enable and maintain strict TypeScript type checking for all files
**/*.{ts,tsx}: Always use try-catch to handle possible errors in TypeScript code
Provide meaningful error messages when catching errors
Log detailed error logs including error details, context, and stack traces
Distinguish and handle different error types (UserError, NetworkError, SystemError, BusinessError) with appropriate handlers in TypeScript
Use structured logging with logger.error(), logger.warn(), logger.info(), logger.debug() methods from logging utilities
Do not suppress errors (avoid empty catch blocks or silently ignoring errors)
Provide user-friendly error messages for user-facing errors in TypeScript components
Implement error retry mechanisms for transient failures in TypeScript
Avoid logging sensitive information (passwords, tokens, PII) in logs
Files:
electron.vite.config.tssrc/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Do not include AI co-authoring information (e.g., 'Co-Authored-By: Claude') in git commits
Files:
electron.vite.config.tssrc/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
**/*.{js,ts,jsx,tsx,mjs,cjs}
📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)
Write logs and comments in English
Files:
electron.vite.config.tssrc/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
src/renderer/src/i18n/**/*.json
📄 CodeRabbit inference engine (.cursor/rules/i18n.mdc)
src/renderer/src/i18n/**/*.json: Translation key naming convention: use dot-separated hierarchical structure with lowercase letters and descriptive names (e.g., 'common.button.submit')
Maintain consistent key-value structure across all language translation files (zh-CN, en-US, ko-KR, ru-RU, zh-HK, fr-FR, fa-IR)
Files:
src/renderer/src/i18n/zh-HK/common.jsonsrc/renderer/src/i18n/ko-KR/common.jsonsrc/renderer/src/i18n/fa-IR/common.jsonsrc/renderer/src/i18n/he-IL/common.jsonsrc/renderer/src/i18n/da-DK/common.jsonsrc/renderer/src/i18n/zh-CN/common.jsonsrc/renderer/src/i18n/zh-TW/common.jsonsrc/renderer/src/i18n/en-US/common.jsonsrc/renderer/src/i18n/pt-BR/common.jsonsrc/renderer/src/i18n/ja-JP/common.jsonsrc/renderer/src/i18n/fr-FR/common.jsonsrc/renderer/src/i18n/ru-RU/common.json
src/**/*
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
New features should be developed in the
srcdirectory
Files:
src/renderer/src/i18n/zh-HK/common.jsonsrc/renderer/src/i18n/ko-KR/common.jsonsrc/renderer/src/i18n/fa-IR/common.jsonsrc/renderer/src/i18n/he-IL/common.jsonsrc/renderer/src/i18n/da-DK/common.jsonsrc/renderer/src/i18n/zh-CN/common.jsonsrc/renderer/shell/components/app-bar/AppBarTabItem.vuesrc/renderer/shell/components/AppBar.vuesrc/renderer/shell/tooltip-overlay/main.tssrc/renderer/shell/tooltip-overlay/index.htmlsrc/renderer/src/i18n/zh-TW/common.jsonsrc/renderer/src/i18n/en-US/common.jsonsrc/renderer/src/i18n/pt-BR/common.jsonsrc/renderer/src/i18n/ja-JP/common.jsonsrc/main/presenter/windowPresenter/index.tssrc/renderer/src/i18n/fr-FR/common.jsonsrc/renderer/src/i18n/ru-RU/common.json
src/renderer/**
📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)
Use lowercase with dashes for directories (e.g., components/auth-wizard)
Files:
src/renderer/src/i18n/zh-HK/common.jsonsrc/renderer/src/i18n/ko-KR/common.jsonsrc/renderer/src/i18n/fa-IR/common.jsonsrc/renderer/src/i18n/he-IL/common.jsonsrc/renderer/src/i18n/da-DK/common.jsonsrc/renderer/src/i18n/zh-CN/common.jsonsrc/renderer/shell/components/app-bar/AppBarTabItem.vuesrc/renderer/shell/components/AppBar.vuesrc/renderer/shell/tooltip-overlay/main.tssrc/renderer/shell/tooltip-overlay/index.htmlsrc/renderer/src/i18n/zh-TW/common.jsonsrc/renderer/src/i18n/en-US/common.jsonsrc/renderer/src/i18n/pt-BR/common.jsonsrc/renderer/src/i18n/ja-JP/common.jsonsrc/renderer/src/i18n/fr-FR/common.jsonsrc/renderer/src/i18n/ru-RU/common.json
**/*.vue
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.vue: Use Vue 3 Composition API for all components instead of Options API
Use Tailwind CSS with scoped styles for component styling
Files:
src/renderer/shell/components/app-bar/AppBarTabItem.vuesrc/renderer/shell/components/AppBar.vue
src/renderer/**/*.vue
📄 CodeRabbit inference engine (CLAUDE.md)
src/renderer/**/*.vue: All user-facing strings must use i18n keys via vue-i18n for internationalization
Ensure proper error handling and loading states in all UI components
Implement responsive design using Tailwind CSS utilities for all UI components
src/renderer/**/*.vue: Use composition API and declarative programming patterns; avoid options API
Structure files: exported component, composables, helpers, static content, types
Use PascalCase for component names (e.g., AuthWizard.vue)
Use Vue 3 with TypeScript, leveraging defineComponent and PropType
Use template syntax for declarative rendering
Use Shadcn Vue, Radix Vue, and Tailwind for components and styling
Implement responsive design with Tailwind CSS; use a mobile-first approach
Use Suspense for asynchronous components
Use <script setup> syntax for concise component definitions
Prefer 'lucide:' icon family as the primary choice for Iconify icons
Import Icon component from '@iconify/vue' and use with lucide icons following pattern '{collection}:{icon-name}'
Files:
src/renderer/shell/components/app-bar/AppBarTabItem.vuesrc/renderer/shell/components/AppBar.vue
src/renderer/**/*.{vue,js,ts}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Renderer process code should be placed in
src/renderer(Vue 3 application)
Files:
src/renderer/shell/components/app-bar/AppBarTabItem.vuesrc/renderer/shell/components/AppBar.vuesrc/renderer/shell/tooltip-overlay/main.ts
src/renderer/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)
src/renderer/**/*.{ts,tsx,vue}: Write concise, technical TypeScript code with accurate examples
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Avoid enums; use const objects instead
Use arrow functions for methods and computed properties
Avoid unnecessary curly braces in conditionals; use concise syntax for simple statementsVue 3 app code in
src/renderer/srcshould be organized intocomponents/,stores/,views/,i18n/,lib/directories with shell UI insrc/renderer/shell/
Files:
src/renderer/shell/components/app-bar/AppBarTabItem.vuesrc/renderer/shell/components/AppBar.vuesrc/renderer/shell/tooltip-overlay/main.ts
src/renderer/**/*.{ts,vue}
📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)
src/renderer/**/*.{ts,vue}: Use useFetch and useAsyncData for data fetching
Leverage ref, reactive, and computed for reactive state management
Use provide/inject for dependency injection when appropriate
Use Iconify/Vue for icon implementation
Files:
src/renderer/shell/components/app-bar/AppBarTabItem.vuesrc/renderer/shell/components/AppBar.vuesrc/renderer/shell/tooltip-overlay/main.ts
src/**/*.{ts,tsx,vue,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Prettier with single quotes, no semicolons, and 100 character width
Files:
src/renderer/shell/components/app-bar/AppBarTabItem.vuesrc/renderer/shell/components/AppBar.vuesrc/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
src/renderer/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use the
usePresenter.tscomposable for renderer-to-main IPC communication to call presenter methods directly
Files:
src/renderer/shell/tooltip-overlay/main.ts
{src/main/presenter/**/*.ts,src/renderer/**/*.ts}
📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)
Implement proper inter-process communication (IPC) patterns using Electron's ipcRenderer and ipcMain APIs
Files:
src/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
src/renderer/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)
Use TypeScript for all code; prefer types over interfaces
Files:
src/renderer/shell/tooltip-overlay/main.ts
src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use OxLint for linting JavaScript and TypeScript files
Files:
src/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.{ts,tsx}: Use camelCase for variable and function names in TypeScript files
Use PascalCase for type and class names in TypeScript
Use SCREAMING_SNAKE_CASE for constant names
Files:
src/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use EventBus for inter-process communication events
Files:
src/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
src/main/presenter/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Organize core business logic into dedicated Presenter classes, with one presenter per functional domain
Files:
src/main/presenter/windowPresenter/index.ts
src/main/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use EventBus from
src/main/eventbus.tsfor main-to-renderer communication, broadcasting events viamainWindow.webContents.send()
src/main/**/*.ts: Use EventBus pattern for inter-process communication within the main process to decouple modules
Use Electron's built-in APIs for file system and native dialogs instead of Node.js or custom implementations
src/main/**/*.ts: Electron main process code belongs insrc/main/with presenters inpresenter/(Window/Tab/Thread/Mcp/Config/LLMProvider) andeventbus.tsfor app events
Use the Presenter pattern in the main process for UI coordination
Files:
src/main/presenter/windowPresenter/index.ts
src/main/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Main process code for Electron should be placed in
src/main
Files:
src/main/presenter/windowPresenter/index.ts
🧠 Learnings (28)
📓 Common learnings
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/electron-best-practices.mdc:0-0
Timestamp: 2025-11-25T05:26:24.867Z
Learning: Applies to {src/main/presenter/**/*.ts,src/renderer/**/*.ts} : Implement proper inter-process communication (IPC) patterns using Electron's ipcRenderer and ipcMain APIs
📚 Learning: 2025-11-25T05:26:24.867Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/electron-best-practices.mdc:0-0
Timestamp: 2025-11-25T05:26:24.867Z
Learning: Applies to {src/main/presenter/**/*.ts,src/renderer/**/*.ts} : Implement proper inter-process communication (IPC) patterns using Electron's ipcRenderer and ipcMain APIs
Applied to files:
electron.vite.config.tssrc/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
📚 Learning: 2025-10-12T00:58:06.513Z
Learnt from: zerob13
Repo: ThinkInAIXYZ/deepchat PR: 977
File: package.json:137-137
Timestamp: 2025-10-12T00:58:06.513Z
Learning: In this Electron project, renderer process dependencies (used only in src/renderer/) should be placed in devDependencies because they are bundled by electron-vite during the build process. Only main process dependencies (used in src/main/) need to be in the dependencies section.
Applied to files:
electron.vite.config.ts
📚 Learning: 2025-11-25T05:26:24.867Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/electron-best-practices.mdc:0-0
Timestamp: 2025-11-25T05:26:24.867Z
Learning: Applies to src/main/**/*.ts : Use Electron's built-in APIs for file system and native dialogs instead of Node.js or custom implementations
Applied to files:
electron.vite.config.tssrc/renderer/shell/tooltip-overlay/main.ts
📚 Learning: 2025-11-25T05:28:20.513Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T05:28:20.513Z
Learning: Applies to src/renderer/src/**/*.{ts,tsx,vue} : Use TypeScript with Vue 3 Composition API for the renderer application
Applied to files:
electron.vite.config.tssrc/renderer/shell/components/AppBar.vue
📚 Learning: 2025-11-25T05:28:20.513Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T05:28:20.513Z
Learning: Applies to src/renderer/**/*.{ts,tsx,vue} : Vue 3 app code in `src/renderer/src` should be organized into `components/`, `stores/`, `views/`, `i18n/`, `lib/` directories with shell UI in `src/renderer/shell/`
Applied to files:
electron.vite.config.ts
📚 Learning: 2025-11-25T05:28:04.454Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-shadcn.mdc:0-0
Timestamp: 2025-11-25T05:28:04.454Z
Learning: Applies to src/renderer/**/*.{ts,tsx,vue} : Write concise, technical TypeScript code with accurate examples
Applied to files:
electron.vite.config.tssrc/renderer/shell/tooltip-overlay/main.ts
📚 Learning: 2025-11-25T05:27:39.200Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/provider-guidelines.mdc:0-0
Timestamp: 2025-11-25T05:27:39.200Z
Learning: Applies to **/*Provider**/index.ts : Do not introduce renderer dependencies inside Provider implementations
Applied to files:
electron.vite.config.ts
📚 Learning: 2025-11-25T05:28:04.454Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-shadcn.mdc:0-0
Timestamp: 2025-11-25T05:28:04.454Z
Learning: Applies to src/renderer/**/*.vue : Structure files: exported component, composables, helpers, static content, types
Applied to files:
electron.vite.config.ts
📚 Learning: 2025-11-25T05:26:43.510Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/i18n.mdc:0-0
Timestamp: 2025-11-25T05:26:43.510Z
Learning: Applies to src/renderer/src/i18n/**/*.json : Maintain consistent key-value structure across all language translation files (zh-CN, en-US, ko-KR, ru-RU, zh-HK, fr-FR, fa-IR)
Applied to files:
src/renderer/src/i18n/zh-HK/common.jsonsrc/renderer/src/i18n/ko-KR/common.jsonsrc/renderer/src/i18n/fa-IR/common.jsonsrc/renderer/src/i18n/he-IL/common.jsonsrc/renderer/src/i18n/da-DK/common.jsonsrc/renderer/src/i18n/zh-CN/common.jsonsrc/renderer/src/i18n/zh-TW/common.jsonsrc/renderer/src/i18n/en-US/common.jsonsrc/renderer/src/i18n/pt-BR/common.jsonsrc/renderer/src/i18n/ja-JP/common.jsonsrc/renderer/src/i18n/fr-FR/common.jsonsrc/renderer/src/i18n/ru-RU/common.json
📚 Learning: 2025-11-25T05:26:43.510Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/i18n.mdc:0-0
Timestamp: 2025-11-25T05:26:43.510Z
Learning: Applies to src/renderer/src/i18n/**/*.json : Translation key naming convention: use dot-separated hierarchical structure with lowercase letters and descriptive names (e.g., 'common.button.submit')
Applied to files:
src/renderer/src/i18n/zh-HK/common.jsonsrc/renderer/src/i18n/ko-KR/common.jsonsrc/renderer/src/i18n/fa-IR/common.jsonsrc/renderer/src/i18n/he-IL/common.jsonsrc/renderer/src/i18n/da-DK/common.jsonsrc/renderer/src/i18n/zh-CN/common.jsonsrc/renderer/src/i18n/zh-TW/common.jsonsrc/renderer/src/i18n/en-US/common.jsonsrc/renderer/src/i18n/pt-BR/common.jsonsrc/renderer/src/i18n/ja-JP/common.jsonsrc/renderer/src/i18n/fr-FR/common.jsonsrc/renderer/src/i18n/ru-RU/common.json
📚 Learning: 2025-11-25T05:26:43.510Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/i18n.mdc:0-0
Timestamp: 2025-11-25T05:26:43.510Z
Learning: Applies to src/renderer/src/**/*.{vue,ts,tsx} : Avoid hardcoding user-facing text and ensure all user-visible text uses the i18n translation system
Applied to files:
src/renderer/src/i18n/zh-CN/common.json
📚 Learning: 2025-11-25T05:27:45.545Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-best-practices.mdc:0-0
Timestamp: 2025-11-25T05:27:45.545Z
Learning: Applies to src/renderer/src/**/*.{vue,ts,tsx,js,jsx} : Use the Composition API for better code organization and reusability in Vue.js applications
Applied to files:
src/renderer/shell/components/AppBar.vue
📚 Learning: 2025-11-25T05:28:04.454Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-shadcn.mdc:0-0
Timestamp: 2025-11-25T05:28:04.454Z
Learning: Applies to src/renderer/**/*.{ts,vue} : Use Iconify/Vue for icon implementation
Applied to files:
src/renderer/shell/components/AppBar.vue
📚 Learning: 2025-11-25T05:28:04.454Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-shadcn.mdc:0-0
Timestamp: 2025-11-25T05:28:04.454Z
Learning: Applies to src/renderer/**/composables/*.ts : Use VueUse for common composables and utility functions
Applied to files:
src/renderer/shell/components/AppBar.vue
📚 Learning: 2025-11-25T05:28:04.454Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-shadcn.mdc:0-0
Timestamp: 2025-11-25T05:28:04.454Z
Learning: Applies to src/renderer/**/*.{ts,vue} : Leverage ref, reactive, and computed for reactive state management
Applied to files:
src/renderer/shell/components/AppBar.vue
📚 Learning: 2025-11-25T05:28:04.454Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-shadcn.mdc:0-0
Timestamp: 2025-11-25T05:28:04.454Z
Learning: Applies to src/renderer/**/*.vue : Use <script setup> syntax for concise component definitions
Applied to files:
src/renderer/shell/components/AppBar.vue
📚 Learning: 2025-11-25T05:26:11.312Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T05:26:11.312Z
Learning: Applies to src/renderer/**/*.ts : Use the `usePresenter.ts` composable for renderer-to-main IPC communication to call presenter methods directly
Applied to files:
src/renderer/shell/components/AppBar.vuesrc/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
📚 Learning: 2025-11-25T05:28:04.454Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-shadcn.mdc:0-0
Timestamp: 2025-11-25T05:28:04.454Z
Learning: Applies to src/renderer/**/*.vue : Use composition API and declarative programming patterns; avoid options API
Applied to files:
src/renderer/shell/components/AppBar.vue
📚 Learning: 2025-11-25T05:28:04.454Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-shadcn.mdc:0-0
Timestamp: 2025-11-25T05:28:04.454Z
Learning: Applies to src/renderer/**/*.vue : Use Vue 3 with TypeScript, leveraging defineComponent and PropType
Applied to files:
src/renderer/shell/components/AppBar.vue
📚 Learning: 2025-11-25T05:27:45.545Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-best-practices.mdc:0-0
Timestamp: 2025-11-25T05:27:45.545Z
Learning: Applies to src/renderer/src/**/*.{vue,ts,tsx,js,jsx} : Leverage Vue's built-in reactivity system for efficient data handling
Applied to files:
src/renderer/shell/components/AppBar.vue
📚 Learning: 2025-11-25T05:28:20.513Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T05:28:20.513Z
Learning: Applies to src/main/**/*.ts : Electron main process code belongs in `src/main/` with presenters in `presenter/` (Window/Tab/Thread/Mcp/Config/LLMProvider) and `eventbus.ts` for app events
Applied to files:
src/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
📚 Learning: 2025-11-25T05:26:24.867Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/electron-best-practices.mdc:0-0
Timestamp: 2025-11-25T05:26:24.867Z
Learning: Applies to src/shared/**/*.d.ts : Define type definitions in shared/*.d.ts files for objects exposed by the main process to the renderer process
Applied to files:
src/renderer/shell/tooltip-overlay/main.ts
📚 Learning: 2025-11-25T05:28:20.513Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T05:28:20.513Z
Learning: Applies to src/main/**/*.ts : Use the Presenter pattern in the main process for UI coordination
Applied to files:
src/renderer/shell/tooltip-overlay/main.tssrc/main/presenter/windowPresenter/index.ts
📚 Learning: 2025-11-25T05:27:26.656Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T05:27:26.656Z
Learning: Applies to src/shared/**/*.{js,ts} : Shared type definitions and utilities between main and renderer processes should be placed in `src/shared`
Applied to files:
src/renderer/shell/tooltip-overlay/main.ts
📚 Learning: 2025-11-25T05:28:04.454Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-shadcn.mdc:0-0
Timestamp: 2025-11-25T05:28:04.454Z
Learning: Applies to src/renderer/**/*.{ts,tsx} : Use TypeScript for all code; prefer types over interfaces
Applied to files:
src/renderer/shell/tooltip-overlay/main.ts
📚 Learning: 2025-11-25T05:28:20.513Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T05:28:20.513Z
Learning: Applies to src/preload/**/*.ts : Secure IPC bridge should be implemented in `src/preload/` with contextIsolation enabled
Applied to files:
src/renderer/shell/tooltip-overlay/main.ts
📚 Learning: 2025-11-25T05:26:11.312Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T05:26:11.312Z
Learning: Applies to src/main/presenter/mcpPresenter/**/*.ts : Register new MCP tools in `mcpPresenter/index.ts` after implementing them in `inMemoryServers/`
Applied to files:
src/main/presenter/windowPresenter/index.ts
🧬 Code graph analysis (2)
src/renderer/shell/tooltip-overlay/main.ts (1)
test/mocks/electron.ts (1)
ipcRenderer(18-23)
src/main/presenter/windowPresenter/index.ts (2)
test/mocks/electron.ts (2)
BrowserWindow(36-51)ipcMain(12-16)test/mocks/electron-toolkit-utils.ts (1)
is(1-3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-check (x64)
🔇 Additional comments (19)
src/renderer/src/i18n/fa-IR/common.json (2)
81-81: Trailing comma correctly added.The comma after
"saving"properly accommodates the new entries that follow.
82-87: Tooltip keys are consistent across all language files.All six tooltip keys (
scrollLeft,scrollRight,history,minimize,maximize,restore) are present in all language translation files (en-US, zh-CN, ko-KR, ru-RU, zh-HK, fr-FR, and fa-IR), maintaining the required consistent key-value structure.src/renderer/src/i18n/he-IL/common.json (1)
82-87: Consider organizing new tooltip keys under a hierarchical parent key.The six new keys (scrollLeft, scrollRight, history, minimize, maximize, restore) are added as flat root-level keys. Per coding guidelines, translation keys should use dot-separated hierarchical structure with lowercase descriptive names (e.g.,
common.button.submit). While these keys follow the existing flat-key pattern in the file, they could be better organized under a parent key since they're all tooltip-related UI actions and window controls.Consider restructuring as:
"tooltip": { "scrollLeft": "גלול שמאלה", "scrollRight": "גלול ימינה", "history": "היסטוריה", "minimize": "מזער", "maximize": "הגדל", "restore": "שחזר" }However, this should match the structure used in other language files to maintain consistency.
[request_verification, suggest_recommended_refactor]Verify that:
- These keys are added with the same structure (flat vs. nested) across all 11 language files mentioned in the AI summary
- The hierarchical naming convention is consistently applied or if flat keys are acceptable for general UI actions in this project
#!/bin/bash # Verify consistency of new tooltip keys across language files # Check if the keys exist in other i18n files and their structure find src/renderer/src/i18n -name "common.json" -type f | while read file; do echo "=== Checking $file ===" grep -E '"(scrollLeft|scrollRight|history|minimize|maximize|restore)"' "$file" || echo "Missing keys in $file" donesrc/renderer/src/i18n/da-DK/common.json (1)
81-87: Locale parity looks consistent with the new tooltip/AppBar actions.Added
scrollLeft,scrollRight,history,minimize,maximize,restorealigns with the PR’s new UI actions and matches the en-US key set.src/renderer/src/i18n/zh-HK/common.json (1)
81-87: LGTM: keys added + kept consistent with other locales.src/renderer/src/i18n/ja-JP/common.json (1)
81-87: LGTM: keys added + consistent with the new AppBar tooltip actions.src/main/presenter/windowPresenter/index.ts (4)
43-45: Good direction: per-window overlay state + pending payload tracking is straightforward.
Based on learnings/coding guidelines, keeping this scoped byparentWindow.idis the right granularity for IPC-driven overlays.
767-778: Good lifecycle hook: clear tooltip on parent blur avoids “stuck tooltip”.
911-929: Good cleanup: destroy overlay on parent window close.
This should prevent orphaned overlay windows and payload state.
71-107: [rewritten comment]
[classification tag]src/renderer/src/i18n/en-US/common.json (1)
81-87: i18n keys follow the established flat camelCase convention and are correctly distributed across locales.The added keys (
scrollLeft,scrollRight,history,minimize,maximize,restore) match the existing naming pattern in the file and are present across all localecommon.jsonfiles. The documented guideline for dot-separated lowercase naming does not reflect the actual implementation pattern used throughout the codebase; the established convention is flat camelCase, which these keys correctly follow. No action required.src/renderer/shell/components/app-bar/AppBarTabItem.vue (1)
5-5: LGTM! Correct implementation of non-draggable region for Electron.The addition of the
window-no-drag-regionclass and corresponding scoped style correctly disables window dragging for tab items, enabling stable hover interactions for the new tooltip overlay system.Also applies to: 60-65
electron.vite.config.ts (1)
105-105: LGTM! Correctly adds tooltip overlay entry point.The new
shellTooltipOverlayentry follows the existing pattern and correctly points to the tooltip overlay HTML file.src/renderer/shell/tooltip-overlay/index.html (1)
1-12: LGTM! Clean HTML entry point for tooltip overlay.The minimal HTML structure is appropriate for an overlay window, with transparent background and module script loading correctly configured.
src/renderer/src/i18n/ru-RU/common.json (1)
82-87: LGTM! Translation keys correctly added.The new translation keys for tooltip actions follow the naming convention and are consistent with the tooltip overlay feature. Ensure these same keys are present across all language files.
src/renderer/src/i18n/fr-FR/common.json (1)
82-87: LGTM! French translations correctly added.The translation keys match the structure added to other language files and properly support the tooltip overlay feature.
src/renderer/shell/components/AppBar.vue (2)
187-216: LGTM! Well-implemented tooltip lifecycle management.The tooltip system correctly implements:
- Debounced hover (200ms) to prevent flickering
- Proper cleanup on unmount (timer and tooltip hiding)
- Position updates on resize and scroll
- Type-safe state management
The IPC channel naming issue is addressed in the
tooltip-overlay/main.tsreview.Also applies to: 576-582
218-251: Well-structured hover event handlers.The hover logic correctly:
- Tracks the currently hovered target to prevent race conditions
- Clears timers on leave to cancel pending tooltips
- Uses unique keys for each hoverable element (including tab IDs)
src/renderer/shell/tooltip-overlay/main.ts (1)
46-56: The IPC channel architecture is correct and working as designed. The main process (windowPresenter) intentionally bridges renderer channels (shell-tooltip:show,shell-tooltip:hide) to overlay window channels (shell-tooltip-overlay:show,shell-tooltip-overlay:hide), as shown in the handler implementation. This is proper inter-process communication design—not a channel mismatch issue.Likely an incorrect or invalid review comment.
| private getOrCreateTooltipOverlay(parentWindow: BrowserWindow): BrowserWindow | null { | ||
| if (parentWindow.isDestroyed()) return null | ||
|
|
||
| const existing = this.tooltipOverlayWindows.get(parentWindow.id) | ||
| if (existing && !existing.isDestroyed()) { | ||
| this.syncTooltipOverlayBounds(parentWindow, existing) | ||
| if (!existing.isVisible()) { | ||
| existing.showInactive() | ||
| } | ||
| return existing | ||
| } | ||
|
|
||
| const bounds = parentWindow.getContentBounds() | ||
|
|
||
| const overlay = new BrowserWindow({ | ||
| x: bounds.x, | ||
| y: bounds.y, | ||
| width: bounds.width, | ||
| height: bounds.height, | ||
| parent: parentWindow, | ||
| show: false, | ||
| frame: false, | ||
| transparent: true, | ||
| backgroundColor: '#00000000', | ||
| resizable: false, | ||
| movable: false, | ||
| minimizable: false, | ||
| maximizable: false, | ||
| closable: false, | ||
| hasShadow: false, | ||
| focusable: false, | ||
| skipTaskbar: true, | ||
| autoHideMenuBar: true, | ||
| webPreferences: { | ||
| preload: join(__dirname, '../preload/index.mjs'), | ||
| sandbox: false, | ||
| devTools: is.dev | ||
| } | ||
| }) | ||
|
|
||
| overlay.setIgnoreMouseEvents(true, { forward: true }) | ||
|
|
||
| const sync = () => { | ||
| const current = this.tooltipOverlayWindows.get(parentWindow.id) | ||
| if (!current || current.isDestroyed() || parentWindow.isDestroyed()) return | ||
| this.syncTooltipOverlayBounds(parentWindow, current) | ||
| } | ||
|
|
||
| parentWindow.on('move', sync) | ||
| parentWindow.on('resize', sync) | ||
| parentWindow.on('show', () => { | ||
| if (!overlay.isDestroyed()) overlay.showInactive() | ||
| }) | ||
| parentWindow.on('hide', () => { | ||
| if (!overlay.isDestroyed()) overlay.hide() | ||
| }) | ||
| parentWindow.on('minimize', () => { | ||
| if (!overlay.isDestroyed()) overlay.hide() | ||
| }) | ||
| parentWindow.on('restore', () => { | ||
| if (!overlay.isDestroyed()) overlay.showInactive() | ||
| }) | ||
|
|
||
| overlay.on('closed', () => { | ||
| this.tooltipOverlayWindows.delete(parentWindow.id) | ||
| this.pendingTooltipPayload.delete(parentWindow.id) | ||
| }) | ||
|
|
||
| if (is.dev && process.env['ELECTRON_RENDERER_URL']) { | ||
| overlay.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/shell/tooltip-overlay/index.html') | ||
| } else { | ||
| overlay.loadFile(join(__dirname, '../renderer/shell/tooltip-overlay/index.html')) | ||
| } | ||
|
|
||
| overlay.webContents.once('did-finish-load', () => { | ||
| if (overlay.isDestroyed()) return | ||
| overlay.showInactive() | ||
| overlay.webContents.send('shell-tooltip-overlay:clear') | ||
|
|
||
| const pending = this.pendingTooltipPayload.get(parentWindow.id) | ||
| if (pending) { | ||
| overlay.webContents.send('shell-tooltip-overlay:show', pending) | ||
| } | ||
| }) | ||
|
|
||
| this.tooltipOverlayWindows.set(parentWindow.id, overlay) | ||
| return overlay | ||
| } | ||
|
|
||
| private syncTooltipOverlayBounds(parentWindow: BrowserWindow, overlay: BrowserWindow): void { | ||
| if (parentWindow.isDestroyed() || overlay.isDestroyed()) return | ||
| const bounds = parentWindow.getContentBounds() | ||
| overlay.setBounds(bounds) | ||
| } | ||
|
|
||
| private clearTooltipOverlay(windowId: number): void { | ||
| const overlay = this.tooltipOverlayWindows.get(windowId) | ||
| if (!overlay || overlay.isDestroyed()) return | ||
| this.pendingTooltipPayload.delete(windowId) | ||
| overlay.webContents.send('shell-tooltip-overlay:hide') | ||
| } | ||
|
|
||
| private destroyTooltipOverlay(windowId: number): void { | ||
| const overlay = this.tooltipOverlayWindows.get(windowId) | ||
| if (overlay && !overlay.isDestroyed()) { | ||
| overlay.destroy() | ||
| } | ||
| this.tooltipOverlayWindows.delete(windowId) | ||
| this.pendingTooltipPayload.delete(windowId) | ||
| } |
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.
🧩 Analysis chain
🏁 Script executed:
# First, let's verify the exact code at the specified lines
cat -n src/main/presenter/windowPresenter/index.ts | sed -n '1033,1142p'Repository: ThinkInAIXYZ/deepchat
Length of output: 4394
🏁 Script executed:
# Check the full context of the windowPresenter file to understand patterns
wc -l src/main/presenter/windowPresenter/index.tsRepository: ThinkInAIXYZ/deepchat
Length of output: 113
🏁 Script executed:
# Look for how tooltips are managed and destroyed
rg -A 5 -B 5 "tooltipOverlayWindows" src/main/presenter/windowPresenter/index.tsRepository: ThinkInAIXYZ/deepchat
Length of output: 3152
🏁 Script executed:
# Check if there are other overlay window management patterns in the codebase
rg -A 10 "overlay\.on\('closed'" src/Repository: ThinkInAIXYZ/deepchat
Length of output: 967
🏁 Script executed:
# Verify error handling patterns for webContents.send() in the codebase
rg "webContents\.send" src/main/presenter/windowPresenter/index.ts | head -20Repository: ThinkInAIXYZ/deepchat
Length of output: 1360
🏁 Script executed:
# Check if try-catch is used for webContents.send() elsewhere
rg -B 3 -A 3 "webContents\.send" src/main/ | grep -A 3 "try\|catch" | head -30Repository: ThinkInAIXYZ/deepchat
Length of output: 1912
🏁 Script executed:
# Check how overlay is called to understand if recreation scenario is realistic
rg "getOrCreateTooltipOverlay|destroyTooltipOverlay" src/main/presenter/windowPresenter/index.ts -B 2 -A 2Repository: ThinkInAIXYZ/deepchat
Length of output: 1015
🏁 Script executed:
# Check the parent window lifecycle to understand if parent stays alive while overlay is destroyed
rg "shell-tooltip" src/main/presenter/windowPresenter/index.ts | head -20Repository: ThinkInAIXYZ/deepchat
Length of output: 544
Remove parentWindow event listeners when overlay is destroyed to prevent listener accumulation, check parent visibility before showing overlay, and add error handling for webContents.send() calls.
getOrCreateTooltipOverlay() attaches listeners to parentWindow every time a new overlay is created. When an overlay is destroyed (due to crash, reload, or manual destruction) while the parent window remains alive, the listeners are never removed. If the overlay is recreated for the same parent, multiple listener instances accumulate, causing memory leaks and duplicate event handling.
Additionally, did-finish-load unconditionally calls overlay.showInactive() without checking if the parent window is hidden or minimized, potentially showing the overlay when it shouldn't be visible. Finally, webContents.send() calls lack error handling, inconsistent with the error handling pattern used elsewhere in this file for similar operations.
const sync = () => {
const current = this.tooltipOverlayWindows.get(parentWindow.id)
if (!current || current.isDestroyed() || parentWindow.isDestroyed()) return
this.syncTooltipOverlayBounds(parentWindow, current)
}
+ const onShow = () => {
+ if (!overlay.isDestroyed()) overlay.showInactive()
+ }
+ const onHide = () => {
+ if (!overlay.isDestroyed()) overlay.hide()
+ }
+ const onMinimize = () => {
+ if (!overlay.isDestroyed()) overlay.hide()
+ }
+ const onRestore = () => {
+ if (!overlay.isDestroyed()) overlay.showInactive()
+ }
+
parentWindow.on('move', sync)
parentWindow.on('resize', sync)
- parentWindow.on('show', () => {
- if (!overlay.isDestroyed()) overlay.showInactive()
- })
- parentWindow.on('hide', () => {
- if (!overlay.isDestroyed()) overlay.hide()
- })
- parentWindow.on('minimize', () => {
- if (!overlay.isDestroyed()) overlay.hide()
- })
- parentWindow.on('restore', () => {
- if (!overlay.isDestroyed()) overlay.showInactive()
- })
+ parentWindow.on('show', onShow)
+ parentWindow.on('hide', onHide)
+ parentWindow.on('minimize', onMinimize)
+ parentWindow.on('restore', onRestore)
overlay.on('closed', () => {
+ if (!parentWindow.isDestroyed()) {
+ parentWindow.removeListener('move', sync)
+ parentWindow.removeListener('resize', sync)
+ parentWindow.removeListener('show', onShow)
+ parentWindow.removeListener('hide', onHide)
+ parentWindow.removeListener('minimize', onMinimize)
+ parentWindow.removeListener('restore', onRestore)
+ }
this.tooltipOverlayWindows.delete(parentWindow.id)
this.pendingTooltipPayload.delete(parentWindow.id)
})
overlay.webContents.once('did-finish-load', () => {
if (overlay.isDestroyed()) return
+ if (parentWindow.isDestroyed() || parentWindow.isMinimized() || !parentWindow.isVisible()) return
overlay.showInactive()
- overlay.webContents.send('shell-tooltip-overlay:clear')
+ try {
+ overlay.webContents.send('shell-tooltip-overlay:clear')
+ } catch (error) {
+ console.error('Failed to clear tooltip overlay:', error)
+ }
const pending = this.pendingTooltipPayload.get(parentWindow.id)
if (pending) {
- overlay.webContents.send('shell-tooltip-overlay:show', pending)
+ try {
+ overlay.webContents.send('shell-tooltip-overlay:show', pending)
+ } catch (error) {
+ console.error('Failed to show pending tooltip payload:', error)
+ }
}
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private getOrCreateTooltipOverlay(parentWindow: BrowserWindow): BrowserWindow | null { | |
| if (parentWindow.isDestroyed()) return null | |
| const existing = this.tooltipOverlayWindows.get(parentWindow.id) | |
| if (existing && !existing.isDestroyed()) { | |
| this.syncTooltipOverlayBounds(parentWindow, existing) | |
| if (!existing.isVisible()) { | |
| existing.showInactive() | |
| } | |
| return existing | |
| } | |
| const bounds = parentWindow.getContentBounds() | |
| const overlay = new BrowserWindow({ | |
| x: bounds.x, | |
| y: bounds.y, | |
| width: bounds.width, | |
| height: bounds.height, | |
| parent: parentWindow, | |
| show: false, | |
| frame: false, | |
| transparent: true, | |
| backgroundColor: '#00000000', | |
| resizable: false, | |
| movable: false, | |
| minimizable: false, | |
| maximizable: false, | |
| closable: false, | |
| hasShadow: false, | |
| focusable: false, | |
| skipTaskbar: true, | |
| autoHideMenuBar: true, | |
| webPreferences: { | |
| preload: join(__dirname, '../preload/index.mjs'), | |
| sandbox: false, | |
| devTools: is.dev | |
| } | |
| }) | |
| overlay.setIgnoreMouseEvents(true, { forward: true }) | |
| const sync = () => { | |
| const current = this.tooltipOverlayWindows.get(parentWindow.id) | |
| if (!current || current.isDestroyed() || parentWindow.isDestroyed()) return | |
| this.syncTooltipOverlayBounds(parentWindow, current) | |
| } | |
| parentWindow.on('move', sync) | |
| parentWindow.on('resize', sync) | |
| parentWindow.on('show', () => { | |
| if (!overlay.isDestroyed()) overlay.showInactive() | |
| }) | |
| parentWindow.on('hide', () => { | |
| if (!overlay.isDestroyed()) overlay.hide() | |
| }) | |
| parentWindow.on('minimize', () => { | |
| if (!overlay.isDestroyed()) overlay.hide() | |
| }) | |
| parentWindow.on('restore', () => { | |
| if (!overlay.isDestroyed()) overlay.showInactive() | |
| }) | |
| overlay.on('closed', () => { | |
| this.tooltipOverlayWindows.delete(parentWindow.id) | |
| this.pendingTooltipPayload.delete(parentWindow.id) | |
| }) | |
| if (is.dev && process.env['ELECTRON_RENDERER_URL']) { | |
| overlay.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/shell/tooltip-overlay/index.html') | |
| } else { | |
| overlay.loadFile(join(__dirname, '../renderer/shell/tooltip-overlay/index.html')) | |
| } | |
| overlay.webContents.once('did-finish-load', () => { | |
| if (overlay.isDestroyed()) return | |
| overlay.showInactive() | |
| overlay.webContents.send('shell-tooltip-overlay:clear') | |
| const pending = this.pendingTooltipPayload.get(parentWindow.id) | |
| if (pending) { | |
| overlay.webContents.send('shell-tooltip-overlay:show', pending) | |
| } | |
| }) | |
| this.tooltipOverlayWindows.set(parentWindow.id, overlay) | |
| return overlay | |
| } | |
| private syncTooltipOverlayBounds(parentWindow: BrowserWindow, overlay: BrowserWindow): void { | |
| if (parentWindow.isDestroyed() || overlay.isDestroyed()) return | |
| const bounds = parentWindow.getContentBounds() | |
| overlay.setBounds(bounds) | |
| } | |
| private clearTooltipOverlay(windowId: number): void { | |
| const overlay = this.tooltipOverlayWindows.get(windowId) | |
| if (!overlay || overlay.isDestroyed()) return | |
| this.pendingTooltipPayload.delete(windowId) | |
| overlay.webContents.send('shell-tooltip-overlay:hide') | |
| } | |
| private destroyTooltipOverlay(windowId: number): void { | |
| const overlay = this.tooltipOverlayWindows.get(windowId) | |
| if (overlay && !overlay.isDestroyed()) { | |
| overlay.destroy() | |
| } | |
| this.tooltipOverlayWindows.delete(windowId) | |
| this.pendingTooltipPayload.delete(windowId) | |
| } | |
| private getOrCreateTooltipOverlay(parentWindow: BrowserWindow): BrowserWindow | null { | |
| if (parentWindow.isDestroyed()) return null | |
| const existing = this.tooltipOverlayWindows.get(parentWindow.id) | |
| if (existing && !existing.isDestroyed()) { | |
| this.syncTooltipOverlayBounds(parentWindow, existing) | |
| if (!existing.isVisible()) { | |
| existing.showInactive() | |
| } | |
| return existing | |
| } | |
| const bounds = parentWindow.getContentBounds() | |
| const overlay = new BrowserWindow({ | |
| x: bounds.x, | |
| y: bounds.y, | |
| width: bounds.width, | |
| height: bounds.height, | |
| parent: parentWindow, | |
| show: false, | |
| frame: false, | |
| transparent: true, | |
| backgroundColor: '#00000000', | |
| resizable: false, | |
| movable: false, | |
| minimizable: false, | |
| maximizable: false, | |
| closable: false, | |
| hasShadow: false, | |
| focusable: false, | |
| skipTaskbar: true, | |
| autoHideMenuBar: true, | |
| webPreferences: { | |
| preload: join(__dirname, '../preload/index.mjs'), | |
| sandbox: false, | |
| devTools: is.dev | |
| } | |
| }) | |
| overlay.setIgnoreMouseEvents(true, { forward: true }) | |
| const sync = () => { | |
| const current = this.tooltipOverlayWindows.get(parentWindow.id) | |
| if (!current || current.isDestroyed() || parentWindow.isDestroyed()) return | |
| this.syncTooltipOverlayBounds(parentWindow, current) | |
| } | |
| const onShow = () => { | |
| if (!overlay.isDestroyed()) overlay.showInactive() | |
| } | |
| const onHide = () => { | |
| if (!overlay.isDestroyed()) overlay.hide() | |
| } | |
| const onMinimize = () => { | |
| if (!overlay.isDestroyed()) overlay.hide() | |
| } | |
| const onRestore = () => { | |
| if (!overlay.isDestroyed()) overlay.showInactive() | |
| } | |
| parentWindow.on('move', sync) | |
| parentWindow.on('resize', sync) | |
| parentWindow.on('show', onShow) | |
| parentWindow.on('hide', onHide) | |
| parentWindow.on('minimize', onMinimize) | |
| parentWindow.on('restore', onRestore) | |
| overlay.on('closed', () => { | |
| if (!parentWindow.isDestroyed()) { | |
| parentWindow.removeListener('move', sync) | |
| parentWindow.removeListener('resize', sync) | |
| parentWindow.removeListener('show', onShow) | |
| parentWindow.removeListener('hide', onHide) | |
| parentWindow.removeListener('minimize', onMinimize) | |
| parentWindow.removeListener('restore', onRestore) | |
| } | |
| this.tooltipOverlayWindows.delete(parentWindow.id) | |
| this.pendingTooltipPayload.delete(parentWindow.id) | |
| }) | |
| if (is.dev && process.env['ELECTRON_RENDERER_URL']) { | |
| overlay.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/shell/tooltip-overlay/index.html') | |
| } else { | |
| overlay.loadFile(join(__dirname, '../renderer/shell/tooltip-overlay/index.html')) | |
| } | |
| overlay.webContents.once('did-finish-load', () => { | |
| if (overlay.isDestroyed()) return | |
| if (parentWindow.isDestroyed() || parentWindow.isMinimized() || !parentWindow.isVisible()) return | |
| overlay.showInactive() | |
| try { | |
| overlay.webContents.send('shell-tooltip-overlay:clear') | |
| } catch (error) { | |
| console.error('Failed to clear tooltip overlay:', error) | |
| } | |
| const pending = this.pendingTooltipPayload.get(parentWindow.id) | |
| if (pending) { | |
| try { | |
| overlay.webContents.send('shell-tooltip-overlay:show', pending) | |
| } catch (error) { | |
| console.error('Failed to show pending tooltip payload:', error) | |
| } | |
| } | |
| }) | |
| this.tooltipOverlayWindows.set(parentWindow.id, overlay) | |
| return overlay | |
| } | |
| private syncTooltipOverlayBounds(parentWindow: BrowserWindow, overlay: BrowserWindow): void { | |
| if (parentWindow.isDestroyed() || overlay.isDestroyed()) return | |
| const bounds = parentWindow.getContentBounds() | |
| overlay.setBounds(bounds) | |
| } | |
| private clearTooltipOverlay(windowId: number): void { | |
| const overlay = this.tooltipOverlayWindows.get(windowId) | |
| if (!overlay || overlay.isDestroyed()) return | |
| this.pendingTooltipPayload.delete(windowId) | |
| overlay.webContents.send('shell-tooltip-overlay:hide') | |
| } | |
| private destroyTooltipOverlay(windowId: number): void { | |
| const overlay = this.tooltipOverlayWindows.get(windowId) | |
| if (overlay && !overlay.isDestroyed()) { | |
| overlay.destroy() | |
| } | |
| this.tooltipOverlayWindows.delete(windowId) | |
| this.pendingTooltipPayload.delete(windowId) | |
| } |
|
@codex review |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
LGTM |

Summary by CodeRabbit
New Features
Internationalization
✏️ Tip: You can customize this high-level summary in your review settings.