Files
OneCAD-Draft/web/ocs-control.js
T
Afonsop db8db14006 feat: web automation wrapper (#1349), DATALINK table on web, System theme; honest decode accounting
Ported from 2c38de53 (#1717). Main already carries the hatch PDF draw
order fix (#1345), the numeric set_properties compare (#1348) and the
web open from bytes, so those parts stay out; what remains:

#1349 — window.ocsControl.ready/request promise wrapper with an
ocs-ready event (web/ocs-control.js, loaded by the web page), and the
startup donation prompt is suppressed once a control request is seen
(opt-in pre-boot via localStorage["opencadstudio.automation"] = "1").
docs/automation/web.md documents both. The control protocol stays at 1.

DATALINK CSV-to-table works on web (picker + bytes through the session
registry — was std::fs, silently dead); COLORSCHEME accepts System
(prefers-color-scheme on web, AppsUseLightTheme on Windows).

Decode accounting, made honest per the fix-of-fix review: into_rgba8
allocates the RGBA destination while the native buffer is alive for
every non-Rgba8 layout (L8 5, La8 6, Rgb8 7, L16 6, La16 8 B/px
peaks — the formula now says so), GIF bills the screen+frame
coexistence at 8 B/px, and TIFF drops the 3x multipliers both ways —
tiff 0.11 splits the given budget between frame and scratch (a total
ceiling), so the old double-billing refused honest 16-bit GeoTIFFs
~2.25x early while admitting hostile chunk transients at 3x.

(cherry picked from commit 2c38de53)
2026-10-12 12:02:05 +03:00

73 lines
2.6 KiB
JavaScript

// Web automation convenience API (#1349).
//
// The wasm module exposes the control bridge through `window.wasmBindings`
// (ocs_control_submit / ocs_control_take). This module wraps it in promises:
//
// await window.ocsControl.ready resolves once the bridge exists
// window.addEventListener("ocs-ready", …) fired once, at the same moment
// await window.ocsControl.request({...}) one control request -> final reply
//
// Hosts that intend to automate from the start should set
// localStorage["opencadstudio.automation"] = "1" before the page loads so the
// startup donation prompt never appears (see docs/automation/web.md).
(() => {
if (window.ocsControl) return;
const bridge = () => {
const api = window.wasmBindings;
return api && api.ocs_control_submit && api.ocs_control_take ? api : null;
};
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
let readyResolve;
const ready = new Promise(resolve => { readyResolve = resolve; });
const settle = () => {
readyResolve(window.ocsControl);
window.dispatchEvent(new Event("ocs-ready"));
};
const poll = setInterval(() => {
if (bridge()) {
clearInterval(poll);
settle();
}
}, 50);
if (bridge()) {
clearInterval(poll);
settle();
}
// Submit one request and poll take() until the app stores its reply.
async function exchange(api, payload, pollMs, deadline) {
const ticket = api.ocs_control_submit(JSON.stringify(payload));
for (;;) {
const raw = api.ocs_control_take(ticket);
if (raw) return JSON.parse(raw);
if (performance.now() >= deadline) {
throw new Error("ocs-control: timed out: " + JSON.stringify(payload));
}
await sleep(pollMs);
}
}
// One control request. Replies that are still `accepted`/`running` (an
// async open, capture or save) are followed through `op:"operation"` until
// a final status arrives. Resolves with the final reply object; rejects
// only on timeout or a missing bridge.
async function request(payload, options = {}) {
const pollMs = options.pollMs ?? 50;
const deadline = performance.now() + (options.timeoutMs ?? 120000);
const api = bridge();
if (!api) {
throw new Error("ocs-control: bridge not ready (await ocsControl.ready)");
}
let reply = await exchange(api, payload, pollMs, deadline);
while (["accepted", "running"].includes(reply.status) && reply.request_id) {
await sleep(pollMs);
reply = await exchange(
api, {op: "operation", request_id: reply.request_id}, pollMs, deadline);
}
return reply;
}
window.ocsControl = {ready, request};
})();