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 60ef94f085..79966909c8 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts @@ -43,17 +43,20 @@ import type { AffineAIPanelWidgetConfig, } from '../widgets/ai-panel/type'; -export const getCustomPageEditorBlockSpecs: () => ExtensionType[] = () => [ - ...getViewManager().get('page'), - { - setup: di => { - di.override( - BlockViewIdentifier('affine:page'), - () => literal`affine-page-root` - ); +export const getCustomPageEditorBlockSpecs: () => ExtensionType[] = () => { + const manager = getViewManager().config.init().value; + return [ + ...manager.get('page'), + { + setup: di => { + di.override( + BlockViewIdentifier('affine:page'), + () => literal`affine-page-root` + ); + }, }, - }, -]; + ]; +}; const customHeadingStyles = css` .custom-heading { diff --git a/packages/frontend/core/src/blocksuite/block-suite-editor/bi-directional-link-panel.tsx b/packages/frontend/core/src/blocksuite/block-suite-editor/bi-directional-link-panel.tsx index 54cf2ce36d..c30c4fdab3 100644 --- a/packages/frontend/core/src/blocksuite/block-suite-editor/bi-directional-link-panel.tsx +++ b/packages/frontend/core/src/blocksuite/block-suite-editor/bi-directional-link-panel.tsx @@ -163,7 +163,13 @@ const usePreviewExtensions = () => { const enableAI = useEnableAI(); const extensions = useMemo(() => { - const manager = getViewManager(framework, enableAI); + const manager = getViewManager() + .config.init() + .common(framework, enableAI) + .theme(framework) + .database(framework) + .linkedDoc(framework) + .paragraph(enableAI).value; const specs = manager.get('preview-page'); return [...specs, patchReferenceRenderer(reactToLit, referenceRenderer)]; }, [reactToLit, referenceRenderer, framework, enableAI]); diff --git a/packages/frontend/core/src/blocksuite/block-suite-editor/lit-adaper.tsx b/packages/frontend/core/src/blocksuite/block-suite-editor/lit-adaper.tsx index d4a472a3b8..ff678f6e23 100644 --- a/packages/frontend/core/src/blocksuite/block-suite-editor/lit-adaper.tsx +++ b/packages/frontend/core/src/blocksuite/block-suite-editor/lit-adaper.tsx @@ -6,7 +6,6 @@ import { LitEdgelessEditor, type PageEditor, } from '@affine/core/blocksuite/editors'; -import type { AffineEditorViewOptions } from '@affine/core/blocksuite/manager/editor-view'; import { getViewManager } from '@affine/core/blocksuite/manager/migrating-view'; import { useEnableAI } from '@affine/core/components/hooks/affine/use-enable-ai'; import type { DocCustomPropertyInfo } from '@affine/core/modules/db'; @@ -22,9 +21,8 @@ import { WorkspaceService } from '@affine/core/modules/workspace'; import track from '@affine/track'; import type { DocTitle } from '@blocksuite/affine/fragments/doc-title'; import type { DocMode } from '@blocksuite/affine/model'; -import type { ExtensionType, Store } from '@blocksuite/affine/store'; +import type { Store } from '@blocksuite/affine/store'; import { - type FrameworkProvider, useFramework, useLiveData, useService, @@ -69,7 +67,7 @@ const usePatchSpecs = (mode: DocMode) => { const enableAI = useEnableAI(); - const insidePeekView = useInsidePeekView(); + const isInPeekView = useInsidePeekView(); const enableTurboRenderer = useLiveData( featureFlagService.flags.enable_turbo_renderer.$ @@ -81,33 +79,51 @@ const usePatchSpecs = (mode: DocMode) => { ) ); - const editorOptions: AffineEditorViewOptions = useMemo(() => { - return { - isCloud, - isInPeekView: insidePeekView, + const patchedSpecs = useMemo(() => { + const manager = getViewManager() + .config.init() + .common(framework, enableAI) + .theme(framework) + .editorConfig(framework) + .editorView({ + isCloud, + isInPeekView, + enableTurboRenderer, + enablePDFEmbedPreview, + framework, + reactToLit, + confirmModal, + }) + .edgelessBlockHeader({ + framework, + isInPeekView, + reactToLit, + }) + .database(framework) + .linkedDoc(framework) + .paragraph(enableAI).value; - enableTurboRenderer, - enablePDFEmbedPreview, - - framework, - - reactToLit: reactToLit as AffineEditorViewOptions['reactToLit'], - confirmModal, - }; + if (BUILD_CONFIG.isMobileEdition) { + if (mode === 'page') { + return manager.get('mobile-page'); + } else { + return manager.get('mobile-edgeless'); + } + } else { + return manager.get(mode); + } }, [ confirmModal, + enableAI, enablePDFEmbedPreview, enableTurboRenderer, framework, - insidePeekView, + isInPeekView, isCloud, + mode, reactToLit, ]); - const patchedSpecs = useMemo(() => { - return enableEditorExtension(framework, mode, enableAI, editorOptions); - }, [framework, mode, enableAI, editorOptions]); - return [ patchedSpecs, useMemo( @@ -302,20 +318,3 @@ export const BlocksuiteEdgelessEditor = forwardRef< ); }); - -function enableEditorExtension( - framework: FrameworkProvider, - mode: 'edgeless' | 'page', - enableAI: boolean, - options: AffineEditorViewOptions -): ExtensionType[] { - const manager = getViewManager(framework, enableAI, options); - if (BUILD_CONFIG.isMobileEdition) { - if (mode === 'page') { - return manager.get('mobile-page'); - } - - return manager.get('mobile-edgeless'); - } - return manager.get(mode); -} diff --git a/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/index.tsx b/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/index.tsx index 56c13e5c04..b7c3425be3 100644 --- a/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/index.tsx +++ b/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/index.tsx @@ -1,110 +1,42 @@ -import type { ReactToLit } from '@affine/component'; -import { JournalService } from '@affine/core/modules/journal'; -import { EmbedSyncedDocConfigExtension } from '@blocksuite/affine/blocks/embed-doc'; -import { NoteConfigExtension } from '@blocksuite/affine/blocks/note'; -import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine/blocks/root'; -import { Bound, Vec } from '@blocksuite/affine/global/gfx'; +import type { ElementOrFactory } from '@affine/component'; import { - DocModeProvider, - EditPropsStore, -} from '@blocksuite/affine/shared/services'; -import { GfxControllerIdentifier } from '@blocksuite/affine/std/gfx'; -import type { FrameworkProvider } from '@toeverything/infra'; -import { html } from 'lit'; + patchForEdgelessNoteConfig, + patchForEmbedSyncedDocConfig, +} from '@affine/core/blocksuite/extensions/edgeless-block-header/patch'; +import { + type ViewExtensionContext, + ViewExtensionProvider, +} from '@blocksuite/affine/ext-loader'; +import { FrameworkProvider } from '@toeverything/infra'; +import type { TemplateResult } from 'lit'; +import { z } from 'zod'; -import { BlocksuiteEditorJournalDocTitle } from '../../block-suite-editor/journal-doc-title'; -import { EdgelessEmbedSyncedDocHeader } from './edgeless-embed-synced-doc-header'; -import { EdgelessNoteHeader } from './edgeless-note-header'; +const optionsSchema = z.object({ + isInPeekView: z.boolean(), + framework: z.instanceof(FrameworkProvider), + reactToLit: z + .function() + .args(z.custom(), z.boolean().optional()) + .returns(z.custom()), +}); -export function patchForEdgelessNoteConfig( - framework: FrameworkProvider, - reactToLit: ReactToLit, - insidePeekView: boolean -) { - return NoteConfigExtension({ - edgelessNoteHeader: ({ note }) => - reactToLit(), - pageBlockTitle: ({ note }) => { - const journalService = framework.get(JournalService); - const isJournal = !!journalService.journalDate$(note.store.id).value; - if (isJournal) { - return reactToLit( - - ); - } else { - return html``; - } - }, - pageBlockViewportFitAnimation: insidePeekView - ? undefined - : ({ std, note }) => { - const storedViewport = std.get(EditPropsStore).getStorage('viewport'); - // if there is a stored viewport, don't run the animation - // in other word, this doc has been opened before - if (storedViewport) return false; +export type EdgelessBlockHeaderViewOptions = z.infer; - if (!std.store.root) return false; - const rootView = std.view.getBlock(std.store.root.id); - if (!rootView) return false; +export class EdgelessBlockHeaderConfigViewExtension extends ViewExtensionProvider { + override name = 'header-config-view'; + override schema = optionsSchema; - const gfx = std.get(GfxControllerIdentifier); - const primaryMode = std - .get(DocModeProvider) - .getPrimaryMode(std.store.id); + override setup( + context: ViewExtensionContext, + options?: EdgelessBlockHeaderViewOptions + ) { + super.setup(context, options); + if (!options) return; + const { framework, isInPeekView, reactToLit } = options; - if (primaryMode !== 'page' || !note || note.props.edgeless.collapse) { - return false; - } - - const leftPadding = parseInt( - window - .getComputedStyle(rootView) - .getPropertyValue('--affine-editor-side-padding') - .replace('px', '') - ); - if (isNaN(leftPadding)) { - return false; - } - - let editorWidth = parseInt( - window - .getComputedStyle(rootView) - .getPropertyValue('--affine-editor-width') - .replace('px', '') - ); - if (isNaN(editorWidth)) { - return false; - } - - const containerWidth = rootView.getBoundingClientRect().width; - const leftMargin = - containerWidth > editorWidth - ? (containerWidth - editorWidth) / 2 - : 0; - - const pageTitleAnchor = gfx.viewport.toModelCoord( - leftPadding + leftMargin, - 0 - ); - - const noteBound = Bound.deserialize(note.xywh); - const edgelessTitleAnchor = Vec.add(noteBound.tl, [ - EDGELESS_BLOCK_CHILD_PADDING, - 12, - ]); - - const center = Vec.sub(edgelessTitleAnchor, pageTitleAnchor); - gfx.viewport.setCenter(center[0], center[1]); - gfx.viewport.smoothZoom(0.65, undefined, 15); - - return true; - }, - }); -} - -export function patchForEmbedSyncedDocConfig(reactToLit: ReactToLit) { - return EmbedSyncedDocConfigExtension({ - edgelessHeader: ({ model, std }) => - reactToLit(), - }); + context.register( + patchForEdgelessNoteConfig(framework, reactToLit, isInPeekView) + ); + context.register(patchForEmbedSyncedDocConfig(reactToLit)); + } } diff --git a/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/patch.tsx b/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/patch.tsx new file mode 100644 index 0000000000..56c13e5c04 --- /dev/null +++ b/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/patch.tsx @@ -0,0 +1,110 @@ +import type { ReactToLit } from '@affine/component'; +import { JournalService } from '@affine/core/modules/journal'; +import { EmbedSyncedDocConfigExtension } from '@blocksuite/affine/blocks/embed-doc'; +import { NoteConfigExtension } from '@blocksuite/affine/blocks/note'; +import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine/blocks/root'; +import { Bound, Vec } from '@blocksuite/affine/global/gfx'; +import { + DocModeProvider, + EditPropsStore, +} from '@blocksuite/affine/shared/services'; +import { GfxControllerIdentifier } from '@blocksuite/affine/std/gfx'; +import type { FrameworkProvider } from '@toeverything/infra'; +import { html } from 'lit'; + +import { BlocksuiteEditorJournalDocTitle } from '../../block-suite-editor/journal-doc-title'; +import { EdgelessEmbedSyncedDocHeader } from './edgeless-embed-synced-doc-header'; +import { EdgelessNoteHeader } from './edgeless-note-header'; + +export function patchForEdgelessNoteConfig( + framework: FrameworkProvider, + reactToLit: ReactToLit, + insidePeekView: boolean +) { + return NoteConfigExtension({ + edgelessNoteHeader: ({ note }) => + reactToLit(), + pageBlockTitle: ({ note }) => { + const journalService = framework.get(JournalService); + const isJournal = !!journalService.journalDate$(note.store.id).value; + if (isJournal) { + return reactToLit( + + ); + } else { + return html``; + } + }, + pageBlockViewportFitAnimation: insidePeekView + ? undefined + : ({ std, note }) => { + const storedViewport = std.get(EditPropsStore).getStorage('viewport'); + // if there is a stored viewport, don't run the animation + // in other word, this doc has been opened before + if (storedViewport) return false; + + if (!std.store.root) return false; + const rootView = std.view.getBlock(std.store.root.id); + if (!rootView) return false; + + const gfx = std.get(GfxControllerIdentifier); + const primaryMode = std + .get(DocModeProvider) + .getPrimaryMode(std.store.id); + + if (primaryMode !== 'page' || !note || note.props.edgeless.collapse) { + return false; + } + + const leftPadding = parseInt( + window + .getComputedStyle(rootView) + .getPropertyValue('--affine-editor-side-padding') + .replace('px', '') + ); + if (isNaN(leftPadding)) { + return false; + } + + let editorWidth = parseInt( + window + .getComputedStyle(rootView) + .getPropertyValue('--affine-editor-width') + .replace('px', '') + ); + if (isNaN(editorWidth)) { + return false; + } + + const containerWidth = rootView.getBoundingClientRect().width; + const leftMargin = + containerWidth > editorWidth + ? (containerWidth - editorWidth) / 2 + : 0; + + const pageTitleAnchor = gfx.viewport.toModelCoord( + leftPadding + leftMargin, + 0 + ); + + const noteBound = Bound.deserialize(note.xywh); + const edgelessTitleAnchor = Vec.add(noteBound.tl, [ + EDGELESS_BLOCK_CHILD_PADDING, + 12, + ]); + + const center = Vec.sub(edgelessTitleAnchor, pageTitleAnchor); + gfx.viewport.setCenter(center[0], center[1]); + gfx.viewport.smoothZoom(0.65, undefined, 15); + + return true; + }, + }); +} + +export function patchForEmbedSyncedDocConfig(reactToLit: ReactToLit) { + return EmbedSyncedDocConfigExtension({ + edgelessHeader: ({ model, std }) => + reactToLit(), + }); +} diff --git a/packages/frontend/core/src/blocksuite/extensions/editor-config/get-config.ts b/packages/frontend/core/src/blocksuite/extensions/editor-config/get-config.ts new file mode 100644 index 0000000000..abcbb6698c --- /dev/null +++ b/packages/frontend/core/src/blocksuite/extensions/editor-config/get-config.ts @@ -0,0 +1,29 @@ +import { + createCustomToolbarExtension, + createToolbarMoreMenuConfig, +} from '@affine/core/blocksuite/extensions/editor-config/toolbar'; +import { WorkspaceServerService } from '@affine/core/modules/cloud'; +import { EditorSettingService } from '@affine/core/modules/editor-setting'; +import { ToolbarMoreMenuConfigExtension } from '@blocksuite/affine/components/toolbar'; +import { EditorSettingExtension } from '@blocksuite/affine/shared/services'; +import type { ExtensionType } from '@blocksuite/store'; +import type { FrameworkProvider } from '@toeverything/infra'; + +export function getEditorConfigExtension( + framework: FrameworkProvider +): ExtensionType[] { + const editorSettingService = framework.get(EditorSettingService); + const workspaceServerService = framework.get(WorkspaceServerService); + const baseUrl = workspaceServerService.server?.baseUrl ?? location.origin; + + return [ + EditorSettingExtension({ + // eslint-disable-next-line rxjs/finnish + setting$: editorSettingService.editorSetting.settingSignal, + set: (k, v) => editorSettingService.editorSetting.set(k, v), + }), + ToolbarMoreMenuConfigExtension(createToolbarMoreMenuConfig(framework)), + + createCustomToolbarExtension(editorSettingService.editorSetting, baseUrl), + ].flat(); +} diff --git a/packages/frontend/core/src/blocksuite/extensions/editor-config/index.ts b/packages/frontend/core/src/blocksuite/extensions/editor-config/index.ts index 834dc066e6..1f93db7217 100644 --- a/packages/frontend/core/src/blocksuite/extensions/editor-config/index.ts +++ b/packages/frontend/core/src/blocksuite/extensions/editor-config/index.ts @@ -1,30 +1,34 @@ -import { WorkspaceServerService } from '@affine/core/modules/cloud'; -import { EditorSettingService } from '@affine/core/modules/editor-setting'; -import { ToolbarMoreMenuConfigExtension } from '@blocksuite/affine/components/toolbar'; -import { EditorSettingExtension } from '@blocksuite/affine/shared/services'; -import type { ExtensionType } from '@blocksuite/affine/store'; -import type { FrameworkProvider } from '@toeverything/infra'; - +import { getEditorConfigExtension } from '@affine/core/blocksuite/extensions/editor-config/get-config'; import { - createCustomToolbarExtension, - createToolbarMoreMenuConfig, -} from './toolbar'; + type ViewExtensionContext, + ViewExtensionProvider, +} from '@blocksuite/affine/ext-loader'; +import { FrameworkProvider } from '@toeverything/infra'; +import { z } from 'zod'; -export function getEditorConfigExtension( - framework: FrameworkProvider -): ExtensionType[] { - const editorSettingService = framework.get(EditorSettingService); - const workspaceServerService = framework.get(WorkspaceServerService); - const baseUrl = workspaceServerService.server?.baseUrl ?? location.origin; +const optionsSchema = z.object({ + framework: z.instanceof(FrameworkProvider).optional(), +}); - return [ - EditorSettingExtension({ - // eslint-disable-next-line rxjs/finnish - setting$: editorSettingService.editorSetting.settingSignal, - set: (k, v) => editorSettingService.editorSetting.set(k, v), - }), - ToolbarMoreMenuConfigExtension(createToolbarMoreMenuConfig(framework)), +type AffineEditorConfigViewOptions = z.infer; - createCustomToolbarExtension(editorSettingService.editorSetting, baseUrl), - ].flat(); +export class AffineEditorConfigViewExtension extends ViewExtensionProvider { + override name = 'affine-view-editor-config'; + + override schema = optionsSchema; + + override setup( + context: ViewExtensionContext, + options?: AffineEditorConfigViewOptions + ) { + super.setup(context, options); + const framework = options?.framework; + if (!framework) { + return; + } + + if (context.scope === 'edgeless' || context.scope === 'page') { + context.register(getEditorConfigExtension(framework)); + } + } } diff --git a/packages/frontend/core/src/blocksuite/extensions/theme/index.ts b/packages/frontend/core/src/blocksuite/extensions/theme/index.ts new file mode 100644 index 0000000000..71460304e6 --- /dev/null +++ b/packages/frontend/core/src/blocksuite/extensions/theme/index.ts @@ -0,0 +1,37 @@ +import { getPreviewThemeExtension } from '@affine/core/blocksuite/extensions/theme/preview-theme'; +import { getThemeExtension } from '@affine/core/blocksuite/extensions/theme/theme'; +import { + type ViewExtensionContext, + ViewExtensionProvider, +} from '@blocksuite/affine/ext-loader'; +import { FrameworkProvider } from '@toeverything/infra'; +import { z } from 'zod'; + +const optionsSchema = z.object({ + framework: z.instanceof(FrameworkProvider).optional(), +}); + +type AffineThemeViewOptions = z.infer; + +export class AffineThemeViewExtension extends ViewExtensionProvider { + override name = 'affine-view-theme'; + + override schema = optionsSchema; + + override setup( + context: ViewExtensionContext, + options?: AffineThemeViewOptions + ) { + super.setup(context, options); + const framework = options?.framework; + if (!framework) { + return; + } + + if (this.isPreview(context.scope)) { + context.register(getPreviewThemeExtension(framework)); + } else { + context.register(getThemeExtension(framework)); + } + } +} diff --git a/packages/frontend/core/src/blocksuite/extensions/theme/preview-theme.ts b/packages/frontend/core/src/blocksuite/extensions/theme/preview-theme.ts new file mode 100644 index 0000000000..9fa06674cf --- /dev/null +++ b/packages/frontend/core/src/blocksuite/extensions/theme/preview-theme.ts @@ -0,0 +1,73 @@ +import { AppThemeService } from '@affine/core/modules/theme'; +import { ColorScheme } from '@blocksuite/affine/model'; +import { + type ThemeExtension, + ThemeExtensionIdentifier, +} from '@blocksuite/affine/shared/services'; +import { + createSignalFromObservable, + type Signal, +} from '@blocksuite/affine/shared/utils'; +import { + type BlockStdScope, + LifeCycleWatcher, + StdIdentifier, +} from '@blocksuite/affine/std'; +import type { Container } from '@blocksuite/global/di'; +import type { FrameworkProvider } from '@toeverything/infra'; +import type { Observable } from 'rxjs'; + +export function getPreviewThemeExtension(framework: FrameworkProvider) { + class AffinePagePreviewThemeExtension + extends LifeCycleWatcher + implements ThemeExtension + { + static override readonly key = 'affine-page-preview-theme'; + + readonly theme: Signal; + + readonly disposables: (() => void)[] = []; + + static override setup(di: Container) { + super.setup(di); + di.override(ThemeExtensionIdentifier, AffinePagePreviewThemeExtension, [ + StdIdentifier, + ]); + } + + constructor(std: BlockStdScope) { + super(std); + const theme$: Observable = framework + .get(AppThemeService) + .appTheme.theme$.map(theme => { + return theme === ColorScheme.Dark + ? ColorScheme.Dark + : ColorScheme.Light; + }); + const { signal, cleanup } = createSignalFromObservable( + theme$, + ColorScheme.Light + ); + this.theme = signal; + this.disposables.push(cleanup); + } + + getAppTheme() { + return this.theme; + } + + getEdgelessTheme() { + return this.theme; + } + + override unmounted() { + this.dispose(); + } + + dispose() { + this.disposables.forEach(dispose => dispose()); + } + } + + return AffinePagePreviewThemeExtension; +} diff --git a/packages/frontend/core/src/blocksuite/extensions/theme.ts b/packages/frontend/core/src/blocksuite/extensions/theme/theme.ts similarity index 67% rename from packages/frontend/core/src/blocksuite/extensions/theme.ts rename to packages/frontend/core/src/blocksuite/extensions/theme/theme.ts index 950d0bce4b..2ee25ae72b 100644 --- a/packages/frontend/core/src/blocksuite/extensions/theme.ts +++ b/packages/frontend/core/src/blocksuite/extensions/theme/theme.ts @@ -10,11 +10,7 @@ import { createSignalFromObservable, type Signal, } from '@blocksuite/affine/shared/utils'; -import { - type BlockStdScope, - LifeCycleWatcher, - StdIdentifier, -} from '@blocksuite/affine/std'; +import { LifeCycleWatcher, StdIdentifier } from '@blocksuite/affine/std'; import { type FrameworkProvider } from '@toeverything/infra'; import type { Observable } from 'rxjs'; import { combineLatest, map } from 'rxjs'; @@ -99,58 +95,3 @@ export function getThemeExtension( return AffineThemeExtension; } - -export function getPreviewThemeExtension(framework: FrameworkProvider) { - class AffinePagePreviewThemeExtension - extends LifeCycleWatcher - implements ThemeExtension - { - static override readonly key = 'affine-page-preview-theme'; - - readonly theme: Signal; - - readonly disposables: (() => void)[] = []; - - static override setup(di: Container) { - super.setup(di); - di.override(ThemeExtensionIdentifier, AffinePagePreviewThemeExtension, [ - StdIdentifier, - ]); - } - - constructor(std: BlockStdScope) { - super(std); - const theme$: Observable = framework - .get(AppThemeService) - .appTheme.theme$.map(theme => { - return theme === ColorScheme.Dark - ? ColorScheme.Dark - : ColorScheme.Light; - }); - const { signal, cleanup } = createSignalFromObservable( - theme$, - ColorScheme.Light - ); - this.theme = signal; - this.disposables.push(cleanup); - } - - getAppTheme() { - return this.theme; - } - - getEdgelessTheme() { - return this.theme; - } - - override unmounted() { - this.dispose(); - } - - dispose() { - this.disposables.forEach(dispose => dispose()); - } - } - - return AffinePagePreviewThemeExtension; -} diff --git a/packages/frontend/core/src/blocksuite/manager/code-block-preview.ts b/packages/frontend/core/src/blocksuite/manager/code-block-preview.ts index 4819a57bc1..d06c820aee 100644 --- a/packages/frontend/core/src/blocksuite/manager/code-block-preview.ts +++ b/packages/frontend/core/src/blocksuite/manager/code-block-preview.ts @@ -8,7 +8,7 @@ import { effects as htmlPreviewEffects, } from '../extensions/code-block-preview/html-preview'; -export class CodeBlockPreviewExtensionProvider extends ViewExtensionProvider { +export class CodeBlockPreviewViewExtension extends ViewExtensionProvider { override name = 'code-block-preview'; override effect() { diff --git a/packages/frontend/core/src/blocksuite/manager/common-view.ts b/packages/frontend/core/src/blocksuite/manager/common-view.ts index 1ecabcec5d..10036a8630 100644 --- a/packages/frontend/core/src/blocksuite/manager/common-view.ts +++ b/packages/frontend/core/src/blocksuite/manager/common-view.ts @@ -11,15 +11,10 @@ import { CopilotTool } from '@affine/core/blocksuite/ai/tool/copilot-tool'; import { aiPanelWidget } from '@affine/core/blocksuite/ai/widgets/ai-panel/ai-panel'; import { edgelessCopilotWidget } from '@affine/core/blocksuite/ai/widgets/edgeless-copilot'; import { buildDocDisplayMetaExtension } from '@affine/core/blocksuite/extensions/display-meta'; -import { getEditorConfigExtension } from '@affine/core/blocksuite/extensions/editor-config'; import { patchFileSizeLimitExtension } from '@affine/core/blocksuite/extensions/file-size-limit'; import { getFontConfigExtension } from '@affine/core/blocksuite/extensions/font-config'; import { patchPeekViewService } from '@affine/core/blocksuite/extensions/peek-view-service'; import { getTelemetryExtension } from '@affine/core/blocksuite/extensions/telemetry'; -import { - getPreviewThemeExtension, - getThemeExtension, -} from '@affine/core/blocksuite/extensions/theme'; import { PeekViewService } from '@affine/core/modules/peek-view'; import { type ViewExtensionContext, @@ -42,17 +37,6 @@ export class AffineCommonViewExtension extends ViewExtensionProvider< override schema = optionsSchema; - private _setupTheme( - context: ViewExtensionContext, - framework: FrameworkProvider - ) { - if (this.isPreview(context.scope)) { - context.register(getPreviewThemeExtension(framework)); - } else { - context.register(getThemeExtension(framework)); - } - } - private _setupAI( context: ViewExtensionContext, framework: FrameworkProvider @@ -97,7 +81,7 @@ export class AffineCommonViewExtension extends ViewExtensionProvider< context: ViewExtensionContext, options?: z.infer ) { - super.setup(context); + super.setup(context, options); const { framework, enableAI } = options || {}; if (framework) { context.register(patchPeekViewService(framework.get(PeekViewService))); @@ -106,9 +90,7 @@ export class AffineCommonViewExtension extends ViewExtensionProvider< buildDocDisplayMetaExtension(framework), ]); context.register(getTelemetryExtension()); - this._setupTheme(context, framework); if (context.scope === 'edgeless' || context.scope === 'page') { - context.register(getEditorConfigExtension(framework)); context.register(patchFileSizeLimitExtension(framework)); } if (enableAI) { diff --git a/packages/frontend/core/src/blocksuite/manager/editor-view.tsx b/packages/frontend/core/src/blocksuite/manager/editor-view.tsx index a6c5737d81..17877a090d 100644 --- a/packages/frontend/core/src/blocksuite/manager/editor-view.tsx +++ b/packages/frontend/core/src/blocksuite/manager/editor-view.tsx @@ -6,10 +6,6 @@ import { import { patchDatabaseBlockConfigService } from '@affine/core/blocksuite/extensions/database-block-config-service'; import { patchDocModeService } from '@affine/core/blocksuite/extensions/doc-mode-service'; import { patchDocUrlExtensions } from '@affine/core/blocksuite/extensions/doc-url'; -import { - patchForEdgelessNoteConfig, - patchForEmbedSyncedDocConfig, -} from '@affine/core/blocksuite/extensions/edgeless-block-header'; import { EdgelessClipboardAIChatConfig } from '@affine/core/blocksuite/extensions/edgeless-clipboard'; import { patchForClipboardInElectron } from '@affine/core/blocksuite/extensions/electron-clipboard'; import { patchNotificationService } from '@affine/core/blocksuite/extensions/notification-service'; @@ -116,13 +112,12 @@ export class AffineEditorViewExtension extends ViewExtensionProvider { - const placeholders = { - text: "Type '/' for commands, 'space' for AI", - h1: 'Heading 1', - h2: 'Heading 2', - h3: 'Heading 3', - h4: 'Heading 4', - h5: 'Heading 5', - h6: 'Heading 6', - quote: '', - }; - return placeholders[model.props.type] ?? ''; - }, + private readonly _manager: ViewExtensionManager; + + constructor() { + this._manager = new ViewExtensionManager([ + ...getInternalViewExtensions(), + + AffineThemeViewExtension, + AffineCommonViewExtension, + AffineEditorViewExtension, + AffineEditorConfigViewExtension, + CodeBlockPreviewViewExtension, + EdgelessBlockHeaderConfigViewExtension, + ]); + } + + get value() { + return this._manager; + } + + get config() { + return { + init: this._initDefaultConfig, + common: this._configureCommon, + editorView: this._configureEditorView, + theme: this._configureTheme, + editorConfig: this._configureEditorConfig, + edgelessBlockHeader: this._configureEdgelessBlockHeader, + database: this._configureDatabase, + linkedDoc: this._configureLinkedDoc, + paragraph: this._configureParagraph, + value: this._manager, + }; + } + + private readonly _initDefaultConfig = () => { + this.config + .common() + .theme() + .editorView() + .editorConfig() + .edgelessBlockHeader() + .database() + .linkedDoc() + .paragraph(); + + return this.config; + }; + + private readonly _configureCommon = ( + framework?: FrameworkProvider, + enableAI?: boolean + ) => { + this._manager.configure(AffineCommonViewExtension, { + framework, + enableAI, }); - } - return manager; + return this.config; + }; + + private readonly _configureEditorView = ( + options?: AffineEditorViewOptions + ) => { + this._manager.configure(AffineEditorViewExtension, options); + return this.config; + }; + + private readonly _configureTheme = (framework?: FrameworkProvider) => { + this._manager.configure(AffineThemeViewExtension, { framework }); + return this.config; + }; + + private readonly _configureEditorConfig = (framework?: FrameworkProvider) => { + this._manager.configure(AffineEditorConfigViewExtension, { framework }); + return this.config; + }; + + private readonly _configureEdgelessBlockHeader = ( + options?: EdgelessBlockHeaderViewOptions + ) => { + this._manager.configure(EdgelessBlockHeaderConfigViewExtension, options); + return this.config; + }; + + private readonly _configureDatabase = (framework?: FrameworkProvider) => { + if (framework) { + this._manager.configure( + DatabaseViewExtension, + createDatabaseOptionsConfig(framework) + ); + } + return this.config; + }; + + private readonly _configureLinkedDoc = (framework?: FrameworkProvider) => { + if (framework) { + this._manager.configure( + LinkedDocViewExtension, + createLinkedWidgetConfig(framework) + ); + } + return this.config; + }; + + private readonly _configureParagraph = (enableAI?: boolean) => { + if (enableAI) { + this._manager.configure(ParagraphViewExtension, { + getPlaceholder: model => { + const placeholders = { + text: "Type '/' for commands, 'space' for AI", + h1: 'Heading 1', + h2: 'Heading 2', + h3: 'Heading 3', + h4: 'Heading 4', + h5: 'Heading 5', + h6: 'Heading 6', + quote: '', + }; + return placeholders[model.props.type] ?? ''; + }, + }); + } + return this.config; + }; +} + +export function getViewManager() { + return ViewProvider.getInstance(); } diff --git a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx index ff41948577..fbff4ea53f 100644 --- a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx +++ b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx @@ -56,7 +56,12 @@ export const EdgelessSnapshot = (props: Props) => { const { editorSetting } = framework.get(EditorSettingService); const extensions = useMemo(() => { - const manager = getViewManager(framework, false); + const manager = getViewManager() + .config.init() + .common(framework) + .theme(framework) + .database(framework) + .linkedDoc(framework).value; return manager .get('preview-edgeless') .concat([ViewportElementExtension('.ref-viewport')]); diff --git a/packages/frontend/core/src/modules/doc-info/use-std.ts b/packages/frontend/core/src/modules/doc-info/use-std.ts index 9529b1e742..7815e057c3 100644 --- a/packages/frontend/core/src/modules/doc-info/use-std.ts +++ b/packages/frontend/core/src/modules/doc-info/use-std.ts @@ -10,7 +10,7 @@ export function createBlockStdScope(doc: Store) { logger.debug('createBlockStdScope', doc.id); const std = new BlockStdScope({ store: doc, - extensions: getViewManager().get('page'), + extensions: getViewManager().config.init().value.get('page'), }); return std; }