@godv61/dsh-task-engine 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/lib/client.js CHANGED
@@ -168,8 +168,8 @@ var util;
168
168
  return util2.objectValues(filtered);
169
169
  };
170
170
  util2.objectValues = (obj) => {
171
- return util2.objectKeys(obj).map(function(e2) {
172
- return obj[e2];
171
+ return util2.objectKeys(obj).map(function(e) {
172
+ return obj[e];
173
173
  });
174
174
  };
175
175
  util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object) => {
@@ -3337,13 +3337,13 @@ var ZodFunction = class _ZodFunction extends ZodType {
3337
3337
  const me = this;
3338
3338
  return OK(async function(...args) {
3339
3339
  const error = new ZodError([]);
3340
- const parsedArgs = await me._def.args.parseAsync(args, params).catch((e2) => {
3341
- error.addIssue(makeArgsIssue(args, e2));
3340
+ const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
3341
+ error.addIssue(makeArgsIssue(args, e));
3342
3342
  throw error;
3343
3343
  });
3344
3344
  const result = await Reflect.apply(fn, this, parsedArgs);
3345
- const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e2) => {
3346
- error.addIssue(makeReturnsIssue(result, e2));
3345
+ const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
3346
+ error.addIssue(makeReturnsIssue(result, e));
3347
3347
  throw error;
3348
3348
  });
3349
3349
  return parsedReturns;
@@ -4146,8 +4146,9 @@ var TYPERT_REMOTE = {
4146
4146
  };
4147
4147
  var remote_default = TYPERT_REMOTE;
4148
4148
 
4149
- // src/client/TaskEngineSection.ts
4150
- var import_react = require("react");
4149
+ // src/client/TaskEngineSection.tsx
4150
+ var import_react2 = require("react");
4151
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
4151
4152
 
4152
4153
  // src/engine.ts
4153
4154
  var GUARD_NAMES = [
@@ -4223,251 +4224,674 @@ function validateWorkflow(config) {
4223
4224
  return problems;
4224
4225
  }
4225
4226
 
4226
- // src/client/TaskEngineSection.ts
4227
- var e = import_react.createElement;
4227
+ // src/client/FlowDiagram.tsx
4228
+ var import_react = require("react");
4229
+ var nodeW = 108;
4230
+ var nodeH = 40;
4231
+ var stepX = nodeW + 56;
4232
+ var margin = 40;
4233
+ var nodeTop = 92;
4234
+ var nodeCenterY = nodeTop + nodeH / 2;
4235
+ var textTertiary = "var(--dsw-alias-label-tertiary)";
4236
+ var textSecondary = "var(--dsw-alias-label-secondary)";
4237
+ var border = "var(--dsw-alias-border-l2)";
4238
+ var surface = "var(--dsw-alias-bg-layer-2)";
4239
+ var brand = "var(--dsw-alias-state-business-primary)";
4240
+ var danger = "var(--dsw-alias-state-error-primary)";
4241
+ function edgeList(config) {
4242
+ return config.transitions.map((t) => {
4243
+ const label = (t.requires ?? []).length > 0 ? (t.requires ?? []).join(" \xB7 ") : null;
4244
+ const orphaned = !config.stages.includes(t.from) || !config.stages.includes(t.to);
4245
+ return { from: t.from, to: t.to, label, orphaned };
4246
+ });
4247
+ }
4248
+ function nodeX(index, stageCount) {
4249
+ return index < 0 ? margin + stageCount * stepX + nodeW : margin + index * stepX;
4250
+ }
4251
+ function FlowDiagram({ config }) {
4252
+ const stages = config.stages;
4253
+ const edges = edgeList(config);
4254
+ const stageCount = stages.length;
4255
+ const indexOf = (s) => {
4256
+ const i = stages.indexOf(s);
4257
+ return i < 0 ? -1 : i;
4258
+ };
4259
+ const width = Math.max(margin * 2 + stageCount * stepX, 360);
4260
+ const height = 210;
4261
+ const nodeTitle = (stage) => `${stage}${stage === config.start_stage ? "\uFF08\u8D77\u59CB\uFF09" : ""}`;
4262
+ const forwardPath = (from, to) => {
4263
+ const x1 = nodeX(from, stageCount) + nodeW;
4264
+ const x2 = nodeX(to, stageCount);
4265
+ if (to === from + 1) {
4266
+ return `M ${x1} ${nodeCenterY} L ${x2} ${nodeCenterY}`;
4267
+ }
4268
+ const cx = (x1 + x2) / 2;
4269
+ const cy = nodeCenterY + (to - from) * 20;
4270
+ return `M ${x1} ${nodeCenterY} Q ${cx} ${cy} ${x2} ${nodeCenterY}`;
4271
+ };
4272
+ const returnPath = (from, to) => {
4273
+ const x1 = nodeX(from, stageCount) + nodeW / 2;
4274
+ const x2 = nodeX(to, stageCount) + nodeW / 2;
4275
+ const y1 = nodeTop;
4276
+ const lift = 26 + Math.abs(from - to) * 6;
4277
+ if (from === to) {
4278
+ const cx2 = x1 + nodeW / 2 + 8;
4279
+ return `M ${x1 + nodeW / 2} ${y1} Q ${cx2} ${y1 - lift} ${x1 + nodeW / 2 + 18} ${y1}`;
4280
+ }
4281
+ const cx = (x1 + x2) / 2;
4282
+ return `M ${x1} ${y1} Q ${cx} ${y1 - lift} ${x2} ${y1}`;
4283
+ };
4284
+ const strokeOf = (edge) => edge.orphaned ? danger : textTertiary;
4285
+ const forwardLabelY = (from, to) => to === from + 1 ? nodeCenterY - 8 : nodeCenterY + (to - from) * 20 + 16;
4286
+ const returnLabelY = (from, to) => nodeTop - 30 - Math.abs(from - to) * 6;
4287
+ return (0, import_react.createElement)(
4288
+ "svg",
4289
+ {
4290
+ width,
4291
+ height,
4292
+ viewBox: `0 0 ${width} ${height}`,
4293
+ style: { display: "block", minWidth: "100%", fontFamily: "inherit" },
4294
+ role: "img",
4295
+ "aria-label": "\u5DE5\u7A0B\u9636\u6BB5\u6D41\u8F6C\u56FE"
4296
+ },
4297
+ (0, import_react.createElement)(
4298
+ "defs",
4299
+ null,
4300
+ (0, import_react.createElement)("marker", {
4301
+ id: "flow-arrow",
4302
+ viewBox: "0 0 10 10",
4303
+ refX: 8,
4304
+ refY: 5,
4305
+ markerWidth: 7,
4306
+ markerHeight: 7,
4307
+ orient: "auto"
4308
+ }, (0, import_react.createElement)("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: "context-stroke" }))
4309
+ ),
4310
+ ...edges.map((edge, i) => {
4311
+ const from = indexOf(edge.from);
4312
+ const to = indexOf(edge.to);
4313
+ const stroke = strokeOf(edge);
4314
+ const d = from < to ? forwardPath(from, to) : returnPath(from, to < 0 ? from : to);
4315
+ const labelY = from < to ? forwardLabelY(from, to) : returnLabelY(from, to < 0 ? from : to);
4316
+ const labelX = from < to ? (nodeX(from, stageCount) + nodeW + nodeX(to, stageCount)) / 2 : (nodeX(from, stageCount) + nodeX(to < 0 ? from : to, stageCount) + nodeW) / 2;
4317
+ const common = {
4318
+ key: `edge-${i}`,
4319
+ d,
4320
+ fill: "none",
4321
+ stroke,
4322
+ strokeWidth: 1.5,
4323
+ markerEnd: "url(#flow-arrow)",
4324
+ ...edge.orphaned ? { strokeDasharray: "4 3" } : {}
4325
+ };
4326
+ const label = edge.label === null ? null : (0, import_react.createElement)("text", {
4327
+ x: labelX,
4328
+ y: labelY,
4329
+ textAnchor: "middle",
4330
+ fontSize: 11,
4331
+ fill: edge.orphaned ? danger : textSecondary
4332
+ }, edge.from === edge.to ? edge.label : edge.label);
4333
+ return (0, import_react.createElement)(
4334
+ "g",
4335
+ { key: `edgeg-${i}` },
4336
+ (0, import_react.createElement)("path", common),
4337
+ label
4338
+ );
4339
+ }),
4340
+ ...stages.map((stage, i) => {
4341
+ const x = nodeX(i, stageCount);
4342
+ const isStart = stage === config.start_stage;
4343
+ return (0, import_react.createElement)(
4344
+ "g",
4345
+ { key: stage },
4346
+ (0, import_react.createElement)("rect", {
4347
+ x,
4348
+ y: nodeTop,
4349
+ width: nodeW,
4350
+ height: nodeH,
4351
+ rx: 20,
4352
+ ry: 20,
4353
+ fill: surface,
4354
+ stroke: isStart ? brand : border,
4355
+ strokeWidth: isStart ? 2 : 1.5
4356
+ }),
4357
+ (0, import_react.createElement)("text", {
4358
+ x: x + nodeW / 2,
4359
+ y: nodeCenterY,
4360
+ textAnchor: "middle",
4361
+ alignmentBaseline: "middle",
4362
+ fontSize: 12,
4363
+ fill: "var(--dsw-alias-label-primary)",
4364
+ fontWeight: isStart ? 600 : 400
4365
+ }, stage),
4366
+ (0, import_react.createElement)("title", null, nodeTitle(stage))
4367
+ );
4368
+ })
4369
+ );
4370
+ }
4371
+
4372
+ // src/client/styles.ts
4373
+ var text = "var(--dsw-alias-label-primary)";
4374
+ var textSecondary2 = "var(--dsw-alias-label-secondary)";
4375
+ var textTertiary2 = "var(--dsw-alias-label-tertiary)";
4376
+ var border2 = "var(--dsw-alias-border-l2)";
4377
+ var surface2 = "var(--dsw-alias-bg-layer-2)";
4378
+ var danger2 = "var(--dsw-alias-state-error-primary)";
4379
+ var success = "var(--dsw-alias-state-success-primary)";
4380
+ var warn = "var(--dsw-alias-state-warn-primary)";
4381
+ var styles = {
4382
+ wrap: { display: "flex", flexDirection: "column", gap: 16, padding: 20, maxWidth: 900 },
4383
+ head: { display: "flex", flexDirection: "column", gap: 4 },
4384
+ title: { margin: 0, color: text },
4385
+ muted: { margin: 0, color: textSecondary2, fontSize: 13, lineHeight: 1.5 },
4386
+ sourceRow: { display: "flex", alignItems: "center", gap: 8 },
4387
+ sourceText: { color: textTertiary2, fontSize: 13 },
4388
+ workspaceRow: { display: "flex", alignItems: "center", gap: 8 },
4389
+ workspaceLabel: { color: textSecondary2, fontSize: 13 },
4390
+ flowCard: { border: `1px solid ${border2}`, borderRadius: 10, background: surface2, padding: 12, overflowX: "auto" },
4391
+ section: { display: "flex", flexDirection: "column", gap: 12 },
4392
+ cardBody: { padding: "4px 0 8px", display: "flex", flexDirection: "column", gap: 12 },
4393
+ row: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" },
4394
+ field: { display: "flex", flexDirection: "column", gap: 4, flex: "1 1 220px", minWidth: 180 },
4395
+ fieldLabel: { color: textSecondary2, fontSize: 13 },
4396
+ hint: { color: textTertiary2, fontSize: 12, margin: 0, lineHeight: 1.4 },
4397
+ chips: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center" },
4398
+ cardRow: {
4399
+ display: "flex",
4400
+ alignItems: "center",
4401
+ gap: 8,
4402
+ flexWrap: "wrap",
4403
+ border: `1px solid ${border2}`,
4404
+ borderRadius: 8,
4405
+ padding: "8px 10px"
4406
+ },
4407
+ inline: { display: "inline-flex", alignItems: "center", gap: 6, marginRight: 8 },
4408
+ problems: {
4409
+ display: "flex",
4410
+ flexDirection: "column",
4411
+ gap: 8,
4412
+ border: `1px solid ${danger2}`,
4413
+ borderRadius: 8,
4414
+ padding: "10px 12px"
4415
+ },
4416
+ problemsTitle: { color: danger2, fontSize: 13, fontWeight: 600, display: "flex", alignItems: "center", gap: 8 },
4417
+ problemsList: { margin: 0, paddingLeft: 20, color: text, fontSize: 13, display: "flex", flexDirection: "column", gap: 4 },
4418
+ noticeBox: {
4419
+ border: `1px solid ${warn}`,
4420
+ borderRadius: 8,
4421
+ padding: "10px 12px",
4422
+ display: "flex",
4423
+ flexDirection: "column",
4424
+ gap: 8
4425
+ },
4426
+ noticeTitle: { color: warn, fontSize: 13, fontWeight: 600, display: "flex", alignItems: "center", gap: 8 },
4427
+ okText: { color: success, fontSize: 13, display: "flex", alignItems: "center", gap: 6 },
4428
+ status: { color: textTertiary2, fontSize: 13, display: "flex", alignItems: "center", gap: 6 },
4429
+ actions: { display: "flex", alignItems: "center", gap: 12, marginTop: 4 }
4430
+ };
4431
+
4432
+ // src/client/TaskEngineSection.tsx
4433
+ var import_jsx_runtime = require("react/jsx-runtime");
4434
+ var text2 = "var(--dsw-alias-label-primary)";
4435
+ var textTertiary3 = "var(--dsw-alias-label-tertiary)";
4436
+ var control = {
4437
+ background: "var(--dsw-alias-bg-layer-1)",
4438
+ border: `1px solid ${border2}`,
4439
+ borderRadius: 8,
4440
+ padding: "5px 8px",
4441
+ color: text2,
4442
+ fontSize: 13,
4443
+ minWidth: 0
4444
+ };
4445
+ var cardV = {
4446
+ display: "flex",
4447
+ flexDirection: "column",
4448
+ gap: 8,
4449
+ border: `1px solid ${border2}`,
4450
+ borderRadius: 8,
4451
+ padding: "10px 12px"
4452
+ };
4453
+ var GUARD_OPTIONS = [
4454
+ { value: "requirement_confirmation", label: "\u9700\u6C42\u786E\u8BA4" },
4455
+ { value: "solution_confirmation", label: "\u65B9\u6848\u786E\u8BA4" },
4456
+ { value: "todos_done", label: "\u5F85\u529E\u5B8C\u6210" },
4457
+ { value: "verified", label: "\u5DF2\u9A8C\u8BC1" },
4458
+ { value: "review_passed", label: "\u8BC4\u5BA1\u901A\u8FC7" },
4459
+ { value: "artifacts_present", label: "\u4EA7\u7269\u9F50\u5168" }
4460
+ ];
4461
+ var POLICY_OPTIONS = [
4462
+ { value: "task", label: "task", hint: "\u68C0\u67E5\u70B9\u4E00\u6B21\u63D0\u4EA4" },
4463
+ { value: "item", label: "item", hint: "\u9010\u9879\u63D0\u4EA4 + \u6536\u5C3E TASK" },
4464
+ { value: "manual", label: "manual", hint: "\u4EBA\u5DE5\u6307\u793A" }
4465
+ ];
4228
4466
  function TaskEngineSection(props) {
4229
4467
  const { remote, useWorkspaces } = props;
4230
4468
  const workspaces = useWorkspaces((state) => state.items);
4231
- const [workspace, setWorkspace] = (0, import_react.useState)("");
4232
- const [config, setConfig] = (0, import_react.useState)(null);
4233
- const [source, setSource] = (0, import_react.useState)("default");
4234
- const [savedAt, setSavedAt] = (0, import_react.useState)("");
4235
- (0, import_react.useEffect)(() => {
4469
+ const [workspace, setWorkspace] = (0, import_react2.useState)("");
4470
+ const [config, setConfig] = (0, import_react2.useState)(null);
4471
+ const [source, setSource] = (0, import_react2.useState)("default");
4472
+ const [savedAt, setSavedAt] = (0, import_react2.useState)("");
4473
+ const [loadError, setLoadError] = (0, import_react2.useState)("");
4474
+ const [loadedProblems, setLoadedProblems] = (0, import_react2.useState)([]);
4475
+ (0, import_react2.useEffect)(() => {
4236
4476
  if (workspaces.length > 0 && workspace === "") {
4237
4477
  setWorkspace(workspaces[0].path);
4238
4478
  }
4239
4479
  }, [workspaces, workspace]);
4240
- (0, import_react.useEffect)(() => {
4480
+ (0, import_react2.useEffect)(() => {
4241
4481
  if (workspace === "") return;
4242
4482
  let alive = true;
4483
+ setLoadError("");
4243
4484
  void remote.read(workspace).then((result) => {
4244
4485
  if (!alive) return;
4245
4486
  if (result.ok) {
4246
4487
  setConfig(result.value.config);
4247
4488
  setSource(result.value.source);
4489
+ setLoadedProblems(result.value.source === "invalid" ? result.value.problems : []);
4248
4490
  setSavedAt("");
4491
+ } else {
4492
+ setLoadError("\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25\uFF1A" + describeError(result.error));
4249
4493
  }
4494
+ }, (error) => {
4495
+ if (alive) setLoadError("\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25\uFF1A" + describeError(error));
4250
4496
  });
4251
4497
  return () => {
4252
4498
  alive = false;
4253
4499
  };
4254
4500
  }, [remote, workspace]);
4255
- const problems = (0, import_react.useMemo)(() => config === null ? [] : validateWorkflow(config), [config]);
4256
- if (config === null) return e("div", { style: styles.wrap }, e("p", null, "\u52A0\u8F7D\u4E2D\u2026"));
4257
- return e(
4258
- "div",
4259
- { style: styles.wrap },
4260
- e("h2", { style: styles.title }, "\u5DE5\u7A0B\u6D41\u7A0B\u914D\u7F6E"),
4261
- e("p", { style: styles.muted }, "\u8FD9\u4EFD\u914D\u7F6E\u540C\u65F6\u9A71\u52A8 dev_task \u7684\u786C\u95E8\u7981\uFF1B\u6709\u95EE\u9898\u7684\u914D\u7F6E\u4E0D\u4F1A\u88AB\u4FDD\u5B58\u3002"),
4262
- workspacePicker(workspaces, workspace, setWorkspace),
4263
- e("h3", null, "\u9636\u6BB5"),
4264
- stageEditor(config, setConfig),
4265
- e("h3", null, "\u6D41\u8F6C\uFF08\u9636\u6BB5 \u2192 \u9636\u6BB5\uFF0C\u53EF\u5E26\u5B88\u536B\uFF09"),
4266
- transitionEditor(config, setConfig),
4267
- e("h3", null, "\u4EA7\u7269\uFF08\u6BCF\u4E2A\u9636\u6BB5\u8981\u586B\u7684\u5B57\u6BB5\uFF09"),
4268
- artifactEditor(config, setConfig),
4269
- e("h3", null, "\u63D0\u4EA4\u89C4\u5219"),
4270
- commitEditor(config, setConfig),
4271
- e(
4272
- "label",
4273
- { style: styles.row },
4274
- e("input", {
4275
- type: "checkbox",
4276
- checked: config.high_risk_requires_verification,
4277
- onChange: (ev) => update(config, setConfig, (c) => ({ ...c, high_risk_requires_verification: ev.target.checked }))
4278
- }),
4279
- "\u9AD8\u98CE\u9669\u4EFB\u52A1\u5FC5\u987B\u9644\u9A8C\u8BC1\u8BC1\u636E\u624D\u80FD\u8FC7 verified \u5B88\u536B"
4280
- ),
4281
- problems.length > 0 ? e(
4282
- "div",
4283
- { style: styles.problems },
4284
- e("strong", null, `\u6709 ${problems.length} \u4E2A\u95EE\u9898\uFF08\u4FDD\u5B58\u4F1A\u88AB\u62D2\u7EDD\uFF09\uFF1A`),
4285
- e("ul", null, ...problems.map((p) => e("li", { key: p }, p)))
4286
- ) : e("p", { style: styles.ok }, source === "default" ? "\u5F53\u524D\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E\u3002" : "\u914D\u7F6E\u6709\u6548\u3002"),
4287
- e(
4288
- "div",
4289
- { style: styles.actions },
4290
- e("button", { onClick: () => save(props, config, workspace, setSavedAt, setSource), disabled: problems.length > 0 }, "\u4FDD\u5B58\u5230 .dsh/eng.json"),
4291
- savedAt !== "" ? e("span", { style: styles.ok }, savedAt) : null
4292
- )
4293
- );
4501
+ const problems = (0, import_react2.useMemo)(() => config === null ? [] : validateWorkflow(config), [config]);
4502
+ const notice = problems.length === 0 && loadedProblems.length > 0 ? loadedProblems : [];
4503
+ if (workspaces.length === 0) {
4504
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.wrap, children: [
4505
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.title, children: "\u5DE5\u7A0B\u6D41\u7A0B\u914D\u7F6E" }),
4506
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u5F53\u524D\u6CA1\u6709\u4EFB\u4F55\u5DE5\u4F5C\u533A\uFF1A\u8BF7\u5148\u5728\u4FA7\u680F\u521B\u5EFA\u4E00\u4E2A\u5DE5\u4F5C\u533A\u3002" })
4507
+ ] });
4508
+ }
4509
+ if (loadError !== "") {
4510
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.wrap, children: [
4511
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.title, children: "\u5DE5\u7A0B\u6D41\u7A0B\u914D\u7F6E" }),
4512
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.problems, children: [
4513
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.problemsTitle, children: [
4514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "error" }),
4515
+ "\u8BFB\u53D6\u5931\u8D25"
4516
+ ] }),
4517
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: loadError })
4518
+ ] })
4519
+ ] });
4520
+ }
4521
+ if (config === null) {
4522
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.wrap, children: [
4523
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.title, children: "\u5DE5\u7A0B\u6D41\u7A0B\u914D\u7F6E" }),
4524
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u52A0\u8F7D\u4E2D\u2026" })
4525
+ ] });
4526
+ }
4527
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.wrap, children: [
4528
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.head, children: [
4529
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.title, children: "\u5DE5\u7A0B\u6D41\u7A0B\u914D\u7F6E" }),
4530
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u8FD9\u4EFD\u914D\u7F6E\u540C\u65F6\u9A71\u52A8 dev_task \u7684\u786C\u95E8\u7981\uFF1B\u6709\u95EE\u9898\u7684\u914D\u7F6E\u4E0D\u4F1A\u88AB\u4FDD\u5B58\u3002" })
4531
+ ] }),
4532
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.sourceRow, children: [
4533
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: source === "project" ? "done" : source === "invalid" ? "warning" : "ongoing" }),
4534
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.sourceText, children: sourceText(source) })
4535
+ ] }),
4536
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.workspaceRow, children: [
4537
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.workspaceLabel, children: "\u5DE5\u4F5C\u533A" }),
4538
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4539
+ "select",
4540
+ {
4541
+ style: control,
4542
+ value: workspace,
4543
+ onChange: (ev) => {
4544
+ setWorkspace(ev.target.value);
4545
+ },
4546
+ children: workspaces.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: w.path, children: w.title || w.path }, w.path))
4547
+ }
4548
+ )
4549
+ ] }),
4550
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.flowCard, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FlowDiagram, { config }) }),
4551
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconSettingsOutline16, { size: 16 }), title: "\u9636\u6BB5", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StageEditor, { config, setConfig }) }),
4552
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconBranchOutline16, { size: 16 }), title: "\u6D41\u8F6C\uFF08\u9636\u6BB5 \u2192 \u9636\u6BB5\uFF0C\u53EF\u5E26\u5B88\u536B\uFF09", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TransitionEditor, { config, setConfig }) }),
4553
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconCheckOutline16, { size: 16 }), title: "\u4EA7\u7269\uFF08\u6BCF\u4E2A\u9636\u6BB5\u8981\u586B\u7684\u5B57\u6BB5\uFF09", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArtifactEditor, { config, setConfig }) }),
4554
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconEditOutline16, { size: 16 }), title: "\u63D0\u4EA4\u89C4\u5219", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitEditor, { config, setConfig }) }),
4555
+ problems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.problems, children: [
4556
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.problemsTitle, children: [
4557
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "error" }),
4558
+ "\u6709 ",
4559
+ problems.length,
4560
+ " \u4E2A\u95EE\u9898\uFF08\u4FDD\u5B58\u4F1A\u88AB\u62D2\u7EDD\uFF09"
4561
+ ] }),
4562
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: styles.problemsList, children: problems.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: p }, p)) })
4563
+ ] }) : notice.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.noticeBox, children: [
4564
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.noticeTitle, children: [
4565
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "warning" }),
4566
+ "\u539F .dsh/eng.json \u65E0\u6CD5\u89E3\u6790"
4567
+ ] }),
4568
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: styles.problemsList, children: notice.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: p }, p)) }),
4569
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u5F53\u524D\u663E\u793A\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E\uFF1B\u70B9\u51FB\u4FDD\u5B58\u4F1A\u8986\u76D6\u539F\u6587\u4EF6\u3002" })
4570
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.okText, children: [
4571
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "done" }),
4572
+ source === "default" ? "\u5F53\u524D\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E" : "\u914D\u7F6E\u6709\u6548"
4573
+ ] }),
4574
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.actions, children: [
4575
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4576
+ import_dsh_client_ui_primitives.Button,
4577
+ {
4578
+ variant: "primary",
4579
+ size: "md",
4580
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconCheckOutline16, { size: 16 }),
4581
+ disabled: problems.length > 0,
4582
+ onClick: () => {
4583
+ void save(remote, config, workspace, setSavedAt, setSource, setLoadedProblems);
4584
+ },
4585
+ children: "\u4FDD\u5B58\u5230 .dsh/eng.json"
4586
+ }
4587
+ ),
4588
+ savedAt !== "" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.okText, children: [
4589
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: savedAt.startsWith("\u5DF2") ? "done" : "error" }),
4590
+ savedAt
4591
+ ] }) : null
4592
+ ] })
4593
+ ] });
4294
4594
  }
4295
- function workspacePicker(workspaces, workspace, setWorkspace) {
4296
- if (workspaces.length === 0) return e("p", { style: styles.problems }, "\u5F53\u524D\u6CA1\u6709\u4EFB\u4F55\u5DE5\u4F5C\u533A\uFF1A\u8BF7\u5148\u5728\u4FA7\u680F\u521B\u5EFA\u4E00\u4E2A\u5DE5\u4F5C\u533A\u3002");
4297
- return e(
4298
- "label",
4299
- { style: styles.row },
4300
- "\u5DE5\u4F5C\u533A ",
4301
- e(
4302
- "select",
4303
- { value: workspace, onChange: (ev) => setWorkspace(ev.target.value) },
4304
- ...workspaces.map((w) => e("option", { key: w.path, value: w.path }, w.title || w.path))
4305
- )
4306
- );
4595
+ function sourceText(source) {
4596
+ switch (source) {
4597
+ case "project":
4598
+ return "\u9879\u76EE\u914D\u7F6E\uFF08.dsh/eng.json\uFF09";
4599
+ case "invalid":
4600
+ return "\u539F\u6587\u4EF6\u65E0\u6CD5\u89E3\u6790\uFF0C\u663E\u793A\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E";
4601
+ default:
4602
+ return "\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E\uFF08\u5DE5\u4F5C\u533A\u5C1A\u65E0 .dsh/eng.json\uFF09";
4603
+ }
4604
+ }
4605
+ function SectionCard({ icon, title, children }) {
4606
+ const [open, setOpen] = (0, import_react2.useState)(true);
4607
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.section, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4608
+ import_dsh_client_ui_primitives.DisclosureRow,
4609
+ {
4610
+ icon,
4611
+ title,
4612
+ open,
4613
+ expandable: true,
4614
+ onToggle: () => {
4615
+ setOpen(!open);
4616
+ },
4617
+ keepContentWhenOpen: true,
4618
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.cardBody, children })
4619
+ }
4620
+ ) });
4307
4621
  }
4308
- function stageEditor(config, setConfig) {
4309
- const stageText = () => config.stages.join("\n");
4310
- return e(
4311
- "div",
4312
- null,
4313
- e("textarea", {
4314
- style: styles.textarea,
4315
- rows: 4,
4316
- value: stageText(),
4317
- onChange: (ev) => update(config, setConfig, (c) => ({
4622
+ function StageEditor({ config, setConfig }) {
4623
+ const [draft, setDraft] = (0, import_react2.useState)("");
4624
+ const addStage = () => {
4625
+ const name = draft.trim();
4626
+ if (name === "" || config.stages.includes(name)) return;
4627
+ update(config, setConfig, (c) => ({ ...c, stages: [...c.stages, name] }));
4628
+ setDraft("");
4629
+ };
4630
+ const removeStage = (stage) => {
4631
+ update(config, setConfig, (c) => {
4632
+ const stages = c.stages.filter((s) => s !== stage);
4633
+ return {
4318
4634
  ...c,
4319
- stages: ev.target.value.split("\n").map((s) => s.trim()).filter((s) => s.length > 0)
4320
- }))
4321
- }),
4322
- e(
4323
- "label",
4324
- { style: styles.row },
4325
- "\u8D77\u59CB\u9636\u6BB5 ",
4326
- e(
4635
+ stages,
4636
+ start_stage: c.start_stage === stage ? stages[0] ?? "" : c.start_stage
4637
+ };
4638
+ });
4639
+ };
4640
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
4641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: config.stages.map((stage) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4642
+ import_dsh_client_ui_primitives.Pill,
4643
+ {
4644
+ active: stage === config.start_stage,
4645
+ onClick: stage === config.start_stage ? void 0 : () => {
4646
+ removeStage(stage);
4647
+ },
4648
+ title: stage === config.start_stage ? "\u8D77\u59CB\u9636\u6BB5" : `\u70B9\u51FB\u5220\u9664 ${stage}`,
4649
+ children: stage
4650
+ },
4651
+ stage
4652
+ )) }),
4653
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
4654
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4655
+ "input",
4656
+ {
4657
+ style: { ...control, flex: "1 1 180px" },
4658
+ placeholder: "\u65B0\u9636\u6BB5\u540D",
4659
+ value: draft,
4660
+ onChange: (ev) => {
4661
+ setDraft(ev.target.value);
4662
+ },
4663
+ onKeyDown: (ev) => {
4664
+ if (ev.key === "Enter") addStage();
4665
+ }
4666
+ }
4667
+ ),
4668
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), disabled: draft.trim() === "", onClick: addStage, children: "\u6DFB\u52A0\u9636\u6BB5" })
4669
+ ] }),
4670
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
4671
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u8D77\u59CB\u9636\u6BB5" }),
4672
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4327
4673
  "select",
4328
- { value: config.start_stage, onChange: (ev) => update(config, setConfig, (c) => ({ ...c, start_stage: ev.target.value })) },
4329
- ...config.stages.map((s) => e("option", { key: s, value: s }, s))
4674
+ {
4675
+ style: control,
4676
+ value: config.start_stage,
4677
+ onChange: (ev) => {
4678
+ update(config, setConfig, (c) => ({ ...c, start_stage: ev.target.value }));
4679
+ },
4680
+ children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: s, children: s }, s))
4681
+ }
4330
4682
  )
4331
- )
4332
- );
4333
- }
4334
- function transitionEditor(config, setConfig) {
4335
- const rows = config.transitions.map((t, i) => e(
4336
- "div",
4337
- { key: i, style: styles.card },
4338
- selectOf(config, t.from, (v) => update(config, setConfig, (c) => ({ ...c, transitions: c.transitions.map((x, j) => j === i ? { ...x, from: v } : x) }))),
4339
- " \u2192 ",
4340
- selectOf(config, t.to, (v) => update(config, setConfig, (c) => ({ ...c, transitions: c.transitions.map((x, j) => j === i ? { ...x, to: v } : x) }))),
4341
- e("input", {
4342
- style: styles.input,
4343
- placeholder: "\u5B88\u536B\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u5982 todos_done, verified\uFF09",
4344
- value: (t.requires ?? []).join(", "),
4345
- onChange: (ev) => update(config, setConfig, (c) => ({ ...c, transitions: c.transitions.map((x, j) => j === i ? { ...x, requires: splitList(ev.target.value) } : x) }))
4346
- }),
4347
- e("button", { type: "button", onClick: () => update(config, setConfig, (c) => ({ ...c, transitions: c.transitions.filter((_, j) => j !== i) })) }, "\u5220\u9664")
4348
- ));
4349
- return e(
4350
- "div",
4351
- null,
4352
- ...rows,
4353
- e("button", { type: "button", onClick: () => update(config, setConfig, (c) => ({ ...c, transitions: [...c.transitions, { from: c.start_stage, to: c.start_stage }] })) }, "+ \u52A0\u4E00\u6761\u6D41\u8F6C")
4354
- );
4683
+ ] })
4684
+ ] });
4355
4685
  }
4356
- function artifactEditor(config, setConfig) {
4357
- const rows = config.artifacts.map((a, i) => e(
4358
- "div",
4359
- { key: i, style: styles.card },
4360
- selectOf(config, a.stage, (v) => update(config, setConfig, (c) => ({ ...c, artifacts: c.artifacts.map((x, j) => j === i ? { ...x, stage: v } : x) }))),
4361
- e("input", { style: styles.input, placeholder: "id", value: a.id, onChange: (ev) => update(config, setConfig, (c) => ({ ...c, artifacts: c.artifacts.map((x, j) => j === i ? { ...x, id: ev.target.value } : x) })) }),
4362
- e("input", { style: styles.input, placeholder: "\u540D\u79F0", value: a.name, onChange: (ev) => update(config, setConfig, (c) => ({ ...c, artifacts: c.artifacts.map((x, j) => j === i ? { ...x, name: ev.target.value } : x) })) }),
4363
- e("input", { style: styles.input, placeholder: "\u5B57\u6BB5\uFF0C\u9017\u53F7\u5206\u9694", value: a.fields.join(", "), onChange: (ev) => update(config, setConfig, (c) => ({ ...c, artifacts: c.artifacts.map((x, j) => j === i ? { ...x, fields: splitList(ev.target.value) } : x) })) }),
4364
- e("button", { type: "button", onClick: () => update(config, setConfig, (c) => ({ ...c, artifacts: c.artifacts.filter((_, j) => j !== i) })) }, "\u5220\u9664")
4365
- ));
4366
- return e(
4367
- "div",
4368
- null,
4369
- ...rows,
4370
- e("button", { type: "button", onClick: () => update(config, setConfig, (c) => ({ ...c, artifacts: [...c.artifacts, { stage: c.start_stage, id: "", name: "", fields: [] }] })) }, "+ \u52A0\u4E00\u4E2A\u4EA7\u7269")
4371
- );
4686
+ function TransitionEditor({ config, setConfig }) {
4687
+ const setTransition = (i, patch) => {
4688
+ update(config, setConfig, (c) => ({
4689
+ ...c,
4690
+ transitions: c.transitions.map((t, j) => j === i ? { ...t, ...patch } : t)
4691
+ }));
4692
+ };
4693
+ const toggleGuard = (i, guard) => {
4694
+ const t = config.transitions[i];
4695
+ if (t === void 0) return;
4696
+ const current = t.requires ?? [];
4697
+ const next = current.includes(guard) ? current.filter((g) => g !== guard) : [...current, guard];
4698
+ setTransition(i, { requires: next });
4699
+ };
4700
+ const remove = (i) => {
4701
+ update(config, setConfig, (c) => ({ ...c, transitions: c.transitions.filter((_, j) => j !== i) }));
4702
+ };
4703
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
4704
+ config.transitions.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: cardV, children: [
4705
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
4706
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { style: control, value: t.from, onChange: (ev) => {
4707
+ setTransition(i, { from: ev.target.value });
4708
+ }, children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: s, children: s }, s)) }),
4709
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: textTertiary3 }, children: "\u2192" }),
4710
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { style: control, value: t.to, onChange: (ev) => {
4711
+ setTransition(i, { to: ev.target.value });
4712
+ }, children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: s, children: s }, s)) }),
4713
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 }), onClick: () => {
4714
+ remove(i);
4715
+ }, title: "\u5220\u9664\u8FD9\u6761\u6D41\u8F6C" })
4716
+ ] }),
4717
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: GUARD_OPTIONS.map((g) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4718
+ import_dsh_client_ui_primitives.Pill,
4719
+ {
4720
+ active: (t.requires ?? []).includes(g.value),
4721
+ onClick: () => {
4722
+ toggleGuard(i, g.value);
4723
+ },
4724
+ title: `\u5B88\u536B ${g.value}`,
4725
+ children: g.label
4726
+ },
4727
+ g.value
4728
+ )) })
4729
+ ] }, i)),
4730
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4731
+ import_dsh_client_ui_primitives.Button,
4732
+ {
4733
+ variant: "outline",
4734
+ size: "sm",
4735
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
4736
+ onClick: () => {
4737
+ update(config, setConfig, (c) => ({ ...c, transitions: [...c.transitions, { from: c.start_stage, to: c.start_stage }] }));
4738
+ },
4739
+ children: "\u52A0\u4E00\u6761\u6D41\u8F6C"
4740
+ }
4741
+ ) })
4742
+ ] });
4372
4743
  }
4373
- function commitEditor(config, setConfig) {
4374
- const c = config.commit;
4375
- return e(
4376
- "div",
4377
- null,
4378
- e(
4379
- "label",
4380
- { style: styles.row },
4381
- "\u63D0\u4EA4\u7B56\u7565 ",
4382
- e(
4383
- "select",
4384
- { value: c.policy, onChange: (ev) => update(config, setConfig, (cfg) => ({ ...cfg, commit: { ...cfg.commit, policy: ev.target.value } })) },
4385
- e("option", { value: "task" }, "task\uFF08\u68C0\u67E5\u70B9\u4E00\u6B21\u63D0\u4EA4\uFF09"),
4386
- e("option", { value: "item" }, "item\uFF08\u9010\u9879\u63D0\u4EA4 + \u6536\u5C3E TASK\uFF09"),
4387
- e("option", { value: "manual" }, "manual\uFF08\u4EBA\u5DE5\u6307\u793A\uFF09")
4744
+ function ArtifactEditor({ config, setConfig }) {
4745
+ const setArtifact = (i, patch) => {
4746
+ update(config, setConfig, (c) => ({
4747
+ ...c,
4748
+ artifacts: c.artifacts.map((a, j) => j === i ? { ...a, ...patch } : a)
4749
+ }));
4750
+ };
4751
+ const remove = (i) => {
4752
+ update(config, setConfig, (c) => ({ ...c, artifacts: c.artifacts.filter((_, j) => j !== i) }));
4753
+ };
4754
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
4755
+ config.artifacts.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: cardV, children: [
4756
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
4757
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { style: control, value: a.stage, onChange: (ev) => {
4758
+ setArtifact(i, { stage: ev.target.value });
4759
+ }, children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: s, children: s }, s)) }),
4760
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: { ...control, flex: "1 1 120px" }, placeholder: "id", value: a.id, onChange: (ev) => {
4761
+ setArtifact(i, { id: ev.target.value });
4762
+ } }),
4763
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: { ...control, flex: "1 1 140px" }, placeholder: "\u540D\u79F0", value: a.name, onChange: (ev) => {
4764
+ setArtifact(i, { name: ev.target.value });
4765
+ } }),
4766
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 }), onClick: () => {
4767
+ remove(i);
4768
+ }, title: "\u5220\u9664\u8FD9\u4E2A\u4EA7\u7269" })
4769
+ ] }),
4770
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4771
+ "input",
4772
+ {
4773
+ style: control,
4774
+ placeholder: "\u5B57\u6BB5\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u5982 scope, acceptance_criteria\uFF09",
4775
+ value: a.fields.join(", "),
4776
+ onChange: (ev) => {
4777
+ setArtifact(i, { fields: splitList(ev.target.value) });
4778
+ }
4779
+ }
4388
4780
  )
4389
- ),
4390
- e(
4391
- "label",
4392
- { style: styles.row },
4393
- "\u6D88\u606F\u683C\u5F0F\uFF08\u6B63\u5219\uFF09",
4394
- e("input", { style: styles.input, value: c.message_pattern, onChange: (ev) => update(config, setConfig, (cfg) => ({ ...cfg, commit: { ...cfg.commit, message_pattern: ev.target.value } })) })
4395
- ),
4396
- e(
4397
- "label",
4398
- { style: styles.row },
4399
- "\u683C\u5F0F\u8BF4\u660E",
4400
- e("input", { style: styles.input, value: c.message_hint, onChange: (ev) => update(config, setConfig, (cfg) => ({ ...cfg, commit: { ...cfg.commit, message_hint: ev.target.value } })) })
4401
- ),
4402
- e(
4403
- "div",
4404
- { style: styles.row },
4405
- "\u63D0\u4EA4\u68C0\u67E5\u70B9\uFF1A",
4406
- ...config.stages.map((s) => e(
4407
- "label",
4408
- { key: s, style: styles.inline },
4409
- e("input", {
4410
- type: "checkbox",
4411
- checked: c.checkpoints.includes(s),
4412
- onChange: (ev) => update(config, setConfig, (cfg) => ({
4413
- ...cfg,
4414
- commit: { ...cfg.commit, checkpoints: toggle(cfg.commit.checkpoints, s, ev.target.checked) }
4415
- }))
4416
- }),
4417
- s
4418
- ))
4419
- ),
4420
- e(
4421
- "label",
4422
- { style: styles.row },
4423
- e("input", { type: "checkbox", checked: c.file_scope, onChange: (ev) => update(config, setConfig, (cfg) => ({ ...cfg, commit: { ...cfg.commit, file_scope: ev.target.checked } })) }),
4424
- "\u6587\u4EF6\u8303\u56F4\u5F00\u5173\uFF08\u63D0\u4EA4\u53EA\u5141\u8BB8\u4EFB\u52A1\u91CC\u58F0\u660E\u7684\u6587\u4EF6\uFF09"
4425
- )
4426
- );
4781
+ ] }, i)),
4782
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4783
+ import_dsh_client_ui_primitives.Button,
4784
+ {
4785
+ variant: "outline",
4786
+ size: "sm",
4787
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
4788
+ onClick: () => {
4789
+ update(config, setConfig, (c) => ({ ...c, artifacts: [...c.artifacts, { stage: c.start_stage, id: "", name: "", fields: [] }] }));
4790
+ },
4791
+ children: "\u52A0\u4E00\u4E2A\u4EA7\u7269"
4792
+ }
4793
+ ) })
4794
+ ] });
4427
4795
  }
4428
- function selectOf(config, value, onChange) {
4429
- return e(
4430
- "select",
4431
- { style: styles.select, value, onChange: (ev) => onChange(ev.target.value) },
4432
- ...config.stages.map((s) => e("option", { key: s, value: s }, s))
4433
- );
4796
+ function CommitEditor({ config, setConfig }) {
4797
+ const commit = config.commit;
4798
+ const setCommit = (patch) => {
4799
+ update(config, setConfig, (c) => ({ ...c, commit: { ...c.commit, ...patch } }));
4800
+ };
4801
+ const toggleCheckpoint = (stage) => {
4802
+ const current = commit.checkpoints;
4803
+ const next = current.includes(stage) ? current.filter((s) => s !== stage) : [...current, stage];
4804
+ setCommit({ checkpoints: next });
4805
+ };
4806
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
4807
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
4808
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u63D0\u4EA4\u7B56\u7565" }),
4809
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: POLICY_OPTIONS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: commit.policy === p.value, onClick: () => {
4810
+ setCommit({ policy: p.value });
4811
+ }, title: p.hint, children: p.label }, p.value)) }),
4812
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.hint, children: POLICY_OPTIONS.find((p) => p.value === commit.policy)?.hint })
4813
+ ] }),
4814
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
4815
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: styles.field, children: [
4816
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u63D0\u4EA4\u6D88\u606F\u683C\u5F0F\uFF08\u6B63\u5219\uFF0C\u7559\u7A7A\u5219\u4E0D\u6821\u9A8C\uFF09" }),
4817
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: control, value: commit.message_pattern, onChange: (ev) => {
4818
+ setCommit({ message_pattern: ev.target.value });
4819
+ } })
4820
+ ] }),
4821
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: styles.field, children: [
4822
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u683C\u5F0F\u8BF4\u660E" }),
4823
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: control, value: commit.message_hint, onChange: (ev) => {
4824
+ setCommit({ message_hint: ev.target.value });
4825
+ } })
4826
+ ] })
4827
+ ] }),
4828
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
4829
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u63D0\u4EA4\u68C0\u67E5\u70B9\uFF08\u5230\u8FBE\u8FD9\u4E9B\u9636\u6BB5\u524D\u9700\u63D0\u4EA4\uFF09" }),
4830
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: commit.checkpoints.includes(s), onClick: () => {
4831
+ toggleCheckpoint(s);
4832
+ }, title: `\u68C0\u67E5\u70B9 ${s}`, children: s }, s)) })
4833
+ ] }),
4834
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
4835
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.inline, children: [
4836
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4837
+ "input",
4838
+ {
4839
+ type: "checkbox",
4840
+ checked: commit.file_scope,
4841
+ onChange: (ev) => {
4842
+ setCommit({ file_scope: ev.target.checked });
4843
+ }
4844
+ }
4845
+ ),
4846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u6587\u4EF6\u8303\u56F4\u5F00\u5173\uFF08\u63D0\u4EA4\u53EA\u5141\u8BB8\u4EFB\u52A1\u91CC\u58F0\u660E\u7684\u6587\u4EF6\uFF09" })
4847
+ ] }),
4848
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.inline, children: [
4849
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4850
+ "input",
4851
+ {
4852
+ type: "checkbox",
4853
+ checked: config.high_risk_requires_verification,
4854
+ onChange: (ev) => {
4855
+ update(config, setConfig, (c) => ({ ...c, high_risk_requires_verification: ev.target.checked }));
4856
+ }
4857
+ }
4858
+ ),
4859
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u9AD8\u98CE\u9669\u4EFB\u52A1\u5FC5\u987B\u9644\u9A8C\u8BC1\u8BC1\u636E\u624D\u80FD\u8FC7 verified \u5B88\u536B" })
4860
+ ] })
4861
+ ] })
4862
+ ] });
4434
4863
  }
4435
4864
  function update(config, setConfig, fn) {
4436
4865
  setConfig(fn(config));
4437
4866
  }
4438
- async function save(props, config, workspace, setSavedAt, setSource) {
4439
- const result = await props.remote.write({ path: workspace, config });
4440
- if (result.ok) {
4441
- setSource(result.value.source);
4442
- setSavedAt("\u5DF2\u4FDD\u5B58");
4443
- }
4867
+ async function save(remote, config, workspace, setSavedAt, setSource, setLoadedProblems) {
4868
+ const result = await remote.write({ path: workspace, config });
4869
+ if (!result.ok) {
4870
+ setSavedAt("\u4FDD\u5B58\u5931\u8D25\uFF1A" + describeError(result.error));
4871
+ return;
4872
+ }
4873
+ setSource(result.value.source);
4874
+ setLoadedProblems(result.value.source === "invalid" ? result.value.problems : []);
4875
+ setSavedAt(result.value.ok ? "\u5DF2\u4FDD\u5B58" : "");
4444
4876
  }
4445
- function splitList(text) {
4446
- return text.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
4877
+ function splitList(textInput) {
4878
+ return textInput.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
4447
4879
  }
4448
- function toggle(list, value, on) {
4449
- return on ? list.includes(value) ? list : [...list, value] : list.filter((v) => v !== value);
4880
+ function describeError(error) {
4881
+ if (error instanceof Error) return error.message;
4882
+ if (typeof error === "string") return error;
4883
+ return "\u672A\u77E5\u9519\u8BEF";
4450
4884
  }
4451
- var styles = {
4452
- wrap: { padding: "16px", maxWidth: "760px", display: "flex", flexDirection: "column", gap: "8px" },
4453
- title: { margin: "0 0 4px" },
4454
- muted: { margin: "0 0 8px", opacity: "0.7" },
4455
- row: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" },
4456
- inline: { display: "inline-flex", alignItems: "center", gap: "4px", marginRight: "12px" },
4457
- card: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap", padding: "4px 0" },
4458
- input: { flex: "1 1 220px", minWidth: "140px" },
4459
- select: { minWidth: "90px" },
4460
- textarea: { width: "100%", fontFamily: "monospace" },
4461
- problems: { color: "#c62828", background: "#fdecea", padding: "8px 12px", borderRadius: "4px" },
4462
- ok: { color: "#2e7d32" },
4463
- actions: { display: "flex", alignItems: "center", gap: "12px", marginTop: "8px" }
4464
- };
4465
4885
 
4466
4886
  // src/client/index.ts
4467
4887
  var inject = ["remote", "slots"];
4468
4888
  async function apply(ctx) {
4469
4889
  const disposeRemote = await ctx.remote.$mount(remote_default);
4470
- const taskEngine = ctx.remote["task-engine"];
4890
+ const taskEngine = ctx.get("remote.task-engine");
4891
+ if (taskEngine === void 0) {
4892
+ await disposeRemote();
4893
+ throw new Error("task-engine: remote.task-engine namespace failed to mount");
4894
+ }
4471
4895
  ctx.slots.inject("settings.section", () => ctx.slots.register({
4472
4896
  name: "settings.section",
4473
4897
  id: "task-engine",