refactor(editor): extract note block (#9310)

This commit is contained in:
Saul-Mirone
2024-12-26 01:30:43 +00:00
parent 40b90ef51b
commit 2ffd0e561c
50 changed files with 467 additions and 394 deletions

View File

@@ -5,14 +5,18 @@ import {
NoteDisplayMode, NoteDisplayMode,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts'; 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 { matchFlavours, SpecProvider } from '@blocksuite/affine-shared/utils';
import { BlockStdScope } from '@blocksuite/block-std'; import { BlockStdScope } from '@blocksuite/block-std';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
import { import {
type BlockModel, type BlockModel,
type BlockSnapshot,
BlockViewType, BlockViewType,
type Doc, type Doc,
type DraftModel,
type Query, type Query,
Slice,
} from '@blocksuite/store'; } from '@blocksuite/store';
import { render, type TemplateResult } from 'lit'; import { render, type TemplateResult } from 'lit';
@@ -295,3 +299,122 @@ export function getDocContentWithMaxLength(doc: Doc, maxlength = 500) {
return texts.join('\n'); 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<DraftModel[]>,
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;
}

View File

@@ -28,7 +28,7 @@ export {
LinkPreviewer, LinkPreviewer,
type LinkPreviewResponseData, type LinkPreviewResponseData,
} from './common/link-previewer.js'; } 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 { toEdgelessEmbedBlock } from './common/to-edgeless-embed-block';
export * from './common/utils'; export * from './common/utils';
export * from './embed-figma-block'; export * from './embed-figma-block';

View File

@@ -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__"
]
}

View File

@@ -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;
}
}
}

View File

@@ -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';

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { MoreIndicatorIcon } from '@blocksuite/affine-components/icons'; import { MoreIndicatorIcon } from '@blocksuite/affine-components/icons';
import type { NoteBlockModel } from '@blocksuite/affine-model'; import type { NoteBlockModel } from '@blocksuite/affine-model';
import { import {
@@ -14,7 +13,11 @@ import {
matchFlavours, matchFlavours,
stopPropagation, stopPropagation,
} from '@blocksuite/affine-shared/utils'; } 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 { ShadowlessElement, toGfxBlockComponent } from '@blocksuite/block-std';
import { import {
almostEqual, almostEqual,
@@ -23,13 +26,12 @@ import {
Point, Point,
WithDisposable, WithDisposable,
} from '@blocksuite/global/utils'; } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel, Slot } from '@blocksuite/store';
import { css, html, nothing } from 'lit'; import { css, html, nothing } from 'lit';
import { property, query, state } from 'lit/decorators.js'; import { property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import type { EdgelessRootService } from '../root-block/index.js';
import { NoteBlockComponent } from './note-block.js'; import { NoteBlockComponent } from './note-block.js';
export class EdgelessNoteMask extends WithDisposable(ShadowlessElement) { export class EdgelessNoteMask extends WithDisposable(ShadowlessElement) {
@@ -151,7 +153,9 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
} }
get rootService() { get rootService() {
return this.std.getService('affine:page') as EdgelessRootService; return this.std.getService('affine:page') as BlockService & {
slots: Record<string, Slot>;
};
} }
private _collapsedContent() { private _collapsedContent() {
@@ -312,7 +316,7 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
override connectedCallback(): void { override connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
const selection = this.rootService.selection; const selection = this.gfx.selection;
this._editing = selection.has(this.model.id) && selection.editing; this._editing = selection.has(this.model.id) && selection.editing;
this._disposables.add( this._disposables.add(
@@ -328,7 +332,7 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
override firstUpdated() { override firstUpdated() {
const { _disposables } = this; const { _disposables } = this;
const selection = this.rootService.selection; const selection = this.gfx.selection;
_disposables.add( _disposables.add(
this.rootService.slots.elementResizeStart.on(() => { this.rootService.slots.elementResizeStart.on(() => {

View File

@@ -1,3 +1,4 @@
import { textConversionConfigs } from '@blocksuite/affine-components/rich-text';
import { NoteBlockSchema } from '@blocksuite/affine-model'; import { NoteBlockSchema } from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import { import {
@@ -5,14 +6,14 @@ import {
type BlockComponent, type BlockComponent,
type BlockSelection, type BlockSelection,
BlockService, BlockService,
type BlockStdScope,
type UIEventHandler, type UIEventHandler,
type UIEventStateContext, type UIEventStateContext,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store';
import { moveBlockConfigs } from '../_common/configs/move-block.js'; import { moveBlockConfigs } from './move-block';
import { quickActionConfig } from '../_common/configs/quick-action/config.js'; import { quickActionConfig } from './quick-action';
import { textConversionConfigs } from '../_common/configs/text-conversion.js';
import { onModelElementUpdated } from '../root-block/utils/callback.js';
export class NoteBlockService extends BlockService { export class NoteBlockService extends BlockService {
static override readonly flavour = NoteBlockSchema.model.flavour; static override readonly flavour = NoteBlockSchema.model.flavour;
@@ -51,10 +52,10 @@ export class NoteBlockService extends BlockService {
return { return {
...acc, ...acc,
[config.hotkey!]: ctx => { [config.hotkey!]: ctx => {
if (!config.showWhen(this.std.host)) return; if (!config.showWhen(this.std)) return;
ctx.get('defaultState').event.preventDefault(); 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) => { .inline((ctx, next) => {
const newModels = ctx.updatedBlocks; const newModels = ctx.updatedBlocks;
const host = ctx.std.host; if (!newModels) {
if (!host || !newModels) {
return; return;
} }
@@ -93,7 +93,7 @@ export class NoteBlockService extends BlockService {
} }
const [codeModel] = newModels; const [codeModel] = newModels;
onModelElementUpdated(host, codeModel, codeElement => { onModelElementUpdated(ctx.std, codeModel, codeElement => {
this._std.selection.setGroup('note', [ this._std.selection.setGroup('note', [
this._std.selection.create('text', { this._std.selection.create('text', {
from: { 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);
}

View File

@@ -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);
},
},
];

View File

@@ -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"
}
]
}

View File

@@ -1,3 +1,5 @@
import type { TemplateResult } from 'lit';
import { import {
BulletedListIcon, BulletedListIcon,
CheckBoxIcon, CheckBoxIcon,
@@ -12,8 +14,7 @@ import {
NumberedListIcon, NumberedListIcon,
QuoteIcon, QuoteIcon,
TextIcon, TextIcon,
} from '@blocksuite/affine-components/icons'; } from '../icons';
import type { TemplateResult } from 'lit';
/** /**
* Text primitive entries used in slash menu and format bar, * Text primitive entries used in slash menu and format bar,

View File

@@ -1,4 +1,5 @@
export * from './all-extensions.js'; export * from './all-extensions';
export { type TextConversionConfig, textConversionConfigs } from './conversion';
export { export {
asyncGetRichText, asyncGetRichText,
asyncSetInlineRange, asyncSetInlineRange,
@@ -7,9 +8,9 @@ export {
getRichTextByModel, getRichTextByModel,
onModelTextUpdated, onModelTextUpdated,
selectTextModel, selectTextModel,
} from './dom.js'; } from './dom';
export * from './effects.js'; export * from './effects';
export * from './extension/index.js'; export * from './extension';
export { export {
clearMarksOnDiscontinuousInput, clearMarksOnDiscontinuousInput,
FORMAT_BLOCK_SUPPORT_FLAVOURS, FORMAT_BLOCK_SUPPORT_FLAVOURS,
@@ -20,9 +21,9 @@ export {
textCommands, textCommands,
type TextFormatConfig, type TextFormatConfig,
textFormatConfigs, textFormatConfigs,
} from './format/index.js'; } from './format';
export * from './inline/index.js'; export * from './inline';
export { textKeymap } from './keymap/index.js'; export { textKeymap } from './keymap';
export { insertLinkedNode } from './linked-node.js'; export { insertLinkedNode } from './linked-node';
export { markdownInput } from './markdown/index.js'; export { markdownInput } from './markdown';
export { RichText } from './rich-text.js'; export { RichText } from './rich-text';

View File

@@ -19,6 +19,7 @@
"@blocksuite/affine-block-embed": "workspace:*", "@blocksuite/affine-block-embed": "workspace:*",
"@blocksuite/affine-block-image": "workspace:*", "@blocksuite/affine-block-image": "workspace:*",
"@blocksuite/affine-block-list": "workspace:*", "@blocksuite/affine-block-list": "workspace:*",
"@blocksuite/affine-block-note": "workspace:*",
"@blocksuite/affine-block-paragraph": "workspace:*", "@blocksuite/affine-block-paragraph": "workspace:*",
"@blocksuite/affine-block-surface": "workspace:*", "@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*", "@blocksuite/affine-components": "workspace:*",

View File

@@ -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);
},
},
];

View File

@@ -1,19 +1,9 @@
import type { FrameBlockModel, NoteBlockModel } from '@blocksuite/affine-model'; import type { FrameBlockModel, NoteBlockModel } from '@blocksuite/affine-model';
import { NoteDisplayMode } from '@blocksuite/affine-model'; import { NoteDisplayMode } from '@blocksuite/affine-model';
import { import { DocModeProvider } from '@blocksuite/affine-shared/services';
DocModeProvider, import { getBlockProps } from '@blocksuite/affine-shared/utils';
NotificationProvider,
} from '@blocksuite/affine-shared/services';
import { getBlockProps, matchFlavours } from '@blocksuite/affine-shared/utils';
import type { EditorHost } from '@blocksuite/block-std'; import type { EditorHost } from '@blocksuite/block-std';
import { assertExists } from '@blocksuite/global/utils'; import { type BlockModel, type Doc } from '@blocksuite/store';
import {
type BlockModel,
type BlockSnapshot,
type Doc,
type DraftModel,
Slice,
} from '@blocksuite/store';
import { GfxBlockModel } from '../../root-block/edgeless/block-model.js'; import { GfxBlockModel } from '../../root-block/edgeless/block-model.js';
import { import {
@@ -27,68 +17,6 @@ import {
} from '../../root-block/edgeless/utils/query.js'; } from '../../root-block/edgeless/utils/query.js';
import { getSurfaceBlock } from '../../surface-ref-block/utils.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( export function addBlocksToDoc(
targetDoc: Doc, targetDoc: Doc,
model: BlockModel, model: BlockModel,
@@ -110,61 +38,6 @@ export function addBlocksToDoc(
} }
} }
export async function convertSelectedBlocksToLinkedDoc(
std: BlockSuite.Std,
doc: Doc,
selectedModels: DraftModel[] | Promise<DraftModel[]>,
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( export function createLinkedDocFromNote(
doc: Doc, doc: Doc,
note: NoteBlockModel, note: NoteBlockModel,

View File

@@ -3,6 +3,10 @@ import { BookmarkBlockSpec } from '@blocksuite/affine-block-bookmark';
import { EmbedExtensions } from '@blocksuite/affine-block-embed'; import { EmbedExtensions } from '@blocksuite/affine-block-embed';
import { ImageBlockSpec } from '@blocksuite/affine-block-image'; import { ImageBlockSpec } from '@blocksuite/affine-block-image';
import { ListBlockSpec } from '@blocksuite/affine-block-list'; import { ListBlockSpec } from '@blocksuite/affine-block-list';
import {
EdgelessNoteBlockSpec,
NoteBlockSpec,
} from '@blocksuite/affine-block-note';
import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph'; import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph';
import { RichTextExtensions } from '@blocksuite/affine-components/rich-text'; import { RichTextExtensions } from '@blocksuite/affine-components/rich-text';
import { EditPropsStore } from '@blocksuite/affine-shared/services'; 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 { DataViewBlockSpec } from '../data-view-block/data-view-spec.js';
import { DatabaseBlockSpec } from '../database-block/database-spec.js'; import { DatabaseBlockSpec } from '../database-block/database-spec.js';
import { DividerBlockSpec } from '../divider-block/divider-spec.js'; import { DividerBlockSpec } from '../divider-block/divider-spec.js';
import {
EdgelessNoteBlockSpec,
NoteBlockSpec,
} from '../note-block/note-spec.js';
export const CommonFirstPartyBlockSpecs: ExtensionType[] = [ export const CommonFirstPartyBlockSpecs: ExtensionType[] = [
RichTextExtensions, RichTextExtensions,

View File

@@ -11,16 +11,16 @@ import {
} from '@blocksuite/affine-block-embed'; } from '@blocksuite/affine-block-embed';
import { ImageBlockSpec } from '@blocksuite/affine-block-image'; import { ImageBlockSpec } from '@blocksuite/affine-block-image';
import { ListBlockSpec } from '@blocksuite/affine-block-list'; import { ListBlockSpec } from '@blocksuite/affine-block-list';
import {
EdgelessNoteBlockSpec,
NoteBlockSpec,
} from '@blocksuite/affine-block-note';
import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph'; import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph';
import { CodeBlockSpec } from '../../code-block/code-block-spec.js'; import { CodeBlockSpec } from '../../code-block/code-block-spec.js';
import { DataViewBlockSpec } from '../../data-view-block/data-view-spec.js'; import { DataViewBlockSpec } from '../../data-view-block/data-view-spec.js';
import { DatabaseBlockSpec } from '../../database-block/database-spec.js'; import { DatabaseBlockSpec } from '../../database-block/database-spec.js';
import { DividerBlockSpec } from '../../divider-block/divider-spec.js'; import { DividerBlockSpec } from '../../divider-block/divider-spec.js';
import {
EdgelessNoteBlockSpec,
NoteBlockSpec,
} from '../../note-block/note-spec.js';
export { export {
AttachmentBlockSpec, AttachmentBlockSpec,

View File

@@ -1,3 +1,4 @@
import type { NoteBlockComponent } from '@blocksuite/affine-block-note';
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption'; import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
import { import {
menu, menu,
@@ -40,7 +41,6 @@ import { html } from 'lit/static-html.js';
import { BlockRenderer } from '../database-block/detail-panel/block-renderer.js'; import { BlockRenderer } from '../database-block/detail-panel/block-renderer.js';
import { NoteRenderer } from '../database-block/detail-panel/note-renderer.js'; import { NoteRenderer } from '../database-block/detail-panel/note-renderer.js';
import type { NoteBlockComponent } from '../note-block/index.js';
import { import {
EdgelessRootBlockComponent, EdgelessRootBlockComponent,
type RootService, type RootService,

View File

@@ -1,3 +1,4 @@
import type { NoteBlockComponent } from '@blocksuite/affine-block-note';
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption'; import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
import { import {
menu, menu,
@@ -44,7 +45,6 @@ import { autoUpdate } from '@floating-ui/dom';
import { computed, signal } from '@preact/signals-core'; import { computed, signal } from '@preact/signals-core';
import { css, html, nothing, unsafeCSS } from 'lit'; import { css, html, nothing, unsafeCSS } from 'lit';
import type { NoteBlockComponent } from '../note-block/index.js';
import { EdgelessRootBlockComponent } from '../root-block/index.js'; import { EdgelessRootBlockComponent } from '../root-block/index.js';
import { getDropResult } from '../root-block/widgets/drag-handle/utils.js'; import { getDropResult } from '../root-block/widgets/drag-handle/utils.js';
import { popSideDetail } from './components/layout.js'; import { popSideDetail } from './components/layout.js';

View File

@@ -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 blockEmbedEffects } from '@blocksuite/affine-block-embed/effects';
import { effects as blockImageEffects } from '@blocksuite/affine-block-image/effects'; import { effects as blockImageEffects } from '@blocksuite/affine-block-image/effects';
import { effects as blockListEffects } from '@blocksuite/affine-block-list/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 blockParagraphEffects } from '@blocksuite/affine-block-paragraph/effects';
import { effects as blockSurfaceEffects } from '@blocksuite/affine-block-surface/effects'; import { effects as blockSurfaceEffects } from '@blocksuite/affine-block-surface/effects';
import { effects as componentAiItemEffects } from '@blocksuite/affine-components/ai-item'; 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 { FrameBlockComponent } from './frame-block/index.js';
import { effects as blockLatexEffects } from './latex-block/effects.js'; import { effects as blockLatexEffects } from './latex-block/effects.js';
import { LatexBlockComponent } from './latex-block/index.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 { 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 { EdgelessAutoComplete } from './root-block/edgeless/components/auto-complete/edgeless-auto-complete.js';
import { EdgelessToolIconButton } from './root-block/edgeless/components/buttons/tool-icon-button.js'; import { EdgelessToolIconButton } from './root-block/edgeless/components/buttons/tool-icon-button.js';
@@ -264,6 +248,7 @@ export function effects() {
stdEffects(); stdEffects();
inlineEffects(); inlineEffects();
blockNoteEffects();
blockAttachmentEffects(); blockAttachmentEffects();
blockBookmarkEffects(); blockBookmarkEffects();
blockListEffects(); blockListEffects();
@@ -314,8 +299,6 @@ export function effects() {
customElements.define('database-datasource-block-renderer', BlockRenderer); customElements.define('database-datasource-block-renderer', BlockRenderer);
customElements.define('affine-latex', LatexBlockComponent); customElements.define('affine-latex', LatexBlockComponent);
customElements.define('affine-page-root', PageRootBlockComponent); 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-preview-root', PreviewRootBlockComponent);
customElements.define('affine-code', CodeBlockComponent); customElements.define('affine-code', CodeBlockComponent);
customElements.define('mini-mindmap-preview', MiniMindmapPreview); customElements.define('mini-mindmap-preview', MiniMindmapPreview);
@@ -445,7 +428,6 @@ export function effects() {
customElements.define('edgeless-present-button', EdgelessPresentButton); customElements.define('edgeless-present-button', EdgelessPresentButton);
customElements.define('edgeless-color-picker', EdgelessColorPicker); customElements.define('edgeless-color-picker', EdgelessColorPicker);
customElements.define('overlay-scrollbar', OverlayScrollbar); customElements.define('overlay-scrollbar', OverlayScrollbar);
customElements.define('affine-note', NoteBlockComponent);
customElements.define('affine-template-loading', AffineTemplateLoading); customElements.define('affine-template-loading', AffineTemplateLoading);
customElements.define( customElements.define(
'edgeless-color-picker-button', 'edgeless-color-picker-button',
@@ -549,18 +531,7 @@ export function effects() {
declare global { declare global {
namespace BlockSuite { namespace BlockSuite {
interface Commands { 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; insertEdgelessText: typeof insertEdgelessTextCommand;
dedentBlockToRoot: typeof dedentBlockToRoot;
} }
interface CommandContext { interface CommandContext {
focusBlock?: BlockComponent | null; focusBlock?: BlockComponent | null;
@@ -573,7 +544,6 @@ declare global {
'affine:page': RootBlockConfig; 'affine:page': RootBlockConfig;
} }
interface BlockServices { interface BlockServices {
'affine:note': NoteBlockService;
'affine:page': RootService; 'affine:page': RootService;
'affine:database': DatabaseBlockService; 'affine:database': DatabaseBlockService;
} }

View File

@@ -23,7 +23,6 @@ export * from './divider-block/index.js';
export * from './edgeless-text-block/index.js'; export * from './edgeless-text-block/index.js';
export * from './frame-block/index.js'; export * from './frame-block/index.js';
export * from './latex-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 { EdgelessTemplatePanel } from './root-block/edgeless/components/toolbar/template/template-panel.js';
export type { export type {
Template, Template,
@@ -52,6 +51,7 @@ export * from '@blocksuite/affine-block-bookmark';
export * from '@blocksuite/affine-block-embed'; export * from '@blocksuite/affine-block-embed';
export * from '@blocksuite/affine-block-image'; export * from '@blocksuite/affine-block-image';
export * from '@blocksuite/affine-block-list'; export * from '@blocksuite/affine-block-list';
export * from '@blocksuite/affine-block-note';
export * from '@blocksuite/affine-block-paragraph'; export * from '@blocksuite/affine-block-paragraph';
export * from '@blocksuite/affine-block-surface'; export * from '@blocksuite/affine-block-surface';
export { export {

View File

@@ -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';

View File

@@ -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 { import {
convertSelectedBlocksToLinkedDoc, convertSelectedBlocksToLinkedDoc,
getTitleFromSelectedModels, getTitleFromSelectedModels,
notifyDocCreated, notifyDocCreated,
promptDocTitle, 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 { export class PageKeyboardManager {
private readonly _handleDelete = () => { private readonly _handleDelete = () => {
@@ -117,7 +116,7 @@ export class PageKeyboardManager {
const doc = rootComponent.host.doc; const doc = rootComponent.host.doc;
const autofill = getTitleFromSelectedModels(selectedModels); const autofill = getTitleFromSelectedModels(selectedModels);
promptDocTitle(rootComponent.host, autofill) promptDocTitle(rootComponent.std, autofill)
.then(title => { .then(title => {
if (title === null) return; if (title === null) return;
convertSelectedBlocksToLinkedDoc( convertSelectedBlocksToLinkedDoc(
@@ -126,7 +125,7 @@ export class PageKeyboardManager {
draftedModels, draftedModels,
title title
).catch(console.error); ).catch(console.error);
notifyDocCreated(rootComponent.host, doc); notifyDocCreated(rootComponent.std, doc);
}) })
.catch(console.error); .catch(console.error);
} }

View File

@@ -1,3 +1,4 @@
import type { NoteBlockComponent } from '@blocksuite/affine-block-note';
import { captureEventTarget } from '@blocksuite/affine-shared/utils'; import { captureEventTarget } from '@blocksuite/affine-shared/utils';
import { import {
BLOCK_ID_ATTR, BLOCK_ID_ATTR,
@@ -8,7 +9,6 @@ import {
import { Point, throttle } from '@blocksuite/global/utils'; import { Point, throttle } from '@blocksuite/global/utils';
import { computed } from '@preact/signals-core'; import { computed } from '@preact/signals-core';
import type { NoteBlockComponent } from '../../../../note-block/index.js';
import type { EdgelessRootBlockComponent } from '../../../edgeless/index.js'; import type { EdgelessRootBlockComponent } from '../../../edgeless/index.js';
import { import {
DRAG_HANDLE_CONTAINER_WIDTH, DRAG_HANDLE_CONTAINER_WIDTH,

View File

@@ -1,10 +1,12 @@
import type { AttachmentBlockComponent } from '@blocksuite/affine-block-attachment'; import type { AttachmentBlockComponent } from '@blocksuite/affine-block-attachment';
import type { BookmarkBlockComponent } from '@blocksuite/affine-block-bookmark'; import type { BookmarkBlockComponent } from '@blocksuite/affine-block-bookmark';
import type { import {
EmbedFigmaBlockComponent, type EmbedFigmaBlockComponent,
EmbedGithubBlockComponent, type EmbedGithubBlockComponent,
EmbedLoomBlockComponent, type EmbedLoomBlockComponent,
EmbedYoutubeBlockComponent, type EmbedYoutubeBlockComponent,
notifyDocCreated,
promptDocTitle,
} from '@blocksuite/affine-block-embed'; } from '@blocksuite/affine-block-embed';
import type { ImageBlockComponent } from '@blocksuite/affine-block-image'; import type { ImageBlockComponent } from '@blocksuite/affine-block-image';
import { isPeekable, peek } from '@blocksuite/affine-components/peek'; import { isPeekable, peek } from '@blocksuite/affine-components/peek';
@@ -30,8 +32,6 @@ import {
import { import {
createLinkedDocFromEdgelessElements, createLinkedDocFromEdgelessElements,
createLinkedDocFromNote, createLinkedDocFromNote,
notifyDocCreated,
promptDocTitle,
} from '../../../../_common/utils/render-linked-doc.js'; } from '../../../../_common/utils/render-linked-doc.js';
import { duplicate } from '../../../edgeless/utils/clipboard-utils.js'; import { duplicate } from '../../../edgeless/utils/clipboard-utils.js';
import { getSortedCloneElements } from '../../../edgeless/utils/clone-utils.js'; import { getSortedCloneElements } from '../../../edgeless/utils/clone-utils.js';
@@ -245,11 +245,11 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
label: 'Turn into linked doc', label: 'Turn into linked doc',
type: 'turn-into-linked-doc', type: 'turn-into-linked-doc',
action: async ctx => { action: async ctx => {
const { doc, service, surface, host, std } = ctx; const { doc, service, surface, std } = ctx;
const element = ctx.getNoteBlock(); const element = ctx.getNoteBlock();
if (!element) return; if (!element) return;
const title = await promptDocTitle(host); const title = await promptDocTitle(std);
if (title === null) return; if (title === null) return;
const linkedDoc = createLinkedDocFromNote(doc, element, title); const linkedDoc = createLinkedDocFromNote(doc, element, title);
@@ -309,7 +309,7 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
host, host,
std, std,
}) => { }) => {
const title = await promptDocTitle(host); const title = await promptDocTitle(std);
if (title === null) return; if (title === null) return;
const elements = getSortedCloneElements(selection.selectedElements); const elements = getSortedCloneElements(selection.selectedElements);
@@ -360,7 +360,7 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
other: 'new doc', other: 'new doc',
}); });
notifyDocCreated(host, doc); notifyDocCreated(std, doc);
}, },
when: ctx => !(ctx.getLinkedDocBlock() || ctx.getNoteBlock()), when: ctx => !(ctx.getLinkedDocBlock() || ctx.getNoteBlock()),
}, },

View File

@@ -1,5 +1,6 @@
import { whenHover } from '@blocksuite/affine-components/hover'; import { whenHover } from '@blocksuite/affine-components/hover';
import { ArrowDownIcon } from '@blocksuite/affine-components/icons'; import { ArrowDownIcon } from '@blocksuite/affine-components/icons';
import { textConversionConfigs } from '@blocksuite/affine-components/rich-text';
import type { ParagraphBlockModel } from '@blocksuite/affine-model'; import type { ParagraphBlockModel } from '@blocksuite/affine-model';
import type { EditorHost } from '@blocksuite/block-std'; import type { EditorHost } from '@blocksuite/block-std';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
@@ -8,7 +9,6 @@ import { html } from 'lit';
import { ref, type RefOrCallback } from 'lit/directives/ref.js'; import { ref, type RefOrCallback } from 'lit/directives/ref.js';
import { repeat } from 'lit/directives/repeat.js'; import { repeat } from 'lit/directives/repeat.js';
import { textConversionConfigs } from '../../../../_common/configs/text-conversion.js';
import type { ParagraphActionConfigItem } from '../config.js'; import type { ParagraphActionConfigItem } from '../config.js';
import type { AffineFormatBarWidget } from '../format-bar.js'; import type { AffineFormatBarWidget } from '../format-bar.js';

View File

@@ -1,3 +1,9 @@
import {
convertSelectedBlocksToLinkedDoc,
getTitleFromSelectedModels,
notifyDocCreated,
promptDocTitle,
} from '@blocksuite/affine-block-embed';
import { import {
BoldIcon, BoldIcon,
BulletedListIcon, BulletedListIcon,
@@ -37,12 +43,6 @@ import { assertExists } from '@blocksuite/global/utils';
import { Slice } from '@blocksuite/store'; import { Slice } from '@blocksuite/store';
import { html, type TemplateResult } from 'lit'; 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 { convertToDatabase } from '../../../database-block/data-source.js';
import { DATABASE_CONVERT_WHITE_LIST } from '../../../database-block/utils/block-utils.js'; import { DATABASE_CONVERT_WHITE_LIST } from '../../../database-block/utils/block-utils.js';
import { FormatBarContext } from './context.js'; import { FormatBarContext } from './context.js';
@@ -201,7 +201,7 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) {
}) })
.draftSelectedModels() .draftSelectedModels()
.run(); .run();
const { draftedModels, selectedModels } = ctx; const { draftedModels, selectedModels, std } = ctx;
if (!selectedModels?.length || !draftedModels) return; if (!selectedModels?.length || !draftedModels) return;
const host = formatBar.host; const host = formatBar.host;
@@ -209,16 +209,16 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) {
const doc = host.doc; const doc = host.doc;
const autofill = getTitleFromSelectedModels(selectedModels); const autofill = getTitleFromSelectedModels(selectedModels);
promptDocTitle(host, autofill) promptDocTitle(std, autofill)
.then(async title => { .then(async title => {
if (title === null) return; if (title === null) return;
await convertSelectedBlocksToLinkedDoc( await convertSelectedBlocksToLinkedDoc(
host.std, std,
doc, doc,
draftedModels, draftedModels,
title title
); );
notifyDocCreated(host, doc); notifyDocCreated(std, doc);
host.std.getOptional(TelemetryProvider)?.track('DocCreated', { host.std.getOptional(TelemetryProvider)?.track('DocCreated', {
control: 'create linked doc', control: 'create linked doc',
page: 'doc editor', page: 'doc editor',

View File

@@ -28,6 +28,7 @@ import {
getInlineEditorByModel, getInlineEditorByModel,
insertContent, insertContent,
REFERENCE_NODE, REFERENCE_NODE,
textConversionConfigs,
textFormatConfigs, textFormatConfigs,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import { toast } from '@blocksuite/affine-components/toast'; import { toast } from '@blocksuite/affine-components/toast';
@@ -49,7 +50,6 @@ import { Slice, Text } from '@blocksuite/store';
import type { TemplateResult } from 'lit'; import type { TemplateResult } from 'lit';
import { toggleEmbedCardCreateModal } from '../../../_common/components/embed-card/modal/embed-card-create-modal.js'; 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 type { DataViewBlockComponent } from '../../../data-view-block/index.js';
import { getSurfaceBlock } from '../../../surface-ref-block/utils.js'; import { getSurfaceBlock } from '../../../surface-ref-block/utils.js';
import type { RootBlockComponent } from '../../types.js'; import type { RootBlockComponent } from '../../types.js';

View File

@@ -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 { isInsideBlockByFlavour } from '@blocksuite/affine-shared/utils';
import { assertType } from '@blocksuite/global/utils'; import { assertType } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
import type { TextConversionConfig } from '../../../_common/configs/text-conversion.js';
import type { import type {
SlashMenuActionItem, SlashMenuActionItem,
SlashMenuContext, SlashMenuContext,

View File

@@ -37,6 +37,9 @@
{ {
"path": "../affine/block-embed" "path": "../affine/block-embed"
}, },
{
"path": "../affine/block-note"
},
{ {
"path": "../affine/block-bookmark" "path": "../affine/block-bookmark"
}, },

View File

@@ -81,6 +81,20 @@ export const PackageList = [
'blocksuite/framework/store', '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', location: 'blocksuite/affine/block-paragraph',
name: '@blocksuite/affine-block-paragraph', name: '@blocksuite/affine-block-paragraph',
@@ -170,6 +184,7 @@ export const PackageList = [
'blocksuite/affine/block-embed', 'blocksuite/affine/block-embed',
'blocksuite/affine/block-image', 'blocksuite/affine/block-image',
'blocksuite/affine/block-list', 'blocksuite/affine/block-list',
'blocksuite/affine/block-note',
'blocksuite/affine/block-paragraph', 'blocksuite/affine/block-paragraph',
'blocksuite/affine/block-surface', 'blocksuite/affine/block-surface',
'blocksuite/affine/components', 'blocksuite/affine/components',
@@ -506,6 +521,7 @@ export type PackageName =
| '@blocksuite/affine-block-embed' | '@blocksuite/affine-block-embed'
| '@blocksuite/affine-block-image' | '@blocksuite/affine-block-image'
| '@blocksuite/affine-block-list' | '@blocksuite/affine-block-list'
| '@blocksuite/affine-block-note'
| '@blocksuite/affine-block-paragraph' | '@blocksuite/affine-block-paragraph'
| '@blocksuite/affine-block-surface' | '@blocksuite/affine-block-surface'
| '@blocksuite/affine-components' | '@blocksuite/affine-components'

View File

@@ -3340,6 +3340,29 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft 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": "@blocksuite/affine-block-paragraph@workspace:*, @blocksuite/affine-block-paragraph@workspace:blocksuite/affine/block-paragraph":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@blocksuite/affine-block-paragraph@workspace:blocksuite/affine/block-paragraph" resolution: "@blocksuite/affine-block-paragraph@workspace:blocksuite/affine/block-paragraph"
@@ -3511,6 +3534,7 @@ __metadata:
"@blocksuite/affine-block-embed": "workspace:*" "@blocksuite/affine-block-embed": "workspace:*"
"@blocksuite/affine-block-image": "workspace:*" "@blocksuite/affine-block-image": "workspace:*"
"@blocksuite/affine-block-list": "workspace:*" "@blocksuite/affine-block-list": "workspace:*"
"@blocksuite/affine-block-note": "workspace:*"
"@blocksuite/affine-block-paragraph": "workspace:*" "@blocksuite/affine-block-paragraph": "workspace:*"
"@blocksuite/affine-block-surface": "workspace:*" "@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-components": "workspace:*" "@blocksuite/affine-components": "workspace:*"