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.
- package/dist/{Files-XUUCSCHL.js → Files-ZPMM53WI.js} +4 -4
- package/dist/{Files-XUUCSCHL.js.map → Files-ZPMM53WI.js.map} +1 -1
- package/dist/Preview-QI4WKYS2.js +6 -0
- package/dist/{Preview-4M4HF2RC.js.map → Preview-QI4WKYS2.js.map} +1 -1
- package/dist/apps/index.js +4 -4
- package/dist/{chunk-HQJRNCTU.js → chunk-LJ6DLGTY.js} +234 -99
- package/dist/chunk-LJ6DLGTY.js.map +1 -0
- package/dist/{chunk-4ZGTBQWZ.js → chunk-NC7UQF6K.js} +3 -3
- package/dist/{chunk-4ZGTBQWZ.js.map → chunk-NC7UQF6K.js.map} +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/Preview-4M4HF2RC.js +0 -6
- package/dist/chunk-HQJRNCTU.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { Files as default, openFilesInTrashMode } from './chunk-
|
|
2
|
-
import './chunk-
|
|
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-
|
|
7
|
-
//# sourceMappingURL=Files-
|
|
6
|
+
//# sourceMappingURL=Files-ZPMM53WI.js.map
|
|
7
|
+
//# sourceMappingURL=Files-ZPMM53WI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Files-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Files-ZPMM53WI.js"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Preview-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Preview-QI4WKYS2.js"}
|
package/dist/apps/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { openFilesInTrashMode } from '../chunk-
|
|
2
|
-
export { setPdfPreview } from '../chunk-
|
|
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-
|
|
23
|
+
var Preview = lazy(() => import('../Preview-QI4WKYS2.js'));
|
|
24
24
|
var Documents = lazy(() => import('../Documents-QMP6QN3C.js'));
|
|
25
|
-
var Files = lazy(() => import('../Files-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
270
|
+
await new Promise((resolve) => {
|
|
255
271
|
svgImg.onload = () => resolve();
|
|
256
|
-
svgImg.onerror =
|
|
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
|
-
|
|
442
|
+
PendingTextEditor,
|
|
406
443
|
{
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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__ */
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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-
|
|
2104
|
-
//# sourceMappingURL=chunk-
|
|
2238
|
+
//# sourceMappingURL=chunk-LJ6DLGTY.js.map
|
|
2239
|
+
//# sourceMappingURL=chunk-LJ6DLGTY.js.map
|