mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Small refactoring
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
Module['preRun'] = () => {
|
Module["preRun"] = () => {
|
||||||
ENV = process.env;
|
ENV = process.env;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ThemeProvider } from "@/components/theme-provider";
|
import { ThemeProvider } from "@/components/theme-provider";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
import { HashRouter, Route, Routes, Navigate } from "react-router-dom";
|
import { HashRouter, Route, Routes, Navigate } from "react-router-dom";
|
||||||
import { Playground } from "./Playground";
|
import { Playground } from "./playground";
|
||||||
import { LandingPage } from "./LandingPage";
|
import { LandingPage } from "./landing-page";
|
||||||
|
|
||||||
import "@fontsource/inter/100.css";
|
import "@fontsource/inter/100.css";
|
||||||
import "@fontsource/inter/200.css";
|
import "@fontsource/inter/200.css";
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useState, useRef, useReducer } from "react";
|
import React from "react";
|
||||||
import { Output } from "@/components/output";
|
|
||||||
|
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Output } from "@/components/output";
|
||||||
|
|
||||||
import { Emulator, EmulationState } from "./emulator";
|
import { Emulator, EmulationState } from "./emulator";
|
||||||
import { Filesystem, setupFilesystem } from "./filesystem";
|
import { Filesystem, setupFilesystem } from "./filesystem";
|
||||||
@@ -13,7 +12,7 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
|
|
||||||
import { createDefaultSettings } from "./settings";
|
import { createDefaultSettings, Settings } from "./settings";
|
||||||
import { SettingsMenu } from "@/components/settings-menu";
|
import { SettingsMenu } from "@/components/settings-menu";
|
||||||
|
|
||||||
import { PlayFill, StopFill, GearFill, PauseFill } from "react-bootstrap-icons";
|
import { PlayFill, StopFill, GearFill, PauseFill } from "react-bootstrap-icons";
|
||||||
@@ -30,176 +29,229 @@ import {
|
|||||||
DrawerHeader,
|
DrawerHeader,
|
||||||
DrawerTitle,
|
DrawerTitle,
|
||||||
} from "@/components/ui/drawer";
|
} from "@/components/ui/drawer";
|
||||||
import { FilesystemExplorer } from "./FilesystemExplorer";
|
import { FilesystemExplorer } from "./filesystem-explorer";
|
||||||
|
|
||||||
export function Playground() {
|
interface PlaygroundProps {}
|
||||||
const output = useRef<Output>(null);
|
interface PlaygroundState {
|
||||||
const [settings, setSettings] = useState(createDefaultSettings());
|
settings: Settings;
|
||||||
const [emulator, setEmulator] = useState<Emulator | null>(null);
|
filesystemPromise: Promise<Filesystem> | null;
|
||||||
const [drawerOpen, setDrawerOpen] = useState<boolean>(false);
|
filesystem: Filesystem | null;
|
||||||
const [filesystem, setFilesystem] = useState<Filesystem | null>(null);
|
emulator: Emulator | null;
|
||||||
const [filesystemPromise, setFilesystemPromise] =
|
drawerOpen: boolean;
|
||||||
useState<Promise<Filesystem> | null>(null);
|
}
|
||||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
||||||
|
|
||||||
async function resetFilesys() {
|
export class Playground extends React.Component<
|
||||||
const fs = await initFilesys();
|
PlaygroundProps,
|
||||||
await fs.delete();
|
PlaygroundState
|
||||||
|
> {
|
||||||
|
private output: React.RefObject<Output | null>;
|
||||||
|
|
||||||
setFilesystemPromise(null);
|
constructor(props: PlaygroundProps) {
|
||||||
setFilesystem(null);
|
super(props);
|
||||||
setDrawerOpen(false);
|
|
||||||
|
|
||||||
output.current?.clear();
|
this.output = React.createRef();
|
||||||
|
|
||||||
|
this.start = this.start.bind(this);
|
||||||
|
this.resetFilesys = this.resetFilesys.bind(this);
|
||||||
|
this.createEmulator = this.createEmulator.bind(this);
|
||||||
|
this.toggleEmulatorState = this.toggleEmulatorState.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
settings: createDefaultSettings(),
|
||||||
|
filesystemPromise: null,
|
||||||
|
filesystem: null,
|
||||||
|
emulator: null,
|
||||||
|
drawerOpen: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function initFilesys() {
|
async resetFilesys() {
|
||||||
if (filesystemPromise) {
|
if (!this.state.filesystem) {
|
||||||
return filesystemPromise;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.state.filesystem.delete();
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
filesystemPromise: null,
|
||||||
|
filesystem: null,
|
||||||
|
drawerOpen: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.output.current?.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
initFilesys() {
|
||||||
|
if (this.state.filesystemPromise) {
|
||||||
|
return this.state.filesystemPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
const promise = new Promise<Filesystem>((resolve) => {
|
const promise = new Promise<Filesystem>((resolve) => {
|
||||||
logLine("Loading filesystem...");
|
this.logLine("Loading filesystem...");
|
||||||
setupFilesystem((current, total, file) => {
|
setupFilesystem((current, total, file) => {
|
||||||
logLine(`Processing filesystem (${current}/${total}): ${file}`);
|
this.logLine(`Processing filesystem (${current}/${total}): ${file}`);
|
||||||
}).then(resolve);
|
}).then(resolve);
|
||||||
});
|
});
|
||||||
|
|
||||||
promise.then(setFilesystem);
|
promise.then((filesystem) => this.setState({ filesystem }));
|
||||||
setFilesystemPromise(promise);
|
this.setState({ filesystemPromise: promise });
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function start() {
|
setDrawerOpen(drawerOpen: boolean) {
|
||||||
await initFilesys();
|
this.setState({ drawerOpen });
|
||||||
setDrawerOpen(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function logLine(line: string) {
|
async start() {
|
||||||
output.current?.logLine(line);
|
await this.initFilesys();
|
||||||
|
this.setDrawerOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function logLines(lines: string[]) {
|
logLine(line: string) {
|
||||||
output.current?.logLines(lines);
|
this.output.current?.logLine(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmulatorPaused() {
|
logLines(lines: string[]) {
|
||||||
return emulator && emulator.getState() == EmulationState.Paused;
|
this.output.current?.logLines(lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleEmulatorState() {
|
isEmulatorPaused() {
|
||||||
if (isEmulatorPaused()) {
|
return (
|
||||||
emulator?.resume();
|
this.state.emulator &&
|
||||||
|
this.state.emulator.getState() == EmulationState.Paused
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleEmulatorState() {
|
||||||
|
if (this.isEmulatorPaused()) {
|
||||||
|
this.state.emulator?.resume();
|
||||||
} else {
|
} else {
|
||||||
emulator?.pause();
|
this.state.emulator?.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createEmulator(userFile: string) {
|
async createEmulator(userFile: string) {
|
||||||
emulator?.stop();
|
this.state.emulator?.stop();
|
||||||
output.current?.clear();
|
this.output.current?.clear();
|
||||||
|
|
||||||
setDrawerOpen(false);
|
this.setDrawerOpen(false);
|
||||||
|
|
||||||
logLine("Starting emulation...");
|
this.logLine("Starting emulation...");
|
||||||
|
|
||||||
if (filesystemPromise) {
|
if (this.state.filesystemPromise) {
|
||||||
await filesystemPromise;
|
await this.state.filesystemPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
const new_emulator = new Emulator(logLines, (_) => forceUpdate());
|
const new_emulator = new Emulator(
|
||||||
new_emulator.onTerminate().then(() => setEmulator(null));
|
(l) => this.logLines(l),
|
||||||
setEmulator(new_emulator);
|
(_) => this.forceUpdate(),
|
||||||
|
);
|
||||||
|
new_emulator.onTerminate().then(() => this.setState({ emulator: null }));
|
||||||
|
|
||||||
new_emulator.start(settings, userFile);
|
this.setState({ emulator: new_emulator });
|
||||||
|
|
||||||
|
new_emulator.start(this.state.settings, userFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
render() {
|
||||||
<>
|
return (
|
||||||
<Header
|
<>
|
||||||
title="Playground - Sogen"
|
<Header
|
||||||
description="Playground to test and run Sogen, the Windows user space emulator, right in your browser."
|
title="Playground - Sogen"
|
||||||
/>
|
description="Playground to test and run Sogen, the Windows user space emulator, right in your browser."
|
||||||
<div className="h-[100dvh] flex flex-col">
|
/>
|
||||||
<header className="flex shrink-0 items-center gap-2 border-b p-2 overflow-y-auto">
|
<div className="h-[100dvh] flex flex-col">
|
||||||
<Button size="sm" className="fancy" onClick={start}>
|
<header className="flex shrink-0 items-center gap-2 border-b p-2 overflow-y-auto">
|
||||||
<PlayFill /> <span>Start</span>
|
<Button size="sm" className="fancy" onClick={this.start}>
|
||||||
</Button>
|
<PlayFill /> <span>Start</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
disabled={!emulator}
|
disabled={!this.state.emulator}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="fancy"
|
className="fancy"
|
||||||
onClick={() => emulator?.stop()}
|
onClick={() => this.state.emulator?.stop()}
|
||||||
>
|
>
|
||||||
<StopFill /> <span className="hidden sm:inline">Stop</span>
|
<StopFill /> <span className="hidden sm:inline">Stop</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={!emulator}
|
disabled={!this.state.emulator}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="fancy"
|
className="fancy"
|
||||||
onClick={toggleEmulatorState}
|
onClick={this.toggleEmulatorState}
|
||||||
>
|
>
|
||||||
{isEmulatorPaused() ? (
|
{this.isEmulatorPaused() ? (
|
||||||
<>
|
<>
|
||||||
<PlayFill /> <span className="hidden sm:inline">Resume</span>
|
<PlayFill /> <span className="hidden sm:inline">Resume</span>
|
||||||
</>
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<PauseFill /> <span className="hidden sm:inline">Pause</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button size="sm" variant="secondary" className="fancy">
|
||||||
|
<GearFill />{" "}
|
||||||
|
<span className="hidden sm:inline">Settings</span>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<SettingsMenu
|
||||||
|
settings={this.state.settings}
|
||||||
|
onChange={(s) => this.setState({ settings: s })}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
{!this.state.filesystem ? (
|
||||||
|
<></>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<Drawer
|
||||||
<PauseFill /> <span className="hidden sm:inline">Pause</span>
|
open={this.state.drawerOpen}
|
||||||
</>
|
onOpenChange={(o) => this.setState({ drawerOpen: o })}
|
||||||
|
>
|
||||||
|
<DrawerContent>
|
||||||
|
<DrawerHeader>
|
||||||
|
<DrawerTitle className="hidden">
|
||||||
|
Filesystem Explorer
|
||||||
|
</DrawerTitle>
|
||||||
|
<DrawerDescription className="hidden">
|
||||||
|
Filesystem Explorer
|
||||||
|
</DrawerDescription>
|
||||||
|
</DrawerHeader>
|
||||||
|
<DrawerFooter>
|
||||||
|
<FilesystemExplorer
|
||||||
|
filesystem={this.state.filesystem}
|
||||||
|
runFile={this.createEmulator}
|
||||||
|
resetFilesys={this.resetFilesys}
|
||||||
|
path={["c"]}
|
||||||
|
/>
|
||||||
|
</DrawerFooter>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
)}
|
)}
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Popover>
|
<div className="text-right flex-1">
|
||||||
<PopoverTrigger asChild>
|
<StatusIndicator
|
||||||
<Button size="sm" variant="secondary" className="fancy">
|
state={
|
||||||
<GearFill /> <span className="hidden sm:inline">Settings</span>
|
this.state.emulator
|
||||||
</Button>
|
? this.state.emulator.getState()
|
||||||
</PopoverTrigger>
|
: EmulationState.Stopped
|
||||||
<PopoverContent>
|
}
|
||||||
<SettingsMenu settings={settings} onChange={setSettings} />
|
/>
|
||||||
</PopoverContent>
|
</div>
|
||||||
</Popover>
|
</header>
|
||||||
|
<div className="flex flex-1 flex-col gap-2 p-2 overflow-auto">
|
||||||
{!filesystem ? (
|
<Output ref={this.output} />
|
||||||
<></>
|
|
||||||
) : (
|
|
||||||
<Drawer open={drawerOpen} onOpenChange={setDrawerOpen}>
|
|
||||||
<DrawerContent>
|
|
||||||
<DrawerHeader>
|
|
||||||
<DrawerTitle className="hidden">
|
|
||||||
Filesystem Explorer
|
|
||||||
</DrawerTitle>
|
|
||||||
<DrawerDescription className="hidden">
|
|
||||||
Filesystem Explorer
|
|
||||||
</DrawerDescription>
|
|
||||||
</DrawerHeader>
|
|
||||||
<DrawerFooter>
|
|
||||||
<FilesystemExplorer
|
|
||||||
filesystem={filesystem}
|
|
||||||
runFile={createEmulator}
|
|
||||||
resetFilesys={resetFilesys}
|
|
||||||
path={["c"]}
|
|
||||||
/>
|
|
||||||
</DrawerFooter>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="text-right flex-1">
|
|
||||||
<StatusIndicator
|
|
||||||
state={emulator ? emulator.getState() : EmulationState.Stopped}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
|
||||||
<div className="flex flex-1 flex-col gap-2 p-2 overflow-auto">
|
|
||||||
<Output ref={output} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
</>
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ export class SettingsMenu extends React.Component<SettingsMenuProps, Settings> {
|
|||||||
this.setState(() => settings);
|
this.setState(() => settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate(_: SettingsMenuProps, oldSettings: Settings) {
|
||||||
this.props.onChange(this.state);
|
if (JSON.stringify(oldSettings) !== JSON.stringify(this.state)) {
|
||||||
|
this.props.onChange(this.state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ export class FilesystemExplorer extends React.Component<
|
|||||||
_renderBreadcrumbElements() {
|
_renderBreadcrumbElements() {
|
||||||
const elements = generateBreadcrumbElements(this.state.path);
|
const elements = generateBreadcrumbElements(this.state.path);
|
||||||
|
|
||||||
return elements.map((e, index) => {
|
const nodes = elements.map((e, index) => {
|
||||||
if (index == this.state.path.length) {
|
if (index == this.state.path.length) {
|
||||||
return (
|
return (
|
||||||
<BreadcrumbItem key={`breadcrumb-item-${index}`}>
|
<BreadcrumbItem key={`breadcrumb-item-${index}`}>
|
||||||
@@ -498,16 +498,20 @@ export class FilesystemExplorer extends React.Component<
|
|||||||
|
|
||||||
const navigate = () => this.setState({ path: e.targetPath });
|
const navigate = () => this.setState({ path: e.targetPath });
|
||||||
return (
|
return (
|
||||||
<>
|
<BreadcrumbItem key={`breadcrumb-item-${index}`}>
|
||||||
<BreadcrumbItem key={`breadcrumb-item-${index}`}>
|
<BreadcrumbLink key={`breadcrumb-link-${index}`} onClick={navigate}>
|
||||||
<BreadcrumbLink key={`breadcrumb-link-${index}`} onClick={navigate}>
|
{e.node}
|
||||||
{e.node}
|
</BreadcrumbLink>
|
||||||
</BreadcrumbLink>
|
</BreadcrumbItem>
|
||||||
</BreadcrumbItem>
|
|
||||||
<BreadcrumbSeparator key={`breadcrumb-separator-${index}`} />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
...nodes.map((n, index) => [
|
||||||
|
n,
|
||||||
|
<BreadcrumbSeparator key={`breadcrumb-separator-${index}`} />,
|
||||||
|
]),
|
||||||
|
].slice(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderBreadCrumb() {
|
_renderBreadCrumb() {
|
||||||
Reference in New Issue
Block a user