Allow the Id of a device to be changed, toast errors
This commit is contained in:
@@ -339,7 +339,7 @@ pub struct RegisterSpec {
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeviceSpec {
|
||||
pub device: Device,
|
||||
pub connection: Option<u32>,
|
||||
pub connection: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -424,7 +424,7 @@ impl Operand {
|
||||
ic: &interpreter::IC,
|
||||
inst: InstructionOp,
|
||||
index: u32,
|
||||
) -> Result<(Option<u32>, Option<u32>), interpreter::ICError> {
|
||||
) -> Result<(Option<u32>, Option<usize>), interpreter::ICError> {
|
||||
match self.translate_alias(ic) {
|
||||
Operand::DeviceSpec(DeviceSpec { device, connection }) => match device {
|
||||
Device::Db => Ok((Some(ic.device), connection)),
|
||||
@@ -615,7 +615,7 @@ impl FromStr for Operand {
|
||||
let connection_str = rest_iter
|
||||
.take_while_ref(|c| c.is_ascii_digit())
|
||||
.collect::<String>();
|
||||
let connection = connection_str.parse::<u32>().unwrap();
|
||||
let connection = connection_str.parse::<usize>().unwrap();
|
||||
if rest_iter.next().is_none() {
|
||||
Ok(Some(connection))
|
||||
} else {
|
||||
@@ -669,7 +669,7 @@ impl FromStr for Operand {
|
||||
let connection_str = rest_iter
|
||||
.take_while_ref(|c| c.is_ascii_digit())
|
||||
.collect::<String>();
|
||||
let connection = connection_str.parse::<u32>().unwrap();
|
||||
let connection = connection_str.parse::<usize>().unwrap();
|
||||
if rest_iter.next().is_none() {
|
||||
Ok(Some(connection))
|
||||
} else {
|
||||
|
||||
@@ -12,9 +12,7 @@ use itertools::Itertools;
|
||||
|
||||
use time::format_description;
|
||||
|
||||
use crate::{
|
||||
grammar::{self, ParseError},
|
||||
};
|
||||
use crate::grammar::{self, ParseError};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -112,6 +110,8 @@ pub enum ICError {
|
||||
NetworkNotConnected(usize),
|
||||
#[error("bad network Id '{0}'")]
|
||||
BadNetworkId(u32),
|
||||
#[error("channel index out of range '{0}'")]
|
||||
ChannelIndexOutOfRange(usize)
|
||||
}
|
||||
|
||||
impl ICError {
|
||||
@@ -2147,10 +2147,10 @@ impl IC {
|
||||
};
|
||||
let network_id = vm
|
||||
.get_device_same_network(this.device, device_id)
|
||||
.map(|device| device.borrow().get_network_id(connection as usize))
|
||||
.map(|device| device.borrow().get_network_id(connection))
|
||||
.unwrap_or(Err(UnknownDeviceID(device_id as f64)))?;
|
||||
let val = val.as_value(this, inst, 3)?;
|
||||
vm.set_network_channel(network_id as usize, channel, val)?;
|
||||
vm.set_network_channel(network_id, channel, val)?;
|
||||
return Ok(());
|
||||
}
|
||||
let device = vm.get_device_same_network(this.device, device_id);
|
||||
@@ -2256,9 +2256,9 @@ impl IC {
|
||||
};
|
||||
let network_id = vm
|
||||
.get_device_same_network(this.device, device_id)
|
||||
.map(|device| device.borrow().get_network_id(connection as usize))
|
||||
.map(|device| device.borrow().get_network_id(connection))
|
||||
.unwrap_or(Err(UnknownDeviceID(device_id as f64)))?;
|
||||
let val = vm.get_network_channel(network_id as usize, channel)?;
|
||||
let val = vm.get_network_channel(network_id, channel)?;
|
||||
this.set_register(indirection, target, val)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ pub enum VMError {
|
||||
InvalidNetwork(u32),
|
||||
#[error("device {0} not visible to device {1} (not on the same networks)")]
|
||||
DeviceNotVisible(u32, u32),
|
||||
#[error("a device with id {0} already exists")]
|
||||
IdInUse(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@@ -65,7 +67,7 @@ impl SlotOccupant {
|
||||
id,
|
||||
prefab_hash,
|
||||
quantity: 1,
|
||||
max_quantity: 1, // FIXME: need a good way to set a better default
|
||||
max_quantity: 1,
|
||||
damage: 0.0,
|
||||
fields: HashMap::new(),
|
||||
}
|
||||
@@ -976,6 +978,35 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_device_id(&mut self, old_id: u32, new_id: u32) -> Result<(), VMError> {
|
||||
if self.devices.contains_key(&new_id) | self.ics.contains_key(&new_id) {
|
||||
return Err(VMError::IdInUse(new_id));
|
||||
}
|
||||
let device = self
|
||||
.devices
|
||||
.remove(&old_id)
|
||||
.ok_or(VMError::UnknownId(old_id))?;
|
||||
device.borrow_mut().id = new_id;
|
||||
self.devices.insert(new_id, device);
|
||||
self.ics.iter().for_each(|(_id, ic)| {
|
||||
if let Ok(mut ic_ref) = ic.try_borrow_mut() {
|
||||
ic_ref.pins.iter_mut().for_each(|pin| {
|
||||
if pin.is_some_and(|d| d == old_id) {
|
||||
pin.replace(new_id);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
self.networks.iter().for_each(|(_net_id, net)| {
|
||||
if let Ok(mut net_ref) = net.try_borrow_mut() {
|
||||
if net_ref.devices.remove(&old_id) {
|
||||
net_ref.devices.insert(new_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set program code if it's valid
|
||||
pub fn set_code(&self, id: u32, code: &str) -> Result<bool, VMError> {
|
||||
let device = self
|
||||
@@ -1113,21 +1144,23 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_network_channel(&self, id: usize, channel: usize) -> Result<f64, ICError> {
|
||||
let network = self
|
||||
.networks
|
||||
.get(&(id as u32))
|
||||
.ok_or(ICError::BadNetworkId(id as u32))?;
|
||||
Ok(network.borrow().channels[channel])
|
||||
pub fn get_network_channel(&self, id: u32, channel: usize) -> Result<f64, ICError> {
|
||||
let network = self.networks.get(&id).ok_or(ICError::BadNetworkId(id))?;
|
||||
if !(0..8).contains(&channel) {
|
||||
Err(ICError::ChannelIndexOutOfRange(channel))
|
||||
} else {
|
||||
Ok(network.borrow().channels[channel])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_network_channel(&self, id: usize, channel: usize, val: f64) -> Result<(), ICError> {
|
||||
let network = self
|
||||
.networks
|
||||
.get(&(id as u32))
|
||||
.ok_or(ICError::BadNetworkId(id as u32))?;
|
||||
network.borrow_mut().channels[channel] = val;
|
||||
Ok(())
|
||||
pub fn set_network_channel(&self, id: u32, channel: usize, val: f64) -> Result<(), ICError> {
|
||||
let network = self.networks.get(&(id)).ok_or(ICError::BadNetworkId(id))?;
|
||||
if !(0..8).contains(&channel) {
|
||||
Err(ICError::ChannelIndexOutOfRange(channel))
|
||||
} else {
|
||||
network.borrow_mut().channels[channel] = val;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn devices_on_same_network(&self, ids: &[u32]) -> bool {
|
||||
@@ -1200,7 +1233,7 @@ impl VM {
|
||||
{
|
||||
// scope this borrow
|
||||
let connections = &device.borrow().connections;
|
||||
let Connection::CableNetwork { net, .. } = & connections[connection] else {
|
||||
let Connection::CableNetwork { net, .. } = &connections[connection] else {
|
||||
return Err(ICError::NotACableConnection(connection).into());
|
||||
};
|
||||
// remove from current network
|
||||
|
||||
@@ -439,6 +439,11 @@ impl VM {
|
||||
pub fn set_pin(&self, id: u32, pin: usize, val: Option<u32>) -> Result<bool, JsError> {
|
||||
Ok(self.vm.borrow().set_pin(id, pin, val)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "changeDeviceId")]
|
||||
pub fn change_device_id(&self, old_id: u32, new_id: u32) -> Result<(), JsError> {
|
||||
Ok(self.vm.borrow_mut().change_device_id(old_id, new_id)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VM {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-bs-theme="dark">
|
||||
<html data-bs-theme="dark" class="sl-theme-dark">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
@@ -65,6 +65,9 @@ $accordion-button-padding-y: 0.5rem;
|
||||
// Utilities
|
||||
@import "bootstrap/scss/utilities/api";
|
||||
|
||||
// Sholace theme
|
||||
@import "@shoelace-style/shoelace/dist/themes/dark.css";
|
||||
|
||||
|
||||
//
|
||||
// Custom styles
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { html, css, HTMLTemplateResult, PropertyValueMap } from "lit";
|
||||
import {
|
||||
html,
|
||||
css,
|
||||
HTMLTemplateResult,
|
||||
PropertyValueMap,
|
||||
CSSResultGroup,
|
||||
} from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
import SlDetails from "@shoelace-style/shoelace/dist/components/details/details.js";
|
||||
@@ -7,6 +13,18 @@ import SlDetails from "@shoelace-style/shoelace/dist/components/details/details.
|
||||
export class IC10Details extends SlDetails {
|
||||
@query(".details__summary-icon") accessor summaryIcon: HTMLSpanElement;
|
||||
|
||||
static styles = [
|
||||
SlDetails.styles,
|
||||
css`
|
||||
.details__header {
|
||||
cursor: auto;
|
||||
}
|
||||
.details__summary-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
@@ -48,34 +66,16 @@ export class IC10Details extends SlDetails {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<details
|
||||
part="base"
|
||||
class=${classMap({
|
||||
details: true,
|
||||
"details--open": this.open,
|
||||
"details--disabled": this.disabled,
|
||||
})}
|
||||
>
|
||||
<summary
|
||||
part="header"
|
||||
id="header"
|
||||
class="details__header"
|
||||
role="button"
|
||||
aria-expanded=${this.open ? "true" : "false"}
|
||||
aria-controls="content"
|
||||
aria-disabled=${this.disabled ? "true" : "false"}
|
||||
tabindex=${this.disabled ? "-1" : "0"}
|
||||
>
|
||||
<slot name="summary" part="summary" class="details__summary"
|
||||
>${this.summary}</slot
|
||||
<details part="base" class=${classMap({ details: true, "details--open" : this.open, "details--disabled" : this.disabled,
|
||||
})}>
|
||||
<summary part="header" id="header" class="details__header" role="button" aria-expanded=${this.open ? "true" : "false"
|
||||
} aria-controls="content" aria-disabled=${this.disabled ? "true" : "false" } tabindex=${this.disabled ? "-1" : "0" }
|
||||
@click=${(e: Event)=> e.preventDefault()}
|
||||
>
|
||||
<slot name="summary" part="summary" class="details__summary">${this.summary}</slot>
|
||||
|
||||
<span
|
||||
part="summary-icon"
|
||||
class="details__summary-icon"
|
||||
@click=${this.handleSummaryIconClick}
|
||||
@keydown=${this.handleSummaryIconKeyDown}
|
||||
>
|
||||
<span part="summary-icon" class="details__summary-icon" @click=${this.handleSummaryIconClick}
|
||||
@keydown=${this.handleSummaryIconKeyDown}>
|
||||
<slot name="expand-icon">
|
||||
<sl-icon library="system" name="chevron-right"></sl-icon>
|
||||
</slot>
|
||||
|
||||
@@ -28,8 +28,7 @@ declare global {
|
||||
|
||||
import { BaseElement, defaultCss } from "../components";
|
||||
import { html } from "lit";
|
||||
import { Ref, createRef, ref } from "lit/directives/ref.js";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
import { customElement, state, query } from "lit/decorators.js";
|
||||
import { editorStyles } from "./styles";
|
||||
import "./shortcuts_ui";
|
||||
import { AceKeyboardShortcuts } from "./shortcuts_ui";
|
||||
@@ -45,8 +44,7 @@ export class IC10Editor extends BaseElement {
|
||||
};
|
||||
sessions: Map<number, Ace.EditSession>;
|
||||
|
||||
@property({ type: Number })
|
||||
accessor active_session: number = 0;
|
||||
@state() active_session: number = 0;
|
||||
|
||||
active_line_markers: Map<number, number | null> = new Map();
|
||||
languageProvider?: LanguageProvider;
|
||||
@@ -291,7 +289,7 @@ export class IC10Editor extends BaseElement {
|
||||
|
||||
window.App!.session.onActiveLine(((e: CustomEvent) => {
|
||||
const session = window.App?.session!;
|
||||
const id = e.detail;
|
||||
const id: number = e.detail;
|
||||
const active_line = session.getActiveLine(id);
|
||||
if (typeof active_line !== "undefined") {
|
||||
const marker = that.active_line_markers.get(id);
|
||||
@@ -485,14 +483,14 @@ export class IC10Editor extends BaseElement {
|
||||
}
|
||||
|
||||
createOrSetSession(session_id: number, content: any) {
|
||||
if (!this.sessions.hasOwnProperty(session_id)) {
|
||||
if (!this.sessions.has(session_id)) {
|
||||
this.newSession(session_id);
|
||||
}
|
||||
this.sessions.get(session_id)?.setValue(content);
|
||||
}
|
||||
|
||||
newSession(session_id: number) {
|
||||
if (this.sessions.hasOwnProperty(session_id)) {
|
||||
if (this.sessions.has(session_id)) {
|
||||
return false;
|
||||
}
|
||||
const session = ace.createEditSession("", this.mode as any);
|
||||
@@ -564,7 +562,7 @@ export class IC10Editor extends BaseElement {
|
||||
}
|
||||
|
||||
destroySession(session_id: number) {
|
||||
if (!this.sessions.hasOwnProperty(session_id)) {
|
||||
if (!this.sessions.has(session_id)) {
|
||||
return false;
|
||||
}
|
||||
if (!(Object.keys(this.sessions).length > 1)) {
|
||||
|
||||
@@ -52,6 +52,7 @@ export class VMICControls extends VMActiveICMixin(BaseElement) {
|
||||
sl-button[variant="success"] {
|
||||
/* Changes the success theme color to purple using primitives */
|
||||
--sl-color-success-600: var(--sl-color-purple-700);
|
||||
--sl-color-success-500: var(--sl-color-purple-600);
|
||||
}
|
||||
sl-button[variant="primary"] {
|
||||
/* Changes the success theme color to purple using primitives */
|
||||
|
||||
@@ -62,7 +62,7 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
width: 10rem;
|
||||
}
|
||||
.device-id::part(input) {
|
||||
width: 2rem;
|
||||
width: 7rem;
|
||||
}
|
||||
.device-name-hash::part(input) {
|
||||
width: 7rem;
|
||||
@@ -129,7 +129,7 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
size="small"
|
||||
pill
|
||||
value=${this.deviceID}
|
||||
disabled
|
||||
@sl-change=${this._handleChangeID}
|
||||
>
|
||||
<span slot="prefix">Id</span>
|
||||
<sl-copy-button slot="suffix" value=${this.deviceID}></sl-copy-button>
|
||||
@@ -314,6 +314,16 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
_handleChangeID(e: CustomEvent) {
|
||||
const input = e.target as SlInput;
|
||||
const val = input.valueAsNumber;
|
||||
if (!isNaN(val)) {
|
||||
window.VM.changeDeviceId(this.deviceID, val);
|
||||
} else {
|
||||
input.value = this.deviceID.toString();
|
||||
}
|
||||
}
|
||||
|
||||
_handleChangeName(e: CustomEvent) {
|
||||
const input = e.target as SlInput;
|
||||
window.VM?.setDeviceName(this.deviceID, input.value);
|
||||
@@ -404,7 +414,14 @@ export class VMDeviceList extends BaseElement {
|
||||
}
|
||||
|
||||
protected render(): HTMLTemplateResult {
|
||||
return html`
|
||||
const deviceCards: HTMLTemplateResult[] = this.devices.map(
|
||||
(id, _index, _ids) =>
|
||||
html`<vm-device-card
|
||||
.deviceID=${id}
|
||||
class="device-list-card"
|
||||
></vm-device-card>`,
|
||||
);
|
||||
const result = html`
|
||||
<div class="header">
|
||||
<span>
|
||||
Devices:
|
||||
@@ -413,15 +430,11 @@ export class VMDeviceList extends BaseElement {
|
||||
<vm-add-device-button class="ms-auto"></vm-add-device-button>
|
||||
</div>
|
||||
<div class="device-list">
|
||||
${this.devices.map(
|
||||
(id, _index, _ids) =>
|
||||
html`<vm-device-card
|
||||
.deviceID=${id}
|
||||
class="device-list-card"
|
||||
></vm-device-card>`,
|
||||
)}
|
||||
${deviceCards}
|
||||
</div>
|
||||
`;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -386,7 +386,7 @@ export type DeviceDBEntry = {
|
||||
slotlogic?: { [key in SlotLogicType]: number[] };
|
||||
slots?: { name: string; typ: SlotClass }[];
|
||||
modes?: { [key: string]: string };
|
||||
conn?: { [key in SlotLogicType]: [NetworkType, ConnectionRole] };
|
||||
conn?: { [key: number]: [NetworkType, ConnectionRole] };
|
||||
slotclass?: SlotClass;
|
||||
sorting?: SortingClass;
|
||||
pins?: number;
|
||||
@@ -403,3 +403,30 @@ export type DeviceDB = {
|
||||
};
|
||||
names_by_hash: { [key: number]: string };
|
||||
};
|
||||
|
||||
|
||||
export type PreCastDeviceDBEntry = {
|
||||
name: string;
|
||||
hash: number;
|
||||
desc: string;
|
||||
logic?: { [key in LogicType]?: string };
|
||||
slotlogic?: { [key in SlotLogicType]?: number[] };
|
||||
slots?: { name: string; typ: string }[];
|
||||
modes?: { [key: string]: string };
|
||||
conn?: { [key: number]: string[] };
|
||||
slotclass?: string;
|
||||
sorting?: string;
|
||||
pins?: number;
|
||||
};
|
||||
|
||||
export type PreCastDeviceDB = {
|
||||
logic_enabled: string[];
|
||||
slot_logic_enabled: string[];
|
||||
devices: string[];
|
||||
items: string[];
|
||||
structures: string[];
|
||||
db: {
|
||||
[key: string]: PreCastDeviceDBEntry;
|
||||
};
|
||||
names_by_hash: { [key: number]: string };
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DeviceRef, VM, init } from "ic10emu_wasm";
|
||||
import { DeviceDB } from "./device_db";
|
||||
import { DeviceDB, PreCastDeviceDB } from "./device_db";
|
||||
import "./base_device";
|
||||
|
||||
declare global {
|
||||
@@ -8,13 +8,21 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ToastMessage {
|
||||
variant: "warning" | "danger" | "success" | "primary" | "neutral";
|
||||
icon: string;
|
||||
title: string;
|
||||
msg: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
class VirtualMachine extends EventTarget {
|
||||
ic10vm: VM;
|
||||
_devices: Map<number, DeviceRef>;
|
||||
_ics: Map<number, DeviceRef>;
|
||||
|
||||
accessor db: DeviceDB;
|
||||
dbPromise: Promise<{ default: DeviceDB }>;
|
||||
dbPromise: Promise<{ default: PreCastDeviceDB }>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -28,7 +36,9 @@ class VirtualMachine extends EventTarget {
|
||||
this._ics = new Map();
|
||||
|
||||
this.dbPromise = import("../../../data/database.json");
|
||||
this.dbPromise.then((module) => this.setupDeviceDatabase(module.default));
|
||||
this.dbPromise.then((module) =>
|
||||
this.setupDeviceDatabase(module.default as DeviceDB),
|
||||
);
|
||||
|
||||
this.updateDevices();
|
||||
this.updateCode();
|
||||
@@ -78,7 +88,6 @@ class VirtualMachine extends EventTarget {
|
||||
}
|
||||
for (const id of this._devices.keys()) {
|
||||
if (!device_ids.includes(id)) {
|
||||
this._devices.get(id)!.free();
|
||||
this._devices.delete(id);
|
||||
update_flag = true;
|
||||
}
|
||||
@@ -102,7 +111,9 @@ class VirtualMachine extends EventTarget {
|
||||
|
||||
if (update_flag) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-devices-update", { detail: device_ids }),
|
||||
new CustomEvent("vm-devices-update", {
|
||||
detail: Array.from(device_ids),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -122,8 +133,8 @@ class VirtualMachine extends EventTarget {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-device-modified", { detail: id }),
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
console.timeEnd(`CompileProgram_${id}_${attempt}`);
|
||||
}
|
||||
@@ -136,8 +147,8 @@ class VirtualMachine extends EventTarget {
|
||||
if (ic) {
|
||||
try {
|
||||
ic.step(false);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
this.update();
|
||||
this.dispatchEvent(
|
||||
@@ -151,8 +162,8 @@ class VirtualMachine extends EventTarget {
|
||||
if (ic) {
|
||||
try {
|
||||
ic.run(false);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
this.update();
|
||||
this.dispatchEvent(
|
||||
@@ -178,7 +189,7 @@ class VirtualMachine extends EventTarget {
|
||||
);
|
||||
}
|
||||
}, this);
|
||||
this.updateDevice(this.activeIC)
|
||||
this.updateDevice(this.activeIC);
|
||||
}
|
||||
|
||||
updateDevice(device: DeviceRef) {
|
||||
@@ -190,13 +201,37 @@ class VirtualMachine extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
handleVmError(err: Error) {
|
||||
console.log("Error in Virtual Machine", err);
|
||||
const message: ToastMessage = {
|
||||
variant: "danger",
|
||||
icon: "bug",
|
||||
title: `Error in Virtual Machine ${err.name}`,
|
||||
msg: err.message,
|
||||
id: Date.now().toString(16),
|
||||
};
|
||||
this.dispatchEvent(new CustomEvent("vm-message", { detail: message }));
|
||||
}
|
||||
|
||||
changeDeviceId(old_id: number, new_id: number) {
|
||||
try {
|
||||
this.ic10vm.changeDeviceId(old_id, new_id);
|
||||
this.updateDevices();
|
||||
if (window.App.session.activeIC === old_id) {
|
||||
window.App.session.activeIC = new_id;
|
||||
}
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
}
|
||||
|
||||
setRegister(index: number, val: number) {
|
||||
const ic = this.activeIC!;
|
||||
try {
|
||||
ic.setRegister(index, val);
|
||||
this.updateDevice(ic);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,8 +240,8 @@ class VirtualMachine extends EventTarget {
|
||||
try {
|
||||
ic!.setStack(addr, val);
|
||||
this.updateDevice(ic);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,8 +262,8 @@ class VirtualMachine extends EventTarget {
|
||||
device.setField(field, val);
|
||||
this.updateDevice(device);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -241,8 +276,8 @@ class VirtualMachine extends EventTarget {
|
||||
device.setSlotField(slot, field, val);
|
||||
this.updateDevice(device);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { HTMLTemplateResult, html, css } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "../components";
|
||||
import "@shoelace-style/shoelace/dist/components/details/details.js";
|
||||
import "@shoelace-style/shoelace/dist/components/tab/tab.js";
|
||||
import "@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.js";
|
||||
import "@shoelace-style/shoelace/dist/components/tab-group/tab-group.js";
|
||||
import "@shoelace-style/shoelace/dist/components/alert/alert.js";
|
||||
|
||||
import "./controls";
|
||||
import "./registers";
|
||||
import "./stack";
|
||||
import "./device";
|
||||
import { ToastMessage } from ".";
|
||||
|
||||
@customElement("vm-ui")
|
||||
export class VMUI extends BaseElement {
|
||||
@@ -34,16 +36,39 @@ export class VMUI extends BaseElement {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.side-container {
|
||||
height: 100%
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
window.VM.addEventListener("vm-message", this._handleVMMessage.bind(this) )
|
||||
}
|
||||
|
||||
_handleVMMessage(e: CustomEvent) {
|
||||
const msg: ToastMessage = e.detail;
|
||||
const alert = Object.assign(document.createElement('sl-alert'), {
|
||||
variant: msg.variant,
|
||||
closable: true,
|
||||
// duration: 5000,
|
||||
innerHTML: `
|
||||
<sl-icon slot="icon" name="${msg.icon}"></sl-icon>
|
||||
<strong>${msg.title}</strong><br />
|
||||
${msg.msg}
|
||||
`
|
||||
});
|
||||
|
||||
document.body.append(alert);
|
||||
alert.toast();
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<div class="side-container">
|
||||
|
||||
Reference in New Issue
Block a user