diff --git a/blocksuite/affine/components/src/caption/captioned-block-component.ts b/blocksuite/affine/components/src/caption/captioned-block-component.ts
index 9c8621c0ef..3f84b4f000 100644
--- a/blocksuite/affine/components/src/caption/captioned-block-component.ts
+++ b/blocksuite/affine/components/src/caption/captioned-block-component.ts
@@ -1,4 +1,5 @@
import { ThemeProvider } from '@blocksuite/affine-shared/services';
+import { watch } from '@blocksuite/global/lit';
import { BlockComponent, type BlockService } from '@blocksuite/std';
import type { BlockModel } from '@blocksuite/store';
import { html, nothing } from 'lit';
@@ -59,7 +60,7 @@ export class CaptionedBlockComponent<
: nothing}
${this.selectedStyle === SelectedStyle.Background
? html``
: null}
${this.useZeroWidth && !this.store.readonly
diff --git a/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts b/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts
index 446a30dc0a..c42ca33362 100644
--- a/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts
+++ b/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts
@@ -5,7 +5,7 @@ import type {
TextSelection,
} from '@blocksuite/std';
import { BlockComponent } from '@blocksuite/std';
-import type { RoleType } from '@blocksuite/store';
+import type { BlockModel, RoleType } from '@blocksuite/store';
import type { ImageSelection } from '../../selection/index.js';
@@ -129,35 +129,33 @@ export const getSelectedBlocksCommand: Command<
dirtyResult = dirtyResult.filter(ctx.filter);
}
- const getModelPath = (el: BlockComponent) => {
- const path: number[] = [];
- let model = el.model;
- while (model) {
- const parent = ctx.std.store.getParent(model.id);
- if (!parent) break;
- path.unshift(parent.children.findIndex(child => child.id === model.id));
- model = parent;
- }
- return path;
- };
-
- const compareByModelPath = (a: BlockComponent, b: BlockComponent) => {
- if (a === b) return 0;
- const aPath = getModelPath(a);
- const bPath = getModelPath(b);
- const length = Math.min(aPath.length, bPath.length);
- for (let i = 0; i < length; i++) {
- const diff = aPath[i] - bPath[i];
- if (diff !== 0) return diff;
- }
- return aPath.length - bPath.length;
- };
+ const seen = new Set();
// remove duplicate elements
- const result: BlockComponent[] = dirtyResult
- .filter((el, index) => dirtyResult.indexOf(el) === index)
+ const result: BlockComponent[] = dirtyResult.filter(el => {
+ if (seen.has(el)) return false;
+ seen.add(el);
+ return true;
+ });
+
+ if (result.length > 1) {
+ const modelOrder = new Map();
+ const visit = (model: BlockModel) => {
+ modelOrder.set(model.id, modelOrder.size);
+ model.children.forEach(visit);
+ };
+ const root = ctx.std.store.root;
+ if (root) {
+ visit(root);
+ }
+
// sort by model tree position, which is the order used for paste/export
- .sort(compareByModelPath);
+ result.sort(
+ (a, b) =>
+ (modelOrder.get(a.blockId) ?? Number.MAX_SAFE_INTEGER) -
+ (modelOrder.get(b.blockId) ?? Number.MAX_SAFE_INTEGER)
+ );
+ }
if (result.length === 0) return;
diff --git a/blocksuite/affine/widgets/toolbar/src/toolbar.ts b/blocksuite/affine/widgets/toolbar/src/toolbar.ts
index 8ce6201de3..7a2c4b593f 100644
--- a/blocksuite/affine/widgets/toolbar/src/toolbar.ts
+++ b/blocksuite/affine/widgets/toolbar/src/toolbar.ts
@@ -146,7 +146,16 @@ export class AffineToolbarWidget extends WidgetComponent {
}
setReferenceElementWithBlocks(blocks: BlockComponent[]) {
- const getClientRects = () => blocks.map(e => e.getBoundingClientRect());
+ let cachedClientRects: DOMRect[] | null = null;
+ const getClientRects = () => {
+ if (!cachedClientRects) {
+ cachedClientRects = blocks.map(e => e.getBoundingClientRect());
+ requestAnimationFrame(() => {
+ cachedClientRects = null;
+ });
+ }
+ return cachedClientRects;
+ };
this.referenceElement$.value = blocks.length
? () => ({
diff --git a/blocksuite/affine/widgets/toolbar/src/utils.ts b/blocksuite/affine/widgets/toolbar/src/utils.ts
index d4688991f2..087484d17d 100644
--- a/blocksuite/affine/widgets/toolbar/src/utils.ts
+++ b/blocksuite/affine/widgets/toolbar/src/utils.ts
@@ -56,7 +56,7 @@ export function autoUpdatePosition(
flavour: string,
placement: ToolbarPlacement,
sideOptions: Partial | null,
- options: AutoUpdateOptions = { elementResize: false, animationFrame: true }
+ options: AutoUpdateOptions = { elementResize: false }
) {
const isInline = flavour === 'affine:note';
const hasSurfaceScope = flavour.includes('surface');
@@ -147,7 +147,7 @@ export function autoUpdatePosition(
() => {
update().catch(console.error);
},
- options
+ { animationFrame: hasSurfaceScope, ...options }
);
}