pxengine 0.1.113 → 0.1.115
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 +569 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -1
- package/dist/index.d.ts +79 -1
- package/dist/index.mjs +569 -17
- package/dist/index.mjs.map +1 -1
- package/dist/registry.json +67 -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,7 +16851,7 @@ var FullscreenPreviewModal = ({ url, title, onClose }) => {
|
|
|
16503
16851
|
) })
|
|
16504
16852
|
] });
|
|
16505
16853
|
};
|
|
16506
|
-
function
|
|
16854
|
+
function deriveJobStatusUrl2(newJobId, pollUrl, regenerateUrl) {
|
|
16507
16855
|
const base = pollUrl?.replace(/\/api\/jobs\/[^/]+\/status.*$/, "") ?? regenerateUrl?.replace(/\/api\/reports\/[^/]+\/regenerate.*$/, "") ?? "";
|
|
16508
16856
|
return `${base}/api/jobs/${newJobId}/status`;
|
|
16509
16857
|
}
|
|
@@ -16520,10 +16868,12 @@ var ResearchReportJobCard = (props) => {
|
|
|
16520
16868
|
executive_summary: initialSummary,
|
|
16521
16869
|
key_findings: _initialFindings,
|
|
16522
16870
|
html_url: initialHtmlUrl,
|
|
16871
|
+
pdf_url: initialPdfUrl,
|
|
16523
16872
|
generation_mode: initialGenerationMode,
|
|
16524
16873
|
template_id: initialTemplateId,
|
|
16525
16874
|
template_version_id: initialTemplateVersionId,
|
|
16526
16875
|
review_status: initialReviewStatus,
|
|
16876
|
+
outline: initialOutline,
|
|
16527
16877
|
theme: initialTheme,
|
|
16528
16878
|
error: initialError,
|
|
16529
16879
|
progress: initialProgress,
|
|
@@ -16531,7 +16881,9 @@ var ResearchReportJobCard = (props) => {
|
|
|
16531
16881
|
authToken,
|
|
16532
16882
|
shareUrl,
|
|
16533
16883
|
approveUrl,
|
|
16884
|
+
approveOutlineUrl,
|
|
16534
16885
|
regenerateUrl,
|
|
16886
|
+
hideApproval = false,
|
|
16535
16887
|
className,
|
|
16536
16888
|
onComplete,
|
|
16537
16889
|
onFailed,
|
|
@@ -16546,6 +16898,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16546
16898
|
const [wordCount, setWordCount] = useState11(initialWordCount ?? 0);
|
|
16547
16899
|
const [summary, setSummary] = useState11(initialSummary || "");
|
|
16548
16900
|
const [htmlUrl, setHtmlUrl] = useState11(initialHtmlUrl || "");
|
|
16901
|
+
const [pdfUrl, setPdfUrl] = useState11(initialPdfUrl || "");
|
|
16549
16902
|
const [generationMode, setGenerationMode] = useState11(
|
|
16550
16903
|
initialGenerationMode || (initialHtmlUrl ? "template" : "")
|
|
16551
16904
|
);
|
|
@@ -16554,6 +16907,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16554
16907
|
const [reviewStatus, setReviewStatus] = useState11(
|
|
16555
16908
|
initialReviewStatus || (initialHtmlUrl ? "pending_review" : "")
|
|
16556
16909
|
);
|
|
16910
|
+
const [outline, setOutline] = useState11(initialOutline);
|
|
16557
16911
|
const [theme, setTheme] = useState11(initialTheme || DEFAULT_THEME);
|
|
16558
16912
|
const [error, setError] = useState11(initialError);
|
|
16559
16913
|
const [progress, setProgress] = useState11(initialProgress);
|
|
@@ -16563,6 +16917,9 @@ var ResearchReportJobCard = (props) => {
|
|
|
16563
16917
|
const [approving, setApproving] = useState11(false);
|
|
16564
16918
|
const [regenerating, setRegenerating] = useState11(false);
|
|
16565
16919
|
const [approveError, setApproveError] = useState11(null);
|
|
16920
|
+
const [headingEdits, setHeadingEdits] = useState11({});
|
|
16921
|
+
const [approvingOutline, setApprovingOutline] = useState11(false);
|
|
16922
|
+
const [outlineWritePollUrl, setOutlineWritePollUrl] = useState11(null);
|
|
16566
16923
|
const [regenPollUrl, setRegenPollUrl] = useState11(null);
|
|
16567
16924
|
const previewRef = useRef7(null);
|
|
16568
16925
|
const onCompleteRef = useRef7(onComplete);
|
|
@@ -16592,6 +16949,10 @@ var ResearchReportJobCard = (props) => {
|
|
|
16592
16949
|
useEffect8(() => {
|
|
16593
16950
|
if (initialReviewStatus) setReviewStatus(initialReviewStatus);
|
|
16594
16951
|
}, [initialReviewStatus]);
|
|
16952
|
+
const initialOutlineSectionCount = initialOutline?.sections?.length;
|
|
16953
|
+
useEffect8(() => {
|
|
16954
|
+
if (initialOutline) setOutline(initialOutline);
|
|
16955
|
+
}, [initialOutlineSectionCount]);
|
|
16595
16956
|
useEffect8(() => {
|
|
16596
16957
|
if (initialDepth) setDepth(initialDepth);
|
|
16597
16958
|
}, [initialDepth]);
|
|
@@ -16657,6 +17018,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16657
17018
|
}
|
|
16658
17019
|
if (newStatus === "complete" && data.output) {
|
|
16659
17020
|
const output = data.output;
|
|
17021
|
+
const isOutlinePhase = output.review_status === "pending_outline_approval" || output.type === "research-report-outline";
|
|
16660
17022
|
setTitle(output.title || initialTitle);
|
|
16661
17023
|
setDepth(output.depth || "");
|
|
16662
17024
|
setSectionCount(output.section_count || 0);
|
|
@@ -16664,14 +17026,16 @@ var ResearchReportJobCard = (props) => {
|
|
|
16664
17026
|
setWordCount(output.word_count || 0);
|
|
16665
17027
|
setSummary(output.executive_summary || "");
|
|
16666
17028
|
setHtmlUrl(output.html_url || "");
|
|
17029
|
+
setPdfUrl(output.pdf_url || "");
|
|
16667
17030
|
if (output.generation_mode) setGenerationMode(output.generation_mode);
|
|
16668
17031
|
if (output.template_id) setTemplateId(output.template_id);
|
|
16669
17032
|
if (output.template_version_id) setTemplateVersionId(output.template_version_id);
|
|
16670
17033
|
if (output.review_status) setReviewStatus(output.review_status);
|
|
17034
|
+
if (output.outline) setOutline(output.outline);
|
|
16671
17035
|
if (output.theme) {
|
|
16672
17036
|
setTheme(output.theme);
|
|
16673
17037
|
}
|
|
16674
|
-
if (!hasNotifiedRef.current && onCompleteRef.current) {
|
|
17038
|
+
if (!isOutlinePhase && !hasNotifiedRef.current && onCompleteRef.current) {
|
|
16675
17039
|
hasNotifiedRef.current = true;
|
|
16676
17040
|
onCompleteRef.current(output);
|
|
16677
17041
|
}
|
|
@@ -16690,6 +17054,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16690
17054
|
const applyRegeneratedOutput = useCallback5(
|
|
16691
17055
|
(out) => {
|
|
16692
17056
|
if (out.html_url) setHtmlUrl(out.html_url);
|
|
17057
|
+
setPdfUrl(out.pdf_url || "");
|
|
16693
17058
|
if (out.template_id) setTemplateId(out.template_id);
|
|
16694
17059
|
setReviewStatus("pending_review");
|
|
16695
17060
|
setTemplateVersionId("");
|
|
@@ -16706,6 +17071,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16706
17071
|
word_count: out.word_count ?? wordCount,
|
|
16707
17072
|
executive_summary: out.executive_summary ?? summary,
|
|
16708
17073
|
html_url: out.html_url ?? htmlUrl,
|
|
17074
|
+
pdf_url: out.pdf_url ?? pdfUrl,
|
|
16709
17075
|
generation_mode: "template",
|
|
16710
17076
|
template_id: out.template_id ?? templateId,
|
|
16711
17077
|
template_version_id: void 0,
|
|
@@ -16713,7 +17079,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16713
17079
|
theme: out.theme ?? theme
|
|
16714
17080
|
});
|
|
16715
17081
|
},
|
|
16716
|
-
[title, depth, sectionCount, sourceCount, wordCount, summary, htmlUrl, templateId, theme]
|
|
17082
|
+
[title, depth, sectionCount, sourceCount, wordCount, summary, htmlUrl, pdfUrl, templateId, theme]
|
|
16717
17083
|
);
|
|
16718
17084
|
useSharedPoll(
|
|
16719
17085
|
{
|
|
@@ -16740,6 +17106,104 @@ var ResearchReportJobCard = (props) => {
|
|
|
16740
17106
|
}
|
|
16741
17107
|
}
|
|
16742
17108
|
);
|
|
17109
|
+
const refetchSourceAndApply = useCallback5(async () => {
|
|
17110
|
+
if (!pollUrl) return;
|
|
17111
|
+
try {
|
|
17112
|
+
const headers = {};
|
|
17113
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
17114
|
+
const res = await fetch(pollUrl, { headers });
|
|
17115
|
+
if (!res.ok) return;
|
|
17116
|
+
const data = await res.json();
|
|
17117
|
+
const out = data.output;
|
|
17118
|
+
if (!out) return;
|
|
17119
|
+
if (out.title) setTitle(out.title);
|
|
17120
|
+
if (out.depth) setDepth(out.depth);
|
|
17121
|
+
if (out.section_count !== void 0) setSectionCount(out.section_count);
|
|
17122
|
+
if (out.source_count !== void 0) setSourceCount(out.source_count);
|
|
17123
|
+
if (out.word_count !== void 0) setWordCount(out.word_count);
|
|
17124
|
+
if (out.executive_summary) setSummary(out.executive_summary);
|
|
17125
|
+
if (out.html_url) setHtmlUrl(out.html_url);
|
|
17126
|
+
setPdfUrl(out.pdf_url || "");
|
|
17127
|
+
if (out.generation_mode) setGenerationMode(out.generation_mode);
|
|
17128
|
+
if (out.template_id) setTemplateId(out.template_id);
|
|
17129
|
+
setTemplateVersionId(out.template_version_id || "");
|
|
17130
|
+
setReviewStatus(out.review_status || "pending_review");
|
|
17131
|
+
if (out.theme) setTheme(out.theme);
|
|
17132
|
+
if (!hasNotifiedRef.current && onCompleteRef.current) {
|
|
17133
|
+
hasNotifiedRef.current = true;
|
|
17134
|
+
onCompleteRef.current(out);
|
|
17135
|
+
}
|
|
17136
|
+
} catch {
|
|
17137
|
+
}
|
|
17138
|
+
}, [pollUrl, authToken]);
|
|
17139
|
+
useSharedPoll(
|
|
17140
|
+
{
|
|
17141
|
+
key: outlineWritePollUrl,
|
|
17142
|
+
intervalMs: 3e3,
|
|
17143
|
+
fetcher: async () => {
|
|
17144
|
+
const headers = {};
|
|
17145
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
17146
|
+
const res = await fetch(outlineWritePollUrl, { headers });
|
|
17147
|
+
if (!res.ok) throw new Error(`poll ${res.status}`);
|
|
17148
|
+
return res.json();
|
|
17149
|
+
},
|
|
17150
|
+
shouldContinue: (data) => data.status !== "complete" && data.status !== "failed"
|
|
17151
|
+
},
|
|
17152
|
+
(data) => {
|
|
17153
|
+
if (data.status === "complete") {
|
|
17154
|
+
setOutlineWritePollUrl(null);
|
|
17155
|
+
setApprovingOutline(false);
|
|
17156
|
+
void refetchSourceAndApply();
|
|
17157
|
+
} else if (data.status === "failed") {
|
|
17158
|
+
setApproveError(data.error || "Could not build the report");
|
|
17159
|
+
setOutlineWritePollUrl(null);
|
|
17160
|
+
setApprovingOutline(false);
|
|
17161
|
+
}
|
|
17162
|
+
}
|
|
17163
|
+
);
|
|
17164
|
+
const buildEditedOutline = () => {
|
|
17165
|
+
if (!outline || !Array.isArray(outline.sections)) return void 0;
|
|
17166
|
+
let changed = false;
|
|
17167
|
+
const sections = outline.sections.map((s, i) => {
|
|
17168
|
+
const edited = headingEdits[i];
|
|
17169
|
+
if (edited !== void 0 && edited !== s.heading) {
|
|
17170
|
+
changed = true;
|
|
17171
|
+
return { ...s, heading: edited };
|
|
17172
|
+
}
|
|
17173
|
+
return s;
|
|
17174
|
+
});
|
|
17175
|
+
return changed ? { ...outline, sections } : void 0;
|
|
17176
|
+
};
|
|
17177
|
+
const handleApproveOutline = async () => {
|
|
17178
|
+
if (!job_id || approvingOutline) return;
|
|
17179
|
+
const endpoint = approveOutlineUrl || `/api/reports/${job_id}/approve-outline`;
|
|
17180
|
+
setApprovingOutline(true);
|
|
17181
|
+
setApproveError(null);
|
|
17182
|
+
try {
|
|
17183
|
+
const headers = { "Content-Type": "application/json" };
|
|
17184
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
17185
|
+
const editedOutline = buildEditedOutline();
|
|
17186
|
+
const res = await fetch(endpoint, {
|
|
17187
|
+
method: "POST",
|
|
17188
|
+
headers,
|
|
17189
|
+
body: JSON.stringify(editedOutline ? { outline: editedOutline } : {})
|
|
17190
|
+
});
|
|
17191
|
+
if (!res.ok) {
|
|
17192
|
+
const err = await res.json().catch(() => ({}));
|
|
17193
|
+
throw new Error(err.detail || err.error || `Approve failed (${res.status})`);
|
|
17194
|
+
}
|
|
17195
|
+
const data = await res.json();
|
|
17196
|
+
if (typeof data.job_id === "string" && data.job_id !== job_id) {
|
|
17197
|
+
setOutlineWritePollUrl(deriveJobStatusUrl2(data.job_id, pollUrl, regenerateUrl));
|
|
17198
|
+
return;
|
|
17199
|
+
}
|
|
17200
|
+
setApprovingOutline(false);
|
|
17201
|
+
void refetchSourceAndApply();
|
|
17202
|
+
} catch (e) {
|
|
17203
|
+
setApproveError(e instanceof Error ? e.message : "Could not build the report");
|
|
17204
|
+
setApprovingOutline(false);
|
|
17205
|
+
}
|
|
17206
|
+
};
|
|
16743
17207
|
const formatWordCount = (count) => {
|
|
16744
17208
|
if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
|
|
16745
17209
|
return count.toString();
|
|
@@ -16845,6 +17309,61 @@ var ResearchReportJobCard = (props) => {
|
|
|
16845
17309
|
] })
|
|
16846
17310
|
] }) }) });
|
|
16847
17311
|
}
|
|
17312
|
+
if (status === "complete" && reviewStatus === "pending_outline_approval") {
|
|
17313
|
+
const sections = Array.isArray(outline?.sections) ? outline.sections : [];
|
|
17314
|
+
const building = Boolean(outlineWritePollUrl) || approvingOutline;
|
|
17315
|
+
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: [
|
|
17316
|
+
/* @__PURE__ */ jsx148(
|
|
17317
|
+
"div",
|
|
17318
|
+
{
|
|
17319
|
+
className: "h-[3px]",
|
|
17320
|
+
style: { background: "linear-gradient(90deg, #8B7D5E, #C0AE82, #DCC99B)" }
|
|
17321
|
+
}
|
|
17322
|
+
),
|
|
17323
|
+
/* @__PURE__ */ jsxs109("div", { className: "p-5", children: [
|
|
17324
|
+
/* @__PURE__ */ jsxs109("div", { className: "flex items-start gap-3", children: [
|
|
17325
|
+
/* @__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, {}) }),
|
|
17326
|
+
/* @__PURE__ */ jsxs109("div", { className: "flex-1 min-w-0", children: [
|
|
17327
|
+
/* @__PURE__ */ jsx148("h3", { className: "text-sm font-semibold text-zinc-100 truncate leading-tight", children: title || "Report outline" }),
|
|
17328
|
+
/* @__PURE__ */ jsxs109("p", { className: "text-xs text-amber-400/90 mt-0.5", children: [
|
|
17329
|
+
"Review the outline",
|
|
17330
|
+
sections.length > 0 ? " \u2014 edit any heading" : "",
|
|
17331
|
+
", then build the full report."
|
|
17332
|
+
] })
|
|
17333
|
+
] })
|
|
17334
|
+
] }),
|
|
17335
|
+
/* @__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)),
|
|
17348
|
+
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
|
+
] }),
|
|
17350
|
+
approveError && /* @__PURE__ */ jsx148("p", { className: "mt-3 text-xs text-red-400/90", children: approveError }),
|
|
17351
|
+
/* @__PURE__ */ jsx148("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs109(
|
|
17352
|
+
"button",
|
|
17353
|
+
{
|
|
17354
|
+
onClick: handleApproveOutline,
|
|
17355
|
+
disabled: building,
|
|
17356
|
+
title: "Approve this outline and build the full report",
|
|
17357
|
+
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",
|
|
17358
|
+
children: [
|
|
17359
|
+
/* @__PURE__ */ jsx148(CheckIcon2, {}),
|
|
17360
|
+
/* @__PURE__ */ jsx148("span", { children: building ? "Building report..." : "Approve & Build" })
|
|
17361
|
+
]
|
|
17362
|
+
}
|
|
17363
|
+
) })
|
|
17364
|
+
] })
|
|
17365
|
+
] }) });
|
|
17366
|
+
}
|
|
16848
17367
|
if (status === "complete" && !hasHTML) {
|
|
16849
17368
|
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: [
|
|
16850
17369
|
/* @__PURE__ */ jsx148(
|
|
@@ -16890,6 +17409,26 @@ var ResearchReportJobCard = (props) => {
|
|
|
16890
17409
|
window.open(htmlUrl, "_blank", "noopener,noreferrer");
|
|
16891
17410
|
}
|
|
16892
17411
|
};
|
|
17412
|
+
const handleDownloadPdf = async () => {
|
|
17413
|
+
if (!pdfUrl) return;
|
|
17414
|
+
const filename = `${(title ?? "").replace(/[^a-z0-9]/gi, "-").toLowerCase()}.pdf`;
|
|
17415
|
+
const href = `/api/download?url=${encodeURIComponent(pdfUrl)}&filename=${encodeURIComponent(filename)}`;
|
|
17416
|
+
try {
|
|
17417
|
+
const response = await fetch(href);
|
|
17418
|
+
if (!response.ok) throw new Error(`status ${response.status}`);
|
|
17419
|
+
const blob = await response.blob();
|
|
17420
|
+
const objectUrl = window.URL.createObjectURL(blob);
|
|
17421
|
+
const anchor = document.createElement("a");
|
|
17422
|
+
anchor.href = objectUrl;
|
|
17423
|
+
anchor.download = filename;
|
|
17424
|
+
document.body.appendChild(anchor);
|
|
17425
|
+
anchor.click();
|
|
17426
|
+
anchor.remove();
|
|
17427
|
+
window.URL.revokeObjectURL(objectUrl);
|
|
17428
|
+
} catch {
|
|
17429
|
+
window.open(pdfUrl, "_blank", "noopener,noreferrer");
|
|
17430
|
+
}
|
|
17431
|
+
};
|
|
16893
17432
|
const resolveShareLink = () => {
|
|
16894
17433
|
if (shareUrl) return shareUrl;
|
|
16895
17434
|
if (typeof window !== "undefined" && job_id) {
|
|
@@ -16908,8 +17447,8 @@ var ResearchReportJobCard = (props) => {
|
|
|
16908
17447
|
window.open(link, "_blank", "noopener,noreferrer");
|
|
16909
17448
|
}
|
|
16910
17449
|
};
|
|
16911
|
-
const canApprove = generationMode === "template" && reviewStatus === "pending_review" && Boolean(job_id);
|
|
16912
|
-
const canRegenerate = generationMode === "template" && reviewStatus === "pending_review" && Boolean(job_id);
|
|
17450
|
+
const canApprove = !hideApproval && generationMode === "template" && reviewStatus === "pending_review" && Boolean(job_id);
|
|
17451
|
+
const canRegenerate = !hideApproval && generationMode === "template" && reviewStatus === "pending_review" && Boolean(job_id);
|
|
16913
17452
|
const canShare = Boolean(hasHTML || job_id) && (generationMode !== "template" || reviewStatus !== "pending_review");
|
|
16914
17453
|
const handleApprove = async () => {
|
|
16915
17454
|
if (!job_id || approving) return;
|
|
@@ -16942,6 +17481,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16942
17481
|
word_count: wordCount,
|
|
16943
17482
|
executive_summary: summary,
|
|
16944
17483
|
html_url: htmlUrl,
|
|
17484
|
+
pdf_url: pdfUrl,
|
|
16945
17485
|
generation_mode: generationMode,
|
|
16946
17486
|
template_id: data.template_id || templateId,
|
|
16947
17487
|
template_version_id: data.template_version_id || templateVersionId,
|
|
@@ -16974,7 +17514,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16974
17514
|
}
|
|
16975
17515
|
const data = await res.json();
|
|
16976
17516
|
if (typeof data.job_id === "string" && data.job_id !== job_id) {
|
|
16977
|
-
setRegenPollUrl(
|
|
17517
|
+
setRegenPollUrl(deriveJobStatusUrl2(data.job_id, pollUrl, regenerateUrl));
|
|
16978
17518
|
return;
|
|
16979
17519
|
}
|
|
16980
17520
|
applyRegeneratedOutput(data.output || data);
|
|
@@ -16984,7 +17524,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
16984
17524
|
setRegenerating(false);
|
|
16985
17525
|
}
|
|
16986
17526
|
};
|
|
16987
|
-
const templateLabel =
|
|
17527
|
+
const templateLabel = formatTemplateLabel2(templateId);
|
|
16988
17528
|
const isApproved = reviewStatus === "approved";
|
|
16989
17529
|
return /* @__PURE__ */ jsxs109(Fragment6, { children: [
|
|
16990
17530
|
/* @__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: [
|
|
@@ -17033,7 +17573,7 @@ var ResearchReportJobCard = (props) => {
|
|
|
17033
17573
|
title: "Try another report template with the same data",
|
|
17034
17574
|
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",
|
|
17035
17575
|
children: [
|
|
17036
|
-
/* @__PURE__ */ jsx148(
|
|
17576
|
+
/* @__PURE__ */ jsx148(RefreshIcon2, {}),
|
|
17037
17577
|
/* @__PURE__ */ jsx148("span", { children: regenerating ? "Switching..." : "Regenerate" })
|
|
17038
17578
|
]
|
|
17039
17579
|
}
|
|
@@ -17079,6 +17619,18 @@ var ResearchReportJobCard = (props) => {
|
|
|
17079
17619
|
]
|
|
17080
17620
|
}
|
|
17081
17621
|
),
|
|
17622
|
+
pdfUrl && /* @__PURE__ */ jsxs109(
|
|
17623
|
+
"button",
|
|
17624
|
+
{
|
|
17625
|
+
onClick: handleDownloadPdf,
|
|
17626
|
+
title: "Download PDF",
|
|
17627
|
+
className: "flex items-center gap-1.5 px-2.5 h-7 rounded-lg border border-zinc-700 bg-zinc-800 hover:border-zinc-500 text-zinc-400 hover:text-zinc-200 text-xs font-medium transition-colors",
|
|
17628
|
+
children: [
|
|
17629
|
+
/* @__PURE__ */ jsx148(DownloadIcon2, {}),
|
|
17630
|
+
/* @__PURE__ */ jsx148("span", { children: "PDF" })
|
|
17631
|
+
]
|
|
17632
|
+
}
|
|
17633
|
+
),
|
|
17082
17634
|
/* @__PURE__ */ jsxs109(
|
|
17083
17635
|
"button",
|
|
17084
17636
|
{
|