pxengine 0.1.74 → 0.1.76

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.d.cts CHANGED
@@ -3078,6 +3078,12 @@ interface PresentationJobCardProps {
3078
3078
  * e.g. /api/agents-proxy/jobs/{job_id}/status
3079
3079
  */
3080
3080
  pollUrl?: string;
3081
+ /**
3082
+ * Shareable link copied when the user clicks Share.
3083
+ * Falls back to formats.html_url if not provided.
3084
+ * Pass a /p/{id} permanent URL once that route exists.
3085
+ */
3086
+ shareUrl?: string;
3081
3087
  className?: string;
3082
3088
  onAction?: (action: string, payload?: any) => void;
3083
3089
  sendMessage?: (message: string) => void;
package/dist/index.d.ts CHANGED
@@ -3078,6 +3078,12 @@ interface PresentationJobCardProps {
3078
3078
  * e.g. /api/agents-proxy/jobs/{job_id}/status
3079
3079
  */
3080
3080
  pollUrl?: string;
3081
+ /**
3082
+ * Shareable link copied when the user clicks Share.
3083
+ * Falls back to formats.html_url if not provided.
3084
+ * Pass a /p/{id} permanent URL once that route exists.
3085
+ */
3086
+ shareUrl?: string;
3081
3087
  className?: string;
3082
3088
  onAction?: (action: string, payload?: any) => void;
3083
3089
  sendMessage?: (message: string) => void;
package/dist/index.mjs CHANGED
@@ -38525,7 +38525,7 @@ var NextStepCard = ({
38525
38525
  ] });
38526
38526
 
38527
38527
  // src/molecules/generic/PresentationJobCard/PresentationJobCard.tsx
38528
- import { useEffect as useEffect6, useRef as useRef5, useState as useState10, useCallback as useCallback4 } from "react";
38528
+ import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef5, useState as useState10 } from "react";
38529
38529
  import { Fragment as Fragment5, jsx as jsx146, jsxs as jsxs108 } from "react/jsx-runtime";
38530
38530
  var DownloadIcon = () => /* @__PURE__ */ jsxs108("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: [
38531
38531
  /* @__PURE__ */ jsx146("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
@@ -38542,23 +38542,26 @@ var CloseIcon = () => /* @__PURE__ */ jsxs108("svg", { width: "16", height: "16"
38542
38542
  ] });
38543
38543
  var ChevronLeft2 = () => /* @__PURE__ */ jsx146("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx146("polyline", { points: "15 18 9 12 15 6" }) });
38544
38544
  var ChevronRight2 = () => /* @__PURE__ */ jsx146("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx146("polyline", { points: "9 18 15 12 9 6" }) });
38545
+ var ExpandIcon = () => /* @__PURE__ */ jsxs108("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: [
38546
+ /* @__PURE__ */ jsx146("polyline", { points: "15 3 21 3 21 9" }),
38547
+ /* @__PURE__ */ jsx146("polyline", { points: "9 21 3 21 3 15" }),
38548
+ /* @__PURE__ */ jsx146("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
38549
+ /* @__PURE__ */ jsx146("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
38550
+ ] });
38551
+ var ShareIcon = () => /* @__PURE__ */ jsxs108("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: [
38552
+ /* @__PURE__ */ jsx146("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
38553
+ /* @__PURE__ */ jsx146("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
38554
+ ] });
38555
+ var CheckIcon = () => /* @__PURE__ */ jsx146("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx146("polyline", { points: "20 6 9 17 4 12" }) });
38545
38556
  var Skel = ({ className, style }) => /* @__PURE__ */ jsx146("div", { className: cn("animate-pulse rounded-md bg-zinc-800/60", className), style });
38546
38557
  var FORMATS = [
38547
- {
38548
- key: "pptx_url",
38549
- label: "PowerPoint",
38550
- ext: ".pptx",
38551
- emoji: "\u{1F4CA}",
38552
- desc: "Editable slides \u2014 open in Microsoft PowerPoint or Google Slides",
38553
- accent: { text: "text-orange-400", bg: "bg-orange-500/10 border-orange-500/20", pill: "bg-orange-500/15 text-orange-400 border-orange-500/25" }
38554
- },
38555
38558
  {
38556
38559
  key: "pdf_url",
38557
38560
  label: "PDF",
38558
38561
  ext: ".pdf",
38559
38562
  emoji: "\u{1F4C4}",
38560
38563
  desc: "Print-ready, fixed layout \u2014 open in any PDF viewer",
38561
- accent: { text: "text-red-400", bg: "bg-red-500/10 border-red-500/20", pill: "bg-red-500/15 text-red-400 border-red-500/25" }
38564
+ accent: { text: "text-red-400", bg: "bg-red-500/10 border-red-500/20" }
38562
38565
  },
38563
38566
  {
38564
38567
  key: "html_url",
@@ -38566,12 +38569,36 @@ var FORMATS = [
38566
38569
  ext: ".html",
38567
38570
  emoji: "\u{1F310}",
38568
38571
  desc: "Interactive web presentation \u2014 download and open in browser",
38569
- accent: { text: "text-indigo-400", bg: "bg-indigo-500/10 border-indigo-500/20", pill: "bg-indigo-500/15 text-indigo-400 border-indigo-500/25" }
38572
+ accent: { text: "text-indigo-400", bg: "bg-indigo-500/10 border-indigo-500/20" }
38570
38573
  }
38571
38574
  ];
38572
38575
  var ExportModal = ({ formats, title, onClose }) => {
38573
38576
  const available = FORMATS.filter((f) => formats[f.key]);
38574
38577
  const filename = title.replace(/[^a-z0-9]/gi, "-").toLowerCase();
38578
+ const [downloadingKey, setDownloadingKey] = useState10(null);
38579
+ const handleDownload = async (fmtKey, url, ext) => {
38580
+ if (downloadingKey) return;
38581
+ const downloadName = `${filename}${ext}`;
38582
+ const href = `/api/download?url=${encodeURIComponent(url)}&filename=${encodeURIComponent(downloadName)}`;
38583
+ try {
38584
+ setDownloadingKey(fmtKey);
38585
+ const response = await fetch(href);
38586
+ if (!response.ok) throw new Error(`status ${response.status}`);
38587
+ const blob = await response.blob();
38588
+ const objectUrl = window.URL.createObjectURL(blob);
38589
+ const anchor = document.createElement("a");
38590
+ anchor.href = objectUrl;
38591
+ anchor.download = downloadName;
38592
+ document.body.appendChild(anchor);
38593
+ anchor.click();
38594
+ anchor.remove();
38595
+ window.URL.revokeObjectURL(objectUrl);
38596
+ } catch {
38597
+ window.open(href, "_blank", "noopener,noreferrer");
38598
+ } finally {
38599
+ setDownloadingKey(null);
38600
+ }
38601
+ };
38575
38602
  return /* @__PURE__ */ jsxs108("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
38576
38603
  /* @__PURE__ */ jsx146("div", { className: "absolute inset-0 bg-black/70 backdrop-blur-sm", onClick: onClose }),
38577
38604
  /* @__PURE__ */ jsxs108("div", { className: "relative z-10 bg-zinc-900 border border-zinc-800 rounded-2xl w-full max-w-sm p-6 shadow-2xl", children: [
@@ -38580,38 +38607,31 @@ var ExportModal = ({ formats, title, onClose }) => {
38580
38607
  /* @__PURE__ */ jsx146("h3", { className: "text-sm font-semibold text-zinc-100", children: "Export Presentation" }),
38581
38608
  /* @__PURE__ */ jsx146("p", { className: "text-xs text-zinc-500 mt-0.5 truncate", children: title })
38582
38609
  ] }),
38583
- /* @__PURE__ */ jsx146(
38584
- "button",
38585
- {
38586
- onClick: onClose,
38587
- className: "text-zinc-500 hover:text-zinc-200 transition-colors flex-shrink-0 mt-0.5",
38588
- "aria-label": "Close",
38589
- children: /* @__PURE__ */ jsx146(CloseIcon, {})
38590
- }
38591
- )
38610
+ /* @__PURE__ */ jsx146("button", { onClick: onClose, className: "text-zinc-500 hover:text-zinc-200 transition-colors flex-shrink-0 mt-0.5", children: /* @__PURE__ */ jsx146(CloseIcon, {}) })
38592
38611
  ] }),
38593
38612
  /* @__PURE__ */ jsxs108("div", { className: "space-y-2.5", children: [
38594
38613
  available.length === 0 && /* @__PURE__ */ jsx146("p", { className: "text-sm text-zinc-500 text-center py-6", children: "No formats available yet" }),
38595
38614
  available.map((fmt) => {
38596
38615
  const url = formats[fmt.key];
38597
- const dlName = `${filename}${fmt.ext}`;
38616
+ const isDownloading = downloadingKey === fmt.key;
38598
38617
  return /* @__PURE__ */ jsxs108(
38599
- "a",
38618
+ "button",
38600
38619
  {
38601
- href: url,
38602
- download: dlName,
38620
+ type: "button",
38621
+ disabled: Boolean(downloadingKey),
38622
+ onClick: () => handleDownload(fmt.key, url, fmt.ext),
38603
38623
  className: cn(
38604
- "flex items-center gap-3.5 p-3.5 rounded-xl border transition-all",
38605
- "hover:brightness-110 cursor-pointer no-underline group",
38624
+ "flex w-full items-center gap-3.5 p-3.5 rounded-xl border transition-all text-left",
38625
+ "hover:brightness-110 disabled:opacity-60 disabled:cursor-not-allowed",
38606
38626
  fmt.accent.bg
38607
38627
  ),
38608
38628
  children: [
38609
38629
  /* @__PURE__ */ jsx146("span", { className: "text-xl flex-shrink-0", children: fmt.emoji }),
38610
38630
  /* @__PURE__ */ jsxs108("div", { className: "flex-1 min-w-0", children: [
38611
- /* @__PURE__ */ jsx146("p", { className: cn("text-sm font-semibold", fmt.accent.text), children: fmt.label }),
38612
- /* @__PURE__ */ jsx146("p", { className: "text-xs text-zinc-500 mt-0.5 leading-relaxed", children: fmt.desc })
38631
+ /* @__PURE__ */ jsx146("p", { className: cn("text-sm font-semibold", fmt.accent.text), children: isDownloading ? `Downloading ${fmt.label}...` : fmt.label }),
38632
+ /* @__PURE__ */ jsx146("p", { className: "text-xs text-zinc-500 mt-0.5 leading-relaxed", children: isDownloading ? "Please wait while the file is being prepared." : fmt.desc })
38613
38633
  ] }),
38614
- /* @__PURE__ */ jsx146("span", { className: cn("flex-shrink-0 transition-colors", fmt.accent.text), children: /* @__PURE__ */ jsx146(DownloadIcon, {}) })
38634
+ /* @__PURE__ */ jsx146("span", { className: cn("flex-shrink-0", fmt.accent.text), children: /* @__PURE__ */ jsx146(DownloadIcon, {}) })
38615
38635
  ]
38616
38636
  },
38617
38637
  fmt.key
@@ -38622,6 +38642,87 @@ var ExportModal = ({ formats, title, onClose }) => {
38622
38642
  ] })
38623
38643
  ] });
38624
38644
  };
38645
+ var FullscreenModal = ({ url, title, slideCount, onClose }) => {
38646
+ const [currentSlide, setCurrentSlide] = useState10(1);
38647
+ const iframeRef = useRef5(null);
38648
+ useEffect6(() => {
38649
+ const onKey = (e) => {
38650
+ if (e.key === "Escape") onClose();
38651
+ };
38652
+ const onMsg = (e) => {
38653
+ if (e.data?.type === "presentationExit") onClose();
38654
+ if (e.data?.type === "slideChanged") setCurrentSlide(e.data.slide);
38655
+ };
38656
+ document.addEventListener("keydown", onKey);
38657
+ window.addEventListener("message", onMsg);
38658
+ return () => {
38659
+ document.removeEventListener("keydown", onKey);
38660
+ window.removeEventListener("message", onMsg);
38661
+ };
38662
+ }, [onClose]);
38663
+ useEffect6(() => {
38664
+ document.body.style.overflow = "hidden";
38665
+ return () => {
38666
+ document.body.style.overflow = "";
38667
+ };
38668
+ }, []);
38669
+ const send = (type) => iframeRef.current?.contentWindow?.postMessage({ type }, "*");
38670
+ return /* @__PURE__ */ jsxs108("div", { className: "fixed inset-0 z-50 bg-black flex flex-col", children: [
38671
+ /* @__PURE__ */ jsxs108("div", { className: "flex items-center justify-between gap-4 px-4 h-11 bg-zinc-950/90 border-b border-zinc-800/60 flex-shrink-0", children: [
38672
+ /* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-3 min-w-0", children: [
38673
+ /* @__PURE__ */ jsx146("span", { className: "text-indigo-400 flex-shrink-0", children: /* @__PURE__ */ jsx146(SlidesIcon, {}) }),
38674
+ /* @__PURE__ */ jsx146("span", { className: "text-sm font-medium text-zinc-300 truncate", children: title }),
38675
+ slideCount > 0 && /* @__PURE__ */ jsxs108("span", { className: "text-xs text-zinc-600 flex-shrink-0", children: [
38676
+ currentSlide,
38677
+ " / ",
38678
+ slideCount
38679
+ ] })
38680
+ ] }),
38681
+ /* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
38682
+ /* @__PURE__ */ jsx146(
38683
+ "button",
38684
+ {
38685
+ onClick: () => send("prevSlide"),
38686
+ className: "w-7 h-7 rounded-full border border-zinc-700 bg-zinc-800 hover:border-indigo-500 hover:text-indigo-400 text-zinc-400 flex items-center justify-center transition-colors",
38687
+ "aria-label": "Previous slide",
38688
+ children: /* @__PURE__ */ jsx146(ChevronLeft2, {})
38689
+ }
38690
+ ),
38691
+ /* @__PURE__ */ jsx146(
38692
+ "button",
38693
+ {
38694
+ onClick: () => send("nextSlide"),
38695
+ className: "w-7 h-7 rounded-full border border-zinc-700 bg-zinc-800 hover:border-indigo-500 hover:text-indigo-400 text-zinc-400 flex items-center justify-center transition-colors",
38696
+ "aria-label": "Next slide",
38697
+ children: /* @__PURE__ */ jsx146(ChevronRight2, {})
38698
+ }
38699
+ ),
38700
+ /* @__PURE__ */ jsxs108(
38701
+ "button",
38702
+ {
38703
+ onClick: onClose,
38704
+ className: "flex items-center gap-1.5 px-3 h-7 rounded-lg border border-zinc-700 bg-zinc-800 hover:border-zinc-500 text-zinc-400 hover:text-zinc-200 text-xs transition-colors",
38705
+ children: [
38706
+ /* @__PURE__ */ jsx146(CloseIcon, {}),
38707
+ /* @__PURE__ */ jsx146("span", { children: "ESC" })
38708
+ ]
38709
+ }
38710
+ )
38711
+ ] })
38712
+ ] }),
38713
+ /* @__PURE__ */ jsx146("div", { className: "flex-1 relative", children: /* @__PURE__ */ jsx146(
38714
+ "iframe",
38715
+ {
38716
+ ref: iframeRef,
38717
+ src: url,
38718
+ title,
38719
+ onLoad: () => iframeRef.current?.contentWindow?.postMessage({ type: "goToSlide", slide: currentSlide }, "*"),
38720
+ sandbox: "allow-same-origin allow-scripts allow-fullscreen",
38721
+ className: "absolute inset-0 w-full h-full border-0"
38722
+ }
38723
+ ) })
38724
+ ] });
38725
+ };
38625
38726
  var PresentationJobCard = ({
38626
38727
  job_id: _job_id,
38627
38728
  title: initialTitle,
@@ -38630,6 +38731,7 @@ var PresentationJobCard = ({
38630
38731
  formats: initialFormats = {},
38631
38732
  error: initialError,
38632
38733
  pollUrl,
38734
+ shareUrl,
38633
38735
  className
38634
38736
  }) => {
38635
38737
  const [status, setStatus] = useState10(initialStatus);
@@ -38637,7 +38739,9 @@ var PresentationJobCard = ({
38637
38739
  const [slideCount, setSlideCount] = useState10(initialSlideCount ?? 0);
38638
38740
  const [formats, setFormats] = useState10(initialFormats);
38639
38741
  const [error, setError] = useState10(initialError);
38640
- const [showModal, setShowModal] = useState10(false);
38742
+ const [showExport, setShowExport] = useState10(false);
38743
+ const [showFullscreen, setShowFullscreen] = useState10(false);
38744
+ const [copied, setCopied] = useState10(false);
38641
38745
  const [currentSlide, setCurrentSlide] = useState10(1);
38642
38746
  const [previewScale, setPreviewScale] = useState10(1);
38643
38747
  const [iframeReady, setIframeReady] = useState10(false);
@@ -38646,9 +38750,7 @@ var PresentationJobCard = ({
38646
38750
  const previewRef = useRef5(null);
38647
38751
  const iframeRef = useRef5(null);
38648
38752
  const updateScale = useCallback4(() => {
38649
- if (previewRef.current) {
38650
- setPreviewScale(previewRef.current.offsetWidth / 1280);
38651
- }
38753
+ if (previewRef.current) setPreviewScale(previewRef.current.offsetWidth / 1280);
38652
38754
  }, []);
38653
38755
  useEffect6(() => {
38654
38756
  updateScale();
@@ -38665,9 +38767,7 @@ var PresentationJobCard = ({
38665
38767
  const handler = (e) => {
38666
38768
  if (e.data?.type === "slideChanged") {
38667
38769
  setCurrentSlide(e.data.slide);
38668
- if (e.data.total && !slideCount) {
38669
- setSlideCount(e.data.total);
38670
- }
38770
+ if (e.data.total && !slideCount) setSlideCount(e.data.total);
38671
38771
  }
38672
38772
  };
38673
38773
  window.addEventListener("message", handler);
@@ -38678,6 +38778,10 @@ var PresentationJobCard = ({
38678
38778
  iframeRef.current?.contentWindow?.postMessage({ type: "goToSlide", slide: pendingSlide }, "*");
38679
38779
  setPendingSlide(null);
38680
38780
  }, [iframeReady, pendingSlide]);
38781
+ const sendSlideCommand = (command) => {
38782
+ if (!iframeReady) return;
38783
+ iframeRef.current?.contentWindow?.postMessage({ type: command }, "*");
38784
+ };
38681
38785
  const goToSlide = (n) => {
38682
38786
  const total2 = slideCount || 1;
38683
38787
  const target = (n - 1 + total2) % total2 + 1;
@@ -38703,9 +38807,7 @@ var PresentationJobCard = ({
38703
38807
  if (data.output.slide_count) setSlideCount(data.output.slide_count);
38704
38808
  if (data.output.formats) setFormats(data.output.formats);
38705
38809
  }
38706
- if (newStatus === "failed" && data.error) {
38707
- setError(data.error);
38708
- }
38810
+ if (newStatus === "failed" && data.error) setError(data.error);
38709
38811
  } catch {
38710
38812
  }
38711
38813
  };
@@ -38721,6 +38823,17 @@ var PresentationJobCard = ({
38721
38823
  intervalRef.current = null;
38722
38824
  }
38723
38825
  }, [isTerminal]);
38826
+ const handleShare = async () => {
38827
+ const link = shareUrl ?? formats.html_url;
38828
+ if (!link) return;
38829
+ try {
38830
+ await navigator.clipboard.writeText(link);
38831
+ setCopied(true);
38832
+ setTimeout(() => setCopied(false), 2e3);
38833
+ } catch {
38834
+ window.open(link, "_blank", "noopener,noreferrer");
38835
+ }
38836
+ };
38724
38837
  if (status === "pending" || status === "running") {
38725
38838
  return /* @__PURE__ */ jsx146("div", { className: cn("w-full", className), children: /* @__PURE__ */ jsxs108("div", { className: "bg-zinc-900 border border-zinc-800 rounded-2xl p-5", children: [
38726
38839
  /* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-3 mb-5", children: [
@@ -38746,8 +38859,8 @@ var PresentationJobCard = ({
38746
38859
  ] })
38747
38860
  ] }) }) });
38748
38861
  }
38749
- const availablePills = FORMATS.filter((f) => formats[f.key]);
38750
38862
  const total = slideCount || 1;
38863
+ const hasHtml = Boolean(formats.html_url);
38751
38864
  return /* @__PURE__ */ jsxs108(Fragment5, { children: [
38752
38865
  /* @__PURE__ */ jsx146("div", { className: cn("w-full", className), children: /* @__PURE__ */ jsxs108("div", { className: "bg-zinc-900 border border-zinc-800 rounded-2xl overflow-hidden", children: [
38753
38866
  /* @__PURE__ */ jsx146("div", { className: "h-[3px] bg-gradient-to-r from-indigo-600 via-violet-500 to-purple-600" }),
@@ -38761,106 +38874,127 @@ var PresentationJobCard = ({
38761
38874
  " \xB7 Ready to export"
38762
38875
  ] })
38763
38876
  ] }),
38764
- /* @__PURE__ */ jsxs108(
38765
- "button",
38766
- {
38767
- onClick: () => setShowModal(true),
38768
- className: "flex items-center gap-1.5 px-3 py-1.5 bg-indigo-600 hover:bg-indigo-500 active:bg-indigo-700 text-white text-xs font-medium rounded-lg transition-colors flex-shrink-0",
38769
- children: [
38770
- /* @__PURE__ */ jsx146(DownloadIcon, {}),
38771
- "Export"
38772
- ]
38773
- }
38774
- )
38877
+ /* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-1.5 flex-shrink-0", children: [
38878
+ /* @__PURE__ */ jsx146(
38879
+ "button",
38880
+ {
38881
+ onClick: handleShare,
38882
+ disabled: !hasHtml,
38883
+ title: copied ? "Copied!" : "Copy shareable link",
38884
+ className: cn(
38885
+ "w-7 h-7 rounded-lg border flex items-center justify-center transition-all",
38886
+ copied ? "border-green-500/40 bg-green-500/10 text-green-400" : "border-zinc-700 bg-zinc-800 text-zinc-400 hover:border-zinc-500 hover:text-zinc-200",
38887
+ "disabled:opacity-30 disabled:cursor-not-allowed"
38888
+ ),
38889
+ children: copied ? /* @__PURE__ */ jsx146(CheckIcon, {}) : /* @__PURE__ */ jsx146(ShareIcon, {})
38890
+ }
38891
+ ),
38892
+ /* @__PURE__ */ jsxs108(
38893
+ "button",
38894
+ {
38895
+ onClick: () => setShowFullscreen(true),
38896
+ disabled: !hasHtml,
38897
+ title: "Open fullscreen preview",
38898
+ className: "flex items-center gap-1.5 px-2.5 h-7 rounded-lg border border-zinc-700 bg-zinc-800 hover:border-zinc-500 text-zinc-400 hover:text-zinc-200 text-xs font-medium transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
38899
+ children: [
38900
+ /* @__PURE__ */ jsx146(ExpandIcon, {}),
38901
+ /* @__PURE__ */ jsx146("span", { children: "Preview" })
38902
+ ]
38903
+ }
38904
+ ),
38905
+ /* @__PURE__ */ jsxs108(
38906
+ "button",
38907
+ {
38908
+ onClick: () => setShowExport(true),
38909
+ className: "flex items-center gap-1.5 px-2.5 h-7 bg-indigo-600 hover:bg-indigo-500 active:bg-indigo-700 text-white text-xs font-medium rounded-lg transition-colors",
38910
+ children: [
38911
+ /* @__PURE__ */ jsx146(DownloadIcon, {}),
38912
+ /* @__PURE__ */ jsx146("span", { children: "Export" })
38913
+ ]
38914
+ }
38915
+ )
38916
+ ] })
38775
38917
  ] }),
38776
- formats.html_url && /* @__PURE__ */ jsxs108("div", { className: "mt-4", children: [
38777
- /* @__PURE__ */ jsx146(
38918
+ hasHtml && /* @__PURE__ */ jsxs108("div", { className: "mt-4", children: [
38919
+ /* @__PURE__ */ jsxs108(
38778
38920
  "div",
38779
38921
  {
38780
38922
  ref: previewRef,
38781
- className: "relative overflow-hidden rounded-xl bg-zinc-950 border border-zinc-800/50",
38923
+ onClick: () => setShowFullscreen(true),
38924
+ className: "group relative overflow-hidden rounded-xl bg-zinc-950 border border-zinc-800/50 cursor-pointer",
38782
38925
  style: { height: Math.round(previewScale * 720) },
38783
- children: /* @__PURE__ */ jsx146(
38784
- "iframe",
38785
- {
38786
- ref: iframeRef,
38787
- src: formats.html_url,
38788
- title,
38789
- onLoad: () => setIframeReady(true),
38790
- sandbox: "allow-same-origin allow-scripts",
38791
- style: {
38792
- width: 1280,
38793
- height: 720,
38794
- transform: `scale(${previewScale})`,
38795
- transformOrigin: "top left",
38796
- border: "none",
38797
- pointerEvents: "none",
38798
- display: "block"
38926
+ title: "Click to open fullscreen",
38927
+ children: [
38928
+ /* @__PURE__ */ jsx146(
38929
+ "iframe",
38930
+ {
38931
+ ref: iframeRef,
38932
+ src: formats.html_url,
38933
+ title,
38934
+ onLoad: () => setIframeReady(true),
38935
+ sandbox: "allow-same-origin allow-scripts",
38936
+ style: {
38937
+ width: 1280,
38938
+ height: 720,
38939
+ transform: `scale(${previewScale})`,
38940
+ transformOrigin: "top left",
38941
+ border: "none",
38942
+ pointerEvents: "none",
38943
+ display: "block"
38944
+ }
38799
38945
  }
38800
- }
38801
- )
38946
+ ),
38947
+ /* @__PURE__ */ jsx146("div", { className: "absolute inset-0 flex items-center justify-center bg-black/0 group-hover:bg-black/30 transition-colors", children: /* @__PURE__ */ jsxs108("span", { className: "opacity-0 group-hover:opacity-100 transition-opacity flex items-center gap-2 bg-zinc-900/80 backdrop-blur-sm border border-zinc-700 rounded-lg px-3 py-1.5 text-xs text-zinc-200 font-medium", children: [
38948
+ /* @__PURE__ */ jsx146(ExpandIcon, {}),
38949
+ "Open fullscreen"
38950
+ ] }) })
38951
+ ]
38802
38952
  }
38803
38953
  ),
38804
- /* @__PURE__ */ jsxs108("div", { className: "flex items-center justify-between mt-3", children: [
38805
- /* @__PURE__ */ jsx146("div", { className: "flex items-center gap-2", children: availablePills.map((fmt) => /* @__PURE__ */ jsx146(
38806
- "span",
38954
+ /* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-2 mt-3", children: [
38955
+ /* @__PURE__ */ jsx146(
38956
+ "button",
38807
38957
  {
38808
- className: cn(
38809
- "text-xs px-2.5 py-0.5 rounded-md border font-medium",
38810
- fmt.accent.pill
38811
- ),
38812
- children: fmt.label
38813
- },
38814
- fmt.key
38815
- )) }),
38816
- /* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-2", children: [
38817
- /* @__PURE__ */ jsx146(
38818
- "button",
38819
- {
38820
- onClick: () => goToSlide(currentSlide - 1),
38821
- disabled: total <= 1,
38822
- className: "w-7 h-7 rounded-full border border-zinc-700 bg-zinc-800 hover:border-indigo-500 hover:text-indigo-400 text-zinc-400 flex items-center justify-center transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
38823
- "aria-label": "Previous slide",
38824
- children: /* @__PURE__ */ jsx146(ChevronLeft2, {})
38825
- }
38826
- ),
38827
- /* @__PURE__ */ jsxs108("span", { className: "text-xs text-zinc-500 font-medium tabular-nums min-w-[3rem] text-center", children: [
38828
- currentSlide,
38829
- " / ",
38830
- total
38831
- ] }),
38832
- /* @__PURE__ */ jsx146(
38833
- "button",
38834
- {
38835
- onClick: () => goToSlide(currentSlide + 1),
38836
- disabled: total <= 1,
38837
- className: "w-7 h-7 rounded-full border border-zinc-700 bg-zinc-800 hover:border-indigo-500 hover:text-indigo-400 text-zinc-400 flex items-center justify-center transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
38838
- "aria-label": "Next slide",
38839
- children: /* @__PURE__ */ jsx146(ChevronRight2, {})
38840
- }
38841
- )
38842
- ] })
38843
- ] })
38844
- ] }),
38845
- !formats.html_url && availablePills.length > 0 && /* @__PURE__ */ jsx146("div", { className: "flex items-center gap-2 mt-4", children: availablePills.map((fmt) => /* @__PURE__ */ jsx146(
38846
- "span",
38847
- {
38848
- className: cn(
38849
- "text-xs px-2.5 py-0.5 rounded-md border font-medium",
38850
- fmt.accent.pill
38958
+ onClick: (e) => {
38959
+ e.stopPropagation();
38960
+ slideCount > 1 ? goToSlide(currentSlide - 1) : sendSlideCommand("prevSlide");
38961
+ },
38962
+ disabled: !hasHtml,
38963
+ className: "w-7 h-7 rounded-full border border-zinc-700 bg-zinc-800 hover:border-indigo-500 hover:text-indigo-400 text-zinc-400 flex items-center justify-center transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
38964
+ "aria-label": "Previous slide",
38965
+ children: /* @__PURE__ */ jsx146(ChevronLeft2, {})
38966
+ }
38851
38967
  ),
38852
- children: fmt.label
38853
- },
38854
- fmt.key
38855
- )) })
38968
+ /* @__PURE__ */ jsxs108("span", { className: "text-xs text-zinc-500 font-medium tabular-nums min-w-[3rem] text-center", children: [
38969
+ currentSlide,
38970
+ " / ",
38971
+ total
38972
+ ] }),
38973
+ /* @__PURE__ */ jsx146(
38974
+ "button",
38975
+ {
38976
+ onClick: (e) => {
38977
+ e.stopPropagation();
38978
+ slideCount > 1 ? goToSlide(currentSlide + 1) : sendSlideCommand("nextSlide");
38979
+ },
38980
+ disabled: !hasHtml,
38981
+ className: "w-7 h-7 rounded-full border border-zinc-700 bg-zinc-800 hover:border-indigo-500 hover:text-indigo-400 text-zinc-400 flex items-center justify-center transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
38982
+ "aria-label": "Next slide",
38983
+ children: /* @__PURE__ */ jsx146(ChevronRight2, {})
38984
+ }
38985
+ )
38986
+ ] })
38987
+ ] })
38856
38988
  ] })
38857
38989
  ] }) }),
38858
- showModal && /* @__PURE__ */ jsx146(
38859
- ExportModal,
38990
+ showExport && /* @__PURE__ */ jsx146(ExportModal, { formats, title, onClose: () => setShowExport(false) }),
38991
+ showFullscreen && hasHtml && /* @__PURE__ */ jsx146(
38992
+ FullscreenModal,
38860
38993
  {
38861
- formats,
38994
+ url: formats.html_url,
38862
38995
  title,
38863
- onClose: () => setShowModal(false)
38996
+ slideCount,
38997
+ onClose: () => setShowFullscreen(false)
38864
38998
  }
38865
38999
  )
38866
39000
  ] });