ensure all numeric values preserve across session
This commit is contained in:
@@ -52,7 +52,7 @@ export class App extends BaseElement {
|
||||
`,
|
||||
];
|
||||
|
||||
version = packageJson.version;
|
||||
appVersion = packageJson.version;
|
||||
gitVer = __COMMIT_HASH__;
|
||||
buildDate = __BUILD_DATE__;
|
||||
|
||||
@@ -80,7 +80,7 @@ export class App extends BaseElement {
|
||||
const root = super.createRenderRoot();
|
||||
root.addEventListener("app-share-session", this._handleShare.bind(this));
|
||||
root.addEventListener("app-open-file", this._handleOpenFile.bind(this));
|
||||
root.addEventListener("app-save-as", this._handleSaveAs.bind(this));
|
||||
root.addEventListener("app-export", this._handleExport.bind(this));
|
||||
root.addEventListener("app-save", this._handleSave.bind(this));
|
||||
return root;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ export class App extends BaseElement {
|
||||
protected render(): HTMLTemplateResult {
|
||||
return html`
|
||||
<div class="app-container">
|
||||
<app-nav></app-nav>
|
||||
<app-nav appVer=${this.appVersion} gitVer=${this.gitVer} buildDate=${this.buildDate} ></app-nav>
|
||||
<div class="app-body">
|
||||
<sl-split-panel
|
||||
style="--min: 20em; --max: calc(100% - 20em);"
|
||||
@@ -114,7 +114,7 @@ export class App extends BaseElement {
|
||||
this.shareDialog.show();
|
||||
}
|
||||
|
||||
_handleSaveAs(_e: Event) {
|
||||
_handleExport(_e: Event) {
|
||||
saveFile(window.Editor.editorValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import "@shoelace-style/shoelace/dist/components/menu/menu.js";
|
||||
import "@shoelace-style/shoelace/dist/components/divider/divider.js";
|
||||
import "@shoelace-style/shoelace/dist/components/menu-item/menu-item.js";
|
||||
import "@shoelace-style/shoelace/dist/components/dropdown/dropdown.js";
|
||||
import '@shoelace-style/shoelace/dist/components/relative-time/relative-time.js';
|
||||
import '@shoelace-style/shoelace/dist/components/tooltip/tooltip.js';
|
||||
import SlMenuItem from "@shoelace-style/shoelace/dist/components/menu-item/menu-item.js";
|
||||
|
||||
@customElement("app-nav")
|
||||
@@ -59,6 +61,10 @@ export class Nav extends BaseElement {
|
||||
position: relative;
|
||||
color: #fff;
|
||||
}
|
||||
.navbar-header .version {
|
||||
color: var(--sl-color-neutral-500);
|
||||
font-size: var(--sl-font-size-small);
|
||||
}
|
||||
.nav > li > a {
|
||||
color: #fff;
|
||||
line-height: 20px;
|
||||
@@ -92,6 +98,10 @@ export class Nav extends BaseElement {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
@property() gitVer: string;
|
||||
@property() appVer: string;
|
||||
@property() buildDate: string;
|
||||
protected render(): HTMLTemplateResult {
|
||||
return html`
|
||||
<nav id="navBar" class="navbar navbar-default">
|
||||
@@ -105,26 +115,60 @@ export class Nav extends BaseElement {
|
||||
></sl-icon-button>
|
||||
|
||||
<sl-menu class="menu" @sl-select=${this._menuClickHandler} style="z-index: 10">
|
||||
<sl-menu-item value="share">Share</sl-menu-item>
|
||||
<sl-menu-item value="openFile">Open File</sl-menu-item>
|
||||
<sl-menu-item value="save">Save</sl-menu-item>
|
||||
<sl-menu-item value="saveAs">Save As</sl-menu-item>
|
||||
<sl-menu-item value="share">
|
||||
Share
|
||||
<sl-icon name="share" slot="prefix"></sl-icon>
|
||||
</sl-menu-item>
|
||||
<sl-menu-item value="openFile">
|
||||
Open File
|
||||
<sl-icon name="folder2-open" slot="prefix"></sl-icon>
|
||||
</sl-menu-item>
|
||||
<sl-menu-item value="save">
|
||||
Save
|
||||
<sl-icon name="box-arrow-in-down" slot="prefix"></sl-icon>
|
||||
</sl-menu-item>
|
||||
<sl-menu-item value="load">
|
||||
Load
|
||||
<sl-icon name="box-arrow-up" slot="prefix"></sl-icon>
|
||||
</sl-menu-item>
|
||||
<sl-menu-item value="export">
|
||||
Export current file
|
||||
<sl-icon name="file-earmark-arrow-up" slot="prefix"></sl-icon>
|
||||
</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item value="editorSettings"
|
||||
>Editor Settings</sl-menu-item
|
||||
>
|
||||
<sl-menu-item value="editorSettings">
|
||||
Editor Settings
|
||||
<sl-icon name="sliders2" slot="prefix"></sl-icon>
|
||||
</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item value="keyboardShortcuts"
|
||||
>Editor Keyboard Shortcuts</sl-menu-item
|
||||
>
|
||||
<sl-menu-item value="keyboardShortcuts">
|
||||
Editor Keyboard Shortcuts
|
||||
<sl-icon name="command" slot="prefix"></sl-icon>
|
||||
</sl-menu-item>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item>
|
||||
Presets
|
||||
<sl-icon name="code-square" slot="prefix"></sl-icon>
|
||||
<sl-menu slot="submenu">
|
||||
<sl-menu-item value="preset-demo">
|
||||
Demo
|
||||
</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="nav navbar-nav navbar-header ms-2">
|
||||
<a class="navbar-brand" aria-current="page" href=""
|
||||
>Stationeers IC10 Emulator</a
|
||||
>
|
||||
<div class="nav navbar-header ms-2 hstack">
|
||||
<div>
|
||||
<a class="navbar-brand" aria-current="page" href="">
|
||||
Stationeers IC10 Emulator
|
||||
</a>
|
||||
</div>
|
||||
<div class="hstack version mt-auto mb-auto">
|
||||
<small>v${this.appVer}-${this.gitVer}</small>
|
||||
<small class="ms-2"><sl-relative-time date=${this.buildDate}></sl-relative-time></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav navbar-nav ms-auto d-flex flex-row">
|
||||
@@ -183,8 +227,11 @@ export class Nav extends BaseElement {
|
||||
case "save":
|
||||
this.dispatchEvent(new CustomEvent("app-save", { bubbles: true }));
|
||||
break;
|
||||
case "saveAs":
|
||||
this.dispatchEvent(new CustomEvent("app-save-as", { bubbles: true }));
|
||||
case "load":
|
||||
this.dispatchEvent(new CustomEvent("app-load", { bubbles: true }));
|
||||
break;
|
||||
case "export":
|
||||
this.dispatchEvent(new CustomEvent("app-export", { bubbles: true }));
|
||||
break;
|
||||
case "editorSettings":
|
||||
window.Editor.settingDialog.show();
|
||||
@@ -192,6 +239,8 @@ export class Nav extends BaseElement {
|
||||
case "keyboardShortcuts":
|
||||
window.Editor.kbShortcuts.show();
|
||||
break;
|
||||
case 'preset-demo':
|
||||
window.location.hash = "demo";
|
||||
default:
|
||||
console.log("Unknown main menu item", item.value);
|
||||
}
|
||||
|
||||
@@ -137,6 +137,9 @@ export class Session extends EventTarget {
|
||||
|
||||
setProgramCode(id: number, code: string) {
|
||||
this._programs.set(id, code);
|
||||
if (this.app.vm) {
|
||||
this.app.vm.updateCode();
|
||||
}
|
||||
this.save();
|
||||
}
|
||||
|
||||
@@ -184,9 +187,6 @@ export class Session extends EventTarget {
|
||||
save() {
|
||||
if (this._save_timeout) clearTimeout(this._save_timeout);
|
||||
this._save_timeout = setTimeout(() => {
|
||||
if (this.app.vm) {
|
||||
this.app.vm.updateCode();
|
||||
}
|
||||
this.saveToFragment();
|
||||
this._save_timeout = undefined;
|
||||
}, 1000);
|
||||
|
||||
@@ -12,21 +12,37 @@ export function docReady(fn: () => void) {
|
||||
}
|
||||
}
|
||||
|
||||
function isZeroNegative(zero: number) {
|
||||
const isZero = zero === 0;
|
||||
const isNegative = 1 / zero === -Infinity;
|
||||
return isNegative && isZero;
|
||||
}
|
||||
|
||||
function replacer(key: any, value: any) {
|
||||
if(value instanceof Map) {
|
||||
export function numberToString(n: number): string {
|
||||
if (isZeroNegative(n)) return "-0";
|
||||
return n.toString();
|
||||
}
|
||||
export function displayNumber(n: number): string {
|
||||
return numberToString(n).replace("Infinity", "∞");
|
||||
}
|
||||
|
||||
function replacer(_key: any, value: any) {
|
||||
if (value instanceof Map) {
|
||||
return {
|
||||
dataType: 'Map',
|
||||
dataType: "Map",
|
||||
value: Array.from(value.entries()), // or with spread: value: [...value]
|
||||
};
|
||||
} else if (Number.isNaN(value)) {
|
||||
} else if (
|
||||
typeof value === "number" &&
|
||||
(!Number.isFinite(value) || Number.isNaN(value) || isZeroNegative(value))
|
||||
) {
|
||||
return {
|
||||
dataType: 'Number',
|
||||
value: "NaN",
|
||||
dataType: "Number",
|
||||
value: numberToString(value),
|
||||
};
|
||||
} else if (typeof value === "undefined" ) {
|
||||
} else if (typeof value === "undefined") {
|
||||
return {
|
||||
dataType: 'undefined',
|
||||
dataType: "undefined",
|
||||
};
|
||||
} else {
|
||||
return value;
|
||||
@@ -34,12 +50,12 @@ function replacer(key: any, value: any) {
|
||||
}
|
||||
|
||||
function reviver(_key: any, value: any) {
|
||||
if(typeof value === 'object' && value !== null) {
|
||||
if (value.dataType === 'Map') {
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if (value.dataType === "Map") {
|
||||
return new Map(value.value);
|
||||
} else if (value.dataType === 'Number') {
|
||||
return parseFloat(value.value)
|
||||
} else if (value.dataType === 'undefined') {
|
||||
} else if (value.dataType === "Number") {
|
||||
return parseFloat(value.value);
|
||||
} else if (value.dataType === "undefined") {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -51,14 +67,13 @@ export function toJson(value: any): string {
|
||||
}
|
||||
|
||||
export function fromJson(value: string): any {
|
||||
return JSON.parse(value, reviver)
|
||||
return JSON.parse(value, reviver);
|
||||
}
|
||||
|
||||
export function structuralEqual(a: any, b: any): boolean {
|
||||
const _a = JSON.stringify(a, replacer);
|
||||
const _b = JSON.stringify(b, replacer);
|
||||
return _a === _b;
|
||||
|
||||
}
|
||||
|
||||
// probably not needed, fetch() exists now
|
||||
@@ -173,26 +188,26 @@ export async function openFile(editor: Ace.Editor) {
|
||||
|
||||
export function parseNumber(s: string): number {
|
||||
switch (s.toLowerCase()) {
|
||||
case 'nan':
|
||||
case "nan":
|
||||
return Number.NaN;
|
||||
case 'pinf':
|
||||
case "pinf":
|
||||
return Number.POSITIVE_INFINITY;
|
||||
case 'ninf':
|
||||
case "ninf":
|
||||
return Number.NEGATIVE_INFINITY;
|
||||
case 'pi':
|
||||
case "pi":
|
||||
return 3.141592653589793;
|
||||
case 'deg2rad':
|
||||
case "deg2rad":
|
||||
return 0.0174532923847437;
|
||||
case 'rad2deg':
|
||||
case "rad2deg":
|
||||
return 57.2957801818848;
|
||||
case 'epsilon':
|
||||
case "epsilon":
|
||||
return Number.EPSILON;
|
||||
}
|
||||
if (/^%[01]+$/.test(s)) {
|
||||
return parseInt(s.slice(1), 2)
|
||||
return parseInt(s.slice(1), 2);
|
||||
}
|
||||
if (/^\$[0-9A-Fa-f]+$/.test(s)) {
|
||||
return parseInt(s.slice(1), 16)
|
||||
return parseInt(s.slice(1), 16);
|
||||
}
|
||||
if (/[a-fA-F]/.test(s)) {
|
||||
const hex = parseHex(s);
|
||||
@@ -200,10 +215,11 @@ export function parseNumber(s: string): number {
|
||||
return hex;
|
||||
}
|
||||
}
|
||||
s.replace("∞", "Infinity");
|
||||
return parseFloat(s);
|
||||
}
|
||||
|
||||
export function parseHex(h: string) : number {
|
||||
export function parseHex(h: string): number {
|
||||
var val = parseInt(h, 16);
|
||||
if (val.toString(16) === h.toLowerCase()) {
|
||||
return val;
|
||||
@@ -214,10 +230,10 @@ export function parseHex(h: string) : number {
|
||||
|
||||
export function parseIntWithHexOrBinary(s: string): number {
|
||||
if (/^%[01]+$/.test(s)) {
|
||||
return parseInt(s.slice(1), 2)
|
||||
return parseInt(s.slice(1), 2);
|
||||
}
|
||||
if (/^\$[0-9A-Fa-f]+$/.test(s)) {
|
||||
return parseInt(s.slice(1), 16)
|
||||
return parseInt(s.slice(1), 16);
|
||||
}
|
||||
if (/[a-fA-F]/.test(s)) {
|
||||
const hex = parseHex(s);
|
||||
|
||||
@@ -36,6 +36,7 @@ import "@shoelace-style/shoelace/dist/components/icon/icon.js";
|
||||
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
|
||||
import {
|
||||
displayNumber,
|
||||
parseIntWithHexOrBinary,
|
||||
parseNumber,
|
||||
structuralEqual,
|
||||
@@ -274,7 +275,7 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
return html` <sl-input
|
||||
id="${inputIdBase}${name}"
|
||||
key="${name}"
|
||||
value="${field.value}"
|
||||
value="${displayNumber(field.value)}"
|
||||
size="small"
|
||||
@sl-change=${this._handleChangeField}
|
||||
>
|
||||
@@ -334,8 +335,14 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
<div class="slot-fields">
|
||||
${fields.map(
|
||||
([name, field], _index, _fields) => html`
|
||||
<sl-input id="${inputIdBase}${name}" slotIndex=${slotIndex} key="${name}" value="${field.value}" size="small"
|
||||
@sl-change=${this._handleChangeSlotField}>
|
||||
<sl-input
|
||||
id="${inputIdBase}${name}"
|
||||
slotIndex=${slotIndex}
|
||||
key="${name}"
|
||||
value="${displayNumber(field.value)}"
|
||||
size="small"
|
||||
@sl-change=${this._handleChangeSlotField}
|
||||
>
|
||||
<span slot="prefix">${name}</span>
|
||||
<sl-copy-button slot="suffix" from="${inputIdBase}${name}.value"></sl-copy-button>
|
||||
<span slot="suffix">${field.field_type}</span>
|
||||
@@ -1023,7 +1030,7 @@ export class VmDeviceTemplate extends BaseElement {
|
||||
return html`
|
||||
<sl-input
|
||||
key="${name}"
|
||||
value="${field.value}"
|
||||
value="${displayNumber(field.value)}"
|
||||
size="small"
|
||||
@sl-change=${this._handleChangeField}
|
||||
?disabled=${name === "PrefabHash"}
|
||||
|
||||
@@ -137,7 +137,7 @@ class VirtualMachine extends EventTarget {
|
||||
const attempt = Date.now().toString(16);
|
||||
const ic = this._ics.get(id);
|
||||
const prog = progs.get(id);
|
||||
if (ic && prog) {
|
||||
if (ic && prog && ic.code !== prog) {
|
||||
try {
|
||||
console.time(`CompileProgram_${id}_${attempt}`);
|
||||
this.ics.get(id)!.setCodeInvalid(progs.get(id)!);
|
||||
|
||||
@@ -9,7 +9,7 @@ import "@shoelace-style/shoelace/dist/components/tooltip/tooltip.js";
|
||||
import "@shoelace-style/shoelace/dist/components/input/input.js";
|
||||
import { RegisterSpec } from "ic10emu_wasm";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
|
||||
import { parseNumber } from "../utils";
|
||||
import { displayNumber, parseNumber } from "../utils";
|
||||
|
||||
@customElement("vm-ic-registers")
|
||||
export class VMICRegisters extends VMActiveICMixin(BaseElement) {
|
||||
@@ -47,16 +47,6 @@ export class VMICRegisters extends VMActiveICMixin(BaseElement) {
|
||||
}
|
||||
|
||||
protected render() {
|
||||
// const inputTypeFromVal = (val: number) => { if (val === Number.NEGATIVE_INFINITY || val === Number.POSITIVE_INFINITY || Number.isNaN(val)) { return "text"; } else { return "number"; } };
|
||||
const displayVal = (val: number) => {
|
||||
if (Number.POSITIVE_INFINITY === val) {
|
||||
return "∞";
|
||||
} else if (Number.NEGATIVE_INFINITY === val) {
|
||||
return "-∞";
|
||||
} else {
|
||||
return val.toString();
|
||||
}
|
||||
};
|
||||
const registerAliases: [string, number][] = (
|
||||
(
|
||||
[...(this.aliases ?? [])].filter(
|
||||
@@ -83,7 +73,7 @@ export class VMICRegisters extends VMActiveICMixin(BaseElement) {
|
||||
</div>
|
||||
<sl-input
|
||||
type="text"
|
||||
value="${displayVal(val)}"
|
||||
value="${displayNumber(val)}"
|
||||
size="small"
|
||||
class="reg-input"
|
||||
@sl-change=${this._handleCellChange}
|
||||
|
||||
@@ -8,7 +8,7 @@ import "@shoelace-style/shoelace/dist/components/icon/icon.js";
|
||||
import "@shoelace-style/shoelace/dist/components/tooltip/tooltip.js";
|
||||
import "@shoelace-style/shoelace/dist/components/input/input.js";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
|
||||
import { parseNumber } from "../utils";
|
||||
import { displayNumber, parseNumber } from "../utils";
|
||||
|
||||
@customElement("vm-ic-stack")
|
||||
export class VMICStack extends VMActiveICMixin(BaseElement) {
|
||||
@@ -44,15 +44,6 @@ export class VMICStack extends VMActiveICMixin(BaseElement) {
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const displayVal = (val: number) => {
|
||||
if (Number.POSITIVE_INFINITY === val) {
|
||||
return "∞";
|
||||
} else if (Number.NEGATIVE_INFINITY === val) {
|
||||
return "-∞";
|
||||
} else {
|
||||
return val.toString();
|
||||
}
|
||||
};
|
||||
const sp = this.registers![16];
|
||||
|
||||
return html`
|
||||
@@ -67,7 +58,7 @@ export class VMICStack extends VMActiveICMixin(BaseElement) {
|
||||
</div>
|
||||
<sl-input
|
||||
type="text"
|
||||
value="${displayVal(val)}"
|
||||
value="${displayNumber(val)}"
|
||||
size="small"
|
||||
class="stack-input ${sp === index ? "stack-pointer" : ""}"
|
||||
@sl-change=${this._handleCellChange}
|
||||
|
||||
Reference in New Issue
Block a user