refactor(core): ai input images display & max images count (#12395)

### TL;DR

* refactor: use horizontal scrolling to display images​
* refactor: change max images to 9
* test: add test cases for validating the maximum number of uploadable images

> CLOSE AI-6

![截屏2025-05-20 17.12.02.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/MyktQ6Qwc7H6TiRCFoYN/e234dd02-3bb8-4ea8-b69b-93c71b203a4e.png)

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

## Summary by CodeRabbit

- **New Features**
  - Added a notification to inform users when attempting to upload more images than allowed in the AI chat input.

- **Bug Fixes**
  - Prevented uploading more than 9 images at once in the AI chat input.

- **Style**
  - Improved image preview grid layout to display images in a single horizontally scrollable row.

- **Tests**
  - Added an end-to-end test to verify that an error message appears when too many images are uploaded.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
yoyoyohamapi
2025-05-21 07:19:49 +00:00
parent 029a98c435
commit 928892c5b4
5 changed files with 48 additions and 7 deletions
@@ -1,3 +1,4 @@
import { toast } from '@affine/component';
import { stopPropagation } from '@affine/core/utils';
import type { CopilotSessionType } from '@affine/graphql';
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
@@ -531,6 +532,10 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
multiple: true,
});
if (!images) return;
if (this.chatContextValue.images.length + images.length > MAX_IMAGE_COUNT) {
toast(`You can only upload up to ${MAX_IMAGE_COUNT} images`);
return;
}
this.addImages(images);
};
@@ -1 +1 @@
export const MAX_IMAGE_COUNT = 32;
export const MAX_IMAGE_COUNT = 9;
@@ -7,16 +7,19 @@ import { repeat } from 'lit/directives/repeat.js';
export class ImagePreviewGrid extends LitElement {
static override styles = css`
.image-preview-wrapper {
overflow: hidden scroll;
max-height: 128px;
overflow-x: auto;
overflow-y: hidden;
max-height: 80px;
white-space: nowrap;
}
${scrollbarStyle('.image-preview-wrapper')}
.images-container {
display: flex;
flex-direction: row;
gap: 4px;
flex-wrap: wrap;
flex-wrap: nowrap;
position: relative;
}
@@ -31,6 +34,7 @@ export class ImagePreviewGrid extends LitElement {
display: flex;
justify-content: center;
align-items: center;
flex: 0 0 auto;
}
.image-container img {
File diff suppressed because one or more lines are too long
@@ -236,10 +236,9 @@ export class ChatPanelUtils {
await this.makeChat(page, text);
}
public static async chatWithImages(
public static async uploadImages(
page: Page,
images: { name: string; mimeType: string; buffer: Buffer }[],
text: string
images: { name: string; mimeType: string; buffer: Buffer }[]
) {
await page.evaluate(() => {
delete window.showOpenFilePicker;
@@ -251,6 +250,14 @@ export class ChatPanelUtils {
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(images);
}
public static async chatWithImages(
page: Page,
images: { name: string; mimeType: string; buffer: Buffer }[],
text: string
) {
await this.uploadImages(page, images);
await page.waitForSelector('ai-chat-input img');
await this.makeChat(page, text);