Files
crumbmissions/crumbblocks/crumbblocks.html
2025-12-23 20:52:00 +01:00

367 lines
17 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<title>Crumbblocks Wasserkocher (Demo)</title>
<!-- Feste Blockly-Version (verhindert textToDom-Fehler) -->
<script src="https://unpkg.com/blockly@9.3.3/blockly.min.js"></script>
<script src="https://unpkg.com/blockly@9.3.3/msg/de.js"></script>
<style>
:root {
--bg: #0b1016;
--panel: #0e141b;
--ink: #e6edf3;
--muted: #93a1b3;
--accent: #4caf50;
--border: #1f2a37;
--console: #0a0f14;
--console-ink: #b7ffb2;
}
html, body {
height: 100%;
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}
.wrap {
display: grid;
grid-template-rows: auto auto 1fr auto;
gap: 12px;
max-width: 1200px;
margin: 0 auto;
padding: 16px;
}
header h1 { font-size: 20px; margin: 0; }
.controls {
display: flex; gap: 8px; flex-wrap: wrap;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px; padding: 10px;
}
button {
background: var(--accent);
color: #fff;
border: 0;
border-radius: 8px;
padding: 10px 14px;
font-weight: 700;
cursor: pointer;
}
button.secondary { background: #374151; }
button:active { transform: translateY(1px); }
.stage {
display: grid; grid-template-rows: 64vh 28vh; gap: 12px;
}
#blocklyDiv {
width: 100%;
height: 64vh;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
overflow: hidden;
}
#output {
width: 100%;
height: 28vh;
background: var(--console);
color: var(--console-ink);
border: 1px solid var(--border);
border-radius: 10px;
padding: 12px;
margin: 0;
overflow: auto;
white-space: pre-wrap;
}
.hint {
color: var(--muted);
font-size: 12px;
margin-top: 6px;
}
</style>
</head>
<body>
<div class="wrap">
<header>
<h1>🧁 Crumbblocks Virtueller Wasserkocher (Kids-Demo)</h1>
<div class="hint">
Variablen: <code>power_w</code>, <code>wasser_ml</code>, <code>sensor_temp_c</code>, <code>zeit_s</code>,
<code>energie_wh</code>, <code>verbrauch_wh</code>. &nbsp; Ziel: Bis 100 °C erhitzen, Energie/Zeit mitrechnen.
</div>
</header>
<div class="controls">
<button onclick="loadDemo()">🎬 Demo laden</button>
<button onclick="runCode()">▶️ Ausführen</button>
<button class="secondary" onclick="clearOutput()">🧹 Leeren</button>
<span class="hint">Sendet per <code>POST /crumbapi/blockly-terminal</code> (Fallback: Echo).</span>
</div>
<div class="stage">
<div id="blocklyDiv"></div>
<pre id="output">🌲 Terminal wartet …</pre>
</div>
<!-- TOOLBOX (Standardkategorien + Variablen/Funktionen) -->
<xml id="toolbox" style="display:none">
<category name="Logik" categorystyle="logic_category">
<block type="controls_if"></block>
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_boolean"></block>
</category>
<category name="Schleifen" categorystyle="loop_category">
<block type="controls_repeat_ext"><value name="TIMES"><block type="math_number"><field name="NUM">10</field></block></value></block>
<block type="controls_whileUntil"><field name="MODE">WHILE</field></block>
<block type="controls_flow_statements"></block>
</category>
<category name="Mathe" categorystyle="math_category">
<block type="math_number"></block>
<block type="math_arithmetic"></block>
<block type="math_round"></block>
</category>
<category name="Text" categorystyle="text_category">
<block type="text"></block>
<block type="text_print"></block>
</category>
<sep></sep>
<category name="Variablen" categorystyle="variable_category" custom="VARIABLE_DYNAMIC"></category>
<category name="Funktionen" categorystyle="procedure_category" custom="PROCEDURE"></category>
</xml>
<!-- DEMO-WORKSPACE (Wasserkocher) -->
<script id="demo-xml" type="text/plain">
<xml xmlns="https://developers.google.com/blockly/xml">
<variables>
<variable>sensor_temp_c</variable>
<variable>power_w</variable>
<variable>wasser_ml</variable>
<variable>zeit_s</variable>
<variable>energie_wh</variable>
<variable>verbrauch_wh</variable>
</variables>
<!-- Startwerte -->
<block type="variables_set" x="24" y="24">
<field name="VAR">sensor_temp_c</field>
<value name="VALUE"><block type="math_number"><field name="NUM">20</field></block></value>
<next>
<block type="variables_set">
<field name="VAR">power_w</field>
<value name="VALUE"><block type="math_number"><field name="NUM">2000</field></block></value>
<next>
<block type="variables_set">
<field name="VAR">wasser_ml</field>
<value name="VALUE"><block type="math_number"><field name="NUM">250</field></block></value>
<next>
<!-- Sicherheitslimit Wasser -->
<block type="controls_if">
<value name="IF0">
<block type="logic_compare">
<field name="OP">GT</field>
<value name="A"><block type="variables_get"><field name="VAR">wasser_ml</field></block></value>
<value name="B"><block type="math_number"><field name="NUM">2000</field></block></value>
</block>
</value>
<statement name="DO0">
<block type="variables_set">
<field name="VAR">wasser_ml</field>
<value name="VALUE"><block type="math_number"><field name="NUM">2000</field></block></value>
</block>
</statement>
<next>
<block type="variables_set">
<field name="VAR">zeit_s</field>
<value name="VALUE"><block type="math_number"><field name="NUM">0</field></block></value>
<next>
<block type="variables_set">
<field name="VAR">energie_wh</field>
<value name="VALUE"><block type="math_number"><field name="NUM">0</field></block></value>
<next>
<block type="variables_set">
<field name="VAR">verbrauch_wh</field>
<value name="VALUE"><block type="math_number"><field name="NUM">0</field></block></value>
<next>
<!-- while (sensor_temp_c < 100) -->
<block type="controls_whileUntil">
<field name="MODE">WHILE</field>
<value name="BOOL">
<block type="logic_compare">
<field name="OP">LT</field>
<value name="A"><block type="variables_get"><field name="VAR">sensor_temp_c</field></block></value>
<value name="B"><block type="math_number"><field name="NUM">100</field></block></value>
</block>
</value>
<statement name="DO">
<!-- Abbruch wenn kein Strom -->
<block type="controls_if">
<value name="IF0">
<block type="logic_compare">
<field name="OP">LTE</field>
<value name="A"><block type="variables_get"><field name="VAR">power_w</field></block></value>
<value name="B"><block type="math_number"><field name="NUM">0</field></block></value>
</block>
</value>
<statement name="DO0">
<block type="text_print">
<value name="TEXT"><block type="text"><field name="TEXT">Kein Strom STOP</field></block></value>
<next>
<block type="controls_flow_statements"><field name="FLOW">BREAK</field></block>
</next>
</block>
</statement>
<next>
<!-- Heize-Schritt -->
<block type="text_print">
<value name="TEXT"><block type="text"><field name="TEXT">Heize</field></block></value>
<next>
<!-- sensor_temp_c += 5 -->
<block type="variables_set">
<field name="VAR">sensor_temp_c</field>
<value name="VALUE">
<block type="math_arithmetic">
<field name="OP">ADD</field>
<value name="A"><block type="variables_get"><field name="VAR">sensor_temp_c</field></block></value>
<value name="B"><block type="math_number"><field name="NUM">5</field></block></value>
</block>
</value>
<next>
<!-- zeit_s += 5 -->
<block type="variables_set">
<field name="VAR">zeit_s</field>
<value name="VALUE">
<block type="math_arithmetic">
<field name="OP">ADD</field>
<value name="A"><block type="variables_get"><field name="VAR">zeit_s</field></block></value>
<value name="B"><block type="math_number"><field name="NUM">5</field></block></value>
</block>
</value>
<next>
<!-- energie_wh += power_w * 5 / 3600 -->
<block type="variables_set">
<field name="VAR">energie_wh</field>
<value name="VALUE">
<block type="math_arithmetic">
<field name="OP">ADD</field>
<value name="A"><block type="variables_get"><field name="VAR">energie_wh</field></block></value>
<value name="B">
<block type="math_arithmetic">
<field name="OP">DIVIDE</field>
<value name="A">
<block type="math_arithmetic">
<field name="OP">MULTIPLY</field>
<value name="A"><block type="variables_get"><field name="VAR">power_w</field></block></value>
<value name="B"><block type="math_number"><field name="NUM">5</field></block></value>
</block>
</value>
<value name="B"><block type="math_number"><field name="NUM">3600</field></block></value>
</block>
</value>
</block>
</value>
<next>
<block type="variables_set">
<field name="VAR">verbrauch_wh</field>
<value name="VALUE"><block type="variables_get"><field name="VAR">energie_wh</field></block></value>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</statement>
<next>
<block type="text_print">
<value name="TEXT"><block type="text"><field name="TEXT">Fertig. STOP.</field></block></value>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</xml>
</script>
<script>
// Workspace
const workspace = Blockly.inject('blocklyDiv', {
toolbox: document.getElementById('toolbox'),
theme: Blockly.Themes.Dark,
renderer: 'geras',
grid: { spacing: 24, length: 3, colour: '#223045', snap: true },
zoom: { controls: true, wheel: true, startScale: 1.05, maxScale: 2.0, minScale: 0.5, pinch: true },
trashcan: true
});
window.addEventListener('resize', () => Blockly.svgResize(workspace));
function out(txt, replace=false) {
const el = document.getElementById('output');
el.textContent = replace ? String(txt) : (el.textContent + '\n' + String(txt));
el.scrollTop = el.scrollHeight;
}
function clearOutput() { document.getElementById('output').textContent = '🌲 Terminal wartet …'; }
function loadDemo() {
try {
const xmlTxt = document.getElementById('demo-xml').textContent.trim();
const dom = (Blockly.Xml && Blockly.Xml.textToDom)
? Blockly.Xml.textToDom(xmlTxt)
: Blockly.utils.xml.textToDom(xmlTxt);
workspace.clear();
Blockly.Xml.domToWorkspace(dom, workspace);
out('✅ Demo geladen.', true);
} catch (e) {
out('❌ Demo konnte nicht geladen werden: ' + e, true);
}
}
async function runCode() {
const code = Blockly.JavaScript.workspaceToCode(workspace);
out('📤 Sende an Terminal…\n\n' + code, true);
// Variablenliste (für spätere Auswertung im Backend hilfreich)
const knownVars = ['sensor_temp_c','power_w','wasser_ml','zeit_s','energie_wh','verbrauch_wh'];
try {
const res = await fetch('/crumbapi/blockly-terminal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mode: 'blockly', blockcode: code, vars: knownVars })
});
if (!res.ok) throw new Error('HTTP ' + res.status);
const data = await res.json();
out('\n✅ Antwort:\n' + JSON.stringify(data, null, 2));
} catch (err) {
// Fallback: Echo-JSON lokal anzeigen
const echo = {
ok: true,
mode: 'echo-fallback',
request: { mode: 'blockly', blockcode: code, vars: knownVars }
};
out('\n Endpoint nicht bereit. Fallback aktiv.\n\n' + JSON.stringify(echo, null, 2));
}
}
// Direkt beim Laden einmal die Demo hineinlegen:
loadDemo();
</script>
</div>
</body>
</html>