Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 31, 2025

Enables dragging files from preview directory listings directly into the WaveAI panel for analysis.

Changes

Modified frontend/app/aipanel/aipanel.tsx:

  • Added useDrop hook to accept FILE_ITEM drag type from preview directory
  • Implemented handleFileItemDrop to:
    • Read file content via RpcApi.FileReadCommand using the remote URI
    • Convert base64 data to browser File object with proper MIME type
    • Validate and add to panel using existing model.addFile() flow
  • Integrated with existing drag overlay for visual feedback
  • Rejects directories with appropriate error messaging

Implementation

const handleFileItemDrop = useCallback(
    async (draggedFile: DraggedFile) => {
        if (draggedFile.isDir) {
            model.setError("Cannot add directories to Wave AI. Please select a file.");
            return;
        }
        
        const fileData = await RpcApi.FileReadCommand(TabRpcClient, {
            info: { path: draggedFile.uri }
        }, null);
        
        const bytes = new Uint8Array(atob(fileData.data64).split('').map(c => c.charCodeAt(0)));
        const file = new File([bytes], draggedFile.relName, { 
            type: fileData.info?.mimetype || "application/octet-stream" 
        });
        
        // Existing validation and addFile flow
        await model.addFile(file);
    },
    [model]
);

const [{ isOver, canDrop }, drop] = useDrop(() => ({
    accept: "FILE_ITEM",
    drop: handleFileItemDrop,
    collect: (monitor) => ({ isOver: monitor.isOver(), canDrop: monitor.canDrop() })
}), [handleFileItemDrop]);

No changes required to preview directory—it already exports FILE_ITEM drag items. Works independently from native file system drag-and-drop.

Original prompt

In Wave, we have our preview widget (preview.tsx in frontend/app/view/preview). and we also have the WaveAI panel in aipanel.tsx.

I'd like to see if we can allow a drag&drop from the preview-directory.tsx listings directly into the WaveAI panel.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@CLAassistant
Copy link

CLAassistant commented Oct 31, 2025

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ sawka
❌ Copilot
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI and others added 3 commits October 31, 2025 20:18
- Import useDrop from react-dnd
- Add RpcApi and TabRpcClient imports for file reading
- Implement handleFileItemDrop to convert DraggedFile to File object
- Add useDrop hook to accept FILE_ITEM drops
- Update isDragOver state for FILE_ITEM drags
- Handle file reading via RPC API and base64 decoding
- Validate file types and sizes before adding to AI panel

Co-authored-by: sawka <[email protected]>
Simplify the drag over state logic to properly clear when neither FILE_ITEM nor native files are being dragged.

Co-authored-by: sawka <[email protected]>
- Improve error handling with instanceof Error check
- Simplify drag state logic by removing mock object usage
- Add clearer comments for drag state handling

Co-authored-by: sawka <[email protected]>
Copilot AI changed the title [WIP] Add drag and drop from preview widget to WaveAI panel Add drag-and-drop from preview directory to WaveAI panel Oct 31, 2025
Copilot finished work on behalf of sawka October 31, 2025 20:29
Copilot AI requested a review from sawka October 31, 2025 20:29
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 31, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request adds React Drag-and-Drop (React DnD) support to an AI panel component for handling remote file uploads. The changes introduce a new file size validation utility in ai-utils.ts with module-scoped size thresholds, enhance the panel component in aipanel.tsx to detect and handle FILE_ITEM drag events alongside native file drags, and add a new async method addFileFromRemoteUri to the WaveAIModel class that fetches remote files, validates them, and converts them to base64 for storage. The implementation integrates React DnD's useDrop hook, manages dual drag-over states, and includes error handling for file validation and fetching operations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–75 minutes

  • React DnD integration in aipanel.tsx: Verify correct useDrop hook configuration, proper drop ref attachment to container, and correct handling of drag events to avoid conflicts between native and React DnD drag flows
  • Dual drag-over state management: Ensure isDragOver and isReactDndDragOver states are properly unified for overlay rendering and that event guards correctly distinguish between drag types
  • Remote file workflow in waveai-model.tsx: Review addFileFromRemoteUri for proper async error handling, validation chaining (size, MIME type, acceptability), base64 conversion, and error message propagation
  • Validation consistency: Confirm the new validateFileSizeFromInfo utility integrates correctly with existing validators and that file type handling matches expectations across modules
  • File object conversion and storage: Verify the remote file-to-File conversion and subsequent delegation to addFile maintains parity with local file handling

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Add drag-and-drop from preview directory to WaveAI panel" directly and accurately describes the primary objective of the changeset. The title is concise, specific, and clearly conveys the main feature being implemented—enabling users to drag files from the preview directory into the WaveAI panel. The title is neither vague nor generic, and aligns well with the changes across all three modified files (ai-utils.ts, aipanel.tsx, and waveai-model.tsx) which collectively enable this functionality.
Description Check ✅ Passed The pull request description is clearly related to the changeset and provides comprehensive detail about the changes being made. It explicitly states the purpose ("Enables dragging files from preview directory listings directly into the WaveAI panel"), documents the modified files with specific changes, includes implementation code snippets, and explains the behavior and independence from native file system drag-and-drop. The description demonstrates meaningful information about what is being changed and how, well beyond the minimum requirements for this lenient check.

Comment @coderabbitai help to get the list of available commands and usage tips.

@sawka
Copy link
Member

sawka commented Oct 31, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 31, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sawka sawka marked this pull request as ready for review October 31, 2025 20:56
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d4fa5b and d59c058.

📒 Files selected for processing (3)
  • frontend/app/aipanel/ai-utils.ts (2 hunks)
  • frontend/app/aipanel/aipanel.tsx (7 hunks)
  • frontend/app/aipanel/waveai-model.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: sawka
Repo: wavetermdev/waveterm PR: 2430
File: frontend/app/aipanel/aimessage.tsx:137-144
Timestamp: 2025-10-14T06:30:54.763Z
Learning: In `frontend/app/aipanel/aimessage.tsx`, the `AIToolUseGroup` component splits file operation tool calls into separate batches (`fileOpsNeedApproval` and `fileOpsNoApproval`) based on their approval state before passing them to `AIToolUseBatch`. This ensures each batch has homogeneous approval states, making group-level approval handling valid.
📚 Learning: 2025-10-14T06:30:54.763Z
Learnt from: sawka
Repo: wavetermdev/waveterm PR: 2430
File: frontend/app/aipanel/aimessage.tsx:137-144
Timestamp: 2025-10-14T06:30:54.763Z
Learning: In `frontend/app/aipanel/aimessage.tsx`, the `AIToolUseGroup` component splits file operation tool calls into separate batches (`fileOpsNeedApproval` and `fileOpsNoApproval`) based on their approval state before passing them to `AIToolUseBatch`. This ensures each batch has homogeneous approval states, making group-level approval handling valid.

Applied to files:

  • frontend/app/aipanel/aipanel.tsx
  • frontend/app/aipanel/ai-utils.ts
  • frontend/app/aipanel/waveai-model.tsx

Comment on lines +408 to +425
const handleFileItemDrop = useCallback(
(draggedFile: DraggedFile) => model.addFileFromRemoteUri(draggedFile),
[model]
);

const [{ isOver, canDrop }, drop] = useDrop(
() => ({
accept: "FILE_ITEM",
drop: handleFileItemDrop,
collect: (monitor) => ({
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}),
}),
[handleFileItemDrop]
);

// Update drag over state for FILE_ITEM drags
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Bring DraggedFile into scope for the drop handler.

handleFileItemDrop and the useDrop spec type this argument as DraggedFile, but the type isn’t imported anywhere in this file. TypeScript will fail with “Cannot find name 'DraggedFile'” until you add the appropriate import from the preview drag-and-drop module.

🤖 Prompt for AI Agents
In frontend/app/aipanel/aipanel.tsx around lines 408 to 425, the drop handler
and useDrop callback reference the DraggedFile type but it’s not imported; add
an import for DraggedFile from the preview drag-and-drop module at the top of
the file (e.g. import { DraggedFile } from
'<your-preview-drag-and-drop-module>') so the type is in scope, then rebuild to
ensure TypeScript errors are resolved.

Comment on lines +163 to +208
async addFileFromRemoteUri(draggedFile: DraggedFile): Promise<void> {
if (draggedFile.isDir) {
this.setError("Cannot add directories to Wave AI. Please select a file.");
return;
}

try {
const fileInfo = await RpcApi.FileInfoCommand(TabRpcClient, { info: { path: draggedFile.uri } }, null);
if (fileInfo.notfound) {
this.setError(`File not found: ${draggedFile.relName}`);
return;
}
if (fileInfo.isdir) {
this.setError("Cannot add directories to Wave AI. Please select a file.");
return;
}

const mimeType = fileInfo.mimetype || "application/octet-stream";
const fileSize = fileInfo.size || 0;
const sizeError = validateFileSizeFromInfo(draggedFile.relName, fileSize, mimeType);
if (sizeError) {
this.setError(formatFileSizeError(sizeError));
return;
}

const fileData = await RpcApi.FileReadCommand(TabRpcClient, { info: { path: draggedFile.uri } }, null);
if (!fileData.data64) {
this.setError(`Failed to read file: ${draggedFile.relName}`);
return;
}

const buffer = base64ToArrayBuffer(fileData.data64);
const file = new File([buffer], draggedFile.relName, { type: mimeType });
if (!isAcceptableFile(file)) {
this.setError(
`File type not supported: ${draggedFile.relName}. Supported: images, PDFs, and text/code files.`
);
return;
}

await this.addFile(file);
} catch (error) {
console.error("Error handling FILE_ITEM drop:", error);
const errorMsg = error instanceof Error ? error.message : String(error);
this.setError(`Failed to add file: ${errorMsg}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Import DraggedFile to avoid a TS build break.

DraggedFile is referenced here but never brought into scope, so TypeScript will raise “Cannot find name 'DraggedFile'” and block the build. Please import the DraggedFile type from the preview drag-and-drop module before using it in addFileFromRemoteUri.

🤖 Prompt for AI Agents
In frontend/app/aipanel/waveai-model.tsx around lines 163 to 208, the function
addFileFromRemoteUri references the DraggedFile type but it is not imported,
causing a TypeScript “Cannot find name 'DraggedFile'” build error; add an import
for DraggedFile from the preview drag-and-drop module (using a type-only import
if desired) at the top of the file so the type is in scope, and run the
TypeScript build to verify the error is resolved.

@sawka sawka merged commit bf0312f into main Oct 31, 2025
4 of 7 checks passed
@sawka sawka deleted the copilot/add-drag-and-drop-feature branch October 31, 2025 23:28
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