@godv61/dsh-task-engine 0.22.3 → 0.22.4
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/.p0-test.mjs +42 -3
- package/README.md +2 -1
- package/docs/manual.html +1 -1
- package/lib/client.d.ts +6 -0
- package/lib/client.js +142 -20
- package/lib/client.js.map +3 -3
- package/lib/controller.d.ts +2 -0
- package/lib/controller.js +8 -2
- package/lib/controller.js.map +1 -1
- package/lib/dev-task.js +33 -12
- package/lib/dev-task.js.map +1 -1
- package/package.json +6 -2
package/.p0-test.mjs
CHANGED
|
@@ -33,6 +33,7 @@ async function assertThrows(fn, fragment, msg) {
|
|
|
33
33
|
|
|
34
34
|
function makeFs(initial = {}) {
|
|
35
35
|
const files = new Map(Object.entries(initial))
|
|
36
|
+
const versions = new Map()
|
|
36
37
|
return {
|
|
37
38
|
async resolve(relPath, opts) {
|
|
38
39
|
const base = opts && opts.cwd ? String(opts.cwd).replace(/\\/g, '/') : ''
|
|
@@ -42,8 +43,31 @@ function makeFs(initial = {}) {
|
|
|
42
43
|
async readText(target) {
|
|
43
44
|
return files.get(target.targetKey)
|
|
44
45
|
},
|
|
45
|
-
async
|
|
46
|
-
|
|
46
|
+
async lstat(path, opts) {
|
|
47
|
+
const base = opts && opts.cwd ? String(opts.cwd).replace(/\\/g, '/') : ''
|
|
48
|
+
const key = base ? `${base}/${path}` : path
|
|
49
|
+
if (!files.has(key)) return undefined
|
|
50
|
+
return { version: `v${versions.get(key) ?? 0}` }
|
|
51
|
+
},
|
|
52
|
+
async writeText(target, content, expected, signal, sandboxPolicy) {
|
|
53
|
+
const key = target.targetKey
|
|
54
|
+
if (expected !== undefined && expected !== null) {
|
|
55
|
+
if (expected.kind === 'createIfAbsent' && files.has(key)) {
|
|
56
|
+
const err = new Error('already exists')
|
|
57
|
+
err.code = 'FS_NOT_OBSERVED'
|
|
58
|
+
throw err
|
|
59
|
+
}
|
|
60
|
+
if (expected.kind === 'replaceIfVersion') {
|
|
61
|
+
const current = `v${versions.get(key) ?? 0}`
|
|
62
|
+
if (!files.has(key) || current !== expected.version) {
|
|
63
|
+
const err = new Error('stale version')
|
|
64
|
+
err.code = 'FS_STALE_VERSION'
|
|
65
|
+
throw err
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
files.set(key, content)
|
|
70
|
+
versions.set(key, (versions.get(key) ?? 0) + 1)
|
|
47
71
|
},
|
|
48
72
|
_files: files,
|
|
49
73
|
}
|
|
@@ -152,13 +176,25 @@ await assertThrows(
|
|
|
152
176
|
const exe = await registered(fs)
|
|
153
177
|
const prop10 = await exe({ operation: 'init', phase: 'propose', content: '# new' }, EXEC)
|
|
154
178
|
const hash10 = /content hash: ([0-9a-f]{64})/.exec(prop10)[1]
|
|
179
|
+
const existingHash10 = /existing hash: ([0-9a-f]{64})/.exec(prop10)[1]
|
|
180
|
+
assert(existingHash10 !== undefined, 'propose returns the existing-file hash when the file exists')
|
|
155
181
|
await assertThrows(
|
|
156
182
|
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10 }, EXEC),
|
|
183
|
+
'requires existing_hash',
|
|
184
|
+
'apply without existing_hash on an existing file is rejected',
|
|
185
|
+
)
|
|
186
|
+
await assertThrows(
|
|
187
|
+
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10, existing_hash: '0'.repeat(64) }, EXEC),
|
|
188
|
+
'existing_hash mismatch',
|
|
189
|
+
'apply with a stale existing_hash is rejected (TOCTOU)',
|
|
190
|
+
)
|
|
191
|
+
await assertThrows(
|
|
192
|
+
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10, existing_hash: existingHash10 }, EXEC),
|
|
157
193
|
'protected',
|
|
158
194
|
'apply without overwrite on an existing file is rejected',
|
|
159
195
|
)
|
|
160
196
|
await assertThrows(
|
|
161
|
-
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10, overwrite: true }, EXEC),
|
|
197
|
+
() => exe({ operation: 'init', phase: 'apply', content: '# new', expected_hash: hash10, existing_hash: existingHash10, overwrite: true }, EXEC),
|
|
162
198
|
'approval',
|
|
163
199
|
'overwrite requires an approval service',
|
|
164
200
|
)
|
|
@@ -667,6 +703,9 @@ function memProbe(files) {
|
|
|
667
703
|
ctl.registerWorkspace('D:/proj/one')
|
|
668
704
|
assert(ctl.checkedPath('D:/PROJ/one') === 'D:/PROJ/one', 'registered workspace passes under any case')
|
|
669
705
|
await assertThrows(() => ctl.checkedPath('D:/other-project'), 'not registered', 'unregistered workspace is rejected once the registry is active')
|
|
706
|
+
ctl.enableStrictWorkspaces()
|
|
707
|
+
await assertThrows(() => ctl.checkedPath('D:/still-unregistered'), 'not registered', 'strict mode keeps rejecting unregistered workspaces')
|
|
708
|
+
assert(ctl.checkedPath('D:/proj/one') === 'D:/proj/one', 'strict mode still admits registered workspaces')
|
|
670
709
|
}
|
|
671
710
|
|
|
672
711
|
console.log(`\nP0 acceptance: ${passed} checks passed`)
|
package/README.md
CHANGED
|
@@ -186,4 +186,5 @@ dsh plugin --profile <name> add .
|
|
|
186
186
|
- **审计和发布质量(0.22.0)**:① 流程快照 hash——任务创建时固化 `config` 的 SHA-256,工具与提交钩子读任务记录时校验,被手改的快照一律拒绝继续;② 提交钩子完整性检测——新增 `dev_task verify_hook`,比对 `.git/hooks/commit-msg` 与内置门禁的 hash,被替换/篡改立即报错;③ 风险降级审批——新增 `set_risk`,`high_risk → standard` 必须人工批准并落 `risk_downgrades` 审计记录;④ 验证回执绑定项目根——receipt 记录 `root`,与任务 workspace 绑定;⑤ 内置规则指纹锁定——创建时固化内置规则内容指纹,包升级后 `status` 报 `bindings_drift` 而非静默换规则;⑥ 多项目 / 多语言 / 多任务测试矩阵补强。
|
|
187
187
|
- **沙箱写入修复(0.22.1)**:`dev_task` 的文件写入此前没有携带按调用传递的沙箱策略(`writeText` 的 `sandboxPolicy` 参数),在 DSH 文件沙箱下会把 workspace 内的任务记录写入误判为越界而拒绝(`file access denied under workspace-write mode`,且会话策略变化无法影响它);现在每次写入显式携带 `{ mode, workspaceRoot: 会话目录 }`,并新增 `sandbox_permissions` 参数(`workspace-write` / `danger-full-access`)作为被拒后的一次性升级路径。
|
|
188
188
|
- **质量修补(0.22.2,采纳 codex 评审五项)**:① client typecheck 修复——`project.ts` 不再依赖 `node:path`,浏览器面可完整类型检查;② sandbox 升级补审批——`sandbox_permissions` 必须与 `justification` 成对出现,`danger-full-access` 需人工一次批准,无审批服务即拒绝;③ Remote 任意路径设防——工作台 Remote 拒绝非绝对路径与系统级根目录;④ `set_risk` 升到 `high_risk` 时受流程能力门约束(`minimal`/`agile` 拒绝,不再绕过 `create` 的检查);⑤ `loadTask` 补齐旧记录缺失字段(items / verification / review / commits),损坏记录 fail-closed 而非引擎裸崩。
|
|
189
|
-
- **可信边界加固(0.22.3,采纳 GPT 评审)**:① 验证命令统一在任务记录的项目根运行(monorepo 子目录不再跑错目录),receipt.root 与任务根强校验;② 旧任务(无 frozen 快照)禁止升 `high_risk`——迁移或重建后才可;③ `init apply` 强制 `expected_hash`,新增 `existing_hash` 防审批期间文件被换(TOCTOU);④ 任务记录新增 `revision`,每次写入 compare-and-swap,并发覆盖直接报错(`changed concurrently`);⑤ 工作台 Remote 增加 host workspace registry(`registerWorkspace` 供 harness 集成,注册后未授权路径一律拒绝);⑥ sandbox 升级审批展示 workspace;⑦ 文档明确 hook(本地反馈)/ host(工具流约束)/ CI(最终可信门禁)三层职责边界。
|
|
189
|
+
- **可信边界加固(0.22.3,采纳 GPT 评审)**:① 验证命令统一在任务记录的项目根运行(monorepo 子目录不再跑错目录),receipt.root 与任务根强校验;② 旧任务(无 frozen 快照)禁止升 `high_risk`——迁移或重建后才可;③ `init apply` 强制 `expected_hash`,新增 `existing_hash` 防审批期间文件被换(TOCTOU);④ 任务记录新增 `revision`,每次写入 compare-and-swap,并发覆盖直接报错(`changed concurrently`);⑤ 工作台 Remote 增加 host workspace registry(`registerWorkspace` 供 harness 集成,注册后未授权路径一律拒绝);⑥ sandbox 升级审批展示 workspace;⑦ 文档明确 hook(本地反馈)/ host(工具流约束)/ CI(最终可信门禁)三层职责边界。
|
|
190
|
+
- **并发与发布闭环(0.22.4,采纳 REVIEW-0.22.3)**:① `init apply` 在目标文件存在时**强制** `existing_hash`(不再可选),propose→apply 之间文件被篡改直接 fail-closed;② 任务写入升级为真正的原子 CAS——逻辑 `revision` 检查 + dsh-fs 的 `lstat` 版本号 `replaceIfVersion` write intent,并发写入报 `FS_STALE_VERSION` 而非静默覆盖;③ workspace registry 增加 `enableStrictWorkspaces()` 生产模式(空 registry 不再回退放行);④ `exports["./client"]` 补 `types` 条件(`lib/client.d.ts` 随构建生成);⑤ 新增 `npm run verify:package` 发布包黑盒验证(pack → 白名单一致性 → 真实安装 → 主入口 import → client 注册 → 包内 121 checks → CLI 语法)。REVIEW 指出的 Typert invocation context、host 可信状态签名、CI 最终门禁仍属 harness 层,集成点已在文档标注。
|
package/docs/manual.html
CHANGED
|
@@ -229,7 +229,7 @@ pnpm install && pnpm run build && pnpm dsh web</code></pre>
|
|
|
229
229
|
<ol>
|
|
230
230
|
<li><b>inspect</b>:对 AI 说「帮我初始化这个项目」;AI 读现有 <code>AGENTS.md</code>(若有)返回,没有则扫目录结构、技术栈、构建 / 运行命令、约定与红线。同时报告:项目根(自动从 workspace 向上发现)、语言栈(Node / Java / Python / Go / Rust)与默认验证命令、其它治理文件(<code>CLAUDE.md</code>、<code>.cursorrules</code>——init 只管理 AGENTS.md,不覆盖它们)、项目级 skill/rule 目录(<code>.dsh/rules/*.md</code>、<code>.dsh/skills/<name>/SKILL.md</code>)。</li>
|
|
231
231
|
<li><b>propose</b>:AI 写成 <code>AGENTS.md</code> 草稿,此步<b>只预览、不落盘</b>,同样硬校验<b>行数 ≤ 200 行</b>,并返回内容 hash。</li>
|
|
232
|
-
<li><b>apply</b
|
|
232
|
+
<li><b>apply</b>:落盘。<b>必须传回 propose 返回的 <code>expected_hash</code></b>(缺失或与内容不一致一律拒绝,防止预览与落盘之间内容被换);覆盖已有 <code>AGENTS.md</code> 时<b>还必须传回 <code>existing_hash</code></b>(证明审批期间现有文件未被他人改动)并带 <code>overwrite</code> 且<b>经人工批准</b>。写入目标必须在项目根内。</li>
|
|
233
233
|
</ol>
|
|
234
234
|
<pre><code>项目根/
|
|
235
235
|
├─ AGENTS.md ← init 生成,DSH 每会话自动注入
|
package/lib/client.d.ts
ADDED
package/lib/client.js
CHANGED
|
@@ -4469,6 +4469,7 @@ var surface = "var(--dsw-alias-bg-layer-2)";
|
|
|
4469
4469
|
var danger = "var(--dsw-alias-state-error-primary)";
|
|
4470
4470
|
var success = "var(--dsw-alias-state-success-primary)";
|
|
4471
4471
|
var warn = "var(--dsw-alias-state-warn-primary)";
|
|
4472
|
+
var brand = "var(--dsw-alias-state-business-primary)";
|
|
4472
4473
|
var styles = {
|
|
4473
4474
|
wrap: { display: "flex", flexDirection: "column", gap: 16, padding: 20, maxWidth: 900 },
|
|
4474
4475
|
head: { display: "flex", flexDirection: "column", gap: 4 },
|
|
@@ -4533,7 +4534,54 @@ var styles = {
|
|
|
4533
4534
|
noticeTitle: { color: warn, fontSize: 13, fontWeight: 600, display: "flex", alignItems: "center", gap: 8 },
|
|
4534
4535
|
okText: { color: success, fontSize: 13, display: "flex", alignItems: "center", gap: 6 },
|
|
4535
4536
|
status: { color: textTertiary, fontSize: 13, display: "flex", alignItems: "center", gap: 6 },
|
|
4536
|
-
actions: {
|
|
4537
|
+
actions: {
|
|
4538
|
+
display: "flex",
|
|
4539
|
+
alignItems: "center",
|
|
4540
|
+
gap: 12,
|
|
4541
|
+
marginTop: 4,
|
|
4542
|
+
paddingTop: 14,
|
|
4543
|
+
borderTop: `1px solid ${border}`
|
|
4544
|
+
},
|
|
4545
|
+
// ── flow preset picker ────────────────────────────────────────────────────
|
|
4546
|
+
flowGrid: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(230px, 1fr))", gap: 10 },
|
|
4547
|
+
flowOption: {
|
|
4548
|
+
display: "flex",
|
|
4549
|
+
flexDirection: "column",
|
|
4550
|
+
gap: 6,
|
|
4551
|
+
alignItems: "stretch",
|
|
4552
|
+
border: `1px solid ${border}`,
|
|
4553
|
+
borderRadius: 10,
|
|
4554
|
+
padding: "10px 12px",
|
|
4555
|
+
background: surface,
|
|
4556
|
+
cursor: "pointer",
|
|
4557
|
+
color: text,
|
|
4558
|
+
fontSize: 13,
|
|
4559
|
+
textAlign: "left",
|
|
4560
|
+
fontFamily: "inherit",
|
|
4561
|
+
transition: "border-color 120ms ease, box-shadow 120ms ease"
|
|
4562
|
+
},
|
|
4563
|
+
flowOptionActive: {
|
|
4564
|
+
borderColor: brand,
|
|
4565
|
+
boxShadow: `0 0 0 1px ${brand}`
|
|
4566
|
+
},
|
|
4567
|
+
flowOptionTitle: {
|
|
4568
|
+
display: "flex",
|
|
4569
|
+
alignItems: "center",
|
|
4570
|
+
gap: 7,
|
|
4571
|
+
fontSize: 13,
|
|
4572
|
+
fontWeight: 600,
|
|
4573
|
+
color: text
|
|
4574
|
+
},
|
|
4575
|
+
flowOptionDesc: { color: textTertiary, fontSize: 12, lineHeight: 1.45, margin: 0 },
|
|
4576
|
+
// ── init preview ──────────────────────────────────────────────────────────
|
|
4577
|
+
mdPreview: {
|
|
4578
|
+
border: `1px solid ${border}`,
|
|
4579
|
+
borderRadius: 8,
|
|
4580
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
4581
|
+
padding: "12px 14px",
|
|
4582
|
+
maxHeight: 420,
|
|
4583
|
+
overflowY: "auto"
|
|
4584
|
+
}
|
|
4537
4585
|
};
|
|
4538
4586
|
|
|
4539
4587
|
// src/client/TaskEngineSection.tsx
|
|
@@ -4722,8 +4770,8 @@ function deriveCapabilities(config) {
|
|
|
4722
4770
|
if (config.commit.file_scope) caps.push("file_scope");
|
|
4723
4771
|
return caps;
|
|
4724
4772
|
}
|
|
4725
|
-
function preset(id, version,
|
|
4726
|
-
return { id, version, label, description, capabilities: deriveCapabilities(config), config };
|
|
4773
|
+
function preset(id, version, label2, description, config) {
|
|
4774
|
+
return { id, version, label: label2, description, capabilities: deriveCapabilities(config), config };
|
|
4727
4775
|
}
|
|
4728
4776
|
var FLOW_PRESETS = {
|
|
4729
4777
|
standard: preset("standard", 1, "\u6807\u51C6\u7814\u53D1", "\u9700\u6C42\u8BC4\u5BA1 \u2192 \u8BBE\u8BA1 \u2192 \u5F00\u53D1 \u2192 \u4EA4\u4ED8 \u2192 \u4EE3\u7801\u5BA1\u6838\uFF0C\u542B\u4EA7\u7269\u95E8 + \u786E\u8BA4\u95E8", STANDARD),
|
|
@@ -4873,9 +4921,28 @@ function TaskEngineSection(props) {
|
|
|
4873
4921
|
] }),
|
|
4874
4922
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
|
|
4875
4923
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u6D41\u7A0B\u9884\u8BBE" }),
|
|
4876
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.
|
|
4877
|
-
|
|
4878
|
-
|
|
4924
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.flowGrid, children: FLOW_OPTIONS.map((option) => {
|
|
4925
|
+
const active = flow === option.id;
|
|
4926
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
4927
|
+
"button",
|
|
4928
|
+
{
|
|
4929
|
+
type: "button",
|
|
4930
|
+
"aria-pressed": active,
|
|
4931
|
+
onClick: () => {
|
|
4932
|
+
selectFlow(option.id);
|
|
4933
|
+
},
|
|
4934
|
+
style: { ...styles.flowOption, ...active ? styles.flowOptionActive : {} },
|
|
4935
|
+
children: [
|
|
4936
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.flowOptionTitle, children: [
|
|
4937
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: active ? "done" : "ongoing", size: 8 }),
|
|
4938
|
+
option.label
|
|
4939
|
+
] }),
|
|
4940
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.flowOptionDesc, children: option.description })
|
|
4941
|
+
]
|
|
4942
|
+
},
|
|
4943
|
+
option.id
|
|
4944
|
+
);
|
|
4945
|
+
}) }),
|
|
4879
4946
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.hint, children: FLOW_OPTIONS.find((option) => option.id === flow)?.description })
|
|
4880
4947
|
] }),
|
|
4881
4948
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconBranchOutline16, { size: 16 }), title: "\u6D41\u7A0B\u8282\u70B9", hint: "\u8BE5\u6D41\u7A0B\u9884\u8BBE\u56FA\u5B9A\u7684\u9636\u6BB5\u987A\u5E8F\uFF1B\u6539\u53D8\u6D41\u7A0B\u8BF7\u5207\u6362\u4E0A\u9762\u7684\u9884\u8BBE\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: stages.map((stage) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: stage === (FLOW_PRESETS[flow]?.config.start_stage ?? ""), title: stage === FLOW_PRESETS[flow]?.config.start_stage ? "\u8D77\u59CB\u9636\u6BB5" : void 0, children: stage }, stage)) }) }),
|
|
@@ -5113,12 +5180,25 @@ function InitPanel({ workspace, remote }) {
|
|
|
5113
5180
|
{ style: styles.muted },
|
|
5114
5181
|
"\u9879\u76EE\u6839 AGENTS.md \u4F1A\u88AB DSH \u81EA\u52A8\u6CE8\u5165\u5230\u6BCF\u4E2A\u4F1A\u8BDD\u3002\u521D\u59CB\u5316\u4E00\u6B21\uFF0C\u4E4B\u540E\u6BCF\u6B21\u4EFB\u52A1\u5F00\u5DE5 AI \u90FD\u81EA\u5E26\u8FD9\u4EFD\u9879\u76EE\u8BA4\u77E5\uFF08\u9879\u76EE\u662F\u4EC0\u4E48 \u2192 \u600E\u4E48\u8DD1 \u2192 \u7ED3\u6784 \u2192 \u7EA6\u5B9A \u2192 \u5751\uFF09\u3002"
|
|
5115
5182
|
),
|
|
5116
|
-
exists && !editing && draft === null ? (0, import_react2.createElement)(
|
|
5183
|
+
exists && !editing && draft === null ? (0, import_react2.createElement)(
|
|
5184
|
+
"div",
|
|
5185
|
+
{ style: styles.mdPreview },
|
|
5186
|
+
(0, import_react2.createElement)(import_dsh_client_ui_primitives2.MarkdownText, { text: view.content, labels: MD_LABELS })
|
|
5187
|
+
) : null,
|
|
5117
5188
|
draft !== null ? (0, import_react2.createElement)(
|
|
5118
5189
|
"div",
|
|
5119
5190
|
{ style: styles.section },
|
|
5120
|
-
(0, import_react2.createElement)(
|
|
5121
|
-
|
|
5191
|
+
(0, import_react2.createElement)(
|
|
5192
|
+
"div",
|
|
5193
|
+
{ style: styles.sourceRow },
|
|
5194
|
+
(0, import_react2.createElement)(import_dsh_client_ui_primitives2.StateDot, { state: "ongoing", size: 8 }),
|
|
5195
|
+
(0, import_react2.createElement)("span", { style: styles.sourceBadge }, `AI \u8349\u7A3F \xB7 ${draft.lines} \u884C \xB7 \u5C1A\u672A\u5199\u5165`)
|
|
5196
|
+
),
|
|
5197
|
+
(0, import_react2.createElement)(
|
|
5198
|
+
"div",
|
|
5199
|
+
{ style: styles.mdPreview },
|
|
5200
|
+
(0, import_react2.createElement)(import_dsh_client_ui_primitives2.MarkdownText, { text: draft.content, labels: MD_LABELS })
|
|
5201
|
+
)
|
|
5122
5202
|
) : null,
|
|
5123
5203
|
editing ? (0, import_react2.createElement)("textarea", {
|
|
5124
5204
|
style: { ...styles.textarea, minHeight: 300 },
|
|
@@ -5946,6 +6026,10 @@ var header = {
|
|
|
5946
6026
|
padding: "12px 20px",
|
|
5947
6027
|
borderBottom: "1px solid var(--dsw-alias-border-l2)"
|
|
5948
6028
|
};
|
|
6029
|
+
var headerBrand = { display: "flex", alignItems: "center", gap: 10, minWidth: 0 };
|
|
6030
|
+
var headerText = { display: "flex", flexDirection: "column", gap: 1, minWidth: 0 };
|
|
6031
|
+
var headerTitle = { fontSize: 15, fontWeight: 600, color: "var(--dsw-alias-label-primary)" };
|
|
6032
|
+
var headerSub = { fontSize: 12, color: "var(--dsw-alias-label-tertiary)", margin: 0 };
|
|
5949
6033
|
var tabbar = {
|
|
5950
6034
|
display: "flex",
|
|
5951
6035
|
alignItems: "center",
|
|
@@ -5999,12 +6083,33 @@ function Workbench({ useStore, actions, useWorkspaces, remote }) {
|
|
|
5999
6083
|
(0, import_react7.createElement)(
|
|
6000
6084
|
"div",
|
|
6001
6085
|
{ style: header },
|
|
6002
|
-
(0, import_react7.createElement)(
|
|
6086
|
+
(0, import_react7.createElement)(
|
|
6087
|
+
"div",
|
|
6088
|
+
{ style: headerBrand },
|
|
6089
|
+
(0, import_react7.createElement)(import_dsh_client_ui_primitives7.IconSettingsOutline16, { size: 18 }),
|
|
6090
|
+
(0, import_react7.createElement)(
|
|
6091
|
+
"div",
|
|
6092
|
+
{ style: headerText },
|
|
6093
|
+
(0, import_react7.createElement)("span", { style: headerTitle }, "\u5DE5\u7A0B\u6D41\u7A0B"),
|
|
6094
|
+
(0, import_react7.createElement)("p", { style: headerSub }, "\u5DE5\u7A0B\u5316\u4EA4\u4ED8\u5DE5\u4F5C\u53F0 \xB7 \u9700\u6C42\u8BC4\u5BA1 \u2192 \u8BBE\u8BA1 \u2192 \u5F00\u53D1 \u2192 \u4EA4\u4ED8 \u2192 \u4EE3\u7801\u5BA1\u6838")
|
|
6095
|
+
)
|
|
6096
|
+
),
|
|
6003
6097
|
(0, import_react7.createElement)(
|
|
6004
6098
|
"button",
|
|
6005
|
-
{
|
|
6006
|
-
|
|
6007
|
-
|
|
6099
|
+
{
|
|
6100
|
+
type: "button",
|
|
6101
|
+
title: "\u5173\u95ED",
|
|
6102
|
+
onClick: () => {
|
|
6103
|
+
actions.close();
|
|
6104
|
+
},
|
|
6105
|
+
style: closeStyle,
|
|
6106
|
+
onMouseEnter: (event) => {
|
|
6107
|
+
event.currentTarget.style.background = "var(--dsw-alias-bg-layer-2)";
|
|
6108
|
+
},
|
|
6109
|
+
onMouseLeave: (event) => {
|
|
6110
|
+
event.currentTarget.style.background = "transparent";
|
|
6111
|
+
}
|
|
6112
|
+
},
|
|
6008
6113
|
(0, import_react7.createElement)(import_dsh_client_ui_primitives7.IconCloseOutline16, { size: 16 })
|
|
6009
6114
|
)
|
|
6010
6115
|
),
|
|
@@ -6033,6 +6138,8 @@ function Workbench({ useStore, actions, useWorkspaces, remote }) {
|
|
|
6033
6138
|
// src/client/TriggerButton.tsx
|
|
6034
6139
|
var import_react8 = require("react");
|
|
6035
6140
|
var import_dsh_client_ui_primitives8 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
6141
|
+
var hover = "var(--dsw-alias-bg-layer-2)";
|
|
6142
|
+
var label = "var(--dsw-alias-label-primary)";
|
|
6036
6143
|
function TriggerButton({ wide, actions }) {
|
|
6037
6144
|
return (0, import_react8.createElement)(
|
|
6038
6145
|
"button",
|
|
@@ -6043,24 +6150,39 @@ function TriggerButton({ wide, actions }) {
|
|
|
6043
6150
|
onClick: () => {
|
|
6044
6151
|
actions.open();
|
|
6045
6152
|
},
|
|
6153
|
+
onMouseEnter: (event) => {
|
|
6154
|
+
event.currentTarget.style.background = hover;
|
|
6155
|
+
},
|
|
6156
|
+
onMouseLeave: (event) => {
|
|
6157
|
+
event.currentTarget.style.background = "transparent";
|
|
6158
|
+
},
|
|
6046
6159
|
style: {
|
|
6047
6160
|
display: "flex",
|
|
6048
6161
|
alignItems: "center",
|
|
6049
6162
|
justifyContent: wide ? "flex-start" : "center",
|
|
6050
6163
|
gap: wide ? 8 : 0,
|
|
6051
|
-
width: "100%",
|
|
6052
|
-
|
|
6164
|
+
width: wide ? "100%" : 36,
|
|
6165
|
+
minWidth: wide ? 0 : 36,
|
|
6166
|
+
height: wide ? 42 : 36,
|
|
6167
|
+
flex: wide ? "1 1 auto" : "none",
|
|
6053
6168
|
border: "none",
|
|
6054
6169
|
background: "transparent",
|
|
6055
6170
|
cursor: "pointer",
|
|
6056
|
-
color:
|
|
6057
|
-
borderRadius:
|
|
6058
|
-
padding: wide ? "0 10px" : 0,
|
|
6059
|
-
|
|
6171
|
+
color: label,
|
|
6172
|
+
borderRadius: wide ? 12 : "50%",
|
|
6173
|
+
padding: wide ? "0 10px 0 8px" : 0,
|
|
6174
|
+
// The native settings row bleeds 2px past its seat on both sides so its
|
|
6175
|
+
// hover chrome spans the full row; matching keeps both icon columns on
|
|
6176
|
+
// the same x.
|
|
6177
|
+
margin: wide ? "0 -2px" : "0 auto 2px",
|
|
6178
|
+
fontSize: wide ? 14 : void 0,
|
|
6179
|
+
lineHeight: wide ? "22px" : void 0,
|
|
6180
|
+
boxSizing: "border-box",
|
|
6181
|
+
transition: "background 120ms ease"
|
|
6060
6182
|
}
|
|
6061
6183
|
},
|
|
6062
6184
|
(0, import_react8.createElement)(import_dsh_client_ui_primitives8.IconSettingsOutline16, { size: wide ? 16 : 18 }),
|
|
6063
|
-
wide ? (0, import_react8.createElement)("span", { style: { fontSize:
|
|
6185
|
+
wide ? (0, import_react8.createElement)("span", { style: { fontSize: 14, lineHeight: "22px" } }, "\u5DE5\u7A0B\u6D41\u7A0B") : null
|
|
6064
6186
|
);
|
|
6065
6187
|
}
|
|
6066
6188
|
|