@knotx/plugins-history 0.4.11 → 0.4.13

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 (3) hide show
  1. package/README.en.md +378 -0
  2. package/README.md +378 -0
  3. package/package.json +6 -6
package/README.en.md ADDED
@@ -0,0 +1,378 @@
1
+ # @knotx/plugins-history
2
+
3
+ History plugin that provides undo/redo functionality for KnotX.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @knotx/plugins-history
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ The History plugin provides complete history management functionality for KnotX, including undo, redo, state tagging, and more. This plugin tracks all data changes and provides convenient history operation interfaces.
14
+
15
+ ## Implementation Principle
16
+
17
+ The core implementation principles of the History plugin:
18
+
19
+ 1. **Operation Interception**: Intercepts all data manager operations and records states before and after changes
20
+ 2. **Buffering Mechanism**: Uses debouncing to batch operations and avoid frequent history record creation
21
+ 3. **Reverse Operations**: Generates reverse operations for each operation to enable undo functionality
22
+ 4. **State Tagging**: Supports tagging specific states for quick return to designated history points
23
+
24
+ ## Dependencies
25
+
26
+ ### Core Dependencies
27
+ - `@knotx/core`: Provides base plugin architecture and data management
28
+ - `@knotx/decorators`: Provides decorator support
29
+ - `rxjs`: Provides reactive programming support
30
+
31
+ ### Automatic Dependencies
32
+ - `nodesManager` and `edgesManager` from `@knotx/core`: Automatically registered to the history system
33
+
34
+ ## API Documentation
35
+
36
+ ### Main Classes
37
+
38
+ #### History
39
+
40
+ The main class of the History plugin, extending `BasePlugin`.
41
+
42
+ ```typescript
43
+ export class History<T extends Record<string, IData> = Record<string, any>> extends BasePlugin<'history', HistoryConfig> {
44
+ name = 'history' as const
45
+ }
46
+ ```
47
+
48
+ ### Configuration Options
49
+
50
+ #### HistoryConfig
51
+
52
+ ```typescript
53
+ export interface HistoryConfig {
54
+ /** Maximum number of history records */
55
+ maxHistory?: number
56
+ /** Debounce time (milliseconds) */
57
+ debounceTime?: number
58
+ }
59
+ ```
60
+
61
+ ### Plugin Data (PluginData)
62
+
63
+ The History plugin provides the following data:
64
+
65
+ ```typescript
66
+ interface PluginData {
67
+ history: {
68
+ ref: IHistory
69
+ canUndo: boolean
70
+ canRedo: boolean
71
+ registerDataManager: (dataManager: DataManager<any>) => (() => void)
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### Interface Definitions
77
+
78
+ #### IHistory
79
+
80
+ ```typescript
81
+ export interface IHistory {
82
+ isUndoRedo: boolean
83
+ undo: () => void
84
+ redo: () => void
85
+ addTag: (tag: string) => void
86
+ removeTag: (tag: string) => void
87
+ toTag: (tag: string) => void
88
+ clearTags: () => void
89
+ }
90
+ ```
91
+
92
+ #### HistoryState
93
+
94
+ ```typescript
95
+ export interface HistoryState<T extends Record<string, IData> = Record<string, any>> {
96
+ value: {
97
+ [key: string]: HistoryOperation<T>[]
98
+ }
99
+ timestamp: number
100
+ }
101
+ ```
102
+
103
+ ## Usage Examples
104
+
105
+ ### Basic Usage
106
+
107
+ ```typescript
108
+ import { History } from '@knotx/plugins-history'
109
+
110
+ const engine = new Engine({
111
+ plugins: [History],
112
+ pluginConfig: {
113
+ history: {
114
+ maxHistory: 50,
115
+ debounceTime: 100,
116
+ },
117
+ },
118
+ })
119
+ ```
120
+
121
+ ### Undo and Redo Operations
122
+
123
+ ```typescript
124
+ const engine = new Engine({
125
+ plugins: [History],
126
+ })
127
+
128
+ const historyData = engine.getPluginData('history')
129
+
130
+ // Undo operation
131
+ if (historyData.canUndo) {
132
+ historyData.ref.undo()
133
+ }
134
+
135
+ // Redo operation
136
+ if (historyData.canRedo) {
137
+ historyData.ref.redo()
138
+ }
139
+ ```
140
+
141
+ ### Listening to History State
142
+
143
+ ```tsx
144
+ class HistoryUIPlugin extends BasePlugin {
145
+ @inject.history.canUndo()
146
+ canUndo!: boolean
147
+
148
+ @inject.history.canRedo()
149
+ canRedo!: boolean
150
+
151
+ @inject.history.ref()
152
+ history!: IHistory
153
+
154
+ @panel('bottom')
155
+ render() {
156
+ return (
157
+ <div>
158
+ <button
159
+ disabled={!this.canUndo}
160
+ onClick={() => this.history.undo()}
161
+ >
162
+ Undo
163
+ </button>
164
+ <button
165
+ disabled={!this.canRedo}
166
+ onClick={() => this.history.redo()}
167
+ >
168
+ Redo
169
+ </button>
170
+ </div>
171
+ )
172
+ }
173
+ }
174
+ ```
175
+
176
+ ### State Tagging Functionality
177
+
178
+ ```typescript
179
+ const engine = new Engine({
180
+ plugins: [History],
181
+ })
182
+
183
+ const historyData = engine.getPluginData('history')
184
+
185
+ // Tag current state
186
+ historyData.ref.addTag('checkpoint-1')
187
+
188
+ // After performing a series of operations...
189
+ // Return to tagged state
190
+ historyData.ref.toTag('checkpoint-1')
191
+
192
+ // Remove tag
193
+ historyData.ref.removeTag('checkpoint-1')
194
+
195
+ // Clear all tags
196
+ historyData.ref.clearTags()
197
+ ```
198
+
199
+ ### Custom Data Manager Integration
200
+
201
+ ```typescript
202
+ class CustomDataManager extends DataManager<MyData> {
203
+ constructor() {
204
+ super('custom')
205
+ }
206
+ }
207
+
208
+ const engine = new Engine({
209
+ plugins: [History],
210
+ })
211
+
212
+ const historyData = engine.getPluginData('history')
213
+ const customManager = new CustomDataManager()
214
+
215
+ // Register custom data manager
216
+ const unregister = historyData.registerDataManager(customManager)
217
+
218
+ // Unregister
219
+ unregister()
220
+ ```
221
+
222
+ ## Advanced Features
223
+
224
+ ### Batch Operation Optimization
225
+
226
+ ```typescript
227
+ class BatchHistoryPlugin extends BasePlugin {
228
+ @inject.history.ref()
229
+ history!: IHistory
230
+
231
+ performBatchOperation() {
232
+ // Batch operations are automatically debounced
233
+ engine.dispatchNodeOperation({
234
+ type: 'add',
235
+ data: { id: 'node1', /* ... */ },
236
+ })
237
+
238
+ engine.dispatchNodeOperation({
239
+ type: 'add',
240
+ data: { id: 'node2', /* ... */ },
241
+ })
242
+
243
+ // These operations will be merged into a single history entry
244
+ }
245
+ }
246
+ ```
247
+
248
+ ### Conditional History Recording
249
+
250
+ ```typescript
251
+ class ConditionalHistoryPlugin extends BasePlugin {
252
+ @inject.history.ref()
253
+ history!: IHistory
254
+
255
+ @inject.nodesManager()
256
+ nodesManager!: DataManager<Node>
257
+
258
+ performTemporaryOperation() {
259
+ // Check if in undo/redo process
260
+ if (this.history.isUndoRedo) {
261
+ // Avoid creating new history records during undo/redo
262
+ return
263
+ }
264
+
265
+ // Normal operation
266
+ this.nodesManager.dispatch({
267
+ type: 'update',
268
+ data: { id: 'node1', /* ... */ },
269
+ })
270
+ }
271
+ }
272
+ ```
273
+
274
+ ### History State Monitoring
275
+
276
+ ```typescript
277
+ class HistoryMonitorPlugin extends BasePlugin {
278
+ @inject.history.ref()
279
+ history!: IHistory
280
+
281
+ @subscribe.history.canUndo()
282
+ onCanUndoChange(canUndo: boolean) {
283
+ console.log('Can undo:', canUndo)
284
+ }
285
+
286
+ @subscribe.history.canRedo()
287
+ onCanRedoChange(canRedo: boolean) {
288
+ console.log('Can redo:', canRedo)
289
+ }
290
+
291
+ // Custom history record monitoring
292
+ @OnInit
293
+ init() {
294
+ // Listen to history changes
295
+ this.history.onHistoryChange?.subscribe((state) => {
296
+ console.log('History changed:', state)
297
+ })
298
+ }
299
+ }
300
+ ```
301
+
302
+ ## Performance Optimization
303
+
304
+ ### Debounce Configuration
305
+
306
+ ```typescript
307
+ const engine = new Engine({
308
+ plugins: [History],
309
+ pluginConfig: {
310
+ history: {
311
+ debounceTime: 200, // Increase debounce time to reduce history entries
312
+ },
313
+ },
314
+ })
315
+ ```
316
+
317
+ ### History Record Limits
318
+
319
+ ```typescript
320
+ const engine = new Engine({
321
+ plugins: [History],
322
+ pluginConfig: {
323
+ history: {
324
+ maxHistory: 20, // Reduce maximum history records to save memory
325
+ },
326
+ },
327
+ })
328
+ ```
329
+
330
+ ## File Directory Structure
331
+
332
+ ```
333
+ packages/plugins-history/
334
+ ├── src/
335
+ │ ├── history.ts # Main implementation file
336
+ │ └── index.ts # Export file
337
+ ├── dist/ # Build output directory
338
+ ├── package.json # Package configuration
339
+ ├── build.config.ts # Build configuration
340
+ ├── tsconfig.json # TypeScript configuration
341
+ ├── eslint.config.mjs # ESLint configuration
342
+ └── CHANGELOG.md # Changelog
343
+ ```
344
+
345
+ ### Core Files Description
346
+
347
+ - **history.ts**: Contains the main implementation of the History plugin, including operation interception, history management, and state tagging functionality
348
+ - **index.ts**: Exports the History class and related type definitions
349
+
350
+ ## Best Practices
351
+
352
+ ### Memory Management
353
+
354
+ 1. **Set Reasonable History Count**: Configure appropriate `maxHistory` value based on application needs
355
+ 2. **Clean Up Tags Timely**: Remove state tags that are no longer needed
356
+ 3. **Avoid Excessive Debouncing**: Too long debounce time may affect user experience
357
+
358
+ ### User Experience
359
+
360
+ 1. **Provide Undo/Redo Buttons**: Offer clear undo/redo operation entry points in the UI
361
+ 2. **Display Operation Status**: Enable/disable buttons based on `canUndo` and `canRedo` states
362
+ 3. **Keyboard Shortcuts**: Support `Ctrl+Z` and `Ctrl+Y` shortcuts
363
+
364
+ ### Development and Debugging
365
+
366
+ 1. **Monitor History State**: Listen to history changes during development for debugging
367
+ 2. **Use State Tags**: Set state tags at key operation points for testing and debugging
368
+
369
+ ## Notes
370
+
371
+ 1. **Performance with Large Data**: History records consume memory; consider performance optimization with large datasets
372
+ 2. **Asynchronous Operations**: Async operations may cause history order confusion; requires special attention
373
+ 3. **Data Consistency**: Ensure undo/redo operations don't break data consistency
374
+ 4. **Circular References**: Avoid creating circular references during history record processing
375
+
376
+ ## License
377
+
378
+ MIT
package/README.md ADDED
@@ -0,0 +1,378 @@
1
+ # @knotx/plugins-history
2
+
3
+ 历史记录插件,为 KnotX 提供撤销/重做功能。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install @knotx/plugins-history
9
+ ```
10
+
11
+ ## 概述
12
+
13
+ History 插件为 KnotX 提供了完整的历史记录管理功能,包括撤销、重做、标记状态等。该插件能够跟踪所有数据变化,并提供便捷的历史记录操作接口。
14
+
15
+ ## 实现原理
16
+
17
+ History 插件的核心实现原理:
18
+
19
+ 1. **操作拦截**:拦截所有数据管理器的操作,记录变化前后的状态
20
+ 2. **缓冲机制**:使用防抖机制批量处理操作,避免频繁的历史记录创建
21
+ 3. **反向操作**:为每个操作生成反向操作,实现撤销功能
22
+ 4. **状态标记**:支持标记特定状态,快速回到指定的历史点
23
+
24
+ ## 依赖关系
25
+
26
+ ### 核心依赖
27
+ - `@knotx/core`:提供基础插件架构和数据管理
28
+ - `@knotx/decorators`:提供装饰器支持
29
+ - `rxjs`:提供响应式编程支持
30
+
31
+ ### 自动依赖
32
+ - `@knotx/core` 中的 `nodesManager` 和 `edgesManager`:自动注册到历史记录系统
33
+
34
+ ## API 文档
35
+
36
+ ### 主要类
37
+
38
+ #### History
39
+
40
+ 历史记录插件的主要类,继承自 `BasePlugin`。
41
+
42
+ ```typescript
43
+ export class History<T extends Record<string, IData> = Record<string, any>> extends BasePlugin<'history', HistoryConfig> {
44
+ name = 'history' as const
45
+ }
46
+ ```
47
+
48
+ ### 配置选项
49
+
50
+ #### HistoryConfig
51
+
52
+ ```typescript
53
+ export interface HistoryConfig {
54
+ /** 最大历史记录数量 */
55
+ maxHistory?: number
56
+ /** 防抖时间(毫秒) */
57
+ debounceTime?: number
58
+ }
59
+ ```
60
+
61
+ ### 插件数据 (PluginData)
62
+
63
+ History 插件提供以下数据:
64
+
65
+ ```typescript
66
+ interface PluginData {
67
+ history: {
68
+ ref: IHistory
69
+ canUndo: boolean
70
+ canRedo: boolean
71
+ registerDataManager: (dataManager: DataManager<any>) => (() => void)
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### 接口定义
77
+
78
+ #### IHistory
79
+
80
+ ```typescript
81
+ export interface IHistory {
82
+ isUndoRedo: boolean
83
+ undo: () => void
84
+ redo: () => void
85
+ addTag: (tag: string) => void
86
+ removeTag: (tag: string) => void
87
+ toTag: (tag: string) => void
88
+ clearTags: () => void
89
+ }
90
+ ```
91
+
92
+ #### HistoryState
93
+
94
+ ```typescript
95
+ export interface HistoryState<T extends Record<string, IData> = Record<string, any>> {
96
+ value: {
97
+ [key: string]: HistoryOperation<T>[]
98
+ }
99
+ timestamp: number
100
+ }
101
+ ```
102
+
103
+ ## 使用示例
104
+
105
+ ### 基本用法
106
+
107
+ ```typescript
108
+ import { History } from '@knotx/plugins-history'
109
+
110
+ const engine = new Engine({
111
+ plugins: [History],
112
+ pluginConfig: {
113
+ history: {
114
+ maxHistory: 50,
115
+ debounceTime: 100,
116
+ },
117
+ },
118
+ })
119
+ ```
120
+
121
+ ### 撤销和重做操作
122
+
123
+ ```typescript
124
+ const engine = new Engine({
125
+ plugins: [History],
126
+ })
127
+
128
+ const historyData = engine.getPluginData('history')
129
+
130
+ // 撤销操作
131
+ if (historyData.canUndo) {
132
+ historyData.ref.undo()
133
+ }
134
+
135
+ // 重做操作
136
+ if (historyData.canRedo) {
137
+ historyData.ref.redo()
138
+ }
139
+ ```
140
+
141
+ ### 监听历史记录状态
142
+
143
+ ```tsx
144
+ class HistoryUIPlugin extends BasePlugin {
145
+ @inject.history.canUndo()
146
+ canUndo!: boolean
147
+
148
+ @inject.history.canRedo()
149
+ canRedo!: boolean
150
+
151
+ @inject.history.ref()
152
+ history!: IHistory
153
+
154
+ @panel('bottom')
155
+ render() {
156
+ return (
157
+ <div>
158
+ <button
159
+ disabled={!this.canUndo}
160
+ onClick={() => this.history.undo()}
161
+ >
162
+ 撤销
163
+ </button>
164
+ <button
165
+ disabled={!this.canRedo}
166
+ onClick={() => this.history.redo()}
167
+ >
168
+ 重做
169
+ </button>
170
+ </div>
171
+ )
172
+ }
173
+ }
174
+ ```
175
+
176
+ ### 状态标记功能
177
+
178
+ ```typescript
179
+ const engine = new Engine({
180
+ plugins: [History],
181
+ })
182
+
183
+ const historyData = engine.getPluginData('history')
184
+
185
+ // 标记当前状态
186
+ historyData.ref.addTag('checkpoint-1')
187
+
188
+ // 在进行一系列操作后...
189
+ // 回到标记的状态
190
+ historyData.ref.toTag('checkpoint-1')
191
+
192
+ // 移除标记
193
+ historyData.ref.removeTag('checkpoint-1')
194
+
195
+ // 清空所有标记
196
+ historyData.ref.clearTags()
197
+ ```
198
+
199
+ ### 自定义数据管理器集成
200
+
201
+ ```typescript
202
+ class CustomDataManager extends DataManager<MyData> {
203
+ constructor() {
204
+ super('custom')
205
+ }
206
+ }
207
+
208
+ const engine = new Engine({
209
+ plugins: [History],
210
+ })
211
+
212
+ const historyData = engine.getPluginData('history')
213
+ const customManager = new CustomDataManager()
214
+
215
+ // 注册自定义数据管理器
216
+ const unregister = historyData.registerDataManager(customManager)
217
+
218
+ // 取消注册
219
+ unregister()
220
+ ```
221
+
222
+ ## 高级功能
223
+
224
+ ### 批量操作优化
225
+
226
+ ```typescript
227
+ class BatchHistoryPlugin extends BasePlugin {
228
+ @inject.history.ref()
229
+ history!: IHistory
230
+
231
+ performBatchOperation() {
232
+ // 批量操作会自动被防抖处理
233
+ engine.dispatchNodeOperation({
234
+ type: 'add',
235
+ data: { id: 'node1', /* ... */ },
236
+ })
237
+
238
+ engine.dispatchNodeOperation({
239
+ type: 'add',
240
+ data: { id: 'node2', /* ... */ },
241
+ })
242
+
243
+ // 这些操作会被合并为一个历史记录条目
244
+ }
245
+ }
246
+ ```
247
+
248
+ ### 条件性历史记录
249
+
250
+ ```typescript
251
+ class ConditionalHistoryPlugin extends BasePlugin {
252
+ @inject.history.ref()
253
+ history!: IHistory
254
+
255
+ @inject.nodesManager()
256
+ nodesManager!: DataManager<Node>
257
+
258
+ performTemporaryOperation() {
259
+ // 检查是否在撤销/重做过程中
260
+ if (this.history.isUndoRedo) {
261
+ // 在撤销/重做过程中,避免创建新的历史记录
262
+ return
263
+ }
264
+
265
+ // 正常操作
266
+ this.nodesManager.dispatch({
267
+ type: 'update',
268
+ data: { id: 'node1', /* ... */ },
269
+ })
270
+ }
271
+ }
272
+ ```
273
+
274
+ ### 历史记录状态监听
275
+
276
+ ```typescript
277
+ class HistoryMonitorPlugin extends BasePlugin {
278
+ @inject.history.ref()
279
+ history!: IHistory
280
+
281
+ @subscribe.history.canUndo()
282
+ onCanUndoChange(canUndo: boolean) {
283
+ console.log('Can undo:', canUndo)
284
+ }
285
+
286
+ @subscribe.history.canRedo()
287
+ onCanRedoChange(canRedo: boolean) {
288
+ console.log('Can redo:', canRedo)
289
+ }
290
+
291
+ // 自定义历史记录监听
292
+ @OnInit
293
+ init() {
294
+ // 监听历史记录变化
295
+ this.history.onHistoryChange?.subscribe((state) => {
296
+ console.log('History changed:', state)
297
+ })
298
+ }
299
+ }
300
+ ```
301
+
302
+ ## 性能优化
303
+
304
+ ### 防抖配置
305
+
306
+ ```typescript
307
+ const engine = new Engine({
308
+ plugins: [History],
309
+ pluginConfig: {
310
+ history: {
311
+ debounceTime: 200, // 增加防抖时间以减少历史记录条目
312
+ },
313
+ },
314
+ })
315
+ ```
316
+
317
+ ### 历史记录限制
318
+
319
+ ```typescript
320
+ const engine = new Engine({
321
+ plugins: [History],
322
+ pluginConfig: {
323
+ history: {
324
+ maxHistory: 20, // 减少最大历史记录数量以节省内存
325
+ },
326
+ },
327
+ })
328
+ ```
329
+
330
+ ## 文件目录结构
331
+
332
+ ```
333
+ packages/plugins-history/
334
+ ├── src/
335
+ │ ├── history.ts # 主要实现文件
336
+ │ └── index.ts # 导出文件
337
+ ├── dist/ # 构建输出目录
338
+ ├── package.json # 包配置文件
339
+ ├── build.config.ts # 构建配置
340
+ ├── tsconfig.json # TypeScript 配置
341
+ ├── eslint.config.mjs # ESLint 配置
342
+ └── CHANGELOG.md # 更新日志
343
+ ```
344
+
345
+ ### 核心文件说明
346
+
347
+ - **history.ts**:包含 History 插件的主要实现,包括操作拦截、历史记录管理和状态标记功能
348
+ - **index.ts**:导出 History 类和相关类型定义
349
+
350
+ ## 最佳实践
351
+
352
+ ### 内存管理
353
+
354
+ 1. **合理设置历史记录数量**:根据应用需求设置合适的 `maxHistory` 值
355
+ 2. **及时清理标记**:不再需要的状态标记应及时清理
356
+ 3. **避免过度防抖**:过长的防抖时间可能影响用户体验
357
+
358
+ ### 用户体验
359
+
360
+ 1. **提供撤销/重做按钮**:在 UI 中提供清晰的撤销/重做操作入口
361
+ 2. **显示操作状态**:根据 `canUndo` 和 `canRedo` 状态禁用/启用按钮
362
+ 3. **键盘快捷键**:支持 `Ctrl+Z` 和 `Ctrl+Y` 快捷键
363
+
364
+ ### 开发调试
365
+
366
+ 1. **监听历史记录状态**:在开发阶段监听历史记录变化以便调试
367
+ 2. **使用状态标记**:在关键操作点设置状态标记,方便测试和调试
368
+
369
+ ## 注意事项
370
+
371
+ 1. **大量数据性能**:历史记录会占用内存,大量数据时注意性能优化
372
+ 2. **异步操作**:异步操作可能导致历史记录顺序混乱,需要特别注意
373
+ 3. **数据一致性**:确保撤销/重做操作不会破坏数据的一致性
374
+ 4. **循环引用**:避免在历史记录处理过程中创建循环引用
375
+
376
+ ## 许可证
377
+
378
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knotx/plugins-history",
3
- "version": "0.4.11",
3
+ "version": "0.4.13",
4
4
  "description": "History Plugin for Knotx",
5
5
  "author": "boenfu",
6
6
  "license": "MIT",
@@ -29,13 +29,13 @@
29
29
  ],
30
30
  "dependencies": {
31
31
  "rxjs": "^7.8.1",
32
- "@knotx/core": "0.4.11",
33
- "@knotx/decorators": "0.4.11"
32
+ "@knotx/core": "0.4.13",
33
+ "@knotx/decorators": "0.4.13"
34
34
  },
35
35
  "devDependencies": {
36
- "@knotx/build-config": "0.4.11",
37
- "@knotx/eslint-config": "0.4.11",
38
- "@knotx/typescript-config": "0.4.11"
36
+ "@knotx/build-config": "0.4.13",
37
+ "@knotx/eslint-config": "0.4.13",
38
+ "@knotx/typescript-config": "0.4.13"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "unbuild",