pxengine 0.1.112 → 0.1.114
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 +590 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -1
- package/dist/index.d.ts +76 -1
- package/dist/index.mjs +590 -37
- package/dist/index.mjs.map +1 -1
- package/dist/registry.json +62 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -15793,6 +15793,22 @@ var ShareIcon = () => /* @__PURE__ */ jsxs108("svg", { width: "14", height: "14"
|
|
|
15793
15793
|
/* @__PURE__ */ jsx147("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
15794
15794
|
] });
|
|
15795
15795
|
var CheckIcon = () => /* @__PURE__ */ jsx147("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx147("polyline", { points: "20 6 9 17 4 12" }) });
|
|
15796
|
+
var RefreshIcon = () => /* @__PURE__ */ jsxs108("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
15797
|
+
/* @__PURE__ */ jsx147("polyline", { points: "23 4 23 10 17 10" }),
|
|
15798
|
+
/* @__PURE__ */ jsx147("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
|
|
15799
|
+
] });
|
|
15800
|
+
var SlideTypeIcon = () => /* @__PURE__ */ jsxs108("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
15801
|
+
/* @__PURE__ */ jsx147("rect", { x: "3", y: "4", width: "18", height: "14", rx: "2" }),
|
|
15802
|
+
/* @__PURE__ */ jsx147("line", { x1: "3", y1: "10", x2: "21", y2: "10" })
|
|
15803
|
+
] });
|
|
15804
|
+
function formatTemplateLabel(templateId) {
|
|
15805
|
+
if (!templateId) return null;
|
|
15806
|
+
return templateId.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
15807
|
+
}
|
|
15808
|
+
function deriveJobStatusUrl(newJobId, pollUrl, regenerateUrl) {
|
|
15809
|
+
const base = pollUrl?.replace(/\/api\/jobs\/[^/]+\/status.*$/, "") ?? regenerateUrl?.replace(/\/api\/presentations\/[^/]+\/regenerate.*$/, "") ?? "";
|
|
15810
|
+
return `${base}/api/jobs/${newJobId}/status`;
|
|
15811
|
+
}
|
|
15796
15812
|
var FORMATS = [
|
|
15797
15813
|
{
|
|
15798
15814
|
key: "pptx_url",
|
|
@@ -15998,11 +16014,20 @@ var PresentationJobCard = ({
|
|
|
15998
16014
|
status: initialStatus,
|
|
15999
16015
|
slide_count: initialSlideCount,
|
|
16000
16016
|
formats: initialFormats = {},
|
|
16017
|
+
generation_mode: initialGenerationMode,
|
|
16018
|
+
template_id: initialTemplateId,
|
|
16019
|
+
template_version_id: initialTemplateVersionId,
|
|
16020
|
+
review_status: initialReviewStatus,
|
|
16021
|
+
outline: initialOutline,
|
|
16001
16022
|
error: initialError,
|
|
16002
16023
|
progress: initialProgress,
|
|
16003
16024
|
pollUrl,
|
|
16004
16025
|
authToken,
|
|
16005
16026
|
shareUrl,
|
|
16027
|
+
approveUrl,
|
|
16028
|
+
approveOutlineUrl,
|
|
16029
|
+
regenerateUrl,
|
|
16030
|
+
hideApproval = false,
|
|
16006
16031
|
className,
|
|
16007
16032
|
theme,
|
|
16008
16033
|
onComplete,
|
|
@@ -16016,9 +16041,23 @@ var PresentationJobCard = ({
|
|
|
16016
16041
|
const [formats, setFormats] = useState10(initialFormats);
|
|
16017
16042
|
const [error, setError] = useState10(initialError);
|
|
16018
16043
|
const [progress, setProgress] = useState10(initialProgress);
|
|
16044
|
+
const [generationMode, setGenerationMode] = useState10(
|
|
16045
|
+
initialGenerationMode || (initialFormats?.html_url ? "template" : "")
|
|
16046
|
+
);
|
|
16047
|
+
const [templateId, setTemplateId] = useState10(initialTemplateId || "");
|
|
16048
|
+
const [, setTemplateVersionId] = useState10(initialTemplateVersionId || "");
|
|
16049
|
+
const [reviewStatus, setReviewStatus] = useState10(initialReviewStatus || "");
|
|
16050
|
+
const [outline, setOutline] = useState10(initialOutline);
|
|
16019
16051
|
const [showExport, setShowExport] = useState10(false);
|
|
16020
16052
|
const [showFullscreen, setShowFullscreen] = useState10(false);
|
|
16021
16053
|
const [copied, setCopied] = useState10(false);
|
|
16054
|
+
const [approving, setApproving] = useState10(false);
|
|
16055
|
+
const [regenerating, setRegenerating] = useState10(false);
|
|
16056
|
+
const [approveError, setApproveError] = useState10(null);
|
|
16057
|
+
const [messageEdits, setMessageEdits] = useState10({});
|
|
16058
|
+
const [approvingOutline, setApprovingOutline] = useState10(false);
|
|
16059
|
+
const [outlineWritePollUrl, setOutlineWritePollUrl] = useState10(null);
|
|
16060
|
+
const [regenPollUrl, setRegenPollUrl] = useState10(null);
|
|
16022
16061
|
const [currentSlide, setCurrentSlide] = useState10(1);
|
|
16023
16062
|
const [previewScale, setPreviewScale] = useState10(1);
|
|
16024
16063
|
const [iframeReady, setIframeReady] = useState10(false);
|
|
@@ -16045,6 +16084,22 @@ var PresentationJobCard = ({
|
|
|
16045
16084
|
useEffect7(() => {
|
|
16046
16085
|
if (initialTitle) setTitle(initialTitle);
|
|
16047
16086
|
}, [initialTitle]);
|
|
16087
|
+
useEffect7(() => {
|
|
16088
|
+
if (initialGenerationMode) setGenerationMode(initialGenerationMode);
|
|
16089
|
+
}, [initialGenerationMode]);
|
|
16090
|
+
useEffect7(() => {
|
|
16091
|
+
if (initialTemplateId) setTemplateId(initialTemplateId);
|
|
16092
|
+
}, [initialTemplateId]);
|
|
16093
|
+
useEffect7(() => {
|
|
16094
|
+
if (initialTemplateVersionId) setTemplateVersionId(initialTemplateVersionId);
|
|
16095
|
+
}, [initialTemplateVersionId]);
|
|
16096
|
+
useEffect7(() => {
|
|
16097
|
+
if (initialReviewStatus) setReviewStatus(initialReviewStatus);
|
|
16098
|
+
}, [initialReviewStatus]);
|
|
16099
|
+
const initialOutlineSlideCount = initialOutline?.slides?.length;
|
|
16100
|
+
useEffect7(() => {
|
|
16101
|
+
if (initialOutline) setOutline(initialOutline);
|
|
16102
|
+
}, [initialOutlineSlideCount]);
|
|
16048
16103
|
const updateScale = useCallback4(() => {
|
|
16049
16104
|
if (previewRef.current) setPreviewScale(previewRef.current.offsetWidth / 1280);
|
|
16050
16105
|
}, []);
|
|
@@ -16108,13 +16163,21 @@ var PresentationJobCard = ({
|
|
|
16108
16163
|
setProgress(data.progress);
|
|
16109
16164
|
}
|
|
16110
16165
|
if (newStatus === "complete" && data.output) {
|
|
16111
|
-
const
|
|
16112
|
-
const
|
|
16113
|
-
const
|
|
16166
|
+
const output = data.output;
|
|
16167
|
+
const isOutlinePhase = output.review_status === "pending_outline_approval" || output.type === "presentation-outline";
|
|
16168
|
+
const newTitle = output.title || initialTitle;
|
|
16169
|
+
const newSlideCount = output.slide_count || 0;
|
|
16170
|
+
const newFormats = output.formats || {};
|
|
16114
16171
|
setTitle(newTitle);
|
|
16115
16172
|
setSlideCount(newSlideCount);
|
|
16116
16173
|
setFormats(newFormats);
|
|
16117
|
-
if (
|
|
16174
|
+
if (output.generation_mode) setGenerationMode(output.generation_mode);
|
|
16175
|
+
if (output.template_id) setTemplateId(output.template_id);
|
|
16176
|
+
if (output.template_version_id) setTemplateVersionId(output.template_version_id);
|
|
16177
|
+
if (output.review_status) setReviewStatus(output.review_status);
|
|
16178
|
+
if (output.outline) setOutline(output.outline);
|
|
16179
|
+
else if (output.deck_plan) setOutline(output.deck_plan);
|
|
16180
|
+
if (!isOutlinePhase && !hasNotifiedRef.current && onCompleteRef.current) {
|
|
16118
16181
|
hasNotifiedRef.current = true;
|
|
16119
16182
|
onCompleteRef.current({
|
|
16120
16183
|
title: newTitle,
|
|
@@ -16134,6 +16197,184 @@ var PresentationJobCard = ({
|
|
|
16134
16197
|
}
|
|
16135
16198
|
// Transient fetch errors are ignored — the shared loop retries next tick.
|
|
16136
16199
|
);
|
|
16200
|
+
const applyDeckOutput = useCallback4((out, opts) => {
|
|
16201
|
+
if (out.title) setTitle(out.title);
|
|
16202
|
+
if (out.slide_count !== void 0) setSlideCount(out.slide_count);
|
|
16203
|
+
if (out.formats) setFormats(out.formats);
|
|
16204
|
+
if (out.generation_mode) setGenerationMode(out.generation_mode);
|
|
16205
|
+
if (out.template_id) setTemplateId(out.template_id);
|
|
16206
|
+
setReviewStatus(out.review_status || "pending_review");
|
|
16207
|
+
setTemplateVersionId(opts?.regenerated ? "" : out.template_version_id || "");
|
|
16208
|
+
if (!opts?.regenerated && !hasNotifiedRef.current && onCompleteRef.current) {
|
|
16209
|
+
hasNotifiedRef.current = true;
|
|
16210
|
+
onCompleteRef.current({
|
|
16211
|
+
title: out.title ?? title,
|
|
16212
|
+
slide_count: out.slide_count ?? slideCount,
|
|
16213
|
+
formats: out.formats ?? formats
|
|
16214
|
+
});
|
|
16215
|
+
}
|
|
16216
|
+
}, [title, slideCount, formats]);
|
|
16217
|
+
const refetchSourceAndApply = useCallback4(async () => {
|
|
16218
|
+
if (!pollUrl) return;
|
|
16219
|
+
try {
|
|
16220
|
+
const headers = {};
|
|
16221
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
16222
|
+
const res = await fetch(pollUrl, { headers });
|
|
16223
|
+
if (!res.ok) return;
|
|
16224
|
+
const data = await res.json();
|
|
16225
|
+
if (data.output) applyDeckOutput(data.output);
|
|
16226
|
+
} catch {
|
|
16227
|
+
}
|
|
16228
|
+
}, [pollUrl, authToken, applyDeckOutput]);
|
|
16229
|
+
useSharedPoll(
|
|
16230
|
+
{
|
|
16231
|
+
key: outlineWritePollUrl,
|
|
16232
|
+
intervalMs: 3e3,
|
|
16233
|
+
fetcher: async () => {
|
|
16234
|
+
const headers = {};
|
|
16235
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
16236
|
+
const res = await fetch(outlineWritePollUrl, { headers });
|
|
16237
|
+
if (!res.ok) throw new Error(`poll ${res.status}`);
|
|
16238
|
+
return res.json();
|
|
16239
|
+
},
|
|
16240
|
+
shouldContinue: (data) => data.status !== "complete" && data.status !== "failed"
|
|
16241
|
+
},
|
|
16242
|
+
(data) => {
|
|
16243
|
+
if (data.status === "complete") {
|
|
16244
|
+
setOutlineWritePollUrl(null);
|
|
16245
|
+
setApprovingOutline(false);
|
|
16246
|
+
void refetchSourceAndApply();
|
|
16247
|
+
} else if (data.status === "failed") {
|
|
16248
|
+
setApproveError(data.error || "Could not build the presentation");
|
|
16249
|
+
setOutlineWritePollUrl(null);
|
|
16250
|
+
setApprovingOutline(false);
|
|
16251
|
+
}
|
|
16252
|
+
}
|
|
16253
|
+
);
|
|
16254
|
+
useSharedPoll(
|
|
16255
|
+
{
|
|
16256
|
+
key: regenPollUrl,
|
|
16257
|
+
intervalMs: 3e3,
|
|
16258
|
+
fetcher: async () => {
|
|
16259
|
+
const headers = {};
|
|
16260
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
16261
|
+
const res = await fetch(regenPollUrl, { headers });
|
|
16262
|
+
if (!res.ok) throw new Error(`poll ${res.status}`);
|
|
16263
|
+
return res.json();
|
|
16264
|
+
},
|
|
16265
|
+
shouldContinue: (data) => data.status !== "complete" && data.status !== "failed"
|
|
16266
|
+
},
|
|
16267
|
+
(data) => {
|
|
16268
|
+
if (data.status === "complete") {
|
|
16269
|
+
if (data.output) applyDeckOutput(data.output, { regenerated: true });
|
|
16270
|
+
setRegenPollUrl(null);
|
|
16271
|
+
setRegenerating(false);
|
|
16272
|
+
} else if (data.status === "failed") {
|
|
16273
|
+
setApproveError(data.error || "Could not switch template");
|
|
16274
|
+
setRegenPollUrl(null);
|
|
16275
|
+
setRegenerating(false);
|
|
16276
|
+
}
|
|
16277
|
+
}
|
|
16278
|
+
);
|
|
16279
|
+
const buildEditedOutline = () => {
|
|
16280
|
+
if (!outline || !Array.isArray(outline.slides)) return void 0;
|
|
16281
|
+
let changed = false;
|
|
16282
|
+
const slides = outline.slides.map((s, i) => {
|
|
16283
|
+
const edited = messageEdits[i];
|
|
16284
|
+
if (edited !== void 0 && edited !== s.message) {
|
|
16285
|
+
changed = true;
|
|
16286
|
+
return { ...s, message: edited };
|
|
16287
|
+
}
|
|
16288
|
+
return s;
|
|
16289
|
+
});
|
|
16290
|
+
return changed ? { ...outline, slides } : void 0;
|
|
16291
|
+
};
|
|
16292
|
+
const handleApproveOutline = async () => {
|
|
16293
|
+
if (!_job_id || approvingOutline) return;
|
|
16294
|
+
const endpoint = approveOutlineUrl || `/api/presentations/${_job_id}/approve-outline`;
|
|
16295
|
+
setApprovingOutline(true);
|
|
16296
|
+
setApproveError(null);
|
|
16297
|
+
try {
|
|
16298
|
+
const headers = { "Content-Type": "application/json" };
|
|
16299
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
16300
|
+
const editedOutline = buildEditedOutline();
|
|
16301
|
+
const res = await fetch(endpoint, {
|
|
16302
|
+
method: "POST",
|
|
16303
|
+
headers,
|
|
16304
|
+
body: JSON.stringify(editedOutline ? { outline: editedOutline } : {})
|
|
16305
|
+
});
|
|
16306
|
+
if (!res.ok) {
|
|
16307
|
+
const err = await res.json().catch(() => ({}));
|
|
16308
|
+
throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
|
|
16309
|
+
}
|
|
16310
|
+
const data = await res.json();
|
|
16311
|
+
if (typeof data.job_id === "string" && data.job_id !== _job_id) {
|
|
16312
|
+
setOutlineWritePollUrl(deriveJobStatusUrl(data.job_id, pollUrl, regenerateUrl));
|
|
16313
|
+
return;
|
|
16314
|
+
}
|
|
16315
|
+
setApprovingOutline(false);
|
|
16316
|
+
void refetchSourceAndApply();
|
|
16317
|
+
} catch (e) {
|
|
16318
|
+
setApproveError(e instanceof Error ? e.message : "Could not build the presentation");
|
|
16319
|
+
setApprovingOutline(false);
|
|
16320
|
+
}
|
|
16321
|
+
};
|
|
16322
|
+
const handleApprove = async () => {
|
|
16323
|
+
if (!_job_id || approving) return;
|
|
16324
|
+
const endpoint = approveUrl || `/api/presentations/${_job_id}/approve-standard`;
|
|
16325
|
+
setApproving(true);
|
|
16326
|
+
setApproveError(null);
|
|
16327
|
+
try {
|
|
16328
|
+
const headers = { "Content-Type": "application/json" };
|
|
16329
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
16330
|
+
const res = await fetch(endpoint, {
|
|
16331
|
+
method: "POST",
|
|
16332
|
+
headers,
|
|
16333
|
+
body: JSON.stringify({ label: title })
|
|
16334
|
+
});
|
|
16335
|
+
if (!res.ok) {
|
|
16336
|
+
const err = await res.json().catch(() => ({}));
|
|
16337
|
+
throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
|
|
16338
|
+
}
|
|
16339
|
+
const data = await res.json();
|
|
16340
|
+
setReviewStatus(data.review_status || "approved");
|
|
16341
|
+
if (data.template_version_id) setTemplateVersionId(data.template_version_id);
|
|
16342
|
+
if (data.template_id) setTemplateId(data.template_id);
|
|
16343
|
+
} catch (e) {
|
|
16344
|
+
setApproveError(e instanceof Error ? e.message : "Could not save the format");
|
|
16345
|
+
} finally {
|
|
16346
|
+
setApproving(false);
|
|
16347
|
+
}
|
|
16348
|
+
};
|
|
16349
|
+
const handleRegenerate = async () => {
|
|
16350
|
+
if (!_job_id || regenerating) return;
|
|
16351
|
+
const endpoint = regenerateUrl || `/api/presentations/${_job_id}/regenerate`;
|
|
16352
|
+
setRegenerating(true);
|
|
16353
|
+
setApproveError(null);
|
|
16354
|
+
try {
|
|
16355
|
+
const headers = { "Content-Type": "application/json" };
|
|
16356
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
16357
|
+
const res = await fetch(endpoint, {
|
|
16358
|
+
method: "POST",
|
|
16359
|
+
headers,
|
|
16360
|
+
body: JSON.stringify({})
|
|
16361
|
+
});
|
|
16362
|
+
if (!res.ok) {
|
|
16363
|
+
const err = await res.json().catch(() => ({}));
|
|
16364
|
+
throw new Error(err.detail || err.error || `Regenerate failed (${res.status})`);
|
|
16365
|
+
}
|
|
16366
|
+
const data = await res.json();
|
|
16367
|
+
if (typeof data.job_id === "string" && data.job_id !== _job_id) {
|
|
16368
|
+
setRegenPollUrl(deriveJobStatusUrl(data.job_id, pollUrl, regenerateUrl));
|
|
16369
|
+
return;
|
|
16370
|
+
}
|
|
16371
|
+
applyDeckOutput(data.output || data, { regenerated: true });
|
|
16372
|
+
setRegenerating(false);
|
|
16373
|
+
} catch (e) {
|
|
16374
|
+
setApproveError(e instanceof Error ? e.message : "Could not switch template");
|
|
16375
|
+
setRegenerating(false);
|
|
16376
|
+
}
|
|
16377
|
+
};
|
|
16137
16378
|
const handleShare = async () => {
|
|
16138
16379
|
const link = shareUrl ?? (typeof window !== "undefined" && _job_id ? `${window.location.origin}/p/${_job_id}` : formats.html_url);
|
|
16139
16380
|
if (!link) return;
|
|
@@ -16254,8 +16495,85 @@ var PresentationJobCard = ({
|
|
|
16254
16495
|
] })
|
|
16255
16496
|
] }) }) });
|
|
16256
16497
|
}
|
|
16498
|
+
if (status === "complete" && reviewStatus === "pending_outline_approval") {
|
|
16499
|
+
const slides = Array.isArray(outline?.slides) ? outline.slides : [];
|
|
16500
|
+
const building = Boolean(outlineWritePollUrl) || approvingOutline;
|
|
16501
|
+
return /* @__PURE__ */ jsx147("div", { className: cn("w-full", className), children: /* @__PURE__ */ jsxs108("div", { className: "bg-zinc-900 border border-zinc-800 rounded-2xl overflow-hidden", style: t.root, children: [
|
|
16502
|
+
/* @__PURE__ */ jsx147(
|
|
16503
|
+
"div",
|
|
16504
|
+
{
|
|
16505
|
+
className: "h-[3px]",
|
|
16506
|
+
style: { background: accentGradient || "linear-gradient(90deg, #8B7D5E, #C0AE82, #DCC99B)" }
|
|
16507
|
+
}
|
|
16508
|
+
),
|
|
16509
|
+
/* @__PURE__ */ jsxs108("div", { className: "p-5", children: [
|
|
16510
|
+
/* @__PURE__ */ jsxs108("div", { className: "flex items-start gap-3", children: [
|
|
16511
|
+
/* @__PURE__ */ jsx147(
|
|
16512
|
+
"div",
|
|
16513
|
+
{
|
|
16514
|
+
className: "w-10 h-10 rounded-xl bg-indigo-500/10 border border-indigo-500/20 flex items-center justify-center flex-shrink-0 text-indigo-400",
|
|
16515
|
+
style: { ...t.accentSubtle(0.1), ...t.accentBorder, ...t.accent },
|
|
16516
|
+
children: /* @__PURE__ */ jsx147(SlidesIcon, {})
|
|
16517
|
+
}
|
|
16518
|
+
),
|
|
16519
|
+
/* @__PURE__ */ jsxs108("div", { className: "flex-1 min-w-0", children: [
|
|
16520
|
+
/* @__PURE__ */ jsx147("h3", { className: "text-sm font-semibold text-zinc-100 truncate leading-tight", style: t.text, children: title || "Presentation outline" }),
|
|
16521
|
+
/* @__PURE__ */ jsxs108("p", { className: "text-xs text-amber-400/90 mt-0.5", children: [
|
|
16522
|
+
"Review the slide outline",
|
|
16523
|
+
slides.length > 0 ? " \u2014 edit any slide message" : "",
|
|
16524
|
+
", then build the deck."
|
|
16525
|
+
] })
|
|
16526
|
+
] })
|
|
16527
|
+
] }),
|
|
16528
|
+
/* @__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)),
|
|
16552
|
+
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
|
+
] }),
|
|
16554
|
+
approveError && /* @__PURE__ */ jsx147("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
|
|
16555
|
+
/* @__PURE__ */ jsx147("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs108(
|
|
16556
|
+
"button",
|
|
16557
|
+
{
|
|
16558
|
+
onClick: handleApproveOutline,
|
|
16559
|
+
disabled: building,
|
|
16560
|
+
title: "Approve this outline and build the full deck",
|
|
16561
|
+
className: "flex items-center gap-1.5 px-3 h-8 rounded-lg border border-emerald-700/50 bg-emerald-950/40 hover:border-emerald-500 text-emerald-300 text-xs font-medium transition-colors disabled:opacity-50",
|
|
16562
|
+
children: [
|
|
16563
|
+
/* @__PURE__ */ jsx147(CheckIcon, {}),
|
|
16564
|
+
/* @__PURE__ */ jsx147("span", { children: building ? "Building deck..." : "Approve & Build" })
|
|
16565
|
+
]
|
|
16566
|
+
}
|
|
16567
|
+
) })
|
|
16568
|
+
] })
|
|
16569
|
+
] }) });
|
|
16570
|
+
}
|
|
16257
16571
|
const total = slideCount || 1;
|
|
16258
16572
|
const hasHtml = Boolean(formats.html_url);
|
|
16573
|
+
const templateLabel = formatTemplateLabel(templateId);
|
|
16574
|
+
const isApproved = reviewStatus === "approved";
|
|
16575
|
+
const canApprove = !hideApproval && generationMode === "template" && reviewStatus === "pending_review" && Boolean(_job_id);
|
|
16576
|
+
const canRegenerate = canApprove;
|
|
16259
16577
|
return /* @__PURE__ */ jsxs108(Fragment5, { children: [
|
|
16260
16578
|
/* @__PURE__ */ jsx147("div", { className: cn("w-full", className), children: /* @__PURE__ */ jsxs108("div", { className: "bg-zinc-900 border border-zinc-800 rounded-2xl overflow-hidden", style: t.root, children: [
|
|
16261
16579
|
/* @__PURE__ */ jsx147("div", { className: "h-[3px]", style: { background: accentGradient || "linear-gradient(90deg, #8B7D5E, #C0AE82, #DCC99B)" } }),
|
|
@@ -16273,10 +16591,39 @@ var PresentationJobCard = ({
|
|
|
16273
16591
|
/* @__PURE__ */ jsx147("h3", { className: "text-sm font-semibold text-zinc-100 truncate leading-tight", style: t.text, children: title }),
|
|
16274
16592
|
/* @__PURE__ */ jsxs108("p", { className: "text-xs text-zinc-500 mt-0.5", style: t.muted, children: [
|
|
16275
16593
|
slideCount ? `${slideCount} slides` : "Presentation",
|
|
16276
|
-
" \xB7 Ready to export"
|
|
16277
|
-
|
|
16594
|
+
" \xB7 Ready to export",
|
|
16595
|
+
templateLabel && ` \xB7 ${templateLabel}`
|
|
16596
|
+
] }),
|
|
16597
|
+
generationMode === "template" && /* @__PURE__ */ jsx147("p", { className: "text-xs mt-1", children: isApproved ? /* @__PURE__ */ jsx147("span", { className: "text-emerald-400/90", children: "Approved \u2014 future decks use this format" }) : reviewStatus === "pending_review" ? /* @__PURE__ */ jsx147("span", { className: "text-amber-400/90", children: "Review this deck, then approve to lock the format" }) : null })
|
|
16278
16598
|
] }),
|
|
16279
|
-
/* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-1.5 flex-shrink-0", children: [
|
|
16599
|
+
/* @__PURE__ */ jsxs108("div", { className: "flex items-center gap-1.5 flex-shrink-0 flex-wrap justify-end", children: [
|
|
16600
|
+
canRegenerate && /* @__PURE__ */ jsxs108(
|
|
16601
|
+
"button",
|
|
16602
|
+
{
|
|
16603
|
+
onClick: handleRegenerate,
|
|
16604
|
+
disabled: regenerating || approving,
|
|
16605
|
+
title: "Try another design template with the same content",
|
|
16606
|
+
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-300 text-xs font-medium transition-colors disabled:opacity-50",
|
|
16607
|
+
children: [
|
|
16608
|
+
/* @__PURE__ */ jsx147(RefreshIcon, {}),
|
|
16609
|
+
/* @__PURE__ */ jsx147("span", { children: regenerating ? "Switching..." : "Regenerate" })
|
|
16610
|
+
]
|
|
16611
|
+
}
|
|
16612
|
+
),
|
|
16613
|
+
canApprove && /* @__PURE__ */ jsxs108(
|
|
16614
|
+
"button",
|
|
16615
|
+
{
|
|
16616
|
+
onClick: handleApprove,
|
|
16617
|
+
disabled: approving,
|
|
16618
|
+
title: "Approve and save this deck format for future runs",
|
|
16619
|
+
className: "flex items-center gap-1.5 px-2.5 h-7 rounded-lg border border-emerald-700/50 bg-emerald-950/40 hover:border-emerald-500 text-emerald-300 text-xs font-medium transition-colors disabled:opacity-50",
|
|
16620
|
+
children: [
|
|
16621
|
+
/* @__PURE__ */ jsx147(CheckIcon, {}),
|
|
16622
|
+
/* @__PURE__ */ jsx147("span", { children: approving ? "Saving..." : "Approve" })
|
|
16623
|
+
]
|
|
16624
|
+
}
|
|
16625
|
+
),
|
|
16626
|
+
isApproved && /* @__PURE__ */ jsx147("span", { className: "flex items-center justify-center w-7 h-7 rounded-lg border border-emerald-500/40 bg-emerald-500/10 text-emerald-400", title: "Format saved", children: /* @__PURE__ */ jsx147(CheckIcon, {}) }),
|
|
16280
16627
|
/* @__PURE__ */ jsx147(
|
|
16281
16628
|
"button",
|
|
16282
16629
|
{
|
|
@@ -16318,6 +16665,7 @@ var PresentationJobCard = ({
|
|
|
16318
16665
|
)
|
|
16319
16666
|
] })
|
|
16320
16667
|
] }),
|
|
16668
|
+
approveError && /* @__PURE__ */ jsx147("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
|
|
16321
16669
|
hasHtml && /* @__PURE__ */ jsxs108("div", { className: "mt-4", children: [
|
|
16322
16670
|
/* @__PURE__ */ jsxs108(
|
|
16323
16671
|
"div",
|
|
@@ -16452,11 +16800,11 @@ var ShareIcon2 = () => /* @__PURE__ */ jsxs109("svg", { width: "14", height: "14
|
|
|
16452
16800
|
/* @__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" })
|
|
16453
16801
|
] });
|
|
16454
16802
|
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" }) });
|
|
16455
|
-
var
|
|
16803
|
+
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: [
|
|
16456
16804
|
/* @__PURE__ */ jsx148("polyline", { points: "23 4 23 10 17 10" }),
|
|
16457
16805
|
/* @__PURE__ */ jsx148("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
|
|
16458
16806
|
] });
|
|
16459
|
-
function
|
|
16807
|
+
function formatTemplateLabel2(templateId) {
|
|
16460
16808
|
if (!templateId) return null;
|
|
16461
16809
|
return templateId.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
16462
16810
|
}
|
|
@@ -16503,6 +16851,10 @@ var FullscreenPreviewModal = ({ url, title, onClose }) => {
|
|
|
16503
16851
|
) })
|
|
16504
16852
|
] });
|
|
16505
16853
|
};
|
|
16854
|
+
function deriveJobStatusUrl2(newJobId, pollUrl, regenerateUrl) {
|
|
16855
|
+
const base = pollUrl?.replace(/\/api\/jobs\/[^/]+\/status.*$/, "") ?? regenerateUrl?.replace(/\/api\/reports\/[^/]+\/regenerate.*$/, "") ?? "";
|
|
16856
|
+
return `${base}/api/jobs/${newJobId}/status`;
|
|
16857
|
+
}
|
|
16506
16858
|
var ResearchReportJobCard = (props) => {
|
|
16507
16859
|
const {
|
|
16508
16860
|
job_id,
|
|
@@ -16520,6 +16872,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16520
16872
|
template_id: initialTemplateId,
|
|
16521
16873
|
template_version_id: initialTemplateVersionId,
|
|
16522
16874
|
review_status: initialReviewStatus,
|
|
16875
|
+
outline: initialOutline,
|
|
16523
16876
|
theme: initialTheme,
|
|
16524
16877
|
error: initialError,
|
|
16525
16878
|
progress: initialProgress,
|
|
@@ -16527,7 +16880,9 @@ var ResearchReportJobCard = (props) => {
|
|
|
16527
16880
|
authToken,
|
|
16528
16881
|
shareUrl,
|
|
16529
16882
|
approveUrl,
|
|
16883
|
+
approveOutlineUrl,
|
|
16530
16884
|
regenerateUrl,
|
|
16885
|
+
hideApproval = false,
|
|
16531
16886
|
className,
|
|
16532
16887
|
onComplete,
|
|
16533
16888
|
onFailed,
|
|
@@ -16550,6 +16905,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16550
16905
|
const [reviewStatus, setReviewStatus] = useState11(
|
|
16551
16906
|
initialReviewStatus || (initialHtmlUrl ? "pending_review" : "")
|
|
16552
16907
|
);
|
|
16908
|
+
const [outline, setOutline] = useState11(initialOutline);
|
|
16553
16909
|
const [theme, setTheme] = useState11(initialTheme || DEFAULT_THEME);
|
|
16554
16910
|
const [error, setError] = useState11(initialError);
|
|
16555
16911
|
const [progress, setProgress] = useState11(initialProgress);
|
|
@@ -16559,6 +16915,10 @@ var ResearchReportJobCard = (props) => {
|
|
|
16559
16915
|
const [approving, setApproving] = useState11(false);
|
|
16560
16916
|
const [regenerating, setRegenerating] = useState11(false);
|
|
16561
16917
|
const [approveError, setApproveError] = useState11(null);
|
|
16918
|
+
const [headingEdits, setHeadingEdits] = useState11({});
|
|
16919
|
+
const [approvingOutline, setApprovingOutline] = useState11(false);
|
|
16920
|
+
const [outlineWritePollUrl, setOutlineWritePollUrl] = useState11(null);
|
|
16921
|
+
const [regenPollUrl, setRegenPollUrl] = useState11(null);
|
|
16562
16922
|
const previewRef = useRef7(null);
|
|
16563
16923
|
const onCompleteRef = useRef7(onComplete);
|
|
16564
16924
|
const onFailedRef = useRef7(onFailed);
|
|
@@ -16587,6 +16947,10 @@ var ResearchReportJobCard = (props) => {
|
|
|
16587
16947
|
useEffect8(() => {
|
|
16588
16948
|
if (initialReviewStatus) setReviewStatus(initialReviewStatus);
|
|
16589
16949
|
}, [initialReviewStatus]);
|
|
16950
|
+
const initialOutlineSectionCount = initialOutline?.sections?.length;
|
|
16951
|
+
useEffect8(() => {
|
|
16952
|
+
if (initialOutline) setOutline(initialOutline);
|
|
16953
|
+
}, [initialOutlineSectionCount]);
|
|
16590
16954
|
useEffect8(() => {
|
|
16591
16955
|
if (initialDepth) setDepth(initialDepth);
|
|
16592
16956
|
}, [initialDepth]);
|
|
@@ -16652,6 +17016,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16652
17016
|
}
|
|
16653
17017
|
if (newStatus === "complete" && data.output) {
|
|
16654
17018
|
const output = data.output;
|
|
17019
|
+
const isOutlinePhase = output.review_status === "pending_outline_approval" || output.type === "research-report-outline";
|
|
16655
17020
|
setTitle(output.title || initialTitle);
|
|
16656
17021
|
setDepth(output.depth || "");
|
|
16657
17022
|
setSectionCount(output.section_count || 0);
|
|
@@ -16663,10 +17028,11 @@ var ResearchReportJobCard = (props) => {
|
|
|
16663
17028
|
if (output.template_id) setTemplateId(output.template_id);
|
|
16664
17029
|
if (output.template_version_id) setTemplateVersionId(output.template_version_id);
|
|
16665
17030
|
if (output.review_status) setReviewStatus(output.review_status);
|
|
17031
|
+
if (output.outline) setOutline(output.outline);
|
|
16666
17032
|
if (output.theme) {
|
|
16667
17033
|
setTheme(output.theme);
|
|
16668
17034
|
}
|
|
16669
|
-
if (!hasNotifiedRef.current && onCompleteRef.current) {
|
|
17035
|
+
if (!isOutlinePhase && !hasNotifiedRef.current && onCompleteRef.current) {
|
|
16670
17036
|
hasNotifiedRef.current = true;
|
|
16671
17037
|
onCompleteRef.current(output);
|
|
16672
17038
|
}
|
|
@@ -16682,6 +17048,156 @@ var ResearchReportJobCard = (props) => {
|
|
|
16682
17048
|
}
|
|
16683
17049
|
// Transient fetch errors are ignored — the shared loop retries next tick.
|
|
16684
17050
|
);
|
|
17051
|
+
const applyRegeneratedOutput = useCallback5(
|
|
17052
|
+
(out) => {
|
|
17053
|
+
if (out.html_url) setHtmlUrl(out.html_url);
|
|
17054
|
+
if (out.template_id) setTemplateId(out.template_id);
|
|
17055
|
+
setReviewStatus("pending_review");
|
|
17056
|
+
setTemplateVersionId("");
|
|
17057
|
+
if (out.section_count !== void 0) setSectionCount(out.section_count);
|
|
17058
|
+
if (out.title) setTitle(out.title);
|
|
17059
|
+
if (out.depth) setDepth(out.depth);
|
|
17060
|
+
if (out.theme) setTheme(out.theme);
|
|
17061
|
+
if (!onCompleteRef.current) return;
|
|
17062
|
+
onCompleteRef.current({
|
|
17063
|
+
title: out.title ?? title,
|
|
17064
|
+
depth: out.depth ?? depth,
|
|
17065
|
+
section_count: out.section_count ?? sectionCount,
|
|
17066
|
+
source_count: out.source_count ?? sourceCount,
|
|
17067
|
+
word_count: out.word_count ?? wordCount,
|
|
17068
|
+
executive_summary: out.executive_summary ?? summary,
|
|
17069
|
+
html_url: out.html_url ?? htmlUrl,
|
|
17070
|
+
generation_mode: "template",
|
|
17071
|
+
template_id: out.template_id ?? templateId,
|
|
17072
|
+
template_version_id: void 0,
|
|
17073
|
+
review_status: "pending_review",
|
|
17074
|
+
theme: out.theme ?? theme
|
|
17075
|
+
});
|
|
17076
|
+
},
|
|
17077
|
+
[title, depth, sectionCount, sourceCount, wordCount, summary, htmlUrl, templateId, theme]
|
|
17078
|
+
);
|
|
17079
|
+
useSharedPoll(
|
|
17080
|
+
{
|
|
17081
|
+
key: regenPollUrl,
|
|
17082
|
+
intervalMs: 3e3,
|
|
17083
|
+
fetcher: async () => {
|
|
17084
|
+
const headers = {};
|
|
17085
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
17086
|
+
const res = await fetch(regenPollUrl, { headers });
|
|
17087
|
+
if (!res.ok) throw new Error(`poll ${res.status}`);
|
|
17088
|
+
return res.json();
|
|
17089
|
+
},
|
|
17090
|
+
shouldContinue: (data) => data.status !== "complete" && data.status !== "failed"
|
|
17091
|
+
},
|
|
17092
|
+
(data) => {
|
|
17093
|
+
if (data.status === "complete") {
|
|
17094
|
+
if (data.output) applyRegeneratedOutput(data.output);
|
|
17095
|
+
setRegenPollUrl(null);
|
|
17096
|
+
setRegenerating(false);
|
|
17097
|
+
} else if (data.status === "failed") {
|
|
17098
|
+
setApproveError(data.error || "Could not switch template");
|
|
17099
|
+
setRegenPollUrl(null);
|
|
17100
|
+
setRegenerating(false);
|
|
17101
|
+
}
|
|
17102
|
+
}
|
|
17103
|
+
);
|
|
17104
|
+
const refetchSourceAndApply = useCallback5(async () => {
|
|
17105
|
+
if (!pollUrl) return;
|
|
17106
|
+
try {
|
|
17107
|
+
const headers = {};
|
|
17108
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
17109
|
+
const res = await fetch(pollUrl, { headers });
|
|
17110
|
+
if (!res.ok) return;
|
|
17111
|
+
const data = await res.json();
|
|
17112
|
+
const out = data.output;
|
|
17113
|
+
if (!out) return;
|
|
17114
|
+
if (out.title) setTitle(out.title);
|
|
17115
|
+
if (out.depth) setDepth(out.depth);
|
|
17116
|
+
if (out.section_count !== void 0) setSectionCount(out.section_count);
|
|
17117
|
+
if (out.source_count !== void 0) setSourceCount(out.source_count);
|
|
17118
|
+
if (out.word_count !== void 0) setWordCount(out.word_count);
|
|
17119
|
+
if (out.executive_summary) setSummary(out.executive_summary);
|
|
17120
|
+
if (out.html_url) setHtmlUrl(out.html_url);
|
|
17121
|
+
if (out.generation_mode) setGenerationMode(out.generation_mode);
|
|
17122
|
+
if (out.template_id) setTemplateId(out.template_id);
|
|
17123
|
+
setTemplateVersionId(out.template_version_id || "");
|
|
17124
|
+
setReviewStatus(out.review_status || "pending_review");
|
|
17125
|
+
if (out.theme) setTheme(out.theme);
|
|
17126
|
+
if (!hasNotifiedRef.current && onCompleteRef.current) {
|
|
17127
|
+
hasNotifiedRef.current = true;
|
|
17128
|
+
onCompleteRef.current(out);
|
|
17129
|
+
}
|
|
17130
|
+
} catch {
|
|
17131
|
+
}
|
|
17132
|
+
}, [pollUrl, authToken]);
|
|
17133
|
+
useSharedPoll(
|
|
17134
|
+
{
|
|
17135
|
+
key: outlineWritePollUrl,
|
|
17136
|
+
intervalMs: 3e3,
|
|
17137
|
+
fetcher: async () => {
|
|
17138
|
+
const headers = {};
|
|
17139
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
17140
|
+
const res = await fetch(outlineWritePollUrl, { headers });
|
|
17141
|
+
if (!res.ok) throw new Error(`poll ${res.status}`);
|
|
17142
|
+
return res.json();
|
|
17143
|
+
},
|
|
17144
|
+
shouldContinue: (data) => data.status !== "complete" && data.status !== "failed"
|
|
17145
|
+
},
|
|
17146
|
+
(data) => {
|
|
17147
|
+
if (data.status === "complete") {
|
|
17148
|
+
setOutlineWritePollUrl(null);
|
|
17149
|
+
setApprovingOutline(false);
|
|
17150
|
+
void refetchSourceAndApply();
|
|
17151
|
+
} else if (data.status === "failed") {
|
|
17152
|
+
setApproveError(data.error || "Could not build the report");
|
|
17153
|
+
setOutlineWritePollUrl(null);
|
|
17154
|
+
setApprovingOutline(false);
|
|
17155
|
+
}
|
|
17156
|
+
}
|
|
17157
|
+
);
|
|
17158
|
+
const buildEditedOutline = () => {
|
|
17159
|
+
if (!outline || !Array.isArray(outline.sections)) return void 0;
|
|
17160
|
+
let changed = false;
|
|
17161
|
+
const sections = outline.sections.map((s, i) => {
|
|
17162
|
+
const edited = headingEdits[i];
|
|
17163
|
+
if (edited !== void 0 && edited !== s.heading) {
|
|
17164
|
+
changed = true;
|
|
17165
|
+
return { ...s, heading: edited };
|
|
17166
|
+
}
|
|
17167
|
+
return s;
|
|
17168
|
+
});
|
|
17169
|
+
return changed ? { ...outline, sections } : void 0;
|
|
17170
|
+
};
|
|
17171
|
+
const handleApproveOutline = async () => {
|
|
17172
|
+
if (!job_id || approvingOutline) return;
|
|
17173
|
+
const endpoint = approveOutlineUrl || `/api/reports/${job_id}/approve-outline`;
|
|
17174
|
+
setApprovingOutline(true);
|
|
17175
|
+
setApproveError(null);
|
|
17176
|
+
try {
|
|
17177
|
+
const headers = { "Content-Type": "application/json" };
|
|
17178
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
17179
|
+
const editedOutline = buildEditedOutline();
|
|
17180
|
+
const res = await fetch(endpoint, {
|
|
17181
|
+
method: "POST",
|
|
17182
|
+
headers,
|
|
17183
|
+
body: JSON.stringify(editedOutline ? { outline: editedOutline } : {})
|
|
17184
|
+
});
|
|
17185
|
+
if (!res.ok) {
|
|
17186
|
+
const err = await res.json().catch(() => ({}));
|
|
17187
|
+
throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
|
|
17188
|
+
}
|
|
17189
|
+
const data = await res.json();
|
|
17190
|
+
if (typeof data.job_id === "string" && data.job_id !== job_id) {
|
|
17191
|
+
setOutlineWritePollUrl(deriveJobStatusUrl2(data.job_id, pollUrl, regenerateUrl));
|
|
17192
|
+
return;
|
|
17193
|
+
}
|
|
17194
|
+
setApprovingOutline(false);
|
|
17195
|
+
void refetchSourceAndApply();
|
|
17196
|
+
} catch (e) {
|
|
17197
|
+
setApproveError(e instanceof Error ? e.message : "Could not build the report");
|
|
17198
|
+
setApprovingOutline(false);
|
|
17199
|
+
}
|
|
17200
|
+
};
|
|
16685
17201
|
const formatWordCount = (count) => {
|
|
16686
17202
|
if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
|
|
16687
17203
|
return count.toString();
|
|
@@ -16787,6 +17303,61 @@ var ResearchReportJobCard = (props) => {
|
|
|
16787
17303
|
] })
|
|
16788
17304
|
] }) }) });
|
|
16789
17305
|
}
|
|
17306
|
+
if (status === "complete" && reviewStatus === "pending_outline_approval") {
|
|
17307
|
+
const sections = Array.isArray(outline?.sections) ? outline.sections : [];
|
|
17308
|
+
const building = Boolean(outlineWritePollUrl) || approvingOutline;
|
|
17309
|
+
return /* @__PURE__ */ jsx148("div", { className: cn("w-full", className), children: /* @__PURE__ */ jsxs109("div", { className: "bg-zinc-900 border border-zinc-800 rounded-2xl overflow-hidden", children: [
|
|
17310
|
+
/* @__PURE__ */ jsx148(
|
|
17311
|
+
"div",
|
|
17312
|
+
{
|
|
17313
|
+
className: "h-[3px]",
|
|
17314
|
+
style: { background: "linear-gradient(90deg, #8B7D5E, #C0AE82, #DCC99B)" }
|
|
17315
|
+
}
|
|
17316
|
+
),
|
|
17317
|
+
/* @__PURE__ */ jsxs109("div", { className: "p-5", children: [
|
|
17318
|
+
/* @__PURE__ */ jsxs109("div", { className: "flex items-start gap-3", children: [
|
|
17319
|
+
/* @__PURE__ */ jsx148("div", { className: "w-10 h-10 rounded-xl bg-violet-500/10 border border-violet-500/20 flex items-center justify-center flex-shrink-0 text-violet-300", children: /* @__PURE__ */ jsx148(DocumentIcon, {}) }),
|
|
17320
|
+
/* @__PURE__ */ jsxs109("div", { className: "flex-1 min-w-0", children: [
|
|
17321
|
+
/* @__PURE__ */ jsx148("h3", { className: "text-sm font-semibold text-zinc-100 truncate leading-tight", children: title || "Report outline" }),
|
|
17322
|
+
/* @__PURE__ */ jsxs109("p", { className: "text-xs text-amber-400/90 mt-0.5", children: [
|
|
17323
|
+
"Review the outline",
|
|
17324
|
+
sections.length > 0 ? " \u2014 edit any heading" : "",
|
|
17325
|
+
", then build the full report."
|
|
17326
|
+
] })
|
|
17327
|
+
] })
|
|
17328
|
+
] }),
|
|
17329
|
+
/* @__PURE__ */ jsxs109("div", { className: "mt-4 space-y-2", children: [
|
|
17330
|
+
sections.map((s, i) => /* @__PURE__ */ jsxs109("div", { className: "flex items-center gap-2", children: [
|
|
17331
|
+
/* @__PURE__ */ jsx148("span", { className: "text-xs text-zinc-600 w-5 text-right tabular-nums flex-shrink-0", children: i + 1 }),
|
|
17332
|
+
/* @__PURE__ */ jsx148(
|
|
17333
|
+
"input",
|
|
17334
|
+
{
|
|
17335
|
+
value: headingEdits[i] ?? s.heading ?? "",
|
|
17336
|
+
onChange: (e) => setHeadingEdits((prev) => ({ ...prev, [i]: e.target.value })),
|
|
17337
|
+
disabled: building,
|
|
17338
|
+
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"
|
|
17339
|
+
}
|
|
17340
|
+
)
|
|
17341
|
+
] }, i)),
|
|
17342
|
+
sections.length === 0 && /* @__PURE__ */ jsx148("p", { className: "text-xs text-zinc-500", children: "No outline sections were returned; you can still build the report." })
|
|
17343
|
+
] }),
|
|
17344
|
+
approveError && /* @__PURE__ */ jsx148("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
|
|
17345
|
+
/* @__PURE__ */ jsx148("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs109(
|
|
17346
|
+
"button",
|
|
17347
|
+
{
|
|
17348
|
+
onClick: handleApproveOutline,
|
|
17349
|
+
disabled: building,
|
|
17350
|
+
title: "Approve this outline and build the full report",
|
|
17351
|
+
className: "flex items-center gap-1.5 px-3 h-8 rounded-lg border border-emerald-700/50 bg-emerald-950/40 hover:border-emerald-500 text-emerald-300 text-xs font-medium transition-colors disabled:opacity-50",
|
|
17352
|
+
children: [
|
|
17353
|
+
/* @__PURE__ */ jsx148(CheckIcon2, {}),
|
|
17354
|
+
/* @__PURE__ */ jsx148("span", { children: building ? "Building report..." : "Approve & Build" })
|
|
17355
|
+
]
|
|
17356
|
+
}
|
|
17357
|
+
) })
|
|
17358
|
+
] })
|
|
17359
|
+
] }) });
|
|
17360
|
+
}
|
|
16790
17361
|
if (status === "complete" && !hasHTML) {
|
|
16791
17362
|
return /* @__PURE__ */ jsx148("div", { className: cn("w-full", className), children: /* @__PURE__ */ jsxs109("div", { className: "bg-zinc-900 border border-zinc-800 rounded-2xl overflow-hidden", children: [
|
|
16792
17363
|
/* @__PURE__ */ jsx148(
|
|
@@ -16850,8 +17421,8 @@ var ResearchReportJobCard = (props) => {
|
|
|
16850
17421
|
window.open(link, "_blank", "noopener,noreferrer");
|
|
16851
17422
|
}
|
|
16852
17423
|
};
|
|
16853
|
-
const canApprove = generationMode === "template" && reviewStatus === "pending_review" && Boolean(job_id);
|
|
16854
|
-
const canRegenerate = generationMode === "template" && reviewStatus === "pending_review" && Boolean(job_id);
|
|
17424
|
+
const canApprove = !hideApproval && generationMode === "template" && reviewStatus === "pending_review" && Boolean(job_id);
|
|
17425
|
+
const canRegenerate = !hideApproval && generationMode === "template" && reviewStatus === "pending_review" && Boolean(job_id);
|
|
16855
17426
|
const canShare = Boolean(hasHTML || job_id) && (generationMode !== "template" || reviewStatus !== "pending_review");
|
|
16856
17427
|
const handleApprove = async () => {
|
|
16857
17428
|
if (!job_id || approving) return;
|
|
@@ -16915,36 +17486,18 @@ var ResearchReportJobCard = (props) => {
|
|
|
16915
17486
|
throw new Error(err.detail || err.error || `Regenerate failed (${res.status})`);
|
|
16916
17487
|
}
|
|
16917
17488
|
const data = await res.json();
|
|
16918
|
-
|
|
16919
|
-
|
|
16920
|
-
|
|
16921
|
-
setReviewStatus("pending_review");
|
|
16922
|
-
setTemplateVersionId("");
|
|
16923
|
-
if (out.section_count !== void 0) setSectionCount(out.section_count);
|
|
16924
|
-
if (out.title) setTitle(out.title);
|
|
16925
|
-
if (onCompleteRef.current) {
|
|
16926
|
-
onCompleteRef.current({
|
|
16927
|
-
title: out.title || title,
|
|
16928
|
-
depth,
|
|
16929
|
-
section_count: out.section_count ?? sectionCount,
|
|
16930
|
-
source_count: sourceCount,
|
|
16931
|
-
word_count: wordCount,
|
|
16932
|
-
executive_summary: summary,
|
|
16933
|
-
html_url: out.html_url || htmlUrl,
|
|
16934
|
-
generation_mode: "template",
|
|
16935
|
-
template_id: data.template_id || out.template_id || templateId,
|
|
16936
|
-
template_version_id: void 0,
|
|
16937
|
-
review_status: "pending_review",
|
|
16938
|
-
theme
|
|
16939
|
-
});
|
|
17489
|
+
if (typeof data.job_id === "string" && data.job_id !== job_id) {
|
|
17490
|
+
setRegenPollUrl(deriveJobStatusUrl2(data.job_id, pollUrl, regenerateUrl));
|
|
17491
|
+
return;
|
|
16940
17492
|
}
|
|
17493
|
+
applyRegeneratedOutput(data.output || data);
|
|
17494
|
+
setRegenerating(false);
|
|
16941
17495
|
} catch (e) {
|
|
16942
17496
|
setApproveError(e instanceof Error ? e.message : "Could not switch template");
|
|
16943
|
-
} finally {
|
|
16944
17497
|
setRegenerating(false);
|
|
16945
17498
|
}
|
|
16946
17499
|
};
|
|
16947
|
-
const templateLabel =
|
|
17500
|
+
const templateLabel = formatTemplateLabel2(templateId);
|
|
16948
17501
|
const isApproved = reviewStatus === "approved";
|
|
16949
17502
|
return /* @__PURE__ */ jsxs109(Fragment6, { children: [
|
|
16950
17503
|
/* @__PURE__ */ jsx148("div", { className: cn("w-full", className), children: /* @__PURE__ */ jsxs109("div", { className: "bg-zinc-900 border border-zinc-800 rounded-2xl overflow-hidden", children: [
|
|
@@ -16993,7 +17546,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16993
17546
|
title: "Try another report template with the same data",
|
|
16994
17547
|
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-300 text-xs font-medium transition-colors disabled:opacity-50",
|
|
16995
17548
|
children: [
|
|
16996
|
-
/* @__PURE__ */ jsx148(
|
|
17549
|
+
/* @__PURE__ */ jsx148(RefreshIcon2, {}),
|
|
16997
17550
|
/* @__PURE__ */ jsx148("span", { children: regenerating ? "Switching..." : "Regenerate" })
|
|
16998
17551
|
]
|
|
16999
17552
|
}
|