feat: Icons, better UI
|
@ -1,7 +1,7 @@
|
||||||
@import "./status.less";
|
@import "./status.less";
|
||||||
@import "./emulator.less";
|
@import "./emulator.less";
|
||||||
@import "./info.less";
|
@import "./info.less";
|
||||||
@import "./floppy.less";
|
@import "./settings.less";
|
||||||
|
|
||||||
/* GENERAL RESETS */
|
/* GENERAL RESETS */
|
||||||
|
|
||||||
|
@ -40,13 +40,35 @@ section {
|
||||||
width: 75%;
|
width: 75%;
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
min-width: 400px;
|
min-width: 400px;
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
img {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link > img,
|
||||||
|
.btn > img {
|
||||||
|
height: 24px;
|
||||||
|
margin-top: -3px;
|
||||||
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.windows95 {
|
.windows95 {
|
||||||
|
* {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
*:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
nav .nav-link,
|
nav .nav-link,
|
||||||
nav .nav-logo {
|
nav .nav-logo {
|
||||||
height: 30px;
|
height: 33px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav .nav-logo img {
|
nav .nav-logo img {
|
||||||
|
@ -61,6 +83,11 @@ section {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
height: 40px;
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn:focus {
|
.btn:focus {
|
||||||
border-color: #fff #000 #000 #fff;
|
border-color: #fff #000 #000 #fff;
|
||||||
outline: 5px auto -webkit-focus-ring-color;
|
outline: 5px auto -webkit-focus-ring-color;
|
||||||
|
|
|
@ -13,3 +13,9 @@
|
||||||
#file-input {
|
#file-input {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings {
|
||||||
|
legend > img {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
111
src/renderer/card-drive.tsx
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { shell } from "electron";
|
||||||
|
|
||||||
|
interface CardDriveProps {
|
||||||
|
showDiskImage: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CardDriveState {}
|
||||||
|
|
||||||
|
export class CardDrive extends React.Component<CardDriveProps, CardDriveState> {
|
||||||
|
constructor(props: CardDriveProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
let advice: JSX.Element | null = null;
|
||||||
|
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
advice = this.renderAdviceWindows();
|
||||||
|
} else if (process.platform === "darwin") {
|
||||||
|
advice = this.renderAdviceMac();
|
||||||
|
} else {
|
||||||
|
advice = this.renderAdviceLinux();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<div className="card settings">
|
||||||
|
<div className="card-header">
|
||||||
|
<h2 className="card-title">
|
||||||
|
<img src="../../static/drive.png" />
|
||||||
|
Modify C: Drive
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<p>
|
||||||
|
windows95 (this app) uses a raw disk image. Windows 95 (the
|
||||||
|
operating system) is fragile, so adding or removing files is
|
||||||
|
risky.
|
||||||
|
</p>
|
||||||
|
{advice}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderAdviceWindows(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<fieldset>
|
||||||
|
<legend>Changing the disk on Windows</legend>
|
||||||
|
<p>
|
||||||
|
Windows 10 cannot mount raw disk images (ironically, macOS and Linux
|
||||||
|
can). However, tools exist that let you mount this drive, like the
|
||||||
|
freeware tool{" "}
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={() =>
|
||||||
|
shell.openExternal(
|
||||||
|
"https://www.osforensics.com/tools/mount-disk-images.html"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
OSFMount
|
||||||
|
</a>
|
||||||
|
. I am not affiliated with it, so please use it at your own risk.
|
||||||
|
</p>
|
||||||
|
{this.renderMountButton("Windows Explorer")}
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderAdviceMac(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<fieldset>
|
||||||
|
<legend>Changing the disk on macOS</legend>
|
||||||
|
<p>
|
||||||
|
macOS can mount the disk image directly. Click the button below to see
|
||||||
|
the disk image in Finder. Then, double-click the image to mount it.
|
||||||
|
</p>
|
||||||
|
{this.renderMountButton("Finder")}
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderAdviceLinux(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<fieldset>
|
||||||
|
<legend>Changing the disk on Linux</legend>
|
||||||
|
<p>
|
||||||
|
There are plenty of tools that enable Linux users to mount and modify
|
||||||
|
disk images. The disk image used by windows95 is a raw "img" disk
|
||||||
|
image and can probably be mounted using the <code>mount</code> tool,
|
||||||
|
which is likely installed on your machine.
|
||||||
|
</p>
|
||||||
|
{this.renderMountButton("file viewer")}
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderMountButton(explorer: string) {
|
||||||
|
return (
|
||||||
|
<button className="btn" onClick={this.props.showDiskImage}>
|
||||||
|
<img src="../../static/show-disk-image.png" />
|
||||||
|
<span>Show disk image in {explorer}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,78 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
export interface CardFloppyProps {
|
|
||||||
setFloppyPath: (path: string) => void;
|
|
||||||
floppyPath?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CardFloppy extends React.Component<CardFloppyProps, {}> {
|
|
||||||
constructor(props: CardFloppyProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.onChange = this.onChange.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
return (
|
|
||||||
<section>
|
|
||||||
<div className="card">
|
|
||||||
<div className="card-header">
|
|
||||||
<h2 className="card-title">Floppy Drive</h2>
|
|
||||||
</div>
|
|
||||||
<div className="card-body">
|
|
||||||
<input
|
|
||||||
id="floppy-input"
|
|
||||||
type="file"
|
|
||||||
onChange={this.onChange}
|
|
||||||
style={{ display: "none" }}
|
|
||||||
/>
|
|
||||||
<p>
|
|
||||||
windows95 comes with a virtual floppy drive. If you have floppy
|
|
||||||
disk images in the "img" format, you can mount them here.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Back in the 90s and before CD-ROM became a popular format,
|
|
||||||
software was typically distributed on floppy disks. Some
|
|
||||||
developers have since released their apps or games for free,
|
|
||||||
usually on virtual floppy disks using the "img" format.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Once you've mounted a disk image, you might have to reboot your
|
|
||||||
virtual windows95 machine from scratch.
|
|
||||||
</p>
|
|
||||||
<p id="floppy-path">
|
|
||||||
{this.props.floppyPath
|
|
||||||
? `Inserted Floppy Disk: ${this.props.floppyPath}`
|
|
||||||
: `No floppy mounted`}
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
id="floppy-select"
|
|
||||||
className="btn"
|
|
||||||
onClick={() =>
|
|
||||||
(document.querySelector("#floppy-input") as any).click()
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Mount floppy disk
|
|
||||||
</button>
|
|
||||||
<button id="floppy-reboot" className="btn">
|
|
||||||
Reboot from scratch
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onChange(event: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
const floppyFile =
|
|
||||||
event.target.files && event.target.files.length > 0
|
|
||||||
? event.target.files[0]
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (floppyFile) {
|
|
||||||
this.props.setFloppyPath(floppyFile.path);
|
|
||||||
} else {
|
|
||||||
console.log(`Floppy: Input changed but no file selected`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
161
src/renderer/card-settings.tsx
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import * as fs from "fs-extra";
|
||||||
|
|
||||||
|
import { CONSTANTS } from "../constants";
|
||||||
|
|
||||||
|
interface CardSettingsProps {
|
||||||
|
bootFromScratch: () => void;
|
||||||
|
setFloppy: (file: File) => void;
|
||||||
|
floppy?: File;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CardSettingsState {
|
||||||
|
isStateReset: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CardSettings extends React.Component<
|
||||||
|
CardSettingsProps,
|
||||||
|
CardSettingsState
|
||||||
|
> {
|
||||||
|
constructor(props: CardSettingsProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.onChangeFloppy = this.onChangeFloppy.bind(this);
|
||||||
|
this.onResetState = this.onResetState.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isStateReset: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<div className="card settings">
|
||||||
|
<div className="card-header">
|
||||||
|
<h2 className="card-title">
|
||||||
|
<img src="../../static/settings.png" />
|
||||||
|
Settings
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
{this.renderFloppy()}
|
||||||
|
<hr />
|
||||||
|
{this.renderState()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderFloppy() {
|
||||||
|
const { floppy } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
<img src="../../static/floppy.png" />
|
||||||
|
Floppy
|
||||||
|
</legend>
|
||||||
|
<input
|
||||||
|
id="floppy-input"
|
||||||
|
type="file"
|
||||||
|
onChange={this.onChangeFloppy}
|
||||||
|
style={{ display: "none" }}
|
||||||
|
/>
|
||||||
|
<p>
|
||||||
|
windows95 comes with a virtual floppy drive. It can mount floppy disk
|
||||||
|
images in the "img" format.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Back in the 90s and before CD-ROMs became a popular, software was
|
||||||
|
typically distributed on floppy disks. Some developers have since
|
||||||
|
released their apps or games for free, usually on virtual floppy disks
|
||||||
|
using the "img" format.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Once you've mounted a disk image, you might have to boot your virtual
|
||||||
|
windows95 machine from scratch.
|
||||||
|
</p>
|
||||||
|
<p id="floppy-path">
|
||||||
|
{floppy
|
||||||
|
? `Inserted Floppy Disk: ${floppy.path}`
|
||||||
|
: `No floppy mounted`}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
className="btn"
|
||||||
|
onClick={() =>
|
||||||
|
(document.querySelector("#floppy-input") as any).click()
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<img src="../../static/select-floppy.png" />
|
||||||
|
<span>Mount floppy disk</span>
|
||||||
|
</button>
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderState() {
|
||||||
|
const { isStateReset } = this.state;
|
||||||
|
const { bootFromScratch } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
<img src="../../static/reset.png" />
|
||||||
|
Reset machine state
|
||||||
|
</legend>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
windows95 stores changes to your machine (like saved files) in a
|
||||||
|
state file. If you encounter any trouble, you can reset your state
|
||||||
|
or boot Windows 95 from scratch.{" "}
|
||||||
|
<strong>All your changes will be lost.</strong>
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
className="btn"
|
||||||
|
onClick={this.onResetState}
|
||||||
|
disabled={isStateReset}
|
||||||
|
style={{ marginRight: "5px" }}
|
||||||
|
>
|
||||||
|
<img src="../../static/reset-state.png" />
|
||||||
|
{isStateReset ? "State reset" : "Reset state"}
|
||||||
|
</button>
|
||||||
|
<button className="btn" onClick={bootFromScratch}>
|
||||||
|
<img src="../../static/boot-fresh.png" />
|
||||||
|
Boot from scratch
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a change in the floppy input
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
private onChangeFloppy(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
const floppyFile =
|
||||||
|
event.target.files && event.target.files.length > 0
|
||||||
|
? event.target.files[0]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (floppyFile) {
|
||||||
|
this.props.setFloppy(floppyFile);
|
||||||
|
} else {
|
||||||
|
console.log(`Floppy: Input changed but no file selected`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the state reset
|
||||||
|
*/
|
||||||
|
private async onResetState() {
|
||||||
|
if (fs.existsSync(CONSTANTS.STATE_PATH)) {
|
||||||
|
await fs.remove(CONSTANTS.STATE_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ isStateReset: true });
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,9 @@ export class CardStart extends React.Component<CardStartProps, {}> {
|
||||||
id="win95"
|
id="win95"
|
||||||
onClick={this.props.startEmulator}
|
onClick={this.props.startEmulator}
|
||||||
>
|
>
|
||||||
Start Windows 95
|
<img src="../../static/run.png" />
|
||||||
|
<span>Start Windows 95</span>
|
||||||
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<small>Hit ESC to lock or unlock your mouse</small>
|
<small>Hit ESC to lock or unlock your mouse</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import * as fs from "fs-extra";
|
|
||||||
|
|
||||||
import { CONSTANTS } from "../constants";
|
|
||||||
|
|
||||||
export interface CardStateProps {
|
|
||||||
bootFromScratch: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CardState extends React.Component<CardStateProps, {}> {
|
|
||||||
constructor(props: CardStateProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.onReset = this.onReset.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
return (
|
|
||||||
<section>
|
|
||||||
<div className="card">
|
|
||||||
<div className="card-header">
|
|
||||||
<h2 className="card-title">Machine State</h2>
|
|
||||||
</div>
|
|
||||||
<div className="card-body">
|
|
||||||
<p>
|
|
||||||
windows95 stores any changes to your machine (like saved files) in
|
|
||||||
a state file. If you encounter any trouble, you can either reset
|
|
||||||
your state or boot Windows 95 from scratch.
|
|
||||||
</p>
|
|
||||||
<button className="btn" onClick={this.onReset}>
|
|
||||||
Reset state
|
|
||||||
</button>
|
|
||||||
<button className="btn" onClick={this.props.bootFromScratch}>
|
|
||||||
Reboot from scratch
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onReset() {
|
|
||||||
if (fs.existsSync(CONSTANTS.STATE_PATH)) {
|
|
||||||
await fs.remove(CONSTANTS.STATE_PATH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +1,21 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as fs from "fs-extra";
|
import * as fs from "fs-extra";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { ipcRenderer, remote, shell, webFrame } from "electron";
|
import { ipcRenderer, remote, shell } from "electron";
|
||||||
|
|
||||||
import { CONSTANTS, IPC_COMMANDS } from "../constants";
|
import { CONSTANTS, IPC_COMMANDS } from "../constants";
|
||||||
import { getDiskImageSize } from "../utils/disk-image-size";
|
import { getDiskImageSize } from "../utils/disk-image-size";
|
||||||
import { CardStart } from "./card-start";
|
import { CardStart } from "./card-start";
|
||||||
import { StartMenu } from "./start-menu";
|
import { StartMenu } from "./start-menu";
|
||||||
import { CardFloppy } from "./card-floppy";
|
import { CardSettings } from "./card-settings";
|
||||||
import { CardState } from "./card-state";
|
|
||||||
import { EmulatorInfo } from "./emulator-info";
|
import { EmulatorInfo } from "./emulator-info";
|
||||||
|
import { CardDrive } from "./card-drive";
|
||||||
|
|
||||||
export interface EmulatorState {
|
export interface EmulatorState {
|
||||||
currentUiCard: string;
|
currentUiCard: string;
|
||||||
emulator?: any;
|
emulator?: any;
|
||||||
scale: number;
|
scale: number;
|
||||||
floppyFile?: string;
|
floppyFile?: File;
|
||||||
isBootingFresh: boolean;
|
isBootingFresh: boolean;
|
||||||
isCursorCaptured: boolean;
|
isCursorCaptured: boolean;
|
||||||
isInfoDisplayed: boolean;
|
isInfoDisplayed: boolean;
|
||||||
|
@ -33,6 +33,7 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||||
this.stopEmulator = this.stopEmulator.bind(this);
|
this.stopEmulator = this.stopEmulator.bind(this);
|
||||||
this.restartEmulator = this.restartEmulator.bind(this);
|
this.restartEmulator = this.restartEmulator.bind(this);
|
||||||
this.resetEmulator = this.resetEmulator.bind(this);
|
this.resetEmulator = this.resetEmulator.bind(this);
|
||||||
|
this.bootFromScratch = this.bootFromScratch.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isBootingFresh: false,
|
isBootingFresh: false,
|
||||||
|
@ -87,13 +88,22 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||||
public setupUnloadListeners() {
|
public setupUnloadListeners() {
|
||||||
const handleClose = async () => {
|
const handleClose = async () => {
|
||||||
await this.saveState();
|
await this.saveState();
|
||||||
|
|
||||||
|
console.log(`Unload: Now done, quitting again.`);
|
||||||
this.isQuitting = true;
|
this.isQuitting = true;
|
||||||
remote.app.quit();
|
|
||||||
|
setImmediate(() => {
|
||||||
|
remote.app.quit();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
window.onbeforeunload = event => {
|
window.onbeforeunload = event => {
|
||||||
if (this.isQuitting) return;
|
if (this.isQuitting || this.isResetting) {
|
||||||
if (this.isResetting) return;
|
console.log(`Unload: Not preventing`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Unload: Preventing to first save state`);
|
||||||
|
|
||||||
handleClose();
|
handleClose();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -167,6 +177,10 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||||
ipcRenderer.on(IPC_COMMANDS.ZOOM_OUT, () => {
|
ipcRenderer.on(IPC_COMMANDS.ZOOM_OUT, () => {
|
||||||
this.setScale(this.state.scale * 0.8);
|
this.setScale(this.state.scale * 0.8);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on(IPC_COMMANDS.ZOOM_RESET, () => {
|
||||||
|
this.setScale(1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -175,7 +189,7 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||||
* 🤡
|
* 🤡
|
||||||
*/
|
*/
|
||||||
public renderUI() {
|
public renderUI() {
|
||||||
const { isRunning, currentUiCard } = this.state;
|
const { isRunning, currentUiCard, floppyFile } = this.state;
|
||||||
|
|
||||||
if (isRunning) {
|
if (isRunning) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -183,15 +197,16 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||||
|
|
||||||
let card;
|
let card;
|
||||||
|
|
||||||
if (currentUiCard === "floppy") {
|
if (currentUiCard === "settings") {
|
||||||
card = (
|
card = (
|
||||||
<CardFloppy
|
<CardSettings
|
||||||
setFloppyPath={floppyFile => this.setState({ floppyFile })}
|
setFloppy={floppyFile => this.setState({ floppyFile })}
|
||||||
floppyPath={this.state.floppyFile}
|
bootFromScratch={this.bootFromScratch}
|
||||||
|
floppy={floppyFile}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (currentUiCard === "state") {
|
} else if (currentUiCard === "drive") {
|
||||||
card = <CardState bootFromScratch={this.bootFromScratch} />;
|
card = <CardDrive showDiskImage={this.showDiskImage} />;
|
||||||
} else {
|
} else {
|
||||||
card = <CardStart startEmulator={this.startEmulator} />;
|
card = <CardStart startEmulator={this.startEmulator} />;
|
||||||
}
|
}
|
||||||
|
@ -348,16 +363,16 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||||
private async saveState(): Promise<void> {
|
private async saveState(): Promise<void> {
|
||||||
const { emulator } = this.state;
|
const { emulator } = this.state;
|
||||||
|
|
||||||
if (!emulator || !emulator.save_state) {
|
|
||||||
console.log(`restoreState: No emulator present`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
|
if (!emulator || !emulator.save_state) {
|
||||||
|
console.log(`restoreState: No emulator present`);
|
||||||
|
return resolve();
|
||||||
|
}
|
||||||
|
|
||||||
emulator.save_state(async (error: Error, newState: ArrayBuffer) => {
|
emulator.save_state(async (error: Error, newState: ArrayBuffer) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.warn(`saveState: Could not save state`, error);
|
console.warn(`saveState: Could not save state`, error);
|
||||||
return;
|
return resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.outputFile(CONSTANTS.STATE_PATH, Buffer.from(newState));
|
await fs.outputFile(CONSTANTS.STATE_PATH, Buffer.from(newState));
|
||||||
|
|
|
@ -14,19 +14,23 @@ export class StartMenu extends React.Component<StartMenuProps, {}> {
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<nav className="nav nav-bottom">
|
<nav className="nav nav-bottom">
|
||||||
<a onClick={this.navigate} href="#" id="start" className="nav-logo">
|
<a onClick={this.navigate} href="#" id="start" className="nav-link">
|
||||||
<img src="../../static/start.png" alt="" />
|
<img src="../../static/start.png" alt="Start" />
|
||||||
<span>Start</span>
|
<span>Start</span>
|
||||||
</a>
|
</a>
|
||||||
<div className="nav-menu">
|
<div className="nav-menu">
|
||||||
<a onClick={this.navigate} href="#" id="floppy" className="nav-link">
|
<a
|
||||||
Floppy Disk
|
onClick={this.navigate}
|
||||||
|
href="#"
|
||||||
|
id="settings"
|
||||||
|
className="nav-link"
|
||||||
|
>
|
||||||
|
<img src="../../static/settings.png" />
|
||||||
|
<span>Settings</span>
|
||||||
</a>
|
</a>
|
||||||
<a onClick={this.navigate} href="#" id="state" className="nav-link">
|
<a onClick={this.navigate} href="#" id="drive" className="nav-link">
|
||||||
Reset Machine
|
<img src="../../static/drive.png" />
|
||||||
</a>
|
<span>Modify C: Drive</span>
|
||||||
<a onClick={this.navigate} href="#" id="disk" className="nav-link">
|
|
||||||
Modify C: Drive
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
BIN
static/boot-fresh.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
static/drive.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
static/floppy.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
|
@ -12,5 +12,15 @@
|
||||||
<body class="paused windows95">
|
<body class="paused windows95">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script src="../src/renderer/app.tsx"></script>
|
<script src="../src/renderer/app.tsx"></script>
|
||||||
|
<link rel="prefetch" href="../static/boot-fresh.png" />
|
||||||
|
<link rel="prefetch" href="../static/drive.png" />
|
||||||
|
<link rel="prefetch" href="../static/floppy.png" />
|
||||||
|
<link rel="prefetch" href="../static/reset-state.png" />
|
||||||
|
<link rel="prefetch" href="../static/reset.png" />
|
||||||
|
<link rel="prefetch" href="../static/run.png" />
|
||||||
|
<link rel="prefetch" href="../static/select-floppy.png" />
|
||||||
|
<link rel="prefetch" href="../static/settings.png" />
|
||||||
|
<link rel="prefetch" href="../static/show-disk-image.png" />
|
||||||
|
<link rel="prefetch" href="../static/start.png" />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
BIN
static/reset-state.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
static/reset.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
static/run.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
static/select-floppy.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
static/settings.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
static/show-disk-image.png
Normal file
After Width: | Height: | Size: 1.9 KiB |