canvu-react 0.3.10 → 0.3.12

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/react.d.cts CHANGED
@@ -1,24 +1,16 @@
1
+ import { I as IndexedDbImageStore } from './asset-hydration-3Iv5xHxM.cjs';
2
+ export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-3Iv5xHxM.cjs';
1
3
  import { V as VectorSceneItem } from './types-CB0TZZuk.cjs';
2
- import { V as VectorViewportAssetKind, a as VectorViewportAssetStore, b as VectorToolDefinition, C as CanvasPlugin, B as BoardComponentPosition } from './types-BtLGGw0r.cjs';
3
- export { c as CanvasPluginComponentProps, d as CanvasPluginContribution, e as CanvasPluginItemsChangeMiddlewareContext, f as CanvasPluginRenderContext, g as CanvuChromeActiveToolStyle, h as CanvuChromeContext, i as CanvuChromeContextValue, j as CanvuChromeSelectionStyleChange, k as CanvuPluginContext, l as CanvuPluginContextValue, m as CanvuPluginViewportSnapshot, n as CustomShapePlacementOptions, N as NavMenu, o as NavMenuProps, P as PlacementPreview, p as VectorCanvas, q as VectorCanvasBody, r as VectorCanvasHeader, s as VectorCanvasMain, t as VectorCanvasRoot, u as VectorCanvasSlotProps, v as VectorCanvasSpacePosition, w as VectorCanvasSpaceProps, x as VectorCanvasToolbar, y as VectorCanvasToolbarProps, z as VectorCanvasViewportSurface, A as VectorSelectionInspector, D as VectorSelectionInspectorProps, E as VectorViewport, F as VectorViewportAssetResolveRequest, G as VectorViewportAssetResolveResult, H as VectorViewportAssetUploadRequest, I as VectorViewportAssetUploadResult, J as VectorViewportHandle, K as VectorViewportProps, W as WorldPointerDownDetail, L as createCanvuPlugin, M as getBoardPositionStyle, O as useCanvuChromeContext, Q as useCanvuDocumentContext, R as useCanvuPluginContext, S as useCanvuPluginContribution, T as useCanvuResolvedTools, U as useCanvuViewportContext } from './types-BtLGGw0r.cjs';
4
- import { V as VectorCanvasPersistenceAdapter, a as VectorCanvasRemoteAdapter } from './types-B58i5k-u.cjs';
5
- export { b as VectorCanvasSnapshot } from './types-B58i5k-u.cjs';
4
+ import { V as VectorViewportAssetKind, u as VectorViewportAssetStore } from './shape-builders-DFudWDFI.cjs';
5
+ export { v as VectorViewportAssetHydrationRequest, w as VectorViewportAssetResolveRequest, x as VectorViewportAssetResolveResult, y as VectorViewportAssetUploadRequest, z as VectorViewportAssetUploadResult } from './shape-builders-DFudWDFI.cjs';
6
+ import { V as VectorToolDefinition, C as CanvasPlugin, B as BoardComponentPosition } from './types-CTyASYIm.cjs';
7
+ export { a as CanvasPluginComponentProps, b as CanvasPluginContribution, c as CanvasPluginItemsChangeMiddlewareContext, d as CanvasPluginRenderContext, e as CanvuChromeActiveToolStyle, f as CanvuChromeContext, g as CanvuChromeContextValue, h as CanvuChromeSelectionStyleChange, i as CanvuPluginContext, j as CanvuPluginContextValue, k as CanvuPluginViewportSnapshot, l as CustomShapePlacementOptions, N as NavMenu, m as NavMenuProps, P as PlacementPreview, n as VectorCanvas, o as VectorCanvasBody, p as VectorCanvasHeader, q as VectorCanvasMain, r as VectorCanvasRoot, s as VectorCanvasSlotProps, t as VectorCanvasSpacePosition, u as VectorCanvasSpaceProps, v as VectorCanvasToolbar, w as VectorCanvasToolbarProps, x as VectorCanvasViewportSurface, y as VectorSelectionInspector, z as VectorSelectionInspectorProps, A as VectorViewport, D as VectorViewportHandle, E as VectorViewportProps, W as WorldPointerDownDetail, F as createCanvuPlugin, G as getBoardPositionStyle, H as useCanvuChromeContext, I as useCanvuDocumentContext, J as useCanvuPluginContext, K as useCanvuPluginContribution, L as useCanvuResolvedTools, M as useCanvuViewportContext } from './types-CTyASYIm.cjs';
8
+ import { V as VectorCanvasPersistenceAdapter, a as VectorCanvasRemoteAdapter } from './types-DNwjgs5U.cjs';
9
+ export { b as VectorCanvasSnapshot } from './types-DNwjgs5U.cjs';
6
10
  import * as react from 'react';
7
11
  import { SVGProps, CSSProperties, ReactNode } from 'react';
8
12
  import * as react_jsx_runtime from 'react/jsx-runtime';
9
13
  import './camera-BwQjm5oh.cjs';
10
- import './shape-builders-DxPoOecg.cjs';
11
-
12
- declare class IndexedDbImageStore {
13
- private dbPromise;
14
- private getDb;
15
- storeOriginal(blob: Blob): Promise<string>;
16
- getOriginal(id: string): Promise<Blob | undefined>;
17
- deleteOriginal(id: string): Promise<void>;
18
- storeThumbnail(blob: Blob): Promise<string>;
19
- getThumbnail(id: string): Promise<Blob | undefined>;
20
- deleteThumbnail(id: string): Promise<void>;
21
- }
22
14
 
23
15
  /**
24
16
  * Extra context for each item created by {@link ingestAssetFilesToSceneItems}.
@@ -118,6 +110,7 @@ type IngestAssetFilesToSceneItemsOptions = {
118
110
  * Defaults to `1.5` for crisp zoom while keeping payloads reasonable.
119
111
  */
120
112
  pdfScale?: number;
113
+ pdfPageConcurrency?: number;
121
114
  /**
122
115
  * Final hook to customize each created item after canvu attaches its own
123
116
  * image metadata and any `assetStore.upload(...)` `pluginData`.
package/dist/react.d.ts CHANGED
@@ -1,24 +1,16 @@
1
+ import { I as IndexedDbImageStore } from './asset-hydration-BEG21hMp.js';
2
+ export { H as HydratedSceneItemsWithAssetsResult, h as hydrateSceneItemsWithAssets } from './asset-hydration-BEG21hMp.js';
1
3
  import { V as VectorSceneItem } from './types-CB0TZZuk.js';
2
- import { V as VectorViewportAssetKind, a as VectorViewportAssetStore, b as VectorToolDefinition, C as CanvasPlugin, B as BoardComponentPosition } from './types-ChnTSRSe.js';
3
- export { c as CanvasPluginComponentProps, d as CanvasPluginContribution, e as CanvasPluginItemsChangeMiddlewareContext, f as CanvasPluginRenderContext, g as CanvuChromeActiveToolStyle, h as CanvuChromeContext, i as CanvuChromeContextValue, j as CanvuChromeSelectionStyleChange, k as CanvuPluginContext, l as CanvuPluginContextValue, m as CanvuPluginViewportSnapshot, n as CustomShapePlacementOptions, N as NavMenu, o as NavMenuProps, P as PlacementPreview, p as VectorCanvas, q as VectorCanvasBody, r as VectorCanvasHeader, s as VectorCanvasMain, t as VectorCanvasRoot, u as VectorCanvasSlotProps, v as VectorCanvasSpacePosition, w as VectorCanvasSpaceProps, x as VectorCanvasToolbar, y as VectorCanvasToolbarProps, z as VectorCanvasViewportSurface, A as VectorSelectionInspector, D as VectorSelectionInspectorProps, E as VectorViewport, F as VectorViewportAssetResolveRequest, G as VectorViewportAssetResolveResult, H as VectorViewportAssetUploadRequest, I as VectorViewportAssetUploadResult, J as VectorViewportHandle, K as VectorViewportProps, W as WorldPointerDownDetail, L as createCanvuPlugin, M as getBoardPositionStyle, O as useCanvuChromeContext, Q as useCanvuDocumentContext, R as useCanvuPluginContext, S as useCanvuPluginContribution, T as useCanvuResolvedTools, U as useCanvuViewportContext } from './types-ChnTSRSe.js';
4
- import { V as VectorCanvasPersistenceAdapter, a as VectorCanvasRemoteAdapter } from './types-DgEArHkA.js';
5
- export { b as VectorCanvasSnapshot } from './types-DgEArHkA.js';
4
+ import { V as VectorViewportAssetKind, u as VectorViewportAssetStore } from './shape-builders-ENwnK-zT.js';
5
+ export { v as VectorViewportAssetHydrationRequest, w as VectorViewportAssetResolveRequest, x as VectorViewportAssetResolveResult, y as VectorViewportAssetUploadRequest, z as VectorViewportAssetUploadResult } from './shape-builders-ENwnK-zT.js';
6
+ import { V as VectorToolDefinition, C as CanvasPlugin, B as BoardComponentPosition } from './types-UvUy2Eed.js';
7
+ export { a as CanvasPluginComponentProps, b as CanvasPluginContribution, c as CanvasPluginItemsChangeMiddlewareContext, d as CanvasPluginRenderContext, e as CanvuChromeActiveToolStyle, f as CanvuChromeContext, g as CanvuChromeContextValue, h as CanvuChromeSelectionStyleChange, i as CanvuPluginContext, j as CanvuPluginContextValue, k as CanvuPluginViewportSnapshot, l as CustomShapePlacementOptions, N as NavMenu, m as NavMenuProps, P as PlacementPreview, n as VectorCanvas, o as VectorCanvasBody, p as VectorCanvasHeader, q as VectorCanvasMain, r as VectorCanvasRoot, s as VectorCanvasSlotProps, t as VectorCanvasSpacePosition, u as VectorCanvasSpaceProps, v as VectorCanvasToolbar, w as VectorCanvasToolbarProps, x as VectorCanvasViewportSurface, y as VectorSelectionInspector, z as VectorSelectionInspectorProps, A as VectorViewport, D as VectorViewportHandle, E as VectorViewportProps, W as WorldPointerDownDetail, F as createCanvuPlugin, G as getBoardPositionStyle, H as useCanvuChromeContext, I as useCanvuDocumentContext, J as useCanvuPluginContext, K as useCanvuPluginContribution, L as useCanvuResolvedTools, M as useCanvuViewportContext } from './types-UvUy2Eed.js';
8
+ import { V as VectorCanvasPersistenceAdapter, a as VectorCanvasRemoteAdapter } from './types-BtAJFS_-.js';
9
+ export { b as VectorCanvasSnapshot } from './types-BtAJFS_-.js';
6
10
  import * as react from 'react';
7
11
  import { SVGProps, CSSProperties, ReactNode } from 'react';
8
12
  import * as react_jsx_runtime from 'react/jsx-runtime';
9
13
  import './camera-KwCYYPhm.js';
10
- import './shape-builders-DTYvub8W.js';
11
-
12
- declare class IndexedDbImageStore {
13
- private dbPromise;
14
- private getDb;
15
- storeOriginal(blob: Blob): Promise<string>;
16
- getOriginal(id: string): Promise<Blob | undefined>;
17
- deleteOriginal(id: string): Promise<void>;
18
- storeThumbnail(blob: Blob): Promise<string>;
19
- getThumbnail(id: string): Promise<Blob | undefined>;
20
- deleteThumbnail(id: string): Promise<void>;
21
- }
22
14
 
23
15
  /**
24
16
  * Extra context for each item created by {@link ingestAssetFilesToSceneItems}.
@@ -118,6 +110,7 @@ type IngestAssetFilesToSceneItemsOptions = {
118
110
  * Defaults to `1.5` for crisp zoom while keeping payloads reasonable.
119
111
  */
120
112
  pdfScale?: number;
113
+ pdfPageConcurrency?: number;
121
114
  /**
122
115
  * Final hook to customize each created item after canvu attaches its own
123
116
  * image metadata and any `assetStore.upload(...)` `pluginData`.
package/dist/react.js CHANGED
@@ -921,35 +921,336 @@ function canvasToBlob2(canvas, mime, quality) {
921
921
  );
922
922
  });
923
923
  }
924
+ function normalizePdfPageNumbers(pageNumbers, pageCount) {
925
+ if (!pageNumbers || pageNumbers.length === 0) {
926
+ return Array.from({ length: pageCount }, (_, index) => index + 1);
927
+ }
928
+ return [...new Set(pageNumbers)].filter((pageNumber) => pageNumber >= 1 && pageNumber <= pageCount).sort((left, right) => left - right);
929
+ }
930
+ async function runWithConcurrency(items, concurrency, execute) {
931
+ const results = new Array(items.length);
932
+ let nextIndex = 0;
933
+ const workerCount = Math.max(1, Math.min(concurrency, items.length));
934
+ await Promise.all(
935
+ Array.from({ length: workerCount }, async () => {
936
+ while (nextIndex < items.length) {
937
+ const currentIndex = nextIndex;
938
+ nextIndex += 1;
939
+ const item = items[currentIndex];
940
+ if (item === void 0) {
941
+ continue;
942
+ }
943
+ results[currentIndex] = await execute(item);
944
+ }
945
+ })
946
+ );
947
+ return results;
948
+ }
924
949
  async function loadPdfToStore(file, store, options) {
925
950
  const scale = options?.scale ?? 1.5;
951
+ const pageConcurrency = options?.pageConcurrency ?? 2;
926
952
  const pdfjs = await getPdfJs();
927
953
  const arrayBuffer = await file.arrayBuffer();
928
954
  const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise;
929
- const results = [];
930
- for (let i = 1; i <= pdf.numPages; i++) {
931
- const page = await pdf.getPage(i);
932
- const { canvas, width, height } = await renderPageToCanvas(page, scale);
933
- const mime = "image/png";
934
- const pageBlob = await canvasToBlob2(canvas, mime);
935
- const blobId = await store.storeOriginal(pageBlob);
936
- const thumbScale = Math.min(1, 256 / Math.max(width, height));
937
- const tw = Math.max(1, Math.round(width * thumbScale));
938
- const th = Math.max(1, Math.round(height * thumbScale));
939
- const thumbCanvas = document.createElement("canvas");
940
- thumbCanvas.width = tw;
941
- thumbCanvas.height = th;
942
- const tCtx = thumbCanvas.getContext("2d");
943
- if (tCtx) {
944
- tCtx.imageSmoothingEnabled = true;
945
- tCtx.imageSmoothingQuality = "high";
946
- tCtx.drawImage(canvas, 0, 0, tw, th);
955
+ const pageNumbers = normalizePdfPageNumbers(options?.pageNumbers, pdf.numPages);
956
+ return await runWithConcurrency(
957
+ pageNumbers,
958
+ pageConcurrency,
959
+ async (pageNumber) => {
960
+ const page = await pdf.getPage(pageNumber);
961
+ const { canvas, width, height } = await renderPageToCanvas(page, scale);
962
+ const mime = "image/png";
963
+ const pageBlob = await canvasToBlob2(canvas, mime);
964
+ const blobId = await store.storeOriginal(pageBlob);
965
+ const thumbScale = Math.min(1, 256 / Math.max(width, height));
966
+ const tw = Math.max(1, Math.round(width * thumbScale));
967
+ const th = Math.max(1, Math.round(height * thumbScale));
968
+ const thumbCanvas = document.createElement("canvas");
969
+ thumbCanvas.width = tw;
970
+ thumbCanvas.height = th;
971
+ const tCtx = thumbCanvas.getContext("2d");
972
+ if (tCtx) {
973
+ tCtx.imageSmoothingEnabled = true;
974
+ tCtx.imageSmoothingQuality = "high";
975
+ tCtx.drawImage(canvas, 0, 0, tw, th);
976
+ }
977
+ const thumbBlob = await canvasToBlob2(thumbCanvas, mime);
978
+ const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
979
+ return {
980
+ blobId,
981
+ thumbnailBlobId,
982
+ width,
983
+ height,
984
+ pageNumber
985
+ };
947
986
  }
948
- const thumbBlob = await canvasToBlob2(thumbCanvas, mime);
949
- const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
950
- results.push({ blobId, thumbnailBlobId, width, height, pageNumber: i });
987
+ );
988
+ }
989
+
990
+ // src/react/asset-hydration.ts
991
+ init_shape_builders();
992
+ var HYDRATION_CACHE_NAME = "canvu-asset-hydration-v1";
993
+ var DEFAULT_PDF_SCALE = 1.15;
994
+ var DEFAULT_PDF_PAGE_CONCURRENCY = 2;
995
+ var hydrationBlobPromises = /* @__PURE__ */ new Map();
996
+ function buildImageHydrationKey(assetId) {
997
+ return `image:${assetId}`;
998
+ }
999
+ function buildPdfHydrationKey(assetId, pageNumber, scale) {
1000
+ return `pdf:${assetId}:page:${pageNumber}:scale:${scale}`;
1001
+ }
1002
+ function getCacheRequest(key) {
1003
+ return new Request(
1004
+ `https://canvu.local/asset-hydration/${encodeURIComponent(key)}`
1005
+ );
1006
+ }
1007
+ async function openHydrationCache() {
1008
+ if (typeof caches === "undefined") return null;
1009
+ try {
1010
+ return await caches.open(HYDRATION_CACHE_NAME);
1011
+ } catch {
1012
+ return null;
951
1013
  }
952
- return results;
1014
+ }
1015
+ async function readCachedHydrationBlob(key) {
1016
+ const cache = await openHydrationCache();
1017
+ if (!cache) return null;
1018
+ try {
1019
+ const response = await cache.match(getCacheRequest(key));
1020
+ if (!response?.ok) return null;
1021
+ return await response.blob();
1022
+ } catch {
1023
+ return null;
1024
+ }
1025
+ }
1026
+ async function writeCachedHydrationBlob(key, blob) {
1027
+ const cache = await openHydrationCache();
1028
+ if (!cache) return;
1029
+ try {
1030
+ await cache.put(getCacheRequest(key), new Response(blob));
1031
+ } catch {
1032
+ }
1033
+ }
1034
+ async function fetchBlob(url) {
1035
+ try {
1036
+ const response = await fetch(url);
1037
+ if (!response.ok) return null;
1038
+ return await response.blob();
1039
+ } catch {
1040
+ return null;
1041
+ }
1042
+ }
1043
+ async function getHydrationBlob(key, preferCachedRasters, loader) {
1044
+ const cached = preferCachedRasters ? await readCachedHydrationBlob(key) : null;
1045
+ if (cached) return cached;
1046
+ const inFlight = hydrationBlobPromises.get(key);
1047
+ if (inFlight) return await inFlight;
1048
+ const nextPromise = (async () => {
1049
+ const blob = await loader();
1050
+ if (blob) {
1051
+ await writeCachedHydrationBlob(key, blob);
1052
+ }
1053
+ return blob;
1054
+ })();
1055
+ hydrationBlobPromises.set(key, nextPromise);
1056
+ try {
1057
+ return await nextPromise;
1058
+ } finally {
1059
+ hydrationBlobPromises.delete(key);
1060
+ }
1061
+ }
1062
+ function registerObjectUrl(objectUrls, blob) {
1063
+ const href = URL.createObjectURL(blob);
1064
+ objectUrls.push(href);
1065
+ return href;
1066
+ }
1067
+ function getHydrationRequests(items, assetStore, pdfScale) {
1068
+ if (!assetStore.getHydrationRequest) return [];
1069
+ return items.flatMap((item) => {
1070
+ const request = assetStore.getHydrationRequest?.(item);
1071
+ if (!request) return [];
1072
+ return [
1073
+ {
1074
+ request: {
1075
+ ...request,
1076
+ scale: request.scale ?? pdfScale
1077
+ }
1078
+ }
1079
+ ];
1080
+ });
1081
+ }
1082
+ function createPdfHydrationGroups(requests) {
1083
+ const groups = /* @__PURE__ */ new Map();
1084
+ for (const entry of requests) {
1085
+ if (entry.request.kind !== "pdf" || entry.request.pageNumber == null) continue;
1086
+ const scale = entry.request.scale ?? DEFAULT_PDF_SCALE;
1087
+ const key = `${entry.request.assetId}:${scale}`;
1088
+ const existingGroup = groups.get(key);
1089
+ if (existingGroup) {
1090
+ if (!existingGroup.pageNumbers.includes(entry.request.pageNumber)) {
1091
+ existingGroup.pageNumbers.push(entry.request.pageNumber);
1092
+ }
1093
+ continue;
1094
+ }
1095
+ groups.set(key, {
1096
+ assetId: entry.request.assetId,
1097
+ scale,
1098
+ pageNumbers: [entry.request.pageNumber]
1099
+ });
1100
+ }
1101
+ return [...groups.values()];
1102
+ }
1103
+ async function hydrateImageAssets(requests, resolvedAssetUrls, objectUrls, preferCachedRasters) {
1104
+ const uniqueAssetIds = [
1105
+ ...new Set(requests.map((entry) => entry.request.assetId))
1106
+ ];
1107
+ const hydratedEntries = await Promise.all(
1108
+ uniqueAssetIds.map(async (assetId) => {
1109
+ const resolvedAsset = resolvedAssetUrls[assetId];
1110
+ if (!resolvedAsset?.url) {
1111
+ return [assetId, null];
1112
+ }
1113
+ const blob = await getHydrationBlob(
1114
+ buildImageHydrationKey(assetId),
1115
+ preferCachedRasters,
1116
+ async () => await fetchBlob(resolvedAsset.url)
1117
+ );
1118
+ if (!blob) {
1119
+ return [assetId, null];
1120
+ }
1121
+ return [
1122
+ assetId,
1123
+ {
1124
+ href: registerObjectUrl(objectUrls, blob)
1125
+ }
1126
+ ];
1127
+ })
1128
+ );
1129
+ return new Map(hydratedEntries);
1130
+ }
1131
+ async function hydratePdfAssets(requests, resolvedAssetUrls, objectUrls, options) {
1132
+ const hydratedPages = /* @__PURE__ */ new Map();
1133
+ const groups = createPdfHydrationGroups(requests);
1134
+ for (const group of groups) {
1135
+ const resolvedAsset = resolvedAssetUrls[group.assetId];
1136
+ if (!resolvedAsset?.url) {
1137
+ continue;
1138
+ }
1139
+ const missingPages = [];
1140
+ for (const pageNumber of group.pageNumbers) {
1141
+ const cacheKey = buildPdfHydrationKey(group.assetId, pageNumber, group.scale);
1142
+ const cachedBlob = await readCachedHydrationBlob(cacheKey);
1143
+ if (!cachedBlob) {
1144
+ missingPages.push(pageNumber);
1145
+ continue;
1146
+ }
1147
+ hydratedPages.set(cacheKey, {
1148
+ href: registerObjectUrl(objectUrls, cachedBlob)
1149
+ });
1150
+ }
1151
+ if (missingPages.length === 0) {
1152
+ continue;
1153
+ }
1154
+ const pdfBlob = await fetchBlob(resolvedAsset.url);
1155
+ if (!pdfBlob) {
1156
+ continue;
1157
+ }
1158
+ const renderedPages = await loadPdfToStore(pdfBlob, options.imageStore, {
1159
+ scale: group.scale,
1160
+ pageNumbers: missingPages,
1161
+ pageConcurrency: options.pdfPageConcurrency
1162
+ });
1163
+ for (const renderedPage of renderedPages) {
1164
+ const cacheKey = buildPdfHydrationKey(
1165
+ group.assetId,
1166
+ renderedPage.pageNumber,
1167
+ group.scale
1168
+ );
1169
+ const pageBlob = await options.imageStore.getOriginal(renderedPage.blobId);
1170
+ if (!pageBlob) {
1171
+ continue;
1172
+ }
1173
+ await writeCachedHydrationBlob(cacheKey, pageBlob);
1174
+ hydratedPages.set(cacheKey, {
1175
+ href: registerObjectUrl(objectUrls, pageBlob),
1176
+ width: renderedPage.width,
1177
+ height: renderedPage.height
1178
+ });
1179
+ }
1180
+ }
1181
+ return hydratedPages;
1182
+ }
1183
+ async function hydrateSceneItemsWithAssets(items, assetStore, options = {}) {
1184
+ if (!assetStore.resolve || !assetStore.getHydrationRequest) {
1185
+ return {
1186
+ items: [...items],
1187
+ objectUrls: []
1188
+ };
1189
+ }
1190
+ const pdfScale = options.pdfScale ?? DEFAULT_PDF_SCALE;
1191
+ const pdfPageConcurrency = options.pdfPageConcurrency ?? DEFAULT_PDF_PAGE_CONCURRENCY;
1192
+ const preferCachedRasters = options.preferCachedRasters ?? true;
1193
+ const imageStore = options.imageStore ?? new IndexedDbImageStore();
1194
+ const hydrationRequests = getHydrationRequests(items, assetStore, pdfScale);
1195
+ if (hydrationRequests.length === 0) {
1196
+ return {
1197
+ items: [...items],
1198
+ objectUrls: []
1199
+ };
1200
+ }
1201
+ const assetIds = [
1202
+ ...new Set(hydrationRequests.map((entry) => entry.request.assetId))
1203
+ ];
1204
+ const resolvedAssetUrls = await assetStore.resolve({ assetIds });
1205
+ const objectUrls = [];
1206
+ const hydratedImages = await hydrateImageAssets(
1207
+ hydrationRequests.filter((entry) => entry.request.kind === "image"),
1208
+ resolvedAssetUrls,
1209
+ objectUrls,
1210
+ preferCachedRasters
1211
+ );
1212
+ const hydratedPdfPages = await hydratePdfAssets(
1213
+ hydrationRequests.filter((entry) => entry.request.kind === "pdf"),
1214
+ resolvedAssetUrls,
1215
+ objectUrls,
1216
+ {
1217
+ imageStore,
1218
+ pdfPageConcurrency}
1219
+ );
1220
+ return {
1221
+ items: items.map((item) => {
1222
+ const request = assetStore.getHydrationRequest?.(item);
1223
+ if (!request || item.toolKind !== "image") return item;
1224
+ if (request.kind === "image") {
1225
+ const hydratedImage = hydratedImages.get(request.assetId);
1226
+ if (!hydratedImage) return item;
1227
+ return rebuildItemSvg({
1228
+ ...item,
1229
+ imageRasterHref: hydratedImage.href,
1230
+ imageThumbnailHref: hydratedImage.href
1231
+ });
1232
+ }
1233
+ if (request.pageNumber == null) return item;
1234
+ const hydratedPdfPage = hydratedPdfPages.get(
1235
+ buildPdfHydrationKey(
1236
+ request.assetId,
1237
+ request.pageNumber,
1238
+ request.scale ?? pdfScale
1239
+ )
1240
+ );
1241
+ if (!hydratedPdfPage) return item;
1242
+ return rebuildItemSvg({
1243
+ ...item,
1244
+ imageRasterHref: hydratedPdfPage.href,
1245
+ imageThumbnailHref: hydratedPdfPage.href,
1246
+ imageIntrinsicSize: {
1247
+ width: item.imageIntrinsicSize?.width ?? Math.max(1, Math.round(hydratedPdfPage.width ?? item.bounds.width)),
1248
+ height: item.imageIntrinsicSize?.height ?? Math.max(1, Math.round(hydratedPdfPage.height ?? item.bounds.height))
1249
+ }
1250
+ });
1251
+ }),
1252
+ objectUrls
1253
+ };
953
1254
  }
954
1255
 
955
1256
  // src/react/asset-ingestion.ts
@@ -992,6 +1293,7 @@ async function ingestAssetFilesToSceneItems(options) {
992
1293
  gapWorld = 16,
993
1294
  stepWorld = 48,
994
1295
  pdfScale = 1.5,
1296
+ pdfPageConcurrency = 2,
995
1297
  decorateItem,
996
1298
  onError
997
1299
  } = options;
@@ -1013,9 +1315,14 @@ async function ingestAssetFilesToSceneItems(options) {
1013
1315
  continue;
1014
1316
  }
1015
1317
  try {
1016
- const uploadResult = await uploadAssetIfNeeded(assetStore, file, kind);
1017
1318
  if (kind === "pdf") {
1018
- const pages = await loadPdfToStore(file, imageStore, { scale: pdfScale });
1319
+ const [uploadResult2, pages] = await Promise.all([
1320
+ uploadAssetIfNeeded(assetStore, file, kind),
1321
+ loadPdfToStore(file, imageStore, {
1322
+ scale: pdfScale,
1323
+ pageConcurrency: pdfPageConcurrency
1324
+ })
1325
+ ]);
1019
1326
  for (const page of pages) {
1020
1327
  const fullUrl2 = await createBlobUrlFromStore(imageStore, page.blobId);
1021
1328
  const naturalTopY2 = worldCenter.y - page.height / 2;
@@ -1052,7 +1359,7 @@ async function ingestAssetFilesToSceneItems(options) {
1052
1359
  ) : ""
1053
1360
  },
1054
1361
  itemContext2,
1055
- uploadResult,
1362
+ uploadResult2,
1056
1363
  decorateItem
1057
1364
  );
1058
1365
  items.push(item2);
@@ -1063,10 +1370,11 @@ async function ingestAssetFilesToSceneItems(options) {
1063
1370
  imageYOffsetAdjustment = 0;
1064
1371
  continue;
1065
1372
  }
1066
- const { blobId, thumbnailBlobId, width, height } = await loadImageToStore(
1067
- file,
1068
- imageStore
1069
- );
1373
+ const [uploadResult, storedImage] = await Promise.all([
1374
+ uploadAssetIfNeeded(assetStore, file, kind),
1375
+ loadImageToStore(file, imageStore)
1376
+ ]);
1377
+ const { blobId, thumbnailBlobId, width, height } = storedImage;
1070
1378
  const fullUrl = await createBlobUrlFromStore(imageStore, blobId);
1071
1379
  const thumbBlob = await imageStore.getThumbnail(thumbnailBlobId);
1072
1380
  const thumbnailHref = thumbBlob ? URL.createObjectURL(thumbBlob) : null;
@@ -4226,16 +4534,14 @@ function itemHitTestWorldPoint(item, worldX, worldY, options) {
4226
4534
  return true;
4227
4535
  }
4228
4536
  }
4537
+ return false;
4229
4538
  }
4230
- if (pts?.length === 1) {
4539
+ if (pts && pts.length === 1) {
4231
4540
  const p = pts[0];
4232
- if (p) {
4233
- const cw = itemLocalToWorld(p.x, p.y, item.x, item.y, w, h, rot);
4234
- const dsq = (worldX - cw.x) ** 2 + (worldY - cw.y) ** 2;
4235
- if (dsq <= tolSq) {
4236
- return true;
4237
- }
4238
- }
4541
+ if (!p) return false;
4542
+ const cw = itemLocalToWorld(p.x, p.y, item.x, item.y, w, h, rot);
4543
+ const dsq = (worldX - cw.x) ** 2 + (worldY - cw.y) ** 2;
4544
+ return dsq <= tolSq;
4239
4545
  }
4240
4546
  return hitTestFilledShape(item, worldX, worldY);
4241
4547
  }
@@ -4780,101 +5086,12 @@ init_shape_builders();
4780
5086
  var HANDLE_ORDER = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
4781
5087
  var ERASER_TINT = "#cbd5e1";
4782
5088
  var ERASER_TINT_OPACITY = 0.95;
5089
+ var ERASER_PREVIEW_OPACITY = 0.3;
4783
5090
  function pointsToSmoothPath(points) {
4784
5091
  if (points.length === 0) return null;
4785
5092
  const d = smoothFreehandPointsToPathD(points);
4786
5093
  return d || null;
4787
5094
  }
4788
- function renderEraserSkeleton(it, overlayStrokePx) {
4789
- const b = normalizeRect(it.bounds);
4790
- const tool = it.toolKind;
4791
- const strokeWidth = Math.max(it.strokeWidth ?? 2, overlayStrokePx);
4792
- const common = {
4793
- stroke: ERASER_TINT,
4794
- strokeWidth,
4795
- strokeOpacity: ERASER_TINT_OPACITY,
4796
- vectorEffect: "non-scaling-stroke",
4797
- strokeLinecap: "round",
4798
- strokeLinejoin: "round",
4799
- fill: "none"
4800
- };
4801
- if (tool === "rect") {
4802
- return /* @__PURE__ */ jsx("rect", { x: 0, y: 0, width: b.width, height: b.height, ...common });
4803
- }
4804
- if (tool === "ellipse") {
4805
- return /* @__PURE__ */ jsx(
4806
- "ellipse",
4807
- {
4808
- cx: b.width / 2,
4809
- cy: b.height / 2,
4810
- rx: Math.max(0, b.width / 2),
4811
- ry: Math.max(0, b.height / 2),
4812
- ...common
4813
- }
4814
- );
4815
- }
4816
- if (tool === "line" || tool === "arrow") {
4817
- const ln = it.line;
4818
- if (!ln)
4819
- return /* @__PURE__ */ jsx("rect", { x: 0, y: 0, width: b.width, height: b.height, ...common });
4820
- const geometry = tool === "arrow" ? computeStraightArrowGeometry(
4821
- ln,
4822
- Math.max(it.strokeWidth ?? 2, overlayStrokePx)
4823
- ) : null;
4824
- return /* @__PURE__ */ jsxs(Fragment, { children: [
4825
- /* @__PURE__ */ jsx(
4826
- "line",
4827
- {
4828
- x1: ln.x1,
4829
- y1: ln.y1,
4830
- x2: geometry?.shaftEndX ?? ln.x2,
4831
- y2: geometry?.shaftEndY ?? ln.y2,
4832
- ...common
4833
- }
4834
- ),
4835
- tool === "arrow" && geometry ? /* @__PURE__ */ jsx(
4836
- "path",
4837
- {
4838
- d: `M ${geometry.headLeftX} ${geometry.headLeftY} L ${geometry.headTipX} ${geometry.headTipY} L ${geometry.headRightX} ${geometry.headRightY}`,
4839
- ...common
4840
- }
4841
- ) : null
4842
- ] });
4843
- }
4844
- if (tool === "draw" || tool === "marker" || tool === "pencil" || tool === "brush") {
4845
- const pts = it.pathPointsLocal ?? [];
4846
- if (pts.length === 1) {
4847
- const p = pts[0];
4848
- if (!p) return null;
4849
- return /* @__PURE__ */ jsx(
4850
- "circle",
4851
- {
4852
- cx: p.x,
4853
- cy: p.y,
4854
- r: Math.max((it.strokeWidth ?? 2) / 2, 2),
4855
- fill: ERASER_TINT,
4856
- fillOpacity: 0.8,
4857
- vectorEffect: "non-scaling-stroke"
4858
- }
4859
- );
4860
- }
4861
- const d = pointsToSmoothPath(pts);
4862
- if (d) {
4863
- return /* @__PURE__ */ jsx("path", { d, ...common, shapeRendering: "geometricPrecision" });
4864
- }
4865
- }
4866
- return /* @__PURE__ */ jsx(
4867
- "rect",
4868
- {
4869
- x: 0,
4870
- y: 0,
4871
- width: b.width,
4872
- height: b.height,
4873
- ...common,
4874
- strokeDasharray: "4 4"
4875
- }
4876
- );
4877
- }
4878
5095
  function InteractionOverlay({
4879
5096
  camera,
4880
5097
  cameraVersion: _cameraVersion,
@@ -5122,16 +5339,15 @@ function InteractionOverlay({
5122
5339
  }
5123
5340
  let eraserPreview = null;
5124
5341
  if (eraserPreviewItems.length > 0) {
5125
- eraserPreview = /* @__PURE__ */ jsx("g", { children: eraserPreviewItems.map((it) => {
5126
- return /* @__PURE__ */ jsx(
5127
- "g",
5128
- {
5129
- transform: formatItemPlacementTransform(it),
5130
- children: renderEraserSkeleton(it, overlayStrokePx)
5131
- },
5132
- `erase-preview-${it.id}`
5133
- );
5134
- }) });
5342
+ eraserPreview = /* @__PURE__ */ jsx("g", { children: eraserPreviewItems.map((it) => /* @__PURE__ */ jsx(
5343
+ "g",
5344
+ {
5345
+ transform: formatItemPlacementTransform(it),
5346
+ opacity: ERASER_PREVIEW_OPACITY,
5347
+ dangerouslySetInnerHTML: { __html: it.childrenSvg }
5348
+ },
5349
+ `erase-preview-${it.id}`
5350
+ )) });
5135
5351
  }
5136
5352
  let marqueeCandidates = null;
5137
5353
  if (marqueeCandidateItems.length > 0) {
@@ -8595,6 +8811,6 @@ function ViewportZoomControls({
8595
8811
  );
8596
8812
  }
8597
8813
 
8598
- export { CanvuChromeContext, CanvuPluginContext, DEFAULT_OVERFLOW_TOOL_IDS, DEFAULT_VECTOR_CANVAS_STORAGE_KEY, DEFAULT_VECTOR_TOOLS, IconArrow, IconDraw, IconEllipse, IconHand, IconImage, IconLaser, IconLine, IconRect, IconSelect, IconText, NavMenu, ShapeContextMenu, VectorCanvas, VectorCanvasBody, VectorCanvasHeader, VectorCanvasMain, VectorCanvasRoot, VectorCanvasToolbar, VectorCanvasViewportSurface, VectorSelectionInspector, VectorToolbar, VectorViewport, ViewportZoomControls, createCanvuPlugin, createIndexedDbPersistenceAdapter, createLocalStoragePersistenceAdapter, createNoopPersistenceAdapter, createToolPlugin, cursorForVectorToolId, getBoardPositionStyle, ingestAssetFilesToSceneItems, useCanvuChromeContext, useCanvuDocumentContext, useCanvuPluginContext, useCanvuPluginContribution, useCanvuResolvedTools, useCanvuViewportContext, useVectorCanvasDocument };
8814
+ export { CanvuChromeContext, CanvuPluginContext, DEFAULT_OVERFLOW_TOOL_IDS, DEFAULT_VECTOR_CANVAS_STORAGE_KEY, DEFAULT_VECTOR_TOOLS, IconArrow, IconDraw, IconEllipse, IconHand, IconImage, IconLaser, IconLine, IconRect, IconSelect, IconText, NavMenu, ShapeContextMenu, VectorCanvas, VectorCanvasBody, VectorCanvasHeader, VectorCanvasMain, VectorCanvasRoot, VectorCanvasToolbar, VectorCanvasViewportSurface, VectorSelectionInspector, VectorToolbar, VectorViewport, ViewportZoomControls, createCanvuPlugin, createIndexedDbPersistenceAdapter, createLocalStoragePersistenceAdapter, createNoopPersistenceAdapter, createToolPlugin, cursorForVectorToolId, getBoardPositionStyle, hydrateSceneItemsWithAssets, ingestAssetFilesToSceneItems, useCanvuChromeContext, useCanvuDocumentContext, useCanvuPluginContext, useCanvuPluginContribution, useCanvuResolvedTools, useCanvuViewportContext, useVectorCanvasDocument };
8599
8815
  //# sourceMappingURL=react.js.map
8600
8816
  //# sourceMappingURL=react.js.map