mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-22 00:37:05 +08:00
feat(editor): add basic note support in turbo renderer (#11607)
After landing layout tree refactoring, this PR adds basic note support in turbo renderer. In this demo recording, the code and image block needs to be further supported. [Screen Recording 2025-04-10 at 5.16.15 PM.mov <span class="graphite__hidden">(uploaded via Graphite)</span> <img class="graphite__hidden" src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/lEGcysB4lFTEbCwZ8jMv/2e416b41-5609-4e52-a90f-5b7bb77db682.mov" />](https://app.graphite.dev/media/video/lEGcysB4lFTEbCwZ8jMv/2e416b41-5609-4e52-a90f-5b7bb77db682.mov)
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
"@blocksuite/affine-block-surface": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-fragment-doc-title": "workspace:*",
|
||||
"@blocksuite/affine-gfx-turbo-renderer": "workspace:*",
|
||||
"@blocksuite/affine-inline-preset": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-rich-text": "workspace:*",
|
||||
@@ -37,7 +38,8 @@
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effects": "./src/effects.ts"
|
||||
"./effects": "./src/effects.ts",
|
||||
"./turbo-painter": "./src/turbo/note-painter.worker.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
|
||||
@@ -6,3 +6,5 @@ export * from './edgeless-clipboard-config';
|
||||
export * from './note-block';
|
||||
export * from './note-edgeless-block';
|
||||
export * from './note-spec';
|
||||
export * from './turbo/note-layout-handler';
|
||||
export * from './turbo/note-painter.worker';
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import type { Rect } from '@blocksuite/affine-gfx-turbo-renderer';
|
||||
import {
|
||||
BlockLayoutHandlerExtension,
|
||||
BlockLayoutHandlersIdentifier,
|
||||
} from '@blocksuite/affine-gfx-turbo-renderer';
|
||||
import {
|
||||
ColorScheme,
|
||||
type NoteBlockModel,
|
||||
resolveColor,
|
||||
} from '@blocksuite/affine-model';
|
||||
import type { Container } from '@blocksuite/global/di';
|
||||
import type { EditorHost, GfxBlockComponent } from '@blocksuite/std';
|
||||
import { clientToModelCoord, type ViewportRecord } from '@blocksuite/std/gfx';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
|
||||
import type { NoteLayout } from './note-painter.worker';
|
||||
|
||||
export class NoteLayoutHandlerExtension extends BlockLayoutHandlerExtension<NoteLayout> {
|
||||
readonly blockType = 'affine:note';
|
||||
|
||||
static override setup(di: Container) {
|
||||
di.addImpl(
|
||||
BlockLayoutHandlersIdentifier('note'),
|
||||
NoteLayoutHandlerExtension
|
||||
);
|
||||
}
|
||||
|
||||
override queryLayout(
|
||||
model: BlockModel,
|
||||
host: EditorHost,
|
||||
viewportRecord: ViewportRecord
|
||||
): NoteLayout | null {
|
||||
const component = host.std.view.getBlock(model.id) as GfxBlockComponent;
|
||||
if (!component) return null;
|
||||
|
||||
// Get the note container element
|
||||
const noteContainer = component.querySelector('.affine-note-mask');
|
||||
if (!noteContainer) return null;
|
||||
|
||||
// Get the bounding client rect of the note container
|
||||
const clientRect = noteContainer.getBoundingClientRect();
|
||||
|
||||
// Convert client coordinates to model coordinates
|
||||
const [modelX, modelY] = clientToModelCoord(viewportRecord, [
|
||||
clientRect.x,
|
||||
clientRect.y,
|
||||
]);
|
||||
|
||||
const { zoom, viewScale } = viewportRecord;
|
||||
|
||||
// Cast model to NoteBlockModel to access background property from props
|
||||
const noteModel = model as NoteBlockModel;
|
||||
const background = noteModel.props.background;
|
||||
// Resolve the color to a string
|
||||
const backgroundString = resolveColor(background, ColorScheme.Light);
|
||||
|
||||
// Create the note layout object
|
||||
const noteLayout: NoteLayout = {
|
||||
type: 'affine:note',
|
||||
blockId: model.id,
|
||||
rect: {
|
||||
x: modelX,
|
||||
y: modelY,
|
||||
w: clientRect.width / zoom / viewScale,
|
||||
h: clientRect.height / zoom / viewScale,
|
||||
},
|
||||
background: backgroundString,
|
||||
};
|
||||
|
||||
return noteLayout;
|
||||
}
|
||||
|
||||
calculateBound(layout: NoteLayout) {
|
||||
const rect: Rect = layout.rect;
|
||||
|
||||
return {
|
||||
rect,
|
||||
subRects: [rect], // The note is represented by a single rectangle
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import type {
|
||||
BlockLayout,
|
||||
BlockLayoutPainter,
|
||||
WorkerToHostMessage,
|
||||
} from '@blocksuite/affine-gfx-turbo-renderer';
|
||||
import { BlockLayoutPainterExtension } from '@blocksuite/affine-gfx-turbo-renderer/painter';
|
||||
|
||||
export interface NoteLayout extends BlockLayout {
|
||||
type: 'affine:note';
|
||||
background?: string;
|
||||
}
|
||||
|
||||
function isNoteLayout(layout: BlockLayout): layout is NoteLayout {
|
||||
return layout.type === 'affine:note';
|
||||
}
|
||||
|
||||
class NoteLayoutPainter implements BlockLayoutPainter {
|
||||
paint(
|
||||
ctx: OffscreenCanvasRenderingContext2D,
|
||||
layout: BlockLayout,
|
||||
layoutBaseX: number,
|
||||
layoutBaseY: number
|
||||
): void {
|
||||
if (!isNoteLayout(layout)) {
|
||||
const message: WorkerToHostMessage = {
|
||||
type: 'paintError',
|
||||
error: 'Invalid layout format',
|
||||
blockType: 'affine:note',
|
||||
};
|
||||
self.postMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the layout rectangle
|
||||
const x = layout.rect.x - layoutBaseX;
|
||||
const y = layout.rect.y - layoutBaseY;
|
||||
const width = layout.rect.w;
|
||||
const height = layout.rect.h;
|
||||
|
||||
ctx.fillStyle = layout.background || 'rgb(255, 255, 255)';
|
||||
ctx.fillRect(x, y, width, height);
|
||||
ctx.strokeRect(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
export const NoteLayoutPainterExtension = BlockLayoutPainterExtension(
|
||||
'affine:note',
|
||||
NoteLayoutPainter
|
||||
);
|
||||
@@ -11,6 +11,7 @@
|
||||
{ "path": "../surface" },
|
||||
{ "path": "../../components" },
|
||||
{ "path": "../../fragments/doc-title" },
|
||||
{ "path": "../../gfx/turbo-renderer" },
|
||||
{ "path": "../../inlines/preset" },
|
||||
{ "path": "../../model" },
|
||||
{ "path": "../../rich-text" },
|
||||
|
||||
Reference in New Issue
Block a user