pxengine 0.1.115 → 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.d.cts CHANGED
@@ -3556,6 +3556,8 @@ interface DeckOutlineSlide {
3556
3556
  n?: number;
3557
3557
  type?: string;
3558
3558
  message?: string;
3559
+ /** Informational review flag (DEV-194) — never gates Approve & Build. */
3560
+ approved?: boolean;
3559
3561
  [key: string]: unknown;
3560
3562
  }
3561
3563
  /** The planned deck outline (deck_plan) shown for approval before rendering. */
@@ -3633,6 +3635,18 @@ interface PresentationJobCardProps {
3633
3635
  * `/api/presentations/{job_id}/approve-outline`. Proceeds to render the deck.
3634
3636
  */
3635
3637
  approveOutlineUrl?: string;
3638
+ /**
3639
+ * Reorder-slides endpoint (POST), DEV-194. Defaults to
3640
+ * `/api/presentations/{job_id}/outline/reorder-slides`. Called from the
3641
+ * outline-review phase's move-up/move-down controls.
3642
+ */
3643
+ reorderSlidesUrl?: string;
3644
+ /**
3645
+ * Approve-slide endpoint (POST), DEV-194. Defaults to
3646
+ * `/api/presentations/{job_id}/outline/approve-slide`. Informational only —
3647
+ * never gates the whole-outline Approve & Build action.
3648
+ */
3649
+ approveSlideUrl?: string;
3636
3650
  /** Regenerate endpoint (POST). Defaults to `/api/presentations/{job_id}/regenerate`. */
3637
3651
  regenerateUrl?: string;
3638
3652
  /**
@@ -3671,6 +3685,8 @@ interface ReportOutlineSection {
3671
3685
  heading?: string;
3672
3686
  body?: string;
3673
3687
  takeaway?: string;
3688
+ /** Informational review flag (DEV-194) — never gates Approve & Build. */
3689
+ approved?: boolean;
3674
3690
  [key: string]: unknown;
3675
3691
  }
3676
3692
  /** The planned report outline (report_data) shown for approval. */
@@ -3765,6 +3781,18 @@ interface ResearchReportJobCardProps {
3765
3781
  * phase to proceed to rendering the full report.
3766
3782
  */
3767
3783
  approveOutlineUrl?: string;
3784
+ /**
3785
+ * Reorder-sections endpoint (POST), DEV-194. Defaults to
3786
+ * `/api/reports/{job_id}/outline/reorder-sections`. Called from the
3787
+ * outline-review phase's move-up/move-down controls.
3788
+ */
3789
+ reorderSectionsUrl?: string;
3790
+ /**
3791
+ * Approve-section endpoint (POST), DEV-194. Defaults to
3792
+ * `/api/reports/{job_id}/outline/approve-section`. Informational only —
3793
+ * never gates the whole-outline Approve & Build action.
3794
+ */
3795
+ approveSectionUrl?: string;
3768
3796
  /** Regenerate endpoint (POST). Defaults to `/api/reports/{job_id}/regenerate`. */
3769
3797
  regenerateUrl?: string;
3770
3798
  /**
package/dist/index.d.ts CHANGED
@@ -3556,6 +3556,8 @@ interface DeckOutlineSlide {
3556
3556
  n?: number;
3557
3557
  type?: string;
3558
3558
  message?: string;
3559
+ /** Informational review flag (DEV-194) — never gates Approve & Build. */
3560
+ approved?: boolean;
3559
3561
  [key: string]: unknown;
3560
3562
  }
3561
3563
  /** The planned deck outline (deck_plan) shown for approval before rendering. */
@@ -3633,6 +3635,18 @@ interface PresentationJobCardProps {
3633
3635
  * `/api/presentations/{job_id}/approve-outline`. Proceeds to render the deck.
3634
3636
  */
3635
3637
  approveOutlineUrl?: string;
3638
+ /**
3639
+ * Reorder-slides endpoint (POST), DEV-194. Defaults to
3640
+ * `/api/presentations/{job_id}/outline/reorder-slides`. Called from the
3641
+ * outline-review phase's move-up/move-down controls.
3642
+ */
3643
+ reorderSlidesUrl?: string;
3644
+ /**
3645
+ * Approve-slide endpoint (POST), DEV-194. Defaults to
3646
+ * `/api/presentations/{job_id}/outline/approve-slide`. Informational only —
3647
+ * never gates the whole-outline Approve & Build action.
3648
+ */
3649
+ approveSlideUrl?: string;
3636
3650
  /** Regenerate endpoint (POST). Defaults to `/api/presentations/{job_id}/regenerate`. */
3637
3651
  regenerateUrl?: string;
3638
3652
  /**
@@ -3671,6 +3685,8 @@ interface ReportOutlineSection {
3671
3685
  heading?: string;
3672
3686
  body?: string;
3673
3687
  takeaway?: string;
3688
+ /** Informational review flag (DEV-194) — never gates Approve & Build. */
3689
+ approved?: boolean;
3674
3690
  [key: string]: unknown;
3675
3691
  }
3676
3692
  /** The planned report outline (report_data) shown for approval. */
@@ -3765,6 +3781,18 @@ interface ResearchReportJobCardProps {
3765
3781
  * phase to proceed to rendering the full report.
3766
3782
  */
3767
3783
  approveOutlineUrl?: string;
3784
+ /**
3785
+ * Reorder-sections endpoint (POST), DEV-194. Defaults to
3786
+ * `/api/reports/{job_id}/outline/reorder-sections`. Called from the
3787
+ * outline-review phase's move-up/move-down controls.
3788
+ */
3789
+ reorderSectionsUrl?: string;
3790
+ /**
3791
+ * Approve-section endpoint (POST), DEV-194. Defaults to
3792
+ * `/api/reports/{job_id}/outline/approve-section`. Informational only —
3793
+ * never gates the whole-outline Approve & Build action.
3794
+ */
3795
+ approveSectionUrl?: string;
3768
3796
  /** Regenerate endpoint (POST). Defaults to `/api/reports/{job_id}/regenerate`. */
3769
3797
  regenerateUrl?: string;
3770
3798
  /**
package/dist/index.mjs CHANGED
@@ -15801,6 +15801,8 @@ var SlideTypeIcon = () => /* @__PURE__ */ jsxs108("svg", { width: "14", height:
15801
15801
  /* @__PURE__ */ jsx147("rect", { x: "3", y: "4", width: "18", height: "14", rx: "2" }),
15802
15802
  /* @__PURE__ */ jsx147("line", { x1: "3", y1: "10", x2: "21", y2: "10" })
15803
15803
  ] });
15804
+ var ChevronUpIcon = () => /* @__PURE__ */ jsx147("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx147("polyline", { points: "18 15 12 9 6 15" }) });
15805
+ var ChevronDownIcon = () => /* @__PURE__ */ jsx147("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx147("polyline", { points: "6 9 12 15 18 9" }) });
15804
15806
  function formatTemplateLabel(templateId) {
15805
15807
  if (!templateId) return null;
15806
15808
  return templateId.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
@@ -16026,6 +16028,8 @@ var PresentationJobCard = ({
16026
16028
  shareUrl,
16027
16029
  approveUrl,
16028
16030
  approveOutlineUrl,
16031
+ reorderSlidesUrl,
16032
+ approveSlideUrl,
16029
16033
  regenerateUrl,
16030
16034
  hideApproval = false,
16031
16035
  className,
@@ -16057,6 +16061,8 @@ var PresentationJobCard = ({
16057
16061
  const [messageEdits, setMessageEdits] = useState10({});
16058
16062
  const [approvingOutline, setApprovingOutline] = useState10(false);
16059
16063
  const [outlineWritePollUrl, setOutlineWritePollUrl] = useState10(null);
16064
+ const [rowBusyIndex, setRowBusyIndex] = useState10(null);
16065
+ const [rowError, setRowError] = useState10(null);
16060
16066
  const [regenPollUrl, setRegenPollUrl] = useState10(null);
16061
16067
  const [currentSlide, setCurrentSlide] = useState10(1);
16062
16068
  const [previewScale, setPreviewScale] = useState10(1);
@@ -16289,6 +16295,76 @@ var PresentationJobCard = ({
16289
16295
  });
16290
16296
  return changed ? { ...outline, slides } : void 0;
16291
16297
  };
16298
+ const moveOutlineSlide = async (index, delta) => {
16299
+ if (!outline || !Array.isArray(outline.slides) || rowBusyIndex !== null) return;
16300
+ const slides = outline.slides;
16301
+ const target = index + delta;
16302
+ if (target < 0 || target >= slides.length) return;
16303
+ const order = slides.map((_, i) => i);
16304
+ [order[index], order[target]] = [order[target], order[index]];
16305
+ const reordered = order.map((i, newIndex) => ({ ...slides[i], n: newIndex + 1 }));
16306
+ const previousOutline = outline;
16307
+ const previousEdits = messageEdits;
16308
+ setOutline({ ...outline, slides: reordered });
16309
+ setMessageEdits((prev) => {
16310
+ const remapped = {};
16311
+ order.forEach((originalIndex, newIndex) => {
16312
+ if (prev[originalIndex] !== void 0) remapped[newIndex] = prev[originalIndex];
16313
+ });
16314
+ return remapped;
16315
+ });
16316
+ setRowBusyIndex(index);
16317
+ setRowError(null);
16318
+ try {
16319
+ const endpoint = reorderSlidesUrl || `/api/presentations/${_job_id}/outline/reorder-slides`;
16320
+ const headers = { "Content-Type": "application/json" };
16321
+ if (authToken) headers.Authorization = `Bearer ${authToken}`;
16322
+ const res = await fetch(endpoint, {
16323
+ method: "POST",
16324
+ headers,
16325
+ body: JSON.stringify({ order })
16326
+ });
16327
+ if (!res.ok) {
16328
+ const err = await res.json().catch(() => ({}));
16329
+ throw new Error(err.detail || err.error || `Reorder failed (${res.status})`);
16330
+ }
16331
+ } catch (e) {
16332
+ setOutline(previousOutline);
16333
+ setMessageEdits(previousEdits);
16334
+ setRowError(e instanceof Error ? e.message : "Could not reorder slides");
16335
+ } finally {
16336
+ setRowBusyIndex(null);
16337
+ }
16338
+ };
16339
+ const toggleSlideApproved = async (index) => {
16340
+ if (!outline || !Array.isArray(outline.slides) || rowBusyIndex !== null) return;
16341
+ const slides = outline.slides;
16342
+ const nextApproved = !slides[index]?.approved;
16343
+ const previousOutline = outline;
16344
+ const updated = slides.map((s, i) => i === index ? { ...s, approved: nextApproved } : s);
16345
+ setOutline({ ...outline, slides: updated });
16346
+ setRowBusyIndex(index);
16347
+ setRowError(null);
16348
+ try {
16349
+ const endpoint = approveSlideUrl || `/api/presentations/${_job_id}/outline/approve-slide`;
16350
+ const headers = { "Content-Type": "application/json" };
16351
+ if (authToken) headers.Authorization = `Bearer ${authToken}`;
16352
+ const res = await fetch(endpoint, {
16353
+ method: "POST",
16354
+ headers,
16355
+ body: JSON.stringify({ slide_index: index, approved: nextApproved })
16356
+ });
16357
+ if (!res.ok) {
16358
+ const err = await res.json().catch(() => ({}));
16359
+ throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
16360
+ }
16361
+ } catch (e) {
16362
+ setOutline(previousOutline);
16363
+ setRowError(e instanceof Error ? e.message : "Could not update slide");
16364
+ } finally {
16365
+ setRowBusyIndex(null);
16366
+ }
16367
+ };
16292
16368
  const handleApproveOutline = async () => {
16293
16369
  if (!_job_id || approvingOutline) return;
16294
16370
  const endpoint = approveOutlineUrl || `/api/presentations/${_job_id}/approve-outline`;
@@ -16526,31 +16602,71 @@ var PresentationJobCard = ({
16526
16602
  ] })
16527
16603
  ] }),
16528
16604
  /* @__PURE__ */ jsxs108("div", { className: "mt-4 space-y-2", children: [
16529
- slides.map((s, i) => /* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-2", children: [
16530
- /* @__PURE__ */ jsx147("span", { className: "text-xs text-zinc-600 w-5 text-right tabular-nums flex-shrink-0", children: s.n ?? i + 1 }),
16531
- /* @__PURE__ */ jsxs108(
16532
- "span",
16533
- {
16534
- className: "flex items-center gap-1 text-[10px] uppercase tracking-wide text-zinc-500 w-20 flex-shrink-0 truncate",
16535
- title: s.type,
16536
- children: [
16537
- /* @__PURE__ */ jsx147(SlideTypeIcon, {}),
16538
- s.type || "slide"
16539
- ]
16540
- }
16541
- ),
16542
- /* @__PURE__ */ jsx147(
16543
- "input",
16544
- {
16545
- value: messageEdits[i] ?? s.message ?? "",
16546
- onChange: (e) => setMessageEdits((prev) => ({ ...prev, [i]: e.target.value })),
16547
- disabled: building,
16548
- 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"
16549
- }
16550
- )
16551
- ] }, i)),
16605
+ slides.map((s, i) => {
16606
+ const rowBusy = building || rowBusyIndex === i;
16607
+ return /* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-1.5", children: [
16608
+ /* @__PURE__ */ jsx147("span", { className: "text-xs text-zinc-600 w-5 text-right tabular-nums flex-shrink-0", children: s.n ?? i + 1 }),
16609
+ /* @__PURE__ */ jsxs108(
16610
+ "span",
16611
+ {
16612
+ className: "flex items-center gap-1 text-[10px] uppercase tracking-wide text-zinc-500 w-20 flex-shrink-0 truncate",
16613
+ title: s.type,
16614
+ children: [
16615
+ /* @__PURE__ */ jsx147(SlideTypeIcon, {}),
16616
+ s.type || "slide"
16617
+ ]
16618
+ }
16619
+ ),
16620
+ /* @__PURE__ */ jsx147(
16621
+ "input",
16622
+ {
16623
+ value: messageEdits[i] ?? s.message ?? "",
16624
+ onChange: (e) => setMessageEdits((prev) => ({ ...prev, [i]: e.target.value })),
16625
+ disabled: building,
16626
+ 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"
16627
+ }
16628
+ ),
16629
+ /* @__PURE__ */ jsx147(
16630
+ "button",
16631
+ {
16632
+ type: "button",
16633
+ onClick: () => moveOutlineSlide(i, -1),
16634
+ disabled: rowBusy || i === 0,
16635
+ title: "Move up",
16636
+ 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",
16637
+ children: /* @__PURE__ */ jsx147(ChevronUpIcon, {})
16638
+ }
16639
+ ),
16640
+ /* @__PURE__ */ jsx147(
16641
+ "button",
16642
+ {
16643
+ type: "button",
16644
+ onClick: () => moveOutlineSlide(i, 1),
16645
+ disabled: rowBusy || i === slides.length - 1,
16646
+ title: "Move down",
16647
+ 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",
16648
+ children: /* @__PURE__ */ jsx147(ChevronDownIcon, {})
16649
+ }
16650
+ ),
16651
+ /* @__PURE__ */ jsx147(
16652
+ "button",
16653
+ {
16654
+ type: "button",
16655
+ onClick: () => toggleSlideApproved(i),
16656
+ disabled: rowBusy,
16657
+ title: s.approved ? "Marked reviewed" : "Mark as reviewed",
16658
+ className: cn(
16659
+ "w-7 h-7 flex items-center justify-center rounded-md border flex-shrink-0 disabled:opacity-40",
16660
+ 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"
16661
+ ),
16662
+ children: /* @__PURE__ */ jsx147(CheckIcon, {})
16663
+ }
16664
+ )
16665
+ ] }, i);
16666
+ }),
16552
16667
  slides.length === 0 && /* @__PURE__ */ jsx147("p", { className: "text-xs text-zinc-500", children: "No outline slides were returned; you can still build the deck." })
16553
16668
  ] }),
16669
+ rowError && /* @__PURE__ */ jsx147("p", { className: "mt-2 text-xs text-red-400/90", children: rowError }),
16554
16670
  approveError && /* @__PURE__ */ jsx147("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
16555
16671
  /* @__PURE__ */ jsx147("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs108(
16556
16672
  "button",
@@ -16800,6 +16916,8 @@ var ShareIcon2 = () => /* @__PURE__ */ jsxs109("svg", { width: "14", height: "14
16800
16916
  /* @__PURE__ */ jsx148("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
16801
16917
  ] });
16802
16918
  var CheckIcon2 = () => /* @__PURE__ */ jsx148("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx148("polyline", { points: "20 6 9 17 4 12" }) });
16919
+ var ChevronUpIcon2 = () => /* @__PURE__ */ jsx148("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx148("polyline", { points: "18 15 12 9 6 15" }) });
16920
+ var ChevronDownIcon2 = () => /* @__PURE__ */ jsx148("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx148("polyline", { points: "6 9 12 15 18 9" }) });
16803
16921
  var RefreshIcon2 = () => /* @__PURE__ */ jsxs109("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: [
16804
16922
  /* @__PURE__ */ jsx148("polyline", { points: "23 4 23 10 17 10" }),
16805
16923
  /* @__PURE__ */ jsx148("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
@@ -16882,6 +17000,8 @@ var ResearchReportJobCard = (props) => {
16882
17000
  shareUrl,
16883
17001
  approveUrl,
16884
17002
  approveOutlineUrl,
17003
+ reorderSectionsUrl,
17004
+ approveSectionUrl,
16885
17005
  regenerateUrl,
16886
17006
  hideApproval = false,
16887
17007
  className,
@@ -16919,6 +17039,8 @@ var ResearchReportJobCard = (props) => {
16919
17039
  const [approveError, setApproveError] = useState11(null);
16920
17040
  const [headingEdits, setHeadingEdits] = useState11({});
16921
17041
  const [approvingOutline, setApprovingOutline] = useState11(false);
17042
+ const [rowBusyIndex, setRowBusyIndex] = useState11(null);
17043
+ const [rowError, setRowError] = useState11(null);
16922
17044
  const [outlineWritePollUrl, setOutlineWritePollUrl] = useState11(null);
16923
17045
  const [regenPollUrl, setRegenPollUrl] = useState11(null);
16924
17046
  const previewRef = useRef7(null);
@@ -17174,6 +17296,76 @@ var ResearchReportJobCard = (props) => {
17174
17296
  });
17175
17297
  return changed ? { ...outline, sections } : void 0;
17176
17298
  };
17299
+ const moveOutlineSection = async (index, delta) => {
17300
+ if (!outline || !Array.isArray(outline.sections) || rowBusyIndex !== null) return;
17301
+ const sections = outline.sections;
17302
+ const target = index + delta;
17303
+ if (target < 0 || target >= sections.length) return;
17304
+ const order = sections.map((_, i) => i);
17305
+ [order[index], order[target]] = [order[target], order[index]];
17306
+ const reordered = order.map((i) => sections[i]);
17307
+ const previousOutline = outline;
17308
+ const previousEdits = headingEdits;
17309
+ setOutline({ ...outline, sections: reordered });
17310
+ setHeadingEdits((prev) => {
17311
+ const remapped = {};
17312
+ order.forEach((originalIndex, newIndex) => {
17313
+ if (prev[originalIndex] !== void 0) remapped[newIndex] = prev[originalIndex];
17314
+ });
17315
+ return remapped;
17316
+ });
17317
+ setRowBusyIndex(index);
17318
+ setRowError(null);
17319
+ try {
17320
+ const endpoint = reorderSectionsUrl || `/api/reports/${job_id}/outline/reorder-sections`;
17321
+ const headers = { "Content-Type": "application/json" };
17322
+ if (authToken) headers.Authorization = `Bearer ${authToken}`;
17323
+ const res = await fetch(endpoint, {
17324
+ method: "POST",
17325
+ headers,
17326
+ body: JSON.stringify({ order })
17327
+ });
17328
+ if (!res.ok) {
17329
+ const err = await res.json().catch(() => ({}));
17330
+ throw new Error(err.detail || err.error || `Reorder failed (${res.status})`);
17331
+ }
17332
+ } catch (e) {
17333
+ setOutline(previousOutline);
17334
+ setHeadingEdits(previousEdits);
17335
+ setRowError(e instanceof Error ? e.message : "Could not reorder sections");
17336
+ } finally {
17337
+ setRowBusyIndex(null);
17338
+ }
17339
+ };
17340
+ const toggleSectionApproved = async (index) => {
17341
+ if (!outline || !Array.isArray(outline.sections) || rowBusyIndex !== null) return;
17342
+ const sections = outline.sections;
17343
+ const nextApproved = !sections[index]?.approved;
17344
+ const previousOutline = outline;
17345
+ const updated = sections.map((s, i) => i === index ? { ...s, approved: nextApproved } : s);
17346
+ setOutline({ ...outline, sections: updated });
17347
+ setRowBusyIndex(index);
17348
+ setRowError(null);
17349
+ try {
17350
+ const endpoint = approveSectionUrl || `/api/reports/${job_id}/outline/approve-section`;
17351
+ const headers = { "Content-Type": "application/json" };
17352
+ if (authToken) headers.Authorization = `Bearer ${authToken}`;
17353
+ const res = await fetch(endpoint, {
17354
+ method: "POST",
17355
+ headers,
17356
+ body: JSON.stringify({ section_index: index, approved: nextApproved })
17357
+ });
17358
+ if (!res.ok) {
17359
+ const err = await res.json().catch(() => ({}));
17360
+ throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
17361
+ }
17362
+ } catch (e) {
17363
+ setOutline(previousOutline);
17364
+ setRowError(e instanceof Error ? e.message : "Could not update section");
17365
+ } finally {
17366
+ setRowBusyIndex(null);
17367
+ }
17368
+ };
17177
17369
  const handleApproveOutline = async () => {
17178
17370
  if (!job_id || approvingOutline) return;
17179
17371
  const endpoint = approveOutlineUrl || `/api/reports/${job_id}/approve-outline`;
@@ -17333,20 +17525,60 @@ var ResearchReportJobCard = (props) => {
17333
17525
  ] })
17334
17526
  ] }),
17335
17527
  /* @__PURE__ */ jsxs109("div", { className: "mt-4 space-y-2", children: [
17336
- sections.map((s, i) => /* @__PURE__ */ jsxs109("div", { className: "flex items-center gap-2", children: [
17337
- /* @__PURE__ */ jsx148("span", { className: "text-xs text-zinc-600 w-5 text-right tabular-nums flex-shrink-0", children: i + 1 }),
17338
- /* @__PURE__ */ jsx148(
17339
- "input",
17340
- {
17341
- value: headingEdits[i] ?? s.heading ?? "",
17342
- onChange: (e) => setHeadingEdits((prev) => ({ ...prev, [i]: e.target.value })),
17343
- disabled: building,
17344
- 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"
17345
- }
17346
- )
17347
- ] }, i)),
17528
+ sections.map((s, i) => {
17529
+ const rowBusy = building || rowBusyIndex === i;
17530
+ return /* @__PURE__ */ jsxs109("div", { className: "flex items-center gap-1.5", children: [
17531
+ /* @__PURE__ */ jsx148("span", { className: "text-xs text-zinc-600 w-5 text-right tabular-nums flex-shrink-0", children: i + 1 }),
17532
+ /* @__PURE__ */ jsx148(
17533
+ "input",
17534
+ {
17535
+ value: headingEdits[i] ?? s.heading ?? "",
17536
+ onChange: (e) => setHeadingEdits((prev) => ({ ...prev, [i]: e.target.value })),
17537
+ disabled: building,
17538
+ 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"
17539
+ }
17540
+ ),
17541
+ /* @__PURE__ */ jsx148(
17542
+ "button",
17543
+ {
17544
+ type: "button",
17545
+ onClick: () => moveOutlineSection(i, -1),
17546
+ disabled: rowBusy || i === 0,
17547
+ title: "Move up",
17548
+ 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",
17549
+ children: /* @__PURE__ */ jsx148(ChevronUpIcon2, {})
17550
+ }
17551
+ ),
17552
+ /* @__PURE__ */ jsx148(
17553
+ "button",
17554
+ {
17555
+ type: "button",
17556
+ onClick: () => moveOutlineSection(i, 1),
17557
+ disabled: rowBusy || i === sections.length - 1,
17558
+ title: "Move down",
17559
+ 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",
17560
+ children: /* @__PURE__ */ jsx148(ChevronDownIcon2, {})
17561
+ }
17562
+ ),
17563
+ /* @__PURE__ */ jsx148(
17564
+ "button",
17565
+ {
17566
+ type: "button",
17567
+ onClick: () => toggleSectionApproved(i),
17568
+ disabled: rowBusy,
17569
+ title: s.approved ? "Marked reviewed" : "Mark as reviewed",
17570
+ className: cn(
17571
+ "w-7 h-7 flex items-center justify-center rounded-md border flex-shrink-0 disabled:opacity-40",
17572
+ 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"
17573
+ ),
17574
+ children: /* @__PURE__ */ jsx148(CheckIcon2, {})
17575
+ }
17576
+ )
17577
+ ] }, i);
17578
+ }),
17348
17579
  sections.length === 0 && /* @__PURE__ */ jsx148("p", { className: "text-xs text-zinc-500", children: "No outline sections were returned; you can still build the report." })
17349
17580
  ] }),
17581
+ rowError && /* @__PURE__ */ jsx148("p", { className: "mt-2 text-xs text-red-400/90", children: rowError }),
17350
17582
  approveError && /* @__PURE__ */ jsx148("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
17351
17583
  /* @__PURE__ */ jsx148("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs109(
17352
17584
  "button",