@@ -3,7 +3,7 @@ import { WebContents } from 'electron';
3
3
4
4
import { createLogger } from '@/utils/logger' ;
5
5
6
- import { AppBrowsersIdentifiers , appBrowsers } from '../../appBrowsers' ;
6
+ import { AppBrowsersIdentifiers , appBrowsers , WindowTemplate , WindowTemplateIdentifiers , windowTemplates } from '../../appBrowsers' ;
7
7
import type { App } from '../App' ;
8
8
import type { BrowserWindowOpts } from './Browser' ;
9
9
import Browser from './Browser' ;
@@ -14,9 +14,9 @@ const logger = createLogger('core:BrowserManager');
14
14
export class BrowserManager {
15
15
app : App ;
16
16
17
- browsers : Map < AppBrowsersIdentifiers , Browser > = new Map ( ) ;
17
+ browsers : Map < string , Browser > = new Map ( ) ;
18
18
19
- private webContentsMap = new Map < WebContents , AppBrowsersIdentifiers > ( ) ;
19
+ private webContentsMap = new Map < WebContents , string > ( ) ;
20
20
21
21
constructor ( app : App ) {
22
22
logger . debug ( 'Initializing BrowserManager' ) ;
@@ -51,12 +51,12 @@ export class BrowserManager {
51
51
} ;
52
52
53
53
broadcastToWindow = < T extends MainBroadcastEventKey > (
54
- identifier : AppBrowsersIdentifiers ,
54
+ identifier : string ,
55
55
event : T ,
56
56
data : MainBroadcastParams < T > ,
57
57
) => {
58
58
logger . debug ( `Broadcasting event ${ event } to window: ${ identifier } ` ) ;
59
- this . browsers . get ( identifier ) . broadcast ( event , data ) ;
59
+ this . browsers . get ( identifier ) ? .broadcast ( event , data ) ;
60
60
} ;
61
61
62
62
/**
@@ -87,13 +87,21 @@ export class BrowserManager {
87
87
* @param identifier Window identifier
88
88
* @param subPath Sub-path, such as 'agent', 'about', etc.
89
89
*/
90
- async redirectToPage ( identifier : AppBrowsersIdentifiers , subPath ?: string ) {
90
+ async redirectToPage ( identifier : string , subPath ?: string ) {
91
91
try {
92
92
// Ensure window is retrieved or created
93
93
const browser = this . retrieveByIdentifier ( identifier ) ;
94
94
browser . hide ( ) ;
95
95
96
- const baseRoute = appBrowsers [ identifier ] . path ;
96
+ // Handle both static and dynamic windows
97
+ let baseRoute : string ;
98
+ if ( identifier in appBrowsers ) {
99
+ baseRoute = appBrowsers [ identifier as AppBrowsersIdentifiers ] . path ;
100
+ } else {
101
+ // For dynamic windows, extract base route from the browser options
102
+ const browserOptions = browser . options ;
103
+ baseRoute = browserOptions . path ;
104
+ }
97
105
98
106
// Build complete URL path
99
107
const fullPath = subPath ? `${ baseRoute } /${ subPath } ` : baseRoute ;
@@ -114,13 +122,75 @@ export class BrowserManager {
114
122
/**
115
123
* get Browser by identifier
116
124
*/
117
- retrieveByIdentifier ( identifier : AppBrowsersIdentifiers ) {
125
+ retrieveByIdentifier ( identifier : string ) {
118
126
const browser = this . browsers . get ( identifier ) ;
119
127
120
128
if ( browser ) return browser ;
121
129
122
- logger . debug ( `Browser ${ identifier } not found, initializing new instance` ) ;
123
- return this . retrieveOrInitialize ( appBrowsers [ identifier ] ) ;
130
+ // Check if it's a static browser
131
+ if ( identifier in appBrowsers ) {
132
+ logger . debug ( `Browser ${ identifier } not found, initializing new instance` ) ;
133
+ return this . retrieveOrInitialize ( appBrowsers [ identifier as AppBrowsersIdentifiers ] ) ;
134
+ }
135
+
136
+ throw new Error ( `Browser ${ identifier } not found and is not a static browser` ) ;
137
+ }
138
+
139
+ /**
140
+ * Create a multi-instance window from template
141
+ * @param templateId Template identifier
142
+ * @param path Full path with query parameters
143
+ * @param uniqueId Optional unique identifier, will be generated if not provided
144
+ * @returns The window identifier and Browser instance
145
+ */
146
+ createMultiInstanceWindow ( templateId : WindowTemplateIdentifiers , path : string , uniqueId ?: string ) {
147
+ const template = windowTemplates [ templateId ] ;
148
+ if ( ! template ) {
149
+ throw new Error ( `Window template ${ templateId } not found` ) ;
150
+ }
151
+
152
+ // Generate unique identifier
153
+ const windowId = uniqueId || `${ template . baseIdentifier } _${ Date . now ( ) } _${ Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) } ` ;
154
+
155
+ // Create browser options from template
156
+ const browserOpts : BrowserWindowOpts = {
157
+ ...template ,
158
+ identifier : windowId ,
159
+ path : path ,
160
+ } ;
161
+
162
+ logger . debug ( `Creating multi-instance window: ${ windowId } with path: ${ path } ` ) ;
163
+
164
+ const browser = this . retrieveOrInitialize ( browserOpts ) ;
165
+
166
+ return {
167
+ identifier : windowId ,
168
+ browser : browser ,
169
+ } ;
170
+ }
171
+
172
+ /**
173
+ * Get all windows based on template
174
+ * @param templateId Template identifier
175
+ * @returns Array of window identifiers matching the template
176
+ */
177
+ getWindowsByTemplate ( templateId : string ) : string [ ] {
178
+ const prefix = `${ templateId } _` ;
179
+ return Array . from ( this . browsers . keys ( ) ) . filter ( id => id . startsWith ( prefix ) ) ;
180
+ }
181
+
182
+ /**
183
+ * Close all windows based on template
184
+ * @param templateId Template identifier
185
+ */
186
+ closeWindowsByTemplate ( templateId : string ) : void {
187
+ const windowIds = this . getWindowsByTemplate ( templateId ) ;
188
+ windowIds . forEach ( id => {
189
+ const browser = this . browsers . get ( id ) ;
190
+ if ( browser ) {
191
+ browser . close ( ) ;
192
+ }
193
+ } ) ;
124
194
}
125
195
126
196
/**
@@ -144,7 +214,7 @@ export class BrowserManager {
144
214
* @param options Browser window options
145
215
*/
146
216
private retrieveOrInitialize ( options : BrowserWindowOpts ) {
147
- let browser = this . browsers . get ( options . identifier as AppBrowsersIdentifiers ) ;
217
+ let browser = this . browsers . get ( options . identifier ) ;
148
218
if ( browser ) {
149
219
logger . debug ( `Retrieved existing browser: ${ options . identifier } ` ) ;
150
220
return browser ;
@@ -153,7 +223,7 @@ export class BrowserManager {
153
223
logger . debug ( `Creating new browser: ${ options . identifier } ` ) ;
154
224
browser = new Browser ( options , this . app ) ;
155
225
156
- const identifier = options . identifier as AppBrowsersIdentifiers ;
226
+ const identifier = options . identifier ;
157
227
this . browsers . set ( identifier , browser ) ;
158
228
159
229
// 记录 WebContents 和 identifier 的映射
@@ -166,32 +236,32 @@ export class BrowserManager {
166
236
167
237
browser . browserWindow . on ( 'show' , ( ) => {
168
238
if ( browser . webContents )
169
- this . webContentsMap . set ( browser . webContents , browser . identifier as AppBrowsersIdentifiers ) ;
239
+ this . webContentsMap . set ( browser . webContents , browser . identifier ) ;
170
240
} ) ;
171
241
172
242
return browser ;
173
243
}
174
244
175
245
closeWindow ( identifier : string ) {
176
- const browser = this . browsers . get ( identifier as AppBrowsersIdentifiers ) ;
246
+ const browser = this . browsers . get ( identifier ) ;
177
247
browser ?. close ( ) ;
178
248
}
179
249
180
250
minimizeWindow ( identifier : string ) {
181
- const browser = this . browsers . get ( identifier as AppBrowsersIdentifiers ) ;
251
+ const browser = this . browsers . get ( identifier ) ;
182
252
browser ?. browserWindow . minimize ( ) ;
183
253
}
184
254
185
255
maximizeWindow ( identifier : string ) {
186
- const browser = this . browsers . get ( identifier as AppBrowsersIdentifiers ) ;
187
- if ( browser . browserWindow . isMaximized ( ) ) {
256
+ const browser = this . browsers . get ( identifier ) ;
257
+ if ( browser ? .browserWindow . isMaximized ( ) ) {
188
258
browser ?. browserWindow . unmaximize ( ) ;
189
259
} else {
190
260
browser ?. browserWindow . maximize ( ) ;
191
261
}
192
262
}
193
263
194
- getIdentifierByWebContents ( webContents : WebContents ) : AppBrowsersIdentifiers | null {
264
+ getIdentifierByWebContents ( webContents : WebContents ) : string | null {
195
265
return this . webContentsMap . get ( webContents ) || null ;
196
266
}
197
267
0 commit comments