Skip to content

Commit

Permalink
Add snippets with import/export
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfgangmeyers committed Apr 10, 2022
1 parent a84e4a3 commit 2e45a7b
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 11 deletions.
68 changes: 57 additions & 11 deletions app/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
<title>Roblox Block Coding Prototype</title>
<link rel="stylesheet" href="./dark-filetree/style.css" />
<link rel="stylesheet" href="./jquery.simplepopup.css" />
<link rel="stylesheet" href="./bootstrap.min.css" />
<script src="./jquery-3.5.1.min.js"></script>
<script src="./bootstrap.min.js"></script>
<script src="./jstree.min.js"></script>
<script src="./blockly_compressed.js"></script>
<script src="./blocks_compressed.js"></script>
Expand All @@ -17,7 +19,7 @@
<style>
body {
background-color: #181818;
color: #fff;
color: #fff !important;
font-family: sans-serif;
}

Expand All @@ -26,6 +28,10 @@
font-size: 140%;
}

hr {
border: 1px solid white;
}

button,
.btn {
background-color: #2B4764;
Expand Down Expand Up @@ -56,19 +62,20 @@

<body>
<img src="bloxcode-logo-truncated.png"
style="margin-bottom: 30px; display: block; margin-left: auto; margin-right: auto" />
style="margin-bottom: 30px; margin-top: 10px; display: block; margin-left: auto; margin-right: auto" />

<div id="controls" style="display: none">
<button id="save-script-button" style="display: none" onclick="saveScript()">Save</button>
<button id="delete-script-button" style="display: none" onclick="deleteScript()">Delete</button>
<button id="new-local-script-button" onclick="newLocalScript()" style="display: none">New LocalScript</button>
<button id="new-script-button" onclick="newScript()" style="display: none">New Script</button>

<button onclick="refresh()">Refresh</button>
<button onclick="showCode()">Print Lua Code</button>
<a target="_blank" class="btn" href="https://github.com/wolfgangmeyers/roblox-blockly">
<button>Documentation</button>
<button class="btn btn-sm" id="save-script-button" style="display: none" onclick="saveScript()">Save</button>
<button class="btn btn-sm" id="delete-script-button" style="display: none" onclick="deleteScript()">Delete</button>
<button class="btn btn-sm" id="new-local-script-button" onclick="newLocalScript()" style="display: none">New LocalScript</button>
<button class="btn btn-sm" id="new-script-button" onclick="newScript()" style="display: none">New Script</button>

<button class="btn btn-sm" onclick="refresh()">Refresh</button>
<button class="btn btn-sm" onclick="showCode()">Print Lua Code</button>
<a target="_blank" class="btn btn-sm" href="https://github.com/wolfgangmeyers/roblox-blockly">
Documentation
</a>
<button class="btn btn-sm" onclick="snippets()">Snippets</button>
</div>
<hr />

Expand All @@ -77,6 +84,38 @@
</div>
<div id="blocklyDiv" style="left: 200px; height: 480px; width: 600px;"></div>

<!-- bootstrap Modal window with title Snippets -->
<div class="modal fade" id="snippetsModal" tabindex="-1" role="dialog" aria-labelledby="snippetsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="snippetsModalLabel">Snippets</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
Snippet: <select id="snippetSelector">
<option value="">Select a snippet</option>
</select>
<hr />
<div id="snippetDisplay" style="height: 300px"></div>
</div>
<div class="modal-footer">
<!-- bootstrap styled file input for import -->
<div class="custom-file">
<input type="file" class="custom-file-input" id="snippetFile" accept=".blox">
<label class="custom-file-label" for="snippetFile">Import Snippet</label>
</div>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button id="btnInsertSnippet" type="button" class="btn btn-primary" onclick="insertSnippet()" disabled>Insert</button>
<button id="btnDeleteSnippet" type="button" class="btn btn-primary" onclick="deleteSnippet()" disabled>Delete</button>
<button id="btnExportSnippet" type="button" class="btn btn-primary" onclick="exportSnippet()" disabled>Export</button>
</div>
</div>
</div>
</div>

<script type="text/javascript">
const onResize = () => {
const div = document.getElementById("blocklyDiv")
Expand Down Expand Up @@ -108,6 +147,12 @@
},
});
demoWorkspace.setTheme(theme)

// snippetDisplay
var snippetWorkspace = Blockly.inject('snippetDisplay', {
media: '../../media/',
});
snippetWorkspace.setTheme(theme)
</script>
<script src="blocks.js"></script>
<script src="sync.js"></script>
Expand All @@ -126,6 +171,7 @@
})
}(jQuery))
</script>

</body>

</html>
128 changes: 128 additions & 0 deletions app/public/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,134 @@ Blockly.prompt = function(message, defaultValue, callback) {
});
};

// BEGIN code for snippets

function snippets() {
$("#btnInsertSnippet").attr("disabled", true);
$("#btnDeleteSnippet").attr("disabled", true);
$("#btnExportSnippet").attr("disabled", true);
// refresh snippet list
const savedSnippets = JSON.parse(localStorage.getItem("bloxcode_snippets") || "[]");
let selectorHtml = `<option value="">Select a snippet</option>\n`;
for (let snippet of savedSnippets) {
selectorHtml += `<option value="${snippet.id}">${snippet.name}</option>\n`;
}
$("#snippetSelector").html(selectorHtml);
// clear snippetWorkspace
snippetWorkspace.clear();

$("#snippetsModal").modal("show")
// Seems like this is needed to make the workspace display properly
$("#snippetDisplay").find("svg").css("width", "100%")
$("#snippetDisplay").find("svg").css("height", "100%")
// saveFile();
}

function deleteSnippet() {
const selectedSnippet = $("#snippetSelector").val();
const savedSnippets = JSON.parse(localStorage.getItem("bloxcode_snippets") || "[]");
const newSnippets = savedSnippets.filter(snippet => snippet.id !== selectedSnippet);
localStorage.setItem("bloxcode_snippets", JSON.stringify(newSnippets));
snippets();
}

function insertSnippet() {
const selectedSnippet = $("#snippetSelector").val();
const savedSnippets = JSON.parse(localStorage.getItem("bloxcode_snippets") || "[]");
const selectedSnippetObject = savedSnippets.find(snippet => snippet.id === selectedSnippet);
if (selectedSnippetObject) {
const blocks = Blockly.serialization.blocks.append(selectedSnippetObject.source, demoWorkspace);
}
}

$("#snippetSelector").on("change", function() {
const selectedSnippet = $(this).val();
$("#btnInsertSnippet").attr("disabled", !selectedSnippet);
$("#btnDeleteSnippet").attr("disabled", !selectedSnippet);
$("#btnExportSnippet").attr("disabled", !selectedSnippet);
if (selectedSnippet) {
const savedSnippets = JSON.parse(localStorage.getItem("bloxcode_snippets") || "[]");
const snippet = savedSnippets.find(snippet => snippet.id === selectedSnippet);
if (snippet) {
// clear snippetWorkspace
snippetWorkspace.clear();
// load snippet
Blockly.serialization.blocks.append(snippet.source, snippetWorkspace);
}
}
});

// register save as snippet option
Blockly.ContextMenuRegistry.registry.register({
displayText: function() {
return "Save as snippet";
},
preconditionFn: function(scope) {
const block = scope.block;
if (!block.isInFlyout && block.isDeletable() && block.isMovable()) {
return 'enabled';
}
return 'hidden';
},
callback: function(scope) {
if (scope.block) {
// prompt user for name
var promptBox = simplePopup(2, 'Enter a name for the new snippet');
$.when(promptBox).then(function(name) {
if (name) {
// save snippet
const id = Blockly.utils.idGenerator.getNextUniqueId();
const savedSnippets = JSON.parse(localStorage.getItem("bloxcode_snippets") || "[]");
const newSnippet = {
id: id,
name: name,
source: Blockly.serialization.blocks.save(scope.block),
};
savedSnippets.push(newSnippet);
localStorage.setItem("bloxcode_snippets", JSON.stringify(savedSnippets));
}
});
}
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockSaveAsSnippet',
weight: 1,
});

$("#snippetFile").on("change", function() {
const file = $(this).prop("files")[0];
const reader = new FileReader();
reader.onload = function(e) {
const data = e.target.result;
const savedSnippets = JSON.parse(localStorage.getItem("bloxcode_snippets") || "[]");
const importedSnippet = JSON.parse(data);
savedSnippets.push({
...importedSnippet,
id: Blockly.utils.idGenerator.getNextUniqueId(),
});
localStorage.setItem("bloxcode_snippets", JSON.stringify(savedSnippets));
snippets();
}
reader.readAsText(file);
});

// https://stackoverflow.com/questions/32979630/how-can-i-display-a-save-as-dialog-in-an-electron-app
function exportSnippet() {
const selectedSnippet = $("#snippetSelector").val();
const savedSnippets = JSON.parse(localStorage.getItem("bloxcode_snippets") || "[]");
const selectedSnippetObject = savedSnippets.find(snippet => snippet.id === selectedSnippet);
if (selectedSnippetObject) {
const content = JSON.stringify(selectedSnippetObject);
const element = document.createElement("a");
const file = new Blob([content], {type: "text/plain"});
element.href = URL.createObjectURL(file);
element.download = `${selectedSnippetObject.name}.blox`;
element.click();
}
}

// END code for snippets

async function init() {

document.getElementById("controls").style = "display: block";
Expand Down

0 comments on commit 2e45a7b

Please sign in to comment.