@becrafter/prompt-manager 0.1.17 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/env.example +1 -1
  2. package/package.json +19 -7
  3. package/packages/server/server.js +2 -1
  4. package/packages/server/services/TerminalService.js +247 -13
  5. package/packages/server/toolm/tool-sync.service.js +8 -0
  6. package/packages/server/utils/config.js +1 -1
  7. package/packages/web/css/{main.3b61356b384d2f11f47f.css → main.196f434e6a88cd448158.css} +10 -0
  8. package/packages/web/index.html +1 -1
  9. package/packages/web/{main.77c2c4b553ca3fac223b.js → main.b427a9e6f77a32a2f87f.js} +2 -2
  10. package/app/desktop/assets/app.1.png +0 -0
  11. package/app/desktop/assets/app.png +0 -0
  12. package/app/desktop/assets/icons/icon.icns +0 -0
  13. package/app/desktop/assets/icons/icon.ico +0 -0
  14. package/app/desktop/assets/icons/icon.png +0 -0
  15. package/app/desktop/assets/icons/tray.png +0 -0
  16. package/app/desktop/assets/templates/about.html +0 -147
  17. package/app/desktop/assets/tray.1.png +0 -0
  18. package/app/desktop/assets/tray.png +0 -0
  19. package/app/desktop/docs/ASSETS_PLANNING.md +0 -351
  20. package/app/desktop/docs/REFACTORING_SUMMARY.md +0 -205
  21. package/app/desktop/main.js +0 -340
  22. package/app/desktop/package-lock.json +0 -6912
  23. package/app/desktop/package.json +0 -119
  24. package/app/desktop/preload.js +0 -7
  25. package/app/desktop/src/core/error-handler.js +0 -108
  26. package/app/desktop/src/core/event-emitter.js +0 -84
  27. package/app/desktop/src/core/logger.js +0 -130
  28. package/app/desktop/src/core/state-manager.js +0 -125
  29. package/app/desktop/src/services/module-loader.js +0 -330
  30. package/app/desktop/src/services/runtime-manager.js +0 -398
  31. package/app/desktop/src/services/service-manager.js +0 -210
  32. package/app/desktop/src/services/update-manager.js +0 -267
  33. package/app/desktop/src/ui/about-dialog-manager.js +0 -208
  34. package/app/desktop/src/ui/admin-window-manager.js +0 -757
  35. package/app/desktop/src/ui/splash-manager.js +0 -253
  36. package/app/desktop/src/ui/tray-manager.js +0 -186
  37. package/app/desktop/src/utils/icon-manager.js +0 -133
  38. package/app/desktop/src/utils/path-utils.js +0 -58
  39. package/app/desktop/src/utils/resource-paths.js +0 -49
  40. package/app/desktop/src/utils/resource-sync.js +0 -260
  41. package/app/desktop/src/utils/runtime-sync.js +0 -241
  42. package/app/desktop/src/utils/self-check.js +0 -288
  43. package/app/desktop/src/utils/template-renderer.js +0 -284
  44. package/app/desktop/src/utils/version-utils.js +0 -59
  45. package/packages/server/.eslintrc.js +0 -70
  46. package/packages/server/.husky/pre-commit +0 -8
  47. package/packages/server/.husky/pre-push +0 -8
  48. package/packages/server/.prettierrc +0 -14
  49. package/packages/server/dev-server.js +0 -90
  50. package/packages/server/jsdoc.conf.json +0 -39
  51. package/packages/server/package.json +0 -85
  52. package/packages/server/playwright.config.js +0 -62
  53. package/packages/server/scripts/generate-docs.js +0 -300
  54. package/packages/server/tests/e2e/terminal-e2e.test.js +0 -315
  55. package/packages/server/tests/integration/terminal-websocket.test.js +0 -372
  56. package/packages/server/tests/integration/tools.test.js +0 -264
  57. package/packages/server/tests/setup.js +0 -45
  58. package/packages/server/tests/unit/TerminalService.test.js +0 -410
  59. package/packages/server/tests/unit/WebSocketService.test.js +0 -403
  60. package/packages/server/tests/unit/core.test.js +0 -94
  61. package/packages/server/typedoc.json +0 -52
  62. package/packages/server/vitest.config.js +0 -74
  63. /package/packages/web/{main.77c2c4b553ca3fac223b.js.LICENSE.txt → main.b427a9e6f77a32a2f87f.js.LICENSE.txt} +0 -0
@@ -1,757 +0,0 @@
1
- const { BrowserWindow, nativeImage } = require('electron');
2
- const path = require('path');
3
-
4
- /**
5
- * 管理后台窗口管理器
6
- * 负责创建和管理管理后台窗口,提供加载效果
7
- */
8
- class AdminWindowManager {
9
- constructor(logger, iconManager) {
10
- this.logger = logger;
11
- this.iconManager = iconManager;
12
- this.adminWindow = null;
13
- }
14
-
15
- /**
16
- * 打开管理后台窗口
17
- * @param {string} adminUrl - 管理后台URL
18
- */
19
- openAdminWindow(adminUrl) {
20
- if (this.adminWindow && !this.adminWindow.isDestroyed()) {
21
- this.adminWindow.focus();
22
- return;
23
- }
24
-
25
- try {
26
- this.logger.info('Opening admin window', { url: adminUrl });
27
-
28
- // 创建窗口
29
- this.adminWindow = this.createAdminWindow();
30
-
31
- // 先显示加载页面(随机选择一种动画效果)
32
- const animationTypes = ['spinner', 'dots', 'wave', 'pulse', 'bars', 'gradient', 'orbit', 'ripple'];
33
- const randomAnimation = animationTypes[Math.floor(Math.random() * animationTypes.length)];
34
- this.showLoadingPage(randomAnimation);
35
-
36
- // 设置窗口事件
37
- this.setupWindowEvents(adminUrl);
38
-
39
- this.logger.info('Admin window created successfully');
40
- } catch (error) {
41
- this.logger.error('Failed to open admin window', error);
42
- }
43
- }
44
-
45
- /**
46
- * 创建管理后台窗口
47
- */
48
- createAdminWindow() {
49
- const window = new BrowserWindow({
50
- width: 1200,
51
- height: 800,
52
- minWidth: 800,
53
- minHeight: 600,
54
- title: 'Prompt Server 管理后台',
55
- show: false, // 先不显示,等加载完成后再显示
56
- backgroundColor: '#ffffff',
57
- webPreferences: {
58
- contextIsolation: true,
59
- nodeIntegration: false,
60
- sandbox: true
61
- }
62
- });
63
-
64
- // 设置窗口图标(如果可用)
65
- try {
66
- const icon = this.iconManager.getAppIcon();
67
- if (icon && !icon.isEmpty()) {
68
- window.setIcon(icon);
69
- }
70
- } catch (error) {
71
- this.logger.warn('Failed to set window icon', error);
72
- }
73
-
74
- return window;
75
- }
76
-
77
- /**
78
- * 显示加载页面
79
- * @param {string} animationType - 动画类型
80
- */
81
- showLoadingPage(animationType = 'spinner') {
82
- if (!this.adminWindow || this.adminWindow.isDestroyed()) {
83
- return;
84
- }
85
-
86
- const htmlContent = this.generateLoadingHTML(animationType);
87
- this.adminWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`);
88
-
89
- // 加载页面后显示窗口
90
- this.adminWindow.once('ready-to-show', () => {
91
- if (this.adminWindow && !this.adminWindow.isDestroyed()) {
92
- this.adminWindow.show();
93
- this.logger.info('Admin window loading page shown', { animationType });
94
- }
95
- });
96
- }
97
-
98
- /**
99
- * 生成加载页面HTML
100
- * @param {string} animationType - 动画类型: 'spinner' | 'dots' | 'wave' | 'pulse' | 'skeleton' | 'bars' | 'gradient' | 'orbit' | 'ripple'
101
- */
102
- generateLoadingHTML(animationType = 'spinner') {
103
- const baseStyles = this.getBaseStyles();
104
- const animationStyles = this.getAnimationStyles(animationType);
105
- const animationHTML = this.getAnimationHTML(animationType);
106
-
107
- return `<!DOCTYPE html>
108
- <html>
109
- <head>
110
- <meta charset="UTF-8">
111
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
112
- <title>正在加载管理后台...</title>
113
- <style>
114
- ${baseStyles}
115
- ${animationStyles}
116
- </style>
117
- </head>
118
- <body>
119
- <div class="loading-container">
120
- <div class="loading-text">正在加载管理后台...</div>
121
- ${animationHTML}
122
- </div>
123
-
124
- <script>
125
- // 模拟进度更新(如果动画类型支持)
126
- const progressBar = document.querySelector('.progress-bar');
127
- if (progressBar) {
128
- let progress = 0;
129
- const progressFill = document.querySelector('.progress-fill');
130
- const interval = setInterval(() => {
131
- progress += Math.random() * 10;
132
- if (progress > 90) {
133
- progress = 90;
134
- }
135
- if (progressFill) {
136
- progressFill.style.width = progress + '%';
137
- }
138
- }, 200);
139
- window.addEventListener('beforeunload', () => {
140
- clearInterval(interval);
141
- });
142
- }
143
- </script>
144
- </body>
145
- </html>`;
146
- }
147
-
148
- /**
149
- * 获取基础样式
150
- */
151
- getBaseStyles() {
152
- return `
153
- * {
154
- margin: 0;
155
- padding: 0;
156
- box-sizing: border-box;
157
- }
158
-
159
- body {
160
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
161
- background: #ffffff;
162
- display: flex;
163
- align-items: center;
164
- justify-content: center;
165
- height: 100vh;
166
- overflow: hidden;
167
- color: rgb(134, 134, 134);
168
- }
169
-
170
- .loading-container {
171
- text-align: center;
172
- padding: 60px 40px;
173
- background: #ffffff;
174
- border-radius: 16px;
175
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
176
- border: 1px solid rgb(200, 200, 200);
177
- max-width: 500px;
178
- width: 90%;
179
- }
180
-
181
- .loading-text {
182
- font-size: 14px;
183
- margin-bottom: 48px;
184
- color: rgb(134, 134, 134);
185
- font-weight: 400;
186
- letter-spacing: 0.5px;
187
- }
188
-
189
- .animation-wrapper {
190
- margin: 40px 0;
191
- display: flex;
192
- justify-content: center;
193
- align-items: center;
194
- }`;
195
- }
196
-
197
- /**
198
- * 获取动画样式
199
- */
200
- getAnimationStyles(animationType) {
201
- const styles = {
202
- spinner: `
203
- .spinner {
204
- width: 80px;
205
- height: 80px;
206
- margin: 0 auto;
207
- position: relative;
208
- }
209
-
210
- .spinner-ring {
211
- width: 100%;
212
- height: 100%;
213
- border: 5px solid rgba(200, 200, 200, 0.2);
214
- border-top-color: rgb(134, 134, 134);
215
- border-radius: 50%;
216
- animation: spin 1s linear infinite;
217
- }
218
-
219
- @keyframes spin {
220
- to { transform: rotate(360deg); }
221
- }`,
222
-
223
- dots: `
224
- .dots {
225
- display: flex;
226
- justify-content: center;
227
- gap: 12px;
228
- margin: 40px 0;
229
- }
230
-
231
- .dot {
232
- width: 16px;
233
- height: 16px;
234
- background: rgb(134, 134, 134);
235
- border-radius: 50%;
236
- animation: dotPulse 1.4s infinite ease-in-out;
237
- }
238
-
239
- .dot:nth-child(1) { animation-delay: -0.32s; }
240
- .dot:nth-child(2) { animation-delay: -0.16s; }
241
-
242
- @keyframes dotPulse {
243
- 0%, 80%, 100% {
244
- transform: scale(0.8);
245
- opacity: 0.5;
246
- }
247
- 40% {
248
- transform: scale(1.2);
249
- opacity: 1;
250
- }
251
- }`,
252
-
253
- wave: `
254
- .wave {
255
- display: flex;
256
- justify-content: center;
257
- align-items: center;
258
- gap: 6px;
259
- margin: 40px 0;
260
- height: 80px;
261
- }
262
-
263
- .wave-bar {
264
- width: 6px;
265
- height: 60px;
266
- background: rgb(134, 134, 134);
267
- border-radius: 3px;
268
- animation: wave 1.2s infinite ease-in-out;
269
- }
270
-
271
- .wave-bar:nth-child(1) { animation-delay: -0.4s; }
272
- .wave-bar:nth-child(2) { animation-delay: -0.3s; }
273
- .wave-bar:nth-child(3) { animation-delay: -0.2s; }
274
- .wave-bar:nth-child(4) { animation-delay: -0.1s; }
275
- .wave-bar:nth-child(5) { animation-delay: 0s; }
276
-
277
- @keyframes wave {
278
- 0%, 40%, 100% {
279
- transform: scaleY(0.4);
280
- opacity: 0.5;
281
- }
282
- 20% {
283
- transform: scaleY(1);
284
- opacity: 1;
285
- }
286
- }`,
287
-
288
- pulse: `
289
- .pulse {
290
- width: 100px;
291
- height: 100px;
292
- margin: 0 auto;
293
- position: relative;
294
- }
295
-
296
- .pulse-circle {
297
- width: 100%;
298
- height: 100%;
299
- border: 4px solid rgb(134, 134, 134);
300
- border-radius: 50%;
301
- position: absolute;
302
- animation: pulseRing 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
303
- }
304
-
305
- .pulse-circle:nth-child(2) {
306
- animation-delay: 0.5s;
307
- }
308
-
309
- .pulse-circle:nth-child(3) {
310
- animation-delay: 1s;
311
- }
312
-
313
- @keyframes pulseRing {
314
- 0% {
315
- transform: scale(0.8);
316
- opacity: 1;
317
- }
318
- 100% {
319
- transform: scale(1.8);
320
- opacity: 0;
321
- }
322
- }`,
323
-
324
- bars: `
325
- .bars {
326
- display: flex;
327
- justify-content: center;
328
- align-items: flex-end;
329
- gap: 5px;
330
- margin: 40px 0;
331
- height: 80px;
332
- }
333
-
334
- .bar {
335
- width: 8px;
336
- background: rgb(134, 134, 134);
337
- border-radius: 4px;
338
- animation: barBounce 1.4s infinite ease-in-out;
339
- }
340
-
341
- .bar:nth-child(1) { height: 30px; animation-delay: -0.32s; }
342
- .bar:nth-child(2) { height: 55px; animation-delay: -0.16s; }
343
- .bar:nth-child(3) { height: 80px; animation-delay: 0s; }
344
- .bar:nth-child(4) { height: 55px; animation-delay: 0.16s; }
345
- .bar:nth-child(5) { height: 30px; animation-delay: 0.32s; }
346
-
347
- @keyframes barBounce {
348
- 0%, 80%, 100% {
349
- transform: scaleY(0.5);
350
- opacity: 0.7;
351
- }
352
- 40% {
353
- transform: scaleY(1);
354
- opacity: 1;
355
- }
356
- }`,
357
-
358
- gradient: `
359
- .gradient-loader {
360
- width: 100px;
361
- height: 100px;
362
- margin: 0 auto;
363
- border-radius: 50%;
364
- background: linear-gradient(45deg,
365
- rgb(134, 134, 134) 0%,
366
- rgba(134, 134, 134, 0.3) 50%,
367
- rgb(134, 134, 134) 100%);
368
- background-size: 200% 200%;
369
- animation: gradientMove 2s ease infinite;
370
- }
371
-
372
- @keyframes gradientMove {
373
- 0% {
374
- background-position: 0% 50%;
375
- }
376
- 50% {
377
- background-position: 100% 50%;
378
- }
379
- 100% {
380
- background-position: 0% 50%;
381
- }
382
- }`,
383
-
384
- orbit: `
385
- .orbit {
386
- width: 100px;
387
- height: 100px;
388
- margin: 0 auto;
389
- position: relative;
390
- }
391
-
392
- .orbit-dot {
393
- width: 16px;
394
- height: 16px;
395
- background: rgb(134, 134, 134);
396
- border-radius: 50%;
397
- position: absolute;
398
- top: 0;
399
- left: 50%;
400
- transform: translateX(-50%);
401
- animation: orbitRotate 1.5s linear infinite;
402
- }
403
-
404
- .orbit-dot:nth-child(2) {
405
- animation-delay: 0.5s;
406
- }
407
-
408
- .orbit-dot:nth-child(3) {
409
- animation-delay: 1s;
410
- }
411
-
412
- @keyframes orbitRotate {
413
- 0% {
414
- transform: translateX(-50%) rotate(0deg) translateY(0);
415
- }
416
- 100% {
417
- transform: translateX(-50%) rotate(360deg) translateY(0);
418
- }
419
- }`,
420
-
421
- ripple: `
422
- .ripple {
423
- width: 100px;
424
- height: 100px;
425
- margin: 0 auto;
426
- position: relative;
427
- }
428
-
429
- .ripple-circle {
430
- width: 100%;
431
- height: 100%;
432
- border: 3px solid rgb(134, 134, 134);
433
- border-radius: 50%;
434
- position: absolute;
435
- animation: rippleExpand 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
436
- }
437
-
438
- .ripple-circle:nth-child(2) {
439
- animation-delay: 0.5s;
440
- }
441
-
442
- .ripple-circle:nth-child(3) {
443
- animation-delay: 1s;
444
- }
445
-
446
- @keyframes rippleExpand {
447
- 0% {
448
- transform: scale(0.8);
449
- opacity: 1;
450
- }
451
- 100% {
452
- transform: scale(1.8);
453
- opacity: 0;
454
- }
455
- }`
456
- };
457
-
458
- // 添加进度条样式(所有动画共用)
459
- const progressBarStyle = `
460
- .progress-bar {
461
- width: 100%;
462
- height: 3px;
463
- background: rgba(200, 200, 200, 0.3);
464
- border-radius: 2px;
465
- margin-top: 48px;
466
- overflow: hidden;
467
- }
468
-
469
- .progress-fill {
470
- height: 100%;
471
- background: rgb(134, 134, 134);
472
- border-radius: 2px;
473
- animation: progress 2s ease-in-out infinite;
474
- width: 60%;
475
- }
476
-
477
- @keyframes progress {
478
- 0% { transform: translateX(-100%); }
479
- 50% { transform: translateX(0%); }
480
- 100% { transform: translateX(100%); }
481
- }`;
482
-
483
- return (styles[animationType] || styles.spinner) + progressBarStyle;
484
- }
485
-
486
- /**
487
- * 获取动画HTML
488
- */
489
- getAnimationHTML(animationType) {
490
- const htmls = {
491
- spinner: `
492
- <div class="animation-wrapper">
493
- <div class="spinner">
494
- <div class="spinner-ring"></div>
495
- </div>
496
- </div>
497
- <div class="progress-bar">
498
- <div class="progress-fill"></div>
499
- </div>`,
500
-
501
- dots: `
502
- <div class="dots">
503
- <div class="dot"></div>
504
- <div class="dot"></div>
505
- <div class="dot"></div>
506
- </div>
507
- <div class="progress-bar">
508
- <div class="progress-fill"></div>
509
- </div>`,
510
-
511
- wave: `
512
- <div class="wave">
513
- <div class="wave-bar"></div>
514
- <div class="wave-bar"></div>
515
- <div class="wave-bar"></div>
516
- <div class="wave-bar"></div>
517
- <div class="wave-bar"></div>
518
- </div>
519
- <div class="progress-bar">
520
- <div class="progress-fill"></div>
521
- </div>`,
522
-
523
- pulse: `
524
- <div class="animation-wrapper">
525
- <div class="pulse">
526
- <div class="pulse-circle"></div>
527
- <div class="pulse-circle"></div>
528
- <div class="pulse-circle"></div>
529
- </div>
530
- </div>
531
- <div class="progress-bar">
532
- <div class="progress-fill"></div>
533
- </div>`,
534
-
535
- bars: `
536
- <div class="bars">
537
- <div class="bar"></div>
538
- <div class="bar"></div>
539
- <div class="bar"></div>
540
- <div class="bar"></div>
541
- <div class="bar"></div>
542
- </div>
543
- <div class="progress-bar">
544
- <div class="progress-fill"></div>
545
- </div>`,
546
-
547
- gradient: `
548
- <div class="animation-wrapper">
549
- <div class="gradient-loader"></div>
550
- </div>
551
- <div class="progress-bar">
552
- <div class="progress-fill"></div>
553
- </div>`,
554
-
555
- orbit: `
556
- <div class="animation-wrapper">
557
- <div class="orbit">
558
- <div class="orbit-dot"></div>
559
- <div class="orbit-dot"></div>
560
- <div class="orbit-dot"></div>
561
- </div>
562
- </div>
563
- <div class="progress-bar">
564
- <div class="progress-fill"></div>
565
- </div>`,
566
-
567
- ripple: `
568
- <div class="animation-wrapper">
569
- <div class="ripple">
570
- <div class="ripple-circle"></div>
571
- <div class="ripple-circle"></div>
572
- <div class="ripple-circle"></div>
573
- </div>
574
- </div>
575
- <div class="progress-bar">
576
- <div class="progress-fill"></div>
577
- </div>`
578
- };
579
-
580
- return htmls[animationType] || htmls.spinner;
581
- }
582
-
583
- /**
584
- * 设置窗口事件
585
- * @param {string} adminUrl - 管理后台URL
586
- */
587
- setupWindowEvents(adminUrl) {
588
- if (!this.adminWindow) return;
589
-
590
- // 监听页面开始加载
591
- this.adminWindow.webContents.on('did-start-loading', () => {
592
- this.logger.debug('Admin page started loading');
593
- });
594
-
595
- // 监听页面加载完成
596
- this.adminWindow.webContents.on('did-finish-load', () => {
597
- this.logger.info('Admin page finished loading');
598
- // 页面加载完成后,窗口会自动显示实际内容
599
- });
600
-
601
- // 监听页面加载失败
602
- this.adminWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL) => {
603
- this.logger.error('Admin page failed to load', {
604
- errorCode,
605
- errorDescription,
606
- url: validatedURL
607
- });
608
-
609
- // 显示错误页面
610
- this.showErrorPage(errorDescription, validatedURL);
611
- });
612
-
613
- // 监听窗口关闭
614
- this.adminWindow.on('closed', () => {
615
- this.adminWindow = null;
616
- this.logger.info('Admin window closed');
617
- });
618
-
619
- // 延迟加载实际URL,给加载页面一些显示时间
620
- setTimeout(() => {
621
- if (this.adminWindow && !this.adminWindow.isDestroyed()) {
622
- this.logger.info('Loading admin URL', { url: adminUrl });
623
- this.adminWindow.loadURL(adminUrl);
624
- }
625
- }, 500); // 500ms延迟,让用户看到加载动画
626
- }
627
-
628
- /**
629
- * 显示错误页面
630
- * @param {string} errorDescription - 错误描述
631
- * @param {string} url - 失败的URL
632
- */
633
- showErrorPage(errorDescription, url) {
634
- if (!this.adminWindow || this.adminWindow.isDestroyed()) {
635
- return;
636
- }
637
-
638
- const htmlContent = `<!DOCTYPE html>
639
- <html>
640
- <head>
641
- <meta charset="UTF-8">
642
- <title>加载失败</title>
643
- <style>
644
- * {
645
- margin: 0;
646
- padding: 0;
647
- box-sizing: border-box;
648
- }
649
-
650
- body {
651
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
652
- background: #f5f5f5;
653
- display: flex;
654
- align-items: center;
655
- justify-content: center;
656
- height: 100vh;
657
- color: #333;
658
- }
659
-
660
- .error-container {
661
- text-align: center;
662
- padding: 40px;
663
- background: white;
664
- border-radius: 12px;
665
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
666
- max-width: 500px;
667
- width: 90%;
668
- }
669
-
670
- .error-icon {
671
- font-size: 64px;
672
- margin-bottom: 24px;
673
- }
674
-
675
- .error-title {
676
- font-size: 24px;
677
- font-weight: 600;
678
- margin-bottom: 16px;
679
- color: #e74c3c;
680
- }
681
-
682
- .error-message {
683
- font-size: 14px;
684
- color: #666;
685
- margin-bottom: 8px;
686
- line-height: 1.6;
687
- }
688
-
689
- .error-url {
690
- font-size: 12px;
691
- color: #999;
692
- word-break: break-all;
693
- margin-top: 16px;
694
- padding: 12px;
695
- background: #f9f9f9;
696
- border-radius: 6px;
697
- }
698
-
699
- .retry-button {
700
- margin-top: 24px;
701
- padding: 12px 24px;
702
- background: #667eea;
703
- color: white;
704
- border: none;
705
- border-radius: 6px;
706
- font-size: 14px;
707
- cursor: pointer;
708
- transition: background 0.2s;
709
- }
710
-
711
- .retry-button:hover {
712
- background: #5568d3;
713
- }
714
- </style>
715
- </head>
716
- <body>
717
- <div class="error-container">
718
- <div class="error-icon">⚠️</div>
719
- <div class="error-title">无法加载管理后台</div>
720
- <div class="error-message">${errorDescription || '页面加载失败'}</div>
721
- <div class="error-url">${url || ''}</div>
722
- <button class="retry-button" onclick="window.location.reload()">重试</button>
723
- </div>
724
- </body>
725
- </html>`;
726
-
727
- this.adminWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`);
728
- }
729
-
730
- /**
731
- * 关闭管理后台窗口
732
- */
733
- closeAdminWindow() {
734
- if (this.adminWindow && !this.adminWindow.isDestroyed()) {
735
- this.adminWindow.close();
736
- this.adminWindow = null;
737
- this.logger.info('Admin window closed');
738
- }
739
- }
740
-
741
- /**
742
- * 检查窗口是否存在
743
- */
744
- hasWindow() {
745
- return this.adminWindow && !this.adminWindow.isDestroyed();
746
- }
747
-
748
- /**
749
- * 获取窗口实例
750
- */
751
- getWindow() {
752
- return this.adminWindow;
753
- }
754
- }
755
-
756
- module.exports = AdminWindowManager;
757
-