pxengine 0.1.115 → 0.1.117
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 +346 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.mjs +346 -35
- package/dist/index.mjs.map +1 -1
- package/dist/registry.json +31 -1
- package/package.json +3 -3
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,10 @@ var PresentationJobCard = ({
|
|
|
16358
16360
|
shareUrl,
|
|
16359
16361
|
approveUrl,
|
|
16360
16362
|
approveOutlineUrl,
|
|
16363
|
+
reorderSlidesUrl,
|
|
16364
|
+
approveSlideUrl,
|
|
16365
|
+
setSlideTemplateUrl,
|
|
16366
|
+
templatesUrl,
|
|
16361
16367
|
regenerateUrl,
|
|
16362
16368
|
hideApproval = false,
|
|
16363
16369
|
className,
|
|
@@ -16380,6 +16386,7 @@ var PresentationJobCard = ({
|
|
|
16380
16386
|
const [, setTemplateVersionId] = (0, import_react78.useState)(initialTemplateVersionId || "");
|
|
16381
16387
|
const [reviewStatus, setReviewStatus] = (0, import_react78.useState)(initialReviewStatus || "");
|
|
16382
16388
|
const [outline, setOutline] = (0, import_react78.useState)(initialOutline);
|
|
16389
|
+
const [slideTemplateOptions, setSlideTemplateOptions] = (0, import_react78.useState)(null);
|
|
16383
16390
|
const [showExport, setShowExport] = (0, import_react78.useState)(false);
|
|
16384
16391
|
const [showFullscreen, setShowFullscreen] = (0, import_react78.useState)(false);
|
|
16385
16392
|
const [copied, setCopied] = (0, import_react78.useState)(false);
|
|
@@ -16389,6 +16396,8 @@ var PresentationJobCard = ({
|
|
|
16389
16396
|
const [messageEdits, setMessageEdits] = (0, import_react78.useState)({});
|
|
16390
16397
|
const [approvingOutline, setApprovingOutline] = (0, import_react78.useState)(false);
|
|
16391
16398
|
const [outlineWritePollUrl, setOutlineWritePollUrl] = (0, import_react78.useState)(null);
|
|
16399
|
+
const [rowBusyIndex, setRowBusyIndex] = (0, import_react78.useState)(null);
|
|
16400
|
+
const [rowError, setRowError] = (0, import_react78.useState)(null);
|
|
16392
16401
|
const [regenPollUrl, setRegenPollUrl] = (0, import_react78.useState)(null);
|
|
16393
16402
|
const [currentSlide, setCurrentSlide] = (0, import_react78.useState)(1);
|
|
16394
16403
|
const [previewScale, setPreviewScale] = (0, import_react78.useState)(1);
|
|
@@ -16432,6 +16441,33 @@ var PresentationJobCard = ({
|
|
|
16432
16441
|
(0, import_react78.useEffect)(() => {
|
|
16433
16442
|
if (initialOutline) setOutline(initialOutline);
|
|
16434
16443
|
}, [initialOutlineSlideCount]);
|
|
16444
|
+
(0, import_react78.useEffect)(() => {
|
|
16445
|
+
if (reviewStatus !== "pending_outline_approval" || slideTemplateOptions !== null) return;
|
|
16446
|
+
let cancelled = false;
|
|
16447
|
+
(async () => {
|
|
16448
|
+
try {
|
|
16449
|
+
const endpoint = templatesUrl || "/api/presentations/templates";
|
|
16450
|
+
const headers = {};
|
|
16451
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
16452
|
+
const res = await fetch(endpoint, { headers });
|
|
16453
|
+
if (!res.ok) throw new Error(`Templates fetch failed (${res.status})`);
|
|
16454
|
+
const list = await res.json();
|
|
16455
|
+
if (!cancelled && Array.isArray(list)) {
|
|
16456
|
+
setSlideTemplateOptions(
|
|
16457
|
+
list.map((t2) => ({
|
|
16458
|
+
id: t2.template_id || t2.id || "",
|
|
16459
|
+
name: t2.name || t2.template_id || t2.id || ""
|
|
16460
|
+
}))
|
|
16461
|
+
);
|
|
16462
|
+
}
|
|
16463
|
+
} catch {
|
|
16464
|
+
if (!cancelled) setSlideTemplateOptions([]);
|
|
16465
|
+
}
|
|
16466
|
+
})();
|
|
16467
|
+
return () => {
|
|
16468
|
+
cancelled = true;
|
|
16469
|
+
};
|
|
16470
|
+
}, [reviewStatus, slideTemplateOptions, templatesUrl, authToken]);
|
|
16435
16471
|
const updateScale = (0, import_react78.useCallback)(() => {
|
|
16436
16472
|
if (previewRef.current) setPreviewScale(previewRef.current.offsetWidth / 1280);
|
|
16437
16473
|
}, []);
|
|
@@ -16621,6 +16657,111 @@ var PresentationJobCard = ({
|
|
|
16621
16657
|
});
|
|
16622
16658
|
return changed ? { ...outline, slides } : void 0;
|
|
16623
16659
|
};
|
|
16660
|
+
const moveOutlineSlide = async (index, delta) => {
|
|
16661
|
+
if (!outline || !Array.isArray(outline.slides) || rowBusyIndex !== null) return;
|
|
16662
|
+
const slides = outline.slides;
|
|
16663
|
+
const target = index + delta;
|
|
16664
|
+
if (target < 0 || target >= slides.length) return;
|
|
16665
|
+
const order = slides.map((_, i) => i);
|
|
16666
|
+
[order[index], order[target]] = [order[target], order[index]];
|
|
16667
|
+
const reordered = order.map((i, newIndex) => ({ ...slides[i], n: newIndex + 1 }));
|
|
16668
|
+
const previousOutline = outline;
|
|
16669
|
+
const previousEdits = messageEdits;
|
|
16670
|
+
setOutline({ ...outline, slides: reordered });
|
|
16671
|
+
setMessageEdits((prev) => {
|
|
16672
|
+
const remapped = {};
|
|
16673
|
+
order.forEach((originalIndex, newIndex) => {
|
|
16674
|
+
if (prev[originalIndex] !== void 0) remapped[newIndex] = prev[originalIndex];
|
|
16675
|
+
});
|
|
16676
|
+
return remapped;
|
|
16677
|
+
});
|
|
16678
|
+
setRowBusyIndex(index);
|
|
16679
|
+
setRowError(null);
|
|
16680
|
+
try {
|
|
16681
|
+
const endpoint = reorderSlidesUrl || `/api/presentations/${_job_id}/outline/reorder-slides`;
|
|
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({ order })
|
|
16688
|
+
});
|
|
16689
|
+
if (!res.ok) {
|
|
16690
|
+
const err = await res.json().catch(() => ({}));
|
|
16691
|
+
throw new Error(err.detail || err.error || `Reorder failed (${res.status})`);
|
|
16692
|
+
}
|
|
16693
|
+
} catch (e) {
|
|
16694
|
+
setOutline(previousOutline);
|
|
16695
|
+
setMessageEdits(previousEdits);
|
|
16696
|
+
setRowError(e instanceof Error ? e.message : "Could not reorder slides");
|
|
16697
|
+
} finally {
|
|
16698
|
+
setRowBusyIndex(null);
|
|
16699
|
+
}
|
|
16700
|
+
};
|
|
16701
|
+
const toggleSlideApproved = async (index) => {
|
|
16702
|
+
if (!outline || !Array.isArray(outline.slides) || rowBusyIndex !== null) return;
|
|
16703
|
+
const slides = outline.slides;
|
|
16704
|
+
const nextApproved = !slides[index]?.approved;
|
|
16705
|
+
const previousOutline = outline;
|
|
16706
|
+
const updated = slides.map((s, i) => i === index ? { ...s, approved: nextApproved } : s);
|
|
16707
|
+
setOutline({ ...outline, slides: updated });
|
|
16708
|
+
setRowBusyIndex(index);
|
|
16709
|
+
setRowError(null);
|
|
16710
|
+
try {
|
|
16711
|
+
const endpoint = approveSlideUrl || `/api/presentations/${_job_id}/outline/approve-slide`;
|
|
16712
|
+
const headers = { "Content-Type": "application/json" };
|
|
16713
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
16714
|
+
const res = await fetch(endpoint, {
|
|
16715
|
+
method: "POST",
|
|
16716
|
+
headers,
|
|
16717
|
+
body: JSON.stringify({ slide_index: index, approved: nextApproved })
|
|
16718
|
+
});
|
|
16719
|
+
if (!res.ok) {
|
|
16720
|
+
const err = await res.json().catch(() => ({}));
|
|
16721
|
+
throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
|
|
16722
|
+
}
|
|
16723
|
+
} catch (e) {
|
|
16724
|
+
setOutline(previousOutline);
|
|
16725
|
+
setRowError(e instanceof Error ? e.message : "Could not update slide");
|
|
16726
|
+
} finally {
|
|
16727
|
+
setRowBusyIndex(null);
|
|
16728
|
+
}
|
|
16729
|
+
};
|
|
16730
|
+
const setSlideTemplate = async (index, templateId2) => {
|
|
16731
|
+
if (!outline || !Array.isArray(outline.slides) || rowBusyIndex !== null) return;
|
|
16732
|
+
const slides = outline.slides;
|
|
16733
|
+
const previousOutline = outline;
|
|
16734
|
+
const updated = slides.map((s, i) => {
|
|
16735
|
+
if (i !== index) return s;
|
|
16736
|
+
if (!templateId2) {
|
|
16737
|
+
const { template_id: _drop, ...rest } = s;
|
|
16738
|
+
return rest;
|
|
16739
|
+
}
|
|
16740
|
+
return { ...s, template_id: templateId2 };
|
|
16741
|
+
});
|
|
16742
|
+
setOutline({ ...outline, slides: updated });
|
|
16743
|
+
setRowBusyIndex(index);
|
|
16744
|
+
setRowError(null);
|
|
16745
|
+
try {
|
|
16746
|
+
const endpoint = setSlideTemplateUrl || `/api/presentations/${_job_id}/outline/set-slide-template`;
|
|
16747
|
+
const headers = { "Content-Type": "application/json" };
|
|
16748
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
16749
|
+
const res = await fetch(endpoint, {
|
|
16750
|
+
method: "POST",
|
|
16751
|
+
headers,
|
|
16752
|
+
body: JSON.stringify({ slide_index: index, template_id: templateId2 || null })
|
|
16753
|
+
});
|
|
16754
|
+
if (!res.ok) {
|
|
16755
|
+
const err = await res.json().catch(() => ({}));
|
|
16756
|
+
throw new Error(err.detail || err.error || `Template update failed (${res.status})`);
|
|
16757
|
+
}
|
|
16758
|
+
} catch (e) {
|
|
16759
|
+
setOutline(previousOutline);
|
|
16760
|
+
setRowError(e instanceof Error ? e.message : "Could not update slide's template");
|
|
16761
|
+
} finally {
|
|
16762
|
+
setRowBusyIndex(null);
|
|
16763
|
+
}
|
|
16764
|
+
};
|
|
16624
16765
|
const handleApproveOutline = async () => {
|
|
16625
16766
|
if (!_job_id || approvingOutline) return;
|
|
16626
16767
|
const endpoint = approveOutlineUrl || `/api/presentations/${_job_id}/approve-outline`;
|
|
@@ -16858,31 +16999,85 @@ var PresentationJobCard = ({
|
|
|
16858
16999
|
] })
|
|
16859
17000
|
] }),
|
|
16860
17001
|
/* @__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
|
-
|
|
17002
|
+
slides.map((s, i) => {
|
|
17003
|
+
const rowBusy = building || rowBusyIndex === i;
|
|
17004
|
+
return /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
17005
|
+
/* @__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 }),
|
|
17006
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsxs)(
|
|
17007
|
+
"span",
|
|
17008
|
+
{
|
|
17009
|
+
className: "flex items-center gap-1 text-[10px] uppercase tracking-wide text-zinc-500 w-20 flex-shrink-0 truncate",
|
|
17010
|
+
title: s.type,
|
|
17011
|
+
children: [
|
|
17012
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(SlideTypeIcon, {}),
|
|
17013
|
+
s.type || "slide"
|
|
17014
|
+
]
|
|
17015
|
+
}
|
|
17016
|
+
),
|
|
17017
|
+
slideTemplateOptions && slideTemplateOptions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)(
|
|
17018
|
+
"select",
|
|
17019
|
+
{
|
|
17020
|
+
value: s.template_id || "",
|
|
17021
|
+
onChange: (e) => setSlideTemplate(i, e.target.value),
|
|
17022
|
+
disabled: rowBusy,
|
|
17023
|
+
title: "Design template for this slide only (default: deck's template)",
|
|
17024
|
+
className: "w-24 flex-shrink-0 bg-zinc-800/60 border border-zinc-700 rounded-lg px-1.5 h-8 text-[10px] text-zinc-300 focus:border-indigo-500 outline-none disabled:opacity-60",
|
|
17025
|
+
children: [
|
|
17026
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)("option", { value: "", children: "Deck default" }),
|
|
17027
|
+
slideTemplateOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("option", { value: opt.id, children: opt.name }, opt.id))
|
|
17028
|
+
]
|
|
17029
|
+
}
|
|
17030
|
+
),
|
|
17031
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(
|
|
17032
|
+
"input",
|
|
17033
|
+
{
|
|
17034
|
+
value: messageEdits[i] ?? s.message ?? "",
|
|
17035
|
+
onChange: (e) => setMessageEdits((prev) => ({ ...prev, [i]: e.target.value })),
|
|
17036
|
+
disabled: building,
|
|
17037
|
+
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"
|
|
17038
|
+
}
|
|
17039
|
+
),
|
|
17040
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(
|
|
17041
|
+
"button",
|
|
17042
|
+
{
|
|
17043
|
+
type: "button",
|
|
17044
|
+
onClick: () => moveOutlineSlide(i, -1),
|
|
17045
|
+
disabled: rowBusy || i === 0,
|
|
17046
|
+
title: "Move up",
|
|
17047
|
+
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",
|
|
17048
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime147.jsx)(ChevronUpIcon, {})
|
|
17049
|
+
}
|
|
17050
|
+
),
|
|
17051
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(
|
|
17052
|
+
"button",
|
|
17053
|
+
{
|
|
17054
|
+
type: "button",
|
|
17055
|
+
onClick: () => moveOutlineSlide(i, 1),
|
|
17056
|
+
disabled: rowBusy || i === slides.length - 1,
|
|
17057
|
+
title: "Move down",
|
|
17058
|
+
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",
|
|
17059
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime147.jsx)(ChevronDownIcon, {})
|
|
17060
|
+
}
|
|
17061
|
+
),
|
|
17062
|
+
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)(
|
|
17063
|
+
"button",
|
|
17064
|
+
{
|
|
17065
|
+
type: "button",
|
|
17066
|
+
onClick: () => toggleSlideApproved(i),
|
|
17067
|
+
disabled: rowBusy,
|
|
17068
|
+
title: s.approved ? "Marked reviewed" : "Mark as reviewed",
|
|
17069
|
+
className: cn(
|
|
17070
|
+
"w-7 h-7 flex items-center justify-center rounded-md border flex-shrink-0 disabled:opacity-40",
|
|
17071
|
+
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"
|
|
17072
|
+
),
|
|
17073
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime147.jsx)(CheckIcon, {})
|
|
17074
|
+
}
|
|
17075
|
+
)
|
|
17076
|
+
] }, i);
|
|
17077
|
+
}),
|
|
16884
17078
|
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
17079
|
] }),
|
|
17080
|
+
rowError && /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("p", { className: "mt-2 text-xs text-red-400/90", children: rowError }),
|
|
16886
17081
|
approveError && /* @__PURE__ */ (0, import_jsx_runtime147.jsx)("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
|
|
16887
17082
|
/* @__PURE__ */ (0, import_jsx_runtime147.jsx)("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime147.jsxs)(
|
|
16888
17083
|
"button",
|
|
@@ -17132,6 +17327,8 @@ var ShareIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("svg", {
|
|
|
17132
17327
|
/* @__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
17328
|
] });
|
|
17134
17329
|
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" }) });
|
|
17330
|
+
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" }) });
|
|
17331
|
+
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
17332
|
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
17333
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)("polyline", { points: "23 4 23 10 17 10" }),
|
|
17137
17334
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
|
|
@@ -17214,6 +17411,8 @@ var ResearchReportJobCard = (props) => {
|
|
|
17214
17411
|
shareUrl,
|
|
17215
17412
|
approveUrl,
|
|
17216
17413
|
approveOutlineUrl,
|
|
17414
|
+
reorderSectionsUrl,
|
|
17415
|
+
approveSectionUrl,
|
|
17217
17416
|
regenerateUrl,
|
|
17218
17417
|
hideApproval = false,
|
|
17219
17418
|
className,
|
|
@@ -17251,6 +17450,8 @@ var ResearchReportJobCard = (props) => {
|
|
|
17251
17450
|
const [approveError, setApproveError] = (0, import_react79.useState)(null);
|
|
17252
17451
|
const [headingEdits, setHeadingEdits] = (0, import_react79.useState)({});
|
|
17253
17452
|
const [approvingOutline, setApprovingOutline] = (0, import_react79.useState)(false);
|
|
17453
|
+
const [rowBusyIndex, setRowBusyIndex] = (0, import_react79.useState)(null);
|
|
17454
|
+
const [rowError, setRowError] = (0, import_react79.useState)(null);
|
|
17254
17455
|
const [outlineWritePollUrl, setOutlineWritePollUrl] = (0, import_react79.useState)(null);
|
|
17255
17456
|
const [regenPollUrl, setRegenPollUrl] = (0, import_react79.useState)(null);
|
|
17256
17457
|
const previewRef = (0, import_react79.useRef)(null);
|
|
@@ -17506,6 +17707,76 @@ var ResearchReportJobCard = (props) => {
|
|
|
17506
17707
|
});
|
|
17507
17708
|
return changed ? { ...outline, sections } : void 0;
|
|
17508
17709
|
};
|
|
17710
|
+
const moveOutlineSection = async (index, delta) => {
|
|
17711
|
+
if (!outline || !Array.isArray(outline.sections) || rowBusyIndex !== null) return;
|
|
17712
|
+
const sections = outline.sections;
|
|
17713
|
+
const target = index + delta;
|
|
17714
|
+
if (target < 0 || target >= sections.length) return;
|
|
17715
|
+
const order = sections.map((_, i) => i);
|
|
17716
|
+
[order[index], order[target]] = [order[target], order[index]];
|
|
17717
|
+
const reordered = order.map((i) => sections[i]);
|
|
17718
|
+
const previousOutline = outline;
|
|
17719
|
+
const previousEdits = headingEdits;
|
|
17720
|
+
setOutline({ ...outline, sections: reordered });
|
|
17721
|
+
setHeadingEdits((prev) => {
|
|
17722
|
+
const remapped = {};
|
|
17723
|
+
order.forEach((originalIndex, newIndex) => {
|
|
17724
|
+
if (prev[originalIndex] !== void 0) remapped[newIndex] = prev[originalIndex];
|
|
17725
|
+
});
|
|
17726
|
+
return remapped;
|
|
17727
|
+
});
|
|
17728
|
+
setRowBusyIndex(index);
|
|
17729
|
+
setRowError(null);
|
|
17730
|
+
try {
|
|
17731
|
+
const endpoint = reorderSectionsUrl || `/api/reports/${job_id}/outline/reorder-sections`;
|
|
17732
|
+
const headers = { "Content-Type": "application/json" };
|
|
17733
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
17734
|
+
const res = await fetch(endpoint, {
|
|
17735
|
+
method: "POST",
|
|
17736
|
+
headers,
|
|
17737
|
+
body: JSON.stringify({ order })
|
|
17738
|
+
});
|
|
17739
|
+
if (!res.ok) {
|
|
17740
|
+
const err = await res.json().catch(() => ({}));
|
|
17741
|
+
throw new Error(err.detail || err.error || `Reorder failed (${res.status})`);
|
|
17742
|
+
}
|
|
17743
|
+
} catch (e) {
|
|
17744
|
+
setOutline(previousOutline);
|
|
17745
|
+
setHeadingEdits(previousEdits);
|
|
17746
|
+
setRowError(e instanceof Error ? e.message : "Could not reorder sections");
|
|
17747
|
+
} finally {
|
|
17748
|
+
setRowBusyIndex(null);
|
|
17749
|
+
}
|
|
17750
|
+
};
|
|
17751
|
+
const toggleSectionApproved = async (index) => {
|
|
17752
|
+
if (!outline || !Array.isArray(outline.sections) || rowBusyIndex !== null) return;
|
|
17753
|
+
const sections = outline.sections;
|
|
17754
|
+
const nextApproved = !sections[index]?.approved;
|
|
17755
|
+
const previousOutline = outline;
|
|
17756
|
+
const updated = sections.map((s, i) => i === index ? { ...s, approved: nextApproved } : s);
|
|
17757
|
+
setOutline({ ...outline, sections: updated });
|
|
17758
|
+
setRowBusyIndex(index);
|
|
17759
|
+
setRowError(null);
|
|
17760
|
+
try {
|
|
17761
|
+
const endpoint = approveSectionUrl || `/api/reports/${job_id}/outline/approve-section`;
|
|
17762
|
+
const headers = { "Content-Type": "application/json" };
|
|
17763
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
17764
|
+
const res = await fetch(endpoint, {
|
|
17765
|
+
method: "POST",
|
|
17766
|
+
headers,
|
|
17767
|
+
body: JSON.stringify({ section_index: index, approved: nextApproved })
|
|
17768
|
+
});
|
|
17769
|
+
if (!res.ok) {
|
|
17770
|
+
const err = await res.json().catch(() => ({}));
|
|
17771
|
+
throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
|
|
17772
|
+
}
|
|
17773
|
+
} catch (e) {
|
|
17774
|
+
setOutline(previousOutline);
|
|
17775
|
+
setRowError(e instanceof Error ? e.message : "Could not update section");
|
|
17776
|
+
} finally {
|
|
17777
|
+
setRowBusyIndex(null);
|
|
17778
|
+
}
|
|
17779
|
+
};
|
|
17509
17780
|
const handleApproveOutline = async () => {
|
|
17510
17781
|
if (!job_id || approvingOutline) return;
|
|
17511
17782
|
const endpoint = approveOutlineUrl || `/api/reports/${job_id}/approve-outline`;
|
|
@@ -17665,20 +17936,60 @@ var ResearchReportJobCard = (props) => {
|
|
|
17665
17936
|
] })
|
|
17666
17937
|
] }),
|
|
17667
17938
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "mt-4 space-y-2", children: [
|
|
17668
|
-
sections.map((s, i) =>
|
|
17669
|
-
|
|
17670
|
-
/* @__PURE__ */ (0, import_jsx_runtime148.
|
|
17671
|
-
"
|
|
17672
|
-
|
|
17673
|
-
|
|
17674
|
-
|
|
17675
|
-
|
|
17676
|
-
|
|
17677
|
-
|
|
17678
|
-
|
|
17679
|
-
|
|
17939
|
+
sections.map((s, i) => {
|
|
17940
|
+
const rowBusy = building || rowBusyIndex === i;
|
|
17941
|
+
return /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
17942
|
+
/* @__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 }),
|
|
17943
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
|
|
17944
|
+
"input",
|
|
17945
|
+
{
|
|
17946
|
+
value: headingEdits[i] ?? s.heading ?? "",
|
|
17947
|
+
onChange: (e) => setHeadingEdits((prev) => ({ ...prev, [i]: e.target.value })),
|
|
17948
|
+
disabled: building,
|
|
17949
|
+
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"
|
|
17950
|
+
}
|
|
17951
|
+
),
|
|
17952
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
|
|
17953
|
+
"button",
|
|
17954
|
+
{
|
|
17955
|
+
type: "button",
|
|
17956
|
+
onClick: () => moveOutlineSection(i, -1),
|
|
17957
|
+
disabled: rowBusy || i === 0,
|
|
17958
|
+
title: "Move up",
|
|
17959
|
+
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",
|
|
17960
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(ChevronUpIcon2, {})
|
|
17961
|
+
}
|
|
17962
|
+
),
|
|
17963
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
|
|
17964
|
+
"button",
|
|
17965
|
+
{
|
|
17966
|
+
type: "button",
|
|
17967
|
+
onClick: () => moveOutlineSection(i, 1),
|
|
17968
|
+
disabled: rowBusy || i === sections.length - 1,
|
|
17969
|
+
title: "Move down",
|
|
17970
|
+
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",
|
|
17971
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(ChevronDownIcon2, {})
|
|
17972
|
+
}
|
|
17973
|
+
),
|
|
17974
|
+
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)(
|
|
17975
|
+
"button",
|
|
17976
|
+
{
|
|
17977
|
+
type: "button",
|
|
17978
|
+
onClick: () => toggleSectionApproved(i),
|
|
17979
|
+
disabled: rowBusy,
|
|
17980
|
+
title: s.approved ? "Marked reviewed" : "Mark as reviewed",
|
|
17981
|
+
className: cn(
|
|
17982
|
+
"w-7 h-7 flex items-center justify-center rounded-md border flex-shrink-0 disabled:opacity-40",
|
|
17983
|
+
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"
|
|
17984
|
+
),
|
|
17985
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime148.jsx)(CheckIcon2, {})
|
|
17986
|
+
}
|
|
17987
|
+
)
|
|
17988
|
+
] }, i);
|
|
17989
|
+
}),
|
|
17680
17990
|
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." })
|
|
17681
17991
|
] }),
|
|
17992
|
+
rowError && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: "mt-2 text-xs text-red-400/90", children: rowError }),
|
|
17682
17993
|
approveError && /* @__PURE__ */ (0, import_jsx_runtime148.jsx)("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
|
|
17683
17994
|
/* @__PURE__ */ (0, import_jsx_runtime148.jsx)("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime148.jsxs)(
|
|
17684
17995
|
"button",
|