feat(editor): add embed doc block extension (#12090)

Closes: BS-3393

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced a new "Embed Doc" block, enabling embedding and rendering of linked and synced documents within cards, including support for banners and note previews.
  - Added new toolbar and quick search options for inserting embedded linked and synced documents.

- **Improvements**
  - Updated dependencies and internal references to support the new embed doc functionality across related blocks and components.
  - Enhanced support for edgeless environments with new clipboard and configuration options for embedded docs.

- **Refactor**
  - Streamlined and reorganized embed-related code, moving linked and synced doc logic into a dedicated embed doc module.
  - Removed obsolete adapter and utility files to simplify maintenance.

- **Chores**
  - Updated project and TypeScript configuration files to include the new embed doc module in builds and references.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Saul-Mirone 2025-04-30 09:16:17 +00:00
parent 4660b41d20
commit 04531508cb
No known key found for this signature in database
GPG Key ID: 0D941B4A9125B742
77 changed files with 496 additions and 586 deletions

View File

@ -19,6 +19,7 @@
"@blocksuite/affine-block-divider": "workspace:*",
"@blocksuite/affine-block-edgeless-text": "workspace:*",
"@blocksuite/affine-block-embed": "workspace:*",
"@blocksuite/affine-block-embed-doc": "workspace:*",
"@blocksuite/affine-block-frame": "workspace:*",
"@blocksuite/affine-block-image": "workspace:*",
"@blocksuite/affine-block-latex": "workspace:*",
@ -121,6 +122,9 @@
"./blocks/embed": "./src/blocks/embed/index.ts",
"./blocks/embed/store": "./src/blocks/embed/store.ts",
"./blocks/embed/view": "./src/blocks/embed/view.ts",
"./blocks/embed-doc": "./src/blocks/embed-doc/index.ts",
"./blocks/embed-doc/store": "./src/blocks/embed-doc/store.ts",
"./blocks/embed-doc/view": "./src/blocks/embed-doc/view.ts",
"./blocks/frame": "./src/blocks/frame/index.ts",
"./blocks/frame/store": "./src/blocks/frame/store.ts",
"./blocks/frame/view": "./src/blocks/frame/view.ts",

View File

@ -1,65 +0,0 @@
import {
HtmlInlineToDeltaAdapterExtensions,
InlineDeltaToHtmlAdapterExtensions,
InlineDeltaToMarkdownAdapterExtensions,
MarkdownInlineToDeltaAdapterExtensions,
NotionHtmlInlineToDeltaAdapterExtensions,
} from '@blocksuite/affine-inline-preset';
import {
AttachmentAdapterFactoryExtension,
HtmlAdapterFactoryExtension,
ImageAdapterFactoryExtension,
MarkdownAdapterFactoryExtension,
MixTextAdapterFactoryExtension,
NotionHtmlAdapterFactoryExtension,
NotionTextAdapterFactoryExtension,
PlainTextAdapterFactoryExtension,
} from '@blocksuite/affine-shared/adapters';
import type { ExtensionType } from '@blocksuite/store';
import { defaultBlockHtmlAdapterMatchers } from './html/block-matcher';
import { defaultBlockMarkdownAdapterMatchers } from './markdown/block-matcher';
import { defaultMarkdownPreprocessors } from './markdown/preprocessor';
import { defaultBlockNotionHtmlAdapterMatchers } from './notion-html/block-matcher';
import { defaultBlockPlainTextAdapterMatchers } from './plain-text/block-matcher';
export function getAdapterFactoryExtensions(): ExtensionType[] {
return [
AttachmentAdapterFactoryExtension,
ImageAdapterFactoryExtension,
MarkdownAdapterFactoryExtension,
PlainTextAdapterFactoryExtension,
HtmlAdapterFactoryExtension,
NotionTextAdapterFactoryExtension,
NotionHtmlAdapterFactoryExtension,
MixTextAdapterFactoryExtension,
];
}
export function getHtmlAdapterExtensions(): ExtensionType[] {
return [
...HtmlInlineToDeltaAdapterExtensions,
...defaultBlockHtmlAdapterMatchers,
...InlineDeltaToHtmlAdapterExtensions,
];
}
export function getMarkdownAdapterExtensions(): ExtensionType[] {
return [
...MarkdownInlineToDeltaAdapterExtensions,
...defaultBlockMarkdownAdapterMatchers,
...InlineDeltaToMarkdownAdapterExtensions,
...defaultMarkdownPreprocessors,
];
}
export function getNotionHtmlAdapterExtensions(): ExtensionType[] {
return [
...NotionHtmlInlineToDeltaAdapterExtensions,
...defaultBlockNotionHtmlAdapterMatchers,
];
}
export function getPlainTextAdapterExtensions(): ExtensionType[] {
return [...defaultBlockPlainTextAdapterMatchers];
}

View File

@ -1,37 +0,0 @@
import { BookmarkBlockHtmlAdapterExtension } from '@blocksuite/affine-block-bookmark';
import { CodeBlockHtmlAdapterExtension } from '@blocksuite/affine-block-code';
import { DatabaseBlockHtmlAdapterExtension } from '@blocksuite/affine-block-database';
import { DividerBlockHtmlAdapterExtension } from '@blocksuite/affine-block-divider';
import {
EmbedFigmaBlockHtmlAdapterExtension,
EmbedGithubBlockHtmlAdapterExtension,
EmbedIframeBlockHtmlAdapterExtension,
EmbedLinkedDocHtmlAdapterExtension,
EmbedLoomBlockHtmlAdapterExtension,
EmbedSyncedDocBlockHtmlAdapterExtension,
EmbedYoutubeBlockHtmlAdapterExtension,
} from '@blocksuite/affine-block-embed';
import { ImageBlockHtmlAdapterExtension } from '@blocksuite/affine-block-image';
import { ListBlockHtmlAdapterExtension } from '@blocksuite/affine-block-list';
import { ParagraphBlockHtmlAdapterExtension } from '@blocksuite/affine-block-paragraph';
import { RootBlockHtmlAdapterExtension } from '@blocksuite/affine-block-root';
import { TableBlockHtmlAdapterExtension } from '@blocksuite/affine-block-table';
export const defaultBlockHtmlAdapterMatchers = [
ListBlockHtmlAdapterExtension,
ParagraphBlockHtmlAdapterExtension,
CodeBlockHtmlAdapterExtension,
DividerBlockHtmlAdapterExtension,
ImageBlockHtmlAdapterExtension,
RootBlockHtmlAdapterExtension,
EmbedYoutubeBlockHtmlAdapterExtension,
EmbedFigmaBlockHtmlAdapterExtension,
EmbedLoomBlockHtmlAdapterExtension,
EmbedGithubBlockHtmlAdapterExtension,
EmbedIframeBlockHtmlAdapterExtension,
BookmarkBlockHtmlAdapterExtension,
DatabaseBlockHtmlAdapterExtension,
TableBlockHtmlAdapterExtension,
EmbedLinkedDocHtmlAdapterExtension,
EmbedSyncedDocBlockHtmlAdapterExtension,
];

View File

@ -1,6 +0,0 @@
export * from './extension.js';
export * from './html/block-matcher.js';
export * from './markdown/block-matcher.js';
export * from './markdown/preprocessor.js';
export * from './notion-html/block-matcher.js';
export * from './plain-text/block-matcher.js';

View File

@ -1,43 +0,0 @@
import { AttachmentBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-attachment';
import { BookmarkBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-bookmark';
import { CodeBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-code';
import { DatabaseBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-database';
import { DividerBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-divider';
import {
EmbedFigmaMarkdownAdapterExtension,
EmbedGithubMarkdownAdapterExtension,
EmbedIframeBlockMarkdownAdapterExtension,
EmbedLinkedDocMarkdownAdapterExtension,
EmbedLoomMarkdownAdapterExtension,
EmbedSyncedDocMarkdownAdapterExtension,
EmbedYoutubeMarkdownAdapterExtension,
} from '@blocksuite/affine-block-embed';
import { ImageBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-image';
import { LatexBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-latex';
import { ListBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-list';
import { DocNoteBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-note';
import { ParagraphBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-paragraph';
import { RootBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-root';
import { TableBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-table';
export const defaultBlockMarkdownAdapterMatchers = [
RootBlockMarkdownAdapterExtension,
DocNoteBlockMarkdownAdapterExtension,
EmbedFigmaMarkdownAdapterExtension,
EmbedGithubMarkdownAdapterExtension,
EmbedLinkedDocMarkdownAdapterExtension,
EmbedLoomMarkdownAdapterExtension,
EmbedSyncedDocMarkdownAdapterExtension,
EmbedYoutubeMarkdownAdapterExtension,
EmbedIframeBlockMarkdownAdapterExtension,
ListBlockMarkdownAdapterExtension,
ParagraphBlockMarkdownAdapterExtension,
BookmarkBlockMarkdownAdapterExtension,
CodeBlockMarkdownAdapterExtension,
DatabaseBlockMarkdownAdapterExtension,
TableBlockMarkdownAdapterExtension,
DividerBlockMarkdownAdapterExtension,
ImageBlockMarkdownAdapterExtension,
LatexBlockMarkdownAdapterExtension,
AttachmentBlockMarkdownAdapterExtension,
];

View File

@ -1,9 +0,0 @@
import { BookmarkBlockMarkdownPreprocessorExtension } from '@blocksuite/affine-block-bookmark';
import { CodeMarkdownPreprocessorExtension } from '@blocksuite/affine-block-code';
import { LatexMarkdownPreprocessorExtension } from '@blocksuite/affine-block-latex';
export const defaultMarkdownPreprocessors = [
LatexMarkdownPreprocessorExtension,
CodeMarkdownPreprocessorExtension,
BookmarkBlockMarkdownPreprocessorExtension,
];

View File

@ -1,34 +0,0 @@
import { AttachmentBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-attachment';
import { BookmarkBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-bookmark';
import { CodeBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-code';
import { DatabaseBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-database';
import { DividerBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-divider';
import {
EmbedFigmaBlockNotionHtmlAdapterExtension,
EmbedGithubBlockNotionHtmlAdapterExtension,
EmbedLoomBlockNotionHtmlAdapterExtension,
EmbedYoutubeBlockNotionHtmlAdapterExtension,
} from '@blocksuite/affine-block-embed';
import { ImageBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-image';
import { LatexBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-latex';
import { ListBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-list';
import { ParagraphBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-paragraph';
import { RootBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-root';
import type { ExtensionType } from '@blocksuite/store';
export const defaultBlockNotionHtmlAdapterMatchers: ExtensionType[] = [
ListBlockNotionHtmlAdapterExtension,
ParagraphBlockNotionHtmlAdapterExtension,
CodeBlockNotionHtmlAdapterExtension,
DividerBlockNotionHtmlAdapterExtension,
ImageBlockNotionHtmlAdapterExtension,
RootBlockNotionHtmlAdapterExtension,
BookmarkBlockNotionHtmlAdapterExtension,
DatabaseBlockNotionHtmlAdapterExtension,
LatexBlockNotionHtmlAdapterExtension,
EmbedYoutubeBlockNotionHtmlAdapterExtension,
EmbedFigmaBlockNotionHtmlAdapterExtension,
EmbedGithubBlockNotionHtmlAdapterExtension,
EmbedLoomBlockNotionHtmlAdapterExtension,
AttachmentBlockNotionHtmlAdapterExtension,
];

View File

@ -1,34 +0,0 @@
import { BookmarkBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-bookmark';
import { CodeBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-code';
import { DatabaseBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-database';
import { DividerBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-divider';
import {
EmbedFigmaBlockPlainTextAdapterExtension,
EmbedGithubBlockPlainTextAdapterExtension,
EmbedIframeBlockPlainTextAdapterExtension,
EmbedLinkedDocBlockPlainTextAdapterExtension,
EmbedLoomBlockPlainTextAdapterExtension,
EmbedSyncedDocBlockPlainTextAdapterExtension,
EmbedYoutubeBlockPlainTextAdapterExtension,
} from '@blocksuite/affine-block-embed';
import { LatexBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-latex';
import { ListBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-list';
import { ParagraphBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-paragraph';
import type { ExtensionType } from '@blocksuite/store';
export const defaultBlockPlainTextAdapterMatchers: ExtensionType[] = [
ParagraphBlockPlainTextAdapterExtension,
ListBlockPlainTextAdapterExtension,
DividerBlockPlainTextAdapterExtension,
CodeBlockPlainTextAdapterExtension,
BookmarkBlockPlainTextAdapterExtension,
EmbedFigmaBlockPlainTextAdapterExtension,
EmbedGithubBlockPlainTextAdapterExtension,
EmbedLoomBlockPlainTextAdapterExtension,
EmbedYoutubeBlockPlainTextAdapterExtension,
EmbedLinkedDocBlockPlainTextAdapterExtension,
EmbedSyncedDocBlockPlainTextAdapterExtension,
EmbedIframeBlockPlainTextAdapterExtension,
LatexBlockPlainTextAdapterExtension,
DatabaseBlockPlainTextAdapterExtension,
];

View File

@ -0,0 +1 @@
export * from '@blocksuite/affine-block-embed-doc';

View File

@ -0,0 +1 @@
export * from '@blocksuite/affine-block-embed-doc/store';

View File

@ -0,0 +1 @@
export * from '@blocksuite/affine-block-embed-doc/view';

View File

@ -7,6 +7,7 @@ import { DatabaseStoreExtension } from '@blocksuite/affine-block-database/store'
import { DividerStoreExtension } from '@blocksuite/affine-block-divider/store';
import { EdgelessTextStoreExtension } from '@blocksuite/affine-block-edgeless-text/store';
import { EmbedStoreExtension } from '@blocksuite/affine-block-embed/store';
import { EmbedDocStoreExtension } from '@blocksuite/affine-block-embed-doc/store';
import { FrameStoreExtension } from '@blocksuite/affine-block-frame/store';
import { ImageStoreExtension } from '@blocksuite/affine-block-image/store';
import { LatexStoreExtension } from '@blocksuite/affine-block-latex/store';
@ -43,6 +44,7 @@ export function getInternalStoreExtensions() {
DividerStoreExtension,
EdgelessTextStoreExtension,
EmbedStoreExtension,
EmbedDocStoreExtension,
FrameStoreExtension,
ImageStoreExtension,
LatexStoreExtension,

View File

@ -7,6 +7,7 @@ import { DatabaseViewExtension } from '@blocksuite/affine-block-database/view';
import { DividerViewExtension } from '@blocksuite/affine-block-divider/view';
import { EdgelessTextViewExtension } from '@blocksuite/affine-block-edgeless-text/view';
import { EmbedViewExtension } from '@blocksuite/affine-block-embed/view';
import { EmbedDocViewExtension } from '@blocksuite/affine-block-embed-doc/view';
import { FrameViewExtension } from '@blocksuite/affine-block-frame/view';
import { ImageViewExtension } from '@blocksuite/affine-block-image/view';
import { LatexViewExtension } from '@blocksuite/affine-block-latex/view';
@ -77,6 +78,7 @@ export function getInternalViewExtensions() {
DividerViewExtension,
EdgelessTextViewExtension,
EmbedViewExtension,
EmbedDocViewExtension,
FrameViewExtension,
ImageViewExtension,
LatexViewExtension,

View File

@ -16,6 +16,7 @@
{ "path": "../blocks/divider" },
{ "path": "../blocks/edgeless-text" },
{ "path": "../blocks/embed" },
{ "path": "../blocks/embed-doc" },
{ "path": "../blocks/frame" },
{ "path": "../blocks/image" },
{ "path": "../blocks/latex" },

View File

@ -11,6 +11,7 @@
"license": "MIT",
"dependencies": {
"@blocksuite/affine-block-embed": "workspace:*",
"@blocksuite/affine-block-embed-doc": "workspace:*",
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",

View File

@ -1,9 +1,9 @@
import { insertEmbedIframeWithUrlCommand } from '@blocksuite/affine-block-embed';
import {
type InsertedLinkType,
insertEmbedIframeWithUrlCommand,
insertEmbedLinkedDocCommand,
type LinkableFlavour,
} from '@blocksuite/affine-block-embed';
} from '@blocksuite/affine-block-embed-doc';
import { QuickSearchProvider } from '@blocksuite/affine-shared/services';
import type { Command } from '@blocksuite/std';

View File

@ -8,6 +8,7 @@
"include": ["./src"],
"references": [
{ "path": "../embed" },
{ "path": "../embed-doc" },
{ "path": "../surface" },
{ "path": "../../components" },
{ "path": "../../ext-loader" },

View File

@ -0,0 +1,54 @@
{
"name": "@blocksuite/affine-block-embed-doc",
"description": "Embed doc blocks for BlockSuite.",
"type": "module",
"scripts": {
"build": "tsc"
},
"sideEffects": false,
"keywords": [],
"author": "toeverything",
"license": "MIT",
"dependencies": {
"@blocksuite/affine-block-embed": "workspace:*",
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
"@blocksuite/affine-inline-reference": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-rich-text": "workspace:*",
"@blocksuite/affine-shared": "workspace:*",
"@blocksuite/affine-widget-slash-menu": "workspace:*",
"@blocksuite/global": "workspace:*",
"@blocksuite/icons": "^2.2.12",
"@blocksuite/std": "workspace:*",
"@blocksuite/store": "workspace:*",
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"yjs": "^13.6.21",
"zod": "^3.23.8"
},
"devDependencies": {
"vitest": "3.1.2"
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},
"files": [
"src",
"dist",
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.21.0"
}

View File

@ -0,0 +1,213 @@
import { getNotesFromDoc } from '@blocksuite/affine-block-embed';
import { ViewExtensionManagerIdentifier } from '@blocksuite/affine-ext-loader';
import {
ImageBlockModel,
ListBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts';
import { matchModels } from '@blocksuite/affine-shared/utils';
import { BlockStdScope } from '@blocksuite/std';
import type { BlockModel, Query } from '@blocksuite/store';
import { render, type TemplateResult } from 'lit';
import type { EmbedLinkedDocBlockComponent } from '../embed-linked-doc-block';
import type { EmbedSyncedDocCard } from '../embed-synced-doc-block/components/embed-synced-doc-card';
export function renderLinkedDocInCard(
card: EmbedLinkedDocBlockComponent | EmbedSyncedDocCard
) {
const linkedDoc = card.linkedDoc;
if (!linkedDoc) {
console.error(
`Trying to load page ${card.model.props.pageId} in linked page block, but the page is not found.`
);
return;
}
// eslint-disable-next-line sonarjs/no-collapsible-if
if ('bannerContainer' in card) {
if (card.editorMode === 'page') {
renderPageAsBanner(card).catch(e => {
console.error(e);
card.isError = true;
});
}
}
renderNoteContent(card).catch(e => {
console.error(e);
card.isError = true;
});
}
async function renderPageAsBanner(card: EmbedSyncedDocCard) {
const linkedDoc = card.linkedDoc;
if (!linkedDoc) {
console.error(
`Trying to load page ${card.model.props.pageId} in linked page block, but the page is not found.`
);
return;
}
const notes = getNotesFromDoc(linkedDoc);
if (!notes) {
card.isBannerEmpty = true;
return;
}
const target = notes.flatMap(note =>
note.children.filter(child => matchModels(child, [ImageBlockModel]))
)[0];
if (target) {
await renderImageAsBanner(card, target);
return;
}
card.isBannerEmpty = true;
}
async function renderImageAsBanner(
card: EmbedSyncedDocCard,
image: BlockModel
) {
const sourceId = (image as ImageBlockModel).props.sourceId;
if (!sourceId) return;
const storage = card.linkedDoc?.blobSync;
if (!storage) return;
const blob = await storage.get(sourceId);
if (!blob) return;
const url = URL.createObjectURL(blob);
const $img = document.createElement('img');
$img.src = url;
await addCover(card, $img);
card.isBannerEmpty = false;
}
async function addCover(
card: EmbedSyncedDocCard,
cover: HTMLElement | TemplateResult<1>
) {
const coverContainer = await card.bannerContainer;
if (!coverContainer) return;
while (coverContainer.firstChild) {
coverContainer.firstChild.remove();
}
if (cover instanceof HTMLElement) {
coverContainer.append(cover);
} else {
render(cover, coverContainer);
}
}
async function renderNoteContent(
card: EmbedLinkedDocBlockComponent | EmbedSyncedDocCard
) {
card.isNoteContentEmpty = true;
const doc = card.linkedDoc;
if (!doc) {
console.error(
`Trying to load page ${card.model.props.pageId} in linked page block, but the page is not found.`
);
return;
}
const notes = getNotesFromDoc(doc);
if (!notes) {
return;
}
const cardStyle = card.model.props.style;
const isHorizontal = cardStyle === 'horizontal';
const allowFlavours = isHorizontal ? [] : [ImageBlockModel];
const noteChildren = notes.flatMap(note =>
note.children.filter(model => {
if (matchModels(model, allowFlavours)) {
return true;
}
return filterTextModel(model);
})
);
if (!noteChildren.length) {
return;
}
card.isNoteContentEmpty = false;
const noteContainer = await card.noteContainer;
if (!noteContainer) {
return;
}
while (noteContainer.firstChild) {
noteContainer.firstChild.remove();
}
const noteBlocksContainer = document.createElement('div');
noteBlocksContainer.classList.add('affine-embed-doc-content-note-blocks');
noteBlocksContainer.contentEditable = 'false';
noteContainer.append(noteBlocksContainer);
if (isHorizontal) {
// When the card is horizontal, we only render the first block
noteChildren.splice(1);
} else {
// Before rendering, we can not know the height of each block
// But we can limit the number of blocks to render simply by the height of the card
const cardHeight = EMBED_CARD_HEIGHT[cardStyle];
const minSingleBlockHeight = 20;
const maxBlockCount = Math.floor(cardHeight / minSingleBlockHeight);
if (noteChildren.length > maxBlockCount) {
noteChildren.splice(maxBlockCount);
}
}
const childIds = noteChildren.map(child => child.id);
const ids: string[] = [];
childIds.forEach(block => {
let parent: string | null = block;
while (parent && !ids.includes(parent)) {
ids.push(parent);
parent = doc.getParent(parent)?.id ?? null;
}
});
const query: Query = {
mode: 'strict',
match: ids.map(id => ({ id, viewType: 'display' })),
};
const previewDoc = doc.doc.getStore({ query });
const std = card.host.std;
const previewSpec = std
.get(ViewExtensionManagerIdentifier)
.get('preview-page');
const previewStd = new BlockStdScope({
store: previewDoc,
extensions: previewSpec,
});
const previewTemplate = previewStd.render();
const fragment = document.createDocumentFragment();
render(previewTemplate, fragment);
noteBlocksContainer.append(fragment);
const contentEditableElements = noteBlocksContainer.querySelectorAll(
'[contenteditable="true"]'
);
contentEditableElements.forEach(element => {
(element as HTMLElement).contentEditable = 'false';
});
}
function filterTextModel(model: BlockModel) {
if (matchModels(model, [ParagraphBlockModel, ListBlockModel])) {
return !!model.text?.toString().length;
}
return false;
}

View File

@ -0,0 +1,37 @@
import { EmbedLinkedDocBlockComponent } from './embed-linked-doc-block';
import { EmbedEdgelessLinkedDocBlockComponent } from './embed-linked-doc-block/embed-edgeless-linked-doc-block';
import { EmbedSyncedDocBlockComponent } from './embed-synced-doc-block';
import { EmbedSyncedDocCard } from './embed-synced-doc-block/components/embed-synced-doc-card';
import { EmbedEdgelessSyncedDocBlockComponent } from './embed-synced-doc-block/embed-edgeless-synced-doc-block';
export function effects() {
customElements.define('affine-embed-synced-doc-card', EmbedSyncedDocCard);
customElements.define(
'affine-embed-edgeless-linked-doc-block',
EmbedEdgelessLinkedDocBlockComponent
);
customElements.define(
'affine-embed-linked-doc-block',
EmbedLinkedDocBlockComponent
);
customElements.define(
'affine-embed-edgeless-synced-doc-block',
EmbedEdgelessSyncedDocBlockComponent
);
customElements.define(
'affine-embed-synced-doc-block',
EmbedSyncedDocBlockComponent
);
}
declare global {
interface HTMLElementTagNameMap {
'affine-embed-synced-doc-card': EmbedSyncedDocCard;
'affine-embed-synced-doc-block': EmbedSyncedDocBlockComponent;
'affine-embed-edgeless-synced-doc-block': EmbedEdgelessSyncedDocBlockComponent;
'affine-embed-linked-doc-block': EmbedLinkedDocBlockComponent;
'affine-embed-edgeless-linked-doc-block': EmbedEdgelessLinkedDocBlockComponent;
}
}

View File

@ -1,8 +1,7 @@
import { insertEmbedCard } from '@blocksuite/affine-block-embed';
import type { EmbedCardStyle, ReferenceParams } from '@blocksuite/affine-model';
import type { Command } from '@blocksuite/std';
import { insertEmbedCard } from '../../common/insert-embed-card.js';
export type LinkableFlavour =
| 'affine:bookmark'
| 'affine:embed-linked-doc'

View File

@ -1,3 +1,4 @@
import { toEdgelessEmbedBlock } from '@blocksuite/affine-block-embed';
import {
EdgelessCRUDIdentifier,
reassociateConnectorsCommand,
@ -13,7 +14,6 @@ import {
} from '@blocksuite/affine-shared/utils';
import { Bound } from '@blocksuite/global/gfx';
import { toEdgelessEmbedBlock } from '../common/to-edgeless-embed-block.js';
import { EmbedLinkedDocBlockComponent } from './embed-linked-doc-block.js';
export class EmbedEdgelessLinkedDocBlockComponent extends toEdgelessEmbedBlock(

View File

@ -1,3 +1,7 @@
import {
EmbedBlockComponent,
RENDER_CARD_THROTTLE_MS,
} from '@blocksuite/affine-block-embed';
import { SurfaceBlockModel } from '@blocksuite/affine-block-surface';
import { isPeekable, Peekable } from '@blocksuite/affine-components/peek';
import { RefNodeSlotsProvider } from '@blocksuite/affine-inline-reference';
@ -39,11 +43,7 @@ import { when } from 'lit/directives/when.js';
import throttle from 'lodash-es/throttle';
import * as Y from 'yjs';
import { EmbedBlockComponent } from '../common/embed-block-element.js';
import {
RENDER_CARD_THROTTLE_MS,
renderLinkedDocInCard,
} from '../common/render-linked-doc.js';
import { renderLinkedDocInCard } from '../common/render-linked-doc';
import { SyncedDocErrorIcon } from '../embed-synced-doc-block/styles.js';
import { styles } from './styles.js';
import { getEmbedLinkedDocIcons } from './utils.js';

View File

@ -1,8 +1,7 @@
import { embedNoteContentStyles } from '@blocksuite/affine-block-embed';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { css, html } from 'lit';
import { embedNoteContentStyles } from '../common/embed-note-content-styles.js';
unsafeCSSVarV2('layer/background/linkedDocOnEdgeless');
export const styles = css`

View File

@ -1,3 +1,4 @@
import { RENDER_CARD_THROTTLE_MS } from '@blocksuite/affine-block-embed';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { WithDisposable } from '@blocksuite/global/lit';
import {
@ -10,10 +11,7 @@ import { property, queryAsync } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import throttle from 'lodash-es/throttle';
import {
RENDER_CARD_THROTTLE_MS,
renderLinkedDocInCard,
} from '../../common/render-linked-doc.js';
import { renderLinkedDocInCard } from '../../common/render-linked-doc';
import type { EmbedSyncedDocBlockComponent } from '../embed-synced-doc-block.js';
import { cardStyles } from '../styles.js';
import { getSyncedDocIcons } from '../utils.js';

View File

@ -1,3 +1,4 @@
import { toEdgelessEmbedBlock } from '@blocksuite/affine-block-embed';
import {
EdgelessCRUDIdentifier,
reassociateConnectorsCommand,
@ -20,9 +21,8 @@ import { guard } from 'lit/directives/guard.js';
import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
import { toEdgelessEmbedBlock } from '../common/to-edgeless-embed-block.js';
import { EmbedSyncedDocConfigExtension } from './configs/index.js';
import { EmbedSyncedDocBlockComponent } from './embed-synced-doc-block.js';
import { EmbedSyncedDocConfigExtension } from './configs';
import { EmbedSyncedDocBlockComponent } from './embed-synced-doc-block';
export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock(
EmbedSyncedDocBlockComponent

View File

@ -1,3 +1,7 @@
import {
EmbedBlockComponent,
isEmptyDoc,
} from '@blocksuite/affine-block-embed';
import { Peekable } from '@blocksuite/affine-components/peek';
import { ViewExtensionManagerIdentifier } from '@blocksuite/affine-ext-loader';
import {
@ -40,8 +44,6 @@ import { guard } from 'lit/directives/guard.js';
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
import * as Y from 'yjs';
import { EmbedBlockComponent } from '../common/embed-block-element.js';
import { isEmptyDoc } from '../common/render-linked-doc.js';
import type { EmbedSyncedDocCard } from './components/embed-synced-doc-card.js';
import { blockStyles } from './styles.js';

View File

@ -1,3 +1,4 @@
import { embedNoteContentStyles } from '@blocksuite/affine-block-embed';
import {
EMBED_CARD_HEIGHT,
EMBED_CARD_WIDTH,
@ -5,8 +6,6 @@ import {
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { css, html } from 'lit';
import { embedNoteContentStyles } from '../common/embed-note-content-styles.js';
export const blockStyles = css`
affine-embed-synced-doc-block {
--embed-padding: 24px;

View File

@ -0,0 +1,2 @@
export * from './embed-linked-doc-block';
export * from './embed-synced-doc-block';

View File

@ -0,0 +1,25 @@
import {
type StoreExtensionContext,
StoreExtensionProvider,
} from '@blocksuite/affine-ext-loader';
import {
EmbedLinkedDocBlockSchemaExtension,
EmbedSyncedDocBlockSchemaExtension,
} from '@blocksuite/affine-model';
import { EmbedLinkedDocBlockAdapterExtensions } from './embed-linked-doc-block/adapters/extension';
import { EmbedSyncedDocBlockAdapterExtensions } from './embed-synced-doc-block/adapters/extension';
export class EmbedDocStoreExtension extends StoreExtensionProvider {
override name = 'affine-embed-doc-block';
override setup(context: StoreExtensionContext) {
super.setup(context);
context.register([
EmbedSyncedDocBlockSchemaExtension,
EmbedLinkedDocBlockSchemaExtension,
]);
context.register(EmbedLinkedDocBlockAdapterExtensions);
context.register(EmbedSyncedDocBlockAdapterExtensions);
}
}

View File

@ -0,0 +1,36 @@
import {
type ViewExtensionContext,
ViewExtensionProvider,
} from '@blocksuite/affine-ext-loader';
import { effects } from './effects';
import {
EdgelessClipboardEmbedLinkedDocConfig,
EmbedLinkedDocViewExtensions,
} from './embed-linked-doc-block';
import {
EdgelessClipboardEmbedSyncedDocConfig,
EmbedSyncedDocViewExtensions,
} from './embed-synced-doc-block';
export class EmbedDocViewExtension extends ViewExtensionProvider {
override name = 'affine-embed-doc-block';
override effect(): void {
super.effect();
effects();
}
override setup(context: ViewExtensionContext) {
super.setup(context);
context.register(EmbedLinkedDocViewExtensions);
context.register(EmbedSyncedDocViewExtensions);
const isEdgeless = this.isEdgeless(context.scope);
if (isEdgeless) {
context.register([
EdgelessClipboardEmbedLinkedDocConfig,
EdgelessClipboardEmbedSyncedDocConfig,
]);
}
}
}

View File

@ -0,0 +1,23 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
},
"include": ["./src"],
"references": [
{ "path": "../embed" },
{ "path": "../surface" },
{ "path": "../../components" },
{ "path": "../../ext-loader" },
{ "path": "../../inlines/reference" },
{ "path": "../../model" },
{ "path": "../../rich-text" },
{ "path": "../../shared" },
{ "path": "../../widgets/slash-menu" },
{ "path": "../../../framework/global" },
{ "path": "../../../framework/std" },
{ "path": "../../../framework/store" }
]
}

View File

@ -1,30 +1,22 @@
import { getSurfaceBlock } from '@blocksuite/affine-block-surface';
import { ViewExtensionManagerIdentifier } from '@blocksuite/affine-ext-loader';
import {
type DocMode,
ImageBlockModel,
ListBlockModel,
NoteBlockModel,
NoteDisplayMode,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts';
import { NotificationProvider } from '@blocksuite/affine-shared/services';
import { matchModels } from '@blocksuite/affine-shared/utils';
import { BlockStdScope } from '@blocksuite/std';
import type { BlockStdScope } from '@blocksuite/std';
import {
type BlockModel,
type BlockSnapshot,
type DraftModel,
type Query,
Slice,
type Store,
Text,
} from '@blocksuite/store';
import { render, type TemplateResult } from 'lit';
import type { EmbedLinkedDocBlockComponent } from '../embed-linked-doc-block/index.js';
import type { EmbedSyncedDocCard } from '../embed-synced-doc-block/components/embed-synced-doc-card.js';
// Throttle delay for block updates to reduce unnecessary re-renders
// - Prevents rapid-fire updates when multiple blocks are updated in quick succession
@ -32,197 +24,6 @@ import type { EmbedSyncedDocCard } from '../embed-synced-doc-block/components/em
// - Small enough to feel instant to users, large enough to batch updates effectively
export const RENDER_CARD_THROTTLE_MS = 60;
export function renderLinkedDocInCard(
card: EmbedLinkedDocBlockComponent | EmbedSyncedDocCard
) {
const linkedDoc = card.linkedDoc;
if (!linkedDoc) {
console.error(
`Trying to load page ${card.model.props.pageId} in linked page block, but the page is not found.`
);
return;
}
// eslint-disable-next-line sonarjs/no-collapsible-if
if ('bannerContainer' in card) {
if (card.editorMode === 'page') {
renderPageAsBanner(card).catch(e => {
console.error(e);
card.isError = true;
});
}
}
renderNoteContent(card).catch(e => {
console.error(e);
card.isError = true;
});
}
async function renderPageAsBanner(card: EmbedSyncedDocCard) {
const linkedDoc = card.linkedDoc;
if (!linkedDoc) {
console.error(
`Trying to load page ${card.model.props.pageId} in linked page block, but the page is not found.`
);
return;
}
const notes = getNotesFromDoc(linkedDoc);
if (!notes) {
card.isBannerEmpty = true;
return;
}
const target = notes.flatMap(note =>
note.children.filter(child => matchModels(child, [ImageBlockModel]))
)[0];
if (target) {
await renderImageAsBanner(card, target);
return;
}
card.isBannerEmpty = true;
}
async function renderImageAsBanner(
card: EmbedSyncedDocCard,
image: BlockModel
) {
const sourceId = (image as ImageBlockModel).props.sourceId;
if (!sourceId) return;
const storage = card.linkedDoc?.blobSync;
if (!storage) return;
const blob = await storage.get(sourceId);
if (!blob) return;
const url = URL.createObjectURL(blob);
const $img = document.createElement('img');
$img.src = url;
await addCover(card, $img);
card.isBannerEmpty = false;
}
async function addCover(
card: EmbedSyncedDocCard,
cover: HTMLElement | TemplateResult<1>
) {
const coverContainer = await card.bannerContainer;
if (!coverContainer) return;
while (coverContainer.firstChild) {
coverContainer.firstChild.remove();
}
if (cover instanceof HTMLElement) {
coverContainer.append(cover);
} else {
render(cover, coverContainer);
}
}
async function renderNoteContent(
card: EmbedLinkedDocBlockComponent | EmbedSyncedDocCard
) {
card.isNoteContentEmpty = true;
const doc = card.linkedDoc;
if (!doc) {
console.error(
`Trying to load page ${card.model.props.pageId} in linked page block, but the page is not found.`
);
return;
}
const notes = getNotesFromDoc(doc);
if (!notes) {
return;
}
const cardStyle = card.model.props.style;
const isHorizontal = cardStyle === 'horizontal';
const allowFlavours = isHorizontal ? [] : [ImageBlockModel];
const noteChildren = notes.flatMap(note =>
note.children.filter(model => {
if (matchModels(model, allowFlavours)) {
return true;
}
return filterTextModel(model);
})
);
if (!noteChildren.length) {
return;
}
card.isNoteContentEmpty = false;
const noteContainer = await card.noteContainer;
if (!noteContainer) {
return;
}
while (noteContainer.firstChild) {
noteContainer.firstChild.remove();
}
const noteBlocksContainer = document.createElement('div');
noteBlocksContainer.classList.add('affine-embed-doc-content-note-blocks');
noteBlocksContainer.contentEditable = 'false';
noteContainer.append(noteBlocksContainer);
if (isHorizontal) {
// When the card is horizontal, we only render the first block
noteChildren.splice(1);
} else {
// Before rendering, we can not know the height of each block
// But we can limit the number of blocks to render simply by the height of the card
const cardHeight = EMBED_CARD_HEIGHT[cardStyle];
const minSingleBlockHeight = 20;
const maxBlockCount = Math.floor(cardHeight / minSingleBlockHeight);
if (noteChildren.length > maxBlockCount) {
noteChildren.splice(maxBlockCount);
}
}
const childIds = noteChildren.map(child => child.id);
const ids: string[] = [];
childIds.forEach(block => {
let parent: string | null = block;
while (parent && !ids.includes(parent)) {
ids.push(parent);
parent = doc.getParent(parent)?.id ?? null;
}
});
const query: Query = {
mode: 'strict',
match: ids.map(id => ({ id, viewType: 'display' })),
};
const previewDoc = doc.doc.getStore({ query });
const std = card.host.std;
const previewSpec = std
.get(ViewExtensionManagerIdentifier)
.get('preview-page');
const previewStd = new BlockStdScope({
store: previewDoc,
extensions: previewSpec,
});
const previewTemplate = previewStd.render();
const fragment = document.createDocumentFragment();
render(previewTemplate, fragment);
noteBlocksContainer.append(fragment);
const contentEditableElements = noteBlocksContainer.querySelectorAll(
'[contenteditable="true"]'
);
contentEditableElements.forEach(element => {
(element as HTMLElement).contentEditable = 'false';
});
}
function filterTextModel(model: BlockModel) {
if (matchModels(model, [ParagraphBlockModel, ListBlockModel])) {
return !!model.text?.toString().length;

View File

@ -12,13 +12,8 @@ import { EmbedIframeLinkInputPopup } from './embed-iframe-block/components/embed
import { EmbedIframeLoadingCard } from './embed-iframe-block/components/embed-iframe-loading-card';
import { EmbedEdgelessIframeBlockComponent } from './embed-iframe-block/embed-edgeless-iframe-block';
import { EmbedIframeBlockComponent } from './embed-iframe-block/embed-iframe-block';
import { EmbedLinkedDocBlockComponent } from './embed-linked-doc-block';
import { EmbedEdgelessLinkedDocBlockComponent } from './embed-linked-doc-block/embed-edgeless-linked-doc-block';
import { EmbedLoomBlockComponent } from './embed-loom-block';
import { EmbedEdgelessLoomBlockComponent } from './embed-loom-block/embed-edgeless-loom-bock';
import { EmbedSyncedDocBlockComponent } from './embed-synced-doc-block';
import { EmbedSyncedDocCard } from './embed-synced-doc-block/components/embed-synced-doc-card';
import { EmbedEdgelessSyncedDocBlockComponent } from './embed-synced-doc-block/embed-edgeless-synced-doc-block';
import { EmbedYoutubeBlockComponent } from './embed-youtube-block';
import { EmbedEdgelessYoutubeBlockComponent } from './embed-youtube-block/embed-edgeless-youtube-block';
@ -60,26 +55,6 @@ export function effects() {
);
customElements.define('affine-embed-loom-block', EmbedLoomBlockComponent);
customElements.define('affine-embed-synced-doc-card', EmbedSyncedDocCard);
customElements.define(
'affine-embed-edgeless-linked-doc-block',
EmbedEdgelessLinkedDocBlockComponent
);
customElements.define(
'affine-embed-linked-doc-block',
EmbedLinkedDocBlockComponent
);
customElements.define(
'affine-embed-edgeless-synced-doc-block',
EmbedEdgelessSyncedDocBlockComponent
);
customElements.define(
'affine-embed-synced-doc-block',
EmbedSyncedDocBlockComponent
);
customElements.define(
'affine-embed-edgeless-iframe-block',
EmbedEdgelessIframeBlockComponent
@ -111,11 +86,6 @@ declare global {
'affine-embed-loom-block': EmbedLoomBlockComponent;
'affine-embed-youtube-block': EmbedYoutubeBlockComponent;
'affine-embed-edgeless-youtube-block': EmbedEdgelessYoutubeBlockComponent;
'affine-embed-synced-doc-card': EmbedSyncedDocCard;
'affine-embed-synced-doc-block': EmbedSyncedDocBlockComponent;
'affine-embed-edgeless-synced-doc-block': EmbedEdgelessSyncedDocBlockComponent;
'affine-embed-linked-doc-block': EmbedLinkedDocBlockComponent;
'affine-embed-edgeless-linked-doc-block': EmbedEdgelessLinkedDocBlockComponent;
'affine-embed-iframe-block': EmbedIframeBlockComponent;
'embed-iframe-link-input-popup': EmbedIframeLinkInputPopup;
'embed-iframe-loading-card': EmbedIframeLoadingCard;

View File

@ -4,9 +4,7 @@ import { EmbedFigmaBlockSpec } from './embed-figma-block';
import { EmbedGithubBlockSpec } from './embed-github-block';
import { EmbedHtmlBlockSpec } from './embed-html-block';
import { EmbedIframeBlockSpec } from './embed-iframe-block';
import { EmbedLinkedDocBlockSpec } from './embed-linked-doc-block';
import { EmbedLoomBlockSpec } from './embed-loom-block';
import { EmbedSyncedDocBlockSpec } from './embed-synced-doc-block';
import { EmbedYoutubeBlockSpec } from './embed-youtube-block';
export const EmbedExtensions: ExtensionType[] = [
@ -15,11 +13,7 @@ export const EmbedExtensions: ExtensionType[] = [
EmbedGithubBlockSpec,
EmbedLoomBlockSpec,
EmbedYoutubeBlockSpec,
// Internal embed blocks
EmbedHtmlBlockSpec,
EmbedLinkedDocBlockSpec,
EmbedSyncedDocBlockSpec,
EmbedIframeBlockSpec,
].flat();
@ -27,6 +21,7 @@ export { createEmbedBlockHtmlAdapterMatcher } from './common/adapters/html';
export { createEmbedBlockMarkdownAdapterMatcher } from './common/adapters/markdown';
export { createEmbedBlockPlainTextAdapterMatcher } from './common/adapters/plain-text';
export { EmbedBlockComponent } from './common/embed-block-element';
export * from './common/embed-note-content-styles';
export { insertEmbedCard } from './common/insert-embed-card';
export * from './common/render-linked-doc';
export { toEdgelessEmbedBlock } from './common/to-edgeless-embed-block';
@ -35,8 +30,6 @@ export * from './embed-figma-block';
export * from './embed-github-block';
export * from './embed-html-block';
export * from './embed-iframe-block';
export * from './embed-linked-doc-block';
export * from './embed-loom-block';
export * from './embed-synced-doc-block';
export * from './embed-youtube-block';
export * from './types';

View File

@ -18,9 +18,7 @@ import { EmbedFigmaBlockAdapterExtensions } from './embed-figma-block/adapters/e
import { EmbedGithubBlockAdapterExtensions } from './embed-github-block/adapters/extension';
import { EmbedIframeConfigExtensions } from './embed-iframe-block';
import { EmbedIframeBlockAdapterExtensions } from './embed-iframe-block/adapters';
import { EmbedLinkedDocBlockAdapterExtensions } from './embed-linked-doc-block/adapters/extension';
import { EmbedLoomBlockAdapterExtensions } from './embed-loom-block/adapters/extension';
import { EmbedSyncedDocBlockAdapterExtensions } from './embed-synced-doc-block/adapters/extension';
import { EmbedYoutubeBlockAdapterExtensions } from './embed-youtube-block/adapters/extension';
export class EmbedStoreExtension extends StoreExtensionProvider {
@ -43,8 +41,6 @@ export class EmbedStoreExtension extends StoreExtensionProvider {
context.register(EmbedYoutubeBlockAdapterExtensions);
context.register(EmbedLoomBlockAdapterExtensions);
context.register(EmbedIframeBlockAdapterExtensions);
context.register(EmbedLinkedDocBlockAdapterExtensions);
context.register(EmbedSyncedDocBlockAdapterExtensions);
context.register(EmbedIframeConfigExtensions);
context.register(EmbedIframeService);

View File

@ -2,9 +2,7 @@ import type { BlockComponent } from '@blocksuite/std';
import { EmbedFigmaBlockComponent } from './embed-figma-block';
import { EmbedGithubBlockComponent } from './embed-github-block';
import type { EmbedLinkedDocBlockComponent } from './embed-linked-doc-block';
import { EmbedLoomBlockComponent } from './embed-loom-block';
import type { EmbedSyncedDocBlockComponent } from './embed-synced-doc-block';
import { EmbedYoutubeBlockComponent } from './embed-youtube-block';
export type ExternalEmbedBlockComponent =
@ -13,14 +11,6 @@ export type ExternalEmbedBlockComponent =
| EmbedLoomBlockComponent
| EmbedYoutubeBlockComponent;
export type InternalEmbedBlockComponent =
| EmbedLinkedDocBlockComponent
| EmbedSyncedDocBlockComponent;
export type LinkableEmbedBlockComponent =
| ExternalEmbedBlockComponent
| InternalEmbedBlockComponent;
export function isExternalEmbedBlockComponent(
block: BlockComponent
): block is ExternalEmbedBlockComponent {

View File

@ -20,18 +20,10 @@ import {
EdgelessClipboardEmbedIframeConfig,
EmbedIframeViewExtensions,
} from './embed-iframe-block';
import {
EdgelessClipboardEmbedLinkedDocConfig,
EmbedLinkedDocViewExtensions,
} from './embed-linked-doc-block';
import {
EdgelessClipboardEmbedLoomConfig,
EmbedLoomViewExtensions,
} from './embed-loom-block';
import {
EdgelessClipboardEmbedSyncedDocConfig,
EmbedSyncedDocViewExtensions,
} from './embed-synced-doc-block';
import {
EdgelessClipboardEmbedYoutubeConfig,
EmbedYoutubeViewExtensions,
@ -52,8 +44,6 @@ export class EmbedViewExtension extends ViewExtensionProvider {
context.register(EmbedLoomViewExtensions);
context.register(EmbedYoutubeViewExtensions);
context.register(EmbedHtmlViewExtensions);
context.register(EmbedLinkedDocViewExtensions);
context.register(EmbedSyncedDocViewExtensions);
context.register(EmbedIframeViewExtensions);
const isEdgeless = this.isEdgeless(context.scope);
if (isEdgeless) {
@ -64,8 +54,6 @@ export class EmbedViewExtension extends ViewExtensionProvider {
EdgelessClipboardEmbedLoomConfig,
EdgelessClipboardEmbedYoutubeConfig,
EdgelessClipboardEmbedIframeConfig,
EdgelessClipboardEmbedLinkedDocConfig,
EdgelessClipboardEmbedSyncedDocConfig,
]);
}
}

View File

@ -15,6 +15,7 @@
"@blocksuite/affine-block-database": "workspace:*",
"@blocksuite/affine-block-edgeless-text": "workspace:*",
"@blocksuite/affine-block-embed": "workspace:*",
"@blocksuite/affine-block-embed-doc": "workspace:*",
"@blocksuite/affine-block-frame": "workspace:*",
"@blocksuite/affine-block-image": "workspace:*",
"@blocksuite/affine-block-note": "workspace:*",

View File

@ -2,9 +2,11 @@ import type { EdgelessTextBlockComponent } from '@blocksuite/affine-block-edgele
import {
EMBED_HTML_MIN_HEIGHT,
EMBED_HTML_MIN_WIDTH,
} from '@blocksuite/affine-block-embed';
import {
SYNCED_MIN_HEIGHT,
SYNCED_MIN_WIDTH,
} from '@blocksuite/affine-block-embed';
} from '@blocksuite/affine-block-embed-doc';
import {
EdgelessFrameManagerIdentifier,
type FrameOverlay,

View File

@ -10,7 +10,6 @@ export * from './edgeless/index.js';
export * from './page/page-root-block.js';
export * from './preview/preview-root-block.js';
export * from './types.js';
export * from './utils/index.js';
declare type _GLOBAL_ =
| typeof PointerEffect

View File

@ -1,23 +0,0 @@
import type { BlockComponent, EditorHost } from '@blocksuite/std';
import type { BlockModel } from '@blocksuite/store';
// Run the callback until a model's element updated.
// Please notice that the callback will be called **once the element itself is ready**.
// The children may be not updated.
// If you want to wait for the text elements,
// please use `onModelTextUpdated`.
export async function onModelElementUpdated(
editorHost: EditorHost,
model: BlockModel,
callback: (block: BlockComponent) => void
) {
const page = model.doc;
if (!page.root) return;
const rootComponent = editorHost.view.getBlock(page.root.id);
if (!rootComponent) return;
await rootComponent.updateComplete;
const element = editorHost.view.getBlock(model.id);
if (element) callback(element);
}

View File

@ -1,2 +0,0 @@
export * from './callback';
export * from './types';

View File

@ -1,7 +0,0 @@
import type { RootBlockComponent } from '../types.js';
export function getClosestRootBlockComponent(
el: HTMLElement
): RootBlockComponent | null {
return el.closest('affine-edgeless-root, affine-page-root');
}

View File

@ -1,32 +0,0 @@
import { BookmarkBlockComponent } from '@blocksuite/affine-block-bookmark';
import {
EmbedFigmaBlockComponent,
EmbedGithubBlockComponent,
EmbedHtmlBlockComponent,
EmbedLinkedDocBlockComponent,
EmbedLoomBlockComponent,
EmbedSyncedDocBlockComponent,
EmbedYoutubeBlockComponent,
type LinkableEmbedBlockComponent,
} from '@blocksuite/affine-block-embed';
import type { BlockComponent } from '@blocksuite/std';
export type BuiltInEmbedBlockComponent =
| BookmarkBlockComponent
| LinkableEmbedBlockComponent
| EmbedHtmlBlockComponent;
export function isEmbedCardBlockComponent(
block: BlockComponent
): block is BuiltInEmbedBlockComponent {
return (
block instanceof BookmarkBlockComponent ||
block instanceof EmbedFigmaBlockComponent ||
block instanceof EmbedGithubBlockComponent ||
block instanceof EmbedHtmlBlockComponent ||
block instanceof EmbedLoomBlockComponent ||
block instanceof EmbedYoutubeBlockComponent ||
block instanceof EmbedLinkedDocBlockComponent ||
block instanceof EmbedSyncedDocBlockComponent
);
}

View File

@ -12,6 +12,7 @@
{ "path": "../database" },
{ "path": "../edgeless-text" },
{ "path": "../embed" },
{ "path": "../embed-doc" },
{ "path": "../frame" },
{ "path": "../image" },
{ "path": "../note" },

View File

@ -3,7 +3,7 @@ import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { WorkbenchService } from '@affine/core/modules/workbench';
import { stopPropagation } from '@affine/core/utils';
import { useI18n } from '@affine/i18n';
import { EmbedSyncedDocBlockComponent } from '@blocksuite/affine/blocks/embed';
import { EmbedSyncedDocBlockComponent } from '@blocksuite/affine/blocks/embed-doc';
import { isPeekable, peek } from '@blocksuite/affine/components/peek';
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
import { Bound } from '@blocksuite/affine/global/gfx';

View File

@ -1,6 +1,6 @@
import type { ElementOrFactory } from '@affine/component';
import { JournalService } from '@affine/core/modules/journal';
import { EmbedSyncedDocConfigExtension } from '@blocksuite/affine/blocks/embed';
import { EmbedSyncedDocConfigExtension } from '@blocksuite/affine/blocks/embed-doc';
import { NoteConfigExtension } from '@blocksuite/affine/blocks/note';
import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine/blocks/root';
import { Bound, Vec } from '@blocksuite/affine/global/gfx';

View File

@ -14,12 +14,14 @@ import {
EmbedFigmaBlockComponent,
EmbedGithubBlockComponent,
EmbedIframeBlockComponent,
EmbedLinkedDocBlockComponent,
EmbedLoomBlockComponent,
EmbedSyncedDocBlockComponent,
EmbedYoutubeBlockComponent,
getDocContentWithMaxLength,
} from '@blocksuite/affine/blocks/embed';
import {
EmbedLinkedDocBlockComponent,
EmbedSyncedDocBlockComponent,
} from '@blocksuite/affine/blocks/embed-doc';
import { SurfaceRefBlockComponent } from '@blocksuite/affine/blocks/surface-ref';
import { toggleEmbedCardEditModal } from '@blocksuite/affine/components/embed-card-modal';
import {

View File

@ -14,7 +14,7 @@ import {
BookmarkSlashMenuConfigIdentifier,
insertLinkByQuickSearchCommand,
} from '@blocksuite/affine/blocks/bookmark';
import { LinkedDocSlashMenuConfigIdentifier } from '@blocksuite/affine/blocks/embed';
import { LinkedDocSlashMenuConfigIdentifier } from '@blocksuite/affine/blocks/embed-doc';
import type { ServiceIdentifier } from '@blocksuite/affine/global/di';
import {
QuickSearchExtension,

View File

@ -14,6 +14,7 @@ export const PackageList = [
'blocksuite/affine/blocks/divider',
'blocksuite/affine/blocks/edgeless-text',
'blocksuite/affine/blocks/embed',
'blocksuite/affine/blocks/embed-doc',
'blocksuite/affine/blocks/frame',
'blocksuite/affine/blocks/image',
'blocksuite/affine/blocks/latex',
@ -91,6 +92,7 @@ export const PackageList = [
name: '@blocksuite/affine-block-bookmark',
workspaceDependencies: [
'blocksuite/affine/blocks/embed',
'blocksuite/affine/blocks/embed-doc',
'blocksuite/affine/blocks/surface',
'blocksuite/affine/components',
'blocksuite/affine/ext-loader',
@ -220,6 +222,24 @@ export const PackageList = [
'blocksuite/framework/store',
],
},
{
location: 'blocksuite/affine/blocks/embed-doc',
name: '@blocksuite/affine-block-embed-doc',
workspaceDependencies: [
'blocksuite/affine/blocks/embed',
'blocksuite/affine/blocks/surface',
'blocksuite/affine/components',
'blocksuite/affine/ext-loader',
'blocksuite/affine/inlines/reference',
'blocksuite/affine/model',
'blocksuite/affine/rich-text',
'blocksuite/affine/shared',
'blocksuite/affine/widgets/slash-menu',
'blocksuite/framework/global',
'blocksuite/framework/std',
'blocksuite/framework/store',
],
},
{
location: 'blocksuite/affine/blocks/frame',
name: '@blocksuite/affine-block-frame',
@ -331,6 +351,7 @@ export const PackageList = [
'blocksuite/affine/blocks/database',
'blocksuite/affine/blocks/edgeless-text',
'blocksuite/affine/blocks/embed',
'blocksuite/affine/blocks/embed-doc',
'blocksuite/affine/blocks/frame',
'blocksuite/affine/blocks/image',
'blocksuite/affine/blocks/note',
@ -1377,6 +1398,7 @@ export type PackageName =
| '@blocksuite/affine-block-divider'
| '@blocksuite/affine-block-edgeless-text'
| '@blocksuite/affine-block-embed'
| '@blocksuite/affine-block-embed-doc'
| '@blocksuite/affine-block-frame'
| '@blocksuite/affine-block-image'
| '@blocksuite/affine-block-latex'

View File

@ -60,6 +60,7 @@
{ "path": "./blocksuite/affine/blocks/divider" },
{ "path": "./blocksuite/affine/blocks/edgeless-text" },
{ "path": "./blocksuite/affine/blocks/embed" },
{ "path": "./blocksuite/affine/blocks/embed-doc" },
{ "path": "./blocksuite/affine/blocks/frame" },
{ "path": "./blocksuite/affine/blocks/image" },
{ "path": "./blocksuite/affine/blocks/latex" },

View File

@ -2435,6 +2435,7 @@ __metadata:
resolution: "@blocksuite/affine-block-bookmark@workspace:blocksuite/affine/blocks/bookmark"
dependencies:
"@blocksuite/affine-block-embed": "workspace:*"
"@blocksuite/affine-block-embed-doc": "workspace:*"
"@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-ext-loader": "workspace:*"
@ -2628,6 +2629,38 @@ __metadata:
languageName: unknown
linkType: soft
"@blocksuite/affine-block-embed-doc@workspace:*, @blocksuite/affine-block-embed-doc@workspace:blocksuite/affine/blocks/embed-doc":
version: 0.0.0-use.local
resolution: "@blocksuite/affine-block-embed-doc@workspace:blocksuite/affine/blocks/embed-doc"
dependencies:
"@blocksuite/affine-block-embed": "workspace:*"
"@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-ext-loader": "workspace:*"
"@blocksuite/affine-inline-reference": "workspace:*"
"@blocksuite/affine-model": "workspace:*"
"@blocksuite/affine-rich-text": "workspace:*"
"@blocksuite/affine-shared": "workspace:*"
"@blocksuite/affine-widget-slash-menu": "workspace:*"
"@blocksuite/global": "workspace:*"
"@blocksuite/icons": "npm:^2.2.12"
"@blocksuite/std": "workspace:*"
"@blocksuite/store": "workspace:*"
"@floating-ui/dom": "npm:^1.6.13"
"@lit/context": "npm:^1.1.2"
"@preact/signals-core": "npm:^1.8.0"
"@toeverything/theme": "npm:^1.1.14"
"@types/lodash-es": "npm:^4.17.12"
lit: "npm:^3.2.0"
lodash-es: "npm:^4.17.21"
minimatch: "npm:^10.0.1"
rxjs: "npm:^7.8.1"
vitest: "npm:3.1.2"
yjs: "npm:^13.6.21"
zod: "npm:^3.23.8"
languageName: unknown
linkType: soft
"@blocksuite/affine-block-embed@workspace:*, @blocksuite/affine-block-embed@workspace:blocksuite/affine/blocks/embed":
version: 0.0.0-use.local
resolution: "@blocksuite/affine-block-embed@workspace:blocksuite/affine/blocks/embed"
@ -2842,6 +2875,7 @@ __metadata:
"@blocksuite/affine-block-database": "workspace:*"
"@blocksuite/affine-block-edgeless-text": "workspace:*"
"@blocksuite/affine-block-embed": "workspace:*"
"@blocksuite/affine-block-embed-doc": "workspace:*"
"@blocksuite/affine-block-frame": "workspace:*"
"@blocksuite/affine-block-image": "workspace:*"
"@blocksuite/affine-block-note": "workspace:*"
@ -4030,6 +4064,7 @@ __metadata:
"@blocksuite/affine-block-divider": "workspace:*"
"@blocksuite/affine-block-edgeless-text": "workspace:*"
"@blocksuite/affine-block-embed": "workspace:*"
"@blocksuite/affine-block-embed-doc": "workspace:*"
"@blocksuite/affine-block-frame": "workspace:*"
"@blocksuite/affine-block-image": "workspace:*"
"@blocksuite/affine-block-latex": "workspace:*"