@sirendesign/markup 1.0.13 → 1.0.14

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.js CHANGED
@@ -851,6 +851,7 @@ const initialState = {
851
851
  currentUser: null,
852
852
  isAuthenticated: false,
853
853
  viewportMode: null,
854
+ showPins: true,
854
855
  };
855
856
  const useMarkupStore = create()(persist((set, get) => ({
856
857
  ...initialState,
@@ -913,6 +914,7 @@ const useMarkupStore = create()(persist((set, get) => ({
913
914
  activeTab: 'create',
914
915
  activeTool: null,
915
916
  }),
917
+ setShowPins: (show) => set({ showPins: show }),
916
918
  }), {
917
919
  name: 'markup-storage',
918
920
  storage: createJSONStorage(() => localStorage),
@@ -17034,6 +17036,7 @@ const deleteScreenRecording = async (downloadURL) => {
17034
17036
  }
17035
17037
  };
17036
17038
 
17039
+ const MessageIcon = ({ className, ...props }) => (jsxRuntime.jsx("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: jsxRuntime.jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }));
17037
17040
  const CloseIcon = ({ className, ...props }) => (jsxRuntime.jsxs("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }));
17038
17041
  const CameraIcon = ({ className, ...props }) => (jsxRuntime.jsxs("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsxRuntime.jsx("path", { d: "M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" }), jsxRuntime.jsx("circle", { cx: "12", cy: "13", r: "4" })] }));
17039
17042
  const CircleIcon = ({ className, ...props }) => (jsxRuntime.jsx("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }) }));
@@ -17376,7 +17379,7 @@ const AnnotationOverlay = ({ screenshot, onComplete, onCancel, }) => {
17376
17379
 
17377
17380
  const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastClickedElement, lastClickedText, }) => {
17378
17381
  var _a, _b;
17379
- const { config, currentScreenshot, setCurrentScreenshot, annotations, setAnnotations, clearAnnotations, setIsCapturing, isCapturing, currentUser, } = useMarkupStore();
17382
+ const { config, currentScreenshot, setCurrentScreenshot, annotations, setAnnotations, clearAnnotations, setIsCapturing, isCapturing, currentUser, setIsOpen, } = useMarkupStore();
17380
17383
  const [title, setTitle] = require$$0.useState((initialData === null || initialData === void 0 ? void 0 : initialData.title) || "");
17381
17384
  const [description, setDescription] = require$$0.useState((initialData === null || initialData === void 0 ? void 0 : initialData.description) || "");
17382
17385
  const [priority, setPriority] = require$$0.useState((initialData === null || initialData === void 0 ? void 0 : initialData.priority) || "medium");
@@ -17395,12 +17398,48 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17395
17398
  const [isUploadingVideo, setIsUploadingVideo] = require$$0.useState(false);
17396
17399
  const [mediaRecorder, setMediaRecorder] = require$$0.useState(null);
17397
17400
  const [recordingStream, setRecordingStream] = require$$0.useState(null);
17401
+ // Pin placement states
17402
+ const [isPlacingPin, setIsPlacingPin] = require$$0.useState(false);
17403
+ const [pinPosition, setPinPosition] = require$$0.useState((initialData === null || initialData === void 0 ? void 0 : initialData.pinLocation)
17404
+ ? {
17405
+ x: initialData.pinLocation.x,
17406
+ y: initialData.pinLocation.y,
17407
+ pageX: initialData.pinLocation.pageX,
17408
+ pageY: initialData.pinLocation.pageY,
17409
+ }
17410
+ : null);
17398
17411
  // Load initial annotations into store if editing
17399
17412
  require$$0.useEffect(() => {
17400
17413
  if (initialData === null || initialData === void 0 ? void 0 : initialData.annotations) {
17401
17414
  setAnnotations(initialData.annotations);
17402
17415
  }
17403
17416
  }, [initialData, setAnnotations]);
17417
+ // Handle pin placement mode
17418
+ require$$0.useEffect(() => {
17419
+ if (!isPlacingPin)
17420
+ return;
17421
+ // Close widget during pin placement
17422
+ setIsOpen(false);
17423
+ const handlePinClick = (e) => {
17424
+ e.preventDefault();
17425
+ e.stopPropagation();
17426
+ setPinPosition({
17427
+ x: e.clientX,
17428
+ y: e.clientY,
17429
+ pageX: e.pageX,
17430
+ pageY: e.pageY,
17431
+ });
17432
+ setIsPlacingPin(false);
17433
+ };
17434
+ document.addEventListener('click', handlePinClick, true);
17435
+ document.body.style.cursor = 'crosshair';
17436
+ return () => {
17437
+ document.removeEventListener('click', handlePinClick, true);
17438
+ document.body.style.cursor = '';
17439
+ // Reopen widget when pin placement is done
17440
+ setIsOpen(true);
17441
+ };
17442
+ }, [isPlacingPin, setIsOpen]);
17404
17443
  // Auto-capture selected text when component mounts (only if creating new)
17405
17444
  require$$0.useEffect(() => {
17406
17445
  var _a;
@@ -17438,12 +17477,12 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17438
17477
  const handleStartRecording = async () => {
17439
17478
  try {
17440
17479
  const stream = await navigator.mediaDevices.getDisplayMedia({
17441
- video: { mediaSource: 'screen' },
17480
+ video: { mediaSource: "screen" },
17442
17481
  audio: false,
17443
17482
  });
17444
17483
  setRecordingStream(stream);
17445
17484
  const recorder = new MediaRecorder(stream, {
17446
- mimeType: 'video/webm;codecs=vp8',
17485
+ mimeType: "video/webm;codecs=vp8",
17447
17486
  });
17448
17487
  const chunks = [];
17449
17488
  recorder.ondataavailable = (e) => {
@@ -17452,14 +17491,14 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17452
17491
  }
17453
17492
  };
17454
17493
  recorder.onstop = () => {
17455
- const blob = new Blob(chunks, { type: 'video/webm' });
17494
+ const blob = new Blob(chunks, { type: "video/webm" });
17456
17495
  // Store blob for upload and create preview URL
17457
17496
  setRecordedBlob(blob);
17458
17497
  const previewURL = URL.createObjectURL(blob);
17459
17498
  setRecordedVideo(previewURL);
17460
17499
  console.log(`Recording saved: ${(blob.size / 1048576).toFixed(2)}MB`);
17461
17500
  // Stop all tracks
17462
- stream.getTracks().forEach(track => track.stop());
17501
+ stream.getTracks().forEach((track) => track.stop());
17463
17502
  setRecordingStream(null);
17464
17503
  };
17465
17504
  recorder.start();
@@ -17467,25 +17506,25 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17467
17506
  setIsRecording(true);
17468
17507
  }
17469
17508
  catch (error) {
17470
- console.error('Failed to start recording:', error);
17471
- alert('Screen recording is not supported or permission was denied.');
17509
+ console.error("Failed to start recording:", error);
17510
+ alert("Screen recording is not supported or permission was denied.");
17472
17511
  }
17473
17512
  };
17474
17513
  const handleStopRecording = () => {
17475
- if (mediaRecorder && mediaRecorder.state !== 'inactive') {
17514
+ if (mediaRecorder && mediaRecorder.state !== "inactive") {
17476
17515
  mediaRecorder.stop();
17477
17516
  setIsRecording(false);
17478
17517
  }
17479
17518
  };
17480
17519
  const handleDeleteRecording = () => {
17481
17520
  // Revoke blob URL to free memory
17482
- if (recordedVideo && recordedVideo.startsWith('blob:')) {
17521
+ if (recordedVideo && recordedVideo.startsWith("blob:")) {
17483
17522
  URL.revokeObjectURL(recordedVideo);
17484
17523
  }
17485
17524
  setRecordedVideo(undefined);
17486
17525
  setRecordedBlob(null);
17487
17526
  if (recordingStream) {
17488
- recordingStream.getTracks().forEach(track => track.stop());
17527
+ recordingStream.getTracks().forEach((track) => track.stop());
17489
17528
  setRecordingStream(null);
17490
17529
  }
17491
17530
  };
@@ -17543,11 +17582,11 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17543
17582
  setIsUploadingVideo(true);
17544
17583
  const tempId = generateId();
17545
17584
  screenRecordingURL = await uploadScreenRecording(recordedBlob, tempId);
17546
- console.log('Video uploaded to Storage:', screenRecordingURL);
17585
+ console.log("Video uploaded to Storage:", screenRecordingURL);
17547
17586
  }
17548
17587
  catch (error) {
17549
- console.error('Failed to upload video:', error);
17550
- alert('Failed to upload screen recording. Please try again.');
17588
+ console.error("Failed to upload video:", error);
17589
+ alert("Failed to upload screen recording. Please try again.");
17551
17590
  setIsSubmitting(false);
17552
17591
  setIsUploadingVideo(false);
17553
17592
  return;
@@ -17556,10 +17595,13 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17556
17595
  setIsUploadingVideo(false);
17557
17596
  }
17558
17597
  }
17598
+ const pageWidth = document.documentElement.scrollWidth;
17599
+ const pageHeight = document.documentElement.scrollHeight;
17559
17600
  const feedback = {
17560
17601
  id: (initialData === null || initialData === void 0 ? void 0 : initialData.id) || generateId(),
17561
17602
  type: feedbackType,
17562
- title: title.trim() || (feedbackType === "copy-amendment" ? "Copy Amendment" : ""),
17603
+ title: title.trim() ||
17604
+ (feedbackType === "copy-amendment" ? "Copy Amendment" : ""),
17563
17605
  description: description.trim(),
17564
17606
  currentCopy: feedbackType === "copy-amendment"
17565
17607
  ? currentCopy.trim()
@@ -17569,10 +17611,12 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17569
17611
  annotations: annotations,
17570
17612
  status: (initialData === null || initialData === void 0 ? void 0 : initialData.status) || "open",
17571
17613
  priority,
17572
- pageMetadata: (initialData === null || initialData === void 0 ? void 0 : initialData.pageMetadata) || getPageMetadata(lastClickedElement || undefined),
17614
+ pageMetadata: (initialData === null || initialData === void 0 ? void 0 : initialData.pageMetadata) ||
17615
+ getPageMetadata(lastClickedElement || undefined),
17573
17616
  createdAt: (initialData === null || initialData === void 0 ? void 0 : initialData.createdAt) || new Date().toISOString(),
17574
17617
  updatedAt: new Date().toISOString(),
17575
- createdBy: (initialData === null || initialData === void 0 ? void 0 : initialData.createdBy) || currentUser || {
17618
+ createdBy: (initialData === null || initialData === void 0 ? void 0 : initialData.createdBy) ||
17619
+ currentUser || {
17576
17620
  id: ((_a = config.user) === null || _a === void 0 ? void 0 : _a.id) || "anonymous",
17577
17621
  name: ((_b = config.user) === null || _b === void 0 ? void 0 : _b.name) || "Anonymous",
17578
17622
  email: ((_c = config.user) === null || _c === void 0 ? void 0 : _c.email) || "",
@@ -17583,6 +17627,22 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17583
17627
  sessionEvents: getSessionEvents ? getSessionEvents() : undefined,
17584
17628
  designMockup,
17585
17629
  screenRecording: screenRecordingURL,
17630
+ pinLocation: pinPosition
17631
+ ? {
17632
+ x: pinPosition.x,
17633
+ y: pinPosition.y,
17634
+ pageX: pinPosition.pageX,
17635
+ pageY: pinPosition.pageY,
17636
+ viewportWidth: window.innerWidth,
17637
+ viewportHeight: window.innerHeight,
17638
+ pageWidth,
17639
+ pageHeight,
17640
+ scrollX: window.scrollX,
17641
+ scrollY: window.scrollY,
17642
+ percentX: pageWidth ? (pinPosition.pageX / pageWidth) * 100 : undefined,
17643
+ percentY: pageHeight ? (pinPosition.pageY / pageHeight) * 100 : undefined,
17644
+ }
17645
+ : undefined,
17586
17646
  };
17587
17647
  try {
17588
17648
  if (config.onSubmit) {
@@ -17607,6 +17667,10 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17607
17667
  if (isAnnotating && currentScreenshot) {
17608
17668
  return (jsxRuntime.jsx(AnnotationOverlay, { screenshot: currentScreenshot, onComplete: handleAnnotationComplete, onCancel: handleAnnotationCancel }));
17609
17669
  }
17670
+ // Show pin placement overlay
17671
+ if (isPlacingPin) {
17672
+ return (jsxRuntime.jsx("div", { className: "fixed top-4 left-1/2 -translate-x-1/2 z-[999999] pointer-events-none", children: jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl shadow-2xl p-6 max-w-md mx-4 text-center border-2 border-orange-500", children: [jsxRuntime.jsx("div", { className: "w-12 h-12 mx-auto mb-3 bg-orange-100 rounded-full flex items-center justify-center", children: jsxRuntime.jsxs("svg", { className: "w-6 h-6 text-orange-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })] }) }), jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-900 mb-2", children: "Click to Place Pin" }), jsxRuntime.jsx("p", { className: "text-gray-600 text-sm mb-3", children: "Click anywhere on the page to mark the location" }), jsxRuntime.jsx("button", { type: "button", onClick: () => setIsPlacingPin(false), className: "px-4 py-2 text-sm font-medium text-white bg-orange-600 hover:bg-orange-700 rounded-lg transition-colors pointer-events-auto", children: "Cancel" })] }) }));
17673
+ }
17610
17674
  return (jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Type" }), jsxRuntime.jsx("div", { className: "flex gap-2", children: [
17611
17675
  { id: "general", label: "General" },
17612
17676
  { id: "copy-amendment", label: "Copy Change" },
@@ -17614,10 +17678,14 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17614
17678
  ? "bg-[#C2D1D9] text-black shadow-md shadow-[#C2D1D9]/30"
17615
17679
  : "bg-gray-100 text-gray-700 hover:bg-gray-200 hover:shadow-sm"), children: type.label }, type.id))) })] }), feedbackType === "copy-amendment" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Current Copy" }), jsxRuntime.jsx("textarea", { placeholder: "Select text on page or paste current copy here...", value: currentCopy, onChange: (e) => setCurrentCopy(e.target.value), className: "w-full px-4 py-3 bg-red-50 border border-red-200 rounded-xl text-sm min-h-[80px] resize-y focus:outline-none focus:bg-white focus:border-red-500 focus:shadow-lg focus:shadow-red-500/10 transition-all" })] }), jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "New Copy *" }), jsxRuntime.jsx("textarea", { placeholder: "Enter the replacement text...", value: newCopy, onChange: (e) => setNewCopy(e.target.value), required: feedbackType === "copy-amendment", className: "w-full px-4 py-3 bg-green-50 border border-green-200 rounded-xl text-sm min-h-[80px] resize-y focus:outline-none focus:bg-white focus:border-green-500 focus:shadow-lg focus:shadow-green-500/10 transition-all" })] })] })), feedbackType !== "copy-amendment" && (jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Title *" }), jsxRuntime.jsx("input", { type: "text", placeholder: "Brief description of the issue", value: title, onChange: (e) => setTitle(e.target.value), required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl text-sm focus:outline-none focus:bg-white focus:border-[#C2D1D9] focus:shadow-lg focus:shadow-[#C2D1D9]/10 transition-all" })] })), feedbackType !== "copy-amendment" && (jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Description" }), jsxRuntime.jsx("textarea", { placeholder: "Provide more details about the issue...", value: description, onChange: (e) => setDescription(e.target.value), required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl text-sm focus:outline-none focus:bg-white focus:border-[#C2D1D9] focus:shadow-lg focus:shadow-[#C2D1D9]/10 transition-all min-h-[100px] resize-y" })] })), jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Priority" }), jsxRuntime.jsx("div", { className: "flex gap-2", children: Object.keys(priorityColors).map((p) => (jsxRuntime.jsx("div", { onClick: () => setPriority(p), className: cn("flex-1 py-2 px-2 rounded-xl text-xs font-medium cursor-pointer transition-all text-center capitalize border border-transparent", priority === p
17616
17680
  ? cn(priorityColors[p], "border-transparent shadow-sm")
17617
- : "bg-gray-100 text-gray-600 hover:bg-gray-200"), children: p }, p))) })] }), annotatedScreenshot || currentScreenshot ? (jsxRuntime.jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md mb-5 group", children: [jsxRuntime.jsx("img", { src: annotatedScreenshot || currentScreenshot || "", alt: "Screenshot", className: "w-full block" }), jsxRuntime.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" }), jsxRuntime.jsxs("div", { className: "absolute top-3 right-3 flex gap-2", children: [jsxRuntime.jsxs("button", { type: "button", onClick: handleEditScreenshot, className: "bg-white text-gray-700 border-none px-3 py-1.5 rounded-lg cursor-pointer text-xs font-medium flex items-center gap-1.5 hover:bg-gray-100 shadow-lg transition-all", children: [jsxRuntime.jsx(EditIcon, { className: "w-3.5 h-3.5" }), " Edit"] }), jsxRuntime.jsxs("button", { type: "button", onClick: handleRemoveScreenshot, className: "bg-white text-red-600 border-none px-3 py-1.5 rounded-lg cursor-pointer text-xs font-medium flex items-center gap-1.5 hover:bg-red-50 shadow-lg transition-all", children: [jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }), " Remove"] })] })] })) : (jsxRuntime.jsxs("div", { className: "flex gap-3 mb-5", children: [jsxRuntime.jsx("div", { onClick: handleCaptureScreenshot, className: "flex-1 p-4 bg-white border-2 border-dashed border-gray-300 rounded-xl cursor-pointer flex items-center justify-center gap-2.5 text-gray-600 text-sm font-medium hover:border-indigo-400 hover:text-indigo-600 hover:bg-indigo-50 hover:shadow-md transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isCapturing ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "w-5 h-5 border-2 border-indigo-200 border-t-indigo-600 rounded-full animate-spin" }), "Capturing..."] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(CameraIcon, { className: "w-5 h-5" }), " Screenshot"] })) }), jsxRuntime.jsx("div", { onClick: isRecording ? handleStopRecording : handleStartRecording, className: "flex-1 p-4 bg-white border-2 border-dashed border-gray-300 rounded-xl cursor-pointer flex items-center justify-center gap-2.5 text-gray-600 text-sm font-medium hover:border-red-400 hover:text-red-600 hover:bg-red-50 hover:shadow-md transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isRecording ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "w-2.5 h-2.5 bg-red-600 rounded-sm animate-pulse" }), "Stop Recording"] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", strokeWidth: "2" }), jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4", fill: "currentColor" })] }), "Record Screen"] })) })] })), recordedVideo && (jsxRuntime.jsxs("div", { className: "mb-5 relative rounded-xl overflow-hidden shadow-md border border-gray-200", children: [jsxRuntime.jsx("video", { src: recordedVideo, controls: true, className: "w-full h-auto" }), jsxRuntime.jsx("button", { type: "button", onClick: handleDeleteRecording, className: "absolute top-2 right-2 bg-white text-red-500 p-1.5 rounded-lg shadow hover:bg-gray-100 cursor-pointer border-none", children: jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }) })] })), jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Design Mockup" }), designMockup ? (jsxRuntime.jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md group border border-gray-200", children: [jsxRuntime.jsx("img", { src: designMockup, className: "w-full h-32 object-cover", alt: "Mockup" }), jsxRuntime.jsx("button", { type: "button", onClick: () => setDesignMockup(undefined), className: "absolute top-2 right-2 bg-white text-red-500 p-1.5 rounded-lg shadow hover:bg-gray-100 cursor-pointer border-none", children: jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }) })] })) : (jsxRuntime.jsxs("div", { className: "border-2 border-dashed border-gray-200 rounded-xl p-4 bg-gray-50 flex items-center justify-center relative hover:bg-white hover:border-[#C2D1D9] transition-all", children: [jsxRuntime.jsx("input", { type: "file", accept: "image/*", onChange: handleMockupUpload, className: "absolute inset-0 opacity-0 cursor-pointer w-full h-full" }), jsxRuntime.jsxs("span", { className: "text-sm text-gray-500 flex items-center gap-2 font-medium", children: [jsxRuntime.jsx(PlusIcon, { className: "w-4 h-4" }), " Upload Mockup"] })] }))] }), config.availableTags && config.availableTags.length > 0 && (jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Tags" }), jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: config.availableTags.map((tag) => (jsxRuntime.jsx("div", { onClick: () => toggleTag(tag), className: cn("px-3 py-1.5 rounded-full text-xs font-medium cursor-pointer transition-all", selectedTags.includes(tag)
17681
+ : "bg-gray-100 text-gray-600 hover:bg-gray-200"), children: p }, p))) })] }), annotatedScreenshot || currentScreenshot ? (jsxRuntime.jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md mb-5 group", children: [jsxRuntime.jsx("img", { src: annotatedScreenshot || currentScreenshot || "", alt: "Screenshot", className: "w-full block" }), jsxRuntime.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" }), jsxRuntime.jsxs("div", { className: "absolute top-3 right-3 flex gap-2", children: [jsxRuntime.jsxs("button", { type: "button", onClick: handleEditScreenshot, className: "bg-white text-gray-700 border-none px-3 py-1.5 rounded-lg cursor-pointer text-xs font-medium flex items-center gap-1.5 hover:bg-gray-100 shadow-lg transition-all", children: [jsxRuntime.jsx(EditIcon, { className: "w-3.5 h-3.5" }), " Edit"] }), jsxRuntime.jsxs("button", { type: "button", onClick: handleRemoveScreenshot, className: "bg-white text-red-600 border-none px-3 py-1.5 rounded-lg cursor-pointer text-xs font-medium flex items-center gap-1.5 hover:bg-red-50 shadow-lg transition-all", children: [jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }), " Remove"] })] })] })) : (jsxRuntime.jsxs("div", { className: "flex gap-3 mb-5", children: [jsxRuntime.jsx("div", { onClick: handleCaptureScreenshot, className: "flex-1 p-4 bg-white border-2 border-dashed border-gray-300 rounded-xl cursor-pointer flex items-center justify-center gap-2.5 text-gray-600 text-sm font-medium hover:border-indigo-400 hover:text-indigo-600 hover:bg-indigo-50 hover:shadow-md transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isCapturing ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "w-5 h-5 border-2 border-indigo-200 border-t-indigo-600 rounded-full animate-spin" }), "Capturing..."] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(CameraIcon, { className: "w-5 h-5" }), " Screenshot"] })) }), jsxRuntime.jsx("div", { onClick: isRecording
17682
+ ? handleStopRecording
17683
+ : handleStartRecording, className: "flex-1 p-4 bg-white border-2 border-dashed border-gray-300 rounded-xl cursor-pointer flex items-center justify-center gap-2.5 text-gray-600 text-sm font-medium hover:border-red-400 hover:text-red-600 hover:bg-red-50 hover:shadow-md transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isRecording ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "w-2.5 h-2.5 bg-red-600 rounded-sm animate-pulse" }), "Stop Recording"] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", strokeWidth: "2" }), jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4", fill: "currentColor" })] }), "Record Screen"] })) })] })), jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsxs("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: ["Pin Location", " ", !pinPosition && jsxRuntime.jsx("span", { className: "text-red-500", children: "*" })] }), pinPosition ? (jsxRuntime.jsxs("div", { className: "flex items-center gap-3 p-3 bg-green-50 border border-green-200 rounded-xl", children: [jsxRuntime.jsx("div", { className: "w-8 h-8 rounded-full bg-green-500 flex items-center justify-center shadow-md", children: jsxRuntime.jsx("svg", { className: "w-4 h-4 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M5 13l4 4L19 7" }) }) }), jsxRuntime.jsxs("div", { className: "flex-1", children: [jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-800", children: "Pin placed on page" }), jsxRuntime.jsxs("div", { className: "text-xs text-gray-600", children: ["Position: (", Math.round(pinPosition.pageX), ",", " ", Math.round(pinPosition.pageY), ")"] })] }), jsxRuntime.jsx("div", { onClick: () => setIsPlacingPin(true), className: "px-3 py-1.5 text-xs font-medium text-indigo-600 hover:text-indigo-700 hover:bg-indigo-50 rounded-lg transition-all", children: "Re-place" })] })) : (jsxRuntime.jsxs("div", { onClick: () => setIsPlacingPin(true), className: "p-4 bg-white border-2 border-dashed border-orange-300 rounded-xl cursor-pointer flex items-center justify-center gap-2.5 text-orange-600 text-sm font-medium hover:border-orange-400 hover:bg-orange-50 hover:shadow-md transition-all", children: [jsxRuntime.jsxs("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })] }), "Click to place pin on page"] }))] }), recordedVideo && (jsxRuntime.jsxs("div", { className: "mb-5 relative rounded-xl overflow-hidden shadow-md border border-gray-200", children: [jsxRuntime.jsx("video", { src: recordedVideo, controls: true, className: "w-full h-auto" }), jsxRuntime.jsx("button", { type: "button", onClick: handleDeleteRecording, className: "absolute top-2 right-2 bg-white text-red-500 p-1.5 rounded-lg shadow hover:bg-gray-100 cursor-pointer border-none", children: jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }) })] })), jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Design Mockup" }), designMockup ? (jsxRuntime.jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md group border border-gray-200", children: [jsxRuntime.jsx("img", { src: designMockup, className: "w-full h-32 object-cover", alt: "Mockup" }), jsxRuntime.jsx("button", { type: "button", onClick: () => setDesignMockup(undefined), className: "absolute top-2 right-2 bg-white text-red-500 p-1.5 rounded-lg shadow hover:bg-gray-100 cursor-pointer border-none", children: jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }) })] })) : (jsxRuntime.jsxs("div", { className: "border-2 border-dashed border-gray-200 rounded-xl p-4 bg-gray-50 flex items-center justify-center relative hover:bg-white hover:border-[#C2D1D9] transition-all", children: [jsxRuntime.jsx("input", { type: "file", accept: "image/*", onChange: handleMockupUpload, className: "absolute inset-0 opacity-0 cursor-pointer w-full h-full" }), jsxRuntime.jsxs("span", { className: "text-sm text-gray-500 flex items-center gap-2 font-medium", children: [jsxRuntime.jsx(PlusIcon, { className: "w-4 h-4" }), " Upload Mockup"] })] }))] }), config.availableTags && config.availableTags.length > 0 && (jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Tags" }), jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: config.availableTags.map((tag) => (jsxRuntime.jsx("div", { onClick: () => toggleTag(tag), className: cn("px-3 py-1.5 rounded-full text-xs font-medium cursor-pointer transition-all", selectedTags.includes(tag)
17618
17684
  ? "bg-[#C2D1D9] text-white shadow-md shadow-[#C2D1D9]/30"
17619
17685
  : "bg-gray-100 text-gray-700 hover:bg-gray-200 hover:shadow-sm"), children: tag }, tag))) })] })), jsxRuntime.jsxs("div", { className: "flex gap-3 mt-6 pt-6 border-t border-gray-100", children: [jsxRuntime.jsx("div", { onClick: onCancel, className: "flex-1 py-3 px-5 rounded-xl text-sm font-semibold cursor-pointer transition-all bg-gray-100 text-gray-700 hover:bg-gray-200 hover:shadow-md text-center", children: ((_a = config.labels) === null || _a === void 0 ? void 0 : _a.cancelButton) || "Cancel" }), jsxRuntime.jsx("div", { onClick: (e) => {
17620
- const isValid = feedbackType === "copy-amendment" ? newCopy.trim() : title.trim();
17686
+ const isValid = feedbackType === "copy-amendment"
17687
+ ? newCopy.trim()
17688
+ : title.trim();
17621
17689
  if (!isValid || isSubmitting)
17622
17690
  return;
17623
17691
  const form = e.currentTarget.closest("form");
@@ -17628,12 +17696,18 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
17628
17696
  });
17629
17697
  form.dispatchEvent(submitEvent);
17630
17698
  }
17631
- }, className: cn("flex-1 py-3 px-5 rounded-xl text-sm font-semibold cursor-pointer transition-all text-white border-none hover:shadow-lg text-center", ((feedbackType === "copy-amendment" ? !newCopy.trim() : !title.trim()) || isSubmitting) &&
17699
+ }, className: cn("flex-1 py-3 px-5 rounded-xl text-sm font-semibold cursor-pointer transition-all text-white border-none hover:shadow-lg text-center", ((feedbackType === "copy-amendment"
17700
+ ? !newCopy.trim()
17701
+ : !title.trim()) ||
17702
+ isSubmitting) &&
17632
17703
  "opacity-50 cursor-not-allowed"), style: {
17633
- backgroundColor: (feedbackType === "copy-amendment" ? !newCopy.trim() : !title.trim()) || isSubmitting
17704
+ backgroundColor: (feedbackType === "copy-amendment"
17705
+ ? !newCopy.trim()
17706
+ : !title.trim()) || isSubmitting
17634
17707
  ? "#9ca3af"
17635
17708
  : "green-500",
17636
- }, children: isSubmitting ? (isUploadingVideo ? (jsxRuntime.jsxs("span", { className: "flex items-center justify-center gap-2", children: [jsxRuntime.jsx("span", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }), jsxRuntime.jsx("span", { children: "Uploading video..." })] })) : (jsxRuntime.jsx("span", { className: "w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin mx-auto block" }))) : (((_b = config.labels) === null || _b === void 0 ? void 0 : _b.submitButton) || (initialData ? "Update Feedback" : "Submit Feedback")) })] })] }));
17709
+ }, children: isSubmitting ? (isUploadingVideo ? (jsxRuntime.jsxs("span", { className: "flex items-center justify-center gap-2", children: [jsxRuntime.jsx("span", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }), jsxRuntime.jsx("span", { children: "Uploading video..." })] })) : (jsxRuntime.jsx("span", { className: "w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin mx-auto block" }))) : (((_b = config.labels) === null || _b === void 0 ? void 0 : _b.submitButton) ||
17710
+ (initialData ? "Update Feedback" : "Submit Feedback")) })] })] }));
17637
17711
  };
17638
17712
 
17639
17713
  const statusOptions$1 = [
@@ -39766,6 +39840,7 @@ const subscribeFeedback = (projectId, callback) => {
39766
39840
  componentName: data.componentName,
39767
39841
  designMockup: data.designMockup,
39768
39842
  screenRecording: data.screenRecording,
39843
+ pinLocation: data.pinLocation,
39769
39844
  };
39770
39845
  });
39771
39846
  callback(feedbackItems);
@@ -39874,6 +39949,7 @@ const FeedbackDetail = ({ feedback: initialFeedback, onBack, onDelete, onUpdate,
39874
39949
  const [showFullScreenshot, setShowFullScreenshot] = require$$0.useState(false);
39875
39950
  const [copied, setCopied] = require$$0.useState(false);
39876
39951
  const [isEditing, setIsEditing] = require$$0.useState(false);
39952
+ const [showDeleteConfirm, setShowDeleteConfirm] = require$$0.useState(false);
39877
39953
  // Feature 2 & 5 states
39878
39954
  const [showReplay, setShowReplay] = require$$0.useState(false);
39879
39955
  const [showOverlay, setShowOverlay] = require$$0.useState(false);
@@ -40027,9 +40103,8 @@ ${feedback.comments && feedback.comments.length > 0
40027
40103
  setNewComment("");
40028
40104
  setIsAddingComment(false);
40029
40105
  };
40030
- const handleDelete = async () => {
40031
- if (!confirm("Are you sure you want to delete this feedback?"))
40032
- return;
40106
+ const handleDelete = () => setShowDeleteConfirm(true);
40107
+ const handleConfirmDelete = async () => {
40033
40108
  // Delete from Firebase if initialized
40034
40109
  try {
40035
40110
  await deleteFeedback(feedback.id);
@@ -40043,8 +40118,10 @@ ${feedback.comments && feedback.comments.length > 0
40043
40118
  if (onDelete) {
40044
40119
  onDelete(feedback.id);
40045
40120
  }
40121
+ setShowDeleteConfirm(false);
40046
40122
  };
40047
- return (jsxRuntime.jsxs("div", { className: "flex flex-col h-full px-5 overflow-y-auto", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between pb-3 mb-3 border-b border-gray-200", children: [jsxRuntime.jsxs("div", { onClick: onBack, className: "flex items-center gap-1.5 text-sm text-gray-600 bg-transparent border-none cursor-pointer hover:text-gray-900 transition-colors", children: [jsxRuntime.jsx(ArrowLeftIcon, { className: "w-4 h-4" }), "Back"] }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx("div", { onClick: handleCopy, className: "flex items-center gap-1.5 text-sm text-gray-600 bg-transparent border-none cursor-pointer hover:text-gray-900 transition-colors", children: copied ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(CheckIcon, { className: "w-4 h-4" }), "Copied!"] })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(CopyIcon, { className: "w-4 h-4" }) })) }), canEdit && (jsxRuntime.jsx("div", { onClick: () => setIsEditing(true), className: "flex items-center gap-1.5 text-sm text-gray-600 bg-transparent border-none cursor-pointer hover:text-gray-900 transition-colors", children: jsxRuntime.jsx(EditIcon, { className: "w-4 h-4" }) })), canDelete && onDelete && (jsxRuntime.jsx("div", { onClick: handleDelete, className: "flex items-center gap-1.5 text-sm text-red-600 bg-transparent border-none cursor-pointer hover:text-red-700 transition-colors", children: jsxRuntime.jsx(TrashIcon, { className: "w-4 h-4" }) }))] })] }), jsxRuntime.jsxs("div", { className: "flex-1 pr-1", children: [jsxRuntime.jsxs("div", { className: "mb-4", children: [jsxRuntime.jsxs("div", { className: "flex items-start gap-2 mb-2", children: [jsxRuntime.jsx("h3", { className: "text-base font-semibold text-gray-900 m-0 flex-1", children: feedback.title }), ((_b = feedback.pageMetadata) === null || _b === void 0 ? void 0 : _b.deviceType) && (jsxRuntime.jsxs("div", { className: "flex-shrink-0 mt-0.5", children: [feedback.pageMetadata.deviceType ===
40123
+ const handleCancelDelete = () => setShowDeleteConfirm(false);
40124
+ return (jsxRuntime.jsxs("div", { className: "flex flex-col h-full px-5 overflow-y-auto", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between pb-3 mb-3 border-b border-gray-200", children: [jsxRuntime.jsxs("div", { onClick: onBack, className: "flex items-center gap-1.5 text-sm text-gray-600 bg-transparent border-none cursor-pointer hover:text-gray-900 transition-colors", children: [jsxRuntime.jsx(ArrowLeftIcon, { className: "w-4 h-4" }), "Back"] }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx("div", { onClick: handleCopy, className: "flex items-center gap-1.5 text-sm text-gray-600 bg-transparent border-none cursor-pointer hover:text-gray-900 transition-colors", children: copied ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(CheckIcon, { className: "w-4 h-4" }), "Copied!"] })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(CopyIcon, { className: "w-4 h-4" }) })) }), canEdit && (jsxRuntime.jsx("div", { onClick: () => setIsEditing(true), className: "flex items-center gap-1.5 text-sm text-gray-600 bg-transparent border-none cursor-pointer hover:text-gray-900 transition-colors", children: jsxRuntime.jsx(EditIcon, { className: "w-4 h-4" }) })), canDelete && onDelete && (jsxRuntime.jsx("div", { onClick: handleDelete, className: "flex items-center gap-1.5 text-sm text-red-600 bg-transparent border-none cursor-pointer hover:text-red-700 transition-colors", children: jsxRuntime.jsx(TrashIcon, { className: "w-4 h-4" }) }))] })] }), showDeleteConfirm && (jsxRuntime.jsxs("div", { className: "mb-3 p-2 rounded-xl border border-red-200 bg-red-50 text-red-800 flex items-center gap-3", children: [jsxRuntime.jsx("div", { className: "flex-1 text-xs font-medium", children: "Delete this feedback?" }), jsxRuntime.jsx("div", { onClick: handleCancelDelete, className: "px-2 py-1.5 text-xs font-semibold text-red-700 rounded-lg hover:bg-red-100 cursor-pointer", children: "Cancel" }), jsxRuntime.jsx("div", { onClick: handleConfirmDelete, className: "px-2 py-1.5 text-xs font-semibold text-white bg-red-600 rounded-lg hover:bg-red-700 cursor-pointer", children: "Delete" })] })), jsxRuntime.jsxs("div", { className: "flex-1 pr-1", children: [jsxRuntime.jsxs("div", { className: "mb-4", children: [jsxRuntime.jsxs("div", { className: "flex items-start gap-2 mb-2", children: [jsxRuntime.jsx("h3", { className: "text-base font-semibold text-gray-900 m-0 flex-1", children: feedback.title }), ((_b = feedback.pageMetadata) === null || _b === void 0 ? void 0 : _b.deviceType) && (jsxRuntime.jsxs("div", { className: "flex-shrink-0 mt-0.5", children: [feedback.pageMetadata.deviceType ===
40048
40125
  "desktop" && (jsxRuntime.jsx(DesktopIcon, { className: "w-5 h-5 text-gray-500", title: "Desktop" })), feedback.pageMetadata.deviceType ===
40049
40126
  "tablet" && (jsxRuntime.jsx(TabletIcon, { className: "w-5 h-5 text-gray-500", title: "Tablet" })), feedback.pageMetadata.deviceType ===
40050
40127
  "mobile" && (jsxRuntime.jsx(MobileIcon, { className: "w-5 h-5 text-gray-500", title: "Mobile" }))] }))] }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [feedback.type && (jsxRuntime.jsx("span", { className: cn("px-2 py-0.5 rounded-full text-[10px] font-medium uppercase", feedback.type === "copy-amendment"
@@ -40225,6 +40302,89 @@ const ViewportControls = () => {
40225
40302
  : "hover:bg-gray-100"), children: [jsxRuntime.jsx(DesktopIcon, { className: cn("w-4 h-4 transition-colors", isActivePreset(preset) ? "text-blue-600" : "text-gray-400 group-hover:text-gray-600") }), jsxRuntime.jsxs("div", { className: "flex-1", children: [jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-700 group-hover:text-gray-900", children: preset.name }), jsxRuntime.jsxs("div", { className: "text-xs text-gray-500", children: [preset.width, " \u00D7 ", preset.height] })] })] }, preset.name)))] }) }))] }));
40226
40303
  };
40227
40304
 
40305
+ const FeedbackPins = ({ onPinClick }) => {
40306
+ const { feedbackItems, showPins } = useMarkupStore();
40307
+ const [docSize, setDocSize] = require$$0.useState({ width: 0, height: 0 });
40308
+ require$$0.useEffect(() => {
40309
+ const measure = () => {
40310
+ const root = document.documentElement;
40311
+ const body = document.body;
40312
+ const width = root.clientWidth || body.clientWidth || window.innerWidth;
40313
+ const height = Math.max(root.scrollHeight, body.scrollHeight, root.clientHeight, body.clientHeight);
40314
+ setDocSize({ width, height });
40315
+ };
40316
+ measure();
40317
+ const resizeObserver = new ResizeObserver(() => measure());
40318
+ resizeObserver.observe(document.documentElement);
40319
+ resizeObserver.observe(document.body);
40320
+ const handleResize = () => measure();
40321
+ const handleScroll = () => measure();
40322
+ window.addEventListener('resize', handleResize);
40323
+ window.addEventListener('scroll', handleScroll, { passive: true });
40324
+ return () => {
40325
+ resizeObserver.disconnect();
40326
+ window.removeEventListener('resize', handleResize);
40327
+ window.removeEventListener('scroll', handleScroll);
40328
+ };
40329
+ }, []);
40330
+ // Filter pins for current page URL
40331
+ const currentPagePins = require$$0.useMemo(() => {
40332
+ const currentUrl = window.location.href;
40333
+ return feedbackItems.filter(item => {
40334
+ var _a;
40335
+ // Only show pins with pinLocation data
40336
+ if (!item.pinLocation)
40337
+ return false;
40338
+ // Only show pins from the same page
40339
+ if (((_a = item.pageMetadata) === null || _a === void 0 ? void 0 : _a.url) !== currentUrl)
40340
+ return false;
40341
+ return true;
40342
+ });
40343
+ }, [feedbackItems]);
40344
+ // Don't show pins if toggle is off
40345
+ if (!showPins)
40346
+ return null;
40347
+ if (currentPagePins.length === 0)
40348
+ return null;
40349
+ const containerStyle = {
40350
+ position: 'absolute',
40351
+ top: 0,
40352
+ left: 0,
40353
+ width: docSize.width || '100%',
40354
+ height: docSize.height || '100%',
40355
+ pointerEvents: 'none',
40356
+ zIndex: 999998,
40357
+ };
40358
+ return (jsxRuntime.jsx("div", { className: "feedback-pins-container", "data-markup-pins": true, style: containerStyle, children: currentPagePins.map((feedback) => {
40359
+ const { pinLocation } = feedback;
40360
+ if (!pinLocation)
40361
+ return null;
40362
+ const pageWidth = docSize.width || pinLocation.pageWidth || pinLocation.viewportWidth || window.innerWidth;
40363
+ const pageHeight = docSize.height || pinLocation.pageHeight || pinLocation.viewportHeight || window.innerHeight;
40364
+ // Prefer stored percentages; fall back to computing from stored absolute coords
40365
+ const leftPercent = typeof pinLocation.percentX === 'number'
40366
+ ? pinLocation.percentX
40367
+ : pageWidth ? (pinLocation.pageX / pageWidth) * 100 : 0;
40368
+ const topPercent = typeof pinLocation.percentY === 'number'
40369
+ ? pinLocation.percentY
40370
+ : pageHeight ? (pinLocation.pageY / pageHeight) * 100 : 0;
40371
+ const style = {
40372
+ position: 'absolute',
40373
+ left: `${leftPercent}%`,
40374
+ top: `${topPercent}%`,
40375
+ transform: 'translate(-50%, -50%)',
40376
+ pointerEvents: 'auto',
40377
+ };
40378
+ return (jsxRuntime.jsx("div", { className: "feedback-pin-wrapper", style: style, onClick: () => onPinClick(feedback), children: jsxRuntime.jsxs("div", { className: "feedback-pin group relative cursor-pointer", children: [jsxRuntime.jsx("div", { className: `
40379
+ w-8 h-8 rounded-full flex items-center justify-center
40380
+ shadow-lg hover:shadow-xl transition-all
40381
+ ${feedback.status === 'open' ? 'bg-[#E6B6CF] hover:bg-[#d9a3c0]' : 'bg-green-500 hover:bg-green-600'}
40382
+ border-2 border-white
40383
+ hover:scale-110
40384
+ `, children: jsxRuntime.jsx(MessageIcon, { className: "w-4 h-4 text-white" }) }), jsxRuntime.jsxs("div", { className: "\n absolute bottom-full left-1/2 -translate-x-1/2 mb-2\n opacity-0 group-hover:opacity-100\n pointer-events-none\n transition-opacity duration-200\n bg-gray-900 text-white text-xs rounded-lg py-2 px-3\n whitespace-nowrap shadow-xl\n max-w-[200px]\n ", children: [jsxRuntime.jsx("div", { className: "font-semibold truncate", children: feedback.title || 'Feedback' }), jsxRuntime.jsx("div", { className: "text-gray-300 text-[10px] mt-0.5", children: "Click to view" }), jsxRuntime.jsx("div", { className: "\n absolute top-full left-1/2 -translate-x-1/2\n w-0 h-0\n border-l-4 border-l-transparent\n border-r-4 border-r-transparent\n border-t-4 border-t-gray-900\n " })] })] }) }, feedback.id));
40385
+ }) }));
40386
+ };
40387
+
40228
40388
  const useFirebaseSync = () => {
40229
40389
  const config = useMarkupStore((state) => state.config);
40230
40390
  const setCurrentUser = useMarkupStore((state) => state.setCurrentUser);
@@ -40436,7 +40596,7 @@ const MarkupWidget = ({ config: userConfig, }) => {
40436
40596
  console.error('Failed to initialize Firebase:', error);
40437
40597
  }
40438
40598
  }
40439
- const { isOpen, setIsOpen, activeTab, setActiveTab, selectedFeedback, setSelectedFeedback, feedbackItems, addFeedbackItem, removeFeedbackItem, updateFeedback, config, setConfig, reset, isAuthenticated, currentUser, viewportMode, setViewportMode, } = useMarkupStore();
40599
+ const { isOpen, setIsOpen, activeTab, setActiveTab, selectedFeedback, setSelectedFeedback, feedbackItems, addFeedbackItem, removeFeedbackItem, updateFeedback, config, setConfig, reset, isAuthenticated, currentUser, viewportMode, setViewportMode, showPins, setShowPins, } = useMarkupStore();
40440
40600
  // Initialize Firebase sync
40441
40601
  useFirebaseSync();
40442
40602
  // Initialize Session Recording (Feature 2)
@@ -40575,6 +40735,11 @@ const MarkupWidget = ({ config: userConfig, }) => {
40575
40735
  const handleSelectFeedback = require$$0.useCallback((feedback) => {
40576
40736
  setSelectedFeedback(feedback);
40577
40737
  }, [setSelectedFeedback]);
40738
+ const handlePinClick = require$$0.useCallback((feedback) => {
40739
+ setIsOpen(true);
40740
+ setActiveTab("list");
40741
+ setSelectedFeedback(feedback);
40742
+ }, [setIsOpen, setActiveTab, setSelectedFeedback]);
40578
40743
  const handleBackFromDetail = require$$0.useCallback(() => {
40579
40744
  setSelectedFeedback(null);
40580
40745
  setViewportMode(null);
@@ -40618,11 +40783,14 @@ const MarkupWidget = ({ config: userConfig, }) => {
40618
40783
  ? adjustColor(config.primaryColor, -20)
40619
40784
  : "#4f46e5",
40620
40785
  });
40621
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ViewportControls, {}), jsxRuntime.jsxs("div", { className: cn("markup-widget fixed z-[999999] right-0 top-1/2 flex items-center transition-transform duration-300 ease-in-out", isOpen ? "" : "hover:translate-x-[-8px]"), style: {
40786
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(FeedbackPins, { onPinClick: handlePinClick }), jsxRuntime.jsx(ViewportControls, {}), jsxRuntime.jsxs("div", { className: cn("markup-widget fixed z-[999999] right-0 top-1/2 flex items-center transition-transform duration-300 ease-in-out", isOpen ? "" : "hover:translate-x-[-8px]"), style: {
40622
40787
  transform: isOpen
40623
40788
  ? "translateY(-50%) translateX(0)"
40624
40789
  : "translateY(-50%) translateX(calc(100% - 40px))",
40625
- }, "data-markup-widget": true, children: [jsxRuntime.jsxs("div", { onClick: handleToggle, className: "relative bg-white h-fit flex items-center gap-2 px-2 pr-5 text-black border-none rounded-l-2xl cursor-pointer text-sm font-semibold transition-all py-5 shadow-lg", children: [isOpen && jsxRuntime.jsx(CloseIcon, { className: "w-5 h-5" }), jsxRuntime.jsx("span", { className: "text-black", style: { writingMode: "sideways-lr" }, children: "Siren" }), openFeedbackCount > 0 && (jsxRuntime.jsx("span", { className: "absolute -top-3 -left-1 min-w-[24px] h-[24px] px-1 bg-[#E6B6CF] text-black rounded-full text-xs font-semibold flex items-center justify-center", children: openFeedbackCount }))] }), jsxRuntime.jsxs("div", { className: "bg-white w-[500px] h-[750px] rounded-l-xl shadow-2xl overflow-hidden flex flex-col text-black", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-4 text-black", style: { backgroundColor: "var(--markup-primary)" }, children: [jsxRuntime.jsx("h2", { className: "text-base font-semibold m-0", children: ((_a = config.labels) === null || _a === void 0 ? void 0 : _a.feedbackTitle) || "Feedback" }), isAuthenticated && currentUser && (jsxRuntime.jsx("div", { onClick: handleLogout, className: "text-xs py-2 px-4 bg-gray-50 border border-gray-200 text-black rounded-xl hover:bg-gray-200 cursor-pointer transition-all flex items-center gap-2", children: jsxRuntime.jsx("h2", { children: "Sign Out" }) }))] }), !isAuthenticated ? (jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: jsxRuntime.jsx(AuthForm, { onSuccess: handleAuthSuccess }) })) : selectedFeedback ? (jsxRuntime.jsx(FeedbackDetail, { feedback: selectedFeedback, onBack: handleBackFromDetail, onDelete: handleDeleteFeedback, onUpdate: handleUpdateFeedback })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "flex border-b border-gray-200 px-5", children: [jsxRuntime.jsxs("div", { className: cn("flex-1 py-3 px-4 border border-b-0 border-gray-200 rounded-t-xl hover:bg-gray-100 cursor-pointer text-sm font-medium transition-all flex items-center justify-center gap-1", activeTab === "create"
40790
+ }, "data-markup-widget": true, children: [jsxRuntime.jsxs("div", { onClick: handleToggle, className: "relative bg-white h-fit flex items-center gap-2 px-2 pr-5 text-black border-none rounded-l-2xl cursor-pointer text-sm font-semibold transition-all py-5 shadow-lg", children: [isOpen && jsxRuntime.jsx(CloseIcon, { className: "w-5 h-5" }), jsxRuntime.jsx("span", { className: "text-black", style: { writingMode: "sideways-lr" }, children: "Siren" }), openFeedbackCount > 0 && (jsxRuntime.jsx("span", { className: "absolute -top-3 -left-1 min-w-[24px] h-[24px] px-1 bg-[#E6B6CF] text-black rounded-full text-xs font-semibold flex items-center justify-center", children: openFeedbackCount }))] }), jsxRuntime.jsxs("div", { className: "bg-white w-[500px] h-[750px] rounded-l-xl shadow-2xl overflow-hidden flex flex-col text-black", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-4 text-black", style: { backgroundColor: "var(--markup-primary)" }, children: [jsxRuntime.jsx("h2", { className: "text-base font-semibold m-0", children: ((_a = config.labels) === null || _a === void 0 ? void 0 : _a.feedbackTitle) || "Feedback" }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsxs("div", { onClick: (e) => {
40791
+ e.stopPropagation();
40792
+ setShowPins(!showPins);
40793
+ }, className: "p-2 text-xs font-medium bg-gray-50 border border-gray-200 text-black rounded-lg hover:bg-gray-100 cursor-pointer transition-all flex items-center gap-1.5", title: showPins ? "Hide pins" : "Show pins", children: [jsxRuntime.jsxs("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })] }), showPins ? 'Hide' : 'Show'] }), isAuthenticated && currentUser && (jsxRuntime.jsx("div", { onClick: handleLogout, className: "text-xs py-2 px-2 bg-gray-50 border border-gray-200 text-gray-900 rounded-lg hover:bg-gray-200 cursor-pointer transition-all flex items-center gap-2", children: jsxRuntime.jsx("h2", { children: "Sign Out" }) }))] })] }), !isAuthenticated ? (jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: jsxRuntime.jsx(AuthForm, { onSuccess: handleAuthSuccess }) })) : selectedFeedback ? (jsxRuntime.jsx(FeedbackDetail, { feedback: selectedFeedback, onBack: handleBackFromDetail, onDelete: handleDeleteFeedback, onUpdate: handleUpdateFeedback })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "flex border-b border-gray-200 px-5", children: [jsxRuntime.jsxs("div", { className: cn("flex-1 py-3 px-4 border border-b-0 border-gray-200 rounded-t-xl hover:bg-gray-100 cursor-pointer text-sm font-medium transition-all flex items-center justify-center gap-1", activeTab === "create"
40626
40794
  ? "bg-gray-200"
40627
40795
  : "border-white"), onClick: () => setActiveTab("create"), children: [jsxRuntime.jsx(PlusIcon, { className: "w-3.5 h-3.5" }), "New"] }), jsxRuntime.jsxs("div", { className: cn("flex-1 py-3 px-4 border border-b-0 border-gray-200 rounded-t-xl hover:bg-gray-100 cursor-pointer text-sm font-medium transition-all flex items-center justify-center gap-1", activeTab === "list"
40628
40796
  ? "bg-gray-200"