refactor everything to typescript
We don't need React -- we'll reinvent it ourselves! Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
31f0dff822
commit
ceb2d55543
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "ui-barebones",
|
||||
"name": "ui",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
178
src/index.ts
178
src/index.ts
@ -1,16 +1,9 @@
|
||||
import { FarpatchWidget } from './interfaces';
|
||||
import { UartWidget } from './interface/uart';
|
||||
import { DashboardWidget } from './interface/dashboard';
|
||||
|
||||
var FarpatchWidgets: { [key: string]: FarpatchWidget } = {
|
||||
"dashboard": new DashboardWidget(),
|
||||
"uart": new UartWidget(),
|
||||
};
|
||||
import { FarpatchWidget, farpatchWidgets } from './interfaces';
|
||||
|
||||
// TODO: Get the current widget from the address bar, if one exists
|
||||
var currentWidgetName: string = "dashboard";
|
||||
var currentWidget: FarpatchWidget = FarpatchWidgets[currentWidgetName];
|
||||
var currentWidgetView: HTMLElement = document.getElementById(currentWidgetName + "-view") as HTMLElement;
|
||||
var currentWidget: FarpatchWidget = farpatchWidgets[0];
|
||||
var widgetViews: HTMLElement[] = [];
|
||||
// var currentWidgetView: HTMLElement = document.getElementById(currentWidgetName + "-view") as HTMLElement;
|
||||
|
||||
function addToggleSidebarListener(element: HTMLElement) {
|
||||
// Hide or show the sidebar. MaterialUI calls this toggling between
|
||||
@ -31,97 +24,88 @@ function addToggleSidebarListener(element: HTMLElement) {
|
||||
}, false);
|
||||
}
|
||||
|
||||
function deactivateWidget(widget: FarpatchWidget) {
|
||||
widget.navItem.classList.remove("sidenav-item-active");
|
||||
widgetViews[widget.index].classList.remove("main-content-active");
|
||||
widget.onBlur(widgetViews[widget.index]);
|
||||
}
|
||||
|
||||
function activateWidget(widget: FarpatchWidget) {
|
||||
widget.navItem.classList.add("sidenav-item-active");
|
||||
widgetViews[widget.index].classList.add("main-content-active");
|
||||
widget.onFocus(widgetViews[widget.index]);
|
||||
}
|
||||
|
||||
function switchToWidget(widget: FarpatchWidget) {
|
||||
if (widget === currentWidget) {
|
||||
return;
|
||||
}
|
||||
deactivateWidget(currentWidget);
|
||||
currentWidget = widget;
|
||||
activateWidget(widget);
|
||||
}
|
||||
|
||||
// On load, add a listener for mouse clicks on the navigation bar.
|
||||
function setupNavItem(element: HTMLElement) {
|
||||
// Get the base name of the element in order to set up the callback.
|
||||
var elementBase = element.id.replace("-button", "");
|
||||
if (typeof (FarpatchWidgets[elementBase]) === 'undefined') {
|
||||
console.log("No widget found for " + elementBase);
|
||||
// return;
|
||||
} else {
|
||||
console.log("Setting up widget for " + elementBase);
|
||||
var elementContent = document.getElementById(elementBase + "-view");
|
||||
if (typeof (elementContent) === 'undefined' || elementContent === null) {
|
||||
console.log("No element found for " + elementBase + "-view");
|
||||
return;
|
||||
}
|
||||
FarpatchWidgets[elementBase].onInit();
|
||||
|
||||
if (FarpatchWidgets[elementBase] === currentWidget) {
|
||||
element.classList.add("sidenav-item-active");
|
||||
FarpatchWidgets[elementBase].onFocus(elementContent);
|
||||
currentWidgetView = elementContent;
|
||||
elementContent.classList.add("main-content-active");
|
||||
} else {
|
||||
console.log("Not initializing widget " + elementBase);
|
||||
}
|
||||
}
|
||||
|
||||
// Hook "click" for each navigation item. The listener will:
|
||||
// 1. Loop through each nav item and remove the "active" class for
|
||||
// each inactive item and add it to the newly-clicked item.
|
||||
// 2. Unhide the correct view to activate it
|
||||
element.addEventListener('click', function () {
|
||||
var main = document.getElementsByTagName("main")[0];
|
||||
var navItems = document.querySelectorAll(".sidenav-item");
|
||||
var elementBase = element.id.replace("-button", "");
|
||||
if (typeof (FarpatchWidgets[elementBase]) !== 'undefined') {
|
||||
if (FarpatchWidgets[elementBase] !== currentWidget) {
|
||||
var newElementContent = document.getElementById(elementBase + "-view");
|
||||
if (typeof (newElementContent) === 'undefined' || newElementContent === null) {
|
||||
console.log("No element found for " + elementBase + "-view");
|
||||
return;
|
||||
}
|
||||
|
||||
currentWidget.onBlur(currentWidgetView);
|
||||
FarpatchWidgets[elementBase].onFocus(newElementContent);
|
||||
currentWidget = FarpatchWidgets[elementBase];
|
||||
currentWidgetName = elementBase;
|
||||
currentWidgetView = newElementContent;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < navItems.length; i++) {
|
||||
var navItem = navItems[i];
|
||||
if (navItem === element) {
|
||||
if (!navItem.classList.contains("sidenav-item-active")) {
|
||||
navItem.classList.add("sidenav-item-active");
|
||||
}
|
||||
} else {
|
||||
navItem.classList.remove("sidenav-item-active");
|
||||
}
|
||||
}
|
||||
|
||||
var mainViews = document.querySelectorAll(".main-content-active");
|
||||
for (var i = 0; i < mainViews.length; i++) {
|
||||
var mainView = mainViews[i];
|
||||
mainView.classList.remove("main-content-active");
|
||||
}
|
||||
|
||||
var elementBase = element.id.replace("-button", "");
|
||||
var elementContent = document.getElementById(elementBase + "-view");
|
||||
if (typeof (elementContent) === 'undefined' || elementContent === null) {
|
||||
console.log("No element found for " + elementBase + "-view");
|
||||
return;
|
||||
}
|
||||
elementContent.classList.add("main-content-active");
|
||||
function setupNavItem(widget: FarpatchWidget) {
|
||||
var w = widget;
|
||||
widget.navItem.addEventListener('click', function () {
|
||||
switchToWidget(w);
|
||||
}, false);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var navItems = document.querySelectorAll(".sidenav-item");
|
||||
for (var i = 0; i < navItems.length; i++) {
|
||||
// For the "toggle" button, don't add a listener. Instead, have it
|
||||
// toggle the visibility of the sidebar.
|
||||
if (navItems[i].classList.contains("sidenav-item-toggle")) {
|
||||
addToggleSidebarListener(navItems[i] as HTMLElement);
|
||||
} else {
|
||||
setupNavItem(navItems[i] as HTMLElement);
|
||||
}
|
||||
// Populate the page
|
||||
var body = document.getElementsByTagName("body")[0];
|
||||
var sidenav = document.createElement("nav");
|
||||
var mainView: HTMLElement = document.createElement("main");
|
||||
|
||||
sidenav.classList.add("sidenav");
|
||||
var sidenavList = document.createElement("ul");
|
||||
sidenavList.classList.add("sidenav-nav");
|
||||
|
||||
for (var i = 0; i < farpatchWidgets.length; i++) {
|
||||
var widget = farpatchWidgets[i];
|
||||
widget.onInit();
|
||||
|
||||
var widgetView = document.createElement("div");
|
||||
widgetView.classList.add("main-content");
|
||||
widgetView.id = widget.name + "-view";
|
||||
widgetViews.push(widgetView);
|
||||
|
||||
mainView.appendChild(widgetView);
|
||||
sidenavList.appendChild(widget.navItem);
|
||||
|
||||
setupNavItem(widget);
|
||||
}
|
||||
|
||||
// var navItems = document.querySelectorAll(".main-viewport");
|
||||
// for (var i = 0; i < navItems.length; i++) {
|
||||
// addNavListener(navItems[i]);
|
||||
// }
|
||||
// Add the button to collapse the sidebar
|
||||
var sidebarFiller = document.createElement("li");
|
||||
sidebarFiller.classList.add("sidenav-item-filler");
|
||||
sidenavList.appendChild(sidebarFiller);
|
||||
var toggleSidebar = document.createElement("li");
|
||||
toggleSidebar.classList.add("sidenav-item");
|
||||
toggleSidebar.classList.add("sidenav-item-toggle");
|
||||
toggleSidebar.id = "rail-toggle-button";
|
||||
var toggleSidebarLink = document.createElement("a");
|
||||
toggleSidebarLink.classList.add("sidenav-link");
|
||||
var toggleSidebarIcon = document.createElement("span");
|
||||
toggleSidebarIcon.classList.add("las");
|
||||
toggleSidebarIcon.classList.add("la-3x");
|
||||
toggleSidebarIcon.classList.add("la-bars");
|
||||
toggleSidebarIcon.classList.add("icon");
|
||||
var toggleSidebarText = document.createElement("span");
|
||||
toggleSidebarText.classList.add("link-text");
|
||||
toggleSidebarText.innerText = "Hide Sidebar";
|
||||
toggleSidebarLink.appendChild(toggleSidebarIcon);
|
||||
toggleSidebarLink.appendChild(toggleSidebarText);
|
||||
toggleSidebar.appendChild(toggleSidebarLink);
|
||||
sidenavList.appendChild(toggleSidebar);
|
||||
addToggleSidebarListener(toggleSidebar);
|
||||
|
||||
sidenav.appendChild(sidenavList);
|
||||
body.appendChild(sidenav);
|
||||
body.appendChild(mainView);
|
||||
|
||||
currentWidget = farpatchWidgets[0];
|
||||
activateWidget(farpatchWidgets[0]);
|
||||
}, false);
|
||||
|
@ -1,7 +1,21 @@
|
||||
import { FarpatchWidget } from "../interfaces";
|
||||
import { FarpatchWidget, makeNavView as makeNavItem } from "../interfaces";
|
||||
|
||||
export class DashboardWidget implements FarpatchWidget {
|
||||
view: Element = document.createElement("div");
|
||||
index: number = 0;
|
||||
view: HTMLElement = document.createElement("div");
|
||||
navItem: HTMLElement;
|
||||
name: string;
|
||||
icon: string = "home";
|
||||
title: string = "Dashboard";
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
this.navItem = makeNavItem(name, this.icon, this.title);
|
||||
}
|
||||
|
||||
updateIndex(index: number): void {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
onInit(): void {
|
||||
this.view.innerHTML = `
|
||||
|
76
src/interface/debug.ts
Normal file
76
src/interface/debug.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { FarpatchWidget, makeNavView } from "../interfaces";
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { SerializeAddon } from '@xterm/addon-serialize';
|
||||
|
||||
export class DebugWidget implements FarpatchWidget {
|
||||
name: string;
|
||||
icon: string = "scroll";
|
||||
title: string = "Debug";
|
||||
index: number = 0;
|
||||
|
||||
view: HTMLElement;
|
||||
navItem: HTMLElement;
|
||||
|
||||
terminal: Terminal;
|
||||
fitAddon: FitAddon;
|
||||
serializeAddon: SerializeAddon;
|
||||
resizeFunction: () => void;
|
||||
initialized: boolean = false;
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
this.navItem = makeNavView(name, this.icon, this.title);
|
||||
this.view = document.createElement("div");
|
||||
this.view.classList.add("terminal");
|
||||
this.terminal = new Terminal({ theme: { background: "#000000" } });
|
||||
this.fitAddon = new FitAddon();
|
||||
this.serializeAddon = new SerializeAddon();
|
||||
this.resizeFunction = this.resizeTerminal.bind(this);
|
||||
}
|
||||
|
||||
updateIndex(index: number): void {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
onInit(): void {
|
||||
console.log("Initialized Debug Widget");
|
||||
}
|
||||
onFocus(element: HTMLElement): void {
|
||||
console.log("Displaying Debug Widget");
|
||||
if (!this.initialized) {
|
||||
// Ensure the parent frame doesn't get any scrollbars, since we're taking up the whole view
|
||||
element.style.overflow = "hidden";
|
||||
console.log("Initializing xterm.js");
|
||||
// var terminalContainer = document.createElement("div");
|
||||
// this.view.appendChild(terminalContainer);
|
||||
this.terminal.loadAddon(this.fitAddon);
|
||||
this.terminal.loadAddon(this.serializeAddon);
|
||||
this.terminal.onKey((e) => {
|
||||
console.log("Key pressed: " + e.key);
|
||||
this.terminal.write(e.key);
|
||||
if (e.key === '\r') {
|
||||
this.terminal.write('\n');
|
||||
}
|
||||
});
|
||||
this.terminal.open(this.view);
|
||||
this.terminal.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n');
|
||||
this.initialized = true;
|
||||
}
|
||||
element.appendChild(this.view);
|
||||
window.addEventListener('resize', this.resizeFunction);
|
||||
window.setTimeout(() => {
|
||||
this.terminal.focus();
|
||||
this.resizeFunction();
|
||||
}, 10);
|
||||
}
|
||||
onBlur(element: HTMLElement): void {
|
||||
console.log("Archiving UART Widget");
|
||||
element.removeChild(this.view);
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
|
||||
// Whenever the window is resized, update the size of the terminal
|
||||
resizeTerminal() {
|
||||
this.fitAddon.fit();
|
||||
}
|
||||
}
|
76
src/interface/rtt.ts
Normal file
76
src/interface/rtt.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { FarpatchWidget, makeNavView } from "../interfaces";
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { SerializeAddon } from '@xterm/addon-serialize';
|
||||
|
||||
export class RttWidget implements FarpatchWidget {
|
||||
name: string;
|
||||
icon: string = "microchip";
|
||||
title: string = "RTT";
|
||||
index: number = 0;
|
||||
|
||||
view: HTMLElement;
|
||||
navItem: HTMLElement;
|
||||
|
||||
terminal: Terminal;
|
||||
fitAddon: FitAddon;
|
||||
serializeAddon: SerializeAddon;
|
||||
resizeFunction: () => void;
|
||||
initialized: boolean = false;
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
this.navItem = makeNavView(name, this.icon, this.title);
|
||||
this.view = document.createElement("div");
|
||||
this.view.classList.add("terminal");
|
||||
this.terminal = new Terminal({ theme: { background: "#000000" } });
|
||||
this.fitAddon = new FitAddon();
|
||||
this.serializeAddon = new SerializeAddon();
|
||||
this.resizeFunction = this.resizeTerminal.bind(this);
|
||||
}
|
||||
|
||||
updateIndex(index: number): void {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
onInit(): void {
|
||||
console.log("Initialized RTT Widget");
|
||||
}
|
||||
onFocus(element: HTMLElement): void {
|
||||
console.log("Displaying RTT Widget");
|
||||
if (!this.initialized) {
|
||||
// Ensure the parent frame doesn't get any scrollbars, since we're taking up the whole view
|
||||
element.style.overflow = "hidden";
|
||||
console.log("Initializing xterm.js");
|
||||
// var terminalContainer = document.createElement("div");
|
||||
// this.view.appendChild(terminalContainer);
|
||||
this.terminal.loadAddon(this.fitAddon);
|
||||
this.terminal.loadAddon(this.serializeAddon);
|
||||
this.terminal.onKey((e) => {
|
||||
console.log("Key pressed: " + e.key);
|
||||
this.terminal.write(e.key);
|
||||
if (e.key === '\r') {
|
||||
this.terminal.write('\n');
|
||||
}
|
||||
});
|
||||
this.terminal.open(this.view);
|
||||
this.terminal.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n');
|
||||
this.initialized = true;
|
||||
}
|
||||
element.appendChild(this.view);
|
||||
window.addEventListener('resize', this.resizeFunction);
|
||||
window.setTimeout(() => {
|
||||
this.terminal.focus();
|
||||
this.resizeFunction();
|
||||
}, 10);
|
||||
}
|
||||
onBlur(element: HTMLElement): void {
|
||||
console.log("Archiving UART Widget");
|
||||
element.removeChild(this.view);
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
|
||||
// Whenever the window is resized, update the size of the terminal
|
||||
resizeTerminal() {
|
||||
this.fitAddon.fit();
|
||||
}
|
||||
}
|
57
src/interface/settings.ts
Normal file
57
src/interface/settings.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { FarpatchWidget, makeNavView as makeNavItem } from "../interfaces";
|
||||
|
||||
export class SettingsWidget implements FarpatchWidget {
|
||||
index: number = 0;
|
||||
view: HTMLElement = document.createElement("div");
|
||||
navItem: HTMLElement;
|
||||
name: string;
|
||||
icon: string = "sliders-h";
|
||||
title: string = "Settings";
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
this.navItem = makeNavItem(name, this.icon, this.title);
|
||||
}
|
||||
|
||||
updateIndex(index: number): void {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
onInit(): void {
|
||||
this.view.innerHTML = `
|
||||
<div class="fieldset-item">
|
||||
<picture aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<title>A note icon</title>
|
||||
<path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
|
||||
</svg>
|
||||
</picture>
|
||||
<div class="input-stack">
|
||||
<label
|
||||
for="media-volume"
|
||||
id="media-volume"
|
||||
aria-hidden="true">
|
||||
Media volume
|
||||
</label>
|
||||
<input
|
||||
name="media-volume"
|
||||
aria-labelledby="media-volume"
|
||||
type="range"
|
||||
value="3"
|
||||
max="10"
|
||||
style="--track-fill: 30%"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
console.log("Initialized Dashboard Widget");
|
||||
}
|
||||
onFocus(element: HTMLElement): void {
|
||||
console.log("Displaying Dashboard Widget");
|
||||
element.appendChild(this.view);
|
||||
}
|
||||
onBlur(element: HTMLElement): void {
|
||||
console.log("Archiving Dashboard Widget");
|
||||
element.removeChild(this.view);
|
||||
}
|
||||
}
|
@ -1,16 +1,25 @@
|
||||
import { FarpatchWidget } from "../interfaces";
|
||||
import { FarpatchWidget, makeNavView } from "../interfaces";
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { SerializeAddon } from '@xterm/addon-serialize';
|
||||
|
||||
export class UartWidget implements FarpatchWidget {
|
||||
name: string;
|
||||
icon: string = "keyboard";
|
||||
title: string = "UART";
|
||||
index: number = 0;
|
||||
|
||||
view: HTMLElement;
|
||||
navItem: HTMLElement;
|
||||
|
||||
terminal: Terminal;
|
||||
fitAddon: FitAddon;
|
||||
serializeAddon: SerializeAddon;
|
||||
resizeFunction: () => void;
|
||||
initialized: boolean = false;
|
||||
constructor() {
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
this.navItem = makeNavView(name, this.icon, this.title);
|
||||
this.view = document.createElement("div");
|
||||
this.view.classList.add("terminal");
|
||||
this.terminal = new Terminal({ theme: { background: "#000000" } });
|
||||
@ -18,6 +27,11 @@ export class UartWidget implements FarpatchWidget {
|
||||
this.serializeAddon = new SerializeAddon();
|
||||
this.resizeFunction = this.resizeTerminal.bind(this);
|
||||
}
|
||||
|
||||
updateIndex(index: number): void {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
onInit(): void {
|
||||
console.log("Initialized UART Widget");
|
||||
}
|
||||
|
@ -1,5 +1,57 @@
|
||||
|
||||
import { UartWidget } from "./interface/uart";
|
||||
import { DashboardWidget } from "./interface/dashboard";
|
||||
import { RttWidget } from "./interface/rtt";
|
||||
import { DebugWidget } from "./interface/debug";
|
||||
import { SettingsWidget } from "./interface/settings";
|
||||
|
||||
export interface FarpatchWidget {
|
||||
index: number,
|
||||
name: string,
|
||||
view: HTMLElement,
|
||||
navItem: HTMLElement,
|
||||
icon: string,
|
||||
title: string,
|
||||
updateIndex(index: number): void,
|
||||
onInit(): void,
|
||||
onFocus(element: HTMLElement): void,
|
||||
onBlur(element: HTMLElement): void,
|
||||
}
|
||||
|
||||
export function makeNavView(name: string, icon: string, title: string): HTMLElement {
|
||||
var navView: HTMLElement = document.createElement("li");
|
||||
navView.classList.add("sidenav-item");
|
||||
navView.id = name + "-button";
|
||||
|
||||
var navViewLink = document.createElement("a");
|
||||
navViewLink.classList.add("sidenav-link");
|
||||
|
||||
var navViewIcon = document.createElement("span");
|
||||
navViewIcon.classList.add("las");
|
||||
navViewIcon.classList.add("la-3x");
|
||||
navViewIcon.classList.add("la-" + icon);
|
||||
navViewIcon.classList.add("icon");
|
||||
|
||||
var navViewText = document.createElement("span");
|
||||
navViewText.classList.add("link-text");
|
||||
navViewText.innerText = title;
|
||||
|
||||
navViewLink.appendChild(navViewIcon);
|
||||
navViewLink.appendChild(navViewText);
|
||||
|
||||
navView.appendChild(navViewLink);
|
||||
|
||||
return navView;
|
||||
}
|
||||
|
||||
export const farpatchWidgets: FarpatchWidget[] = [
|
||||
new DashboardWidget("dashboard"),
|
||||
new UartWidget("uart"),
|
||||
new RttWidget("rtt"),
|
||||
new DebugWidget("debug"),
|
||||
new SettingsWidget("settings"),
|
||||
];
|
||||
|
||||
for (var i = 0; i < farpatchWidgets.length; i++) {
|
||||
farpatchWidgets[i].updateIndex(i);
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="sidenav">
|
||||
<!-- <nav class="sidenav">
|
||||
<ul class="sidenav-nav">
|
||||
<li class="sidenav-item" id="dashboard-button">
|
||||
<a class="sidenav-link">
|
||||
@ -75,7 +75,7 @@
|
||||
<div class="main-content" id="settings-view">
|
||||
<div>[Settings goes here]</div>
|
||||
</div>
|
||||
</main>
|
||||
</main> -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user