From 2ffd0e561cc5ad1ea0e4ac172d01e50c3c141eb6 Mon Sep 17 00:00:00 2001 From: Saul-Mirone Date: Thu, 26 Dec 2024 01:30:43 +0000 Subject: [PATCH] refactor(editor): extract note block (#9310) --- .../src/common/render-linked-doc.ts | 123 ++++++++++++++ blocksuite/affine/block-embed/src/index.ts | 2 +- blocksuite/affine/block-note/package.json | 43 +++++ .../block-note/src}/adapters/html.ts | 0 .../block-note/src}/adapters/index.ts | 0 .../block-note/src}/adapters/markdown.ts | 0 .../block-note/src}/adapters/plain-text.ts | 0 .../block-note/src}/commands/block-type.ts | 0 .../src}/commands/dedent-block-to-root.ts | 0 .../block-note/src}/commands/dedent-block.ts | 0 .../src}/commands/dedent-blocks-to-root.ts | 0 .../block-note/src}/commands/dedent-blocks.ts | 0 .../src}/commands/focus-block-end.ts | 0 .../src}/commands/focus-block-start.ts | 0 .../block-note/src}/commands/indent-block.ts | 0 .../block-note/src}/commands/indent-blocks.ts | 0 .../block-note/src}/commands/index.ts | 0 .../block-note/src}/commands/select-block.ts | 0 .../src}/commands/select-blocks-between.ts | 0 blocksuite/affine/block-note/src/effects.ts | 52 ++++++ blocksuite/affine/block-note/src/index.ts | 6 + .../block-note/src}/move-block.ts | 0 .../block-note/src}/note-block.ts | 0 .../block-note/src}/note-edgeless-block.ts | 18 +- .../block-note/src}/note-service.ts | 34 +++- .../block-note/src}/note-spec.ts | 0 .../affine/block-note/src/quick-action.ts | 62 +++++++ blocksuite/affine/block-note/tsconfig.json | 35 ++++ .../components/src/rich-text/conversion.ts} | 5 +- .../affine/components/src/rich-text/index.ts | 21 +-- blocksuite/blocks/package.json | 1 + .../_common/configs/quick-action/config.ts | 154 ------------------ .../src/_common/utils/render-linked-doc.ts | 133 +-------------- blocksuite/blocks/src/_specs/common.ts | 8 +- blocksuite/blocks/src/_specs/group/common.ts | 8 +- .../src/data-view-block/data-view-block.ts | 2 +- .../src/database-block/database-block.ts | 2 +- blocksuite/blocks/src/effects.ts | 34 +--- blocksuite/blocks/src/index.ts | 2 +- blocksuite/blocks/src/note-block/index.ts | 4 - .../root-block/keyboard/keyboard-manager.ts | 15 +- .../watchers/pointer-event-watcher.ts | 2 +- .../element-toolbar/more-menu/config.ts | 22 +-- .../format-bar/components/paragraph-button.ts | 2 +- .../root-block/widgets/format-bar/config.ts | 20 +-- .../root-block/widgets/slash-menu/config.ts | 2 +- .../root-block/widgets/slash-menu/utils.ts | 6 +- blocksuite/blocks/tsconfig.json | 3 + tools/utils/src/workspace.gen.ts | 16 ++ yarn.lock | 24 +++ 50 files changed, 467 insertions(+), 394 deletions(-) create mode 100644 blocksuite/affine/block-note/package.json rename blocksuite/{blocks/src/note-block => affine/block-note/src}/adapters/html.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/adapters/index.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/adapters/markdown.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/adapters/plain-text.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/block-type.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/dedent-block-to-root.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/dedent-block.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/dedent-blocks-to-root.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/dedent-blocks.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/focus-block-end.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/focus-block-start.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/indent-block.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/indent-blocks.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/index.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/select-block.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/commands/select-blocks-between.ts (100%) create mode 100644 blocksuite/affine/block-note/src/effects.ts create mode 100644 blocksuite/affine/block-note/src/index.ts rename blocksuite/{blocks/src/_common/configs => affine/block-note/src}/move-block.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/note-block.ts (100%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/note-edgeless-block.ts (97%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/note-service.ts (94%) rename blocksuite/{blocks/src/note-block => affine/block-note/src}/note-spec.ts (100%) create mode 100644 blocksuite/affine/block-note/src/quick-action.ts create mode 100644 blocksuite/affine/block-note/tsconfig.json rename blocksuite/{blocks/src/_common/configs/text-conversion.ts => affine/components/src/rich-text/conversion.ts} (98%) delete mode 100644 blocksuite/blocks/src/_common/configs/quick-action/config.ts delete mode 100644 blocksuite/blocks/src/note-block/index.ts diff --git a/blocksuite/affine/block-embed/src/common/render-linked-doc.ts b/blocksuite/affine/block-embed/src/common/render-linked-doc.ts index ac40477454..7b65858b51 100644 --- a/blocksuite/affine/block-embed/src/common/render-linked-doc.ts +++ b/blocksuite/affine/block-embed/src/common/render-linked-doc.ts @@ -5,14 +5,18 @@ import { NoteDisplayMode, } from '@blocksuite/affine-model'; import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts'; +import { NotificationProvider } from '@blocksuite/affine-shared/services'; import { matchFlavours, SpecProvider } from '@blocksuite/affine-shared/utils'; import { BlockStdScope } from '@blocksuite/block-std'; import { assertExists } from '@blocksuite/global/utils'; import { type BlockModel, + type BlockSnapshot, BlockViewType, type Doc, + type DraftModel, type Query, + Slice, } from '@blocksuite/store'; import { render, type TemplateResult } from 'lit'; @@ -295,3 +299,122 @@ export function getDocContentWithMaxLength(doc: Doc, maxlength = 500) { return texts.join('\n'); } + +export function getTitleFromSelectedModels(selectedModels: DraftModel[]) { + const firstBlock = selectedModels[0]; + if ( + matchFlavours(firstBlock, ['affine:paragraph']) && + firstBlock.type.startsWith('h') + ) { + return firstBlock.text.toString(); + } + return undefined; +} + +export function promptDocTitle(std: BlockStdScope, autofill?: string) { + const notification = std.getOptional(NotificationProvider); + if (!notification) return Promise.resolve(undefined); + + return notification.prompt({ + title: 'Create linked doc', + message: 'Enter a title for the new doc.', + placeholder: 'Untitled', + autofill, + confirmText: 'Confirm', + cancelText: 'Cancel', + }); +} + +export function notifyDocCreated(std: BlockStdScope, doc: Doc) { + const notification = std.getOptional(NotificationProvider); + if (!notification) return; + + const abortController = new AbortController(); + const clear = () => { + doc.history.off('stack-item-added', addHandler); + doc.history.off('stack-item-popped', popHandler); + disposable.dispose(); + }; + const closeNotify = () => { + abortController.abort(); + clear(); + }; + + // edit or undo or switch doc, close notify toast + const addHandler = doc.history.on('stack-item-added', closeNotify); + const popHandler = doc.history.on('stack-item-popped', closeNotify); + const disposable = std.host.slots.unmounted.on(closeNotify); + + notification.notify({ + title: 'Linked doc created', + message: 'You can click undo to recovery block content', + accent: 'info', + duration: 10 * 1000, + action: { + label: 'Undo', + onClick: () => { + doc.undo(); + clear(); + }, + }, + abort: abortController.signal, + onClose: clear, + }); +} + +export async function convertSelectedBlocksToLinkedDoc( + std: BlockStdScope, + doc: Doc, + selectedModels: DraftModel[] | Promise, + docTitle?: string +) { + const models = await selectedModels; + const slice = std.clipboard.sliceToSnapshot(Slice.fromModels(doc, models)); + if (!slice) { + return; + } + const firstBlock = models[0]; + if (!firstBlock) { + return; + } + // if title undefined, use the first heading block content as doc title + const title = docTitle || getTitleFromSelectedModels(models); + const linkedDoc = createLinkedDocFromSlice(std, doc, slice.content, title); + // insert linked doc card + doc.addSiblingBlocks( + doc.getBlock(firstBlock.id)!.model, + [ + { + flavour: 'affine:embed-linked-doc', + pageId: linkedDoc.id, + }, + ], + 'before' + ); + // delete selected elements + models.forEach(model => doc.deleteBlock(model)); + return linkedDoc; +} + +export function createLinkedDocFromSlice( + std: BlockStdScope, + doc: Doc, + snapshots: BlockSnapshot[], + docTitle?: string +) { + // const modelsWithChildren = (list:BlockModel[]):BlockModel[]=>list.flatMap(model=>[model,...modelsWithChildren(model.children)]) + const linkedDoc = doc.collection.createDoc({}); + linkedDoc.load(() => { + const rootId = linkedDoc.addBlock('affine:page', { + title: new doc.Text(docTitle), + }); + linkedDoc.addBlock('affine:surface', {}, rootId); + const noteId = linkedDoc.addBlock('affine:note', {}, rootId); + snapshots.forEach(snapshot => { + std.clipboard + .pasteBlockSnapshot(snapshot, linkedDoc, noteId) + .catch(console.error); + }); + }); + return linkedDoc; +} diff --git a/blocksuite/affine/block-embed/src/index.ts b/blocksuite/affine/block-embed/src/index.ts index 51b36c7df7..b1131184bd 100644 --- a/blocksuite/affine/block-embed/src/index.ts +++ b/blocksuite/affine/block-embed/src/index.ts @@ -28,7 +28,7 @@ export { LinkPreviewer, type LinkPreviewResponseData, } from './common/link-previewer.js'; -export { getDocContentWithMaxLength } from './common/render-linked-doc'; +export * from './common/render-linked-doc'; export { toEdgelessEmbedBlock } from './common/to-edgeless-embed-block'; export * from './common/utils'; export * from './embed-figma-block'; diff --git a/blocksuite/affine/block-note/package.json b/blocksuite/affine/block-note/package.json new file mode 100644 index 0000000000..492e515999 --- /dev/null +++ b/blocksuite/affine/block-note/package.json @@ -0,0 +1,43 @@ +{ + "name": "@blocksuite/affine-block-note", + "description": "Note 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-block-embed": "workspace:*", + "@blocksuite/affine-components": "workspace:*", + "@blocksuite/affine-model": "workspace:*", + "@blocksuite/affine-shared": "workspace:*", + "@blocksuite/block-std": "workspace:*", + "@blocksuite/global": "workspace:*", + "@blocksuite/icons": "^2.1.75", + "@blocksuite/inline": "workspace:*", + "@blocksuite/store": "workspace:*", + "@floating-ui/dom": "^1.6.10", + "@lit/context": "^1.1.2", + "@preact/signals-core": "^1.8.0", + "@toeverything/theme": "^1.1.1", + "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__" + ] +} diff --git a/blocksuite/blocks/src/note-block/adapters/html.ts b/blocksuite/affine/block-note/src/adapters/html.ts similarity index 100% rename from blocksuite/blocks/src/note-block/adapters/html.ts rename to blocksuite/affine/block-note/src/adapters/html.ts diff --git a/blocksuite/blocks/src/note-block/adapters/index.ts b/blocksuite/affine/block-note/src/adapters/index.ts similarity index 100% rename from blocksuite/blocks/src/note-block/adapters/index.ts rename to blocksuite/affine/block-note/src/adapters/index.ts diff --git a/blocksuite/blocks/src/note-block/adapters/markdown.ts b/blocksuite/affine/block-note/src/adapters/markdown.ts similarity index 100% rename from blocksuite/blocks/src/note-block/adapters/markdown.ts rename to blocksuite/affine/block-note/src/adapters/markdown.ts diff --git a/blocksuite/blocks/src/note-block/adapters/plain-text.ts b/blocksuite/affine/block-note/src/adapters/plain-text.ts similarity index 100% rename from blocksuite/blocks/src/note-block/adapters/plain-text.ts rename to blocksuite/affine/block-note/src/adapters/plain-text.ts diff --git a/blocksuite/blocks/src/note-block/commands/block-type.ts b/blocksuite/affine/block-note/src/commands/block-type.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/block-type.ts rename to blocksuite/affine/block-note/src/commands/block-type.ts diff --git a/blocksuite/blocks/src/note-block/commands/dedent-block-to-root.ts b/blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/dedent-block-to-root.ts rename to blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts diff --git a/blocksuite/blocks/src/note-block/commands/dedent-block.ts b/blocksuite/affine/block-note/src/commands/dedent-block.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/dedent-block.ts rename to blocksuite/affine/block-note/src/commands/dedent-block.ts diff --git a/blocksuite/blocks/src/note-block/commands/dedent-blocks-to-root.ts b/blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/dedent-blocks-to-root.ts rename to blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts diff --git a/blocksuite/blocks/src/note-block/commands/dedent-blocks.ts b/blocksuite/affine/block-note/src/commands/dedent-blocks.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/dedent-blocks.ts rename to blocksuite/affine/block-note/src/commands/dedent-blocks.ts diff --git a/blocksuite/blocks/src/note-block/commands/focus-block-end.ts b/blocksuite/affine/block-note/src/commands/focus-block-end.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/focus-block-end.ts rename to blocksuite/affine/block-note/src/commands/focus-block-end.ts diff --git a/blocksuite/blocks/src/note-block/commands/focus-block-start.ts b/blocksuite/affine/block-note/src/commands/focus-block-start.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/focus-block-start.ts rename to blocksuite/affine/block-note/src/commands/focus-block-start.ts diff --git a/blocksuite/blocks/src/note-block/commands/indent-block.ts b/blocksuite/affine/block-note/src/commands/indent-block.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/indent-block.ts rename to blocksuite/affine/block-note/src/commands/indent-block.ts diff --git a/blocksuite/blocks/src/note-block/commands/indent-blocks.ts b/blocksuite/affine/block-note/src/commands/indent-blocks.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/indent-blocks.ts rename to blocksuite/affine/block-note/src/commands/indent-blocks.ts diff --git a/blocksuite/blocks/src/note-block/commands/index.ts b/blocksuite/affine/block-note/src/commands/index.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/index.ts rename to blocksuite/affine/block-note/src/commands/index.ts diff --git a/blocksuite/blocks/src/note-block/commands/select-block.ts b/blocksuite/affine/block-note/src/commands/select-block.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/select-block.ts rename to blocksuite/affine/block-note/src/commands/select-block.ts diff --git a/blocksuite/blocks/src/note-block/commands/select-blocks-between.ts b/blocksuite/affine/block-note/src/commands/select-blocks-between.ts similarity index 100% rename from blocksuite/blocks/src/note-block/commands/select-blocks-between.ts rename to blocksuite/affine/block-note/src/commands/select-blocks-between.ts diff --git a/blocksuite/affine/block-note/src/effects.ts b/blocksuite/affine/block-note/src/effects.ts new file mode 100644 index 0000000000..64e73cf3a4 --- /dev/null +++ b/blocksuite/affine/block-note/src/effects.ts @@ -0,0 +1,52 @@ +import type { BlockComponent } from '@blocksuite/block-std'; +import type { BlockModel } from '@blocksuite/store'; + +import type { updateBlockType } from './commands/block-type'; +import type { dedentBlock } from './commands/dedent-block'; +import type { dedentBlockToRoot } from './commands/dedent-block-to-root'; +import type { dedentBlocks } from './commands/dedent-blocks'; +import type { dedentBlocksToRoot } from './commands/dedent-blocks-to-root'; +import type { focusBlockEnd } from './commands/focus-block-end'; +import type { focusBlockStart } from './commands/focus-block-start'; +import type { indentBlock } from './commands/indent-block'; +import type { indentBlocks } from './commands/indent-blocks'; +import type { selectBlock } from './commands/select-block'; +import type { selectBlocksBetween } from './commands/select-blocks-between'; +import { NoteBlockComponent } from './note-block'; +import { + EdgelessNoteBlockComponent, + EdgelessNoteMask, +} from './note-edgeless-block'; +import type { NoteBlockService } from './note-service'; + +export function effects() { + customElements.define('affine-note', NoteBlockComponent); + customElements.define('edgeless-note-mask', EdgelessNoteMask); + customElements.define('affine-edgeless-note', EdgelessNoteBlockComponent); +} + +declare global { + namespace BlockSuite { + interface Commands { + selectBlock: typeof selectBlock; + selectBlocksBetween: typeof selectBlocksBetween; + focusBlockStart: typeof focusBlockStart; + focusBlockEnd: typeof focusBlockEnd; + indentBlocks: typeof indentBlocks; + dedentBlock: typeof dedentBlock; + dedentBlocksToRoot: typeof dedentBlocksToRoot; + dedentBlocks: typeof dedentBlocks; + indentBlock: typeof indentBlock; + updateBlockType: typeof updateBlockType; + dedentBlockToRoot: typeof dedentBlockToRoot; + } + interface CommandContext { + focusBlock?: BlockComponent | null; + anchorBlock?: BlockComponent | null; + updatedBlocks?: BlockModel[]; + } + interface BlockServices { + 'affine:note': NoteBlockService; + } + } +} diff --git a/blocksuite/affine/block-note/src/index.ts b/blocksuite/affine/block-note/src/index.ts new file mode 100644 index 0000000000..60d4bdd7ea --- /dev/null +++ b/blocksuite/affine/block-note/src/index.ts @@ -0,0 +1,6 @@ +export * from './adapters'; +export * from './commands'; +export * from './note-block'; +export * from './note-edgeless-block'; +export * from './note-service'; +export * from './note-spec'; diff --git a/blocksuite/blocks/src/_common/configs/move-block.ts b/blocksuite/affine/block-note/src/move-block.ts similarity index 100% rename from blocksuite/blocks/src/_common/configs/move-block.ts rename to blocksuite/affine/block-note/src/move-block.ts diff --git a/blocksuite/blocks/src/note-block/note-block.ts b/blocksuite/affine/block-note/src/note-block.ts similarity index 100% rename from blocksuite/blocks/src/note-block/note-block.ts rename to blocksuite/affine/block-note/src/note-block.ts diff --git a/blocksuite/blocks/src/note-block/note-edgeless-block.ts b/blocksuite/affine/block-note/src/note-edgeless-block.ts similarity index 97% rename from blocksuite/blocks/src/note-block/note-edgeless-block.ts rename to blocksuite/affine/block-note/src/note-edgeless-block.ts index 1250ffa41f..6f69d7ac40 100644 --- a/blocksuite/blocks/src/note-block/note-edgeless-block.ts +++ b/blocksuite/affine/block-note/src/note-edgeless-block.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { MoreIndicatorIcon } from '@blocksuite/affine-components/icons'; import type { NoteBlockModel } from '@blocksuite/affine-model'; import { @@ -14,7 +13,11 @@ import { matchFlavours, stopPropagation, } from '@blocksuite/affine-shared/utils'; -import type { BlockComponent, EditorHost } from '@blocksuite/block-std'; +import type { + BlockComponent, + BlockService, + EditorHost, +} from '@blocksuite/block-std'; import { ShadowlessElement, toGfxBlockComponent } from '@blocksuite/block-std'; import { almostEqual, @@ -23,13 +26,12 @@ import { Point, WithDisposable, } from '@blocksuite/global/utils'; -import type { BlockModel } from '@blocksuite/store'; +import type { BlockModel, Slot } from '@blocksuite/store'; import { css, html, nothing } from 'lit'; import { property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { styleMap } from 'lit/directives/style-map.js'; -import type { EdgelessRootService } from '../root-block/index.js'; import { NoteBlockComponent } from './note-block.js'; export class EdgelessNoteMask extends WithDisposable(ShadowlessElement) { @@ -151,7 +153,9 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent( } get rootService() { - return this.std.getService('affine:page') as EdgelessRootService; + return this.std.getService('affine:page') as BlockService & { + slots: Record; + }; } private _collapsedContent() { @@ -312,7 +316,7 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent( override connectedCallback(): void { super.connectedCallback(); - const selection = this.rootService.selection; + const selection = this.gfx.selection; this._editing = selection.has(this.model.id) && selection.editing; this._disposables.add( @@ -328,7 +332,7 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent( override firstUpdated() { const { _disposables } = this; - const selection = this.rootService.selection; + const selection = this.gfx.selection; _disposables.add( this.rootService.slots.elementResizeStart.on(() => { diff --git a/blocksuite/blocks/src/note-block/note-service.ts b/blocksuite/affine/block-note/src/note-service.ts similarity index 94% rename from blocksuite/blocks/src/note-block/note-service.ts rename to blocksuite/affine/block-note/src/note-service.ts index 686a71e939..4d0ab19665 100644 --- a/blocksuite/blocks/src/note-block/note-service.ts +++ b/blocksuite/affine/block-note/src/note-service.ts @@ -1,3 +1,4 @@ +import { textConversionConfigs } from '@blocksuite/affine-components/rich-text'; import { NoteBlockSchema } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { @@ -5,14 +6,14 @@ import { type BlockComponent, type BlockSelection, BlockService, + type BlockStdScope, type UIEventHandler, type UIEventStateContext, } from '@blocksuite/block-std'; +import type { BlockModel } from '@blocksuite/store'; -import { moveBlockConfigs } from '../_common/configs/move-block.js'; -import { quickActionConfig } from '../_common/configs/quick-action/config.js'; -import { textConversionConfigs } from '../_common/configs/text-conversion.js'; -import { onModelElementUpdated } from '../root-block/utils/callback.js'; +import { moveBlockConfigs } from './move-block'; +import { quickActionConfig } from './quick-action'; export class NoteBlockService extends BlockService { static override readonly flavour = NoteBlockSchema.model.flavour; @@ -51,10 +52,10 @@ export class NoteBlockService extends BlockService { return { ...acc, [config.hotkey!]: ctx => { - if (!config.showWhen(this.std.host)) return; + if (!config.showWhen(this.std)) return; ctx.get('defaultState').event.preventDefault(); - config.action(this.std.host); + config.action(this.std); }, }; }, @@ -83,8 +84,7 @@ export class NoteBlockService extends BlockService { }) .inline((ctx, next) => { const newModels = ctx.updatedBlocks; - const host = ctx.std.host; - if (!host || !newModels) { + if (!newModels) { return; } @@ -93,7 +93,7 @@ export class NoteBlockService extends BlockService { } const [codeModel] = newModels; - onModelElementUpdated(host, codeModel, codeElement => { + onModelElementUpdated(ctx.std, codeModel, codeElement => { this._std.selection.setGroup('note', [ this._std.selection.create('text', { from: { @@ -584,3 +584,19 @@ export class NoteBlockService extends BlockService { }); } } + +async function onModelElementUpdated( + std: BlockStdScope, + model: BlockModel, + callback: (block: BlockComponent) => void +) { + const page = model.doc; + if (!page.root) return; + + const rootComponent = std.view.getBlock(page.root.id); + if (!rootComponent) return; + await rootComponent.updateComplete; + + const element = std.view.getBlock(model.id); + if (element) callback(element); +} diff --git a/blocksuite/blocks/src/note-block/note-spec.ts b/blocksuite/affine/block-note/src/note-spec.ts similarity index 100% rename from blocksuite/blocks/src/note-block/note-spec.ts rename to blocksuite/affine/block-note/src/note-spec.ts diff --git a/blocksuite/affine/block-note/src/quick-action.ts b/blocksuite/affine/block-note/src/quick-action.ts new file mode 100644 index 0000000000..68e129af16 --- /dev/null +++ b/blocksuite/affine/block-note/src/quick-action.ts @@ -0,0 +1,62 @@ +import { + convertSelectedBlocksToLinkedDoc, + getTitleFromSelectedModels, + notifyDocCreated, + promptDocTitle, +} from '@blocksuite/affine-block-embed'; +import type { BlockStdScope } from '@blocksuite/block-std'; + +export interface QuickActionConfig { + id: string; + hotkey?: string; + showWhen: (std: BlockStdScope) => boolean; + action: (std: BlockStdScope) => void; +} + +export const quickActionConfig: QuickActionConfig[] = [ + { + id: 'convert-to-linked-doc', + hotkey: `Mod-Shift-l`, + showWhen: std => { + const [_, ctx] = std.command + .chain() + .getSelectedModels({ + types: ['block'], + }) + .run(); + const { selectedModels } = ctx; + return !!selectedModels && selectedModels.length > 0; + }, + action: std => { + const [_, ctx] = std.command + .chain() + .getSelectedModels({ + types: ['block'], + mode: 'highest', + }) + .draftSelectedModels() + .run(); + const { selectedModels, draftedModels } = ctx; + if (!selectedModels) return; + + if (!selectedModels.length || !draftedModels) return; + + std.selection.clear(); + + const doc = std.doc; + const autofill = getTitleFromSelectedModels(selectedModels); + promptDocTitle(std, autofill) + .then(title => { + if (title === null) return; + convertSelectedBlocksToLinkedDoc( + std, + doc, + draftedModels, + title + ).catch(console.error); + notifyDocCreated(std, doc); + }) + .catch(console.error); + }, + }, +]; diff --git a/blocksuite/affine/block-note/tsconfig.json b/blocksuite/affine/block-note/tsconfig.json new file mode 100644 index 0000000000..54b5d3348b --- /dev/null +++ b/blocksuite/affine/block-note/tsconfig.json @@ -0,0 +1,35 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src/", + "outDir": "./dist/", + "noEmit": false + }, + "include": ["./src"], + "references": [ + { + "path": "../../framework/global" + }, + { + "path": "../../framework/store" + }, + { + "path": "../../framework/block-std" + }, + { + "path": "../../framework/inline" + }, + { + "path": "../model" + }, + { + "path": "../components" + }, + { + "path": "../shared" + }, + { + "path": "../block-embed" + } + ] +} diff --git a/blocksuite/blocks/src/_common/configs/text-conversion.ts b/blocksuite/affine/components/src/rich-text/conversion.ts similarity index 98% rename from blocksuite/blocks/src/_common/configs/text-conversion.ts rename to blocksuite/affine/components/src/rich-text/conversion.ts index d9890a0331..bde5f01c91 100644 --- a/blocksuite/blocks/src/_common/configs/text-conversion.ts +++ b/blocksuite/affine/components/src/rich-text/conversion.ts @@ -1,3 +1,5 @@ +import type { TemplateResult } from 'lit'; + import { BulletedListIcon, CheckBoxIcon, @@ -12,8 +14,7 @@ import { NumberedListIcon, QuoteIcon, TextIcon, -} from '@blocksuite/affine-components/icons'; -import type { TemplateResult } from 'lit'; +} from '../icons'; /** * Text primitive entries used in slash menu and format bar, diff --git a/blocksuite/affine/components/src/rich-text/index.ts b/blocksuite/affine/components/src/rich-text/index.ts index 19577e8709..124dbd399d 100644 --- a/blocksuite/affine/components/src/rich-text/index.ts +++ b/blocksuite/affine/components/src/rich-text/index.ts @@ -1,4 +1,5 @@ -export * from './all-extensions.js'; +export * from './all-extensions'; +export { type TextConversionConfig, textConversionConfigs } from './conversion'; export { asyncGetRichText, asyncSetInlineRange, @@ -7,9 +8,9 @@ export { getRichTextByModel, onModelTextUpdated, selectTextModel, -} from './dom.js'; -export * from './effects.js'; -export * from './extension/index.js'; +} from './dom'; +export * from './effects'; +export * from './extension'; export { clearMarksOnDiscontinuousInput, FORMAT_BLOCK_SUPPORT_FLAVOURS, @@ -20,9 +21,9 @@ export { textCommands, type TextFormatConfig, textFormatConfigs, -} from './format/index.js'; -export * from './inline/index.js'; -export { textKeymap } from './keymap/index.js'; -export { insertLinkedNode } from './linked-node.js'; -export { markdownInput } from './markdown/index.js'; -export { RichText } from './rich-text.js'; +} from './format'; +export * from './inline'; +export { textKeymap } from './keymap'; +export { insertLinkedNode } from './linked-node'; +export { markdownInput } from './markdown'; +export { RichText } from './rich-text'; diff --git a/blocksuite/blocks/package.json b/blocksuite/blocks/package.json index bca3ad81da..e8ad30a023 100644 --- a/blocksuite/blocks/package.json +++ b/blocksuite/blocks/package.json @@ -19,6 +19,7 @@ "@blocksuite/affine-block-embed": "workspace:*", "@blocksuite/affine-block-image": "workspace:*", "@blocksuite/affine-block-list": "workspace:*", + "@blocksuite/affine-block-note": "workspace:*", "@blocksuite/affine-block-paragraph": "workspace:*", "@blocksuite/affine-block-surface": "workspace:*", "@blocksuite/affine-components": "workspace:*", diff --git a/blocksuite/blocks/src/_common/configs/quick-action/config.ts b/blocksuite/blocks/src/_common/configs/quick-action/config.ts deleted file mode 100644 index dacc634143..0000000000 --- a/blocksuite/blocks/src/_common/configs/quick-action/config.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { - CopyIcon, - DatabaseTableViewIcon20, - LinkedDocIcon, -} from '@blocksuite/affine-components/icons'; -import { toast } from '@blocksuite/affine-components/toast'; -import { matchFlavours } from '@blocksuite/affine-shared/utils'; -import type { EditorHost } from '@blocksuite/block-std'; -import { tableViewMeta } from '@blocksuite/data-view/view-presets'; -import { assertExists } from '@blocksuite/global/utils'; -import type { TemplateResult } from 'lit'; - -import { convertToDatabase } from '../../../database-block/data-source.js'; -import { DATABASE_CONVERT_WHITE_LIST } from '../../../database-block/utils/block-utils.js'; -import { - convertSelectedBlocksToLinkedDoc, - getTitleFromSelectedModels, - notifyDocCreated, - promptDocTitle, -} from '../../utils/render-linked-doc.js'; - -export interface QuickActionConfig { - id: string; - name: string; - disabledToolTip?: string; - icon: TemplateResult<1>; - hotkey?: string; - showWhen: (host: EditorHost) => boolean; - enabledWhen: (host: EditorHost) => boolean; - action: (host: EditorHost) => void; -} - -export const quickActionConfig: QuickActionConfig[] = [ - { - id: 'copy', - name: 'Copy', - disabledToolTip: undefined, - icon: CopyIcon, - hotkey: undefined, - showWhen: () => true, - enabledWhen: () => true, - action: host => { - host.std.command - .chain() - .getSelectedModels() - .with({ - onCopy: () => { - toast(host, 'Copied to clipboard'); - }, - }) - .draftSelectedModels() - .copySelectedModels() - .run(); - }, - }, - { - id: 'convert-to-database', - name: 'Group as Table', - disabledToolTip: - 'Contains Block types that cannot be converted to Database', - icon: DatabaseTableViewIcon20, - showWhen: host => { - const [_, ctx] = host.std.command - .chain() - .getSelectedModels({ - types: ['block', 'text'], - }) - .run(); - const { selectedModels } = ctx; - if (!selectedModels || selectedModels.length === 0) return false; - - const firstBlock = selectedModels[0]; - assertExists(firstBlock); - if (matchFlavours(firstBlock, ['affine:database'])) { - return false; - } - - return true; - }, - enabledWhen: host => { - const [_, ctx] = host.std.command - .chain() - .getSelectedModels({ - types: ['block', 'text'], - }) - .run(); - const { selectedModels } = ctx; - if (!selectedModels || selectedModels.length === 0) return false; - - return selectedModels.every(block => - DATABASE_CONVERT_WHITE_LIST.includes(block.flavour) - ); - }, - action: host => { - convertToDatabase(host, tableViewMeta.type); - }, - }, - { - id: 'convert-to-linked-doc', - name: 'Create Linked Doc', - icon: LinkedDocIcon, - hotkey: `Mod-Shift-l`, - showWhen: host => { - const [_, ctx] = host.std.command - .chain() - .getSelectedModels({ - types: ['block'], - }) - .run(); - const { selectedModels } = ctx; - return !!selectedModels && selectedModels.length > 0; - }, - enabledWhen: host => { - const [_, ctx] = host.std.command - .chain() - .getSelectedModels({ - types: ['block'], - }) - .run(); - const { selectedModels } = ctx; - return !!selectedModels && selectedModels.length > 0; - }, - action: host => { - const [_, ctx] = host.std.command - .chain() - .getSelectedModels({ - types: ['block'], - mode: 'highest', - }) - .draftSelectedModels() - .run(); - const { selectedModels, draftedModels } = ctx; - assertExists(selectedModels); - if (!selectedModels.length || !draftedModels) return; - - host.selection.clear(); - - const doc = host.doc; - const autofill = getTitleFromSelectedModels(selectedModels); - promptDocTitle(host, autofill) - .then(title => { - if (title === null) return; - convertSelectedBlocksToLinkedDoc( - host.std, - doc, - draftedModels, - title - ).catch(console.error); - notifyDocCreated(host, doc); - }) - .catch(console.error); - }, - }, -]; diff --git a/blocksuite/blocks/src/_common/utils/render-linked-doc.ts b/blocksuite/blocks/src/_common/utils/render-linked-doc.ts index 5e5c6f9878..7a323840d9 100644 --- a/blocksuite/blocks/src/_common/utils/render-linked-doc.ts +++ b/blocksuite/blocks/src/_common/utils/render-linked-doc.ts @@ -1,19 +1,9 @@ import type { FrameBlockModel, NoteBlockModel } from '@blocksuite/affine-model'; import { NoteDisplayMode } from '@blocksuite/affine-model'; -import { - DocModeProvider, - NotificationProvider, -} from '@blocksuite/affine-shared/services'; -import { getBlockProps, matchFlavours } from '@blocksuite/affine-shared/utils'; +import { DocModeProvider } from '@blocksuite/affine-shared/services'; +import { getBlockProps } from '@blocksuite/affine-shared/utils'; import type { EditorHost } from '@blocksuite/block-std'; -import { assertExists } from '@blocksuite/global/utils'; -import { - type BlockModel, - type BlockSnapshot, - type Doc, - type DraftModel, - Slice, -} from '@blocksuite/store'; +import { type BlockModel, type Doc } from '@blocksuite/store'; import { GfxBlockModel } from '../../root-block/edgeless/block-model.js'; import { @@ -27,68 +17,6 @@ import { } from '../../root-block/edgeless/utils/query.js'; import { getSurfaceBlock } from '../../surface-ref-block/utils.js'; -export function promptDocTitle(host: EditorHost, autofill?: string) { - const notification = host.std.getOptional(NotificationProvider); - if (!notification) return Promise.resolve(undefined); - - return notification.prompt({ - title: 'Create linked doc', - message: 'Enter a title for the new doc.', - placeholder: 'Untitled', - autofill, - confirmText: 'Confirm', - cancelText: 'Cancel', - }); -} - -export function getTitleFromSelectedModels(selectedModels: DraftModel[]) { - const firstBlock = selectedModels[0]; - if ( - matchFlavours(firstBlock, ['affine:paragraph']) && - firstBlock.type.startsWith('h') - ) { - return firstBlock.text.toString(); - } - return undefined; -} - -export function notifyDocCreated(host: EditorHost, doc: Doc) { - const notification = host.std.getOptional(NotificationProvider); - if (!notification) return; - - const abortController = new AbortController(); - const clear = () => { - doc.history.off('stack-item-added', addHandler); - doc.history.off('stack-item-popped', popHandler); - disposable.dispose(); - }; - const closeNotify = () => { - abortController.abort(); - clear(); - }; - - // edit or undo or switch doc, close notify toast - const addHandler = doc.history.on('stack-item-added', closeNotify); - const popHandler = doc.history.on('stack-item-popped', closeNotify); - const disposable = host.slots.unmounted.on(closeNotify); - - notification.notify({ - title: 'Linked doc created', - message: 'You can click undo to recovery block content', - accent: 'info', - duration: 10 * 1000, - action: { - label: 'Undo', - onClick: () => { - doc.undo(); - clear(); - }, - }, - abort: abortController.signal, - onClose: clear, - }); -} - export function addBlocksToDoc( targetDoc: Doc, model: BlockModel, @@ -110,61 +38,6 @@ export function addBlocksToDoc( } } -export async function convertSelectedBlocksToLinkedDoc( - std: BlockSuite.Std, - doc: Doc, - selectedModels: DraftModel[] | Promise, - docTitle?: string -) { - const models = await selectedModels; - const slice = std.clipboard.sliceToSnapshot(Slice.fromModels(doc, models)); - if (!slice) { - return; - } - const firstBlock = models[0]; - assertExists(firstBlock); - // if title undefined, use the first heading block content as doc title - const title = docTitle || getTitleFromSelectedModels(models); - const linkedDoc = createLinkedDocFromSlice(std, doc, slice.content, title); - // insert linked doc card - doc.addSiblingBlocks( - doc.getBlock(firstBlock.id)!.model, - [ - { - flavour: 'affine:embed-linked-doc', - pageId: linkedDoc.id, - }, - ], - 'before' - ); - // delete selected elements - models.forEach(model => doc.deleteBlock(model)); - return linkedDoc; -} - -export function createLinkedDocFromSlice( - std: BlockSuite.Std, - doc: Doc, - snapshots: BlockSnapshot[], - docTitle?: string -) { - // const modelsWithChildren = (list:BlockModel[]):BlockModel[]=>list.flatMap(model=>[model,...modelsWithChildren(model.children)]) - const linkedDoc = doc.collection.createDoc({}); - linkedDoc.load(() => { - const rootId = linkedDoc.addBlock('affine:page', { - title: new doc.Text(docTitle), - }); - linkedDoc.addBlock('affine:surface', {}, rootId); - const noteId = linkedDoc.addBlock('affine:note', {}, rootId); - snapshots.forEach(snapshot => { - std.clipboard - .pasteBlockSnapshot(snapshot, linkedDoc, noteId) - .catch(console.error); - }); - }); - return linkedDoc; -} - export function createLinkedDocFromNote( doc: Doc, note: NoteBlockModel, diff --git a/blocksuite/blocks/src/_specs/common.ts b/blocksuite/blocks/src/_specs/common.ts index 2ee23b5611..e3e06013dc 100644 --- a/blocksuite/blocks/src/_specs/common.ts +++ b/blocksuite/blocks/src/_specs/common.ts @@ -3,6 +3,10 @@ import { BookmarkBlockSpec } from '@blocksuite/affine-block-bookmark'; import { EmbedExtensions } from '@blocksuite/affine-block-embed'; import { ImageBlockSpec } from '@blocksuite/affine-block-image'; import { ListBlockSpec } from '@blocksuite/affine-block-list'; +import { + EdgelessNoteBlockSpec, + NoteBlockSpec, +} from '@blocksuite/affine-block-note'; import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph'; import { RichTextExtensions } from '@blocksuite/affine-components/rich-text'; import { EditPropsStore } from '@blocksuite/affine-shared/services'; @@ -13,10 +17,6 @@ import { CodeBlockSpec } from '../code-block/code-block-spec.js'; import { DataViewBlockSpec } from '../data-view-block/data-view-spec.js'; import { DatabaseBlockSpec } from '../database-block/database-spec.js'; import { DividerBlockSpec } from '../divider-block/divider-spec.js'; -import { - EdgelessNoteBlockSpec, - NoteBlockSpec, -} from '../note-block/note-spec.js'; export const CommonFirstPartyBlockSpecs: ExtensionType[] = [ RichTextExtensions, diff --git a/blocksuite/blocks/src/_specs/group/common.ts b/blocksuite/blocks/src/_specs/group/common.ts index f4411b15de..29b0608caa 100644 --- a/blocksuite/blocks/src/_specs/group/common.ts +++ b/blocksuite/blocks/src/_specs/group/common.ts @@ -11,16 +11,16 @@ import { } from '@blocksuite/affine-block-embed'; import { ImageBlockSpec } from '@blocksuite/affine-block-image'; import { ListBlockSpec } from '@blocksuite/affine-block-list'; +import { + EdgelessNoteBlockSpec, + NoteBlockSpec, +} from '@blocksuite/affine-block-note'; import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph'; import { CodeBlockSpec } from '../../code-block/code-block-spec.js'; import { DataViewBlockSpec } from '../../data-view-block/data-view-spec.js'; import { DatabaseBlockSpec } from '../../database-block/database-spec.js'; import { DividerBlockSpec } from '../../divider-block/divider-spec.js'; -import { - EdgelessNoteBlockSpec, - NoteBlockSpec, -} from '../../note-block/note-spec.js'; export { AttachmentBlockSpec, diff --git a/blocksuite/blocks/src/data-view-block/data-view-block.ts b/blocksuite/blocks/src/data-view-block/data-view-block.ts index 6c14a174cc..1d8f9d86db 100644 --- a/blocksuite/blocks/src/data-view-block/data-view-block.ts +++ b/blocksuite/blocks/src/data-view-block/data-view-block.ts @@ -1,3 +1,4 @@ +import type { NoteBlockComponent } from '@blocksuite/affine-block-note'; import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption'; import { menu, @@ -40,7 +41,6 @@ import { html } from 'lit/static-html.js'; import { BlockRenderer } from '../database-block/detail-panel/block-renderer.js'; import { NoteRenderer } from '../database-block/detail-panel/note-renderer.js'; -import type { NoteBlockComponent } from '../note-block/index.js'; import { EdgelessRootBlockComponent, type RootService, diff --git a/blocksuite/blocks/src/database-block/database-block.ts b/blocksuite/blocks/src/database-block/database-block.ts index cd1fe8d5a1..7331400cb6 100644 --- a/blocksuite/blocks/src/database-block/database-block.ts +++ b/blocksuite/blocks/src/database-block/database-block.ts @@ -1,3 +1,4 @@ +import type { NoteBlockComponent } from '@blocksuite/affine-block-note'; import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption'; import { menu, @@ -44,7 +45,6 @@ import { autoUpdate } from '@floating-ui/dom'; import { computed, signal } from '@preact/signals-core'; import { css, html, nothing, unsafeCSS } from 'lit'; -import type { NoteBlockComponent } from '../note-block/index.js'; import { EdgelessRootBlockComponent } from '../root-block/index.js'; import { getDropResult } from '../root-block/widgets/drag-handle/utils.js'; import { popSideDetail } from './components/layout.js'; diff --git a/blocksuite/blocks/src/effects.ts b/blocksuite/blocks/src/effects.ts index 90fdbb701d..729bc6e0b2 100644 --- a/blocksuite/blocks/src/effects.ts +++ b/blocksuite/blocks/src/effects.ts @@ -3,6 +3,7 @@ import { effects as blockBookmarkEffects } from '@blocksuite/affine-block-bookma import { effects as blockEmbedEffects } from '@blocksuite/affine-block-embed/effects'; import { effects as blockImageEffects } from '@blocksuite/affine-block-image/effects'; import { effects as blockListEffects } from '@blocksuite/affine-block-list/effects'; +import { effects as blockNoteEffects } from '@blocksuite/affine-block-note/effects'; import { effects as blockParagraphEffects } from '@blocksuite/affine-block-paragraph/effects'; import { effects as blockSurfaceEffects } from '@blocksuite/affine-block-surface/effects'; import { effects as componentAiItemEffects } from '@blocksuite/affine-components/ai-item'; @@ -66,23 +67,6 @@ import { EdgelessTextBlockComponent } from './edgeless-text-block/index.js'; import { FrameBlockComponent } from './frame-block/index.js'; import { effects as blockLatexEffects } from './latex-block/effects.js'; import { LatexBlockComponent } from './latex-block/index.js'; -import type { updateBlockType } from './note-block/commands/block-type.js'; -import type { dedentBlock } from './note-block/commands/dedent-block.js'; -import type { dedentBlockToRoot } from './note-block/commands/dedent-block-to-root.js'; -import type { dedentBlocks } from './note-block/commands/dedent-blocks.js'; -import type { dedentBlocksToRoot } from './note-block/commands/dedent-blocks-to-root.js'; -import type { focusBlockEnd } from './note-block/commands/focus-block-end.js'; -import type { focusBlockStart } from './note-block/commands/focus-block-start.js'; -import type { indentBlock } from './note-block/commands/indent-block.js'; -import type { indentBlocks } from './note-block/commands/indent-blocks.js'; -import type { selectBlock } from './note-block/commands/select-block.js'; -import type { selectBlocksBetween } from './note-block/commands/select-blocks-between.js'; -import { - EdgelessNoteBlockComponent, - EdgelessNoteMask, - NoteBlockComponent, - type NoteBlockService, -} from './note-block/index.js'; import { EdgelessAutoCompletePanel } from './root-block/edgeless/components/auto-complete/auto-complete-panel.js'; import { EdgelessAutoComplete } from './root-block/edgeless/components/auto-complete/edgeless-auto-complete.js'; import { EdgelessToolIconButton } from './root-block/edgeless/components/buttons/tool-icon-button.js'; @@ -264,6 +248,7 @@ export function effects() { stdEffects(); inlineEffects(); + blockNoteEffects(); blockAttachmentEffects(); blockBookmarkEffects(); blockListEffects(); @@ -314,8 +299,6 @@ export function effects() { customElements.define('database-datasource-block-renderer', BlockRenderer); customElements.define('affine-latex', LatexBlockComponent); customElements.define('affine-page-root', PageRootBlockComponent); - customElements.define('edgeless-note-mask', EdgelessNoteMask); - customElements.define('affine-edgeless-note', EdgelessNoteBlockComponent); customElements.define('affine-preview-root', PreviewRootBlockComponent); customElements.define('affine-code', CodeBlockComponent); customElements.define('mini-mindmap-preview', MiniMindmapPreview); @@ -445,7 +428,6 @@ export function effects() { customElements.define('edgeless-present-button', EdgelessPresentButton); customElements.define('edgeless-color-picker', EdgelessColorPicker); customElements.define('overlay-scrollbar', OverlayScrollbar); - customElements.define('affine-note', NoteBlockComponent); customElements.define('affine-template-loading', AffineTemplateLoading); customElements.define( 'edgeless-color-picker-button', @@ -549,18 +531,7 @@ export function effects() { declare global { namespace BlockSuite { interface Commands { - selectBlock: typeof selectBlock; - selectBlocksBetween: typeof selectBlocksBetween; - focusBlockStart: typeof focusBlockStart; - focusBlockEnd: typeof focusBlockEnd; - indentBlocks: typeof indentBlocks; - dedentBlock: typeof dedentBlock; - dedentBlocksToRoot: typeof dedentBlocksToRoot; - dedentBlocks: typeof dedentBlocks; - indentBlock: typeof indentBlock; - updateBlockType: typeof updateBlockType; insertEdgelessText: typeof insertEdgelessTextCommand; - dedentBlockToRoot: typeof dedentBlockToRoot; } interface CommandContext { focusBlock?: BlockComponent | null; @@ -573,7 +544,6 @@ declare global { 'affine:page': RootBlockConfig; } interface BlockServices { - 'affine:note': NoteBlockService; 'affine:page': RootService; 'affine:database': DatabaseBlockService; } diff --git a/blocksuite/blocks/src/index.ts b/blocksuite/blocks/src/index.ts index a524cd3081..4e5d7fd2b4 100644 --- a/blocksuite/blocks/src/index.ts +++ b/blocksuite/blocks/src/index.ts @@ -23,7 +23,6 @@ export * from './divider-block/index.js'; export * from './edgeless-text-block/index.js'; export * from './frame-block/index.js'; export * from './latex-block/index.js'; -export * from './note-block/index.js'; export { EdgelessTemplatePanel } from './root-block/edgeless/components/toolbar/template/template-panel.js'; export type { Template, @@ -52,6 +51,7 @@ export * from '@blocksuite/affine-block-bookmark'; export * from '@blocksuite/affine-block-embed'; export * from '@blocksuite/affine-block-image'; export * from '@blocksuite/affine-block-list'; +export * from '@blocksuite/affine-block-note'; export * from '@blocksuite/affine-block-paragraph'; export * from '@blocksuite/affine-block-surface'; export { diff --git a/blocksuite/blocks/src/note-block/index.ts b/blocksuite/blocks/src/note-block/index.ts deleted file mode 100644 index de6076b058..0000000000 --- a/blocksuite/blocks/src/note-block/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './commands/index.js'; -export * from './note-block.js'; -export * from './note-edgeless-block.js'; -export * from './note-service.js'; diff --git a/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts b/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts index 849370d0bf..6d8da439c9 100644 --- a/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts +++ b/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts @@ -1,14 +1,13 @@ -import { matchFlavours } from '@blocksuite/affine-shared/utils'; -import type { BlockComponent, BlockSelection } from '@blocksuite/block-std'; -import { IS_MAC, IS_WINDOWS } from '@blocksuite/global/env'; -import { assertExists } from '@blocksuite/global/utils'; - import { convertSelectedBlocksToLinkedDoc, getTitleFromSelectedModels, notifyDocCreated, promptDocTitle, -} from '../../_common/utils/render-linked-doc.js'; +} from '@blocksuite/affine-block-embed'; +import { matchFlavours } from '@blocksuite/affine-shared/utils'; +import type { BlockComponent, BlockSelection } from '@blocksuite/block-std'; +import { IS_MAC, IS_WINDOWS } from '@blocksuite/global/env'; +import { assertExists } from '@blocksuite/global/utils'; export class PageKeyboardManager { private readonly _handleDelete = () => { @@ -117,7 +116,7 @@ export class PageKeyboardManager { const doc = rootComponent.host.doc; const autofill = getTitleFromSelectedModels(selectedModels); - promptDocTitle(rootComponent.host, autofill) + promptDocTitle(rootComponent.std, autofill) .then(title => { if (title === null) return; convertSelectedBlocksToLinkedDoc( @@ -126,7 +125,7 @@ export class PageKeyboardManager { draftedModels, title ).catch(console.error); - notifyDocCreated(rootComponent.host, doc); + notifyDocCreated(rootComponent.std, doc); }) .catch(console.error); } diff --git a/blocksuite/blocks/src/root-block/widgets/drag-handle/watchers/pointer-event-watcher.ts b/blocksuite/blocks/src/root-block/widgets/drag-handle/watchers/pointer-event-watcher.ts index 3c7b636307..326cedd745 100644 --- a/blocksuite/blocks/src/root-block/widgets/drag-handle/watchers/pointer-event-watcher.ts +++ b/blocksuite/blocks/src/root-block/widgets/drag-handle/watchers/pointer-event-watcher.ts @@ -1,3 +1,4 @@ +import type { NoteBlockComponent } from '@blocksuite/affine-block-note'; import { captureEventTarget } from '@blocksuite/affine-shared/utils'; import { BLOCK_ID_ATTR, @@ -8,7 +9,6 @@ import { import { Point, throttle } from '@blocksuite/global/utils'; import { computed } from '@preact/signals-core'; -import type { NoteBlockComponent } from '../../../../note-block/index.js'; import type { EdgelessRootBlockComponent } from '../../../edgeless/index.js'; import { DRAG_HANDLE_CONTAINER_WIDTH, diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts index 08e2bdb0e0..0d9f99c618 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts @@ -1,10 +1,12 @@ import type { AttachmentBlockComponent } from '@blocksuite/affine-block-attachment'; import type { BookmarkBlockComponent } from '@blocksuite/affine-block-bookmark'; -import type { - EmbedFigmaBlockComponent, - EmbedGithubBlockComponent, - EmbedLoomBlockComponent, - EmbedYoutubeBlockComponent, +import { + type EmbedFigmaBlockComponent, + type EmbedGithubBlockComponent, + type EmbedLoomBlockComponent, + type EmbedYoutubeBlockComponent, + notifyDocCreated, + promptDocTitle, } from '@blocksuite/affine-block-embed'; import type { ImageBlockComponent } from '@blocksuite/affine-block-image'; import { isPeekable, peek } from '@blocksuite/affine-components/peek'; @@ -30,8 +32,6 @@ import { import { createLinkedDocFromEdgelessElements, createLinkedDocFromNote, - notifyDocCreated, - promptDocTitle, } from '../../../../_common/utils/render-linked-doc.js'; import { duplicate } from '../../../edgeless/utils/clipboard-utils.js'; import { getSortedCloneElements } from '../../../edgeless/utils/clone-utils.js'; @@ -245,11 +245,11 @@ export const conversionsGroup: MenuItemGroup = { label: 'Turn into linked doc', type: 'turn-into-linked-doc', action: async ctx => { - const { doc, service, surface, host, std } = ctx; + const { doc, service, surface, std } = ctx; const element = ctx.getNoteBlock(); if (!element) return; - const title = await promptDocTitle(host); + const title = await promptDocTitle(std); if (title === null) return; const linkedDoc = createLinkedDocFromNote(doc, element, title); @@ -309,7 +309,7 @@ export const conversionsGroup: MenuItemGroup = { host, std, }) => { - const title = await promptDocTitle(host); + const title = await promptDocTitle(std); if (title === null) return; const elements = getSortedCloneElements(selection.selectedElements); @@ -360,7 +360,7 @@ export const conversionsGroup: MenuItemGroup = { other: 'new doc', }); - notifyDocCreated(host, doc); + notifyDocCreated(std, doc); }, when: ctx => !(ctx.getLinkedDocBlock() || ctx.getNoteBlock()), }, diff --git a/blocksuite/blocks/src/root-block/widgets/format-bar/components/paragraph-button.ts b/blocksuite/blocks/src/root-block/widgets/format-bar/components/paragraph-button.ts index cca3a30bac..8f372411ab 100644 --- a/blocksuite/blocks/src/root-block/widgets/format-bar/components/paragraph-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/format-bar/components/paragraph-button.ts @@ -1,5 +1,6 @@ import { whenHover } from '@blocksuite/affine-components/hover'; import { ArrowDownIcon } from '@blocksuite/affine-components/icons'; +import { textConversionConfigs } from '@blocksuite/affine-components/rich-text'; import type { ParagraphBlockModel } from '@blocksuite/affine-model'; import type { EditorHost } from '@blocksuite/block-std'; import { assertExists } from '@blocksuite/global/utils'; @@ -8,7 +9,6 @@ import { html } from 'lit'; import { ref, type RefOrCallback } from 'lit/directives/ref.js'; import { repeat } from 'lit/directives/repeat.js'; -import { textConversionConfigs } from '../../../../_common/configs/text-conversion.js'; import type { ParagraphActionConfigItem } from '../config.js'; import type { AffineFormatBarWidget } from '../format-bar.js'; diff --git a/blocksuite/blocks/src/root-block/widgets/format-bar/config.ts b/blocksuite/blocks/src/root-block/widgets/format-bar/config.ts index 1b115d4058..904a2f90bc 100644 --- a/blocksuite/blocks/src/root-block/widgets/format-bar/config.ts +++ b/blocksuite/blocks/src/root-block/widgets/format-bar/config.ts @@ -1,3 +1,9 @@ +import { + convertSelectedBlocksToLinkedDoc, + getTitleFromSelectedModels, + notifyDocCreated, + promptDocTitle, +} from '@blocksuite/affine-block-embed'; import { BoldIcon, BulletedListIcon, @@ -37,12 +43,6 @@ import { assertExists } from '@blocksuite/global/utils'; import { Slice } from '@blocksuite/store'; import { html, type TemplateResult } from 'lit'; -import { - convertSelectedBlocksToLinkedDoc, - getTitleFromSelectedModels, - notifyDocCreated, - promptDocTitle, -} from '../../../_common/utils/render-linked-doc.js'; import { convertToDatabase } from '../../../database-block/data-source.js'; import { DATABASE_CONVERT_WHITE_LIST } from '../../../database-block/utils/block-utils.js'; import { FormatBarContext } from './context.js'; @@ -201,7 +201,7 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) { }) .draftSelectedModels() .run(); - const { draftedModels, selectedModels } = ctx; + const { draftedModels, selectedModels, std } = ctx; if (!selectedModels?.length || !draftedModels) return; const host = formatBar.host; @@ -209,16 +209,16 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) { const doc = host.doc; const autofill = getTitleFromSelectedModels(selectedModels); - promptDocTitle(host, autofill) + promptDocTitle(std, autofill) .then(async title => { if (title === null) return; await convertSelectedBlocksToLinkedDoc( - host.std, + std, doc, draftedModels, title ); - notifyDocCreated(host, doc); + notifyDocCreated(std, doc); host.std.getOptional(TelemetryProvider)?.track('DocCreated', { control: 'create linked doc', page: 'doc editor', diff --git a/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts b/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts index 3768e94af4..2f695bbde4 100644 --- a/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts +++ b/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts @@ -28,6 +28,7 @@ import { getInlineEditorByModel, insertContent, REFERENCE_NODE, + textConversionConfigs, textFormatConfigs, } from '@blocksuite/affine-components/rich-text'; import { toast } from '@blocksuite/affine-components/toast'; @@ -49,7 +50,6 @@ import { Slice, Text } from '@blocksuite/store'; import type { TemplateResult } from 'lit'; import { toggleEmbedCardCreateModal } from '../../../_common/components/embed-card/modal/embed-card-create-modal.js'; -import { textConversionConfigs } from '../../../_common/configs/text-conversion.js'; import type { DataViewBlockComponent } from '../../../data-view-block/index.js'; import { getSurfaceBlock } from '../../../surface-ref-block/utils.js'; import type { RootBlockComponent } from '../../types.js'; diff --git a/blocksuite/blocks/src/root-block/widgets/slash-menu/utils.ts b/blocksuite/blocks/src/root-block/widgets/slash-menu/utils.ts index 9b3547461c..e9011f6174 100644 --- a/blocksuite/blocks/src/root-block/widgets/slash-menu/utils.ts +++ b/blocksuite/blocks/src/root-block/widgets/slash-menu/utils.ts @@ -1,9 +1,11 @@ -import type { TextFormatConfig } from '@blocksuite/affine-components/rich-text'; +import type { + TextConversionConfig, + TextFormatConfig, +} from '@blocksuite/affine-components/rich-text'; import { isInsideBlockByFlavour } from '@blocksuite/affine-shared/utils'; import { assertType } from '@blocksuite/global/utils'; import type { BlockModel } from '@blocksuite/store'; -import type { TextConversionConfig } from '../../../_common/configs/text-conversion.js'; import type { SlashMenuActionItem, SlashMenuContext, diff --git a/blocksuite/blocks/tsconfig.json b/blocksuite/blocks/tsconfig.json index b609c33394..a6c6e94f1c 100644 --- a/blocksuite/blocks/tsconfig.json +++ b/blocksuite/blocks/tsconfig.json @@ -37,6 +37,9 @@ { "path": "../affine/block-embed" }, + { + "path": "../affine/block-note" + }, { "path": "../affine/block-bookmark" }, diff --git a/tools/utils/src/workspace.gen.ts b/tools/utils/src/workspace.gen.ts index cfbf654437..0c48fc16db 100644 --- a/tools/utils/src/workspace.gen.ts +++ b/tools/utils/src/workspace.gen.ts @@ -81,6 +81,20 @@ export const PackageList = [ 'blocksuite/framework/store', ], }, + { + location: 'blocksuite/affine/block-note', + name: '@blocksuite/affine-block-note', + workspaceDependencies: [ + 'blocksuite/affine/block-embed', + '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-paragraph', name: '@blocksuite/affine-block-paragraph', @@ -170,6 +184,7 @@ export const PackageList = [ 'blocksuite/affine/block-embed', 'blocksuite/affine/block-image', 'blocksuite/affine/block-list', + 'blocksuite/affine/block-note', 'blocksuite/affine/block-paragraph', 'blocksuite/affine/block-surface', 'blocksuite/affine/components', @@ -506,6 +521,7 @@ export type PackageName = | '@blocksuite/affine-block-embed' | '@blocksuite/affine-block-image' | '@blocksuite/affine-block-list' + | '@blocksuite/affine-block-note' | '@blocksuite/affine-block-paragraph' | '@blocksuite/affine-block-surface' | '@blocksuite/affine-components' diff --git a/yarn.lock b/yarn.lock index 8fe31d3fab..57055190e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3340,6 +3340,29 @@ __metadata: languageName: unknown linkType: soft +"@blocksuite/affine-block-note@workspace:*, @blocksuite/affine-block-note@workspace:blocksuite/affine/block-note": + version: 0.0.0-use.local + resolution: "@blocksuite/affine-block-note@workspace:blocksuite/affine/block-note" + dependencies: + "@blocksuite/affine-block-embed": "workspace:*" + "@blocksuite/affine-components": "workspace:*" + "@blocksuite/affine-model": "workspace:*" + "@blocksuite/affine-shared": "workspace:*" + "@blocksuite/block-std": "workspace:*" + "@blocksuite/global": "workspace:*" + "@blocksuite/icons": "npm:^2.1.75" + "@blocksuite/inline": "workspace:*" + "@blocksuite/store": "workspace:*" + "@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.1" + lit: "npm:^3.2.0" + minimatch: "npm:^10.0.1" + zod: "npm:^3.23.8" + languageName: unknown + linkType: soft + "@blocksuite/affine-block-paragraph@workspace:*, @blocksuite/affine-block-paragraph@workspace:blocksuite/affine/block-paragraph": version: 0.0.0-use.local resolution: "@blocksuite/affine-block-paragraph@workspace:blocksuite/affine/block-paragraph" @@ -3511,6 +3534,7 @@ __metadata: "@blocksuite/affine-block-embed": "workspace:*" "@blocksuite/affine-block-image": "workspace:*" "@blocksuite/affine-block-list": "workspace:*" + "@blocksuite/affine-block-note": "workspace:*" "@blocksuite/affine-block-paragraph": "workspace:*" "@blocksuite/affine-block-surface": "workspace:*" "@blocksuite/affine-components": "workspace:*"