triggerix-editor-preset-war3 0.0.6 → 0.0.8
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 +351 -0
- package/dist/index.cjs +171 -131
- package/dist/index.d.cts +80 -45
- package/dist/index.d.mts +80 -45
- package/dist/index.d.ts +80 -45
- package/dist/index.mjs +171 -131
- package/package.json +10 -7
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
|
+
[](https://www.npmjs.com/package/triggerix-editor-preset-war3)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](./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`(单事件便捷);多事件:`addEvent` / `removeEvent` / `setEventSlotAt(index, …)`
|
|
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
|
+
events: { type: string, payload?: Record<string, Value> }[], // 多事件 OR 触发
|
|
196
|
+
conditions?: ConditionItem[], // 扁平数组,默认隐式 AND,可嵌套显式 group
|
|
197
|
+
actions: Action[] // [{ type, params? }]
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
> 注:编辑期 Condition 在内部以 `Action` 形式(`{ type, params }`)存储,作为扁平 `ConditionItem[]` 透传至运行时;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
|
+
events: ItemState[] // 多事件源(运行时按 OR 匹配);UI 单事件时通常为 0 或 1 项
|
|
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
|