t3code/support non-image chat attachments#3927
Conversation
- Store supported text files in the project workspace - Insert attachment links into the chat prompt
- Base user-message collapse thresholds on visible link text - Add regression coverage for markdown attachment links
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ 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 |
ApprovabilityVerdict: Needs human review This PR introduces a new feature (text file attachments with filesystem writes) that adds significant user-facing capability beyond simple changes. New features with file system operations warrant human review. You can customize Macroscope's approvability policy. Learn more. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f044e40. Configure here.
| setIsDragOverComposer(false); | ||
| const files = Array.from(event.dataTransfer.files); | ||
| addComposerImages(files); | ||
| void addComposerAttachments(files); |
There was a problem hiding this comment.
Send races text attachment
Medium Severity
When text files are dropped or pasted, their asynchronous processing to write the file and add its link to the prompt is not awaited. This allows the composer's prompt to be submitted prematurely, resulting in messages sent without the intended attachment link and potentially leaving orphaned attachment files.
Reviewed by Cursor Bugbot for commit f044e40. Configure here.
| currentPrompt.length, | ||
| currentPrompt.length, | ||
| `${separator}${serializeComposerFileLink(resolvePathLinkTarget(relativePath, gitCwd))} `, | ||
| ); |
There was a problem hiding this comment.
Parallel drops corrupt prompt
High Severity
Each text attachment appends using applyPromptReplacement at promptRef.current.length captured after its own async work, with no serialization across overlapping addComposerAttachments calls. Concurrent drops can insert links at the same stale offset and splice into the middle of the prompt instead of the end.
Reviewed by Cursor Bugbot for commit f044e40. Configure here.
| const imageFiles = files.filter((file) => file.type.startsWith("image/")); | ||
| if (imageFiles.length === 0) return; | ||
| event.preventDefault(); | ||
| addComposerImages(imageFiles); |
There was a problem hiding this comment.
Mixed batch errors overwritten
Medium Severity
In a mixed drop, text failures use error = (await addComposerTextAttachment(file)) ?? error, but image size and count checks assign error = ... directly. A later image error replaces an earlier text error, and a lingering image error can remain after a later text file attaches successfully.
Reviewed by Cursor Bugbot for commit f044e40. Configure here.
| const imageFiles = files.filter((file) => file.type.startsWith("image/")); | ||
| if (imageFiles.length === 0) return; | ||
| event.preventDefault(); | ||
| addComposerImages(imageFiles); |
There was a problem hiding this comment.
Image limit skips text files
Low Severity
When the per-message image cap is reached, addComposerAttachments breaks out of the file loop. Any non-image files later in the same DataTransfer list are never passed to addComposerTextAttachment, so they are dropped silently aside from the image-limit error.
Reviewed by Cursor Bugbot for commit f044e40. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f044e403be
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| setIsDragOverComposer(false); | ||
| const files = Array.from(event.dataTransfer.files); | ||
| addComposerImages(files); | ||
| void addComposerAttachments(files); |
There was a problem hiding this comment.
Prevent sending while dropped text files are still attaching
For dropped markdown/text files this handler now fire-and-forgets an async path: addComposerAttachments waits for file.arrayBuffer() and projects.writeFile before inserting the markdown link. If the user presses Send before that RPC finishes, onSend snapshots the prompt without the file link and clears it; the async callback then appends the link afterward, so the turn is sent without the attachment and a stale attachment link is left in the composer.
Useful? React with 👍 / 👎.
| return `'${file.name}' exceeds the 1 MB text attachment limit.`; | ||
| } | ||
| const safeName = file.name.replace(/[^a-zA-Z0-9._-]+/g, "-") || "context.md"; | ||
| const relativePath = `.t3/attachments/${randomUUID()}/${safeName}`; |
There was a problem hiding this comment.
Avoid storing dropped text attachments in tracked workspaces
This writes every dropped text/markdown attachment under .t3/attachments inside the user's project, but the code does not ensure that .t3/ is ignored for arbitrary workspaces. In projects that have not already added that ignore rule, attaching a file creates untracked files that show up in git status and can be accidentally committed along with the user's changes.
Useful? React with 👍 / 👎.
| applyPromptReplacement( | ||
| currentPrompt.length, | ||
| currentPrompt.length, | ||
| `${separator}${serializeComposerFileLink(resolvePathLinkTarget(relativePath, gitCwd))} `, |
There was a problem hiding this comment.
Keep text attachments inside the new worktree
When the first message is sent in New worktree mode, gitCwd is still the original project root because the worktree has not been created yet, so this inserts an absolute link to .t3/attachments in the original checkout. The bootstrap path then creates the worktree and starts the provider session there, leaving the model with a prompt that points outside its active workspace/sandbox instead of to a file in the worktree.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c7e248cb18
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const currentPrompt = promptRef.current; | ||
| const separator = currentPrompt.length > 0 && !/\s$/.test(currentPrompt) ? " " : ""; | ||
| applyPromptReplacement( |
There was a problem hiding this comment.
Cancel stale text-attachment appends
When a dropped text file is still awaiting arrayBuffer()/writeProjectFile and the user switches to another thread or draft, this continuation reads the shared promptRef.current after it has been updated for the new composer, while the closure still writes through the old composerDraftTarget. That can overwrite the old draft with the new thread's prompt plus the attachment link, or append a stale link after navigation; capture the original draft prompt/target or abort if the target changed before applying the replacement.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3a5cf3e282
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for (const file of files) { | ||
| if (!file.type.startsWith("image/")) { | ||
| error = `Unsupported file type for '${file.name}'. Please attach image files only.`; | ||
| error = (await addComposerTextAttachment(file)) ?? error; |
There was a problem hiding this comment.
Cap the number of text attachments
When a drop contains many non-image files, this branch awaits addComposerTextAttachment for every file and writes each one (up to 1 MB) into the workspace before appending a link. The existing attachment count guard only applies in the image branch below, so a user can accidentally enqueue hundreds of workspace writes/refreshes and create a very large prompt by dropping many small text files; add a count or aggregate-size limit before writing text attachments.
Useful? React with 👍 / 👎.


Verification
Summary
.t3/attachmentsand insert standard file-link chips into the promptWhy
The composer previously rejected every dropped file that was not an image. This made it impossible to attach Markdown, source, configuration, or other text files as agent context.
Text attachments now reuse the existing project file-writing and Markdown file-link paths, so they render and behave like other file references. Files are limited to 1 MB and rejected when they contain NUL bytes or invalid UTF-8.
The message collapse heuristic also now measures displayed link labels instead of their backing absolute paths. Without this, prompts containing several attachments could appear hidden even though their visible content was short.
Validation
pnpm exec vp test apps/web/src/components/chat/MessagesTimeline.test.tsxpnpm exec vp checkpnpm exec vp run typecheckNote
Low Risk
Composer UX and workspace file writes under
.t3/attachments; no auth or core session changes, with size and encoding guards.Overview
The chat composer now accepts UTF-8 text files on drag-and-drop (alongside images), not only image MIME types.
Dropped non-image files are validated (1 MB cap, no NUL bytes, strict UTF-8), written into the workspace under
.t3/attachments/<uuid>/, and the prompt gets a standard markdown file-link chip via the existingwriteProjectFileandserializeComposerFileLinkpaths.User message collapse in the timeline now measures length using link labels instead of full markdown destinations, so short prompts with many attachment links are not hidden behind “Show full message.” A regression test covers that case.
Reviewed by Cursor Bugbot for commit 3a5cf3e. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add markdown file attachment support to the chat composer
addComposerAttachmentspath instead of an image-only handler.addComposerTextAttachment, which validates UTF-8 content, enforces a 1 MB limit, writes the file to.t3/attachments/<uuid>/<safeName>in the workspace, and inserts a markdown link into the prompt.📊 Macroscope summarized 3a5cf3e. 2 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.