@assistant-ui/react 0.15.24 → 0.15.25

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 (90) hide show
  1. package/dist/hooks/useToolCallElapsed.d.ts +3 -1
  2. package/dist/hooks/useToolCallElapsed.d.ts.map +1 -1
  3. package/dist/hooks/useToolCallElapsed.js +7 -8
  4. package/dist/hooks/useToolCallElapsed.js.map +1 -1
  5. package/dist/index.d.ts +10 -4
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -3
  8. package/dist/model-context/makeAssistantVisible.d.ts +1 -0
  9. package/dist/model-context/makeAssistantVisible.d.ts.map +1 -1
  10. package/dist/model-context/makeAssistantVisible.js +31 -12
  11. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  12. package/dist/primitives/composer/ComposerInput.d.ts +1 -1
  13. package/dist/primitives/composer/ComposerInput.js +1 -1
  14. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  15. package/dist/primitives/composer/trigger/TriggerPopover.d.ts +11 -11
  16. package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
  17. package/dist/primitives/composer/trigger/TriggerPopover.js +62 -60
  18. package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
  19. package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts +8 -8
  20. package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts.map +1 -1
  21. package/dist/primitives/composer/trigger/TriggerPopoverAction.js +6 -6
  22. package/dist/primitives/composer/trigger/TriggerPopoverAction.js.map +1 -1
  23. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts +3 -3
  24. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -1
  25. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
  26. package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts +9 -9
  27. package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts.map +1 -1
  28. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js +7 -7
  29. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js.map +1 -1
  30. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts +5 -5
  31. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -1
  32. package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
  33. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts +8 -8
  34. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js +8 -8
  35. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
  36. package/dist/primitives/composer/trigger/index.d.ts +1 -1
  37. package/dist/primitives/composer.d.ts +20 -1
  38. package/dist/primitives/composer.d.ts.map +1 -1
  39. package/dist/primitives/composer.js +8 -1
  40. package/dist/primitives/composer.js.map +1 -1
  41. package/dist/primitives/thread/ThreadLoadEarlier.d.ts +19 -0
  42. package/dist/primitives/thread/ThreadLoadEarlier.d.ts.map +1 -0
  43. package/dist/primitives/thread/ThreadLoadEarlier.js +36 -0
  44. package/dist/primitives/thread/ThreadLoadEarlier.js.map +1 -0
  45. package/dist/primitives/thread/ThreadMessages.d.ts +1 -1
  46. package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -1
  47. package/dist/primitives/thread/ThreadMessages.js +2 -2
  48. package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
  49. package/dist/primitives/thread/useThreadViewportAutoScroll.js +111 -2
  50. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  51. package/dist/primitives/thread.d.ts +2 -0
  52. package/dist/primitives/thread.d.ts.map +1 -1
  53. package/dist/primitives/thread.js +5 -2
  54. package/dist/primitives/thread.js.map +1 -1
  55. package/dist/sandbox-host/SandboxHost.d.ts.map +1 -1
  56. package/dist/sandbox-host/SandboxHost.js +6 -1
  57. package/dist/sandbox-host/SandboxHost.js.map +1 -1
  58. package/dist/unstable/useMessageStallDetection.d.ts.map +1 -1
  59. package/dist/unstable/useMessageStallDetection.js +10 -6
  60. package/dist/unstable/useMessageStallDetection.js.map +1 -1
  61. package/dist/utils/smooth/useSmooth.js +6 -6
  62. package/dist/utils/smooth/useSmooth.js.map +1 -1
  63. package/package.json +5 -4
  64. package/src/hooks/useToolCallElapsed.test.tsx +35 -0
  65. package/src/hooks/useToolCallElapsed.ts +9 -7
  66. package/src/index.ts +28 -3
  67. package/src/model-context/makeAssistantVisible.test.tsx +315 -5
  68. package/src/model-context/makeAssistantVisible.tsx +43 -7
  69. package/src/primitives/composer/ComposerInput.tsx +1 -1
  70. package/src/primitives/composer/trigger/TriggerPopover.tsx +11 -11
  71. package/src/primitives/composer/trigger/TriggerPopoverAction.tsx +10 -13
  72. package/src/primitives/composer/trigger/TriggerPopoverCategories.tsx +2 -2
  73. package/src/primitives/composer/trigger/TriggerPopoverDirective.tsx +11 -14
  74. package/src/primitives/composer/trigger/TriggerPopoverExports.test.ts +61 -0
  75. package/src/primitives/composer/trigger/TriggerPopoverItems.tsx +3 -3
  76. package/src/primitives/composer/trigger/TriggerPopoverRootContext.tsx +8 -8
  77. package/src/primitives/composer.ts +21 -1
  78. package/src/primitives/message/MessageRoot.test.tsx +125 -2
  79. package/src/primitives/thread/ThreadLoadEarlier.tsx +35 -0
  80. package/src/primitives/thread/ThreadMessages.tsx +1 -0
  81. package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +256 -5
  82. package/src/primitives/thread/useThreadViewportAutoScroll.ts +147 -1
  83. package/src/primitives/thread.ts +2 -0
  84. package/src/sandbox-host/SandboxHost.test.tsx +34 -0
  85. package/src/sandbox-host/SandboxHost.tsx +8 -4
  86. package/src/tests/toolCallTiming.test.tsx +19 -2
  87. package/src/unstable/useMessageStallDetection.test.tsx +40 -0
  88. package/src/unstable/useMessageStallDetection.ts +10 -6
  89. package/src/utils/smooth/useSmooth.test.tsx +16 -5
  90. package/src/utils/smooth/useSmooth.ts +7 -7
@@ -0,0 +1,40 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { renderToString } from "react-dom/server";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+
6
+ const { message } = vi.hoisted(() => ({
7
+ message: {
8
+ status: { type: "running" as const },
9
+ content: [{ type: "text" as const, text: "streaming" }],
10
+ },
11
+ }));
12
+
13
+ vi.mock("@assistant-ui/store", async (importOriginal) => ({
14
+ ...(await importOriginal<typeof import("@assistant-ui/store")>()),
15
+ useAuiState: (selector: (state: { message: typeof message }) => unknown) =>
16
+ selector({ message }),
17
+ }));
18
+
19
+ vi.mock("@assistant-ui/store/internal", async (importOriginal) => ({
20
+ ...(await importOriginal<typeof import("@assistant-ui/store/internal")>()),
21
+ useShallowSelector: <T,>(selector: T) => selector,
22
+ }));
23
+
24
+ import { unstable_useMessageStallDetection } from "./useMessageStallDetection";
25
+
26
+ describe("unstable_useMessageStallDetection", () => {
27
+ afterEach(() => {
28
+ vi.restoreAllMocks();
29
+ });
30
+
31
+ it("does not read the wall clock during server render", () => {
32
+ const nowSpy = vi.spyOn(Date, "now");
33
+ const Probe = () => (
34
+ <span>{String(unstable_useMessageStallDetection().stalled)}</span>
35
+ );
36
+
37
+ expect(renderToString(<Probe />)).toBe("<span>false</span>");
38
+ expect(nowSpy).not.toHaveBeenCalled();
39
+ });
40
+ });
@@ -57,9 +57,9 @@ export function unstable_useMessageStallDetection(
57
57
  );
58
58
 
59
59
  const running = activity[0] === true;
60
- const lastActivityRef = useRef(Date.now());
60
+ const lastActivityRef = useRef(0);
61
61
  const [stalled, setStalled] = useState(false);
62
- const [, setTick] = useState(0);
62
+ const [now, setNow] = useState(0);
63
63
 
64
64
  useEffect(() => {
65
65
  if (!running) return undefined;
@@ -73,26 +73,30 @@ export function unstable_useMessageStallDetection(
73
73
  return undefined;
74
74
  }
75
75
 
76
+ const stall = () => {
77
+ setNow(Date.now());
78
+ setStalled(true);
79
+ };
76
80
  const sinceActivity = Date.now() - lastActivityRef.current;
77
81
  if (sinceActivity >= thresholdMs) {
78
- setStalled(true);
82
+ stall();
79
83
  return undefined;
80
84
  }
81
85
 
82
86
  setStalled(false);
83
- const id = setTimeout(() => setStalled(true), thresholdMs - sinceActivity);
87
+ const id = setTimeout(stall, thresholdMs - sinceActivity);
84
88
  return () => clearTimeout(id);
85
89
  }, [running, activity, thresholdMs]);
86
90
 
87
91
  useEffect(() => {
88
92
  if (!stalled) return undefined;
89
- const id = setInterval(() => setTick((t) => t + 1), 1000);
93
+ const id = setInterval(() => setNow(Date.now()), 1000);
90
94
  return () => clearInterval(id);
91
95
  }, [stalled]);
92
96
 
93
97
  if (!stalled) return { stalled: false, stalledForMs: 0 };
94
98
  return {
95
99
  stalled: true,
96
- stalledForMs: Math.max(0, Date.now() - lastActivityRef.current),
100
+ stalledForMs: Math.max(0, now - lastActivityRef.current),
97
101
  };
98
102
  }
@@ -1,5 +1,6 @@
1
1
  /** @vitest-environment jsdom */
2
2
  import { act, renderHook } from "@testing-library/react";
3
+ import { renderToString } from "react-dom/server";
3
4
  import { afterEach, describe, expect, it, vi } from "vitest";
4
5
  import type {
5
6
  MessagePartState,
@@ -68,7 +69,7 @@ const driveAndCount = (minCommitMs: number) => {
68
69
  .spyOn(globalThis, "cancelAnimationFrame")
69
70
  .mockImplementation(() => {});
70
71
  let now = 1000;
71
- const nowSpy = vi.spyOn(Date, "now").mockImplementation(() => now);
72
+ const nowSpy = vi.spyOn(performance, "now").mockImplementation(() => now);
72
73
 
73
74
  const state = runningState("0123456789");
74
75
  const { result, unmount } = renderHook(() =>
@@ -105,6 +106,16 @@ describe("useSmooth", () => {
105
106
  Reflect.deleteProperty(window, "matchMedia");
106
107
  });
107
108
 
109
+ it("does not read the wall clock during server render", () => {
110
+ const nowSpy = vi.spyOn(Date, "now");
111
+ const Probe = () => (
112
+ <span>{useSmooth(runningState("streaming"), true).text}</span>
113
+ );
114
+
115
+ expect(renderToString(<Probe />)).toBe("<span></span>");
116
+ expect(nowSpy).not.toHaveBeenCalled();
117
+ });
118
+
108
119
  it("returns the input state unchanged when disabled", () => {
109
120
  const state = textState("hello");
110
121
  const { result } = renderHook(() => useSmooth(state, false));
@@ -189,8 +200,8 @@ describe("useSmooth", () => {
189
200
  return raf.length;
190
201
  });
191
202
  vi.spyOn(globalThis, "cancelAnimationFrame").mockImplementation(() => {});
192
- let now = 1_000_000;
193
- vi.spyOn(Date, "now").mockImplementation(() => now);
203
+ let now = 0;
204
+ vi.spyOn(performance, "now").mockImplementation(() => now);
194
205
 
195
206
  const frame = () => {
196
207
  if (raf.length === 0) return;
@@ -246,7 +257,7 @@ describe("useSmooth", () => {
246
257
  });
247
258
  vi.spyOn(globalThis, "cancelAnimationFrame").mockImplementation(() => {});
248
259
  let now = 1000;
249
- vi.spyOn(Date, "now").mockImplementation(() => now);
260
+ vi.spyOn(performance, "now").mockImplementation(() => now);
250
261
 
251
262
  const frame = () => {
252
263
  if (raf.length === 0) return;
@@ -297,7 +308,7 @@ describe("useSmooth", () => {
297
308
  });
298
309
  vi.spyOn(globalThis, "cancelAnimationFrame").mockImplementation(() => {});
299
310
  let now = 1000;
300
- vi.spyOn(Date, "now").mockImplementation(() => now);
311
+ vi.spyOn(performance, "now").mockImplementation(() => now);
301
312
 
302
313
  const frame = () => {
303
314
  if (raf.length === 0) return;
@@ -49,8 +49,8 @@ const DEFAULT_MAX_CHAR_INTERVAL_MS = 5;
49
49
 
50
50
  class TextStreamAnimator {
51
51
  private animationFrameId: number | null = null;
52
- private lastUpdateTime: number = Date.now();
53
- public lastCommitTime: number = 0;
52
+ private lastUpdateTime: number = 0;
53
+ public lastCommitTime: number = -Infinity;
54
54
 
55
55
  public targetText: string = "";
56
56
  public drainMs: number = DEFAULT_DRAIN_MS;
@@ -68,7 +68,7 @@ class TextStreamAnimator {
68
68
 
69
69
  start() {
70
70
  if (this.animationFrameId !== null) return;
71
- this.lastUpdateTime = Date.now();
71
+ this.lastUpdateTime = performance.now();
72
72
  this.animate();
73
73
  }
74
74
 
@@ -80,7 +80,7 @@ class TextStreamAnimator {
80
80
  }
81
81
 
82
82
  private animate = () => {
83
- const currentTime = Date.now();
83
+ const currentTime = performance.now();
84
84
  const deltaTime = currentTime - this.lastUpdateTime;
85
85
  let timeToConsume = deltaTime;
86
86
 
@@ -214,8 +214,8 @@ export const useSmooth = (
214
214
  }
215
215
  }, [smoothStatusStore, enabled, text, displayedText, state.status]);
216
216
 
217
- const [animatorRef] = useState<TextStreamAnimator>(
218
- new TextStreamAnimator(displayedText, setText),
217
+ const [animatorRef] = useState(
218
+ () => new TextStreamAnimator(displayedText, setText),
219
219
  );
220
220
 
221
221
  useEffect(() => {
@@ -240,7 +240,7 @@ export const useSmooth = (
240
240
  if (state.status.type === "running") {
241
241
  animatorRef.currentText = "";
242
242
  animatorRef.targetText = text;
243
- animatorRef.lastCommitTime = 0;
243
+ animatorRef.lastCommitTime = -Infinity;
244
244
  animatorRef.start();
245
245
  } else {
246
246
  animatorRef.currentText = text;