# Left Toolbar Remote Commands

本文档说明如何在“工具条端与白板端隔离”的场景下，通过命令控制白板左侧工具能力。

## 适用场景

- 工具条和白板不在同一个组件中
- 通过 `WebSocket` / `postMessage` / 其他消息通道传 JSON
- 白板端收到命令后执行，不直接暴露 UI

## 白板端执行入口

白板端拿到 `excalidrawAPI` 后，统一调用：

```ts
excalidrawAPI.leftToolbar.dispatch(command);
```

其中 `command` 的类型是 `LeftToolbarCommand`。

## Command 总览

| `type` | 作用 | `payload` |
| --- | --- | --- |
| `selectSelection` | 切换到“选择”工具 | 无 |
| `selectFreedraw` | 切换到“自由书写”工具 | 可选 |
| `selectLine` | 切换到“线条/箭头”工具组 | 可选 |
| `selectShape` | 切换到“形状”工具组 | 可选 |
| `selectText` | 切换到“文字”工具 | 可选 |
| `selectEraser` | 切换到“橡皮”工具 | 无 |
| `updateStyle` | 仅更新样式，不切换工具 | 必填 |

---

## 1) `selectSelection`

切换到选择工具。

```json
{ "type": "selectSelection" }
```

---

## 2) `selectFreedraw`

切换到自由书写工具（普通画笔/荧光笔）。

### payload 字段

| 字段 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| `variant` | `"pen" \| "highlighter"` | 否 | 画笔类型。`pen` 普通笔，`highlighter` 荧光笔（半透明） |
| `strokeWidth` | `number` | 否 | 线宽 |
| `strokeColor` | `string` | 否 | 笔颜色（如 `#1677ff`） |

### `variant` 取值对照（freedraw）

| 值 | 含义 |
| --- | --- |
| `pen` | 普通画笔（不透明） |
| `highlighter` | 荧光笔（半透明，适合标注） |

### 示例

```json
{
  "type": "selectFreedraw",
  "payload": {
    "variant": "highlighter",
    "strokeWidth": 6,
    "strokeColor": "#ffcc00"
  }
}
```

---

## 3) `selectLine`

切换到线条/箭头工具组，并可指定箭头类型。

### payload 字段

| 字段 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| `variant` | `"line" \| "arrow" \| "doubleArrow" \| "outlineArrow" \| "outlineDoubleArrow"` | 否 | 线条变体：直线、单箭头、双箭头、空心单箭头、空心双箭头 |
| `strokeWidth` | `number` | 否 | 线宽 |
| `strokeColor` | `string` | 否 | 线条颜色 |

### `variant` 取值对照（line）

| 值 | 含义 |
| --- | --- |
| `line` | 直线 |
| `arrow` | 实心单箭头 |
| `doubleArrow` | 实心双箭头 |
| `outlineArrow` | 空心单箭头 |
| `outlineDoubleArrow` | 空心双箭头 |

### 示例

```json
{
  "type": "selectLine",
  "payload": {
    "variant": "outlineDoubleArrow",
    "strokeWidth": 2,
    "strokeColor": "#1677ff"
  }
}
```

---

## 4) `selectShape`

切换到形状工具组，并可指定具体形状。

### payload 字段

| 字段 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| `shape` | `"rectangle" \| "diamond" \| "ellipse" \| "triangle" \| "pentagon" \| "callout" \| "hollow_arrow" \| "hollow_double_arrow"` | 否 | 具体形状。不传时默认矩形 |
| `strokeWidth` | `number` | 否 | 描边宽度 |
| `strokeColor` | `string` | 否 | 描边颜色 |
| `backgroundColor` | `string` | 否 | 填充背景色 |
| `fillStyle` | `AppState["currentItemFillStyle"]` | 否 | 填充样式（如 `solid` / `hachure` / `cross-hatch`） |

### `shape` 取值对照

| 值 | 对应图形 |
| --- | --- |
| `rectangle` | 矩形 |
| `diamond` | 菱形 |
| `ellipse` | 椭圆 |
| `triangle` | 三角形 |
| `pentagon` | 五边形 |
| `callout` | 标注框（气泡） |
| `hollow_arrow` | 空心单箭头形状 |
| `hollow_double_arrow` | 空心双箭头形状 |

### `fillStyle` 常见值对照

| 值 | 含义 |
| --- | --- |
| `hachure` | 斜线填充 |
| `cross-hatch` | 交叉线填充 |
| `solid` | 实心填充 |

### 示例

```json
{
  "type": "selectShape",
  "payload": {
    "shape": "ellipse",
    "strokeWidth": 3,
    "strokeColor": "#1f1f1f",
    "backgroundColor": "#E6F4FF",
    "fillStyle": "solid"
  }
}
```

---

## 5) `selectText`

切换到文字工具，并可设置文字默认样式。

### payload 字段

| 字段 | 类型 | 必填 | 含义 |
| --- | --- | --- | --- |
| `fontSize` | `number` | 否 | 字号 |
| `textAlign` | `"left" \| "center" \| "right"` | 否 | 对齐方式 |
| `strokeColor` | `string` | 否 | 文字颜色 |
| `bold` | `boolean` | 否 | 是否粗体 |
| `italic` | `boolean` | 否 | 是否斜体 |
| `underline` | `boolean` | 否 | 是否下划线 |
| `strikethrough` | `boolean` | 否 | 是否删除线 |

### `textAlign` 取值对照

| 值 | 含义 |
| --- | --- |
| `left` | 左对齐 |
| `center` | 居中对齐 |
| `right` | 右对齐 |

### 示例

```json
{
  "type": "selectText",
  "payload": {
    "fontSize": 24,
    "textAlign": "center",
    "strokeColor": "#222222",
    "bold": true
  }
}
```

---

## 6) `selectEraser`

切换到橡皮工具。

```json
{ "type": "selectEraser" }
```

---

## 7) `updateStyle`

只更新样式，不切换当前工具。

### payload 字段（可选子集）

可以传递以下字段中的任意组合：

- `currentItemStrokeColor`
- `currentItemBackgroundColor`
- `currentItemFillStyle`
- `currentItemStrokeWidth`
- `currentItemOpacity`
- `currentItemStartArrowhead`
- `currentItemEndArrowhead`
- `currentItemFontSize`
- `currentItemTextAlign`
- `currentTextBold`
- `currentTextItalic`
- `currentTextUnderline`
- `currentTextStrikethrough`

### `updateStyle` 字段含义对照

| 字段 | 含义 |
| --- | --- |
| `currentItemStrokeColor` | 当前工具的描边颜色 |
| `currentItemBackgroundColor` | 当前工具的填充背景色 |
| `currentItemFillStyle` | 当前工具的填充样式 |
| `currentItemStrokeWidth` | 当前工具线宽 |
| `currentItemOpacity` | 当前工具透明度（0-100） |
| `currentItemStartArrowhead` | 箭头起点箭头样式 |
| `currentItemEndArrowhead` | 箭头终点箭头样式 |
| `currentItemFontSize` | 文字默认字号 |
| `currentItemTextAlign` | 文字默认对齐方式 |
| `currentTextBold` | 文字是否粗体 |
| `currentTextItalic` | 文字是否斜体 |
| `currentTextUnderline` | 文字是否下划线 |
| `currentTextStrikethrough` | 文字是否删除线 |

### 示例

```json
{
  "type": "updateStyle",
  "payload": {
    "currentItemStrokeWidth": 4,
    "currentItemStrokeColor": "#ff4d4f"
  }
}
```

---

## 接入示例（白板端）

```ts
socket.on("toolbar-command", (command) => {
  excalidrawAPI?.leftToolbar.dispatch(command);
});
```

## 完整命令 JSON 示例集合

以下示例可直接用于联调（通过 socket 发送）：

### 1) 选择工具

```json
{ "type": "selectSelection" }
```

### 2) 自由书写（普通画笔）

```json
{
  "type": "selectFreedraw",
  "payload": {
    "variant": "pen",
    "strokeWidth": 2,
    "strokeColor": "#1677ff"
  }
}
```

### 3) 自由书写（荧光笔）

```json
{
  "type": "selectFreedraw",
  "payload": {
    "variant": "highlighter",
    "strokeWidth": 6,
    "strokeColor": "#ffcc00"
  }
}
```

### 4) 线条（空心双箭头）

```json
{
  "type": "selectLine",
  "payload": {
    "variant": "outlineDoubleArrow",
    "strokeWidth": 2,
    "strokeColor": "#1f1f1f"
  }
}
```

### 5) 形状（椭圆 + 实心填充）

```json
{
  "type": "selectShape",
  "payload": {
    "shape": "ellipse",
    "strokeWidth": 3,
    "strokeColor": "#1f1f1f",
    "backgroundColor": "#E6F4FF",
    "fillStyle": "solid"
  }
}
```

### 6) 文字（24号，居中，加粗）

```json
{
  "type": "selectText",
  "payload": {
    "fontSize": 24,
    "textAlign": "center",
    "strokeColor": "#222222",
    "bold": true
  }
}
```

### 7) 橡皮

```json
{ "type": "selectEraser" }
```

### 8) 仅更新样式（不切工具）

```json
{
  "type": "updateStyle",
  "payload": {
    "currentItemStrokeWidth": 4,
    "currentItemStrokeColor": "#ff4d4f"
  }
}
```

## 注意事项

- 命令建议使用 JSON 格式，便于跨端传输和记录日志
- 白板端建议做一次类型校验后再 `dispatch`
- 建议在“进入注释模式”时再接收命令，退出时解绑监听
