duaer-spec 0.27.2 → 0.27.3
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/.duaer/active-job.json +3 -3
- package/.duaer/specs/281-release-0272/delivery.json +8 -0
- package/.duaer/specs/281-release-0272/tasks.md +1 -1
- package/.duaer/specs/282-dispatch-confirm-lock/delivery.json +12 -0
- package/.duaer/specs/282-dispatch-confirm-lock/spec.md +26 -0
- package/.duaer/specs/282-dispatch-confirm-lock/tasks.md +5 -0
- package/.duaer/specs/283-desk-theme-compact/delivery.json +12 -0
- package/.duaer/specs/283-desk-theme-compact/spec.md +26 -0
- package/.duaer/specs/283-desk-theme-compact/tasks.md +5 -0
- package/.duaer/specs/284-dispatch-graph-regen/delivery.json +12 -0
- package/.duaer/specs/284-dispatch-graph-regen/spec.md +33 -0
- package/.duaer/specs/284-dispatch-graph-regen/tasks.md +5 -0
- package/.duaer/specs/285-desk-light-compact/delivery.json +11 -0
- package/.duaer/specs/285-desk-light-compact/spec.md +13 -0
- package/.duaer/specs/285-desk-light-compact/tasks.md +5 -0
- package/.duaer/specs/286-modules-bug-theme/delivery.json +10 -0
- package/.duaer/specs/286-modules-bug-theme/spec.md +26 -0
- package/.duaer/specs/286-modules-bug-theme/tasks.md +5 -0
- package/.duaer/specs/287-progress-modules-ux/delivery.json +10 -0
- package/.duaer/specs/287-progress-modules-ux/spec.md +31 -0
- package/.duaer/specs/287-progress-modules-ux/tasks.md +5 -0
- package/.duaer/specs/288-settings-close-compact/delivery.json +10 -0
- package/.duaer/specs/288-settings-close-compact/spec.md +17 -0
- package/.duaer/specs/288-settings-close-compact/tasks.md +5 -0
- package/.duaer/specs/289-settings-close-click/delivery.json +10 -0
- package/.duaer/specs/289-settings-close-click/spec.md +16 -0
- package/.duaer/specs/289-settings-close-click/tasks.md +4 -0
- package/.duaer/specs/290-remove-quicknav/delivery.json +10 -0
- package/.duaer/specs/290-remove-quicknav/spec.md +11 -0
- package/.duaer/specs/290-remove-quicknav/tasks.md +4 -0
- package/.duaer/specs/291-release-0273/spec.md +12 -0
- package/.duaer/specs/291-release-0273/tasks.md +4 -0
- package/CHANGELOG.md +56 -0
- package/bin/duaer-live.mjs +11 -20
- package/bin/live-modules.mjs +23 -3
- package/docs/agent/e2e-test-plan.md +16 -5
- package/docs/agent/live-desk.md +24 -11
- package/package.json +1 -1
- package/web/live-dev/app.js +329 -167
- package/web/live-dev/dispatch-center.html +29 -1
- package/web/live-dev/dispatch-center.js +25 -1
- package/web/live-dev/i18n-de.js +5 -0
- package/web/live-dev/i18n-es.js +5 -0
- package/web/live-dev/i18n-fr.js +5 -0
- package/web/live-dev/i18n-ja.js +5 -0
- package/web/live-dev/i18n-ko.js +5 -0
- package/web/live-dev/i18n-pt-BR.js +5 -0
- package/web/live-dev/i18n-ru.js +5 -0
- package/web/live-dev/i18n-vi.js +5 -0
- package/web/live-dev/i18n-zh-TW.js +5 -0
- package/web/live-dev/i18n.js +10 -0
- package/web/live-dev/index.html +31 -32
- package/web/live-dev/styles.css +296 -315
- package/web/live-dev/task-graph.mjs +4 -1
- package/web/live-dev/theme.mjs +63 -0
package/web/live-dev/app.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
setLocale,
|
|
11
11
|
applyDomI18n,
|
|
12
12
|
} from "./i18n.js";
|
|
13
|
+
import { getTheme, initTheme, toggleTheme } from "./theme.mjs";
|
|
13
14
|
import { structuredHtml, escapeHtml, reqEditModel, serializeReqEdit } from "./structured-html.mjs";
|
|
14
15
|
import {
|
|
15
16
|
deriveProjectDeliveryStatus,
|
|
@@ -110,6 +111,8 @@ const state = {
|
|
|
110
111
|
dispatchGraphReady: false,
|
|
111
112
|
dispatchGraphBuilding: false,
|
|
112
113
|
dispatchGraphUrl: null,
|
|
114
|
+
/** True after at least one successful graph build this desk session (regen copy). */
|
|
115
|
+
dispatchGraphBuiltOnce: false,
|
|
113
116
|
/** Latest /api/status progress.tasks for dispatch-graph run colors. */
|
|
114
117
|
lastJobProgress: null,
|
|
115
118
|
recommendedWorkerCount: 1,
|
|
@@ -231,9 +234,6 @@ const el = {
|
|
|
231
234
|
settingsClose: document.getElementById("settingsClose"),
|
|
232
235
|
desk: document.getElementById("desk"),
|
|
233
236
|
log: document.getElementById("log"),
|
|
234
|
-
chatQuickNav: document.getElementById("chatQuickNav"),
|
|
235
|
-
chatQuickNavMenu: document.getElementById("chatQuickNavMenu"),
|
|
236
|
-
chatQuickNavWrap: document.getElementById("chatQuickNavWrap"),
|
|
237
237
|
chatEmpty: document.getElementById("chatEmpty"),
|
|
238
238
|
form: document.getElementById("composer"),
|
|
239
239
|
input: document.getElementById("input"),
|
|
@@ -448,6 +448,8 @@ const el = {
|
|
|
448
448
|
cardPanel: document.querySelector(".card-panel"),
|
|
449
449
|
updateNotice: document.getElementById("updateNotice"),
|
|
450
450
|
langSelect: document.getElementById("langSelect"),
|
|
451
|
+
themeToggle: document.getElementById("themeToggle"),
|
|
452
|
+
themeToggleLabel: document.getElementById("themeToggleLabel"),
|
|
451
453
|
historyToggle: document.getElementById("historyToggle"),
|
|
452
454
|
employeeToggle: document.getElementById("employeeToggle"),
|
|
453
455
|
employeePanel: document.getElementById("employeePanel"),
|
|
@@ -592,69 +594,6 @@ function scrollChatToLatest() {
|
|
|
592
594
|
el.log.scrollTop = el.log.scrollHeight;
|
|
593
595
|
}
|
|
594
596
|
|
|
595
|
-
function setChatQuickNavOpen(open) {
|
|
596
|
-
if (!el.chatQuickNav || !el.chatQuickNavMenu) return;
|
|
597
|
-
el.chatQuickNav.setAttribute("aria-expanded", open ? "true" : "false");
|
|
598
|
-
el.chatQuickNavMenu.hidden = !open;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function quickNavTarget(kind) {
|
|
602
|
-
if (kind === "chat") return el.log;
|
|
603
|
-
if (kind === "card") {
|
|
604
|
-
if (el.revisePanel && !el.revisePanel.hidden) return el.revisePanel;
|
|
605
|
-
return el.confirm || el.cardPanel;
|
|
606
|
-
}
|
|
607
|
-
if (kind === "result") {
|
|
608
|
-
if (el.previewPanel && !el.previewPanel.hidden) return el.previewPanel;
|
|
609
|
-
if (el.revisePanel && !el.revisePanel.hidden) return el.revisePanel;
|
|
610
|
-
return el.confirm || el.cardPanel;
|
|
611
|
-
}
|
|
612
|
-
if (kind === "progress") {
|
|
613
|
-
return activeProgressFocusEl() || el.progressCol;
|
|
614
|
-
}
|
|
615
|
-
return null;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
function jumpQuickNav(kind) {
|
|
619
|
-
setChatQuickNavOpen(false);
|
|
620
|
-
if (kind === "chat") {
|
|
621
|
-
clearPanelUserScroll(el.log);
|
|
622
|
-
scrollChatToLatest();
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
const target = quickNavTarget(kind);
|
|
626
|
-
if (!target) return;
|
|
627
|
-
if (kind === "progress") {
|
|
628
|
-
clearPanelUserScroll(el.progressCol);
|
|
629
|
-
scrollPanelToTarget(el.progressCol, target, { smooth: true, force: true });
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
clearPanelUserScroll(el.cardPanel);
|
|
633
|
-
scrollPanelToTarget(el.cardPanel, target, { smooth: true, force: true });
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function wireChatQuickNav() {
|
|
637
|
-
if (!el.chatQuickNav || el.chatQuickNav.dataset.bound === "1") return;
|
|
638
|
-
el.chatQuickNav.dataset.bound = "1";
|
|
639
|
-
el.chatQuickNav.addEventListener("click", (ev) => {
|
|
640
|
-
ev.stopPropagation();
|
|
641
|
-
const open = el.chatQuickNav.getAttribute("aria-expanded") !== "true";
|
|
642
|
-
setChatQuickNavOpen(open);
|
|
643
|
-
});
|
|
644
|
-
el.chatQuickNavMenu?.addEventListener("click", (ev) => {
|
|
645
|
-
const btn = ev.target?.closest?.("[data-nav]");
|
|
646
|
-
if (!btn) return;
|
|
647
|
-
ev.preventDefault();
|
|
648
|
-
jumpQuickNav(btn.getAttribute("data-nav"));
|
|
649
|
-
});
|
|
650
|
-
document.addEventListener("click", (ev) => {
|
|
651
|
-
if (!el.chatQuickNavWrap?.contains(ev.target)) setChatQuickNavOpen(false);
|
|
652
|
-
});
|
|
653
|
-
document.addEventListener("keydown", (ev) => {
|
|
654
|
-
if (ev.key === "Escape") setChatQuickNavOpen(false);
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
|
|
658
597
|
function syncChatPlaceholder() {
|
|
659
598
|
if (!el.input) return;
|
|
660
599
|
el.input.placeholder =
|
|
@@ -888,7 +827,21 @@ function explainChatBlocked() {
|
|
|
888
827
|
addBubble("bot", msg);
|
|
889
828
|
}
|
|
890
829
|
|
|
830
|
+
function syncThemeToggleUi() {
|
|
831
|
+
const theme = getTheme();
|
|
832
|
+
if (el.themeToggle) {
|
|
833
|
+
el.themeToggle.dataset.theme = theme;
|
|
834
|
+
el.themeToggle.setAttribute("aria-pressed", theme === "dark" ? "true" : "false");
|
|
835
|
+
el.themeToggle.setAttribute("aria-label", t("theme.aria"));
|
|
836
|
+
}
|
|
837
|
+
if (el.themeToggleLabel) {
|
|
838
|
+
el.themeToggleLabel.textContent =
|
|
839
|
+
theme === "dark" ? t("theme.toLight") : t("theme.toDark");
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
891
843
|
function syncDynamicI18n() {
|
|
844
|
+
syncThemeToggleUi();
|
|
892
845
|
for (const p of state.providers) {
|
|
893
846
|
if (p.id === "custom") p.label = t("provider.custom");
|
|
894
847
|
}
|
|
@@ -1019,7 +972,8 @@ function renderModuleTabs() {
|
|
|
1019
972
|
if (!el.moduleTabs) return;
|
|
1020
973
|
ensureModulesSeed();
|
|
1021
974
|
const list = state.modules;
|
|
1022
|
-
|
|
975
|
+
// Keep tabs visible in bug mode so prior requirement modules stay reachable.
|
|
976
|
+
el.moduleTabs.hidden = list.length < 1;
|
|
1023
977
|
el.moduleTabs.replaceChildren();
|
|
1024
978
|
for (const m of list) {
|
|
1025
979
|
const btn = document.createElement("button");
|
|
@@ -1031,20 +985,24 @@ function renderModuleTabs() {
|
|
|
1031
985
|
btn.setAttribute("aria-selected", active ? "true" : "false");
|
|
1032
986
|
if (active) btn.classList.add("is-active");
|
|
1033
987
|
if (m.status === "confirmed") btn.classList.add("is-confirmed");
|
|
988
|
+
if (m.id === "bug") btn.classList.add("is-bug");
|
|
1034
989
|
btn.textContent = `${m.title} · ${statusLabel(m.status)}`;
|
|
1035
990
|
btn.addEventListener("click", () => {
|
|
1036
991
|
if (m.id === state.activeModuleId) return;
|
|
1037
992
|
syncActiveModuleCardFromFields();
|
|
1038
993
|
state.activeModuleId = m.id;
|
|
1039
994
|
applyActiveModuleToFields();
|
|
995
|
+
applyConfirmCardChrome();
|
|
1040
996
|
resetValidateGate();
|
|
1041
997
|
setConfirmFieldsReadonly(
|
|
1042
|
-
m.status === "confirmed" ||
|
|
998
|
+
m.status === "confirmed" ||
|
|
999
|
+
(modulesAllConfirmedLocal() && m.id !== "bug"),
|
|
1043
1000
|
);
|
|
1044
1001
|
renderModuleTabs();
|
|
1045
1002
|
syncConfirmEnabled();
|
|
1046
1003
|
if (
|
|
1047
1004
|
m.status !== "confirmed" &&
|
|
1005
|
+
!(state.deskKind === "bug" && m.id !== "bug") &&
|
|
1048
1006
|
!modulesAllConfirmedLocal() &&
|
|
1049
1007
|
state.mode !== "revise"
|
|
1050
1008
|
) {
|
|
@@ -1082,18 +1040,21 @@ function mergeModulesFromChatPayload(data) {
|
|
|
1082
1040
|
externalDeps: String(data.externalDeps || data.modules?.[0]?.card?.externalDeps || activeModule()?.card?.externalDeps || ""),
|
|
1083
1041
|
perfBudget: String(data.perfBudget || data.modules?.[0]?.card?.perfBudget || activeModule()?.card?.perfBudget || ""),
|
|
1084
1042
|
};
|
|
1085
|
-
const
|
|
1043
|
+
const kept = (state.modules || []).filter((m) => m.id !== "bug");
|
|
1044
|
+
const prevBug = (state.modules || []).find((m) => m.id === "bug");
|
|
1045
|
+
const bugConfirmed = prevBug?.status === "confirmed";
|
|
1086
1046
|
state.modules = [
|
|
1047
|
+
...kept,
|
|
1087
1048
|
{
|
|
1088
1049
|
id: "bug",
|
|
1089
1050
|
title: t("card.bugModuleTitle"),
|
|
1090
|
-
status:
|
|
1091
|
-
card:
|
|
1051
|
+
status: bugConfirmed ? "confirmed" : "draft",
|
|
1052
|
+
card: bugConfirmed ? prevBug.card : card,
|
|
1092
1053
|
dependsOn: [],
|
|
1093
1054
|
},
|
|
1094
1055
|
];
|
|
1095
1056
|
state.activeModuleId = "bug";
|
|
1096
|
-
if (
|
|
1057
|
+
if (!bugConfirmed) applyActiveModuleToFields();
|
|
1097
1058
|
renderModuleTabs();
|
|
1098
1059
|
return;
|
|
1099
1060
|
}
|
|
@@ -1268,25 +1229,81 @@ function renderTaskPoolList(tasks) {
|
|
|
1268
1229
|
}
|
|
1269
1230
|
}
|
|
1270
1231
|
|
|
1271
|
-
function
|
|
1272
|
-
|
|
1273
|
-
|
|
1232
|
+
function syncConfirmWorkersGraphButton() {
|
|
1233
|
+
const btn = el.confirmWorkersGraph;
|
|
1234
|
+
if (!btn) return;
|
|
1235
|
+
const busy = Boolean(state.dispatchGraphBuilding);
|
|
1236
|
+
const locked = Boolean(state.dispatchGraphReady);
|
|
1237
|
+
const regen =
|
|
1238
|
+
!locked &&
|
|
1239
|
+
!busy &&
|
|
1240
|
+
Boolean(state.dispatchGraphBuiltOnce);
|
|
1241
|
+
btn.disabled = busy || locked;
|
|
1242
|
+
btn.setAttribute("aria-busy", busy ? "true" : "false");
|
|
1243
|
+
btn.classList.toggle("is-busy", busy);
|
|
1244
|
+
btn.classList.toggle("is-locked", locked && !busy);
|
|
1245
|
+
btn.classList.toggle("is-regen", regen);
|
|
1246
|
+
if (busy) {
|
|
1247
|
+
btn.textContent = t("dispatch.graphBuilding");
|
|
1248
|
+
} else if (locked) {
|
|
1249
|
+
btn.textContent = t("dispatch.confirmWorkersLocked");
|
|
1250
|
+
} else if (regen) {
|
|
1251
|
+
btn.textContent = t("dispatch.regenWorkersGraph");
|
|
1252
|
+
} else {
|
|
1253
|
+
btn.textContent = t("dispatch.confirmWorkersGraph");
|
|
1254
|
+
}
|
|
1274
1255
|
}
|
|
1275
1256
|
|
|
1276
|
-
function
|
|
1277
|
-
if (
|
|
1278
|
-
|
|
1279
|
-
return;
|
|
1257
|
+
function syncOpenTaskGraphButton() {
|
|
1258
|
+
if (el.openTaskGraph) {
|
|
1259
|
+
el.openTaskGraph.disabled = !state.dispatchGraphReady;
|
|
1280
1260
|
}
|
|
1261
|
+
syncConfirmWorkersGraphButton();
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/**
|
|
1265
|
+
* Drop the current 派工图 so the operator must confirm+build again.
|
|
1266
|
+
* @param {{ silent?: boolean, hidePanel?: boolean }} [opts]
|
|
1267
|
+
*/
|
|
1268
|
+
function clearDispatchGraphState(opts = {}) {
|
|
1269
|
+
const silent = Boolean(opts.silent);
|
|
1270
|
+
const hidePanel = Boolean(opts.hidePanel);
|
|
1281
1271
|
state.dispatchGraphReady = false;
|
|
1272
|
+
state.dispatchGraphBuilding = false;
|
|
1273
|
+
state.dispatchGraphUrl = null;
|
|
1274
|
+
if (hidePanel && el.dispatch) el.dispatch.hidden = true;
|
|
1282
1275
|
syncOpenTaskGraphButton();
|
|
1283
|
-
|
|
1276
|
+
renderWorkerCountList();
|
|
1277
|
+
if (!silent && el.dispatchErr && state.dispatchGraphBuiltOnce) {
|
|
1284
1278
|
el.dispatchErr.hidden = false;
|
|
1285
1279
|
el.dispatchErr.textContent = t("dispatch.graphStale");
|
|
1286
1280
|
}
|
|
1287
1281
|
schedulePersistProjectDesk();
|
|
1288
1282
|
}
|
|
1289
1283
|
|
|
1284
|
+
/**
|
|
1285
|
+
* Start a new kickoff wave: fresh task pool + new 派工图 required.
|
|
1286
|
+
* @param {{ hidePanel?: boolean, silent?: boolean }} [opts]
|
|
1287
|
+
*/
|
|
1288
|
+
function resetDispatchGraphForNewWave(opts = {}) {
|
|
1289
|
+
state.taskPool = null;
|
|
1290
|
+
if (el.taskPoolList) el.taskPoolList.replaceChildren();
|
|
1291
|
+
if (el.taskPoolEmpty) el.taskPoolEmpty.hidden = false;
|
|
1292
|
+
clearDispatchGraphState({
|
|
1293
|
+
silent: opts.silent !== false,
|
|
1294
|
+
hidePanel: Boolean(opts.hidePanel),
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
function markDispatchGraphStale() {
|
|
1299
|
+
if (!state.dispatchGraphReady && !state.dispatchGraphUrl) {
|
|
1300
|
+
syncOpenTaskGraphButton();
|
|
1301
|
+
renderWorkerCountList();
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
clearDispatchGraphState({ silent: false, hidePanel: false });
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1290
1307
|
function applyRecommendedWorkerCount(tasks) {
|
|
1291
1308
|
const rec = recommendWorkerCount(tasks, { max: 4 });
|
|
1292
1309
|
state.recommendedWorkerCount = rec;
|
|
@@ -1304,12 +1321,15 @@ function renderWorkerCountList() {
|
|
|
1304
1321
|
el.workerCountList.replaceChildren();
|
|
1305
1322
|
const cur = Math.max(1, Math.min(4, Number(state.workerCount) || 1));
|
|
1306
1323
|
state.workerCount = cur;
|
|
1324
|
+
const chipsLocked =
|
|
1325
|
+
Boolean(state.dispatchGraphReady) || Boolean(state.dispatchGraphBuilding);
|
|
1307
1326
|
for (let n = 1; n <= 4; n += 1) {
|
|
1308
1327
|
const b = document.createElement("button");
|
|
1309
1328
|
b.type = "button";
|
|
1310
1329
|
b.className = "worker-count-chip";
|
|
1311
1330
|
b.setAttribute("aria-pressed", n === cur ? "true" : "false");
|
|
1312
1331
|
b.setAttribute("aria-label", String(n));
|
|
1332
|
+
b.disabled = chipsLocked;
|
|
1313
1333
|
const num = document.createElement("span");
|
|
1314
1334
|
num.textContent = String(n);
|
|
1315
1335
|
b.appendChild(num);
|
|
@@ -1323,6 +1343,7 @@ function renderWorkerCountList() {
|
|
|
1323
1343
|
sub.textContent = label;
|
|
1324
1344
|
b.appendChild(sub);
|
|
1325
1345
|
b.addEventListener("click", () => {
|
|
1346
|
+
if (chipsLocked) return;
|
|
1326
1347
|
if (state.workerCount !== n) {
|
|
1327
1348
|
state.workerCount = n;
|
|
1328
1349
|
markDispatchGraphStale();
|
|
@@ -1333,9 +1354,19 @@ function renderWorkerCountList() {
|
|
|
1333
1354
|
}
|
|
1334
1355
|
}
|
|
1335
1356
|
|
|
1336
|
-
function
|
|
1357
|
+
function confirmedModulesForPool() {
|
|
1337
1358
|
ensureModulesSeed();
|
|
1338
1359
|
const confirmed = state.modules.filter((m) => m.status === "confirmed");
|
|
1360
|
+
if (state.deskKind === "bug") {
|
|
1361
|
+
const bug = confirmed.filter((m) => m.id === "bug");
|
|
1362
|
+
return bug.length ? bug : confirmed.slice(0, 1);
|
|
1363
|
+
}
|
|
1364
|
+
return confirmed.filter((m) => m.id !== "bug");
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
function decomposeTasksFromModules() {
|
|
1368
|
+
ensureModulesSeed();
|
|
1369
|
+
const confirmed = confirmedModulesForPool();
|
|
1339
1370
|
if (!confirmed.length) {
|
|
1340
1371
|
state.taskPool = null;
|
|
1341
1372
|
renderTaskPoolList([]);
|
|
@@ -1350,9 +1381,7 @@ function decomposeTasksFromModules() {
|
|
|
1350
1381
|
applyRecommendedWorkerCount(annotated);
|
|
1351
1382
|
renderWorkerCountList();
|
|
1352
1383
|
renderTaskPoolList(annotated);
|
|
1353
|
-
|
|
1354
|
-
syncOpenTaskGraphButton();
|
|
1355
|
-
schedulePersistProjectDesk();
|
|
1384
|
+
clearDispatchGraphState({ silent: true, hidePanel: false });
|
|
1356
1385
|
return state.taskPool;
|
|
1357
1386
|
}
|
|
1358
1387
|
|
|
@@ -1363,7 +1392,7 @@ function syncTaskPoolPreview() {
|
|
|
1363
1392
|
async function syncTaskPoolPreviewAsync() {
|
|
1364
1393
|
if (!el.taskPoolPreview) return;
|
|
1365
1394
|
ensureModulesSeed();
|
|
1366
|
-
const confirmed =
|
|
1395
|
+
const confirmed = confirmedModulesForPool();
|
|
1367
1396
|
if (!confirmed.length || !state.locked || !state.architecture?.confirmed) {
|
|
1368
1397
|
if (el.taskPoolEmpty) el.taskPoolEmpty.hidden = false;
|
|
1369
1398
|
renderTaskPoolList([]);
|
|
@@ -1378,20 +1407,14 @@ async function syncTaskPoolPreviewAsync() {
|
|
|
1378
1407
|
}
|
|
1379
1408
|
|
|
1380
1409
|
function setConfirmWorkersGraphBusy(busy) {
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
btn.setAttribute("aria-busy", busy ? "true" : "false");
|
|
1385
|
-
btn.classList.toggle("is-busy", Boolean(busy));
|
|
1386
|
-
if (busy) {
|
|
1387
|
-
btn.textContent = t("dispatch.graphBuilding");
|
|
1388
|
-
} else {
|
|
1389
|
-
btn.textContent = t("dispatch.confirmWorkersGraph");
|
|
1390
|
-
}
|
|
1410
|
+
state.dispatchGraphBuilding = Boolean(busy);
|
|
1411
|
+
syncConfirmWorkersGraphButton();
|
|
1412
|
+
renderWorkerCountList();
|
|
1391
1413
|
}
|
|
1392
1414
|
|
|
1393
1415
|
async function confirmWorkersAndBuildGraph() {
|
|
1394
1416
|
if (state.dispatchGraphBuilding) return;
|
|
1417
|
+
if (state.dispatchGraphReady) return;
|
|
1395
1418
|
ensureModulesSeed();
|
|
1396
1419
|
if (!state.architecture?.confirmed) {
|
|
1397
1420
|
if (el.dispatchErr) {
|
|
@@ -1439,13 +1462,16 @@ async function confirmWorkersAndBuildGraph() {
|
|
|
1439
1462
|
}
|
|
1440
1463
|
state.dispatchGraphUrl = String(data.url || "").trim() || null;
|
|
1441
1464
|
state.dispatchGraphReady = true;
|
|
1465
|
+
state.dispatchGraphBuiltOnce = true;
|
|
1442
1466
|
syncOpenTaskGraphButton();
|
|
1467
|
+
renderWorkerCountList();
|
|
1443
1468
|
schedulePersistProjectDesk();
|
|
1444
1469
|
addBubble("bot", t("dispatch.graphBuilt", { n: String(workerCount) }));
|
|
1445
1470
|
} catch (err) {
|
|
1446
1471
|
state.dispatchGraphReady = false;
|
|
1447
1472
|
state.dispatchGraphUrl = null;
|
|
1448
1473
|
syncOpenTaskGraphButton();
|
|
1474
|
+
renderWorkerCountList();
|
|
1449
1475
|
const msg =
|
|
1450
1476
|
err instanceof Error ? err.message : t("dispatch.graphBuildFail");
|
|
1451
1477
|
if (el.dispatchErr) {
|
|
@@ -1508,12 +1534,7 @@ async function openDispatchGraphPresent() {
|
|
|
1508
1534
|
}
|
|
1509
1535
|
|
|
1510
1536
|
function invalidateDispatchAfterArchChange() {
|
|
1511
|
-
|
|
1512
|
-
state.dispatchGraphBuilding = false;
|
|
1513
|
-
state.dispatchGraphUrl = null;
|
|
1514
|
-
setConfirmWorkersGraphBusy(false);
|
|
1515
|
-
syncOpenTaskGraphButton();
|
|
1516
|
-
if (el.dispatch) el.dispatch.hidden = true;
|
|
1537
|
+
clearDispatchGraphState({ silent: true, hidePanel: true });
|
|
1517
1538
|
}
|
|
1518
1539
|
|
|
1519
1540
|
function openDispatchCenterPage(projectPath) {
|
|
@@ -2048,9 +2069,9 @@ function restoreConfirmCardFromOriginal() {
|
|
|
2048
2069
|
syncReqSections();
|
|
2049
2070
|
}
|
|
2050
2071
|
|
|
2051
|
-
/** Top confirm card chrome never becomes 改进卡. */
|
|
2072
|
+
/** Top confirm card chrome never becomes 改进卡. Labels follow the active tab. */
|
|
2052
2073
|
function applyConfirmCardChrome() {
|
|
2053
|
-
const bug =
|
|
2074
|
+
const bug = activeModule()?.id === "bug";
|
|
2054
2075
|
if (el.cardMark) el.cardMark.textContent = t(bug ? "card.bugMark" : "card.mark");
|
|
2055
2076
|
if (el.cardTitle) el.cardTitle.textContent = t(bug ? "card.bugTitle" : "card.title");
|
|
2056
2077
|
if (el.lblGoal) el.lblGoal.textContent = t(bug ? "card.bugGoal" : "card.goal");
|
|
@@ -2122,6 +2143,52 @@ function syncStartBugFixButtons() {
|
|
|
2122
2143
|
}
|
|
2123
2144
|
}
|
|
2124
2145
|
|
|
2146
|
+
function ensureBugModuleOnDesk() {
|
|
2147
|
+
let kept = (state.modules || []).filter((m) => m && m.id !== "bug");
|
|
2148
|
+
// Recover a readonly requirement tab when an older wipe left only `bug`.
|
|
2149
|
+
if (
|
|
2150
|
+
!kept.length &&
|
|
2151
|
+
state.originalCard &&
|
|
2152
|
+
(state.originalCard.goal || state.originalCard.acceptance)
|
|
2153
|
+
) {
|
|
2154
|
+
kept = [
|
|
2155
|
+
{
|
|
2156
|
+
id: "main",
|
|
2157
|
+
title: "Main",
|
|
2158
|
+
status: "confirmed",
|
|
2159
|
+
card: {
|
|
2160
|
+
goal: String(state.originalCard.goal || ""),
|
|
2161
|
+
outOfScope: String(state.originalCard.outOfScope || ""),
|
|
2162
|
+
acceptance: String(state.originalCard.acceptance || ""),
|
|
2163
|
+
assumptions: String(state.originalCard.assumptions || ""),
|
|
2164
|
+
deviceMatrix: String(state.originalCard.deviceMatrix || ""),
|
|
2165
|
+
criticalPaths: String(state.originalCard.criticalPaths || ""),
|
|
2166
|
+
exceptionCases: String(state.originalCard.exceptionCases || ""),
|
|
2167
|
+
apiContract: String(state.originalCard.apiContract || ""),
|
|
2168
|
+
envChecklist: String(state.originalCard.envChecklist || ""),
|
|
2169
|
+
dataPrecheck: String(state.originalCard.dataPrecheck || ""),
|
|
2170
|
+
externalDeps: String(state.originalCard.externalDeps || ""),
|
|
2171
|
+
perfBudget: String(state.originalCard.perfBudget || ""),
|
|
2172
|
+
},
|
|
2173
|
+
dependsOn: [],
|
|
2174
|
+
},
|
|
2175
|
+
];
|
|
2176
|
+
}
|
|
2177
|
+
let bug = (state.modules || []).find((m) => m?.id === "bug");
|
|
2178
|
+
if (!bug) {
|
|
2179
|
+
bug = {
|
|
2180
|
+
id: "bug",
|
|
2181
|
+
title: t("card.bugModuleTitle"),
|
|
2182
|
+
status: "draft",
|
|
2183
|
+
card: { goal: "", outOfScope: "", acceptance: "", assumptions: "" },
|
|
2184
|
+
dependsOn: [],
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
state.modules = [...kept, bug];
|
|
2188
|
+
state.activeModuleId = "bug";
|
|
2189
|
+
return bug;
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2125
2192
|
function beginBugFixFromCta() {
|
|
2126
2193
|
if (state.busy || state.reviseDispatching) return;
|
|
2127
2194
|
if (!state.projectPath) {
|
|
@@ -2131,13 +2198,16 @@ function beginBugFixFromCta() {
|
|
|
2131
2198
|
const fromDelivery = Boolean(state.locked || state.lastDeliveryAccepted);
|
|
2132
2199
|
if (fromDelivery) {
|
|
2133
2200
|
// Fresh defect card on this project (same spirit as「再改一版」).
|
|
2201
|
+
// Keep prior requirement modules — append a bug tab instead of wiping them.
|
|
2134
2202
|
state.locked = false;
|
|
2135
2203
|
state.lastDeliveryAccepted = false;
|
|
2136
2204
|
state.jobId = null;
|
|
2137
2205
|
state.dispatchPhase = null;
|
|
2138
2206
|
state.taskPool = null;
|
|
2139
2207
|
state.dispatchGraphReady = false;
|
|
2208
|
+
state.dispatchGraphBuilding = false;
|
|
2140
2209
|
state.dispatchGraphUrl = null;
|
|
2210
|
+
state.dispatchGraphBuiltOnce = false;
|
|
2141
2211
|
state.reviseLocked = false;
|
|
2142
2212
|
state.revisePlanConfirmed = false;
|
|
2143
2213
|
state.architecture = {
|
|
@@ -2154,16 +2224,14 @@ function beginBugFixFromCta() {
|
|
|
2154
2224
|
if (el.dispatch) el.dispatch.hidden = true;
|
|
2155
2225
|
setConfirmFieldsReadonly(false);
|
|
2156
2226
|
state.deskKind = "bug";
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
];
|
|
2166
|
-
state.activeModuleId = "bug";
|
|
2227
|
+
ensureModulesSeed();
|
|
2228
|
+
ensureBugModuleOnDesk();
|
|
2229
|
+
const bug = state.modules.find((m) => m.id === "bug");
|
|
2230
|
+
if (bug) {
|
|
2231
|
+
bug.status = "draft";
|
|
2232
|
+
bug.card = { goal: "", outOfScope: "", acceptance: "", assumptions: "" };
|
|
2233
|
+
bug.title = t("card.bugModuleTitle");
|
|
2234
|
+
}
|
|
2167
2235
|
applyActiveModuleToFields();
|
|
2168
2236
|
applyConfirmCardChrome();
|
|
2169
2237
|
renderModuleTabs();
|
|
@@ -2199,45 +2267,47 @@ function isFeatureIntentText(text) {
|
|
|
2199
2267
|
function enterDeskKind(kind) {
|
|
2200
2268
|
const next = kind === "bug" ? "bug" : "feature";
|
|
2201
2269
|
if (state.deskKind === next && next === "bug" && !state.locked) {
|
|
2270
|
+
ensureModulesSeed();
|
|
2271
|
+
ensureBugModuleOnDesk();
|
|
2272
|
+
applyActiveModuleToFields();
|
|
2273
|
+
applyConfirmCardChrome();
|
|
2274
|
+
renderModuleTabs();
|
|
2202
2275
|
syncStartBugFixButtons();
|
|
2203
2276
|
return;
|
|
2204
2277
|
}
|
|
2205
2278
|
if (state.locked && next !== "bug") return;
|
|
2206
2279
|
state.deskKind = next;
|
|
2207
2280
|
if (next === "bug") {
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
title: t("card.bugModuleTitle"),
|
|
2218
|
-
status: "draft",
|
|
2219
|
-
card: state.locked
|
|
2220
|
-
? { goal: "", outOfScope: "", acceptance: "", assumptions: "" }
|
|
2221
|
-
: card,
|
|
2222
|
-
dependsOn: [],
|
|
2223
|
-
},
|
|
2224
|
-
];
|
|
2225
|
-
state.activeModuleId = "bug";
|
|
2281
|
+
ensureModulesSeed();
|
|
2282
|
+
ensureBugModuleOnDesk();
|
|
2283
|
+
const bug = state.modules.find((m) => m.id === "bug");
|
|
2284
|
+
if (bug && bug.status !== "confirmed") {
|
|
2285
|
+
if (state.locked) {
|
|
2286
|
+
bug.card = { goal: "", outOfScope: "", acceptance: "", assumptions: "" };
|
|
2287
|
+
}
|
|
2288
|
+
bug.title = t("card.bugModuleTitle");
|
|
2289
|
+
}
|
|
2226
2290
|
applyActiveModuleToFields();
|
|
2227
|
-
} else
|
|
2228
|
-
|
|
2229
|
-
state.modules[
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
{
|
|
2233
|
-
id
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2291
|
+
} else {
|
|
2292
|
+
// Leave bug mode: keep requirement modules; drop the defect tab.
|
|
2293
|
+
const kept = (state.modules || []).filter((m) => m && m.id !== "bug");
|
|
2294
|
+
if (kept.length) {
|
|
2295
|
+
state.modules = kept;
|
|
2296
|
+
if (!kept.some((m) => m.id === state.activeModuleId)) {
|
|
2297
|
+
state.activeModuleId = kept[0].id;
|
|
2298
|
+
}
|
|
2299
|
+
} else {
|
|
2300
|
+
state.modules = [
|
|
2301
|
+
{
|
|
2302
|
+
id: "main",
|
|
2303
|
+
title: "Main",
|
|
2304
|
+
status: "draft",
|
|
2305
|
+
card: { goal: "", outOfScope: "", acceptance: "", assumptions: "" },
|
|
2306
|
+
dependsOn: [],
|
|
2307
|
+
},
|
|
2308
|
+
];
|
|
2309
|
+
state.activeModuleId = "main";
|
|
2310
|
+
}
|
|
2241
2311
|
applyActiveModuleToFields();
|
|
2242
2312
|
}
|
|
2243
2313
|
applyConfirmCardChrome();
|
|
@@ -2986,6 +3056,7 @@ async function persistProjectChat() {
|
|
|
2986
3056
|
bugHotfix: Boolean(state.bugHotfix),
|
|
2987
3057
|
dispatchGraphReady: Boolean(state.dispatchGraphReady),
|
|
2988
3058
|
dispatchGraphUrl: state.dispatchGraphUrl || null,
|
|
3059
|
+
dispatchGraphBuiltOnce: Boolean(state.dispatchGraphBuiltOnce),
|
|
2989
3060
|
reviseCard: reviseCardValues(),
|
|
2990
3061
|
reviseCards: cardsLite,
|
|
2991
3062
|
bugCards: bugCardsLite,
|
|
@@ -3127,6 +3198,7 @@ function clearDeskWorkspace() {
|
|
|
3127
3198
|
state.dispatchGraphReady = false;
|
|
3128
3199
|
state.dispatchGraphBuilding = false;
|
|
3129
3200
|
state.dispatchGraphUrl = null;
|
|
3201
|
+
state.dispatchGraphBuiltOnce = false;
|
|
3130
3202
|
state.recommendedWorkerCount = 1;
|
|
3131
3203
|
state.mode = "specify";
|
|
3132
3204
|
if (el.moduleTabs) {
|
|
@@ -3377,8 +3449,20 @@ async function loadProjectChatIntoUi(projectPath) {
|
|
|
3377
3449
|
state.workerCount = Number(data.workerCount) > 0 ? Number(data.workerCount) : 1;
|
|
3378
3450
|
state.deskKind = data.deskKind === "bug" ? "bug" : "feature";
|
|
3379
3451
|
state.bugHotfix = Boolean(data.bugHotfix);
|
|
3452
|
+
if (state.deskKind === "bug") {
|
|
3453
|
+
ensureBugModuleOnDesk();
|
|
3454
|
+
if (data.activeModuleId) {
|
|
3455
|
+
const want = String(data.activeModuleId);
|
|
3456
|
+
if (state.modules.some((m) => m.id === want)) {
|
|
3457
|
+
state.activeModuleId = want;
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3380
3461
|
state.dispatchGraphReady = Boolean(data.dispatchGraphReady);
|
|
3381
3462
|
state.dispatchGraphUrl = String(data.dispatchGraphUrl || "").trim() || null;
|
|
3463
|
+
state.dispatchGraphBuiltOnce = Boolean(
|
|
3464
|
+
data.dispatchGraphBuiltOnce || data.dispatchGraphReady || data.dispatchGraphUrl,
|
|
3465
|
+
);
|
|
3382
3466
|
if (state.taskPool?.tasks?.length) {
|
|
3383
3467
|
state.recommendedWorkerCount = recommendWorkerCount(state.taskPool.tasks, {
|
|
3384
3468
|
max: 4,
|
|
@@ -3782,15 +3866,25 @@ function setSettingsOpen(open) {
|
|
|
3782
3866
|
}
|
|
3783
3867
|
}
|
|
3784
3868
|
el.settingsPanel.hidden = !want;
|
|
3869
|
+
el.settingsPanel.setAttribute("aria-hidden", want ? "false" : "true");
|
|
3785
3870
|
if (el.cfgOpen) {
|
|
3786
3871
|
el.cfgOpen.setAttribute("aria-expanded", want ? "true" : "false");
|
|
3787
3872
|
}
|
|
3788
3873
|
if (el.settingsClose) {
|
|
3789
3874
|
el.settingsClose.hidden = false;
|
|
3875
|
+
el.settingsClose.disabled = false;
|
|
3790
3876
|
}
|
|
3791
3877
|
syncDrawerBackdrop();
|
|
3792
3878
|
}
|
|
3793
3879
|
|
|
3880
|
+
function closeSettingsDrawer(ev) {
|
|
3881
|
+
if (ev) {
|
|
3882
|
+
ev.preventDefault();
|
|
3883
|
+
ev.stopPropagation();
|
|
3884
|
+
}
|
|
3885
|
+
setSettingsOpen(false);
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3794
3888
|
function fillAliyunFields(cfg) {
|
|
3795
3889
|
if (el.cfgAliyunId) {
|
|
3796
3890
|
el.cfgAliyunId.value = "";
|
|
@@ -3882,7 +3976,10 @@ function showSetup(cfg) {
|
|
|
3882
3976
|
applyProvider(id, { fillEmptyOnly: Boolean(cfg?.baseUrl || cfg?.model) });
|
|
3883
3977
|
// Always show the desk; open Settings so the operator can fill the model.
|
|
3884
3978
|
if (el.desk) el.desk.hidden = false;
|
|
3885
|
-
|
|
3979
|
+
// Re-open from the gear must not clear an already-ready session.
|
|
3980
|
+
if (!cfg?.ready) {
|
|
3981
|
+
state.ready = false;
|
|
3982
|
+
}
|
|
3886
3983
|
syncComposerEnabled();
|
|
3887
3984
|
setSettingsOpen(true);
|
|
3888
3985
|
if (el.meta) {
|
|
@@ -4376,21 +4473,30 @@ el.clearAwsCfg?.addEventListener("click", () => {
|
|
|
4376
4473
|
});
|
|
4377
4474
|
|
|
4378
4475
|
el.cfgOpen?.addEventListener("click", () => {
|
|
4379
|
-
|
|
4380
|
-
if (open) {
|
|
4476
|
+
if (el.settingsPanel?.hidden) {
|
|
4381
4477
|
showSetup({
|
|
4382
4478
|
...(state.lastCfg || {}),
|
|
4383
4479
|
providers: state.providers,
|
|
4384
4480
|
provider: state.providerId || state.lastCfg?.provider,
|
|
4385
4481
|
});
|
|
4386
|
-
} else
|
|
4387
|
-
|
|
4482
|
+
} else {
|
|
4483
|
+
closeSettingsDrawer();
|
|
4388
4484
|
}
|
|
4389
4485
|
});
|
|
4390
4486
|
|
|
4391
|
-
el.settingsClose?.addEventListener("click",
|
|
4392
|
-
|
|
4393
|
-
|
|
4487
|
+
el.settingsClose?.addEventListener("click", closeSettingsDrawer);
|
|
4488
|
+
// Capture-phase backup: sticky head / overlays must not swallow 关闭.
|
|
4489
|
+
el.settingsPanel?.addEventListener(
|
|
4490
|
+
"click",
|
|
4491
|
+
(ev) => {
|
|
4492
|
+
const t = ev.target;
|
|
4493
|
+
if (!(t instanceof Element)) return;
|
|
4494
|
+
if (t.id === "settingsClose" || t.closest?.("#settingsClose")) {
|
|
4495
|
+
closeSettingsDrawer(ev);
|
|
4496
|
+
}
|
|
4497
|
+
},
|
|
4498
|
+
true,
|
|
4499
|
+
);
|
|
4394
4500
|
|
|
4395
4501
|
el.form.addEventListener("submit", (e) => {
|
|
4396
4502
|
e.preventDefault();
|
|
@@ -4875,7 +4981,7 @@ async function autoHandleFromGate(kind, btn) {
|
|
|
4875
4981
|
|
|
4876
4982
|
async function applyConfirmSuccess(data) {
|
|
4877
4983
|
if (Array.isArray(data.modules) && data.modules.length) {
|
|
4878
|
-
|
|
4984
|
+
const mapped = data.modules.map((m) => ({
|
|
4879
4985
|
id: String(m.id),
|
|
4880
4986
|
title: String(m.title || m.id),
|
|
4881
4987
|
status: m.status === "confirmed" ? "confirmed" : String(m.status || "draft"),
|
|
@@ -4895,6 +5001,29 @@ async function applyConfirmSuccess(data) {
|
|
|
4895
5001
|
},
|
|
4896
5002
|
dependsOn: Array.isArray(m.dependsOn) ? m.dependsOn.map(String) : [],
|
|
4897
5003
|
}));
|
|
5004
|
+
const bugConfirm =
|
|
5005
|
+
state.deskKind === "bug" ||
|
|
5006
|
+
data.deskKind === "bug" ||
|
|
5007
|
+
mapped.some((m) => m.id === "bug");
|
|
5008
|
+
if (bugConfirm) {
|
|
5009
|
+
// Never let a bug-only server payload wipe prior requirement modules.
|
|
5010
|
+
const kept = (state.modules || []).filter((m) => m && m.id !== "bug");
|
|
5011
|
+
const byId = new Map(kept.map((m) => [m.id, m]));
|
|
5012
|
+
for (const m of mapped) {
|
|
5013
|
+
if (m.id === "bug") continue;
|
|
5014
|
+
byId.set(m.id, m);
|
|
5015
|
+
}
|
|
5016
|
+
const bug = mapped.find((m) => m.id === "bug") || mapped[0];
|
|
5017
|
+
state.modules = bug
|
|
5018
|
+
? [...byId.values()].filter((m) => m.id !== "bug").concat([
|
|
5019
|
+
bug.id === "bug"
|
|
5020
|
+
? bug
|
|
5021
|
+
: { ...bug, id: "bug", title: bug.title || t("card.bugModuleTitle") },
|
|
5022
|
+
])
|
|
5023
|
+
: [...byId.values()];
|
|
5024
|
+
} else {
|
|
5025
|
+
state.modules = mapped;
|
|
5026
|
+
}
|
|
4898
5027
|
}
|
|
4899
5028
|
if (data.activeModuleId || data.moduleId) {
|
|
4900
5029
|
state.activeModuleId = String(data.activeModuleId || data.moduleId);
|
|
@@ -4920,7 +5049,11 @@ async function applyConfirmSuccess(data) {
|
|
|
4920
5049
|
}
|
|
4921
5050
|
applyActiveModuleToFields();
|
|
4922
5051
|
}
|
|
4923
|
-
const allDone =
|
|
5052
|
+
const allDone =
|
|
5053
|
+
Boolean(data.modulesAllConfirmed) ||
|
|
5054
|
+
(state.deskKind === "bug"
|
|
5055
|
+
? Boolean(state.modules?.some((m) => m.id === "bug" && m.status === "confirmed"))
|
|
5056
|
+
: modulesAllConfirmedLocal());
|
|
4924
5057
|
state.locked = allDone;
|
|
4925
5058
|
// Brief is written at kickoff — do not set jobId from module confirm.
|
|
4926
5059
|
if (data.jobId) state.jobId = data.jobId;
|
|
@@ -5709,7 +5842,8 @@ function confirmArchitecture() {
|
|
|
5709
5842
|
schedulePersistProjectDesk();
|
|
5710
5843
|
addBubble("bot", t("arch.hintConfirmed"));
|
|
5711
5844
|
// Decompose → recommend workers → confirm graph, then launch (incl. revise).
|
|
5712
|
-
|
|
5845
|
+
// Always start a new 派工图 wave after architecture confirm.
|
|
5846
|
+
void showDispatchPanel({ forceNewWave: true });
|
|
5713
5847
|
}
|
|
5714
5848
|
|
|
5715
5849
|
async function autoFixAccept(btn, issues, kind = "confirm") {
|
|
@@ -5863,7 +5997,8 @@ async function tryRecoverDispatchDone() {
|
|
|
5863
5997
|
}
|
|
5864
5998
|
}
|
|
5865
5999
|
|
|
5866
|
-
async function showDispatchPanel() {
|
|
6000
|
+
async function showDispatchPanel(opts = {}) {
|
|
6001
|
+
const forceNewWave = Boolean(opts.forceNewWave);
|
|
5867
6002
|
el.dispatch.hidden = false;
|
|
5868
6003
|
el.dispatchErr.hidden = true;
|
|
5869
6004
|
el.dispatchStatus.hidden = true;
|
|
@@ -5872,7 +6007,9 @@ async function showDispatchPanel() {
|
|
|
5872
6007
|
if (state.projectPath && el.repoPath) {
|
|
5873
6008
|
el.repoPath.value = state.projectPath;
|
|
5874
6009
|
}
|
|
5875
|
-
|
|
6010
|
+
// Each architecture-confirm wave needs a fresh pool + new 派工图.
|
|
6011
|
+
if (forceNewWave || !state.taskPool?.tasks?.length) {
|
|
6012
|
+
resetDispatchGraphForNewWave({ silent: true, hidePanel: false });
|
|
5876
6013
|
decomposeTasksFromModules();
|
|
5877
6014
|
} else {
|
|
5878
6015
|
applyRecommendedWorkerCount(state.taskPool.tasks);
|
|
@@ -6190,7 +6327,7 @@ function openBaselineChange() {
|
|
|
6190
6327
|
confirmed: false,
|
|
6191
6328
|
status: state.architecture.url ? "preview" : "idle",
|
|
6192
6329
|
};
|
|
6193
|
-
|
|
6330
|
+
clearDispatchGraphState({ silent: true, hidePanel: false });
|
|
6194
6331
|
if (el.baselineChangeReason) el.baselineChangeReason.value = "";
|
|
6195
6332
|
if (el.baselineSigner) el.baselineSigner.value = "";
|
|
6196
6333
|
applyActiveModuleToFields();
|
|
@@ -7878,6 +8015,8 @@ function enterReviseMode() {
|
|
|
7878
8015
|
state.reviseLocked = false;
|
|
7879
8016
|
state.reviseDispatching = false;
|
|
7880
8017
|
state.revisePlanConfirmed = false;
|
|
8018
|
+
// New improve wave — prior 派工图 must not stay locked/ready.
|
|
8019
|
+
resetDispatchGraphForNewWave({ silent: true, hidePanel: true });
|
|
7881
8020
|
// Keep prior revise dialogue history (do not wipe reviseMessages).
|
|
7882
8021
|
// Advance to the next iteration card — never overwrite prior reviseCards.
|
|
7883
8022
|
const nextRev = nextReviseRevisionNumber();
|
|
@@ -8271,7 +8410,7 @@ async function confirmReviseAndDispatch() {
|
|
|
8271
8410
|
el.reviseErr.hidden = false;
|
|
8272
8411
|
el.reviseErr.textContent = t("dispatch.needGraphConfirm");
|
|
8273
8412
|
}
|
|
8274
|
-
void showDispatchPanel();
|
|
8413
|
+
void showDispatchPanel({ forceNewWave: !state.taskPool?.tasks?.length });
|
|
8275
8414
|
return;
|
|
8276
8415
|
}
|
|
8277
8416
|
await dispatchReviseAgent();
|
|
@@ -8603,6 +8742,18 @@ document.addEventListener("keydown", (ev) => {
|
|
|
8603
8742
|
if (ev.key !== "Escape") return;
|
|
8604
8743
|
if (el.deployPicker && !el.deployPicker.hidden) {
|
|
8605
8744
|
closeDeployPicker();
|
|
8745
|
+
return;
|
|
8746
|
+
}
|
|
8747
|
+
if (el.settingsPanel && !el.settingsPanel.hidden) {
|
|
8748
|
+
closeSettingsDrawer();
|
|
8749
|
+
return;
|
|
8750
|
+
}
|
|
8751
|
+
if (el.employeePanel && !el.employeePanel.hidden) {
|
|
8752
|
+
setEmployeeOpen(false);
|
|
8753
|
+
return;
|
|
8754
|
+
}
|
|
8755
|
+
if (el.historyPanel && !el.historyPanel.hidden) {
|
|
8756
|
+
setHistoryOpen(false);
|
|
8606
8757
|
}
|
|
8607
8758
|
});
|
|
8608
8759
|
|
|
@@ -8717,10 +8868,6 @@ function setHistoryOpen(open) {
|
|
|
8717
8868
|
if (!el.historyPanel) return;
|
|
8718
8869
|
const want = Boolean(open);
|
|
8719
8870
|
if (want && el.settingsPanel && !el.settingsPanel.hidden) {
|
|
8720
|
-
if (!state.ready) {
|
|
8721
|
-
// Cannot open projects over mandatory first-time settings.
|
|
8722
|
-
return;
|
|
8723
|
-
}
|
|
8724
8871
|
el.settingsPanel.hidden = true;
|
|
8725
8872
|
if (el.cfgOpen) el.cfgOpen.setAttribute("aria-expanded", "false");
|
|
8726
8873
|
}
|
|
@@ -9121,8 +9268,15 @@ if (el.openTaskGraph) {
|
|
|
9121
9268
|
}
|
|
9122
9269
|
if (el.redecomposeTasks) {
|
|
9123
9270
|
el.redecomposeTasks.addEventListener("click", () => {
|
|
9271
|
+
const hadGraph =
|
|
9272
|
+
state.dispatchGraphReady ||
|
|
9273
|
+
Boolean(state.dispatchGraphUrl) ||
|
|
9274
|
+
state.dispatchGraphBuiltOnce;
|
|
9124
9275
|
decomposeTasksFromModules();
|
|
9125
|
-
|
|
9276
|
+
if (hadGraph && el.dispatchErr) {
|
|
9277
|
+
el.dispatchErr.hidden = false;
|
|
9278
|
+
el.dispatchErr.textContent = t("dispatch.graphStale");
|
|
9279
|
+
}
|
|
9126
9280
|
});
|
|
9127
9281
|
}
|
|
9128
9282
|
if (el.confirmWorkersGraph) {
|
|
@@ -9154,7 +9308,7 @@ if (el.historyClose) {
|
|
|
9154
9308
|
if (el.historyBackdrop) {
|
|
9155
9309
|
el.historyBackdrop.addEventListener("click", () => {
|
|
9156
9310
|
if (el.settingsPanel && !el.settingsPanel.hidden) {
|
|
9157
|
-
|
|
9311
|
+
closeSettingsDrawer();
|
|
9158
9312
|
return;
|
|
9159
9313
|
}
|
|
9160
9314
|
if (el.employeePanel && !el.employeePanel.hidden) {
|
|
@@ -9239,6 +9393,14 @@ onLocaleChange(() => {
|
|
|
9239
9393
|
});
|
|
9240
9394
|
|
|
9241
9395
|
const initialLocale = initI18n();
|
|
9396
|
+
initTheme();
|
|
9397
|
+
syncThemeToggleUi();
|
|
9398
|
+
if (el.themeToggle) {
|
|
9399
|
+
el.themeToggle.addEventListener("click", () => {
|
|
9400
|
+
toggleTheme();
|
|
9401
|
+
syncThemeToggleUi();
|
|
9402
|
+
});
|
|
9403
|
+
}
|
|
9242
9404
|
if (el.langSelect) {
|
|
9243
9405
|
el.langSelect.value = initialLocale;
|
|
9244
9406
|
el.langSelect.addEventListener("change", () => {
|
|
@@ -9246,13 +9408,13 @@ if (el.langSelect) {
|
|
|
9246
9408
|
});
|
|
9247
9409
|
}
|
|
9248
9410
|
applyDomI18n();
|
|
9411
|
+
syncThemeToggleUi();
|
|
9249
9412
|
syncChatPlaceholder();
|
|
9250
9413
|
wireReqSections();
|
|
9251
9414
|
syncComposerEnabled();
|
|
9252
9415
|
renderWorkerCountList();
|
|
9253
9416
|
wirePanelScrollHold(el.cardPanel);
|
|
9254
9417
|
wirePanelScrollHold(el.progressCol);
|
|
9255
|
-
wireChatQuickNav();
|
|
9256
9418
|
|
|
9257
9419
|
if (el.autoFixCard) {
|
|
9258
9420
|
el.autoFixCard.addEventListener("click", () => {
|