@oh-just-another/renderer-canvas 0.57.0

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 (81) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +21 -0
  3. package/README.md +36 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/canvas-target.d.ts +83 -0
  6. package/dist/canvas-target.d.ts.map +1 -0
  7. package/dist/canvas-target.js +208 -0
  8. package/dist/canvas-target.js.map +1 -0
  9. package/dist/canvas-text-shaper.d.ts +11 -0
  10. package/dist/canvas-text-shaper.d.ts.map +1 -0
  11. package/dist/canvas-text-shaper.js +56 -0
  12. package/dist/canvas-text-shaper.js.map +1 -0
  13. package/dist/constants.d.ts +52 -0
  14. package/dist/constants.d.ts.map +1 -0
  15. package/dist/constants.js +52 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/hi-dpi.d.ts +26 -0
  18. package/dist/hi-dpi.d.ts.map +1 -0
  19. package/dist/hi-dpi.js +62 -0
  20. package/dist/hi-dpi.js.map +1 -0
  21. package/dist/image-source.d.ts +3 -0
  22. package/dist/image-source.d.ts.map +1 -0
  23. package/dist/image-source.js +65 -0
  24. package/dist/image-source.js.map +1 -0
  25. package/dist/index.d.ts +15 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +24 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/layered-canvas.d.ts +46 -0
  30. package/dist/layered-canvas.d.ts.map +1 -0
  31. package/dist/layered-canvas.js +95 -0
  32. package/dist/layered-canvas.js.map +1 -0
  33. package/dist/layered-surface.d.ts +75 -0
  34. package/dist/layered-surface.d.ts.map +1 -0
  35. package/dist/layered-surface.js +262 -0
  36. package/dist/layered-surface.js.map +1 -0
  37. package/dist/offscreen.d.ts +30 -0
  38. package/dist/offscreen.d.ts.map +1 -0
  39. package/dist/offscreen.js +50 -0
  40. package/dist/offscreen.js.map +1 -0
  41. package/dist/recording-target.d.ts +203 -0
  42. package/dist/recording-target.d.ts.map +1 -0
  43. package/dist/recording-target.js +260 -0
  44. package/dist/recording-target.js.map +1 -0
  45. package/dist/render-worker.d.ts +2 -0
  46. package/dist/render-worker.d.ts.map +1 -0
  47. package/dist/render-worker.js +112 -0
  48. package/dist/render-worker.js.map +1 -0
  49. package/dist/tile-compositor.d.ts +44 -0
  50. package/dist/tile-compositor.d.ts.map +1 -0
  51. package/dist/tile-compositor.js +110 -0
  52. package/dist/tile-compositor.js.map +1 -0
  53. package/dist/webgl2-color.d.ts +13 -0
  54. package/dist/webgl2-color.d.ts.map +1 -0
  55. package/dist/webgl2-color.js +26 -0
  56. package/dist/webgl2-color.js.map +1 -0
  57. package/dist/webgl2-curve.d.ts +46 -0
  58. package/dist/webgl2-curve.d.ts.map +1 -0
  59. package/dist/webgl2-curve.js +162 -0
  60. package/dist/webgl2-curve.js.map +1 -0
  61. package/dist/webgl2-ellipse.d.ts +52 -0
  62. package/dist/webgl2-ellipse.d.ts.map +1 -0
  63. package/dist/webgl2-ellipse.js +171 -0
  64. package/dist/webgl2-ellipse.js.map +1 -0
  65. package/dist/webgl2-msdf-text.d.ts +84 -0
  66. package/dist/webgl2-msdf-text.d.ts.map +1 -0
  67. package/dist/webgl2-msdf-text.js +306 -0
  68. package/dist/webgl2-msdf-text.js.map +1 -0
  69. package/dist/webgl2-stroke.d.ts +52 -0
  70. package/dist/webgl2-stroke.d.ts.map +1 -0
  71. package/dist/webgl2-stroke.js +318 -0
  72. package/dist/webgl2-stroke.js.map +1 -0
  73. package/dist/webgl2-target.d.ts +311 -0
  74. package/dist/webgl2-target.d.ts.map +1 -0
  75. package/dist/webgl2-target.js +1368 -0
  76. package/dist/webgl2-target.js.map +1 -0
  77. package/dist/webgpu-detect.d.ts +30 -0
  78. package/dist/webgpu-detect.d.ts.map +1 -0
  79. package/dist/webgpu-detect.js +74 -0
  80. package/dist/webgpu-detect.js.map +1 -0
  81. package/package.json +67 -0
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Tunable thresholds for the canvas backend.
3
+ */
4
+ /**
5
+ * Element count above which a host that has wired up `WorkerPool` and
6
+ * `transferCanvasToWorker` should prefer the worker-rendering path.
7
+ * Below this number the per-postMessage overhead dominates and a
8
+ * main-thread render is faster.
9
+ *
10
+ * At 50k shapes a full-scene main-thread render at low zoom starts to
11
+ * drop frames; below 50k the main-thread path stays within 16 ms via
12
+ * viewport culling and ElementCache.
13
+ */
14
+ export declare const LARGE_SCENE_WORKER_THRESHOLD = 50000;
15
+ /**
16
+ * LRU cap on `WebGL2Target.textBitmaps` — the OffscreenCanvas cache
17
+ * used by the fallback text path (when no MSDF shaper is available:
18
+ * Safari without WASM, older modules). Key is `text|font|color`; each
19
+ * combination maps to one OffscreenCanvas plus one GPU texture after
20
+ * the first drawImage.
21
+ *
22
+ * Without a cap the Map grows unbounded: 1000 text mutations (renaming
23
+ * labels, comments, counter numbers) produce 1000 live GPU textures
24
+ * plus 1000 OffscreenCanvases on the JS heap, leading to VRAM pressure
25
+ * over a long session.
26
+ *
27
+ * 256 entries — a typical 200-shape scene uses 50-150 unique texts; a
28
+ * 1.5-3x margin covers editing without thrashing. On overflow the LRU
29
+ * evicts the least-recently-used entry: `Map.delete` plus
30
+ * `gl.deleteTexture` for the associated WebGL texture (deterministic
31
+ * VRAM release).
32
+ *
33
+ * Hosts with an MSDF shaper (WasmTextShaper) never use this path —
34
+ * text renders through an atlas, without an OffscreenCanvas round-trip.
35
+ */
36
+ export declare const WEBGL2_TEXT_BITMAP_CACHE_CAP = 256;
37
+ /**
38
+ * LRU cap on `WebGL2Target.textures` — image-source to WebGLTexture
39
+ * cache used by `drawImage`. Each entry holds a GPU texture
40
+ * (`width × height × 4` bytes VRAM).
41
+ *
42
+ * A cap plus an explicit `gl.deleteTexture` on evict makes the release
43
+ * deterministic.
44
+ *
45
+ * 64 — a typical scene rarely has more than 10-20 unique images at
46
+ * once; a 3-6x margin covers intensive editing without thrashing. On
47
+ * overflow the coldest image textures are unloaded; the next drawImage
48
+ * of the same image source re-uploads via `gl.texImage2D` (extra frame
49
+ * cost).
50
+ */
51
+ export declare const WEBGL2_IMAGE_TEXTURE_CACHE_CAP = 64;
52
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,KAAK,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Tunable thresholds for the canvas backend.
3
+ */
4
+ /**
5
+ * Element count above which a host that has wired up `WorkerPool` and
6
+ * `transferCanvasToWorker` should prefer the worker-rendering path.
7
+ * Below this number the per-postMessage overhead dominates and a
8
+ * main-thread render is faster.
9
+ *
10
+ * At 50k shapes a full-scene main-thread render at low zoom starts to
11
+ * drop frames; below 50k the main-thread path stays within 16 ms via
12
+ * viewport culling and ElementCache.
13
+ */
14
+ export const LARGE_SCENE_WORKER_THRESHOLD = 50_000;
15
+ /**
16
+ * LRU cap on `WebGL2Target.textBitmaps` — the OffscreenCanvas cache
17
+ * used by the fallback text path (when no MSDF shaper is available:
18
+ * Safari without WASM, older modules). Key is `text|font|color`; each
19
+ * combination maps to one OffscreenCanvas plus one GPU texture after
20
+ * the first drawImage.
21
+ *
22
+ * Without a cap the Map grows unbounded: 1000 text mutations (renaming
23
+ * labels, comments, counter numbers) produce 1000 live GPU textures
24
+ * plus 1000 OffscreenCanvases on the JS heap, leading to VRAM pressure
25
+ * over a long session.
26
+ *
27
+ * 256 entries — a typical 200-shape scene uses 50-150 unique texts; a
28
+ * 1.5-3x margin covers editing without thrashing. On overflow the LRU
29
+ * evicts the least-recently-used entry: `Map.delete` plus
30
+ * `gl.deleteTexture` for the associated WebGL texture (deterministic
31
+ * VRAM release).
32
+ *
33
+ * Hosts with an MSDF shaper (WasmTextShaper) never use this path —
34
+ * text renders through an atlas, without an OffscreenCanvas round-trip.
35
+ */
36
+ export const WEBGL2_TEXT_BITMAP_CACHE_CAP = 256;
37
+ /**
38
+ * LRU cap on `WebGL2Target.textures` — image-source to WebGLTexture
39
+ * cache used by `drawImage`. Each entry holds a GPU texture
40
+ * (`width × height × 4` bytes VRAM).
41
+ *
42
+ * A cap plus an explicit `gl.deleteTexture` on evict makes the release
43
+ * deterministic.
44
+ *
45
+ * 64 — a typical scene rarely has more than 10-20 unique images at
46
+ * once; a 3-6x margin covers intensive editing without thrashing. On
47
+ * overflow the coldest image textures are unloaded; the next drawImage
48
+ * of the same image source re-uploads via `gl.texImage2D` (extra frame
49
+ * cost).
50
+ */
51
+ export const WEBGL2_IMAGE_TEXTURE_CACHE_CAP = 64;
52
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Configures a `<canvas>` for hi-DPI rendering. Sets the bitmap size to
3
+ * `width * dpr` × `height * dpr`, the CSS size to `width` × `height`, and the
4
+ * context transform to `scale(dpr, dpr)` so subsequent draw calls operate in
5
+ * CSS pixels. Returns the DPR used so callers can recompute on monitor changes.
6
+ *
7
+ * IDEMPOTENT: assigning `canvas.width` / `canvas.height` resets both
8
+ * the bitmap AND the context transform — even when the new value equals
9
+ * the old. Hosts call this from a `ResizeObserver` that may fire with
10
+ * the same size on attach; without the guard each tick would wipe the
11
+ * existing canvas content. We early-return when bitmap + CSS size +
12
+ * DPR are already exactly right.
13
+ */
14
+ export declare const setupHiDpi: (canvas: HTMLCanvasElement, width: number, height: number, dpr?: number) => number;
15
+ /**
16
+ * Hi-DPI sizing for canvases that will host a non-2D context (WebGL2,
17
+ * WebGPU). Same bitmap + CSS dimensions as `setupHiDpi`, but skips
18
+ * the `getContext("2d")` call — `HTMLCanvasElement` only ever yields
19
+ * one *kind* of context, so touching `2d` on a canvas destined for
20
+ * `webgl2` poisons the slot and every subsequent `getContext("webgl2")`
21
+ * returns null. Use this for the WebGL2 main canvas in
22
+ * `WebGL2LayeredSurface`; the WebGL2 viewport is set inside
23
+ * `WebGL2Target` after the GL context is obtained.
24
+ */
25
+ export declare const setupHiDpiNoContext: (canvas: HTMLCanvasElement, width: number, height: number, dpr?: number) => number;
26
+ //# sourceMappingURL=hi-dpi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hi-dpi.d.ts","sourceRoot":"","sources":["../src/hi-dpi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,GACrB,QAAQ,iBAAiB,EACzB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,MAAK,MAAqC,KACzC,MAqBF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,iBAAiB,EACzB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,MAAK,MAAqC,KACzC,MAkBF,CAAC"}
package/dist/hi-dpi.js ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Configures a `<canvas>` for hi-DPI rendering. Sets the bitmap size to
3
+ * `width * dpr` × `height * dpr`, the CSS size to `width` × `height`, and the
4
+ * context transform to `scale(dpr, dpr)` so subsequent draw calls operate in
5
+ * CSS pixels. Returns the DPR used so callers can recompute on monitor changes.
6
+ *
7
+ * IDEMPOTENT: assigning `canvas.width` / `canvas.height` resets both
8
+ * the bitmap AND the context transform — even when the new value equals
9
+ * the old. Hosts call this from a `ResizeObserver` that may fire with
10
+ * the same size on attach; without the guard each tick would wipe the
11
+ * existing canvas content. We early-return when bitmap + CSS size +
12
+ * DPR are already exactly right.
13
+ */
14
+ export const setupHiDpi = (canvas, width, height, dpr = window.devicePixelRatio || 1) => {
15
+ const targetW = Math.round(width * dpr);
16
+ const targetH = Math.round(height * dpr);
17
+ const cssW = `${width}px`;
18
+ const cssH = `${height}px`;
19
+ if (canvas.width === targetW &&
20
+ canvas.height === targetH &&
21
+ canvas.style.width === cssW &&
22
+ canvas.style.height === cssH) {
23
+ return dpr;
24
+ }
25
+ canvas.width = targetW;
26
+ canvas.height = targetH;
27
+ canvas.style.width = cssW;
28
+ canvas.style.height = cssH;
29
+ const ctx = canvas.getContext("2d");
30
+ if (!ctx)
31
+ throw new Error("Failed to obtain 2D context");
32
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
33
+ return dpr;
34
+ };
35
+ /**
36
+ * Hi-DPI sizing for canvases that will host a non-2D context (WebGL2,
37
+ * WebGPU). Same bitmap + CSS dimensions as `setupHiDpi`, but skips
38
+ * the `getContext("2d")` call — `HTMLCanvasElement` only ever yields
39
+ * one *kind* of context, so touching `2d` on a canvas destined for
40
+ * `webgl2` poisons the slot and every subsequent `getContext("webgl2")`
41
+ * returns null. Use this for the WebGL2 main canvas in
42
+ * `WebGL2LayeredSurface`; the WebGL2 viewport is set inside
43
+ * `WebGL2Target` after the GL context is obtained.
44
+ */
45
+ export const setupHiDpiNoContext = (canvas, width, height, dpr = window.devicePixelRatio || 1) => {
46
+ const targetW = Math.round(width * dpr);
47
+ const targetH = Math.round(height * dpr);
48
+ const cssW = `${width}px`;
49
+ const cssH = `${height}px`;
50
+ if (canvas.width === targetW &&
51
+ canvas.height === targetH &&
52
+ canvas.style.width === cssW &&
53
+ canvas.style.height === cssH) {
54
+ return dpr;
55
+ }
56
+ canvas.width = targetW;
57
+ canvas.height = targetH;
58
+ canvas.style.width = cssW;
59
+ canvas.style.height = cssH;
60
+ return dpr;
61
+ };
62
+ //# sourceMappingURL=hi-dpi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hi-dpi.js","sourceRoot":"","sources":["../src/hi-dpi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,MAAyB,EACzB,KAAa,EACb,MAAc,EACd,MAAc,MAAM,CAAC,gBAAgB,IAAI,CAAC,EAClC,EAAE;IACV,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC;IAC1B,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC;IAC3B,IACE,MAAM,CAAC,KAAK,KAAK,OAAO;QACxB,MAAM,CAAC,MAAM,KAAK,OAAO;QACzB,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI;QAC3B,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,IAAI,EAC5B,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC;IACvB,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACzD,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAyB,EACzB,KAAa,EACb,MAAc,EACd,MAAc,MAAM,CAAC,gBAAgB,IAAI,CAAC,EAClC,EAAE;IACV,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC;IAC1B,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC;IAC3B,IACE,MAAM,CAAC,KAAK,KAAK,OAAO;QACxB,MAAM,CAAC,MAAM,KAAK,OAAO;QACzB,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI;QAC3B,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,IAAI,EAC5B,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC;IACvB,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,GAAG,CAAC;AACb,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const isDrawableImageSource: (value: unknown) => value is CanvasImageSource;
2
+ export declare const warnSkippedImage: (value: unknown) => void;
3
+ //# sourceMappingURL=image-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-source.d.ts","sourceRoot":"","sources":["../src/image-source.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,qBAAqB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,iBAU/D,CAAC;AAUF,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,IAoBjD,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Runtime guard: is `value` an actual drawable image source that
3
+ * `ctx.drawImage` / `gl.texImage2D` will accept?
4
+ *
5
+ * Needed because a deserialized scene can carry a **garbage**
6
+ * `metadata.image`: a live `<img>` DOM element serialises to `{}`
7
+ * via `JSON.stringify`, so a scene restored from localStorage has
8
+ * `metadata.image === {}` — a truthy object that passes a naive
9
+ * `typeof === "object"` check but throws inside `drawImage`
10
+ * ("provided value is not of type …") / `texImage2D` ("overload
11
+ * resolution failed").
12
+ *
13
+ * The check is environment-safe: each constructor is probed for
14
+ * existence first (workers / SSR / older browsers may lack some),
15
+ * so it never throws on a missing global. A bare `{}` matches none
16
+ * of them and is rejected.
17
+ */
18
+ const DRAWABLE_CTOR_NAMES = [
19
+ "HTMLImageElement",
20
+ "HTMLCanvasElement",
21
+ "HTMLVideoElement",
22
+ "ImageBitmap",
23
+ "OffscreenCanvas",
24
+ "SVGImageElement",
25
+ "VideoFrame",
26
+ ];
27
+ export const isDrawableImageSource = (value) => {
28
+ if (typeof value !== "object" || value === null)
29
+ return false;
30
+ const g = globalThis;
31
+ for (const name of DRAWABLE_CTOR_NAMES) {
32
+ const ctor = g[name];
33
+ if (typeof ctor === "function" && value instanceof ctor) {
34
+ return true;
35
+ }
36
+ }
37
+ return false;
38
+ };
39
+ /**
40
+ * Warn (once per distinct kind) when an image draw is skipped because
41
+ * the handle isn't drawable. Throttled by a module-level `Set` so a
42
+ * per-frame render loop doesn't spam the console — but the host still
43
+ * sees that an image failed to render and the likely cause.
44
+ */
45
+ const warnedImageKinds = new Set();
46
+ export const warnSkippedImage = (value) => {
47
+ if (typeof console === "undefined")
48
+ return;
49
+ const kind = typeof value === "string"
50
+ ? value.startsWith("blob:")
51
+ ? "dead-blob-url"
52
+ : "string-src"
53
+ : value === null || value === undefined
54
+ ? "empty"
55
+ : "stale-object"; // e.g. a {} from a serialised <img>
56
+ if (warnedImageKinds.has(kind))
57
+ return;
58
+ warnedImageKinds.add(kind);
59
+ console.warn(`[renderer] skipped a non-drawable image source (kind: ${kind}). ` +
60
+ "The shape's image handle isn't a live HTMLImageElement / canvas / " +
61
+ "bitmap — likely a scene restored from storage where the <img> was " +
62
+ "lost (metadata.image) and src is a dead blob: URL. The image won't " +
63
+ "render until rehydrated from Scene.files.");
64
+ };
65
+ //# sourceMappingURL=image-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-source.js","sourceRoot":"","sources":["../src/image-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,mBAAmB,GAAG;IAC1B,kBAAkB;IAClB,mBAAmB;IACnB,kBAAkB;IAClB,aAAa;IACb,iBAAiB;IACjB,iBAAiB;IACjB,YAAY;CACJ,CAAC;AAEX,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAc,EAA8B,EAAE;IAClF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,UAAqC,CAAC;IAChD,KAAK,MAAM,IAAI,IAAI,mBAAmB,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QACrB,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,KAAK,YAAa,IAA0C,EAAE,CAAC;YAC/F,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;AAE3C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAc,EAAQ,EAAE;IACvD,IAAI,OAAO,OAAO,KAAK,WAAW;QAAE,OAAO;IAC3C,MAAM,IAAI,GACR,OAAO,KAAK,KAAK,QAAQ;QACvB,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;YACzB,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,YAAY;QAChB,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YACrC,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,cAAc,CAAC,CAAC,oCAAoC;IAC5D,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO;IACvC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE3B,OAAO,CAAC,IAAI,CACV,yDAAyD,IAAI,KAAK;QAChE,oEAAoE;QACpE,oEAAoE;QACpE,qEAAqE;QACrE,2CAA2C,CAC9C,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ export { Canvas2DTarget } from "./canvas-target.js";
2
+ export { Canvas2DTextShaper } from "./canvas-text-shaper.js";
3
+ export { WebGL2Target } from "./webgl2-target.js";
4
+ export { renderViaTiles, type RenderViaTilesOptions } from "./tile-compositor.js";
5
+ export { setupHiDpi } from "./hi-dpi.js";
6
+ export { LayeredCanvas, type LayeredCanvasOptions } from "./layered-canvas.js";
7
+ export { supportsOffscreenCanvas, createOffscreenCanvas2DTarget, transferCanvasToWorker, } from "./offscreen.js";
8
+ export { WorkerPool, LayerWorkerPool } from "@oh-just-another/renderer-workers";
9
+ export { RecordingTarget, replayCommands, type RenderCommand, } from "./recording-target.js";
10
+ export { createLayeredSurface, createLayeredSurfaceWithFallback, type LayeredSurface, type RendererBackend, type CreateLayeredSurfaceOptions, } from "./layered-surface.js";
11
+ export { isWebGPUAvailable, isWebGL2Available, pickAvailableBackend, } from "./webgpu-detect.js";
12
+ export { LARGE_SCENE_WORKER_THRESHOLD } from "./constants.js";
13
+ export { installBuiltinRenderers, wrapText } from "@oh-just-another/renderer-core";
14
+ export type { WrapOptions, WrappedLine } from "@oh-just-another/renderer-core";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAChF,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,gCAAgC,EAChC,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,2BAA2B,GACjC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAK9D,OAAO,EAAE,uBAAuB,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AACnF,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ export { Canvas2DTarget } from "./canvas-target.js";
2
+ export { Canvas2DTextShaper } from "./canvas-text-shaper.js";
3
+ export { WebGL2Target } from "./webgl2-target.js";
4
+ export { renderViaTiles } from "./tile-compositor.js";
5
+ export { setupHiDpi } from "./hi-dpi.js";
6
+ export { LayeredCanvas } from "./layered-canvas.js";
7
+ export { supportsOffscreenCanvas, createOffscreenCanvas2DTarget, transferCanvasToWorker, } from "./offscreen.js";
8
+ // WorkerPool / LayerWorkerPool live in @oh-just-another/renderer-workers
9
+ // (backend-neutral primitives don't belong in the Canvas-specific package).
10
+ // Re-export to keep existing imports working.
11
+ export { WorkerPool, LayerWorkerPool } from "@oh-just-another/renderer-workers";
12
+ export { RecordingTarget, replayCommands, } from "./recording-target.js";
13
+ export { createLayeredSurface, createLayeredSurfaceWithFallback, } from "./layered-surface.js";
14
+ export { isWebGPUAvailable, isWebGL2Available, pickAvailableBackend, } from "./webgpu-detect.js";
15
+ export { LARGE_SCENE_WORKER_THRESHOLD } from "./constants.js";
16
+ // `installBuiltinRenderers` and `wrapText` live in
17
+ // `@oh-just-another/renderer-core` so the SVG / headless backends can share them.
18
+ // These re-exports keep existing imports working.
19
+ export { installBuiltinRenderers, wrapText } from "@oh-just-another/renderer-core";
20
+ // `installBuiltinRenderers()` must be called once before `renderScene`
21
+ // from `@oh-just-another/renderer-core` knows how to draw built-in shapes. It is
22
+ // not auto-invoked so that this package stays `sideEffects: false` and tree-
23
+ // shakeable. Hosts typically call it in their entry file.
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAA8B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,yEAAyE;AACzE,4EAA4E;AAC5E,8CAA8C;AAC9C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAChF,OAAO,EACL,eAAe,EACf,cAAc,GAEf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,gCAAgC,GAIjC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAE9D,mDAAmD;AACnD,kFAAkF;AAClF,kDAAkD;AAClD,OAAO,EAAE,uBAAuB,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAGnF,uEAAuE;AACvE,iFAAiF;AACjF,6EAA6E;AAC7E,0DAA0D"}
@@ -0,0 +1,46 @@
1
+ import { type LayerName } from "@oh-just-another/renderer-core";
2
+ import { Canvas2DTarget } from "./canvas-target.js";
3
+ export interface LayeredCanvasOptions {
4
+ /** Subset of layers to create. Defaults to all three. */
5
+ readonly layers?: readonly LayerName[];
6
+ /** Override `window.devicePixelRatio`. */
7
+ readonly dpr?: number;
8
+ }
9
+ /**
10
+ * Manages one stacked `<canvas>` per layer. The background canvas sits at the
11
+ * bottom of the DOM stack and the overlay at the top, so drawing onto `main`
12
+ * never invalidates the static `background`. Each layer is a `Canvas2DTarget`
13
+ * with its own CSS-pixel coordinate space.
14
+ *
15
+ * The host element gets `position: relative` so absolute children stack on top
16
+ * of each other; each canvas is `position: absolute, inset: 0`.
17
+ */
18
+ export declare class LayeredCanvas {
19
+ private readonly host;
20
+ private readonly layers;
21
+ private readonly canvases;
22
+ private width;
23
+ private height;
24
+ private dprOverride;
25
+ constructor(host: HTMLElement, width: number, height: number, options?: LayeredCanvasOptions);
26
+ /** Get the target for a layer. Throws if the layer was not created. */
27
+ get(name: LayerName): Canvas2DTarget;
28
+ /** Get the underlying `<canvas>` element for a layer. */
29
+ getCanvas(name: LayerName): HTMLCanvasElement;
30
+ /**
31
+ * Resize every canvas in the stack to the new CSS-pixel size.
32
+ *
33
+ * No-op when neither `width` nor `height` changed. Setting `canvas.width`
34
+ * resets the bitmap even when the new value equals the old, so this guard
35
+ * prevents the content getting wiped when a `ResizeObserver` fires with
36
+ * the current box size on its initial attach tick.
37
+ */
38
+ resize(width: number, height: number): void;
39
+ get size(): {
40
+ readonly width: number;
41
+ readonly height: number;
42
+ };
43
+ /** Detach all canvases from the host. After this the instance is unusable. */
44
+ dispose(): void;
45
+ }
46
+ //# sourceMappingURL=layered-canvas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layered-canvas.d.ts","sourceRoot":"","sources":["../src/layered-canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,MAAM,WAAW,oBAAoB;IACnC,yDAAyD;IACzD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4C;IACrE,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAqB;gBAGtC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,oBAAyB;IAkCpC,uEAAuE;IACvE,GAAG,CAAC,IAAI,EAAE,SAAS,GAAG,cAAc;IAMpC,yDAAyD;IACzD,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,iBAAiB;IAM7C;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAY3C,IAAI,IAAI,IAAI;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAE9D;IAED,8EAA8E;IAC9E,OAAO,IAAI,IAAI;CAKhB"}
@@ -0,0 +1,95 @@
1
+ import { LAYER_ORDER } from "@oh-just-another/renderer-core";
2
+ import { Canvas2DTarget } from "./canvas-target.js";
3
+ import { setupHiDpi } from "./hi-dpi.js";
4
+ /**
5
+ * Manages one stacked `<canvas>` per layer. The background canvas sits at the
6
+ * bottom of the DOM stack and the overlay at the top, so drawing onto `main`
7
+ * never invalidates the static `background`. Each layer is a `Canvas2DTarget`
8
+ * with its own CSS-pixel coordinate space.
9
+ *
10
+ * The host element gets `position: relative` so absolute children stack on top
11
+ * of each other; each canvas is `position: absolute, inset: 0`.
12
+ */
13
+ export class LayeredCanvas {
14
+ host;
15
+ layers;
16
+ canvases;
17
+ width;
18
+ height;
19
+ dprOverride;
20
+ constructor(host, width, height, options = {}) {
21
+ this.host = host;
22
+ this.width = width;
23
+ this.height = height;
24
+ this.dprOverride = options.dpr;
25
+ if (getComputedStyle(host).position === "static") {
26
+ host.style.position = "relative";
27
+ }
28
+ const requested = options.layers ?? LAYER_ORDER;
29
+ const layers = new Map();
30
+ const canvases = new Map();
31
+ for (const name of LAYER_ORDER) {
32
+ if (!requested.includes(name))
33
+ continue;
34
+ const canvas = host.ownerDocument.createElement("canvas");
35
+ canvas.dataset.layer = name;
36
+ canvas.style.position = "absolute";
37
+ canvas.style.inset = "0";
38
+ canvas.style.pointerEvents = name === "overlay" ? "auto" : "none";
39
+ host.appendChild(canvas);
40
+ const dpr = setupHiDpi(canvas, width, height, this.dprOverride);
41
+ const ctx = canvas.getContext("2d");
42
+ if (!ctx)
43
+ throw new Error("Failed to obtain 2D context");
44
+ layers.set(name, new Canvas2DTarget(ctx, width, height, dpr));
45
+ canvases.set(name, canvas);
46
+ }
47
+ this.layers = layers;
48
+ this.canvases = canvases;
49
+ }
50
+ /** Get the target for a layer. Throws if the layer was not created. */
51
+ get(name) {
52
+ const target = this.layers.get(name);
53
+ if (!target)
54
+ throw new Error(`Layer not created: ${name}`);
55
+ return target;
56
+ }
57
+ /** Get the underlying `<canvas>` element for a layer. */
58
+ getCanvas(name) {
59
+ const canvas = this.canvases.get(name);
60
+ if (!canvas)
61
+ throw new Error(`Layer not created: ${name}`);
62
+ return canvas;
63
+ }
64
+ /**
65
+ * Resize every canvas in the stack to the new CSS-pixel size.
66
+ *
67
+ * No-op when neither `width` nor `height` changed. Setting `canvas.width`
68
+ * resets the bitmap even when the new value equals the old, so this guard
69
+ * prevents the content getting wiped when a `ResizeObserver` fires with
70
+ * the current box size on its initial attach tick.
71
+ */
72
+ resize(width, height) {
73
+ if (this.width === width && this.height === height)
74
+ return;
75
+ this.width = width;
76
+ this.height = height;
77
+ for (const [name, canvas] of this.canvases) {
78
+ const dpr = setupHiDpi(canvas, width, height, this.dprOverride);
79
+ const layer = this.layers.get(name);
80
+ if (layer === undefined)
81
+ continue;
82
+ layer.resize(width, height, dpr);
83
+ }
84
+ }
85
+ get size() {
86
+ return { width: this.width, height: this.height };
87
+ }
88
+ /** Detach all canvases from the host. After this the instance is unusable. */
89
+ dispose() {
90
+ for (const canvas of this.canvases.values()) {
91
+ canvas.remove();
92
+ }
93
+ }
94
+ }
95
+ //# sourceMappingURL=layered-canvas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layered-canvas.js","sourceRoot":"","sources":["../src/layered-canvas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { type LayerName, type RenderTarget } from "@oh-just-another/renderer-core";
2
+ /**
3
+ * Backend selector for `createLayeredSurface`.
4
+ *
5
+ * - `canvas2d` per-layer `Canvas2DTarget` over stacked DOM canvases.
6
+ * - `webgl2` per-layer `WebGL2Target` over stacked DOM canvases
7
+ * (each with its own WebGL2 context).
8
+ * - `offscreen` per-layer `RecordingTarget` on the main thread,
9
+ * forwarded into a dedicated worker per layer that
10
+ * owns the transferred OffscreenCanvas and replays
11
+ * the buffered command stream.
12
+ */
13
+ export type RendererBackend = "canvas2d" | "webgl2" | "offscreen";
14
+ /**
15
+ * Common interface every backend's surface exposes. The Editor reaches
16
+ * each layer's `RenderTarget` via `get(name)`; the host calls
17
+ * `present()` after each frame so backends with deferred submission
18
+ * (offscreen) can ship their buffered commands to workers. Synchronous
19
+ * backends (`canvas2d`, `webgl2`) implement `present` as a no-op.
20
+ */
21
+ export interface LayeredSurface {
22
+ readonly backend: RendererBackend;
23
+ get(name: LayerName): RenderTarget;
24
+ getCanvas(name: LayerName): HTMLCanvasElement;
25
+ resize(width: number, height: number): void;
26
+ readonly size: {
27
+ readonly width: number;
28
+ readonly height: number;
29
+ };
30
+ /** Hook called once per frame after the Editor finishes drawing. */
31
+ present(): void;
32
+ dispose(): void;
33
+ }
34
+ export interface CreateLayeredSurfaceOptions {
35
+ readonly backend?: RendererBackend;
36
+ /**
37
+ * Override the worker factory used by the `offscreen` backend.
38
+ * Hosts pass a Vite-aware factory:
39
+ *
40
+ * new Worker(new URL("@oh-just-another/renderer-canvas/render-worker",
41
+ * import.meta.url), { type: "module" })
42
+ *
43
+ * because `import.meta.url` resolution depends on the bundler.
44
+ * When missing, the offscreen backend throws on construction (there
45
+ * is no sensible cross-bundler default).
46
+ */
47
+ readonly workerFactory?: () => Worker;
48
+ }
49
+ /**
50
+ * Factory entry point. Picks the right backend implementation, builds
51
+ * it, and returns the common `LayeredSurface` interface. Canvas2D is
52
+ * always safe; WebGL2 throws if the browser doesn't expose
53
+ * `getContext("webgl2")`; Offscreen throws if either `OffscreenCanvas`
54
+ * or `transferControlToOffscreen` is missing.
55
+ *
56
+ * The host (DiagramRoot) catches errors and falls back to `canvas2d`
57
+ * automatically when an opt-in backend isn't supported.
58
+ */
59
+ export declare const createLayeredSurface: (host: HTMLElement, width: number, height: number, options?: CreateLayeredSurfaceOptions) => LayeredSurface;
60
+ /**
61
+ * Build the requested surface and fall back to `canvas2d` if the
62
+ * opt-in backend throws (no WebGL2 / no OffscreenCanvas / per-page
63
+ * WebGL context limit hit). On fallback, `onFallback` is invoked
64
+ * with the original error so the host can surface a toast / log.
65
+ *
66
+ * Use this when the backend is user-controlled (demo dropdown,
67
+ * URL param) so a misclick doesn't crash the React tree. Hosts
68
+ * that want hard failures should call `createLayeredSurface`
69
+ * directly and handle the throw themselves.
70
+ */
71
+ export declare const createLayeredSurfaceWithFallback: (host: HTMLElement, width: number, height: number, options?: CreateLayeredSurfaceOptions, onFallback?: (backend: RendererBackend, error: unknown) => void) => {
72
+ surface: LayeredSurface;
73
+ effectiveBackend: RendererBackend;
74
+ };
75
+ //# sourceMappingURL=layered-surface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layered-surface.d.ts","sourceRoot":"","sources":["../src/layered-surface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAMhG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,GAAG,CAAC,IAAI,EAAE,SAAS,GAAG,YAAY,CAAC;IACnC,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,iBAAiB,CAAC;IAC9C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,oEAAoE;IACpE,OAAO,IAAI,IAAI,CAAC;IAChB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC;IACnC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,MAAM,CAAC;CACvC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,WAAW,EACjB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,UAAS,2BAAgC,KACxC,cAeF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gCAAgC,GAC3C,MAAM,WAAW,EACjB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,UAAS,2BAAgC,EACzC,aAAa,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,KAC9D;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,gBAAgB,EAAE,eAAe,CAAA;CAY9D,CAAC"}