@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.js CHANGED
@@ -483,7 +483,6 @@ function MarkdownImageV2({
483
483
  setIsLoaded(false);
484
484
  setHasError(false);
485
485
  setRetryCount(0);
486
- setIsVisible(false);
487
486
  }, [src]);
488
487
  react.useEffect(() => {
489
488
  const el = sentinelRef.current;
@@ -507,7 +506,13 @@ function MarkdownImageV2({
507
506
  {
508
507
  className: cn("payman-v2-md-image", "payman-v2-md-image-resolving"),
509
508
  children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-md-image-resolving-inner", children: [
510
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payman-v2-md-image-loading", "aria-hidden": true }),
509
+ /* @__PURE__ */ jsxRuntime.jsx(
510
+ lucideReact.Loader2,
511
+ {
512
+ style: { animation: "payman-v2-spin 0.7s linear infinite" },
513
+ "aria-hidden": true
514
+ }
515
+ ),
511
516
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Loading image" }),
512
517
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Reference image" })
513
518
  ] })
@@ -546,14 +551,14 @@ function MarkdownImageV2({
546
551
  "aria-busy": !isLoaded,
547
552
  style: {
548
553
  ...frameStyle,
549
- ...!isLoaded ? { minHeight: "10rem" } : {}
554
+ minHeight: isLoaded ? 0 : "10rem"
550
555
  },
551
556
  children: [
552
- !isLoaded && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-md-image-placeholder", children: [
557
+ !isLoaded && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-md-image-loading", children: [
553
558
  /* @__PURE__ */ jsxRuntime.jsx(
554
559
  lucideReact.Loader2,
555
560
  {
556
- className: "payman-v2-md-image-loading",
561
+ style: { animation: "payman-v2-spin 0.7s linear infinite" },
557
562
  "aria-hidden": true
558
563
  }
559
564
  ),
@@ -897,26 +902,29 @@ function parseThinkingContent(content) {
897
902
  }
898
903
  return out;
899
904
  }
905
+ function formatDuration(seconds) {
906
+ if (seconds < 60) return `${seconds}s`;
907
+ const m = Math.floor(seconds / 60);
908
+ const s = seconds % 60;
909
+ return s > 0 ? `${m}m ${s}s` : `${m}m`;
910
+ }
900
911
  function AnimatedSeconds({
901
912
  value,
902
913
  className,
903
914
  ariaLabel
904
915
  }) {
905
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("payman-v2-thinking-seconds", className), "aria-label": ariaLabel, children: [
906
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-thinking-seconds-track", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
907
- framerMotion.motion.span,
908
- {
909
- className: "payman-v2-thinking-seconds-value",
910
- initial: { opacity: 0, y: 6, filter: "blur(2px)" },
911
- animate: { opacity: 1, y: 0, filter: "blur(0px)" },
912
- exit: { opacity: 0, y: -6, filter: "blur(2px)" },
913
- transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] },
914
- children: value
915
- },
916
- value
917
- ) }) }),
918
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-thinking-seconds-suffix", "aria-hidden": "true", children: "s" })
919
- ] });
916
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("payman-v2-thinking-seconds", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-thinking-seconds-track", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
917
+ framerMotion.motion.span,
918
+ {
919
+ className: "payman-v2-thinking-seconds-value",
920
+ initial: { opacity: 0, y: 6, filter: "blur(2px)" },
921
+ animate: { opacity: 1, y: 0, filter: "blur(0px)" },
922
+ exit: { opacity: 0, y: -6, filter: "blur(2px)" },
923
+ transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] },
924
+ children: formatDuration(value)
925
+ },
926
+ value
927
+ ) }) }) });
920
928
  }
921
929
  function ThinkingBlockV2({
922
930
  content,
@@ -927,8 +935,14 @@ function ThinkingBlockV2({
927
935
  const [open, setOpen] = react.useState(true);
928
936
  const [cursorIdx, setCursorIdx] = react.useState(0);
929
937
  const [elapsedSec, setElapsedSec] = react.useState(
930
- () => startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)) : 0
938
+ () => (
939
+ // Only seed from startedAt when actively streaming — otherwise (e.g.
940
+ // after a hot-reload on a completed message) Date.now()-startedAt would
941
+ // be the time since completion, not the actual thinking duration.
942
+ isStreaming && startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)) : 0
943
+ )
931
944
  );
945
+ const elapsedSecRef = react.useRef(elapsedSec);
932
946
  const frozenSecRef = react.useRef(null);
933
947
  const prevStreaming = react.useRef(isStreaming);
934
948
  const parsed = react.useMemo(() => parseThinkingContent(content), [content]);
@@ -941,25 +955,25 @@ function ThinkingBlockV2({
941
955
  react.useEffect(() => {
942
956
  if (prevStreaming.current && !isStreaming) {
943
957
  setOpen(false);
944
- if (startedAt) {
945
- frozenSecRef.current = Math.max(
946
- 0,
947
- Math.floor((Date.now() - startedAt) / 1e3)
948
- );
949
- }
958
+ frozenSecRef.current = elapsedSecRef.current;
950
959
  }
951
960
  prevStreaming.current = isStreaming;
952
961
  }, [isStreaming, startedAt]);
953
962
  react.useEffect(() => {
954
963
  if (!isStreaming || !startedAt) return;
964
+ let active = true;
955
965
  const tick = () => {
956
- setElapsedSec(
957
- Math.max(0, Math.floor((Date.now() - startedAt) / 1e3))
958
- );
966
+ if (!active) return;
967
+ const val = Math.max(0, Math.floor((Date.now() - startedAt) / 1e3));
968
+ elapsedSecRef.current = val;
969
+ setElapsedSec(val);
959
970
  };
960
971
  tick();
961
972
  const id = window.setInterval(tick, 1e3);
962
- return () => window.clearInterval(id);
973
+ return () => {
974
+ active = false;
975
+ window.clearInterval(id);
976
+ };
963
977
  }, [isStreaming, startedAt]);
964
978
  react.useEffect(() => {
965
979
  if (!isStreaming || isFinalizingPhase) return;
@@ -970,10 +984,11 @@ function ThinkingBlockV2({
970
984
  }, [isStreaming, isFinalizingPhase]);
971
985
  const justStoppedSec = !isStreaming && prevStreaming.current && startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)) : void 0;
972
986
  const finalSec = getMaxDuration(
973
- durationSec,
974
987
  frozenSecRef.current,
975
988
  justStoppedSec,
976
- !isStreaming ? elapsedSec : void 0
989
+ // Only fall back to durationSec when the timer never ran this session
990
+ // (i.e. the message was already complete when this component mounted).
991
+ frozenSecRef.current == null && !prevStreaming.current ? durationSec : void 0
977
992
  );
978
993
  const headerLabel = isStreaming ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-thinking-header-label", children: [
979
994
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-thinking-shimmer", children: "Working on it\u2026" }),
@@ -981,11 +996,11 @@ function ThinkingBlockV2({
981
996
  AnimatedSeconds,
982
997
  {
983
998
  className: "payman-v2-thinking-timer",
984
- ariaLabel: `Elapsed ${elapsedSec} seconds`,
999
+ ariaLabel: `Elapsed ${formatDuration(elapsedSec)}`,
985
1000
  value: elapsedSec
986
1001
  }
987
1002
  )
988
- ] }) : finalSec !== null && finalSec !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-thinking-header-label", children: [
1003
+ ] }) : finalSec != null && finalSec > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-thinking-header-label", children: [
989
1004
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Thought for" }),
990
1005
  /* @__PURE__ */ jsxRuntime.jsx(
991
1006
  AnimatedSeconds,
@@ -994,7 +1009,7 @@ function ThinkingBlockV2({
994
1009
  "payman-v2-thinking-timer",
995
1010
  "payman-v2-thinking-duration"
996
1011
  ),
997
- ariaLabel: `Thought for ${finalSec} seconds`,
1012
+ ariaLabel: `Thought for ${formatDuration(finalSec)}`,
998
1013
  value: finalSec
999
1014
  }
1000
1015
  )
@@ -1116,6 +1131,7 @@ function charDelay(char, speed) {
1116
1131
  return speed.punctuation[0] + Math.random() * speed.punctuation[1];
1117
1132
  return speed.normal[0] + Math.random() * speed.normal[1];
1118
1133
  }
1134
+ var MARKDOWN_IMAGE_REGEX = /^!\[[^\]]*\]\([^)]*\)/;
1119
1135
  var typingProgressCache = /* @__PURE__ */ new Map();
1120
1136
  function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDisplayedText, cacheKey) {
1121
1137
  const cached = cacheKey ? typingProgressCache.get(cacheKey) : void 0;
@@ -1153,6 +1169,11 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
1153
1169
  }
1154
1170
  return;
1155
1171
  }
1172
+ if (displayedRef.current && !targetRef.current.startsWith(displayedRef.current)) {
1173
+ displayedRef.current = "";
1174
+ setDisplayedText("");
1175
+ if (cacheKeyRef.current) typingProgressCache.delete(cacheKeyRef.current);
1176
+ }
1156
1177
  if (displayedRef.current.length === 0 && initialDisplayedRef.current && targetRef.current.startsWith(initialDisplayedRef.current)) {
1157
1178
  writeDisplayed(initialDisplayedRef.current);
1158
1179
  }
@@ -1163,8 +1184,41 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
1163
1184
  runningRef.current = false;
1164
1185
  return;
1165
1186
  }
1187
+ if (displayedRef.current && !targetRef.current.startsWith(displayedRef.current)) {
1188
+ let divergeAt = 0;
1189
+ const minLen = Math.min(displayedRef.current.length, targetRef.current.length);
1190
+ while (divergeAt < minLen && displayedRef.current[divergeAt] === targetRef.current[divergeAt]) {
1191
+ divergeAt++;
1192
+ }
1193
+ const imgStart = targetRef.current.slice(0, divergeAt).lastIndexOf("![");
1194
+ if (imgStart >= 0) {
1195
+ const newImgMatch = MARKDOWN_IMAGE_REGEX.exec(targetRef.current.slice(imgStart));
1196
+ const oldImgMatch = MARKDOWN_IMAGE_REGEX.exec(displayedRef.current.slice(imgStart));
1197
+ if (newImgMatch && oldImgMatch) {
1198
+ const oldImgEnd = imgStart + oldImgMatch[0].length;
1199
+ const newImgEnd = imgStart + newImgMatch[0].length;
1200
+ const newCursor = Math.min(
1201
+ Math.max(displayedRef.current.length + (newImgEnd - oldImgEnd), newImgEnd),
1202
+ targetRef.current.length
1203
+ );
1204
+ writeDisplayed(targetRef.current.slice(0, newCursor));
1205
+ timerRef.current = setTimeout(tick, 0);
1206
+ return;
1207
+ }
1208
+ }
1209
+ writeDisplayed(targetRef.current.slice(0, imgStart >= 0 ? imgStart : divergeAt));
1210
+ timerRef.current = setTimeout(tick, 0);
1211
+ return;
1212
+ }
1166
1213
  if (displayedRef.current.length < targetRef.current.length) {
1167
- const nextChar = targetRef.current[displayedRef.current.length];
1214
+ const remaining = targetRef.current.slice(displayedRef.current.length);
1215
+ const imgMatch = MARKDOWN_IMAGE_REGEX.exec(remaining);
1216
+ if (imgMatch) {
1217
+ writeDisplayed(displayedRef.current + imgMatch[0]);
1218
+ timerRef.current = setTimeout(tick, 0);
1219
+ return;
1220
+ }
1221
+ const nextChar = remaining[0];
1168
1222
  writeDisplayed(displayedRef.current + nextChar);
1169
1223
  const delay = charDelay(nextChar, speed);
1170
1224
  timerRef.current = setTimeout(tick, delay);
@@ -1404,7 +1458,7 @@ function AssistantMessageV2({
1404
1458
  {
1405
1459
  content: thinkingContent,
1406
1460
  isStreaming: isThinkingStreaming,
1407
- durationSec: thinkingDuration,
1461
+ durationSec: message.thinkingDurationSec ?? thinkingDuration,
1408
1462
  startedAt: requestStartedAt
1409
1463
  }
1410
1464
  ),
@@ -3055,17 +3109,7 @@ function SessionList({
3055
3109
  [sessions, formattedNow]
3056
3110
  );
3057
3111
  if (!isReady) {
3058
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-sidebar-notice", children: [
3059
- "Pass ",
3060
- /* @__PURE__ */ jsxRuntime.jsx(InlineCode, { children: "workflow.id" }),
3061
- " (or",
3062
- " ",
3063
- /* @__PURE__ */ jsxRuntime.jsx(InlineCode, { children: "workflow.name" }),
3064
- ") and",
3065
- " ",
3066
- /* @__PURE__ */ jsxRuntime.jsx(InlineCode, { children: "session.owner.id" }),
3067
- " in your chat config to view recent sessions."
3068
- ] });
3112
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-notice-shell", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-notice", children: "Configure the chat settings." }) });
3069
3113
  }
3070
3114
  return /* @__PURE__ */ jsxRuntime.jsxs(
3071
3115
  "div",
@@ -3232,9 +3276,6 @@ function Spinner({ size = 12 }) {
3232
3276
  }
3233
3277
  );
3234
3278
  }
3235
- function InlineCode({ children }) {
3236
- return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "payman-sidebar-inline-code", children });
3237
- }
3238
3279
  function groupSessionsByDate(sessions, now) {
3239
3280
  const groups = /* @__PURE__ */ new Map();
3240
3281
  const orderedGroups = [];
@@ -3896,7 +3937,8 @@ var PaymanChat = react.forwardRef(function PaymanChat2({
3896
3937
  alignItems: "center",
3897
3938
  justifyContent: "center",
3898
3939
  width: "100%",
3899
- minHeight: 0
3940
+ minHeight: 0,
3941
+ marginTop: "1.5rem"
3900
3942
  },
3901
3943
  children: /* @__PURE__ */ jsxRuntime.jsx(
3902
3944
  EmptyState2,