Skip to content

Conversation

@yyhhyyyyyy
Copy link
Collaborator

@yyhhyyyyyy yyhhyyyyyy commented Aug 11, 2025

close #719

  • Add complete floating chat window with position calculation and screen boundary handling
image
  • Add right-click context menu for floating button (open main window, exit app)
image

Summary by CodeRabbit

  • New Features
    • Floating Chat Window: A compact, always-on-top chat panel you can show, hide, or toggle. It appears near the floating button, with platform-tailored visuals (transparent/frameless).
    • Right-click Menu on Floating Button: Access “Open main window” and “Exit application” from a new context menu.
  • Improvements
    • Clicking the floating button opens the chat near the button for quicker access.
    • Messages and actions now reach the floating chat window alongside the main app.
    • On macOS, file previews can originate from the floating chat when active.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 11, 2025

Walkthrough

Adds a floating chat window feature with always-on-top BrowserWindow, positioning relative to the floating button, and lifecycle management. Introduces right-click handling on the floating button to show a context menu. Extends presenters and shared interfaces for floating window registration, visibility control, IPC broadcasting, and app-quit awareness.

Changes

Cohort / File(s) Summary
Event constants
src/main/events.ts
Added FLOATING_BUTTON_EVENTS.RIGHT_CLICKED = 'floating-button:right-clicked'.
Floating button presenter
src/main/presenter/floatingButtonPresenter/index.ts
Added right-click context menu flow; async click handling with button bounds; pre-creation of floating chat window; cleanup of CLICKED/RIGHT_CLICKED listeners; helpers for main window open/exit.
Floating button window accessor
src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
Added public getWindow(): BrowserWindow
Floating chat window (new)
src/main/presenter/windowPresenter/FloatingChatWindow.ts
New class managing a frameless, transparent, always-on-top chat window; creation, show/hide/toggle/destroy, positioning near button, environment-aware content loading, tab registration, events, and visibility state.
Window presenter integration
src/main/presenter/windowPresenter/index.ts
Integrated FloatingChatWindow; added create/show/hide/toggle/destroy/visible/get APIs; included in sendToAllWindows; considered in previewFile; exposes isApplicationQuitting.
Tab presenter support
src/main/presenter/tabPresenter.ts
Added registerFloatingWindow/unregisterFloatingWindow to manage virtual tab entries for floating windows.
Preload and renderer wiring
src/preload/floating-preload.ts, src/renderer/floating/FloatingButton.vue, src/renderer/floating/env.d.ts
Added onRightClick() to floatingButtonAPI; bound @contextmenu to send RIGHT_CLICKED with animation; updated Window.floatingButtonAPI type to include onRightClick.
Shared interfaces
src/shared/presenter.d.ts
Extended IWindowPresenter with isApplicationQuitting(); extended ITabPresenter with registerFloatingWindow/unregisterFloatingWindow.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Renderer as FloatingButton.vue
  participant Preload as floating-preload
  participant Main as FloatingButtonPresenter
  participant Menu as Electron.Menu
  participant WP as WindowPresenter

  User->>Renderer: Right-click button
  Renderer->>Preload: floatingButtonAPI.onRightClick()
  Preload->>Main: IPC RIGHT_CLICKED
  Main->>Menu: build context menu
  alt Button window exists
    Main->>Menu: popup(buttonWindow)
  else Main window available
    Main->>Menu: popup(mainWindow)
  else
    Main->>Menu: popup()
  end
  Menu->>WP: Open main / Exit app (menu selection)
Loading
sequenceDiagram
  participant User
  participant Renderer as FloatingButton.vue
  participant Preload as floating-preload
  participant Main as FloatingButtonPresenter
  participant WP as WindowPresenter
  participant FCW as FloatingChatWindow

  User->>Renderer: Left-click button
  Renderer->>Preload: floatingButtonAPI.onClick()
  Preload->>Main: IPC CLICKED
  Main->>Main: get button bounds
  Main->>WP: toggleFloatingChatWindow(bounds?)
  WP->>FCW: create if needed
  WP->>FCW: toggle(show/hide) with positioning
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
Always on top for floating chat window [#719]
Should not lose focus when clicking elsewhere [#719] Focus retention behavior is not explicitly enforced; only visibility/show logic is defined.
Should have top priority on the screen [#719]

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Add right-click context menu to floating button with “Open main window” and “Exit application” (src/main/presenter/floatingButtonPresenter/index.ts) Not related to ensuring the window stays on top or focus behavior.
Add openMainWindow and exitApplication flows (src/main/presenter/floatingButtonPresenter/index.ts) Unrelated to always-on-top/focus persistence.
Add TabPresenter virtual tab registration for floating windows (src/main/presenter/tabPresenter.ts) Orthogonal to the always-on-top/focus requirements.
Extend sendToAllWindows to include floating chat window (src/main/presenter/windowPresenter/index.ts) IPC broadcast changes are not required by #719.
Modify previewFile to consider floating chat window (src/main/presenter/windowPresenter/index.ts) Quick Look targeting is outside the scope of #719.

Possibly related PRs

  • feat: Floating window #553: Extends floating-window functionality with similar presenters and RIGHT_CLICKED/context-menu integration.

Suggested reviewers

  • zerob13

Poem

I thump my paws on windows bright,
A chat cloud floats, day and night.
Right-click sprinkles menu stew,
Left-click pops a view anew.
Always-on-top, I guard the scene—
A rabbit dev with code pristine. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🔭 Outside diff range comments (1)
src/renderer/floating/FloatingButton.vue (1)

30-72: Extract common animation logic to reduce duplication

Both handleClick and handleRightClick contain identical animation code. Consider extracting this into a reusable function:

+// Animation helper
+const triggerButtonAnimation = () => {
+  if (floatingButton.value) {
+    floatingButton.value.style.transform = 'scale(0.9)'
+    setTimeout(() => {
+      if (floatingButton.value) {
+        floatingButton.value.style.transform = ''
+      }
+    }, 150)
+  }
+}

 // 点击处理 - 专注于唤起主窗口
 const handleClick = () => {
-  // 点击反馈动画
-  if (floatingButton.value) {
-    floatingButton.value.style.transform = 'scale(0.9)'
-    setTimeout(() => {
-      if (floatingButton.value) {
-        floatingButton.value.style.transform = ''
-      }
-    }, 150)
-  }
+  triggerButtonAnimation()

   if (window.floatingButtonAPI) {
     try {
       window.floatingButtonAPI.onClick()
     } catch (error) {
       console.error('=== FloatingButton: Error calling onClick API ===:', error)
     }
   } else {
     console.error('=== FloatingButton: floatingButtonAPI not available ===')
   }
 }

 const handleRightClick = (event: MouseEvent) => {
   event.preventDefault()
-  if (floatingButton.value) {
-    floatingButton.value.style.transform = 'scale(0.9)'
-    setTimeout(() => {
-      if (floatingButton.value) {
-        floatingButton.value.style.transform = ''
-      }
-    }, 150)
-  }
+  triggerButtonAnimation()

   if (window.floatingButtonAPI) {
     try {
       window.floatingButtonAPI.onRightClick()
     } catch (error) {
       console.error('=== FloatingButton: Error calling onRightClick API ===:', error)
     }
   } else {
     console.error('=== FloatingButton: floatingButtonAPI not available ===')
   }
 }
🧹 Nitpick comments (5)
src/main/presenter/windowPresenter/FloatingChatWindow.ts (3)

116-148: Simplify show method logic

The current implementation has redundant show() and focus() calls across different branches. Consider consolidating the logic:

 public show(floatingButtonPosition?: FloatingButtonPosition): void {
   if (!this.window) {
     return
   }

   if (floatingButtonPosition) {
     const position = this.calculatePosition(floatingButtonPosition)
     this.window.setPosition(position.x, position.y)
   }
-  if (!this.window.isVisible()) {
-    if (this.window.webContents.isLoading() === false) {
-      this.window.show()
-      this.window.focus()
-      this.refreshWindowData()
-    } else {
-      this.window.show()
-      this.window.focus()
-      this.shouldShowWhenReady = true
-      this.window.webContents.once('did-finish-load', () => {
-        if (this.shouldShowWhenReady) {
-          this.refreshWindowData()
-          this.shouldShowWhenReady = false
-        }
-      })
-    }
-  } else {
-    this.window.show()
-    this.window.focus()
-    this.refreshWindowData()
-  }
+  this.window.show()
+  this.window.focus()
+  
+  if (this.window.webContents.isLoading()) {
+    this.shouldShowWhenReady = true
+    this.window.webContents.once('did-finish-load', () => {
+      if (this.shouldShowWhenReady) {
+        this.refreshWindowData()
+        this.shouldShowWhenReady = false
+      }
+    })
+  } else {
+    this.refreshWindowData()
+  }
   this.isVisible = true
   logger.debug('FloatingChatWindow shown')
 }

258-258: Consider making the gap configurable

The gap value is hardcoded to 15 pixels. Consider making this configurable through the FloatingChatConfig interface for better flexibility.

 const windowHeight = this.config.size.height
-const gap = 15
+const gap = this.config.gap ?? 15

Also add the gap property to the FloatingChatConfig interface:

interface FloatingChatConfig {
  // ... existing properties
  gap?: number
}

340-344: Consider making the dev server URL configurable

The development server URL is hardcoded to http://localhost:5173/. Consider making this configurable through environment variables to support different development setups.

 if (isDev) {
-  await this.window.loadURL('http://localhost:5173/')
+  const devServerUrl = process.env.ELECTRON_RENDERER_URL || 'http://localhost:5173/'
+  await this.window.loadURL(devServerUrl)
 } else {
src/main/presenter/floatingButtonPresenter/index.ts (1)

155-167: Pre-create floating chat window: consider deferring to idle to reduce startup contention

Pre-warming improves first-toggle latency, but can compete with startup work. Optionally schedule after a short idle timeout or on first hover/focus of the floating button.

Example:

- this.preCreateFloatingChatWindow()
+ setTimeout(() => this.preCreateFloatingChatWindow(), 800)
src/main/presenter/windowPresenter/index.ts (1)

177-198: Preview target selection enhanced (includes floating window)

Good fallback order; macOS-only previewFile usage is correct. Consider logging/handling the string return value from shell.openPath for error messages on non-macOS if needed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7a5953 and 7d1aa98.

📒 Files selected for processing (10)
  • src/main/events.ts (1 hunks)
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts (1 hunks)
  • src/main/presenter/floatingButtonPresenter/index.ts (4 hunks)
  • src/main/presenter/tabPresenter.ts (1 hunks)
  • src/main/presenter/windowPresenter/FloatingChatWindow.ts (1 hunks)
  • src/main/presenter/windowPresenter/index.ts (6 hunks)
  • src/preload/floating-preload.ts (2 hunks)
  • src/renderer/floating/FloatingButton.vue (2 hunks)
  • src/renderer/floating/env.d.ts (1 hunks)
  • src/shared/presenter.d.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Use English for logs and comments

Files:

  • src/renderer/floating/FloatingButton.vue
  • src/main/events.ts
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
  • src/shared/presenter.d.ts
  • src/renderer/floating/env.d.ts
  • src/main/presenter/tabPresenter.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
  • src/main/presenter/windowPresenter/FloatingChatWindow.ts
  • src/preload/floating-preload.ts
src/renderer/**/*.{vue,ts,js,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

渲染进程代码放在 src/renderer

Files:

  • src/renderer/floating/FloatingButton.vue
  • src/renderer/floating/env.d.ts
src/renderer/**/*.{ts,tsx,vue}

📄 CodeRabbit Inference Engine (.cursor/rules/vue-shadcn.mdc)

src/renderer/**/*.{ts,tsx,vue}: Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).
Use TypeScript for all code; prefer types over interfaces.
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 statements.

Files:

  • src/renderer/floating/FloatingButton.vue
  • src/renderer/floating/env.d.ts
src/renderer/**/*.{vue,ts}

📄 CodeRabbit Inference Engine (.cursor/rules/vue-shadcn.mdc)

Implement lazy loading for routes and components.

Files:

  • src/renderer/floating/FloatingButton.vue
  • src/renderer/floating/env.d.ts
src/renderer/**/*.{ts,vue}

📄 CodeRabbit Inference Engine (.cursor/rules/vue-shadcn.mdc)

src/renderer/**/*.{ts,vue}: Use useFetch and useAsyncData for data fetching.
Implement SEO best practices using Nuxt's useHead and useSeoMeta.

Files:

  • src/renderer/floating/FloatingButton.vue
  • src/renderer/floating/env.d.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

Strict type checking enabled for TypeScript

**/*.{ts,tsx}: 始终使用 try-catch 处理可能的错误
提供有意义的错误信息
记录详细的错误日志
优雅降级处理
日志应包含时间戳、日志级别、错误代码、错误描述、堆栈跟踪(如适用)、相关上下文信息
日志级别应包括 ERROR、WARN、INFO、DEBUG
不要吞掉错误
提供用户友好的错误信息
实现错误重试机制
避免记录敏感信息
使用结构化日志
设置适当的日志级别

Files:

  • src/main/events.ts
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
  • src/shared/presenter.d.ts
  • src/renderer/floating/env.d.ts
  • src/main/presenter/tabPresenter.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
  • src/main/presenter/windowPresenter/FloatingChatWindow.ts
  • src/preload/floating-preload.ts
src/main/**/*.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

Main to Renderer: Use EventBus to broadcast events via mainWindow.webContents.send()

Use Electron's built-in APIs for file system and native dialogs

Files:

  • src/main/events.ts
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
  • src/main/presenter/tabPresenter.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
  • src/main/presenter/windowPresenter/FloatingChatWindow.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development-setup.mdc)

**/*.{js,jsx,ts,tsx}: 使用 OxLint 进行代码检查
Log和注释使用英文书写

Files:

  • src/main/events.ts
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
  • src/shared/presenter.d.ts
  • src/renderer/floating/env.d.ts
  • src/main/presenter/tabPresenter.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
  • src/main/presenter/windowPresenter/FloatingChatWindow.ts
  • src/preload/floating-preload.ts
src/{main,renderer}/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/electron-best-practices.mdc)

src/{main,renderer}/**/*.ts: Use context isolation for improved security
Implement proper inter-process communication (IPC) patterns
Optimize application startup time with lazy loading
Implement proper error handling and logging for debugging

Files:

  • src/main/events.ts
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
  • src/renderer/floating/env.d.ts
  • src/main/presenter/tabPresenter.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
  • src/main/presenter/windowPresenter/FloatingChatWindow.ts
src/main/**/*.{ts,js,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

主进程代码放在 src/main

Files:

  • src/main/events.ts
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
  • src/main/presenter/tabPresenter.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
  • src/main/presenter/windowPresenter/FloatingChatWindow.ts
src/main/presenter/**/*.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

One presenter per functional domain

Files:

  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
  • src/main/presenter/tabPresenter.ts
  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
  • src/main/presenter/windowPresenter/FloatingChatWindow.ts
src/shared/**/*.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

Shared types in src/shared/

Files:

  • src/shared/presenter.d.ts
src/shared/*.d.ts

📄 CodeRabbit Inference Engine (.cursor/rules/electron-best-practices.mdc)

The shared/*.d.ts files are used to define the types of objects exposed by the main process to the renderer process

Files:

  • src/shared/presenter.d.ts
src/shared/**/*.{ts,tsx,d.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

共享类型定义放在 shared 目录

Files:

  • src/shared/presenter.d.ts
src/preload/**/*.ts

📄 CodeRabbit Inference Engine (CLAUDE.md)

Context isolation enabled with preload scripts for security

Files:

  • src/preload/floating-preload.ts
🧠 Learnings (3)
📚 Learning: 2025-07-21T01:45:54.229Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/electron-best-practices.mdc:0-0
Timestamp: 2025-07-21T01:45:54.229Z
Learning: Applies to src/renderer/src/composables/usePresenter.ts : The IPC in the renderer process is implemented in usePresenter.ts, allowing direct calls to the presenter-related interfaces exposed by the main process

Applied to files:

  • src/shared/presenter.d.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
📚 Learning: 2025-07-21T01:45:54.229Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/electron-best-practices.mdc:0-0
Timestamp: 2025-07-21T01:45:54.229Z
Learning: Applies to src/main/presenter/index.ts : The IPC messages from the main process to notify the view mainly rely on the EventBus index.ts to listen for events that need to be notified and then send them to the renderer through the mainWindow

Applied to files:

  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
📚 Learning: 2025-07-21T01:45:33.790Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-21T01:45:33.790Z
Learning: Applies to src/main/presenter/configPresenter/**/*.ts : Centralize configuration in configPresenter/

Applied to files:

  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
🧬 Code Graph Analysis (3)
src/main/presenter/windowPresenter/index.ts (1)
src/main/presenter/windowPresenter/FloatingChatWindow.ts (1)
  • FloatingChatWindow (44-398)
src/main/presenter/windowPresenter/FloatingChatWindow.ts (3)
src/main/eventbus.ts (1)
  • eventBus (151-151)
src/main/events.ts (1)
  • TAB_EVENTS (156-164)
src/main/presenter/index.ts (1)
  • presenter (184-184)
src/preload/floating-preload.ts (1)
src/main/events.ts (1)
  • FLOATING_BUTTON_EVENTS (178-184)
⏰ 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 (16)
src/main/events.ts (1)

180-180: LGTM!

The new RIGHT_CLICKED event follows the established naming convention and integrates well with the existing event structure.

src/renderer/floating/env.d.ts (1)

14-14: LGTM!

The onRightClick callback follows the same pattern as the existing onClick handler and properly extends the floating button API.

src/shared/presenter.d.ts (2)

181-181: LGTM!

The isApplicationQuitting() method provides essential state information for proper window lifecycle management during app shutdown.


218-219: LGTM!

The floating window registration methods properly integrate the floating window with the tab management system using appropriate Electron types.

src/main/presenter/windowPresenter/FloatingChatWindow.ts (2)

1-10: LGTM!

Imports are well-organized and all necessary dependencies are included for the floating window implementation.


357-397: LGTM!

The window event handling is well-implemented, properly managing the window lifecycle and preventing accidental closure when the app is not quitting. This aligns perfectly with the PR objective of maintaining an always-on-top floating window.

src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts (1)

173-175: Accessor looks good; minimal, non-breaking addition

No concerns with exposing a nullable getter for the floating button BrowserWindow.

src/preload/floating-preload.ts (2)

20-26: Right-click IPC path is correct and safe

The try/catch and contextIsolation exposure are appropriate. No issues.


5-7: Event name consistency confirmed

The RIGHT_CLICKED event name 'floating-button:right-clicked' is defined identically in both src/preload/floating-preload.ts and src/main/events.ts, and is used consistently throughout the IPC handlers in the presenters. No mismatches found—no changes required.

src/main/presenter/floatingButtonPresenter/index.ts (2)

51-52: Cleanup completeness

Removing both CLICKED and RIGHT_CLICKED handlers on destroy prevents duplicate bindings. Good.


111-112: Idempotent rebind of IPC handlers

Removing and re-registering both handlers before creating the window is a safe pattern to prevent multiple listeners. Good.

src/main/presenter/windowPresenter/index.ts (5)

16-16: Importing FloatingChatWindow is correct and scoped

No issues.


42-42: FloatingChatWindow state holder added

Addition is straightforward and contained.


59-63: Clean shutdown of floating chat on before-quit

Explicitly destroying the floating chat window on app quit is good to prevent leaks.


504-513: Broadcast includes floating chat window

Including the floating chat window webContents in broadcasts is correct and guarded against destruction.


1178-1180: Expose application quitting flag

Useful for cooperating presenters (e.g., FloatingChatWindow close handler). Looks good.

Comment on lines +113 to +137
ipcMain.on(FLOATING_BUTTON_EVENTS.CLICKED, async () => {
try {
// 触发内置事件处理器
handleShowHiddenWindow(true)
} catch {}
let floatingButtonPosition: { x: number; y: number; width: number; height: number } | null =
null
if (this.floatingWindow && this.floatingWindow.exists()) {
const buttonWindow = this.floatingWindow.getWindow()
if (buttonWindow && !buttonWindow.isDestroyed()) {
const bounds = buttonWindow.getBounds()
floatingButtonPosition = {
x: bounds.x,
y: bounds.y,
width: bounds.width,
height: bounds.height
}
}
}
if (floatingButtonPosition) {
await presenter.windowPresenter.toggleFloatingChatWindow(floatingButtonPosition)
} else {
await presenter.windowPresenter.toggleFloatingChatWindow()
}
} catch (error) {
console.error('Failed to handle floating button click:', error)
}
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Pass-through of button bounds is good; consider multi-display correctness

You pass the floating button’s bounds to help position the chat window near the button. However, FloatingChatWindow.calculatePosition() uses screen.getPrimaryDisplay(), which can misplace the window when the button is on a non-primary monitor. Prefer deriving the display nearest the button center and using that display’s workArea.

Additional change needed in FloatingChatWindow (for reference):

- const primaryDisplay = screen.getPrimaryDisplay()
- const { workArea } = primaryDisplay
+ const buttonCenter = floatingButtonPosition
+  ? { x: floatingButtonPosition.x + floatingButtonPosition.width / 2,
+      y: floatingButtonPosition.y + floatingButtonPosition.height / 2 }
+  : undefined
+ const display = buttonCenter
+  ? screen.getDisplayNearestPoint(buttonCenter)
+  : screen.getPrimaryDisplay()
+ const { workArea } = display

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/index.ts around lines 113 to 137,
the presenter passes button bounds but the downstream
FloatingChatWindow.calculatePosition() still uses screen.getPrimaryDisplay(),
which will misplace the chat on multi-monitor setups; update the code so the
display nearest the button center is used (use screen.getDisplayNearestPoint or
equivalent with the button center x/y) and pass that display’s workArea (or pass
the display object) into calculatePosition(), and modify
FloatingChatWindow.calculatePosition() to prefer the provided display/workArea
over screen.getPrimaryDisplay().

Comment on lines +169 to +204
private showContextMenu(): void {
const template = [
{
label: '打开主窗口',
click: () => {
this.openMainWindow()
}
},
{
type: 'separator' as const
},
{
label: '退出应用',
click: () => {
this.exitApplication()
}
}
]

const contextMenu = Menu.buildFromTemplate(template)

if (this.floatingWindow && this.floatingWindow.exists()) {
const buttonWindow = this.floatingWindow.getWindow()
if (buttonWindow && !buttonWindow.isDestroyed()) {
contextMenu.popup({ window: buttonWindow })
return
}
}

const mainWindow = presenter.windowPresenter.mainWindow
if (mainWindow) {
contextMenu.popup({ window: mainWindow })
} else {
contextMenu.popup()
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Localize context menu labels and reuse existing i18n infra

Hardcoded Chinese labels break localization. Reuse your i18n helpers (like getContextMenuLabels) or add keys for “Open main window” and “Exit application” to keep UX consistent.

Example:

- const template = [
-   { label: '打开主窗口', click: () => this.openMainWindow() },
-   { type: 'separator' as const },
-   { label: '退出应用', click: () => this.exitApplication() }
- ]
+ const lang = presenter?.configPresenter?.getLanguage?.() ?? 'en'
+ const labels = { openMain: /* t('menu.openMain') by lang */, exitApp: /* t('menu.exitApp') */ }
+ const template = [
+   { label: labels.openMain, click: () => this.openMainWindow() },
+   { type: 'separator' as const },
+   { label: labels.exitApp, click: () => this.exitApplication() }
+ ]

I can wire this to your existing i18n utilities on request.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +228 to 235
private exitApplication(): void {
try {
console.log('Exiting application from floating button context menu')
app.quit()
} catch (error) {
console.error('Failed to exit application from floating button:', error)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Route app exit through WindowPresenter to set isQuitting before window closes

Direct app.quit() can race with close handlers that rely on isQuitting. Emit the FORCE_QUIT_APP event instead, which your WindowPresenter already handles to set isQuitting then quit.

-  private exitApplication(): void {
-    try {
-      console.log('Exiting application from floating button context menu')
-      app.quit()
-    } catch (error) {
-      console.error('Failed to exit application from floating button:', error)
-    }
-  }
+  private exitApplication(): void {
+    try {
+      console.log('Exiting application from floating button context menu')
+      // Ensure centralized quit path sets isQuitting before windows receive 'close'
+      const { eventBus } = require('@/eventbus')
+      const { WINDOW_EVENTS } = require('@/events')
+      eventBus.sendToMain(WINDOW_EVENTS.FORCE_QUIT_APP)
+    } catch (error) {
+      console.error('Failed to exit application from floating button:', error)
+    }
+  }

Note: add missing imports if your bundler supports them at top-level instead of require().

📝 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.

Suggested change
private exitApplication(): void {
try {
console.log('Exiting application from floating button context menu')
app.quit()
} catch (error) {
console.error('Failed to exit application from floating button:', error)
}
}
private exitApplication(): void {
try {
console.log('Exiting application from floating button context menu')
// Ensure centralized quit path sets isQuitting before windows receive 'close'
const { eventBus } = require('@/eventbus')
const { WINDOW_EVENTS } = require('@/events')
eventBus.sendToMain(WINDOW_EVENTS.FORCE_QUIT_APP)
} catch (error) {
console.error('Failed to exit application from floating button:', error)
}
}
🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/index.ts around lines 228 to 235,
replace the direct app.quit() call with emitting the existing FORCE_QUIT_APP
event that WindowPresenter listens for so isQuitting is set before windows
close; keep the try/catch and logs but remove app.quit(), import the
FORCE_QUIT_APP constant (and the app/event emitter or WindowPresenter helper
used to emit it) at top-level, and call the emitter/WindowPresenter to emit
FORCE_QUIT_APP instead of invoking app.quit() directly.

Comment on lines +946 to +980
registerFloatingWindow(webContentsId: number, webContents: Electron.WebContents): void {
try {
console.log(`TabPresenter: Registering floating window as virtual tab, ID: ${webContentsId}`)
if (this.tabs.has(webContentsId)) {
console.warn(`TabPresenter: Tab ${webContentsId} already exists, skipping registration`)
return
}
const virtualView = {
webContents: webContents,
setVisible: () => {},
setBounds: () => {},
getBounds: () => ({ x: 0, y: 0, width: 400, height: 600 })
} as any
this.webContentsToTabId.set(webContentsId, webContentsId)
this.tabs.set(webContentsId, virtualView)
console.log(
`TabPresenter: Virtual tab registered successfully for floating window ${webContentsId}`
)
} catch (error) {
console.error('TabPresenter: Failed to register floating window:', error)
}
}

unregisterFloatingWindow(webContentsId: number): void {
try {
console.log(`TabPresenter: Unregistering floating window virtual tab, ID: ${webContentsId}`)
this.webContentsToTabId.delete(webContentsId)
this.tabs.delete(webContentsId)
console.log(
`TabPresenter: Virtual tab unregistered successfully for floating window ${webContentsId}`
)
} catch (error) {
console.error('TabPresenter: Failed to unregister floating window:', error)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid storing non-WebContentsView “virtual tabs” in Map<number, WebContentsView>

Using a plain object cast as any risks runtime/typing issues if later code assumes a real WebContentsView (e.g., addChildView, setBorderRadius). Recommend isolating virtual entries in a separate map or introducing a safe abstraction instead of widening via any.

Proposed direction:

  • Keep tabs: Map<number, WebContentsView> strictly for real views.
  • Add virtualTabs: Map<number, { webContents: Electron.WebContents }> for floating windows.
  • Update getTabIdByWebContentsId/getWindowIdByWebContentsId to consult both maps, or keep only webContentsToTabId as source of truth.
    This avoids accidental operations (attach/detach/bring-to-front/bounds updates) on non-view objects.
🤖 Prompt for AI Agents
In src/main/presenter/tabPresenter.ts around lines 946 to 980, the current
registerFloatingWindow/unregisterFloatingWindow functions insert a plain object
cast to any into tabs: Map<number, WebContentsView>, which can break code that
assumes real WebContentsView methods; instead create a new private virtualTabs:
Map<number, { webContents: Electron.WebContents }>, stop inserting virtual
entries into tabs, set webContentsToTabId as before to preserve lookup, and move
the virtual object into virtualTabs in registerFloatingWindow and remove it from
virtualTabs in unregisterFloatingWindow; also update any tab-lookup helpers
(getTabIdByWebContentsId/getWindowIdByWebContentsId) to consult
webContentsToTabId and then check tabs first and virtualTabs second (or rely
solely on webContentsToTabId) and ensure callers that operate on WebContentsView
guard against virtual entries so real view-only operations aren’t invoked on
virtualTabs.

Comment on lines +89 to +96
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, '../preload/index.mjs'),
webSecurity: false,
devTools: isDev,
sandbox: false
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security concern: Consider enabling web security and sandbox

The current configuration disables important security features:

  • webSecurity: false allows cross-origin requests without restrictions
  • sandbox: false disables process isolation

These settings expose the application to potential security vulnerabilities. Consider enabling these security features unless they are absolutely necessary for the floating window functionality.

If these settings are required, document the specific reasons and ensure proper input validation and content sanitization are implemented.

🤖 Prompt for AI Agents
In src/main/presenter/windowPresenter/FloatingChatWindow.ts around lines 89 to
96, the BrowserWindow options disable key security features (webSecurity: false,
sandbox: false); change them to webSecurity: true and sandbox: true unless there
is a documented, justified need to disable them; if disabling is absolutely
required, add an inline comment referencing the specific justification,
implement strict input validation and content sanitization in the preload and
renderer, enforce a Content Security Policy and origin checks before loading
remote content, restrict exposed APIs in the preload script, and include a short
note in the PR describing why these settings are necessary and how risks are
mitigated.

Comment on lines +1106 to +1121
public async createFloatingChatWindow(): Promise<void> {
if (this.floatingChatWindow) {
console.log('FloatingChatWindow already exists')
return
}

try {
this.floatingChatWindow = new FloatingChatWindow()
await this.floatingChatWindow.create()
console.log('FloatingChatWindow created successfully')
} catch (error) {
console.error('Failed to create FloatingChatWindow:', error)
this.floatingChatWindow = null
throw error
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard against concurrent createFloatingChatWindow calls

pre-create plus first-click can race. Add a creation promise sentinel to ensure only one create runs and subsequent callers await it.

+ private floatingChatInitPromise: Promise<void> | null = null
  public async createFloatingChatWindow(): Promise<void> {
-   if (this.floatingChatWindow) {
+   if (this.floatingChatWindow) {
      console.log('FloatingChatWindow already exists')
      return
    }
-   try {
-     this.floatingChatWindow = new FloatingChatWindow()
-     await this.floatingChatWindow.create()
-     console.log('FloatingChatWindow created successfully')
-   } catch (error) {
-     console.error('Failed to create FloatingChatWindow:', error)
-     this.floatingChatWindow = null
-     throw error
-   }
+   if (!this.floatingChatInitPromise) {
+     this.floatingChatInitPromise = (async () => {
+       try {
+         this.floatingChatWindow = new FloatingChatWindow()
+         await this.floatingChatWindow.create()
+         console.log('FloatingChatWindow created successfully')
+       } catch (error) {
+         console.error('Failed to create FloatingChatWindow:', error)
+         this.floatingChatWindow = null
+         throw error
+       } finally {
+         this.floatingChatInitPromise = null
+       }
+     })()
+   }
+   await this.floatingChatInitPromise
  }
📝 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.

Suggested change
public async createFloatingChatWindow(): Promise<void> {
if (this.floatingChatWindow) {
console.log('FloatingChatWindow already exists')
return
}
try {
this.floatingChatWindow = new FloatingChatWindow()
await this.floatingChatWindow.create()
console.log('FloatingChatWindow created successfully')
} catch (error) {
console.error('Failed to create FloatingChatWindow:', error)
this.floatingChatWindow = null
throw error
}
}
// sentinel to prevent concurrent creations
private floatingChatInitPromise: Promise<void> | null = null
public async createFloatingChatWindow(): Promise<void> {
if (this.floatingChatWindow) {
console.log('FloatingChatWindow already exists')
return
}
if (!this.floatingChatInitPromise) {
this.floatingChatInitPromise = (async () => {
try {
this.floatingChatWindow = new FloatingChatWindow()
await this.floatingChatWindow.create()
console.log('FloatingChatWindow created successfully')
} catch (error) {
console.error('Failed to create FloatingChatWindow:', error)
this.floatingChatWindow = null
throw error
} finally {
this.floatingChatInitPromise = null
}
})()
}
await this.floatingChatInitPromise
}
🤖 Prompt for AI Agents
In src/main/presenter/windowPresenter/index.ts around lines 1106 to 1121,
concurrent calls to createFloatingChatWindow can race (pre-create + first-click)
so add a creation promise sentinel (e.g., this.creatingFloatingChatWindow:
Promise<void> | null) that is set before starting creation and returned/awaited
by subsequent callers; implement: if sentinel exists return await sentinel,
otherwise set sentinel = (async () => { try { this.floatingChatWindow = new
FloatingChatWindow(); await this.floatingChatWindow.create(); } catch (err) {
this.floatingChatWindow = null; throw err } finally {
this.creatingFloatingChatWindow = null } })(); await sentinel; this ensures only
one create runs, handles success/error cleanup, and clears the sentinel when
done.

@zerob13 zerob13 merged commit 08658c3 into ThinkInAIXYZ:dev Aug 11, 2025
2 checks passed
@yyhhyyyyyy yyhhyyyyyy mentioned this pull request Aug 11, 2025
zerob13 added a commit that referenced this pull request Aug 11, 2025
* wip: add modelscope provider

* feat: add mcp sync to modelscope

* fix: add scrollable support to PopoverContent to prevent overflow (#720)

* feat: implement floating chat window system with performance optimization (#724)

* chore: i18n and format

* feat: better style

* fix: mcp tool display

---------

Co-authored-by: yyhhyyyyyy <yyhhyyyyyy8@gmail.com>
zerob13 added a commit that referenced this pull request Aug 13, 2025
* fix: add AlertDialogDescription to resolve accessibility warning (#706)

* fix: resolve focus flicker when creating new windows with Ctrl+Shift+N (#707)

* feat: enhance window management by implementing main window ID handling (#709)

* docs: update zhipu developer doc website link (#715)

Co-authored-by: gongchao <chao.gong@aminer.cn>

* refactor: better translate (#716)

* chore: en-us i18n

* chore(i18n): polish ja-JP translations across UI; keep chat.input.placeholder unchanged

* chore(i18n): polish fr-FR translations; keep chat.input.placeholder unchanged

* chore(i18n): refine fr-FR MCP & Settings copy; idiomatic, concise, brand-consistent

* chore(i18n): polish ru-RU translations across UI; keep chat.input.placeholder unchanged

* chore(i18n): polish fa-IR translations across UI; keep chat.input.placeholder unchanged

* chore: fix format

* chore: fix i18n

* chore: lock rolldown-vite version

* feat: add GPT-5 series model support (#717)

* ci(vite): Bundle the main file into a single file to speed up loading. (#718)

* fix(math): parser by upgrade vue-renderer-markdown (#722)

* chore: bump deps (#721)

* chore: bump deps

* fix: rolldown-vite 7.1.0 and duckdb bundle issue

* chore: back to vite

* chore: update electron

* chore: update versions

* fix(math): parser by upgrade vue-renderer-markdown (#722)

* chore: bump deps

---------

Co-authored-by: Simon He <57086651+Simon-He95@users.noreply.github.com>

* fix: add scrollable support to PopoverContent to prevent overflow (#720)

* feat: implement floating chat window system with performance optimization (#724)

* feat: add mcp sync and modelscope provider #615 (#723)

* wip: add modelscope provider

* feat: add mcp sync to modelscope

* fix: add scrollable support to PopoverContent to prevent overflow (#720)

* feat: implement floating chat window system with performance optimization (#724)

* chore: i18n and format

* feat: better style

* fix: mcp tool display

---------

Co-authored-by: yyhhyyyyyy <yyhhyyyyyy8@gmail.com>

* fix: move_files newPath parse issue (#725)

* fix: move_files newPath 参数计算规则

* fix: move_files 移动前需要判断dest是目录还是文件

* feat: add Claude Opus 4.1 to anthropic default model list (#726)

* feat: Add mcprouter's MCP marketplace api support (#727)

* wip: add mcp market

* feat: mcp market install

* wip: mcp install status sync

* feat: mcp server config mask

* chore: remove working doc

* chore: add translate

* feat: add ESC key to close floating chat window (#728)

* feat: add floating button position persistence with boundary validation (#729)

* feat: add floating button position persistence with boundary validation

* feat: refactor floating button to use electron-window-state

* chore: bump to 0.3.0

* feat: add reasoning_effort parameter support for gpt-oss models (#731)

* feat: add reasoning_effort parameter support for gpt-oss models

- add reasoning effort UI support across all components

* fix: preserve user reasoning effort settings and improve display logic

* fix: artifacts code not streaming (#732)

* fix: artifact react load failed

* chore: remove log

* fix: artifacts code not stream

* fix: format

---------

Co-authored-by: yyhhyyyyyy <yyhhyyyyyy8@gmail.com>
Co-authored-by: hllshiro <40970081+hllshiro@users.noreply.github.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: gongchao <chao.gong@aminer.cn>
Co-authored-by: Simon He <57086651+Simon-He95@users.noreply.github.com>
Co-authored-by: wanna <wanna.w@binarywalk.com>
zerob13 added a commit that referenced this pull request Aug 13, 2025
* fix: add AlertDialogDescription to resolve accessibility warning (#706)

* fix: resolve focus flicker when creating new windows with Ctrl+Shift+N (#707)

* feat: enhance window management by implementing main window ID handling (#709)

* docs: update zhipu developer doc website link (#715)

Co-authored-by: gongchao <chao.gong@aminer.cn>

* refactor: better translate (#716)

* chore: en-us i18n

* chore(i18n): polish ja-JP translations across UI; keep chat.input.placeholder unchanged

* chore(i18n): polish fr-FR translations; keep chat.input.placeholder unchanged

* chore(i18n): refine fr-FR MCP & Settings copy; idiomatic, concise, brand-consistent

* chore(i18n): polish ru-RU translations across UI; keep chat.input.placeholder unchanged

* chore(i18n): polish fa-IR translations across UI; keep chat.input.placeholder unchanged

* chore: fix format

* chore: fix i18n

* chore: lock rolldown-vite version

* feat: add GPT-5 series model support (#717)

* ci(vite): Bundle the main file into a single file to speed up loading. (#718)

* fix(math): parser by upgrade vue-renderer-markdown (#722)

* chore: bump deps (#721)

* chore: bump deps

* fix: rolldown-vite 7.1.0 and duckdb bundle issue

* chore: back to vite

* chore: update electron

* chore: update versions

* fix(math): parser by upgrade vue-renderer-markdown (#722)

* chore: bump deps

---------

Co-authored-by: Simon He <57086651+Simon-He95@users.noreply.github.com>

* fix: add scrollable support to PopoverContent to prevent overflow (#720)

* feat: implement floating chat window system with performance optimization (#724)

* feat: add mcp sync and modelscope provider #615 (#723)

* wip: add modelscope provider

* feat: add mcp sync to modelscope

* fix: add scrollable support to PopoverContent to prevent overflow (#720)

* feat: implement floating chat window system with performance optimization (#724)

* chore: i18n and format

* feat: better style

* fix: mcp tool display

---------

Co-authored-by: yyhhyyyyyy <yyhhyyyyyy8@gmail.com>

* fix: move_files newPath parse issue (#725)

* fix: move_files newPath 参数计算规则

* fix: move_files 移动前需要判断dest是目录还是文件

* feat: add Claude Opus 4.1 to anthropic default model list (#726)

* feat: Add mcprouter's MCP marketplace api support (#727)

* wip: add mcp market

* feat: mcp market install

* wip: mcp install status sync

* feat: mcp server config mask

* chore: remove working doc

* chore: add translate

* feat: add ESC key to close floating chat window (#728)

* feat: add floating button position persistence with boundary validation (#729)

* feat: add floating button position persistence with boundary validation

* feat: refactor floating button to use electron-window-state

* chore: bump to 0.3.0

* feat: add reasoning_effort parameter support for gpt-oss models (#731)

* feat: add reasoning_effort parameter support for gpt-oss models

- add reasoning effort UI support across all components

* fix: preserve user reasoning effort settings and improve display logic

* fix: artifacts code not streaming (#732)

* fix: artifact react load failed

* chore: remove log

* fix: artifacts code not stream

* fix: format

* feat: disable automatic model enabling for better UX (#734)

* feat: sync provider sorting from settings to model selection (#736)

* feat: sync provider sorting from settings to model selection

* feat: refactor ModelSelect to use computed providers for better reactivity

---------

Co-authored-by: yyhhyyyyyy <yyhhyyyyyy8@gmail.com>
Co-authored-by: hllshiro <40970081+hllshiro@users.noreply.github.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: gongchao <chao.gong@aminer.cn>
Co-authored-by: Simon He <57086651+Simon-He95@users.noreply.github.com>
Co-authored-by: wanna <wanna.w@binarywalk.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]

2 participants