diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
index 4694c338e..b68a7e8bc 100644
--- a/.github/workflows/build-test.yml
+++ b/.github/workflows/build-test.yml
@@ -1081,6 +1081,7 @@ jobs:
- 'packages/backend/server/src/plugins/copilot/**'
- 'packages/backend/server/tests/copilot.*'
- 'packages/frontend/core/src/blocksuite/ai/**'
+ - 'packages/frontend/core/src/modules/workspace-indexer-embedding/**'
- 'tests/affine-cloud-copilot/**'
- name: Setup Node.js
diff --git a/blocksuite/affine/components/src/icons/file-icons-rc.ts b/blocksuite/affine/components/src/icons/file-icons-rc.ts
new file mode 100644
index 000000000..60a64db05
--- /dev/null
+++ b/blocksuite/affine/components/src/icons/file-icons-rc.ts
@@ -0,0 +1,173 @@
+import {
+ FileIconAepIcon,
+ FileIconAiIcon,
+ FileIconAviIcon,
+ FileIconCssIcon,
+ FileIconCsvIcon,
+ FileIconDmgIcon,
+ FileIconDocIcon,
+ FileIconDocxIcon,
+ FileIconEpsIcon,
+ FileIconExeIcon,
+ FileIconFigIcon,
+ FileIconGifIcon,
+ FileIconHtmlIcon,
+ FileIconInddIcon,
+ FileIconJavaIcon,
+ FileIconJpegIcon,
+ FileIconJsIcon,
+ FileIconJsonIcon,
+ FileIconMkvIcon,
+ FileIconMp3Icon,
+ FileIconMp4Icon,
+ FileIconMpegIcon,
+ FileIconNoneIcon,
+ FileIconPdfIcon,
+ FileIconPngIcon,
+ FileIconPptIcon,
+ FileIconPptxIcon,
+ FileIconPsdIcon,
+ FileIconRarIcon,
+ FileIconRssIcon,
+ FileIconSqlIcon,
+ FileIconSvgIcon,
+ FileIconTiffIcon,
+ FileIconTxtIcon,
+ FileIconWavIcon,
+ FileIconWebpIcon,
+ FileIconXlsIcon,
+ FileIconXlsxIcon,
+ FileIconXmlIcon,
+ FileIconZipIcon,
+ ImageIcon,
+} from '@blocksuite/icons/rc';
+
+export function getAttachmentFileIconRC(filetype: string) {
+ switch (filetype) {
+ case 'img':
+ return ImageIcon;
+ case 'image/jpeg':
+ case 'jpg':
+ case 'jpeg':
+ return FileIconJpegIcon;
+ case 'image/png':
+ case 'png':
+ return FileIconPngIcon;
+ case 'image/webp':
+ case 'webp':
+ return FileIconWebpIcon;
+ case 'image/tiff':
+ case 'tiff':
+ return FileIconTiffIcon;
+ case 'image/gif':
+ case 'gif':
+ return FileIconGifIcon;
+ case 'image/svg':
+ case 'svg':
+ return FileIconSvgIcon;
+ case 'image/eps':
+ case 'eps':
+ return FileIconEpsIcon;
+ case 'application/pdf':
+ case 'pdf':
+ return FileIconPdfIcon;
+ case 'application/msword':
+ case 'application/x-iwork-pages-sffpages':
+ case 'doc':
+ return FileIconDocIcon;
+ case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
+ case 'docx':
+ return FileIconDocxIcon;
+ case 'text/plain':
+ case 'txt':
+ return FileIconTxtIcon;
+ case 'csv':
+ return FileIconCsvIcon;
+ case 'application/vnd.ms-excel':
+ case 'xls':
+ return FileIconXlsIcon;
+ case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
+ case 'application/x-iwork-numbers-sffnumbers':
+ case 'xlsx':
+ return FileIconXlsxIcon;
+ case 'application/vnd.ms-powerpoint':
+ case 'application/x-iwork-keynote-sffkeynote':
+ case 'ppt':
+ return FileIconPptIcon;
+ case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
+ case 'pptx':
+ return FileIconPptxIcon;
+ case 'application/illustrator':
+ case 'fig':
+ return FileIconFigIcon;
+ case 'application/postscript':
+ case 'ai':
+ return FileIconAiIcon;
+ case 'application/vnd.adobe.photoshop':
+ case 'psd':
+ return FileIconPsdIcon;
+ case 'application/vnd.adobe.indesign':
+ case 'indd':
+ return FileIconInddIcon;
+ case 'application/vnd.adobe.afterfx':
+ case 'aep':
+ return FileIconAepIcon;
+ case 'audio/mpeg':
+ case 'audio/mp3':
+ case 'mp3':
+ return FileIconMp3Icon;
+ case 'audio/wav':
+ case 'wav':
+ return FileIconWavIcon;
+ case 'video/mpeg':
+ case 'mpeg':
+ return FileIconMpegIcon;
+ case 'video/mp4':
+ case 'mp4':
+ return FileIconMp4Icon;
+ case 'video/avi':
+ case 'avi':
+ return FileIconAviIcon;
+ case 'video/mkv':
+ case 'mkv':
+ return FileIconMkvIcon;
+ case 'text/html':
+ case 'html':
+ return FileIconHtmlIcon;
+ case 'text/css':
+ case 'css':
+ return FileIconCssIcon;
+ case 'application/rss+xml':
+ case 'rss':
+ return FileIconRssIcon;
+ case 'application/sql':
+ case 'sql':
+ return FileIconSqlIcon;
+ case 'application/javascript':
+ case 'js':
+ return FileIconJsIcon;
+ case 'application/json':
+ case 'json':
+ return FileIconJsonIcon;
+ case 'application/java':
+ case 'java':
+ return FileIconJavaIcon;
+ case 'application/xml':
+ case 'xml':
+ return FileIconXmlIcon;
+ case 'application/x-msdos-program':
+ case 'exe':
+ return FileIconExeIcon;
+ case 'application/x-apple-diskimage':
+ case 'dmg':
+ return FileIconDmgIcon;
+ case 'application/zip':
+ case 'zip':
+ return FileIconZipIcon;
+ case 'application/x-rar-compressed':
+ case 'rar':
+ return FileIconRarIcon;
+ default:
+ return FileIconNoneIcon;
+ }
+}
diff --git a/blocksuite/affine/components/src/icons/index.ts b/blocksuite/affine/components/src/icons/index.ts
index b08a7a2e4..acce40c9c 100644
--- a/blocksuite/affine/components/src/icons/index.ts
+++ b/blocksuite/affine/components/src/icons/index.ts
@@ -1,5 +1,6 @@
export * from './ai.js';
export * from './file-icons.js';
+export * from './file-icons-rc';
export * from './import-export.js';
export * from './list.js';
export * from './loading.js';
diff --git a/packages/frontend/component/src/components/setting-components/setting-row.tsx b/packages/frontend/component/src/components/setting-components/setting-row.tsx
index df0a10070..fefc639d5 100644
--- a/packages/frontend/component/src/components/setting-components/setting-row.tsx
+++ b/packages/frontend/component/src/components/setting-components/setting-row.tsx
@@ -5,7 +5,7 @@ import { settingRow } from './share.css';
export type SettingRowProps = PropsWithChildren<{
name: ReactNode;
- desc: ReactNode;
+ desc?: ReactNode;
style?: CSSProperties;
onClick?: () => void;
spreadCol?: boolean;
@@ -41,7 +41,7 @@ export const SettingRow = ({
>
{name}
-
{desc}
+ {desc &&
{desc}
}
{spreadCol ? {children}
: children}
diff --git a/packages/frontend/component/src/components/setting-components/wrapper.tsx b/packages/frontend/component/src/components/setting-components/wrapper.tsx
index 396b682af..cfdd5bd36 100644
--- a/packages/frontend/component/src/components/setting-components/wrapper.tsx
+++ b/packages/frontend/component/src/components/setting-components/wrapper.tsx
@@ -7,6 +7,7 @@ interface SettingWrapperProps {
id?: string;
title?: ReactNode;
disabled?: boolean;
+ testId?: string;
}
export const SettingWrapper = ({
@@ -14,9 +15,14 @@ export const SettingWrapper = ({
title,
children,
disabled,
+ testId,
}: PropsWithChildren) => {
return (
-
+
{title ?
{title}
: null}
{children}
diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/ai-chat-input.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/ai-chat-input.ts
index 0e4cd147c..01dc8dfdd 100644
--- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/ai-chat-input.ts
+++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/ai-chat-input.ts
@@ -20,17 +20,8 @@ import { ChatAbortIcon, ChatSendIcon } from '../../_common/icons';
import { type AIError, AIProvider } from '../../provider';
import { reportResponse } from '../../utils/action-reporter';
import { readBlobAsURL } from '../../utils/image';
-import type {
- ChatChip,
- DocDisplayConfig,
- FileChip,
-} from '../ai-chat-chips/type';
-import {
- isCollectionChip,
- isDocChip,
- isFileChip,
- isTagChip,
-} from '../ai-chat-chips/utils';
+import type { ChatChip, DocDisplayConfig } from '../ai-chat-chips/type';
+import { isDocChip } from '../ai-chat-chips/utils';
import type { ChatMessage } from '../ai-chat-messages';
import { MAX_IMAGE_COUNT } from './const';
import type {
@@ -588,7 +579,6 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
const sessionId = await this.createSessionId();
let contexts = await this._getMatchedContexts(userInput);
- contexts = this._filterContexts(contexts);
if (abortController.signal.aborted) {
return;
}
@@ -673,9 +663,7 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
private async _getMatchedContexts(userInput: string) {
const contextId = await this.getContextId();
- if (!contextId) {
- return { files: [], docs: [] };
- }
+ const workspaceId = this.host.store.workspace.id;
const docContexts = new Map<
string,
@@ -687,7 +675,11 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
>();
const { files: matchedFiles = [], docs: matchedDocs = [] } =
- (await AIProvider.context?.matchContext(userInput, contextId)) ?? {};
+ (await AIProvider.context?.matchContext(
+ userInput,
+ contextId,
+ workspaceId
+ )) ?? {};
matchedDocs.forEach(doc => {
docContexts.set(doc.docId, {
@@ -701,17 +693,12 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
if (context) {
context.fileContent += `\n${file.content}`;
} else {
- const fileChip = this.chips.find(
- chip => isFileChip(chip) && chip.fileId === file.fileId
- ) as FileChip | undefined;
- if (fileChip && fileChip.blobId) {
- fileContexts.set(file.fileId, {
- blobId: fileChip.blobId,
- fileName: fileChip.file.name,
- fileType: fileChip.file.type,
- fileContent: file.content,
- });
- }
+ fileContexts.set(file.fileId, {
+ blobId: file.blobId,
+ fileName: file.name,
+ fileType: file.mimeType,
+ fileContent: file.content,
+ });
}
});
@@ -753,42 +740,6 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
files: Array.from(fileContexts.values()),
};
}
-
- // TODO: remove this function after workspace embedding is ready
- private _filterContexts(contexts: {
- docs: BlockSuitePresets.AIDocContextOption[];
- files: BlockSuitePresets.AIFileContextOption[];
- }) {
- const docIds = this.chips.reduce((acc, chip) => {
- if (isDocChip(chip)) {
- acc.push(chip.docId);
- }
- if (isTagChip(chip)) {
- const docIds = this.docDisplayConfig.getTagPageIds(chip.tagId);
- acc.push(...docIds);
- }
- if (isCollectionChip(chip)) {
- const docIds = this.docDisplayConfig.getCollectionPageIds(
- chip.collectionId
- );
- acc.push(...docIds);
- }
- return acc;
- }, [] as string[]);
-
- const fileIds = this.chips.reduce((acc, chip) => {
- if (isFileChip(chip) && chip.blobId) {
- acc.push(chip.blobId);
- }
- return acc;
- }, [] as string[]);
-
- const { docs, files } = contexts;
- return {
- docs: docs.filter(doc => docIds.includes(doc.docId)),
- files: files.filter(file => fileIds.includes(file.blobId)),
- };
- }
}
declare global {
diff --git a/packages/frontend/core/src/components/page-list/selector/selector-layout.tsx b/packages/frontend/core/src/components/page-list/selector/selector-layout.tsx
index f8f5e4348..69ff4afd4 100644
--- a/packages/frontend/core/src/components/page-list/selector/selector-layout.tsx
+++ b/packages/frontend/core/src/components/page-list/selector/selector-layout.tsx
@@ -44,9 +44,10 @@ export const SelectorLayout = ({
);
return (
-
+
{actions ?? (
<>
-