forked from 1b-agent/opencadstudio-upstream
feat: hatch PDF draw order (#1345), numeric CAS (#1348), web automation (#1349), DATALINK table, System theme; honest decode accounting
Upstream issues, fixed: #1345 — hatches plotted at draw_depth 0.0: the plot path cloned models from the hatch cache, which bakes the depth at seed time; the canvas re-reads the live depth map every frame, which is why the editor was right and every export (PDF and native printing — both route through the exporter) was wrong, with hatch-vs-hatch ties broken by HashMap order. The plot path now re-reads the depth map per entity, and the exporter's tie-break ranks follow the editor's own pass order (hatch < image < wire fill < wire < text < wipeout) instead of pinning hatches between fills and wires. Pinned by content-stream operator- order tests (back/front hatches, overlapping hatches, interleaved wire-between-hatches) and a scene test that fails pre-fix. #1348 — set_properties' optimistic-concurrency compare now matches numbers by value (10.0 == 10) through json_values_match, compatible with the shape of upstream PR #1350; nested objects/arrays recurse. #1349 — web automation contract: control protocol 2 (v1 clients still accepted) adds {op:'open', name, data_base64} routed through the guarded open queue; window.ocsControl.ready/request promise wrapper with an ocs-ready event (web/ocs-control.js); the startup donation prompt is suppressed once a control request is seen (opt-in pre-boot via localStorage); docs/automation documents the whole web contract. Also: 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. The image-refusal-unknown-size locale key moves to the group the catalog always claimed (file).
This commit is contained in:
1 parent
84b093748f
commit
2c38de53e2
41 files changed
+1026
-92
No files matched your search
@@ -0,0 +1,72 @@
|
||||
// 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/README.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};
|
||||
})();
|
||||
Reference in new issue
Block a user