mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-30 00:29:46 +08:00
feat(editor): add basic image support in turbo renderer (#11620)
This PR adds basic support for image block: 
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
"@blocksuite/affine-block-note": "workspace:*",
|
"@blocksuite/affine-block-note": "workspace:*",
|
||||||
"@blocksuite/affine-block-surface": "workspace:*",
|
"@blocksuite/affine-block-surface": "workspace:*",
|
||||||
"@blocksuite/affine-components": "workspace:*",
|
"@blocksuite/affine-components": "workspace:*",
|
||||||
|
"@blocksuite/affine-gfx-turbo-renderer": "workspace:*",
|
||||||
"@blocksuite/affine-model": "workspace:*",
|
"@blocksuite/affine-model": "workspace:*",
|
||||||
"@blocksuite/affine-shared": "workspace:*",
|
"@blocksuite/affine-shared": "workspace:*",
|
||||||
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
||||||
@@ -32,7 +33,8 @@
|
|||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
"./effects": "./src/effects.ts"
|
"./effects": "./src/effects.ts",
|
||||||
|
"./turbo-painter": "./src/turbo/image-painter.worker.ts"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
|
|||||||
@@ -7,5 +7,7 @@ export { ImageProxyService } from './image-proxy-service';
|
|||||||
export * from './image-service';
|
export * from './image-service';
|
||||||
export * from './image-spec';
|
export * from './image-spec';
|
||||||
export * from './styles';
|
export * from './styles';
|
||||||
|
export * from './turbo/image-layout-handler';
|
||||||
|
export * from './turbo/image-painter.worker';
|
||||||
export { addImages, downloadImageBlob, uploadBlobForImage } from './utils';
|
export { addImages, downloadImageBlob, uploadBlobForImage } from './utils';
|
||||||
export { ImageSelection } from '@blocksuite/affine-shared/selection';
|
export { ImageSelection } from '@blocksuite/affine-shared/selection';
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import type { Rect } from '@blocksuite/affine-gfx-turbo-renderer';
|
||||||
|
import {
|
||||||
|
BlockLayoutHandlerExtension,
|
||||||
|
BlockLayoutHandlersIdentifier,
|
||||||
|
} from '@blocksuite/affine-gfx-turbo-renderer';
|
||||||
|
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 { ImageLayout } from './image-painter.worker';
|
||||||
|
|
||||||
|
export class ImageLayoutHandlerExtension extends BlockLayoutHandlerExtension<ImageLayout> {
|
||||||
|
readonly blockType = 'affine:image';
|
||||||
|
|
||||||
|
static override setup(di: Container) {
|
||||||
|
di.addImpl(
|
||||||
|
BlockLayoutHandlersIdentifier('image'),
|
||||||
|
ImageLayoutHandlerExtension
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override queryLayout(
|
||||||
|
model: BlockModel,
|
||||||
|
host: EditorHost,
|
||||||
|
viewportRecord: ViewportRecord
|
||||||
|
): ImageLayout | null {
|
||||||
|
const component = host.std.view.getBlock(model.id) as GfxBlockComponent;
|
||||||
|
if (!component) return null;
|
||||||
|
|
||||||
|
const imageContainer = component.querySelector('.affine-image-container');
|
||||||
|
if (!imageContainer) return null;
|
||||||
|
|
||||||
|
const resizableImg = component.querySelector(
|
||||||
|
'.resizable-img'
|
||||||
|
) as HTMLElement;
|
||||||
|
if (!resizableImg) return null;
|
||||||
|
|
||||||
|
const { zoom, viewScale } = viewportRecord;
|
||||||
|
const rect = resizableImg.getBoundingClientRect();
|
||||||
|
|
||||||
|
const [modelX, modelY] = clientToModelCoord(viewportRecord, [
|
||||||
|
rect.x,
|
||||||
|
rect.y,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const imageLayout: ImageLayout = {
|
||||||
|
type: 'affine:image',
|
||||||
|
blockId: model.id,
|
||||||
|
rect: {
|
||||||
|
x: modelX,
|
||||||
|
y: modelY,
|
||||||
|
w: rect.width / zoom / viewScale,
|
||||||
|
h: rect.height / zoom / viewScale,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return imageLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateBound(layout: ImageLayout) {
|
||||||
|
const rect: Rect = layout.rect;
|
||||||
|
|
||||||
|
return {
|
||||||
|
rect,
|
||||||
|
subRects: [rect],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import type {
|
||||||
|
BlockLayout,
|
||||||
|
BlockLayoutPainter,
|
||||||
|
} from '@blocksuite/affine-gfx-turbo-renderer';
|
||||||
|
import { BlockLayoutPainterExtension } from '@blocksuite/affine-gfx-turbo-renderer/painter';
|
||||||
|
|
||||||
|
export interface ImageLayout extends BlockLayout {
|
||||||
|
type: 'affine:image';
|
||||||
|
rect: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
w: number;
|
||||||
|
h: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImageLayout(layout: BlockLayout): layout is ImageLayout {
|
||||||
|
return layout.type === 'affine:image';
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageLayoutPainter implements BlockLayoutPainter {
|
||||||
|
paint(
|
||||||
|
ctx: OffscreenCanvasRenderingContext2D,
|
||||||
|
layout: BlockLayout,
|
||||||
|
layoutBaseX: number,
|
||||||
|
layoutBaseY: number
|
||||||
|
): void {
|
||||||
|
if (!isImageLayout(layout)) {
|
||||||
|
console.warn(
|
||||||
|
'Expected image layout but received different format:',
|
||||||
|
layout
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, just paint a white rectangle
|
||||||
|
const x = layout.rect.x - layoutBaseX;
|
||||||
|
const y = layout.rect.y - layoutBaseY;
|
||||||
|
const width = layout.rect.w;
|
||||||
|
const height = layout.rect.h;
|
||||||
|
|
||||||
|
// Draw a white rectangle with border
|
||||||
|
ctx.fillStyle = 'white';
|
||||||
|
ctx.fillRect(x, y, width, height);
|
||||||
|
|
||||||
|
// Add a border
|
||||||
|
ctx.strokeStyle = '#e0e0e0';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeRect(x, y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ImageLayoutPainterExtension = BlockLayoutPainterExtension(
|
||||||
|
'affine:image',
|
||||||
|
ImageLayoutPainter
|
||||||
|
);
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
{ "path": "../note" },
|
{ "path": "../note" },
|
||||||
{ "path": "../surface" },
|
{ "path": "../surface" },
|
||||||
{ "path": "../../components" },
|
{ "path": "../../components" },
|
||||||
|
{ "path": "../../gfx/turbo-renderer" },
|
||||||
{ "path": "../../model" },
|
{ "path": "../../model" },
|
||||||
{ "path": "../../shared" },
|
{ "path": "../../shared" },
|
||||||
{ "path": "../../widgets/slash-menu" },
|
{ "path": "../../widgets/slash-menu" },
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ImageLayoutHandlerExtension } from '@blocksuite/affine/blocks/image';
|
||||||
import { ListLayoutHandlerExtension } from '@blocksuite/affine/blocks/list';
|
import { ListLayoutHandlerExtension } from '@blocksuite/affine/blocks/list';
|
||||||
import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph';
|
import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph';
|
||||||
import {
|
import {
|
||||||
@@ -13,6 +14,7 @@ async function init() {
|
|||||||
setupEditor('edgeless', [
|
setupEditor('edgeless', [
|
||||||
ParagraphLayoutHandlerExtension,
|
ParagraphLayoutHandlerExtension,
|
||||||
ListLayoutHandlerExtension,
|
ListLayoutHandlerExtension,
|
||||||
|
ImageLayoutHandlerExtension,
|
||||||
TurboRendererConfigFactory({
|
TurboRendererConfigFactory({
|
||||||
painterWorkerEntry: createPainterWorker,
|
painterWorkerEntry: createPainterWorker,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ImageLayoutPainterExtension } from '@blocksuite/affine-block-image/turbo-painter';
|
||||||
import { ListLayoutPainterExtension } from '@blocksuite/affine-block-list/turbo-painter';
|
import { ListLayoutPainterExtension } from '@blocksuite/affine-block-list/turbo-painter';
|
||||||
import { NoteLayoutPainterExtension } from '@blocksuite/affine-block-note/turbo-painter';
|
import { NoteLayoutPainterExtension } from '@blocksuite/affine-block-note/turbo-painter';
|
||||||
import { ParagraphLayoutPainterExtension } from '@blocksuite/affine-block-paragraph/turbo-painter';
|
import { ParagraphLayoutPainterExtension } from '@blocksuite/affine-block-paragraph/turbo-painter';
|
||||||
@@ -7,4 +8,5 @@ new ViewportLayoutPainter([
|
|||||||
ParagraphLayoutPainterExtension,
|
ParagraphLayoutPainterExtension,
|
||||||
ListLayoutPainterExtension,
|
ListLayoutPainterExtension,
|
||||||
NoteLayoutPainterExtension,
|
NoteLayoutPainterExtension,
|
||||||
|
ImageLayoutPainterExtension,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CodeLayoutPainterExtension } from '@blocksuite/affine/blocks/code';
|
import { CodeLayoutPainterExtension } from '@blocksuite/affine/blocks/code';
|
||||||
|
import { ImageLayoutPainterExtension } from '@blocksuite/affine/blocks/image';
|
||||||
import { ListLayoutPainterExtension } from '@blocksuite/affine/blocks/list';
|
import { ListLayoutPainterExtension } from '@blocksuite/affine/blocks/list';
|
||||||
import { NoteLayoutPainterExtension } from '@blocksuite/affine/blocks/note';
|
import { NoteLayoutPainterExtension } from '@blocksuite/affine/blocks/note';
|
||||||
import { ParagraphLayoutPainterExtension } from '@blocksuite/affine/blocks/paragraph';
|
import { ParagraphLayoutPainterExtension } from '@blocksuite/affine/blocks/paragraph';
|
||||||
@@ -9,4 +10,5 @@ new ViewportLayoutPainter([
|
|||||||
ListLayoutPainterExtension,
|
ListLayoutPainterExtension,
|
||||||
NoteLayoutPainterExtension,
|
NoteLayoutPainterExtension,
|
||||||
CodeLayoutPainterExtension,
|
CodeLayoutPainterExtension,
|
||||||
|
ImageLayoutPainterExtension,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { getWorkerUrl } from '@affine/env/worker';
|
import { getWorkerUrl } from '@affine/env/worker';
|
||||||
import { CodeLayoutHandlerExtension } from '@blocksuite/affine/blocks/code';
|
import { CodeLayoutHandlerExtension } from '@blocksuite/affine/blocks/code';
|
||||||
|
import { ImageLayoutHandlerExtension } from '@blocksuite/affine/blocks/image';
|
||||||
import { ListLayoutHandlerExtension } from '@blocksuite/affine/blocks/list';
|
import { ListLayoutHandlerExtension } from '@blocksuite/affine/blocks/list';
|
||||||
import { NoteLayoutHandlerExtension } from '@blocksuite/affine/blocks/note';
|
import { NoteLayoutHandlerExtension } from '@blocksuite/affine/blocks/note';
|
||||||
import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph';
|
import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph';
|
||||||
@@ -19,6 +20,7 @@ export function patchTurboRendererExtension() {
|
|||||||
ListLayoutHandlerExtension,
|
ListLayoutHandlerExtension,
|
||||||
NoteLayoutHandlerExtension,
|
NoteLayoutHandlerExtension,
|
||||||
CodeLayoutHandlerExtension,
|
CodeLayoutHandlerExtension,
|
||||||
|
ImageLayoutHandlerExtension,
|
||||||
TurboRendererConfigFactory({
|
TurboRendererConfigFactory({
|
||||||
options: {
|
options: {
|
||||||
zoomThreshold: 1,
|
zoomThreshold: 1,
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ export const PackageList = [
|
|||||||
'blocksuite/affine/blocks/note',
|
'blocksuite/affine/blocks/note',
|
||||||
'blocksuite/affine/blocks/surface',
|
'blocksuite/affine/blocks/surface',
|
||||||
'blocksuite/affine/components',
|
'blocksuite/affine/components',
|
||||||
|
'blocksuite/affine/gfx/turbo-renderer',
|
||||||
'blocksuite/affine/model',
|
'blocksuite/affine/model',
|
||||||
'blocksuite/affine/shared',
|
'blocksuite/affine/shared',
|
||||||
'blocksuite/affine/widgets/slash-menu',
|
'blocksuite/affine/widgets/slash-menu',
|
||||||
|
|||||||
@@ -2646,6 +2646,7 @@ __metadata:
|
|||||||
"@blocksuite/affine-block-note": "workspace:*"
|
"@blocksuite/affine-block-note": "workspace:*"
|
||||||
"@blocksuite/affine-block-surface": "workspace:*"
|
"@blocksuite/affine-block-surface": "workspace:*"
|
||||||
"@blocksuite/affine-components": "workspace:*"
|
"@blocksuite/affine-components": "workspace:*"
|
||||||
|
"@blocksuite/affine-gfx-turbo-renderer": "workspace:*"
|
||||||
"@blocksuite/affine-model": "workspace:*"
|
"@blocksuite/affine-model": "workspace:*"
|
||||||
"@blocksuite/affine-shared": "workspace:*"
|
"@blocksuite/affine-shared": "workspace:*"
|
||||||
"@blocksuite/affine-widget-slash-menu": "workspace:*"
|
"@blocksuite/affine-widget-slash-menu": "workspace:*"
|
||||||
|
|||||||
Reference in New Issue
Block a user