> **本文回答**：操作描述 = 给撤销组附加一段文本，会显示在 撤销 / 重做 按钮 tooltip、历史下拉、命令行消息上。三种传值方式：① 字符串简写 `'修改颜色'`；② 完整 `UndoDescriptor` `{ category, i18nKey?, resolvedText?, sourceCommand? }`；③ 不传 → 在命令上下文里自动从 `Engine.editor.currentCommandName` 推导。监听 `CadEvents.UndoStackChanged` 拿"下一步撤销/重做"的标签。

## 一、`UndoDescriptor` 结构

```ts
interface UndoDescriptor {
  i18nKey?: string;                              // 国际化 key
  params?: Record<string, string | number>;     // i18n 参数（预留）
  resolvedText?: string;                         // 已解析文本（最高优先级）
  category: 'edit' | 'view' | 'system';          // 必填，分类
  sourceCommand?: string;                        // 来源命令名
}

type UndoCategory = 'edit' | 'view' | 'system';
```

MCP 参考：`vjcad/doc/undo/undo-redo.md`。

## 二、三种传值

```js
// 方式 1：字符串简写 = { category: 'edit', resolvedText: str }
Engine.startUndoRecord('修改颜色');

// 方式 2：i18n key（推荐多语言）
Engine.startUndoRecord({
  category: 'edit',
  i18nKey: 'undo.modify.color'
});

// 方式 3：明确来源命令
Engine.startUndoRecord({
  category: 'edit',
  sourceCommand: 'MYCMD'
});

// 方式 4：在命令上下文里不传 → 自动推导
class MyCommand {
  async main() {
    Engine.startUndoRecord();    // 自动 = 命令的 description / name
    try { /* ... */ } finally { Engine.endUndoRecord(); }
  }
}
```

MCP 参考：`vjcad/doc/undo/undo-redo.md`、`vjcad/example/undo/04description.js`。

## 三、解析优先级

显示 label 时按以下顺序查找：

1. `resolvedText` — 直接使用
2. `i18nKey` — 通过 `t()` 翻译
3. `sourceCommand` — 命令名兜底
4. 通用文案 — "撤销" / "重做"

MCP 参考：`vjcad/doc/undo/undo-redo.md`。

## 四、自动推导（命令上下文）

不传 description 时，系统按以下顺序推导：

1. `Engine.editor.currentCommandName`
2. 从 `CommandRegistry` 查找命令定义
3. 用 `CommandDefinition.description` 或 `CommandDefinition.name` 当 `resolvedText`

适用：通过 `editor.executerWithOp()` 执行的命令（Editor 上是 `executerWithOp`/`invokeCommandWithOp`——`executeCommand` 是 `PluginContext` 的方法，不是 Editor 的）；不适用：属性面板、对话框等非命令上下文（必须显式传 description）。

MCP 参考：`vjcad/doc/undo/undo-redo.md`。

## 五、查询撤销 / 重做历史

```js
const u = Engine.undoManager;

const undoHistory = u.getUndoHistory();
console.log('共有', undoHistory.length, '步可撤销');

undoHistory.forEach((item, i) => {
  const desc = item.description;
  const label = desc?.resolvedText || desc?.i18nKey || '(无描述)';
  console.log(`${i + 1}. ${label} 分组=${item.isGroup}`);
});

// 重做历史
const redoHistory = u.getRedoHistory();

// 步数
const undoCount = u.getUndoCount();
const redoCount = u.getRedoCount();

// 最后一次撤销 / 重做的描述
console.log(u.lastUndoDescription);
console.log(u.lastRedoDescription);
```

`UndoHistoryItem` 结构：

```ts
interface UndoHistoryItem {
  description?: UndoDescriptor;
  index: number;        // 在历史栈中的索引
  isGroup: boolean;     // 是否为分组操作
}
```

MCP 参考：`vjcad/doc/undo/undo-redo.md`、`vjcad/example/undo/04description.js`。

## 六、按名称撤销 / 重做

```js
u.undoSteps('绘制直线');       // 撤销到最近一次匹配（含），无匹配则不执行
u.redoSteps('绘制圆');        // 同理
u.undoSteps('不存在的');       // 返回 0，不执行
u.undoSteps(3, { silent: true });   // 静默：只抑制逐步的 UndoStackChanged（结束仍派发一次）；UndoManager 本就不写命令行
```

MCP 参考：`vjcad/example/undo/04description.js`。

## 七、监听撤销栈变更（UI 必备）

```js
import { CadEventManager, CadEvents } from 'vjcad';

CadEventManager.getInstance().on(CadEvents.UndoStackChanged, (args) => {
  console.log('撤销步数:', args.undoCount);
  console.log('重做步数:', args.redoCount);
  console.log('下一步撤销:', args.nextUndoLabel);
  console.log('下一步重做:', args.nextRedoLabel);
});
```

事件参数：

```ts
interface UndoStackChangedEventArgs {
  document: any;
  undoCount: number;
  redoCount: number;
  nextUndoLabel?: string;     // 已翻译
  nextRedoLabel?: string;     // 已翻译
}
```

MCP 参考：`vjcad/doc/undo/undo-redo.md`、`vjcad/example/undo/04description.js`。

## 八、操作分类（`category`）

| 值 | 用途 | UI 展示 |
|---|---|---|
| `edit` | 编辑操作（绘图、改属性） | 历史面板主列表 |
| `view` | 视图操作（缩放、平移、UCS） | 历史面板折叠组 |
| `system` | 系统操作（预留） | — |

MCP 参考：`vjcad/doc/undo/undo-redo.md`。

## 反例

- 不传 description 但又不在命令上下文（属性面板、对话框）：UI 显示 "撤销" 兜底文案，看不出做了什么。
- 用 `i18nKey` 但 i18n 字典里没注册：fallback 显示 key 本身（不友好）。
- 把不可撤销的视图操作也挂 `category: 'edit'`：会污染编辑历史面板。

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/undo/undo-redo.md`
- 示例：`vjcad/example/undo/04description.js`
