Show Delete in red in the track and item menus of both timelines

A composition clip had a red button in the inspector that did the
same as its menu entry. Deleting a track looked like any other entry
in the menu, although it takes the track with everything on it.

- The inspector of a clip drops "Remove clip(s)". Right-click and
  the Delete key still remove it.
- "Delete track" is red in the Composition and Song tabs, behind a
  separator like "Remove item" in the other menus.
- "Delete" on a Song item is red, too.

Tests:
- Gate on melektaus-atx: tsc, eslint and Prettier clean,
  305 files, 4722 tests passed.
- No check clicks the inspector button or counts the track menu, so
  no e2e module ran. The red entries were not looked at in a picture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-13 06:38:54 +02:00
1 parent 7a952cb20d
commit 5df2173559
3 files changed
+8 -14

No files matched your search

@@ -902,8 +902,10 @@ export class CompositionTimeline extends Widget {
{ label: 'Add track below', onSelect: () => this.addTrack(row + 1) }, { label: 'Add track below', onSelect: () => this.addTrack(row + 1) },
{ label: 'Move up', disabled: row === 0, onSelect: () => swap(row - 1) }, { label: 'Move up', disabled: row === 0, onSelect: () => swap(row - 1) },
{ label: 'Move down', disabled: row === asset.tracks.length - 1, onSelect: () => swap(row + 1) }, { label: 'Move down', disabled: row === asset.tracks.length - 1, onSelect: () => swap(row + 1) },
{ separator: true },
{ {
label: 'Delete track', label: 'Delete track',
danger: true,
disabled: asset.tracks.length <= 1, disabled: asset.tracks.length <= 1,
onSelect: () => { onSelect: () => {
if (this.state.execute(removeCompositionTrack(this.state.project, asset.id, track.id))) if (this.state.execute(removeCompositionTrack(this.state.project, asset.id, track.id)))
+3 -13
View File
@@ -1,5 +1,5 @@
import { MakeProjectAssetCommand } from '../../core/commands/assetCommands'; import { MakeProjectAssetCommand } from '../../core/commands/assetCommands';
import { removeFromComposition, SetCompositionCommand } from '../../core/commands/compositionCommands'; import { SetCompositionCommand } from '../../core/commands/compositionCommands';
import type { CompositionAsset, CompositionItem } from '../../core/model/Asset'; import type { CompositionAsset, CompositionItem } from '../../core/model/Asset';
import { import {
clipLength, clipLength,
@@ -13,7 +13,7 @@ import { findAsset, findAssetOf, type Project } from '../../core/model/Project';
import type { PropertyDef } from '../../core/properties/PropertySchema'; import type { PropertyDef } from '../../core/properties/PropertySchema';
import { Button, formatNumber, type MenuEntry } from '../../ui'; import { Button, formatNumber, type MenuEntry } from '../../ui';
import { createFieldRow } from './fieldFactory'; import { createFieldRow } from './fieldFactory';
import { addGap, addNameField, addNote, addTitle, type InspectorHost } from './host'; import { addNameField, addNote, addTitle, type InspectorHost } from './host';
import { assetLink } from './objectSection'; import { assetLink } from './objectSection';
const START_ROW = 'composition:start'; const START_ROW = 'composition:start';
@@ -44,7 +44,7 @@ export function compositionSignature(
return clipIds.length > 0 ? `clips:${clipIds.join(',')}` : 'track'; return clipIds.length > 0 ? `clips:${clipIds.join(',')}` : 'track';
} }
/** A track: its name; the timeline's menu adds, moves and deletes tracks. Clips: what they play, from when, and the way to remove them. */ /** A track: its name; the timeline's menu adds, moves and deletes tracks. Clips: what they play and from when; the timeline's menu removes them. */
export function buildCompositionSection( export function buildCompositionSection(
host: InspectorHost, host: InspectorHost,
assetId: string, assetId: string,
@@ -92,16 +92,6 @@ export function buildCompositionSection(
`One pass: ${formatNumber(length / fps, 2)} s. It plays on every item of its track it overlaps.`, `One pass: ${formatNumber(length / fps, 2)} s. It plays on every item of its track it overlaps.`,
); );
} else addTitle(host, `${clipIds.length} ANIMATION CLIPS`); } else addTitle(host, `${clipIds.length} ANIMATION CLIPS`);
addGap(host);
host.body.add(
new Button(clipIds.length === 1 ? 'Remove clip' : 'Remove clips', {
variant: 'danger',
onClick: () => {
if (host.state.execute(removeFromComposition(host.state.project, assetId, [], clipIds)))
host.state.select({ kind: 'asset', assetId });
},
}),
);
} }
export function refreshCompositionSection( export function refreshCompositionSection(
+3 -1
View File
@@ -1176,7 +1176,7 @@ export class SongTimeline extends Widget {
} }
: { label: 'Embed a copy', disabled: !played, onSelect: () => this.embedCopy(asset, hit) }, : { label: 'Embed a copy', disabled: !played, onSelect: () => this.embedCopy(asset, hit) },
{ separator: true }, { separator: true },
{ label: 'Delete', onSelect: () => this.deleteSelection() }, { label: 'Delete', danger: true, onSelect: () => this.deleteSelection() },
]).open(global.x, global.y); ]).open(global.x, global.y);
} }
@@ -1265,8 +1265,10 @@ export class SongTimeline extends Widget {
{ label: 'Add track below', onSelect: () => this.addTrack(row + 1) }, { label: 'Add track below', onSelect: () => this.addTrack(row + 1) },
{ label: 'Move up', disabled: row === 0, onSelect: () => swap(row - 1) }, { label: 'Move up', disabled: row === 0, onSelect: () => swap(row - 1) },
{ label: 'Move down', disabled: row === asset.tracks.length - 1, onSelect: () => swap(row + 1) }, { label: 'Move down', disabled: row === asset.tracks.length - 1, onSelect: () => swap(row + 1) },
{ separator: true },
{ {
label: 'Delete track', label: 'Delete track',
danger: true,
disabled: asset.tracks.length <= 1, disabled: asset.tracks.length <= 1,
onSelect: () => { onSelect: () => {
this.pick([], track.id); this.pick([], track.id);