diff --git a/blocksuite/affine/blocks/block-embed/src/configs/toolbar.ts b/blocksuite/affine/blocks/block-embed/src/configs/toolbar.ts
index 4292519f3..74735d6f2 100644
--- a/blocksuite/affine/blocks/block-embed/src/configs/toolbar.ts
+++ b/blocksuite/affine/blocks/block-embed/src/configs/toolbar.ts
@@ -1,18 +1,29 @@
+import { reassociateConnectorsCommand } from '@blocksuite/affine-block-surface';
import { toast } from '@blocksuite/affine-components/toast';
import {
BookmarkStyles,
+ type EmbedCardStyle,
EmbedGithubModel,
+ EmbedGithubStyles,
isExternalEmbedModel,
} from '@blocksuite/affine-model';
+import {
+ EMBED_CARD_HEIGHT,
+ EMBED_CARD_WIDTH,
+} from '@blocksuite/affine-shared/consts';
import {
ActionPlacement,
EmbedOptionProvider,
+ type LinkEventType,
type ToolbarAction,
type ToolbarActionGroup,
+ type ToolbarContext,
type ToolbarModuleConfig,
+ ToolbarModuleExtension,
} from '@blocksuite/affine-shared/services';
import { getBlockProps } from '@blocksuite/affine-shared/utils';
-import { BlockSelection } from '@blocksuite/block-std';
+import { BlockFlavourIdentifier, BlockSelection } from '@blocksuite/block-std';
+import { Bound } from '@blocksuite/global/gfx';
import {
CaptionIcon,
CopyIcon,
@@ -20,8 +31,8 @@ import {
DuplicateIcon,
ResetIcon,
} from '@blocksuite/icons/lit';
-import { Slice, Text } from '@blocksuite/store';
-import { signal } from '@preact/signals-core';
+import { type ExtensionType, Slice, Text } from '@blocksuite/store';
+import { computed, signal } from '@preact/signals-core';
import { html } from 'lit';
import { keyed } from 'lit/directives/keyed.js';
import * as Y from 'yjs';
@@ -32,15 +43,46 @@ import type { EmbedLoomBlockComponent } from '../embed-loom-block';
import type { EmbedYoutubeBlockComponent } from '../embed-youtube-block';
const trackBaseProps = {
- segment: 'doc',
- page: 'doc editor',
- module: 'toolbar',
category: 'link',
type: 'card view',
};
+const previewAction = {
+ id: 'a.preview',
+ content(ctx) {
+ const model = ctx.getCurrentModel();
+ if (!model || !isExternalEmbedModel(model)) return null;
+
+ const { url } = model.props;
+ const options = ctx.std.get(EmbedOptionProvider).getEmbedBlockOptions(url);
+
+ if (options?.viewType !== 'card') return null;
+
+ return html``;
+ },
+} satisfies ToolbarAction;
+
+const createOnToggleFn =
+ (
+ ctx: ToolbarContext,
+ name: Extract<
+ LinkEventType,
+ | 'OpenedViewSelector'
+ | 'OpenedCardStyleSelector'
+ | 'OpenedCardScaleSelector'
+ >,
+ control: 'switch view' | 'switch card style' | 'switch card scale'
+ ) =>
+ (e: CustomEvent) => {
+ e.stopPropagation();
+ const opened = e.detail;
+ if (!opened) return;
+
+ ctx.track(name, { ...trackBaseProps, control });
+ };
+
// External embed blocks
-export function createBuiltinToolbarConfigForExternal(
+function createBuiltinToolbarConfigForExternal(
klass:
| typeof EmbedGithubBlockComponent
| typeof EmbedFigmaBlockComponent
@@ -49,22 +91,7 @@ export function createBuiltinToolbarConfigForExternal(
) {
return {
actions: [
- {
- id: 'a.preview',
- content(ctx) {
- const model = ctx.getCurrentBlockByType(klass)?.model;
- if (!model || !isExternalEmbedModel(model)) return null;
-
- const { url } = model.props;
- const options = ctx.std
- .get(EmbedOptionProvider)
- .getEmbedBlockOptions(url);
-
- if (options?.viewType !== 'card') return null;
-
- return html``;
- },
- },
+ previewAction,
{
id: 'b.conversions',
actions: [
@@ -72,7 +99,7 @@ export function createBuiltinToolbarConfigForExternal(
id: 'inline',
label: 'Inline view',
run(ctx) {
- const model = ctx.getCurrentBlockByType(klass)?.model;
+ const model = ctx.getCurrentModel();
if (!model || !isExternalEmbedModel(model)) return;
const { title, caption, url: link } = model.props;
@@ -105,7 +132,7 @@ export function createBuiltinToolbarConfigForExternal(
id: 'card',
label: 'Card view',
disabled(ctx) {
- const model = ctx.getCurrentBlockByType(klass)?.model;
+ const model = ctx.getCurrentModel();
if (!model || !isExternalEmbedModel(model)) return true;
const { url } = model.props;
@@ -116,7 +143,7 @@ export function createBuiltinToolbarConfigForExternal(
return options?.viewType === 'card';
},
run(ctx) {
- const model = ctx.getCurrentBlockByType(klass)?.model;
+ const model = ctx.getCurrentModel();
if (!model || !isExternalEmbedModel(model)) return;
const { url, caption } = model.props;
@@ -165,7 +192,7 @@ export function createBuiltinToolbarConfigForExternal(
id: 'embed',
label: 'Embed view',
disabled(ctx) {
- const model = ctx.getCurrentBlockByType(klass)?.model;
+ const model = ctx.getCurrentModel();
if (!model || !isExternalEmbedModel(model)) return false;
const { url } = model.props;
@@ -176,7 +203,7 @@ export function createBuiltinToolbarConfigForExternal(
return options?.viewType === 'embed';
},
when(ctx) {
- const model = ctx.getCurrentBlockByType(klass)?.model;
+ const model = ctx.getCurrentModel();
if (!model || !isExternalEmbedModel(model)) return false;
const { url } = model.props;
@@ -187,7 +214,7 @@ export function createBuiltinToolbarConfigForExternal(
return options?.viewType === 'embed';
},
run(ctx) {
- const model = ctx.getCurrentBlockByType(klass)?.model;
+ const model = ctx.getCurrentModel();
if (!model || !isExternalEmbedModel(model)) return;
const { url, caption } = model.props;
@@ -231,7 +258,7 @@ export function createBuiltinToolbarConfigForExternal(
},
],
content(ctx) {
- const model = ctx.getCurrentBlockByType(klass)?.model;
+ const model = ctx.getCurrentModel();
if (!model || !isExternalEmbedModel(model)) return null;
const { url } = model.props;
@@ -242,15 +269,11 @@ export function createBuiltinToolbarConfigForExternal(
const viewType$ = signal(
`${viewType === 'card' ? 'Card' : 'Embed'} view`
);
- const onToggle = (e: CustomEvent) => {
- const opened = e.detail;
- if (!opened) return;
-
- ctx.track('OpenedViewSelector', {
- ...trackBaseProps,
- control: 'switch view',
- });
- };
+ const onToggle = createOnToggleFn(
+ ctx,
+ 'OpenedViewSelector',
+ 'switch view'
+ );
return html`${keyed(
model,
@@ -275,6 +298,9 @@ export function createBuiltinToolbarConfigForExternal(
label: 'Small horizontal style',
},
],
+ when(ctx) {
+ return Boolean(ctx.getCurrentModelByType(EmbedGithubModel));
+ },
content(ctx) {
const model = ctx.getCurrentModelByType(EmbedGithubModel);
if (!model) return null;
@@ -291,16 +317,11 @@ export function createBuiltinToolbarConfigForExternal(
});
},
})) satisfies ToolbarAction[];
-
- const onToggle = (e: CustomEvent) => {
- const opened = e.detail;
- if (!opened) return;
-
- ctx.track('OpenedCardStyleSelector', {
- ...trackBaseProps,
- control: 'switch card style',
- });
- };
+ const onToggle = createOnToggleFn(
+ ctx,
+ 'OpenedCardStyleSelector',
+ 'switch card style'
+ );
return html`${keyed(
model,
@@ -393,3 +414,264 @@ export function createBuiltinToolbarConfigForExternal(
],
} as const satisfies ToolbarModuleConfig;
}
+
+const createBuiltinSurfaceToolbarConfigForExternal = (
+ klass:
+ | typeof EmbedGithubBlockComponent
+ | typeof EmbedFigmaBlockComponent
+ | typeof EmbedLoomBlockComponent
+ | typeof EmbedYoutubeBlockComponent
+) => {
+ return {
+ actions: [
+ previewAction,
+ {
+ id: 'b.conversions',
+ actions: [
+ {
+ id: 'card',
+ label: 'Card view',
+ run(ctx) {
+ const model = ctx.getCurrentBlockByType(klass)?.model;
+ if (!model || !isExternalEmbedModel(model)) return;
+
+ const { id: oldId, xywh, parent } = model;
+ const { url, caption } = model.props;
+
+ let { style } = model.props;
+ let flavour = 'affine:bookmark';
+
+ if (!BookmarkStyles.includes(style)) {
+ style = BookmarkStyles[0];
+ }
+
+ const bounds = Bound.deserialize(xywh);
+ bounds.w = EMBED_CARD_WIDTH[style];
+ bounds.h = EMBED_CARD_HEIGHT[style];
+
+ const newId = ctx.store.addBlock(
+ flavour,
+ { url, caption, style, xywh: bounds.serialize() },
+ parent
+ );
+
+ ctx.command.exec(reassociateConnectorsCommand, { oldId, newId });
+
+ ctx.store.deleteBlock(model);
+
+ // Selects new block
+ ctx.gfx.selection.set({ editing: false, elements: [newId] });
+
+ ctx.track('SelectedView', {
+ ...trackBaseProps,
+ control: 'select view',
+ type: 'card view',
+ });
+ },
+ },
+ {
+ id: 'embed',
+ label: 'Embed view',
+ disabled: true,
+ },
+ ],
+ when(ctx) {
+ const model = ctx.getCurrentBlockByType(klass)?.model;
+ if (!model || !isExternalEmbedModel(model)) return false;
+
+ const { url } = model.props;
+ const options = ctx.std
+ .get(EmbedOptionProvider)
+ .getEmbedBlockOptions(url);
+
+ return options?.viewType === 'embed';
+ },
+ content(ctx) {
+ const model = ctx.getCurrentBlockByType(klass)?.model;
+ if (!model || !isExternalEmbedModel(model)) return null;
+
+ const { url } = model.props;
+ const viewType =
+ ctx.std.get(EmbedOptionProvider).getEmbedBlockOptions(url)
+ ?.viewType ?? 'card';
+ const actions = this.actions.map(action => ({ ...action }));
+ const viewType$ = signal(
+ `${viewType === 'card' ? 'Card' : 'Embed'} view`
+ );
+ const onToggle = createOnToggleFn(
+ ctx,
+ 'OpenedViewSelector',
+ 'switch view'
+ );
+
+ return html`${keyed(
+ model,
+ html``
+ )}`;
+ },
+ } satisfies ToolbarActionGroup,
+ {
+ id: 'c.style',
+ actions: [
+ {
+ id: 'horizontal',
+ label: 'Large horizontal style',
+ },
+ {
+ id: 'list',
+ label: 'Small horizontal style',
+ },
+ {
+ id: 'vertical',
+ label: 'Large vertical style',
+ },
+ {
+ id: 'cube',
+ label: 'Small vertical style',
+ },
+ ].filter(action =>
+ EmbedGithubStyles.includes(action.id as EmbedCardStyle)
+ ),
+ when(ctx) {
+ return Boolean(ctx.getCurrentModelByType(EmbedGithubModel));
+ },
+ content(ctx) {
+ const model = ctx.getCurrentBlockByType(klass)?.model;
+ if (!model) return null;
+
+ const actions = this.actions.map(action => ({
+ ...action,
+ run: ({ store }) => {
+ const style = action.id as EmbedCardStyle;
+ const bounds = Bound.deserialize(model.xywh);
+ bounds.w = EMBED_CARD_WIDTH[style];
+ bounds.h = EMBED_CARD_HEIGHT[style];
+ const xywh = bounds.serialize();
+
+ store.updateBlock(model, { style, xywh });
+
+ ctx.track('SelectedCardStyle', {
+ ...trackBaseProps,
+ control: 'select card style',
+ type: style,
+ });
+ },
+ })) satisfies ToolbarAction[];
+ const style$ = model.props.style$;
+ const onToggle = createOnToggleFn(
+ ctx,
+ 'OpenedCardStyleSelector',
+ 'switch card style'
+ );
+
+ return html`${keyed(
+ model,
+ html``
+ )}`;
+ },
+ } satisfies ToolbarActionGroup,
+ {
+ id: 'd.caption',
+ tooltip: 'Caption',
+ icon: CaptionIcon(),
+ run(ctx) {
+ const block = ctx.getCurrentBlockByType(klass);
+ block?.captionEditor?.show();
+
+ ctx.track('OpenedCaptionEditor', {
+ ...trackBaseProps,
+ control: 'add caption',
+ });
+ },
+ },
+ {
+ id: 'e.scale',
+ content(ctx) {
+ const model = ctx.getCurrentBlockByType(klass)?.model;
+ if (!model) return null;
+
+ const scale$ = computed(() => {
+ const {
+ xywh$: { value: xywh },
+ } = model;
+ const {
+ style$: { value: style },
+ } = model.props;
+ const bounds = Bound.deserialize(xywh);
+ const height = EMBED_CARD_HEIGHT[style];
+ return Math.round(100 * (bounds.h / height));
+ });
+ const onSelect = (e: CustomEvent) => {
+ e.stopPropagation();
+
+ const scale = e.detail / 100;
+
+ const bounds = Bound.deserialize(model.xywh);
+ const style = model.props.style;
+ bounds.h = EMBED_CARD_HEIGHT[style] * scale;
+ bounds.w = EMBED_CARD_WIDTH[style] * scale;
+ const xywh = bounds.serialize();
+
+ ctx.store.updateBlock(model, { xywh });
+
+ ctx.track('SelectedCardScale', {
+ ...trackBaseProps,
+ control: 'select card scale',
+ });
+ };
+ const onToggle = createOnToggleFn(
+ ctx,
+ 'OpenedCardScaleSelector',
+ 'switch card scale'
+ );
+ const format = (value: number) => `${value}%`;
+
+ return html`${keyed(
+ model,
+ html``
+ )}`;
+ },
+ },
+ ],
+
+ when: ctx => ctx.getSurfaceBlocksByType(klass).length === 1,
+ } as const satisfies ToolbarModuleConfig;
+};
+
+export const createBuiltinToolbarConfigExtension = (
+ flavour: string,
+ klass:
+ | typeof EmbedGithubBlockComponent
+ | typeof EmbedFigmaBlockComponent
+ | typeof EmbedLoomBlockComponent
+ | typeof EmbedYoutubeBlockComponent
+): ExtensionType[] => {
+ const name = flavour.split(':').pop();
+
+ return [
+ ToolbarModuleExtension({
+ id: BlockFlavourIdentifier(flavour),
+ config: createBuiltinToolbarConfigForExternal(klass),
+ }),
+
+ ToolbarModuleExtension({
+ id: BlockFlavourIdentifier(`affine:surface:${name}`),
+ config: createBuiltinSurfaceToolbarConfigForExternal(klass),
+ }),
+ ];
+};
diff --git a/blocksuite/affine/blocks/block-embed/src/embed-figma-block/embed-figma-spec.ts b/blocksuite/affine/blocks/block-embed/src/embed-figma-block/embed-figma-spec.ts
index 330c57c9d..a41da4f25 100644
--- a/blocksuite/affine/blocks/block-embed/src/embed-figma-block/embed-figma-spec.ts
+++ b/blocksuite/affine/blocks/block-embed/src/embed-figma-block/embed-figma-spec.ts
@@ -1,15 +1,10 @@
import { EmbedFigmaBlockSchema } from '@blocksuite/affine-model';
-import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
-import {
- BlockServiceIdentifier,
- BlockViewExtension,
- FlavourExtension,
-} from '@blocksuite/block-std';
+import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
-import { createBuiltinToolbarConfigForExternal } from '../configs/toolbar';
+import { createBuiltinToolbarConfigExtension } from '../configs/toolbar';
import { EmbedFigmaBlockAdapterExtensions } from './adapters/extension';
import { embedFigmaSlashMenuConfig } from './configs/slash-menu';
import { EmbedFigmaBlockComponent } from './embed-figma-block';
@@ -26,9 +21,6 @@ export const EmbedFigmaBlockSpec: ExtensionType[] = [
}),
EmbedFigmaBlockAdapterExtensions,
EmbedFigmaBlockOptionConfig,
- ToolbarModuleExtension({
- id: BlockServiceIdentifier(flavour),
- config: createBuiltinToolbarConfigForExternal(EmbedFigmaBlockComponent),
- }),
+ createBuiltinToolbarConfigExtension(flavour, EmbedFigmaBlockComponent),
SlashMenuConfigExtension(flavour, embedFigmaSlashMenuConfig),
].flat();
diff --git a/blocksuite/affine/blocks/block-embed/src/embed-github-block/embed-github-spec.ts b/blocksuite/affine/blocks/block-embed/src/embed-github-block/embed-github-spec.ts
index 67b1e57fd..1f97d360d 100644
--- a/blocksuite/affine/blocks/block-embed/src/embed-github-block/embed-github-spec.ts
+++ b/blocksuite/affine/blocks/block-embed/src/embed-github-block/embed-github-spec.ts
@@ -1,15 +1,10 @@
import { EmbedGithubBlockSchema } from '@blocksuite/affine-model';
-import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
-import {
- BlockServiceIdentifier,
- BlockViewExtension,
- FlavourExtension,
-} from '@blocksuite/block-std';
+import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
-import { createBuiltinToolbarConfigForExternal } from '../configs/toolbar';
+import { createBuiltinToolbarConfigExtension } from '../configs/toolbar';
import { EmbedGithubBlockAdapterExtensions } from './adapters/extension';
import { embedGithubSlashMenuConfig } from './configs/slash-menu';
import { EmbedGithubBlockComponent } from './embed-github-block';
@@ -30,9 +25,6 @@ export const EmbedGithubBlockSpec: ExtensionType[] = [
}),
EmbedGithubBlockAdapterExtensions,
EmbedGithubBlockOptionConfig,
- ToolbarModuleExtension({
- id: BlockServiceIdentifier(flavour),
- config: createBuiltinToolbarConfigForExternal(EmbedGithubBlockComponent),
- }),
+ createBuiltinToolbarConfigExtension(flavour, EmbedGithubBlockComponent),
SlashMenuConfigExtension(flavour, embedGithubSlashMenuConfig),
].flat();
diff --git a/blocksuite/affine/blocks/block-embed/src/embed-loom-block/embed-loom-spec.ts b/blocksuite/affine/blocks/block-embed/src/embed-loom-block/embed-loom-spec.ts
index bdf3ae33d..4edf2d877 100644
--- a/blocksuite/affine/blocks/block-embed/src/embed-loom-block/embed-loom-spec.ts
+++ b/blocksuite/affine/blocks/block-embed/src/embed-loom-block/embed-loom-spec.ts
@@ -1,15 +1,10 @@
import { EmbedLoomBlockSchema } from '@blocksuite/affine-model';
-import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
-import {
- BlockServiceIdentifier,
- BlockViewExtension,
- FlavourExtension,
-} from '@blocksuite/block-std';
+import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
-import { createBuiltinToolbarConfigForExternal } from '../configs/toolbar';
+import { createBuiltinToolbarConfigExtension } from '../configs/toolbar';
import { EmbedLoomBlockAdapterExtensions } from './adapters/extension';
import { embedLoomSlashMenuConfig } from './configs/slash-menu';
import { EmbedLoomBlockComponent } from './embed-loom-block';
@@ -30,9 +25,6 @@ export const EmbedLoomBlockSpec: ExtensionType[] = [
}),
EmbedLoomBlockAdapterExtensions,
EmbedLoomBlockOptionConfig,
- ToolbarModuleExtension({
- id: BlockServiceIdentifier(flavour),
- config: createBuiltinToolbarConfigForExternal(EmbedLoomBlockComponent),
- }),
+ createBuiltinToolbarConfigExtension(flavour, EmbedLoomBlockComponent),
SlashMenuConfigExtension(flavour, embedLoomSlashMenuConfig),
].flat();
diff --git a/blocksuite/affine/blocks/block-embed/src/embed-youtube-block/embed-youtube-spec.ts b/blocksuite/affine/blocks/block-embed/src/embed-youtube-block/embed-youtube-spec.ts
index d40893ffc..101f79ce4 100644
--- a/blocksuite/affine/blocks/block-embed/src/embed-youtube-block/embed-youtube-spec.ts
+++ b/blocksuite/affine/blocks/block-embed/src/embed-youtube-block/embed-youtube-spec.ts
@@ -1,15 +1,10 @@
import { EmbedYoutubeBlockSchema } from '@blocksuite/affine-model';
-import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
-import {
- BlockServiceIdentifier,
- BlockViewExtension,
- FlavourExtension,
-} from '@blocksuite/block-std';
+import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
-import { createBuiltinToolbarConfigForExternal } from '../configs/toolbar';
+import { createBuiltinToolbarConfigExtension } from '../configs/toolbar';
import { EmbedYoutubeBlockAdapterExtensions } from './adapters/extension';
import { embedYoutubeSlashMenuConfig } from './configs/slash-menu';
import { EmbedYoutubeBlockComponent } from './embed-youtube-block';
@@ -30,9 +25,6 @@ export const EmbedYoutubeBlockSpec: ExtensionType[] = [
}),
EmbedYoutubeBlockAdapterExtensions,
EmbedYoutubeBlockOptionConfig,
- ToolbarModuleExtension({
- id: BlockServiceIdentifier(flavour),
- config: createBuiltinToolbarConfigForExternal(EmbedYoutubeBlockComponent),
- }),
+ createBuiltinToolbarConfigExtension(flavour, EmbedYoutubeBlockComponent),
SlashMenuConfigExtension('affine:embed-youtube', embedYoutubeSlashMenuConfig),
].flat();
diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/embed.ts b/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/embed.ts
deleted file mode 100644
index af6fe6286..000000000
--- a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/embed.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { ToolbarModuleConfig } from '@blocksuite/affine-shared/services';
-
-export const builtinEmbedToolbarConfig = {
- actions: [
- {
- id: 'a.test',
- label: 'Embed',
- run() {},
- },
- ],
-} as const satisfies ToolbarModuleConfig;
diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts b/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts
index 99242da8c..3890dc353 100644
--- a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts
+++ b/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts
@@ -4,7 +4,6 @@ import type { ExtensionType } from '@blocksuite/store';
import { builtinBrushToolbarConfig } from './brush';
import { builtinConnectorToolbarConfig } from './connector';
-import { builtinEmbedToolbarConfig } from './embed';
import { builtinFrameToolbarConfig } from './frame';
import { builtinGroupToolbarConfig } from './group';
import { builtinImageToolbarConfig } from './image';
@@ -30,11 +29,6 @@ export const EdgelessElementToolbarExtension: ExtensionType[] = [
config: builtinConnectorToolbarConfig,
}),
- ToolbarModuleExtension({
- id: BlockFlavourIdentifier('affine:surface:embed'),
- config: builtinEmbedToolbarConfig,
- }),
-
ToolbarModuleExtension({
id: BlockFlavourIdentifier('affine:surface:frame'),
config: builtinFrameToolbarConfig,
diff --git a/blocksuite/affine/model/src/blocks/embed/types.ts b/blocksuite/affine/model/src/blocks/embed/types.ts
index 446588d2d..5ff4cf53f 100644
--- a/blocksuite/affine/model/src/blocks/embed/types.ts
+++ b/blocksuite/affine/model/src/blocks/embed/types.ts
@@ -1,3 +1,4 @@
+import type { GfxModel } from '@blocksuite/block-std/gfx';
import type { BlockModel } from '@blocksuite/store';
import { EmbedFigmaModel } from './figma';
@@ -34,7 +35,7 @@ export type LinkableEmbedModel = EmbedCardModel | EmbedIframeBlockModel;
export type BuiltInEmbedModel = EmbedCardModel | EmbedHtmlModel;
export function isExternalEmbedModel(
- model: BlockModel
+ model: GfxModel | BlockModel
): model is InstanceType {
return (
model instanceof EmbedFigmaModel ||
@@ -45,7 +46,7 @@ export function isExternalEmbedModel(
}
export function isInternalEmbedModel(
- model: BlockModel
+ model: GfxModel | BlockModel
): model is InstanceType {
return (
model instanceof EmbedLinkedDocModel || model instanceof EmbedSyncedDocModel
diff --git a/blocksuite/affine/shared/src/services/toolbar-service/context.ts b/blocksuite/affine/shared/src/services/toolbar-service/context.ts
index b99862bb8..6d2426ef0 100644
--- a/blocksuite/affine/shared/src/services/toolbar-service/context.ts
+++ b/blocksuite/affine/shared/src/services/toolbar-service/context.ts
@@ -128,8 +128,8 @@ abstract class ToolbarContextBase {
);
}
- getSurfaceModelsByType any>(
- klass: M
+ getSurfaceModelsByType any>(
+ klass: T
) {
if (this.hasSelectedSurfaceModels) {
const elements = this.elementsMap$.peek().get(this.flavour$.peek());
@@ -140,6 +140,20 @@ abstract class ToolbarContextBase {
return [];
}
+ getSurfaceBlocksByType any>(
+ klass: T
+ ) {
+ if (this.hasSelectedSurfaceModels) {
+ const elements = this.elementsMap$.peek().get(this.flavour$.peek());
+ if (elements?.length) {
+ return elements
+ .map(model => this.gfx.view.get(model.id))
+ .filter(block => block && this.matchBlock(block, klass));
+ }
+ }
+ return [];
+ }
+
getCurrentBlockBy(type: T) {
const selection = this.selection.find(type);
if (!selection) return null;
@@ -160,17 +174,17 @@ abstract class ToolbarContextBase {
: this.getCurrentBlockBy(BlockSelection);
}
- getCurrentBlockByType any>(
- klass: K
+ getCurrentBlockByType any>(
+ klass: T
) {
const block = this.getCurrentBlock();
return this.matchBlock(block, klass) ? block : null;
}
- matchBlock any>(
+ matchBlock any>(
component: GfxElementModelView | BlockComponent | null,
- klass: K
- ): component is InstanceType {
+ klass: T
+ ): component is InstanceType {
return component instanceof klass;
}
@@ -184,23 +198,23 @@ abstract class ToolbarContextBase {
return this.store.getBlock(selection.blockId)?.model ?? null;
}
- getCurrentModel() {
+ getCurrentModel(): GfxModel | BlockModel | null {
return this.hasSelectedSurfaceModels
? this.getCurrentModelBy(SurfaceSelection)
: this.getCurrentModelBy(BlockSelection);
}
- getCurrentModelByType any>(
- klass: M
+ getCurrentModelByType any>(
+ klass: T
) {
const model = this.getCurrentModel();
return this.matchModel(model, klass) ? model : null;
}
- matchModel any>(
+ matchModel any>(
model: GfxModel | BlockModel | null,
- klass: K
- ): model is InstanceType {
+ klass: T
+ ): model is InstanceType {
return model instanceof klass;
}
diff --git a/blocksuite/affine/widgets/widget-toolbar/src/toolbar.ts b/blocksuite/affine/widgets/widget-toolbar/src/toolbar.ts
index c3934a427..d48452b0a 100644
--- a/blocksuite/affine/widgets/widget-toolbar/src/toolbar.ts
+++ b/blocksuite/affine/widgets/widget-toolbar/src/toolbar.ts
@@ -397,23 +397,30 @@ export class AffineToolbarWidget extends WidgetComponent {
// `card view` or `embed view`
disposables.add(
std.view.viewUpdated.subscribe(record => {
- if (record.type !== 'block') return;
- if (!flags.isBlock()) return;
+ const hasAddedBlock =
+ record.type === 'block' && record.method === 'add';
+ if (!hasAddedBlock) return;
- const blockIds = std.selection
- .filter$(BlockSelection)
- .peek()
- .map(s => s.blockId);
+ if (flags.isBlock()) {
+ const blockIds = std.selection
+ .filter$(BlockSelection)
+ .peek()
+ .map(s => s.blockId);
+ if (blockIds.includes(record.id)) {
+ batch(() => {
+ this.setReferenceElementWithBlocks(
+ blockIds
+ .map(id => std.view.getBlock(id))
+ .filter(block => block !== null)
+ );
+ flags.refresh(Flag.Block);
+ });
+ }
+ return;
+ }
- if (record.method === 'add' && blockIds.includes(record.id)) {
- batch(() => {
- this.setReferenceElementWithBlocks(
- blockIds
- .map(id => std.view.getBlock(id))
- .filter(block => block !== null)
- );
- flags.refresh(Flag.Block);
- });
+ if (flags.isSurface()) {
+ flags.refresh(Flag.Surface);
return;
}
})
diff --git a/packages/frontend/core/src/blocksuite/extensions/editor-config/toolbar/index.ts b/packages/frontend/core/src/blocksuite/extensions/editor-config/toolbar/index.ts
index 74fe93f43..e1d8ccc7d 100644
--- a/packages/frontend/core/src/blocksuite/extensions/editor-config/toolbar/index.ts
+++ b/packages/frontend/core/src/blocksuite/extensions/editor-config/toolbar/index.ts
@@ -425,7 +425,6 @@ function createExternalLinkableToolbarConfig(
toast(ctx.host, 'Copied link to clipboard');
ctx.track('CopiedLink', {
- module: 'toolbar',
category: matchModels(model, [BookmarkBlockModel])
? 'bookmark'
: 'link',
@@ -462,7 +461,6 @@ function createExternalLinkableToolbarConfig(
);
ctx.track('OpenedAliasPopup', {
- module: 'toolbar',
category: matchModels(model, [BookmarkBlockModel])
? 'bookmark'
: 'link',
@@ -600,9 +598,6 @@ const embedLinkedDocToolbarConfig = {
toast(ctx.host, 'Copied link to clipboard');
ctx.track('CopiedLink', {
- segment: 'doc',
- page: 'doc editor',
- module: 'toolbar',
category: 'linked doc',
type: 'card view',
control: 'copy link',
@@ -648,9 +643,6 @@ const embedLinkedDocToolbarConfig = {
);
ctx.track('OpenedAliasPopup', {
- segment: 'doc',
- page: 'doc editor',
- module: 'toolbar',
category: 'linked doc',
type: 'embed view',
control: 'edit',
@@ -689,9 +681,6 @@ const embedSyncedDocToolbarConfig = {
toast(ctx.host, 'Copied link to clipboard');
ctx.track('CopiedLink', {
- segment: 'doc',
- page: 'doc editor',
- module: 'toolbar',
category: 'linked doc',
type: 'embed view',
control: 'copy link',
@@ -730,9 +719,6 @@ const embedSyncedDocToolbarConfig = {
);
ctx.track('OpenedAliasPopup', {
- segment: 'doc',
- page: 'doc editor',
- module: 'toolbar',
category: 'linked doc',
type: 'embed view',
control: 'edit',
@@ -846,9 +832,6 @@ const inlineReferenceToolbarConfig = {
toast(ctx.host, 'Copied link to clipboard');
ctx.track('CopiedLink', {
- segment: 'doc',
- page: 'doc editor',
- module: 'toolbar',
category: 'linked doc',
type: 'inline view',
control: 'copy link',
@@ -882,9 +865,6 @@ const inlineReferenceToolbarConfig = {
abortController.signal.onabort = () => popover.remove();
ctx.track('OpenedAliasPopup', {
- segment: 'doc',
- page: 'doc editor',
- module: 'toolbar',
category: 'linked doc',
type: 'inline view',
control: 'edit',
@@ -992,21 +972,41 @@ export const createCustomToolbarExtension = (
config: createExternalLinkableToolbarConfig(EmbedFigmaBlockComponent),
}),
+ ToolbarModuleExtension({
+ id: BlockFlavourIdentifier('custom:affine:surface:embed-figma'),
+ config: createExternalLinkableToolbarConfig(BookmarkBlockComponent),
+ }),
+
ToolbarModuleExtension({
id: BlockFlavourIdentifier('custom:affine:embed-github'),
config: createExternalLinkableToolbarConfig(EmbedGithubBlockComponent),
}),
+ ToolbarModuleExtension({
+ id: BlockFlavourIdentifier('custom:affine:surface:embed-github'),
+ config: createExternalLinkableToolbarConfig(BookmarkBlockComponent),
+ }),
+
ToolbarModuleExtension({
id: BlockFlavourIdentifier('custom:affine:embed-loom'),
config: createExternalLinkableToolbarConfig(EmbedLoomBlockComponent),
}),
+ ToolbarModuleExtension({
+ id: BlockFlavourIdentifier('custom:affine:surface:embed-loom'),
+ config: createExternalLinkableToolbarConfig(BookmarkBlockComponent),
+ }),
+
ToolbarModuleExtension({
id: BlockFlavourIdentifier('custom:affine:embed-youtube'),
config: createExternalLinkableToolbarConfig(EmbedYoutubeBlockComponent),
}),
+ ToolbarModuleExtension({
+ id: BlockFlavourIdentifier('custom:affine:surface:embed-youtube'),
+ config: createExternalLinkableToolbarConfig(BookmarkBlockComponent),
+ }),
+
ToolbarModuleExtension({
id: BlockFlavourIdentifier('custom:affine:embed-linked-doc'),
config: embedLinkedDocToolbarConfig,