Skip to content

feat(client-vue): redesign bucket file upload with drag-and-drop dropzone#1681

Merged
tada5hi merged 3 commits into
masterfrom
feat/bucket-file-upload-dropzone
Jun 15, 2026
Merged

feat(client-vue): redesign bucket file upload with drag-and-drop dropzone#1681
tada5hi merged 3 commits into
masterfrom
feat/bucket-file-upload-dropzone

Conversation

@tada5hi

@tada5hi tada5hi commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

The analysis file upload area used a bare Bootstrap <input class="form-control"> that was barely visible against the content surface. This replaces it with a custom, theme-aware drag-and-drop dropzone.

FBucketFilesUpload.vue

  • Custom drag-and-drop dropzone — dashed border, gradient cloud-upload icon badge, brand wash on hover, solid coral border + lift + bobbing icon on drag-over.
  • Real drag-and-drop — previously a file-picker only; now files can be dropped or browsed by clicking the zone.
  • Segmented Files / Directories toggle replacing the old Bootstrap switch (active button keeps white text on hover; only the surface darkens).
  • Selection summary — file count, total human-readable size, and a "Clear all" action, plus a scrollable file list.
  • Theme-native — uses --vc-color-* / --privateaim-brand-* tokens, so it flips correctly between light and dark; respects prefers-reduced-motion.
  • Bug fixes — the :disbaled typo (the input was never disabled while busy) and the buggy vNode reset hack; handlers are now properly typed (Event / DragEvent / FileList) instead of any.

eslint.config.js

  • Registered globals.browser for **/*.vue. The shared @tada5hi/eslint-config only sets Node globals for SFCs, so DOM-only globals (DragEvent, FileList, HTMLInputElement) tripped no-undef. .ts files in the package already had browser globals — this closes the gap for .vue.

Test plan

  • npm run dev --workspace=apps/client-ui → open an analysis → Code Files+ (Add) to open the upload modal.
  • Verify in both light and dark mode: dropzone visibility, hover/drag-over states, mode toggle (active hover stays white), drag-and-drop a few files, the count/size summary, clear all, and upload.
  • npm run lint passes.

Notes

  • Folder drag-and-drop drops only top-level files (dataTransfer.files doesn't recurse); full directory-tree uploads still use Directories mode + browse (webkitdirectory), as before.

closes #1186
closes #1155
closes #1223
closes #1192

Summary by CodeRabbit

Release Notes

  • New Features

    • Added drag-and-drop upload support for both files and directories
    • Users can select multiple items with visual drag-over feedback
    • Displays a selection summary (item count and total size) and updates the Upload button label accordingly
    • Added a “Clear all” option to quickly reset selections
  • Improvements

    • Improved how bucket file names are displayed by using available path information when present

…zone

Replace the near-invisible Bootstrap file input with a custom, theme-aware
drag-and-drop dropzone for analysis file uploads:

- real drag-and-drop (was file-picker only) plus click-to-browse
- segmented Files / Directories mode toggle
- selection summary (count, total size, clear all) + scrollable list
- light/dark via --vc-color-* / --privateaim-brand-* tokens
- fix pre-existing :disbaled typo and buggy vNode reset hack; type the
  change/drop handlers (Event/DragEvent/FileList) instead of any

Also register browser globals for *.vue in eslint.config.js — the shared
config only set Node globals for SFCs, so DOM-only globals (DragEvent,
FileList, HTMLInputElement) tripped no-undef.
Copilot AI review requested due to automatic review settings June 15, 2026 12:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

eslint.config.js adds a browser globals override for **/*.vue files to prevent false no-undef errors on browser APIs. FBucketFileForm gains a relativePath prop for structured path tracking, and FBucketFile prefers data.path in display. FBucketFilesUpload is reworked to support drag-and-drop file and directory selection with deduplication, path preservation, total-size computation, and a new dropzone-styled template.

Changes

Drag-and-drop upload and path handling

Layer / File(s) Summary
ESLint browser globals for Vue SFCs
eslint.config.js
Adds globals import and a **/*.vue override with languageOptions.globals: globals.browser to stop no-undef false positives on browser APIs in Vue SFCs.
File path prop and display contracts
packages/client-vue/src/components/bucket-file/FBucketFileForm.vue, packages/client-vue/src/components/bucket-file/FBucketFile.vue
FBucketFileForm adds an optional relativePath prop and updates its path computed to prioritize relativePath over webkitRelativePath or file.name. FBucketFile updates its filename display to prefer data.path when present, falling back to data.name.
Drag-and-drop state and file ingestion logic
packages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue
Adds computed and humanFileSize imports, dragOver ref, totalSize computed from staged files, onDragOver/onDragLeave/onDrop handlers with FileSystemEntry traversal and flat-file fallback, checkTempFiles/dropTempFile helpers with webkitRelativePath-based deduplication, clearFiles reset, and updated FormData upload to send each file with its relative path as the multipart filename.
Dropzone template, wiring, and scoped styles
packages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue
Rewrites the template with a dropzone label wrapping an invisible file input (toggling multiple/webkitdirectory per mode), empty-state and count+humanized-size summary views with a "Clear all" button, renders FBucketFileForm per staged item with relativePath prop and @drop removal, updates upload button label/disabled logic, and adds scoped CSS for mode toggle, drag-over/busy/focus states, summary bar, and scrollable file list.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 Hippity-hop, I drag and I drop,
Files tumble in—don't ever stop!
webkitRelativePath keeps dupes at bay,
humanFileSize shows how big they weigh.
Paths flow through components with care,
Browser globals no longer snare,
The bunny uploads with flair! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: redesigning the bucket file upload component with drag-and-drop dropzone functionality.
Linked Issues check ✅ Passed The PR comprehensively addresses all four linked issues by implementing drag-and-drop (#1186), fixing deletion/re-upload bugs (#1155), providing improved file structure display (#1223), and clarifying the upload interface (#1192).
Out of Scope Changes check ✅ Passed All changes are directly related to the bucket file upload redesign objective: ESLint config updates for Vue globals, FBucketFilesUpload component redesign, FBucketFileForm prop addition, and FBucketFile display logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/bucket-file-upload-dropzone

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.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue`:
- Around line 34-42: The addFiles function blindly appends files to
tempFiles.value without deduplication, and the file removal logic (which also
needs fixing based on the comment's scope) uses name/path matching that can
cause collisions when multiple files have the same name. Modify the addFiles
function to check if each file from the incoming FileList already exists in
tempFiles.value before adding it, using object identity comparison since
FBucketFileForm emits the same File instance. Additionally, update the file
removal logic (around lines 70-79) to use the same object identity matching
instead of relying on name or path heuristics to ensure the correct file is
removed.
- Around line 155-171: The file input element with id="files" is hidden using
`display: none` at lines 352-354, which removes it from the tab order and makes
it inaccessible to keyboard users. Replace the `display: none` CSS rule with an
alternative hiding technique that keeps the input in the tab order and
accessible to assistive technologies, such as positioning it off-screen with
`position: absolute; left: -9999px;` or using `width: 0; height: 0; overflow:
hidden;`. This allows keyboard users to tab to the input and open the file
picker without relying on pointer devices.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 38f7a430-1021-407c-99e5-1dfc579ff551

📥 Commits

Reviewing files that changed from the base of the PR and between 5b53dac and a46acc7.

📒 Files selected for processing (2)
  • eslint.config.js
  • packages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue

Comment thread packages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue Outdated
…load

- send each file's relative path as the multipart filename so the storage service (busboy preservePath) keeps the directory in BucketFile.path/directory
- expand drag-dropped folders via the FileSystem Entry API (webkitGetAsEntry + recursive readEntries), deriving each path from entry.fullPath (dropped files have an empty webkitRelativePath)
- stage files as { file, path } and dedupe by path on ingest (matches the server unique bucket_id+path); remove by object identity
- render the relative path in the file lists (FBucketFile: data.path || data.name; FBucketFileForm: explicit relative-path prop)
- a11y: file input is visually hidden but kept tabbable (was display:none) with a focus-within ring on the dropzone
@tada5hi tada5hi requested a review from Copilot June 15, 2026 12:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@tada5hi

tada5hi commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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.

@tada5hi

tada5hi commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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.

@tada5hi

tada5hi commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue`:
- Around line 125-159: The onDrop async function lacks error handling for the
collectEntry call within the entry traversal loop. Wrap the loop that calls
collectEntry (around line 151) in a try/catch block, and when an error is caught
during File System Entry traversal, ensure a failed event is emitted to properly
notify the caller instead of silently failing. This handles permission errors
and other edge cases that may occur during the async traversal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c887c010-57c2-49c4-9761-336bbf4067ea

📥 Commits

Reviewing files that changed from the base of the PR and between 5b53dac and c61ca69.

📒 Files selected for processing (4)
  • eslint.config.js
  • packages/client-vue/src/components/bucket-file/FBucketFile.vue
  • packages/client-vue/src/components/bucket-file/FBucketFileForm.vue
  • packages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue

@tada5hi

tada5hi commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai pause

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews paused.

collectEntry() (FileSystem Entry API) can reject on read/permission errors; onDrop awaited it without a guard, so the drop would fail silently. Wrap the traversal in try/catch and emit "failed", matching the upload path's error handling.
@tada5hi tada5hi merged commit d682e31 into master Jun 15, 2026
6 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants