@cntyclub/agent-react 0.14.4 → 0.16.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"}
@@ -7,8 +7,10 @@ export interface ThinkingIndicatorProps {
7
7
  className?: string;
8
8
  }
9
9
  /**
10
- * A muted "typing" bubble: a spinning gradient ring plus a phrase that rotates
11
- * every ~1.8s, so a long tool run reads as active progress instead of a frozen "…".
10
+ * A muted "typing" bubble: a monochrome thinking orb plus an aurora-gradient
11
+ * phrase that rotates every ~1.8s, so a long tool run reads as active progress
12
+ * instead of a frozen "…". The aurora treatment lives on the TEXT only — the
13
+ * orb keeps the stock theme ink.
12
14
  */
13
15
  export declare function ThinkingIndicator({ className, intervalMs, phrases }: ThinkingIndicatorProps): React.JSX.Element;
14
16
  //# 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,qBA8ClG"}
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;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,UAAiB,EAAE,OAAO,EAAE,EAAE,sBAAsB,qBAoClG"}
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, ThinkingOrb, 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, 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,8 +559,8 @@ 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" }),
562
+ phase === "transcribing" ? /* @__PURE__ */ jsxs("span", { className: "flex flex-1 items-center gap-2 text-muted-foreground text-sm", children: [
563
+ /* @__PURE__ */ jsx(ThinkingOrb, { className: "shrink-0", size: 20, speed: 1, state: "working" }),
557
564
  "Transcribing\u2026"
558
565
  ] }) : null
559
566
  ]
@@ -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}`;
@@ -1029,18 +1036,7 @@ function ThinkingIndicator({ className, intervalMs = 1800, phrases }) {
1029
1036
  "data-slot": "thinking-indicator",
1030
1037
  role: "status",
1031
1038
  children: [
1032
- /* @__PURE__ */ jsx(
1033
- "span",
1034
- {
1035
- "aria-hidden": "true",
1036
- className: "size-4 shrink-0 animate-spin rounded-full [animation-duration:900ms]",
1037
- style: {
1038
- background: "conic-gradient(from 0deg, #FF0080, #7928CA, #0070F3, #38bdf8, #FF0080)",
1039
- WebkitMask: "radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px))",
1040
- mask: "radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px))"
1041
- }
1042
- }
1043
- ),
1039
+ /* @__PURE__ */ jsx(ThinkingOrb, { className: "shrink-0", size: 20, speed: 0.7, state: "solving" }),
1044
1040
  /* @__PURE__ */ jsx(
1045
1041
  "span",
1046
1042
  {
@@ -1897,8 +1893,16 @@ function useAgentChat(config) {
1897
1893
  if (response.messages?.length) {
1898
1894
  setMessages((current) => {
1899
1895
  const settled = current.filter((message) => !message.id.startsWith(OPTIMISTIC_ID_PREFIX));
1896
+ const optimistic = current.filter((message) => message.id.startsWith(OPTIMISTIC_ID_PREFIX));
1900
1897
  const seen = new Set(settled.map((message) => message.id));
1901
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
+ }
1902
1906
  return [...settled, ...fresh];
1903
1907
  });
1904
1908
  }
@@ -1918,8 +1922,13 @@ function useAgentChat(config) {
1918
1922
  case "user_message":
1919
1923
  setMessages((current) => {
1920
1924
  const settled = current.filter((message) => !message.id.startsWith(OPTIMISTIC_ID_PREFIX));
1921
- const seen = new Set(settled.map((message) => message.id));
1922
- 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
+ );
1923
1932
  });
1924
1933
  break;
1925
1934
  case "text_delta": {
@@ -1947,9 +1956,18 @@ function useAgentChat(config) {
1947
1956
  const streamingId = ctx.streamingId;
1948
1957
  ctx.streamingId = null;
1949
1958
  setMessages((current) => {
1950
- const withoutTransient = streamingId ? current.filter((message) => message.id !== streamingId) : current;
1951
- const seen = new Set(withoutTransient.map((message) => message.id));
1952
- 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];
1953
1971
  });
1954
1972
  break;
1955
1973
  }