@copilotkit/react-core 1.56.4-canary.1777529757 → 1.56.4-canary.1777538870
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/dist/{copilotkit-DAatqMh2.mjs → copilotkit-Bd0m5HFp.mjs} +80 -97
- package/dist/copilotkit-Bd0m5HFp.mjs.map +1 -0
- package/dist/copilotkit-DFaI4j2r.d.mts.map +1 -1
- package/dist/copilotkit-Dg4r4Gi_.d.cts.map +1 -1
- package/dist/{copilotkit-BAkj3zUc.cjs → copilotkit-tb4zqaMK.cjs} +80 -97
- package/dist/copilotkit-tb4zqaMK.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.css +1 -1
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/index.umd.js +82 -99
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +8 -8
- package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +113 -3
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +6 -26
- package/src/v2/components/chat/CopilotChatUserMessage.tsx +2 -2
- package/src/v2/components/chat/CopilotChatView.tsx +13 -28
- package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.tsx +0 -92
- package/dist/copilotkit-BAkj3zUc.cjs.map +0 -1
- package/dist/copilotkit-DAatqMh2.mjs.map +0 -1
- package/src/v2/components/chat/Lightbox.tsx +0 -103
- package/src/v2/components/chat/__tests__/CopilotChat.suggestionsAlways.test.tsx +0 -189
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@copilotkit/react-core",
|
|
3
|
-
"version": "1.56.4-canary.
|
|
3
|
+
"version": "1.56.4-canary.1777538870",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@ag-ui/client": "0.0.
|
|
56
|
-
"@ag-ui/core": "0.0.
|
|
55
|
+
"@ag-ui/client": "0.0.52",
|
|
56
|
+
"@ag-ui/core": "0.0.52",
|
|
57
57
|
"@jetbrains/websandbox": "^1.1.3",
|
|
58
58
|
"@lit-labs/react": "^2.0.2",
|
|
59
59
|
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
|
@@ -73,11 +73,11 @@
|
|
|
73
73
|
"untruncate-json": "^0.0.1",
|
|
74
74
|
"use-stick-to-bottom": "^1.1.1",
|
|
75
75
|
"zod-to-json-schema": "^3.24.5",
|
|
76
|
-
"@copilotkit/
|
|
77
|
-
"@copilotkit/
|
|
78
|
-
"@copilotkit/
|
|
79
|
-
"@copilotkit/
|
|
80
|
-
"@copilotkit/
|
|
76
|
+
"@copilotkit/core": "1.56.4-canary.1777538870",
|
|
77
|
+
"@copilotkit/runtime-client-gql": "1.56.4-canary.1777538870",
|
|
78
|
+
"@copilotkit/shared": "1.56.4-canary.1777538870",
|
|
79
|
+
"@copilotkit/a2ui-renderer": "1.56.4-canary.1777538870",
|
|
80
|
+
"@copilotkit/web-inspector": "1.56.4-canary.1777538870"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@tailwindcss/cli": "^4.1.11",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
|
1
|
+
import React, { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
2
|
+
import { createPortal, flushSync } from "react-dom";
|
|
2
3
|
import type { Attachment } from "@copilotkit/shared";
|
|
3
4
|
import {
|
|
4
5
|
formatFileSize,
|
|
5
6
|
getSourceUrl,
|
|
6
7
|
getDocumentIcon,
|
|
7
8
|
} from "@copilotkit/shared";
|
|
8
|
-
import { Play } from "lucide-react";
|
|
9
|
+
import { Play, X } from "lucide-react";
|
|
9
10
|
import { cn } from "../../lib/utils";
|
|
10
|
-
import { Lightbox, useLightbox } from "./Lightbox";
|
|
11
11
|
|
|
12
12
|
interface CopilotChatAttachmentQueueProps {
|
|
13
13
|
attachments: Attachment[];
|
|
@@ -88,6 +88,116 @@ function AttachmentPreview({ attachment }: { attachment: Attachment }) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Lightbox – fullscreen overlay for images and videos (portal to body)
|
|
93
|
+
// Uses the View Transition API when available for a smooth thumbnail-to-
|
|
94
|
+
// fullscreen morph; falls back to a simple opacity fade.
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
interface LightboxProps {
|
|
98
|
+
onClose: () => void;
|
|
99
|
+
children: React.ReactNode;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function Lightbox({ onClose, children }: LightboxProps) {
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
const handleKey = (e: KeyboardEvent) => {
|
|
105
|
+
if (e.key === "Escape") onClose();
|
|
106
|
+
};
|
|
107
|
+
document.addEventListener("keydown", handleKey);
|
|
108
|
+
return () => document.removeEventListener("keydown", handleKey);
|
|
109
|
+
}, [onClose]);
|
|
110
|
+
|
|
111
|
+
if (typeof document === "undefined") return null;
|
|
112
|
+
|
|
113
|
+
return createPortal(
|
|
114
|
+
<div
|
|
115
|
+
className="cpk:fixed cpk:inset-0 cpk:z-[9999] cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/80 cpk:animate-fade-in"
|
|
116
|
+
onClick={onClose}
|
|
117
|
+
>
|
|
118
|
+
<button
|
|
119
|
+
onClick={onClose}
|
|
120
|
+
className="cpk:absolute cpk:top-4 cpk:right-4 cpk:text-white cpk:bg-white/10 cpk:hover:bg-white/20 cpk:rounded-full cpk:w-10 cpk:h-10 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:border-none cpk:z-10"
|
|
121
|
+
aria-label="Close preview"
|
|
122
|
+
>
|
|
123
|
+
<X className="cpk:w-5 cpk:h-5" />
|
|
124
|
+
</button>
|
|
125
|
+
|
|
126
|
+
<div onClick={(e) => e.stopPropagation()}>{children}</div>
|
|
127
|
+
</div>,
|
|
128
|
+
document.body,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type DocWithVT = Document & {
|
|
133
|
+
startViewTransition?: (cb: () => void) => { finished: Promise<void> };
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Hook that manages lightbox open/close and uses the View Transition API to
|
|
138
|
+
* morph the thumbnail into fullscreen content.
|
|
139
|
+
*
|
|
140
|
+
* The trick: `view-transition-name` must live on exactly ONE element at a time.
|
|
141
|
+
* - Old state (thumbnail visible): name is on the thumbnail.
|
|
142
|
+
* - New state (lightbox visible): name moves to the lightbox content.
|
|
143
|
+
* `flushSync` ensures React commits the DOM change synchronously inside the
|
|
144
|
+
* `startViewTransition` callback so the API can snapshot old → new correctly.
|
|
145
|
+
*/
|
|
146
|
+
function useLightbox() {
|
|
147
|
+
const thumbnailRef = useRef<HTMLElement>(null);
|
|
148
|
+
const [open, setOpen] = useState(false);
|
|
149
|
+
const vtName = useId();
|
|
150
|
+
|
|
151
|
+
const openLightbox = useCallback(() => {
|
|
152
|
+
const thumb = thumbnailRef.current;
|
|
153
|
+
const doc = document as DocWithVT;
|
|
154
|
+
|
|
155
|
+
if (doc.startViewTransition && thumb) {
|
|
156
|
+
// Old snapshot: name on the thumbnail
|
|
157
|
+
thumb.style.viewTransitionName = vtName;
|
|
158
|
+
|
|
159
|
+
doc.startViewTransition(() => {
|
|
160
|
+
// New snapshot: remove from thumb (lightbox content will have it)
|
|
161
|
+
thumb.style.viewTransitionName = "";
|
|
162
|
+
flushSync(() => setOpen(true));
|
|
163
|
+
});
|
|
164
|
+
} else {
|
|
165
|
+
setOpen(true);
|
|
166
|
+
}
|
|
167
|
+
}, []);
|
|
168
|
+
|
|
169
|
+
const closeLightbox = useCallback(() => {
|
|
170
|
+
const thumb = thumbnailRef.current;
|
|
171
|
+
const doc = document as DocWithVT;
|
|
172
|
+
|
|
173
|
+
if (doc.startViewTransition && thumb) {
|
|
174
|
+
const transition = doc.startViewTransition(() => {
|
|
175
|
+
// New snapshot: name back on thumbnail
|
|
176
|
+
flushSync(() => setOpen(false));
|
|
177
|
+
thumb.style.viewTransitionName = vtName;
|
|
178
|
+
});
|
|
179
|
+
// Clean up the name after animation finishes (or fails)
|
|
180
|
+
transition.finished
|
|
181
|
+
.then(() => {
|
|
182
|
+
thumb.style.viewTransitionName = "";
|
|
183
|
+
})
|
|
184
|
+
.catch(() => {
|
|
185
|
+
thumb.style.viewTransitionName = "";
|
|
186
|
+
});
|
|
187
|
+
} else {
|
|
188
|
+
setOpen(false);
|
|
189
|
+
}
|
|
190
|
+
}, []);
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
thumbnailRef,
|
|
194
|
+
vtName,
|
|
195
|
+
open,
|
|
196
|
+
openLightbox,
|
|
197
|
+
closeLightbox,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
91
201
|
// ---------------------------------------------------------------------------
|
|
92
202
|
// Image
|
|
93
203
|
// ---------------------------------------------------------------------------
|
|
@@ -2,7 +2,6 @@ import React, { memo, useState } from "react";
|
|
|
2
2
|
import type { InputContentSource } from "@copilotkit/shared";
|
|
3
3
|
import { getSourceUrl, getDocumentIcon } from "@copilotkit/shared";
|
|
4
4
|
import { cn } from "../../lib/utils";
|
|
5
|
-
import { Lightbox, useLightbox } from "./Lightbox";
|
|
6
5
|
|
|
7
6
|
interface CopilotChatAttachmentRendererProps {
|
|
8
7
|
type: "image" | "audio" | "video" | "document";
|
|
@@ -19,8 +18,6 @@ const ImageAttachment = memo(function ImageAttachment({
|
|
|
19
18
|
className?: string;
|
|
20
19
|
}) {
|
|
21
20
|
const [error, setError] = useState(false);
|
|
22
|
-
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } =
|
|
23
|
-
useLightbox();
|
|
24
21
|
|
|
25
22
|
if (error) {
|
|
26
23
|
return (
|
|
@@ -36,29 +33,12 @@ const ImageAttachment = memo(function ImageAttachment({
|
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
return (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"cpk:max-w-[80px] cpk:max-h-[80px] cpk:w-auto cpk:h-auto cpk:rounded-xl cpk:object-cover cpk:cursor-pointer cpk:bg-muted",
|
|
46
|
-
className,
|
|
47
|
-
)}
|
|
48
|
-
onClick={openLightbox}
|
|
49
|
-
onError={() => setError(true)}
|
|
50
|
-
/>
|
|
51
|
-
{open && (
|
|
52
|
-
<Lightbox onClose={closeLightbox}>
|
|
53
|
-
<img
|
|
54
|
-
style={{ viewTransitionName: vtName }}
|
|
55
|
-
src={src}
|
|
56
|
-
alt="Image attachment"
|
|
57
|
-
className="cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
|
|
58
|
-
/>
|
|
59
|
-
</Lightbox>
|
|
60
|
-
)}
|
|
61
|
-
</>
|
|
36
|
+
<img
|
|
37
|
+
src={src}
|
|
38
|
+
alt="Image attachment"
|
|
39
|
+
className={cn("cpk:max-w-full cpk:h-auto cpk:rounded-lg", className)}
|
|
40
|
+
onError={() => setError(true)}
|
|
41
|
+
/>
|
|
62
42
|
);
|
|
63
43
|
});
|
|
64
44
|
|
|
@@ -217,8 +217,9 @@ export function CopilotChatUserMessage({
|
|
|
217
217
|
data-message-id={message.id}
|
|
218
218
|
{...props}
|
|
219
219
|
>
|
|
220
|
+
{BoundMessageRenderer}
|
|
220
221
|
{mediaParts.length > 0 && (
|
|
221
|
-
<div className="cpk:flex cpk:flex-
|
|
222
|
+
<div className="cpk:flex cpk:flex-col cpk:items-end cpk:gap-2 cpk:mt-2">
|
|
222
223
|
{mediaParts.map((part, index) => (
|
|
223
224
|
<CopilotChatAttachmentRenderer
|
|
224
225
|
key={index}
|
|
@@ -229,7 +230,6 @@ export function CopilotChatUserMessage({
|
|
|
229
230
|
))}
|
|
230
231
|
</div>
|
|
231
232
|
)}
|
|
232
|
-
{BoundMessageRenderer}
|
|
233
233
|
{BoundToolbar}
|
|
234
234
|
</div>
|
|
235
235
|
);
|
|
@@ -167,17 +167,7 @@ export function CopilotChatView({
|
|
|
167
167
|
className,
|
|
168
168
|
...props
|
|
169
169
|
}: CopilotChatViewProps) {
|
|
170
|
-
|
|
171
|
-
// chat-view branch (the welcome-screen branch omits it), so a plain
|
|
172
|
-
// useRef + `[]` useEffect would observe `null` on mount whenever the chat
|
|
173
|
-
// starts on the welcome screen and never re-attach after the user sends
|
|
174
|
-
// their first message — leaving inputContainerHeight at 0 and the scroll
|
|
175
|
-
// content's reserved bottom padding at 32px instead of ~input height. The
|
|
176
|
-
// result is the last messages scrolling underneath the absolute-positioned
|
|
177
|
-
// input pill. Subscribing to element state lets the observer attach (and
|
|
178
|
-
// detach) reactively as the overlay mounts/unmounts.
|
|
179
|
-
const [inputContainerEl, setInputContainerEl] =
|
|
180
|
-
useState<HTMLDivElement | null>(null);
|
|
170
|
+
const inputContainerRef = useRef<HTMLDivElement>(null);
|
|
181
171
|
const [inputContainerHeight, setInputContainerHeight] = useState(0);
|
|
182
172
|
const [isResizing, setIsResizing] = useState(false);
|
|
183
173
|
const resizeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
@@ -188,14 +178,8 @@ export function CopilotChatView({
|
|
|
188
178
|
|
|
189
179
|
// Track input container height changes
|
|
190
180
|
useEffect(() => {
|
|
191
|
-
const element =
|
|
192
|
-
if (!element)
|
|
193
|
-
// Reset measured height so the scroll content's paddingBottom doesn't
|
|
194
|
-
// hold a stale value if the overlay unmounts (e.g. messages cleared
|
|
195
|
-
// and the welcome screen returns).
|
|
196
|
-
setInputContainerHeight(0);
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
181
|
+
const element = inputContainerRef.current;
|
|
182
|
+
if (!element) return;
|
|
199
183
|
|
|
200
184
|
const resizeObserver = new ResizeObserver((entries) => {
|
|
201
185
|
for (const entry of entries) {
|
|
@@ -234,7 +218,7 @@ export function CopilotChatView({
|
|
|
234
218
|
clearTimeout(resizeTimeoutRef.current);
|
|
235
219
|
}
|
|
236
220
|
};
|
|
237
|
-
}, [
|
|
221
|
+
}, []);
|
|
238
222
|
|
|
239
223
|
const BoundMessageView = renderSlot(messageView, CopilotChatMessageView, {
|
|
240
224
|
messages,
|
|
@@ -264,14 +248,15 @@ export function CopilotChatView({
|
|
|
264
248
|
...(disclaimer !== undefined ? { disclaimer } : {}),
|
|
265
249
|
} as CopilotChatInputProps);
|
|
266
250
|
|
|
267
|
-
// Hide suggestions while a thread is connecting
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
// only "always" ones are restored — so a non-empty `suggestions` array
|
|
272
|
-
// here already means "this is something the user opted to keep visible."
|
|
251
|
+
// Hide suggestions while a thread is connecting or a run is in flight.
|
|
252
|
+
// Otherwise, mid-replay (bootstrap stream from /connect) or mid-run, the
|
|
253
|
+
// suggestions would render against a still-assembling message tree and
|
|
254
|
+
// visibly jump as each final text chunk reflows the layout.
|
|
273
255
|
const hasSuggestions =
|
|
274
|
-
!isConnecting &&
|
|
256
|
+
!isConnecting &&
|
|
257
|
+
!isRunning &&
|
|
258
|
+
Array.isArray(suggestions) &&
|
|
259
|
+
suggestions.length > 0;
|
|
275
260
|
const BoundSuggestionView = hasSuggestions
|
|
276
261
|
? renderSlot(suggestionView, CopilotChatSuggestionView, {
|
|
277
262
|
suggestions,
|
|
@@ -413,7 +398,7 @@ export function CopilotChatView({
|
|
|
413
398
|
{BoundScrollView}
|
|
414
399
|
|
|
415
400
|
<div
|
|
416
|
-
ref={
|
|
401
|
+
ref={inputContainerRef}
|
|
417
402
|
data-testid="copilot-input-overlay"
|
|
418
403
|
className="cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-0 cpk:z-20 cpk:pointer-events-none"
|
|
419
404
|
>
|
|
@@ -6,7 +6,6 @@ import { CopilotChatConfigurationProvider } from "../../../providers/CopilotChat
|
|
|
6
6
|
import { CopilotChatView } from "../CopilotChatView";
|
|
7
7
|
import { LastUserMessageContext } from "../last-user-message-context";
|
|
8
8
|
import type { Attachment } from "@copilotkit/shared";
|
|
9
|
-
import type { Message } from "@ag-ui/core";
|
|
10
9
|
|
|
11
10
|
beforeEach(() => {
|
|
12
11
|
HTMLElement.prototype.scrollTo = vi.fn();
|
|
@@ -170,95 +169,4 @@ describe("CopilotChatView input overlay layout", () => {
|
|
|
170
169
|
(global as any).ResizeObserver = OriginalRO;
|
|
171
170
|
}
|
|
172
171
|
});
|
|
173
|
-
|
|
174
|
-
it("attaches the resize observer when transitioning from welcome to chat view", async () => {
|
|
175
|
-
// Regression: a `[]`-deps useEffect captured `inputContainerRef.current`
|
|
176
|
-
// as null when mounted on the welcome screen and never re-ran after the
|
|
177
|
-
// user sent their first message. The overlay rendered without a measured
|
|
178
|
-
// height, so paddingBottom stayed at 32 and the last messages slid
|
|
179
|
-
// underneath the absolute-positioned input pill. Verify the observer
|
|
180
|
-
// attaches reactively when the overlay mounts post-transition.
|
|
181
|
-
const callbacks: Array<{
|
|
182
|
-
cb: ResizeObserverCallback;
|
|
183
|
-
target: Element | null;
|
|
184
|
-
}> = [];
|
|
185
|
-
const OriginalRO = global.ResizeObserver;
|
|
186
|
-
class MockResizeObserver {
|
|
187
|
-
private cb: ResizeObserverCallback;
|
|
188
|
-
constructor(cb: ResizeObserverCallback) {
|
|
189
|
-
this.cb = cb;
|
|
190
|
-
}
|
|
191
|
-
observe(target: Element) {
|
|
192
|
-
callbacks.push({ cb: this.cb, target });
|
|
193
|
-
}
|
|
194
|
-
unobserve() {}
|
|
195
|
-
disconnect() {}
|
|
196
|
-
}
|
|
197
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
198
|
-
(global as any).ResizeObserver = MockResizeObserver as any;
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
// Render with no messages to start on the welcome screen branch — the
|
|
202
|
-
// overlay wrapper does not exist in this DOM, so the observer cannot
|
|
203
|
-
// attach yet.
|
|
204
|
-
const initialMessages: Message[] = [];
|
|
205
|
-
const screen = render(
|
|
206
|
-
<TestWrapper>
|
|
207
|
-
<LastUserMessageContext.Provider value={{ id: null, sendNonce: 0 }}>
|
|
208
|
-
<CopilotChatView messages={initialMessages} />
|
|
209
|
-
</LastUserMessageContext.Provider>
|
|
210
|
-
</TestWrapper>,
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
await screen.findByTestId("copilot-welcome-screen");
|
|
214
|
-
expect(screen.queryByTestId("copilot-input-overlay")).toBeNull();
|
|
215
|
-
|
|
216
|
-
// Transition to the chat view by re-rendering with messages — mirrors
|
|
217
|
-
// what happens when CopilotChat re-renders after the user submits.
|
|
218
|
-
screen.rerender(
|
|
219
|
-
<TestWrapper>
|
|
220
|
-
<LastUserMessageContext.Provider value={{ id: null, sendNonce: 0 }}>
|
|
221
|
-
<CopilotChatView messages={sampleMessages} />
|
|
222
|
-
</LastUserMessageContext.Provider>
|
|
223
|
-
</TestWrapper>,
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
await waitForMount(screen);
|
|
227
|
-
const overlay = screen.getByTestId("copilot-input-overlay");
|
|
228
|
-
|
|
229
|
-
// The bug: observer was attached at mount when the overlay element was
|
|
230
|
-
// null, so it never re-attached after the transition. Verify it now
|
|
231
|
-
// observes the overlay specifically.
|
|
232
|
-
await waitFor(() =>
|
|
233
|
-
expect(callbacks.some(({ target }) => target === overlay)).toBe(true),
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
const scrollContent = screen.getByTestId("copilot-scroll-content");
|
|
237
|
-
// Simulate the overlay reporting a real height (e.g. 88px input pill).
|
|
238
|
-
// Only fire on the overlay's own observer — other components (e.g. the
|
|
239
|
-
// textarea autosize) also use ResizeObserver and would corrupt the
|
|
240
|
-
// assertion if we fed all observers a 88px contentRect.
|
|
241
|
-
for (const { cb, target } of callbacks) {
|
|
242
|
-
if (target !== overlay) continue;
|
|
243
|
-
cb(
|
|
244
|
-
[
|
|
245
|
-
{
|
|
246
|
-
contentRect: { height: 88 } as DOMRectReadOnly,
|
|
247
|
-
} as ResizeObserverEntry,
|
|
248
|
-
],
|
|
249
|
-
{} as ResizeObserver,
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// 88 (input) + 32 (no suggestions baseline) = 120px. Without the fix,
|
|
254
|
-
// paddingBottom would be stuck at 32px because the observer never
|
|
255
|
-
// attached.
|
|
256
|
-
await waitFor(() =>
|
|
257
|
-
expect(scrollContent.style.paddingBottom).toBe("120px"),
|
|
258
|
-
);
|
|
259
|
-
} finally {
|
|
260
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
261
|
-
(global as any).ResizeObserver = OriginalRO;
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
172
|
});
|