npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@knotx/plugins-history

v0.5.10

Published

History Plugin for Knotx

Readme

@knotx/plugins-history

历史记录插件,为 KnotX 提供撤销/重做功能。

安装

npm install @knotx/plugins-history

概述

History 插件为 KnotX 提供了完整的历史记录管理功能,包括撤销、重做、标记状态等。该插件能够跟踪所有数据变化,并提供便捷的历史记录操作接口。

实现原理

History 插件的核心实现原理:

  1. 操作拦截:拦截所有数据管理器的操作,记录变化前后的状态
  2. 缓冲机制:使用防抖机制批量处理操作,避免频繁的历史记录创建
  3. 反向操作:为每个操作生成反向操作,实现撤销功能
  4. 状态标记:支持标记特定状态,快速回到指定的历史点

依赖关系

核心依赖

  • @knotx/core:提供基础插件架构和数据管理
  • @knotx/decorators:提供装饰器支持
  • rxjs:提供响应式编程支持

自动依赖

  • @knotx/core 中的 nodesManageredgesManager:自动注册到历史记录系统

API 文档

主要类

History

历史记录插件的主要类,继承自 BasePlugin

export class History<T extends Record<string, IData> = Record<string, any>> extends BasePlugin<'history', HistoryConfig> {
  name = 'history' as const
}

配置选项

HistoryConfig

export interface HistoryConfig {
  /** 最大历史记录数量 */
  maxHistory?: number
  /** 防抖时间(毫秒) */
  debounceTime?: number
}

插件数据 (PluginData)

History 插件提供以下数据:

interface PluginData {
  history: {
    ref: IHistory
    canUndo: boolean
    canRedo: boolean
    registerDataManager: (dataManager: DataManager<any>) => (() => void)
  }
}

接口定义

IHistory

export interface IHistory {
  isUndoRedo: boolean
  undo: () => void
  redo: () => void
  addTag: (tag: string) => void
  removeTag: (tag: string) => void
  toTag: (tag: string) => void
  clearTags: () => void
}

HistoryState

export interface HistoryState<T extends Record<string, IData> = Record<string, any>> {
  value: {
    [key: string]: HistoryOperation<T>[]
  }
  timestamp: number
}

使用示例

基本用法

import { History } from '@knotx/plugins-history'

const engine = new Engine({
  plugins: [History],
  pluginConfig: {
    history: {
      maxHistory: 50,
      debounceTime: 100,
    },
  },
})

撤销和重做操作

const engine = new Engine({
  plugins: [History],
})

const historyData = engine.getPluginData('history')

// 撤销操作
if (historyData.canUndo) {
  historyData.ref.undo()
}

// 重做操作
if (historyData.canRedo) {
  historyData.ref.redo()
}

监听历史记录状态

class HistoryUIPlugin extends BasePlugin {
  @inject.history.canUndo()
  canUndo!: boolean

  @inject.history.canRedo()
  canRedo!: boolean

  @inject.history.ref()
  history!: IHistory

  @panel('bottom')
  render() {
    return (
      <div>
        <button
          disabled={!this.canUndo}
          onClick={() => this.history.undo()}
        >
          撤销
        </button>
        <button
          disabled={!this.canRedo}
          onClick={() => this.history.redo()}
        >
          重做
        </button>
      </div>
    )
  }
}

状态标记功能

const engine = new Engine({
  plugins: [History],
})

const historyData = engine.getPluginData('history')

// 标记当前状态
historyData.ref.addTag('checkpoint-1')

// 在进行一系列操作后...
// 回到标记的状态
historyData.ref.toTag('checkpoint-1')

// 移除标记
historyData.ref.removeTag('checkpoint-1')

// 清空所有标记
historyData.ref.clearTags()

自定义数据管理器集成

class CustomDataManager extends DataManager<MyData> {
  constructor() {
    super('custom')
  }
}

const engine = new Engine({
  plugins: [History],
})

const historyData = engine.getPluginData('history')
const customManager = new CustomDataManager()

// 注册自定义数据管理器
const unregister = historyData.registerDataManager(customManager)

// 取消注册
unregister()

高级功能

批量操作优化

class BatchHistoryPlugin extends BasePlugin {
  @inject.history.ref()
  history!: IHistory

  performBatchOperation() {
    // 批量操作会自动被防抖处理
    engine.dispatchNodeOperation({
      type: 'add',
      data: { id: 'node1', /* ... */ },
    })

    engine.dispatchNodeOperation({
      type: 'add',
      data: { id: 'node2', /* ... */ },
    })

    // 这些操作会被合并为一个历史记录条目
  }
}

条件性历史记录

class ConditionalHistoryPlugin extends BasePlugin {
  @inject.history.ref()
  history!: IHistory

  @inject.nodesManager()
  nodesManager!: DataManager<Node>

  performTemporaryOperation() {
    // 检查是否在撤销/重做过程中
    if (this.history.isUndoRedo) {
      // 在撤销/重做过程中,避免创建新的历史记录
      return
    }

    // 正常操作
    this.nodesManager.dispatch({
      type: 'update',
      data: { id: 'node1', /* ... */ },
    })
  }
}

历史记录状态监听

class HistoryMonitorPlugin extends BasePlugin {
  @inject.history.ref()
  history!: IHistory

  @subscribe.history.canUndo()
  onCanUndoChange(canUndo: boolean) {
    console.log('Can undo:', canUndo)
  }

  @subscribe.history.canRedo()
  onCanRedoChange(canRedo: boolean) {
    console.log('Can redo:', canRedo)
  }

  // 自定义历史记录监听
  @OnInit
  init() {
    // 监听历史记录变化
    this.history.onHistoryChange?.subscribe((state) => {
      console.log('History changed:', state)
    })
  }
}

性能优化

防抖配置

const engine = new Engine({
  plugins: [History],
  pluginConfig: {
    history: {
      debounceTime: 200, // 增加防抖时间以减少历史记录条目
    },
  },
})

历史记录限制

const engine = new Engine({
  plugins: [History],
  pluginConfig: {
    history: {
      maxHistory: 20, // 减少最大历史记录数量以节省内存
    },
  },
})

文件目录结构

packages/plugins-history/
├── src/
│   ├── history.ts              # 主要实现文件
│   └── index.ts               # 导出文件
├── dist/                      # 构建输出目录
├── package.json              # 包配置文件
├── build.config.ts           # 构建配置
├── tsconfig.json             # TypeScript 配置
├── eslint.config.mjs         # ESLint 配置
└── CHANGELOG.md              # 更新日志

核心文件说明

  • history.ts:包含 History 插件的主要实现,包括操作拦截、历史记录管理和状态标记功能
  • index.ts:导出 History 类和相关类型定义

最佳实践

内存管理

  1. 合理设置历史记录数量:根据应用需求设置合适的 maxHistory
  2. 及时清理标记:不再需要的状态标记应及时清理
  3. 避免过度防抖:过长的防抖时间可能影响用户体验

用户体验

  1. 提供撤销/重做按钮:在 UI 中提供清晰的撤销/重做操作入口
  2. 显示操作状态:根据 canUndocanRedo 状态禁用/启用按钮
  3. 键盘快捷键:支持 Ctrl+ZCtrl+Y 快捷键

开发调试

  1. 监听历史记录状态:在开发阶段监听历史记录变化以便调试
  2. 使用状态标记:在关键操作点设置状态标记,方便测试和调试

注意事项

  1. 大量数据性能:历史记录会占用内存,大量数据时注意性能优化
  2. 异步操作:异步操作可能导致历史记录顺序混乱,需要特别注意
  3. 数据一致性:确保撤销/重做操作不会破坏数据的一致性
  4. 循环引用:避免在历史记录处理过程中创建循环引用

许可证

MIT