@godv61/dsh-task-engine 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/defaults/eng.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
- "stages": ["requirement_review", "design", "implement", "deliver", "review", "done"],
3
- "start_stage": "requirement_review",
2
+ "stages": ["需求评审", "设计", "开发", "交付", "代码审核", "完成"],
3
+ "start_stage": "需求评审",
4
4
  "transitions": [
5
- { "from": "requirement_review", "to": "design", "requires": ["requirement_confirmation", "artifacts_present"] },
6
- { "from": "design", "to": "implement", "requires": ["solution_confirmation", "artifacts_present"] },
7
- { "from": "implement", "to": "deliver", "requires": ["todos_done"] },
8
- { "from": "deliver", "to": "review", "requires": ["verified"] },
9
- { "from": "review", "to": "done", "requires": ["review_passed", "artifacts_present"] }
5
+ { "from": "需求评审", "to": "设计", "requires": ["requirement_confirmation", "artifacts_present"] },
6
+ { "from": "设计", "to": "开发", "requires": ["solution_confirmation", "artifacts_present"] },
7
+ { "from": "开发", "to": "交付", "requires": ["todos_done"] },
8
+ { "from": "交付", "to": "代码审核", "requires": ["verified"] },
9
+ { "from": "代码审核", "to": "完成", "requires": ["review_passed", "artifacts_present"] }
10
10
  ],
11
11
  "artifacts": [
12
- { "stage": "requirement_review", "id": "requirement", "name": "需求说明", "fields": ["scope", "acceptance_criteria"] },
13
- { "stage": "design", "id": "design", "name": "设计文档", "fields": ["approach", "risks", "impact"] },
14
- { "stage": "review", "id": "review", "name": "评审记录", "fields": ["conclusion", "issues"] }
12
+ { "stage": "需求评审", "id": "requirement", "name": "需求说明", "fields": ["scope", "acceptance_criteria"] },
13
+ { "stage": "设计", "id": "design", "name": "设计文档", "fields": ["approach", "risks", "impact"] },
14
+ { "stage": "代码审核", "id": "review", "name": "评审记录", "fields": ["conclusion", "issues"] }
15
15
  ],
16
16
  "commit": {
17
17
  "policy": "task",
18
18
  "message_pattern": "^【(\\S+)】【(?:TASK|T\\d+)】.+",
19
19
  "message_hint": "【<module>】【TASK/T1】说明 —— 写结果,不写空泛动作",
20
- "checkpoints": ["deliver"],
20
+ "checkpoints": ["交付"],
21
21
  "file_scope": true
22
22
  },
23
23
  "high_risk_requires_verification": true
package/hooks/commit-msg CHANGED
@@ -33,25 +33,25 @@ function readJson(abs) {
33
33
 
34
34
  // Mirrors defaults/eng.json — keep in sync.
35
35
  const DEFAULTS = {
36
- stages: ['requirement_review', 'design', 'implement', 'deliver', 'review', 'done'],
37
- start_stage: 'requirement_review',
36
+ stages: ['需求评审', '设计', '开发', '交付', '代码审核', '完成'],
37
+ start_stage: '需求评审',
38
38
  transitions: [
39
- { from: 'requirement_review', to: 'design', requires: ['requirement_confirmation', 'artifacts_present'] },
40
- { from: 'design', to: 'implement', requires: ['solution_confirmation', 'artifacts_present'] },
41
- { from: 'implement', to: 'deliver', requires: ['todos_done'] },
42
- { from: 'deliver', to: 'review', requires: ['verified'] },
43
- { from: 'review', to: 'done', requires: ['review_passed', 'artifacts_present'] },
39
+ { from: '需求评审', to: '设计', requires: ['requirement_confirmation', 'artifacts_present'] },
40
+ { from: '设计', to: '开发', requires: ['solution_confirmation', 'artifacts_present'] },
41
+ { from: '开发', to: '交付', requires: ['todos_done'] },
42
+ { from: '交付', to: '代码审核', requires: ['verified'] },
43
+ { from: '代码审核', to: '完成', requires: ['review_passed', 'artifacts_present'] },
44
44
  ],
45
45
  artifacts: [
46
- { stage: 'requirement_review', id: 'requirement', name: '需求说明', fields: ['scope', 'acceptance_criteria'] },
47
- { stage: 'design', id: 'design', name: '设计文档', fields: ['approach', 'risks', 'impact'] },
48
- { stage: 'review', id: 'review', name: '评审记录', fields: ['conclusion', 'issues'] },
46
+ { stage: '需求评审', id: 'requirement', name: '需求说明', fields: ['scope', 'acceptance_criteria'] },
47
+ { stage: '设计', id: 'design', name: '设计文档', fields: ['approach', 'risks', 'impact'] },
48
+ { stage: '代码审核', id: 'review', name: '评审记录', fields: ['conclusion', 'issues'] },
49
49
  ],
50
50
  commit: {
51
51
  policy: 'task',
52
52
  message_pattern: '^【(\\S+)】【(?:TASK|T\\d+)】.+',
53
53
  message_hint: '【<module>】【TASK/T1】说明',
54
- checkpoints: ['deliver'],
54
+ checkpoints: ['交付'],
55
55
  file_scope: true,
56
56
  },
57
57
  high_risk_requires_verification: true,
package/lib/client.js CHANGED
@@ -4248,7 +4248,10 @@ function edgeList(config) {
4248
4248
  function nodeX(index, stageCount) {
4249
4249
  return index < 0 ? margin + stageCount * stepX + nodeW : margin + index * stepX;
4250
4250
  }
4251
- function FlowDiagram({ config }) {
4251
+ function FlowDiagram({ config, onAddTransition, onRemoveTransition }) {
4252
+ const [dragFrom, setDragFrom] = (0, import_react.useState)(null);
4253
+ const [dragPos, setDragPos] = (0, import_react.useState)(null);
4254
+ const svgRef = (0, import_react.useRef)(null);
4252
4255
  const stages = config.stages;
4253
4256
  const edges = edgeList(config);
4254
4257
  const stageCount = stages.length;
@@ -4258,13 +4261,27 @@ function FlowDiagram({ config }) {
4258
4261
  };
4259
4262
  const width = Math.max(margin * 2 + stageCount * stepX, 360);
4260
4263
  const height = 210;
4261
- const nodeTitle = (stage) => `${stage}${stage === config.start_stage ? "\uFF08\u8D77\u59CB\uFF09" : ""}`;
4264
+ const toSvg = (e) => {
4265
+ const rect = svgRef.current.getBoundingClientRect();
4266
+ return { x: e.clientX - rect.left, y: e.clientY - rect.top };
4267
+ };
4268
+ const startDrag = (stage, e) => {
4269
+ setDragFrom(stage);
4270
+ setDragPos(toSvg(e));
4271
+ };
4272
+ const dropOn = (stage) => {
4273
+ if (dragFrom !== null && dragFrom !== stage) onAddTransition(dragFrom, stage);
4274
+ setDragFrom(null);
4275
+ setDragPos(null);
4276
+ };
4277
+ const cancelDrag = () => {
4278
+ setDragFrom(null);
4279
+ setDragPos(null);
4280
+ };
4262
4281
  const forwardPath = (from, to) => {
4263
4282
  const x1 = nodeX(from, stageCount) + nodeW;
4264
4283
  const x2 = nodeX(to, stageCount);
4265
- if (to === from + 1) {
4266
- return `M ${x1} ${nodeCenterY} L ${x2} ${nodeCenterY}`;
4267
- }
4284
+ if (to === from + 1) return `M ${x1} ${nodeCenterY} L ${x2} ${nodeCenterY}`;
4268
4285
  const cx = (x1 + x2) / 2;
4269
4286
  const cy = nodeCenterY + (to - from) * 20;
4270
4287
  return `M ${x1} ${nodeCenterY} Q ${cx} ${cy} ${x2} ${nodeCenterY}`;
@@ -4281,18 +4298,38 @@ function FlowDiagram({ config }) {
4281
4298
  const cx = (x1 + x2) / 2;
4282
4299
  return `M ${x1} ${y1} Q ${cx} ${y1 - lift} ${x2} ${y1}`;
4283
4300
  };
4284
- const strokeOf = (edge) => edge.orphaned ? danger : textTertiary;
4285
4301
  const forwardLabelY = (from, to) => to === from + 1 ? nodeCenterY - 8 : nodeCenterY + (to - from) * 20 + 16;
4286
4302
  const returnLabelY = (from, to) => nodeTop - 30 - Math.abs(from - to) * 6;
4303
+ const dragLine = dragFrom !== null && dragPos !== null ? (() => {
4304
+ const from = indexOf(dragFrom);
4305
+ const cx = from < 0 ? nodeX(-1, stageCount) + nodeW / 2 : nodeX(from, stageCount) + nodeW / 2;
4306
+ const cy = nodeTop;
4307
+ return (0, import_react.createElement)("line", {
4308
+ x1: cx,
4309
+ y1: cy,
4310
+ x2: dragPos.x,
4311
+ y2: dragPos.y,
4312
+ stroke: brand,
4313
+ strokeWidth: 1.5,
4314
+ strokeDasharray: "5 3",
4315
+ pointerEvents: "none"
4316
+ });
4317
+ })() : null;
4287
4318
  return (0, import_react.createElement)(
4288
4319
  "svg",
4289
4320
  {
4321
+ ref: svgRef,
4290
4322
  width,
4291
4323
  height,
4292
4324
  viewBox: `0 0 ${width} ${height}`,
4293
4325
  style: { display: "block", minWidth: "100%", fontFamily: "inherit" },
4294
4326
  role: "img",
4295
- "aria-label": "\u5DE5\u7A0B\u9636\u6BB5\u6D41\u8F6C\u56FE"
4327
+ "aria-label": "\u5DE5\u7A0B\u9636\u6BB5\u6D41\u8F6C\u56FE\uFF08\u53EF\u62D6\u62FD\u8FDE\u7EBF\u3001\u70B9\u51FB\u7BAD\u5934\u5220\u9664\uFF09",
4328
+ onMouseMove: dragFrom === null ? void 0 : (e) => {
4329
+ setDragPos(toSvg(e));
4330
+ },
4331
+ onMouseUp: cancelDrag,
4332
+ onMouseLeave: cancelDrag
4296
4333
  },
4297
4334
  (0, import_react.createElement)(
4298
4335
  "defs",
@@ -4310,7 +4347,7 @@ function FlowDiagram({ config }) {
4310
4347
  ...edges.map((edge, i) => {
4311
4348
  const from = indexOf(edge.from);
4312
4349
  const to = indexOf(edge.to);
4313
- const stroke = strokeOf(edge);
4350
+ const stroke = edge.orphaned ? danger : textTertiary;
4314
4351
  const d = from < to ? forwardPath(from, to) : returnPath(from, to < 0 ? from : to);
4315
4352
  const labelY = from < to ? forwardLabelY(from, to) : returnLabelY(from, to < 0 ? from : to);
4316
4353
  const labelX = from < to ? (nodeX(from, stageCount) + nodeW + nodeX(to, stageCount)) / 2 : (nodeX(from, stageCount) + nodeX(to < 0 ? from : to, stageCount) + nodeW) / 2;
@@ -4321,6 +4358,10 @@ function FlowDiagram({ config }) {
4321
4358
  stroke,
4322
4359
  strokeWidth: 1.5,
4323
4360
  markerEnd: "url(#flow-arrow)",
4361
+ style: { cursor: "pointer" },
4362
+ onClick: () => {
4363
+ onRemoveTransition(i);
4364
+ },
4324
4365
  ...edge.orphaned ? { strokeDasharray: "4 3" } : {}
4325
4366
  };
4326
4367
  const label = edge.label === null ? null : (0, import_react.createElement)("text", {
@@ -4329,10 +4370,21 @@ function FlowDiagram({ config }) {
4329
4370
  textAnchor: "middle",
4330
4371
  fontSize: 11,
4331
4372
  fill: edge.orphaned ? danger : textSecondary
4332
- }, edge.from === edge.to ? edge.label : edge.label);
4373
+ }, edge.label);
4333
4374
  return (0, import_react.createElement)(
4334
4375
  "g",
4335
4376
  { key: `edgeg-${i}` },
4377
+ (0, import_react.createElement)(
4378
+ "path",
4379
+ {
4380
+ ...common,
4381
+ strokeWidth: 6,
4382
+ stroke: "transparent",
4383
+ fill: "none",
4384
+ markerEnd: void 0,
4385
+ strokeDasharray: void 0
4386
+ }
4387
+ ),
4336
4388
  (0, import_react.createElement)("path", common),
4337
4389
  label
4338
4390
  );
@@ -4342,7 +4394,16 @@ function FlowDiagram({ config }) {
4342
4394
  const isStart = stage === config.start_stage;
4343
4395
  return (0, import_react.createElement)(
4344
4396
  "g",
4345
- { key: stage },
4397
+ {
4398
+ key: stage,
4399
+ style: { cursor: dragFrom === null ? "crosshair" : "pointer" },
4400
+ onMouseDown: (e) => {
4401
+ startDrag(stage, e);
4402
+ },
4403
+ onMouseUp: () => {
4404
+ dropOn(stage);
4405
+ }
4406
+ },
4346
4407
  (0, import_react.createElement)("rect", {
4347
4408
  x,
4348
4409
  y: nodeTop,
@@ -4363,12 +4424,80 @@ function FlowDiagram({ config }) {
4363
4424
  fill: "var(--dsw-alias-label-primary)",
4364
4425
  fontWeight: isStart ? 600 : 400
4365
4426
  }, stage),
4366
- (0, import_react.createElement)("title", null, nodeTitle(stage))
4427
+ (0, import_react.createElement)("title", null, `${stage}${stage === config.start_stage ? "\uFF08\u8D77\u59CB\uFF09" : ""} \u2014 \u62D6\u5230\u53E6\u4E00\u9636\u6BB5\u65B0\u5EFA\u6D41\u8F6C`)
4367
4428
  );
4368
- })
4429
+ }),
4430
+ dragLine
4369
4431
  );
4370
4432
  }
4371
4433
 
4434
+ // src/client/templates.ts
4435
+ var STANDARD = {
4436
+ stages: ["\u9700\u6C42\u8BC4\u5BA1", "\u8BBE\u8BA1", "\u5F00\u53D1", "\u4EA4\u4ED8", "\u4EE3\u7801\u5BA1\u6838", "\u5B8C\u6210"],
4437
+ start_stage: "\u9700\u6C42\u8BC4\u5BA1",
4438
+ transitions: [
4439
+ { from: "\u9700\u6C42\u8BC4\u5BA1", to: "\u8BBE\u8BA1", requires: ["requirement_confirmation", "artifacts_present"] },
4440
+ { from: "\u8BBE\u8BA1", to: "\u5F00\u53D1", requires: ["solution_confirmation", "artifacts_present"] },
4441
+ { from: "\u5F00\u53D1", to: "\u4EA4\u4ED8", requires: ["todos_done"] },
4442
+ { from: "\u4EA4\u4ED8", to: "\u4EE3\u7801\u5BA1\u6838", requires: ["verified"] },
4443
+ { from: "\u4EE3\u7801\u5BA1\u6838", to: "\u5B8C\u6210", requires: ["review_passed", "artifacts_present"] }
4444
+ ],
4445
+ artifacts: [
4446
+ { stage: "\u9700\u6C42\u8BC4\u5BA1", id: "requirement", name: "\u9700\u6C42\u8BF4\u660E", fields: ["scope", "acceptance_criteria"] },
4447
+ { stage: "\u8BBE\u8BA1", id: "design", name: "\u8BBE\u8BA1\u6587\u6863", fields: ["approach", "risks", "impact"] },
4448
+ { stage: "\u4EE3\u7801\u5BA1\u6838", id: "review", name: "\u8BC4\u5BA1\u8BB0\u5F55", fields: ["conclusion", "issues"] }
4449
+ ],
4450
+ commit: {
4451
+ policy: "task",
4452
+ message_pattern: "^\u3010(\\S+)\u3011\u3010(?:TASK|T\\d+)\u3011.+",
4453
+ message_hint: "\u3010<module>\u3011\u3010TASK/T1\u3011\u8BF4\u660E \u2014\u2014 \u5199\u7ED3\u679C\uFF0C\u4E0D\u5199\u7A7A\u6CDB\u52A8\u4F5C",
4454
+ checkpoints: ["\u4EA4\u4ED8"],
4455
+ file_scope: true
4456
+ },
4457
+ high_risk_requires_verification: true
4458
+ };
4459
+ var AGILE = {
4460
+ stages: ["\u9700\u6C42", "\u5F00\u53D1", "\u4EA4\u4ED8", "\u5BA1\u67E5"],
4461
+ start_stage: "\u9700\u6C42",
4462
+ transitions: [
4463
+ { from: "\u9700\u6C42", to: "\u5F00\u53D1", requires: ["requirement_confirmation"] },
4464
+ { from: "\u5F00\u53D1", to: "\u4EA4\u4ED8", requires: ["todos_done"] },
4465
+ { from: "\u4EA4\u4ED8", to: "\u5BA1\u67E5", requires: [] }
4466
+ ],
4467
+ artifacts: [
4468
+ { stage: "\u9700\u6C42", id: "requirement", name: "\u9700\u6C42\u8BF4\u660E", fields: ["scope"] }
4469
+ ],
4470
+ commit: {
4471
+ policy: "item",
4472
+ message_pattern: "^\u3010(\\S+)\u3011\u3010T\\d+\u3011.+",
4473
+ message_hint: "\u3010<module>\u3011\u3010T1\u3011\u8BF4\u660E",
4474
+ checkpoints: ["\u4EA4\u4ED8"],
4475
+ file_scope: true
4476
+ },
4477
+ high_risk_requires_verification: false
4478
+ };
4479
+ var MINIMAL = {
4480
+ stages: ["\u5F00\u53D1", "\u4EA4\u4ED8"],
4481
+ start_stage: "\u5F00\u53D1",
4482
+ transitions: [
4483
+ { from: "\u5F00\u53D1", to: "\u4EA4\u4ED8", requires: ["todos_done"] }
4484
+ ],
4485
+ artifacts: [],
4486
+ commit: {
4487
+ policy: "task",
4488
+ message_pattern: "",
4489
+ message_hint: "\u65E0\u683C\u5F0F\u8981\u6C42",
4490
+ checkpoints: ["\u4EA4\u4ED8"],
4491
+ file_scope: false
4492
+ },
4493
+ high_risk_requires_verification: false
4494
+ };
4495
+ var WORKFLOW_TEMPLATES = [
4496
+ { id: "standard", label: "\u6807\u51C6\u7814\u53D1", description: "\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", config: STANDARD },
4497
+ { id: "agile", label: "\u654F\u6377\u8F7B\u91CF", description: "\u9700\u6C42 \u2192 \u5F00\u53D1 \u2192 \u4EA4\u4ED8 \u2192 \u5BA1\u67E5\uFF0C\u56DB\u9636\u6BB5\u3001\u5C11\u4EA7\u7269", config: AGILE },
4498
+ { id: "minimal", label: "\u7EAF\u4EE3\u7801", description: "\u5F00\u53D1 \u2192 \u4EA4\u4ED8\uFF0C\u4E24\u9636\u6BB5\uFF0C\u53EA\u7559\u63D0\u4EA4\u95E8\u7981", config: MINIMAL }
4499
+ ];
4500
+
4372
4501
  // src/client/styles.ts
4373
4502
  var text = "var(--dsw-alias-label-primary)";
4374
4503
  var textSecondary2 = "var(--dsw-alias-label-secondary)";
@@ -4500,6 +4629,17 @@ function TaskEngineSection(props) {
4500
4629
  }, [remote, workspace]);
4501
4630
  const problems = (0, import_react2.useMemo)(() => config === null ? [] : validateWorkflow(config), [config]);
4502
4631
  const notice = problems.length === 0 && loadedProblems.length > 0 ? loadedProblems : [];
4632
+ const addTransition = (from, to) => {
4633
+ if (config === null || from === "" || to === "") return;
4634
+ update(config, setConfig, (c) => ({
4635
+ ...c,
4636
+ transitions: [...c.transitions, { from, to }]
4637
+ }));
4638
+ };
4639
+ const removeTransition = (index) => {
4640
+ if (config === null) return;
4641
+ update(config, setConfig, (c) => ({ ...c, transitions: c.transitions.filter((_, j) => j !== index) }));
4642
+ };
4503
4643
  if (workspaces.length === 0) {
4504
4644
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.wrap, children: [
4505
4645
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.title, children: "\u5DE5\u7A0B\u6D41\u7A0B\u914D\u7F6E" }),
@@ -4547,11 +4687,12 @@ function TaskEngineSection(props) {
4547
4687
  }
4548
4688
  )
4549
4689
  ] }),
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 }) }),
4690
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TemplatePicker, { config, setConfig }),
4691
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.flowCard, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FlowDiagram, { config, onAddTransition: addTransition, onRemoveTransition: removeTransition }) }),
4692
+ /* @__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", hint: "\u6D41\u7A0B\u7531\u8FD9\u4E9B\u9636\u6BB5\u6309\u987A\u5E8F\u7EC4\u6210\u3002\u70B9\u4E00\u4E2A\u6807\u7B7E\u53EF\u5220\u9664\u5B83\uFF08\u8D77\u59CB\u9636\u6BB5\u9664\u5916\uFF09\uFF1B\u7528\u4E2D\u6587\u540D\u66F4\u6613\u61C2\uFF0C\u4F8B\u5982\u300C\u9700\u6C42\u8BC4\u5BA1\u300D\u300C\u5F00\u53D1\u300D\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StageEditor, { config, setConfig }) }),
4693
+ /* @__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", hint: "\u6BCF\u6761\u6D41\u8F6C = \u4ECE\u300C\u4E0A\u4E00\u6B65\u300D\u5230\u300C\u4E0B\u4E00\u6B65\u300D\u5141\u8BB8\u8D70\u7684\u8DEF\uFF1B\u53EF\u9644\u52A0\u5B88\u536B\uFF0C\u6EE1\u8DB3\u5B88\u536B\u624D\u653E\u884C\u3002\u4E5F\u53EF\u5728\u6D41\u7A0B\u56FE\u91CC\u76F4\u63A5\u8FDE\u7EBF/\u70B9\u7BAD\u5934\u5220\u9664\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TransitionEditor, { config, setConfig }) }),
4694
+ /* @__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", hint: "\u7ED9\u67D0\u9636\u6BB5\u58F0\u660E\u8981\u4EA4\u7684\u4EA7\u7269\u548C\u5FC5\u586B\u5B57\u6BB5\uFF1B\u914D\u5408\u6D41\u8F6C\u91CC\u7684\u300C\u4EA7\u7269\u9F50\u5168\u300D\u5B88\u536B\uFF0C\u5B57\u6BB5\u6CA1\u586B\u5168\u5C31\u8FC7\u4E0D\u53BB\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArtifactEditor, { config, setConfig }) }),
4695
+ /* @__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", hint: "\u63D0\u4EA4\u7B56\u7565\u3001\u6D88\u606F\u683C\u5F0F\u3001\u63D0\u4EA4\u68C0\u67E5\u70B9\u3001\u6587\u4EF6\u8303\u56F4\uFF0C\u4EE5\u53CA\u9AD8\u98CE\u9669\u662F\u5426\u5FC5\u987B\u9644\u9A8C\u8BC1\u8BC1\u636E\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitEditor, { config, setConfig }) }),
4555
4696
  problems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.problems, children: [
4556
4697
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.problemsTitle, children: [
4557
4698
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "error" }),
@@ -4602,7 +4743,7 @@ function sourceText(source) {
4602
4743
  return "\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E\uFF08\u5DE5\u4F5C\u533A\u5C1A\u65E0 .dsh/eng.json\uFF09";
4603
4744
  }
4604
4745
  }
4605
- function SectionCard({ icon, title, children }) {
4746
+ function SectionCard({ icon, title, hint, children }) {
4606
4747
  const [open, setOpen] = (0, import_react2.useState)(true);
4607
4748
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.section, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4608
4749
  import_dsh_client_ui_primitives.DisclosureRow,
@@ -4615,10 +4756,30 @@ function SectionCard({ icon, title, children }) {
4615
4756
  setOpen(!open);
4616
4757
  },
4617
4758
  keepContentWhenOpen: true,
4618
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.cardBody, children })
4759
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.cardBody, children: [
4760
+ hint !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: hint }) : null,
4761
+ children
4762
+ ] })
4619
4763
  }
4620
4764
  ) });
4621
4765
  }
4766
+ function TemplatePicker({ config, setConfig }) {
4767
+ const apply2 = (id) => {
4768
+ const template = WORKFLOW_TEMPLATES.find((t) => t.id === id);
4769
+ if (template === void 0) return;
4770
+ setConfig(cloneConfig(template.config));
4771
+ };
4772
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
4773
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u6D41\u7A0B\u6A21\u677F\uFF08\u70B9\u4E00\u4E0B\u6574\u4F53\u5957\u7528\uFF0C\u4F1A\u8986\u76D6\u5F53\u524D\u7F16\u8F91\uFF09" }),
4774
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: WORKFLOW_TEMPLATES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: config.stages.join(",") === t.config.stages.join(","), onClick: () => {
4775
+ apply2(t.id);
4776
+ }, title: t.description, children: t.label }, t.id)) }),
4777
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.hint, children: "\u4E0D\u786E\u5B9A\u600E\u4E48\u914D\u5C31\u4ECE\u300C\u6807\u51C6\u7814\u53D1\u300D\u8D77\u6B65\uFF0C\u5957\u7528\u540E\u518D\u5FAE\u8C03\uFF1B\u4E5F\u53EF\u4EE5\u5148\u5957\u6A21\u677F\uFF0C\u518D\u7528\u4E0B\u9762\u7684\u5361\u7247\u6539\u3002" })
4778
+ ] });
4779
+ }
4780
+ function cloneConfig(config) {
4781
+ return JSON.parse(JSON.stringify(config));
4782
+ }
4622
4783
  function StageEditor({ config, setConfig }) {
4623
4784
  const [draft, setDraft] = (0, import_react2.useState)("");
4624
4785
  const addStage = () => {