pxengine 0.1.114 → 0.1.116
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 +307 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.mjs +307 -36
- package/dist/index.mjs.map +1 -1
- package/dist/registry.json +26 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -16133,6 +16133,8 @@ var SlideTypeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("svg",
|
|
|
16133
16133
|
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)("rect", { x: "3", y: "4", width: "18", height: "14", rx: "2" }),
|
|
16134
16134
|
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)("line", { x1: "3", y1: "10", x2: "21", y2: "10" })
|
|
16135
16135
|
] });
|
|
16136
|
+
var ChevronUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("polyline", { points: "18 15 12 9 6 15" }) });
|
|
16137
|
+
var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("polyline", { points: "6 9 12 15 18 9" }) });
|
|
16136
16138
|
function formatTemplateLabel(templateId) {
|
|
16137
16139
|
if (!templateId) return null;
|
|
16138
16140
|
return templateId.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
@@ -16358,6 +16360,8 @@ var PresentationJobCard = ({
|
|
|
16358
16360
|
shareUrl,
|
|
16359
16361
|
approveUrl,
|
|
16360
16362
|
approveOutlineUrl,
|
|
16363
|
+
reorderSlidesUrl,
|
|
16364
|
+
approveSlideUrl,
|
|
16361
16365
|
regenerateUrl,
|
|
16362
16366
|
hideApproval = false,
|
|
16363
16367
|
className,
|
|
@@ -16389,6 +16393,8 @@ var PresentationJobCard = ({
|
|
|
16389
16393
|
const [messageEdits, setMessageEdits] = (0, import_react78.useState)({});
|
|
16390
16394
|
const [approvingOutline, setApprovingOutline] = (0, import_react78.useState)(false);
|
|
16391
16395
|
const [outlineWritePollUrl, setOutlineWritePollUrl] = (0, import_react78.useState)(null);
|
|
16396
|
+
const [rowBusyIndex, setRowBusyIndex] = (0, import_react78.useState)(null);
|
|
16397
|
+
const [rowError, setRowError] = (0, import_react78.useState)(null);
|
|
16392
16398
|
const [regenPollUrl, setRegenPollUrl] = (0, import_react78.useState)(null);
|
|
16393
16399
|
const [currentSlide, setCurrentSlide] = (0, import_react78.useState)(1);
|
|
16394
16400
|
const [previewScale, setPreviewScale] = (0, import_react78.useState)(1);
|
|
@@ -16621,6 +16627,76 @@ var PresentationJobCard = ({
|
|
|
16621
16627
|
});
|
|
16622
16628
|
return changed ? { ...outline, slides } : void 0;
|
|
16623
16629
|
};
|
|
16630
|
+
const moveOutlineSlide = async (index, delta) => {
|
|
16631
|
+
if (!outline || !Array.isArray(outline.slides) || rowBusyIndex !== null) return;
|
|
16632
|
+
const slides = outline.slides;
|
|
16633
|
+
const target = index + delta;
|
|
16634
|
+
if (target < 0 || target >= slides.length) return;
|
|
16635
|
+
const order = slides.map((_, i) => i);
|
|
16636
|
+
[order[index], order[target]] = [order[target], order[index]];
|
|
16637
|
+
const reordered = order.map((i, newIndex) => ({ ...slides[i], n: newIndex + 1 }));
|
|
16638
|
+
const previousOutline = outline;
|
|
16639
|
+
const previousEdits = messageEdits;
|
|
16640
|
+
setOutline({ ...outline, slides: reordered });
|
|
16641
|
+
setMessageEdits((prev) => {
|
|
16642
|
+
const remapped = {};
|
|
16643
|
+
order.forEach((originalIndex, newIndex) => {
|
|
16644
|
+
if (prev[originalIndex] !== void 0) remapped[newIndex] = prev[originalIndex];
|
|
16645
|
+
});
|
|
16646
|
+
return remapped;
|
|
16647
|
+
});
|
|
16648
|
+
setRowBusyIndex(index);
|
|
16649
|
+
setRowError(null);
|
|
16650
|
+
try {
|
|
16651
|
+
const endpoint = reorderSlidesUrl || `/api/presentations/${_job_id}/outline/reorder-slides`;
|
|
16652
|
+
const headers = { "Content-Type": "application/json" };
|
|
16653
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
16654
|
+
const res = await fetch(endpoint, {
|
|
16655
|
+
method: "POST",
|
|
16656
|
+
headers,
|
|
16657
|
+
body: JSON.stringify({ order })
|
|
16658
|
+
});
|
|
16659
|
+
if (!res.ok) {
|
|
16660
|
+
const err = await res.json().catch(() => ({}));
|
|
16661
|
+
throw new Error(err.detail || err.error || `Reorder failed (${res.status})`);
|
|
16662
|
+
}
|
|
16663
|
+
} catch (e) {
|
|
16664
|
+
setOutline(previousOutline);
|
|
16665
|
+
setMessageEdits(previousEdits);
|
|
16666
|
+
setRowError(e instanceof Error ? e.message : "Could not reorder slides");
|
|
16667
|
+
} finally {
|
|
16668
|
+
setRowBusyIndex(null);
|
|
16669
|
+
}
|
|
16670
|
+
};
|
|
16671
|
+
const toggleSlideApproved = async (index) => {
|
|
16672
|
+
if (!outline || !Array.isArray(outline.slides) || rowBusyIndex !== null) return;
|
|
16673
|
+
const slides = outline.slides;
|
|
16674
|
+
const nextApproved = !slides[index]?.approved;
|
|
16675
|
+
const previousOutline = outline;
|
|
16676
|
+
const updated = slides.map((s, i) => i === index ? { ...s, approved: nextApproved } : s);
|
|
16677
|
+
setOutline({ ...outline, slides: updated });
|
|
16678
|
+
setRowBusyIndex(index);
|
|
16679
|
+
setRowError(null);
|
|
16680
|
+
try {
|
|
16681
|
+
const endpoint = approveSlideUrl || `/api/presentations/${_job_id}/outline/approve-slide`;
|
|
16682
|
+
const headers = { "Content-Type": "application/json" };
|
|
16683
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
16684
|
+
const res = await fetch(endpoint, {
|
|
16685
|
+
method: "POST",
|
|
16686
|
+
headers,
|
|
16687
|
+
body: JSON.stringify({ slide_index: index, approved: nextApproved })
|
|
16688
|
+
});
|
|
16689
|
+
if (!res.ok) {
|
|
16690
|
+
const err = await res.json().catch(() => ({}));
|
|
16691
|
+
throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
|
|
16692
|
+
}
|
|
16693
|
+
} catch (e) {
|
|
16694
|
+
setOutline(previousOutline);
|
|
16695
|
+
setRowError(e instanceof Error ? e.message : "Could not update slide");
|
|
16696
|
+
} finally {
|
|
16697
|
+
setRowBusyIndex(null);
|
|
16698
|
+
}
|
|
16699
|
+
};
|
|
16624
16700
|
const handleApproveOutline = async () => {
|
|
16625
16701
|
if (!_job_id || approvingOutline) return;
|
|
16626
16702
|
const endpoint = approveOutlineUrl || `/api/presentations/${_job_id}/approve-outline`;
|
|
@@ -16858,31 +16934,71 @@ var PresentationJobCard = ({
|
|
|
16858
16934
|
] })
|
|
16859
16935
|
] }),
|
|
16860
16936
|
/* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("div", { className: "mt-4 space-y-2", children: [
|
|
16861
|
-
slides.map((s, i) =>
|
|
16862
|
-
|
|
16863
|
-
/* @__PURE__ */ (0, import_jsx_runtime147.jsxs)(
|
|
16864
|
-
"span",
|
|
16865
|
-
|
|
16866
|
-
|
|
16867
|
-
|
|
16868
|
-
|
|
16869
|
-
|
|
16870
|
-
|
|
16871
|
-
|
|
16872
|
-
|
|
16873
|
-
|
|
16874
|
-
|
|
16875
|
-
|
|
16876
|
-
|
|
16877
|
-
|
|
16878
|
-
|
|
16879
|
-
|
|
16880
|
-
|
|
16881
|
-
|
|
16882
|
-
|
|
16883
|
-
|
|
16937
|
+
slides.map((s, i) => {
|
|
16938
|
+
const rowBusy = building || rowBusyIndex === i;
|
|
16939
|
+
return /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
16940
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)("span", { className: "text-xs text-zinc-600 w-5 text-right tabular-nums flex-shrink-0", children: s.n ?? i + 1 }),
|
|
16941
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsxs)(
|
|
16942
|
+
"span",
|
|
16943
|
+
{
|
|
16944
|
+
className: "flex items-center gap-1 text-[10px] uppercase tracking-wide text-zinc-500 w-20 flex-shrink-0 truncate",
|
|
16945
|
+
title: s.type,
|
|
16946
|
+
children: [
|
|
16947
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(SlideTypeIcon, {}),
|
|
16948
|
+
s.type || "slide"
|
|
16949
|
+
]
|
|
16950
|
+
}
|
|
16951
|
+
),
|
|
16952
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(
|
|
16953
|
+
"input",
|
|
16954
|
+
{
|
|
16955
|
+
value: messageEdits[i] ?? s.message ?? "",
|
|
16956
|
+
onChange: (e) => setMessageEdits((prev) => ({ ...prev, [i]: e.target.value })),
|
|
16957
|
+
disabled: building,
|
|
16958
|
+
className: "flex-1 min-w-0 bg-zinc-800/60 border border-zinc-700 rounded-lg px-3 h-8 text-xs text-zinc-200 focus:border-indigo-500 outline-none disabled:opacity-60"
|
|
16959
|
+
}
|
|
16960
|
+
),
|
|
16961
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(
|
|
16962
|
+
"button",
|
|
16963
|
+
{
|
|
16964
|
+
type: "button",
|
|
16965
|
+
onClick: () => moveOutlineSlide(i, -1),
|
|
16966
|
+
disabled: rowBusy || i === 0,
|
|
16967
|
+
title: "Move up",
|
|
16968
|
+
className: "w-7 h-7 flex items-center justify-center rounded-md border border-zinc-700 text-zinc-400 hover:text-zinc-100 hover:border-zinc-500 disabled:opacity-30 disabled:hover:text-zinc-400 disabled:hover:border-zinc-700 flex-shrink-0",
|
|
16969
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime147.jsx)(ChevronUpIcon, {})
|
|
16970
|
+
}
|
|
16971
|
+
),
|
|
16972
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(
|
|
16973
|
+
"button",
|
|
16974
|
+
{
|
|
16975
|
+
type: "button",
|
|
16976
|
+
onClick: () => moveOutlineSlide(i, 1),
|
|
16977
|
+
disabled: rowBusy || i === slides.length - 1,
|
|
16978
|
+
title: "Move down",
|
|
16979
|
+
className: "w-7 h-7 flex items-center justify-center rounded-md border border-zinc-700 text-zinc-400 hover:text-zinc-100 hover:border-zinc-500 disabled:opacity-30 disabled:hover:text-zinc-400 disabled:hover:border-zinc-700 flex-shrink-0",
|
|
16980
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime147.jsx)(ChevronDownIcon, {})
|
|
16981
|
+
}
|
|
16982
|
+
),
|
|
16983
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(
|
|
16984
|
+
"button",
|
|
16985
|
+
{
|
|
16986
|
+
type: "button",
|
|
16987
|
+
onClick: () => toggleSlideApproved(i),
|
|
16988
|
+
disabled: rowBusy,
|
|
16989
|
+
title: s.approved ? "Marked reviewed" : "Mark as reviewed",
|
|
16990
|
+
className: cn(
|
|
16991
|
+
"w-7 h-7 flex items-center justify-center rounded-md border flex-shrink-0 disabled:opacity-40",
|
|
16992
|
+
s.approved ? "border-emerald-600/50 bg-emerald-950/40 text-emerald-300" : "border-zinc-700 text-zinc-500 hover:text-zinc-300 hover:border-zinc-500"
|
|
16993
|
+
),
|
|
16994
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime147.jsx)(CheckIcon, {})
|
|
16995
|
+
}
|
|
16996
|
+
)
|
|
16997
|
+
] }, i);
|
|
16998
|
+
}),
|
|
16884
16999
|
slides.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("p", { className: "text-xs text-zinc-500", children: "No outline slides were returned; you can still build the deck." })
|
|
16885
17000
|
] }),
|
|
17001
|
+
rowError && /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("p", { className: "mt-2 text-xs text-red-400/90", children: rowError }),
|
|
16886
17002
|
approveError && /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
|
|
16887
17003
|
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)(
|
|
16888
17004
|
"button",
|
|
@@ -17132,6 +17248,8 @@ var ShareIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("svg", {
|
|
|
17132
17248
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
17133
17249
|
] });
|
|
17134
17250
|
var CheckIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("polyline", { points: "20 6 9 17 4 12" }) });
|
|
17251
|
+
var ChevronUpIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("polyline", { points: "18 15 12 9 6 15" }) });
|
|
17252
|
+
var ChevronDownIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("polyline", { points: "6 9 12 15 18 9" }) });
|
|
17135
17253
|
var RefreshIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
17136
17254
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)("polyline", { points: "23 4 23 10 17 10" }),
|
|
17137
17255
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
|
|
@@ -17200,6 +17318,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
17200
17318
|
executive_summary: initialSummary,
|
|
17201
17319
|
key_findings: _initialFindings,
|
|
17202
17320
|
html_url: initialHtmlUrl,
|
|
17321
|
+
pdf_url: initialPdfUrl,
|
|
17203
17322
|
generation_mode: initialGenerationMode,
|
|
17204
17323
|
template_id: initialTemplateId,
|
|
17205
17324
|
template_version_id: initialTemplateVersionId,
|
|
@@ -17213,6 +17332,8 @@ var ResearchReportJobCard = (props) => {
|
|
|
17213
17332
|
shareUrl,
|
|
17214
17333
|
approveUrl,
|
|
17215
17334
|
approveOutlineUrl,
|
|
17335
|
+
reorderSectionsUrl,
|
|
17336
|
+
approveSectionUrl,
|
|
17216
17337
|
regenerateUrl,
|
|
17217
17338
|
hideApproval = false,
|
|
17218
17339
|
className,
|
|
@@ -17229,6 +17350,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
17229
17350
|
const [wordCount, setWordCount] = (0, import_react79.useState)(initialWordCount ?? 0);
|
|
17230
17351
|
const [summary, setSummary] = (0, import_react79.useState)(initialSummary || "");
|
|
17231
17352
|
const [htmlUrl, setHtmlUrl] = (0, import_react79.useState)(initialHtmlUrl || "");
|
|
17353
|
+
const [pdfUrl, setPdfUrl] = (0, import_react79.useState)(initialPdfUrl || "");
|
|
17232
17354
|
const [generationMode, setGenerationMode] = (0, import_react79.useState)(
|
|
17233
17355
|
initialGenerationMode || (initialHtmlUrl ? "template" : "")
|
|
17234
17356
|
);
|
|
@@ -17249,6 +17371,8 @@ var ResearchReportJobCard = (props) => {
|
|
|
17249
17371
|
const [approveError, setApproveError] = (0, import_react79.useState)(null);
|
|
17250
17372
|
const [headingEdits, setHeadingEdits] = (0, import_react79.useState)({});
|
|
17251
17373
|
const [approvingOutline, setApprovingOutline] = (0, import_react79.useState)(false);
|
|
17374
|
+
const [rowBusyIndex, setRowBusyIndex] = (0, import_react79.useState)(null);
|
|
17375
|
+
const [rowError, setRowError] = (0, import_react79.useState)(null);
|
|
17252
17376
|
const [outlineWritePollUrl, setOutlineWritePollUrl] = (0, import_react79.useState)(null);
|
|
17253
17377
|
const [regenPollUrl, setRegenPollUrl] = (0, import_react79.useState)(null);
|
|
17254
17378
|
const previewRef = (0, import_react79.useRef)(null);
|
|
@@ -17356,6 +17480,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
17356
17480
|
setWordCount(output.word_count || 0);
|
|
17357
17481
|
setSummary(output.executive_summary || "");
|
|
17358
17482
|
setHtmlUrl(output.html_url || "");
|
|
17483
|
+
setPdfUrl(output.pdf_url || "");
|
|
17359
17484
|
if (output.generation_mode) setGenerationMode(output.generation_mode);
|
|
17360
17485
|
if (output.template_id) setTemplateId(output.template_id);
|
|
17361
17486
|
if (output.template_version_id) setTemplateVersionId(output.template_version_id);
|
|
@@ -17383,6 +17508,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
17383
17508
|
const applyRegeneratedOutput = (0, import_react79.useCallback)(
|
|
17384
17509
|
(out) => {
|
|
17385
17510
|
if (out.html_url) setHtmlUrl(out.html_url);
|
|
17511
|
+
setPdfUrl(out.pdf_url || "");
|
|
17386
17512
|
if (out.template_id) setTemplateId(out.template_id);
|
|
17387
17513
|
setReviewStatus("pending_review");
|
|
17388
17514
|
setTemplateVersionId("");
|
|
@@ -17399,6 +17525,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
17399
17525
|
word_count: out.word_count ?? wordCount,
|
|
17400
17526
|
executive_summary: out.executive_summary ?? summary,
|
|
17401
17527
|
html_url: out.html_url ?? htmlUrl,
|
|
17528
|
+
pdf_url: out.pdf_url ?? pdfUrl,
|
|
17402
17529
|
generation_mode: "template",
|
|
17403
17530
|
template_id: out.template_id ?? templateId,
|
|
17404
17531
|
template_version_id: void 0,
|
|
@@ -17406,7 +17533,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
17406
17533
|
theme: out.theme ?? theme
|
|
17407
17534
|
});
|
|
17408
17535
|
},
|
|
17409
|
-
[title, depth, sectionCount, sourceCount, wordCount, summary, htmlUrl, templateId, theme]
|
|
17536
|
+
[title, depth, sectionCount, sourceCount, wordCount, summary, htmlUrl, pdfUrl, templateId, theme]
|
|
17410
17537
|
);
|
|
17411
17538
|
useSharedPoll(
|
|
17412
17539
|
{
|
|
@@ -17450,6 +17577,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
17450
17577
|
if (out.word_count !== void 0) setWordCount(out.word_count);
|
|
17451
17578
|
if (out.executive_summary) setSummary(out.executive_summary);
|
|
17452
17579
|
if (out.html_url) setHtmlUrl(out.html_url);
|
|
17580
|
+
setPdfUrl(out.pdf_url || "");
|
|
17453
17581
|
if (out.generation_mode) setGenerationMode(out.generation_mode);
|
|
17454
17582
|
if (out.template_id) setTemplateId(out.template_id);
|
|
17455
17583
|
setTemplateVersionId(out.template_version_id || "");
|
|
@@ -17500,6 +17628,76 @@ var ResearchReportJobCard = (props) => {
|
|
|
17500
17628
|
});
|
|
17501
17629
|
return changed ? { ...outline, sections } : void 0;
|
|
17502
17630
|
};
|
|
17631
|
+
const moveOutlineSection = async (index, delta) => {
|
|
17632
|
+
if (!outline || !Array.isArray(outline.sections) || rowBusyIndex !== null) return;
|
|
17633
|
+
const sections = outline.sections;
|
|
17634
|
+
const target = index + delta;
|
|
17635
|
+
if (target < 0 || target >= sections.length) return;
|
|
17636
|
+
const order = sections.map((_, i) => i);
|
|
17637
|
+
[order[index], order[target]] = [order[target], order[index]];
|
|
17638
|
+
const reordered = order.map((i) => sections[i]);
|
|
17639
|
+
const previousOutline = outline;
|
|
17640
|
+
const previousEdits = headingEdits;
|
|
17641
|
+
setOutline({ ...outline, sections: reordered });
|
|
17642
|
+
setHeadingEdits((prev) => {
|
|
17643
|
+
const remapped = {};
|
|
17644
|
+
order.forEach((originalIndex, newIndex) => {
|
|
17645
|
+
if (prev[originalIndex] !== void 0) remapped[newIndex] = prev[originalIndex];
|
|
17646
|
+
});
|
|
17647
|
+
return remapped;
|
|
17648
|
+
});
|
|
17649
|
+
setRowBusyIndex(index);
|
|
17650
|
+
setRowError(null);
|
|
17651
|
+
try {
|
|
17652
|
+
const endpoint = reorderSectionsUrl || `/api/reports/${job_id}/outline/reorder-sections`;
|
|
17653
|
+
const headers = { "Content-Type": "application/json" };
|
|
17654
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
17655
|
+
const res = await fetch(endpoint, {
|
|
17656
|
+
method: "POST",
|
|
17657
|
+
headers,
|
|
17658
|
+
body: JSON.stringify({ order })
|
|
17659
|
+
});
|
|
17660
|
+
if (!res.ok) {
|
|
17661
|
+
const err = await res.json().catch(() => ({}));
|
|
17662
|
+
throw new Error(err.detail || err.error || `Reorder failed (${res.status})`);
|
|
17663
|
+
}
|
|
17664
|
+
} catch (e) {
|
|
17665
|
+
setOutline(previousOutline);
|
|
17666
|
+
setHeadingEdits(previousEdits);
|
|
17667
|
+
setRowError(e instanceof Error ? e.message : "Could not reorder sections");
|
|
17668
|
+
} finally {
|
|
17669
|
+
setRowBusyIndex(null);
|
|
17670
|
+
}
|
|
17671
|
+
};
|
|
17672
|
+
const toggleSectionApproved = async (index) => {
|
|
17673
|
+
if (!outline || !Array.isArray(outline.sections) || rowBusyIndex !== null) return;
|
|
17674
|
+
const sections = outline.sections;
|
|
17675
|
+
const nextApproved = !sections[index]?.approved;
|
|
17676
|
+
const previousOutline = outline;
|
|
17677
|
+
const updated = sections.map((s, i) => i === index ? { ...s, approved: nextApproved } : s);
|
|
17678
|
+
setOutline({ ...outline, sections: updated });
|
|
17679
|
+
setRowBusyIndex(index);
|
|
17680
|
+
setRowError(null);
|
|
17681
|
+
try {
|
|
17682
|
+
const endpoint = approveSectionUrl || `/api/reports/${job_id}/outline/approve-section`;
|
|
17683
|
+
const headers = { "Content-Type": "application/json" };
|
|
17684
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
17685
|
+
const res = await fetch(endpoint, {
|
|
17686
|
+
method: "POST",
|
|
17687
|
+
headers,
|
|
17688
|
+
body: JSON.stringify({ section_index: index, approved: nextApproved })
|
|
17689
|
+
});
|
|
17690
|
+
if (!res.ok) {
|
|
17691
|
+
const err = await res.json().catch(() => ({}));
|
|
17692
|
+
throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
|
|
17693
|
+
}
|
|
17694
|
+
} catch (e) {
|
|
17695
|
+
setOutline(previousOutline);
|
|
17696
|
+
setRowError(e instanceof Error ? e.message : "Could not update section");
|
|
17697
|
+
} finally {
|
|
17698
|
+
setRowBusyIndex(null);
|
|
17699
|
+
}
|
|
17700
|
+
};
|
|
17503
17701
|
const handleApproveOutline = async () => {
|
|
17504
17702
|
if (!job_id || approvingOutline) return;
|
|
17505
17703
|
const endpoint = approveOutlineUrl || `/api/reports/${job_id}/approve-outline`;
|
|
@@ -17659,20 +17857,60 @@ var ResearchReportJobCard = (props) => {
|
|
|
17659
17857
|
] })
|
|
17660
17858
|
] }),
|
|
17661
17859
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "mt-4 space-y-2", children: [
|
|
17662
|
-
sections.map((s, i) =>
|
|
17663
|
-
|
|
17664
|
-
/* @__PURE__ */ (0, import_jsx_runtime148.
|
|
17665
|
-
"
|
|
17666
|
-
|
|
17667
|
-
|
|
17668
|
-
|
|
17669
|
-
|
|
17670
|
-
|
|
17671
|
-
|
|
17672
|
-
|
|
17673
|
-
|
|
17860
|
+
sections.map((s, i) => {
|
|
17861
|
+
const rowBusy = building || rowBusyIndex === i;
|
|
17862
|
+
return /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
17863
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { className: "text-xs text-zinc-600 w-5 text-right tabular-nums flex-shrink-0", children: i + 1 }),
|
|
17864
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
|
|
17865
|
+
"input",
|
|
17866
|
+
{
|
|
17867
|
+
value: headingEdits[i] ?? s.heading ?? "",
|
|
17868
|
+
onChange: (e) => setHeadingEdits((prev) => ({ ...prev, [i]: e.target.value })),
|
|
17869
|
+
disabled: building,
|
|
17870
|
+
className: "flex-1 min-w-0 bg-zinc-800/60 border border-zinc-700 rounded-lg px-3 h-8 text-xs text-zinc-200 focus:border-violet-500 outline-none disabled:opacity-60"
|
|
17871
|
+
}
|
|
17872
|
+
),
|
|
17873
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
|
|
17874
|
+
"button",
|
|
17875
|
+
{
|
|
17876
|
+
type: "button",
|
|
17877
|
+
onClick: () => moveOutlineSection(i, -1),
|
|
17878
|
+
disabled: rowBusy || i === 0,
|
|
17879
|
+
title: "Move up",
|
|
17880
|
+
className: "w-7 h-7 flex items-center justify-center rounded-md border border-zinc-700 text-zinc-400 hover:text-zinc-100 hover:border-zinc-500 disabled:opacity-30 disabled:hover:text-zinc-400 disabled:hover:border-zinc-700 flex-shrink-0",
|
|
17881
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(ChevronUpIcon2, {})
|
|
17882
|
+
}
|
|
17883
|
+
),
|
|
17884
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
|
|
17885
|
+
"button",
|
|
17886
|
+
{
|
|
17887
|
+
type: "button",
|
|
17888
|
+
onClick: () => moveOutlineSection(i, 1),
|
|
17889
|
+
disabled: rowBusy || i === sections.length - 1,
|
|
17890
|
+
title: "Move down",
|
|
17891
|
+
className: "w-7 h-7 flex items-center justify-center rounded-md border border-zinc-700 text-zinc-400 hover:text-zinc-100 hover:border-zinc-500 disabled:opacity-30 disabled:hover:text-zinc-400 disabled:hover:border-zinc-700 flex-shrink-0",
|
|
17892
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(ChevronDownIcon2, {})
|
|
17893
|
+
}
|
|
17894
|
+
),
|
|
17895
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
|
|
17896
|
+
"button",
|
|
17897
|
+
{
|
|
17898
|
+
type: "button",
|
|
17899
|
+
onClick: () => toggleSectionApproved(i),
|
|
17900
|
+
disabled: rowBusy,
|
|
17901
|
+
title: s.approved ? "Marked reviewed" : "Mark as reviewed",
|
|
17902
|
+
className: cn(
|
|
17903
|
+
"w-7 h-7 flex items-center justify-center rounded-md border flex-shrink-0 disabled:opacity-40",
|
|
17904
|
+
s.approved ? "border-emerald-600/50 bg-emerald-950/40 text-emerald-300" : "border-zinc-700 text-zinc-500 hover:text-zinc-300 hover:border-zinc-500"
|
|
17905
|
+
),
|
|
17906
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(CheckIcon2, {})
|
|
17907
|
+
}
|
|
17908
|
+
)
|
|
17909
|
+
] }, i);
|
|
17910
|
+
}),
|
|
17674
17911
|
sections.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: "text-xs text-zinc-500", children: "No outline sections were returned; you can still build the report." })
|
|
17675
17912
|
] }),
|
|
17913
|
+
rowError && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: "mt-2 text-xs text-red-400/90", children: rowError }),
|
|
17676
17914
|
approveError && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
|
|
17677
17915
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(
|
|
17678
17916
|
"button",
|
|
@@ -17735,6 +17973,26 @@ var ResearchReportJobCard = (props) => {
|
|
|
17735
17973
|
window.open(htmlUrl, "_blank", "noopener,noreferrer");
|
|
17736
17974
|
}
|
|
17737
17975
|
};
|
|
17976
|
+
const handleDownloadPdf = async () => {
|
|
17977
|
+
if (!pdfUrl) return;
|
|
17978
|
+
const filename = `${(title ?? "").replace(/[^a-z0-9]/gi, "-").toLowerCase()}.pdf`;
|
|
17979
|
+
const href = `/api/download?url=${encodeURIComponent(pdfUrl)}&filename=${encodeURIComponent(filename)}`;
|
|
17980
|
+
try {
|
|
17981
|
+
const response = await fetch(href);
|
|
17982
|
+
if (!response.ok) throw new Error(`status ${response.status}`);
|
|
17983
|
+
const blob = await response.blob();
|
|
17984
|
+
const objectUrl = window.URL.createObjectURL(blob);
|
|
17985
|
+
const anchor = document.createElement("a");
|
|
17986
|
+
anchor.href = objectUrl;
|
|
17987
|
+
anchor.download = filename;
|
|
17988
|
+
document.body.appendChild(anchor);
|
|
17989
|
+
anchor.click();
|
|
17990
|
+
anchor.remove();
|
|
17991
|
+
window.URL.revokeObjectURL(objectUrl);
|
|
17992
|
+
} catch {
|
|
17993
|
+
window.open(pdfUrl, "_blank", "noopener,noreferrer");
|
|
17994
|
+
}
|
|
17995
|
+
};
|
|
17738
17996
|
const resolveShareLink = () => {
|
|
17739
17997
|
if (shareUrl) return shareUrl;
|
|
17740
17998
|
if (typeof window !== "undefined" && job_id) {
|
|
@@ -17787,6 +18045,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
17787
18045
|
word_count: wordCount,
|
|
17788
18046
|
executive_summary: summary,
|
|
17789
18047
|
html_url: htmlUrl,
|
|
18048
|
+
pdf_url: pdfUrl,
|
|
17790
18049
|
generation_mode: generationMode,
|
|
17791
18050
|
template_id: data.template_id || templateId,
|
|
17792
18051
|
template_version_id: data.template_version_id || templateVersionId,
|
|
@@ -17924,6 +18183,18 @@ var ResearchReportJobCard = (props) => {
|
|
|
17924
18183
|
]
|
|
17925
18184
|
}
|
|
17926
18185
|
),
|
|
18186
|
+
pdfUrl && /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(
|
|
18187
|
+
"button",
|
|
18188
|
+
{
|
|
18189
|
+
onClick: handleDownloadPdf,
|
|
18190
|
+
title: "Download PDF",
|
|
18191
|
+
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",
|
|
18192
|
+
children: [
|
|
18193
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)(DownloadIcon2, {}),
|
|
18194
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)("span", { children: "PDF" })
|
|
18195
|
+
]
|
|
18196
|
+
}
|
|
18197
|
+
),
|
|
17927
18198
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(
|
|
17928
18199
|
"button",
|
|
17929
18200
|
{
|