-
-
Notifications
You must be signed in to change notification settings - Fork 7
Auto-convert large pasted text to files #474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Auto-convert large pasted text to files #474
Conversation
This change implements a UX improvement where pasted text exceeding 5,000 characters is automatically converted into a `.txt` file attachment named `pasted-text.txt`. Key changes: - Added `onPaste` handler to the chat input `Textarea`. - Implemented character threshold (5,000) for auto-conversion. - Added collision handling: if a file is already attached, the large paste is prevented and a toast notification is shown. - Updated file size validation to use `sonner` toasts instead of browser alerts for consistency. - Ensured `e.preventDefault()` is called for large pastes to prevent UI clogging. - Verified functionality with targeted Playwright tests and frontend screenshots. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
WalkthroughReplaces browser alerts with Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Textarea as Textarea (ChatPanel)
participant ChatPanel as ChatPanel State
participant FileAPI as Browser File API
participant Toast as sonner.toast
User->>Textarea: Paste text
Textarea->>ChatPanel: onPaste event (clipboardData)
ChatPanel->>ChatPanel: read clipboard text, check length (>500?) and check existing attachment
alt no attachment & text > 500 chars
ChatPanel->>FileAPI: create File("pasted-text.txt") (ensure <=10MB)
FileAPI-->>ChatPanel: File object
ChatPanel->>ChatPanel: set attachment state, clear input
else attachment exists
ChatPanel->>Toast: toast.error("Please remove the current attachment")
ChatPanel-->>Textarea: prevent default paste
else text <= 500 chars
ChatPanel-->>Textarea: allow default paste (no file created)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The core UX behavior looks correct, but the new paste-to-file path currently bypasses existing file-size limits, allowing very large clipboard content to be converted into an attachment. Additionally, the paste handler should avoid interfering with non-text clipboard payloads and should not rely on an inline magic number for the threshold. Addressing these will improve robustness and prevent memory/performance issues.
Summary of changes
Summary
This PR improves chat input UX by converting large pasted text into a file attachment and modernizing validation messaging.
Key updates
- Added
sonnertoast notifications viaimport { toast } from 'sonner'. - Replaced the 10MB attachment validation
alert(...)withtoast.error(...). - Introduced an
onPastehandler on the chatTextareato:- Detect large clipboard text (
> 5000chars) preventDefault()to avoid inserting huge text into the textarea- Create a
Filenamedpasted-text.txtand attach it when no attachment exists - Show an error toast when an attachment already exists (collision handling)
- Detect large clipboard text (
- Removed
server.logfrom the repository.
| const handlePaste = (e: React.ClipboardEvent) => { | ||
| const pastedText = e.clipboardData.getData('text') | ||
| if (pastedText.length > 5000) { | ||
| e.preventDefault() | ||
| if (selectedFile) { | ||
| toast.error( | ||
| 'Please remove the current attachment to convert large paste to file' | ||
| ) | ||
| return | ||
| } | ||
| const file = new File([pastedText], 'pasted-text.txt', { | ||
| type: 'text/plain' | ||
| }) | ||
| setSelectedFile(file) | ||
| setInput('') | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The paste threshold (5000) is a magic number here. It should be extracted into a named constant (ideally colocated with related upload/validation constants) so it’s easier to discover, adjust, and test consistently across the codebase.
Suggestion
Extract the threshold into a constant (and consider reusing it in tests).
const LARGE_PASTE_CHAR_THRESHOLD = 5000
const handlePaste = (e: React.ClipboardEvent) => {
const pastedText = e.clipboardData.getData('text')
if (pastedText.length > LARGE_PASTE_CHAR_THRESHOLD) {
// ...
}
}Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
| const handlePaste = (e: React.ClipboardEvent) => { | ||
| const pastedText = e.clipboardData.getData('text') | ||
| if (pastedText.length > 5000) { | ||
| e.preventDefault() | ||
| if (selectedFile) { | ||
| toast.error( | ||
| 'Please remove the current attachment to convert large paste to file' | ||
| ) | ||
| return | ||
| } | ||
| const file = new File([pastedText], 'pasted-text.txt', { | ||
| type: 'text/plain' | ||
| }) | ||
| setSelectedFile(file) | ||
| setInput('') | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handlePaste always reads text from the clipboard. In some browsers/contexts, a paste may primarily contain files/HTML, and forcing getData('text') + large-text prevention could accidentally interfere with non-text pastes. Consider guarding so this logic only runs when there is meaningful plain-text content and otherwise lets the default paste behavior proceed.
Suggestion
Add conservative guards so you only intercept large plain-text pastes and do not block other clipboard payloads.
const handlePaste = (e: React.ClipboardEvent) => {
const text = e.clipboardData.getData('text/plain')
if (!text) return
if (text.length > LARGE_PASTE_CHAR_THRESHOLD) {
e.preventDefault()
if (selectedFile) {
toast.error('Please remove the current attachment to convert large paste to file')
return
}
setSelectedFile(new File([text], 'pasted-text.txt', { type: 'text/plain' }))
setInput('')
}
}Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
| const handlePaste = (e: React.ClipboardEvent) => { | ||
| const pastedText = e.clipboardData.getData('text') | ||
| if (pastedText.length > 5000) { | ||
| e.preventDefault() | ||
| if (selectedFile) { | ||
| toast.error( | ||
| 'Please remove the current attachment to convert large paste to file' | ||
| ) | ||
| return | ||
| } | ||
| const file = new File([pastedText], 'pasted-text.txt', { | ||
| type: 'text/plain' | ||
| }) | ||
| setSelectedFile(file) | ||
| setInput('') | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Converting pasted text into a File bypasses the existing 10MB check in handleFileChange. A user could paste extremely large text and end up with a huge in-memory File (and later potentially a failed upload/server rejection). You should apply the same size limit (and ideally share the constant) when creating the file from pasted content.
Suggestion
Reuse the same max-size validation for pasted-text files before calling setSelectedFile.
const MAX_FILE_BYTES = 10 * 1024 * 1024
// ... inside large paste branch
const file = new File([text], 'pasted-text.txt', { type: 'text/plain' })
if (file.size > MAX_FILE_BYTES) {
toast.error('File size must be less than 10MB')
return
}
setSelectedFile(file)
setInput('')Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@components/chat-panel.tsx`:
- Around line 90-93: The pasted-text File creation bypasses the same 10MB
validation used in handleFileChange; before calling setSelectedFile(file) in the
paste handling code that constructs File([pastedText], 'pasted-text.txt', ...),
check the byte size of pastedText (or file.size) against the existing maxSize
used by handleFileChange (10 * 1024 * 1024) and reject/notify the user the same
way handleFileChange does if it exceeds the limit so pasted files follow the
same size validation flow.
- Around line 80-96: Add a success toast when large pasted text is converted to
a File: inside handlePaste, after creating the File and calling
setSelectedFile(file) and setInput(''), call toast.success with a brief message
like "Pasted text attached as file" so the user sees the conversion. Also wrap
handlePaste in React.useCallback (same pattern as debouncedGetSuggestions) and
include its dependencies (selectedFile, setSelectedFile, setInput, toast) to
avoid unnecessary re-renders and keep prop stability for any memoized Textarea
component.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
server.logis excluded by!**/*.log
📒 Files selected for processing (1)
components/chat-panel.tsx
🔇 Additional comments (3)
components/chat-panel.tsx (3)
6-6: LGTM!The
toastimport fromsonneris correctly added to support the new toast-based feedback.
72-75: LGTM!Replacing the browser alert with
toast.erroris a good UX improvement — it's non-blocking and consistent with the new paste handling feedback.
271-271: LGTM!The
onPastehandler is correctly wired to the Textarea component.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| const handlePaste = (e: React.ClipboardEvent) => { | ||
| const pastedText = e.clipboardData.getData('text') | ||
| if (pastedText.length > 5000) { | ||
| e.preventDefault() | ||
| if (selectedFile) { | ||
| toast.error( | ||
| 'Please remove the current attachment to convert large paste to file' | ||
| ) | ||
| return | ||
| } | ||
| const file = new File([pastedText], 'pasted-text.txt', { | ||
| type: 'text/plain' | ||
| }) | ||
| setSelectedFile(file) | ||
| setInput('') | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider adding a success toast when paste is converted to file.
When large text is converted to a file, the user sees their input cleared and a file attachment appear. Adding a success toast would make this behavior more discoverable and less confusing.
Additionally, consider wrapping handlePaste in useCallback for consistency with debouncedGetSuggestions and to avoid unnecessary re-renders if the Textarea memoizes props.
♻️ Proposed enhancement
- const handlePaste = (e: React.ClipboardEvent) => {
+ const handlePaste = useCallback((e: React.ClipboardEvent) => {
const pastedText = e.clipboardData.getData('text')
if (pastedText.length > 5000) {
e.preventDefault()
if (selectedFile) {
toast.error(
'Please remove the current attachment to convert large paste to file'
)
return
}
const file = new File([pastedText], 'pasted-text.txt', {
type: 'text/plain'
})
setSelectedFile(file)
setInput('')
+ toast.success('Large text converted to file attachment')
}
- }
+ }, [selectedFile, setInput])🤖 Prompt for AI Agents
In `@components/chat-panel.tsx` around lines 80 - 96, Add a success toast when
large pasted text is converted to a File: inside handlePaste, after creating the
File and calling setSelectedFile(file) and setInput(''), call toast.success with
a brief message like "Pasted text attached as file" so the user sees the
conversion. Also wrap handlePaste in React.useCallback (same pattern as
debouncedGetSuggestions) and include its dependencies (selectedFile,
setSelectedFile, setInput, toast) to avoid unnecessary re-renders and keep prop
stability for any memoized Textarea component.
|
@coderabbitai the pasted texts supposed to turn into an attachment as soon as pasted leaving the chat input empty for follow up text |
I have implemented this behavior in my latest commit. When a text block exceeding 5,000 characters is pasted, it is now automatically converted into a |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 10540
Here's the corrected const handlePaste = (e: React.ClipboardEvent) => {
const pastedText = e.clipboardData.getData('text')
if (pastedText.length > 5000) {
e.preventDefault() // This prevents the paste from happening
if (selectedFile) {
toast.error(
'Please remove the current attachment to convert large paste to file'
)
return
}
const file = new File([pastedText], 'pasted-text.txt', {
type: 'text/plain'
})
setSelectedFile(file)
// Removed setInput('') - e.preventDefault() already stops the paste
}
}What this fixes:
This way, you can add context before or after attaching the large pasted text file! 🐰 Task: Fix handlePaste to preserve existing input textThe fix removes the unnecessary 💡 Use |
…andling and input clearing This commit ensures: - Large pastes (>5000 chars) are converted to a file attachment. - Chat input is cleared upon conversion to allow for follow-up text. - Paste is prevented (preventDefault) even on collision to keep UI clean. - All validation uses sonner toasts for consistent UI. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
|
@coderabbitai So this should immediately turn into a file as opposed to a long input where the chat input is empty and ready for an input |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 10540
The current code should work, but there's likely a race condition between React's paste and change events. Even with Here's a more robust solution using a ref to track when we're converting a large paste: const [selectedFile, setSelectedFile] = useState<File | null>(null)
const isConvertingPaste = useRef(false)
const handlePaste = (e: React.ClipboardEvent) => {
const pastedText = e.clipboardData.getData('text')
if (pastedText.length > 5000) {
e.preventDefault()
if (selectedFile) {
toast.error(
'Please remove the current attachment to convert large paste to file'
)
return
}
isConvertingPaste.current = true
const file = new File([pastedText], 'pasted-text.txt', {
type: 'text/plain'
})
setSelectedFile(file)
setInput('')
// Reset the flag after React processes the state update
setTimeout(() => {
isConvertingPaste.current = false
}, 0)
}
}
// In the onChange handler for the Textarea:
onChange={e => {
// Ignore input changes while converting a large paste
if (isConvertingPaste.current) {
return
}
setInput(e.target.value)
debouncedGetSuggestions(e.target.value)
}}What this fixes:
This should give you the exact behavior shown in your requirement: paste large text → instant file attachment → empty input ready for typing! 🐰 |
Understood! In the current implementation, the threshold is set to 5,000 characters. If the text in your screenshot is shorter than that, it will still appear in the input as shown. Would you like me to lower the threshold? For example, to 1,000 or 2,000 characters? Or should it trigger if the text is simply multi-line and exceeds the visible area of the input (e.g., more than 5 lines)? |
- Lowered the threshold for automatic file conversion from 5,000 to 1,000 characters to better catch long inputs. - Added a 10MB limit check for pasted content to ensure consistency with file upload limits. - Ensured input is cleared and default behavior is prevented on all large pastes. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@components/chat-panel.tsx`:
- Around line 84-87: The pasted-text size check uses pastedText.length (UTF-16
code units) instead of byte length; update the validation in the paste handling
code to compute the actual byte size (e.g., via
TextEncoder().encode(pastedText).length or new Blob([pastedText]).size) and
compare that byte length to 10 * 1024 * 1024 so it matches the behavior in
handleFileChange; modify the conditional that currently references
pastedText.length to use the computed byteSize and keep the same toast.error and
return flow if it exceeds the limit.
- Line 98: The call to setInput('') in the paste handler is clearing any
pre-typed user text; remove that setInput('') so the existing input is preserved
(locate the onPaste / paste handler in components/chat-panel.tsx and delete the
setInput('') statement), or if you must clear to avoid a race with onChange
implement the ref-based guard discussed in the PR: add a boolean ref (e.g.,
skipOnChangeRef) and set it around the paste flow, then check
skipOnChangeRef.current inside the onChange handler to ignore the spurious
update instead of wiping the input.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
components/chat-panel.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
components/chat-panel.tsx (1)
tests/images.spec.ts (2)
testInfo(124-139)testInfo(55-76)
🔇 Additional comments (3)
components/chat-panel.tsx (3)
6-6: LGTM!The toast import from sonner is correctly added for the new notification functionality.
72-75: LGTM!Good improvement replacing the browser alert with a toast notification for consistent UX across all validation messages.
275-275: LGTM!The paste handler is correctly bound to the Textarea.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| if (pastedText.length > 10 * 1024 * 1024) { | ||
| toast.error('Pasted text exceeds 10MB limit') | ||
| return | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Size check compares character count against byte threshold.
pastedText.length returns the number of characters (UTF-16 code units), not bytes. For multi-byte Unicode text, the actual byte size can be significantly larger than the character count. This check should validate byte size to be consistent with handleFileChange.
🐛 Proposed fix
const handlePaste = (e: React.ClipboardEvent) => {
const pastedText = e.clipboardData.getData('text')
if (pastedText.length > 1000) {
e.preventDefault()
- if (pastedText.length > 10 * 1024 * 1024) {
+ const byteSize = new TextEncoder().encode(pastedText).length
+ if (byteSize > 10 * 1024 * 1024) {
toast.error('Pasted text exceeds 10MB limit')
return
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (pastedText.length > 10 * 1024 * 1024) { | |
| toast.error('Pasted text exceeds 10MB limit') | |
| return | |
| } | |
| const byteSize = new TextEncoder().encode(pastedText).length | |
| if (byteSize > 10 * 1024 * 1024) { | |
| toast.error('Pasted text exceeds 10MB limit') | |
| return | |
| } |
🤖 Prompt for AI Agents
In `@components/chat-panel.tsx` around lines 84 - 87, The pasted-text size check
uses pastedText.length (UTF-16 code units) instead of byte length; update the
validation in the paste handling code to compute the actual byte size (e.g., via
TextEncoder().encode(pastedText).length or new Blob([pastedText]).size) and
compare that byte length to 10 * 1024 * 1024 so it matches the behavior in
handleFileChange; modify the conditional that currently references
pastedText.length to use the computed byteSize and keep the same toast.error and
return flow if it exceeds the limit.
| type: 'text/plain' | ||
| }) | ||
| setSelectedFile(file) | ||
| setInput('') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setInput('') clears any pre-typed text.
If a user types some context before pasting large text, this call erases their typed content. Per PR discussion, e.preventDefault() already stops the pasted text from being inserted, so clearing input may not be necessary.
Consider preserving existing input or showing a success toast so users understand why their input changed.
♻️ Option A: Remove setInput to preserve typed text
setSelectedFile(file)
- setInput('')
+ toast.success('Large text converted to file attachment')♻️ Option B: Add ref guard to prevent race condition (per PR discussion)
If you need to clear input to prevent race conditions with onChange, use a ref to guard against the change event:
+ const isConvertingPaste = useRef(false)
+
const handlePaste = (e: React.ClipboardEvent) => {
const pastedText = e.clipboardData.getData('text')
if (pastedText.length > 1000) {
e.preventDefault()
// ... size and attachment checks ...
+ isConvertingPaste.current = true
const file = new File([pastedText], 'pasted-text.txt', {
type: 'text/plain'
})
setSelectedFile(file)
setInput('')
+ toast.success('Large text converted to file attachment')
+ // Reset flag after state updates
+ setTimeout(() => { isConvertingPaste.current = false }, 0)
}
}Then guard the onChange:
onChange={e => {
+ if (isConvertingPaste.current) return
setInput(e.target.value)
debouncedGetSuggestions(e.target.value)
}}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| setInput('') | |
| setSelectedFile(file) | |
| toast.success('Large text converted to file attachment') |
🤖 Prompt for AI Agents
In `@components/chat-panel.tsx` at line 98, The call to setInput('') in the paste
handler is clearing any pre-typed user text; remove that setInput('') so the
existing input is preserved (locate the onPaste / paste handler in
components/chat-panel.tsx and delete the setInput('') statement), or if you must
clear to avoid a race with onChange implement the ref-based guard discussed in
the PR: add a boolean ref (e.g., skipOnChangeRef) and set it around the paste
flow, then check skipOnChangeRef.current inside the onChange handler to ignore
the spurious update instead of wiping the input.
|
@coderabbitai resolution search has stopped working |
|
Tip For best results, initiate chat on the files or code changes.
Once I understand what you're referring to, I can investigate the issue and help you resolve it! 🐰 |
- Intercept paste events in `ChatPanel` and convert text > 500 chars to `pasted-text.txt`. - Clear the textarea immediately upon conversion. - Unify toast notifications by switching from `react-toastify` to `sonner` in `ChatPanel` and `HeaderSearchButton`. - Add E2E tests for paste-to-file conversion logic. - Fix regression in Resolution Search by ensuring consistent notification library usage. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@tests/paste.spec.ts`:
- Around line 72-78: The second paste dispatch uses document.activeElement which
may not be the chat input after the first paste; before creating/dispatching the
ClipboardEvent for largeText2, explicitly focus the chat input element used
earlier (e.g., call page.focus(selector) or inside page.evaluate call
document.querySelector('<chat-input-selector>').focus()) so the paste event
targets the correct element instead of relying on document.activeElement; update
the block that constructs the DataTransfer/ClipboardEvent (the page.evaluate
invocation that uses largeText2) to focus the intended input element first.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
components/chat-panel.tsxcomponents/header-search-button.tsxtests/paste.spec.ts
🧰 Additional context used
🧬 Code graph analysis (2)
components/chat-panel.tsx (1)
tests/images.spec.ts (1)
testInfo(124-139)
components/header-search-button.tsx (4)
components/ui/sonner.tsx (1)
props(8-29)components/ui/toaster.tsx (2)
Toaster(6-26)Toast(11-22)components/ui/hooks/use-toast.tsx (4)
useToast(36-89)toasts(60-66)setToasts(40-42)toastId(71-82)components/ui/toast.tsx (3)
props(94-96)props(45-47)props(68-80)
🔇 Additional comments (5)
components/header-search-button.tsx (1)
12-12: LGTM! Toast library standardization.Switching from
react-toastifytosonneraligns with the existingsonnersetup incomponents/ui/sonner.tsx. Thetoast.error()API is compatible, and no other changes are needed.tests/paste.spec.ts (1)
1-82: Good test coverage for paste-to-file feature.The tests cover the three key scenarios: large paste conversion, small paste passthrough, and collision handling. The use of
DataTransferandClipboardEventto simulate paste events is appropriate for E2E testing.components/chat-panel.tsx (3)
6-6: LGTM! Toast library import.Consistent with the standardization to
sonneracross the codebase.
69-78: LGTM! File size validation with toast.The 10MB limit check and toast error notification are correctly implemented for file uploads.
275-275: LGTM! onPaste handler binding.Correctly binds
handlePasteto the Textarea to intercept paste events.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| const largeText2 = 'B'.repeat(501); | ||
| await page.evaluate((text) => { | ||
| const dt = new DataTransfer(); | ||
| dt.setData('text/plain', text); | ||
| const event = new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true }); | ||
| document.activeElement?.dispatchEvent(event); | ||
| }, largeText2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing focus before second paste may cause test flakiness.
After the first paste creates an attachment, the focus might be lost. The second paste (lines 73-78) dispatches the event to document.activeElement, which may no longer be the chat input.
🛡️ Proposed fix
await expect(page.getByText('pasted-text.txt')).toBeVisible();
const largeText2 = 'B'.repeat(501);
+ await chatInput.focus();
await page.evaluate((text) => {
const dt = new DataTransfer();
dt.setData('text/plain', text);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const largeText2 = 'B'.repeat(501); | |
| await page.evaluate((text) => { | |
| const dt = new DataTransfer(); | |
| dt.setData('text/plain', text); | |
| const event = new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true }); | |
| document.activeElement?.dispatchEvent(event); | |
| }, largeText2); | |
| const largeText2 = 'B'.repeat(501); | |
| await chatInput.focus(); | |
| await page.evaluate((text) => { | |
| const dt = new DataTransfer(); | |
| dt.setData('text/plain', text); | |
| const event = new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true }); | |
| document.activeElement?.dispatchEvent(event); | |
| }, largeText2); |
🤖 Prompt for AI Agents
In `@tests/paste.spec.ts` around lines 72 - 78, The second paste dispatch uses
document.activeElement which may not be the chat input after the first paste;
before creating/dispatching the ClipboardEvent for largeText2, explicitly focus
the chat input element used earlier (e.g., call page.focus(selector) or inside
page.evaluate call document.querySelector('<chat-input-selector>').focus()) so
the paste event targets the correct element instead of relying on
document.activeElement; update the block that constructs the
DataTransfer/ClipboardEvent (the page.evaluate invocation that uses largeText2)
to focus the intended input element first.

Automatically convert large pasted text (>5,000 characters) into a file attachment in the chat input to improve UX and prevent UI clogging. Includes collision handling and improved validation feedback via toast notifications.
PR created automatically by Jules for task 5580601503969984577 started by @ngoiyaeric
Summary by CodeRabbit
Bug Fixes
New Features
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.