Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions apps/studio/src/ClipPicture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState } from "preact/hooks";
import { clipLayersOf, layersAt, type Layer } from "@openclapper/vocab";
import { fetchLayer } from "./api";
import { Composition, useShotLayers } from "./compose/stack";
import { stepBake } from "./format";
import { stepBake, stepStale } from "./format";
import { Framed, type MaskMode } from "./Framed";
import { presentationFrame } from "./FramingSelect";
import type { ResolvedClip } from "./TimelineParts";
Expand All @@ -28,12 +28,15 @@ export function useClipLayers(own: readonly Layer[]): Layer[] {

/**
* A photo clip's picture (epic #275): live on the stage and in the clip panel (the step's layers
* from the shot, then the clip's own), its step's bake in the strip (a layers glyph says it has
* its own on top).
* from the shot, then the clip's own). The strip uses the step's bake when that shows everything,
* and composes live when the clip has its own layers or the bake is not made yet, so a thumb never
* drops what the stage shows.
*/
export function ClipPicture({ c, mode, maxHeight, live }: { c: PhotoClip; mode: MaskMode; maxHeight?: string; live?: boolean }) {
const frame = presentationFrame(c.photo, c.pres);
if (!live) return <Framed photo={c.photo} mode={mode} frame={frame} vignette={presentationVignette(c.photo, c.pres)} src={stepBake(c.shot, c.step)?.url} maxHeight={maxHeight} />;
const bake = stepBake(c.shot, c.step);
const needsLive = live || c.ownLayers.length > 0 || (!bake && stepStale(c.shot, c.step));
if (!needsLive) return <Framed photo={c.photo} mode={mode} frame={frame} vignette={presentationVignette(c.photo, c.pres)} src={bake?.url} maxHeight={maxHeight} />;
return <LivePicture c={c} mode={mode} maxHeight={maxHeight} />;
}

Expand Down
2 changes: 1 addition & 1 deletion docs/features/timeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ On the desktop Timeline is one window (`apps/TimelineApp.tsx`) for all of the pr
- **Head**: name, total length, the **Presentation lock** ("Per clip" or one mode for all), **Review**: "Approve all · n" while shots in the cut are unreviewed (approves those), else "Back to review · n" for the approved ones (archived shots are left alone; one bulk request, Undo in the toast), save status, menu (Rename…, Delete timeline…). Changes **autosave** 600 ms after the last edit and at once when leaving the page; the server's answer replaces the clip list, so clips of deleted photos disappear on the next save.
- **Preview**: the selected clip, large, in its presentation, composed live (its step's layers, then its own; a sketch clip's sketch on white). The stage keeps **one height** for every clip (`--tl-stage-h`): shots, sketches and placeholders letterbox into it, so the layout never jumps. The grip under the stage **resizes** it (remembered per browser in `localStorage["timelineStageH"]`; double-click goes back to the default, which fills the window). Transport: first · previous · **Play/Pause** (`Space`) · next · last, the playhead clock over the total, "clip n of m · 3.0 s", **Placeholder** (`P`) and **Shots** (`N`: the browser ⇄ the clip panel).
Playback steps through the clips at their hold times (100 ms ticks) and stops at the end; Play at the end starts over.
- **Strip**: one block per clip, its width proportional to the hold time (44 px a second by default; zoomed out a clip keeps at least 20–64 px), with a thumbnail (its step's bake; a photo clip's carries its shot's state marker), the shot's title (and its place in the block for sequences; a layers icon marks a clip with layers of its own) and the hold time. Placeholders are dashed. A ruler labels the time (the spacing adapts to the zoom); **dragging along the ruler scrubs** (the preview follows; playback, if running, continues from there).
- **Strip**: one block per clip, its width proportional to the hold time (44 px a second by default; zoomed out a clip keeps at least 20–64 px), with a thumbnail (its step's bake, composed live instead while the step is not baked yet or when the clip has layers of its own; a photo clip's carries its shot's state marker), the shot's title (and its place in the block for sequences; a layers icon marks a clip with layers of its own) and the hold time. Placeholders are dashed. A ruler labels the time (the spacing adapts to the zoom); **dragging along the ruler scrubs** (the preview follows; playback, if running, continues from there).
- **Zoom**: Ctrl/⌘ + scroll over the strip zooms around the pointer; the bar above has −, a slider (double-click: default), +, and **Fit** (the whole cut in the width). Remembered in `localStorage["timelineZoom"]`.
- **Playhead**: one red line across ruler and clips with a **knob above the strip**; drag the knob to scrub.
- **Height**: the grip under the strip sets the thumbnail height (double-click resets; `localStorage["timelineStripH"]`). Click selects, double-click opens the shot view with the timeline's shots as its list, right-click opens the clip menu (Re-frame, Layers or Sketch it, Approve / Archive / Back to review for the clip's shot, Open shot, Extract as own shot…, Duplicate, Remove). **Drag a clip** to reorder (an accent line marks the drop position); shots drop in from the browser.
Expand Down
Loading