Files
feuerwerk/src/core/model/placement.ts
T
adminandClaude Opus 5.5 e435c54afb Edit a composition's tracks, items and clips in its own tab (#417)
A composition could be placed and played, but not filled. Its tab
puts the picture at the playhead above a timeline of tracks, so a cut
or a cutscene is laid out by dragging assets onto tracks.

- The timeline takes the song tab's geometry with frames for ticks:
  ruler in seconds, scrubbing, play and pause, + Track. Zoom and pan
  glide through viewTween like the other tabs.
- A placeable asset dropped on a track becomes an item, 3 s long or
  as long as its sprite's video. Dropped on an item it lands behind.
- Items move without overlapping. The right edge stops at the next
  item. The left edge trims into a video and not before its start.
- An animation dropped on a track becomes a narrower clip bar above
  the item it plays on. The asset panel now starts a drag for
  animations too.
- The picture is a scene view over compositionHolder. A click picks
  an item or its child, a drag moves the real object. It takes no
  assets, since an object there would have no item.
- The hierarchy lists the items alive at the playhead. Reparenting is
  off there, items stay at the top level.
- The inspector shows a TIMING block on an item (start, length,
  offset for a video, Fit to media) and edits tracks and clips.
- Delete, the context menu and Remove item take item and object in
  one undo step.
- fitMedia: an override that gives an item another video sets its
  length in the same undo step, capped at the next item. Dragging an
  edge or typing a length turns it off.
- A composition inside a composition is refused.

Tests:
- Unit composition-edit (new): 19 tests. Four counter-checks each
  turned their test red. The "back to 3 s" test got a step in
  between after one stayed green.
- e2e 69 (new): 11 checks, 9 own. The hierarchy check reads the rows
  shown, after a counter-check on the model's rows stayed green.
- Gate on melektaus-atx: CHECK EXIT 0, 302 files, 4662 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:47:22 +02:00

21 lines
1.1 KiB
TypeScript

import type { Asset } from './Asset';
import { ASSET_TYPE_LABELS, isPlaceableType } from './AssetTypes';
import { containerReaches } from './container';
import type { ObjectHolder, Project } from './Project';
/**
* Why `asset` cannot be placed into `holder`, or '' when it can. A drag needs the answer while
* it hovers, `PlaceAssetCommand` needs it at the drop; both ask here so the preview cannot
* promise a placement the command then refuses.
*/
export function placementRejection(project: Project, holder: ObjectHolder, asset: Asset): string {
// A pattern or a sound is dragged out of the panel for a song (#334), and may pass over a scene.
if (!isPlaceableType(asset.type))
return `A ${ASSET_TYPE_LABELS[asset.type].toLowerCase()} cannot be placed into a scene`;
if (asset.type === 'container' && containerReaches(project, asset, holder.id))
return `${asset.name} cannot contain ${holder.name}: that nests forever`;
if (asset.type === 'composition' && holder.type === 'composition')
return 'A composition cannot be placed into a composition';
return '';
}