triggerix-editor-preset-war3 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,351 @@
1
+ # triggerix-editor-preset-war3
2
+
3
+ > **War3-style template editor preset for [Triggerix](https://github.com/triggerix-collective)**
4
+ >
5
+ > 为 Triggerix 提供的「魔兽争霸 III 触发器」风格编辑器预设 —— 用模板字符串 + 槽位的方式声明 Event / Condition / Action,并最终序列化为标准 Trigger JSON。
6
+
7
+ [![npm](https://img.shields.io/npm/v/triggerix-editor-preset-war3)](https://www.npmjs.com/package/triggerix-editor-preset-war3)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/)
9
+ [![License](https://img.shields.io/github/license/triggerix-collective/triggerix-editor-preset-war3)](./LICENSE)
10
+
11
+ ## 特性
12
+
13
+ - 🎯 **模板驱动**:用 `"${unit} 发动技能效果"` 这样的自然语言模板声明触发器节点
14
+ - 🧩 **嵌套复合工具 (Composite Tool)**:Tool 可以由其他 Tool 组合,槽位无限嵌套
15
+ - 🪝 **类型安全工厂**:`defineLeafTool` / `defineCompositeTool` / `defineCondition` 提供完整类型推断
16
+ - 🔌 **可插拔预设**:`defineWar3Preset` 一键注入 Event/Action/Condition/Tool 集合
17
+ - 📤 **标准输出**:`toTrigger()` 直接产出 `@triggerix/core` 兼容的 Trigger JSON
18
+ - 🔍 **值来源查询**:`getValueSources(type)` 过滤同类型 Condition / Tool,作为槽位候选项
19
+ - 🪶 **零依赖运行**:仅依赖 `@triggerix/core` 与 `@triggerix/editor`
20
+
21
+ ## 安装
22
+
23
+ ```bash
24
+ pnpm add triggerix-editor-preset-war3 @triggerix/core @triggerix/editor
25
+ # 或
26
+ npm install triggerix-editor-preset-war3 @triggerix/core @triggerix/editor
27
+ ```
28
+
29
+ > 运行时需要 `crypto.randomUUID`(Node 18+ / 现代浏览器);不支持时会回退到基于时间戳的 ID。
30
+
31
+ ## 快速开始
32
+
33
+ ```ts
34
+ import {
35
+ createWar3Editor,
36
+ defineCondition,
37
+ defineLeafTool,
38
+ defineWar3Preset
39
+ } from 'triggerix-editor-preset-war3'
40
+
41
+ // 1. 定义工具 (槽位填充器)
42
+ const elementTool = defineLeafTool<string, string>({
43
+ type: 'element',
44
+ label: '元素',
45
+ input: {
46
+ type: 'select',
47
+ options: [
48
+ { value: '#submit-btn', label: '提交按钮' },
49
+ { value: '#search-input', label: '搜索框' }
50
+ ]
51
+ },
52
+ resolve: v => v
53
+ })
54
+
55
+ const eventNameTool = defineLeafTool<string, string>({
56
+ type: 'event-name',
57
+ label: '事件名',
58
+ input: { type: 'text', placeholder: '如 submit-success' },
59
+ resolve: v => v
60
+ })
61
+
62
+ const booleanTool = defineLeafTool<boolean, boolean>({
63
+ type: 'boolean',
64
+ label: '开关',
65
+ input: {
66
+ type: 'select',
67
+ options: [
68
+ { value: true, label: '启用' },
69
+ { value: false, label: '禁用' }
70
+ ]
71
+ },
72
+ resolve: v => v
73
+ })
74
+
75
+ // 2. 定义 Condition
76
+ const elementVisible = defineCondition<{ element: string }>({
77
+ id: 'element-visible',
78
+ label: '元素可见',
79
+ template: '${element} 当前可见',
80
+ slots: {
81
+ element: { label: '元素', tools: ['element'] }
82
+ },
83
+ resolve: ({ element }) => ({ element })
84
+ })
85
+
86
+ // 3. 定义 Event / Action(结构与 Condition 类似,继承 BaseItemDef)
87
+ // ...
88
+
89
+ // 4. 打包成 Preset
90
+ const preset = defineWar3Preset({
91
+ name: 'my-web-app',
92
+ events: [/* War3EventDef[] */],
93
+ actions: [/* War3ActionDef[] */],
94
+ conditions: [elementVisible],
95
+ tools: {
96
+ element: elementTool,
97
+ 'event-name': eventNameTool,
98
+ boolean: booleanTool
99
+ }
100
+ })
101
+
102
+ // 5. 创建编辑器并应用 Preset
103
+ const editor = createWar3Editor()
104
+ editor.applyPreset(preset)
105
+
106
+ // 6. 改变状态:监听 #submit-btn 点击后派发 submit-success
107
+ editor.setEvent('element-click')
108
+ editor.addCondition('element-visible')
109
+ editor.setConditionSlot(0, 'element', { tool: 'element', value: '#submit-btn' })
110
+
111
+ editor.addAction('emit-event')
112
+ editor.setActionSlot(0, 'element', { tool: 'element', value: '#submit-btn' })
113
+ editor.setActionSlot(0, 'event', { tool: 'event-name', value: 'submit-success' })
114
+
115
+ // 7. 输出标准 Trigger
116
+ const trigger = editor.toTrigger()
117
+ console.log(JSON.stringify(trigger, null, 2))
118
+ ```
119
+
120
+ ## 核心概念
121
+
122
+ | 概念 | 含义 |
123
+ |---|---|
124
+ | **Trigger** | 一条完整的触发器:`{ event, conditions?, actions[] }`,即 `toTrigger()` 的输出 |
125
+ | **Template** | 模板字符串,如 `"${element} 被点击时,发送 ${eventName} 事件"`,`${key}` 为槽位 |
126
+ | **Slot (槽位)** | 模板中的可替换位置,由 `SlotDef` 声明(label + 允许填入的 tool 列表) |
127
+ | **Tool (工具)** | 槽位的填充器;分 `LeafTool`(单值输入)和 `CompositeTool`(由其他 Tool 嵌套) |
128
+ | **Leaf Tool Input** | `text` / `number` / `select`(`select` 携带 `options: { value, label }[]`) |
129
+ | **Segment** | 模板解析后的最小单元:`{ type: 'text', content }` 或 `{ type: 'slot', key, label, tools, value, entry }` |
130
+ | **Item State** | 编辑器运行时状态:`{ id, slotValues: Record<key, SlotValueEntry> }` |
131
+ | **Descriptor** | UI 渲染用:把 Item/Template 解析为 `Segment[]`,附加 `SlotValueEntry` 上下文 |
132
+ | **Value Source** | 同类型的 Condition + Tool 集合,可作为另一个槽位的可选值来源 |
133
+
134
+ ### 数据流
135
+
136
+ ```
137
+ 定义侧 (开发者) 运行侧 (编辑器)
138
+ ───────────────── ─────────────────
139
+ War3EventDef / War3ActionDef / editor.applyPreset(preset)
140
+ War3ConditionDef / ToolDef │
141
+ │ ▼
142
+ └── defineWar3Preset ──▶ Preset War3Registry
143
+
144
+ 状态操作 │ setEvent / addAction / ...
145
+
146
+ War3EditorStateManager
147
+
148
+ ┌─────────────────────────┼─────────────────────────┐
149
+ ▼ ▼ ▼
150
+ parseTemplate resolveSlotDisplayText toTrigger
151
+ (→ ItemDescriptor) (→ 人类可读展示文本) (→ Trigger JSON)
152
+ ```
153
+
154
+ ## API 概览
155
+
156
+ ### 工厂与预设
157
+
158
+ | 导出 | 说明 |
159
+ |---|---|
160
+ | `createWar3Editor()` | 创建 `War3Editor` 实例(`Editor<War3EditorState>`) |
161
+ | `defineWar3Preset(options)` | 打包为 `Preset<War3Editor>`,批量注册 events / actions / conditions / tools |
162
+ | `defineLeafTool(def)` | 类型安全的叶子工具工厂 |
163
+ | `defineCompositeTool(def)` | 类型安全的复合工具工厂(槽位值类型从 `resolve` 推断) |
164
+ | `defineCondition(def)` | 类型安全的 Condition 工厂 |
165
+
166
+ ### `War3Editor` 方法
167
+
168
+ - **注册**:`registerEvent` / `registerAction` / `registerCondition` / `registerTool`
169
+ - **查询**:`getAvailableEvents` / `getAvailableActions` / `getAvailableConditions`
170
+ - **描述符**:`getEventDescriptor` / `getActionDescriptor(index)` / `getConditionDescriptor(index)` / `getToolDescriptor(name, slotValues?)` / `getSlotTools(slotDef)`
171
+ - **状态操作**:
172
+ - Event:`setEvent` / `clearEvent` / `setEventSlot`
173
+ - Action:`addAction` / `removeAction` / `moveAction` / `setActionSlot`
174
+ - Condition:`addCondition` / `removeCondition` / `setConditionSlot`
175
+ - **值来源**:`getValueSources(valueType?)` 返回 `{ conditions, tools }`
176
+ - **生命周期**:`getState` / `onChange` / `reset` / `dispose`
177
+ - **序列化**:`toTrigger(triggerId?)` 输出标准 Trigger;`resolveSlotValue(entry)` 解析单个槽位
178
+
179
+ ### 独立函数
180
+
181
+ | 导出 | 说明 |
182
+ |---|---|
183
+ | `parseTemplate(template, slots?, slotValues?)` | 模板 → `Segment[]` |
184
+ | `resolveSlotDisplayText(entry, registry, fallbackLabel)` | `SlotValueEntry` → 可读文本 |
185
+ | `resolveSlotValue(entry, registry)` | `SlotValueEntry` → `Value`(递归) |
186
+ | `getEventDescriptor` / `getActionDescriptor` / `getConditionDescriptor` / `getToolDescriptor` / `getSlotToolDescriptors` | 不走 Editor 的纯函数版本(直接接收 `War3Registry`) |
187
+
188
+ ### `toTrigger` 输出结构
189
+
190
+ 与 `@triggerix/core` 完全兼容:
191
+
192
+ ```ts
193
+ {
194
+ id: string, // 默认 crypto.randomUUID(),可外部传入
195
+ event: { type: string, payload?: Record<string, Value> },
196
+ conditions?: { type: 'and', conditions: Action[] }, // 仅当 conditions 非空时存在
197
+ actions: Action[] // [{ type, params? }]
198
+ }
199
+ ```
200
+
201
+ > 注:编辑期 Condition 在内部以 `Action` 形式(`{ type, params }`)存储,序列化为 `and` 分组;Condition ↔ Action 的语义映射由运行时层完成。
202
+
203
+ ## 模块结构
204
+
205
+ ```
206
+ src/
207
+ ├── index.ts # 统一入口
208
+ ├── types.ts # 所有类型定义
209
+ ├── createWar3Editor.ts # 工厂函数:组装 Registry + State + Serializer
210
+ ├── preset.ts # defineWar3Preset → Preset<War3Editor>
211
+ ├── registry.ts # War3Registry(继承 BaseRegistry + Tool)
212
+ ├── state.ts # War3EditorStateManager(继承 ObservableState)
213
+ ├── parser.ts # 模板字符串 → Segment[]
214
+ ├── descriptor.ts # Item/Template → ItemDescriptor / ToolDescriptor
215
+ ├── display.ts # SlotValueEntry → 人类可读展示文本
216
+ ├── serializer.ts # War3EditorState → Trigger JSON
217
+ └── helpers.ts # defineLeafTool / defineCompositeTool / defineCondition
218
+ ```
219
+
220
+ ## 模板系统详解
221
+
222
+ ### 模板字符串
223
+
224
+ 模板中以 `${key}` 标记槽位,`key` 须为 `SlotDef` 中声明的键:
225
+
226
+ ```ts
227
+ {
228
+ id: 'element-focus',
229
+ label: '元素获得焦点',
230
+ template: '${element} 获得焦点时,切换为 ${state}',
231
+ slots: {
232
+ element: { label: '元素', tools: ['element'] },
233
+ state: { label: '状态', tools: ['boolean'] }
234
+ }
235
+ }
236
+ ```
237
+
238
+ 解析后 `Segment[]`:
239
+
240
+ ```ts
241
+ [
242
+ { type: 'slot', key: 'element', label: '元素', tools: ['element'], value: ..., entry: ... },
243
+ { type: 'text', content: ' 获得焦点时,切换为 ' },
244
+ { type: 'slot', key: 'state', label: '状态', tools: ['boolean'], value: ..., entry: ... }
245
+ ]
246
+ ```
247
+
248
+ > **未知 `${key}`**:未在 `slots` 中声明的占位符将作为普通文本保留(不抛错),便于迁移期兼容。
249
+
250
+ ### Leaf Tool
251
+
252
+ ```ts
253
+ defineLeafTool<boolean, boolean>({
254
+ type: 'boolean', // 语义类型(用于 getValueSources 过滤)
255
+ label: '开关',
256
+ input: {
257
+ type: 'select',
258
+ options: [{ value: true, label: '启用' }]
259
+ },
260
+ resolve: v => v // 序列化为 Trigger.params 时的最终值
261
+ })
262
+ ```
263
+
264
+ `resolve` 接收 `input` 类型,返回**最终写入 `params`** 的值(可做单位换算、字符串映射等)。
265
+
266
+ ### Composite Tool
267
+
268
+ ```ts
269
+ const emitEventTool = defineCompositeTool<
270
+ { element: string, event: string },
271
+ { target: string, type: string }
272
+ >({
273
+ type: 'emit-event',
274
+ label: '发送事件',
275
+ template: '从 ${element} 派发 ${event}',
276
+ slots: {
277
+ element: { label: '元素', tools: ['element'] },
278
+ event: { label: '事件名', tools: ['event-name'] }
279
+ },
280
+ resolve: ({ element, event }) => ({ target: element, type: event })
281
+ })
282
+ ```
283
+
284
+ - 槽位键 `source` / `target` 会从 `resolve` 参数中推断,保证 `slots` 键与 `resolve` 参数键一致
285
+ - Composite 可无限嵌套(一个 Composite 的槽位可以填入另一个 Composite)
286
+
287
+ ## 编辑器状态形状
288
+
289
+ ```ts
290
+ interface War3EditorState {
291
+ event: ItemState | null // 单一事件
292
+ conditions: ItemState[] // 条件列表(AND 语义)
293
+ actions: ItemState[] // 动作列表(顺序敏感)
294
+ }
295
+
296
+ interface ItemState {
297
+ id: string // 对应 War3EventDef/Action/Condition 的 id
298
+ slotValues: Record<string, SlotValueEntry> // 各槽位填充值
299
+ }
300
+
301
+ interface SlotValueEntry {
302
+ tool: string | null // 选中的 tool 名
303
+ value: unknown // leaf: 原值; composite: 通常为空对象
304
+ subSlots?: Record<string, SlotValueEntry> // composite 递归结构
305
+ }
306
+ ```
307
+
308
+ ## 监听变化
309
+
310
+ ```ts
311
+ const editor = createWar3Editor()
312
+ const off = editor.onChange((state) => {
313
+ // 触发时机:setEvent / addAction / setActionSlot / reset ...
314
+ console.log('state changed:', state)
315
+ })
316
+
317
+ off() // 取消订阅
318
+ ```
319
+
320
+ `onChange` 返回的解绑函数也可以在 `dispose()` 后自动失效。
321
+
322
+ ## 开发
323
+
324
+ ### 命令
325
+
326
+ ```bash
327
+ pnpm install # 安装依赖
328
+ pnpm dev # 监听构建(生成 stub 供本地调试)
329
+ pnpm build # 产出 dist/{index.mjs,index.cjs,index.d.ts}
330
+ pnpm typecheck # tsc --noEmit
331
+ pnpm lint # eslint --cache
332
+ pnpm release # bumpp 交互式升级版本号并打 tag
333
+ ```
334
+
335
+ ### 构建产物
336
+
337
+ - ESM: `dist/index.mjs`
338
+ - CJS: `dist/index.cjs`
339
+ - 类型: `dist/index.d.ts`
340
+
341
+ 由 [unbuild](https://github.com/unjs/unbuild) 打包,`@triggerix/core` 与 `@triggerix/editor` 标记为 external。
342
+
343
+ ### 质量保障
344
+
345
+ - TypeScript `strict: true` + `declaration: true`
346
+ - ESLint (`@antfu/eslint-config`) + pre-commit (simple-git-hooks + nano-staged)
347
+ - 发布:推送 `v*` tag → GitHub Actions 自动 `changelogithub` + `pnpm publish`
348
+
349
+ ## License
350
+
351
+ [MIT](./LICENSE) © Triggerix Collective
package/dist/index.cjs CHANGED
@@ -34,47 +34,6 @@ function parseTemplate(template, slots, slotValues) {
34
34
  return segments;
35
35
  }
36
36
 
37
- function buildDescriptor(def, slotValues) {
38
- return {
39
- id: def.id,
40
- segments: parseTemplate(def.template, def.slots, slotValues)
41
- };
42
- }
43
- function getEventDescriptor(registry, id, slotValues) {
44
- const def = registry.getEvent(id);
45
- return def ? buildDescriptor(def, slotValues) : null;
46
- }
47
- function getActionDescriptor(registry, id, slotValues) {
48
- const def = registry.getAction(id);
49
- return def ? buildDescriptor(def, slotValues) : null;
50
- }
51
- function getConditionDescriptor(registry, id, slotValues) {
52
- const def = registry.getCondition(id);
53
- return def ? buildDescriptor(def, slotValues) : null;
54
- }
55
- function getToolDescriptor(registry, toolName, slotValues) {
56
- const def = registry.getTool(toolName);
57
- if (!def)
58
- return null;
59
- if (def.kind === "leaf") {
60
- return {
61
- kind: "leaf",
62
- name: toolName,
63
- label: def.label,
64
- input: def.input
65
- };
66
- }
67
- return {
68
- kind: "composite",
69
- name: toolName,
70
- label: def.label,
71
- segments: parseTemplate(def.template, def.slots, slotValues)
72
- };
73
- }
74
- function getSlotToolDescriptors(registry, slotDef) {
75
- return slotDef.tools.map((name) => getToolDescriptor(registry, name)).filter((d) => d !== null);
76
- }
77
-
78
37
  class War3Registry extends editor.BaseRegistry {
79
38
  tools = /* @__PURE__ */ new Map();
80
39
  registerTool(name, def) {
@@ -97,72 +56,6 @@ class War3Registry extends editor.BaseRegistry {
97
56
  }
98
57
  }
99
58
 
100
- function resolveSlotValue(entry, registry) {
101
- if (!entry.tool)
102
- return void 0;
103
- const toolDef = registry.getTool(entry.tool);
104
- if (!toolDef)
105
- return void 0;
106
- if (toolDef.kind === "leaf") {
107
- return toolDef.resolve(entry.value);
108
- }
109
- const resolvedSubSlots = {};
110
- if (entry.subSlots) {
111
- for (const [key, subEntry] of Object.entries(entry.subSlots)) {
112
- resolvedSubSlots[key] = resolveSlotValue(subEntry, registry);
113
- }
114
- }
115
- return toolDef.resolve(resolvedSubSlots);
116
- }
117
- function resolveItemParams(slotValues, registry) {
118
- const params = {};
119
- let hasParams = false;
120
- for (const [key, entry] of Object.entries(slotValues)) {
121
- const resolved = resolveSlotValue(entry, registry);
122
- if (resolved !== void 0) {
123
- params[key] = resolved;
124
- hasParams = true;
125
- }
126
- }
127
- return hasParams ? params : void 0;
128
- }
129
- function serializeItems(items, registry) {
130
- return items.map((item) => {
131
- const params = resolveItemParams(item.slotValues, registry);
132
- const action = { type: item.id };
133
- if (params) {
134
- action.params = params;
135
- }
136
- return action;
137
- });
138
- }
139
- function generateRuleId() {
140
- if (typeof globalThis.crypto?.randomUUID === "function") {
141
- return globalThis.crypto.randomUUID();
142
- }
143
- return `rule-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
144
- }
145
- function toRule(state, registry, ruleId) {
146
- const eventParams = state.event ? resolveItemParams(state.event.slotValues, registry) : void 0;
147
- const event = {
148
- type: state.event?.id ?? "",
149
- ...eventParams ? { payload: eventParams } : {}
150
- };
151
- const actions = serializeItems(state.actions, registry);
152
- const rule = {
153
- id: ruleId ?? generateRuleId(),
154
- event,
155
- actions
156
- };
157
- if (state.conditions.length > 0) {
158
- rule.conditions = {
159
- type: "and",
160
- conditions: serializeItems(state.conditions, registry)
161
- };
162
- }
163
- return rule;
164
- }
165
-
166
59
  const INITIAL_STATE = {
167
60
  event: null,
168
61
  conditions: [],
@@ -249,6 +142,113 @@ class War3EditorStateManager extends editor.ObservableState {
249
142
  }
250
143
  }
251
144
 
145
+ function buildDescriptor(def, slotValues) {
146
+ return {
147
+ id: def.id,
148
+ segments: parseTemplate(def.template, def.slots, slotValues)
149
+ };
150
+ }
151
+ function getEventDescriptor(registry, id, slotValues) {
152
+ const def = registry.getEvent(id);
153
+ return def ? buildDescriptor(def, slotValues) : null;
154
+ }
155
+ function getActionDescriptor(registry, id, slotValues) {
156
+ const def = registry.getAction(id);
157
+ return def ? buildDescriptor(def, slotValues) : null;
158
+ }
159
+ function getConditionDescriptor(registry, id, slotValues) {
160
+ const def = registry.getCondition(id);
161
+ return def ? buildDescriptor(def, slotValues) : null;
162
+ }
163
+ function getToolDescriptor(registry, toolName, slotValues) {
164
+ const def = registry.getTool(toolName);
165
+ if (!def)
166
+ return null;
167
+ if (def.kind === "leaf") {
168
+ return {
169
+ kind: "leaf",
170
+ name: toolName,
171
+ label: def.label,
172
+ input: def.input
173
+ };
174
+ }
175
+ return {
176
+ kind: "composite",
177
+ name: toolName,
178
+ label: def.label,
179
+ segments: parseTemplate(def.template, def.slots, slotValues)
180
+ };
181
+ }
182
+ function getSlotToolDescriptors(registry, slotDef) {
183
+ return slotDef.tools.map((name) => getToolDescriptor(registry, name)).filter((d) => d !== null);
184
+ }
185
+
186
+ function resolveSlotValue(entry, registry) {
187
+ if (!entry.tool)
188
+ return void 0;
189
+ const toolDef = registry.getTool(entry.tool);
190
+ if (!toolDef)
191
+ return void 0;
192
+ if (toolDef.kind === "leaf") {
193
+ return toolDef.resolve(entry.value);
194
+ }
195
+ const resolvedSubSlots = {};
196
+ if (entry.subSlots) {
197
+ for (const [key, subEntry] of Object.entries(entry.subSlots)) {
198
+ resolvedSubSlots[key] = resolveSlotValue(subEntry, registry);
199
+ }
200
+ }
201
+ return toolDef.resolve(resolvedSubSlots);
202
+ }
203
+ function resolveItemParams(slotValues, registry) {
204
+ const params = {};
205
+ let hasParams = false;
206
+ for (const [key, entry] of Object.entries(slotValues)) {
207
+ const resolved = resolveSlotValue(entry, registry);
208
+ if (resolved !== void 0) {
209
+ params[key] = resolved;
210
+ hasParams = true;
211
+ }
212
+ }
213
+ return hasParams ? params : void 0;
214
+ }
215
+ function serializeItems(items, registry) {
216
+ return items.map((item) => {
217
+ const params = resolveItemParams(item.slotValues, registry);
218
+ const action = { type: item.id };
219
+ if (params) {
220
+ action.params = params;
221
+ }
222
+ return action;
223
+ });
224
+ }
225
+ function generateTriggerId() {
226
+ if (typeof globalThis.crypto?.randomUUID === "function") {
227
+ return globalThis.crypto.randomUUID();
228
+ }
229
+ return `trigger-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
230
+ }
231
+ function toTrigger(state, registry, triggerId) {
232
+ const eventParams = state.event ? resolveItemParams(state.event.slotValues, registry) : void 0;
233
+ const event = {
234
+ type: state.event?.id ?? "",
235
+ ...eventParams ? { payload: eventParams } : {}
236
+ };
237
+ const actions = serializeItems(state.actions, registry);
238
+ const trigger = {
239
+ id: triggerId ?? generateTriggerId(),
240
+ event,
241
+ actions
242
+ };
243
+ if (state.conditions.length > 0) {
244
+ trigger.conditions = {
245
+ type: "and",
246
+ conditions: serializeItems(state.conditions, registry)
247
+ };
248
+ }
249
+ return trigger;
250
+ }
251
+
252
252
  function createWar3Editor() {
253
253
  const registry = new War3Registry();
254
254
  const stateManager = new War3EditorStateManager();
@@ -256,7 +256,7 @@ function createWar3Editor() {
256
256
  // --- Editor 接口 ---
257
257
  getState: () => stateManager.getState(),
258
258
  onChange: (listener) => stateManager.onChange(listener),
259
- toRule: (ruleId) => toRule(stateManager.getState(), registry, ruleId),
259
+ toTrigger: (triggerId) => toTrigger(stateManager.getState(), registry, triggerId),
260
260
  reset: () => stateManager.reset(),
261
261
  dispose: () => stateManager.dispose(),
262
262
  // --- 注册 ---
@@ -316,6 +316,16 @@ function createWar3Editor() {
316
316
  return editor;
317
317
  }
318
318
 
319
+ function defineLeafTool(def) {
320
+ return { ...def, kind: "leaf" };
321
+ }
322
+ function defineCompositeTool(def) {
323
+ return { ...def, kind: "composite" };
324
+ }
325
+ function defineCondition(def) {
326
+ return def;
327
+ }
328
+
319
329
  function valueEquals(a, b) {
320
330
  if (a === b)
321
331
  return true;
@@ -368,16 +378,6 @@ function resolveSlotDisplayText(entry, registry, fallbackLabel) {
368
378
  }).join("");
369
379
  }
370
380
 
371
- function defineLeafTool(def) {
372
- return { ...def, kind: "leaf" };
373
- }
374
- function defineCompositeTool(def) {
375
- return { ...def, kind: "composite" };
376
- }
377
- function defineCondition(def) {
378
- return def;
379
- }
380
-
381
381
  function defineWar3Preset(options) {
382
382
  return {
383
383
  name: options.name,
@@ -409,4 +409,4 @@ exports.getToolDescriptor = getToolDescriptor;
409
409
  exports.parseTemplate = parseTemplate;
410
410
  exports.resolveSlotDisplayText = resolveSlotDisplayText;
411
411
  exports.resolveSlotValue = resolveSlotValue;
412
- exports.toRule = toRule;
412
+ exports.toTrigger = toTrigger;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { BaseItemDef, BaseRegistry, Editor, Preset, ObservableState } from '@triggerix/editor';
1
+ import { BaseItemDef, BaseRegistry, ObservableState, Editor, Preset } from '@triggerix/editor';
2
2
  export { BaseItemDef, Preset } from '@triggerix/editor';
3
- import { Value, Rule } from '@triggerix/core';
3
+ import { Value, Trigger } from '@triggerix/core';
4
4
 
5
5
  interface SlotDef {
6
6
  label: string;
@@ -93,6 +93,12 @@ interface War3PresetOptions {
93
93
  tools?: Record<string, ToolDef>;
94
94
  }
95
95
 
96
+ /**
97
+ * 解析模板字符串为 Segment 数组
98
+ * 模板格式: "普通文本${slotKey}更多文本"
99
+ */
100
+ declare function parseTemplate(template: string, slots?: Record<string, SlotDef>, slotValues?: Record<string, SlotValueEntry>): Segment[];
101
+
96
102
  /**
97
103
  * War3 注册表 - 继承 BaseRegistry,增加 Tool 注册能力
98
104
  */
@@ -107,6 +113,26 @@ declare class War3Registry extends BaseRegistry<War3EventDef, War3ActionDef, War
107
113
  };
108
114
  }
109
115
 
116
+ /**
117
+ * War3 编辑器状态管理
118
+ * 继承 ObservableState,提供模板模式特有的状态操作
119
+ */
120
+ declare class War3EditorStateManager extends ObservableState<War3EditorState> {
121
+ constructor();
122
+ setEvent(id: string): void;
123
+ clearEvent(): void;
124
+ setEventSlot(key: string, entry: SlotValueEntry): void;
125
+ addAction(id: string): void;
126
+ removeAction(index: number): void;
127
+ moveAction(from: number, to: number): void;
128
+ setActionSlot(actionIndex: number, key: string, entry: SlotValueEntry): void;
129
+ addCondition(id: string): void;
130
+ removeCondition(index: number): void;
131
+ setConditionSlot(conditionIndex: number, key: string, entry: SlotValueEntry): void;
132
+ reset(): void;
133
+ private setItemSlot;
134
+ }
135
+
110
136
  interface War3Editor extends Editor<War3EditorState> {
111
137
  registerEvent: (def: War3EventDef) => void;
112
138
  registerAction: (def: War3ActionDef) => void;
@@ -140,24 +166,6 @@ interface War3Editor extends Editor<War3EditorState> {
140
166
  }
141
167
  declare function createWar3Editor(): War3Editor;
142
168
 
143
- declare function getEventDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
144
- declare function getActionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
145
- declare function getConditionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
146
- declare function getToolDescriptor(registry: War3Registry, toolName: string, slotValues?: Record<string, SlotValueEntry>): ToolDescriptor | null;
147
- declare function getSlotToolDescriptors(registry: War3Registry, slotDef: SlotDef): ToolDescriptor[];
148
-
149
- /**
150
- * 递归解析 SlotValueEntry 为人类可读的展示文本
151
- *
152
- * - 如果 entry 为 null/undefined 或未选择 tool,返回 fallbackLabel(slot 占位文案)
153
- * - 如果 tool 为 leaf:
154
- * - select 类型:尝试匹配 options 找到对应 label(支持对象值的深比较)
155
- * - 其它类型:返回 value 的字符串形式
156
- * - 若 value 为空:返回 fallbackLabel
157
- * - 如果 tool 为 composite:使用 tool 的 template 递归展开
158
- */
159
- declare function resolveSlotDisplayText(entry: SlotValueEntry | null | undefined, registry: War3Registry, fallbackLabel: string): string;
160
-
161
169
  /**
162
170
  * Type-safe LeafTool definition.
163
171
  * Input type comes from the resolve callback annotation; output is inferred.
@@ -192,11 +200,23 @@ declare function defineCondition<TSlotValues extends Record<string, unknown> = R
192
200
  resolve?: (slotValues: TSlotValues) => unknown;
193
201
  }): War3ConditionDef;
194
202
 
203
+ declare function getEventDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
204
+ declare function getActionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
205
+ declare function getConditionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
206
+ declare function getToolDescriptor(registry: War3Registry, toolName: string, slotValues?: Record<string, SlotValueEntry>): ToolDescriptor | null;
207
+ declare function getSlotToolDescriptors(registry: War3Registry, slotDef: SlotDef): ToolDescriptor[];
208
+
195
209
  /**
196
- * 解析模板字符串为 Segment 数组
197
- * 模板格式: "普通文本${slotKey}更多文本"
210
+ * 递归解析 SlotValueEntry 为人类可读的展示文本
211
+ *
212
+ * - 如果 entry 为 null/undefined 或未选择 tool,返回 fallbackLabel(slot 占位文案)
213
+ * - 如果 tool 为 leaf:
214
+ * - select 类型:尝试匹配 options 找到对应 label(支持对象值的深比较)
215
+ * - 其它类型:返回 value 的字符串形式
216
+ * - 若 value 为空:返回 fallbackLabel
217
+ * - 如果 tool 为 composite:使用 tool 的 template 递归展开
198
218
  */
199
- declare function parseTemplate(template: string, slots?: Record<string, SlotDef>, slotValues?: Record<string, SlotValueEntry>): Segment[];
219
+ declare function resolveSlotDisplayText(entry: SlotValueEntry | null | undefined, registry: War3Registry, fallbackLabel: string): string;
200
220
 
201
221
  declare function defineWar3Preset(options: War3PresetOptions): Preset<War3Editor>;
202
222
 
@@ -205,7 +225,7 @@ declare function defineWar3Preset(options: War3PresetOptions): Preset<War3Editor
205
225
  */
206
226
  declare function resolveSlotValue(entry: SlotValueEntry, registry: War3Registry): Value | undefined;
207
227
  /**
208
- * Serialize editor state into a standard Rule JSON.
228
+ * Serialize editor state into a standard Trigger JSON.
209
229
  *
210
230
  * Output shape (compatible with @triggerix/core):
211
231
  * {
@@ -213,27 +233,7 @@ declare function resolveSlotValue(entry: SlotValueEntry, registry: War3Registry)
213
233
  * actions: [{ type, params? }]
214
234
  * }
215
235
  */
216
- declare function toRule(state: War3EditorState, registry: War3Registry, ruleId?: string): Rule;
217
-
218
- /**
219
- * War3 编辑器状态管理
220
- * 继承 ObservableState,提供模板模式特有的状态操作
221
- */
222
- declare class War3EditorStateManager extends ObservableState<War3EditorState> {
223
- constructor();
224
- setEvent(id: string): void;
225
- clearEvent(): void;
226
- setEventSlot(key: string, entry: SlotValueEntry): void;
227
- addAction(id: string): void;
228
- removeAction(index: number): void;
229
- moveAction(from: number, to: number): void;
230
- setActionSlot(actionIndex: number, key: string, entry: SlotValueEntry): void;
231
- addCondition(id: string): void;
232
- removeCondition(index: number): void;
233
- setConditionSlot(conditionIndex: number, key: string, entry: SlotValueEntry): void;
234
- reset(): void;
235
- private setItemSlot;
236
- }
236
+ declare function toTrigger(state: War3EditorState, registry: War3Registry, triggerId?: string): Trigger;
237
237
 
238
- export { War3EditorStateManager, War3Registry, createWar3Editor, defineCompositeTool, defineCondition, defineLeafTool, defineWar3Preset, getActionDescriptor, getConditionDescriptor, getEventDescriptor, getSlotToolDescriptors, getToolDescriptor, parseTemplate, resolveSlotDisplayText, resolveSlotValue, toRule };
238
+ export { War3EditorStateManager, War3Registry, createWar3Editor, defineCompositeTool, defineCondition, defineLeafTool, defineWar3Preset, getActionDescriptor, getConditionDescriptor, getEventDescriptor, getSlotToolDescriptors, getToolDescriptor, parseTemplate, resolveSlotDisplayText, resolveSlotValue, toTrigger };
239
239
  export type { CompositeToolDef, CompositeToolDescriptor, ItemDescriptor, ItemState, LeafToolDef, LeafToolDescriptor, LeafToolInput, Segment, SlotDef, SlotValueEntry, ToolDef, ToolDescriptor, War3ActionDef, War3ConditionDef, War3Editor, War3EditorState, War3EventDef, War3PresetOptions };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { BaseItemDef, BaseRegistry, Editor, Preset, ObservableState } from '@triggerix/editor';
1
+ import { BaseItemDef, BaseRegistry, ObservableState, Editor, Preset } from '@triggerix/editor';
2
2
  export { BaseItemDef, Preset } from '@triggerix/editor';
3
- import { Value, Rule } from '@triggerix/core';
3
+ import { Value, Trigger } from '@triggerix/core';
4
4
 
5
5
  interface SlotDef {
6
6
  label: string;
@@ -93,6 +93,12 @@ interface War3PresetOptions {
93
93
  tools?: Record<string, ToolDef>;
94
94
  }
95
95
 
96
+ /**
97
+ * 解析模板字符串为 Segment 数组
98
+ * 模板格式: "普通文本${slotKey}更多文本"
99
+ */
100
+ declare function parseTemplate(template: string, slots?: Record<string, SlotDef>, slotValues?: Record<string, SlotValueEntry>): Segment[];
101
+
96
102
  /**
97
103
  * War3 注册表 - 继承 BaseRegistry,增加 Tool 注册能力
98
104
  */
@@ -107,6 +113,26 @@ declare class War3Registry extends BaseRegistry<War3EventDef, War3ActionDef, War
107
113
  };
108
114
  }
109
115
 
116
+ /**
117
+ * War3 编辑器状态管理
118
+ * 继承 ObservableState,提供模板模式特有的状态操作
119
+ */
120
+ declare class War3EditorStateManager extends ObservableState<War3EditorState> {
121
+ constructor();
122
+ setEvent(id: string): void;
123
+ clearEvent(): void;
124
+ setEventSlot(key: string, entry: SlotValueEntry): void;
125
+ addAction(id: string): void;
126
+ removeAction(index: number): void;
127
+ moveAction(from: number, to: number): void;
128
+ setActionSlot(actionIndex: number, key: string, entry: SlotValueEntry): void;
129
+ addCondition(id: string): void;
130
+ removeCondition(index: number): void;
131
+ setConditionSlot(conditionIndex: number, key: string, entry: SlotValueEntry): void;
132
+ reset(): void;
133
+ private setItemSlot;
134
+ }
135
+
110
136
  interface War3Editor extends Editor<War3EditorState> {
111
137
  registerEvent: (def: War3EventDef) => void;
112
138
  registerAction: (def: War3ActionDef) => void;
@@ -140,24 +166,6 @@ interface War3Editor extends Editor<War3EditorState> {
140
166
  }
141
167
  declare function createWar3Editor(): War3Editor;
142
168
 
143
- declare function getEventDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
144
- declare function getActionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
145
- declare function getConditionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
146
- declare function getToolDescriptor(registry: War3Registry, toolName: string, slotValues?: Record<string, SlotValueEntry>): ToolDescriptor | null;
147
- declare function getSlotToolDescriptors(registry: War3Registry, slotDef: SlotDef): ToolDescriptor[];
148
-
149
- /**
150
- * 递归解析 SlotValueEntry 为人类可读的展示文本
151
- *
152
- * - 如果 entry 为 null/undefined 或未选择 tool,返回 fallbackLabel(slot 占位文案)
153
- * - 如果 tool 为 leaf:
154
- * - select 类型:尝试匹配 options 找到对应 label(支持对象值的深比较)
155
- * - 其它类型:返回 value 的字符串形式
156
- * - 若 value 为空:返回 fallbackLabel
157
- * - 如果 tool 为 composite:使用 tool 的 template 递归展开
158
- */
159
- declare function resolveSlotDisplayText(entry: SlotValueEntry | null | undefined, registry: War3Registry, fallbackLabel: string): string;
160
-
161
169
  /**
162
170
  * Type-safe LeafTool definition.
163
171
  * Input type comes from the resolve callback annotation; output is inferred.
@@ -192,11 +200,23 @@ declare function defineCondition<TSlotValues extends Record<string, unknown> = R
192
200
  resolve?: (slotValues: TSlotValues) => unknown;
193
201
  }): War3ConditionDef;
194
202
 
203
+ declare function getEventDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
204
+ declare function getActionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
205
+ declare function getConditionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
206
+ declare function getToolDescriptor(registry: War3Registry, toolName: string, slotValues?: Record<string, SlotValueEntry>): ToolDescriptor | null;
207
+ declare function getSlotToolDescriptors(registry: War3Registry, slotDef: SlotDef): ToolDescriptor[];
208
+
195
209
  /**
196
- * 解析模板字符串为 Segment 数组
197
- * 模板格式: "普通文本${slotKey}更多文本"
210
+ * 递归解析 SlotValueEntry 为人类可读的展示文本
211
+ *
212
+ * - 如果 entry 为 null/undefined 或未选择 tool,返回 fallbackLabel(slot 占位文案)
213
+ * - 如果 tool 为 leaf:
214
+ * - select 类型:尝试匹配 options 找到对应 label(支持对象值的深比较)
215
+ * - 其它类型:返回 value 的字符串形式
216
+ * - 若 value 为空:返回 fallbackLabel
217
+ * - 如果 tool 为 composite:使用 tool 的 template 递归展开
198
218
  */
199
- declare function parseTemplate(template: string, slots?: Record<string, SlotDef>, slotValues?: Record<string, SlotValueEntry>): Segment[];
219
+ declare function resolveSlotDisplayText(entry: SlotValueEntry | null | undefined, registry: War3Registry, fallbackLabel: string): string;
200
220
 
201
221
  declare function defineWar3Preset(options: War3PresetOptions): Preset<War3Editor>;
202
222
 
@@ -205,7 +225,7 @@ declare function defineWar3Preset(options: War3PresetOptions): Preset<War3Editor
205
225
  */
206
226
  declare function resolveSlotValue(entry: SlotValueEntry, registry: War3Registry): Value | undefined;
207
227
  /**
208
- * Serialize editor state into a standard Rule JSON.
228
+ * Serialize editor state into a standard Trigger JSON.
209
229
  *
210
230
  * Output shape (compatible with @triggerix/core):
211
231
  * {
@@ -213,27 +233,7 @@ declare function resolveSlotValue(entry: SlotValueEntry, registry: War3Registry)
213
233
  * actions: [{ type, params? }]
214
234
  * }
215
235
  */
216
- declare function toRule(state: War3EditorState, registry: War3Registry, ruleId?: string): Rule;
217
-
218
- /**
219
- * War3 编辑器状态管理
220
- * 继承 ObservableState,提供模板模式特有的状态操作
221
- */
222
- declare class War3EditorStateManager extends ObservableState<War3EditorState> {
223
- constructor();
224
- setEvent(id: string): void;
225
- clearEvent(): void;
226
- setEventSlot(key: string, entry: SlotValueEntry): void;
227
- addAction(id: string): void;
228
- removeAction(index: number): void;
229
- moveAction(from: number, to: number): void;
230
- setActionSlot(actionIndex: number, key: string, entry: SlotValueEntry): void;
231
- addCondition(id: string): void;
232
- removeCondition(index: number): void;
233
- setConditionSlot(conditionIndex: number, key: string, entry: SlotValueEntry): void;
234
- reset(): void;
235
- private setItemSlot;
236
- }
236
+ declare function toTrigger(state: War3EditorState, registry: War3Registry, triggerId?: string): Trigger;
237
237
 
238
- export { War3EditorStateManager, War3Registry, createWar3Editor, defineCompositeTool, defineCondition, defineLeafTool, defineWar3Preset, getActionDescriptor, getConditionDescriptor, getEventDescriptor, getSlotToolDescriptors, getToolDescriptor, parseTemplate, resolveSlotDisplayText, resolveSlotValue, toRule };
238
+ export { War3EditorStateManager, War3Registry, createWar3Editor, defineCompositeTool, defineCondition, defineLeafTool, defineWar3Preset, getActionDescriptor, getConditionDescriptor, getEventDescriptor, getSlotToolDescriptors, getToolDescriptor, parseTemplate, resolveSlotDisplayText, resolveSlotValue, toTrigger };
239
239
  export type { CompositeToolDef, CompositeToolDescriptor, ItemDescriptor, ItemState, LeafToolDef, LeafToolDescriptor, LeafToolInput, Segment, SlotDef, SlotValueEntry, ToolDef, ToolDescriptor, War3ActionDef, War3ConditionDef, War3Editor, War3EditorState, War3EventDef, War3PresetOptions };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { BaseItemDef, BaseRegistry, Editor, Preset, ObservableState } from '@triggerix/editor';
1
+ import { BaseItemDef, BaseRegistry, ObservableState, Editor, Preset } from '@triggerix/editor';
2
2
  export { BaseItemDef, Preset } from '@triggerix/editor';
3
- import { Value, Rule } from '@triggerix/core';
3
+ import { Value, Trigger } from '@triggerix/core';
4
4
 
5
5
  interface SlotDef {
6
6
  label: string;
@@ -93,6 +93,12 @@ interface War3PresetOptions {
93
93
  tools?: Record<string, ToolDef>;
94
94
  }
95
95
 
96
+ /**
97
+ * 解析模板字符串为 Segment 数组
98
+ * 模板格式: "普通文本${slotKey}更多文本"
99
+ */
100
+ declare function parseTemplate(template: string, slots?: Record<string, SlotDef>, slotValues?: Record<string, SlotValueEntry>): Segment[];
101
+
96
102
  /**
97
103
  * War3 注册表 - 继承 BaseRegistry,增加 Tool 注册能力
98
104
  */
@@ -107,6 +113,26 @@ declare class War3Registry extends BaseRegistry<War3EventDef, War3ActionDef, War
107
113
  };
108
114
  }
109
115
 
116
+ /**
117
+ * War3 编辑器状态管理
118
+ * 继承 ObservableState,提供模板模式特有的状态操作
119
+ */
120
+ declare class War3EditorStateManager extends ObservableState<War3EditorState> {
121
+ constructor();
122
+ setEvent(id: string): void;
123
+ clearEvent(): void;
124
+ setEventSlot(key: string, entry: SlotValueEntry): void;
125
+ addAction(id: string): void;
126
+ removeAction(index: number): void;
127
+ moveAction(from: number, to: number): void;
128
+ setActionSlot(actionIndex: number, key: string, entry: SlotValueEntry): void;
129
+ addCondition(id: string): void;
130
+ removeCondition(index: number): void;
131
+ setConditionSlot(conditionIndex: number, key: string, entry: SlotValueEntry): void;
132
+ reset(): void;
133
+ private setItemSlot;
134
+ }
135
+
110
136
  interface War3Editor extends Editor<War3EditorState> {
111
137
  registerEvent: (def: War3EventDef) => void;
112
138
  registerAction: (def: War3ActionDef) => void;
@@ -140,24 +166,6 @@ interface War3Editor extends Editor<War3EditorState> {
140
166
  }
141
167
  declare function createWar3Editor(): War3Editor;
142
168
 
143
- declare function getEventDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
144
- declare function getActionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
145
- declare function getConditionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
146
- declare function getToolDescriptor(registry: War3Registry, toolName: string, slotValues?: Record<string, SlotValueEntry>): ToolDescriptor | null;
147
- declare function getSlotToolDescriptors(registry: War3Registry, slotDef: SlotDef): ToolDescriptor[];
148
-
149
- /**
150
- * 递归解析 SlotValueEntry 为人类可读的展示文本
151
- *
152
- * - 如果 entry 为 null/undefined 或未选择 tool,返回 fallbackLabel(slot 占位文案)
153
- * - 如果 tool 为 leaf:
154
- * - select 类型:尝试匹配 options 找到对应 label(支持对象值的深比较)
155
- * - 其它类型:返回 value 的字符串形式
156
- * - 若 value 为空:返回 fallbackLabel
157
- * - 如果 tool 为 composite:使用 tool 的 template 递归展开
158
- */
159
- declare function resolveSlotDisplayText(entry: SlotValueEntry | null | undefined, registry: War3Registry, fallbackLabel: string): string;
160
-
161
169
  /**
162
170
  * Type-safe LeafTool definition.
163
171
  * Input type comes from the resolve callback annotation; output is inferred.
@@ -192,11 +200,23 @@ declare function defineCondition<TSlotValues extends Record<string, unknown> = R
192
200
  resolve?: (slotValues: TSlotValues) => unknown;
193
201
  }): War3ConditionDef;
194
202
 
203
+ declare function getEventDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
204
+ declare function getActionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
205
+ declare function getConditionDescriptor(registry: War3Registry, id: string, slotValues?: Record<string, SlotValueEntry>): ItemDescriptor | null;
206
+ declare function getToolDescriptor(registry: War3Registry, toolName: string, slotValues?: Record<string, SlotValueEntry>): ToolDescriptor | null;
207
+ declare function getSlotToolDescriptors(registry: War3Registry, slotDef: SlotDef): ToolDescriptor[];
208
+
195
209
  /**
196
- * 解析模板字符串为 Segment 数组
197
- * 模板格式: "普通文本${slotKey}更多文本"
210
+ * 递归解析 SlotValueEntry 为人类可读的展示文本
211
+ *
212
+ * - 如果 entry 为 null/undefined 或未选择 tool,返回 fallbackLabel(slot 占位文案)
213
+ * - 如果 tool 为 leaf:
214
+ * - select 类型:尝试匹配 options 找到对应 label(支持对象值的深比较)
215
+ * - 其它类型:返回 value 的字符串形式
216
+ * - 若 value 为空:返回 fallbackLabel
217
+ * - 如果 tool 为 composite:使用 tool 的 template 递归展开
198
218
  */
199
- declare function parseTemplate(template: string, slots?: Record<string, SlotDef>, slotValues?: Record<string, SlotValueEntry>): Segment[];
219
+ declare function resolveSlotDisplayText(entry: SlotValueEntry | null | undefined, registry: War3Registry, fallbackLabel: string): string;
200
220
 
201
221
  declare function defineWar3Preset(options: War3PresetOptions): Preset<War3Editor>;
202
222
 
@@ -205,7 +225,7 @@ declare function defineWar3Preset(options: War3PresetOptions): Preset<War3Editor
205
225
  */
206
226
  declare function resolveSlotValue(entry: SlotValueEntry, registry: War3Registry): Value | undefined;
207
227
  /**
208
- * Serialize editor state into a standard Rule JSON.
228
+ * Serialize editor state into a standard Trigger JSON.
209
229
  *
210
230
  * Output shape (compatible with @triggerix/core):
211
231
  * {
@@ -213,27 +233,7 @@ declare function resolveSlotValue(entry: SlotValueEntry, registry: War3Registry)
213
233
  * actions: [{ type, params? }]
214
234
  * }
215
235
  */
216
- declare function toRule(state: War3EditorState, registry: War3Registry, ruleId?: string): Rule;
217
-
218
- /**
219
- * War3 编辑器状态管理
220
- * 继承 ObservableState,提供模板模式特有的状态操作
221
- */
222
- declare class War3EditorStateManager extends ObservableState<War3EditorState> {
223
- constructor();
224
- setEvent(id: string): void;
225
- clearEvent(): void;
226
- setEventSlot(key: string, entry: SlotValueEntry): void;
227
- addAction(id: string): void;
228
- removeAction(index: number): void;
229
- moveAction(from: number, to: number): void;
230
- setActionSlot(actionIndex: number, key: string, entry: SlotValueEntry): void;
231
- addCondition(id: string): void;
232
- removeCondition(index: number): void;
233
- setConditionSlot(conditionIndex: number, key: string, entry: SlotValueEntry): void;
234
- reset(): void;
235
- private setItemSlot;
236
- }
236
+ declare function toTrigger(state: War3EditorState, registry: War3Registry, triggerId?: string): Trigger;
237
237
 
238
- export { War3EditorStateManager, War3Registry, createWar3Editor, defineCompositeTool, defineCondition, defineLeafTool, defineWar3Preset, getActionDescriptor, getConditionDescriptor, getEventDescriptor, getSlotToolDescriptors, getToolDescriptor, parseTemplate, resolveSlotDisplayText, resolveSlotValue, toRule };
238
+ export { War3EditorStateManager, War3Registry, createWar3Editor, defineCompositeTool, defineCondition, defineLeafTool, defineWar3Preset, getActionDescriptor, getConditionDescriptor, getEventDescriptor, getSlotToolDescriptors, getToolDescriptor, parseTemplate, resolveSlotDisplayText, resolveSlotValue, toTrigger };
239
239
  export type { CompositeToolDef, CompositeToolDescriptor, ItemDescriptor, ItemState, LeafToolDef, LeafToolDescriptor, LeafToolInput, Segment, SlotDef, SlotValueEntry, ToolDef, ToolDescriptor, War3ActionDef, War3ConditionDef, War3Editor, War3EditorState, War3EventDef, War3PresetOptions };
package/dist/index.mjs CHANGED
@@ -32,47 +32,6 @@ function parseTemplate(template, slots, slotValues) {
32
32
  return segments;
33
33
  }
34
34
 
35
- function buildDescriptor(def, slotValues) {
36
- return {
37
- id: def.id,
38
- segments: parseTemplate(def.template, def.slots, slotValues)
39
- };
40
- }
41
- function getEventDescriptor(registry, id, slotValues) {
42
- const def = registry.getEvent(id);
43
- return def ? buildDescriptor(def, slotValues) : null;
44
- }
45
- function getActionDescriptor(registry, id, slotValues) {
46
- const def = registry.getAction(id);
47
- return def ? buildDescriptor(def, slotValues) : null;
48
- }
49
- function getConditionDescriptor(registry, id, slotValues) {
50
- const def = registry.getCondition(id);
51
- return def ? buildDescriptor(def, slotValues) : null;
52
- }
53
- function getToolDescriptor(registry, toolName, slotValues) {
54
- const def = registry.getTool(toolName);
55
- if (!def)
56
- return null;
57
- if (def.kind === "leaf") {
58
- return {
59
- kind: "leaf",
60
- name: toolName,
61
- label: def.label,
62
- input: def.input
63
- };
64
- }
65
- return {
66
- kind: "composite",
67
- name: toolName,
68
- label: def.label,
69
- segments: parseTemplate(def.template, def.slots, slotValues)
70
- };
71
- }
72
- function getSlotToolDescriptors(registry, slotDef) {
73
- return slotDef.tools.map((name) => getToolDescriptor(registry, name)).filter((d) => d !== null);
74
- }
75
-
76
35
  class War3Registry extends BaseRegistry {
77
36
  tools = /* @__PURE__ */ new Map();
78
37
  registerTool(name, def) {
@@ -95,72 +54,6 @@ class War3Registry extends BaseRegistry {
95
54
  }
96
55
  }
97
56
 
98
- function resolveSlotValue(entry, registry) {
99
- if (!entry.tool)
100
- return void 0;
101
- const toolDef = registry.getTool(entry.tool);
102
- if (!toolDef)
103
- return void 0;
104
- if (toolDef.kind === "leaf") {
105
- return toolDef.resolve(entry.value);
106
- }
107
- const resolvedSubSlots = {};
108
- if (entry.subSlots) {
109
- for (const [key, subEntry] of Object.entries(entry.subSlots)) {
110
- resolvedSubSlots[key] = resolveSlotValue(subEntry, registry);
111
- }
112
- }
113
- return toolDef.resolve(resolvedSubSlots);
114
- }
115
- function resolveItemParams(slotValues, registry) {
116
- const params = {};
117
- let hasParams = false;
118
- for (const [key, entry] of Object.entries(slotValues)) {
119
- const resolved = resolveSlotValue(entry, registry);
120
- if (resolved !== void 0) {
121
- params[key] = resolved;
122
- hasParams = true;
123
- }
124
- }
125
- return hasParams ? params : void 0;
126
- }
127
- function serializeItems(items, registry) {
128
- return items.map((item) => {
129
- const params = resolveItemParams(item.slotValues, registry);
130
- const action = { type: item.id };
131
- if (params) {
132
- action.params = params;
133
- }
134
- return action;
135
- });
136
- }
137
- function generateRuleId() {
138
- if (typeof globalThis.crypto?.randomUUID === "function") {
139
- return globalThis.crypto.randomUUID();
140
- }
141
- return `rule-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
142
- }
143
- function toRule(state, registry, ruleId) {
144
- const eventParams = state.event ? resolveItemParams(state.event.slotValues, registry) : void 0;
145
- const event = {
146
- type: state.event?.id ?? "",
147
- ...eventParams ? { payload: eventParams } : {}
148
- };
149
- const actions = serializeItems(state.actions, registry);
150
- const rule = {
151
- id: ruleId ?? generateRuleId(),
152
- event,
153
- actions
154
- };
155
- if (state.conditions.length > 0) {
156
- rule.conditions = {
157
- type: "and",
158
- conditions: serializeItems(state.conditions, registry)
159
- };
160
- }
161
- return rule;
162
- }
163
-
164
57
  const INITIAL_STATE = {
165
58
  event: null,
166
59
  conditions: [],
@@ -247,6 +140,113 @@ class War3EditorStateManager extends ObservableState {
247
140
  }
248
141
  }
249
142
 
143
+ function buildDescriptor(def, slotValues) {
144
+ return {
145
+ id: def.id,
146
+ segments: parseTemplate(def.template, def.slots, slotValues)
147
+ };
148
+ }
149
+ function getEventDescriptor(registry, id, slotValues) {
150
+ const def = registry.getEvent(id);
151
+ return def ? buildDescriptor(def, slotValues) : null;
152
+ }
153
+ function getActionDescriptor(registry, id, slotValues) {
154
+ const def = registry.getAction(id);
155
+ return def ? buildDescriptor(def, slotValues) : null;
156
+ }
157
+ function getConditionDescriptor(registry, id, slotValues) {
158
+ const def = registry.getCondition(id);
159
+ return def ? buildDescriptor(def, slotValues) : null;
160
+ }
161
+ function getToolDescriptor(registry, toolName, slotValues) {
162
+ const def = registry.getTool(toolName);
163
+ if (!def)
164
+ return null;
165
+ if (def.kind === "leaf") {
166
+ return {
167
+ kind: "leaf",
168
+ name: toolName,
169
+ label: def.label,
170
+ input: def.input
171
+ };
172
+ }
173
+ return {
174
+ kind: "composite",
175
+ name: toolName,
176
+ label: def.label,
177
+ segments: parseTemplate(def.template, def.slots, slotValues)
178
+ };
179
+ }
180
+ function getSlotToolDescriptors(registry, slotDef) {
181
+ return slotDef.tools.map((name) => getToolDescriptor(registry, name)).filter((d) => d !== null);
182
+ }
183
+
184
+ function resolveSlotValue(entry, registry) {
185
+ if (!entry.tool)
186
+ return void 0;
187
+ const toolDef = registry.getTool(entry.tool);
188
+ if (!toolDef)
189
+ return void 0;
190
+ if (toolDef.kind === "leaf") {
191
+ return toolDef.resolve(entry.value);
192
+ }
193
+ const resolvedSubSlots = {};
194
+ if (entry.subSlots) {
195
+ for (const [key, subEntry] of Object.entries(entry.subSlots)) {
196
+ resolvedSubSlots[key] = resolveSlotValue(subEntry, registry);
197
+ }
198
+ }
199
+ return toolDef.resolve(resolvedSubSlots);
200
+ }
201
+ function resolveItemParams(slotValues, registry) {
202
+ const params = {};
203
+ let hasParams = false;
204
+ for (const [key, entry] of Object.entries(slotValues)) {
205
+ const resolved = resolveSlotValue(entry, registry);
206
+ if (resolved !== void 0) {
207
+ params[key] = resolved;
208
+ hasParams = true;
209
+ }
210
+ }
211
+ return hasParams ? params : void 0;
212
+ }
213
+ function serializeItems(items, registry) {
214
+ return items.map((item) => {
215
+ const params = resolveItemParams(item.slotValues, registry);
216
+ const action = { type: item.id };
217
+ if (params) {
218
+ action.params = params;
219
+ }
220
+ return action;
221
+ });
222
+ }
223
+ function generateTriggerId() {
224
+ if (typeof globalThis.crypto?.randomUUID === "function") {
225
+ return globalThis.crypto.randomUUID();
226
+ }
227
+ return `trigger-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
228
+ }
229
+ function toTrigger(state, registry, triggerId) {
230
+ const eventParams = state.event ? resolveItemParams(state.event.slotValues, registry) : void 0;
231
+ const event = {
232
+ type: state.event?.id ?? "",
233
+ ...eventParams ? { payload: eventParams } : {}
234
+ };
235
+ const actions = serializeItems(state.actions, registry);
236
+ const trigger = {
237
+ id: triggerId ?? generateTriggerId(),
238
+ event,
239
+ actions
240
+ };
241
+ if (state.conditions.length > 0) {
242
+ trigger.conditions = {
243
+ type: "and",
244
+ conditions: serializeItems(state.conditions, registry)
245
+ };
246
+ }
247
+ return trigger;
248
+ }
249
+
250
250
  function createWar3Editor() {
251
251
  const registry = new War3Registry();
252
252
  const stateManager = new War3EditorStateManager();
@@ -254,7 +254,7 @@ function createWar3Editor() {
254
254
  // --- Editor 接口 ---
255
255
  getState: () => stateManager.getState(),
256
256
  onChange: (listener) => stateManager.onChange(listener),
257
- toRule: (ruleId) => toRule(stateManager.getState(), registry, ruleId),
257
+ toTrigger: (triggerId) => toTrigger(stateManager.getState(), registry, triggerId),
258
258
  reset: () => stateManager.reset(),
259
259
  dispose: () => stateManager.dispose(),
260
260
  // --- 注册 ---
@@ -314,6 +314,16 @@ function createWar3Editor() {
314
314
  return editor;
315
315
  }
316
316
 
317
+ function defineLeafTool(def) {
318
+ return { ...def, kind: "leaf" };
319
+ }
320
+ function defineCompositeTool(def) {
321
+ return { ...def, kind: "composite" };
322
+ }
323
+ function defineCondition(def) {
324
+ return def;
325
+ }
326
+
317
327
  function valueEquals(a, b) {
318
328
  if (a === b)
319
329
  return true;
@@ -366,16 +376,6 @@ function resolveSlotDisplayText(entry, registry, fallbackLabel) {
366
376
  }).join("");
367
377
  }
368
378
 
369
- function defineLeafTool(def) {
370
- return { ...def, kind: "leaf" };
371
- }
372
- function defineCompositeTool(def) {
373
- return { ...def, kind: "composite" };
374
- }
375
- function defineCondition(def) {
376
- return def;
377
- }
378
-
379
379
  function defineWar3Preset(options) {
380
380
  return {
381
381
  name: options.name,
@@ -392,4 +392,4 @@ function defineWar3Preset(options) {
392
392
  };
393
393
  }
394
394
 
395
- export { War3EditorStateManager, War3Registry, createWar3Editor, defineCompositeTool, defineCondition, defineLeafTool, defineWar3Preset, getActionDescriptor, getConditionDescriptor, getEventDescriptor, getSlotToolDescriptors, getToolDescriptor, parseTemplate, resolveSlotDisplayText, resolveSlotValue, toRule };
395
+ export { War3EditorStateManager, War3Registry, createWar3Editor, defineCompositeTool, defineCondition, defineLeafTool, defineWar3Preset, getActionDescriptor, getConditionDescriptor, getEventDescriptor, getSlotToolDescriptors, getToolDescriptor, parseTemplate, resolveSlotDisplayText, resolveSlotValue, toTrigger };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "triggerix-editor-preset-war3",
3
3
  "type": "module",
4
- "version": "0.0.5",
4
+ "version": "0.0.7",
5
5
  "description": "War3-style template editor preset for Triggerix",
6
- "homepage": "https://github.com/triggerix-lang/triggerix-editor-preset-war3#readme",
6
+ "homepage": "https://github.com/triggerix-collective/triggerix-editor-preset-war3#readme",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git@github.com:triggerix-lang/triggerix-editor-preset-war3.git"
9
+ "url": "git@github.com:triggerix-collective/triggerix-editor-preset-war3.git"
10
10
  },
11
11
  "bugs": {
12
- "url": "https://github.com/triggerix-lang/triggerix-editor-preset-war3/issues"
12
+ "url": "https://github.com/triggerix-collective/triggerix-editor-preset-war3/issues"
13
13
  },
14
14
  "exports": {
15
15
  ".": {
@@ -25,8 +25,8 @@
25
25
  "dist"
26
26
  ],
27
27
  "dependencies": {
28
- "@triggerix/core": "^0.0.6",
29
- "@triggerix/editor": "^0.0.6"
28
+ "@triggerix/core": "^0.0.8",
29
+ "@triggerix/editor": "^0.0.8"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@antfu/eslint-config": "^9.0.0",
@@ -36,7 +36,8 @@
36
36
  "nano-staged": "^1.0.2",
37
37
  "simple-git-hooks": "^2.13.1",
38
38
  "typescript": "^6.0.3",
39
- "unbuild": "^3.6.1"
39
+ "unbuild": "^3.6.1",
40
+ "vitest": "^4.1.9"
40
41
  },
41
42
  "simple-git-hooks": {
42
43
  "pre-commit": "pnpm nano-staged"
@@ -49,6 +50,8 @@
49
50
  "dev": "unbuild --stub",
50
51
  "lint": "eslint --cache",
51
52
  "typecheck": "tsc --noEmit",
53
+ "test": "vitest run",
54
+ "test:watch": "vitest",
52
55
  "release": "bumpp"
53
56
  }
54
57
  }