refactor(editor): adjust folder structure for slash menu extension (#10588)

Close [BS-2743](https://linear.app/affine-design/issue/BS-2743/slash-menu插件化:调整文件夹结构)

This PR move slash menu from `affine-root-block` to `widget-affine-slash-menu`, and make it as a `WidgetViewExtension`
This commit is contained in:
L-Sun
2025-03-06 16:12:06 +00:00
parent 289d3cd20e
commit 62d8c0c7cb
73 changed files with 397 additions and 230 deletions

View File

@@ -36,6 +36,7 @@
"@blocksuite/affine-widget-frame-title": "workspace:*", "@blocksuite/affine-widget-frame-title": "workspace:*",
"@blocksuite/affine-widget-remote-selection": "workspace:*", "@blocksuite/affine-widget-remote-selection": "workspace:*",
"@blocksuite/affine-widget-scroll-anchoring": "workspace:*", "@blocksuite/affine-widget-scroll-anchoring": "workspace:*",
"@blocksuite/affine-widget-slash-menu": "workspace:*",
"@blocksuite/affine-widget-toolbar": "workspace:*", "@blocksuite/affine-widget-toolbar": "workspace:*",
"@blocksuite/block-std": "workspace:*", "@blocksuite/block-std": "workspace:*",
"@blocksuite/data-view": "workspace:*", "@blocksuite/data-view": "workspace:*",

View File

@@ -12,6 +12,7 @@ import {
import { dragHandleWidget } from '@blocksuite/affine-widget-drag-handle'; import { dragHandleWidget } from '@blocksuite/affine-widget-drag-handle';
import { docRemoteSelectionWidget } from '@blocksuite/affine-widget-remote-selection'; import { docRemoteSelectionWidget } from '@blocksuite/affine-widget-remote-selection';
import { scrollAnchoringWidget } from '@blocksuite/affine-widget-scroll-anchoring'; import { scrollAnchoringWidget } from '@blocksuite/affine-widget-scroll-anchoring';
import { SlashMenuExtension } from '@blocksuite/affine-widget-slash-menu';
import { toolbarWidget } from '@blocksuite/affine-widget-toolbar'; import { toolbarWidget } from '@blocksuite/affine-widget-toolbar';
import { import {
BlockFlavourIdentifier, BlockFlavourIdentifier,
@@ -25,7 +26,6 @@ import {
innerModalWidget, innerModalWidget,
linkedDocWidget, linkedDocWidget,
modalWidget, modalWidget,
slashMenuWidget,
viewportOverlayWidget, viewportOverlayWidget,
} from './widgets'; } from './widgets';
@@ -42,7 +42,7 @@ export const CommonSpecs: ExtensionType[] = [
modalWidget, modalWidget,
innerModalWidget, innerModalWidget,
slashMenuWidget, SlashMenuExtension,
linkedDocWidget, linkedDocWidget,
dragHandleWidget, dragHandleWidget,
docRemoteSelectionWidget, docRemoteSelectionWidget,

View File

@@ -4,7 +4,6 @@ import { literal, unsafeStatic } from 'lit/static-html.js';
import { AFFINE_INNER_MODAL_WIDGET } from '../widgets/inner-modal/inner-modal.js'; import { AFFINE_INNER_MODAL_WIDGET } from '../widgets/inner-modal/inner-modal.js';
import { AFFINE_LINKED_DOC_WIDGET } from '../widgets/linked-doc/config.js'; import { AFFINE_LINKED_DOC_WIDGET } from '../widgets/linked-doc/config.js';
import { AFFINE_MODAL_WIDGET } from '../widgets/modal/modal.js'; import { AFFINE_MODAL_WIDGET } from '../widgets/modal/modal.js';
import { AFFINE_SLASH_MENU_WIDGET } from '../widgets/slash-menu/index.js';
import { AFFINE_VIEWPORT_OVERLAY_WIDGET } from '../widgets/viewport-overlay/viewport-overlay.js'; import { AFFINE_VIEWPORT_OVERLAY_WIDGET } from '../widgets/viewport-overlay/viewport-overlay.js';
export const modalWidget = WidgetViewExtension( export const modalWidget = WidgetViewExtension(
@@ -17,11 +16,6 @@ export const innerModalWidget = WidgetViewExtension(
AFFINE_INNER_MODAL_WIDGET, AFFINE_INNER_MODAL_WIDGET,
literal`${unsafeStatic(AFFINE_INNER_MODAL_WIDGET)}` literal`${unsafeStatic(AFFINE_INNER_MODAL_WIDGET)}`
); );
export const slashMenuWidget = WidgetViewExtension(
'affine:page',
AFFINE_SLASH_MENU_WIDGET,
literal`${unsafeStatic(AFFINE_SLASH_MENU_WIDGET)}`
);
export const linkedDocWidget = WidgetViewExtension( export const linkedDocWidget = WidgetViewExtension(
'affine:page', 'affine:page',
AFFINE_LINKED_DOC_WIDGET, AFFINE_LINKED_DOC_WIDGET,

View File

@@ -106,14 +106,6 @@ import {
AFFINE_PAGE_DRAGGING_AREA_WIDGET, AFFINE_PAGE_DRAGGING_AREA_WIDGET,
AffinePageDraggingAreaWidget, AffinePageDraggingAreaWidget,
} from './widgets/page-dragging-area/page-dragging-area.js'; } from './widgets/page-dragging-area/page-dragging-area.js';
import {
AFFINE_SLASH_MENU_WIDGET,
AffineSlashMenuWidget,
} from './widgets/slash-menu/index.js';
import {
InnerSlashMenu,
SlashMenu,
} from './widgets/slash-menu/slash-menu-popover.js';
import { import {
AFFINE_SURFACE_REF_TOOLBAR, AFFINE_SURFACE_REF_TOOLBAR,
AffineSurfaceRefToolbar, AffineSurfaceRefToolbar,
@@ -156,7 +148,6 @@ function registerWidgets() {
AffinePageDraggingAreaWidget AffinePageDraggingAreaWidget
); );
customElements.define(AFFINE_IMAGE_TOOLBAR_WIDGET, AffineImageToolbarWidget); customElements.define(AFFINE_IMAGE_TOOLBAR_WIDGET, AffineImageToolbarWidget);
customElements.define(AFFINE_SLASH_MENU_WIDGET, AffineSlashMenuWidget);
customElements.define( customElements.define(
AFFINE_VIEWPORT_OVERLAY_WIDGET, AFFINE_VIEWPORT_OVERLAY_WIDGET,
AffineViewportOverlayWidget AffineViewportOverlayWidget
@@ -274,8 +265,6 @@ function registerEdgelessEditorComponents() {
function registerMiscComponents() { function registerMiscComponents() {
// Modal and menu components // Modal and menu components
customElements.define('affine-custom-modal', AffineCustomModal); customElements.define('affine-custom-modal', AffineCustomModal);
customElements.define('affine-slash-menu', SlashMenu);
customElements.define('inner-slash-menu', InnerSlashMenu);
// Loading and preview components // Loading and preview components
customElements.define('loader-element', Loader); customElements.define('loader-element', Loader);

View File

@@ -4,6 +4,7 @@ import type {
AFFINE_DOC_REMOTE_SELECTION_WIDGET, AFFINE_DOC_REMOTE_SELECTION_WIDGET,
AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET, AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET,
} from '@blocksuite/affine-widget-remote-selection'; } from '@blocksuite/affine-widget-remote-selection';
import type { AFFINE_SLASH_MENU_WIDGET } from '@blocksuite/affine-widget-slash-menu';
import type { EdgelessRootBlockComponent } from './edgeless/edgeless-root-block.js'; import type { EdgelessRootBlockComponent } from './edgeless/edgeless-root-block.js';
import type { PageRootBlockComponent } from './page/page-root-block.js'; import type { PageRootBlockComponent } from './page/page-root-block.js';
@@ -14,7 +15,6 @@ import type { AFFINE_INNER_MODAL_WIDGET } from './widgets/inner-modal/inner-moda
import type { AFFINE_LINKED_DOC_WIDGET } from './widgets/linked-doc/config.js'; import type { AFFINE_LINKED_DOC_WIDGET } from './widgets/linked-doc/config.js';
import type { AFFINE_MODAL_WIDGET } from './widgets/modal/modal.js'; import type { AFFINE_MODAL_WIDGET } from './widgets/modal/modal.js';
import type { AFFINE_PAGE_DRAGGING_AREA_WIDGET } from './widgets/page-dragging-area/page-dragging-area.js'; import type { AFFINE_PAGE_DRAGGING_AREA_WIDGET } from './widgets/page-dragging-area/page-dragging-area.js';
import type { AFFINE_SLASH_MENU_WIDGET } from './widgets/slash-menu/index.js';
import type { AFFINE_VIEWPORT_OVERLAY_WIDGET } from './widgets/viewport-overlay/viewport-overlay.js'; import type { AFFINE_VIEWPORT_OVERLAY_WIDGET } from './widgets/viewport-overlay/viewport-overlay.js';
export type PageRootBlockWidgetName = export type PageRootBlockWidgetName =

View File

@@ -1,3 +1,4 @@
// TODO(@L-Sun): remove these utils
export function formatDate(date: Date) { export function formatDate(date: Date) {
// yyyy-mm-dd // yyyy-mm-dd
const year = date.getFullYear(); const year = date.getFullYear();

View File

@@ -21,15 +21,6 @@ export {
export { AffineLinkedDocWidget } from './linked-doc/index.js'; export { AffineLinkedDocWidget } from './linked-doc/index.js';
export { AffineModalWidget } from './modal/modal.js'; export { AffineModalWidget } from './modal/modal.js';
export { AffinePageDraggingAreaWidget } from './page-dragging-area/page-dragging-area.js'; export { AffinePageDraggingAreaWidget } from './page-dragging-area/page-dragging-area.js';
export {
type AffineSlashMenuActionItem,
type AffineSlashMenuContext,
type AffineSlashMenuGroupDivider,
type AffineSlashMenuItem,
type AffineSlashMenuItemGenerator,
AffineSlashMenuWidget,
type AffineSlashSubMenu,
} from './slash-menu/index.js';
export { AffineSurfaceRefToolbar } from './surface-ref-toolbar/surface-ref-toolbar.js'; export { AffineSurfaceRefToolbar } from './surface-ref-toolbar/surface-ref-toolbar.js';
export * from './viewport-overlay/viewport-overlay.js'; export * from './viewport-overlay/viewport-overlay.js';
export { AffineFrameTitleWidget } from '@blocksuite/affine-widget-frame-title'; export { AffineFrameTitleWidget } from '@blocksuite/affine-widget-frame-title';

View File

@@ -8,6 +8,7 @@ import { unsafeCSSVar } from '@blocksuite/affine-shared/theme';
import { import {
createKeydownObserver, createKeydownObserver,
getCurrentNativeRange, getCurrentNativeRange,
getPopperPosition,
getViewportElement, getViewportElement,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { PropTypes, requiredProperties } from '@blocksuite/block-std'; import { PropTypes, requiredProperties } from '@blocksuite/block-std';
@@ -20,7 +21,6 @@ import { property, query, queryAll, state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import throttle from 'lodash-es/throttle'; import throttle from 'lodash-es/throttle';
import { getPopperPosition } from '../../utils/position.js';
import type { LinkedDocContext, LinkedMenuGroup } from './config.js'; import type { LinkedDocContext, LinkedMenuGroup } from './config.js';
import { linkedDocPopoverStyles } from './styles.js'; import { linkedDocPopoverStyles } from './styles.js';
import { resolveSignal } from './utils.js'; import { resolveSignal } from './utils.js';

View File

@@ -30,6 +30,7 @@
{ "path": "../widget-frame-title" }, { "path": "../widget-frame-title" },
{ "path": "../widget-remote-selection" }, { "path": "../widget-remote-selection" },
{ "path": "../widget-scroll-anchoring" }, { "path": "../widget-scroll-anchoring" },
{ "path": "../widget-slash-menu" },
{ "path": "../widget-toolbar" }, { "path": "../widget-toolbar" },
{ "path": "../../framework/block-std" }, { "path": "../../framework/block-std" },
{ "path": "../data-view" }, { "path": "../data-view" },

View File

@@ -14,6 +14,7 @@ export * from './insert';
export * from './is-abort-error'; export * from './is-abort-error';
export * from './math'; export * from './math';
export * from './model'; export * from './model';
export * from './popper-position';
export * from './print-to-pdf'; export * from './print-to-pdf';
export * from './reference'; export * from './reference';
export * from './reordering'; export * from './reordering';

View File

@@ -18,7 +18,7 @@ type CollisionBox = {
edgeGap?: number; edgeGap?: number;
}; };
export function calcSafeCoordinate({ function calcSafeCoordinate({
positioningPoint, positioningPoint,
objRect = { width: 0, height: 0 }, objRect = { width: 0, height: 0 },
boundaryRect = document.body.getBoundingClientRect(), boundaryRect = document.body.getBoundingClientRect(),
@@ -50,7 +50,7 @@ export function calcSafeCoordinate({
* *
* Please give preference to {@link getPopperPosition} * Please give preference to {@link getPopperPosition}
*/ */
export function compareTopAndBottomSpace( function compareTopAndBottomSpace(
obj: { getBoundingClientRect: () => DOMRect }, obj: { getBoundingClientRect: () => DOMRect },
container = document.body, container = document.body,
gap = 20 gap = 20

View File

@@ -0,0 +1 @@
# widget-slash-menu

View File

@@ -0,0 +1,51 @@
{
"name": "@blocksuite/affine-widget-slash-menu",
"description": "Affine slash menu widget.",
"type": "module",
"scripts": {
"build": "tsc",
"test:unit": "nx vite:test --run --passWithNoTests",
"test:unit:coverage": "nx vite:test --run --coverage",
"test:e2e": "playwright test"
},
"sideEffects": false,
"keywords": [],
"author": "toeverything",
"license": "MIT",
"dependencies": {
"@blocksuite/affine-block-attachment": "workspace:*",
"@blocksuite/affine-block-database": "workspace:*",
"@blocksuite/affine-block-embed": "workspace:*",
"@blocksuite/affine-block-image": "workspace:*",
"@blocksuite/affine-block-latex": "workspace:*",
"@blocksuite/affine-block-note": "workspace:*",
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-block-surface-ref": "workspace:*",
"@blocksuite/affine-block-table": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-shared": "workspace:*",
"@blocksuite/block-std": "workspace:*",
"@blocksuite/data-view": "workspace:*",
"@blocksuite/global": "workspace:*",
"@blocksuite/icons": "^2.2.4",
"@blocksuite/inline": "workspace:*",
"@blocksuite/store": "workspace:*",
"@floating-ui/dom": "^1.6.13",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.12",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.21"
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts"
},
"files": [
"src",
"dist",
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.20.0"
}

View File

@@ -57,8 +57,8 @@ import {
createDefaultDoc, createDefaultDoc,
openFileOrFiles, openFileOrFiles,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std';
import { viewPresets } from '@blocksuite/data-view/view-presets'; import { viewPresets } from '@blocksuite/data-view/view-presets';
import { assertType } from '@blocksuite/global/utils';
import { import {
DualLinkIcon, DualLinkIcon,
ExportToPdfIcon, ExportToPdfIcon,
@@ -74,16 +74,15 @@ import type { BlockModel } from '@blocksuite/store';
import { Slice, Text } from '@blocksuite/store'; import { Slice, Text } from '@blocksuite/store';
import type { TemplateResult } from 'lit'; import type { TemplateResult } from 'lit';
import type { RootBlockComponent } from '../../types.js'; import { type SlashMenuTooltip, slashMenuToolTips } from './tooltips';
import { formatDate, formatTime } from '../../utils/misc.js';
import type { AffineLinkedDocWidget } from '../linked-doc/index.js';
import { type SlashMenuTooltip, slashMenuToolTips } from './tooltips/index.js';
import { import {
createConversionItem, createConversionItem,
createTextFormatItem, createTextFormatItem,
formatDate,
formatTime,
insideEdgelessText, insideEdgelessText,
tryRemoveEmptyLine, tryRemoveEmptyLine,
} from './utils.js'; } from './utils';
export type SlashMenuConfig = { export type SlashMenuConfig = {
triggerKeys: string[]; triggerKeys: string[];
@@ -136,7 +135,7 @@ export type SlashMenuItemGenerator = (
) => (SlashMenuGroupDivider | SlashMenuActionItem | SlashSubMenu)[]; ) => (SlashMenuGroupDivider | SlashMenuActionItem | SlashSubMenu)[];
export type SlashMenuContext = { export type SlashMenuContext = {
rootComponent: RootBlockComponent; std: BlockStdScope;
model: BlockModel; model: BlockModel;
}; };
@@ -183,7 +182,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
showWhen: ({ model }) => { showWhen: ({ model }) => {
return model.doc.get(FeatureFlagService).getFlag('enable_callout'); return model.doc.get(FeatureFlagService).getFlag('enable_callout');
}, },
action: ({ model, rootComponent }) => { action: ({ model, std }) => {
const { doc } = model; const { doc } = model;
const parent = doc.getParent(model); const parent = doc.getParent(model);
if (!parent) return; if (!parent) return;
@@ -194,11 +193,11 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
if (!calloutId) return; if (!calloutId) return;
const paragraphId = doc.addBlock('affine:paragraph', {}, calloutId); const paragraphId = doc.addBlock('affine:paragraph', {}, calloutId);
if (!paragraphId) return; if (!paragraphId) return;
rootComponent.updateComplete std.host.updateComplete
.then(() => { .then(() => {
const paragraph = rootComponent.std.view.getBlock(paragraphId); const paragraph = std.view.getBlock(paragraphId);
if (!paragraph) return; if (!paragraph) return;
rootComponent.std.command.exec(focusBlockEnd, { std.command.exec(focusBlockEnd, {
focusBlock: paragraph, focusBlock: paragraph,
}); });
}) })
@@ -211,8 +210,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: 'Create a equation block.', description: 'Create a equation block.',
icon: TeXIcon(), icon: TeXIcon(),
alias: ['inlineMath, inlineEquation', 'inlineLatex'], alias: ['inlineMath, inlineEquation', 'inlineLatex'],
action: ({ rootComponent }) => { action: ({ std }) => {
rootComponent.std.command std.command
.chain() .chain()
.pipe(getTextSelectionCommand) .pipe(getTextSelectionCommand)
.pipe(insertInlineLatex) .pipe(insertInlineLatex)
@@ -245,9 +244,9 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
tooltip: slashMenuToolTips['New Doc'], tooltip: slashMenuToolTips['New Doc'],
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'), model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'),
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
const newDoc = createDefaultDoc(rootComponent.doc.workspace); const newDoc = createDefaultDoc(std.host.doc.workspace);
insertContent(rootComponent.host, model, REFERENCE_NODE, { insertContent(std.host, model, REFERENCE_NODE, {
reference: { reference: {
type: 'LinkedPage', type: 'LinkedPage',
pageId: newDoc.id, pageId: newDoc.id,
@@ -261,33 +260,36 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: LinkedDocIcon, icon: LinkedDocIcon,
tooltip: slashMenuToolTips['Linked Doc'], tooltip: slashMenuToolTips['Linked Doc'],
alias: ['dual link'], alias: ['dual link'],
showWhen: ({ rootComponent, model }) => { showWhen: ({ std, model }) => {
const { std } = rootComponent; const root = model.doc.root;
if (!root) return false;
const linkedDocWidget = std.view.getWidget( const linkedDocWidget = std.view.getWidget(
'affine-linked-doc-widget', 'affine-linked-doc-widget',
rootComponent.model.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, rootComponent }) => { action: ({ model, std }) => {
const { std } = rootComponent; const root = model.doc.root;
if (!root) return;
const linkedDocWidget = std.view.getWidget( const linkedDocWidget = std.view.getWidget(
'affine-linked-doc-widget', 'affine-linked-doc-widget',
rootComponent.model.id root.id
); );
if (!linkedDocWidget) return; if (!linkedDocWidget) return;
assertType<AffineLinkedDocWidget>(linkedDocWidget); // TODO(@L-Sun): make linked-doc-widget as extension
// @ts-expect-error same as above
const triggerKey = linkedDocWidget.config.triggerKeys[0]; const triggerKey = linkedDocWidget.config.triggerKeys[0];
insertContent(rootComponent.host, model, triggerKey); insertContent(std.host, model, triggerKey);
const inlineEditor = getInlineEditorByModel(rootComponent.host, model); const inlineEditor = getInlineEditorByModel(std.host, model);
// Wait for range to be updated // Wait for range to be updated
inlineEditor?.slots.inlineRangeSync.once(() => { inlineEditor?.slots.inlineRangeSync.once(() => {
// TODO(@L-Sun): make linked-doc-widget as extension
// @ts-expect-error same as above
linkedDocWidget.show({ addTriggerKey: true }); linkedDocWidget.show({ addTriggerKey: true });
}); });
}, },
@@ -301,8 +303,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: TableIcon(), icon: TableIcon(),
tooltip: slashMenuToolTips['Table View'], tooltip: slashMenuToolTips['Table View'],
showWhen: ({ model }) => !insideEdgelessText(model), showWhen: ({ model }) => !insideEdgelessText(model),
action: ({ rootComponent }) => { action: ({ std }) => {
rootComponent.std.command std.command
.chain() .chain()
.pipe(getSelectedModelsCommand) .pipe(getSelectedModelsCommand)
.pipe(insertTableBlockCommand, { .pipe(insertTableBlockCommand, {
@@ -311,8 +313,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
}) })
.pipe(({ insertedTableBlockId }) => { .pipe(({ insertedTableBlockId }) => {
if (insertedTableBlockId) { if (insertedTableBlockId) {
const telemetry = const telemetry = std.getOptional(TelemetryProvider);
rootComponent.std.getOptional(TelemetryProvider);
telemetry?.track('BlockCreated', { telemetry?.track('BlockCreated', {
blockType: 'affine:table', blockType: 'affine:table',
}); });
@@ -328,8 +329,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
tooltip: slashMenuToolTips['Image'], tooltip: slashMenuToolTips['Image'],
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:image'), model.doc.schema.flavourSchemaMap.has('affine:image'),
action: async ({ rootComponent }) => { action: async ({ std }) => {
const [success, ctx] = rootComponent.std.command const [success, ctx] = std.command
.chain() .chain()
.pipe(getSelectedModelsCommand) .pipe(getSelectedModelsCommand)
.pipe(insertImagesCommand, { removeEmptyLine: true }) .pipe(insertImagesCommand, { removeEmptyLine: true })
@@ -345,14 +346,15 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
tooltip: slashMenuToolTips['Link'], tooltip: slashMenuToolTips['Link'],
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:bookmark'), model.doc.schema.flavourSchemaMap.has('affine:bookmark'),
action: async ({ rootComponent, model }) => { action: async ({ std, model }) => {
const parentModel = rootComponent.doc.getParent(model); const { host } = std;
const parentModel = host.doc.getParent(model);
if (!parentModel) { if (!parentModel) {
return; return;
} }
const index = parentModel.children.indexOf(model) + 1; const index = parentModel.children.indexOf(model) + 1;
await toggleEmbedCardCreateModal( await toggleEmbedCardCreateModal(
rootComponent.host, host,
'Links', 'Links',
'The added link will be displayed as a card view.', 'The added link will be displayed as a card view.',
{ mode: 'page', parentModel, index } { mode: 'page', parentModel, index }
@@ -368,19 +370,13 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
alias: ['file'], alias: ['file'],
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:attachment'), model.doc.schema.flavourSchemaMap.has('affine:attachment'),
action: async ({ rootComponent, model }) => { action: async ({ std, model }) => {
const file = await openFileOrFiles(); const file = await openFileOrFiles();
if (!file) return; if (!file) return;
const maxFileSize = const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
rootComponent.std.store.get(FileSizeLimitService).maxFileSize;
await addSiblingAttachmentBlocks( await addSiblingAttachmentBlocks(std.host, [file], maxFileSize, model);
rootComponent.host,
[file],
maxFileSize,
model
);
tryRemoveEmptyLine(model); tryRemoveEmptyLine(model);
}, },
}, },
@@ -391,15 +387,14 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
tooltip: slashMenuToolTips['PDF'], tooltip: slashMenuToolTips['PDF'],
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:attachment'), model.doc.schema.flavourSchemaMap.has('affine:attachment'),
action: async ({ rootComponent, model }) => { action: async ({ std, model }) => {
const file = await openFileOrFiles(); const file = await openFileOrFiles();
if (!file) return; if (!file) return;
const maxFileSize = const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
rootComponent.std.store.get(FileSizeLimitService).maxFileSize;
await addSiblingAttachmentBlocks( await addSiblingAttachmentBlocks(
rootComponent.host, std.host,
[file], [file],
maxFileSize, maxFileSize,
model, model,
@@ -416,14 +411,15 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
tooltip: slashMenuToolTips['YouTube'], tooltip: slashMenuToolTips['YouTube'],
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:embed-youtube'), model.doc.schema.flavourSchemaMap.has('affine:embed-youtube'),
action: async ({ rootComponent, model }) => { action: async ({ std, model }) => {
const parentModel = rootComponent.doc.getParent(model); const { host } = std;
const parentModel = host.doc.getParent(model);
if (!parentModel) { if (!parentModel) {
return; return;
} }
const index = parentModel.children.indexOf(model) + 1; const index = parentModel.children.indexOf(model) + 1;
await toggleEmbedCardCreateModal( await toggleEmbedCardCreateModal(
rootComponent.host, host,
'YouTube', 'YouTube',
'The added YouTube video link will be displayed as an embed view.', 'The added YouTube video link will be displayed as an embed view.',
{ mode: 'page', parentModel, index } { mode: 'page', parentModel, index }
@@ -438,14 +434,15 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
tooltip: slashMenuToolTips['Github'], tooltip: slashMenuToolTips['Github'],
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:embed-github'), model.doc.schema.flavourSchemaMap.has('affine:embed-github'),
action: async ({ rootComponent, model }) => { action: async ({ std, model }) => {
const parentModel = rootComponent.doc.getParent(model); const { host } = std;
const parentModel = host.doc.getParent(model);
if (!parentModel) { if (!parentModel) {
return; return;
} }
const index = parentModel.children.indexOf(model) + 1; const index = parentModel.children.indexOf(model) + 1;
await toggleEmbedCardCreateModal( await toggleEmbedCardCreateModal(
rootComponent.host, host,
'GitHub', 'GitHub',
'The added GitHub issue or pull request link will be displayed as a card view.', 'The added GitHub issue or pull request link will be displayed as a card view.',
{ mode: 'page', parentModel, index } { mode: 'page', parentModel, index }
@@ -462,14 +459,15 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
tooltip: slashMenuToolTips['Figma'], tooltip: slashMenuToolTips['Figma'],
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:embed-figma'), model.doc.schema.flavourSchemaMap.has('affine:embed-figma'),
action: async ({ rootComponent, model }) => { action: async ({ std, model }) => {
const parentModel = rootComponent.doc.getParent(model); const { host } = std;
const parentModel = host.doc.getParent(model);
if (!parentModel) { if (!parentModel) {
return; return;
} }
const index = parentModel.children.indexOf(model) + 1; const index = parentModel.children.indexOf(model) + 1;
await toggleEmbedCardCreateModal( await toggleEmbedCardCreateModal(
rootComponent.host, host,
'Figma', 'Figma',
'The added Figma link will be displayed as an embed view.', 'The added Figma link will be displayed as an embed view.',
{ mode: 'page', parentModel, index } { mode: 'page', parentModel, index }
@@ -483,14 +481,15 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: LoomIcon, icon: LoomIcon,
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:embed-loom'), model.doc.schema.flavourSchemaMap.has('affine:embed-loom'),
action: async ({ rootComponent, model }) => { action: async ({ std, model }) => {
const parentModel = rootComponent.doc.getParent(model); const { host } = std;
const parentModel = host.doc.getParent(model);
if (!parentModel) { if (!parentModel) {
return; return;
} }
const index = parentModel.children.indexOf(model) + 1; const index = parentModel.children.indexOf(model) + 1;
await toggleEmbedCardCreateModal( await toggleEmbedCardCreateModal(
rootComponent.host, host,
'Loom', 'Loom',
'The added Loom video link will be displayed as an embed view.', 'The added Loom video link will be displayed as an embed view.',
{ mode: 'page', parentModel, index } { mode: 'page', parentModel, index }
@@ -504,8 +503,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: 'Create a equation block.', description: 'Create a equation block.',
icon: TeXIcon(), icon: TeXIcon(),
alias: ['mathBlock, equationBlock', 'latexBlock'], alias: ['mathBlock, equationBlock', 'latexBlock'],
action: ({ rootComponent }) => { action: ({ std }) => {
rootComponent.std.command std.command
.chain() .chain()
.pipe(getSelectedModelsCommand) .pipe(getSelectedModelsCommand)
.pipe(insertLatexBlockCommand, { .pipe(insertLatexBlockCommand, {
@@ -519,24 +518,24 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
// TODO(@L-Sun): Linear // TODO(@L-Sun): Linear
// --------------------------------------------------------- // ---------------------------------------------------------
({ model, rootComponent }) => { ({ model, std }) => {
const { doc } = rootComponent; const { host } = std;
const surfaceModel = getSurfaceBlock(doc); const surfaceModel = getSurfaceBlock(host.doc);
if (!surfaceModel) return []; if (!surfaceModel) return [];
const parent = doc.getParent(model); const parent = host.doc.getParent(model);
if (!parent) return []; if (!parent) return [];
const frameModels = doc const frameModels = host.doc
.getBlocksByFlavour('affine:frame') .getBlocksByFlavour('affine:frame')
.map(block => block.model as FrameBlockModel); .map(block => block.model as FrameBlockModel);
const frameItems = frameModels.map<SlashMenuActionItem>(frameModel => ({ const frameItems = frameModels.map<SlashMenuActionItem>(frameModel => ({
name: 'Frame: ' + frameModel.title, name: 'Frame: ' + frameModel.title,
icon: FrameIcon(), icon: FrameIcon(),
action: ({ rootComponent }) => { action: ({ std }) => {
rootComponent.std.command std.command
.chain() .chain()
.pipe(getSelectedModelsCommand) .pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, { .pipe(insertSurfaceRefBlockCommand, {
@@ -553,7 +552,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
name: 'Group: ' + group.title.toString(), name: 'Group: ' + group.title.toString(),
icon: GroupingIcon(), icon: GroupingIcon(),
action: () => { action: () => {
rootComponent.std.command std.command
.chain() .chain()
.pipe(getSelectedModelsCommand) .pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, { .pipe(insertSurfaceRefBlockCommand, {
@@ -594,8 +593,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: TodayIcon, icon: TodayIcon,
tooltip: slashMenuToolTips['Today'], tooltip: slashMenuToolTips['Today'],
description: formatDate(now), description: formatDate(now),
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
insertContent(rootComponent.host, model, formatDate(now)); insertContent(std.host, model, formatDate(now));
}, },
}, },
{ {
@@ -603,10 +602,10 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: TomorrowIcon, icon: TomorrowIcon,
tooltip: slashMenuToolTips['Tomorrow'], tooltip: slashMenuToolTips['Tomorrow'],
description: formatDate(tomorrow), description: formatDate(tomorrow),
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
const tomorrow = new Date(); const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setDate(tomorrow.getDate() + 1);
insertContent(rootComponent.host, model, formatDate(tomorrow)); insertContent(std.host, model, formatDate(tomorrow));
}, },
}, },
{ {
@@ -614,10 +613,10 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: YesterdayIcon, icon: YesterdayIcon,
tooltip: slashMenuToolTips['Yesterday'], tooltip: slashMenuToolTips['Yesterday'],
description: formatDate(yesterday), description: formatDate(yesterday),
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
const yesterday = new Date(); const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1); yesterday.setDate(yesterday.getDate() - 1);
insertContent(rootComponent.host, model, formatDate(yesterday)); insertContent(std.host, model, formatDate(yesterday));
}, },
}, },
{ {
@@ -625,8 +624,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: NowIcon, icon: NowIcon,
tooltip: slashMenuToolTips['Now'], tooltip: slashMenuToolTips['Now'],
description: formatTime(now), description: formatTime(now),
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
insertContent(rootComponent.host, model, formatTime(now)); insertContent(std.host, model, formatTime(now));
}, },
}, },
]; ];
@@ -643,8 +642,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:database') && model.doc.schema.flavourSchemaMap.has('affine:database') &&
!insideEdgelessText(model), !insideEdgelessText(model),
action: ({ rootComponent }) => { action: ({ std }) => {
rootComponent.std.command std.command
.chain() .chain()
.pipe(getSelectedModelsCommand) .pipe(getSelectedModelsCommand)
.pipe(insertDatabaseBlockCommand, { .pipe(insertDatabaseBlockCommand, {
@@ -654,8 +653,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
}) })
.pipe(({ insertedDatabaseBlockId }) => { .pipe(({ insertedDatabaseBlockId }) => {
if (insertedDatabaseBlockId) { if (insertedDatabaseBlockId) {
const telemetry = const telemetry = std.getOptional(TelemetryProvider);
rootComponent.std.getOptional(TelemetryProvider);
telemetry?.track('BlockCreated', { telemetry?.track('BlockCreated', {
blockType: 'affine:database', blockType: 'affine:database',
}); });
@@ -674,21 +672,22 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
!insideEdgelessText(model) && !insideEdgelessText(model) &&
!!model.doc.get(FeatureFlagService).getFlag('enable_block_query'), !!model.doc.get(FeatureFlagService).getFlag('enable_block_query'),
action: ({ model, rootComponent }) => { action: ({ model, std }) => {
const parent = rootComponent.doc.getParent(model); const { host } = std;
const parent = host.doc.getParent(model);
if (!parent) return; if (!parent) return;
const index = parent.children.indexOf(model); const index = parent.children.indexOf(model);
const id = rootComponent.doc.addBlock( const id = host.doc.addBlock(
'affine:data-view', 'affine:data-view',
{}, {},
rootComponent.doc.getParent(model), host.doc.getParent(model),
index + 1 index + 1
); );
const dataViewModel = rootComponent.doc.getBlock(id)!; const dataViewModel = host.doc.getBlock(id)!;
Promise.resolve() Promise.resolve()
.then(() => { .then(() => {
const dataView = rootComponent.std.view.getBlock( const dataView = std.view.getBlock(
dataViewModel.id dataViewModel.id
) as DataViewBlockComponent | null; ) as DataViewBlockComponent | null;
dataView?.dataSource.viewManager.viewAdd('table'); dataView?.dataSource.viewManager.viewAdd('table');
@@ -706,8 +705,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
showWhen: ({ model }) => showWhen: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:database') && model.doc.schema.flavourSchemaMap.has('affine:database') &&
!insideEdgelessText(model), !insideEdgelessText(model),
action: ({ rootComponent }) => { action: ({ std }) => {
rootComponent.std.command std.command
.chain() .chain()
.pipe(getSelectedModelsCommand) .pipe(getSelectedModelsCommand)
.pipe(insertDatabaseBlockCommand, { .pipe(insertDatabaseBlockCommand, {
@@ -717,8 +716,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
}) })
.pipe(({ insertedDatabaseBlockId }) => { .pipe(({ insertedDatabaseBlockId }) => {
if (insertedDatabaseBlockId) { if (insertedDatabaseBlockId) {
const telemetry = const telemetry = std.getOptional(TelemetryProvider);
rootComponent.std.getOptional(TelemetryProvider);
telemetry?.track('BlockCreated', { telemetry?.track('BlockCreated', {
blockType: 'affine:database', blockType: 'affine:database',
}); });
@@ -735,15 +733,15 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: 'Shift this line up.', description: 'Shift this line up.',
icon: ArrowUpBigIcon, icon: ArrowUpBigIcon,
tooltip: slashMenuToolTips['Move Up'], tooltip: slashMenuToolTips['Move Up'],
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
const doc = rootComponent.doc; const { host } = std;
const previousSiblingModel = doc.getPrev(model); const previousSiblingModel = host.doc.getPrev(model);
if (!previousSiblingModel) return; if (!previousSiblingModel) return;
const parentModel = doc.getParent(previousSiblingModel); const parentModel = host.doc.getParent(previousSiblingModel);
if (!parentModel) return; if (!parentModel) return;
doc.moveBlocks([model], parentModel, previousSiblingModel, true); host.doc.moveBlocks([model], parentModel, previousSiblingModel, true);
}, },
}, },
{ {
@@ -751,15 +749,15 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: 'Shift this line down.', description: 'Shift this line down.',
icon: ArrowDownBigIcon, icon: ArrowDownBigIcon,
tooltip: slashMenuToolTips['Move Down'], tooltip: slashMenuToolTips['Move Down'],
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
const doc = rootComponent.doc; const { host } = std;
const nextSiblingModel = doc.getNext(model); const nextSiblingModel = host.doc.getNext(model);
if (!nextSiblingModel) return; if (!nextSiblingModel) return;
const parentModel = doc.getParent(nextSiblingModel); const parentModel = host.doc.getParent(nextSiblingModel);
if (!parentModel) return; if (!parentModel) return;
doc.moveBlocks([model], parentModel, nextSiblingModel, false); host.doc.moveBlocks([model], parentModel, nextSiblingModel, false);
}, },
}, },
{ {
@@ -767,13 +765,13 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: 'Copy this line to clipboard.', description: 'Copy this line to clipboard.',
icon: CopyIcon, icon: CopyIcon,
tooltip: slashMenuToolTips['Copy'], tooltip: slashMenuToolTips['Copy'],
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
const slice = Slice.fromModels(rootComponent.std.store, [model]); const slice = Slice.fromModels(std.store, [model]);
rootComponent.std.clipboard std.clipboard
.copy(slice) .copy(slice)
.then(() => { .then(() => {
toast(rootComponent.host, 'Copied to clipboard'); toast(std.host, 'Copied to clipboard');
}) })
.catch(e => { .catch(e => {
console.error(e); console.error(e);
@@ -785,12 +783,13 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: 'Create a duplicate of this line.', description: 'Create a duplicate of this line.',
icon: DualLinkIcon(), icon: DualLinkIcon(),
tooltip: slashMenuToolTips['Copy'], tooltip: slashMenuToolTips['Copy'],
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
if (!model.text || !(model.text instanceof Text)) { if (!model.text || !(model.text instanceof Text)) {
console.error("Can't duplicate a block without text"); console.error("Can't duplicate a block without text");
return; return;
} }
const parent = rootComponent.doc.getParent(model); const { host } = std;
const parent = host.doc.getParent(model);
if (!parent) { if (!parent) {
console.error( console.error(
'Failed to duplicate block! Parent not found: ' + 'Failed to duplicate block! Parent not found: ' +
@@ -803,7 +802,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
const index = parent.children.indexOf(model); const index = parent.children.indexOf(model);
// TODO add clone model util // TODO add clone model util
rootComponent.doc.addBlock( host.doc.addBlock(
model.flavour as never, model.flavour as never,
{ {
type: (model as ParagraphBlockModel).type, type: (model as ParagraphBlockModel).type,
@@ -811,7 +810,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
// @ts-expect-error FIXME: ts error // @ts-expect-error FIXME: ts error
checked: model.checked, checked: model.checked,
}, },
rootComponent.doc.getParent(model), host.doc.getParent(model),
index index
); );
}, },
@@ -822,8 +821,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
alias: ['remove'], alias: ['remove'],
icon: DeleteIcon, icon: DeleteIcon,
tooltip: slashMenuToolTips['Delete'], tooltip: slashMenuToolTips['Delete'],
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
rootComponent.doc.deleteBlock(model); std.host.doc.deleteBlock(model);
}, },
}, },
], ],

View File

@@ -0,0 +1,14 @@
import { InnerSlashMenu, SlashMenu } from './slash-menu-popover';
import { AFFINE_SLASH_MENU_WIDGET, AffineSlashMenuWidget } from './widget';
export function effects() {
customElements.define(AFFINE_SLASH_MENU_WIDGET, AffineSlashMenuWidget);
customElements.define('affine-slash-menu', SlashMenu);
customElements.define('inner-slash-menu', InnerSlashMenu);
}
declare global {
interface HTMLElementTagNameMap {
[AFFINE_SLASH_MENU_WIDGET]: AffineSlashMenuWidget;
}
}

View File

@@ -0,0 +1,18 @@
import { WidgetViewExtension } from '@blocksuite/block-std';
import { type Container } from '@blocksuite/global/di';
import { Extension } from '@blocksuite/store';
import { literal, unsafeStatic } from 'lit/static-html.js';
import { AFFINE_SLASH_MENU_WIDGET } from './widget';
export class SlashMenuExtension extends Extension {
static override setup(di: Container) {
WidgetViewExtension(
'affine:page',
AFFINE_SLASH_MENU_WIDGET,
literal`${unsafeStatic(AFFINE_SLASH_MENU_WIDGET)}`
).setup(di);
di.add(this);
}
}

View File

@@ -0,0 +1,3 @@
// TODO(@L-Sun): narrow the scope of the exported symbols
export * from './extensions';
export * from './widget';

View File

@@ -9,6 +9,7 @@ import {
import { import {
createKeydownObserver, createKeydownObserver,
getCurrentNativeRange, getCurrentNativeRange,
getPopperPosition,
isControlledKeyboardEvent, isControlledKeyboardEvent,
isFuzzyMatch, isFuzzyMatch,
substringMatchScore, substringMatchScore,
@@ -21,7 +22,6 @@ import { ifDefined } from 'lit/directives/if-defined.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import throttle from 'lodash-es/throttle'; import throttle from 'lodash-es/throttle';
import { getPopperPosition } from '../../utils/position.js';
import type { import type {
SlashMenuActionItem, SlashMenuActionItem,
SlashMenuContext, SlashMenuContext,
@@ -147,7 +147,7 @@ export class SlashMenu extends WithDisposable(LitElement) {
} }
get host() { get host() {
return this.context.rootComponent.host; return this.context.std.host;
} }
constructor( constructor(
@@ -480,7 +480,7 @@ export class InnerSlashMenu extends WithDisposable(LitElement) {
}); });
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(
this.context.rootComponent.host, this.context.std.host,
this.context.model this.context.model
); );

View File

@@ -1,44 +1,44 @@
import type { TemplateResult } from 'lit'; import type { TemplateResult } from 'lit';
import { AttachmentTooltip } from './attachment.js'; import { AttachmentTooltip } from './attachment';
import { BoldTextTooltip } from './bold-text.js'; import { BoldTextTooltip } from './bold-text';
import { BulletedListTooltip } from './bulleted-list.js'; import { BulletedListTooltip } from './bulleted-list';
import { CodeBlockTooltip } from './code-block.js'; import { CodeBlockTooltip } from './code-block';
import { CopyTooltip } from './copy.js'; import { CopyTooltip } from './copy';
import { DeleteTooltip } from './delete.js'; import { DeleteTooltip } from './delete';
import { DividerTooltip } from './divider.js'; import { DividerTooltip } from './divider';
import { EdgelessTooltip } from './edgeless.js'; import { EdgelessTooltip } from './edgeless';
import { FigmaTooltip } from './figma.js'; import { FigmaTooltip } from './figma';
import { GithubRepoTooltip } from './github-repo.js'; import { GithubRepoTooltip } from './github-repo';
import { Heading1Tooltip } from './heading-1.js'; import { Heading1Tooltip } from './heading-1';
import { Heading2Tooltip } from './heading-2.js'; import { Heading2Tooltip } from './heading-2';
import { Heading3Tooltip } from './heading-3.js'; import { Heading3Tooltip } from './heading-3';
import { Heading4Tooltip } from './heading-4.js'; import { Heading4Tooltip } from './heading-4';
import { Heading5Tooltip } from './heading-5.js'; import { Heading5Tooltip } from './heading-5';
import { Heading6Tooltip } from './heading-6.js'; import { Heading6Tooltip } from './heading-6';
import { ItalicTooltip } from './italic.js'; import { ItalicTooltip } from './italic';
import { KanbanViewTooltip } from './kanban-view.js'; import { KanbanViewTooltip } from './kanban-view';
import { LinearTooltip } from './linear.js'; import { LinearTooltip } from './linear';
import { LinkTooltip } from './link.js'; import { LinkTooltip } from './link';
import { LinkDocTooltip } from './link-doc.js'; import { LinkDocTooltip } from './link-doc';
import { MoveDownTooltip } from './move-down.js'; import { MoveDownTooltip } from './move-down';
import { MoveUpTooltip } from './move-up.js'; import { MoveUpTooltip } from './move-up';
import { NewDocTooltip } from './new-doc.js'; import { NewDocTooltip } from './new-doc';
import { NowTooltip } from './now.js'; import { NowTooltip } from './now';
import { NumberedListTooltip } from './numbered-list.js'; import { NumberedListTooltip } from './numbered-list';
import { PDFTooltip } from './pdf.js'; import { PDFTooltip } from './pdf';
import { PhotoTooltip } from './photo.js'; import { PhotoTooltip } from './photo';
import { QuoteTooltip } from './quote.js'; import { QuoteTooltip } from './quote';
import { StrikethroughTooltip } from './strikethrough.js'; import { StrikethroughTooltip } from './strikethrough';
import { TableViewTooltip } from './table-view.js'; import { TableViewTooltip } from './table-view';
import { TextTooltip } from './text.js'; import { TextTooltip } from './text';
import { ToDoListTooltip } from './to-do-list.js'; import { ToDoListTooltip } from './to-do-list';
import { TodayTooltip } from './today.js'; import { TodayTooltip } from './today';
import { TomorrowTooltip } from './tomorrow.js'; import { TomorrowTooltip } from './tomorrow';
import { TweetTooltip } from './tweet.js'; import { TweetTooltip } from './tweet';
import { UnderlineTooltip } from './underline.js'; import { UnderlineTooltip } from './underline';
import { YesterdayTooltip } from './yesterday.js'; import { YesterdayTooltip } from './yesterday';
import { YoutubeVideoTooltip } from './youtube-video.js'; import { YoutubeVideoTooltip } from './youtube-video';
export type SlashMenuTooltip = { export type SlashMenuTooltip = {
figure: TemplateResult; figure: TemplateResult;

View File

@@ -103,8 +103,8 @@ export function createConversionItem(
icon, icon,
tooltip: slashMenuToolTips[name], tooltip: slashMenuToolTips[name],
showWhen: ({ model }) => model.doc.schema.flavourSchemaMap.has(flavour), showWhen: ({ model }) => model.doc.schema.flavourSchemaMap.has(flavour),
action: ({ rootComponent }) => { action: ({ std }) => {
rootComponent.std.command.exec(updateBlockType, { std.command.exec(updateBlockType, {
flavour, flavour,
props: { type }, props: { type },
}); });
@@ -120,8 +120,8 @@ export function createTextFormatItem(
name, name,
icon, icon,
tooltip: slashMenuToolTips[name], tooltip: slashMenuToolTips[name],
action: ({ rootComponent, model }) => { action: ({ std, model }) => {
const { std, host } = rootComponent; const { host } = std;
if (model.text?.length !== 0) { if (model.text?.length !== 0) {
std.command.exec(formatBlockCommand, { std.command.exec(formatBlockCommand, {
@@ -139,3 +139,22 @@ export function createTextFormatItem(
}, },
}; };
} }
export function formatDate(date: Date) {
// yyyy-mm-dd
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const strTime = `${year}-${month}-${day}`;
return strTime;
}
export function formatTime(date: Date) {
// mm-dd hh:mm
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
const strTime = `${month}-${day} ${hours}:${minutes}`;
return strTime;
}

View File

@@ -5,11 +5,9 @@ import {
import type { UIEventStateContext } from '@blocksuite/block-std'; import type { UIEventStateContext } from '@blocksuite/block-std';
import { TextSelection, WidgetComponent } from '@blocksuite/block-std'; import { TextSelection, WidgetComponent } from '@blocksuite/block-std';
import { DisposableGroup } from '@blocksuite/global/slot'; import { DisposableGroup } from '@blocksuite/global/slot';
import { assertType } from '@blocksuite/global/utils';
import { InlineEditor } from '@blocksuite/inline'; import { InlineEditor } from '@blocksuite/inline';
import debounce from 'lodash-es/debounce'; import debounce from 'lodash-es/debounce';
import type { RootBlockComponent } from '../../types.js';
import { import {
defaultSlashMenuConfig, defaultSlashMenuConfig,
type SlashMenuActionItem, type SlashMenuActionItem,
@@ -19,9 +17,9 @@ import {
type SlashMenuItemGenerator, type SlashMenuItemGenerator,
type SlashMenuStaticConfig, type SlashMenuStaticConfig,
type SlashSubMenu, type SlashSubMenu,
} from './config.js'; } from './config';
import { SlashMenu } from './slash-menu-popover.js'; import { SlashMenu } from './slash-menu-popover';
import { filterEnabledSlashMenuItems } from './utils.js'; import { filterEnabledSlashMenuItems } from './utils';
export type AffineSlashMenuContext = SlashMenuContext; export type AffineSlashMenuContext = SlashMenuContext;
export type AffineSlashMenuItem = SlashMenuItem; export type AffineSlashMenuItem = SlashMenuItem;
@@ -57,7 +55,7 @@ const showSlashMenu = debounce(
); );
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(
context.rootComponent.host, context.std.host,
context.model context.model
); );
if (!inlineEditor) return; if (!inlineEditor) return;
@@ -115,12 +113,10 @@ export class AffineSlashMenuWidget extends WidgetComponent {
else inlineEditor.slots.inlineRangeSync.once(callback); else inlineEditor.slots.inlineRangeSync.once(callback);
}; };
const rootComponent = this.block; if (this.block.model.flavour !== 'affine:page') {
if (rootComponent.model.flavour !== 'affine:page') {
console.error('SlashMenuWidget should be used in RootBlock'); console.error('SlashMenuWidget should be used in RootBlock');
return; return;
} }
assertType<RootBlockComponent>(rootComponent);
inlineRangeApplyCallback(() => { inlineRangeApplyCallback(() => {
const textSelection = this.host.selection.find(TextSelection); const textSelection = this.host.selection.find(TextSelection);
@@ -155,7 +151,7 @@ export class AffineSlashMenuWidget extends WidgetComponent {
...this.config, ...this.config,
items: filterEnabledSlashMenuItems(this.config.items, { items: filterEnabledSlashMenuItems(this.config.items, {
model, model,
rootComponent, std: this.std,
}), }),
}; };
@@ -163,7 +159,7 @@ export class AffineSlashMenuWidget extends WidgetComponent {
showSlashMenu({ showSlashMenu({
context: { context: {
model, model,
rootComponent, std: this.std,
}, },
triggerKey: matchedKey, triggerKey: matchedKey,
config, config,
@@ -224,9 +220,3 @@ export class AffineSlashMenuWidget extends WidgetComponent {
this.handleEvent('compositionEnd', this._onCompositionEnd); this.handleEvent('compositionEnd', this._onCompositionEnd);
} }
} }
declare global {
interface HTMLElementTagNameMap {
[AFFINE_SLASH_MENU_WIDGET]: AffineSlashMenuWidget;
}
}

View File

@@ -0,0 +1,27 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
},
"include": ["./src"],
"references": [
{ "path": "../block-attachment" },
{ "path": "../block-database" },
{ "path": "../block-embed" },
{ "path": "../block-image" },
{ "path": "../block-latex" },
{ "path": "../block-note" },
{ "path": "../block-surface" },
{ "path": "../block-surface-ref" },
{ "path": "../block-table" },
{ "path": "../components" },
{ "path": "../shared" },
{ "path": "../../framework/block-std" },
{ "path": "../data-view" },
{ "path": "../../framework/global" },
{ "path": "../../framework/inline" },
{ "path": "../../framework/store" }
]
}

View File

@@ -43,6 +43,7 @@
"@blocksuite/affine-widget-frame-title": "workspace:*", "@blocksuite/affine-widget-frame-title": "workspace:*",
"@blocksuite/affine-widget-remote-selection": "workspace:*", "@blocksuite/affine-widget-remote-selection": "workspace:*",
"@blocksuite/affine-widget-scroll-anchoring": "workspace:*", "@blocksuite/affine-widget-scroll-anchoring": "workspace:*",
"@blocksuite/affine-widget-slash-menu": "workspace:*",
"@blocksuite/affine-widget-toolbar": "workspace:*", "@blocksuite/affine-widget-toolbar": "workspace:*",
"@blocksuite/block-std": "workspace:*", "@blocksuite/block-std": "workspace:*",
"@blocksuite/data-view": "workspace:*", "@blocksuite/data-view": "workspace:*",

View File

@@ -46,6 +46,7 @@ import { effects as widgetEdgelessAutoConnectEffects } from '@blocksuite/affine-
import { effects as widgetFrameTitleEffects } from '@blocksuite/affine-widget-frame-title/effects'; import { effects as widgetFrameTitleEffects } from '@blocksuite/affine-widget-frame-title/effects';
import { effects as widgetRemoteSelectionEffects } from '@blocksuite/affine-widget-remote-selection/effects'; import { effects as widgetRemoteSelectionEffects } from '@blocksuite/affine-widget-remote-selection/effects';
import { effects as widgetScrollAnchoringEffects } from '@blocksuite/affine-widget-scroll-anchoring/effects'; import { effects as widgetScrollAnchoringEffects } from '@blocksuite/affine-widget-scroll-anchoring/effects';
import { effects as widgetSlashMenuEffects } from '@blocksuite/affine-widget-slash-menu/effects';
import { effects as widgetToolbarEffects } from '@blocksuite/affine-widget-toolbar/effects'; import { effects as widgetToolbarEffects } from '@blocksuite/affine-widget-toolbar/effects';
import { effects as stdEffects } from '@blocksuite/block-std/effects'; import { effects as stdEffects } from '@blocksuite/block-std/effects';
import { effects as dataViewEffects } from '@blocksuite/data-view/effects'; import { effects as dataViewEffects } from '@blocksuite/data-view/effects';
@@ -103,6 +104,7 @@ export function effects() {
widgetRemoteSelectionEffects(); widgetRemoteSelectionEffects();
widgetDragHandleEffects(); widgetDragHandleEffects();
widgetEdgelessAutoConnectEffects(); widgetEdgelessAutoConnectEffects();
widgetSlashMenuEffects();
widgetToolbarEffects(); widgetToolbarEffects();
fragmentFramePanelEffects(); fragmentFramePanelEffects();

View File

@@ -114,6 +114,9 @@ export {
export type { DragBlockPayload } from '@blocksuite/affine-widget-drag-handle'; export type { DragBlockPayload } from '@blocksuite/affine-widget-drag-handle';
export { toolbarWidget } from '@blocksuite/affine-widget-toolbar'; export { toolbarWidget } from '@blocksuite/affine-widget-toolbar';
// TODO(@L-Sun): Narrow the export scope
export * from '@blocksuite/affine-widget-slash-menu';
const env: Record<string, unknown> = const env: Record<string, unknown> =
typeof globalThis !== 'undefined' typeof globalThis !== 'undefined'
? globalThis ? globalThis

View File

@@ -36,6 +36,7 @@
{ "path": "../affine/widget-frame-title" }, { "path": "../affine/widget-frame-title" },
{ "path": "../affine/widget-remote-selection" }, { "path": "../affine/widget-remote-selection" },
{ "path": "../affine/widget-scroll-anchoring" }, { "path": "../affine/widget-scroll-anchoring" },
{ "path": "../affine/widget-slash-menu" },
{ "path": "../affine/widget-toolbar" }, { "path": "../affine/widget-toolbar" },
{ "path": "../framework/block-std" }, { "path": "../framework/block-std" },
{ "path": "../affine/data-view" }, { "path": "../affine/data-view" },

View File

@@ -29,26 +29,28 @@ export function setupSlashMenuAIEntry(slashMenu: AffineSlashMenuWidget) {
const showWhenWrapper = const showWhenWrapper =
(item?: AIItemConfig) => (item?: AIItemConfig) =>
({ rootComponent }: AffineSlashMenuContext) => { ({ std }: AffineSlashMenuContext) => {
const affineAIPanelWidget = rootComponent.host.view.getWidget( const root = std.host.doc.root;
if (!root) return false;
const affineAIPanelWidget = std.view.getWidget(
AFFINE_AI_PANEL_WIDGET, AFFINE_AI_PANEL_WIDGET,
rootComponent.model.id root.id
); );
if (affineAIPanelWidget === null) return false; if (affineAIPanelWidget === null) return false;
const chain = rootComponent.host.command.chain(); const chain = std.host.command.chain();
const docModeService = rootComponent.std.get(DocModeProvider); const docModeService = std.get(DocModeProvider);
const editorMode = docModeService.getPrimaryMode(rootComponent.doc.id); const editorMode = docModeService.getPrimaryMode(std.host.doc.id);
return item?.showWhen?.(chain, editorMode, rootComponent.host) ?? true; return item?.showWhen?.(chain, editorMode, std.host) ?? true;
}; };
const actionItemWrapper = ( const actionItemWrapper = (
item: AIItemConfig item: AIItemConfig
): AffineSlashMenuActionItem => ({ ): AffineSlashMenuActionItem => ({
...basicItemConfig(item), ...basicItemConfig(item),
action: ({ rootComponent }: AffineSlashMenuContext) => { action: ({ std }: AffineSlashMenuContext) => {
item?.handler?.(rootComponent.host); item?.handler?.(std.host);
}, },
}); });
@@ -58,7 +60,7 @@ export function setupSlashMenuAIEntry(slashMenu: AffineSlashMenuWidget) {
subMenu: (item.subItem ?? []).map<AffineSlashMenuActionItem>( subMenu: (item.subItem ?? []).map<AffineSlashMenuActionItem>(
({ type, handler }) => ({ ({ type, handler }) => ({
name: type, name: type,
action: ({ rootComponent }) => handler?.(rootComponent.host), action: ({ std }) => handler?.(std.host),
}) })
), ),
}; };
@@ -78,11 +80,12 @@ export function setupSlashMenuAIEntry(slashMenu: AffineSlashMenuWidget) {
name: 'Ask AI', name: 'Ask AI',
icon: AIStarIcon, icon: AIStarIcon,
showWhen: showWhenWrapper(), showWhen: showWhenWrapper(),
action: ({ rootComponent }) => { action: ({ std }) => {
const view = rootComponent.host.view; const root = std.host.doc.root;
const affineAIPanelWidget = view.getWidget( if (!root) return;
const affineAIPanelWidget = std.view.getWidget(
AFFINE_AI_PANEL_WIDGET, AFFINE_AI_PANEL_WIDGET,
rootComponent.model.id root.id
) as AffineAIPanelWidget; ) as AffineAIPanelWidget;
handleInlineAskAIAction(affineAIPanelWidget.host); handleInlineAskAIAction(affineAIPanelWidget.host);
}, },

View File

@@ -17,7 +17,7 @@ import {
imageToolbarWidget, imageToolbarWidget,
ParagraphBlockService, ParagraphBlockService,
ReferenceNodeConfigIdentifier, ReferenceNodeConfigIdentifier,
slashMenuWidget, SlashMenuExtension,
surfaceRefToolbarWidget, surfaceRefToolbarWidget,
toolbarWidget, toolbarWidget,
VirtualKeyboardProvider as BSVirtualKeyboardProvider, VirtualKeyboardProvider as BSVirtualKeyboardProvider,
@@ -166,10 +166,10 @@ export function enableMobileExtension(
specBuilder: SpecBuilder, specBuilder: SpecBuilder,
framework: FrameworkProvider framework: FrameworkProvider
): void { ): void {
specBuilder.omit(slashMenuWidget);
specBuilder.omit(codeToolbarWidget); specBuilder.omit(codeToolbarWidget);
specBuilder.omit(imageToolbarWidget); specBuilder.omit(imageToolbarWidget);
specBuilder.omit(surfaceRefToolbarWidget); specBuilder.omit(surfaceRefToolbarWidget);
specBuilder.omit(toolbarWidget); specBuilder.omit(toolbarWidget);
specBuilder.omit(SlashMenuExtension);
specBuilder.extend([MobileSpecsPatches, KeyboardToolbarExtension(framework)]); specBuilder.extend([MobileSpecsPatches, KeyboardToolbarExtension(framework)]);
} }

View File

@@ -137,11 +137,10 @@ export function patchQuickSearchService(framework: FrameworkProvider) {
'action' in item && 'action' in item &&
(item.name === 'Linked Doc' || item.name === 'Link') (item.name === 'Linked Doc' || item.name === 'Link')
) { ) {
item.action = async ({ rootComponent }) => { item.action = async ({ std }) => {
const [success, { insertedLinkType }] = const [success, { insertedLinkType }] = std.command.exec(
rootComponent.std.command.exec( insertLinkByQuickSearchCommand
insertLinkByQuickSearchCommand );
);
if (!success) return; if (!success) return;

View File

@@ -250,6 +250,7 @@ export const PackageList = [
'blocksuite/affine/widget-frame-title', 'blocksuite/affine/widget-frame-title',
'blocksuite/affine/widget-remote-selection', 'blocksuite/affine/widget-remote-selection',
'blocksuite/affine/widget-scroll-anchoring', 'blocksuite/affine/widget-scroll-anchoring',
'blocksuite/affine/widget-slash-menu',
'blocksuite/affine/widget-toolbar', 'blocksuite/affine/widget-toolbar',
'blocksuite/framework/block-std', 'blocksuite/framework/block-std',
'blocksuite/affine/data-view', 'blocksuite/affine/data-view',
@@ -435,6 +436,28 @@ export const PackageList = [
'blocksuite/framework/global', 'blocksuite/framework/global',
], ],
}, },
{
location: 'blocksuite/affine/widget-slash-menu',
name: '@blocksuite/affine-widget-slash-menu',
workspaceDependencies: [
'blocksuite/affine/block-attachment',
'blocksuite/affine/block-database',
'blocksuite/affine/block-embed',
'blocksuite/affine/block-image',
'blocksuite/affine/block-latex',
'blocksuite/affine/block-note',
'blocksuite/affine/block-surface',
'blocksuite/affine/block-surface-ref',
'blocksuite/affine/block-table',
'blocksuite/affine/components',
'blocksuite/affine/shared',
'blocksuite/framework/block-std',
'blocksuite/affine/data-view',
'blocksuite/framework/global',
'blocksuite/framework/inline',
'blocksuite/framework/store',
],
},
{ {
location: 'blocksuite/affine/widget-toolbar', location: 'blocksuite/affine/widget-toolbar',
name: '@blocksuite/affine-widget-toolbar', name: '@blocksuite/affine-widget-toolbar',
@@ -481,6 +504,7 @@ export const PackageList = [
'blocksuite/affine/widget-frame-title', 'blocksuite/affine/widget-frame-title',
'blocksuite/affine/widget-remote-selection', 'blocksuite/affine/widget-remote-selection',
'blocksuite/affine/widget-scroll-anchoring', 'blocksuite/affine/widget-scroll-anchoring',
'blocksuite/affine/widget-slash-menu',
'blocksuite/affine/widget-toolbar', 'blocksuite/affine/widget-toolbar',
'blocksuite/framework/block-std', 'blocksuite/framework/block-std',
'blocksuite/affine/data-view', 'blocksuite/affine/data-view',
@@ -887,6 +911,7 @@ export type PackageName =
| '@blocksuite/affine-widget-frame-title' | '@blocksuite/affine-widget-frame-title'
| '@blocksuite/affine-widget-remote-selection' | '@blocksuite/affine-widget-remote-selection'
| '@blocksuite/affine-widget-scroll-anchoring' | '@blocksuite/affine-widget-scroll-anchoring'
| '@blocksuite/affine-widget-slash-menu'
| '@blocksuite/affine-widget-toolbar' | '@blocksuite/affine-widget-toolbar'
| '@blocksuite/blocks' | '@blocksuite/blocks'
| '@blocksuite/block-std' | '@blocksuite/block-std'

View File

@@ -81,6 +81,7 @@
{ "path": "./blocksuite/affine/widget-frame-title" }, { "path": "./blocksuite/affine/widget-frame-title" },
{ "path": "./blocksuite/affine/widget-remote-selection" }, { "path": "./blocksuite/affine/widget-remote-selection" },
{ "path": "./blocksuite/affine/widget-scroll-anchoring" }, { "path": "./blocksuite/affine/widget-scroll-anchoring" },
{ "path": "./blocksuite/affine/widget-slash-menu" },
{ "path": "./blocksuite/affine/widget-toolbar" }, { "path": "./blocksuite/affine/widget-toolbar" },
{ "path": "./blocksuite/blocks" }, { "path": "./blocksuite/blocks" },
{ "path": "./blocksuite/framework/block-std" }, { "path": "./blocksuite/framework/block-std" },

View File

@@ -2608,6 +2608,7 @@ __metadata:
"@blocksuite/affine-widget-frame-title": "workspace:*" "@blocksuite/affine-widget-frame-title": "workspace:*"
"@blocksuite/affine-widget-remote-selection": "workspace:*" "@blocksuite/affine-widget-remote-selection": "workspace:*"
"@blocksuite/affine-widget-scroll-anchoring": "workspace:*" "@blocksuite/affine-widget-scroll-anchoring": "workspace:*"
"@blocksuite/affine-widget-slash-menu": "workspace:*"
"@blocksuite/affine-widget-toolbar": "workspace:*" "@blocksuite/affine-widget-toolbar": "workspace:*"
"@blocksuite/block-std": "workspace:*" "@blocksuite/block-std": "workspace:*"
"@blocksuite/data-view": "workspace:*" "@blocksuite/data-view": "workspace:*"
@@ -2948,6 +2949,36 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@blocksuite/affine-widget-slash-menu@workspace:*, @blocksuite/affine-widget-slash-menu@workspace:blocksuite/affine/widget-slash-menu":
version: 0.0.0-use.local
resolution: "@blocksuite/affine-widget-slash-menu@workspace:blocksuite/affine/widget-slash-menu"
dependencies:
"@blocksuite/affine-block-attachment": "workspace:*"
"@blocksuite/affine-block-database": "workspace:*"
"@blocksuite/affine-block-embed": "workspace:*"
"@blocksuite/affine-block-image": "workspace:*"
"@blocksuite/affine-block-latex": "workspace:*"
"@blocksuite/affine-block-note": "workspace:*"
"@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-block-surface-ref": "workspace:*"
"@blocksuite/affine-block-table": "workspace:*"
"@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-shared": "workspace:*"
"@blocksuite/block-std": "workspace:*"
"@blocksuite/data-view": "workspace:*"
"@blocksuite/global": "workspace:*"
"@blocksuite/icons": "npm:^2.2.4"
"@blocksuite/inline": "workspace:*"
"@blocksuite/store": "workspace:*"
"@floating-ui/dom": "npm:^1.6.13"
"@preact/signals-core": "npm:^1.8.0"
"@toeverything/theme": "npm:^1.1.12"
"@types/lodash-es": "npm:^4.17.12"
lit: "npm:^3.2.0"
lodash-es: "npm:^4.17.21"
languageName: unknown
linkType: soft
"@blocksuite/affine-widget-toolbar@workspace:*, @blocksuite/affine-widget-toolbar@workspace:blocksuite/affine/widget-toolbar": "@blocksuite/affine-widget-toolbar@workspace:*, @blocksuite/affine-widget-toolbar@workspace:blocksuite/affine/widget-toolbar":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@blocksuite/affine-widget-toolbar@workspace:blocksuite/affine/widget-toolbar" resolution: "@blocksuite/affine-widget-toolbar@workspace:blocksuite/affine/widget-toolbar"
@@ -3049,6 +3080,7 @@ __metadata:
"@blocksuite/affine-widget-frame-title": "workspace:*" "@blocksuite/affine-widget-frame-title": "workspace:*"
"@blocksuite/affine-widget-remote-selection": "workspace:*" "@blocksuite/affine-widget-remote-selection": "workspace:*"
"@blocksuite/affine-widget-scroll-anchoring": "workspace:*" "@blocksuite/affine-widget-scroll-anchoring": "workspace:*"
"@blocksuite/affine-widget-slash-menu": "workspace:*"
"@blocksuite/affine-widget-toolbar": "workspace:*" "@blocksuite/affine-widget-toolbar": "workspace:*"
"@blocksuite/block-std": "workspace:*" "@blocksuite/block-std": "workspace:*"
"@blocksuite/data-view": "workspace:*" "@blocksuite/data-view": "workspace:*"
@@ -3097,7 +3129,7 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@blocksuite/icons@npm:2.2.4, @blocksuite/icons@npm:^2.2.1, @blocksuite/icons@npm:^2.2.2, @blocksuite/icons@npm:^2.2.3": "@blocksuite/icons@npm:2.2.4, @blocksuite/icons@npm:^2.2.1, @blocksuite/icons@npm:^2.2.2, @blocksuite/icons@npm:^2.2.3, @blocksuite/icons@npm:^2.2.4":
version: 2.2.4 version: 2.2.4
resolution: "@blocksuite/icons@npm:2.2.4" resolution: "@blocksuite/icons@npm:2.2.4"
peerDependencies: peerDependencies: