@cntyclub/agent-react 0.14.3 → 0.15.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-voice-recorder.d.ts","sourceRoot":"","sources":["../../src/components/agent-voice-recorder.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,WAAW,uBAAuB;IACtC,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,4EAA4E;IAC5E,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,sEAAsE;IACtE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAqBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,uBAAuB,qBAwRhH"}
1
+ {"version":3,"file":"agent-voice-recorder.d.ts","sourceRoot":"","sources":["../../src/components/agent-voice-recorder.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,WAAW,uBAAuB;IACtC,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,4EAA4E;IAC5E,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,sEAAsE;IACtE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AA8BD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,uBAAuB,qBAiShH"}
@@ -1 +1 @@
1
- {"version":3,"file":"message-item.d.ts","sourceRoot":"","sources":["../../src/components/message-item.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAM7C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,4BAmEjE"}
1
+ {"version":3,"file":"message-item.d.ts","sourceRoot":"","sources":["../../src/components/message-item.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAM7C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,4BAsGjE"}
@@ -7,8 +7,9 @@ export interface ThinkingIndicatorProps {
7
7
  className?: string;
8
8
  }
9
9
  /**
10
- * A muted "typing" bubble: three bouncing dots plus a phrase that rotates every
11
- * ~1.8s, so a long tool run reads as active progress instead of a frozen "…".
10
+ * A muted "typing" bubble: an aurora-colored thinking orb plus a phrase that
11
+ * rotates every ~1.8s, so a long tool run reads as active progress instead of
12
+ * a frozen "…".
12
13
  */
13
14
  export declare function ThinkingIndicator({ className, intervalMs, phrases }: ThinkingIndicatorProps): React.JSX.Element;
14
15
  //# sourceMappingURL=thinking-indicator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"thinking-indicator.d.ts","sourceRoot":"","sources":["../../src/components/thinking-indicator.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,MAAM,WAAW,sBAAsB;IACrC,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,UAAiB,EAAE,OAAO,EAAE,EAAE,sBAAsB,qBAuClG"}
1
+ {"version":3,"file":"thinking-indicator.d.ts","sourceRoot":"","sources":["../../src/components/thinking-indicator.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,MAAM,WAAW,sBAAsB;IACrC,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,UAAiB,EAAE,OAAO,EAAE,EAAE,sBAAsB,qBAqClG"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { cn, Button, Tooltip, TooltipTrigger, TooltipContent, ChatMessage, ChatMessageBubble, Markdown, AuroraText, Spinner, Checkbox, Collapsible, CollapsibleTrigger, ChatToolChip, CollapsiblePanel, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Group, Menu, MenuTrigger, MenuPopup, MenuItem, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, Chat, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, useMediaQuery, TooltipProvider, DataTablePaged } from '@cntyclub/ui-react';
2
- import { XIcon, Trash2Icon, SquareIcon, PauseIcon, PlayIcon, CheckIcon, Loader2Icon, FileTextIcon, PaperclipIcon, MicIcon, ArrowUpIcon, CopyIcon, Maximize2Icon, ListChecksIcon, ChevronDownIcon, ShieldAlertIcon, CheckCheckIcon, UploadCloudIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, ChevronsRightIcon, SparklesIcon, DownloadIcon, CodeIcon } from 'lucide-react';
1
+ import { cn, Button, AuroraOrb, AuroraText, Tooltip, TooltipTrigger, TooltipContent, ChatMessage, ChatMessageBubble, Markdown, Spinner, Checkbox, Collapsible, CollapsibleTrigger, ChatToolChip, CollapsiblePanel, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Group, Menu, MenuTrigger, MenuPopup, MenuItem, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, Chat, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, useMediaQuery, TooltipProvider, DataTablePaged } from '@cntyclub/ui-react';
2
+ import { XIcon, Trash2Icon, SquareIcon, PauseIcon, PlayIcon, CheckIcon, FileTextIcon, PaperclipIcon, MicIcon, ArrowUpIcon, CopyIcon, Maximize2Icon, ListChecksIcon, ChevronDownIcon, ShieldAlertIcon, CheckCheckIcon, UploadCloudIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, ChevronsRightIcon, SparklesIcon, DownloadIcon, CodeIcon } from 'lucide-react';
3
3
  import * as React11 from 'react';
4
4
  import { AnimatePresence, motion } from 'motion/react';
5
5
  import { createPortal } from 'react-dom';
@@ -312,7 +312,10 @@ function AgentImageLightbox({ src, alt, onClose }) {
312
312
  document.body
313
313
  );
314
314
  }
315
- var BAR_COUNT = 40;
315
+ var BAR_WIDTH = 2;
316
+ var BAR_GAP = 3;
317
+ var MAX_BARS = 1024;
318
+ var FADE_BARS = 12;
316
319
  function formatClock(totalSeconds) {
317
320
  const minutes = Math.floor(totalSeconds / 60);
318
321
  const seconds = totalSeconds % 60;
@@ -340,7 +343,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
340
343
  const audioContextRef = React11.useRef(null);
341
344
  const analyserRef = React11.useRef(null);
342
345
  const rafRef = React11.useRef(null);
343
- const levelsRef = React11.useRef(new Array(BAR_COUNT).fill(0));
346
+ const levelsRef = React11.useRef(new Array(MAX_BARS).fill(0));
344
347
  const onErrorRef = React11.useRef(onError);
345
348
  onErrorRef.current = onError;
346
349
  const stopMeter = React11.useCallback(() => {
@@ -379,7 +382,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
379
382
  analyser.smoothingTimeConstant = 0.75;
380
383
  source.connect(analyser);
381
384
  analyserRef.current = analyser;
382
- levelsRef.current = new Array(BAR_COUNT).fill(0);
385
+ levelsRef.current = new Array(MAX_BARS).fill(0);
383
386
  const buffer = new Uint8Array(analyser.fftSize);
384
387
  const draw = () => {
385
388
  const canvas = canvasRef.current;
@@ -407,16 +410,19 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
407
410
  if (canvas.height !== pxHeight) canvas.height = pxHeight;
408
411
  ctx2d.clearRect(0, 0, pxWidth, pxHeight);
409
412
  ctx2d.fillStyle = getComputedStyle(canvas).color || "currentColor";
410
- const gap = Math.max(1, pxWidth / BAR_COUNT / 3);
411
- const barWidth = Math.max(1, (pxWidth - gap * (BAR_COUNT - 1)) / BAR_COUNT);
413
+ const barWidth = BAR_WIDTH * dpr;
414
+ const slot = (BAR_WIDTH + BAR_GAP) * dpr;
415
+ const count = Math.max(1, Math.min(MAX_BARS, Math.floor((pxWidth + BAR_GAP * dpr) / slot)));
412
416
  const midY = pxHeight / 2;
413
- const minBar = Math.max(1, dpr);
417
+ const minBar = barWidth;
418
+ const maxBar = pxHeight * 0.92;
414
419
  const radius = barWidth / 2;
415
- for (let i = 0; i < BAR_COUNT; i += 1) {
416
- const value = levels[i];
417
- const barHeight = Math.max(minBar, value * pxHeight);
418
- const x = i * (barWidth + gap);
420
+ for (let i = 0; i < count; i += 1) {
421
+ const value = levels[levels.length - count + i];
422
+ const barHeight = Math.max(minBar, value * maxBar);
423
+ const x = pxWidth - barWidth - (count - 1 - i) * slot;
419
424
  const y = midY - barHeight / 2;
425
+ ctx2d.globalAlpha = Math.min(1, (i + 1) / FADE_BARS);
420
426
  if (typeof ctx2d.roundRect === "function") {
421
427
  ctx2d.beginPath();
422
428
  ctx2d.roundRect(x, y, barWidth, barHeight, Math.min(radius, barHeight / 2));
@@ -425,6 +431,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
425
431
  ctx2d.fillRect(x, y, barWidth, barHeight);
426
432
  }
427
433
  }
434
+ ctx2d.globalAlpha = 1;
428
435
  rafRef.current = requestAnimationFrame(draw);
429
436
  };
430
437
  rafRef.current = requestAnimationFrame(draw);
@@ -540,7 +547,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
540
547
  ),
541
548
  /* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums", children: formatClock(elapsed) })
542
549
  ] }),
543
- /* @__PURE__ */ jsx("canvas", { "aria-hidden": true, className: "h-6 min-w-0 flex-1 text-foreground/80", ref: canvasRef }),
550
+ /* @__PURE__ */ jsx("canvas", { "aria-hidden": true, className: "h-6 min-w-0 flex-1 text-foreground/70", ref: canvasRef }),
544
551
  /* @__PURE__ */ jsx(Button, { "aria-label": "Discard recording", onClick: onCancel, size: "icon-sm", type: "button", variant: "ghost", children: /* @__PURE__ */ jsx(Trash2Icon, {}) }),
545
552
  /* @__PURE__ */ jsx(Button, { "aria-label": "Stop recording", onClick: stopRecording, size: "icon-sm", type: "button", children: /* @__PURE__ */ jsx(SquareIcon, {}) })
546
553
  ] }) : null,
@@ -552,9 +559,9 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
552
559
  /* @__PURE__ */ jsx(Button, { "aria-label": "Discard recording", onClick: onCancel, size: "icon-sm", type: "button", variant: "ghost", children: /* @__PURE__ */ jsx(Trash2Icon, {}) }),
553
560
  /* @__PURE__ */ jsx(Button, { "aria-label": "Use recording", className: "rounded-full before:rounded-full", onClick: () => void confirm(), size: "icon-sm", type: "button", children: /* @__PURE__ */ jsx(CheckIcon, {}) })
554
561
  ] }) : null,
555
- phase === "transcribing" ? /* @__PURE__ */ jsxs("span", { className: cn("flex flex-1 items-center gap-2 text-muted-foreground text-sm"), children: [
556
- /* @__PURE__ */ jsx(Loader2Icon, { className: "size-4 animate-spin" }),
557
- "Transcribing\u2026"
562
+ phase === "transcribing" ? /* @__PURE__ */ jsxs("span", { className: "flex flex-1 items-center gap-2 font-medium text-sm", children: [
563
+ /* @__PURE__ */ jsx(AuroraOrb, { speed: 1, state: "working" }),
564
+ /* @__PURE__ */ jsx(AuroraText, { children: "Transcribing" })
558
565
  ] }) : null
559
566
  ]
560
567
  }
@@ -809,14 +816,14 @@ function groupMessages(messages) {
809
816
  for (const message of messages) {
810
817
  if (message.role === "user") {
811
818
  flush();
812
- items.push({ key: message.id, kind: "user", message });
819
+ items.push({ key: message.client_key ?? message.id, kind: "user", message });
813
820
  continue;
814
821
  }
815
822
  if (message.role === "assistant") {
816
823
  const hasText = Boolean(message.content && message.content.trim());
817
824
  if (hasText) {
818
825
  flush();
819
- items.push({ key: message.id, kind: "assistant-text", message });
826
+ items.push({ key: message.client_key ?? message.id, kind: "assistant-text", message });
820
827
  }
821
828
  for (const toolCall of message.tool_calls ?? []) {
822
829
  const callId2 = toolCall.id ?? `${message.id}-${toolCall.name}`;
@@ -931,7 +938,7 @@ function MessageItem({ message }) {
931
938
  const attachments = message.attachments ?? [];
932
939
  const images = attachments.filter((a) => isImageAttachment(a.format) && a.url);
933
940
  const others = attachments.filter((a) => !isImageAttachment(a.format) || !a.url);
934
- return /* @__PURE__ */ jsxs(ChatMessage, { from: "user", children: [
941
+ return /* @__PURE__ */ jsxs(ChatMessage, { className: "group", from: "user", children: [
935
942
  images.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-end gap-1.5", children: images.map((attachment) => /* @__PURE__ */ jsx(
936
943
  "button",
937
944
  {
@@ -954,14 +961,37 @@ function MessageItem({ message }) {
954
961
  },
955
962
  attachment.id
956
963
  )) }) : null,
957
- message.content ? /* @__PURE__ */ jsx(ChatMessageBubble, { from: "user", children: message.content }) : null,
964
+ message.content ? /* @__PURE__ */ jsx(ChatMessageBubble, { className: "whitespace-pre-wrap", from: "user", children: message.content }) : null,
965
+ message.content ? /* @__PURE__ */ jsx(
966
+ "div",
967
+ {
968
+ className: cn(
969
+ "mt-0.5 flex justify-end opacity-0 transition-opacity",
970
+ "group-hover:opacity-100 focus-within:opacity-100"
971
+ ),
972
+ children: /* @__PURE__ */ jsx(CopyButton, { text: message.content })
973
+ }
974
+ ) : null,
958
975
  /* @__PURE__ */ jsx(AgentImageLightbox, { onClose: () => setLightboxSrc(null), src: lightboxSrc })
959
976
  ] });
960
977
  }
961
978
  if (message.role === "assistant") {
962
979
  if (!message.content || !message.content.trim()) return null;
963
980
  return /* @__PURE__ */ jsxs(ChatMessage, { className: "group", from: "assistant", children: [
964
- /* @__PURE__ */ jsx(ChatMessageBubble, { from: "assistant", children: /* @__PURE__ */ jsx(Markdown, { children: message.content }) }),
981
+ /* @__PURE__ */ jsx(ChatMessageBubble, { from: "assistant", children: /* @__PURE__ */ jsx(
982
+ "div",
983
+ {
984
+ className: "[&_img]:cursor-zoom-in [&_img]:rounded-md",
985
+ onClick: (event) => {
986
+ const target = event.target;
987
+ if (target.tagName !== "IMG") return;
988
+ const src = target.currentSrc || target.src;
989
+ if (src) setLightboxSrc(src);
990
+ },
991
+ role: "presentation",
992
+ children: /* @__PURE__ */ jsx(Markdown, { children: message.content })
993
+ }
994
+ ) }),
965
995
  /* @__PURE__ */ jsx(
966
996
  "div",
967
997
  {
@@ -971,7 +1001,8 @@ function MessageItem({ message }) {
971
1001
  ),
972
1002
  children: /* @__PURE__ */ jsx(CopyButton, { text: message.content })
973
1003
  }
974
- )
1004
+ ),
1005
+ /* @__PURE__ */ jsx(AgentImageLightbox, { onClose: () => setLightboxSrc(null), src: lightboxSrc })
975
1006
  ] });
976
1007
  }
977
1008
  return null;
@@ -1005,11 +1036,7 @@ function ThinkingIndicator({ className, intervalMs = 1800, phrases }) {
1005
1036
  "data-slot": "thinking-indicator",
1006
1037
  role: "status",
1007
1038
  children: [
1008
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
1009
- /* @__PURE__ */ jsx("span", { className: "size-1.5 animate-bounce rounded-full bg-muted-foreground/60 [animation-delay:0ms]" }),
1010
- /* @__PURE__ */ jsx("span", { className: "size-1.5 animate-bounce rounded-full bg-muted-foreground/60 [animation-delay:150ms]" }),
1011
- /* @__PURE__ */ jsx("span", { className: "size-1.5 animate-bounce rounded-full bg-muted-foreground/60 [animation-delay:300ms]" })
1012
- ] }),
1039
+ /* @__PURE__ */ jsx(AuroraOrb, { speed: 0.7, state: "solving" }),
1013
1040
  /* @__PURE__ */ jsx(
1014
1041
  "span",
1015
1042
  {
@@ -1866,8 +1893,16 @@ function useAgentChat(config) {
1866
1893
  if (response.messages?.length) {
1867
1894
  setMessages((current) => {
1868
1895
  const settled = current.filter((message) => !message.id.startsWith(OPTIMISTIC_ID_PREFIX));
1896
+ const optimistic = current.filter((message) => message.id.startsWith(OPTIMISTIC_ID_PREFIX));
1869
1897
  const seen = new Set(settled.map((message) => message.id));
1870
1898
  const fresh = response.messages?.filter((message) => !seen.has(message.id)) ?? [];
1899
+ const placeholder = optimistic[optimistic.length - 1];
1900
+ if (placeholder) {
1901
+ const echoIndex = fresh.findIndex((message) => message.role === "user");
1902
+ if (echoIndex !== -1) {
1903
+ fresh[echoIndex] = { ...fresh[echoIndex], client_key: placeholder.client_key ?? placeholder.id };
1904
+ }
1905
+ }
1871
1906
  return [...settled, ...fresh];
1872
1907
  });
1873
1908
  }
@@ -1887,8 +1922,13 @@ function useAgentChat(config) {
1887
1922
  case "user_message":
1888
1923
  setMessages((current) => {
1889
1924
  const settled = current.filter((message) => !message.id.startsWith(OPTIMISTIC_ID_PREFIX));
1890
- const seen = new Set(settled.map((message) => message.id));
1891
- return seen.has(event.message.id) ? settled : [...settled, event.message];
1925
+ if (settled.some((message) => message.id === event.message.id)) return settled;
1926
+ const optimistic = current.filter((message) => message.id.startsWith(OPTIMISTIC_ID_PREFIX));
1927
+ const placeholder = optimistic[optimistic.length - 1];
1928
+ if (!placeholder) return [...settled, event.message];
1929
+ return current.filter((message) => !message.id.startsWith(OPTIMISTIC_ID_PREFIX) || message.id === placeholder.id).map(
1930
+ (message) => message.id === placeholder.id ? { ...event.message, client_key: placeholder.client_key ?? placeholder.id } : message
1931
+ );
1892
1932
  });
1893
1933
  break;
1894
1934
  case "text_delta": {
@@ -1916,9 +1956,18 @@ function useAgentChat(config) {
1916
1956
  const streamingId = ctx.streamingId;
1917
1957
  ctx.streamingId = null;
1918
1958
  setMessages((current) => {
1919
- const withoutTransient = streamingId ? current.filter((message) => message.id !== streamingId) : current;
1920
- const seen = new Set(withoutTransient.map((message) => message.id));
1921
- return seen.has(event.message.id) ? withoutTransient : [...withoutTransient, event.message];
1959
+ if (current.some((message) => message.id === event.message.id)) {
1960
+ return streamingId ? current.filter((message) => message.id !== streamingId) : current;
1961
+ }
1962
+ if (streamingId) {
1963
+ const index = current.findIndex((message) => message.id === streamingId);
1964
+ if (index !== -1) {
1965
+ const next = [...current];
1966
+ next[index] = { ...event.message, client_key: streamingId };
1967
+ return next;
1968
+ }
1969
+ }
1970
+ return [...current, event.message];
1922
1971
  });
1923
1972
  break;
1924
1973
  }