viewstrap/assets/index.html
2023-03-05 20:38:30 +01:00

384 lines
9.4 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Viewstrap</title>
<style>
html {
font-family: sans-serif;
}
h1 {
margin: 0;
}
.center-inner {
display: flex;
align-items: center;
justify-content: center;
}
.item {
height: 100px;
width: 100px;
border: 1px black solid;
}
.item-source {
background: rgb(188, 188, 188);
}
.source-box-text {
font-size: 18pt;
text-align: center;
}
#inventory-wrapper {
display: flex;
height: 100px;
gap: 5px;
}
.item-compiler {
background: rgb(185, 210, 181);
}
.region {
border: 2px solid grey;
padding: 5px;
}
.crafting-wrapper {
display: flex;
}
.crafting-slot {
background: grey;
}
.crafting-slot-dragover {
background: green;
}
.crafting-icon {
height: 100px;
width: 70px;
font-size: 20pt;
}
.crafting-progress-indicator {
height: 100px;
width: 100px;
}
.crafting-progress-arrow {
cursor: pointer;
}
.crafting-progress-arrow-running {
background: linear-gradient(
0deg,
#ff2400,
#e81d1d,
#e8b71d,
#e3e81d,
#1de840,
#1ddde8,
#2b1de8,
#dd00f3,
#dd00f3
);
background-size: 1800% 1800%;
animation: rainbow 10s ease infinite;
}
@keyframes rainbow {
0% {
background-position: 0% 82%;
}
50% {
background-position: 100% 19%;
}
100% {
background-position: 0% 82%;
}
}
#error {
color: red;
margin-left: 10px;
font-size: 18px;
}
</style>
</head>
<body>
<div class="region">
<h1>Inventory</h1>
<div id="inventory-wrapper"></div>
</div>
<div class="region">
<h1>Crafting</h1>
<div class="crafting-wrapper">
<div id="crafting-slot-1" class="crafting-slot item"></div>
<div class="crafting-icon center-inner">
<span class>&#x2795;</span>
</div>
<div id="crafting-slot-2" class="crafting-slot item"></div>
<div
id="crafting-progress-arrow"
class="crafting-progress-arrow"
role="button"
onclick="compile()"
>
<svg class="crafting-progress-indicator" viewBox="0 0 100 100">
<!--
Arrow.
line length 50, line height 20
triangle full height 20+2*20
triangle width 30
-->
<path
fill="currentColor"
d="
M 10,40
l 50,0
l 0,-20
L 90,50
L 60,80
l 0,-20
l -50,0
"
></path>
</svg>
</div>
<div class="crafting-slot item center-inner"></div>
<div id="error"></div>
</div>
</div>
<div>
<button class="compile-action" disabled onclick="updateCompilers()">
update compilers
</button>
</div>
<div>
<textarea id="stdout" rows="30" cols="80"></textarea>
<textarea id="stderr" rows="30" cols="80"></textarea>
</div>
<script>
class Item {
constructor(name, type) {
this.name = name;
this.type = type;
}
}
const errorElem = document.getElementById("error");
function setError(msg) {
error.innerText = msg;
}
function resetError() {
error.innerText = "";
}
function renderItem(item) {
const inner = document.createElement("div");
inner.innerText = item.name;
inner.classList.add("source-box-text");
const elem = document.createElement("div");
elem.classList.add(`item-${item.type}`);
elem.classList.add("center-inner");
elem.classList.add("item");
elem.item = item;
elem.appendChild(inner);
elem.draggable = true;
elem.addEventListener("dragstart", (event) => {
const dt = event.dataTransfer;
dt.setData("application/json", JSON.stringify(item));
dt.dropEffect = "move";
});
return elem;
}
function setupDragDropSlot(elem) {
elem.addEventListener("dragover", (event) => {
event.preventDefault();
elem.classList.add("crafting-slot-dragover");
});
elem.addEventListener("dragleave", (event) => {
event.preventDefault();
elem.classList.remove("crafting-slot-dragover");
});
elem.addEventListener("drop", (event) => {
event.preventDefault();
resetError();
const data = JSON.parse(
event.dataTransfer.getData("application/json")
);
const item = new Item(data.name, data.type);
const itemElem = renderItem(item);
elem.innerHTML = "";
elem.appendChild(itemElem);
elem.classList.remove("crafting-slot-dragover");
elem.item = item;
});
}
class Inventory {
constructor(initialItems) {
this.dom = document.getElementById("inventory-wrapper");
for (const item of initialItems) {
const elem = renderItem(item);
this.dom.appendChild(elem);
}
this.craftingSlot1 = document.getElementById("crafting-slot-1");
this.craftingSlot2 = document.getElementById("crafting-slot-2");
setupDragDropSlot(this.craftingSlot1);
setupDragDropSlot(this.craftingSlot2);
}
items() {
const children = this.dom.children;
return Array.from(children).map((child) => child.item);
}
appendIfNotExists(item) {
const items = this.items();
if (
!items.find(
(inner) => inner.name == item.name && inner.type == item.type
)
) {
const elem = renderItem(item);
this.dom.appendChild(elem);
}
}
}
const stdout = document.getElementById("stdout");
const stderr = document.getElementById("stderr");
const compilersWrapper = document.getElementById("inventory-wrapper");
const compileActions = document.querySelectorAll(".compile-action");
const craftingProgressArrow = document.getElementById(
"crafting-progress-arrow"
);
const inventory = new Inventory([
new Item("compiler", "source"),
new Item("library", "source"),
]);
const ws = new WebSocket("ws://localhost:3000/ws");
ws.addEventListener("open", () => {
for (elem of compileActions) {
elem.disabled = false;
}
ws.send(JSON.stringify("ListCompilers"));
});
ws.addEventListener("message", (event) => {
console.log("Received event", event.data);
const msg = JSON.parse(event.data);
if ("Stdout" in msg) {
stdout.value += msg.Stdout;
}
if ("Stderr" in msg) {
stdout.value += msg.Stderr;
}
if ("AvailableCompilers" in msg) {
for (const compiler of msg.AvailableCompilers) {
inventory.appendIfNotExists(
new Item(`rustc ${compiler}`, "compiler")
);
}
}
if ("Done" in msg) {
craftingProgressArrow.classList.remove(
"crafting-progress-arrow-running"
);
const error = msg.Done ? "" : "an error occurred";
setError(error);
}
});
function compile() {
const arrowClasses = craftingProgressArrow.classList;
if (arrowClasses.contains("crafting-progress-arrow-running")) {
return;
}
console.log("compiling");
resetError();
const item1 = inventory.craftingSlot1.item;
if (!item1) {
setError("left side must be filled");
return;
}
const item2 = inventory.craftingSlot2.item;
if (!item2) {
setError("right side must be filled");
return;
}
console.log("Items", item1, item2);
const action = [item1, item2].find((item) => item.type == "source");
if (!action) {
setError("must include a source in the crafting receipe");
return;
}
const compiler = [item1, item2].find((item) => item.type == "compiler");
if (!compiler) {
setError("must include a compiler in the crafting receipe");
return;
}
arrowClasses.add("crafting-progress-arrow-running");
const compilerName = compiler.name.split(" ")[1];
const actionName = {
library: "BuildLibrary",
compiler: "BuildCompiler",
}[action.name];
ws.send(
JSON.stringify({
Compile: { compiler: compilerName, action: actionName },
})
);
stdout.value = "";
stderr.value = "";
}
function updateCompilers() {
console.log("list compilers");
ws.send(JSON.stringify("ListCompilers"));
}
</script>
</body>
</html>