diff --git a/app.js b/app.js index b540b32..cf25363 100644 --- a/app.js +++ b/app.js @@ -230,9 +230,17 @@ function setupUI() { const btn = e.target.closest("button"); if (!btn) return; if (handleBlockAction(btn)) return; + const prog = $id("main-program"); if (btn.dataset.action === "add-section" && typeof enterSectionCreateMode === "function") { enterSectionCreateMode(); } + if (btn.dataset.action === "generate") prog.generateAndDownload(); + if (btn.dataset.action === "save") saveWorkspace(); + if (btn.dataset.action === "load") loadWorkspace(); + if (btn.dataset.action === "new") newWorkspace(); + if (btn.dataset.action === "run-tests") { + if (window.APP_TEST_SUITE) APP_TEST_SUITE.run(); + } }); } @@ -340,25 +348,6 @@ function setupUI() { }); } - /* Global toolbar */ - const globalToolbar = document.querySelector(".global-toolbar"); - if (globalToolbar) { - globalToolbar.addEventListener("click", e => { - const btn = e.target.closest("button"); - if (!btn) return; - const prog = $id("main-program"); - if (!prog) { showError("Main program element not found"); return; } - if (btn.dataset.action === "generate") prog.generateCode(); - if (btn.dataset.action === "save") saveWorkspace(); - if (btn.dataset.action === "load") loadWorkspace(); - if (btn.dataset.action === "new") newWorkspace(); - if (btn.dataset.action === "copy-code") copyCode(); - if (btn.dataset.action === "run-tests") { - if (window.APP_TEST_SUITE) APP_TEST_SUITE.run(); - } - }); - } - /* Tab bar */ const tabBar = document.querySelector(".tab-bar"); if (tabBar) { diff --git a/helpers.js b/helpers.js index d945830..82cd5a9 100644 --- a/helpers.js +++ b/helpers.js @@ -232,11 +232,6 @@ function findDataSource(hook) { } /** Copies the generated C code text to the clipboard. */ -function copyCode() { - const el = $id("ir-code"); - if (el && el.textContent) navigator.clipboard.writeText(el.textContent).catch(() => {}); -} - /** * Escapes special characters for embedding in a C string literal. * Handles backslash, double-quote, newline, carriage return, and tab. diff --git a/index.html b/index.html index 42326a3..3e85c94 100644 --- a/index.html +++ b/index.html @@ -374,6 +374,12 @@ + + + + + +
@@ -438,18 +444,7 @@
- -
- - - - - - -
-
-
-
+
× diff --git a/style.css b/style.css index 34c14c1..f7fcdb0 100644 --- a/style.css +++ b/style.css @@ -46,6 +46,12 @@ h1 { flex-shrink: 0; flex-wrap: wrap; background: var(--surface); border-bottom: 1px solid var(--border); } +.pane-toolbar button.action-btn { + padding: 0.3rem 0.6rem; font-size: 0.78rem; font-weight: 500; + background: var(--accent); color: #fff; border: 1px solid var(--accent); + line-height: 1.2; +} +.pane-toolbar button.action-btn:hover { background: #357abd; border-color: #357abd; } .pane-toolbar button { padding: 0.3rem; font: inherit; font-size: 0.75rem; cursor: pointer; background: var(--bg); color: var(--text); border: 1px solid var(--border); @@ -463,31 +469,7 @@ wc-assign #hook-next[data-direction="out"] { flex-direction: column; } -.global-toolbar { - display: flex; gap: 0.25rem; padding: 0.3rem; - align-items: flex-start; flex-shrink: 0; - border-top: 1px solid var(--border); background: var(--surface); -} -.global-toolbar button { - padding: 0.4rem 0.8rem; font: inherit; font-size: 0.8rem; cursor: pointer; - background: var(--accent); color: #fff; border: none; border-radius: 3px; -} -.global-toolbar button:hover { background: #357abd; } -.global-toolbar .code-output { - flex: 1; min-height: 80px; max-height: 50vh; overflow: auto; resize: vertical; - background: var(--bg); border: 1px solid var(--border); - font-family: 'Courier New', monospace; font-size: 0.8rem; line-height: 1.5; - padding: 0.3rem; position: relative; -} -.global-toolbar .code-output pre { - margin: 0; white-space: pre; tab-size: 4; -} -.global-toolbar .code-output code { - display: block; -} -.global-toolbar .code-output code.hljs { - background: transparent; padding: 0; -} + [data-block] { diff --git a/wcprogram.js b/wcprogram.js index 300fa28..ee5f6cb 100644 --- a/wcprogram.js +++ b/wcprogram.js @@ -208,12 +208,8 @@ class WCProgram extends HTMLElement { return lines.join("\n"); } - generateCode() { - const outputEl = $id("ir-code"); - if (!outputEl) { showError("Missing #ir-code output element"); return; } - + _generateIR() { const { usedQuantities, usedUnits } = this._gatherUsedTypes(); - const typeDefs = this._generateTypeDefs(usedQuantities); const unitDefs = this._generateUnitDefs(usedUnits); const { boolDefs, scalarDefs, vectorDefs, stringDefs, arrayDefs } = this._generateVariableDeclarations(); @@ -225,24 +221,32 @@ class WCProgram extends HTMLElement { ? CodeGen.emitFlat(firstChild) : "; no flowchart body\n"; - // Collect external function declarations from used libraries const proccalls = this.querySelectorAll("wc-proccall"); const decls = this._generateLLVMDecls(proccalls); - // Collect string globals const strPool = CodeGen.getStringPool(); const globals = strPool.map(function (_, i) { return CodeGen.stringGlobal(i); }).join("\n"); - // Variable declarations -> alloca instructions in the prologue const dataSection = (boolDefs || scalarDefs || vectorDefs || stringDefs || arrayDefs) ? [boolDefs, scalarDefs, vectorDefs, stringDefs, arrayDefs] .filter(Boolean).map(function (s) { return s + "\n"; }).join("") : ""; - const raw = CodeGen.generateIRFile(firstChild, decls, globals, dataSection); - outputEl.textContent = raw; - if (typeof hljs !== "undefined") hljs.highlightElement(outputEl); - outputEl.parentElement.dataset.language = "llvm"; + return CodeGen.generateIRFile(firstChild, decls, globals, dataSection); + } + + generateAndDownload() { + const ir = this._generateIR(); + if (!ir) { showError("No IR to download"); return; } + const blob = new Blob([ir], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "program.ll"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); } }