@gram-ai/elements 1.33.2 → 1.35.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 (243) hide show
  1. package/dist/compat-shims-CO9JXXV4.cjs.map +1 -1
  2. package/dist/compat-shims-DxtUrORi.js.map +1 -1
  3. package/dist/compat-shims.d.ts +9 -8
  4. package/dist/components/Chat/index.d.ts +2 -1
  5. package/dist/components/ChatHistory.d.ts +1 -1
  6. package/dist/components/FrontendTools/index.d.ts +1 -1
  7. package/dist/components/Replay.d.ts +1 -1
  8. package/dist/components/Replay.stories.d.ts +2 -2
  9. package/dist/components/ShadowRoot.d.ts +1 -1
  10. package/dist/components/ShareButton/index.d.ts +1 -1
  11. package/dist/components/ui/avatar.d.ts +3 -3
  12. package/dist/components/ui/button.d.ts +2 -2
  13. package/dist/components/ui/buttonVariants.d.ts +2 -2
  14. package/dist/components/ui/calendar.d.ts +2 -1
  15. package/dist/components/ui/collapsible.d.ts +3 -3
  16. package/dist/components/ui/dialog.d.ts +10 -10
  17. package/dist/components/ui/popover.d.ts +4 -4
  18. package/dist/components/ui/skeleton.d.ts +1 -1
  19. package/dist/components/ui/time-range-picker.d.ts +18 -1
  20. package/dist/components/ui/time-range-picker.test.d.ts +1 -0
  21. package/dist/components/ui/tool-ui.d.ts +7 -7
  22. package/dist/components/ui/tooltip.d.ts +4 -4
  23. package/dist/contexts/ConnectionStatusContext.d.ts +1 -1
  24. package/dist/contexts/ElementsProvider.d.ts +1 -1
  25. package/dist/contexts/ToolApprovalContext.d.ts +2 -2
  26. package/dist/contexts/ToolExecutionContext.d.ts +1 -1
  27. package/dist/contexts/portal-container.d.ts +1 -1
  28. package/dist/elements.cjs +1 -1
  29. package/dist/elements.css +1 -1
  30. package/dist/elements.js +19 -16
  31. package/dist/hooks/useDensity.d.ts +1 -1
  32. package/dist/hooks/useElements.d.ts +2 -1
  33. package/dist/hooks/useGramThreadListAdapter.d.ts +26 -0
  34. package/dist/hooks/useRadius.d.ts +1 -1
  35. package/dist/hooks/useThemeProps.d.ts +1 -1
  36. package/dist/hooks/useToolApproval.d.ts +2 -1
  37. package/dist/{index-reVrRxP1.js → index-BhIowiZF.js} +9744 -9493
  38. package/dist/index-BhIowiZF.js.map +1 -0
  39. package/dist/{index-DAWGW1Nj.cjs → index-D0jIGQr7.cjs} +43 -43
  40. package/dist/index-D0jIGQr7.cjs.map +1 -0
  41. package/dist/{index-CGoLfO5p.js → index-Dz13dSDa.js} +108 -51
  42. package/dist/index-Dz13dSDa.js.map +1 -0
  43. package/dist/index-PXd3rs95.cjs +194 -0
  44. package/dist/index-PXd3rs95.cjs.map +1 -0
  45. package/dist/index.d.ts +4 -1
  46. package/dist/lib/errorTracking.d.ts +1 -1
  47. package/dist/lib/messageConverter.d.ts +58 -8
  48. package/dist/lib/models.d.ts +1 -1
  49. package/dist/lib/tools.d.ts +11 -10
  50. package/dist/lib/utils.d.ts +2 -0
  51. package/dist/plugins/generative-ui/catalog.d.ts +3 -3
  52. package/dist/plugins/generative-ui/ui/accordion-wrapper.d.ts +2 -2
  53. package/dist/plugins/generative-ui/ui/accordion.d.ts +4 -4
  54. package/dist/plugins/generative-ui/ui/action-button.d.ts +1 -1
  55. package/dist/plugins/generative-ui/ui/alert-wrapper.d.ts +2 -1
  56. package/dist/plugins/generative-ui/ui/alert.d.ts +3 -3
  57. package/dist/plugins/generative-ui/ui/avatar-wrapper.d.ts +2 -1
  58. package/dist/plugins/generative-ui/ui/avatar.d.ts +6 -6
  59. package/dist/plugins/generative-ui/ui/badge.d.ts +2 -2
  60. package/dist/plugins/generative-ui/ui/button-wrapper.d.ts +2 -1
  61. package/dist/plugins/generative-ui/ui/button.d.ts +3 -3
  62. package/dist/plugins/generative-ui/ui/card-wrapper.d.ts +1 -1
  63. package/dist/plugins/generative-ui/ui/card.d.ts +7 -7
  64. package/dist/plugins/generative-ui/ui/checkbox-wrapper.d.ts +2 -1
  65. package/dist/plugins/generative-ui/ui/checkbox.d.ts +1 -1
  66. package/dist/plugins/generative-ui/ui/data-table.d.ts +1 -1
  67. package/dist/plugins/generative-ui/ui/dialog.d.ts +10 -10
  68. package/dist/plugins/generative-ui/ui/dropdown-menu.d.ts +15 -15
  69. package/dist/plugins/generative-ui/ui/grid.d.ts +1 -1
  70. package/dist/plugins/generative-ui/ui/index.d.ts +57 -40
  71. package/dist/plugins/generative-ui/ui/input-wrapper.d.ts +2 -1
  72. package/dist/plugins/generative-ui/ui/input.d.ts +1 -1
  73. package/dist/plugins/generative-ui/ui/label.d.ts +1 -1
  74. package/dist/plugins/generative-ui/ui/list.d.ts +2 -1
  75. package/dist/plugins/generative-ui/ui/metric.d.ts +1 -1
  76. package/dist/plugins/generative-ui/ui/pagination.d.ts +7 -7
  77. package/dist/plugins/generative-ui/ui/popover.d.ts +7 -7
  78. package/dist/plugins/generative-ui/ui/progress.d.ts +1 -1
  79. package/dist/plugins/generative-ui/ui/radio-group.d.ts +2 -2
  80. package/dist/plugins/generative-ui/ui/select-wrapper.d.ts +2 -1
  81. package/dist/plugins/generative-ui/ui/select.d.ts +10 -10
  82. package/dist/plugins/generative-ui/ui/separator.d.ts +1 -1
  83. package/dist/plugins/generative-ui/ui/skeleton-wrapper.d.ts +2 -1
  84. package/dist/plugins/generative-ui/ui/skeleton.d.ts +1 -1
  85. package/dist/plugins/generative-ui/ui/stack.d.ts +1 -1
  86. package/dist/plugins/generative-ui/ui/switch.d.ts +1 -1
  87. package/dist/plugins/generative-ui/ui/table.d.ts +8 -8
  88. package/dist/plugins/generative-ui/ui/tabs-wrapper.d.ts +2 -2
  89. package/dist/plugins/generative-ui/ui/tabs.d.ts +4 -4
  90. package/dist/plugins/generative-ui/ui/text.d.ts +1 -1
  91. package/dist/plugins/generative-ui/ui/textarea.d.ts +1 -1
  92. package/dist/plugins/generative-ui/ui/tooltip.d.ts +4 -4
  93. package/dist/plugins.cjs +1 -1
  94. package/dist/plugins.js +1 -1
  95. package/dist/{profiler-noho3NG9.js → profiler-CtGKTWWP.js} +2 -2
  96. package/dist/{profiler-noho3NG9.js.map → profiler-CtGKTWWP.js.map} +1 -1
  97. package/dist/{profiler-B3tfiOx4.cjs → profiler-l7_HjTyw.cjs} +2 -2
  98. package/dist/{profiler-B3tfiOx4.cjs.map → profiler-l7_HjTyw.cjs.map} +1 -1
  99. package/dist/react-shim.cjs.map +1 -1
  100. package/dist/react-shim.d.ts +1 -1
  101. package/dist/react-shim.js +1 -4
  102. package/dist/react-shim.js.map +1 -1
  103. package/dist/server/bun.cjs.map +1 -1
  104. package/dist/server/bun.js.map +1 -1
  105. package/dist/server/express.cjs.map +1 -1
  106. package/dist/server/express.js.map +1 -1
  107. package/dist/server/fastify.cjs.map +1 -1
  108. package/dist/server/fastify.js.map +1 -1
  109. package/dist/server/hono.cjs.map +1 -1
  110. package/dist/server/hono.js.map +1 -1
  111. package/dist/server/nextjs.cjs.map +1 -1
  112. package/dist/server/nextjs.js.map +1 -1
  113. package/dist/server/tanstack-start.cjs.map +1 -1
  114. package/dist/server/tanstack-start.js.map +1 -1
  115. package/dist/{startRecording-7Oy6wM18.cjs → startRecording-DEw2Aeq4.cjs} +2 -2
  116. package/dist/{startRecording-7Oy6wM18.cjs.map → startRecording-DEw2Aeq4.cjs.map} +1 -1
  117. package/dist/{startRecording-mkmig-2n.js → startRecording-iYEL0-vr.js} +2 -2
  118. package/dist/{startRecording-mkmig-2n.js.map → startRecording-iYEL0-vr.js.map} +1 -1
  119. package/dist/types/index.d.ts +93 -4
  120. package/package.json +8 -9
  121. package/src/compat-shims.ts +16 -2
  122. package/src/components/Chat/index.tsx +4 -1
  123. package/src/components/Chat/stories/FrontendTools.stories.tsx +1 -1
  124. package/src/components/Chat/stories/ToolApproval.stories.tsx +2 -2
  125. package/src/components/Chat/stories/Tools.stories.tsx +13 -5
  126. package/src/components/ChatHistory.tsx +3 -1
  127. package/src/components/FrontendTools/index.tsx +1 -1
  128. package/src/components/MessageContent.tsx +1 -0
  129. package/src/components/Replay.stories.tsx +2 -3
  130. package/src/components/Replay.tsx +17 -10
  131. package/src/components/ShadowRoot.tsx +2 -2
  132. package/src/components/ShareButton/index.tsx +4 -2
  133. package/src/components/assistant-ui/assistant-modal.tsx +5 -3
  134. package/src/components/assistant-ui/attachment.tsx +1 -1
  135. package/src/components/assistant-ui/error-boundary.tsx +1 -1
  136. package/src/components/assistant-ui/markdown-text.tsx +1 -1
  137. package/src/components/assistant-ui/thread.tsx +256 -14
  138. package/src/components/assistant-ui/tool-mention-autocomplete.tsx +1 -1
  139. package/src/components/ui/avatar.tsx +3 -3
  140. package/src/components/ui/calendar.tsx +1 -1
  141. package/src/components/ui/collapsible.tsx +7 -3
  142. package/src/components/ui/dialog.tsx +18 -10
  143. package/src/components/ui/generative-ui.tsx +9 -4
  144. package/src/components/ui/popover.tsx +4 -4
  145. package/src/components/ui/skeleton.tsx +4 -1
  146. package/src/components/ui/time-range-picker.stories.tsx +164 -154
  147. package/src/components/ui/time-range-picker.test.ts +57 -0
  148. package/src/components/ui/time-range-picker.tsx +40 -9
  149. package/src/components/ui/tool-ui.tsx +18 -9
  150. package/src/components/ui/tooltip.tsx +4 -4
  151. package/src/contexts/ChatIdContext.tsx +1 -1
  152. package/src/contexts/ConnectionStatusContext.tsx +6 -5
  153. package/src/contexts/ElementsProvider.tsx +109 -37
  154. package/src/contexts/ReplayContext.ts +1 -1
  155. package/src/contexts/ToolApprovalContext.tsx +5 -1
  156. package/src/contexts/ToolExecutionContext.tsx +1 -1
  157. package/src/contexts/portal-container.tsx +1 -1
  158. package/src/hooks/useAuth.ts +2 -1
  159. package/src/hooks/useDensity.ts +1 -1
  160. package/src/hooks/useElements.ts +2 -1
  161. package/src/hooks/useFollowOnSuggestions.ts +3 -6
  162. package/src/hooks/useGramThreadListAdapter.tsx +118 -9
  163. package/src/hooks/useMCPTools.ts +2 -2
  164. package/src/hooks/useModel.ts +1 -3
  165. package/src/hooks/usePluginComponents.ts +3 -1
  166. package/src/hooks/useRadius.ts +1 -1
  167. package/src/hooks/useSession.ts +3 -1
  168. package/src/hooks/useThemeProps.ts +5 -5
  169. package/src/hooks/useToolApproval.ts +2 -1
  170. package/src/index.ts +16 -0
  171. package/src/lib/cassette.ts +21 -27
  172. package/src/lib/contextCompaction.test.ts +2 -2
  173. package/src/lib/contextCompaction.ts +20 -8
  174. package/src/lib/errorTracking.ts +1 -4
  175. package/src/lib/messageConverter.test.ts +11 -13
  176. package/src/lib/messageConverter.ts +105 -58
  177. package/src/lib/models.ts +19 -7
  178. package/src/lib/token.ts +2 -5
  179. package/src/lib/tool-mentions.ts +5 -2
  180. package/src/lib/tools.byte-cap.test.ts +1 -1
  181. package/src/lib/tools.test.ts +1 -1
  182. package/src/lib/tools.ts +15 -5
  183. package/src/lib/utils.ts +22 -2
  184. package/src/lib.d.ts +8 -1
  185. package/src/plugins/chart/chart.test.ts +3 -4
  186. package/src/plugins/chart/component.tsx +7 -6
  187. package/src/plugins/chart/ui/area-chart.tsx +1 -1
  188. package/src/plugins/chart/ui/line-chart.tsx +1 -1
  189. package/src/plugins/generative-ui/ui/accordion-wrapper.tsx +2 -2
  190. package/src/plugins/generative-ui/ui/accordion.tsx +4 -4
  191. package/src/plugins/generative-ui/ui/action-button.tsx +4 -2
  192. package/src/plugins/generative-ui/ui/alert-wrapper.tsx +1 -1
  193. package/src/plugins/generative-ui/ui/alert.tsx +7 -3
  194. package/src/plugins/generative-ui/ui/avatar-wrapper.tsx +5 -1
  195. package/src/plugins/generative-ui/ui/avatar.tsx +12 -6
  196. package/src/plugins/generative-ui/ui/badge.tsx +1 -1
  197. package/src/plugins/generative-ui/ui/button-wrapper.tsx +1 -1
  198. package/src/plugins/generative-ui/ui/button.tsx +1 -1
  199. package/src/plugins/generative-ui/ui/card-wrapper.tsx +1 -1
  200. package/src/plugins/generative-ui/ui/card.tsx +28 -7
  201. package/src/plugins/generative-ui/ui/checkbox-wrapper.tsx +1 -1
  202. package/src/plugins/generative-ui/ui/checkbox.tsx +1 -1
  203. package/src/plugins/generative-ui/ui/data-table.tsx +1 -1
  204. package/src/plugins/generative-ui/ui/dialog.tsx +15 -10
  205. package/src/plugins/generative-ui/ui/dropdown-menu.tsx +33 -15
  206. package/src/plugins/generative-ui/ui/grid.tsx +1 -1
  207. package/src/plugins/generative-ui/ui/index.ts +154 -40
  208. package/src/plugins/generative-ui/ui/input-wrapper.tsx +1 -1
  209. package/src/plugins/generative-ui/ui/input.tsx +5 -1
  210. package/src/plugins/generative-ui/ui/label.tsx +1 -1
  211. package/src/plugins/generative-ui/ui/list.tsx +5 -1
  212. package/src/plugins/generative-ui/ui/metric.tsx +2 -1
  213. package/src/plugins/generative-ui/ui/pagination.tsx +12 -7
  214. package/src/plugins/generative-ui/ui/popover.tsx +13 -7
  215. package/src/plugins/generative-ui/ui/progress.tsx +1 -1
  216. package/src/plugins/generative-ui/ui/radio-group.tsx +2 -2
  217. package/src/plugins/generative-ui/ui/select-wrapper.tsx +1 -1
  218. package/src/plugins/generative-ui/ui/select.tsx +14 -10
  219. package/src/plugins/generative-ui/ui/separator.tsx +1 -1
  220. package/src/plugins/generative-ui/ui/skeleton-wrapper.tsx +1 -1
  221. package/src/plugins/generative-ui/ui/skeleton.tsx +4 -1
  222. package/src/plugins/generative-ui/ui/stack.tsx +1 -1
  223. package/src/plugins/generative-ui/ui/switch.tsx +1 -1
  224. package/src/plugins/generative-ui/ui/table.tsx +29 -8
  225. package/src/plugins/generative-ui/ui/tabs-wrapper.tsx +5 -2
  226. package/src/plugins/generative-ui/ui/tabs.tsx +4 -4
  227. package/src/plugins/generative-ui/ui/text.tsx +1 -1
  228. package/src/plugins/generative-ui/ui/textarea.tsx +4 -1
  229. package/src/plugins/generative-ui/ui/tooltip.tsx +4 -4
  230. package/src/react-shim.ts +9 -4
  231. package/src/server/bun.ts +1 -1
  232. package/src/server/express.ts +1 -1
  233. package/src/server/fastify.ts +1 -1
  234. package/src/server/hono.ts +1 -1
  235. package/src/server/nextjs.ts +1 -1
  236. package/src/server/tanstack-start.ts +1 -1
  237. package/src/storybook.d.ts +5 -0
  238. package/src/types/index.ts +112 -4
  239. package/dist/index-BCV7Zf9E.cjs +0 -194
  240. package/dist/index-BCV7Zf9E.cjs.map +0 -1
  241. package/dist/index-CGoLfO5p.js.map +0 -1
  242. package/dist/index-DAWGW1Nj.cjs.map +0 -1
  243. package/dist/index-reVrRxP1.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  ArrowDownIcon,
3
3
  ArrowUpIcon,
4
+ AtSign,
4
5
  CheckIcon,
5
6
  ChevronLeftIcon,
6
7
  ChevronRightIcon,
@@ -8,8 +9,10 @@ import {
8
9
  CopyIcon,
9
10
  DownloadIcon,
10
11
  PencilIcon,
12
+ Search,
11
13
  Settings2,
12
14
  Square,
15
+ Wrench,
13
16
  } from "lucide-react";
14
17
 
15
18
  import {
@@ -20,6 +23,7 @@ import {
20
23
  ImageMessagePartProps,
21
24
  MessagePrimitive,
22
25
  ThreadPrimitive,
26
+ useAssistantApi,
23
27
  useAssistantState,
24
28
  } from "@assistant-ui/react";
25
29
 
@@ -68,6 +72,10 @@ import { useToolMentions } from "@/hooks/useToolMentions";
68
72
  import { getApiUrl } from "@/lib/api";
69
73
  import { EASE_OUT_QUINT } from "@/lib/easing";
70
74
  import { MODELS } from "@/lib/models";
75
+ import {
76
+ type MentionableTool,
77
+ toolSetToMentionableTools,
78
+ } from "@/lib/tool-mentions";
71
79
  import { cn } from "@/lib/utils";
72
80
  import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
73
81
  import {
@@ -262,7 +270,7 @@ const ThreadScrollToBottom: FC = () => {
262
270
  const ThreadWelcome: FC = () => {
263
271
  const { config } = useElements();
264
272
  const d = useDensity();
265
- const { title, subtitle } = config.welcome ?? {};
273
+ const { logo, title, subtitle } = config.welcome ?? {};
266
274
  const isStandalone = config.variant === "standalone";
267
275
 
268
276
  return (
@@ -288,6 +296,19 @@ const ThreadWelcome: FC = () => {
288
296
  !isStandalone && d("py-md"),
289
297
  )}
290
298
  >
299
+ {logo && (
300
+ <m.img
301
+ src={logo}
302
+ alt=""
303
+ initial={{ opacity: 0, y: 10 }}
304
+ animate={{ opacity: 1, y: 0 }}
305
+ exit={{ opacity: 0, y: 10 }}
306
+ transition={{ duration: 0.25, ease: EASE_OUT_QUINT }}
307
+ className={cn(
308
+ "aui-thread-welcome-logo mb-2 size-12 object-contain",
309
+ )}
310
+ />
311
+ )}
291
312
  <m.div
292
313
  initial={{ opacity: 0, y: 10 }}
293
314
  animate={{ opacity: 1, y: 0 }}
@@ -524,7 +545,9 @@ const ComposerFeedback: FC = () => {
524
545
  <MessageFeedback
525
546
  className="mx-auto"
526
547
  onResolved={setResolved}
527
- onFeedback={handleFeedback}
548
+ onFeedback={(type) => {
549
+ void handleFeedback(type);
550
+ }}
528
551
  />
529
552
  </m.div>
530
553
  )}
@@ -794,6 +817,218 @@ const ComposerCassetteRecorder: FC = () => {
794
817
  );
795
818
  };
796
819
 
820
+ // Sentinel for the "All" pseudo-category in the tool-mention picker.
821
+ const TOOL_MENTION_ALL_CATEGORY = "__all__";
822
+
823
+ function humanizeToolCategory(raw: string): string {
824
+ const cleaned = raw.replace(/[-_]+/g, " ").trim();
825
+ if (!cleaned) return "Tools";
826
+ return cleaned.replace(/\b\w/g, (c) => c.toUpperCase());
827
+ }
828
+
829
+ // Derive a grouping label for a tool. Tools from multiple MCP servers are
830
+ // namespaced as `<server>__<tool>`; otherwise group by the first
831
+ // underscore-delimited segment (e.g. `platform_search_logs` -> "Platform"),
832
+ // falling back to a single "Tools" bucket.
833
+ function deriveToolCategory(name: string): string {
834
+ const namespaceIdx = name.indexOf("__");
835
+ if (namespaceIdx > 0)
836
+ return humanizeToolCategory(name.slice(0, namespaceIdx));
837
+ const underscoreIdx = name.indexOf("_");
838
+ if (underscoreIdx > 0)
839
+ return humanizeToolCategory(name.slice(0, underscoreIdx));
840
+ return "Tools";
841
+ }
842
+
843
+ interface ToolCategory {
844
+ name: string;
845
+ tools: MentionableTool[];
846
+ }
847
+
848
+ // A discoverable counterpart to the type-`@` autocomplete: a composer button
849
+ // that opens a searchable, category-grouped picker of the available tools and
850
+ // inserts an @mention for the chosen one. Inserts through the composer runtime
851
+ // so it stays in sync with the autocomplete's own textarea handling. Hidden when
852
+ // tool mentions are disabled or there are no tools.
853
+ const ComposerToolMentionPicker: FC = () => {
854
+ const { config, mcpTools, mcpToolsLoading } = useElements();
855
+ const api = useAssistantApi();
856
+ // Read the composer text from the same reactive source the tool-mention
857
+ // badges parse, so an inserted mention renders a pill just like the type-`@`
858
+ // autocomplete does.
859
+ const composerText = useAssistantState(({ composer }) => composer.text);
860
+ const [open, setOpen] = useState(false);
861
+ const [query, setQuery] = useState("");
862
+ const [activeCategory, setActiveCategory] = useState(
863
+ TOOL_MENTION_ALL_CATEGORY,
864
+ );
865
+
866
+ const composerConfig = config.composer;
867
+ const toolMentionsEnabled =
868
+ composerConfig?.toolMentions === undefined ||
869
+ composerConfig.toolMentions === true ||
870
+ (typeof composerConfig.toolMentions === "object" &&
871
+ composerConfig.toolMentions.enabled !== false);
872
+
873
+ const tools = useMemo(() => toolSetToMentionableTools(mcpTools), [mcpTools]);
874
+
875
+ const categories = useMemo<ToolCategory[]>(() => {
876
+ const grouped = new Map<string, MentionableTool[]>();
877
+ for (const tool of tools) {
878
+ const category = deriveToolCategory(tool.name);
879
+ const existing = grouped.get(category);
880
+ if (existing) {
881
+ existing.push(tool);
882
+ } else {
883
+ grouped.set(category, [tool]);
884
+ }
885
+ }
886
+ return [...grouped.entries()]
887
+ .map(([name, categoryTools]) => ({ name, tools: categoryTools }))
888
+ .sort((a, b) => a.name.localeCompare(b.name));
889
+ }, [tools]);
890
+
891
+ // Show the button while tools are still loading (so it appears immediately
892
+ // rather than popping in once the async MCP list resolves) or once there are
893
+ // tools — but hide it when the list has loaded and is empty, so we don't
894
+ // expose a dead-end control.
895
+ if (!toolMentionsEnabled || (!mcpToolsLoading && tools.length === 0)) {
896
+ return null;
897
+ }
898
+
899
+ const normalizedQuery = query.trim().toLowerCase();
900
+ const inActiveCategory =
901
+ activeCategory === TOOL_MENTION_ALL_CATEGORY
902
+ ? tools
903
+ : (categories.find((c) => c.name === activeCategory)?.tools ?? []);
904
+ const visibleTools = normalizedQuery
905
+ ? inActiveCategory.filter(
906
+ (tool) =>
907
+ tool.name.toLowerCase().includes(normalizedQuery) ||
908
+ (tool.description?.toLowerCase().includes(normalizedQuery) ?? false),
909
+ )
910
+ : inActiveCategory;
911
+
912
+ const insertMention = (toolName: string) => {
913
+ const base =
914
+ composerText && !/\s$/.test(composerText)
915
+ ? `${composerText} `
916
+ : composerText;
917
+ api.composer().setText(`${base}@${toolName} `);
918
+ setOpen(false);
919
+ setQuery("");
920
+ };
921
+
922
+ const handleOpenChange = (next: boolean) => {
923
+ setOpen(next);
924
+ if (!next) {
925
+ setQuery("");
926
+ setActiveCategory(TOOL_MENTION_ALL_CATEGORY);
927
+ }
928
+ };
929
+
930
+ return (
931
+ <Popover open={open} onOpenChange={handleOpenChange}>
932
+ <PopoverTrigger asChild>
933
+ <Button
934
+ variant="ghost"
935
+ size="icon"
936
+ data-state={open ? "open" : "closed"}
937
+ className="aui-composer-tool-mention-picker flex w-fit items-center gap-2 rounded-full px-2.5 py-1 text-xs font-semibold data-[state=open]:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30"
938
+ aria-label="Mention a tool"
939
+ >
940
+ <AtSign className="size-5 stroke-[1.5px]" />
941
+ </Button>
942
+ </PopoverTrigger>
943
+ <PopoverContent
944
+ side="top"
945
+ align="start"
946
+ className="aui-composer-tool-mention-popover w-[420px] overflow-hidden p-0"
947
+ >
948
+ <div className="flex items-center gap-2 border-b border-input px-3 py-2">
949
+ <Search className="size-4 shrink-0 text-muted-foreground" />
950
+ <input
951
+ autoFocus
952
+ value={query}
953
+ onChange={(e) => setQuery(e.target.value)}
954
+ placeholder="Search tools…"
955
+ className="w-full bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground"
956
+ aria-label="Search tools"
957
+ />
958
+ </div>
959
+ <div className="flex h-72">
960
+ <div className="w-36 shrink-0 overflow-y-auto border-r border-input p-2">
961
+ <div className="px-2 pb-1 text-[10px] font-semibold tracking-wide text-muted-foreground uppercase">
962
+ Categories
963
+ </div>
964
+ <button
965
+ type="button"
966
+ onClick={() => setActiveCategory(TOOL_MENTION_ALL_CATEGORY)}
967
+ className={cn(
968
+ "flex w-full items-center justify-between rounded px-2 py-1 text-left text-xs transition-colors",
969
+ activeCategory === TOOL_MENTION_ALL_CATEGORY
970
+ ? "bg-muted font-medium text-foreground"
971
+ : "text-muted-foreground hover:bg-muted/60",
972
+ )}
973
+ >
974
+ <span className="truncate">All</span>
975
+ <span className="ml-2 shrink-0 tabular-nums opacity-60">
976
+ {tools.length}
977
+ </span>
978
+ </button>
979
+ {categories.map((category) => (
980
+ <button
981
+ key={category.name}
982
+ type="button"
983
+ onClick={() => setActiveCategory(category.name)}
984
+ className={cn(
985
+ "flex w-full items-center justify-between rounded px-2 py-1 text-left text-xs transition-colors",
986
+ activeCategory === category.name
987
+ ? "bg-muted font-medium text-foreground"
988
+ : "text-muted-foreground hover:bg-muted/60",
989
+ )}
990
+ >
991
+ <span className="truncate">{category.name}</span>
992
+ <span className="ml-2 shrink-0 tabular-nums opacity-60">
993
+ {category.tools.length}
994
+ </span>
995
+ </button>
996
+ ))}
997
+ </div>
998
+ <div className="min-w-0 flex-1 overflow-y-auto p-2">
999
+ {visibleTools.length === 0 ? (
1000
+ <div className="px-2 py-6 text-center text-xs text-muted-foreground">
1001
+ {mcpToolsLoading ? "Loading tools…" : "No tools found"}
1002
+ </div>
1003
+ ) : (
1004
+ visibleTools.map((tool) => (
1005
+ <button
1006
+ key={tool.id}
1007
+ type="button"
1008
+ onClick={() => insertMention(tool.name)}
1009
+ className="flex w-full items-start gap-2 rounded px-2 py-1.5 text-left transition-colors hover:bg-muted"
1010
+ >
1011
+ <Wrench className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
1012
+ <span className="min-w-0 flex-1">
1013
+ <span className="block truncate text-sm font-medium text-foreground">
1014
+ {tool.name}
1015
+ </span>
1016
+ {tool.description && (
1017
+ <span className="line-clamp-2 text-xs text-muted-foreground">
1018
+ {tool.description}
1019
+ </span>
1020
+ )}
1021
+ </span>
1022
+ </button>
1023
+ ))
1024
+ )}
1025
+ </div>
1026
+ </div>
1027
+ </PopoverContent>
1028
+ </Popover>
1029
+ );
1030
+ };
1031
+
797
1032
  const ComposerAction: FC = () => {
798
1033
  const { config } = useElements();
799
1034
  const r = useRadius();
@@ -807,6 +1042,8 @@ const ComposerAction: FC = () => {
807
1042
  <div className="aui-composer-add-attachment-placeholder" />
808
1043
  )}
809
1044
 
1045
+ <ComposerToolMentionPicker />
1046
+
810
1047
  {config.model?.showModelPicker && !config.languageModel && (
811
1048
  <ComposerModelPicker />
812
1049
  )}
@@ -879,21 +1116,22 @@ const ToolCallStreamingIndicator: FC = () => {
879
1116
  const AssistantMessage: FC = () => {
880
1117
  const { config } = useElements();
881
1118
  const toolsConfig = config.tools ?? {};
882
- const components = config.components ?? {};
1119
+ const components = config.components;
1120
+ const toolsComponents = toolsConfig.components;
883
1121
 
884
1122
  const partsComponents = useMemo(
885
1123
  () => ({
886
- Text: components.Text ?? MarkdownText,
887
- Image: components.Image ?? Image,
1124
+ Text: components?.Text ?? MarkdownText,
1125
+ Image: components?.Image ?? Image,
888
1126
  tools: {
889
- by_name: toolsConfig.components,
890
- Fallback: components.ToolFallback ?? ToolFallback,
1127
+ by_name: toolsComponents,
1128
+ Fallback: components?.ToolFallback ?? ToolFallback,
891
1129
  },
892
- Reasoning: components.Reasoning ?? Reasoning,
893
- ReasoningGroup: components.ReasoningGroup ?? ReasoningGroup,
894
- ToolGroup: components.ToolGroup ?? ToolGroup,
1130
+ Reasoning: components?.Reasoning ?? Reasoning,
1131
+ ReasoningGroup: components?.ReasoningGroup ?? ReasoningGroup,
1132
+ ToolGroup: components?.ToolGroup ?? ToolGroup,
895
1133
  }),
896
- [components, toolsConfig.components],
1134
+ [components, toolsComponents],
897
1135
  );
898
1136
 
899
1137
  return (
@@ -950,6 +1188,8 @@ const AssistantActionBar: FC = () => {
950
1188
 
951
1189
  const UserMessage: FC = () => {
952
1190
  const r = useRadius();
1191
+ const { config } = useElements();
1192
+ const allowEdit = config.allowMessageEdit !== false;
953
1193
  return (
954
1194
  <MessagePrimitive.Root asChild>
955
1195
  <div
@@ -967,9 +1207,11 @@ const UserMessage: FC = () => {
967
1207
  >
968
1208
  <MessagePrimitive.Parts />
969
1209
  </div>
970
- <div className="aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2">
971
- <UserActionBar />
972
- </div>
1210
+ {allowEdit && (
1211
+ <div className="aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2">
1212
+ <UserActionBar />
1213
+ </div>
1214
+ )}
973
1215
  </div>
974
1216
 
975
1217
  <BranchPicker className="aui-user-branch-picker col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
@@ -74,7 +74,7 @@ export const ToolMentionAutocomplete: FC<ToolMentionAutocompleteProps> = ({
74
74
  onValueChange(result.text, result.cursorPosition);
75
75
  setIsVisible(false);
76
76
  },
77
- [mentionContext, value, cursorPosition, onValueChange, textareaRef],
77
+ [mentionContext, value, cursorPosition, onValueChange],
78
78
  );
79
79
 
80
80
  useEffect(() => {
@@ -6,7 +6,7 @@ import { cn } from "@/lib/utils";
6
6
  function Avatar({
7
7
  className,
8
8
  ...props
9
- }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
9
+ }: React.ComponentProps<typeof AvatarPrimitive.Root>): React.JSX.Element {
10
10
  return (
11
11
  <AvatarPrimitive.Root
12
12
  data-slot="avatar"
@@ -22,7 +22,7 @@ function Avatar({
22
22
  function AvatarImage({
23
23
  className,
24
24
  ...props
25
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
25
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>): React.JSX.Element {
26
26
  return (
27
27
  <AvatarPrimitive.Image
28
28
  data-slot="avatar-image"
@@ -35,7 +35,7 @@ function AvatarImage({
35
35
  function AvatarFallback({
36
36
  className,
37
37
  ...props
38
- }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
38
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>): React.JSX.Element {
39
39
  return (
40
40
  <AvatarPrimitive.Fallback
41
41
  data-slot="avatar-fallback"
@@ -130,7 +130,7 @@ function Calendar({
130
130
  minDate,
131
131
  maxDate,
132
132
  className,
133
- }: CalendarProps) {
133
+ }: CalendarProps): React.JSX.Element {
134
134
  const [viewDate, setViewDate] = React.useState(() => {
135
135
  if (selected.start) return new Date(selected.start);
136
136
  return new Date();
@@ -2,13 +2,15 @@ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
2
2
 
3
3
  function Collapsible({
4
4
  ...props
5
- }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
5
+ }: React.ComponentProps<typeof CollapsiblePrimitive.Root>): React.JSX.Element {
6
6
  return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
7
7
  }
8
8
 
9
9
  function CollapsibleTrigger({
10
10
  ...props
11
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
11
+ }: React.ComponentProps<
12
+ typeof CollapsiblePrimitive.CollapsibleTrigger
13
+ >): React.JSX.Element {
12
14
  return (
13
15
  <CollapsiblePrimitive.CollapsibleTrigger
14
16
  data-slot="collapsible-trigger"
@@ -19,7 +21,9 @@ function CollapsibleTrigger({
19
21
 
20
22
  function CollapsibleContent({
21
23
  ...props
22
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
24
+ }: React.ComponentProps<
25
+ typeof CollapsiblePrimitive.CollapsibleContent
26
+ >): React.JSX.Element {
23
27
  return (
24
28
  <CollapsiblePrimitive.CollapsibleContent
25
29
  data-slot="collapsible-content"
@@ -7,20 +7,20 @@ import { usePortalContainer } from "@/hooks/usePortalContainer";
7
7
 
8
8
  function Dialog({
9
9
  ...props
10
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
10
+ }: React.ComponentProps<typeof DialogPrimitive.Root>): React.JSX.Element {
11
11
  return <DialogPrimitive.Root data-slot="dialog" {...props} />;
12
12
  }
13
13
 
14
14
  function DialogTrigger({
15
15
  ...props
16
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
16
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>): React.JSX.Element {
17
17
  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
18
18
  }
19
19
 
20
20
  function DialogPortal({
21
21
  container,
22
22
  ...props
23
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
23
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>): React.JSX.Element {
24
24
  const portalContainer = usePortalContainer();
25
25
  return (
26
26
  <DialogPrimitive.Portal
@@ -33,14 +33,14 @@ function DialogPortal({
33
33
 
34
34
  function DialogClose({
35
35
  ...props
36
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
36
+ }: React.ComponentProps<typeof DialogPrimitive.Close>): React.JSX.Element {
37
37
  return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
38
38
  }
39
39
 
40
40
  function DialogOverlay({
41
41
  className,
42
42
  ...props
43
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
43
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>): React.JSX.Element {
44
44
  return (
45
45
  <DialogPrimitive.Overlay
46
46
  data-slot="dialog-overlay"
@@ -60,7 +60,7 @@ function DialogContent({
60
60
  ...props
61
61
  }: React.ComponentProps<typeof DialogPrimitive.Content> & {
62
62
  showCloseButton?: boolean;
63
- }) {
63
+ }): React.JSX.Element {
64
64
  return (
65
65
  <DialogPortal data-slot="dialog-portal">
66
66
  <DialogOverlay />
@@ -87,7 +87,10 @@ function DialogContent({
87
87
  );
88
88
  }
89
89
 
90
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
90
+ function DialogHeader({
91
+ className,
92
+ ...props
93
+ }: React.ComponentProps<"div">): React.JSX.Element {
91
94
  return (
92
95
  <div
93
96
  data-slot="dialog-header"
@@ -97,7 +100,10 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
97
100
  );
98
101
  }
99
102
 
100
- function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
103
+ function DialogFooter({
104
+ className,
105
+ ...props
106
+ }: React.ComponentProps<"div">): React.JSX.Element {
101
107
  return (
102
108
  <div
103
109
  data-slot="dialog-footer"
@@ -113,7 +119,7 @@ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
113
119
  function DialogTitle({
114
120
  className,
115
121
  ...props
116
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
122
+ }: React.ComponentProps<typeof DialogPrimitive.Title>): React.JSX.Element {
117
123
  return (
118
124
  <DialogPrimitive.Title
119
125
  data-slot="dialog-title"
@@ -126,7 +132,9 @@ function DialogTitle({
126
132
  function DialogDescription({
127
133
  className,
128
134
  ...props
129
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
135
+ }: React.ComponentProps<
136
+ typeof DialogPrimitive.Description
137
+ >): React.JSX.Element {
130
138
  return (
131
139
  <DialogPrimitive.Description
132
140
  data-slot="dialog-description"
@@ -4,7 +4,7 @@ import { useDensity } from "@/hooks/useDensity";
4
4
  import { cn } from "@/lib/utils";
5
5
  import { isJsonRenderTree, type JsonRenderNode } from "@/lib/generative-ui";
6
6
  import { AlertCircleIcon } from "lucide-react";
7
- import { FC, useMemo } from "react";
7
+ import { ElementType, FC, useMemo } from "react";
8
8
 
9
9
  // Import all components from the generative-ui plugin ui directory
10
10
  import {
@@ -42,9 +42,10 @@ interface GenerativeUIProps {
42
42
  /**
43
43
  * Built-in components for rendering json-render trees.
44
44
  * These provide a default set of UI primitives for tool results.
45
+ * Each entry has its own prop shape; the registry erases those generics via
46
+ * `ElementType` so heterogeneous components can coexist under one map.
45
47
  */
46
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
- const components: Record<string, FC<any>> = {
48
+ const components: Record<string, ElementType> = {
48
49
  // Layout
49
50
  Card: CardWrapper,
50
51
  Grid,
@@ -98,7 +99,11 @@ function renderNode(node: JsonRenderNode, key?: number): React.ReactNode {
98
99
  ? node.children.map((child, i) => renderNode(child, i))
99
100
  : undefined;
100
101
 
101
- return <Component key={key} {...(node.props ?? {})} children={children} />;
102
+ return (
103
+ <Component key={key} {...(node.props ?? {})}>
104
+ {children}
105
+ </Component>
106
+ );
102
107
  }
103
108
 
104
109
  /**
@@ -6,13 +6,13 @@ import { usePortalContainer } from "@/hooks/usePortalContainer";
6
6
 
7
7
  function Popover({
8
8
  ...props
9
- }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
9
+ }: React.ComponentProps<typeof PopoverPrimitive.Root>): React.JSX.Element {
10
10
  return <PopoverPrimitive.Root data-slot="popover" {...props} />;
11
11
  }
12
12
 
13
13
  function PopoverTrigger({
14
14
  ...props
15
- }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
15
+ }: React.ComponentProps<typeof PopoverPrimitive.Trigger>): React.JSX.Element {
16
16
  return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
17
17
  }
18
18
 
@@ -24,7 +24,7 @@ function PopoverContent({
24
24
  ...props
25
25
  }: React.ComponentProps<typeof PopoverPrimitive.Content> & {
26
26
  container?: HTMLElement | null;
27
- }) {
27
+ }): React.JSX.Element {
28
28
  const portalContainer = usePortalContainer();
29
29
  return (
30
30
  <PopoverPrimitive.Portal container={container ?? portalContainer}>
@@ -44,7 +44,7 @@ function PopoverContent({
44
44
 
45
45
  function PopoverAnchor({
46
46
  ...props
47
- }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
47
+ }: React.ComponentProps<typeof PopoverPrimitive.Anchor>): React.JSX.Element {
48
48
  return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
49
49
  }
50
50
 
@@ -1,6 +1,9 @@
1
1
  import { cn } from "@/lib/utils";
2
2
 
3
- function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
3
+ function Skeleton({
4
+ className,
5
+ ...props
6
+ }: React.ComponentProps<"div">): React.JSX.Element {
4
7
  return (
5
8
  <div
6
9
  data-slot="skeleton"