feat(editor): add callout block (#10563)

- Add `CalloutBlockModel `
- Implement `CalloutBlockComponent `
- Integrate with slash menu (/)
This commit is contained in:
Flrande
2025-03-05 09:28:51 +00:00
parent 1c2a6eac85
commit bd62634a76
35 changed files with 519 additions and 14 deletions
@@ -0,0 +1,45 @@
{
"name": "@blocksuite/affine-block-callout",
"description": "Callout block for BlockSuite.",
"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-components": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-shared": "workspace:*",
"@blocksuite/block-std": "workspace:*",
"@blocksuite/global": "workspace:*",
"@blocksuite/inline": "workspace:*",
"@blocksuite/store": "workspace:*",
"@emoji-mart/data": "^1.2.1",
"@floating-ui/dom": "^1.6.10",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.12",
"@types/mdast": "^4.0.4",
"emoji-mart": "^5.6.0",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"zod": "^3.23.8"
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts"
},
"files": [
"src",
"dist",
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.20.0"
}
@@ -0,0 +1,121 @@
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
import { createLitPortal } from '@blocksuite/affine-components/portal';
import { DefaultInlineManagerExtension } from '@blocksuite/affine-components/rich-text';
import { type CalloutBlockModel } from '@blocksuite/affine-model';
import { NOTE_SELECTOR } from '@blocksuite/affine-shared/consts';
import {
DocModeProvider,
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import type { BlockComponent } from '@blocksuite/block-std';
import { flip, offset } from '@floating-ui/dom';
import { css, html } from 'lit';
import { query } from 'lit/decorators.js';
export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockModel> {
static override styles = css`
:host {
display: block;
margin: 8px 0;
}
.affine-callout-block-container {
display: flex;
padding: 12px 16px;
border-radius: 8px;
background-color: ${unsafeCSSVarV2('block/callout/background/grey')};
}
.affine-callout-emoji-container {
margin-right: 12px;
margin-top: 10px;
user-select: none;
font-size: 1.2em;
}
.affine-callout-emoji:hover {
cursor: pointer;
opacity: 0.7;
}
.affine-callout-children {
flex: 1;
min-width: 0;
}
`;
private _emojiMenuAbortController: AbortController | null = null;
private readonly _toggleEmojiMenu = () => {
if (this._emojiMenuAbortController) {
this._emojiMenuAbortController.abort();
}
this._emojiMenuAbortController = new AbortController();
const theme = this.std.get(ThemeProvider).theme$.value;
createLitPortal({
template: html`<affine-emoji-menu
.theme=${theme}
.onEmojiSelect=${(data: any) => {
this.model.emoji = data.native;
console.log(data);
}}
></affine-emoji-menu>`,
portalStyles: {
zIndex: 'var(--affine-z-index-popover)',
},
container: this.host,
computePosition: {
referenceElement: this._emojiButton,
placement: 'bottom-start',
middleware: [flip(), offset(4)],
autoUpdate: { animationFrame: true },
},
abortController: this._emojiMenuAbortController,
closeOnClickAway: true,
});
};
get attributeRenderer() {
return this.inlineManager.getRenderer();
}
get attributesSchema() {
return this.inlineManager.getSchema();
}
get embedChecker() {
return this.inlineManager.embedChecker;
}
get inlineManager() {
return this.std.get(DefaultInlineManagerExtension.identifier);
}
@query('.affine-callout-emoji')
private accessor _emojiButton!: HTMLElement;
override get topContenteditableElement() {
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
return this.closest<BlockComponent>(NOTE_SELECTOR);
}
return this.rootComponent;
}
override renderBlock() {
return html`
<div class="affine-callout-block-container">
<div
@click=${this._toggleEmojiMenu}
contenteditable="false"
class="affine-callout-emoji-container"
>
<span class="affine-callout-emoji">${this.model.emoji}</span>
</div>
<div class="affine-callout-children">
${this.renderChildren(this.model)}
</div>
</div>
`;
}
}
@@ -0,0 +1,8 @@
import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
export const CalloutBlockSpec: ExtensionType[] = [
FlavourExtension('affine:callout'),
BlockViewExtension('affine:callout', literal`affine-callout`),
];
@@ -0,0 +1,14 @@
import { CalloutBlockComponent } from './callout-block';
import { EmojiMenu } from './emoji-menu';
export function effects() {
customElements.define('affine-callout', CalloutBlockComponent);
customElements.define('affine-emoji-menu', EmojiMenu);
}
declare global {
interface HTMLElementTagNameMap {
'affine-callout': CalloutBlockComponent;
'affine-emoji-menu': EmojiMenu;
}
}
@@ -0,0 +1,34 @@
import { WithDisposable } from '@blocksuite/global/utils';
import data from '@emoji-mart/data';
import { Picker } from 'emoji-mart';
import { html, LitElement, type PropertyValues } from 'lit';
import { property, query } from 'lit/decorators.js';
export class EmojiMenu extends WithDisposable(LitElement) {
override firstUpdated(props: PropertyValues) {
const result = super.firstUpdated(props);
const picker = new Picker({
data,
onEmojiSelect: this.onEmojiSelect,
autoFocus: true,
theme: this.theme,
});
this.emojiMenu.append(picker as unknown as Node);
return result;
}
@property({ attribute: false })
accessor onEmojiSelect: (data: any) => void = () => {};
@property({ attribute: false })
accessor theme: 'light' | 'dark' = 'light';
@query('.affine-emoji-menu')
accessor emojiMenu!: HTMLElement;
override render() {
return html`<div class="affine-emoji-menu"></div>`;
}
}
@@ -0,0 +1,3 @@
export * from './callout-block.js';
export * from './callout-spec.js';
export * from './effects.js';
@@ -0,0 +1,18 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
},
"include": ["./src"],
"references": [
{ "path": "../components" },
{ "path": "../model" },
{ "path": "../shared" },
{ "path": "../../framework/block-std" },
{ "path": "../../framework/global" },
{ "path": "../../framework/inline" },
{ "path": "../../framework/store" }
]
}
@@ -5,6 +5,7 @@ import {
textKeymap,
} from '@blocksuite/affine-components/rich-text';
import {
CalloutBlockModel,
ParagraphBlockModel,
ParagraphBlockSchema,
} from '@blocksuite/affine-model';
@@ -40,7 +41,12 @@ export const ParagraphKeymapExtension = KeymapExtension(
const { store } = std;
const model = store.getBlock(text.from.blockId)?.model;
if (!model || !matchModels(model, [ParagraphBlockModel])) return;
if (
!model ||
!matchModels(model, [ParagraphBlockModel]) ||
matchModels(model.parent, [CalloutBlockModel])
)
return;
const event = ctx.get('defaultState').event;
event.preventDefault();
@@ -71,7 +77,12 @@ export const ParagraphKeymapExtension = KeymapExtension(
const text = std.selection.find(TextSelection);
if (!text) return;
const model = store.getBlock(text.from.blockId)?.model;
if (!model || !matchModels(model, [ParagraphBlockModel])) return;
if (
!model ||
!matchModels(model, [ParagraphBlockModel]) ||
matchModels(model.parent, [CalloutBlockModel])
)
return;
const inlineEditor = getInlineEditorByModel(
std.host,
text.from.blockId
@@ -98,16 +109,21 @@ export const ParagraphKeymapExtension = KeymapExtension(
const text = std.selection.find(TextSelection);
if (!text) return;
const model = store.getBlock(text.from.blockId)?.model;
if (!model || !matchModels(model, [ParagraphBlockModel])) return;
if (
!model ||
!matchModels(model, [ParagraphBlockModel]) ||
matchModels(model.parent, [CalloutBlockModel])
)
return;
const inlineEditor = getInlineEditorByModel(
std.host,
text.from.blockId
);
const range = inlineEditor?.getInlineRange();
if (!range || !inlineEditor) return;
const inlineRange = inlineEditor?.getInlineRange();
if (!inlineRange || !inlineEditor) return;
const raw = ctx.get('keyboardState').raw;
const isEnd = model.text.length === range.index;
const isEnd = model.text.length === inlineRange.index;
if (model.type === 'quote') {
const textStr = model.text.toString();
@@ -129,7 +145,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
if (isEnd && endWithTwoBlankLines) {
raw.preventDefault();
store.captureSync();
model.text.delete(range.index - 1, 1);
model.text.delete(inlineRange.index - 1, 1);
std.command.chain().pipe(addParagraphCommand).run();
return true;
}
@@ -149,7 +165,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
if (index === -1) return true;
const collapsedSiblings = calculateCollapsedSiblings(model);
const rightText = model.text.split(range.index);
const rightText = model.text.split(inlineRange.index);
const newId = store.addBlock(
model.flavour,
{ type: model.type, text: rightText },
@@ -1,6 +1,7 @@
import {
AttachmentBlockModel,
BookmarkBlockModel,
CalloutBlockModel,
CodeBlockModel,
DatabaseBlockModel,
DividerBlockModel,
@@ -25,7 +26,12 @@ export function forwardDelete(std: BlockStdScope) {
if (!text) return;
const isCollapsed = text.isCollapsed();
const model = store.getBlock(text.from.blockId)?.model;
if (!model || !matchModels(model, [ParagraphBlockModel])) return;
if (
!model ||
!matchModels(model, [ParagraphBlockModel]) ||
matchModels(model.parent, [CalloutBlockModel])
)
return;
const isEnd = isCollapsed && text.from.index === model.text.length;
if (!isEnd) return;
const parent = store.getParent(model);
@@ -9,6 +9,7 @@ import {
} from '@blocksuite/affine-block-embed';
import { insertImagesCommand } from '@blocksuite/affine-block-image';
import { insertLatexBlockCommand } from '@blocksuite/affine-block-latex';
import { focusBlockEnd } from '@blocksuite/affine-block-note';
import { getSurfaceBlock } from '@blocksuite/affine-block-surface';
import { insertSurfaceRefBlockCommand } from '@blocksuite/affine-block-surface-ref';
import { insertTableBlockCommand } from '@blocksuite/affine-block-table';
@@ -61,6 +62,7 @@ import { assertType } from '@blocksuite/global/utils';
import {
DualLinkIcon,
ExportToPdfIcon,
FontIcon,
FrameIcon,
GroupingIcon,
ImageIcon,
@@ -171,6 +173,37 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
!insideEdgelessText(model),
})),
{
name: 'Callout',
description: 'Let your words stand out.',
icon: FontIcon(),
alias: ['callout'],
showWhen: ({ model }) => {
return model.doc.get(FeatureFlagService).getFlag('enable_callout');
},
action: ({ model, rootComponent }) => {
const { doc } = model;
const parent = doc.getParent(model);
if (!parent) return;
const index = parent.children.indexOf(model);
if (index === -1) return;
const calloutId = doc.addBlock('affine:callout', {}, parent, index + 1);
if (!calloutId) return;
const paragraphId = doc.addBlock('affine:paragraph', {}, calloutId);
if (!paragraphId) return;
rootComponent.updateComplete
.then(() => {
const paragraph = rootComponent.std.view.getBlock(paragraphId);
if (!paragraph) return;
rootComponent.std.command.exec(focusBlockEnd, {
focusBlock: paragraph,
});
})
.catch(console.error);
},
},
{
name: 'Inline equation',
description: 'Create a equation block.',
@@ -1,4 +1,8 @@
import { CodeBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model';
import {
CalloutBlockModel,
CodeBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import {
isHorizontalRuleMarkdown,
isMarkdownPrefix,
@@ -37,7 +41,13 @@ export function markdownInput(
const isHeading = isParagraph && model.type.startsWith('h');
const isParagraphQuoteBlock = isParagraph && model.type === 'quote';
const isCodeBlock = matchModels(model, [CodeBlockModel]);
if (isHeading || isParagraphQuoteBlock || isCodeBlock) return;
if (
isHeading ||
isParagraphQuoteBlock ||
isCodeBlock ||
matchModels(model.parent, [CalloutBlockModel])
)
return;
const lineInfo = inline.getLine(range.index);
if (!lineInfo) return;
@@ -0,0 +1,39 @@
import {
BlockModel,
BlockSchemaExtension,
defineBlockSchema,
type Text,
} from '@blocksuite/store';
export const CalloutBlockSchema = defineBlockSchema({
flavour: 'affine:callout',
props: internal => ({
emoji: '😀',
text: internal.Text(),
}),
metadata: {
version: 1,
role: 'hub',
parent: [
'affine:note',
'affine:database',
'affine:paragraph',
'affine:list',
'affine:edgeless-text',
],
children: ['affine:paragraph'],
},
toModel: () => new CalloutBlockModel(),
});
export type CalloutProps = {
emoji: string;
text: Text;
};
export class CalloutBlockModel extends BlockModel<CalloutProps> {
override text!: Text;
}
export const CalloutBlockSchemaExtension =
BlockSchemaExtension(CalloutBlockSchema);
@@ -0,0 +1 @@
export * from './callout-model.js';
@@ -1,5 +1,6 @@
export * from './attachment/index.js';
export * from './bookmark/index.js';
export * from './callout/index.js';
export * from './code/index.js';
export * from './database/index.js';
export * from './divider/index.js';
@@ -88,6 +88,7 @@ export const NoteBlockSchema = defineBlockSchema({
'affine:surface-ref',
'affine:embed-*',
'affine:latex',
'affine:callout',
TableModelFlavour,
],
},
@@ -37,6 +37,7 @@ export const ParagraphBlockSchema = defineBlockSchema({
'affine:paragraph',
'affine:list',
'affine:edgeless-text',
'affine:callout',
],
},
toModel: () => new ParagraphBlockModel(),
@@ -17,6 +17,7 @@ export interface BlockSuiteFlags {
enable_mobile_keyboard_toolbar: boolean;
enable_mobile_linked_doc_menu: boolean;
enable_block_meta: boolean;
enable_callout: boolean;
}
export class FeatureFlagService extends StoreExtension {
@@ -38,6 +39,7 @@ export class FeatureFlagService extends StoreExtension {
enable_mobile_keyboard_toolbar: false,
enable_mobile_linked_doc_menu: false,
enable_block_meta: false,
enable_callout: false,
});
setFlag(key: keyof BlockSuiteFlags, value: boolean) {
@@ -13,6 +13,7 @@
"author": "toeverything",
"license": "MIT",
"dependencies": {
"@blocksuite/affine-block-callout": "workspace:*",
"@blocksuite/affine-block-list": "workspace:*",
"@blocksuite/affine-block-note": "workspace:*",
"@blocksuite/affine-block-paragraph": "workspace:*",
@@ -1,3 +1,4 @@
import { type CalloutBlockComponent } from '@blocksuite/affine-block-callout';
import {
AFFINE_EDGELESS_NOTE,
type EdgelessNoteBlockComponent,
@@ -6,7 +7,7 @@ import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph';
import {
DatabaseBlockModel,
ListBlockModel,
type ParagraphBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import { DocModeProvider } from '@blocksuite/affine-shared/services';
import {
@@ -209,6 +210,14 @@ export const getClosestBlockByPoint = (
return null;
}
if (matchModels(closestBlock.model, [ParagraphBlockModel])) {
const callout =
closestBlock.closest<CalloutBlockComponent>('affine-callout');
if (callout) {
return callout;
}
}
return closestBlock;
};
@@ -7,6 +7,7 @@
},
"include": ["./src"],
"references": [
{ "path": "../block-callout" },
{ "path": "../block-list" },
{ "path": "../block-note" },
{ "path": "../block-paragraph" },
+1
View File
@@ -16,6 +16,7 @@
"dependencies": {
"@blocksuite/affine-block-attachment": "workspace:*",
"@blocksuite/affine-block-bookmark": "workspace:*",
"@blocksuite/affine-block-callout": "workspace:*",
"@blocksuite/affine-block-code": "workspace:*",
"@blocksuite/affine-block-data-view": "workspace:*",
"@blocksuite/affine-block-database": "workspace:*",
+2
View File
@@ -1,5 +1,6 @@
import { effects as blockAttachmentEffects } from '@blocksuite/affine-block-attachment/effects';
import { effects as blockBookmarkEffects } from '@blocksuite/affine-block-bookmark/effects';
import { effects as blockCalloutEffects } from '@blocksuite/affine-block-callout/effects';
import { effects as blockCodeEffects } from '@blocksuite/affine-block-code/effects';
import { effects as blockDataViewEffects } from '@blocksuite/affine-block-data-view/effects';
import { effects as blockDatabaseEffects } from '@blocksuite/affine-block-database/effects';
@@ -72,6 +73,7 @@ export function effects() {
blockCodeEffects();
blockTableEffects();
blockRootEffects();
blockCalloutEffects();
componentCaptionEffects();
componentContextMenuEffects();
@@ -1,5 +1,6 @@
import { AttachmentBlockSpec } from '@blocksuite/affine-block-attachment';
import { BookmarkBlockSpec } from '@blocksuite/affine-block-bookmark';
import { CalloutBlockSpec } from '@blocksuite/affine-block-callout';
import { CodeBlockSpec } from '@blocksuite/affine-block-code';
import { DataViewBlockSpec } from '@blocksuite/affine-block-data-view';
import { DatabaseBlockSpec } from '@blocksuite/affine-block-database';
@@ -55,6 +56,7 @@ export const CommonBlockSpecs: ExtensionType[] = [
ParagraphBlockSpec,
DefaultOpenDocExtension,
FontLoaderService,
CalloutBlockSpec,
].flat();
export const PageFirstPartyBlockSpecs: ExtensionType[] = [
@@ -6,6 +6,7 @@ import { TableSelectionExtension } from '@blocksuite/affine-block-table';
import {
AttachmentBlockSchemaExtension,
BookmarkBlockSchemaExtension,
CalloutBlockSchemaExtension,
CodeBlockSchemaExtension,
DatabaseBlockSchemaExtension,
DividerBlockSchemaExtension,
@@ -78,6 +79,7 @@ export const StoreExtensions: ExtensionType[] = [
EdgelessTextBlockSchemaExtension,
LatexBlockSchemaExtension,
TableBlockSchemaExtension,
CalloutBlockSchemaExtension,
BlockSelectionExtension,
TextSelectionExtension,
+2
View File
@@ -4,6 +4,7 @@ import { SurfaceBlockSchema } from '@blocksuite/affine-block-surface';
import {
AttachmentBlockSchema,
BookmarkBlockSchema,
CalloutBlockSchema,
CodeBlockSchema,
DatabaseBlockSchema,
DividerBlockSchema,
@@ -54,4 +55,5 @@ export const AffineSchemas: z.infer<typeof BlockSchema>[] = [
EdgelessTextBlockSchema,
LatexBlockSchema,
TableBlockSchema,
CalloutBlockSchema,
];
+1
View File
@@ -9,6 +9,7 @@
"references": [
{ "path": "../affine/block-attachment" },
{ "path": "../affine/block-bookmark" },
{ "path": "../affine/block-callout" },
{ "path": "../affine/block-code" },
{ "path": "../affine/block-data-view" },
{ "path": "../affine/block-database" },
@@ -17,6 +17,11 @@ const extensions = StoreExtensions;
beforeEach(async () => {
vi.useFakeTimers({ toFake: ['requestIdleCallback'] });
vi.mock('emoji-mart', () => {
return {
Picker: vi.fn(),
};
});
docCollection = new TestWorkspace({ id: 'test' });
docCollection.meta.initialize();
const initPage = async (page: Store) => {
@@ -113,6 +113,16 @@ export const AFFINE_FLAGS = {
configurable: isCanaryBuild,
defaultState: false,
},
enable_callout: {
category: 'blocksuite',
bsFlag: 'enable_callout',
displayName:
'com.affine.settings.workspace.experimental-features.enable-callout.name',
description:
'com.affine.settings.workspace.experimental-features.enable-callout.description',
configurable: isCanaryBuild,
defaultState: false,
},
enable_emoji_folder_icon: {
category: 'affine',
displayName:
@@ -5,7 +5,13 @@
import { JOURNAL_DATE_FORMAT } from '@affine/core/modules/journal';
import { I18n } from '@affine/i18n';
import dayjs from 'dayjs';
import { describe, expect, test } from 'vitest';
import { describe, expect, test, vi } from 'vitest';
vi.mock('emoji-mart', () => {
return {
Picker: vi.fn(),
};
});
import { suggestJournalDate } from '../suggest-journal-date';
+8
View File
@@ -5391,6 +5391,14 @@ export function useAFFiNEI18N(): {
* `Once enabled, all blocks will have created time, updated time, created by and updated by.`
*/
["com.affine.settings.workspace.experimental-features.enable-block-meta.description"](): string;
/**
* `Callout`
*/
["com.affine.settings.workspace.experimental-features.enable-callout.name"](): string;
/**
* `Let your words stand out.`
*/
["com.affine.settings.workspace.experimental-features.enable-callout.description"](): string;
/**
* `Emoji Folder Icon`
*/
@@ -1345,6 +1345,8 @@
"com.affine.settings.workspace.experimental-features.enable-mind-map-import.description": "Enables mind map import.",
"com.affine.settings.workspace.experimental-features.enable-block-meta.name": "Block Meta",
"com.affine.settings.workspace.experimental-features.enable-block-meta.description": "Once enabled, all blocks will have created time, updated time, created by and updated by.",
"com.affine.settings.workspace.experimental-features.enable-callout.name": "Callout",
"com.affine.settings.workspace.experimental-features.enable-callout.description": "Let your words stand out.",
"com.affine.settings.workspace.experimental-features.enable-emoji-folder-icon.name": "Emoji Folder Icon",
"com.affine.settings.workspace.experimental-features.enable-emoji-folder-icon.description": "Once enabled, you can use an emoji as the folder icon. When the first character of the folder name is an emoji, it will be extracted and used as its icon.",
"com.affine.settings.workspace.experimental-features.enable-emoji-doc-icon.name": "Emoji Doc Icon",
@@ -0,0 +1,43 @@
import { openHomePage } from '@affine-test/kit/utils/load-page';
import { type } from '@affine-test/kit/utils/page-logic';
import { expect, test } from '@playwright/test';
test('add callout block using slash menu and change emoji', async ({
page,
}) => {
await openHomePage(page);
await page.getByTestId('settings-modal-trigger').click();
await page.getByText('Experimental features').click();
await page.getByText('I am aware of the risks, and').click();
await page.getByTestId('experimental-confirm-button').click();
await page.getByTestId('enable_callout').locator('span').click();
await page.getByTestId('modal-close-button').click();
await page.getByTestId('sidebar-new-page-button').click();
await page.locator('affine-paragraph v-line div').click();
await type(page, '/callout\naaaa\nbbbb');
const callout = page.locator('affine-callout');
const emoji = page.locator('affine-callout .affine-callout-emoji');
await expect(callout).toBeVisible();
await expect(emoji).toContainText('😀');
const paragraph = page.locator('affine-callout affine-paragraph');
await expect(paragraph).toHaveCount(1);
const vLine = page.locator('affine-callout v-line');
await expect(vLine).toHaveCount(2);
expect(await vLine.nth(0).innerText()).toBe('aaaa');
expect(await vLine.nth(1).innerText()).toBe('bbbb');
await emoji.click();
const emojiMenu = page.locator('affine-emoji-menu');
await expect(emojiMenu).toBeVisible();
await page
.locator('div')
.filter({ hasText: /^😀😃😄😁😆😅🤣😂🙂$/ })
.getByLabel('😆')
.click();
await page.getByTestId('page-editor-blank').click();
await expect(emojiMenu).not.toBeVisible();
await expect(emoji).toContainText('😆');
});
+16
View File
@@ -42,6 +42,19 @@ export const PackageList = [
'blocksuite/framework/store',
],
},
{
location: 'blocksuite/affine/block-callout',
name: '@blocksuite/affine-block-callout',
workspaceDependencies: [
'blocksuite/affine/components',
'blocksuite/affine/model',
'blocksuite/affine/shared',
'blocksuite/framework/block-std',
'blocksuite/framework/global',
'blocksuite/framework/inline',
'blocksuite/framework/store',
],
},
{
location: 'blocksuite/affine/block-code',
name: '@blocksuite/affine-block-code',
@@ -361,6 +374,7 @@ export const PackageList = [
location: 'blocksuite/affine/widget-drag-handle',
name: '@blocksuite/affine-widget-drag-handle',
workspaceDependencies: [
'blocksuite/affine/block-callout',
'blocksuite/affine/block-list',
'blocksuite/affine/block-note',
'blocksuite/affine/block-paragraph',
@@ -426,6 +440,7 @@ export const PackageList = [
workspaceDependencies: [
'blocksuite/affine/block-attachment',
'blocksuite/affine/block-bookmark',
'blocksuite/affine/block-callout',
'blocksuite/affine/block-code',
'blocksuite/affine/block-data-view',
'blocksuite/affine/block-database',
@@ -816,6 +831,7 @@ export type PackageName =
| '@blocksuite/affine'
| '@blocksuite/affine-block-attachment'
| '@blocksuite/affine-block-bookmark'
| '@blocksuite/affine-block-callout'
| '@blocksuite/affine-block-code'
| '@blocksuite/affine-block-data-view'
| '@blocksuite/affine-block-database'
+1
View File
@@ -53,6 +53,7 @@
{ "path": "./blocksuite/affine/all" },
{ "path": "./blocksuite/affine/block-attachment" },
{ "path": "./blocksuite/affine/block-bookmark" },
{ "path": "./blocksuite/affine/block-callout" },
{ "path": "./blocksuite/affine/block-code" },
{ "path": "./blocksuite/affine/block-data-view" },
{ "path": "./blocksuite/affine/block-database" },
+41 -1
View File
@@ -2244,6 +2244,30 @@ __metadata:
languageName: unknown
linkType: soft
"@blocksuite/affine-block-callout@workspace:*, @blocksuite/affine-block-callout@workspace:blocksuite/affine/block-callout":
version: 0.0.0-use.local
resolution: "@blocksuite/affine-block-callout@workspace:blocksuite/affine/block-callout"
dependencies:
"@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-model": "workspace:*"
"@blocksuite/affine-shared": "workspace:*"
"@blocksuite/block-std": "workspace:*"
"@blocksuite/global": "workspace:*"
"@blocksuite/inline": "workspace:*"
"@blocksuite/store": "workspace:*"
"@emoji-mart/data": "npm:^1.2.1"
"@floating-ui/dom": "npm:^1.6.10"
"@lit/context": "npm:^1.1.2"
"@preact/signals-core": "npm:^1.8.0"
"@toeverything/theme": "npm:^1.1.12"
"@types/mdast": "npm:^4.0.4"
emoji-mart: "npm:^5.6.0"
lit: "npm:^3.2.0"
minimatch: "npm:^10.0.1"
zod: "npm:^3.23.8"
languageName: unknown
linkType: soft
"@blocksuite/affine-block-code@workspace:*, @blocksuite/affine-block-code@workspace:blocksuite/affine/block-code":
version: 0.0.0-use.local
resolution: "@blocksuite/affine-block-code@workspace:blocksuite/affine/block-code"
@@ -2811,6 +2835,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@blocksuite/affine-widget-drag-handle@workspace:blocksuite/affine/widget-drag-handle"
dependencies:
"@blocksuite/affine-block-callout": "workspace:*"
"@blocksuite/affine-block-list": "workspace:*"
"@blocksuite/affine-block-note": "workspace:*"
"@blocksuite/affine-block-paragraph": "workspace:*"
@@ -2944,6 +2969,7 @@ __metadata:
dependencies:
"@blocksuite/affine-block-attachment": "workspace:*"
"@blocksuite/affine-block-bookmark": "workspace:*"
"@blocksuite/affine-block-callout": "workspace:*"
"@blocksuite/affine-block-code": "workspace:*"
"@blocksuite/affine-block-data-view": "workspace:*"
"@blocksuite/affine-block-database": "workspace:*"
@@ -4223,6 +4249,13 @@ __metadata:
languageName: node
linkType: hard
"@emoji-mart/data@npm:^1.2.1":
version: 1.2.1
resolution: "@emoji-mart/data@npm:1.2.1"
checksum: 10/a9f50edaf354aadfede604fb26d80055a085e9160db2c924fd5e6afc27033cd5beb0006a9ee48240ce9c543e58e1bf1cf9ed83baba5db83a395154984b30bd91
languageName: node
linkType: hard
"@emotion/babel-plugin@npm:^11.13.5":
version: 11.13.5
resolution: "@emotion/babel-plugin@npm:11.13.5"
@@ -4864,7 +4897,7 @@ __metadata:
languageName: node
linkType: hard
"@floating-ui/dom@npm:^1.0.0, @floating-ui/dom@npm:^1.6.12, @floating-ui/dom@npm:^1.6.13":
"@floating-ui/dom@npm:^1.0.0, @floating-ui/dom@npm:^1.6.10, @floating-ui/dom@npm:^1.6.12, @floating-ui/dom@npm:^1.6.13":
version: 1.6.13
resolution: "@floating-ui/dom@npm:1.6.13"
dependencies:
@@ -19225,6 +19258,13 @@ __metadata:
languageName: node
linkType: hard
"emoji-mart@npm:^5.6.0":
version: 5.6.0
resolution: "emoji-mart@npm:5.6.0"
checksum: 10/fbbd6ce6fe6bc30020a7de4bfd6375f3c00da9880a147fdbee37303d985856e5463a48a61177166ad056a355e0bd589df9c0866bd54f9c065d60a9efa80639dd
languageName: node
linkType: hard
"emoji-regex-xs@npm:^1.0.0":
version: 1.0.0
resolution: "emoji-regex-xs@npm:1.0.0"