Live-Konsole + Progress-Bars — Engine.log + Engine.progress

Log-Zeilen und Progress-Bars ans UI / die CLI streamen, **während das Skript läuft**, nicht erst danach. `Engine.log("…")` erscheint sofort im Console-Panel; ein `Engine.progress("Encoding", { total: 40 })`-Handle treibt einen inline Progress-Balken, der bei jedem `p.step()` tickt. Mehrere Handles können gleichzeitig aktiv sein — jeder bekommt seinen eigenen Balken. Drei Muster gezeigt: 1. **Indeterminate Progress** (Spinner-Style) für Arbeit ohne bekannte Stückzahl. `.message("…")` aktualisiert nur den Status-Text, ohne den Wert zu ändern. 2. **Determinate Progress** (gefüllter Balken) für Schleifen mit bekanntem Total. `.step(delta, msg)` schiebt vor und aktualisiert optional die Statuszeile. 3. **Mehrere parallele Progresses** — beliebig viele Handles deklarieren; das UI stapelt sie inline. Zusammen mit Batch-Modus oder länger laufenden ML-Tools sieht der Nutzer endlich, was das Skript gerade macht.

INPUT
INPUT — Live-Konsole + Progress-Bars — Engine.log + Engine.progress
Hue-cycle montage
Hue-cycle montage — Live-Konsole + Progress-Bars — Engine.log + Engine.progress
JavaScript
// Live console + progress bars — Engine.log() + Engine.progress()
// stream straight to the v3 UI's console panel and the CLI's
// indicatif renderer as work happens. No more waiting for the
// script to finish before output appears.
// demo_progress.js
//!INPUT: INPUT
//!OUTPUT: OUTPUT
//!PARAM: FRAMES:integer=12,min=4,max=60

const src = Engine.loadImage(INPUT);

// 1) Indeterminate progress — no `total`, just a "still working"
//    spinner. The UI shows a moving pulse; the CLI shows a spinner.
const setup = Engine.progress("Preparing");
setup.message("loading reference frame");
const reference = src.clone().resize(256, 256);
setup.message("computing hue cycle");
setup.done("ready");

// 2) Determinate progress — the bar fills from 0 to FRAMES. Each
//    step() call carries a status message that the UI / CLI shows
//    next to the percentage.
const pass = Engine.progress("Animating hue cycle", { total: FRAMES });
const frames = [];
for (let i = 0; i < FRAMES; i++) {
    const t = i / (FRAMES - 1);
    const frame = reference.clone()
        .hue(t * 360)
        .saturation(1 + 0.5 * Math.sin(t * Math.PI));
    frames.push(frame);
    Engine.log(`frame ${i + 1}/${FRAMES} — hue=${Math.round(t * 360)}°`);
    pass.step(1, `frame ${i + 1}/${FRAMES}`);
}
pass.done(`✓ ${FRAMES} frames rendered`);

// 3) A second determinate progress, in parallel with the first if
//    the script were async — here it just shows that multiple
//    handles can be active independently.
// One montageH() call with all frames as args allocates a single
// output handle — avoids the N-1 intermediates a fold loop would
// leak. The progress bar ticks for visual feedback even though the
// actual stitch is one engine call.
const stitch = Engine.progress("Stitching montage", { total: FRAMES });
for (let i = 1; i < FRAMES; i++) {
    stitch.step(1, `column ${i + 1}/${FRAMES}`);
}
const row = frames[0].montageH(...frames.slice(1));
stitch.done("✓ montage ready");

row.save(OUTPUT);
Engine.log("done — saved to OUTPUT");

// Cleanup — free every frame + the reference. The original src too.
for (const f of frames) f.free();
reference.free();
row.free();
src.free();

// © 2026 Michael Lechner · mlc OpticScript · https://mlcgo.eu · Elastic License 2.0