perf: vastly improve load speed

- delay render non visable device tabs

in the future there are some significant speed gains to be made by
limiting calls into the rust VM for data. serialize it all up front.

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers
2024-04-25 15:04:51 -07:00
parent dbb2f71fdc
commit 6cc2189921
21 changed files with 177 additions and 133 deletions

View File

@@ -30,7 +30,7 @@ fn write_repr_enum<'a, T: std::io::Write, I, P>(
let additional_strum = if use_phf { "#[strum(use_phf)]\n" } else { "" };
write!(
writer,
"#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash, EnumString, AsRefStr, EnumProperty, EnumIter, Serialize, Deserialize)]\n\
"#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, EnumString, AsRefStr, EnumProperty, EnumIter, Serialize, Deserialize)]\n\
{additional_strum}\
pub enum {name} {{\n"
)

View File

@@ -1,3 +1,6 @@
#![allow(non_snake_case)]
// use std::collections::BTreeMap;
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
@@ -57,3 +60,38 @@ impl From<&ic10emu::device::Slot> for Slot {
include!(concat!(env!("OUT_DIR"), "/ts_types.rs"));
// #[serde_as]
// #[derive(Tsify, Serialize, Deserialize)]
// #[tsify(into_wasm_abi, from_wasm_abi)]
// pub struct DeviceLogicField {
// field_type: FieldType,
// value: f64,
// }
//
// #[serde_as]
// #[derive(Tsify, Serialize, Deserialize)]
// #[tsify(into_wasm_abi, from_wasm_abi)]
// pub struct DeviceSlot {
// typ: SlotType,
// #[serde_as(as = "Vec<(_, _)>")]
// fields: BTreeMap<SlotLogicType, DeviceLogicField>,
// }
//
//
// #[serde_as]
// #[derive(Tsify, Serialize, Deserialize)]
// #[tsify(into_wasm_abi, from_wasm_abi)]
// pub struct DeviceState{
// name: Option<String>,
// name_hash: Option<i32>,
// prefab_name: Option<String>,
// #[serde_as(as = "Vec<(_, _)>")]
// fields: BTreeMap<LogicType, DeviceLogicField>,
// slots: Vec<DeviceSlot>,
// #[serde_as(as = "Vec<(_, _)>")]
// reagents: BTreeMap<ReagentMode, Vec<(i32, f64)>>,
// connections: Vec<Connection>,
// ic: Option<u32>,
// }
// serde_with::DisplayFromStr

View File

@@ -4,14 +4,6 @@ import { BaseElement, defaultCss } from "../components";
import "./nav";
import "./share";
import { ShareSessionDialog } from "./share";
import { setBasePath } from "@shoelace-style/shoelace/dist/utilities/base-path.js";
// Set the base path to the folder you copied Shoelace's assets to
setBasePath("shoelace");
import "@shoelace-style/shoelace/dist/components/split-panel/split-panel.js";
import "../editor";
import { IC10Editor } from "../editor";
import { Session } from "../session";

View File

@@ -2,5 +2,4 @@ import { App } from "./app";
import { Nav } from "./nav";
import { SaveDialog } from "./save";
import { ShareSessionDialog } from "./share";
import "./icons";
export { App, Nav, ShareSessionDialog }

View File

@@ -2,14 +2,6 @@ import { HTMLTemplateResult, html, css } from "lit";
import { customElement, property } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import "@shoelace-style/shoelace/dist/components/icon/icon.js";
import "@shoelace-style/shoelace/dist/components/icon-button/icon-button.js";
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")

View File

@@ -3,11 +3,6 @@ import { customElement, property, query, state } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMState } from "session";
import "@shoelace-style/shoelace/dist/components/dialog/dialog.js";
import "@shoelace-style/shoelace/dist/components/format-date/format-date.js";
import "@shoelace-style/shoelace/dist/components/relative-time/relative-time.js";
import "@shoelace-style/shoelace/dist/components/format-bytes/format-bytes.js";
import "@shoelace-style/shoelace/dist/components/spinner/spinner.js";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
import { repeat } from "lit/directives/repeat.js";
import SlDialog from "@shoelace-style/shoelace/dist/components/dialog/dialog.js";

View File

@@ -2,11 +2,6 @@ import { HTMLTemplateResult, html, css } from "lit";
import { customElement, property, query } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import "@shoelace-style/shoelace/dist/components/dialog/dialog.js";
import "@shoelace-style/shoelace/dist/components/input/input.js";
import "@shoelace-style/shoelace/dist/components/icon/icon.js";
import "@shoelace-style/shoelace/dist/components/icon-button/icon-button.js";
import "@shoelace-style/shoelace/dist/components/copy-button/copy-button.js";
import SlDialog from "@shoelace-style/shoelace/dist/components/dialog/dialog.js";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";

View File

@@ -5,9 +5,6 @@ import { BaseElement, defaultCss } from "components";
import { SlDialog, SlSwitch } from "@shoelace-style/shoelace";
import { until } from "lit/directives/until.js";
import "@shoelace-style/shoelace/dist/components/spinner/spinner.js";
import '@shoelace-style/shoelace/dist/components/switch/switch.js';
import { marked } from "marked";
import { gfmStyles } from "./gfm-styles";

View File

@@ -1,9 +1,6 @@
import {
html,
css,
HTMLTemplateResult,
PropertyValueMap,
CSSResultGroup,
} from "lit";
import { customElement, query, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";

View File

@@ -2,13 +2,6 @@ import { ace, Ace, Range, AceLanguageClient, setupLspWorker } from "./ace";
import { LanguageProvider } from "ace-linters/types/language-provider";
import "@shoelace-style/shoelace/dist/components/dialog/dialog.js";
import "@shoelace-style/shoelace/dist/components/button-group/button-group.js";
import "@shoelace-style/shoelace/dist/components/button/button.js";
import "@shoelace-style/shoelace/dist/components/input/input.js";
import "@shoelace-style/shoelace/dist/components/radio-button/radio-button.js";
import "@shoelace-style/shoelace/dist/components/radio-group/radio-group.js";
import "@shoelace-style/shoelace/dist/components/switch/switch.js";
import SlDialog from "@shoelace-style/shoelace/dist/components/dialog/dialog.js";
import SlRadioGroup from "@shoelace-style/shoelace/dist/components/radio-group/radio-group.js";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";

View File

@@ -1,6 +1,43 @@
import "@popperjs/core";
import "../scss/styles.scss";
import { Dropdown, Modal } from "bootstrap";
import { setBasePath } from "@shoelace-style/shoelace/dist/utilities/base-path.js";
setBasePath("shoelace");
import "./icons";
import "@shoelace-style/shoelace/dist/components/split-panel/split-panel.js";
import "@shoelace-style/shoelace/dist/components/dialog/dialog.js";
import "@shoelace-style/shoelace/dist/components/drawer/drawer.js";
import "@shoelace-style/shoelace/dist/components/icon/icon.js";
import "@shoelace-style/shoelace/dist/components/icon-button/icon-button.js";
import "@shoelace-style/shoelace/dist/components/copy-button/copy-button.js";
import "@shoelace-style/shoelace/dist/components/button-group/button-group.js";
import "@shoelace-style/shoelace/dist/components/button/button.js";
import '@shoelace-style/shoelace/dist/components/switch/switch.js';
import "@shoelace-style/shoelace/dist/components/radio-button/radio-button.js";
import "@shoelace-style/shoelace/dist/components/radio-group/radio-group.js";
import "@shoelace-style/shoelace/dist/components/menu/menu.js";
import "@shoelace-style/shoelace/dist/components/menu-item/menu-item.js";
import "@shoelace-style/shoelace/dist/components/divider/divider.js";
import "@shoelace-style/shoelace/dist/components/dropdown/dropdown.js";
import "@shoelace-style/shoelace/dist/components/tooltip/tooltip.js";
import "@shoelace-style/shoelace/dist/components/input/input.js";
import "@shoelace-style/shoelace/dist/components/spinner/spinner.js";
import "@shoelace-style/shoelace/dist/components/card/card.js";
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/select/select.js";
import "@shoelace-style/shoelace/dist/components/badge/badge.js";
import "@shoelace-style/shoelace/dist/components/option/option.js";
import "@shoelace-style/shoelace/dist/components/alert/alert.js";
import "@shoelace-style/shoelace/dist/components/format-number/format-number.js";
import "@shoelace-style/shoelace/dist/components/format-date/format-date.js";
import "@shoelace-style/shoelace/dist/components/format-bytes/format-bytes.js";
import "@shoelace-style/shoelace/dist/components/relative-time/relative-time.js";
import "ace-builds";
import "ace-builds/esm-resolver";
class DeferedApp {

View File

@@ -3,15 +3,6 @@ import { customElement, query } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMActiveICMixin } from "virtual_machine/base_device";
import "@shoelace-style/shoelace/dist/components/card/card.js";
import "@shoelace-style/shoelace/dist/components/button-group/button-group.js";
import "@shoelace-style/shoelace/dist/components/button/button.js";
import "@shoelace-style/shoelace/dist/components/icon/icon.js";
import "@shoelace-style/shoelace/dist/components/tooltip/tooltip.js";
import "@shoelace-style/shoelace/dist/components/divider/divider.js";
import "@shoelace-style/shoelace/dist/components/select/select.js";
import "@shoelace-style/shoelace/dist/components/badge/badge.js";
import "@shoelace-style/shoelace/dist/components/option/option.js";
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.js";
@customElement("vm-ic-controls")

View File

@@ -1,13 +1,25 @@
import { html, css, HTMLTemplateResult } 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 { VMDeviceDBMixin, VMDeviceMixin } from "virtual_machine/base_device";
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
import { displayNumber, parseIntWithHexOrBinary, parseNumber } from "utils";
import { LogicType, Slot, SlotLogicType, SlotOccupant, SlotType } from "ic10emu_wasm";
import {
LogicType,
Slot,
SlotLogicType,
SlotOccupant,
SlotType,
} from "ic10emu_wasm";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
import SlDialog from "@shoelace-style/shoelace/dist/components/dialog/dialog.component.js";
import "./slot"
import "./slot";
import { when } from "lit/directives/when.js";
import { cache } from "lit/directives/cache.js";
import { until } from "lit/directives/until.js";
import { repeat } from "lit/directives/repeat.js";
export type CardTab = "fields" | "slots" | "reagents" | "networks" | "pins";
@customElement("vm-device-card")
export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
@@ -151,8 +163,8 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
<span slot="prefix">Id</span>
<sl-copy-button slot="suffix" .value=${this.deviceID}></sl-copy-button>
</sl-input>
<sl-input id="vmDeviceCard${this.deviceID}Name" class="device-name me-1" size="small" pill placeholder=${this.prefabName}
value=${this.name} @sl-change=${this._handleChangeName}>
<sl-input id="vmDeviceCard${this.deviceID}Name" class="device-name me-1" size="small" pill
placeholder=${this.prefabName} value=${this.name} @sl-change=${this._handleChangeName}>
<span slot="prefix">Name</span>
<sl-copy-button slot="suffix" from="vmDeviceCard${this.deviceID}Name.value"></sl-copy-button>
</sl-input>
@@ -172,10 +184,10 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
`;
}
renderFields(): HTMLTemplateResult {
renderFields() {
const fields = Array.from(this.fields.entries());
const inputIdBase = `vmDeviceCard${this.deviceID}Field`;
return html`
return this.delayRenderTab("fields", html`
${fields.map(([name, field], _index, _fields) => {
return html` <sl-input id="${inputIdBase}${name}" key="${name}" value="${displayNumber(field.value)}" size="small"
@sl-change=${this._handleChangeField}>
@@ -184,10 +196,9 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
<span slot="suffix">${field.field_type}</span>
</sl-input>`;
})}
`;
`);
}
_onSlotImageErr(e: Event) {
console.log("image_err", e);
}
@@ -195,26 +206,26 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
static transparentImg =
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" as const;
renderSlots(): HTMLTemplateResult {
return html`
async renderSlots() {
return this.delayRenderTab("slots", html`
<div class="flex flex-row flex-wrap">
${this.slots.map((_slot, index, _slots) => html`
<vm-device-slot
.deviceID=${this.deviceID}
.slotIndex=${index}
class-"flex flex-row max-w-lg mr-2 mb-2"
>
</vm-device-slot>
` )}
${repeat(
this.slots,
(_slot, index) => index,
(_slot, index) => html`
<vm-device-slot .deviceID=${this.deviceID} .slotIndex=${index} class-"flex flex-row max-w-lg mr-2 mb-2">
</vm-device-slot>
`,
)}
</div>
`;
`);
}
renderReagents(): HTMLTemplateResult {
return html``;
renderReagents() {
return this.delayRenderTab("reagents", html``);
}
renderNetworks(): HTMLTemplateResult {
renderNetworks() {
const vmNetworks = window.VM.vm.networks;
const networks = this.connections.map((connection, index, _conns) => {
const conn =
@@ -231,13 +242,10 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
</sl-select>
`;
});
return html`
<div class="networks">
${networks}
</div>
`;
return this.delayRenderTab("networks", html`<div class="networks">${networks}</div>`);
}
renderPins(): HTMLTemplateResult {
renderPins() {
const pins = this.pins;
const visibleDevices = window.VM.vm.visibleDevices(this.deviceID);
const pinsHtml = pins?.map(
@@ -255,29 +263,73 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
)}
</sl-select>`,
);
return html`
<div class="pins" >
${pinsHtml}
</div>
`;
return this.delayRenderTab("pins", html`<div class="pins">${pinsHtml}</div>`);
}
private tabsShown: CardTab[] = ["fields"];
private tabResolves: {
[key in CardTab]: {
result?: HTMLTemplateResult;
resolver?: (result: HTMLTemplateResult) => void;
};
} = {
fields: {},
slots: {},
reagents: {},
networks: {},
pins: {},
};
delayRenderTab(
name: CardTab,
result: HTMLTemplateResult,
): Promise<HTMLTemplateResult> {
this.tabResolves[name].result = result;
return new Promise((resolve) => {
if (this.tabsShown.includes(name)) {
this.tabResolves[name].resolver = undefined;
resolve(result);
} else {
this.tabResolves[name].resolver = resolve;
}
});
}
resolveTab(name: CardTab) {
if (
typeof this.tabResolves[name].resolver !== "undefined" &&
typeof this.tabResolves[name].result !== "undefined"
) {
this.tabResolves[name].resolver(this.tabResolves[name].result);
this.tabsShown.push(name);
}
}
render(): HTMLTemplateResult {
return html`
<ic10-details class="device-card" ?open=${this.open}>
<div class="header" slot="summary">${this.renderHeader()}</div>
<sl-tab-group>
<sl-tab-group @sl-tab-show=${this._handleTabChange}>
<sl-tab slot="nav" panel="fields" active>Fields</sl-tab>
<sl-tab slot="nav" panel="slots">Slots</sl-tab>
<sl-tab slot="nav" panel="reagents" disabled>Reagents</sl-tab>
<sl-tab slot="nav" panel="networks">Networks</sl-tab>
<sl-tab slot="nav" panel="pins" ?disabled=${!this.pins}>Pins</sl-tab>
<sl-tab-panel name="fields" active>${this.renderFields()}</sl-tab-panel>
<sl-tab-panel name="slots">${this.renderSlots()}</sl-tab-panel>
<sl-tab-panel name="reagents">${this.renderReagents()}</sl-tab-panel>
<sl-tab-panel name="networks">${this.renderNetworks()}</sl-tab-panel>
<sl-tab-panel name="pins">${this.renderPins()}</sl-tab-panel>
<sl-tab-panel name="fields" active>
${until(this.renderFields(), html`<sl-spinner></sl-spinner>`)}
</sl-tab-panel>
<sl-tab-panel name="slots">
${until(this.renderSlots(), html`<sl-spinner></sl-spinner>`)}
</sl-tab-panel>
<sl-tab-panel name="reagents">
${until(this.renderReagents(), html`<sl-spinner></sl-spinner>`)}
</sl-tab-panel>
<sl-tab-panel name="networks">
${until(this.renderNetworks(), html`<sl-spinner></sl-spinner>`)}
</sl-tab-panel>
<sl-tab-panel name="pins"> ${this.renderPins()} </sl-tab-panel>
</sl-tab-group>
</ic10-details>
<sl-dialog class="remove-device-dialog" no-header @sl-request-close=${this._preventOverlayClose}>
@@ -297,6 +349,10 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
`;
}
_handleTabChange(e: CustomEvent<{ name: string }>) {
setTimeout(() => this.resolveTab(e.detail.name as CardTab), 100);
}
@query(".remove-device-dialog") removeDialog: SlDialog;
_preventOverlayClose(event: CustomEvent) {
@@ -313,7 +369,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
const input = e.target as SlInput;
const val = parseIntWithHexOrBinary(input.value);
if (!isNaN(val)) {
window.VM.get().then(vm => {
window.VM.get().then((vm) => {
if (!vm.changeDeviceId(this.deviceID, val)) {
input.value = this.deviceID.toString();
}
@@ -326,7 +382,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
_handleChangeName(e: CustomEvent) {
const input = e.target as SlInput;
const name = input.value.length === 0 ? undefined : input.value;
window.VM.get().then(vm => {
window.VM.get().then((vm) => {
if (!vm.setDeviceName(this.deviceID, name)) {
input.value = this.name;
}
@@ -372,4 +428,3 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
this.updateDevice();
}
}

View File

@@ -3,7 +3,7 @@ import { customElement, query, state } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
import { structuralEqual } from "../../utils";
import { structuralEqual } from "utils";
import { repeat } from "lit/directives/repeat.js";
import { default as uFuzzy } from "@leeoniya/ufuzzy";

View File

@@ -1,20 +1,3 @@
import "@shoelace-style/shoelace/dist/components/card/card.js";
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 "@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/copy-button/copy-button.js";
import "@shoelace-style/shoelace/dist/components/select/select.js";
import "@shoelace-style/shoelace/dist/components/badge/badge.js";
import "@shoelace-style/shoelace/dist/components/option/option.js";
import "@shoelace-style/shoelace/dist/components/drawer/drawer.js";
import "@shoelace-style/shoelace/dist/components/icon/icon.js";
import "@shoelace-style/shoelace/dist/components/format-number/format-number.js";
import "./template"
import "./card"
import "./device_list"

View File

@@ -1,25 +1,18 @@
import type {
Connection,
DeviceTemplate,
LogicField,
LogicFields,
LogicType,
Slot,
SlotTemplate,
SlotOccupant,
SlotOccupantTemplate,
SlotLogicType,
ConnectionCableNetwork,
SlotType,
} from "ic10emu_wasm";
import { html, css, HTMLTemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import type { DeviceDB, DeviceDBEntry } from "virtual_machine/device_db";
import { connectionFromDeviceDBConnection } from "./utils";
import { connectionFromDeviceDBConnection } from "./dbutils";
import { displayNumber, parseNumber } from "utils";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";

View File

@@ -3,10 +3,6 @@ import { customElement } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMActiveICMixin } from "virtual_machine/base_device";
import "@shoelace-style/shoelace/dist/components/card/card.js";
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 { RegisterSpec } from "ic10emu_wasm";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
import { displayNumber, parseNumber } from "utils";

View File

@@ -3,10 +3,6 @@ import { customElement } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMActiveICMixin } from "virtual_machine/base_device";
import "@shoelace-style/shoelace/dist/components/card/card.js";
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 { displayNumber, parseNumber } from "utils";

View File

@@ -1,11 +1,6 @@
import { html, css } from "lit";
import { customElement } 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";