Keine Layer im Projekt.
+ ) : (
+ layers.map((layer) => {
+ const selected = layer.id === selectedId;
+ const isCursor = layer.id === cursorId;
+ const editing = layer.id === editingId;
+ const op = opacityOf(layer);
+ const dur =
+ layer.timeDurationSec === 0 ? '∞' : formatTimecode(layer.timeDurationSec);
+ return (
+
{
+ props.onSelect(layer.id);
+ props.onCursor(layer.id);
+ }}
+ >
+ {/* Status – Glyphe + Ton + Tooltip, nie Farbe allein (§17.6) */}
+
+
+ {STATUS_LABEL[layer.status]}
+
+ {/* Layer: Z-Reihenfolge + editierbarer Name */}
+
+
+ {layer.z}
+
+ {editing ? (
+ setDraftName(e.target.value)}
+ onKeyDown={(e) => {
+ e.stopPropagation();
+ if (e.key === 'Enter') commitEdit();
+ else if (e.key === 'Escape') setEditingId(null);
+ }}
+ onBlur={commitEdit}
+ />
+ ) : (
+ beginEdit(layer)}
+ >
+ {layer.name}
+
+ )}
+
+ {/* Typ */}
+
+ {TYPE_LABEL[layer.type]}
+
+ {/* Source: Clip-/Generatorname + Bank/Index */}
+
+ {layer.sourceName}
+ {layer.sourceBank}
+
+ {/* Target */}
+
+ {layer.target}
+
+ {/* Time – monospace/tabellarisch (§17.6) */}
+
+ {formatTimecode(layer.timePositionSec)}
+ {dur}
+
+ {/* Opacity: Wert + schmaler Fader (64 px) */}
+
+ {op}
+ e.stopPropagation()}
+ onChange={(e) => handleOpacityInput(layer, Number(e.target.value))}
+ onPointerUp={() => commitOpacity(layer)}
+ onKeyUp={() => commitOpacity(layer)}
+ onBlur={() => commitOpacity(layer)}
+ />
+
+ {/* Blend */}
+
+ {layer.blend}
+
+ {/* FX: zwei Slots mit Bypass-/Fehlerstatus */}
+
+ {layer.fxSlots.map((slot, idx) =>
+ slot === null ? (
+
+ —
+
+ ) : (
+
+
+ {slot.name}
+ {slot.state === 'bypassed' && BYP}
+ {slot.state === 'error' && ERR}
+
+ ),
+ )}
+
+ {/* Control: Quellen + Owner */}
+
+ {layer.control.sources.join(' · ')}
+ {layer.control.owner}
+
+