From 286a8d1879bf6d550e0f26a31d03f9fb422d24c8 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Tue, 14 Apr 2026 19:38:09 +0200 Subject: [PATCH] Enhance App.tsx with schedule pane splitter and resize functionality --- src/app/App.tsx | 561 +++++++++++++++++++++++++----------------------- 1 file changed, 297 insertions(+), 264 deletions(-) diff --git a/src/app/App.tsx b/src/app/App.tsx index 6c4381df..3b37d2d2 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -10132,7 +10132,12 @@ export function App({ store, initialStatusMessage }: AppProps) { -
+
{schedulePaneOpen ? ( - setSchedulePaneOpen(false)} - onSetRoutineTarget={(routineId, targetKey) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule target", - "Retargeted schedule routine.", - (routine) => { - const targetOption = resolveProjectScheduleTargetOption(targetKey); + <> +
+
+ setSchedulePaneOpen(false)} + onSetRoutineTarget={(routineId, targetKey) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule target", + "Retargeted schedule routine.", + (routine) => { + const targetOption = + resolveProjectScheduleTargetOption(targetKey); - if (targetOption === null) { - throw new Error("Selected schedule target no longer exists."); - } + if (targetOption === null) { + throw new Error( + "Selected schedule target no longer exists." + ); + } - if (targetOption.target.kind === "actor") { - routine.target = targetOption.target; - routine.effects = []; - return; - } + if (targetOption.target.kind === "actor") { + routine.target = targetOption.target; + routine.effects = []; + return; + } - const effectOptions = listProjectScheduleEffectOptions(targetOption); - const currentPrimaryEffect = routine.effects[0] ?? null; - const nextEffectOptionId = - currentPrimaryEffect === null - ? effectOptions[0]?.id - : effectOptions.find( - (effectOption) => - effectOption.id === - getProjectScheduleEffectOptionId(currentPrimaryEffect) - )?.id ?? effectOptions[0]?.id; + const effectOptions = + listProjectScheduleEffectOptions(targetOption); + const currentPrimaryEffect = routine.effects[0] ?? null; + const nextEffectOptionId = + currentPrimaryEffect === null + ? effectOptions[0]?.id + : effectOptions.find( + (effectOption) => + effectOption.id === + getProjectScheduleEffectOptionId( + currentPrimaryEffect + ) + )?.id ?? effectOptions[0]?.id; - if (nextEffectOptionId === undefined) { - throw new Error( - "Selected schedule target does not expose a schedulable effect." - ); - } + if (nextEffectOptionId === undefined) { + throw new Error( + "Selected schedule target does not expose a schedulable effect." + ); + } - routine.target = targetOption.target; - routine.effects = [ - createProjectScheduleEffectFromOption({ - targetOption, - effectOptionId: nextEffectOptionId, - previousEffect: currentPrimaryEffect - }) - ]; + routine.target = targetOption.target; + routine.effects = [ + createProjectScheduleEffectFromOption({ + targetOption, + effectOptionId: nextEffectOptionId, + previousEffect: currentPrimaryEffect + }) + ]; + } + ) } - ) - } - onSetRoutineTitle={(routineId, title) => - updateProjectScheduleRoutine( - routineId, - "Rename project schedule routine", - "Updated schedule routine title.", - (routine) => { - routine.title = title.trim(); + onSetRoutineTitle={(routineId, title) => + updateProjectScheduleRoutine( + routineId, + "Rename project schedule routine", + "Updated schedule routine title.", + (routine) => { + routine.title = title.trim(); + } + ) } - ) - } - onSetRoutineEnabled={(routineId, enabled) => - updateProjectScheduleRoutine( - routineId, - "Toggle project schedule routine", - enabled - ? "Enabled schedule routine." - : "Disabled schedule routine.", - (routine) => { - routine.enabled = enabled; + onSetRoutineEnabled={(routineId, enabled) => + updateProjectScheduleRoutine( + routineId, + "Toggle project schedule routine", + enabled + ? "Enabled schedule routine." + : "Disabled schedule routine.", + (routine) => { + routine.enabled = enabled; + } + ) } - ) - } - onSetRoutineStartHour={(routineId, startHour) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule start time", - "Updated schedule routine start time.", - (routine) => { - routine.startHour = normalizeTimeOfDayHours(startHour); - if (routine.startHour === routine.endHour) { - throw new Error( - "Schedule routine start and end hours must differ." - ); - } + onSetRoutineStartHour={(routineId, startHour) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule start time", + "Updated schedule routine start time.", + (routine) => { + routine.startHour = normalizeTimeOfDayHours(startHour); + if (routine.startHour === routine.endHour) { + throw new Error( + "Schedule routine start and end hours must differ." + ); + } + } + ) } - ) - } - onSetRoutineEndHour={(routineId, endHour) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule end time", - "Updated schedule routine end time.", - (routine) => { - routine.endHour = normalizeTimeOfDayHours(endHour); - if (routine.startHour === routine.endHour) { - throw new Error( - "Schedule routine start and end hours must differ." - ); - } + onSetRoutineEndHour={(routineId, endHour) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule end time", + "Updated schedule routine end time.", + (routine) => { + routine.endHour = normalizeTimeOfDayHours(endHour); + if (routine.startHour === routine.endHour) { + throw new Error( + "Schedule routine start and end hours must differ." + ); + } + } + ) } - ) - } - onSetRoutinePriority={(routineId, priority) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule priority", - "Updated schedule routine priority.", - (routine) => { - if (!Number.isFinite(priority)) { - throw new Error( - "Schedule routine priority must be a finite number." - ); - } + onSetRoutinePriority={(routineId, priority) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule priority", + "Updated schedule routine priority.", + (routine) => { + if (!Number.isFinite(priority)) { + throw new Error( + "Schedule routine priority must be a finite number." + ); + } - routine.priority = Math.trunc(priority); + routine.priority = Math.trunc(priority); + } + ) } - ) - } - onSetRoutineEffectOption={(routineId, effectOptionId) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule effect", - "Updated schedule routine effect.", - (routine) => { - if (routine.target.kind === "actor") { - throw new Error( - "Actor routines use dedicated presence, animation, and path controls." - ); - } + onSetRoutineEffectOption={(routineId, effectOptionId) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule effect", + "Updated schedule routine effect.", + (routine) => { + if (routine.target.kind === "actor") { + throw new Error( + "Actor routines use dedicated presence, animation, and path controls." + ); + } - const targetOption = getProjectScheduleTargetOptionByKey( - projectScheduleTargetOptions, - getControlTargetRefKey(routine.target) - ); - - if (targetOption === null) { - throw new Error( - "The current schedule target no longer exists in the project." - ); - } - - routine.effects = [ - createProjectScheduleEffectFromOption({ - targetOption, - effectOptionId, - previousEffect: routine.effects[0] ?? null - }) - ]; - } - ) - } - onSetRoutineDays={(routineId, mode, days) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule days", - "Updated schedule routine days.", - (routine) => { - routine.days = - mode === "everyDay" - ? createProjectScheduleEveryDaySelection() - : { - mode: "selectedDays", - days - }; - } - ) - } - onSetRoutineNumericValue={(routineId, value) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule numeric value", - "Updated schedule routine value.", - (routine) => { - if (!Number.isFinite(value) || value < 0) { - throw new Error( - "Schedule routine numeric values must be finite and zero or greater." - ); - } - - const effect = routine.effects[0]; - - if (effect === undefined) { - throw new Error( - "The current schedule routine does not expose a numeric value." - ); - } - - switch (effect.type) { - case "setSoundVolume": - effect.volume = value; - return; - case "setLightIntensity": - case "setAmbientLightIntensity": - case "setSunLightIntensity": - effect.intensity = value; - return; - default: - throw new Error( - "The current schedule effect does not expose a numeric value." + const targetOption = getProjectScheduleTargetOptionByKey( + projectScheduleTargetOptions, + getControlTargetRefKey(routine.target) ); - } + + if (targetOption === null) { + throw new Error( + "The current schedule target no longer exists in the project." + ); + } + + routine.effects = [ + createProjectScheduleEffectFromOption({ + targetOption, + effectOptionId, + previousEffect: routine.effects[0] ?? null + }) + ]; + } + ) } - ) - } - onSetRoutineColorValue={(routineId, colorHex) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule color", - "Updated schedule routine color.", - (routine) => { - const effect = routine.effects[0]; - - if (effect === undefined) { - throw new Error( - "The current schedule routine does not expose a color value." - ); - } - - switch (effect.type) { - case "setLightColor": - case "setAmbientLightColor": - case "setSunLightColor": - effect.colorHex = colorHex; - return; - default: - throw new Error( - "The current schedule effect does not expose a color value." - ); - } + onSetRoutineDays={(routineId, mode, days) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule days", + "Updated schedule routine days.", + (routine) => { + routine.days = + mode === "everyDay" + ? createProjectScheduleEveryDaySelection() + : { + mode: "selectedDays", + days + }; + } + ) } - ) - } - onSetRoutineAnimationClip={(routineId, clipName) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule animation clip", - "Updated schedule animation clip.", - (routine) => { - const effect = routine.effects[0]; + onSetRoutineNumericValue={(routineId, value) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule numeric value", + "Updated schedule routine value.", + (routine) => { + if (!Number.isFinite(value) || value < 0) { + throw new Error( + "Schedule routine numeric values must be finite and zero or greater." + ); + } - if (effect?.type !== "playModelAnimation") { - throw new Error( - "The current schedule effect does not expose an animation clip." - ); - } + const effect = routine.effects[0]; - effect.clipName = clipName; + if (effect === undefined) { + throw new Error( + "The current schedule routine does not expose a numeric value." + ); + } + + switch (effect.type) { + case "setSoundVolume": + effect.volume = value; + return; + case "setLightIntensity": + case "setAmbientLightIntensity": + case "setSunLightIntensity": + effect.intensity = value; + return; + default: + throw new Error( + "The current schedule effect does not expose a numeric value." + ); + } + } + ) } - ) - } - onSetRoutineAnimationLoop={(routineId, loop) => - updateProjectScheduleRoutine( - routineId, - "Set project schedule animation loop", - loop - ? "Schedule animation now loops." - : "Schedule animation now plays once.", - (routine) => { - const effect = routine.effects[0]; + onSetRoutineColorValue={(routineId, colorHex) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule color", + "Updated schedule routine color.", + (routine) => { + const effect = routine.effects[0]; - if (effect?.type !== "playModelAnimation") { - throw new Error( - "The current schedule effect does not expose animation looping." - ); - } + if (effect === undefined) { + throw new Error( + "The current schedule routine does not expose a color value." + ); + } - effect.loop = loop; + switch (effect.type) { + case "setLightColor": + case "setAmbientLightColor": + case "setSunLightColor": + effect.colorHex = colorHex; + return; + default: + throw new Error( + "The current schedule effect does not expose a color value." + ); + } + } + ) } - ) - } - onSetActorRoutinePresence={(routineId, active) => - updateProjectScheduleRoutine( - routineId, - "Set actor schedule presence", - active - ? "Actor routine now keeps the NPC present." - : "Actor routine now hides the NPC during this block.", - (routine) => { - if (routine.target.kind !== "actor") { - throw new Error("Only actor routines expose presence."); - } + onSetRoutineAnimationClip={(routineId, clipName) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule animation clip", + "Updated schedule animation clip.", + (routine) => { + const effect = routine.effects[0]; + + if (effect?.type !== "playModelAnimation") { + throw new Error( + "The current schedule effect does not expose an animation clip." + ); + } + + effect.clipName = clipName; + } + ) + } + onSetRoutineAnimationLoop={(routineId, loop) => + updateProjectScheduleRoutine( + routineId, + "Set project schedule animation loop", + loop + ? "Schedule animation now loops." + : "Schedule animation now plays once.", + (routine) => { + const effect = routine.effects[0]; + + if (effect?.type !== "playModelAnimation") { + throw new Error( + "The current schedule effect does not expose animation looping." + ); + } + + effect.loop = loop; + } + ) + } + onSetActorRoutinePresence={(routineId, active) => + updateProjectScheduleRoutine( + routineId, + "Set actor schedule presence", + active + ? "Actor routine now keeps the NPC present." + : "Actor routine now hides the NPC during this block.", + (routine) => { + if (routine.target.kind !== "actor") { + throw new Error("Only actor routines expose presence."); + } upsertActorRoutineEffect( routine,