From 56e2837c7179ecec87f21bb6ea71312ee027e373 Mon Sep 17 00:00:00 2001
From: Maxwell Jeffress
Date: Sat, 27 Sep 2025 15:09:26 +1000
Subject: [PATCH] crap
---
client/index.css | 76 ++++++---
client/index.html | 2 +-
client/index.js | 409 +++++++++++++++++++++++++++++++++-------------
3 files changed, 341 insertions(+), 146 deletions(-)
diff --git a/client/index.css b/client/index.css
index 881cb5d..54aca79 100644
--- a/client/index.css
+++ b/client/index.css
@@ -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;
+}
diff --git a/client/index.html b/client/index.html
index a2e9aa2..1b90e94 100644
--- a/client/index.html
+++ b/client/index.html
@@ -12,7 +12,7 @@
Run
- Choose File
+ Choose File
Output will be shown here, click the run button to run your code!
diff --git a/client/index.js b/client/index.js
index b08d2b5..19bba24 100644
--- a/client/index.js
+++ b/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 += "";
- const tokens = line.split(" ");
- for (const token of tokens) {
- renderedText += highlightToken(token) + " ";
- }
- renderedText += "
";
- 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 += "
"; // Preserve empty lines
+ } else {
+ const commentIndex = line.indexOf("#");
+ if (commentIndex !== -1) {
+ const beforeComment = line.substring(0, commentIndex);
+ const comment = line.substring(commentIndex);
+ renderedText += `${highlightTokens(beforeComment)}${comment}
`;
+ } else {
+ renderedText += `${highlightTokens(line)}
`;
+ }
+ }
+ }
+
+ 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 `${token} `;
}
- return `${token} `
- }
-
- // Comments
- if (iscomment) {
- return ` ${token} "`
- }
-
- if (token[0] == '#') {
- iscomment = true;
- return `${token} `;
- }
-
- // Direct references
- if (token[0] == '&') {
- return `${token} `;
- }
-
- // Value references
- if (token[0] == '$') {
- return `${token} `;
- }
-
- // Strings and characters
- if (token[0] == '"' || token[0] == "'") {
- if (!(token[token.length - 1] == '"' || token[token.length - 1] == '"')) {
- instring = true;
+ if (instring) {
+ return `${token} `;
}
- return `${token} `;
- }
-
- return `${token} `;
-}
-
-// When we press a key, start doing stuff
-textbox.addEventListener('input', () => {
- text = textbox.innerText;
- renderHighlightedText();
-});
-
-
-// Old code
-/*
-onkeydown = (event) => {
- if (event.key == "Enter") {
- //text += "
\n";
- 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 += "
";
- const tokens = line.split(" ");
- for (const token of tokens) {
- renderedText += highlightToken(token) + " ";
+ if (keywords.includes(token)) {
+ return `${token} `;
}
- renderedText += "
";
- instring = false;
- iscomment = false;
- }
- textbox.innerHTML = renderedText;
+ if (!isNaN(token) && token.trim() !== "") {
+ return `${token} `;
+ }
+ if (token.startsWith('&')) {
+ return `${token} `;
+ }
+ if (token.startsWith('$')) {
+ return `${token} `;
+ }
+ if (token.startsWith('-')) {
+ return `${token} `;
+ }
+ if (token.startsWith('!')) {
+ return `${token} `;
+ }
+ if (token.startsWith('%')) {
+ return `${token} `;
+ }
+ if (token.startsWith('@')) {
+ return `${token} `;
+ }
+ return token;
+ }).join('');
}
-*/
// Function to run code on the server
async function runCode() {
- console.log(text.split("").join("").split("
").join(""));
- const result = await fetch("http://localhost:5000/runProgram", {
- "method": "POST",
- //"mode": "no-cors",
- "body": text.split("").join("").split("
").join("")
- });
- const data = await result.json();
- // Ensure everything's seperated
- textconsole.innerHTML = "" + data.stdout.split("\n").join("
") + "
";
+ 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('');
+ const timestamp = new Date().toLocaleTimeString();
+ textconsole.innerHTML += `
[${timestamp}]
${output}
`;
+ textconsole.scrollTop = textconsole.scrollHeight; // Scroll to bottom
+ } catch (error) {
+ console.error("Error running code:", error);
+ textconsole.innerHTML =
+ "
Error: " + error.message + "
";
+ }
}