refactor(editor): surface-ref slash menu config extension (#10681)

This commit is contained in:
L-Sun
2025-03-07 09:05:03 +00:00
parent d37868d97a
commit 83b05aca4d
11 changed files with 293 additions and 293 deletions

View File

@@ -17,9 +17,10 @@
"@blocksuite/affine-components": "workspace:*", "@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-model": "workspace:*", "@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-shared": "workspace:*", "@blocksuite/affine-shared": "workspace:*",
"@blocksuite/affine-widget-slash-menu": "workspace:*",
"@blocksuite/block-std": "workspace:*", "@blocksuite/block-std": "workspace:*",
"@blocksuite/global": "workspace:*", "@blocksuite/global": "workspace:*",
"@blocksuite/icons": "^2.2.3", "@blocksuite/icons": "^2.2.4",
"@blocksuite/inline": "workspace:*", "@blocksuite/inline": "workspace:*",
"@blocksuite/store": "workspace:*", "@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2", "@lit/context": "^1.1.2",

View File

@@ -0,0 +1,75 @@
import { getSurfaceBlock } from '@blocksuite/affine-block-surface';
import type { FrameBlockModel } from '@blocksuite/affine-model';
import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands';
import {
type SlashMenuActionItem,
type SlashMenuConfig,
SlashMenuConfigExtension,
} from '@blocksuite/affine-widget-slash-menu';
import { FrameIcon, GroupingIcon } from '@blocksuite/icons/lit';
import { insertSurfaceRefBlockCommand } from '../commands';
import { EdgelessTooltip } from './tooltips';
const surfaceRefSlashMenuConfig: SlashMenuConfig = {
items: ({ std }) => {
let index = 0;
const surfaceModel = getSurfaceBlock(std.store);
if (!surfaceModel) return [];
const frameModels = std.store
.getBlocksByFlavour('affine:frame')
.map(block => block.model as FrameBlockModel);
const frameItems = frameModels.map<SlashMenuActionItem>(frameModel => ({
name: 'Frame: ' + frameModel.title,
icon: FrameIcon(),
group: `5_Document Group & Frame@${index++}`,
tooltip: {
figure: EdgelessTooltip,
caption: 'Edgeless',
},
action: ({ std }) => {
std.command
.chain()
.pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, {
reference: frameModel.id,
place: 'after',
removeEmptyLine: true,
})
.run();
},
}));
const groupElements = surfaceModel.getElementsByType('group');
const groupItems = groupElements.map<SlashMenuActionItem>(group => ({
name: 'Group: ' + group.title.toString(),
icon: GroupingIcon(),
group: `5_Document Group & Frame@${index++}`,
tooltip: {
figure: EdgelessTooltip,
caption: 'Edgeless',
},
action: ({ std }) => {
std.command
.chain()
.pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, {
reference: group.id,
place: 'after',
removeEmptyLine: true,
})
.run();
},
}));
return [...frameItems, ...groupItems];
},
};
export const SurfaceRefSlashMenuConfigExtension = SlashMenuConfigExtension(
'affine:surface-ref',
surfaceRefSlashMenuConfig
);

View File

@@ -6,6 +6,8 @@ import {
import type { ExtensionType } from '@blocksuite/store'; import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js'; import { literal } from 'lit/static-html.js';
import { SurfaceRefSlashMenuConfigExtension } from './configs/slash-menu';
export const surfaceRefToolbarWidget = WidgetViewExtension( export const surfaceRefToolbarWidget = WidgetViewExtension(
'affine:surface-ref', 'affine:surface-ref',
'surfaceToolbar', 'surfaceToolbar',
@@ -16,6 +18,7 @@ export const PageSurfaceRefBlockSpec: ExtensionType[] = [
FlavourExtension('affine:surface-ref'), FlavourExtension('affine:surface-ref'),
BlockViewExtension('affine:surface-ref', literal`affine-surface-ref`), BlockViewExtension('affine:surface-ref', literal`affine-surface-ref`),
surfaceRefToolbarWidget, surfaceRefToolbarWidget,
SurfaceRefSlashMenuConfigExtension,
]; ];
export const EdgelessSurfaceRefBlockSpec: ExtensionType[] = [ export const EdgelessSurfaceRefBlockSpec: ExtensionType[] = [
@@ -24,4 +27,5 @@ export const EdgelessSurfaceRefBlockSpec: ExtensionType[] = [
'affine:surface-ref', 'affine:surface-ref',
literal`affine-edgeless-surface-ref` literal`affine-edgeless-surface-ref`
), ),
SurfaceRefSlashMenuConfigExtension,
]; ];

View File

@@ -11,6 +11,7 @@
{ "path": "../components" }, { "path": "../components" },
{ "path": "../model" }, { "path": "../model" },
{ "path": "../shared" }, { "path": "../shared" },
{ "path": "../widget-slash-menu" },
{ "path": "../../framework/block-std" }, { "path": "../../framework/block-std" },
{ "path": "../../framework/global" }, { "path": "../../framework/global" },
{ "path": "../../framework/inline" }, { "path": "../../framework/inline" },

View File

@@ -13,8 +13,6 @@
"author": "toeverything", "author": "toeverything",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-block-surface-ref": "workspace:*",
"@blocksuite/affine-components": "workspace:*", "@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-rich-text": "workspace:*", "@blocksuite/affine-rich-text": "workspace:*",
"@blocksuite/affine-shared": "workspace:*", "@blocksuite/affine-shared": "workspace:*",

View File

@@ -1,15 +1,9 @@
import { getSurfaceBlock } from '@blocksuite/affine-block-surface';
import { insertSurfaceRefBlockCommand } from '@blocksuite/affine-block-surface-ref';
import { toast } from '@blocksuite/affine-components/toast'; import { toast } from '@blocksuite/affine-components/toast';
import type { import type { ParagraphBlockModel } from '@blocksuite/affine-model';
FrameBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import { import {
getInlineEditorByModel, getInlineEditorByModel,
insertContent, insertContent,
} from '@blocksuite/affine-rich-text'; } from '@blocksuite/affine-rich-text';
import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands';
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts'; import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
import { createDefaultDoc } from '@blocksuite/affine-shared/utils'; import { createDefaultDoc } from '@blocksuite/affine-shared/utils';
import { import {
@@ -18,8 +12,6 @@ import {
CopyIcon, CopyIcon,
DeleteIcon, DeleteIcon,
DualLinkIcon, DualLinkIcon,
FrameIcon,
GroupingIcon,
LinkedPageIcon, LinkedPageIcon,
NowIcon, NowIcon,
PlusIcon, PlusIcon,
@@ -31,289 +23,228 @@ import type { DeltaInsert } from '@blocksuite/inline';
import { Slice, Text } from '@blocksuite/store'; import { Slice, Text } from '@blocksuite/store';
import { slashMenuToolTips } from './tooltips'; import { slashMenuToolTips } from './tooltips';
import type { SlashMenuActionItem, SlashMenuConfig } from './types'; import type { SlashMenuConfig } from './types';
import { formatDate, formatTime } from './utils'; import { formatDate, formatTime } from './utils';
// TODO(@L-Sun): This counter temporarily added variables for refactoring.
let index = 0;
export const defaultSlashMenuConfig: SlashMenuConfig = { export const defaultSlashMenuConfig: SlashMenuConfig = {
disableWhen: ({ model }) => { disableWhen: ({ model }) => {
return model.flavour === 'affine:code'; return model.flavour === 'affine:code';
}, },
items: ({ std, model }) => [ items: () => {
{ const now = new Date();
name: 'New Doc', const tomorrow = new Date();
description: 'Start a new document.', const yesterday = new Date();
icon: PlusIcon(), yesterday.setDate(yesterday.getDate() - 1);
tooltip: slashMenuToolTips['New Doc'], tomorrow.setDate(tomorrow.getDate() + 1);
group: `3_Page@${index++}`,
when: ({ model }) => return [
model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'), {
action: ({ std, model }) => { name: 'New Doc',
const newDoc = createDefaultDoc(std.host.doc.workspace); description: 'Start a new document.',
insertContent(std.host, model, REFERENCE_NODE, { icon: PlusIcon(),
reference: { tooltip: slashMenuToolTips['New Doc'],
type: 'LinkedPage', group: '3_Page@0',
pageId: newDoc.id, when: ({ model }) =>
}, model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'),
}); action: ({ std, model }) => {
const newDoc = createDefaultDoc(std.host.doc.workspace);
insertContent(std.host, model, REFERENCE_NODE, {
reference: {
type: 'LinkedPage',
pageId: newDoc.id,
},
});
},
}, },
}, {
{ name: 'Linked Doc',
name: 'Linked Doc', description: 'Link to another document.',
description: 'Link to another document.', icon: LinkedPageIcon(),
icon: LinkedPageIcon(), tooltip: slashMenuToolTips['Linked Doc'],
tooltip: slashMenuToolTips['Linked Doc'], searchAlias: ['dual link'],
searchAlias: ['dual link'], group: '3_Page@1',
group: `3_Page@${index++}`, when: ({ std, model }) => {
when: ({ std, model }) => { const root = model.doc.root;
const root = model.doc.root; if (!root) return false;
if (!root) return false; const linkedDocWidget = std.view.getWidget(
const linkedDocWidget = std.view.getWidget( 'affine-linked-doc-widget',
'affine-linked-doc-widget', root.id
root.id );
); if (!linkedDocWidget) return false;
if (!linkedDocWidget) return false;
return model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'); return model.doc.schema.flavourSchemaMap.has(
}, 'affine:embed-linked-doc'
action: ({ model, std }) => { );
const root = model.doc.root; },
if (!root) return; action: ({ model, std }) => {
const linkedDocWidget = std.view.getWidget( const root = model.doc.root;
'affine-linked-doc-widget', if (!root) return;
root.id const linkedDocWidget = std.view.getWidget(
); 'affine-linked-doc-widget',
if (!linkedDocWidget) return; root.id
// TODO(@L-Sun): make linked-doc-widget as extension );
// @ts-expect-error same as above if (!linkedDocWidget) return;
const triggerKey = linkedDocWidget.config.triggerKeys[0];
insertContent(std.host, model, triggerKey);
const inlineEditor = getInlineEditorByModel(std.host, model);
// Wait for range to be updated
inlineEditor?.slots.inlineRangeSync.once(() => {
// TODO(@L-Sun): make linked-doc-widget as extension // TODO(@L-Sun): make linked-doc-widget as extension
// @ts-expect-error same as above // @ts-expect-error same as above
linkedDocWidget.show({ addTriggerKey: true }); const triggerKey = linkedDocWidget.config.triggerKeys[0];
});
},
},
// --------------------------------------------------------- insertContent(std.host, model, triggerKey);
...(() => {
const { host } = std;
const surfaceModel = getSurfaceBlock(host.doc); const inlineEditor = getInlineEditorByModel(std.host, model);
if (!surfaceModel) return []; // Wait for range to be updated
inlineEditor?.slots.inlineRangeSync.once(() => {
const parent = host.doc.getParent(model); // TODO(@L-Sun): make linked-doc-widget as extension
if (!parent) return []; // @ts-expect-error same as above
linkedDocWidget.show({ addTriggerKey: true });
const frameModels = host.doc
.getBlocksByFlavour('affine:frame')
.map(block => block.model as FrameBlockModel);
const frameItems = frameModels.map<SlashMenuActionItem>(frameModel => ({
name: 'Frame: ' + frameModel.title,
icon: FrameIcon(),
group: `5_Document Group & Frame@${index++}`,
action: ({ std }) => {
std.command
.chain()
.pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, {
reference: frameModel.id,
place: 'after',
removeEmptyLine: true,
})
.run();
},
}));
const groupElements = surfaceModel.getElementsByType('group');
const groupItems = groupElements.map<SlashMenuActionItem>(group => ({
name: 'Group: ' + group.title.toString(),
icon: GroupingIcon(),
group: `5_Document Group & Frame@${index++}`,
action: ({ std }) => {
std.command
.chain()
.pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, {
reference: group.id,
place: 'after',
removeEmptyLine: true,
})
.run();
},
}));
return [...frameItems, ...groupItems];
})(),
// ---------------------------------------------------------
...((): SlashMenuActionItem[] => {
const now = new Date();
const tomorrow = new Date();
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
tomorrow.setDate(tomorrow.getDate() + 1);
return [
{
name: 'Today',
icon: TodayIcon(),
tooltip: slashMenuToolTips['Today'],
description: formatDate(now),
group: `6_Date@${index++}`,
action: ({ std, model }) => {
insertContent(std.host, model, formatDate(now));
},
},
{
name: 'Tomorrow',
icon: TomorrowIcon(),
tooltip: slashMenuToolTips['Tomorrow'],
description: formatDate(tomorrow),
group: `6_Date@${index++}`,
action: ({ std, model }) => {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
insertContent(std.host, model, formatDate(tomorrow));
},
},
{
name: 'Yesterday',
icon: YesterdayIcon(),
tooltip: slashMenuToolTips['Yesterday'],
description: formatDate(yesterday),
group: `6_Date@${index++}`,
action: ({ std, model }) => {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
insertContent(std.host, model, formatDate(yesterday));
},
},
{
name: 'Now',
icon: NowIcon(),
tooltip: slashMenuToolTips['Now'],
description: formatTime(now),
group: `6_Date@${index++}`,
action: ({ std, model }) => {
insertContent(std.host, model, formatTime(now));
},
},
];
})(),
// ---------------------------------------------------------
// { groupName: 'Actions' },
{
name: 'Move Up',
description: 'Shift this line up.',
icon: ArrowUpBigIcon(),
tooltip: slashMenuToolTips['Move Up'],
group: `8_Actions@${index++}`,
action: ({ std, model }) => {
const { host } = std;
const previousSiblingModel = host.doc.getPrev(model);
if (!previousSiblingModel) return;
const parentModel = host.doc.getParent(previousSiblingModel);
if (!parentModel) return;
host.doc.moveBlocks([model], parentModel, previousSiblingModel, true);
},
},
{
name: 'Move Down',
description: 'Shift this line down.',
icon: ArrowDownBigIcon(),
tooltip: slashMenuToolTips['Move Down'],
group: `8_Actions@${index++}`,
action: ({ std, model }) => {
const { host } = std;
const nextSiblingModel = host.doc.getNext(model);
if (!nextSiblingModel) return;
const parentModel = host.doc.getParent(nextSiblingModel);
if (!parentModel) return;
host.doc.moveBlocks([model], parentModel, nextSiblingModel, false);
},
},
{
name: 'Copy',
description: 'Copy this line to clipboard.',
icon: CopyIcon(),
tooltip: slashMenuToolTips['Copy'],
group: `8_Actions@${index++}`,
action: ({ std, model }) => {
const slice = Slice.fromModels(std.store, [model]);
std.clipboard
.copy(slice)
.then(() => {
toast(std.host, 'Copied to clipboard');
})
.catch(e => {
console.error(e);
}); });
},
}, },
}, {
{ name: 'Today',
name: 'Duplicate', icon: TodayIcon(),
description: 'Create a duplicate of this line.', tooltip: slashMenuToolTips['Today'],
icon: DualLinkIcon(), description: formatDate(now),
tooltip: slashMenuToolTips['Copy'], group: '6_Date@0',
group: `8_Actions@${index++}`, action: ({ std, model }) => {
action: ({ std, model }) => { insertContent(std.host, model, formatDate(now));
if (!model.text || !(model.text instanceof Text)) { },
console.error("Can't duplicate a block without text"); },
return; {
} name: 'Tomorrow',
const { host } = std; icon: TomorrowIcon(),
const parent = host.doc.getParent(model); tooltip: slashMenuToolTips['Tomorrow'],
if (!parent) { description: formatDate(tomorrow),
console.error( group: '6_Date@1',
'Failed to duplicate block! Parent not found: ' + action: ({ std, model }) => {
model.id + const tomorrow = new Date();
'|' + tomorrow.setDate(tomorrow.getDate() + 1);
model.flavour insertContent(std.host, model, formatDate(tomorrow));
); },
return; },
} {
const index = parent.children.indexOf(model); name: 'Yesterday',
icon: YesterdayIcon(),
tooltip: slashMenuToolTips['Yesterday'],
description: formatDate(yesterday),
group: '6_Date@2',
action: ({ std, model }) => {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
insertContent(std.host, model, formatDate(yesterday));
},
},
{
name: 'Now',
icon: NowIcon(),
tooltip: slashMenuToolTips['Now'],
description: formatTime(now),
group: '6_Date@3',
action: ({ std, model }) => {
insertContent(std.host, model, formatTime(now));
},
},
{
name: 'Move Up',
description: 'Shift this line up.',
icon: ArrowUpBigIcon(),
tooltip: slashMenuToolTips['Move Up'],
group: '8_Actions@0',
action: ({ std, model }) => {
const { host } = std;
const previousSiblingModel = host.doc.getPrev(model);
if (!previousSiblingModel) return;
// TODO add clone model util const parentModel = host.doc.getParent(previousSiblingModel);
host.doc.addBlock( if (!parentModel) return;
model.flavour as never,
{ host.doc.moveBlocks([model], parentModel, previousSiblingModel, true);
type: (model as ParagraphBlockModel).type, },
text: new Text(model.text.toDelta() as DeltaInsert[]),
// @ts-expect-error FIXME: ts error
checked: model.checked,
},
host.doc.getParent(model),
index
);
}, },
}, {
{ name: 'Move Down',
name: 'Delete', description: 'Shift this line down.',
description: 'Remove this line permanently.', icon: ArrowDownBigIcon(),
searchAlias: ['remove'], tooltip: slashMenuToolTips['Move Down'],
icon: DeleteIcon(), group: '8_Actions@1',
tooltip: slashMenuToolTips['Delete'], action: ({ std, model }) => {
group: `8_Actions@${index++}`, const { host } = std;
action: ({ std, model }) => { const nextSiblingModel = host.doc.getNext(model);
std.host.doc.deleteBlock(model); if (!nextSiblingModel) return;
const parentModel = host.doc.getParent(nextSiblingModel);
if (!parentModel) return;
host.doc.moveBlocks([model], parentModel, nextSiblingModel, false);
},
}, },
}, {
], name: 'Copy',
description: 'Copy this line to clipboard.',
icon: CopyIcon(),
tooltip: slashMenuToolTips['Copy'],
group: '8_Actions@2',
action: ({ std, model }) => {
const slice = Slice.fromModels(std.store, [model]);
std.clipboard
.copy(slice)
.then(() => {
toast(std.host, 'Copied to clipboard');
})
.catch(e => {
console.error(e);
});
},
},
{
name: 'Duplicate',
description: 'Create a duplicate of this line.',
icon: DualLinkIcon(),
tooltip: slashMenuToolTips['Copy'],
group: '8_Actions@3',
action: ({ std, model }) => {
if (!model.text || !(model.text instanceof Text)) {
console.error("Can't duplicate a block without text");
return;
}
const { host } = std;
const parent = host.doc.getParent(model);
if (!parent) {
console.error(
'Failed to duplicate block! Parent not found: ' +
model.id +
'|' +
model.flavour
);
return;
}
const index = parent.children.indexOf(model);
// TODO add clone model util
host.doc.addBlock(
model.flavour as never,
{
type: (model as ParagraphBlockModel).type,
text: new Text(model.text.toDelta() as DeltaInsert[]),
// @ts-expect-error FIXME: ts error
checked: model.checked,
},
host.doc.getParent(model),
index
);
},
},
{
name: 'Delete',
description: 'Remove this line permanently.',
searchAlias: ['remove'],
icon: DeleteIcon(),
tooltip: slashMenuToolTips['Delete'],
group: '8_Actions@4',
action: ({ std, model }) => {
std.host.doc.deleteBlock(model);
},
},
];
},
}; };

View File

@@ -1,7 +1,6 @@
import type { SlashMenuTooltip } from '../types'; import type { SlashMenuTooltip } from '../types';
import { CopyTooltip } from './copy'; import { CopyTooltip } from './copy';
import { DeleteTooltip } from './delete'; import { DeleteTooltip } from './delete';
import { EdgelessTooltip } from './edgeless';
import { LinkDocTooltip } from './link-doc'; import { LinkDocTooltip } from './link-doc';
import { MoveDownTooltip } from './move-down'; import { MoveDownTooltip } from './move-down';
import { MoveUpTooltip } from './move-up'; import { MoveUpTooltip } from './move-up';
@@ -61,9 +60,4 @@ export const slashMenuToolTips: Record<string, SlashMenuTooltip> = {
figure: DeleteTooltip, figure: DeleteTooltip,
caption: 'Delete', caption: 'Delete',
}, },
'Group & Frame': {
figure: EdgelessTooltip,
caption: 'Edgeless',
},
}; };

View File

@@ -7,8 +7,6 @@
}, },
"include": ["./src"], "include": ["./src"],
"references": [ "references": [
{ "path": "../block-surface" },
{ "path": "../block-surface-ref" },
{ "path": "../components" }, { "path": "../components" },
{ "path": "../rich-text" }, { "path": "../rich-text" },
{ "path": "../shared" }, { "path": "../shared" },

View File

@@ -301,6 +301,7 @@ export const PackageList = [
'blocksuite/affine/components', 'blocksuite/affine/components',
'blocksuite/affine/model', 'blocksuite/affine/model',
'blocksuite/affine/shared', 'blocksuite/affine/shared',
'blocksuite/affine/widget-slash-menu',
'blocksuite/framework/block-std', 'blocksuite/framework/block-std',
'blocksuite/framework/global', 'blocksuite/framework/global',
'blocksuite/framework/inline', 'blocksuite/framework/inline',
@@ -495,8 +496,6 @@ export const PackageList = [
location: 'blocksuite/affine/widget-slash-menu', location: 'blocksuite/affine/widget-slash-menu',
name: '@blocksuite/affine-widget-slash-menu', name: '@blocksuite/affine-widget-slash-menu',
workspaceDependencies: [ workspaceDependencies: [
'blocksuite/affine/block-surface',
'blocksuite/affine/block-surface-ref',
'blocksuite/affine/components', 'blocksuite/affine/components',
'blocksuite/affine/rich-text', 'blocksuite/affine/rich-text',
'blocksuite/affine/shared', 'blocksuite/affine/shared',

View File

@@ -2665,9 +2665,10 @@ __metadata:
"@blocksuite/affine-components": "workspace:*" "@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-model": "workspace:*" "@blocksuite/affine-model": "workspace:*"
"@blocksuite/affine-shared": "workspace:*" "@blocksuite/affine-shared": "workspace:*"
"@blocksuite/affine-widget-slash-menu": "workspace:*"
"@blocksuite/block-std": "workspace:*" "@blocksuite/block-std": "workspace:*"
"@blocksuite/global": "workspace:*" "@blocksuite/global": "workspace:*"
"@blocksuite/icons": "npm:^2.2.3" "@blocksuite/icons": "npm:^2.2.4"
"@blocksuite/inline": "workspace:*" "@blocksuite/inline": "workspace:*"
"@blocksuite/store": "workspace:*" "@blocksuite/store": "workspace:*"
"@lit/context": "npm:^1.1.2" "@lit/context": "npm:^1.1.2"
@@ -3040,8 +3041,6 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@blocksuite/affine-widget-slash-menu@workspace:blocksuite/affine/widget-slash-menu" resolution: "@blocksuite/affine-widget-slash-menu@workspace:blocksuite/affine/widget-slash-menu"
dependencies: dependencies:
"@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-block-surface-ref": "workspace:*"
"@blocksuite/affine-components": "workspace:*" "@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-rich-text": "workspace:*" "@blocksuite/affine-rich-text": "workspace:*"
"@blocksuite/affine-shared": "workspace:*" "@blocksuite/affine-shared": "workspace:*"