ready?
This commit is contained in:
parent
c6d280dfd8
commit
577557146d
2 changed files with 97 additions and 18 deletions
|
@ -30,15 +30,13 @@
|
|||
display: inline-block; padding: 0.75rem 1.5rem; background: var(--primary); color: #fff; text-decoration: none;
|
||||
border: none; border-radius: 0.375rem; font-weight: 600; cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.btn:hover:not([disabled]) { background: var(--primary-light); }
|
||||
.btn-stop {
|
||||
background: #ef4444; color: #fff; font-weight: 600; cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
margin-top: 1rem; padding: 0.75rem 1.5rem; border: none; border-radius: 0.375rem;
|
||||
padding: 0.75rem 1.5rem; border: none; border-radius: 0.375rem;
|
||||
display: inline-block; text-decoration: none;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
.btn-stop:hover:not([disabled]) { background: #dc2626; }
|
||||
.btn[disabled] { opacity: 0.6; cursor: not-allowed; }
|
||||
|
@ -46,13 +44,32 @@
|
|||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid #fff;
|
||||
border-radius: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 0.5rem;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.spinner::before,
|
||||
.spinner::after {
|
||||
content: "";
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.spinner::before {
|
||||
border: 4px solid rgba(0,0,0,0.1);
|
||||
}
|
||||
.spinner::after {
|
||||
border: 4px solid transparent;
|
||||
border-top-color: #3498db;
|
||||
border-right-color: #8e44ad;
|
||||
box-shadow: 0 0 8px rgba(52, 152, 219, 0.6),
|
||||
0 0 8px rgba(142, 68, 173, 0.6) inset;
|
||||
animation: spin 1s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
|
||||
}
|
||||
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
||||
.flex { display: flex; gap: 1rem; }
|
||||
.flex > div { flex: 1; }
|
||||
|
@ -79,12 +96,46 @@
|
|||
font-size: 0.8rem;
|
||||
color: var(--text);
|
||||
margin-top: 1rem;
|
||||
line-height: 2em;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
.styled-select {
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--card);
|
||||
color: var(--text);
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg fill='%23374151' height='20' viewBox='0 0 24 24' width='20' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/></svg>");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0.75rem center;
|
||||
background-size: 1rem;
|
||||
padding-right: 2rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.styled-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2);
|
||||
}
|
||||
.align-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
.flex { flex-direction: column; }
|
||||
.flex > div { width: 100%; }
|
||||
.align-center {
|
||||
align-items: normal;
|
||||
}
|
||||
.styled-select {
|
||||
width: 100%;
|
||||
max-width: 500px !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -109,8 +160,15 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" id="run-btn" class="btn">Run</button>
|
||||
<button type="button" id="stop-btn" class="btn-stop" style="display: none;">Stop</button>
|
||||
<div class="flex align-center" style="margin-top: 1rem; flex-wrap: wrap; gap: 1rem;">
|
||||
<select id="speed" class="styled-select">
|
||||
<option value="slllooow">pretty, pretty slow</option>
|
||||
<option value="med">medium</option>
|
||||
<option value="fast" selected>blazingly fast</option>
|
||||
</select>
|
||||
<button type="button" id="run-btn" class="btn">Run</button>
|
||||
<button type="button" id="stop-btn" class="btn-stop" style="display: none;">Stop</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card">
|
||||
|
@ -129,6 +187,7 @@
|
|||
let abortSignal = false;
|
||||
const outElement = document.getElementById('output');
|
||||
const lastStateElement = document.getElementById('last_state');
|
||||
const speedSelect = document.getElementById('speed');
|
||||
|
||||
class Interpreter {
|
||||
constructor({ commandsPerSecond = 1_000_000_000, debug = false} = {}) {
|
||||
|
@ -146,6 +205,7 @@
|
|||
this.userInput = "";
|
||||
this.userInputIndex = 0;
|
||||
this.savedOutput = "";
|
||||
this.steps = 0;
|
||||
}
|
||||
|
||||
ourI32(value) {
|
||||
|
@ -277,11 +337,32 @@
|
|||
}
|
||||
|
||||
async execute({ codeString, userInput = '', callback } = {}) {
|
||||
let waitTime = 0;
|
||||
let breath = 0;
|
||||
const speed = speedSelect.value;
|
||||
if (speed === 'slllooow') {
|
||||
this.commandsPerSecond = 1;
|
||||
waitTime = 1 / this.commandsPerSecond;
|
||||
breath = 1;
|
||||
} else if (speed === 'med') {
|
||||
this.commandsPerSecond = 10;
|
||||
waitTime = 1 / this.commandsPerSecond;
|
||||
breath = 1
|
||||
} else if (speed === 'fast') {
|
||||
this.commandsPerSecond = 1_000_000_000_000;
|
||||
waitTime = 0;
|
||||
breath = 100000;
|
||||
}
|
||||
|
||||
this.loadCodeFromString(codeString);
|
||||
this.userInput = userInput;
|
||||
let steps = 0;
|
||||
let breath = 100;
|
||||
while (this.pc < this.code.length) {
|
||||
if (abortSignal) {
|
||||
console.log("Execution aborted");
|
||||
this.savedOutput = "Execution aborted";
|
||||
break;
|
||||
}
|
||||
const com = this.code[this.pc];
|
||||
switch (com) {
|
||||
case '+': this.handlePlus(); break;
|
||||
|
@ -295,16 +376,15 @@
|
|||
}
|
||||
this.pc++;
|
||||
steps++;
|
||||
if (steps % breath === 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
}
|
||||
if (abortSignal) {
|
||||
console.log("Execution aborted");
|
||||
this.savedOutput = "Execution aborted";
|
||||
break;
|
||||
}
|
||||
this.steps = steps;
|
||||
outElement.textContent = this.savedOutput;
|
||||
lastStateElement.textContent = this.getState();
|
||||
// auto-scroll to the bottom
|
||||
outElement.scrollTop = outElement.scrollHeight;
|
||||
lastStateElement.scrollTop = lastStateElement.scrollHeight;
|
||||
if (steps % breath === 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
|
||||
}
|
||||
}
|
||||
callback(this.savedOutput, this.getState());
|
||||
return this.savedOutput;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from django.template.response import TemplateResponse
|
||||
from django.http import JsonResponse as JSONResponse
|
||||
from .interpreter import Interpreter
|
||||
from urllib.parse import parse_qsl, unquote_plus, unquote
|
||||
from typing import Dict
|
||||
|
||||
|
|
Loading…
Reference in a new issue