@floegence/redevplugin-ui 3.0.26 → 3.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/surface.js +49 -30
- package/package.json +2 -2
package/dist/surface.js
CHANGED
|
@@ -413,7 +413,8 @@ export class PluginBridgeClient {
|
|
|
413
413
|
return Promise.reject(requestCancelledError(options, false));
|
|
414
414
|
let posted = false;
|
|
415
415
|
const result = new Promise((resolve, reject) => {
|
|
416
|
-
|
|
416
|
+
// Canvas transfer waits for measurable layout; surface disposal owns cancellation.
|
|
417
|
+
const timer = options.kind === "canvas" ? undefined : setTimeout(() => {
|
|
417
418
|
const pending = this.#takePending(id);
|
|
418
419
|
if (!pending)
|
|
419
420
|
return;
|
|
@@ -1153,6 +1154,8 @@ export function createOpaquePluginBootstrapHTML(options = {}) {
|
|
|
1153
1154
|
};
|
|
1154
1155
|
const terminateQuiescedWorker = () => {
|
|
1155
1156
|
cancelPendingRendererCommits();
|
|
1157
|
+
for (const runtime of canvasRuntimes.values()) runtime.dispose();
|
|
1158
|
+
canvasRuntimes.clear();
|
|
1156
1159
|
if (workerHeartbeatTimer) clearTimeout(workerHeartbeatTimer);
|
|
1157
1160
|
if (workerHeartbeatTimeout) clearTimeout(workerHeartbeatTimeout);
|
|
1158
1161
|
workerHeartbeatTimer = undefined;
|
|
@@ -2253,13 +2256,17 @@ export function createOpaquePluginBootstrapHTML(options = {}) {
|
|
|
2253
2256
|
};
|
|
2254
2257
|
const canvasMetrics = (canvas) => {
|
|
2255
2258
|
const rect = canvas.getBoundingClientRect();
|
|
2256
|
-
|
|
2257
|
-
const
|
|
2259
|
+
if (rect.width === 0 || rect.height === 0) return undefined;
|
|
2260
|
+
const cssWidth = Math.ceil(rect.width);
|
|
2261
|
+
const cssHeight = Math.ceil(rect.height);
|
|
2258
2262
|
const devicePixelRatio = Math.min(4, Math.max(0.5, Number(globalThis.devicePixelRatio) || 1));
|
|
2259
2263
|
if (!Number.isSafeInteger(cssWidth) || !Number.isSafeInteger(cssHeight) || cssWidth <= 0 || cssHeight <= 0 || cssWidth > maxCanvasDimension || cssHeight > maxCanvasDimension) {
|
|
2260
2264
|
throw new Error("plugin canvas dimensions exceed the renderer budget");
|
|
2261
2265
|
}
|
|
2262
|
-
const
|
|
2266
|
+
const pixelWidth = Math.ceil(cssWidth * devicePixelRatio);
|
|
2267
|
+
const pixelHeight = Math.ceil(cssHeight * devicePixelRatio);
|
|
2268
|
+
if (pixelWidth > maxCanvasDimension || pixelHeight > maxCanvasDimension) throw new Error("plugin canvas dimensions exceed the renderer budget");
|
|
2269
|
+
const pixelCount = pixelWidth * pixelHeight;
|
|
2263
2270
|
return { cssWidth, cssHeight, devicePixelRatio, pixelCount };
|
|
2264
2271
|
};
|
|
2265
2272
|
const openCanvas = (id, canvasID) => {
|
|
@@ -2268,6 +2275,10 @@ export function createOpaquePluginBootstrapHTML(options = {}) {
|
|
|
2268
2275
|
completeWorkerRequest(id);
|
|
2269
2276
|
return rejectWorkerRequest(id, "plugin canvas is missing, already transferred, or unsupported");
|
|
2270
2277
|
}
|
|
2278
|
+
if (canvasRuntimes.size >= maxCanvasCount) {
|
|
2279
|
+
completeWorkerRequest(id);
|
|
2280
|
+
return rejectWorkerRequest(id, "plugin canvas resources exceed renderer limits");
|
|
2281
|
+
}
|
|
2271
2282
|
if (canvas.tabIndex < 0) canvas.tabIndex = 0;
|
|
2272
2283
|
let pointerWindowStartedAt = 0;
|
|
2273
2284
|
let pointerEvents = 0;
|
|
@@ -2278,16 +2289,34 @@ export function createOpaquePluginBootstrapHTML(options = {}) {
|
|
|
2278
2289
|
};
|
|
2279
2290
|
const sendInput = (event) => sendWorker({ type: "redevplugin.ui.canvas.input", canvas_id: canvasID, event });
|
|
2280
2291
|
const sendResize = () => {
|
|
2292
|
+
if (runtimeDisposed || canvasRuntimes.get(canvasID) !== runtime) return;
|
|
2281
2293
|
const metrics = canvasMetrics(canvas);
|
|
2282
|
-
|
|
2294
|
+
if (!metrics) return;
|
|
2283
2295
|
let totalPixels = metrics.pixelCount;
|
|
2284
2296
|
for (const [identifier, active] of canvasRuntimes) {
|
|
2285
|
-
if (identifier !== canvasID) totalPixels += active.pixelCount;
|
|
2297
|
+
if (identifier !== canvasID) totalPixels += active.metrics?.pixelCount ?? 0;
|
|
2286
2298
|
}
|
|
2287
2299
|
if (totalPixels > maxCanvasTotalPixels) throw new Error("plugin canvases exceed the renderer pixel budget");
|
|
2288
|
-
if (runtime
|
|
2300
|
+
if (runtime.openRequestID !== undefined) {
|
|
2301
|
+
const offscreen = canvas.transferControlToOffscreen();
|
|
2302
|
+
runtime.metrics = metrics;
|
|
2303
|
+
runtime.openRequestID = undefined;
|
|
2304
|
+
completeWorkerRequest(id);
|
|
2305
|
+
sendWorkerTransfer({ type: "redevplugin.ui.canvas.ready", id, canvas_id: canvasID, canvas: offscreen, css_width: metrics.cssWidth, css_height: metrics.cssHeight, device_pixel_ratio: metrics.devicePixelRatio }, [offscreen]);
|
|
2306
|
+
return;
|
|
2307
|
+
}
|
|
2308
|
+
runtime.metrics = metrics;
|
|
2289
2309
|
sendInput({ type: "resize", css_width: metrics.cssWidth, css_height: metrics.cssHeight, device_pixel_ratio: metrics.devicePixelRatio });
|
|
2290
|
-
|
|
2310
|
+
};
|
|
2311
|
+
const updateCanvas = () => {
|
|
2312
|
+
try { sendResize(); }
|
|
2313
|
+
catch (error) {
|
|
2314
|
+
if (runtime.openRequestID === undefined) return fail(error && error.message || "plugin canvas resize exceeded renderer limits");
|
|
2315
|
+
dispose();
|
|
2316
|
+
canvasRuntimes.delete(canvasID);
|
|
2317
|
+
completeWorkerRequest(id);
|
|
2318
|
+
rejectWorkerRequest(id, String(error && error.message || "plugin canvas transfer failed").slice(0, 512));
|
|
2319
|
+
}
|
|
2291
2320
|
};
|
|
2292
2321
|
const handlePointer = (event) => {
|
|
2293
2322
|
const now = performance.now();
|
|
@@ -2352,34 +2381,17 @@ export function createOpaquePluginBootstrapHTML(options = {}) {
|
|
|
2352
2381
|
listen("wheel", handleWheel);
|
|
2353
2382
|
listen("focus", () => sendInput({ type: "focus" }));
|
|
2354
2383
|
listen("blur", () => sendInput({ type: "blur" }));
|
|
2355
|
-
const observer = new ResizeObserver(
|
|
2356
|
-
|
|
2357
|
-
catch (error) { fail(error && error.message || "plugin canvas resize exceeded renderer limits"); }
|
|
2358
|
-
});
|
|
2359
|
-
observer.observe(canvas);
|
|
2360
|
-
const runtime = { canvas, description: undefined, dispose: undefined, pixelCount: 0 };
|
|
2384
|
+
const observer = new ResizeObserver(updateCanvas);
|
|
2385
|
+
const runtime = { canvas, description: undefined, dispose: undefined, metrics: undefined, openRequestID: id };
|
|
2361
2386
|
const dispose = () => {
|
|
2362
2387
|
observer.disconnect();
|
|
2363
2388
|
for (const [type, handler] of listeners) canvas.removeEventListener(type, handler);
|
|
2364
2389
|
runtime.description?.remove();
|
|
2365
2390
|
};
|
|
2366
2391
|
runtime.dispose = dispose;
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
let totalPixels = metrics.pixelCount;
|
|
2371
|
-
for (const runtime of canvasRuntimes.values()) totalPixels += runtime.pixelCount;
|
|
2372
|
-
if (canvasRuntimes.size >= maxCanvasCount || totalPixels > maxCanvasTotalPixels) throw new Error("plugin canvas resources exceed renderer limits");
|
|
2373
|
-
runtime.pixelCount = metrics.pixelCount;
|
|
2374
|
-
canvasRuntimes.set(canvasID, runtime);
|
|
2375
|
-
completeWorkerRequest(id);
|
|
2376
|
-
sendWorkerTransfer({ type: "redevplugin.ui.canvas.ready", id, canvas_id: canvasID, canvas: offscreen, css_width: metrics.cssWidth, css_height: metrics.cssHeight, device_pixel_ratio: metrics.devicePixelRatio }, [offscreen]);
|
|
2377
|
-
sendResize();
|
|
2378
|
-
} catch (error) {
|
|
2379
|
-
dispose();
|
|
2380
|
-
completeWorkerRequest(id);
|
|
2381
|
-
rejectWorkerRequest(id, String(error && error.message || "plugin canvas transfer failed").slice(0, 512));
|
|
2382
|
-
}
|
|
2392
|
+
canvasRuntimes.set(canvasID, runtime);
|
|
2393
|
+
observer.observe(canvas);
|
|
2394
|
+
updateCanvas();
|
|
2383
2395
|
};
|
|
2384
2396
|
const scheduleWorkerHeartbeat = () => {
|
|
2385
2397
|
if (workerHeartbeatTimer) clearTimeout(workerHeartbeatTimer);
|
|
@@ -2545,6 +2557,13 @@ export function createOpaquePluginBootstrapHTML(options = {}) {
|
|
|
2545
2557
|
}
|
|
2546
2558
|
if (exactKeys(message, ["type", "id"]) && message.type === "redevplugin.bridge.cancel" && typeof message.id === "string") {
|
|
2547
2559
|
if (!pendingWorkerRequests.has(message.id)) return rejectWorkerRequest(message.id, "plugin request is not pending");
|
|
2560
|
+
for (const [canvasID, runtime] of canvasRuntimes) {
|
|
2561
|
+
if (runtime.openRequestID !== message.id) continue;
|
|
2562
|
+
runtime.dispose();
|
|
2563
|
+
canvasRuntimes.delete(canvasID);
|
|
2564
|
+
completeWorkerRequest(message.id);
|
|
2565
|
+
return;
|
|
2566
|
+
}
|
|
2548
2567
|
if (pendingImageRequests.has(message.id)) {
|
|
2549
2568
|
pendingImageRequests.delete(message.id);
|
|
2550
2569
|
completeWorkerRequest(message.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floegence/redevplugin-ui",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.27",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"dist"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@floegence/redevplugin-contracts": "3.0.
|
|
43
|
+
"@floegence/redevplugin-contracts": "3.0.27"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|