@assistant-ui/react 0.14.14 → 0.14.15

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 (207) hide show
  1. package/README.md +5 -1
  2. package/dist/client/ExternalThread.d.ts +2 -12
  3. package/dist/client/ExternalThread.d.ts.map +1 -1
  4. package/dist/client/ExternalThread.js +30 -29
  5. package/dist/client/ExternalThread.js.map +1 -1
  6. package/dist/client/InMemoryThreadList.d.ts.map +1 -1
  7. package/dist/client/InMemoryThreadList.js +11 -10
  8. package/dist/client/InMemoryThreadList.js.map +1 -1
  9. package/dist/client/SingleThreadList.d.ts.map +1 -1
  10. package/dist/client/SingleThreadList.js +9 -8
  11. package/dist/client/SingleThreadList.js.map +1 -1
  12. package/dist/context/providers/ThreadViewportProvider.js +1 -1
  13. package/dist/context/providers/ThreadViewportProvider.js.map +1 -1
  14. package/dist/context/react/ThreadViewportContext.js +1 -1
  15. package/dist/context/react/utils/createContextHook.js +1 -1
  16. package/dist/context/react/utils/ensureBinding.js.map +1 -1
  17. package/dist/context/react/utils/useRuntimeState.js +1 -1
  18. package/dist/context/stores/ThreadViewport.js.map +1 -1
  19. package/dist/devtools/DevToolsHooks.js.map +1 -1
  20. package/dist/index.d.ts +4 -4
  21. package/dist/index.js +3 -3
  22. package/dist/legacy-runtime/AssistantRuntimeProvider.js +1 -1
  23. package/dist/legacy-runtime/cloud/auiV0.js +1 -1
  24. package/dist/legacy-runtime/hooks/AssistantContext.js.map +1 -1
  25. package/dist/legacy-runtime/hooks/AttachmentContext.js.map +1 -1
  26. package/dist/legacy-runtime/hooks/ComposerContext.js.map +1 -1
  27. package/dist/legacy-runtime/hooks/MessageContext.js.map +1 -1
  28. package/dist/legacy-runtime/hooks/MessagePartContext.js.map +1 -1
  29. package/dist/legacy-runtime/hooks/ThreadContext.js +1 -1
  30. package/dist/legacy-runtime/hooks/ThreadContext.js.map +1 -1
  31. package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +1 -1
  32. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +1 -1
  33. package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js +1 -1
  34. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +1 -1
  35. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +1 -1
  36. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  37. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +1 -1
  38. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js +1 -1
  39. package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  40. package/dist/mcp-apps/McpAppRenderer.js +7 -7
  41. package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
  42. package/dist/mcp-apps/McpAppsRemoteHost.d.ts.map +1 -1
  43. package/dist/mcp-apps/McpAppsRemoteHost.js +5 -4
  44. package/dist/mcp-apps/McpAppsRemoteHost.js.map +1 -1
  45. package/dist/mcp-apps/app-frame.d.ts +1 -1
  46. package/dist/mcp-apps/app-frame.d.ts.map +1 -1
  47. package/dist/mcp-apps/app-frame.js +82 -104
  48. package/dist/mcp-apps/app-frame.js.map +1 -1
  49. package/dist/mcp-apps/bridge.d.ts +3 -3
  50. package/dist/mcp-apps/bridge.d.ts.map +1 -1
  51. package/dist/mcp-apps/bridge.js +35 -10
  52. package/dist/mcp-apps/bridge.js.map +1 -1
  53. package/dist/mcp-apps/types.d.ts +2 -12
  54. package/dist/mcp-apps/types.d.ts.map +1 -1
  55. package/dist/mcp-apps/types.js.map +1 -1
  56. package/dist/model-context/frame/useAssistantFrameHost.js +1 -1
  57. package/dist/model-context/makeAssistantVisible.js +1 -1
  58. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  59. package/dist/primitives/actionBar/ActionBarCopy.js +1 -1
  60. package/dist/primitives/actionBar/ActionBarExportMarkdown.js +1 -1
  61. package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
  62. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +1 -1
  63. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +1 -1
  64. package/dist/primitives/actionBar/ActionBarInteractionContext.js +1 -1
  65. package/dist/primitives/actionBar/ActionBarRoot.js +1 -1
  66. package/dist/primitives/actionBar/ActionBarStopSpeaking.js +1 -1
  67. package/dist/primitives/actionBarMore/ActionBarMoreContent.js +1 -1
  68. package/dist/primitives/actionBarMore/ActionBarMoreItem.js +1 -1
  69. package/dist/primitives/actionBarMore/ActionBarMoreRoot.js +1 -1
  70. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js +1 -1
  71. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js +1 -1
  72. package/dist/primitives/assistantModal/AssistantModalAnchor.js +1 -1
  73. package/dist/primitives/assistantModal/AssistantModalContent.js +1 -1
  74. package/dist/primitives/assistantModal/AssistantModalRoot.js +1 -1
  75. package/dist/primitives/assistantModal/AssistantModalTrigger.js +1 -1
  76. package/dist/primitives/attachment/AttachmentRemove.js +1 -1
  77. package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
  78. package/dist/primitives/attachment/AttachmentRoot.js +1 -1
  79. package/dist/primitives/attachment/AttachmentThumb.js +1 -1
  80. package/dist/primitives/branchPicker/BranchPickerRoot.js +1 -1
  81. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +1 -1
  82. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
  83. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js +1 -1
  84. package/dist/primitives/composer/ComposerAddAttachment.js +1 -1
  85. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
  86. package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -1
  87. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  88. package/dist/primitives/composer/ComposerDictationTranscript.js +1 -1
  89. package/dist/primitives/composer/ComposerInput.js +1 -1
  90. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  91. package/dist/primitives/composer/ComposerInputPluginContext.js +1 -1
  92. package/dist/primitives/composer/ComposerQuote.js +1 -1
  93. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  94. package/dist/primitives/composer/ComposerRoot.js +1 -1
  95. package/dist/primitives/composer/ComposerSend.js +1 -1
  96. package/dist/primitives/composer/ComposerStopDictation.js +1 -1
  97. package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
  98. package/dist/primitives/composer/trigger/TriggerPopover.js +2 -2
  99. package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
  100. package/dist/primitives/composer/trigger/TriggerPopoverAction.js +1 -1
  101. package/dist/primitives/composer/trigger/TriggerPopoverBack.js +1 -1
  102. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js +1 -1
  103. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js +1 -1
  104. package/dist/primitives/composer/trigger/TriggerPopoverItems.js +1 -1
  105. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -1
  106. package/dist/primitives/composer/trigger/TriggerPopoverResource.js +8 -7
  107. package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -1
  108. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js +1 -1
  109. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -1
  110. package/dist/primitives/composer/trigger/triggerDetectionResource.js +5 -4
  111. package/dist/primitives/composer/trigger/triggerDetectionResource.js.map +1 -1
  112. package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts.map +1 -1
  113. package/dist/primitives/composer/trigger/triggerKeyboardResource.js +8 -7
  114. package/dist/primitives/composer/trigger/triggerKeyboardResource.js.map +1 -1
  115. package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts.map +1 -1
  116. package/dist/primitives/composer/trigger/triggerNavigationResource.js +13 -12
  117. package/dist/primitives/composer/trigger/triggerNavigationResource.js.map +1 -1
  118. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  119. package/dist/primitives/composer/trigger/triggerSelectionResource.js +7 -6
  120. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
  121. package/dist/primitives/error/ErrorMessage.js +1 -1
  122. package/dist/primitives/error/ErrorRoot.js +1 -1
  123. package/dist/primitives/message/MessagePartsGrouped.js +1 -1
  124. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  125. package/dist/primitives/message/MessageRoot.js +1 -1
  126. package/dist/primitives/message/MessageRoot.js.map +1 -1
  127. package/dist/primitives/messagePart/MessagePartImage.js +1 -1
  128. package/dist/primitives/messagePart/MessagePartText.js +1 -1
  129. package/dist/primitives/queueItem/QueueItemRemove.js +1 -1
  130. package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
  131. package/dist/primitives/queueItem/QueueItemSteer.js +1 -1
  132. package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
  133. package/dist/primitives/queueItem/QueueItemText.js +1 -1
  134. package/dist/primitives/reasoning/useScrollLock.js +1 -1
  135. package/dist/primitives/reasoning/useScrollLock.js.map +1 -1
  136. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +1 -1
  137. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
  138. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +1 -1
  139. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
  140. package/dist/primitives/suggestion/SuggestionDescription.js +1 -1
  141. package/dist/primitives/suggestion/SuggestionTitle.js +1 -1
  142. package/dist/primitives/suggestion/SuggestionTrigger.js +1 -1
  143. package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
  144. package/dist/primitives/thread/ThreadRoot.js +1 -1
  145. package/dist/primitives/thread/ThreadScrollToBottom.js +1 -1
  146. package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
  147. package/dist/primitives/thread/ThreadViewport.js +1 -1
  148. package/dist/primitives/thread/ThreadViewport.js.map +1 -1
  149. package/dist/primitives/thread/ThreadViewportFooter.js +1 -1
  150. package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -1
  151. package/dist/primitives/thread/topAnchor/topAnchorTurn.js.map +1 -1
  152. package/dist/primitives/thread/topAnchor/topAnchorUtils.js.map +1 -1
  153. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js +1 -1
  154. package/dist/primitives/thread/useThreadViewportAutoScroll.js +1 -1
  155. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  156. package/dist/primitives/threadList/ThreadListNew.js +1 -1
  157. package/dist/primitives/threadList/ThreadListRoot.js +1 -1
  158. package/dist/primitives/threadListItem/ThreadListItemRoot.js +1 -1
  159. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js +1 -1
  160. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js +1 -1
  161. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js +1 -1
  162. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js +1 -1
  163. package/dist/sandbox-host/SandboxHost.d.ts +50 -0
  164. package/dist/sandbox-host/SandboxHost.d.ts.map +1 -0
  165. package/dist/sandbox-host/SandboxHost.js +85 -0
  166. package/dist/sandbox-host/SandboxHost.js.map +1 -0
  167. package/dist/unstable/useMentionAdapter.js +1 -1
  168. package/dist/unstable/useMentionAdapter.js.map +1 -1
  169. package/dist/unstable/useSlashCommandAdapter.js +1 -1
  170. package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
  171. package/dist/utils/Primitive.js +1 -1
  172. package/dist/utils/createActionButton.js +1 -1
  173. package/dist/utils/createActionButton.js.map +1 -1
  174. package/dist/utils/hooks/useManagedRef.js +1 -1
  175. package/dist/utils/hooks/useMediaQuery.js +1 -1
  176. package/dist/utils/hooks/useMediaQuery.js.map +1 -1
  177. package/dist/utils/hooks/useOnResizeContent.js +1 -1
  178. package/dist/utils/hooks/useOnScrollToBottom.js +1 -1
  179. package/dist/utils/hooks/useSizeHandle.js +1 -1
  180. package/dist/utils/json/is-json.js.map +1 -1
  181. package/dist/utils/smooth/SmoothContext.js +1 -1
  182. package/dist/utils/smooth/SmoothContext.js.map +1 -1
  183. package/dist/utils/smooth/useSmooth.js +1 -1
  184. package/dist/utils/smooth/useSmooth.js.map +1 -1
  185. package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
  186. package/dist/utils/useToolArgsFieldStatus.d.ts.map +1 -1
  187. package/package.json +21 -20
  188. package/src/client/ExternalThread.ts +484 -515
  189. package/src/client/InMemoryThreadList.ts +153 -162
  190. package/src/client/SingleThreadList.ts +87 -84
  191. package/src/context/providers/ThreadViewportProvider.tsx +2 -2
  192. package/src/index.ts +8 -1
  193. package/src/mcp-apps/McpAppRenderer.tsx +28 -35
  194. package/src/mcp-apps/McpAppsRemoteHost.ts +25 -24
  195. package/src/mcp-apps/app-frame.tsx +100 -141
  196. package/src/mcp-apps/bridge.test.ts +100 -60
  197. package/src/mcp-apps/bridge.ts +43 -21
  198. package/src/mcp-apps/types.ts +2 -12
  199. package/src/primitives/composer/trigger/TriggerPopover.tsx +1 -1
  200. package/src/primitives/composer/trigger/TriggerPopoverResource.ts +75 -76
  201. package/src/primitives/composer/trigger/triggerDetectionResource.ts +6 -5
  202. package/src/primitives/composer/trigger/triggerKeyboardResource.ts +9 -13
  203. package/src/primitives/composer/trigger/triggerNavigationResource.ts +14 -19
  204. package/src/primitives/composer/trigger/triggerSelectionResource.ts +8 -7
  205. package/src/sandbox-host/SandboxHost.test.tsx +231 -0
  206. package/src/sandbox-host/SandboxHost.tsx +185 -0
  207. package/src/tests/local-runtime-queue.test.tsx +305 -0
@@ -1,4 +1,4 @@
1
- import type { RenderedFrame } from "safe-content-frame";
1
+ import type { SandboxHostFrame } from "../sandbox-host/SandboxHost";
2
2
  import {
3
3
  MCP_APP_PROTOCOL_VERSION,
4
4
  type McpAppBridgeHandlers,
@@ -10,6 +10,7 @@ import {
10
10
  type McpAppJsonRpcRequest,
11
11
  type McpAppJsonRpcResponse,
12
12
  } from "./types";
13
+ import { isRecord } from "../utils/json/is-json";
13
14
 
14
15
  const VALID_DISPLAY_MODES = [
15
16
  "inline",
@@ -17,20 +18,17 @@ const VALID_DISPLAY_MODES = [
17
18
  "pip",
18
19
  ] as const satisfies readonly McpAppDisplayMode[];
19
20
 
20
- export type McpAppBridgeFrame = Pick<
21
- RenderedFrame,
22
- "iframe" | "origin" | "sendMessage"
23
- >;
21
+ export type McpAppBridgeFrame = SandboxHostFrame;
24
22
 
25
23
  export type CreateMcpAppBridgeOptions = {
26
24
  frame: McpAppBridgeFrame;
27
25
  handlers?: McpAppBridgeHandlers | undefined;
28
26
  hostInfo?: McpAppHostInfo | undefined;
29
27
  hostContext?: McpAppHostContext | undefined;
30
- targetWindow?: Window | undefined;
31
28
  };
32
29
 
33
30
  export type McpAppBridge = {
31
+ onMessage: (event: MessageEvent) => void;
34
32
  dispose: () => void;
35
33
  notifyToolInput: (input: unknown) => void;
36
34
  notifyToolResult: (result: unknown) => void;
@@ -90,13 +88,8 @@ export function createMcpAppBridge(
90
88
  handlers = {},
91
89
  hostInfo = DEFAULT_HOST_INFO,
92
90
  hostContext = {},
93
- targetWindow = typeof window !== "undefined" ? window : undefined,
94
91
  } = opts;
95
92
 
96
- if (!targetWindow) {
97
- throw new Error("createMcpAppBridge requires a window context");
98
- }
99
-
100
93
  const post = (msg: McpAppJsonRpcMessage) => {
101
94
  frame.sendMessage(msg);
102
95
  };
@@ -136,10 +129,15 @@ export function createMcpAppBridge(
136
129
 
137
130
  switch (normalizeMethod(req.method)) {
138
131
  case "ui/initialize": {
132
+ const requestedProtocolVersion =
133
+ isRecord(params) && typeof params.protocolVersion === "string"
134
+ ? params.protocolVersion
135
+ : MCP_APP_PROTOCOL_VERSION;
139
136
  respond(req.id, {
140
137
  result: {
141
- protocolVersion: MCP_APP_PROTOCOL_VERSION,
138
+ protocolVersion: requestedProtocolVersion,
142
139
  host: hostInfo,
140
+ hostInfo,
143
141
  hostContext,
144
142
  capabilities: {
145
143
  tools: handlers.callTool ? {} : undefined,
@@ -154,6 +152,18 @@ export function createMcpAppBridge(
154
152
  updateModelContext: !!handlers.updateModelContext,
155
153
  },
156
154
  },
155
+ hostCapabilities: {
156
+ ...(handlers.openLink ? { openLinks: {} } : {}),
157
+ ...(handlers.callTool ? { serverTools: {} } : {}),
158
+ ...(handlers.readResource || handlers.listResources
159
+ ? { serverResources: {} }
160
+ : {}),
161
+ ...(handlers.updateModelContext
162
+ ? { updateModelContext: { text: {} } }
163
+ : {}),
164
+ ...(handlers.sendMessage ? { message: { text: {} } } : {}),
165
+ ...(handlers.onLog ? { logging: {} } : {}),
166
+ },
157
167
  },
158
168
  });
159
169
  return;
@@ -405,11 +415,9 @@ export function createMcpAppBridge(
405
415
  }
406
416
  };
407
417
 
408
- // Cross-origin guard: ignore any postMessage not originating from this
409
- // app's iframe contentWindow at the SafeContentFrame-issued origin.
418
+ // The host applies the cross-origin guard before delegating; this only
419
+ // validates the JSON-RPC envelope.
410
420
  const onMessage = (event: MessageEvent) => {
411
- if (event.source !== frame.iframe.contentWindow) return;
412
- if (event.origin !== frame.origin) return;
413
421
  if (!isJsonRpcMessage(event.data)) return;
414
422
 
415
423
  const msg = event.data;
@@ -420,18 +428,20 @@ export function createMcpAppBridge(
420
428
  }
421
429
  };
422
430
 
423
- targetWindow.addEventListener("message", onMessage);
424
-
425
431
  return {
426
- dispose: () => {
427
- targetWindow.removeEventListener("message", onMessage);
428
- },
432
+ onMessage,
433
+ dispose: () => {},
429
434
  notifyToolInput: (input: unknown) => {
430
435
  post({
431
436
  jsonrpc: "2.0",
432
437
  method: "notifications/tools/call/input",
433
438
  params: { input },
434
439
  });
440
+ post({
441
+ jsonrpc: "2.0",
442
+ method: "ui/notifications/tool-input",
443
+ params: isRecord(input) ? { arguments: input } : {},
444
+ });
435
445
  },
436
446
  notifyToolResult: (result: unknown) => {
437
447
  post({
@@ -439,6 +449,13 @@ export function createMcpAppBridge(
439
449
  method: "notifications/tools/call/result",
440
450
  params: { result },
441
451
  });
452
+ post({
453
+ jsonrpc: "2.0",
454
+ method: "ui/notifications/tool-result",
455
+ params: isRecord(result)
456
+ ? result
457
+ : { content: [{ type: "text", text: String(result) }] },
458
+ });
442
459
  },
443
460
  notifyHostContextChanged: (ctx: McpAppHostContext) => {
444
461
  post({
@@ -446,6 +463,11 @@ export function createMcpAppBridge(
446
463
  method: "notifications/host_context/changed",
447
464
  params: ctx,
448
465
  });
466
+ post({
467
+ jsonrpc: "2.0",
468
+ method: "ui/notifications/host-context-changed",
469
+ params: ctx,
470
+ });
449
471
  },
450
472
  };
451
473
  }
@@ -1,9 +1,8 @@
1
- import type { CSSProperties } from "react";
2
1
  import type {
3
2
  McpAppMetadata,
4
3
  ToolCallMessagePartMcpMetadata,
5
4
  } from "@assistant-ui/core";
6
- import type { SandboxOption } from "safe-content-frame";
5
+ import type { SandboxHostConfig } from "../sandbox-host/SandboxHost";
7
6
 
8
7
  export type { McpAppMetadata, ToolCallMessagePartMcpMetadata };
9
8
 
@@ -97,16 +96,7 @@ export type McpAppBridgeHandlers = {
97
96
  onError?: (error: Error) => void;
98
97
  };
99
98
 
100
- export type McpAppSandboxConfig = {
101
- sandbox?: SandboxOption[];
102
- useShadowDom?: boolean;
103
- enableBrowserCaching?: boolean;
104
- salt?: string;
105
- product?: string;
106
- className?: string;
107
- style?: CSSProperties;
108
- unsafeDocumentWrite?: boolean;
109
- };
99
+ export type McpAppSandboxConfig = SandboxHostConfig;
110
100
 
111
101
  export type McpAppFrameProps = {
112
102
  app: McpAppMetadata;
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useAui, useAuiState } from "@assistant-ui/store";
4
- import { useResource } from "@assistant-ui/tap/react";
4
+ import { useResource } from "@assistant-ui/tap";
5
5
  import type { Unstable_TriggerAdapter } from "@assistant-ui/core";
6
6
  import {
7
7
  createContext,
@@ -1,4 +1,5 @@
1
- import { resource, tapEffectEvent, tapResource } from "@assistant-ui/tap";
1
+ import { useEffectEvent } from "react";
2
+ import { useResource, resource } from "@assistant-ui/tap";
2
3
  import type {
3
4
  Unstable_TriggerAdapter,
4
5
  Unstable_TriggerCategory,
@@ -50,88 +51,86 @@ export type TriggerPopoverResourceOutput = {
50
51
  };
51
52
 
52
53
  /** Composes detection, navigation, keyboard, and selection sub-resources. */
53
- export const TriggerPopoverResource = resource(
54
- ({
55
- adapter,
56
- text,
57
- triggerChar,
58
- behavior,
59
- aui,
60
- popoverId,
61
- }: {
62
- adapter: Unstable_TriggerAdapter | undefined;
63
- text: string;
64
- triggerChar: string;
65
- behavior: TriggerBehavior | undefined;
66
- aui: AssistantClient;
67
- /** Stable ID for accessible element IDs (pass React's useId() from component layer). */
68
- popoverId: string;
69
- }): TriggerPopoverResourceOutput => {
70
- const detection = tapResource(
71
- TriggerDetectionResource({ text, triggerChar }),
72
- );
73
-
74
- const open =
75
- detection.trigger !== null &&
76
- adapter !== undefined &&
77
- behavior !== undefined;
54
+ export const TriggerPopoverResource = resource(function TriggerPopoverResource({
55
+ adapter,
56
+ text,
57
+ triggerChar,
58
+ behavior,
59
+ aui,
60
+ popoverId,
61
+ }: {
62
+ adapter: Unstable_TriggerAdapter | undefined;
63
+ text: string;
64
+ triggerChar: string;
65
+ behavior: TriggerBehavior | undefined;
66
+ aui: AssistantClient;
67
+ /** Stable ID for accessible element IDs (pass React's useId() from component layer). */
68
+ popoverId: string;
69
+ }): TriggerPopoverResourceOutput {
70
+ const detection = useResource(
71
+ TriggerDetectionResource({ text, triggerChar }),
72
+ );
78
73
 
79
- const navigation = tapResource(
80
- TriggerNavigationResource({
81
- adapter,
82
- query: detection.query,
83
- open,
84
- }),
85
- );
74
+ const open =
75
+ detection.trigger !== null &&
76
+ adapter !== undefined &&
77
+ behavior !== undefined;
86
78
 
87
- const onSelected = tapEffectEvent(() => {
88
- navigation.goBack();
89
- });
79
+ const navigation = useResource(
80
+ TriggerNavigationResource({
81
+ adapter,
82
+ query: detection.query,
83
+ open,
84
+ }),
85
+ );
90
86
 
91
- const selection = tapResource(
92
- TriggerSelectionResource({
93
- behavior,
94
- trigger: detection.trigger,
95
- aui,
96
- triggerChar,
97
- setCursorPosition: detection.setCursorPosition,
98
- onSelected,
99
- }),
100
- );
87
+ const onSelected = useEffectEvent(() => {
88
+ navigation.goBack();
89
+ });
101
90
 
102
- const keyboard = tapResource(
103
- TriggerKeyboardResource({
104
- navigableList: navigation.navigableList,
105
- isSearchMode: navigation.isSearchMode,
106
- activeCategoryId: navigation.activeCategoryId,
107
- query: detection.query,
108
- popoverId,
109
- open,
110
- selectItem: selection.selectItem,
111
- selectCategory: navigation.selectCategory,
112
- goBack: navigation.goBack,
113
- close: selection.close,
114
- }),
115
- );
91
+ const selection = useResource(
92
+ TriggerSelectionResource({
93
+ behavior,
94
+ trigger: detection.trigger,
95
+ aui,
96
+ triggerChar,
97
+ setCursorPosition: detection.setCursorPosition,
98
+ onSelected,
99
+ }),
100
+ );
116
101
 
117
- return {
118
- open,
119
- query: detection.query,
120
- activeCategoryId: navigation.activeCategoryId,
121
- categories: navigation.categories,
122
- items: navigation.items,
123
- highlightedIndex: keyboard.highlightedIndex,
102
+ const keyboard = useResource(
103
+ TriggerKeyboardResource({
104
+ navigableList: navigation.navigableList,
124
105
  isSearchMode: navigation.isSearchMode,
106
+ activeCategoryId: navigation.activeCategoryId,
107
+ query: detection.query,
125
108
  popoverId,
126
- highlightedItemId: keyboard.highlightedItemId,
109
+ open,
110
+ selectItem: selection.selectItem,
127
111
  selectCategory: navigation.selectCategory,
128
112
  goBack: navigation.goBack,
129
- selectItem: selection.selectItem,
130
113
  close: selection.close,
131
- highlightIndex: keyboard.highlightIndex,
132
- handleKeyDown: keyboard.handleKeyDown,
133
- setCursorPosition: detection.setCursorPosition,
134
- registerSelectItemOverride: selection.registerSelectItemOverride,
135
- };
136
- },
137
- );
114
+ }),
115
+ );
116
+
117
+ return {
118
+ open,
119
+ query: detection.query,
120
+ activeCategoryId: navigation.activeCategoryId,
121
+ categories: navigation.categories,
122
+ items: navigation.items,
123
+ highlightedIndex: keyboard.highlightedIndex,
124
+ isSearchMode: navigation.isSearchMode,
125
+ popoverId,
126
+ highlightedItemId: keyboard.highlightedItemId,
127
+ selectCategory: navigation.selectCategory,
128
+ goBack: navigation.goBack,
129
+ selectItem: selection.selectItem,
130
+ close: selection.close,
131
+ highlightIndex: keyboard.highlightIndex,
132
+ handleKeyDown: keyboard.handleKeyDown,
133
+ setCursorPosition: detection.setCursorPosition,
134
+ registerSelectItemOverride: selection.registerSelectItemOverride,
135
+ };
136
+ });
@@ -1,4 +1,5 @@
1
- import { resource, tapMemo, tapState } from "@assistant-ui/tap";
1
+ import { useMemo, useState } from "react";
2
+ import { resource } from "@assistant-ui/tap";
2
3
  import { detectTrigger } from "./detectTrigger";
3
4
 
4
5
  /** Detected trigger position within the composer text. */
@@ -18,16 +19,16 @@ export type TriggerDetectionResourceOutput = {
18
19
 
19
20
  /** Tracks cursor position and derives the active trigger + query from composer text. */
20
21
  export const TriggerDetectionResource = resource(
21
- ({
22
+ function TriggerDetectionResource({
22
23
  text,
23
24
  triggerChar,
24
25
  }: {
25
26
  text: string;
26
27
  triggerChar: string;
27
- }): TriggerDetectionResourceOutput => {
28
- const [cursorPosition, setCursorPosition] = tapState(text.length);
28
+ }): TriggerDetectionResourceOutput {
29
+ const [cursorPosition, setCursorPosition] = useState(text.length);
29
30
 
30
- const trigger = tapMemo(() => {
31
+ const trigger = useMemo(() => {
31
32
  const pos = Math.min(cursorPosition, text.length);
32
33
  return detectTrigger(text, triggerChar, pos);
33
34
  }, [cursorPosition, text, triggerChar]);
@@ -1,9 +1,5 @@
1
- import {
2
- resource,
3
- tapEffect,
4
- tapEffectEvent,
5
- tapState,
6
- } from "@assistant-ui/tap";
1
+ import { useEffect, useEffectEvent, useState } from "react";
2
+ import { resource } from "@assistant-ui/tap";
7
3
  import type {
8
4
  Unstable_TriggerCategory,
9
5
  Unstable_TriggerItem,
@@ -39,7 +35,7 @@ export type TriggerKeyboardResourceOutput = {
39
35
  * category drill-in, back, and close to the callbacks supplied by the parent.
40
36
  */
41
37
  export const TriggerKeyboardResource = resource(
42
- ({
38
+ function TriggerKeyboardResource({
43
39
  navigableList,
44
40
  isSearchMode,
45
41
  activeCategoryId,
@@ -61,24 +57,24 @@ export const TriggerKeyboardResource = resource(
61
57
  selectCategory: (categoryId: string) => void;
62
58
  goBack: () => void;
63
59
  close: () => void;
64
- }): TriggerKeyboardResourceOutput => {
65
- const [highlightedIndex, setHighlightedIndex] = tapState(0);
60
+ }): TriggerKeyboardResourceOutput {
61
+ const [highlightedIndex, setHighlightedIndex] = useState(0);
66
62
 
67
- tapEffect(() => {
63
+ useEffect(() => {
68
64
  setHighlightedIndex(0);
69
65
  }, [navigableList]);
70
66
 
71
- tapEffect(() => {
67
+ useEffect(() => {
72
68
  setHighlightedIndex(0);
73
69
  }, [isSearchMode, activeCategoryId]);
74
70
 
75
- const highlightIndex = tapEffectEvent((index: number) => {
71
+ const highlightIndex = useEffectEvent((index: number) => {
76
72
  if (index < 0 || index >= navigableList.length) return;
77
73
  if (index === highlightedIndex) return;
78
74
  setHighlightedIndex(index);
79
75
  });
80
76
 
81
- const handleKeyDown = tapEffectEvent(
77
+ const handleKeyDown = useEffectEvent(
82
78
  (e: TriggerPopoverKeyEvent): boolean => {
83
79
  if (!open) return false;
84
80
 
@@ -1,10 +1,5 @@
1
- import {
2
- resource,
3
- tapEffect,
4
- tapEffectEvent,
5
- tapMemo,
6
- tapState,
7
- } from "@assistant-ui/tap";
1
+ import { useEffect, useEffectEvent, useMemo, useState } from "react";
2
+ import { resource } from "@assistant-ui/tap";
8
3
  import type {
9
4
  Unstable_TriggerAdapter,
10
5
  Unstable_TriggerCategory,
@@ -44,7 +39,7 @@ export type TriggerNavigationResourceOutput = {
44
39
  * adapter + current query. Pure derivation — no side effects on the composer.
45
40
  */
46
41
  export const TriggerNavigationResource = resource(
47
- ({
42
+ function TriggerNavigationResource({
48
43
  adapter,
49
44
  query,
50
45
  open,
@@ -52,28 +47,28 @@ export const TriggerNavigationResource = resource(
52
47
  adapter: Unstable_TriggerAdapter | undefined;
53
48
  query: string;
54
49
  open: boolean;
55
- }): TriggerNavigationResourceOutput => {
56
- const [activeCategoryId, setActiveCategoryId] = tapState<string | null>(
50
+ }): TriggerNavigationResourceOutput {
51
+ const [activeCategoryId, setActiveCategoryId] = useState<string | null>(
57
52
  null,
58
53
  );
59
54
 
60
- tapEffect(() => {
55
+ useEffect(() => {
61
56
  if (!open) setActiveCategoryId(null);
62
57
  }, [open]);
63
58
 
64
- const categories = tapMemo<readonly Unstable_TriggerCategory[]>(() => {
59
+ const categories = useMemo<readonly Unstable_TriggerCategory[]>(() => {
65
60
  if (!open || !adapter) return [];
66
61
  return adapter.categories();
67
62
  }, [open, adapter]);
68
63
 
69
64
  const effectiveActiveCategoryId = open ? activeCategoryId : null;
70
65
 
71
- const allItems = tapMemo<readonly Unstable_TriggerItem[]>(() => {
66
+ const allItems = useMemo<readonly Unstable_TriggerItem[]>(() => {
72
67
  if (!effectiveActiveCategoryId || !adapter) return [];
73
68
  return adapter.categoryItems(effectiveActiveCategoryId);
74
69
  }, [effectiveActiveCategoryId, adapter]);
75
70
 
76
- const searchResults = tapMemo<
71
+ const searchResults = useMemo<
77
72
  readonly Unstable_TriggerItem[] | null
78
73
  >(() => {
79
74
  if (!open || !adapter || effectiveActiveCategoryId) return null;
@@ -96,7 +91,7 @@ export const TriggerNavigationResource = resource(
96
91
 
97
92
  const isSearchMode = searchResults !== null;
98
93
 
99
- const filteredCategories = tapMemo(() => {
94
+ const filteredCategories = useMemo(() => {
100
95
  if (isSearchMode) return [];
101
96
  if (!query) return categories;
102
97
  const lower = query.toLowerCase();
@@ -105,14 +100,14 @@ export const TriggerNavigationResource = resource(
105
100
  );
106
101
  }, [categories, query, isSearchMode]);
107
102
 
108
- const filteredItems = tapMemo(() => {
103
+ const filteredItems = useMemo(() => {
109
104
  if (isSearchMode) return searchResults ?? [];
110
105
  if (!query) return allItems;
111
106
  const lower = query.toLowerCase();
112
107
  return allItems.filter((item) => matchesQuery(item, lower));
113
108
  }, [allItems, query, isSearchMode, searchResults]);
114
109
 
115
- const navigableList = tapMemo(() => {
110
+ const navigableList = useMemo(() => {
116
111
  if (isSearchMode) return searchResults ?? [];
117
112
  if (effectiveActiveCategoryId) return filteredItems;
118
113
  return filteredCategories;
@@ -124,11 +119,11 @@ export const TriggerNavigationResource = resource(
124
119
  filteredCategories,
125
120
  ]);
126
121
 
127
- const selectCategory = tapEffectEvent((categoryId: string) => {
122
+ const selectCategory = useEffectEvent((categoryId: string) => {
128
123
  setActiveCategoryId(categoryId);
129
124
  });
130
125
 
131
- const goBack = tapEffectEvent(() => {
126
+ const goBack = useEffectEvent(() => {
132
127
  setActiveCategoryId(null);
133
128
  });
134
129
 
@@ -1,4 +1,5 @@
1
- import { resource, tapEffectEvent, tapRef } from "@assistant-ui/tap";
1
+ import { useEffectEvent, useRef } from "react";
2
+ import { resource } from "@assistant-ui/tap";
2
3
  import type {
3
4
  Unstable_DirectiveFormatter,
4
5
  Unstable_TriggerItem,
@@ -33,7 +34,7 @@ export type TriggerSelectionResourceOutput = {
33
34
 
34
35
  /** Owns composer text mutation + behavior dispatch on item selection. */
35
36
  export const TriggerSelectionResource = resource(
36
- ({
37
+ function TriggerSelectionResource({
37
38
  behavior,
38
39
  trigger,
39
40
  aui,
@@ -48,12 +49,12 @@ export const TriggerSelectionResource = resource(
48
49
  setCursorPosition: (pos: number) => void;
49
50
  /** Called after a successful selection so the parent can reset nav state. */
50
51
  onSelected: () => void;
51
- }): TriggerSelectionResourceOutput => {
52
+ }): TriggerSelectionResourceOutput {
52
53
  // Select-item override: lets Lexical's DirectivePlugin intercept selection
53
54
  // and drive its own node insertion.
54
- const selectItemOverrideRef = tapRef<SelectItemOverride | null>(null);
55
+ const selectItemOverrideRef = useRef<SelectItemOverride | null>(null);
55
56
 
56
- const registerSelectItemOverride = tapEffectEvent(
57
+ const registerSelectItemOverride = useEffectEvent(
57
58
  (fn: SelectItemOverride) => {
58
59
  selectItemOverrideRef.current = fn;
59
60
  return () => {
@@ -64,7 +65,7 @@ export const TriggerSelectionResource = resource(
64
65
  },
65
66
  );
66
67
 
67
- const selectItem = tapEffectEvent((item: Unstable_TriggerItem) => {
68
+ const selectItem = useEffectEvent((item: Unstable_TriggerItem) => {
68
69
  if (!trigger || !behavior) return;
69
70
 
70
71
  if (selectItemOverrideRef.current?.(item)) {
@@ -105,7 +106,7 @@ export const TriggerSelectionResource = resource(
105
106
  onSelected();
106
107
  });
107
108
 
108
- const close = tapEffectEvent(() => {
109
+ const close = useEffectEvent(() => {
109
110
  onSelected();
110
111
  // Move cursor before the trigger so trigger detection deactivates
111
112
  if (trigger) {