// Which loaded module an offset is into: the game's own executable (the // usual case, and the default) or one of the libraries it loads. Shows // nothing at all when the game has only the one, so most people never see it. import { useEffect, useState } from 'react'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { loadedModules } from '../lib/locations.ts'; import type { Location } from '../lib/locations.ts'; import { useWorkbench } from './workbench.tsx'; // Radix selects cannot hold an empty value, so the executable gets a stand-in. const MAIN = ':main'; type InModule = Extract; /** For a location editor: the same location with the module set, or cleared for the executable. */ export function ModuleChoice(props: { loc: InModule; onChange: (loc: Location) => void }) { const { ctx } = useWorkbench(); const value = props.loc.module; const pick = (module: string | undefined) => { const { module: _old, ...rest } = props.loc; props.onChange(module === undefined ? rest : { ...rest, module }); }; const [names, setNames] = useState([]); useEffect(() => { let canceled = false; loadedModules(ctx) .then((list) => !canceled && setNames(list.map((m) => m.module).filter((n): n is string => n !== undefined))) .catch(() => {}); return () => { canceled = true; }; }, [ctx]); // A module the cheat names but the game has not loaded (yet) must stay selectable, or opening the editor would silently change it. const all = value !== undefined && !names.includes(value) ? [...names, value] : names; if (all.length === 0) return null; return (
Inside
); }