mcp-osp-prompt 1.0.0

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.
@@ -0,0 +1,532 @@
1
+ import { writeFileSync } from 'fs';
2
+ import { tmpdir } from 'os';
3
+ import path from 'path';
4
+ import open from 'open';
5
+ import { startDialogServer, waitForResponse, stopDialogServer } from './http-server.js';
6
+ // ✅ UNIFIED: 导入统一的按钮管理常量
7
+ import { BASE_DIALOG_BUTTONS, getSafeButtons, validateDialogButtons } from './constants.js';
8
+
9
+ let serverPort = null;
10
+
11
+ async function ensureServer() {
12
+ if (!serverPort) {
13
+ serverPort = await startDialogServer();
14
+ }
15
+ return serverPort;
16
+ }
17
+
18
+ export async function showBrowserDialog(html) {
19
+ // 确保HTTP服务器已启动
20
+ const port = await ensureServer();
21
+ console.log(`[Browser Dialog] Using HTTP server on port ${port}`);
22
+
23
+ const tempFile = path.join(tmpdir(), `dialog_${Date.now()}.html`);
24
+ writeFileSync(tempFile, html, 'utf8');
25
+ await open(tempFile);
26
+ return await waitForResponse();
27
+ }
28
+
29
+ export async function showInputDialog(opts) {
30
+ const { title, message, defaultValue } = opts;
31
+
32
+ // Handle automated testing mode
33
+ if (process.env.AUTOMATED_MODE === 'true') {
34
+ console.log(`🤖 [AUTOMATED] Browser showInputDialog: ${title}, returning: "${defaultValue || ''}"`);
35
+ return defaultValue || '';
36
+ }
37
+
38
+ const port = await ensureServer();
39
+
40
+ const html = `
41
+ <html>
42
+ <head>
43
+ <title>${title}</title>
44
+ <style>
45
+ body { font-family: Arial, sans-serif; padding: 20px; max-width: 600px; margin: 0 auto; }
46
+ textarea { width: 100%; height: 200px; margin: 10px 0; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
47
+ button { padding: 12px 24px; margin: 5px; font-size: 16px; border: none; border-radius: 4px; cursor: pointer; }
48
+ .submit-btn { background: #007bff; color: white; }
49
+ .cancel-btn { background: #6c757d; color: white; }
50
+ .instruction { background: #e7f3ff; padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 4px solid #007bff; }
51
+ #status { padding: 10px; margin: 10px 0; border-radius: 4px; display: none; }
52
+ .success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <h2>🌐 ${title}</h2>
57
+ <p><strong>消息:</strong> ${message}</p>
58
+
59
+ <textarea id='input' placeholder="请在此输入您的回复...">${defaultValue || ''}</textarea>
60
+
61
+ <div>
62
+ <button class='submit-btn' onclick='submitInput()'>✅ 确认</button>
63
+ </div>
64
+
65
+ <div id='status'></div>
66
+
67
+ <div class='instruction'>
68
+ <strong>🌐 浏览器对话框模式:</strong>
69
+ <ul>
70
+ <li>在上方文本框中输入您的内容</li>
71
+ <li>点击"确认"按钮提交</li>
72
+ <li>系统将自动发送内容到MCP服务器</li>
73
+ <li>请勿关闭此窗口,等待处理完成</li>
74
+ </ul>
75
+ </div>
76
+
77
+ <script>
78
+ const serverPort = ${port};
79
+
80
+ function showStatus(message, isSuccess = true) {
81
+ const status = document.getElementById('status');
82
+ status.className = isSuccess ? 'success' : 'error';
83
+ status.innerHTML = message;
84
+ status.style.display = 'block';
85
+ }
86
+
87
+ async function submitInput() {
88
+ const text = document.getElementById('input').value;
89
+
90
+ try {
91
+ showStatus('📤 正在发送回复...', true);
92
+
93
+ const response = await fetch('http://localhost:' + serverPort + '/submit?text=' + encodeURIComponent(text), {
94
+ method: 'GET'
95
+ });
96
+
97
+ if (response.ok) {
98
+ const result = await response.json();
99
+ if (result.success) {
100
+ showStatus('✅ 回复发送成功!正在关闭窗口...', true);
101
+ document.body.innerHTML = '<div style="text-align:center;padding:30px;"><h2>✅ 回复已成功发送</h2><p>内容:<strong>' + text + '</strong></p><p>窗口将在3秒后自动关闭...</p></div>';
102
+
103
+ // 1秒后尝试关闭窗口
104
+ setTimeout(() => {
105
+ // 尝试关闭窗口
106
+ try {
107
+ window.close();
108
+ } catch (e) {
109
+ console.log('Cannot close window automatically');
110
+ }
111
+
112
+ // 显示关闭提示
113
+ document.body.innerHTML = '<div style="text-align:center;padding:30px;background:#f8f9fa;"><h3 style="color:#28a745;">✅ 任务完成</h3><p style="margin:20px 0;">回复已成功发送到MCP服务器</p><p style="color:#666;font-size:14px;">您可以安全地关闭此浏览器窗口/标签页</p><button onclick="window.close()" style="padding:10px 20px;background:#007bff;color:white;border:none;border-radius:4px;cursor:pointer;margin-top:10px;">关闭窗口</button></div>';
114
+ }, 1000);
115
+ } else {
116
+ showStatus('❌ 服务器响应异常', false);
117
+ }
118
+ } else {
119
+ showStatus('❌ 发送失败,请重试', false);
120
+ }
121
+ } catch (error) {
122
+ showStatus('❌ 连接失败:' + error.message, false);
123
+ }
124
+ }
125
+
126
+ </script>
127
+ </body>
128
+ </html>
129
+ `;
130
+
131
+ console.log(`[Browser Dialog] Opening input dialog - Server: http://localhost:${port}`);
132
+ return await showBrowserDialog(html);
133
+ }
134
+
135
+ export async function showConfirmDialog(opts) {
136
+ const { title, message, buttons } = opts;
137
+
138
+ // ✅ UNIFIED: 使用统一的按钮管理系统
139
+ const finalButtons = getSafeButtons(buttons);
140
+ console.log('🔧 [BUTTON-UNIFIED] Browser dialog using unified button management:', finalButtons);
141
+
142
+ // Handle automated testing mode
143
+ if (process.env.AUTOMATED_MODE === 'true') {
144
+ // ✅ UNIFIED: 验证按钮包含"修改计划"选项,确保统一标准
145
+ if (!validateDialogButtons(finalButtons)) {
146
+ console.warn(`⚠️ [AUTOMATED] Browser dialog missing '修改计划' option:`, finalButtons);
147
+ }
148
+ console.log(`🤖 [AUTOMATED] Browser showConfirmDialog: ${title}, returning: "${finalButtons[0]}" (buttons: ${JSON.stringify(finalButtons)})`);
149
+ return finalButtons[0];
150
+ }
151
+
152
+ const port = await ensureServer();
153
+
154
+ const btnHtml = finalButtons.map(b =>
155
+ `<button class='btn' onclick='selectOption("${b}")'>${b}</button>`
156
+ ).join(' ');
157
+
158
+ const html = `
159
+ <html>
160
+ <head>
161
+ <title>${title}</title>
162
+ <style>
163
+ body { font-family: Arial, sans-serif; padding: 20px; max-width: 500px; margin: 0 auto; }
164
+ .btn { padding: 12px 24px; margin: 5px; font-size: 16px; border: none; border-radius: 4px; cursor: pointer; background: #007bff; color: white; }
165
+ .btn:hover { background: #0056b3; }
166
+ .instruction { background: #e7f3ff; padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 4px solid #007bff; }
167
+ #status { padding: 10px; margin: 10px 0; border-radius: 4px; display: none; }
168
+ .success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
169
+ </style>
170
+ </head>
171
+ <body>
172
+ <h2>🌐 ${title}</h2>
173
+ <p><strong>消息:</strong> ${message}</p>
174
+
175
+ <div style='text-align: center; margin: 20px 0;'>
176
+ ${btnHtml}
177
+ </div>
178
+
179
+ <div id='status'></div>
180
+
181
+ <div class='instruction'>
182
+ <strong>🌐 浏览器确认对话框:</strong>
183
+ <ul>
184
+ <li>请点击上方按钮选择您的响应</li>
185
+ <li>系统将自动发送选择到MCP服务器</li>
186
+ <li>请勿关闭此窗口,等待处理完成</li>
187
+ </ul>
188
+ </div>
189
+
190
+ <script>
191
+ const serverPort = ${port};
192
+
193
+ function showStatus(message, isSuccess = true) {
194
+ const status = document.getElementById('status');
195
+ status.className = isSuccess ? 'success' : 'error';
196
+ status.innerHTML = message;
197
+ status.style.display = 'block';
198
+ }
199
+
200
+ async function selectOption(option) {
201
+ try {
202
+ showStatus('📤 正在发送选择...', true);
203
+
204
+ const response = await fetch('http://localhost:' + serverPort + '/submit?text=' + encodeURIComponent(option), {
205
+ method: 'GET'
206
+ });
207
+
208
+ if (response.ok) {
209
+ const result = await response.json();
210
+ if (result.success) {
211
+ showStatus('✅ 选择发送成功!正在关闭窗口...', true);
212
+ document.body.innerHTML = '<div style="text-align:center;padding:30px;"><h2>✅ 已选择:' + option + '</h2><p>窗口将在3秒后自动关闭...</p></div>';
213
+
214
+ // 3秒后自动关闭窗口
215
+ setTimeout(() => {
216
+ window.close();
217
+ document.body.innerHTML = '<div style="text-align:center;padding:50px;color:#888;"><h3>✅ 任务完成</h3><p>您可以手动关闭此窗口</p></div>';
218
+ }, 3000);
219
+ } else {
220
+ showStatus('❌ 服务器响应异常', false);
221
+ }
222
+ } else {
223
+ showStatus('❌ 发送失败,请重试', false);
224
+ }
225
+ } catch (error) {
226
+ showStatus('❌ 连接失败:' + error.message, false);
227
+ }
228
+ }
229
+ </script>
230
+ </body>
231
+ </html>
232
+ `;
233
+
234
+ console.log(`[Browser Dialog] Opening confirm dialog - Server: http://localhost:${port}`);
235
+ return await showBrowserDialog(html);
236
+ }
237
+
238
+ export async function showPlanAdjustmentDialog(opts) {
239
+ const { title = '', message = '', currentPlan, options } = opts; // ✅ UNIFIED: 只接收传入的选项,不设置默认值
240
+
241
+ if (!title || !message) {
242
+ throw new Error('Title and message are required for plan adjustment dialog');
243
+ }
244
+
245
+ // ✅ UNIFIED: 使用统一的按钮管理系统
246
+ const finalOptions = getSafeButtons(options);
247
+ console.log('🔧 [BUTTON-UNIFIED] Browser plan dialog using unified button management:', finalOptions);
248
+
249
+ // Handle automated mode
250
+ if (process.env.AUTOMATED_MODE === 'true') {
251
+ // ✅ UNIFIED: 验证选项包含"修改计划"选项,确保统一标准
252
+ if (!validateDialogButtons(finalOptions)) {
253
+ console.warn(`⚠️ [AUTOMATED] Browser plan dialog missing '修改计划' option:`, finalOptions);
254
+ }
255
+ console.log(`🤖 [AUTOMATED] Browser showPlanAdjustmentDialog: ${title}, returning: "${finalOptions[0]}" (options: ${JSON.stringify(finalOptions)})`);
256
+ return finalOptions[0]; // Return first option
257
+ }
258
+
259
+ const port = await ensureServer();
260
+
261
+ // First dialog: only "继续" and "修改计划" buttons
262
+ const html = `
263
+ <html>
264
+ <head>
265
+ <title>${title}</title>
266
+ <style>
267
+ body { font-family: Arial, sans-serif; padding: 20px; max-width: 600px; margin: 0 auto; }
268
+ .option-btn { padding: 12px 24px; margin: 5px 0; font-size: 16px; border: none; border-radius: 4px; cursor: pointer; background: #007bff; color: white; width: 100%; display: block; }
269
+ .option-btn:hover { background: #0056b3; }
270
+ .plan-section { background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 4px solid #28a745; }
271
+ .instruction { background: #e7f3ff; padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 4px solid #007bff; }
272
+ #status { padding: 10px; margin: 10px 0; border-radius: 4px; display: none; }
273
+ .success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
274
+ </style>
275
+ </head>
276
+ <body>
277
+ <h2>📋 ${title}</h2>
278
+ <p><strong>消息:</strong> ${message}</p>
279
+
280
+ ${currentPlan ? `<div class='plan-section'>
281
+ <h4>📝 当前计划:</h4>
282
+ <pre>${currentPlan}</pre>
283
+ </div>` : ''}
284
+
285
+ <div style='margin: 20px 0;'>
286
+ <button class='option-btn' onclick='selectOption("继续")'>继续</button>
287
+ <button class='option-btn' onclick='selectOption("修改计划")'>修改计划</button>
288
+ </div>
289
+
290
+ <div id='status'></div>
291
+
292
+ <div class='instruction'>
293
+ <strong>📋 计划调整对话框:</strong>
294
+ <ul>
295
+ <li>选择"继续"直接执行当前计划</li>
296
+ <li>选择"修改计划"可输入调整建议</li>
297
+ <li>系统将根据您的选择继续执行或调整计划</li>
298
+ </ul>
299
+ </div>
300
+
301
+ <script>
302
+ const serverPort = ${port};
303
+
304
+ function showStatus(message, isSuccess = true) {
305
+ const status = document.getElementById('status');
306
+ status.className = isSuccess ? 'success' : 'error';
307
+ status.innerHTML = message;
308
+ status.style.display = 'block';
309
+ }
310
+
311
+ async function selectOption(option) {
312
+ if (option === '修改计划') {
313
+ // Show input dialog for plan adjustment
314
+ showPlanInputDialog();
315
+ return;
316
+ }
317
+
318
+ try {
319
+ showStatus('📤 正在发送选择...', true);
320
+
321
+ const response = await fetch('http://localhost:' + serverPort + '/submit?text=' + encodeURIComponent(option), {
322
+ method: 'GET'
323
+ });
324
+
325
+ if (response.ok) {
326
+ const result = await response.json();
327
+ if (result.success) {
328
+ showStatus('✅ 选择发送成功!正在关闭窗口...', true);
329
+ document.body.innerHTML = '<div style="text-align:center;padding:30px;"><h2>✅ 已选择:' + option + '</h2><p>窗口将在3秒后自动关闭...</p></div>';
330
+
331
+ setTimeout(() => {
332
+ window.close();
333
+ document.body.innerHTML = '<div style="text-align:center;padding:50px;color:#888;"><h3>✅ 任务完成</h3><p>您可以手动关闭此窗口</p></div>';
334
+ }, 3000);
335
+ } else {
336
+ showStatus('❌ 服务器响应异常', false);
337
+ }
338
+ } else {
339
+ showStatus('❌ 发送失败,请重试', false);
340
+ }
341
+ } catch (error) {
342
+ showStatus('❌ 连接失败:' + error.message, false);
343
+ }
344
+ }
345
+
346
+ function showPlanInputDialog() {
347
+ // Replace the current page with input dialog
348
+ document.body.innerHTML = \`
349
+ <div style="font-family: Arial, sans-serif; padding: 20px; max-width: 600px; margin: 0 auto;">
350
+ <h2>📝 修改计划</h2>
351
+ <p><strong>请输入您的调整建议:</strong></p>
352
+
353
+ <textarea id='planInput'
354
+ placeholder='请在此输入您对计划的调整建议...'
355
+ style='width: 100%; height: 200px; margin: 15px 0; padding: 10px; border: 1px solid #ddd; border-radius: 4px; resize: vertical; font-size: 14px;'></textarea>
356
+
357
+ <div style='margin: 20px 0;'>
358
+ <button class='confirm-btn' onclick='confirmPlanInput()'
359
+ style='padding: 12px 24px; margin: 5px; font-size: 16px; border: none; border-radius: 4px; cursor: pointer; background: #007bff; color: white;'>
360
+ 确认
361
+ </button>
362
+ </div>
363
+
364
+ <div id='status' style='padding: 10px; margin: 10px 0; border-radius: 4px; display: none;'></div>
365
+
366
+ <div style='background: #e7f3ff; padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 4px solid #007bff;'>
367
+ <strong>💡 使用说明:</strong>
368
+ <ul>
369
+ <li>请详细描述您希望调整的内容</li>
370
+ <li>可以包含具体的修改建议或新的需求</li>
371
+ <li>点击"确认"后将发送到系统进行处理</li>
372
+ </ul>
373
+ </div>
374
+ </div>
375
+ \`;
376
+ }
377
+
378
+ async function confirmPlanInput() {
379
+ const inputValue = document.getElementById('planInput').value.trim();
380
+
381
+ if (!inputValue) {
382
+ showStatus('请输入调整建议!', false);
383
+ return;
384
+ }
385
+
386
+ try {
387
+ showStatus('📤 正在发送调整建议...', true);
388
+
389
+ const responseData = JSON.stringify({
390
+ action: '修改计划',
391
+ input: inputValue
392
+ });
393
+
394
+ const response = await fetch('http://localhost:' + serverPort + '/submit?text=' + encodeURIComponent(responseData), {
395
+ method: 'GET'
396
+ });
397
+
398
+ if (response.ok) {
399
+ const result = await response.json();
400
+ if (result.success) {
401
+ showStatus('✅ 调整建议发送成功!正在关闭窗口...', true);
402
+ document.body.innerHTML = '<div style="text-align:center;padding:30px;"><h2>✅ 调整建议已发送</h2><p><strong>内容:</strong>' + inputValue + '</p><p>窗口将在3秒后自动关闭...</p></div>';
403
+
404
+ setTimeout(() => {
405
+ window.close();
406
+ document.body.innerHTML = '<div style="text-align:center;padding:50px;color:#888;"><h3>✅ 任务完成</h3><p>您可以手动关闭此窗口</p></div>';
407
+ }, 3000);
408
+ } else {
409
+ showStatus('❌ 服务器响应异常', false);
410
+ }
411
+ } else {
412
+ showStatus('❌ 发送失败,请重试', false);
413
+ }
414
+ } catch (error) {
415
+ showStatus('❌ 连接失败:' + error.message, false);
416
+ }
417
+ }
418
+ </script>
419
+ </body>
420
+ </html>
421
+ `;
422
+
423
+ console.log(`[Browser Dialog] Opening plan adjustment dialog - Server: http://localhost:${port}`);
424
+ const result = await showBrowserDialog(html);
425
+
426
+ // Try to parse as JSON for input responses
427
+ if (result) {
428
+ try {
429
+ return JSON.parse(result);
430
+ } catch (e) {
431
+ // Fallback to simple string response
432
+ return result;
433
+ }
434
+ }
435
+
436
+ return result;
437
+ }
438
+
439
+ export async function showSelectDialog(opts) {
440
+ const { title, message, items = [] } = opts;
441
+
442
+ // Handle automated testing mode
443
+ if (process.env.AUTOMATED_MODE === 'true') {
444
+ console.log(`🤖 [AUTOMATED] Browser showSelectDialog: ${title}, returning: "${items[0] || ''}"`);
445
+ return items[0] || '';
446
+ }
447
+
448
+ const port = await ensureServer();
449
+
450
+ const optionHtml = items.map(item =>
451
+ `<button class='option-btn' onclick='selectOption("${item}")'>${item}</button>`
452
+ ).join('<br/>');
453
+
454
+ const html = `
455
+ <html>
456
+ <head>
457
+ <title>${title}</title>
458
+ <style>
459
+ body { font-family: Arial, sans-serif; padding: 20px; max-width: 500px; margin: 0 auto; }
460
+ .option-btn { padding: 12px 24px; margin: 5px 0; font-size: 16px; border: none; border-radius: 4px; cursor: pointer; background: #007bff; color: white; width: 100%; display: block; }
461
+ .option-btn:hover { background: #0056b3; }
462
+ .instruction { background: #e7f3ff; padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 4px solid #007bff; }
463
+ #status { padding: 10px; margin: 10px 0; border-radius: 4px; display: none; }
464
+ .success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
465
+ </style>
466
+ </head>
467
+ <body>
468
+ <h2>🌐 ${title}</h2>
469
+ <p><strong>消息:</strong> ${message}</p>
470
+
471
+ <div style='margin: 20px 0;'>
472
+ ${optionHtml}
473
+ </div>
474
+
475
+ <div id='status'></div>
476
+
477
+ <div class='instruction'>
478
+ <strong>🌐 浏览器选择对话框:</strong>
479
+ <ul>
480
+ <li>请点击上方选项按钮进行选择</li>
481
+ <li>系统将自动发送选择到MCP服务器</li>
482
+ <li>请勿关闭此窗口,等待处理完成</li>
483
+ </ul>
484
+ </div>
485
+
486
+ <script>
487
+ const serverPort = ${port};
488
+
489
+ function showStatus(message, isSuccess = true) {
490
+ const status = document.getElementById('status');
491
+ status.className = isSuccess ? 'success' : 'error';
492
+ status.innerHTML = message;
493
+ status.style.display = 'block';
494
+ }
495
+
496
+ async function selectOption(option) {
497
+ try {
498
+ showStatus('📤 正在发送选择...', true);
499
+
500
+ const response = await fetch('http://localhost:' + serverPort + '/submit?text=' + encodeURIComponent(option), {
501
+ method: 'GET'
502
+ });
503
+
504
+ if (response.ok) {
505
+ const result = await response.json();
506
+ if (result.success) {
507
+ showStatus('✅ 选择发送成功!正在关闭窗口...', true);
508
+ document.body.innerHTML = '<div style="text-align:center;padding:30px;"><h2>✅ 已选择:' + option + '</h2><p>窗口将在3秒后自动关闭...</p></div>';
509
+
510
+ // 3秒后自动关闭窗口
511
+ setTimeout(() => {
512
+ window.close();
513
+ document.body.innerHTML = '<div style="text-align:center;padding:50px;color:#888;"><h3>✅ 任务完成</h3><p>您可以手动关闭此窗口</p></div>';
514
+ }, 3000);
515
+ } else {
516
+ showStatus('❌ 服务器响应异常', false);
517
+ }
518
+ } else {
519
+ showStatus('❌ 发送失败,请重试', false);
520
+ }
521
+ } catch (error) {
522
+ showStatus('❌ 连接失败:' + error.message, false);
523
+ }
524
+ }
525
+ </script>
526
+ </body>
527
+ </html>
528
+ `;
529
+
530
+ console.log(`[Browser Dialog] Opening select dialog - Server: http://localhost:${port}`);
531
+ return await showBrowserDialog(html);
532
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Dialog系统统一常量定义
3
+ *
4
+ * 这个文件包含了所有弹窗系统使用的标准常量,
5
+ * 避免在各个模块中硬编码按钮定义,确保统一管理。
6
+ */
7
+
8
+ // 🔵 UNIFIED: 基础标准按钮:所有弹窗统一使用
9
+ export const BASE_DIALOG_BUTTONS = ['继续', '修改计划'];
10
+
11
+ // 🔵 UNIFIED: 标准弹窗按钮配置
12
+ export const STANDARD_DIALOG_BUTTONS = {
13
+ // 默认场景:['继续', '修改计划'] - 适用于确认、输入、计划调整等所有基础场景
14
+ default: BASE_DIALOG_BUTTONS,
15
+
16
+ // 选择场景:自定义选项(最多2个)+ 固定的'修改计划'
17
+ select: (options = []) => {
18
+ // 🔧 FIX: 先从所有选项中移除'修改计划',然后取前2个,最后强制追加
19
+ const cleanedOptions = options.filter(option => option !== '修改计划');
20
+ const maxOptions = cleanedOptions.slice(0, 2);
21
+
22
+ // 强制追加'修改计划'到末尾,确保总是存在且唯一
23
+ return [...maxOptions, '修改计划'];
24
+ }
25
+ };
26
+
27
+ /**
28
+ * 获取标准按钮配置(自动判断场景)
29
+ * @param {Array} options - 可选的自定义选项数组,如果提供则会添加到"修改计划"前面
30
+ * @returns {Array} 标准按钮数组,始终包含"修改计划"
31
+ */
32
+ export function getStandardButtons(options = []) {
33
+ console.log(`🔧 [BUTTON-GENERATION] 输入options:`, options);
34
+
35
+ if (options && Array.isArray(options) && options.length > 0) {
36
+ // 📋 自定义按钮模式:有用户提供的选项
37
+ return STANDARD_DIALOG_BUTTONS.select(options);
38
+ } else {
39
+ // 🎯 默认按钮模式:标准的["继续", "修改计划"]
40
+ return [...STANDARD_DIALOG_BUTTONS.default];
41
+ }
42
+ }
43
+
44
+ /**
45
+ * 验证弹窗按钮配置是否符合标准
46
+ * @param {Array} buttons - 按钮数组
47
+ * @returns {boolean} 是否符合标准
48
+ */
49
+ export function validateDialogButtons(buttons) {
50
+ if (!Array.isArray(buttons) || buttons.length === 0) {
51
+ return false;
52
+ }
53
+
54
+ // 最后一个按钮必须是'修改计划'
55
+ return buttons[buttons.length - 1] === '修改计划';
56
+ }
57
+
58
+ /**
59
+ * 获取安全的默认按钮(当传入的按钮无效时使用)
60
+ * @param {Array} buttons - 原始按钮数组
61
+ * @returns {Array} 安全的按钮数组
62
+ */
63
+ export function getSafeButtons(buttons) {
64
+ if (!buttons || !Array.isArray(buttons) || buttons.length === 0) {
65
+ return [...BASE_DIALOG_BUTTONS];
66
+ }
67
+ return buttons;
68
+ }