This commit is contained in:
ticvac 2025-05-07 23:41:39 +02:00
parent c6d280dfd8
commit 577557146d
2 changed files with 97 additions and 18 deletions

View file

@ -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;

View file

@ -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