pxengine 0.1.118 → 0.1.119

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.cjs CHANGED
@@ -16143,6 +16143,7 @@ function formatTemplateLabel(templateId) {
16143
16143
  if (!templateId) return null;
16144
16144
  return templateId.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
16145
16145
  }
16146
+ var Chip = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("span", { className: "inline-flex items-center px-2 h-[22px] rounded-md bg-zinc-800/70 border border-zinc-700/50 text-[11px] font-medium text-zinc-400 whitespace-nowrap", children });
16146
16147
  function deriveJobStatusUrl(newJobId, pollUrl, regenerateUrl) {
16147
16148
  const base = pollUrl?.replace(/\/api\/jobs\/[^/]+\/status.*$/, "") ?? regenerateUrl?.replace(/\/api\/presentations\/[^/]+\/regenerate.*$/, "") ?? "";
16148
16149
  return `${base}/api/jobs/${newJobId}/status`;
@@ -17121,12 +17122,22 @@ var PresentationJobCard = ({
17121
17122
  ),
17122
17123
  /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("div", { className: "flex-1 min-w-0", children: [
17123
17124
  /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("h3", { className: "text-sm font-semibold text-zinc-100 truncate leading-tight", style: t.text, children: title }),
17124
- /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("p", { className: "text-xs text-zinc-500 mt-0.5", style: t.muted, children: [
17125
- slideCount ? `${slideCount} slides` : "Presentation",
17126
- " \xB7 Ready to export",
17127
- templateLabel && ` \xB7 ${templateLabel}`
17125
+ /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5 mt-1.5", children: [
17126
+ /* @__PURE__ */ (0, import_jsx_runtime147.jsx)(Chip, { children: slideCount ? `${slideCount} slides` : "Presentation" }),
17127
+ templateLabel && /* @__PURE__ */ (0, import_jsx_runtime147.jsx)(Chip, { children: templateLabel })
17128
17128
  ] }),
17129
- generationMode === "template" && /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("p", { className: "text-xs mt-1", children: isApproved ? /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("span", { className: "text-emerald-400/90", children: "Approved \u2014 future decks use this format" }) : reviewStatus === "pending_review" ? /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("span", { className: "text-amber-400/90", children: "Review this deck, then approve to lock the format" }) : null })
17129
+ generationMode === "template" && (isApproved || reviewStatus === "pending_review") && /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("p", { className: "flex items-center gap-1.5 text-xs mt-2", children: [
17130
+ /* @__PURE__ */ (0, import_jsx_runtime147.jsx)(
17131
+ "span",
17132
+ {
17133
+ className: cn(
17134
+ "w-1.5 h-1.5 rounded-full flex-shrink-0",
17135
+ isApproved ? "bg-emerald-400" : "bg-amber-400"
17136
+ )
17137
+ }
17138
+ ),
17139
+ isApproved ? /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("span", { className: "text-emerald-400/90", children: "Approved \u2014 future decks use this format" }) : /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("span", { className: "text-amber-400/90", children: "Review this deck, then approve to lock the format" })
17140
+ ] })
17130
17141
  ] }),
17131
17142
  /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("div", { className: "flex items-center gap-1.5 flex-shrink-0 flex-wrap justify-end", children: [
17132
17143
  /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("div", { className: "flex items-center rounded-lg border border-zinc-700 bg-zinc-800 overflow-hidden divide-x divide-zinc-700", children: [
@@ -17411,6 +17422,102 @@ var FullscreenPreviewModal = ({ url, title, onClose }) => {
17411
17422
  ) })
17412
17423
  ] });
17413
17424
  };
17425
+ var Chip2 = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { className: "inline-flex items-center px-2 h-[22px] rounded-md bg-zinc-800/70 border border-zinc-700/50 text-[11px] font-medium text-zinc-400 whitespace-nowrap capitalize", children });
17426
+ var REPORT_FORMATS = [
17427
+ {
17428
+ key: "pdf",
17429
+ label: "PDF",
17430
+ ext: ".pdf",
17431
+ emoji: "\u{1F4C4}",
17432
+ desc: "Print-ready, fixed layout \u2014 open in any PDF viewer",
17433
+ accent: { text: "text-red-400", bg: "bg-red-500/10 border-red-500/20" }
17434
+ },
17435
+ {
17436
+ key: "html",
17437
+ label: "HTML",
17438
+ ext: ".html",
17439
+ emoji: "\u{1F310}",
17440
+ desc: "Interactive web page \u2014 download and open in a browser",
17441
+ accent: { text: "text-sky-400", bg: "bg-sky-500/10 border-sky-500/20" }
17442
+ }
17443
+ ];
17444
+ var ReportExportModal = ({ htmlUrl, pdfUrl, title, onClose }) => {
17445
+ const urls = { pdf: pdfUrl, html: htmlUrl };
17446
+ const available = REPORT_FORMATS.filter((f) => urls[f.key]);
17447
+ const filename = (title ?? "").replace(/[^a-z0-9]/gi, "-").toLowerCase();
17448
+ const [downloadingKey, setDownloadingKey] = (0, import_react79.useState)(null);
17449
+ (0, import_react79.useEffect)(() => {
17450
+ const onKey = (e) => {
17451
+ if (e.key === "Escape") onClose();
17452
+ };
17453
+ document.addEventListener("keydown", onKey);
17454
+ return () => document.removeEventListener("keydown", onKey);
17455
+ }, [onClose]);
17456
+ const handleDownload = async (key, url, ext) => {
17457
+ if (downloadingKey) return;
17458
+ const downloadName = `${filename}${ext}`;
17459
+ const href = `/api/download?url=${encodeURIComponent(url)}&filename=${encodeURIComponent(downloadName)}`;
17460
+ try {
17461
+ setDownloadingKey(key);
17462
+ const response = await fetch(href);
17463
+ if (!response.ok) throw new Error(`status ${response.status}`);
17464
+ const blob = await response.blob();
17465
+ const objectUrl = window.URL.createObjectURL(blob);
17466
+ const anchor = document.createElement("a");
17467
+ anchor.href = objectUrl;
17468
+ anchor.download = downloadName;
17469
+ document.body.appendChild(anchor);
17470
+ anchor.click();
17471
+ anchor.remove();
17472
+ window.URL.revokeObjectURL(objectUrl);
17473
+ } catch {
17474
+ window.open(url, "_blank", "noopener,noreferrer");
17475
+ } finally {
17476
+ setDownloadingKey(null);
17477
+ }
17478
+ };
17479
+ return /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
17480
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("div", { className: "absolute inset-0 bg-black/70 backdrop-blur-sm", onClick: onClose }),
17481
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "relative z-10 bg-zinc-900 border border-zinc-800 rounded-2xl w-full max-w-sm p-6 shadow-2xl", children: [
17482
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "flex items-start justify-between mb-5", children: [
17483
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "min-w-0 pr-3", children: [
17484
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("h3", { className: "text-sm font-semibold text-zinc-100", children: "Download report" }),
17485
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: "text-xs text-zinc-500 mt-0.5 truncate", children: title })
17486
+ ] }),
17487
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("button", { onClick: onClose, className: "text-zinc-500 hover:text-zinc-200 transition-colors flex-shrink-0 mt-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(CloseIcon2, {}) })
17488
+ ] }),
17489
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "space-y-2.5", children: [
17490
+ available.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: "text-sm text-zinc-500 text-center py-6", children: "No formats available yet" }),
17491
+ available.map((fmt) => {
17492
+ const url = urls[fmt.key];
17493
+ const isDownloading = downloadingKey === fmt.key;
17494
+ return /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(
17495
+ "button",
17496
+ {
17497
+ type: "button",
17498
+ disabled: Boolean(downloadingKey),
17499
+ onClick: () => handleDownload(fmt.key, url, fmt.ext),
17500
+ className: cn(
17501
+ "flex w-full items-center gap-3.5 p-3.5 rounded-xl border transition-all text-left",
17502
+ "hover:brightness-110 disabled:opacity-60 disabled:cursor-not-allowed",
17503
+ fmt.accent.bg
17504
+ ),
17505
+ children: [
17506
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { className: "text-xl flex-shrink-0", children: fmt.emoji }),
17507
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "flex-1 min-w-0", children: [
17508
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: cn("text-sm font-semibold", fmt.accent.text), children: isDownloading ? `Downloading ${fmt.label}...` : fmt.label }),
17509
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: "text-xs text-zinc-500 mt-0.5 leading-relaxed", children: isDownloading ? "Please wait while the file is being prepared." : fmt.desc })
17510
+ ] }),
17511
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { className: cn("flex-shrink-0", fmt.accent.text), children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(DownloadIcon2, {}) })
17512
+ ]
17513
+ },
17514
+ fmt.key
17515
+ );
17516
+ })
17517
+ ] })
17518
+ ] })
17519
+ ] });
17520
+ };
17414
17521
  function deriveJobStatusUrl2(newJobId, pollUrl, regenerateUrl) {
17415
17522
  const base = pollUrl?.replace(/\/api\/jobs\/[^/]+\/status.*$/, "") ?? regenerateUrl?.replace(/\/api\/reports\/[^/]+\/regenerate.*$/, "") ?? "";
17416
17523
  return `${base}/api/jobs/${newJobId}/status`;
@@ -17474,6 +17581,7 @@ var ResearchReportJobCard = (props) => {
17474
17581
  const [error, setError] = (0, import_react79.useState)(initialError);
17475
17582
  const [progress, setProgress] = (0, import_react79.useState)(initialProgress);
17476
17583
  const [showPreview, setShowPreview] = (0, import_react79.useState)(false);
17584
+ const [showExport, setShowExport] = (0, import_react79.useState)(false);
17477
17585
  const [previewScale, setPreviewScale] = (0, import_react79.useState)(1);
17478
17586
  const [copied, setCopied] = (0, import_react79.useState)(false);
17479
17587
  const [approving, setApproving] = (0, import_react79.useState)(false);
@@ -18064,46 +18172,6 @@ var ResearchReportJobCard = (props) => {
18064
18172
  ] }) })
18065
18173
  ] }) });
18066
18174
  }
18067
- const handleDownload = async () => {
18068
- if (!htmlUrl) return;
18069
- const filename = `${(title ?? "").replace(/[^a-z0-9]/gi, "-").toLowerCase()}.html`;
18070
- const href = `/api/download?url=${encodeURIComponent(htmlUrl)}&filename=${encodeURIComponent(filename)}`;
18071
- try {
18072
- const response = await fetch(href);
18073
- if (!response.ok) throw new Error(`status ${response.status}`);
18074
- const blob = await response.blob();
18075
- const objectUrl = window.URL.createObjectURL(blob);
18076
- const anchor = document.createElement("a");
18077
- anchor.href = objectUrl;
18078
- anchor.download = filename;
18079
- document.body.appendChild(anchor);
18080
- anchor.click();
18081
- anchor.remove();
18082
- window.URL.revokeObjectURL(objectUrl);
18083
- } catch {
18084
- window.open(htmlUrl, "_blank", "noopener,noreferrer");
18085
- }
18086
- };
18087
- const handleDownloadPdf = async () => {
18088
- if (!pdfUrl) return;
18089
- const filename = `${(title ?? "").replace(/[^a-z0-9]/gi, "-").toLowerCase()}.pdf`;
18090
- const href = `/api/download?url=${encodeURIComponent(pdfUrl)}&filename=${encodeURIComponent(filename)}`;
18091
- try {
18092
- const response = await fetch(href);
18093
- if (!response.ok) throw new Error(`status ${response.status}`);
18094
- const blob = await response.blob();
18095
- const objectUrl = window.URL.createObjectURL(blob);
18096
- const anchor = document.createElement("a");
18097
- anchor.href = objectUrl;
18098
- anchor.download = filename;
18099
- document.body.appendChild(anchor);
18100
- anchor.click();
18101
- anchor.remove();
18102
- window.URL.revokeObjectURL(objectUrl);
18103
- } catch {
18104
- window.open(pdfUrl, "_blank", "noopener,noreferrer");
18105
- }
18106
- };
18107
18175
  const resolveShareLink = () => {
18108
18176
  if (shareUrl) return shareUrl;
18109
18177
  if (typeof window !== "undefined" && job_id) {
@@ -18229,15 +18297,34 @@ var ResearchReportJobCard = (props) => {
18229
18297
  ),
18230
18298
  /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "flex-1 min-w-0", children: [
18231
18299
  /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("h3", { className: "text-sm font-semibold text-zinc-100 truncate leading-tight", children: title }),
18232
- /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("p", { className: "text-xs text-zinc-500 mt-0.5", children: [
18233
- depth && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { className: "capitalize", children: depth }),
18234
- depth && sectionCount > 0 && " \xB7 ",
18235
- sectionCount > 0 && `${sectionCount} sections`,
18236
- sourceCount > 0 && ` \xB7 ${sourceCount} sources`,
18237
- wordCount > 0 && ` \xB7 ${formatWordCount(wordCount)} words`,
18238
- templateLabel && ` \xB7 ${templateLabel}`
18300
+ (depth || sectionCount > 0 || sourceCount > 0 || wordCount > 0 || templateLabel) && /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5 mt-1.5", children: [
18301
+ depth && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(Chip2, { children: depth }),
18302
+ sectionCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(Chip2, { children: [
18303
+ sectionCount,
18304
+ " sections"
18305
+ ] }),
18306
+ sourceCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(Chip2, { children: [
18307
+ sourceCount,
18308
+ " sources"
18309
+ ] }),
18310
+ wordCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(Chip2, { children: [
18311
+ formatWordCount(wordCount),
18312
+ " words"
18313
+ ] }),
18314
+ templateLabel && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(Chip2, { children: templateLabel })
18239
18315
  ] }),
18240
- generationMode === "template" && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: "text-xs mt-1", children: isApproved ? /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { className: "text-emerald-400/90", children: "Approved \u2014 future reports use this format" }) : reviewStatus === "pending_review" ? /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { className: "text-amber-400/90", children: "Review this report, then approve to lock the layout" }) : null })
18316
+ generationMode === "template" && (isApproved || reviewStatus === "pending_review") && /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("p", { className: "flex items-center gap-1.5 text-xs mt-2", children: [
18317
+ /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
18318
+ "span",
18319
+ {
18320
+ className: cn(
18321
+ "w-1.5 h-1.5 rounded-full flex-shrink-0",
18322
+ isApproved ? "bg-emerald-400" : "bg-amber-400"
18323
+ )
18324
+ }
18325
+ ),
18326
+ isApproved ? /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { className: "text-emerald-400/90", children: "Approved \u2014 future reports use this format" }) : /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { className: "text-amber-400/90", children: "Review this report, then approve to lock the layout" })
18327
+ ] })
18241
18328
  ] }),
18242
18329
  /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "flex items-center gap-1.5 flex-shrink-0 flex-wrap justify-end", children: [
18243
18330
  /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "flex items-center rounded-lg border border-zinc-700 bg-zinc-800 overflow-hidden divide-x divide-zinc-700", children: [
@@ -18295,18 +18382,6 @@ var ResearchReportJobCard = (props) => {
18295
18382
  ),
18296
18383
  children: copied ? /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(CheckIcon2, {}) : /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(ShareIcon2, {})
18297
18384
  }
18298
- ),
18299
- pdfUrl && /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(
18300
- "button",
18301
- {
18302
- onClick: handleDownloadPdf,
18303
- title: "Download PDF",
18304
- className: "flex items-center gap-1.5 px-2.5 h-7 text-xs font-medium text-zinc-300 hover:text-zinc-100 hover:bg-zinc-700/60 transition-colors",
18305
- children: [
18306
- /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(DownloadIcon2, {}),
18307
- /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { children: "PDF" })
18308
- ]
18309
- }
18310
18385
  )
18311
18386
  ] }),
18312
18387
  canApprove && /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(
@@ -18326,8 +18401,9 @@ var ResearchReportJobCard = (props) => {
18326
18401
  /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(
18327
18402
  "button",
18328
18403
  {
18329
- onClick: handleDownload,
18330
- disabled: !hasHTML,
18404
+ onClick: () => setShowExport(true),
18405
+ disabled: !hasHTML && !pdfUrl,
18406
+ title: "Download this report (PDF or HTML)",
18331
18407
  className: "flex items-center gap-1.5 px-3 h-7 text-xs font-semibold rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
18332
18408
  style: { backgroundImage: GOLD_GRADIENT, color: readableOn(primaryColor) },
18333
18409
  onMouseEnter: (e) => e.currentTarget.style.filter = "brightness(1.1)",
@@ -18384,6 +18460,15 @@ var ResearchReportJobCard = (props) => {
18384
18460
  title,
18385
18461
  onClose: () => setShowPreview(false)
18386
18462
  }
18463
+ ),
18464
+ showExport && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
18465
+ ReportExportModal,
18466
+ {
18467
+ htmlUrl,
18468
+ pdfUrl,
18469
+ title,
18470
+ onClose: () => setShowExport(false)
18471
+ }
18387
18472
  )
18388
18473
  ] });
18389
18474
  };