dsh-taskboard 0.4.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/lib/client.js +217 -34
- package/lib/host/routes.js +30 -3
- package/lib/host/routes.js.map +1 -1
- package/lib/host/store.js.map +1 -1
- package/lib/host/tools.js +4 -4
- package/lib/host/tools.js.map +1 -1
- package/lib/shared/api.js.map +1 -1
- package/lib/shared/protocol.js +26 -4
- package/lib/shared/protocol.js.map +1 -1
- package/package.json +7 -7
- package/src/client/api.ts +8 -0
- package/src/client/board/SettingsModal.tsx +84 -0
- package/src/client/board/TaskBoard.tsx +40 -2
- package/src/client/board/TaskFormModal.tsx +9 -8
- package/src/client/controller.ts +26 -21
- package/src/client/styles.ts +4 -0
- package/src/host/routes.ts +36 -2
- package/src/host/store.ts +1 -1
- package/src/host/tools.ts +7 -3
- package/src/shared/api.ts +10 -1
- package/src/shared/protocol.ts +48 -5
- package/src/shared/version.ts +1 -1
package/README.md
CHANGED
|
@@ -144,7 +144,8 @@ agent:
|
|
|
144
144
|
**执行**
|
|
145
145
|
- 手动执行或 cron 定时:每次执行在任务项目内新建全新会话(干净上下文、可指定模型、可指定 preset);开场两条消息同一回合送达——插件上下文行携带任务框架与交接协议(含失败回退路径),卡片内容(提示词或标题+描述)以正常用户消息呈现
|
|
146
146
|
- **任务级 preset(0.3.3)**:新建/编辑表单「执行模式(preset)」下拉——执行会话按该 preset 组合(工具集与人设由此而来,对齐 GUI 新会话的组合方式);默认预选部署默认 preset,也可选「跟随部署默认」;preset 损坏时执行直接失败并把原因写进执行记录(不产出半组合会话);随时可改,下轮执行生效
|
|
147
|
-
- **Git Worktree 隔离执行(0.3.0
|
|
147
|
+
- **Git Worktree 隔离执行(0.3.0)**:任务级开关(0.5.0 起新建任务的默认值由「看板设置」统一决定,出厂默认原目录执行),每次执行在 `<项目>/.dsh-worktrees/<任务ID>` 独立 worktree 上进行,分支 `task/<标题>+<任务ID>`(首次创建后定死,改名不改);执行会话归属项目根目录(分组、工具与文件沙箱完整可用——DSH 要求会话 cwd 即工作区根,0.3.2 修正),worktree 路径与边界纪律在开场指令中明确下达;结算自动采集提交列表 / 未提交修改警告 / 改动统计;非 git 项目或 git 不可用时自动降级原目录执行(执行记录注明降级原因,台账与执行主流程永不因 git 失败而失败);验收时详情页一键 `--no-ff` 合并到主工作区(主区脏或冲突原样报告,不自动解决)、删除 worktree(有未提交修改时拒绝)、可选删分支;支持「↻ 续跑」在现有 worktree/分支上继续执行(保留上次改动与提交)
|
|
148
|
+
- **看板设置(0.5.0)**:顶栏「🛠 设置」——选择新建任务默认怎么执行(🌿 Worktree 隔离 / 📁 原目录执行,出厂默认后者)。保存后新建的任务都按它来;之后改设置,已建好的任务不受影响
|
|
148
149
|
> Worktree 隔离是协作约定而非沙箱:执行会话拥有完整工具权限,隔离依赖分支约定,不适用于运行不可信代码的场景。
|
|
149
150
|
- host 侧调度:关掉浏览器照常触发;错过窗口跳过不补跑
|
|
150
151
|
- 乐观并发(ifVersion)+ 完整归因(谁改的、哪个会话执行的)
|
|
@@ -213,6 +214,13 @@ node scripts/screenshot.mjs # 重新生成 img/ 截图(需本机 Edge)
|
|
|
213
214
|
|
|
214
215
|
## 升级日志
|
|
215
216
|
|
|
217
|
+
### 0.5.0
|
|
218
|
+
|
|
219
|
+
- **新增「看板设置」**:看板顶栏多了个「🛠 设置」按钮,可以选择新建任务默认怎么执行——在独立分支里隔离着干,或者直接在项目文件夹里干。保存后,新建的任务都按这个来;已经建好的任务不受影响
|
|
220
|
+
- **新建任务默认改为「直接在项目文件夹执行」**:以前新任务默认会在独立分支里执行,现在默认不开分支、直接在项目文件夹干活。习惯分支隔离的团队,到「🛠 设置」里改回来就行
|
|
221
|
+
- **导出按钮合并成一个**:原来的「⬇ JSON」「⬇ CSV」两个按钮合并成一个「⬇ 导出」,点开再选要哪种格式
|
|
222
|
+
- 新建任务时不再记住上一次的隔离选择,统一跟着「看板设置」走
|
|
223
|
+
|
|
216
224
|
### 0.4.5
|
|
217
225
|
|
|
218
226
|
- **移除 0.4.4 引入的「每 2 秒自动检查样式」机制**:上一版给样式表打的归属标记已经从根上解决了误删问题——其他插件更新时不会再删看板的样式,周期性检查只是多余的保险。现在去掉这个常驻的后台定时器,页面少一份轮询;防掉样式的效果不变(归属标记仍在,本插件自己热更新后也会重新挂回样式)
|
package/lib/client.js
CHANGED
|
@@ -60,6 +60,8 @@ window.__ModuleLoader__.load({
|
|
|
60
60
|
templates: () => unwrap(fetch("/dsh-taskboard/templates")),
|
|
61
61
|
templateUpsert: (body) => post("/dsh-taskboard/templates", body),
|
|
62
62
|
templateDelete: (id) => post("/dsh-taskboard/templates/delete", { id }),
|
|
63
|
+
settings: () => unwrap(fetch("/dsh-taskboard/settings")),
|
|
64
|
+
updateSettings: (body) => post("/dsh-taskboard/settings/update", body),
|
|
63
65
|
stream(onChange, onGap) {
|
|
64
66
|
const es = new EventSource("/dsh-taskboard/events");
|
|
65
67
|
let revision;
|
|
@@ -134,6 +136,16 @@ window.__ModuleLoader__.load({
|
|
|
134
136
|
return TRANSITIONS[from].includes(to);
|
|
135
137
|
}
|
|
136
138
|
/**
|
|
139
|
+
* Factory-default isolation (0.5.0): 原目录执行. Applies when neither the
|
|
140
|
+
* task record nor the board setting (`BoardSettings.defaultIsolation`)
|
|
141
|
+
* says otherwise. Before 0.5.0 the implicit default was 'worktree'.
|
|
142
|
+
*/
|
|
143
|
+
const DEFAULT_ISOLATION = "none";
|
|
144
|
+
/** The effective default isolation for NEW tasks (board setting → factory default). */
|
|
145
|
+
function defaultIsolationOf(settings) {
|
|
146
|
+
return settings?.defaultIsolation ?? "none";
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
137
149
|
* Parse a five-field cron expression. Supported field syntax: star, star/step
|
|
138
150
|
* (`* / n` without spaces), a single number, an `a-b` range, and comma lists
|
|
139
151
|
* of those. Day-of-week accepts both 0 and 7 as Sunday (normalized to 0).
|
|
@@ -238,22 +250,6 @@ window.__ModuleLoader__.load({
|
|
|
238
250
|
//#region src/client/controller.ts
|
|
239
251
|
/** localStorage key for persisted view state (filters + sort). */
|
|
240
252
|
const VIEW_KEY = "dsh-taskboard-view-v1";
|
|
241
|
-
/** localStorage key for the remembered isolation toggle choice (0.3.0). */
|
|
242
|
-
const ISOLATION_KEY = "dsh-taskboard-isolation-v1";
|
|
243
|
-
/** Load the remembered default isolation (worktree unless explicitly turned off). */
|
|
244
|
-
function loadDefaultIsolation() {
|
|
245
|
-
try {
|
|
246
|
-
return localStorage.getItem(ISOLATION_KEY) === "none" ? "none" : "worktree";
|
|
247
|
-
} catch {
|
|
248
|
-
return "worktree";
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
/** Remember the isolation toggle choice across forms (best effort). */
|
|
252
|
-
function saveDefaultIsolation(mode) {
|
|
253
|
-
try {
|
|
254
|
-
localStorage.setItem(ISOLATION_KEY, mode);
|
|
255
|
-
} catch {}
|
|
256
|
-
}
|
|
257
253
|
/** Load the persisted view state (never throws; fresh on any parse error). */
|
|
258
254
|
function loadView() {
|
|
259
255
|
try {
|
|
@@ -294,7 +290,8 @@ window.__ModuleLoader__.load({
|
|
|
294
290
|
diagOpen: false,
|
|
295
291
|
templates: [],
|
|
296
292
|
tplManagerOpen: false,
|
|
297
|
-
importOpen: false
|
|
293
|
+
importOpen: false,
|
|
294
|
+
settingsOpen: false
|
|
298
295
|
};
|
|
299
296
|
}
|
|
300
297
|
/**
|
|
@@ -683,6 +680,29 @@ window.__ModuleLoader__.load({
|
|
|
683
680
|
closeDiagnostics() {
|
|
684
681
|
this.setState({ diagOpen: false });
|
|
685
682
|
}
|
|
683
|
+
/** Open the board-settings modal (0.5.0). */
|
|
684
|
+
openSettings() {
|
|
685
|
+
this.setState({ settingsOpen: true });
|
|
686
|
+
}
|
|
687
|
+
/** Close the board-settings modal. */
|
|
688
|
+
closeSettings() {
|
|
689
|
+
this.setState({ settingsOpen: false });
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Replace board settings (0.5.0). The host broadcasts a settings-updated
|
|
693
|
+
* frame; refresh() pulls ledger.settings so every open view follows.
|
|
694
|
+
* @returns whether the write succeeded.
|
|
695
|
+
*/
|
|
696
|
+
async updateSettings(body) {
|
|
697
|
+
try {
|
|
698
|
+
await this.client.updateSettings(body);
|
|
699
|
+
await this.refresh();
|
|
700
|
+
return true;
|
|
701
|
+
} catch (error) {
|
|
702
|
+
this.setState({ error: error instanceof Error ? error.message : String(error) });
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
686
706
|
/** Clean one orphan worktree (⚙ panel); refreshes the diagnostics payload. */
|
|
687
707
|
async cleanupOrphan(workspaceId, taskId) {
|
|
688
708
|
try {
|
|
@@ -1585,6 +1605,10 @@ window.__ModuleLoader__.load({
|
|
|
1585
1605
|
.dsh-atb-imp-row-status { font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); flex-shrink: 0; }
|
|
1586
1606
|
.dsh-atb-imp-result { font-size: 12px; color: var(--dsw-alias-state-success-primary, #30a46c); margin-top: 10px; }
|
|
1587
1607
|
.dsh-atb-badge[data-kind="checklist"] { color: var(--dsw-alias-label-secondary, inherit); }
|
|
1608
|
+
/* ---------- 0.5.0 board settings ---------- */
|
|
1609
|
+
.dsh-atb-set { max-width: 460px; width: min(460px, 92vw); }
|
|
1610
|
+
.dsh-atb-set .dsh-atb-mode-picker { margin-top: 8px; }
|
|
1611
|
+
.dsh-atb-set .dsh-atb-isolation-note { margin-top: 10px; }
|
|
1588
1612
|
`;
|
|
1589
1613
|
/** Style element id (stable since 0.1.x: hook for tests and debugging). */
|
|
1590
1614
|
const STYLE_ID = "dsh-taskboard-styles";
|
|
@@ -1865,7 +1889,7 @@ window.__ModuleLoader__.load({
|
|
|
1865
1889
|
* @module dsh-taskboard/shared/version
|
|
1866
1890
|
*/
|
|
1867
1891
|
/** The package version (must equal package.json "version"). */
|
|
1868
|
-
const PLUGIN_VERSION = "0.
|
|
1892
|
+
const PLUGIN_VERSION = "0.5.0";
|
|
1869
1893
|
|
|
1870
1894
|
//#endregion
|
|
1871
1895
|
//#region src/client/board/TaskCard.tsx
|
|
@@ -3231,7 +3255,7 @@ window.__ModuleLoader__.load({
|
|
|
3231
3255
|
const [presetId, setPresetId] = (0, react.useState)(initialPreset);
|
|
3232
3256
|
const [presets, setPresets] = (0, react.useState)([]);
|
|
3233
3257
|
const [presetDefault, setPresetDefault] = (0, react.useState)(void 0);
|
|
3234
|
-
const [isolation, setIsolation] = (0, react.useState)(task?.isolation ?? (prefill?.isolation === "none" ? "none" : prefill?.isolation === "worktree" ? "worktree" :
|
|
3258
|
+
const [isolation, setIsolation] = (0, react.useState)(task?.isolation ?? (prefill?.isolation === "none" ? "none" : prefill?.isolation === "worktree" ? "worktree" : defaultIsolationOf(state.ledger.settings)));
|
|
3235
3259
|
const [checkRows, setCheckRows] = (0, react.useState)(task?.checklist !== void 0 && task.checklist.length > 0 ? task.checklist.map((i) => ({ ...i })) : (prefill?.checklist ?? []).map((text) => ({
|
|
3236
3260
|
text,
|
|
3237
3261
|
checked: false
|
|
@@ -3271,10 +3295,12 @@ window.__ModuleLoader__.load({
|
|
|
3271
3295
|
const isolationLocked = editing && ((task.executions?.length ?? 0) > 0 || task.status === "in_progress");
|
|
3272
3296
|
const gitOk = controller.gitAvailable(workspaceId);
|
|
3273
3297
|
const isolationDisabled = isolationLocked || !gitOk;
|
|
3274
|
-
/**
|
|
3298
|
+
/**
|
|
3299
|
+
* Isolation payload for submit: undefined lets the HOST materialize the
|
|
3300
|
+
* current board default at creation (non-git projects degrade naturally).
|
|
3301
|
+
*/
|
|
3275
3302
|
const isolationPayload = () => {
|
|
3276
3303
|
if (!gitOk) return void 0;
|
|
3277
|
-
if (!editing) saveDefaultIsolation(isolation);
|
|
3278
3304
|
return isolation;
|
|
3279
3305
|
};
|
|
3280
3306
|
/** Preset payload: '' = follow the deployment default (submit omits). */
|
|
@@ -3649,6 +3675,133 @@ window.__ModuleLoader__.load({
|
|
|
3649
3675
|
});
|
|
3650
3676
|
}
|
|
3651
3677
|
|
|
3678
|
+
//#endregion
|
|
3679
|
+
//#region src/client/board/SettingsModal.tsx
|
|
3680
|
+
/**
|
|
3681
|
+
* Board-settings modal (0.5.0): the user-owned defaults applied when a NEW
|
|
3682
|
+
* task is created without an explicit choice. Currently one section — 默认执行
|
|
3683
|
+
* 隔离 (worktree vs original directory); further sections can slot into the
|
|
3684
|
+
* body below. Saving goes through the host route (whole-object replace) and
|
|
3685
|
+
* the SSE change stream refreshes every open view.
|
|
3686
|
+
*
|
|
3687
|
+
* @module dsh-taskboard/client/board/SettingsModal
|
|
3688
|
+
*/
|
|
3689
|
+
/** The isolation options with one-line hints (mirrors the task form). */
|
|
3690
|
+
const ISOLATION_OPTIONS = [{
|
|
3691
|
+
value: "none",
|
|
3692
|
+
name: "📁 原目录执行",
|
|
3693
|
+
hint: "不使用 git,直接在项目目录工作(出厂默认)"
|
|
3694
|
+
}, {
|
|
3695
|
+
value: "worktree",
|
|
3696
|
+
name: "🌿 Worktree 隔离",
|
|
3697
|
+
hint: "每次执行在独立 worktree 分支上进行(task/标题+ID),互不污染"
|
|
3698
|
+
}];
|
|
3699
|
+
/**
|
|
3700
|
+
* The 看板设置 modal: reads the live ledger settings, stages a local draft,
|
|
3701
|
+
* and writes back through the controller on save.
|
|
3702
|
+
* @param controller - the board controller.
|
|
3703
|
+
*/
|
|
3704
|
+
function SettingsModal({ controller }) {
|
|
3705
|
+
const current = controller.getSnapshot().ledger.settings?.defaultIsolation ?? "none";
|
|
3706
|
+
const [draft, setDraft] = (0, react.useState)(current);
|
|
3707
|
+
const dirty = draft !== current;
|
|
3708
|
+
const save = () => {
|
|
3709
|
+
controller.updateSettings({ defaultIsolation: draft }).then((ok) => {
|
|
3710
|
+
if (ok) controller.closeSettings();
|
|
3711
|
+
});
|
|
3712
|
+
};
|
|
3713
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3714
|
+
className: "dsh-atb-modal-backdrop",
|
|
3715
|
+
onClick: (e) => {
|
|
3716
|
+
if (e.target === e.currentTarget) controller.closeSettings();
|
|
3717
|
+
},
|
|
3718
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3719
|
+
className: "dsh-atb-modal dsh-atb-set",
|
|
3720
|
+
role: "dialog",
|
|
3721
|
+
"aria-modal": "true",
|
|
3722
|
+
"aria-label": "看板设置",
|
|
3723
|
+
children: [
|
|
3724
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3725
|
+
className: "dsh-atb-modal-head",
|
|
3726
|
+
children: [
|
|
3727
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3728
|
+
className: "dsh-atb-modal-headicon",
|
|
3729
|
+
children: "🛠"
|
|
3730
|
+
}),
|
|
3731
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3732
|
+
className: "dsh-atb-modal-headtext",
|
|
3733
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "看板设置" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "新建任务时应用的默认值(不影响已有任务)" })]
|
|
3734
|
+
}),
|
|
3735
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3736
|
+
type: "button",
|
|
3737
|
+
className: "dsh-atb-modal-close",
|
|
3738
|
+
"aria-label": "关闭",
|
|
3739
|
+
onClick: () => controller.closeSettings(),
|
|
3740
|
+
children: "✕"
|
|
3741
|
+
})
|
|
3742
|
+
]
|
|
3743
|
+
}),
|
|
3744
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3745
|
+
className: "dsh-atb-modal-body",
|
|
3746
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3747
|
+
className: "dsh-atb-diag-sec",
|
|
3748
|
+
children: [
|
|
3749
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: "默认执行隔离" }),
|
|
3750
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3751
|
+
className: "dsh-atb-mode-picker",
|
|
3752
|
+
children: ISOLATION_OPTIONS.map((o) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3753
|
+
type: "button",
|
|
3754
|
+
className: "dsh-atb-mode-opt",
|
|
3755
|
+
"data-on": draft === o.value,
|
|
3756
|
+
title: o.hint,
|
|
3757
|
+
onClick: () => setDraft(o.value),
|
|
3758
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3759
|
+
className: "dsh-atb-mode-name",
|
|
3760
|
+
children: o.name
|
|
3761
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3762
|
+
className: "dsh-atb-mode-hint",
|
|
3763
|
+
children: o.hint
|
|
3764
|
+
})]
|
|
3765
|
+
}, o.value))
|
|
3766
|
+
}),
|
|
3767
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3768
|
+
className: "dsh-atb-isolation-note",
|
|
3769
|
+
children: [
|
|
3770
|
+
"当前保存的默认:",
|
|
3771
|
+
current === "worktree" ? "🌿 Worktree 隔离" : "📁 原目录执行",
|
|
3772
|
+
"。 仅影响之后新建的任务;已有任务保持创建时的选择,非 git 项目运行时仍自动降级原目录。"
|
|
3773
|
+
]
|
|
3774
|
+
})
|
|
3775
|
+
]
|
|
3776
|
+
})
|
|
3777
|
+
}),
|
|
3778
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3779
|
+
className: "dsh-atb-modal-foot",
|
|
3780
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3781
|
+
className: "dsh-atb-modal-hint",
|
|
3782
|
+
children: dirty ? "有未保存的修改" : "与看板当前设置一致"
|
|
3783
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3784
|
+
className: "dsh-atb-modal-footbtns",
|
|
3785
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3786
|
+
type: "button",
|
|
3787
|
+
className: "dsh-atb-btn",
|
|
3788
|
+
onClick: () => controller.closeSettings(),
|
|
3789
|
+
children: "取消"
|
|
3790
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3791
|
+
type: "button",
|
|
3792
|
+
className: "dsh-atb-btn",
|
|
3793
|
+
"data-primary": "true",
|
|
3794
|
+
disabled: !dirty,
|
|
3795
|
+
onClick: save,
|
|
3796
|
+
children: "保存设置"
|
|
3797
|
+
})]
|
|
3798
|
+
})]
|
|
3799
|
+
})
|
|
3800
|
+
]
|
|
3801
|
+
})
|
|
3802
|
+
});
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3652
3805
|
//#endregion
|
|
3653
3806
|
//#region src/client/board/ImportModal.tsx
|
|
3654
3807
|
/**
|
|
@@ -4144,6 +4297,8 @@ window.__ModuleLoader__.load({
|
|
|
4144
4297
|
const { alert: showAlert, el: alertEl } = useAlert();
|
|
4145
4298
|
const [newMenuOpen, setNewMenuOpen] = (0, react.useState)(false);
|
|
4146
4299
|
const closeMenu = () => setNewMenuOpen(false);
|
|
4300
|
+
const [exportOpen, setExportOpen] = (0, react.useState)(false);
|
|
4301
|
+
const closeExport = () => setExportOpen(false);
|
|
4147
4302
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4148
4303
|
className: "dsh-atb-board",
|
|
4149
4304
|
children: [
|
|
@@ -4277,6 +4432,13 @@ window.__ModuleLoader__.load({
|
|
|
4277
4432
|
onClick: () => controller.toggleSecondary(),
|
|
4278
4433
|
children: state.secondaryOpen ? "返回看板" : "其它任务"
|
|
4279
4434
|
}),
|
|
4435
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4436
|
+
type: "button",
|
|
4437
|
+
className: "dsh-atb-btn",
|
|
4438
|
+
title: "看板设置:新建任务的默认执行隔离等",
|
|
4439
|
+
onClick: () => controller.openSettings(),
|
|
4440
|
+
children: "🛠 设置"
|
|
4441
|
+
}),
|
|
4280
4442
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4281
4443
|
type: "button",
|
|
4282
4444
|
className: "dsh-atb-btn",
|
|
@@ -4291,19 +4453,39 @@ window.__ModuleLoader__.load({
|
|
|
4291
4453
|
onClick: () => controller.openImport(),
|
|
4292
4454
|
children: "⬆ 导入"
|
|
4293
4455
|
}),
|
|
4294
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4456
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4457
|
+
className: "dsh-atb-newmenu",
|
|
4458
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4459
|
+
type: "button",
|
|
4460
|
+
className: "dsh-atb-btn",
|
|
4461
|
+
title: "导出台账:完整 JSON 备份或任务清单 CSV",
|
|
4462
|
+
onClick: () => setExportOpen(!exportOpen),
|
|
4463
|
+
children: "⬇ 导出 ▼"
|
|
4464
|
+
}), exportOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4465
|
+
className: "dsh-atb-newmenu-backdrop",
|
|
4466
|
+
onClick: closeExport
|
|
4467
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4468
|
+
className: "dsh-atb-newmenu-list",
|
|
4469
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4470
|
+
type: "button",
|
|
4471
|
+
className: "dsh-atb-newmenu-opt",
|
|
4472
|
+
title: "完整台账备份(含执行历史与看板设置),可用于导入恢复",
|
|
4473
|
+
onClick: () => {
|
|
4474
|
+
closeExport();
|
|
4475
|
+
controller.exportJson();
|
|
4476
|
+
},
|
|
4477
|
+
children: "完整台账(JSON)"
|
|
4478
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4479
|
+
type: "button",
|
|
4480
|
+
className: "dsh-atb-newmenu-opt",
|
|
4481
|
+
title: "任务清单表格(Excel 可直接打开,中文已加 BOM)",
|
|
4482
|
+
onClick: () => {
|
|
4483
|
+
closeExport();
|
|
4484
|
+
controller.exportCsv();
|
|
4485
|
+
},
|
|
4486
|
+
children: "任务清单(CSV)"
|
|
4487
|
+
})]
|
|
4488
|
+
})] })]
|
|
4307
4489
|
}),
|
|
4308
4490
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
4309
4491
|
className: "dsh-atb-ver",
|
|
@@ -4392,6 +4574,7 @@ window.__ModuleLoader__.load({
|
|
|
4392
4574
|
task: state.editingId === void 0 ? void 0 : state.ledger.tasks.find((t) => t.id === state.editingId)
|
|
4393
4575
|
}),
|
|
4394
4576
|
state.diagOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DiagnosticsPanel, { controller }),
|
|
4577
|
+
state.settingsOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsModal, { controller }),
|
|
4395
4578
|
state.importOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImportModal, { controller }),
|
|
4396
4579
|
state.tplManagerOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TemplateManager, { controller }),
|
|
4397
4580
|
alertEl
|
package/lib/host/routes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { asIsolation, asStatus, asUrgency, canTransition, checklistFromTexts, newCommentId, newTaskId, normalizeBody, normalizeChecklist, normalizeExecution, normalizeModel, normalizePrompt, normalizeTitle, summarize, syncClaim, validateLedgerImport } from "../shared/protocol.js";
|
|
1
|
+
import { asBoardSettings, asIsolation, asStatus, asUrgency, canTransition, checklistFromTexts, defaultIsolationOf, newCommentId, newTaskId, normalizeBody, normalizeChecklist, normalizeExecution, normalizeModel, normalizePrompt, normalizeTitle, summarize, syncClaim, validateLedgerImport } from "../shared/protocol.js";
|
|
2
2
|
import { WORKTREE_DIR, worktreePathOf } from "./git.js";
|
|
3
3
|
import { ROUTE_PREFIX, SSE_PATH } from "../shared/api.js";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -278,6 +278,14 @@ function registerTaskboardRoutes(ctx, options) {
|
|
|
278
278
|
});
|
|
279
279
|
return;
|
|
280
280
|
}
|
|
281
|
+
if (pathname === `/dsh-taskboard/settings`) {
|
|
282
|
+
await store.load();
|
|
283
|
+
json(res, {
|
|
284
|
+
ok: true,
|
|
285
|
+
value: store.snapshot().settings ?? {}
|
|
286
|
+
});
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
281
289
|
const taskMatch = pathname.match(new RegExp(`^${ROUTE_PREFIX}/tasks/([^/]+)$`));
|
|
282
290
|
if (taskMatch !== null) {
|
|
283
291
|
const task = store.get(taskMatch[1]);
|
|
@@ -320,7 +328,7 @@ function registerTaskboardRoutes(ctx, options) {
|
|
|
320
328
|
const execution = normalizeExecution(body.execution ?? {}, options.now());
|
|
321
329
|
const model = body.model === void 0 ? void 0 : checkModel(body.model, options.modelProviders);
|
|
322
330
|
const isolationRaw = str(body, "isolation");
|
|
323
|
-
const isolation = isolationRaw === null ?
|
|
331
|
+
const isolation = isolationRaw === null ? defaultIsolationOf(store.snapshot().settings) : asIsolation(isolationRaw);
|
|
324
332
|
const presetId = normalizePresetId(str(body, "presetId"));
|
|
325
333
|
let checklist = void 0;
|
|
326
334
|
if (body.checklist !== void 0) {
|
|
@@ -340,7 +348,7 @@ function registerTaskboardRoutes(ctx, options) {
|
|
|
340
348
|
blocked: false,
|
|
341
349
|
execution,
|
|
342
350
|
model,
|
|
343
|
-
|
|
351
|
+
isolation,
|
|
344
352
|
...presetId !== void 0 ? { presetId } : {},
|
|
345
353
|
...checklist !== void 0 ? { checklist } : {},
|
|
346
354
|
version: 1,
|
|
@@ -752,6 +760,8 @@ function registerTaskboardRoutes(ctx, options) {
|
|
|
752
760
|
if (mode === "replace") {
|
|
753
761
|
replacedTotal = ledger.tasks.length;
|
|
754
762
|
ledger.tasks = structuredClone(imported);
|
|
763
|
+
if (plan.settings !== void 0) ledger.settings = structuredClone(plan.settings);
|
|
764
|
+
else delete ledger.settings;
|
|
755
765
|
return ledger.tasks;
|
|
756
766
|
}
|
|
757
767
|
const byId = new Map(ledger.tasks.map((t) => [t.id, t]));
|
|
@@ -806,6 +816,23 @@ function registerTaskboardRoutes(ctx, options) {
|
|
|
806
816
|
}
|
|
807
817
|
return;
|
|
808
818
|
}
|
|
819
|
+
if (pathname === `/dsh-taskboard/settings/update`) {
|
|
820
|
+
try {
|
|
821
|
+
const next = asBoardSettings(body);
|
|
822
|
+
await store.mutate("settings-updated", (ledger) => {
|
|
823
|
+
ledger.settings = next;
|
|
824
|
+
return [];
|
|
825
|
+
});
|
|
826
|
+
json(res, {
|
|
827
|
+
ok: true,
|
|
828
|
+
value: next
|
|
829
|
+
});
|
|
830
|
+
} catch (error) {
|
|
831
|
+
const f = toFail(error);
|
|
832
|
+
json(res, f.res, f.status);
|
|
833
|
+
}
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
809
836
|
res.writeHead(404);
|
|
810
837
|
res.end();
|
|
811
838
|
} catch (error) {
|