Skip to content

Inspector 编辑:动画值可编辑 + Transform Reset(1:1 上游)#173

Closed
appergb wants to merge 3 commits into
feat/drag-swap-and-exportfrom
feat/inspector-keyframe-editing
Closed

Inspector 编辑:动画值可编辑 + Transform Reset(1:1 上游)#173
appergb wants to merge 3 commits into
feat/drag-swap-and-exportfrom
feat/inspector-keyframe-editing

Conversation

@appergb

@appergb appergb commented Jul 1, 2026

Copy link
Copy Markdown
Owner

概要

Inspector 剪辑编辑的两块上游对齐能力,共 3 提交。栈在 #172 之上(base 暂设为 feat/drag-swap-and-export)——#172 合并后 rebase 到 main。

变更

  • UpsertKeyframe 命令1c7c7bd):新增 EditCommand::UpsertKeyframe { clip_id, property, frame, value } + KeyframeValue{Scalar,Pair,Crop},镜像上游 write<Property> 的 upsert 分支(属性已有关键帧轨道时在播放头按显式值 upsert,而非只读)。含 IPC DTO(camelCase)+ TS wrapper。区别于 StampKeyframe(压采样值)与 SetClipProperties(清轨)。
  • Inspector 动画值可编辑225410c):7 个属性(音量/缩放/旋转/不透明度/位置 X·Y/裁剪四边)从只读改为可编辑,编辑即 upsertKeyframe。关键修正:seed 取原始轨道采样liveVolumeKfLinearAt/rawOpacityAt)而非有效合成值(volumeAt/opacityAt)——对齐上游 InspectorView(liveVolumeKfDb/rawOpacityAt,后者注释明写 "without the fade envelope"),否则编辑会把静态增益/淡变烘进关键帧。
  • Transform Reset 按钮61f420e):EditCommand::ResetTransform { clip_ids }——1:1 复刻上游 transformHeader.onReset:transform→identity、opacity→1、清 opacity/position/scale/rotation 四轨道、fadeIn/Out→0、插值→linear,crop 不动。RotateCcw 按钮 + i18n。

测试

  • Rust:opentake-ops apply 测试(UpsertKeyframe 6 + ResetTransform 6,含类型不匹配/越界/no-op-when-default 报 unchanged);opentake-tauri serde camelCase 往返测试(守 IPC 边界)。
  • Web:keyframeValue 11 单测 + clip.ts 3 回归测试(证明 seed 排除增益/淡变)。
  • 本地全绿:cargo fmt --check / clippy -D warnings / cargo testpnpm build / pnpm test(240)。

备注

栈式 PR。#172 合并后 git rebase --onto main <#172 tip> feat/inspector-keyframe-editing 落到干净 main 基线,base 改回 main。

baiqing added 3 commits July 1, 2026 14:49
…playhead

Mirror upstream write<Property>'s upsert branch: when a property already has a
keyframe track, editing its value should upsert a keyframe at the playhead —
not fall back to read-only (today's Inspector gap). Distinct from StampKeyframe
(samples the current value) and SetClipProperties (clears tracks).

- command.rs: EditCommand::UpsertKeyframe { clip_id, property, frame, value }
  + KeyframeValue{Scalar,Pair,Crop}; validates the property-value pairing
  (Opacity/Volume/Rotation=Scalar, Position/Scale=Pair, Crop=Crop; Volume in
  dB) then upserts at the clip-relative offset. 6 apply tests.
- commands.rs: IPC DTO KeyframeValueDto (tagged "kind", camelCase) -> KeyframeValue;
  3 raw-JSON camelCase serde round-trip tests guarding the IPC boundary.
- types.ts / editActions.ts: KeyframeValueReq + upsertKeyframe() wrapper.

Step A of the Inspector animation-editing cluster; frontend wiring (step B) follows.
… the playhead

Close the T2-5 gap: when a clip property already had a keyframe track, its
Inspector field was rendered READ-ONLY. Upstream keeps it editable and upserts a
keyframe at the playhead on edit (writeVolume/writeScale/writePosition/...). Wire
the 7 animated fields (Volume, Scale, Rotation, Opacity, Position X/Y, Crop edges)
to the existing upsertKeyframe() through a pure value-mapping helper.

- keyframeValue.ts: pure (field value, sampled state) -> KeyframeValueReq per
  property (Opacity/Rotation/Volume=scalar, Scale/Position=AnimPair {a,b},
  Crop=crop; Scale reuses resizeTransformKeepingSourceAspect so height derives
  identically to the static path). 11 unit tests.
- Inspector.tsx: each animated field is now an editable ScrubbableNumberField
  (seeded from the sampled-at-playhead value) whose onCommit branches
  animated -> upsertKeyframe / static -> the existing commit. AnimatedHint kept;
  the now-dead ReadOnlyValue removed.
- clip.ts: seed animated Volume/Opacity from the RAW track sample
  (liveVolumeKfLinearAt / rawOpacityAt), NOT the composited volumeAt/opacityAt —
  matching upstream InspectorView (liveVolumeKfDb / rawOpacityAt) so editing
  upserts the authored value instead of baking in the static gain / fade
  envelope. 3 regression tests.

Frontend only; the UpsertKeyframe command/IPC/wrapper already landed in 1c7c7bd.
Verified: pnpm build (tsc) clean; pnpm test 240 passed (+14 new).
…ades (upstream parity)

Port upstream InspectorView.transformHeader's onReset as a 1:1 replica of its
exact field set: reset the selected clip's transform to identity, opacity to 1,
clear the opacity/position/scale/rotation keyframe tracks, and zero both fades
(interpolation back to linear). Crop is untouched (a separate Inspector section).

- command.rs: EditCommand::ResetTransform { clip_ids } — a dedicated command so
  the whole reset is one atomic undo step (mirrors SetColorGrade/SetEffects shape
  and reuses set_clip_effect_field for up-front id validation), NOT a chain of
  SetClipProperties + SetKeyframes which would break atomicity. 6 tests: clears
  exactly the four tracks, crop survives, other clips untouched, undoable,
  missing/empty-ids error with no change, and no-op-when-already-default reports
  unchanged.
- commands.rs: camelCase EditRequest::ResetTransform { clipIds } DTO + serde
  round-trip test guarding the IPC boundary.
- types.ts / editActions.ts: resetTransform(clipIds) action (empty-guarded).
- Inspector.tsx: RotateCcw HoverButton in the Transform section header,
  right-aligned to match upstream's arrow.counterclockwise placement.
- dict.ts: inspector.action.resetTransform (zh/en).

Verified: cargo fmt/clippy -D warnings clean; ops reset tests 6/6 + tauri serde
test pass; pnpm build clean; pnpm test 240 passed.
@appergb appergb deleted the branch feat/drag-swap-and-export July 2, 2026 00:19
@appergb appergb closed this Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant