dsh-taskboard 0.5.0 → 0.5.2
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/README.md +25 -1
- package/lib/client.js +242 -174
- package/lib/host/execution.js +80 -33
- package/lib/host/execution.js.map +1 -1
- package/lib/host/git.js +49 -5
- package/lib/host/git.js.map +1 -1
- package/lib/host/routes.js +180 -109
- package/lib/host/routes.js.map +1 -1
- package/lib/host/scheduler.js +50 -28
- package/lib/host/scheduler.js.map +1 -1
- package/lib/host/sdk.js +7 -2
- package/lib/host/sdk.js.map +1 -1
- package/lib/host/store.js +41 -8
- package/lib/host/store.js.map +1 -1
- package/lib/host/templates.js +10 -3
- package/lib/host/templates.js.map +1 -1
- package/lib/host/tools.js +124 -93
- package/lib/host/tools.js.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/shared/api.js.map +1 -1
- package/lib/shared/protocol.js +23 -2
- package/lib/shared/protocol.js.map +1 -1
- package/package.json +3 -2
- package/src/client/api.ts +19 -9
- package/src/client/board/ImportModal.tsx +1 -1
- package/src/client/board/TaskBoard.tsx +7 -38
- package/src/client/board/TaskCard.tsx +3 -5
- package/src/client/board/TaskDetail.tsx +30 -21
- package/src/client/board/TaskFormModal.tsx +30 -23
- package/src/client/board/format.ts +26 -0
- package/src/client/board/labels.ts +44 -0
- package/src/client/board-mount.tsx +9 -6
- package/src/client/controller.ts +60 -13
- package/src/client/index.ts +7 -5
- package/src/client/sidebar-entry.ts +16 -5
- package/src/client/styles.ts +5 -3
- package/src/host/execution.ts +90 -16
- package/src/host/git.ts +39 -10
- package/src/host/routes.ts +227 -126
- package/src/host/scheduler.ts +62 -36
- package/src/host/sdk.ts +12 -1
- package/src/host/store.ts +53 -7
- package/src/host/templates.ts +12 -3
- package/src/host/tools.ts +180 -123
- package/src/index.ts +10 -1
- package/src/shared/api.ts +1 -1
- package/src/shared/protocol.ts +35 -1
- package/src/shared/version.ts +1 -1
- package/src/client/board/NewTaskModal.tsx +0 -8
package/lib/client.js
CHANGED
|
@@ -18,20 +18,26 @@ window.__ModuleLoader__.load({
|
|
|
18
18
|
if (!body.ok) throw new Error(`taskboard: ${body.error.code}: ${body.error.message}`);
|
|
19
19
|
return body.value;
|
|
20
20
|
}
|
|
21
|
+
/** Request timeout (S15: a hung fetch must never pin refreshInFlight forever). */
|
|
22
|
+
const TIMEOUT_MS = 1e4;
|
|
23
|
+
async function get(path) {
|
|
24
|
+
return unwrap(fetch(path, { signal: AbortSignal.timeout(TIMEOUT_MS) }));
|
|
25
|
+
}
|
|
21
26
|
async function post(path, body) {
|
|
22
27
|
return unwrap(await fetch(path, {
|
|
23
28
|
method: "POST",
|
|
24
29
|
headers: { "content-type": "application/json" },
|
|
25
|
-
body: JSON.stringify(body)
|
|
30
|
+
body: JSON.stringify(body),
|
|
31
|
+
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
26
32
|
}));
|
|
27
33
|
}
|
|
28
34
|
/** Build the client over fetch + EventSource. */
|
|
29
35
|
function createClient() {
|
|
30
36
|
return {
|
|
31
|
-
state: () =>
|
|
32
|
-
workspaces: () =>
|
|
37
|
+
state: () => get("/dsh-taskboard/state"),
|
|
38
|
+
workspaces: () => get("/dsh-taskboard/workspaces"),
|
|
33
39
|
create: (body) => post("/dsh-taskboard/tasks", body),
|
|
34
|
-
get: (id) =>
|
|
40
|
+
get: (id) => get(`/dsh-taskboard/tasks/${encodeURIComponent(id)}`),
|
|
35
41
|
update: (id, body) => post(`/dsh-taskboard/tasks/${encodeURIComponent(id)}/update`, body),
|
|
36
42
|
move: (id, body) => post(`/dsh-taskboard/tasks/${encodeURIComponent(id)}/move`, body),
|
|
37
43
|
reject: (id, body) => post(`/dsh-taskboard/tasks/${encodeURIComponent(id)}/reject`, body),
|
|
@@ -41,7 +47,7 @@ window.__ModuleLoader__.load({
|
|
|
41
47
|
cancel: (id) => post(`/dsh-taskboard/tasks/${encodeURIComponent(id)}/cancel`, {}),
|
|
42
48
|
mergeBranch: (id) => post(`/dsh-taskboard/tasks/${encodeURIComponent(id)}/merge`, {}),
|
|
43
49
|
worktreeRemove: (id, body) => post(`/dsh-taskboard/tasks/${encodeURIComponent(id)}/worktree-remove`, body),
|
|
44
|
-
diagnostics: () =>
|
|
50
|
+
diagnostics: () => get("/dsh-taskboard/diagnostics"),
|
|
45
51
|
worktreeCleanup: (workspaceId, taskId) => post("/dsh-taskboard/worktree-cleanup", {
|
|
46
52
|
workspaceId,
|
|
47
53
|
taskId
|
|
@@ -50,28 +56,38 @@ window.__ModuleLoader__.load({
|
|
|
50
56
|
const params = new URLSearchParams({ execution: query.execution });
|
|
51
57
|
if (query.commit !== void 0) params.set("commit", query.commit);
|
|
52
58
|
if (query.path !== void 0) params.set("path", query.path);
|
|
53
|
-
return
|
|
59
|
+
return get(`/dsh-taskboard/tasks/${encodeURIComponent(taskId)}/diff?${params.toString()}`);
|
|
54
60
|
},
|
|
55
61
|
importPreview: (file) => post("/dsh-taskboard/import/preview", file),
|
|
56
62
|
importCommit: (mode, ledger) => post("/dsh-taskboard/import", {
|
|
57
63
|
mode,
|
|
58
64
|
ledger
|
|
59
65
|
}),
|
|
60
|
-
templates: () =>
|
|
66
|
+
templates: () => get("/dsh-taskboard/templates"),
|
|
61
67
|
templateUpsert: (body) => post("/dsh-taskboard/templates", body),
|
|
62
68
|
templateDelete: (id) => post("/dsh-taskboard/templates/delete", { id }),
|
|
63
|
-
settings: () =>
|
|
69
|
+
settings: () => get("/dsh-taskboard/settings"),
|
|
64
70
|
updateSettings: (body) => post("/dsh-taskboard/settings/update", body),
|
|
65
71
|
stream(onChange, onGap) {
|
|
66
72
|
const es = new EventSource("/dsh-taskboard/events");
|
|
67
73
|
let revision;
|
|
68
74
|
const hello = (event) => {
|
|
69
|
-
|
|
75
|
+
let payload;
|
|
76
|
+
try {
|
|
77
|
+
payload = JSON.parse(event.data);
|
|
78
|
+
} catch {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
70
81
|
if (revision !== void 0 && payload.revision !== revision) onGap();
|
|
71
82
|
revision = payload.revision;
|
|
72
83
|
};
|
|
73
84
|
const change = (event) => {
|
|
74
|
-
|
|
85
|
+
let payload;
|
|
86
|
+
try {
|
|
87
|
+
payload = JSON.parse(event.data);
|
|
88
|
+
} catch {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
75
91
|
if (revision !== void 0 && payload.revision !== revision + 1) onGap();
|
|
76
92
|
revision = payload.revision;
|
|
77
93
|
onChange(payload);
|
|
@@ -304,7 +320,11 @@ window.__ModuleLoader__.load({
|
|
|
304
320
|
disposed = false;
|
|
305
321
|
disposeStream;
|
|
306
322
|
refreshInFlight;
|
|
323
|
+
/** Newest change-frame revision seen on the SSE stream (S16 refresh chase). */
|
|
324
|
+
seenRevision;
|
|
307
325
|
sessionJumper;
|
|
326
|
+
/** Composer catalog faces, installed formally by the client entry (T13). */
|
|
327
|
+
catalogFaces = {};
|
|
308
328
|
/** @param client - the route client. */
|
|
309
329
|
constructor(client) {
|
|
310
330
|
this.client = client;
|
|
@@ -333,10 +353,7 @@ window.__ModuleLoader__.load({
|
|
|
333
353
|
start() {
|
|
334
354
|
this.refresh();
|
|
335
355
|
this.disposeStream = this.client.stream((change) => {
|
|
336
|
-
this.
|
|
337
|
-
...this.state.ledger,
|
|
338
|
-
revision: change.revision
|
|
339
|
-
} });
|
|
356
|
+
this.seenRevision = change.revision;
|
|
340
357
|
this.refresh();
|
|
341
358
|
}, () => {
|
|
342
359
|
this.refresh();
|
|
@@ -347,15 +364,18 @@ window.__ModuleLoader__.load({
|
|
|
347
364
|
if (this.refreshInFlight !== void 0) return this.refreshInFlight;
|
|
348
365
|
this.refreshInFlight = (async () => {
|
|
349
366
|
try {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
367
|
+
for (let round = 0; round < 3; round++) {
|
|
368
|
+
const [ledger, workspaces] = await Promise.all([this.client.state(), this.client.workspaces()]);
|
|
369
|
+
let selected;
|
|
370
|
+
if (this.state.selectedId !== void 0) selected = ledger.tasks.find((t) => t.id === this.state.selectedId);
|
|
371
|
+
this.setState({
|
|
372
|
+
ledger,
|
|
373
|
+
workspaces,
|
|
374
|
+
error: void 0,
|
|
375
|
+
selectedId: selected === void 0 ? void 0 : this.state.selectedId
|
|
376
|
+
});
|
|
377
|
+
if (this.seenRevision === void 0 || ledger.revision >= this.seenRevision) break;
|
|
378
|
+
}
|
|
359
379
|
} catch (error) {
|
|
360
380
|
this.setState({ error: error instanceof Error ? error.message : String(error) });
|
|
361
381
|
} finally {
|
|
@@ -473,6 +493,22 @@ window.__ModuleLoader__.load({
|
|
|
473
493
|
installSessionJumper(jumper) {
|
|
474
494
|
this.sessionJumper = jumper;
|
|
475
495
|
}
|
|
496
|
+
/** T13: formal installers for the composer catalog faces (was a monkeypatch from the client entry). */
|
|
497
|
+
installModelCatalog(fn) {
|
|
498
|
+
this.catalogFaces.models = fn;
|
|
499
|
+
}
|
|
500
|
+
/** T13: formal installer for the preset roster face. */
|
|
501
|
+
installPresetRoster(fn) {
|
|
502
|
+
this.catalogFaces.presets = fn;
|
|
503
|
+
}
|
|
504
|
+
/** The installed model catalog face, when the runtime provides one. */
|
|
505
|
+
get modelCatalog() {
|
|
506
|
+
return this.catalogFaces.models;
|
|
507
|
+
}
|
|
508
|
+
/** The installed preset roster face, when the runtime provides one. */
|
|
509
|
+
get presetCatalog() {
|
|
510
|
+
return this.catalogFaces.presets;
|
|
511
|
+
}
|
|
476
512
|
/**
|
|
477
513
|
* Jump to an execution's session (open it in the GUI). On success the board
|
|
478
514
|
* closes so the conversation shows; a deleted-or-archived session reports
|
|
@@ -606,13 +642,18 @@ window.__ModuleLoader__.load({
|
|
|
606
642
|
return;
|
|
607
643
|
}
|
|
608
644
|
}
|
|
609
|
-
/**
|
|
645
|
+
/**
|
|
646
|
+
* Append a user comment. Returns whether it landed — the composer keeps its
|
|
647
|
+
* text on failure (T13: it used to clear unconditionally and lose the draft).
|
|
648
|
+
*/
|
|
610
649
|
async comment(id, body) {
|
|
611
650
|
try {
|
|
612
651
|
await this.client.comment(id, body);
|
|
613
652
|
await this.refresh();
|
|
653
|
+
return true;
|
|
614
654
|
} catch (error) {
|
|
615
655
|
this.setState({ error: error instanceof Error ? error.message : String(error) });
|
|
656
|
+
return false;
|
|
616
657
|
}
|
|
617
658
|
}
|
|
618
659
|
/** Trigger a manual run (fresh in-project session, pinned model); `reuse` = 续跑. */
|
|
@@ -730,7 +771,7 @@ window.__ModuleLoader__.load({
|
|
|
730
771
|
async duplicate(task) {
|
|
731
772
|
try {
|
|
732
773
|
await this.client.create({
|
|
733
|
-
title: `${task.title}(副本)`,
|
|
774
|
+
title: `${task.title.slice(0, 196)}(副本)`,
|
|
734
775
|
workspaceId: task.workspaceId,
|
|
735
776
|
urgency: task.urgency,
|
|
736
777
|
description: task.description.length > 0 ? task.description : void 0,
|
|
@@ -855,7 +896,8 @@ window.__ModuleLoader__.load({
|
|
|
855
896
|
/** Download the task list as a CSV (BOM-prefixed for Excel + Chinese text). */
|
|
856
897
|
exportCsv() {
|
|
857
898
|
const esc = (v) => {
|
|
858
|
-
|
|
899
|
+
let s = String(v ?? "");
|
|
900
|
+
if (/^[=+\-@\t\r]/.test(s)) s = `'${s}`;
|
|
859
901
|
return /[",\n\r]/.test(s) ? `"${s.replace(/"/g, "\"\"")}"` : s;
|
|
860
902
|
};
|
|
861
903
|
const header = [
|
|
@@ -985,10 +1027,12 @@ window.__ModuleLoader__.load({
|
|
|
985
1027
|
.dsh-atb-search { width: 130px; }
|
|
986
1028
|
.dsh-atb-badge[data-kind="stale"] { background: rgba(217,130,43,.15); color: #d9822b; }
|
|
987
1029
|
|
|
988
|
-
/*
|
|
989
|
-
* shell's CSS-Module hashed centerCol (
|
|
1030
|
+
/* Triple-generation column matching — dev shell's data-pane pane, the
|
|
1031
|
+
* official layout shell's CSS-Module hashed centerCol (0.4.2), or DSH
|
|
1032
|
+
* Desktop's non-compat extended frame surface (0.5.2, see board-mount.tsx). */
|
|
990
1033
|
html[data-dsh-atb-active] [data-pane="conversation"] > *:not([data-dsh-atb-view]),
|
|
991
|
-
html[data-dsh-atb-active] [class*="centerCol"] > *:not([data-dsh-atb-view])
|
|
1034
|
+
html[data-dsh-atb-active] [class*="centerCol"] > *:not([data-dsh-atb-view]),
|
|
1035
|
+
html[data-dsh-atb-active] .dshDesktopConversationSurface > *:not([data-dsh-atb-view]) { display: none !important; }
|
|
992
1036
|
.dsh-atb-view { display: none; }
|
|
993
1037
|
html[data-dsh-atb-active] .dsh-atb-view { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
|
|
994
1038
|
|
|
@@ -1656,14 +1700,19 @@ window.__ModuleLoader__.load({
|
|
|
1656
1700
|
//#region src/client/sidebar-entry.ts
|
|
1657
1701
|
/** Stable data attribute identifying this entry row. */
|
|
1658
1702
|
const ENTRY_SELECTOR = "[data-dsh-atb-entry]";
|
|
1659
|
-
/** Inline icon (16px nav-icon look). */
|
|
1660
|
-
const ICON = "<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2
|
|
1703
|
+
/** Inline icon: a three-lane kanban board (16px nav-icon look). */
|
|
1704
|
+
const ICON = "<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2\" width=\"12\" height=\"12\" rx=\"2\"/><path d=\"M6 2v12M10 2v12\"/></svg>";
|
|
1661
1705
|
/**
|
|
1662
1706
|
* Find the sidebar shell root element, or undefined while not yet mounted.
|
|
1663
|
-
* (
|
|
1707
|
+
* Triple-generation matching (0.5.2): the dev shell's data-pane pane, the
|
|
1708
|
+
* official layout's CSS-Module sidebarCol, and DSH Desktop's non-compat
|
|
1709
|
+
* (extended) frame — which disables the official ui-layout row and owns
|
|
1710
|
+
* the columns itself (aside.dshDesktopSidebarSurface >
|
|
1711
|
+
* div.dshDesktopUpstreamSidebar wrapping the unchanged official sidebar).
|
|
1712
|
+
* logoRow-owner resolution is identical on all three generations.
|
|
1664
1713
|
*/
|
|
1665
1714
|
function sidebarRoot() {
|
|
1666
|
-
const column = document.querySelector("[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]");
|
|
1715
|
+
const column = document.querySelector("[data-pane=\"sidebar\"], [class*=\"sidebarCol\"], .dshDesktopUpstreamSidebar, .dshDesktopSidebarSurface");
|
|
1667
1716
|
if (column === null) return void 0;
|
|
1668
1717
|
return column.querySelector("[class*=\"logoRow\"]")?.parentElement ?? column.firstElementChild;
|
|
1669
1718
|
}
|
|
@@ -1818,7 +1867,8 @@ window.__ModuleLoader__.load({
|
|
|
1818
1867
|
found: false,
|
|
1819
1868
|
placed: false
|
|
1820
1869
|
};
|
|
1821
|
-
|
|
1870
|
+
const host = globalThis.location?.hostname;
|
|
1871
|
+
if (host === "localhost" || host === "127.0.0.1") window.__atbDebug = debug;
|
|
1822
1872
|
let root;
|
|
1823
1873
|
let placed = false;
|
|
1824
1874
|
const tryPlace = () => {
|
|
@@ -1889,7 +1939,70 @@ window.__ModuleLoader__.load({
|
|
|
1889
1939
|
* @module dsh-taskboard/shared/version
|
|
1890
1940
|
*/
|
|
1891
1941
|
/** The package version (must equal package.json "version"). */
|
|
1892
|
-
const PLUGIN_VERSION = "0.5.
|
|
1942
|
+
const PLUGIN_VERSION = "0.5.2";
|
|
1943
|
+
|
|
1944
|
+
//#endregion
|
|
1945
|
+
//#region src/client/board/labels.ts
|
|
1946
|
+
/** Column headers on the five-column main board (+ secondary tab). */
|
|
1947
|
+
const COLUMN_LABELS = {
|
|
1948
|
+
backlog: "待规划",
|
|
1949
|
+
todo: "待办",
|
|
1950
|
+
in_progress: "进行中",
|
|
1951
|
+
in_review: "待验收",
|
|
1952
|
+
done: "已完成",
|
|
1953
|
+
canceled: "已取消",
|
|
1954
|
+
archived: "已归档"
|
|
1955
|
+
};
|
|
1956
|
+
/** Status pill text (detail pane) — historical wording kept verbatim:
|
|
1957
|
+
* terminal states read short here, the column headers carry the full forms. */
|
|
1958
|
+
const STATUS_LABEL = {
|
|
1959
|
+
backlog: "待规划",
|
|
1960
|
+
todo: "待办",
|
|
1961
|
+
in_progress: "进行中",
|
|
1962
|
+
in_review: "待验收",
|
|
1963
|
+
done: "完成",
|
|
1964
|
+
canceled: "取消",
|
|
1965
|
+
archived: "归档"
|
|
1966
|
+
};
|
|
1967
|
+
/** Move-button verbs (shorter than the pill text). */
|
|
1968
|
+
const MOVE_LABEL = {
|
|
1969
|
+
backlog: "待规划",
|
|
1970
|
+
todo: "待办",
|
|
1971
|
+
in_progress: "进行中",
|
|
1972
|
+
in_review: "待验收",
|
|
1973
|
+
done: "完成",
|
|
1974
|
+
canceled: "取消",
|
|
1975
|
+
archived: "归档"
|
|
1976
|
+
};
|
|
1977
|
+
/** Urgency chip labels. */
|
|
1978
|
+
const URGENCY_LABEL = {
|
|
1979
|
+
urgent: "紧急",
|
|
1980
|
+
normal: "一般",
|
|
1981
|
+
relaxed: "不急"
|
|
1982
|
+
};
|
|
1983
|
+
/** Execution outcome labels. */
|
|
1984
|
+
const OUTCOME_LABEL = {
|
|
1985
|
+
running: "执行中",
|
|
1986
|
+
succeeded: "成功",
|
|
1987
|
+
failed: "失败",
|
|
1988
|
+
cancelled: "已取消"
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1991
|
+
//#endregion
|
|
1992
|
+
//#region src/client/board/format.ts
|
|
1993
|
+
/** Format an epoch ms as a short local stamp. */
|
|
1994
|
+
function fmtTime(ms) {
|
|
1995
|
+
if (ms === void 0) return "";
|
|
1996
|
+
const d = new Date(ms);
|
|
1997
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1998
|
+
return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
1999
|
+
}
|
|
2000
|
+
/** A claim idle for longer than this is highlighted as stale (ms). */
|
|
2001
|
+
const STALE_CLAIM_MS = 30 * 6e4;
|
|
2002
|
+
/** Whether the task's claim is stale (in_progress, held, idle too long). */
|
|
2003
|
+
function isStaleClaim(task, now) {
|
|
2004
|
+
return task.status === "in_progress" && task.claimedAt !== void 0 && now - task.claimedAt > 18e5;
|
|
2005
|
+
}
|
|
1893
2006
|
|
|
1894
2007
|
//#endregion
|
|
1895
2008
|
//#region src/client/board/TaskCard.tsx
|
|
@@ -1906,17 +2019,6 @@ window.__ModuleLoader__.load({
|
|
|
1906
2019
|
*
|
|
1907
2020
|
* @module dsh-taskboard/client/board/TaskCard
|
|
1908
2021
|
*/
|
|
1909
|
-
const URGENCY_LABEL$1 = {
|
|
1910
|
-
urgent: "紧急",
|
|
1911
|
-
normal: "一般",
|
|
1912
|
-
relaxed: "不急"
|
|
1913
|
-
};
|
|
1914
|
-
const OUTCOME_LABEL$1 = {
|
|
1915
|
-
running: "执行中",
|
|
1916
|
-
succeeded: "成功",
|
|
1917
|
-
failed: "失败",
|
|
1918
|
-
cancelled: "已取消"
|
|
1919
|
-
};
|
|
1920
2022
|
/** dataTransfer type carrying the dragged task id. */
|
|
1921
2023
|
const DRAG_TYPE = "application/x-dsh-atb-task";
|
|
1922
2024
|
/**
|
|
@@ -1952,7 +2054,7 @@ window.__ModuleLoader__.load({
|
|
|
1952
2054
|
onDragStart: (e) => {
|
|
1953
2055
|
if (running !== void 0) {
|
|
1954
2056
|
e.preventDefault();
|
|
1955
|
-
const msg =
|
|
2057
|
+
const msg = `该任务正由会话执行中(${task.title}),不能拖动`;
|
|
1956
2058
|
if (onAlert !== void 0) onAlert(msg);
|
|
1957
2059
|
else alert(msg);
|
|
1958
2060
|
return;
|
|
@@ -1982,7 +2084,7 @@ window.__ModuleLoader__.load({
|
|
|
1982
2084
|
children: [
|
|
1983
2085
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1984
2086
|
className: "dsh-atb-badge",
|
|
1985
|
-
children: URGENCY_LABEL
|
|
2087
|
+
children: URGENCY_LABEL[task.urgency]
|
|
1986
2088
|
}),
|
|
1987
2089
|
task.blocked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1988
2090
|
className: "dsh-atb-badge",
|
|
@@ -2022,7 +2124,7 @@ window.__ModuleLoader__.load({
|
|
|
2022
2124
|
last !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2023
2125
|
className: "dsh-atb-badge",
|
|
2024
2126
|
"data-kind": last.outcome === "running" ? "running" : last.outcome,
|
|
2025
|
-
children: OUTCOME_LABEL
|
|
2127
|
+
children: OUTCOME_LABEL[last.outcome] ?? last.outcome
|
|
2026
2128
|
}),
|
|
2027
2129
|
task.comments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["💬 ", task.comments.length] }),
|
|
2028
2130
|
task.trashedAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -2175,27 +2277,6 @@ window.__ModuleLoader__.load({
|
|
|
2175
2277
|
"archived"
|
|
2176
2278
|
].filter((to) => canTransition(task.status, to));
|
|
2177
2279
|
}
|
|
2178
|
-
const MOVE_LABEL = {
|
|
2179
|
-
backlog: "待规划",
|
|
2180
|
-
todo: "待办",
|
|
2181
|
-
in_progress: "进行中",
|
|
2182
|
-
in_review: "待验收",
|
|
2183
|
-
done: "完成",
|
|
2184
|
-
canceled: "取消",
|
|
2185
|
-
archived: "归档"
|
|
2186
|
-
};
|
|
2187
|
-
const STATUS_LABEL = { ...MOVE_LABEL };
|
|
2188
|
-
const URGENCY_LABEL = {
|
|
2189
|
-
urgent: "紧急",
|
|
2190
|
-
normal: "一般",
|
|
2191
|
-
relaxed: "不急"
|
|
2192
|
-
};
|
|
2193
|
-
const OUTCOME_LABEL = {
|
|
2194
|
-
running: "执行中",
|
|
2195
|
-
succeeded: "成功",
|
|
2196
|
-
failed: "失败",
|
|
2197
|
-
cancelled: "已取消"
|
|
2198
|
-
};
|
|
2199
2280
|
/** Compact session-id display (execution sessions carry the taskboard infix). */
|
|
2200
2281
|
function shortId(id) {
|
|
2201
2282
|
if (id === void 0) return "";
|
|
@@ -2664,6 +2745,7 @@ window.__ModuleLoader__.load({
|
|
|
2664
2745
|
const [confirmDone, setConfirmDone] = (0, react.useState)(false);
|
|
2665
2746
|
const [confirmPurge, setConfirmPurge] = (0, react.useState)(false);
|
|
2666
2747
|
const [confirmCancel, setConfirmCancel] = (0, react.useState)(false);
|
|
2748
|
+
const [actionBusy, setActionBusy] = (0, react.useState)(false);
|
|
2667
2749
|
const { alert: showAlert, el: alertEl } = useAlert();
|
|
2668
2750
|
const ws = controller.getSnapshot().workspaces.find((w) => w.id === task.workspaceId);
|
|
2669
2751
|
const canRun = task.status !== "in_progress" && task.status !== "done" && task.status !== "archived";
|
|
@@ -2671,6 +2753,12 @@ window.__ModuleLoader__.load({
|
|
|
2671
2753
|
const holder = task.status === "in_progress" ? task.claimedBy : void 0;
|
|
2672
2754
|
const stale = now !== void 0 && isStaleClaim(task, now);
|
|
2673
2755
|
const unchecked = (task.checklist ?? []).filter((i) => !i.checked).length;
|
|
2756
|
+
/** Fire one top action under the shared busy guard; re-enable on settle. */
|
|
2757
|
+
const runAction = (action) => {
|
|
2758
|
+
if (actionBusy) return;
|
|
2759
|
+
setActionBusy(true);
|
|
2760
|
+
action().catch(() => void 0).finally(() => setActionBusy(false));
|
|
2761
|
+
};
|
|
2674
2762
|
/** Jump to an execution's session; prompt precisely when it cannot open. */
|
|
2675
2763
|
const jumpToSession = (sessionId) => {
|
|
2676
2764
|
controller.openSession(sessionId).then((result) => {
|
|
@@ -2770,7 +2858,7 @@ window.__ModuleLoader__.load({
|
|
|
2770
2858
|
"更新 ",
|
|
2771
2859
|
fmtTime(task.updatedAt),
|
|
2772
2860
|
" · 最近操作 ",
|
|
2773
|
-
task.updatedBy.kind === "agent" ? `🤖 ${shortId(task.updatedBy.sessionId)}` : "👤 用户"
|
|
2861
|
+
task.updatedBy.kind === "agent" ? `🤖 ${shortId(task.updatedBy.sessionId)}` : task.updatedBy.kind === "system" ? "⚙️ 系统" : "👤 用户"
|
|
2774
2862
|
]
|
|
2775
2863
|
})
|
|
2776
2864
|
]
|
|
@@ -2787,32 +2875,34 @@ window.__ModuleLoader__.load({
|
|
|
2787
2875
|
type: "button",
|
|
2788
2876
|
className: "dsh-atb-detail-edit",
|
|
2789
2877
|
title: "复制此任务的全部配置为一张新卡(待办列)",
|
|
2790
|
-
|
|
2878
|
+
disabled: actionBusy,
|
|
2879
|
+
onClick: () => runAction(() => controller.duplicate(task)),
|
|
2791
2880
|
children: "⧉ 复制"
|
|
2792
2881
|
}),
|
|
2793
2882
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2794
2883
|
type: "button",
|
|
2795
2884
|
className: "dsh-atb-detail-edit",
|
|
2796
2885
|
title: "把此任务的配置(含清单)保存为模板,新建任务时可用",
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
},
|
|
2886
|
+
disabled: actionBusy,
|
|
2887
|
+
onClick: () => runAction(async () => {
|
|
2888
|
+
if (await controller.saveAsTemplate(task)) showAlert("已存为模板(新建任务 ▼ 下拉可用,可在模板管理中改名)");
|
|
2889
|
+
}),
|
|
2802
2890
|
children: "⌗ 存为模板"
|
|
2803
2891
|
}),
|
|
2804
2892
|
canRun && task.branch !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2805
2893
|
type: "button",
|
|
2806
2894
|
className: "dsh-atb-detail-run",
|
|
2807
2895
|
title: "续跑:保留现有 worktree 与分支(上次的改动和提交都在原处),在其上继续执行;默认「立即执行」会重置为全新基线",
|
|
2808
|
-
|
|
2896
|
+
disabled: actionBusy,
|
|
2897
|
+
onClick: () => runAction(() => controller.run(task.id, true)),
|
|
2809
2898
|
children: "↻ 续跑"
|
|
2810
2899
|
}),
|
|
2811
2900
|
canRun && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2812
2901
|
type: "button",
|
|
2813
2902
|
className: "dsh-atb-detail-run",
|
|
2814
2903
|
title: task.model !== void 0 ? `新会话执行(${task.model.model})` : "新会话执行(默认模型)",
|
|
2815
|
-
|
|
2904
|
+
disabled: actionBusy,
|
|
2905
|
+
onClick: () => runAction(() => controller.run(task.id)),
|
|
2816
2906
|
children: "▶ 立即执行"
|
|
2817
2907
|
}),
|
|
2818
2908
|
runningExecution !== void 0 && (confirmCancel ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -2986,18 +3076,18 @@ window.__ModuleLoader__.load({
|
|
|
2986
3076
|
placeholder: "以用户身份留言(agent 开工前会读)…",
|
|
2987
3077
|
onChange: (e) => setComment(e.target.value),
|
|
2988
3078
|
onKeyDown: (e) => {
|
|
2989
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && comment.trim().length > 0) {
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
}
|
|
3079
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && comment.trim().length > 0) controller.comment(task.id, comment).then((ok) => {
|
|
3080
|
+
if (ok) setComment("");
|
|
3081
|
+
});
|
|
2993
3082
|
}
|
|
2994
3083
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2995
3084
|
type: "button",
|
|
2996
3085
|
className: "dsh-atb-composer-send",
|
|
2997
3086
|
disabled: comment.trim().length === 0,
|
|
2998
3087
|
onClick: () => {
|
|
2999
|
-
controller.comment(task.id, comment)
|
|
3000
|
-
|
|
3088
|
+
controller.comment(task.id, comment).then((ok) => {
|
|
3089
|
+
if (ok) setComment("");
|
|
3090
|
+
});
|
|
3001
3091
|
},
|
|
3002
3092
|
children: "发表"
|
|
3003
3093
|
})]
|
|
@@ -3261,6 +3351,7 @@ window.__ModuleLoader__.load({
|
|
|
3261
3351
|
checked: false
|
|
3262
3352
|
})));
|
|
3263
3353
|
const titleRef = (0, react.useRef)(null);
|
|
3354
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
3264
3355
|
(0, react.useEffect)(() => {
|
|
3265
3356
|
titleRef.current?.focus();
|
|
3266
3357
|
const onKey = (e) => {
|
|
@@ -3280,10 +3371,11 @@ window.__ModuleLoader__.load({
|
|
|
3280
3371
|
face().then((roster) => {
|
|
3281
3372
|
setPresets(roster.presets);
|
|
3282
3373
|
setPresetDefault(roster.defaultId);
|
|
3283
|
-
if (
|
|
3374
|
+
if (!editing && initialPreset === "" && roster.defaultId !== void 0) setPresetId(roster.defaultId);
|
|
3284
3375
|
}).catch(() => setPresets([]));
|
|
3285
3376
|
}, [
|
|
3286
3377
|
controller,
|
|
3378
|
+
editing,
|
|
3287
3379
|
task?.presetId,
|
|
3288
3380
|
initialPreset
|
|
3289
3381
|
]);
|
|
@@ -3311,12 +3403,13 @@ window.__ModuleLoader__.load({
|
|
|
3311
3403
|
text: r.text.trim()
|
|
3312
3404
|
})).filter((r) => r.text.length > 0);
|
|
3313
3405
|
const submit = () => {
|
|
3314
|
-
if (!valid) return;
|
|
3406
|
+
if (!valid || busy) return;
|
|
3315
3407
|
const picked = model !== "" ? JSON.parse(model) : void 0;
|
|
3316
3408
|
const isolationOut = isolationPayload();
|
|
3317
3409
|
const presetOut = presetPayload();
|
|
3318
3410
|
const rows = filledRows();
|
|
3319
|
-
|
|
3411
|
+
setBusy(true);
|
|
3412
|
+
(editing ? controller.update(task.id, task.version, {
|
|
3320
3413
|
title,
|
|
3321
3414
|
description,
|
|
3322
3415
|
prompt,
|
|
@@ -3330,8 +3423,7 @@ window.__ModuleLoader__.load({
|
|
|
3330
3423
|
...isolationOut !== void 0 && !isolationLocked ? { isolation: isolationOut } : {},
|
|
3331
3424
|
presetId: presetOut ?? null,
|
|
3332
3425
|
checklist: rows.length > 0 ? rows : null
|
|
3333
|
-
})
|
|
3334
|
-
else controller.create({
|
|
3426
|
+
}) : controller.create({
|
|
3335
3427
|
title,
|
|
3336
3428
|
workspaceId,
|
|
3337
3429
|
urgency,
|
|
@@ -3345,50 +3437,52 @@ window.__ModuleLoader__.load({
|
|
|
3345
3437
|
...isolationOut !== void 0 ? { isolation: isolationOut } : {},
|
|
3346
3438
|
...presetOut !== void 0 ? { presetId: presetOut } : {},
|
|
3347
3439
|
...rows.length > 0 ? { checklist: rows.map((r) => r.text) } : {}
|
|
3348
|
-
});
|
|
3440
|
+
})).catch(() => void 0).finally(() => setBusy(false));
|
|
3349
3441
|
};
|
|
3350
3442
|
/** Save the form, then immediately trigger a manual run of the task. */
|
|
3351
3443
|
const submitAndRun = () => {
|
|
3352
|
-
if (!valid || runBlocked) return;
|
|
3444
|
+
if (!valid || runBlocked || busy) return;
|
|
3353
3445
|
const picked = model !== "" ? JSON.parse(model) : void 0;
|
|
3354
3446
|
const isolationOut = isolationPayload();
|
|
3355
3447
|
const presetOut = presetPayload();
|
|
3356
3448
|
const rows = filledRows();
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
mode
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3449
|
+
setBusy(true);
|
|
3450
|
+
(async () => {
|
|
3451
|
+
if (editing) {
|
|
3452
|
+
if (await controller.update(task.id, task.version, {
|
|
3453
|
+
title,
|
|
3454
|
+
description,
|
|
3455
|
+
prompt,
|
|
3456
|
+
urgency,
|
|
3457
|
+
workspaceId,
|
|
3458
|
+
execution: mode === "scheduled" ? {
|
|
3459
|
+
mode,
|
|
3460
|
+
cron: cron.trim()
|
|
3461
|
+
} : { mode },
|
|
3462
|
+
model: picked ?? null,
|
|
3463
|
+
...isolationOut !== void 0 && !isolationLocked ? { isolation: isolationOut } : {},
|
|
3464
|
+
presetId: presetOut ?? null,
|
|
3465
|
+
checklist: rows.length > 0 ? rows : null
|
|
3466
|
+
})) await controller.run(task.id);
|
|
3467
|
+
} else {
|
|
3468
|
+
const id = await controller.create({
|
|
3469
|
+
title,
|
|
3470
|
+
workspaceId,
|
|
3471
|
+
urgency,
|
|
3472
|
+
description: description.length > 0 ? description : void 0,
|
|
3473
|
+
prompt: prompt.length > 0 ? prompt : void 0,
|
|
3474
|
+
execution: mode === "scheduled" ? {
|
|
3475
|
+
mode,
|
|
3476
|
+
cron: cron.trim()
|
|
3477
|
+
} : { mode },
|
|
3478
|
+
model: picked,
|
|
3479
|
+
...isolationOut !== void 0 ? { isolation: isolationOut } : {},
|
|
3480
|
+
...presetOut !== void 0 ? { presetId: presetOut } : {},
|
|
3481
|
+
...rows.length > 0 ? { checklist: rows.map((r) => r.text) } : {}
|
|
3482
|
+
});
|
|
3483
|
+
if (id !== void 0) await controller.run(id);
|
|
3484
|
+
}
|
|
3485
|
+
})().catch(() => void 0).finally(() => setBusy(false));
|
|
3392
3486
|
};
|
|
3393
3487
|
const hint = !valid ? title.trim().length === 0 ? "请填写标题" : workspaceId === "" ? "请选择项目" : "Cron 表达式无效(分 时 日 月 周)" : mode === "scheduled" && nextRun !== null ? `下次运行 ${fmtTime(nextRun)}` : editing ? `保存后版本 v${task.version} → v${task.version + 1}` : "创建后项目内会话可认领执行";
|
|
3394
3488
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -3654,8 +3748,8 @@ window.__ModuleLoader__.load({
|
|
|
3654
3748
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3655
3749
|
type: "button",
|
|
3656
3750
|
className: "dsh-atb-btn",
|
|
3657
|
-
disabled: !valid || runBlocked,
|
|
3658
|
-
title: runBlocked ? "任务正在执行中,不能重复发起" : "保存后立即发起执行(新会话)",
|
|
3751
|
+
disabled: !valid || runBlocked || busy,
|
|
3752
|
+
title: runBlocked ? "任务正在执行中,不能重复发起" : busy ? "正在提交…" : "保存后立即发起执行(新会话)",
|
|
3659
3753
|
onClick: submitAndRun,
|
|
3660
3754
|
children: "⚡ 立即执行"
|
|
3661
3755
|
}),
|
|
@@ -3663,7 +3757,7 @@ window.__ModuleLoader__.load({
|
|
|
3663
3757
|
type: "button",
|
|
3664
3758
|
className: "dsh-atb-btn",
|
|
3665
3759
|
"data-primary": "true",
|
|
3666
|
-
disabled: !valid,
|
|
3760
|
+
disabled: !valid || busy,
|
|
3667
3761
|
onClick: submit,
|
|
3668
3762
|
children: editing ? "保存修改" : "创建任务"
|
|
3669
3763
|
})
|
|
@@ -4045,7 +4139,7 @@ window.__ModuleLoader__.load({
|
|
|
4045
4139
|
className: "dsh-atb-btn",
|
|
4046
4140
|
"data-primary": "true",
|
|
4047
4141
|
"data-danger": mode === "replace" && confirmReplace ? "true" : void 0,
|
|
4048
|
-
disabled: plan === void 0 || busy
|
|
4142
|
+
disabled: plan === void 0 || busy,
|
|
4049
4143
|
onClick: commit,
|
|
4050
4144
|
children: mode === "replace" && confirmReplace ? "确认整册替换" : "执行导入"
|
|
4051
4145
|
})]
|
|
@@ -4235,35 +4329,6 @@ window.__ModuleLoader__.load({
|
|
|
4235
4329
|
*
|
|
4236
4330
|
* @module dsh-taskboard/client/board/TaskBoard
|
|
4237
4331
|
*/
|
|
4238
|
-
/** Column labels. */
|
|
4239
|
-
const COLUMN_LABELS = {
|
|
4240
|
-
backlog: "待规划",
|
|
4241
|
-
todo: "待办",
|
|
4242
|
-
in_progress: "进行中",
|
|
4243
|
-
in_review: "待验收",
|
|
4244
|
-
done: "已完成",
|
|
4245
|
-
canceled: "已取消",
|
|
4246
|
-
archived: "已归档"
|
|
4247
|
-
};
|
|
4248
|
-
/** Urgency chip labels. */
|
|
4249
|
-
const URGENCY_LABELS = {
|
|
4250
|
-
urgent: "紧急",
|
|
4251
|
-
normal: "一般",
|
|
4252
|
-
relaxed: "不急"
|
|
4253
|
-
};
|
|
4254
|
-
/** Format an epoch ms as a short local stamp. */
|
|
4255
|
-
function fmtTime(ms) {
|
|
4256
|
-
if (ms === void 0) return "";
|
|
4257
|
-
const d = new Date(ms);
|
|
4258
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
4259
|
-
return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
4260
|
-
}
|
|
4261
|
-
/** A claim idle for longer than this is highlighted as stale (ms). */
|
|
4262
|
-
const STALE_CLAIM_MS = 30 * 6e4;
|
|
4263
|
-
/** Whether the task's claim is stale (in_progress, held, idle too long). */
|
|
4264
|
-
function isStaleClaim(task, now) {
|
|
4265
|
-
return task.status === "in_progress" && task.claimedAt !== void 0 && now - task.claimedAt > 18e5;
|
|
4266
|
-
}
|
|
4267
4332
|
/** Urgency sort rank (urgent first). */
|
|
4268
4333
|
const URGENCY_RANK = {
|
|
4269
4334
|
urgent: 0,
|
|
@@ -4344,7 +4409,7 @@ window.__ModuleLoader__.load({
|
|
|
4344
4409
|
},
|
|
4345
4410
|
children: "空白任务"
|
|
4346
4411
|
}),
|
|
4347
|
-
state.templates.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4412
|
+
state.templates.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4348
4413
|
type: "button",
|
|
4349
4414
|
className: "dsh-atb-newmenu-opt",
|
|
4350
4415
|
title: t.task.description !== void 0 && t.task.description.length > 0 ? t.task.description.slice(0, 120) : t.name,
|
|
@@ -4352,7 +4417,7 @@ window.__ModuleLoader__.load({
|
|
|
4352
4417
|
closeMenu();
|
|
4353
4418
|
controller.newFromTemplate(t.task);
|
|
4354
4419
|
},
|
|
4355
|
-
children:
|
|
4420
|
+
children: t.name
|
|
4356
4421
|
}, t.id)),
|
|
4357
4422
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "dsh-atb-newmenu-sep" }),
|
|
4358
4423
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -4424,7 +4489,7 @@ window.__ModuleLoader__.load({
|
|
|
4424
4489
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4425
4490
|
className: "dsh-atb-dot",
|
|
4426
4491
|
"data-urgency": u
|
|
4427
|
-
}),
|
|
4492
|
+
}), URGENCY_LABEL[u]]
|
|
4428
4493
|
}, u)),
|
|
4429
4494
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4430
4495
|
type: "button",
|
|
@@ -4567,7 +4632,7 @@ window.__ModuleLoader__.load({
|
|
|
4567
4632
|
task: selected,
|
|
4568
4633
|
controller,
|
|
4569
4634
|
now
|
|
4570
|
-
})
|
|
4635
|
+
}, selected.id)
|
|
4571
4636
|
}),
|
|
4572
4637
|
state.composerOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskFormModal, {
|
|
4573
4638
|
controller,
|
|
@@ -4776,15 +4841,18 @@ window.__ModuleLoader__.load({
|
|
|
4776
4841
|
* conversation content while the board is active. Toggling rides a data
|
|
4777
4842
|
* attribute on <html> — no React involvement in the shell.
|
|
4778
4843
|
*
|
|
4779
|
-
* Column matching is
|
|
4780
|
-
* `data-pane="conversation"`; the
|
|
4781
|
-
* dropped data-pane
|
|
4782
|
-
* (`pI_x6G_centerCol`) —
|
|
4783
|
-
*
|
|
4844
|
+
* Column matching is TRIPLE-generation: the dev shell marks the column
|
|
4845
|
+
* with `data-pane="conversation"`; the official layout shell
|
|
4846
|
+
* (dsh-client-ui-layout) dropped data-pane and uses CSS-Module hashed
|
|
4847
|
+
* class names (`pI_x6G_centerCol`) — and DSH Desktop's non-compat
|
|
4848
|
+
* (extended) mode disables the official layout row entirely, owning the
|
|
4849
|
+
* columns itself (`main.dshDesktopConversationSurface`, 0.5.2) — the
|
|
4850
|
+
* fallbacks keep all three mounting, exactly like sidebar-entry's column
|
|
4851
|
+
* selector.
|
|
4784
4852
|
*
|
|
4785
4853
|
* @module dsh-taskboard/client/board-mount
|
|
4786
4854
|
*/
|
|
4787
|
-
const CONVERSATION_COLUMN_SELECTOR = "[data-pane=\"conversation\"], [class*=\"centerCol\"]";
|
|
4855
|
+
const CONVERSATION_COLUMN_SELECTOR = "[data-pane=\"conversation\"], [class*=\"centerCol\"], .dshDesktopConversationSurface";
|
|
4788
4856
|
const ACTIVE_ATTR = "data-dsh-atb-active";
|
|
4789
4857
|
/** Sibling panels' activation attributes, evicted when this board opens. */
|
|
4790
4858
|
const OTHER_ACTIVE_ATTRS = ["data-dsh-taskboard-active", "data-dsh-ssh-active"];
|
|
@@ -4917,7 +4985,7 @@ window.__ModuleLoader__.load({
|
|
|
4917
4985
|
const controller = new BoardController(createClient());
|
|
4918
4986
|
const connection = ctx.get?.("connection");
|
|
4919
4987
|
if (connection !== void 0) {
|
|
4920
|
-
controller.
|
|
4988
|
+
controller.installModelCatalog(async () => {
|
|
4921
4989
|
const response = await connection.api.llm.models({});
|
|
4922
4990
|
if (!response.result.ok) return [];
|
|
4923
4991
|
const out = [];
|
|
@@ -4927,8 +4995,8 @@ window.__ModuleLoader__.load({
|
|
|
4927
4995
|
name: model.name
|
|
4928
4996
|
});
|
|
4929
4997
|
return out;
|
|
4930
|
-
};
|
|
4931
|
-
controller.
|
|
4998
|
+
});
|
|
4999
|
+
controller.installPresetRoster(async () => {
|
|
4932
5000
|
const list = connection.api.agentPresets;
|
|
4933
5001
|
if (list === void 0) return { presets: [] };
|
|
4934
5002
|
const response = await list.list({});
|
|
@@ -4942,7 +5010,7 @@ window.__ModuleLoader__.load({
|
|
|
4942
5010
|
presets,
|
|
4943
5011
|
...def !== void 0 ? { defaultId: def.id } : {}
|
|
4944
5012
|
};
|
|
4945
|
-
};
|
|
5013
|
+
});
|
|
4946
5014
|
}
|
|
4947
5015
|
controller.installSessionJumper(createSessionJumper({
|
|
4948
5016
|
getSessions: () => ctx.get?.("sessions"),
|