dsh-prompt 0.1.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.
package/lib/client.js ADDED
@@ -0,0 +1,2647 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-prompt",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region src/client/templates.ts
8
+ const PRESET_TEMPLATES = [
9
+ {
10
+ id: "fp",
11
+ name: "第一性原理拆解",
12
+ nameEn: "First Principles",
13
+ domain: "思考框架",
14
+ stage: "执行前",
15
+ action: ["拆解"],
16
+ body: [
17
+ "请用第一性原理分析以下主题,按编号结构化输出,结论先行:",
18
+ "1. 这个问题的基本组成单元是什么?",
19
+ "2. 哪些是真正不可改变的真理(而非假设)?",
20
+ "3. 如果去掉所有既有假设,问题本身还剩下什么?",
21
+ "4. 有哪些被大多数人默认为常识、但其实可以颠覆的?",
22
+ "5. 基于以上推演给出你的原创见解,并说明它与主流看法的差异。",
23
+ "主题:"
24
+ ].join("\n")
25
+ },
26
+ {
27
+ id: "socratic",
28
+ name: "苏格拉底式追问",
29
+ nameEn: "Socratic Questioning",
30
+ domain: "思考框架",
31
+ stage: "执行前",
32
+ action: ["检验"],
33
+ body: [
34
+ "请用苏格拉底式追问帮助我思考,按编号结构化输出:",
35
+ "1. 先复述我当前的观点或核心假设;",
36
+ "2. 提出 3 个最有力的质疑;",
37
+ "3. 找出一个我尚未考虑到的视角;",
38
+ "4. 如果你是深思熟虑的对话者,你会问我哪几个我没想到的问题?",
39
+ "5. 追问结束后,把整个过程浓缩成一个更精准的问题。",
40
+ "主题:"
41
+ ].join("\n")
42
+ },
43
+ {
44
+ id: "deep",
45
+ name: "深度多维分析",
46
+ nameEn: "Deep Analysis",
47
+ domain: "思考框架",
48
+ stage: "执行前",
49
+ action: ["归因"],
50
+ body: [
51
+ "请从多个维度深度分析以下主题,按编号结构化输出,结论先行:",
52
+ "1. 本质是什么?",
53
+ "2. 有哪些常见的误解?",
54
+ "3. 支持和反对的证据分别是什么?",
55
+ "4. 这个问题的边界在哪里(什么情况下它不再成立)?",
56
+ "5. 如果你必须做出一个判断,会是什么?",
57
+ "6. 你最强的那个结论,弱点在哪里?",
58
+ "主题:"
59
+ ].join("\n")
60
+ },
61
+ {
62
+ id: "adversarial",
63
+ name: "对抗式审查",
64
+ nameEn: "Adversarial Review",
65
+ domain: "思考框架",
66
+ stage: "执行前",
67
+ action: ["检验"],
68
+ body: [
69
+ "请对以下方案/结论做对抗式审查(红队角色),先给一段结论摘要,再按编号展开:",
70
+ "1. 找出其中最强的 5 个假设,逐一论证它们可能如何失效;",
71
+ "2. 构造最可能推翻它的 3 个反例或失败场景;",
72
+ "3. 指出其中隐藏的认知偏差与利益相关方视角的盲区;",
73
+ "4. 给出「如果这个方案失败,最可能的原因是什么」的排序清单;",
74
+ "5. 最后给出:在何种条件下你才会认为它是错的——以及何种证据能让你改变立场。",
75
+ "方案 / 结论:"
76
+ ].join("\n")
77
+ },
78
+ {
79
+ id: "decision",
80
+ name: "决策博弈分析",
81
+ nameEn: "Decision Analysis",
82
+ domain: "思考框架",
83
+ stage: "执行前",
84
+ action: ["决策"],
85
+ body: [
86
+ "你是一位冷静的决策分析师。请分析我的处境,按编号结构化输出(你的选择仅作参考,重点是分析过程):",
87
+ "1. 列出每个选项最坏的结果是什么;",
88
+ "2. 每个选项最好的结果是什么;",
89
+ "3. 最可能的结果是什么;",
90
+ "4. 如果 10 年后回头看,我可能会后悔什么;",
91
+ "5. 有没有哪条路是「没有回头路」的;",
92
+ "6. 给出你本人的选择并说明理由;再指出我可能忽略的第三个选项。",
93
+ "我的处境(选项与背景):"
94
+ ].join("\n")
95
+ },
96
+ {
97
+ id: "premortem",
98
+ name: "事前验尸",
99
+ nameEn: "Pre-mortem",
100
+ domain: "思考框架",
101
+ stage: "执行前",
102
+ action: ["决策"],
103
+ body: [
104
+ "假设以下方案/项目已经在一年后彻底失败。请以「事后回溯」的方式写失败分析,按编号结构化输出:",
105
+ "1. 按可能性从高到低列出 8 个失败原因;",
106
+ "2. 对前 3 个原因,给出现在就能做的预防措施;",
107
+ "3. 指出哪些早期信号现在就该监控;",
108
+ "4. 最后说明:这个方案是否从一开始就有致命缺陷。",
109
+ "方案描述:"
110
+ ].join("\n")
111
+ },
112
+ {
113
+ id: "feynman",
114
+ name: "费曼技巧",
115
+ nameEn: "Feynman Technique",
116
+ domain: "思考框架",
117
+ stage: "执行后",
118
+ action: ["理解"],
119
+ body: [
120
+ "请用费曼技巧确认我是否真正理解了以下概念,按编号结构化输出:",
121
+ "1. 用最简单的语言、面向完全不懂的人解释这个概念(禁止术语);",
122
+ "2. 给出 2 个生动类比和 2 个反例;",
123
+ "3. 指出我理解中的错误或不准确之处;",
124
+ "4. 设计 3 个判断题检验我是否真的懂了。",
125
+ "概念:",
126
+ "我的理解(可选):"
127
+ ].join("\n")
128
+ },
129
+ {
130
+ id: "fivewhys",
131
+ name: "五个为什么",
132
+ nameEn: "Five Whys",
133
+ domain: "思考框架",
134
+ stage: "执行中",
135
+ action: ["归因"],
136
+ body: ["针对以下问题,请连续追问五个「为什么」,每层基于上一层的事实回答,最后区分根本原因与表层触发因素并给出解决方向:", "问题描述:"].join("\n")
137
+ },
138
+ {
139
+ id: "mece",
140
+ name: "MECE 拆解",
141
+ nameEn: "MECE Decomposition",
142
+ domain: "思考框架",
143
+ stage: "执行前",
144
+ action: ["拆解"],
145
+ body: [
146
+ "请用 MECE 原则拆解以下问题,按编号结构化输出:",
147
+ "1. 给出 3~6 个相互独立(Mutually Exclusive)的分类维度;",
148
+ "2. 每个维度下列出所有子项,确保完全穷尽(Collectively Exhaustive);",
149
+ "3. 指出拆解中可能的遗漏与重叠;",
150
+ "4. 给出按重要性排序后的关键子问题清单。",
151
+ "问题:"
152
+ ].join("\n")
153
+ },
154
+ {
155
+ id: "bias",
156
+ name: "认知偏差检查",
157
+ nameEn: "Cognitive Bias Check",
158
+ domain: "思考框架",
159
+ stage: "执行前",
160
+ action: ["检验"],
161
+ body: [
162
+ "请审查以下推理/计划/结论中的认知偏差,按编号结构化输出:",
163
+ "1. 列出可能影响它的偏差(确认偏误、沉没成本、可得性、锚定、乐观/悲观偏差、群体思维等),并指出具体证据;",
164
+ "2. 对每条偏差,给出「如何检验是否真的受影响」的方法;",
165
+ "3. 给出一个反事实检验:如果结论被证明是错的,最可能因为哪条偏差?",
166
+ "推理 / 计划 / 结论:"
167
+ ].join("\n")
168
+ },
169
+ {
170
+ id: "learnpath",
171
+ name: "学习路线设计",
172
+ nameEn: "Learning Path",
173
+ domain: "学习",
174
+ stage: "执行前",
175
+ action: ["路线"],
176
+ body: [
177
+ "请为以下领域/技能设计一条从零到可实战的学习路线,按编号结构化输出:",
178
+ "1. 按阶段拆分(入门→进阶→实战),每个阶段给出学习目标;",
179
+ "2. 每个阶段推荐 2~3 个核心主题和练习方式;",
180
+ "3. 指出常见的「伪学习陷阱」(只收藏不练习等);",
181
+ "4. 给出一个 30 天内可完成的最小闭环,以及检验掌握程度的基准。",
182
+ "领域 / 技能:",
183
+ "当前水平(可选):",
184
+ "目标(可选):"
185
+ ].join("\n")
186
+ },
187
+ {
188
+ id: "concept",
189
+ name: "概念澄清",
190
+ nameEn: "Concept Clarification",
191
+ domain: "学习",
192
+ stage: "执行前",
193
+ action: ["概念"],
194
+ body: [
195
+ "请帮我彻底理解以下概念,按编号结构化输出:",
196
+ "1. 一句话定义 + 一个最小例子;",
197
+ "2. 它与常见相近概念的本质区别(各给一个对比场景);",
198
+ "3. 这个概念在什么情况下不适用(边界);",
199
+ "4. 给出 3 个由浅入深的练习场景让我检验理解。",
200
+ "概念:",
201
+ "相近概念(可选,不知道可留空,AI 自行选常见相近概念对比):"
202
+ ].join("\n")
203
+ },
204
+ {
205
+ id: "quiz",
206
+ name: "AI 反向考验",
207
+ nameEn: "AI Quiz",
208
+ domain: "学习",
209
+ stage: "执行后",
210
+ action: ["考验"],
211
+ body: [
212
+ "请通过出题检验我是否真正理解了以下主题/交付内容:",
213
+ "1. 设计选择题/判断题/简答题/案例题(由浅入深);",
214
+ "2. 我作答后指出错误与盲区;",
215
+ "3. 直到回答正确、知识点无盲区后再确认交付。",
216
+ "主题 / 交付内容:"
217
+ ].join("\n")
218
+ },
219
+ {
220
+ id: "codereview",
221
+ name: "代码审查",
222
+ nameEn: "Code Review",
223
+ domain: "工程",
224
+ stage: "执行前",
225
+ action: ["审查"],
226
+ body: [
227
+ "请审查以下代码,先给总体结论,再按严重程度排序输出,每个问题附具体修改建议(含示例代码):",
228
+ "1. 正确性:潜在 bug、边界条件、并发/异步问题;",
229
+ "2. 安全:注入、鉴权、敏感信息、依赖风险;",
230
+ "3. 可维护性:命名、结构、重复、可读性;",
231
+ "4. 性能:明显的低效点。",
232
+ "代码过长时可分段审查,但最终汇总为一页结论。",
233
+ "代码 / 文件 / 技术栈:"
234
+ ].join("\n")
235
+ },
236
+ {
237
+ id: "testdesign",
238
+ name: "测试用例设计",
239
+ nameEn: "Test Design",
240
+ domain: "工程",
241
+ stage: "执行中",
242
+ action: ["测试"],
243
+ body: [
244
+ "请为以下功能设计测试用例,按编号结构化输出:",
245
+ "1. 正常路径用例(含输入/预期输出);",
246
+ "2. 边界与极端值用例;",
247
+ "3. 异常与错误处理用例;",
248
+ "4. 并发/重复/幂等场景(如适用);",
249
+ "5. 指出哪些用例最容易漏、以及对应的风险。",
250
+ "功能描述:"
251
+ ].join("\n")
252
+ },
253
+ {
254
+ id: "refactor",
255
+ name: "重构方案",
256
+ nameEn: "Refactoring Plan",
257
+ domain: "工程",
258
+ stage: "执行中",
259
+ action: ["重构"],
260
+ body: [
261
+ "请为以下代码提出重构方案,按编号结构化输出:",
262
+ "1. 指出当前结构的主要问题(职责、耦合、可测试性);",
263
+ "2. 给出目标结构(抽象与拆分建议,含示例);",
264
+ "3. 按「小步重构」顺序列出步骤,每一步可独立提交且行为不变;",
265
+ "4. 指出哪些重构有风险、需要测试保护。",
266
+ "代码 / 模块:"
267
+ ].join("\n")
268
+ },
269
+ {
270
+ id: "explain",
271
+ name: "解释代码",
272
+ nameEn: "Explain Code",
273
+ domain: "工程",
274
+ stage: "执行中",
275
+ action: ["解读"],
276
+ body: [
277
+ "请解释以下代码,按编号结构化输出:",
278
+ "1. 这段代码做什么(一句话);",
279
+ "2. 逐段讲解执行流程与关键数据结构;",
280
+ "3. 指出其中「为什么这样写」的设计取舍;",
281
+ "4. 指出潜在问题与改进点;",
282
+ "5. 用一句话概括它在我更大的系统里扮演的角色。",
283
+ "代码:"
284
+ ].join("\n")
285
+ },
286
+ {
287
+ id: "blindspot",
288
+ name: "盲区检查",
289
+ nameEn: "Blind Spot Pass",
290
+ domain: "执行",
291
+ stage: "执行前",
292
+ action: ["启动"],
293
+ body: [
294
+ "在我开始以下任务之前,请帮我做一次盲区检查,按编号结构化输出:",
295
+ "1. 我可能遗漏的关键变量有哪些?",
296
+ "2. 潜在的风险与最容易忽略的影响因素?",
297
+ "3. 哪些点需要提前决策、哪些可以后置?",
298
+ "4. 给我一个 3 分钟内可完成的快速检查清单。",
299
+ "我的计划(目标 / 约束):"
300
+ ].join("\n")
301
+ },
302
+ {
303
+ id: "snapshot",
304
+ name: "零丢失快照",
305
+ nameEn: "Zero-loss Snapshot",
306
+ domain: "执行",
307
+ stage: "执行中",
308
+ action: ["固化"],
309
+ body: [
310
+ "请执行一次「零丢失快照」:",
311
+ "1. 把我们从会话开始至今的关键信息,按「目的地 / 约束与偏好 / 已确认的决定 / 待决问题 / 雾区(隐约可见但还不清晰)」五类逐条列出——不压缩、不合并,宁可啰嗦不可省略;",
312
+ "2. 每条标注出处(引用我原话);",
313
+ "3. 单独列一节「可疑遗漏」:凡是我提过但你觉得与主线无关、太模糊或像执行细节而没纳入的,全部摆出并写明理由;",
314
+ "4. 列完后停下等我逐条核对,确认后再落盘。"
315
+ ].join("\n")
316
+ },
317
+ {
318
+ id: "kickoff",
319
+ name: "任务启动",
320
+ nameEn: "Task Kickoff",
321
+ domain: "执行",
322
+ stage: "执行前",
323
+ action: ["启动"],
324
+ body: [
325
+ "在开始以下任务之前,请先确认起点并澄清细节,按编号结构化输出:",
326
+ "1. 最终目标(要做什么);",
327
+ "2. 当前进度(目前想到哪一步);",
328
+ "3. 经验水平(新手 / 有经验 / 专家);",
329
+ "4. 不熟悉的地方;",
330
+ "5. 成功的标准;",
331
+ "6. 通过提问澄清细节(成功标准之外还有哪些约束、有哪些风险现在就该知道)。",
332
+ "最终目标:",
333
+ "当前进度(可选):",
334
+ "经验水平(新手 / 有经验 / 专家):",
335
+ "不熟悉的地方(可选):"
336
+ ].join("\n")
337
+ },
338
+ {
339
+ id: "prototype",
340
+ name: "原型验证",
341
+ nameEn: "Prototype Validate",
342
+ domain: "执行",
343
+ stage: "执行前",
344
+ action: ["启动"],
345
+ body: [
346
+ "请为以下主题/想法做原型验证,每轮输出保持克制(方向概要即可,不要太长):",
347
+ "1. 给出 2~4 个方向差距很大的候选方案;",
348
+ "2. 每个方向给出最小的验证方式(怎么快速试错);",
349
+ "3. 由我选择和反馈,几轮后逐步收敛到满意的方向;",
350
+ "4. 收敛后给出下一步的执行计划要点。",
351
+ "主题 / 想法:"
352
+ ].join("\n")
353
+ },
354
+ {
355
+ id: "plan",
356
+ name: "执行计划",
357
+ nameEn: "Execution Plan",
358
+ domain: "执行",
359
+ stage: "执行前",
360
+ action: ["启动"],
361
+ body: [
362
+ "请为以下目标制定执行计划,按编号结构化输出(时间安排是可调整的草案):",
363
+ "1. 明确步骤(含顺序与依赖);",
364
+ "2. 时间安排(每步预计时长/里程碑);",
365
+ "3. 所需资源和工具;",
366
+ "4. 交付物和验收标准;",
367
+ "5. 风险和应对方案。",
368
+ "目标:",
369
+ "约束(可选):"
370
+ ].join("\n")
371
+ },
372
+ {
373
+ id: "deviation",
374
+ name: "记录偏离",
375
+ nameEn: "Deviation Log",
376
+ domain: "执行",
377
+ stage: "执行中",
378
+ action: ["记录"],
379
+ body: [
380
+ "请担任我的过程记录员(多轮持续角色):从现在起,在每次交互后更新一份简洁的偏离记录,按编号结构化:",
381
+ "1. 遇到了什么新的情况?",
382
+ "2. 为什么改变了方案?",
383
+ "3. 做了哪些关键决定?",
384
+ "4. 对结果有什么影响?",
385
+ "每次只输出「新增/变更」条目,不要重复已记录内容。"
386
+ ].join("\n")
387
+ },
388
+ {
389
+ id: "retro",
390
+ name: "过程复盘",
391
+ nameEn: "Retrospective",
392
+ domain: "执行",
393
+ stage: "执行后",
394
+ action: ["复盘"],
395
+ body: [
396
+ "请帮我复盘刚刚完成的以下任务/项目,按编号结构化输出:",
397
+ "1. 整体步骤回顾(实际 vs 计划的差异);",
398
+ "2. 使用的资料和工具;",
399
+ "3. 遇到的困难;",
400
+ "4. 解决思路与可复用经验;",
401
+ "5. 如果重来一次,哪些地方会做得不同?",
402
+ "任务 / 项目:"
403
+ ].join("\n")
404
+ }
405
+ ];
406
+ PRESET_TEMPLATES.forEach((t) => {
407
+ t.builtin = true;
408
+ });
409
+ function getPresetById(id) {
410
+ return PRESET_TEMPLATES.find((t) => t.id === id);
411
+ }
412
+ //#endregion
413
+ //#region src/client/store.ts
414
+ const CUSTOMS_KEY = "dsh.prompt.customs";
415
+ const USAGE_KEY = "dsh.prompt.usage";
416
+ const PINNED_KEY = "dsh.prompt.pinned";
417
+ const LAST_USED_KEY = "dsh.prompt.lastUsed";
418
+ const MAX_PIN = 5;
419
+ function ls() {
420
+ try {
421
+ return typeof globalThis !== "undefined" && globalThis.localStorage ? globalThis.localStorage : null;
422
+ } catch (e) {
423
+ return null;
424
+ }
425
+ }
426
+ function loadJSON(key, fallback) {
427
+ try {
428
+ const s = ls();
429
+ if (!s) return fallback;
430
+ const raw = s.getItem(key);
431
+ return raw ? JSON.parse(raw) : fallback;
432
+ } catch (e) {
433
+ return fallback;
434
+ }
435
+ }
436
+ function saveJSON(key, value) {
437
+ try {
438
+ const s = ls();
439
+ if (s) s.setItem(key, JSON.stringify(value));
440
+ } catch (e) {}
441
+ }
442
+ function loadCustoms() {
443
+ return loadJSON(CUSTOMS_KEY, []);
444
+ }
445
+ function saveCustoms(list) {
446
+ saveJSON(CUSTOMS_KEY, list);
447
+ }
448
+ function loadUsage() {
449
+ return loadJSON(USAGE_KEY, {});
450
+ }
451
+ function bumpUsage(id) {
452
+ const u = loadUsage();
453
+ u[id] = (u[id] || 0) + 1;
454
+ saveJSON(USAGE_KEY, u);
455
+ saveJSON(LAST_USED_KEY, id);
456
+ }
457
+ /** 最近一次使用的模板 id(智能模式「最近使用」候选;无 → null) */
458
+ function loadLastUsed() {
459
+ try {
460
+ const s = ls();
461
+ if (!s) return null;
462
+ const raw = s.getItem(LAST_USED_KEY);
463
+ return raw ? JSON.parse(raw) : null;
464
+ } catch (e) {
465
+ return null;
466
+ }
467
+ }
468
+ function loadPinned() {
469
+ return loadJSON(PINNED_KEY, []);
470
+ }
471
+ function savePinned(ids) {
472
+ saveJSON(PINNED_KEY, ids);
473
+ }
474
+ /** 全部模板(预制 + 自定义) */
475
+ function allTemplates() {
476
+ return [...PRESET_TEMPLATES, ...loadCustoms()];
477
+ }
478
+ function getTemplate(id) {
479
+ return allTemplates().find((t) => t.id === id);
480
+ }
481
+ /** 展示标签:预制 = 领域;自定义 = tag(默认「自定义」) */
482
+ function displayTag(t) {
483
+ if (!t.builtin) return t.tag || "自定义";
484
+ return t.domain;
485
+ }
486
+ /** 检索串(面板搜索与 /prompt 触发源共用):名称/英文名/正文/领域/阶段/动作/标签 */
487
+ function templateHaystack(t) {
488
+ const tag = !t.builtin ? t.tag || "自定义" : "";
489
+ return (t.name + " " + (t.nameEn || "") + " " + (t.body || "") + " " + t.domain + " " + t.stage + " " + (t.action || []).join(" ") + " " + tag).toLowerCase();
490
+ }
491
+ /** 排序:置顶(pin 在前)→ 用量计数降序。用量仅供排序,不显示数字。 */
492
+ function sortedTemplates(list) {
493
+ const usage = loadUsage();
494
+ const pinned = loadPinned();
495
+ const pinIdx = (id) => {
496
+ const i = pinned.indexOf(id);
497
+ return i < 0 ? MAX_PIN : i;
498
+ };
499
+ return [...list].sort((a, b) => {
500
+ const pa = pinIdx(a.id), pb = pinIdx(b.id);
501
+ if (pa !== pb) return pa - pb;
502
+ return (usage[b.id] || 0) - (usage[a.id] || 0);
503
+ });
504
+ }
505
+ /** 置顶切换(置顶数 ≤5);返回是否成功(超限返回 false) */
506
+ function togglePin(id) {
507
+ const pinned = loadPinned();
508
+ const i = pinned.indexOf(id);
509
+ if (i >= 0) pinned.splice(i, 1);
510
+ else if (pinned.length >= MAX_PIN) return {
511
+ pinned,
512
+ ok: false
513
+ };
514
+ else pinned.push(id);
515
+ savePinned(pinned);
516
+ return {
517
+ pinned,
518
+ ok: true
519
+ };
520
+ }
521
+ function isPinned(id) {
522
+ return loadPinned().indexOf(id) >= 0;
523
+ }
524
+ /** 新增自定义模板 */
525
+ function addCustom(name, tag, body) {
526
+ const t = {
527
+ id: "c" + Date.now().toString(36) + Math.floor(Math.random() * 1e4).toString(36),
528
+ name,
529
+ nameEn: "",
530
+ domain: "执行",
531
+ stage: "执行前",
532
+ action: [],
533
+ body,
534
+ builtin: false,
535
+ tag,
536
+ createdAt: Date.now()
537
+ };
538
+ const list = loadCustoms();
539
+ list.push(t);
540
+ saveCustoms(list);
541
+ return t;
542
+ }
543
+ /** 更新自定义模板 */
544
+ function updateCustom(id, patch) {
545
+ saveCustoms(loadCustoms().map((t) => {
546
+ if (t.id !== id) return t;
547
+ if (patch.name !== void 0) t.name = patch.name;
548
+ if (patch.tag !== void 0) t.tag = patch.tag;
549
+ if (patch.body !== void 0) t.body = patch.body;
550
+ return t;
551
+ }));
552
+ }
553
+ /** 删除自定义模板(仅限 builtin=false);返回是否成功 */
554
+ function removeCustom(id) {
555
+ const list = loadCustoms();
556
+ const next = list.filter((t) => t.id !== id);
557
+ if (next.length === list.length) return false;
558
+ saveCustoms(next);
559
+ savePinned(loadPinned().filter((p) => p !== id));
560
+ return true;
561
+ }
562
+ /** 复制预制为自定义 */
563
+ function copyPresetToCustom(id) {
564
+ const src = getPresetById(id);
565
+ if (!src) return null;
566
+ return addCustom(src.name + "(副本)", src.domain, src.body);
567
+ }
568
+ //#endregion
569
+ //#region src/client/state.ts
570
+ /**
571
+ * dsh-prompt — 面板开合状态(入口按钮 ↔ 面板共享,模块级 + 订阅)
572
+ */
573
+ let panelOpen = false;
574
+ const listeners$1 = /* @__PURE__ */ new Set();
575
+ function isPanelOpen() {
576
+ return panelOpen;
577
+ }
578
+ function setPanelOpen(v) {
579
+ if (panelOpen !== v) {
580
+ panelOpen = v;
581
+ listeners$1.forEach((fn) => {
582
+ try {
583
+ fn(v);
584
+ } catch (e) {}
585
+ });
586
+ }
587
+ }
588
+ function onPanelOpen(fn) {
589
+ listeners$1.add(fn);
590
+ return () => {
591
+ listeners$1.delete(fn);
592
+ };
593
+ }
594
+ //#endregion
595
+ //#region src/client/i18n.ts
596
+ function getLang() {
597
+ try {
598
+ const l = document.documentElement && document.documentElement.lang || navigator.language || "en";
599
+ return /^zh/i.test(l) ? "zh" : "en";
600
+ } catch (e) {
601
+ return "zh";
602
+ }
603
+ }
604
+ function tr(lang, m) {
605
+ return lang === "zh" ? m.zh : m.en;
606
+ }
607
+ const STR = {
608
+ panelTitle: {
609
+ zh: "Prompt",
610
+ en: "Prompt"
611
+ },
612
+ entryBtn: {
613
+ zh: "Prompt",
614
+ en: "Prompt"
615
+ },
616
+ add: {
617
+ zh: "新增自定义模板",
618
+ en: "Add custom template"
619
+ },
620
+ addShort: {
621
+ zh: "新增",
622
+ en: "Add"
623
+ },
624
+ searchPh: {
625
+ zh: "搜索模板…",
626
+ en: "Search templates…"
627
+ },
628
+ tabAll: {
629
+ zh: "全部",
630
+ en: "All"
631
+ },
632
+ tabBefore: {
633
+ zh: "执行前",
634
+ en: "Before"
635
+ },
636
+ tabDuring: {
637
+ zh: "执行中",
638
+ en: "During"
639
+ },
640
+ tabAfter: {
641
+ zh: "执行后",
642
+ en: "After"
643
+ },
644
+ tabCustom: {
645
+ zh: "自定义",
646
+ en: "Custom"
647
+ },
648
+ domainAll: {
649
+ zh: "全领域",
650
+ en: "All domains"
651
+ },
652
+ presetCount: {
653
+ zh: "预制",
654
+ en: "Preset"
655
+ },
656
+ customCount: {
657
+ zh: "自定义",
658
+ en: "Custom"
659
+ },
660
+ goSettings: {
661
+ zh: "设置 → 模板管理",
662
+ en: "Settings → Templates"
663
+ },
664
+ edit: {
665
+ zh: "编辑",
666
+ en: "Edit"
667
+ },
668
+ del: {
669
+ zh: "删除",
670
+ en: "Delete"
671
+ },
672
+ copy: {
673
+ zh: "复制为自定义",
674
+ en: "Copy to custom"
675
+ },
676
+ pin: {
677
+ zh: "置顶/取消置顶",
678
+ en: "Pin/Unpin"
679
+ },
680
+ noMatch: {
681
+ zh: "无匹配模板",
682
+ en: "No matching templates"
683
+ },
684
+ addTitle: {
685
+ zh: "新增自定义模板",
686
+ en: "Add custom template"
687
+ },
688
+ editTitle: {
689
+ zh: "编辑自定义模板",
690
+ en: "Edit custom template"
691
+ },
692
+ namePh: {
693
+ zh: "模板名称",
694
+ en: "Template name"
695
+ },
696
+ tagPh: {
697
+ zh: "标签",
698
+ en: "Tag"
699
+ },
700
+ bodyPh: {
701
+ zh: "Prompt 正文(≤1000 字)",
702
+ en: "Prompt body (≤1000 chars)"
703
+ },
704
+ cancel: {
705
+ zh: "取消",
706
+ en: "Cancel"
707
+ },
708
+ save: {
709
+ zh: "保存",
710
+ en: "Save"
711
+ },
712
+ addOk: {
713
+ zh: "添加",
714
+ en: "Add"
715
+ },
716
+ nameRequired: {
717
+ zh: "名称必填",
718
+ en: "Name is required"
719
+ },
720
+ bodyTooLong: {
721
+ zh: "正文超过 1000 字上限",
722
+ en: "Body exceeds 1000 chars"
723
+ },
724
+ delTitle: {
725
+ zh: "删除确认",
726
+ en: "Confirm delete"
727
+ },
728
+ delMsg: {
729
+ zh: "确定删除自定义模板",
730
+ en: "Delete custom template"
731
+ },
732
+ delUnrecover: {
733
+ zh: "此操作不可撤销。",
734
+ en: "This cannot be undone."
735
+ },
736
+ delOk: {
737
+ zh: "删除",
738
+ en: "Delete"
739
+ },
740
+ pinFull: {
741
+ zh: "置顶已达上限(5 个)",
742
+ en: "Pin limit reached (5)"
743
+ },
744
+ insertHint: {
745
+ zh: "点击即插入",
746
+ en: "Click to insert"
747
+ },
748
+ close: {
749
+ zh: "关闭",
750
+ en: "Close"
751
+ },
752
+ smartToggle: {
753
+ zh: "智能模式悬浮卡(输入匹配时推荐模板;默认开)",
754
+ en: "Smart card (recommends templates while typing; on by default)"
755
+ },
756
+ smartToggleHint: {
757
+ zh: "默认开;关闭后仅保留 ⚡Prompt 面板与 /prompt 触发源",
758
+ en: "On by default; when off, only the panel and /prompt remain"
759
+ },
760
+ smartDot: {
761
+ zh: "智能模式 · 拖动调整位置;输入匹配时出卡",
762
+ en: "Smart mode · drag to move; card appears on match"
763
+ },
764
+ smartTitle: {
765
+ zh: "智能推荐",
766
+ en: "Smart suggest"
767
+ },
768
+ smartHint: {
769
+ zh: "点击即填入",
770
+ en: "Click to insert"
771
+ },
772
+ smartFill: {
773
+ zh: "点击填入",
774
+ en: "Insert"
775
+ },
776
+ smartDismiss: {
777
+ zh: "收起(继续输入可再次出现)",
778
+ en: "Dismiss (reappears as you type)"
779
+ },
780
+ smartRecent: {
781
+ zh: "最近使用",
782
+ en: "Recently used"
783
+ },
784
+ smartCommon: {
785
+ zh: "常用模板",
786
+ en: "Common templates"
787
+ },
788
+ gitHubRepo: {
789
+ zh: "GitHub 仓库",
790
+ en: "GitHub repo"
791
+ },
792
+ feedback: {
793
+ zh: "反馈故障",
794
+ en: "Report issue"
795
+ }
796
+ };
797
+ //#endregion
798
+ //#region src/client/panel.ts
799
+ function getReact() {
800
+ if (typeof require === "function") try {
801
+ return require("react");
802
+ } catch (e) {}
803
+ if (typeof globalThis !== "undefined" && globalThis.React) return globalThis.React;
804
+ return null;
805
+ }
806
+ const DOMAIN_FILTERS = [
807
+ "all",
808
+ "思考框架",
809
+ "学习",
810
+ "工程",
811
+ "执行"
812
+ ];
813
+ const CUSTOM_TAG = "自定义";
814
+ /** 插入正文到当前草稿(光标处优先,否则末尾;不覆盖;自动聚焦) */
815
+ function insertBody(useInput, inputActions, body) {
816
+ let draft = "";
817
+ try {
818
+ const st = useInput ? useInput((s) => s) : null;
819
+ draft = st && st.draft || "";
820
+ } catch (e) {}
821
+ let pos = draft.length;
822
+ try {
823
+ if (typeof document !== "undefined") {
824
+ const tas = document.querySelectorAll("textarea");
825
+ for (let i = 0; i < tas.length; i++) {
826
+ const ta = tas[i];
827
+ if (ta.value === draft) {
828
+ pos = typeof ta.selectionStart === "number" ? ta.selectionStart : draft.length;
829
+ break;
830
+ }
831
+ }
832
+ }
833
+ } catch (e) {}
834
+ const newDraft = draft.slice(0, pos) + body + draft.slice(pos);
835
+ if (inputActions && typeof inputActions.setDraft === "function") inputActions.setDraft(newDraft);
836
+ setTimeout(() => {
837
+ try {
838
+ if (typeof document !== "undefined") {
839
+ const tas = document.querySelectorAll("textarea");
840
+ for (let i = 0; i < tas.length; i++) {
841
+ const ta = tas[i];
842
+ if (ta.value === newDraft) {
843
+ ta.focus();
844
+ ta.setSelectionRange(pos + body.length, pos + body.length);
845
+ break;
846
+ }
847
+ }
848
+ }
849
+ } catch (e) {}
850
+ }, 0);
851
+ }
852
+ /** 点击模板:插入 + 用量 +1 + 面板关闭 */
853
+ function onPick(t, useInput, inputActions) {
854
+ insertBody(useInput, inputActions, t.body);
855
+ bumpUsage(t.id);
856
+ setPanelOpen(false);
857
+ }
858
+ /** 模板浏览(面板 / 设置页共用) */
859
+ function TemplateBrowser(props) {
860
+ const react = getReact();
861
+ if (!react) return null;
862
+ const h = react.createElement;
863
+ const { compact, inputActions, useInput } = props;
864
+ const langState = react.useState(getLang());
865
+ const lang = langState[0];
866
+ const setTick = react.useState(0)[1];
867
+ const tabState = react.useState("all");
868
+ const tab = tabState[0];
869
+ const domainState = react.useState("all");
870
+ const domain = domainState[0];
871
+ const qState = react.useState("");
872
+ const q = qState[0];
873
+ const modalState = react.useState(null);
874
+ const modal = modalState[0];
875
+ const offsetState = react.useState(0);
876
+ const offsetLeft = offsetState[0];
877
+ const rootRef = react.useRef(null);
878
+ const highlightState = react.useState(null);
879
+ const highlightId = highlightState[0];
880
+ react.useEffect(() => {
881
+ if (!compact) return;
882
+ try {
883
+ const btn = typeof document !== "undefined" ? document.querySelector("[data-dsh-prompt-entry]") : null;
884
+ if (btn && rootRef.current && rootRef.current.offsetParent) {
885
+ const br = btn.getBoundingClientRect();
886
+ const ar = rootRef.current.offsetParent.getBoundingClientRect();
887
+ offsetState[1](Math.max(0, br.left - ar.left));
888
+ }
889
+ } catch (e) {}
890
+ }, []);
891
+ react.useEffect(() => {
892
+ if (typeof document === "undefined") return;
893
+ const onLang = () => {
894
+ langState[1](getLang());
895
+ };
896
+ const obs = new MutationObserver(onLang);
897
+ obs.observe(document.documentElement, {
898
+ attributes: true,
899
+ attributeFilter: ["lang"]
900
+ });
901
+ return () => {
902
+ obs.disconnect();
903
+ };
904
+ }, []);
905
+ const refresh = () => setTick((n) => n + 1);
906
+ const t = (k) => tr(lang, STR[k]);
907
+ const customs = allTemplates().filter((x) => !x.builtin);
908
+ const list = allTemplates().filter((x) => {
909
+ if (tab === CUSTOM_TAG) return !x.builtin;
910
+ if (tab !== "all" && x.stage !== tab) return false;
911
+ if (domain !== "all" && displayTag(x) !== domain) return false;
912
+ return true;
913
+ });
914
+ const ql = q.trim().toLowerCase();
915
+ const sorted = sortedTemplates(ql ? list.filter((x) => templateHaystack(x).indexOf(ql) >= 0) : list);
916
+ const presetCount = PRESET_TEMPLATES.length;
917
+ const customCount = customs.length;
918
+ const base = "var(--dsw-alias-label-primary)";
919
+ const muted = "var(--dsw-alias-label-secondary)";
920
+ const dim = "var(--dsw-alias-label-tertiary)";
921
+ const line = "1px solid var(--dsw-alias-border-l1)";
922
+ const panelStyle = compact ? {
923
+ position: "absolute",
924
+ left: offsetLeft,
925
+ bottom: "calc(100% + 8px)",
926
+ zIndex: 300,
927
+ width: 430,
928
+ display: "flex",
929
+ flexDirection: "column",
930
+ background: "var(--dsw-specific-menu)",
931
+ border: "1px solid var(--dsw-alias-border-inverted)",
932
+ borderRadius: 12,
933
+ boxShadow: "var(--dsw-shadow-lv3)",
934
+ overflow: "hidden",
935
+ fontFamily: "var(--dsw-font-family)",
936
+ fontSize: "var(--dsw-font-markdown-base-font-size)",
937
+ color: base
938
+ } : {
939
+ display: "flex",
940
+ flexDirection: "column",
941
+ gap: 6,
942
+ padding: "6px 8px",
943
+ fontFamily: "var(--dsw-font-family)",
944
+ fontSize: "var(--dsw-font-markdown-base-font-size)",
945
+ color: base
946
+ };
947
+ const headStyle = {
948
+ display: "flex",
949
+ alignItems: "center",
950
+ justifyContent: "space-between",
951
+ padding: "6px 8px",
952
+ borderBottom: line
953
+ };
954
+ const titleStyle = {
955
+ fontWeight: 700,
956
+ fontSize: "1em"
957
+ };
958
+ const addBtn = {
959
+ width: 26,
960
+ height: 26,
961
+ borderRadius: 7,
962
+ border: line,
963
+ background: "var(--dsw-alias-bg-layer-3)",
964
+ color: "var(--dsw-specific-accent,#f0a45c)",
965
+ fontSize: "1.2em",
966
+ lineHeight: 1,
967
+ cursor: "pointer",
968
+ display: "flex",
969
+ alignItems: "center",
970
+ justifyContent: "center",
971
+ padding: 0
972
+ };
973
+ const closeBtn = {
974
+ width: 26,
975
+ height: 26,
976
+ borderRadius: 7,
977
+ border: 0,
978
+ background: "transparent",
979
+ color: dim,
980
+ fontSize: "1.2em",
981
+ lineHeight: 1,
982
+ cursor: "pointer",
983
+ display: "flex",
984
+ alignItems: "center",
985
+ justifyContent: "center",
986
+ padding: 0
987
+ };
988
+ const headBtns = {
989
+ display: "flex",
990
+ gap: 4,
991
+ alignItems: "center"
992
+ };
993
+ const tabsStyle = {
994
+ display: "flex",
995
+ gap: 4,
996
+ padding: "4px 8px 0",
997
+ flexWrap: "wrap"
998
+ };
999
+ const tabBtn = (on) => ({
1000
+ padding: "2px 8px",
1001
+ borderRadius: 999,
1002
+ border: line,
1003
+ background: on ? "var(--dsw-alias-bg-layer-3)" : "transparent",
1004
+ color: on ? base : muted,
1005
+ cursor: "pointer",
1006
+ fontFamily: "var(--dsw-font-family)",
1007
+ fontSize: "0.85em"
1008
+ });
1009
+ const domainRowStyle = {
1010
+ display: "flex",
1011
+ gap: 4,
1012
+ padding: "3px 8px 0",
1013
+ flexWrap: "wrap"
1014
+ };
1015
+ const searchStyle = {
1016
+ margin: "5px 8px 4px",
1017
+ padding: "5px 9px",
1018
+ borderRadius: 8,
1019
+ border: line,
1020
+ background: "var(--dsw-alias-bg-layer-3)",
1021
+ color: base,
1022
+ fontFamily: "var(--dsw-font-family)",
1023
+ fontSize: "0.96em",
1024
+ outline: "none"
1025
+ };
1026
+ const listStyle = compact ? {
1027
+ overflow: "auto",
1028
+ padding: "2px 2px 8px",
1029
+ maxHeight: 170
1030
+ } : { padding: "2px 2px 8px" };
1031
+ const itemStyle = {
1032
+ display: "flex",
1033
+ alignItems: "flex-start",
1034
+ gap: 6,
1035
+ padding: "4px 2px",
1036
+ borderRadius: 8,
1037
+ cursor: "pointer"
1038
+ };
1039
+ const pinStyle = (on) => ({
1040
+ flex: "none",
1041
+ width: 20,
1042
+ height: 20,
1043
+ display: "inline-flex",
1044
+ alignItems: "center",
1045
+ justifyContent: "center",
1046
+ cursor: "pointer",
1047
+ border: 0,
1048
+ background: "transparent",
1049
+ borderRadius: 6
1050
+ });
1051
+ const nmStyle = {
1052
+ flex: "none",
1053
+ minWidth: 0,
1054
+ fontSize: "0.98em",
1055
+ color: base,
1056
+ fontWeight: 500,
1057
+ whiteSpace: "nowrap",
1058
+ overflow: "hidden",
1059
+ textOverflow: "ellipsis"
1060
+ };
1061
+ const subStyle = {
1062
+ display: "block",
1063
+ fontSize: "0.85em",
1064
+ color: dim,
1065
+ whiteSpace: "nowrap",
1066
+ overflow: "hidden",
1067
+ textOverflow: "ellipsis"
1068
+ };
1069
+ const tagStyle = {
1070
+ flex: "none",
1071
+ fontSize: "0.7em",
1072
+ fontWeight: 500,
1073
+ color: muted,
1074
+ background: "var(--dsw-alias-bg-layer-3)",
1075
+ border: "1px solid var(--dsw-alias-border-l2)",
1076
+ padding: "0 7px",
1077
+ borderRadius: 999,
1078
+ lineHeight: "15px",
1079
+ letterSpacing: "0.02em",
1080
+ whiteSpace: "nowrap"
1081
+ };
1082
+ const actStyle = {
1083
+ display: "flex",
1084
+ gap: 4,
1085
+ flex: "none"
1086
+ };
1087
+ const actBtn = (danger) => ({
1088
+ border: line,
1089
+ background: "transparent",
1090
+ color: danger ? "var(--dsw-specific-danger,#e06c75)" : dim,
1091
+ cursor: "pointer",
1092
+ fontSize: "0.85em",
1093
+ padding: "1px 8px",
1094
+ borderRadius: 999,
1095
+ fontFamily: "var(--dsw-font-family)",
1096
+ whiteSpace: "nowrap"
1097
+ });
1098
+ const footLink = {
1099
+ color: "var(--dsw-specific-accent,#f0a45c)",
1100
+ cursor: "pointer",
1101
+ textDecoration: "none",
1102
+ background: "transparent",
1103
+ border: 0,
1104
+ fontFamily: "var(--dsw-font-family)",
1105
+ fontSize: "0.85em"
1106
+ };
1107
+ const maskStyle = {
1108
+ position: "fixed",
1109
+ inset: 0,
1110
+ background: "rgba(0,0,0,0.55)",
1111
+ display: "flex",
1112
+ alignItems: "center",
1113
+ justifyContent: "center",
1114
+ zIndex: 400
1115
+ };
1116
+ const modalStyle = {
1117
+ width: 460,
1118
+ background: "var(--dsw-specific-menu)",
1119
+ border: "1px solid var(--dsw-alias-border-inverted)",
1120
+ borderRadius: 12,
1121
+ padding: 16,
1122
+ display: "flex",
1123
+ flexDirection: "column",
1124
+ gap: 10,
1125
+ fontFamily: "var(--dsw-font-family)",
1126
+ color: base
1127
+ };
1128
+ const fieldStyle = {
1129
+ width: "100%",
1130
+ background: "var(--dsw-alias-bg-layer-3)",
1131
+ border: line,
1132
+ color: base,
1133
+ borderRadius: 8,
1134
+ padding: "8px 10px",
1135
+ fontFamily: "var(--dsw-font-family)",
1136
+ fontSize: "0.96em",
1137
+ outline: "none",
1138
+ boxSizing: "border-box"
1139
+ };
1140
+ const mbtnsStyle = {
1141
+ display: "flex",
1142
+ justifyContent: "flex-end",
1143
+ gap: 8
1144
+ };
1145
+ const mbtn = (primary, danger) => ({
1146
+ padding: "6px 14px",
1147
+ borderRadius: 8,
1148
+ border: primary ? 0 : line,
1149
+ background: primary ? "var(--dsw-specific-accent,#f0a45c)" : "var(--dsw-alias-bg-layer-3)",
1150
+ color: primary ? "#1a1a1e" : danger ? "var(--dsw-specific-danger,#e06c75)" : base,
1151
+ cursor: "pointer",
1152
+ fontFamily: "var(--dsw-font-family)",
1153
+ fontSize: "0.96em"
1154
+ });
1155
+ const handlePick = (x) => {
1156
+ onPick(x, useInput, inputActions);
1157
+ };
1158
+ const handlePin = (e, x) => {
1159
+ e.stopPropagation();
1160
+ if (!togglePin(x.id).ok) alert(t("pinFull"));
1161
+ refresh();
1162
+ };
1163
+ const handleCopy = (e, id) => {
1164
+ e.stopPropagation();
1165
+ const c = copyPresetToCustom(id);
1166
+ if (!c) return;
1167
+ togglePin(c.id);
1168
+ tabState[1](CUSTOM_TAG);
1169
+ highlightState[1](c.id);
1170
+ refresh();
1171
+ setTimeout(() => {
1172
+ highlightState[1](null);
1173
+ try {
1174
+ const el = typeof document !== "undefined" ? document.querySelector("[data-dsh-prompt-id=\"" + c.id + "\"]") : null;
1175
+ if (el) el.scrollIntoView({ block: "nearest" });
1176
+ } catch (err) {}
1177
+ }, 1600);
1178
+ };
1179
+ const handleDel = (e, x) => {
1180
+ e.stopPropagation();
1181
+ modalState[1]({
1182
+ kind: "del",
1183
+ t: x
1184
+ });
1185
+ };
1186
+ const handleEdit = (e, x) => {
1187
+ e.stopPropagation();
1188
+ modalState[1]({
1189
+ kind: "edit",
1190
+ t: x
1191
+ });
1192
+ };
1193
+ let modalNode = null;
1194
+ if (modal) {
1195
+ if (modal.kind === "add" || modal.kind === "edit") {
1196
+ const editing = modal.kind === "edit" && modal.t;
1197
+ let name = editing ? modal.t.name : "";
1198
+ let tag = editing ? modal.t.tag || CUSTOM_TAG : CUSTOM_TAG;
1199
+ let body = editing ? modal.t.body : "";
1200
+ const ModalInner = () => {
1201
+ const s1 = react.useState(name);
1202
+ name = s1[0];
1203
+ const setName = s1[1];
1204
+ const s2 = react.useState(tag);
1205
+ tag = s2[0];
1206
+ const setTag = s2[1];
1207
+ const s3 = react.useState(body);
1208
+ body = s3[0];
1209
+ const setBody = s3[1];
1210
+ const err = react.useState(null);
1211
+ const doOk = () => {
1212
+ const nm = name.trim(), bd = body.trim();
1213
+ if (!nm) {
1214
+ err[1]("nameRequired");
1215
+ return;
1216
+ }
1217
+ if (bd.length > 1e3) {
1218
+ err[1]("bodyTooLong");
1219
+ return;
1220
+ }
1221
+ if (editing) updateCustom(modal.t.id, {
1222
+ name: nm,
1223
+ tag,
1224
+ body: bd
1225
+ });
1226
+ else addCustom(nm, tag || CUSTOM_TAG, bd);
1227
+ modalState[1](null);
1228
+ refresh();
1229
+ };
1230
+ return h("div", {
1231
+ style: maskStyle,
1232
+ onClick: (e) => {
1233
+ if (e.target === e.currentTarget) modalState[1](null);
1234
+ }
1235
+ }, [h("div", { style: modalStyle }, [
1236
+ h("h3", { style: {
1237
+ fontSize: "1.08em",
1238
+ margin: 0
1239
+ } }, editing ? t("editTitle") : t("addTitle")),
1240
+ h("input", {
1241
+ style: fieldStyle,
1242
+ placeholder: t("namePh"),
1243
+ value: name,
1244
+ onChange: (e) => setName(e.target.value)
1245
+ }),
1246
+ h("input", {
1247
+ style: fieldStyle,
1248
+ placeholder: t("tagPh"),
1249
+ value: tag,
1250
+ onChange: (e) => setTag(e.target.value)
1251
+ }),
1252
+ h("textarea", {
1253
+ style: {
1254
+ ...fieldStyle,
1255
+ height: 110,
1256
+ resize: "vertical"
1257
+ },
1258
+ placeholder: t("bodyPh"),
1259
+ value: body,
1260
+ onChange: (e) => setBody(e.target.value)
1261
+ }),
1262
+ err[0] ? h("div", { style: {
1263
+ fontSize: "0.92em",
1264
+ color: "var(--dsw-specific-danger,#e06c75)"
1265
+ } }, t(err[0])) : null,
1266
+ h("div", { style: mbtnsStyle }, [h("button", {
1267
+ style: mbtn(),
1268
+ onClick: () => modalState[1](null)
1269
+ }, t("cancel")), h("button", {
1270
+ style: mbtn(true),
1271
+ onClick: doOk
1272
+ }, editing ? t("save") : t("addOk"))])
1273
+ ])]);
1274
+ };
1275
+ modalNode = h(ModalInner);
1276
+ } else if (modal.kind === "del" && modal.t) {
1277
+ const doDel = () => {
1278
+ removeCustom(modal.t.id);
1279
+ modalState[1](null);
1280
+ refresh();
1281
+ };
1282
+ modalNode = h("div", {
1283
+ style: maskStyle,
1284
+ onClick: (e) => {
1285
+ if (e.target === e.currentTarget) modalState[1](null);
1286
+ }
1287
+ }, [h("div", { style: modalStyle }, [
1288
+ h("h3", { style: {
1289
+ fontSize: "1.08em",
1290
+ margin: 0
1291
+ } }, t("delTitle")),
1292
+ h("div", { style: {
1293
+ fontSize: "0.96em",
1294
+ color: dim
1295
+ } }, t("delMsg") + "「" + modal.t.name + "」" + t("delUnrecover")),
1296
+ h("div", { style: mbtnsStyle }, [h("button", {
1297
+ style: mbtn(),
1298
+ onClick: () => modalState[1](null)
1299
+ }, t("cancel")), h("button", {
1300
+ style: mbtn(false, true),
1301
+ onClick: doDel
1302
+ }, t("delOk"))])
1303
+ ])]);
1304
+ }
1305
+ }
1306
+ const tabs = [
1307
+ {
1308
+ id: "all",
1309
+ label: t("tabAll")
1310
+ },
1311
+ {
1312
+ id: "执行前",
1313
+ label: t("tabBefore")
1314
+ },
1315
+ {
1316
+ id: "执行中",
1317
+ label: t("tabDuring")
1318
+ },
1319
+ {
1320
+ id: "执行后",
1321
+ label: t("tabAfter")
1322
+ },
1323
+ {
1324
+ id: CUSTOM_TAG,
1325
+ label: t("tabCustom")
1326
+ }
1327
+ ];
1328
+ const tabNodes = h("div", { style: tabsStyle }, tabs.map((tb) => h("button", {
1329
+ key: tb.id,
1330
+ style: tabBtn(tab === tb.id),
1331
+ onClick: () => {
1332
+ tabState[1](tb.id);
1333
+ refresh();
1334
+ }
1335
+ }, tb.label)));
1336
+ const domainNodes = h("div", { style: domainRowStyle }, DOMAIN_FILTERS.map((d) => h("button", {
1337
+ key: d,
1338
+ style: tabBtn(domain === d),
1339
+ onClick: () => {
1340
+ domainState[1](d);
1341
+ refresh();
1342
+ }
1343
+ }, d === "all" ? t("domainAll") : d)));
1344
+ const rows = sorted.map((x) => {
1345
+ const pinned = isPinned(x.id);
1346
+ const acts = !x.builtin ? h("span", { style: actStyle }, [h("button", {
1347
+ style: actBtn(),
1348
+ onClick: (e) => handleEdit(e, x)
1349
+ }, t("edit")), h("button", {
1350
+ style: actBtn(true),
1351
+ onClick: (e) => handleDel(e, x)
1352
+ }, t("del"))]) : h("span", { style: actStyle }, [h("button", {
1353
+ style: actBtn(),
1354
+ onClick: (e) => handleCopy(e, x.id)
1355
+ }, t("copy"))]);
1356
+ const itemBg = highlightId === x.id ? "var(--dsw-alias-interactive-bg-hover)" : void 0;
1357
+ return h("div", {
1358
+ key: x.id,
1359
+ style: {
1360
+ ...itemStyle,
1361
+ background: itemBg
1362
+ },
1363
+ "data-dsh-prompt-id": x.id,
1364
+ onClick: () => handlePick(x),
1365
+ title: t("insertHint")
1366
+ }, [
1367
+ h("div", { style: {
1368
+ flex: "none",
1369
+ paddingTop: 2
1370
+ } }, [h("button", {
1371
+ style: pinStyle(pinned),
1372
+ title: t("pin"),
1373
+ onClick: (e) => handlePin(e, x)
1374
+ }, [h("svg", {
1375
+ width: 14,
1376
+ height: 14,
1377
+ viewBox: "0 0 24 24",
1378
+ fill: pinned ? "var(--dsw-specific-accent,#f0a45c)" : "none",
1379
+ stroke: pinned ? "var(--dsw-specific-accent,#f0a45c)" : dim,
1380
+ strokeWidth: 1.8,
1381
+ strokeLinecap: "round",
1382
+ strokeLinejoin: "round",
1383
+ style: { display: "block" }
1384
+ }, [h("path", { d: "M12 17v5" }), h("path", { d: "M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1z" })])])]),
1385
+ h("div", { style: {
1386
+ flex: 1,
1387
+ minWidth: 0,
1388
+ display: "flex",
1389
+ flexDirection: "column",
1390
+ gap: 1
1391
+ } }, [h("div", { style: {
1392
+ display: "flex",
1393
+ alignItems: "center",
1394
+ gap: 6,
1395
+ minWidth: 0
1396
+ } }, [h("span", { style: nmStyle }, x.name), h("span", { style: tagStyle }, displayTag(x))]), h("span", { style: subStyle }, (x.body || "").slice(0, 44) + "…")]),
1397
+ h("div", { style: {
1398
+ flex: "none",
1399
+ display: "flex",
1400
+ alignItems: "center",
1401
+ gap: 4,
1402
+ paddingTop: 2
1403
+ } }, [acts])
1404
+ ]);
1405
+ });
1406
+ const listNode = rows.length > 0 ? h("div", { style: listStyle }, rows) : h("div", { style: {
1407
+ padding: 14,
1408
+ color: dim,
1409
+ fontSize: "0.92em"
1410
+ } }, t("noMatch"));
1411
+ const footer = null;
1412
+ return h("div", {
1413
+ ref: rootRef,
1414
+ style: panelStyle
1415
+ }, [
1416
+ compact ? h("div", { style: headStyle }, [
1417
+ h("svg", {
1418
+ width: 15,
1419
+ height: 15,
1420
+ viewBox: "0 0 24 24",
1421
+ fill: "none",
1422
+ stroke: "var(--dsw-specific-accent,#f0a45c)",
1423
+ strokeWidth: 2,
1424
+ strokeLinecap: "round",
1425
+ strokeLinejoin: "round",
1426
+ style: { flex: "none" }
1427
+ }, [
1428
+ h("path", { d: "M15 14c.2-1 .7-1.7 1.5-2.5C17.5 10.6 18 9.3 18 8a6 6 0 1 0-12 0c0 1.3.5 2.6 1.5 3.5.8.8 1.3 1.5 1.5 2.5" }),
1429
+ h("path", { d: "M9 18h6" }),
1430
+ h("path", { d: "M10 22h4" }),
1431
+ h("path", { d: "M18.5 2.5l.8 1.7 1.7.8-1.7.8-.8 1.7-.8-1.7-1.7-.8 1.7-.8z" })
1432
+ ]),
1433
+ h("span", { style: titleStyle }, t("panelTitle")),
1434
+ h("span", { style: {
1435
+ fontSize: "0.85em",
1436
+ color: dim,
1437
+ marginLeft: 6
1438
+ } }, t("presetCount") + " " + presetCount + " · " + t("customCount") + " " + customCount),
1439
+ h("div", { style: { flex: 1 } }),
1440
+ h("button", {
1441
+ style: footLink,
1442
+ onClick: () => {
1443
+ setPanelOpen(false);
1444
+ emitGoSettings();
1445
+ }
1446
+ }, t("goSettings")),
1447
+ h("div", { style: headBtns }, [h("button", {
1448
+ style: addBtn,
1449
+ title: t("add"),
1450
+ onClick: () => modalState[1]({ kind: "add" })
1451
+ }, "+"), h("button", {
1452
+ style: closeBtn,
1453
+ title: t("close"),
1454
+ onClick: () => setPanelOpen(false)
1455
+ }, "×")])
1456
+ ]) : h("div", { style: headStyle }, [
1457
+ h("svg", {
1458
+ width: 15,
1459
+ height: 15,
1460
+ viewBox: "0 0 24 24",
1461
+ fill: "none",
1462
+ stroke: "var(--dsw-specific-accent,#f0a45c)",
1463
+ strokeWidth: 2,
1464
+ strokeLinecap: "round",
1465
+ strokeLinejoin: "round",
1466
+ style: { flex: "none" }
1467
+ }, [
1468
+ h("path", { d: "M15 14c.2-1 .7-1.7 1.5-2.5C17.5 10.6 18 9.3 18 8a6 6 0 1 0-12 0c0 1.3.5 2.6 1.5 3.5.8.8 1.3 1.5 1.5 2.5" }),
1469
+ h("path", { d: "M9 18h6" }),
1470
+ h("path", { d: "M10 22h4" }),
1471
+ h("path", { d: "M18.5 2.5l.8 1.7 1.7.8-1.7.8-.8 1.7-.8-1.7-1.7-.8 1.7-.8z" })
1472
+ ]),
1473
+ h("span", { style: titleStyle }, t("panelTitle")),
1474
+ h("div", { style: { flex: 1 } }),
1475
+ h("button", {
1476
+ style: {
1477
+ ...addBtn,
1478
+ width: "auto",
1479
+ padding: "0 12px",
1480
+ fontSize: "0.92em"
1481
+ },
1482
+ title: t("add"),
1483
+ onClick: () => modalState[1]({ kind: "add" })
1484
+ }, "+ " + t("addShort"))
1485
+ ]),
1486
+ tabNodes,
1487
+ domainNodes,
1488
+ h("input", {
1489
+ style: searchStyle,
1490
+ placeholder: t("searchPh"),
1491
+ value: q,
1492
+ onChange: (e) => qState[1](e.target.value)
1493
+ }),
1494
+ listNode,
1495
+ footer,
1496
+ modalNode
1497
+ ]);
1498
+ }
1499
+ /** 面板 → 设置页跳转(宿主 settings 路由:由 index.ts 注册) */
1500
+ let goSettingsHandler = null;
1501
+ function setGoSettingsHandler(fn) {
1502
+ goSettingsHandler = fn;
1503
+ }
1504
+ function emitGoSettings() {
1505
+ if (goSettingsHandler) goSettingsHandler();
1506
+ }
1507
+ //#endregion
1508
+ //#region src/client/button.ts
1509
+ /**
1510
+ * dsh-prompt — 入口按钮(conversation.input.left)
1511
+ */
1512
+ function EntryButton(props) {
1513
+ const react = getReact();
1514
+ if (!react) return null;
1515
+ const h = react.createElement;
1516
+ const open = props.open ?? false;
1517
+ const lang = getLang();
1518
+ return h("button", {
1519
+ style: {
1520
+ display: "inline-flex",
1521
+ alignItems: "center",
1522
+ gap: 6,
1523
+ padding: "5px 10px",
1524
+ borderRadius: 8,
1525
+ background: "var(--dsw-alias-bg-layer-3)",
1526
+ border: "1px solid var(--dsw-alias-border-l1)",
1527
+ color: open ? "var(--dsw-specific-accent,#f0a45c)" : "var(--dsw-alias-label-primary)",
1528
+ cursor: "pointer",
1529
+ fontSize: 12,
1530
+ fontWeight: 500,
1531
+ fontFamily: "var(--dsw-font-family)",
1532
+ whiteSpace: "nowrap",
1533
+ flex: "none"
1534
+ },
1535
+ title: tr(lang, STR.entryBtn),
1536
+ "data-dsh-prompt-entry": "1",
1537
+ onClick: () => setPanelOpen(!isPanelOpen())
1538
+ }, [h("svg", {
1539
+ width: 14,
1540
+ height: 14,
1541
+ viewBox: "0 0 24 24",
1542
+ fill: "none",
1543
+ stroke: "var(--dsw-specific-accent,#f0a45c)",
1544
+ strokeWidth: 2,
1545
+ strokeLinecap: "round",
1546
+ strokeLinejoin: "round",
1547
+ style: { flex: "none" }
1548
+ }, [
1549
+ h("path", { d: "M15 14c.2-1 .7-1.7 1.5-2.5C17.5 10.6 18 9.3 18 8a6 6 0 1 0-12 0c0 1.3.5 2.6 1.5 3.5.8.8 1.3 1.5 1.5 2.5" }),
1550
+ h("path", { d: "M9 18h6" }),
1551
+ h("path", { d: "M10 22h4" }),
1552
+ h("path", { d: "M18.5 2.5l.8 1.7 1.7.8-1.7.8-.8 1.7-.8-1.7-1.7-.8 1.7-.8z" })
1553
+ ]), h("span", null, tr(lang, STR.entryBtn))]);
1554
+ }
1555
+ //#endregion
1556
+ //#region src/client/smartstore.ts
1557
+ let current = { draft: "" };
1558
+ const listeners = /* @__PURE__ */ new Set();
1559
+ function setSmartInput(next) {
1560
+ if (next.draft === current.draft && next.sessionId === current.sessionId && next.actions === current.actions && next.useInput === current.useInput) return;
1561
+ current = next;
1562
+ listeners.forEach((fn) => {
1563
+ try {
1564
+ fn();
1565
+ } catch (e) {}
1566
+ });
1567
+ }
1568
+ function getSmartInput() {
1569
+ return current;
1570
+ }
1571
+ function onSmartInput(fn) {
1572
+ listeners.add(fn);
1573
+ return () => {
1574
+ listeners.delete(fn);
1575
+ };
1576
+ }
1577
+ const SMART_KEY = "dsh.prompt.smart";
1578
+ const POS_KEY = "dsh.prompt.smartPos";
1579
+ /** 智能模式开关:默认开(localStorage 缺省 true);变更广播(设置页 ↔ 悬浮卡实时同步) */
1580
+ const enabledListeners = /* @__PURE__ */ new Set();
1581
+ function isSmartEnabled() {
1582
+ try {
1583
+ const s = globalThis.localStorage;
1584
+ if (!s) return true;
1585
+ const v = s.getItem(SMART_KEY);
1586
+ return v === null ? true : v !== "0";
1587
+ } catch (e) {
1588
+ return true;
1589
+ }
1590
+ }
1591
+ function setSmartEnabled(on) {
1592
+ try {
1593
+ globalThis.localStorage?.setItem(SMART_KEY, on ? "1" : "0");
1594
+ } catch (e) {}
1595
+ enabledListeners.forEach((fn) => {
1596
+ try {
1597
+ fn(on);
1598
+ } catch (e) {}
1599
+ });
1600
+ }
1601
+ function onSmartEnabled(fn) {
1602
+ enabledListeners.add(fn);
1603
+ return () => {
1604
+ enabledListeners.delete(fn);
1605
+ };
1606
+ }
1607
+ function loadSmartPos() {
1608
+ try {
1609
+ const s = globalThis.localStorage;
1610
+ if (!s) return null;
1611
+ const raw = s.getItem(POS_KEY);
1612
+ if (!raw) return null;
1613
+ const p = JSON.parse(raw);
1614
+ return typeof p.x === "number" && typeof p.y === "number" ? p : null;
1615
+ } catch (e) {
1616
+ return null;
1617
+ }
1618
+ }
1619
+ function saveSmartPos(p) {
1620
+ try {
1621
+ globalThis.localStorage?.setItem(POS_KEY, JSON.stringify(p));
1622
+ } catch (e) {}
1623
+ }
1624
+ /** 插入抑制:插入后 draft 变为新值 → 卡片不重现;用户改动草稿后自动解除 */
1625
+ let suppressDraft = null;
1626
+ function suppressCard(draft) {
1627
+ suppressDraft = draft;
1628
+ }
1629
+ function isSuppressed(draft) {
1630
+ return suppressDraft !== null && suppressDraft === draft;
1631
+ }
1632
+ function clearSuppression(draft) {
1633
+ if (suppressDraft !== null && suppressDraft !== draft) suppressDraft = null;
1634
+ }
1635
+ //#endregion
1636
+ //#region src/client/settings.ts
1637
+ /**
1638
+ * dsh-prompt — 设置页模板管理(settings.plugins.tab)+ 智能模式开关
1639
+ */
1640
+ function SettingsPage(props) {
1641
+ const react = getReact();
1642
+ if (!react) return null;
1643
+ const h = react.createElement;
1644
+ const smartState = react.useState(isSmartEnabled());
1645
+ const smartOn = smartState[0];
1646
+ const lang = getLang();
1647
+ const t = (k) => tr(lang, STR[k]);
1648
+ const row = {
1649
+ display: "flex",
1650
+ alignItems: "center",
1651
+ gap: 8,
1652
+ padding: "10px 4px",
1653
+ fontFamily: "var(--dsw-font-family)",
1654
+ fontSize: 12.5,
1655
+ color: "var(--dsw-alias-label-primary)"
1656
+ };
1657
+ const linkRow = {
1658
+ display: "flex",
1659
+ gap: 18,
1660
+ padding: "12px 4px 2px",
1661
+ borderTop: "1px solid var(--dsw-alias-border-l1)",
1662
+ fontSize: "0.88em",
1663
+ fontFamily: "var(--dsw-font-family)"
1664
+ };
1665
+ const linkStyle = {
1666
+ color: "var(--dsw-specific-accent,#f0a45c)",
1667
+ cursor: "pointer",
1668
+ textDecoration: "none",
1669
+ display: "inline-flex",
1670
+ alignItems: "center",
1671
+ gap: 5
1672
+ };
1673
+ return h("div", { style: {
1674
+ padding: 4,
1675
+ display: "flex",
1676
+ flexDirection: "column"
1677
+ } }, [
1678
+ h("div", { style: linkRow }, [h("a", {
1679
+ style: linkStyle,
1680
+ href: "https://github.com/FeatherHunter/dsh-prompt",
1681
+ target: "_blank",
1682
+ rel: "noreferrer"
1683
+ }, "⛭ " + t("gitHubRepo")), h("a", {
1684
+ style: linkStyle,
1685
+ href: "https://github.com/FeatherHunter/dsh-prompt/issues/new",
1686
+ target: "_blank",
1687
+ rel: "noreferrer"
1688
+ }, "⚠ " + t("feedback"))]),
1689
+ h("label", {
1690
+ style: row,
1691
+ title: t("smartToggleHint")
1692
+ }, [h("input", {
1693
+ type: "checkbox",
1694
+ checked: smartOn,
1695
+ onChange: (e) => {
1696
+ const on = e.target.checked;
1697
+ smartState[1](on);
1698
+ setSmartEnabled(on);
1699
+ }
1700
+ }), h("span", null, t("smartToggle"))]),
1701
+ h(TemplateBrowser, { compact: false })
1702
+ ]);
1703
+ }
1704
+ //#endregion
1705
+ //#region src/client/trigger.ts
1706
+ const SOURCE_NAME = "prompt";
1707
+ const MAX_ITEMS = 30;
1708
+ /**
1709
+ * /prompt 过滤:先剥离源名前缀(/prompt 本身 → 全量列出;/prompt <词> → 按词过滤),
1710
+ * 剩余词匹配 名称/英文名/正文/领域/阶段/动作/标签(场景标签手动筛选的检索底座)。
1711
+ */
1712
+ function filterPromptTemplates(query) {
1713
+ let q = (query || "").trim();
1714
+ const ql = q.toLowerCase();
1715
+ if (ql === SOURCE_NAME.toLowerCase() || ql.startsWith("prompt ") || ql.startsWith("prompt ") || ql.startsWith("prompt\n")) q = q.slice(6).trim();
1716
+ if (!q) return sortedTemplates(allTemplates());
1717
+ const needle = q.toLowerCase();
1718
+ return sortedTemplates(allTemplates().filter((x) => templateHaystack(x).indexOf(needle) >= 0));
1719
+ }
1720
+ /** /prompt 触发源(inputTriggers source 契约:candidates → onPick → {text} span 替换) */
1721
+ function buildPromptSource() {
1722
+ return {
1723
+ trigger: "/",
1724
+ name: SOURCE_NAME,
1725
+ order: 5,
1726
+ candidates: async (_projection, req) => filterPromptTemplates(req.query).slice(0, MAX_ITEMS).map((t) => ({
1727
+ name: t.name,
1728
+ description: displayTag(t) + " · " + t.stage + " — " + (t.body || "").replace(/\s+/g, " ").slice(0, 42),
1729
+ templateId: t.id
1730
+ })),
1731
+ onPick: (pick) => {
1732
+ const tpl = getTemplate(pick.candidate.templateId);
1733
+ if (!tpl) return void 0;
1734
+ bumpUsage(tpl.id);
1735
+ return { text: tpl.body };
1736
+ }
1737
+ };
1738
+ }
1739
+ //#endregion
1740
+ //#region src/client/words.ts
1741
+ const SMART_WORDS = {
1742
+ fp: {
1743
+ strong: [
1744
+ "第一性原理",
1745
+ "基本组成单元",
1746
+ "不可改变的真理",
1747
+ "颠覆",
1748
+ "去掉假设"
1749
+ ],
1750
+ weak: [
1751
+ "分析",
1752
+ "本质",
1753
+ "假设",
1754
+ "拆解",
1755
+ "主题"
1756
+ ]
1757
+ },
1758
+ socratic: {
1759
+ strong: [
1760
+ "苏格拉底",
1761
+ "追问",
1762
+ "质疑",
1763
+ "复述观点",
1764
+ "没想到的问题"
1765
+ ],
1766
+ weak: [
1767
+ "思考",
1768
+ "问题",
1769
+ "假设",
1770
+ "观点"
1771
+ ]
1772
+ },
1773
+ deep: {
1774
+ strong: [
1775
+ "深度分析",
1776
+ "多维",
1777
+ "常见误解",
1778
+ "边界",
1779
+ "证据"
1780
+ ],
1781
+ weak: [
1782
+ "分析",
1783
+ "本质",
1784
+ "判断",
1785
+ "主题"
1786
+ ]
1787
+ },
1788
+ adversarial: {
1789
+ strong: [
1790
+ "对抗式",
1791
+ "红队",
1792
+ "反例",
1793
+ "推翻",
1794
+ "失败场景",
1795
+ "认知偏差"
1796
+ ],
1797
+ weak: [
1798
+ "审查",
1799
+ "方案",
1800
+ "结论",
1801
+ "假设"
1802
+ ]
1803
+ },
1804
+ decision: {
1805
+ strong: [
1806
+ "决策",
1807
+ "选项",
1808
+ "纠结",
1809
+ "后悔",
1810
+ "最坏",
1811
+ "沉没成本",
1812
+ "没有回头路"
1813
+ ],
1814
+ weak: [
1815
+ "选择",
1816
+ "权衡",
1817
+ "处境",
1818
+ "分析"
1819
+ ]
1820
+ },
1821
+ premortem: {
1822
+ strong: [
1823
+ "事前验尸",
1824
+ "premortem",
1825
+ "失败原因",
1826
+ "回溯",
1827
+ "预防",
1828
+ "致命缺陷",
1829
+ "早期信号"
1830
+ ],
1831
+ weak: [
1832
+ "方案",
1833
+ "项目",
1834
+ "风险",
1835
+ "失败"
1836
+ ]
1837
+ },
1838
+ feynman: {
1839
+ strong: [
1840
+ "费曼",
1841
+ "类比",
1842
+ "反例",
1843
+ "禁止术语",
1844
+ "判断题"
1845
+ ],
1846
+ weak: [
1847
+ "理解",
1848
+ "概念",
1849
+ "解释"
1850
+ ]
1851
+ },
1852
+ fivewhys: {
1853
+ strong: [
1854
+ "五个为什么",
1855
+ "5why",
1856
+ "根本原因",
1857
+ "根因",
1858
+ "连续追问"
1859
+ ],
1860
+ weak: [
1861
+ "为什么",
1862
+ "问题",
1863
+ "原因"
1864
+ ]
1865
+ },
1866
+ mece: {
1867
+ strong: [
1868
+ "mece",
1869
+ "相互独立",
1870
+ "完全穷尽",
1871
+ "不重不漏",
1872
+ "分类维度"
1873
+ ],
1874
+ weak: [
1875
+ "拆解",
1876
+ "分类",
1877
+ "问题"
1878
+ ]
1879
+ },
1880
+ bias: {
1881
+ strong: [
1882
+ "认知偏差",
1883
+ "确认偏误",
1884
+ "锚定",
1885
+ "群体思维",
1886
+ "反事实",
1887
+ "可得性"
1888
+ ],
1889
+ weak: [
1890
+ "偏差",
1891
+ "检查",
1892
+ "推理",
1893
+ "结论"
1894
+ ]
1895
+ },
1896
+ learnpath: {
1897
+ strong: [
1898
+ "学习路线",
1899
+ "从零到实战",
1900
+ "伪学习",
1901
+ "最小闭环",
1902
+ "掌握程度"
1903
+ ],
1904
+ weak: [
1905
+ "学习",
1906
+ "路线",
1907
+ "技能",
1908
+ "入门",
1909
+ "进阶"
1910
+ ]
1911
+ },
1912
+ concept: {
1913
+ strong: [
1914
+ "概念澄清",
1915
+ "本质区别",
1916
+ "一句话定义",
1917
+ "对比场景",
1918
+ "不适用"
1919
+ ],
1920
+ weak: [
1921
+ "概念",
1922
+ "理解",
1923
+ "定义"
1924
+ ]
1925
+ },
1926
+ codereview: {
1927
+ strong: [
1928
+ "代码审查",
1929
+ "code review",
1930
+ "review",
1931
+ "pr",
1932
+ "正确性",
1933
+ "并发",
1934
+ "注入",
1935
+ "可维护性",
1936
+ "性能"
1937
+ ],
1938
+ weak: ["安全", "技术栈"]
1939
+ },
1940
+ testdesign: {
1941
+ strong: [
1942
+ "测试用例",
1943
+ "正常路径",
1944
+ "边界值",
1945
+ "异常",
1946
+ "幂等",
1947
+ "并发"
1948
+ ],
1949
+ weak: [
1950
+ "测试",
1951
+ "用例",
1952
+ "功能"
1953
+ ]
1954
+ },
1955
+ refactor: {
1956
+ strong: [
1957
+ "重构",
1958
+ "refactor",
1959
+ "小步",
1960
+ "可测试性",
1961
+ "耦合"
1962
+ ],
1963
+ weak: [
1964
+ "代码",
1965
+ "方案",
1966
+ "结构",
1967
+ "职责"
1968
+ ]
1969
+ },
1970
+ explain: {
1971
+ strong: [
1972
+ "解释代码",
1973
+ "执行流程",
1974
+ "为什么这样写",
1975
+ "设计取舍",
1976
+ "扮演的角色"
1977
+ ],
1978
+ weak: [
1979
+ "代码",
1980
+ "解释",
1981
+ "讲解"
1982
+ ]
1983
+ },
1984
+ blindspot: {
1985
+ strong: [
1986
+ "盲区",
1987
+ "遗漏",
1988
+ "检查清单",
1989
+ "关键变量"
1990
+ ],
1991
+ weak: [
1992
+ "计划",
1993
+ "开始",
1994
+ "任务",
1995
+ "启动",
1996
+ "风险"
1997
+ ]
1998
+ },
1999
+ snapshot: {
2000
+ strong: [
2001
+ "零丢失",
2002
+ "快照",
2003
+ "出处",
2004
+ "逐条核对",
2005
+ "可疑遗漏"
2006
+ ],
2007
+ weak: [
2008
+ "记录",
2009
+ "固化",
2010
+ "里程碑",
2011
+ "会话"
2012
+ ]
2013
+ },
2014
+ kickoff: {
2015
+ strong: [
2016
+ "任务启动",
2017
+ "kickoff",
2018
+ "成功标准",
2019
+ "澄清",
2020
+ "经验水平"
2021
+ ],
2022
+ weak: [
2023
+ "任务",
2024
+ "开始",
2025
+ "目标",
2026
+ "启动"
2027
+ ]
2028
+ },
2029
+ prototype: {
2030
+ strong: [
2031
+ "原型",
2032
+ "试错",
2033
+ "候选方案",
2034
+ "快速验证"
2035
+ ],
2036
+ weak: [
2037
+ "想法",
2038
+ "验证",
2039
+ "方案",
2040
+ "方向"
2041
+ ]
2042
+ },
2043
+ plan: {
2044
+ strong: [
2045
+ "执行计划",
2046
+ "里程碑",
2047
+ "验收标准",
2048
+ "依赖"
2049
+ ],
2050
+ weak: [
2051
+ "计划",
2052
+ "目标",
2053
+ "安排",
2054
+ "步骤"
2055
+ ]
2056
+ },
2057
+ deviation: {
2058
+ strong: [
2059
+ "偏离",
2060
+ "记录偏离",
2061
+ "关键决定",
2062
+ "变更"
2063
+ ],
2064
+ weak: [
2065
+ "记录",
2066
+ "过程",
2067
+ "复盘"
2068
+ ]
2069
+ },
2070
+ retro: {
2071
+ strong: [
2072
+ "复盘",
2073
+ "retro",
2074
+ "重来一次",
2075
+ "可复用经验",
2076
+ "实际 vs 计划"
2077
+ ],
2078
+ weak: [
2079
+ "回顾",
2080
+ "总结",
2081
+ "任务",
2082
+ "项目"
2083
+ ]
2084
+ },
2085
+ quiz: {
2086
+ strong: [
2087
+ "反向考验",
2088
+ "出题",
2089
+ "选择题",
2090
+ "判断题",
2091
+ "盲区"
2092
+ ],
2093
+ weak: [
2094
+ "考验",
2095
+ "检验",
2096
+ "主题",
2097
+ "理解"
2098
+ ]
2099
+ }
2100
+ };
2101
+ /** 命中统计:专属词×2 + 通用词×1(子串匹配,忽略大小写) */
2102
+ function scoreDraft(draft, tpl) {
2103
+ const words = SMART_WORDS[tpl.id];
2104
+ if (!words) return {
2105
+ score: 0,
2106
+ strong: [],
2107
+ weak: []
2108
+ };
2109
+ const text = draft.toLowerCase();
2110
+ const strong = words.strong.filter((w) => text.includes(w.toLowerCase()));
2111
+ const weak = words.weak.filter((w) => text.includes(w.toLowerCase()));
2112
+ return {
2113
+ score: strong.length * 2 + weak.length,
2114
+ strong,
2115
+ weak
2116
+ };
2117
+ }
2118
+ /** 代码块降权:draft 中 ``` 出现奇数次 → 视为处于代码块内 → 抑制出卡 */
2119
+ function insideCodeBlock(draft) {
2120
+ let n = 0;
2121
+ let i = 0;
2122
+ for (;;) {
2123
+ i = draft.indexOf("```", i);
2124
+ if (i < 0) break;
2125
+ n++;
2126
+ i += 3;
2127
+ }
2128
+ return n % 2 === 1;
2129
+ }
2130
+ /** 智能候选:draft → ≤3 条(top-2 评分 + 1 最近使用),不足不凑;自定义无词表,仅经最近使用槽进入 */
2131
+ function smartCandidates(draft) {
2132
+ const text = draft.trim().toLowerCase();
2133
+ if (!text || text.length < 2) return [];
2134
+ if (insideCodeBlock(draft)) return [];
2135
+ const usage = loadUsage();
2136
+ const scored = [];
2137
+ for (const tpl of allTemplates()) {
2138
+ if (!tpl.builtin) continue;
2139
+ const r = scoreDraft(text, tpl);
2140
+ if (r.score >= 2) scored.push({
2141
+ tpl,
2142
+ score: r.score,
2143
+ strongHits: r.strong,
2144
+ weakHits: r.weak
2145
+ });
2146
+ }
2147
+ scored.sort((a, b) => b.score - a.score || (usage[b.tpl.id] || 0) - (usage[a.tpl.id] || 0));
2148
+ const top = scored.slice(0, 2);
2149
+ const used = new Set(top.map((s) => s.tpl.id));
2150
+ const recentId = loadLastUsed();
2151
+ if (recentId && !used.has(recentId)) {
2152
+ const tpl = getTemplate(recentId);
2153
+ if (tpl) top.push({
2154
+ tpl,
2155
+ score: 0,
2156
+ strongHits: [],
2157
+ weakHits: []
2158
+ });
2159
+ }
2160
+ return top;
2161
+ }
2162
+ /** DEC11 修订:插入后光标定位到第一个字段的冒号后(冒号表单式正文);无字段 → 正文末尾 */
2163
+ function firstFieldCaret(body) {
2164
+ const i = body.indexOf(":");
2165
+ return i < 0 ? body.length : i + 1;
2166
+ }
2167
+ //#endregion
2168
+ //#region src/client/smart.ts
2169
+ /**
2170
+ * dsh-prompt — 智能模式悬浮卡(v1.1,shell.overlay root 作用域)
2171
+ * #5 定稿:全局单手柄点(点=缩小的卡,卡显示时点消失,互斥)/ 卡从点向右展开 /
2172
+ * 自由拖动 + 位置记忆(localStorage)+ 视口 clamp / 仅命中出现 / ≤3 候选(top-2 评分 + 最近使用,不足不凑)/
2173
+ * 评分=专属词×2+通用词×1(≥2 出卡)/ 排序=评分→用量 / 点击即填入 + 光标定位首字段冒号后(DEC11 修订)/
2174
+ * 默认开可配置关闭 / 与面板各管各的;键盘可达(↑↓/Enter/Esc)。
2175
+ */
2176
+ const DOT_SIZE = 12;
2177
+ const CARD_W = 280;
2178
+ const CARD_H_EST = 150;
2179
+ function clampPos(p, w, h) {
2180
+ const vw = typeof window !== "undefined" ? window.innerWidth : 1280;
2181
+ const vh = typeof window !== "undefined" ? window.innerHeight : 800;
2182
+ return {
2183
+ x: Math.max(8, Math.min(p.x, vw - w - 8)),
2184
+ y: Math.max(8, Math.min(p.y, vh - h - 8))
2185
+ };
2186
+ }
2187
+ /** 读取当前输入框草稿:优先焦点 textarea,其次可见 textarea;无 → 空串 */
2188
+ function readActiveDraft() {
2189
+ try {
2190
+ if (typeof document === "undefined") return "";
2191
+ const ae = document.activeElement;
2192
+ if (ae && ae.tagName === "TEXTAREA") return ae.value || "";
2193
+ const tas = document.querySelectorAll("textarea");
2194
+ for (let i = 0; i < tas.length; i++) {
2195
+ const ta = tas[i];
2196
+ if (ta.offsetParent !== null) return ta.value || "";
2197
+ }
2198
+ } catch (e) {}
2199
+ return "";
2200
+ }
2201
+ /** 光标位置:焦点 textarea(value===draft)的 selectionStart,找不到 → 末尾 */
2202
+ function caretInDraft(draft) {
2203
+ try {
2204
+ if (typeof document === "undefined") return draft.length;
2205
+ const tas = document.querySelectorAll("textarea");
2206
+ for (let i = 0; i < tas.length; i++) {
2207
+ const ta = tas[i];
2208
+ if (ta.value === draft && typeof ta.selectionStart === "number") return ta.selectionStart;
2209
+ }
2210
+ } catch (e) {}
2211
+ return draft.length;
2212
+ }
2213
+ /** 智能插入:光标处插入模板正文(不覆盖),光标定位到首字段冒号后;用量+1;抑制卡片重现 */
2214
+ function smartInsert(body, id) {
2215
+ const { actions } = getSmartInput();
2216
+ if (!actions || typeof actions.setDraft !== "function") return;
2217
+ const draft = readActiveDraft();
2218
+ const caret = caretInDraft(draft);
2219
+ const newDraft = draft.slice(0, caret) + body + draft.slice(caret);
2220
+ actions.setDraft(newDraft);
2221
+ const fieldCaret = caret + firstFieldCaret(body);
2222
+ setTimeout(() => {
2223
+ try {
2224
+ if (typeof document === "undefined") return;
2225
+ const tas = document.querySelectorAll("textarea");
2226
+ for (let i = 0; i < tas.length; i++) {
2227
+ const ta = tas[i];
2228
+ if (ta.value === newDraft) {
2229
+ ta.focus();
2230
+ ta.setSelectionRange(fieldCaret, fieldCaret);
2231
+ break;
2232
+ }
2233
+ }
2234
+ } catch (e) {}
2235
+ }, 0);
2236
+ bumpUsage(id);
2237
+ suppressCard(newDraft);
2238
+ }
2239
+ function SmartCardHost(props) {
2240
+ const react = getReact();
2241
+ if (!react) return null;
2242
+ const h = react.createElement;
2243
+ const [enabled, setEnabled] = react.useState(isSmartEnabled());
2244
+ const [input, setInput] = react.useState(getSmartInput());
2245
+ const [draft, setDraft] = react.useState(readActiveDraft());
2246
+ const [pos, setPos] = react.useState(null);
2247
+ const [dismissed, setDismissed] = react.useState(false);
2248
+ const [manualOpen, setManualOpen] = react.useState(false);
2249
+ const [cardH, setCardH] = react.useState(0);
2250
+ const posRef = react.useRef(null);
2251
+ const cardRef = react.useRef(null);
2252
+ const rowsRef = react.useRef([]);
2253
+ const dragMovedRef = react.useRef(false);
2254
+ react.useEffect(() => {
2255
+ const off1 = onSmartInput(() => setInput(getSmartInput()));
2256
+ const off2 = onSmartEnabled((on) => setEnabled(on));
2257
+ return () => {
2258
+ off1();
2259
+ off2();
2260
+ };
2261
+ }, []);
2262
+ react.useEffect(() => {
2263
+ const timer = setInterval(() => {
2264
+ const d = readActiveDraft();
2265
+ setDraft((prev) => prev === d ? prev : d);
2266
+ }, 350);
2267
+ const onFocus = () => {
2268
+ setDraft(readActiveDraft());
2269
+ };
2270
+ if (typeof document !== "undefined") document.addEventListener("focusin", onFocus, true);
2271
+ return () => {
2272
+ clearInterval(timer);
2273
+ if (typeof document !== "undefined") document.removeEventListener("focusin", onFocus, true);
2274
+ };
2275
+ }, []);
2276
+ react.useEffect(() => {
2277
+ typeof window !== "undefined" && window.innerWidth;
2278
+ const vh = typeof window !== "undefined" ? window.innerHeight : 800;
2279
+ const p = clampPos(loadSmartPos() || {
2280
+ x: 32,
2281
+ y: Math.round(vh * .72)
2282
+ }, DOT_SIZE, DOT_SIZE);
2283
+ posRef.current = p;
2284
+ setPos(p);
2285
+ }, []);
2286
+ const applyPos = (p) => {
2287
+ const c = clampPos(p, DOT_SIZE, DOT_SIZE);
2288
+ posRef.current = c;
2289
+ setPos(c);
2290
+ };
2291
+ react.useEffect(() => {
2292
+ clearSuppression(draft);
2293
+ if (dismissed) setDismissed(false);
2294
+ if (manualOpen) setManualOpen(false);
2295
+ }, [draft]);
2296
+ const lang = getLang();
2297
+ const t = (k) => tr(lang, STR[k]);
2298
+ const suppressed = isSuppressed(draft);
2299
+ const candidates = !enabled || suppressed ? [] : smartCandidates(draft);
2300
+ const fallbackRows = allTemplates().slice(0, 3).map((tpl) => ({
2301
+ tpl,
2302
+ score: 0,
2303
+ strongHits: [],
2304
+ weakHits: []
2305
+ }));
2306
+ const rows = candidates.length > 0 ? candidates : manualOpen ? fallbackRows : [];
2307
+ rowsRef.current = rows;
2308
+ const showCard = rows.length > 0 && !dismissed;
2309
+ react.useEffect(() => {
2310
+ if (!showCard) return;
2311
+ const el = cardRef.current;
2312
+ if (!el) return;
2313
+ const h = el.offsetHeight || 0;
2314
+ if (h > 0 && h !== cardH) setCardH(h);
2315
+ }, [showCard, rows.length]);
2316
+ const doPick = (c) => {
2317
+ smartInsert(c.tpl.body, c.tpl.id);
2318
+ setDismissed(true);
2319
+ setManualOpen(false);
2320
+ };
2321
+ const startDrag = (e) => {
2322
+ e.preventDefault();
2323
+ e.stopPropagation();
2324
+ dragMovedRef.current = false;
2325
+ const base = posRef.current || {
2326
+ x: 0,
2327
+ y: 0
2328
+ };
2329
+ const sx = e.clientX, sy = e.clientY;
2330
+ const move = (ev) => {
2331
+ dragMovedRef.current = true;
2332
+ applyPos({
2333
+ x: base.x + (ev.clientX - sx),
2334
+ y: base.y + (ev.clientY - sy)
2335
+ });
2336
+ };
2337
+ const up = (ev) => {
2338
+ document.removeEventListener("pointermove", move);
2339
+ document.removeEventListener("pointerup", up);
2340
+ const p = clampPos({
2341
+ x: base.x + (ev.clientX - sx),
2342
+ y: base.y + (ev.clientY - sy)
2343
+ }, DOT_SIZE, DOT_SIZE);
2344
+ posRef.current = p;
2345
+ setPos(p);
2346
+ saveSmartPos(p);
2347
+ };
2348
+ document.addEventListener("pointermove", move);
2349
+ document.addEventListener("pointerup", up);
2350
+ };
2351
+ const vw = typeof window !== "undefined" ? window.innerWidth : 1280;
2352
+ const vh = typeof window !== "undefined" ? window.innerHeight : 800;
2353
+ const effectivePos = pos !== null ? pos : {
2354
+ x: 32,
2355
+ y: Math.round(vh * .72)
2356
+ };
2357
+ if (!enabled) return null;
2358
+ const base = "var(--dsw-alias-label-primary)";
2359
+ const muted = "var(--dsw-alias-label-secondary)";
2360
+ const dim = "var(--dsw-alias-label-tertiary)";
2361
+ const line = "1px solid var(--dsw-alias-border-l1)";
2362
+ const dot = h("div", {
2363
+ key: "dot",
2364
+ style: {
2365
+ position: "fixed",
2366
+ left: effectivePos.x,
2367
+ top: effectivePos.y,
2368
+ width: DOT_SIZE,
2369
+ height: DOT_SIZE,
2370
+ borderRadius: "50%",
2371
+ background: "var(--dsw-alias-label-tertiary)",
2372
+ opacity: .45,
2373
+ boxShadow: "var(--dsw-shadow-lv1)",
2374
+ cursor: "grab",
2375
+ zIndex: 400,
2376
+ pointerEvents: "auto",
2377
+ userSelect: "none"
2378
+ },
2379
+ title: t("smartDot"),
2380
+ onPointerDown: startDrag,
2381
+ onClick: () => {
2382
+ if (dragMovedRef.current) return;
2383
+ setManualOpen(true);
2384
+ setDismissed(false);
2385
+ }
2386
+ });
2387
+ const dotX = effectivePos.x, dotY = effectivePos.y;
2388
+ const cardHeight = cardH > 0 ? cardH : CARD_H_EST;
2389
+ const dotCx = dotX + DOT_SIZE / 2, dotCy = dotY + DOT_SIZE / 2;
2390
+ let cardX = dotCx;
2391
+ if (cardX + CARD_W > vw - 8) cardX = Math.max(8, dotCx - CARD_W);
2392
+ cardX = Math.max(8, Math.min(cardX, vw - CARD_W - 8));
2393
+ let cardY = dotCy - cardHeight;
2394
+ if (cardY < 8) cardY = 8;
2395
+ cardY = Math.max(8, Math.min(cardY, vh - cardHeight - 8));
2396
+ const cardPos = {
2397
+ x: cardX,
2398
+ y: cardY
2399
+ };
2400
+ const cardStyle = {
2401
+ position: "fixed",
2402
+ left: cardPos.x,
2403
+ top: cardPos.y,
2404
+ width: CARD_W,
2405
+ zIndex: 400,
2406
+ pointerEvents: "auto",
2407
+ background: "var(--dsw-specific-menu)",
2408
+ border: "1px solid var(--dsw-alias-border-inverted)",
2409
+ borderRadius: 12,
2410
+ boxShadow: "var(--dsw-shadow-lv3)",
2411
+ display: "flex",
2412
+ flexDirection: "column",
2413
+ gap: 6,
2414
+ padding: "8px 10px",
2415
+ fontFamily: "var(--dsw-font-family)",
2416
+ fontSize: "var(--dsw-font-markdown-base-font-size)",
2417
+ color: base
2418
+ };
2419
+ const headStyle = {
2420
+ display: "flex",
2421
+ alignItems: "center",
2422
+ gap: 4,
2423
+ padding: "4px 10px",
2424
+ borderBottom: line,
2425
+ cursor: "grab",
2426
+ minWidth: 0
2427
+ };
2428
+ const rowStyle = {
2429
+ display: "flex",
2430
+ alignItems: "center",
2431
+ gap: 6,
2432
+ padding: "3px 7px",
2433
+ minWidth: 0,
2434
+ background: "var(--dsw-alias-bg-layer-2)",
2435
+ border: "1px solid var(--dsw-alias-border-l1)",
2436
+ borderRadius: 8,
2437
+ cursor: "pointer",
2438
+ overflow: "hidden"
2439
+ };
2440
+ const rowNum = {
2441
+ color: dim,
2442
+ fontSize: "0.77em",
2443
+ flex: "none"
2444
+ };
2445
+ const rowName = {
2446
+ fontWeight: 500,
2447
+ fontSize: "0.88em",
2448
+ flex: "none",
2449
+ whiteSpace: "nowrap",
2450
+ overflow: "hidden",
2451
+ textOverflow: "ellipsis",
2452
+ maxWidth: 90
2453
+ };
2454
+ const rowTag = {
2455
+ color: muted,
2456
+ flex: "none",
2457
+ fontSize: "0.77em"
2458
+ };
2459
+ const rowHint = {
2460
+ color: dim,
2461
+ flex: "1 1 auto",
2462
+ minWidth: 0,
2463
+ overflow: "hidden",
2464
+ textOverflow: "ellipsis",
2465
+ whiteSpace: "nowrap",
2466
+ fontSize: "0.77em"
2467
+ };
2468
+ const fillBtn = {
2469
+ flex: "none",
2470
+ border: "1px solid var(--dsw-alias-border-l2)",
2471
+ background: "var(--dsw-alias-bg-layer-3)",
2472
+ color: base,
2473
+ borderRadius: 5,
2474
+ padding: "1px 7px",
2475
+ cursor: "pointer",
2476
+ fontFamily: "var(--dsw-font-family)",
2477
+ fontSize: "0.8em",
2478
+ fontWeight: 500,
2479
+ whiteSpace: "nowrap"
2480
+ };
2481
+ const rowNodes = rows.map((c, i) => {
2482
+ const common = c.score === 0;
2483
+ const domain = c.tpl.domain || c.tpl.tag || "";
2484
+ const tagText = (domain ? domain + " " : "") + (common ? "·常用" : "·分" + c.score);
2485
+ const hits = common ? t("smartCommon") : c.strongHits.join(" / ") + (c.weakHits.length ? " · " + c.weakHits.join(" / ") : "");
2486
+ return h("div", {
2487
+ key: c.tpl.id,
2488
+ style: rowStyle,
2489
+ onClick: () => doPick(c),
2490
+ title: t("smartFill")
2491
+ }, [
2492
+ h("span", { style: rowNum }, String(i + 1)),
2493
+ h("span", { style: rowName }, c.tpl.name),
2494
+ h("span", { style: rowTag }, tagText),
2495
+ h("span", { style: rowHint }, hits),
2496
+ h("button", {
2497
+ style: fillBtn,
2498
+ title: t("smartFill"),
2499
+ onClick: (e) => {
2500
+ e.stopPropagation();
2501
+ doPick(c);
2502
+ }
2503
+ }, t("smartFill"))
2504
+ ]);
2505
+ });
2506
+ const card = h("div", {
2507
+ key: "card",
2508
+ ref: cardRef,
2509
+ style: cardStyle
2510
+ }, [h("div", {
2511
+ style: headStyle,
2512
+ onPointerDown: startDrag
2513
+ }, [
2514
+ h("span", { style: {
2515
+ fontWeight: 600,
2516
+ fontSize: "0.85em"
2517
+ } }, t("smartTitle")),
2518
+ h("span", { style: {
2519
+ color: dim,
2520
+ fontSize: "0.77em"
2521
+ } }, t("smartHint")),
2522
+ h("div", { style: { flex: 1 } }),
2523
+ h("button", {
2524
+ style: {
2525
+ border: 0,
2526
+ background: "transparent",
2527
+ color: dim,
2528
+ cursor: "pointer",
2529
+ fontSize: "0.92em",
2530
+ lineHeight: 1,
2531
+ padding: "0 2px",
2532
+ flex: "none"
2533
+ },
2534
+ title: t("smartDismiss"),
2535
+ onPointerDown: (e) => e.stopPropagation(),
2536
+ onClick: (e) => {
2537
+ e.stopPropagation();
2538
+ setDismissed(true);
2539
+ setManualOpen(false);
2540
+ }
2541
+ }, "×")
2542
+ ]), h("div", { style: {
2543
+ maxHeight: 220,
2544
+ overflowY: "auto",
2545
+ overflowX: "hidden"
2546
+ } }, rowNodes)]);
2547
+ return showCard ? card : dot;
2548
+ }
2549
+ //#endregion
2550
+ //#region src/client/index.ts
2551
+ /**
2552
+ * dsh-prompt — client 入口(v1 常规模式 + v1.1 智能模式)
2553
+ * 装配:input.left 入口按钮 / input.overlay 面板浮层 / settings.section 配置页(直属设置面板)/ inputTriggers /prompt 触发源(#9)/ shell.overlay 智能悬浮卡(#10)
2554
+ */
2555
+ const inject = ["slots", "inputTriggers"];
2556
+ /** 面板浮层(conversation.input.overlay,session 作用域 → 有 useInput/inputActions) */
2557
+ function PanelHost(props) {
2558
+ const react = getReact();
2559
+ if (!react) return null;
2560
+ const h = react.createElement;
2561
+ const openState = react.useState(isPanelOpen());
2562
+ const open = openState[0];
2563
+ react.useEffect(() => {
2564
+ onPanelOpen((v) => openState[1](v));
2565
+ console.log("[dsh-prompt] PanelHost props:", Object.keys(props), "| useInput?", !!props.useInput, "| inputActions?", !!props.inputActions);
2566
+ }, []);
2567
+ react.useEffect(() => {
2568
+ let st = null;
2569
+ try {
2570
+ st = props.useInput ? props.useInput((s) => s) : null;
2571
+ } catch (e) {}
2572
+ setSmartInput({
2573
+ sessionId: props.sessionId,
2574
+ draft: st && st.draft || "",
2575
+ useInput: props.useInput,
2576
+ actions: props.inputActions
2577
+ });
2578
+ });
2579
+ react.useEffect(() => {
2580
+ const sid = props.sessionId;
2581
+ return () => {
2582
+ if (sid !== void 0) setSmartInput({ draft: "" });
2583
+ };
2584
+ }, [props.sessionId]);
2585
+ if (!open) return null;
2586
+ return h(TemplateBrowser, {
2587
+ compact: true,
2588
+ useInput: props.useInput,
2589
+ inputActions: props.inputActions
2590
+ });
2591
+ }
2592
+ function apply(ctx) {
2593
+ ctx.effect(() => ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
2594
+ name: "conversation.input.left",
2595
+ id: "dsh-prompt-entry",
2596
+ order: 10,
2597
+ label: () => "dsh-prompt"
2598
+ }, (props) => {
2599
+ const react = getReact();
2600
+ if (!react) return null;
2601
+ const h = react.createElement;
2602
+ const openState = react.useState(isPanelOpen());
2603
+ react.useEffect(() => onPanelOpen((v) => openState[1](v)), []);
2604
+ return h(EntryButton, { open: openState[0] });
2605
+ })), "dsh-prompt: entry");
2606
+ ctx.effect(() => ctx.slots.inject("conversation.input.overlay", () => ctx.slots.register({
2607
+ name: "conversation.input.overlay",
2608
+ id: "dsh-prompt-panel",
2609
+ order: 2,
2610
+ label: () => "dsh-prompt panel"
2611
+ }, PanelHost)), "dsh-prompt: panel");
2612
+ ctx.effect(() => ctx.slots.inject("settings.section", () => ctx.slots.register({
2613
+ name: "settings.section",
2614
+ id: "dsh-prompt-palette",
2615
+ priority: 10,
2616
+ order: 50,
2617
+ label: () => getLang() === "zh" ? "提示词模板" : "Prompt Templates"
2618
+ }, SettingsPage)), "dsh-prompt: settings");
2619
+ setGoSettingsHandler(() => {
2620
+ try {
2621
+ if (typeof document === "undefined") return;
2622
+ const btns = Array.prototype.slice.call(document.querySelectorAll("button[aria-haspopup=\"dialog\"]"));
2623
+ const trig = btns.find((b) => /设置|Settings/.test((b.textContent || "").trim())) || btns[0];
2624
+ if (trig) trig.click();
2625
+ setTimeout(() => {
2626
+ const label = getLang() === "zh" ? "提示词模板" : "Prompt Templates";
2627
+ const cell = Array.prototype.slice.call(document.querySelectorAll("button")).find((b) => (b.textContent || "").trim() === label);
2628
+ if (cell) cell.click();
2629
+ }, 150);
2630
+ } catch (e) {}
2631
+ });
2632
+ if (ctx.inputTriggers && typeof ctx.inputTriggers.registerSource === "function") ctx.effect(() => ctx.inputTriggers.registerSource(buildPromptSource()), "dsh-prompt: /prompt source");
2633
+ ctx.effect(() => ctx.slots.inject("shell.overlay", () => ctx.slots.register({
2634
+ name: "shell.overlay",
2635
+ id: "dsh-prompt-smart",
2636
+ order: 200,
2637
+ label: () => "dsh-prompt smart"
2638
+ }, SmartCardHost)), "dsh-prompt: smart card");
2639
+ }
2640
+ //#endregion
2641
+ exports.apply = apply;
2642
+ exports.inject = inject;
2643
+ return module.exports;
2644
+ }
2645
+ });
2646
+
2647
+ //# sourceMappingURL=client.js.map