From d87119f1cede9d7bf826bed79fc3d03d33d68718 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Wed, 15 Apr 2026 00:52:41 +0200 Subject: [PATCH] Update App and ProjectSchedulePane components with new handlers and styles for sequence duration and clip timing --- src/app/App.tsx | 2 + src/app/ProjectSchedulePane.tsx | 40 +++++---- src/app/app.css | 143 ++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 14 deletions(-) diff --git a/src/app/App.tsx b/src/app/App.tsx index 97528771..b3c370f3 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -11888,6 +11888,7 @@ export function App({ store, initialStatusMessage }: AppProps) { } ) } + onSetSequenceDurationMinutes={updateProjectSequenceDurationMinutes} onAddHeldControlStep={(sequenceId, targetKey) => handleAddProjectSequenceControlStep( sequenceId, @@ -11904,6 +11905,7 @@ export function App({ store, initialStatusMessage }: AppProps) { } onAddDialogueStep={handleAddProjectSequenceDialogueStep} onDeleteStep={handleDeleteProjectSequenceStep} + onSetClipTiming={updateProjectSequenceClipTiming} onSetControlStepTarget={updateProjectSequenceControlStepTarget} onSetControlStepEffectOption={ updateProjectSequenceControlStepEffectOption diff --git a/src/app/ProjectSchedulePane.tsx b/src/app/ProjectSchedulePane.tsx index fc071a13..a55f48aa 100644 --- a/src/app/ProjectSchedulePane.tsx +++ b/src/app/ProjectSchedulePane.tsx @@ -324,10 +324,12 @@ export function ProjectSequencerPane({ onSetActorRoutinePathSpeed, onSetActorRoutinePathLoop, onSetSequenceTitle, + onSetSequenceDurationMinutes, onAddHeldControlStep, onAddImpulseControlStep, onAddDialogueStep, onDeleteStep, + onSetClipTiming, onSetControlStepTarget, onSetControlStepEffectOption, onSetControlStepNumericValue, @@ -457,10 +459,12 @@ export function ProjectSequencerPane({ onAddSequence={onAddSequence} onDeleteSequence={onDeleteSequence} onSetSequenceTitle={onSetSequenceTitle} + onSetSequenceDurationMinutes={onSetSequenceDurationMinutes} onAddHeldControlStep={onAddHeldControlStep} onAddImpulseControlStep={onAddImpulseControlStep} onAddDialogueStep={onAddDialogueStep} onDeleteStep={onDeleteStep} + onSetClipTiming={onSetClipTiming} onSetControlStepTarget={onSetControlStepTarget} onSetControlStepEffectOption={onSetControlStepEffectOption} onSetControlStepNumericValue={onSetControlStepNumericValue} @@ -736,22 +740,26 @@ export function ProjectSequencerPane({ onSetRoutineStartHour( selectedRoutine.id, - Number(event.currentTarget.value) + parseTimeOfDayInputHours( + event.currentTarget.value, + "Clip start" + ) ) } onKeyDown={(event) => handleCommitOnEnter(event, () => onSetRoutineStartHour( selectedRoutine.id, - Number(event.currentTarget.value) + parseTimeOfDayInputHours( + event.currentTarget.value, + "Clip start" + ) ) ) } @@ -762,22 +770,26 @@ export function ProjectSequencerPane({ onSetRoutineEndHour( selectedRoutine.id, - Number(event.currentTarget.value) + parseTimeOfDayInputHours( + event.currentTarget.value, + "Clip end" + ) ) } onKeyDown={(event) => handleCommitOnEnter(event, () => onSetRoutineEndHour( selectedRoutine.id, - Number(event.currentTarget.value) + parseTimeOfDayInputHours( + event.currentTarget.value, + "Clip end" + ) ) ) } diff --git a/src/app/app.css b/src/app/app.css index b3373e1f..5eac4280 100644 --- a/src/app/app.css +++ b/src/app/app.css @@ -1119,6 +1119,149 @@ button:disabled { white-space: nowrap; } +.sequence-timeline { + display: flex; + flex-direction: column; + gap: 10px; +} + +.sequence-timeline__summary { + color: var(--color-muted); + font-size: 0.8rem; +} + +.sequence-timeline__ruler { + position: relative; + height: 24px; + margin-left: 72px; +} + +.sequence-timeline__hour { + position: absolute; + top: 0; + transform: translateX(-50%); + color: var(--color-muted); + font-size: 0.7rem; + white-space: nowrap; +} + +.sequence-timeline__track { + position: relative; + margin-left: 72px; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 16px; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0.01)), + rgba(9, 13, 18, 0.9); + overflow: hidden; +} + +.sequence-timeline__lane { + position: absolute; + left: 0; + right: 0; + height: 52px; + border-top: 1px solid rgba(255, 255, 255, 0.04); +} + +.sequence-timeline__lane:first-child { + border-top: 0; +} + +.sequence-timeline__lane-label { + position: absolute; + left: -68px; + top: 50%; + width: 56px; + transform: translateY(-50%); + color: var(--color-muted); + font-size: 0.72rem; + text-align: right; +} + +.sequence-timeline__minor-grid, +.sequence-timeline__major-grid { + position: absolute; + top: 0; + bottom: 0; + width: 1px; + pointer-events: none; +} + +.sequence-timeline__minor-grid { + background: rgba(255, 255, 255, 0.04); +} + +.sequence-timeline__major-grid { + background: rgba(255, 255, 255, 0.11); +} + +.sequence-timeline__clip { + position: absolute; + min-width: 18px; + height: 36px; + display: flex; + align-items: center; + gap: 8px; + padding: 0 10px; + border-radius: 12px; + border: 1px solid rgba(255, 255, 255, 0.12); + cursor: grab; + overflow: hidden; +} + +.sequence-timeline__clip:active { + cursor: grabbing; +} + +.sequence-timeline__clip--held { + background: linear-gradient(135deg, #f5b66f, #d17f4a); + color: #24150d; +} + +.sequence-timeline__clip--impulse { + background: linear-gradient(135deg, #8ec7d9, #5d8aa1); + color: #0d1a22; +} + +.sequence-timeline__clip--selected { + box-shadow: 0 0 0 2px rgba(255, 250, 240, 0.35); +} + +.sequence-timeline__clip-title { + min-width: 0; + flex: 1 1 auto; + overflow: hidden; + font-size: 0.76rem; + font-weight: 700; + text-overflow: ellipsis; + white-space: nowrap; +} + +.sequence-timeline__clip-time { + font-size: 0.68rem; + opacity: 0.8; + white-space: nowrap; +} + +.sequence-timeline__clip-handle { + position: absolute; + top: 4px; + bottom: 4px; + width: 8px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.45); + cursor: ew-resize; +} + +.sequence-timeline__clip-handle--start { + left: 4px; +} + +.sequence-timeline__clip-handle--end { + right: 4px; +} + .schedule-days { display: flex; flex-wrap: wrap;