@paymanai/payman-ask-sdk 2.0.1 → 2.0.3

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
@@ -458,7 +458,6 @@ function MarkdownImageV2({
458
458
  setIsLoaded(false);
459
459
  setHasError(false);
460
460
  setRetryCount(0);
461
- setIsVisible(false);
462
461
  }, [src]);
463
462
  useEffect(() => {
464
463
  const el = sentinelRef.current;
@@ -482,7 +481,13 @@ function MarkdownImageV2({
482
481
  {
483
482
  className: cn("payman-v2-md-image", "payman-v2-md-image-resolving"),
484
483
  children: /* @__PURE__ */ jsxs("span", { className: "payman-v2-md-image-resolving-inner", children: [
485
- /* @__PURE__ */ jsx(Loader2, { className: "payman-v2-md-image-loading", "aria-hidden": true }),
484
+ /* @__PURE__ */ jsx(
485
+ Loader2,
486
+ {
487
+ style: { animation: "payman-v2-spin 0.7s linear infinite" },
488
+ "aria-hidden": true
489
+ }
490
+ ),
486
491
  /* @__PURE__ */ jsx("span", { children: "Loading image" }),
487
492
  /* @__PURE__ */ jsx("span", { children: "Reference image" })
488
493
  ] })
@@ -521,14 +526,14 @@ function MarkdownImageV2({
521
526
  "aria-busy": !isLoaded,
522
527
  style: {
523
528
  ...frameStyle,
524
- ...!isLoaded ? { minHeight: "10rem" } : {}
529
+ minHeight: isLoaded ? 0 : "10rem"
525
530
  },
526
531
  children: [
527
- !isLoaded && /* @__PURE__ */ jsxs("span", { className: "payman-v2-md-image-placeholder", children: [
532
+ !isLoaded && /* @__PURE__ */ jsxs("span", { className: "payman-v2-md-image-loading", children: [
528
533
  /* @__PURE__ */ jsx(
529
534
  Loader2,
530
535
  {
531
- className: "payman-v2-md-image-loading",
536
+ style: { animation: "payman-v2-spin 0.7s linear infinite" },
532
537
  "aria-hidden": true
533
538
  }
534
539
  ),
@@ -872,26 +877,29 @@ function parseThinkingContent(content) {
872
877
  }
873
878
  return out;
874
879
  }
880
+ function formatDuration(seconds) {
881
+ if (seconds < 60) return `${seconds}s`;
882
+ const m = Math.floor(seconds / 60);
883
+ const s = seconds % 60;
884
+ return s > 0 ? `${m}m ${s}s` : `${m}m`;
885
+ }
875
886
  function AnimatedSeconds({
876
887
  value,
877
888
  className,
878
889
  ariaLabel
879
890
  }) {
880
- return /* @__PURE__ */ jsxs("span", { className: cn("payman-v2-thinking-seconds", className), "aria-label": ariaLabel, children: [
881
- /* @__PURE__ */ jsx("span", { className: "payman-v2-thinking-seconds-track", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: /* @__PURE__ */ jsx(
882
- motion.span,
883
- {
884
- className: "payman-v2-thinking-seconds-value",
885
- initial: { opacity: 0, y: 6, filter: "blur(2px)" },
886
- animate: { opacity: 1, y: 0, filter: "blur(0px)" },
887
- exit: { opacity: 0, y: -6, filter: "blur(2px)" },
888
- transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] },
889
- children: value
890
- },
891
- value
892
- ) }) }),
893
- /* @__PURE__ */ jsx("span", { className: "payman-v2-thinking-seconds-suffix", "aria-hidden": "true", children: "s" })
894
- ] });
891
+ return /* @__PURE__ */ jsx("span", { className: cn("payman-v2-thinking-seconds", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx("span", { className: "payman-v2-thinking-seconds-track", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: /* @__PURE__ */ jsx(
892
+ motion.span,
893
+ {
894
+ className: "payman-v2-thinking-seconds-value",
895
+ initial: { opacity: 0, y: 6, filter: "blur(2px)" },
896
+ animate: { opacity: 1, y: 0, filter: "blur(0px)" },
897
+ exit: { opacity: 0, y: -6, filter: "blur(2px)" },
898
+ transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] },
899
+ children: formatDuration(value)
900
+ },
901
+ value
902
+ ) }) }) });
895
903
  }
896
904
  function ThinkingBlockV2({
897
905
  content,
@@ -902,8 +910,14 @@ function ThinkingBlockV2({
902
910
  const [open, setOpen] = useState(true);
903
911
  const [cursorIdx, setCursorIdx] = useState(0);
904
912
  const [elapsedSec, setElapsedSec] = useState(
905
- () => startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)) : 0
913
+ () => (
914
+ // Only seed from startedAt when actively streaming — otherwise (e.g.
915
+ // after a hot-reload on a completed message) Date.now()-startedAt would
916
+ // be the time since completion, not the actual thinking duration.
917
+ isStreaming && startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)) : 0
918
+ )
906
919
  );
920
+ const elapsedSecRef = useRef(elapsedSec);
907
921
  const frozenSecRef = useRef(null);
908
922
  const prevStreaming = useRef(isStreaming);
909
923
  const parsed = useMemo(() => parseThinkingContent(content), [content]);
@@ -916,25 +930,25 @@ function ThinkingBlockV2({
916
930
  useEffect(() => {
917
931
  if (prevStreaming.current && !isStreaming) {
918
932
  setOpen(false);
919
- if (startedAt) {
920
- frozenSecRef.current = Math.max(
921
- 0,
922
- Math.floor((Date.now() - startedAt) / 1e3)
923
- );
924
- }
933
+ frozenSecRef.current = elapsedSecRef.current;
925
934
  }
926
935
  prevStreaming.current = isStreaming;
927
936
  }, [isStreaming, startedAt]);
928
937
  useEffect(() => {
929
938
  if (!isStreaming || !startedAt) return;
939
+ let active = true;
930
940
  const tick = () => {
931
- setElapsedSec(
932
- Math.max(0, Math.floor((Date.now() - startedAt) / 1e3))
933
- );
941
+ if (!active) return;
942
+ const val = Math.max(0, Math.floor((Date.now() - startedAt) / 1e3));
943
+ elapsedSecRef.current = val;
944
+ setElapsedSec(val);
934
945
  };
935
946
  tick();
936
947
  const id = window.setInterval(tick, 1e3);
937
- return () => window.clearInterval(id);
948
+ return () => {
949
+ active = false;
950
+ window.clearInterval(id);
951
+ };
938
952
  }, [isStreaming, startedAt]);
939
953
  useEffect(() => {
940
954
  if (!isStreaming || isFinalizingPhase) return;
@@ -945,10 +959,11 @@ function ThinkingBlockV2({
945
959
  }, [isStreaming, isFinalizingPhase]);
946
960
  const justStoppedSec = !isStreaming && prevStreaming.current && startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)) : void 0;
947
961
  const finalSec = getMaxDuration(
948
- durationSec,
949
962
  frozenSecRef.current,
950
963
  justStoppedSec,
951
- !isStreaming ? elapsedSec : void 0
964
+ // Only fall back to durationSec when the timer never ran this session
965
+ // (i.e. the message was already complete when this component mounted).
966
+ frozenSecRef.current == null && !prevStreaming.current ? durationSec : void 0
952
967
  );
953
968
  const headerLabel = isStreaming ? /* @__PURE__ */ jsxs("span", { className: "payman-v2-thinking-header-label", children: [
954
969
  /* @__PURE__ */ jsx("span", { className: "payman-v2-thinking-shimmer", children: "Working on it\u2026" }),
@@ -956,11 +971,11 @@ function ThinkingBlockV2({
956
971
  AnimatedSeconds,
957
972
  {
958
973
  className: "payman-v2-thinking-timer",
959
- ariaLabel: `Elapsed ${elapsedSec} seconds`,
974
+ ariaLabel: `Elapsed ${formatDuration(elapsedSec)}`,
960
975
  value: elapsedSec
961
976
  }
962
977
  )
963
- ] }) : finalSec !== null && finalSec !== void 0 ? /* @__PURE__ */ jsxs("span", { className: "payman-v2-thinking-header-label", children: [
978
+ ] }) : finalSec != null && finalSec > 0 ? /* @__PURE__ */ jsxs("span", { className: "payman-v2-thinking-header-label", children: [
964
979
  /* @__PURE__ */ jsx("span", { children: "Thought for" }),
965
980
  /* @__PURE__ */ jsx(
966
981
  AnimatedSeconds,
@@ -969,7 +984,7 @@ function ThinkingBlockV2({
969
984
  "payman-v2-thinking-timer",
970
985
  "payman-v2-thinking-duration"
971
986
  ),
972
- ariaLabel: `Thought for ${finalSec} seconds`,
987
+ ariaLabel: `Thought for ${formatDuration(finalSec)}`,
973
988
  value: finalSec
974
989
  }
975
990
  )
@@ -1091,6 +1106,7 @@ function charDelay(char, speed) {
1091
1106
  return speed.punctuation[0] + Math.random() * speed.punctuation[1];
1092
1107
  return speed.normal[0] + Math.random() * speed.normal[1];
1093
1108
  }
1109
+ var MARKDOWN_IMAGE_REGEX = /^!\[[^\]]*\]\([^)]*\)/;
1094
1110
  var typingProgressCache = /* @__PURE__ */ new Map();
1095
1111
  function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDisplayedText, cacheKey) {
1096
1112
  const cached = cacheKey ? typingProgressCache.get(cacheKey) : void 0;
@@ -1128,6 +1144,11 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
1128
1144
  }
1129
1145
  return;
1130
1146
  }
1147
+ if (displayedRef.current && !targetRef.current.startsWith(displayedRef.current)) {
1148
+ displayedRef.current = "";
1149
+ setDisplayedText("");
1150
+ if (cacheKeyRef.current) typingProgressCache.delete(cacheKeyRef.current);
1151
+ }
1131
1152
  if (displayedRef.current.length === 0 && initialDisplayedRef.current && targetRef.current.startsWith(initialDisplayedRef.current)) {
1132
1153
  writeDisplayed(initialDisplayedRef.current);
1133
1154
  }
@@ -1138,8 +1159,41 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
1138
1159
  runningRef.current = false;
1139
1160
  return;
1140
1161
  }
1162
+ if (displayedRef.current && !targetRef.current.startsWith(displayedRef.current)) {
1163
+ let divergeAt = 0;
1164
+ const minLen = Math.min(displayedRef.current.length, targetRef.current.length);
1165
+ while (divergeAt < minLen && displayedRef.current[divergeAt] === targetRef.current[divergeAt]) {
1166
+ divergeAt++;
1167
+ }
1168
+ const imgStart = targetRef.current.slice(0, divergeAt).lastIndexOf("![");
1169
+ if (imgStart >= 0) {
1170
+ const newImgMatch = MARKDOWN_IMAGE_REGEX.exec(targetRef.current.slice(imgStart));
1171
+ const oldImgMatch = MARKDOWN_IMAGE_REGEX.exec(displayedRef.current.slice(imgStart));
1172
+ if (newImgMatch && oldImgMatch) {
1173
+ const oldImgEnd = imgStart + oldImgMatch[0].length;
1174
+ const newImgEnd = imgStart + newImgMatch[0].length;
1175
+ const newCursor = Math.min(
1176
+ Math.max(displayedRef.current.length + (newImgEnd - oldImgEnd), newImgEnd),
1177
+ targetRef.current.length
1178
+ );
1179
+ writeDisplayed(targetRef.current.slice(0, newCursor));
1180
+ timerRef.current = setTimeout(tick, 0);
1181
+ return;
1182
+ }
1183
+ }
1184
+ writeDisplayed(targetRef.current.slice(0, imgStart >= 0 ? imgStart : divergeAt));
1185
+ timerRef.current = setTimeout(tick, 0);
1186
+ return;
1187
+ }
1141
1188
  if (displayedRef.current.length < targetRef.current.length) {
1142
- const nextChar = targetRef.current[displayedRef.current.length];
1189
+ const remaining = targetRef.current.slice(displayedRef.current.length);
1190
+ const imgMatch = MARKDOWN_IMAGE_REGEX.exec(remaining);
1191
+ if (imgMatch) {
1192
+ writeDisplayed(displayedRef.current + imgMatch[0]);
1193
+ timerRef.current = setTimeout(tick, 0);
1194
+ return;
1195
+ }
1196
+ const nextChar = remaining[0];
1143
1197
  writeDisplayed(displayedRef.current + nextChar);
1144
1198
  const delay = charDelay(nextChar, speed);
1145
1199
  timerRef.current = setTimeout(tick, delay);
@@ -1379,7 +1433,7 @@ function AssistantMessageV2({
1379
1433
  {
1380
1434
  content: thinkingContent,
1381
1435
  isStreaming: isThinkingStreaming,
1382
- durationSec: thinkingDuration,
1436
+ durationSec: message.thinkingDurationSec ?? thinkingDuration,
1383
1437
  startedAt: requestStartedAt
1384
1438
  }
1385
1439
  ),
@@ -3030,17 +3084,7 @@ function SessionList({
3030
3084
  [sessions, formattedNow]
3031
3085
  );
3032
3086
  if (!isReady) {
3033
- return /* @__PURE__ */ jsxs("div", { className: "payman-sidebar-notice", children: [
3034
- "Pass ",
3035
- /* @__PURE__ */ jsx(InlineCode, { children: "workflow.id" }),
3036
- " (or",
3037
- " ",
3038
- /* @__PURE__ */ jsx(InlineCode, { children: "workflow.name" }),
3039
- ") and",
3040
- " ",
3041
- /* @__PURE__ */ jsx(InlineCode, { children: "session.owner.id" }),
3042
- " in your chat config to view recent sessions."
3043
- ] });
3087
+ return /* @__PURE__ */ jsx("div", { className: "payman-sidebar-notice-shell", children: /* @__PURE__ */ jsx("div", { className: "payman-sidebar-notice", children: "Configure the chat settings." }) });
3044
3088
  }
3045
3089
  return /* @__PURE__ */ jsxs(
3046
3090
  "div",
@@ -3207,9 +3251,6 @@ function Spinner({ size = 12 }) {
3207
3251
  }
3208
3252
  );
3209
3253
  }
3210
- function InlineCode({ children }) {
3211
- return /* @__PURE__ */ jsx("code", { className: "payman-sidebar-inline-code", children });
3212
- }
3213
3254
  function groupSessionsByDate(sessions, now) {
3214
3255
  const groups = /* @__PURE__ */ new Map();
3215
3256
  const orderedGroups = [];
@@ -3871,7 +3912,8 @@ var PaymanChat = forwardRef(function PaymanChat2({
3871
3912
  alignItems: "center",
3872
3913
  justifyContent: "center",
3873
3914
  width: "100%",
3874
- minHeight: 0
3915
+ minHeight: 0,
3916
+ marginTop: "1.5rem"
3875
3917
  },
3876
3918
  children: /* @__PURE__ */ jsx(
3877
3919
  EmptyState2,