概述
用户实测「整个剪辑套件都坏了」。逐项根因排查后,问题分两类:(A) 一个 IPC 序列化总根因(已在 #141 修复) + (B) 时间轴播放/预览逻辑需按上游 palmier-pro 1:1 重写 (本 issue 收口,串联既有重写 issue)。
A. 总根因:EditRequest 多词字段 camelCase 反序列化失败(已修复,留档)
现象 :删除片段、在播放头分割、Inspector 改属性、关键帧、Link/Unlink、SwapMedia、文件夹操作——点了完全没反应 。删除时 toast 暴露真因:
invalid args 'command' for command 'edit_apply': missing field 'clip_ids'
根因 :src-tauri/src/commands.rs 的 EditRequest 用 #[serde(tag="type", rename_all="camelCase")],但 serde 的 enum 级 rename_all 只改变体名、不改 struct 变体的字段名 (已实测确认)。于是所有多词字段 (clip_ids/clip_id/at_frame/track_index/offset_frames…)仍只认 snake_case,而前端发 camelCase → 整条命令反序列化失败、被 void 静默吞掉。只有单词字段 命令(AddClips/MoveClips/TrimClips)能用——这就是"拖入/移动/修剪正常,但删除/分割/Inspector 全失效"的原因。此 bug 在 main 上同样存在,长期被误诊为'选中没生效'。
修复(#141 2975dca) :给 EditRequest 每个 struct 变体加 #[serde(rename_all="camelCase")] + 回归测试。
B. 时间轴播放/预览逻辑需按上游重写(本 issue 主体)
当前预览是双渲染面拼接 (播放=DOM <video>/<audio> + 自写 rAF 时钟;暂停=GPU 合成帧 <img>),与上游 Preview/VideoEngine.swift 的**单一 AVPlayer+单一 AVPlayerLayer**模型本质不同,导致一系列顽疾:
时间轴播放暂停时抽搐 (单素材预览正常,但时间轴剪辑预览暂停会抽搐)。根因:暂停后残留 rAF tick 用已超前的音频 master 时间覆写播放头(集成:Codex 多功能分支 + 本轮编辑阻断修复(删除/拖入落点/蓝选/预览/菜单/递归导入) #141 加了 if(!isPlaying)return 守卫止血,但暂停逻辑需要完全重写 )。
拖动时间轴预览不及时 ——只有松手才出正确帧,拖动中不更新。根因:合成帧靠 140ms 防抖(逐帧合成会卡死整机,见 [CRITICAL][perf] 拖动/暂停 scrub 触发逐帧 ffmpeg+wgpu 合成,可致整机卡死 #92 )。应参照单素材视频预览(它本身没问题)做到实时 ,需流式 scrub 引擎。
整个时间轴播放逻辑都需对齐上游 (play/pause/seek/scrub 都应是"同一渲染面上的状态切换",显示帧恒等于播放位置)。
需重写/标记的文件
web/src/components/preview/TimelinePlaybackLayer.tsx(双面 rAF 时钟 + 音频 master)
web/src/components/preview/Preview.tsx(<img>/<video> 切换 + 合成帧防抖)
web/src/components/preview/useTimelineFrame.ts(逐帧合成请求)
web/src/components/preview/playbackClock.ts
上游权威参考 :palmier-pro-upstream/Sources/PalmierPro/Preview/VideoEngine.swift(play/pause/seek/addPeriodicTimeObserver/playbackStartFrame)、PreviewView.swift(单 AVPlayerLayer + CALayer 文字叠层)。
忠实复刻路线(等价物)
WebView 跑不了 AVFoundation,单面等价物 = 流式播放引擎 #53 :Rust 连续解码+合成 → MJPEG 回环(#64 )+ cpal 音频(#63 )→ 前端单 <canvas> ,让 play/pause/seek/scrub 变成单面状态切换,和上游一致。
关联已有 issue
给贡献者的统一要求
所有剪辑功能一律按复刻目标仓库 palmier-pro 1:1 复刻其交互与实现 ,不要自己发明。涉及播放/预览的 PR 请先读上游 VideoEngine.swift 对齐语义,再在单面(#53 路线)上实现。
cc @cuic19053-hue @H-Chris233
概述
用户实测「整个剪辑套件都坏了」。逐项根因排查后,问题分两类:(A) 一个 IPC 序列化总根因(已在 #141 修复) + (B) 时间轴播放/预览逻辑需按上游 palmier-pro 1:1 重写(本 issue 收口,串联既有重写 issue)。
A. 总根因:EditRequest 多词字段 camelCase 反序列化失败(已修复,留档)
现象:删除片段、在播放头分割、Inspector 改属性、关键帧、Link/Unlink、SwapMedia、文件夹操作——点了完全没反应。删除时 toast 暴露真因:
根因:
src-tauri/src/commands.rs的EditRequest用#[serde(tag="type", rename_all="camelCase")],但 serde 的 enum 级rename_all只改变体名、不改 struct 变体的字段名(已实测确认)。于是所有多词字段(clip_ids/clip_id/at_frame/track_index/offset_frames…)仍只认 snake_case,而前端发 camelCase → 整条命令反序列化失败、被void静默吞掉。只有单词字段命令(AddClips/MoveClips/TrimClips)能用——这就是"拖入/移动/修剪正常,但删除/分割/Inspector 全失效"的原因。此 bug 在 main 上同样存在,长期被误诊为'选中没生效'。修复(#141
2975dca):给EditRequest每个 struct 变体加#[serde(rename_all="camelCase")]+ 回归测试。B. 时间轴播放/预览逻辑需按上游重写(本 issue 主体)
当前预览是双渲染面拼接(播放=DOM
<video>/<audio>+ 自写 rAF 时钟;暂停=GPU 合成帧<img>),与上游Preview/VideoEngine.swift的**单一AVPlayer+单一AVPlayerLayer**模型本质不同,导致一系列顽疾:if(!isPlaying)return守卫止血,但暂停逻辑需要完全重写)。需重写/标记的文件
web/src/components/preview/TimelinePlaybackLayer.tsx(双面 rAF 时钟 + 音频 master)web/src/components/preview/Preview.tsx(<img>/<video>切换 + 合成帧防抖)web/src/components/preview/useTimelineFrame.ts(逐帧合成请求)web/src/components/preview/playbackClock.tspalmier-pro-upstream/Sources/PalmierPro/Preview/VideoEngine.swift(play/pause/seek/addPeriodicTimeObserver/playbackStartFrame)、PreviewView.swift(单 AVPlayerLayer + CALayer 文字叠层)。忠实复刻路线(等价物)
WebView 跑不了 AVFoundation,单面等价物 = 流式播放引擎 #53:Rust 连续解码+合成 → MJPEG 回环(#64)+ cpal 音频(#63)→ 前端单
<canvas>,让 play/pause/seek/scrub 变成单面状态切换,和上游一致。关联已有 issue
给贡献者的统一要求
所有剪辑功能一律按复刻目标仓库
palmier-pro1:1 复刻其交互与实现,不要自己发明。涉及播放/预览的 PR 请先读上游VideoEngine.swift对齐语义,再在单面(#53 路线)上实现。cc @cuic19053-hue @H-Chris233