Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 22 additions & 33 deletions src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import path from 'path'
import { FloatingButtonConfig, FloatingButtonState } from './types'
import logger from '../../../shared/logger'
import { platform } from '@electron-toolkit/utils'
import windowStateManager from 'electron-window-state'

export class FloatingButtonWindow {
private window: BrowserWindow | null = null
private config: FloatingButtonConfig
private state: FloatingButtonState
private windowState: any
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

Use proper TypeScript typing for windowState property.

The windowState property should use a proper type instead of any to enable better type safety and IntelliSense support.

Apply this diff to add proper typing:

-  private windowState: any
+  private windowState: WindowState
📝 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 windowState: any
private windowState: WindowState
🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts around
line 12, change the windowState property from type any to a proper typed
interface (e.g. import and use the State type from the electron-window-state
package or declare an explicit interface with x, y, width, height, isMaximized,
isMinimized, etc.) and make it nullable if it can be uninitialized (e.g. State |
null). Update the top of the file to import the State type (import { State }
from 'electron-window-state') or add the inline interface, then replace "private
windowState: any" with "private windowState: State | null" (or your chosen
interface) and update any usages to handle the nullable type if needed.


constructor(config: FloatingButtonConfig) {
this.config = config
Expand All @@ -20,6 +22,13 @@ export class FloatingButtonWindow {
height: config.size.height
}
}

// 初始化窗口状态管理器
this.windowState = windowStateManager({
file: 'floating-button-window-state.json',
defaultWidth: config.size.width,
defaultHeight: config.size.height
})
}

/**
Expand All @@ -31,16 +40,14 @@ export class FloatingButtonWindow {
}

try {
const position = this.calculatePosition()

// 根据环境选择正确的预加载脚本路径
const isDev = process.env.NODE_ENV === 'development'

this.window = new BrowserWindow({
width: this.config.size.width,
height: this.config.size.height,
x: position.x,
y: position.y,
x: this.windowState.x,
y: this.windowState.y,
width: this.windowState.width,
height: this.windowState.height,
frame: false,
transparent: platform.isMacOS,
alwaysOnTop: this.config.alwaysOnTop,
Expand All @@ -63,6 +70,7 @@ export class FloatingButtonWindow {
sandbox: false // 禁用沙盒模式,确保预加载脚本能正常工作
}
})
this.windowState.manage(this.window)
this.window.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })
this.window.setAlwaysOnTop(true, 'floating')
// 设置窗口透明度
Expand Down Expand Up @@ -140,7 +148,7 @@ export class FloatingButtonWindow {
}

if (config.position || config.offset) {
const position = this.calculatePosition()
const position = this.getDefaultPosition()
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify position calculation logic change.

The change from calculatePosition() to getDefaultPosition() alters the positioning behavior. The new implementation always uses bottom-right positioning, while the old logic likely supported multiple position options based on config.position.

Run the following script to verify if the removed calculatePosition method handled multiple position types:


🏁 Script executed:

#!/bin/bash
# Description: Check if calculatePosition method existed and how it handled different position types

# Search for any references to calculatePosition in the codebase
rg -A 10 -B 5 "calculatePosition"

# Search for position-related logic that might have been removed
rg -A 5 -B 5 "(top-left|top-right|bottom-left|bottom-right)" --type ts

Length of output: 5898


🏁 Script executed:

#!/bin/bash
# Inspect getDefaultPosition and surrounding logic in FloatingButtonWindow.ts

rg -C 10 "getDefaultPosition" --glob "src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts"

Length of output: 4044


Re-enable configurable positioning for the floating button

The current call to getDefaultPosition() always places the window bottom-right with a hard-coded 20px offset, ignoring both config.position and config.offset. We need to restore the branching logic so that 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' (and any custom offsets) are respected.

• File: src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts
– In the block
ts if (config.position || config.offset) { const position = this.getDefaultPosition() … }
replace getDefaultPosition() with a calculatePosition() method that:
• Switches on this.config.position
• Applies this.config.offset.x/.y instead of the fixed 20px
• Falls back to bottom-right when unspecified

• Implement a new private calculatePosition(): { x: number; y: number } (or enhance getDefaultPosition) akin to the one in FloatingChatWindow.ts, for example:

- private getDefaultPosition(): { x: number; y: number } {
-   const { workArea } = screen.getPrimaryDisplay()
-   return {
-     x: workArea.width - this.config.size.width - 20,
-     y: workArea.height - this.config.size.height - 20
-   }
- }
+ private calculatePosition(): { x: number; y: number } {
+   const { workArea } = screen.getPrimaryDisplay()
+   const { width, height } = this.config.size
+   const { x: ox, y: oy } = this.config.offset
+   let x: number, y: number
+   switch (this.config.position) {
+     case 'top-left':
+       x = workArea.x + ox;  y = workArea.y + oy;  break
+     case 'top-right':
+       x = workArea.width  - width  - ox; y = workArea.y + oy;  break
+     case 'bottom-left':
+       x = workArea.x + ox;  y = workArea.height - height - oy;  break
+     default: // 'bottom-right'
+       x = workArea.width  - width  - ox; y = workArea.height - height - oy;  
+   }
+   return { x, y }
+ }

• Update the call site to use this.calculatePosition() instead of getDefaultPosition() when config.position or config.offset is set.

This will ensure all four corner options and custom offsets behave as intended.

🤖 Prompt for AI Agents
In src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts around
line 151, the code always uses getDefaultPosition() which hardcodes bottom-right
with a 20px offset and ignores config.position and config.offset; replace that
call with a new private calculatePosition() that switches on
this.config.position ('top-left'|'top-right'|'bottom-left'|'bottom-right'),
applies offsets from this.config.offset.x and .y (falling back to 20px only when
offsets are undefined), and returns {x,y}; update the block that currently
checks if (config.position || config.offset) to call this.calculatePosition()
instead of getDefaultPosition(), ensuring the fallback remains bottom-right when
position is unspecified.

this.window.setPosition(position.x, position.y)
this.state.bounds.x = position.x
this.state.bounds.y = position.y
Expand Down Expand Up @@ -175,35 +183,16 @@ export class FloatingButtonWindow {
}

/**
* 计算悬浮按钮位置
* 获取默认位置(右下角)
*/
private calculatePosition(): { x: number; y: number } {
private getDefaultPosition(): { x: number; y: number } {
const primaryDisplay = screen.getPrimaryDisplay()
const { workAreaSize } = primaryDisplay

let x: number, y: number

switch (this.config.position) {
case 'top-left':
x = this.config.offset.x
y = this.config.offset.y
break
case 'top-right':
x = workAreaSize.width - this.config.size.width - this.config.offset.x
y = this.config.offset.y
break
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
}
const { workArea } = primaryDisplay

return { x, y }
return {
x: workArea.width - this.config.size.width - 20,
y: workArea.height - this.config.size.height - 20
}
}

/**
Expand Down