ManimCat / frontend /src /studio /plot-studio-shell /hooks /use-plot-studio-shell.ts
Bin29's picture
Sync from main: e764154 feat(plot-skill): add math-exam-diagram SKILL.md for exam-style math figures
abcf568
import { useMemo, useState } from 'react'
import { useStudioSession } from '../../hooks/use-studio-session'
import { areSameIds, computeMaxHistorySlots, orderWorkSummaries } from '../utils/work-order'
export function usePlotStudioShell() {
const studio = useStudioSession({
studioKind: 'plot',
title: 'Plot Studio'
})
const [selectedWorkId, setSelectedWorkId] = useState<string | null>(null)
const [orderedWorkIds, setOrderedWorkIds] = useState<string[]>([])
const [confirmExitOpen, setConfirmExitOpen] = useState(false)
const [interruptArmedUntil, setInterruptArmedUntil] = useState<number | null>(null)
const orderedWorkSummaries = useMemo(
() => orderWorkSummaries(studio.workSummaries, orderedWorkIds),
[orderedWorkIds, studio.workSummaries]
)
const effectiveSelectedWorkId =
selectedWorkId && orderedWorkSummaries.some((entry) => entry.work.id === selectedWorkId)
? selectedWorkId
: orderedWorkSummaries[0]?.work.id ?? null
const selected = studio.selectWork(effectiveSelectedWorkId)
const historyCount = orderedWorkSummaries.length
const historyCountLabel = String(historyCount).padStart(2, '0')
const maxHistorySlots = computeMaxHistorySlots(historyCount)
const interruptHintActive = interruptArmedUntil !== null && interruptArmedUntil > Date.now()
const handleReorderWorks = (nextWorkIds: string[]) => {
setOrderedWorkIds((current) => (areSameIds(current, nextWorkIds) ? current : nextWorkIds))
}
const handleEscapePress = () => {
const activeRun = studio.latestRun
const runIsInterruptible = activeRun && (activeRun.status === 'pending' || activeRun.status === 'running')
if (!runIsInterruptible) {
setInterruptArmedUntil(null)
return
}
const now = Date.now()
if (interruptArmedUntil && interruptArmedUntil > now) {
setInterruptArmedUntil(null)
void studio.cancelCurrentRun('Cancelled by double-escape in Plot Studio')
return
}
setInterruptArmedUntil(now + 3000)
window.setTimeout(() => {
setInterruptArmedUntil((current) => (current && current <= Date.now() ? null : current))
}, 3100)
}
return {
studio,
selected,
orderedWorkSummaries,
effectiveSelectedWorkId,
historyCountLabel,
maxHistorySlots,
confirmExitOpen,
setConfirmExitOpen,
interruptHintActive,
setSelectedWorkId,
handleReorderWorks,
handleEscapePress,
}
}