pxengine 0.1.147 → 0.1.148

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
@@ -3880,6 +3880,8 @@ interface ReportTheme {
3880
3880
  /** One planned section in a report outline (content only, no styling). */
3881
3881
  interface ReportOutlineSection {
3882
3882
  heading?: string;
3883
+ /** One-line description of what this section covers (DEV-286). */
3884
+ sub_heading?: string;
3883
3885
  body?: string;
3884
3886
  takeaway?: string;
3885
3887
  /** Informational review flag (DEV-194) — never gates Approve & Build. */
package/dist/index.d.ts CHANGED
@@ -3880,6 +3880,8 @@ interface ReportTheme {
3880
3880
  /** One planned section in a report outline (content only, no styling). */
3881
3881
  interface ReportOutlineSection {
3882
3882
  heading?: string;
3883
+ /** One-line description of what this section covers (DEV-286). */
3884
+ sub_heading?: string;
3883
3885
  body?: string;
3884
3886
  takeaway?: string;
3885
3887
  /** Informational review flag (DEV-194) — never gates Approve & Build. */
package/dist/index.mjs CHANGED
@@ -17985,7 +17985,26 @@ var PresentationJobCard = ({
17985
17985
  // src/molecules/generic/ResearchReportJobCard/ResearchReportJobCard.tsx
17986
17986
  import { useCallback as useCallback5, useEffect as useEffect10, useRef as useRef11, useState as useState12 } from "react";
17987
17987
  import { createPortal as createPortal2 } from "react-dom";
17988
+ import {
17989
+ DndContext,
17990
+ closestCenter,
17991
+ pointerWithin,
17992
+ PointerSensor,
17993
+ useSensor,
17994
+ useSensors
17995
+ } from "@dnd-kit/core";
17996
+ import {
17997
+ SortableContext,
17998
+ useSortable,
17999
+ verticalListSortingStrategy,
18000
+ arrayMove
18001
+ } from "@dnd-kit/sortable";
18002
+ import { CSS as CSS2 } from "@dnd-kit/utilities";
17988
18003
  import { Fragment as Fragment6, jsx as jsx154, jsxs as jsxs112 } from "react/jsx-runtime";
18004
+ var outlineCollisionDetection = (args) => {
18005
+ const pointerHits = pointerWithin(args);
18006
+ return pointerHits.length > 0 ? pointerHits : closestCenter(args);
18007
+ };
17989
18008
  var DEFAULT_THEME = {
17990
18009
  primary: "#C0AE82",
17991
18010
  secondary: "#C4AD7C",
@@ -18229,6 +18248,90 @@ function deriveJobStatusUrl2(newJobId, pollUrl, regenerateUrl) {
18229
18248
  const base = pollUrl?.replace(/\/api\/jobs\/[^/]+\/status.*$/, "") ?? regenerateUrl?.replace(/\/api\/reports\/[^/]+\/regenerate.*$/, "") ?? "";
18230
18249
  return `${base}/api/jobs/${newJobId}/status`;
18231
18250
  }
18251
+ var OutlineSectionRow = ({
18252
+ index,
18253
+ section,
18254
+ headingValue,
18255
+ subHeadingValue,
18256
+ onHeadingChange,
18257
+ onSubHeadingChange,
18258
+ onToggleApproved,
18259
+ disabled,
18260
+ rowBusy
18261
+ }) => {
18262
+ const { attributes, listeners: listeners2, setNodeRef, transform, transition, isDragging } = useSortable({
18263
+ id: String(index),
18264
+ disabled
18265
+ });
18266
+ return /* @__PURE__ */ jsx154(
18267
+ "div",
18268
+ {
18269
+ ref: setNodeRef,
18270
+ style: { transform: CSS2.Transform.toString(transform), transition, opacity: isDragging ? 0.5 : 1 },
18271
+ className: cn(
18272
+ "rounded-xl border bg-zinc-800/40 p-3 transition-colors",
18273
+ section.approved ? "border-emerald-700/40" : "border-zinc-700/60"
18274
+ ),
18275
+ children: /* @__PURE__ */ jsxs112("div", { className: "flex items-start gap-2.5", children: [
18276
+ /* @__PURE__ */ jsx154(
18277
+ "span",
18278
+ {
18279
+ ...attributes,
18280
+ ...listeners2,
18281
+ className: "flex-shrink-0 mt-1.5 cursor-grab text-zinc-600 hover:text-zinc-400 active:cursor-grabbing touch-none",
18282
+ children: /* @__PURE__ */ jsx154(GripVertical, { size: 14 })
18283
+ }
18284
+ ),
18285
+ /* @__PURE__ */ jsx154(
18286
+ "span",
18287
+ {
18288
+ className: cn(
18289
+ "flex-shrink-0 mt-1 w-5 h-5 rounded-full flex items-center justify-center text-[10px] font-semibold tabular-nums",
18290
+ section.approved ? "bg-emerald-500/15 text-emerald-300" : "bg-zinc-700/70 text-zinc-400"
18291
+ ),
18292
+ children: index + 1
18293
+ }
18294
+ ),
18295
+ /* @__PURE__ */ jsxs112("div", { className: "flex-1 min-w-0", children: [
18296
+ /* @__PURE__ */ jsx154(
18297
+ "input",
18298
+ {
18299
+ value: headingValue,
18300
+ onChange: (e) => onHeadingChange(e.target.value),
18301
+ disabled,
18302
+ placeholder: "Section heading",
18303
+ className: "w-full bg-transparent text-sm text-zinc-100 placeholder:text-zinc-600 outline-none disabled:opacity-60"
18304
+ }
18305
+ ),
18306
+ /* @__PURE__ */ jsx154(
18307
+ "input",
18308
+ {
18309
+ value: subHeadingValue,
18310
+ onChange: (e) => onSubHeadingChange(e.target.value),
18311
+ disabled,
18312
+ placeholder: "One-line summary",
18313
+ className: "mt-0.5 w-full bg-transparent text-xs text-zinc-500 placeholder:text-zinc-700 outline-none disabled:opacity-60"
18314
+ }
18315
+ )
18316
+ ] }),
18317
+ /* @__PURE__ */ jsx154(
18318
+ "button",
18319
+ {
18320
+ type: "button",
18321
+ onClick: onToggleApproved,
18322
+ disabled: rowBusy,
18323
+ title: section.approved ? "Marked reviewed" : "Mark as reviewed",
18324
+ className: cn(
18325
+ "w-7 h-7 flex-shrink-0 flex items-center justify-center rounded-md border disabled:opacity-40",
18326
+ section.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"
18327
+ ),
18328
+ children: /* @__PURE__ */ jsx154(CheckIcon, {})
18329
+ }
18330
+ )
18331
+ ] })
18332
+ }
18333
+ );
18334
+ };
18232
18335
  var ResearchReportJobCard = (props) => {
18233
18336
  const {
18234
18337
  job_id,
@@ -18294,9 +18397,12 @@ var ResearchReportJobCard = (props) => {
18294
18397
  const [regenerating, setRegenerating] = useState12(false);
18295
18398
  const [approveError, setApproveError] = useState12(null);
18296
18399
  const [headingEdits, setHeadingEdits] = useState12({});
18400
+ const [subHeadingEdits, setSubHeadingEdits] = useState12({});
18297
18401
  const [approvingOutline, setApprovingOutline] = useState12(false);
18298
18402
  const [rowBusyIndex, setRowBusyIndex] = useState12(null);
18299
18403
  const [rowError, setRowError] = useState12(null);
18404
+ const [reordering, setReordering] = useState12(false);
18405
+ const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 4 } }));
18300
18406
  const [outlineWritePollUrl, setOutlineWritePollUrl] = useState12(null);
18301
18407
  const [regenPollUrl, setRegenPollUrl] = useState12(null);
18302
18408
  const onCompleteRef = useRef11(onComplete);
@@ -18558,34 +18664,39 @@ var ResearchReportJobCard = (props) => {
18558
18664
  if (!outline || !Array.isArray(outline.sections)) return void 0;
18559
18665
  let changed = false;
18560
18666
  const sections = outline.sections.map((s, i) => {
18561
- const edited = headingEdits[i];
18562
- if (edited !== void 0 && edited !== s.heading) {
18667
+ const editedHeading = headingEdits[i];
18668
+ const editedSubHeading = subHeadingEdits[i];
18669
+ let next = s;
18670
+ if (editedHeading !== void 0 && editedHeading !== s.heading) {
18671
+ next = { ...next, heading: editedHeading };
18563
18672
  changed = true;
18564
- return { ...s, heading: edited };
18565
18673
  }
18566
- return s;
18674
+ if (editedSubHeading !== void 0 && editedSubHeading !== s.sub_heading) {
18675
+ next = { ...next, sub_heading: editedSubHeading };
18676
+ changed = true;
18677
+ }
18678
+ return next;
18567
18679
  });
18568
18680
  return changed ? { ...outline, sections } : void 0;
18569
18681
  };
18570
- const moveOutlineSection = async (index, delta) => {
18571
- if (!outline || !Array.isArray(outline.sections) || rowBusyIndex !== null) return;
18682
+ const reorderOutlineSections = async (order) => {
18683
+ if (!outline || !Array.isArray(outline.sections) || reordering) return;
18572
18684
  const sections = outline.sections;
18573
- const target = index + delta;
18574
- if (target < 0 || target >= sections.length) return;
18575
- const order = sections.map((_, i) => i);
18576
- [order[index], order[target]] = [order[target], order[index]];
18577
18685
  const reordered = order.map((i) => sections[i]);
18578
18686
  const previousOutline = outline;
18579
- const previousEdits = headingEdits;
18580
- setOutline({ ...outline, sections: reordered });
18581
- setHeadingEdits((prev) => {
18687
+ const previousHeadingEdits = headingEdits;
18688
+ const previousSubHeadingEdits = subHeadingEdits;
18689
+ const remap = (prev) => {
18582
18690
  const remapped = {};
18583
18691
  order.forEach((originalIndex, newIndex) => {
18584
18692
  if (prev[originalIndex] !== void 0) remapped[newIndex] = prev[originalIndex];
18585
18693
  });
18586
18694
  return remapped;
18587
- });
18588
- setRowBusyIndex(index);
18695
+ };
18696
+ setOutline({ ...outline, sections: reordered });
18697
+ setHeadingEdits(remap(previousHeadingEdits));
18698
+ setSubHeadingEdits(remap(previousSubHeadingEdits));
18699
+ setReordering(true);
18589
18700
  setRowError(null);
18590
18701
  try {
18591
18702
  const endpoint = reorderSectionsUrl || `/api/reports/${job_id}/outline/reorder-sections`;
@@ -18602,12 +18713,26 @@ var ResearchReportJobCard = (props) => {
18602
18713
  }
18603
18714
  } catch (e) {
18604
18715
  setOutline(previousOutline);
18605
- setHeadingEdits(previousEdits);
18716
+ setHeadingEdits(previousHeadingEdits);
18717
+ setSubHeadingEdits(previousSubHeadingEdits);
18606
18718
  setRowError(e instanceof Error ? e.message : "Could not reorder sections");
18607
18719
  } finally {
18608
- setRowBusyIndex(null);
18720
+ setReordering(false);
18609
18721
  }
18610
18722
  };
18723
+ const handleOutlineDragEnd = (event) => {
18724
+ const { active, over } = event;
18725
+ if (!over || active.id === over.id || !outline?.sections) return;
18726
+ const oldIndex = Number(active.id);
18727
+ const newIndex = Number(over.id);
18728
+ if (Number.isNaN(oldIndex) || Number.isNaN(newIndex)) return;
18729
+ const order = arrayMove(
18730
+ outline.sections.map((_, i) => i),
18731
+ oldIndex,
18732
+ newIndex
18733
+ );
18734
+ void reorderOutlineSections(order);
18735
+ };
18611
18736
  const toggleSectionApproved = async (index) => {
18612
18737
  if (!outline || !Array.isArray(outline.sections) || rowBusyIndex !== null) return;
18613
18738
  const sections = outline.sections;
@@ -18802,87 +18927,42 @@ var ResearchReportJobCard = (props) => {
18802
18927
  /* @__PURE__ */ jsx154("h3", { className: "text-sm font-semibold text-zinc-100 truncate leading-tight", children: title || "Report outline" }),
18803
18928
  /* @__PURE__ */ jsxs112("p", { className: "text-xs text-amber-400/90 mt-0.5", children: [
18804
18929
  "Review the outline",
18805
- sections.length > 0 ? " \u2014 edit any heading" : "",
18930
+ sections.length > 0 ? " \u2014 edit any heading, drag to reorder" : "",
18806
18931
  ", then build the full report."
18807
18932
  ] })
18808
18933
  ] })
18809
18934
  ] }),
18810
18935
  /* @__PURE__ */ jsxs112("div", { className: "mt-4 space-y-2", children: [
18811
- sections.map((s, i) => {
18812
- const rowBusy = building || rowBusyIndex === i;
18813
- return /* @__PURE__ */ jsxs112(
18814
- "div",
18815
- {
18816
- className: cn(
18817
- "rounded-xl border bg-zinc-800/40 p-3 transition-colors",
18818
- s.approved ? "border-emerald-700/40" : "border-zinc-700/60"
18819
- ),
18820
- children: [
18821
- /* @__PURE__ */ jsxs112("div", { className: "flex items-start gap-2.5", children: [
18822
- /* @__PURE__ */ jsx154(
18823
- "span",
18824
- {
18825
- className: cn(
18826
- "flex-shrink-0 mt-1 w-5 h-5 rounded-full flex items-center justify-center text-[10px] font-semibold tabular-nums",
18827
- s.approved ? "bg-emerald-500/15 text-emerald-300" : "bg-zinc-700/70 text-zinc-400"
18828
- ),
18829
- children: i + 1
18830
- }
18831
- ),
18832
- /* @__PURE__ */ jsx154(
18833
- "input",
18834
- {
18835
- value: headingEdits[i] ?? s.heading ?? "",
18836
- onChange: (e) => setHeadingEdits((prev) => ({ ...prev, [i]: e.target.value })),
18837
- disabled: building,
18838
- placeholder: "Section heading",
18839
- className: "flex-1 min-w-0 bg-transparent text-sm text-zinc-100 placeholder:text-zinc-600 outline-none disabled:opacity-60"
18840
- }
18841
- ),
18842
- /* @__PURE__ */ jsx154(
18843
- "button",
18844
- {
18845
- type: "button",
18846
- onClick: () => toggleSectionApproved(i),
18847
- disabled: rowBusy,
18848
- title: s.approved ? "Marked reviewed" : "Mark as reviewed",
18849
- className: cn(
18850
- "w-7 h-7 flex-shrink-0 flex items-center justify-center rounded-md border disabled:opacity-40",
18851
- 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"
18852
- ),
18853
- children: /* @__PURE__ */ jsx154(CheckIcon, {})
18854
- }
18855
- )
18856
- ] }),
18857
- /* @__PURE__ */ jsxs112("div", { className: "flex items-center justify-end gap-1 mt-2 pl-[30px]", children: [
18858
- /* @__PURE__ */ jsx154(
18859
- "button",
18860
- {
18861
- type: "button",
18862
- onClick: () => moveOutlineSection(i, -1),
18863
- disabled: rowBusy || i === 0,
18864
- title: "Move up",
18865
- className: "w-6 h-6 flex items-center justify-center rounded-md text-zinc-500 hover:text-zinc-200 hover:bg-zinc-700/60 disabled:opacity-30 disabled:hover:bg-transparent",
18866
- children: /* @__PURE__ */ jsx154(ChevronUpIcon, {})
18867
- }
18868
- ),
18869
- /* @__PURE__ */ jsx154(
18870
- "button",
18871
- {
18872
- type: "button",
18873
- onClick: () => moveOutlineSection(i, 1),
18874
- disabled: rowBusy || i === sections.length - 1,
18875
- title: "Move down",
18876
- className: "w-6 h-6 flex items-center justify-center rounded-md text-zinc-500 hover:text-zinc-200 hover:bg-zinc-700/60 disabled:opacity-30 disabled:hover:bg-transparent",
18877
- children: /* @__PURE__ */ jsx154(ChevronDownIcon, {})
18878
- }
18879
- )
18880
- ] })
18881
- ]
18882
- },
18883
- i
18884
- );
18885
- }),
18936
+ /* @__PURE__ */ jsx154(
18937
+ DndContext,
18938
+ {
18939
+ sensors,
18940
+ collisionDetection: outlineCollisionDetection,
18941
+ onDragEnd: handleOutlineDragEnd,
18942
+ children: /* @__PURE__ */ jsx154(
18943
+ SortableContext,
18944
+ {
18945
+ items: sections.map((_, i) => String(i)),
18946
+ strategy: verticalListSortingStrategy,
18947
+ children: sections.map((s, i) => /* @__PURE__ */ jsx154(
18948
+ OutlineSectionRow,
18949
+ {
18950
+ index: i,
18951
+ section: s,
18952
+ headingValue: headingEdits[i] ?? s.heading ?? "",
18953
+ subHeadingValue: subHeadingEdits[i] ?? s.sub_heading ?? "",
18954
+ onHeadingChange: (v) => setHeadingEdits((prev) => ({ ...prev, [i]: v })),
18955
+ onSubHeadingChange: (v) => setSubHeadingEdits((prev) => ({ ...prev, [i]: v })),
18956
+ onToggleApproved: () => toggleSectionApproved(i),
18957
+ disabled: building || reordering,
18958
+ rowBusy: building || rowBusyIndex === i
18959
+ },
18960
+ i
18961
+ ))
18962
+ }
18963
+ )
18964
+ }
18965
+ ),
18886
18966
  sections.length === 0 && /* @__PURE__ */ jsx154("p", { className: "text-xs text-zinc-500", children: "No outline sections were returned; you can still build the report." })
18887
18967
  ] }),
18888
18968
  rowError && /* @__PURE__ */ jsx154("p", { className: "mt-2 text-xs text-red-400/90", children: rowError }),