@assistant-ui/react 0.12.22 → 0.12.24

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 (136) hide show
  1. package/dist/client/ExternalThread.d.ts.map +1 -1
  2. package/dist/client/ExternalThread.js +1 -0
  3. package/dist/client/ExternalThread.js.map +1 -1
  4. package/dist/client/InMemoryThreadList.d.ts.map +1 -1
  5. package/dist/client/InMemoryThreadList.js +2 -0
  6. package/dist/client/InMemoryThreadList.js.map +1 -1
  7. package/dist/client/SingleThreadList.d.ts.map +1 -1
  8. package/dist/client/SingleThreadList.js +2 -0
  9. package/dist/client/SingleThreadList.js.map +1 -1
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/internal.d.ts +1 -0
  15. package/dist/internal.d.ts.map +1 -1
  16. package/dist/internal.js +2 -0
  17. package/dist/internal.js.map +1 -1
  18. package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
  19. package/dist/primitives/composer/ComposerInput.js +27 -12
  20. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  21. package/dist/primitives/composer/ComposerInputPluginContext.d.ts +31 -0
  22. package/dist/primitives/composer/ComposerInputPluginContext.d.ts.map +1 -0
  23. package/dist/primitives/composer/ComposerInputPluginContext.js +32 -0
  24. package/dist/primitives/composer/ComposerInputPluginContext.js.map +1 -0
  25. package/dist/primitives/composer/mention/ComposerMentionContext.d.ts +4 -2
  26. package/dist/primitives/composer/mention/ComposerMentionContext.d.ts.map +1 -1
  27. package/dist/primitives/composer/mention/ComposerMentionContext.js +21 -13
  28. package/dist/primitives/composer/mention/ComposerMentionContext.js.map +1 -1
  29. package/dist/primitives/composer/mention/index.d.ts +4 -4
  30. package/dist/primitives/composer/mention/index.d.ts.map +1 -1
  31. package/dist/primitives/composer/mention/index.js +6 -4
  32. package/dist/primitives/composer/mention/index.js.map +1 -1
  33. package/dist/primitives/composer/slash-command/ComposerSlashCommandRoot.d.ts +36 -0
  34. package/dist/primitives/composer/slash-command/ComposerSlashCommandRoot.d.ts.map +1 -0
  35. package/dist/primitives/composer/slash-command/ComposerSlashCommandRoot.js +36 -0
  36. package/dist/primitives/composer/slash-command/ComposerSlashCommandRoot.js.map +1 -0
  37. package/dist/primitives/composer/slash-command/index.d.ts +2 -0
  38. package/dist/primitives/composer/slash-command/index.d.ts.map +1 -0
  39. package/dist/primitives/composer/slash-command/index.js +2 -0
  40. package/dist/primitives/composer/slash-command/index.js.map +1 -0
  41. package/dist/primitives/composer/{mention/ComposerMentionBack.d.ts → trigger/TriggerPopoverBack.d.ts} +3 -10
  42. package/dist/primitives/composer/trigger/TriggerPopoverBack.d.ts.map +1 -0
  43. package/dist/primitives/composer/trigger/TriggerPopoverBack.js +19 -0
  44. package/dist/primitives/composer/trigger/TriggerPopoverBack.js.map +1 -0
  45. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts +38 -0
  46. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -0
  47. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js +35 -0
  48. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -0
  49. package/dist/primitives/composer/trigger/TriggerPopoverContext.d.ts +37 -0
  50. package/dist/primitives/composer/trigger/TriggerPopoverContext.d.ts.map +1 -0
  51. package/dist/primitives/composer/trigger/TriggerPopoverContext.js +70 -0
  52. package/dist/primitives/composer/trigger/TriggerPopoverContext.js.map +1 -0
  53. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts +40 -0
  54. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -0
  55. package/dist/primitives/composer/trigger/TriggerPopoverItems.js +35 -0
  56. package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -0
  57. package/dist/primitives/composer/trigger/TriggerPopoverPopover.d.ts +26 -0
  58. package/dist/primitives/composer/trigger/TriggerPopoverPopover.d.ts.map +1 -0
  59. package/dist/primitives/composer/trigger/TriggerPopoverPopover.js +28 -0
  60. package/dist/primitives/composer/trigger/TriggerPopoverPopover.js.map +1 -0
  61. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts +53 -0
  62. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -0
  63. package/dist/primitives/composer/{mention/MentionResource.js → trigger/TriggerPopoverResource.js} +50 -25
  64. package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -0
  65. package/dist/primitives/composer/trigger/detectTrigger.d.ts +2 -0
  66. package/dist/primitives/composer/trigger/detectTrigger.d.ts.map +1 -0
  67. package/dist/primitives/composer/{mention/detectMentionTrigger.js → trigger/detectTrigger.js} +4 -4
  68. package/dist/primitives/composer/trigger/detectTrigger.js.map +1 -0
  69. package/dist/primitives/composer/trigger/index.d.ts +7 -0
  70. package/dist/primitives/composer/trigger/index.d.ts.map +1 -0
  71. package/dist/primitives/composer/trigger/index.js +6 -0
  72. package/dist/primitives/composer/trigger/index.js.map +1 -0
  73. package/dist/primitives/composer.d.ts +10 -0
  74. package/dist/primitives/composer.d.ts.map +1 -1
  75. package/dist/primitives/composer.js +14 -0
  76. package/dist/primitives/composer.js.map +1 -1
  77. package/dist/primitives/message/MessageRoot.d.ts +25 -3
  78. package/dist/primitives/message/MessageRoot.d.ts.map +1 -1
  79. package/dist/primitives/message/MessageRoot.js +2 -2
  80. package/dist/primitives/message/MessageRoot.js.map +1 -1
  81. package/dist/primitives/thread/ThreadViewportSlack.d.ts +2 -2
  82. package/dist/primitives/thread/ThreadViewportSlack.d.ts.map +1 -1
  83. package/dist/unstable/useSlashCommandAdapter.d.ts +34 -0
  84. package/dist/unstable/useSlashCommandAdapter.d.ts.map +1 -0
  85. package/dist/unstable/useSlashCommandAdapter.js +50 -0
  86. package/dist/unstable/useSlashCommandAdapter.js.map +1 -0
  87. package/package.json +7 -7
  88. package/src/client/ExternalThread.ts +1 -0
  89. package/src/client/InMemoryThreadList.ts +3 -0
  90. package/src/client/SingleThreadList.ts +2 -0
  91. package/src/index.ts +14 -0
  92. package/src/internal.ts +3 -0
  93. package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.test.ts +186 -3
  94. package/src/primitives/composer/ComposerInput.tsx +25 -18
  95. package/src/primitives/composer/ComposerInputPluginContext.tsx +100 -0
  96. package/src/primitives/composer/mention/ComposerMentionContext.tsx +56 -22
  97. package/src/primitives/composer/mention/index.ts +11 -8
  98. package/src/primitives/composer/slash-command/ComposerSlashCommandRoot.tsx +76 -0
  99. package/src/primitives/composer/slash-command/index.ts +1 -0
  100. package/src/primitives/composer/trigger/TriggerPopoverBack.tsx +40 -0
  101. package/src/primitives/composer/{mention/ComposerMentionCategories.tsx → trigger/TriggerPopoverCategories.tsx} +33 -28
  102. package/src/primitives/composer/trigger/TriggerPopoverContext.tsx +129 -0
  103. package/src/primitives/composer/{mention/ComposerMentionItems.tsx → trigger/TriggerPopoverItems.tsx} +34 -29
  104. package/src/primitives/composer/trigger/TriggerPopoverPopover.tsx +51 -0
  105. package/src/primitives/composer/{mention/MentionResource.ts → trigger/TriggerPopoverResource.ts} +146 -98
  106. package/src/primitives/composer/{mention/detectMentionTrigger.test.ts → trigger/detectTrigger.test.ts} +15 -15
  107. package/src/primitives/composer/{mention/detectMentionTrigger.ts → trigger/detectTrigger.ts} +3 -3
  108. package/src/primitives/composer/trigger/index.ts +16 -0
  109. package/src/primitives/composer.ts +16 -0
  110. package/src/primitives/message/MessageRoot.tsx +18 -4
  111. package/src/primitives/thread/ThreadViewportSlack.tsx +2 -2
  112. package/src/tests/BaseComposerRuntimeCore.test.ts +33 -1
  113. package/src/unstable/useSlashCommandAdapter.ts +83 -0
  114. package/dist/primitives/composer/mention/ComposerMentionBack.d.ts.map +0 -1
  115. package/dist/primitives/composer/mention/ComposerMentionBack.js +0 -28
  116. package/dist/primitives/composer/mention/ComposerMentionBack.js.map +0 -1
  117. package/dist/primitives/composer/mention/ComposerMentionCategories.d.ts +0 -46
  118. package/dist/primitives/composer/mention/ComposerMentionCategories.d.ts.map +0 -1
  119. package/dist/primitives/composer/mention/ComposerMentionCategories.js +0 -32
  120. package/dist/primitives/composer/mention/ComposerMentionCategories.js.map +0 -1
  121. package/dist/primitives/composer/mention/ComposerMentionItems.d.ts +0 -50
  122. package/dist/primitives/composer/mention/ComposerMentionItems.d.ts.map +0 -1
  123. package/dist/primitives/composer/mention/ComposerMentionItems.js +0 -30
  124. package/dist/primitives/composer/mention/ComposerMentionItems.js.map +0 -1
  125. package/dist/primitives/composer/mention/ComposerMentionPopover.d.ts +0 -26
  126. package/dist/primitives/composer/mention/ComposerMentionPopover.d.ts.map +0 -1
  127. package/dist/primitives/composer/mention/ComposerMentionPopover.js +0 -28
  128. package/dist/primitives/composer/mention/ComposerMentionPopover.js.map +0 -1
  129. package/dist/primitives/composer/mention/MentionResource.d.ts +0 -39
  130. package/dist/primitives/composer/mention/MentionResource.d.ts.map +0 -1
  131. package/dist/primitives/composer/mention/MentionResource.js.map +0 -1
  132. package/dist/primitives/composer/mention/detectMentionTrigger.d.ts +0 -2
  133. package/dist/primitives/composer/mention/detectMentionTrigger.d.ts.map +0 -1
  134. package/dist/primitives/composer/mention/detectMentionTrigger.js.map +0 -1
  135. package/src/primitives/composer/mention/ComposerMentionBack.tsx +0 -55
  136. package/src/primitives/composer/mention/ComposerMentionPopover.tsx +0 -52
@@ -9,52 +9,56 @@ import {
9
9
  useCallback,
10
10
  } from "react";
11
11
  import { composeEventHandlers } from "@radix-ui/primitive";
12
- import { useMentionContext } from "./ComposerMentionContext";
13
- import type { Unstable_MentionCategory } from "@assistant-ui/core";
12
+ import { useTriggerPopoverContext } from "./TriggerPopoverContext";
13
+ import type { Unstable_TriggerCategory } from "@assistant-ui/core";
14
14
 
15
15
  // =============================================================================
16
- // MentionCategories — Renders the list of categories
16
+ // TriggerPopoverCategories — Renders the list of categories
17
17
  // =============================================================================
18
18
 
19
- export namespace ComposerPrimitiveMentionCategories {
19
+ export namespace ComposerPrimitiveTriggerPopoverCategories {
20
20
  export type Element = ComponentRef<typeof Primitive.div>;
21
21
  export type Props = Omit<
22
22
  ComponentPropsWithoutRef<typeof Primitive.div>,
23
23
  "children"
24
24
  > & {
25
- /**
26
- * Render function that receives the filtered categories and returns
27
- * the UI. A render-function pattern is used here (instead of a
28
- * `components` prop) to give consumers full control over list layout,
29
- * ordering, grouping, and empty states.
30
- */
31
- children: (categories: readonly Unstable_MentionCategory[]) => ReactNode;
25
+ children: (categories: readonly Unstable_TriggerCategory[]) => ReactNode;
32
26
  };
33
27
  }
34
28
 
35
- export const ComposerPrimitiveMentionCategories = forwardRef<
36
- ComposerPrimitiveMentionCategories.Element,
37
- ComposerPrimitiveMentionCategories.Props
38
- >(({ children, ...props }, forwardedRef) => {
39
- const { categories, activeCategoryId, isSearchMode } = useMentionContext();
29
+ /**
30
+ * Renders the top-level category list via a render function.
31
+ * Only renders when no category is active and search mode is off.
32
+ */
33
+ export const ComposerPrimitiveTriggerPopoverCategories = forwardRef<
34
+ ComposerPrimitiveTriggerPopoverCategories.Element,
35
+ ComposerPrimitiveTriggerPopoverCategories.Props
36
+ >(({ children, "aria-label": ariaLabel, ...props }, forwardedRef) => {
37
+ const { categories, activeCategoryId, isSearchMode } =
38
+ useTriggerPopoverContext();
40
39
 
41
40
  if (activeCategoryId || isSearchMode) return null;
42
41
 
43
42
  return (
44
- <Primitive.div role="group" {...props} ref={forwardedRef}>
43
+ <Primitive.div
44
+ role="group"
45
+ aria-label={ariaLabel ?? "Categories"}
46
+ {...props}
47
+ ref={forwardedRef}
48
+ >
45
49
  {children(categories)}
46
50
  </Primitive.div>
47
51
  );
48
52
  });
49
53
 
50
- ComposerPrimitiveMentionCategories.displayName =
51
- "ComposerPrimitive.MentionCategories";
54
+ ComposerPrimitiveTriggerPopoverCategories.displayName =
55
+ "ComposerPrimitive.TriggerPopoverCategories";
52
56
 
53
57
  // =============================================================================
54
- // MentionCategoryItem — A single category row (clickable to drill-down)
58
+ // TriggerPopoverCategoryItem — A single category row
55
59
  // =============================================================================
56
60
 
57
- export namespace ComposerPrimitiveMentionCategoryItem {
61
+ export namespace ComposerPrimitiveTriggerPopoverCategoryItem {
58
62
  export type Element = ComponentRef<typeof Primitive.button>;
59
63
  export type Props = ComponentPropsWithoutRef<typeof Primitive.button> & {
60
64
  categoryId: string;
@@ -65,9 +69,9 @@ export namespace ComposerPrimitiveMentionCategoryItem {
65
69
  * A button that selects a category and triggers drill-down navigation.
66
70
  * Automatically receives `data-highlighted` when keyboard-navigated.
67
71
  */
68
- export const ComposerPrimitiveMentionCategoryItem = forwardRef<
69
- ComposerPrimitiveMentionCategoryItem.Element,
70
- ComposerPrimitiveMentionCategoryItem.Props
72
+ export const ComposerPrimitiveTriggerPopoverCategoryItem = forwardRef<
73
+ ComposerPrimitiveTriggerPopoverCategoryItem.Element,
74
+ ComposerPrimitiveTriggerPopoverCategoryItem.Props
71
75
  >(({ categoryId, onClick, ...props }, forwardedRef) => {
72
76
  const {
73
77
  selectCategory,
@@ -75,13 +79,13 @@ export const ComposerPrimitiveMentionCategoryItem = forwardRef<
75
79
  highlightedIndex,
76
80
  activeCategoryId,
77
81
  isSearchMode,
78
- } = useMentionContext();
82
+ popoverId,
83
+ } = useTriggerPopoverContext();
79
84
 
80
85
  const handleClick = useCallback(() => {
81
86
  selectCategory(categoryId);
82
87
  }, [selectCategory, categoryId]);
83
88
 
84
- // Derive highlighted state from context — no manual wiring needed
85
89
  const isHighlighted =
86
90
  !activeCategoryId &&
87
91
  !isSearchMode &&
@@ -91,6 +95,7 @@ export const ComposerPrimitiveMentionCategoryItem = forwardRef<
91
95
  <Primitive.button
92
96
  type="button"
93
97
  role="option"
98
+ id={`${popoverId}-option-${categoryId}`}
94
99
  aria-selected={isHighlighted}
95
100
  data-highlighted={isHighlighted ? "" : undefined}
96
101
  {...props}
@@ -100,5 +105,5 @@ export const ComposerPrimitiveMentionCategoryItem = forwardRef<
100
105
  );
101
106
  });
102
107
 
103
- ComposerPrimitiveMentionCategoryItem.displayName =
104
- "ComposerPrimitive.MentionCategoryItem";
108
+ ComposerPrimitiveTriggerPopoverCategoryItem.displayName =
109
+ "ComposerPrimitive.TriggerPopoverCategoryItem";
@@ -0,0 +1,129 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useContext,
6
+ useEffect,
7
+ useId,
8
+ type ReactNode,
9
+ type FC,
10
+ } from "react";
11
+ import { useResource } from "@assistant-ui/tap/react";
12
+ import { useAui, useAuiState } from "@assistant-ui/store";
13
+ import type { Unstable_TriggerAdapter } from "@assistant-ui/core";
14
+ import {
15
+ TriggerPopoverResource,
16
+ type TriggerPopoverResourceOutput,
17
+ type OnSelectBehavior,
18
+ } from "./TriggerPopoverResource";
19
+ import {
20
+ useComposerInputPluginRegistryOptional,
21
+ ComposerInputPluginProvider,
22
+ } from "../ComposerInputPluginContext";
23
+
24
+ // =============================================================================
25
+ // Context
26
+ // =============================================================================
27
+
28
+ const TriggerPopoverContext =
29
+ createContext<TriggerPopoverResourceOutput | null>(null);
30
+
31
+ export const useTriggerPopoverContext = () => {
32
+ const ctx = useContext(TriggerPopoverContext);
33
+ if (!ctx)
34
+ throw new Error(
35
+ "useTriggerPopoverContext must be used within ComposerPrimitive.TriggerPopoverRoot",
36
+ );
37
+ return ctx;
38
+ };
39
+
40
+ export const useTriggerPopoverContextOptional = () => {
41
+ return useContext(TriggerPopoverContext);
42
+ };
43
+
44
+ // =============================================================================
45
+ // Root Component
46
+ // =============================================================================
47
+
48
+ export namespace ComposerPrimitiveTriggerPopoverRoot {
49
+ export type Props = {
50
+ children: ReactNode;
51
+ /** The adapter providing categories and items. */
52
+ adapter: Unstable_TriggerAdapter;
53
+ /** Character(s) that trigger the popover. @default "@" */
54
+ trigger?: string | undefined;
55
+ /** What happens when an item is selected. */
56
+ onSelect: OnSelectBehavior;
57
+ };
58
+ }
59
+
60
+ const TriggerPopoverRootInner: FC<
61
+ ComposerPrimitiveTriggerPopoverRoot.Props
62
+ > = ({ children, adapter, trigger: triggerChar = "@", onSelect }) => {
63
+ const aui = useAui();
64
+ const text = useAuiState((s) => s.composer.text);
65
+ const popoverId = useId();
66
+
67
+ const triggerPopover = useResource(
68
+ TriggerPopoverResource({
69
+ adapter,
70
+ text,
71
+ triggerChar,
72
+ onSelect,
73
+ aui,
74
+ popoverId,
75
+ }),
76
+ );
77
+
78
+ // Register as ComposerInput plugin
79
+ const pluginRegistry = useComposerInputPluginRegistryOptional();
80
+
81
+ useEffect(() => {
82
+ if (!pluginRegistry) return undefined;
83
+ return pluginRegistry.register(triggerPopover);
84
+ }, [pluginRegistry, triggerPopover]);
85
+
86
+ return (
87
+ <TriggerPopoverContext.Provider value={triggerPopover}>
88
+ {children}
89
+ </TriggerPopoverContext.Provider>
90
+ );
91
+ };
92
+
93
+ /**
94
+ * Provider that wraps the composer with trigger detection, keyboard navigation,
95
+ * and popover state. Supports any trigger character (`@`, `/`, `:`, etc.).
96
+ * Multiple trigger roots can coexist around the same input.
97
+ *
98
+ * @example
99
+ * ```tsx
100
+ * <ComposerPrimitive.Unstable_TriggerPopoverRoot
101
+ * trigger="/"
102
+ * adapter={slashAdapter}
103
+ * onSelect={{ type: "action", handler: (item) => console.log(item) }}
104
+ * >
105
+ * <ComposerPrimitive.Input />
106
+ * <ComposerPrimitive.Unstable_TriggerPopoverPopover>
107
+ * ...
108
+ * </ComposerPrimitive.Unstable_TriggerPopoverPopover>
109
+ * </ComposerPrimitive.Unstable_TriggerPopoverRoot>
110
+ * ```
111
+ */
112
+ export const ComposerPrimitiveTriggerPopoverRoot: FC<
113
+ ComposerPrimitiveTriggerPopoverRoot.Props
114
+ > = (props) => {
115
+ const existingRegistry = useComposerInputPluginRegistryOptional();
116
+
117
+ if (existingRegistry) {
118
+ return <TriggerPopoverRootInner {...props} />;
119
+ }
120
+
121
+ return (
122
+ <ComposerInputPluginProvider>
123
+ <TriggerPopoverRootInner {...props} />
124
+ </ComposerInputPluginProvider>
125
+ );
126
+ };
127
+
128
+ ComposerPrimitiveTriggerPopoverRoot.displayName =
129
+ "ComposerPrimitive.TriggerPopoverRoot";
@@ -9,66 +9,69 @@ import {
9
9
  useCallback,
10
10
  } from "react";
11
11
  import { composeEventHandlers } from "@radix-ui/primitive";
12
- import { useMentionContext } from "./ComposerMentionContext";
13
- import type { Unstable_MentionItem } from "@assistant-ui/core";
12
+ import { useTriggerPopoverContext } from "./TriggerPopoverContext";
13
+ import type { Unstable_TriggerItem } from "@assistant-ui/core";
14
14
 
15
15
  // =============================================================================
16
- // MentionItems — Renders the list of items within a category
16
+ // TriggerPopoverItems — Renders the list of items within a category
17
17
  // =============================================================================
18
18
 
19
- export namespace ComposerPrimitiveMentionItems {
19
+ export namespace ComposerPrimitiveTriggerPopoverItems {
20
20
  export type Element = ComponentRef<typeof Primitive.div>;
21
21
  export type Props = Omit<
22
22
  ComponentPropsWithoutRef<typeof Primitive.div>,
23
23
  "children"
24
24
  > & {
25
- /**
26
- * Render function that receives the filtered items and returns
27
- * the UI. A render-function pattern is used here (instead of a
28
- * `components` prop) to give consumers full control over list layout,
29
- * ordering, grouping, and empty states.
30
- */
31
- children: (items: readonly Unstable_MentionItem[]) => ReactNode;
25
+ children: (items: readonly Unstable_TriggerItem[]) => ReactNode;
32
26
  };
33
27
  }
34
28
 
35
- export const ComposerPrimitiveMentionItems = forwardRef<
36
- ComposerPrimitiveMentionItems.Element,
37
- ComposerPrimitiveMentionItems.Props
38
- >(({ children, ...props }, forwardedRef) => {
39
- const { items, activeCategoryId, isSearchMode } = useMentionContext();
29
+ /**
30
+ * Renders the list of items within a category or search results via a render function.
31
+ * Only renders when a category is active or search mode is on.
32
+ */
33
+ export const ComposerPrimitiveTriggerPopoverItems = forwardRef<
34
+ ComposerPrimitiveTriggerPopoverItems.Element,
35
+ ComposerPrimitiveTriggerPopoverItems.Props
36
+ >(({ children, "aria-label": ariaLabel, ...props }, forwardedRef) => {
37
+ const { items, activeCategoryId, isSearchMode } = useTriggerPopoverContext();
40
38
 
41
39
  if (!activeCategoryId && !isSearchMode) return null;
42
40
 
43
41
  return (
44
- <Primitive.div role="group" {...props} ref={forwardedRef}>
42
+ <Primitive.div
43
+ role="group"
44
+ aria-label={ariaLabel ?? "Items"}
45
+ {...props}
46
+ ref={forwardedRef}
47
+ >
45
48
  {children(items)}
46
49
  </Primitive.div>
47
50
  );
48
51
  });
49
52
 
50
- ComposerPrimitiveMentionItems.displayName = "ComposerPrimitive.MentionItems";
53
+ ComposerPrimitiveTriggerPopoverItems.displayName =
54
+ "ComposerPrimitive.TriggerPopoverItems";
51
55
 
52
56
  // =============================================================================
53
- // MentionItem — A single selectable mention item
57
+ // TriggerPopoverItem — A single selectable item
54
58
  // =============================================================================
55
59
 
56
- export namespace ComposerPrimitiveMentionItem {
60
+ export namespace ComposerPrimitiveTriggerPopoverItem {
57
61
  export type Element = ComponentRef<typeof Primitive.button>;
58
62
  export type Props = ComponentPropsWithoutRef<typeof Primitive.button> & {
59
- item: Unstable_MentionItem;
60
- /** Position in the items list. Used for keyboard highlight matching. Falls back to findIndex by id. */
63
+ item: Unstable_TriggerItem;
61
64
  index?: number | undefined;
62
65
  };
63
66
  }
64
67
 
65
68
  /**
66
- * A button that inserts the mention item into the composer.
69
+ * A button that selects a trigger item.
67
70
  * Automatically receives `data-highlighted` when keyboard-navigated.
68
71
  */
69
- export const ComposerPrimitiveMentionItem = forwardRef<
70
- ComposerPrimitiveMentionItem.Element,
71
- ComposerPrimitiveMentionItem.Props
72
+ export const ComposerPrimitiveTriggerPopoverItem = forwardRef<
73
+ ComposerPrimitiveTriggerPopoverItem.Element,
74
+ ComposerPrimitiveTriggerPopoverItem.Props
72
75
  >(({ item, index: indexProp, onClick, ...props }, forwardedRef) => {
73
76
  const {
74
77
  selectItem,
@@ -76,13 +79,13 @@ export const ComposerPrimitiveMentionItem = forwardRef<
76
79
  highlightedIndex,
77
80
  activeCategoryId,
78
81
  isSearchMode,
79
- } = useMentionContext();
82
+ popoverId,
83
+ } = useTriggerPopoverContext();
80
84
 
81
85
  const handleClick = useCallback(() => {
82
86
  selectItem(item);
83
87
  }, [selectItem, item]);
84
88
 
85
- // Use explicit index prop if provided, fall back to findIndex
86
89
  const itemIndex = indexProp ?? items.findIndex((i) => i.id === item.id);
87
90
  const isHighlighted =
88
91
  (isSearchMode || activeCategoryId !== null) &&
@@ -92,6 +95,7 @@ export const ComposerPrimitiveMentionItem = forwardRef<
92
95
  <Primitive.button
93
96
  type="button"
94
97
  role="option"
98
+ id={`${popoverId}-option-${item.id}`}
95
99
  aria-selected={isHighlighted}
96
100
  data-highlighted={isHighlighted ? "" : undefined}
97
101
  {...props}
@@ -101,4 +105,5 @@ export const ComposerPrimitiveMentionItem = forwardRef<
101
105
  );
102
106
  });
103
107
 
104
- ComposerPrimitiveMentionItem.displayName = "ComposerPrimitive.MentionItem";
108
+ ComposerPrimitiveTriggerPopoverItem.displayName =
109
+ "ComposerPrimitive.TriggerPopoverItem";
@@ -0,0 +1,51 @@
1
+ "use client";
2
+
3
+ import { Primitive } from "../../../utils/Primitive";
4
+ import {
5
+ type ComponentRef,
6
+ type ComponentPropsWithoutRef,
7
+ forwardRef,
8
+ } from "react";
9
+ import { useTriggerPopoverContext } from "./TriggerPopoverContext";
10
+
11
+ export namespace ComposerPrimitiveTriggerPopoverPopover {
12
+ export type Element = ComponentRef<typeof Primitive.div>;
13
+ export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;
14
+ }
15
+
16
+ /**
17
+ * Renders a container for the trigger popover.
18
+ * Only renders when a trigger character is detected in the composer text.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <ComposerPrimitive.Unstable_TriggerPopoverRoot trigger="/" adapter={adapter} onSelect={onSelect}>
23
+ * <ComposerPrimitive.Input />
24
+ * <ComposerPrimitive.Unstable_TriggerPopoverPopover>
25
+ * <ComposerPrimitive.Unstable_TriggerPopoverCategories />
26
+ * </ComposerPrimitive.Unstable_TriggerPopoverPopover>
27
+ * </ComposerPrimitive.Unstable_TriggerPopoverRoot>
28
+ * ```
29
+ */
30
+ export const ComposerPrimitiveTriggerPopoverPopover = forwardRef<
31
+ ComposerPrimitiveTriggerPopoverPopover.Element,
32
+ ComposerPrimitiveTriggerPopoverPopover.Props
33
+ >(({ "aria-label": ariaLabel, ...props }, forwardedRef) => {
34
+ const { open, popoverId, highlightedItemId } = useTriggerPopoverContext();
35
+ if (!open) return null;
36
+
37
+ return (
38
+ <Primitive.div
39
+ role="listbox"
40
+ id={popoverId}
41
+ aria-label={ariaLabel ?? "Suggestions"}
42
+ aria-activedescendant={highlightedItemId}
43
+ data-state="open"
44
+ {...props}
45
+ ref={forwardedRef}
46
+ />
47
+ );
48
+ });
49
+
50
+ ComposerPrimitiveTriggerPopoverPopover.displayName =
51
+ "ComposerPrimitive.TriggerPopoverPopover";