t3code/support markdown 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 👍 / 👎.


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
Medium Risk
New workspace writes under
.t3/attachmentsand broader drop handling change composer behavior; limits and UTF-8 checks reduce risk, but failed writes only surface as thread errors.Overview
Chat composer now treats non-image drops as text attachments (images still use the existing image path). Valid files are written to
.t3/attachments/<uuid>/viaprojectEnvironment.writeFile, capped at 1 MB, rejected if they contain NUL bytes or invalid UTF-8, then a standard file-link chip is appended to the prompt usingserializeComposerFileLink/resolvePathLinkTarget. Drag-and-drop and the shared attachment handler were renamed from image-only toaddComposerAttachments; plan-question blocking copy was broadened to “files.”Messages timeline fixes “Show full message” so collapse is based on visible link labels, not the full markdown URL—short prompts with many attachment links stay expanded.
A MessagesTimeline test covers prompts with many long-path file links plus short trailing text.
Reviewed by Cursor Bugbot for commit f044e40. 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 f044e40. 2 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.