@meshagent/meshagent-tailwind 0.38.1 → 0.38.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/Chat.d.ts +11 -3
  3. package/dist/cjs/Chat.js +376 -29
  4. package/dist/cjs/ChatBotView.d.ts +29 -0
  5. package/dist/cjs/ChatBotView.js +491 -0
  6. package/dist/cjs/ChatInput.d.ts +12 -3
  7. package/dist/cjs/ChatInput.js +143 -44
  8. package/dist/cjs/ChatThread.d.ts +17 -3
  9. package/dist/cjs/ChatThread.js +646 -90
  10. package/dist/cjs/ChatTypingIndicator.d.ts +12 -5
  11. package/dist/cjs/ChatTypingIndicator.js +104 -13
  12. package/dist/cjs/FileUploader.d.ts +3 -2
  13. package/dist/cjs/FileUploader.js +35 -11
  14. package/dist/cjs/UploadPill.d.ts +2 -2
  15. package/dist/cjs/UploadPill.js +70 -32
  16. package/dist/cjs/chat-hooks.d.ts +38 -0
  17. package/dist/cjs/chat-hooks.js +390 -0
  18. package/dist/cjs/chat-message.d.ts +11 -0
  19. package/dist/cjs/chat-message.js +33 -0
  20. package/dist/cjs/components/ui/button.d.ts +1 -1
  21. package/dist/cjs/conversation-descriptor.d.ts +59 -0
  22. package/dist/cjs/conversation-descriptor.js +300 -0
  23. package/dist/cjs/file-attachment.d.ts +45 -0
  24. package/dist/cjs/file-attachment.js +171 -0
  25. package/dist/cjs/index.d.ts +5 -0
  26. package/dist/cjs/index.js +5 -0
  27. package/dist/cjs/multi-thread-view.d.ts +18 -0
  28. package/dist/cjs/multi-thread-view.js +88 -0
  29. package/dist/cjs/tools/ui-toolkit.d.ts +1 -1
  30. package/dist/cjs/tools/ui-toolkit.js +2 -1
  31. package/dist/esm/Chat.d.ts +11 -3
  32. package/dist/esm/Chat.js +378 -31
  33. package/dist/esm/ChatBotView.d.ts +29 -0
  34. package/dist/esm/ChatBotView.js +486 -0
  35. package/dist/esm/ChatInput.d.ts +12 -3
  36. package/dist/esm/ChatInput.js +143 -34
  37. package/dist/esm/ChatThread.d.ts +17 -3
  38. package/dist/esm/ChatThread.js +648 -92
  39. package/dist/esm/ChatTypingIndicator.d.ts +12 -5
  40. package/dist/esm/ChatTypingIndicator.js +94 -13
  41. package/dist/esm/FileUploader.d.ts +3 -2
  42. package/dist/esm/FileUploader.js +26 -12
  43. package/dist/esm/UploadPill.d.ts +2 -2
  44. package/dist/esm/UploadPill.js +60 -32
  45. package/dist/esm/chat-hooks.d.ts +38 -0
  46. package/dist/esm/chat-hooks.js +372 -0
  47. package/dist/esm/chat-message.d.ts +11 -0
  48. package/dist/esm/chat-message.js +13 -0
  49. package/dist/esm/components/ui/button.d.ts +1 -1
  50. package/dist/esm/conversation-descriptor.d.ts +59 -0
  51. package/dist/esm/conversation-descriptor.js +280 -0
  52. package/dist/esm/file-attachment.d.ts +45 -0
  53. package/dist/esm/file-attachment.js +151 -0
  54. package/dist/esm/index.d.ts +5 -0
  55. package/dist/esm/index.js +5 -0
  56. package/dist/esm/multi-thread-view.d.ts +18 -0
  57. package/dist/esm/multi-thread-view.js +68 -0
  58. package/dist/esm/tools/ui-toolkit.d.ts +1 -1
  59. package/dist/esm/tools/ui-toolkit.js +2 -1
  60. package/dist/index.css +1 -1
  61. package/package.json +3 -3
@@ -1,7 +1,14 @@
1
- import * as React from 'react';
2
- import { RoomClient } from '@meshagent/meshagent';
1
+ import * as React from "react";
2
+ import { RoomClient } from "@meshagent/meshagent";
3
3
  export interface ChatTypingIndicatorProps {
4
- room: RoomClient | null;
5
- path: string;
4
+ room?: RoomClient | null;
5
+ path?: string;
6
+ typing?: boolean;
7
+ thinking?: boolean;
8
+ statusText?: string | null;
9
+ startedAt?: Date | null;
10
+ onCancel?: () => void;
11
+ showCancelButton?: boolean;
12
+ cancelEnabled?: boolean;
6
13
  }
7
- export declare function ChatTypingIndicator({ room, path }: ChatTypingIndicatorProps): React.ReactElement | null;
14
+ export declare function ChatTypingIndicator({ room, path, typing, thinking, statusText, startedAt, onCancel, showCancelButton, cancelEnabled, }: ChatTypingIndicatorProps): React.ReactElement | null;
@@ -1,28 +1,109 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
2
3
  import { useRoomIndicators } from "@meshagent/meshagent-react";
3
- function ChatTypingIndicator({ room, path }) {
4
- const { typing, thinking } = useRoomIndicators({ room, path });
5
- return typing || thinking ? /* @__PURE__ */ jsxs("div", { className: "flex items-end space-x-1 h-6 p-6", children: [
6
- [0, 1, 2].map((index) => /* @__PURE__ */ jsx(
4
+ import { LoaderCircle, X } from "lucide-react";
5
+ import { formatThreadStatusText } from "./chat-hooks";
6
+ import { cn } from "./lib/utils";
7
+ function useStatusLabel(text, startedAt) {
8
+ const normalizedText = text?.trim() ?? "";
9
+ const [tick, setTick] = React.useState(0);
10
+ React.useEffect(() => {
11
+ if (normalizedText === "" || !(startedAt instanceof Date) || Number.isNaN(startedAt.getTime())) {
12
+ return;
13
+ }
14
+ const timer = window.setInterval(() => {
15
+ setTick((current) => current + 1);
16
+ }, 1e3);
17
+ return () => {
18
+ window.clearInterval(timer);
19
+ };
20
+ }, [normalizedText, startedAt]);
21
+ if (normalizedText === "") {
22
+ return null;
23
+ }
24
+ void tick;
25
+ return formatThreadStatusText(normalizedText, startedAt);
26
+ }
27
+ function ProcessingStatusRow({
28
+ text,
29
+ onCancel,
30
+ showCancelButton = false,
31
+ cancelEnabled = true
32
+ }) {
33
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-full border border-border/70 bg-background/90 px-3 py-2 shadow-sm backdrop-blur", children: [
34
+ showCancelButton ? /* @__PURE__ */ jsxs(
35
+ "button",
36
+ {
37
+ type: "button",
38
+ onClick: cancelEnabled ? onCancel : void 0,
39
+ disabled: !cancelEnabled,
40
+ title: cancelEnabled ? "Stop" : "Cancelling",
41
+ className: cn(
42
+ "relative inline-flex h-6 w-6 items-center justify-center rounded-full transition-opacity",
43
+ cancelEnabled ? "cursor-pointer" : "cursor-default opacity-55"
44
+ ),
45
+ children: [
46
+ /* @__PURE__ */ jsx(LoaderCircle, { className: "absolute h-6 w-6 animate-spin text-muted-foreground" }),
47
+ /* @__PURE__ */ jsx("span", { className: "relative inline-flex h-4 w-4 items-center justify-center rounded-full bg-foreground text-background", children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }) })
48
+ ]
49
+ }
50
+ ) : /* @__PURE__ */ jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-muted-foreground" }),
51
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: text })
52
+ ] });
53
+ }
54
+ function ChatTypingIndicator({
55
+ room = null,
56
+ path = "",
57
+ typing,
58
+ thinking,
59
+ statusText,
60
+ startedAt,
61
+ onCancel,
62
+ showCancelButton = false,
63
+ cancelEnabled = true
64
+ }) {
65
+ const roomIndicators = useRoomIndicators({ room, path });
66
+ const resolvedTyping = typing ?? roomIndicators.typing;
67
+ const resolvedThinking = thinking ?? roomIndicators.thinking;
68
+ const resolvedStatusText = useStatusLabel(
69
+ statusText?.trim() ? statusText : resolvedThinking ? "Thinking" : null,
70
+ startedAt
71
+ );
72
+ if (resolvedStatusText) {
73
+ return /* @__PURE__ */ jsx(
74
+ ProcessingStatusRow,
75
+ {
76
+ text: resolvedStatusText,
77
+ onCancel,
78
+ showCancelButton,
79
+ cancelEnabled
80
+ }
81
+ );
82
+ }
83
+ if (!resolvedTyping) {
84
+ return null;
85
+ }
86
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-full border border-border/70 bg-background/90 px-3 py-2 shadow-sm backdrop-blur", children: [
87
+ /* @__PURE__ */ jsx("div", { className: "flex items-end gap-1", children: [0, 1, 2].map((index) => /* @__PURE__ */ jsx(
7
88
  "span",
8
89
  {
9
- className: `inline-block w-2 h-2 bg-current rounded-full`,
90
+ className: "inline-block h-2 w-2 rounded-full bg-muted-foreground",
10
91
  style: {
11
- animation: "typingBounce 0.6s ease-in-out infinite",
92
+ animation: "chatTypingBounce 0.6s ease-in-out infinite",
12
93
  animationDelay: `${index * 0.2}s`
13
94
  }
14
95
  },
15
96
  index
16
- )),
97
+ )) }),
98
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Typing" }),
17
99
  /* @__PURE__ */ jsx("style", { children: `
18
- @keyframes typingBounce {
19
- 0%, 100% { transform: translateY(0); }
20
- 50% { transform: translateY(-4px); }
100
+ @keyframes chatTypingBounce {
101
+ 0%, 100% { transform: translateY(0); opacity: 0.55; }
102
+ 50% { transform: translateY(-4px); opacity: 1; }
21
103
  }
22
- ` })
23
- ] }) : null;
104
+ ` })
105
+ ] });
24
106
  }
25
- ;
26
107
  export {
27
108
  ChatTypingIndicator
28
109
  };
@@ -1,6 +1,7 @@
1
- import React from 'react';
1
+ import * as React from "react";
2
2
  export interface FileUploaderProps {
3
3
  onFilesSelected?: (files: File[]) => void;
4
4
  accept?: string;
5
+ disabled?: boolean;
5
6
  }
6
- export declare function FileUploader({ onFilesSelected, accept }: FileUploaderProps): React.ReactElement;
7
+ export declare function FileUploader({ onFilesSelected, accept, disabled, }: FileUploaderProps): React.ReactElement;
@@ -1,18 +1,28 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useRef } from "react";
3
- import { Plus } from "lucide-react";
2
+ import * as React from "react";
3
+ import { Paperclip } from "lucide-react";
4
4
  import { Button } from "./components/ui/button";
5
- function FileUploader({ onFilesSelected, accept = "" }) {
6
- const inputRef = useRef(null);
7
- const handleButtonClick = () => inputRef.current?.click();
8
- const handleFileChange = (e) => {
9
- if (!e.target.files) {
5
+ function FileUploader({
6
+ onFilesSelected,
7
+ accept = "",
8
+ disabled = false
9
+ }) {
10
+ const inputRef = React.useRef(null);
11
+ const handleButtonClick = React.useCallback(() => {
12
+ if (disabled) {
10
13
  return;
11
14
  }
12
- const fileArray = Array.from(e.target.files);
13
- onFilesSelected?.(fileArray);
14
- };
15
- return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15
+ inputRef.current?.click();
16
+ }, [disabled]);
17
+ const handleFileChange = React.useCallback((event) => {
18
+ if (!event.target.files) {
19
+ return;
20
+ }
21
+ const files = Array.from(event.target.files);
22
+ onFilesSelected?.(files);
23
+ event.target.value = "";
24
+ }, [onFilesSelected]);
25
+ return /* @__PURE__ */ jsxs("div", { className: "shrink-0", children: [
16
26
  /* @__PURE__ */ jsx(
17
27
  "input",
18
28
  {
@@ -20,6 +30,7 @@ function FileUploader({ onFilesSelected, accept = "" }) {
20
30
  type: "file",
21
31
  multiple: true,
22
32
  accept,
33
+ disabled,
23
34
  className: "hidden",
24
35
  onChange: handleFileChange
25
36
  }
@@ -27,11 +38,14 @@ function FileUploader({ onFilesSelected, accept = "" }) {
27
38
  /* @__PURE__ */ jsx(
28
39
  Button,
29
40
  {
41
+ type: "button",
30
42
  variant: "ghost",
31
43
  size: "icon",
32
44
  "aria-label": "Attach file",
45
+ className: "h-9 w-9 rounded-md",
46
+ disabled,
33
47
  onClick: handleButtonClick,
34
- children: /* @__PURE__ */ jsx(Plus, { className: "w-10 h-10" })
48
+ children: /* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4" })
35
49
  }
36
50
  )
37
51
  ] });
@@ -1,5 +1,5 @@
1
- import React from "react";
2
- import { FileUpload } from "@meshagent/meshagent-react";
1
+ import * as React from "react";
2
+ import { type FileUpload } from "./file-attachment";
3
3
  export interface UploadPillProps {
4
4
  attachment: FileUpload;
5
5
  onCancel: (attachment: FileUpload) => void;
@@ -1,39 +1,67 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useState, useCallback, useEffect } from "react";
2
+ import * as React from "react";
3
+ import { FileUp, LoaderCircle, TriangleAlert, X } from "lucide-react";
3
4
  import { Progress } from "./components/ui/progress";
4
- import { X } from "lucide-react";
5
- function UploadPill({ attachment, onCancel }) {
6
- const [progress, setProgress] = useState(0);
7
- const handleCancel = useCallback(() => onCancel(attachment), [attachment, onCancel]);
8
- useEffect(() => {
9
- const onChange = () => setProgress(
10
- Math.round(attachment.bytesUploaded * 100 / attachment.size)
11
- );
12
- attachment.on("status", onChange);
13
- return () => attachment.off("status", onChange);
5
+ import { UploadStatus } from "./file-attachment";
6
+ import { cn } from "./lib/utils";
7
+ function measureUploadProgress(attachment) {
8
+ if (attachment.size <= 0) {
9
+ return attachment.status === UploadStatus.Completed ? 100 : 0;
10
+ }
11
+ return Math.max(0, Math.min(100, Math.round(attachment.bytesUploaded * 100 / attachment.size)));
12
+ }
13
+ function useUploadState(attachment) {
14
+ const [progress, setProgress] = React.useState(() => measureUploadProgress(attachment));
15
+ const [status, setStatus] = React.useState(attachment.status);
16
+ React.useEffect(() => {
17
+ const updateState = () => {
18
+ setStatus(attachment.status);
19
+ setProgress(measureUploadProgress(attachment));
20
+ };
21
+ updateState();
22
+ attachment.on("change", updateState);
23
+ return () => {
24
+ attachment.off("change", updateState);
25
+ };
14
26
  }, [attachment]);
15
- return /* @__PURE__ */ jsxs("div", { className: "relative inline-flex max-w-full items-center border bg-muted pl-3 pr-1 py-1 gap-2", children: [
16
- /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium leading-none", children: attachment.filename }),
17
- /* @__PURE__ */ jsx(
18
- "button",
19
- {
20
- type: "button",
21
- onClick: handleCancel,
22
- "aria-label": "Cancel upload",
23
- className: "rounded-full p-1 transition-colors hover:bg-muted-foreground/20 focus:outline-none cursor-pointer focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
24
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
25
- }
26
- ),
27
- /* @__PURE__ */ jsx(
28
- Progress,
29
- {
30
- value: progress,
31
- className: "absolute left-0 bottom-0 h-0.5 w-full rounded-full bg-muted-foreground/20"
32
- }
33
- )
34
- ] });
27
+ return { progress, status };
28
+ }
29
+ function UploadPill({ attachment, onCancel }) {
30
+ const { progress, status } = useUploadState(attachment);
31
+ const handleCancel = React.useCallback(() => {
32
+ onCancel(attachment);
33
+ }, [attachment, onCancel]);
34
+ return /* @__PURE__ */ jsxs(
35
+ "div",
36
+ {
37
+ className: cn(
38
+ "relative inline-flex max-w-full items-center gap-2 overflow-hidden rounded-md border bg-muted/60 pl-3 pr-2 py-2 text-sm",
39
+ status === UploadStatus.Failed && "border-destructive/40 bg-destructive/5 text-destructive"
40
+ ),
41
+ children: [
42
+ status === UploadStatus.Failed ? /* @__PURE__ */ jsx(TriangleAlert, { className: "h-4 w-4 shrink-0" }) : status === UploadStatus.Completed ? /* @__PURE__ */ jsx(FileUp, { className: "h-4 w-4 shrink-0 text-muted-foreground" }) : /* @__PURE__ */ jsx(LoaderCircle, { className: "h-4 w-4 shrink-0 animate-spin text-muted-foreground" }),
43
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium leading-none", children: attachment.filename }),
44
+ /* @__PURE__ */ jsx(
45
+ "button",
46
+ {
47
+ type: "button",
48
+ onClick: handleCancel,
49
+ "aria-label": "Remove attachment",
50
+ className: "rounded-md p-1 transition-colors hover:bg-foreground/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
51
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
52
+ }
53
+ ),
54
+ /* @__PURE__ */ jsx(
55
+ Progress,
56
+ {
57
+ value: status === UploadStatus.Completed ? 100 : progress,
58
+ className: "absolute inset-x-0 bottom-0 h-0.5 rounded-none bg-border/60"
59
+ }
60
+ )
61
+ ]
62
+ }
63
+ );
35
64
  }
36
- ;
37
65
  export {
38
66
  UploadPill
39
67
  };
@@ -0,0 +1,38 @@
1
+ import { Element, MeshDocument, Participant, RemoteParticipant, RoomClient } from "@meshagent/meshagent";
2
+ import { ChatMessage } from "./chat-message";
3
+ import { type FileUpload } from "./file-attachment";
4
+ export interface UseChatThreadProps {
5
+ room: RoomClient;
6
+ path: string;
7
+ participants?: Participant[];
8
+ participantNames?: string[];
9
+ includeLocalParticipant?: boolean;
10
+ initialMessage?: ChatMessage;
11
+ agentName?: string;
12
+ }
13
+ export interface UseChatThreadResult {
14
+ document: MeshDocument | null;
15
+ messages: Element[];
16
+ sendMessage: (message: ChatMessage) => void;
17
+ selectAttachments: (files: File[]) => void;
18
+ attachments: FileUpload[];
19
+ setAttachments: (attachments: FileUpload[]) => void;
20
+ schemaFileExists: boolean;
21
+ onlineParticipants: RemoteParticipant[];
22
+ localParticipantName: string;
23
+ cancelRequest?: () => void;
24
+ }
25
+ export interface ThreadStatus {
26
+ text: string | null;
27
+ mode: string | null;
28
+ startedAt: Date | null;
29
+ supportsAgentMessages: boolean;
30
+ }
31
+ export interface UseThreadStatusProps {
32
+ room: RoomClient;
33
+ path: string;
34
+ agentName?: string;
35
+ }
36
+ export declare function formatThreadStatusText(text: string, startedAt?: Date | null): string;
37
+ export declare function useChatThread({ room, path, participants, participantNames, initialMessage, includeLocalParticipant, agentName, }: UseChatThreadProps): UseChatThreadResult;
38
+ export declare function useThreadStatus({ room, path, agentName }: UseThreadStatusProps): ThreadStatus;