From 0bcbf6280acca18ac092257a638e141935ec9600 Mon Sep 17 00:00:00 2001 From: Jon Manning Date: Sun, 8 Dec 2024 01:02:19 +1100 Subject: [PATCH] Create embeddable version of UI for docs site --- scss/yarnspinner.scss | 19 ++++ src/embed.html | 205 +++++++++++++++++++++++++++++++++++ src/playground.ts | 161 ++++++++++++++------------- src/staticwebapp.config.json | 10 +- webpack.config.js | 58 +++++----- 5 files changed, 353 insertions(+), 100 deletions(-) create mode 100644 src/embed.html diff --git a/scss/yarnspinner.scss b/scss/yarnspinner.scss index 38089bd..db3d9f8 100644 --- a/scss/yarnspinner.scss +++ b/scss/yarnspinner.scss @@ -231,3 +231,22 @@ body { .btn:hover { color: white; } + +#mini-app-header { + background-color: $background-tint; + + border-bottom-style: solid; + border-color: $darker-green; + border-width: 2px; +} + +#mini-app-header #title-link { + font-family: "Space Mono"; + font-weight: bold; + text-decoration: none; + color: var(--bs-heading-color); +} + +#mini-app-header button { + +} \ No newline at end of file diff --git a/src/embed.html b/src/embed.html new file mode 100644 index 0000000..44cc9e8 --- /dev/null +++ b/src/embed.html @@ -0,0 +1,205 @@ + + + + + + Try Yarn Spinner - Browser Based Game Dialogue Editor + + + + + + + + + + + + + +
+
+
+
+ + Try Yarn Spinner +
+
+ +
+
+
+ +
+
+
+ Loading... +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ Click Run to play your conversation! +
+
+
+
+
+
+
+
+ + + + + + +
VariableValue
+
+
+
+
+
+
+ +
+ + diff --git a/src/playground.ts b/src/playground.ts index 7b24f2f..e114b5d 100644 --- a/src/playground.ts +++ b/src/playground.ts @@ -149,10 +149,13 @@ export async function load(script: string) { let runtimeInfo = await yarnspinner.init(variableStorage); - document.getElementById("yarn-spinner-version-value").innerText = - `v${runtimeInfo.version} (${runtimeInfo.gitHash})`; + const versionElement = document.getElementById("yarn-spinner-version-value"); + if (versionElement) { + document.getElementById("yarn-spinner-version-value").innerText = + `v${runtimeInfo.version} (${runtimeInfo.gitHash})`; - document.getElementById("yarn-spinner-version").classList.remove("d-none"); + versionElement.classList.remove("d-none"); + } dialogue = yarnspinner.create(); @@ -290,88 +293,95 @@ export async function load(script: string) { console.error(error.message); }; - document.getElementById("button-test").addEventListener("click", async () => { - if (errorsExist) { - return; - } + const playButton = document.getElementById("button-test"); - clearLog(); - hideVariableStorageDisplay(); - - variableStorage.clear(); - - shouldShowJumpLine = false; - - // Get the text out of the editor and compile it - var source = editor.getModel().getValue(); - var compilation = await dialogue.compileSource(source); - - // Display any diagnostics we have - for (let diagnostic of compilation.diagnostics) { - let displayPosition = `Line ${diagnostic.range.start.line + 1}`; - let message = `${displayPosition}: ${diagnostic.message}`; - - let cssClasses: string[]; - - switch (diagnostic.severity) { - case yarnspinner.DiagnosticSeverity.Error: - cssClasses = ["list-group-item-danger", "error"]; - break; - case yarnspinner.DiagnosticSeverity.Warning: - cssClasses = ["list-group-item-warning", "warning"]; - break; - case yarnspinner.DiagnosticSeverity.Info: - cssClasses = []; - default: - console.warn( - `Unknown diagnostic severity type ${diagnostic.severity}`, - ); - break; + if (playButton) { + playButton.addEventListener("click", async () => { + if (errorsExist) { + return; } - addLogText(message, ...cssClasses); - } - if (compilation.compiled) { - let nodeToRun; + clearLog(); + hideVariableStorageDisplay(); - // If we have a node name Start, start from that - nodeToRun = compilation.nodes.find((n) => n.name === "Start"); + variableStorage.clear(); - if (!nodeToRun) { - // Otherwise, use the first node present that is not internal or part of - // a node group. + shouldShowJumpLine = false; - nodeToRun = compilation.nodes.find((n) => { - // Is the node title prefixed with a tag that indicates that it's internal? - if (n.name.startsWith("$Yarn.Internal")) { - return false; - } + // Get the text out of the editor and compile it + var source = editor.getModel().getValue(); + var compilation = await dialogue.compileSource(source); + + // Display any diagnostics we have + for (let diagnostic of compilation.diagnostics) { + let displayPosition = `Line ${diagnostic.range.start.line + 1}`; + let message = `${displayPosition}: ${diagnostic.message}`; + + let cssClasses: string[]; + + switch (diagnostic.severity) { + case yarnspinner.DiagnosticSeverity.Error: + cssClasses = ["list-group-item-danger", "error"]; + break; + case yarnspinner.DiagnosticSeverity.Warning: + cssClasses = ["list-group-item-warning", "warning"]; + break; + case yarnspinner.DiagnosticSeverity.Info: + cssClasses = []; + default: + console.warn( + `Unknown diagnostic severity type ${diagnostic.severity}`, + ); + break; + } + addLogText(message, ...cssClasses); + } - // Does the node have a header that indicates it's part of a node group? - if (n.headers.find((h) => h.key == "$Yarn.Internal.NodeGroup")) { - return false; - } + if (compilation.compiled) { + let nodeToRun; - // The node is able to run. - return true; - }); + // If we have a node name Start, start from that + nodeToRun = compilation.nodes.find((n) => n.name === "Start"); + + if (!nodeToRun) { + // Otherwise, use the first node present that is not internal or part of + // a node group. + + nodeToRun = compilation.nodes.find((n) => { + // Is the node title prefixed with a tag that indicates that it's internal? + if (n.name.startsWith("$Yarn.Internal")) { + return false; + } + + // Does the node have a header that indicates it's part of a node group? + if (n.headers.find((h) => h.key == "$Yarn.Internal.NodeGroup")) { + return false; + } + + // The node is able to run. + return true; + }); + } + await dialogue.startDialogue(nodeToRun.name); } - await dialogue.startDialogue(nodeToRun.name); - } - }); + }); + } - document - .getElementById("button-save-script") - .addEventListener("click", (async) => { + const saveScriptButton = document.getElementById("button-save-script"); + + if (saveScriptButton) { + saveScriptButton.addEventListener("click", (async) => { var source = editor.getModel().getValue(); const fileName = "YarnScript.yarn"; downloadFile(source, fileName); }); + } + + const exportRunnerButton = document.getElementById("button-export-runner"); - document - .getElementById("button-export-runner") - .addEventListener("click", (async) => { + if (exportRunnerButton) { + exportRunnerButton.addEventListener("click", (async) => { if (dialogue.programData.length == 0) { window.alert( "Your Yarn script contains errors. Fix them before exporting a runner!", @@ -403,12 +413,14 @@ export async function load(script: string) { const fileName = "Runner.html"; downloadFile(html, fileName); }); + } let pdfDownloadInProgress = false; - document - .getElementById("button-download-pdf") - .addEventListener("click", (async) => { + const downloadPDFButton = document.getElementById("button-download-pdf"); + + if (downloadPDFButton) { + downloadPDFButton.addEventListener("click", (async) => { if (pdfDownloadInProgress) { return; } @@ -450,8 +462,9 @@ export async function load(script: string) { }); }); - // Finally, compile our source immediately. - compileSource(); + // Finally, compile our source immediately. + compileSource(); + } } async function compileSource() { diff --git a/src/staticwebapp.config.json b/src/staticwebapp.config.json index 0967ef4..bf820a9 100644 --- a/src/staticwebapp.config.json +++ b/src/staticwebapp.config.json @@ -1 +1,9 @@ -{} +{ + "$schema": "https://json.schemastore.org/staticwebapp.config.json", + "routes": [ + { + "route": "/embed", + "rewrite": "/embed.html" + } + ] +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index a5c24b2..8f483b2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -39,6 +39,14 @@ module.exports = (env, argv) => { gitHash: getGitHash().substring(0, 8), urlBase: process.env["BASE_URL"] || "", }), + new HtmlWebpackPlugin({ + title: "Yarn Spinner for JS", + template: "src/embed.html", + filename: "embed.html", + jsExtension: ".br", + gitHash: getGitHash().substring(0, 8), + urlBase: process.env["BASE_URL"] || "", + }), new MiniCssExtractPlugin({ filename: "[name].css", @@ -55,29 +63,29 @@ module.exports = (env, argv) => { ...(isProduction ? [ - new CompressionWebpackPlugin({ - filename: "[path][base].br", - algorithm: "brotliCompress", - test: /\.(js|svg)$/, - compressionOptions: { - params: { - [zlib.constants.BROTLI_PARAM_QUALITY]: 11, - }, + new CompressionWebpackPlugin({ + filename: "[path][base].br", + algorithm: "brotliCompress", + test: /\.(js|svg)$/, + compressionOptions: { + params: { + [zlib.constants.BROTLI_PARAM_QUALITY]: 11, }, - // Don't compress *.worker.js, because Monaco doesn't know - // to look for .worker.js.br and isn't expecting it to be - // compressed - exclude: /\.worker\.js/, - threshold: 0, - minRatio: Infinity, + }, + // Don't compress *.worker.js, because Monaco doesn't know + // to look for .worker.js.br and isn't expecting it to be + // compressed + exclude: /\.worker\.js/, + threshold: 0, + minRatio: Infinity, - // If DELETE_ORIGINAL_ASSETS is 1, keep only the output and - // delete the input files - deleteOriginalAssets: - process.env["DELETE_ORIGINAL_ASSETS"] || 0 ? true : false, - }), - new HtmlWebpackChangeAssetsExtensionPlugin(), - ] + // If DELETE_ORIGINAL_ASSETS is 1, keep only the output and + // delete the input files + deleteOriginalAssets: + process.env["DELETE_ORIGINAL_ASSETS"] || 0 ? true : false, + }), + new HtmlWebpackChangeAssetsExtensionPlugin(), + ] : []), ], devServer: { @@ -93,11 +101,11 @@ module.exports = (env, argv) => { ...(isProduction ? { - devtool: "source-map", - } + devtool: "source-map", + } : { - devtool: "inline-source-map", - }), + devtool: "inline-source-map", + }), mode: "development", module: {