mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-01 17:39:55 +08:00
refactor(editor): config the extension provider directly (#12252)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added new export paths to improve module accessibility for foundation store and view components. - Introduced new extension points for telemetry, font configuration, link preview cache, and peek view services in the view extension foundation. - **Improvements** - Enhanced flexibility by allowing optional configuration of placeholders in paragraph view extensions. - Switched to runtime schema validation for font configuration, increasing reliability. - Streamlined service registration for peek view providers. - **Refactor** - Centralized and simplified extension management by removing redundant extension files and consolidating setup logic. - Updated internal integration of telemetry, font, and peek view services for improved maintainability. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -274,7 +274,9 @@
|
|||||||
"./model": "./src/model/index.ts",
|
"./model": "./src/model/index.ts",
|
||||||
"./sync": "./src/sync/index.ts",
|
"./sync": "./src/sync/index.ts",
|
||||||
"./extensions/store": "./src/extensions/store.ts",
|
"./extensions/store": "./src/extensions/store.ts",
|
||||||
"./extensions/view": "./src/extensions/view.ts"
|
"./extensions/view": "./src/extensions/view.ts",
|
||||||
|
"./foundation/store": "./src/foundation/store.ts",
|
||||||
|
"./foundation/view": "./src/foundation/view.ts"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from '@blocksuite/affine-foundation/store';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from '@blocksuite/affine-foundation/view';
|
||||||
@@ -28,10 +28,9 @@ import { z } from 'zod';
|
|||||||
import { effects } from './effects';
|
import { effects } from './effects';
|
||||||
|
|
||||||
const optionsSchema = z.object({
|
const optionsSchema = z.object({
|
||||||
getPlaceholder: z
|
getPlaceholder: z.optional(
|
||||||
.function()
|
z.function().args(z.instanceof(ParagraphBlockModel)).returns(z.string())
|
||||||
.args(z.instanceof(ParagraphBlockModel))
|
),
|
||||||
.returns(z.string()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export class ParagraphViewExtension extends ViewExtensionProvider<
|
export class ParagraphViewExtension extends ViewExtensionProvider<
|
||||||
@@ -50,7 +49,7 @@ export class ParagraphViewExtension extends ViewExtensionProvider<
|
|||||||
context: ViewExtensionContext,
|
context: ViewExtensionContext,
|
||||||
options?: z.infer<typeof optionsSchema>
|
options?: z.infer<typeof optionsSchema>
|
||||||
) {
|
) {
|
||||||
super.setup(context);
|
super.setup(context, options);
|
||||||
const getPlaceholder =
|
const getPlaceholder =
|
||||||
options?.getPlaceholder ?? (model => placeholders[model.props.type]);
|
options?.getPlaceholder ?? (model => placeholders[model.props.type]);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const PeekViewProvider = createIdentifier<PeekViewService>(
|
|||||||
export function PeekViewExtension(service: PeekViewService): ExtensionType {
|
export function PeekViewExtension(service: PeekViewService): ExtensionType {
|
||||||
return {
|
return {
|
||||||
setup: di => {
|
setup: di => {
|
||||||
di.addImpl(PeekViewProvider, () => service);
|
di.override(PeekViewProvider, () => service);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
|
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
|
||||||
|
import {
|
||||||
|
PeekViewExtension,
|
||||||
|
type PeekViewService,
|
||||||
|
} from '@blocksuite/affine-components/peek';
|
||||||
import {
|
import {
|
||||||
type ViewExtensionContext,
|
type ViewExtensionContext,
|
||||||
ViewExtensionProvider,
|
ViewExtensionProvider,
|
||||||
@@ -12,28 +16,49 @@ import {
|
|||||||
EditPropsStore,
|
EditPropsStore,
|
||||||
EmbedOptionService,
|
EmbedOptionService,
|
||||||
FileSizeLimitService,
|
FileSizeLimitService,
|
||||||
|
FontConfigExtension,
|
||||||
|
fontConfigSchema,
|
||||||
FontLoaderService,
|
FontLoaderService,
|
||||||
LinkPreviewCache,
|
LinkPreviewCache,
|
||||||
|
LinkPreviewCacheConfigSchema,
|
||||||
|
LinkPreviewCacheExtension,
|
||||||
LinkPreviewService,
|
LinkPreviewService,
|
||||||
PageViewportServiceExtension,
|
PageViewportServiceExtension,
|
||||||
|
TelemetryExtension,
|
||||||
|
type TelemetryService,
|
||||||
ThemeService,
|
ThemeService,
|
||||||
ToolbarRegistryExtension,
|
ToolbarRegistryExtension,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
import { InteractivityManager, ToolController } from '@blocksuite/std/gfx';
|
import { InteractivityManager, ToolController } from '@blocksuite/std/gfx';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { clipboardConfigs } from './clipboard';
|
import { clipboardConfigs } from './clipboard';
|
||||||
import { effects } from './effects';
|
import { effects } from './effects';
|
||||||
|
|
||||||
export class FoundationViewExtension extends ViewExtensionProvider {
|
const optionsSchema = z.object({
|
||||||
|
linkPreviewCacheConfig: z.optional(LinkPreviewCacheConfigSchema),
|
||||||
|
fontConfig: z.optional(z.array(fontConfigSchema)),
|
||||||
|
telemetry: z.optional(z.custom<TelemetryService>()),
|
||||||
|
peekView: z.optional(z.custom<PeekViewService>()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type FoundationViewExtensionOptions = z.infer<typeof optionsSchema>;
|
||||||
|
|
||||||
|
export class FoundationViewExtension extends ViewExtensionProvider<FoundationViewExtensionOptions> {
|
||||||
override name = 'foundation';
|
override name = 'foundation';
|
||||||
|
|
||||||
|
override schema = optionsSchema;
|
||||||
|
|
||||||
override effect() {
|
override effect() {
|
||||||
super.effect();
|
super.effect();
|
||||||
effects();
|
effects();
|
||||||
}
|
}
|
||||||
|
|
||||||
override setup(context: ViewExtensionContext) {
|
override setup(
|
||||||
super.setup(context);
|
context: ViewExtensionContext,
|
||||||
|
options?: FoundationViewExtensionOptions
|
||||||
|
) {
|
||||||
|
super.setup(context, options);
|
||||||
context.register([
|
context.register([
|
||||||
DocDisplayMetaService,
|
DocDisplayMetaService,
|
||||||
EditPropsStore,
|
EditPropsStore,
|
||||||
@@ -56,5 +81,21 @@ export class FoundationViewExtension extends ViewExtensionProvider {
|
|||||||
if (this.isEdgeless(context.scope)) {
|
if (this.isEdgeless(context.scope)) {
|
||||||
context.register([InteractivityManager, ToolController]);
|
context.register([InteractivityManager, ToolController]);
|
||||||
}
|
}
|
||||||
|
const fontConfig = options?.fontConfig;
|
||||||
|
if (fontConfig) {
|
||||||
|
context.register(FontConfigExtension(fontConfig));
|
||||||
|
}
|
||||||
|
const linkPreviewCacheConfig = options?.linkPreviewCacheConfig;
|
||||||
|
if (linkPreviewCacheConfig) {
|
||||||
|
context.register(LinkPreviewCacheExtension(linkPreviewCacheConfig));
|
||||||
|
}
|
||||||
|
const telemetry = options?.telemetry;
|
||||||
|
if (telemetry) {
|
||||||
|
context.register(TelemetryExtension(telemetry));
|
||||||
|
}
|
||||||
|
const peekView = options?.peekView;
|
||||||
|
if (peekView) {
|
||||||
|
context.register(PeekViewExtension(peekView));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { FontFamily, FontStyle, FontWeight } from '@blocksuite/affine-model';
|
import { FontFamily, FontStyle, FontWeight } from '@blocksuite/affine-model';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
export interface FontConfig {
|
export const fontConfigSchema = z.object({
|
||||||
font: string;
|
font: z.string(),
|
||||||
weight: string;
|
weight: z.string(),
|
||||||
url: string;
|
url: z.string(),
|
||||||
style: string;
|
style: z.string(),
|
||||||
}
|
});
|
||||||
|
|
||||||
|
export type FontConfig = z.infer<typeof fontConfigSchema>;
|
||||||
|
|
||||||
export const AffineCanvasTextFonts: FontConfig[] = [
|
export const AffineCanvasTextFonts: FontConfig[] = [
|
||||||
// Inter, https://fonts.cdnfonts.com/css/inter?styles=29139,29134,29135,29136,29140,29141
|
// Inter, https://fonts.cdnfonts.com/css/inter?styles=29139,29134,29135,29136,29140,29141
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createIdentifier } from '@blocksuite/global/di';
|
import { createIdentifier } from '@blocksuite/global/di';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
import type { OutDatabaseAllEvents } from './database.js';
|
import type { OutDatabaseAllEvents } from './database.js';
|
||||||
import type { LinkToolbarEvents } from './link.js';
|
import type { LinkToolbarEvents } from './link.js';
|
||||||
@@ -46,3 +47,13 @@ export interface TelemetryService {
|
|||||||
export const TelemetryProvider = createIdentifier<TelemetryService>(
|
export const TelemetryProvider = createIdentifier<TelemetryService>(
|
||||||
'AffineTelemetryService'
|
'AffineTelemetryService'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const TelemetryExtension = (
|
||||||
|
service: TelemetryService
|
||||||
|
): ExtensionType => {
|
||||||
|
return {
|
||||||
|
setup: di => {
|
||||||
|
di.override(TelemetryProvider, () => service);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import {
|
|
||||||
AffineCanvasTextFonts,
|
|
||||||
FontConfigExtension,
|
|
||||||
} from '@blocksuite/affine/shared/services';
|
|
||||||
|
|
||||||
export function getFontConfigExtension() {
|
|
||||||
return FontConfigExtension(
|
|
||||||
AffineCanvasTextFonts.map(font => ({
|
|
||||||
...font,
|
|
||||||
url: environment.publicPath + 'fonts/' + font.url.split('/').pop(),
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import type { PeekViewService } from '@affine/core/modules/peek-view';
|
|
||||||
import { DebugLogger } from '@affine/debug';
|
|
||||||
import type {
|
|
||||||
PeekOptions,
|
|
||||||
PeekViewService as BSPeekViewService,
|
|
||||||
} from '@blocksuite/affine/components/peek';
|
|
||||||
import { PeekViewExtension } from '@blocksuite/affine/components/peek';
|
|
||||||
import type { TemplateResult } from 'lit';
|
|
||||||
|
|
||||||
const logger = new DebugLogger('affine::patch-peek-view-service');
|
|
||||||
|
|
||||||
export function patchPeekViewService(service: PeekViewService) {
|
|
||||||
return PeekViewExtension({
|
|
||||||
peek: (
|
|
||||||
element: {
|
|
||||||
target: HTMLElement;
|
|
||||||
docId: string;
|
|
||||||
blockIds?: string[];
|
|
||||||
template?: TemplateResult;
|
|
||||||
},
|
|
||||||
options?: PeekOptions
|
|
||||||
) => {
|
|
||||||
logger.debug('center peek', element);
|
|
||||||
const { template, target, ...props } = element;
|
|
||||||
|
|
||||||
return service.peekView.open(
|
|
||||||
{
|
|
||||||
element: target,
|
|
||||||
docRef: props,
|
|
||||||
},
|
|
||||||
template,
|
|
||||||
options?.abortSignal
|
|
||||||
);
|
|
||||||
},
|
|
||||||
} satisfies BSPeekViewService);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { mixpanel } from '@affine/track';
|
|
||||||
import {
|
|
||||||
type TelemetryEventMap,
|
|
||||||
TelemetryProvider,
|
|
||||||
} from '@blocksuite/affine/shared/services';
|
|
||||||
import type { ExtensionType } from '@blocksuite/affine/store';
|
|
||||||
|
|
||||||
export function getTelemetryExtension(): ExtensionType {
|
|
||||||
return {
|
|
||||||
setup: di => {
|
|
||||||
di.addImpl(TelemetryProvider, () => ({
|
|
||||||
track: <T extends keyof TelemetryEventMap>(
|
|
||||||
eventName: T,
|
|
||||||
props: TelemetryEventMap[T]
|
|
||||||
) => {
|
|
||||||
mixpanel.track(eventName as string, props as Record<string, unknown>);
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
import { buildDocDisplayMetaExtension } from '@affine/core/blocksuite/extensions/display-meta';
|
import { buildDocDisplayMetaExtension } from '@affine/core/blocksuite/extensions/display-meta';
|
||||||
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 { patchPeekViewService } from '@affine/core/blocksuite/extensions/peek-view-service';
|
|
||||||
import { getTelemetryExtension } from '@affine/core/blocksuite/extensions/telemetry';
|
|
||||||
import { PeekViewService } from '@affine/core/modules/peek-view';
|
|
||||||
import {
|
import {
|
||||||
type ViewExtensionContext,
|
type ViewExtensionContext,
|
||||||
ViewExtensionProvider,
|
ViewExtensionProvider,
|
||||||
@@ -29,12 +25,7 @@ export class AffineCommonViewExtension extends ViewExtensionProvider<
|
|||||||
super.setup(context, options);
|
super.setup(context, options);
|
||||||
const { framework } = options || {};
|
const { framework } = options || {};
|
||||||
if (framework) {
|
if (framework) {
|
||||||
context.register(patchPeekViewService(framework.get(PeekViewService)));
|
context.register(buildDocDisplayMetaExtension(framework));
|
||||||
context.register([
|
|
||||||
getFontConfigExtension(),
|
|
||||||
buildDocDisplayMetaExtension(framework),
|
|
||||||
]);
|
|
||||||
context.register(getTelemetryExtension());
|
|
||||||
if (context.scope === 'edgeless' || context.scope === 'page') {
|
if (context.scope === 'edgeless' || context.scope === 'page') {
|
||||||
context.register(patchFileSizeLimitExtension(framework));
|
context.register(patchFileSizeLimitExtension(framework));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,22 @@ import {
|
|||||||
AffineEditorViewExtension,
|
AffineEditorViewExtension,
|
||||||
type AffineEditorViewOptions,
|
type AffineEditorViewOptions,
|
||||||
} from '@affine/core/blocksuite/manager/editor-view';
|
} from '@affine/core/blocksuite/manager/editor-view';
|
||||||
|
import { PeekViewService } from '@affine/core/modules/peek-view';
|
||||||
|
import { DebugLogger } from '@affine/debug';
|
||||||
|
import { mixpanel } from '@affine/track';
|
||||||
import { DatabaseViewExtension } from '@blocksuite/affine/blocks/database/view';
|
import { DatabaseViewExtension } from '@blocksuite/affine/blocks/database/view';
|
||||||
import { ParagraphViewExtension } from '@blocksuite/affine/blocks/paragraph/view';
|
import { ParagraphViewExtension } from '@blocksuite/affine/blocks/paragraph/view';
|
||||||
|
import type {
|
||||||
|
PeekOptions,
|
||||||
|
PeekViewService as BSPeekViewService,
|
||||||
|
} from '@blocksuite/affine/components/peek';
|
||||||
import { ViewExtensionManager } from '@blocksuite/affine/ext-loader';
|
import { ViewExtensionManager } from '@blocksuite/affine/ext-loader';
|
||||||
import { getInternalViewExtensions } from '@blocksuite/affine/extensions/view';
|
import { getInternalViewExtensions } from '@blocksuite/affine/extensions/view';
|
||||||
|
import { FoundationViewExtension } from '@blocksuite/affine/foundation/view';
|
||||||
|
import { AffineCanvasTextFonts } from '@blocksuite/affine/shared/services';
|
||||||
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 type { TemplateResult } from 'lit';
|
||||||
|
|
||||||
import { CodeBlockPreviewViewExtension } from './code-block-preview';
|
import { CodeBlockPreviewViewExtension } from './code-block-preview';
|
||||||
|
|
||||||
@@ -50,6 +60,8 @@ type Configure = {
|
|||||||
value: ViewExtensionManager;
|
value: ViewExtensionManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const peekViewLogger = new DebugLogger('affine::patch-peek-view-service');
|
||||||
|
|
||||||
class ViewProvider {
|
class ViewProvider {
|
||||||
static instance: ViewProvider | null = null;
|
static instance: ViewProvider | null = null;
|
||||||
static getInstance() {
|
static getInstance() {
|
||||||
@@ -129,6 +141,45 @@ class ViewProvider {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly _configureCommon = (framework?: FrameworkProvider) => {
|
private readonly _configureCommon = (framework?: FrameworkProvider) => {
|
||||||
|
const peekViewService = framework?.get(PeekViewService);
|
||||||
|
|
||||||
|
this._manager.configure(FoundationViewExtension, {
|
||||||
|
telemetry: {
|
||||||
|
track: (eventName, props) => {
|
||||||
|
mixpanel.track(eventName, props);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fontConfig: AffineCanvasTextFonts.map(font => ({
|
||||||
|
...font,
|
||||||
|
url: environment.publicPath + 'fonts/' + font.url.split('/').pop(),
|
||||||
|
})),
|
||||||
|
peekView: !peekViewService
|
||||||
|
? undefined
|
||||||
|
: ({
|
||||||
|
peek: (
|
||||||
|
element: {
|
||||||
|
target: HTMLElement;
|
||||||
|
docId: string;
|
||||||
|
blockIds?: string[];
|
||||||
|
template?: TemplateResult;
|
||||||
|
},
|
||||||
|
options?: PeekOptions
|
||||||
|
) => {
|
||||||
|
peekViewLogger.debug('center peek', element);
|
||||||
|
const { template, target, ...props } = element;
|
||||||
|
|
||||||
|
return peekViewService.peekView.open(
|
||||||
|
{
|
||||||
|
element: target,
|
||||||
|
docRef: props,
|
||||||
|
},
|
||||||
|
template,
|
||||||
|
options?.abortSignal
|
||||||
|
);
|
||||||
|
},
|
||||||
|
} satisfies BSPeekViewService),
|
||||||
|
});
|
||||||
|
|
||||||
this._manager.configure(AffineCommonViewExtension, {
|
this._manager.configure(AffineCommonViewExtension, {
|
||||||
framework,
|
framework,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user