@zerohive/hive-viewer 2.0.3 → 2.0.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/index.mjs CHANGED
@@ -11,7 +11,7 @@ import { MessageSquare } from "lucide-react";
11
11
  import { useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
12
12
 
13
13
  // src/components/RenderableSignatureImage.tsx
14
- import { useEffect, useState } from "react";
14
+ import { memo, useEffect, useState } from "react";
15
15
 
16
16
  // src/utils/signature.ts
17
17
  var SIGNATURE_INK_COLORS = [
@@ -93,7 +93,8 @@ function normalizeSignaturePlacement(placement) {
93
93
  }
94
94
 
95
95
  // src/utils/signatureImage.ts
96
- var processedSignatureCache = /* @__PURE__ */ new Map();
96
+ var signatureAlphaSourceCache = /* @__PURE__ */ new Map();
97
+ var renderedSignatureCache = /* @__PURE__ */ new Map();
97
98
  function ensureSignatureSource(source) {
98
99
  const trimmedSource = source.trim();
99
100
  if (trimmedSource.startsWith("data:") || trimmedSource.startsWith("blob:") || trimmedSource.startsWith("http:") || trimmedSource.startsWith("https:")) {
@@ -141,6 +142,12 @@ function colorDistance(leftRed, leftGreen, leftBlue, rightRed, rightGreen, right
141
142
  deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue
142
143
  );
143
144
  }
145
+ function computeLuminance(red, green, blue) {
146
+ return red * 0.2126 + green * 0.7152 + blue * 0.0722;
147
+ }
148
+ function clamp01(value) {
149
+ return Math.min(1, Math.max(0, value));
150
+ }
144
151
  function hasMeaningfulTransparency(pixels) {
145
152
  let translucentPixels = 0;
146
153
  const totalPixels = pixels.length / 4;
@@ -152,7 +159,10 @@ function hasMeaningfulTransparency(pixels) {
152
159
  return translucentPixels > Math.max(12, totalPixels * 4e-3);
153
160
  }
154
161
  function detectUniformBackground(pixels, width, height) {
155
- const sampleRadius = Math.max(2, Math.min(12, Math.floor(Math.min(width, height) / 12)));
162
+ const sampleRadius = Math.max(
163
+ 2,
164
+ Math.min(12, Math.floor(Math.min(width, height) / 12))
165
+ );
156
166
  const sampleOrigins = [
157
167
  [0, 0],
158
168
  [Math.max(0, width - sampleRadius), 0],
@@ -216,56 +226,98 @@ function detectUniformBackground(pixels, width, height) {
216
226
  }, 0);
217
227
  return maxDistance <= 26 ? background : null;
218
228
  }
219
- function createTintedSignatureFromAlpha(pixels, width, height, inkColor) {
220
- const canvas = createCanvas(width, height);
221
- const context = canvas.getContext("2d");
222
- if (!context) {
223
- return null;
229
+ function measureAlphaBounds(alphaMask, width, height, alphaThreshold = 8) {
230
+ let minX = width;
231
+ let minY = height;
232
+ let maxX = -1;
233
+ let maxY = -1;
234
+ for (let y = 0; y < height; y += 1) {
235
+ for (let x = 0; x < width; x += 1) {
236
+ const alpha = alphaMask[y * width + x];
237
+ if (alpha <= alphaThreshold) {
238
+ continue;
239
+ }
240
+ minX = Math.min(minX, x);
241
+ minY = Math.min(minY, y);
242
+ maxX = Math.max(maxX, x);
243
+ maxY = Math.max(maxY, y);
244
+ }
224
245
  }
225
- const output = context.createImageData(width, height);
226
- for (let offset = 0; offset < pixels.length; offset += 4) {
227
- output.data[offset] = inkColor[0];
228
- output.data[offset + 1] = inkColor[1];
229
- output.data[offset + 2] = inkColor[2];
230
- output.data[offset + 3] = pixels[offset + 3];
246
+ if (maxX < minX || maxY < minY) {
247
+ return { sx: 0, sy: 0, sw: width, sh: height };
231
248
  }
232
- context.putImageData(output, 0, 0);
233
- return canvas;
249
+ const padding = Math.max(6, Math.round(Math.max(width, height) * 0.04));
250
+ const sx = Math.max(0, minX - padding);
251
+ const sy = Math.max(0, minY - padding);
252
+ const sw = Math.min(width - sx, maxX - minX + 1 + padding * 2);
253
+ const sh = Math.min(height - sy, maxY - minY + 1 + padding * 2);
254
+ return { sx, sy, sw, sh };
234
255
  }
235
- function createTintedSignatureFromSolidBackground(pixels, width, height, background, inkColor) {
236
- const canvas = createCanvas(width, height);
237
- const context = canvas.getContext("2d");
238
- if (!context) {
239
- return null;
256
+ function createAlphaMaskFromTransparency(pixels) {
257
+ const alphaMask = new Uint8ClampedArray(pixels.length / 4);
258
+ for (let pixelIndex = 0; pixelIndex < alphaMask.length; pixelIndex += 1) {
259
+ alphaMask[pixelIndex] = pixels[pixelIndex * 4 + 3];
240
260
  }
241
- const output = context.createImageData(width, height);
261
+ return alphaMask;
262
+ }
263
+ function createAlphaMaskFromSolidBackground(pixels, width, height, background) {
264
+ const alphaMask = new Uint8ClampedArray(width * height);
242
265
  let foregroundPixels = 0;
243
- const totalPixels = width * height;
244
- for (let offset = 0; offset < pixels.length; offset += 4) {
266
+ let maxAlpha = 0;
267
+ const backgroundLuminance = computeLuminance(
268
+ background[0],
269
+ background[1],
270
+ background[2]
271
+ );
272
+ for (let pixelIndex = 0; pixelIndex < alphaMask.length; pixelIndex += 1) {
273
+ const offset = pixelIndex * 4;
274
+ const red = pixels[offset];
275
+ const green = pixels[offset + 1];
276
+ const blue = pixels[offset + 2];
245
277
  const alpha = pixels[offset + 3] / 255;
246
278
  const distance = colorDistance(
247
- pixels[offset],
248
- pixels[offset + 1],
249
- pixels[offset + 2],
279
+ red,
280
+ green,
281
+ blue,
250
282
  background[0],
251
283
  background[1],
252
284
  background[2]
253
285
  );
254
- const normalizedStrength = Math.max(0, Math.min(1, (distance - 14) / 120));
255
- const outputAlpha = Math.round(normalizedStrength * alpha * 255);
256
- output.data[offset] = inkColor[0];
257
- output.data[offset + 1] = inkColor[1];
258
- output.data[offset + 2] = inkColor[2];
259
- output.data[offset + 3] = outputAlpha;
286
+ const channelDelta = Math.max(
287
+ Math.abs(red - background[0]),
288
+ Math.abs(green - background[1]),
289
+ Math.abs(blue - background[2])
290
+ );
291
+ const luminance = computeLuminance(red, green, blue);
292
+ const luminanceDelta = Math.abs(luminance - backgroundLuminance);
293
+ const strength = clamp01(
294
+ Math.max(distance / 120, channelDelta / 90, luminanceDelta / 72)
295
+ );
296
+ const sharpened = strength <= 0.06 ? 0 : Math.pow(strength, 0.68);
297
+ const outputAlpha = Math.round(sharpened * alpha * 255);
298
+ alphaMask[pixelIndex] = outputAlpha;
260
299
  if (outputAlpha > 10) {
261
300
  foregroundPixels += 1;
301
+ maxAlpha = Math.max(maxAlpha, outputAlpha);
262
302
  }
263
303
  }
264
- if (foregroundPixels < Math.max(24, totalPixels * 15e-4) || foregroundPixels > totalPixels * 0.42) {
304
+ const totalPixels = width * height;
305
+ if (foregroundPixels < Math.max(24, totalPixels * 15e-4) || foregroundPixels > totalPixels * 0.42 || maxAlpha === 0) {
265
306
  return null;
266
307
  }
267
- context.putImageData(output, 0, 0);
268
- return canvas;
308
+ const normalizeScale = 255 / maxAlpha;
309
+ for (let pixelIndex = 0; pixelIndex < alphaMask.length; pixelIndex += 1) {
310
+ const alpha = alphaMask[pixelIndex];
311
+ if (alpha === 0) {
312
+ continue;
313
+ }
314
+ const normalized = Math.min(
315
+ 255,
316
+ Math.round(Math.pow(alpha * normalizeScale / 255, 0.92) * 255)
317
+ );
318
+ alphaMask[pixelIndex] = normalized <= 10 ? 0 : normalized;
319
+ }
320
+ return alphaMask;
269
321
  }
270
322
  function parseInkColor(inkColor) {
271
323
  const hex = SIGNATURE_INK_COLOR_VALUES[inkColor];
@@ -275,62 +327,148 @@ function parseInkColor(inkColor) {
275
327
  Number.parseInt(hex.slice(5, 7), 16)
276
328
  ];
277
329
  }
278
- async function getRenderableSignatureImage(source, inkColor) {
330
+ function createTintedSignatureCanvas(alphaSource, inkColor) {
331
+ if (!alphaSource.alphaMask) {
332
+ return null;
333
+ }
334
+ const { bounds } = alphaSource;
335
+ const canvas = createCanvas(bounds.sw, bounds.sh);
336
+ const context = canvas.getContext("2d");
337
+ if (!context) {
338
+ return null;
339
+ }
340
+ const output = context.createImageData(bounds.sw, bounds.sh);
341
+ const ink = parseInkColor(inkColor);
342
+ for (let y = 0; y < bounds.sh; y += 1) {
343
+ for (let x = 0; x < bounds.sw; x += 1) {
344
+ const sourceIndex = (bounds.sy + y) * alphaSource.width + bounds.sx + x;
345
+ const targetIndex = (y * bounds.sw + x) * 4;
346
+ output.data[targetIndex] = ink[0];
347
+ output.data[targetIndex + 1] = ink[1];
348
+ output.data[targetIndex + 2] = ink[2];
349
+ output.data[targetIndex + 3] = alphaSource.alphaMask[sourceIndex];
350
+ }
351
+ }
352
+ context.putImageData(output, 0, 0);
353
+ return canvas;
354
+ }
355
+ async function getSignatureAlphaSource(source) {
279
356
  const resolvedSource = ensureSignatureSource(source);
280
- const normalizedInkColor = normalizeSignatureInkColor(inkColor);
281
- const cacheKey = `${normalizedInkColor}::${resolvedSource}`;
282
- const cached = processedSignatureCache.get(cacheKey);
357
+ const cached = signatureAlphaSourceCache.get(resolvedSource);
283
358
  if (cached) {
284
359
  return cached;
285
360
  }
286
361
  const pending = (async () => {
287
362
  const image = await loadSignatureImage(resolvedSource);
363
+ const fallbackBounds = {
364
+ sx: 0,
365
+ sy: 0,
366
+ sw: image.width,
367
+ sh: image.height
368
+ };
288
369
  const canvas = createCanvas(image.width, image.height);
289
370
  const context = canvas.getContext("2d");
290
371
  if (!context) {
291
- return resolvedSource;
372
+ return {
373
+ source: resolvedSource,
374
+ width: image.width,
375
+ height: image.height,
376
+ alphaMask: null,
377
+ bounds: fallbackBounds
378
+ };
292
379
  }
293
380
  context.drawImage(image, 0, 0, image.width, image.height);
294
381
  let imageData;
295
382
  try {
296
383
  imageData = context.getImageData(0, 0, canvas.width, canvas.height);
297
384
  } catch {
298
- return resolvedSource;
385
+ return {
386
+ source: resolvedSource,
387
+ width: image.width,
388
+ height: image.height,
389
+ alphaMask: null,
390
+ bounds: fallbackBounds
391
+ };
299
392
  }
300
- const inkRgb = parseInkColor(normalizedInkColor);
301
- const transparentRender = hasMeaningfulTransparency(imageData.data) ? createTintedSignatureFromAlpha(
302
- imageData.data,
303
- canvas.width,
304
- canvas.height,
305
- inkRgb
306
- ) : null;
307
- if (transparentRender) {
308
- return transparentRender.toDataURL("image/png");
393
+ const alphaMask = hasMeaningfulTransparency(imageData.data) ? createAlphaMaskFromTransparency(imageData.data) : (() => {
394
+ const background = detectUniformBackground(
395
+ imageData.data,
396
+ canvas.width,
397
+ canvas.height
398
+ );
399
+ return background ? createAlphaMaskFromSolidBackground(
400
+ imageData.data,
401
+ canvas.width,
402
+ canvas.height,
403
+ background
404
+ ) : null;
405
+ })();
406
+ if (!alphaMask) {
407
+ return {
408
+ source: resolvedSource,
409
+ width: image.width,
410
+ height: image.height,
411
+ alphaMask: null,
412
+ bounds: fallbackBounds
413
+ };
309
414
  }
310
- const background = detectUniformBackground(
311
- imageData.data,
312
- canvas.width,
313
- canvas.height
314
- );
315
- if (!background) {
316
- return resolvedSource;
415
+ return {
416
+ source: resolvedSource,
417
+ width: image.width,
418
+ height: image.height,
419
+ alphaMask,
420
+ bounds: measureAlphaBounds(alphaMask, image.width, image.height)
421
+ };
422
+ })().catch(async () => {
423
+ const image = await loadSignatureImage(resolvedSource);
424
+ return {
425
+ source: resolvedSource,
426
+ width: image.width,
427
+ height: image.height,
428
+ alphaMask: null,
429
+ bounds: { sx: 0, sy: 0, sw: image.width, sh: image.height }
430
+ };
431
+ });
432
+ signatureAlphaSourceCache.set(resolvedSource, pending);
433
+ return pending;
434
+ }
435
+ async function getSignatureRenderMetrics(source) {
436
+ const alphaSource = await getSignatureAlphaSource(source);
437
+ const width = alphaSource.alphaMask ? alphaSource.bounds.sw : alphaSource.width;
438
+ const height = alphaSource.alphaMask ? alphaSource.bounds.sh : alphaSource.height;
439
+ return {
440
+ width,
441
+ height,
442
+ aspectRatio: width > 0 && height > 0 ? width / height : 3.1,
443
+ canTint: Boolean(alphaSource.alphaMask)
444
+ };
445
+ }
446
+ async function getRenderableSignatureImage(source, inkColor) {
447
+ const resolvedSource = ensureSignatureSource(source);
448
+ const normalizedInkColor = normalizeSignatureInkColor(inkColor);
449
+ const cacheKey = `${normalizedInkColor}::${resolvedSource}`;
450
+ const cached = renderedSignatureCache.get(cacheKey);
451
+ if (cached) {
452
+ return cached;
453
+ }
454
+ const pending = (async () => {
455
+ const alphaSource = await getSignatureAlphaSource(resolvedSource);
456
+ if (!alphaSource.alphaMask) {
457
+ return alphaSource.source;
317
458
  }
318
- const extractedRender = createTintedSignatureFromSolidBackground(
319
- imageData.data,
320
- canvas.width,
321
- canvas.height,
322
- background,
323
- inkRgb
459
+ const tintedCanvas = createTintedSignatureCanvas(
460
+ alphaSource,
461
+ normalizedInkColor
324
462
  );
325
- return extractedRender ? extractedRender.toDataURL("image/png") : resolvedSource;
463
+ return tintedCanvas ? tintedCanvas.toDataURL("image/png") : alphaSource.source;
326
464
  })().catch(() => resolvedSource);
327
- processedSignatureCache.set(cacheKey, pending);
465
+ renderedSignatureCache.set(cacheKey, pending);
328
466
  return pending;
329
467
  }
330
468
 
331
469
  // src/components/RenderableSignatureImage.tsx
332
470
  import { jsx } from "react/jsx-runtime";
333
- function RenderableSignatureImage(props) {
471
+ var RenderableSignatureImage = memo(function RenderableSignatureImage2(props) {
334
472
  const { signatureImageUrl, inkColor, ...imageProps } = props;
335
473
  const [resolvedSrc, setResolvedSrc] = useState(signatureImageUrl);
336
474
  useEffect(() => {
@@ -349,25 +487,54 @@ function RenderableSignatureImage(props) {
349
487
  };
350
488
  }, [inkColor, signatureImageUrl]);
351
489
  return /* @__PURE__ */ jsx("img", { ...imageProps, src: resolvedSrc, decoding: "async", draggable: false });
352
- }
490
+ });
491
+ RenderableSignatureImage.displayName = "RenderableSignatureImage";
353
492
 
354
493
  // src/components/SignatureOverlay.tsx
355
494
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
495
+ var DEFAULT_SIGNATURE_ASPECT_RATIO = 3.1;
356
496
  function clamp(value, min, max) {
357
497
  return Math.min(max, Math.max(min, value));
358
498
  }
359
- function getDefaultPlacementSize(kind) {
499
+ function clampAspectRatio(value) {
500
+ return clamp(value, 1.8, 6.2);
501
+ }
502
+ function getDefaultSurfaceAspectRatio(kind) {
360
503
  switch (kind) {
361
- case "sheet":
362
- return { width: 0.3, height: 0.16 };
363
504
  case "slide":
364
- return { width: 0.34, height: 0.18 };
505
+ return 16 / 9;
506
+ case "sheet":
507
+ return 1.9;
365
508
  case "image":
366
- return { width: 0.36, height: 0.18 };
509
+ return 1;
367
510
  default:
368
- return { width: 0.34, height: 0.16 };
511
+ return 816 / 1056;
369
512
  }
370
513
  }
514
+ function getSignatureCardAspectRatio(aspectRatio = DEFAULT_SIGNATURE_ASPECT_RATIO) {
515
+ return clamp(aspectRatio * 0.78, 1.35, 4.8);
516
+ }
517
+ function getDefaultPlacementSize(kind, aspectRatio = DEFAULT_SIGNATURE_ASPECT_RATIO, surfaceAspectRatio = getDefaultSurfaceAspectRatio(kind)) {
518
+ const effectiveAspectRatio = getSignatureCardAspectRatio(
519
+ clampAspectRatio(aspectRatio)
520
+ );
521
+ const effectiveSurfaceAspectRatio = clamp(surfaceAspectRatio, 0.45, 2.8);
522
+ const heightTarget = kind === "sheet" ? 0.11 : kind === "slide" ? 0.135 : kind === "image" ? 0.14 : 0.115;
523
+ const widthMin = kind === "sheet" ? 0.2 : kind === "slide" ? 0.22 : kind === "image" ? 0.24 : 0.22;
524
+ const widthMax = kind === "sheet" ? 0.38 : kind === "slide" ? 0.44 : kind === "image" ? 0.48 : 0.4;
525
+ const heightMax = kind === "sheet" ? 0.18 : kind === "slide" ? 0.2 : kind === "image" ? 0.22 : 0.19;
526
+ const width = clamp(
527
+ heightTarget * effectiveAspectRatio / effectiveSurfaceAspectRatio,
528
+ widthMin,
529
+ widthMax
530
+ );
531
+ const height = clamp(
532
+ width * effectiveSurfaceAspectRatio / effectiveAspectRatio,
533
+ 0.075,
534
+ heightMax
535
+ );
536
+ return { width, height };
537
+ }
371
538
  function getDefaultAnnotationSize(kind) {
372
539
  switch (kind) {
373
540
  case "sheet":
@@ -395,11 +562,13 @@ function SignatureOverlay(props) {
395
562
  placements,
396
563
  annotations,
397
564
  pendingSignature,
565
+ pendingSignatureColor,
398
566
  pendingAnnotation,
399
567
  activePlacementId,
400
568
  activeAnnotationId,
401
569
  placeHint,
402
570
  annotationHint,
571
+ cancelPlacementLabel,
403
572
  annotationPlaceholder,
404
573
  signatureAltLabel,
405
574
  signatureAltByLabel,
@@ -419,10 +588,21 @@ function SignatureOverlay(props) {
419
588
  onRemovePlacement,
420
589
  onRemoveAnnotation,
421
590
  onSelectPlacement,
422
- onSelectAnnotation
591
+ onSelectAnnotation,
592
+ onCancelPlacementMode
423
593
  } = props;
424
594
  const layerRef = useRef(null);
425
595
  const [dragState, setDragState] = useState2(null);
596
+ const [dragPreview, setDragPreview] = useState2(null);
597
+ const [placementPreview, setPlacementPreview] = useState2(
598
+ null
599
+ );
600
+ const [pendingSignatureAspectRatio, setPendingSignatureAspectRatio] = useState2(
601
+ DEFAULT_SIGNATURE_ASPECT_RATIO
602
+ );
603
+ const [surfaceAspectRatio, setSurfaceAspectRatio] = useState2(
604
+ () => getDefaultSurfaceAspectRatio(surfaceKind)
605
+ );
426
606
  const visiblePlacements = useMemo(
427
607
  () => placements.filter((placement) => placement.surfaceKey === surfaceKey),
428
608
  [placements, surfaceKey]
@@ -436,6 +616,105 @@ function SignatureOverlay(props) {
436
616
  [visibleAnnotations]
437
617
  );
438
618
  const captureMode = pendingSignature ? "signature" : pendingAnnotation ? "annotation" : null;
619
+ const defaultSignatureSize = useMemo(
620
+ () => getDefaultPlacementSize(
621
+ surfaceKind,
622
+ pendingSignatureAspectRatio,
623
+ surfaceAspectRatio
624
+ ),
625
+ [pendingSignatureAspectRatio, surfaceAspectRatio, surfaceKind]
626
+ );
627
+ const defaultAnnotationSize = useMemo(
628
+ () => getDefaultAnnotationSize(surfaceKind),
629
+ [surfaceKind]
630
+ );
631
+ const previewGeometry = useMemo(() => {
632
+ if (!placementPreview || !captureMode) {
633
+ return null;
634
+ }
635
+ const size = captureMode === "signature" ? defaultSignatureSize : defaultAnnotationSize;
636
+ return {
637
+ width: size.width,
638
+ height: size.height,
639
+ x: clamp(placementPreview.x - size.width / 2, 0, 1 - size.width),
640
+ y: clamp(placementPreview.y - size.height / 2, 0, 1 - size.height)
641
+ };
642
+ }, [
643
+ captureMode,
644
+ defaultAnnotationSize,
645
+ defaultSignatureSize,
646
+ placementPreview
647
+ ]);
648
+ useEffect2(() => {
649
+ const element = layerRef.current;
650
+ if (!element) {
651
+ return;
652
+ }
653
+ const syncSurfaceAspectRatio = () => {
654
+ const rect = element.getBoundingClientRect();
655
+ if (rect.width <= 0 || rect.height <= 0) {
656
+ return;
657
+ }
658
+ const nextAspectRatio = rect.width / rect.height;
659
+ setSurfaceAspectRatio(
660
+ (current) => Math.abs(current - nextAspectRatio) < 1e-3 ? current : nextAspectRatio
661
+ );
662
+ };
663
+ syncSurfaceAspectRatio();
664
+ if (typeof ResizeObserver === "undefined") {
665
+ return;
666
+ }
667
+ const observer = new ResizeObserver(() => {
668
+ syncSurfaceAspectRatio();
669
+ });
670
+ observer.observe(element);
671
+ return () => {
672
+ observer.disconnect();
673
+ };
674
+ }, [surfaceKey, surfaceKind]);
675
+ useEffect2(() => {
676
+ if (!pendingSignature?.signatureImageUrl) {
677
+ setPendingSignatureAspectRatio(DEFAULT_SIGNATURE_ASPECT_RATIO);
678
+ return;
679
+ }
680
+ let cancelled = false;
681
+ void getSignatureRenderMetrics(pendingSignature.signatureImageUrl).then((metrics) => {
682
+ if (!cancelled) {
683
+ setPendingSignatureAspectRatio(
684
+ clampAspectRatio(metrics.aspectRatio || DEFAULT_SIGNATURE_ASPECT_RATIO)
685
+ );
686
+ }
687
+ }).catch(() => {
688
+ if (!cancelled) {
689
+ setPendingSignatureAspectRatio(DEFAULT_SIGNATURE_ASPECT_RATIO);
690
+ }
691
+ });
692
+ return () => {
693
+ cancelled = true;
694
+ };
695
+ }, [pendingSignature?.signatureImageUrl]);
696
+ useEffect2(() => {
697
+ if (!captureMode) {
698
+ setPlacementPreview(null);
699
+ return;
700
+ }
701
+ const handleKeyDown = (event) => {
702
+ if (event.key !== "Escape") {
703
+ return;
704
+ }
705
+ event.preventDefault();
706
+ onCancelPlacementMode();
707
+ };
708
+ window.addEventListener("keydown", handleKeyDown);
709
+ return () => {
710
+ window.removeEventListener("keydown", handleKeyDown);
711
+ };
712
+ }, [captureMode, onCancelPlacementMode]);
713
+ useEffect2(() => {
714
+ if (!dragState) {
715
+ setDragPreview(null);
716
+ }
717
+ }, [dragState]);
439
718
  useEffect2(() => {
440
719
  if (captureMode || dragState) {
441
720
  return;
@@ -474,11 +753,12 @@ function SignatureOverlay(props) {
474
753
  0,
475
754
  1 - dragState.originHeight
476
755
  );
477
- if (dragState.targetType === "signature") {
478
- onUpdatePlacement(dragState.id, { x: nextX, y: nextY });
479
- return;
480
- }
481
- onUpdateAnnotation(dragState.id, { x: nextX, y: nextY });
756
+ setDragPreview({
757
+ x: nextX,
758
+ y: nextY,
759
+ width: dragState.originWidth,
760
+ height: dragState.originHeight
761
+ });
482
762
  return;
483
763
  }
484
764
  if (dragState.targetType === "signature") {
@@ -492,7 +772,9 @@ function SignatureOverlay(props) {
492
772
  0.035,
493
773
  1 - dragState.originY
494
774
  );
495
- onUpdatePlacement(dragState.id, {
775
+ setDragPreview({
776
+ x: dragState.originX,
777
+ y: dragState.originY,
496
778
  width: nextWidth2,
497
779
  height: nextHeight2
498
780
  });
@@ -508,12 +790,22 @@ function SignatureOverlay(props) {
508
790
  0.08,
509
791
  1 - dragState.originY
510
792
  );
511
- onUpdateAnnotation(dragState.id, {
793
+ setDragPreview({
794
+ x: dragState.originX,
795
+ y: dragState.originY,
512
796
  width: nextWidth,
513
797
  height: nextHeight
514
798
  });
515
799
  };
516
800
  const handlePointerUp = () => {
801
+ if (dragPreview) {
802
+ if (dragState.targetType === "signature") {
803
+ onUpdatePlacement(dragState.id, dragPreview);
804
+ } else {
805
+ onUpdateAnnotation(dragState.id, dragPreview);
806
+ }
807
+ }
808
+ setDragPreview(null);
517
809
  setDragState(null);
518
810
  };
519
811
  window.addEventListener("pointermove", handlePointerMove);
@@ -522,7 +814,7 @@ function SignatureOverlay(props) {
522
814
  window.removeEventListener("pointermove", handlePointerMove);
523
815
  window.removeEventListener("pointerup", handlePointerUp);
524
816
  };
525
- }, [dragState, onUpdateAnnotation, onUpdatePlacement]);
817
+ }, [dragPreview, dragState, onUpdateAnnotation, onUpdatePlacement]);
526
818
  const handlePlace = (event) => {
527
819
  if (!layerRef.current || !captureMode) {
528
820
  return;
@@ -531,7 +823,7 @@ function SignatureOverlay(props) {
531
823
  const clickX = (event.clientX - rect.left) / rect.width;
532
824
  const clickY = (event.clientY - rect.top) / rect.height;
533
825
  if (captureMode === "signature" && pendingSignature) {
534
- const { width, height } = getDefaultPlacementSize(surfaceKind);
826
+ const { width, height } = defaultSignatureSize;
535
827
  onPlaceSignature({
536
828
  signature: pendingSignature,
537
829
  surfaceKey,
@@ -547,7 +839,7 @@ function SignatureOverlay(props) {
547
839
  return;
548
840
  }
549
841
  if (captureMode === "annotation") {
550
- const { width, height } = getDefaultAnnotationSize(surfaceKind);
842
+ const { width, height } = defaultAnnotationSize;
551
843
  onPlaceAnnotation({
552
844
  surfaceKey,
553
845
  surfaceKind,
@@ -562,6 +854,16 @@ function SignatureOverlay(props) {
562
854
  });
563
855
  }
564
856
  };
857
+ const updatePlacementPreview = (clientX, clientY) => {
858
+ if (!layerRef.current) {
859
+ return;
860
+ }
861
+ const rect = layerRef.current.getBoundingClientRect();
862
+ setPlacementPreview({
863
+ x: clamp((clientX - rect.left) / rect.width, 0, 1),
864
+ y: clamp((clientY - rect.top) / rect.height, 0, 1)
865
+ });
866
+ };
565
867
  const beginDrag = (event, target, mode) => {
566
868
  if (!layerRef.current) {
567
869
  return;
@@ -571,6 +873,7 @@ function SignatureOverlay(props) {
571
873
  if (target.kind === "signature") {
572
874
  onSelectPlacement(target.item.id);
573
875
  onSelectAnnotation(null);
876
+ setDragPreview(null);
574
877
  setDragState({
575
878
  id: target.item.id,
576
879
  targetType: "signature",
@@ -588,6 +891,7 @@ function SignatureOverlay(props) {
588
891
  }
589
892
  onSelectAnnotation(target.item.id);
590
893
  onSelectPlacement(null);
894
+ setDragPreview(null);
591
895
  setDragState({
592
896
  id: target.item.id,
593
897
  targetType: "annotation",
@@ -601,9 +905,110 @@ function SignatureOverlay(props) {
601
905
  rect: layerRef.current.getBoundingClientRect()
602
906
  });
603
907
  };
908
+ const getSignatureGeometry = (placement) => dragState?.targetType === "signature" && dragState.id === placement.id && dragPreview ? dragPreview : {
909
+ x: placement.x,
910
+ y: placement.y,
911
+ width: placement.width,
912
+ height: placement.height
913
+ };
914
+ const getAnnotationGeometry = (annotation) => dragState?.targetType === "annotation" && dragState.id === annotation.id && dragPreview ? dragPreview : {
915
+ x: annotation.x,
916
+ y: annotation.y,
917
+ width: annotation.width,
918
+ height: annotation.height
919
+ };
604
920
  return /* @__PURE__ */ jsxs("div", { ref: layerRef, className: "hv-signature-overlay", children: [
605
- captureMode && /* @__PURE__ */ jsx2("div", { className: "hv-signature-overlay-capture", onClick: handlePlace, children: /* @__PURE__ */ jsx2("div", { className: "hv-signature-overlay-hint", children: captureMode === "annotation" ? annotationHint : placeHint }) }),
921
+ captureMode && /* @__PURE__ */ jsxs(
922
+ "div",
923
+ {
924
+ className: "hv-signature-overlay-capture",
925
+ onClick: handlePlace,
926
+ onPointerEnter: (event) => updatePlacementPreview(event.clientX, event.clientY),
927
+ onPointerMove: (event) => updatePlacementPreview(event.clientX, event.clientY),
928
+ onPointerLeave: () => setPlacementPreview(null),
929
+ children: [
930
+ /* @__PURE__ */ jsxs(
931
+ "div",
932
+ {
933
+ className: "hv-signature-overlay-banner",
934
+ onPointerDown: (event) => {
935
+ event.stopPropagation();
936
+ },
937
+ onClick: (event) => {
938
+ event.stopPropagation();
939
+ },
940
+ children: [
941
+ /* @__PURE__ */ jsxs("div", { className: "hv-signature-overlay-banner-copy", children: [
942
+ /* @__PURE__ */ jsx2("strong", { className: "hv-signature-overlay-title", children: captureMode === "annotation" ? annotationTitle : signatureAltLabel }),
943
+ /* @__PURE__ */ jsx2("span", { className: "hv-signature-overlay-hint", children: captureMode === "annotation" ? annotationHint : placeHint })
944
+ ] }),
945
+ /* @__PURE__ */ jsx2(
946
+ "button",
947
+ {
948
+ type: "button",
949
+ className: "hv-signature-overlay-cancel",
950
+ onPointerDown: (event) => {
951
+ event.stopPropagation();
952
+ },
953
+ onClick: (event) => {
954
+ event.stopPropagation();
955
+ onCancelPlacementMode();
956
+ },
957
+ children: cancelPlacementLabel
958
+ }
959
+ )
960
+ ]
961
+ }
962
+ ),
963
+ captureMode === "signature" && previewGeometry && pendingSignature && /* @__PURE__ */ jsxs(
964
+ "div",
965
+ {
966
+ className: "hv-signature-stamp hv-signature-ghost",
967
+ style: {
968
+ left: `${previewGeometry.x * 100}%`,
969
+ top: `${previewGeometry.y * 100}%`,
970
+ width: `${previewGeometry.width * 100}%`,
971
+ height: `${previewGeometry.height * 100}%`
972
+ },
973
+ children: [
974
+ /* @__PURE__ */ jsx2("div", { className: "hv-signature-image-wrap", children: /* @__PURE__ */ jsx2(
975
+ RenderableSignatureImage,
976
+ {
977
+ signatureImageUrl: pendingSignature.signatureImageUrl,
978
+ inkColor: pendingSignatureColor,
979
+ alt: signatureAltLabel,
980
+ className: "hv-signature-image"
981
+ }
982
+ ) }),
983
+ /* @__PURE__ */ jsxs("div", { className: "hv-signature-meta", children: [
984
+ pendingSignature.signedBy?.trim() && /* @__PURE__ */ jsx2("span", { className: "hv-signature-meta-name", children: pendingSignature.signedBy.trim() }),
985
+ pendingSignature.jobTitle?.trim() && /* @__PURE__ */ jsx2("span", { className: "hv-signature-meta-jobtitle", children: pendingSignature.jobTitle.trim() }),
986
+ /* @__PURE__ */ jsx2("span", { className: "hv-signature-meta-date", children: normalizeSignatureDate(pendingSignature.dateSigned) })
987
+ ] })
988
+ ]
989
+ }
990
+ ),
991
+ captureMode === "annotation" && previewGeometry && /* @__PURE__ */ jsxs(
992
+ "div",
993
+ {
994
+ className: "hv-annotation-card hv-annotation-ghost",
995
+ style: {
996
+ left: `${previewGeometry.x * 100}%`,
997
+ top: `${previewGeometry.y * 100}%`,
998
+ width: `${previewGeometry.width * 100}%`,
999
+ height: `${previewGeometry.height * 100}%`
1000
+ },
1001
+ children: [
1002
+ /* @__PURE__ */ jsx2("div", { className: "hv-annotation-header", children: /* @__PURE__ */ jsx2("div", { className: "hv-annotation-header-copy", children: /* @__PURE__ */ jsx2("span", { className: "hv-annotation-title", children: annotationTitle }) }) }),
1003
+ /* @__PURE__ */ jsx2("div", { className: "hv-annotation-body", children: /* @__PURE__ */ jsx2("div", { className: "hv-annotation-preview empty", children: annotationPlaceholder }) })
1004
+ ]
1005
+ }
1006
+ )
1007
+ ]
1008
+ }
1009
+ ),
606
1010
  visiblePlacements.map((placement) => {
1011
+ const placementGeometry = getSignatureGeometry(placement);
607
1012
  const isActive = placement.id === activePlacementId;
608
1013
  const hasLinkedAnnotation = linkedAnnotationIds.has(placement.id);
609
1014
  const signer = placement.signature.signedBy?.trim();
@@ -615,20 +1020,12 @@ function SignatureOverlay(props) {
615
1020
  {
616
1021
  className: `hv-signature-stamp ${isActive ? "active" : ""}`,
617
1022
  style: {
618
- left: `${placement.x * 100}%`,
619
- top: `${placement.y * 100}%`,
620
- width: `${placement.width * 100}%`,
621
- height: `${placement.height * 100}%`
622
- },
623
- onPointerDown: (event) => {
624
- if (!isActive) {
625
- event.stopPropagation();
626
- onSelectPlacement(placement.id);
627
- onSelectAnnotation(null);
628
- return;
629
- }
630
- beginDrag(event, { kind: "signature", item: placement }, "move");
1023
+ left: `${placementGeometry.x * 100}%`,
1024
+ top: `${placementGeometry.y * 100}%`,
1025
+ width: `${placementGeometry.width * 100}%`,
1026
+ height: `${placementGeometry.height * 100}%`
631
1027
  },
1028
+ onPointerDown: (event) => beginDrag(event, { kind: "signature", item: placement }, "move"),
632
1029
  onClick: (event) => {
633
1030
  event.stopPropagation();
634
1031
  onSelectPlacement(placement.id);
@@ -710,6 +1107,7 @@ function SignatureOverlay(props) {
710
1107
  );
711
1108
  }),
712
1109
  visibleAnnotations.map((annotation) => {
1110
+ const annotationGeometry = getAnnotationGeometry(annotation);
713
1111
  const isActive = annotation.id === activeAnnotationId;
714
1112
  const isLinked = Boolean(annotation.linkedSignaturePlacementId);
715
1113
  const hasText = annotation.text.trim().length > 0;
@@ -720,8 +1118,8 @@ function SignatureOverlay(props) {
720
1118
  type: "button",
721
1119
  className: `hv-annotation-chip ${isLinked ? "linked" : ""} ${hasText ? "" : "empty"}`,
722
1120
  style: {
723
- left: `${annotation.x * 100}%`,
724
- top: `${annotation.y * 100}%`
1121
+ left: `${annotationGeometry.x * 100}%`,
1122
+ top: `${annotationGeometry.y * 100}%`
725
1123
  },
726
1124
  "aria-label": hasText ? annotation.text : openAnnotationLabel,
727
1125
  title: hasText ? annotation.text : openAnnotationLabel,
@@ -740,10 +1138,10 @@ function SignatureOverlay(props) {
740
1138
  {
741
1139
  className: `hv-annotation-card ${isActive ? "active" : ""} ${isLinked ? "linked" : ""}`,
742
1140
  style: {
743
- left: `${annotation.x * 100}%`,
744
- top: `${annotation.y * 100}%`,
745
- width: `${annotation.width * 100}%`,
746
- height: `${annotation.height * 100}%`
1141
+ left: `${annotationGeometry.x * 100}%`,
1142
+ top: `${annotationGeometry.y * 100}%`,
1143
+ width: `${annotationGeometry.width * 100}%`,
1144
+ height: `${annotationGeometry.height * 100}%`
747
1145
  },
748
1146
  onClick: (event) => {
749
1147
  event.stopPropagation();
@@ -5156,11 +5554,13 @@ function RichTextEditor(props) {
5156
5554
  placements: props.signatureOverlay.placements,
5157
5555
  annotations: props.signatureOverlay.annotations,
5158
5556
  pendingSignature: props.signatureOverlay.pendingSignature,
5557
+ pendingSignatureColor: props.signatureOverlay.pendingSignatureColor,
5159
5558
  pendingAnnotation: props.signatureOverlay.pendingAnnotation,
5160
5559
  activePlacementId: props.signatureOverlay.activePlacementId,
5161
5560
  activeAnnotationId: props.signatureOverlay.activeAnnotationId,
5162
5561
  placeHint: props.signatureOverlay.placeHint,
5163
5562
  annotationHint: props.signatureOverlay.annotationHint,
5563
+ cancelPlacementLabel: props.signatureOverlay.cancelPlacementLabel,
5164
5564
  annotationPlaceholder: props.signatureOverlay.annotationPlaceholder,
5165
5565
  signatureAltLabel: props.signatureOverlay.signatureAltLabel,
5166
5566
  signatureAltByLabel: props.signatureOverlay.signatureAltByLabel,
@@ -5180,7 +5580,8 @@ function RichTextEditor(props) {
5180
5580
  onRemovePlacement: props.signatureOverlay.onRemovePlacement,
5181
5581
  onRemoveAnnotation: props.signatureOverlay.onRemoveAnnotation,
5182
5582
  onSelectPlacement: props.signatureOverlay.onSelectPlacement,
5183
- onSelectAnnotation: props.signatureOverlay.onSelectAnnotation
5583
+ onSelectAnnotation: props.signatureOverlay.onSelectAnnotation,
5584
+ onCancelPlacementMode: props.signatureOverlay.onCancelPlacementMode
5184
5585
  }
5185
5586
  )
5186
5587
  ]
@@ -5219,11 +5620,13 @@ function RichTextEditor(props) {
5219
5620
  placements: props.signatureOverlay.placements,
5220
5621
  annotations: props.signatureOverlay.annotations,
5221
5622
  pendingSignature: props.signatureOverlay.pendingSignature,
5623
+ pendingSignatureColor: props.signatureOverlay.pendingSignatureColor,
5222
5624
  pendingAnnotation: props.signatureOverlay.pendingAnnotation,
5223
5625
  activePlacementId: props.signatureOverlay.activePlacementId,
5224
5626
  activeAnnotationId: props.signatureOverlay.activeAnnotationId,
5225
5627
  placeHint: props.signatureOverlay.placeHint,
5226
5628
  annotationHint: props.signatureOverlay.annotationHint,
5629
+ cancelPlacementLabel: props.signatureOverlay.cancelPlacementLabel,
5227
5630
  annotationPlaceholder: props.signatureOverlay.annotationPlaceholder,
5228
5631
  signatureAltLabel: props.signatureOverlay.signatureAltLabel,
5229
5632
  signatureAltByLabel: props.signatureOverlay.signatureAltByLabel,
@@ -5243,7 +5646,8 @@ function RichTextEditor(props) {
5243
5646
  onRemovePlacement: props.signatureOverlay.onRemovePlacement,
5244
5647
  onRemoveAnnotation: props.signatureOverlay.onRemoveAnnotation,
5245
5648
  onSelectPlacement: props.signatureOverlay.onSelectPlacement,
5246
- onSelectAnnotation: props.signatureOverlay.onSelectAnnotation
5649
+ onSelectAnnotation: props.signatureOverlay.onSelectAnnotation,
5650
+ onCancelPlacementMode: props.signatureOverlay.onCancelPlacementMode
5247
5651
  }
5248
5652
  )
5249
5653
  ] }) })
@@ -5290,11 +5694,13 @@ function RichTextEditor(props) {
5290
5694
  placements: props.signatureOverlay.placements,
5291
5695
  annotations: props.signatureOverlay.annotations,
5292
5696
  pendingSignature: props.signatureOverlay.pendingSignature,
5697
+ pendingSignatureColor: props.signatureOverlay.pendingSignatureColor,
5293
5698
  pendingAnnotation: props.signatureOverlay.pendingAnnotation,
5294
5699
  activePlacementId: props.signatureOverlay.activePlacementId,
5295
5700
  activeAnnotationId: props.signatureOverlay.activeAnnotationId,
5296
5701
  placeHint: props.signatureOverlay.placeHint,
5297
5702
  annotationHint: props.signatureOverlay.annotationHint,
5703
+ cancelPlacementLabel: props.signatureOverlay.cancelPlacementLabel,
5298
5704
  annotationPlaceholder: props.signatureOverlay.annotationPlaceholder,
5299
5705
  signatureAltLabel: props.signatureOverlay.signatureAltLabel,
5300
5706
  signatureAltByLabel: props.signatureOverlay.signatureAltByLabel,
@@ -5314,7 +5720,8 @@ function RichTextEditor(props) {
5314
5720
  onRemovePlacement: props.signatureOverlay.onRemovePlacement,
5315
5721
  onRemoveAnnotation: props.signatureOverlay.onRemoveAnnotation,
5316
5722
  onSelectPlacement: props.signatureOverlay.onSelectPlacement,
5317
- onSelectAnnotation: props.signatureOverlay.onSelectAnnotation
5723
+ onSelectAnnotation: props.signatureOverlay.onSelectAnnotation,
5724
+ onCancelPlacementMode: props.signatureOverlay.onCancelPlacementMode
5318
5725
  }
5319
5726
  )
5320
5727
  ]
@@ -5860,11 +6267,13 @@ function RichTextEditor(props) {
5860
6267
  placements: props.signatureOverlay.placements,
5861
6268
  annotations: props.signatureOverlay.annotations,
5862
6269
  pendingSignature: props.signatureOverlay.pendingSignature,
6270
+ pendingSignatureColor: props.signatureOverlay.pendingSignatureColor,
5863
6271
  pendingAnnotation: props.signatureOverlay.pendingAnnotation,
5864
6272
  activePlacementId: props.signatureOverlay.activePlacementId,
5865
6273
  activeAnnotationId: props.signatureOverlay.activeAnnotationId,
5866
6274
  placeHint: props.signatureOverlay.placeHint,
5867
6275
  annotationHint: props.signatureOverlay.annotationHint,
6276
+ cancelPlacementLabel: props.signatureOverlay.cancelPlacementLabel,
5868
6277
  annotationPlaceholder: props.signatureOverlay.annotationPlaceholder,
5869
6278
  signatureAltLabel: props.signatureOverlay.signatureAltLabel,
5870
6279
  signatureAltByLabel: props.signatureOverlay.signatureAltByLabel,
@@ -5884,7 +6293,8 @@ function RichTextEditor(props) {
5884
6293
  onRemovePlacement: props.signatureOverlay.onRemovePlacement,
5885
6294
  onRemoveAnnotation: props.signatureOverlay.onRemoveAnnotation,
5886
6295
  onSelectPlacement: props.signatureOverlay.onSelectPlacement,
5887
- onSelectAnnotation: props.signatureOverlay.onSelectAnnotation
6296
+ onSelectAnnotation: props.signatureOverlay.onSelectAnnotation,
6297
+ onCancelPlacementMode: props.signatureOverlay.onCancelPlacementMode
5888
6298
  }
5889
6299
  )
5890
6300
  ] }) })
@@ -6770,11 +7180,13 @@ function SpreadsheetEditor(props) {
6770
7180
  placements: props.signatureOverlay.placements,
6771
7181
  annotations: props.signatureOverlay.annotations,
6772
7182
  pendingSignature: props.signatureOverlay.pendingSignature,
7183
+ pendingSignatureColor: props.signatureOverlay.pendingSignatureColor,
6773
7184
  pendingAnnotation: props.signatureOverlay.pendingAnnotation,
6774
7185
  activePlacementId: props.signatureOverlay.activePlacementId,
6775
7186
  activeAnnotationId: props.signatureOverlay.activeAnnotationId,
6776
7187
  placeHint: props.signatureOverlay.placeHint,
6777
7188
  annotationHint: props.signatureOverlay.annotationHint,
7189
+ cancelPlacementLabel: props.signatureOverlay.cancelPlacementLabel,
6778
7190
  annotationPlaceholder: props.signatureOverlay.annotationPlaceholder,
6779
7191
  signatureAltLabel: props.signatureOverlay.signatureAltLabel,
6780
7192
  signatureAltByLabel: props.signatureOverlay.signatureAltByLabel,
@@ -6794,7 +7206,8 @@ function SpreadsheetEditor(props) {
6794
7206
  onRemovePlacement: props.signatureOverlay.onRemovePlacement,
6795
7207
  onRemoveAnnotation: props.signatureOverlay.onRemoveAnnotation,
6796
7208
  onSelectPlacement: props.signatureOverlay.onSelectPlacement,
6797
- onSelectAnnotation: props.signatureOverlay.onSelectAnnotation
7209
+ onSelectAnnotation: props.signatureOverlay.onSelectAnnotation,
7210
+ onCancelPlacementMode: props.signatureOverlay.onCancelPlacementMode
6798
7211
  }
6799
7212
  )
6800
7213
  ] }) })
@@ -6832,11 +7245,13 @@ function ImageRenderer(props) {
6832
7245
  placements: props.signatureOverlay.placements,
6833
7246
  annotations: props.signatureOverlay.annotations,
6834
7247
  pendingSignature: props.signatureOverlay.pendingSignature,
7248
+ pendingSignatureColor: props.signatureOverlay.pendingSignatureColor,
6835
7249
  pendingAnnotation: props.signatureOverlay.pendingAnnotation,
6836
7250
  activePlacementId: props.signatureOverlay.activePlacementId,
6837
7251
  activeAnnotationId: props.signatureOverlay.activeAnnotationId,
6838
7252
  placeHint: props.signatureOverlay.placeHint,
6839
7253
  annotationHint: props.signatureOverlay.annotationHint,
7254
+ cancelPlacementLabel: props.signatureOverlay.cancelPlacementLabel,
6840
7255
  annotationPlaceholder: props.signatureOverlay.annotationPlaceholder,
6841
7256
  signatureAltLabel: props.signatureOverlay.signatureAltLabel,
6842
7257
  signatureAltByLabel: props.signatureOverlay.signatureAltByLabel,
@@ -6856,7 +7271,8 @@ function ImageRenderer(props) {
6856
7271
  onRemovePlacement: props.signatureOverlay.onRemovePlacement,
6857
7272
  onRemoveAnnotation: props.signatureOverlay.onRemoveAnnotation,
6858
7273
  onSelectPlacement: props.signatureOverlay.onSelectPlacement,
6859
- onSelectAnnotation: props.signatureOverlay.onSelectAnnotation
7274
+ onSelectAnnotation: props.signatureOverlay.onSelectAnnotation,
7275
+ onCancelPlacementMode: props.signatureOverlay.onCancelPlacementMode
6860
7276
  }
6861
7277
  )
6862
7278
  ] }) });
@@ -7045,11 +7461,13 @@ function PdfPage({
7045
7461
  placements: signatureOverlay.placements,
7046
7462
  annotations: signatureOverlay.annotations,
7047
7463
  pendingSignature: signatureOverlay.pendingSignature,
7464
+ pendingSignatureColor: signatureOverlay.pendingSignatureColor,
7048
7465
  pendingAnnotation: signatureOverlay.pendingAnnotation,
7049
7466
  activePlacementId: signatureOverlay.activePlacementId,
7050
7467
  activeAnnotationId: signatureOverlay.activeAnnotationId,
7051
7468
  placeHint: signatureOverlay.placeHint,
7052
7469
  annotationHint: signatureOverlay.annotationHint,
7470
+ cancelPlacementLabel: signatureOverlay.cancelPlacementLabel,
7053
7471
  annotationPlaceholder: signatureOverlay.annotationPlaceholder,
7054
7472
  signatureAltLabel: signatureOverlay.signatureAltLabel,
7055
7473
  signatureAltByLabel: signatureOverlay.signatureAltByLabel,
@@ -7069,7 +7487,8 @@ function PdfPage({
7069
7487
  onRemovePlacement: signatureOverlay.onRemovePlacement,
7070
7488
  onRemoveAnnotation: signatureOverlay.onRemoveAnnotation,
7071
7489
  onSelectPlacement: signatureOverlay.onSelectPlacement,
7072
- onSelectAnnotation: signatureOverlay.onSelectAnnotation
7490
+ onSelectAnnotation: signatureOverlay.onSelectAnnotation,
7491
+ onCancelPlacementMode: signatureOverlay.onCancelPlacementMode
7073
7492
  }
7074
7493
  )
7075
7494
  ]
@@ -7568,11 +7987,13 @@ function PptxRenderer(props) {
7568
7987
  placements: props.signatureOverlay.placements,
7569
7988
  annotations: props.signatureOverlay.annotations,
7570
7989
  pendingSignature: props.signatureOverlay.pendingSignature,
7990
+ pendingSignatureColor: props.signatureOverlay.pendingSignatureColor,
7571
7991
  pendingAnnotation: props.signatureOverlay.pendingAnnotation,
7572
7992
  activePlacementId: props.signatureOverlay.activePlacementId,
7573
7993
  activeAnnotationId: props.signatureOverlay.activeAnnotationId,
7574
7994
  placeHint: props.signatureOverlay.placeHint,
7575
7995
  annotationHint: props.signatureOverlay.annotationHint,
7996
+ cancelPlacementLabel: props.signatureOverlay.cancelPlacementLabel,
7576
7997
  annotationPlaceholder: props.signatureOverlay.annotationPlaceholder,
7577
7998
  signatureAltLabel: props.signatureOverlay.signatureAltLabel,
7578
7999
  signatureAltByLabel: props.signatureOverlay.signatureAltByLabel,
@@ -7592,7 +8013,8 @@ function PptxRenderer(props) {
7592
8013
  onRemovePlacement: props.signatureOverlay.onRemovePlacement,
7593
8014
  onRemoveAnnotation: props.signatureOverlay.onRemoveAnnotation,
7594
8015
  onSelectPlacement: props.signatureOverlay.onSelectPlacement,
7595
- onSelectAnnotation: props.signatureOverlay.onSelectAnnotation
8016
+ onSelectAnnotation: props.signatureOverlay.onSelectAnnotation,
8017
+ onCancelPlacementMode: props.signatureOverlay.onCancelPlacementMode
7596
8018
  }
7597
8019
  )
7598
8020
  ]
@@ -7630,13 +8052,13 @@ var defaultLocale = {
7630
8052
  "signatures.title": "Signatures",
7631
8053
  "signatures.empty": "No signatures",
7632
8054
  "signatures.new": "New Signature",
7633
- "signatures.ready": "Ready to place",
8055
+ "signatures.ready": "Placement mode active",
7634
8056
  "signatures.cancelPlacement": "Cancel placement",
7635
8057
  "signatures.drawTitle": "Draw Signature",
7636
8058
  "signatures.drawHelp": "Sign above using your mouse or finger",
7637
8059
  "signatures.clear": "Clear",
7638
8060
  "signatures.createAndUse": "Create & Use",
7639
- "signatures.placeHint": "Click on the document surface to place the signature.",
8061
+ "signatures.placeHint": "Move your pointer to preview the signature, click to place it, then drag to reposition. Press Esc to cancel.",
7640
8062
  "signatures.alt": "Signature",
7641
8063
  "signatures.altBy": "Signature by",
7642
8064
  "signatures.noteIndicator": "Note",
@@ -7646,7 +8068,7 @@ var defaultLocale = {
7646
8068
  "signatures.color.blue": "Blue",
7647
8069
  "signatures.color.red": "Red",
7648
8070
  "signatures.color.green": "Green",
7649
- "annotations.placeHint": "Click on the document surface to place an annotation.",
8071
+ "annotations.placeHint": "Move your pointer to preview the note, click to place it, then drag to reposition. Press Esc to cancel.",
7650
8072
  "annotations.placeholder": "Add instruction or review note...",
7651
8073
  "annotations.title": "Annotation",
7652
8074
  "annotations.linkedTitle": "Signature Note",
@@ -8669,6 +9091,7 @@ function getFileNameHint(fileUrl, fileName) {
8669
9091
  function DocumentViewer(props) {
8670
9092
  const mode = props.mode ?? "view";
8671
9093
  const theme = props.theme ?? "light";
9094
+ const externalLoading = props.loading ?? false;
8672
9095
  const locale = useMemo9(
8673
9096
  () => ({ ...defaultLocale, ...props.locale }),
8674
9097
  [props.locale]
@@ -8818,6 +9241,12 @@ function DocumentViewer(props) {
8818
9241
  let active = true;
8819
9242
  let cleanupSource;
8820
9243
  const loadFile = async () => {
9244
+ if (externalLoading) {
9245
+ setLoading(true);
9246
+ setError("");
9247
+ setResolved(null);
9248
+ return;
9249
+ }
8821
9250
  setLoading(true);
8822
9251
  setError("");
8823
9252
  setResolved(null);
@@ -8863,6 +9292,7 @@ function DocumentViewer(props) {
8863
9292
  cleanupSource?.();
8864
9293
  };
8865
9294
  }, [
9295
+ externalLoading,
8866
9296
  hasIncomingSource,
8867
9297
  isRequestedCreateUnsupported,
8868
9298
  mode,
@@ -9024,12 +9454,17 @@ function DocumentViewer(props) {
9024
9454
  };
9025
9455
  const handleSignatureSelect = (signature) => {
9026
9456
  setSelectedSignature(normalizeSignature(signature));
9027
- setSelectedSignatureColor("black");
9028
9457
  setIsPlacingAnnotation(false);
9029
9458
  setActivePlacementId(null);
9030
9459
  setActiveAnnotationId(null);
9031
9460
  props.onSign?.(normalizeSignature(signature));
9032
9461
  };
9462
+ const handleCancelPlacementMode = () => {
9463
+ setSelectedSignature(null);
9464
+ setIsPlacingAnnotation(false);
9465
+ setActivePlacementId(null);
9466
+ setActiveAnnotationId(null);
9467
+ };
9033
9468
  const handlePlaceSignature = (placement) => {
9034
9469
  const nextPlacement = normalizeSignaturePlacement({
9035
9470
  id: createPlacementId(),
@@ -9048,8 +9483,7 @@ function DocumentViewer(props) {
9048
9483
  });
9049
9484
  updatePlacements((prev) => [...prev, nextPlacement]);
9050
9485
  setSelectedSignature(null);
9051
- setSelectedSignatureColor("black");
9052
- setActivePlacementId(null);
9486
+ setActivePlacementId(nextPlacement.id);
9053
9487
  setActiveAnnotationId(null);
9054
9488
  };
9055
9489
  const handlePlaceAnnotation = (annotation) => {
@@ -9099,11 +9533,13 @@ function DocumentViewer(props) {
9099
9533
  placements,
9100
9534
  annotations,
9101
9535
  pendingSignature: selectedSignature,
9536
+ pendingSignatureColor: selectedSignature ? selectedSignatureColor : void 0,
9102
9537
  pendingAnnotation: isPlacingAnnotation,
9103
9538
  activePlacementId,
9104
9539
  activeAnnotationId,
9105
9540
  placeHint: locale["signatures.placeHint"],
9106
9541
  annotationHint: locale["annotations.placeHint"],
9542
+ cancelPlacementLabel: locale["signatures.cancelPlacement"],
9107
9543
  annotationPlaceholder: locale["annotations.placeholder"],
9108
9544
  signatureAltLabel: locale["signatures.alt"],
9109
9545
  signatureAltByLabel: locale["signatures.altBy"],
@@ -9130,15 +9566,20 @@ function DocumentViewer(props) {
9130
9566
  onSelectPlacement: (id) => {
9131
9567
  setActivePlacementId(id);
9132
9568
  if (id) {
9569
+ setSelectedSignature(null);
9570
+ setIsPlacingAnnotation(false);
9133
9571
  setActiveAnnotationId(null);
9134
9572
  }
9135
9573
  },
9136
9574
  onSelectAnnotation: (id) => {
9137
9575
  setActiveAnnotationId(id);
9138
9576
  if (id) {
9577
+ setSelectedSignature(null);
9578
+ setIsPlacingAnnotation(false);
9139
9579
  setActivePlacementId(null);
9140
9580
  }
9141
- }
9581
+ },
9582
+ onCancelPlacementMode: handleCancelPlacementMode
9142
9583
  };
9143
9584
  const saveReady = useMemo9(() => {
9144
9585
  if (!resolved) {