dsh-plugin-prompt-tool 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +6 -6
  2. package/engine/actions/catalog.mjs +1 -1
  3. package/engine/actions/inbox-prepend.mjs +1 -2
  4. package/engine/actions/shared.mjs +1 -2
  5. package/engine/branch.mjs +1 -1
  6. package/engine/compositions/source/local/instruction-hint.yml +2 -3
  7. package/engine/conditions/composite.mjs +1 -1
  8. package/engine/conditions/count.mjs +1 -2
  9. package/engine/conditions/phase.mjs +1 -2
  10. package/engine/conditions/subject.mjs +1 -1
  11. package/engine/layers.mjs +3 -4
  12. package/engine/rule-spec.mjs +1 -1
  13. package/engine/shared.mjs +1 -13
  14. package/engine/subagent-tool-policy.mjs +2 -2
  15. package/engine/tool-config-engine.mjs +1 -1
  16. package/engine/trigger-spec.mjs +3 -3
  17. package/engine/trigger.mjs +2 -2
  18. package/lib/client.js +848 -217
  19. package/lib/client.js.map +1 -1
  20. package/lib/index.d.mts +1380 -1
  21. package/lib/index.mjs +33 -3
  22. package/lib/migrate-rules.mjs +572 -1
  23. package/lib/{module-package-CHPFweqK.mjs → module-package-DqJZaE71.mjs} +1 -1
  24. package/lib/{module-package-Cnd1_mwx.mjs → module-package-q2gDRu9H.mjs} +19 -22
  25. package/lib/{rules-migration-DJ6vASB5.mjs → rule-builder-DMrJten6.mjs} +28 -595
  26. package/package.json +4 -2
  27. package/skills/dsh-module/SKILL.md +199 -0
  28. package/skills/dsh-module/reference.md +158 -0
  29. package/templates/61-deny-tools-presentation.yml +27 -0
  30. package/templates/62-guard-deny-tools.yml +20 -0
  31. package/templates/63-append-context.yml +22 -0
  32. package/templates/64-pre-step-filter.yml +23 -0
  33. package/templates/67-inbox-prepend.yml +22 -0
  34. package/templates/80-tool-surface.yml +6 -6
  35. package/engine/compositions/library/agent-instructions.yml +0 -9
  36. package/engine/compositions/library/command-goal.yml +0 -7
  37. package/engine/compositions/library/compaction.yml +0 -22
  38. package/engine/compositions/library/delegation-ptc.yml +0 -58
  39. package/engine/compositions/library/delegation.yml +0 -56
  40. package/engine/compositions/library/persistent-shell.yml +0 -50
  41. package/engine/compositions/library/planning.yml +0 -26
  42. package/engine/compositions/library/skill-filesystem-cordis.yml +0 -10
  43. package/engine/compositions/library/skill-filesystem.yml +0 -7
  44. package/engine/compositions/library/tool-ask-user.yml +0 -7
  45. package/engine/compositions/library/tool-bash.yml +0 -8
  46. package/engine/compositions/library/tool-cordis.yml +0 -7
  47. package/engine/compositions/library/tool-fs-search.yml +0 -9
  48. package/engine/compositions/library/tool-fs.yml +0 -7
  49. package/engine/compositions/library/tool-goal.yml +0 -7
  50. package/engine/compositions/library/tool-jobs.yml +0 -7
  51. package/engine/compositions/library/tool-plugin-manager-disabled.yml +0 -8
  52. package/engine/compositions/library/tool-plugin-manager.yml +0 -8
  53. package/engine/compositions/library/tool-present.yml +0 -7
  54. package/engine/compositions/library/tool-presentation.yml +0 -9
  55. package/engine/compositions/library/tool-pwsh.yml +0 -8
  56. package/engine/compositions/library/tool-skill.yml +0 -7
  57. package/engine/compositions/library/tool-todo.yml +0 -9
  58. package/engine/compositions/library/tool-web.yml +0 -10
  59. package/engine/compositions/source/local/declared-triggers.yml +0 -12
  60. package/engine/trigger-editor-meta.mjs +0 -29
  61. package/lib/paths-CyP0IkaM.d.mts +0 -1390
  62. package/lib/preset-transfer.d.mts +0 -2
  63. package/lib/preset-transfer.mjs +0 -3
package/lib/client.js CHANGED
@@ -1240,7 +1240,7 @@ window.__ModuleLoader__.load({
1240
1240
  ...typeof config.sequence === "number" ? { sequence: config.sequence } : {}
1241
1241
  };
1242
1242
  }
1243
- const asRecord$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
1243
+ const asRecord$2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
1244
1244
  const readString = (source, key) => {
1245
1245
  const value = source[key];
1246
1246
  return typeof value === "string" ? value : void 0;
@@ -1312,8 +1312,8 @@ window.__ModuleLoader__.load({
1312
1312
  }
1313
1313
  function fieldsFromView(res) {
1314
1314
  const ns = res.ok ? res.value : void 0;
1315
- const value = asRecord$1(ns?.value);
1316
- const base = asRecord$1(ns?.base);
1315
+ const value = asRecord$2(ns?.value);
1316
+ const base = asRecord$2(ns?.base);
1317
1317
  return {
1318
1318
  ...EMPTY_FIELDS,
1319
1319
  promptText: readString(value, "promptText") ?? readString(base, "promptText") ?? "",
@@ -3104,6 +3104,23 @@ window.__ModuleLoader__.load({
3104
3104
  stroke: "currentColor"
3105
3105
  })
3106
3106
  });
3107
+ /** 关闭/移除:官方 IconCloseFill 的复制件(同一 artwork,14px,1px 描边)。 */
3108
+ const IconCloseFillRegular = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
3109
+ width: "14",
3110
+ height: "14",
3111
+ viewBox: "0 0 16 16",
3112
+ fill: "none",
3113
+ strokeWidth: 1,
3114
+ "aria-hidden": "true",
3115
+ ...props,
3116
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
3117
+ d: "M3.5 3.5L12.5 12.5",
3118
+ stroke: "currentColor"
3119
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
3120
+ d: "M12.5 3.5L3.5 12.5",
3121
+ stroke: "currentColor"
3122
+ })]
3123
+ });
3107
3124
  const IconSearchOutlineRegular = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3108
3125
  width: "16",
3109
3126
  height: "16",
@@ -6947,7 +6964,7 @@ window.__ModuleLoader__.load({
6947
6964
  className: controls_module_css_default.tagChipRemove,
6948
6965
  "aria-label": `移除 ${tag}`,
6949
6966
  onClick: () => remove(tag),
6950
- children: "×"
6967
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCloseFillRegular, {})
6951
6968
  })]
6952
6969
  }, tag)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6953
6970
  id,
@@ -9430,11 +9447,8 @@ window.__ModuleLoader__.load({
9430
9447
  }
9431
9448
  //#endregion
9432
9449
  //#region \0dsh-css:D:\AI\GitHub\dsh-plugin-prompt-tool\src\client\features\prompts\rules.module.css.mjs
9433
- sheets.push(".pt_nDoCUG_workspace{min-width:0;color:var(--dsw-alias-label-primary);font-feature-settings:\"cv01\", \"ss03\";flex-direction:column;gap:12px;font:12px/18px system-ui,sans-serif;display:flex}.pt_nDoCUG_row,.pt_nDoCUG_actionHead{flex-wrap:wrap;align-items:center;gap:8px 12px;min-width:0;display:flex}.pt_nDoCUG_cardPanel{min-width:0}.pt_nDoCUG_headerActions{flex:none;align-items:center;gap:6px;max-width:100%;display:inline-flex}.pt_nDoCUG_headerActions .pt_nDoCUG_row{flex-wrap:nowrap;gap:4px}.pt_nDoCUG_body{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:16px;min-width:0;padding:16px;display:flex;container:pt_nDoCUG_rule-editor/inline-size}.pt_nDoCUG_fields{flex-wrap:wrap;align-items:flex-start;gap:8px 12px;min-width:0;display:flex}.pt_nDoCUG_fields>*{flex:0 auto;min-width:0;max-width:100%}.pt_nDoCUG_fields>.pt_nDoCUG_full,.pt_nDoCUG_fields>.pt_nDoCUG_parameterGroup{flex-basis:100%}.pt_nDoCUG_fields>.pt_nDoCUG_exclusiveGroup{align-items:flex-start;gap:12px;display:inline-flex}.pt_nDoCUG_exclusiveGroup>:first-child{min-width:0}.pt_nDoCUG_full,.pt_nDoCUG_parameterGroup{width:100%;min-width:0}.pt_nDoCUG_parameterGroup{flex-direction:column;gap:8px;display:flex}.pt_nDoCUG_parameterGroup h5,.pt_nDoCUG_actionHead h4,.pt_nDoCUG_section h3{color:var(--dsw-alias-label-primary);margin:0;font:500 13px/20px system-ui,sans-serif}.pt_nDoCUG_control{min-width:0;max-width:100%}.pt_nDoCUG_body textarea{resize:vertical;width:100%;min-width:0;font:12px/18px system-ui,sans-serif}.pt_nDoCUG_body .pt_nDoCUG_listControl{width:24ch;max-width:100%;min-height:46px}.pt_nDoCUG_section{flex-direction:column;gap:12px;min-width:0;display:flex}.pt_nDoCUG_section+.pt_nDoCUG_section{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:16px}.pt_nDoCUG_toggle{flex-direction:column;align-items:flex-start;gap:4px;min-width:0;font:12px/18px system-ui,sans-serif;display:flex}.pt_nDoCUG_switchLine{flex:none;align-items:center;height:28px;min-height:28px;display:inline-flex}.pt_nDoCUG_actions,.pt_nDoCUG_condition,.pt_nDoCUG_conditions{flex-direction:column;gap:8px;min-width:0;display:flex}.pt_nDoCUG_conditionHead{flex-wrap:wrap;align-items:flex-end;gap:8px;min-width:0;display:flex}.pt_nDoCUG_conditionHead>div{min-width:0;max-width:100%}.pt_nDoCUG_conditionOrdinal{height:28px;color:var(--dsw-alias-label-secondary);align-items:center;display:inline-flex}.pt_nDoCUG_conditions>button,.pt_nDoCUG_condition>button{align-self:flex-start}.pt_nDoCUG_action{border-bottom:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;min-width:0;padding:12px 0;display:flex}.pt_nDoCUG_conditionChild{border-inline-start:2px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;min-width:0;padding-inline-start:12px;display:flex}.pt_nDoCUG_status{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;margin:0;font:12px/18px system-ui,sans-serif}.pt_nDoCUG_error{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere}.pt_nDoCUG_body [hidden]{display:none!important}@container pt_nDoCUG_rule-editor (width<=480px){.pt_nDoCUG_conditionChild{padding-inline-start:8px}.pt_nDoCUG_actionHead{gap:8px}}@media (pointer:coarse){.pt_nDoCUG_switchLine,.pt_nDoCUG_conditionOrdinal{height:44px;min-height:44px}}");
9450
+ sheets.push(".pt_nDoCUG_workspace{min-width:0;color:var(--dsw-alias-label-primary);font-feature-settings:\"cv01\", \"ss03\";flex-direction:column;gap:12px;font:12px/18px system-ui,sans-serif;display:flex}.pt_nDoCUG_row{flex-wrap:wrap;align-items:center;gap:8px 12px;min-width:0;display:flex}.pt_nDoCUG_cardPanel{min-width:0}.pt_nDoCUG_headerActions{flex:none;align-items:center;gap:6px;max-width:100%;display:inline-flex}.pt_nDoCUG_headerActions .pt_nDoCUG_row{flex-wrap:nowrap;gap:4px}.pt_nDoCUG_body{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:16px;min-width:0;padding:16px;display:flex;container:pt_nDoCUG_rule-editor/inline-size}.pt_nDoCUG_fields{flex-wrap:wrap;align-items:flex-start;gap:8px 12px;min-width:0;display:flex}.pt_nDoCUG_fields>*{flex:0 auto;min-width:0;max-width:100%}.pt_nDoCUG_fields>.pt_nDoCUG_full,.pt_nDoCUG_fields>.pt_nDoCUG_parameterGroup{flex-basis:100%}.pt_nDoCUG_fields>.pt_nDoCUG_exclusiveGroup{align-items:flex-start;gap:12px;display:inline-flex}.pt_nDoCUG_exclusiveGroup>:first-child{min-width:0}.pt_nDoCUG_full,.pt_nDoCUG_parameterGroup{width:100%;min-width:0}.pt_nDoCUG_parameterGroup{flex-direction:column;gap:8px;display:flex}.pt_nDoCUG_parameterGroup h5,.pt_nDoCUG_section h3{color:var(--dsw-alias-label-primary);margin:0;font:500 13px/20px system-ui,sans-serif}.pt_nDoCUG_control{min-width:0;max-width:100%}.pt_nDoCUG_body textarea{resize:vertical;width:100%;min-width:0;font:12px/18px system-ui,sans-serif}.pt_nDoCUG_body .pt_nDoCUG_listControl{width:24ch;max-width:100%;min-height:46px}.pt_nDoCUG_section{flex-direction:column;gap:12px;min-width:0;display:flex}.pt_nDoCUG_section+.pt_nDoCUG_section{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:16px}.pt_nDoCUG_toggle{flex-direction:column;align-items:flex-start;gap:4px;min-width:0;font:12px/18px system-ui,sans-serif;display:flex}.pt_nDoCUG_switchLine{flex:none;align-items:center;height:28px;min-height:28px;display:inline-flex}.pt_nDoCUG_condition,.pt_nDoCUG_conditions{flex-direction:column;gap:8px;min-width:0;display:flex}.pt_nDoCUG_conditionHead{flex-wrap:wrap;align-items:flex-end;gap:8px;min-width:0;display:flex}.pt_nDoCUG_conditionHead>div{min-width:0;max-width:100%}.pt_nDoCUG_conditionOrdinal{height:28px;color:var(--dsw-alias-label-secondary);align-items:center;display:inline-flex}.pt_nDoCUG_conditions>button,.pt_nDoCUG_condition>button{align-self:flex-start}.pt_nDoCUG_conditionChild{border-inline-start:2px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;min-width:0;padding-inline-start:12px;display:flex}.pt_nDoCUG_status{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;margin:0;font:12px/18px system-ui,sans-serif}.pt_nDoCUG_error{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere}.pt_nDoCUG_body [hidden]{display:none!important}.pt_nDoCUG_steps{flex-direction:column;gap:8px;min-width:0;display:flex}.pt_nDoCUG_stepCard{border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;flex-direction:column;min-width:0;display:flex}.pt_nDoCUG_stepCard:hover,.pt_nDoCUG_stepCardOpen{border-color:var(--dsw-alias-border-l3)}.pt_nDoCUG_stepHead{align-items:center;gap:6px;min-width:0;padding:2px 6px 2px 8px;display:flex}.pt_nDoCUG_stepToggle{appearance:none;min-width:0;min-height:28px;color:inherit;font:inherit;text-align:start;cursor:pointer;background:0 0;border:0;border-radius:6px;flex:auto;align-items:center;gap:8px;padding:2px 0;display:flex}.pt_nDoCUG_stepToggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.pt_nDoCUG_stepKind{border:.5px solid var(--dsw-alias-border-l2);corner-shape:round;color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:999px;flex:none;padding:1px 8px;font:11px/16px system-ui,sans-serif}.pt_nDoCUG_stepSummary{min-width:0;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:auto;font:12px/18px system-ui,sans-serif;overflow:hidden}.pt_nDoCUG_stepActions{flex:none;align-items:center;gap:4px;display:inline-flex}.pt_nDoCUG_stepClose{color:var(--dsw-alias-label-tertiary)}.pt_nDoCUG_stepClose:disabled{cursor:default;opacity:.4}.pt_nDoCUG_stepBody{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;min-width:0;padding:8px 8px 10px 12px;display:flex}.pt_nDoCUG_stepScope{border-inline-start:2px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;min-width:0;padding-inline-start:10px;display:flex}.pt_nDoCUG_scopeHead{align-items:center;gap:8px;min-width:0;display:flex}.pt_nDoCUG_stepSlot{color:var(--dsw-alias-label-secondary);margin:0;font:11px/16px system-ui,sans-serif}.pt_nDoCUG_stepAdd{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;display:flex}.pt_nDoCUG_stepHint{color:var(--dsw-alias-label-tertiary);font:12px/18px system-ui,sans-serif}@container pt_nDoCUG_rule-editor (width<=480px){.pt_nDoCUG_conditionChild,.pt_nDoCUG_stepBody{padding-inline-start:8px}.pt_nDoCUG_stepScope{padding-inline-start:6px}}@media (pointer:coarse){.pt_nDoCUG_switchLine,.pt_nDoCUG_conditionOrdinal,.pt_nDoCUG_stepToggle{height:44px;min-height:44px}}");
9434
9451
  var rules_module_css_default = {
9435
- "action": "pt_nDoCUG_action",
9436
- "actionHead": "pt_nDoCUG_actionHead",
9437
- "actions": "pt_nDoCUG_actions",
9438
9452
  "body": "pt_nDoCUG_body",
9439
9453
  "cardPanel": "pt_nDoCUG_cardPanel",
9440
9454
  "condition": "pt_nDoCUG_condition",
@@ -9452,8 +9466,23 @@ window.__ModuleLoader__.load({
9452
9466
  "parameterGroup": "pt_nDoCUG_parameterGroup",
9453
9467
  "row": "pt_nDoCUG_row",
9454
9468
  "rule-editor": "pt_nDoCUG_rule-editor",
9469
+ "scopeHead": "pt_nDoCUG_scopeHead",
9455
9470
  "section": "pt_nDoCUG_section",
9456
9471
  "status": "pt_nDoCUG_status",
9472
+ "stepActions": "pt_nDoCUG_stepActions",
9473
+ "stepAdd": "pt_nDoCUG_stepAdd",
9474
+ "stepBody": "pt_nDoCUG_stepBody",
9475
+ "stepCard": "pt_nDoCUG_stepCard",
9476
+ "stepCardOpen": "pt_nDoCUG_stepCardOpen",
9477
+ "stepClose": "pt_nDoCUG_stepClose",
9478
+ "stepHead": "pt_nDoCUG_stepHead",
9479
+ "stepHint": "pt_nDoCUG_stepHint",
9480
+ "stepKind": "pt_nDoCUG_stepKind",
9481
+ "stepScope": "pt_nDoCUG_stepScope",
9482
+ "stepSlot": "pt_nDoCUG_stepSlot",
9483
+ "stepSummary": "pt_nDoCUG_stepSummary",
9484
+ "stepToggle": "pt_nDoCUG_stepToggle",
9485
+ "steps": "pt_nDoCUG_steps",
9457
9486
  "switchLine": "pt_nDoCUG_switchLine",
9458
9487
  "toggle": "pt_nDoCUG_toggle",
9459
9488
  "workspace": "pt_nDoCUG_workspace"
@@ -9475,6 +9504,7 @@ window.__ModuleLoader__.load({
9475
9504
  fill: "form.fill.label",
9476
9505
  order: "form.order.label"
9477
9506
  };
9507
+ /** 清掉某个字段前缀下的原始草稿(含嵌套键)。 */
9478
9508
  const cleared = (fields, prefix) => {
9479
9509
  for (const key of fields.keys()) if (key === prefix || key.startsWith(prefix + ":")) fields.delete(key);
9480
9510
  };
@@ -9789,172 +9819,717 @@ window.__ModuleLoader__.load({
9789
9819
  ]
9790
9820
  });
9791
9821
  }
9792
- function RuleActionsFields(props) {
9793
- const { t, meta } = props;
9794
- const [addKind, setAddKind] = (0, react.useState)("inject-text");
9795
- const selectable = meta.actions.filter((action) => !props.conditional || action.supportsWhen);
9796
- const selected = selectable.find((action) => action.kind === addKind) ?? selectable[0];
9797
- const swap = (index, offset) => {
9798
- const next = [...props.value];
9799
- [next[index], next[index + offset]] = [next[index + offset], next[index]];
9800
- props.onChange(next);
9801
- };
9822
+ //#endregion
9823
+ //#region src/client/features/prompts/rule-steps.ts
9824
+ const asRecord$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
9825
+ /** 动作节点的判据只有 `kind`;分支与无法识别的节点都没有它。 */
9826
+ const isActionNode = (node) => {
9827
+ const kind = asRecord$1(node).kind;
9828
+ return typeof kind === "string" && kind.length > 0;
9829
+ };
9830
+ const isBranchNode = (node) => !isActionNode(node) && Array.isArray(asRecord$1(node).then);
9831
+ const nodeList = (value) => Array.isArray(value) ? value : [];
9832
+ /** 摘要里的枚举上限:超过就退化成「前两项 等 N 项」,卡头永远单行。 */
9833
+ const SUMMARY_LIMIT = 2;
9834
+ /** 值以内联标签显示的技术键;缺省时按原值显示。 */
9835
+ const VALUE_LABEL_KEYS = {
9836
+ subject: SUBJECT_LABEL_KEYS,
9837
+ audience: AUDIENCE_LABEL_KEYS,
9838
+ modelScope: MODEL_SCOPE_LABEL_KEYS,
9839
+ layer: LAYER_LABEL_KEYS,
9840
+ strategy: STRATEGY_LABEL_KEYS,
9841
+ position: POSITION_LABEL_KEYS,
9842
+ promotion: PROMOTION_LABEL_KEYS
9843
+ };
9844
+ function clipped(t, items, separator) {
9845
+ if (items.length <= SUMMARY_LIMIT) return items.join(separator);
9846
+ return t("rules.steps.more", {
9847
+ head: items.slice(0, SUMMARY_LIMIT).join(separator),
9848
+ count: items.length
9849
+ });
9850
+ }
9851
+ function valueText(t, key, value) {
9852
+ if (typeof value === "boolean") return value ? t("rules.steps.yes") : t("rules.steps.no");
9853
+ if (typeof value === "number") return String(value);
9854
+ if (typeof value === "string") {
9855
+ const keys = VALUE_LABEL_KEYS[key];
9856
+ return keys === void 0 ? value : translateLabel(t, keys, value);
9857
+ }
9858
+ if (Array.isArray(value)) return clipped(t, value.map((item) => valueText(t, key, item)), t("rules.steps.listSeparator"));
9859
+ const entries = Object.entries(asRecord$1(value));
9860
+ if (entries.length === 0) return "";
9861
+ return clipped(t, entries.map(([field, item]) => `${triggerLabel(t, field)}=${valueText(t, field, item)}`), t("rules.steps.listSeparator"));
9862
+ }
9863
+ /** 单个条件(含组合递归)的摘要;畸形条件退化为项数,不猜结构。 */
9864
+ function conditionSummary(t, condition) {
9865
+ if (condition === void 0) return t("triggers.unconditional");
9866
+ const record = asRecord$1(condition);
9867
+ const kinds = Object.keys(record);
9868
+ if (kinds.length !== 1) return t("rules.steps.complex", { count: kinds.length });
9869
+ const kind = kinds[0];
9870
+ const value = record[kind];
9871
+ if (kind === "not") return t("rules.steps.not", { value: conditionSummary(t, asRecord$1(value)) });
9872
+ if (kind === "all" || kind === "any" || kind === "notAny") {
9873
+ const children = nodeList(value).map((child) => conditionSummary(t, asRecord$1(child)));
9874
+ if (children.length === 0) return triggerLabel(t, kind);
9875
+ if (kind === "all") return clipped(t, children, ` ${t("rules.steps.and")} `);
9876
+ const joined = clipped(t, children, ` ${t("rules.steps.or")} `);
9877
+ return kind === "any" ? joined : t("rules.steps.not", { value: joined });
9878
+ }
9879
+ const detail = valueText(t, kind, value);
9880
+ return detail.length === 0 ? triggerLabel(t, kind) : t("rules.steps.condition", {
9881
+ kind: triggerLabel(t, kind),
9882
+ detail
9883
+ });
9884
+ }
9885
+ /** 注入正文的首行摘要:去掉 Markdown 标题记号与空行,超长截断。 */
9886
+ function textLead(text) {
9887
+ for (const raw of text.split("\n")) {
9888
+ const line = raw.replace(/^[#>\s*-]+/, "").trim();
9889
+ if (line.length > 0) return line.length > 28 ? `${line.slice(0, 28)}…` : line;
9890
+ }
9891
+ return "";
9892
+ }
9893
+ /** 动作摘要:类型 + 执行层 + 策略 + 正文首行(或 request-params 的覆盖项)。 */
9894
+ function actionSummary(t, action) {
9895
+ const config = asRecord$1(action.config);
9896
+ const parts = [triggerLabel(t, String(action.kind))];
9897
+ if (typeof config.layer === "string" && config.layer.length > 0) parts.push(translateLabel(t, LAYER_LABEL_KEYS, config.layer));
9898
+ if (typeof config.strategy === "string" && config.strategy.length > 0) parts.push(translateLabel(t, STRATEGY_LABEL_KEYS, config.strategy));
9899
+ const lead = typeof config.text === "string" ? textLead(config.text) : "";
9900
+ if (lead.length > 0) parts.push(lead);
9901
+ else {
9902
+ const entries = Object.entries(asRecord$1(config.patch)).filter(([, value]) => value !== void 0 && value !== "");
9903
+ if (entries.length > 0) parts.push(clipped(t, entries.map(([key, value]) => `${key}=${valueText(t, key, value)}`), t("rules.steps.listSeparator")));
9904
+ }
9905
+ return parts.join(" · ");
9906
+ }
9907
+ /** 分支卡头:`当 ⟨条件⟩ → ⟨动作⟩`;单个动作时直接写该动作,多动作写条数。 */
9908
+ function branchSummary(t, branch) {
9909
+ const then = nodeList(branch.then);
9910
+ const otherwise = nodeList(branch.else);
9911
+ const lead = then.length === 1 && otherwise.length === 0 && isActionNode(then[0]) ? actionSummary(t, then[0]) : t("rules.steps.actionCount", { count: then.length });
9912
+ return t("rules.steps.branch", {
9913
+ condition: conditionSummary(t, branch.if),
9914
+ actions: lead
9915
+ });
9916
+ }
9917
+ function nodeSummary(t, node) {
9918
+ if (isActionNode(node)) return actionSummary(t, node);
9919
+ if (isBranchNode(node)) return branchSummary(t, node);
9920
+ return t("rules.steps.unknown");
9921
+ }
9922
+ /** 递归统计生效动作数与分支数(卡头摘要用)。 */
9923
+ function countNodes(nodes) {
9924
+ return nodes.reduce((total, node) => {
9925
+ if (!isBranchNode(node)) return {
9926
+ actions: total.actions + 1,
9927
+ branches: total.branches
9928
+ };
9929
+ const then = countNodes(nodeList(node.then));
9930
+ const otherwise = countNodes(nodeList(node.else));
9931
+ return {
9932
+ actions: total.actions + then.actions + otherwise.actions,
9933
+ branches: total.branches + 1 + then.branches + otherwise.branches
9934
+ };
9935
+ }, {
9936
+ actions: 0,
9937
+ branches: 0
9938
+ });
9939
+ }
9940
+ /** 整条规则已用的动作 id:嵌套后新建动作必须在**全规则**范围内唯一(引擎拒绝重复 id)。 */
9941
+ function collectActionIds(nodes, into = /* @__PURE__ */ new Set()) {
9942
+ for (const node of nodes) if (isActionNode(node)) into.add(node.id);
9943
+ else if (isBranchNode(node)) {
9944
+ collectActionIds(nodeList(node.then), into);
9945
+ collectActionIds(nodeList(node.else), into);
9946
+ }
9947
+ return into;
9948
+ }
9949
+ /** 叶子节点数(含组合条件展开)与作用域条件计数,供卡头摘要。 */
9950
+ function countConditions(condition) {
9951
+ if (condition === void 0) return 0;
9952
+ const record = asRecord$1(condition);
9953
+ const kind = Object.keys(record)[0];
9954
+ if (kind === "all" || kind === "any" || kind === "notAny") return nodeList(record[kind]).reduce((total, child) => total + countConditions(asRecord$1(child)), 0);
9955
+ if (kind === "not") return countConditions(asRecord$1(record.not));
9956
+ return 1;
9957
+ }
9958
+ /** 路径上带条件的叶子动作:这些动作必须是 event 生命周期,否则引擎拒绝(rule-spec.mjs:143)。 */
9959
+ function conditionalActions(nodes, inherited = false, into = []) {
9960
+ for (const node of nodes) {
9961
+ if (isActionNode(node)) {
9962
+ if (inherited) into.push(node);
9963
+ continue;
9964
+ }
9965
+ if (isBranchNode(node)) {
9966
+ const inner = inherited || node.if !== void 0;
9967
+ conditionalActions(nodeList(node.then), inner, into);
9968
+ conditionalActions(nodeList(node.else), inner, into);
9969
+ }
9970
+ }
9971
+ return into;
9972
+ }
9973
+ /**
9974
+ * JSON 面板的结构门槛:动作要有非空 `id` + `kind`,分支要有 `then` 数组(可递归嵌套)。
9975
+ * 分支节点没有 `kind`,不能再用「每个 then 项都必须是动作」判定,否则合法规则被报成非法 JSON。
9976
+ */
9977
+ function isValidNode(node) {
9978
+ if (isBranchNode(node)) {
9979
+ const record = asRecord$1(node);
9980
+ return nodeList(record.then).every(isValidNode) && nodeList(record.else).every(isValidNode);
9981
+ }
9982
+ const record = asRecord$1(node);
9983
+ return isActionNode(node) && typeof record.id === "string" && record.id.length > 0;
9984
+ }
9985
+ /** 生效条件链:从根到该节点的所有条件用「且」连接,放进卡头提示逐字可读。 */
9986
+ function conditionChain(t, chain) {
9987
+ return t("rules.steps.chain", { chain: chain.map((item) => conditionSummary(t, item)).join(` ${t("rules.steps.and")} `) });
9988
+ }
9989
+ //#endregion
9990
+ //#region src/client/features/prompts/RuleSteps.tsx
9991
+ /**
9992
+ * 规则卡内部的条件卡视图(「一个条件一张卡,对应它的动作」)。
9993
+ *
9994
+ * 结构对应引擎真值(engine/branch.mjs#expandActions):
9995
+ * IF 卡 = 规则级条件,卡内是它的直属动作;
9996
+ * 分支卡 = `then` 里的分支节点 `{if, then, else}`,卡头即「当 ⟨条件⟩ → ⟨动作⟩」;
9997
+ * 动作卡 = 叶子动作,可展开改字段;
9998
+ * 否则卡 = 规则级 `else`。
9999
+ * 每张卡都是 TagInput 式的小卡:卡头单行摘要,展开才编辑,右侧 ↑↓ 与 × 。
10000
+ */
10001
+ /** 注册制层没有逐轮求值时机,引擎拒绝带条件的动作(engine/rule-spec.mjs:79,143)。 */
10002
+ const REGISTRATION_LAYERS = ["system-section", "runtime-context"];
10003
+ /** 关闭按钮:与 TagInput 的 chip 删除同形态,图标是官方 IconClose 的复制件。 */
10004
+ function CloseButton(props) {
10005
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HintTooltip, {
10006
+ label: props.label,
10007
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10008
+ type: "button",
10009
+ className: clsx(controls_module_css_default.tagChipRemove, rules_module_css_default.stepClose),
10010
+ "aria-label": props.label,
10011
+ disabled: props.disabled,
10012
+ onClick: props.onClick,
10013
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCloseFillRegular, {})
10014
+ })
10015
+ });
10016
+ }
10017
+ /** 一张小卡:卡头是一整行摘要(点开才编辑),右侧是这张卡自己的操作。 */
10018
+ function StepCard(props) {
10019
+ const panelId = (0, react.useId)();
10020
+ const toggleRef = (0, react.useRef)(null);
10021
+ const bodyRef = (0, react.useRef)(null);
10022
+ const toggle = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
10023
+ ref: toggleRef,
10024
+ type: "button",
10025
+ className: rules_module_css_default.stepToggle,
10026
+ "aria-expanded": props.open,
10027
+ "aria-controls": panelId,
10028
+ onClick: () => {
10029
+ if (props.open && bodyRef.current !== null && bodyRef.current.contains(document.activeElement)) toggleRef.current?.focus();
10030
+ props.onToggle();
10031
+ },
10032
+ children: [
10033
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutlineRegular, { className: clsx(controls_module_css_default.chevron, props.open && controls_module_css_default.chevronOpen) }),
10034
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10035
+ className: rules_module_css_default.stepKind,
10036
+ children: props.kindLabel
10037
+ }),
10038
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10039
+ className: rules_module_css_default.stepSummary,
10040
+ children: props.summary
10041
+ })
10042
+ ]
10043
+ });
10044
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
10045
+ className: clsx(rules_module_css_default.stepCard, props.open && rules_module_css_default.stepCardOpen),
10046
+ "data-step": props.kind,
10047
+ "data-open": props.open || void 0,
10048
+ "data-level": props.level,
10049
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10050
+ className: rules_module_css_default.stepHead,
10051
+ children: [props.hint === void 0 ? toggle : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HintTooltip, {
10052
+ label: props.hint,
10053
+ children: toggle
10054
+ }), props.actions !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10055
+ className: rules_module_css_default.stepActions,
10056
+ children: props.actions
10057
+ })]
10058
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10059
+ id: panelId,
10060
+ ref: bodyRef,
10061
+ hidden: !props.open,
10062
+ className: rules_module_css_default.stepBody,
10063
+ children: props.open && props.children
10064
+ })]
10065
+ });
10066
+ }
10067
+ /** 作用域头:只声明「下面这批动作属于哪个条件」,本身没有可编辑内容。 */
10068
+ function ScopeHead(props) {
9802
10069
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9803
- className: rules_module_css_default.actions,
9804
- children: [props.value.map((action, index) => {
9805
- const entry = meta.actions.find((item) => item.kind === action.kind), fieldKey = props.fieldKey + ":" + action.id;
9806
- const config = asTriggerRecord(action.config), params = asTriggerRecord(config.params);
9807
- const layer = String(config.layer ?? "pre-step"), policy = props.engineMeta.layerFieldPolicies[layer];
9808
- const staticAudience = layer === "system-section" && (params.complete === true || params.suppressRuntimeContext === true);
9809
- const example = action.kind === "request-params" ? {
9810
- ...entry?.example,
9811
- patch: {
9812
- provider: "",
9813
- model: "",
9814
- reasoningEffort: "",
9815
- temperature: void 0,
9816
- maxTokens: void 0
9817
- }
9818
- } : action.kind === "inject-text" ? {
9819
- ...entry?.example,
9820
- config: {
9821
- strategy: "static",
9822
- configKind: "ordered",
9823
- ...policy?.merge ? { mergeMode: "separate" } : {},
9824
- ...policy?.position ? { position: "after-user" } : {},
9825
- ...staticAudience ? { audience: "" } : {},
9826
- ...asTriggerRecord(entry?.example.config)
10070
+ className: rules_module_css_default.scopeHead,
10071
+ children: [
10072
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10073
+ className: rules_module_css_default.stepKind,
10074
+ children: props.kindLabel
10075
+ }),
10076
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10077
+ className: rules_module_css_default.stepSummary,
10078
+ children: props.summary
10079
+ }),
10080
+ props.onRemove !== void 0 && props.removeLabel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10081
+ className: rules_module_css_default.stepActions,
10082
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseButton, {
10083
+ label: props.removeLabel,
10084
+ disabled: props.disabled,
10085
+ onClick: props.onRemove
10086
+ })
10087
+ })
10088
+ ]
10089
+ });
10090
+ }
10091
+ /** 移动 / 删除两个按钮:动作卡与分支卡共用。 */
10092
+ function NodeActions(props) {
10093
+ const { t } = props;
10094
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
10095
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HintTooltip, {
10096
+ label: t("rules.moveUp", { id: props.id }),
10097
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
10098
+ shape: "pill",
10099
+ variant: "outline",
10100
+ icon: true,
10101
+ "aria-label": t("rules.moveUp", { id: props.id }),
10102
+ disabled: props.disabled || props.index === 0,
10103
+ onClick: () => props.onMove(-1),
10104
+ children: "↑"
10105
+ })
10106
+ }),
10107
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HintTooltip, {
10108
+ label: t("rules.moveDown", { id: props.id }),
10109
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
10110
+ shape: "pill",
10111
+ variant: "outline",
10112
+ icon: true,
10113
+ "aria-label": t("rules.moveDown", { id: props.id }),
10114
+ disabled: props.disabled || props.index === props.total - 1,
10115
+ onClick: () => props.onMove(1),
10116
+ children: "↓"
10117
+ })
10118
+ }),
10119
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseButton, {
10120
+ label: t("rules.steps.remove", { id: props.id }),
10121
+ disabled: props.disabled,
10122
+ onClick: props.onRemove
10123
+ })
10124
+ ] });
10125
+ }
10126
+ const rowKey = (context, path) => `${context.prefix}:${path}`;
10127
+ const rowOpen = (context, path) => context.expanded.get(rowKey(context, path)) === true;
10128
+ function toggleRow(context, path) {
10129
+ context.expanded.set(rowKey(context, path), !rowOpen(context, path));
10130
+ context.onDraft();
10131
+ }
10132
+ /** 叶子动作卡:卡头是动作摘要,展开是该动作的全部字段。 */
10133
+ function ActionCard(props) {
10134
+ const { context, action } = props;
10135
+ const { t } = context;
10136
+ const entry = context.meta.actions.find((item) => item.kind === action.kind);
10137
+ const fieldKey = `${props.path}:${action.id}`;
10138
+ const config = asTriggerRecord(action.config), params = asTriggerRecord(config.params);
10139
+ const layer = String(config.layer ?? "pre-step"), policy = context.engineMeta.layerFieldPolicies[layer];
10140
+ const staticAudience = layer === "system-section" && (params.complete === true || params.suppressRuntimeContext === true);
10141
+ const example = action.kind === "request-params" ? {
10142
+ ...entry?.example,
10143
+ patch: {
10144
+ provider: "",
10145
+ model: "",
10146
+ reasoningEffort: "",
10147
+ temperature: void 0,
10148
+ maxTokens: void 0
10149
+ }
10150
+ } : action.kind === "inject-text" ? {
10151
+ ...entry?.example,
10152
+ config: {
10153
+ strategy: "static",
10154
+ configKind: "ordered",
10155
+ ...policy?.merge ? { mergeMode: "separate" } : {},
10156
+ ...policy?.position ? { position: "after-user" } : {},
10157
+ ...staticAudience ? { audience: "" } : {},
10158
+ ...asTriggerRecord(entry?.example.config)
10159
+ }
10160
+ } : entry?.example;
10161
+ const chainLabel = props.chain.length > 0 ? conditionChain(t, props.chain) : void 0;
10162
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepCard, {
10163
+ kind: "action",
10164
+ kindLabel: triggerLabel(t, String(action.kind)),
10165
+ summary: actionSummary(t, action),
10166
+ hint: chainLabel,
10167
+ level: 2,
10168
+ open: rowOpen(context, props.path),
10169
+ onToggle: () => toggleRow(context, props.path),
10170
+ actions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeActions, {
10171
+ t,
10172
+ id: action.id,
10173
+ index: props.index,
10174
+ total: props.total,
10175
+ disabled: context.disabled,
10176
+ onMove: props.onMove,
10177
+ onRemove: props.onRemove
10178
+ }),
10179
+ children: entry === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10180
+ role: "note",
10181
+ children: t("rules.unknown")
10182
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriggerJsonField, {
10183
+ ...context,
10184
+ label: t("rules.rawAction"),
10185
+ shape: "object",
10186
+ value: action,
10187
+ fieldKey,
10188
+ onChange: (next) => props.onChange(asTriggerRecord(next))
10189
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10190
+ className: rules_module_css_default.row,
10191
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuSelect, {
10192
+ compact: true,
10193
+ ariaLabel: t("triggers.actionType"),
10194
+ value: action.kind,
10195
+ disabled: context.disabled,
10196
+ options: (props.scopeConditional ? context.meta.actions.filter((item) => item.supportsWhen) : context.meta.actions).map((item) => ({
10197
+ value: item.kind,
10198
+ label: triggerLabel(t, item.kind)
10199
+ })),
10200
+ onChange: (kind) => {
10201
+ const next = context.meta.actions.find((item) => item.kind === kind);
10202
+ if (next === void 0) return;
10203
+ cleared(context.fields, fieldKey);
10204
+ props.onChange({
10205
+ ...structuredClone(next.example),
10206
+ id: action.id,
10207
+ kind
10208
+ });
9827
10209
  }
9828
- } : entry?.example;
9829
- const patch = (next) => props.onChange(props.value.map((item, at) => at === index ? {
9830
- ...next,
9831
- id: action.id,
9832
- kind: String(next.kind ?? action.kind)
9833
- } : item));
9834
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
9835
- className: rules_module_css_default.action,
9836
- "data-rule-action": action.id,
9837
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9838
- className: rules_module_css_default.actionHead,
9839
- children: [
9840
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("rules.action", { index: index + 1 }) }),
9841
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuSelect, {
9842
- compact: true,
9843
- className: rules_module_css_default.control,
9844
- ariaLabel: t("triggers.actionType"),
9845
- value: action.kind,
9846
- disabled: props.disabled,
9847
- options: [.../* @__PURE__ */ new Set([...selectable.map((item) => item.kind), action.kind])].map((kind) => ({
9848
- value: kind,
9849
- label: triggerLabel(t, kind)
9850
- })),
9851
- onChange: (kind) => {
9852
- const next = meta.actions.find((item) => item.kind === kind);
9853
- if (next) {
9854
- cleared(props.fields, fieldKey);
9855
- patch({
9856
- ...structuredClone(next.example),
9857
- id: action.id,
9858
- kind
9859
- });
9860
- }
9861
- }
9862
- }),
9863
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9864
- variant: "outline",
9865
- shape: "pill",
9866
- icon: true,
9867
- "aria-label": t("rules.moveUp", { id: action.id }),
9868
- disabled: props.disabled || index === 0,
9869
- onClick: () => swap(index, -1),
9870
- children: "↑"
9871
- }),
9872
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9873
- variant: "outline",
9874
- shape: "pill",
9875
- icon: true,
9876
- "aria-label": t("rules.moveDown", { id: action.id }),
9877
- disabled: props.disabled || index === props.value.length - 1,
9878
- onClick: () => swap(index, 1),
9879
- children: "↓"
9880
- }),
9881
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9882
- variant: "outline",
9883
- shape: "pill",
9884
- "data-danger": true,
9885
- "aria-label": t("rules.removeAction", { id: action.id }),
9886
- disabled: props.disabled,
9887
- onClick: () => {
9888
- cleared(props.fields, fieldKey);
9889
- props.onChange(props.value.filter((_, at) => at !== index));
9890
- },
9891
- children: t("rules.removeActionShort")
9892
- })
9893
- ]
9894
- }), entry === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10210
+ })
10211
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10212
+ className: rules_module_css_default.fields,
10213
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuleParameterFields, {
10214
+ ...context,
10215
+ fieldKey,
10216
+ value: action,
10217
+ requestPatch: action.kind === "request-params",
10218
+ example,
10219
+ omit: [
10220
+ "id",
10221
+ "kind",
10222
+ ...action.kind === "request-params" ? ["audience", "modelScope"] : []
10223
+ ],
10224
+ configOmit: action.kind === "inject-text" ? [
10225
+ "modelScope",
10226
+ "promotion",
10227
+ "subject",
10228
+ "match",
10229
+ ...staticAudience ? [] : ["audience"]
10230
+ ] : void 0,
10231
+ onChange: (next) => props.onChange({
10232
+ ...next,
10233
+ id: action.id,
10234
+ kind: String(next.kind ?? action.kind)
10235
+ })
10236
+ })
10237
+ })] })
10238
+ });
10239
+ }
10240
+ /** 分支卡:卡头即「当 ⟨条件⟩ → ⟨动作⟩」,卡内是本层条件 + 它的 then / else 动作。 */
10241
+ function BranchCard(props) {
10242
+ const { context, branch } = props;
10243
+ const { t } = context;
10244
+ const branchChain = [...props.chain, ...branch.if === void 0 ? [] : [branch.if]];
10245
+ const elseChain = [...props.chain, ...branch.if === void 0 ? [] : [{ not: branch.if }]];
10246
+ const patch = (next) => props.onChange({
10247
+ ...branch,
10248
+ ...next
10249
+ });
10250
+ const ordinal = String(props.index + 1);
10251
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StepCard, {
10252
+ kind: "branch",
10253
+ kindLabel: t("rules.steps.branchLabel"),
10254
+ summary: branchSummary(t, branch),
10255
+ hint: conditionChain(t, branchChain),
10256
+ level: 1,
10257
+ open: rowOpen(context, props.path),
10258
+ onToggle: () => toggleRow(context, props.path),
10259
+ actions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeActions, {
10260
+ t,
10261
+ id: ordinal,
10262
+ index: props.index,
10263
+ total: props.total,
10264
+ disabled: context.disabled,
10265
+ onMove: props.onMove,
10266
+ onRemove: props.onRemove
10267
+ }),
10268
+ children: [
10269
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuleConditionFields, {
10270
+ ...context,
10271
+ fieldKey: `${props.path}:if`,
10272
+ meta: context.meta,
10273
+ value: branch.if,
10274
+ onChange: (condition) => patch({ if: condition })
10275
+ }),
10276
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10277
+ className: rules_module_css_default.stepSlot,
10278
+ children: t("rules.steps.then")
10279
+ }),
10280
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10281
+ className: rules_module_css_default.stepScope,
10282
+ "data-step-scope": "then",
10283
+ children: props.renderList(nodeList(branch.then), `${props.path}:then`, branchChain, true, (nodes) => patch({ then: nodes }))
10284
+ }),
10285
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10286
+ className: rules_module_css_default.stepSlot,
10287
+ children: t("rules.steps.else")
10288
+ }),
10289
+ branch.else === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10290
+ className: rules_module_css_default.stepAdd,
10291
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
10292
+ shape: "pill",
10293
+ variant: "outline",
10294
+ disabled: context.disabled,
10295
+ onClick: () => patch({ else: [] }),
10296
+ children: t("rules.steps.addElse")
10297
+ })
10298
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10299
+ className: rules_module_css_default.stepScope,
10300
+ "data-step-scope": "else",
10301
+ children: props.renderList(nodeList(branch.else), `${props.path}:else`, elseChain, true, (nodes) => patch({ else: nodes }))
10302
+ })
10303
+ ]
10304
+ });
10305
+ }
10306
+ /**
10307
+ * 规则卡的条件/动作编辑面:一个条件作用域一张卡,卡内是它对应的动作。
10308
+ * 添加沿用「选择后添加」的 tag 式交互;`then` 里的分支节点在这里成为一等公民。
10309
+ */
10310
+ function RuleStepsPanel(props) {
10311
+ const { t, rule, meta } = props;
10312
+ const [addActionKind, setAddActionKind] = (0, react.useState)("inject-text");
10313
+ const [addConditionKind, setAddConditionKind] = (0, react.useState)("");
10314
+ const disabled = props.disabled === true;
10315
+ const then = nodeList(rule.then), otherwise = nodeList(rule.else);
10316
+ const usedIds = collectActionIds([...then, ...otherwise]);
10317
+ const conditional = rule.if !== void 0;
10318
+ const branchAllowed = !REGISTRATION_LAYERS.includes(rule.layer ?? "pre-step");
10319
+ const context = {
10320
+ t,
10321
+ meta,
10322
+ engineMeta: props.engineMeta,
10323
+ fields: props.fields,
10324
+ expanded: props.expanded,
10325
+ prefix: props.prefix,
10326
+ disabled,
10327
+ onDraft: props.onDraft
10328
+ };
10329
+ const patch = (next) => {
10330
+ if (!disabled) props.onChange({
10331
+ ...rule,
10332
+ ...next
10333
+ });
10334
+ };
10335
+ const setNodes = (next) => patch({ then: next });
10336
+ const nextActionId = () => {
10337
+ let number = 1;
10338
+ while (usedIds.has(`action-${number}`)) number++;
10339
+ return `action-${number}`;
10340
+ };
10341
+ const actionSelectable = (scopeConditional) => scopeConditional ? meta.actions.filter((action) => action.supportsWhen) : meta.actions;
10342
+ /** 列表渲染器:动作卡与分支卡递归共用(分支内可再嵌分支)。 */
10343
+ const renderList = (nodes, path, chain, scopeConditional, onChange) => {
10344
+ const setAt = (index, next) => onChange(nodes.map((node, at) => at === index ? next : node));
10345
+ const move = (index, offset) => {
10346
+ const next = [...nodes], target = index + offset;
10347
+ if (target < 0 || target >= next.length) return;
10348
+ [next[index], next[target]] = [next[target], next[index]];
10349
+ onChange(next);
10350
+ };
10351
+ const candidates = actionSelectable(scopeConditional);
10352
+ const selected = candidates.find((action) => action.kind === addActionKind) ?? candidates[0];
10353
+ const addAction = () => {
10354
+ if (selected !== void 0) onChange([...nodes, {
10355
+ ...structuredClone(selected.example),
10356
+ id: nextActionId(),
10357
+ kind: selected.kind
10358
+ }]);
10359
+ };
10360
+ const addBranch = () => {
10361
+ const predicate = meta.predicates.find((item) => item.kind === addConditionKind) ?? meta.predicates.find((item) => !meta.composites.includes(item.kind));
10362
+ const action = actionSelectable(true).find((item) => item.kind === addActionKind) ?? actionSelectable(true)[0];
10363
+ if (predicate === void 0 || action === void 0) return;
10364
+ onChange([...nodes, {
10365
+ if: structuredClone(predicate.example),
10366
+ then: [{
10367
+ ...structuredClone(action.example),
10368
+ id: nextActionId(),
10369
+ kind: action.kind
10370
+ }]
10371
+ }]);
10372
+ };
10373
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [nodes.map((node, index) => {
10374
+ const nodePath = `${path}:${index}`;
10375
+ if (isActionNode(node)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionCard, {
10376
+ context,
10377
+ action: node,
10378
+ path: nodePath,
10379
+ chain,
10380
+ scopeConditional,
10381
+ index,
10382
+ total: nodes.length,
10383
+ onMove: (offset) => move(index, offset),
10384
+ onRemove: () => {
10385
+ cleared(props.fields, nodePath);
10386
+ onChange(nodes.filter((_, at) => at !== index));
10387
+ },
10388
+ onChange: (next) => setAt(index, next)
10389
+ }, nodePath);
10390
+ if (isBranchNode(node)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BranchCard, {
10391
+ context,
10392
+ branch: node,
10393
+ path: nodePath,
10394
+ chain,
10395
+ index,
10396
+ total: nodes.length,
10397
+ onMove: (offset) => move(index, offset),
10398
+ onRemove: () => {
10399
+ cleared(props.fields, nodePath);
10400
+ onChange(nodes.filter((_, at) => at !== index));
10401
+ },
10402
+ onChange: (next) => setAt(index, next),
10403
+ renderList
10404
+ }, nodePath);
10405
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StepCard, {
10406
+ kind: "unknown",
10407
+ kindLabel: t("rules.steps.unknown"),
10408
+ summary: nodeSummary(t, node),
10409
+ level: 1,
10410
+ open: rowOpen(context, nodePath),
10411
+ onToggle: () => toggleRow(context, nodePath),
10412
+ actions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeActions, {
10413
+ t,
10414
+ id: String(index + 1),
10415
+ index,
10416
+ total: nodes.length,
10417
+ disabled,
10418
+ onMove: (offset) => move(index, offset),
10419
+ onRemove: () => onChange(nodes.filter((_, at) => at !== index))
10420
+ }),
10421
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9895
10422
  role: "note",
9896
10423
  children: t("rules.unknown")
9897
10424
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriggerJsonField, {
9898
- ...props,
9899
- fieldKey,
10425
+ ...context,
9900
10426
  label: t("rules.rawAction"),
9901
10427
  shape: "object",
9902
- value: action,
9903
- onChange: (next) => patch(asTriggerRecord(next))
9904
- })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9905
- className: rules_module_css_default.fields,
9906
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuleParameterFields, {
9907
- ...props,
9908
- fieldKey,
9909
- value: action,
9910
- requestPatch: action.kind === "request-params",
9911
- example,
9912
- omit: [
9913
- "id",
9914
- "kind",
9915
- ...action.kind === "request-params" ? ["audience", "modelScope"] : []
9916
- ],
9917
- configOmit: action.kind === "inject-text" ? [
9918
- "modelScope",
9919
- "promotion",
9920
- "subject",
9921
- "match",
9922
- ...staticAudience ? [] : ["audience"]
9923
- ] : void 0,
9924
- onChange: patch
9925
- })
10428
+ value: node,
10429
+ fieldKey: nodePath,
10430
+ onChange: (next) => setAt(index, asTriggerRecord(next))
9926
10431
  })]
9927
- }, action.id || index);
10432
+ }, nodePath);
9928
10433
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9929
- className: rules_module_css_default.row,
9930
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuSelect, {
9931
- compact: true,
9932
- className: rules_module_css_default.control,
9933
- ariaLabel: t("rules.addAction"),
9934
- value: selected?.kind ?? "",
9935
- disabled: props.disabled || selectable.length === 0,
9936
- options: selectable.map((action) => ({
9937
- value: action.kind,
9938
- label: triggerLabel(t, action.kind)
9939
- })),
9940
- onChange: setAddKind
9941
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9942
- variant: "outline",
9943
- shape: "pill",
9944
- disabled: props.disabled || selected === void 0,
9945
- onClick: () => {
9946
- if (!selected) return;
9947
- let number = 1;
9948
- while (props.value.some((action) => action.id === "action-" + number)) number++;
9949
- props.onChange([...props.value, {
9950
- ...structuredClone(selected.example),
9951
- id: "action-" + number,
9952
- kind: selected.kind
9953
- }]);
9954
- },
9955
- children: t("rules.addAction")
9956
- })]
9957
- })]
10434
+ className: rules_module_css_default.stepAdd,
10435
+ "data-step-add": true,
10436
+ children: [
10437
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuSelect, {
10438
+ compact: true,
10439
+ ariaLabel: t("rules.steps.addConditionType"),
10440
+ value: addConditionKind,
10441
+ disabled: disabled || candidates.length === 0,
10442
+ options: [{
10443
+ value: "",
10444
+ label: t("triggers.unconditional")
10445
+ }, ...meta.predicates.map((item) => ({
10446
+ value: item.kind,
10447
+ label: triggerLabel(t, item.kind)
10448
+ }))],
10449
+ onChange: setAddConditionKind
10450
+ }),
10451
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuSelect, {
10452
+ compact: true,
10453
+ ariaLabel: t("rules.steps.addActionType"),
10454
+ value: selected?.kind ?? "",
10455
+ disabled: disabled || candidates.length === 0,
10456
+ options: candidates.map((item) => ({
10457
+ value: item.kind,
10458
+ label: triggerLabel(t, item.kind)
10459
+ })),
10460
+ onChange: setAddActionKind
10461
+ }),
10462
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
10463
+ shape: "pill",
10464
+ variant: "outline",
10465
+ disabled: disabled || !branchAllowed || addConditionKind.length === 0 || candidates.length === 0,
10466
+ onClick: addBranch,
10467
+ children: t("rules.steps.addBranch")
10468
+ }),
10469
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
10470
+ shape: "pill",
10471
+ variant: "outline",
10472
+ disabled: disabled || selected === void 0,
10473
+ onClick: addAction,
10474
+ children: t("rules.addAction")
10475
+ }),
10476
+ !branchAllowed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10477
+ className: rules_module_css_default.stepHint,
10478
+ children: t("rules.steps.branchUnsupported")
10479
+ })
10480
+ ]
10481
+ })] });
10482
+ };
10483
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10484
+ className: rules_module_css_default.steps,
10485
+ "data-rule-steps": true,
10486
+ children: [
10487
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepCard, {
10488
+ kind: "if",
10489
+ kindLabel: t("rules.steps.ifLabel"),
10490
+ summary: conditionSummary(t, rule.if),
10491
+ level: 0,
10492
+ open: rowOpen(context, `${props.fieldKey}:if`),
10493
+ onToggle: () => toggleRow(context, `${props.fieldKey}:if`),
10494
+ actions: rule.if === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseButton, {
10495
+ label: t("rules.clearCondition"),
10496
+ disabled,
10497
+ onClick: () => patch({ if: void 0 })
10498
+ }),
10499
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuleConditionFields, {
10500
+ ...context,
10501
+ fieldKey: `${props.fieldKey}:if`,
10502
+ meta,
10503
+ value: rule.if,
10504
+ onChange: (condition) => patch({ if: condition })
10505
+ })
10506
+ }),
10507
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10508
+ className: rules_module_css_default.stepScope,
10509
+ "data-step-scope": "then",
10510
+ children: renderList(then, `${props.fieldKey}:then`, rule.if === void 0 ? [] : [rule.if], conditional, setNodes)
10511
+ }),
10512
+ rule.else === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10513
+ className: rules_module_css_default.stepAdd,
10514
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
10515
+ shape: "pill",
10516
+ variant: "outline",
10517
+ disabled,
10518
+ onClick: () => patch({ else: [] }),
10519
+ children: t("rules.steps.addElse")
10520
+ })
10521
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10522
+ className: rules_module_css_default.stepScope,
10523
+ "data-step-scope": "else",
10524
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScopeHead, {
10525
+ kindLabel: t("rules.steps.elseLabel"),
10526
+ summary: t("rules.steps.actionCount", { count: otherwise.length }),
10527
+ disabled,
10528
+ removeLabel: t("rules.steps.removeElse"),
10529
+ onRemove: () => patch({ else: void 0 })
10530
+ }), renderList(otherwise, `${props.fieldKey}:else`, rule.if === void 0 ? [] : [{ not: rule.if }], rule.if !== void 0, (nodes) => patch({ else: nodes }))]
10531
+ })
10532
+ ]
9958
10533
  });
9959
10534
  }
9960
10535
  //#endregion
@@ -9964,7 +10539,17 @@ window.__ModuleLoader__.load({
9964
10539
  const panelId = (0, react.useId)(), ownsFocus = (0, react.useRef)(false);
9965
10540
  const [confirming, setConfirming] = (0, react.useState)(false);
9966
10541
  const disabled = props.disabled === true || draft.busy === "save";
9967
- const unsupported = rule.if !== void 0 && rule.then.some((action) => draft.meta?.actions.find((item) => item.kind === action.kind)?.supportsWhen === false);
10542
+ const nodes = [...nodeList(rule.then), ...nodeList(rule.else)];
10543
+ const unsupported = conditionalActions(nodes, rule.if !== void 0).some((action) => draft.meta?.actions.find((item) => item.kind === action.kind)?.supportsWhen === false);
10544
+ const totals = countNodes(nodes);
10545
+ const summary = totals.branches === 0 ? t("rules.summary", {
10546
+ conditions: countConditions(rule.if),
10547
+ actions: totals.actions
10548
+ }) : t("rules.summary.branches", {
10549
+ conditions: countConditions(rule.if),
10550
+ actions: totals.actions,
10551
+ branches: totals.branches
10552
+ });
9968
10553
  const patch = (next) => {
9969
10554
  if (!disabled) editor.patch(entry.key, {
9970
10555
  ...entry.value,
@@ -9984,7 +10569,7 @@ window.__ModuleLoader__.load({
9984
10569
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleCard, {
9985
10570
  id: panelId,
9986
10571
  title: rule.name || rule.id,
9987
- meta: (props.source ? t("rules.source", { module: props.source }) + " · " : "") + (rule.layer === void 0 ? t("rules.module") : translateLabel(t, LAYER_LABEL_KEYS, rule.layer)) + " · " + rule.then.length + " " + t("triggers.action"),
10572
+ meta: (props.source ? t("rules.source", { module: props.source }) + " · " : "") + (rule.layer === void 0 ? t("rules.module") : translateLabel(t, LAYER_LABEL_KEYS, rule.layer)) + " · " + summary,
9988
10573
  expanded: props.expanded,
9989
10574
  onToggle: () => {
9990
10575
  if (props.expanded) save();
@@ -10100,56 +10685,45 @@ window.__ModuleLoader__.load({
10100
10685
  "aria-label": t("rules.settings"),
10101
10686
  children: props.renderSettings?.(rule)
10102
10687
  }) : void 0,
10103
- children: [
10104
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
10105
- "data-config-panel": "conditions",
10106
- "aria-label": t("form.navigation.conditions"),
10107
- className: rules_module_css_default.section,
10108
- children: draft.meta && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuleConditionFields, {
10109
- ...fieldContext,
10110
- fieldKey: entry.key + ":if",
10111
- meta: draft.meta,
10112
- value: rule.if,
10113
- onChange: (condition) => patch({ if: condition })
10114
- })
10115
- }),
10116
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
10117
- "data-config-panel": "execution",
10118
- "aria-label": t("rules.actionsTab"),
10119
- className: rules_module_css_default.section,
10120
- children: draft.meta && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuleActionsFields, {
10121
- ...fieldContext,
10122
- fieldKey: entry.key + ":then",
10123
- meta: draft.meta,
10124
- engineMeta: props.meta,
10125
- conditional: rule.if !== void 0,
10126
- value: rule.then,
10127
- onChange: (actions) => patch({ then: actions })
10128
- })
10129
- }),
10130
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
10131
- "data-config-panel": "definition",
10132
- "aria-label": t("rules.jsonTab"),
10133
- className: rules_module_css_default.section,
10134
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriggerJsonField, {
10135
- ...fieldContext,
10136
- fieldKey: entry.key + ":full",
10137
- label: t("triggers.advanced"),
10138
- shape: "object",
10139
- value: rule,
10140
- onChange: (value) => {
10141
- const candidate = asTriggerRecord(value);
10142
- if (typeof candidate.id !== "string" || !Array.isArray(candidate.then) || candidate.then.some((action) => typeof asTriggerRecord(action).id !== "string" || typeof asTriggerRecord(action).kind !== "string")) {
10143
- const raw = draft.fields.get(entry.key + ":full");
10144
- if (raw) raw.error = t("triggers.invalidJson");
10145
- return;
10146
- }
10147
- for (const key of draft.fields.keys()) if (key.startsWith(entry.key + ":") && key !== entry.key + ":full") draft.fields.delete(key);
10148
- editor.patch(entry.key, candidate);
10688
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
10689
+ "data-config-panel": "rule",
10690
+ "aria-label": t("rules.stepsTab"),
10691
+ className: rules_module_css_default.section,
10692
+ children: draft.meta && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuleStepsPanel, {
10693
+ t,
10694
+ rule,
10695
+ meta: draft.meta,
10696
+ engineMeta: props.meta,
10697
+ fields: draft.fields,
10698
+ expanded: props.expandedRows,
10699
+ prefix: props.source ?? rule.id,
10700
+ fieldKey: entry.key,
10701
+ disabled,
10702
+ onDraft: editor.changed,
10703
+ onChange: (next) => patch(next)
10704
+ })
10705
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
10706
+ "data-config-panel": "definition",
10707
+ "aria-label": t("rules.jsonTab"),
10708
+ className: rules_module_css_default.section,
10709
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriggerJsonField, {
10710
+ ...fieldContext,
10711
+ fieldKey: entry.key + ":full",
10712
+ label: t("triggers.advanced"),
10713
+ shape: "object",
10714
+ value: rule,
10715
+ onChange: (value) => {
10716
+ const candidate = asTriggerRecord(value);
10717
+ if (typeof candidate.id !== "string" || !Array.isArray(candidate.then) || !candidate.then.every(isValidNode) || candidate.else !== void 0 && !nodeList(candidate.else).every(isValidNode)) {
10718
+ const raw = draft.fields.get(entry.key + ":full");
10719
+ if (raw) raw.error = t("triggers.invalidJson");
10720
+ return;
10149
10721
  }
10150
- })
10722
+ for (const key of draft.fields.keys()) if (key.startsWith(entry.key + ":") && key !== entry.key + ":full") draft.fields.delete(key);
10723
+ editor.patch(entry.key, candidate);
10724
+ }
10151
10725
  })
10152
- ]
10726
+ })]
10153
10727
  }),
10154
10728
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10155
10729
  className: rules_module_css_default.row,
@@ -10519,6 +11093,7 @@ window.__ModuleLoader__.load({
10519
11093
  expanded: expanded === key,
10520
11094
  onToggle: () => toggle(key),
10521
11095
  onDuplicate: () => duplicate(row, entry.value),
11096
+ expandedRows: store.editorDrafts.expanded,
10522
11097
  headerActions: orderEntry && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10523
11098
  type: "button",
10524
11099
  className: controls_module_css_default.dragHandle,
@@ -16397,7 +16972,35 @@ window.__ModuleLoader__.load({
16397
16972
  "rules.rawAction": "动作 JSON",
16398
16973
  "rules.settings": "本层共享设置",
16399
16974
  "rules.expand": "展开 {name}",
16400
- "rules.collapse": "收起 {name}"
16975
+ "rules.collapse": "收起 {name}",
16976
+ "rules.stepsTab": "规则",
16977
+ "rules.summary": "{conditions} 条件 · {actions} 动作",
16978
+ "rules.summary.branches": "{conditions} 条件 · {actions} 动作 · {branches} 分支",
16979
+ "rules.steps.ifLabel": "当",
16980
+ "rules.steps.then": "则",
16981
+ "rules.steps.else": "否则",
16982
+ "rules.steps.branchLabel": "分支",
16983
+ "rules.steps.elseLabel": "否则",
16984
+ "rules.steps.addBranch": "添加条件分支",
16985
+ "rules.steps.addElse": "添加否则分支",
16986
+ "rules.steps.addConditionType": "条件类型",
16987
+ "rules.steps.addActionType": "动作类型",
16988
+ "rules.steps.remove": "删除 {id}",
16989
+ "rules.steps.removeElse": "删除否则分支",
16990
+ "rules.steps.branch": "当 {condition} → {actions}",
16991
+ "rules.steps.actionCount": "{count} 个动作",
16992
+ "rules.steps.condition": "{kind}:{detail}",
16993
+ "rules.steps.complex": "{count} 项条件",
16994
+ "rules.steps.unknown": "无法识别的节点",
16995
+ "rules.steps.not": "非({value})",
16996
+ "rules.steps.and": "且",
16997
+ "rules.steps.or": "或",
16998
+ "rules.steps.more": "{head} 等 {count} 项",
16999
+ "rules.steps.listSeparator": "、",
17000
+ "rules.steps.yes": "是",
17001
+ "rules.steps.no": "否",
17002
+ "rules.steps.chain": "生效条件:{chain}",
17003
+ "rules.steps.branchUnsupported": "该层是注册制层(装配时注册一次),没有逐轮求值时机,不能使用条件分支;请改用 pre-step、subagent、tool-pipeline 或 turn-stop 层。"
16401
17004
  };
16402
17005
  const RULES_EN = {
16403
17006
  "rules.title": "Rules",
@@ -16450,7 +17053,35 @@ window.__ModuleLoader__.load({
16450
17053
  "rules.rawAction": "Action JSON",
16451
17054
  "rules.settings": "Shared layer settings",
16452
17055
  "rules.expand": "Expand {name}",
16453
- "rules.collapse": "Collapse {name}"
17056
+ "rules.collapse": "Collapse {name}",
17057
+ "rules.stepsTab": "Rule",
17058
+ "rules.summary": "{conditions} conditions · {actions} actions",
17059
+ "rules.summary.branches": "{conditions} conditions · {actions} actions · {branches} branches",
17060
+ "rules.steps.ifLabel": "If",
17061
+ "rules.steps.then": "Then",
17062
+ "rules.steps.else": "Else",
17063
+ "rules.steps.branchLabel": "Branch",
17064
+ "rules.steps.elseLabel": "Else",
17065
+ "rules.steps.addBranch": "Add conditional branch",
17066
+ "rules.steps.addElse": "Add else branch",
17067
+ "rules.steps.addConditionType": "Condition type",
17068
+ "rules.steps.addActionType": "Action type",
17069
+ "rules.steps.remove": "Remove {id}",
17070
+ "rules.steps.removeElse": "Remove else branch",
17071
+ "rules.steps.branch": "If {condition} → {actions}",
17072
+ "rules.steps.actionCount": "{count} actions",
17073
+ "rules.steps.condition": "{kind}: {detail}",
17074
+ "rules.steps.complex": "{count} conditions",
17075
+ "rules.steps.unknown": "Unrecognized node",
17076
+ "rules.steps.not": "not ({value})",
17077
+ "rules.steps.and": "and",
17078
+ "rules.steps.or": "or",
17079
+ "rules.steps.more": "{head} +{count} more",
17080
+ "rules.steps.listSeparator": ", ",
17081
+ "rules.steps.yes": "yes",
17082
+ "rules.steps.no": "no",
17083
+ "rules.steps.chain": "Effective condition: {chain}",
17084
+ "rules.steps.branchUnsupported": "This layer registers once at assembly time and has no per-turn evaluation point, so conditional branches are unavailable here. Use pre-step, subagent, tool-pipeline, or turn-stop instead."
16454
17085
  };
16455
17086
  //#endregion
16456
17087
  //#region src/client/locales.ts