close
Skip to content

Add paste-to-upload#8725

Open
alexr00 wants to merge 3 commits intoalexr00/drab-mammalfrom
alexr00/ideological-gull
Open

Add paste-to-upload#8725
alexr00 wants to merge 3 commits intoalexr00/drab-mammalfrom
alexr00/ideological-gull

Conversation

@alexr00
Copy link
Copy Markdown
Member

@alexr00 alexr00 commented May 6, 2026

No description provided.

Copilot AI review requested due to automatic review settings May 6, 2026 10:45
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds “paste-to-upload” support for GitHub attachments across both the PR/issue overview webview comment boxes and the VS Code comment editor, by plumbing clipboard files through new upload APIs and refactoring upload internals to support byte-based uploads.

Changes:

  • Add webview textarea onPaste handling to upload clipboard files and insert/replace placeholders in comments.
  • Add a new pr.upload-pasted-files message path from webview to extension host, with server-side handling to run uploads and stream completion events back.
  • Refactor upload helpers to support “pending uploads” sourced from raw bytes (URIs, paste bytes, etc.), and add GitHubRepository.uploadFileBytes.
Show a summary per file
File Description
webviews/components/comment.tsx Adds paste handlers and shared placeholder insertion/replacement helpers for edit and pending comments.
webviews/common/context.tsx Adds uploadPastedFiles + pending-comment wrapper and refactors placeholder handler registration.
src/github/views.ts Introduces UploadPastedFilesArgs message args type.
src/github/issueOverview.ts Handles pr.upload-pasted-files by replying with placeholders and uploading decoded bytes via runPendingUploads.
src/github/githubRepository.ts Adds uploadFileBytes and routes uploadFile through it.
src/github/fileUpload.ts Adds base64 decode + MIME->extension helper, placeholder helper, and generalizes upload runner to byte-sourced uploads.
src/commands.ts Registers a DocumentPasteEditProvider for comment documents to paste-upload attachments with placeholders.

Copilot's findings

  • Files reviewed: 7/7 changed files
  • Comments generated: 3

Comment thread webviews/common/context.tsx
Comment thread webviews/common/context.tsx
Comment thread src/github/fileUpload.ts Outdated
Copilot AI review requested due to automatic review settings May 6, 2026 12:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Comments suppressed due to low confidence (2)

src/github/githubRepository.ts:2147

  • uploadFileBytes does not enforce MAX_UPLOAD_SIZE_BYTES. Since runFileUploads/runPendingUploads now call uploadFileBytes directly, the size guard in uploadFile is bypassed and oversized uploads can proceed (or cause memory/UX issues) instead of failing with the intended error. Add the same size check at the start of uploadFileBytes (or refactor to a shared guard) so all upload paths enforce the limit consistently.
	/**
	 * Upload a file's raw bytes to GitHub via the mobile upload policy API.
	 * Returns a markdown snippet appropriate for embedding in an issue/PR comment.
	 */
	public async uploadFileBytes(fileBytes: Uint8Array, fileName: string): Promise<string> {
		const contentType = guessContentType(fileName);

		const { octokit } = await this.ensure();
		const metadata = await this.getMetadata();

webviews/components/comment.tsx:363

  • handlePaste invokes uploadPastedFiles(...) (async) without awaiting or handling errors. Since postMessage promises reject on message.err, a failure here can become an unhandled promise rejection in the webview. Consider awaiting the call in an async handler or adding explicit error handling.
	const handlePaste = useCallback((e: React.ClipboardEvent<HTMLTextAreaElement>) => {
		const files = filesFromClipboard(e);
		if (files.length === 0) {
			return;
		}
		e.preventDefault();
		uploadPastedFiles(
			files,
			placeholders => insertEditPlaceholders(form, draftComment, placeholders),
			(placeholder, markdown) => replaceEditPlaceholder(form, draftComment, placeholder, markdown),
		);
	}, [uploadPastedFiles, form, draftComment]);
  • Files reviewed: 7/7 changed files
  • Comments generated: 3

Comment thread src/github/fileUpload.ts
Comment on lines 138 to 150
const runOne = async (): Promise<void> => {
while (next < uploads.length) {
const u = uploads[next++];
try {
const markdown = await githubRepository.uploadFile(u.uri, u.name);
await onComplete(u.placeholder, u.name, markdown);
} catch (err) {
(async () => {
const bytes = await u.getBytes();
return githubRepository.uploadFileBytes(bytes, u.name);
})().then(markdown => {
return onComplete(u.placeholder, u.name, markdown);
}).catch(err => {
Logger.error(`Failed to upload file ${u.name}: ${formatError(err)}`, logId);
await onError(u.placeholder, u.name, formatError(err));
}
return onError(u.placeholder, u.name, formatError(err));
});
}
Comment on lines +213 to +225
public uploadPastedFiles = async (
files: readonly File[],
insertPlaceholders: (placeholders: string) => void,
replacePlaceholder: (placeholder: string, markdownOrEmpty: string) => void,
) => {
if (files.length === 0) {
return;
}
const fileArgs = await Promise.all(files.map(async file => {
const buffer = new Uint8Array(await file.arrayBuffer());
const bytesBase64 = bytesToBase64(buffer);
return { name: file.name || 'pasted-file', type: file.type ?? '', bytesBase64 };
}));
Comment on lines +42 to +48
return (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
const files = filesFromClipboard(e);
if (files.length === 0) {
return;
}
e.preventDefault();
uploader(files);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants