feat(core): workspace attachment uploading & error (#12330)

### TL;DR

feat: optimize workspace attachment uploading & error display

![截屏2025-05-16 15.29.43.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/MyktQ6Qwc7H6TiRCFoYN/2408fe5e-e54d-44a8-882c-91e1b26bb660.png)

### What Changes
####
Support for Workspace Attachment Uploading & Error Handling
* Added support for three attachment states: uploading (local), upload failed (local error), and uploaded (persisted). The frontend UI now displays real-time upload progress and error messages.
* Attachments that fail to upload can be deleted directly without confirmation.
* Merged display of uploading and uploaded attachments for a smoother user experience.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Attachments now show real-time upload status including uploading, error, and uploaded states.
  - Users can remove failed (error) attachments instantly without confirmation.
  - Attachment list merges uploading and uploaded files, displaying up to 10 items.
- **Bug Fixes**
  - Improved error handling and messaging for failed attachment uploads.
- **Style**
  - Enhanced visual styling for error attachments with distinct colors and backgrounds.
- **Tests**
  - Added tests simulating slow network uploads, upload failures, and direct removal of error attachments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
yoyoyohamapi
2025-05-22 02:35:03 +00:00
parent 21ea65edc5
commit 45ed9038b6
8 changed files with 346 additions and 64 deletions

View File

@@ -87,23 +87,35 @@ export class SettingsPanelUtils {
while (count > 0) {
const attachmentItem = await page.getByTestId(itemId).first();
const hasErrorItem = await attachmentItem
.getByTestId('workspace-embedding-setting-attachment-error-item')
.isVisible();
await attachmentItem
.getByTestId('workspace-embedding-setting-attachment-delete-button')
.click();
await page.getByTestId('confirm-modal-confirm').click();
if (!hasErrorItem) {
await page.getByTestId('confirm-modal-confirm').click();
}
await page.waitForTimeout(1000);
count = await page.getByTestId(itemId).count();
}
}
public static async removeAttachment(page: Page, attachment: string) {
public static async removeAttachment(
page: Page,
attachment: string,
shouldConfirm = true
) {
const attachmentItem = await page
.getByTestId('workspace-embedding-setting-attachment-item')
.filter({ hasText: attachment });
await attachmentItem
.getByTestId('workspace-embedding-setting-attachment-delete-button')
.click();
await page.getByTestId('confirm-modal-confirm').click();
if (shouldConfirm) {
await page.getByTestId('confirm-modal-confirm').click();
}
await page
.getByTestId('workspace-embedding-setting-attachment-item')
.filter({ hasText: attachment })