dsh-prompt-refine 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/LICENSE +21 -0
- package/README.md +93 -0
- package/cordis.patch.yml +8 -0
- package/lib/client.iife.js +518 -0
- package/lib/index.mjs +272 -0
- package/package.json +57 -0
- package/scripts/install.mjs +97 -0
- package/src/client.ts +387 -0
- package/src/dsh-prompt-refine.ts +24 -0
- package/src/host.ts +239 -0
- package/src/shared/prompt.ts +73 -0
- package/src/shared/types.ts +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 願
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# dsh-prompt-refine
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
|
|
5
|
+
DSH 提示词优化插件 —— 在发送前点一下,让模型结合上下文告诉你这条 prompt 缺什么。
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## 它做什么
|
|
10
|
+
|
|
11
|
+
在输入框发送按钮**左侧**加一个 ✨ 按钮。点击后:
|
|
12
|
+
|
|
13
|
+
1. 读当前草稿 + 最近几条对话历史
|
|
14
|
+
2. 调你已配置好的模型(自动读 `~/.dsh/settings.yaml` 的 `agent-default-model`)
|
|
15
|
+
3. 弹窗列出 **3 条「问题 + 补丁」** 形式的建议
|
|
16
|
+
4. 勾选想要的 → 实时预览合并后的 prompt
|
|
17
|
+
5. 点「采纳 N 条并填入」→ 新 prompt 写回输入框
|
|
18
|
+
|
|
19
|
+
**不抢你的发送权**:填好后你自己按发送。Esc / 点遮罩 / 「跳过」都能随时退出。
|
|
20
|
+
|
|
21
|
+
## 为什么是「补丁」而不是「重写」
|
|
22
|
+
|
|
23
|
+
建议保留你原话,只补缺口:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
原文 写个排序
|
|
27
|
+
|
|
28
|
+
scope 未说明排序对象 → 对一组整数数组进行升序排序。
|
|
29
|
+
format 未指定语言签名 → 请用 Python 实现,函数签名 sort_list(nums)。
|
|
30
|
+
constraint 缺边界约束 → 要求 O(n log n),处理空数组与重复元素。
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
三条可以同时采纳,不用三选一。
|
|
34
|
+
|
|
35
|
+
## 安装
|
|
36
|
+
|
|
37
|
+
### 方式一:npm 安装(推荐)
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cd ~/.dsh/profiles/web # 换成你要装进去的 profile
|
|
41
|
+
pnpm add dsh-prompt-refine
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
包内自带 `dsh.bundle.patch` 声明,安装后把 `dsh-prompt-refine` 加进该 profile `package.json` 的 `dsh.profile.bundles` 数组即可激活,然后重启 DSH。
|
|
45
|
+
|
|
46
|
+
### 方式二:源码安装(开发用)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pnpm install
|
|
50
|
+
pnpm build
|
|
51
|
+
|
|
52
|
+
# 装进 web profile
|
|
53
|
+
node scripts/install.mjs --profile web
|
|
54
|
+
rm -rf ~/.dsh/profiles/web/node_modules/dsh-prompt-refine
|
|
55
|
+
(cd ~/.dsh/profiles/web && pnpm install --force)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 运行(开发模式)
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 在 deepseek-harness 仓库根目录
|
|
62
|
+
pnpm dsh web --patch ./dsh-prompt-refine/cordis.yml --no-open --port 0
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
或者用附带的脚本(自动杀旧实例 + 重启 + 打印新 URL):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bash scripts/dev-restart.sh
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 模型配置
|
|
72
|
+
|
|
73
|
+
**v0.1 无需配置** —— 自动读用户 settings.yaml 里的 `agent-default-model`,
|
|
74
|
+
跟随你平时用的模型。改模型只改那里。
|
|
75
|
+
|
|
76
|
+
v0.2 计划:插件自己的设置页,可单独指定"优化用的模型"(比如用便宜的快模型)。
|
|
77
|
+
|
|
78
|
+
## 文件结构
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
src/
|
|
82
|
+
dsh-prompt-refine.ts host 入口
|
|
83
|
+
host.ts HTTP 端点 + ctx.llm 调用 + 降级
|
|
84
|
+
shared/types.ts 请求/响应形状
|
|
85
|
+
shared/prompt.ts system prompt + JSON 解析
|
|
86
|
+
client.ts ✨ 按钮 + Portal 弹窗
|
|
87
|
+
scripts/
|
|
88
|
+
install.mjs 装进 profile
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# dsh-prompt-refine 的 bundle patch —— 安装到 profile 后自动进配置层
|
|
2
|
+
# name 用包名(不是 file:// 源码路径):loader 从 profile 的 node_modules
|
|
3
|
+
# 解析到编译好的 lib/index.mjs。指向 TS 源码只在仓库内 dev 模式(pnpm dsh,
|
|
4
|
+
# 带 tsx)下能跑;全局 npm 安装的 dsh 是纯 node ESM,会报
|
|
5
|
+
# ERR_MODULE_NOT_FOUND: Cannot find module '...\src\host'。
|
|
6
|
+
- insert:
|
|
7
|
+
- id: dsh-prompt-refine
|
|
8
|
+
name: 'dsh-prompt-refine'
|
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
//#region src/client.ts
|
|
3
|
+
function makeFactory() {
|
|
4
|
+
return (require) => {
|
|
5
|
+
const React = require("react");
|
|
6
|
+
const h = React.createElement;
|
|
7
|
+
const { createPortal } = require("react-dom");
|
|
8
|
+
const name = "dsh-prompt-refine";
|
|
9
|
+
const inject = ["slots"];
|
|
10
|
+
let portalHost = null;
|
|
11
|
+
function getPortalHost() {
|
|
12
|
+
if (portalHost === null || !portalHost.isConnected) {
|
|
13
|
+
portalHost = document.createElement("div");
|
|
14
|
+
portalHost.id = "dsh-prompt-refine-portal";
|
|
15
|
+
document.body.appendChild(portalHost);
|
|
16
|
+
}
|
|
17
|
+
return portalHost;
|
|
18
|
+
}
|
|
19
|
+
const S = {
|
|
20
|
+
btn: {
|
|
21
|
+
padding: 0,
|
|
22
|
+
width: "32px",
|
|
23
|
+
height: "32px",
|
|
24
|
+
borderRadius: "8px",
|
|
25
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
26
|
+
background: "transparent",
|
|
27
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
28
|
+
cursor: "pointer",
|
|
29
|
+
display: "inline-flex",
|
|
30
|
+
alignItems: "center",
|
|
31
|
+
justifyContent: "center",
|
|
32
|
+
fontSize: "15px",
|
|
33
|
+
lineHeight: 1
|
|
34
|
+
},
|
|
35
|
+
overlay: {
|
|
36
|
+
position: "fixed",
|
|
37
|
+
inset: 0,
|
|
38
|
+
background: "rgba(0,0,0,0.55)",
|
|
39
|
+
display: "flex",
|
|
40
|
+
alignItems: "center",
|
|
41
|
+
justifyContent: "center",
|
|
42
|
+
zIndex: 9999,
|
|
43
|
+
padding: "20px"
|
|
44
|
+
},
|
|
45
|
+
modal: {
|
|
46
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
47
|
+
borderRadius: "14px",
|
|
48
|
+
width: "100%",
|
|
49
|
+
maxWidth: "560px",
|
|
50
|
+
maxHeight: "86vh",
|
|
51
|
+
overflowY: "auto",
|
|
52
|
+
boxShadow: "0 20px 60px rgba(0,0,0,0.28)",
|
|
53
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
54
|
+
color: "var(--dsw-alias-label-primary)"
|
|
55
|
+
},
|
|
56
|
+
header: {
|
|
57
|
+
padding: "18px 22px 14px",
|
|
58
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2)"
|
|
59
|
+
},
|
|
60
|
+
title: {
|
|
61
|
+
fontSize: "16px",
|
|
62
|
+
fontWeight: 600,
|
|
63
|
+
margin: "0 0 4px"
|
|
64
|
+
},
|
|
65
|
+
subtitle: {
|
|
66
|
+
fontSize: "13px",
|
|
67
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
68
|
+
margin: 0
|
|
69
|
+
},
|
|
70
|
+
original: {
|
|
71
|
+
margin: "14px 22px 0",
|
|
72
|
+
padding: "10px 12px",
|
|
73
|
+
background: "var(--dsw-alias-bg-layer-1, rgba(0,0,0,0.04))",
|
|
74
|
+
borderRadius: "8px",
|
|
75
|
+
fontSize: "13px",
|
|
76
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
77
|
+
borderLeft: "3px solid var(--dsw-alias-label-tertiary)"
|
|
78
|
+
},
|
|
79
|
+
originalLabel: {
|
|
80
|
+
fontSize: "10px",
|
|
81
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
82
|
+
textTransform: "uppercase",
|
|
83
|
+
letterSpacing: "0.5px",
|
|
84
|
+
marginBottom: "4px"
|
|
85
|
+
},
|
|
86
|
+
list: { padding: "14px 22px" },
|
|
87
|
+
row: {
|
|
88
|
+
display: "flex",
|
|
89
|
+
gap: "12px",
|
|
90
|
+
padding: "12px",
|
|
91
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
92
|
+
borderRadius: "10px",
|
|
93
|
+
marginBottom: "8px",
|
|
94
|
+
cursor: "pointer"
|
|
95
|
+
},
|
|
96
|
+
rowOn: {
|
|
97
|
+
borderColor: "var(--dsw-alias-state-business-primary)",
|
|
98
|
+
background: "var(--dsw-alias-interactive-bg-active, rgba(37,99,235,0.08))"
|
|
99
|
+
},
|
|
100
|
+
rowBody: {
|
|
101
|
+
flex: 1,
|
|
102
|
+
minWidth: 0
|
|
103
|
+
},
|
|
104
|
+
tag: {
|
|
105
|
+
display: "inline-block",
|
|
106
|
+
fontSize: "11px",
|
|
107
|
+
padding: "2px 7px",
|
|
108
|
+
borderRadius: "4px",
|
|
109
|
+
fontWeight: 500,
|
|
110
|
+
marginBottom: "6px"
|
|
111
|
+
},
|
|
112
|
+
tagScope: {
|
|
113
|
+
background: "var(--dsw-alias-state-error-secondary)",
|
|
114
|
+
color: "var(--dsw-alias-state-error-primary)"
|
|
115
|
+
},
|
|
116
|
+
tagFormat: {
|
|
117
|
+
background: "rgba(217,119,6,0.14)",
|
|
118
|
+
color: "#d97706"
|
|
119
|
+
},
|
|
120
|
+
tagConstraint: {
|
|
121
|
+
background: "rgba(37,99,235,0.14)",
|
|
122
|
+
color: "var(--dsw-alias-state-business-primary)"
|
|
123
|
+
},
|
|
124
|
+
tagClarity: {
|
|
125
|
+
background: "rgba(5,150,105,0.16)",
|
|
126
|
+
color: "var(--dsw-alias-state-success-primary)"
|
|
127
|
+
},
|
|
128
|
+
tagOther: {
|
|
129
|
+
background: "rgba(128,128,128,0.16)",
|
|
130
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
131
|
+
},
|
|
132
|
+
issue: {
|
|
133
|
+
fontSize: "13.5px",
|
|
134
|
+
margin: "0 0 8px",
|
|
135
|
+
lineHeight: 1.45
|
|
136
|
+
},
|
|
137
|
+
patch: {
|
|
138
|
+
fontSize: "12.5px",
|
|
139
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
140
|
+
background: "rgba(128,128,128,0.08)",
|
|
141
|
+
padding: "7px 10px",
|
|
142
|
+
borderRadius: "6px",
|
|
143
|
+
margin: 0,
|
|
144
|
+
fontFamily: "ui-monospace, \"SF Mono\", Consolas, monospace",
|
|
145
|
+
border: "1px dashed var(--dsw-alias-border-l2)"
|
|
146
|
+
},
|
|
147
|
+
preview: {
|
|
148
|
+
margin: "0 22px 14px",
|
|
149
|
+
padding: "10px 12px",
|
|
150
|
+
background: "rgba(128,128,128,0.08)",
|
|
151
|
+
borderRadius: "8px",
|
|
152
|
+
fontSize: "13px"
|
|
153
|
+
},
|
|
154
|
+
previewLabel: {
|
|
155
|
+
fontSize: "10px",
|
|
156
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
157
|
+
textTransform: "uppercase",
|
|
158
|
+
letterSpacing: "0.5px",
|
|
159
|
+
marginBottom: "6px",
|
|
160
|
+
display: "flex",
|
|
161
|
+
justifyContent: "space-between"
|
|
162
|
+
},
|
|
163
|
+
previewCount: {
|
|
164
|
+
color: "var(--dsw-alias-state-business-primary)",
|
|
165
|
+
fontWeight: 600
|
|
166
|
+
},
|
|
167
|
+
previewText: { lineHeight: 1.6 },
|
|
168
|
+
added: {
|
|
169
|
+
background: "rgba(5,150,105,0.16)",
|
|
170
|
+
color: "var(--dsw-alias-state-success-primary)",
|
|
171
|
+
padding: "1px 5px",
|
|
172
|
+
borderRadius: "3px",
|
|
173
|
+
margin: "0 2px",
|
|
174
|
+
fontWeight: 500
|
|
175
|
+
},
|
|
176
|
+
footer: {
|
|
177
|
+
padding: "14px 22px",
|
|
178
|
+
borderTop: "1px solid var(--dsw-alias-border-l2)",
|
|
179
|
+
display: "flex",
|
|
180
|
+
justifyContent: "space-between",
|
|
181
|
+
alignItems: "center",
|
|
182
|
+
gap: "12px"
|
|
183
|
+
},
|
|
184
|
+
hint: {
|
|
185
|
+
fontSize: "12px",
|
|
186
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
187
|
+
},
|
|
188
|
+
btnSec: {
|
|
189
|
+
padding: "8px 14px",
|
|
190
|
+
borderRadius: "8px",
|
|
191
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
192
|
+
background: "transparent",
|
|
193
|
+
color: "var(--dsw-alias-label-primary)",
|
|
194
|
+
cursor: "pointer",
|
|
195
|
+
fontSize: "14px"
|
|
196
|
+
},
|
|
197
|
+
btnPri: {
|
|
198
|
+
padding: "8px 14px",
|
|
199
|
+
borderRadius: "8px",
|
|
200
|
+
border: "none",
|
|
201
|
+
background: "var(--dsw-alias-state-business-primary)",
|
|
202
|
+
color: "var(--dsw-alias-button-info-fill, #fff)",
|
|
203
|
+
cursor: "pointer",
|
|
204
|
+
fontSize: "14px",
|
|
205
|
+
fontWeight: 600
|
|
206
|
+
},
|
|
207
|
+
skel: {
|
|
208
|
+
height: "58px",
|
|
209
|
+
background: "rgba(128,128,128,0.14)",
|
|
210
|
+
borderRadius: "10px",
|
|
211
|
+
marginBottom: "8px"
|
|
212
|
+
},
|
|
213
|
+
tip: {
|
|
214
|
+
textAlign: "center",
|
|
215
|
+
fontSize: "13px",
|
|
216
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
217
|
+
padding: "8px 0"
|
|
218
|
+
},
|
|
219
|
+
err: {
|
|
220
|
+
padding: "16px 22px",
|
|
221
|
+
fontSize: "13px",
|
|
222
|
+
color: "var(--dsw-alias-state-error-primary)"
|
|
223
|
+
},
|
|
224
|
+
warn: {
|
|
225
|
+
padding: "0 22px 10px",
|
|
226
|
+
fontSize: "12px",
|
|
227
|
+
color: "var(--dsw-alias-state-warn-primary, #d97706)"
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
function tagStyle(tag) {
|
|
231
|
+
switch (tag) {
|
|
232
|
+
case "scope": return S.tagScope;
|
|
233
|
+
case "format": return S.tagFormat;
|
|
234
|
+
case "constraint": return S.tagConstraint;
|
|
235
|
+
case "clarity": return S.tagClarity;
|
|
236
|
+
default: return S.tagOther;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function PromptRefineControl(props) {
|
|
240
|
+
const useInput = props.useInput;
|
|
241
|
+
const useChat = props.useChat;
|
|
242
|
+
const inputActions = props.inputActions;
|
|
243
|
+
const draftNow = (useInput ? useInput((s) => s.draft) : "") ?? "";
|
|
244
|
+
const nodes = (useChat ? useChat((s) => s.legacy.nodes) : []) ?? [];
|
|
245
|
+
const [host] = React.useState(() => getPortalHost());
|
|
246
|
+
const [open, setOpen] = React.useState(false);
|
|
247
|
+
const [phase, setPhase] = React.useState("loading");
|
|
248
|
+
const [reqDraft, setReqDraft] = React.useState("");
|
|
249
|
+
const [items, setItems] = React.useState([]);
|
|
250
|
+
const [checked, setChecked] = React.useState([]);
|
|
251
|
+
const [errMsg, setErrMsg] = React.useState("");
|
|
252
|
+
const [usedModel, setUsedModel] = React.useState("");
|
|
253
|
+
const collectHistory = () => {
|
|
254
|
+
const out = [];
|
|
255
|
+
for (let i = nodes.length - 1; i >= 0 && out.length < 6; i--) {
|
|
256
|
+
const n = nodes[i];
|
|
257
|
+
if (!n) continue;
|
|
258
|
+
if (n.kind === "user") {
|
|
259
|
+
const text = (n.content ?? []).filter((b) => b && b.type === "text").map((b) => b.text ?? "").join("").trim();
|
|
260
|
+
if (text) out.unshift({
|
|
261
|
+
role: "user",
|
|
262
|
+
content: text.slice(0, 500)
|
|
263
|
+
});
|
|
264
|
+
} else if (n.kind === "assistant") {
|
|
265
|
+
const text = (n.blocks ?? []).filter((b) => b && b.kind === "text").map((b) => b.text ?? "").join("").trim();
|
|
266
|
+
if (text) out.unshift({
|
|
267
|
+
role: "assistant",
|
|
268
|
+
content: text.slice(0, 500)
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return out;
|
|
273
|
+
};
|
|
274
|
+
const request = () => {
|
|
275
|
+
const d = draftNow.trim();
|
|
276
|
+
if (!d) {
|
|
277
|
+
setReqDraft("");
|
|
278
|
+
setPhase("empty");
|
|
279
|
+
setOpen(true);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
setReqDraft(d);
|
|
283
|
+
setItems([]);
|
|
284
|
+
setChecked([]);
|
|
285
|
+
setErrMsg("");
|
|
286
|
+
setUsedModel("");
|
|
287
|
+
setPhase("loading");
|
|
288
|
+
setOpen(true);
|
|
289
|
+
const history = collectHistory();
|
|
290
|
+
fetch("/dsh-prompt-refine/suggest", {
|
|
291
|
+
method: "POST",
|
|
292
|
+
headers: { "Content-Type": "application/json" },
|
|
293
|
+
body: JSON.stringify({
|
|
294
|
+
draft: d,
|
|
295
|
+
history
|
|
296
|
+
})
|
|
297
|
+
}).then((r) => r.json()).then((data) => {
|
|
298
|
+
const list = Array.isArray(data?.suggestions) ? data.suggestions.slice(0, 3) : [];
|
|
299
|
+
if (data?.ok && list.length > 0) {
|
|
300
|
+
setItems(list);
|
|
301
|
+
setChecked(list.map((_, i) => i < 2));
|
|
302
|
+
setUsedModel(data.usedModel ?? "");
|
|
303
|
+
setErrMsg(data.error ?? "");
|
|
304
|
+
setPhase("result");
|
|
305
|
+
} else {
|
|
306
|
+
setErrMsg(data?.error ?? "未知错误");
|
|
307
|
+
setPhase("error");
|
|
308
|
+
}
|
|
309
|
+
}).catch((e) => {
|
|
310
|
+
setErrMsg("网络错误: " + (e instanceof Error ? e.message : String(e)));
|
|
311
|
+
setPhase("error");
|
|
312
|
+
});
|
|
313
|
+
};
|
|
314
|
+
React.useEffect(() => {
|
|
315
|
+
if (!open) return;
|
|
316
|
+
const onKey = (e) => {
|
|
317
|
+
if (e.key === "Escape") setOpen(false);
|
|
318
|
+
};
|
|
319
|
+
window.addEventListener("keydown", onKey);
|
|
320
|
+
return () => {
|
|
321
|
+
window.removeEventListener("keydown", onKey);
|
|
322
|
+
};
|
|
323
|
+
}, [open]);
|
|
324
|
+
const close = () => setOpen(false);
|
|
325
|
+
const apply = () => {
|
|
326
|
+
const patches = [];
|
|
327
|
+
for (let i = 0; i < items.length; i++) if (checked[i] && items[i]?.patch) patches.push(items[i].patch);
|
|
328
|
+
const next = reqDraft + patches.join("");
|
|
329
|
+
if (inputActions?.setDraft) inputActions.setDraft(next);
|
|
330
|
+
setOpen(false);
|
|
331
|
+
};
|
|
332
|
+
const button = h("button", {
|
|
333
|
+
type: "button",
|
|
334
|
+
title: "优化提示词",
|
|
335
|
+
"aria-label": "优化提示词",
|
|
336
|
+
style: S.btn,
|
|
337
|
+
onClick: (e) => {
|
|
338
|
+
e.stopPropagation();
|
|
339
|
+
e.preventDefault();
|
|
340
|
+
request();
|
|
341
|
+
},
|
|
342
|
+
onMouseDown: (e) => e.preventDefault()
|
|
343
|
+
}, "✨");
|
|
344
|
+
if (!open || !host) return button;
|
|
345
|
+
const selected = [];
|
|
346
|
+
for (let i = 0; i < items.length; i++) if (checked[i] && items[i]?.patch) selected.push(items[i].patch);
|
|
347
|
+
const selectedCount = checked.filter(Boolean).length;
|
|
348
|
+
let body;
|
|
349
|
+
if (phase === "empty") body = h("div", {
|
|
350
|
+
style: S.err,
|
|
351
|
+
children: "请先在输入框写点内容,再点 ✨ 优化。"
|
|
352
|
+
});
|
|
353
|
+
else if (phase === "loading") body = h("div", {
|
|
354
|
+
style: { padding: "18px 22px" },
|
|
355
|
+
children: [
|
|
356
|
+
h("div", { style: S.skel }),
|
|
357
|
+
h("div", { style: S.skel }),
|
|
358
|
+
h("div", { style: S.skel }),
|
|
359
|
+
h("div", {
|
|
360
|
+
style: S.tip,
|
|
361
|
+
children: "正在结合上下文分析…"
|
|
362
|
+
})
|
|
363
|
+
]
|
|
364
|
+
});
|
|
365
|
+
else if (phase === "error") body = h("div", {
|
|
366
|
+
style: S.err,
|
|
367
|
+
children: "⚠ " + (errMsg || "出错了")
|
|
368
|
+
});
|
|
369
|
+
else {
|
|
370
|
+
const rows = items.map((s, i) => h("label", {
|
|
371
|
+
key: i,
|
|
372
|
+
style: {
|
|
373
|
+
...S.row,
|
|
374
|
+
...checked[i] ? S.rowOn : {}
|
|
375
|
+
},
|
|
376
|
+
children: [h("input", {
|
|
377
|
+
type: "checkbox",
|
|
378
|
+
checked: !!checked[i],
|
|
379
|
+
onChange: (e) => {
|
|
380
|
+
setChecked((prev) => {
|
|
381
|
+
const n = [...prev];
|
|
382
|
+
n[i] = e.target.checked;
|
|
383
|
+
return n;
|
|
384
|
+
});
|
|
385
|
+
},
|
|
386
|
+
style: {
|
|
387
|
+
marginTop: "2px",
|
|
388
|
+
width: "16px",
|
|
389
|
+
height: "16px",
|
|
390
|
+
accentColor: "var(--dsw-alias-state-business-primary)",
|
|
391
|
+
cursor: "pointer"
|
|
392
|
+
}
|
|
393
|
+
}), h("div", {
|
|
394
|
+
style: S.rowBody,
|
|
395
|
+
children: [
|
|
396
|
+
h("span", {
|
|
397
|
+
style: tagStyle(s.tag),
|
|
398
|
+
children: s.tag
|
|
399
|
+
}),
|
|
400
|
+
h("p", {
|
|
401
|
+
style: S.issue,
|
|
402
|
+
children: s.issue
|
|
403
|
+
}),
|
|
404
|
+
s.patch ? h("p", {
|
|
405
|
+
style: S.patch,
|
|
406
|
+
children: s.patch
|
|
407
|
+
}) : null
|
|
408
|
+
]
|
|
409
|
+
})]
|
|
410
|
+
}));
|
|
411
|
+
const previewChildren = [h("span", {
|
|
412
|
+
key: "draft",
|
|
413
|
+
children: reqDraft
|
|
414
|
+
})];
|
|
415
|
+
selected.forEach((p, i) => {
|
|
416
|
+
previewChildren.push(h("span", {
|
|
417
|
+
key: "p" + i,
|
|
418
|
+
style: S.added,
|
|
419
|
+
children: p
|
|
420
|
+
}));
|
|
421
|
+
});
|
|
422
|
+
body = h(React.Fragment, null, [
|
|
423
|
+
h("div", {
|
|
424
|
+
style: S.original,
|
|
425
|
+
children: [h("div", {
|
|
426
|
+
style: S.originalLabel,
|
|
427
|
+
children: "原文"
|
|
428
|
+
}), h("div", { children: reqDraft })]
|
|
429
|
+
}),
|
|
430
|
+
h("div", {
|
|
431
|
+
style: S.list,
|
|
432
|
+
children: rows
|
|
433
|
+
}),
|
|
434
|
+
h("div", {
|
|
435
|
+
style: S.preview,
|
|
436
|
+
children: [h("div", {
|
|
437
|
+
style: S.previewLabel,
|
|
438
|
+
children: [h("span", { children: "采纳后预览" }), h("span", {
|
|
439
|
+
style: S.previewCount,
|
|
440
|
+
children: `已选 ${selectedCount} / ${items.length}`
|
|
441
|
+
})]
|
|
442
|
+
}), h("div", {
|
|
443
|
+
style: S.previewText,
|
|
444
|
+
children: previewChildren
|
|
445
|
+
})]
|
|
446
|
+
}),
|
|
447
|
+
errMsg ? h("div", {
|
|
448
|
+
style: S.warn,
|
|
449
|
+
children: "提示:" + errMsg
|
|
450
|
+
}) : null,
|
|
451
|
+
h("div", {
|
|
452
|
+
style: S.footer,
|
|
453
|
+
children: [h("span", {
|
|
454
|
+
style: S.hint,
|
|
455
|
+
children: (usedModel ? usedModel + " · " : "") + "Esc 关闭"
|
|
456
|
+
}), h("div", {
|
|
457
|
+
style: {
|
|
458
|
+
display: "flex",
|
|
459
|
+
gap: "8px"
|
|
460
|
+
},
|
|
461
|
+
children: [h("button", {
|
|
462
|
+
type: "button",
|
|
463
|
+
style: S.btnSec,
|
|
464
|
+
onClick: close,
|
|
465
|
+
children: "跳过"
|
|
466
|
+
}), h("button", {
|
|
467
|
+
type: "button",
|
|
468
|
+
style: S.btnPri,
|
|
469
|
+
onClick: apply,
|
|
470
|
+
children: selectedCount > 0 ? `采纳 ${selectedCount} 条并填入` : "填入原文"
|
|
471
|
+
})]
|
|
472
|
+
})]
|
|
473
|
+
})
|
|
474
|
+
]);
|
|
475
|
+
}
|
|
476
|
+
const overlay = h("div", {
|
|
477
|
+
style: S.overlay,
|
|
478
|
+
onClick: (e) => {
|
|
479
|
+
if (e.target === e.currentTarget) close();
|
|
480
|
+
},
|
|
481
|
+
children: h("div", {
|
|
482
|
+
style: S.modal,
|
|
483
|
+
onClick: (e) => e.stopPropagation(),
|
|
484
|
+
children: [h("div", {
|
|
485
|
+
style: S.header,
|
|
486
|
+
children: [h("div", {
|
|
487
|
+
style: S.title,
|
|
488
|
+
children: "✨ 提示词优化建议"
|
|
489
|
+
}), h("p", {
|
|
490
|
+
style: S.subtitle,
|
|
491
|
+
children: phase === "result" ? `发现 ${items.length} 处可以更精确` : "正在结合上下文生成建议"
|
|
492
|
+
})]
|
|
493
|
+
}), body]
|
|
494
|
+
})
|
|
495
|
+
});
|
|
496
|
+
return h(React.Fragment, null, [button, createPortal(overlay, host)]);
|
|
497
|
+
}
|
|
498
|
+
function apply(ctx) {
|
|
499
|
+
const c = ctx;
|
|
500
|
+
c.slots.inject("conversation.input.right", () => c.slots.register({
|
|
501
|
+
name: "conversation.input.right",
|
|
502
|
+
id: "dsh-prompt-refine-button",
|
|
503
|
+
order: 10
|
|
504
|
+
}, PromptRefineControl));
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
name,
|
|
508
|
+
inject,
|
|
509
|
+
apply
|
|
510
|
+
};
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
window.__ModuleLoader__.load({
|
|
514
|
+
id: "dsh-prompt-refine",
|
|
515
|
+
factory: makeFactory()
|
|
516
|
+
});
|
|
517
|
+
//#endregion
|
|
518
|
+
})();
|