@gugacoder/agentic-chat 0.2.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.
Files changed (187) hide show
  1. package/dist/components/Chat.d.ts +21 -0
  2. package/dist/components/Chat.js +13 -0
  3. package/dist/components/ErrorNote.d.ts +5 -0
  4. package/dist/components/ErrorNote.js +6 -0
  5. package/dist/components/LazyRender.d.ts +8 -0
  6. package/dist/components/LazyRender.js +22 -0
  7. package/dist/components/Markdown.d.ts +5 -0
  8. package/dist/components/Markdown.js +65 -0
  9. package/dist/components/MessageBubble.d.ts +10 -0
  10. package/dist/components/MessageBubble.js +39 -0
  11. package/dist/components/MessageInput.d.ts +19 -0
  12. package/dist/components/MessageInput.js +214 -0
  13. package/dist/components/MessageList.d.ts +12 -0
  14. package/dist/components/MessageList.js +68 -0
  15. package/dist/components/StreamingIndicator.d.ts +1 -0
  16. package/dist/components/StreamingIndicator.js +9 -0
  17. package/dist/conversations/CollapsibleGroup.d.ts +11 -0
  18. package/dist/conversations/CollapsibleGroup.js +9 -0
  19. package/dist/conversations/ConversationBar.d.ts +27 -0
  20. package/dist/conversations/ConversationBar.js +53 -0
  21. package/dist/conversations/ConversationList.d.ts +33 -0
  22. package/dist/conversations/ConversationList.js +48 -0
  23. package/dist/conversations/ConversationListItem.d.ts +20 -0
  24. package/dist/conversations/ConversationListItem.js +22 -0
  25. package/dist/conversations/DeleteDialog.d.ts +13 -0
  26. package/dist/conversations/DeleteDialog.js +8 -0
  27. package/dist/conversations/RenameDialog.d.ts +15 -0
  28. package/dist/conversations/RenameDialog.js +15 -0
  29. package/dist/conversations/index.d.ts +9 -0
  30. package/dist/conversations/index.js +5 -0
  31. package/dist/conversations/types.d.ts +21 -0
  32. package/dist/conversations/types.js +1 -0
  33. package/dist/conversations/useConversations.d.ts +19 -0
  34. package/dist/conversations/useConversations.js +102 -0
  35. package/dist/conversations/utils.d.ts +8 -0
  36. package/dist/conversations/utils.js +134 -0
  37. package/dist/display/AlertRenderer.d.ts +2 -0
  38. package/dist/display/AlertRenderer.js +13 -0
  39. package/dist/display/CarouselRenderer.d.ts +2 -0
  40. package/dist/display/CarouselRenderer.js +41 -0
  41. package/dist/display/ChartRenderer.d.ts +2 -0
  42. package/dist/display/ChartRenderer.js +76 -0
  43. package/dist/display/ChoiceButtonsRenderer.d.ts +6 -0
  44. package/dist/display/ChoiceButtonsRenderer.js +23 -0
  45. package/dist/display/CodeBlockRenderer.d.ts +2 -0
  46. package/dist/display/CodeBlockRenderer.js +17 -0
  47. package/dist/display/ComparisonTableRenderer.d.ts +2 -0
  48. package/dist/display/ComparisonTableRenderer.js +26 -0
  49. package/dist/display/DataTableRenderer.d.ts +2 -0
  50. package/dist/display/DataTableRenderer.js +74 -0
  51. package/dist/display/FileCardRenderer.d.ts +2 -0
  52. package/dist/display/FileCardRenderer.js +31 -0
  53. package/dist/display/GalleryRenderer.d.ts +2 -0
  54. package/dist/display/GalleryRenderer.js +11 -0
  55. package/dist/display/ImageViewerRenderer.d.ts +2 -0
  56. package/dist/display/ImageViewerRenderer.js +15 -0
  57. package/dist/display/LinkPreviewRenderer.d.ts +2 -0
  58. package/dist/display/LinkPreviewRenderer.js +20 -0
  59. package/dist/display/MapViewRenderer.d.ts +2 -0
  60. package/dist/display/MapViewRenderer.js +20 -0
  61. package/dist/display/MetricCardRenderer.d.ts +2 -0
  62. package/dist/display/MetricCardRenderer.js +12 -0
  63. package/dist/display/PriceHighlightRenderer.d.ts +2 -0
  64. package/dist/display/PriceHighlightRenderer.js +13 -0
  65. package/dist/display/ProductCardRenderer.d.ts +2 -0
  66. package/dist/display/ProductCardRenderer.js +23 -0
  67. package/dist/display/ProgressStepsRenderer.d.ts +2 -0
  68. package/dist/display/ProgressStepsRenderer.js +14 -0
  69. package/dist/display/SourcesListRenderer.d.ts +2 -0
  70. package/dist/display/SourcesListRenderer.js +5 -0
  71. package/dist/display/SpreadsheetRenderer.d.ts +2 -0
  72. package/dist/display/SpreadsheetRenderer.js +32 -0
  73. package/dist/display/StepTimelineRenderer.d.ts +2 -0
  74. package/dist/display/StepTimelineRenderer.js +21 -0
  75. package/dist/display/index.d.ts +21 -0
  76. package/dist/display/index.js +20 -0
  77. package/dist/display/registry.d.ts +5 -0
  78. package/dist/display/registry.js +50 -0
  79. package/dist/hooks/ChatProvider.d.ts +10 -0
  80. package/dist/hooks/ChatProvider.js +14 -0
  81. package/dist/hooks/useBackboneChat.d.ts +37 -0
  82. package/dist/hooks/useBackboneChat.js +121 -0
  83. package/dist/hooks/useIsMobile.d.ts +1 -0
  84. package/dist/hooks/useIsMobile.js +12 -0
  85. package/dist/index.d.ts +47 -0
  86. package/dist/index.js +40 -0
  87. package/dist/lib/utils.d.ts +2 -0
  88. package/dist/lib/utils.js +5 -0
  89. package/dist/parts/PartRenderer.d.ts +40 -0
  90. package/dist/parts/PartRenderer.js +97 -0
  91. package/dist/parts/ReasoningBlock.d.ts +6 -0
  92. package/dist/parts/ReasoningBlock.js +18 -0
  93. package/dist/parts/ToolActivity.d.ts +11 -0
  94. package/dist/parts/ToolActivity.js +52 -0
  95. package/dist/parts/ToolResult.d.ts +7 -0
  96. package/dist/parts/ToolResult.js +38 -0
  97. package/dist/styles.css +2 -0
  98. package/dist/ui/alert.d.ts +12 -0
  99. package/dist/ui/alert.js +28 -0
  100. package/dist/ui/badge.d.ts +9 -0
  101. package/dist/ui/badge.js +20 -0
  102. package/dist/ui/button.d.ts +11 -0
  103. package/dist/ui/button.js +31 -0
  104. package/dist/ui/card.d.ts +8 -0
  105. package/dist/ui/card.js +21 -0
  106. package/dist/ui/collapsible.d.ts +1 -0
  107. package/dist/ui/collapsible.js +2 -0
  108. package/dist/ui/dialog.d.ts +19 -0
  109. package/dist/ui/dialog.js +23 -0
  110. package/dist/ui/dropdown-menu.d.ts +11 -0
  111. package/dist/ui/dropdown-menu.js +15 -0
  112. package/dist/ui/input.d.ts +3 -0
  113. package/dist/ui/input.js +6 -0
  114. package/dist/ui/progress.d.ts +7 -0
  115. package/dist/ui/progress.js +9 -0
  116. package/dist/ui/scroll-area.d.ts +5 -0
  117. package/dist/ui/scroll-area.js +12 -0
  118. package/dist/ui/separator.d.ts +4 -0
  119. package/dist/ui/separator.js +8 -0
  120. package/dist/ui/skeleton.d.ts +3 -0
  121. package/dist/ui/skeleton.js +6 -0
  122. package/dist/ui/table.d.ts +10 -0
  123. package/dist/ui/table.js +27 -0
  124. package/package.json +53 -0
  125. package/src/components/Chat.tsx +80 -0
  126. package/src/components/ErrorNote.tsx +32 -0
  127. package/src/components/LazyRender.tsx +42 -0
  128. package/src/components/Markdown.tsx +114 -0
  129. package/src/components/MessageBubble.tsx +102 -0
  130. package/src/components/MessageInput.tsx +421 -0
  131. package/src/components/MessageList.tsx +139 -0
  132. package/src/components/StreamingIndicator.tsx +19 -0
  133. package/src/conversations/CollapsibleGroup.tsx +41 -0
  134. package/src/conversations/ConversationBar.tsx +200 -0
  135. package/src/conversations/ConversationList.tsx +234 -0
  136. package/src/conversations/ConversationListItem.tsx +123 -0
  137. package/src/conversations/DeleteDialog.tsx +55 -0
  138. package/src/conversations/RenameDialog.tsx +74 -0
  139. package/src/conversations/index.ts +14 -0
  140. package/src/conversations/types.ts +17 -0
  141. package/src/conversations/useConversations.ts +148 -0
  142. package/src/conversations/utils.ts +159 -0
  143. package/src/display/AlertRenderer.tsx +27 -0
  144. package/src/display/CarouselRenderer.tsx +141 -0
  145. package/src/display/ChartRenderer.tsx +195 -0
  146. package/src/display/ChoiceButtonsRenderer.tsx +114 -0
  147. package/src/display/CodeBlockRenderer.tsx +49 -0
  148. package/src/display/ComparisonTableRenderer.tsx +132 -0
  149. package/src/display/DataTableRenderer.tsx +144 -0
  150. package/src/display/FileCardRenderer.tsx +55 -0
  151. package/src/display/GalleryRenderer.tsx +65 -0
  152. package/src/display/ImageViewerRenderer.tsx +114 -0
  153. package/src/display/LinkPreviewRenderer.tsx +74 -0
  154. package/src/display/MapViewRenderer.tsx +75 -0
  155. package/src/display/MetricCardRenderer.tsx +29 -0
  156. package/src/display/PriceHighlightRenderer.tsx +44 -0
  157. package/src/display/ProductCardRenderer.tsx +112 -0
  158. package/src/display/ProgressStepsRenderer.tsx +59 -0
  159. package/src/display/SourcesListRenderer.tsx +47 -0
  160. package/src/display/SpreadsheetRenderer.tsx +86 -0
  161. package/src/display/StepTimelineRenderer.tsx +75 -0
  162. package/src/display/index.ts +21 -0
  163. package/src/display/registry.ts +81 -0
  164. package/src/hooks/ChatProvider.tsx +22 -0
  165. package/src/hooks/useBackboneChat.ts +148 -0
  166. package/src/hooks/useIsMobile.ts +15 -0
  167. package/src/index.ts +80 -0
  168. package/src/lib/utils.ts +6 -0
  169. package/src/parts/PartRenderer.tsx +198 -0
  170. package/src/parts/ReasoningBlock.tsx +41 -0
  171. package/src/parts/ToolActivity.tsx +79 -0
  172. package/src/parts/ToolResult.tsx +79 -0
  173. package/src/styles.css +2 -0
  174. package/src/ui/alert.tsx +77 -0
  175. package/src/ui/badge.tsx +36 -0
  176. package/src/ui/button.tsx +54 -0
  177. package/src/ui/card.tsx +68 -0
  178. package/src/ui/collapsible.tsx +7 -0
  179. package/src/ui/dialog.tsx +122 -0
  180. package/src/ui/dropdown-menu.tsx +76 -0
  181. package/src/ui/input.tsx +24 -0
  182. package/src/ui/progress.tsx +36 -0
  183. package/src/ui/scroll-area.tsx +48 -0
  184. package/src/ui/separator.tsx +31 -0
  185. package/src/ui/skeleton.tsx +9 -0
  186. package/src/ui/table.tsx +114 -0
  187. package/tsconfig.json +17 -0
package/src/index.ts ADDED
@@ -0,0 +1,80 @@
1
+ // @codrstudio/agentic-chat
2
+ // Barrel exports
3
+
4
+ // Componente principal
5
+ export { Chat } from "./components/Chat.js";
6
+ export type { ChatProps } from "./components/Chat.js";
7
+
8
+ export { useBackboneChat } from "./hooks/useBackboneChat.js";
9
+ export type { UseBackboneChatOptions, Message } from "./hooks/useBackboneChat.js";
10
+
11
+ export { ChatProvider, useChatContext } from "./hooks/ChatProvider.js";
12
+ export type { ChatProviderProps } from "./hooks/ChatProvider.js";
13
+
14
+ export { Markdown } from "./components/Markdown.js";
15
+ export { StreamingIndicator } from "./components/StreamingIndicator.js";
16
+ export { ErrorNote } from "./components/ErrorNote.js";
17
+ export type { ErrorNoteProps } from "./components/ErrorNote.js";
18
+ export { MessageBubble } from "./components/MessageBubble.js";
19
+ export type { MessageBubbleProps } from "./components/MessageBubble.js";
20
+ export { MessageList } from "./components/MessageList.js";
21
+ export type { MessageListProps } from "./components/MessageList.js";
22
+ export { MessageInput } from "./components/MessageInput.js";
23
+ export type { MessageInputProps, Attachment } from "./components/MessageInput.js";
24
+
25
+ export { PartRenderer } from "./parts/PartRenderer.js";
26
+ export type { PartRendererProps } from "./parts/PartRenderer.js";
27
+
28
+ export { ReasoningBlock } from "./parts/ReasoningBlock.js";
29
+ export type { ReasoningBlockProps } from "./parts/ReasoningBlock.js";
30
+
31
+ export { ToolActivity, defaultToolIconMap } from "./parts/ToolActivity.js";
32
+ export type { ToolActivityProps, ToolActivityState } from "./parts/ToolActivity.js";
33
+
34
+ export { ToolResult } from "./parts/ToolResult.js";
35
+ export type { ToolResultProps } from "./parts/ToolResult.js";
36
+
37
+ // Display Renderers
38
+ export { AlertRenderer } from "./display/AlertRenderer.js";
39
+ export { MetricCardRenderer } from "./display/MetricCardRenderer.js";
40
+ export { PriceHighlightRenderer } from "./display/PriceHighlightRenderer.js";
41
+ export { FileCardRenderer } from "./display/FileCardRenderer.js";
42
+ export { CodeBlockRenderer } from "./display/CodeBlockRenderer.js";
43
+ export { SourcesListRenderer } from "./display/SourcesListRenderer.js";
44
+ export { StepTimelineRenderer } from "./display/StepTimelineRenderer.js";
45
+ export { ProgressStepsRenderer } from "./display/ProgressStepsRenderer.js";
46
+ export { ChartRenderer } from "./display/ChartRenderer.js";
47
+ export { CarouselRenderer } from "./display/CarouselRenderer.js";
48
+ export { ProductCardRenderer } from "./display/ProductCardRenderer.js";
49
+ export { ComparisonTableRenderer } from "./display/ComparisonTableRenderer.js";
50
+ export { DataTableRenderer } from "./display/DataTableRenderer.js";
51
+ export { SpreadsheetRenderer } from "./display/SpreadsheetRenderer.js";
52
+ export { GalleryRenderer } from "./display/GalleryRenderer.js";
53
+ export { ImageViewerRenderer } from "./display/ImageViewerRenderer.js";
54
+ export { LinkPreviewRenderer } from "./display/LinkPreviewRenderer.js";
55
+ export { MapViewRenderer } from "./display/MapViewRenderer.js";
56
+ export { ChoiceButtonsRenderer } from "./display/ChoiceButtonsRenderer.js";
57
+
58
+ // Display Registry
59
+ export { defaultDisplayRenderers, resolveDisplayRenderer } from "./display/registry.js";
60
+ export type { DisplayRendererMap, DisplayActionName } from "./display/registry.js";
61
+
62
+ // Conversation management
63
+ export {
64
+ ConversationList,
65
+ ConversationBar,
66
+ useConversations,
67
+ useIsMobile,
68
+ formatRelativeTime,
69
+ buildInitialMessages,
70
+ groupConversations,
71
+ } from "./conversations/index.js";
72
+
73
+ export type {
74
+ ConversationListProps,
75
+ ConversationBarProps,
76
+ UseConversationsOptions,
77
+ UseConversationsReturn,
78
+ Conversation,
79
+ BackendMessage,
80
+ } from "./conversations/index.js";
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,198 @@
1
+ import { memo, useState } from "react";
2
+ import { Paperclip, FileText, ChevronDown } from "lucide-react";
3
+ import { Markdown } from "../components/Markdown.js";
4
+ import { LazyRender } from "../components/LazyRender.js";
5
+ import { ReasoningBlock } from "./ReasoningBlock.js";
6
+ import { ToolActivity } from "./ToolActivity.js";
7
+ import { ToolResult } from "./ToolResult.js";
8
+ import { resolveDisplayRenderer } from "../display/registry.js";
9
+ import type { DisplayRendererMap } from "../display/registry.js";
10
+ import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "../ui/collapsible.js";
11
+ import { cn } from "../lib/utils.js";
12
+
13
+ const HEAVY_RENDERERS = new Set([
14
+ "chart", "map", "table",
15
+ "spreadsheet", "gallery", "image",
16
+ ]);
17
+
18
+ // Minimal part types — mirrors @ai-sdk/react Message["parts"]
19
+ type TextPart = { type: "text"; text: string };
20
+ type ReasoningPart = { type: "reasoning"; reasoning: string };
21
+ type ToolInvocationPart = {
22
+ type: "tool-invocation";
23
+ toolInvocation: {
24
+ toolName: string;
25
+ toolCallId?: string;
26
+ state: "call" | "partial-call" | "result";
27
+ args?: Record<string, unknown>;
28
+ result?: unknown;
29
+ };
30
+ };
31
+ type ImageAttachmentPart = { type: "image"; _ref?: string; mimeType?: string };
32
+ type FileAttachmentPart = { type: "file"; _ref?: string; mimeType?: string };
33
+ type MessagePart = TextPart | ReasoningPart | ToolInvocationPart | ImageAttachmentPart | FileAttachmentPart | { type: string };
34
+
35
+ export interface PartRendererProps {
36
+ part: MessagePart;
37
+ isStreaming?: boolean;
38
+ displayRenderers?: DisplayRendererMap;
39
+ attachmentUrl?: (ref: string) => string;
40
+ }
41
+
42
+ // ─── Attachment sub-components ────────────────────────────────────────────────
43
+
44
+ function AttachmentImage({ src }: { src: string }) {
45
+ const [open, setOpen] = useState(false);
46
+ return (
47
+ <>
48
+ <img
49
+ src={src}
50
+ loading="lazy"
51
+ alt="Imagem anexada"
52
+ className="max-w-xs rounded-lg border cursor-pointer object-cover"
53
+ onClick={() => setOpen(true)}
54
+ />
55
+ {open && (
56
+ <div
57
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/80"
58
+ onClick={() => setOpen(false)}
59
+ >
60
+ <img
61
+ src={src}
62
+ alt="Imagem em tamanho real"
63
+ className="max-w-[90vw] max-h-[90vh] rounded-lg"
64
+ onClick={(e) => e.stopPropagation()}
65
+ />
66
+ </div>
67
+ )}
68
+ </>
69
+ );
70
+ }
71
+
72
+ function AudioAttachment({ src }: { src: string }) {
73
+ return (
74
+ <audio controls src={src} className="max-w-xs w-full" />
75
+ );
76
+ }
77
+
78
+ function PdfChip({ src, filename }: { src: string; filename: string }) {
79
+ return (
80
+ <a
81
+ href={src}
82
+ target="_blank"
83
+ rel="noopener noreferrer"
84
+ className="inline-flex items-center gap-2 rounded-lg border bg-muted px-3 py-2 text-sm hover:bg-muted/80 transition-colors"
85
+ >
86
+ <FileText className="h-4 w-4 shrink-0 text-muted-foreground" />
87
+ <span className="max-w-[200px] truncate">{filename}</span>
88
+ </a>
89
+ );
90
+ }
91
+
92
+ function AttachmentTextBlock({ filename, content }: { filename: string; content: string }) {
93
+ const [open, setOpen] = useState(false);
94
+ const lines = content.split("\n");
95
+ const preview = lines.slice(0, 3).join("\n") + (lines.length > 3 && !open ? "\n…" : "");
96
+
97
+ return (
98
+ <Collapsible open={open} onOpenChange={setOpen} className="rounded-lg border text-xs overflow-hidden">
99
+ <div className="flex items-center gap-2 px-3 py-2 bg-muted/50 border-b">
100
+ <Paperclip className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
101
+ <span className="flex-1 truncate font-medium">{filename}</span>
102
+ <CollapsibleTrigger asChild>
103
+ <button type="button" className="text-muted-foreground hover:text-foreground transition-colors" aria-label={open ? "Recolher" : "Expandir"}>
104
+ <ChevronDown className={cn("h-3.5 w-3.5 transition-transform", open && "rotate-180")} />
105
+ </button>
106
+ </CollapsibleTrigger>
107
+ </div>
108
+ <pre className="px-3 py-2 text-muted-foreground whitespace-pre-wrap break-words">{preview}</pre>
109
+ <CollapsibleContent>
110
+ <pre className="px-3 py-2 max-h-40 overflow-auto whitespace-pre-wrap break-words border-t">{content}</pre>
111
+ </CollapsibleContent>
112
+ </Collapsible>
113
+ );
114
+ }
115
+
116
+ // ─── Main renderer ────────────────────────────────────────────────────────────
117
+
118
+ export const PartRenderer = memo(function PartRenderer({ part, isStreaming, displayRenderers, attachmentUrl }: PartRendererProps) {
119
+ switch (part.type) {
120
+ case "text": {
121
+ const p = part as TextPart;
122
+ // Pre-processed attachment text: "[📎 filename]\ncontent"
123
+ if (p.text.startsWith("[📎")) {
124
+ const firstNewline = p.text.indexOf("\n");
125
+ const header = firstNewline >= 0 ? p.text.slice(0, firstNewline) : p.text;
126
+ const body = firstNewline >= 0 ? p.text.slice(firstNewline + 1) : "";
127
+ const match = header.match(/^\[📎\s+(.+?)\]?$/);
128
+ const filename = match?.[1] ?? header;
129
+ return <AttachmentTextBlock filename={filename} content={body} />;
130
+ }
131
+ return <Markdown>{p.text}</Markdown>;
132
+ }
133
+
134
+ case "reasoning": {
135
+ const p = part as ReasoningPart;
136
+ return <ReasoningBlock content={p.reasoning} isStreaming={isStreaming} />;
137
+ }
138
+
139
+ case "tool-invocation": {
140
+ const { toolInvocation } = part as ToolInvocationPart;
141
+ const isDisplay = toolInvocation.toolName.startsWith("display_");
142
+
143
+ if (isDisplay && toolInvocation.state === "result") {
144
+ const result = toolInvocation.result as Record<string, unknown>;
145
+ const action = toolInvocation.toolName.replace("display_", "");
146
+ const Renderer = resolveDisplayRenderer(action, displayRenderers);
147
+ if (Renderer) {
148
+ const rendered = <Renderer {...result} />;
149
+ if (!isStreaming && action && HEAVY_RENDERERS.has(action)) {
150
+ return <LazyRender>{rendered}</LazyRender>;
151
+ }
152
+ return rendered;
153
+ }
154
+ }
155
+
156
+ if (toolInvocation.state === "result") {
157
+ return (
158
+ <ToolResult
159
+ toolName={toolInvocation.toolName}
160
+ result={toolInvocation.result}
161
+ />
162
+ );
163
+ }
164
+
165
+ return (
166
+ <ToolActivity
167
+ toolName={toolInvocation.toolName}
168
+ state={toolInvocation.state}
169
+ args={toolInvocation.args}
170
+ />
171
+ );
172
+ }
173
+
174
+ case "image": {
175
+ const p = part as ImageAttachmentPart;
176
+ if (!p._ref || !attachmentUrl) return null;
177
+ return <AttachmentImage src={attachmentUrl(p._ref)} />;
178
+ }
179
+
180
+ case "file": {
181
+ const p = part as FileAttachmentPart;
182
+ if (!p._ref || !attachmentUrl) return null;
183
+ const src = attachmentUrl(p._ref);
184
+ if (p.mimeType?.startsWith("audio/")) {
185
+ return <AudioAttachment src={src} />;
186
+ }
187
+ if (p.mimeType === "application/pdf") {
188
+ // Extract a human-readable filename from the att_ts_hex.ext pattern
189
+ const filename = p._ref.replace(/^att_\d+_[0-9a-f]+\./, "") || p._ref;
190
+ return <PdfChip src={src} filename={filename} />;
191
+ }
192
+ return null;
193
+ }
194
+
195
+ default:
196
+ return null;
197
+ }
198
+ });
@@ -0,0 +1,41 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Brain, ChevronDown, ChevronRight } from "lucide-react";
3
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible.js";
4
+
5
+ export interface ReasoningBlockProps {
6
+ content: string;
7
+ isStreaming?: boolean;
8
+ className?: string;
9
+ }
10
+
11
+ export function ReasoningBlock({ content, isStreaming = false, className }: ReasoningBlockProps) {
12
+ const [expanded, setExpanded] = useState(isStreaming);
13
+
14
+ useEffect(() => {
15
+ if (isStreaming) {
16
+ setExpanded(true);
17
+ } else {
18
+ setExpanded(false);
19
+ }
20
+ }, [isStreaming]);
21
+
22
+ return (
23
+ <Collapsible open={expanded} onOpenChange={setExpanded} className={className}>
24
+ <div className="border border-purple-500/30 bg-purple-500/5 rounded-md text-sm overflow-hidden">
25
+ <CollapsibleTrigger className="flex items-center gap-2 px-3 py-2 w-full text-left font-medium text-purple-400 hover:bg-purple-500/10 cursor-pointer">
26
+ <Brain className="h-3.5 w-3.5" aria-hidden="true" />
27
+ <span className="font-mono text-xs">reasoning:</span>
28
+ {expanded
29
+ ? <ChevronDown className="h-3.5 w-3.5 ml-auto" aria-hidden="true" />
30
+ : <ChevronRight className="h-3.5 w-3.5 ml-auto" aria-hidden="true" />
31
+ }
32
+ </CollapsibleTrigger>
33
+ <CollapsibleContent>
34
+ <div className="max-h-96 overflow-y-auto px-3 py-3 text-purple-300/80 whitespace-pre-wrap break-words border-t border-purple-500/20 text-xs">
35
+ {content}
36
+ </div>
37
+ </CollapsibleContent>
38
+ </div>
39
+ </Collapsible>
40
+ );
41
+ }
@@ -0,0 +1,79 @@
1
+ import { Check, Download, FileText, FilePlus, FolderSearch, Globe, Loader2, Pencil, Search, Terminal, Wrench, type LucideIcon } from "lucide-react";
2
+ import { cn } from "../lib/utils.js";
3
+
4
+ export type ToolActivityState = "call" | "partial-call" | "result";
5
+
6
+ export interface ToolActivityProps {
7
+ toolName: string;
8
+ state: ToolActivityState;
9
+ args?: Record<string, unknown>;
10
+ className?: string;
11
+ iconMap?: Partial<Record<string, LucideIcon>>;
12
+ }
13
+
14
+ export const defaultToolIconMap: Record<string, LucideIcon> = {
15
+ WebSearch: Globe,
16
+ Bash: Terminal,
17
+ Read: FileText,
18
+ Edit: Pencil,
19
+ Write: FilePlus,
20
+ Grep: Search,
21
+ Glob: FolderSearch,
22
+ WebFetch: Download,
23
+ ListDir: FolderSearch,
24
+ };
25
+
26
+ function formatToolName(name: string): string {
27
+ return name
28
+ .replace(/_/g, " ")
29
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
30
+ .replace(/\b\w/g, (c) => c.toUpperCase());
31
+ }
32
+
33
+ function resolveIcon(toolName: string, iconMap: Record<string, LucideIcon | undefined>): LucideIcon {
34
+ return iconMap[toolName] ?? Wrench;
35
+ }
36
+
37
+ function formatArgs(toolName: string, args?: Record<string, unknown>): string | null {
38
+ if (!args) return null;
39
+ if (toolName === "Bash" && args.command) return String(args.command);
40
+ if (toolName === "Read" && args.path) return String(args.path);
41
+ if (toolName === "Edit" && args.file_path) return String(args.file_path);
42
+ if (toolName === "Write" && args.file_path) return String(args.file_path);
43
+ if (toolName === "Grep" && args.pattern) return String(args.pattern);
44
+ if (toolName === "Glob" && args.pattern) return String(args.pattern);
45
+ if (toolName === "WebSearch" && args.query) return String(args.query);
46
+ if (toolName === "ListDir" && args.path) return String(args.path);
47
+ return null;
48
+ }
49
+
50
+ export function ToolActivity({ toolName, state, args, className, iconMap }: ToolActivityProps) {
51
+ const mergedIconMap = iconMap ? { ...defaultToolIconMap, ...iconMap } : defaultToolIconMap;
52
+ const Icon = resolveIcon(toolName, mergedIconMap);
53
+ const isActive = state === "call" || state === "partial-call";
54
+ const displayName = formatToolName(toolName);
55
+ const argsPreview = formatArgs(toolName, args);
56
+
57
+ return (
58
+ <div className={cn(
59
+ "flex items-center gap-2 rounded-md border px-3 py-2 text-sm",
60
+ "border-amber-500/30 bg-amber-500/5",
61
+ className
62
+ )}>
63
+ <span className="text-amber-500 shrink-0" aria-hidden="true">
64
+ <Icon size={14} />
65
+ </span>
66
+ <span className="font-medium font-mono text-amber-500">{displayName}</span>
67
+ {argsPreview && (
68
+ <span className="truncate text-xs text-muted-foreground font-mono max-w-[300px]">{argsPreview}</span>
69
+ )}
70
+ <span className="ml-auto text-muted-foreground">
71
+ {isActive ? (
72
+ <Loader2 size={14} className="animate-spin text-amber-500" aria-label="Executando..." />
73
+ ) : (
74
+ <Check size={14} className="text-green-500" aria-label="Concluido" />
75
+ )}
76
+ </span>
77
+ </div>
78
+ );
79
+ }
@@ -0,0 +1,79 @@
1
+ import { AlertCircle, CheckCircle, ChevronDown, ChevronRight } from "lucide-react";
2
+ import { useState } from "react";
3
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible.js";
4
+ import { cn } from "../lib/utils.js";
5
+
6
+ export interface ToolResultProps {
7
+ toolName: string;
8
+ result: unknown;
9
+ isError?: boolean;
10
+ className?: string;
11
+ }
12
+
13
+ function serializeResult(result: unknown): string {
14
+ try {
15
+ if (typeof result === "object" && result !== null && "value" in (result as Record<string, unknown>)) {
16
+ return String((result as Record<string, unknown>).value);
17
+ }
18
+ return JSON.stringify(result, null, 2);
19
+ } catch {
20
+ return String(result);
21
+ }
22
+ }
23
+
24
+ function extractPreview(result: unknown): string | null {
25
+ if (typeof result === "string") return result.length > 120 ? result.slice(0, 120) + "..." : result;
26
+ if (typeof result === "object" && result !== null) {
27
+ const r = result as Record<string, unknown>;
28
+ if (typeof r.value === "string") {
29
+ const v = r.value as string;
30
+ return v.length > 120 ? v.slice(0, 120) + "..." : v;
31
+ }
32
+ }
33
+ return null;
34
+ }
35
+
36
+ export function ToolResult({ toolName, result, isError = false, className }: ToolResultProps) {
37
+ const [expanded, setExpanded] = useState(false);
38
+ const serialized = serializeResult(result);
39
+ const preview = extractPreview(result);
40
+
41
+ return (
42
+ <Collapsible open={expanded} onOpenChange={setExpanded} className={className}>
43
+ <div className={cn(
44
+ "rounded-md border text-sm overflow-hidden",
45
+ isError
46
+ ? "border-red-500/30 bg-red-500/5"
47
+ : "border-green-500/30 bg-green-500/5"
48
+ )}>
49
+ <CollapsibleTrigger
50
+ className={cn(
51
+ "flex items-center gap-2 w-full px-3 py-2 text-left font-medium hover:bg-muted/50 cursor-pointer",
52
+ )}
53
+ aria-expanded={expanded}
54
+ >
55
+ {isError ? (
56
+ <AlertCircle className="h-3.5 w-3.5 shrink-0 text-red-500" aria-hidden="true" />
57
+ ) : (
58
+ <CheckCircle className="h-3.5 w-3.5 shrink-0 text-green-500" aria-hidden="true" />
59
+ )}
60
+ <span className={cn("font-mono text-xs", isError ? "text-red-500" : "text-green-500")}>
61
+ {isError ? `${toolName} erro` : `${toolName} ok`}
62
+ </span>
63
+ {!expanded && preview && (
64
+ <span className="truncate text-xs text-muted-foreground font-mono max-w-[400px] ml-1">{preview}</span>
65
+ )}
66
+ {expanded
67
+ ? <ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground ml-auto" aria-hidden="true" />
68
+ : <ChevronRight className="h-3.5 w-3.5 shrink-0 text-muted-foreground ml-auto" aria-hidden="true" />
69
+ }
70
+ </CollapsibleTrigger>
71
+ <CollapsibleContent>
72
+ <pre className="max-h-80 overflow-y-auto p-3 font-mono text-xs whitespace-pre-wrap break-all bg-muted/30 border-t border-border">
73
+ {serialized}
74
+ </pre>
75
+ </CollapsibleContent>
76
+ </div>
77
+ </Collapsible>
78
+ );
79
+ }
package/src/styles.css ADDED
@@ -0,0 +1,2 @@
1
+ @import "tailwindcss/utilities" layer(utilities);
2
+ @source "./**/*.tsx";
@@ -0,0 +1,77 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "../lib/utils.js";
4
+
5
+ const alertVariants = cva(
6
+ "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-card text-card-foreground",
11
+ destructive:
12
+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ variant: "default",
17
+ },
18
+ }
19
+ );
20
+
21
+ function Alert({
22
+ className,
23
+ variant,
24
+ ...props
25
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
26
+ return (
27
+ <div
28
+ data-slot="alert"
29
+ role="alert"
30
+ className={cn(alertVariants({ variant }), className)}
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
37
+ return (
38
+ <div
39
+ data-slot="alert-title"
40
+ className={cn(
41
+ "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
42
+ className
43
+ )}
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function AlertDescription({
50
+ className,
51
+ ...props
52
+ }: React.ComponentProps<"div">) {
53
+ return (
54
+ <div
55
+ data-slot="alert-description"
56
+ className={cn(
57
+ "text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
58
+ className
59
+ )}
60
+ {...props}
61
+ />
62
+ );
63
+ }
64
+
65
+ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
66
+ return (
67
+ <div
68
+ data-slot="alert-action"
69
+ className={cn("absolute top-2 right-2", className)}
70
+ {...props}
71
+ />
72
+ );
73
+ }
74
+
75
+ export { Alert, AlertTitle, AlertDescription, AlertAction };
76
+ export type { VariantProps };
77
+ export { alertVariants };
@@ -0,0 +1,36 @@
1
+ import { cva, type VariantProps } from "class-variance-authority"
2
+ import * as React from "react"
3
+
4
+ import { cn } from "../lib/utils"
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13
+ secondary:
14
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
+ destructive:
16
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17
+ outline: "text-foreground",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ variant: "default",
22
+ },
23
+ }
24
+ )
25
+
26
+ export interface BadgeProps
27
+ extends React.HTMLAttributes<HTMLDivElement>,
28
+ VariantProps<typeof badgeVariants> {}
29
+
30
+ function Badge({ className, variant, ...props }: BadgeProps) {
31
+ return (
32
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
33
+ )
34
+ }
35
+
36
+ export { Badge, badgeVariants }
@@ -0,0 +1,54 @@
1
+ import { cva, type VariantProps } from "class-variance-authority"
2
+ import * as React from "react"
3
+
4
+ import { cn } from "../lib/utils"
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ destructive:
13
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14
+ outline:
15
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
16
+ secondary:
17
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
18
+ ghost: "hover:bg-accent hover:text-accent-foreground",
19
+ link: "text-primary underline-offset-4 hover:underline",
20
+ },
21
+ size: {
22
+ default: "h-10 px-4 py-2",
23
+ sm: "h-9 rounded-md px-3",
24
+ lg: "h-11 rounded-md px-8",
25
+ icon: "h-10 w-10",
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: "default",
30
+ size: "default",
31
+ },
32
+ }
33
+ )
34
+
35
+ export interface ButtonProps
36
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
37
+ VariantProps<typeof buttonVariants> {
38
+ asChild?: boolean
39
+ }
40
+
41
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
42
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
43
+ return (
44
+ <button
45
+ className={cn(buttonVariants({ variant, size, className }))}
46
+ ref={ref}
47
+ {...props}
48
+ />
49
+ )
50
+ }
51
+ )
52
+ Button.displayName = "Button"
53
+
54
+ export { Button, buttonVariants }