import type { KeyboardEvent as ReactKeyboardEvent } from "react"; import { HOURS_PER_DAY, formatTimeOfDayHours } from "../document/project-time-settings"; import { formatControlEffectValue, getControlTargetRefKey } from "../controls/control-surface"; import { PROJECT_SCHEDULE_WEEKDAYS, formatProjectScheduleDaySelection, formatProjectScheduleWeekdayLabel, getProjectScheduleTimelineSegments, type ProjectScheduler, type ProjectScheduleRoutine, type ProjectScheduleWeekday } from "../scheduler/project-scheduler"; import { getProjectScheduleEffectOptionId, getProjectScheduleTargetOptionForRoutine, listProjectScheduleEffectOptions, type ProjectScheduleEffectOptionId, type ProjectScheduleTargetOption } from "../scheduler/project-schedule-control-options"; interface ProjectSchedulePaneProps { targetOptions: ProjectScheduleTargetOption[]; scheduler: ProjectScheduler; selectedRoutineId: string | null; onSelectRoutine(routineId: string | null): void; onAddRoutine(targetKey: string): void; onDeleteRoutine(routineId: string): void; onClose(): void; onSetRoutineTarget(routineId: string, targetKey: string): void; onSetRoutineTitle(routineId: string, title: string): void; onSetRoutineEnabled(routineId: string, enabled: boolean): void; onSetRoutineStartHour(routineId: string, startHour: number): void; onSetRoutineEndHour(routineId: string, endHour: number): void; onSetRoutinePriority(routineId: string, priority: number): void; onSetRoutineEffectOption( routineId: string, effectOptionId: ProjectScheduleEffectOptionId ): void; onSetRoutineDays( routineId: string, mode: "everyDay" | "selectedDays", days: ProjectScheduleWeekday[] ): void; onSetRoutineNumericValue(routineId: string, value: number): void; onSetRoutineColorValue(routineId: string, colorHex: string): void; onSetRoutineAnimationClip(routineId: string, clipName: string): void; onSetRoutineAnimationLoop(routineId: string, loop: boolean): void; } function handleCommitOnEnter( event: ReactKeyboardEvent, commit: () => void ) { if (event.key !== "Enter") { return; } event.currentTarget.blur(); commit(); } function getRoutineSummary(routine: ProjectScheduleRoutine): string { return `${formatProjectScheduleDaySelection(routine.days)} · ${formatTimeOfDayHours(routine.startHour)}-${formatTimeOfDayHours(routine.endHour)} · ${formatControlEffectValue(routine.effect)} · P${routine.priority}`; } function isRoutineEffectInactive(routine: ProjectScheduleRoutine): boolean { switch (routine.effect.type) { case "setActorPresence": return !routine.effect.active; case "stopModelAnimation": case "stopSound": return true; case "setModelInstanceVisible": return !routine.effect.visible; case "setInteractionEnabled": case "setLightEnabled": return !routine.effect.enabled; default: return false; } } function getRoutineNumericValue(routine: ProjectScheduleRoutine): number | null { switch (routine.effect.type) { case "setSoundVolume": return routine.effect.volume; case "setLightIntensity": case "setAmbientLightIntensity": case "setSunLightIntensity": return routine.effect.intensity; default: return null; } } function getRoutineColorValue(routine: ProjectScheduleRoutine): string | null { switch (routine.effect.type) { case "setLightColor": case "setAmbientLightColor": case "setSunLightColor": return routine.effect.colorHex; default: return null; } } function groupTargetOptions( targetOptions: ProjectScheduleTargetOption[] ): Array<{ groupLabel: string; options: ProjectScheduleTargetOption[] }> { const grouped = new Map(); for (const option of targetOptions) { const entries = grouped.get(option.groupLabel) ?? []; entries.push(option); grouped.set(option.groupLabel, entries); } return [...grouped.entries()].map(([groupLabel, options]) => ({ groupLabel, options })); } export function ProjectSchedulePane({ targetOptions, scheduler, selectedRoutineId, onSelectRoutine, onAddRoutine, onDeleteRoutine, onClose, onSetRoutineTarget, onSetRoutineTitle, onSetRoutineEnabled, onSetRoutineStartHour, onSetRoutineEndHour, onSetRoutinePriority, onSetRoutineEffectOption, onSetRoutineDays, onSetRoutineNumericValue, onSetRoutineColorValue, onSetRoutineAnimationClip, onSetRoutineAnimationLoop }: ProjectSchedulePaneProps) { const selectedRoutine = selectedRoutineId === null ? null : scheduler.routines[selectedRoutineId] ?? null; const selectedTargetOption = selectedRoutine === null ? null : getProjectScheduleTargetOptionForRoutine( targetOptions, selectedRoutine.target ); const selectedEffectOptionId = selectedRoutine === null ? null : getProjectScheduleEffectOptionId(selectedRoutine.effect); const selectedEffectOptions = selectedTargetOption === null ? [] : listProjectScheduleEffectOptions(selectedTargetOption); const selectedRoutineDays = selectedRoutine === null ? PROJECT_SCHEDULE_WEEKDAYS : selectedRoutine.days.mode === "everyDay" ? PROJECT_SCHEDULE_WEEKDAYS : selectedRoutine.days.days; const hourTicks = Array.from({ length: HOURS_PER_DAY }, (_, hour) => hour); return (
Schedule
Scheduler-owned orchestration over typed control targets and global project time.
Targets
{hourTicks.map((hour) => (
{String(hour).padStart(2, "0")}
))}
{targetOptions.length === 0 ? (
No scheduler-addressable control targets are authored in this project yet.
) : ( targetOptions.map((targetOption) => { const routines = Object.values(scheduler.routines) .filter( (routine) => getControlTargetRefKey(routine.target) === targetOption.key ) .sort((left, right) => left.startHour - right.startHour); return (
{targetOption.label}
{targetOption.groupLabel} · {targetOption.subtitle}
{routines.map((routine) => getProjectScheduleTimelineSegments(routine).map( (segment) => ( ) ) )}
); }) )}
); }