diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cdf951bee3..742da5525d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,2 @@ /blocksuite/ @toeverything/blocksuite-core +/packages/frontend/core/src/blocksuite @toeverything/blocksuite-core 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 34d7f3fa78..cdabedd6b5 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 @@ -36,12 +36,12 @@ import { AffineSharedPageReference, } from '../../components/affine/reference-link'; import { LitTextRenderer } from '../ai/components/text-renderer'; +import { enableEditorExtension } from '../extensions/entry/enable-editor'; import { patchReferenceRenderer, type ReferenceReactRenderer, } from '../extensions/reference-renderer'; import * as styles from './bi-directional-link-panel.css'; -import { createPageModeSpecs } from './specs/page'; const PREFIX = 'bi-directional-link-panel-collapse:'; @@ -144,7 +144,7 @@ const usePreviewExtensions = () => { }, [workspaceService]); const extensions = useMemo(() => { - const specs = createPageModeSpecs(framework); + const specs = enableEditorExtension(framework, 'page'); specs.extend([patchReferenceRenderer(reactToLit, referenceRenderer)]); return specs.value; }, [reactToLit, referenceRenderer, framework]); diff --git a/packages/frontend/core/src/blocksuite/block-suite-editor/index.ts b/packages/frontend/core/src/blocksuite/block-suite-editor/index.ts index b878a7d61a..f28fbfa26c 100644 --- a/packages/frontend/core/src/blocksuite/block-suite-editor/index.ts +++ b/packages/frontend/core/src/blocksuite/block-suite-editor/index.ts @@ -2,14 +2,12 @@ import { registerAIEffects } from '@affine/core/blocksuite/ai/effects'; import { effects as editorEffects } from '@affine/core/blocksuite/editors'; import { effects as bsEffects } from '@blocksuite/affine/effects'; -import { effects as edgelessEffects } from './specs/edgeless'; -import { effects as patchEffects } from './specs/preview'; +import { registerTemplates } from './register-templates'; bsEffects(); -patchEffects(); editorEffects(); -edgelessEffects(); registerAIEffects(); +registerTemplates(); export * from './blocksuite-editor'; export * from './blocksuite-editor-container'; 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 5778190731..a2eaae18d0 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 @@ -51,7 +51,9 @@ import { patchDocModeService } from '../extensions/doc-mode-service'; import { patchDocUrlExtensions } from '../extensions/doc-url'; import { EdgelessClipboardWatcher } from '../extensions/edgeless-clipboard'; import { patchForClipboardInElectron } from '../extensions/electron-clipboard'; +import { enableEditorExtension } from '../extensions/entry/enable-editor'; import { enableMobileExtension } from '../extensions/entry/enable-mobile'; +import { enablePreviewExtension } from '../extensions/entry/enable-preview'; import { patchForEdgelessNoteConfig } from '../extensions/note-config'; import { patchNotificationService } from '../extensions/notification-service'; import { patchOpenDocExtension } from '../extensions/open-doc'; @@ -64,9 +66,6 @@ import { import { patchSideBarService } from '../extensions/side-bar-service'; import { BiDirectionalLinkPanel } from './bi-directional-link-panel'; import { BlocksuiteEditorJournalDocTitle } from './journal-doc-title'; -import { createEdgelessModeSpecs } from './specs/edgeless'; -import { createPageModeSpecs } from './specs/page'; -import { extendEdgelessPreviewSpec } from './specs/preview'; import { StarterBar } from './starter-bar'; import * as styles from './styles.css'; @@ -125,16 +124,13 @@ const usePatchSpecs = (mode: DocMode) => { }, [workspaceService]); useMemo(() => { - extendEdgelessPreviewSpec(framework); + enablePreviewExtension(framework); }, [framework]); const confirmModal = useConfirmModal(); const patchedSpecs = useMemo(() => { - const builder = - mode === 'edgeless' - ? createEdgelessModeSpecs(framework) - : createPageModeSpecs(framework); + const builder = enableEditorExtension(framework, mode); builder.extend( [ diff --git a/packages/frontend/core/src/blocksuite/block-suite-editor/register-templates.ts b/packages/frontend/core/src/blocksuite/block-suite-editor/register-templates.ts new file mode 100644 index 0000000000..739a30dd09 --- /dev/null +++ b/packages/frontend/core/src/blocksuite/block-suite-editor/register-templates.ts @@ -0,0 +1,15 @@ +import { builtInTemplates as builtInEdgelessTemplates } from '@affine/templates/edgeless'; +import { builtInTemplates as builtInStickersTemplates } from '@affine/templates/stickers'; +import { + EdgelessTemplatePanel, + type TemplateManager, +} from '@blocksuite/affine/blocks'; + +export function registerTemplates() { + EdgelessTemplatePanel.templates.extend( + builtInStickersTemplates as TemplateManager + ); + EdgelessTemplatePanel.templates.extend( + builtInEdgelessTemplates as TemplateManager + ); +} diff --git a/packages/frontend/core/src/blocksuite/block-suite-editor/specs/edgeless.ts b/packages/frontend/core/src/blocksuite/block-suite-editor/specs/edgeless.ts deleted file mode 100644 index a26f14f093..0000000000 --- a/packages/frontend/core/src/blocksuite/block-suite-editor/specs/edgeless.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { enableAIExtension } from '@affine/core/blocksuite/ai'; -import { enableAffineExtension } from '@affine/core/blocksuite/extensions'; -import { builtInTemplates as builtInEdgelessTemplates } from '@affine/templates/edgeless'; -import { builtInTemplates as builtInStickersTemplates } from '@affine/templates/stickers'; -import type { SpecBuilder, TemplateManager } from '@blocksuite/affine/blocks'; -import { EdgelessTemplatePanel, SpecProvider } from '@blocksuite/affine/blocks'; -import { type FrameworkProvider } from '@toeverything/infra'; - -export function createEdgelessModeSpecs( - framework: FrameworkProvider -): SpecBuilder { - const edgelessSpec = SpecProvider._.getSpec('edgeless'); - enableAffineExtension(edgelessSpec, framework); - enableAIExtension(edgelessSpec, framework); - - return edgelessSpec; -} - -export function effects() { - EdgelessTemplatePanel.templates.extend( - builtInStickersTemplates as TemplateManager - ); - EdgelessTemplatePanel.templates.extend( - builtInEdgelessTemplates as TemplateManager - ); -} diff --git a/packages/frontend/core/src/blocksuite/block-suite-editor/specs/page.ts b/packages/frontend/core/src/blocksuite/block-suite-editor/specs/page.ts deleted file mode 100644 index f074e016f0..0000000000 --- a/packages/frontend/core/src/blocksuite/block-suite-editor/specs/page.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { enableAIExtension } from '@affine/core/blocksuite/ai'; -import { enableAffineExtension } from '@affine/core/blocksuite/extensions'; -import { type SpecBuilder, SpecProvider } from '@blocksuite/affine/blocks'; -import { type FrameworkProvider } from '@toeverything/infra'; - -export function createPageModeSpecs(framework: FrameworkProvider): SpecBuilder { - const pageSpec = SpecProvider._.getSpec('page'); - enableAffineExtension(pageSpec, framework); - enableAIExtension(pageSpec, framework); - return pageSpec; -} diff --git a/packages/frontend/core/src/blocksuite/extensions/entry/enable-editor.ts b/packages/frontend/core/src/blocksuite/extensions/entry/enable-editor.ts new file mode 100644 index 0000000000..a59d308c41 --- /dev/null +++ b/packages/frontend/core/src/blocksuite/extensions/entry/enable-editor.ts @@ -0,0 +1,16 @@ +import { enableAIExtension } from '@affine/core/blocksuite/ai'; +import { enableAffineExtension } from '@affine/core/blocksuite/extensions'; +import type { SpecBuilder } from '@blocksuite/affine/blocks'; +import { SpecProvider } from '@blocksuite/affine/blocks'; +import { type FrameworkProvider } from '@toeverything/infra'; + +export function enableEditorExtension( + framework: FrameworkProvider, + mode: 'edgeless' | 'page' +): SpecBuilder { + const spec = SpecProvider._.getSpec(mode); + enableAffineExtension(spec, framework); + enableAIExtension(spec, framework); + + return spec; +} diff --git a/packages/frontend/core/src/blocksuite/block-suite-editor/specs/preview.ts b/packages/frontend/core/src/blocksuite/extensions/entry/enable-preview.ts similarity index 55% rename from packages/frontend/core/src/blocksuite/block-suite-editor/specs/preview.ts rename to packages/frontend/core/src/blocksuite/extensions/entry/enable-preview.ts index 9c728ec6c9..5dc67de57f 100644 --- a/packages/frontend/core/src/blocksuite/block-suite-editor/specs/preview.ts +++ b/packages/frontend/core/src/blocksuite/extensions/entry/enable-preview.ts @@ -1,4 +1,3 @@ -import { AIChatBlockSpec } from '@affine/core/blocksuite/ai/blocks'; import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view'; import { AppThemeService } from '@affine/core/modules/theme'; import { @@ -9,8 +8,8 @@ import { import { ColorScheme, createSignalFromObservable, + FootNoteNodeConfigExtension, type Signal, - type SpecBuilder, SpecProvider, type ThemeExtension, ThemeExtensionIdentifier, @@ -20,30 +19,13 @@ import type { ExtensionType } from '@blocksuite/affine/store'; import type { FrameworkProvider } from '@toeverything/infra'; import type { Observable } from 'rxjs'; -import { buildDocDisplayMetaExtension } from '../../extensions/display-meta'; -import { getFontConfigExtension } from '../../extensions/font-config'; -import { patchPeekViewService } from '../../extensions/peek-view-service'; -import { getThemeExtension } from '../../extensions/theme'; +import { AIChatBlockSpec } from '../../ai/blocks'; +import { buildDocDisplayMetaExtension } from '../display-meta'; +import { getFontConfigExtension } from '../font-config'; +import { patchPeekViewService } from '../peek-view-service'; +import { getThemeExtension } from '../theme'; -const CustomSpecs: ExtensionType[] = [ - AIChatBlockSpec, - getFontConfigExtension(), -].flat(); - -function patchPreviewSpec( - id: 'preview:edgeless' | 'preview:page', - specs: ExtensionType[] -) { - const specProvider = SpecProvider._; - specProvider.extendSpec(id, specs); -} - -export function effects() { - // Patch edgeless preview spec for blocksuite surface-ref and embed-synced-doc - patchPreviewSpec('preview:edgeless', CustomSpecs); -} - -export function getPagePreviewThemeExtension(framework: FrameworkProvider) { +function getPagePreviewThemeExtension(framework: FrameworkProvider) { class AffinePagePreviewThemeExtension extends LifeCycleWatcher implements ThemeExtension @@ -98,33 +80,42 @@ export function getPagePreviewThemeExtension(framework: FrameworkProvider) { return AffinePagePreviewThemeExtension; } -export function createPageModePreviewSpecs( - framework: FrameworkProvider -): SpecBuilder { +// Disable hover effect for footnote node in center peek preview mode +const footnodeConfig = FootNoteNodeConfigExtension({ + disableHoverEffect: true, +}); + +const fontConfig = getFontConfigExtension(); + +let _framework: FrameworkProvider; +let _previewExtensions: ExtensionType[]; +export function enablePreviewExtension(framework: FrameworkProvider): void { + if (_framework === framework && _previewExtensions) { + return; + } + const specProvider = SpecProvider._; - const pagePreviewSpec = specProvider.getSpec('preview:page'); - // Enable theme extension, doc display meta extension and peek view service + + if (_previewExtensions) { + _previewExtensions.forEach(extension => { + specProvider.omitSpec('preview:page', extension); + specProvider.omitSpec('preview:edgeless', extension); + }); + } + + _framework = framework; const peekViewService = framework.get(PeekViewService); - pagePreviewSpec.extend([ + + _previewExtensions = [ + ...AIChatBlockSpec, + footnodeConfig, + fontConfig, + getThemeExtension(framework), getPagePreviewThemeExtension(framework), buildDocDisplayMetaExtension(framework), patchPeekViewService(peekViewService), - ]); - return pagePreviewSpec; -} + ]; -export const extendEdgelessPreviewSpec = (function () { - let _extension: ExtensionType; - let _framework: FrameworkProvider; - return function (framework: FrameworkProvider) { - if (framework === _framework && _extension) { - return _extension; - } else { - _extension && SpecProvider._.omitSpec('preview:edgeless', _extension); - _extension = getThemeExtension(framework); - _framework = framework; - SpecProvider._.extendSpec('preview:edgeless', [_extension]); - return _extension; - } - }; -})(); + specProvider.extendSpec('preview:page', _previewExtensions); + specProvider.extendSpec('preview:edgeless', _previewExtensions); +} 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 38c3878fb4..afbc544445 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 @@ -1,35 +1,24 @@ import { Skeleton } from '@affine/component'; import type { EditorSettingSchema } from '@affine/core/modules/editor-setting'; import { EditorSettingService } from '@affine/core/modules/editor-setting'; -import { AppThemeService } from '@affine/core/modules/theme'; import type { EditorHost } from '@blocksuite/affine/block-std'; import { BlockServiceIdentifier, BlockStdScope, - LifeCycleWatcher, - StdIdentifier, } from '@blocksuite/affine/block-std'; import { GfxControllerIdentifier, type GfxPrimitiveElementModel, } from '@blocksuite/affine/block-std/gfx'; -import type { ThemeExtension } from '@blocksuite/affine/blocks'; import { - ColorScheme, - createSignalFromObservable, EdgelessCRUDIdentifier, SpecProvider, - ThemeExtensionIdentifier, } from '@blocksuite/affine/blocks'; -import type { Container } from '@blocksuite/affine/global/di'; import { Bound } from '@blocksuite/affine/global/utils'; import type { Block, Store } from '@blocksuite/affine/store'; -import type { Signal } from '@preact/signals-core'; -import type { FrameworkProvider } from '@toeverything/infra'; import { useFramework } from '@toeverything/infra'; import { isEqual } from 'lodash-es'; import { useCallback, useEffect, useRef } from 'react'; -import type { Observable } from 'rxjs'; import { map, pairwise } from 'rxjs'; import { @@ -91,10 +80,7 @@ export const EdgelessSnapshot = (props: Props) => { const editorHost = new BlockStdScope({ store: doc, - extensions: [ - ...SpecProvider._.getSpec('preview:edgeless').value, - getThemeExtension(framework), - ], + extensions: SpecProvider._.getSpec('preview:edgeless').value, }).render(); docRef.current = doc; editorHostRef.current?.remove(); @@ -128,7 +114,7 @@ export const EdgelessSnapshot = (props: Props) => { // append to dom node wrapperRef.current.append(editorHost); - }, [docName, firstUpdate, framework, updateElements]); + }, [docName, firstUpdate, updateElements]); useEffect(() => { // eslint-disable-next-line @typescript-eslint/no-floating-promises @@ -172,58 +158,3 @@ export const EdgelessSnapshot = (props: Props) => { ); }; - -function getThemeExtension(framework: FrameworkProvider) { - class AffineThemeExtension - extends LifeCycleWatcher - implements ThemeExtension - { - static override readonly key = 'affine-settings-theme'; - - private readonly theme: Signal; - - protected readonly disposables: (() => void)[] = []; - - static override setup(di: Container) { - super.setup(di); - di.override(ThemeExtensionIdentifier, AffineThemeExtension, [ - 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 AffineThemeExtension; -} diff --git a/packages/frontend/core/src/desktop/pages/workspace/detail-page/tabs/chat.tsx b/packages/frontend/core/src/desktop/pages/workspace/detail-page/tabs/chat.tsx index f407489922..fa1237e323 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/detail-page/tabs/chat.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/detail-page/tabs/chat.tsx @@ -1,6 +1,5 @@ import { ChatPanel } from '@affine/core/blocksuite/ai'; import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor'; -import { createPageModePreviewSpecs } from '@affine/core/blocksuite/block-suite-editor/specs/preview'; import { AINetworkSearchService } from '@affine/core/modules/ai-button/services/network-search'; import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta'; import { DocSearchMenuService } from '@affine/core/modules/doc-search-menu/services'; @@ -8,8 +7,8 @@ import { WorkspaceService } from '@affine/core/modules/workspace'; import { createSignalFromObservable, DocModeProvider, - FootNoteNodeConfigExtension, RefNodeSlotsProvider, + SpecProvider, } from '@blocksuite/affine/blocks'; import { useFramework } from '@toeverything/infra'; import { forwardRef, useEffect, useRef } from 'react'; @@ -21,11 +20,6 @@ export interface SidebarTabProps { onLoad?: ((component: HTMLElement) => void) | null; } -// Disable hover effect for footnote node in chat panel -const FOOTNOTE_CONFIG = FootNoteNodeConfigExtension({ - disableHoverEffect: true, -}); - // A wrapper for CopilotPanel export const EditorChatPanel = forwardRef(function EditorChatPanel( { editor, onLoad }: SidebarTabProps, @@ -90,8 +84,7 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel( ); }, }; - const previewSpecBuilder = createPageModePreviewSpecs(framework); - previewSpecBuilder.extend([FOOTNOTE_CONFIG]); + const previewSpecBuilder = SpecProvider._.getSpec('preview:page'); chatPanelRef.current.previewSpecBuilder = previewSpecBuilder; } else { chatPanelRef.current.host = editor.host; diff --git a/packages/frontend/core/src/modules/dnd/services/index.ts b/packages/frontend/core/src/modules/dnd/services/index.ts index bc01ab9119..5c935def37 100644 --- a/packages/frontend/core/src/modules/dnd/services/index.ts +++ b/packages/frontend/core/src/modules/dnd/services/index.ts @@ -5,10 +5,10 @@ import { type MonitorGetFeedback, type toExternalData, } from '@affine/component'; -import { createPageModeSpecs } from '@affine/core/blocksuite/block-suite-editor/specs/page'; import type { AffineDNDData } from '@affine/core/types/dnd'; import { BlockStdScope } from '@blocksuite/affine/block-std'; import { + DNDAPIExtension, DndApiExtensionIdentifier, type DragBlockPayload, } from '@blocksuite/affine/blocks'; @@ -163,7 +163,7 @@ export class DndService extends Service { const std = new BlockStdScope({ store: doc, - extensions: createPageModeSpecs(this.framework).value, + extensions: [DNDAPIExtension], }); const dndAPI = std.get(DndApiExtensionIdentifier); return dndAPI; diff --git a/packages/frontend/core/src/modules/peek-view/view/ai-chat-block-peek-view/index.tsx b/packages/frontend/core/src/modules/peek-view/view/ai-chat-block-peek-view/index.tsx index 73d2277574..a7e7e7e135 100644 --- a/packages/frontend/core/src/modules/peek-view/view/ai-chat-block-peek-view/index.tsx +++ b/packages/frontend/core/src/modules/peek-view/view/ai-chat-block-peek-view/index.tsx @@ -1,10 +1,9 @@ import { toReactNode } from '@affine/component'; import { AIChatBlockPeekViewTemplate } from '@affine/core/blocksuite/ai'; import type { AIChatBlockModel } from '@affine/core/blocksuite/ai/blocks/ai-chat-block/model/ai-chat-model'; -import { createPageModePreviewSpecs } from '@affine/core/blocksuite/block-suite-editor/specs/preview'; import { AINetworkSearchService } from '@affine/core/modules/ai-button/services/network-search'; import type { EditorHost } from '@blocksuite/affine/block-std'; -import { FootNoteNodeConfigExtension } from '@blocksuite/affine/blocks'; +import { SpecProvider } from '@blocksuite/affine/blocks'; import { useFramework } from '@toeverything/infra'; import { useMemo } from 'react'; @@ -13,11 +12,6 @@ export type AIChatBlockPeekViewProps = { host: EditorHost; }; -// Disable hover effect for footnote node in center peek preview mode -const FOOTNOTE_CONFIG = FootNoteNodeConfigExtension({ - disableHoverEffect: true, -}); - export const AIChatBlockPeekView = ({ model, host, @@ -25,8 +19,7 @@ export const AIChatBlockPeekView = ({ const framework = useFramework(); const searchService = framework.get(AINetworkSearchService); return useMemo(() => { - const previewSpecBuilder = createPageModePreviewSpecs(framework); - previewSpecBuilder.extend([FOOTNOTE_CONFIG]); + const previewSpecBuilder = SpecProvider._.getSpec('preview:page'); const networkSearchConfig = { visible: searchService.visible, enabled: searchService.enabled, @@ -39,5 +32,5 @@ export const AIChatBlockPeekView = ({ networkSearchConfig ); return toReactNode(template); - }, [framework, model, host, searchService]); + }, [model, host, searchService]); };