diff --git a/client/src/client-config.tsx b/client/src/client-config.tsx index 40a3889f5..57c81ab02 100644 --- a/client/src/client-config.tsx +++ b/client/src/client-config.tsx @@ -15,6 +15,7 @@ import { } from './colors' import { BrightButton, Button } from './components/button' import { useKeyboard } from './util/keyboard' +import { SectionHeader } from './components/section-header' export type ClientConfig = typeof DEFAULT_CONFIG @@ -76,6 +77,38 @@ const configDescription: Record = { colors: '' } +const configCategories: Record = { + // Game Visualization + showAllIndicators: 'Game Visualization', + showAllRobotRadii: 'Game Visualization', + showSRPOutlines: 'Game Visualization', + showSRPText: 'Game Visualization', + showMapXY: 'Game Visualization', + enableFancyPaint: 'Game Visualization', + + // Robot Display & Status + showHealthBars: 'Robot Display & Status', + showPaintBars: 'Robot Display & Status', + showExceededBytecode: 'Robot Display & Status', + focusRobotTurn: 'Robot Display & Status', + + // Markers & Paint Debugging + showTimelineMarkers: 'Markers & Paint Debugging', + showPaintMarkers: 'Markers & Paint Debugging', + + // Game Playback + streamRunnerGames: 'Game Playback', + populateRunnerGames: 'Game Playback', + + // Developer & Validation Tools + profileGames: 'Developer Tools', + validateMaps: 'Developer Tools', + + // Mischellanous + resolutionScale: '', + colors: '' +} + export function getDefaultConfig(): ClientConfig { const config: ClientConfig = { ...DEFAULT_CONFIG } for (const key in config) { @@ -100,8 +133,14 @@ export const ConfigPage: React.FC = (props) => { const context = useAppContext() const keyboard = useKeyboard() + const [input, setInput] = useState('') + const [isSearchFocused, setIsSearchFocused] = useState(false) + const [shouldForceOpen, setShouldForceOpen] = useState(false) + + const sidebarColor = context.state.config.colors[Colors.SIDEBAR_BACKGROUND] + useEffect(() => { - if (context.state.disableHotkeys) return + if (context.state.disableHotkeys || isSearchFocused) return if (keyboard.keyCode === 'KeyF') context.updateConfigValue('focusRobotTurn', !context.state.config.focusRobotTurn) @@ -111,15 +150,77 @@ export const ConfigPage: React.FC = (props) => { if (!props.open) return null + const configEntries = Object.keys(DEFAULT_CONFIG).map((key) => ({ + key: key as keyof ClientConfig, + category: configCategories[key as keyof ClientConfig], + value: DEFAULT_CONFIG[key as keyof ClientConfig] + })) + + const filteredEntries = configEntries.filter(({ key, category }) => { + if (!input.trim()) return true + const s = input.toLowerCase() + const description = configDescription[key]?.toLowerCase() || '' + return key.toLowerCase().includes(s) || description.includes(s) + }) + + const groupedCategories = filteredEntries.reduce( + (acc, { category, key }) => { + if (!acc[category]) acc[category] = [] + acc[category].push(key) + return acc + }, + {} as Record> + ) + return (
Edit Client Config:
- {Object.entries(DEFAULT_CONFIG).map(([k, v]) => { - const key = k as keyof ClientConfig - if (typeof v === 'string') return - if (typeof v === 'boolean') return - if (typeof v === 'number') return - })} + { + setInput(e.target.value) + setShouldForceOpen(true) + }} + onFocus={(e) => { + setIsSearchFocused(true) + setTimeout(() => e.target.select(), 0) + }} + onBlur={() => { + setIsSearchFocused(false) + setShouldForceOpen(false) + }} + autoCapitalize="off" + autoCorrect="off" + autoComplete="off" + style={{ + backgroundColor: sidebarColor + }} + /> + {Object.entries(groupedCategories) + .filter(([category]) => category !== '') + .map(([category, keys]) => ( + + ))} + + {groupedCategories[''] && ( +
+ {groupedCategories[''].map((key) => { + const value = DEFAULT_CONFIG[key] + if (key === 'colors') return null + if (typeof value === 'number') return + return null + })} +
+ )}
@@ -251,6 +352,44 @@ const SingleColorPicker = (props: { displayName: string; colorName: Colors }) => ) } +const ConfigCategoryDropdown: React.FC<{ + title: string + keys: Array + forceOpen?: boolean + hasInput?: boolean +}> = ({ title, keys, forceOpen, hasInput }) => { + const [open, setOpen] = useState(false) + + useEffect(() => { + if (forceOpen) { + setOpen(true) + } else if (!hasInput) { + setOpen(false) + } + }, [forceOpen, hasInput]) + + const onClick = () => { + setOpen(!open) + } + return ( +
+
}> + + {open && ( +
+ {keys.map((key) => { + const value = DEFAULT_CONFIG[key] + if (typeof value === 'boolean') return + if (typeof value === 'number') return + if (typeof value === 'string') return + return null + })} +
+ )} + + ) +} + const ConfigBooleanElement: React.FC<{ configKey: keyof ClientConfig }> = ({ configKey }) => { const context = useAppContext() const value = context.state.config[configKey] as boolean