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:*"