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
2 changes: 1 addition & 1 deletion docs/Features/3D-Layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Camera clips expose their own Properties tab with:

The Transform tab becomes scene-navigation controls for the active camera clip. In FPS mode, the preview accepts WASD/QE navigation plus uncapped mouse look. Free scene navigation now belongs to camera clips rather than gaussian-splat clips.

Camera rotation keyframes interpolate through the shortest angular path so timeline flights do not spin the long way around when yaw, pitch, or roll crosses a 360-degree wrap. FPS-look camera segments with keyed position/forward travel render through world-pose interpolation, keeping vertical-look roll moves from drifting away between keyframes.
Camera rotation keyframes interpolate through the shortest angular path so timeline flights do not spin the long way around when yaw, pitch, or roll crosses a 360-degree wrap. Camera transform keyframes for pan, distance, zoom, forward travel, and rotation render through world-pose interpolation: the camera eye and target are interpolated between keyed world poses, keeping FPS-look moves, zoom-outs, and vertical-look roll moves from drifting away between keyframes.

## Gaussian Splats

Expand Down
7 changes: 5 additions & 2 deletions docs/Features/Slot-Grid.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ When `window.__ENGINE_FLAGS__.useLiveSlotTrigger` is enabled, the primary click

### Other Actions

- re-clicking an active slot restarts playback from the beginning
- re-clicking an active slot restarts playback from the slot `trimIn` point
- clicking an empty slot clears that layer
- dragging moves or swaps slot assignments
- right-clicking a filled slot opens `Open in Editor` and `Remove from Slot`
- right-clicking a filled slot opens `Open in Editor`, `Map MIDI to Slot`, and `Remove from Slot`
- `Map MIDI to Slot` opens the MIDI Mapping panel, creates a pending slot trigger mapping, and arms MIDI learn mode for that slot

---

Expand All @@ -71,6 +72,8 @@ The current defaults are:

These settings are stored in `mediaStore.slotClipSettings` and edited through the `Slot Clip` properties tab.

Slot launches use the configured slot window, not the composition editor playhead. The `Slot Clip` tab renders the composition tracks, the active trim window, and the current layer playhead in the same timeline surface so the tab reflects the playback state used by `layerPlaybackManager`.

---

## Multi-Layer Playback
Expand Down
9 changes: 5 additions & 4 deletions docs/Features/UI-Panels.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,13 @@ MasterSelects currently exposes 17 dockable panel types, plus the Slot Grid over

- Open from `View -> Panels -> MIDI Mapping`
- Shows all currently assigned MIDI notes in one list
- Includes both global transport bindings and per-marker bindings
- Includes global transport bindings, per-marker bindings, and Slot Grid trigger bindings
- Each row shows the note, target, and resulting command behavior
- Click any mapping card to trigger the assigned action and preview what the MIDI note does
- `Edit` opens inline controls for manual channel/note changes and marker reassignment
- `Learn` and `Clear` remain available directly from the panel
- Marker bindings support `Jump To Marker`, `Play From Marker`, and `Jump To Marker And Stop`
- Transport and marker learning still happen in Settings and the marker context menu
- Slot bindings can be created from the Slot Grid filled-slot context menu, which opens this panel with a pending `Listening...` mapping

### AI Chat Panel

Expand Down Expand Up @@ -289,11 +289,11 @@ Resolume-style slot grid for simultaneous multi-layer composition playback. The
| Action | Behavior |
|--------|----------|
| Click a filled slot | Select slot clip settings, open the Slot Clip tab, and either open the comp in the editor or trigger it live depending on `useLiveSlotTrigger` |
| Re-click an active slot | Restart playback from the beginning |
| Re-click an active slot | Restart playback from the slot trim-in point |
| Click an empty slot | Deactivate that layer |
| Click a column header | Activate all compositions in that column |
| Drag a slot | Reorder or swap a composition position |
| Right-click a filled slot | Open in Editor or Remove from Slot |
| Right-click a filled slot | Open in Editor, map MIDI to the slot, or Remove from Slot |

### Multi-Layer Playback

Expand Down Expand Up @@ -356,6 +356,7 @@ The unified Properties panel adapts its tabs to the selected clip type and to sl
- Tabs switch automatically based on clip type
- Badge counts appear for effects, masks, transcripts, and analysis readiness
- Slot grid mode switches the panel to the Slot Clip tab
- The Slot Clip tab shows the slotted composition tracks, the configured trim window, and the current live layer playhead on one range timeline

---

Expand Down
227 changes: 195 additions & 32 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -3768,6 +3768,10 @@ input, textarea {
gap: var(--sp-2);
}

.slot-clip-summary-title {
min-width: 0;
}

.slot-clip-title {
margin-top: 2px;
font-size: var(--font-md);
Expand All @@ -3794,13 +3798,19 @@ input, textarea {

.slot-clip-summary-meta span {
padding: 3px 7px;
border-radius: 999px;
border-radius: var(--radius-sm);
background: var(--bg-primary);
border: 1px solid var(--border-color);
color: var(--text-secondary);
font-size: var(--font-xs);
}

.slot-clip-summary-meta .slot-clip-status-active {
border-color: rgba(58, 206, 128, 0.38);
color: #56d98f;
background: rgba(58, 206, 128, 0.11);
}

.slot-clip-transport {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
Expand All @@ -3823,6 +3833,11 @@ input, textarea {
font-weight: var(--font-medium);
}

.slot-clip-transport-btn.primary {
border-color: rgba(74, 158, 255, 0.5);
background: rgba(74, 158, 255, 0.16);
}

.slot-clip-transport-btn:hover:not(:disabled),
.slot-clip-end-btn:hover {
background: var(--bg-hover);
Expand All @@ -3833,89 +3848,237 @@ input, textarea {
cursor: default;
}

.slot-clip-section-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--sp-2);
}

.slot-clip-section-header span {
font-family: 'SF Mono', 'Cascadia Code', 'Consolas', monospace;
color: var(--text-secondary);
font-size: var(--font-xs);
}

.slot-clip-timeline-shell {
display: flex;
flex-direction: column;
gap: var(--sp-1);
border-radius: var(--radius-md);
border: 1px solid var(--border-color);
background: var(--bg-primary);
overflow: hidden;
}

.slot-clip-timeline-preview {
.slot-clip-ruler {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
align-items: center;
min-height: 22px;
padding: 0 8px;
color: var(--text-tertiary);
font-family: 'SF Mono', 'Cascadia Code', 'Consolas', monospace;
font-size: 10px;
background: rgba(255, 255, 255, 0.03);
border-bottom: 1px solid var(--border-color);
}

.slot-clip-ruler span:nth-child(2) {
text-align: center;
}

.slot-clip-ruler span:nth-child(3) {
text-align: right;
}

.slot-clip-track-stack {
position: relative;
min-height: 84px;
border-radius: var(--radius-md);
border: 1px solid var(--border-color);
min-height: 116px;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(0, 0, 0, 0.18)),
linear-gradient(135deg, rgba(74, 158, 255, 0.08), rgba(240, 196, 90, 0.04));
repeating-linear-gradient(
90deg,
rgba(255, 255, 255, 0.035) 0,
rgba(255, 255, 255, 0.035) 1px,
transparent 1px,
transparent 10%
),
linear-gradient(180deg, rgba(255, 255, 255, 0.025), rgba(0, 0, 0, 0.12));
overflow: hidden;
}

.slot-clip-range-overlay {
.slot-clip-track-row {
position: relative;
height: 26px;
border-radius: var(--radius-md);
background: var(--bg-primary);
border: 1px solid var(--border-color);
display: grid;
grid-template-columns: 30px minmax(0, 1fr);
min-height: 29px;
border-bottom: 1px solid rgba(255, 255, 255, 0.055);
}

.slot-clip-track-row:last-of-type {
border-bottom: none;
}

.slot-clip-track-label {
position: relative;
z-index: 4;
display: flex;
align-items: center;
justify-content: center;
border-right: 1px solid var(--border-color);
background: rgba(0, 0, 0, 0.32);
color: var(--text-secondary);
font-size: 10px;
font-weight: var(--font-semibold);
}

.slot-clip-track-label-audio {
color: #57cf87;
}

.slot-clip-track-label-video {
color: #63adff;
}

.slot-clip-track-lane {
position: relative;
min-width: 0;
overflow: hidden;
}

.slot-clip-clip {
position: absolute;
top: 5px;
bottom: 5px;
min-width: 3px;
border-radius: var(--radius-sm);
border: 1px solid rgba(255, 255, 255, 0.2);
background: rgba(98, 157, 255, 0.64);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
overflow: hidden;
}

.slot-clip-clip span {
display: block;
padding: 2px 5px;
color: rgba(255, 255, 255, 0.86);
font-size: 10px;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.slot-clip-clip-audio {
background: rgba(56, 190, 108, 0.68);
}

.slot-clip-range-mask {
.slot-clip-clip-image,
.slot-clip-clip-lottie {
background: rgba(153, 122, 255, 0.66);
}

.slot-clip-clip-text,
.slot-clip-clip-solid {
background: rgba(238, 156, 68, 0.66);
}

.slot-clip-clip-model,
.slot-clip-clip-gaussian-avatar,
.slot-clip-clip-gaussian-splat,
.slot-clip-clip-splat-effector,
.slot-clip-clip-camera {
background: rgba(90, 202, 200, 0.64);
}

.slot-clip-empty-timeline {
display: flex;
align-items: center;
justify-content: center;
min-height: 116px;
color: var(--text-tertiary);
font-size: var(--font-sm);
}

.slot-clip-trim-mask {
position: absolute;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.45);
pointer-events: none;
z-index: 2;
}

.slot-clip-range-mask-left {
.slot-clip-trim-mask-left {
left: 0;
}

.slot-clip-range-mask-right {
.slot-clip-trim-mask-right {
right: 0;
}

.slot-clip-range-window {
position: absolute;
top: 2px;
bottom: 2px;
border-radius: var(--radius-sm);
background: rgba(74, 158, 255, 0.18);
border: 1px solid rgba(74, 158, 255, 0.45);
top: 0;
bottom: 0;
background: rgba(74, 158, 255, 0.08);
border-left: 1px solid rgba(240, 196, 90, 0.7);
border-right: 1px solid rgba(240, 196, 90, 0.7);
pointer-events: none;
z-index: 3;
}

.slot-clip-range-progress {
position: absolute;
top: 0;
bottom: 0;
background: rgba(74, 158, 255, 0.12);
pointer-events: none;
z-index: 3;
}

.slot-clip-playhead {
position: absolute;
top: 2px;
bottom: 2px;
top: 0;
bottom: 0;
width: 2px;
margin-left: -1px;
background: #ff6b57;
box-shadow: 0 0 0 1px rgba(255, 107, 87, 0.18);
pointer-events: none;
z-index: 5;
}

.slot-clip-handle {
position: absolute;
top: 2px;
bottom: 2px;
width: 10px;
margin-left: -5px;
border-radius: 999px;
background: #f0c45a;
top: 0;
bottom: 0;
width: 14px;
margin-left: -7px;
background: transparent;
border: none;
cursor: ew-resize;
z-index: 2;
z-index: 6;
}

.slot-clip-handle::before {
content: '';
position: absolute;
top: 6px;
bottom: 6px;
left: 5px;
width: 4px;
border-radius: var(--radius-sm);
background: #f0c45a;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.22);
}

.slot-clip-handle.dragging {
box-shadow: 0 0 0 2px rgba(240, 196, 90, 0.2);
background: rgba(240, 196, 90, 0.08);
}

.slot-clip-timecodes {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: repeat(auto-fit, minmax(86px, 1fr));
gap: var(--sp-1);
}

Expand Down
Loading
Loading