diff --git a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/chat-panel-messages.ts b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/chat-panel-messages.ts index 9671664e76..063e93b163 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/chat-panel-messages.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/chat-panel-messages.ts @@ -213,6 +213,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) { width: '85%', alignItems: 'center', justifyContent: 'start', + cursor: 'pointer', })} > ${config.icon} diff --git a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/preload-config.ts b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/preload-config.ts index cae6e6b1cd..656ce2024f 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/preload-config.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/preload-config.ts @@ -5,31 +5,62 @@ import { PreloadImageIcon, PreloadPenIcon, } from '../_common/icons.js'; +import { AIProvider } from '../provider.js'; +import completeWritingWithAI from './templates/completeWritingWithAI.zip'; +import freelyCommunicateWithAI from './templates/freelyCommunicateWithAI.zip'; +import readAforeign from './templates/readAforeign.zip'; +import redHat from './templates/redHat.zip'; +import TidyMindMapV3 from './templates/TidyMindMapV3.zip'; export const AIPreloadConfig = [ { icon: ArticleIcon, text: 'Read a foreign language article with AI', - handler: () => {}, //waiting for implementation + handler: () => { + AIProvider.slots.requestInsertTemplate.emit({ + template: readAforeign, + mode: 'edgeless', + }); + }, }, { icon: MindmapIcon, text: 'Tidy a article with AI MindMap Action', - handler: () => {}, + handler: () => { + AIProvider.slots.requestInsertTemplate.emit({ + template: TidyMindMapV3, + mode: 'edgeless', + }); + }, }, { icon: PreloadImageIcon, text: 'Add illustrations to the article', - handler: () => {}, + handler: () => { + AIProvider.slots.requestInsertTemplate.emit({ + template: redHat, + mode: 'edgeless', + }); + }, }, { icon: PreloadPenIcon, text: 'Complete writing with AI', - handler: () => {}, + handler: () => { + AIProvider.slots.requestInsertTemplate.emit({ + template: completeWritingWithAI, + mode: 'edgeless', + }); + }, }, { icon: CommunicateIcon, text: 'Freely communicate with AI', - handler: () => {}, + handler: () => { + AIProvider.slots.requestInsertTemplate.emit({ + template: freelyCommunicateWithAI, + mode: 'edgeless', + }); + }, }, ]; diff --git a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/TidyMindMapV3.zip b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/TidyMindMapV3.zip new file mode 100644 index 0000000000..b6dbc3336d Binary files /dev/null and b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/TidyMindMapV3.zip differ diff --git a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/completeWritingWithAI.zip b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/completeWritingWithAI.zip new file mode 100644 index 0000000000..0eff324b61 Binary files /dev/null and b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/completeWritingWithAI.zip differ diff --git a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/freelyCommunicateWithAI.zip b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/freelyCommunicateWithAI.zip new file mode 100644 index 0000000000..db77575cdc Binary files /dev/null and b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/freelyCommunicateWithAI.zip differ diff --git a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/readAforeign.zip b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/readAforeign.zip new file mode 100644 index 0000000000..bca6943ea7 Binary files /dev/null and b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/readAforeign.zip differ diff --git a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/redHat.zip b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/redHat.zip new file mode 100644 index 0000000000..fb46c953bb Binary files /dev/null and b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/templates/redHat.zip differ diff --git a/packages/frontend/core/src/blocksuite/presets/ai/provider.ts b/packages/frontend/core/src/blocksuite/presets/ai/provider.ts index 6fe8f6e085..7556523bba 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/provider.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/provider.ts @@ -106,6 +106,10 @@ export class AIProvider { // use case: when user selects "continue in chat" in an ask ai result panel // do we need to pass the context to the chat panel? requestOpenWithChat: new Slot(), + requestInsertTemplate: new Slot<{ + template: string; + mode: 'page' | 'edgeless'; + }>(), requestLogin: new Slot<{ host: EditorHost }>(), requestUpgradePlan: new Slot<{ host: EditorHost }>(), // when an action is requested to run in edgeless mode (show a toast in affine) diff --git a/packages/frontend/core/src/layouts/workspace-layout.tsx b/packages/frontend/core/src/layouts/workspace-layout.tsx index 5c91181ecf..e96c1ded46 100644 --- a/packages/frontend/core/src/layouts/workspace-layout.tsx +++ b/packages/frontend/core/src/layouts/workspace-layout.tsx @@ -1,3 +1,4 @@ +import { ZipTransformer } from '@blocksuite/blocks'; import { assertExists } from '@blocksuite/global/utils'; import { DndContext, @@ -21,6 +22,7 @@ import { createPortal } from 'react-dom'; import { Map as YMap } from 'yjs'; import { openSettingModalAtom } from '../atoms'; +import { AIProvider } from '../blocksuite/presets/ai'; import { WorkspaceAIOnboarding } from '../components/affine/ai-onboarding'; import { AppContainer } from '../components/affine/app-container'; import { SyncAwareness } from '../components/affine/awareness'; @@ -107,6 +109,7 @@ export const WorkspaceLayout = function WorkspaceLayout({ export const WorkspaceLayoutInner = ({ children }: PropsWithChildren) => { const currentWorkspace = useService(WorkspaceService).workspace; + const docsList = useService(DocsService).list; const { openPage } = useNavigateHelper(); const pageHelper = usePageHelper(currentWorkspace.docCollection); @@ -121,6 +124,28 @@ export const WorkspaceLayoutInner = ({ children }: PropsWithChildren) => { workbench.location$.map(location => basename + location.pathname) ); + useEffect(() => { + const disposable = AIProvider.slots.requestInsertTemplate.on( + ({ template, mode }) => { + (async () => { + const templateZip = await fetch(template); + const templateBlob = await templateZip.blob(); + const [doc] = await ZipTransformer.importDocs( + currentWorkspace.docCollection, + templateBlob + ); + doc.resetHistory(); + + docsList.setMode(doc.id, mode); + workbench.openPage(doc.id); + })().catch(err => { + console.error(err); + }); + } + ); + return () => disposable.dispose(); + }, [currentWorkspace.docCollection, docsList, workbench]); + useRegisterWorkspaceCommands(); useRegisterNavigationCommands(); useRegisterFindInPageCommands(); diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx index 78ae4b477b..d24ed8b413 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx @@ -95,14 +95,13 @@ const DetailPageImpl = memo(function DetailPageImpl() { }, [editor, isActiveView, setActiveBlockSuiteEditor]); useEffect(() => { - AIProvider.slots.requestOpenWithChat.on(params => { + const disposable = AIProvider.slots.requestOpenWithChat.on(params => { const opened = rightSidebar.isOpen$.value; const actived = activeTabName === 'chat'; if (!opened) { rightSidebar.open(); } - if (!actived) { setActiveTabName('chat'); } @@ -122,7 +121,8 @@ const DetailPageImpl = memo(function DetailPageImpl() { setTabOnLoad(null); } }); - }, [activeTabName, rightSidebar, setActiveTabName, setTabOnLoad]); + return disposable.dispose(); + }, [activeTabName, rightSidebar, setActiveTabName]); useEffect(() => { if (isActiveView) {