refactor(editor): reduce getService (#10100)

This commit is contained in:
Saul-Mirone
2025-02-11 12:26:01 +00:00
parent dbf0f9dc20
commit 6b78d2dcf2
33 changed files with 189 additions and 300 deletions
@@ -76,10 +76,6 @@ export class BlockRenderer
return this.host?.doc.getBlock(this.rowId)?.model; return this.host?.doc.getBlock(this.rowId)?.model;
} }
get service() {
return this.host.std.getService('affine:database');
}
override connectedCallback() { override connectedCallback() {
super.connectedCallback(); super.connectedCallback();
if (this.model && this.model.text) { if (this.model && this.model.text) {
@@ -123,10 +123,6 @@ abstract class BaseTextCell extends BaseCellRenderer<Text> {
return this.host?.std.get(DefaultInlineManagerExtension.identifier); return this.host?.std.get(DefaultInlineManagerExtension.identifier);
} }
get service() {
return this.host?.std.getService('affine:database');
}
get topContenteditableElement() { get topContenteditableElement() {
const databaseBlock = const databaseBlock =
this.closest<DatabaseBlockComponent>('affine-database'); this.closest<DatabaseBlockComponent>('affine-database');
@@ -40,10 +40,6 @@ export function toEdgelessEmbedBlock<
return Bound.deserialize(this.model.xywh); return Bound.deserialize(this.model.xywh);
} }
get rootService() {
return this.std.getService('affine:page');
}
_handleClick(_: MouseEvent): void { _handleClick(_: MouseEvent): void {
return; return;
} }
@@ -36,12 +36,6 @@ export class EmbedEdgelessLinkedDocBlockComponent extends toEdgelessEmbedBlock(
bound.w = EMBED_CARD_WIDTH[style]; bound.w = EMBED_CARD_WIDTH[style];
bound.h = EMBED_CARD_HEIGHT[style]; bound.h = EMBED_CARD_HEIGHT[style];
const edgelessService = this.rootService;
if (!edgelessService) {
return;
}
const { addBlock } = this.std.get(EdgelessCRUDIdentifier); const { addBlock } = this.std.get(EdgelessCRUDIdentifier);
const surface = this.gfx.surface ?? undefined; const surface = this.gfx.surface ?? undefined;
const newId = addBlock( const newId = addBlock(
@@ -67,10 +61,6 @@ export class EmbedEdgelessLinkedDocBlockComponent extends toEdgelessEmbedBlock(
doc.deleteBlock(this.model); doc.deleteBlock(this.model);
}; };
get rootService() {
return this.std.getService('affine:page');
}
protected override _handleClick(evt: MouseEvent): void { protected override _handleClick(evt: MouseEvent): void {
if (isNewTabTrigger(evt)) { if (isNewTabTrigger(evt)) {
this.open({ openMode: 'open-in-new-tab', event: evt }); this.open({ openMode: 'open-in-new-tab', event: evt });
@@ -123,16 +123,11 @@ export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock(
override convertToCard = (aliasInfo?: AliasInfo) => { override convertToCard = (aliasInfo?: AliasInfo) => {
const { id, doc, caption, xywh } = this.model; const { id, doc, caption, xywh } = this.model;
const edgelessService = this.rootService;
const style = 'vertical'; const style = 'vertical';
const bound = Bound.deserialize(xywh); const bound = Bound.deserialize(xywh);
bound.w = EMBED_CARD_WIDTH[style]; bound.w = EMBED_CARD_WIDTH[style];
bound.h = EMBED_CARD_HEIGHT[style]; bound.h = EMBED_CARD_HEIGHT[style];
if (!edgelessService) {
return;
}
const { addBlock } = this.std.get(EdgelessCRUDIdentifier); const { addBlock } = this.std.get(EdgelessCRUDIdentifier);
const surface = this.gfx.surface ?? undefined; const surface = this.gfx.surface ?? undefined;
const newId = addBlock( const newId = addBlock(
@@ -159,10 +154,6 @@ export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock(
doc.deleteBlock(this.model); doc.deleteBlock(this.model);
}; };
get rootService() {
return this.std.getService('affine:page');
}
override renderGfxBlock() { override renderGfxBlock() {
const { style, xywh } = this.model; const { style, xywh } = this.model;
const bound = Bound.deserialize(xywh); const bound = Bound.deserialize(xywh);
@@ -351,10 +351,6 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
.title(pageId, { params, referenced: true }); .title(pageId, { params, referenced: true });
}); });
private get _rootService() {
return this.std.getService('affine:page');
}
get blockState() { get blockState() {
return { return {
isLoading: this._loading, isLoading: this._loading,
@@ -504,8 +500,8 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
}) })
); );
if (this._rootService && !this.linkedMode) { if (!this.linkedMode) {
const docMode = this._rootService.std.get(DocModeProvider); const docMode = this.std.get(DocModeProvider);
this.syncedDocMode = docMode.getPrimaryMode(this.model.pageId); this.syncedDocMode = docMode.getPrimaryMode(this.model.pageId);
this._isEmptySyncedDoc = isEmptyDoc(this.syncedDoc, this.editorMode); this._isEmptySyncedDoc = isEmptyDoc(this.syncedDoc, this.editorMode);
this.disposables.add( this.disposables.add(
@@ -34,10 +34,6 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
return this.gfx.viewport.zoom; return this.gfx.viewport.zoom;
} }
get rootService() {
return this.std.getService('affine:page');
}
private _collapsedContent() { private _collapsedContent() {
if (!this._isShowCollapsedContent) { if (!this._isShowCollapsedContent) {
return nothing; return nothing;
@@ -1,5 +1,7 @@
import type { Command } from '@blocksuite/block-std'; import type { Command } from '@blocksuite/block-std';
import { SurfaceBlockService } from '../surface-service';
/** /**
* Re-associate bindings for block that have been converted. * Re-associate bindings for block that have been converted.
* *
@@ -11,7 +13,7 @@ export const reassociateConnectorsCommand: Command<{
newId: string; newId: string;
}> = (ctx, next) => { }> = (ctx, next) => {
const { oldId, newId } = ctx; const { oldId, newId } = ctx;
const service = ctx.std.getService('affine:surface'); const service = ctx.std.get(SurfaceBlockService);
if (!oldId || !newId || !service) { if (!oldId || !newId || !service) {
next(); next();
return; return;
@@ -2,8 +2,10 @@ import type { SurfaceElementModelMap } from '@blocksuite/affine-model';
import { EditPropsStore } from '@blocksuite/affine-shared/services'; import { EditPropsStore } from '@blocksuite/affine-shared/services';
import { type BlockStdScope, StdIdentifier } from '@blocksuite/block-std'; import { type BlockStdScope, StdIdentifier } from '@blocksuite/block-std';
import { import {
GfxBlockElementModel,
GfxControllerIdentifier, GfxControllerIdentifier,
type GfxModel, type GfxModel,
isGfxGroupCompatibleModel,
} from '@blocksuite/block-std/gfx'; } from '@blocksuite/block-std/gfx';
import { type Container, createIdentifier } from '@blocksuite/global/di'; import { type Container, createIdentifier } from '@blocksuite/global/di';
import { type BlockModel, Extension } from '@blocksuite/store'; import { type BlockModel, Extension } from '@blocksuite/store';
@@ -155,4 +157,25 @@ export class EdgelessCRUDExtension extends Extension {
} }
return this._surface.getElementsByType(type); return this._surface.getElementsByType(type);
} }
removeElement(id: string | GfxModel) {
id = typeof id === 'string' ? id : id.id;
const el = this.getElementById(id);
if (isGfxGroupCompatibleModel(el)) {
el.childIds.forEach(childId => {
this.removeElement(childId);
});
}
if (el instanceof GfxBlockElementModel) {
this.std.store.deleteBlock(el);
return;
}
if (this._surface?.hasElementById(id)) {
this._surface.deleteElement(id);
return;
}
}
} }
@@ -13,7 +13,10 @@ import { query } from 'lit/decorators.js';
import { ConnectorElementModel } from './element-model/index.js'; import { ConnectorElementModel } from './element-model/index.js';
import { CanvasRenderer } from './renderer/canvas-renderer.js'; import { CanvasRenderer } from './renderer/canvas-renderer.js';
import type { ElementRenderer } from './renderer/elements/index.js'; import {
type ElementRenderer,
elementRenderers,
} from './renderer/elements/index.js';
import { OverlayIdentifier } from './renderer/overlay.js'; import { OverlayIdentifier } from './renderer/overlay.js';
import type { SurfaceBlockModel } from './surface-model.js'; import type { SurfaceBlockModel } from './surface-model.js';
import type { SurfaceBlockService } from './surface-service.js'; import type { SurfaceBlockService } from './surface-service.js';
@@ -126,10 +129,6 @@ export class SurfaceBlockComponent extends BlockComponent<
this._renderer?.refresh(); this._renderer?.refresh();
}; };
private get _edgelessService() {
return this.std.getService('affine:page') as unknown as SurfaceContext;
}
private get _gfx() { private get _gfx() {
return this.std.get(GfxControllerIdentifier); return this.std.get(GfxControllerIdentifier);
} }
@@ -179,12 +178,12 @@ export class SurfaceBlockComponent extends BlockComponent<
property, property,
themeService.edgelessTheme themeService.edgelessTheme
), ),
selectedElements: () => this._edgelessService.selection.selectedIds, selectedElements: () => gfx.selection.selectedIds,
}, },
onStackingCanvasCreated(canvas) { onStackingCanvasCreated(canvas) {
canvas.className = 'indexable-canvas'; canvas.className = 'indexable-canvas';
}, },
elementRenderers: this._edgelessService.elementRenderers, elementRenderers,
surfaceModel: this.model, surfaceModel: this.model,
}); });
@@ -205,7 +204,7 @@ export class SurfaceBlockComponent extends BlockComponent<
}) })
); );
this._disposables.add( this._disposables.add(
this._edgelessService.selection.slots.updated.on(() => { gfx.selection.slots.updated.on(() => {
this._renderer.refresh(); this._renderer.refresh();
}) })
); );
@@ -1,4 +1,5 @@
import type { CodeBlockConfig } from '@blocksuite/affine-block-code'; import type { CodeBlockConfig } from '@blocksuite/affine-block-code';
import { ParagraphBlockService } from '@blocksuite/affine-block-paragraph';
import { import {
type ReferenceNodeConfig, type ReferenceNodeConfig,
ReferenceNodeConfigIdentifier, ReferenceNodeConfigIdentifier,
@@ -100,7 +101,7 @@ export class MobileSpecsPatches extends LifeCycleWatcher {
override mounted() { override mounted() {
// remove slash placeholder for mobile: `type / ...` // remove slash placeholder for mobile: `type / ...`
{ {
const paragraphService = this.std.getService('affine:paragraph'); const paragraphService = this.std.get(ParagraphBlockService);
if (!paragraphService) return; if (!paragraphService) return;
paragraphService.placeholderGenerator = model => { paragraphService.placeholderGenerator = model => {
@@ -5,6 +5,7 @@ import {
type EditorHost, type EditorHost,
ShadowlessElement, ShadowlessElement,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { import {
Bound, Bound,
debounce, debounce,
@@ -19,7 +20,6 @@ import { styleMap } from 'lit/directives/style-map.js';
import { SpecProvider } from '../../../../_specs/index.js'; import { SpecProvider } from '../../../../_specs/index.js';
import type { EdgelessRootPreviewBlockComponent } from '../../edgeless-root-preview-block.js'; import type { EdgelessRootPreviewBlockComponent } from '../../edgeless-root-preview-block.js';
import type { EdgelessRootService } from '../../edgeless-root-service.js';
const DEFAULT_PREVIEW_CONTAINER_WIDTH = 280; const DEFAULT_PREVIEW_CONTAINER_WIDTH = 280;
const DEFAULT_PREVIEW_CONTAINER_HEIGHT = 166; const DEFAULT_PREVIEW_CONTAINER_HEIGHT = 166;
@@ -144,12 +144,9 @@ export class FramePreview extends WithDisposable(ShadowlessElement) {
if (!previewEditorHost) return; if (!previewEditorHost) return;
const edgelessService = previewEditorHost.std.getService( const { viewport } = previewEditorHost.std.get(GfxControllerIdentifier);
'affine:page'
) as EdgelessRootService;
const frameBound = Bound.deserialize(this.frame.xywh); const frameBound = Bound.deserialize(this.frame.xywh);
edgelessService.viewport.setViewportByBound(frameBound); viewport.setViewportByBound(frameBound);
} }
private _renderSurfaceContent() { private _renderSurfaceContent() {
@@ -1,5 +1,6 @@
import type { AIError } from '@blocksuite/affine-components/ai-item'; import type { AIError } from '@blocksuite/affine-components/ai-item';
import { import {
DocModeProvider,
NotificationProvider, NotificationProvider,
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
@@ -8,6 +9,7 @@ import {
stopPropagation, stopPropagation,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { WidgetComponent } from '@blocksuite/block-std'; import { WidgetComponent } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
import type { BaseSelection } from '@blocksuite/store'; import type { BaseSelection } from '@blocksuite/store';
import { import {
@@ -24,8 +26,6 @@ import { css, html, nothing, type PropertyValues } from 'lit';
import { property, query } from 'lit/decorators.js'; import { property, query } from 'lit/decorators.js';
import { choose } from 'lit/directives/choose.js'; import { choose } from 'lit/directives/choose.js';
import type { EdgelessRootService } from '../../edgeless/edgeless-root-service.js';
import { PageRootService } from '../../page/page-root-service.js';
import { AFFINE_FORMAT_BAR_WIDGET } from '../format-bar/format-bar.js'; import { AFFINE_FORMAT_BAR_WIDGET } from '../format-bar/format-bar.js';
import { import {
AFFINE_VIEWPORT_OVERLAY_WIDGET, AFFINE_VIEWPORT_OVERLAY_WIDGET,
@@ -363,12 +363,13 @@ export class AffineAIPanelWidget extends WidgetComponent {
): Partial<ComputePositionConfig> { ): Partial<ComputePositionConfig> {
let rootBoundary: Rect | undefined; let rootBoundary: Rect | undefined;
{ {
const rootService = this.host.std.getService('affine:page'); const docModeProvider = this.host.std.get(DocModeProvider);
if (rootService instanceof PageRootService) { if (docModeProvider.getEditorMode() === 'page') {
rootBoundary = undefined; rootBoundary = undefined;
} else { } else {
const gfx = this.host.std.get(GfxControllerIdentifier);
// TODO circular dependency: instanceof EdgelessRootService // TODO circular dependency: instanceof EdgelessRootService
const viewport = (rootService as EdgelessRootService).viewport; const viewport = gfx.viewport;
rootBoundary = { rootBoundary = {
x: viewport.left, x: viewport.left,
y: viewport.top, y: viewport.top,
@@ -19,6 +19,7 @@ import './left-side-panel.js';
import { NotionHtmlAdapter } from '@blocksuite/affine-shared/adapters'; import { NotionHtmlAdapter } from '@blocksuite/affine-shared/adapters';
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
import { ShadowlessElement } from '@blocksuite/block-std'; import { ShadowlessElement } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { import {
ColorScheme, ColorScheme,
ColorVariables, ColorVariables,
@@ -28,7 +29,6 @@ import {
type DocMode, type DocMode,
DocModeProvider, DocModeProvider,
download, download,
EdgelessRootService,
ExportManager, ExportManager,
FontFamilyVariables, FontFamilyVariables,
HtmlAdapterFactoryIdentifier, HtmlAdapterFactoryIdentifier,
@@ -210,10 +210,6 @@ export class StarterDebugMenu extends ShadowlessElement {
this.editor.mode = value; this.editor.mode = value;
} }
get rootService() {
return this.editor.std?.getService('affine:page');
}
private _addNote() { private _addNote() {
const rootModel = this.doc.root; const rootModel = this.doc.root;
if (!rootModel) return; if (!rootModel) return;
@@ -544,18 +540,8 @@ export class StarterDebugMenu extends ShadowlessElement {
private _present() { private _present() {
if (!this.editor.std || !this.editor.host) return; if (!this.editor.std || !this.editor.host) return;
const rootService = this.editor.std.getService('affine:page'); const gfx = this.editor.std.get(GfxControllerIdentifier);
if (!(rootService instanceof EdgelessRootService)) { gfx.tool.setTool('frameNavigator', {
toast(
this.editor.host,
'The presentation mode is only available on edgeless mode.',
3000
);
return;
}
const edgelessRootService = rootService as EdgelessRootService;
edgelessRootService?.gfx.tool.setTool('frameNavigator', {
mode: 'fit', mode: 'fit',
}); });
} }
@@ -1016,7 +1016,7 @@ export async function deleteAllConnectors(page: Page) {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
if (!container) throw new Error('container not found'); if (!container) throw new Error('container not found');
container.service.crud.getElementsByType('connector').forEach(c => { container.service.crud.getElementsByType('connector').forEach(c => {
container.service.removeElement(c.id); container.service.crud.removeElement(c.id);
}); });
}); });
} }
@@ -1,18 +1,16 @@
import { ChatHistoryOrder } from '@affine/graphql'; import { ChatHistoryOrder } from '@affine/graphql';
import { import {
BlockSelection, BlockSelection,
type BlockStdScope,
type EditorHost, type EditorHost,
TextSelection, TextSelection,
} from '@blocksuite/affine/block-std'; } from '@blocksuite/affine/block-std';
import type { import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
DocMode, import type { DocMode, ImageSelection } from '@blocksuite/affine/blocks';
EdgelessRootService,
ImageSelection,
RootService,
} from '@blocksuite/affine/blocks';
import { import {
BlocksUtils, BlocksUtils,
DocModeProvider, DocModeProvider,
EdgelessCRUDIdentifier,
EditPropsStore, EditPropsStore,
getSelectedBlocksCommand, getSelectedBlocksCommand,
NoteDisplayMode, NoteDisplayMode,
@@ -116,10 +114,10 @@ export async function constructRootChatBlockMessages(
return constructUserInfoWithMessages(forkMessages, userInfo); return constructUserInfoWithMessages(forkMessages, userInfo);
} }
function getViewportCenter(mode: DocMode, rootService: RootService) { function getViewportCenter(mode: DocMode, std: BlockStdScope) {
const center = { x: 400, y: 50 }; const center = { x: 400, y: 50 };
if (mode === 'page') { if (mode === 'page') {
const viewport = rootService.std.get(EditPropsStore).getStorage('viewport'); const viewport = std.get(EditPropsStore).getStorage('viewport');
if (viewport) { if (viewport) {
if ('xywh' in viewport) { if ('xywh' in viewport) {
const bound = Bound.deserialize(viewport.xywh); const bound = Bound.deserialize(viewport.xywh);
@@ -132,9 +130,9 @@ function getViewportCenter(mode: DocMode, rootService: RootService) {
} }
} else { } else {
// Else we should get latest viewport center from the edgeless root service // Else we should get latest viewport center from the edgeless root service
const edgelessService = rootService as EdgelessRootService; const viewport = std.get(GfxControllerIdentifier).viewport;
center.x = edgelessService.viewport.centerX; center.x = viewport.centerX;
center.y = edgelessService.viewport.centerY; center.y = viewport.centerY;
} }
return center; return center;
@@ -310,18 +308,11 @@ const SAVE_CHAT_TO_BLOCK_ACTION: ChatAction = {
return false; return false;
} }
const rootService = host.std.getService('affine:page');
const surfaceService = host.std.getService('affine:surface');
if (!rootService || !surfaceService) return false;
const notificationService = host.std.getOptional(NotificationProvider); const notificationService = host.std.getOptional(NotificationProvider);
const docModeService = host.std.get(DocModeProvider); const docModeService = host.std.get(DocModeProvider);
const { layer } = surfaceService; const layer = host.std.get(GfxControllerIdentifier).layer;
const curMode = docModeService.getEditorMode() || 'page'; const curMode = docModeService.getEditorMode() || 'page';
const viewportCenter = getViewportCenter( const viewportCenter = getViewportCenter(curMode, host.std);
curMode,
rootService as RootService
);
const newBlockIndex = layer.generateIndex(); const newBlockIndex = layer.generateIndex();
// If current mode is not edgeless, switch to edgeless mode first // If current mode is not edgeless, switch to edgeless mode first
if (curMode !== 'edgeless') { if (curMode !== 'edgeless') {
@@ -402,11 +393,9 @@ const ADD_TO_EDGELESS_AS_NOTE = {
handler: async (host: EditorHost, content: string) => { handler: async (host: EditorHost, content: string) => {
reportResponse('result:add-note'); reportResponse('result:add-note');
const { doc } = host; const { doc } = host;
const service = host.std.getService<EdgelessRootService>('affine:page');
if (!service) return;
const elements = service.selection.selectedElements;
const gfx = host.std.get(GfxControllerIdentifier);
const elements = gfx.selection.selectedElements;
const props: { displayMode: NoteDisplayMode; xywh?: SerializedXYWH } = { const props: { displayMode: NoteDisplayMode; xywh?: SerializedXYWH } = {
displayMode: NoteDisplayMode.EdgelessOnly, displayMode: NoteDisplayMode.EdgelessOnly,
}; };
@@ -423,7 +412,7 @@ const ADD_TO_EDGELESS_AS_NOTE = {
await insertFromMarkdown(host, content, doc, id, 0); await insertFromMarkdown(host, content, doc, id, 0);
service.selection.set({ gfx.selection.set({
elements: [id], elements: [id],
editing: false, editing: false,
}); });
@@ -488,10 +477,6 @@ const CREATE_AS_LINKED_DOC = {
return false; return false;
} }
const service = host.std.getService<EdgelessRootService>('affine:page');
if (!service) {
return false;
}
const docModeService = host.std.get(DocModeProvider); const docModeService = host.std.get(DocModeProvider);
const mode = docModeService.getEditorMode(); const mode = docModeService.getEditorMode();
if (mode !== 'edgeless') { if (mode !== 'edgeless') {
@@ -506,8 +491,9 @@ const CREATE_AS_LINKED_DOC = {
const noteId = newDoc.addBlock('affine:note', {}, rootId); const noteId = newDoc.addBlock('affine:note', {}, rootId);
await insertFromMarkdown(host, content, newDoc, noteId, 0); await insertFromMarkdown(host, content, newDoc, noteId, 0);
const gfx = host.std.get(GfxControllerIdentifier);
// Add a linked doc card to link to the new doc // Add a linked doc card to link to the new doc
const elements = service.selection.selectedElements; const elements = gfx.selection.selectedElements;
const width = 364; const width = 364;
const height = 390; const height = 390;
let x = 0; let x = 0;
@@ -523,12 +509,12 @@ const CREATE_AS_LINKED_DOC = {
// If the selected elements are not in the viewport, center the linked doc card // If the selected elements are not in the viewport, center the linked doc card
if (x === Number.POSITIVE_INFINITY || y === Number.POSITIVE_INFINITY) { if (x === Number.POSITIVE_INFINITY || y === Number.POSITIVE_INFINITY) {
const viewportCenter = getViewportCenter(mode, service); const viewportCenter = getViewportCenter(mode, host.std);
x = viewportCenter.x - width / 2; x = viewportCenter.x - width / 2;
y = viewportCenter.y - height / 2; y = viewportCenter.y - height / 2;
} }
service.crud.addBlock( host.std.get(EdgelessCRUDIdentifier).addBlock(
'affine:embed-linked-doc', 'affine:embed-linked-doc',
{ {
xywh: `[${x}, ${y}, ${width}, ${height}]`, xywh: `[${x}, ${y}, ${width}, ${height}]`,
@@ -4,7 +4,6 @@ import { type EditorHost } from '@blocksuite/affine/block-std';
import { import {
type AIItemGroupConfig, type AIItemGroupConfig,
createLitPortal, createLitPortal,
EdgelessRootService,
HoverController, HoverController,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { WithDisposable } from '@blocksuite/affine/global/utils'; import { WithDisposable } from '@blocksuite/affine/global/utils';
@@ -14,7 +13,6 @@ import { property, query } from 'lit/decorators.js';
import { ref } from 'lit/directives/ref.js'; import { ref } from 'lit/directives/ref.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import { getRootService } from '../../utils/selection-utils';
import type { ButtonSize } from './ask-ai-icon'; import type { ButtonSize } from './ask-ai-icon';
type toggleType = 'hover' | 'click'; type toggleType = 'hover' | 'click';
@@ -27,14 +25,6 @@ export type AskAIButtonOptions = {
}; };
export class AskAIButton extends WithDisposable(LitElement) { export class AskAIButton extends WithDisposable(LitElement) {
get _edgeless() {
const rootService = getRootService(this.host);
if (rootService instanceof EdgelessRootService) {
return rootService;
}
return null;
}
static override styles = css` static override styles = css`
.ask-ai-button { .ask-ai-button {
border-radius: 4px; border-radius: 4px;
@@ -1,7 +1,7 @@
import { type EditorHost } from '@blocksuite/affine/block-std'; import { type EditorHost } from '@blocksuite/affine/block-std';
import { import {
type AIItemGroupConfig, type AIItemGroupConfig,
EdgelessRootService, DocModeProvider,
scrollbarStyle, scrollbarStyle,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { WithDisposable } from '@blocksuite/affine/global/utils'; import { WithDisposable } from '@blocksuite/affine/global/utils';
@@ -9,8 +9,6 @@ import { css, html, LitElement } from 'lit';
import { property } from 'lit/decorators.js'; import { property } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import { getRootService } from '../../utils/selection-utils';
export class AskAIPanel extends WithDisposable(LitElement) { export class AskAIPanel extends WithDisposable(LitElement) {
static override styles = css` static override styles = css`
:host { :host {
@@ -50,12 +48,8 @@ export class AskAIPanel extends WithDisposable(LitElement) {
@property({ attribute: false }) @property({ attribute: false })
accessor minWidth = 330; accessor minWidth = 330;
get _edgeless() { get _isEdgelessMode() {
const rootService = getRootService(this.host); return this.host.std.get(DocModeProvider).getEditorMode() === 'edgeless';
if (rootService instanceof EdgelessRootService) {
return rootService;
}
return null;
} }
get _actionGroups() { get _actionGroups() {
@@ -66,7 +60,7 @@ export class AskAIPanel extends WithDisposable(LitElement) {
item.showWhen item.showWhen
? item.showWhen( ? item.showWhen(
this.host.command.chain(), this.host.command.chain(),
this._edgeless ? 'edgeless' : 'page', this._isEdgelessMode ? 'edgeless' : 'page',
this.host this.host
) )
: true : true
@@ -74,10 +74,6 @@ export class ChatCopyMore extends WithDisposable(LitElement) {
} }
`; `;
private get _rootService() {
return this.host.std.getService('affine:page');
}
private get _selectionValue() { private get _selectionValue() {
return this.host.selection.value; return this.host.selection.value;
} }
@@ -130,7 +126,6 @@ export class ChatCopyMore extends WithDisposable(LitElement) {
} }
private readonly _notifySuccess = (title: string) => { private readonly _notifySuccess = (title: string) => {
if (!this._rootService) return;
const notificationService = this.host.std.getOptional(NotificationProvider); const notificationService = this.host.std.getOptional(NotificationProvider);
notificationService?.notify({ notificationService?.notify({
title: title, title: title,
@@ -1,10 +1,10 @@
import type { EditorHost } from '@blocksuite/affine/block-std'; import type { EditorHost } from '@blocksuite/affine/block-std';
import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
import type { import type {
AffineAIPanelWidget, AffineAIPanelWidget,
AIItemConfig, AIItemConfig,
EdgelessCopilotWidget, EdgelessCopilotWidget,
EdgelessElementToolbarWidget, EdgelessElementToolbarWidget,
EdgelessRootService,
MindmapElementModel, MindmapElementModel,
ShapeElementModel, ShapeElementModel,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
@@ -35,17 +35,12 @@ import { AIProvider } from '../provider';
import { reportResponse } from '../utils/action-reporter'; import { reportResponse } from '../utils/action-reporter';
import { getAIPanelWidget } from '../utils/ai-widgets'; import { getAIPanelWidget } from '../utils/ai-widgets';
import type { AIContext } from '../utils/context'; import type { AIContext } from '../utils/context';
import { import { getEdgelessCopilotWidget, isMindMapRoot } from '../utils/edgeless';
getEdgelessCopilotWidget,
getService,
isMindMapRoot,
} from '../utils/edgeless';
import { preprocessHtml } from '../utils/html'; import { preprocessHtml } from '../utils/html';
import { fetchImageToFile } from '../utils/image'; import { fetchImageToFile } from '../utils/image';
import { import {
getCopilotSelectedElems, getCopilotSelectedElems,
getEdgelessRootFromEditor, getEdgelessRootFromEditor,
getEdgelessService,
getSurfaceElementFromEditor, getSurfaceElementFromEditor,
} from '../utils/selection-utils'; } from '../utils/selection-utils';
import { createTemplateJob } from '../utils/template-job'; import { createTemplateJob } from '../utils/template-job';
@@ -222,9 +217,9 @@ function insertBelow(
) { ) {
insertFromMarkdown(host, markdown, host.doc, parentId, index) insertFromMarkdown(host, markdown, host.doc, parentId, index)
.then(() => { .then(() => {
const service = getService(host); const gfx = host.std.get(GfxControllerIdentifier);
service.selection.set({ gfx.selection.set({
elements: [parentId], elements: [parentId],
editing: false, editing: false,
}); });
@@ -398,9 +393,8 @@ export function responseToExpandMindmap(host: EditorHost, ctx: AIContext) {
}); });
setTimeout(() => { setTimeout(() => {
const edgelessService = getEdgelessService(host); const gfx = host.std.get(GfxControllerIdentifier);
gfx.selection.set({
edgelessService.selection.set({
elements: [subtree.element.id], elements: [subtree.element.id],
editing: false, editing: false,
}); });
@@ -410,7 +404,7 @@ export function responseToExpandMindmap(host: EditorHost, ctx: AIContext) {
function responseToBrainstormMindmap(host: EditorHost, ctx: AIContext) { function responseToBrainstormMindmap(host: EditorHost, ctx: AIContext) {
const aiPanel = getAIPanelWidget(host); const aiPanel = getAIPanelWidget(host);
const edgelessService = getEdgelessService(host); const gfx = host.std.get(GfxControllerIdentifier);
const edgelessCopilot = getEdgelessCopilotWidget(host); const edgelessCopilot = getEdgelessCopilotWidget(host);
const selectionRect = edgelessCopilot.selectionModelRect; const selectionRect = edgelessCopilot.selectionModelRect;
const surface = getSurfaceBlock(host.doc); const surface = getSurfaceBlock(host.doc);
@@ -456,13 +450,13 @@ function responseToBrainstormMindmap(host: EditorHost, ctx: AIContext) {
// This is a workaround to make sure mindmap and other microtask are done // This is a workaround to make sure mindmap and other microtask are done
setTimeout(() => { setTimeout(() => {
edgelessService.viewport.setViewportByBound( gfx.viewport.setViewportByBound(
mindmap.elementBound, mindmap.elementBound,
[20, 20, 20, 20], [20, 20, 20, 20],
true true
); );
edgelessService.selection.set({ gfx.selection.set({
elements: [mindmap.tree.element.id], elements: [mindmap.tree.element.id],
editing: false, editing: false,
}); });
@@ -508,9 +502,6 @@ async function responseToCreateSlides(host: EditorHost, ctx: AIContext) {
const { contents = [], images = [] } = data; const { contents = [], images = [] } = data;
if (contents.length === 0) return; if (contents.length === 0) return;
const service = host.std.getService<EdgelessRootService>('affine:page');
if (!service) return;
try { try {
for (let i = 0; i < contents.length; i++) { for (let i = 0; i < contents.length; i++) {
const image = images[i] || []; const image = images[i] || [];
@@ -1,5 +1,6 @@
import { AINetworkSearchService } from '@affine/core/modules/ai-button/services/network-search'; import { AINetworkSearchService } from '@affine/core/modules/ai-button/services/network-search';
import type { EditorHost } from '@blocksuite/affine/block-std'; import type { EditorHost } from '@blocksuite/affine/block-std';
import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
import { import {
type AffineAIPanelWidget, type AffineAIPanelWidget,
type AffineAIPanelWidgetConfig, type AffineAIPanelWidgetConfig,
@@ -38,7 +39,7 @@ import { AIProvider } from './provider';
import { reportResponse } from './utils/action-reporter'; import { reportResponse } from './utils/action-reporter';
import { getAIPanelWidget } from './utils/ai-widgets'; import { getAIPanelWidget } from './utils/ai-widgets';
import { AIContext } from './utils/context'; import { AIContext } from './utils/context';
import { findNoteBlockModel, getService } from './utils/edgeless'; import { findNoteBlockModel } from './utils/edgeless';
import { copyTextAnswer } from './utils/editor-actions'; import { copyTextAnswer } from './utils/editor-actions';
import { getSelections } from './utils/selection-utils'; import { getSelections } from './utils/selection-utils';
@@ -93,7 +94,7 @@ function createNewNote(host: EditorHost): AIItemConfig {
const newBound = new Bound(bound.x - bound.w - 20, bound.y, bound.w, 72); const newBound = new Bound(bound.x - bound.w - 20, bound.y, bound.w, 72);
const doc = host.doc; const doc = host.doc;
const panel = getAIPanelWidget(host); const panel = getAIPanelWidget(host);
const service = getService(host); const gfx = host.std.get(GfxControllerIdentifier);
doc.transact(() => { doc.transact(() => {
assertExists(doc.root); assertExists(doc.root);
const noteBlockId = doc.addBlock( const noteBlockId = doc.addBlock(
@@ -101,7 +102,7 @@ function createNewNote(host: EditorHost): AIItemConfig {
{ {
xywh: newBound.serialize(), xywh: newBound.serialize(),
displayMode: NoteDisplayMode.EdgelessOnly, displayMode: NoteDisplayMode.EdgelessOnly,
index: service.generateIndex(), index: gfx.layer.generateIndex(),
}, },
doc.root.id doc.root.id
); );
@@ -109,7 +110,7 @@ function createNewNote(host: EditorHost): AIItemConfig {
assertExists(panel.answer); assertExists(panel.answer);
insertFromMarkdown(host, panel.answer, doc, noteBlockId) insertFromMarkdown(host, panel.answer, doc, noteBlockId)
.then(() => { .then(() => {
service.selection.set({ gfx.selection.set({
elements: [noteBlockId], elements: [noteBlockId],
editing: false, editing: false,
}); });
@@ -119,7 +120,7 @@ function createNewNote(host: EditorHost): AIItemConfig {
if (!newNote || !matchModels(newNote, [NoteBlockModel])) return; if (!newNote || !matchModels(newNote, [NoteBlockModel])) return;
const newNoteBound = Bound.deserialize(newNote.xywh); const newNoteBound = Bound.deserialize(newNote.xywh);
const bounds = [bound, newNoteBound]; const bounds = [bound, newNoteBound];
service.gfx.fitToScreen({ gfx.fitToScreen({
bounds, bounds,
padding: [20, 20, 20, 20], padding: [20, 20, 20, 20],
}); });
@@ -16,7 +16,7 @@ import type { Bound } from '@blocksuite/affine/global/utils';
import { html } from 'lit'; import { html } from 'lit';
import { query } from 'lit/decorators.js'; import { query } from 'lit/decorators.js';
import type { MindmapService } from './mindmap-service.js'; import { MindmapService } from './mindmap-service.js';
export class MindmapSurfaceBlock extends BlockComponent<SurfaceBlockModel> { export class MindmapSurfaceBlock extends BlockComponent<SurfaceBlockModel> {
renderer?: CanvasRenderer; renderer?: CanvasRenderer;
@@ -30,7 +30,7 @@ export class MindmapSurfaceBlock extends BlockComponent<SurfaceBlockModel> {
} }
get mindmapService() { get mindmapService() {
return this.std.getService('affine:page') as unknown as MindmapService; return this.std.get(MindmapService);
} }
get viewport() { get viewport() {
@@ -4,7 +4,8 @@ import {
CanvasElementType, CanvasElementType,
ConnectorMode, ConnectorMode,
DocModeProvider, DocModeProvider,
type EdgelessRootService, EdgelessCRUDIdentifier,
getSurfaceBlock,
NotificationProvider, NotificationProvider,
TelemetryProvider, TelemetryProvider,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
@@ -33,10 +34,6 @@ import { calcChildBound } from './utils';
export class AIChatBlockPeekView extends LitElement { export class AIChatBlockPeekView extends LitElement {
static override styles = PeekViewStyles; static override styles = PeekViewStyles;
private get _rootService() {
return this.host.std.getService('affine:page');
}
private get _modeService() { private get _modeService() {
return this.host.std.get(DocModeProvider); return this.host.std.get(DocModeProvider);
} }
@@ -172,9 +169,10 @@ export class AIChatBlockPeekView extends LitElement {
return; return;
} }
const edgelessService = this._rootService as EdgelessRootService; const bound = calcChildBound(this.parentModel, this.host.std);
const bound = calcChildBound(this.parentModel, edgelessService);
const aiChatBlockId = edgelessService.crud.addBlock( const crud = this.host.std.get(EdgelessCRUDIdentifier);
const aiChatBlockId = crud.addBlock(
'affine:embed-ai-chat', 'affine:embed-ai-chat',
{ {
xywh: bound.serialize(), xywh: bound.serialize(),
@@ -193,7 +191,7 @@ export class AIChatBlockPeekView extends LitElement {
this.updateContext({ currentChatBlockId: aiChatBlockId }); this.updateContext({ currentChatBlockId: aiChatBlockId });
// Connect the parent chat block to the AI chat block // Connect the parent chat block to the AI chat block
edgelessService.crud.addElement(CanvasElementType.CONNECTOR, { crud.addElement(CanvasElementType.CONNECTOR, {
mode: ConnectorMode.Curve, mode: ConnectorMode.Curve,
controllers: [], controllers: [],
source: { id: this.parentChatBlockId }, source: { id: this.parentChatBlockId },
@@ -249,7 +247,6 @@ export class AIChatBlockPeekView extends LitElement {
* Clean current chat messages and delete the newly created AI chat block * Clean current chat messages and delete the newly created AI chat block
*/ */
cleanCurrentChatHistories = async () => { cleanCurrentChatHistories = async () => {
if (!this._rootService) return;
const notificationService = this.host.std.getOptional(NotificationProvider); const notificationService = this.host.std.getOptional(NotificationProvider);
if (!notificationService) return; if (!notificationService) return;
@@ -275,18 +272,17 @@ export class AIChatBlockPeekView extends LitElement {
} }
if (currentChatBlockId) { if (currentChatBlockId) {
const edgelessService = this._rootService as EdgelessRootService; const surface = getSurfaceBlock(doc);
const crud = this.host.std.get(EdgelessCRUDIdentifier);
const chatBlock = doc.getBlock(currentChatBlockId)?.model; const chatBlock = doc.getBlock(currentChatBlockId)?.model;
if (chatBlock) { if (chatBlock) {
const connectors = edgelessService.getConnectors( const connectors = surface?.getConnectors(chatBlock.id);
chatBlock as AIChatBlockModel
);
doc.transact(() => { doc.transact(() => {
// Delete the AI chat block // Delete the AI chat block
edgelessService.removeElement(currentChatBlockId); crud.removeElement(currentChatBlockId);
// Delete the connectors // Delete the connectors
connectors.forEach(connector => { connectors?.forEach(connector => {
edgelessService.removeElement(connector.id); crud.removeElement(connector.id);
}); });
}); });
} }
@@ -1,4 +1,8 @@
import type { EdgelessRootService } from '@blocksuite/affine/blocks'; import type { BlockStdScope } from '@blocksuite/affine/block-std';
import {
EdgelessCRUDIdentifier,
getSurfaceBlock,
} from '@blocksuite/affine/blocks';
import { Bound } from '@blocksuite/affine/global/utils'; import { Bound } from '@blocksuite/affine/global/utils';
import { import {
@@ -19,18 +23,19 @@ import {
*/ */
export function calcChildBound( export function calcChildBound(
parentModel: AIChatBlockModel, parentModel: AIChatBlockModel,
service: EdgelessRootService std: BlockStdScope
) { ) {
const surface = getSurfaceBlock(std.store);
const crud = std.get(EdgelessCRUDIdentifier);
const parentXYWH = Bound.deserialize(parentModel.xywh); const parentXYWH = Bound.deserialize(parentModel.xywh);
const { x: parentX, y: parentY, w: parentWidth } = parentXYWH; const { x: parentX, y: parentY, w: parentWidth } = parentXYWH;
const connectors = service.getConnectors(parentModel.id); const connectors = surface?.getConnectors(parentModel.id);
const gapX = CHAT_BLOCK_WIDTH; const gapX = CHAT_BLOCK_WIDTH;
const gapY = 60; const gapY = 60;
const defaultX = parentX + parentWidth + gapX; const defaultX = parentX + parentWidth + gapX;
const defaultY = parentY; const defaultY = parentY;
if (!connectors?.length) {
if (!connectors.length) {
return new Bound(defaultX, defaultY, CHAT_BLOCK_WIDTH, CHAT_BLOCK_HEIGHT); return new Bound(defaultX, defaultY, CHAT_BLOCK_WIDTH, CHAT_BLOCK_HEIGHT);
} else { } else {
// Filter out the connectors which source is the parent block // Filter out the connectors which source is the parent block
@@ -41,7 +46,7 @@ export function calcChildBound(
const targetBlocks = childConnectors const targetBlocks = childConnectors
.map(connector => connector.target.id) .map(connector => connector.target.id)
.filter(id => id !== undefined) .filter(id => id !== undefined)
.map(id => service.crud.getElementById(id)) .map(id => crud.getElementById(id))
.filter(block => !!block); .filter(block => !!block);
if (targetBlocks.length) { if (targetBlocks.length) {
@@ -1,5 +1,5 @@
import type { EditorHost } from '@blocksuite/affine/block-std'; import type { EditorHost } from '@blocksuite/affine/block-std';
import type { EdgelessRootService } from '@blocksuite/affine/blocks'; import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
import type { BlockSnapshot } from '@blocksuite/affine/store'; import type { BlockSnapshot } from '@blocksuite/affine/store';
import { markdownToSnapshot } from '../../_common'; import { markdownToSnapshot } from '../../_common';
@@ -13,11 +13,10 @@ import {
} from './template'; } from './template';
export const PPTBuilder = (host: EditorHost) => { export const PPTBuilder = (host: EditorHost) => {
const service = host.std.getService<EdgelessRootService>('affine:page'); const gfx = host.std.get(GfxControllerIdentifier);
const docs: PPTDoc[] = []; const docs: PPTDoc[] = [];
const contents: unknown[] = []; const contents: unknown[] = [];
const allImages: TemplateImage[][] = []; const allImages: TemplateImage[][] = [];
if (!service) return;
const addDoc = async (block: BlockSnapshot) => { const addDoc = async (block: BlockSnapshot) => {
const sections = block.children.map(v => { const sections = block.children.map(v => {
@@ -62,7 +61,7 @@ export const PPTBuilder = (host: EditorHost) => {
const block = snapshot.snapshot.content[0]; const block = snapshot.snapshot.content[0];
for (const child of block.children) { for (const child of block.children) {
await addDoc(child); await addDoc(child);
service.gfx.fitToScreen(); gfx.fitToScreen();
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@@ -3,7 +3,6 @@ import type { GfxModel } from '@blocksuite/affine/block-std/gfx';
import { import {
AFFINE_EDGELESS_COPILOT_WIDGET, AFFINE_EDGELESS_COPILOT_WIDGET,
type EdgelessCopilotWidget, type EdgelessCopilotWidget,
type EdgelessRootService,
matchModels, matchModels,
MindmapElementModel, MindmapElementModel,
NoteBlockModel, NoteBlockModel,
@@ -43,14 +42,6 @@ export function isMindmapChild(ele: GfxModel) {
return ele?.group instanceof MindmapElementModel && !isMindMapRoot(ele); return ele?.group instanceof MindmapElementModel && !isMindMapRoot(ele);
} }
export function getService(host: EditorHost) {
const edgelessService = host.std.getService(
'affine:page'
) as EdgelessRootService;
return edgelessService;
}
export function getEdgelessCopilotWidget( export function getEdgelessCopilotWidget(
host: EditorHost host: EditorHost
): EdgelessCopilotWidget { ): EdgelessCopilotWidget {
@@ -1,10 +1,12 @@
import { type EditorHost, TextSelection } from '@blocksuite/affine/block-std'; import { type EditorHost, TextSelection } from '@blocksuite/affine/block-std';
import type { GfxModel } from '@blocksuite/affine/block-std/gfx'; import {
GfxControllerIdentifier,
type GfxModel,
} from '@blocksuite/affine/block-std/gfx';
import { import {
BlocksUtils, BlocksUtils,
type CopilotTool, type CopilotTool,
DatabaseBlockModel, DatabaseBlockModel,
EdgelessRootService,
type FrameBlockModel, type FrameBlockModel,
getBlockSelectionsCommand, getBlockSelectionsCommand,
getImageSelectionsCommand, getImageSelectionsCommand,
@@ -22,11 +24,7 @@ import {
} from '@blocksuite/affine/store'; } from '@blocksuite/affine/store';
import { getContentFromSlice } from '../../_common'; import { getContentFromSlice } from '../../_common';
import { getEdgelessCopilotWidget, getService } from './edgeless'; import { getEdgelessCopilotWidget } from './edgeless';
export const getRootService = (host: EditorHost) => {
return host.std.getService('affine:page');
};
export function getEdgelessRootFromEditor(editor: EditorHost) { export function getEdgelessRootFromEditor(editor: EditorHost) {
const edgelessRoot = editor.getElementsByTagName('affine-edgeless-root')[0]; const edgelessRoot = editor.getElementsByTagName('affine-edgeless-root')[0];
@@ -36,14 +34,6 @@ export function getEdgelessRootFromEditor(editor: EditorHost) {
} }
return edgelessRoot; return edgelessRoot;
} }
export function getEdgelessService(editor: EditorHost) {
const rootService = editor.std.getService('affine:page');
if (rootService instanceof EdgelessRootService) {
return rootService;
}
alert('Please switch to edgeless mode');
throw new Error('Please open switch to edgeless mode');
}
export async function selectedToCanvas(host: EditorHost) { export async function selectedToCanvas(host: EditorHost) {
const edgelessRoot = getEdgelessRootFromEditor(host); const edgelessRoot = getEdgelessRootFromEditor(host);
@@ -287,15 +277,15 @@ export const getSelectedNoteAnchor = (host: EditorHost, id: string) => {
}; };
export function getCopilotSelectedElems(host: EditorHost): GfxModel[] { export function getCopilotSelectedElems(host: EditorHost): GfxModel[] {
const service = getService(host); const gfx = host.std.get(GfxControllerIdentifier);
const copilotWidget = getEdgelessCopilotWidget(host); const copilotWidget = getEdgelessCopilotWidget(host);
if (copilotWidget.visible) { if (copilotWidget.visible) {
const currentTool = service.gfx.tool.currentTool$.peek() as CopilotTool; const currentTool = gfx.tool.currentTool$.peek() as CopilotTool;
return currentTool?.selectedElements ?? []; return currentTool?.selectedElements ?? [];
} }
return service.selection.selectedElements; return gfx.selection.selectedElements;
} }
export const imageCustomInput = async (host: EditorHost) => { export const imageCustomInput = async (host: EditorHost) => {
@@ -9,14 +9,12 @@ import { I18n } from '@affine/i18n';
import type { BlockStdScope } from '@blocksuite/affine/block-std'; import type { BlockStdScope } from '@blocksuite/affine/block-std';
import { import {
type GfxBlockElementModel, type GfxBlockElementModel,
GfxControllerIdentifier,
type GfxModel, type GfxModel,
GfxPrimitiveElementModel, GfxPrimitiveElementModel,
isGfxGroupCompatibleModel, isGfxGroupCompatibleModel,
} from '@blocksuite/affine/block-std/gfx'; } from '@blocksuite/affine/block-std/gfx';
import type { import type { MenuContext } from '@blocksuite/affine/blocks';
EdgelessRootService,
MenuContext,
} from '@blocksuite/affine/blocks';
import { Bound, getCommonBound } from '@blocksuite/affine/global/utils'; import { Bound, getCommonBound } from '@blocksuite/affine/global/utils';
import { CopyAsImgaeIcon } from '@blocksuite/icons/lit'; import { CopyAsImgaeIcon } from '@blocksuite/icons/lit';
import type { FrameworkProvider } from '@toeverything/infra'; import type { FrameworkProvider } from '@toeverything/infra';
@@ -135,11 +133,9 @@ export function createCopyAsPngMenuItem(framework: FrameworkProvider) {
return; return;
} }
const service = const gfx = ctx.host.std.get(GfxControllerIdentifier);
ctx.host.std.getService<EdgelessRootService>('affine:page');
if (!service) return;
let selected = service.selection.selectedElements; let selected = gfx.selection.selectedElements;
// select mindmap if root node selected // select mindmap if root node selected
const maybeMindmap = selected[0]; const maybeMindmap = selected[0];
const mindmapId = maybeMindmap.group?.id; const mindmapId = maybeMindmap.group?.id;
@@ -148,31 +144,31 @@ export function createCopyAsPngMenuItem(framework: FrameworkProvider) {
mindmapId && mindmapId &&
(isMindMapRoot(maybeMindmap) || isMindmapChild(maybeMindmap)) (isMindMapRoot(maybeMindmap) || isMindmapChild(maybeMindmap))
) { ) {
service.gfx.selection.set({ elements: [mindmapId] }); gfx.selection.set({ elements: [mindmapId] });
} }
// select bound // select bound
selected = service.selection.selectedElements; selected = gfx.selection.selectedElements;
const elements = withDescendantElements(selected); const elements = withDescendantElements(selected);
const bounds = elements.map(element => Bound.deserialize(element.xywh)); const bounds = elements.map(element => Bound.deserialize(element.xywh));
const bound = getCommonBound(bounds); const bound = getCommonBound(bounds);
if (!bound) return; if (!bound) return;
const { zoom } = service.viewport; const { zoom } = gfx.viewport;
const exBound = expandBound(bound, MARGIN * zoom); const exBound = expandBound(bound, MARGIN * zoom);
// fit to screen // fit to screen
if ( if (
!isInside(service.viewport.viewportBounds, exBound) || !isInside(gfx.viewport.viewportBounds, exBound) ||
service.viewport.zoom < 1 gfx.viewport.zoom < 1
) { ) {
service.viewport.setViewportByBound(bound, [20, 20, 20, 20], false); gfx.viewport.setViewportByBound(bound, [20, 20, 20, 20], false);
if (service.viewport.zoom > 1) { if (gfx.viewport.zoom > 1) {
service.viewport.setZoom(1); gfx.viewport.setZoom(1);
} }
} }
// hide unselected overlap elements // hide unselected overlap elements
const overlapElements = service.gfx.gfxElements.filter(ele => { const overlapElements = gfx.gfxElements.filter(ele => {
const eleBound = Bound.deserialize(ele.xywh); const eleBound = Bound.deserialize(ele.xywh);
const exEleBound = expandBound(eleBound, MARGIN * zoom); const exEleBound = expandBound(eleBound, MARGIN * zoom);
const isSelected = elements.includes(ele); const isSelected = elements.includes(ele);
@@ -190,14 +186,14 @@ export function createCopyAsPngMenuItem(framework: FrameworkProvider) {
if (!apis) return; if (!apis) return;
try { try {
const domRect = getSelectedRect(); const domRect = getSelectedRect();
const { zoom } = service.viewport; const { zoom } = gfx.viewport;
const isFrameSelected = const isFrameSelected =
selected.length === 1 && selected.length === 1 &&
(selected[0] as GfxBlockElementModel).flavour === 'affine:frame'; (selected[0] as GfxBlockElementModel).flavour === 'affine:frame';
const margin = isFrameSelected ? -2 : MARGIN * zoom; const margin = isFrameSelected ? -2 : MARGIN * zoom;
service.selection.clear(); gfx.selection.clear();
// eslint-disable-next-line @typescript-eslint/no-floating-promises
apis.ui apis.ui
.captureArea({ .captureArea({
x: domRect.left - margin, x: domRect.left - margin,
@@ -5,10 +5,12 @@ import { copyTextToClipboard } from '@affine/core/utils/clipboard';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import { type EditorHost } from '@blocksuite/affine/block-std'; import { type EditorHost } from '@blocksuite/affine/block-std';
import { GfxBlockElementModel } from '@blocksuite/affine/block-std/gfx'; import {
GfxBlockElementModel,
GfxControllerIdentifier,
} from '@blocksuite/affine/block-std/gfx';
import { import {
type DocMode, type DocMode,
type EdgelessRootService,
getBlockSelectionsCommand, getBlockSelectionsCommand,
getImageSelectionsCommand, getImageSelectionsCommand,
getSelectedModelsCommand, getSelectedModelsCommand,
@@ -87,10 +89,9 @@ export const getSelectedNodes = (
} }
if (mode === 'edgeless') { if (mode === 'edgeless') {
const service = std.getService<EdgelessRootService>('affine:page'); const { selection } = std.get(GfxControllerIdentifier);
if (!service) return result;
for (const element of service.selection.selectedElements) { for (const element of selection.selectedElements) {
if (element instanceof GfxBlockElementModel) { if (element instanceof GfxBlockElementModel) {
blockIds.push(element.id); blockIds.push(element.id);
} else { } else {
@@ -9,13 +9,10 @@ import { SettingRow } from '@affine/component/setting-components';
import { EditorSettingService } from '@affine/core/modules/editor-setting'; import { EditorSettingService } from '@affine/core/modules/editor-setting';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import type { EditorHost } from '@blocksuite/affine/block-std'; import type { EditorHost } from '@blocksuite/affine/block-std';
import type { import type { ShapeElementModel, ShapeName } from '@blocksuite/affine/blocks';
EdgelessRootService,
ShapeElementModel,
ShapeName,
} from '@blocksuite/affine/blocks';
import { import {
DefaultTheme, DefaultTheme,
EdgelessCRUDIdentifier,
FontFamily, FontFamily,
FontFamilyMap, FontFamilyMap,
FontStyle, FontStyle,
@@ -354,18 +351,16 @@ export const ShapeSettings = () => {
const firstUpdate = useCallback( const firstUpdate = useCallback(
(doc: Store, editorHost: EditorHost) => { (doc: Store, editorHost: EditorHost) => {
const edgelessService = editorHost.std.getService(
'affine:page'
) as EdgelessRootService;
const surface = getSurfaceBlock(doc); const surface = getSurfaceBlock(doc);
if (!surface) return; if (!surface) return;
const crud = editorHost.std.get(EdgelessCRUDIdentifier);
doc.readonly = false; doc.readonly = false;
surface.getElementsByType('shape').forEach(node => { surface.getElementsByType('shape').forEach(node => {
const shape = node as ShapeElementModel; const shape = node as ShapeElementModel;
const { shapeType, radius } = shape; const { shapeType, radius } = shape;
const shapeName = getShapeName(shapeType, radius); const shapeName = getShapeName(shapeType, radius);
const props = editorSetting.get(`shape:${shapeName}`); const props = editorSetting.get(`shape:${shapeName}`);
edgelessService.crud.updateElement(shape.id, props); crud.updateElement(shape.id, props);
}); });
doc.readonly = true; doc.readonly = true;
}, },
@@ -4,18 +4,20 @@ import { EditorSettingService } from '@affine/core/modules/editor-setting';
import { AppThemeService } from '@affine/core/modules/theme'; import { AppThemeService } from '@affine/core/modules/theme';
import type { EditorHost } from '@blocksuite/affine/block-std'; import type { EditorHost } from '@blocksuite/affine/block-std';
import { import {
BlockServiceIdentifier,
BlockStdScope, BlockStdScope,
LifeCycleWatcher, LifeCycleWatcher,
StdIdentifier, StdIdentifier,
} from '@blocksuite/affine/block-std'; } from '@blocksuite/affine/block-std';
import type { GfxPrimitiveElementModel } from '@blocksuite/affine/block-std/gfx'; import {
import type { GfxControllerIdentifier,
EdgelessRootService, type GfxPrimitiveElementModel,
ThemeExtension, } from '@blocksuite/affine/block-std/gfx';
} from '@blocksuite/affine/blocks'; import type { ThemeExtension } from '@blocksuite/affine/blocks';
import { import {
ColorScheme, ColorScheme,
createSignalFromObservable, createSignalFromObservable,
EdgelessCRUDIdentifier,
SpecProvider, SpecProvider,
ThemeExtensionIdentifier, ThemeExtensionIdentifier,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
@@ -72,14 +74,12 @@ export const EdgelessSnapshot = (props: Props) => {
const editorHost = editorHostRef.current; const editorHost = editorHostRef.current;
const doc = docRef.current; const doc = docRef.current;
if (!editorHost || !doc) return; if (!editorHost || !doc) return;
const edgelessService = editorHost.std.getService( const crud = editorHost.std.get(EdgelessCRUDIdentifier);
'affine:page'
) as EdgelessRootService;
const elements = getElements(doc); const elements = getElements(doc);
const props = editorSetting.get(keyName) as any; const props = editorSetting.get(keyName) as any;
doc.readonly = false; doc.readonly = false;
elements.forEach(element => { elements.forEach(element => {
edgelessService.crud.updateElement(element.id, props); crud.updateElement(element.id, props);
}); });
doc.readonly = true; doc.readonly = true;
}, [editorSetting, getElements, keyName]); }, [editorSetting, getElements, keyName]);
@@ -107,9 +107,10 @@ export const EdgelessSnapshot = (props: Props) => {
} }
// refresh viewport // refresh viewport
const edgelessService = editorHost.std.getService( const edgelessService = editorHost.std.get(
'affine:page' BlockServiceIdentifier('affine:page')
) as EdgelessRootService; );
const gfx = editorHost.std.get(GfxControllerIdentifier);
edgelessService.specSlots.viewConnected.once(({ component }) => { edgelessService.specSlots.viewConnected.once(({ component }) => {
const edgelessBlock = component as any; const edgelessBlock = component as any;
doc.readonly = false; doc.readonly = false;
@@ -121,7 +122,7 @@ export const EdgelessSnapshot = (props: Props) => {
doc.deleteBlock(frame); doc.deleteBlock(frame);
} }
const bound = boundMap.get(docName); const bound = boundMap.get(docName);
bound && edgelessService.viewport.setViewportByBound(bound); bound && gfx.viewport.setViewportByBound(bound);
doc.readonly = true; doc.readonly = true;
}); });
@@ -1,7 +1,7 @@
import type { DefaultOpenProperty } from '@affine/core/components/doc-properties'; import type { DefaultOpenProperty } from '@affine/core/components/doc-properties';
import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
import { import {
type DocMode, type DocMode,
EdgelessRootService,
FeatureFlagService as BSFeatureFlagService, FeatureFlagService as BSFeatureFlagService,
HighlightSelection, HighlightSelection,
type ReferenceParams, type ReferenceParams,
@@ -73,13 +73,11 @@ export class Editor extends Entity {
isPresenting$ = new LiveData<boolean>(false); isPresenting$ = new LiveData<boolean>(false);
togglePresentation() { togglePresentation() {
const edgelessRootService = const gfx = this.editorContainer$.value?.host?.std.get(
this.editorContainer$.value?.host?.std.getService( GfxControllerIdentifier
'affine:page' );
) as EdgelessRootService; if (!gfx) return;
if (!edgelessRootService) return; gfx.tool.setTool({
edgelessRootService.gfx.tool.setTool({
type: !this.isPresenting$.value ? 'frameNavigator' : 'default', type: !this.isPresenting$.value ? 'frameNavigator' : 'default',
}); });
} }
@@ -200,7 +198,7 @@ export class Editor extends Entity {
this.editorContainer$.next(editorContainer); this.editorContainer$.next(editorContainer);
const unsubs: (() => void)[] = []; const unsubs: (() => void)[] = [];
const rootService = editorContainer.host?.std.getService('affine:page'); const gfx = editorContainer.host?.std.get(GfxControllerIdentifier);
// ----- Scroll Position and Selection ----- // ----- Scroll Position and Selection -----
// if we have default scroll position, we should restore it // if we have default scroll position, we should restore it
@@ -209,9 +207,9 @@ export class Editor extends Entity {
} else if ( } else if (
this.mode$.value === 'edgeless' && this.mode$.value === 'edgeless' &&
this.scrollPosition.edgeless && this.scrollPosition.edgeless &&
rootService instanceof EdgelessRootService gfx
) { ) {
rootService.viewport.setViewport(this.scrollPosition.edgeless.zoom, [ gfx.viewport.setViewport(this.scrollPosition.edgeless.zoom, [
this.scrollPosition.edgeless.centerX, this.scrollPosition.edgeless.centerX,
this.scrollPosition.edgeless.centerY, this.scrollPosition.edgeless.centerY,
]); ]);
@@ -253,14 +251,11 @@ export class Editor extends Entity {
if (this.mode$.value === 'page' && scrollViewport) { if (this.mode$.value === 'page' && scrollViewport) {
this.scrollPosition.page = scrollViewport.scrollTop; this.scrollPosition.page = scrollViewport.scrollTop;
this.workbenchView?.setScrollPosition(scrollViewport.scrollTop); this.workbenchView?.setScrollPosition(scrollViewport.scrollTop);
} else if ( } else if (this.mode$.value === 'edgeless' && gfx) {
this.mode$.value === 'edgeless' &&
rootService instanceof EdgelessRootService
) {
const pos = { const pos = {
centerX: rootService.viewport.centerX, centerX: gfx.viewport.centerX,
centerY: rootService.viewport.centerY, centerY: gfx.viewport.centerY,
zoom: rootService.viewport.zoom, zoom: gfx.viewport.zoom,
}; };
this.scrollPosition.edgeless = pos; this.scrollPosition.edgeless = pos;
this.workbenchView?.setScrollPosition(pos); this.workbenchView?.setScrollPosition(pos);
@@ -270,10 +265,8 @@ export class Editor extends Entity {
unsubs.push(() => { unsubs.push(() => {
scrollViewport?.removeEventListener('scroll', saveScrollPosition); scrollViewport?.removeEventListener('scroll', saveScrollPosition);
}); });
if (rootService instanceof EdgelessRootService) { if (gfx) {
unsubs.push( unsubs.push(gfx.viewport.viewportUpdated.on(saveScrollPosition).dispose);
rootService.viewport.viewportUpdated.on(saveScrollPosition).dispose
);
} }
// update selection when focusAt$ changed // update selection when focusAt$ changed
@@ -10,10 +10,8 @@ import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-
import { PageNotFound } from '@affine/core/desktop/pages/404'; import { PageNotFound } from '@affine/core/desktop/pages/404';
import { EditorService } from '@affine/core/modules/editor'; import { EditorService } from '@affine/core/modules/editor';
import { DebugLogger } from '@affine/debug'; import { DebugLogger } from '@affine/debug';
import { import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
type EdgelessRootService, import { RefNodeSlotsProvider } from '@blocksuite/affine/blocks';
RefNodeSlotsProvider,
} from '@blocksuite/affine/blocks';
import { import {
Bound, Bound,
type Disposable, type Disposable,
@@ -46,25 +44,22 @@ function fitViewport(
throw new Error('editor host is not ready'); throw new Error('editor host is not ready');
} }
const rootService = const gfx = editor.host.std.get(GfxControllerIdentifier);
editor.host.std.getService<EdgelessRootService>('affine:page'); const viewport = gfx.viewport;
if (!rootService) { viewport.onResize();
return;
}
rootService.viewport.onResize();
if (xywh) { if (xywh) {
const viewport = { const newViewport = {
xywh: xywh, xywh: xywh,
padding: [60, 20, 20, 20] as [number, number, number, number], padding: [60, 20, 20, 20] as [number, number, number, number],
}; };
rootService.viewport.setViewportByBound( viewport.setViewportByBound(
Bound.deserialize(viewport.xywh), Bound.deserialize(newViewport.xywh),
viewport.padding, newViewport.padding,
false false
); );
} else { } else {
rootService.gfx.fitToScreen({ gfx.fitToScreen({
smooth: false, smooth: false,
}); });
} }