pxengine 0.1.108 → 0.1.109

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.mjs CHANGED
@@ -38853,7 +38853,128 @@ var NextStepCard = ({
38853
38853
  };
38854
38854
 
38855
38855
  // src/molecules/generic/PresentationJobCard/PresentationJobCard.tsx
38856
- import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef5, useState as useState10 } from "react";
38856
+ import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef6, useState as useState10 } from "react";
38857
+
38858
+ // src/lib/shared-poll.ts
38859
+ import { useEffect as useEffect6, useRef as useRef5 } from "react";
38860
+ var entries = /* @__PURE__ */ new Map();
38861
+ function clearTimer(entry) {
38862
+ if (entry.timer !== null) {
38863
+ clearInterval(entry.timer);
38864
+ entry.timer = null;
38865
+ }
38866
+ entry.controller?.abort();
38867
+ entry.controller = null;
38868
+ }
38869
+ async function runPoll(key) {
38870
+ const entry = entries.get(key);
38871
+ if (!entry || entry.inFlight || entry.stopped) return;
38872
+ entry.inFlight = true;
38873
+ const controller = new AbortController();
38874
+ entry.controller = controller;
38875
+ try {
38876
+ const data = await entry.fetcher(controller.signal);
38877
+ if (entries.get(key) !== entry) return;
38878
+ entry.lastData = data;
38879
+ entry.hasData = true;
38880
+ for (const listener of Array.from(entry.dataListeners)) {
38881
+ try {
38882
+ listener(data);
38883
+ } catch {
38884
+ }
38885
+ }
38886
+ if (!entry.shouldContinue(data)) {
38887
+ entry.stopped = true;
38888
+ clearTimer(entry);
38889
+ }
38890
+ } catch (error) {
38891
+ if (entries.get(key) !== entry) return;
38892
+ for (const listener of Array.from(entry.errorListeners)) {
38893
+ try {
38894
+ listener(error);
38895
+ } catch {
38896
+ }
38897
+ }
38898
+ } finally {
38899
+ entry.inFlight = false;
38900
+ }
38901
+ }
38902
+ function subscribeSharedPoll(config, onData, onError) {
38903
+ const { key, intervalMs, fetcher, shouldContinue } = config;
38904
+ if (!key) return () => {
38905
+ };
38906
+ let entry = entries.get(key);
38907
+ if (!entry) {
38908
+ entry = {
38909
+ intervalMs,
38910
+ fetcher,
38911
+ shouldContinue: shouldContinue ?? (() => true),
38912
+ dataListeners: /* @__PURE__ */ new Set(),
38913
+ errorListeners: /* @__PURE__ */ new Set(),
38914
+ timer: null,
38915
+ controller: null,
38916
+ lastData: void 0,
38917
+ hasData: false,
38918
+ stopped: false,
38919
+ inFlight: false
38920
+ };
38921
+ entries.set(key, entry);
38922
+ }
38923
+ const activeEntry = entry;
38924
+ activeEntry.dataListeners.add(onData);
38925
+ if (onError) activeEntry.errorListeners.add(onError);
38926
+ if (activeEntry.hasData && activeEntry.lastData !== void 0) {
38927
+ try {
38928
+ onData(activeEntry.lastData);
38929
+ } catch {
38930
+ }
38931
+ }
38932
+ if (!activeEntry.stopped && activeEntry.timer === null) {
38933
+ void runPoll(key);
38934
+ activeEntry.timer = setInterval(() => void runPoll(key), activeEntry.intervalMs);
38935
+ }
38936
+ return () => {
38937
+ activeEntry.dataListeners.delete(onData);
38938
+ if (onError) activeEntry.errorListeners.delete(onError);
38939
+ if (activeEntry.dataListeners.size === 0 && activeEntry.errorListeners.size === 0) {
38940
+ clearTimer(activeEntry);
38941
+ entries.delete(key);
38942
+ }
38943
+ };
38944
+ }
38945
+ function stopSharedPoll(key) {
38946
+ const entry = entries.get(key);
38947
+ if (!entry) return;
38948
+ entry.stopped = true;
38949
+ clearTimer(entry);
38950
+ }
38951
+ function useSharedPoll(config, onData, onError) {
38952
+ const fetcherRef = useRef5(config.fetcher);
38953
+ fetcherRef.current = config.fetcher;
38954
+ const shouldContinueRef = useRef5(config.shouldContinue);
38955
+ shouldContinueRef.current = config.shouldContinue;
38956
+ const onDataRef = useRef5(onData);
38957
+ onDataRef.current = onData;
38958
+ const onErrorRef = useRef5(onError);
38959
+ onErrorRef.current = onError;
38960
+ const { key, intervalMs } = config;
38961
+ useEffect6(() => {
38962
+ if (!key) return;
38963
+ const unsubscribe = subscribeSharedPoll(
38964
+ {
38965
+ key,
38966
+ intervalMs,
38967
+ fetcher: (signal) => fetcherRef.current(signal),
38968
+ shouldContinue: (data) => shouldContinueRef.current ? shouldContinueRef.current(data) : true
38969
+ },
38970
+ (data) => onDataRef.current(data),
38971
+ (error) => onErrorRef.current?.(error)
38972
+ );
38973
+ return unsubscribe;
38974
+ }, [key, intervalMs]);
38975
+ }
38976
+
38977
+ // src/molecules/generic/PresentationJobCard/PresentationJobCard.tsx
38857
38978
  import { Fragment as Fragment5, jsx as jsx147, jsxs as jsxs108 } from "react/jsx-runtime";
38858
38979
  var DownloadIcon = () => /* @__PURE__ */ jsxs108("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: [
38859
38980
  /* @__PURE__ */ jsx147("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
@@ -38980,8 +39101,8 @@ var ExportModal = ({ formats, title, onClose }) => {
38980
39101
  var FullscreenModal = ({ url, title, slideCount, initialSlide = 1, onClose }) => {
38981
39102
  const [currentSlide, setCurrentSlide] = useState10(initialSlide);
38982
39103
  const [iframeReady, setIframeReady] = useState10(false);
38983
- const iframeRef = useRef5(null);
38984
- useEffect6(() => {
39104
+ const iframeRef = useRef6(null);
39105
+ useEffect7(() => {
38985
39106
  const onKey = (e) => {
38986
39107
  if (e.key === "Escape") onClose();
38987
39108
  };
@@ -38999,7 +39120,7 @@ var FullscreenModal = ({ url, title, slideCount, initialSlide = 1, onClose }) =>
38999
39120
  window.removeEventListener("message", onMsg);
39000
39121
  };
39001
39122
  }, [onClose, iframeReady]);
39002
- useEffect6(() => {
39123
+ useEffect7(() => {
39003
39124
  document.body.style.overflow = "hidden";
39004
39125
  return () => {
39005
39126
  document.body.style.overflow = "";
@@ -39110,34 +39231,33 @@ var PresentationJobCard = ({
39110
39231
  const [currentSlide, setCurrentSlide] = useState10(1);
39111
39232
  const [previewScale, setPreviewScale] = useState10(1);
39112
39233
  const [iframeReady, setIframeReady] = useState10(false);
39113
- const intervalRef = useRef5(null);
39114
- const previewRef = useRef5(null);
39115
- const iframeRef = useRef5(null);
39116
- useEffect6(() => {
39234
+ const previewRef = useRef6(null);
39235
+ const iframeRef = useRef6(null);
39236
+ useEffect7(() => {
39117
39237
  setStatus(initialStatus);
39118
39238
  }, [initialStatus]);
39119
39239
  const progressPct = initialProgress?.percentage;
39120
39240
  const progressStep = initialProgress?.current_step;
39121
- useEffect6(() => {
39241
+ useEffect7(() => {
39122
39242
  if (initialProgress) setProgress(initialProgress);
39123
39243
  }, [progressPct, progressStep]);
39124
- useEffect6(() => {
39244
+ useEffect7(() => {
39125
39245
  if (initialError) setError(initialError);
39126
39246
  }, [initialError]);
39127
- useEffect6(() => {
39247
+ useEffect7(() => {
39128
39248
  if (initialSlideCount !== void 0) setSlideCount(initialSlideCount);
39129
39249
  }, [initialSlideCount]);
39130
39250
  const htmlUrl = initialFormats?.html_url;
39131
- useEffect6(() => {
39251
+ useEffect7(() => {
39132
39252
  if (initialFormats) setFormats(initialFormats);
39133
39253
  }, [htmlUrl]);
39134
- useEffect6(() => {
39254
+ useEffect7(() => {
39135
39255
  if (initialTitle) setTitle(initialTitle);
39136
39256
  }, [initialTitle]);
39137
39257
  const updateScale = useCallback4(() => {
39138
39258
  if (previewRef.current) setPreviewScale(previewRef.current.offsetWidth / 1280);
39139
39259
  }, []);
39140
- useEffect6(() => {
39260
+ useEffect7(() => {
39141
39261
  updateScale();
39142
39262
  setIframeReady(false);
39143
39263
  if (typeof ResizeObserver === "undefined") return;
@@ -39145,7 +39265,7 @@ var PresentationJobCard = ({
39145
39265
  if (previewRef.current) ro.observe(previewRef.current);
39146
39266
  return () => ro.disconnect();
39147
39267
  }, [updateScale, formats.html_url]);
39148
- useEffect6(() => {
39268
+ useEffect7(() => {
39149
39269
  const handler = (e) => {
39150
39270
  if (e.data?.type === "slideChanged") {
39151
39271
  setCurrentSlide(e.data.slide);
@@ -39170,66 +39290,59 @@ var PresentationJobCard = ({
39170
39290
  iframe.contentWindow.postMessage({ type: command }, "*");
39171
39291
  };
39172
39292
  const isTerminal = status === "complete" || status === "failed";
39173
- const onCompleteRef = useRef5(onComplete);
39174
- const onFailedRef = useRef5(onFailed);
39175
- const hasNotifiedRef = useRef5(false);
39293
+ const onCompleteRef = useRef6(onComplete);
39294
+ const onFailedRef = useRef6(onFailed);
39295
+ const hasNotifiedRef = useRef6(false);
39176
39296
  onCompleteRef.current = onComplete;
39177
39297
  onFailedRef.current = onFailed;
39178
- useEffect6(() => {
39179
- if (isTerminal || !pollUrl) return;
39180
- const poll = async () => {
39181
- try {
39298
+ useSharedPoll(
39299
+ {
39300
+ key: !isTerminal && pollUrl ? pollUrl : null,
39301
+ intervalMs: 3e3,
39302
+ fetcher: async () => {
39182
39303
  const headers = {};
39183
39304
  if (authToken) {
39184
39305
  headers["Authorization"] = `Bearer ${authToken}`;
39185
39306
  }
39186
39307
  const res = await fetch(pollUrl, { headers });
39187
- if (!res.ok) return;
39188
- const data = await res.json();
39189
- const newStatus = data.status;
39190
- setStatus(newStatus);
39191
- if (data.progress) {
39192
- setProgress(data.progress);
39193
- }
39194
- if (newStatus === "complete" && data.output) {
39195
- const newTitle = data.output.title || initialTitle;
39196
- const newSlideCount = data.output.slide_count || 0;
39197
- const newFormats = data.output.formats || {};
39198
- setTitle(newTitle);
39199
- setSlideCount(newSlideCount);
39200
- setFormats(newFormats);
39201
- if (!hasNotifiedRef.current && onCompleteRef.current) {
39202
- hasNotifiedRef.current = true;
39203
- onCompleteRef.current({
39204
- title: newTitle,
39205
- slide_count: newSlideCount,
39206
- formats: newFormats
39207
- });
39208
- }
39308
+ if (!res.ok) throw new Error(`poll ${res.status}`);
39309
+ return res.json();
39310
+ },
39311
+ shouldContinue: (data) => data.status !== "complete" && data.status !== "failed"
39312
+ },
39313
+ (data) => {
39314
+ const newStatus = data.status;
39315
+ setStatus(newStatus);
39316
+ if (data.progress) {
39317
+ setProgress(data.progress);
39318
+ }
39319
+ if (newStatus === "complete" && data.output) {
39320
+ const newTitle = data.output.title || initialTitle;
39321
+ const newSlideCount = data.output.slide_count || 0;
39322
+ const newFormats = data.output.formats || {};
39323
+ setTitle(newTitle);
39324
+ setSlideCount(newSlideCount);
39325
+ setFormats(newFormats);
39326
+ if (!hasNotifiedRef.current && onCompleteRef.current) {
39327
+ hasNotifiedRef.current = true;
39328
+ onCompleteRef.current({
39329
+ title: newTitle,
39330
+ slide_count: newSlideCount,
39331
+ formats: newFormats
39332
+ });
39209
39333
  }
39210
- if (newStatus === "failed") {
39211
- const errorMsg = data.error || "Job failed";
39212
- setError(errorMsg);
39213
- if (!hasNotifiedRef.current && onFailedRef.current) {
39214
- hasNotifiedRef.current = true;
39215
- onFailedRef.current(errorMsg);
39216
- }
39334
+ }
39335
+ if (newStatus === "failed") {
39336
+ const errorMsg = data.error || "Job failed";
39337
+ setError(errorMsg);
39338
+ if (!hasNotifiedRef.current && onFailedRef.current) {
39339
+ hasNotifiedRef.current = true;
39340
+ onFailedRef.current(errorMsg);
39217
39341
  }
39218
- } catch {
39219
39342
  }
39220
- };
39221
- poll();
39222
- intervalRef.current = setInterval(poll, 3e3);
39223
- return () => {
39224
- if (intervalRef.current) clearInterval(intervalRef.current);
39225
- };
39226
- }, [isTerminal, pollUrl, authToken, initialTitle]);
39227
- useEffect6(() => {
39228
- if (isTerminal && intervalRef.current) {
39229
- clearInterval(intervalRef.current);
39230
- intervalRef.current = null;
39231
39343
  }
39232
- }, [isTerminal]);
39344
+ // Transient fetch errors are ignored — the shared loop retries next tick.
39345
+ );
39233
39346
  const handleShare = async () => {
39234
39347
  const link = shareUrl ?? (typeof window !== "undefined" && _job_id ? `${window.location.origin}/p/${_job_id}` : formats.html_url);
39235
39348
  if (!link) return;
@@ -39501,7 +39614,7 @@ var PresentationJobCard = ({
39501
39614
  };
39502
39615
 
39503
39616
  // src/molecules/generic/ResearchReportJobCard/ResearchReportJobCard.tsx
39504
- import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef6, useState as useState11 } from "react";
39617
+ import { useCallback as useCallback5, useEffect as useEffect8, useRef as useRef7, useState as useState11 } from "react";
39505
39618
  import { Fragment as Fragment6, jsx as jsx148, jsxs as jsxs109 } from "react/jsx-runtime";
39506
39619
  var DEFAULT_THEME = {
39507
39620
  primary: "#8b5cf6",
@@ -39557,14 +39670,14 @@ function formatTemplateLabel(templateId) {
39557
39670
  return templateId.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
39558
39671
  }
39559
39672
  var FullscreenPreviewModal = ({ url, title, onClose }) => {
39560
- useEffect7(() => {
39673
+ useEffect8(() => {
39561
39674
  const onKey = (e) => {
39562
39675
  if (e.key === "Escape") onClose();
39563
39676
  };
39564
39677
  document.addEventListener("keydown", onKey);
39565
39678
  return () => document.removeEventListener("keydown", onKey);
39566
39679
  }, [onClose]);
39567
- useEffect7(() => {
39680
+ useEffect8(() => {
39568
39681
  document.body.style.overflow = "hidden";
39569
39682
  return () => {
39570
39683
  document.body.style.overflow = "";
@@ -39655,60 +39768,59 @@ var ResearchReportJobCard = (props) => {
39655
39768
  const [approving, setApproving] = useState11(false);
39656
39769
  const [regenerating, setRegenerating] = useState11(false);
39657
39770
  const [approveError, setApproveError] = useState11(null);
39658
- const previewRef = useRef6(null);
39659
- const intervalRef = useRef6(null);
39660
- const onCompleteRef = useRef6(onComplete);
39661
- const onFailedRef = useRef6(onFailed);
39662
- const hasNotifiedRef = useRef6(false);
39771
+ const previewRef = useRef7(null);
39772
+ const onCompleteRef = useRef7(onComplete);
39773
+ const onFailedRef = useRef7(onFailed);
39774
+ const hasNotifiedRef = useRef7(false);
39663
39775
  onCompleteRef.current = onComplete;
39664
39776
  onFailedRef.current = onFailed;
39665
- useEffect7(() => {
39777
+ useEffect8(() => {
39666
39778
  const newStatus = initialStatus ?? (initialHtmlUrl ? "complete" : void 0);
39667
39779
  setStatus(newStatus);
39668
39780
  }, [initialStatus, initialHtmlUrl]);
39669
- useEffect7(() => {
39781
+ useEffect8(() => {
39670
39782
  if (initialTitle) setTitle(initialTitle);
39671
39783
  }, [initialTitle]);
39672
- useEffect7(() => {
39784
+ useEffect8(() => {
39673
39785
  if (initialHtmlUrl) setHtmlUrl(initialHtmlUrl);
39674
39786
  }, [initialHtmlUrl]);
39675
- useEffect7(() => {
39787
+ useEffect8(() => {
39676
39788
  if (initialGenerationMode) setGenerationMode(initialGenerationMode);
39677
39789
  }, [initialGenerationMode]);
39678
- useEffect7(() => {
39790
+ useEffect8(() => {
39679
39791
  if (initialTemplateId) setTemplateId(initialTemplateId);
39680
39792
  }, [initialTemplateId]);
39681
- useEffect7(() => {
39793
+ useEffect8(() => {
39682
39794
  if (initialTemplateVersionId) setTemplateVersionId(initialTemplateVersionId);
39683
39795
  }, [initialTemplateVersionId]);
39684
- useEffect7(() => {
39796
+ useEffect8(() => {
39685
39797
  if (initialReviewStatus) setReviewStatus(initialReviewStatus);
39686
39798
  }, [initialReviewStatus]);
39687
- useEffect7(() => {
39799
+ useEffect8(() => {
39688
39800
  if (initialDepth) setDepth(initialDepth);
39689
39801
  }, [initialDepth]);
39690
- useEffect7(() => {
39802
+ useEffect8(() => {
39691
39803
  if (initialSectionCount !== void 0) setSectionCount(initialSectionCount);
39692
39804
  }, [initialSectionCount]);
39693
- useEffect7(() => {
39805
+ useEffect8(() => {
39694
39806
  if (initialSourceCount !== void 0) setSourceCount(initialSourceCount);
39695
39807
  }, [initialSourceCount]);
39696
- useEffect7(() => {
39808
+ useEffect8(() => {
39697
39809
  if (initialWordCount !== void 0) setWordCount(initialWordCount);
39698
39810
  }, [initialWordCount]);
39699
- useEffect7(() => {
39811
+ useEffect8(() => {
39700
39812
  if (initialSummary) setSummary(initialSummary);
39701
39813
  }, [initialSummary]);
39702
39814
  const themePrimary = initialTheme?.primary;
39703
- useEffect7(() => {
39815
+ useEffect8(() => {
39704
39816
  if (initialTheme) setTheme(initialTheme);
39705
39817
  }, [themePrimary]);
39706
- useEffect7(() => {
39818
+ useEffect8(() => {
39707
39819
  if (initialError) setError(initialError);
39708
39820
  }, [initialError]);
39709
39821
  const progressPct = initialProgress?.percentage;
39710
39822
  const progressStep = initialProgress?.current_step;
39711
- useEffect7(() => {
39823
+ useEffect8(() => {
39712
39824
  if (initialProgress) setProgress(initialProgress);
39713
39825
  }, [progressPct, progressStep]);
39714
39826
  const isTerminal = status === "complete" || status === "failed";
@@ -39719,73 +39831,66 @@ var ResearchReportJobCard = (props) => {
39719
39831
  setPreviewScale(previewRef.current.offsetWidth / 800);
39720
39832
  }
39721
39833
  }, []);
39722
- useEffect7(() => {
39834
+ useEffect8(() => {
39723
39835
  updateScale();
39724
39836
  if (typeof ResizeObserver === "undefined") return;
39725
39837
  const ro = new ResizeObserver(updateScale);
39726
39838
  if (previewRef.current) ro.observe(previewRef.current);
39727
39839
  return () => ro.disconnect();
39728
39840
  }, [updateScale, htmlUrl]);
39729
- useEffect7(() => {
39730
- if (isTerminal || !pollUrl) return;
39731
- const poll = async () => {
39732
- try {
39841
+ useSharedPoll(
39842
+ {
39843
+ key: !isTerminal && pollUrl ? pollUrl : null,
39844
+ intervalMs: 3e3,
39845
+ fetcher: async () => {
39733
39846
  const headers = {};
39734
39847
  if (authToken) {
39735
39848
  headers["Authorization"] = `Bearer ${authToken}`;
39736
39849
  }
39737
39850
  const res = await fetch(pollUrl, { headers });
39738
- if (!res.ok) return;
39739
- const data = await res.json();
39740
- const newStatus = data.status;
39741
- setStatus(newStatus);
39742
- if (data.progress) {
39743
- setProgress(data.progress);
39851
+ if (!res.ok) throw new Error(`poll ${res.status}`);
39852
+ return res.json();
39853
+ },
39854
+ shouldContinue: (data) => data.status !== "complete" && data.status !== "failed"
39855
+ },
39856
+ (data) => {
39857
+ const newStatus = data.status;
39858
+ setStatus(newStatus);
39859
+ if (data.progress) {
39860
+ setProgress(data.progress);
39861
+ }
39862
+ if (newStatus === "complete" && data.output) {
39863
+ const output = data.output;
39864
+ setTitle(output.title || initialTitle);
39865
+ setDepth(output.depth || "");
39866
+ setSectionCount(output.section_count || 0);
39867
+ setSourceCount(output.source_count || 0);
39868
+ setWordCount(output.word_count || 0);
39869
+ setSummary(output.executive_summary || "");
39870
+ setHtmlUrl(output.html_url || "");
39871
+ if (output.generation_mode) setGenerationMode(output.generation_mode);
39872
+ if (output.template_id) setTemplateId(output.template_id);
39873
+ if (output.template_version_id) setTemplateVersionId(output.template_version_id);
39874
+ if (output.review_status) setReviewStatus(output.review_status);
39875
+ if (output.theme) {
39876
+ setTheme(output.theme);
39744
39877
  }
39745
- if (newStatus === "complete" && data.output) {
39746
- const output = data.output;
39747
- setTitle(output.title || initialTitle);
39748
- setDepth(output.depth || "");
39749
- setSectionCount(output.section_count || 0);
39750
- setSourceCount(output.source_count || 0);
39751
- setWordCount(output.word_count || 0);
39752
- setSummary(output.executive_summary || "");
39753
- setHtmlUrl(output.html_url || "");
39754
- if (output.generation_mode) setGenerationMode(output.generation_mode);
39755
- if (output.template_id) setTemplateId(output.template_id);
39756
- if (output.template_version_id) setTemplateVersionId(output.template_version_id);
39757
- if (output.review_status) setReviewStatus(output.review_status);
39758
- if (output.theme) {
39759
- setTheme(output.theme);
39760
- }
39761
- if (!hasNotifiedRef.current && onCompleteRef.current) {
39762
- hasNotifiedRef.current = true;
39763
- onCompleteRef.current(output);
39764
- }
39878
+ if (!hasNotifiedRef.current && onCompleteRef.current) {
39879
+ hasNotifiedRef.current = true;
39880
+ onCompleteRef.current(output);
39765
39881
  }
39766
- if (newStatus === "failed") {
39767
- const errorMsg = data.error || "Job failed";
39768
- setError(errorMsg);
39769
- if (!hasNotifiedRef.current && onFailedRef.current) {
39770
- hasNotifiedRef.current = true;
39771
- onFailedRef.current(errorMsg);
39772
- }
39882
+ }
39883
+ if (newStatus === "failed") {
39884
+ const errorMsg = data.error || "Job failed";
39885
+ setError(errorMsg);
39886
+ if (!hasNotifiedRef.current && onFailedRef.current) {
39887
+ hasNotifiedRef.current = true;
39888
+ onFailedRef.current(errorMsg);
39773
39889
  }
39774
- } catch {
39775
39890
  }
39776
- };
39777
- poll();
39778
- intervalRef.current = setInterval(poll, 3e3);
39779
- return () => {
39780
- if (intervalRef.current) clearInterval(intervalRef.current);
39781
- };
39782
- }, [isTerminal, pollUrl, authToken, initialTitle]);
39783
- useEffect7(() => {
39784
- if (isTerminal && intervalRef.current) {
39785
- clearInterval(intervalRef.current);
39786
- intervalRef.current = null;
39787
39891
  }
39788
- }, [isTerminal]);
39892
+ // Transient fetch errors are ignored — the shared loop retries next tick.
39893
+ );
39789
39894
  const formatWordCount = (count) => {
39790
39895
  if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
39791
39896
  return count.toString();
@@ -40209,7 +40314,7 @@ var ResearchReportJobCard = (props) => {
40209
40314
  };
40210
40315
 
40211
40316
  // src/molecules/generic/WebSearchJobCard/WebSearchJobCard.tsx
40212
- import { useEffect as useEffect8, useRef as useRef7, useState as useState12 } from "react";
40317
+ import { useEffect as useEffect9, useRef as useRef8, useState as useState12 } from "react";
40213
40318
  import { jsx as jsx149, jsxs as jsxs110 } from "react/jsx-runtime";
40214
40319
  var SearchIcon = () => /* @__PURE__ */ jsxs110("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
40215
40320
  /* @__PURE__ */ jsx149("circle", { cx: "11", cy: "11", r: "8" }),
@@ -40246,91 +40351,83 @@ var WebSearchJobCard = ({
40246
40351
  const [results, setResults] = useState12(initialResults || []);
40247
40352
  const [error, setError] = useState12(initialError);
40248
40353
  const [progress, setProgress] = useState12(initialProgress);
40249
- const intervalRef = useRef7(null);
40250
- const onCompleteRef = useRef7(onComplete);
40251
- const onFailedRef = useRef7(onFailed);
40252
- const hasNotifiedRef = useRef7(false);
40354
+ const onCompleteRef = useRef8(onComplete);
40355
+ const onFailedRef = useRef8(onFailed);
40356
+ const hasNotifiedRef = useRef8(false);
40253
40357
  onCompleteRef.current = onComplete;
40254
40358
  onFailedRef.current = onFailed;
40255
- useEffect8(() => {
40359
+ useEffect9(() => {
40256
40360
  setStatus(initialStatus);
40257
40361
  }, [initialStatus]);
40258
- useEffect8(() => {
40362
+ useEffect9(() => {
40259
40363
  if (initialQuery) setQuery(initialQuery);
40260
40364
  }, [initialQuery]);
40261
- useEffect8(() => {
40365
+ useEffect9(() => {
40262
40366
  if (initialTitle && !initialQuery) setQuery(initialTitle);
40263
40367
  }, [initialTitle, initialQuery]);
40264
- useEffect8(() => {
40368
+ useEffect9(() => {
40265
40369
  if (initialResultCount !== void 0) setResultCount(initialResultCount);
40266
40370
  }, [initialResultCount]);
40267
- useEffect8(() => {
40371
+ useEffect9(() => {
40268
40372
  if (initialSearchCount !== void 0) setSearchCount(initialSearchCount);
40269
40373
  }, [initialSearchCount]);
40270
- useEffect8(() => {
40374
+ useEffect9(() => {
40271
40375
  if (initialSummary) setSummary(initialSummary);
40272
40376
  }, [initialSummary]);
40273
- useEffect8(() => {
40377
+ useEffect9(() => {
40274
40378
  if (initialResults) setResults(initialResults);
40275
40379
  }, [initialResults]);
40276
- useEffect8(() => {
40380
+ useEffect9(() => {
40277
40381
  if (initialError) setError(initialError);
40278
40382
  }, [initialError]);
40279
40383
  const progressPct = initialProgress?.percentage;
40280
40384
  const progressStep = initialProgress?.current_step;
40281
- useEffect8(() => {
40385
+ useEffect9(() => {
40282
40386
  if (initialProgress) setProgress(initialProgress);
40283
40387
  }, [progressPct, progressStep]);
40284
40388
  const isTerminal = status === "complete" || status === "failed";
40285
- useEffect8(() => {
40286
- if (isTerminal || !pollUrl) return;
40287
- const poll = async () => {
40288
- try {
40389
+ useSharedPoll(
40390
+ {
40391
+ key: !isTerminal && pollUrl ? pollUrl : null,
40392
+ intervalMs: 3e3,
40393
+ fetcher: async () => {
40289
40394
  const headers = {};
40290
40395
  if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
40291
40396
  const res = await fetch(pollUrl, { headers });
40292
- if (!res.ok) return;
40293
- const data = await res.json();
40294
- const newStatus = data.status;
40295
- setStatus(newStatus);
40296
- if (data.progress) {
40297
- setProgress(data.progress);
40298
- }
40299
- if (newStatus === "complete" && data.output) {
40300
- const output = data.output;
40301
- setQuery(output.query || "");
40302
- setResultCount(output.result_count ?? 0);
40303
- setSearchCount(output.search_count ?? 0);
40304
- setSummary(output.summary || "");
40305
- setResults(output.results || []);
40306
- if (!hasNotifiedRef.current && onCompleteRef.current) {
40307
- hasNotifiedRef.current = true;
40308
- onCompleteRef.current(output);
40309
- }
40397
+ if (!res.ok) throw new Error(`poll ${res.status}`);
40398
+ return res.json();
40399
+ },
40400
+ shouldContinue: (data) => data.status !== "complete" && data.status !== "failed"
40401
+ },
40402
+ (data) => {
40403
+ const newStatus = data.status;
40404
+ setStatus(newStatus);
40405
+ if (data.progress) {
40406
+ setProgress(data.progress);
40407
+ }
40408
+ if (newStatus === "complete" && data.output) {
40409
+ const output = data.output;
40410
+ setQuery(output.query || "");
40411
+ setResultCount(output.result_count ?? 0);
40412
+ setSearchCount(output.search_count ?? 0);
40413
+ setSummary(output.summary || "");
40414
+ setResults(output.results || []);
40415
+ if (!hasNotifiedRef.current && onCompleteRef.current) {
40416
+ hasNotifiedRef.current = true;
40417
+ onCompleteRef.current(output);
40310
40418
  }
40311
- if (newStatus === "failed") {
40312
- const errorMsg = data.error || "Job failed";
40313
- setError(errorMsg);
40314
- if (!hasNotifiedRef.current && onFailedRef.current) {
40315
- hasNotifiedRef.current = true;
40316
- onFailedRef.current(errorMsg);
40317
- }
40419
+ }
40420
+ if (newStatus === "failed") {
40421
+ const errorMsg = data.error || "Job failed";
40422
+ setError(errorMsg);
40423
+ if (!hasNotifiedRef.current && onFailedRef.current) {
40424
+ hasNotifiedRef.current = true;
40425
+ onFailedRef.current(errorMsg);
40318
40426
  }
40319
- } catch {
40320
40427
  }
40321
- };
40322
- poll();
40323
- intervalRef.current = setInterval(poll, 3e3);
40324
- return () => {
40325
- if (intervalRef.current) clearInterval(intervalRef.current);
40326
- };
40327
- }, [isTerminal, pollUrl, authToken]);
40328
- useEffect8(() => {
40329
- if (isTerminal && intervalRef.current) {
40330
- clearInterval(intervalRef.current);
40331
- intervalRef.current = null;
40332
40428
  }
40333
- }, [isTerminal]);
40429
+ // Transient fetch errors are ignored — the shared loop retries next tick.
40430
+ );
40334
40431
  if (status === "pending" || status === "running") {
40335
40432
  const pct = progress?.percentage ?? 0;
40336
40433
  const step = progress?.current_step ?? "Starting web search...";
@@ -40473,7 +40570,7 @@ var WebSearchJobCard = ({
40473
40570
  import React110, { useMemo as useMemo6 } from "react";
40474
40571
 
40475
40572
  // src/molecules/creator-discovery/SearchSpecCard/CustomFieldRenderers.tsx
40476
- import { useState as useState13, useRef as useRef8, useEffect as useEffect9, useMemo as useMemo5 } from "react";
40573
+ import { useState as useState13, useRef as useRef9, useEffect as useEffect10, useMemo as useMemo5 } from "react";
40477
40574
 
40478
40575
  // src/lib/countries.ts
40479
40576
  var countries = [
@@ -40687,8 +40784,8 @@ var CountrySelectEdit = ({
40687
40784
  }) => {
40688
40785
  const [isDropdownOpen, setIsDropdownOpen] = useState13(false);
40689
40786
  const [searchTerm, setSearchTerm] = useState13("");
40690
- const dropdownRef = useRef8(null);
40691
- useEffect9(() => {
40787
+ const dropdownRef = useRef9(null);
40788
+ useEffect10(() => {
40692
40789
  const handleClickOutside = (event) => {
40693
40790
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
40694
40791
  setIsDropdownOpen(false);
@@ -41395,9 +41492,23 @@ import React112 from "react";
41395
41492
 
41396
41493
  // src/lib/auth-provider.ts
41397
41494
  var _provider = null;
41495
+ var _onUnauthorized = null;
41398
41496
  function setPxAuthTokenProvider(provider) {
41399
41497
  _provider = provider;
41400
41498
  }
41499
+ function setPxUnauthorizedHandler(handler) {
41500
+ _onUnauthorized = handler;
41501
+ }
41502
+ function notifyPxUnauthorized(status) {
41503
+ if (status !== 401) return false;
41504
+ if (_onUnauthorized) {
41505
+ try {
41506
+ _onUnauthorized();
41507
+ } catch {
41508
+ }
41509
+ }
41510
+ return true;
41511
+ }
41401
41512
  function getPxAuthToken() {
41402
41513
  if (_provider) {
41403
41514
  try {
@@ -41467,6 +41578,7 @@ async function defaultFetchSelections(sessionId) {
41467
41578
  body: "{}"
41468
41579
  }
41469
41580
  );
41581
+ if (res.status === 401) notifyPxUnauthorized(res.status);
41470
41582
  if (!res.ok) return {};
41471
41583
  const data = await res.json();
41472
41584
  const selections = data.selections || {};
@@ -41484,7 +41596,7 @@ async function defaultFetchSelections(sessionId) {
41484
41596
  async function defaultPersistSelection(sessionId, questionKey, value) {
41485
41597
  setLocalSelection(sessionId, questionKey, value);
41486
41598
  try {
41487
- await fetch(
41599
+ const res = await fetch(
41488
41600
  `${getBaseUrl()}/sessions/${sessionId}/mcq-selections`,
41489
41601
  {
41490
41602
  method: "PATCH",
@@ -41492,6 +41604,7 @@ async function defaultPersistSelection(sessionId, questionKey, value) {
41492
41604
  body: JSON.stringify({ question_key: questionKey, value })
41493
41605
  }
41494
41606
  );
41607
+ if (res.status === 401) notifyPxUnauthorized(res.status);
41495
41608
  } catch (err) {
41496
41609
  console.warn("[MCQ persist failed]", err);
41497
41610
  }
@@ -42480,10 +42593,10 @@ var CampaignConceptCard = React114.memo(
42480
42593
  }) });
42481
42594
  }
42482
42595
  if (typeof val === "object") {
42483
- const entries = Object.entries(val);
42484
- if (entries.length === 0)
42596
+ const entries2 = Object.entries(val);
42597
+ if (entries2.length === 0)
42485
42598
  return /* @__PURE__ */ jsx165("span", { className: "text-muted-foreground text-sm", children: "-" });
42486
- return /* @__PURE__ */ jsx165("div", { className: "space-y-2", children: entries.map(([k, v], idx) => /* @__PURE__ */ jsxs125("div", { className: "flex items-center gap-2", children: [
42599
+ return /* @__PURE__ */ jsx165("div", { className: "space-y-2", children: entries2.map(([k, v], idx) => /* @__PURE__ */ jsxs125("div", { className: "flex items-center gap-2", children: [
42487
42600
  /* @__PURE__ */ jsxs125("span", { className: "text-muted-foreground font-medium", children: [
42488
42601
  idx + 1,
42489
42602
  "."
@@ -42741,11 +42854,11 @@ CampaignConceptCard.displayName = "CampaignConceptCard";
42741
42854
  import { useCallback as useCallback9, useState as useState22, memo } from "react";
42742
42855
 
42743
42856
  // src/molecules/creator-discovery/CreatorWidget/CreatorImageList.tsx
42744
- import { useEffect as useEffect10, useState as useState15 } from "react";
42857
+ import { useEffect as useEffect11, useState as useState15 } from "react";
42745
42858
  import { Fragment as Fragment7, jsx as jsx166, jsxs as jsxs126 } from "react/jsx-runtime";
42746
42859
  function useMediaQuery(query) {
42747
42860
  const [matches, setMatches] = useState15(false);
42748
- useEffect10(() => {
42861
+ useEffect11(() => {
42749
42862
  const media = window.matchMedia(query);
42750
42863
  const listener = () => setMatches(media.matches);
42751
42864
  listener();
@@ -42828,7 +42941,7 @@ function CreatorImageList({
42828
42941
  }
42829
42942
 
42830
42943
  // src/molecules/creator-discovery/CreatorWidget/CreatorProgressBar.tsx
42831
- import { useEffect as useEffect11, useState as useState16 } from "react";
42944
+ import { useEffect as useEffect12, useState as useState16 } from "react";
42832
42945
  import { motion as motion2, AnimatePresence as AnimatePresence2 } from "framer-motion";
42833
42946
  import { jsx as jsx167, jsxs as jsxs127 } from "react/jsx-runtime";
42834
42947
  function truncateName(name, maxLength) {
@@ -42837,7 +42950,7 @@ function truncateName(name, maxLength) {
42837
42950
  }
42838
42951
  function ProgressBar({ overallPercentage }) {
42839
42952
  const [showTooltip, setShowTooltip] = useState16(true);
42840
- useEffect11(() => {
42953
+ useEffect12(() => {
42841
42954
  if (overallPercentage && overallPercentage >= 100) {
42842
42955
  setShowTooltip(false);
42843
42956
  }
@@ -42998,7 +43111,7 @@ function CreatorCompactView({
42998
43111
  }
42999
43112
 
43000
43113
  // src/molecules/creator-discovery/CreatorWidget/CreatorExpandedPanel.tsx
43001
- import { useState as useState20, useEffect as useEffect13, useCallback as useCallback7 } from "react";
43114
+ import { useState as useState20, useEffect as useEffect14, useCallback as useCallback7 } from "react";
43002
43115
  import ReactDOM2 from "react-dom";
43003
43116
  import { AnimatePresence as AnimatePresence4, motion as motion5 } from "framer-motion";
43004
43117
 
@@ -43026,6 +43139,7 @@ async function defaultFetchVersions(params) {
43026
43139
  const versionParam = params.version ? `&version=${params.version}` : "";
43027
43140
  const url = backend ? `${backend}/api/creators/versions?sessionId=${params.sessionId}${versionParam}&validated=${params.validated}` : `/api/get-creator-versions?sessionId=${params.sessionId}${versionParam}&validated=${params.validated}`;
43028
43141
  const res = await fetch(url, { headers: buildHeaders2() });
43142
+ if (res.status === 401) notifyPxUnauthorized(res.status);
43029
43143
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
43030
43144
  return res.json();
43031
43145
  }
@@ -43033,6 +43147,7 @@ async function defaultFetchStatus(params) {
43033
43147
  const backend = getBackendOrigin2();
43034
43148
  const url = backend ? `${backend}/api/creators/version-status?session_id=${params.sessionId}&version_no=${params.versionNo}` : `/api/get-creator-detail-status?session_id=${params.sessionId}&version_no=${params.versionNo}`;
43035
43149
  const res = await fetch(url, { headers: buildHeaders2() });
43150
+ if (res.status === 401) notifyPxUnauthorized(res.status);
43036
43151
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
43037
43152
  return res.json();
43038
43153
  }
@@ -43048,6 +43163,7 @@ async function defaultFetchCreatorDetails(params) {
43048
43163
  version_no: params.versionNo
43049
43164
  })
43050
43165
  });
43166
+ if (res.status === 401) notifyPxUnauthorized(res.status);
43051
43167
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
43052
43168
  return res.json();
43053
43169
  }
@@ -43956,7 +44072,7 @@ function BrandCollaborationsList({
43956
44072
  }
43957
44073
 
43958
44074
  // src/molecules/creator-discovery/CreatorWidget/CreatorGridView.tsx
43959
- import { useState as useState19, useMemo as useMemo10, useRef as useRef9, useCallback as useCallback6, useEffect as useEffect12 } from "react";
44075
+ import { useState as useState19, useMemo as useMemo10, useRef as useRef10, useCallback as useCallback6, useEffect as useEffect13 } from "react";
43960
44076
  import { motion as motion4 } from "framer-motion";
43961
44077
  import { jsx as jsx175, jsxs as jsxs134 } from "react/jsx-runtime";
43962
44078
  var formatFollowerCount3 = (count) => {
@@ -44008,17 +44124,17 @@ function CreatorGridViewCard({ creator }) {
44008
44124
  const [isExpanded, setIsExpanded] = useState19(false);
44009
44125
  const [showFullDescription, setShowFullDescription] = useState19(false);
44010
44126
  const [isDescriptionOverflowing, setIsDescriptionOverflowing] = useState19(false);
44011
- const descriptionRef = useRef9(null);
44012
- const cardRef = useRef9(null);
44127
+ const descriptionRef = useRef10(null);
44128
+ const cardRef = useRef10(null);
44013
44129
  const checkDescriptionOverflow = useCallback6(() => {
44014
44130
  const el = descriptionRef.current;
44015
44131
  if (!el) return;
44016
44132
  setIsDescriptionOverflowing(el.scrollHeight > el.clientHeight + 1);
44017
44133
  }, []);
44018
- useEffect12(() => {
44134
+ useEffect13(() => {
44019
44135
  checkDescriptionOverflow();
44020
44136
  }, [checkDescriptionOverflow, isExpanded, showFullDescription]);
44021
- useEffect12(() => {
44137
+ useEffect13(() => {
44022
44138
  const onResize = () => checkDescriptionOverflow();
44023
44139
  window.addEventListener("resize", onResize);
44024
44140
  return () => window.removeEventListener("resize", onResize);
@@ -44689,7 +44805,7 @@ function CreatorExpandedPanel({
44689
44805
  setLoading(false);
44690
44806
  }
44691
44807
  }, [creatorIds, sessionId, version, fetcher]);
44692
- useEffect13(() => {
44808
+ useEffect14(() => {
44693
44809
  if (isOpen && creatorIds.length > 0) {
44694
44810
  loadCreators();
44695
44811
  }
@@ -44743,13 +44859,18 @@ function CreatorExpandedPanel({
44743
44859
  }
44744
44860
 
44745
44861
  // src/molecules/creator-discovery/CreatorWidget/useCreatorWidgetPolling.ts
44746
- import { useState as useState21, useEffect as useEffect14, useCallback as useCallback8, useMemo as useMemo11, useRef as useRef10 } from "react";
44862
+ import { useState as useState21, useEffect as useEffect15, useCallback as useCallback8, useMemo as useMemo11, useRef as useRef11 } from "react";
44747
44863
  var DEFAULT_POLLING_CONFIG = {
44748
44864
  pollInterval: 5e3,
44749
44865
  maxDuration: 15 * 60 * 1e3,
44750
44866
  maxErrors: 10,
44751
44867
  secondsPerCreator: 13
44752
44868
  };
44869
+ var formatTime = (seconds) => {
44870
+ if (seconds <= 0) return "to complete";
44871
+ const minutes = Math.floor(seconds / 60);
44872
+ return minutes >= 1 ? `${minutes} min remaining...` : `${seconds} sec remaining...`;
44873
+ };
44753
44874
  function useCreatorWidgetPolling({
44754
44875
  sessionId,
44755
44876
  currentVersion,
@@ -44773,122 +44894,123 @@ function useCreatorWidgetPolling({
44773
44894
  const [statusDetails, setStatusDetails] = useState21();
44774
44895
  const [timeDisplay, setTimeDisplay] = useState21("");
44775
44896
  const [loadingStatus, setLoadingStatus] = useState21(true);
44776
- const remainingTimeRef = useRef10(0);
44897
+ const remainingTimeRef = useRef11(0);
44898
+ const countdownRef = useRef11(null);
44777
44899
  const requestedVersion = selectedVersion ?? currentVersion ?? versionData?.currentVersion;
44778
- const fetchVersionData = useCallback8(async () => {
44779
- if (!sessionId) return;
44780
- if (!versionData) setIsLoadingVersion(true);
44781
- try {
44782
- const data = await fetchVersions({
44783
- sessionId,
44784
- version: requestedVersion,
44785
- validated: isValidationComplete
44786
- });
44787
- if (data.success !== false) {
44900
+ const updateStatus = useCallback8(
44901
+ (status) => {
44902
+ setVersionStatus(status);
44903
+ onStatusChange?.(status);
44904
+ },
44905
+ [onStatusChange]
44906
+ );
44907
+ const versionKey = sessionId ? `creator:versions:${sessionId}:${requestedVersion ?? "latest"}:${isValidationComplete ? 1 : 0}` : null;
44908
+ useSharedPoll(
44909
+ {
44910
+ key: versionKey,
44911
+ intervalMs: config.pollInterval,
44912
+ fetcher: async () => {
44913
+ if (!versionData) setIsLoadingVersion(true);
44914
+ return fetchVersions({
44915
+ sessionId,
44916
+ version: requestedVersion,
44917
+ validated: isValidationComplete
44918
+ });
44919
+ },
44920
+ shouldContinue: (data) => !((data?.totalVersions || 0) > 0)
44921
+ },
44922
+ (data) => {
44923
+ if (data && data.success !== false) {
44788
44924
  setVersionData(data);
44789
44925
  setTotalVersions(data.totalVersions || 0);
44790
44926
  }
44791
- } catch (err) {
44792
- if (err.name !== "AbortError") {
44927
+ setIsLoadingVersion(false);
44928
+ },
44929
+ (err) => {
44930
+ if (err?.name !== "AbortError") {
44793
44931
  console.error("Error fetching creator version:", err);
44794
44932
  }
44795
- } finally {
44796
44933
  setIsLoadingVersion(false);
44797
44934
  }
44798
- }, [sessionId, requestedVersion, isValidationComplete, fetchVersions, versionData]);
44799
- useEffect14(() => {
44800
- fetchVersionData();
44801
- }, [sessionId, requestedVersion, isValidationComplete]);
44802
- useEffect14(() => {
44803
- if (totalVersions > 0 || !sessionId) return;
44804
- const interval = setInterval(() => {
44805
- if (totalVersions === 0) fetchVersionData();
44806
- }, config.pollInterval);
44807
- return () => clearInterval(interval);
44808
- }, [totalVersions, sessionId, fetchVersionData, config.pollInterval]);
44809
- useEffect14(() => {
44810
- if (!selectedVersion && !requestedVersion) return;
44811
- const activeVersion = selectedVersion ?? requestedVersion;
44812
- let isMounted = true;
44813
- let intervalId = null;
44814
- let timerIntervalId = null;
44815
- let elapsed = 0;
44816
- let errorCount = 0;
44935
+ );
44936
+ const activeVersion = selectedVersion ?? requestedVersion;
44937
+ const statusKey = sessionId && activeVersion != null ? `creator:status:${sessionId}:${activeVersion}` : null;
44938
+ const errorCountRef = useRef11(0);
44939
+ const deadlineRef = useRef11(0);
44940
+ const doneRef = useRef11(false);
44941
+ const stopCountdown = useCallback8(() => {
44942
+ if (countdownRef.current) {
44943
+ clearInterval(countdownRef.current);
44944
+ countdownRef.current = null;
44945
+ }
44946
+ setTimeDisplay("");
44947
+ }, []);
44948
+ useEffect15(() => {
44949
+ if (statusKey == null) return;
44817
44950
  setLoadingStatus(true);
44818
44951
  setStatusDetails(void 0);
44819
44952
  setVersionStatus("checking");
44953
+ errorCountRef.current = 0;
44954
+ doneRef.current = false;
44955
+ deadlineRef.current = Date.now() + config.maxDuration;
44820
44956
  const creatorLength2 = versionData?.length || 0;
44821
44957
  remainingTimeRef.current = creatorLength2 > 0 ? creatorLength2 * config.secondsPerCreator : 60;
44822
- const formatTime = (seconds) => {
44823
- if (seconds <= 0) return "to complete";
44824
- const minutes = Math.floor(seconds / 60);
44825
- return minutes >= 1 ? `${minutes} min remaining...` : `${seconds} sec remaining...`;
44826
- };
44827
44958
  setTimeDisplay(formatTime(remainingTimeRef.current));
44828
- timerIntervalId = setInterval(() => {
44959
+ countdownRef.current = setInterval(() => {
44829
44960
  if (remainingTimeRef.current > 0) remainingTimeRef.current -= 1;
44830
44961
  setTimeDisplay(formatTime(remainingTimeRef.current));
44831
44962
  }, 1e3);
44832
- const updateStatus = (status) => {
44833
- setVersionStatus(status);
44834
- onStatusChange?.(status);
44835
- };
44836
- const stopPolling = () => {
44837
- if (intervalId) clearInterval(intervalId);
44838
- if (timerIntervalId) clearInterval(timerIntervalId);
44839
- setTimeDisplay("");
44963
+ return () => {
44964
+ if (countdownRef.current) {
44965
+ clearInterval(countdownRef.current);
44966
+ countdownRef.current = null;
44967
+ }
44840
44968
  };
44841
- const pollStatus = async () => {
44842
- try {
44843
- const data = await fetchStatus({
44844
- sessionId,
44845
- versionNo: activeVersion
44846
- });
44847
- if (!isMounted) return;
44848
- if (data?.status) setStatusDetails(data.status);
44969
+ }, [statusKey]);
44970
+ useSharedPoll(
44971
+ {
44972
+ key: statusKey,
44973
+ intervalMs: config.pollInterval,
44974
+ fetcher: async () => fetchStatus({ sessionId, versionNo: activeVersion }),
44975
+ shouldContinue: (data) => {
44976
+ if (Date.now() >= deadlineRef.current) return false;
44849
44977
  const s = data?.status?.status;
44850
- if (s === "completed" || s === "complete") {
44851
- updateStatus(s);
44852
- setIsValidationComplete(true);
44853
- stopPolling();
44854
- return;
44855
- }
44856
- if (s === "failed") {
44857
- updateStatus("failed");
44858
- stopPolling();
44859
- return;
44860
- }
44861
- errorCount = 0;
44862
- updateStatus(s || "in-progress");
44863
- } catch (err) {
44864
- console.error("Error fetching status:", err);
44865
- errorCount++;
44866
- if (errorCount >= config.maxErrors) {
44867
- console.error(`Polling failed after ${config.maxErrors} consecutive errors`);
44868
- updateStatus("failed");
44869
- setLoadingStatus(false);
44870
- stopPolling();
44871
- return;
44872
- }
44873
- } finally {
44874
- setLoadingStatus(false);
44978
+ return !(s === "completed" || s === "complete" || s === "failed");
44875
44979
  }
44876
- };
44877
- pollStatus();
44878
- intervalId = setInterval(() => {
44879
- elapsed += config.pollInterval;
44880
- if (elapsed >= config.maxDuration) {
44881
- console.warn("Stopped polling after max duration");
44882
- stopPolling();
44980
+ },
44981
+ (data) => {
44982
+ if (data?.status) setStatusDetails(data.status);
44983
+ const s = data?.status?.status;
44984
+ if (s === "completed" || s === "complete") {
44985
+ updateStatus(s);
44986
+ setIsValidationComplete(true);
44987
+ doneRef.current = true;
44988
+ stopCountdown();
44989
+ } else if (s === "failed") {
44990
+ updateStatus("failed");
44991
+ doneRef.current = true;
44992
+ stopCountdown();
44883
44993
  } else {
44884
- pollStatus();
44994
+ errorCountRef.current = 0;
44995
+ updateStatus(s || "in-progress");
44885
44996
  }
44886
- }, config.pollInterval);
44887
- return () => {
44888
- isMounted = false;
44889
- stopPolling();
44890
- };
44891
- }, [selectedVersion, requestedVersion, sessionId]);
44997
+ setLoadingStatus(false);
44998
+ },
44999
+ (err) => {
45000
+ console.error("Error fetching status:", err);
45001
+ errorCountRef.current++;
45002
+ if (errorCountRef.current >= config.maxErrors) {
45003
+ console.error(
45004
+ `Polling failed after ${config.maxErrors} consecutive errors`
45005
+ );
45006
+ updateStatus("failed");
45007
+ doneRef.current = true;
45008
+ stopCountdown();
45009
+ if (statusKey) stopSharedPoll(statusKey);
45010
+ }
45011
+ setLoadingStatus(false);
45012
+ }
45013
+ );
44892
45014
  const versionNumbers = useMemo11(() => {
44893
45015
  if (!totalVersions) return [];
44894
45016
  return Array.from({ length: totalVersions }, (_, i) => i + 1);
@@ -44998,7 +45120,7 @@ function CreatorWidgetInner({
44998
45120
  var CreatorWidget = memo(CreatorWidgetInner);
44999
45121
 
45000
45122
  // src/molecules/analytics/AnalyticsChart.tsx
45001
- import { useEffect as useEffect15, useRef as useRef11, useState as useState23 } from "react";
45123
+ import { useEffect as useEffect16, useRef as useRef12, useState as useState23 } from "react";
45002
45124
  import { jsx as jsx178, jsxs as jsxs137 } from "react/jsx-runtime";
45003
45125
  function getCSSVar(name) {
45004
45126
  if (typeof document === "undefined") return "";
@@ -45115,12 +45237,12 @@ function AnalyticsChart({
45115
45237
  const [fetchedConfig, setFetchedConfig] = useState23(null);
45116
45238
  const [fetching, setFetching] = useState23(false);
45117
45239
  const [fetchError, setFetchError] = useState23(null);
45118
- const containerRef = useRef11(null);
45119
- const chartRef = useRef11(null);
45120
- useEffect15(() => {
45240
+ const containerRef = useRef12(null);
45241
+ const chartRef = useRef12(null);
45242
+ useEffect16(() => {
45121
45243
  setMounted(true);
45122
45244
  }, []);
45123
- useEffect15(() => {
45245
+ useEffect16(() => {
45124
45246
  if (!chartId || configProp) return;
45125
45247
  let cancelled = false;
45126
45248
  setFetching(true);
@@ -45142,7 +45264,7 @@ function AnalyticsChart({
45142
45264
  };
45143
45265
  }, [chartId, apiBase, authToken, configProp]);
45144
45266
  const activeConfig = configProp ?? fetchedConfig;
45145
- useEffect15(() => {
45267
+ useEffect16(() => {
45146
45268
  if (!mounted || !activeConfig || !containerRef.current) return;
45147
45269
  const container = containerRef.current;
45148
45270
  let cancelled = false;
@@ -45162,7 +45284,7 @@ function AnalyticsChart({
45162
45284
  cancelled = true;
45163
45285
  };
45164
45286
  }, [mounted, activeConfig]);
45165
- useEffect15(() => {
45287
+ useEffect16(() => {
45166
45288
  return () => {
45167
45289
  if (chartRef.current) {
45168
45290
  try {
@@ -45173,7 +45295,7 @@ function AnalyticsChart({
45173
45295
  }
45174
45296
  };
45175
45297
  }, []);
45176
- useEffect15(() => {
45298
+ useEffect16(() => {
45177
45299
  if (!mounted || !containerRef.current) return;
45178
45300
  const obs = new ResizeObserver(() => {
45179
45301
  try {
@@ -47584,7 +47706,9 @@ export {
47584
47706
  generateFieldsFromPropDefinitions,
47585
47707
  getPxAuthToken,
47586
47708
  isInputAtom,
47709
+ notifyPxUnauthorized,
47587
47710
  setPxAuthTokenProvider,
47711
+ setPxUnauthorizedHandler,
47588
47712
  submitWidgetToAgent,
47589
47713
  th,
47590
47714
  useCreatorWidgetPolling,