@@ -13,6 +13,7 @@ import { RingBuffer } from '../../../util/ring-buffer'
1313import { ProfilerDialog } from './profiler'
1414import { GameRenderer } from '../../../playback/GameRenderer'
1515import GameRunner from '../../../playback/GameRunner'
16+ import { Resizable } from 're-resizable'
1617
1718type RunnerPageProps = {
1819 open : boolean
@@ -91,14 +92,20 @@ export const RunnerPage: React.FC<RunnerPageProps> = ({ open, scaffold }) => {
9192
9293 const MemoConsole = React . useMemo ( ( ) => < Console lines = { consoleLines } /> , [ consoleLines . effectiveLength ( ) ] )
9394
94- if ( ! open ) return null
95+ if ( open && ! nativeAPI ) return < > Run the client locally to use the runner </ >
9596
96- if ( ! nativeAPI ) return < > Run the client locally to use the runner </ >
97+ /* Keep the component mounted but hidden when !open so we retain any locally resized/edited elements */
9798
9899 const lastLogLine = consoleLines . get ( consoleLines . length ( ) - 1 )
99100 const runDisabled = ! teamA || ! teamB || maps . size === 0 || ! langVersion
100101 return (
101- < div className = { 'flex flex-col grow ' + ( scaffoldLoading ? 'opacity-50 pointer-events-none' : '' ) } >
102+ < div
103+ className = {
104+ 'flex flex-col grow' +
105+ ( scaffoldLoading ? ' opacity-50 pointer-events-none' : '' ) +
106+ ( open ? '' : ' hidden' )
107+ }
108+ >
102109 { ! setup ? (
103110 < >
104111 { error && < div className = "text-red" > { `Setup Error: ${ error } ` } </ div > }
@@ -286,7 +293,7 @@ const TeamSelector: React.FC<TeamSelectorProps> = ({ teamA, teamB, options, onCh
286293 < label > Team A</ label >
287294 < Select className = "w-full" value = { teamA ?? 'NONE' } onChange = { ( e ) => onChangeA ( e ) } >
288295 { teamA === undefined && < option value = { 'NONE' } > Select a team</ option > }
289- { [ ...options ] . map ( ( t ) => (
296+ { [ ...options ] . sort ( ) . map ( ( t ) => (
290297 < option key = { t } value = { t } >
291298 { t }
292299 </ option >
@@ -309,7 +316,7 @@ const TeamSelector: React.FC<TeamSelectorProps> = ({ teamA, teamB, options, onCh
309316 < label className = "ml-auto" > Team B</ label >
310317 < Select className = "w-full" value = { teamB ?? 'NONE' } onChange = { ( e ) => onChangeB ( e ) } >
311318 { teamB === undefined && < option value = { 'NONE' } > Select a team</ option > }
312- { [ ...options ] . map ( ( t ) => (
319+ { [ ...options ] . sort ( ) . map ( ( t ) => (
313320 < option key = { t } value = { t } >
314321 { t }
315322 </ option >
@@ -331,21 +338,36 @@ const MapSelector: React.FC<MapSelectorProps> = ({ maps, availableMaps, onSelect
331338 return (
332339 < div className = "flex flex-col mt-3" >
333340 < label > Maps</ label >
334- < div className = "flex flex-col border border-white py-1 px-1 rounded-md max-h-[190px] overflow-y-auto" >
335- { [ ...availableMaps ] . map ( ( m ) => {
336- const selected = maps . has ( m )
337- return (
338- < div
339- key = { m }
340- className = { 'cursor-pointer hover:bg-lightHighlight flex items-center justify-between' }
341- onClick = { ( ) => ( maps . has ( m ) ? onDeselect ( m ) : onSelect ( m ) ) }
342- >
343- { m }
344- < input type = { 'checkbox' } readOnly checked = { selected } className = "pointer-events-none mr-2" />
345- </ div >
346- )
347- } ) }
348- </ div >
341+ < Resizable
342+ minWidth = "100%"
343+ maxWidth = "100%"
344+ minHeight = { 50 }
345+ defaultSize = { {
346+ width : '100%' ,
347+ height : 120
348+ } }
349+ >
350+ < div className = "flex flex-col border border-white py-1 px-1 h-full rounded-md overflow-y-auto" >
351+ { [ ...availableMaps ] . sort ( ) . map ( ( m ) => {
352+ const selected = maps . has ( m )
353+ return (
354+ < div
355+ key = { m }
356+ className = { 'cursor-pointer hover:bg-lightHighlight flex items-center justify-between' }
357+ onClick = { ( ) => ( maps . has ( m ) ? onDeselect ( m ) : onSelect ( m ) ) }
358+ >
359+ { m }
360+ < input
361+ type = { 'checkbox' }
362+ readOnly
363+ checked = { selected }
364+ className = "pointer-events-none mr-2"
365+ />
366+ </ div >
367+ )
368+ } ) }
369+ </ div >
370+ </ Resizable >
349371 </ div >
350372 )
351373}
@@ -377,6 +399,11 @@ export const Console: React.FC<Props> = ({ lines }) => {
377399 setPopout ( false )
378400 GameRunner . jumpToRound ( round )
379401 GameRenderer . setSelectedRobot ( id )
402+
403+ // If turn playback is enabled, focus the robot's exact turn as well
404+ if ( GameRunner . match ?. playbackPerTurn ) {
405+ GameRunner . jumpToRobotTurn ( id )
406+ }
380407 }
381408
382409 const ConsoleRow = ( props : { index : number ; style : any } ) => {
@@ -390,7 +417,7 @@ export const Console: React.FC<Props> = ({ lines }) => {
390417 const team = found [ 1 ]
391418 const id = Number ( found [ 2 ] )
392419 const round = Number ( found [ 3 ] )
393- const ogText = found [ 4 ]
420+ const ogText = found [ 4 ] . replace ( / \n / g , ' ' )
394421
395422 return (
396423 < div className = "flex items-center gap-1 sele" style = { props . style } >
@@ -400,9 +427,7 @@ export const Console: React.FC<Props> = ({ lines }) => {
400427 >
401428 { `[Team ${ team } , ID #${ id } , Round ${ round } ]` }
402429 </ span >
403- < span className = { getLineClass ( lines . get ( props . index ) ! ) + ' text-xs whitespace-nowrap' } >
404- { ogText }
405- </ span >
430+ < span className = { getLineClass ( lines . get ( props . index ) ! ) + ' text-xs whitespace-pre' } > { ogText } </ span >
406431 </ div >
407432 )
408433 }
0 commit comments