@iloveagents/foundry-web-ui 0.3.0 → 0.4.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 (223) hide show
  1. package/README.md +14 -5
  2. package/dist/components/ag-ui-runtime-provider.d.ts +64 -0
  3. package/dist/components/ag-ui-runtime-provider.js +71 -0
  4. package/dist/components/app-brand.d.ts +6 -0
  5. package/dist/components/app-brand.js +13 -0
  6. package/dist/components/assistant-chat.d.ts +26 -0
  7. package/dist/components/assistant-chat.js +93 -0
  8. package/dist/components/chat-attachments.d.ts +25 -0
  9. package/dist/components/chat-attachments.js +20 -0
  10. package/dist/components/chat-bubble.d.ts +1 -0
  11. package/dist/components/chat-bubble.js +103 -0
  12. package/dist/components/chat-header.d.ts +2 -0
  13. package/dist/components/chat-header.js +16 -0
  14. package/dist/components/client-tool-executor.d.ts +12 -0
  15. package/dist/components/client-tool-executor.js +108 -0
  16. package/dist/components/collection-empty-state.d.ts +9 -0
  17. package/dist/components/collection-empty-state.js +6 -0
  18. package/dist/components/collection-filter-bar.d.ts +19 -0
  19. package/dist/components/collection-filter-bar.js +28 -0
  20. package/dist/components/collection-search-input.d.ts +6 -0
  21. package/dist/components/collection-search-input.js +8 -0
  22. package/dist/components/collection-skeleton.d.ts +6 -0
  23. package/dist/components/collection-skeleton.js +10 -0
  24. package/dist/components/collection-sort-menu.d.ts +10 -0
  25. package/dist/components/collection-sort-menu.js +9 -0
  26. package/dist/components/collection-surface.d.ts +8 -0
  27. package/dist/components/collection-surface.js +5 -0
  28. package/dist/components/collection-toolbar.d.ts +9 -0
  29. package/dist/components/collection-toolbar.js +5 -0
  30. package/dist/components/collection-view-toggle.d.ts +7 -0
  31. package/dist/components/collection-view-toggle.js +17 -0
  32. package/dist/components/composer-submit-bridge.d.ts +1 -0
  33. package/dist/components/composer-submit-bridge.js +15 -0
  34. package/dist/components/confirm-dialog.d.ts +11 -0
  35. package/dist/components/confirm-dialog.js +6 -0
  36. package/dist/components/confirmation-card.d.ts +14 -0
  37. package/dist/components/confirmation-card.js +12 -0
  38. package/dist/components/context-badges.d.ts +16 -0
  39. package/dist/components/context-badges.js +56 -0
  40. package/dist/components/context-bar.d.ts +41 -0
  41. package/dist/components/context-bar.js +123 -0
  42. package/dist/components/form-dialog.d.ts +12 -0
  43. package/dist/components/form-dialog.js +13 -0
  44. package/dist/components/global-selection-popover.d.ts +7 -0
  45. package/dist/components/global-selection-popover.js +167 -0
  46. package/dist/components/infinite-scroll-sentinel.d.ts +6 -0
  47. package/dist/components/infinite-scroll-sentinel.js +25 -0
  48. package/dist/components/json-viewer.d.ts +10 -0
  49. package/dist/components/json-viewer.js +80 -0
  50. package/dist/components/loading-indicator.d.ts +4 -0
  51. package/dist/components/loading-indicator.js +22 -0
  52. package/dist/components/markdown-text.d.ts +13 -0
  53. package/dist/components/markdown-text.js +221 -0
  54. package/dist/components/name-dialog.d.ts +16 -0
  55. package/dist/components/name-dialog.js +23 -0
  56. package/dist/components/selection-popover.d.ts +12 -0
  57. package/dist/components/selection-popover.js +111 -0
  58. package/dist/components/show-document-tool-ui.d.ts +1 -0
  59. package/dist/components/show-document-tool-ui.js +53 -0
  60. package/dist/components/sidebar.d.ts +4 -0
  61. package/dist/components/sidebar.js +904 -0
  62. package/dist/components/surface-card.d.ts +9 -0
  63. package/dist/components/surface-card.js +33 -0
  64. package/dist/components/theme-runtime-provider.d.ts +15 -0
  65. package/dist/components/theme-runtime-provider.js +48 -0
  66. package/dist/components/theme-toggle.d.ts +2 -0
  67. package/dist/components/theme-toggle.js +19 -0
  68. package/dist/components/tool-call-card.d.ts +27 -0
  69. package/dist/components/tool-call-card.js +13 -0
  70. package/dist/components/tool-fallback.d.ts +26 -0
  71. package/dist/components/tool-fallback.js +129 -0
  72. package/dist/components/tool-panel-layout.d.ts +10 -0
  73. package/dist/components/tool-panel-layout.js +12 -0
  74. package/dist/components/tool-panel.d.ts +1 -0
  75. package/dist/components/tool-panel.js +89 -0
  76. package/dist/components/tooltip-icon-button.d.ts +7 -0
  77. package/dist/components/tooltip-icon-button.js +9 -0
  78. package/dist/components/user-menu.d.ts +2 -0
  79. package/dist/components/user-menu.js +14 -0
  80. package/dist/index.d.ts +68 -0
  81. package/dist/index.js +83 -0
  82. package/dist/lib/ag-ui-adapter.d.ts +29 -0
  83. package/dist/lib/ag-ui-adapter.js +373 -0
  84. package/dist/lib/app-store.d.ts +115 -0
  85. package/dist/lib/app-store.js +199 -0
  86. package/dist/lib/attachment-adapter.d.ts +20 -0
  87. package/dist/lib/attachment-adapter.js +99 -0
  88. package/dist/lib/auth-provider.d.ts +16 -0
  89. package/dist/lib/auth-provider.js +88 -0
  90. package/dist/lib/chat-bubble-store.d.ts +30 -0
  91. package/dist/lib/chat-bubble-store.js +23 -0
  92. package/dist/lib/chat-lifecycle-store.d.ts +10 -0
  93. package/{src/lib/chat-lifecycle-store.ts → dist/lib/chat-lifecycle-store.js} +3 -13
  94. package/dist/lib/client-tools.d.ts +11 -0
  95. package/dist/lib/client-tools.js +274 -0
  96. package/dist/lib/composer-submit-store.d.ts +12 -0
  97. package/dist/lib/composer-submit-store.js +17 -0
  98. package/dist/lib/dev-store.d.ts +38 -0
  99. package/dist/lib/dev-store.js +32 -0
  100. package/dist/lib/nav-config.d.ts +159 -0
  101. package/dist/lib/nav-config.js +162 -0
  102. package/dist/lib/nav-dnd.d.ts +8 -0
  103. package/dist/lib/nav-dnd.js +9 -0
  104. package/dist/lib/nav-store.d.ts +45 -0
  105. package/dist/lib/nav-store.js +49 -0
  106. package/dist/lib/selection-context.d.ts +21 -0
  107. package/dist/lib/selection-context.js +34 -0
  108. package/dist/lib/sidebar-store.d.ts +23 -0
  109. package/dist/lib/sidebar-store.js +98 -0
  110. package/dist/lib/theme-runtime.d.ts +130 -0
  111. package/dist/lib/theme-runtime.js +479 -0
  112. package/dist/lib/theme-store.d.ts +15 -0
  113. package/dist/lib/theme-store.js +53 -0
  114. package/dist/lib/tool-panel-store.d.ts +48 -0
  115. package/dist/lib/tool-panel-store.js +69 -0
  116. package/dist/lib/use-new-conversation.d.ts +8 -0
  117. package/dist/lib/use-new-conversation.js +62 -0
  118. package/dist/lib/use-page-tools.d.ts +27 -0
  119. package/{src/lib/use-page-tools.ts → dist/lib/use-page-tools.js} +11 -13
  120. package/dist/styles.css +142 -0
  121. package/dist/ui/collapsible.d.ts +5 -0
  122. package/{src/ui/collapsible.tsx → dist/ui/collapsible.js} +0 -2
  123. package/dist/ui/dialog.d.ts +15 -0
  124. package/dist/ui/dialog.js +25 -0
  125. package/dist/ui/dropdown-menu.d.ts +7 -0
  126. package/dist/ui/dropdown-menu.js +19 -0
  127. package/dist/ui/popover.d.ts +7 -0
  128. package/dist/ui/popover.js +10 -0
  129. package/dist/ui/select.d.ts +4 -0
  130. package/dist/ui/select.js +9 -0
  131. package/dist/ui/tooltip.d.ts +7 -0
  132. package/dist/ui/tooltip.js +10 -0
  133. package/package.json +31 -12
  134. package/AGENTS.md +0 -59
  135. package/CHANGELOG.md +0 -203
  136. package/CLAUDE.md +0 -1
  137. package/src/__tests__/no-spaces-imports.test.ts +0 -59
  138. package/src/__tests__/open-core-guards.test.ts +0 -307
  139. package/src/__tests__/theme-runtime.test.ts +0 -81
  140. package/src/__tests__/tool-fallback.test.tsx +0 -109
  141. package/src/components/__tests__/context-bar.test.ts +0 -112
  142. package/src/components/ag-ui-runtime-provider.tsx +0 -170
  143. package/src/components/app-brand.tsx +0 -38
  144. package/src/components/assistant-chat.tsx +0 -461
  145. package/src/components/chat-attachments.tsx +0 -68
  146. package/src/components/chat-bubble.tsx +0 -373
  147. package/src/components/chat-header.tsx +0 -44
  148. package/src/components/client-tool-executor.tsx +0 -230
  149. package/src/components/collection-empty-state.tsx +0 -37
  150. package/src/components/collection-filter-bar.tsx +0 -168
  151. package/src/components/collection-search-input.tsx +0 -41
  152. package/src/components/collection-skeleton.tsx +0 -55
  153. package/src/components/collection-sort-menu.tsx +0 -62
  154. package/src/components/collection-surface.tsx +0 -46
  155. package/src/components/collection-toolbar.tsx +0 -33
  156. package/src/components/collection-view-toggle.tsx +0 -61
  157. package/src/components/composer-submit-bridge.tsx +0 -19
  158. package/src/components/confirm-dialog.tsx +0 -49
  159. package/src/components/confirmation-card.tsx +0 -32
  160. package/src/components/context-badges.tsx +0 -136
  161. package/src/components/context-bar.tsx +0 -238
  162. package/src/components/form-dialog.tsx +0 -56
  163. package/src/components/global-selection-popover.tsx +0 -266
  164. package/src/components/infinite-scroll-sentinel.tsx +0 -48
  165. package/src/components/json-viewer.tsx +0 -232
  166. package/src/components/loading-indicator.tsx +0 -40
  167. package/src/components/markdown-text-citations.test.tsx +0 -108
  168. package/src/components/markdown-text.tsx +0 -508
  169. package/src/components/name-dialog.tsx +0 -87
  170. package/src/components/selection-popover.tsx +0 -156
  171. package/src/components/show-document-tool-ui.tsx +0 -90
  172. package/src/components/sidebar.test.tsx +0 -611
  173. package/src/components/sidebar.tsx +0 -1689
  174. package/src/components/surface-card.tsx +0 -45
  175. package/src/components/theme-runtime-provider.tsx +0 -93
  176. package/src/components/theme-toggle.tsx +0 -27
  177. package/src/components/tool-call-card.tsx +0 -126
  178. package/src/components/tool-fallback.tsx +0 -169
  179. package/src/components/tool-panel-layout.tsx +0 -36
  180. package/src/components/tool-panel.tsx +0 -233
  181. package/src/components/tooltip-icon-button.tsx +0 -28
  182. package/src/components/user-menu.tsx +0 -60
  183. package/src/index.ts +0 -185
  184. package/src/lib/__tests__/ag-ui-adapter.test.ts +0 -273
  185. package/src/lib/__tests__/app-store.test.ts +0 -405
  186. package/src/lib/__tests__/attachment-adapter.test.ts +0 -48
  187. package/src/lib/__tests__/chat-bubble-store.test.ts +0 -67
  188. package/src/lib/__tests__/client-tools.test.ts +0 -124
  189. package/src/lib/__tests__/dev-store.test.ts +0 -78
  190. package/src/lib/__tests__/nav-config.test.ts +0 -135
  191. package/src/lib/__tests__/nav-dnd.test.ts +0 -24
  192. package/src/lib/__tests__/nav-store.test.ts +0 -59
  193. package/src/lib/__tests__/selection-context.test.ts +0 -114
  194. package/src/lib/__tests__/sidebar-store.test.ts +0 -144
  195. package/src/lib/__tests__/theme-store.test.ts +0 -83
  196. package/src/lib/__tests__/tool-panel-store.test.ts +0 -61
  197. package/src/lib/__tests__/use-page-context.test.ts +0 -58
  198. package/src/lib/ag-ui-adapter.ts +0 -412
  199. package/src/lib/app-store.ts +0 -361
  200. package/src/lib/attachment-adapter.ts +0 -92
  201. package/src/lib/auth-provider.tsx +0 -144
  202. package/src/lib/chat-bubble-store.ts +0 -46
  203. package/src/lib/client-tools.ts +0 -293
  204. package/src/lib/composer-submit-store.ts +0 -32
  205. package/src/lib/dev-store.ts +0 -69
  206. package/src/lib/nav-config.ts +0 -329
  207. package/src/lib/nav-dnd.ts +0 -14
  208. package/src/lib/nav-store.ts +0 -89
  209. package/src/lib/selection-context.ts +0 -65
  210. package/src/lib/sidebar-store.ts +0 -113
  211. package/src/lib/theme-runtime.ts +0 -660
  212. package/src/lib/theme-store.ts +0 -67
  213. package/src/lib/tool-panel-store.ts +0 -121
  214. package/src/lib/use-new-conversation.test.tsx +0 -168
  215. package/src/lib/use-new-conversation.ts +0 -64
  216. package/src/ui/dialog.tsx +0 -109
  217. package/src/ui/dropdown-menu.tsx +0 -75
  218. package/src/ui/popover.tsx +0 -34
  219. package/src/ui/select.tsx +0 -15
  220. package/src/ui/tooltip.tsx +0 -30
  221. package/src/vite-env.d.ts +0 -1
  222. package/tsconfig.json +0 -16
  223. package/vitest.config.ts +0 -8
@@ -1,461 +0,0 @@
1
- import { type FC, useState, useRef, useCallback } from "react";
2
- import {
3
- ThreadPrimitive,
4
- ComposerPrimitive,
5
- MessagePrimitive,
6
- ActionBarPrimitive,
7
- BranchPickerPrimitive,
8
- ErrorPrimitive,
9
- useAuiState,
10
- useComposerRuntime,
11
- useThreadViewport,
12
- } from "@assistant-ui/react";
13
- import {
14
- ArrowUp,
15
- Copy,
16
- Check,
17
- RefreshCw,
18
- PencilIcon,
19
- ChevronLeft,
20
- ChevronRight,
21
- ChevronDown,
22
- Square,
23
- Bot,
24
- Paperclip,
25
- Minimize2,
26
- PanelRightClose,
27
- PanelRightOpen,
28
- } from "lucide-react";
29
- import { cn } from "@iloveagents/foundry-web-primitives";
30
- import { Button } from "@iloveagents/foundry-web-primitives";
31
- import { TooltipIconButton } from "./tooltip-icon-button.tsx";
32
- import { userAttachmentComponents, composerAttachmentComponents } from "./chat-attachments.tsx";
33
- import { SentContextBadges, ComposerContextBadges } from "./context-badges.tsx";
34
- import { ContextPins } from "./context-bar.tsx";
35
- import { ComposerSubmitBridge } from "./composer-submit-bridge.tsx";
36
- import { useChatBubbleStore } from "../lib/chat-bubble-store.ts";
37
- import { MarkdownText } from "./markdown-text.tsx";
38
- import { ToolFallback } from "./tool-fallback.tsx";
39
- import { LoadingIndicator, LoadingBar } from "./loading-indicator.tsx";
40
-
41
- const BranchPicker: FC = () => (
42
- <BranchPickerPrimitive.Root
43
- hideWhenSingleBranch
44
- className="inline-flex items-center gap-1 text-xs"
45
- >
46
- <BranchPickerPrimitive.Previous asChild>
47
- <TooltipIconButton tooltip="Previous" size="icon">
48
- <ChevronLeft className="size-4" />
49
- </TooltipIconButton>
50
- </BranchPickerPrimitive.Previous>
51
- <span className="text-muted-foreground">
52
- <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
53
- </span>
54
- <BranchPickerPrimitive.Next asChild>
55
- <TooltipIconButton tooltip="Next" size="icon">
56
- <ChevronRight className="size-4" />
57
- </TooltipIconButton>
58
- </BranchPickerPrimitive.Next>
59
- </BranchPickerPrimitive.Root>
60
- );
61
-
62
- const UserMessage: FC = () => {
63
- const messageId = useAuiState((s) => s.message.id);
64
-
65
- return (
66
- <MessagePrimitive.Root className="w-full mb-2 group">
67
- <div className="flex flex-col items-end gap-1">
68
- <MessagePrimitive.Attachments components={userAttachmentComponents} />
69
- <SentContextBadges messageId={messageId} />
70
- <div className={cn("max-w-[80%]", "bg-muted rounded-2xl", "px-4 py-3", "text-foreground")}>
71
- <MessagePrimitive.Content />
72
- </div>
73
- <div className="flex items-center justify-end gap-2 pr-2">
74
- <BranchPicker />
75
- <div className="w-10 flex items-center justify-end">
76
- <UserActionBar />
77
- </div>
78
- </div>
79
- </div>
80
- </MessagePrimitive.Root>
81
- );
82
- };
83
-
84
- const UserActionBar: FC = () => (
85
- <ActionBarPrimitive.Root
86
- hideWhenRunning
87
- className="flex items-center opacity-0 group-hover:opacity-100 transition-opacity"
88
- >
89
- <ActionBarPrimitive.Edit asChild>
90
- <TooltipIconButton tooltip="Edit" size="icon">
91
- <PencilIcon className="size-4" />
92
- </TooltipIconButton>
93
- </ActionBarPrimitive.Edit>
94
- </ActionBarPrimitive.Root>
95
- );
96
-
97
- const AssistantMessage: FC = () => (
98
- <MessagePrimitive.Root className="w-full mb-2 group">
99
- <div className="mx-auto max-w-3xl px-4">
100
- <div className="text-foreground leading-relaxed flex flex-col gap-4">
101
- <MessagePrimitive.Parts
102
- components={{
103
- Text: MarkdownText,
104
- tools: { Fallback: ToolFallback },
105
- }}
106
- />
107
- </div>
108
- <MessageError />
109
-
110
- <div className="flex items-center gap-2 mt-1">
111
- <div className="flex h-8 w-[4.25rem] items-center">
112
- <AssistantActionBar />
113
- </div>
114
- <BranchPicker />
115
- </div>
116
- </div>
117
- </MessagePrimitive.Root>
118
- );
119
-
120
- const AssistantActionBar: FC = () => (
121
- <ActionBarPrimitive.Root
122
- hideWhenRunning
123
- className={cn(
124
- "pointer-events-none flex items-center gap-1 -ml-2 opacity-0 transition-opacity",
125
- "group-hover:pointer-events-auto group-hover:opacity-100",
126
- "group-focus-within:pointer-events-auto group-focus-within:opacity-100",
127
- )}
128
- >
129
- <ActionBarPrimitive.Copy asChild>
130
- <TooltipIconButton tooltip="Copy" size="icon">
131
- <MessagePrimitive.If copied={false}>
132
- <Copy className="size-4" />
133
- </MessagePrimitive.If>
134
- <MessagePrimitive.If copied>
135
- <Check className="size-4 text-primary" />
136
- </MessagePrimitive.If>
137
- </TooltipIconButton>
138
- </ActionBarPrimitive.Copy>
139
- <ActionBarPrimitive.Reload asChild>
140
- <TooltipIconButton tooltip="Refresh" size="icon">
141
- <RefreshCw className="size-4" />
142
- </TooltipIconButton>
143
- </ActionBarPrimitive.Reload>
144
- </ActionBarPrimitive.Root>
145
- );
146
-
147
- const MessageError: FC = () => (
148
- <MessagePrimitive.Error>
149
- <ErrorPrimitive.Root className="mt-2 rounded-lg border border-destructive bg-destructive/10 p-3">
150
- <div className="flex items-center gap-2">
151
- <span className="text-sm font-medium text-destructive">Error:</span>
152
- <ErrorPrimitive.Message className="text-sm text-destructive" />
153
- </div>
154
- </ErrorPrimitive.Root>
155
- </MessagePrimitive.Error>
156
- );
157
-
158
- const EditComposer: FC = () => (
159
- <MessagePrimitive.Root className="w-full mb-2">
160
- <div className="flex items-start justify-end">
161
- <div className="w-full max-w-[80%]">
162
- <ComposerPrimitive.Root
163
- className={cn(
164
- "flex flex-col gap-2",
165
- "rounded-2xl border border-input",
166
- "bg-background shadow-sm",
167
- "px-4 py-3",
168
- )}
169
- >
170
- <ComposerPrimitive.Input
171
- autoFocus
172
- className={cn(
173
- "w-full bg-transparent",
174
- "text-base text-foreground",
175
- "outline-none resize-none",
176
- "placeholder:text-muted-foreground",
177
- )}
178
- />
179
- <div className="flex items-center justify-end gap-2">
180
- <ComposerPrimitive.Cancel asChild>
181
- <Button variant="ghost" size="sm">
182
- Cancel
183
- </Button>
184
- </ComposerPrimitive.Cancel>
185
- <ComposerPrimitive.Send asChild>
186
- <Button size="sm">Update</Button>
187
- </ComposerPrimitive.Send>
188
- </div>
189
- </ComposerPrimitive.Root>
190
- </div>
191
- </div>
192
- </MessagePrimitive.Root>
193
- );
194
-
195
- const ComposerActionButton: FC = () => {
196
- return (
197
- <>
198
- <ThreadPrimitive.If running>
199
- <ComposerPrimitive.Cancel asChild>
200
- <Button
201
- variant="secondary"
202
- size="icon"
203
- className="rounded-full size-10 flex-shrink-0"
204
- aria-label="Stop generating"
205
- >
206
- <Square className="size-4" />
207
- </Button>
208
- </ComposerPrimitive.Cancel>
209
- </ThreadPrimitive.If>
210
-
211
- <ThreadPrimitive.If running={false}>
212
- <ComposerPrimitive.Send asChild>
213
- <Button
214
- size="icon"
215
- className="rounded-full size-10 flex-shrink-0"
216
- aria-label="Send message"
217
- >
218
- <ArrowUp className="size-5" />
219
- </Button>
220
- </ComposerPrimitive.Send>
221
- </ThreadPrimitive.If>
222
- </>
223
- );
224
- };
225
-
226
- /**
227
- * Default starter prompts shown on empty state. Override per-app by passing
228
- * a `starterSuggestions` prop to `<ChatContent>`. An empty array (`[]`)
229
- * suppresses the suggestion buttons entirely.
230
- */
231
- export const DEFAULT_STARTER_SUGGESTIONS = [
232
- "Create a new Contract Space",
233
- "List all contracts I have access to",
234
- "Show me what I can do here",
235
- ];
236
-
237
- const ScrollToBottomButton: FC = () => {
238
- const isAtBottom = useThreadViewport((s) => s.isAtBottom);
239
- const scrollToBottom = useThreadViewport((s) => s.scrollToBottom);
240
-
241
- if (isAtBottom) return null;
242
-
243
- return (
244
- <Button
245
- variant="outline"
246
- size="icon"
247
- className="absolute bottom-4 left-1/2 -translate-x-1/2 z-10 rounded-full shadow-md"
248
- onClick={() => scrollToBottom({ behavior: "smooth" })}
249
- >
250
- <ChevronDown className="size-4" />
251
- </Button>
252
- );
253
- };
254
-
255
- const DropZone: FC<{ children: React.ReactNode }> = ({ children }) => {
256
- const [isDragging, setIsDragging] = useState(false);
257
- const dragCounter = useRef(0);
258
- const composerRuntime = useComposerRuntime();
259
-
260
- const handleDragEnter = useCallback((e: React.DragEvent) => {
261
- e.preventDefault();
262
- dragCounter.current++;
263
- if (dragCounter.current === 1) setIsDragging(true);
264
- }, []);
265
-
266
- const handleDragLeave = useCallback((e: React.DragEvent) => {
267
- e.preventDefault();
268
- dragCounter.current--;
269
- if (dragCounter.current === 0) setIsDragging(false);
270
- }, []);
271
-
272
- const handleDragOver = useCallback((e: React.DragEvent) => {
273
- e.preventDefault();
274
- }, []);
275
-
276
- const handleDrop = useCallback(
277
- async (e: React.DragEvent) => {
278
- e.preventDefault();
279
- dragCounter.current = 0;
280
- setIsDragging(false);
281
- const files = Array.from(e.dataTransfer.files);
282
- for (const file of files) {
283
- await composerRuntime.addAttachment(file);
284
- }
285
- },
286
- [composerRuntime],
287
- );
288
-
289
- return (
290
- <div
291
- className="relative flex flex-col flex-1 overflow-hidden"
292
- onDragEnter={handleDragEnter}
293
- onDragLeave={handleDragLeave}
294
- onDragOver={handleDragOver}
295
- onDrop={handleDrop}
296
- >
297
- {children}
298
- {isDragging && (
299
- <div
300
- className={cn(
301
- "absolute inset-0 z-50 flex items-center justify-center pointer-events-none",
302
- "bg-background/80 backdrop-blur-sm",
303
- "border-2 border-dashed border-primary rounded-xl",
304
- )}
305
- >
306
- <p className="text-lg text-primary font-medium">Drop files here</p>
307
- </div>
308
- )}
309
- </div>
310
- );
311
- };
312
-
313
- export interface ChatContentProps {
314
- /**
315
- * Starter prompts shown when the thread is empty. Defaults to
316
- * `DEFAULT_STARTER_SUGGESTIONS`. Pass an empty array to hide the
317
- * suggestion buttons entirely. Customer apps (e.g. ANDRITZ Contracts)
318
- * can pass a workspace-aware list derived from app context.
319
- */
320
- starterSuggestions?: string[];
321
- }
322
-
323
- /**
324
- * Reusable chat content — used by ChatPage and ChatBubble.
325
- * Renders inside a ThreadPrimitive.Root context.
326
- */
327
- export function ChatContent({
328
- starterSuggestions = DEFAULT_STARTER_SUGGESTIONS,
329
- }: ChatContentProps = {}) {
330
- const isExpanded = useChatBubbleStore((s) => s.isExpanded);
331
- const collapseChat = useChatBubbleStore((s) => s.collapse);
332
- const showPagePanel = useChatBubbleStore((s) => s.showPagePanel);
333
- const togglePagePanel = useChatBubbleStore((s) => s.togglePagePanel);
334
-
335
- return (
336
- <ThreadPrimitive.Root
337
- className={cn("flex flex-col flex-1", "bg-background text-foreground", "overflow-hidden")}
338
- >
339
- {/* Collapse header — only in expanded layout mode */}
340
- {isExpanded && (
341
- <div className="shrink-0 flex items-center justify-between px-4 py-2 border-b border-border">
342
- <span className="text-sm font-medium text-foreground flex items-center gap-2">
343
- <Bot className="size-4 text-primary" />
344
- Chat
345
- </span>
346
- <div className="flex items-center gap-1">
347
- <TooltipIconButton
348
- tooltip={showPagePanel ? "Hide page panel" : "Show page panel"}
349
- size="icon"
350
- onClick={togglePagePanel}
351
- className="hidden xl:inline-flex"
352
- >
353
- {showPagePanel ? (
354
- <PanelRightClose className="size-4" />
355
- ) : (
356
- <PanelRightOpen className="size-4" />
357
- )}
358
- </TooltipIconButton>
359
- <TooltipIconButton tooltip="Minimize to bubble" size="icon" onClick={collapseChat}>
360
- <Minimize2 className="size-4" />
361
- </TooltipIconButton>
362
- </div>
363
- </div>
364
- )}
365
- <DropZone>
366
- <div className="relative flex-1 overflow-hidden">
367
- <ThreadPrimitive.Viewport className="absolute inset-0 overflow-y-auto bg-background">
368
- <ThreadPrimitive.Empty>
369
- <div
370
- className={cn("flex min-h-full flex-col", "items-center justify-center", "px-4")}
371
- >
372
- <Bot className="size-16 mb-6 text-primary/70" />
373
- <h1 className="text-[2rem] font-normal text-foreground text-center mb-6">
374
- How can I help you today?
375
- </h1>
376
- {starterSuggestions.length > 0 && (
377
- <div className="flex flex-wrap justify-center gap-2 max-w-lg">
378
- {starterSuggestions.map((prompt) => (
379
- <ThreadPrimitive.Suggestion key={prompt} prompt={prompt} send asChild>
380
- <Button variant="outline" className="text-sm">
381
- {prompt}
382
- </Button>
383
- </ThreadPrimitive.Suggestion>
384
- ))}
385
- </div>
386
- )}
387
- </div>
388
- </ThreadPrimitive.Empty>
389
-
390
- <ThreadPrimitive.If empty={false}>
391
- <div className="mx-auto max-w-3xl px-4 pt-8">
392
- <ThreadPrimitive.Messages
393
- components={{
394
- UserMessage,
395
- EditComposer,
396
- AssistantMessage,
397
- }}
398
- />
399
- <ThreadPrimitive.If running>
400
- <LoadingIndicator />
401
- </ThreadPrimitive.If>
402
- </div>
403
- </ThreadPrimitive.If>
404
- </ThreadPrimitive.Viewport>
405
- <ThreadPrimitive.If empty={false}>
406
- <ScrollToBottomButton />
407
- </ThreadPrimitive.If>
408
- </div>
409
-
410
- <div className="shrink-0 bg-background">
411
- <ThreadPrimitive.If running={false}>
412
- <div className="border-t border-border" />
413
- </ThreadPrimitive.If>
414
- <ThreadPrimitive.If running>
415
- <LoadingBar />
416
- </ThreadPrimitive.If>
417
-
418
- <div className="mx-auto max-w-3xl px-4 py-4">
419
- <ComposerPrimitive.Root
420
- className={cn(
421
- "flex flex-col gap-2",
422
- "rounded-3xl border border-input",
423
- "bg-background shadow-sm",
424
- "px-4 py-2",
425
- "focus-within:border-primary/50 focus-within:shadow-md",
426
- "transition-all duration-300",
427
- )}
428
- >
429
- <ComposerSubmitBridge />
430
- <ComposerContextBadges />
431
- <ComposerPrimitive.Attachments components={composerAttachmentComponents} />
432
- <div className="flex items-center gap-2">
433
- <div className="flex items-center -space-x-1 shrink-0">
434
- <ComposerPrimitive.AddAttachment asChild>
435
- <TooltipIconButton tooltip="Attach file" variant="ghost" className="shrink-0">
436
- <Paperclip className="size-5" />
437
- </TooltipIconButton>
438
- </ComposerPrimitive.AddAttachment>
439
- <ContextPins showPinButton />
440
- </div>
441
- <ComposerPrimitive.Input
442
- autoFocus
443
- placeholder="Message Assistant..."
444
- rows={1}
445
- className={cn(
446
- "flex-1 bg-transparent",
447
- "py-2 text-base",
448
- "text-foreground outline-none",
449
- "placeholder:text-muted-foreground",
450
- "resize-none max-h-32",
451
- )}
452
- />
453
- <ComposerActionButton />
454
- </div>
455
- </ComposerPrimitive.Root>
456
- </div>
457
- </div>
458
- </DropZone>
459
- </ThreadPrimitive.Root>
460
- );
461
- }
@@ -1,68 +0,0 @@
1
- /**
2
- * Attachment rendering components for chat messages and composer.
3
- *
4
- * Following the official assistant-ui pattern, attachment components
5
- * live in a separate file from the main thread components.
6
- *
7
- * @see https://www.assistant-ui.com/ - assistant-ui Documentation
8
- */
9
-
10
- import { type FC } from "react";
11
- import { AttachmentPrimitive } from "@assistant-ui/react";
12
- import { FileText, X } from "lucide-react";
13
- import { cn } from "@iloveagents/foundry-web-primitives";
14
-
15
- /** Renders a pending file attachment chip in the composer with a remove button. */
16
- export const ComposerAttachment: FC = () => (
17
- <AttachmentPrimitive.Root
18
- className={cn(
19
- "inline-flex items-center gap-2 max-w-[250px]",
20
- "rounded-lg border border-border bg-muted/50",
21
- "px-3 py-2",
22
- )}
23
- >
24
- <FileText className="size-4 text-muted-foreground flex-shrink-0" />
25
- <span className="text-sm truncate">
26
- <AttachmentPrimitive.Name />
27
- </span>
28
- <AttachmentPrimitive.Remove asChild>
29
- <button
30
- type="button"
31
- className="p-0.5 rounded hover:bg-muted flex-shrink-0"
32
- aria-label="Remove attachment"
33
- >
34
- <X className="size-3.5" />
35
- </button>
36
- </AttachmentPrimitive.Remove>
37
- </AttachmentPrimitive.Root>
38
- );
39
-
40
- /** Renders a file badge on sent user messages. */
41
- export const UserMessageAttachment: FC = () => (
42
- <AttachmentPrimitive.Root
43
- className={cn(
44
- "inline-flex items-center gap-2 max-w-[250px]",
45
- "rounded-lg border border-border/50 bg-muted/30",
46
- "px-3 py-1.5",
47
- )}
48
- >
49
- <FileText className="size-3.5 text-muted-foreground flex-shrink-0" />
50
- <span className="text-xs truncate">
51
- <AttachmentPrimitive.Name />
52
- </span>
53
- </AttachmentPrimitive.Root>
54
- );
55
-
56
- /** Component map for MessagePrimitive.Attachments on user messages. */
57
- export const userAttachmentComponents = {
58
- Document: UserMessageAttachment,
59
- Image: UserMessageAttachment,
60
- File: UserMessageAttachment,
61
- };
62
-
63
- /** Component map for ComposerPrimitive.Attachments in the composer. */
64
- export const composerAttachmentComponents = {
65
- Document: ComposerAttachment,
66
- Image: ComposerAttachment,
67
- File: ComposerAttachment,
68
- };