Skip to content

Comments

🎨 Palette: Improve file upload UX with dynamic icons and size formatting#604

Open
anchapin wants to merge 1 commit intomainfrom
palette-ux-improvement-file-upload-8726980627345380001
Open

🎨 Palette: Improve file upload UX with dynamic icons and size formatting#604
anchapin wants to merge 1 commit intomainfrom
palette-ux-improvement-file-upload-8726980627345380001

Conversation

@anchapin
Copy link
Owner

Improved the file upload experience in ConversionUploadEnhanced.tsx by adding:

  1. Dynamic File Icons: Now shows a Coffee icon for .jar files and a Clamp icon for .zip files, giving immediate visual feedback.
  2. Human-Readable File Sizes: Implemented formatFileSize utility to display file sizes in KB, MB, or GB instead of raw bytes or fixed MB.
  3. Better Drag Feedback: The dropzone text changes to "Drop file to upload 📂" when dragging a file over it.

Verified with Playwright (screenshots generated) and existing unit tests (updated to wrap in ProgressProvider).


PR created automatically by Jules for task 8726980627345380001 started by @anchapin

- Added `frontend/src/utils/formatters.ts` for file size formatting (bytes to KB/MB/GB).
- Updated `ConversionUploadEnhanced.tsx` to use the new formatter.
- Added dynamic file icons (`.jar` -> ☕, `.zip` -> 🗜️).
- Enhanced drag-and-drop feedback text.
- Verified changes with Playwright and unit tests.

Co-authored-by: anchapin <6326294+anchapin@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 23, 2026 00:37
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Sorry @anchapin, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

Copy link
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 enhances the file upload user experience in the ConversionUploadEnhanced component by adding dynamic file type icons, human-readable file size formatting, and improved drag-and-drop feedback.

Changes:

  • Added a new formatFileSize utility function to format file sizes in human-readable units (Bytes, KB, MB, GB, TB)
  • Implemented dynamic file icons that display different emojis based on file type (☕ for .jar, 🗜️ for .zip, 📦 for others)
  • Enhanced drag-and-drop UX with contextual text that changes to "Drop file to upload 📂" when actively dragging
  • Updated tests to properly wrap components in ProgressProvider and improved WebSocket mock return values

Reviewed changes

Copilot reviewed 3 out of 6 changed files in this pull request and generated 4 comments.

File Description
pnpm-lock.yaml Minor storybook version update from 10.2.9 to 10.2.10
frontend/src/utils/formatters.ts New utility file with formatFileSize function for human-readable file size display
frontend/src/components/ConversionUpload/ConversionUploadEnhanced.tsx Added getFileIcon function, integrated formatFileSize, added dynamic drag feedback text, improved accessibility with aria-hidden attributes
frontend/src/components/ConversionUpload/ConversionUploadEnhanced.test.tsx Wrapped test components in ProgressProvider, updated WebSocket mocks to return cleanup functions
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +1 to +7
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The formatFileSize function is duplicated across the codebase in at least 5 different locations (ConversionAssetDetails.tsx, ConversionAssetsUpload.tsx, ConversionHistory/utils.ts, urlParser.ts, and now formatters.ts). This creates a maintainability issue where bug fixes or improvements need to be applied in multiple places. Consider consolidating all instances to use the new utility in formatters.ts and removing the duplicated implementations. This would be especially beneficial since the implementations are slightly different - some use 'Bytes' while others use 'B', and the ConversionHistory version always shows MB regardless of size.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +6
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The formatFileSize function doesn't handle negative byte values. While file sizes should never be negative in practice, defensive programming suggests either validating the input (throwing an error or returning an error message like "Invalid size") or documenting that the function expects non-negative values. Currently, a negative value would produce an incorrect result like "-1.00 KB" due to the Math.log calculation with negative numbers.

Copilot uses AI. Check for mistakes.
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The new formatFileSize utility function lacks test coverage. Consider adding unit tests to verify edge cases such as: zero bytes, very small files (< 1KB), boundary values between units (e.g., 1023 bytes vs 1024 bytes), large files (GB and TB ranges), and the formatting precision (2 decimal places). This is especially important since the function will likely be reused across the codebase to replace duplicated implementations.

Suggested change
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
const value = (bytes / Math.pow(k, i)).toFixed(2);
return `${value} ${sizes[i]}`;

Copilot uses AI. Check for mistakes.
Comment on lines 414 to 474
@@ -442,10 +449,10 @@ export const ConversionUploadEnhanced: React.FC<ConversionUploadProps> = ({
</div>
) : selectedFile ? (
<div className="file-preview">
<div className="file-icon">📦</div>
<div className="file-icon" aria-hidden="true">{getFileIcon(selectedFile.name)}</div>
<div className="file-info">
<div className="file-name">{selectedFile.name}</div>
<div className="file-size">{(selectedFile.size / 1024 / 1024).toFixed(2)} MB</div>
<div className="file-size">{formatFileSize(selectedFile.size)}</div>
<div className="status">{getStatusMessage()}</div>
</div>
<button
@@ -463,8 +470,8 @@ export const ConversionUploadEnhanced: React.FC<ConversionUploadProps> = ({
</div>
) : (
<div className="upload-prompt initial-prompt">
<div className="upload-icon-large">☁️</div>
<h3>Drag & drop your modpack here</h3>
<div className="upload-icon-large" aria-hidden="true">☁️</div>
<h3>{isDragActive ? "Drop file to upload 📂" : "Drag & drop your modpack here"}</h3>
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The new dynamic file icon feature (getFileIcon function) and drag feedback text changes lack test coverage. The existing tests have been updated to wrap components in ProgressProvider, but no tests verify that: 1) the coffee emoji (☕) appears for .jar files, 2) the clamp emoji (🗜️) appears for .zip files, 3) the default package emoji (📦) appears for other files, or 4) the drag-active text "Drop file to upload 📂" appears when dragging files. Consider adding tests to verify these new UX improvements work correctly.

Copilot uses AI. Check for mistakes.
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.

1 participant