@reekon-tools/boldr-utils 1.19.3 → 1.19.5
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/annotation/canvas/AnnotationCanvasInner.d.ts +1 -0
- package/dist/annotation/canvas/AnnotationCanvasInner.js +2 -1
- package/dist/annotation/canvas/AnnotationCanvasInner.native.d.ts +1 -0
- package/dist/annotation/canvas/AnnotationCanvasInner.native.js +3 -2
- package/dist/annotation/canvas/AnnotationCanvasSkia.d.ts +2 -1
- package/dist/annotation/canvas/AnnotationCanvasSkia.js +2 -2
- package/dist/annotation/canvas/elements/BackgroundImageElement.d.ts +5 -3
- package/dist/annotation/canvas/elements/BackgroundImageElement.js +7 -7
- package/dist/annotation/canvas/elements/BackgroundSvg.d.ts +1 -0
- package/dist/annotation/canvas/elements/BackgroundSvg.js +4 -4
- package/dist/annotation/canvas/elements/backgroundUrl.d.ts +2 -1
- package/dist/annotation/canvas/elements/backgroundUrl.js +15 -2
- package/dist/annotation/canvas/stampLayout.d.ts +1 -1
- package/dist/annotation/canvas/stampLayout.js +7 -1
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@ export interface AnnotationCanvasInnerProps {
|
|
|
20
20
|
fractionalTolerance?: FractionalTolerance;
|
|
21
21
|
decimalTolerance?: DecimalTolerance;
|
|
22
22
|
resolveImageUrl?: (storagePath: string) => Promise<string>;
|
|
23
|
+
deferBackgroundToResolver?: boolean;
|
|
23
24
|
pickMeasurement?: () => Promise<MeasurementRef | null>;
|
|
24
25
|
requestTextInput?: RequestTextInput;
|
|
25
26
|
renderBackground?: RenderCanvasBackground;
|
|
@@ -27,7 +27,7 @@ const DEFAULT_PAN_TRIGGERS = ['middleMouse', 'space'];
|
|
|
27
27
|
// (SSR / the vitest environment), where 1 is the right answer.
|
|
28
28
|
const devicePixelRatioOf = () => typeof window === 'undefined' ? 1 : window.devicePixelRatio || 1;
|
|
29
29
|
export const AnnotationCanvasInner = (props) => {
|
|
30
|
-
const { resolveImageUrl, stampFontSource, stampValueFontSize = 14, gestures, width, height, style, activeToolId, tools, snapshotRef, renderBackground, } = props;
|
|
30
|
+
const { resolveImageUrl, deferBackgroundToResolver, stampFontSource, stampValueFontSize = 14, gestures, width, height, style, activeToolId, tools, snapshotRef, renderBackground, } = props;
|
|
31
31
|
// Snapshot plumbing for thumbnail capture: hold the <Canvas> ref and publish
|
|
32
32
|
// a capture fn on the consumer's snapshotRef. encodeToBytes() yields PNG.
|
|
33
33
|
const canvasRef = useCanvasRef();
|
|
@@ -315,6 +315,7 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
315
315
|
worldTransform: state.worldTransform,
|
|
316
316
|
viewport: state.viewport,
|
|
317
317
|
resolveImageUrl,
|
|
318
|
+
deferBackgroundToResolver,
|
|
318
319
|
backgroundContent,
|
|
319
320
|
canvasRef,
|
|
320
321
|
valueFont,
|
|
@@ -21,6 +21,7 @@ export interface AnnotationCanvasInnerProps {
|
|
|
21
21
|
fractionalTolerance?: FractionalTolerance;
|
|
22
22
|
decimalTolerance?: DecimalTolerance;
|
|
23
23
|
resolveImageUrl?: (storagePath: string) => Promise<string>;
|
|
24
|
+
deferBackgroundToResolver?: boolean;
|
|
24
25
|
pickMeasurement?: () => Promise<MeasurementRef | null>;
|
|
25
26
|
requestTextInput?: RequestTextInput;
|
|
26
27
|
renderBackground?: RenderCanvasBackground;
|
|
@@ -64,7 +64,7 @@ const LOUPE_CROSS_WIDTH_PX = 1.5;
|
|
|
64
64
|
// is only re-synced once each gesture ends. The tool/tap path still hops to
|
|
65
65
|
// JS (`runOnJS(true)`) since drawing/selection are React-state operations.
|
|
66
66
|
export const AnnotationCanvasInner = (props) => {
|
|
67
|
-
const { resolveImageUrl, stampFontSource, stampValueFontSize = 14, width, height, style, magnifierTopOffset = 0, renderBackground, } = props;
|
|
67
|
+
const { resolveImageUrl, deferBackgroundToResolver, stampFontSource, stampValueFontSize = 14, width, height, style, magnifierTopOffset = 0, renderBackground, } = props;
|
|
68
68
|
const valueFont = useFont(stampFontSource, stampValueFontSize);
|
|
69
69
|
// The animated-viewport implementation needs the Reanimated shared values,
|
|
70
70
|
// which are created below AFTER the state hook — so the hook gets a stable
|
|
@@ -1949,6 +1949,7 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
1949
1949
|
effectiveCanvas: state.effectiveCanvas,
|
|
1950
1950
|
worldTransform,
|
|
1951
1951
|
resolveImageUrl,
|
|
1952
|
+
deferBackgroundToResolver,
|
|
1952
1953
|
backgroundSkImages,
|
|
1953
1954
|
// Shared with the loupe overlay below (it re-runs this same scene through a
|
|
1954
1955
|
// magnifying transform), so the lens shows the substrate magnified instead
|
|
@@ -2045,7 +2046,7 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
2045
2046
|
// drawing to the canvas box by construction, but the stamp overlay is
|
|
2046
2047
|
// plain RN views — without clipping, tiles panned past the edge of a
|
|
2047
2048
|
// non-fullscreen canvas (e.g. a diagram strip) escape into surrounding UI.
|
|
2048
|
-
_jsxs(GestureHandlerRootView, { style: [{ width, height, overflow: 'hidden' }, style], children: [backgroundLayers.map((layer) => (_jsx(BackgroundSkImageLoader, { layer: layer, resolveUrl: resolveImageUrl, onImage: onBackgroundSkImage }, layer.id))), _jsx(GestureDetector, { gesture: gesture, children: _jsx(View, { style: { width, height }, collapsable: false, children: AnnotationCanvasSkia(skiaProps) }) }), renderMeasurementStamp && (_jsx(View, { pointerEvents: "box-none", style: StyleSheet.absoluteFill, children: state.effectiveCanvas.placedMeasurements.map((placed) => (_jsx(MeasurementStampOverlayItem, { placed: placed, measurement: placed.measurementId
|
|
2049
|
+
_jsxs(GestureHandlerRootView, { style: [{ width, height, overflow: 'hidden' }, style], children: [backgroundLayers.map((layer) => (_jsx(BackgroundSkImageLoader, { layer: layer, resolveUrl: resolveImageUrl, deferToResolver: deferBackgroundToResolver, onImage: onBackgroundSkImage }, layer.id))), _jsx(GestureDetector, { gesture: gesture, children: _jsx(View, { style: { width, height }, collapsable: false, children: AnnotationCanvasSkia(skiaProps) }) }), renderMeasurementStamp && (_jsx(View, { pointerEvents: "box-none", style: StyleSheet.absoluteFill, children: state.effectiveCanvas.placedMeasurements.map((placed) => (_jsx(MeasurementStampOverlayItem, { placed: placed, measurement: placed.measurementId
|
|
2049
2050
|
? (state.measurementsById.get(placed.measurementId) ?? null)
|
|
2050
2051
|
: (resolveStampMeasurement?.(placed) ?? null), selected: selection?.ids.includes(placed.id) ?? false, dragging: draggingId === placed.id, sliding: slidingId === placed.id, endpointDragging: epDragId === placed.id, rectResizing: rectDragId === placed.id, planeEdgeDragging: planeDrag != null && placed.planeId === planeDrag.planeId, zoomSnapshot: state.viewport.zoom, zoom: zoom, panX: panX, panY: panY, dragX: dragX, dragY: dragY, slideCtx: slideCtx, epCtx: epCtx, rectCtx: rectCtx, planeTileCtx: planeTileCtx, planeDragTX: planeDragTX, planeDragTY: planeDragTY, renderMeasurementStamp: renderMeasurementStamp, tileScaleFactor: state.tileScaleFactor, headerTileScaleFactor: state.headerTileScaleFactor, fileTileScaleFactor: state.fileTileScaleFactor, tileViewportScale: state.tileViewportScale, onStampPress: onMeasurementStampPress, onStampLongPress: onMeasurementStampLongPress, onRemove: onMeasurementStampRemove
|
|
2051
2052
|
? () => {
|
|
@@ -23,6 +23,7 @@ export interface AnnotationCanvasSkiaProps {
|
|
|
23
23
|
};
|
|
24
24
|
viewport?: ViewportState;
|
|
25
25
|
resolveImageUrl?: (storagePath: string) => Promise<string>;
|
|
26
|
+
deferBackgroundToResolver?: boolean;
|
|
26
27
|
backgroundSkImages?: Record<string, SkImage | null>;
|
|
27
28
|
backgroundContent?: ReactNode;
|
|
28
29
|
valueFont: SkFont | null;
|
|
@@ -105,5 +106,5 @@ export interface AnnotationCanvasSkiaProps {
|
|
|
105
106
|
wrapContent?: (content: ReactNode) => ReactNode;
|
|
106
107
|
canvasRef?: RefObject<CanvasRef | null>;
|
|
107
108
|
}
|
|
108
|
-
export declare const AnnotationCanvasSkia: ({ width, height, effectiveCanvas, worldTransform, viewport, resolveImageUrl, backgroundSkImages, backgroundContent, valueFont, textFontMgr, penDrawingStroke, livePreview, shapePreview, draggingId, dragTransform, resizingId, resizeTransform, selectedId, endpointDragId, shapeEndpointDragId, liveLineP1, liveLineP2, rectDragId, liveRect, shapeResizeId, shapeResizePath, shapeResizeBox, handleRadius, handleRingWidth, showPlaneHandles, planeDragId, planeDragIndex, planeDragPath, planeDragGridPath, planeDragHandle, planeOutlineScale, measurementLineScale, customPreview, wrapContent, canvasRef, }: AnnotationCanvasSkiaProps) => import("react/jsx-runtime").JSX.Element;
|
|
109
|
+
export declare const AnnotationCanvasSkia: ({ width, height, effectiveCanvas, worldTransform, viewport, resolveImageUrl, deferBackgroundToResolver, backgroundSkImages, backgroundContent, valueFont, textFontMgr, penDrawingStroke, livePreview, shapePreview, draggingId, dragTransform, resizingId, resizeTransform, selectedId, endpointDragId, shapeEndpointDragId, liveLineP1, liveLineP2, rectDragId, liveRect, shapeResizeId, shapeResizePath, shapeResizeBox, handleRadius, handleRingWidth, showPlaneHandles, planeDragId, planeDragIndex, planeDragPath, planeDragGridPath, planeDragHandle, planeOutlineScale, measurementLineScale, customPreview, wrapContent, canvasRef, }: AnnotationCanvasSkiaProps) => import("react/jsx-runtime").JSX.Element;
|
|
109
110
|
export {};
|
|
@@ -80,7 +80,7 @@ const SelectionBox = ({ bounds, isDragging, transform, }) => (_jsx(DraggableElem
|
|
|
80
80
|
// since the function-call pattern works identically on native we use it
|
|
81
81
|
// in both Inners for consistency. Don't add hooks here; this is a plain
|
|
82
82
|
// JSX-returning helper, not a component.
|
|
83
|
-
export const AnnotationCanvasSkia = ({ width, height, effectiveCanvas, worldTransform, viewport, resolveImageUrl, backgroundSkImages, backgroundContent, valueFont, textFontMgr, penDrawingStroke, livePreview, shapePreview, draggingId, dragTransform, resizingId, resizeTransform, selectedId, endpointDragId, shapeEndpointDragId, liveLineP1, liveLineP2, rectDragId, liveRect, shapeResizeId, shapeResizePath, shapeResizeBox, handleRadius, handleRingWidth, showPlaneHandles, planeDragId, planeDragIndex, planeDragPath, planeDragGridPath, planeDragHandle, planeOutlineScale = 1, measurementLineScale = 1, customPreview, wrapContent, canvasRef, }) => {
|
|
83
|
+
export const AnnotationCanvasSkia = ({ width, height, effectiveCanvas, worldTransform, viewport, resolveImageUrl, deferBackgroundToResolver, backgroundSkImages, backgroundContent, valueFont, textFontMgr, penDrawingStroke, livePreview, shapePreview, draggingId, dragTransform, resizingId, resizeTransform, selectedId, endpointDragId, shapeEndpointDragId, liveLineP1, liveLineP2, rectDragId, liveRect, shapeResizeId, shapeResizePath, shapeResizeBox, handleRadius, handleRingWidth, showPlaneHandles, planeDragId, planeDragIndex, planeDragPath, planeDragGridPath, planeDragHandle, planeOutlineScale = 1, measurementLineScale = 1, customPreview, wrapContent, canvasRef, }) => {
|
|
84
84
|
// Doc rect a SCALE-mode plane grid covers: the first background layer (the
|
|
85
85
|
// photo being calibrated), else the document rect. Pinned to layer 0 like
|
|
86
86
|
// the tile-sizing reference — adding a second image must not re-span the
|
|
@@ -94,7 +94,7 @@ export const AnnotationCanvasSkia = ({ width, height, effectiveCanvas, worldTran
|
|
|
94
94
|
width: effectiveCanvas.viewport.width,
|
|
95
95
|
height: effectiveCanvas.viewport.height,
|
|
96
96
|
};
|
|
97
|
-
const world = (_jsxs(Group, { transform: worldTransform, children: [backgroundContent, backgroundLayersOf(effectiveCanvas.viewport).map((layer) => (_jsx(DraggableElement, { isDragging: layer.id === draggingId || layer.id === resizingId, transform: layer.id === resizingId ? resizeTransform : dragTransform, children: _jsx(BackgroundLayerElement, { layer: layer, resolveUrl: resolveImageUrl, skImage: backgroundSkImages?.[layer.id], viewport: viewport, screenWidth: width, screenHeight: height }) }, layer.id))), (effectiveCanvas.planes ?? []).map((plane) => {
|
|
97
|
+
const world = (_jsxs(Group, { transform: worldTransform, children: [backgroundContent, backgroundLayersOf(effectiveCanvas.viewport).map((layer) => (_jsx(DraggableElement, { isDragging: layer.id === draggingId || layer.id === resizingId, transform: layer.id === resizingId ? resizeTransform : dragTransform, children: _jsx(BackgroundLayerElement, { layer: layer, resolveUrl: resolveImageUrl, deferToResolver: deferBackgroundToResolver, skImage: backgroundSkImages?.[layer.id], viewport: viewport, screenWidth: width, screenHeight: height }) }, layer.id))), (effectiveCanvas.planes ?? []).map((plane) => {
|
|
98
98
|
if (plane.id === planeDragId && planeDragPath) {
|
|
99
99
|
const statics = plane.mode === 'perspective'
|
|
100
100
|
? plane.corners
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { type SkImage } from '@shopify/react-native-skia';
|
|
2
2
|
import type { AnnotationBackgroundImage, AnnotationBackgroundLayer } from '../../../types/annotation.js';
|
|
3
3
|
import type { ViewportState } from '../viewport.js';
|
|
4
|
-
export declare const useBackgroundSkImage: (image: AnnotationBackgroundImage | null | undefined, resolveUrl?: (path: string) => Promise<string
|
|
4
|
+
export declare const useBackgroundSkImage: (image: AnnotationBackgroundImage | null | undefined, resolveUrl?: (path: string) => Promise<string>, deferToResolver?: boolean) => SkImage | null;
|
|
5
5
|
export interface BackgroundSkImageLoaderProps {
|
|
6
6
|
layer: AnnotationBackgroundLayer;
|
|
7
7
|
resolveUrl?: (path: string) => Promise<string>;
|
|
8
|
+
deferToResolver?: boolean;
|
|
8
9
|
onImage: (id: string, image: SkImage | null) => void;
|
|
9
10
|
}
|
|
10
|
-
export declare const BackgroundSkImageLoader: ({ layer, resolveUrl, onImage, }: BackgroundSkImageLoaderProps) => null;
|
|
11
|
+
export declare const BackgroundSkImageLoader: ({ layer, resolveUrl, deferToResolver, onImage, }: BackgroundSkImageLoaderProps) => null;
|
|
11
12
|
export interface BackgroundLayerElementProps {
|
|
12
13
|
layer: AnnotationBackgroundLayer;
|
|
13
14
|
resolveUrl?: (path: string) => Promise<string>;
|
|
15
|
+
deferToResolver?: boolean;
|
|
14
16
|
skImage?: SkImage | null;
|
|
15
17
|
viewport?: ViewportState;
|
|
16
18
|
screenWidth?: number;
|
|
17
19
|
screenHeight?: number;
|
|
18
20
|
}
|
|
19
|
-
export declare const BackgroundLayerElement: import("react").MemoExoticComponent<({ layer, resolveUrl, skImage: skImageProp, viewport, screenWidth, screenHeight, }: BackgroundLayerElementProps) => import("react/jsx-runtime").JSX.Element | null>;
|
|
21
|
+
export declare const BackgroundLayerElement: import("react").MemoExoticComponent<({ layer, resolveUrl, deferToResolver, skImage: skImageProp, viewport, screenWidth, screenHeight, }: BackgroundLayerElementProps) => import("react/jsx-runtime").JSX.Element | null>;
|
|
@@ -11,8 +11,8 @@ import { BackgroundSvgElement } from './BackgroundSvg.js';
|
|
|
11
11
|
// re-decodes. Returns null until loaded, or when `image` is null/undefined.
|
|
12
12
|
// Hooks run unconditionally regardless of `image`. Raster only — SVG
|
|
13
13
|
// backgrounds self-load in BackgroundSvgElement (parsing an SVG is cheap).
|
|
14
|
-
export const useBackgroundSkImage = (image, resolveUrl) => {
|
|
15
|
-
const url = useBackgroundUrl(image, resolveUrl);
|
|
14
|
+
export const useBackgroundSkImage = (image, resolveUrl, deferToResolver) => {
|
|
15
|
+
const url = useBackgroundUrl(image, resolveUrl, deferToResolver);
|
|
16
16
|
return useImage(url || null);
|
|
17
17
|
};
|
|
18
18
|
// Null-rendering decode host: one mounted instance per background layer, so a
|
|
@@ -20,10 +20,10 @@ export const useBackgroundSkImage = (image, resolveUrl) => {
|
|
|
20
20
|
// mapping over hooks. The consumer (the native Inner) mounts one per layer,
|
|
21
21
|
// accumulates the reported images into a Record keyed by layer id, and feeds
|
|
22
22
|
// that to every canvas sharing the decode (main + magnifier loupe).
|
|
23
|
-
export const BackgroundSkImageLoader = ({ layer, resolveUrl, onImage, }) => {
|
|
23
|
+
export const BackgroundSkImageLoader = ({ layer, resolveUrl, deferToResolver, onImage, }) => {
|
|
24
24
|
// SVG layers never draw through an SkImage (BackgroundSvgElement owns its
|
|
25
25
|
// own vector pipeline), so skip the decode and report null.
|
|
26
|
-
const image = useBackgroundSkImage(layer.format === 'svg' ? null : layer, resolveUrl);
|
|
26
|
+
const image = useBackgroundSkImage(layer.format === 'svg' ? null : layer, resolveUrl, deferToResolver);
|
|
27
27
|
// The callback rides a ref so an inline consumer closure doesn't re-report
|
|
28
28
|
// (and re-render the collector) every render.
|
|
29
29
|
const onImageRef = useRef(onImage);
|
|
@@ -38,12 +38,12 @@ export const BackgroundSkImageLoader = ({ layer, resolveUrl, onImage, }) => {
|
|
|
38
38
|
// A component (not inlined into the scene helper) because each layer owns
|
|
39
39
|
// per-image hooks — URL resolution and raster decode — and AnnotationCanvasSkia
|
|
40
40
|
// is a hook-free plain function.
|
|
41
|
-
export const BackgroundLayerElement = memo(({ layer, resolveUrl, skImage: skImageProp, viewport, screenWidth, screenHeight, }) => {
|
|
41
|
+
export const BackgroundLayerElement = memo(({ layer, resolveUrl, deferToResolver, skImage: skImageProp, viewport, screenWidth, screenHeight, }) => {
|
|
42
42
|
const isSvg = layer.format === 'svg';
|
|
43
43
|
// Self-load only when no pre-decoded image is provided; the hook still runs
|
|
44
44
|
// unconditionally (with a null source, so it no-ops) to satisfy hook rules.
|
|
45
45
|
const provided = skImageProp !== undefined;
|
|
46
|
-
const loaded = useBackgroundSkImage(provided || isSvg ? null : layer, resolveUrl);
|
|
46
|
+
const loaded = useBackgroundSkImage(provided || isSvg ? null : layer, resolveUrl, deferToResolver);
|
|
47
47
|
const skImage = provided ? skImageProp : loaded;
|
|
48
48
|
const t = layer.transform;
|
|
49
49
|
let content = null;
|
|
@@ -53,7 +53,7 @@ export const BackgroundLayerElement = memo(({ layer, resolveUrl, skImage: skImag
|
|
|
53
53
|
// through the browser at viewport resolution. Both draw at intrinsic
|
|
54
54
|
// size — the Group below owns the placement — but the web rasterizer
|
|
55
55
|
// still needs the doc rect to crop against the visible region.
|
|
56
|
-
content = (_jsx(BackgroundSvgElement, { image: layer, dims: backgroundLayerDocRect(layer), resolveUrl: resolveUrl, viewport: viewport, screenWidth: screenWidth, screenHeight: screenHeight }));
|
|
56
|
+
content = (_jsx(BackgroundSvgElement, { image: layer, dims: backgroundLayerDocRect(layer), resolveUrl: resolveUrl, deferToResolver: deferToResolver, viewport: viewport, screenWidth: screenWidth, screenHeight: screenHeight }));
|
|
57
57
|
}
|
|
58
58
|
else if (skImage) {
|
|
59
59
|
content = (_jsx(Image, { image: skImage, x: 0, y: 0, width: layer.widthPx, height: layer.heightPx, fit: "fill" }));
|
|
@@ -116,8 +116,8 @@ const useBackgroundSkSvg = (url) => {
|
|
|
116
116
|
}, []);
|
|
117
117
|
return svg;
|
|
118
118
|
};
|
|
119
|
-
const SkSvgBackground = ({ image, resolveUrl }) => {
|
|
120
|
-
const url = useBackgroundUrl(image, resolveUrl);
|
|
119
|
+
const SkSvgBackground = ({ image, resolveUrl, deferToResolver, }) => {
|
|
120
|
+
const url = useBackgroundUrl(image, resolveUrl, deferToResolver);
|
|
121
121
|
const svg = useBackgroundSkSvg(url);
|
|
122
122
|
if (!svg)
|
|
123
123
|
return null;
|
|
@@ -169,8 +169,8 @@ const loadSvgImage = (xml) => new Promise((resolve, reject) => {
|
|
|
169
169
|
};
|
|
170
170
|
img.src = objectUrl;
|
|
171
171
|
});
|
|
172
|
-
const BrowserSvgBackground = ({ image, dims, resolveUrl, viewport, screenWidth, screenHeight, }) => {
|
|
173
|
-
const url = useBackgroundUrl(image, resolveUrl);
|
|
172
|
+
const BrowserSvgBackground = ({ image, dims, resolveUrl, deferToResolver, viewport, screenWidth, screenHeight, }) => {
|
|
173
|
+
const url = useBackgroundUrl(image, resolveUrl, deferToResolver);
|
|
174
174
|
const [svgText, setSvgText] = useState(null);
|
|
175
175
|
useEffect(() => {
|
|
176
176
|
if (!url) {
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { AnnotationBackgroundImage } from '../../../types/annotation.js';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const initialBackgroundUrl: (image: AnnotationBackgroundImage | null | undefined, resolveUrl: ((path: string) => Promise<string>) | undefined, deferToResolver: boolean | undefined) => string;
|
|
3
|
+
export declare const useBackgroundUrl: (image: AnnotationBackgroundImage | null | undefined, resolveUrl?: (path: string) => Promise<string>, deferToResolver?: boolean) => string;
|
|
@@ -2,8 +2,21 @@ import { useEffect, useRef, useState } from 'react';
|
|
|
2
2
|
// Resolve a background image's (possibly expired) storage URL. Shared by the
|
|
3
3
|
// raster loader (BackgroundLayerElement / BackgroundSkImageLoader) and both
|
|
4
4
|
// SVG render paths (BackgroundSvg).
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
//
|
|
6
|
+
// `deferToResolver` — start from '' instead of seeding the doc's stored
|
|
7
|
+
// `downloadUrl` while the resolver runs. A consumer whose resolver is
|
|
8
|
+
// LOCAL-FIRST (a cached copy on disk answers without the network) must set
|
|
9
|
+
// this: the seed is handed to the image loader on the very first render, so
|
|
10
|
+
// it starts a full-resolution download from Storage that the local answer
|
|
11
|
+
// then races — wasted bandwidth at best, and the loader keeps whichever
|
|
12
|
+
// result lands LAST, so the remote fetch can override the local decode.
|
|
13
|
+
// A consumer whose resolver is network-only (web/desktop re-resolving a
|
|
14
|
+
// possibly-expired URL) should leave it off: there the seed is a warm start —
|
|
15
|
+
// the resolver normally confirms the same URL, and the early fetch is the
|
|
16
|
+
// image download it would have started anyway.
|
|
17
|
+
export const initialBackgroundUrl = (image, resolveUrl, deferToResolver) => (deferToResolver && resolveUrl ? '' : (image?.downloadUrl ?? ''));
|
|
18
|
+
export const useBackgroundUrl = (image, resolveUrl, deferToResolver) => {
|
|
19
|
+
const [url, setUrl] = useState(() => initialBackgroundUrl(image, resolveUrl, deferToResolver));
|
|
7
20
|
// Hold resolveUrl in a ref so re-resolution is keyed only on the image
|
|
8
21
|
// identity, not the function's. Consumers commonly pass an inline closure
|
|
9
22
|
// (a new reference each render); depending on it directly would re-run a
|
|
@@ -16,7 +16,7 @@ export declare const GROUP_HEADER_COLLAPSED_WIDTH: number;
|
|
|
16
16
|
export declare const GROUP_HEADER_COLLAPSED_HEIGHT: number;
|
|
17
17
|
export declare const groupHeaderBaseDims: (collapsed?: boolean) => StampTileDims;
|
|
18
18
|
export declare const DEFAULT_TILE_SCALE = 1;
|
|
19
|
-
export declare const TILE_SCALE_MIN = 0.
|
|
19
|
+
export declare const TILE_SCALE_MIN = 0.05;
|
|
20
20
|
export declare const TILE_SCALE_MAX = 2;
|
|
21
21
|
export declare const resolveTileScaleFactor: (canvas: Pick<AnnotationCanvasState, "tileScaleFactor" | "tileScaleFactorMobile">, platform: TileScalePlatform) => number;
|
|
22
22
|
export declare const resolveHeaderTileScaleFactor: (canvas: Pick<AnnotationCanvasState, "tileScaleFactor" | "tileScaleFactorMobile" | "headerTileScaleFactor" | "headerTileScaleFactorMobile">, platform: TileScalePlatform) => number;
|
|
@@ -67,7 +67,13 @@ export const groupHeaderBaseDims = (collapsed) => collapsed
|
|
|
67
67
|
// Web and mobile persist separate knobs (AnnotationCanvasState.tileScaleFactor
|
|
68
68
|
// vs .tileScaleFactorMobile) — read them through resolveTileScaleFactor below.
|
|
69
69
|
export const DEFAULT_TILE_SCALE = 1;
|
|
70
|
-
|
|
70
|
+
// Floor 0.05 ≈ a 5pt speck at the content-fit view — far under tap size,
|
|
71
|
+
// which is fine: the zoom multiplier composes, so pinching in restores the
|
|
72
|
+
// tap target. (Was 0.4; large dense drawings needed much smaller tiles.)
|
|
73
|
+
// NOTE: the slider's 0.05 commit rounding makes the first step off the floor
|
|
74
|
+
// a 2× jump — if that grates in practice, finer low-end rounding and/or a log
|
|
75
|
+
// track in the TileScaleSlider components is the follow-up.
|
|
76
|
+
export const TILE_SCALE_MIN = 0.05;
|
|
71
77
|
export const TILE_SCALE_MAX = 2;
|
|
72
78
|
// Resolve the document-wide tile-scale knob for a platform. Web and mobile
|
|
73
79
|
// persist separate values because a scale calibrated against a desktop window
|