crap
This commit is contained in:
@@ -1,39 +1,61 @@
|
||||
p {
|
||||
line-height: 0.3;
|
||||
/* Remove the p styling since we're using div now */
|
||||
div {
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: monospace;
|
||||
background-color: #48156e;
|
||||
color: #e6e8ff;
|
||||
border: 0px;
|
||||
border-radius: 2px;
|
||||
button, .button {
|
||||
font-family: monospace;
|
||||
background-color: #48156e;
|
||||
color: #e6e8ff;
|
||||
border: 0px;
|
||||
border-radius: 2px;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover, .button:hover {
|
||||
background-color: #5a1a82;
|
||||
}
|
||||
|
||||
.left {
|
||||
height: 100%;
|
||||
width: 70%;
|
||||
padding: 10px;
|
||||
background-color: #0d0a12;
|
||||
color: #e6e8ff;
|
||||
position: absolute;
|
||||
overflow: scroll;
|
||||
height: 100%;
|
||||
width: 70%;
|
||||
padding: 10px;
|
||||
background-color: #0d0a12;
|
||||
color: #e6e8ff;
|
||||
position: absolute;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.right {
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
padding: 10px;
|
||||
background-color: #191324;
|
||||
color: #e6e8ff;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
padding: 10px;
|
||||
background-color: #191324;
|
||||
color: #e6e8ff;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* Remove focus outline on the editor */
|
||||
#editor {
|
||||
outline: none;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
spellcheck: false; /* Disable spellcheck squiggly lines */
|
||||
}
|
||||
|
||||
#console {
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="right">
|
||||
<button onclick="runCode()">Run</button>
|
||||
<input type="file" id="fileInput" hidden accept=".grnd">
|
||||
<button for="fileInput" id="buttonTrigger">Choose File</button>
|
||||
<label for="fileInput" id="buttonTrigger" class="button">Choose File</label>
|
||||
<br>
|
||||
<code id="console">Output will be shown here, click the run button to run your code!</code>
|
||||
</div>
|
||||
|
||||
409
client/index.js
409
client/index.js
@@ -1,166 +1,339 @@
|
||||
let textbox;
|
||||
let textconsole;
|
||||
let text = '# welcome to gride! start typing to code a program in Ground!\n';
|
||||
|
||||
let text = "# welcome to gride!\n# type your code here and click run!\n";
|
||||
let filebutton;
|
||||
let fileinput;
|
||||
let firstRun = true;
|
||||
|
||||
function renderHighlightedText() {
|
||||
let renderedText = "";
|
||||
const lines = text.split("\n");
|
||||
for (const line of lines) {
|
||||
iscomment = false;
|
||||
renderedText += "<p>";
|
||||
const tokens = line.split(" ");
|
||||
for (const token of tokens) {
|
||||
renderedText += highlightToken(token) + " ";
|
||||
}
|
||||
renderedText += "</p>";
|
||||
instring = false;
|
||||
}
|
||||
textbox.innerHTML = renderedText;
|
||||
// Function to save cursor position
|
||||
function saveCursorPosition() {
|
||||
const selection = window.getSelection();
|
||||
if (selection.rangeCount === 0) return null;
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
const preCaretRange = range.cloneRange();
|
||||
preCaretRange.selectNodeContents(textbox);
|
||||
preCaretRange.setEnd(range.startContainer, range.startOffset);
|
||||
|
||||
return preCaretRange.toString().length;
|
||||
}
|
||||
|
||||
// Function to restore cursor position
|
||||
function restoreCursorPosition(savedPos) {
|
||||
if (savedPos === null || !textbox) return;
|
||||
|
||||
const textNodes = [];
|
||||
const walker = document.createTreeWalker(
|
||||
textbox,
|
||||
NodeFilter.SHOW_TEXT,
|
||||
null,
|
||||
false,
|
||||
);
|
||||
|
||||
let node;
|
||||
while ((node = walker.nextNode())) {
|
||||
textNodes.push(node);
|
||||
}
|
||||
|
||||
let currentPos = 0;
|
||||
for (const textNode of textNodes) {
|
||||
const nodeLength = textNode.textContent.length;
|
||||
if (currentPos + nodeLength >= savedPos) {
|
||||
const range = document.createRange();
|
||||
const selection = window.getSelection();
|
||||
|
||||
try {
|
||||
range.setStart(textNode, Math.min(savedPos - currentPos, nodeLength));
|
||||
range.collapse(true);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} catch (e) {
|
||||
// If positioning fails, just focus the element
|
||||
textbox.focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
currentPos += nodeLength;
|
||||
}
|
||||
|
||||
// If we couldn't find the exact position, focus at the end
|
||||
const range = document.createRange();
|
||||
const selection = window.getSelection();
|
||||
try {
|
||||
range.selectNodeContents(textbox);
|
||||
range.collapse(false);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} catch (e) {
|
||||
textbox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function renderHighlightedText() {
|
||||
if (!textbox) return;
|
||||
|
||||
// Save cursor position before updating
|
||||
const cursorPos = saveCursorPosition();
|
||||
|
||||
let renderedText = "";
|
||||
const lines = text.split("\n");
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (line.trim() === "") {
|
||||
renderedText += "<div> </div>"; // Preserve empty lines
|
||||
} else {
|
||||
const commentIndex = line.indexOf("#");
|
||||
if (commentIndex !== -1) {
|
||||
const beforeComment = line.substring(0, commentIndex);
|
||||
const comment = line.substring(commentIndex);
|
||||
renderedText += `<div>${highlightTokens(beforeComment)}<span style="color: ${colours.comment}">${comment}</span></div>`;
|
||||
} else {
|
||||
renderedText += `<div>${highlightTokens(line)}</div>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textbox.innerHTML = renderedText;
|
||||
|
||||
// Restore cursor position after a brief delay to let the DOM update
|
||||
setTimeout(() => {
|
||||
restoreCursorPosition(cursorPos);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function getTextFromEditor() {
|
||||
let newText = "";
|
||||
for (let i = 0; i < textbox.childNodes.length; i++) {
|
||||
const div = textbox.childNodes[i];
|
||||
if (div.nodeType === Node.ELEMENT_NODE && div.tagName === 'DIV') {
|
||||
let line = "";
|
||||
for (let j = 0; j < div.childNodes.length; j++) {
|
||||
line += div.childNodes[j].textContent;
|
||||
}
|
||||
newText += line + (i < textbox.childNodes.length - 1 ? "\n" : "");
|
||||
} else if (div.nodeType === Node.TEXT_NODE) {
|
||||
newText += div.textContent;
|
||||
}
|
||||
}
|
||||
return newText;
|
||||
}
|
||||
|
||||
// Wait for the window to load before doing anything
|
||||
window.addEventListener("load", function() {
|
||||
|
||||
window.addEventListener("load", function () {
|
||||
// Get all the elements
|
||||
textbox = document.getElementById("editor");
|
||||
textconsole = document.getElementById("console");
|
||||
filebutton = document.getElementById("buttonTrigger");
|
||||
fileinput = document.getElementById("fileInput");
|
||||
|
||||
// Check if elements exist
|
||||
if (!textbox || !textconsole || !filebutton || !fileinput) {
|
||||
console.error("Some required elements not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
textbox.innerText = text;
|
||||
filebutton = document.getElementById('buttonTrigger');
|
||||
fileinput = document.getElementById('fileInput');
|
||||
|
||||
// Initial render
|
||||
renderHighlightedText();
|
||||
|
||||
// Set up watchers for everything
|
||||
filebutton.addEventListener('click', () => {
|
||||
filebutton.addEventListener("click", () => {
|
||||
fileinput.click();
|
||||
})
|
||||
});
|
||||
|
||||
fileinput.addEventListener('change', () => {
|
||||
fileinput.addEventListener("change", () => {
|
||||
const file = fileinput.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
reader.onload = function (e) {
|
||||
text = e.target.result;
|
||||
textbox.innerText = text;
|
||||
renderHighlightedText();
|
||||
};
|
||||
reader.readAsText(file);
|
||||
} else {
|
||||
console.log("what");
|
||||
console.log("No file selected");
|
||||
}
|
||||
});
|
||||
|
||||
// When we press a key, start doing stuff
|
||||
textbox.addEventListener("input", () => {
|
||||
text = getTextFromEditor();
|
||||
renderHighlightedText();
|
||||
});
|
||||
|
||||
// Handle tab key to insert 4 spaces
|
||||
textbox.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Tab") {
|
||||
e.preventDefault();
|
||||
|
||||
// Insert 4 spaces at cursor position
|
||||
const selection = window.getSelection();
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
// Create a text node with 4 spaces
|
||||
const textNode = document.createTextNode(" ");
|
||||
range.insertNode(textNode);
|
||||
|
||||
// Move cursor to after the inserted spaces
|
||||
range.setStartAfter(textNode);
|
||||
range.collapse(true);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
|
||||
// Update our text variable and re-render
|
||||
text = getTextFromEditor();
|
||||
renderHighlightedText();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle paste events
|
||||
textbox.addEventListener("paste", (e) => {
|
||||
e.preventDefault();
|
||||
const paste = (e.clipboardData || window.clipboardData).getData("text");
|
||||
document.execCommand("insertText", false, paste);
|
||||
text = getTextFromEditor();
|
||||
renderHighlightedText();
|
||||
});
|
||||
});
|
||||
|
||||
const keywords = ["if", "jump", "end", "input", "stdin", "print", "stdout", "println", "stdlnout", "set", "gettype", "exists", "setlist", "setlistat", "getlistat", "getlistsize", "listappend", "getstrsize", "getstrcharat", "add", "subtract", "multiply", "divide", "equal", "inequal", "not", "greater", "lesser", "stoi", "stod", "tostring", "fun", "return", "endfun", "pusharg", "call", "struct", "endstruct", "init", "use", "extern", "catch"];
|
||||
const keywords = [
|
||||
"if",
|
||||
"jump",
|
||||
"end",
|
||||
"input",
|
||||
"stdin",
|
||||
"print",
|
||||
"stdout",
|
||||
"println",
|
||||
"stdlnout",
|
||||
"set",
|
||||
"gettype",
|
||||
"exists",
|
||||
"setlist",
|
||||
"setlistat",
|
||||
"getlistat",
|
||||
"getlistsize",
|
||||
"listappend",
|
||||
"getstrsize",
|
||||
"getstrcharat",
|
||||
"add",
|
||||
"subtract",
|
||||
"multiply",
|
||||
"divide",
|
||||
"equal",
|
||||
"inequal",
|
||||
"not",
|
||||
"greater",
|
||||
"lesser",
|
||||
"stoi",
|
||||
"stod",
|
||||
"tostring",
|
||||
"fun",
|
||||
"return",
|
||||
"endfun",
|
||||
"pusharg",
|
||||
"call",
|
||||
"struct",
|
||||
"endstruct",
|
||||
"init",
|
||||
"use",
|
||||
"extern",
|
||||
"catch",
|
||||
];
|
||||
|
||||
const colours = {
|
||||
keyword: "#42fff2",
|
||||
comment: "#383838",
|
||||
number: "#42ffb7",
|
||||
number: "#42ff62",
|
||||
string: "#42ff62",
|
||||
valref: "#9544c7",
|
||||
dirref: "#f587ff",
|
||||
lineref: "#9bf542",
|
||||
label: "#fff67d",
|
||||
function: "#ffa640",
|
||||
typeref: "#ff40ac"
|
||||
}
|
||||
typeref: "#ff40ac",
|
||||
};
|
||||
|
||||
let instring = false;
|
||||
let iscomment = false;
|
||||
|
||||
// Function to handle highlighting tokens that have been typed
|
||||
function highlightToken(token) {
|
||||
|
||||
if (instring) {
|
||||
if (token[token.length - 1] == '"' || token[token.length - 1] == '"') {
|
||||
instring = false;
|
||||
function highlightTokens(line) {
|
||||
const tokens = line.split(/(\s+|"[^"]*"|'[^']*')/).filter(Boolean);
|
||||
let instring = false;
|
||||
return tokens.map(token => {
|
||||
if (token.startsWith('"') || token.startsWith("'")) {
|
||||
instring = !instring;
|
||||
return `<span style="color: ${colours.string}">${token}</span>`;
|
||||
}
|
||||
return `<span style="color: ${colours.string}">${token}</span>`
|
||||
}
|
||||
|
||||
// Comments
|
||||
if (iscomment) {
|
||||
return `<span style="color: ${colours.comment}"> ${token}</span>"`
|
||||
}
|
||||
|
||||
if (token[0] == '#') {
|
||||
iscomment = true;
|
||||
return `<span style="color: ${colours.comment}> ${token}</span>"`
|
||||
}
|
||||
|
||||
// Keywords
|
||||
if (keywords.includes(token)) {
|
||||
return `<span style="color: ${colours.keyword}">${token}</span>`;
|
||||
}
|
||||
|
||||
// Direct references
|
||||
if (token[0] == '&') {
|
||||
return `<span style="color: ${colours.dirref}">${token}</span>`;
|
||||
}
|
||||
|
||||
// Value references
|
||||
if (token[0] == '$') {
|
||||
return `<span style="color: ${colours.valref}">${token}</span>`;
|
||||
}
|
||||
|
||||
// Strings and characters
|
||||
if (token[0] == '"' || token[0] == "'") {
|
||||
if (!(token[token.length - 1] == '"' || token[token.length - 1] == '"')) {
|
||||
instring = true;
|
||||
if (instring) {
|
||||
return `<span style="color: ${colours.string}">${token}</span>`;
|
||||
}
|
||||
return `<span style="color: ${colours.string}">${token}</span>`;
|
||||
}
|
||||
|
||||
return `<span>${token}</span>`;
|
||||
}
|
||||
|
||||
// When we press a key, start doing stuff
|
||||
textbox.addEventListener('input', () => {
|
||||
text = textbox.innerText;
|
||||
renderHighlightedText();
|
||||
});
|
||||
|
||||
|
||||
// Old code
|
||||
/*
|
||||
onkeydown = (event) => {
|
||||
if (event.key == "Enter") {
|
||||
//text += "</p>\n<p>";
|
||||
text += "\n"
|
||||
} else if (event.key == "Tab") {
|
||||
text += " ";
|
||||
} else if (event.key == "Backspace") {
|
||||
text = text.slice(0, -1)
|
||||
} else if (!(event.key == "Control" || event.key == "Alt" || event.key == "Meta" || event.key == "Shift" || event.key == "Escape")) {
|
||||
text += event.key;
|
||||
}
|
||||
let renderedText = "";
|
||||
const lines = text.split("\n");
|
||||
for (const line of lines) {
|
||||
renderedText += "<p>";
|
||||
const tokens = line.split(" ");
|
||||
for (const token of tokens) {
|
||||
renderedText += highlightToken(token) + " ";
|
||||
if (keywords.includes(token)) {
|
||||
return `<span style="color: ${colours.keyword}">${token}</span>`;
|
||||
}
|
||||
renderedText += "</p>";
|
||||
instring = false;
|
||||
iscomment = false;
|
||||
}
|
||||
textbox.innerHTML = renderedText;
|
||||
if (!isNaN(token) && token.trim() !== "") {
|
||||
return `<span style="color: ${colours.number}">${token}</span>`;
|
||||
}
|
||||
if (token.startsWith('&')) {
|
||||
return `<span style="color: ${colours.dirref}">${token}</span>`;
|
||||
}
|
||||
if (token.startsWith('$')) {
|
||||
return `<span style="color: ${colours.valref}">${token}</span>`;
|
||||
}
|
||||
if (token.startsWith('-')) {
|
||||
return `<span style="color: ${colours.typeref}">${token}</span>`;
|
||||
}
|
||||
if (token.startsWith('!')) {
|
||||
return `<span style="color: ${colours.function}">${token}</span>`;
|
||||
}
|
||||
if (token.startsWith('%')) {
|
||||
return `<span style="color: ${colours.lineref}">${token}</span>`;
|
||||
}
|
||||
if (token.startsWith('@')) {
|
||||
return `<span style="color: ${colours.label}">${token}</span>`;
|
||||
}
|
||||
return token;
|
||||
}).join('');
|
||||
}
|
||||
*/
|
||||
|
||||
// Function to run code on the server
|
||||
async function runCode() {
|
||||
console.log(text.split("<p>").join("").split("</p>").join(""));
|
||||
const result = await fetch("http://localhost:5000/runProgram", {
|
||||
"method": "POST",
|
||||
//"mode": "no-cors",
|
||||
"body": text.split("<p>").join("").split("</p>").join("")
|
||||
});
|
||||
const data = await result.json();
|
||||
// Ensure everything's seperated
|
||||
textconsole.innerHTML = "<p>" + data.stdout.split("\n").join("</p><p>") + "</p>";
|
||||
if (!textconsole) {
|
||||
console.error("Console element not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have the latest text
|
||||
// The `text` variable is now the single source of truth.
|
||||
|
||||
if (firstRun) {
|
||||
textconsole.innerHTML = "";
|
||||
firstRun = false;
|
||||
}
|
||||
|
||||
console.log("Running code:", text);
|
||||
|
||||
try {
|
||||
const result = await fetch("/runProgram", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
body: text,
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
throw new Error(`HTTP error! status: ${result.status}`);
|
||||
}
|
||||
|
||||
const data = await result.json();
|
||||
const output = data.stdout.split('\n').join('</div><div>');
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
textconsole.innerHTML += `<div>[${timestamp}]</div><div>${output}</div>`;
|
||||
textconsole.scrollTop = textconsole.scrollHeight; // Scroll to bottom
|
||||
} catch (error) {
|
||||
console.error("Error running code:", error);
|
||||
textconsole.innerHTML =
|
||||
"<div style='color: #ff4444'>Error: " + error.message + "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user