@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.
Files changed (2) hide show
  1. package/dist/surface.js +49 -30
  2. 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
- const timer = setTimeout(() => {
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
- const cssWidth = Math.ceil(rect.width || canvas.width || 1);
2257
- const cssHeight = Math.ceil(rect.height || canvas.height || 1);
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 pixelCount = Math.ceil(cssWidth * devicePixelRatio) * Math.ceil(cssHeight * devicePixelRatio);
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
- const runtime = canvasRuntimes.get(canvasID);
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) runtime.pixelCount = metrics.pixelCount;
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
- return metrics;
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
- try { sendResize(); }
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
- try {
2368
- const offscreen = canvas.transferControlToOffscreen();
2369
- const metrics = canvasMetrics(canvas);
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.26",
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.26"
43
+ "@floegence/redevplugin-contracts": "3.0.27"
44
44
  },
45
45
  "publishConfig": {
46
46
  "access": "public"