Skip to content

Commit

Permalink
Editor: Save single snippet and alert to changes (#174)
Browse files Browse the repository at this point in the history
* Improved `console` visibility on Light theme

* Added new option to export a single snippet and alert for large URL

* Added flag to control changes and warn user if closing the tab or selecting other code snippet

* Fixed static analysis issues

* Fixed code formatting
  • Loading branch information
lvcabral authored Feb 1, 2025
1 parent 0f33fac commit 762e33d
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 27 deletions.
12 changes: 11 additions & 1 deletion src/app/brightscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,17 @@ export function defineMode(CodeMirror) {
"stop",
"throw",
];
const endControl = ["next", "endif", "end if", "endfor", "end for", "endwhile", "end while", "endtry", "end try"];
const endControl = [
"next",
"endif",
"end if",
"endfor",
"end for",
"endwhile",
"end while",
"endtry",
"end try",
];
const wordOperators = wordRegexp(["and", "or", "not", "mod"]);
const commonkeywords = ["dim", "print", "goto", "library"];
const commontypes = [
Expand Down
9 changes: 8 additions & 1 deletion src/app/css/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
--header-color: #333333;
--header-background-color: #cccccc;
--help-color: #5d5d5d;
--console-color: #333333;
--console-color: #053369;
--console-background-color: #eeeeee;
--console-selection-color: #aaaaaa;
--item-background-color: #094771;
Expand Down Expand Up @@ -272,6 +272,8 @@ pre {
.dropdown li {
margin-top: 3px;
margin-bottom: 3px;
width: 100%;
white-space: nowrap;
}

.dropdown li a {
Expand All @@ -281,6 +283,7 @@ pre {
color: var(--control-color);
padding: 3px;
width: 100%;
box-sizing: border-box;
}

.dropdown li a i {
Expand Down Expand Up @@ -371,6 +374,10 @@ pre {
color: var(--console-color) !important;
}

.VanillaTerm output span {
color: var(--console-color) !important;
}

.VanillaTerm .command {
align-items: center;
}
Expand Down
9 changes: 5 additions & 4 deletions src/app/editor.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
<li><a href="#" id="saveas-option"><i class="icon-save"></i> Save as</a></li>
<li><a href="#" id="delete-option"><i class="icon-trash"></i> Delete</a></li>
<li><a href="#" id="export-option"><i class="icon-upload"></i> Export</a></li>
<li><a href="#" id="export-all-option"><i class="icon-upload"></i> Export All</a></li>
<li><a href="#" id="import-option"><i class="icon-download"></i> Import</a></li>
</ul>
</div>
Expand Down Expand Up @@ -111,12 +112,12 @@
<input type="hidden" id="actionType" value="save" />
</form>
</dialog>
<dialog id="delete-dialog" class="modal_dialog" closed>
<p class="modal_dialog_content">Do you really want to delete this code snippet?</p>
<dialog id="confirm-dialog" class="modal_dialog" closed>
<p id="dialog-text" class="modal_dialog_content"></p>
<br />
<form method="dialog">
<button value="ok">Yes</button>
<button value="cancel" class="modal_dialog_cancel">No</button>
<button id="confirm-button" value="ok">Yes</button>
<button id="cancel-button" value="cancel" class="modal_dialog_cancel">No</button>
</form>
</dialog>
</main>
Expand Down
162 changes: 141 additions & 21 deletions src/app/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ const codeSelect = document.getElementById("code-selector");
const codeDialog = document.getElementById("code-dialog");
const actionType = document.getElementById("actionType");
const codeForm = document.getElementById("code-form");
const deleteDialog = document.getElementById("delete-dialog");
const confirmDialog = document.getElementById("confirm-dialog");
const dialogText = document.getElementById("dialog-text");
const confirmButton = document.getElementById("confirm-button");
const cancelButton = document.getElementById("cancel-button");
const moreButton = document.getElementById("more-options");
const dropdown = document.getElementById("more-options-dropdown");

Expand Down Expand Up @@ -91,12 +94,14 @@ document.getElementById("rename-option").addEventListener("click", renameCode);
document.getElementById("saveas-option").addEventListener("click", saveAsCode);
document.getElementById("delete-option").addEventListener("click", deleteCode);
document.getElementById("export-option").addEventListener("click", exportCode);
document.getElementById("export-all-option")?.addEventListener("click", exportAllCode);
document.getElementById("import-option").addEventListener("click", importCode);

let consoleLogsContainer = document.getElementById("console-logs");
let isResizing = false;
let editorManager;
let currentId = nanoid(10);
let isCodeChanged = false;

function main() {
updateButtons();
Expand All @@ -110,6 +115,16 @@ function main() {
const cm = document.querySelector(".CodeMirror");
delete cm.CodeMirror.constructor.keyMap.emacsy["Ctrl-V"];
}
editorManager.editor.on("change", () => {
if (codeSelect.value === "0") {
const code = editorManager.editor.getValue();
if (code && code.trim() === "") {
isCodeChanged = false;
return;
}
}
markCodeAsChanged();
});
hideEditor(!(currentApp.title === undefined || currentApp.title.endsWith("editor_code.brs")));
populateCodeSelector();
// Subscribe to Engine events and initialize Console
Expand All @@ -132,6 +147,7 @@ function main() {
endButton.style.display = "inline";
breakButton.style.display = "inline";
}
editorManager.editor.focus();
}

function updateButtons() {
Expand Down Expand Up @@ -212,6 +228,31 @@ function scrollToBottom() {
}
}

// Code Events

function markCodeAsChanged() {
isCodeChanged = true;
updateCodeSelector();
}

function markCodeAsSaved() {
isCodeChanged = false;
updateCodeSelector();
}

function updateCodeSelector() {
const options = Array.from(codeSelect.options);
for (const option of options) {
if (option.value === currentId) {
if (isCodeChanged) {
option.text = `⏺︎ ${option.text.replace(/^ /, "")}`;
} else {
option.text = option.text.replace(/^ /, "");
}
}
}
}

function populateCodeSelector(currentId = "") {
const arrCode = new Array();
for (let i = 0; i < localStorage.length; i++) {
Expand All @@ -235,9 +276,44 @@ function populateCodeSelector(currentId = "") {
const selected = codeId === currentId;
codeSelect.options[i + 1] = new Option(arrCode[i][0], codeId, false, selected);
}
updateCodeSelector();
}

let savedValue = codeSelect.value;
codeSelect.addEventListener("mousedown", async (e) => {
savedValue = codeSelect.value;
});

function showDialog(message) {
return new Promise((resolve) => {
if (message) {
dialogText.innerText = message;
}
confirmDialog.showModal();

confirmButton.onclick = () => {
confirmDialog.close();
resolve(true);
};

cancelButton.onclick = () => {
confirmDialog.close();
resolve(false);
};
});
}

codeSelect.addEventListener("change", (e) => {
codeSelect.addEventListener("change", async (e) => {
if (isCodeChanged) {
const confirmed = await showDialog(
"There are unsaved changes, do you want to discard and continue?"
);
if (!confirmed) {
e.preventDefault();
codeSelect.value = savedValue;
return;
}
}
if (codeSelect.value !== "0") {
loadCode(codeSelect.value);
} else {
Expand All @@ -254,6 +330,7 @@ function loadCode(id) {
code = code.substring(code.indexOf("=@") + 2);
}
resetApp(id, code);
markCodeAsSaved();
} else {
showToast("Could not find the code in the Local Storage!", 3000, true);
}
Expand All @@ -262,7 +339,8 @@ function loadCode(id) {
function renameCode() {
if (currentId && localStorage.getItem(currentId)) {
actionType.value = "rename";
codeForm.codeName.value = codeSelect.options[codeSelect.selectedIndex].text;
const codeName = codeSelect.options[codeSelect.selectedIndex].text;
codeForm.codeName.value = codeName.replace(/^ /, "");
codeDialog.showModal();
} else {
showToast("There is no code snippet selected to rename!", 3000, true);
Expand All @@ -272,22 +350,57 @@ function renameCode() {
function saveAsCode() {
if (currentId && localStorage.getItem(currentId)) {
actionType.value = "saveas";
codeForm.codeName.value = codeSelect.options[codeSelect.selectedIndex].text + " (Copy)";
const codeName = codeSelect.options[codeSelect.selectedIndex].text + " (Copy)";
codeForm.codeName.value = codeName.replace(/^ /, "");
codeDialog.showModal();
} else {
showToast("There is no code snippet selected to save as!", 3000, true);
}
}

function deleteCode() {
async function deleteCode() {
if (currentId && localStorage.getItem(currentId)) {
deleteDialog.showModal();
const confirmed = await showDialog("Are you sure you want to delete this code?");
if (confirmed) {
localStorage.removeItem(currentId);
currentId = nanoid(10);
resetApp();
showToast("Code deleted from the browser local storage!", 3000);
}
} else {
showToast("There is no code snippet selected to delete!", 3000, true);
}
}

function exportCode() {
const codes = {};
let codeContent = editorManager.editor.getValue();
if (codeContent && codeContent.trim() !== "") {
if (codeSelect.value !== "0") {
let codeName = codeSelect.options[codeSelect.selectedIndex].text;
codes[currentId] = { name: codeName, content: codeContent };
const safeFileName = codeName
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/^ /, "")
.replace(/[^a-z0-9-]/g, "");
const json = JSON.stringify(codes, null, 2);
const blob = new Blob([json], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${safeFileName}.json`;
a.click();
URL.revokeObjectURL(url);
} else {
showToast("Please save your Code Snipped before exporting!", 3000, true);
return;
}
} else {
showToast("There is no Code Snippet to Export", 3000, true);
}
}

function exportAllCode() {
const codes = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
Expand Down Expand Up @@ -324,23 +437,19 @@ function importCode() {
localStorage.setItem(id, value);
}
populateCodeSelector(currentId);
showToast("Code snippets imported to the simulator local storage!", 3000);
if (Object.keys(codes).length === 1) {
showToast("Code snippet imported to the simulator local storage!", 3000);
const loadId = Object.keys(codes)[0];
loadCode(loadId);
} else {
showToast("Code snippets imported to the simulator local storage!", 3000);
}
};
reader.readAsText(file);
};
input.click();
}

deleteDialog.addEventListener("close", (e) => {
if (deleteDialog.returnValue === "ok") {
localStorage.removeItem(currentId);
currentId = nanoid(10);
resetApp();
showToast("Code deleted from the simulator local storage.", 3000);
}
deleteDialog.returnValue = "";
});

function resetApp(id = "", code = "") {
populateCodeSelector(id);
if (currentApp.running) {
Expand All @@ -349,13 +458,14 @@ function resetApp(id = "", code = "") {
}
editorManager.editor.setValue(code);
editorManager.editor.focus();
markCodeAsSaved();
}

function shareCode() {
let code = editorManager.editor.getValue();
if (code && code.trim() !== "") {
if (codeSelect.value !== "0") {
let codeName = codeSelect.options[codeSelect.selectedIndex].text;
let codeName = codeSelect.options[codeSelect.selectedIndex].text.replace(/^ /, "");
code = `@=${codeName}=@${code}`;
}
const data = {
Expand All @@ -364,7 +474,15 @@ function shareCode() {
};
getShareUrl(data).then(function (shareLink) {
navigator.clipboard.writeText(shareLink);
showToast("brsFiddle.net share URL copied to clipboard.");
if (shareLink.length > 2048) {
showToast(
"URL copied to clipboard, but it's longer than 2048 bytes, consider exporting as a file instead!",
7000,
true
);
} else {
showToast("brsFiddle.net share URL copied to clipboard.");
}
});
} else {
showToast("There is no Source Code to share!", 3000, true);
Expand All @@ -378,12 +496,13 @@ function saveCode() {
actionType.value = "save";
codeDialog.showModal();
} else {
const codeName = codeSelect.options[codeSelect.selectedIndex].text;
const codeName = codeSelect.options[codeSelect.selectedIndex].text.replace(/^ /, "");
localStorage.setItem(currentId, `@=${codeName}=@${code}`);
showToast(
"Code saved in the simulator local storage.\nTo share it use the Share button.",
5000
);
markCodeAsSaved();
}
} else {
showToast("There is no Source Code to save", 3000, true);
Expand Down Expand Up @@ -417,6 +536,7 @@ codeDialog.addEventListener("close", (e) => {
5000
);
}
markCodeAsSaved();
}
resetDialog();
});
Expand Down

0 comments on commit 762e33d

Please sign in to comment.