codebee 0.1.95 → 0.1.97

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.
Files changed (144) hide show
  1. package/CHANGELOG.md +733 -720
  2. package/LICENSE +21 -21
  3. package/README.md +516 -509
  4. package/app/core/acceptance.py +100 -100
  5. package/app/core/agent_context.py +118 -0
  6. package/app/core/aiflavor.py +197 -197
  7. package/app/core/analytics.py +38 -38
  8. package/app/core/artifacts.py +148 -148
  9. package/app/core/assets.py +110 -110
  10. package/app/core/attachments.py +623 -623
  11. package/app/core/audit.py +30 -30
  12. package/app/core/backup.py +470 -470
  13. package/app/core/beekeeper.py +183 -183
  14. package/app/core/benchstore.py +102 -95
  15. package/app/core/board.py +246 -209
  16. package/app/core/bookmeta.py +1036 -1036
  17. package/app/core/bookmeta_catalog.py +180 -180
  18. package/app/core/branching.py +137 -137
  19. package/app/core/builtin_agent.py +2055 -1680
  20. package/app/core/capability.py +144 -144
  21. package/app/core/chaptersafe.py +32 -32
  22. package/app/core/checkpoints.py +648 -0
  23. package/app/core/cleanup.py +303 -303
  24. package/app/core/compaction.py +201 -201
  25. package/app/core/contracts.py +255 -255
  26. package/app/core/covergen.py +356 -356
  27. package/app/core/defectretro.py +43 -43
  28. package/app/core/diagnostics.py +114 -114
  29. package/app/core/dispatch.py +198 -191
  30. package/app/core/dispatch_log.py +128 -128
  31. package/app/core/env_scrub.py +85 -85
  32. package/app/core/errorlog.py +137 -137
  33. package/app/core/eval_matrix.py +82 -0
  34. package/app/core/evalbench.py +184 -91
  35. package/app/core/evaluation.py +174 -174
  36. package/app/core/failure_notify.py +290 -290
  37. package/app/core/flow_graph.py +33 -0
  38. package/app/core/goal_service.py +167 -167
  39. package/app/core/health.py +389 -389
  40. package/app/core/history.py +38 -38
  41. package/app/core/hooks.py +162 -162
  42. package/app/core/jobs.py +25 -4
  43. package/app/core/knowledge_pipeline.py +86 -0
  44. package/app/core/manager.py +2232 -2232
  45. package/app/core/mcp_client.py +200 -29
  46. package/app/core/mocks.py +64 -64
  47. package/app/core/notify.py +201 -201
  48. package/app/core/novel_quality.py +181 -189
  49. package/app/core/openapi.py +177 -154
  50. package/app/core/operations.py +228 -228
  51. package/app/core/paihang.py +145 -145
  52. package/app/core/pipeline.py +319 -59
  53. package/app/core/planner.py +17 -11
  54. package/app/core/policy.py +84 -0
  55. package/app/core/portguard.py +111 -111
  56. package/app/core/presence.py +77 -77
  57. package/app/core/preview.py +293 -293
  58. package/app/core/project_memory.py +139 -0
  59. package/app/core/prompt_guard.py +78 -78
  60. package/app/core/publish/__init__.py +7 -7
  61. package/app/core/publish/auto.py +106 -22
  62. package/app/core/publish/browser.py +694 -655
  63. package/app/core/publish/chaprows.py +138 -0
  64. package/app/core/publish/fanqie.py +212 -165
  65. package/app/core/publish/flow.py +832 -640
  66. package/app/core/publish/flows-fanqie-calibrated.json +361 -336
  67. package/app/core/publish/ledger.py +51 -2
  68. package/app/core/publish/manager.py +1150 -1118
  69. package/app/core/publish/qimao.py +176 -176
  70. package/app/core/publish/volumes.py +108 -0
  71. package/app/core/publish/ws.py +139 -139
  72. package/app/core/quality_gate.py +194 -194
  73. package/app/core/redact.py +38 -38
  74. package/app/core/repeat_guard.py +129 -129
  75. package/app/core/retrieval.py +137 -137
  76. package/app/core/router.py +398 -398
  77. package/app/core/runner.py +2239 -2238
  78. package/app/core/selfupdate.py +384 -384
  79. package/app/core/session_log.py +161 -161
  80. package/app/core/sessions.py +312 -312
  81. package/app/core/settings.py +33 -5
  82. package/app/core/settings_schema.py +282 -271
  83. package/app/core/share_page.py +114 -114
  84. package/app/core/skill_scan.py +139 -139
  85. package/app/core/skillpacks/fanqie-novel.md +8 -6
  86. package/app/core/skillpacks/market/character-bible.md +66 -66
  87. package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
  88. package/app/core/skillpacks/market/git-workflow.md +57 -57
  89. package/app/core/skillpacks/market/release-notes.md +72 -72
  90. package/app/core/skillpacks/market/weekly-report.md +71 -71
  91. package/app/core/skillpacks/market/worldview-consistency.md +70 -70
  92. package/app/core/skillpacks/qimao-signing.md +14 -11
  93. package/app/core/skills.py +32 -21
  94. package/app/core/step_runner.py +132 -132
  95. package/app/core/store.py +24 -0
  96. package/app/core/story_tracking.py +275 -275
  97. package/app/core/task_compile.py +199 -199
  98. package/app/core/telemetry.py +292 -292
  99. package/app/core/tlsctx.py +64 -64
  100. package/app/core/token_meter.py +147 -147
  101. package/app/core/tool_outputs.py +107 -0
  102. package/app/core/tracing.py +207 -144
  103. package/app/core/upstream.py +41 -41
  104. package/app/core/usage.py +1038 -1038
  105. package/app/core/volumes.py +389 -389
  106. package/app/core/webhooks.py +89 -0
  107. package/app/core/wxdigest.py +865 -858
  108. package/app/core/zentao.py +2692 -2692
  109. package/app/main.py +179 -10
  110. package/app/mcp_server.py +244 -244
  111. package/app/pick_dialog.py +110 -110
  112. package/app/ui/app.js +563 -21
  113. package/app/ui/artdiff.js +74 -74
  114. package/app/ui/assets/community-qr.png +0 -0
  115. package/app/ui/assets/donate-alipay.jpg +0 -0
  116. package/app/ui/assets/donate-qr.jpg +0 -0
  117. package/app/ui/assets/workbench-reference.png +0 -0
  118. package/app/ui/board.css +451 -666
  119. package/app/ui/board.html +165 -162
  120. package/app/ui/board.js +509 -305
  121. package/app/ui/hive3d.js +242 -0
  122. package/app/ui/i18n.js +125 -9
  123. package/app/ui/icons/bee.svg +79 -79
  124. package/app/ui/index.html +60 -3
  125. package/app/ui/manifest.json +16 -16
  126. package/app/ui/qrcode.js +2297 -2297
  127. package/app/ui/style.css +189 -9
  128. package/bin/tutti.js +147 -147
  129. package/package.json +49 -48
  130. package/tools/wx_reader.py +261 -261
  131. package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +0 -88
  132. package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +0 -126
  133. package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
  134. package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +0 -210
  135. package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
  136. package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
  137. package/app/.playwright-cli/run-detail-after.png +0 -0
  138. package/app/output/_createbook_body.json +0 -1
  139. package/app/output/_probe_fq_click.py +0 -50
  140. package/app/output/_probe_fq_form.py +0 -42
  141. package/app/output/_probe_fq_modal.py +0 -55
  142. package/app/output/_probe_fq_points.py +0 -71
  143. package/app/output/_probe_fq_submit.py +0 -34
  144. package/app/output/_probe_fq_watch.py +0 -62
@@ -0,0 +1,242 @@
1
+ /* Fixed-view office: the reference artwork preserves its lighting/materials;
2
+ * localized HTML screens share the same aspect ratio, pan and zoom transform.
3
+ * No WebGL is required. The host supplies actual run/step data and log access. */
4
+ window.Hive3D = (function () {
5
+ "use strict";
6
+
7
+ const tr = (key, ...args) => typeof window.t === "function" ? window.t(key, ...args) : key;
8
+ const clamp = (value, low, high) => Math.max(low, Math.min(high, value));
9
+ const STATUS = { running: "运行中", done: "完成", queued: "排队中", failed: "失败", timeout: "超时", cancelled: "已取消" };
10
+ const STAGES = ["规划", "起草", "评审", "执行", "打磨", "合成"];
11
+ // Glass content rectangles measured on the 2048 × 1151 reference image.
12
+ const SCREENS = [
13
+ [163, 441, 127, 55], [389, 441, 126, 55], [616, 441, 125, 55], [843, 441, 125, 55],
14
+ [1073, 441, 122, 55], [1298, 441, 125, 55], [1531, 441, 120, 55], [1750, 441, 125, 55],
15
+ [166, 724, 165, 66], [470, 724, 165, 66], [778, 724, 165, 66],
16
+ [1091, 724, 162, 66], [1392, 724, 163, 66], [1695, 724, 165, 66],
17
+ ];
18
+ const PLATES = [[278, 206], [578, 206], [876, 206], [1175, 206], [1473, 206], [1766, 206]];
19
+
20
+ function node(tag, className, parent) {
21
+ const el = document.createElement(tag);
22
+ el.className = className;
23
+ if (parent) parent.appendChild(el);
24
+ return el;
25
+ }
26
+
27
+ function ReferenceScene(opts) {
28
+ this.opts = opts;
29
+ this.canvas = opts.canvas;
30
+ this.overlay = opts.overlay;
31
+ this.host = this.canvas.parentElement;
32
+ this.onCellActivate = opts.onCellActivate || function () {};
33
+ this.cellRefresh = opts.cellRefresh || function () { return {}; };
34
+ this.model = null;
35
+ this.page = 0;
36
+ this.zoom = 1;
37
+ this.pan = { x: 0, y: 0 };
38
+ this.active = false;
39
+ this.timer = null;
40
+ this.screenMeta = [];
41
+ this.listeners = [];
42
+ this.layer = node("div", "hive-reference-layer", this.host);
43
+ this.layer.appendChild(this.host.querySelector(".hive-reference"));
44
+ this.layer.appendChild(this.overlay);
45
+ this.overlay.replaceChildren();
46
+ this.canvas.hidden = true;
47
+ this.host.dataset.renderer = "reference";
48
+
49
+ this.stageMeta = PLATES.map(([x, y], index) => {
50
+ const el = node("button", "hg-badge", this.overlay);
51
+ el.type = "button";
52
+ el.style.left = (x / 2048 * 100) + "%";
53
+ el.style.top = (y / 1151 * 100) + "%";
54
+ const name = node("b", "hg-name", el);
55
+ const meta = node("span", "hg-meta", el);
56
+ this.listen(el, "click", () => {
57
+ const lane = this.stageMeta[index].lane;
58
+ if (!lane) return;
59
+ const cell = lane.cells.find((item) => item.status === "running") || lane.cells[lane.cells.length - 1];
60
+ if (cell) this.onCellActivate(this.model.runId, cell.rel, cell);
61
+ });
62
+ return { el, name, meta, lane: null };
63
+ });
64
+ this.monitors = SCREENS.map(([x, y, width, height], index) => {
65
+ const el = node("button", "hg-monitor", this.overlay);
66
+ el.type = "button";
67
+ el.style.left = (x / 2048 * 100) + "%";
68
+ el.style.top = (y / 1151 * 100) + "%";
69
+ el.style.width = (width / 2048 * 100) + "%";
70
+ el.style.height = (height / 1151 * 100) + "%";
71
+ const role = node("b", "hg-monitor-role", el);
72
+ const status = node("span", "hg-monitor-status", el);
73
+ const tail = node("span", "hg-monitor-tail", el);
74
+ const time = node("span", "hg-monitor-time", el);
75
+ this.listen(el, "click", () => {
76
+ const cell = this.screenMeta[index];
77
+ if (cell) this.onCellActivate(this.model.runId, cell.rel, cell);
78
+ });
79
+ return { el, role, status, tail, time };
80
+ });
81
+ const toolbar = this.host.parentElement.querySelector(".hive-scene-tools");
82
+ this.pager = node("div", "hive-scene-pager");
83
+ this.pager.hidden = true;
84
+ this.prev = node("button", "hive-scene-btn", this.pager);
85
+ this.prev.type = "button"; this.prev.textContent = "‹";
86
+ this.pageLabel = node("span", "hive-scene-page-label", this.pager);
87
+ this.next = node("button", "hive-scene-btn", this.pager);
88
+ this.next.type = "button"; this.next.textContent = "›";
89
+ if (toolbar) toolbar.insertBefore(this.pager, toolbar.querySelector(".hive-scene-spacer"));
90
+ this.listen(this.prev, "click", () => this.showPage(this.page - 1));
91
+ this.listen(this.next, "click", () => this.showPage(this.page + 1));
92
+ this.bindSurface();
93
+ this.resizeObserver = new ResizeObserver(() => this.resize());
94
+ this.resizeObserver.observe(this.host);
95
+ this.languageObserver = new MutationObserver(() => this.updateOverlay());
96
+ this.languageObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["data-lang"] });
97
+ this.resize();
98
+ this.updateOverlay();
99
+ window.__hive3d = this;
100
+ }
101
+
102
+ ReferenceScene.prototype.listen = function (el, type, fn, options) {
103
+ el.addEventListener(type, fn, options);
104
+ this.listeners.push(() => el.removeEventListener(type, fn, options));
105
+ };
106
+
107
+ ReferenceScene.prototype.bindSurface = function () {
108
+ let drag = null;
109
+ this.listen(this.host, "contextmenu", (event) => event.preventDefault());
110
+ this.listen(this.host, "pointerdown", (event) => {
111
+ if (!this.active || event.target.closest("button") || event.button !== 0) return;
112
+ drag = { x: event.clientX, y: event.clientY, panX: this.pan.x, panY: this.pan.y };
113
+ this.host.setPointerCapture(event.pointerId);
114
+ this.host.classList.add("is-dragging");
115
+ });
116
+ this.listen(this.host, "pointermove", (event) => {
117
+ if (!drag) return;
118
+ this.pan.x = drag.panX + event.clientX - drag.x;
119
+ this.pan.y = drag.panY + event.clientY - drag.y;
120
+ this.applyTransform();
121
+ });
122
+ const end = () => { drag = null; this.host.classList.remove("is-dragging"); };
123
+ this.listen(this.host, "pointerup", end);
124
+ this.listen(this.host, "pointercancel", end);
125
+ this.listen(this.host, "dblclick", (event) => { if (!event.target.closest("button")) this.resetView(); });
126
+ this.listen(this.host, "wheel", (event) => {
127
+ if (!this.active || event.target.closest("button")) return;
128
+ event.preventDefault();
129
+ this.zoomAt(Math.exp(event.deltaY * 0.0011));
130
+ }, { passive: false });
131
+ };
132
+
133
+ ReferenceScene.prototype.sync = function (model) {
134
+ const changedRun = !this.model || this.model.runId !== model.runId;
135
+ this.model = model;
136
+ this.cells = model.lanes.flatMap((lane, stageIdx) => lane.cells.map((cell) => ({ ...cell, stageIdx })));
137
+ if (changedRun) {
138
+ const running = this.cells.findIndex((cell) => cell.status === "running");
139
+ this.page = running < 0 ? 0 : Math.floor(running / SCREENS.length);
140
+ this.resetView();
141
+ }
142
+ this.showPage(this.page);
143
+ };
144
+
145
+ ReferenceScene.prototype.showPage = function (page) {
146
+ const pages = Math.max(1, Math.ceil((this.cells || []).length / SCREENS.length));
147
+ this.page = clamp(page, 0, pages - 1);
148
+ this.screenMeta = (this.cells || []).slice(this.page * SCREENS.length, (this.page + 1) * SCREENS.length);
149
+ this.pager.hidden = pages <= 1;
150
+ this.prev.disabled = this.page === 0;
151
+ this.next.disabled = this.page === pages - 1;
152
+ this.pageLabel.textContent = (this.page + 1) + " / " + pages;
153
+ this.updateOverlay();
154
+ };
155
+
156
+ ReferenceScene.prototype.updateOverlay = function () {
157
+ const lanes = this.model ? this.model.lanes : [];
158
+ this.stageMeta.forEach((plate, index) => {
159
+ const lane = lanes[index];
160
+ plate.lane = lane || null;
161
+ const name = lane ? tr(lane.nameKey || lane.name) : tr(STAGES[index]);
162
+ plate.name.textContent = tr("阶段 {0} · {1}", index + 1, name);
163
+ plate.meta.textContent = lane ? tr("{0} / {1} 步骤", lane.settled, lane.count) : tr("等待任务");
164
+ plate.el.classList.toggle("hg-active", !!(lane && lane.active));
165
+ plate.el.disabled = !lane || !lane.cells.length;
166
+ plate.el.title = tr("点击查看实时日志");
167
+ });
168
+ this.monitors.forEach((monitor, index) => {
169
+ const cell = this.screenMeta[index];
170
+ const refresh = cell && cell.rel ? this.cellRefresh(cell.rel) || {} : {};
171
+ const state = cell ? cell.status : "idle";
172
+ monitor.el.className = "hg-monitor st-" + state;
173
+ monitor.el.disabled = !cell;
174
+ monitor.el.dataset.log = cell ? cell.rel : "";
175
+ monitor.role.textContent = cell ? tr(cell.role) : tr("空闲工位");
176
+ monitor.status.textContent = cell ? tr(STATUS[state] || "完成") : tr("待命");
177
+ monitor.tail.textContent = cell ? refresh.tail || cell.displayTail ||
178
+ (state === "running" ? tr("等待日志输出…") : state === "queued" ? tr("等待执行") : tr("(无输出)")) : tr("等待任务");
179
+ monitor.time.textContent = cell ? refresh.elapsed || cell.displayElapsed || "" : "";
180
+ monitor.el.title = cell ? [tr(cell.role), tr(STATUS[state] || "完成"), cell.displayAgent,
181
+ monitor.tail.textContent, tr("点击查看实时日志")].filter(Boolean).join(" · ") : tr("空闲工位");
182
+ monitor.el.setAttribute("aria-label", monitor.el.title);
183
+ });
184
+ this.prev.setAttribute("aria-label", tr("上一组工位"));
185
+ this.next.setAttribute("aria-label", tr("下一组工位"));
186
+ };
187
+
188
+ ReferenceScene.prototype.setActive = function (active) {
189
+ this.active = !!active;
190
+ this.layer.hidden = !this.active;
191
+ if (this.timer) clearInterval(this.timer);
192
+ this.timer = null;
193
+ if (this.active) {
194
+ this.updateOverlay();
195
+ this.timer = setInterval(() => {
196
+ if (!document.hidden && this.host.offsetParent !== null) this.updateOverlay();
197
+ }, 1000);
198
+ }
199
+ };
200
+
201
+ ReferenceScene.prototype.resize = function () {
202
+ this.cssW = this.host.clientWidth;
203
+ this.cssH = this.host.clientHeight;
204
+ this.applyTransform();
205
+ };
206
+ ReferenceScene.prototype.applyTransform = function () {
207
+ const xLimit = this.cssW * (this.zoom - 1) / 2;
208
+ const yLimit = this.cssH * (this.zoom - 1) / 2;
209
+ this.pan.x = clamp(this.pan.x, -xLimit, xLimit);
210
+ this.pan.y = clamp(this.pan.y, -yLimit, yLimit);
211
+ this.layer.style.transform = "translate(" + this.pan.x + "px," + this.pan.y + "px) scale(" + this.zoom + ")";
212
+ };
213
+ ReferenceScene.prototype.zoomAt = function (factor) {
214
+ this.zoom = clamp(this.zoom / factor, 1, 3);
215
+ this.applyTransform();
216
+ };
217
+ ReferenceScene.prototype.resetView = function () {
218
+ this.zoom = 1;
219
+ this.pan = { x: 0, y: 0 };
220
+ this.applyTransform();
221
+ };
222
+ ReferenceScene.prototype.info = function () {
223
+ return { renderer: "reference", cells: (this.cells || []).length, screens: SCREENS.length,
224
+ lanes: this.model ? this.model.lanes.length : 0, page: this.page, zoom: this.zoom, cssW: this.cssW, cssH: this.cssH };
225
+ };
226
+ ReferenceScene.prototype.projectCell = function (rel) {
227
+ const index = this.screenMeta.findIndex((cell) => cell.rel === rel);
228
+ if (index < 0) return null;
229
+ const [x, y, width, height] = SCREENS[index];
230
+ return { x: (x + width / 2) / 2048 * this.cssW, y: (y + height / 2) / 1151 * this.cssH };
231
+ };
232
+ ReferenceScene.prototype.dispose = function () {
233
+ this.setActive(false);
234
+ this.resizeObserver.disconnect();
235
+ this.languageObserver.disconnect();
236
+ this.listeners.forEach((remove) => remove());
237
+ this.pager.remove();
238
+ if (window.__hive3d === this) delete window.__hive3d;
239
+ };
240
+
241
+ return { create(opts) { return new ReferenceScene(opts); } };
242
+ })();
package/app/ui/i18n.js CHANGED
@@ -10,6 +10,33 @@
10
10
  const LANG_KEY = "orch.lang";
11
11
  // 字典:键 = 中文原文;值 = 英文译文。未命中即回落中文。
12
12
  const EN = {
13
+ "蜂巢视图控制": "Hive view controls",
14
+ "视图模式": "View mode",
15
+ "3D 场景": "3D scene",
16
+ "2D 列表": "2D list",
17
+ "拖动旋转 · 滚轮缩放 · 右键升降": "Drag to orbit · Scroll to zoom · Right-drag to climb",
18
+ "拖动平移 · 滚轮缩放 · 点击屏幕看日志": "Drag to pan · Scroll to zoom · Click a screen for logs",
19
+ "复位视角": "Reset view",
20
+ "缩小": "Zoom out",
21
+ "放大": "Zoom in",
22
+ "蜂巢流程 3D 场景": "Hive workflow 3D scene",
23
+ "当前环境不支持 WebGL,已切换 2D 列表": "WebGL is unavailable here — switched to 2D list",
24
+ "3D 场景不可用,已切换 2D 列表": "3D scene failed — switched to 2D list",
25
+ "点击格子看日志": "click a cell for its log",
26
+ "点击工位看该阶段最新日志": "click the station for its latest log",
27
+ "点击查看实时日志": "Click to view live log",
28
+ "加载日志中…": "Loading log…",
29
+ "协作工作台参考场景": "Collaborative workbench reference scene",
30
+ "阶段 {0} · {1}": "Stage {0} · {1}",
31
+ "{0} / {1} 步骤": "{0} / {1} steps",
32
+ "等待任务": "Waiting for task",
33
+ "空闲工位": "Idle workstation",
34
+ "待命": "Standby",
35
+ "等待日志输出…": "Waiting for log output…",
36
+ "等待执行": "Waiting to run",
37
+ "(无输出)": "(No output)",
38
+ "上一组工位": "Previous workstation group",
39
+ "下一组工位": "Next workstation group",
13
40
  // —— 短片段(拼接用)——
14
41
  "检查中…": "Checking…",
15
42
  "浏览器": "Browser",
@@ -147,6 +174,7 @@
147
174
  "推荐任务类型": "Recommended task type",
148
175
  "切换类型": "Switch type",
149
176
  "这个需求更适合「{0}」流程,能使用对应的规划与质量门禁。是否切换后执行?": "This request fits the {0} workflow, which provides matching planning and quality gates. Switch before running?",
177
+ "这个需求更适合「{0}」流程,能使用对应的专用数据链路与产出模板。是否切换后执行?": "This request fits the {0} workflow, which brings its own data pipeline and output template. Switch before running?",
150
178
  // 自动化卡片上的偏好标签(短名与胶囊面同步取用)
151
179
  "自动": "Auto",
152
180
  "快速": "Fast",
@@ -353,7 +381,6 @@
353
381
  "个文件": " files",
354
382
  "本次运行没有产生工作区变更。": "No workspace changes were produced in this run.",
355
383
  "Git 工具": "Git tools",
356
- "进度": "Progress",
357
384
  "运行统计": "Run stats",
358
385
  "完整详情": "Full detail",
359
386
  "收起检查器": "Collapse inspector",
@@ -1198,6 +1225,18 @@
1198
1225
  "还没有评测结果:勾选候选模型后点「开始评测」。": "No bench results yet: tick candidates and hit \"Start bench\".",
1199
1226
  "没有可用模型:先在「模型接入」配置启用了密钥的供应商。": "No candidates available: configure a provider with a key under \"Model Access\" first.",
1200
1227
  "请先勾选至少一个候选模型": "Tick at least one candidate model first",
1228
+ // —— 评测反哺 + 两段式停止 ——
1229
+ "强制停止": "Force stop",
1230
+ " · 停止中…当前样题完成后即停": " · stopping… finishes the current sample first",
1231
+ "已请求停止:当前样题完成后即停(生成调用最长可能等几分钟);等不及再点一次「强制停止」": "Stop requested: the current sample finishes first (a slow generation can take minutes); click \"Force stop\" again to detach immediately",
1232
+ "已强制脱离:界面回到空闲,后台当前调用跑完即自行退出": "Detached: the UI is idle again; the in-flight call exits by itself when done",
1233
+ "评测反哺(实测分影响选路)": "Bench feedback (scores affect routing)",
1234
+ "14 天内实测分作为软信号参与模型链排序(±4.5 封顶,只动排序;健康/冷却/协议等硬约束照常一票否决)": "Scores from the last 14 days act as a soft signal in model-chain ranking (capped at ±4.5; ordering only — health/cooldown/protocol hard gates still veto)",
1235
+ "反哺已关闭:评测只出榜单,不影响选路。": "Feedback off: the bench only publishes the board and does not affect routing.",
1236
+ "反哺生效:": "Feedback on: ",
1237
+ " 个模型加权 · ": " models upweighted · ",
1238
+ " 个降权(据 14 天内实测分)": " downweighted (scores from the last 14 days)",
1239
+ "反哺生效中:暂无 14 天内实测分,跑一轮评测后开始影响排序。": "Feedback on: no scores from the last 14 days yet — run a bench round to start influencing ranking.",
1201
1240
  // —— 流程版本历史 ——
1202
1241
  "历史": "History",
1203
1242
  "当前": "current",
@@ -1246,12 +1285,9 @@
1246
1285
  "其他": "Other",
1247
1286
  "协议 · ": "protocol · ",
1248
1287
  "墨金": "Ink & Gold",
1249
- "经典": "Classic",
1288
+ "简白": "Paper",
1250
1289
  "深海": "Deep sea",
1251
1290
  "森野": "Forest",
1252
- "暖阳": "Amber",
1253
- "霓虹": "Neon",
1254
- "高对比": "High contrast",
1255
1291
  "夜间": "Dark",
1256
1292
  "日间": "Light",
1257
1293
  "暖墨底色 + 金色发丝线,Hermes 式古典优雅": "Warm-ink base with gold hairlines — Hermes-style classical elegance",
@@ -1319,7 +1355,6 @@
1319
1355
  "刚刚更新 ": "updated ",
1320
1356
  "停在底部自动跟随新输出;上翻回看时不动": "Stay at the bottom to follow new output; scrolling up freezes the view",
1321
1357
  "共 ": "Total ",
1322
- " 个文件": " files",
1323
1358
  "(仅显示最近 200 个)": " (showing only the latest 200)",
1324
1359
  "删除所选": "Delete selected",
1325
1360
  "条;另有 ": " removed; another ",
@@ -1436,7 +1471,6 @@
1436
1471
  " → <b>": " → <b>",
1437
1472
  "(已是最新版本,无需升级)": " (already up to date — no upgrade needed)",
1438
1473
  "(等待输出…安装/下载阶段可能有一段静默期)": " (waiting for output… install/download may be silent for a while)",
1439
- "(无输出)": " (no output)",
1440
1474
  "(尚无输出)": " (no output yet)",
1441
1475
  "完整运行": "Full run",
1442
1476
  "编辑供应商配置(地址 / 协议 / 难度模型)": "Edit provider config (endpoint / protocol / difficulty models)",
@@ -1562,6 +1596,27 @@
1562
1596
  "创建一个按计划自动运行的任务": "Create a task that runs on a schedule",
1563
1597
  "重新拉取任务列表": "Reload the task list",
1564
1598
  "刷新": "Refresh",
1599
+ "文件检查点": "File checkpoints",
1600
+ "查看检查点": "View checkpoints",
1601
+ "恢复文件": "Restore files",
1602
+ "项目记忆治理": "Project memory review",
1603
+ "刷新记忆": "Refresh memory",
1604
+ "批准": "Approve",
1605
+ "拒绝": "Reject",
1606
+ "项目记忆": "Project memory",
1607
+ "已过期": "Expired",
1608
+ "暂无项目记忆": "No project memory entries",
1609
+ "正在加载项目记忆…": "Loading project memory…",
1610
+ "加载项目记忆失败:": "Could not load project memory: ",
1611
+ "正在读取文件检查点…": "Loading file checkpoints…",
1612
+ "此运行没有文件快照": "This run has no file snapshots",
1613
+ "可恢复": "Restorable",
1614
+ "检查点读取失败:": "Could not load checkpoints: ",
1615
+ "恢复会覆盖检查点记录的文件,shell、MCP 和外部服务副作用不保证可回滚。确定继续?": "Restore overwrites files recorded by the checkpoint. Shell, MCP, and external-service side effects are not guaranteed to roll back. Continue?",
1616
+ "已恢复 ": "Restored ",
1617
+ " 个文件": " files",
1618
+ "冲突 ": " conflicts: ",
1619
+ "项目记忆操作失败:": "Project-memory action failed: ",
1565
1620
  "任务筛选": "Filter tasks",
1566
1621
  "定时任务模板": "Scheduled task templates",
1567
1622
  "每天 ": "Every day at ",
@@ -1753,7 +1808,6 @@
1753
1808
  "工具": "Tool",
1754
1809
  "角色": "Role",
1755
1810
  "智能体": "Agent",
1756
- "模型": "Model",
1757
1811
  "模型:": "Model: ",
1758
1812
  "电脑": "Computer",
1759
1813
  "接管": "Take over",
@@ -2200,6 +2254,7 @@
2200
2254
  "已配置代码版本隔离,但任务分支尚未创建(检出失败见运行错误)。": "Code-version isolation is configured, but the task branch has not been created yet (see run error if checkout failed).",
2201
2255
  "藏青底色 + 天蓝强调,夜间长时间盯任务更沉静(默认)": "Navy base + sky-blue accent; calmer for long night sessions (default)",
2202
2256
  "黑白灰 + 蓝色强调,ChatGPT 式清爽配色": "Black/white/gray + blue accent — the clean ChatGPT-style palette",
2257
+ "纯白画布 + 浅灰分区 + 克制蓝强调,Codex 式极简明亮": "Pure white canvas, light-gray sections, restrained blue — Codex-style minimal clarity",
2203
2258
  "周报晨报生成器守则": "Weekly/Morning Report Generator Rules",
2204
2259
  "提交信息规范 / 分支模型 / 危险操作红线 / 冲突与回滚 / 提交前自检清单": "Commit message conventions / branch model / risky-op red lines / conflict & rollback / pre-commit checklist",
2205
2260
  "提测/评审前的风险过单:边界与异常 / 资源与并发 / 安全 / 兼容与回退": "Pre-review risk triage: boundary & exceptions / resources & concurrency / security / compatibility & rollback",
@@ -2317,6 +2372,15 @@
2317
2372
  "多轮修复未变要停:同一验证失败连续2轮未消除时,停止盲改,切换排查环境、测试夹具、依赖版本或验收命令;记录最小复现。": "Stop when multiple fixes change nothing: when the same verification failure survives two consecutive rounds, stop blind changes and switch to investigating the environment, fixtures, dependency versions or the acceptance command; record a minimal reproduction.",
2318
2373
  "切换前保留失败诊断 / 修复绑定失败证据 / 修复不重复空转": "Keep failure diagnostics before switching / capture bind-failure evidence / no idle repair loops",
2319
2374
 
2375
+ // —— 交流与支持(关于页二维码卡)——
2376
+ "交流与支持": "Community & Support",
2377
+ "GitHub 开源地址": "GitHub repository",
2378
+ "(觉得有用就点个 Star)": " (a Star is much appreciated)",
2379
+ "QQ 交流群(群号 923647047)": "QQ group (ID 923647047)",
2380
+ "微信支付": "WeChat Pay",
2381
+ "支付宝支付": "Alipay",
2382
+ "如果 CodeBee 帮你赚到了稿费、省下了时间,欢迎请作者喝杯咖啡~": "If CodeBee has earned you royalties or saved you time, buying the author a coffee is much appreciated~",
2383
+
2320
2384
  // —— 帮助改进(遥测/诊断包)——
2321
2385
  "帮助改进 CodeBee": "Help Improve CodeBee",
2322
2386
  "发送匿名错误报告(帮助我们发现和修复问题)": "Send anonymous error reports (helps us find and fix issues)",
@@ -2567,7 +2631,7 @@
2567
2631
  "预算": "Budget",
2568
2632
  "级联路由": "Cascade routing",
2569
2633
  "引擎调参(schema 化设置,立即生效)": "Engine tuning (schema-driven settings, effective immediately)",
2570
- "上下文压缩总开关(灰度:与 TUTTI_COMPACTION 任一开启即生效)": "Master switch for context compaction (gray-scale: active when either this or TUTTI_COMPACTION is on)",
2634
+ "上下文压缩总开关(默认开启;可在设置页或用 TUTTI_COMPACTION=0 关闭)": "Master switch for context compaction (enabled by default; disable in settings or with TUTTI_COMPACTION=0)",
2571
2635
  "触发压缩的压力比阈值": "Pressure ratio threshold that triggers compaction",
2572
2636
  "压缩时保留尾部预算(token 估算)": "Tail budget kept when compacting (estimated in tokens)",
2573
2637
  "Goal 续行上限": "Max goal continuation rounds",
@@ -2983,6 +3047,55 @@
2983
3047
  "没有正在运行的任务,新任务在主界面创建后会实时出现在这里": "No running tasks — new ones created in the main UI appear here in real time.",
2984
3048
  "还没有已完成的运行": "No finished runs yet",
2985
3049
  "暂无调用记录": "No calls yet",
3050
+ // —— 大屏改版补词条(2026-10-08:指标副位 / 下钻抽屉 / 换肤)——
3051
+ "今日消耗": "Usage today",
3052
+ "今日运行": "Runs today",
3053
+ "今日零失败": "Zero failures today",
3054
+ "尚无结课运行": "No finished runs yet",
3055
+ "成功率 {0}%": "Success {0}%",
3056
+ "最长等待 {0}": "Longest wait {0}",
3057
+ "队首空闲": "Queue head idle",
3058
+ "无活动": "Idle",
3059
+ "步 {0}/{1}": "steps {0}/{1}",
3060
+ "最近 {0}": "Latest {0}",
3061
+ "次调用": "calls",
3062
+ "7日": "7d",
3063
+ "每 4 秒刷新": "Refreshes every 4s",
3064
+ "蜂群运行中 · {0} 个任务并行": "Hive active · {0} task(s) in parallel",
3065
+ "蜂群待命 · {0} 个任务排队": "Hive standing by · {0} task(s) queued",
3066
+ "服务 {0}/{1} 正常": "Services {0}/{1} nominal",
3067
+ "{0} 个服务异常": "{0} service(s) down",
3068
+ "新任务在主界面创建后会实时出现在这里": "New tasks created in the main UI appear here in real time",
3069
+ "今天还没有已结束的": "Nothing has finished today yet",
3070
+ "等待实时输出…": "Waiting for live output…",
3071
+ "蜂巢槽位": "Hive slots",
3072
+ "对话槽位": "Chat slots",
3073
+ "空闲槽位": "Free slots",
3074
+ "开始": "Started",
3075
+ "已运行": "Elapsed",
3076
+ "进度": "Progress",
3077
+ "预估": "Estimate",
3078
+ "执行体": "Executor",
3079
+ "模型": "Model",
3080
+ "厂商": "Provider",
3081
+ "消耗": "Usage",
3082
+ "无": "None",
3083
+ "步骤日志": "Step log",
3084
+ "加载中": "Loading",
3085
+ "暂无步骤": "No steps",
3086
+ "该步没有日志文件": "This step has no log file",
3087
+ "(日志为空)": "(log empty)",
3088
+ "日志读取失败": "Failed to read log",
3089
+ "等待": "Waiting",
3090
+ "经典": "Classic",
3091
+ "森林": "Forest",
3092
+ "暖阳": "Amber",
3093
+ "霓虹": "Neon",
3094
+ "高对比": "High contrast",
3095
+ "夜": "Night",
3096
+ "切换皮肤(仅大屏记忆)": "Switch skin (board only)",
3097
+ "切换明暗(仅大屏记忆)": "Toggle light/dark (board only)",
3098
+ "关闭 (Esc)": "Close (Esc)",
2986
3099
  // —— EN 词条收尾补全(2026-10-06 巡检 G-② 残件 7 条:本地插件/自定义样题/报告导出/浏览器面板;133 字 webhooks 键词条早已在位,文件级 grep 误报勿重加)——
2987
3100
  "本地插件": "Local plugins",
2988
3101
  "本地插件遵循统一 manifest 规范。技能会接入经验库;MCP 只在启用后合并到内置智能体工具,不执行插件脚本或钩子。": "Local plugins follow a unified manifest spec. Skills feed the experience library; MCP tools merge into the built-in agent's toolkit only once enabled — plugin scripts or hooks are never executed.",
@@ -3141,6 +3254,9 @@
3141
3254
  scope.querySelectorAll("[data-i18n-aria]").forEach((el) => {
3142
3255
  el.setAttribute("aria-label", t(el.dataset.i18nAria));
3143
3256
  });
3257
+ scope.querySelectorAll("[data-i18n-alt]").forEach((el) => {
3258
+ el.setAttribute("alt", t(el.dataset.i18nAlt));
3259
+ });
3144
3260
  // 编排 pill 短名取自 option 文本,重译后要跟着换语言
3145
3261
  if (typeof window.syncCmpSelFace === "function") {
3146
3262
  // au-* 是自动化弹框里的偏好胶囊:弹框未开时这两个 id 不在 DOM 里,
@@ -1,79 +1,79 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
- <!-- CodeBee 写实小蜂:琥珀黑条纹腹 + 绒毛胸背 + 半透明翅脉 + 扇翅动画。
3
- 经 <img> 引用(独立文档,渐变 id 互不串扰);翅动媒体查询随系统 reduce-motion 关停。 -->
4
- <defs>
5
- <linearGradient id="cbAbd" x1="0" y1="0" x2="0" y2="1">
6
- <stop offset="0" stop-color="#ffcd66"/>
7
- <stop offset=".55" stop-color="#f0a92c"/>
8
- <stop offset="1" stop-color="#c67c12"/>
9
- </linearGradient>
10
- <radialGradient id="cbThx" cx=".38" cy=".3" r="1.05">
11
- <stop offset="0" stop-color="#7a5a26"/>
12
- <stop offset=".6" stop-color="#3d2c10"/>
13
- <stop offset="1" stop-color="#1f1505"/>
14
- </radialGradient>
15
- <linearGradient id="cbWing" x1="0" y1="0" x2="1" y2="1">
16
- <stop offset="0" stop-color="#ffffff" stop-opacity=".85"/>
17
- <stop offset="1" stop-color="#cfe0f5" stop-opacity=".16"/>
18
- </linearGradient>
19
- <clipPath id="cbAbdClip"><ellipse cx="32" cy="44" rx="11.5" ry="13.5"/></clipPath>
20
- </defs>
21
- <style>
22
- .wingL, .wingR { transform-box: view-box; }
23
- .wingL { transform-origin: 27px 21px; animation: cbFlapL .16s ease-in-out infinite alternate; }
24
- .wingR { transform-origin: 37px 21px; animation: cbFlapR .16s ease-in-out infinite alternate; }
25
- @keyframes cbFlapL { from { transform: rotate(-16deg); } to { transform: rotate(9deg); } }
26
- @keyframes cbFlapR { from { transform: rotate(16deg); } to { transform: rotate(-9deg); } }
27
- @media (prefers-reduced-motion: reduce) { .wingL, .wingR { animation: none; } }
28
- </style>
29
-
30
- <!-- 触角 -->
31
- <path d="M29.5 8C27.5 4.6 24.6 3 21.8 3.6" fill="none" stroke="#241a05" stroke-width="1.4" stroke-linecap="round"/>
32
- <path d="M34.5 8c2-3.4 4.9-5 7.7-4.4" fill="none" stroke="#241a05" stroke-width="1.4" stroke-linecap="round"/>
33
- <circle cx="21.6" cy="3.7" r="1.5" fill="#241a05"/>
34
- <circle cx="42.4" cy="3.7" r="1.5" fill="#241a05"/>
35
-
36
- <!-- 左翅(前后两片,扇翅绕胸侧基点) -->
37
- <g class="wingL">
38
- <ellipse cx="16.5" cy="19.5" rx="11.5" ry="4.8" transform="rotate(-34 26 20.5)"
39
- fill="url(#cbWing)" stroke="#e8f1fb" stroke-opacity=".65" stroke-width=".7"/>
40
- <ellipse cx="20" cy="24.5" rx="8.2" ry="3.5" transform="rotate(-22 26.5 24.5)"
41
- fill="url(#cbWing)" stroke="#e8f1fb" stroke-opacity=".55" stroke-width=".6"/>
42
- <g transform="rotate(-34 26 20.5)" fill="none" stroke="#a9c1dd" stroke-opacity=".4" stroke-width=".5">
43
- <path d="M26 20.5 8 15.5"/><path d="M26 21.5 7.5 20.5"/>
44
- </g>
45
- </g>
46
- <!-- 右翅 -->
47
- <g class="wingR">
48
- <ellipse cx="47.5" cy="19.5" rx="11.5" ry="4.8" transform="rotate(34 38 20.5)"
49
- fill="url(#cbWing)" stroke="#e8f1fb" stroke-opacity=".65" stroke-width=".7"/>
50
- <ellipse cx="44" cy="24.5" rx="8.2" ry="3.5" transform="rotate(22 37.5 24.5)"
51
- fill="url(#cbWing)" stroke="#e8f1fb" stroke-opacity=".55" stroke-width=".6"/>
52
- <g transform="rotate(34 38 20.5)" fill="none" stroke="#a9c1dd" stroke-opacity=".4" stroke-width=".5">
53
- <path d="M38 20.5 56 15.5"/><path d="M38 21.5 56.5 20.5"/>
54
- </g>
55
- </g>
56
-
57
- <!-- 腹部:琥珀底 + 黑条纹 + 高光 + 螫针 -->
58
- <ellipse cx="32" cy="44" rx="11.5" ry="13.5" fill="url(#cbAbd)"/>
59
- <g clip-path="url(#cbAbdClip)" fill="#241a05">
60
- <path d="M20 40.5 Q32 36.5 44 40.5 L44 46.5 Q32 42.5 20 46.5 Z"/>
61
- <path d="M20.5 49 Q32 45.5 43.5 49 L43.2 54.5 Q32 51.5 20.8 54.5 Z" opacity=".92"/>
62
- </g>
63
- <path d="M25.2 36q-2.6 4-2 9.5" stroke="#ffe9ad" stroke-opacity=".7" stroke-width="1.6" stroke-linecap="round" fill="none"/>
64
- <ellipse cx="32" cy="44" rx="11.5" ry="13.5" fill="none" stroke="#8a5a10" stroke-opacity=".35" stroke-width=".8"/>
65
- <path d="M32 58.5 29.8 54h4.4Z" fill="#241a05"/>
66
-
67
- <!-- 胸背:绒毛渐变 + 虚线描边模拟绒毛缘 -->
68
- <ellipse cx="32" cy="25.5" rx="9.2" ry="9.8" fill="url(#cbThx)"/>
69
- <ellipse cx="32" cy="25.5" rx="9.2" ry="9.8" fill="none" stroke="#8a6a2e" stroke-opacity=".45"
70
- stroke-width="1" stroke-dasharray="1.2 1.6"/>
71
-
72
- <!-- 头:复眼 + 高光 -->
73
- <circle cx="32" cy="12.8" r="6.3" fill="#241a05"/>
74
- <ellipse cx="28" cy="12.6" rx="2.4" ry="3" fill="#0e0a03"/>
75
- <ellipse cx="36" cy="12.6" rx="2.4" ry="3" fill="#0e0a03"/>
76
- <circle cx="28.9" cy="11.3" r=".7" fill="#7d6538" opacity=".8"/>
77
- <circle cx="36.9" cy="11.3" r=".7" fill="#7d6538" opacity=".8"/>
78
- <path d="M28.5 8.6q3.5-1.6 7 0" stroke="#8a6a2e" stroke-opacity=".55" stroke-width=".8" fill="none"/>
79
- </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <!-- CodeBee 写实小蜂:琥珀黑条纹腹 + 绒毛胸背 + 半透明翅脉 + 扇翅动画。
3
+ 经 <img> 引用(独立文档,渐变 id 互不串扰);翅动媒体查询随系统 reduce-motion 关停。 -->
4
+ <defs>
5
+ <linearGradient id="cbAbd" x1="0" y1="0" x2="0" y2="1">
6
+ <stop offset="0" stop-color="#ffcd66"/>
7
+ <stop offset=".55" stop-color="#f0a92c"/>
8
+ <stop offset="1" stop-color="#c67c12"/>
9
+ </linearGradient>
10
+ <radialGradient id="cbThx" cx=".38" cy=".3" r="1.05">
11
+ <stop offset="0" stop-color="#7a5a26"/>
12
+ <stop offset=".6" stop-color="#3d2c10"/>
13
+ <stop offset="1" stop-color="#1f1505"/>
14
+ </radialGradient>
15
+ <linearGradient id="cbWing" x1="0" y1="0" x2="1" y2="1">
16
+ <stop offset="0" stop-color="#ffffff" stop-opacity=".85"/>
17
+ <stop offset="1" stop-color="#cfe0f5" stop-opacity=".16"/>
18
+ </linearGradient>
19
+ <clipPath id="cbAbdClip"><ellipse cx="32" cy="44" rx="11.5" ry="13.5"/></clipPath>
20
+ </defs>
21
+ <style>
22
+ .wingL, .wingR { transform-box: view-box; }
23
+ .wingL { transform-origin: 27px 21px; animation: cbFlapL .16s ease-in-out infinite alternate; }
24
+ .wingR { transform-origin: 37px 21px; animation: cbFlapR .16s ease-in-out infinite alternate; }
25
+ @keyframes cbFlapL { from { transform: rotate(-16deg); } to { transform: rotate(9deg); } }
26
+ @keyframes cbFlapR { from { transform: rotate(16deg); } to { transform: rotate(-9deg); } }
27
+ @media (prefers-reduced-motion: reduce) { .wingL, .wingR { animation: none; } }
28
+ </style>
29
+
30
+ <!-- 触角 -->
31
+ <path d="M29.5 8C27.5 4.6 24.6 3 21.8 3.6" fill="none" stroke="#241a05" stroke-width="1.4" stroke-linecap="round"/>
32
+ <path d="M34.5 8c2-3.4 4.9-5 7.7-4.4" fill="none" stroke="#241a05" stroke-width="1.4" stroke-linecap="round"/>
33
+ <circle cx="21.6" cy="3.7" r="1.5" fill="#241a05"/>
34
+ <circle cx="42.4" cy="3.7" r="1.5" fill="#241a05"/>
35
+
36
+ <!-- 左翅(前后两片,扇翅绕胸侧基点) -->
37
+ <g class="wingL">
38
+ <ellipse cx="16.5" cy="19.5" rx="11.5" ry="4.8" transform="rotate(-34 26 20.5)"
39
+ fill="url(#cbWing)" stroke="#e8f1fb" stroke-opacity=".65" stroke-width=".7"/>
40
+ <ellipse cx="20" cy="24.5" rx="8.2" ry="3.5" transform="rotate(-22 26.5 24.5)"
41
+ fill="url(#cbWing)" stroke="#e8f1fb" stroke-opacity=".55" stroke-width=".6"/>
42
+ <g transform="rotate(-34 26 20.5)" fill="none" stroke="#a9c1dd" stroke-opacity=".4" stroke-width=".5">
43
+ <path d="M26 20.5 8 15.5"/><path d="M26 21.5 7.5 20.5"/>
44
+ </g>
45
+ </g>
46
+ <!-- 右翅 -->
47
+ <g class="wingR">
48
+ <ellipse cx="47.5" cy="19.5" rx="11.5" ry="4.8" transform="rotate(34 38 20.5)"
49
+ fill="url(#cbWing)" stroke="#e8f1fb" stroke-opacity=".65" stroke-width=".7"/>
50
+ <ellipse cx="44" cy="24.5" rx="8.2" ry="3.5" transform="rotate(22 37.5 24.5)"
51
+ fill="url(#cbWing)" stroke="#e8f1fb" stroke-opacity=".55" stroke-width=".6"/>
52
+ <g transform="rotate(34 38 20.5)" fill="none" stroke="#a9c1dd" stroke-opacity=".4" stroke-width=".5">
53
+ <path d="M38 20.5 56 15.5"/><path d="M38 21.5 56.5 20.5"/>
54
+ </g>
55
+ </g>
56
+
57
+ <!-- 腹部:琥珀底 + 黑条纹 + 高光 + 螫针 -->
58
+ <ellipse cx="32" cy="44" rx="11.5" ry="13.5" fill="url(#cbAbd)"/>
59
+ <g clip-path="url(#cbAbdClip)" fill="#241a05">
60
+ <path d="M20 40.5 Q32 36.5 44 40.5 L44 46.5 Q32 42.5 20 46.5 Z"/>
61
+ <path d="M20.5 49 Q32 45.5 43.5 49 L43.2 54.5 Q32 51.5 20.8 54.5 Z" opacity=".92"/>
62
+ </g>
63
+ <path d="M25.2 36q-2.6 4-2 9.5" stroke="#ffe9ad" stroke-opacity=".7" stroke-width="1.6" stroke-linecap="round" fill="none"/>
64
+ <ellipse cx="32" cy="44" rx="11.5" ry="13.5" fill="none" stroke="#8a5a10" stroke-opacity=".35" stroke-width=".8"/>
65
+ <path d="M32 58.5 29.8 54h4.4Z" fill="#241a05"/>
66
+
67
+ <!-- 胸背:绒毛渐变 + 虚线描边模拟绒毛缘 -->
68
+ <ellipse cx="32" cy="25.5" rx="9.2" ry="9.8" fill="url(#cbThx)"/>
69
+ <ellipse cx="32" cy="25.5" rx="9.2" ry="9.8" fill="none" stroke="#8a6a2e" stroke-opacity=".45"
70
+ stroke-width="1" stroke-dasharray="1.2 1.6"/>
71
+
72
+ <!-- 头:复眼 + 高光 -->
73
+ <circle cx="32" cy="12.8" r="6.3" fill="#241a05"/>
74
+ <ellipse cx="28" cy="12.6" rx="2.4" ry="3" fill="#0e0a03"/>
75
+ <ellipse cx="36" cy="12.6" rx="2.4" ry="3" fill="#0e0a03"/>
76
+ <circle cx="28.9" cy="11.3" r=".7" fill="#7d6538" opacity=".8"/>
77
+ <circle cx="36.9" cy="11.3" r=".7" fill="#7d6538" opacity=".8"/>
78
+ <path d="M28.5 8.6q3.5-1.6 7 0" stroke="#8a6a2e" stroke-opacity=".55" stroke-width=".8" fill="none"/>
79
+ </svg>