diff --git a/blocksuite/affine/blocks/bookmark/src/configs/slash-menu.ts b/blocksuite/affine/blocks/bookmark/src/configs/slash-menu.ts
index d1ba6c17fa..cf43bab94e 100644
--- a/blocksuite/affine/blocks/bookmark/src/configs/slash-menu.ts
+++ b/blocksuite/affine/blocks/bookmark/src/configs/slash-menu.ts
@@ -1,3 +1,4 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import { BookmarkBlockSchema } from '@blocksuite/affine-model';
import {
@@ -5,6 +6,7 @@ import {
SlashMenuConfigIdentifier,
} from '@blocksuite/affine-widget-slash-menu';
import { LinkIcon } from '@blocksuite/icons/lit';
+import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import type { ExtensionType } from '@blocksuite/store';
import { LinkTooltip } from './tooltips';
@@ -33,7 +35,13 @@ const bookmarkSlashMenuConfig: SlashMenuConfig = {
host,
'Links',
'The added link will be displayed as a card view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
)
.then(() => {
if (model.text?.length === 0) {
diff --git a/blocksuite/affine/blocks/embed/package.json b/blocksuite/affine/blocks/embed/package.json
index a2e1b5551e..b5e9f74f7e 100644
--- a/blocksuite/affine/blocks/embed/package.json
+++ b/blocksuite/affine/blocks/embed/package.json
@@ -13,6 +13,7 @@
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
+ "@blocksuite/affine-gfx-pointer": "workspace:*",
"@blocksuite/affine-inline-reference": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-rich-text": "workspace:*",
diff --git a/blocksuite/affine/blocks/embed/src/common/insert-embed-card.ts b/blocksuite/affine/blocks/embed/src/common/insert-embed-card.ts
index 74e746c2b6..9636563902 100644
--- a/blocksuite/affine/blocks/embed/src/common/insert-embed-card.ts
+++ b/blocksuite/affine/blocks/embed/src/common/insert-embed-card.ts
@@ -1,4 +1,5 @@
import {
+ DefaultTool,
EdgelessCRUDIdentifier,
SurfaceBlockComponent,
} from '@blocksuite/affine-block-surface';
@@ -82,10 +83,7 @@ export function insertEmbedCard(
surfaceBlock.model
);
- gfx.tool.setTool(
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- 'default'
- );
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [cardId],
editing: false,
diff --git a/blocksuite/affine/blocks/embed/src/embed-figma-block/configs/slash-menu.ts b/blocksuite/affine/blocks/embed/src/embed-figma-block/configs/slash-menu.ts
index c974c64d39..85793d74ad 100644
--- a/blocksuite/affine/blocks/embed/src/embed-figma-block/configs/slash-menu.ts
+++ b/blocksuite/affine/blocks/embed/src/embed-figma-block/configs/slash-menu.ts
@@ -1,6 +1,8 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import type { SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { FigmaDuotoneIcon } from '@blocksuite/icons/lit';
+import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { FigmaTooltip } from './tooltips';
@@ -29,7 +31,13 @@ export const embedFigmaSlashMenuConfig: SlashMenuConfig = {
host,
'Figma',
'The added Figma link will be displayed as an embed view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
);
if (model.text?.length === 0) std.store.deleteBlock(model);
})().catch(console.error);
diff --git a/blocksuite/affine/blocks/embed/src/embed-github-block/configs/slash-menu.ts b/blocksuite/affine/blocks/embed/src/embed-github-block/configs/slash-menu.ts
index 356d93ab81..4a553f0e27 100644
--- a/blocksuite/affine/blocks/embed/src/embed-github-block/configs/slash-menu.ts
+++ b/blocksuite/affine/blocks/embed/src/embed-github-block/configs/slash-menu.ts
@@ -1,6 +1,8 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import type { SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { GithubDuotoneIcon } from '@blocksuite/icons/lit';
+import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { GithubRepoTooltip } from './tooltips';
@@ -29,7 +31,13 @@ export const embedGithubSlashMenuConfig: SlashMenuConfig = {
host,
'GitHub',
'The added GitHub issue or pull request link will be displayed as a card view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
);
if (model.text?.length === 0) std.store.deleteBlock(model);
})().catch(console.error);
diff --git a/blocksuite/affine/blocks/embed/src/embed-iframe-block/commands/insert-embed-iframe-with-url.ts b/blocksuite/affine/blocks/embed/src/embed-iframe-block/commands/insert-embed-iframe-with-url.ts
index 20ab737ce9..fe514b032a 100644
--- a/blocksuite/affine/blocks/embed/src/embed-iframe-block/commands/insert-embed-iframe-with-url.ts
+++ b/blocksuite/affine/blocks/embed/src/embed-iframe-block/commands/insert-embed-iframe-with-url.ts
@@ -1,4 +1,5 @@
import {
+ DefaultTool,
EdgelessCRUDIdentifier,
SurfaceBlockComponent,
} from '@blocksuite/affine-block-surface';
@@ -90,10 +91,7 @@ export const insertEmbedIframeWithUrlCommand: Command<
surfaceBlock.model
);
- gfx.tool.setTool(
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- 'default'
- );
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [newBlockId],
diff --git a/blocksuite/affine/blocks/embed/src/embed-loom-block/configs/slash-menu.ts b/blocksuite/affine/blocks/embed/src/embed-loom-block/configs/slash-menu.ts
index fe63cca223..11b0765f7e 100644
--- a/blocksuite/affine/blocks/embed/src/embed-loom-block/configs/slash-menu.ts
+++ b/blocksuite/affine/blocks/embed/src/embed-loom-block/configs/slash-menu.ts
@@ -1,6 +1,8 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import type { SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { LoomLogoDuotoneIcon } from '@blocksuite/icons/lit';
+import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { LoomTooltip } from './tooltips';
@@ -29,7 +31,13 @@ export const embedLoomSlashMenuConfig: SlashMenuConfig = {
host,
'Loom',
'The added Loom video link will be displayed as an embed view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
);
if (model.text?.length === 0) std.store.deleteBlock(model);
})().catch(console.error);
diff --git a/blocksuite/affine/blocks/embed/src/embed-youtube-block/configs/slash-menu.ts b/blocksuite/affine/blocks/embed/src/embed-youtube-block/configs/slash-menu.ts
index 6e343e7bdf..fa9f9721e6 100644
--- a/blocksuite/affine/blocks/embed/src/embed-youtube-block/configs/slash-menu.ts
+++ b/blocksuite/affine/blocks/embed/src/embed-youtube-block/configs/slash-menu.ts
@@ -1,6 +1,8 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import type { SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { YoutubeDuotoneIcon } from '@blocksuite/icons/lit';
+import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { YoutubeVideoTooltip } from './tooltips';
@@ -29,7 +31,13 @@ export const embedYoutubeSlashMenuConfig: SlashMenuConfig = {
host,
'YouTube',
'The added YouTube video link will be displayed as an embed view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
);
if (model.text?.length === 0) std.store.deleteBlock(model);
})().catch(console.error);
diff --git a/blocksuite/affine/blocks/embed/tsconfig.json b/blocksuite/affine/blocks/embed/tsconfig.json
index 7c75ad9971..e387f0a9c6 100644
--- a/blocksuite/affine/blocks/embed/tsconfig.json
+++ b/blocksuite/affine/blocks/embed/tsconfig.json
@@ -10,6 +10,7 @@
{ "path": "../surface" },
{ "path": "../../components" },
{ "path": "../../ext-loader" },
+ { "path": "../../gfx/pointer" },
{ "path": "../../inlines/reference" },
{ "path": "../../model" },
{ "path": "../../rich-text" },
diff --git a/blocksuite/affine/blocks/frame/package.json b/blocksuite/affine/blocks/frame/package.json
index 928968dec1..a801fb0516 100644
--- a/blocksuite/affine/blocks/frame/package.json
+++ b/blocksuite/affine/blocks/frame/package.json
@@ -13,6 +13,7 @@
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
+ "@blocksuite/affine-gfx-pointer": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-shared": "workspace:*",
"@blocksuite/affine-widget-edgeless-toolbar": "workspace:*",
diff --git a/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-dense-menu.ts b/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-dense-menu.ts
index 7850008864..469b449b81 100644
--- a/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-dense-menu.ts
+++ b/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-dense-menu.ts
@@ -1,29 +1,30 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { menu } from '@blocksuite/affine-components/context-menu';
import type { DenseMenuBuilder } from '@blocksuite/affine-widget-edgeless-toolbar';
import { FrameIcon } from '@blocksuite/icons/lit';
import { EdgelessFrameManagerIdentifier } from '../frame-manager.js';
+import { FrameTool } from '../frame-tool';
import { FrameConfig } from './config.js';
export const buildFrameDenseMenu: DenseMenuBuilder = (edgeless, gfx) =>
menu.subMenu({
name: 'Frame',
prefix: FrameIcon({ width: '20px', height: '20px' }),
- select: () => gfx.tool.setTool({ type: 'frame' }),
+ select: () => gfx.tool.setTool(FrameTool),
isSelected: gfx.tool.currentToolName$.peek() === 'frame',
options: {
items: [
menu.action({
name: 'Custom',
- select: () => gfx.tool.setTool({ type: 'frame' }),
+ select: () => gfx.tool.setTool(FrameTool),
}),
...FrameConfig.map(config =>
menu.action({
name: `Slide ${config.name}`,
select: () => {
const frame = edgeless.std.get(EdgelessFrameManagerIdentifier);
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
frame.createFrameOnViewportCenter(config.wh);
},
})
diff --git a/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-menu.ts b/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-menu.ts
index 8655a7b680..bd82e052b6 100644
--- a/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-menu.ts
+++ b/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-menu.ts
@@ -1,9 +1,10 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import { css, html, LitElement } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
import { EdgelessFrameManagerIdentifier } from '../frame-manager.js';
+import { FrameTool } from '../frame-tool';
import { FrameConfig } from './config.js';
export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
@@ -65,7 +66,7 @@ export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
}
`;
- override type: GfxToolsFullOptionValue['type'] = 'frame';
+ override type = FrameTool;
get frameManager() {
return this.edgeless.std.get(EdgelessFrameManagerIdentifier);
@@ -84,8 +85,7 @@ export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
(item, index) => html`
{
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
frameManager.createFrameOnViewportCenter(item.wh);
}}
class="frame-add-button ${index}"
diff --git a/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-tool-button.ts b/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-tool-button.ts
index c6a3763010..9788d5eb03 100644
--- a/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-tool-button.ts
+++ b/blocksuite/affine/blocks/frame/src/edgeless-toolbar/frame-tool-button.ts
@@ -1,8 +1,9 @@
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { FrameIcon } from '@blocksuite/icons/lit';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import { css, html, LitElement } from 'lit';
+import { FrameTool } from '../frame-tool';
+
export class EdgelessFrameToolButton extends QuickToolMixin(LitElement) {
static override styles = css`
:host {
@@ -10,7 +11,7 @@ export class EdgelessFrameToolButton extends QuickToolMixin(LitElement) {
}
`;
- override type: GfxToolsFullOptionValue['type'] = 'frame';
+ override type = FrameTool;
private _toggleFrameMenu() {
if (this.tryDisposePopper()) return;
@@ -20,7 +21,7 @@ export class EdgelessFrameToolButton extends QuickToolMixin(LitElement) {
}
override render() {
- const type = this.edgelessTool?.type;
+ const type = this.edgelessTool?.toolType?.toolName;
return html`
{
// don't update tool before toggling menu
this._toggleFrameMenu();
- this.setEdgelessTool({ type: 'frame' });
+ this.setEdgelessTool(FrameTool);
}}
>
${FrameIcon()}
diff --git a/blocksuite/affine/blocks/frame/src/edgeless-toolbar/presentation-toolbar.ts b/blocksuite/affine/blocks/frame/src/edgeless-toolbar/presentation-toolbar.ts
index bc796525c6..970bfdb1d4 100644
--- a/blocksuite/affine/blocks/frame/src/edgeless-toolbar/presentation-toolbar.ts
+++ b/blocksuite/affine/blocks/frame/src/edgeless-toolbar/presentation-toolbar.ts
@@ -1,5 +1,9 @@
-import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
+import {
+ DefaultTool,
+ EdgelessLegacySlotIdentifier,
+} from '@blocksuite/affine-block-surface';
import { toast } from '@blocksuite/affine-components/toast';
+import { PanTool } from '@blocksuite/affine-gfx-pointer';
import type { FrameBlockModel } from '@blocksuite/affine-model';
import {
EditPropsStore,
@@ -16,7 +20,7 @@ import {
StopAiIcon,
} from '@blocksuite/icons/lit';
import type { BlockComponent } from '@blocksuite/std';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
+import type { ToolOptions } from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { cssVar } from '@toeverything/theme';
import { css, html, LitElement, nothing, type PropertyValues } from 'lit';
@@ -27,6 +31,7 @@ import {
isFrameBlock,
type NavigatorMode,
} from '../frame-manager';
+import { PresentTool } from '../present-tool';
export class PresentationToolbar extends EdgelessToolbarToolMixin(
SignalWatcher(LitElement)
@@ -114,7 +119,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
private _timer?: ReturnType;
- override type: GfxToolsFullOptionValue['type'] = 'frameNavigator';
+ override type = PresentTool;
private get _cachedPresentHideToolbar() {
return !!this.edgeless.std
@@ -151,7 +156,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
private _bindHotKey() {
const handleKeyIfFrameNavigator = (action: () => void) => () => {
- if (this.edgelessTool.type === 'frameNavigator') {
+ if (this.edgelessTool.toolType === PresentTool) {
action();
}
};
@@ -171,11 +176,11 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
private _exitPresentation() {
// When exit presentation mode, we need to set the tool to default or pan
// And exit fullscreen
- const tool = this.edgeless.doc.readonly
- ? { type: 'pan', panning: false }
- : { type: 'default' };
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool(tool);
+ if (this.edgeless.doc.readonly) {
+ this.setEdgelessTool(PanTool, { panning: false });
+ } else {
+ this.setEdgelessTool(DefaultTool);
+ }
if (document.fullscreenElement) {
document.exitFullscreen().catch(console.error);
@@ -261,9 +266,11 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
const currentTool = this.gfx.tool.currentToolOption$.value;
const selection = this.gfx.selection;
- if (currentTool?.type === 'frameNavigator') {
+ if (currentTool?.toolType === PresentTool) {
this._cachedIndex = this._currentFrameIndex;
- this._navigatorMode = currentTool.mode ?? this._navigatorMode;
+ this._navigatorMode =
+ (currentTool.options as ToolOptions)?.mode ??
+ this._navigatorMode;
if (isFrameBlock(selection.selectedElements[0])) {
this._cachedIndex = this._frames.findIndex(
frame => frame.id === selection.selectedElements[0].id
@@ -306,14 +313,14 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
// When exit fullscreen, we need to clear the timer
clearTimeout(this._timer);
if (
- this.edgelessTool.type === 'frameNavigator' &&
+ this.edgelessTool.toolType === PresentTool &&
this._fullScreenMode
) {
- const tool = this.edgeless.doc.readonly
- ? { type: 'pan', panning: false }
- : { type: 'default' };
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool(tool);
+ if (this.edgeless.doc.readonly) {
+ this.setEdgelessTool(PanTool, { panning: false });
+ } else {
+ this.setEdgelessTool(DefaultTool);
+ }
}
}
@@ -425,7 +432,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
protected override updated(changedProperties: PropertyValues) {
if (
changedProperties.has('_currentFrameIndex') &&
- this.edgelessTool.type === 'frameNavigator'
+ this.edgelessTool.toolType === PresentTool
) {
this._moveToCurrentFrame();
}
diff --git a/blocksuite/affine/blocks/frame/src/frame-tool.ts b/blocksuite/affine/blocks/frame/src/frame-tool.ts
index 18dcdd894a..1fa186371b 100644
--- a/blocksuite/affine/blocks/frame/src/frame-tool.ts
+++ b/blocksuite/affine/blocks/frame/src/frame-tool.ts
@@ -1,4 +1,7 @@
-import { OverlayIdentifier } from '@blocksuite/affine-block-surface';
+import {
+ DefaultTool,
+ OverlayIdentifier,
+} from '@blocksuite/affine-block-surface';
import type { FrameBlockModel } from '@blocksuite/affine-model';
import {
EditPropsStore,
@@ -39,8 +42,7 @@ export class FrameTool extends BaseTool {
if (this._frame) {
const frame = this._frame;
frame.pop('xywh');
- // @ts-expect-error TODO: refactor gfx tool
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
this.gfx.selection.set({
elements: [frame.id],
editing: false,
diff --git a/blocksuite/affine/blocks/frame/src/index.ts b/blocksuite/affine/blocks/frame/src/index.ts
index 9cc10f0588..09033ca996 100644
--- a/blocksuite/affine/blocks/frame/src/index.ts
+++ b/blocksuite/affine/blocks/frame/src/index.ts
@@ -1,5 +1,5 @@
import type { FrameTool } from './frame-tool';
-import type { PresentTool, PresentToolOption } from './preset-tool';
+import type { PresentTool, PresentToolOption } from './present-tool';
export * from './edgeless-clipboard-config';
export * from './edgeless-toolbar';
@@ -9,7 +9,7 @@ export * from './frame-manager';
export * from './frame-spec';
export * from './frame-tool';
export * from './frame-toolbar';
-export * from './preset-tool';
+export * from './present-tool';
declare module '@blocksuite/std/gfx' {
interface GfxToolsMap {
diff --git a/blocksuite/affine/blocks/frame/src/preset-tool.ts b/blocksuite/affine/blocks/frame/src/present-tool.ts
similarity index 100%
rename from blocksuite/affine/blocks/frame/src/preset-tool.ts
rename to blocksuite/affine/blocks/frame/src/present-tool.ts
diff --git a/blocksuite/affine/blocks/frame/src/present/navigator-bg-widget.ts b/blocksuite/affine/blocks/frame/src/present/navigator-bg-widget.ts
index ef52623775..ce51e72765 100644
--- a/blocksuite/affine/blocks/frame/src/present/navigator-bg-widget.ts
+++ b/blocksuite/affine/blocks/frame/src/present/navigator-bg-widget.ts
@@ -9,6 +9,8 @@ import { css, html, nothing } from 'lit';
import { state } from 'lit/decorators.js';
import { literal, unsafeStatic } from 'lit/static-html.js';
+import { PresentTool } from '../present-tool';
+
export const EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET =
'edgeless-navigator-black-background';
export class EdgelessNavigatorBlackBackgroundWidget extends WidgetComponent {
@@ -59,7 +61,7 @@ export class EdgelessNavigatorBlackBackgroundWidget extends WidgetComponent {
- this.setEdgelessTool({
- type: 'frameNavigator',
- });
+ this.setEdgelessTool(PresentTool);
}}
>
${PresentationIcon()}
diff --git a/blocksuite/affine/blocks/frame/src/view.ts b/blocksuite/affine/blocks/frame/src/view.ts
index 88649efa1e..28d029493b 100644
--- a/blocksuite/affine/blocks/frame/src/view.ts
+++ b/blocksuite/affine/blocks/frame/src/view.ts
@@ -11,7 +11,7 @@ import { FrameBlockSpec } from './frame-spec';
import { FrameTool } from './frame-tool';
import { frameToolbarExtension } from './frame-toolbar';
import { edgelessNavigatorBgWidget } from './present/navigator-bg-widget';
-import { PresentTool } from './preset-tool';
+import { PresentTool } from './present-tool';
export class FrameViewExtension extends ViewExtensionProvider {
override name = 'affine-frame-block';
diff --git a/blocksuite/affine/blocks/frame/tsconfig.json b/blocksuite/affine/blocks/frame/tsconfig.json
index 4ea19d5c79..22748efb0c 100644
--- a/blocksuite/affine/blocks/frame/tsconfig.json
+++ b/blocksuite/affine/blocks/frame/tsconfig.json
@@ -10,6 +10,7 @@
{ "path": "../surface" },
{ "path": "../../components" },
{ "path": "../../ext-loader" },
+ { "path": "../../gfx/pointer" },
{ "path": "../../model" },
{ "path": "../../shared" },
{ "path": "../../widgets/edgeless-toolbar" },
diff --git a/blocksuite/affine/blocks/root/src/edgeless/clipboard/clipboard.ts b/blocksuite/affine/blocks/root/src/edgeless/clipboard/clipboard.ts
index 5213300237..1cce82fc0c 100644
--- a/blocksuite/affine/blocks/root/src/edgeless/clipboard/clipboard.ts
+++ b/blocksuite/affine/blocks/root/src/edgeless/clipboard/clipboard.ts
@@ -2,6 +2,7 @@ import { addAttachments } from '@blocksuite/affine-block-attachment';
import { EdgelessFrameManagerIdentifier } from '@blocksuite/affine-block-frame';
import { addImages } from '@blocksuite/affine-block-image';
import {
+ DefaultTool,
EdgelessCRUDIdentifier,
ExportManager,
getSurfaceComponent,
@@ -608,7 +609,7 @@ export class EdgelessClipboardController extends PageClipboard {
elements: [noteId],
editing: false,
});
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
}
copy() {
diff --git a/blocksuite/affine/blocks/root/src/edgeless/components/rects/edgeless-dragging-area-rect.ts b/blocksuite/affine/blocks/root/src/edgeless/components/rects/edgeless-dragging-area-rect.ts
index 62e5415208..38d63d050b 100644
--- a/blocksuite/affine/blocks/root/src/edgeless/components/rects/edgeless-dragging-area-rect.ts
+++ b/blocksuite/affine/blocks/root/src/edgeless/components/rects/edgeless-dragging-area-rect.ts
@@ -1,7 +1,7 @@
import {
DefaultModeDragType,
DefaultTool,
-} from '@blocksuite/affine-gfx-pointer';
+} from '@blocksuite/affine-block-surface';
import type { RootBlockModel } from '@blocksuite/affine-model';
import { WidgetComponent } from '@blocksuite/std';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
@@ -41,6 +41,7 @@ export class EdgelessDraggingAreaRectWidget extends WidgetComponent {
- this._setEdgelessTool('default');
+ this._setEdgelessTool(DefaultTool);
},
t: () => {
- this._setEdgelessTool('text');
+ this._setEdgelessTool(TextTool);
},
c: () => {
const mode = ConnectorMode.Curve;
rootComponent.std.get(EditPropsStore).recordLastProps('connector', {
mode,
});
- this._setEdgelessTool('connector', { mode });
+ this._setEdgelessTool(ConnectorTool, { mode });
},
h: () => {
- this._setEdgelessTool('pan', {
+ this._setEdgelessTool(PanTool, {
panning: false,
});
},
n: () => {
- this._setEdgelessTool('affine:note', {
+ this._setEdgelessTool(NoteTool, {
childFlavour: DEFAULT_NOTE_CHILD_FLAVOUR,
childType: DEFAULT_NOTE_CHILD_TYPE,
tip: DEFAULT_NOTE_TIP,
});
},
p: () => {
- this._setEdgelessTool('brush');
+ this._setEdgelessTool(BrushTool);
},
'Shift-p': () => {
- this._setEdgelessTool('highlighter');
+ this._setEdgelessTool(HighlighterTool);
},
e: () => {
- this._setEdgelessTool('eraser');
+ this._setEdgelessTool(EraserTool);
},
k: () => {
if (this.rootComponent.service.locked) return;
@@ -128,7 +141,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
});
rootComponent.surface.fitToViewport(Bound.deserialize(frame.xywh));
} else if (!this.rootComponent.service.selection.editing) {
- this._setEdgelessTool('frame');
+ this._setEdgelessTool(FrameTool);
}
},
'-': () => {
@@ -184,7 +197,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
}
const { shapeName } = controller.activatedOption;
const nextShapeName = getNextShapeType(shapeName);
- this._setEdgelessTool('shape', {
+ this._setEdgelessTool(ShapeTool, {
shapeName: nextShapeName,
});
@@ -633,11 +646,9 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
});
}
- private _setEdgelessTool(
- toolName: K,
- ...options: K extends keyof GfxToolsOption
- ? [option: GfxToolsOption[K], ignoreActiveState?: boolean]
- : [option: void, ignoreActiveState?: boolean]
+ private _setEdgelessTool(
+ toolType: ToolType,
+ ...options: [options?: ToolOptions, ignoreActiveState?: boolean]
) {
const ignoreActiveState =
typeof options === 'boolean'
@@ -651,9 +662,8 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
return;
}
- this.rootComponent.gfx.tool.setTool(
- toolName,
- // @ts-expect-error FIXME: ts error
+ this.rootComponent.gfx.tool.setTool(
+ toolType,
options[0] !== undefined && typeof options[0] !== 'boolean'
? options[0]
: undefined
@@ -678,8 +688,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
const revertToPrevTool = (ev: KeyboardEvent) => {
if (ev.code === 'Space') {
this._setEdgelessTool(
- // @ts-expect-error FIXME: ts error
- currentTool.toolName,
+ (currentTool as DefaultTool).constructor as typeof DefaultTool,
currentTool?.activatedOption
);
selection.set(currentSel);
@@ -694,7 +703,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
) {
return;
}
- this._setEdgelessTool('pan', { panning: false });
+ this._setEdgelessTool(PanTool, { panning: false });
edgeless.dispatcher.disposables.addFromEvent(
document,
diff --git a/blocksuite/affine/blocks/root/src/edgeless/edgeless-root-block.ts b/blocksuite/affine/blocks/root/src/edgeless/edgeless-root-block.ts
index fd438c477e..3ffc201f89 100644
--- a/blocksuite/affine/blocks/root/src/edgeless/edgeless-root-block.ts
+++ b/blocksuite/affine/blocks/root/src/edgeless/edgeless-root-block.ts
@@ -1,14 +1,14 @@
import { NoteConfigExtension } from '@blocksuite/affine-block-note';
-import type {
- SurfaceBlockComponent,
- SurfaceBlockModel,
-} from '@blocksuite/affine-block-surface';
import {
+ DefaultTool,
EdgelessLegacySlotIdentifier,
getBgGridGap,
normalizeWheelDeltaY,
+ type SurfaceBlockComponent,
+ type SurfaceBlockModel,
} from '@blocksuite/affine-block-surface';
import { isSingleMindMapNode } from '@blocksuite/affine-gfx-mindmap';
+import { PanTool } from '@blocksuite/affine-gfx-pointer';
import { mountShapeTextEditor } from '@blocksuite/affine-gfx-shape';
import {
NoteBlockModel,
@@ -468,9 +468,9 @@ export class EdgelessRootBlockComponent extends BlockComponent<
this._initPinchEvent();
if (this.doc.readonly) {
- this.gfx.tool.setTool('pan', { panning: true });
+ this.gfx.tool.setTool(PanTool, { panning: true });
} else {
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
}
this.gfx.viewport.elementReady.next(this.gfxViewportElm);
diff --git a/blocksuite/affine/blocks/root/src/edgeless/utils/query.ts b/blocksuite/affine/blocks/root/src/edgeless/utils/query.ts
index 2e3a16a7ac..599dfa59a4 100644
--- a/blocksuite/affine/blocks/root/src/edgeless/utils/query.ts
+++ b/blocksuite/affine/blocks/root/src/edgeless/utils/query.ts
@@ -1,4 +1,5 @@
import type { CanvasElementWithText } from '@blocksuite/affine-block-surface';
+import type { PanTool } from '@blocksuite/affine-gfx-pointer';
import {
type AttachmentBlockModel,
type BookmarkBlockModel,
@@ -26,7 +27,7 @@ import { Bound } from '@blocksuite/global/gfx';
import type {
GfxModel,
GfxPrimitiveElementModel,
- GfxToolsFullOptionValue,
+ ToolOptionWithType,
} from '@blocksuite/std/gfx';
import type { BlockModel } from '@blocksuite/store';
@@ -191,15 +192,17 @@ export function isConnectable(
}
// https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
-export function getCursorMode(edgelessTool: GfxToolsFullOptionValue | null) {
+export function getCursorMode(edgelessTool: ToolOptionWithType) {
if (!edgelessTool) {
return 'default';
}
- switch (edgelessTool.type) {
+ switch (edgelessTool.toolType?.toolName) {
case 'default':
return 'default';
case 'pan':
- return edgelessTool.panning ? 'grabbing' : 'grab';
+ return (edgelessTool as ToolOptionWithType).options?.panning
+ ? 'grabbing'
+ : 'grab';
case 'brush':
case 'highlighter':
return drawingCursor;
diff --git a/blocksuite/affine/blocks/surface/src/index.ts b/blocksuite/affine/blocks/surface/src/index.ts
index 76be0673bc..dc6224d110 100644
--- a/blocksuite/affine/blocks/surface/src/index.ts
+++ b/blocksuite/affine/blocks/surface/src/index.ts
@@ -32,9 +32,8 @@ export {
PageSurfaceBlockSpec,
} from './surface-spec.js';
export { SurfaceBlockTransformer } from './surface-transformer.js';
+export * from './tool/default-tool.js';
export {
- addNote,
- addNoteAtPoint,
generateElementId,
getBgGridGap,
getLastPropsKey,
diff --git a/blocksuite/affine/gfx/pointer/src/tools/default-tool.ts b/blocksuite/affine/blocks/surface/src/tool/default-tool.ts
similarity index 99%
rename from blocksuite/affine/gfx/pointer/src/tools/default-tool.ts
rename to blocksuite/affine/blocks/surface/src/tool/default-tool.ts
index ec4ea4b37b..86e711cb51 100644
--- a/blocksuite/affine/gfx/pointer/src/tools/default-tool.ts
+++ b/blocksuite/affine/blocks/surface/src/tool/default-tool.ts
@@ -10,7 +10,7 @@ import {
} from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
-import { calPanDelta } from '../utils/panning-utils.js';
+import { calPanDelta } from './panning-utils.js';
export enum DefaultModeDragType {
/** Moving selected contents */
diff --git a/blocksuite/affine/gfx/pointer/src/utils/panning-utils.ts b/blocksuite/affine/blocks/surface/src/tool/panning-utils.ts
similarity index 100%
rename from blocksuite/affine/gfx/pointer/src/utils/panning-utils.ts
rename to blocksuite/affine/blocks/surface/src/tool/panning-utils.ts
diff --git a/blocksuite/affine/blocks/surface/src/utils/add-note.ts b/blocksuite/affine/blocks/surface/src/utils/add-note.ts
deleted file mode 100644
index 70bb9238ab..0000000000
--- a/blocksuite/affine/blocks/surface/src/utils/add-note.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import {
- DEFAULT_NOTE_HEIGHT,
- DEFAULT_NOTE_WIDTH,
- NOTE_MIN_HEIGHT,
- type NoteBlockModel,
- NoteDisplayMode,
-} from '@blocksuite/affine-model';
-import { focusTextModel } from '@blocksuite/affine-rich-text';
-import { TelemetryProvider } from '@blocksuite/affine-shared/services';
-import type { NoteChildrenFlavour } from '@blocksuite/affine-shared/types';
-import { handleNativeRangeAtPoint } from '@blocksuite/affine-shared/utils';
-import { type IPoint, type Point, serializeXYWH } from '@blocksuite/global/gfx';
-import type { BlockStdScope } from '@blocksuite/std';
-import {
- type GfxBlockElementModel,
- GfxControllerIdentifier,
-} from '@blocksuite/std/gfx';
-
-import { DEFAULT_NOTE_OFFSET_X, DEFAULT_NOTE_OFFSET_Y } from '../consts';
-import { EdgelessCRUDIdentifier } from '../extensions/crud-extension';
-
-export function addNoteAtPoint(
- std: BlockStdScope,
- /**
- * The point is in browser coordinate
- */
- point: IPoint,
- options: {
- width?: number;
- height?: number;
- parentId?: string;
- noteIndex?: number;
- offsetX?: number;
- offsetY?: number;
- scale?: number;
- } = {}
-) {
- const gfx = std.get(GfxControllerIdentifier);
- const crud = std.get(EdgelessCRUDIdentifier);
- const {
- width = DEFAULT_NOTE_WIDTH,
- height = DEFAULT_NOTE_HEIGHT,
- offsetX = DEFAULT_NOTE_OFFSET_X,
- offsetY = DEFAULT_NOTE_OFFSET_Y,
- parentId = gfx.doc.root?.id,
- noteIndex,
- scale = 1,
- } = options;
- const [x, y] = gfx.viewport.toModelCoord(point.x, point.y);
- const blockId = crud.addBlock(
- 'affine:note',
- {
- xywh: serializeXYWH(
- x - offsetX * scale,
- y - offsetY * scale,
- width,
- height
- ),
- displayMode: NoteDisplayMode.EdgelessOnly,
- },
- parentId,
- noteIndex
- );
-
- std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
- control: 'canvas:draw',
- page: 'whiteboard editor',
- module: 'toolbar',
- segment: 'toolbar',
- type: 'note',
- });
-
- return blockId;
-}
-
-type NoteOptions = {
- childFlavour: NoteChildrenFlavour;
- childType: string | null;
- collapse: boolean;
-};
-
-export function addNote(
- std: BlockStdScope,
- point: Point,
- options: NoteOptions,
- width = DEFAULT_NOTE_WIDTH,
- height = DEFAULT_NOTE_HEIGHT
-) {
- const noteId = addNoteAtPoint(std, point, {
- width,
- height,
- });
-
- const gfx = std.get(GfxControllerIdentifier);
- const doc = std.store;
-
- const blockId = doc.addBlock(
- options.childFlavour,
- { type: options.childType },
- noteId
- );
- if (options.collapse && height > NOTE_MIN_HEIGHT) {
- const note = doc.getModelById(noteId) as NoteBlockModel;
- doc.updateBlock(note, () => {
- note.props.edgeless.collapse = true;
- note.props.edgeless.collapsedHeight = height;
- });
- }
- gfx.tool.setTool(
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- 'default'
- );
-
- // Wait for edgelessTool updated
- requestAnimationFrame(() => {
- const blocks =
- (doc.root?.children.filter(
- child => child.flavour === 'affine:note'
- ) as GfxBlockElementModel[]) ?? [];
- const element = blocks.find(b => b.id === noteId);
- if (element) {
- gfx.selection.set({
- elements: [element.id],
- editing: true,
- });
-
- // Waiting dom updated, `note mask` is removed
- if (blockId) {
- focusTextModel(gfx.std, blockId);
- } else {
- // Cannot reuse `handleNativeRangeClick` directly here,
- // since `retargetClick` will re-target to pervious editor
- handleNativeRangeAtPoint(point.x, point.y);
- }
- }
- });
-}
diff --git a/blocksuite/affine/blocks/surface/src/utils/index.ts b/blocksuite/affine/blocks/surface/src/utils/index.ts
index 319d0122af..103dd3ad23 100644
--- a/blocksuite/affine/blocks/surface/src/utils/index.ts
+++ b/blocksuite/affine/blocks/surface/src/utils/index.ts
@@ -33,7 +33,6 @@ export function normalizeWheelDeltaY(delta: number, zoom = 1) {
return newZoom;
}
-export { addNote, addNoteAtPoint } from './add-note';
export { getBgGridGap } from './get-bg-grip-gap';
export { getLastPropsKey } from './get-last-props-key';
export * from './get-surface-block';
diff --git a/blocksuite/affine/blocks/surface/src/view.ts b/blocksuite/affine/blocks/surface/src/view.ts
index 624b973206..02c5cddfed 100644
--- a/blocksuite/affine/blocks/surface/src/view.ts
+++ b/blocksuite/affine/blocks/surface/src/view.ts
@@ -12,6 +12,7 @@ import {
EditPropsMiddlewareBuilder,
} from './extensions';
import { ExportManagerExtension } from './extensions/export-manager/export-manager';
+import { DefaultTool } from './tool/default-tool';
export class SurfaceViewExtension extends ViewExtensionProvider {
override name = 'affine-surface-block';
@@ -30,6 +31,7 @@ export class SurfaceViewExtension extends ViewExtensionProvider {
ExportManagerExtension,
]);
if (this.isEdgeless(context.scope)) {
+ context.register(DefaultTool);
context.register(
BlockViewExtension('affine:surface', literal`affine-surface`)
);
diff --git a/blocksuite/affine/components/src/embed-card-modal/embed-card-create-modal.ts b/blocksuite/affine/components/src/embed-card-modal/embed-card-create-modal.ts
index 63ab1c0d12..6ec4d48bd3 100644
--- a/blocksuite/affine/components/src/embed-card-modal/embed-card-create-modal.ts
+++ b/blocksuite/affine/components/src/embed-card-modal/embed-card-create-modal.ts
@@ -62,13 +62,8 @@ export class EmbedCardCreateModal extends SignalWatcher(
}
this.createOptions.onSave(url);
-
- gfx.tool.setTool(
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- 'default'
- );
}
- this.onConfirm();
+ this.onConfirm({ mode });
this.remove();
};
@@ -176,7 +171,7 @@ export class EmbedCardCreateModal extends SignalWatcher(
accessor input!: HTMLInputElement;
@property({ attribute: false })
- accessor onConfirm!: () => void;
+ accessor onConfirm!: (options: { mode: 'edgeless' | 'page' }) => void;
@property({ attribute: false })
accessor titleText!: string;
@@ -195,7 +190,8 @@ export async function toggleEmbedCardCreateModal(
| {
mode: 'edgeless';
onSave: (url: string) => void;
- }
+ },
+ onConfirm: (options: { mode: 'page' | 'edgeless' }) => void
): Promise {
host.selection.clear();
@@ -208,7 +204,10 @@ export async function toggleEmbedCardCreateModal(
document.body.append(embedCardCreateModal);
return new Promise(resolve => {
- embedCardCreateModal.onConfirm = () => resolve();
+ embedCardCreateModal.onConfirm = options => {
+ onConfirm(options);
+ resolve();
+ };
});
}
diff --git a/blocksuite/affine/fragments/frame-panel/src/header/frame-panel-header.ts b/blocksuite/affine/fragments/frame-panel/src/header/frame-panel-header.ts
index eff64e8c91..acdd7eb855 100644
--- a/blocksuite/affine/fragments/frame-panel/src/header/frame-panel-header.ts
+++ b/blocksuite/affine/fragments/frame-panel/src/header/frame-panel-header.ts
@@ -1,4 +1,7 @@
-import type { NavigatorMode } from '@blocksuite/affine-block-frame';
+import {
+ type NavigatorMode,
+ PresentTool,
+} from '@blocksuite/affine-block-frame';
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
import {
DocModeProvider,
@@ -124,8 +127,7 @@ export class FramePanelHeader extends WithDisposable(LitElement) {
}
setTimeout(() => {
- this._gfx.tool.setTool({
- type: 'frameNavigator',
+ this._gfx.tool.setTool(PresentTool, {
mode: this._navigatorMode,
});
}, 100);
diff --git a/blocksuite/affine/gfx/brush/src/toolbar/components/eraser/eraser-tool-button.ts b/blocksuite/affine/gfx/brush/src/toolbar/components/eraser/eraser-tool-button.ts
index 2b08d35a60..89e06dad07 100644
--- a/blocksuite/affine/gfx/brush/src/toolbar/components/eraser/eraser-tool-button.ts
+++ b/blocksuite/affine/gfx/brush/src/toolbar/components/eraser/eraser-tool-button.ts
@@ -1,8 +1,9 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import { css, html, LitElement } from 'lit';
+import { EraserTool } from '../../../eraser-tool';
import { EdgelessEraserDarkIcon, EdgelessEraserLightIcon } from './icons.js';
export class EdgelessEraserToolButton extends EdgelessToolbarToolMixin(
@@ -33,16 +34,15 @@ export class EdgelessEraserToolButton extends EdgelessToolbarToolMixin(
override enableActiveBackground = true;
- override type: GfxToolsFullOptionValue['type'] = 'eraser';
+ override type = EraserTool;
override firstUpdated() {
this.disposables.add(
this.edgeless.bindHotKey(
{
Escape: () => {
- if (this.edgelessTool.type === 'eraser') {
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool({ type: 'default' });
+ if (this.edgelessTool.toolType === EraserTool) {
+ this.setEdgelessTool(DefaultTool);
}
},
},
@@ -52,7 +52,7 @@ export class EdgelessEraserToolButton extends EdgelessToolbarToolMixin(
}
override render() {
- const type = this.edgelessTool?.type;
+ const type = this.edgelessTool?.toolType;
const appTheme = this.edgeless.std.get(ThemeProvider).app$.value;
const icon =
appTheme === 'dark' ? EdgelessEraserDarkIcon : EdgelessEraserLightIcon;
@@ -65,8 +65,8 @@ export class EdgelessEraserToolButton extends EdgelessToolbarToolMixin(
data-shortcut="${'E'}"
>`}
.tooltipOffset=${4}
- .active=${type === 'eraser'}
- @click=${() => this.setEdgelessTool({ type: 'eraser' })}
+ .active=${type === EraserTool}
+ @click=${() => this.setEdgelessTool(EraserTool)}
>
${icon}
diff --git a/blocksuite/affine/gfx/brush/src/toolbar/components/pen/pen-menu.ts b/blocksuite/affine/gfx/brush/src/toolbar/components/pen/pen-menu.ts
index 92c5e74706..37d5833766 100644
--- a/blocksuite/affine/gfx/brush/src/toolbar/components/pen/pen-menu.ts
+++ b/blocksuite/affine/gfx/brush/src/toolbar/components/pen/pen-menu.ts
@@ -16,6 +16,8 @@ import { css, html, LitElement, type TemplateResult } from 'lit';
import { property } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
+import { BrushTool } from '../../../brush-tool';
+import { HighlighterTool } from '../../../highlighter-tool';
import { penInfoMap } from './consts';
import type { Pen, PenMap } from './types';
@@ -80,7 +82,11 @@ export class EdgelessPenMenu extends EdgelessToolbarToolMixin(
private readonly _onPickPen = (tool: Pen) => {
this.pen$.value = tool;
- this.setEdgelessTool(tool);
+ if (tool === 'brush') {
+ this.setEdgelessTool(BrushTool);
+ } else {
+ this.setEdgelessTool(HighlighterTool);
+ }
};
private readonly _onPickColor = (e: ColorEvent) => {
@@ -91,7 +97,7 @@ export class EdgelessPenMenu extends EdgelessToolbarToolMixin(
this.onChange({ color });
};
- override type: Pen[] = ['brush', 'highlighter'];
+ override type = [BrushTool, HighlighterTool];
override render() {
const {
diff --git a/blocksuite/affine/gfx/brush/src/toolbar/components/pen/pen-tool-button.ts b/blocksuite/affine/gfx/brush/src/toolbar/components/pen/pen-tool-button.ts
index 6f3c0b77ff..9651d512bb 100644
--- a/blocksuite/affine/gfx/brush/src/toolbar/components/pen/pen-tool-button.ts
+++ b/blocksuite/affine/gfx/brush/src/toolbar/components/pen/pen-tool-button.ts
@@ -10,6 +10,8 @@ import { css, html, LitElement, nothing } from 'lit';
import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
+import { BrushTool } from '../../../brush-tool';
+import { HighlighterTool } from '../../../highlighter-tool';
import { penIconMap, penInfoMap } from './consts';
import type { Pen } from './types';
@@ -97,19 +99,19 @@ export class EdgelessPenToolButton extends EdgelessToolbarToolMixin(
override enableActiveBackground = true;
- override type: Pen[] = ['brush', 'highlighter'];
+ override type = [BrushTool, HighlighterTool];
override firstUpdated() {
this.disposables.add(
this.gfx.tool.currentToolName$.subscribe(name => {
- const tool = this.type.find(t => t === name);
+ const tool = this.type.find(t => t.toolName === name);
if (!tool) {
this.tryDisposePopper();
return;
}
- if (tool !== this.pen$.peek()) {
- this.pen$.value = tool;
+ if (tool.toolName !== this.pen$.peek()) {
+ this.pen$.value = tool.toolName as Pen;
}
if (this.active) return;
@@ -121,7 +123,17 @@ export class EdgelessPenToolButton extends EdgelessToolbarToolMixin(
private _togglePenMenu() {
if (this.tryDisposePopper()) return;
- !this.active && this.setEdgelessTool(this.pen$.peek());
+ const setPenByType = (pen: Pen) => {
+ if (pen === 'brush') {
+ this.setEdgelessTool(BrushTool);
+ } else {
+ this.setEdgelessTool(HighlighterTool);
+ }
+ };
+ if (!this.active) {
+ const pen = this.pen$.peek();
+ setPenByType(pen);
+ }
const menu = this.createPopper('edgeless-pen-menu', this);
Object.assign(menu.element, {
colors$: this.colors$,
@@ -132,7 +144,7 @@ export class EdgelessPenToolButton extends EdgelessToolbarToolMixin(
onChange: (props: Record) => {
const pen = this.pen$.peek();
this.edgeless.std.get(EditPropsStore).recordLastProps(pen, props);
- this.setEdgelessTool(pen);
+ setPenByType(pen);
},
});
}
diff --git a/blocksuite/affine/gfx/brush/src/toolbar/components/pen/types.ts b/blocksuite/affine/gfx/brush/src/toolbar/components/pen/types.ts
index 9287423b21..b2d05d0c77 100644
--- a/blocksuite/affine/gfx/brush/src/toolbar/components/pen/types.ts
+++ b/blocksuite/affine/gfx/brush/src/toolbar/components/pen/types.ts
@@ -1,8 +1,3 @@
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
-
-export type Pen = Extract<
- GfxToolsFullOptionValue['type'],
- 'brush' | 'highlighter'
->;
+export type Pen = 'brush' | 'highlighter';
export type PenMap = Record;
diff --git a/blocksuite/affine/gfx/connector/src/connector-tool.ts b/blocksuite/affine/gfx/connector/src/connector-tool.ts
index 45d66df3bc..7a8a409f40 100644
--- a/blocksuite/affine/gfx/connector/src/connector-tool.ts
+++ b/blocksuite/affine/gfx/connector/src/connector-tool.ts
@@ -1,5 +1,6 @@
import {
CanvasElementType,
+ DefaultTool,
OverlayIdentifier,
} from '@blocksuite/affine-block-surface';
import type {
@@ -104,8 +105,7 @@ export class ConnectorTool extends BaseTool {
this._allowCancel = true;
}
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
this.gfx.selection.set({ elements: [focusedId] });
}
@@ -132,8 +132,8 @@ export class ConnectorTool extends BaseTool {
const connector = this._connector;
this.doc.captureSync();
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.gfx.tool.setTool('default');
+
+ this.gfx.tool.setTool(DefaultTool);
this.gfx.selection.set({ elements: [connector.id] });
}
diff --git a/blocksuite/affine/gfx/connector/src/text/edgeless-connector-label-editor.ts b/blocksuite/affine/gfx/connector/src/text/edgeless-connector-label-editor.ts
index 34ceb34870..b9b657a2eb 100644
--- a/blocksuite/affine/gfx/connector/src/text/edgeless-connector-label-editor.ts
+++ b/blocksuite/affine/gfx/connector/src/text/edgeless-connector-label-editor.ts
@@ -1,4 +1,7 @@
-import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
+import {
+ DefaultTool,
+ EdgelessCRUDIdentifier,
+} from '@blocksuite/affine-block-surface';
import { getLineHeight } from '@blocksuite/affine-gfx-text';
import type { ConnectorElementModel } from '@blocksuite/affine-model';
import type { RichText } from '@blocksuite/affine-rich-text';
@@ -40,8 +43,7 @@ export function mountConnectorLabelEditor(
const gfx = edgeless.std.get(GfxControllerIdentifier);
- // @ts-expect-error default tool should be migrated to std
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [connector.id],
editing: true,
diff --git a/blocksuite/affine/gfx/connector/src/text/text.ts b/blocksuite/affine/gfx/connector/src/text/text.ts
index dad71bd7d3..d4ef84e220 100644
--- a/blocksuite/affine/gfx/connector/src/text/text.ts
+++ b/blocksuite/affine/gfx/connector/src/text/text.ts
@@ -1,4 +1,7 @@
-import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
+import {
+ DefaultTool,
+ EdgelessCRUDIdentifier,
+} from '@blocksuite/affine-block-surface';
import type { ConnectorElementModel } from '@blocksuite/affine-model';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import type { IVec } from '@blocksuite/global/gfx';
@@ -24,8 +27,7 @@ export function mountConnectorLabelEditor(
const gfx = edgeless.std.get(GfxControllerIdentifier);
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [connector.id],
editing: true,
diff --git a/blocksuite/affine/gfx/connector/src/toolbar/connector-dense-menu.ts b/blocksuite/affine/gfx/connector/src/toolbar/connector-dense-menu.ts
index f9c878f622..9d00827f77 100644
--- a/blocksuite/affine/gfx/connector/src/toolbar/connector-dense-menu.ts
+++ b/blocksuite/affine/gfx/connector/src/toolbar/connector-dense-menu.ts
@@ -8,6 +8,8 @@ import {
ConnectorLIcon,
} from '@blocksuite/icons/lit';
+import { ConnectorTool } from '../connector-tool';
+
export const buildConnectorDenseMenu: DenseMenuBuilder = (edgeless, gfx) => {
const prevMode =
edgeless.std.get(EditPropsStore).lastProps$.value.connector.mode;
@@ -17,7 +19,7 @@ export const buildConnectorDenseMenu: DenseMenuBuilder = (edgeless, gfx) => {
const createSelect =
(mode: ConnectorMode, record = true) =>
() => {
- gfx.tool.setTool('connector', {
+ gfx.tool.setTool(ConnectorTool, {
mode,
});
record &&
diff --git a/blocksuite/affine/gfx/connector/src/toolbar/connector-menu.ts b/blocksuite/affine/gfx/connector/src/toolbar/connector-menu.ts
index 617b5d32b7..173fdd4899 100644
--- a/blocksuite/affine/gfx/connector/src/toolbar/connector-menu.ts
+++ b/blocksuite/affine/gfx/connector/src/toolbar/connector-menu.ts
@@ -16,11 +16,12 @@ import {
ConnectorEIcon,
ConnectorLIcon,
} from '@blocksuite/icons/lit';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { property } from 'lit/decorators.js';
+import { ConnectorTool } from '../connector-tool';
+
function ConnectorModeButtonGroup(
mode: ConnectorMode,
setConnectorMode: (props: Record) => void
@@ -110,7 +111,7 @@ export class EdgelessConnectorMenu extends EdgelessToolbarToolMixin(
return this.edgeless.std.get(ThemeProvider).theme$.value;
});
- override type: GfxToolsFullOptionValue['type'] = 'connector';
+ override type = ConnectorTool;
override render() {
const { stroke, strokeWidth, mode } = this._props$.value;
diff --git a/blocksuite/affine/gfx/connector/src/toolbar/connector-tool-button.ts b/blocksuite/affine/gfx/connector/src/toolbar/connector-tool-button.ts
index 1b5e93fefd..6b07969a79 100644
--- a/blocksuite/affine/gfx/connector/src/toolbar/connector-tool-button.ts
+++ b/blocksuite/affine/gfx/connector/src/toolbar/connector-tool-button.ts
@@ -10,6 +10,8 @@ import {
import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
+import { ConnectorTool } from '../connector-tool';
+
const IcomMap = {
[ConnectorMode.Straight]: ConnectorLIcon(),
[ConnectorMode.Orthogonal]: ConnectorEIcon(),
@@ -30,7 +32,7 @@ export class EdgelessConnectorToolButton extends QuickToolMixin(
.mode;
});
- override type = 'connector' as const;
+ override type = ConnectorTool;
private _toggleMenu() {
if (this.tryDisposePopper()) return;
@@ -64,7 +66,7 @@ export class EdgelessConnectorToolButton extends QuickToolMixin(
@click=${() => {
// don't update tool before toggling menu
this._toggleMenu();
- this.gfx.tool.setTool('connector', {
+ this.gfx.tool.setTool(ConnectorTool, {
mode,
});
}}
diff --git a/blocksuite/affine/gfx/group/src/text/edgeless-group-title-editor.ts b/blocksuite/affine/gfx/group/src/text/edgeless-group-title-editor.ts
index 08d4eed79a..8250dd1d81 100644
--- a/blocksuite/affine/gfx/group/src/text/edgeless-group-title-editor.ts
+++ b/blocksuite/affine/gfx/group/src/text/edgeless-group-title-editor.ts
@@ -1,3 +1,4 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import type { GroupElementModel } from '@blocksuite/affine-model';
import type { RichText } from '@blocksuite/affine-rich-text';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
@@ -36,8 +37,7 @@ export function mountGroupTitleEditor(
const gfx = edgeless.std.get(GfxControllerIdentifier);
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [group.id],
editing: true,
diff --git a/blocksuite/affine/gfx/group/src/text/text.ts b/blocksuite/affine/gfx/group/src/text/text.ts
index c59c78ca09..673bd4dc71 100644
--- a/blocksuite/affine/gfx/group/src/text/text.ts
+++ b/blocksuite/affine/gfx/group/src/text/text.ts
@@ -1,3 +1,4 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import type { GroupElementModel } from '@blocksuite/affine-model';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import type { BlockComponent } from '@blocksuite/std';
@@ -19,8 +20,7 @@ export function mountGroupTitleEditor(
const gfx = edgeless.std.get(GfxControllerIdentifier);
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [group.id],
editing: true,
diff --git a/blocksuite/affine/gfx/link/src/toolbar/link-tool-button.ts b/blocksuite/affine/gfx/link/src/toolbar/link-tool-button.ts
index 785285f5a3..8c9b5aefff 100644
--- a/blocksuite/affine/gfx/link/src/toolbar/link-tool-button.ts
+++ b/blocksuite/affine/gfx/link/src/toolbar/link-tool-button.ts
@@ -1,14 +1,13 @@
import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark';
import { insertEmbedCard } from '@blocksuite/affine-block-embed';
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import { LinkIcon } from '@blocksuite/affine-components/icons';
-import type * as PointerEffect from '@blocksuite/affine-gfx-pointer';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
+import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { css, html, LitElement } from 'lit';
-declare type _GLOBAL_ = typeof PointerEffect;
-
export class EdgelessLinkToolButton extends QuickToolMixin(LitElement) {
static override styles = css`
.link-icon,
@@ -18,7 +17,7 @@ export class EdgelessLinkToolButton extends QuickToolMixin(LitElement) {
}
`;
- override type = 'default' as const;
+ override type = DefaultTool;
private _onClick() {
const [success, { insertedLinkType }] = this.edgeless.std.command.exec(
@@ -40,6 +39,12 @@ export class EdgelessLinkToolButton extends QuickToolMixin(LitElement) {
props: { url },
});
},
+ },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = this.edgeless.std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
}
).catch(console.error);
return;
diff --git a/blocksuite/affine/gfx/mindmap/package.json b/blocksuite/affine/gfx/mindmap/package.json
index eb510cd028..4d9090286d 100644
--- a/blocksuite/affine/gfx/mindmap/package.json
+++ b/blocksuite/affine/gfx/mindmap/package.json
@@ -17,6 +17,7 @@
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
"@blocksuite/affine-gfx-connector": "workspace:*",
+ "@blocksuite/affine-gfx-pointer": "workspace:*",
"@blocksuite/affine-gfx-shape": "workspace:*",
"@blocksuite/affine-gfx-text": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
diff --git a/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-menu.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-menu.ts
index ac92359907..e1cf5192f7 100644
--- a/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-menu.ts
+++ b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-menu.ts
@@ -1,4 +1,6 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toast } from '@blocksuite/affine-components/toast';
+import { EmptyTool } from '@blocksuite/affine-gfx-pointer';
import type { MindmapStyle } from '@blocksuite/affine-model';
import {
EditPropsStore,
@@ -123,8 +125,7 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin(
ToolbarMindmapItem | TextItem | ImportItem | MediaItem
>;
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- override type = 'empty' as const;
+ override type = EmptyTool;
get mindMaps() {
return getMindMaps(this.theme);
@@ -223,8 +224,7 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin(
this.onActiveStyleChange?.(element.data.style);
}
// a workaround to active mindmap, so that menu cannot be closed by `Escape`
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool({ type: 'empty' });
+ this.setEdgelessTool(EmptyTool);
},
onDrop: (element, bound) => {
if ('render' in element.data) {
@@ -234,8 +234,7 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin(
if (!id) return;
if (element.data.type === 'mindmap') {
this.onActiveStyleChange?.(element.data.style);
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool({ type: 'default' });
+ this.setEdgelessTool(DefaultTool);
this.gfx.selection.set({
elements: [id],
editing: false,
@@ -244,8 +243,7 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin(
element.data.type === 'text' ||
element.data.type === 'media'
) {
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool({ type: 'default' });
+ this.setEdgelessTool(DefaultTool);
}
})
.catch(console.error);
diff --git a/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts
index 5287214107..29adf47047 100644
--- a/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts
+++ b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts
@@ -1,4 +1,9 @@
-import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
+import {
+ DefaultTool,
+ EdgelessCRUDIdentifier,
+} from '@blocksuite/affine-block-surface';
+import { EmptyTool } from '@blocksuite/affine-gfx-pointer';
+import { TextTool } from '@blocksuite/affine-gfx-text';
import type {
MindmapElementModel,
MindmapStyle,
@@ -14,7 +19,6 @@ import {
} from '@blocksuite/affine-widget-edgeless-toolbar';
import type { Bound } from '@blocksuite/global/gfx';
import { SignalWatcher } from '@blocksuite/global/lit';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import { computed } from '@preact/signals-core';
import { css, html, LitElement, nothing } from 'lit';
import { property, query, state } from 'lit/decorators.js';
@@ -150,8 +154,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
override enableActiveBackground = true;
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- override type: GfxToolsFullOptionValue['type'][] = ['empty', 'text'];
+ override type = [EmptyTool, TextTool];
get draggableTools(): DraggableTool[] {
const style = this._style$.value;
@@ -192,8 +195,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
private _toggleMenu() {
if (this.tryDisposePopper()) return;
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool({ type: 'default' });
+ this.setEdgelessTool(DefaultTool);
const menu = this.createPopper('edgeless-mindmap-menu', this);
Object.assign(menu.element, {
@@ -213,8 +215,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
const element = this.crud.getElementById(id) as MindmapElementModel;
this.tryDisposePopper();
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool({ type: 'default' });
+ this.setEdgelessTool(DefaultTool);
this.gfx.selection.set({
elements: [element.tree.id],
editing: false,
@@ -274,15 +275,13 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
if (!id) return;
this.readyToDrop = false;
if (el.data.name === 'mindmap') {
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool({ type: 'default' });
+ this.setEdgelessTool(DefaultTool);
this.gfx.selection.set({
elements: [id],
editing: false,
});
} else if (el.data.name === 'text') {
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool({ type: 'default' });
+ this.setEdgelessTool(DefaultTool);
}
})
.catch(console.error);
@@ -314,8 +313,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
});
return;
}
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool({ type: 'empty' });
+ this.setEdgelessTool(EmptyTool);
const icon = this.mindmapElement;
const { x, y } = gfx.tool.lastMousePos$.peek();
const { viewport } = this.edgeless.std.get(ViewportElementProvider);
diff --git a/blocksuite/affine/gfx/mindmap/tsconfig.json b/blocksuite/affine/gfx/mindmap/tsconfig.json
index 31d472e83b..802a768ef3 100644
--- a/blocksuite/affine/gfx/mindmap/tsconfig.json
+++ b/blocksuite/affine/gfx/mindmap/tsconfig.json
@@ -14,6 +14,7 @@
{ "path": "../../components" },
{ "path": "../../ext-loader" },
{ "path": "../connector" },
+ { "path": "../pointer" },
{ "path": "../shape" },
{ "path": "../text" },
{ "path": "../../model" },
diff --git a/blocksuite/affine/gfx/note/src/note-tool.ts b/blocksuite/affine/gfx/note/src/note-tool.ts
index a28a0159f8..0f00ef55c8 100644
--- a/blocksuite/affine/gfx/note/src/note-tool.ts
+++ b/blocksuite/affine/gfx/note/src/note-tool.ts
@@ -1,18 +1,35 @@
-import type { SurfaceBlockComponent } from '@blocksuite/affine-block-surface';
import {
- addNote,
+ DEFAULT_NOTE_OFFSET_X,
+ DEFAULT_NOTE_OFFSET_Y,
+ DefaultTool,
+ EdgelessCRUDIdentifier,
EXCLUDING_MOUSE_OUT_CLASS_LIST,
+ type SurfaceBlockComponent,
} from '@blocksuite/affine-block-surface';
import {
DEFAULT_NOTE_HEIGHT,
DEFAULT_NOTE_WIDTH,
+ NOTE_MIN_HEIGHT,
+ type NoteBlockModel,
+ NoteDisplayMode,
} from '@blocksuite/affine-model';
-import { EditPropsStore } from '@blocksuite/affine-shared/services';
+import { focusTextModel } from '@blocksuite/affine-rich-text';
+import {
+ EditPropsStore,
+ TelemetryProvider,
+} from '@blocksuite/affine-shared/services';
import type { NoteChildrenFlavour } from '@blocksuite/affine-shared/types';
-import { hasClassNameInList } from '@blocksuite/affine-shared/utils';
-import { Point } from '@blocksuite/global/gfx';
-import type { PointerEventState } from '@blocksuite/std';
-import { BaseTool } from '@blocksuite/std/gfx';
+import {
+ handleNativeRangeAtPoint,
+ hasClassNameInList,
+} from '@blocksuite/affine-shared/utils';
+import { type IPoint, Point, serializeXYWH } from '@blocksuite/global/gfx';
+import type { BlockStdScope, PointerEventState } from '@blocksuite/std';
+import {
+ BaseTool,
+ type GfxBlockElementModel,
+ GfxControllerIdentifier,
+} from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { DraggingNoteOverlay, NoteOverlay } from './overlay';
@@ -210,3 +227,116 @@ declare module '@blocksuite/std/gfx' {
'affine:note': NoteToolOption;
}
}
+
+type NoteOptions = {
+ childFlavour: NoteChildrenFlavour;
+ childType: string | null;
+ collapse: boolean;
+};
+function addNote(
+ std: BlockStdScope,
+ point: Point,
+ options: NoteOptions,
+ width = DEFAULT_NOTE_WIDTH,
+ height = DEFAULT_NOTE_HEIGHT
+) {
+ const noteId = addNoteAtPoint(std, point, {
+ width,
+ height,
+ });
+
+ const gfx = std.get(GfxControllerIdentifier);
+ const doc = std.store;
+
+ const blockId = doc.addBlock(
+ options.childFlavour,
+ { type: options.childType },
+ noteId
+ );
+ if (options.collapse && height > NOTE_MIN_HEIGHT) {
+ const note = doc.getModelById(noteId) as NoteBlockModel;
+ doc.updateBlock(note, () => {
+ note.props.edgeless.collapse = true;
+ note.props.edgeless.collapsedHeight = height;
+ });
+ }
+ gfx.tool.setTool(DefaultTool);
+
+ // Wait for edgelessTool updated
+ requestAnimationFrame(() => {
+ const blocks =
+ (doc.root?.children.filter(
+ child => child.flavour === 'affine:note'
+ ) as GfxBlockElementModel[]) ?? [];
+ const element = blocks.find(b => b.id === noteId);
+ if (element) {
+ gfx.selection.set({
+ elements: [element.id],
+ editing: true,
+ });
+
+ // Waiting dom updated, `note mask` is removed
+ if (blockId) {
+ focusTextModel(gfx.std, blockId);
+ } else {
+ // Cannot reuse `handleNativeRangeClick` directly here,
+ // since `retargetClick` will re-target to pervious editor
+ handleNativeRangeAtPoint(point.x, point.y);
+ }
+ }
+ });
+}
+
+function addNoteAtPoint(
+ std: BlockStdScope,
+ /**
+ * The point is in browser coordinate
+ */
+ point: IPoint,
+ options: {
+ width?: number;
+ height?: number;
+ parentId?: string;
+ noteIndex?: number;
+ offsetX?: number;
+ offsetY?: number;
+ scale?: number;
+ } = {}
+) {
+ const gfx = std.get(GfxControllerIdentifier);
+ const crud = std.get(EdgelessCRUDIdentifier);
+ const {
+ width = DEFAULT_NOTE_WIDTH,
+ height = DEFAULT_NOTE_HEIGHT,
+ offsetX = DEFAULT_NOTE_OFFSET_X,
+ offsetY = DEFAULT_NOTE_OFFSET_Y,
+ parentId = gfx.doc.root?.id,
+ noteIndex,
+ scale = 1,
+ } = options;
+ const [x, y] = gfx.viewport.toModelCoord(point.x, point.y);
+ const blockId = crud.addBlock(
+ 'affine:note',
+ {
+ xywh: serializeXYWH(
+ x - offsetX * scale,
+ y - offsetY * scale,
+ width,
+ height
+ ),
+ displayMode: NoteDisplayMode.EdgelessOnly,
+ },
+ parentId,
+ noteIndex
+ );
+
+ std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
+ control: 'canvas:draw',
+ page: 'whiteboard editor',
+ module: 'toolbar',
+ segment: 'toolbar',
+ type: 'note',
+ });
+
+ return blockId;
+}
diff --git a/blocksuite/affine/gfx/note/src/overlay/overlay.ts b/blocksuite/affine/gfx/note/src/overlay/overlay.ts
index ce45f673a3..23b59b189b 100644
--- a/blocksuite/affine/gfx/note/src/overlay/overlay.ts
+++ b/blocksuite/affine/gfx/note/src/overlay/overlay.ts
@@ -5,10 +5,11 @@ import {
import { type Color, DefaultTheme } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import type { XYWH } from '@blocksuite/global/gfx';
-import type { GfxController, GfxToolsMap } from '@blocksuite/std/gfx';
+import type { GfxController } from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { Subject } from 'rxjs';
+import type { NoteTool } from '../note-tool';
import {
NOTE_OVERLAY_CORNER_RADIUS,
NOTE_OVERLAY_HEIGHT,
@@ -33,8 +34,7 @@ export class NoteOverlay extends ToolOverlay {
effect(() => {
// when change note child type, update overlay text
if (this.gfx.tool.currentToolName$.value !== 'affine:note') return;
- const tool =
- this.gfx.tool.currentTool$.peek() as GfxToolsMap['affine:note'];
+ const tool = this.gfx.tool.currentTool$.peek() as NoteTool;
this.text = this._getOverlayText(tool.activatedOption.tip);
(this.gfx.surfaceComponent as SurfaceBlockComponent).refresh();
})
diff --git a/blocksuite/affine/gfx/note/src/toolbar/note-menu.ts b/blocksuite/affine/gfx/note/src/toolbar/note-menu.ts
index 98c762df48..091d7bd23b 100644
--- a/blocksuite/affine/gfx/note/src/toolbar/note-menu.ts
+++ b/blocksuite/affine/gfx/note/src/toolbar/note-menu.ts
@@ -1,6 +1,7 @@
import { addAttachments } from '@blocksuite/affine-block-attachment';
import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark';
import { addImages } from '@blocksuite/affine-block-image';
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { MAX_IMAGE_WIDTH } from '@blocksuite/affine-model';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import type { NoteChildrenFlavour } from '@blocksuite/affine-shared/types';
@@ -10,13 +11,13 @@ import {
} from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { AttachmentIcon, ImageIcon, LinkIcon } from '@blocksuite/icons/lit';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
+import type { ToolOptions } from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { property, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
-import type { NoteToolOption } from '../note-tool.js';
+import { NoteTool, type NoteToolOption } from '../note-tool.js';
import { NOTE_MENU_ITEMS } from './note-menu-config.js';
export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
@@ -51,7 +52,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
}
`;
- override type: GfxToolsFullOptionValue['type'] = 'affine:note';
+ override type = NoteTool;
private async _addImages() {
this._imageLoading = true;
@@ -60,8 +61,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
maxWidth: MAX_IMAGE_WIDTH,
});
this._imageLoading = false;
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
this.gfx.selection.set({ elements: ids });
}
@@ -96,10 +96,11 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
effect(() => {
const tool = this.gfx.tool.currentToolOption$.value;
- if (tool?.type !== 'affine:note') return;
- this.childFlavour = tool.childFlavour;
- this.childType = tool.childType;
- this.tip = tool.tip;
+ if (tool?.toolType !== NoteTool) return;
+ const options = tool.options as ToolOptions;
+ this.childFlavour = options.childFlavour;
+ this.childType = options.childType;
+ this.tip = options.tip;
})
);
}
@@ -141,8 +142,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
const file = await openFileOrFiles();
if (!file) return;
await addAttachments(this.edgeless.std, [file]);
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
this.edgeless.std
.getOptional(TelemetryProvider)
?.track('CanvasElementAdded', {
diff --git a/blocksuite/affine/gfx/note/src/toolbar/note-senior-button.ts b/blocksuite/affine/gfx/note/src/toolbar/note-senior-button.ts
index e8a65850ed..7d625662e9 100644
--- a/blocksuite/affine/gfx/note/src/toolbar/note-senior-button.ts
+++ b/blocksuite/affine/gfx/note/src/toolbar/note-senior-button.ts
@@ -13,7 +13,7 @@ import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { state } from 'lit/decorators.js';
-import type { NoteToolOption } from '../note-tool.js';
+import { NoteTool, type NoteToolOption } from '../note-tool.js';
import { toShapeNotToAdapt } from './icon.js';
export class EdgelessNoteSeniorButton extends EdgelessToolbarToolMixin(
@@ -138,15 +138,14 @@ export class EdgelessNoteSeniorButton extends EdgelessToolbarToolMixin(
override enableActiveBackground = true;
- override type = 'affine:note' as const;
+ override type = NoteTool;
private _toggleNoteMenu() {
if (this.tryDisposePopper()) return;
const { edgeless, childFlavour, childType, tip } = this;
- this.setEdgelessTool({
- type: 'affine:note',
+ this.setEdgelessTool(NoteTool, {
childFlavour,
childType,
tip,
@@ -171,8 +170,7 @@ export class EdgelessNoteSeniorButton extends EdgelessToolbarToolMixin(
Object.assign(this, { [key]: props[key] });
}
});
- this.setEdgelessTool({
- type: 'affine:note',
+ this.setEdgelessTool(NoteTool, {
childFlavour: this.childFlavour,
childType: this.childType,
tip: this.tip,
diff --git a/blocksuite/affine/gfx/note/src/toolbar/note-tool-button.ts b/blocksuite/affine/gfx/note/src/toolbar/note-tool-button.ts
index 561bf32778..666d4345b5 100644
--- a/blocksuite/affine/gfx/note/src/toolbar/note-tool-button.ts
+++ b/blocksuite/affine/gfx/note/src/toolbar/note-tool-button.ts
@@ -4,12 +4,11 @@ import {
QuickToolMixin,
} from '@blocksuite/affine-widget-edgeless-toolbar';
import { PageIcon } from '@blocksuite/icons/lit';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { state } from 'lit/decorators.js';
-import type { NoteToolOption } from '../note-tool.js';
+import { NoteTool, type NoteToolOption } from '../note-tool.js';
import type { EdgelessNoteMenu } from './note-menu.js';
export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
@@ -23,7 +22,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
private readonly _states = ['childFlavour', 'childType', 'tip'] as const;
- override type: GfxToolsFullOptionValue['type'] = 'affine:note';
+ override type = NoteTool;
private _disposeMenu() {
this._noteMenu?.dispose();
@@ -35,7 +34,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
this._disposeMenu();
this.requestUpdate();
} else {
- this.gfx.tool.setTool('affine:note', {
+ this.gfx.tool.setTool(NoteTool, {
childFlavour: this.childFlavour,
childType: this.childType,
tip: this.tip,
@@ -59,7 +58,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
Object.assign(this, { [key]: props[key] });
}
});
- this.gfx.tool.setTool('affine:note', {
+ this.gfx.tool.setTool(NoteTool, {
childFlavour: this.childFlavour,
childType: this.childType,
tip: this.tip,
diff --git a/blocksuite/affine/gfx/pointer/src/quick-tool/default-tool-button.ts b/blocksuite/affine/gfx/pointer/src/quick-tool/default-tool-button.ts
index 15bbe13a8c..3a168d244f 100644
--- a/blocksuite/affine/gfx/pointer/src/quick-tool/default-tool-button.ts
+++ b/blocksuite/affine/gfx/pointer/src/quick-tool/default-tool-button.ts
@@ -1,10 +1,12 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { HandIcon, SelectIcon } from '@blocksuite/icons/lit';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { query } from 'lit/decorators.js';
+import { PanTool } from '../tools';
+
export class EdgelessDefaultToolButton extends QuickToolMixin(LitElement) {
static override styles = css`
.current-icon {
@@ -17,19 +19,19 @@ export class EdgelessDefaultToolButton extends QuickToolMixin(LitElement) {
}
`;
- override type: GfxToolsFullOptionValue['type'][] = ['default', 'pan'];
+ override type = [DefaultTool, PanTool];
private _changeTool() {
if (this.toolbar.activePopper) {
// click manually always closes the popper
this.toolbar.activePopper.dispose();
}
- const type = this.edgelessTool?.type;
+ const type = this.edgelessTool?.toolType?.toolName;
if (type !== 'default' && type !== 'pan') {
if (localStorage.defaultTool === 'default') {
- this.setEdgelessTool('default');
+ this.setEdgelessTool(DefaultTool);
} else if (localStorage.defaultTool === 'pan') {
- this.setEdgelessTool('pan', { panning: false });
+ this.setEdgelessTool(PanTool, { panning: false });
}
return;
}
@@ -37,9 +39,9 @@ export class EdgelessDefaultToolButton extends QuickToolMixin(LitElement) {
// wait for animation to finish
setTimeout(() => {
if (type === 'default') {
- this.setEdgelessTool('pan', { panning: false });
+ this.setEdgelessTool(PanTool, { panning: false });
} else if (type === 'pan') {
- this.setEdgelessTool('default');
+ this.setEdgelessTool(DefaultTool);
}
this._fadeIn();
}, 100);
@@ -71,7 +73,7 @@ export class EdgelessDefaultToolButton extends QuickToolMixin(LitElement) {
}
override render() {
- const type = this.edgelessTool?.type;
+ const type = this.edgelessTool?.toolType?.toolName;
const { active } = this;
const tipInfo =
type === 'pan'
diff --git a/blocksuite/affine/gfx/pointer/src/tools/index.ts b/blocksuite/affine/gfx/pointer/src/tools/index.ts
index 5c18a61808..d93a129539 100644
--- a/blocksuite/affine/gfx/pointer/src/tools/index.ts
+++ b/blocksuite/affine/gfx/pointer/src/tools/index.ts
@@ -1,3 +1,2 @@
-export * from './default-tool.js';
export * from './empty-tool.js';
export * from './pan-tool.js';
diff --git a/blocksuite/affine/gfx/pointer/src/tools/pan-tool.ts b/blocksuite/affine/gfx/pointer/src/tools/pan-tool.ts
index 4a07b7b59e..4b6ad03fb5 100644
--- a/blocksuite/affine/gfx/pointer/src/tools/pan-tool.ts
+++ b/blocksuite/affine/gfx/pointer/src/tools/pan-tool.ts
@@ -56,11 +56,14 @@ export class PanTool extends BaseTool {
const selection = this.gfx.selection.surfaceSelections;
const currentTool = this.controller.currentToolOption$.peek();
const restoreToPrevious = () => {
- this.controller.setTool(currentTool);
- this.gfx.selection.set(selection);
+ const { toolType, options } = currentTool;
+ if (toolType && options) {
+ this.controller.setTool(toolType, options);
+ this.gfx.selection.set(selection);
+ }
};
- this.controller.setTool('pan', {
+ this.controller.setTool(PanTool, {
panning: true,
});
diff --git a/blocksuite/affine/gfx/pointer/src/view.ts b/blocksuite/affine/gfx/pointer/src/view.ts
index f4b992511c..e9fa3a164c 100644
--- a/blocksuite/affine/gfx/pointer/src/view.ts
+++ b/blocksuite/affine/gfx/pointer/src/view.ts
@@ -7,7 +7,7 @@ import { effects } from './effects';
import { defaultQuickTool } from './quick-tool/quick-tool';
import { SnapExtension } from './snap/snap-manager';
import { SnapOverlay } from './snap/snap-overlay';
-import { DefaultTool, EmptyTool, PanTool } from './tools';
+import { EmptyTool, PanTool } from './tools';
export class PointerViewExtension extends ViewExtensionProvider {
override name = 'affine-pointer-gfx';
@@ -20,7 +20,6 @@ export class PointerViewExtension extends ViewExtensionProvider {
override setup(context: ViewExtensionContext) {
super.setup(context);
context.register(EmptyTool);
- context.register(DefaultTool);
context.register(PanTool);
if (this.isEdgeless(context.scope)) {
context.register(defaultQuickTool);
diff --git a/blocksuite/affine/gfx/shape/src/draggable/shape-draggable.ts b/blocksuite/affine/gfx/shape/src/draggable/shape-draggable.ts
index da17df0c86..16ed9c8314 100644
--- a/blocksuite/affine/gfx/shape/src/draggable/shape-draggable.ts
+++ b/blocksuite/affine/gfx/shape/src/draggable/shape-draggable.ts
@@ -1,5 +1,6 @@
import {
CanvasElementType,
+ DefaultTool,
EdgelessCRUDIdentifier,
} from '@blocksuite/affine-block-surface';
import {
@@ -137,7 +138,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
draggingShape: DraggableShape['name'] = 'roundedRect';
- override type = 'shape' as const;
+ override type = ShapeTool;
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
@@ -169,8 +170,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
this.draggableController.states.draggingElement?.data.name;
if (!shapeName) return;
- this.setEdgelessTool({
- type: 'shape',
+ this.setEdgelessTool(ShapeTool, {
shapeName,
});
const controller = this.gfx.tool.currentTool$.peek();
@@ -206,8 +206,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
this._setShapeOverlayLock(false);
this.readyToDrop = false;
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
this.gfx.selection.set({
elements: [id],
editing: false,
@@ -265,7 +264,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
const clientPos = { x: x + left, y: y + top };
this.draggableController.dragAndMoveTo(el, clientPos);
} else {
- this.setEdgelessTool('shape', {
+ this.setEdgelessTool(ShapeTool, {
shapeName: this.draggingShape,
});
}
diff --git a/blocksuite/affine/gfx/shape/src/draggable/shape-menu.ts b/blocksuite/affine/gfx/shape/src/draggable/shape-menu.ts
index d766e077dd..fb024fe1bf 100644
--- a/blocksuite/affine/gfx/shape/src/draggable/shape-menu.ts
+++ b/blocksuite/affine/gfx/shape/src/draggable/shape-menu.ts
@@ -15,12 +15,16 @@ import type { ColorEvent } from '@blocksuite/affine-shared/utils';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { StyleGeneralIcon, StyleScribbleIcon } from '@blocksuite/icons/lit';
import type { BlockComponent } from '@blocksuite/std';
-import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
+import {
+ GfxControllerIdentifier,
+ type ToolOptionWithType,
+} from '@blocksuite/std/gfx';
import { computed, effect, type Signal, signal } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { property } from 'lit/decorators.js';
import { when } from 'lit/directives/when.js';
+import { ShapeTool } from '../shape-tool';
import { ShapeComponentConfig } from '../toolbar';
export class EdgelessShapeMenu extends SignalWatcher(
@@ -115,8 +119,12 @@ export class EdgelessShapeMenu extends SignalWatcher(
effect(() => {
const value = gfx.tool.currentToolOption$.value;
- if (value && value.type === 'shape') {
- this._shapeName$.value = value.shapeName;
+ if (value && value.toolType === ShapeTool) {
+ const shapeName = (value as ToolOptionWithType).options
+ ?.shapeName;
+ if (shapeName) {
+ this._shapeName$.value = shapeName;
+ }
}
})
);
diff --git a/blocksuite/affine/gfx/shape/src/draggable/shape-tool-button.ts b/blocksuite/affine/gfx/shape/src/draggable/shape-tool-button.ts
index 487a98a96e..1e3442a1f6 100644
--- a/blocksuite/affine/gfx/shape/src/draggable/shape-tool-button.ts
+++ b/blocksuite/affine/gfx/shape/src/draggable/shape-tool-button.ts
@@ -38,7 +38,7 @@ export class EdgelessShapeToolButton extends EdgelessToolbarToolMixin(
if (!this.popper) this._toggleMenu();
};
- override type = 'shape' as const;
+ override type = ShapeTool;
private _toggleMenu() {
this.createPopper('edgeless-shape-menu', this, {
diff --git a/blocksuite/affine/gfx/shape/src/draggable/shape-tool-element.ts b/blocksuite/affine/gfx/shape/src/draggable/shape-tool-element.ts
index 0d6b034535..235d41e5fc 100644
--- a/blocksuite/affine/gfx/shape/src/draggable/shape-tool-element.ts
+++ b/blocksuite/affine/gfx/shape/src/draggable/shape-tool-element.ts
@@ -1,5 +1,6 @@
import {
CanvasElementType,
+ DefaultTool,
EdgelessCRUDIdentifier,
} from '@blocksuite/affine-block-surface';
import {
@@ -109,8 +110,7 @@ export class EdgelessShapeToolElement extends WithDisposable(LitElement) {
return;
}
this._dragging = false;
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
if (this._isOutside) {
const rect = this._shapeElement.getBoundingClientRect();
this._backupShapeElement.style.setProperty('transition', 'none');
diff --git a/blocksuite/affine/gfx/shape/src/shape-tool.ts b/blocksuite/affine/gfx/shape/src/shape-tool.ts
index bba7e4db5a..b4e3e90d82 100644
--- a/blocksuite/affine/gfx/shape/src/shape-tool.ts
+++ b/blocksuite/affine/gfx/shape/src/shape-tool.ts
@@ -1,5 +1,6 @@
import {
CanvasElementType,
+ DefaultTool,
EXCLUDING_MOUSE_OUT_CLASS_LIST,
type SurfaceBlockComponent,
} from '@blocksuite/affine-block-surface';
@@ -180,8 +181,7 @@ export class ShapeTool extends BaseTool {
const element = this.gfx.getElementById(id);
if (!element) return;
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
this.gfx.selection.set({
elements: [element.id],
editing: false,
@@ -257,8 +257,7 @@ export class ShapeTool extends BaseTool {
const element = this.gfx.getElementById(id);
if (!element) return;
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.controller.setTool('default');
+ this.controller.setTool(DefaultTool);
this.gfx.selection.set({
elements: [element.id],
});
diff --git a/blocksuite/affine/gfx/shape/src/text/edgeless-shape-text-editor.ts b/blocksuite/affine/gfx/shape/src/text/edgeless-shape-text-editor.ts
index 767066e55a..a4501f8cd4 100644
--- a/blocksuite/affine/gfx/shape/src/text/edgeless-shape-text-editor.ts
+++ b/blocksuite/affine/gfx/shape/src/text/edgeless-shape-text-editor.ts
@@ -1,4 +1,5 @@
import {
+ DefaultTool,
EdgelessCRUDIdentifier,
TextUtils,
} from '@blocksuite/affine-block-surface';
@@ -49,8 +50,7 @@ export function mountShapeTextEditor(
return;
}
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [shapeElement.id],
editing: true,
diff --git a/blocksuite/affine/gfx/template/src/toolbar/template-panel.ts b/blocksuite/affine/gfx/template/src/toolbar/template-panel.ts
index 74668c0b37..0874eb7b14 100644
--- a/blocksuite/affine/gfx/template/src/toolbar/template-panel.ts
+++ b/blocksuite/affine/gfx/template/src/toolbar/template-panel.ts
@@ -1,3 +1,4 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import {
darkToolbarStyles,
lightToolbarStyles,
@@ -314,8 +315,7 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
}
} finally {
this._loadingTemplate = null;
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
}
}
diff --git a/blocksuite/affine/gfx/template/src/toolbar/template-tool-button.ts b/blocksuite/affine/gfx/template/src/toolbar/template-tool-button.ts
index 4f5997aa2c..89030472a9 100644
--- a/blocksuite/affine/gfx/template/src/toolbar/template-tool-button.ts
+++ b/blocksuite/affine/gfx/template/src/toolbar/template-tool-button.ts
@@ -1,7 +1,8 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import { ArrowDownSmallIcon } from '@blocksuite/affine-components/icons';
import { once } from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
+import type { ToolOptionWithType } from '@blocksuite/std/gfx';
import {
arrow,
autoUpdate,
@@ -14,6 +15,7 @@ import { state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { repeat } from 'lit/directives/repeat.js';
+import { TemplateTool } from '../template-tool';
import { TemplateCard1, TemplateCard2, TemplateCard3 } from './cards.js';
import type { EdgelessTemplatePanel } from './template-panel.js';
@@ -115,11 +117,11 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
private _cleanup: (() => void) | null = null;
- private _prevTool: GfxToolsFullOptionValue | null = null;
+ private _prevTool: ToolOptionWithType | null = null;
override enableActiveBackground = true;
- override type: GfxToolsFullOptionValue['type'] = 'template';
+ override type = TemplateTool;
get cards() {
const { theme } = this;
@@ -134,12 +136,15 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
this._cleanup = null;
this.requestUpdate();
- if (this._prevTool && this._prevTool.type !== 'template') {
- this.setEdgelessTool(this._prevTool);
+ if (
+ this._prevTool &&
+ this._prevTool.toolType &&
+ this._prevTool.toolType !== TemplateTool
+ ) {
+ this.setEdgelessTool(this._prevTool.toolType, this._prevTool.options);
this._prevTool = null;
} else {
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.setEdgelessTool('default');
+ this.setEdgelessTool(DefaultTool);
}
}
}
@@ -147,8 +152,8 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
private _togglePanel() {
if (this._openedPanel) {
this._closePanel();
- if (this._prevTool) {
- this.setEdgelessTool(this._prevTool);
+ if (this._prevTool && this._prevTool.toolType) {
+ this.setEdgelessTool(this._prevTool.toolType, this._prevTool.options);
this._prevTool = null;
}
return;
@@ -156,7 +161,7 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
this._prevTool = this.edgelessTool ? { ...this.edgelessTool } : null;
- this.setEdgelessTool('template');
+ this.setEdgelessTool(TemplateTool);
const panel = document.createElement('edgeless-templates-panel');
panel.edgeless = this.edgeless;
diff --git a/blocksuite/affine/gfx/text/src/edgeless-text-editor.ts b/blocksuite/affine/gfx/text/src/edgeless-text-editor.ts
index 28fc95677f..aa943bdfdd 100644
--- a/blocksuite/affine/gfx/text/src/edgeless-text-editor.ts
+++ b/blocksuite/affine/gfx/text/src/edgeless-text-editor.ts
@@ -1,5 +1,6 @@
import {
CanvasElementType,
+ DefaultTool,
EdgelessCRUDIdentifier,
getSurfaceBlock,
type IModelCoord,
@@ -53,8 +54,7 @@ export function mountTextElementEditor(
const gfx = edgeless.std.get(GfxControllerIdentifier);
- // @ts-expect-error TODO: refactor gfx tool
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [textElement.id],
editing: true,
diff --git a/blocksuite/affine/gfx/text/src/mount-text-editor.ts b/blocksuite/affine/gfx/text/src/mount-text-editor.ts
index 38b0782827..0a3ce89ee0 100644
--- a/blocksuite/affine/gfx/text/src/mount-text-editor.ts
+++ b/blocksuite/affine/gfx/text/src/mount-text-editor.ts
@@ -1,5 +1,6 @@
import {
CanvasElementType,
+ DefaultTool,
EdgelessCRUDIdentifier,
type IModelCoord,
} from '@blocksuite/affine-block-surface';
@@ -37,8 +38,7 @@ export function mountTextElementEditor(
const gfx = edgeless.std.get(GfxControllerIdentifier);
- // @ts-expect-error TODO: refactor gfx tool
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [textElement.id],
editing: true,
diff --git a/blocksuite/affine/gfx/text/src/tool.ts b/blocksuite/affine/gfx/text/src/tool.ts
index e0b77985e8..9e8aacac63 100644
--- a/blocksuite/affine/gfx/text/src/tool.ts
+++ b/blocksuite/affine/gfx/text/src/tool.ts
@@ -1,3 +1,4 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import type { TextElementModel } from '@blocksuite/affine-model';
import {
FeatureFlagService,
@@ -49,8 +50,7 @@ export class TextTool extends BaseTool {
if (textFlag) {
const [x, y] = this.gfx.viewport.toModelCoord(e.x, e.y);
this.gfx.std.command.exec(insertEdgelessTextCommand, { x, y });
- // @ts-expect-error TODO: refactor gfx tool
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
} else {
addText(this.gfx, e);
}
diff --git a/blocksuite/affine/gfx/text/src/toolbar/text-menu.ts b/blocksuite/affine/gfx/text/src/toolbar/text-menu.ts
index 236353e905..e0adfd2f7a 100644
--- a/blocksuite/affine/gfx/text/src/toolbar/text-menu.ts
+++ b/blocksuite/affine/gfx/text/src/toolbar/text-menu.ts
@@ -2,11 +2,12 @@ import { DefaultTheme } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import type { ColorEvent } from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
-import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import { computed } from '@preact/signals-core';
import { css, html, LitElement, nothing } from 'lit';
import { property } from 'lit/decorators.js';
+import { TextTool } from '../tool';
+
export class EdgelessTextMenu extends EdgelessToolbarToolMixin(LitElement) {
static override styles = css`
:host {
@@ -20,10 +21,10 @@ export class EdgelessTextMenu extends EdgelessToolbarToolMixin(LitElement) {
return this.edgeless.std.get(ThemeProvider).theme$.value;
});
- override type: GfxToolsFullOptionValue['type'] = 'text';
+ override type = TextTool;
override render() {
- if (this.edgelessTool.type !== 'text') return nothing;
+ if (this.edgelessTool.toolType !== TextTool) return nothing;
return html`
diff --git a/blocksuite/affine/widgets/drag-handle/src/watchers/edgeless-watcher.ts b/blocksuite/affine/widgets/drag-handle/src/watchers/edgeless-watcher.ts
index 76dc4a8717..3a0e899a61 100644
--- a/blocksuite/affine/widgets/drag-handle/src/watchers/edgeless-watcher.ts
+++ b/blocksuite/affine/widgets/drag-handle/src/watchers/edgeless-watcher.ts
@@ -3,7 +3,7 @@ import { getSelectedRect } from '@blocksuite/affine-shared/utils';
import { type IVec, Rect } from '@blocksuite/global/gfx';
import {
GfxControllerIdentifier,
- type GfxToolsFullOptionValue,
+ type ToolOptionWithType,
} from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
@@ -22,10 +22,9 @@ import type { AffineDragHandleWidget } from '../drag-handle.js';
*/
export class EdgelessWatcher {
private readonly _handleEdgelessToolUpdated = (
- newTool: GfxToolsFullOptionValue
+ newTool: ToolOptionWithType
) => {
- // @ts-expect-error GfxToolsFullOptionValue is extended in other packages
- if (newTool.type === 'default') {
+ if (newTool.toolType?.toolName === 'default') {
this.updateAnchorElement();
} else {
this.widget.hide();
diff --git a/blocksuite/affine/widgets/edgeless-toolbar/src/edgeless-toolbar.ts b/blocksuite/affine/widgets/edgeless-toolbar/src/edgeless-toolbar.ts
index 42d1ec339b..ce3459f2e8 100644
--- a/blocksuite/affine/widgets/edgeless-toolbar/src/edgeless-toolbar.ts
+++ b/blocksuite/affine/widgets/edgeless-toolbar/src/edgeless-toolbar.ts
@@ -1,5 +1,8 @@
/* oxlint-disable @typescript-eslint/no-non-null-assertion */
-import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
+import {
+ DefaultTool,
+ EdgelessLegacySlotIdentifier,
+} from '@blocksuite/affine-block-surface';
import {
type MenuHandler,
popMenu,
@@ -430,8 +433,7 @@ export class EdgelessToolbarWidget extends WidgetComponent {
}
get edgelessTool() {
- // FIXME: maybe we need to fix this type
- return this.gfx.tool.currentToolOption$.value as { type: string };
+ return this.gfx.tool.currentToolName$.value;
}
get gfx() {
@@ -439,7 +441,7 @@ export class EdgelessToolbarWidget extends WidgetComponent {
}
get isPresentMode() {
- return this.edgelessTool.type === 'frameNavigator';
+ return this.edgelessTool === 'frameNavigator';
}
get scrollSeniorToolSize() {
@@ -523,7 +525,7 @@ export class EdgelessToolbarWidget extends WidgetComponent {
@click=${this._openMoreQuickToolsMenu}
?active=${this._quickTools
.slice(this._visibleQuickToolSize)
- .some(tool => tool.type === this.edgelessTool?.type)}
+ .some(tool => tool.type === this.edgelessTool)}
>
${MoreHorizontalIcon({ width: '20px', height: '20px' })}
@@ -602,16 +604,15 @@ export class EdgelessToolbarWidget extends WidgetComponent {
{
Escape: () => {
if (this.gfx.selection.editing) return;
- if (this.edgelessTool.type === 'frameNavigator') return;
- if (this.edgelessTool.type === 'default') {
+ if (this.edgelessTool === 'frameNavigator') return;
+ if (this.edgelessTool === 'default') {
if (this.activePopper) {
this.activePopper.dispose();
this.activePopper = null;
}
return;
}
- // @ts-expect-error FIXME: resolve after gfx tool refactor
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
},
},
{ global: true }
@@ -658,7 +659,7 @@ export class EdgelessToolbarWidget extends WidgetComponent {
}
override render() {
- const { type } = this.edgelessTool || {};
+ const type = this.edgelessTool;
if (this.doc.readonly && type !== 'frameNavigator') {
return nothing;
}
diff --git a/blocksuite/affine/widgets/edgeless-toolbar/src/extension/index.ts b/blocksuite/affine/widgets/edgeless-toolbar/src/extension/index.ts
index 6851517e76..272e23bf6c 100644
--- a/blocksuite/affine/widgets/edgeless-toolbar/src/extension/index.ts
+++ b/blocksuite/affine/widgets/edgeless-toolbar/src/extension/index.ts
@@ -1,12 +1,12 @@
import type { MenuConfig } from '@blocksuite/affine-components/context-menu';
import { createIdentifier } from '@blocksuite/global/di';
import type { BlockComponent } from '@blocksuite/std';
-import type { GfxController, GfxToolsMap } from '@blocksuite/std/gfx';
+import type { GfxController } from '@blocksuite/std/gfx';
import type { ExtensionType } from '@blocksuite/store';
import { type TemplateResult } from 'lit';
export interface QuickTool {
- type?: keyof GfxToolsMap;
+ type?: string;
enable?: boolean;
content: TemplateResult;
/**
diff --git a/blocksuite/affine/widgets/edgeless-toolbar/src/mixins/tool.mixin.ts b/blocksuite/affine/widgets/edgeless-toolbar/src/mixins/tool.mixin.ts
index e7b0e5441c..8994456a98 100644
--- a/blocksuite/affine/widgets/edgeless-toolbar/src/mixins/tool.mixin.ts
+++ b/blocksuite/affine/widgets/edgeless-toolbar/src/mixins/tool.mixin.ts
@@ -9,9 +9,9 @@ import type { BlockComponent } from '@blocksuite/std';
import {
type GfxController,
GfxControllerIdentifier,
- type GfxToolsFullOption,
- type GfxToolsFullOptionValue,
type ToolController,
+ type ToolOptionWithType,
+ type ToolType,
} from '@blocksuite/std/gfx';
import { consume } from '@lit/context';
import { effect } from '@preact/signals-core';
@@ -28,8 +28,6 @@ import {
import { createPopper, type MenuPopper } from '../create-popper';
import type { EdgelessToolbarWidget } from '../edgeless-toolbar';
-type ValueOf = T[keyof T];
-
export declare abstract class EdgelessToolbarToolClass extends DisposableClass {
active: boolean;
@@ -37,7 +35,7 @@ export declare abstract class EdgelessToolbarToolClass extends DisposableClass {
edgeless: BlockComponent;
- edgelessTool: GfxToolsFullOptionValue;
+ edgelessTool: ToolOptionWithType;
enableActiveBackground?: boolean;
@@ -58,9 +56,7 @@ export declare abstract class EdgelessToolbarToolClass extends DisposableClass {
*/
tryDisposePopper: () => boolean;
- abstract type:
- | GfxToolsFullOptionValue['type']
- | GfxToolsFullOptionValue['type'][];
+ abstract type: ToolType | ToolType[];
accessor toolbar: EdgelessToolbarWidget;
}
@@ -71,19 +67,15 @@ export const EdgelessToolbarToolMixin = >(
abstract class DerivedClass extends WithDisposable(SuperClass) {
enableActiveBackground = false;
- abstract type:
- | GfxToolsFullOptionValue['type']
- | GfxToolsFullOptionValue['type'][];
+ abstract type: ToolType | ToolType[];
get active() {
const { type } = this;
- // @ts-expect-error FIXME: we need to fix the type of edgelessTool
- const activeType = this.edgelessTool?.type;
+ const activeType = this.edgelessTool?.toolType;
return activeType
? Array.isArray(type)
- ? // @ts-expect-error FIXME: we need to fix the type of edgelessTool
- type.includes(activeType)
+ ? type.includes(activeType)
: activeType === type
: false;
}
@@ -93,12 +85,7 @@ export const EdgelessToolbarToolMixin = >(
}
get setEdgelessTool() {
- return (...args: Parameters) => {
- this.gfx.tool.setTool(
- // @ts-expect-error FIXME: ts error
- ...args
- );
- };
+ return this.gfx.tool.setTool;
}
private _applyActiveStyle() {
@@ -162,7 +149,7 @@ export const EdgelessToolbarToolMixin = >(
accessor edgeless!: BlockComponent;
@state()
- accessor edgelessTool!: ValueOf | null;
+ accessor edgelessTool!: ToolOptionWithType | null;
@state()
public accessor popper: MenuPopper | null = null;
diff --git a/blocksuite/affine/widgets/frame-title/package.json b/blocksuite/affine/widgets/frame-title/package.json
index c17f960815..f2dbe5e85e 100644
--- a/blocksuite/affine/widgets/frame-title/package.json
+++ b/blocksuite/affine/widgets/frame-title/package.json
@@ -10,6 +10,7 @@
"author": "toeverything",
"license": "MIT",
"dependencies": {
+ "@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
diff --git a/blocksuite/affine/widgets/frame-title/src/mount-frame-title-editor.ts b/blocksuite/affine/widgets/frame-title/src/mount-frame-title-editor.ts
index 324424b324..cf983f109a 100644
--- a/blocksuite/affine/widgets/frame-title/src/mount-frame-title-editor.ts
+++ b/blocksuite/affine/widgets/frame-title/src/mount-frame-title-editor.ts
@@ -1,3 +1,4 @@
+import { DefaultTool } from '@blocksuite/affine-block-surface';
import type { FrameBlockModel } from '@blocksuite/affine-model';
import { BlockSuiteError } from '@blocksuite/global/exceptions';
import type { BlockComponent } from '@blocksuite/std';
@@ -19,8 +20,7 @@ export function mountFrameTitleEditor(
const gfx = edgeless.std.get(GfxControllerIdentifier);
- // @ts-expect-error TODO: refactor gfx tool
- gfx.tool.setTool('default');
+ gfx.tool.setTool(DefaultTool);
gfx.selection.set({
elements: [frame.id],
editing: true,
diff --git a/blocksuite/affine/widgets/frame-title/tsconfig.json b/blocksuite/affine/widgets/frame-title/tsconfig.json
index 6a1c46edbc..179c2edda9 100644
--- a/blocksuite/affine/widgets/frame-title/tsconfig.json
+++ b/blocksuite/affine/widgets/frame-title/tsconfig.json
@@ -7,6 +7,7 @@
},
"include": ["./src"],
"references": [
+ { "path": "../../blocks/surface" },
{ "path": "../../components" },
{ "path": "../../ext-loader" },
{ "path": "../../model" },
diff --git a/blocksuite/affine/widgets/keyboard-toolbar/src/config.ts b/blocksuite/affine/widgets/keyboard-toolbar/src/config.ts
index c25397411f..eb1acc5859 100644
--- a/blocksuite/affine/widgets/keyboard-toolbar/src/config.ts
+++ b/blocksuite/affine/widgets/keyboard-toolbar/src/config.ts
@@ -16,7 +16,7 @@ import {
dedentParagraphCommand,
indentParagraphCommand,
} from '@blocksuite/affine-block-paragraph';
-import { getSurfaceBlock } from '@blocksuite/affine-block-surface';
+import { DefaultTool, getSurfaceBlock } from '@blocksuite/affine-block-surface';
import { insertSurfaceRefBlockCommand } from '@blocksuite/affine-block-surface-ref';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import { toast } from '@blocksuite/affine-components/toast';
@@ -101,6 +101,7 @@ import {
type BlockStdScope,
ConfigExtensionFactory,
} from '@blocksuite/std';
+import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { computed } from '@preact/signals-core';
import { cssVarV2 } from '@toeverything/theme/v2';
import type { TemplateResult } from 'lit';
@@ -393,7 +394,13 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = {
std.host,
'Links',
'The added link will be displayed as a card view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
);
if (model.text?.length === 0) {
std.store.deleteBlock(model);
@@ -486,7 +493,13 @@ const embedToolGroup: KeyboardToolPanelGroup = {
std.host,
'YouTube',
'The added YouTube video link will be displayed as an embed view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
);
if (model.text?.length === 0) {
std.store.deleteBlock(model);
@@ -513,7 +526,13 @@ const embedToolGroup: KeyboardToolPanelGroup = {
std.host,
'GitHub',
'The added GitHub issue or pull request link will be displayed as a card view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
);
if (model.text?.length === 0) {
std.store.deleteBlock(model);
@@ -541,7 +560,13 @@ const embedToolGroup: KeyboardToolPanelGroup = {
std.host,
'Figma',
'The added Figma link will be displayed as an embed view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
);
if (model.text?.length === 0) {
std.store.deleteBlock(model);
@@ -568,7 +593,13 @@ const embedToolGroup: KeyboardToolPanelGroup = {
std.host,
'Loom',
'The added Loom video link will be displayed as an embed view.',
- { mode: 'page', parentModel, index }
+ { mode: 'page', parentModel, index },
+ ({ mode }) => {
+ if (mode === 'edgeless') {
+ const gfx = std.get(GfxControllerIdentifier);
+ gfx.tool.setTool(DefaultTool);
+ }
+ }
);
if (model.text?.length === 0) {
std.store.deleteBlock(model);
diff --git a/blocksuite/framework/std/src/gfx/index.ts b/blocksuite/framework/std/src/gfx/index.ts
index 67278ee230..d0b00d8741 100644
--- a/blocksuite/framework/std/src/gfx/index.ts
+++ b/blocksuite/framework/std/src/gfx/index.ts
@@ -94,6 +94,9 @@ export {
type GfxToolsFullOptionValue,
type GfxToolsMap,
type GfxToolsOption,
+ type ToolOptions,
+ type ToolOptionWithType,
+ type ToolType,
} from './tool/tool.js';
export { MouseButton, ToolController } from './tool/tool-controller.js';
export {
diff --git a/blocksuite/framework/std/src/gfx/tool/tool-controller.ts b/blocksuite/framework/std/src/gfx/tool/tool-controller.ts
index 19e8fac379..f345f338ef 100644
--- a/blocksuite/framework/std/src/gfx/tool/tool-controller.ts
+++ b/blocksuite/framework/std/src/gfx/tool/tool-controller.ts
@@ -10,10 +10,10 @@ import type { GfxController } from '../controller.js';
import { GfxExtension, GfxExtensionIdentifier } from '../extension.js';
import {
type BaseTool,
- type GfxToolsFullOptionValue,
- type GfxToolsMap,
- type GfxToolsOption,
ToolIdentifier,
+ type ToolOptions,
+ type ToolOptionWithType,
+ type ToolType,
} from './tool.js';
type BuiltInHookEvent = {
@@ -23,9 +23,9 @@ type BuiltInHookEvent = {
type BuiltInEventMap = {
beforeToolUpdate: BuiltInHookEvent<{
- toolName: keyof GfxToolsMap;
+ toolName: string;
}>;
- toolUpdate: BuiltInHookEvent<{ toolName: keyof GfxToolsMap }>;
+ toolUpdate: BuiltInHookEvent<{ toolName: string }>;
};
type BuiltInSlotContext = {
@@ -84,13 +84,11 @@ export class ToolController extends GfxExtension {
private readonly _disposableGroup = new DisposableGroup();
- private readonly _toolOption$ = new Signal(
- {} as GfxToolsFullOptionValue
- );
+ private readonly _toolOption$ = new Signal({});
private readonly _tools = new Map();
- readonly currentToolName$ = new Signal();
+ readonly currentToolName$ = new Signal();
readonly dragging$ = new Signal(false);
@@ -157,23 +155,11 @@ export class ToolController extends GfxExtension {
return {
peek() {
- const option = self._toolOption$.peek() as unknown as { type: string };
-
- if (!option.type) {
- option.type = '';
- }
-
- return option as GfxToolsFullOptionValue;
+ return self._toolOption$.peek();
},
- get value(): GfxToolsFullOptionValue {
- const option = self._toolOption$.value as unknown as { type: string };
-
- if (!option.type) {
- option.type = '';
- }
-
- return option as GfxToolsFullOptionValue;
+ get value() {
+ return self._toolOption$.value;
},
};
}
@@ -481,9 +467,16 @@ export class ToolController extends GfxExtension {
tools.mounted();
}
- get(key: K): GfxToolsMap[K] {
- return this._tools.get(key) as GfxToolsMap[K];
- }
+ get = (type: ToolType): T => {
+ const instance = this._tools.get(type.toolName) as T | undefined;
+ if (!instance) {
+ throw new BlockSuiteError(
+ BlockSuiteError.ErrorCode.ValueNotExists,
+ `Trying to get tool "${type.toolName}" is not registered`
+ );
+ }
+ return instance;
+ };
override mounted(): void {
const { addHook } = this._initializeEvents();
@@ -499,24 +492,11 @@ export class ToolController extends GfxExtension {
});
}
- setTool(toolName: GfxToolsFullOptionValue, ...args: [void]): void;
- setTool(
- toolName: K,
- ...args: K extends keyof GfxToolsOption
- ? [option: GfxToolsOption[K]]
- : [void]
- ): void;
- setTool(
- toolName: K | GfxToolsFullOptionValue,
- ...args: K extends keyof GfxToolsOption
- ? [option: GfxToolsOption[K]]
- : [void]
- ): void {
- const option = typeof toolName === 'string' ? args[0] : toolName;
- const toolNameStr =
- typeof toolName === 'string'
- ? toolName
- : ((toolName as { type: string }).type as K);
+ setTool = (
+ toolType: ToolType,
+ options?: ToolOptions
+ ): void => {
+ const toolNameStr = toolType.toolName;
const beforeUpdateCtx = this._createBuiltInHookCtx('beforeToolUpdate', {
toolName: toolNameStr,
@@ -541,18 +521,18 @@ export class ToolController extends GfxExtension {
);
}
- currentTool.activatedOption = option ?? {};
+ currentTool.activatedOption = options ?? {};
this._toolOption$.value = {
- ...currentTool.activatedOption,
- type: toolNameStr,
- } as GfxToolsFullOptionValue;
+ toolType,
+ options: currentTool.activatedOption,
+ };
currentTool.activate(currentTool.activatedOption);
const afterUpdateCtx = this._createBuiltInHookCtx('toolUpdate', {
toolName: toolNameStr,
});
this._builtInHookSlot.next(afterUpdateCtx.slotCtx);
- }
+ };
override unmounted(): void {
this.currentTool$.peek()?.deactivate();
diff --git a/blocksuite/framework/std/src/gfx/tool/tool.ts b/blocksuite/framework/std/src/gfx/tool/tool.ts
index e86bc0ff7b..795c329ef5 100644
--- a/blocksuite/framework/std/src/gfx/tool/tool.ts
+++ b/blocksuite/framework/std/src/gfx/tool/tool.ts
@@ -111,6 +111,19 @@ export abstract class BaseTool<
export const ToolIdentifier = createIdentifier('GfxTool');
+export type ToolType = {
+ new (gfx: GfxController): T;
+ toolName: string;
+};
+
+export type ToolOptions =
+ T extends BaseTool ? O : never;
+
+export type ToolOptionWithType = {
+ toolType?: ToolType;
+ options?: ToolOptions;
+};
+
export interface GfxToolsMap {}
export interface GfxToolsOption {}
diff --git a/blocksuite/integration-test/src/__tests__/edgeless/color-picker.spec.ts b/blocksuite/integration-test/src/__tests__/edgeless/color-picker.spec.ts
index 6f898c1cf7..66e991e0e6 100644
--- a/blocksuite/integration-test/src/__tests__/edgeless/color-picker.spec.ts
+++ b/blocksuite/integration-test/src/__tests__/edgeless/color-picker.spec.ts
@@ -2,6 +2,7 @@ import '@toeverything/theme/style.css';
import '@blocksuite/affine/gfx/pointer';
import type { EdgelessRootBlockComponent } from '@blocksuite/affine/blocks/root';
+import { DefaultTool } from '@blocksuite/affine/blocks/surface';
import { ColorScheme } from '@blocksuite/affine/model';
import { ThemeProvider } from '@blocksuite/affine/shared/services';
import { beforeEach, describe, expect, test } from 'vitest';
@@ -17,7 +18,7 @@ describe('theme service', () => {
edgeless = getDocRootBlock(doc, editor, 'edgeless');
- edgeless.gfx.tool.setTool('default');
+ edgeless.gfx.tool.setTool(DefaultTool);
const themeService = edgeless.gfx.std.get(ThemeProvider);
themeService.theme$.value = ColorScheme.Light;
diff --git a/blocksuite/integration-test/src/__tests__/edgeless/tools.spec.ts b/blocksuite/integration-test/src/__tests__/edgeless/tools.spec.ts
index 898f1c1b6e..6a56ec789c 100644
--- a/blocksuite/integration-test/src/__tests__/edgeless/tools.spec.ts
+++ b/blocksuite/integration-test/src/__tests__/edgeless/tools.spec.ts
@@ -1,5 +1,8 @@
import type { EdgelessRootBlockComponent } from '@blocksuite/affine/blocks/root';
-import type { SurfaceBlockComponent } from '@blocksuite/affine/blocks/surface';
+import {
+ DefaultTool,
+ type SurfaceBlockComponent,
+} from '@blocksuite/affine/blocks/surface';
import { beforeEach, describe, expect, test } from 'vitest';
import { click, drag, wait } from '../utils/common.js';
@@ -18,7 +21,7 @@ describe('default tool', () => {
surface = getSurface(window.doc, window.editor);
service = edgeless.service;
- edgeless.gfx.tool.setTool('default');
+ edgeless.gfx.tool.setTool(DefaultTool);
return cleanup;
});
diff --git a/blocksuite/playground/apps/_common/components/starter-debug-menu.ts b/blocksuite/playground/apps/_common/components/starter-debug-menu.ts
index e4fe670a5d..25d29e58bf 100644
--- a/blocksuite/playground/apps/_common/components/starter-debug-menu.ts
+++ b/blocksuite/playground/apps/_common/components/starter-debug-menu.ts
@@ -16,6 +16,7 @@ import '@shoelace-style/shoelace/dist/themes/light.css';
import '@shoelace-style/shoelace/dist/themes/dark.css';
import './left-side-panel.js';
+import { PresentTool } from '@blocksuite/affine/blocks/frame';
import { ExportManager } from '@blocksuite/affine/blocks/surface';
import { toast } from '@blocksuite/affine/components/toast';
import { StoreExtensionManagerIdentifier } from '@blocksuite/affine/ext-loader';
@@ -554,7 +555,7 @@ export class StarterDebugMenu extends ShadowlessElement {
private _present() {
if (!this.editor.std || !this.editor.host) return;
const gfx = this.editor.std.get(GfxControllerIdentifier);
- gfx.tool.setTool('frameNavigator', {
+ gfx.tool.setTool(PresentTool, {
mode: 'fit',
});
}
diff --git a/packages/frontend/core/src/blocksuite/ai/actions/edgeless-handler.ts b/packages/frontend/core/src/blocksuite/ai/actions/edgeless-handler.ts
index c3a933968a..3abe0f51b6 100644
--- a/packages/frontend/core/src/blocksuite/ai/actions/edgeless-handler.ts
+++ b/packages/frontend/core/src/blocksuite/ai/actions/edgeless-handler.ts
@@ -1,4 +1,5 @@
import { splitElements } from '@blocksuite/affine/blocks/root';
+import { DefaultTool } from '@blocksuite/affine/blocks/surface';
import type * as PointerEffect from '@blocksuite/affine/gfx/pointer';
import {
CodeBlockModel,
@@ -358,7 +359,7 @@ function updateEdgelessAIPanelConfig<
config.hideCallback = () => {
aiPanel.updateComplete
.finally(() => {
- edgelessCopilot.gfx.tool.setTool('default');
+ edgelessCopilot.gfx.tool.setTool(DefaultTool);
edgelessCopilot.gfx.selection.set({
elements: [],
editing: false,
@@ -431,7 +432,7 @@ export function actionToHandler(
if (!referenceElement) {
const gfx = host.std.get(GfxControllerIdentifier);
- gfx?.tool.setTool({ type: 'default' });
+ gfx?.tool.setTool(DefaultTool);
edgelessCopilot.lockToolbar(false);
return;
}
diff --git a/packages/frontend/core/src/blocksuite/ai/tool/copilot-tool.ts b/packages/frontend/core/src/blocksuite/ai/tool/copilot-tool.ts
index 6984abc9d1..4a0c3c658b 100644
--- a/packages/frontend/core/src/blocksuite/ai/tool/copilot-tool.ts
+++ b/packages/frontend/core/src/blocksuite/ai/tool/copilot-tool.ts
@@ -1,4 +1,5 @@
/* oxlint-disable @typescript-eslint/no-non-null-assertion */
+import { DefaultTool } from '@blocksuite/affine/blocks/surface';
import { IS_MAC } from '@blocksuite/affine/global/env';
import {
Bound,
@@ -67,7 +68,7 @@ export class CopilotTool extends BaseTool {
this._dragging = false;
this.dragStartPoint = [0, 0];
this.dragLastPoint = [0, 0];
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
}
override activate(): void {
@@ -129,7 +130,7 @@ export class CopilotTool extends BaseTool {
: evt.raw.ctrlKey);
if (useCopilot) {
- this.controller.setTool('copilot');
+ this.controller.setTool(CopilotTool);
return false;
}
@@ -143,7 +144,7 @@ export class CopilotTool extends BaseTool {
return;
}
- this.gfx.tool.setTool('default');
+ this.gfx.tool.setTool(DefaultTool);
}
updateDragPointsWith(selectedElements: GfxModel[], padding = 0) {
diff --git a/packages/frontend/core/src/blocksuite/ai/widgets/edgeless-copilot-panel/toolbar-entry.ts b/packages/frontend/core/src/blocksuite/ai/widgets/edgeless-copilot-panel/toolbar-entry.ts
index 1a908bdb40..8d61e61ea0 100644
--- a/packages/frontend/core/src/blocksuite/ai/widgets/edgeless-copilot-panel/toolbar-entry.ts
+++ b/packages/frontend/core/src/blocksuite/ai/widgets/edgeless-copilot-panel/toolbar-entry.ts
@@ -10,7 +10,7 @@ import { css, html, LitElement } from 'lit';
import { property } from 'lit/decorators.js';
import type { AIItemGroupConfig } from '../../components/ai-item/types';
-import type { CopilotTool } from '../../tool/copilot-tool';
+import { CopilotTool } from '../../tool/copilot-tool';
export class EdgelessCopilotToolbarEntry extends WithDisposable(LitElement) {
static override styles = css`
@@ -51,7 +51,7 @@ export class EdgelessCopilotToolbarEntry extends WithDisposable(LitElement) {
}
});
- this._gfx.tool.setTool('copilot');
+ this._gfx.tool.setTool(CopilotTool);
(this._gfx.tool.currentTool$.peek() as CopilotTool).updateSelectionWith(
Array.from(toBeSelected),
10
diff --git a/packages/frontend/core/src/blocksuite/ai/widgets/edgeless-copilot/index.ts b/packages/frontend/core/src/blocksuite/ai/widgets/edgeless-copilot/index.ts
index 1e3a463d9b..8c11352f45 100644
--- a/packages/frontend/core/src/blocksuite/ai/widgets/edgeless-copilot/index.ts
+++ b/packages/frontend/core/src/blocksuite/ai/widgets/edgeless-copilot/index.ts
@@ -1,3 +1,4 @@
+import { CopilotTool } from '@affine/core/blocksuite/ai/tool/copilot-tool';
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine/blocks/surface';
import {
Bound,
@@ -205,7 +206,7 @@ export class EdgelessCopilotWidget extends WidgetComponent {
override connectedCallback(): void {
super.connectedCallback();
- const CopilotSelectionTool = this.gfx.tool.get('copilot');
+ const CopilotSelectionTool = this.gfx.tool.get(CopilotTool);
this._disposables.add(
CopilotSelectionTool.draggingAreaUpdated.subscribe(shouldShowPanel => {
diff --git a/packages/frontend/core/src/modules/editor/entities/editor.ts b/packages/frontend/core/src/modules/editor/entities/editor.ts
index 4abbcc1068..d44e4c9ca5 100644
--- a/packages/frontend/core/src/modules/editor/entities/editor.ts
+++ b/packages/frontend/core/src/modules/editor/entities/editor.ts
@@ -1,5 +1,7 @@
import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor';
import type { DefaultOpenProperty } from '@affine/core/components/doc-properties';
+import { PresentTool } from '@blocksuite/affine/blocks/frame';
+import { DefaultTool } from '@blocksuite/affine/blocks/surface';
import type { DocTitle } from '@blocksuite/affine/fragments/doc-title';
import type { DocMode, ReferenceParams } from '@blocksuite/affine/model';
import { HighlightSelection } from '@blocksuite/affine/shared/selection';
@@ -75,9 +77,11 @@ export class Editor extends Entity {
GfxControllerIdentifier
);
if (!gfx) return;
- gfx.tool.setTool({
- type: !this.isPresenting$.value ? 'frameNavigator' : 'default',
- });
+ if (!this.isPresenting$.value) {
+ gfx.tool.setTool(PresentTool);
+ } else {
+ gfx.tool.setTool(DefaultTool);
+ }
}
setSelector(selector: EditorSelector | undefined) {
diff --git a/tests/blocksuite/e2e/utils/actions/edgeless.ts b/tests/blocksuite/e2e/utils/actions/edgeless.ts
index 812839b80b..b6bfb9cd65 100644
--- a/tests/blocksuite/e2e/utils/actions/edgeless.ts
+++ b/tests/blocksuite/e2e/utils/actions/edgeless.ts
@@ -1,8 +1,11 @@
import '../declare-test-window.js';
-import type { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
-import type { IPoint, IVec } from '@blocksuite/global/gfx';
-import { sleep } from '@blocksuite/global/utils';
+import { ConnectorTool } from '@blocksuite/affine/gfx/connector';
+import { ShapeTool } from '@blocksuite/affine/gfx/shape';
+import type { IPoint, IVec } from '@blocksuite/affine/global/gfx';
+import { sleep } from '@blocksuite/affine/global/utils';
+import type { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine/model';
+import type { ToolOptions } from '@blocksuite/affine/std/gfx';
import type { Locator, Page } from '@playwright/test';
import { expect } from '@playwright/test';
@@ -394,21 +397,23 @@ export async function assertEdgelessShapeType(page: Page, type: ShapeName) {
throw new Error('Missing edgeless page');
}
const tool = container.gfx.tool.currentToolOption$.peek();
- if (tool.type !== 'shape') throw new Error('Expected shape tool');
+ if (tool.toolType?.toolName !== 'shape')
+ throw new Error('Expected shape tool');
- return tool.shapeName;
+ return (tool.options as ToolOptions).shapeName;
});
expect(type).toEqual(curType);
}
-export async function assertEdgelessTool(page: Page, mode: EdgelessTool) {
+export async function assertEdgelessTool(page: Page, mode: string) {
+ await page.waitForTimeout(1000);
const type = await page.evaluate(() => {
const container = document.querySelector('affine-edgeless-root');
if (!container) {
throw new Error('Missing edgeless page');
}
- return container.gfx.tool.currentToolOption$.peek().type;
+ return container.gfx.tool.currentTool$.peek()?.toolName;
});
expect(type).toEqual(mode);
}
@@ -417,17 +422,21 @@ export async function assertEdgelessConnectorToolMode(
page: Page,
mode: ConnectorMode
) {
- const tool = await page.evaluate(() => {
+ const [toolName, toolOptions] = await page.evaluate(() => {
const container = document.querySelector('affine-edgeless-root');
if (!container) {
throw new Error('Missing edgeless page');
}
- return container.gfx.tool.currentToolOption$.peek();
+ const tool = container.gfx.tool.currentToolOption$.peek();
+ return [
+ tool.toolType?.toolName as string | undefined,
+ tool.options as ToolOptions,
+ ];
});
- if (tool.type !== 'connector') {
+ if (toolName !== 'connector') {
throw new Error('Expected connector tool');
}
- expect(tool.mode).toEqual(mode);
+ expect((toolOptions as ToolOptions).mode).toEqual(mode);
}
export async function getEdgelessBlockChild(page: Page) {
diff --git a/tools/utils/src/workspace.gen.ts b/tools/utils/src/workspace.gen.ts
index fe7361e992..464c75d8d4 100644
--- a/tools/utils/src/workspace.gen.ts
+++ b/tools/utils/src/workspace.gen.ts
@@ -212,6 +212,7 @@ export const PackageList = [
'blocksuite/affine/blocks/surface',
'blocksuite/affine/components',
'blocksuite/affine/ext-loader',
+ 'blocksuite/affine/gfx/pointer',
'blocksuite/affine/inlines/reference',
'blocksuite/affine/model',
'blocksuite/affine/rich-text',
@@ -247,6 +248,7 @@ export const PackageList = [
'blocksuite/affine/blocks/surface',
'blocksuite/affine/components',
'blocksuite/affine/ext-loader',
+ 'blocksuite/affine/gfx/pointer',
'blocksuite/affine/model',
'blocksuite/affine/shared',
'blocksuite/affine/widgets/edgeless-toolbar',
@@ -598,6 +600,7 @@ export const PackageList = [
'blocksuite/affine/components',
'blocksuite/affine/ext-loader',
'blocksuite/affine/gfx/connector',
+ 'blocksuite/affine/gfx/pointer',
'blocksuite/affine/gfx/shape',
'blocksuite/affine/gfx/text',
'blocksuite/affine/model',
@@ -886,6 +889,7 @@ export const PackageList = [
location: 'blocksuite/affine/widgets/frame-title',
name: '@blocksuite/affine-widget-frame-title',
workspaceDependencies: [
+ 'blocksuite/affine/blocks/surface',
'blocksuite/affine/components',
'blocksuite/affine/ext-loader',
'blocksuite/affine/model',
diff --git a/yarn.lock b/yarn.lock
index 49085d7043..accb8d539b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2668,6 +2668,7 @@ __metadata:
"@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-ext-loader": "workspace:*"
+ "@blocksuite/affine-gfx-pointer": "workspace:*"
"@blocksuite/affine-inline-reference": "workspace:*"
"@blocksuite/affine-model": "workspace:*"
"@blocksuite/affine-rich-text": "workspace:*"
@@ -2699,6 +2700,7 @@ __metadata:
"@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-ext-loader": "workspace:*"
+ "@blocksuite/affine-gfx-pointer": "workspace:*"
"@blocksuite/affine-model": "workspace:*"
"@blocksuite/affine-shared": "workspace:*"
"@blocksuite/affine-widget-edgeless-toolbar": "workspace:*"
@@ -3282,6 +3284,7 @@ __metadata:
"@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-ext-loader": "workspace:*"
"@blocksuite/affine-gfx-connector": "workspace:*"
+ "@blocksuite/affine-gfx-pointer": "workspace:*"
"@blocksuite/affine-gfx-shape": "workspace:*"
"@blocksuite/affine-gfx-text": "workspace:*"
"@blocksuite/affine-model": "workspace:*"
@@ -3843,6 +3846,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@blocksuite/affine-widget-frame-title@workspace:blocksuite/affine/widgets/frame-title"
dependencies:
+ "@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-ext-loader": "workspace:*"
"@blocksuite/affine-model": "workspace:*"