Skip to content

Conversation

@zhangmo8
Copy link
Collaborator

@zhangmo8 zhangmo8 commented Jun 23, 2025

Pull Request Description (中文)

你的功能请求是否与某个问题有关?请描述一下。

Link #244

桌面应用程序的 UI/UX 更改

deepchat floating window base infra

目前的功能:

  • 点击唤起 deepchat
  • 拖拽
  • 设置关闭悬浮框
20250623_185328.mp4

image

Summary by CodeRabbit

  • New Features

    • Introduced a floating button that can be displayed on the desktop for quick activation of the application window.
    • Added settings option to enable or disable the floating button, available in the display settings menu.
    • Floating button supports localization, with new translations added for multiple languages.
    • The floating button features interactive animations and adapts to configuration updates in real time.
  • Bug Fixes

    • Improved robustness in window management and application shutdown processes.
  • Documentation

    • Updated and added type definitions for new APIs and components.
  • Chores

    • Enhanced build and TypeScript configurations to support new floating button components.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 23, 2025

Walkthrough

This update introduces a floating button feature to the Electron application. It adds main process logic, configuration, and inter-process communication for managing a floating button window. The renderer receives a new Vue component, supporting scripts, and localization for the floating button UI, while settings and stores are extended for user toggling. The build system and TypeScript configurations are updated to support these new modules.

Changes

File(s) Change Summary
electron.vite.config.ts, tsconfig.web.json Updated build and TypeScript configs to support new floating button entry points and typings.
src/main/events.ts, src/renderer/src/events.ts Added floating button event constants for IPC and state management.
src/main/index.ts, src/main/utils/index.ts Enhanced main process window/event handling and added utility for toggling window visibility.
src/main/presenter/floatingButtonPresenter/types.ts Introduced types and default config for floating button (config, state, events).
src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts Added class to manage floating button window lifecycle and behavior.
src/main/presenter/floatingButtonPresenter/index.ts Added presenter class to orchestrate floating button logic and IPC.
src/main/presenter/index.ts Integrated floating button presenter into main presenter lifecycle.
src/main/presenter/configPresenter/index.ts, src/shared/presenter.d.ts Extended config presenter and shared interface for floating button enable/disable state.
src/preload/floating-preload.ts, src/preload/index.d.ts Added preload script and typings to expose floating button API to renderer.
src/renderer/floating/FloatingButton.vue, src/renderer/floating/main.ts, src/renderer/floating/index.html, src/renderer/floating/env.d.ts Introduced Vue floating button component, entry point, HTML, and typings.
src/renderer/src/components/settings/DisplaySettings.vue, src/renderer/src/stores/floatingButton.ts Added floating button toggle to settings UI and created Pinia store for state.
src/renderer/src/i18n/*.json Added localization entries for floating button in all supported languages.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FloatingButton (Renderer)
    participant floatingButtonAPI (Preload)
    participant Main Process
    participant FloatingButtonPresenter
    participant FloatingButtonWindow

    User->>FloatingButton: Clicks button
    FloatingButton->>floatingButtonAPI: onClick()
    floatingButtonAPI->>Main Process: Send CLICKED event via IPC
    Main Process->>FloatingButtonPresenter: Handle CLICKED event
    FloatingButtonPresenter->>FloatingButtonWindow: Show main window (or toggle)

    Note over FloatingButtonPresenter, FloatingButtonWindow: Presenter manages window state and syncs config

    Main Process->>FloatingButtonPresenter: On ENABLED_CHANGED event
    FloatingButtonPresenter->>FloatingButtonWindow: Enable/disable window as needed
Loading

Poem

🐇
A button floats, a gentle gleam,
On desktop seas, a rabbit’s dream.
With every click, the app appears,
Quick as whiskers, gone are fears!
Settings now, with toggles bright,
Let users summon joy and light—
Hop, hop, hooray, the button’s in sight!


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 127f47a and 2a80b48.

📒 Files selected for processing (1)
  • src/main/presenter/floatingButtonPresenter/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/presenter/floatingButtonPresenter/index.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-check (x64)
✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • @coderabbitai modularize this function.
  • 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.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

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 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: 8

🧹 Nitpick comments (5)
src/preload/floating-preload.ts (1)

16-20: Consider using event constants for consistency.

The hardcoded event name 'floating-button-config-update' should use a constant from the events module for consistency and maintainability.

+import { FLOATING_BUTTON_EVENTS } from '@/events';

-    ipcRenderer.on('floating-button-config-update', (_event, config) => {
+    ipcRenderer.on(FLOATING_BUTTON_EVENTS.CONFIG_UPDATE, (_event, config) => {
-    ipcRenderer.removeAllListeners('floating-button-config-update');
+    ipcRenderer.removeAllListeners(FLOATING_BUTTON_EVENTS.CONFIG_UPDATE);
src/renderer/floating/FloatingButton.vue (1)

28-49: Consider using Vue's reactivity instead of manual DOM manipulation.

The click animation uses direct DOM manipulation which goes against Vue's reactive paradigm. Consider using a reactive state for better maintainability.

+const isClicked = ref(false)

const handleClick = () => {
-  // 点击反馈动画
-  if (floatingButton.value) {
-    floatingButton.value.style.transform = 'scale(0.9)'
-    setTimeout(() => {
-      if (floatingButton.value) {
-        floatingButton.value.style.transform = ''
-      }
-    }, 150)
-  }
+  // 点击反馈动画
+  isClicked.value = true
+  setTimeout(() => {
+    isClicked.value = false
+  }, 150)

And update the template:

-      :class="{ 'floating-button-pulse': isPulsing }"
+      :class="{
+        'floating-button-pulse': isPulsing,
+        'floating-button-clicked': isClicked
+      }"
src/main/presenter/floatingButtonPresenter/index.ts (1)

110-112: Add parentheses for clarity in boolean expression.

The current expression might be confusing due to operator precedence. Consider adding parentheses to make the intent clearer.

  public isAvailable(): boolean {
-   return this.config.enabled && this.floatingWindow?.exists() || false;
+   return (this.config.enabled && this.floatingWindow?.exists()) || false;
  }

Note: The || false is redundant since the expression already returns a boolean.

  public isAvailable(): boolean {
-   return this.config.enabled && this.floatingWindow?.exists() || false;
+   return this.config.enabled && (this.floatingWindow?.exists() ?? false);
  }
src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts (2)

181-199: Remove redundant 'bottom-right' case.

The 'bottom-right' case has identical logic to the default case, making it redundant.

      case 'bottom-left':
        x = this.config.offset.x;
        y = workAreaSize.height - this.config.size.height - this.config.offset.y;
        break;
-     case 'bottom-right':
      default:
        x = workAreaSize.width - this.config.size.width - this.config.offset.x;
        y = workAreaSize.height - this.config.size.height - this.config.offset.y;
        break;

71-75: Consider making DevTools optional in development mode.

Opening DevTools automatically on every launch in development mode can be disruptive, especially since the comment indicates it may interfere with dragging functionality.

      if (isDev) {
        await this.window.loadURL('http://localhost:5173/floating/');
-       // 开发模式下可选择性打开开发者工具(暂时禁用,避免影响拖拽)
-       this.window.webContents.openDevTools({ mode: 'detach' });
+       // 开发模式下可通过环境变量控制是否打开开发者工具
+       if (process.env.FLOATING_BUTTON_DEVTOOLS === 'true') {
+         this.window.webContents.openDevTools({ mode: 'detach' });
+       }
      } else {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 97e9f61 and 72e998c.

📒 Files selected for processing (14)
  • .vscode/settings.json (1 hunks)
  • electron.vite.config.ts (2 hunks)
  • src/main/events.ts (1 hunks)
  • src/main/index.ts (8 hunks)
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts (1 hunks)
  • src/main/presenter/floatingButtonPresenter/index.ts (1 hunks)
  • src/main/presenter/floatingButtonPresenter/types.ts (1 hunks)
  • src/main/presenter/index.ts (3 hunks)
  • src/preload/floating-preload.ts (1 hunks)
  • src/preload/index.d.ts (1 hunks)
  • src/renderer/floating/FloatingButton.vue (1 hunks)
  • src/renderer/floating/env.d.ts (1 hunks)
  • src/renderer/floating/index.html (1 hunks)
  • src/renderer/floating/main.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/renderer/floating/env.d.ts

[error] 6-6: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 6-6: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

src/main/presenter/floatingButtonPresenter/types.ts

[error] 40-40: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts

[error] 208-210: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.

(lint/complexity/noUselessSwitchCase)

🪛 GitHub Check: build-check (x64)
src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts

[failure] 3-3:
'fs' is declared but its value is never read.

🪛 GitHub Actions: PR Check
src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts

[error] 3-3: TypeScript error TS6133: 'fs' is declared but its value is never read.

🔇 Additional comments (23)
.vscode/settings.json (1)

6-7: Disable Vue-Vine telemetry in workspace settings

Good addition to turn off data tracking for the Vue-Vine extension. The comma after the previous entry is valid, and the JSON remains well-formed.

src/main/events.ts (1)

167-173: LGTM! Clean integration of floating button events.

The new FLOATING_BUTTON_EVENTS constant follows the established naming convention and provides appropriate event types for the floating button feature. The events are well-defined and logically structured.

src/renderer/floating/main.ts (1)

2-6: LGTM! Standard Vue application bootstrap.

The Vue application setup is correctly implemented, creating an app instance with the FloatingButton component and mounting it to the DOM.

src/main/presenter/index.ts (3)

22-22: LGTM! Proper import of FloatingButtonPresenter.

The import follows the established pattern and integrates cleanly with the existing presenter imports.


56-56: LGTM! Consistent property declaration.

The property declaration follows the same pattern as other presenters in the class.


84-84: LGTM! Proper presenter initialization.

The FloatingButtonPresenter is correctly initialized in the constructor following the established pattern. The initialization doesn't require dependencies, which is appropriate for this presenter.

src/renderer/floating/index.html (3)

1-12: LGTM! Proper HTML structure and meta configuration.

The HTML structure is well-formed with appropriate meta tags and viewport settings for a floating button renderer.


8-21: LGTM! Excellent CSS styling for floating UI.

The CSS reset and styling is perfect for a floating button:

  • Global reset with box-sizing: border-box
  • Transparent background for seamless desktop integration
  • Hidden overflow to prevent scrollbars
  • Full viewport dimensions

This ensures the floating button will render cleanly without visual artifacts.


24-25: LGTM! Proper Vue app mounting setup.

The div element provides the correct mounting point for the Vue application, and the module script correctly references the main.ts entry point.

electron.vite.config.ts (2)

36-44: LGTM! Proper multi-entry configuration for Electron preload.

The addition of multiple entry points in the preload build configuration correctly supports the floating button feature architecture.


88-90: LGTM! Renderer entry points properly configured.

The floating renderer entry point is correctly added alongside existing entries, enabling proper build and loading of the floating button UI.

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

10-19: LGTM! Well-defined API interface for floating button.

The global window interface extension properly defines the floating button API with appropriate method signatures for IPC communication.

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

4-27: LGTM! Secure API implementation with proper error handling.

The floating button API is well-structured with appropriate error handling for IPC communication. The method implementations correctly use ipcRenderer for communication with the main process.


29-42: LGTM! Proper security implementation for API exposure.

The context isolation detection and fallback approach correctly handles both secure and legacy Electron contexts while maintaining appropriate error handling.

src/renderer/floating/FloatingButton.vue (2)

71-82: LGTM! Proper lifecycle management for API integration.

The component correctly sets up and cleans up API listeners in the mounted/unmounted hooks, following Vue best practices.


85-168: LGTM! Excellent CSS implementation with smooth animations.

The styling includes well-crafted animations (pulse, shine, hover effects) and a properly positioned tooltip. The gradient background and transitions create a polished user experience.

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

1-20: LGTM! Comprehensive configuration interface.

The FloatingButtonConfig interface provides well-defined properties with clear documentation, covering all necessary aspects of floating button configuration.


43-56: LGTM! Sensible default configuration values.

The default configuration provides reasonable values for all properties, with the button positioned in the bottom-right corner and appropriate sizing and opacity settings.

src/main/index.ts (4)

109-115: LGTM! Good error handling for non-critical feature.

The floating button initialization is properly wrapped in try-catch, preventing failures from affecting the main application flow.


128-158: Well-structured window visibility handling.

The handleShowHiddenWindow function properly handles window state transitions with appropriate checks for destroyed windows. The mustShow parameter elegantly handles the different behaviors needed for tray clicks vs other triggers.


321-337: Correct handling of floating button in app lifecycle.

The modified window-all-closed handler properly excludes floating button windows from the quit decision, ensuring the app behaves correctly when only the floating button remains.


45-47: ```shell
#!/bin/bash

Locate the presenter index file and display its init() definition

INDEX_FILE=$(find src/main/presenter -maxdepth 1 -type f -name "index.ts")
echo "Presenter index file: $INDEX_FILE"
rg -A 5 -n "init\s*()" "$INDEX_FILE"


</details>
<details>
<summary>src/main/presenter/floatingButtonPresenter/index.ts (1)</summary>

`63-91`: **Well-implemented configuration update logic.**

The `updateConfig` method properly handles state transitions between enabled/disabled states, with appropriate error handling and logging. The immediate show after enabling provides good user feedback.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

getWebContentsId(): number
}
},
floatingButtonAPI: typeof floatingButtonAPI;
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

Fix the missing import for floatingButtonAPI type reference.

The type declaration typeof floatingButtonAPI references an undefined variable. This will cause a TypeScript compilation error since floatingButtonAPI is not imported or defined in this file.

You need to either import the floatingButtonAPI or define the type explicitly:

+import { floatingButtonAPI } from '../preload/floating-preload'
+
 declare global {
   interface Window {
     electron: ElectronAPI
     api: {
       copyText(text: string): void
       copyImage(image: string): void
       getPathForFile(file: File): string
       getWindowId(): number | null
       getWebContentsId(): number
     },
     floatingButtonAPI: typeof floatingButtonAPI;
   }
 }
📝 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
floatingButtonAPI: typeof floatingButtonAPI;
import { floatingButtonAPI } from '../preload/floating-preload'
declare global {
interface Window {
electron: ElectronAPI
api: {
copyText(text: string): void
copyImage(image: string): void
getPathForFile(file: File): string
getWindowId(): number | null
getWebContentsId(): number
},
floatingButtonAPI: typeof floatingButtonAPI;
}
}
🤖 Prompt for AI Agents
In src/preload/index.d.ts at line 13, the type reference `typeof
floatingButtonAPI` is used without importing or defining `floatingButtonAPI`,
causing a TypeScript error. Fix this by adding an import statement for
`floatingButtonAPI` from its module at the top of the file, or alternatively,
define the type explicitly in this file if importing is not suitable.

@@ -0,0 +1,6 @@
import '../src/assets/main.css'
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

Fix the incorrect CSS import path.

The CSS import path appears to be incorrect. From src/renderer/floating/main.ts, the path ../src/assets/main.css would resolve to src/src/assets/main.css, which likely doesn't exist.

Apply this fix:

-import '../src/assets/main.css'
+import '../../assets/main.css'
📝 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
import '../src/assets/main.css'
import '../../assets/main.css'
🤖 Prompt for AI Agents
In src/renderer/floating/main.ts at line 1, the CSS import path
'../src/assets/main.css' is incorrect because it resolves to
'src/src/assets/main.css'. Update the import path to correctly point to the
assets directory relative to this file, likely by changing it to
'../../assets/main.css' or the correct relative path that leads to
'src/assets/main.css'.

Comment on lines +3 to +8
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type
const component: DefineComponent<{}, {}, any>
export default component
}
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

Fix TypeScript type definition for Vue components.

The empty object type {} should be replaced with more specific types to improve type safety.

-  const component: DefineComponent<{}, {}, any>
+  const component: DefineComponent<Record<string, never>, Record<string, never>, any>
📝 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
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type
const component: DefineComponent<{}, {}, any>
export default component
}
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type
const component: DefineComponent<Record<string, never>, Record<string, never>, any>
export default component
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 6-6: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 6-6: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🤖 Prompt for AI Agents
In src/renderer/floating/env.d.ts between lines 3 and 8, the TypeScript type
definition for Vue components uses empty object types {} which lack specificity
and reduce type safety. Replace these empty object types with more precise types
such as Record<string, unknown> or appropriate generic parameters to better
represent props and other component options, thereby improving type safety in
the Vue component declarations.

Comment on lines +63 to +69
// 配置更新处理
const handleConfigUpdate = (config: any) => {
// 根据配置更新按钮样式
if (config.opacity !== undefined && document.body) {
document.body.style.opacity = config.opacity.toString()
}
}
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

Improve type safety for configuration parameter.

The config parameter uses any type, reducing type safety. Consider defining a proper interface.

-const handleConfigUpdate = (config: any) => {
+interface FloatingButtonConfig {
+  opacity?: number;
+  // Add other config properties as needed
+}
+
+const handleConfigUpdate = (config: FloatingButtonConfig) => {
📝 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
// 配置更新处理
const handleConfigUpdate = (config: any) => {
// 根据配置更新按钮样式
if (config.opacity !== undefined && document.body) {
document.body.style.opacity = config.opacity.toString()
}
}
// 配置更新处理
interface FloatingButtonConfig {
opacity?: number;
// Add other config properties as needed
}
const handleConfigUpdate = (config: FloatingButtonConfig) => {
// 根据配置更新按钮样式
if (config.opacity !== undefined && document.body) {
document.body.style.opacity = config.opacity.toString()
}
}
🤖 Prompt for AI Agents
In src/renderer/floating/FloatingButton.vue around lines 63 to 69, the config
parameter is typed as any, which reduces type safety. Define a proper TypeScript
interface for the config object specifying expected properties like opacity,
then update the handleConfigUpdate function to use this interface instead of any
to improve type safety and code clarity.

Comment on lines +34 to +41
export interface FloatingButtonEvents {
/** 悬浮按钮被点击 */
'floating-button-clicked': void;
/** 悬浮按钮显示状态改变 */
'floating-button-visibility-changed': { visible: boolean };
/** 悬浮按钮位置改变 */
'floating-button-position-changed': { x: number; y: number };
}
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

Fix void type usage in event interface.

Using void outside of return types is confusing and should be replaced with undefined for better clarity.

export interface FloatingButtonEvents {
  /** 悬浮按钮被点击 */
-  'floating-button-clicked': void;
+  'floating-button-clicked': undefined;
  /** 悬浮按钮显示状态改变 */
  'floating-button-visibility-changed': { visible: boolean };
  /** 悬浮按钮位置改变 */
  'floating-button-position-changed': { x: number; y: number };
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/types.ts between lines 34 and 41,
replace the use of 'void' as the type for the 'floating-button-clicked' event
with 'undefined' to improve clarity and correctness, since 'void' is intended
for function return types and 'undefined' better represents the absence of a
value in this context.

Comment on lines 160 to 172
const handleWindowVisibilityChange = () => {
const allWindows = presenter.windowPresenter.getAllWindows()
const hasVisibleWindow = allWindows.some(win => !win.isDestroyed() && win.isVisible())
floatingButtonPresenter.onMainWindowVisibilityChanged(hasVisibleWindow)
}

// 监听窗口显示事件
app.on('browser-window-created', (_, window) => {
window.on('show', handleWindowVisibilityChange)
window.on('hide', handleWindowVisibilityChange)
window.on('closed', handleWindowVisibilityChange)
})

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

Potential duplicate event listeners on windows.

The current implementation attaches event listeners every time a browser window is created. If a window is reused or if the same window triggers multiple browser-window-created events, this could lead to duplicate listeners and multiple calls to handleWindowVisibilityChange.

Consider tracking which windows already have listeners attached:

+ const windowsWithListeners = new WeakSet<BrowserWindow>();

  app.on('browser-window-created', (_, window) => {
+   if (windowsWithListeners.has(window)) {
+     return;
+   }
+   windowsWithListeners.add(window);
    window.on('show', handleWindowVisibilityChange)
    window.on('hide', handleWindowVisibilityChange)
    window.on('closed', handleWindowVisibilityChange)
  })
📝 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
// 监听窗口显示/隐藏状态变化
const handleWindowVisibilityChange = () => {
const allWindows = presenter.windowPresenter.getAllWindows()
const hasVisibleWindow = allWindows.some(win => !win.isDestroyed() && win.isVisible())
floatingButtonPresenter.onMainWindowVisibilityChanged(hasVisibleWindow)
}
// 监听窗口显示事件
app.on('browser-window-created', (_, window) => {
window.on('show', handleWindowVisibilityChange)
window.on('hide', handleWindowVisibilityChange)
window.on('closed', handleWindowVisibilityChange)
})
// 监听窗口显示/隐藏状态变化
const handleWindowVisibilityChange = () => {
const allWindows = presenter.windowPresenter.getAllWindows()
const hasVisibleWindow = allWindows.some(win => !win.isDestroyed() && win.isVisible())
floatingButtonPresenter.onMainWindowVisibilityChanged(hasVisibleWindow)
}
const windowsWithListeners = new WeakSet<BrowserWindow>();
// 监听窗口显示事件
app.on('browser-window-created', (_, window) => {
if (windowsWithListeners.has(window)) {
return;
}
windowsWithListeners.add(window);
window.on('show', handleWindowVisibilityChange)
window.on('hide', handleWindowVisibilityChange)
window.on('closed', handleWindowVisibilityChange)
})
🤖 Prompt for AI Agents
In src/main/index.ts around lines 160 to 172, the event listeners for 'show',
'hide', and 'closed' are attached every time a new browser window is created,
which can cause duplicate listeners if windows are reused or multiple
'browser-window-created' events occur for the same window. To fix this,
implement a mechanism to track windows that already have listeners attached,
such as using a WeakSet or a Map, and only add listeners if they have not been
added before, preventing duplicate event handling.

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: 4

🧹 Nitpick comments (2)
src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts (2)

193-194: Remove redundant case clause.

The 'bottom-right' case is immediately followed by the default case that handles the same logic, making it redundant.

      case 'bottom-left':
        x = this.config.offset.x;
        y = workAreaSize.height - this.config.size.height - this.config.offset.y;
        break;
-     case 'bottom-right':
      default:
        x = workAreaSize.width - this.config.size.width - this.config.offset.x;
        y = workAreaSize.height - this.config.size.height - this.config.offset.y;
        break;

72-73: Make devTools opening configurable instead of automatic.

Automatically opening devTools may not always be desired and could interfere with testing the floating window behavior.

  if (isDev) {
    await this.window.loadURL('http://localhost:5173/floating/');
-   // 开发模式下可选择性打开开发者工具(暂时禁用,避免影响拖拽)
-   this.window.webContents.openDevTools({ mode: 'detach' });
+   // Optionally open devTools based on environment variable
+   if (process.env.FLOATING_DEVTOOLS === 'true') {
+     this.window.webContents.openDevTools({ mode: 'detach' });
+   }
  }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 72e998c and a8ad206.

📒 Files selected for processing (3)
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts (1 hunks)
  • src/preload/floating-preload.ts (1 hunks)
  • tsconfig.web.json (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tsconfig.web.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/preload/floating-preload.ts
🧰 Additional context used
🪛 Biome (1.9.4)
src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts

[error] 207-209: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.

(lint/complexity/noUselessSwitchCase)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-check (x64)
🔇 Additional comments (1)
src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts (1)

6-228: Well-designed class architecture for floating window management.

The FloatingButtonWindow class provides good encapsulation with clear separation of concerns between window management, configuration, and state tracking. The event handling setup and cleanup logic are properly implemented.

Comment on lines +91 to +99
public show(): void {
if (!this.window) {
return;
}

this.window.show();
this.state.isVisible = true;
logger.debug('FloatingButtonWindow shown');
}
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

Improve window state validation in show/hide methods.

The methods only check if window is null but don't validate if the window is still valid (not destroyed).

  public show(): void {
-   if (!this.window) {
+   if (!this.exists()) {
      return;
    }

    this.window.show();
    this.state.isVisible = true;
    logger.debug('FloatingButtonWindow shown');
  }

  public hide(): void {
-   if (!this.window) {
+   if (!this.exists()) {
      return;
    }

    this.window.hide();
    this.state.isVisible = false;
    logger.debug('FloatingButtonWindow hidden');
  }

Also applies to: 104-112

🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts around
lines 91 to 99 and 104 to 112, the show and hide methods only check if the
window object exists but do not verify if the window is still valid or has been
destroyed. Update these methods to also check if the window is not destroyed
before proceeding. This can be done by adding a condition to verify the window's
validity state, preventing operations on a destroyed window.

Comment on lines +60 to +62
webSecurity: false, // 开发模式下允许跨域
devTools: true, // 开发模式下启用开发者工具
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

Address security vulnerabilities in webPreferences.

The current configuration disables important security features that should only be relaxed in development mode.

  webPreferences: {
    nodeIntegration: false,
    contextIsolation: true,
    preload: preloadPath,
-   webSecurity: false, // 开发模式下允许跨域
-   devTools: true, // 开发模式下启用开发者工具
-   sandbox: false // 禁用沙盒模式,确保预加载脚本能正常工作
+   webSecurity: isDev ? false : true, // Only disable in development
+   devTools: isDev, // Only enable in development
+   sandbox: !isDev // Enable sandbox in production for better security
  }

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

🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts around
lines 60 to 62, the webPreferences settings disable key security features like
webSecurity and sandbox unconditionally. Modify the code to enable these
security features by default and only disable them when running in development
mode, ensuring production builds maintain proper security configurations.

Comment on lines +134 to +145
if (config.size) {
this.window.setSize(this.config.size.width, this.config.size.height);
this.state.bounds.width = this.config.size.width;
this.state.bounds.height = this.config.size.height;
}

if (config.position || config.offset) {
const position = this.calculatePosition();
this.window.setPosition(position.x, position.y);
this.state.bounds.x = position.x;
this.state.bounds.y = position.y;
}
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

Add error handling for window property updates.

State updates should only occur after successful window operations to maintain consistency.

      if (config.size) {
-       this.window.setSize(this.config.size.width, this.config.size.height);
-       this.state.bounds.width = this.config.size.width;
-       this.state.bounds.height = this.config.size.height;
+       try {
+         this.window.setSize(this.config.size.width, this.config.size.height);
+         this.state.bounds.width = this.config.size.width;
+         this.state.bounds.height = this.config.size.height;
+       } catch (error) {
+         logger.error('Failed to update window size:', error);
+       }
      }

      if (config.position || config.offset) {
-       const position = this.calculatePosition();
-       this.window.setPosition(position.x, position.y);
-       this.state.bounds.x = position.x;
-       this.state.bounds.y = position.y;
+       try {
+         const position = this.calculatePosition();
+         this.window.setPosition(position.x, position.y);
+         this.state.bounds.x = position.x;
+         this.state.bounds.y = position.y;
+       } catch (error) {
+         logger.error('Failed to update window position:', 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.

Suggested change
if (config.size) {
this.window.setSize(this.config.size.width, this.config.size.height);
this.state.bounds.width = this.config.size.width;
this.state.bounds.height = this.config.size.height;
}
if (config.position || config.offset) {
const position = this.calculatePosition();
this.window.setPosition(position.x, position.y);
this.state.bounds.x = position.x;
this.state.bounds.y = position.y;
}
if (config.size) {
try {
this.window.setSize(this.config.size.width, this.config.size.height);
this.state.bounds.width = this.config.size.width;
this.state.bounds.height = this.config.size.height;
} catch (error) {
logger.error('Failed to update window size:', error);
}
}
if (config.position || config.offset) {
try {
const position = this.calculatePosition();
this.window.setPosition(position.x, position.y);
this.state.bounds.x = position.x;
this.state.bounds.y = position.y;
} catch (error) {
logger.error('Failed to update window position:', error);
}
}
🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts around
lines 134 to 145, the code updates the state properties immediately after
calling window methods without verifying if those calls succeeded. To fix this,
add error handling around the window.setSize and window.setPosition calls, and
only update the state.bounds properties if the respective window method
completes successfully. This ensures state consistency with the actual window
properties.

Comment on lines +37 to +39
const preloadPath = isDev
? path.join(process.cwd(), 'out/preload/floating.mjs')
: path.join(__dirname, '../../preload/floating.mjs');
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

Validate preload script paths to prevent runtime failures.

The hardcoded paths for the preload script may not exist in all deployment scenarios. Consider adding path existence validation or using more robust path resolution.

  const isDev = process.env.NODE_ENV === 'development';
- const preloadPath = isDev 
-   ? path.join(process.cwd(), 'out/preload/floating.mjs')
-   : path.join(__dirname, '../../preload/floating.mjs');
+ const preloadPath = isDev 
+   ? path.join(process.cwd(), 'out/preload/floating.mjs')
+   : path.join(__dirname, '../../preload/floating.mjs');
+ 
+ // Validate preload script exists
+ if (!require('fs').existsSync(preloadPath)) {
+   throw new Error(`Preload script not found: ${preloadPath}`);
+ }
📝 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
const preloadPath = isDev
? path.join(process.cwd(), 'out/preload/floating.mjs')
: path.join(__dirname, '../../preload/floating.mjs');
const isDev = process.env.NODE_ENV === 'development';
const preloadPath = isDev
? path.join(process.cwd(), 'out/preload/floating.mjs')
: path.join(__dirname, '../../preload/floating.mjs');
// Validate preload script exists
if (!require('fs').existsSync(preloadPath)) {
throw new Error(`Preload script not found: ${preloadPath}`);
}
🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts around
lines 37 to 39, the preload script paths are hardcoded and may not exist in all
deployment environments, risking runtime failures. Add validation to check if
the resolved preloadPath exists on the filesystem before using it, and handle
the case where it does not (e.g., log an error or fallback to a default path).
Alternatively, improve path resolution by dynamically determining the correct
preload script location based on the environment or configuration.

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

♻️ Duplicate comments (1)
src/renderer/floating/env.d.ts (1)

3-8: The empty object type issue was already addressed in a previous review.

This is the same issue identified in the past review comment - using {} as a type should be replaced with more specific types for better type safety.

🧹 Nitpick comments (2)
src/renderer/src/stores/floatingButton.ts (1)

1-2: Remove unused Vue import.

The onMounted import is no longer needed if you move the lifecycle logic to components as suggested above.

-import { ref, onMounted } from 'vue'
+import { ref } from 'vue'
src/main/presenter/floatingButtonPresenter/index.ts (1)

34-35: Consider using consistent logging approach.

The code mixes console.log statements with error logging. Consider using a consistent logging approach throughout the application.

If a logger is available elsewhere in the codebase, consider using it consistently:

-      console.log('FloatingButton is disabled, skipping window creation');
+      // Use your application's logger instead of console.log

Also applies to: 62-63, 67-68, 72-72

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3189c9a and 127f47a.

📒 Files selected for processing (21)
  • electron.vite.config.ts (2 hunks)
  • src/main/events.ts (1 hunks)
  • src/main/index.ts (7 hunks)
  • src/main/presenter/configPresenter/index.ts (4 hunks)
  • src/main/presenter/floatingButtonPresenter/index.ts (1 hunks)
  • src/main/presenter/index.ts (5 hunks)
  • src/main/utils/index.ts (1 hunks)
  • src/renderer/floating/env.d.ts (1 hunks)
  • src/renderer/src/components/settings/DisplaySettings.vue (4 hunks)
  • src/renderer/src/events.ts (1 hunks)
  • src/renderer/src/i18n/en-US/settings.json (1 hunks)
  • src/renderer/src/i18n/fa-IR/settings.json (1 hunks)
  • src/renderer/src/i18n/fr-FR/settings.json (1 hunks)
  • src/renderer/src/i18n/ja-JP/settings.json (1 hunks)
  • src/renderer/src/i18n/ko-KR/settings.json (1 hunks)
  • src/renderer/src/i18n/ru-RU/settings.json (1 hunks)
  • src/renderer/src/i18n/zh-CN/settings.json (1 hunks)
  • src/renderer/src/i18n/zh-HK/settings.json (1 hunks)
  • src/renderer/src/i18n/zh-TW/settings.json (1 hunks)
  • src/renderer/src/stores/floatingButton.ts (1 hunks)
  • src/shared/presenter.d.ts (1 hunks)
✅ Files skipped from review due to trivial changes (9)
  • src/renderer/src/i18n/en-US/settings.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • src/renderer/src/events.ts
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/zh-CN/settings.json
  • src/renderer/src/i18n/zh-HK/settings.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/fr-FR/settings.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/main/events.ts
  • src/main/index.ts
  • electron.vite.config.ts
  • src/main/presenter/index.ts
🧰 Additional context used
📓 Path-based instructions (9)
`src/renderer/src/**/*.{js,ts,vue}`: All user-facing strings must use i18n keys; avoid hardcoded user-visible text.

src/renderer/src/**/*.{js,ts,vue}: All user-facing strings must use i18n keys; avoid hardcoded user-visible text.

  • src/renderer/src/components/settings/DisplaySettings.vue
  • src/renderer/src/stores/floatingButton.ts
`src/**/*`: 所有新功能应该在 src 目录下开发

src/**/*: 所有新功能应该在 src 目录下开发

  • src/renderer/src/components/settings/DisplaySettings.vue
  • src/main/utils/index.ts
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/shared/presenter.d.ts
  • src/renderer/src/stores/floatingButton.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
  • src/main/presenter/configPresenter/index.ts
  • src/renderer/floating/env.d.ts
`src/renderer/**/*`: 渲染进程代码放在 src/renderer

src/renderer/**/*: 渲染进程代码放在 src/renderer

  • src/renderer/src/components/settings/DisplaySettings.vue
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/renderer/src/stores/floatingButton.ts
  • src/renderer/floating/env.d.ts
`src/renderer/src/**/*.{vue,ts,tsx,js,jsx}`: Use scoped styles to prevent CSS conflicts between components

src/renderer/src/**/*.{vue,ts,tsx,js,jsx}: Use scoped styles to prevent CSS conflicts between components

  • src/renderer/src/components/settings/DisplaySettings.vue
  • src/renderer/src/stores/floatingButton.ts
`src/renderer/**/*`: Apply all code style, structure, naming, TypeScript, syntax, UI, performance, and Nuxt-specific guidelines to all files recursively under src/renderer.

src/renderer/**/*: Apply all code style, structure, naming, TypeScript, syntax, UI, performance, and Nuxt-specific guidelines to all files recursively under src/renderer.

  • src/renderer/src/components/settings/DisplaySettings.vue
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/renderer/src/stores/floatingButton.ts
  • src/renderer/floating/env.d.ts
`src/main/**/*`: 主进程代码放在 src/main

src/main/**/*: 主进程代码放在 src/main

  • src/main/utils/index.ts
  • src/main/presenter/floatingButtonPresenter/index.ts
  • src/main/presenter/configPresenter/index.ts
`src/shared/*.d.ts`: Use src/shared/*.d.ts to define the types of objects exposed by the main process to the renderer process.

src/shared/*.d.ts: Use src/shared/*.d.ts to define the types of objects exposed by the main process to the renderer process.

  • src/shared/presenter.d.ts
`src/shared/**/*`: 共享类型定义放在 shared 目录

src/shared/**/*: 共享类型定义放在 shared 目录

  • src/shared/presenter.d.ts
`src/renderer/src/stores/**/*.{vue,ts,tsx,js,jsx}`: Use modules to organize related state and actions in Pinia stores. Implement proper state persistence in Pinia stores to maintai...

src/renderer/src/stores/**/*.{vue,ts,tsx,js,jsx}: Use modules to organize related state and actions in Pinia stores.
Implement proper state persistence in Pinia stores to maintain data across sessions.
Use getters in Pinia stores for computed state properties.
Utilize actions in Pinia stores for side effects and asynchronous operations.
Keep Pinia stores focused on global state, not component-specific data.

  • src/renderer/src/stores/floatingButton.ts
🪛 GitHub Check: build-check (x64)
src/main/presenter/floatingButtonPresenter/index.ts

[failure] 90-90:
'visible' is declared but its value is never read.


[failure] 3-3:
'logger' is declared but its value is never read.

🪛 GitHub Actions: PR Check
src/main/presenter/floatingButtonPresenter/index.ts

[error] 3-3: TypeScript error TS6133: 'logger' is declared but its value is never read.

🪛 Biome (1.9.4)
src/renderer/floating/env.d.ts

[error] 6-6: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 6-6: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🔇 Additional comments (10)
src/shared/presenter.d.ts (1)

341-343: LGTM! Clean interface extension.

The floating button configuration methods are well-defined with clear naming, proper typing, and follow the existing interface patterns.

src/renderer/src/i18n/zh-TW/settings.json (1)

441-443: LGTM! Proper localization implementation.

The Traditional Chinese strings for the floating button feature are correctly formatted and appropriately placed within the display settings section.

src/renderer/src/components/settings/DisplaySettings.vue (4)

166-166: LGTM! Proper store import.

The floating button store import follows the existing pattern used by other stores in this component.


181-181: LGTM! Consistent store instantiation.

The store instantiation follows the same pattern as other stores used in this component.


93-113: LGTM! Well-structured UI implementation.

The floating button toggle UI follows the existing design patterns, includes proper i18n usage, and maintains consistency with other toggle sections in the component.


248-250: LGTM! Simple and effective handler.

The handler function correctly delegates to the store method, maintaining separation of concerns and following the established pattern.

src/main/utils/index.ts (3)

15-32: LGTM! Solid window management logic.

The window visibility logic is well-implemented with proper error handling for destroyed windows and clear show/hide behavior based on the mustShow parameter.


4-4: ```shell
#!/bin/bash

Display the full IWindowPresenter interface definition for verification

echo "Showing IWindowPresenter interface:"
rg "export interface IWindowPresenter" -A 50 src/shared/presenter.d.ts


---

`13-13`: ```shell
#!/bin/bash
# Display IWindowPresenter interface methods to verify getFocusedWindow presence
echo "Extracting IWindowPresenter methods from src/shared/presenter.d.ts:"
sed -n '148,220p' src/shared/presenter.d.ts
src/main/presenter/configPresenter/index.ts (1)

753-762: Improve error handling and avoid potential circular dependencies.

The current implementation has several issues: empty catch block, potential circular dependency with direct presenter access, and no error propagation to callers.

Consider this improved implementation:

  setFloatingButtonEnabled(enabled: boolean): void {
    this.setSetting('floatingButtonEnabled', enabled)
    eventBus.sendToMain(FLOATING_BUTTON_EVENTS.ENABLED_CHANGED, enabled)
-    
-    try {
-      presenter.floatingButtonPresenter.setEnabled(enabled)
-    } catch (error) {
-      console.error('Failed to directly call floatingButtonPresenter:', error)
-    }
  }

The direct presenter call creates a circular dependency risk. The event bus emission should be sufficient for the floating button presenter to react to changes via the main process event handling.

Likely an incorrect or invalid review comment.

Comment on lines +44 to +47
// 在组件挂载时初始化
onMounted(async () => {
await initializeState()
})
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

Move lifecycle hook to component instead of store.

Using onMounted inside a Pinia store is unconventional and can cause issues. Stores should be stateless and not contain lifecycle hooks. Move this initialization logic to the component that uses the store.

Remove the onMounted from the store:

-  // 在组件挂载时初始化
-  onMounted(async () => {
-    await initializeState()
-  })

Instead, call initializeState() from your component's onMounted:

// In your component
import { onMounted } from 'vue'
import { useFloatingButtonStore } from '@/stores/floatingButton'

const floatingButtonStore = useFloatingButtonStore()

onMounted(async () => {
  await floatingButtonStore.initializeState()
})
🤖 Prompt for AI Agents
In src/renderer/src/stores/floatingButton.ts around lines 44 to 47, remove the
onMounted lifecycle hook from the Pinia store because stores should be stateless
and not contain lifecycle hooks. Instead, move the call to initializeState()
into the component that uses this store by importing the store and calling
initializeState() inside the component's onMounted hook.

Comment on lines +22 to +31
const setFloatingButtonEnabled = async (value: boolean) => {
try {
enabled.value = Boolean(value)
await configP.setFloatingButtonEnabled(value)
} catch (error) {
console.error('Failed to set floating button enabled status:', error)
// 如果设置失败,回滚本地状态
enabled.value = !value
}
}
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

Fix rollback logic and add proper TypeScript typing.

The error rollback logic is flawed - !value may not represent the original state. Also, the method is missing a return type annotation.

Apply this fix:

-  const setFloatingButtonEnabled = async (value: boolean) => {
+  const setFloatingButtonEnabled = async (value: boolean): Promise<void> => {
+    const originalValue = enabled.value
     try {
       enabled.value = Boolean(value)
       await configP.setFloatingButtonEnabled(value)
     } catch (error) {
       console.error('Failed to set floating button enabled status:', error)
-      // 如果设置失败,回滚本地状态
-      enabled.value = !value
+      // 如果设置失败,回滚到原始状态
+      enabled.value = originalValue
     }
   }
📝 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
const setFloatingButtonEnabled = async (value: boolean) => {
try {
enabled.value = Boolean(value)
await configP.setFloatingButtonEnabled(value)
} catch (error) {
console.error('Failed to set floating button enabled status:', error)
// 如果设置失败,回滚本地状态
enabled.value = !value
}
}
const setFloatingButtonEnabled = async (value: boolean): Promise<void> => {
const originalValue = enabled.value
try {
enabled.value = Boolean(value)
await configP.setFloatingButtonEnabled(value)
} catch (error) {
console.error('Failed to set floating button enabled status:', error)
// 如果设置失败,回滚到原始状态
enabled.value = originalValue
}
}
🤖 Prompt for AI Agents
In src/renderer/src/stores/floatingButton.ts lines 22 to 31, the rollback logic
on error incorrectly sets enabled.value to !value, which may not reflect the
original state before the change. Fix this by storing the original enabled.value
before setting it and revert to that value on error. Additionally, add an
explicit return type annotation (Promise<void>) to the async function
setFloatingButtonEnabled.

Comment on lines +747 to +750
getFloatingButtonEnabled(): boolean {
const value = this.getSetting<boolean>('floatingButtonEnabled') ?? false
return value === undefined || value === null ? false : value
}
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

Fix inconsistent default value handling.

There's a mismatch between the constructor default (true) and the getter's fallback behavior (false). This inconsistency could lead to unexpected behavior.

Apply this fix to make the getter consistent with the constructor default:

  getFloatingButtonEnabled(): boolean {
-    const value = this.getSetting<boolean>('floatingButtonEnabled') ?? false
-    return value === undefined || value === null ? false : value
+    const value = this.getSetting<boolean>('floatingButtonEnabled') ?? true
+    return value === undefined || value === null ? true : value
  }
📝 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
getFloatingButtonEnabled(): boolean {
const value = this.getSetting<boolean>('floatingButtonEnabled') ?? false
return value === undefined || value === null ? false : value
}
getFloatingButtonEnabled(): boolean {
const value = this.getSetting<boolean>('floatingButtonEnabled') ?? true
return value === undefined || value === null ? true : value
}
🤖 Prompt for AI Agents
In src/main/presenter/configPresenter/index.ts around lines 747 to 750, the
getter getFloatingButtonEnabled returns false as a default, which conflicts with
the constructor default of true. Update the getter to return true when the
setting is undefined or null, ensuring the default behavior is consistent with
the constructor.

Comment on lines +120 to +126
ipcMain.on(FLOATING_BUTTON_EVENTS.CLICKED, () => {
try {
// 触发内置事件处理器
handleShowHiddenWindow(true)
} catch (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

Improve error handling in IPC event handler.

The empty catch block silently swallows errors, making debugging difficult.

Add proper error logging:

    ipcMain.on(FLOATING_BUTTON_EVENTS.CLICKED, () => {
      try {
        // 触发内置事件处理器
        handleShowHiddenWindow(true)
      } catch (error) {
+        console.error('Failed to handle floating button click:', 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.

Suggested change
ipcMain.on(FLOATING_BUTTON_EVENTS.CLICKED, () => {
try {
// 触发内置事件处理器
handleShowHiddenWindow(true)
} catch (error) {
}
})
ipcMain.on(FLOATING_BUTTON_EVENTS.CLICKED, () => {
try {
// 触发内置事件处理器
handleShowHiddenWindow(true)
} catch (error) {
console.error('Failed to handle floating button click:', error)
}
})
🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/index.ts around lines 120 to 126,
the catch block in the IPC event handler is empty, which silently swallows
errors and hinders debugging. Modify the catch block to log the caught error
using an appropriate logging mechanism or console.error, ensuring that any
exceptions thrown by handleShowHiddenWindow(true) are properly recorded for
troubleshooting.

@zerob13
Copy link
Collaborator

zerob13 commented Jun 24, 2025

有几个小问题:
1、浮动按钮的拖拽区域有点小,似乎按钮本身按住是无法拖拽的
2、目前只有唤起应用的能力,可以考虑默认设置为false

@zhangmo8
Copy link
Collaborator Author

有几个小问题:

1、浮动按钮的拖拽区域有点小,似乎按钮本身按住是无法拖拽的

2、目前只有唤起应用的能力,可以考虑默认设置为false

1:取决于窗口大小,太大了感觉不太好看,窗口拖拽和Dom点击有些冲突,我还没想好怎么解决
2: 这个主要是担心其他人发现不了这个功能,哈哈哈🤣

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.

2 participants