From 6d6504e2af18acae1eec7505dae57c97b72b0e94 Mon Sep 17 00:00:00 2001 From: Saul-Mirone Date: Tue, 22 Apr 2025 07:40:41 +0000 Subject: [PATCH] feat(editor): replace spec provider with extension manager (#11861) Closes: BS-3273 --- blocksuite/affine/all/package.json | 4 +- .../affine/all/src/extensions/migrating.ts | 153 ++++++++++++++++++ blocksuite/affine/all/src/extensions/store.ts | 13 ++ blocksuite/affine/all/src/extensions/view.ts | 42 +++++ .../embed/src/common/render-linked-doc.ts | 10 +- .../embed-edgeless-synced-doc-block.ts | 4 +- .../embed-synced-doc-block.ts | 19 +-- .../affine/blocks/surface-ref/package.json | 1 + .../blocks/surface-ref/src/portal/note.ts | 8 +- .../surface-ref/src/surface-ref-block.ts | 27 ++-- .../affine/blocks/surface-ref/tsconfig.json | 1 + .../src/__tests__/ext-loader.unit.spec.ts | 18 ++- blocksuite/affine/ext-loader/src/manager.ts | 14 +- .../affine/fragments/frame-panel/package.json | 1 + .../frame-panel/src/card/frame-preview.ts | 21 ++- .../fragments/frame-panel/tsconfig.json | 1 + .../src/services/editor-setting-service.ts | 2 +- .../affine/widgets/drag-handle/package.json | 1 + .../drag-handle/src/helpers/preview-helper.ts | 10 +- .../affine/widgets/drag-handle/tsconfig.json | 1 + blocksuite/integration-test/package.json | 4 +- .../src/__tests__/utils/setup.ts | 22 +-- blocksuite/integration-test/src/store.ts | 7 + blocksuite/integration-test/src/view.ts | 7 + blocksuite/playground/apps/starter/main.ts | 10 +- .../apps/starter/utils/collection.ts | 8 +- .../apps/starter/utils/extensions.ts | 11 +- .../ai/chat-panel/chat-panel-messages.ts | 7 +- .../ai/chat-panel/content/rich-text.ts | 6 +- .../src/blocksuite/ai/chat-panel/index.ts | 7 +- .../ai/chat-panel/message/assistant.ts | 5 +- .../blocksuite/ai/components/text-renderer.ts | 24 +-- .../src/blocksuite/ai/extensions/ai-code.ts | 2 +- .../ai/extensions/ai-edgeless-root.ts | 2 +- .../blocksuite/ai/extensions/ai-page-root.ts | 2 +- .../blocksuite/ai/messages/slides-renderer.ts | 10 +- .../ai/peek-view/chat-block-peek-view.ts | 16 +- .../bi-directional-link-panel.tsx | 8 +- .../blocksuite/block-suite-editor/index.ts | 5 +- .../block-suite-editor/lit-adaper.tsx | 24 ++- .../extensions/entry/enable-editor.ts | 18 +-- .../extensions/entry/enable-mobile.ts | 27 ++-- .../extensions/entry/enable-preview.ts | 2 +- .../src/blocksuite/manager/migrating-store.ts | 26 +++ .../src/blocksuite/manager/migrating-view.ts | 144 +++++++++++++++++ .../editor/edgeless/snapshot.tsx | 17 +- .../pages/workspace/detail-page/tabs/chat.tsx | 11 +- .../view/ai-chat-block-peek-view/index.tsx | 3 - .../core/src/modules/workspace/impls/doc.ts | 8 +- tools/utils/src/workspace.gen.ts | 3 + yarn.lock | 3 + 51 files changed, 623 insertions(+), 177 deletions(-) create mode 100644 blocksuite/affine/all/src/extensions/migrating.ts create mode 100644 blocksuite/affine/all/src/extensions/view.ts create mode 100644 blocksuite/integration-test/src/store.ts create mode 100644 blocksuite/integration-test/src/view.ts create mode 100644 packages/frontend/core/src/blocksuite/manager/migrating-store.ts create mode 100644 packages/frontend/core/src/blocksuite/manager/migrating-view.ts diff --git a/blocksuite/affine/all/package.json b/blocksuite/affine/all/package.json index 42956c040d..acc45d5f44 100644 --- a/blocksuite/affine/all/package.json +++ b/blocksuite/affine/all/package.json @@ -208,7 +208,9 @@ "./model": "./src/model/index.ts", "./sync": "./src/sync/index.ts", "./adapters": "./src/adapters/index.ts", - "./extensions": "./src/extensions/index.ts" + "./extensions": "./src/extensions/index.ts", + "./extensions/store": "./src/extensions/store.ts", + "./extensions/view": "./src/extensions/view.ts" }, "files": [ "src", diff --git a/blocksuite/affine/all/src/extensions/migrating.ts b/blocksuite/affine/all/src/extensions/migrating.ts new file mode 100644 index 0000000000..aa6411a9c7 --- /dev/null +++ b/blocksuite/affine/all/src/extensions/migrating.ts @@ -0,0 +1,153 @@ +import { AttachmentBlockSpec } from '@blocksuite/affine-block-attachment'; +import { BookmarkBlockSpec } from '@blocksuite/affine-block-bookmark'; +import { CalloutBlockSpec } from '@blocksuite/affine-block-callout'; +import { CodeBlockSpec } from '@blocksuite/affine-block-code'; +import { DataViewBlockSpec } from '@blocksuite/affine-block-data-view'; +import { DatabaseBlockSpec } from '@blocksuite/affine-block-database'; +import { DividerBlockSpec } from '@blocksuite/affine-block-divider'; +import { EdgelessTextBlockSpec } from '@blocksuite/affine-block-edgeless-text'; +import { EmbedExtensions } from '@blocksuite/affine-block-embed'; +import { FrameBlockSpec } from '@blocksuite/affine-block-frame'; +import { ImageBlockSpec } from '@blocksuite/affine-block-image'; +import { LatexBlockSpec } from '@blocksuite/affine-block-latex'; +import { ListBlockSpec } from '@blocksuite/affine-block-list'; +import { + EdgelessNoteBlockSpec, + NoteBlockSpec, +} from '@blocksuite/affine-block-note'; +import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph'; +import { + EdgelessBuiltInSpecs, + PageRootBlockSpec, + PreviewEdgelessRootBlockSpec, + PreviewPageRootBlockSpec, + ReadOnlyClipboard, +} from '@blocksuite/affine-block-root'; +import { + EdgelessSurfaceBlockAdapterExtensions, + EdgelessSurfaceBlockSpec, + PageSurfaceBlockSpec, + SurfaceBlockAdapterExtensions, +} from '@blocksuite/affine-block-surface'; +import { + EdgelessSurfaceRefBlockSpec, + PageSurfaceRefBlockSpec, +} from '@blocksuite/affine-block-surface-ref'; +import { TableBlockSpec } from '@blocksuite/affine-block-table'; +import { + brushToMarkdownAdapterMatcher, + brushToPlainTextAdapterMatcher, +} from '@blocksuite/affine-gfx-brush'; +import { + connectorToMarkdownAdapterMatcher, + connectorToPlainTextAdapterMatcher, +} from '@blocksuite/affine-gfx-connector'; +import { + groupToMarkdownAdapterMatcher, + groupToPlainTextAdapterMatcher, +} from '@blocksuite/affine-gfx-group'; +import { + mindmapToMarkdownAdapterMatcher, + mindmapToPlainTextAdapterMatcher, +} from '@blocksuite/affine-gfx-mindmap'; +import { + shapeToMarkdownAdapterMatcher, + shapeToPlainTextAdapterMatcher, +} from '@blocksuite/affine-gfx-shape'; +import { + textToMarkdownAdapterMatcher, + textToPlainTextAdapterMatcher, +} from '@blocksuite/affine-gfx-text'; +import { inlinePresetExtensions } from '@blocksuite/affine-inline-preset'; +import { + DefaultOpenDocExtension, + DocDisplayMetaService, + EditPropsStore, + FontLoaderService, +} from '@blocksuite/affine-shared/services'; +import type { ExtensionType } from '@blocksuite/store'; + +const elementToPlainTextAdapterMatchers = [ + groupToPlainTextAdapterMatcher, + shapeToPlainTextAdapterMatcher, + connectorToPlainTextAdapterMatcher, + brushToPlainTextAdapterMatcher, + textToPlainTextAdapterMatcher, + mindmapToPlainTextAdapterMatcher, +]; + +const elementToMarkdownAdapterMatchers = [ + groupToMarkdownAdapterMatcher, + shapeToMarkdownAdapterMatcher, + connectorToMarkdownAdapterMatcher, + brushToMarkdownAdapterMatcher, + textToMarkdownAdapterMatcher, + mindmapToMarkdownAdapterMatcher, +]; + +const CommonBlockSpecs: ExtensionType[] = [ + inlinePresetExtensions, + DocDisplayMetaService, + EditPropsStore, + LatexBlockSpec, + ListBlockSpec, + DatabaseBlockSpec, + TableBlockSpec, + DataViewBlockSpec, + DividerBlockSpec, + BookmarkBlockSpec, + EmbedExtensions, + AttachmentBlockSpec, + CodeBlockSpec, + ImageBlockSpec, + ParagraphBlockSpec, + DefaultOpenDocExtension, + FontLoaderService, + CalloutBlockSpec, + FrameBlockSpec, + + elementToPlainTextAdapterMatchers, + elementToMarkdownAdapterMatchers, +].flat(); + +const PageFirstPartyBlockSpecs: ExtensionType[] = [ + CommonBlockSpecs, + NoteBlockSpec, + PageSurfaceBlockSpec, + PageSurfaceRefBlockSpec, + + ...SurfaceBlockAdapterExtensions, +].flat(); + +const EdgelessFirstPartyBlockSpecs: ExtensionType[] = [ + CommonBlockSpecs, + + EdgelessNoteBlockSpec, + EdgelessSurfaceBlockSpec, + EdgelessSurfaceRefBlockSpec, + EdgelessTextBlockSpec, + + ...EdgelessSurfaceBlockAdapterExtensions, +].flat(); + +export const MigratingEdgelessEditorBlockSpecs: ExtensionType[] = [ + EdgelessBuiltInSpecs, + EdgelessFirstPartyBlockSpecs, +].flat(); + +export const MigratingPageEditorBlockSpecs: ExtensionType[] = [ + PageRootBlockSpec, + PageFirstPartyBlockSpecs, +].flat(); + +export const MigratingPreviewEdgelessEditorBlockSpecs: ExtensionType[] = [ + PreviewEdgelessRootBlockSpec, + EdgelessFirstPartyBlockSpecs, + ReadOnlyClipboard, +].flat(); + +export const MigratingPreviewPageEditorBlockSpecs: ExtensionType[] = [ + PreviewPageRootBlockSpec, + PageFirstPartyBlockSpecs, + ReadOnlyClipboard, +].flat(); diff --git a/blocksuite/affine/all/src/extensions/store.ts b/blocksuite/affine/all/src/extensions/store.ts index 48085e9c11..1cdcb8ec5a 100644 --- a/blocksuite/affine/all/src/extensions/store.ts +++ b/blocksuite/affine/all/src/extensions/store.ts @@ -4,6 +4,10 @@ import { EmbedIframeConfigExtensions } from '@blocksuite/affine-block-embed'; import { ImageStoreSpec } from '@blocksuite/affine-block-image'; import { SurfaceBlockSchemaExtension } from '@blocksuite/affine-block-surface'; import { TableSelectionExtension } from '@blocksuite/affine-block-table'; +import { + type StoreExtensionContext, + StoreExtensionProvider, +} from '@blocksuite/affine-ext-loader'; import { AttachmentBlockSchemaExtension, BookmarkBlockSchemaExtension, @@ -110,3 +114,12 @@ export const StoreExtensions: ExtensionType[] = [ EmbedIframeConfigExtensions, EmbedIframeService, ].flat(); + +export class MigratingStoreExtension extends StoreExtensionProvider { + override name = 'migrating'; + + override setup(context: StoreExtensionContext) { + super.setup(context); + context.register(StoreExtensions); + } +} diff --git a/blocksuite/affine/all/src/extensions/view.ts b/blocksuite/affine/all/src/extensions/view.ts new file mode 100644 index 0000000000..e440078357 --- /dev/null +++ b/blocksuite/affine/all/src/extensions/view.ts @@ -0,0 +1,42 @@ +import { + type ViewExtensionContext, + ViewExtensionProvider, +} from '@blocksuite/affine-ext-loader'; + +import { effects } from '../effects'; +import { + MigratingEdgelessEditorBlockSpecs, + MigratingPageEditorBlockSpecs, + MigratingPreviewEdgelessEditorBlockSpecs, + MigratingPreviewPageEditorBlockSpecs, +} from './migrating'; + +export class MigratingViewExtension extends ViewExtensionProvider { + override name = 'migrating'; + + override effect() { + super.effect(); + effects(); + } + + override setup(context: ViewExtensionContext) { + super.setup(context); + const scope = context.scope; + if (scope === 'preview-page') { + context.register(MigratingPreviewPageEditorBlockSpecs); + return; + } + if (scope === 'preview-edgeless') { + context.register(MigratingPreviewEdgelessEditorBlockSpecs); + return; + } + if (scope === 'page' || scope === 'mobile-page') { + context.register(MigratingPageEditorBlockSpecs); + return; + } + if (scope === 'edgeless' || scope === 'mobile-edgeless') { + context.register(MigratingEdgelessEditorBlockSpecs); + return; + } + } +} diff --git a/blocksuite/affine/blocks/embed/src/common/render-linked-doc.ts b/blocksuite/affine/blocks/embed/src/common/render-linked-doc.ts index 270dfc96c2..3601db8c52 100644 --- a/blocksuite/affine/blocks/embed/src/common/render-linked-doc.ts +++ b/blocksuite/affine/blocks/embed/src/common/render-linked-doc.ts @@ -1,4 +1,5 @@ import { getSurfaceBlock } from '@blocksuite/affine-block-surface'; +import { ViewExtensionManagerIdentifier } from '@blocksuite/affine-ext-loader'; import { type DocMode, ImageBlockModel, @@ -9,7 +10,7 @@ import { } from '@blocksuite/affine-model'; import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts'; import { NotificationProvider } from '@blocksuite/affine-shared/services'; -import { matchModels, SpecProvider } from '@blocksuite/affine-shared/utils'; +import { matchModels } from '@blocksuite/affine-shared/utils'; import { BlockStdScope, EditorLifeCycleExtension } from '@blocksuite/std'; import { type BlockModel, @@ -202,10 +203,13 @@ async function renderNoteContent( match: ids.map(id => ({ id, viewType: 'display' })), }; const previewDoc = doc.doc.getStore({ query }); - const previewSpec = SpecProvider._.getSpec('preview:page'); + const std = card.host.std; + const previewSpec = std + .get(ViewExtensionManagerIdentifier) + .get('preview-page'); const previewStd = new BlockStdScope({ store: previewDoc, - extensions: previewSpec.value, + extensions: previewSpec, }); const previewTemplate = previewStd.render(); const fragment = document.createDocumentFragment(); diff --git a/blocksuite/affine/blocks/embed/src/embed-synced-doc-block/embed-edgeless-synced-doc-block.ts b/blocksuite/affine/blocks/embed/src/embed-synced-doc-block/embed-edgeless-synced-doc-block.ts index 00446dd284..0a92d398a6 100644 --- a/blocksuite/affine/blocks/embed/src/embed-synced-doc-block/embed-edgeless-synced-doc-block.ts +++ b/blocksuite/affine/blocks/embed/src/embed-synced-doc-block/embed-edgeless-synced-doc-block.ts @@ -70,7 +70,7 @@ export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock(
${new BlockStdScope({ store: syncedDoc, - extensions: this._buildPreviewSpec('preview:page'), + extensions: this._buildPreviewSpec('preview-page'), }).render()}
`, @@ -81,7 +81,7 @@ export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock(
${new BlockStdScope({ store: syncedDoc, - extensions: this._buildPreviewSpec('preview:edgeless'), + extensions: this._buildPreviewSpec('preview-edgeless'), }).render()}
`, diff --git a/blocksuite/affine/blocks/embed/src/embed-synced-doc-block/embed-synced-doc-block.ts b/blocksuite/affine/blocks/embed/src/embed-synced-doc-block/embed-synced-doc-block.ts index c31d6e4dad..467f78f532 100644 --- a/blocksuite/affine/blocks/embed/src/embed-synced-doc-block/embed-synced-doc-block.ts +++ b/blocksuite/affine/blocks/embed/src/embed-synced-doc-block/embed-synced-doc-block.ts @@ -1,4 +1,5 @@ import { Peekable } from '@blocksuite/affine-components/peek'; +import { ViewExtensionManagerIdentifier } from '@blocksuite/affine-ext-loader'; import { type DocLinkClickedEvent, RefNodeSlotsProvider, @@ -20,10 +21,7 @@ import { ThemeExtensionIdentifier, ThemeProvider, } from '@blocksuite/affine-shared/services'; -import { - cloneReferenceInfo, - SpecProvider, -} from '@blocksuite/affine-shared/utils'; +import { cloneReferenceInfo } from '@blocksuite/affine-shared/utils'; import { Bound, getCommonBound } from '@blocksuite/global/gfx'; import { BlockSelection, @@ -113,9 +111,10 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent { + protected _buildPreviewSpec = (name: 'preview-page' | 'preview-edgeless') => { const nextDepth = this.depth + 1; - const previewSpecBuilder = SpecProvider._.getSpec(name); + const viewExtensionManager = this.std.get(ViewExtensionManagerIdentifier); + const previewSpec = viewExtensionManager.get(name); const currentDisposables = this.disposables; const editorSetting = this.std.getOptional(EditorSettingProvider) ?? { setting$: signal(GeneralSettingSchema.parse({})), @@ -157,13 +156,11 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent { @@ -204,7 +201,7 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent ${new BlockStdScope({ store: syncedDoc, - extensions: this._buildPreviewSpec('preview:page'), + extensions: this._buildPreviewSpec('preview-page'), }).render()} `, @@ -215,7 +212,7 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent ${new BlockStdScope({ store: syncedDoc, - extensions: this._buildPreviewSpec('preview:edgeless'), + extensions: this._buildPreviewSpec('preview-edgeless'), }).render()} `, diff --git a/blocksuite/affine/blocks/surface-ref/package.json b/blocksuite/affine/blocks/surface-ref/package.json index d130cd07c1..b57d48fac2 100644 --- a/blocksuite/affine/blocks/surface-ref/package.json +++ b/blocksuite/affine/blocks/surface-ref/package.json @@ -13,6 +13,7 @@ "@blocksuite/affine-block-frame": "workspace:*", "@blocksuite/affine-block-surface": "workspace:*", "@blocksuite/affine-components": "workspace:*", + "@blocksuite/affine-ext-loader": "workspace:*", "@blocksuite/affine-inline-reference": "workspace:*", "@blocksuite/affine-model": "workspace:*", "@blocksuite/affine-shared": "workspace:*", diff --git a/blocksuite/affine/blocks/surface-ref/src/portal/note.ts b/blocksuite/affine/blocks/surface-ref/src/portal/note.ts index a80637743a..5089bfc072 100644 --- a/blocksuite/affine/blocks/surface-ref/src/portal/note.ts +++ b/blocksuite/affine/blocks/surface-ref/src/portal/note.ts @@ -1,4 +1,5 @@ import type { CanvasRenderer } from '@blocksuite/affine-block-surface'; +import { ViewExtensionManagerIdentifier } from '@blocksuite/affine-ext-loader'; import type { NoteBlockModel } from '@blocksuite/affine-model'; import { DefaultTheme, @@ -10,7 +11,6 @@ import { EDGELESS_BLOCK_CHILD_PADDING, } from '@blocksuite/affine-shared/consts'; import { ThemeProvider } from '@blocksuite/affine-shared/services'; -import { SpecProvider } from '@blocksuite/affine-shared/utils'; import { deserializeXYWH } from '@blocksuite/global/gfx'; import { WithDisposable } from '@blocksuite/global/lit'; import { @@ -122,10 +122,12 @@ export class SurfaceRefNotePortal extends WithDisposable(ShadowlessElement) { query: this.query, readonly: true, }); - const previewSpec = SpecProvider._.getSpec('preview:page'); + const previewSpec = this.host.std + .get(ViewExtensionManagerIdentifier) + .get('preview-page'); return new BlockStdScope({ store: doc, - extensions: previewSpec.value.slice(), + extensions: previewSpec, }).render(); } diff --git a/blocksuite/affine/blocks/surface-ref/src/surface-ref-block.ts b/blocksuite/affine/blocks/surface-ref/src/surface-ref-block.ts index f93c8b5478..4556971f78 100644 --- a/blocksuite/affine/blocks/surface-ref/src/surface-ref-block.ts +++ b/blocksuite/affine/blocks/surface-ref/src/surface-ref-block.ts @@ -6,6 +6,7 @@ import { import type { BlockCaptionEditor } from '@blocksuite/affine-components/caption'; import { whenHover } from '@blocksuite/affine-components/hover'; import { Peekable } from '@blocksuite/affine-components/peek'; +import { ViewExtensionManagerIdentifier } from '@blocksuite/affine-ext-loader'; import { RefNodeSlotsProvider } from '@blocksuite/affine-inline-reference'; import { FrameBlockModel, @@ -20,10 +21,7 @@ import { ViewportElementExtension, } from '@blocksuite/affine-shared/services'; import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme'; -import { - requestConnectedFrame, - SpecProvider, -} from '@blocksuite/affine-shared/utils'; +import { requestConnectedFrame } from '@blocksuite/affine-shared/utils'; import { DisposableGroup } from '@blocksuite/global/disposable'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import { @@ -46,7 +44,7 @@ import { type GfxModel, GfxPrimitiveElementModel, } from '@blocksuite/std/gfx'; -import type { BaseSelection, Store } from '@blocksuite/store'; +import type { BaseSelection, ExtensionType, Store } from '@blocksuite/store'; import { effect, signal } from '@preact/signals-core'; import { css, html, nothing } from 'lit'; import { query } from 'lit/decorators.js'; @@ -114,9 +112,18 @@ export class SurfaceRefBlockComponent extends BlockComponent
{ class ViewExt1 extends ViewExtensionProvider { override name = 'ViewExt1'; - override setup(context: ViewExtensionContext) { - super.setup(context); + constructor() { + super(); + setup1(); + } + + override setup(context: ViewExtensionContext, option?: { foo: number }) { + super.setup(context, option); if (context.scope === 'page') { - setup1(); context.register(Ext2); } if (context.scope === 'edgeless') { - setup2(); context.register(Ext3); } } @@ -69,6 +72,11 @@ describe('multiple scopes', () => { class ViewExt2 extends ViewExtensionProvider { override name = 'ViewExt2'; + constructor() { + super(); + setup2(); + } + override setup(context: ViewExtensionContext) { super.setup(context); if (context.scope === 'page') { @@ -87,7 +95,7 @@ describe('multiple scopes', () => { expect(edgelessExtensions).toEqual([Ext3, Ext5]); }); - it('should setup be cached', () => { + it('should cache provider instances', () => { manager.get('page'); manager.get('edgeless'); expect(setup1).toHaveBeenCalledTimes(1); diff --git a/blocksuite/affine/ext-loader/src/manager.ts b/blocksuite/affine/ext-loader/src/manager.ts index 661e2d20db..da7857e039 100644 --- a/blocksuite/affine/ext-loader/src/manager.ts +++ b/blocksuite/affine/ext-loader/src/manager.ts @@ -84,16 +84,17 @@ export class ExtensionManager { /** * Retrieves all extensions registered for a specific scope. - * If the scope hasn't been built yet, it triggers the build process. + * It triggers the build process. * * @param scope - The scope to retrieve extensions for * @returns An array of extensions registered for the specified scope * @throws {BlockSuiteError} If the scope is not found */ get(scope: Scope) { - if (!this._extensions.has(scope)) { - this._build(scope); + if (this._extensions.has(scope)) { + this._extensions.delete(scope); } + this._build(scope); const extensionSet = this._extensions.get(scope); if (!extensionSet) { throw new BlockSuiteError( @@ -117,14 +118,19 @@ export class ExtensionManager { provider: typeof BaseExtensionProvider, options: ((prev: T | undefined) => T | undefined) | T | undefined ) { + const prev = this._providerOptions.get(provider); + let config: T | undefined; if (typeof options === 'function') { - const prev = this._providerOptions.get(provider); config = (options as (prev: unknown) => T)(prev); } else { config = options; } + if (prev === config) { + return; + } + if (config === undefined) { this._providerOptions.delete(provider); } else { diff --git a/blocksuite/affine/fragments/frame-panel/package.json b/blocksuite/affine/fragments/frame-panel/package.json index c5db24a79f..051485adc0 100644 --- a/blocksuite/affine/fragments/frame-panel/package.json +++ b/blocksuite/affine/fragments/frame-panel/package.json @@ -13,6 +13,7 @@ "@blocksuite/affine-block-frame": "workspace:*", "@blocksuite/affine-block-surface": "workspace:*", "@blocksuite/affine-components": "workspace:*", + "@blocksuite/affine-ext-loader": "workspace:*", "@blocksuite/affine-model": "workspace:*", "@blocksuite/affine-rich-text": "workspace:*", "@blocksuite/affine-shared": "workspace:*", diff --git a/blocksuite/affine/fragments/frame-panel/src/card/frame-preview.ts b/blocksuite/affine/fragments/frame-panel/src/card/frame-preview.ts index 3db44beac0..8399a19bca 100644 --- a/blocksuite/affine/fragments/frame-panel/src/card/frame-preview.ts +++ b/blocksuite/affine/fragments/frame-panel/src/card/frame-preview.ts @@ -1,10 +1,10 @@ +import { ViewExtensionManagerIdentifier } from '@blocksuite/affine-ext-loader'; import type { FrameBlockModel } from '@blocksuite/affine-model'; import { DocModeExtension, DocModeProvider, ViewportElementExtension, } from '@blocksuite/affine-shared/services'; -import { SpecProvider } from '@blocksuite/affine-shared/utils'; import { DisposableGroup } from '@blocksuite/global/disposable'; import { Bound, deserializeXYWH } from '@blocksuite/global/gfx'; import { WithDisposable } from '@blocksuite/global/lit'; @@ -15,13 +15,12 @@ import { ShadowlessElement, } from '@blocksuite/std'; import { GfxControllerIdentifier } from '@blocksuite/std/gfx'; -import { type Query, type Store } from '@blocksuite/store'; +import { type ExtensionType, type Query, type Store } from '@blocksuite/store'; import { css, html, nothing, type PropertyValues } from 'lit'; import { property, query, state } from 'lit/decorators.js'; import { guard } from 'lit/directives/guard.js'; import { styleMap } from 'lit/directives/style-map.js'; import debounce from 'lodash-es/debounce'; - const DEFAULT_PREVIEW_CONTAINER_WIDTH = 280; const DEFAULT_PREVIEW_CONTAINER_HEIGHT = 166; @@ -86,7 +85,15 @@ export class FramePreview extends WithDisposable(ShadowlessElement) { private _previewDoc: Store | null = null; - private readonly _previewSpec = SpecProvider._.getSpec('preview:edgeless'); + private _runtimePreviewExt: ExtensionType[] = []; + + private get _viewExtensionManager() { + return this.std.get(ViewExtensionManagerIdentifier); + } + + private get _previewSpec() { + return this._viewExtensionManager.get('preview-edgeless'); + } private readonly _updateFrameViewportWH = () => { const [, , w, h] = deserializeXYWH(this.frame.xywh); @@ -143,11 +150,11 @@ export class FramePreview extends WithDisposable(ShadowlessElement) { } const docModeService = this.std.get(DocModeProvider); - this._previewSpec.extend([ + this._runtimePreviewExt = [ ViewportElementExtension('.frame-preview-viewport'), FramePreviewWatcher, DocModeExtension(docModeService), - ]); + ]; } private _refreshViewport() { @@ -163,7 +170,7 @@ export class FramePreview extends WithDisposable(ShadowlessElement) { private _renderSurfaceContent() { const { width, height } = this.frameViewportWH; - const _previewSpec = this._previewSpec.value; + const _previewSpec = this._previewSpec.concat(this._runtimePreviewExt); return html`
{ - di.addImpl(EditorSettingProvider, () => service); + di.override(EditorSettingProvider, () => service); }, }; } diff --git a/blocksuite/affine/widgets/drag-handle/package.json b/blocksuite/affine/widgets/drag-handle/package.json index 33ad908da8..82e205cb07 100644 --- a/blocksuite/affine/widgets/drag-handle/package.json +++ b/blocksuite/affine/widgets/drag-handle/package.json @@ -17,6 +17,7 @@ "@blocksuite/affine-block-paragraph": "workspace:*", "@blocksuite/affine-block-surface": "workspace:*", "@blocksuite/affine-components": "workspace:*", + "@blocksuite/affine-ext-loader": "workspace:*", "@blocksuite/affine-model": "workspace:*", "@blocksuite/affine-shared": "workspace:*", "@blocksuite/global": "workspace:*", diff --git a/blocksuite/affine/widgets/drag-handle/src/helpers/preview-helper.ts b/blocksuite/affine/widgets/drag-handle/src/helpers/preview-helper.ts index f1037f675b..39fafe5553 100644 --- a/blocksuite/affine/widgets/drag-handle/src/helpers/preview-helper.ts +++ b/blocksuite/affine/widgets/drag-handle/src/helpers/preview-helper.ts @@ -1,10 +1,10 @@ +import { ViewExtensionManagerIdentifier } from '@blocksuite/affine-ext-loader'; import { DocModeExtension, DocModeProvider, EditorSettingExtension, EditorSettingProvider, } from '@blocksuite/affine-shared/services'; -import { SpecProvider } from '@blocksuite/affine-shared/utils'; import { BlockStdScope, BlockViewIdentifier } from '@blocksuite/std'; import type { BlockModel, @@ -69,7 +69,9 @@ export class PreviewHelper { const editorSetting = std.get(EditorSettingProvider); const query = this._calculateQuery(blockIds as string[]); const store = widget.doc.doc.getStore({ query }); - const previewSpec = SpecProvider._.getSpec('preview:page'); + let previewSpec = widget.std + .get(ViewExtensionManagerIdentifier) + .get('preview-page'); const settingSignal = signal({ ...editorSetting.setting$.peek() }); const extensions = [ DocModeExtension(docModeService), @@ -99,7 +101,7 @@ export class PreviewHelper { } as ExtensionType, ]; - previewSpec.extend(extensions); + previewSpec = previewSpec.concat(extensions); settingSignal.value = { ...settingSignal.value, @@ -108,7 +110,7 @@ export class PreviewHelper { const previewStd = new BlockStdScope({ store, - extensions: previewSpec.value, + extensions: previewSpec, }); let width: number = 500; diff --git a/blocksuite/affine/widgets/drag-handle/tsconfig.json b/blocksuite/affine/widgets/drag-handle/tsconfig.json index 51215efe4e..89887c9a74 100644 --- a/blocksuite/affine/widgets/drag-handle/tsconfig.json +++ b/blocksuite/affine/widgets/drag-handle/tsconfig.json @@ -14,6 +14,7 @@ { "path": "../../blocks/paragraph" }, { "path": "../../blocks/surface" }, { "path": "../../components" }, + { "path": "../../ext-loader" }, { "path": "../../model" }, { "path": "../../shared" }, { "path": "../../../framework/global" }, diff --git a/blocksuite/integration-test/package.json b/blocksuite/integration-test/package.json index d1b0d35711..569eacc957 100644 --- a/blocksuite/integration-test/package.json +++ b/blocksuite/integration-test/package.json @@ -28,7 +28,9 @@ }, "exports": { ".": "./src/index.ts", - "./effects": "./src/effects.ts" + "./effects": "./src/effects.ts", + "./store": "./src/store.ts", + "./view": "./src/view.ts" }, "files": [ "src", diff --git a/blocksuite/integration-test/src/__tests__/utils/setup.ts b/blocksuite/integration-test/src/__tests__/utils/setup.ts index cfc27eaee0..af06acd4df 100644 --- a/blocksuite/integration-test/src/__tests__/utils/setup.ts +++ b/blocksuite/integration-test/src/__tests__/utils/setup.ts @@ -1,13 +1,7 @@ import '@toeverything/theme/style.css'; import '@toeverything/theme/fonts.css'; -import { effects as blocksEffects } from '@blocksuite/affine/effects'; -import { - EdgelessEditorBlockSpecs, - PageEditorBlockSpecs, - registerStoreSpecs, - StoreExtensions, -} from '@blocksuite/affine/extensions'; +import { registerStoreSpecs } from '@blocksuite/affine/extensions'; import type { DocMode } from '@blocksuite/affine/model'; import { AffineSchemas } from '@blocksuite/affine/schemas'; import { @@ -27,11 +21,17 @@ import { import { effects } from '../../effects.js'; import { TestAffineEditorContainer } from '../../index.js'; +import { getTestStoreManager } from '../../store.js'; +import { getTestViewManager } from '../../view.js'; +// FIXME: used for test import/export registerStoreSpecs(); -blocksEffects(); +const storeManager = getTestStoreManager(); +const viewManager = getTestViewManager(); effects(); +const storeExtensions = storeManager.get('store'); + export function getRenderer() { return editor.std.get( ViewportTurboRendererIdentifier @@ -85,12 +85,12 @@ async function createEditor( editor.doc = doc; editor.mode = mode; editor.pageSpecs = [ - ...PageEditorBlockSpecs, + ...viewManager.get('page'), FontConfigExtension(CommunityCanvasTextFonts), ...extensions, ]; editor.edgelessSpecs = [ - ...EdgelessEditorBlockSpecs, + ...viewManager.get('edgeless'), FontConfigExtension(CommunityCanvasTextFonts), ...extensions, ]; @@ -123,7 +123,7 @@ export async function setupEditor( extensions: ExtensionType[] = [] ) { const collection = new TestWorkspace(createCollectionOptions()); - collection.storeExtensions = StoreExtensions; + collection.storeExtensions = storeExtensions; collection.meta.initialize(); window.collection = collection; diff --git a/blocksuite/integration-test/src/store.ts b/blocksuite/integration-test/src/store.ts new file mode 100644 index 0000000000..63afa01014 --- /dev/null +++ b/blocksuite/integration-test/src/store.ts @@ -0,0 +1,7 @@ +import { StoreExtensionManager } from '@blocksuite/affine/ext-loader'; +import { MigratingStoreExtension } from '@blocksuite/affine/extensions/store'; + +export function getTestStoreManager() { + const manager = new StoreExtensionManager([MigratingStoreExtension]); + return manager; +} diff --git a/blocksuite/integration-test/src/view.ts b/blocksuite/integration-test/src/view.ts new file mode 100644 index 0000000000..fa89a7c729 --- /dev/null +++ b/blocksuite/integration-test/src/view.ts @@ -0,0 +1,7 @@ +import { ViewExtensionManager } from '@blocksuite/affine/ext-loader'; +import { MigratingViewExtension } from '@blocksuite/affine/extensions/view'; + +export function getTestViewManager() { + const manager = new ViewExtensionManager([MigratingViewExtension]); + return manager; +} diff --git a/blocksuite/playground/apps/starter/main.ts b/blocksuite/playground/apps/starter/main.ts index e477728413..b59c2fad0e 100644 --- a/blocksuite/playground/apps/starter/main.ts +++ b/blocksuite/playground/apps/starter/main.ts @@ -2,7 +2,6 @@ import '../../style.css'; import * as databaseBlocks from '@blocksuite/affine/blocks/database'; import * as noteBlocks from '@blocksuite/affine/blocks/note'; -import { effects as blocksEffects } from '@blocksuite/affine/effects'; import { registerStoreSpecs } from '@blocksuite/affine/extensions'; import * as globalUtils from '@blocksuite/affine/global/utils'; import * as services from '@blocksuite/affine/shared/services'; @@ -10,7 +9,8 @@ import * as blockStd from '@blocksuite/affine/std'; import * as store from '@blocksuite/affine/store'; import * as affineModel from '@blocksuite/affine-model'; import * as editor from '@blocksuite/integration-test'; -import { effects as presetsEffects } from '@blocksuite/integration-test/effects'; +import { effects as itEffects } from '@blocksuite/integration-test/effects'; +import { getTestStoreManager } from '@blocksuite/integration-test/store'; import { setupEdgelessTemplate } from '../_common/setup.js'; import { effects as commentEffects } from '../comment/effects.js'; @@ -22,8 +22,8 @@ import { mountDefaultDocEditor } from './utils/setup-playground'; import { prepareTestApp } from './utils/test'; registerStoreSpecs(); -blocksEffects(); -presetsEffects(); +itEffects(); +const storeManager = getTestStoreManager(); commentEffects(); async function main() { @@ -34,7 +34,7 @@ async function main() { const params = new URLSearchParams(location.search); const room = params.get('room') ?? Math.random().toString(16).slice(2, 8); const isE2E = room.startsWith('playwright'); - const collection = createStarterDocCollection(); + const collection = createStarterDocCollection(storeManager); if (isE2E) { Object.defineProperty(window, '$blocksuite', { diff --git a/blocksuite/playground/apps/starter/utils/collection.ts b/blocksuite/playground/apps/starter/utils/collection.ts index 3a4dd30dd8..4fed772180 100644 --- a/blocksuite/playground/apps/starter/utils/collection.ts +++ b/blocksuite/playground/apps/starter/utils/collection.ts @@ -1,5 +1,5 @@ +import type { StoreExtensionManager } from '@blocksuite/affine/ext-loader'; import { AffineSchemas } from '@blocksuite/affine/schemas'; -import { SpecProvider } from '@blocksuite/affine/shared/utils'; import { nanoid, Schema, Transformer } from '@blocksuite/affine/store'; import { createAutoIncrementIdGenerator, @@ -23,7 +23,9 @@ const room = params.get('room'); const isE2E = room?.startsWith('playwright'); const blobSourceArgs = (params.get('blobSource') ?? '').split(','); -export function createStarterDocCollection() { +export function createStarterDocCollection( + storeExtensionManager: StoreExtensionManager +) { const collectionId = room ?? 'starter'; const schema = new Schema(); schema.register(AffineSchemas); @@ -56,7 +58,7 @@ export function createStarterDocCollection() { blobSources, }; const collection = new TestWorkspace(options); - collection.storeExtensions = SpecProvider._.getSpec('store').value; + collection.storeExtensions = storeExtensionManager.get('store'); collection.start(); // debug info diff --git a/blocksuite/playground/apps/starter/utils/extensions.ts b/blocksuite/playground/apps/starter/utils/extensions.ts index 8a44f24c38..1c4fc84bb5 100644 --- a/blocksuite/playground/apps/starter/utils/extensions.ts +++ b/blocksuite/playground/apps/starter/utils/extensions.ts @@ -1,7 +1,3 @@ -import { - EdgelessEditorBlockSpecs, - PageEditorBlockSpecs, -} from '@blocksuite/affine/extensions'; import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference'; import { CommunityCanvasTextFonts, @@ -13,6 +9,7 @@ import { } from '@blocksuite/affine/shared/services'; import type { ExtensionType, Store, Workspace } from '@blocksuite/affine/store'; import { type TestAffineEditorContainer } from '@blocksuite/integration-test'; +import { getTestViewManager } from '@blocksuite/integration-test/view'; import { mockDocModeService, @@ -20,6 +17,8 @@ import { mockParseDocUrlService, } from '../../_common/mock-services'; +const viewManager = getTestViewManager(); + export function getTestCommonExtensions( editor: TestAffineEditorContainer ): ExtensionType[] { @@ -48,8 +47,8 @@ export function createTestEditor(store: Store, workspace: Workspace) { editor.doc = store; const defaultExtensions = getTestCommonExtensions(editor); - editor.pageSpecs = [...PageEditorBlockSpecs, ...defaultExtensions]; - editor.edgelessSpecs = [...EdgelessEditorBlockSpecs, ...defaultExtensions]; + editor.pageSpecs = [...viewManager.get('page'), ...defaultExtensions]; + editor.edgelessSpecs = [...viewManager.get('edgeless'), ...defaultExtensions]; editor.std .get(RefNodeSlotsProvider) diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts index 424a53e0d7..5c84a48ca0 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts @@ -3,10 +3,9 @@ import { DocModeProvider, FeatureFlagService, } from '@blocksuite/affine/shared/services'; -import { type SpecBuilder } from '@blocksuite/affine/shared/utils'; import type { EditorHost } from '@blocksuite/affine/std'; import { ShadowlessElement } from '@blocksuite/affine/std'; -import type { BaseSelection } from '@blocksuite/affine/store'; +import type { BaseSelection, ExtensionType } from '@blocksuite/affine/store'; import { ArrowDownBigIcon as ArrowDownIcon } from '@blocksuite/icons/lit'; import { css, html, nothing, type PropertyValues } from 'lit'; import { property, query, state } from 'lit/decorators.js'; @@ -157,7 +156,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) { accessor updateContext!: (context: Partial) => void; @property({ attribute: false }) - accessor previewSpecBuilder!: SpecBuilder; + accessor extensions!: ExtensionType[]; @query('.chat-panel-messages-container') accessor messagesContainer: HTMLDivElement | null = null; @@ -271,7 +270,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) { .isLast=${isLast} .status=${isLast ? status : 'idle'} .error=${isLast ? error : null} - .previewSpecBuilder=${this.previewSpecBuilder} + .extensions=${this.extensions} .getSessionId=${this.getSessionId} .retry=${() => this.retry()} >`; diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/content/rich-text.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/content/rich-text.ts index 0668154f24..13d807524e 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/content/rich-text.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/content/rich-text.ts @@ -1,7 +1,7 @@ import { WithDisposable } from '@blocksuite/affine/global/lit'; -import type { SpecBuilder } from '@blocksuite/affine/shared/utils'; import type { EditorHost } from '@blocksuite/affine/std'; import { ShadowlessElement } from '@blocksuite/affine/std'; +import type { ExtensionType } from '@blocksuite/affine/store'; import { html } from 'lit'; import { property } from 'lit/decorators.js'; @@ -18,13 +18,13 @@ export class ChatContentRichText extends WithDisposable(ShadowlessElement) { accessor state: 'finished' | 'generating' = 'finished'; @property({ attribute: false }) - accessor previewSpecBuilder!: SpecBuilder; + accessor extensions!: ExtensionType[]; protected override render() { const { text, host } = this; return html`${createTextRenderer(host, { customHeading: true, - extensions: this.previewSpecBuilder.value, + extensions: this.extensions, })(text, this.state)}`; } } diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts index 7e49bf4bfb..ce1f2623fa 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts @@ -2,10 +2,9 @@ import './chat-panel-messages'; import type { ContextEmbedStatus } from '@affine/graphql'; import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit'; -import type { SpecBuilder } from '@blocksuite/affine/shared/utils'; import type { EditorHost } from '@blocksuite/affine/std'; import { ShadowlessElement } from '@blocksuite/affine/std'; -import type { Store } from '@blocksuite/affine/store'; +import type { ExtensionType, Store } from '@blocksuite/affine/store'; import { HelpIcon } from '@blocksuite/icons/lit'; import { type Signal, signal } from '@preact/signals-core'; import { css, html, type PropertyValues } from 'lit'; @@ -208,7 +207,7 @@ export class ChatPanel extends SignalWatcher( accessor docDisplayConfig!: DocDisplayConfig; @property({ attribute: false }) - accessor previewSpecBuilder!: SpecBuilder; + accessor extensions!: ExtensionType[]; @state() accessor isLoading = false; @@ -401,7 +400,7 @@ export class ChatPanel extends SignalWatcher( .updateContext=${this.updateContext} .host=${this.host} .isLoading=${this.isLoading} - .previewSpecBuilder=${this.previewSpecBuilder} + .extensions=${this.extensions} > Promise; @@ -90,7 +91,7 @@ export class ChatMessageAssistant extends WithDisposable(ShadowlessElement) { .host=${host} .text=${item.content} .state=${state} - .previewSpecBuilder=${this.previewSpecBuilder} + .extensions=${this.extensions} > ${shouldRenderError ? AIChatErrorRenderer(host, error) : nothing} ${this.renderEditorActions()} diff --git a/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts b/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts index 2532f37751..5146257242 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts @@ -43,6 +43,18 @@ import type { AffineAIPanelWidgetConfig, } from '../widgets/ai-panel/type'; +export const CustomPageEditorBlockSpecs: ExtensionType[] = [ + ...PageEditorBlockSpecs, + { + setup: di => { + di.override( + BlockViewIdentifier('affine:page'), + () => literal`affine-page-root` + ); + }, + }, +]; + const customHeadingStyles = css` .custom-heading { .h1 { @@ -91,18 +103,6 @@ export type TextRendererOptions = { testId?: string; }; -export const CustomPageEditorBlockSpecs: ExtensionType[] = [ - ...PageEditorBlockSpecs, - { - setup: di => { - di.override( - BlockViewIdentifier('affine:page'), - () => literal`affine-page-root` - ); - }, - }, -]; - // todo: refactor it for more general purpose usage instead of AI only? export class TextRenderer extends WithDisposable(ShadowlessElement) { static override styles = css` diff --git a/packages/frontend/core/src/blocksuite/ai/extensions/ai-code.ts b/packages/frontend/core/src/blocksuite/ai/extensions/ai-code.ts index 5d27584bcd..795102e246 100644 --- a/packages/frontend/core/src/blocksuite/ai/extensions/ai-code.ts +++ b/packages/frontend/core/src/blocksuite/ai/extensions/ai-code.ts @@ -7,7 +7,7 @@ import type { ExtensionType } from '@blocksuite/affine/store'; import { setupCodeToolbarAIEntry } from '../entries/code-toolbar/setup-code-toolbar'; -class AICodeBlockWatcher extends LifeCycleWatcher { +export class AICodeBlockWatcher extends LifeCycleWatcher { static override key = 'ai-code-block-watcher'; override mounted() { diff --git a/packages/frontend/core/src/blocksuite/ai/extensions/ai-edgeless-root.ts b/packages/frontend/core/src/blocksuite/ai/extensions/ai-edgeless-root.ts index d2f8bbcf62..a570648f7c 100644 --- a/packages/frontend/core/src/blocksuite/ai/extensions/ai-edgeless-root.ts +++ b/packages/frontend/core/src/blocksuite/ai/extensions/ai-edgeless-root.ts @@ -47,7 +47,7 @@ export function createAIEdgelessRootBlockSpec( ]; } -function getAIEdgelessRootWatcher(framework: FrameworkProvider) { +export function getAIEdgelessRootWatcher(framework: FrameworkProvider) { class AIEdgelessRootWatcher extends LifeCycleWatcher { static override key = 'ai-edgeless-root-watcher'; diff --git a/packages/frontend/core/src/blocksuite/ai/extensions/ai-page-root.ts b/packages/frontend/core/src/blocksuite/ai/extensions/ai-page-root.ts index b221fdc7af..dccd3aaa1f 100644 --- a/packages/frontend/core/src/blocksuite/ai/extensions/ai-page-root.ts +++ b/packages/frontend/core/src/blocksuite/ai/extensions/ai-page-root.ts @@ -16,7 +16,7 @@ import { } from '../widgets/ai-panel/ai-panel'; import { AiSlashMenuConfigExtension } from './ai-slash-menu'; -function getAIPageRootWatcher(framework: FrameworkProvider) { +export function getAIPageRootWatcher(framework: FrameworkProvider) { class AIPageRootWatcher extends LifeCycleWatcher { static override key = 'ai-page-root-watcher'; diff --git a/packages/frontend/core/src/blocksuite/ai/messages/slides-renderer.ts b/packages/frontend/core/src/blocksuite/ai/messages/slides-renderer.ts index ad7aaf93b9..6c4bf6d3f5 100644 --- a/packages/frontend/core/src/blocksuite/ai/messages/slides-renderer.ts +++ b/packages/frontend/core/src/blocksuite/ai/messages/slides-renderer.ts @@ -1,6 +1,6 @@ import { WorkspaceImpl } from '@affine/core/modules/workspace/impls/workspace'; +import { ViewExtensionManagerIdentifier } from '@blocksuite/affine/ext-loader'; import { WithDisposable } from '@blocksuite/affine/global/lit'; -import { SpecProvider } from '@blocksuite/affine/shared/utils'; import { BlockStdScope, type EditorHost } from '@blocksuite/affine/std'; import type { Store } from '@blocksuite/affine/store'; import { css, html, LitElement, nothing } from 'lit'; @@ -91,6 +91,12 @@ export class AISlidesRenderer extends WithDisposable(LitElement) { }); } + protected _getExtensions() { + return this.host.std + .get(ViewExtensionManagerIdentifier) + .get('preview-edgeless'); + } + protected override render() { return html`