react-os-shell 0.2.23 → 0.2.24

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.
@@ -1,7 +1,7 @@
1
- export { Files as default, openFilesInTrashMode } from './chunk-4ZGTBQWZ.js';
2
- import './chunk-HQJRNCTU.js';
1
+ export { Files as default, openFilesInTrashMode } from './chunk-NC7UQF6K.js';
2
+ import './chunk-LJ6DLGTY.js';
3
3
  import './chunk-WIJ45SYD.js';
4
4
  import './chunk-QXY6ZHRX.js';
5
5
  import './chunk-PLGHQ7QW.js';
6
- //# sourceMappingURL=Files-XUUCSCHL.js.map
7
- //# sourceMappingURL=Files-XUUCSCHL.js.map
6
+ //# sourceMappingURL=Files-ZPMM53WI.js.map
7
+ //# sourceMappingURL=Files-ZPMM53WI.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Files-XUUCSCHL.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Files-ZPMM53WI.js"}
@@ -0,0 +1,6 @@
1
+ export { Preview as default, setPdfPreview } from './chunk-LJ6DLGTY.js';
2
+ import './chunk-WIJ45SYD.js';
3
+ import './chunk-QXY6ZHRX.js';
4
+ import './chunk-PLGHQ7QW.js';
5
+ //# sourceMappingURL=Preview-QI4WKYS2.js.map
6
+ //# sourceMappingURL=Preview-QI4WKYS2.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Preview-4M4HF2RC.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Preview-QI4WKYS2.js"}
@@ -1,5 +1,5 @@
1
- export { openFilesInTrashMode } from '../chunk-4ZGTBQWZ.js';
2
- export { setPdfPreview } from '../chunk-HQJRNCTU.js';
1
+ export { openFilesInTrashMode } from '../chunk-NC7UQF6K.js';
2
+ export { setPdfPreview } from '../chunk-LJ6DLGTY.js';
3
3
  import '../chunk-WIJ45SYD.js';
4
4
  import '../chunk-QXY6ZHRX.js';
5
5
  import '../chunk-PLGHQ7QW.js';
@@ -20,9 +20,9 @@ var Minesweeper = lazy(() => import('../Minesweeper-YPAR6SPJ.js'));
20
20
  var Email = lazy(() => import('../Email-XTFUEIE5.js'));
21
21
  var GeminiChat = lazy(() => import('../GeminiChat-ITU46EH4.js'));
22
22
  var Calendar = lazy(() => import('../Calendar-24TAKCAJ.js'));
23
- var Preview = lazy(() => import('../Preview-4M4HF2RC.js'));
23
+ var Preview = lazy(() => import('../Preview-QI4WKYS2.js'));
24
24
  var Documents = lazy(() => import('../Documents-QMP6QN3C.js'));
25
- var Files = lazy(() => import('../Files-XUUCSCHL.js'));
25
+ var Files = lazy(() => import('../Files-ZPMM53WI.js'));
26
26
  var Browser = lazy(() => import('../Browser-RJZLTAJQ.js'));
27
27
  var utilityApps = {
28
28
  "/calculator": { component: Calculator, label: "Calculator", size: "sm", allowPinOnTop: true, utility: true, widget: true, autoHeight: true, dimensions: [280, 420] },
@@ -29,6 +29,7 @@ function ImageAnnotator({ src, filename, onClose }) {
29
29
  const [pendingText, setPendingText] = useState(null);
30
30
  const [pendingCrop, setPendingCrop] = useState(null);
31
31
  const dragRef = useRef(null);
32
+ const [isDragging, setIsDragging] = useState(false);
32
33
  const displaySize = useMemo(() => {
33
34
  if (!fitSize) return null;
34
35
  return { w: fitSize.w * zoom, h: fitSize.h * zoom };
@@ -85,7 +86,7 @@ function ImageAnnotator({ src, filename, onClose }) {
85
86
  const ctx = c.getContext("2d");
86
87
  ctx.drawImage(img, 0, 0);
87
88
  for (const m of mosaicAnnos) applyMosaic(ctx, m);
88
- }, [imageSize, mosaicAnnos]);
89
+ }, [imageSize, mosaicAnnos, fitSize]);
89
90
  const evToImage = (e) => {
90
91
  const svg = svgRef.current;
91
92
  if (!svg) return { x: 0, y: 0 };
@@ -97,13 +98,61 @@ function ImageAnnotator({ src, filename, onClose }) {
97
98
  const t = pt.matrixTransform(ctm.inverse());
98
99
  return { x: t.x, y: t.y };
99
100
  };
101
+ const beginDrag = (drag) => {
102
+ dragRef.current = drag;
103
+ setIsDragging(true);
104
+ };
105
+ useEffect(() => {
106
+ if (!isDragging) return;
107
+ const onMove = (ev) => {
108
+ const drag = dragRef.current;
109
+ if (!drag) return;
110
+ const p = evToImage(ev);
111
+ if (drag.kind === "draw") {
112
+ setPreview(makeShape(tool, drag.start, p, color, stroke));
113
+ } else if (drag.kind === "crop") {
114
+ setPendingCrop(normalizeRect(drag.start, p));
115
+ } else if (drag.kind === "move") {
116
+ const dx = p.x - drag.start.x;
117
+ const dy = p.y - drag.start.y;
118
+ setAnnotations((prev) => prev.map((a) => a.id === drag.id ? translate(drag.original, dx, dy) : a));
119
+ } else if (drag.kind === "resize") {
120
+ setAnnotations((prev) => prev.map((a) => a.id === drag.id ? resize(drag.original, drag.corner, p) : a));
121
+ }
122
+ };
123
+ const onUp = () => {
124
+ const drag = dragRef.current;
125
+ dragRef.current = null;
126
+ setIsDragging(false);
127
+ if (drag?.kind === "draw") {
128
+ setPreview((p) => {
129
+ if (!p || isTrivial(p)) return null;
130
+ const anno = { ...p, id: newId() };
131
+ setAnnotations((prev) => [...prev, anno]);
132
+ setSelectedId(anno.id);
133
+ setTool("select");
134
+ return null;
135
+ });
136
+ }
137
+ };
138
+ window.addEventListener("pointermove", onMove);
139
+ window.addEventListener("pointerup", onUp);
140
+ window.addEventListener("pointercancel", onUp);
141
+ return () => {
142
+ window.removeEventListener("pointermove", onMove);
143
+ window.removeEventListener("pointerup", onUp);
144
+ window.removeEventListener("pointercancel", onUp);
145
+ };
146
+ }, [isDragging, tool, color, stroke]);
100
147
  const handleAnnoPointerDown = (e, anno) => {
101
148
  if (tool !== "select") return;
102
149
  e.stopPropagation();
103
150
  setSelectedId(anno.id);
104
- const start = evToImage(e);
105
- dragRef.current = { kind: "move", id: anno.id, start, original: anno };
106
- e.currentTarget.setPointerCapture?.(e.pointerId);
151
+ beginDrag({ kind: "move", id: anno.id, start: evToImage(e), original: anno });
152
+ };
153
+ const handleHandlePointerDown = (e, anno, corner) => {
154
+ e.stopPropagation();
155
+ beginDrag({ kind: "resize", id: anno.id, corner, start: evToImage(e), original: anno });
107
156
  };
108
157
  const handleSvgPointerDown = (e) => {
109
158
  if (tool === "select") {
@@ -117,44 +166,11 @@ function ImageAnnotator({ src, filename, onClose }) {
117
166
  }
118
167
  const start = evToImage(e);
119
168
  if (tool === "crop") {
120
- dragRef.current = { kind: "crop", start };
121
- e.currentTarget.setPointerCapture(e.pointerId);
169
+ beginDrag({ kind: "crop", start });
122
170
  return;
123
171
  }
124
- dragRef.current = { kind: "draw", start };
125
- e.currentTarget.setPointerCapture(e.pointerId);
126
172
  setPreview(makeShape(tool, start, start, color, stroke));
127
- };
128
- const handleSvgPointerMove = (e) => {
129
- const drag = dragRef.current;
130
- if (!drag) return;
131
- const p = evToImage(e);
132
- if (drag.kind === "draw") {
133
- setPreview(makeShape(tool, drag.start, p, color, stroke));
134
- } else if (drag.kind === "crop") {
135
- setPendingCrop(normalizeRect(drag.start, p));
136
- } else if (drag.kind === "move") {
137
- const dx = p.x - drag.start.x;
138
- const dy = p.y - drag.start.y;
139
- setAnnotations((prev) => prev.map((a) => a.id === drag.id ? translate(drag.original, dx, dy) : a));
140
- }
141
- };
142
- const handleSvgPointerUp = (e) => {
143
- const drag = dragRef.current;
144
- if (!drag) return;
145
- dragRef.current = null;
146
- e.currentTarget.releasePointerCapture?.(e.pointerId);
147
- if (drag.kind === "draw") {
148
- const p = preview;
149
- if (!p) return;
150
- const tooSmall = isTrivial(p);
151
- setPreview(null);
152
- if (tooSmall) return;
153
- const anno = { ...p, id: newId() };
154
- setAnnotations((prev) => [...prev, anno]);
155
- setSelectedId(anno.id);
156
- setTool("select");
157
- } else if (drag.kind === "crop") ; else if (drag.kind === "move") ;
173
+ beginDrag({ kind: "draw", start });
158
174
  };
159
175
  const handleAnnoDoubleClick = (anno) => {
160
176
  if (anno.type !== "text") return;
@@ -236,10 +252,10 @@ function ImageAnnotator({ src, filename, onClose }) {
236
252
  setAnnotations((prev) => prev.slice(0, -1));
237
253
  setSelectedId(null);
238
254
  };
239
- const downloadAnnotated = async () => {
255
+ const compositeToCanvas = async () => {
240
256
  const c = canvasRef.current;
241
257
  const svg = svgRef.current;
242
- if (!c || !svg || !imageSize) return;
258
+ if (!c || !svg || !imageSize) return null;
243
259
  const out = document.createElement("canvas");
244
260
  out.width = imageSize.w;
245
261
  out.height = imageSize.h;
@@ -251,14 +267,18 @@ function ImageAnnotator({ src, filename, onClose }) {
251
267
  const svgBlob = new Blob([xml], { type: "image/svg+xml;charset=utf-8" });
252
268
  const svgUrl = URL.createObjectURL(svgBlob);
253
269
  const svgImg = new Image();
254
- await new Promise((resolve, reject) => {
270
+ await new Promise((resolve) => {
255
271
  svgImg.onload = () => resolve();
256
- svgImg.onerror = reject;
272
+ svgImg.onerror = () => resolve();
257
273
  svgImg.src = svgUrl;
258
- }).catch(() => {
259
274
  });
260
275
  octx.drawImage(svgImg, 0, 0, imageSize.w, imageSize.h);
261
276
  URL.revokeObjectURL(svgUrl);
277
+ return out;
278
+ };
279
+ const downloadAnnotated = async () => {
280
+ const out = await compositeToCanvas();
281
+ if (!out) return;
262
282
  out.toBlob((blob) => {
263
283
  if (!blob) {
264
284
  toast_default.error("Failed to export");
@@ -273,6 +293,26 @@ function ImageAnnotator({ src, filename, onClose }) {
273
293
  URL.revokeObjectURL(url);
274
294
  }, "image/png");
275
295
  };
296
+ const copyToClipboard = async () => {
297
+ if (!("clipboard" in navigator) || typeof ClipboardItem === "undefined") {
298
+ toast_default.error("Clipboard images are not supported in this browser");
299
+ return;
300
+ }
301
+ const out = await compositeToCanvas();
302
+ if (!out) return;
303
+ out.toBlob(async (blob) => {
304
+ if (!blob) {
305
+ toast_default.error("Failed to copy");
306
+ return;
307
+ }
308
+ try {
309
+ await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
310
+ toast_default.success("Copied to clipboard");
311
+ } catch {
312
+ toast_default.error("Copy failed (clipboard permission?)");
313
+ }
314
+ }, "image/png");
315
+ };
276
316
  const tools = useMemo(() => [
277
317
  { id: "select", label: "Select / Move", icon: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.8, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 3l7 17 2-7 7-2L5 3z" }) }) },
278
318
  { id: "rect", label: "Rectangle", icon: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.8, children: /* @__PURE__ */ jsx("rect", { x: "4", y: "6", width: "16", height: "12", rx: "3" }) }) },
@@ -342,6 +382,7 @@ function ImageAnnotator({ src, filename, onClose }) {
342
382
  /* @__PURE__ */ jsx("button", { onClick: () => setZoom(1), className: "px-2 py-1 text-xs rounded hover:bg-gray-200 text-gray-700", title: "Fit to area", children: "Fit" }),
343
383
  /* @__PURE__ */ jsx("div", { className: "h-5 w-px bg-gray-300 mx-1" }),
344
384
  /* @__PURE__ */ jsx("button", { onClick: undoLast, disabled: annotations.length === 0, className: "px-2 py-1 text-xs rounded hover:bg-gray-200 disabled:opacity-30 text-gray-700", children: "Undo" }),
385
+ /* @__PURE__ */ jsx("button", { onClick: copyToClipboard, className: "px-2 py-1 text-xs rounded hover:bg-gray-200 text-gray-700", children: "Copy" }),
345
386
  /* @__PURE__ */ jsx("button", { onClick: downloadAnnotated, className: "px-2 py-1 text-xs rounded hover:bg-gray-200 text-gray-700", children: "Save" }),
346
387
  /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
347
388
  pendingCrop && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -379,20 +420,16 @@ function ImageAnnotator({ src, filename, onClose }) {
379
420
  cursor: tool === "select" ? "default" : tool === "text" ? "text" : "crosshair"
380
421
  },
381
422
  onPointerDown: handleSvgPointerDown,
382
- onPointerMove: handleSvgPointerMove,
383
- onPointerUp: handleSvgPointerUp,
384
- onPointerCancel: () => {
385
- dragRef.current = null;
386
- setPreview(null);
387
- },
388
423
  children: [
389
424
  annotations.map((a) => /* @__PURE__ */ jsx(
390
425
  AnnotationView,
391
426
  {
392
427
  anno: a,
393
428
  selected: selectedId === a.id,
429
+ zoom,
394
430
  onPointerDown: (e) => handleAnnoPointerDown(e, a),
395
- onDoubleClick: () => handleAnnoDoubleClick(a)
431
+ onDoubleClick: () => handleAnnoDoubleClick(a),
432
+ onHandlePointerDown: (e, corner) => handleHandlePointerDown(e, a, corner)
396
433
  },
397
434
  a.id
398
435
  )),
@@ -402,36 +439,14 @@ function ImageAnnotator({ src, filename, onClose }) {
402
439
  }
403
440
  ),
404
441
  pendingText && /* @__PURE__ */ jsx(
405
- "div",
442
+ PendingTextEditor,
406
443
  {
407
- style: {
408
- position: "absolute",
409
- left: `${pendingText.x * scale}px`,
410
- top: `${pendingText.y * scale}px`,
411
- transform: "translateY(-2px)",
412
- zIndex: 5
413
- },
414
- children: /* @__PURE__ */ jsx(
415
- "textarea",
416
- {
417
- autoFocus: true,
418
- value: pendingText.value,
419
- onChange: (e) => setPendingText({ ...pendingText, value: e.target.value }),
420
- onBlur: commitText,
421
- onKeyDown: (e) => {
422
- if (e.key === "Escape") {
423
- setPendingText(null);
424
- } else if (e.key === "Enter" && !e.shiftKey) {
425
- e.preventDefault();
426
- commitText();
427
- }
428
- },
429
- placeholder: "Type then Enter\u2026",
430
- rows: 1,
431
- className: "bg-white/95 border border-blue-400 rounded px-1 py-0.5 text-sm outline-none resize-none shadow-md",
432
- style: { color, fontWeight: 600, minWidth: 80 }
433
- }
434
- )
444
+ pendingText,
445
+ color,
446
+ scale,
447
+ onChange: (value) => setPendingText({ ...pendingText, value }),
448
+ onCommit: commitText,
449
+ onCancel: () => setPendingText(null)
435
450
  }
436
451
  )
437
452
  ]
@@ -440,9 +455,9 @@ function ImageAnnotator({ src, filename, onClose }) {
440
455
  /* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 border-t border-gray-200 bg-white text-[11px] text-gray-500 shrink-0", children: tool === "select" ? selectedId ? "Drag to move. Delete / Backspace removes. Click outside to deselect." : "Tap a shape to select it. Double-click text to edit." : tool === "text" ? "Click to drop a text label." : tool === "crop" ? "Drag a rectangle to set the crop region." : "Drag on the image to draw." })
441
456
  ] });
442
457
  }
443
- function AnnotationView({ anno, selected, preview, onPointerDown, onDoubleClick }) {
458
+ function AnnotationView({ anno, selected, preview, zoom = 1, onPointerDown, onDoubleClick, onHandlePointerDown }) {
444
459
  const dim = boundingBox(anno);
445
- const interactive = onPointerDown ? { onPointerDown, style: { cursor: "move" } } : {};
460
+ const interactive = onPointerDown ? { onPointerDown, pointerEvents: "all", style: { cursor: "move" } } : {};
446
461
  const dblc = onDoubleClick ? { onDoubleClick } : {};
447
462
  let body;
448
463
  if (anno.type === "rect") {
@@ -514,23 +529,112 @@ function AnnotationView({ anno, selected, preview, onPointerDown, onDoubleClick
514
529
  }
515
530
  return /* @__PURE__ */ jsxs("g", { children: [
516
531
  body,
517
- selected && !preview && /* @__PURE__ */ jsx(
518
- "rect",
519
- {
520
- "data-chrome": "selection",
521
- x: dim.x - 4,
522
- y: dim.y - 4,
523
- width: dim.w + 8,
524
- height: dim.h + 8,
525
- fill: "none",
526
- stroke: "#3b82f6",
527
- strokeWidth: 2,
528
- strokeDasharray: "6,4",
529
- pointerEvents: "none"
530
- }
531
- )
532
+ selected && !preview && /* @__PURE__ */ jsxs(Fragment, { children: [
533
+ /* @__PURE__ */ jsx(
534
+ "rect",
535
+ {
536
+ "data-chrome": "selection",
537
+ x: dim.x - 4,
538
+ y: dim.y - 4,
539
+ width: dim.w + 8,
540
+ height: dim.h + 8,
541
+ fill: "none",
542
+ stroke: "#3b82f6",
543
+ strokeWidth: 2 / zoom,
544
+ strokeDasharray: `${6 / zoom},${4 / zoom}`,
545
+ pointerEvents: "none"
546
+ }
547
+ ),
548
+ onHandlePointerDown && /* @__PURE__ */ jsx(ResizeHandles, { anno, zoom, onHandlePointerDown })
549
+ ] })
550
+ ] });
551
+ }
552
+ function ResizeHandles({
553
+ anno,
554
+ zoom,
555
+ onHandlePointerDown
556
+ }) {
557
+ const r = 6 / zoom;
558
+ const sw = 1.5 / zoom;
559
+ const handleProps = (cursor) => ({
560
+ fill: "#fff",
561
+ stroke: "#3b82f6",
562
+ strokeWidth: sw,
563
+ pointerEvents: "all",
564
+ style: { cursor }
565
+ });
566
+ if (anno.type === "arrow") {
567
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
568
+ /* @__PURE__ */ jsx("circle", { cx: anno.x1, cy: anno.y1, r, ...handleProps("grab"), "data-chrome": "handle", onPointerDown: (e) => onHandlePointerDown(e, "start") }),
569
+ /* @__PURE__ */ jsx("circle", { cx: anno.x2, cy: anno.y2, r, ...handleProps("grab"), "data-chrome": "handle", onPointerDown: (e) => onHandlePointerDown(e, "end") })
570
+ ] });
571
+ }
572
+ if (anno.type === "text") {
573
+ return null;
574
+ }
575
+ const x = anno.x;
576
+ const y = anno.y;
577
+ const w = anno.w;
578
+ const h = anno.h;
579
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
580
+ /* @__PURE__ */ jsx("circle", { cx: x, cy: y, r, ...handleProps("nwse-resize"), "data-chrome": "handle", onPointerDown: (e) => onHandlePointerDown(e, "nw") }),
581
+ /* @__PURE__ */ jsx("circle", { cx: x + w, cy: y, r, ...handleProps("nesw-resize"), "data-chrome": "handle", onPointerDown: (e) => onHandlePointerDown(e, "ne") }),
582
+ /* @__PURE__ */ jsx("circle", { cx: x + w, cy: y + h, r, ...handleProps("nwse-resize"), "data-chrome": "handle", onPointerDown: (e) => onHandlePointerDown(e, "se") }),
583
+ /* @__PURE__ */ jsx("circle", { cx: x, cy: y + h, r, ...handleProps("nesw-resize"), "data-chrome": "handle", onPointerDown: (e) => onHandlePointerDown(e, "sw") })
532
584
  ] });
533
585
  }
586
+ function PendingTextEditor({
587
+ pendingText,
588
+ color,
589
+ scale,
590
+ onChange,
591
+ onCommit,
592
+ onCancel
593
+ }) {
594
+ const ref = useRef(null);
595
+ useEffect(() => {
596
+ const id = requestAnimationFrame(() => {
597
+ ref.current?.focus();
598
+ ref.current?.select?.();
599
+ });
600
+ return () => cancelAnimationFrame(id);
601
+ }, []);
602
+ return /* @__PURE__ */ jsx(
603
+ "div",
604
+ {
605
+ style: {
606
+ position: "absolute",
607
+ left: `${pendingText.x * scale}px`,
608
+ top: `${pendingText.y * scale}px`,
609
+ transform: "translateY(-2px)",
610
+ zIndex: 5
611
+ },
612
+ onPointerDown: (e) => e.stopPropagation(),
613
+ children: /* @__PURE__ */ jsx(
614
+ "textarea",
615
+ {
616
+ ref,
617
+ value: pendingText.value,
618
+ onChange: (e) => onChange(e.target.value),
619
+ onBlur: onCommit,
620
+ onKeyDown: (e) => {
621
+ if (e.key === "Escape") {
622
+ e.preventDefault();
623
+ onCancel();
624
+ } else if (e.key === "Enter" && !e.shiftKey) {
625
+ e.preventDefault();
626
+ onCommit();
627
+ }
628
+ },
629
+ placeholder: "Type then Enter\u2026",
630
+ rows: 1,
631
+ className: "bg-white/95 border border-blue-400 rounded px-1 py-0.5 text-sm outline-none resize-none shadow-md",
632
+ style: { color, fontWeight: 600, minWidth: 80 }
633
+ }
634
+ )
635
+ }
636
+ );
637
+ }
534
638
  function ArrowShape({ anno, interactive }) {
535
639
  const headLen = Math.max(12, anno.stroke * 4);
536
640
  const angle = Math.atan2(anno.y2 - anno.y1, anno.x2 - anno.x1);
@@ -610,6 +714,37 @@ function translate(a, dx, dy) {
610
714
  }
611
715
  return { ...a, x: a.x + dx, y: a.y + dy };
612
716
  }
717
+ function resize(original, corner, p) {
718
+ if (original.type === "arrow") {
719
+ if (corner === "start") return { ...original, x1: p.x, y1: p.y };
720
+ if (corner === "end") return { ...original, x2: p.x, y2: p.y };
721
+ return original;
722
+ }
723
+ if (original.type === "text") return original;
724
+ const left = original.x;
725
+ const right = original.x + original.w;
726
+ const top = original.y;
727
+ const bottom = original.y + original.h;
728
+ let x1 = left, y1 = top, x2 = right, y2 = bottom;
729
+ if (corner === "nw") {
730
+ x1 = p.x;
731
+ y1 = p.y;
732
+ }
733
+ if (corner === "ne") {
734
+ x2 = p.x;
735
+ y1 = p.y;
736
+ }
737
+ if (corner === "se") {
738
+ x2 = p.x;
739
+ y2 = p.y;
740
+ }
741
+ if (corner === "sw") {
742
+ x1 = p.x;
743
+ y2 = p.y;
744
+ }
745
+ const r = normalizeRect({ x: x1, y: y1 }, { x: x2, y: y2 });
746
+ return { ...original, x: r.x, y: r.y, w: r.w, h: r.h };
747
+ }
613
748
  function boundingBox(a) {
614
749
  if (a.type === "arrow") {
615
750
  const x = Math.min(a.x1, a.x2);
@@ -2100,5 +2235,5 @@ function ImagePanel({ url, filename, onDownload, onEmail }) {
2100
2235
  }
2101
2236
 
2102
2237
  export { Preview, setPdfPreview };
2103
- //# sourceMappingURL=chunk-HQJRNCTU.js.map
2104
- //# sourceMappingURL=chunk-HQJRNCTU.js.map
2238
+ //# sourceMappingURL=chunk-LJ6DLGTY.js.map
2239
+ //# sourceMappingURL=chunk-LJ6DLGTY.js.map