diff options
author | Jakob Stendahl <jakob.stendahl@outlook.com> | 2021-09-19 19:43:11 +0200 |
---|---|---|
committer | Jakob Stendahl <jakob.stendahl@outlook.com> | 2021-09-19 19:43:11 +0200 |
commit | 7bdce37fd3f18e2712e18c4e2c64cac69af0aca1 (patch) | |
tree | b7ad3f1cca92e2dfd2664ae9e65652bd03ff58b2 /src_frontend/Components/Editor | |
parent | e6880cd8ccf82d993f222cb14b4860581654acb8 (diff) | |
download | Luxcena-Neo-7bdce37fd3f18e2712e18c4e2c64cac69af0aca1.tar.gz Luxcena-Neo-7bdce37fd3f18e2712e18c4e2c64cac69af0aca1.zip |
:boom: Introduce new UI based on svelte, and rewrite a lot of the node app and the NeoRuntime
Diffstat (limited to 'src_frontend/Components/Editor')
-rw-r--r-- | src_frontend/Components/Editor/Controls.svelte | 83 | ||||
-rw-r--r-- | src_frontend/Components/Editor/Editor.svelte | 296 | ||||
-rw-r--r-- | src_frontend/Components/Editor/Output.svelte | 54 | ||||
-rw-r--r-- | src_frontend/Components/Editor/Pane.svelte | 41 | ||||
-rw-r--r-- | src_frontend/Components/Editor/TopBar.svelte | 62 |
5 files changed, 536 insertions, 0 deletions
diff --git a/src_frontend/Components/Editor/Controls.svelte b/src_frontend/Components/Editor/Controls.svelte new file mode 100644 index 0000000..302aa7a --- /dev/null +++ b/src_frontend/Components/Editor/Controls.svelte @@ -0,0 +1,83 @@ +<script> + import { onMount } from "svelte"; + import PrettyVar from "../../ComponentLib/PrettyVar.svelte"; + import { openSocket } from "../../stores/socketStore"; + + let brightnessValue = 0; + let power_on = false; + let variables = {}; + + function setBrightness() { + if (!power_on) { openSocket.emit("power:set", true); } + openSocket.emit("brightness:set", brightnessValue); + } + function setPower() { openSocket.emit("power:set", power_on); } + function setVar(ev) { + openSocket.emit("var:set", ev.target.id, ev.target.value); + } + + openSocket.on("power", (power) => power_on = power); + openSocket.on("brightness", (value) => brightnessValue = value); + openSocket.on("vars", (vars) => variables = vars); + openSocket.on("var", (name, value) => { + name = name.replace("variable/", ""); + if (value.value == null) { + delete variables[name]; + } else { + variables[name] = value; + } + variables = variables; + }); + + onMount(() => { + openSocket.emit("power:get"); + openSocket.emit("brightness:get"); + openSocket.emit("vars:get") + }); +</script> + +<style> + label { + width: 100%; + font-size: 12px; + color: var(--grey-500); + } + .var-group { + display: flex; + } + input[type=range] { + width: 100%; + } + input[type=text] { + margin-top: 5px; + display: block; + width: 100%; + background-color: #737373; + padding: 5px; + color: white; + border: none; + box-sizing: border-box; + border-radius: 5px; + } +</style> + +<div> + <div class="var-group"> + <label for="power">Power</label> + <input type="checkbox" id="power" bind:checked={power_on} on:change={setPower} /> + </div> + <div> + <label for="brightness">Brightness</label> + <div class="var-group"> + <input id="brightness" type="range" min=0 max=255 bind:value={brightnessValue} on:change={setBrightness} /> + {brightnessValue} + </div> + </div> + + {#each Object.entries(variables) as [name, value]} + <div> + <label for="{name}"><PrettyVar varText={name} /></label> + <input type="text" id="{name}" bind:value={value.value} on:blur={setVar} /> + </div> + {/each} +</div>
\ No newline at end of file diff --git a/src_frontend/Components/Editor/Editor.svelte b/src_frontend/Components/Editor/Editor.svelte new file mode 100644 index 0000000..b63ee9b --- /dev/null +++ b/src_frontend/Components/Editor/Editor.svelte @@ -0,0 +1,296 @@ +<script context="module"> + let debuggerInitialised = false; +</script> +<script> + import { onDestroy } from "svelte"; + import { pop } from "svelte-spa-router"; + import { EditorState, EditorView, basicSetup } from "@codemirror/basic-setup" + import { python } from "@codemirror/lang-python" + import { HighlightStyle, tags as t } from "@codemirror/highlight" + import { notif } from "../../stores/notifs"; + import TopBar from "./TopBar.svelte"; + import Pane from "./Pane.svelte"; + import ControlComponents from "../MainControls/ControlComponents.svelte"; + import Controls from "./Controls.svelte"; + import Output from "./Output.svelte"; + + import { authorizedSocket, authorizedSocketNeeded } from "../../stores/socketStore"; + authorizedSocketNeeded.set(true); + + export let modeId; + + let codeEditorView; + let codeEditorEl; + let codeEditorHasChanges = false; + let procIsRunning = false; + + function initDebugger() { + if (debuggerInitialised) { return; } + debuggerInitialised = true; + authorizedSocket.emit("editor:open", `user/${modeId}`, (res) => { + if (!res.success) { notif({title: res.reason, type: "danger"}); return; } + }); + } + + authorizedSocket.on("editor:code", (modeId, code) => { + const chalky = "#e5c07b", + coral = "#e06c75", + cyan = "#56b6c2", + invalid = "#ffffff", + ivory = "#abb2bf", + stone = "#7d8799", + malibu = "#61afef", + sage = "#98c379", + whiskey = "#d19a66", + violet = "#c678dd", + darkBackground = "#21252b", + highlightBackground = "#2c313a", + background = "#282c34", + selection = "#3E4451", + cursor = "#528bff" + + codeEditorView = new EditorView({ + state: EditorState.create({ + extensions: [ + basicSetup, + python(), + EditorView.updateListener.of(update => { + if (update.docChanged) { + codeEditorHasChanges = true; + } + }), + EditorView.theme({ + "&": { + color: ivory, + }, + + ".cm-content": { + caretColor: cursor + }, + + "&.cm-focused .cm-cursor": {borderLeftColor: cursor}, + "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {backgroundColor: selection}, + + ".cm-panels": {backgroundColor: darkBackground, color: ivory}, + ".cm-panels.cm-panels-top": {borderBottom: "2px solid black"}, + ".cm-panels.cm-panels-bottom": {borderTop: "2px solid black"}, + + ".cm-searchMatch": { + backgroundColor: "#72a1ff59", + outline: "1px solid #457dff" + }, + ".cm-searchMatch.cm-searchMatch-selected": { + backgroundColor: "#6199ff2f" + }, + + ".cm-activeLine": {backgroundColor: highlightBackground}, + ".cm-selectionMatch": {backgroundColor: "#aafe661a"}, + + ".cm-matchingBracket, .cm-nonmatchingBracket": { + backgroundColor: "#bad0f847", + outline: "1px solid #515a6b" + }, + + ".cm-gutters": { + backgroundColor: "transparent", + color: stone, + border: "none" + }, + + ".cm-activeLineGutter": { + backgroundColor: highlightBackground + }, + + ".cm-foldPlaceholder": { + backgroundColor: "transparent", + border: "none", + color: "#ddd" + }, + + ".cm-tooltip": { + border: "1px solid #181a1f", + backgroundColor: darkBackground + }, + ".cm-tooltip-autocomplete": { + "& > ul > li[aria-selected]": { + backgroundColor: highlightBackground, + color: ivory + } + } + }, {dark:true}), + HighlightStyle.define([ + {tag: t.keyword, + color: violet}, + {tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], + color: coral}, + {tag: [t.function(t.variableName), t.labelName], + color: malibu}, + {tag: [t.color, t.constant(t.name), t.standard(t.name)], + color: whiskey}, + {tag: [t.definition(t.name), t.separator], + color: ivory}, + {tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], + color: chalky}, + {tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)], + color: cyan}, + {tag: [t.meta, t.comment], + color: stone}, + {tag: t.strong, + fontWeight: "bold"}, + {tag: t.emphasis, + fontStyle: "italic"}, + {tag: t.strikethrough, + textDecoration: "line-through"}, + {tag: t.link, + color: stone, + textDecoration: "underline"}, + {tag: t.heading, + fontWeight: "bold", + color: coral}, + {tag: [t.atom, t.bool, t.special(t.variableName)], + color: whiskey }, + {tag: [t.processingInstruction, t.string, t.inserted], + color: sage}, + {tag: t.invalid, + color: invalid}, + ]), + ], + doc: code + }), + parent: codeEditorEl + }) + }); + authorizedSocket.on("editor:proc:start", () => procIsRunning = true); + authorizedSocket.on("editor:proc:exit", (code) => { + procIsRunning = false; + }); + + function startProc() { + saveCode(() => { + authorizedSocket.emit("editor:startmode", (res) => { + if (!res.success) { notif({title: res.reason, type: "danger"}); } + }); + }); + } + + function stopProc() { + authorizedSocket.emit("editor:stopmode", (res) => { + if (!res.success) { notif({title: res.reason, type: "danger"}); } + }); + } + + function restartProc () { + saveCode((res) => { + if (!res.success) { notif({title: res.reason, type: "danger"}); } + authorizedSocket.emit("editor:restartmode", (res) => { + if (!res.success) { notif({title: res.reason, type: "danger"}); } + }); + }); + } + + function saveCode(fn) { + if (codeEditorView == null) { return; } + authorizedSocket.emit("editor:save", `user/${modeId}`, codeEditorView.state.doc.toString(), res => { + if (!res.success) { notif({title: res.reason, type: "danger"}); } + if (fn != null) { fn(res) } + }); + codeEditorHasChanges = false; + } + + function closeDebugger() { + saveCode((res) => { + if (!res.success) { notif({title: res.reason, type: "danger"}); } + authorizedSocket.emit("editor:close", res => { + if (!res.success) { notif({title: res.reason, type: "danger"}); } + debuggerInitialised = false; + }); + }); + } + + onDestroy(() => { + closeDebugger(); + }) + + document.addEventListener("keydown", function(e) { + if ((window.navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && e.keyCode == 83) { + e.preventDefault(); + saveCode(); + } + }, false); + + setInterval(() => { + if (codeEditorHasChanges) { + saveCode(); + } + }, 5000); +</script> + +<style> + main { + display: grid; + box-sizing: border-box; + padding: 15px; + column-gap: 15px; + row-gap: 15px; + grid-template-columns: 300px 1fr; + grid-template-rows: 50% 1fr 33%; + grid-template-areas: + "simulation editor" + "controls editor" + "controls output"; + width: 100%; + height: calc(100% - 35px); + background-color: #333333; + color: white; + } + .simulation { grid-area: simulation; } + .controls { grid-area: controls; } + .editor { grid-area: editor; } + .output { grid-area: output; } + + .editor { + overflow: auto; + } + + @media (max-width: 800px) { + main { + grid-template-columns: auto; + grid-template-areas: + "editor" + "editor" + "output"; + } + .controls, .simulation { + display: none; + } + } +</style> + +<TopBar modeId={modeId} + hasChange={codeEditorHasChanges} + on:closedebugger={pop} + on:start={startProc} + on:stop={stopProc} + on:restart={restartProc} + bind:procIsRunning={procIsRunning} /> +<main use:initDebugger> + <div class="simulation"> + <Pane header="simulation"> + </Pane> + </div> + + <div class="controls"> + <Pane header="Controls"> + <!-- <ControlComponents /> --> + <Controls /> + </Pane> + </div> + + <div class="editor" bind:this={codeEditorEl}></div> + + <div class="output"> + <Pane header="output" padding={false} scrollable={false}> + <Output /> + </Pane> + </div> +</main> diff --git a/src_frontend/Components/Editor/Output.svelte b/src_frontend/Components/Editor/Output.svelte new file mode 100644 index 0000000..dcd5995 --- /dev/null +++ b/src_frontend/Components/Editor/Output.svelte @@ -0,0 +1,54 @@ +<script> + import { authorizedSocket, authorizedSocketNeeded } from "../../stores/socketStore"; + authorizedSocketNeeded.set(true); + + let scrollBox; + let htmlCode = ""; + + function addData(data, classname) { + // let styles = "white-space:pre-wrap;margin:0;"; + // let styles = "overflow-x:auto;"; + let styles = ""; + switch (classname) { + case "exit": + styles += "color: green"; + break; + case"stderr": + styles += "color: red"; + break; + } + htmlCode += `<span style="${styles}">${data}</span>`; + if (scrollBox != null) { + scrollBox.scrollTop = scrollBox.scrollHeight + 100; + } + } + authorizedSocket.on("editor:proc:exit", (code) => addData(`\nMode exited with ${code}\n\n`, "exit")); + authorizedSocket.on("editor:proc:stdout", (stdout) => addData(stdout, "stdout")); + authorizedSocket.on("editor:proc:stderr", (stderr) => addData(stderr, "stderr")); +</script> + +<style> + div { + height: 100%; + width: 100%; + } + pre { + height: 100%; + width: calc(100vw - 30px); + overflow: auto; + padding: 15px; + margin: 0; + box-sizing: border-box; + } + @media (min-width: 800px) { + pre { + width: calc(100vw - 360px); + } + } +</style> + +<div> + <pre bind:this={scrollBox}> + {@html htmlCode} + </pre> +</div>
\ No newline at end of file diff --git a/src_frontend/Components/Editor/Pane.svelte b/src_frontend/Components/Editor/Pane.svelte new file mode 100644 index 0000000..143c569 --- /dev/null +++ b/src_frontend/Components/Editor/Pane.svelte @@ -0,0 +1,41 @@ +<script> + export let header; + export let padding = true; + export let scrollable = true; +</script> + +<style> + .box { + background-color: #444242; + border-radius: 5px; + height: 100%; + } + .header { + width: 100%; + padding: 10px; + box-sizing: border-box; + border-bottom: 1px solid #333333; + } + .header h1 { + margin: 0; + font-size: 12px; + font-weight: bold; + text-transform: uppercase; + } + .content { + height: calc(100% - 35px); + width: 100%; + box-sizing: border-box; + } + .padding { padding: 15px; } + .scrollable { overflow: auto; } +</style> + +<div class="box"> + <div class="header"> + <h1>{header}</h1> + </div> + <div class="content" class:padding={padding} class:scrollable={scrollable}> + <slot></slot> + </div> +</div>
\ No newline at end of file diff --git a/src_frontend/Components/Editor/TopBar.svelte b/src_frontend/Components/Editor/TopBar.svelte new file mode 100644 index 0000000..c74adf0 --- /dev/null +++ b/src_frontend/Components/Editor/TopBar.svelte @@ -0,0 +1,62 @@ +<script> + import { createEventDispatcher } from 'svelte'; + import { pop } from "svelte-spa-router"; + import PrettyVar from "../../ComponentLib/PrettyVar.svelte"; + + const dispatch = createEventDispatcher(); + + export let modeId; + export let hasChange = false; + export let procIsRunning = false; +</script> + +<style> + .topbar { + display: flex; + background-color: #444242; + height: 35px; + box-sizing: border-box; + padding: 10px; + font-size: 12px; + color: white; + } + .topbar .title { margin: auto; } + .savestatus { + font-size: 10px; + color: var(--grey-400); + } + button { + background: #444242; + border: none; + color: white; + } + button i { + margin-right: 5px; + } + button:hover { + filter: brightness(0.95); + } + button:active { + filter: brightness(0.90); + } +</style> + +<div class="topbar"> + <div><button on:click={() => dispatch("closedebugger")}><i class="fas fa-chevron-left"></i></button></div> + <div class="title"> + <span class="filename"><PrettyVar varText={modeId} /></span> + <span class="savestatus"> + {#if hasChange} + (not saved) + {/if} + </span> + </div> + <div> + {#if procIsRunning} + <button on:click={() => dispatch("restart")}><i class="fas fa-sync-alt"></i>Restart</button> + <button on:click={() => dispatch("stop")}><i class="fas fa-stop"></i>Stop</button> + {:else} + <button on:click={() => dispatch("start")}><i class="fas fa-play"></i>Start</button> + {/if} + </div> +</div>
\ No newline at end of file |