codebee 0.1.35 → 0.1.37
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/CHANGELOG.md +23 -0
- package/README.md +15 -0
- package/app/core/market.py +28 -3
- package/app/core/zentao.py +32 -7
- package/app/pet.py +46 -8
- package/app/ui/app.js +168 -7
- package/app/ui/i18n.js +19 -0
- package/app/ui/index.html +35 -21
- package/app/ui/style.css +56 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,29 @@ README 元数据带回,供老版本在「发现新版本」时展示新版更
|
|
|
6
6
|
|
|
7
7
|
## 未发布
|
|
8
8
|
|
|
9
|
+
## v0.1.37(2026-09-21)
|
|
10
|
+
|
|
11
|
+
### ✨ 新功能
|
|
12
|
+
|
|
13
|
+
- 新建任务输入框焕新:编排模式、思考程度、对话模型收进输入框工具行右侧(与主流 AI 输入框一致的左右分区——左附件、右偏好+发送),不再是散落在框外的下拉;对话模型点开是「厂商 → 模型」两级菜单(选中打勾、底部「管理模型」直达),点厂商即带出该厂商的推荐模型,pill 以「厂商/模型」组合名显示,只对本次对话生效。
|
|
14
|
+
- 图片附件在输入框内以 88×88 缩略卡展示(此前首页按原图尺寸渲染,直接压住输入文字)。
|
|
15
|
+
- 「更多选项」里旧的对话模型下拉已移除(与工具行 pill 重复)。
|
|
16
|
+
|
|
17
|
+
## v0.1.36(2026-09-21)
|
|
18
|
+
|
|
19
|
+
### 🛠 问题修复
|
|
20
|
+
|
|
21
|
+
- 端口占用诊断的「关闭」现在真能关掉:服务类进程对温和关闭信号本就无反应,此前只发信号不看结果——现在温和无效会自动升级强制结束,并以「端口确实释放」为准,不再假报成功。
|
|
22
|
+
- 群摘要:选好 64 位 Python 后直接点「刷新群列表 / 立即扫描」不再误报「请先填路径」(表单会先静默保存);「立即扫描」按钮文字不再重复;新建的群现在也会出现在监听列表(微信本地库还没同步时从最近会话补齐)。
|
|
23
|
+
- 桌面蜜蜂气泡的「点我细看」现在真的能点:点气泡直达群摘要页;网页已打开时也会跳到目标页,而不是只把旧窗口调到前面。
|
|
24
|
+
- 禅道:「测试并保存连接」不再卡住按钮几十秒(产品/账号清单改后台拉取,拉到后下拉自动就位);操作状态面板显示具体操作名(如「拉取禅道产品」);兼容更多老版禅道的模块树与产品清单返回形状,修「模块清单形状不认识」「产品只显示 ID」。
|
|
25
|
+
- 设置页「代码设置」恢复左标签右控件的横排布局(此前被章节评审卡的样式意外覆盖成整列竖排居中)。
|
|
26
|
+
- 修复页面繁忙时「上次使用的任务类型」偶尔没被恢复的时序问题。
|
|
27
|
+
|
|
28
|
+
### ✨ 界面
|
|
29
|
+
|
|
30
|
+
- 编排模式 / 思考程度 / 对话模型从「更多选项」提到输入框正下方,随开随用,不再折叠。
|
|
31
|
+
|
|
9
32
|
## v0.1.35(2026-09-21)
|
|
10
33
|
|
|
11
34
|
### ✨ 新功能
|
package/README.md
CHANGED
|
@@ -23,11 +23,26 @@ Kimi Code、MiMo Code、Grok Build、Pi、DeepSeek Harness……),提供
|
|
|
23
23
|
<!-- relnotes:start -->
|
|
24
24
|
### ✨ 新功能 · New
|
|
25
25
|
|
|
26
|
+
- 新建任务输入框焕新:编排模式、思考程度、「厂商/模型」选择器收进输入框工具行右侧(左附件、右偏好+发送的主流布局);对话模型点开是「厂商 → 模型」两级菜单,选中打勾、底部「管理模型」直达,pill 显示「厂商/模型」组合名。
|
|
27
|
+
- The composer was refreshed: orchestration mode, reasoning level and a "provider/model" picker now sit on the input box's right side (attachments left, preferences + send right, like mainstream AI input boxes). The model picker opens a provider → model cascading menu with checkmarks and a shortcut to model management.
|
|
28
|
+
|
|
26
29
|
- 经验库越用越准:教训按任务结局自动记胜负,真正帮上忙的经验优先注入、帮倒忙的被挤出——随真实运行自我提纯。
|
|
27
30
|
- The lesson library now tracks outcomes: lessons that helped a task pass gain weight, ones that failed to prevent issues fade — it self-refines with every run.
|
|
28
31
|
|
|
29
32
|
### 🛠 问题修复 · Fixes
|
|
30
33
|
|
|
34
|
+
- 端口诊断的「关闭」真能关掉了:温和信号无效自动升级强杀,以「端口确实释放」为准,不再假报成功。
|
|
35
|
+
- Port diagnostics "Close" now really closes: gentle signal escalates to forced kill when ignored, and success is only reported once the port is actually released.
|
|
36
|
+
|
|
37
|
+
- 群摘要:选好 Python 直接刷新不再误报「请先填路径」;新建的群也会出现在监听列表;蜜蜂气泡「点我细看」真能点了。
|
|
38
|
+
- Digests: refreshing right after picking a Python no longer nags "path required"; brand-new groups show up in the picker; the bee bubble "click to read" actually navigates now.
|
|
39
|
+
|
|
40
|
+
- 禅道:「测试并保存连接」不再卡按钮几十秒;兼容更多老版禅道的模块/产品清单形状;操作状态显示具体操作名。
|
|
41
|
+
- ZenTao: "Test & save" no longer holds the button for tens of seconds; more legacy ZenTao module/product list shapes are supported; the operation panel shows concrete action names.
|
|
42
|
+
|
|
43
|
+
- 设置页「代码设置」恢复横排布局(此前被章节评审卡样式意外覆盖)。
|
|
44
|
+
- Settings → Code display restores its row layout after an accidental style override from the chapter-review card.
|
|
45
|
+
|
|
31
46
|
- 手机端操作被「其他设备正在控制」挡住时,弹出「接管并重试」——一键接管并自动重做刚才的操作。
|
|
32
47
|
- When a mobile action is blocked by another device in control, a "Take over & retry" prompt appears — one tap retakes control and redoes the action.
|
|
33
48
|
|
package/app/core/market.py
CHANGED
|
@@ -170,23 +170,48 @@ def installed_ids():
|
|
|
170
170
|
|
|
171
171
|
def view():
|
|
172
172
|
"""市场目录(给 UI/API):{catalog: [...], categories: [...]}。
|
|
173
|
-
每项带 installed: bool
|
|
173
|
+
每项带 installed: bool + enabled: bool(已装包的启停态,卡片开关直接
|
|
174
|
+
切换,不用再去经验库翻——对齐 ZCode 插件管理形态)。
|
|
175
|
+
注意 id 双轨:市场 id(git-workflow)≠ skills 用户包 id(user-哈希),
|
|
176
|
+
启停态要经安装记录的 file 路径映射到真实用户包再查。"""
|
|
174
177
|
installed = installed_ids()
|
|
178
|
+
reg = _load_registry().get("installed") or {}
|
|
179
|
+
ups = skills.user_packs()
|
|
180
|
+
|
|
181
|
+
def _lookup(pack_id, is_builtin):
|
|
182
|
+
"""返回 (enabled, 可启停的真实包 id)。内置即市场 id;市场包装成的
|
|
183
|
+
用户包 id 是 user-哈希,必须经安装记录的 file 路径映射。"""
|
|
184
|
+
if is_builtin:
|
|
185
|
+
return skills._pack_enabled(pack_id), pack_id
|
|
186
|
+
rec = reg.get(pack_id) or {}
|
|
187
|
+
fhint = str(rec.get("file") or "").replace("\\", "/")
|
|
188
|
+
for up in ups:
|
|
189
|
+
f = str(up.get("file") or "").replace("\\", "/")
|
|
190
|
+
if fhint and (f == fhint or f.endswith("/" + fhint)):
|
|
191
|
+
return skills._pack_enabled(up["id"]), up["id"]
|
|
192
|
+
return True, pack_id
|
|
193
|
+
|
|
175
194
|
items = []
|
|
176
195
|
for p in skills.BUILTIN_PACKS: # 已内置:已存在,不可安装
|
|
177
196
|
meta = _BUILTIN_META.get(p["id"]) or {}
|
|
197
|
+
en, pid = _lookup(p["id"], True)
|
|
178
198
|
items.append({"id": p["id"], "name": p["name"],
|
|
179
199
|
"desc": meta.get("desc") or p.get("note") or "",
|
|
180
200
|
"category": meta.get("category") or skills.LESSON_UNCATEGORIZED,
|
|
181
201
|
"scopes": list(p.get("scopes") or ["*"]),
|
|
182
202
|
"builtin": True, "installed": True, "installable": False,
|
|
203
|
+
"enabled": en, "pack_id": pid,
|
|
183
204
|
"chars": len(skills.pack_text(p))})
|
|
184
205
|
for p in BUILTIN_PACKS: # 可安装市场包
|
|
185
206
|
files = p.get("files") or {}
|
|
207
|
+
inst = p["id"] in installed
|
|
208
|
+
en, pid = _lookup(p["id"], False) if inst else (False, p["id"])
|
|
186
209
|
items.append({"id": p["id"], "name": p["name"], "desc": p["desc"],
|
|
187
210
|
"category": p["category"], "scopes": list(p["scopes"]),
|
|
188
|
-
"builtin": False, "installed":
|
|
189
|
-
"installable": True,
|
|
211
|
+
"builtin": False, "installed": inst,
|
|
212
|
+
"installable": True,
|
|
213
|
+
"enabled": en, "pack_id": pid,
|
|
214
|
+
"files": sorted(files),
|
|
190
215
|
"chars": sum(len(v) for v in files.values())})
|
|
191
216
|
cats = list(skills.LESSON_CATEGORIES) # 闭集全枚举先给全(UI 下拉不缺项)
|
|
192
217
|
for it in items:
|
package/app/core/zentao.py
CHANGED
|
@@ -620,6 +620,33 @@ def _pairs_to_list(pairs, id_key, name_key):
|
|
|
620
620
|
return out
|
|
621
621
|
|
|
622
622
|
|
|
623
|
+
def _module_items(d):
|
|
624
|
+
"""老版模块树形状兼容(2026-09-21 真机反馈「响应形状不认识」):
|
|
625
|
+
sons/modules 数组、{id: {name,...}} 字典、children/sons 嵌套树,统一
|
|
626
|
+
递归展开成 [{id,name}] 平铺清单(id=0 是树根容器,跳过)。"""
|
|
627
|
+
out = []
|
|
628
|
+
|
|
629
|
+
def walk(node):
|
|
630
|
+
if isinstance(node, list):
|
|
631
|
+
for x in node:
|
|
632
|
+
walk(x)
|
|
633
|
+
elif isinstance(node, dict):
|
|
634
|
+
if node.get("id") not in (None, "", 0):
|
|
635
|
+
out.append(node)
|
|
636
|
+
for k in ("children", "sons"):
|
|
637
|
+
if isinstance(node.get(k), (list, dict)):
|
|
638
|
+
walk(node[k])
|
|
639
|
+
else:
|
|
640
|
+
for v in node.values():
|
|
641
|
+
walk(v)
|
|
642
|
+
|
|
643
|
+
if isinstance(d, dict):
|
|
644
|
+
walk(d.get("sons") or d.get("modules") or d.get("tree") or d)
|
|
645
|
+
elif isinstance(d, list):
|
|
646
|
+
walk(d)
|
|
647
|
+
return out
|
|
648
|
+
|
|
649
|
+
|
|
623
650
|
def _old_route(api, method, p, q, body):
|
|
624
651
|
"""路径翻译 + 请求 + 归一。会话死抛 _OldSessionDead。"""
|
|
625
652
|
|
|
@@ -698,16 +725,14 @@ def _old_route(api, method, p, q, body):
|
|
|
698
725
|
m = re.match(r"^/products/(\d+)/modules$", p)
|
|
699
726
|
if m and method == "GET":
|
|
700
727
|
d = go(get("/tree-browse-%s-module.json" % m.group(1)), "(拉模块清单)")
|
|
701
|
-
|
|
702
|
-
if isinstance(d, dict):
|
|
703
|
-
items = d.get("sons") or d.get("modules") or []
|
|
704
|
-
elif isinstance(d, list):
|
|
705
|
-
items = d
|
|
706
|
-
return {"_list": [x for x in items if isinstance(x, dict) and x.get("id")]}
|
|
728
|
+
return {"_list": [x for x in _module_items(d) if isinstance(x, dict) and x.get("id")]}
|
|
707
729
|
|
|
708
730
|
if p == "/products" and method == "GET":
|
|
709
731
|
d = go(get("/api-getmodel-product-getpairs.json"), "(拉产品清单)")
|
|
710
|
-
|
|
732
|
+
prods = d.get("products") if isinstance(d, dict) else None
|
|
733
|
+
if isinstance(prods, list): # 部分老版回数组形状 [{id,name,...}]
|
|
734
|
+
return {"_list": [x for x in prods if isinstance(x, dict) and x.get("id")]}
|
|
735
|
+
pairs = prods if isinstance(prods, dict) else d
|
|
711
736
|
return {"_list": _pairs_to_list(pairs, "id", "name")}
|
|
712
737
|
|
|
713
738
|
if p == "/users" and method == "GET":
|
package/app/pet.py
CHANGED
|
@@ -1090,7 +1090,8 @@ class PetApp:
|
|
|
1090
1090
|
dg = snap.get("digest") or {}
|
|
1091
1091
|
self.seen_digests, notify = digest_alert(self.seen_digests, dg)
|
|
1092
1092
|
if notify:
|
|
1093
|
-
self._show_bubble(digest_bubble_text(dg, self.lang), secs=9.0
|
|
1093
|
+
self._show_bubble(digest_bubble_text(dg, self.lang), secs=9.0,
|
|
1094
|
+
action="digest")
|
|
1094
1095
|
self.last_busy = now
|
|
1095
1096
|
if st == "sleep":
|
|
1096
1097
|
self.mood_kind, self.mood_until = "cheer", now + MOOD_HOLD_S
|
|
@@ -1257,8 +1258,14 @@ class PetApp:
|
|
|
1257
1258
|
self._tip_body = tk.Label(tip, text="", bg="#1B2430",
|
|
1258
1259
|
fg="#ECF2F8",
|
|
1259
1260
|
font=("Microsoft YaHei UI", 9),
|
|
1260
|
-
justify="left", wraplength=280
|
|
1261
|
+
justify="left", wraplength=280,
|
|
1262
|
+
cursor="hand2")
|
|
1261
1263
|
self._tip_body.pack(anchor="w", padx=10, pady=(2, 8))
|
|
1264
|
+
# 点清单即打开网页(含群摘要「点我细看」行——此前清单窗没有任何
|
|
1265
|
+
# 点击绑定,文案写着「点我细看」点了却没反应,用户实测反馈)
|
|
1266
|
+
for w in (self._tip_head, self._tip_body):
|
|
1267
|
+
w.bind("<ButtonPress-1>",
|
|
1268
|
+
lambda e: self._open_ui("#goto=__wxdigest"))
|
|
1262
1269
|
self._tip = tip
|
|
1263
1270
|
self._tip_update()
|
|
1264
1271
|
try:
|
|
@@ -1312,11 +1319,14 @@ class PetApp:
|
|
|
1312
1319
|
text = self._L("alert") % bad.get("title", "?")
|
|
1313
1320
|
self._show_bubble(text)
|
|
1314
1321
|
|
|
1315
|
-
def _show_bubble(self, text, secs=5.0, force=False):
|
|
1322
|
+
def _show_bubble(self, text, secs=5.0, force=False, action=""):
|
|
1323
|
+
"""action:气泡承诺的「点我细看」要兑现(2026-09-21 用户实测点不动——
|
|
1324
|
+
气泡窗口没绑点击,蜜蜂左键也只开首页)。digest=直达群摘要页。"""
|
|
1316
1325
|
if not force and time.time() < self.quiet_until:
|
|
1317
1326
|
return # 「安静一会」期间不碎碎念(系统确认语用 force 放行)
|
|
1318
1327
|
self._destroy_win("_bubble")
|
|
1319
1328
|
self.hop_until = time.time() + 0.7 # 冒泡蹦一下,像在说话
|
|
1329
|
+
self._bubble_action = action
|
|
1320
1330
|
tk = self.tk
|
|
1321
1331
|
bub = tk.Toplevel(self.root)
|
|
1322
1332
|
bub.overrideredirect(True)
|
|
@@ -1324,10 +1334,14 @@ class PetApp:
|
|
|
1324
1334
|
bub.attributes("-topmost", True)
|
|
1325
1335
|
except Exception:
|
|
1326
1336
|
pass
|
|
1327
|
-
tk.Label(bub, text=text, bg="#FFF8E1", fg="#4A3200",
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1337
|
+
lab = tk.Label(bub, text=text, bg="#FFF8E1", fg="#4A3200",
|
|
1338
|
+
bd=1, relief="solid",
|
|
1339
|
+
font=("Microsoft YaHei UI", 9), justify="left",
|
|
1340
|
+
wraplength=230, padx=8, pady=5, cursor="hand2")
|
|
1341
|
+
lab.pack()
|
|
1342
|
+
# 气泡本体可点:点了按承诺跳转(点蜜蜂也走同一逻辑,见 _on_release)
|
|
1343
|
+
for w in (bub, lab):
|
|
1344
|
+
w.bind("<Button-1>", lambda e: self._bubble_click())
|
|
1331
1345
|
x = self.root.winfo_x() + self.win_w - 40
|
|
1332
1346
|
y = max(0, self.root.winfo_y() - 8)
|
|
1333
1347
|
bub.update_idletasks()
|
|
@@ -1338,6 +1352,29 @@ class PetApp:
|
|
|
1338
1352
|
self._bubble_until = time.time() + secs
|
|
1339
1353
|
self.root.after(int(secs * 1000), self._hide_bubble)
|
|
1340
1354
|
|
|
1355
|
+
def _bubble_action_live(self):
|
|
1356
|
+
"""最近一条气泡是否还在展示期且带跳转承诺。"""
|
|
1357
|
+
if time.time() > getattr(self, "_bubble_until", 0):
|
|
1358
|
+
return ""
|
|
1359
|
+
return getattr(self, "_bubble_action", "") or ""
|
|
1360
|
+
|
|
1361
|
+
def _bubble_click(self):
|
|
1362
|
+
# 点了立刻消失并清展示期(_hide_bubble 是到点才收的延迟逻辑,别用);
|
|
1363
|
+
# 先记 action 再清——_open_by_bubble_action 读的就是这个展示期
|
|
1364
|
+
action = self._bubble_action_live()
|
|
1365
|
+
self._destroy_win("_bubble")
|
|
1366
|
+
self._bubble_until = 0
|
|
1367
|
+
if action == "digest":
|
|
1368
|
+
self._open_digest()
|
|
1369
|
+
else:
|
|
1370
|
+
self._open_ui()
|
|
1371
|
+
|
|
1372
|
+
def _open_by_bubble_action(self):
|
|
1373
|
+
if self._bubble_action_live() == "digest":
|
|
1374
|
+
self._open_digest()
|
|
1375
|
+
else:
|
|
1376
|
+
self._open_ui()
|
|
1377
|
+
|
|
1341
1378
|
def _hide_bubble(self):
|
|
1342
1379
|
if time.time() < self._bubble_until:
|
|
1343
1380
|
self.root.after(500, self._hide_bubble)
|
|
@@ -1476,7 +1513,8 @@ class PetApp:
|
|
|
1476
1513
|
self._clamp_pos()
|
|
1477
1514
|
self._save_cfg(x=self.root.winfo_x(), y=self.root.winfo_y())
|
|
1478
1515
|
else:
|
|
1479
|
-
|
|
1516
|
+
# 展示期内气泡承诺过「点我细看」就兑现(digest 直达),否则开首页
|
|
1517
|
+
self._open_by_bubble_action()
|
|
1480
1518
|
|
|
1481
1519
|
def _bye(self, write_setting=False):
|
|
1482
1520
|
"""退出。write_setting=True(右键菜单「关闭桌宠」)先把 pet_enabled=False
|
package/app/ui/app.js
CHANGED
|
@@ -59,7 +59,9 @@ function renderTypeOptions() {
|
|
|
59
59
|
const prev = sel.value; sel.innerHTML = (S.flows || []).map((f) => {
|
|
60
60
|
return '<option value="' + esc(f.id) + '">' + esc(t(f.name)) + t("(") + flowDesc(f) + (f.builtin ? "" : t(" · 自定义")) + t(")") + "</option>";
|
|
61
61
|
}).join("");
|
|
62
|
-
|
|
62
|
+
// 保住用户手动选择(typeTouched);出厂默认 direct 不算选择——prefs 迟到
|
|
63
|
+
// 竞态下要让位给偏好记忆,否则「上次类型」永远恢复不了(高负载下实测)
|
|
64
|
+
if (prev && flowById(prev) && (S.typeTouched || prev !== "direct")) sel.value = prev;
|
|
63
65
|
// 偏好记忆:上次创建用的类型优先于出厂默认「直接执行」(老数据/已删流程回落)
|
|
64
66
|
else if (S.lastPrefs && S.lastPrefs.type && flowById(S.lastPrefs.type)) sel.value = S.lastPrefs.type;
|
|
65
67
|
// 首屏默认落在「直接执行」(快档位:单 CLI 直达,无拆解/评审);
|
|
@@ -78,9 +80,122 @@ function applyTaskPreferenceDefaults() {
|
|
|
78
80
|
if (thinking && ["auto", "low", "standard", "high"].includes(lp.thinking)) thinking.value = lp.thinking;
|
|
79
81
|
const manual = $("f-manual-only");
|
|
80
82
|
if (manual && mode) manual.classList.toggle("hidden", mode.value !== "manual");
|
|
83
|
+
syncCmpSelFace("f-mode");
|
|
84
|
+
syncCmpSelFace("f-thinking");
|
|
81
85
|
S.prefsApplied = true;
|
|
82
86
|
}
|
|
83
87
|
|
|
88
|
+
/* composer 内「编排模式/思考程度」pill 的短名:原生 select 透明覆盖在 pill 上,
|
|
89
|
+
* face 只显示选项的短前缀(「自动(推荐):…」→「自动」),值真源仍是 select。 */
|
|
90
|
+
function cmpSelShort(text) {
|
|
91
|
+
return (String(text || "").split(/[::]/)[0]
|
|
92
|
+
.replace(/[((].*?[))]/g, "").trim()) || "";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function syncCmpSelFace(id) {
|
|
96
|
+
const sel = $(id), face = $(id + "-face");
|
|
97
|
+
if (!sel || !face) return;
|
|
98
|
+
const opt = sel.options[sel.selectedIndex];
|
|
99
|
+
face.textContent = cmpSelShort(opt && opt.textContent) || sel.value;
|
|
100
|
+
}
|
|
101
|
+
window.syncCmpSelFace = syncCmpSelFace; // applyI18n 重译 option 后同步短名
|
|
102
|
+
|
|
103
|
+
/* ---- 对话模型 pill:厂商 → 模型两级钻取菜单(ChatGPT 选择器同款语汇)。
|
|
104
|
+
* 真源仍是 f-direct-provider / f-direct-model-name 两个隐藏 select,
|
|
105
|
+
* 菜单只改它们的值——既有提交流 / onTypeChange 显隐 / 测试全兼容。 */
|
|
106
|
+
let cmpModelProv = ""; // ""=厂商级;否则=已钻入的厂商 id(模型级)
|
|
107
|
+
|
|
108
|
+
function cmpDirectBtnSync() {
|
|
109
|
+
const btn = $("f-direct-btn");
|
|
110
|
+
if (!btn) return;
|
|
111
|
+
const pv = $("f-direct-provider").value, mv = $("f-direct-model-name").value;
|
|
112
|
+
if (!pv) { btn.textContent = t("自动推荐"); return; }
|
|
113
|
+
const p = directProviders().find((x) => x.id === pv);
|
|
114
|
+
btn.textContent = (p ? (p.name || p.id) : pv) + (mv ? "/" + mv : "/" + t("推荐"));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function cmpModelMenuRender() {
|
|
118
|
+
const menu = $("cmp-model-menu");
|
|
119
|
+
if (!menu) return;
|
|
120
|
+
const pv = $("f-direct-provider").value, mv = $("f-direct-model-name").value;
|
|
121
|
+
const chk = '<svg class="ico ti-check" aria-hidden="true"><use href="#i-check"></use></svg>';
|
|
122
|
+
const arr = '<svg class="ico ti-check" aria-hidden="true"><use href="#i-chevron-r"></use></svg>';
|
|
123
|
+
if (!cmpModelProv) {
|
|
124
|
+
menu.innerHTML =
|
|
125
|
+
'<button type="button" class="type-item" data-p="" data-m=""><span class="ti-body"><span class="ti-name">' +
|
|
126
|
+
t("自动推荐") + "</span></span>" + (!pv ? chk : "") + "</button>" +
|
|
127
|
+
directProviders().map((p) =>
|
|
128
|
+
'<button type="button" class="type-item" data-p="' + esc(p.id) + '"><span class="ti-body"><span class="ti-name">' +
|
|
129
|
+
esc(p.name || p.id) + "</span></span>" + (pv === p.id ? chk : arr) + "</button>").join("") +
|
|
130
|
+
'<div class="type-menu-foot"><button type="button" class="type-item" data-manage="1"><span class="ti-body"><span class="ti-name">' +
|
|
131
|
+
t("管理模型…") + "</span></span></button></div>";
|
|
132
|
+
} else {
|
|
133
|
+
const p = directProviders().find((x) => x.id === cmpModelProv);
|
|
134
|
+
const names = p ? (p.models || []).filter((m) => m.enabled !== false && !m.hidden)
|
|
135
|
+
.map((m) => m.name).filter(Boolean) : [];
|
|
136
|
+
menu.innerHTML =
|
|
137
|
+
'<button type="button" class="type-item" data-back="1"><span class="ti-body"><span class="ti-name">‹ ' +
|
|
138
|
+
esc(p ? (p.name || p.id) : cmpModelProv) + "</span></span></button>" +
|
|
139
|
+
'<button type="button" class="type-item" data-p="' + esc(cmpModelProv) + '" data-m=""><span class="ti-body"><span class="ti-name">' +
|
|
140
|
+
t("随厂商推荐") + "</span></span>" + (pv === cmpModelProv && !mv ? chk : "") + "</button>" +
|
|
141
|
+
names.map((n) =>
|
|
142
|
+
'<button type="button" class="type-item" data-p="' + esc(cmpModelProv) + '" data-m="' + esc(n) + '"><span class="ti-body"><span class="ti-name">' +
|
|
143
|
+
esc(n) + "</span></span>" + (pv === cmpModelProv && mv === n ? chk : "") + "</button>").join("");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function toggleModelMenu(force) {
|
|
148
|
+
const menu = $("cmp-model-menu");
|
|
149
|
+
if (!menu) return;
|
|
150
|
+
const show = force !== undefined ? force : menu.classList.contains("hidden");
|
|
151
|
+
if (show) { cmpModelProv = ""; cmpModelMenuRender(); }
|
|
152
|
+
menu.classList.toggle("hidden", !show);
|
|
153
|
+
}
|
|
154
|
+
window.toggleModelMenu = toggleModelMenu;
|
|
155
|
+
|
|
156
|
+
(function () {
|
|
157
|
+
const menu = $("cmp-model-menu");
|
|
158
|
+
if (!menu || menu.dataset.cmpBound) return;
|
|
159
|
+
menu.dataset.cmpBound = "1";
|
|
160
|
+
menu.addEventListener("click", (e) => {
|
|
161
|
+
const b = e.target.closest("[data-p],[data-back],[data-manage]");
|
|
162
|
+
if (!b) return;
|
|
163
|
+
if (b.dataset.manage) { toggleModelMenu(false); switchTab("models"); return; }
|
|
164
|
+
if (b.dataset.back) { cmpModelProv = ""; cmpModelMenuRender(); return; }
|
|
165
|
+
if (b.dataset.m === undefined) {
|
|
166
|
+
// 点厂商 = 厂商+推荐模型一起定(用户反馈「只选了厂商」):带出该厂商
|
|
167
|
+
// 排序最前的模型并展开模型级菜单,可立即改选;已是当前厂商则只钻取。
|
|
168
|
+
// 注意先 renderDirectModelPicker 重建模型 options,再赋值才生效。
|
|
169
|
+
const pid = b.dataset.p;
|
|
170
|
+
if ($("f-direct-provider").value !== pid) {
|
|
171
|
+
$("f-direct-provider").value = pid;
|
|
172
|
+
renderDirectModelPicker();
|
|
173
|
+
const p = directProviders().find((x) => x.id === pid);
|
|
174
|
+
const names = p ? (p.models || []).filter((m) => m.enabled !== false && !m.hidden)
|
|
175
|
+
.map((m) => m.name).filter(Boolean) : [];
|
|
176
|
+
const ms = $("f-direct-model-name");
|
|
177
|
+
if (names.length && names.includes(names[0])) ms.value = names[0];
|
|
178
|
+
cmpDirectBtnSync();
|
|
179
|
+
}
|
|
180
|
+
cmpModelProv = pid;
|
|
181
|
+
cmpModelMenuRender();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// 选定:写回隐藏 select 真源(提交流/回归测试全走原路径)
|
|
185
|
+
$("f-direct-provider").value = b.dataset.p;
|
|
186
|
+
$("f-direct-model-name").value = b.dataset.m;
|
|
187
|
+
cmpDirectBtnSync();
|
|
188
|
+
toggleModelMenu(false);
|
|
189
|
+
});
|
|
190
|
+
document.addEventListener("click", (e) => {
|
|
191
|
+
const wrap = $("f-direct-wrap");
|
|
192
|
+
if (wrap && !wrap.contains(e.target)) toggleModelMenu(false);
|
|
193
|
+
});
|
|
194
|
+
document.addEventListener("keydown", (e) => {
|
|
195
|
+
if (e.key === "Escape") toggleModelMenu(false);
|
|
196
|
+
});
|
|
197
|
+
})();
|
|
198
|
+
|
|
84
199
|
/* 类型选择的可见层:带黑白图标的按钮 + 弹出菜单。原生 <option> 渲染不了 SVG,
|
|
85
200
|
* 所以隐藏 select 只当值真源(既有联动/测试全部照旧),外观全靠这层。 */
|
|
86
201
|
function renderTypeMenu() {
|
|
@@ -153,6 +268,7 @@ function toggleTypeMenu(force) {
|
|
|
153
268
|
}
|
|
154
269
|
|
|
155
270
|
function pickType(id) {
|
|
271
|
+
S.typeTouched = true; // 用户手动选过:flows 重渲染时优先保住,不让位偏好记忆
|
|
156
272
|
const sel = $("f-type");
|
|
157
273
|
if (sel && sel.value !== id) {
|
|
158
274
|
sel.value = id;
|
|
@@ -175,8 +291,11 @@ function onTypeChange() {
|
|
|
175
291
|
// 直连:验证命令与评审参数都不适用,两块一起收起(目标+附件即全部输入)
|
|
176
292
|
if (codeOnly) codeOnly.classList.toggle("hidden", isReview || isDirect);
|
|
177
293
|
if (reviewOnly) reviewOnly.classList.toggle("hidden", !isReview);
|
|
178
|
-
|
|
179
|
-
if (
|
|
294
|
+
// 对话模型选择已收进工具行 pill:更多选项里的旧块永久隐藏(select 真源保留)
|
|
295
|
+
if ($("f-direct-model")) $("f-direct-model").classList.add("hidden");
|
|
296
|
+
const dwrap = $("f-direct-wrap");
|
|
297
|
+
if (dwrap) dwrap.classList.toggle("hidden", !isDirect);
|
|
298
|
+
if (isDirect) { renderDirectModelPicker(); cmpDirectBtnSync(); }
|
|
180
299
|
const bibleCreate = $("bible-create-field");
|
|
181
300
|
const isSerialReview = isReview && !!(flow && flow.serial);
|
|
182
301
|
if (bibleCreate) bibleCreate.classList.toggle("hidden", !isSerialReview);
|
|
@@ -237,6 +356,7 @@ function renderDirectModelPicker() {
|
|
|
237
356
|
ms.innerHTML = '<option value="">' + t("随厂商推荐") + '</option>' + names.map((name) =>
|
|
238
357
|
'<option value="' + esc(name) + '">' + esc(name) + "</option>").join("");
|
|
239
358
|
if (oldModel && names.includes(oldModel)) ms.value = oldModel;
|
|
359
|
+
cmpDirectBtnSync(); // 工具行 pill 文字跟随真源
|
|
240
360
|
}
|
|
241
361
|
|
|
242
362
|
function recommendTaskType(goal) {
|
|
@@ -8969,19 +9089,40 @@ function mkCharsText(n) {
|
|
|
8969
9089
|
return t("约 ") + n + " " + t("字");
|
|
8970
9090
|
}
|
|
8971
9091
|
|
|
8972
|
-
function mkCardHtml(p) {
|
|
9092
|
+
function mkCardHtml(p, section) {
|
|
8973
9093
|
let ops;
|
|
8974
9094
|
if (p.builtin) ops = '<button class="ghost small" disabled>' + t("已内置") + "</button>";
|
|
8975
9095
|
else if (p.installed) ops = '<span class="tag ok">' + t("已安装") + "</span>" +
|
|
8976
9096
|
'<button class="ghost small" onclick="mkRemove(\'' + esc(p.id) + '\')">' + t("卸载") + "</button>";
|
|
8977
9097
|
else ops = '<button class="primary small" onclick="mkInstall(\'' + esc(p.id) + '\')">' + t("安装") + "</button>";
|
|
9098
|
+
// 已装插件卡片直接给启停开关(对齐 ZCode 插件管理形态:启停不用去经验库翻)。
|
|
9099
|
+
// 注意传 pack_id(user-哈希)而非市场 id——启停记账在 skills 用户包上
|
|
9100
|
+
const toggle = p.installed
|
|
9101
|
+
? '<label class="switch" title="' + (p.enabled ? t("已启用") : t("已停用")) + '">' +
|
|
9102
|
+
'<input type="checkbox"' + (p.enabled ? " checked" : "") +
|
|
9103
|
+
' onchange="mkTogglePack(\'' + esc(p.pack_id || p.id) + '\', this.checked)"></label>'
|
|
9104
|
+
: "";
|
|
9105
|
+
const sec = section ? '<span class="tag mk-sec">' + esc(section) + "</span>" : "";
|
|
8978
9106
|
return '<div class="card mk-card"><div class="head">' +
|
|
8979
9107
|
'<span class="name">' + esc(t(p.name)) + "</span>" +
|
|
8980
|
-
'<span class="tag">' + esc(t(p.category || "")) + "</span>" +
|
|
9108
|
+
'<span class="tag">' + esc(t(p.category || "")) + "</span>" + sec +
|
|
8981
9109
|
"</div>" +
|
|
8982
9110
|
'<div class="note">' + esc(t(p.desc || "")) + "</div>" +
|
|
8983
9111
|
'<div class="mk-meta"><span title="' + esc(t("运行该类型任务时自动注入提示词,无需手动调用")) + '">' + t("适用:") + esc(mkScopeText(p.scopes)) + "</span><span>" + esc(mkCharsText(p.chars)) + "</span></div>" +
|
|
8984
|
-
'<div class="ops">' + ops + "</div></div>";
|
|
9112
|
+
'<div class="ops">' + ops + toggle + "</div></div>";
|
|
9113
|
+
}
|
|
9114
|
+
|
|
9115
|
+
/* 已装插件启停:走经验库包启停(同一条链),停用后该插件不再注入任务提示词 */
|
|
9116
|
+
async function mkTogglePack(id, enabled) {
|
|
9117
|
+
try {
|
|
9118
|
+
await api("/api/skills/pack-op", { method: "POST",
|
|
9119
|
+
body: JSON.stringify({ id, op: enabled ? "enable" : "disable" }) });
|
|
9120
|
+
toast(enabled ? t("已启用") : t("已停用(不再注入任务提示词)"));
|
|
9121
|
+
loadMarket();
|
|
9122
|
+
} catch (e) {
|
|
9123
|
+
toast(e.message || t("操作失败"), true);
|
|
9124
|
+
loadMarket();
|
|
9125
|
+
}
|
|
8985
9126
|
}
|
|
8986
9127
|
|
|
8987
9128
|
function renderMarket() {
|
|
@@ -9011,7 +9152,23 @@ function renderMarket() {
|
|
|
9011
9152
|
(p.name || "").toLowerCase().indexOf(q) >= 0 || (p.desc || "").toLowerCase().indexOf(q) >= 0);
|
|
9012
9153
|
const cnt = $("mk-count");
|
|
9013
9154
|
if (cnt && mkActiveView() === "local") cnt.textContent = t("共 ") + items.length + t(" 个");
|
|
9014
|
-
|
|
9155
|
+
// 分区渲染(对齐 ZCode 插件管理形态):内置 / 已安装 / 可安装三组。
|
|
9156
|
+
// #mk-grid 本身是 grid 容器,直接塞 h3 会把标题当格子排(样式乱)——
|
|
9157
|
+
// 分区模式给外层加 .mk-grouped 转块级,每组自带独立 .mk-grid。
|
|
9158
|
+
if (st === "all" && !cat && !q) {
|
|
9159
|
+
const builtin = items.filter((p) => p.builtin);
|
|
9160
|
+
const inst = items.filter((p) => !p.builtin && p.installed);
|
|
9161
|
+
const avail = items.filter((p) => !p.builtin && !p.installed);
|
|
9162
|
+
const sec = (title, list) => !list.length ? "" :
|
|
9163
|
+
'<h3 class="sec-title">' + esc(t(title)) + ' <span class="tag">' + list.length + "</span></h3>" +
|
|
9164
|
+
'<div class="mk-grid">' + list.map((p) => mkCardHtml(p)).join("") + "</div>";
|
|
9165
|
+
grid.classList.add("mk-grouped");
|
|
9166
|
+
grid.innerHTML = sec(t("内置"), builtin) + sec(t("已安装"), inst) + sec(t("可安装"), avail) ||
|
|
9167
|
+
'<div class="empty">' + t("没有符合条件插件") + "</div>";
|
|
9168
|
+
return;
|
|
9169
|
+
}
|
|
9170
|
+
grid.classList.remove("mk-grouped");
|
|
9171
|
+
grid.innerHTML = items.map((p) => mkCardHtml(p)).join("") || '<div class="empty">' + t("没有符合条件插件") + "</div>";
|
|
9015
9172
|
}
|
|
9016
9173
|
|
|
9017
9174
|
async function mkInstall(id) {
|
|
@@ -11283,6 +11440,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
11283
11440
|
$("f-git-rev").addEventListener("change", renderGitHint);
|
|
11284
11441
|
if ($("f-workdir").value) queueGitProbe(); // 回填的目录:进页面即探测代码版本
|
|
11285
11442
|
$("f-type").addEventListener("change", onTypeChange);
|
|
11443
|
+
// 编排模式/思考程度 pill:选项变化同步短名(含语言切换后的 option 文本变化)
|
|
11444
|
+
$("f-mode").addEventListener("change", () => syncCmpSelFace("f-mode"));
|
|
11445
|
+
$("f-thinking").addEventListener("change", () => syncCmpSelFace("f-thinking"));
|
|
11286
11446
|
// 类型菜单点外面收起(Escape 同收);按钮自身点击由 toggleTypeMenu 负责
|
|
11287
11447
|
document.addEventListener("click", (e) => {
|
|
11288
11448
|
const menu = $("type-menu"), btn = $("f-type-btn");
|
|
@@ -11308,6 +11468,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
11308
11468
|
$("f-thinking").addEventListener("change", refreshEstimate);
|
|
11309
11469
|
$("f-rounds").addEventListener("change", refreshEstimate);
|
|
11310
11470
|
$("f-direct-provider").addEventListener("change", renderDirectModelPicker);
|
|
11471
|
+
$("f-direct-model-name").addEventListener("change", cmpDirectBtnSync); // 更多选项里改模型也要同步 pill
|
|
11311
11472
|
$("f-type").dispatchEvent(new Event("change"));
|
|
11312
11473
|
cmpGreeting(); // 问候语按时段刷新(切语言/回任务页也会重算)
|
|
11313
11474
|
$("btn-reload-catalog").addEventListener("click", async () => {
|
package/app/ui/i18n.js
CHANGED
|
@@ -1281,6 +1281,8 @@
|
|
|
1281
1281
|
"约 ": "about ",
|
|
1282
1282
|
"字": "chars",
|
|
1283
1283
|
"已内置": "Built-in",
|
|
1284
|
+
"已启用": "Enabled",
|
|
1285
|
+
"已停用(不再注入任务提示词)": "Disabled — no longer injected into task prompts",
|
|
1284
1286
|
"没有符合条件插件": "No plugins match the filters",
|
|
1285
1287
|
"该插件已安装过": "Already installed",
|
|
1286
1288
|
"安装成功,运行任务时自动注入提示词;可到「经验库」启停或查看": "Installed — auto-injected into prompts when tasks run; enable/disable or view it in the skill library",
|
|
@@ -1327,6 +1329,16 @@
|
|
|
1327
1329
|
"更新插件": "Update plugin",
|
|
1328
1330
|
"更新模型配置": "Update model settings",
|
|
1329
1331
|
"更新智能体绑定": "Update agent binding",
|
|
1332
|
+
"测试禅道连接": "Test ZenTao connection",
|
|
1333
|
+
"拉取禅道产品": "Fetch ZenTao products",
|
|
1334
|
+
"拉取禅道账号": "Fetch ZenTao accounts",
|
|
1335
|
+
"拉取模块清单": "Fetch module list",
|
|
1336
|
+
"禅道扫描 Bug": "Scan ZenTao bugs",
|
|
1337
|
+
"保存禅道配置": "Save ZenTao settings",
|
|
1338
|
+
"刷新群列表": "Refresh group list",
|
|
1339
|
+
"扫描群摘要": "Scan digests",
|
|
1340
|
+
"保存群摘要设置": "Save digest settings",
|
|
1341
|
+
"关闭端口进程": "Close port process",
|
|
1330
1342
|
"提交操作": "Submit operation",
|
|
1331
1343
|
"{0} 项处理中": "{0} in progress",
|
|
1332
1344
|
"{0} 项失败": "{0} failed",
|
|
@@ -2464,6 +2476,13 @@
|
|
|
2464
2476
|
scope.querySelectorAll("[data-i18n-aria]").forEach((el) => {
|
|
2465
2477
|
el.setAttribute("aria-label", t(el.dataset.i18nAria));
|
|
2466
2478
|
});
|
|
2479
|
+
// 编排 pill 短名取自 option 文本,重译后要跟着换语言
|
|
2480
|
+
if (typeof window.syncCmpSelFace === "function") {
|
|
2481
|
+
["f-mode", "f-thinking"].forEach((id) => window.syncCmpSelFace(id));
|
|
2482
|
+
}
|
|
2483
|
+
if (typeof window.cmpDirectBtnSync === "function") {
|
|
2484
|
+
window.cmpDirectBtnSync(); // 「自动推荐/推荐」等词也要换语言
|
|
2485
|
+
}
|
|
2467
2486
|
}
|
|
2468
2487
|
|
|
2469
2488
|
// 暴露
|
package/app/ui/index.html
CHANGED
|
@@ -294,7 +294,37 @@
|
|
|
294
294
|
<div class="att-strip">
|
|
295
295
|
<button type="button" class="ghost small" id="btn-attach" title="添加附件:截图 / 文本 / Word·Excel·PPT / PDF 等(普通 8MB,Office 24MB),创建任务时落盘到工作目录 _attachments/;也可直接粘贴截图" data-i18n-title="添加附件:截图 / 文本 / Word·Excel·PPT / PDF 等(普通 8MB,Office 24MB),创建任务时落盘到工作目录 _attachments/;也可直接粘贴截图"><svg class="ico" aria-hidden="true"><use href="#i-paperclip"></use></svg><span data-i18n="附件">附件</span></button>
|
|
296
296
|
</div>
|
|
297
|
+
<!-- 右组(参考主流输入框):编排/思考/对话模型偏好 + 发送。
|
|
298
|
+
pill 用透明原生 select 覆盖,点哪儿都弹原生下拉;短名由
|
|
299
|
+
syncCmpSelFace 同步。2026-09-21 用户拍板「整理到对话框里」。 -->
|
|
297
300
|
<div class="cmp-send">
|
|
301
|
+
<span class="cmp-sel" data-i18n-title="编排模式" title="编排模式">
|
|
302
|
+
<select id="f-mode" class="cmp-sel-native" aria-label="编排模式" data-i18n-aria="编排模式">
|
|
303
|
+
<option data-i18n="自动(推荐):按任务类型与风险动态决定步骤" value="auto">自动(推荐):按任务类型与风险动态决定步骤</option>
|
|
304
|
+
<option data-i18n="快速:少步骤优先,先做确定性验收" value="fast">快速:少步骤优先,先做确定性验收</option>
|
|
305
|
+
<option data-i18n="专家:完整规划、多评审、失败修复与换将" value="expert">专家:完整规划、多评审、失败修复与换将</option>
|
|
306
|
+
<option data-i18n="手动:自行指定实现者与评审组" value="manual">手动:自行指定实现者与评审组</option>
|
|
307
|
+
</select>
|
|
308
|
+
<span class="cmp-sel-face" id="f-mode-face">自动</span>
|
|
309
|
+
<svg class="ico chev" aria-hidden="true"><use href="#i-chevron-r"></use></svg>
|
|
310
|
+
</span>
|
|
311
|
+
<span class="cmp-sel" data-i18n-title="思考程度" title="思考程度">
|
|
312
|
+
<select id="f-thinking" class="cmp-sel-native" aria-label="思考程度" data-i18n-aria="思考程度">
|
|
313
|
+
<option data-i18n="标准:平衡质量与速度" value="standard" selected>标准:平衡质量与速度</option>
|
|
314
|
+
<option data-i18n="自动:随任务难度调整" value="auto">自动:随任务难度调整</option>
|
|
315
|
+
<option data-i18n="快速:少推理、优先响应" value="low">快速:少推理、优先响应</option>
|
|
316
|
+
<option data-i18n="深度:更多推理与检查" value="high">深度:更多推理与检查</option>
|
|
317
|
+
</select>
|
|
318
|
+
<span class="cmp-sel-face" id="f-thinking-face">标准</span>
|
|
319
|
+
<svg class="ico chev" aria-hidden="true"><use href="#i-chevron-r"></use></svg>
|
|
320
|
+
</span>
|
|
321
|
+
<!-- 厂商/模型 pill(仅「直接执行」类型显示):点开厂商→模型两级
|
|
322
|
+
钻取菜单,选定写回隐藏 select 真源 -->
|
|
323
|
+
<span class="cmp-sel hidden" id="f-direct-wrap" data-i18n-title="对话模型(可选)" title="对话模型(可选)">
|
|
324
|
+
<button type="button" class="cmp-sel-face cmp-sel-btn" id="f-direct-btn" onclick="toggleModelMenu()" aria-haspopup="menu" aria-controls="cmp-model-menu">自动推荐</button>
|
|
325
|
+
<svg class="ico chev" aria-hidden="true"><use href="#i-chevron-r"></use></svg>
|
|
326
|
+
<div id="cmp-model-menu" class="type-menu hidden" role="menu" aria-label="对话模型" data-i18n-aria="对话模型"></div>
|
|
327
|
+
</span>
|
|
298
328
|
<span id="create-msg" class="msg"></span>
|
|
299
329
|
<button id="btn-create" class="primary round" title="创建并运行" data-i18n-title="创建并运行" aria-label="创建并运行" data-i18n-aria="创建并运行"><svg class="ico" aria-hidden="true"><use href="#i-arrow-up"></use></svg></button>
|
|
300
330
|
</div>
|
|
@@ -316,6 +346,11 @@
|
|
|
316
346
|
<details id="f-advanced">
|
|
317
347
|
<summary data-i18n="更多选项">更多选项</summary>
|
|
318
348
|
<div class="adv-body">
|
|
349
|
+
<!-- 对话模型(可选)是低频高级项:只对「直接执行」类型生效,收在这 -->
|
|
350
|
+
<div id="f-direct-model" class="field hidden"><label data-i18n="对话模型(可选)">对话模型(可选)</label><div class="grid-2">
|
|
351
|
+
<select id="f-direct-provider" aria-label="对话厂商" data-i18n-aria="对话厂商"><option data-i18n="自动推荐">自动推荐</option></select>
|
|
352
|
+
<select id="f-direct-model-name" aria-label="对话模型" data-i18n-aria="对话模型"><option data-i18n="随厂商推荐">随厂商推荐</option></select>
|
|
353
|
+
</div><p class="hint" data-i18n="只对本次对话生效,不会修改全局绑定;指定渠道不可用时会明确报错。">只对本次对话生效,不会修改全局绑定;指定渠道不可用时会明确报错。</p></div>
|
|
319
354
|
<div class="grid-2">
|
|
320
355
|
<div class="field">
|
|
321
356
|
<label data-i18n="继续会话">继续会话</label>
|
|
@@ -326,27 +361,6 @@
|
|
|
326
361
|
<div class="field"><label data-i18n="任务标题">任务标题</label><input id="f-title" placeholder="可空,自动取目标首行" data-i18n-ph="可空,自动取目标首行"></div>
|
|
327
362
|
</div>
|
|
328
363
|
<div class="field"><label data-i18n="上下文">上下文</label><textarea id="f-context" rows="2" placeholder="背景信息、约束、相关文件(可空)" data-i18n-ph="背景信息、约束、相关文件(可空)"></textarea></div>
|
|
329
|
-
<div class="field">
|
|
330
|
-
<label data-i18n="编排模式">编排模式</label>
|
|
331
|
-
<select id="f-mode">
|
|
332
|
-
<option data-i18n="自动(推荐):按任务类型与风险动态决定步骤" value="auto">自动(推荐):按任务类型与风险动态决定步骤</option>
|
|
333
|
-
<option data-i18n="快速:少步骤优先,先做确定性验收" value="fast">快速:少步骤优先,先做确定性验收</option>
|
|
334
|
-
<option data-i18n="专家:完整规划、多评审、失败修复与换将" value="expert">专家:完整规划、多评审、失败修复与换将</option>
|
|
335
|
-
<option data-i18n="手动:自行指定实现者与评审组" value="manual">手动:自行指定实现者与评审组</option>
|
|
336
|
-
</select>
|
|
337
|
-
</div>
|
|
338
|
-
<div class="grid-2">
|
|
339
|
-
<div class="field"><label data-i18n="思考程度">思考程度</label><select id="f-thinking">
|
|
340
|
-
<option data-i18n="标准:平衡质量与速度" value="standard" selected>标准:平衡质量与速度</option>
|
|
341
|
-
<option data-i18n="自动:随任务难度调整" value="auto">自动:随任务难度调整</option>
|
|
342
|
-
<option data-i18n="快速:少推理、优先响应" value="low">快速:少推理、优先响应</option>
|
|
343
|
-
<option data-i18n="深度:更多推理与检查" value="high">深度:更多推理与检查</option>
|
|
344
|
-
</select></div>
|
|
345
|
-
<div id="f-direct-model" class="field hidden"><label data-i18n="对话模型(可选)">对话模型(可选)</label><div class="grid-2">
|
|
346
|
-
<select id="f-direct-provider" aria-label="对话厂商" data-i18n-aria="对话厂商"><option data-i18n="自动推荐">自动推荐</option></select>
|
|
347
|
-
<select id="f-direct-model-name" aria-label="对话模型" data-i18n-aria="对话模型"><option data-i18n="随厂商推荐">随厂商推荐</option></select>
|
|
348
|
-
</div><p class="hint" data-i18n="只对本次对话生效,不会修改全局绑定;指定渠道不可用时会明确报错。">只对本次对话生效,不会修改全局绑定;指定渠道不可用时会明确报错。</p></div>
|
|
349
|
-
</div>
|
|
350
364
|
|
|
351
365
|
<div id="f-code-only">
|
|
352
366
|
<div class="field"><label data-i18n="验证命令">验证命令</label><input id="f-verify" placeholder="例:python -m pytest -q(可空=只评审)" data-i18n-ph="例:python -m pytest -q(可空=只评审)"></div>
|
package/app/ui/style.css
CHANGED
|
@@ -5615,3 +5615,59 @@ button.request-busy > * { opacity: .18; }
|
|
|
5615
5615
|
.ch-ev { padding-left: 10px; border-left: 2px solid var(--border); color: var(--muted); }
|
|
5616
5616
|
.ch-ev.ok { border-left-color: var(--ok); color: inherit; }
|
|
5617
5617
|
.ch-ev.bad { border-left-color: var(--bad); color: inherit; }
|
|
5618
|
+
|
|
5619
|
+
/* composer 工具行「编排模式/思考程度」pill(2026-09-21 用户拍板「整理到对话
|
|
5620
|
+
* 框里面去」):透明原生 select 覆盖整颗 pill,点哪儿都是原生下拉;face 只显示
|
|
5621
|
+
* 选项短前缀(syncCmpSelFace 同步),视觉与「直接执行」类型 pill 同一语汇。 */
|
|
5622
|
+
.cmp-sel { position: relative; display: inline-flex; align-items: center; gap: 5px;
|
|
5623
|
+
padding: 5px 11px; border: 1px solid var(--border); border-radius: 999px;
|
|
5624
|
+
font-size: 12.5px; color: var(--muted); background: transparent;
|
|
5625
|
+
white-space: nowrap; user-select: none; }
|
|
5626
|
+
.cmp-sel:hover { border-color: var(--border-strong); color: var(--text); }
|
|
5627
|
+
.cmp-sel-face { max-width: 88px; overflow: hidden; text-overflow: ellipsis; }
|
|
5628
|
+
.cmp-sel .chev { width: 13px; height: 13px; transform: rotate(90deg); opacity: .65; flex: none; }
|
|
5629
|
+
.cmp-sel-native { position: absolute; inset: 0; width: 100%; height: 100%;
|
|
5630
|
+
opacity: 0; cursor: pointer; border: none; margin: 0; }
|
|
5631
|
+
.cmp-sel-btn { appearance: none; background: none; border: none; padding: 0;
|
|
5632
|
+
font: inherit; color: inherit; cursor: pointer; }
|
|
5633
|
+
/* 「厂商/模型」组合名比单档选项长:放宽截断(其他 pill 保持 88px 短名) */
|
|
5634
|
+
#f-direct-wrap .cmp-sel-btn { max-width: 200px; overflow: hidden; text-overflow: ellipsis; }
|
|
5635
|
+
/* 厂商/模型钻取菜单:脱离 pill 宽度限制(type-menu 默认 left/right 撑满父级) */
|
|
5636
|
+
#cmp-model-menu { left: 0; right: auto; min-width: 250px; }
|
|
5637
|
+
|
|
5638
|
+
/* 插件市场:内置/已安装/可安装分区标题与已装启停开关(对齐 ZCode 形态) */
|
|
5639
|
+
#mk-grid .sec-title { margin: 14px 0 6px; }
|
|
5640
|
+
.mk-sec { background: color-mix(in srgb, var(--accent) 12%, transparent); }
|
|
5641
|
+
.mk-card .switch { display: inline-flex; align-items: center; cursor: pointer; }
|
|
5642
|
+
.mk-card .switch input { appearance: none; width: 34px; height: 19px;
|
|
5643
|
+
border-radius: 999px; background: var(--border, rgba(128,128,128,.4));
|
|
5644
|
+
position: relative; cursor: pointer; transition: background .15s; margin: 0; }
|
|
5645
|
+
.mk-card .switch input:checked { background: var(--accent); }
|
|
5646
|
+
.mk-card .switch input::after { content: ""; position: absolute; top: 2px; left: 2px;
|
|
5647
|
+
width: 15px; height: 15px; border-radius: 50%; background: #fff; transition: left .15s; }
|
|
5648
|
+
.mk-card .switch input:checked::after { left: 17px; }
|
|
5649
|
+
|
|
5650
|
+
/* 插件市场分区模式:外层转块级,标题不当格子排(此前 h3 塞进 grid 导致
|
|
5651
|
+
标题/卡片错位——用户截图反馈「样式乱了」);每组自带独立 grid。 */
|
|
5652
|
+
#mk-grid.mk-grouped { display: block; }
|
|
5653
|
+
#mk-grid.mk-grouped .mk-grid { display: grid; gap: 12px;
|
|
5654
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
|
|
5655
|
+
#mk-grid.mk-grouped .sec-title { margin: 16px 0 8px; }
|
|
5656
|
+
|
|
5657
|
+
/* 首页 composer 图片附件缩略图:与任务页同款 88×88 卡(此前样式只写在
|
|
5658
|
+
* #sub-tasks 作用域,首页 img 按原始尺寸渲染直接压住输入文字——2026-09-21
|
|
5659
|
+
* 用户实测「有遮挡」)。删除钮右上角悬浮。 */
|
|
5660
|
+
.att-chip.att-image { position: relative; width: 88px; height: 88px; max-width: none;
|
|
5661
|
+
padding: 0; overflow: visible; border-radius: 12px; background: var(--panel2);
|
|
5662
|
+
border-color: var(--border); box-shadow: 0 2px 10px color-mix(in srgb, #000 10%, transparent); }
|
|
5663
|
+
.att-chip.att-image img { display: block; width: 100%; height: 100%; object-fit: cover;
|
|
5664
|
+
border-radius: inherit; background: var(--panel2); }
|
|
5665
|
+
.att-chip.att-image .att-image-name { position: absolute; width: 1px; height: 1px;
|
|
5666
|
+
padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0);
|
|
5667
|
+
white-space: nowrap; border: 0; }
|
|
5668
|
+
.att-chip.att-image .ax { position: absolute; z-index: 2; top: -6px; right: -6px;
|
|
5669
|
+
display: inline-flex; align-items: center; justify-content: center; width: 20px;
|
|
5670
|
+
height: 20px; padding: 0; border: 1px solid var(--panel); border-radius: 50%;
|
|
5671
|
+
background: var(--panel); color: var(--muted); }
|
|
5672
|
+
.att-chip.att-image .ax:hover { background: var(--bad); color: #fff; }
|
|
5673
|
+
.att-chip.att-image .ax .ico { width: 11px; height: 11px; }
|
package/package.json
CHANGED