speccore 7.1.0 → 7.2.1
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.
- package/.agents/skills/spec-ask/SKILL.md +10 -5
- package/README.md +24 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +6 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +607 -56
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/ask.d.ts +1 -1
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +2 -2
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/config.js +2 -2
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/dev.js +30 -8
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.js +16 -276
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration-from-global.js +2 -2
- package/dist/commands/prompts.d.ts +7 -0
- package/dist/commands/prompts.d.ts.map +1 -1
- package/dist/commands/prompts.js +650 -52
- package/dist/commands/prompts.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +149 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +58 -3
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/change-impact-global.d.ts +20 -0
- package/dist/core/change-impact-global.d.ts.map +1 -0
- package/dist/core/change-impact-global.js +130 -0
- package/dist/core/change-impact-global.js.map +1 -0
- package/dist/core/dev-llm.d.ts +10 -0
- package/dist/core/dev-llm.d.ts.map +1 -1
- package/dist/core/dev-llm.js.map +1 -1
- package/dist/core/doc-cross-reference.d.ts +10 -0
- package/dist/core/doc-cross-reference.d.ts.map +1 -0
- package/dist/core/doc-cross-reference.js +108 -0
- package/dist/core/doc-cross-reference.js.map +1 -0
- package/dist/core/doc-quality-gate.d.ts +28 -0
- package/dist/core/doc-quality-gate.d.ts.map +1 -0
- package/dist/core/doc-quality-gate.js +209 -0
- package/dist/core/doc-quality-gate.js.map +1 -0
- package/dist/core/incremental-analyzer.js +1 -1
- package/dist/core/incremental-analyzer.js.map +1 -1
- package/dist/core/intent-recognition.d.ts.map +1 -1
- package/dist/core/intent-recognition.js +61 -2
- package/dist/core/intent-recognition.js.map +1 -1
- package/dist/core/iteration-cache.d.ts +48 -0
- package/dist/core/iteration-cache.d.ts.map +1 -0
- package/dist/core/iteration-cache.js +148 -0
- package/dist/core/iteration-cache.js.map +1 -0
- package/dist/core/platform-addition.js +3 -3
- package/dist/core/platform-addition.js.map +1 -1
- package/dist/core/semantic-locator.d.ts +42 -0
- package/dist/core/semantic-locator.d.ts.map +1 -0
- package/dist/core/semantic-locator.js +290 -0
- package/dist/core/semantic-locator.js.map +1 -0
- package/dist/core/structured-extractor.d.ts +89 -0
- package/dist/core/structured-extractor.d.ts.map +1 -0
- package/dist/core/structured-extractor.js +428 -0
- package/dist/core/structured-extractor.js.map +1 -0
- package/package.json +1 -1
package/dist/commands/prompts.js
CHANGED
|
@@ -15,7 +15,8 @@ async function promptsCommand(options) {
|
|
|
15
15
|
const userPrompts = await loadUserPrompts();
|
|
16
16
|
const allPrompts = [...presets, ...userPrompts];
|
|
17
17
|
// 生成 HTML 页面
|
|
18
|
-
const
|
|
18
|
+
const ctx = await readCurrentContext();
|
|
19
|
+
const html = generatePromptsHtml(attachParamFlags(applyDynamicDefaults(allPrompts, ctx)));
|
|
19
20
|
const outputPath = options.output || (0, path_1.join)(process.cwd(), 'outputs', 'speccore-prompts.html');
|
|
20
21
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(process.cwd(), 'outputs'));
|
|
21
22
|
await (0, fs_extra_1.writeFile)(outputPath, html);
|
|
@@ -60,6 +61,80 @@ async function loadUserPrompts() {
|
|
|
60
61
|
catch { }
|
|
61
62
|
return prompts.sort((a, b) => (b.updatedAt || '').localeCompare(a.updatedAt || ''));
|
|
62
63
|
}
|
|
64
|
+
/** 读取当前运行时上下文(.speccore/local/context.json 的 currentIteration/currentTask) */
|
|
65
|
+
async function readCurrentContext() {
|
|
66
|
+
try {
|
|
67
|
+
const ctxPath = (0, path_1.join)(process.cwd(), '.speccore', 'local', 'context.json');
|
|
68
|
+
if (!(await (0, fs_extra_1.pathExists)(ctxPath)))
|
|
69
|
+
return {};
|
|
70
|
+
const data = JSON.parse(await (0, fs_extra_1.readFile)(ctxPath, 'utf-8'));
|
|
71
|
+
return {
|
|
72
|
+
iteration: data.currentIteration || '',
|
|
73
|
+
task: data.currentTask || '',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 动态填充默认值:迭代名/任务ID 参数的占位符优先取当前运行时上下文,
|
|
82
|
+
* 取不到时保持原 placeholder(如 Q2 / Task-001)。
|
|
83
|
+
*/
|
|
84
|
+
function applyDynamicDefaults(prompts, ctx) {
|
|
85
|
+
if (!ctx.iteration && !ctx.task)
|
|
86
|
+
return prompts;
|
|
87
|
+
return prompts.map(p => ({
|
|
88
|
+
...p,
|
|
89
|
+
params: (p.params || []).map(pr => {
|
|
90
|
+
if (ctx.iteration && pr.key === '迭代名' && pr.placeholder === 'Q2') {
|
|
91
|
+
return { ...pr, placeholder: ctx.iteration };
|
|
92
|
+
}
|
|
93
|
+
if (ctx.task && pr.key === '任务ID' && pr.placeholder === 'Task-001') {
|
|
94
|
+
return { ...pr, placeholder: ctx.task };
|
|
95
|
+
}
|
|
96
|
+
return pr;
|
|
97
|
+
}),
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 为每个参数推断 CLI flag,用于「复制 Skill」生成 flag 风格参数(如 `--task X -I Y`)。
|
|
102
|
+
* 优先从 command 字段解析(`--task {任务ID}` → 任务ID 对应 `--task`),
|
|
103
|
+
* 缺失时回退到 skill 名称的预置映射;两者都没有则视为位置参数(flag 为空)。
|
|
104
|
+
*/
|
|
105
|
+
function attachParamFlags(prompts) {
|
|
106
|
+
const SKILL_FLAGS = {
|
|
107
|
+
'spec-dev': { '迭代名': '-i', '阶段': '--from' },
|
|
108
|
+
'spec-analyze': { '迭代名': '-I', '文档名': '--deep', '功能名': '--feature', '模块关键词': '--filter' },
|
|
109
|
+
'spec-split': { '迭代名': '-i', '功能模块': '--filter' },
|
|
110
|
+
'spec-change': { '迭代名': '-i' },
|
|
111
|
+
'spec-iteration-create': { '迭代名': '-n', '主题': '--topic', '负责人': '--owner', '需求文档路径': '-f' },
|
|
112
|
+
'spec-execute': { '迭代名': '-i', '任务ID': '--task', '数量': '--batch-size' },
|
|
113
|
+
'spec-pr': { '迭代名': '-i', '任务ID': '--task' },
|
|
114
|
+
'spec-done': { '迭代名': '-i', '任务ID': '--task' },
|
|
115
|
+
'spec-doc2spec': { '文件路径': '-f', '迭代名': '--iter', '平台': '-p' },
|
|
116
|
+
'spec-spec2doc': { '迭代名': '-i', '输出文件名': '-o' },
|
|
117
|
+
};
|
|
118
|
+
return prompts.map(p => {
|
|
119
|
+
if (!p.params || p.params.length === 0)
|
|
120
|
+
return p;
|
|
121
|
+
// 从 command 字段解析 flag → 参数 key 映射(单点事实来源)
|
|
122
|
+
const cmdFlags = {};
|
|
123
|
+
if (p.command) {
|
|
124
|
+
const re = /(--?[A-Za-z][\w-]*)\s+["']?\{([^}]+)\}["']?/g;
|
|
125
|
+
let m;
|
|
126
|
+
while ((m = re.exec(p.command)) !== null) {
|
|
127
|
+
cmdFlags[m[2]] = m[1];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const skillFlags = (p.skill && SKILL_FLAGS[p.skill]) || {};
|
|
131
|
+
const params = p.params.map(pr => ({
|
|
132
|
+
...pr,
|
|
133
|
+
flag: cmdFlags[pr.key] || skillFlags[pr.key] || '',
|
|
134
|
+
}));
|
|
135
|
+
return { ...p, params };
|
|
136
|
+
});
|
|
137
|
+
}
|
|
63
138
|
function generatePromptsHtml(prompts) {
|
|
64
139
|
const categories = [...new Set(prompts.map(p => p.category))];
|
|
65
140
|
const categoryLabels = {
|
|
@@ -67,6 +142,7 @@ function generatePromptsHtml(prompts) {
|
|
|
67
142
|
iteration: '迭代管理',
|
|
68
143
|
analysis: '分析文档',
|
|
69
144
|
execute: '开发执行',
|
|
145
|
+
governance: '治理运维',
|
|
70
146
|
custom: '我的'
|
|
71
147
|
};
|
|
72
148
|
return `<!DOCTYPE html>
|
|
@@ -97,7 +173,32 @@ function generatePromptsHtml(prompts) {
|
|
|
97
173
|
padding: 30px;
|
|
98
174
|
text-align: center;
|
|
99
175
|
}
|
|
100
|
-
.header h1 {
|
|
176
|
+
.header h1 {
|
|
177
|
+
font-size: 28px;
|
|
178
|
+
margin-bottom: 8px;
|
|
179
|
+
text-shadow:
|
|
180
|
+
0 0 10px rgba(255,255,255,0.6),
|
|
181
|
+
0 0 20px rgba(255,255,255,0.4),
|
|
182
|
+
0 0 40px rgba(102,126,234,0.6),
|
|
183
|
+
0 0 80px rgba(118,75,162,0.4);
|
|
184
|
+
animation: glowPulse 2s ease-in-out infinite alternate;
|
|
185
|
+
}
|
|
186
|
+
@keyframes glowPulse {
|
|
187
|
+
from {
|
|
188
|
+
text-shadow:
|
|
189
|
+
0 0 10px rgba(255,255,255,0.5),
|
|
190
|
+
0 0 20px rgba(255,255,255,0.3),
|
|
191
|
+
0 0 40px rgba(102,126,234,0.5),
|
|
192
|
+
0 0 80px rgba(118,75,162,0.3);
|
|
193
|
+
}
|
|
194
|
+
to {
|
|
195
|
+
text-shadow:
|
|
196
|
+
0 0 15px rgba(255,255,255,0.9),
|
|
197
|
+
0 0 30px rgba(255,255,255,0.7),
|
|
198
|
+
0 0 60px rgba(102,126,234,0.9),
|
|
199
|
+
0 0 100px rgba(118,75,162,0.7);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
101
202
|
.header p { opacity: 0.9; font-size: 14px; }
|
|
102
203
|
.toolbar {
|
|
103
204
|
padding: 20px 30px;
|
|
@@ -164,6 +265,9 @@ function generatePromptsHtml(prompts) {
|
|
|
164
265
|
.btn-secondary:hover { background: #dee2e6; }
|
|
165
266
|
.content {
|
|
166
267
|
padding: 30px;
|
|
268
|
+
min-height: 520px;
|
|
269
|
+
max-height: 80vh;
|
|
270
|
+
overflow-y: auto;
|
|
167
271
|
}
|
|
168
272
|
.section-title {
|
|
169
273
|
font-size: 18px;
|
|
@@ -188,6 +292,7 @@ function generatePromptsHtml(prompts) {
|
|
|
188
292
|
height: 100%;
|
|
189
293
|
display: flex;
|
|
190
294
|
flex-direction: column;
|
|
295
|
+
position: relative;
|
|
191
296
|
}
|
|
192
297
|
.prompt-card:hover {
|
|
193
298
|
border-color: #667eea;
|
|
@@ -246,6 +351,45 @@ function generatePromptsHtml(prompts) {
|
|
|
246
351
|
height: 30px;
|
|
247
352
|
background: linear-gradient(transparent, #f8f9fa);
|
|
248
353
|
}
|
|
354
|
+
.prompt-section {
|
|
355
|
+
font-size: 11px;
|
|
356
|
+
font-weight: 600;
|
|
357
|
+
color: #868e96;
|
|
358
|
+
margin-bottom: 6px;
|
|
359
|
+
display: flex;
|
|
360
|
+
align-items: center;
|
|
361
|
+
gap: 4px;
|
|
362
|
+
text-transform: uppercase;
|
|
363
|
+
letter-spacing: 0.5px;
|
|
364
|
+
}
|
|
365
|
+
.prompt-say {
|
|
366
|
+
background: #e7f5ff;
|
|
367
|
+
border-radius: 8px;
|
|
368
|
+
padding: 12px;
|
|
369
|
+
font-size: 13px;
|
|
370
|
+
color: #1864ab;
|
|
371
|
+
margin-bottom: 12px;
|
|
372
|
+
line-height: 1.5;
|
|
373
|
+
border-left: 3px solid #339af0;
|
|
374
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
375
|
+
}
|
|
376
|
+
.prompt-cmd {
|
|
377
|
+
background: #212529;
|
|
378
|
+
border-radius: 8px;
|
|
379
|
+
padding: 12px;
|
|
380
|
+
font-size: 12px;
|
|
381
|
+
color: #69db7c;
|
|
382
|
+
margin-bottom: 12px;
|
|
383
|
+
line-height: 1.6;
|
|
384
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
|
385
|
+
overflow-x: auto;
|
|
386
|
+
white-space: pre-wrap;
|
|
387
|
+
word-break: break-all;
|
|
388
|
+
}
|
|
389
|
+
.prompt-cmd code {
|
|
390
|
+
color: #69db7c;
|
|
391
|
+
font-family: inherit;
|
|
392
|
+
}
|
|
249
393
|
.prompt-actions {
|
|
250
394
|
display: flex;
|
|
251
395
|
gap: 8px;
|
|
@@ -255,6 +399,18 @@ function generatePromptsHtml(prompts) {
|
|
|
255
399
|
padding: 8px 12px;
|
|
256
400
|
font-size: 13px;
|
|
257
401
|
}
|
|
402
|
+
.btn-say {
|
|
403
|
+
background: #e7f5ff;
|
|
404
|
+
color: #1864ab;
|
|
405
|
+
border: 1px solid #74c0fc;
|
|
406
|
+
}
|
|
407
|
+
.btn-say:hover { background: #d0ebff; }
|
|
408
|
+
.btn-cmd {
|
|
409
|
+
background: #212529;
|
|
410
|
+
color: #69db7c;
|
|
411
|
+
border: 1px solid #495057;
|
|
412
|
+
}
|
|
413
|
+
.btn-cmd:hover { background: #343a40; }
|
|
258
414
|
.badge {
|
|
259
415
|
display: inline-block;
|
|
260
416
|
padding: 2px 8px;
|
|
@@ -270,6 +426,76 @@ function generatePromptsHtml(prompts) {
|
|
|
270
426
|
background: #fff3bf;
|
|
271
427
|
color: #e67700;
|
|
272
428
|
}
|
|
429
|
+
.env-hint {
|
|
430
|
+
font-size: 11px;
|
|
431
|
+
color: #868e96;
|
|
432
|
+
margin-bottom: 8px;
|
|
433
|
+
display: flex;
|
|
434
|
+
align-items: center;
|
|
435
|
+
gap: 4px;
|
|
436
|
+
}
|
|
437
|
+
.env-hint.cli {
|
|
438
|
+
color: #2b8a3e;
|
|
439
|
+
}
|
|
440
|
+
.env-hint.ai {
|
|
441
|
+
color: #1971c2;
|
|
442
|
+
}
|
|
443
|
+
.prompt-skill {
|
|
444
|
+
margin-top: 10px;
|
|
445
|
+
padding-top: 10px;
|
|
446
|
+
border-top: 1px dashed rgba(51, 154, 240, 0.3);
|
|
447
|
+
font-size: 12px;
|
|
448
|
+
color: #1864ab;
|
|
449
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
|
450
|
+
display: flex;
|
|
451
|
+
align-items: center;
|
|
452
|
+
gap: 6px;
|
|
453
|
+
}
|
|
454
|
+
.prompt-skill-tag {
|
|
455
|
+
display: inline-block;
|
|
456
|
+
padding: 2px 8px;
|
|
457
|
+
background: rgba(51, 154, 240, 0.15);
|
|
458
|
+
border-radius: 4px;
|
|
459
|
+
font-size: 11px;
|
|
460
|
+
font-weight: 600;
|
|
461
|
+
cursor: pointer;
|
|
462
|
+
transition: background 0.2s;
|
|
463
|
+
}
|
|
464
|
+
.prompt-skill-tag:hover {
|
|
465
|
+
background: rgba(51, 154, 240, 0.3);
|
|
466
|
+
}
|
|
467
|
+
.btn-skill {
|
|
468
|
+
background: #f3f0ff;
|
|
469
|
+
color: #7048e8;
|
|
470
|
+
border: 1px solid #d0bfff;
|
|
471
|
+
}
|
|
472
|
+
.btn-skill:hover { background: #e5dbff; }
|
|
473
|
+
.prompt-examples {
|
|
474
|
+
margin-top: 10px;
|
|
475
|
+
padding: 10px 12px;
|
|
476
|
+
background: #fff9db;
|
|
477
|
+
border-radius: 8px;
|
|
478
|
+
border-left: 3px solid #fcc419;
|
|
479
|
+
}
|
|
480
|
+
.prompt-examples-title {
|
|
481
|
+
font-size: 11px;
|
|
482
|
+
font-weight: 600;
|
|
483
|
+
color: #e67700;
|
|
484
|
+
margin-bottom: 6px;
|
|
485
|
+
display: flex;
|
|
486
|
+
align-items: center;
|
|
487
|
+
gap: 4px;
|
|
488
|
+
}
|
|
489
|
+
.prompt-examples ul {
|
|
490
|
+
margin: 0;
|
|
491
|
+
padding-left: 16px;
|
|
492
|
+
font-size: 12px;
|
|
493
|
+
color: #495057;
|
|
494
|
+
line-height: 1.6;
|
|
495
|
+
}
|
|
496
|
+
.prompt-examples li {
|
|
497
|
+
margin-bottom: 2px;
|
|
498
|
+
}
|
|
273
499
|
.empty-state {
|
|
274
500
|
text-align: center;
|
|
275
501
|
padding: 60px 20px;
|
|
@@ -346,6 +572,147 @@ function generatePromptsHtml(prompts) {
|
|
|
346
572
|
gap: 8px;
|
|
347
573
|
justify-content: flex-end;
|
|
348
574
|
}
|
|
575
|
+
/* ═══ 场景快捷入口 ═══ */
|
|
576
|
+
.scene-bar {
|
|
577
|
+
padding: 16px 30px;
|
|
578
|
+
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
|
579
|
+
border-bottom: 1px solid #dee2e6;
|
|
580
|
+
display: flex;
|
|
581
|
+
gap: 12px;
|
|
582
|
+
align-items: center;
|
|
583
|
+
flex-wrap: wrap;
|
|
584
|
+
}
|
|
585
|
+
.scene-label {
|
|
586
|
+
font-size: 12px;
|
|
587
|
+
font-weight: 600;
|
|
588
|
+
color: #868e96;
|
|
589
|
+
text-transform: uppercase;
|
|
590
|
+
letter-spacing: 0.5px;
|
|
591
|
+
margin-right: 4px;
|
|
592
|
+
}
|
|
593
|
+
.scene-card {
|
|
594
|
+
display: flex;
|
|
595
|
+
align-items: center;
|
|
596
|
+
gap: 8px;
|
|
597
|
+
padding: 8px 14px;
|
|
598
|
+
background: white;
|
|
599
|
+
border: 1px solid #e9ecef;
|
|
600
|
+
border-radius: 8px;
|
|
601
|
+
cursor: pointer;
|
|
602
|
+
font-size: 13px;
|
|
603
|
+
font-weight: 500;
|
|
604
|
+
color: #495057;
|
|
605
|
+
transition: all 0.2s;
|
|
606
|
+
white-space: nowrap;
|
|
607
|
+
}
|
|
608
|
+
.scene-card:hover {
|
|
609
|
+
border-color: #667eea;
|
|
610
|
+
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.12);
|
|
611
|
+
transform: translateY(-1px);
|
|
612
|
+
}
|
|
613
|
+
.scene-card.active {
|
|
614
|
+
background: #667eea;
|
|
615
|
+
color: white;
|
|
616
|
+
border-color: #667eea;
|
|
617
|
+
}
|
|
618
|
+
.scene-card .scene-icon {
|
|
619
|
+
font-size: 16px;
|
|
620
|
+
}
|
|
621
|
+
/* ═══ 标签云 ═══ */
|
|
622
|
+
.tag-cloud {
|
|
623
|
+
padding: 12px 30px;
|
|
624
|
+
background: #f8f9fa;
|
|
625
|
+
border-bottom: 1px solid #e9ecef;
|
|
626
|
+
display: flex;
|
|
627
|
+
gap: 8px;
|
|
628
|
+
align-items: center;
|
|
629
|
+
flex-wrap: wrap;
|
|
630
|
+
transition: max-height 0.3s ease;
|
|
631
|
+
overflow: hidden;
|
|
632
|
+
}
|
|
633
|
+
.tag-cloud.collapsed {
|
|
634
|
+
max-height: 48px;
|
|
635
|
+
}
|
|
636
|
+
.tag-cloud.expanded {
|
|
637
|
+
max-height: 500px;
|
|
638
|
+
}
|
|
639
|
+
.tag-toggle-bar {
|
|
640
|
+
text-align: center;
|
|
641
|
+
padding: 0 30px 10px;
|
|
642
|
+
background: #f8f9fa;
|
|
643
|
+
border-bottom: 1px solid #e9ecef;
|
|
644
|
+
}
|
|
645
|
+
.tag-toggle {
|
|
646
|
+
display: inline-flex;
|
|
647
|
+
align-items: center;
|
|
648
|
+
gap: 4px;
|
|
649
|
+
padding: 5px 14px;
|
|
650
|
+
background: #e9ecef;
|
|
651
|
+
border: 1px solid #dee2e6;
|
|
652
|
+
border-radius: 20px;
|
|
653
|
+
font-size: 12px;
|
|
654
|
+
color: #495057;
|
|
655
|
+
cursor: pointer;
|
|
656
|
+
transition: all 0.2s;
|
|
657
|
+
white-space: nowrap;
|
|
658
|
+
}
|
|
659
|
+
.tag-toggle:hover {
|
|
660
|
+
background: #dee2e6;
|
|
661
|
+
}
|
|
662
|
+
.tag-cloud-label {
|
|
663
|
+
font-size: 12px;
|
|
664
|
+
font-weight: 600;
|
|
665
|
+
color: #868e96;
|
|
666
|
+
margin-right: 4px;
|
|
667
|
+
}
|
|
668
|
+
.tag {
|
|
669
|
+
display: inline-flex;
|
|
670
|
+
align-items: center;
|
|
671
|
+
gap: 4px;
|
|
672
|
+
padding: 4px 10px;
|
|
673
|
+
background: white;
|
|
674
|
+
border: 1px solid #e9ecef;
|
|
675
|
+
border-radius: 20px;
|
|
676
|
+
font-size: 12px;
|
|
677
|
+
color: #495057;
|
|
678
|
+
cursor: pointer;
|
|
679
|
+
transition: all 0.2s;
|
|
680
|
+
}
|
|
681
|
+
.tag:hover {
|
|
682
|
+
border-color: #667eea;
|
|
683
|
+
color: #667eea;
|
|
684
|
+
}
|
|
685
|
+
.tag.active {
|
|
686
|
+
background: #667eea;
|
|
687
|
+
color: white;
|
|
688
|
+
border-color: #667eea;
|
|
689
|
+
}
|
|
690
|
+
.tag .tag-count {
|
|
691
|
+
font-size: 10px;
|
|
692
|
+
background: #f1f3f5;
|
|
693
|
+
color: #868e96;
|
|
694
|
+
padding: 1px 5px;
|
|
695
|
+
border-radius: 10px;
|
|
696
|
+
}
|
|
697
|
+
.tag.active .tag-count {
|
|
698
|
+
background: rgba(255,255,255,0.25);
|
|
699
|
+
color: white;
|
|
700
|
+
}
|
|
701
|
+
/* ═══ 卡片标签 ═══ */
|
|
702
|
+
.prompt-tags {
|
|
703
|
+
display: flex;
|
|
704
|
+
gap: 6px;
|
|
705
|
+
flex-wrap: wrap;
|
|
706
|
+
margin-bottom: 12px;
|
|
707
|
+
}
|
|
708
|
+
.prompt-tag {
|
|
709
|
+
display: inline-block;
|
|
710
|
+
padding: 2px 8px;
|
|
711
|
+
background: #f1f3f5;
|
|
712
|
+
border-radius: 4px;
|
|
713
|
+
font-size: 11px;
|
|
714
|
+
color: #868e96;
|
|
715
|
+
}
|
|
349
716
|
.toast {
|
|
350
717
|
position: fixed;
|
|
351
718
|
bottom: 20px;
|
|
@@ -371,6 +738,29 @@ function generatePromptsHtml(prompts) {
|
|
|
371
738
|
<p>预置模板 + 自定义提示词,一键复制,高效开发</p>
|
|
372
739
|
</div>
|
|
373
740
|
|
|
741
|
+
<!-- 常用场景快捷入口 -->
|
|
742
|
+
<div class="scene-bar" id="sceneBar">
|
|
743
|
+
<span class="scene-label">常用场景</span>
|
|
744
|
+
<div class="scene-card" data-scene="init" onclick="filterByScene('init')">
|
|
745
|
+
<span class="scene-icon">🆕</span><span>首次使用</span>
|
|
746
|
+
</div>
|
|
747
|
+
<div class="scene-card" data-scene="analyze" onclick="filterByScene('analyze')">
|
|
748
|
+
<span class="scene-icon">🔍</span><span>开始分析</span>
|
|
749
|
+
</div>
|
|
750
|
+
<div class="scene-card" data-scene="dev" onclick="filterByScene('dev')">
|
|
751
|
+
<span class="scene-icon">▶️</span><span>开始开发</span>
|
|
752
|
+
</div>
|
|
753
|
+
<div class="scene-card" data-scene="finish" onclick="filterByScene('finish')">
|
|
754
|
+
<span class="scene-icon">✅</span><span>任务收尾</span>
|
|
755
|
+
</div>
|
|
756
|
+
<div class="scene-card" data-scene="daily" onclick="filterByScene('daily')">
|
|
757
|
+
<span class="scene-icon">📅</span><span>日常运维</span>
|
|
758
|
+
</div>
|
|
759
|
+
<div class="scene-card" data-scene="change" onclick="filterByScene('change')">
|
|
760
|
+
<span class="scene-icon">🔧</span><span>需求变更</span>
|
|
761
|
+
</div>
|
|
762
|
+
</div>
|
|
763
|
+
|
|
374
764
|
<div class="toolbar">
|
|
375
765
|
<input type="text" class="search-box" id="searchBox" placeholder="搜索提示词名称或内容...">
|
|
376
766
|
<div class="category-tabs">
|
|
@@ -380,6 +770,12 @@ function generatePromptsHtml(prompts) {
|
|
|
380
770
|
<button class="btn btn-primary" onclick="openCreateModal()">+ 新建</button>
|
|
381
771
|
</div>
|
|
382
772
|
|
|
773
|
+
<!-- 标签云 -->
|
|
774
|
+
<div class="tag-cloud collapsed" id="tagCloud">
|
|
775
|
+
<span class="tag-cloud-label">🏷️ 标签</span>
|
|
776
|
+
</div>
|
|
777
|
+
<div class="tag-toggle-bar" id="tagToggleBar"></div>
|
|
778
|
+
|
|
383
779
|
<div class="content" id="content">
|
|
384
780
|
<!-- 动态渲染 -->
|
|
385
781
|
</div>
|
|
@@ -432,9 +828,82 @@ function generatePromptsHtml(prompts) {
|
|
|
432
828
|
let currentCategory = 'all';
|
|
433
829
|
let searchQuery = '';
|
|
434
830
|
let editingId = null;
|
|
831
|
+
let currentScene = null;
|
|
832
|
+
let activeTag = null;
|
|
833
|
+
let tagCloudExpanded = false;
|
|
834
|
+
|
|
835
|
+
// 场景 → 标签映射
|
|
836
|
+
const sceneMap = {
|
|
837
|
+
init: ['首次使用', '初始化'],
|
|
838
|
+
analyze: ['分析阶段'],
|
|
839
|
+
dev: ['开发阶段'],
|
|
840
|
+
finish: ['收尾阶段'],
|
|
841
|
+
daily: ['日常', '进度追踪'],
|
|
842
|
+
change: ['变更阶段', '需求调整']
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
// 标签优先级:必要步骤 + 常用命令在前,不常用(收尾、断点等)在后
|
|
846
|
+
const TAG_PRIORITY = [
|
|
847
|
+
'首次使用', '初始化', '分析阶段', '开发阶段', '标准流程',
|
|
848
|
+
'迭代管理', '文档处理', '全局模式', '深度模式', '全自动',
|
|
849
|
+
'日常', '治理', '进度追踪', '变更阶段',
|
|
850
|
+
'收尾阶段', '断点续传', '批量执行', '迭代模式', '按需模式',
|
|
851
|
+
'定向拆分', '增量拆分', '代码提交', '任务管理', '需求调整',
|
|
852
|
+
'变更驱动', '质量门禁'
|
|
853
|
+
];
|
|
854
|
+
|
|
855
|
+
// 收集所有标签
|
|
856
|
+
function collectTags() {
|
|
857
|
+
const counts = {};
|
|
858
|
+
prompts.forEach(p => {
|
|
859
|
+
(p.tags || []).forEach(tag => {
|
|
860
|
+
counts[tag] = (counts[tag] || 0) + 1;
|
|
861
|
+
});
|
|
862
|
+
});
|
|
863
|
+
return Object.entries(counts).sort((a, b) => {
|
|
864
|
+
const pa = TAG_PRIORITY.indexOf(a[0]);
|
|
865
|
+
const pb = TAG_PRIORITY.indexOf(b[0]);
|
|
866
|
+
const ra = pa === -1 ? TAG_PRIORITY.length : pa;
|
|
867
|
+
const rb = pb === -1 ? TAG_PRIORITY.length : pb;
|
|
868
|
+
if (ra !== rb) return ra - rb;
|
|
869
|
+
return b[1] - a[1];
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// 渲染标签云
|
|
874
|
+
function renderTagCloud() {
|
|
875
|
+
const tags = collectTags();
|
|
876
|
+
const container = document.getElementById('tagCloud');
|
|
877
|
+
const toggleBar = document.getElementById('tagToggleBar');
|
|
878
|
+
if (!container) return;
|
|
879
|
+
|
|
880
|
+
const needsToggle = tags.length > 6;
|
|
881
|
+
container.className = 'tag-cloud ' + (tagCloudExpanded || !needsToggle ? 'expanded' : 'collapsed');
|
|
882
|
+
|
|
883
|
+
let html = '<span class="tag-cloud-label">🏷️ 标签</span>';
|
|
884
|
+
html += '<span class="tag' + (activeTag ? '' : ' active') + '" onclick="filterByTag(null)">全部</span>';
|
|
885
|
+
tags.forEach(([tag, count]) => {
|
|
886
|
+
const isActive = activeTag === tag;
|
|
887
|
+
html += \`<span class="tag \${isActive ? 'active' : ''}" onclick="filterByTag('\${tag}')">\${tag}<span class="tag-count">\${count}</span></span>\`;
|
|
888
|
+
});
|
|
889
|
+
container.innerHTML = html;
|
|
890
|
+
|
|
891
|
+
if (toggleBar) {
|
|
892
|
+
toggleBar.innerHTML = needsToggle
|
|
893
|
+
? \`<span class="tag-toggle" onclick="toggleTagCloud()">\${tagCloudExpanded ? '▲ 收起' : '▼ 展开更多标签'}</span>\`
|
|
894
|
+
: '';
|
|
895
|
+
toggleBar.style.display = needsToggle ? 'block' : 'none';
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function toggleTagCloud() {
|
|
900
|
+
tagCloudExpanded = !tagCloudExpanded;
|
|
901
|
+
renderTagCloud();
|
|
902
|
+
}
|
|
435
903
|
|
|
436
904
|
// 初始化
|
|
437
905
|
document.addEventListener('DOMContentLoaded', () => {
|
|
906
|
+
renderTagCloud();
|
|
438
907
|
render();
|
|
439
908
|
document.getElementById('searchBox').addEventListener('input', (e) => {
|
|
440
909
|
searchQuery = e.target.value.toLowerCase();
|
|
@@ -450,14 +919,34 @@ function generatePromptsHtml(prompts) {
|
|
|
450
919
|
});
|
|
451
920
|
});
|
|
452
921
|
|
|
922
|
+
function filterByScene(scene) {
|
|
923
|
+
if (currentScene === scene) {
|
|
924
|
+
currentScene = null;
|
|
925
|
+
} else {
|
|
926
|
+
currentScene = scene;
|
|
927
|
+
}
|
|
928
|
+
document.querySelectorAll('.scene-card').forEach(c => {
|
|
929
|
+
c.classList.toggle('active', c.dataset.scene === currentScene);
|
|
930
|
+
});
|
|
931
|
+
render();
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function filterByTag(tag) {
|
|
935
|
+
activeTag = tag;
|
|
936
|
+
renderTagCloud();
|
|
937
|
+
render();
|
|
938
|
+
}
|
|
939
|
+
|
|
453
940
|
function render() {
|
|
454
941
|
const filtered = prompts.filter(p => {
|
|
455
942
|
const matchCategory = currentCategory === 'all' || p.category === currentCategory;
|
|
456
|
-
const matchSearch = !searchQuery ||
|
|
943
|
+
const matchSearch = !searchQuery ||
|
|
457
944
|
p.name.toLowerCase().includes(searchQuery) ||
|
|
458
945
|
p.description.toLowerCase().includes(searchQuery) ||
|
|
459
946
|
p.prompt.toLowerCase().includes(searchQuery);
|
|
460
|
-
|
|
947
|
+
const matchScene = !currentScene || (p.tags || []).some(t => sceneMap[currentScene].includes(t));
|
|
948
|
+
const matchTag = !activeTag || (p.tags || []).includes(activeTag);
|
|
949
|
+
return matchCategory && matchSearch && matchScene && matchTag;
|
|
461
950
|
});
|
|
462
951
|
|
|
463
952
|
const builtin = filtered.filter(p => p.builtin);
|
|
@@ -486,11 +975,49 @@ function generatePromptsHtml(prompts) {
|
|
|
486
975
|
document.getElementById('content').innerHTML = html;
|
|
487
976
|
}
|
|
488
977
|
|
|
978
|
+
// 生成带参数的 Skill 命令预览(如 /spec-execute --task {任务ID} -i {迭代名})
|
|
979
|
+
function skillCmdPreview(p) {
|
|
980
|
+
if (!p.skill) return '';
|
|
981
|
+
const args = (p.params || []).map(param => {
|
|
982
|
+
return param.flag ? (param.flag + ' {' + param.key + '}') : ('{' + param.key + '}');
|
|
983
|
+
});
|
|
984
|
+
return '/' + p.skill + (args.length ? ' ' + args.join(' ') : '');
|
|
985
|
+
}
|
|
986
|
+
|
|
489
987
|
function renderCard(p) {
|
|
490
|
-
const badge = p.builtin
|
|
988
|
+
const badge = p.builtin
|
|
491
989
|
? '<span class="badge badge-builtin">预置</span>'
|
|
492
990
|
: '<span class="badge badge-custom">自定义</span>';
|
|
493
|
-
|
|
991
|
+
|
|
992
|
+
const hasCommand = p.command && p.command.trim();
|
|
993
|
+
const cmdBlock = hasCommand
|
|
994
|
+
? \`<div class="prompt-section">⌨️ 触发命令</div><div class="prompt-cmd">\${escapeHtml(p.command)}</div>\`
|
|
995
|
+
: '';
|
|
996
|
+
|
|
997
|
+
const copyCmdBtn = hasCommand
|
|
998
|
+
? \`<button class="btn btn-cmd" onclick="copyCommand('\${p.id}')">⌨️ 复制命令</button>\`
|
|
999
|
+
: '';
|
|
1000
|
+
|
|
1001
|
+
const copySkillBtn = p.skill
|
|
1002
|
+
? \`<button class="btn btn-skill" onclick="copySkill('\${p.id}')">⚡ 复制 Skill</button>\`
|
|
1003
|
+
: '';
|
|
1004
|
+
|
|
1005
|
+
const tagsHtml = (p.tags || []).length > 0
|
|
1006
|
+
? \`<div class="prompt-tags">\${p.tags.map(t => \`<span class="prompt-tag">\${t}</span>\`).join('')}</div>\`
|
|
1007
|
+
: '';
|
|
1008
|
+
|
|
1009
|
+
const env = p.env || (hasCommand ? 'both' : 'ai');
|
|
1010
|
+
const envHintMap = {
|
|
1011
|
+
cli: \`<div class="env-hint cli">💡 纯 CLI 命令(终端直接执行,无 AI 参与)</div>\`,
|
|
1012
|
+
both: \`<div class="env-hint cli">💡 终端 / AI 均可用(AI 中使用有 AI 参与,终端中直接执行)</div>\`,
|
|
1013
|
+
ai: \`<div class="env-hint ai">💡 仅 AI 使用(在 AI 对话框输入,需 AI 参与)</div>\`
|
|
1014
|
+
};
|
|
1015
|
+
const envHint = envHintMap[env] || envHintMap.ai;
|
|
1016
|
+
|
|
1017
|
+
const examplesHtml = (p.examples || []).length > 0
|
|
1018
|
+
? \`<div class="prompt-examples"><div class="prompt-examples-title">💡 典型场景</div><ul>\${p.examples.map(e => \`<li>\${escapeHtml(e)}</li>\`).join('')}</ul></div>\`
|
|
1019
|
+
: '';
|
|
1020
|
+
|
|
494
1021
|
return \`
|
|
495
1022
|
<div class="prompt-card">
|
|
496
1023
|
<div class="prompt-header">
|
|
@@ -500,9 +1027,19 @@ function generatePromptsHtml(prompts) {
|
|
|
500
1027
|
<p>\${p.description || ''}</p>
|
|
501
1028
|
</div>
|
|
502
1029
|
</div>
|
|
503
|
-
|
|
1030
|
+
\${tagsHtml}
|
|
1031
|
+
\${envHint}
|
|
1032
|
+
\${examplesHtml}
|
|
1033
|
+
<div class="prompt-section">💬 AI 说法</div>
|
|
1034
|
+
<div class="prompt-say">
|
|
1035
|
+
\${escapeHtml(p.prompt)}
|
|
1036
|
+
\${p.skill ? \`<div class="prompt-skill">快捷:<span class="prompt-skill-tag" onclick="copySkill('\${p.id}')">\${skillCmdPreview(p)}</span></div>\` : ''}
|
|
1037
|
+
</div>
|
|
1038
|
+
\${cmdBlock}
|
|
504
1039
|
<div class="prompt-actions">
|
|
505
|
-
<button class="btn btn-
|
|
1040
|
+
<button class="btn btn-say" onclick="copyPrompt('\${p.id}')">💬 复制说法</button>
|
|
1041
|
+
\${copySkillBtn}
|
|
1042
|
+
\${copyCmdBtn}
|
|
506
1043
|
\${!p.builtin ? \`
|
|
507
1044
|
<button class="btn btn-secondary" onclick="editPrompt('\${p.id}')">编辑</button>
|
|
508
1045
|
<button class="btn btn-secondary" onclick="deletePrompt('\${p.id}')">删除</button>
|
|
@@ -522,20 +1059,43 @@ function generatePromptsHtml(prompts) {
|
|
|
522
1059
|
const p = prompts.find(x => x.id === id);
|
|
523
1060
|
if (!p) return;
|
|
524
1061
|
|
|
525
|
-
// 如果有参数,显示自定义弹框
|
|
526
1062
|
if (p.params && p.params.length > 0) {
|
|
527
|
-
showParamModal(p);
|
|
1063
|
+
showParamModal(p, 'say');
|
|
528
1064
|
} else {
|
|
529
|
-
// 无参数,直接复制
|
|
530
1065
|
const text = '/spec-ask "' + p.prompt + '"';
|
|
531
|
-
copyToClipboard(text);
|
|
1066
|
+
copyToClipboard(text, '💬 AI 说法已复制');
|
|
532
1067
|
}
|
|
533
1068
|
}
|
|
534
1069
|
|
|
535
|
-
function
|
|
536
|
-
|
|
1070
|
+
function copyCommand(id) {
|
|
1071
|
+
const p = prompts.find(x => x.id === id);
|
|
1072
|
+
if (!p || !p.command) return;
|
|
1073
|
+
|
|
1074
|
+
if (p.params && p.params.length > 0) {
|
|
1075
|
+
showParamModal(p, 'cmd');
|
|
1076
|
+
} else {
|
|
1077
|
+
copyToClipboard(p.command, '⌨️ 命令已复制');
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function copySkill(id) {
|
|
1082
|
+
const p = prompts.find(x => x.id === id);
|
|
1083
|
+
if (!p || !p.skill) return;
|
|
1084
|
+
|
|
1085
|
+
if (p.params && p.params.length > 0) {
|
|
1086
|
+
showParamModal(p, 'skill');
|
|
1087
|
+
} else {
|
|
1088
|
+
const text = p.prompt ? '/' + p.skill + ' ' + p.prompt : '/' + p.skill;
|
|
1089
|
+
copyToClipboard(text, '⚡ Skill 命令已复制');
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
function showParamModal(p, mode) {
|
|
1094
|
+
const isSay = mode === 'say';
|
|
1095
|
+
const isSkill = mode === 'skill';
|
|
537
1096
|
const modal = document.createElement('div');
|
|
538
1097
|
modal.className = 'modal active';
|
|
1098
|
+
modal.dataset.mode = mode;
|
|
539
1099
|
modal.innerHTML = \`
|
|
540
1100
|
<div class="modal-content" style="max-width: 600px;">
|
|
541
1101
|
<div class="modal-header">
|
|
@@ -547,96 +1107,134 @@ function generatePromptsHtml(prompts) {
|
|
|
547
1107
|
\${p.params.map(param => \`
|
|
548
1108
|
<div class="form-group">
|
|
549
1109
|
<label>\${param.key} \${param.required ? '<span style="color: #e03131;">*</span>' : ''}</label>
|
|
550
|
-
<input type="text" id="param-\${param.key}" placeholder="\${param.placeholder || ''}" value="\${param.placeholder || ''}" oninput="updatePreview('\${p.id}')">
|
|
1110
|
+
<input type="text" id="param-\${param.key}" placeholder="\${param.placeholder || ''}" value="\${param.placeholder || ''}" oninput="updatePreview('\${p.id}', '\${mode}')">
|
|
551
1111
|
</div>
|
|
552
1112
|
\`).join('')}
|
|
553
1113
|
<div class="form-group" style="margin-top: 20px;">
|
|
554
|
-
<label style="font-weight: 600; color: #495057;"
|
|
555
|
-
<div id="preview-box" style="
|
|
556
|
-
|
|
1114
|
+
<label style="font-weight: 600; color: #495057;">\${isSay ? '💬 预览(AI 说法)' : isSkill ? '⚡ 预览(Skill 命令)' : '⌨️ 预览(触发命令)'}</label>
|
|
1115
|
+
<div id="preview-box" style="\${isSay
|
|
1116
|
+
? 'background: #e7f5ff; border-radius: 8px; padding: 12px; font-size: 13px; color: #1864ab; line-height: 1.6; min-height: 60px; border: 1px solid #74c0fc;'
|
|
1117
|
+
: isSkill
|
|
1118
|
+
? 'background: #f3f0ff; border-radius: 8px; padding: 12px; font-size: 13px; color: #7048e8; line-height: 1.6; min-height: 60px; border: 1px solid #d0bfff; font-family: monospace;'
|
|
1119
|
+
: 'background: #212529; border-radius: 8px; padding: 12px; font-size: 12px; color: #69db7c; line-height: 1.6; min-height: 60px; border: 1px solid #495057; font-family: monospace;'
|
|
1120
|
+
}">
|
|
1121
|
+
\${escapeHtml(isSay ? p.prompt : isSkill ? skillCmdPreview(p) : (p.command || ''))}
|
|
557
1122
|
</div>
|
|
558
1123
|
</div>
|
|
559
1124
|
</div>
|
|
560
1125
|
<div class="modal-footer">
|
|
561
1126
|
<button class="btn btn-secondary" onclick="this.closest('.modal').remove()">取消</button>
|
|
562
|
-
<button class="btn btn-
|
|
1127
|
+
<button class="btn \${isSay ? 'btn-say' : isSkill ? 'btn-skill' : 'btn-cmd'}" onclick="submitParams('\${p.id}', '\${mode}')">\${isSay ? '💬 复制说法' : isSkill ? '⚡ 复制 Skill' : '⌨️ 复制命令'}</button>
|
|
563
1128
|
</div>
|
|
564
1129
|
</div>
|
|
565
1130
|
\`;
|
|
566
1131
|
document.body.appendChild(modal);
|
|
567
1132
|
|
|
568
|
-
// 聚焦第一个输入框
|
|
569
1133
|
setTimeout(() => {
|
|
1134
|
+
updatePreview(p.id, mode);
|
|
570
1135
|
const firstInput = modal.querySelector('input');
|
|
571
1136
|
if (firstInput) firstInput.focus();
|
|
572
1137
|
}, 100);
|
|
573
1138
|
}
|
|
574
1139
|
|
|
575
|
-
function updatePreview(id) {
|
|
1140
|
+
function updatePreview(id, mode) {
|
|
576
1141
|
const p = prompts.find(x => x.id === id);
|
|
577
1142
|
if (!p) return;
|
|
578
1143
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
1144
|
+
const isSay = mode === 'say';
|
|
1145
|
+
const isSkill = mode === 'skill';
|
|
1146
|
+
let text = isSay ? p.prompt : isSkill ? ('/' + p.skill) : (p.command || '');
|
|
1147
|
+
|
|
1148
|
+
if (isSkill) {
|
|
1149
|
+
const args = [];
|
|
1150
|
+
p.params.forEach(param => {
|
|
1151
|
+
const input = document.getElementById('param-' + param.key);
|
|
1152
|
+
const value = input ? input.value.trim() : '';
|
|
1153
|
+
if (value) args.push(param.flag ? (param.flag + ' ' + value) : value);
|
|
1154
|
+
});
|
|
1155
|
+
if (args.length > 0) text += ' ' + args.join(' ');
|
|
1156
|
+
} else {
|
|
1157
|
+
p.params.forEach(param => {
|
|
1158
|
+
const input = document.getElementById('param-' + param.key);
|
|
1159
|
+
const value = input ? input.value.trim() : '';
|
|
1160
|
+
if (value) {
|
|
1161
|
+
text = text.replace(new RegExp('\\{' + param.key + '\\}', 'g'), value);
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
587
1165
|
|
|
588
1166
|
const previewBox = document.getElementById('preview-box');
|
|
589
1167
|
if (previewBox) {
|
|
590
|
-
|
|
1168
|
+
if (isSay) {
|
|
1169
|
+
previewBox.innerHTML = '<strong>/spec-ask "</strong>' + escapeHtml(text) + '<strong>"</strong>';
|
|
1170
|
+
} else {
|
|
1171
|
+
previewBox.textContent = text;
|
|
1172
|
+
}
|
|
591
1173
|
}
|
|
592
1174
|
}
|
|
593
1175
|
|
|
594
|
-
function submitParams(id) {
|
|
1176
|
+
function submitParams(id, mode) {
|
|
595
1177
|
const p = prompts.find(x => x.id === id);
|
|
596
1178
|
if (!p) return;
|
|
597
1179
|
|
|
598
|
-
|
|
1180
|
+
const isSay = mode === 'say';
|
|
1181
|
+
const isSkill = mode === 'skill';
|
|
1182
|
+
let text = isSay ? p.prompt : isSkill ? ('/' + p.skill) : (p.command || '');
|
|
599
1183
|
let allFilled = true;
|
|
600
1184
|
|
|
601
|
-
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
1185
|
+
if (isSkill) {
|
|
1186
|
+
const args = [];
|
|
1187
|
+
p.params.forEach(param => {
|
|
1188
|
+
const input = document.getElementById('param-' + param.key);
|
|
1189
|
+
const value = input ? input.value.trim() : '';
|
|
1190
|
+
|
|
1191
|
+
if (param.required && !value) {
|
|
1192
|
+
allFilled = false;
|
|
1193
|
+
input.style.borderColor = '#e03131';
|
|
1194
|
+
} else {
|
|
1195
|
+
input.style.borderColor = '#e9ecef';
|
|
1196
|
+
if (value) args.push(param.flag ? (param.flag + ' ' + value) : value);
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
if (args.length > 0) text += ' ' + args.join(' ');
|
|
1200
|
+
} else {
|
|
1201
|
+
p.params.forEach(param => {
|
|
1202
|
+
const input = document.getElementById('param-' + param.key);
|
|
1203
|
+
const value = input ? input.value.trim() : '';
|
|
1204
|
+
|
|
1205
|
+
if (param.required && !value) {
|
|
1206
|
+
allFilled = false;
|
|
1207
|
+
input.style.borderColor = '#e03131';
|
|
1208
|
+
} else {
|
|
1209
|
+
input.style.borderColor = '#e9ecef';
|
|
1210
|
+
text = text.replace(new RegExp('\\{' + param.key + '\\}', 'g'), value);
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
613
1214
|
|
|
614
1215
|
if (!allFilled) {
|
|
615
1216
|
showToast('⚠️ 请填写必填参数');
|
|
616
1217
|
return;
|
|
617
1218
|
}
|
|
618
1219
|
|
|
619
|
-
// 关闭弹框
|
|
620
1220
|
const modal = document.querySelector('.modal.active');
|
|
621
1221
|
if (modal) modal.remove();
|
|
622
1222
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
copyToClipboard(finalText);
|
|
1223
|
+
const finalText = isSay ? ('/spec-ask "' + text + '"') : text;
|
|
1224
|
+
copyToClipboard(finalText, isSay ? '💬 AI 说法已复制' : isSkill ? '⚡ Skill 命令已复制' : '⌨️ 命令已复制');
|
|
626
1225
|
}
|
|
627
1226
|
|
|
628
|
-
function copyToClipboard(text) {
|
|
1227
|
+
function copyToClipboard(text, msg) {
|
|
629
1228
|
navigator.clipboard.writeText(text).then(() => {
|
|
630
|
-
showToast('✅ 已复制到剪贴板');
|
|
1229
|
+
showToast(msg || '✅ 已复制到剪贴板');
|
|
631
1230
|
}).catch(() => {
|
|
632
|
-
// 降级方案
|
|
633
1231
|
const textarea = document.createElement('textarea');
|
|
634
1232
|
textarea.value = text;
|
|
635
1233
|
document.body.appendChild(textarea);
|
|
636
1234
|
textarea.select();
|
|
637
1235
|
document.execCommand('copy');
|
|
638
1236
|
document.body.removeChild(textarea);
|
|
639
|
-
showToast('✅ 已复制到剪贴板');
|
|
1237
|
+
showToast(msg || '✅ 已复制到剪贴板');
|
|
640
1238
|
});
|
|
641
1239
|
}
|
|
642
1240
|
|