@becrafter/prompt-manager 0.1.12 → 0.1.15

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 (139) hide show
  1. package/app/desktop/assets/app.1.png +0 -0
  2. package/app/desktop/assets/app.png +0 -0
  3. package/app/desktop/assets/icons/icon.icns +0 -0
  4. package/app/desktop/assets/icons/icon.ico +0 -0
  5. package/app/desktop/assets/icons/icon.png +0 -0
  6. package/app/desktop/assets/icons/tray.png +0 -0
  7. package/app/desktop/assets/templates/about.html +147 -0
  8. package/app/desktop/assets/tray.1.png +0 -0
  9. package/app/desktop/assets/tray.png +0 -0
  10. package/app/desktop/docs/ASSETS_PLANNING.md +351 -0
  11. package/app/desktop/docs/REFACTORING_SUMMARY.md +205 -0
  12. package/app/desktop/main.js +340 -0
  13. package/app/desktop/package-lock.json +6912 -0
  14. package/app/desktop/package.json +119 -0
  15. package/app/desktop/preload.js +7 -0
  16. package/app/desktop/src/core/error-handler.js +108 -0
  17. package/app/desktop/src/core/event-emitter.js +84 -0
  18. package/app/desktop/src/core/logger.js +130 -0
  19. package/app/desktop/src/core/state-manager.js +125 -0
  20. package/app/desktop/src/services/module-loader.js +330 -0
  21. package/app/desktop/src/services/runtime-manager.js +398 -0
  22. package/app/desktop/src/services/service-manager.js +210 -0
  23. package/app/desktop/src/services/update-manager.js +267 -0
  24. package/app/desktop/src/ui/about-dialog-manager.js +208 -0
  25. package/app/desktop/src/ui/admin-window-manager.js +757 -0
  26. package/app/desktop/src/ui/splash-manager.js +253 -0
  27. package/app/desktop/src/ui/tray-manager.js +186 -0
  28. package/app/desktop/src/utils/icon-manager.js +133 -0
  29. package/app/desktop/src/utils/path-utils.js +58 -0
  30. package/app/desktop/src/utils/resource-paths.js +49 -0
  31. package/app/desktop/src/utils/resource-sync.js +260 -0
  32. package/app/desktop/src/utils/runtime-sync.js +241 -0
  33. package/app/desktop/src/utils/self-check.js +288 -0
  34. package/app/desktop/src/utils/template-renderer.js +284 -0
  35. package/app/desktop/src/utils/version-utils.js +59 -0
  36. package/env.example +1 -1
  37. package/examples/prompts/developer/code-review.yaml +32 -0
  38. package/examples/prompts/developer/code_refactoring.yaml +31 -0
  39. package/examples/prompts/developer/doc-generator.yaml +36 -0
  40. package/examples/prompts/developer/error-code-fixer.yaml +35 -0
  41. package/examples/prompts/engineer/engineer-professional.yaml +92 -0
  42. package/examples/prompts/engineer/laowang-engineer.yaml +132 -0
  43. package/examples/prompts/engineer/nekomata-engineer.yaml +123 -0
  44. package/examples/prompts/engineer/ojousama-engineer.yaml +124 -0
  45. package/examples/prompts/generator/gen_3d_edu_webpage_html.yaml +117 -0
  46. package/examples/prompts/generator/gen_3d_webpage_html.yaml +75 -0
  47. package/examples/prompts/generator/gen_bento_grid_html.yaml +112 -0
  48. package/examples/prompts/generator/gen_html_web_page.yaml +88 -0
  49. package/examples/prompts/generator/gen_knowledge_card_html.yaml +83 -0
  50. package/examples/prompts/generator/gen_magazine_card_html.yaml +82 -0
  51. package/examples/prompts/generator/gen_mimeng_headline_title.yaml +71 -0
  52. package/examples/prompts/generator/gen_podcast_script.yaml +69 -0
  53. package/examples/prompts/generator/gen_prd_prototype_html.yaml +175 -0
  54. package/examples/prompts/generator/gen_summarize.yaml +157 -0
  55. package/examples/prompts/generator/gen_title.yaml +119 -0
  56. package/examples/prompts/generator/others/api_documentation.yaml +32 -0
  57. package/examples/prompts/generator/others/build_mcp_server.yaml +26 -0
  58. package/examples/prompts/generator/others/project_architecture.yaml +31 -0
  59. package/examples/prompts/generator/others/test_case_generator.yaml +30 -0
  60. package/examples/prompts/generator/others/writing_assistant.yaml +72 -0
  61. package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +105 -0
  62. package/examples/prompts/workflow/sixstep-workflow.yaml +192 -0
  63. package/package.json +13 -1
  64. package/packages/server/.eslintrc.js +70 -0
  65. package/packages/server/.husky/pre-commit +8 -0
  66. package/packages/server/.husky/pre-push +8 -0
  67. package/packages/server/.prettierrc +14 -0
  68. package/packages/server/dev-server.js +90 -0
  69. package/packages/server/jsdoc.conf.json +39 -0
  70. package/packages/server/playwright.config.js +62 -0
  71. package/packages/server/scripts/generate-docs.js +300 -0
  72. package/packages/server/server.js +1 -0
  73. package/packages/server/services/TerminalService.js +84 -11
  74. package/packages/server/tests/e2e/terminal-e2e.test.js +315 -0
  75. package/packages/server/tests/integration/terminal-websocket.test.js +372 -0
  76. package/packages/server/tests/integration/tools.test.js +264 -0
  77. package/packages/server/tests/setup.js +45 -0
  78. package/packages/server/tests/unit/TerminalService.test.js +410 -0
  79. package/packages/server/tests/unit/WebSocketService.test.js +403 -0
  80. package/packages/server/tests/unit/core.test.js +94 -0
  81. package/packages/server/typedoc.json +52 -0
  82. package/packages/server/utils/config.js +1 -1
  83. package/packages/server/utils/util.js +59 -5
  84. package/packages/server/vitest.config.js +74 -0
  85. package/packages/web/0.d1c5a72339dfc32ad86a.js +1 -0
  86. package/packages/web/112.8807b976372b2b0541a8.js +1 -0
  87. package/packages/web/130.584c7e365da413f5d9be.js +1 -0
  88. package/packages/web/142.72c985bc29720f975cca.js +1 -0
  89. package/packages/web/165.a05fc53bf84d18db36b8.js +2 -0
  90. package/packages/web/165.a05fc53bf84d18db36b8.js.LICENSE.txt +9 -0
  91. package/packages/web/203.724ab9f717b80554c397.js +1 -0
  92. package/packages/web/241.bf941d4f02866795f64a.js +1 -0
  93. package/packages/web/249.54cfb224af63f5f5ec55.js +1 -0
  94. package/packages/web/291.6df35042f8f296fca7cd.js +1 -0
  95. package/packages/web/319.2fab900a31b29873f666.js +1 -0
  96. package/packages/web/32.c78d866281995ec33a7b.js +1 -0
  97. package/packages/web/325.9ca297d0f73f38468ce9.js +1 -0
  98. package/packages/web/366.2f9b48fdbf8eee039e57.js +1 -0
  99. package/packages/web/378.6be08c612cd5a3ef97dc.js +1 -0
  100. package/packages/web/393.7a2f817515c5e90623d7.js +1 -0
  101. package/packages/web/412.062df5f732d5ba203415.js +1 -0
  102. package/packages/web/426.08656fef4918b3fb19ad.js +1 -0
  103. package/packages/web/465.2be8018327130a3bd798.js +1 -0
  104. package/packages/web/48.8ca96fc93667a715e67a.js +1 -0
  105. package/packages/web/480.44c1f1a2927486ac3d4f.js +1 -0
  106. package/packages/web/489.e041a8d0db15dc96d607.js +1 -0
  107. package/packages/web/490.9ffb26c907de020d671b.js +1 -0
  108. package/packages/web/492.58781369e348d91fc06a.js +1 -0
  109. package/packages/web/495.ed63e99791a87167c6b3.js +1 -0
  110. package/packages/web/510.4cc07ab7d30d5c1cd17f.js +1 -0
  111. package/packages/web/543.3af155ed4fa237664308.js +1 -0
  112. package/packages/web/567.f04ab60f8e2c2fb0745a.js +1 -0
  113. package/packages/web/592.f3ad085fa9c1849daa06.js +1 -0
  114. package/packages/web/616.b03fb801b3433b17750f.js +1 -0
  115. package/packages/web/617.d88def54921d2c4dc44c.js +1 -0
  116. package/packages/web/641.d30787d674f548928261.js +1 -0
  117. package/packages/web/672.5269c8399fa42a5af95d.js +1 -0
  118. package/packages/web/731.97cab92b71811c502bda.js +1 -0
  119. package/packages/web/746.3947c6f0235407e420fb.js +1 -0
  120. package/packages/web/756.a53233b3f3913900d5ac.js +1 -0
  121. package/packages/web/77.68801af593a28a631fbf.js +1 -0
  122. package/packages/web/802.53b2bff3cf2a69f7b80c.js +1 -0
  123. package/packages/web/815.b6dfab82265f56c7e046.js +1 -0
  124. package/packages/web/821.f5a13e5c735aac244eb9.js +1 -0
  125. package/packages/web/846.b9bf97d5f559270675ce.js +1 -0
  126. package/packages/web/869.7c10403f500e6201407f.js +1 -0
  127. package/packages/web/885.135050364f99e6924fb5.js +1 -0
  128. package/packages/web/901.fd5aeb9df630609a2b43.js +1 -0
  129. package/packages/web/928.f67e590de3caa4daa3ae.js +1 -0
  130. package/packages/web/955.d833403521ba4dd567ee.js +1 -0
  131. package/packages/web/981.a45cb745cf424044c8c8.js +1 -0
  132. package/packages/web/992.645320b60c74c8787482.js +1 -0
  133. package/packages/web/996.ed9a963dc9e7439eca9a.js +1 -0
  134. package/packages/web/css/codemirror-theme_xq-light.css +43 -0
  135. package/packages/web/css/codemirror.css +344 -0
  136. package/packages/web/css/main.3b61356b384d2f11f47f.css +7268 -0
  137. package/packages/web/index.html +3 -0
  138. package/packages/web/main.77c2c4b553ca3fac223b.js +2 -0
  139. package/packages/web/main.77c2c4b553ca3fac223b.js.LICENSE.txt +3 -0
@@ -0,0 +1,757 @@
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
+