diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/content/assistant-avatar.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/content/assistant-avatar.ts
index fdedaa7784..4f62e7e972 100644
--- a/packages/frontend/core/src/blocksuite/ai/chat-panel/content/assistant-avatar.ts
+++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/content/assistant-avatar.ts
@@ -1,6 +1,10 @@
+import { AIStarIconWithAnimation } from '@blocksuite/affine/components/icons';
import { ShadowlessElement } from '@blocksuite/affine/std';
import { AiIcon } from '@blocksuite/icons/lit';
import { css, html } from 'lit';
+import { property } from 'lit/decorators.js';
+
+import type { ChatStatus } from '../../components/ai-chat-messages';
const AffineAvatarIcon = AiIcon({
width: '20px',
@@ -9,6 +13,9 @@ const AffineAvatarIcon = AiIcon({
});
export class AssistantAvatar extends ShadowlessElement {
+ @property({ attribute: 'data-status', reflect: true })
+ accessor status: ChatStatus = 'idle';
+
static override styles = css`
chat-assistant-avatar {
display: inline-flex;
@@ -16,8 +23,12 @@ export class AssistantAvatar extends ShadowlessElement {
gap: 8px;
}
`;
+
protected override render() {
- return html`${AffineAvatarIcon} AFFiNE AI`;
+ return html`${this.status === 'transmitting'
+ ? AIStarIconWithAnimation
+ : AffineAvatarIcon}
+ AFFiNE AI`;
}
}
diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/message/assistant.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/message/assistant.ts
index 3140d63864..4b67b58b77 100644
--- a/packages/frontend/core/src/blocksuite/ai/chat-panel/message/assistant.ts
+++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/message/assistant.ts
@@ -15,6 +15,7 @@ import {
} from '../../_common/chat-actions-handle';
import {
type ChatMessage,
+ type ChatStatus,
isChatMessage,
} from '../../components/ai-chat-messages';
import { AIChatErrorRenderer } from '../../messages/error';
@@ -39,7 +40,7 @@ export class ChatMessageAssistant extends WithDisposable(ShadowlessElement) {
accessor isLast: boolean = false;
@property({ attribute: 'data-status', reflect: true })
- accessor status: string = 'idle';
+ accessor status: ChatStatus = 'idle';
@property({ attribute: false })
accessor error: AIError | null = null;
@@ -64,7 +65,7 @@ export class ChatMessageAssistant extends WithDisposable(ShadowlessElement) {
/\[\^\d+\]:{"type":"doc","docId":"[^"]+"}/.test(this.item.content);
return html`
-
+
${isWithDocs
? html`
with your docs`
: nothing}
diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/add-popover.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/add-popover.ts
index bf6ba3a3b4..c7a82ea08f 100644
--- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/add-popover.ts
+++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/add-popover.ts
@@ -161,16 +161,27 @@ export class ChatPanelAddPopover extends SignalWatcher(
};
private readonly _addFileChip = async () => {
- const file = await openFileOrFiles();
- if (!file) return;
- if (file.size > 50 * 1024 * 1024) {
- toast('You can only upload files less than 50MB');
- return;
- }
- this.addChip({
- file,
- state: 'processing',
+ const files = await openFileOrFiles({
+ multiple: true,
});
+ if (!files || files.length === 0) return;
+
+ const images = files.filter(file => file.type.startsWith('image/'));
+ if (images.length > 0) {
+ this.addImages(images);
+ }
+
+ const others = files.filter(file => !file.type.startsWith('image/'));
+ for (const file of others) {
+ if (file.size > 50 * 1024 * 1024) {
+ toast(`${file.name} is too large, please upload a file less than 50MB`);
+ } else {
+ await this.addChip({
+ file,
+ state: 'processing',
+ });
+ }
+ }
this._track('file');
this.abortController.abort();
};
@@ -249,7 +260,10 @@ export class ChatPanelAddPopover extends SignalWatcher(
accessor docDisplayConfig!: DocDisplayConfig;
@property({ attribute: false })
- accessor addChip!: (chip: ChatChip) => void;
+ accessor addChip!: (chip: ChatChip) => Promise
;
+
+ @property({ attribute: false })
+ accessor addImages!: (images: File[]) => void;
@property({ attribute: false })
accessor abortController!: AbortController;
@@ -459,8 +473,8 @@ export class ChatPanelAddPopover extends SignalWatcher(
}
}
- private readonly _addDocChip = (meta: DocMeta) => {
- this.addChip({
+ private readonly _addDocChip = async (meta: DocMeta) => {
+ await this.addChip({
docId: meta.id,
state: 'processing',
});
@@ -469,8 +483,8 @@ export class ChatPanelAddPopover extends SignalWatcher(
this.abortController.abort();
};
- private readonly _addTagChip = (tag: TagMeta) => {
- this.addChip({
+ private readonly _addTagChip = async (tag: TagMeta) => {
+ await this.addChip({
tagId: tag.id,
state: 'processing',
});
@@ -478,8 +492,8 @@ export class ChatPanelAddPopover extends SignalWatcher(
this.abortController.abort();
};
- private readonly _addCollectionChip = (collection: CollectionMeta) => {
- this.addChip({
+ private readonly _addCollectionChip = async (collection: CollectionMeta) => {
+ await this.addChip({
collectionId: collection.id,
state: 'processing',
});
diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/chat-panel-chips.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/chat-panel-chips.ts
index 9ce2e1ac47..a0cdc9d7db 100644
--- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/chat-panel-chips.ts
+++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/chat-panel-chips.ts
@@ -91,6 +91,9 @@ export class ChatPanelChips extends SignalWatcher(
@property({ attribute: false })
accessor updateChips!: (chips: ChatChip[]) => void;
+ @property({ attribute: false })
+ accessor addImages!: (images: File[]) => void;
+
@property({ attribute: false })
accessor pollContextDocsAndFiles!: () => void;
@@ -262,6 +265,7 @@ export class ChatPanelChips extends SignalWatcher(
template: html`
('');
markdown.value = value;
this.updateChip(this.chip, {
- state: 'finished',
markdown,
tokenCount,
});
diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-composer/ai-chat-composer.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-composer/ai-chat-composer.ts
index a8690e3c2b..5eda4a204a 100644
--- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-composer/ai-chat-composer.ts
+++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-composer/ai-chat-composer.ts
@@ -29,6 +29,7 @@ import type {
AINetworkSearchConfig,
AIReasoningConfig,
} from '../ai-chat-input';
+import { MAX_IMAGE_COUNT } from '../ai-chat-input/const';
export class AIChatComposer extends SignalWatcher(
WithDisposable(ShadowlessElement)
@@ -118,6 +119,7 @@ export class AIChatComposer extends SignalWatcher(
.docDisplayConfig=${this.docDisplayConfig}
.searchMenuConfig=${this.searchMenuConfig}
.portalContainer=${this.portalContainer}
+ .addImages=${this.addImages}
>