mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 09:21:24 +08:00
fix(editor): worker loading in webpack env (#10832)
### TL;DR Created dedicated worker entry points to avoid dynamic imports. ### What changed? - Painters are provided during worker initialization - Removed `ParagraphPaintConfigExtension` and the associated configuration system - Created dedicated worker entry points in both the integration test and frontend packages - Modified `ViewportLayoutPainter` to accept painters in its constructor - Updated the `TurboRendererConfig` interface to require a `painterWorkerEntry` function ### Why make this change? Webpack support. Extension objects in main thread are not available to be passed into workers. Dynamic painter path import is hard to support in webpack environment. With the [webpack-ignore](https://webpack.js.org/api/module-methods/#webpackignore) rule, there are still build errors in webpack.
This commit is contained in:
@@ -4,4 +4,4 @@ export * from './paragraph-block.js';
|
|||||||
export * from './paragraph-service.js';
|
export * from './paragraph-service.js';
|
||||||
export * from './paragraph-spec.js';
|
export * from './paragraph-spec.js';
|
||||||
export * from './turbo/paragraph-layout-provider.js';
|
export * from './turbo/paragraph-layout-provider.js';
|
||||||
export * from './turbo/paragraph-painter-config.js';
|
export * from './turbo/paragraph-painter.worker.js';
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import { BlockPainterConfigIdentifier } from '@blocksuite/affine-gfx-turbo-renderer';
|
|
||||||
import type { Container } from '@blocksuite/global/di';
|
|
||||||
import { Extension } from '@blocksuite/store';
|
|
||||||
|
|
||||||
export class ParagraphPaintConfigExtension extends Extension {
|
|
||||||
static override setup(di: Container) {
|
|
||||||
const config = {
|
|
||||||
type: 'affine:paragraph',
|
|
||||||
path: new URL(
|
|
||||||
'@blocksuite/affine-block-paragraph/turbo-painter',
|
|
||||||
import.meta.url
|
|
||||||
).href,
|
|
||||||
};
|
|
||||||
di.addImpl(BlockPainterConfigIdentifier, config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -39,16 +39,23 @@ function isParagraphLayout(layout: BlockLayout): layout is ParagraphLayout {
|
|||||||
return layout.type === 'affine:paragraph';
|
return layout.type === 'affine:paragraph';
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ParagraphLayoutPainter implements BlockLayoutPainter {
|
export class ParagraphLayoutPainter implements BlockLayoutPainter {
|
||||||
static readonly font = new FontFace(
|
private static readonly supportFontFace =
|
||||||
'Inter',
|
typeof FontFace !== 'undefined' &&
|
||||||
`url(https://fonts.gstatic.com/s/inter/v18/UcCo3FwrK3iLTcviYwYZ8UA3.woff2)`
|
typeof self !== 'undefined' &&
|
||||||
);
|
'fonts' in self;
|
||||||
|
|
||||||
static fontLoaded = false;
|
static readonly font = ParagraphLayoutPainter.supportFontFace
|
||||||
|
? new FontFace(
|
||||||
|
'Inter',
|
||||||
|
`url(https://fonts.gstatic.com/s/inter/v18/UcCo3FwrK3iLTcviYwYZ8UA3.woff2)`
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
static fontLoaded = !ParagraphLayoutPainter.supportFontFace;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
if (typeof self !== 'undefined' && 'fonts' in self) {
|
if (ParagraphLayoutPainter.supportFontFace && ParagraphLayoutPainter.font) {
|
||||||
// @ts-expect-error worker fonts API
|
// @ts-expect-error worker fonts API
|
||||||
self.fonts.add(ParagraphLayoutPainter.font);
|
self.fonts.add(ParagraphLayoutPainter.font);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from './layout/block-layout-provider';
|
export * from './layout/block-layout-provider';
|
||||||
|
export * from './painter/painter.worker';
|
||||||
export * from './text-utils';
|
export * from './text-utils';
|
||||||
export * from './turbo-renderer';
|
export * from './turbo-renderer';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|||||||
@@ -17,12 +17,19 @@ class BlockPainterRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewportLayoutPainter {
|
export class ViewportLayoutPainter {
|
||||||
private readonly canvas: OffscreenCanvas = new OffscreenCanvas(0, 0);
|
private readonly canvas: OffscreenCanvas = new OffscreenCanvas(0, 0);
|
||||||
private ctx: OffscreenCanvasRenderingContext2D | null = null;
|
private ctx: OffscreenCanvasRenderingContext2D | null = null;
|
||||||
private zoom = 1;
|
private zoom = 1;
|
||||||
public readonly registry = new BlockPainterRegistry();
|
public readonly registry = new BlockPainterRegistry();
|
||||||
|
|
||||||
|
constructor(painters: Record<string, BlockLayoutPainter>) {
|
||||||
|
Object.entries(painters).forEach(([type, painter]) => {
|
||||||
|
this.registry.register(type, painter);
|
||||||
|
});
|
||||||
|
self.onmessage = this.handler;
|
||||||
|
}
|
||||||
|
|
||||||
setSize(layoutRectW: number, layoutRectH: number, dpr: number, zoom: number) {
|
setSize(layoutRectW: number, layoutRectH: number, dpr: number, zoom: number) {
|
||||||
const width = layoutRectW * dpr * zoom;
|
const width = layoutRectW * dpr * zoom;
|
||||||
const height = layoutRectH * dpr * zoom;
|
const height = layoutRectH * dpr * zoom;
|
||||||
@@ -66,26 +73,16 @@ class ViewportLayoutPainter {
|
|||||||
};
|
};
|
||||||
self.postMessage(message, { transfer: [bitmap] });
|
self.postMessage(message, { transfer: [bitmap] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler = async (e: MessageEvent<HostToWorkerMessage>) => {
|
||||||
|
const { type, data } = e.data;
|
||||||
|
switch (type) {
|
||||||
|
case 'paintLayout': {
|
||||||
|
const { layout, width, height, dpr, zoom, version } = data;
|
||||||
|
this.setSize(width, height, dpr, zoom);
|
||||||
|
this.paint(layout, version);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const painter = new ViewportLayoutPainter();
|
|
||||||
|
|
||||||
self.onmessage = async (e: MessageEvent<HostToWorkerMessage>) => {
|
|
||||||
const { type, data } = e.data;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'paintLayout': {
|
|
||||||
const { layout, width, height, dpr, zoom, version } = data;
|
|
||||||
painter.setSize(width, height, dpr, zoom);
|
|
||||||
painter.paint(layout, version);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'registerPainter': {
|
|
||||||
const { painterConfigs } = data;
|
|
||||||
painterConfigs.forEach(async ({ type, path }) => {
|
|
||||||
painter.registry.register(type, new (await import(path)).default());
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
type GfxViewportElement,
|
type GfxViewportElement,
|
||||||
} from '@blocksuite/block-std/gfx';
|
} from '@blocksuite/block-std/gfx';
|
||||||
import type { Container } from '@blocksuite/global/di';
|
import type { Container } from '@blocksuite/global/di';
|
||||||
import { createIdentifier } from '@blocksuite/global/di';
|
|
||||||
import { DisposableGroup } from '@blocksuite/global/disposable';
|
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||||
import debounce from 'lodash-es/debounce';
|
import debounce from 'lodash-es/debounce';
|
||||||
|
|
||||||
@@ -18,9 +17,7 @@ import {
|
|||||||
syncCanvasSize,
|
syncCanvasSize,
|
||||||
} from './renderer-utils';
|
} from './renderer-utils';
|
||||||
import type {
|
import type {
|
||||||
BlockPainterConfig,
|
|
||||||
MessagePaint,
|
MessagePaint,
|
||||||
MessageRegisterPainter,
|
|
||||||
RendererOptions,
|
RendererOptions,
|
||||||
RenderingState,
|
RenderingState,
|
||||||
TurboRendererConfig,
|
TurboRendererConfig,
|
||||||
@@ -29,16 +26,12 @@ import type {
|
|||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
const debug = false; // Toggle for debug logs
|
const debug = false; // Toggle for debug logs
|
||||||
const workerUrl = new URL('./painter/painter.worker.ts', import.meta.url);
|
|
||||||
|
|
||||||
const defaultOptions: RendererOptions = {
|
const defaultOptions: RendererOptions = {
|
||||||
zoomThreshold: 1, // With high enough zoom, fallback to DOM rendering
|
zoomThreshold: 1, // With high enough zoom, fallback to DOM rendering
|
||||||
debounceTime: 1000, // During this period, fallback to DOM
|
debounceTime: 1000, // During this period, fallback to DOM
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BlockPainterConfigIdentifier =
|
|
||||||
createIdentifier<BlockPainterConfig>('block-painter-config');
|
|
||||||
|
|
||||||
export const TurboRendererConfigFactory =
|
export const TurboRendererConfigFactory =
|
||||||
ConfigExtensionFactory<TurboRendererConfig>('viewport-turbo-renderer');
|
ConfigExtensionFactory<TurboRendererConfig>('viewport-turbo-renderer');
|
||||||
|
|
||||||
@@ -47,13 +40,24 @@ export class ViewportTurboRendererExtension extends GfxExtension {
|
|||||||
|
|
||||||
public state: RenderingState = 'inactive';
|
public state: RenderingState = 'inactive';
|
||||||
public readonly canvas: HTMLCanvasElement = document.createElement('canvas');
|
public readonly canvas: HTMLCanvasElement = document.createElement('canvas');
|
||||||
private readonly worker: Worker = new Worker(workerUrl, { type: 'module' });
|
private readonly worker: Worker;
|
||||||
private readonly disposables = new DisposableGroup();
|
private readonly disposables = new DisposableGroup();
|
||||||
private layoutCacheData: ViewportLayout | null = null;
|
private layoutCacheData: ViewportLayout | null = null;
|
||||||
private layoutVersion = 0;
|
private layoutVersion = 0;
|
||||||
private bitmap: ImageBitmap | null = null;
|
private bitmap: ImageBitmap | null = null;
|
||||||
private viewportElement: GfxViewportElement | null = null;
|
private viewportElement: GfxViewportElement | null = null;
|
||||||
|
|
||||||
|
constructor(gfx: GfxController) {
|
||||||
|
super(gfx);
|
||||||
|
|
||||||
|
const id = TurboRendererConfigFactory.identifier;
|
||||||
|
const config = this.std.getOptional(id);
|
||||||
|
if (!config) {
|
||||||
|
throw new Error('TurboRendererConfig not found');
|
||||||
|
}
|
||||||
|
this.worker = config.painterWorkerEntry();
|
||||||
|
}
|
||||||
|
|
||||||
static override extendGfx(gfx: GfxController) {
|
static override extendGfx(gfx: GfxController) {
|
||||||
Object.defineProperty(gfx, 'turboRenderer', {
|
Object.defineProperty(gfx, 'turboRenderer', {
|
||||||
get() {
|
get() {
|
||||||
@@ -75,13 +79,6 @@ export class ViewportTurboRendererExtension extends GfxExtension {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get painterConfigs() {
|
|
||||||
const painterConfigMap = this.std.provider.getAll(
|
|
||||||
BlockPainterConfigIdentifier
|
|
||||||
);
|
|
||||||
return Array.from(painterConfigMap.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
override mounted() {
|
override mounted() {
|
||||||
const mountPoint = document.querySelector('.affine-edgeless-viewport');
|
const mountPoint = document.querySelector('.affine-edgeless-viewport');
|
||||||
if (mountPoint) {
|
if (mountPoint) {
|
||||||
@@ -123,13 +120,6 @@ export class ViewportTurboRendererExtension extends GfxExtension {
|
|||||||
this.disposables.add(
|
this.disposables.add(
|
||||||
this.std.store.slots.blockUpdated.subscribe(() => this.invalidate())
|
this.std.store.slots.blockUpdated.subscribe(() => this.invalidate())
|
||||||
);
|
);
|
||||||
|
|
||||||
const painterConfigs = this.painterConfigs;
|
|
||||||
const message: MessageRegisterPainter = {
|
|
||||||
type: 'registerPainter',
|
|
||||||
data: { painterConfigs },
|
|
||||||
};
|
|
||||||
this.worker.postMessage(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override unmounted() {
|
override unmounted() {
|
||||||
|
|||||||
@@ -83,20 +83,9 @@ export interface RendererOptions {
|
|||||||
debounceTime: number;
|
debounceTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlockPainterConfig {
|
|
||||||
type: string;
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TurboRendererConfig {
|
export interface TurboRendererConfig {
|
||||||
options?: Partial<RendererOptions>;
|
options?: Partial<RendererOptions>;
|
||||||
|
painterWorkerEntry: () => Worker;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageRegisterPainter = {
|
export type HostToWorkerMessage = MessagePaint;
|
||||||
type: 'registerPainter';
|
|
||||||
data: {
|
|
||||||
painterConfigs: BlockPainterConfig[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type HostToWorkerMessage = MessagePaint | MessageRegisterPainter;
|
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import { ViewportTurboRendererExtension } from '@blocksuite/affine-gfx-turbo-renderer';
|
import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph';
|
||||||
|
import {
|
||||||
|
TurboRendererConfigFactory,
|
||||||
|
ViewportTurboRendererExtension,
|
||||||
|
} from '@blocksuite/affine-gfx-turbo-renderer';
|
||||||
import { beforeEach, describe, expect, test } from 'vitest';
|
import { beforeEach, describe, expect, test } from 'vitest';
|
||||||
|
|
||||||
import { wait } from '../utils/common.js';
|
import { wait } from '../utils/common.js';
|
||||||
import { addSampleNotes } from '../utils/doc-generator.js';
|
import { addSampleNotes } from '../utils/doc-generator.js';
|
||||||
import { setupEditor } from '../utils/setup.js';
|
import { createPainterWorker, setupEditor } from '../utils/setup.js';
|
||||||
|
|
||||||
describe('viewport turbo renderer', () => {
|
describe('viewport turbo renderer', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const cleanup = await setupEditor('edgeless', [
|
const cleanup = await setupEditor('edgeless', [
|
||||||
|
ParagraphLayoutHandlerExtension,
|
||||||
|
TurboRendererConfigFactory({
|
||||||
|
painterWorkerEntry: createPainterWorker,
|
||||||
|
}),
|
||||||
ViewportTurboRendererExtension,
|
ViewportTurboRendererExtension,
|
||||||
]);
|
]);
|
||||||
return cleanup;
|
return cleanup;
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import {
|
import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph';
|
||||||
ParagraphLayoutHandlerExtension,
|
|
||||||
ParagraphPaintConfigExtension,
|
|
||||||
} from '@blocksuite/affine/blocks/paragraph';
|
|
||||||
import {
|
import {
|
||||||
TurboRendererConfigFactory,
|
TurboRendererConfigFactory,
|
||||||
ViewportTurboRendererExtension,
|
ViewportTurboRendererExtension,
|
||||||
@@ -9,17 +6,13 @@ import {
|
|||||||
} from '@blocksuite/affine/gfx/turbo-renderer';
|
} from '@blocksuite/affine/gfx/turbo-renderer';
|
||||||
|
|
||||||
import { addSampleNotes } from './doc-generator.js';
|
import { addSampleNotes } from './doc-generator.js';
|
||||||
import { setupEditor } from './setup.js';
|
import { createPainterWorker, setupEditor } from './setup.js';
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
setupEditor('edgeless', [
|
setupEditor('edgeless', [
|
||||||
ParagraphLayoutHandlerExtension,
|
ParagraphLayoutHandlerExtension,
|
||||||
ParagraphPaintConfigExtension,
|
|
||||||
TurboRendererConfigFactory({
|
TurboRendererConfigFactory({
|
||||||
options: {
|
painterWorkerEntry: createPainterWorker,
|
||||||
zoomThreshold: 1,
|
|
||||||
debounceTime: 1000,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
ViewportTurboRendererExtension,
|
ViewportTurboRendererExtension,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -99,6 +99,16 @@ async function createEditor(
|
|||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createPainterWorker() {
|
||||||
|
const worker = new Worker(
|
||||||
|
new URL('./turbo-painter-entry.worker.ts', import.meta.url),
|
||||||
|
{
|
||||||
|
type: 'module',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return worker;
|
||||||
|
}
|
||||||
|
|
||||||
export async function setupEditor(
|
export async function setupEditor(
|
||||||
mode: DocMode = 'page',
|
mode: DocMode = 'page',
|
||||||
extensions: ExtensionType[] = []
|
extensions: ExtensionType[] = []
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { ParagraphLayoutPainter } from '@blocksuite/affine-block-paragraph/turbo-painter';
|
||||||
|
import { ViewportLayoutPainter } from '@blocksuite/affine-gfx-turbo-renderer/painter';
|
||||||
|
|
||||||
|
new ViewportLayoutPainter({
|
||||||
|
'affine:paragraph': new ParagraphLayoutPainter(),
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { ParagraphLayoutPainter } from '@blocksuite/affine/blocks/paragraph';
|
||||||
|
import { ViewportLayoutPainter } from '@blocksuite/affine/gfx/turbo-renderer';
|
||||||
|
|
||||||
|
new ViewportLayoutPainter({
|
||||||
|
'affine:paragraph': new ParagraphLayoutPainter(),
|
||||||
|
});
|
||||||
@@ -1,21 +1,31 @@
|
|||||||
import {
|
import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph';
|
||||||
ParagraphLayoutHandlerExtension,
|
|
||||||
ParagraphPaintConfigExtension,
|
|
||||||
} from '@blocksuite/affine/blocks/paragraph';
|
|
||||||
import {
|
import {
|
||||||
TurboRendererConfigFactory,
|
TurboRendererConfigFactory,
|
||||||
ViewportTurboRendererExtension,
|
ViewportTurboRendererExtension,
|
||||||
} from '@blocksuite/affine/gfx/turbo-renderer';
|
} from '@blocksuite/affine/gfx/turbo-renderer';
|
||||||
|
|
||||||
|
function createPainterWorker() {
|
||||||
|
const worker = new Worker(
|
||||||
|
/* webpackChunkName: "turbo-painter-entry" */ new URL(
|
||||||
|
'./turbo-painter-entry.worker.ts',
|
||||||
|
import.meta.url
|
||||||
|
),
|
||||||
|
{
|
||||||
|
type: 'module',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return worker;
|
||||||
|
}
|
||||||
|
|
||||||
export function patchTurboRendererExtension() {
|
export function patchTurboRendererExtension() {
|
||||||
return [
|
return [
|
||||||
ParagraphLayoutHandlerExtension,
|
ParagraphLayoutHandlerExtension,
|
||||||
ParagraphPaintConfigExtension,
|
|
||||||
TurboRendererConfigFactory({
|
TurboRendererConfigFactory({
|
||||||
options: {
|
options: {
|
||||||
zoomThreshold: 1,
|
zoomThreshold: 1,
|
||||||
debounceTime: 1000,
|
debounceTime: 1000,
|
||||||
},
|
},
|
||||||
|
painterWorkerEntry: createPainterWorker,
|
||||||
}),
|
}),
|
||||||
ViewportTurboRendererExtension,
|
ViewportTurboRendererExtension,
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user