@firstlovecenter/ai-chat 0.2.1 → 0.2.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/CHANGELOG.md +23 -0
- package/dist/server/index.cjs +38 -0
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +38 -0
- package/dist/server/index.js.map +1 -1
- package/dist/ui/index.cjs +57 -5
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +57 -5
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/ui/index.cjs
CHANGED
|
@@ -402,8 +402,22 @@ function sanitiseBlock(input) {
|
|
|
402
402
|
}
|
|
403
403
|
return { kind: "callout", tone: input.tone, text: input.text };
|
|
404
404
|
}
|
|
405
|
+
function isBlockEmpty(b) {
|
|
406
|
+
if (b.kind === "paragraph_brief") {
|
|
407
|
+
if (b.prose && b.prose.trim()) return false;
|
|
408
|
+
return b.key_facts.length === 0 || b.key_facts.every((f) => !f.trim());
|
|
409
|
+
}
|
|
410
|
+
if (b.kind === "list") {
|
|
411
|
+
return b.items.length === 0 || b.items.every((i) => !i.trim());
|
|
412
|
+
}
|
|
413
|
+
if (b.kind === "callout") return !b.text.trim();
|
|
414
|
+
if (b.kind === "chart") return b.data.length === 0;
|
|
415
|
+
if (b.kind === "table") return b.rows.length === 0;
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
405
418
|
function AnswerBlocks({ blocks }) {
|
|
406
|
-
|
|
419
|
+
const visible = blocks.filter((b) => !isBlockEmpty(b));
|
|
420
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-3", children: visible.map((b, i) => /* @__PURE__ */ jsxRuntime.jsx(BlockView, { block: b }, i)) });
|
|
407
421
|
}
|
|
408
422
|
function BlockView({ block }) {
|
|
409
423
|
if (block.kind === "paragraph_brief") {
|
|
@@ -544,6 +558,14 @@ function AiLineChart({
|
|
|
544
558
|
))
|
|
545
559
|
] }) }) });
|
|
546
560
|
}
|
|
561
|
+
function formatDuration(ms) {
|
|
562
|
+
if (ms < 0) ms = 0;
|
|
563
|
+
const totalSec = Math.round(ms / 1e3);
|
|
564
|
+
if (totalSec < 60) return `${totalSec}s`;
|
|
565
|
+
const m = Math.floor(totalSec / 60);
|
|
566
|
+
const s = totalSec % 60;
|
|
567
|
+
return s === 0 ? `${m}m` : `${m}m ${s}s`;
|
|
568
|
+
}
|
|
547
569
|
var PROVIDER_LABELS = {
|
|
548
570
|
claude: "Claude",
|
|
549
571
|
grok: "Grok",
|
|
@@ -718,7 +740,7 @@ function AiChat({
|
|
|
718
740
|
}
|
|
719
741
|
setAnswers((prev) => [
|
|
720
742
|
...prev,
|
|
721
|
-
{ question: trimmed, blocks: [], done: false }
|
|
743
|
+
{ question: trimmed, blocks: [], done: false, startedAt: Date.now() }
|
|
722
744
|
]);
|
|
723
745
|
const ac = new AbortController();
|
|
724
746
|
abortRef.current = ac;
|
|
@@ -739,6 +761,7 @@ function AiChat({
|
|
|
739
761
|
(prev) => updateLast(prev, (a) => ({
|
|
740
762
|
...a,
|
|
741
763
|
done: true,
|
|
764
|
+
durationMs: a.startedAt != null ? Date.now() - a.startedAt : void 0,
|
|
742
765
|
error: { code: "NETWORK", message }
|
|
743
766
|
}))
|
|
744
767
|
);
|
|
@@ -750,6 +773,7 @@ function AiChat({
|
|
|
750
773
|
(prev) => updateLast(prev, (a) => ({
|
|
751
774
|
...a,
|
|
752
775
|
done: true,
|
|
776
|
+
durationMs: a.startedAt != null ? Date.now() - a.startedAt : void 0,
|
|
753
777
|
error: { code: "NO_BODY", message: "No response stream." }
|
|
754
778
|
}))
|
|
755
779
|
);
|
|
@@ -776,6 +800,7 @@ function AiChat({
|
|
|
776
800
|
(prev) => updateLast(prev, (a) => ({
|
|
777
801
|
...a,
|
|
778
802
|
done: true,
|
|
803
|
+
durationMs: a.startedAt != null ? Date.now() - a.startedAt : void 0,
|
|
779
804
|
error: { code: "STREAM", message }
|
|
780
805
|
}))
|
|
781
806
|
);
|
|
@@ -808,7 +833,7 @@ function AiChat({
|
|
|
808
833
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
809
834
|
"aside",
|
|
810
835
|
{
|
|
811
|
-
|
|
836
|
+
inert: !sidebarOpen,
|
|
812
837
|
className: cn(
|
|
813
838
|
"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",
|
|
814
839
|
sidebarOpen ? "translate-x-0" : "-translate-x-full"
|
|
@@ -1074,6 +1099,14 @@ function AnswerView({
|
|
|
1074
1099
|
}, [answer.blocks, answer.error]);
|
|
1075
1100
|
const showActions = answer.done && (answer.blocks.length > 0 || answer.error != null);
|
|
1076
1101
|
const isThinking = !answer.done && answer.blocks.length === 0 && !answer.error;
|
|
1102
|
+
const [, forceTick] = React.useState(0);
|
|
1103
|
+
React.useEffect(() => {
|
|
1104
|
+
if (answer.done || answer.startedAt == null) return;
|
|
1105
|
+
const id = window.setInterval(() => forceTick((n) => n + 1), 1e3);
|
|
1106
|
+
return () => window.clearInterval(id);
|
|
1107
|
+
}, [answer.done, answer.startedAt]);
|
|
1108
|
+
const liveElapsed = answer.startedAt != null ? Date.now() - answer.startedAt : null;
|
|
1109
|
+
const finalDuration = answer.durationMs;
|
|
1077
1110
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1078
1111
|
"div",
|
|
1079
1112
|
{
|
|
@@ -1092,7 +1125,12 @@ function AnswerView({
|
|
|
1092
1125
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex size-7 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "size-4" }) }),
|
|
1093
1126
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-center text-sm text-muted-foreground", children: [
|
|
1094
1127
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-1 inline size-3.5 animate-spin" }),
|
|
1095
|
-
"Thinking\u2026"
|
|
1128
|
+
"Thinking\u2026",
|
|
1129
|
+
liveElapsed != null && liveElapsed >= 1e3 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-2 text-xs tabular-nums", children: [
|
|
1130
|
+
"(",
|
|
1131
|
+
formatDuration(liveElapsed),
|
|
1132
|
+
")"
|
|
1133
|
+
] })
|
|
1096
1134
|
] })
|
|
1097
1135
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
|
|
1098
1136
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "size-4" }) }),
|
|
@@ -1125,6 +1163,14 @@ function AnswerView({
|
|
|
1125
1163
|
className: "inline-flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
1126
1164
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "size-4" })
|
|
1127
1165
|
}
|
|
1166
|
+
),
|
|
1167
|
+
finalDuration != null && finalDuration >= 1e3 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1168
|
+
"span",
|
|
1169
|
+
{
|
|
1170
|
+
className: "ml-1 text-xs text-muted-foreground tabular-nums",
|
|
1171
|
+
title: "Time taken to generate this response",
|
|
1172
|
+
children: formatDuration(finalDuration)
|
|
1173
|
+
}
|
|
1128
1174
|
)
|
|
1129
1175
|
] })
|
|
1130
1176
|
] })
|
|
@@ -1330,7 +1376,13 @@ function handleEvent(raw, setAnswers) {
|
|
|
1330
1376
|
})
|
|
1331
1377
|
);
|
|
1332
1378
|
} else if (event === "done") {
|
|
1333
|
-
setAnswers(
|
|
1379
|
+
setAnswers(
|
|
1380
|
+
(prev) => updateLast(prev, (a) => ({
|
|
1381
|
+
...a,
|
|
1382
|
+
done: true,
|
|
1383
|
+
durationMs: a.startedAt != null ? Date.now() - a.startedAt : void 0
|
|
1384
|
+
}))
|
|
1385
|
+
);
|
|
1334
1386
|
} else if (event === "error") {
|
|
1335
1387
|
setAnswers(
|
|
1336
1388
|
(prev) => updateLast(prev, (a) => ({
|