@assistant-ui/react 0.15.15 → 0.15.17
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.
- package/README.md +3 -3
- package/dist/devtools/DevToolsHooks.d.ts +0 -2
- package/dist/devtools/DevToolsHooks.d.ts.map +1 -1
- package/dist/devtools/DevToolsHooks.js +10 -12
- package/dist/devtools/DevToolsHooks.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -2
- package/dist/mcp-apps/McpAppRenderer.d.ts +6 -1
- package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.js +20 -13
- package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/mcp-apps/McpAppsRemoteHost.d.ts.map +1 -1
- package/dist/mcp-apps/McpAppsRemoteHost.js +20 -17
- package/dist/mcp-apps/McpAppsRemoteHost.js.map +1 -1
- package/dist/mcp-apps/app-frame.d.ts.map +1 -1
- package/dist/mcp-apps/app-frame.js +189 -94
- package/dist/mcp-apps/app-frame.js.map +1 -1
- package/dist/mcp-apps/bridge.d.ts.map +1 -1
- package/dist/mcp-apps/bridge.js +2 -8
- package/dist/mcp-apps/bridge.js.map +1 -1
- package/dist/mcp-apps/index.d.ts +2 -2
- package/dist/model-context/frame/useAssistantFrameHost.d.ts +1 -1
- package/dist/model-context/frame/useAssistantFrameHost.js +10 -11
- package/dist/model-context/frame/useAssistantFrameHost.js.map +1 -1
- package/dist/model-context/makeAssistantVisible.js +1 -1
- package/dist/model-context/makeAssistantVisible.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarCopy.js +3 -3
- package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js +7 -4
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +3 -3
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +3 -3
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js +3 -3
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreItem.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreRoot.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js.map +1 -1
- package/dist/primitives/actionBarMore/scope.d.ts +2 -2
- package/dist/primitives/actionBarMore/scope.d.ts.map +1 -1
- package/dist/primitives/actionBarMore/scope.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalAnchor.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalContent.js +1 -1
- package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalTrigger.js.map +1 -1
- package/dist/primitives/assistantModal/scope.d.ts +2 -2
- package/dist/primitives/assistantModal/scope.d.ts.map +1 -1
- package/dist/primitives/assistantModal/scope.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerRoot.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerRoot.js +20 -15
- package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/ComposerDictationTranscript.d.ts +2 -2
- package/dist/primitives/composer/ComposerDictationTranscript.js +2 -2
- package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +1 -3
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerQuote.js +5 -5
- package/dist/primitives/composer/ComposerQuote.js.map +1 -1
- package/dist/primitives/composer/ComposerRoot.js +3 -3
- package/dist/primitives/composer/ComposerRoot.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.d.ts +5 -0
- package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.js +157 -130
- package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverBack.js +3 -3
- package/dist/primitives/composer/trigger/TriggerPopoverBack.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.js +4 -4
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.js +4 -4
- package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts +2 -0
- package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverResource.js +63 -61
- package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts +2 -0
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
- package/dist/primitives/composer/trigger/detectTrigger.d.ts +21 -1
- package/dist/primitives/composer/trigger/detectTrigger.d.ts.map +1 -0
- package/dist/primitives/composer/trigger/detectTrigger.js +17 -3
- package/dist/primitives/composer/trigger/detectTrigger.js.map +1 -1
- package/dist/primitives/composer/trigger/index.d.ts +3 -1
- package/dist/primitives/composer/trigger/index.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/index.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts +3 -4
- package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerDetectionResource.js +15 -14
- package/dist/primitives/composer/trigger/triggerDetectionResource.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.js +10 -7
- package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
- package/dist/primitives/composer.d.ts +3 -2
- package/dist/primitives/composer.js.map +1 -1
- package/dist/primitives/error/ErrorMessage.js.map +1 -1
- package/dist/primitives/message/MessageIf.js.map +1 -1
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/message/MessageRoot.js +4 -4
- package/dist/primitives/message/MessageRoot.js.map +1 -1
- package/dist/primitives/message.js +1 -1
- package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +3 -3
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +3 -3
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
- package/dist/primitives/thread/ThreadIf.js.map +1 -1
- package/dist/primitives/thread/ThreadRoot.d.ts +3 -0
- package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadRoot.js +3 -0
- package/dist/primitives/thread/ThreadRoot.js.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js +3 -3
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/ThreadViewportFooter.js +3 -3
- package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/primitives/threadList/ThreadListNew.js +3 -3
- package/dist/primitives/threadList/ThreadListNew.js.map +1 -1
- package/dist/primitives/threadListFocusGroup.d.ts +2 -1
- package/dist/primitives/threadListFocusGroup.d.ts.map +1 -1
- package/dist/primitives/threadListFocusGroup.js +2 -2
- package/dist/primitives/threadListFocusGroup.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.js +3 -3
- package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.js +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js +1 -2
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js.map +1 -1
- package/dist/primitives/threadListItemMore/scope.d.ts +2 -2
- package/dist/primitives/threadListItemMore/scope.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/scope.js.map +1 -1
- package/dist/sandbox-host/SandboxHost.d.ts.map +1 -1
- package/dist/sandbox-host/SandboxHost.js +138 -68
- package/dist/sandbox-host/SandboxHost.js.map +1 -1
- package/dist/unstable/useComposerInputHistory.js +1 -1
- package/dist/unstable/useLiveCompletionAdapter.js +3 -1
- package/dist/unstable/useLiveCompletionAdapter.js.map +1 -1
- package/dist/unstable/useSlashCommandAdapter.js +41 -27
- package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
- package/dist/utils/Primitive.d.ts +2 -2
- package/dist/utils/Primitive.js +2 -2
- package/dist/utils/Primitive.js.map +1 -1
- package/dist/utils/createActionButton.js +3 -3
- package/dist/utils/createActionButton.js.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.js +1 -1
- package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
- package/dist/utils/hooks/useOnScrollToBottom.js +1 -1
- package/dist/utils/hooks/useOnScrollToBottom.js.map +1 -1
- package/dist/utils/invokeCallbackSafely.d.ts +5 -0
- package/dist/utils/invokeCallbackSafely.d.ts.map +1 -0
- package/dist/utils/invokeCallbackSafely.js +15 -0
- package/dist/utils/invokeCallbackSafely.js.map +1 -0
- package/dist/utils/smooth/SmoothContext.d.ts +30 -6
- package/dist/utils/smooth/SmoothContext.d.ts.map +1 -1
- package/dist/utils/smooth/useSmooth.d.ts +3 -0
- package/dist/utils/smooth/useSmooth.d.ts.map +1 -1
- package/dist/utils/smooth/useSmooth.js +17 -2
- package/dist/utils/smooth/useSmooth.js.map +1 -1
- package/dist/utils/useToolArgsFieldStatus.d.ts +5 -1
- package/package.json +12 -20
- package/src/devtools/DevToolsHooks.test.ts +40 -0
- package/src/devtools/DevToolsHooks.ts +10 -15
- package/src/index.ts +9 -1
- package/src/mcp-apps/McpAppRenderer.test.tsx +133 -4
- package/src/mcp-apps/McpAppRenderer.tsx +28 -21
- package/src/mcp-apps/McpAppsRemoteHost.concurrent.test.tsx +215 -0
- package/src/mcp-apps/McpAppsRemoteHost.ts +28 -12
- package/src/mcp-apps/app-frame.test.tsx +102 -0
- package/src/mcp-apps/app-frame.tsx +17 -4
- package/src/mcp-apps/bridge.test.ts +21 -0
- package/src/mcp-apps/bridge.ts +2 -14
- package/src/mcp-apps/index.ts +1 -0
- package/src/model-context/frame/AssistantFrame.test.ts +3 -3
- package/src/model-context/frame/SPEC_AssistantFrame.md +1 -1
- package/src/model-context/frame/useAssistantFrameHost.ts +2 -2
- package/src/model-context/makeAssistantVisible.tsx +1 -1
- package/src/primitives/actionBar/ActionBarCopy.tsx +1 -1
- package/src/primitives/actionBar/ActionBarExportMarkdown.test.tsx +67 -0
- package/src/primitives/actionBar/ActionBarExportMarkdown.tsx +4 -2
- package/src/primitives/actionBar/ActionBarFeedbackNegative.tsx +1 -1
- package/src/primitives/actionBar/ActionBarFeedbackPositive.tsx +1 -1
- package/src/primitives/actionBar/ActionBarStopSpeaking.tsx +1 -1
- package/src/primitives/actionBarMore/scope.ts +2 -2
- package/src/primitives/assistantModal/AssistantModalContent.tsx +1 -1
- package/src/primitives/assistantModal/scope.ts +2 -2
- package/src/primitives/branchPicker/BranchPickerRoot.tsx +5 -3
- package/src/primitives/composer/ComposerAttachmentDropzone.tsx +1 -1
- package/src/primitives/composer/ComposerDictationTranscript.tsx +2 -2
- package/src/primitives/composer/ComposerInput.test.tsx +2 -1
- package/src/primitives/composer/ComposerInput.tsx +3 -3
- package/src/primitives/composer/ComposerQuote.tsx +1 -1
- package/src/primitives/composer/ComposerRoot.tsx +1 -1
- package/src/primitives/composer/trigger/TriggerPopover.tsx +16 -1
- package/src/primitives/composer/trigger/TriggerPopoverBack.tsx +1 -1
- package/src/primitives/composer/trigger/TriggerPopoverCategories.tsx +1 -1
- package/src/primitives/composer/trigger/TriggerPopoverItems.tsx +1 -1
- package/src/primitives/composer/trigger/TriggerPopoverResource.ts +4 -1
- package/src/primitives/composer/trigger/TriggerPopoverRootContext.tsx +2 -0
- package/src/primitives/composer/trigger/detectTrigger.test.ts +88 -5
- package/src/primitives/composer/trigger/detectTrigger.ts +50 -9
- package/src/primitives/composer/trigger/index.ts +1 -0
- package/src/primitives/composer/trigger/triggerDetectionResource.ts +10 -7
- package/src/primitives/composer/trigger/triggerSelectionResource.test.ts +147 -0
- package/src/primitives/composer/trigger/triggerSelectionResource.ts +3 -4
- package/src/primitives/composer.ts +1 -0
- package/src/primitives/message/MessageRoot.tsx +1 -1
- package/src/primitives/selectionToolbar/SelectionToolbarQuote.tsx +1 -1
- package/src/primitives/selectionToolbar/SelectionToolbarRoot.tsx +1 -1
- package/src/primitives/thread/ThreadRoot.tsx +3 -0
- package/src/primitives/thread/ThreadViewport.tsx +1 -1
- package/src/primitives/thread/ThreadViewportFooter.tsx +1 -1
- package/src/primitives/thread/useThreadViewportAutoScroll.ts +1 -1
- package/src/primitives/threadList/ThreadListNew.tsx +1 -1
- package/src/primitives/threadListFocusGroup.ts +2 -2
- package/src/primitives/threadListItem/ThreadListItemRoot.tsx +1 -1
- package/src/primitives/threadListItem/ThreadListItemTrigger.tsx +1 -1
- package/src/primitives/threadListItemMore/ThreadListItemMoreContent.tsx +1 -1
- package/src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx +1 -1
- package/src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx +2 -2
- package/src/primitives/threadListItemMore/scope.ts +2 -2
- package/src/sandbox-host/SandboxHost.test.tsx +106 -1
- package/src/sandbox-host/SandboxHost.tsx +22 -5
- package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +31 -1
- package/src/unstable/useLiveCompletionAdapter.test.tsx +117 -1
- package/src/unstable/useLiveCompletionAdapter.ts +4 -1
- package/src/unstable/useSlashCommandAdapter.test.tsx +141 -0
- package/src/unstable/useSlashCommandAdapter.ts +52 -17
- package/src/utils/Primitive.tsx +2 -2
- package/src/utils/createActionButton.tsx +1 -1
- package/src/utils/hooks/useOnResizeContent.ts +1 -1
- package/src/utils/hooks/useOnScrollToBottom.ts +1 -1
- package/src/utils/invokeCallbackSafely.ts +11 -0
- package/src/utils/smooth/useSmooth.test.tsx +110 -0
- package/src/utils/smooth/useSmooth.ts +21 -7
- package/dist/context/react/utils/ensureBinding.d.ts +0 -5
- package/dist/context/react/utils/ensureBinding.d.ts.map +0 -1
- package/dist/context/react/utils/ensureBinding.js +0 -24
- package/dist/context/react/utils/ensureBinding.js.map +0 -1
- package/dist/context/react/utils/useRuntimeState.d.ts +0 -13
- package/dist/context/react/utils/useRuntimeState.d.ts.map +0 -1
- package/dist/context/react/utils/useRuntimeState.js +0 -33
- package/dist/context/react/utils/useRuntimeState.js.map +0 -1
- package/dist/legacy-runtime/runtime/MessagePartRuntime.d.ts +0 -3
- package/dist/legacy-runtime/runtime/MessagePartRuntime.js +0 -2
- package/dist/legacy-runtime/runtime/MessageRuntime.d.ts +0 -3
- package/dist/legacy-runtime/runtime/MessageRuntime.js +0 -2
- package/dist/legacy-runtime/runtime/ThreadListRuntime.d.ts +0 -3
- package/dist/legacy-runtime/runtime/ThreadListRuntime.js +0 -2
- package/src/context/react/utils/ensureBinding.ts +0 -46
- package/src/context/react/utils/useRuntimeState.ts +0 -46
- package/src/legacy-runtime/runtime/MessagePartRuntime.ts +0 -2
- package/src/legacy-runtime/runtime/MessageRuntime.ts +0 -3
- package/src/legacy-runtime/runtime/ThreadListRuntime.ts +0 -4
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { act, startTransition, Suspense } from "react";
|
|
3
|
+
import { cleanup, render } from "@testing-library/react";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type {
|
|
6
|
+
SandboxBridge,
|
|
7
|
+
SandboxHostProps,
|
|
8
|
+
} from "../sandbox-host/SandboxHost";
|
|
9
|
+
import type { CreateMcpAppBridgeOptions, McpAppBridge } from "./bridge";
|
|
10
|
+
import { MCP_APP_MIME_TYPE } from "./types";
|
|
11
|
+
|
|
12
|
+
const { sandboxHostMock, createMcpAppBridgeMock } = vi.hoisted(() => ({
|
|
13
|
+
sandboxHostMock: vi.fn(),
|
|
14
|
+
createMcpAppBridgeMock: vi.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock("../sandbox-host/SandboxHost", () => ({
|
|
18
|
+
SandboxHost: sandboxHostMock,
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock("./bridge", async (importOriginal) => ({
|
|
22
|
+
...(await importOriginal()),
|
|
23
|
+
createMcpAppBridge: createMcpAppBridgeMock,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
import { McpAppFrame } from "./app-frame";
|
|
27
|
+
|
|
28
|
+
describe("McpAppFrame", () => {
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
cleanup();
|
|
31
|
+
vi.clearAllMocks();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("keeps bridge options scoped to committed renders", async () => {
|
|
35
|
+
let committedCreateBridge: SandboxHostProps["createBridge"] | null = null;
|
|
36
|
+
sandboxHostMock.mockImplementation((props: SandboxHostProps) => {
|
|
37
|
+
committedCreateBridge ??= props.createBridge;
|
|
38
|
+
return null;
|
|
39
|
+
});
|
|
40
|
+
const bridge: McpAppBridge = {
|
|
41
|
+
onMessage: vi.fn(),
|
|
42
|
+
dispose: vi.fn(),
|
|
43
|
+
notifyToolInput: vi.fn(),
|
|
44
|
+
notifyToolResult: vi.fn(),
|
|
45
|
+
notifyHostContextChanged: vi.fn(),
|
|
46
|
+
};
|
|
47
|
+
createMcpAppBridgeMock.mockReturnValue(bridge);
|
|
48
|
+
const callToolA = vi.fn();
|
|
49
|
+
const callToolB = vi.fn();
|
|
50
|
+
const interruptedRender = vi.fn();
|
|
51
|
+
const pending = new Promise<never>(() => {});
|
|
52
|
+
const Block = () => {
|
|
53
|
+
interruptedRender();
|
|
54
|
+
throw pending;
|
|
55
|
+
};
|
|
56
|
+
const view = (
|
|
57
|
+
workspace: string,
|
|
58
|
+
callTool: typeof callToolA,
|
|
59
|
+
blocked: boolean,
|
|
60
|
+
) => (
|
|
61
|
+
<Suspense fallback={null}>
|
|
62
|
+
<McpAppFrame
|
|
63
|
+
app={{ resourceUri: "ui://example/widget" }}
|
|
64
|
+
resource={{
|
|
65
|
+
uri: "ui://example/widget",
|
|
66
|
+
mimeType: MCP_APP_MIME_TYPE,
|
|
67
|
+
html: "",
|
|
68
|
+
}}
|
|
69
|
+
handlers={{ callTool }}
|
|
70
|
+
hostContext={{ workspace }}
|
|
71
|
+
/>
|
|
72
|
+
{blocked ? <Block /> : null}
|
|
73
|
+
</Suspense>
|
|
74
|
+
);
|
|
75
|
+
const rendered = render(view("workspace-a", callToolA, false));
|
|
76
|
+
|
|
77
|
+
act(() => {
|
|
78
|
+
startTransition(() =>
|
|
79
|
+
rendered.rerender(view("workspace-b", callToolB, true)),
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
expect(interruptedRender).toHaveBeenCalled();
|
|
83
|
+
expect(committedCreateBridge).not.toBeNull();
|
|
84
|
+
|
|
85
|
+
const sandboxBridge: SandboxBridge = committedCreateBridge!(
|
|
86
|
+
{
|
|
87
|
+
iframe: document.createElement("iframe"),
|
|
88
|
+
origin: "https://widget.example",
|
|
89
|
+
sendMessage: vi.fn(),
|
|
90
|
+
},
|
|
91
|
+
{ setHeight: vi.fn() },
|
|
92
|
+
);
|
|
93
|
+
const options = createMcpAppBridgeMock.mock
|
|
94
|
+
.calls[0]![0] as CreateMcpAppBridgeOptions;
|
|
95
|
+
expect(options.hostContext).toEqual({ workspace: "workspace-a" });
|
|
96
|
+
await options.handlers?.callTool?.({ name: "search" });
|
|
97
|
+
expect(callToolA).toHaveBeenCalledOnce();
|
|
98
|
+
expect(callToolB).not.toHaveBeenCalled();
|
|
99
|
+
|
|
100
|
+
sandboxBridge.dispose();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type MutableRefObject,
|
|
5
|
+
useEffect,
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
} from "react";
|
|
4
9
|
import { type McpAppBridge, createMcpAppBridge } from "./bridge";
|
|
5
10
|
import {
|
|
6
11
|
SandboxHost,
|
|
@@ -121,14 +126,22 @@ export function McpAppFrame({
|
|
|
121
126
|
undefined,
|
|
122
127
|
);
|
|
123
128
|
|
|
124
|
-
const liveRef = useRef<LiveSnapshot>(
|
|
125
|
-
liveRef.current = {
|
|
129
|
+
const liveRef = useRef<LiveSnapshot>({
|
|
126
130
|
handlers,
|
|
127
131
|
hostInfo,
|
|
128
132
|
hostContext,
|
|
129
133
|
input,
|
|
130
134
|
output,
|
|
131
|
-
};
|
|
135
|
+
});
|
|
136
|
+
useLayoutEffect(() => {
|
|
137
|
+
liveRef.current = {
|
|
138
|
+
handlers,
|
|
139
|
+
hostInfo,
|
|
140
|
+
hostContext,
|
|
141
|
+
input,
|
|
142
|
+
output,
|
|
143
|
+
};
|
|
144
|
+
}, [handlers, hostInfo, hostContext, input, output]);
|
|
132
145
|
|
|
133
146
|
const createBridge = (
|
|
134
147
|
frame: SandboxHostFrame,
|
|
@@ -560,4 +560,25 @@ describe("createMcpAppBridge", () => {
|
|
|
560
560
|
consoleError.mockRestore();
|
|
561
561
|
bridge.dispose();
|
|
562
562
|
});
|
|
563
|
+
|
|
564
|
+
it("preserves the onError receiver", () => {
|
|
565
|
+
const { frame } = makeFrame();
|
|
566
|
+
const handlers = {
|
|
567
|
+
calls: 0,
|
|
568
|
+
onError() {
|
|
569
|
+
expect(this).toBe(handlers);
|
|
570
|
+
this.calls += 1;
|
|
571
|
+
},
|
|
572
|
+
};
|
|
573
|
+
const bridge = createMcpAppBridge({ frame, handlers });
|
|
574
|
+
|
|
575
|
+
deliver(bridge, {
|
|
576
|
+
jsonrpc: "2.0",
|
|
577
|
+
method: "notifications/error",
|
|
578
|
+
params: { message: "kaboom" },
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
expect(handlers.calls).toBe(1);
|
|
582
|
+
bridge.dispose();
|
|
583
|
+
});
|
|
563
584
|
});
|
package/src/mcp-apps/bridge.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SandboxHostFrame } from "../sandbox-host/SandboxHost";
|
|
2
|
+
import { invokeCallbackSafely } from "../utils/invokeCallbackSafely";
|
|
2
3
|
import {
|
|
3
4
|
MCP_APP_PROTOCOL_VERSION,
|
|
4
5
|
type McpAppBridgeHandlers,
|
|
@@ -123,21 +124,8 @@ export function createMcpAppBridge(
|
|
|
123
124
|
});
|
|
124
125
|
};
|
|
125
126
|
|
|
126
|
-
const reportErrorCallbackFailure = (error: unknown) => {
|
|
127
|
-
console.error(
|
|
128
|
-
"[assistant-ui] MCP App onError callback threw an error",
|
|
129
|
-
error,
|
|
130
|
-
);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
127
|
const reportError = (error: Error) => {
|
|
134
|
-
|
|
135
|
-
void Promise.resolve(handlers.onError?.(error)).catch(
|
|
136
|
-
reportErrorCallbackFailure,
|
|
137
|
-
);
|
|
138
|
-
} catch (callbackError) {
|
|
139
|
-
reportErrorCallbackFailure(callbackError);
|
|
140
|
-
}
|
|
128
|
+
invokeCallbackSafely(() => handlers.onError?.(error), "MCP App onError");
|
|
141
129
|
};
|
|
142
130
|
|
|
143
131
|
const handleRequest = async (req: McpAppJsonRpcRequest) => {
|
package/src/mcp-apps/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ describe("AssistantFrame Integration", () => {
|
|
|
27
27
|
parentHandler({
|
|
28
28
|
data,
|
|
29
29
|
source: iframeWindow,
|
|
30
|
-
origin:
|
|
30
|
+
origin: window.location.origin,
|
|
31
31
|
} as MessageEvent);
|
|
32
32
|
});
|
|
33
33
|
}
|
|
@@ -44,7 +44,7 @@ describe("AssistantFrame Integration", () => {
|
|
|
44
44
|
iframeHandler({
|
|
45
45
|
data,
|
|
46
46
|
source: parentWindow, // parent window is the source for subscription
|
|
47
|
-
origin:
|
|
47
|
+
origin: window.location.origin,
|
|
48
48
|
} as MessageEvent);
|
|
49
49
|
});
|
|
50
50
|
}
|
|
@@ -104,7 +104,7 @@ describe("AssistantFrame Integration", () => {
|
|
|
104
104
|
type: "model-context-request",
|
|
105
105
|
}),
|
|
106
106
|
}),
|
|
107
|
-
|
|
107
|
+
window.location.origin,
|
|
108
108
|
);
|
|
109
109
|
});
|
|
110
110
|
|
|
@@ -88,7 +88,7 @@ All messages are wrapped with a channel identifier to avoid conflicts with other
|
|
|
88
88
|
|
|
89
89
|
#### Security Considerations
|
|
90
90
|
|
|
91
|
-
1. **Origin Validation**: Both sides can specify `targetOrigin`
|
|
91
|
+
1. **Origin Validation**: Both sides default to the current page's origin and can specify an exact `targetOrigin` for cross-origin frames; providers share a fail-closed origin policy
|
|
92
92
|
2. **Window Reference**: Host (parent) only accepts messages from the specific iframe window it's connected to
|
|
93
93
|
3. **Message Channel**: Using a unique channel identifier prevents cross-talk with other postMessage users
|
|
94
94
|
|
|
@@ -20,7 +20,7 @@ type UseAssistantFrameHostOptions = {
|
|
|
20
20
|
*
|
|
21
21
|
* useAssistantFrameHost({
|
|
22
22
|
* iframeRef,
|
|
23
|
-
* targetOrigin: "https://trusted-domain.com",
|
|
23
|
+
* targetOrigin: "https://trusted-domain.com",
|
|
24
24
|
* });
|
|
25
25
|
*
|
|
26
26
|
* return <iframe ref={iframeRef} src="..." />;
|
|
@@ -29,7 +29,7 @@ type UseAssistantFrameHostOptions = {
|
|
|
29
29
|
*/
|
|
30
30
|
export const useAssistantFrameHost = ({
|
|
31
31
|
iframeRef,
|
|
32
|
-
targetOrigin
|
|
32
|
+
targetOrigin,
|
|
33
33
|
register,
|
|
34
34
|
}: UseAssistantFrameHostOptions): void => {
|
|
35
35
|
useEffect(() => {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
useContext,
|
|
13
13
|
} from "react";
|
|
14
14
|
import { useAui } from "@assistant-ui/store";
|
|
15
|
-
import { useComposedRefs } from "
|
|
15
|
+
import { useComposedRefs } from "radix-ui/internal";
|
|
16
16
|
import { tool } from "@assistant-ui/core";
|
|
17
17
|
|
|
18
18
|
const click = tool({
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { forwardRef } from "react";
|
|
4
4
|
import type { ActionButtonProps } from "../../utils/createActionButton";
|
|
5
|
-
import { composeEventHandlers } from "
|
|
5
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
6
6
|
import { Primitive } from "../../utils/Primitive";
|
|
7
7
|
import { useActionBarCopy } from "@assistant-ui/core/react";
|
|
8
8
|
import { useAuiState } from "@assistant-ui/store";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import { act } from "react";
|
|
5
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
6
|
+
import type * as AssistantStore from "@assistant-ui/store";
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
8
|
+
import { ActionBarPrimitiveExportMarkdown } from "./ActionBarExportMarkdown";
|
|
9
|
+
|
|
10
|
+
const { getCopyText } = vi.hoisted(() => ({
|
|
11
|
+
getCopyText: vi.fn(() => "# Exported message"),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
(globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
|
|
15
|
+
|
|
16
|
+
vi.mock("@assistant-ui/store", async (importOriginal) => {
|
|
17
|
+
const actual = await importOriginal<typeof AssistantStore>();
|
|
18
|
+
return {
|
|
19
|
+
...actual,
|
|
20
|
+
useAui: () => ({ message: { getCopyText } }),
|
|
21
|
+
useAuiState: () => true,
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("ActionBarPrimitiveExportMarkdown", () => {
|
|
26
|
+
let container: HTMLDivElement;
|
|
27
|
+
let root: Root;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
getCopyText.mockClear();
|
|
31
|
+
container = document.createElement("div");
|
|
32
|
+
document.body.appendChild(container);
|
|
33
|
+
root = createRoot(container);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(async () => {
|
|
37
|
+
await act(async () => {
|
|
38
|
+
root.unmount();
|
|
39
|
+
});
|
|
40
|
+
container.remove();
|
|
41
|
+
vi.restoreAllMocks();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("handles rejected asynchronous exports", async () => {
|
|
45
|
+
const error = new Error("remote save failed");
|
|
46
|
+
const onExport = vi.fn().mockRejectedValue(error);
|
|
47
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
48
|
+
|
|
49
|
+
await act(async () => {
|
|
50
|
+
root.render(
|
|
51
|
+
<ActionBarPrimitiveExportMarkdown onExport={onExport}>
|
|
52
|
+
Export
|
|
53
|
+
</ActionBarPrimitiveExportMarkdown>,
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await act(async () => {
|
|
58
|
+
container.querySelector("button")!.click();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect(onExport).toHaveBeenCalledWith("# Exported message");
|
|
62
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
63
|
+
"[assistant-ui] markdown export failed:",
|
|
64
|
+
error,
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { forwardRef, useCallback } from "react";
|
|
4
4
|
import type { ActionButtonProps } from "../../utils/createActionButton";
|
|
5
|
-
import { composeEventHandlers } from "
|
|
5
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
6
6
|
import { Primitive } from "../../utils/Primitive";
|
|
7
7
|
import { useAuiState, useAui } from "@assistant-ui/store";
|
|
8
8
|
|
|
@@ -61,7 +61,9 @@ export const ActionBarPrimitiveExportMarkdown = forwardRef<
|
|
|
61
61
|
ref={forwardedRef}
|
|
62
62
|
disabled={disabled || !callback}
|
|
63
63
|
onClick={composeEventHandlers(onClick, () => {
|
|
64
|
-
callback?.()
|
|
64
|
+
void callback?.().catch((error: unknown) => {
|
|
65
|
+
console.error("[assistant-ui] markdown export failed:", error);
|
|
66
|
+
});
|
|
65
67
|
})}
|
|
66
68
|
/>
|
|
67
69
|
);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { forwardRef } from "react";
|
|
4
4
|
import type { ActionButtonProps } from "../../utils/createActionButton";
|
|
5
|
-
import { composeEventHandlers } from "
|
|
5
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
6
6
|
import { Primitive } from "../../utils/Primitive";
|
|
7
7
|
import { useAuiState } from "@assistant-ui/store";
|
|
8
8
|
import { useActionBarFeedbackNegative as useActionBarFeedbackNegativeBehavior } from "@assistant-ui/core/react";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { forwardRef } from "react";
|
|
4
4
|
import type { ActionButtonProps } from "../../utils/createActionButton";
|
|
5
|
-
import { composeEventHandlers } from "
|
|
5
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
6
6
|
import { useAuiState } from "@assistant-ui/store";
|
|
7
7
|
import { Primitive } from "../../utils/Primitive";
|
|
8
8
|
import { useActionBarFeedbackPositive as useActionBarFeedbackPositiveBehavior } from "@assistant-ui/core/react";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { forwardRef } from "react";
|
|
4
4
|
import type { ActionButtonProps } from "../../utils/createActionButton";
|
|
5
5
|
import { Primitive } from "../../utils/Primitive";
|
|
6
|
-
import { composeEventHandlers } from "
|
|
6
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
7
7
|
import { useActionBarStopSpeaking as useActionBarStopSpeakingBehavior } from "@assistant-ui/core/react";
|
|
8
8
|
|
|
9
9
|
const useActionBarStopSpeaking = () => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
|
|
2
|
-
import type {
|
|
2
|
+
import type { Context } from "radix-ui/internal";
|
|
3
3
|
|
|
4
4
|
export const useDropdownMenuScope: ReturnType<
|
|
5
5
|
typeof DropdownMenuPrimitive.createDropdownMenuScope
|
|
6
6
|
> = DropdownMenuPrimitive.createDropdownMenuScope();
|
|
7
7
|
|
|
8
|
-
export type ScopedProps<P> = P & { __scopeActionBarMore?: Scope };
|
|
8
|
+
export type ScopedProps<P> = P & { __scopeActionBarMore?: Context.Scope };
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
forwardRef,
|
|
7
7
|
} from "react";
|
|
8
8
|
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
9
|
-
import { composeEventHandlers } from "
|
|
9
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
10
10
|
import type { WithRenderPropProps } from "../../utils/Primitive";
|
|
11
11
|
import { PopoverRenderContent } from "./popoverRenderPrimitives";
|
|
12
12
|
import { type ScopedProps, usePopoverScope } from "./scope";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
2
|
-
import type {
|
|
2
|
+
import type { Context } from "radix-ui/internal";
|
|
3
3
|
|
|
4
4
|
export const usePopoverScope: ReturnType<
|
|
5
5
|
typeof PopoverPrimitive.createPopoverScope
|
|
6
6
|
> = PopoverPrimitive.createPopoverScope();
|
|
7
|
-
export type ScopedProps<P> = P & { __scopeAssistantModal?: Scope };
|
|
7
|
+
export type ScopedProps<P> = P & { __scopeAssistantModal?: Context.Scope };
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
forwardRef,
|
|
7
7
|
type ComponentPropsWithoutRef,
|
|
8
8
|
} from "react";
|
|
9
|
-
import {
|
|
9
|
+
import { AuiIf } from "@assistant-ui/store";
|
|
10
10
|
|
|
11
11
|
export namespace BranchPickerPrimitiveRoot {
|
|
12
12
|
export type Element = ComponentRef<typeof Primitive.div>;
|
|
@@ -42,9 +42,11 @@ export const BranchPickerPrimitiveRoot = forwardRef<
|
|
|
42
42
|
BranchPickerPrimitiveRoot.Props
|
|
43
43
|
>(({ hideWhenSingleBranch, ...rest }, ref) => {
|
|
44
44
|
return (
|
|
45
|
-
<
|
|
45
|
+
<AuiIf
|
|
46
|
+
condition={(s) => !hideWhenSingleBranch || s.message.branchCount >= 2}
|
|
47
|
+
>
|
|
46
48
|
<Primitive.div {...rest} ref={ref} />
|
|
47
|
-
</
|
|
49
|
+
</AuiIf>
|
|
48
50
|
);
|
|
49
51
|
});
|
|
50
52
|
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isValidElement,
|
|
10
10
|
} from "react";
|
|
11
11
|
|
|
12
|
-
import { composeEventHandlers } from "
|
|
12
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
13
13
|
import { Slot } from "radix-ui";
|
|
14
14
|
import type React from "react";
|
|
15
15
|
import { useAui } from "@assistant-ui/store";
|
|
@@ -21,11 +21,11 @@ export namespace ComposerPrimitiveDictationTranscript {
|
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* ```tsx
|
|
24
|
-
* <
|
|
24
|
+
* <AuiIf condition={(s) => s.composer.dictation != null}>
|
|
25
25
|
* <div className="dictation-preview">
|
|
26
26
|
* <ComposerPrimitive.DictationTranscript />
|
|
27
27
|
* </div>
|
|
28
|
-
* </
|
|
28
|
+
* </AuiIf>
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
export const ComposerPrimitiveDictationTranscript = forwardRef<
|
|
@@ -81,7 +81,8 @@ vi.mock("./trigger/TriggerPopoverRootContext", () => ({
|
|
|
81
81
|
|
|
82
82
|
let escapeKeydownHandler: ((event: KeyboardEvent) => void) | null = null;
|
|
83
83
|
|
|
84
|
-
vi.mock("
|
|
84
|
+
vi.mock("radix-ui/internal", async (importOriginal) => ({
|
|
85
|
+
...(await importOriginal<typeof import("radix-ui/internal")>()),
|
|
85
86
|
useEscapeKeydown: (handler: (event: KeyboardEvent) => void) => {
|
|
86
87
|
escapeKeydownHandler = handler;
|
|
87
88
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { composeEventHandlers } from "
|
|
4
|
-
import { useComposedRefs } from "
|
|
3
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
4
|
+
import { useComposedRefs } from "radix-ui/internal";
|
|
5
5
|
import { Slot } from "radix-ui";
|
|
6
6
|
import {
|
|
7
7
|
type ClipboardEvent,
|
|
@@ -19,7 +19,7 @@ import TextareaAutosize, {
|
|
|
19
19
|
type TextareaAutosizeProps,
|
|
20
20
|
type TextareaHeightChangeMeta,
|
|
21
21
|
} from "react-textarea-autosize";
|
|
22
|
-
import { useEscapeKeydown } from "
|
|
22
|
+
import { useEscapeKeydown } from "radix-ui/internal";
|
|
23
23
|
import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom";
|
|
24
24
|
import { useMediaQuery } from "../../utils/hooks/useMediaQuery";
|
|
25
25
|
import { useAui } from "@assistant-ui/store";
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
useTriggerPopoverAriaPublish,
|
|
29
29
|
useTriggerPopoverRootContext,
|
|
30
30
|
} from "./TriggerPopoverRootContext";
|
|
31
|
+
import type { TriggerMatcher } from "./detectTrigger";
|
|
31
32
|
|
|
32
33
|
const TriggerPopoverScopeContext =
|
|
33
34
|
createContext<TriggerPopoverResourceOutput | null>(null);
|
|
@@ -70,6 +71,8 @@ export namespace ComposerPrimitiveTriggerPopover {
|
|
|
70
71
|
> & {
|
|
71
72
|
/** The character(s) that activate this trigger (e.g. `"@"`, `"/"`). Also serves as the trigger identity within the root. */
|
|
72
73
|
readonly char: string;
|
|
74
|
+
/** Overrides trigger detection for both textarea and Lexical inputs. `endOffset` is the exclusive replace bound. */
|
|
75
|
+
readonly matcher?: TriggerMatcher | undefined;
|
|
73
76
|
/** Adapter providing categories and items. */
|
|
74
77
|
readonly adapter?: Unstable_TriggerAdapter | undefined;
|
|
75
78
|
/** Whether the adapter is resolving items, surfaced to the popover scope for async sources. @default false */
|
|
@@ -111,6 +114,7 @@ export const ComposerPrimitiveTriggerPopover = forwardRef<
|
|
|
111
114
|
(
|
|
112
115
|
{
|
|
113
116
|
char,
|
|
117
|
+
matcher,
|
|
114
118
|
adapter,
|
|
115
119
|
isLoading = false,
|
|
116
120
|
"aria-label": ariaLabel,
|
|
@@ -122,6 +126,15 @@ export const ComposerPrimitiveTriggerPopover = forwardRef<
|
|
|
122
126
|
const aui = useAui();
|
|
123
127
|
const text = useAuiState((s) => s.composer.text);
|
|
124
128
|
const popoverId = useId();
|
|
129
|
+
const matcherRef = useRef(matcher);
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
matcherRef.current = matcher;
|
|
132
|
+
});
|
|
133
|
+
const [registeredMatcher] = useState(
|
|
134
|
+
() => (nextText: string, triggerChar: string, cursorPosition: number) =>
|
|
135
|
+
matcherRef.current!(nextText, triggerChar, cursorPosition),
|
|
136
|
+
);
|
|
137
|
+
const hasMatcher = matcher !== undefined;
|
|
125
138
|
|
|
126
139
|
// Track in state (for resource reactivity) + ref (dev warning on duplicate registrations).
|
|
127
140
|
const behaviorRef = useRef<TriggerBehavior | null>(null);
|
|
@@ -165,6 +178,7 @@ export const ComposerPrimitiveTriggerPopover = forwardRef<
|
|
|
165
178
|
adapter,
|
|
166
179
|
text,
|
|
167
180
|
triggerChar: char,
|
|
181
|
+
matcher,
|
|
168
182
|
behavior: behavior ?? undefined,
|
|
169
183
|
aui,
|
|
170
184
|
popoverId,
|
|
@@ -178,10 +192,11 @@ export const ComposerPrimitiveTriggerPopover = forwardRef<
|
|
|
178
192
|
useEffect(() => {
|
|
179
193
|
return root.register({
|
|
180
194
|
char,
|
|
195
|
+
...(hasMatcher ? { matcher: registeredMatcher } : {}),
|
|
181
196
|
...(behavior ? { behavior } : {}),
|
|
182
197
|
resource: getResource(),
|
|
183
198
|
});
|
|
184
|
-
}, [root, char, behavior]);
|
|
199
|
+
}, [root, char, hasMatcher, behavior]);
|
|
185
200
|
|
|
186
201
|
const pluginRegistry = useComposerInputPluginRegistryOptional();
|
|
187
202
|
useEffect(() => {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type ComponentPropsWithoutRef,
|
|
7
7
|
forwardRef,
|
|
8
8
|
} from "react";
|
|
9
|
-
import { composeEventHandlers } from "
|
|
9
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
10
10
|
import { useTriggerPopoverScopeContext } from "./TriggerPopover";
|
|
11
11
|
|
|
12
12
|
export namespace ComposerPrimitiveTriggerPopoverBack {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
forwardRef,
|
|
9
9
|
useCallback,
|
|
10
10
|
} from "react";
|
|
11
|
-
import { composeEventHandlers } from "
|
|
11
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
12
12
|
import { useTriggerPopoverScopeContext } from "./TriggerPopover";
|
|
13
13
|
import type { Unstable_TriggerCategory } from "@assistant-ui/core";
|
|
14
14
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
forwardRef,
|
|
9
9
|
useCallback,
|
|
10
10
|
} from "react";
|
|
11
|
-
import { composeEventHandlers } from "
|
|
11
|
+
import { composeEventHandlers } from "radix-ui/internal";
|
|
12
12
|
import { useTriggerPopoverScopeContext } from "./TriggerPopover";
|
|
13
13
|
import type { Unstable_TriggerItem } from "@assistant-ui/core";
|
|
14
14
|
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type SelectItemOverride,
|
|
15
15
|
type TriggerBehavior,
|
|
16
16
|
} from "./triggerSelectionResource";
|
|
17
|
+
import type { TriggerMatcher } from "./detectTrigger";
|
|
17
18
|
|
|
18
19
|
export type { SelectItemOverride, TriggerBehavior };
|
|
19
20
|
export type { TriggerPopoverKeyEvent } from "./triggerKeyboardResource";
|
|
@@ -57,6 +58,7 @@ const useTriggerPopoverResource = ({
|
|
|
57
58
|
adapter,
|
|
58
59
|
text,
|
|
59
60
|
triggerChar,
|
|
61
|
+
matcher,
|
|
60
62
|
behavior,
|
|
61
63
|
aui,
|
|
62
64
|
popoverId,
|
|
@@ -65,6 +67,7 @@ const useTriggerPopoverResource = ({
|
|
|
65
67
|
adapter: Unstable_TriggerAdapter | undefined;
|
|
66
68
|
text: string;
|
|
67
69
|
triggerChar: string;
|
|
70
|
+
matcher: TriggerMatcher | undefined;
|
|
68
71
|
behavior: TriggerBehavior | undefined;
|
|
69
72
|
aui: AssistantClient;
|
|
70
73
|
/** Stable ID for accessible element IDs (pass React's useId() from component layer). */
|
|
@@ -72,7 +75,7 @@ const useTriggerPopoverResource = ({
|
|
|
72
75
|
isLoading: boolean;
|
|
73
76
|
}): TriggerPopoverResourceOutput => {
|
|
74
77
|
const detection = useResource(
|
|
75
|
-
TriggerDetectionResource({ text, triggerChar }),
|
|
78
|
+
TriggerDetectionResource({ text, triggerChar, matcher }),
|
|
76
79
|
);
|
|
77
80
|
|
|
78
81
|
const open =
|