1- import { useEffect , useState } from 'react' ;
1+ import { useState } from 'react' ;
22import { ItemIcon } from './ItemIcon' ;
33import { MaterialSelector } from './MaterialSelector' ;
4+ import type { Playback } from './VisualEditor' ;
45import type { VisualAnimation , VisualItem , VisualJavaMenu } from '../../editor/model' ;
56
67interface AnimationEditorProps {
78 menu : VisualJavaMenu ;
89 serverVersion : string | null ;
910 onChange : ( menu : VisualJavaMenu ) => void ;
11+ playback : Playback ;
1012}
1113
12- /** Ticks per frame, as the plugin counts them. A tick is 50ms. */
14+ /** Ticks per frame, as the plugin counts them. */
1315const DEFAULT_INTERVAL = 2 ;
14- const TICK_MS = 50 ;
1516
1617/**
1718 * The animation strip: one card per frame, played back at the configured rate
1819 * so the sequence can be judged without opening the game.
1920 */
20- export function AnimationEditor ( { menu, serverVersion, onChange } : AnimationEditorProps ) {
21+ export function AnimationEditor ( { menu, serverVersion, onChange, playback } : AnimationEditorProps ) {
2122 const names = Object . keys ( menu . animations ) ;
2223 const [ selected , setSelected ] = useState < string | null > ( names [ 0 ] ?? null ) ;
23- const [ playing , setPlaying ] = useState ( false ) ;
24- const [ frameIndex , setFrameIndex ] = useState ( 0 ) ;
24+ const [ picked , setPicked ] = useState ( 0 ) ;
2525
2626 const active = selected === null ? undefined : menu . animations [ selected ] ;
2727 const frames = active === undefined ? [ ] : Object . entries ( active . frames ) ;
2828
29- useEffect ( ( ) => {
30- if ( ! playing || active === undefined || frames . length < 2 ) {
31- return ;
32- }
33-
34- const period = Math . max ( 1 , active . interval ) * TICK_MS ;
35- const timer = setInterval ( ( ) => setFrameIndex ( current => ( current + 1 ) % frames . length ) , period ) ;
36-
37- return ( ) => clearInterval ( timer ) ;
38- } , [ playing , active , frames . length ] ) ;
29+ // While it plays, the strip follows the shared clock; otherwise it shows
30+ // whichever frame is being edited.
31+ const frameIndex =
32+ playback . playing && frames . length > 0
33+ ? Math . floor ( playback . tick / Math . max ( 1 , active ?. interval ?? DEFAULT_INTERVAL ) ) % frames . length
34+ : Math . min ( picked , Math . max ( 0 , frames . length - 1 ) ) ;
3935
4036 if ( names . length === 0 ) {
4137 return (
@@ -73,7 +69,7 @@ export function AnimationEditor({ menu, serverVersion, onChange }: AnimationEdit
7369 value = { selected ?? '' }
7470 onChange = { event => {
7571 setSelected ( event . target . value ) ;
76- setFrameIndex ( 0 ) ;
72+ setPicked ( 0 ) ;
7773 } }
7874 aria-label = "Animation"
7975 >
@@ -84,10 +80,10 @@ export function AnimationEditor({ menu, serverVersion, onChange }: AnimationEdit
8480 ) ) }
8581 </ select >
8682
87- < button type = "button" className = "btn btn-secondary btn-sm" onClick = { ( ) => setPlaying ( true ) } title = "Play" >
83+ < button type = "button" className = "btn btn-secondary btn-sm" onClick = { playback . play } title = "Play" >
8884 ▶️
8985 </ button >
90- < button type = "button" className = "btn btn-secondary btn-sm" onClick = { ( ) => setPlaying ( false ) } title = "Stop" >
86+ < button type = "button" className = "btn btn-secondary btn-sm" onClick = { playback . stop } title = "Stop" >
9187 ⏹️
9288 </ button >
9389 < button
@@ -131,12 +127,12 @@ export function AnimationEditor({ menu, serverVersion, onChange }: AnimationEdit
131127 { frames . map ( ( [ key , frame ] , index ) => (
132128 < div
133129 key = { key }
134- className = { `timeline-frame${ playing && index === frameIndex ? ' active' : '' } ` }
130+ className = { `timeline-frame${ index === frameIndex ? ' active' : '' } ` }
135131 title = { `Frame ${ index + 1 } : ${ frame . material } ` }
136132 role = "button"
137133 tabIndex = { 0 }
138- onClick = { ( ) => setFrameIndex ( index ) }
139- onKeyDown = { event => event . key === 'Enter' && setFrameIndex ( index ) }
134+ onClick = { ( ) => setPicked ( index ) }
135+ onKeyDown = { event => event . key === 'Enter' && setPicked ( index ) }
140136 >
141137 < div className = "timeline-frame-number" > { index + 1 } </ div >
142138 < div className = "timeline-frame-preview" >
@@ -157,7 +153,7 @@ export function AnimationEditor({ menu, serverVersion, onChange }: AnimationEdit
157153 const next = { ...active . frames } ;
158154 delete next [ frames [ frameIndex ] [ 0 ] ] ;
159155 update ( { ...active , frames : next } ) ;
160- setFrameIndex ( 0 ) ;
156+ setPicked ( 0 ) ;
161157 } }
162158 />
163159 ) }
0 commit comments