diff --git a/blocksuite/affine/block-image/package.json b/blocksuite/affine/block-image/package.json
new file mode 100644
index 0000000000..5961f76bc4
--- /dev/null
+++ b/blocksuite/affine/block-image/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "@blocksuite/affine-block-image",
+ "description": "Image block for BlockSuite.",
+ "type": "module",
+ "scripts": {
+ "build": "tsc",
+ "test:unit": "nx vite:test --run --passWithNoTests",
+ "test:unit:coverage": "nx vite:test --run --coverage",
+ "test:e2e": "playwright test"
+ },
+ "sideEffects": false,
+ "keywords": [],
+ "author": "toeverything",
+ "license": "MIT",
+ "dependencies": {
+ "@blocksuite/affine-components": "workspace:*",
+ "@blocksuite/affine-model": "workspace:*",
+ "@blocksuite/affine-shared": "workspace:*",
+ "@blocksuite/block-std": "workspace:*",
+ "@blocksuite/global": "workspace:*",
+ "@blocksuite/icons": "^2.1.75",
+ "@blocksuite/inline": "workspace:*",
+ "@blocksuite/store": "workspace:*",
+ "@floating-ui/dom": "^1.6.10",
+ "@lit/context": "^1.1.2",
+ "@preact/signals-core": "^1.8.0",
+ "@toeverything/theme": "^1.1.1",
+ "file-type": "^19.5.0",
+ "lit": "^3.2.0",
+ "minimatch": "^10.0.1",
+ "zod": "^3.23.8"
+ },
+ "exports": {
+ ".": "./src/index.ts",
+ "./effects": "./src/effects.ts"
+ },
+ "files": [
+ "src",
+ "dist",
+ "!src/__tests__",
+ "!dist/__tests__"
+ ]
+}
diff --git a/blocksuite/blocks/src/image-block/adapters/extension.ts b/blocksuite/affine/block-image/src/adapters/extension.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/adapters/extension.ts
rename to blocksuite/affine/block-image/src/adapters/extension.ts
diff --git a/blocksuite/blocks/src/image-block/adapters/html.ts b/blocksuite/affine/block-image/src/adapters/html.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/adapters/html.ts
rename to blocksuite/affine/block-image/src/adapters/html.ts
diff --git a/blocksuite/blocks/src/image-block/adapters/index.ts b/blocksuite/affine/block-image/src/adapters/index.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/adapters/index.ts
rename to blocksuite/affine/block-image/src/adapters/index.ts
diff --git a/blocksuite/blocks/src/image-block/adapters/markdown.ts b/blocksuite/affine/block-image/src/adapters/markdown.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/adapters/markdown.ts
rename to blocksuite/affine/block-image/src/adapters/markdown.ts
diff --git a/blocksuite/affine/block-image/src/adapters/middleware.ts b/blocksuite/affine/block-image/src/adapters/middleware.ts
new file mode 100644
index 0000000000..1b72ba88b1
--- /dev/null
+++ b/blocksuite/affine/block-image/src/adapters/middleware.ts
@@ -0,0 +1,27 @@
+import { DEFAULT_IMAGE_PROXY_ENDPOINT } from '@blocksuite/affine-shared/consts';
+import type { JobMiddleware } from '@blocksuite/store';
+
+export const customImageProxyMiddleware = (
+ imageProxyURL: string
+): JobMiddleware => {
+ return ({ adapterConfigs }) => {
+ adapterConfigs.set('imageProxy', imageProxyURL);
+ };
+};
+
+const imageProxyMiddlewareBuilder = () => {
+ let middleware = customImageProxyMiddleware(DEFAULT_IMAGE_PROXY_ENDPOINT);
+ return {
+ get: () => middleware,
+ set: (url: string) => {
+ middleware = customImageProxyMiddleware(url);
+ },
+ };
+};
+
+const defaultImageProxyMiddlewarBuilder = imageProxyMiddlewareBuilder();
+
+export const setImageProxyMiddlewareURL = defaultImageProxyMiddlewarBuilder.set;
+
+export const defaultImageProxyMiddleware =
+ defaultImageProxyMiddlewarBuilder.get();
diff --git a/blocksuite/blocks/src/image-block/adapters/notion-html.ts b/blocksuite/affine/block-image/src/adapters/notion-html.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/adapters/notion-html.ts
rename to blocksuite/affine/block-image/src/adapters/notion-html.ts
diff --git a/blocksuite/blocks/src/image-block/commands/index.ts b/blocksuite/affine/block-image/src/commands/index.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/commands/index.ts
rename to blocksuite/affine/block-image/src/commands/index.ts
diff --git a/blocksuite/blocks/src/image-block/commands/insert-images.ts b/blocksuite/affine/block-image/src/commands/insert-images.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/commands/insert-images.ts
rename to blocksuite/affine/block-image/src/commands/insert-images.ts
diff --git a/blocksuite/blocks/src/image-block/components/image-block-fallback.ts b/blocksuite/affine/block-image/src/components/image-block-fallback.ts
similarity index 97%
rename from blocksuite/blocks/src/image-block/components/image-block-fallback.ts
rename to blocksuite/affine/block-image/src/components/image-block-fallback.ts
index f603640f55..83a59d2458 100644
--- a/blocksuite/blocks/src/image-block/components/image-block-fallback.ts
+++ b/blocksuite/affine/block-image/src/components/image-block-fallback.ts
@@ -7,7 +7,7 @@ import { css, html } from 'lit';
import { property } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
-import { FailedImageIcon, ImageIcon, LoadingIcon } from '../styles.js';
+import { FailedImageIcon, LoadedImageIcon, LoadingIcon } from '../styles.js';
export const SURFACE_IMAGE_CARD_WIDTH = 220;
export const SURFACE_IMAGE_CARD_HEIGHT = 122;
@@ -85,7 +85,7 @@ export class ImageBlockFallbackCard extends WithDisposable(ShadowlessElement) {
? LoadingIcon
: error
? FailedImageIcon
- : ImageIcon;
+ : LoadedImageIcon;
const titleText = loading
? 'Loading image...'
diff --git a/blocksuite/blocks/src/image-block/components/image-selected-rect.ts b/blocksuite/affine/block-image/src/components/image-selected-rect.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/components/image-selected-rect.ts
rename to blocksuite/affine/block-image/src/components/image-selected-rect.ts
diff --git a/blocksuite/blocks/src/image-block/components/page-image-block.ts b/blocksuite/affine/block-image/src/components/page-image-block.ts
similarity index 98%
rename from blocksuite/blocks/src/image-block/components/page-image-block.ts
rename to blocksuite/affine/block-image/src/components/page-image-block.ts
index ef5d6856fe..e9e07e684a 100644
--- a/blocksuite/blocks/src/image-block/components/page-image-block.ts
+++ b/blocksuite/affine/block-image/src/components/page-image-block.ts
@@ -80,6 +80,7 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
);
};
+ // TODO: use key map extension
this.block.bindHotKey({
Escape: () => {
selection.update(selList => {
@@ -134,6 +135,7 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
return next({ focusBlock: nextBlock });
})
+ // @ts-expect-error FIXME(command): BS-2216
.focusBlockStart()
.run();
return true;
@@ -159,6 +161,7 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
return next({ focusBlock: prevBlock });
})
+ // @ts-expect-error FIXME(command): BS-2216
.focusBlockEnd()
.run();
return true;
diff --git a/blocksuite/blocks/src/image-block/effects.ts b/blocksuite/affine/block-image/src/effects.ts
similarity index 51%
rename from blocksuite/blocks/src/image-block/effects.ts
rename to blocksuite/affine/block-image/src/effects.ts
index a1770e2f5f..f5823d3bfe 100644
--- a/blocksuite/blocks/src/image-block/effects.ts
+++ b/blocksuite/affine/block-image/src/effects.ts
@@ -1,9 +1,17 @@
import type { getImageSelectionsCommand } from '@blocksuite/affine-shared/commands';
import type { insertImagesCommand } from './commands/insert-images.js';
+import { ImageBlockFallbackCard } from './components/image-block-fallback.js';
+import { ImageBlockPageComponent } from './components/page-image-block.js';
+import { ImageBlockComponent } from './image-block.js';
+import { ImageEdgelessBlockComponent } from './image-edgeless-block.js';
+import type { ImageBlockService } from './image-service.js';
export function effects() {
- // TODO(@L-Sun): move other effects to this file
+ customElements.define('affine-image', ImageBlockComponent);
+ customElements.define('affine-edgeless-image', ImageEdgelessBlockComponent);
+ customElements.define('affine-page-image', ImageBlockPageComponent);
+ customElements.define('affine-image-fallback-card', ImageBlockFallbackCard);
}
declare global {
@@ -22,5 +30,9 @@ declare global {
*/
insertImages: typeof insertImagesCommand;
}
+
+ interface BlockServices {
+ 'affine:image': ImageBlockService;
+ }
}
}
diff --git a/blocksuite/blocks/src/image-block/image-block.ts b/blocksuite/affine/block-image/src/image-block.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/image-block.ts
rename to blocksuite/affine/block-image/src/image-block.ts
diff --git a/blocksuite/blocks/src/image-block/image-edgeless-block.ts b/blocksuite/affine/block-image/src/image-edgeless-block.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/image-edgeless-block.ts
rename to blocksuite/affine/block-image/src/image-edgeless-block.ts
diff --git a/blocksuite/blocks/src/image-block/image-resize-manager.ts b/blocksuite/affine/block-image/src/image-resize-manager.ts
similarity index 89%
rename from blocksuite/blocks/src/image-block/image-resize-manager.ts
rename to blocksuite/affine/block-image/src/image-resize-manager.ts
index 3b2752e7bf..0738ee1f55 100644
--- a/blocksuite/blocks/src/image-block/image-resize-manager.ts
+++ b/blocksuite/affine/block-image/src/image-resize-manager.ts
@@ -4,11 +4,9 @@ import {
getModelByElement,
} from '@blocksuite/affine-shared/utils';
import type { BlockComponent, PointerEventState } from '@blocksuite/block-std';
+import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { assertExists } from '@blocksuite/global/utils';
-import type { EdgelessRootBlockComponent } from '../root-block/index.js';
-import { getClosestRootBlockComponent } from '../root-block/utils/query.js';
-
export class ImageResizeManager {
private _activeComponent: BlockComponent | null = null;
@@ -79,9 +77,8 @@ export class ImageResizeManager {
rootComponent.service.std.get(DocModeProvider).getEditorMode() ===
'edgeless'
) {
- this._zoom = (
- rootComponent as EdgelessRootBlockComponent
- ).service.viewport.zoom;
+ const viewport = rootComponent.std.get(GfxControllerIdentifier).viewport;
+ this._zoom = viewport.zoom;
} else {
this._zoom = 1;
}
@@ -97,3 +94,7 @@ export class ImageResizeManager {
}
}
}
+
+function getClosestRootBlockComponent(el: HTMLElement): BlockComponent | null {
+ return el.closest('affine-edgeless-root, affine-page-root');
+}
diff --git a/blocksuite/blocks/src/image-block/image-service.ts b/blocksuite/affine/block-image/src/image-service.ts
similarity index 86%
rename from blocksuite/blocks/src/image-block/image-service.ts
rename to blocksuite/affine/block-image/src/image-service.ts
index 43ea22fa3f..a3946ebbf8 100644
--- a/blocksuite/blocks/src/image-block/image-service.ts
+++ b/blocksuite/affine/block-image/src/image-service.ts
@@ -8,9 +8,8 @@ import {
import { BlockService } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
-import { setImageProxyMiddlewareURL } from '../_common/transformers/middlewares.js';
-import { addImages } from '../root-block/edgeless/utils/common.js';
-import { addSiblingImageBlock } from './utils.js';
+import { setImageProxyMiddlewareURL } from './adapters/middleware.js';
+import { addImages, addSiblingImageBlock } from './utils.js';
// bytes.parse('2GB')
const maxFileSize = 2147483648;
@@ -29,7 +28,10 @@ export const ImageDropOption = FileDropConfigExtension({
const imageFiles = files.filter(file => file.type.startsWith('image/'));
if (!imageFiles.length) return false;
- if (targetModel && !matchFlavours(targetModel, ['affine:surface'])) {
+ if (
+ targetModel &&
+ !matchFlavours(targetModel, ['affine:surface' as BlockSuite.Flavour])
+ ) {
addSiblingImageBlock(
std.host,
imageFiles,
diff --git a/blocksuite/blocks/src/image-block/image-spec.ts b/blocksuite/affine/block-image/src/image-spec.ts
similarity index 100%
rename from blocksuite/blocks/src/image-block/image-spec.ts
rename to blocksuite/affine/block-image/src/image-spec.ts
diff --git a/blocksuite/affine/block-image/src/index.ts b/blocksuite/affine/block-image/src/index.ts
new file mode 100644
index 0000000000..c844dcc910
--- /dev/null
+++ b/blocksuite/affine/block-image/src/index.ts
@@ -0,0 +1,8 @@
+export * from './adapters';
+export * from './image-block';
+export * from './image-edgeless-block';
+export * from './image-service';
+export * from './image-spec';
+export * from './styles';
+export { addImages, downloadImageBlob, uploadBlobForImage } from './utils';
+export { ImageSelection } from '@blocksuite/affine-shared/selection';
diff --git a/blocksuite/blocks/src/image-block/styles.ts b/blocksuite/affine/block-image/src/styles.ts
similarity index 99%
rename from blocksuite/blocks/src/image-block/styles.ts
rename to blocksuite/affine/block-image/src/styles.ts
index 2717e1e057..9902692152 100644
--- a/blocksuite/blocks/src/image-block/styles.ts
+++ b/blocksuite/affine/block-image/src/styles.ts
@@ -28,7 +28,7 @@ export const LoadingIcon = html`
`;
-export const ImageIcon = html`";
export function generateCursorUrl(
angle = 0,
@@ -79,27 +74,6 @@ export function getTooltipWithShortcut(
`;
}
-export function readImageSize(file: File) {
- return new Promise<{ width: number; height: number }>(resolve => {
- const size = { width: 0, height: 0 };
- const img = new Image();
-
- img.onload = () => {
- size.width = img.width;
- size.height = img.height;
- URL.revokeObjectURL(img.src);
- resolve(size);
- };
-
- img.onerror = () => {
- URL.revokeObjectURL(img.src);
- resolve(size);
- };
-
- img.src = URL.createObjectURL(file);
- });
-}
-
const RESIZE_CURSORS: CursorType[] = [
'ew-resize',
'nwse-resize',
@@ -245,14 +219,3 @@ export function launchIntoFullscreen(element: Element) {
element.msRequestFullscreen();
}
}
-
-export function calcBoundByOrigin(
- point: IVec,
- inTopLeft = false,
- width = SURFACE_IMAGE_CARD_WIDTH,
- height = SURFACE_IMAGE_CARD_HEIGHT
-) {
- return inTopLeft
- ? new Bound(point[0], point[1], width, height)
- : Bound.fromCenter(point, width, height);
-}
diff --git a/blocksuite/blocks/src/root-block/edgeless/utils/common.ts b/blocksuite/blocks/src/root-block/edgeless/utils/common.ts
index ec7bdafdd3..bb105c4c8b 100644
--- a/blocksuite/blocks/src/root-block/edgeless/utils/common.ts
+++ b/blocksuite/blocks/src/root-block/edgeless/utils/common.ts
@@ -1,136 +1,25 @@
import { focusTextModel } from '@blocksuite/affine-components/rich-text';
-import { toast } from '@blocksuite/affine-components/toast';
import {
DEFAULT_NOTE_HEIGHT,
DEFAULT_NOTE_WIDTH,
- type ImageBlockProps,
NOTE_MIN_HEIGHT,
type NoteBlockModel,
NoteDisplayMode,
} from '@blocksuite/affine-model';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import type { NoteChildrenFlavour } from '@blocksuite/affine-shared/types';
-import {
- handleNativeRangeAtPoint,
- humanFileSize,
-} from '@blocksuite/affine-shared/utils';
+import { handleNativeRangeAtPoint } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import {
type IPoint,
- type IVec,
- Point,
+ type Point,
serializeXYWH,
- Vec,
} from '@blocksuite/global/utils';
-import { calcBoundByOrigin, readImageSize } from '../components/utils.js';
import { DEFAULT_NOTE_OFFSET_X, DEFAULT_NOTE_OFFSET_Y } from './consts.js';
import { addBlock } from './crud.js';
-export async function addImages(
- std: BlockStdScope,
- files: File[],
- options: {
- point?: IVec;
- maxWidth?: number;
- }
-): Promise {
- const imageFiles = [...files].filter(file => file.type.startsWith('image/'));
- if (!imageFiles.length) return [];
-
- const imageService = std.getService('affine:image');
- const gfx = std.get(GfxControllerIdentifier);
-
- if (!imageService) {
- console.error('Image service not found');
- return [];
- }
-
- const maxFileSize = imageService.maxFileSize;
- const isSizeExceeded = imageFiles.some(file => file.size > maxFileSize);
- if (isSizeExceeded) {
- toast(
- std.host,
- `You can only upload files less than ${humanFileSize(
- maxFileSize,
- true,
- 0
- )}`
- );
- return [];
- }
-
- const { point, maxWidth } = options;
- let { x, y } = gfx.viewport.center;
- if (point) [x, y] = gfx.viewport.toModelCoord(...point);
-
- const dropInfos: { point: Point; blockId: string }[] = [];
- const IMAGE_STACK_GAP = 32;
- const isMultipleFiles = imageFiles.length > 1;
- const inTopLeft = isMultipleFiles ? true : false;
-
- // create image cards without image data
- imageFiles.forEach((file, index) => {
- const point = new Point(
- x + index * IMAGE_STACK_GAP,
- y + index * IMAGE_STACK_GAP
- );
- const center = Vec.toVec(point);
- const bound = calcBoundByOrigin(center, inTopLeft);
- const blockId = std.doc.addBlock(
- 'affine:image',
- {
- size: file.size,
- xywh: bound.serialize(),
- index: gfx.layer.generateIndex(),
- },
- gfx.surface
- );
- dropInfos.push({ point, blockId });
- });
-
- // upload image data and update the image model
- const uploadPromises = imageFiles.map(async (file, index) => {
- const { point, blockId } = dropInfos[index];
-
- const sourceId = await std.doc.blobSync.set(file);
- const imageSize = await readImageSize(file);
-
- const center = Vec.toVec(point);
- // If maxWidth is provided, limit the width of the image to maxWidth
- // Otherwise, use the original width
- const width = maxWidth
- ? Math.min(imageSize.width, maxWidth)
- : imageSize.width;
- const height = maxWidth
- ? (imageSize.height / imageSize.width) * width
- : imageSize.height;
- const bound = calcBoundByOrigin(center, inTopLeft, width, height);
-
- std.doc.withoutTransact(() => {
- gfx.updateElement(blockId, {
- sourceId,
- ...imageSize,
- width,
- height,
- xywh: bound.serialize(),
- } satisfies Partial);
- });
- });
- await Promise.all(uploadPromises);
-
- const blockIds = dropInfos.map(info => info.blockId);
- gfx.selection.set({
- elements: blockIds,
- editing: false,
- });
- if (isMultipleFiles) {
- std.command.exec('autoResizeElements');
- }
- return blockIds;
-}
-
export function addNoteAtPoint(
std: BlockStdScope,
/**
diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-image-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-image-button.ts
index aa0ac93c7a..701087eb12 100644
--- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-image-button.ts
+++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-image-button.ts
@@ -1,11 +1,13 @@
+import {
+ downloadImageBlob,
+ type ImageBlockComponent,
+} from '@blocksuite/affine-block-image';
import { CaptionIcon, DownloadIcon } from '@blocksuite/affine-components/icons';
import type { ImageBlockModel } from '@blocksuite/affine-model';
import { WithDisposable } from '@blocksuite/global/utils';
import { html, LitElement, nothing } from 'lit';
import { property } from 'lit/decorators.js';
-import type { ImageBlockComponent } from '../../../image-block/image-block.js';
-import { downloadImageBlob } from '../../../image-block/utils.js';
import type { EdgelessRootBlockComponent } from '../../edgeless/edgeless-root-block.js';
export class EdgelessChangeImageButton extends WithDisposable(LitElement) {
diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts
index 59bc904bcd..08e2bdb0e0 100644
--- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts
+++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts
@@ -6,6 +6,7 @@ import type {
EmbedLoomBlockComponent,
EmbedYoutubeBlockComponent,
} from '@blocksuite/affine-block-embed';
+import type { ImageBlockComponent } from '@blocksuite/affine-block-image';
import { isPeekable, peek } from '@blocksuite/affine-components/peek';
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
@@ -32,7 +33,6 @@ import {
notifyDocCreated,
promptDocTitle,
} from '../../../../_common/utils/render-linked-doc.js';
-import type { ImageBlockComponent } from '../../../../image-block/image-block.js';
import { duplicate } from '../../../edgeless/utils/clipboard-utils.js';
import { getSortedCloneElements } from '../../../edgeless/utils/clone-utils.js';
import { moveConnectors } from '../../../edgeless/utils/connector.js';
diff --git a/blocksuite/blocks/src/root-block/widgets/image-toolbar/context.ts b/blocksuite/blocks/src/root-block/widgets/image-toolbar/context.ts
index 127cf70714..763938d5ee 100644
--- a/blocksuite/blocks/src/root-block/widgets/image-toolbar/context.ts
+++ b/blocksuite/blocks/src/root-block/widgets/image-toolbar/context.ts
@@ -1,7 +1,6 @@
+import type { ImageBlockComponent } from '@blocksuite/affine-block-image';
import { MenuContext } from '@blocksuite/affine-components/toolbar';
-import type { ImageBlockComponent } from '../../../image-block/image-block.js';
-
export class ImageToolbarContext extends MenuContext {
override close = () => {
this.abortController.abort();
diff --git a/blocksuite/blocks/src/root-block/widgets/image-toolbar/index.ts b/blocksuite/blocks/src/root-block/widgets/image-toolbar/index.ts
index 21388db713..e2abf26529 100644
--- a/blocksuite/blocks/src/root-block/widgets/image-toolbar/index.ts
+++ b/blocksuite/blocks/src/root-block/widgets/image-toolbar/index.ts
@@ -1,3 +1,4 @@
+import type { ImageBlockComponent } from '@blocksuite/affine-block-image';
import { HoverController } from '@blocksuite/affine-components/hover';
import type {
AdvancedMenuItem,
@@ -13,7 +14,6 @@ import { WidgetComponent } from '@blocksuite/block-std';
import { limitShift, shift } from '@floating-ui/dom';
import { html } from 'lit';
-import type { ImageBlockComponent } from '../../../image-block/image-block.js';
import { MORE_GROUPS, PRIMARY_GROUPS } from './config.js';
import { ImageToolbarContext } from './context.js';
diff --git a/blocksuite/blocks/src/root-block/widgets/image-toolbar/utils.ts b/blocksuite/blocks/src/root-block/widgets/image-toolbar/utils.ts
index cd045df926..e7810c9895 100644
--- a/blocksuite/blocks/src/root-block/widgets/image-toolbar/utils.ts
+++ b/blocksuite/blocks/src/root-block/widgets/image-toolbar/utils.ts
@@ -1,11 +1,10 @@
+import type { ImageBlockComponent } from '@blocksuite/affine-block-image';
import {
getBlockProps,
isInsidePageEditor,
} from '@blocksuite/affine-shared/utils';
import { assertExists } from '@blocksuite/global/utils';
-import type { ImageBlockComponent } from '../../../image-block/image-block.js';
-
export function duplicate(
block: ImageBlockComponent,
abortController?: AbortController
diff --git a/blocksuite/blocks/tsconfig.json b/blocksuite/blocks/tsconfig.json
index e489f45465..b609c33394 100644
--- a/blocksuite/blocks/tsconfig.json
+++ b/blocksuite/blocks/tsconfig.json
@@ -43,6 +43,9 @@
{
"path": "../affine/block-attachment"
},
+ {
+ "path": "../affine/block-image"
+ },
{
"path": "../affine/data-view"
},
diff --git a/tools/utils/src/workspace.gen.ts b/tools/utils/src/workspace.gen.ts
index 7664bf16bd..cfbf654437 100644
--- a/tools/utils/src/workspace.gen.ts
+++ b/tools/utils/src/workspace.gen.ts
@@ -55,6 +55,19 @@ export const PackageList = [
'blocksuite/framework/store',
],
},
+ {
+ location: 'blocksuite/affine/block-image',
+ name: '@blocksuite/affine-block-image',
+ workspaceDependencies: [
+ 'blocksuite/affine/components',
+ 'blocksuite/affine/model',
+ 'blocksuite/affine/shared',
+ 'blocksuite/framework/block-std',
+ 'blocksuite/framework/global',
+ 'blocksuite/framework/inline',
+ 'blocksuite/framework/store',
+ ],
+ },
{
location: 'blocksuite/affine/block-list',
name: '@blocksuite/affine-block-list',
@@ -155,6 +168,7 @@ export const PackageList = [
'blocksuite/affine/block-attachment',
'blocksuite/affine/block-bookmark',
'blocksuite/affine/block-embed',
+ 'blocksuite/affine/block-image',
'blocksuite/affine/block-list',
'blocksuite/affine/block-paragraph',
'blocksuite/affine/block-surface',
@@ -490,6 +504,7 @@ export type PackageName =
| '@blocksuite/affine-block-attachment'
| '@blocksuite/affine-block-bookmark'
| '@blocksuite/affine-block-embed'
+ | '@blocksuite/affine-block-image'
| '@blocksuite/affine-block-list'
| '@blocksuite/affine-block-paragraph'
| '@blocksuite/affine-block-surface'
diff --git a/tsconfig.project.json b/tsconfig.project.json
index 8e05cf14c5..c5c59a86e2 100644
--- a/tsconfig.project.json
+++ b/tsconfig.project.json
@@ -10,6 +10,7 @@
{ "path": "./blocksuite/affine/block-attachment" },
{ "path": "./blocksuite/affine/block-bookmark" },
{ "path": "./blocksuite/affine/block-embed" },
+ { "path": "./blocksuite/affine/block-image" },
{ "path": "./blocksuite/affine/block-list" },
{ "path": "./blocksuite/affine/block-paragraph" },
{ "path": "./blocksuite/affine/block-surface" },
diff --git a/yarn.lock b/yarn.lock
index 7168e59d4b..aab3de00e9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3294,6 +3294,29 @@ __metadata:
languageName: unknown
linkType: soft
+"@blocksuite/affine-block-image@workspace:*, @blocksuite/affine-block-image@workspace:blocksuite/affine/block-image":
+ version: 0.0.0-use.local
+ resolution: "@blocksuite/affine-block-image@workspace:blocksuite/affine/block-image"
+ dependencies:
+ "@blocksuite/affine-components": "workspace:*"
+ "@blocksuite/affine-model": "workspace:*"
+ "@blocksuite/affine-shared": "workspace:*"
+ "@blocksuite/block-std": "workspace:*"
+ "@blocksuite/global": "workspace:*"
+ "@blocksuite/icons": "npm:^2.1.75"
+ "@blocksuite/inline": "workspace:*"
+ "@blocksuite/store": "workspace:*"
+ "@floating-ui/dom": "npm:^1.6.10"
+ "@lit/context": "npm:^1.1.2"
+ "@preact/signals-core": "npm:^1.8.0"
+ "@toeverything/theme": "npm:^1.1.1"
+ file-type: "npm:^19.5.0"
+ lit: "npm:^3.2.0"
+ minimatch: "npm:^10.0.1"
+ zod: "npm:^3.23.8"
+ languageName: unknown
+ linkType: soft
+
"@blocksuite/affine-block-list@workspace:*, @blocksuite/affine-block-list@workspace:blocksuite/affine/block-list":
version: 0.0.0-use.local
resolution: "@blocksuite/affine-block-list@workspace:blocksuite/affine/block-list"
@@ -3485,6 +3508,7 @@ __metadata:
"@blocksuite/affine-block-attachment": "workspace:*"
"@blocksuite/affine-block-bookmark": "workspace:*"
"@blocksuite/affine-block-embed": "workspace:*"
+ "@blocksuite/affine-block-image": "workspace:*"
"@blocksuite/affine-block-list": "workspace:*"
"@blocksuite/affine-block-paragraph": "workspace:*"
"@blocksuite/affine-block-surface": "workspace:*"