diff --git a/.env.example b/.env.example
index 222d580..5087031 100644
--- a/.env.example
+++ b/.env.example
@@ -19,6 +19,8 @@ COMPILER_CODE_PROCESSING_TIMEOUT=5
COMPILER_REMOTE_INCLUDE_CACHING=true
COMPILER_NSJAIL_CFG=/third_party/nsjail-emscripten.cfg
+DUSK_START_MAXIMIZED=true
+
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
diff --git a/.env.test b/.env.test
index 3a4a1c4..f84a231 100644
--- a/.env.test
+++ b/.env.test
@@ -19,6 +19,7 @@ COMPILER_CODE_PROCESSING_TIMEOUT=5
COMPILER_REMOTE_INCLUDE_CACHING=false
COMPILER_NSJAIL_CFG=/third_party/nsjail-emscripten-ci.cfg
+DUSK_START_MAXIMIZED=true
BCRYPT_ROUNDS=12
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2669e99..026553c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. Each batch
It is a summary of changes that would be pertinent to the end user of the PGEtinker website. For a comprehensive history of changes made to the project, please refer to the repository's commit history.
+## 2024-05-27
+
+- Changed complete revamp of the frontend code, much more organized
+- Fixed UI annoyances
+- Added Control+S to the Build and Run command
+- Added Default editor font size
+- Added Control+Mouse Wheel zooming in the editor
+- Added Control+0 to reset editor zoom
+- Changed Build & Run to Run, that turns into a stop button when the player is running
+
## 2024-05-25
- Fixed screenshot failure handling
diff --git a/resources/js/app.js b/resources/js/app.js
index 025f05b..b35313a 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -1,6 +1,6 @@
import './lib/bootstrap';
import './lib/goldenLayout';
-import './lib/monaco';
+// import './lib/monaco';
import './lib/lucide';
import version from "./lib/version";
import agreeDialog from './lib/agreeDialog';
@@ -9,33 +9,32 @@ import newsDialog from './lib/newsDialog';
import defaultLayout from './lib/defaultLayout';
import supportersDialog from './lib/supportersDialog';
+import ConsolePanel from './components/ConsolePanel';
+import EditorPanel from './components/EditorPanel';
+import InfoPanel from './components/InfoPanel';
+import PlayerPanel from './components/PlayerPanel';
+
class PGEtinker
{
- sharedFlag = false;
- lastPlayerHtml = "";
+ consolePanel;
+ editorPanel;
+ infoPanel;
+ playerPanel;
layoutInitialized = false;
compiling = false;
layoutConfig = null;
- maxFileSize = 50000;
-
theme = "dark";
- consoleShown = false;
-
- monacoEditor = null;
- monacoModel = null;
- monacoModelIntellisense = null;
-
- consolePanelExist = false;
- informationPanelExist = false;
- consoleAutoScrollEnabled = true;
constructor()
{
- this.sharedFlag = (window.location.pathname.indexOf("/s/") === 0);
-
+ this.consolePanel = new ConsolePanel(this);
+ this.editorPanel = new EditorPanel(this);
+ this.infoPanel = new InfoPanel(this);
+ this.playerPanel = new PlayerPanel(this);
+
this.layoutConfig = window.localStorage.getItem("pgetinkerLayout");
this.layoutConfig = (this.layoutConfig !== null) ? JSON.parse(this.layoutConfig) : defaultLayout;
@@ -43,9 +42,6 @@ class PGEtinker
if(this.theme !== "dark" && this.theme !== "light")
this.theme = "dark";
- this.consoleShown = window.localStorage.getItem("pgetinkerConsoleShown");
- this.consoleShown = (this.consoleShown === "true") ? true : false;
-
// Default Code Button
document.querySelector("#default-code").addEventListener("click", (event) =>
{
@@ -53,8 +49,8 @@ class PGEtinker
axios.get("/api/default-code").then((response) =>
{
- this.monacoModel.setValue(response.data.code);
- this.monacoEditor.revealPositionInCenter({
+ this.editorPanel.setValue(response.data.code);
+ this.editorPanel.reveal({
column: 1,
lineNumber: 1,
});
@@ -90,7 +86,7 @@ class PGEtinker
{
event.preventDefault();
- if(!this.lastPlayerHtml.includes("Emscripten-Generated Code"))
+ if(!this.playerPanel.getHtml().includes("Emscripten-Generated Code"))
{
alert("You have to build the code before you can download!")
return;
@@ -99,7 +95,7 @@ class PGEtinker
const a = document.createElement('a');
// create the data url
- a.href = `data:text/html;base64,${btoa(this.lastPlayerHtml)}`;
+ a.href = `data:text/html;base64,${btoa(this.playerPanel.getHtml())}`;
a.download = "pgetinker.html";
document.body.appendChild(a);
@@ -119,7 +115,7 @@ class PGEtinker
return;
axios.post("/api/share", {
- code: this.monacoEditor.getValue()
+ code: this.editorPanel.getValue()
}).then((response) =>
{
shareDialog(response.data.shareURL, response.data.shareThumbURL)
@@ -152,43 +148,35 @@ class PGEtinker
});
// Compile Button
- document.querySelector("#compile").addEventListener("click", (event) =>
+ document.querySelector("#start-stop").addEventListener("click", (event) =>
{
event.preventDefault();
+ let startStopElem = document.querySelector("#start-stop");
+ let playIconElem = startStopElem.querySelector(".lucide-circle-play");
+ let stopIconElem = startStopElem.querySelector(".lucide-circle-stop");
+ let spanElem = startStopElem.querySelector("span");
- if(this.compiling)
+ if(spanElem.innerHTML == "Run")
+ {
+ playIconElem.classList.toggle("hidden", true);
+ stopIconElem.classList.toggle("hidden", false);
+ spanElem.innerHTML = "Stop";
+ this.compile().catch(() =>
+ {
+ playIconElem.classList.toggle("hidden", false);
+ stopIconElem.classList.toggle("hidden", true);
+ spanElem.innerHTML = "Run";
+ });
return;
+ }
- if(!this.preCompile())
- return;
-
- axios.post("/api/compile", {
- code: this.monacoEditor.getValue()
- }).then((response) =>
- {
- this.compileSuccessHandler(response.data);
- }).catch((error) =>
+ if(spanElem.innerHTML == "Stop")
{
-
- if(error.response)
- {
- if(error.response.status)
- {
- if(error.response.status == 503)
- {
- this.compileFailHandler("pgetinker.cpp:1:1: error: PGEtinker service has gone offline. try again later.\n");
- return;
- }
- }
-
- if(error.response.data.stderr)
- {
- this.compileFailHandler(error.response.data.stderr);
- return;
- }
- }
- this.compileFailHandler("pgetinker.cpp:1:1: error: compilation failed in a way that's not being handled. please make a bug report.\n");
- });
+ this.playerPanel.stop();
+ playIconElem.classList.toggle("hidden", false);
+ stopIconElem.classList.toggle("hidden", true);
+ spanElem.innerHTML = "Run";
+ }
});
document.querySelector("#supporters").addEventListener("click", (event) =>
@@ -203,51 +191,6 @@ class PGEtinker
newsDialog();
});
- window.addEventListener("message", (event) =>
- {
- if(typeof event.data !== "object")
- return;
-
- if(typeof event.data.message !== "string")
- return;
-
- if(event.data.message === "player-ready")
- {
- // update player theme
- document.querySelector("#player-panel iframe").contentWindow.postMessage({
- message: "set-theme",
- theme: this.theme
- }, "*");
-
- // update player theme
- document.querySelector("#player-panel iframe").contentWindow.postMessage({
- message: "show-console",
- value: this.consoleShown
- }, "*");
- }
-
- if(event.data.message === "console-output")
- {
- if(!this.informationPanelExist)
- return;
-
-
- let consoleContainer = document.querySelector("#console-panel");
- consoleContainer.innerHTML += `
${event.data.data}
`;
-
- // auto scroll
- if(this.consoleAutoScrollEnabled)
- consoleContainer.scrollTop = consoleContainer.scrollHeight;
-
- let consolePanel = this.layout.root.getItemsById('console')[0];
- if(consolePanel.parent.isStack)
- {
- consolePanel.parent.setActiveContentItem(consolePanel);
- }
- }
-
- });
-
let agreedToTerms = window.localStorage.getItem("pgetinkerAgreedToTerms");
agreedToTerms = (agreedToTerms == null) ? false : JSON.parse(agreedToTerms);
@@ -276,174 +219,92 @@ class PGEtinker
preCompile()
{
- if(this.monacoEditor.getValue().length > this.maxFileSize)
+ if(this.editorPanel.exceedsMaxSize())
{
alert("Maximum size exceeded!");
return false;
}
- if(this.informationPanelExist)
- {
- let infoPanel = this.layout.root.getItemsById('info')[0];
- if(infoPanel.parent.isStack)
- {
- infoPanel.parent.setActiveContentItem(infoPanel);
- }
-
- document.querySelector("#info-panel").innerHTML = "";
- document.querySelector("#console-panel").innerHTML = "";
- }
+ this.infoPanel.focus();
+ this.infoPanel.clear();
+ this.consolePanel.clear();
+ this.editorPanel.clearMarkers();
+ this.playerPanel.setCompiling();
+
this.compiling = true;
+ return true;
+ }
- this.lastPlayerHtml = "";
- let playerFrame = document.querySelector("#player-panel iframe");
+ compile()
+ {
+ if(this.compiling)
+ return new Promise((_, reject) => reject());
+
+ if(!this.preCompile())
+ return new Promise((_, reject) => reject());
- if(playerFrame != null)
- playerFrame.remove();
+ return new Promise((resolve, reject) =>
+ {
+ axios.post("/api/compile", {
+ code: this.editorPanel.getValue()
+ }).then((response) =>
+ {
+ this.compileSuccessHandler(response.data);
+ resolve();
+ }).catch((error) =>
+ {
+
+ if(error.response)
+ {
+ if(error.response.status)
+ {
+ if(error.response.status == 503)
+ {
+ this.compileFailHandler("pgetinker.cpp:1:1: error: PGEtinker service has gone offline. try again later.\n");
+ reject();
+ return;
+ }
+ }
+
+ if(error.response.data.stderr)
+ {
+ this.compileFailHandler(error.response.data.stderr);
+ reject();
+ return;
+ }
+ }
+ this.compileFailHandler("pgetinker.cpp:1:1: error: compilation failed in a way that's not being handled. please make a bug report.\n");
+ reject();
+ });
+ });
- document.querySelector("#player-panel .compiling").classList.toggle("display-flex", true);
- document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
-
- monaco.editor.removeAllMarkers("owner");
- this.monacoEditor.trigger("", "closeMarkersNavigation");
-
- return true;
}
compileSuccessHandler(data)
{
- this.lastPlayerHtml = data.html;
-
- let playerFrame = document.createElement('iframe');
- playerFrame.setAttribute("srcdoc", this.lastPlayerHtml);
- playerFrame.setAttribute("sandbox", "allow-scripts");
- document.querySelector("#player-panel .iframe-container").append(playerFrame);
-
- playerFrame.classList.toggle("display-block", true);
- document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
- document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
-
+ this.playerPanel.setHtml(data.html);
this.compiling = false;
}
compileFailHandler(stderr)
{
- let infoPanel = document.querySelector("#info-panel");
- infoPanel.innerHTML = `${stderr}
`;
- infoPanel.scrollTop = infoPanel.scrollHeight;
+ this.infoPanel.setContent(stderr);
+ this.editorPanel.extractAndSetMarkers(stderr);
- const compilerRegex = /pgetinker.cpp:(\d+):(\d+): (fatal error|error|warning|note): (.*)/gm;
- const linkerRegex = /wasm-ld: error: pgetinker.o: (.*): (.*)/gm;
-
- let markers = [];
-
- let matches;
-
- while((matches = compilerRegex.exec(stderr)) !== null)
- {
- let severity = monaco.MarkerSeverity.Error;
-
- if(matches[3] == "warning")
- severity = monaco.MarkerSeverity.Warning;
-
- if(matches[3] == "note")
- severity = monaco.MarkerSeverity.Info;
-
- markers.push({
- message: matches[4],
- severity: severity,
- startLineNumber: parseInt(matches[1]),
- startColumn: parseInt(matches[2]),
- endLineNumber: parseInt(matches[1]),
- endColumn: this.monacoModel.getLineLength(parseInt(matches[1])),
- source: "Emscripten Compiler",
- });
- }
-
- while((matches = linkerRegex.exec(stderr)) !== null)
- {
- markers.push({
- message: `${matches[1]} ${matches[2]}`,
- severity: monaco.MarkerSeverity.Error,
- startLineNumber: 1,
- startColumn: 1,
- endLineNumber: 1,
- endColumn: this.monacoModel.getLineLength(1),
- source: "Emscripten Linker",
- });
- }
-
- // show errors in the editor, if they exist
- if(markers.length > 0)
- {
- monaco.editor.setModelMarkers(this.monacoModel, "owner", markers);
- this.monacoEditor.setPosition({lineNumber: markers[0].startLineNumber, column: markers[0].startColumn });
- setTimeout(() => { this.monacoEditor.trigger("", "editor.action.marker.next"); }, 50);
- }
-
- document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
- document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", true);
+ this.playerPanel.setCompilingFailed();
this.compiling = false;
}
SetupLayout()
{
this.layout = new GoldenLayout(this.layoutConfig, document.querySelector("#content"))
-
- this.layout.registerComponent('consoleComponent', function(container)
- {
- container.getElement().html(`
-
-
- `);
- });
-
- this.layout.registerComponent('infoComponent', function(container)
- {
- container.getElement().html(`
-
-
- `);
- });
-
- this.layout.registerComponent('playerComponent', function(container)
- {
- container.getElement().html(`
-
-
-
-
-
-
-
- Compile Failed.
-
-
-
- `);
- });
-
- this.layout.registerComponent('editorComponent', function(container)
- {
- container.getElement().html(`
-
- `);
- });
+ this.consolePanel.register();
+ this.editorPanel.register();
+ this.infoPanel.register();
+ this.playerPanel.register();
+
this.layout.on("stateChanged", () =>
{
if(this.layoutInitialized)
@@ -452,115 +313,14 @@ class PGEtinker
this.layout.on("initialised", () =>
{
- this.informationPanelExist = (this.layout.root.getItemsById('info').length > 0);
- this.consolePanelExist = (this.layout.root.getItemsById('console').length > 0);
-
this.layoutInitialized = true;
window.addEventListener("resize", (event) => this.layout.updateSize());
- if(this.monacoModel === null)
- {
- this.monacoModel = monaco.editor.createModel("", "cpp", monaco.Uri.parse("inmemory://pgetinker.cpp"));
-
- let codeBox = document.querySelector("#code");
- if(codeBox.value !== "")
- {
- this.monacoModel.setValue(document.querySelector("#code").value);
- window.localStorage.setItem("pgetinkerCode", JSON.stringify(document.querySelector("#code").value));
- }
- else
- {
- let code = window.localStorage.getItem("pgetinkerCode");
- code = (code !== null) ? JSON.parse(code) : "";
-
- if(code === "")
- {
- axios.get("/api/default-code").then((response) =>
- {
- this.monacoModel.setValue(response.data.code);
- }).catch((reason) => console.log(reason));
- }
- else
- {
- this.monacoModel.setValue(code);
- }
- }
- }
-
- if(this.monacoModelIntellisense === null)
- {
- this.monacoModelIntellisense = monaco.editor.createModel("", "cpp", monaco.Uri.parse("inmemory://pgetinker.h"));
- axios.get("/api/model/v0.02").then((response) =>
- {
- this.monacoModelIntellisense.setValue(response.data);
- });
- }
-
- this.monacoEditor = monaco.editor.create(document.querySelector('#editor-panel .code-editor'), {
- automaticLayout: true,
- model: this.monacoModel,
- theme: `vs-${this.theme}`,
- });
-
- this.monacoEditor.addAction({
- id: 'build-and-run',
- label: 'Build and Run',
- keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
- run: () =>
- {
- document.querySelector("#compile").dispatchEvent(new Event("click"));
- }
- });
-
- this.monacoEditor.onDidChangeCursorPosition(() => this.UpdateStatusBar());
+ this.consolePanel.onInit();
+ this.editorPanel.onInit();
+ this.infoPanel.onInit();
+ this.playerPanel.onInit();
- this.monacoEditor.onDidChangeModelContent(() =>
- {
- window.localStorage.setItem("pgetinkerCode", JSON.stringify(this.monacoEditor.getValue()));
-
- if(this.sharedFlag)
- {
- window.history.replaceState({}, "", "/");
- }
- });
-
- if(this.lastPlayerHtml != "")
- {
- let playerFrame = document.createElement('iframe');
- playerFrame.setAttribute("srcdoc", this.lastPlayerHtml);
- playerFrame.setAttribute("sandbox", "allow-scripts");
- document.querySelector("#player-panel .iframe-container").append(playerFrame);
-
- playerFrame.classList.toggle("display-block", true);
- document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
- document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
- }
-
- let consoleContainer = document.querySelector("#console-panel");
-
- document.querySelector("#console-auto-scroll").addEventListener("click", () =>
- {
- this.consoleAutoScrollEnabled = true;
- document.querySelector("#console-auto-scroll").classList.toggle("hidden", this.consoleAutoScrollEnabled);
- });
-
- consoleContainer.addEventListener("wheel", (event) =>
- {
- let nearBottom = ((consoleContainer.scrollHeight - consoleContainer.clientHeight) <= (consoleContainer.scrollTop + 1));
-
- if(nearBottom)
- {
- // up
- if(event.deltaY < 0)
- {
- this.consoleAutoScrollEnabled = false;
- consoleContainer.scrollTop = consoleContainer.scrollHeight - 20;
- document.querySelector("#console-auto-scroll").classList.toggle("hidden", this.consoleAutoScrollEnabled);
- }
- }
- });
-
- this.UpdateStatusBar();
this.UpdateTheme();
});
@@ -580,30 +340,6 @@ class PGEtinker
}
- UpdateStatusBar()
- {
- let statusBar = document.querySelector("#editor-panel .status");
-
- let cursor = `Ln ${this.monacoEditor.getPosition().lineNumber}, Col ${this.monacoEditor.getPosition().column}`;
- let fileSize = `${new Intl.NumberFormat().format(this.monacoEditor.getValue().length)} / ${new Intl.NumberFormat().format(this.maxFileSize)}`;
-
- statusBar.classList.toggle('too-fucking-big', false);
- if(this.monacoModel.getValueLength() > this.maxFileSize)
- {
- statusBar.classList.toggle('too-fucking-big', true);
- fileSize += " EXCEEDING MAXIMUM!";
- }
-
- statusBar.innerHTML = `
-
- Bytes: ${fileSize}
-
-
- ${cursor}
-
- `;
- }
-
UpdateTheme()
{
// update overall theme
@@ -626,22 +362,18 @@ class PGEtinker
}
// update editor theme
- if(this.monacoEditor !== null)
- this.monacoEditor.updateOptions({ theme: `vs-${this.theme}`});
+ this.editorPanel.setTheme(this.theme);
// update player theme
- let playerFrame = document.querySelector("#player-panel iframe");
- if(playerFrame != null)
- {
- document.querySelector("#player-panel iframe").contentWindow.postMessage({
- message: "set-theme",
- theme: this.theme
- }, "*");
- }
-
+ this.playerPanel.setTheme(this.theme);
+
// save theme into localStorage
window.localStorage.setItem("pgetinkerTheme", this.theme);
}
}
new PGEtinker();
+
+
+
+
diff --git a/resources/js/components/ConsolePanel.js b/resources/js/components/ConsolePanel.js
new file mode 100644
index 0000000..0e13985
--- /dev/null
+++ b/resources/js/components/ConsolePanel.js
@@ -0,0 +1,103 @@
+
+export default class ConsolePanel
+{
+ consoleShown = false;
+ consolePanelExist = false;
+ consoleAutoScrollEnabled = true;
+
+ state;
+
+ constructor(state)
+ {
+ this.state = state;
+ console.log("Console panel", "constructor");
+
+ this.consoleShown = window.localStorage.getItem("pgetinkerConsoleShown");
+ this.consoleShown = (this.consoleShown === "true") ? true : false;
+
+ window.addEventListener("message", (event) =>
+ {
+ if(typeof event.data !== "object")
+ return;
+
+ if(typeof event.data.message !== "string")
+ return;
+
+ if(event.data.message === "console-output")
+ {
+ if(!this.state.infoPanel.exists())
+ return;
+
+
+ let consoleContainer = document.querySelector("#console-panel");
+ consoleContainer.innerHTML += `${event.data.data}
`;
+
+ // auto scroll
+ if(this.consoleAutoScrollEnabled)
+ consoleContainer.scrollTop = consoleContainer.scrollHeight;
+
+ let consolePanel = this.state.layout.root.getItemsById('console')[0];
+ if(consolePanel.parent.isStack)
+ {
+ consolePanel.parent.setActiveContentItem(consolePanel);
+ }
+ }
+ });
+ }
+
+ clear()
+ {
+ document.querySelector("#console-panel").innerHTML = "";
+ }
+
+ exists()
+ {
+ return this.consolePanelExist;
+ }
+
+ onInit()
+ {
+ this.consolePanelExist = (this.state.layout.root.getItemsById('console').length > 0);
+
+ let consoleContainer = document.querySelector("#console-panel");
+
+ document.querySelector("#console-auto-scroll").addEventListener("click", () =>
+ {
+ this.consoleAutoScrollEnabled = true;
+ document.querySelector("#console-auto-scroll").classList.toggle("hidden", this.consoleAutoScrollEnabled);
+ });
+
+ consoleContainer.addEventListener("wheel", (event) =>
+ {
+ let nearBottom = ((consoleContainer.scrollHeight - consoleContainer.clientHeight) <= (consoleContainer.scrollTop + 1));
+
+ if(nearBottom)
+ {
+ // up
+ if(event.deltaY < 0)
+ {
+ this.consoleAutoScrollEnabled = false;
+ consoleContainer.scrollTop = consoleContainer.scrollHeight - 20;
+ document.querySelector("#console-auto-scroll").classList.toggle("hidden", this.consoleAutoScrollEnabled);
+ }
+ }
+ });
+ }
+
+ register()
+ {
+ this.state.layout.registerComponent('consoleComponent', function(container)
+ {
+ container.getElement().html(`
+
+
+ `);
+ });
+ }
+
+ shown()
+ {
+ return this.consoleShown;
+ }
+
+}
\ No newline at end of file
diff --git a/resources/js/components/EditorPanel.js b/resources/js/components/EditorPanel.js
new file mode 100644
index 0000000..f09189d
--- /dev/null
+++ b/resources/js/components/EditorPanel.js
@@ -0,0 +1,251 @@
+import * as monaco from 'monaco-editor';
+import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
+
+self.MonacoEnvironment = {
+ getWorker: function (workerId, label)
+ {
+ return editorWorker();
+ }
+};
+monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true);
+monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
+ noLib: true,
+ allowNonTsExtensions: true
+});
+
+export default class EditorPanel
+{
+ state;
+
+ monacoEditor = null;
+ monacoModel = null;
+ monacoModelIntellisense = null;
+
+ maxFileSize = 50000;
+
+ sharedFlag = false;
+
+ constructor(state)
+ {
+ this.state = state;
+ this.sharedFlag = (window.location.pathname.indexOf("/s/") === 0);
+ console.log("Editor panel", "constructor");
+ }
+
+ clearMarkers()
+ {
+ monaco.editor.removeAllMarkers("owner");
+ this.monacoEditor.trigger("", "closeMarkersNavigation");
+ }
+
+ exceedsMaxSize()
+ {
+ return this.monacoEditor.getValue().length > this.maxFileSize;
+ }
+
+ extractAndSetMarkers(data)
+ {
+ const compilerRegex = /pgetinker.cpp:(\d+):(\d+): (fatal error|error|warning|note): (.*)/gm;
+ const linkerRegex = /wasm-ld: error: pgetinker.o: (.*): (.*)/gm;
+
+ let markers = [];
+
+ let matches;
+
+ while((matches = compilerRegex.exec(data)) !== null)
+ {
+ let severity = monaco.MarkerSeverity.Error;
+
+ if(matches[3] == "warning")
+ severity = monaco.MarkerSeverity.Warning;
+
+ if(matches[3] == "note")
+ severity = monaco.MarkerSeverity.Info;
+
+ markers.push({
+ message: matches[4],
+ severity: severity,
+ startLineNumber: parseInt(matches[1]),
+ startColumn: parseInt(matches[2]),
+ endLineNumber: parseInt(matches[1]),
+ endColumn: this.monacoModel.getLineLength(parseInt(matches[1])),
+ source: "Emscripten Compiler",
+ });
+ }
+
+ while((matches = linkerRegex.exec(data)) !== null)
+ {
+ markers.push({
+ message: `${matches[1]} ${matches[2]}`,
+ severity: monaco.MarkerSeverity.Error,
+ startLineNumber: 1,
+ startColumn: 1,
+ endLineNumber: 1,
+ endColumn: this.monacoModel.getLineLength(1),
+ source: "Emscripten Linker",
+ });
+ }
+
+ // show errors in the editor, if they exist
+ if(markers.length > 0)
+ {
+ this.setMarkers(markers);
+ }
+ }
+
+ getValue()
+ {
+ return this.monacoEditor.getValue();
+ }
+ setValue(value)
+ {
+ this.monacoModel.setValue(value);
+
+ }
+ onInit()
+ {
+ if(this.monacoModel === null)
+ {
+ this.monacoModel = monaco.editor.createModel("", "cpp", monaco.Uri.parse("inmemory://pgetinker.cpp"));
+
+ let codeBox = document.querySelector("#code");
+ if(codeBox.value !== "")
+ {
+ this.monacoModel.setValue(document.querySelector("#code").value);
+ window.localStorage.setItem("pgetinkerCode", JSON.stringify(document.querySelector("#code").value));
+ }
+ else
+ {
+ let code = window.localStorage.getItem("pgetinkerCode");
+ code = (code !== null) ? JSON.parse(code) : "";
+
+ if(code === "")
+ {
+ axios.get("/api/default-code").then((response) =>
+ {
+ this.monacoModel.setValue(response.data.code);
+ }).catch((reason) => console.log(reason));
+ }
+ else
+ {
+ this.monacoModel.setValue(code);
+ }
+ }
+ }
+
+ if(this.monacoModelIntellisense === null)
+ {
+ this.monacoModelIntellisense = monaco.editor.createModel("", "cpp", monaco.Uri.parse("inmemory://pgetinker.h"));
+ axios.get("/api/model/v0.02").then((response) =>
+ {
+ this.monacoModelIntellisense.setValue(response.data);
+ });
+ }
+
+ this.monacoEditor = monaco.editor.create(document.querySelector('#editor-panel .code-editor'), {
+ automaticLayout: true,
+ model: this.monacoModel,
+ fontSize: 14,
+ mouseWheelZoom: true,
+ theme: `vs-${this.state.theme}`,
+ });
+
+ this.monacoEditor.addAction({
+ id: 'build-and-run',
+ label: 'Build and Run',
+ keybindings: [
+ monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
+ monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS
+ ],
+ run: () =>
+ {
+ document.querySelector("#start-stop").dispatchEvent(new Event("click"));
+ }
+ });
+
+ this.monacoEditor.addAction({
+ id: 'reset-editor-zoom',
+ label: 'Reset Editor Zoom',
+ keybindings: [
+ monaco.KeyMod.CtrlCmd | monaco.KeyCode.Digit0,
+ ],
+ run: () =>
+ {
+ this.monacoEditor.trigger("", "editor.action.fontZoomReset");
+ }
+ })
+
+ this.monacoEditor.onDidChangeCursorPosition(() => this.updateStatusBar());
+
+ this.monacoEditor.onDidChangeModelContent(() =>
+ {
+ window.localStorage.setItem("pgetinkerCode", JSON.stringify(this.monacoEditor.getValue()));
+
+ if(this.sharedFlag)
+ {
+ window.history.replaceState({}, "", "/");
+ }
+ });
+
+ this.updateStatusBar();
+ }
+
+ register()
+ {
+ this.state.layout.registerComponent('editorComponent', function(container)
+ {
+ container.getElement().html(`
+
+ `);
+ });
+ }
+
+ reveal(position)
+ {
+ this.monacoEditor.revealPositionInCenter(position);
+ }
+
+ setMarkers(markers)
+ {
+ // set model markers
+ monaco.editor.setModelMarkers(this.monacoModel, "owner", markers);
+ // move to first marker
+ this.monacoEditor.setPosition({lineNumber: markers[0].startLineNumber, column: markers[0].startColumn });
+ // trigger activate nearest marker
+ setTimeout(() => { this.monacoEditor.trigger("", "editor.action.marker.next"); }, 50);
+ }
+
+ setTheme(theme)
+ {
+ if(this.monacoEditor !== null)
+ this.monacoEditor.updateOptions({ theme: `vs-${theme}`});
+ }
+
+ updateStatusBar()
+ {
+ let statusBar = document.querySelector("#editor-panel .status");
+
+ let cursor = `Ln ${this.monacoEditor.getPosition().lineNumber}, Col ${this.monacoEditor.getPosition().column}`;
+ let fileSize = `${new Intl.NumberFormat().format(this.monacoEditor.getValue().length)} / ${new Intl.NumberFormat().format(this.maxFileSize)}`;
+
+ statusBar.classList.toggle('too-fucking-big', false);
+ if(this.monacoModel.getValueLength() > this.maxFileSize)
+ {
+ statusBar.classList.toggle('too-fucking-big', true);
+ fileSize += " EXCEEDING MAXIMUM!";
+ }
+
+ statusBar.innerHTML = `
+
+ Bytes: ${fileSize}
+
+
+ ${cursor}
+
+ `;
+ }
+
+}
\ No newline at end of file
diff --git a/resources/js/components/InfoPanel.js b/resources/js/components/InfoPanel.js
new file mode 100644
index 0000000..599a613
--- /dev/null
+++ b/resources/js/components/InfoPanel.js
@@ -0,0 +1,61 @@
+
+export default class InfoPanel
+{
+ state;
+
+ informationPanelExist = false;
+
+ constructor(state)
+ {
+ this.state = state;
+ console.log("Info panel", "constructor");
+ }
+
+
+
+ clear()
+ {
+ document.querySelector("#info-panel").innerHTML = "";
+ }
+
+ exists()
+ {
+ return this.informationPanelExist;
+ }
+
+ focus()
+ {
+ if(this.exists())
+ {
+ let infoPanel = this.state.layout.root.getItemsById('info')[0];
+ if(infoPanel.parent.isStack)
+ {
+ infoPanel.parent.setActiveContentItem(infoPanel);
+ }
+ }
+ }
+
+ onInit()
+ {
+ this.informationPanelExist = (this.state.layout.root.getItemsById('info').length > 0);
+ }
+
+ register()
+ {
+ this.state.layout.registerComponent('infoComponent', function(container)
+ {
+ container.getElement().html(`
+
+
+ `);
+ });
+ }
+
+ setContent(content)
+ {
+ let infoPanel = document.querySelector("#info-panel");
+ infoPanel.innerHTML = `${content}
`;
+ infoPanel.scrollTop = infoPanel.scrollHeight;
+ }
+}
+
diff --git a/resources/js/components/PlayerPanel.js b/resources/js/components/PlayerPanel.js
new file mode 100644
index 0000000..b8a97fd
--- /dev/null
+++ b/resources/js/components/PlayerPanel.js
@@ -0,0 +1,149 @@
+
+export default class PlayerPanel
+{
+ state;
+ lastPlayerHtml = "";
+ running = false;
+
+ constructor(state)
+ {
+ this.state = state;
+ console.log("Player panel", "constructor");
+ window.addEventListener("message", (event) =>
+ {
+ if(typeof event.data !== "object")
+ return;
+
+ if(typeof event.data.message !== "string")
+ return;
+
+ if(event.data.message === "player-ready")
+ {
+ // update player theme
+ document.querySelector("#player-panel iframe").contentWindow.postMessage({
+ message: "set-theme",
+ theme: this.state.theme
+ }, "*");
+
+ // update player theme
+ document.querySelector("#player-panel iframe").contentWindow.postMessage({
+ message: "show-console",
+ value: this.state.consolePanel.shown()
+ }, "*");
+ }
+
+ });
+ }
+
+ getHtml()
+ {
+ return this.lastPlayerHtml;
+ }
+
+ isRunning()
+ {
+ return this.running;
+ }
+
+ onInit()
+ {
+ if(this.lastPlayerHtml != "")
+ {
+ let playerFrame = document.createElement('iframe');
+ playerFrame.setAttribute("srcdoc", this.lastPlayerHtml);
+ playerFrame.setAttribute("sandbox", "allow-scripts");
+ document.querySelector("#player-panel .iframe-container").append(playerFrame);
+
+ playerFrame.classList.toggle("display-block", true);
+ document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
+ document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
+ }
+ }
+
+ register()
+ {
+ this.state.layout.registerComponent('playerComponent', function(container)
+ {
+ container.getElement().html(`
+
+
+
+
+
+
+
+ Compile Failed.
+
+
+
+ `);
+ });
+ }
+
+ setCompiling()
+ {
+ this.lastPlayerHtml = "";
+ this.stop();
+
+ document.querySelector("#player-panel .compiling").classList.toggle("display-flex", true);
+ document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
+ }
+
+ setCompilingFailed()
+ {
+ document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
+ document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", true);
+ }
+
+ setHtml(html)
+ {
+ this.lastPlayerHtml = html;
+ this.start();
+ }
+
+ setTheme(theme)
+ {
+ let iframe = document.querySelector("#player-panel iframe");
+ if(iframe != null)
+ {
+ iframe.contentWindow.postMessage({
+ message: "set-theme",
+ theme: theme
+ }, "*");
+ }
+ }
+
+ start()
+ {
+ let playerFrame = document.createElement('iframe');
+ playerFrame.setAttribute("srcdoc", this.lastPlayerHtml);
+ playerFrame.setAttribute("sandbox", "allow-scripts");
+ document.querySelector("#player-panel .iframe-container").append(playerFrame);
+
+ playerFrame.classList.toggle("display-block", true);
+ document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
+ document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
+
+ this.running = true;
+ }
+
+ stop()
+ {
+ let playerFrame = document.querySelector("#player-panel iframe");
+
+ if(playerFrame != null)
+ playerFrame.remove();
+
+ this.running = false;
+ }
+}
diff --git a/resources/js/lib/bootstrap.js b/resources/js/lib/bootstrap.js
index 6c064d7..cb7fff8 100644
--- a/resources/js/lib/bootstrap.js
+++ b/resources/js/lib/bootstrap.js
@@ -6,11 +6,6 @@ window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.axios.defaults.withCredentials = true;
window.axios.defaults.withXSRFToken = true;
-if(window.location.pathname.indexOf("/beta/") == 0)
-{
- window.axios.defaults.baseURL = `${window.location.protocol}//${window.location.host}/staging/`;
-}
-
if(window.location.pathname.indexOf("/staging/") == 0)
{
window.axios.defaults.baseURL = `${window.location.protocol}//${window.location.host}/staging/`;
diff --git a/resources/js/lib/lucide.js b/resources/js/lib/lucide.js
index 37970de..a0a6820 100644
--- a/resources/js/lib/lucide.js
+++ b/resources/js/lib/lucide.js
@@ -4,6 +4,7 @@ import {
Bug,
CircleDollarSign,
CirclePlay,
+ CircleStop,
Download,
ExternalLink,
Github,
@@ -20,6 +21,7 @@ createIcons({
BadgePlus,
Bug,
CirclePlay,
+ CircleStop,
CircleDollarSign,
Download,
ExternalLink,
diff --git a/resources/js/lib/monaco.js b/resources/js/lib/monaco.js
deleted file mode 100644
index 110561c..0000000
--- a/resources/js/lib/monaco.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import * as monaco from 'monaco-editor';
-
-import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
-import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
-import htmlWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
-import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
-import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
-
-self.MonacoEnvironment = {
- getWorker: function (workerId, label)
- {
- switch (label) {
- case 'json':
- return new jsonWorker();
- case 'css':
- case 'scss':
- case 'less':
- return cssWorker();
- case 'html':
- case 'handlebars':
- case 'razor':
- return htmlWorker();
- case 'typescript':
- case 'javascript':
- return tsWorker();
- default:
- return editorWorker();
- }
- }
-};
-monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true);
-monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
- noLib: true,
- allowNonTsExtensions: true
-});
-
-window.monaco = monaco;
diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php
index 5ab379d..d4e7903 100644
--- a/resources/views/home.blade.php
+++ b/resources/views/home.blade.php
@@ -63,9 +63,10 @@
-