@firstlovecenter/ai-chat 0.2.1 → 0.2.2

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/ui/index.js CHANGED
@@ -523,6 +523,14 @@ function AiLineChart({
523
523
  ))
524
524
  ] }) }) });
525
525
  }
526
+ function formatDuration(ms) {
527
+ if (ms < 0) ms = 0;
528
+ const totalSec = Math.round(ms / 1e3);
529
+ if (totalSec < 60) return `${totalSec}s`;
530
+ const m = Math.floor(totalSec / 60);
531
+ const s = totalSec % 60;
532
+ return s === 0 ? `${m}m` : `${m}m ${s}s`;
533
+ }
526
534
  var PROVIDER_LABELS = {
527
535
  claude: "Claude",
528
536
  grok: "Grok",
@@ -697,7 +705,7 @@ function AiChat({
697
705
  }
698
706
  setAnswers((prev) => [
699
707
  ...prev,
700
- { question: trimmed, blocks: [], done: false }
708
+ { question: trimmed, blocks: [], done: false, startedAt: Date.now() }
701
709
  ]);
702
710
  const ac = new AbortController();
703
711
  abortRef.current = ac;
@@ -718,6 +726,7 @@ function AiChat({
718
726
  (prev) => updateLast(prev, (a) => ({
719
727
  ...a,
720
728
  done: true,
729
+ durationMs: a.startedAt != null ? Date.now() - a.startedAt : void 0,
721
730
  error: { code: "NETWORK", message }
722
731
  }))
723
732
  );
@@ -729,6 +738,7 @@ function AiChat({
729
738
  (prev) => updateLast(prev, (a) => ({
730
739
  ...a,
731
740
  done: true,
741
+ durationMs: a.startedAt != null ? Date.now() - a.startedAt : void 0,
732
742
  error: { code: "NO_BODY", message: "No response stream." }
733
743
  }))
734
744
  );
@@ -755,6 +765,7 @@ function AiChat({
755
765
  (prev) => updateLast(prev, (a) => ({
756
766
  ...a,
757
767
  done: true,
768
+ durationMs: a.startedAt != null ? Date.now() - a.startedAt : void 0,
758
769
  error: { code: "STREAM", message }
759
770
  }))
760
771
  );
@@ -787,7 +798,7 @@ function AiChat({
787
798
  /* @__PURE__ */ jsxs(
788
799
  "aside",
789
800
  {
790
- "aria-hidden": !sidebarOpen,
801
+ inert: !sidebarOpen,
791
802
  className: cn(
792
803
  "absolute inset-y-0 left-0 z-20 flex w-72 max-w-[85vw] flex-col border-r border-border bg-sidebar text-sidebar-foreground shadow-lg transition-transform duration-200 ease-out",
793
804
  sidebarOpen ? "translate-x-0" : "-translate-x-full"
@@ -1053,6 +1064,14 @@ function AnswerView({
1053
1064
  }, [answer.blocks, answer.error]);
1054
1065
  const showActions = answer.done && (answer.blocks.length > 0 || answer.error != null);
1055
1066
  const isThinking = !answer.done && answer.blocks.length === 0 && !answer.error;
1067
+ const [, forceTick] = useState(0);
1068
+ useEffect(() => {
1069
+ if (answer.done || answer.startedAt == null) return;
1070
+ const id = window.setInterval(() => forceTick((n) => n + 1), 1e3);
1071
+ return () => window.clearInterval(id);
1072
+ }, [answer.done, answer.startedAt]);
1073
+ const liveElapsed = answer.startedAt != null ? Date.now() - answer.startedAt : null;
1074
+ const finalDuration = answer.durationMs;
1056
1075
  return /* @__PURE__ */ jsxs(
1057
1076
  "div",
1058
1077
  {
@@ -1071,7 +1090,12 @@ function AnswerView({
1071
1090
  /* @__PURE__ */ jsx("div", { className: "flex size-7 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }) }),
1072
1091
  /* @__PURE__ */ jsxs("p", { className: "flex items-center text-sm text-muted-foreground", children: [
1073
1092
  /* @__PURE__ */ jsx(Loader2, { className: "mr-1 inline size-3.5 animate-spin" }),
1074
- "Thinking\u2026"
1093
+ "Thinking\u2026",
1094
+ liveElapsed != null && liveElapsed >= 1e3 && /* @__PURE__ */ jsxs("span", { className: "ml-1 tabular-nums", children: [
1095
+ "(",
1096
+ formatDuration(liveElapsed),
1097
+ ")"
1098
+ ] })
1075
1099
  ] })
1076
1100
  ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
1077
1101
  /* @__PURE__ */ jsx("div", { className: "mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }) }),
@@ -1104,6 +1128,14 @@ function AnswerView({
1104
1128
  className: "inline-flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground",
1105
1129
  children: /* @__PURE__ */ jsx(RotateCcw, { className: "size-4" })
1106
1130
  }
1131
+ ),
1132
+ finalDuration != null && finalDuration >= 1e3 && /* @__PURE__ */ jsx(
1133
+ "span",
1134
+ {
1135
+ className: "ml-1 text-xs text-muted-foreground tabular-nums",
1136
+ title: "Time taken to generate this response",
1137
+ children: formatDuration(finalDuration)
1138
+ }
1107
1139
  )
1108
1140
  ] })
1109
1141
  ] })
@@ -1309,7 +1341,13 @@ function handleEvent(raw, setAnswers) {
1309
1341
  })
1310
1342
  );
1311
1343
  } else if (event === "done") {
1312
- setAnswers((prev) => updateLast(prev, (a) => ({ ...a, done: true })));
1344
+ setAnswers(
1345
+ (prev) => updateLast(prev, (a) => ({
1346
+ ...a,
1347
+ done: true,
1348
+ durationMs: a.startedAt != null ? Date.now() - a.startedAt : void 0
1349
+ }))
1350
+ );
1313
1351
  } else if (event === "error") {
1314
1352
  setAnswers(
1315
1353
  (prev) => updateLast(prev, (a) => ({