-
Notifications
You must be signed in to change notification settings - Fork 649
Expand file tree
/
Copy pathindex.ts
More file actions
241 lines (214 loc) · 6.7 KB
/
index.ts
File metadata and controls
241 lines (214 loc) · 6.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
import { FloatingButtonWindow } from './FloatingButtonWindow'
import { FloatingButtonConfig, FloatingButtonState, DEFAULT_FLOATING_BUTTON_CONFIG } from './types'
import { ConfigPresenter } from '../configPresenter'
import { ipcMain, Menu, app } from 'electron'
import { FLOATING_BUTTON_EVENTS } from '@/events'
import { presenter } from '../index'
export class FloatingButtonPresenter {
private floatingWindow: FloatingButtonWindow | null = null
private config: FloatingButtonConfig
private configPresenter: ConfigPresenter
constructor(configPresenter: ConfigPresenter) {
this.configPresenter = configPresenter
this.config = {
...DEFAULT_FLOATING_BUTTON_CONFIG
}
}
/**
* 初始化悬浮按钮功能
*/
public async initialize(config?: Partial<FloatingButtonConfig>): Promise<void> {
const floatingButtonEnabled = this.configPresenter.getFloatingButtonEnabled()
const savedPosition = this.configPresenter.getFloatingButtonPosition()
try {
this.config = {
...this.config,
...config,
enabled: floatingButtonEnabled
}
if (savedPosition) {
this.config.position = 'custom'
this.config.customPosition = savedPosition
}
if (!this.config.enabled) {
console.log('FloatingButton is disabled, skipping window creation')
return
}
await this.createFloatingWindow()
} catch (error) {
console.error('Failed to initialize FloatingButtonPresenter:', error)
throw error
}
}
/**
* 销毁悬浮按钮功能
*/
public destroy(): void {
this.config.enabled = false
ipcMain.removeAllListeners(FLOATING_BUTTON_EVENTS.CLICKED)
ipcMain.removeAllListeners(FLOATING_BUTTON_EVENTS.RIGHT_CLICKED)
if (this.floatingWindow) {
this.floatingWindow.destroy()
this.floatingWindow = null
}
}
/**
* 启用悬浮按钮
*/
public async enable(): Promise<void> {
console.log(
'FloatingButtonPresenter.enable called, current enabled:',
this.config.enabled,
'has window:',
!!this.floatingWindow
)
this.config.enabled = true
if (this.floatingWindow) {
console.log('FloatingButton window already exists, showing it')
this.floatingWindow.show()
return // 已经存在窗口,只需显示
}
console.log('Creating new floating button window')
await this.createFloatingWindow()
}
/**
* 设置悬浮按钮启用状态
*/
public async setEnabled(enabled: boolean): Promise<void> {
if (enabled) {
await this.enable()
} else {
this.destroy()
}
}
/**
* 获取当前配置
*/
public getConfig(): FloatingButtonConfig {
return { ...this.config }
}
/**
* 获取当前状态
*/
public getState(): FloatingButtonState | null {
return this.floatingWindow?.getState() || null
}
/**
* 创建悬浮窗口
*/
private async createFloatingWindow(): Promise<void> {
ipcMain.removeAllListeners(FLOATING_BUTTON_EVENTS.CLICKED)
ipcMain.removeAllListeners(FLOATING_BUTTON_EVENTS.RIGHT_CLICKED)
ipcMain.on(FLOATING_BUTTON_EVENTS.CLICKED, async () => {
try {
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)
}
})
ipcMain.on(FLOATING_BUTTON_EVENTS.RIGHT_CLICKED, () => {
try {
this.showContextMenu()
} catch (error) {
console.error('Failed to handle floating button right click:', error)
}
})
if (!this.floatingWindow) {
this.floatingWindow = new FloatingButtonWindow(this.config)
await this.floatingWindow.create()
}
// 悬浮按钮创建后立即显示
this.floatingWindow.show()
this.preCreateFloatingChatWindow()
}
private preCreateFloatingChatWindow(): void {
try {
presenter.windowPresenter.createFloatingChatWindow().catch((error) => {
console.error('Failed to pre-create floating chat window:', error)
})
console.log('Started pre-creating floating chat window in background')
} catch (error) {
console.error('Error starting pre-creation of floating chat window:', error)
}
}
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()
}
}
private openMainWindow(): void {
try {
const windowPresenter = presenter.windowPresenter
if (windowPresenter) {
const mainWindow = windowPresenter.mainWindow
if (mainWindow && !mainWindow.isDestroyed()) {
if (mainWindow.isMinimized()) {
mainWindow.restore()
}
mainWindow.show()
mainWindow.focus()
console.log('Main window opened from floating button context menu')
} else {
windowPresenter.createShellWindow({ initialTab: { url: 'local://chat' } })
console.log('Created new main window from floating button context menu')
}
}
} catch (error) {
console.error('Failed to open main window from floating button:', error)
}
}
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)
}
}
}