Behaviors & Code-Erzeugung
Definitionen beschreiben, wie Blöcke aussehen; Behaviors definieren, was sie tun. Die Behaviors-Map paart jeden Block-Identifier mit der Funktion, die seinen ausführbaren Code erzeugt:
import type { MorphicBehaviorMap } from "morphic-blocks";
export const behaviors: MorphicBehaviorMap = { text_print(proxy) { return `console.log(${proxy.inputs.TEXT ?? "undefined"});\n`; },};Die Code-Erzeugung ist einseitig und entwicklergesteuert: Was auch immer dein Behavior als String zurückgibt, ist der Code. Es gibt keine feste Zielsprache — Behaviors geben typischerweise JavaScript aus, weil der Browser das ausführen kann, aber die Strings gehören dir.
Der Behavior-Proxy
Abschnitt betitelt „Der Behavior-Proxy“Deine Generate-Funktion erhält einen Proxy des lebenden Blocks:
| Eigenschaft | Was sie enthält |
|---|---|
blockId |
Die Blockly-Block-ID |
blockType |
Der saubere Block-Identifier (z. B. text_print) |
mode |
Der aktive Workspace-Mode |
inputs |
Erzeugter Code angehängter Blöcke, geschlüsselt nach Input-Namen |
fields |
Unveränderte Feldwerte, geschlüsselt nach Feldnamen |
quoted |
Dieselben Werte als String-Literale in Anführungszeichen |
context |
Render-Kontext |
proxy.inputs.TEXT ist der bereits erzeugte Code des Blocks, der im
TEXT-Slot steckt — die Erzeugung rekursiert für dich durch das Programm.
fields enthält jeden Wert genau so, wie das Feld ihn hat. quoted enthält
denselben Wert als String-Literal in Anführungszeichen, korrekt maskiert und
immer in Anführungszeichen, auch wenn er wie eine Zahl aussieht. Nimm die Form,
die deine Ausgabe braucht:
var_declare(proxy) { // ein Variablenname bleibt ohne Anführungszeichen: let x = … return `let ${proxy.fields.VAR} = ${proxy.inputs.VAL};\n`;},text_value(proxy) { // ein Textwert muss ein String sein: "hello", und "42" bleibt ein String return proxy.quoted.TEXT;},Das vollständige Behavior-Objekt
Abschnitt betitelt „Das vollständige Behavior-Objekt“Ein Behavior kann mehr als eine Generate-Funktion sein. Die Langform:
export const behaviors: MorphicBehaviorMap = { text_print: { init(block, context) { // einmaliges Setup bei Erstellung des Blocks }, onViewApplied(block, context) { // aufgerufen, nachdem ein Mode/View angewendet wurde — hier *eigene* // Blockly-Felder anhängen (Dropdown/Text/Number/Checkbox via `fields`) }, generate(proxy) { return `console.log(${proxy.inputs.TEXT ?? "undefined"});\n`; }, },};| Hook | Wann er läuft |
|---|---|
init |
Einmal, bei Instanziierung des Blocks |
onViewApplied |
Nach jeder Mode-Anwendung — eigene Felder anhängen (Standard-Dropdown/Text/Number/Checkbox gehören in fields) |
generate |
Während der Code-Erzeugung |
Eine reine Funktion ist die Kurzform für { generate }.
Erzeugen und Ausführen
Abschnitt betitelt „Erzeugen und Ausführen“const js = engine.generateJavaScript();
// mit Block-zu-Codeposition-Metadaten (von der Selection-Sync genutzt):const { code, metadata } = engine.generateJavaScriptWithMetadata();
// in der Seite ausführen:const { output, error } = engine.runJavaScript();runJavaScript() sammelt jede Zeile, die das Programm ausgibt, samt ihrer
Stufe. So kannst du die Ausgabe auf deiner Seite zeigen, ohne eine eigene
Konsole zu schreiben:
const { output, error } = engine.runJavaScript();// output = [// { level: "log", text: "Hello" },// { level: "warn", text: "Value is empty" },// ]
for (const line of output) { const div = document.createElement("div"); div.textContent = line.text; div.className = line.level; // .warn / .error in deinem CSS stylen outputEl.appendChild(div);}if (error) outputEl.append(`Error: ${error.message}`);Standardmäßig werden die Zeilen nur in output gesammelt; in die
Browser-Konsole wird nichts ausgegeben. Zwei Optionen ändern das, und output
wird in jedem Fall gefüllt:
engine.runJavaScript({ logToConsole: true }); // zusätzlich in die Browser-Konsoleengine.runJavaScript({ console: myConsole }); // an deine eigene Konsole schickenAusgegebene Werte in der gezeigten Sprache
Abschnitt betitelt „Ausgegebene Werte in der gezeigten Sprache“Das Programm läuft immer als JavaScript, deshalb lesen sich ausgegebene Werte
wie JavaScript: true, false, null, 1,2,3. Ein values-Format an einem
Code-Element im code-Abschnitt lässt
sie sich wie diese Sprache lesen, solange der Codespace sie zeigt:
"code": { "python": { "values": { "true": "True", "false": "False", "null": "None", "NaN": "nan", "Infinity": "inf", "-Infinity": "-inf", "list": { "open": "[", "close": "]", "separator": ", ", "quote": "'" } } }}print(True) zeigt dann True, und eine Liste ['a', 'b']. Jeder Eintrag ist
optional; ein weggelassener Wert bleibt, wie JavaScript ihn schreibt. Nur der
ausgegebene Text in output ändert sich; eine übergebene console bekommt
weiterhin die Werte selbst. Wie das Programm läuft, bleibt JavaScript:
7 / 2 ist 3.5, auch wo die gezeigte Sprache 3 ergäbe.
Klammern in zusammengesetzten Ausdrücken
Abschnitt betitelt „Klammern in zusammengesetzten Ausdrücken“Blöcke kodieren Gruppierung durch Verschachtelung: Ein Multiplikations-Block, der
einen Additions-Block enthält, ist 2 * (3 + 4). Reiner Text verliert das,
sofern die Klammern nicht ausgeschrieben werden — deshalb setzt das Framework sie:
im ausführbaren Code ebenso wie im gerenderten Text.
Ein Operand wird geklammert, wenn zwei Bedingungen zutreffen: Der Operand-Block setzt selbst Werte zusammen (er hat Value-Inputs), und das Template des Eltern-Blocks setzt mehrere Werte zusammen. Die zweite Bedingung hält Templates mit nur einem Slot sauber:
| Template | Value-Slots | Ergebnis |
|---|---|---|
%1 %OP %2 |
2 | 2 * (3 + 4) — Gruppierung bleibt erhalten |
print(%1) |
1 | print(2 * 3) |
if %1: |
1 | if 10 == 20: — idiomatisches Python |
Ein unärer Operator ist die Ausnahme: -%1 hat nur einen Slot, -(3 + 4)
braucht aber Klammern. Schreibe sie dort selbst ins Template — -(%1) —, wo das
Framework die Absicht nicht ableiten kann.
Behaviors vs. Template-Rendering
Abschnitt betitelt „Behaviors vs. Template-Rendering“Zwei verschiedene Dinge machen aus Blöcken Text — verwechsle sie nicht:
- Behaviors erzeugen den ausführbaren Code (
generateJavaScript()). - Templates (
code-Elements mit%N-Einsetzung) erzeugen, was die Codespaces anzeigen.
Sie sind absichtlich unabhängig: Was Lernende sehen, kann Python sein, während das, was läuft, JavaScript ist. Siehe Presets & Views zum Verdrahten der Views und die Codespace-Anleitung für die editierbare Text-Ansicht.