feat(editor): implement view extension manager with builder pattern (#12193)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Refactor** - Streamlined and modularized the configuration of editor and theme extensions, introducing a chainable API for extension management. - Migrated extension setup to use new provider classes and centralized patch logic, improving maintainability and consistency. - Updated internal extension retrieval processes to use a more explicit, stepwise initialization sequence. - Removed legacy theme and editor config registration from common views and editor setups. - Removed direct patch registrations from editor views, consolidating them into extension providers. - Renamed classes and variables for clarity and consistency across the codebase. - **New Features** - Added new extension providers for editor configuration, theme management, and edgeless block header customization, enabling more flexible and validated extension registration. - Introduced animated viewport focus and dynamic header rendering for edgeless notes and embedded synced documents. - Integrated reactive editor settings and toolbar configurations with workspace-aware base URL resolution. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -43,17 +43,20 @@ import type {
|
|||||||
AffineAIPanelWidgetConfig,
|
AffineAIPanelWidgetConfig,
|
||||||
} from '../widgets/ai-panel/type';
|
} from '../widgets/ai-panel/type';
|
||||||
|
|
||||||
export const getCustomPageEditorBlockSpecs: () => ExtensionType[] = () => [
|
export const getCustomPageEditorBlockSpecs: () => ExtensionType[] = () => {
|
||||||
...getViewManager().get('page'),
|
const manager = getViewManager().config.init().value;
|
||||||
{
|
return [
|
||||||
setup: di => {
|
...manager.get('page'),
|
||||||
di.override(
|
{
|
||||||
BlockViewIdentifier('affine:page'),
|
setup: di => {
|
||||||
() => literal`affine-page-root`
|
di.override(
|
||||||
);
|
BlockViewIdentifier('affine:page'),
|
||||||
|
() => literal`affine-page-root`
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
];
|
||||||
];
|
};
|
||||||
|
|
||||||
const customHeadingStyles = css`
|
const customHeadingStyles = css`
|
||||||
.custom-heading {
|
.custom-heading {
|
||||||
|
|||||||
@@ -163,7 +163,13 @@ const usePreviewExtensions = () => {
|
|||||||
const enableAI = useEnableAI();
|
const enableAI = useEnableAI();
|
||||||
|
|
||||||
const extensions = useMemo(() => {
|
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');
|
const specs = manager.get('preview-page');
|
||||||
return [...specs, patchReferenceRenderer(reactToLit, referenceRenderer)];
|
return [...specs, patchReferenceRenderer(reactToLit, referenceRenderer)];
|
||||||
}, [reactToLit, referenceRenderer, framework, enableAI]);
|
}, [reactToLit, referenceRenderer, framework, enableAI]);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
LitEdgelessEditor,
|
LitEdgelessEditor,
|
||||||
type PageEditor,
|
type PageEditor,
|
||||||
} from '@affine/core/blocksuite/editors';
|
} 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 { getViewManager } from '@affine/core/blocksuite/manager/migrating-view';
|
||||||
import { useEnableAI } from '@affine/core/components/hooks/affine/use-enable-ai';
|
import { useEnableAI } from '@affine/core/components/hooks/affine/use-enable-ai';
|
||||||
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
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 track from '@affine/track';
|
||||||
import type { DocTitle } from '@blocksuite/affine/fragments/doc-title';
|
import type { DocTitle } from '@blocksuite/affine/fragments/doc-title';
|
||||||
import type { DocMode } from '@blocksuite/affine/model';
|
import type { DocMode } from '@blocksuite/affine/model';
|
||||||
import type { ExtensionType, Store } from '@blocksuite/affine/store';
|
import type { Store } from '@blocksuite/affine/store';
|
||||||
import {
|
import {
|
||||||
type FrameworkProvider,
|
|
||||||
useFramework,
|
useFramework,
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useService,
|
useService,
|
||||||
@@ -69,7 +67,7 @@ const usePatchSpecs = (mode: DocMode) => {
|
|||||||
|
|
||||||
const enableAI = useEnableAI();
|
const enableAI = useEnableAI();
|
||||||
|
|
||||||
const insidePeekView = useInsidePeekView();
|
const isInPeekView = useInsidePeekView();
|
||||||
|
|
||||||
const enableTurboRenderer = useLiveData(
|
const enableTurboRenderer = useLiveData(
|
||||||
featureFlagService.flags.enable_turbo_renderer.$
|
featureFlagService.flags.enable_turbo_renderer.$
|
||||||
@@ -81,33 +79,51 @@ const usePatchSpecs = (mode: DocMode) => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const editorOptions: AffineEditorViewOptions = useMemo(() => {
|
const patchedSpecs = useMemo(() => {
|
||||||
return {
|
const manager = getViewManager()
|
||||||
isCloud,
|
.config.init()
|
||||||
isInPeekView: insidePeekView,
|
.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,
|
if (BUILD_CONFIG.isMobileEdition) {
|
||||||
enablePDFEmbedPreview,
|
if (mode === 'page') {
|
||||||
|
return manager.get('mobile-page');
|
||||||
framework,
|
} else {
|
||||||
|
return manager.get('mobile-edgeless');
|
||||||
reactToLit: reactToLit as AffineEditorViewOptions['reactToLit'],
|
}
|
||||||
confirmModal,
|
} else {
|
||||||
};
|
return manager.get(mode);
|
||||||
|
}
|
||||||
}, [
|
}, [
|
||||||
confirmModal,
|
confirmModal,
|
||||||
|
enableAI,
|
||||||
enablePDFEmbedPreview,
|
enablePDFEmbedPreview,
|
||||||
enableTurboRenderer,
|
enableTurboRenderer,
|
||||||
framework,
|
framework,
|
||||||
insidePeekView,
|
isInPeekView,
|
||||||
isCloud,
|
isCloud,
|
||||||
|
mode,
|
||||||
reactToLit,
|
reactToLit,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const patchedSpecs = useMemo(() => {
|
|
||||||
return enableEditorExtension(framework, mode, enableAI, editorOptions);
|
|
||||||
}, [framework, mode, enableAI, editorOptions]);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
patchedSpecs,
|
patchedSpecs,
|
||||||
useMemo(
|
useMemo(
|
||||||
@@ -302,20 +318,3 @@ export const BlocksuiteEdgelessEditor = forwardRef<
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,110 +1,42 @@
|
|||||||
import type { ReactToLit } from '@affine/component';
|
import type { ElementOrFactory } 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 {
|
import {
|
||||||
DocModeProvider,
|
patchForEdgelessNoteConfig,
|
||||||
EditPropsStore,
|
patchForEmbedSyncedDocConfig,
|
||||||
} from '@blocksuite/affine/shared/services';
|
} from '@affine/core/blocksuite/extensions/edgeless-block-header/patch';
|
||||||
import { GfxControllerIdentifier } from '@blocksuite/affine/std/gfx';
|
import {
|
||||||
import type { FrameworkProvider } from '@toeverything/infra';
|
type ViewExtensionContext,
|
||||||
import { html } from 'lit';
|
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';
|
const optionsSchema = z.object({
|
||||||
import { EdgelessEmbedSyncedDocHeader } from './edgeless-embed-synced-doc-header';
|
isInPeekView: z.boolean(),
|
||||||
import { EdgelessNoteHeader } from './edgeless-note-header';
|
framework: z.instanceof(FrameworkProvider),
|
||||||
|
reactToLit: z
|
||||||
|
.function()
|
||||||
|
.args(z.custom<ElementOrFactory>(), z.boolean().optional())
|
||||||
|
.returns(z.custom<TemplateResult>()),
|
||||||
|
});
|
||||||
|
|
||||||
export function patchForEdgelessNoteConfig(
|
export type EdgelessBlockHeaderViewOptions = z.infer<typeof optionsSchema>;
|
||||||
framework: FrameworkProvider,
|
|
||||||
reactToLit: ReactToLit,
|
|
||||||
insidePeekView: boolean
|
|
||||||
) {
|
|
||||||
return NoteConfigExtension({
|
|
||||||
edgelessNoteHeader: ({ note }) =>
|
|
||||||
reactToLit(<EdgelessNoteHeader note={note} />),
|
|
||||||
pageBlockTitle: ({ note }) => {
|
|
||||||
const journalService = framework.get(JournalService);
|
|
||||||
const isJournal = !!journalService.journalDate$(note.store.id).value;
|
|
||||||
if (isJournal) {
|
|
||||||
return reactToLit(
|
|
||||||
<BlocksuiteEditorJournalDocTitle page={note.store} />
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return html`<doc-title .doc=${note.store}></doc-title>`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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;
|
export class EdgelessBlockHeaderConfigViewExtension extends ViewExtensionProvider<EdgelessBlockHeaderViewOptions> {
|
||||||
const rootView = std.view.getBlock(std.store.root.id);
|
override name = 'header-config-view';
|
||||||
if (!rootView) return false;
|
override schema = optionsSchema;
|
||||||
|
|
||||||
const gfx = std.get(GfxControllerIdentifier);
|
override setup(
|
||||||
const primaryMode = std
|
context: ViewExtensionContext,
|
||||||
.get(DocModeProvider)
|
options?: EdgelessBlockHeaderViewOptions
|
||||||
.getPrimaryMode(std.store.id);
|
) {
|
||||||
|
super.setup(context, options);
|
||||||
|
if (!options) return;
|
||||||
|
const { framework, isInPeekView, reactToLit } = options;
|
||||||
|
|
||||||
if (primaryMode !== 'page' || !note || note.props.edgeless.collapse) {
|
context.register(
|
||||||
return false;
|
patchForEdgelessNoteConfig(framework, reactToLit, isInPeekView)
|
||||||
}
|
);
|
||||||
|
context.register(patchForEmbedSyncedDocConfig(reactToLit));
|
||||||
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(<EdgelessEmbedSyncedDocHeader model={model} std={std} />),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(<EdgelessNoteHeader note={note} />),
|
||||||
|
pageBlockTitle: ({ note }) => {
|
||||||
|
const journalService = framework.get(JournalService);
|
||||||
|
const isJournal = !!journalService.journalDate$(note.store.id).value;
|
||||||
|
if (isJournal) {
|
||||||
|
return reactToLit(
|
||||||
|
<BlocksuiteEditorJournalDocTitle page={note.store} />
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return html`<doc-title .doc=${note.store}></doc-title>`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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(<EdgelessEmbedSyncedDocHeader model={model} std={std} />),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -1,30 +1,34 @@
|
|||||||
import { WorkspaceServerService } from '@affine/core/modules/cloud';
|
import { getEditorConfigExtension } from '@affine/core/blocksuite/extensions/editor-config/get-config';
|
||||||
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 {
|
import {
|
||||||
createCustomToolbarExtension,
|
type ViewExtensionContext,
|
||||||
createToolbarMoreMenuConfig,
|
ViewExtensionProvider,
|
||||||
} from './toolbar';
|
} from '@blocksuite/affine/ext-loader';
|
||||||
|
import { FrameworkProvider } from '@toeverything/infra';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
export function getEditorConfigExtension(
|
const optionsSchema = z.object({
|
||||||
framework: FrameworkProvider
|
framework: z.instanceof(FrameworkProvider).optional(),
|
||||||
): ExtensionType[] {
|
});
|
||||||
const editorSettingService = framework.get(EditorSettingService);
|
|
||||||
const workspaceServerService = framework.get(WorkspaceServerService);
|
|
||||||
const baseUrl = workspaceServerService.server?.baseUrl ?? location.origin;
|
|
||||||
|
|
||||||
return [
|
type AffineEditorConfigViewOptions = z.infer<typeof optionsSchema>;
|
||||||
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),
|
export class AffineEditorConfigViewExtension extends ViewExtensionProvider<AffineEditorConfigViewOptions> {
|
||||||
].flat();
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<typeof optionsSchema>;
|
||||||
|
|
||||||
|
export class AffineThemeViewExtension extends ViewExtensionProvider<AffineThemeViewOptions> {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<ColorScheme>;
|
||||||
|
|
||||||
|
readonly disposables: (() => void)[] = [];
|
||||||
|
|
||||||
|
static override setup(di: Container) {
|
||||||
|
super.setup(di);
|
||||||
|
di.override(ThemeExtensionIdentifier, AffinePagePreviewThemeExtension, [
|
||||||
|
StdIdentifier,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(std: BlockStdScope) {
|
||||||
|
super(std);
|
||||||
|
const theme$: Observable<ColorScheme> = framework
|
||||||
|
.get(AppThemeService)
|
||||||
|
.appTheme.theme$.map(theme => {
|
||||||
|
return theme === ColorScheme.Dark
|
||||||
|
? ColorScheme.Dark
|
||||||
|
: ColorScheme.Light;
|
||||||
|
});
|
||||||
|
const { signal, cleanup } = createSignalFromObservable<ColorScheme>(
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -10,11 +10,7 @@ import {
|
|||||||
createSignalFromObservable,
|
createSignalFromObservable,
|
||||||
type Signal,
|
type Signal,
|
||||||
} from '@blocksuite/affine/shared/utils';
|
} from '@blocksuite/affine/shared/utils';
|
||||||
import {
|
import { LifeCycleWatcher, StdIdentifier } from '@blocksuite/affine/std';
|
||||||
type BlockStdScope,
|
|
||||||
LifeCycleWatcher,
|
|
||||||
StdIdentifier,
|
|
||||||
} from '@blocksuite/affine/std';
|
|
||||||
import { type FrameworkProvider } from '@toeverything/infra';
|
import { type FrameworkProvider } from '@toeverything/infra';
|
||||||
import type { Observable } from 'rxjs';
|
import type { Observable } from 'rxjs';
|
||||||
import { combineLatest, map } from 'rxjs';
|
import { combineLatest, map } from 'rxjs';
|
||||||
@@ -99,58 +95,3 @@ export function getThemeExtension(
|
|||||||
|
|
||||||
return AffineThemeExtension;
|
return AffineThemeExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPreviewThemeExtension(framework: FrameworkProvider) {
|
|
||||||
class AffinePagePreviewThemeExtension
|
|
||||||
extends LifeCycleWatcher
|
|
||||||
implements ThemeExtension
|
|
||||||
{
|
|
||||||
static override readonly key = 'affine-page-preview-theme';
|
|
||||||
|
|
||||||
readonly theme: Signal<ColorScheme>;
|
|
||||||
|
|
||||||
readonly disposables: (() => void)[] = [];
|
|
||||||
|
|
||||||
static override setup(di: Container) {
|
|
||||||
super.setup(di);
|
|
||||||
di.override(ThemeExtensionIdentifier, AffinePagePreviewThemeExtension, [
|
|
||||||
StdIdentifier,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(std: BlockStdScope) {
|
|
||||||
super(std);
|
|
||||||
const theme$: Observable<ColorScheme> = framework
|
|
||||||
.get(AppThemeService)
|
|
||||||
.appTheme.theme$.map(theme => {
|
|
||||||
return theme === ColorScheme.Dark
|
|
||||||
? ColorScheme.Dark
|
|
||||||
: ColorScheme.Light;
|
|
||||||
});
|
|
||||||
const { signal, cleanup } = createSignalFromObservable<ColorScheme>(
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
effects as htmlPreviewEffects,
|
effects as htmlPreviewEffects,
|
||||||
} from '../extensions/code-block-preview/html-preview';
|
} from '../extensions/code-block-preview/html-preview';
|
||||||
|
|
||||||
export class CodeBlockPreviewExtensionProvider extends ViewExtensionProvider {
|
export class CodeBlockPreviewViewExtension extends ViewExtensionProvider {
|
||||||
override name = 'code-block-preview';
|
override name = 'code-block-preview';
|
||||||
|
|
||||||
override effect() {
|
override effect() {
|
||||||
|
|||||||
@@ -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 { aiPanelWidget } from '@affine/core/blocksuite/ai/widgets/ai-panel/ai-panel';
|
||||||
import { edgelessCopilotWidget } from '@affine/core/blocksuite/ai/widgets/edgeless-copilot';
|
import { edgelessCopilotWidget } from '@affine/core/blocksuite/ai/widgets/edgeless-copilot';
|
||||||
import { buildDocDisplayMetaExtension } from '@affine/core/blocksuite/extensions/display-meta';
|
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 { patchFileSizeLimitExtension } from '@affine/core/blocksuite/extensions/file-size-limit';
|
||||||
import { getFontConfigExtension } from '@affine/core/blocksuite/extensions/font-config';
|
import { getFontConfigExtension } from '@affine/core/blocksuite/extensions/font-config';
|
||||||
import { patchPeekViewService } from '@affine/core/blocksuite/extensions/peek-view-service';
|
import { patchPeekViewService } from '@affine/core/blocksuite/extensions/peek-view-service';
|
||||||
import { getTelemetryExtension } from '@affine/core/blocksuite/extensions/telemetry';
|
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 { PeekViewService } from '@affine/core/modules/peek-view';
|
||||||
import {
|
import {
|
||||||
type ViewExtensionContext,
|
type ViewExtensionContext,
|
||||||
@@ -42,17 +37,6 @@ export class AffineCommonViewExtension extends ViewExtensionProvider<
|
|||||||
|
|
||||||
override schema = optionsSchema;
|
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(
|
private _setupAI(
|
||||||
context: ViewExtensionContext,
|
context: ViewExtensionContext,
|
||||||
framework: FrameworkProvider
|
framework: FrameworkProvider
|
||||||
@@ -97,7 +81,7 @@ export class AffineCommonViewExtension extends ViewExtensionProvider<
|
|||||||
context: ViewExtensionContext,
|
context: ViewExtensionContext,
|
||||||
options?: z.infer<typeof optionsSchema>
|
options?: z.infer<typeof optionsSchema>
|
||||||
) {
|
) {
|
||||||
super.setup(context);
|
super.setup(context, options);
|
||||||
const { framework, enableAI } = options || {};
|
const { framework, enableAI } = options || {};
|
||||||
if (framework) {
|
if (framework) {
|
||||||
context.register(patchPeekViewService(framework.get(PeekViewService)));
|
context.register(patchPeekViewService(framework.get(PeekViewService)));
|
||||||
@@ -106,9 +90,7 @@ export class AffineCommonViewExtension extends ViewExtensionProvider<
|
|||||||
buildDocDisplayMetaExtension(framework),
|
buildDocDisplayMetaExtension(framework),
|
||||||
]);
|
]);
|
||||||
context.register(getTelemetryExtension());
|
context.register(getTelemetryExtension());
|
||||||
this._setupTheme(context, framework);
|
|
||||||
if (context.scope === 'edgeless' || context.scope === 'page') {
|
if (context.scope === 'edgeless' || context.scope === 'page') {
|
||||||
context.register(getEditorConfigExtension(framework));
|
|
||||||
context.register(patchFileSizeLimitExtension(framework));
|
context.register(patchFileSizeLimitExtension(framework));
|
||||||
}
|
}
|
||||||
if (enableAI) {
|
if (enableAI) {
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ import {
|
|||||||
import { patchDatabaseBlockConfigService } from '@affine/core/blocksuite/extensions/database-block-config-service';
|
import { patchDatabaseBlockConfigService } from '@affine/core/blocksuite/extensions/database-block-config-service';
|
||||||
import { patchDocModeService } from '@affine/core/blocksuite/extensions/doc-mode-service';
|
import { patchDocModeService } from '@affine/core/blocksuite/extensions/doc-mode-service';
|
||||||
import { patchDocUrlExtensions } from '@affine/core/blocksuite/extensions/doc-url';
|
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 { EdgelessClipboardAIChatConfig } from '@affine/core/blocksuite/extensions/edgeless-clipboard';
|
||||||
import { patchForClipboardInElectron } from '@affine/core/blocksuite/extensions/electron-clipboard';
|
import { patchForClipboardInElectron } from '@affine/core/blocksuite/extensions/electron-clipboard';
|
||||||
import { patchNotificationService } from '@affine/core/blocksuite/extensions/notification-service';
|
import { patchNotificationService } from '@affine/core/blocksuite/extensions/notification-service';
|
||||||
@@ -116,13 +112,12 @@ export class AffineEditorViewExtension extends ViewExtensionProvider<AffineEdito
|
|||||||
context: ViewExtensionContext,
|
context: ViewExtensionContext,
|
||||||
options?: AffineEditorViewOptions
|
options?: AffineEditorViewOptions
|
||||||
) {
|
) {
|
||||||
super.setup(context);
|
super.setup(context, options);
|
||||||
if (!options) {
|
if (!options) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
isCloud,
|
isCloud,
|
||||||
isInPeekView,
|
|
||||||
|
|
||||||
enableTurboRenderer,
|
enableTurboRenderer,
|
||||||
enablePDFEmbedPreview,
|
enablePDFEmbedPreview,
|
||||||
@@ -155,8 +150,6 @@ export class AffineEditorViewExtension extends ViewExtensionProvider<AffineEdito
|
|||||||
context.register(patchDocUrlExtensions(framework));
|
context.register(patchDocUrlExtensions(framework));
|
||||||
context.register(patchQuickSearchService(framework));
|
context.register(patchQuickSearchService(framework));
|
||||||
context.register([
|
context.register([
|
||||||
patchForEmbedSyncedDocConfig(reactToLit),
|
|
||||||
patchForEdgelessNoteConfig(framework, reactToLit, isInPeekView),
|
|
||||||
patchDatabaseBlockConfigService(),
|
patchDatabaseBlockConfigService(),
|
||||||
patchForAudioEmbedView(reactToLit),
|
patchForAudioEmbedView(reactToLit),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
|
import {
|
||||||
|
EdgelessBlockHeaderConfigViewExtension,
|
||||||
|
type EdgelessBlockHeaderViewOptions,
|
||||||
|
} from '@affine/core/blocksuite/extensions/edgeless-block-header';
|
||||||
|
import { AffineEditorConfigViewExtension } from '@affine/core/blocksuite/extensions/editor-config';
|
||||||
import { createDatabaseOptionsConfig } from '@affine/core/blocksuite/extensions/editor-config/database';
|
import { createDatabaseOptionsConfig } from '@affine/core/blocksuite/extensions/editor-config/database';
|
||||||
import { createLinkedWidgetConfig } from '@affine/core/blocksuite/extensions/editor-config/linked';
|
import { createLinkedWidgetConfig } from '@affine/core/blocksuite/extensions/editor-config/linked';
|
||||||
|
import { AffineThemeViewExtension } from '@affine/core/blocksuite/extensions/theme';
|
||||||
import { AffineCommonViewExtension } from '@affine/core/blocksuite/manager/common-view';
|
import { AffineCommonViewExtension } from '@affine/core/blocksuite/manager/common-view';
|
||||||
import {
|
import {
|
||||||
AffineEditorViewExtension,
|
AffineEditorViewExtension,
|
||||||
@@ -12,54 +18,142 @@ import { getInternalViewExtensions } from '@blocksuite/affine/extensions/view';
|
|||||||
import { LinkedDocViewExtension } from '@blocksuite/affine/widgets/linked-doc/view';
|
import { LinkedDocViewExtension } from '@blocksuite/affine/widgets/linked-doc/view';
|
||||||
import type { FrameworkProvider } from '@toeverything/infra';
|
import type { FrameworkProvider } from '@toeverything/infra';
|
||||||
|
|
||||||
import { CodeBlockPreviewExtensionProvider } from './code-block-preview';
|
import { CodeBlockPreviewViewExtension } from './code-block-preview';
|
||||||
|
|
||||||
const manager = new ViewExtensionManager([
|
class ViewProvider {
|
||||||
...getInternalViewExtensions(),
|
static instance: ViewProvider | null = null;
|
||||||
|
static getInstance() {
|
||||||
AffineCommonViewExtension,
|
if (!ViewProvider.instance) {
|
||||||
AffineEditorViewExtension,
|
ViewProvider.instance = new ViewProvider();
|
||||||
CodeBlockPreviewExtensionProvider,
|
}
|
||||||
]);
|
return ViewProvider.instance;
|
||||||
|
|
||||||
export function getViewManager(
|
|
||||||
framework?: FrameworkProvider,
|
|
||||||
enableAI?: boolean,
|
|
||||||
options?: AffineEditorViewOptions
|
|
||||||
) {
|
|
||||||
manager.configure(AffineCommonViewExtension, {
|
|
||||||
framework,
|
|
||||||
enableAI,
|
|
||||||
});
|
|
||||||
manager.configure(AffineEditorViewExtension, options);
|
|
||||||
|
|
||||||
if (framework) {
|
|
||||||
manager.configure(
|
|
||||||
DatabaseViewExtension,
|
|
||||||
createDatabaseOptionsConfig(framework)
|
|
||||||
);
|
|
||||||
manager.configure(
|
|
||||||
LinkedDocViewExtension,
|
|
||||||
createLinkedWidgetConfig(framework)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableAI) {
|
private readonly _manager: ViewExtensionManager;
|
||||||
manager.configure(ParagraphViewExtension, {
|
|
||||||
getPlaceholder: model => {
|
constructor() {
|
||||||
const placeholders = {
|
this._manager = new ViewExtensionManager([
|
||||||
text: "Type '/' for commands, 'space' for AI",
|
...getInternalViewExtensions(),
|
||||||
h1: 'Heading 1',
|
|
||||||
h2: 'Heading 2',
|
AffineThemeViewExtension,
|
||||||
h3: 'Heading 3',
|
AffineCommonViewExtension,
|
||||||
h4: 'Heading 4',
|
AffineEditorViewExtension,
|
||||||
h5: 'Heading 5',
|
AffineEditorConfigViewExtension,
|
||||||
h6: 'Heading 6',
|
CodeBlockPreviewViewExtension,
|
||||||
quote: '',
|
EdgelessBlockHeaderConfigViewExtension,
|
||||||
};
|
]);
|
||||||
return placeholders[model.props.type] ?? '';
|
}
|
||||||
},
|
|
||||||
|
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 this.config;
|
||||||
return manager;
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,12 @@ export const EdgelessSnapshot = (props: Props) => {
|
|||||||
const { editorSetting } = framework.get(EditorSettingService);
|
const { editorSetting } = framework.get(EditorSettingService);
|
||||||
|
|
||||||
const extensions = useMemo(() => {
|
const extensions = useMemo(() => {
|
||||||
const manager = getViewManager(framework, false);
|
const manager = getViewManager()
|
||||||
|
.config.init()
|
||||||
|
.common(framework)
|
||||||
|
.theme(framework)
|
||||||
|
.database(framework)
|
||||||
|
.linkedDoc(framework).value;
|
||||||
return manager
|
return manager
|
||||||
.get('preview-edgeless')
|
.get('preview-edgeless')
|
||||||
.concat([ViewportElementExtension('.ref-viewport')]);
|
.concat([ViewportElementExtension('.ref-viewport')]);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export function createBlockStdScope(doc: Store) {
|
|||||||
logger.debug('createBlockStdScope', doc.id);
|
logger.debug('createBlockStdScope', doc.id);
|
||||||
const std = new BlockStdScope({
|
const std = new BlockStdScope({
|
||||||
store: doc,
|
store: doc,
|
||||||
extensions: getViewManager().get('page'),
|
extensions: getViewManager().config.init().value.get('page'),
|
||||||
});
|
});
|
||||||
return std;
|
return std;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user