# Nomi 时间轴剪辑体验 PRD

日期：2026-05-23
状态：Draft / 已落地 P0「画布素材拖入时间轴」和 P1-1「drop ghost + 放置失败反馈」

## 1. 背景

用户在桌面版 Nomi 中发现：画布上生成出来的图片 / 视频 / 全景截图等素材，无法直观拖到下方时间轴。过去可能存在类似能力，但当前体验的入口太隐蔽，用户不知道从哪里拖、拖到哪里，也不清楚哪些素材可以进入时间轴。

Nomi 当前定位不是传统素材库 + 剪辑台，而是「生成画布 → 时间轴编排 → 成片预览 / 导出」的一体化工作流。因此时间轴需要具备基础剪辑软件的核心体验，同时保留 Nomi 的生成式素材优势：画布里任何有结果的生成素材，都应该能快速进入时间轴。

## 2. 技术栈与现状梳理

### 2.1 前端与桌面技术栈

- App：Electron + React + TypeScript + Vite。
- 状态：`src/workbench/workbenchStore.ts` 管理画布、时间轴、预览状态。
- 画布：`src/workbench/generationCanvas/*`。
- 时间轴：`src/workbench/timeline/*`。
- 成片 / 预览：`src/workbench/preview/*`。
- 导出：`src/workbench/export/*` + `electron/export/*`。

### 2.2 当前时间轴已有能力

当前代码已具备以下基础功能：

1. **素材进入时间轴**
   - 时间轴轨道支持接收 generation node drag payload。
   - `buildClipFromGenerationNode()` 可把画布节点转成 image/video clip。
   - image 默认 3 秒，video 默认按 durationSeconds 或 5 秒。

2. **轨道与片段**
   - 轨道类型：图片轨、视频轨。
   - 片段支持 start/end/frameCount、offsetStartFrame/offsetEndFrame。
   - 支持同轨碰撞检测，不能重叠放置。

3. **时间轴操作**
   - 点击轨道设置 playhead。
   - 片段可拖动移动。
   - 选中片段后可左右拖动边缘调整起止点。
   - 选中片段可删除、分割、复制、微调。
   - 快捷键：左右方向键移动 playhead、Delete/Backspace 删除、S 分割、Cmd/Ctrl+D 复制、Shift+< / Shift+> 微调。
   - 缩放：按钮缩放、重置缩放、Cmd/Ctrl + 滚轮缩放。

4. **成片预览 / 导出**
   - 预览页显示当前 playhead 下的图片 / 视频。
   - 支持播放、画幅切换、contain/cover、画面缩放/重置、拖拽调整构图、安全框。
   - 支持 MP4 导出，已有导出进度和失败 toast。

## 3. 开源剪辑产品参考

本轮参考了典型开源剪辑产品的基础能力：

- **OpenShot**：README 明确强调桌面 drag & drop、clip resizing / trimming / snapping / cutting，以及高级时间轴的 drag & drop、scrolling、panning、zooming、snapping。
- **Kdenlive**：基于 MLT Framework 的非线性剪辑工具，核心是多轨时间线、素材组织和剪辑操作一致性。
- **Shotcut**：跨平台 Qt + MLT 视频编辑器，强调可用的桌面端剪辑体验。
- **Olive**：开源非线性视频编辑器，面向传统 NLE 时间轴体验。
- **LosslessCut**：以快速切割 / trimming 为核心，说明基础剪辑里「切、修、定位、导出」的反馈必须直接明确。

对 Nomi 的启发：

1. 生成式产品也必须保留 NLE 的低门槛基础体验：拖入、剪切、修边、移动、缩放、撤销、导出。
2. Drag & drop 入口必须可见；不能只有隐藏的 HTML draggable 或隐蔽 hover 区。
3. 时间轴 drop 时要有明确反馈：可放置 / 不可放置、将落在哪一帧、落在哪条轨。
4. 修边 / 分割 / 复制 / 删除必须可发现，不只靠快捷键。
5. 成片页应围绕「预览是否正确、比例是否正确、能否导出、导出后去哪了」来设计。

## 4. 用户问题与体验缺口

### 4.1 P0：画布素材到时间轴不可发现

问题：

- 画布节点虽然已有拖拽代码，但入口隐藏、文案使用「时间线」而用户说的是「时间轴」，不够明确。
- 图像本身拖动用于移动画布节点，和「拖入时间轴」冲突。
- 用户无法判断哪些生成物可拖入。

本轮修复方向：

- 所有有 `result.url` 且非错误状态的生成节点，都显示右侧外置拖拽图标。
- 图标放在素材卡片右侧外边，避免和画布移动、节点连接点冲突。
- icon 使用 grip/handle 语义，表达「可抓取 / 可拖拽」。
- hover / focus 显示「拖拽到时间轴」。
- 拖拽时继续发送 `TIMELINE_GENERATION_NODE_DRAG_MIME`，落到时间轴后按素材类型生成 clip。
- 点击该按钮时也可直接添加到当前 playhead，作为鼠标拖拽之外的兜底。

### 4.2 P1：时间轴基础剪辑体验不足

当前已有能力，但缺少更完整的剪辑反馈：

- 拖入时间轴时没有落点预览线 / ghost clip。
- 碰撞失败时只是不插入，用户不知道为什么失败。
- 缺少吸附：playhead、片段边缘、秒刻度、轨道起点都应可 snap。
- 分割、复制、删除等按钮较小，选中片段后的操作不够解释性。
- 缺少「撤销 / 重做」的可见入口和操作历史反馈。
- 片段被选中后没有属性面板：时长、起始时间、结束时间、素材来源、适配方式等。
- 轨道只有图片 / 视频，暂不支持音频轨、文字轨、叠加层、多层画面。
- 时间轴空状态只写「从生成区拖入素材」，缺少示例动作和可点击引导。

### 4.3 P2：成片页面信息架构需要优化

当前成片页已经有播放、画幅、适配、缩放、安全框、导出，但体验上仍有问题：

- 工具条信息较挤，导出、画幅、适配、构图混在一起。
- 缺少导出前检查：时间轴为空、素材加载失败、视频无法解码、缺少音频等。
- MP4 导出没有清楚的导出设置面板：分辨率、帧率、质量、是否保留透明背景、是否包含音频。
- 构图调整是预览态，是否写回 clip / 项目不够明确。
- 成片页缺少「当前画面用了哪些轨道 / 哪个 clip」的信息。
- 导出完成后的结果页可以更明确：打开文件夹、再次导出、复制路径、继续编辑。

## 5. 目标

### 5.1 P0 目标

- 用户看到画布素材右侧的拖拽图标，就知道可以拖到时间轴。
- 图片、视频、全景截图、切图等所有画布生成素材，只要有可用结果 URL，都能进入时间轴。
- 如果用户不拖，也可以点击入口添加到当前 playhead。
- 入口和节点连线 handle、节点移动不冲突。

### 5.2 P1 目标

- 时间轴具备基础 NLE 的最小闭环：拖入、移动、修边、分割、复制、删除、缩放、吸附、撤销、预览。
- 每个操作都有可见反馈：ghost、snap line、错误提示、选中态、快捷键提示。
- 新用户不用看说明也能完成 3 个素材的顺序剪辑并导出。

### 5.3 P2 目标

- 成片页成为「最终检查 + 导出」页面，而不只是播放器。
- 用户能清楚知道画幅、时长、分辨率、导出格式、导出位置。
- 导出失败要有可理解兜底，导出成功要能直接找到文件。

## 6. 需求拆解

### P0：画布素材拖拽入口（本轮落地）

1. 新增 `timelineDragAffordance` 逻辑：
   - 判断节点是否可发送到时间轴。
   - 统一文案 `拖拽到时间轴`。
2. `BaseGenerationNode`：
   - 对可发送节点展示右侧外置 icon。
   - hover/focus tooltip 显示 `拖拽到时间轴`。
   - `onDragStart` 写入 generation node drag payload。
   - `onClick` 添加到当前 playhead。
3. 测试：
   - 覆盖 image/video/panorama 等有结果节点可拖。
   - 覆盖 error / readonly / empty node 不展示。
   - 覆盖用户文案。
   - 覆盖 image/video 可转 timeline clip。

验收标准：

- 在画布任意成功生成素材右侧可看到拖拽 icon。
- 鼠标 hover 上去能看到「拖拽到时间轴」。
- 拖动 icon 到图片轨 / 视频轨，能生成对应片段。
- 点击 icon，素材能添加到当前 playhead。
- 不影响画布节点移动、连接、重新生成、切图等现有操作。

### P1：时间轴基础剪辑优化

建议分阶段实现：

1. Drop 反馈
   - 拖拽进入轨道时显示 ghost clip。
   - 可放置为蓝/绿，不可放置为红。
   - 显示落点时间码。
   - Drop 失败 toast：例如「这里已有片段，试试拖到空白位置」。
2. Snap / 吸附
   - 拖动 / 修边吸附到 playhead、相邻 clip 边缘、整秒刻度。
   - 按住 Option/Alt 暂时关闭吸附。
3. Clip 操作条
   - 选中片段上方浮出操作条：分割、复制、删除、前后微调。
   - Tooltip 显示快捷键。
4. 属性面板
   - 显示片段名称、轨道类型、开始、结束、时长、素材来源。
   - 支持手动输入开始时间 / 时长。
5. 撤销重做
   - 时间轴编辑操作写入 history。
   - 显示 Undo / Redo 按钮和快捷键。
6. 轨道扩展
   - 后续支持文字轨、音频轨、多图片层叠轨。

### P2：成片页面优化

1. 信息架构
   - 顶部：作品信息（画幅、时长、帧率）。
   - 中间：预览画布。
   - 右侧或底部：导出设置。
   - 底部：时间轴。
2. 预览检查
   - 显示当前 playhead active clip 列表。
   - 素材加载失败时高亮对应 clip。
   - 支持一键跳转到有问题片段。
3. 导出设置
   - 格式：MP4 / WebM fallback。
   - 分辨率：720p / 1080p / 4K。
   - 质量：标准 / 高质量。
   - 帧率：默认跟项目 fps。
   - 音频：当前如果不支持，明确显示「暂不包含音频」。
4. 导出完成
   - 显示成功卡片：文件名、大小、位置、打开文件夹、再次导出。
   - 保留 toast，但不要只靠 toast。

## 7. 非目标

- 本阶段不做完整专业剪辑软件替代。
- 本阶段不做复杂转场、关键帧、调色、音频混音。
- 本阶段不引入重型视频引擎重构，先在现有 React/Electron 架构上补齐基础体验。

## 8. 风险与注意事项

- Canvas 节点本身也可拖动，必须避免与「拖到时间轴」冲突，所以入口需要独立 icon。
- Video clip 的 duration、offset、trim 需要保持和真实媒体时间一致。
- 时间轴状态更新要可撤销，否则剪辑误操作成本高。
- 导出所见即所得需要后续确保 preview transform 能写回 clip / export pipeline。
- 如果将来支持多层图片 / 视频，需要重新设计 track type 与 compositing 规则。

## 9. 推荐实施顺序

1. P0：拖拽入口可发现 + 点击添加兜底。（已落地）
2. P1-1：drop ghost + 放置失败 toast。（已落地）
3. P1-2：snap / 吸附。
4. P1-3：clip 操作条 + 快捷键提示。
5. P1-4：undo / redo。
6. P2-1：成片页导出设置区。
7. P2-2：导出完成卡片与问题片段检查。

## 10. 成功指标

- 新用户首次看到生成素材后，能在 5 秒内发现如何加入时间轴。
- 3 个素材编排成 9 秒视频的操作步数不超过：拖入 3 次 + 调整顺序 / 时长 + 导出。
- Drop 失败不再是静默失败，用户能知道原因。
- 导出完成后用户能直接找到文件。
- 基础剪辑功能不依赖用户安装额外工具或理解底层技术。
