feat(core): support ai insert image, mindmap, slides and make it real in page mode (#9164)

Support issue [BS-2085](https://linear.app/affine-design/issue/BS-2085).

### What changed?
- Refactor the `actionToAnswerRenderer` function to support reuse in both page mode and edgeless mode.
- Add a new `page-response.ts` module to handle AI-generated answers in page mode.
    - Remove the redundant `edgelessHandler` function from `_common/config.ts`.
- Introduce the `AIContext` class along with the `ctx` TypeScript type to standardize context management.
- Implement the `createTemplateJob` function to enable AI slide insertion in both page mode and edgeless mode.

Insert mindmap on page mode:
<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/30630d3e-ebd9-416b-9bb9-5f27086e48a3.mov">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/30630d3e-ebd9-416b-9bb9-5f27086e48a3.mov">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/30630d3e-ebd9-416b-9bb9-5f27086e48a3.mov">mindmap.mov</video>

Insert image on edgeless note
<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/b850ee5a-a06b-4ae7-8b68-ed5929a6e81a.mov">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/b850ee5a-a06b-4ae7-8b68-ed5929a6e81a.mov">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/b850ee5a-a06b-4ae7-8b68-ed5929a6e81a.mov">image3.mov</video>

Insert image on page mode:
<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/c4f98e2d-0b15-4310-b3e0-0725e330302b.mov">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/c4f98e2d-0b15-4310-b3e0-0725e330302b.mov">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/c4f98e2d-0b15-4310-b3e0-0725e330302b.mov">image.mov</video>

Generate image from image:
<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/2776a55f-cbb7-47ce-8e7d-7cae243fa3e9.mov">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/2776a55f-cbb7-47ce-8e7d-7cae243fa3e9.mov">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/2776a55f-cbb7-47ce-8e7d-7cae243fa3e9.mov">image2.mov</video>

Insert presentation on page mode:
<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/4e228fa5-88f4-478c-8b79-647612d5515c.mov">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/4e228fa5-88f4-478c-8b79-647612d5515c.mov">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/4e228fa5-88f4-478c-8b79-647612d5515c.mov">ppt.mov</video>

Insert make it real on page mode:

<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/c71139b2-fb55-4d89-84e2-d52eeb905b57.mov">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/c71139b2-fb55-4d89-84e2-d52eeb905b57.mov">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/c71139b2-fb55-4d89-84e2-d52eeb905b57.mov">make it real.mov</video>
This commit is contained in:
akumatus
2024-12-16 10:04:15 +00:00
parent e6bf4ca6e5
commit 2f79104bdb
38 changed files with 1066 additions and 797 deletions
@@ -1,4 +1,4 @@
import { notify, Scrollable } from '@affine/component';
import { Scrollable } from '@affine/component';
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
import type { ChatPanel } from '@affine/core/blocksuite/presets/ai';
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
@@ -10,7 +10,6 @@ import { useDocMetaHelper } from '@affine/core/components/hooks/use-block-suite-
import { EditorService } from '@affine/core/modules/editor';
import { RecentDocsService } from '@affine/core/modules/quicksearch';
import { ViewService } from '@affine/core/modules/workbench/services/view';
import { useI18n } from '@affine/i18n';
import { RefNodeSlotsProvider } from '@blocksuite/affine/blocks';
import { DisposableGroup } from '@blocksuite/affine/global/utils';
import { type AffineEditorContainer } from '@blocksuite/affine/presets';
@@ -102,7 +101,6 @@ const DetailPageImpl = memo(function DetailPageImpl() {
// TODO(@eyhn): remove jotai here
const [_, setActiveBlockSuiteEditor] = useActiveBlocksuiteEditor();
const t = useI18n();
const enableAI = featureFlagService.flags.enable_ai.value;
useEffect(() => {
@@ -203,22 +201,6 @@ const DetailPageImpl = memo(function DetailPageImpl() {
}
}
disposable.add(
AIProvider.slots.requestRunInEdgeless.on(({ host }) => {
if (host === editorHost) {
notify.warning({
title: t['com.affine.ai.action.edgeless-only.dialog-title'](),
action: {
label: t['Switch'](),
onClick: () => {
editor.setMode('edgeless');
},
},
});
}
})
);
const unbind = editor.bindEditorContainer(
editorContainer,
(editorContainer as any).docTitle, // set from proxy
@@ -230,7 +212,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
disposable.dispose();
};
},
[editor, openPage, docCollection.id, jumpToPageBlock, t]
[editor, openPage, docCollection.id, jumpToPageBlock]
);
const [hasScrollTop, setHasScrollTop] = useState(false);