mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-02 02:00:49 +08:00
refactor(editor): extract note block (#9310)
This commit is contained in:
@@ -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<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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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__"
|
||||
]
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
+11
-7
@@ -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<string, Slot>;
|
||||
};
|
||||
}
|
||||
|
||||
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(() => {
|
||||
+25
-9
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
+3
-2
@@ -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,
|
||||
@@ -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';
|
||||
|
||||
@@ -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:*",
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -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<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(
|
||||
doc: Doc,
|
||||
note: NoteBlockModel,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
@@ -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<ElementToolbarMoreMenuContext> = {
|
||||
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<ElementToolbarMoreMenuContext> = {
|
||||
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<ElementToolbarMoreMenuContext> = {
|
||||
other: 'new doc',
|
||||
});
|
||||
|
||||
notifyDocCreated(host, doc);
|
||||
notifyDocCreated(std, doc);
|
||||
},
|
||||
when: ctx => !(ctx.getLinkedDocBlock() || ctx.getNoteBlock()),
|
||||
},
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
{
|
||||
"path": "../affine/block-embed"
|
||||
},
|
||||
{
|
||||
"path": "../affine/block-note"
|
||||
},
|
||||
{
|
||||
"path": "../affine/block-bookmark"
|
||||
},
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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:*"
|
||||
|
||||
Reference in New Issue
Block a user