@assistant-ui/react 0.11.47 → 0.11.48

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 (66) hide show
  1. package/dist/client/types/Thread.d.ts +4 -0
  2. package/dist/client/types/Thread.d.ts.map +1 -1
  3. package/dist/context/react/index.d.ts +1 -1
  4. package/dist/context/react/index.d.ts.map +1 -1
  5. package/dist/context/react/index.js.map +1 -1
  6. package/dist/context/stores/ThreadViewport.js +1 -1
  7. package/dist/context/stores/ThreadViewport.js.map +1 -1
  8. package/dist/legacy-runtime/client/ThreadRuntimeClient.d.ts.map +1 -1
  9. package/dist/legacy-runtime/client/ThreadRuntimeClient.js +1 -0
  10. package/dist/legacy-runtime/client/ThreadRuntimeClient.js.map +1 -1
  11. package/dist/legacy-runtime/runtime/subscribable/SKIP_UPDATE.js +1 -1
  12. package/dist/legacy-runtime/runtime/subscribable/SKIP_UPDATE.js.map +1 -1
  13. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +1 -1
  14. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  15. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.js +1 -1
  16. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.js.map +1 -1
  17. package/dist/legacy-runtime/runtime-cores/external-store/getExternalStoreMessage.js +2 -2
  18. package/dist/legacy-runtime/runtime-cores/external-store/getExternalStoreMessage.js.map +1 -1
  19. package/dist/model-context/registry/ModelContextRegistry.js +3 -3
  20. package/dist/model-context/registry/ModelContextRegistry.js.map +1 -1
  21. package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts +17 -0
  22. package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts.map +1 -0
  23. package/dist/primitives/actionBar/ActionBarExportMarkdown.js +54 -0
  24. package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -0
  25. package/dist/primitives/actionBar/index.d.ts +1 -0
  26. package/dist/primitives/actionBar/index.d.ts.map +1 -1
  27. package/dist/primitives/actionBar/index.js +2 -0
  28. package/dist/primitives/actionBar/index.js.map +1 -1
  29. package/dist/primitives/assistant/AssistantIf.d.ts +12 -0
  30. package/dist/primitives/assistant/AssistantIf.d.ts.map +1 -0
  31. package/dist/primitives/assistant/AssistantIf.js +16 -0
  32. package/dist/primitives/assistant/AssistantIf.js.map +1 -0
  33. package/dist/primitives/composer/ComposerIf.d.ts +3 -0
  34. package/dist/primitives/composer/ComposerIf.d.ts.map +1 -1
  35. package/dist/primitives/composer/ComposerIf.js.map +1 -1
  36. package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
  37. package/dist/primitives/composer/ComposerInput.js +1 -0
  38. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  39. package/dist/primitives/index.d.ts +1 -0
  40. package/dist/primitives/index.d.ts.map +1 -1
  41. package/dist/primitives/index.js +2 -0
  42. package/dist/primitives/index.js.map +1 -1
  43. package/dist/primitives/message/MessageIf.d.ts +3 -0
  44. package/dist/primitives/message/MessageIf.d.ts.map +1 -1
  45. package/dist/primitives/message/MessageIf.js.map +1 -1
  46. package/dist/primitives/message/MessageParts.js +2 -2
  47. package/dist/primitives/message/MessageParts.js.map +1 -1
  48. package/dist/primitives/thread/ThreadIf.d.ts +3 -0
  49. package/dist/primitives/thread/ThreadIf.d.ts.map +1 -1
  50. package/dist/primitives/thread/ThreadIf.js +2 -3
  51. package/dist/primitives/thread/ThreadIf.js.map +1 -1
  52. package/dist/tests/setup.js +44 -42
  53. package/dist/tests/setup.js.map +1 -1
  54. package/package.json +7 -7
  55. package/src/client/types/Thread.ts +5 -0
  56. package/src/context/react/index.ts +1 -0
  57. package/src/legacy-runtime/client/ThreadRuntimeClient.ts +1 -0
  58. package/src/primitives/actionBar/ActionBarExportMarkdown.tsx +70 -0
  59. package/src/primitives/actionBar/index.ts +1 -0
  60. package/src/primitives/assistant/AssistantIf.tsx +25 -0
  61. package/src/primitives/composer/ComposerIf.tsx +3 -0
  62. package/src/primitives/composer/ComposerInput.tsx +3 -0
  63. package/src/primitives/index.ts +2 -0
  64. package/src/primitives/message/MessageIf.tsx +3 -0
  65. package/src/primitives/message/MessageParts.tsx +2 -2
  66. package/src/primitives/thread/ThreadIf.tsx +5 -3
package/package.json CHANGED
@@ -28,7 +28,7 @@
28
28
  "conversational-ui",
29
29
  "conversational-ai"
30
30
  ],
31
- "version": "0.11.47",
31
+ "version": "0.11.48",
32
32
  "license": "MIT",
33
33
  "type": "module",
34
34
  "exports": {
@@ -48,8 +48,8 @@
48
48
  ],
49
49
  "sideEffects": false,
50
50
  "dependencies": {
51
- "assistant-cloud": "^0.1.9",
52
- "@assistant-ui/tap": "^0.3.1",
51
+ "assistant-cloud": "^0.1.10",
52
+ "@assistant-ui/tap": "^0.3.2",
53
53
  "@radix-ui/primitive": "^1.1.3",
54
54
  "@radix-ui/react-compose-refs": "^1.1.2",
55
55
  "@radix-ui/react-context": "^1.1.3",
@@ -59,11 +59,11 @@
59
59
  "@radix-ui/react-use-callback-ref": "^1.1.1",
60
60
  "@radix-ui/react-use-escape-keydown": "^1.1.1",
61
61
  "@standard-schema/spec": "^1.0.0",
62
- "assistant-stream": "^0.2.42",
62
+ "assistant-stream": "^0.2.43",
63
63
  "nanoid": "5.1.6",
64
64
  "react-textarea-autosize": "^8.5.9",
65
65
  "zod": "^4.1.13",
66
- "zustand": "^5.0.8"
66
+ "zustand": "^5.0.9"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "@types/react": "*",
@@ -84,8 +84,8 @@
84
84
  "@stryker-mutator/vitest-runner": "^9.4.0",
85
85
  "@types/json-schema": "^7.0.15",
86
86
  "@types/node": "^24.10.1",
87
- "tsx": "^4.20.6",
88
- "vitest": "^4.0.14",
87
+ "tsx": "^4.21.0",
88
+ "vitest": "^4.0.15",
89
89
  "@assistant-ui/x-buildutils": "0.0.1"
90
90
  },
91
91
  "publishConfig": {
@@ -19,6 +19,11 @@ import { CreateResumeRunConfig } from "../../legacy-runtime/runtime/ThreadRuntim
19
19
  import { ModelContext } from "../../model-context";
20
20
 
21
21
  export type ThreadClientState = {
22
+ /**
23
+ * Whether the thread is empty. A thread is considered empty when it has no messages and is not loading.
24
+ */
25
+ readonly isEmpty: boolean;
26
+
22
27
  /**
23
28
  * Whether the thread is disabled. Disabled threads cannot receive new messages.
24
29
  */
@@ -6,6 +6,7 @@ export {
6
6
  useAssistantApi,
7
7
  useExtendedAssistantApi,
8
8
  type AssistantApi,
9
+ type AssistantState,
9
10
  } from "./AssistantApiContext";
10
11
  export { useAssistantState } from "./hooks/useAssistantState";
11
12
  export { useAssistantEvent } from "./hooks/useAssistantEvent";
@@ -97,6 +97,7 @@ export const ThreadClient = resource(
97
97
 
98
98
  const state = tapMemo<ThreadClientState>(() => {
99
99
  return {
100
+ isEmpty: messages.state.length === 0 && !runtimeState.isLoading,
100
101
  isDisabled: runtimeState.isDisabled,
101
102
  isLoading: runtimeState.isLoading,
102
103
  isRunning: runtimeState.isRunning,
@@ -0,0 +1,70 @@
1
+ "use client";
2
+
3
+ import { forwardRef, useCallback } from "react";
4
+ import { ActionButtonProps } from "../../utils/createActionButton";
5
+ import { composeEventHandlers } from "@radix-ui/primitive";
6
+ import { Primitive } from "@radix-ui/react-primitive";
7
+ import { useAssistantState, useAssistantApi } from "../../context";
8
+
9
+ const useActionBarExportMarkdown = ({
10
+ filename,
11
+ onExport,
12
+ }: {
13
+ filename?: string | undefined;
14
+ onExport?: ((content: string) => void | Promise<void>) | undefined;
15
+ } = {}) => {
16
+ const api = useAssistantApi();
17
+ const hasExportableContent = useAssistantState(({ message }) => {
18
+ return (
19
+ (message.role !== "assistant" || message.status?.type !== "running") &&
20
+ message.parts.some((c) => c.type === "text" && c.text.length > 0)
21
+ );
22
+ });
23
+
24
+ const callback = useCallback(async () => {
25
+ const content = api.message().getCopyText();
26
+ if (!content) return;
27
+
28
+ if (onExport) {
29
+ await onExport(content);
30
+ return;
31
+ }
32
+
33
+ const blob = new Blob([content], { type: "text/markdown" });
34
+ const url = URL.createObjectURL(blob);
35
+ const a = document.createElement("a");
36
+ a.href = url;
37
+ a.download = filename ?? `message-${Date.now()}.md`;
38
+ a.click();
39
+ URL.revokeObjectURL(url);
40
+ }, [api, filename, onExport]);
41
+
42
+ if (!hasExportableContent) return null;
43
+ return callback;
44
+ };
45
+
46
+ export namespace ActionBarPrimitiveExportMarkdown {
47
+ export type Element = HTMLButtonElement;
48
+ export type Props = ActionButtonProps<typeof useActionBarExportMarkdown>;
49
+ }
50
+
51
+ export const ActionBarPrimitiveExportMarkdown = forwardRef<
52
+ ActionBarPrimitiveExportMarkdown.Element,
53
+ ActionBarPrimitiveExportMarkdown.Props
54
+ >(({ filename, onExport, onClick, disabled, ...props }, forwardedRef) => {
55
+ const callback = useActionBarExportMarkdown({ filename, onExport });
56
+ return (
57
+ <Primitive.button
58
+ type="button"
59
+ {...props}
60
+ ref={forwardedRef}
61
+ disabled={disabled || !callback}
62
+ onClick={composeEventHandlers(onClick, () => {
63
+ callback?.();
64
+ })}
65
+ />
66
+ );
67
+ });
68
+
69
+ ActionBarPrimitiveExportMarkdown.displayName =
70
+ "ActionBarPrimitive.ExportMarkdown";
@@ -6,3 +6,4 @@ export { ActionBarPrimitiveSpeak as Speak } from "./ActionBarSpeak";
6
6
  export { ActionBarPrimitiveStopSpeaking as StopSpeaking } from "./ActionBarStopSpeaking";
7
7
  export { ActionBarPrimitiveFeedbackPositive as FeedbackPositive } from "./ActionBarFeedbackPositive";
8
8
  export { ActionBarPrimitiveFeedbackNegative as FeedbackNegative } from "./ActionBarFeedbackNegative";
9
+ export { ActionBarPrimitiveExportMarkdown as ExportMarkdown } from "./ActionBarExportMarkdown";
@@ -0,0 +1,25 @@
1
+ "use client";
2
+
3
+ import type { FC, PropsWithChildren } from "react";
4
+ import { useAssistantState } from "../../context";
5
+ import type { AssistantState } from "../../context/react/AssistantApiContext";
6
+
7
+ type UseAssistantIfProps = {
8
+ condition: AssistantIf.Condition;
9
+ };
10
+
11
+ const useAssistantIf = (props: UseAssistantIfProps) => {
12
+ return useAssistantState(props.condition);
13
+ };
14
+
15
+ export namespace AssistantIf {
16
+ export type Props = PropsWithChildren<UseAssistantIfProps>;
17
+ export type Condition = (state: AssistantState) => boolean;
18
+ }
19
+
20
+ export const AssistantIf: FC<AssistantIf.Props> = ({ children, condition }) => {
21
+ const result = useAssistantIf({ condition });
22
+ return result ? children : null;
23
+ };
24
+
25
+ AssistantIf.displayName = "AssistantIf";
@@ -23,6 +23,9 @@ export namespace ComposerPrimitiveIf {
23
23
  export type Props = PropsWithChildren<UseComposerIfProps>;
24
24
  }
25
25
 
26
+ /**
27
+ * @deprecated Use `<AssistantIf condition={({ composer }) => ...} />` instead.
28
+ */
26
29
  export const ComposerPrimitiveIf: FC<ComposerPrimitiveIf.Props> = ({
27
30
  children,
28
31
  ...query
@@ -114,6 +114,9 @@ export const ComposerPrimitiveInput = forwardRef<
114
114
  useEscapeKeydown((e) => {
115
115
  if (!cancelOnEscape) return;
116
116
 
117
+ // Only handle ESC if it originated from within this input
118
+ if (!textareaRef.current?.contains(e.target as Node)) return;
119
+
117
120
  const composer = api.composer();
118
121
  if (composer.getState().canCancel) {
119
122
  composer.cancel();
@@ -10,6 +10,8 @@ export * as ThreadPrimitive from "./thread";
10
10
  export * as ThreadListPrimitive from "./threadList";
11
11
  export * as ThreadListItemPrimitive from "./threadListItem";
12
12
 
13
+ export { AssistantIf } from "./assistant/AssistantIf";
14
+
13
15
  export { useMessagePartText } from "./messagePart/useMessagePartText";
14
16
  export { useMessagePartReasoning } from "./messagePart/useMessagePartReasoning";
15
17
  export { useMessagePartSource } from "./messagePart/useMessagePartSource";
@@ -77,6 +77,9 @@ export namespace MessagePrimitiveIf {
77
77
  export type Props = PropsWithChildren<UseMessageIfProps>;
78
78
  }
79
79
 
80
+ /**
81
+ * @deprecated Use `<AssistantIf condition={({ message }) => ...} />` instead.
82
+ */
80
83
  export const MessagePrimitiveIf: FC<MessagePrimitiveIf.Props> = ({
81
84
  children,
82
85
  ...query
@@ -478,7 +478,7 @@ export const MessagePrimitiveParts: FC<MessagePrimitiveParts.Props> = ({
478
478
  );
479
479
  } else if (range.type === "toolGroup") {
480
480
  const ToolGroupComponent =
481
- components!.ToolGroup ?? defaultComponents.ToolGroup;
481
+ components?.ToolGroup ?? defaultComponents.ToolGroup;
482
482
  return (
483
483
  <ToolGroupComponent
484
484
  key={`tool-${range.startIndex}`}
@@ -500,7 +500,7 @@ export const MessagePrimitiveParts: FC<MessagePrimitiveParts.Props> = ({
500
500
  } else {
501
501
  // reasoningGroup
502
502
  const ReasoningGroupComponent =
503
- components!.ReasoningGroup ?? defaultComponents.ReasoningGroup;
503
+ components?.ReasoningGroup ?? defaultComponents.ReasoningGroup;
504
504
  return (
505
505
  <ReasoningGroupComponent
506
506
  key={`reasoning-${range.startIndex}`}
@@ -14,9 +14,8 @@ type UseThreadIfProps = RequireAtLeastOne<ThreadIfFilters>;
14
14
 
15
15
  const useThreadIf = (props: UseThreadIfProps) => {
16
16
  return useAssistantState(({ thread }) => {
17
- const isEmpty = thread.messages.length === 0 && !thread.isLoading;
18
- if (props.empty === true && !isEmpty) return false;
19
- if (props.empty === false && isEmpty) return false;
17
+ if (props.empty === true && !thread.isEmpty) return false;
18
+ if (props.empty === false && thread.isEmpty) return false;
20
19
 
21
20
  if (props.running === true && !thread.isRunning) return false;
22
21
  if (props.running === false && thread.isRunning) return false;
@@ -31,6 +30,9 @@ export namespace ThreadPrimitiveIf {
31
30
  export type Props = PropsWithChildren<UseThreadIfProps>;
32
31
  }
33
32
 
33
+ /**
34
+ * @deprecated Use `<AssistantIf condition={({ thread }) => ...} />` instead.
35
+ */
34
36
  export const ThreadPrimitiveIf: FC<ThreadPrimitiveIf.Props> = ({
35
37
  children,
36
38
  ...query